]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/attribs.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / findlib / attribs.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *  Encode and decode standard Unix attributes and
21  *   Extended attributes for Win32 and
22  *   other non-Unix systems, or Unix systems with ACLs, ...
23  *
24  *    Kern Sibbald, October MMII
25  *
26  */
27
28 //#define _POSIX_C_SOURCE 200809L
29 //#define _BSD_SOURCE 1
30
31 #include "bacula.h"
32 #include "find.h"
33 #include "ch.h"
34
35 static uid_t my_uid = 1;
36 static gid_t my_gid = 1;
37 static bool uid_set = false;
38
39 #ifdef HAVE_WIN32
40 /* Forward referenced Windows subroutines */
41 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd);
42 void unix_name_to_win32(POOLMEM **win32_name, const char *name);
43 void win_error(JCR *jcr, int type, const char *prefix, POOLMEM *ofile);
44 void win_error(JCR *jcr, const char *prefix, POOLMEM *ofile);
45 HANDLE bget_handle(BFILE *bfd);
46 #endif /* HAVE_WIN32 */
47
48 /*
49  * For old systems that don't have lchown() or lchmod()
50  */
51 #ifndef HAVE_LCHOWN
52 #define lchown chown
53 #endif
54 #ifndef HAVE_LCHMOD
55 #define lchmod chmod
56 #endif
57
58 /*=============================================================*/
59 /*                                                             */
60 /*             ***  A l l  S y s t e m s ***                   */
61 /*                                                             */
62 /*=============================================================*/
63
64 /*
65  * To turn off use of fchown(), fchmod(), or futimes(),
66  *   uncomment one or more of the following.
67  */
68 //#undef HAVE_FCHOWN
69 //#undef HAVE_FCHMOD
70 //#undef HAVE_FUTIMES
71
72 /*
73  * Print errors only if debug level defined or we are root.
74  *  root should not get errors.  Errors for users causes
75  *  too much output.
76  */
77 #define print_error(jcr) (chk_dbglvl(100) || (my_uid == 0 && (!jcr || jcr->job_uid == 0)))
78
79 /*
80  * Restore the owner and permissions (mode) of a Directory.
81  *  See attribs.c for the equivalent for files.
82  */
83 void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t mode)
84 {
85    if (lchown(path, owner, group) != 0 && print_error(attr->jcr)
86 #ifdef AFS
87         && errno != EPERM
88 #endif
89    ) {
90       berrno be;
91       Jmsg4(attr->jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s %d %d\n"),
92             path, be.bstrerror(), getuid(), attr->jcr->job_uid);
93    }
94    if (lchmod(path, mode) != 0 && print_error(attr->jcr)) {
95       berrno be;
96       Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
97            path, be.bstrerror());
98    }
99 }
100
101
102 bool set_mod_own_time(JCR *jcr, BFILE *ofd, ATTR *attr)
103 {
104    bool ok = true;
105    struct utimbuf ut;
106
107    /* Do not try to set rights with f functions when using a plugin */
108    if (is_bopen(ofd) && !ofd->cmd_plugin) { /* TODO: Look with opt_plugin */
109       /*
110        * The #ifdefing is a bit ugly, but it is the only
111        *  way we can ensure this works on older systems.
112        */
113 #ifdef HAVE_FCHOWN
114       if (fchown(ofd->fid, attr->statp.st_uid, attr->statp.st_gid) < 0 && print_error(jcr)) {
115 #else
116       if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && print_error(jcr)) {
117 #endif
118          berrno be;
119          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
120             attr->ofname, be.bstrerror());
121          ok = false;
122       }
123
124 #ifdef HAVE_FCHMOD
125       if (fchmod(ofd->fid, attr->statp.st_mode) < 0 && print_error(jcr)) {
126 #else
127       if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && print_error(jcr)) {
128 #endif
129          berrno be;
130          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
131             attr->ofname, be.bstrerror());
132          ok = false;
133       }
134
135 #ifdef HAVE_FUTIMES
136       struct timeval times[2];
137       times[0].tv_sec = attr->statp.st_atime;
138       times[0].tv_usec = 0;
139       times[1].tv_sec = attr->statp.st_mtime;
140       times[1].tv_usec = 0;
141       if (futimes(ofd->fid, times) < 0 && print_error(jcr)) {
142 #else
143       ut.actime = attr->statp.st_atime;
144       ut.modtime = attr->statp.st_mtime;
145       //bclose(ofd);
146       if (utime(attr->ofname, &ut) < 0 && print_error(jcr)) {
147 #endif
148          berrno be;
149          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
150             attr->ofname, be.bstrerror());
151          ok = false;
152       }
153    } else {
154       if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && print_error(jcr)) {
155          berrno be;
156          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
157             attr->ofname, be.bstrerror());
158          ok = false;
159       }
160       if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && print_error(jcr)) {
161          berrno be;
162          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
163             attr->ofname, be.bstrerror());
164          ok = false;
165       }
166       /*
167        * Reset file times.
168        */
169       ut.actime = attr->statp.st_atime;
170       ut.modtime = attr->statp.st_mtime;
171
172       if (utime(attr->ofname, &ut) < 0 && print_error(jcr)) {
173          berrno be;
174          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
175             attr->ofname, be.bstrerror());
176          ok = false;
177       }
178    }
179    return ok;
180 }
181
182 /*
183  * Return the data stream that will be used
184  */
185 int select_data_stream(FF_PKT *ff_pkt)
186 {
187    int stream;
188
189    /* This is a plugin special restore object */
190    if (ff_pkt->type == FT_RESTORE_FIRST) {
191       ff_pkt->flags = 0;
192       return STREAM_FILE_DATA;
193    }
194
195    /*
196     *  Fix all incompatible options
197     */
198    /* No sparse option for encrypted data */
199    if (ff_pkt->flags & FO_ENCRYPT) {
200       ff_pkt->flags &= ~FO_SPARSE;
201    }
202
203    /* Note, no sparse option for win32_data */
204    if (!is_portable_backup(&ff_pkt->bfd)) {
205       stream = STREAM_WIN32_DATA;
206       ff_pkt->flags &= ~FO_SPARSE;
207    } else if (ff_pkt->flags & FO_SPARSE) {
208       stream = STREAM_SPARSE_DATA;
209    } else {
210       stream = STREAM_FILE_DATA;
211    }
212    if (ff_pkt->flags & FO_OFFSETS) {
213       stream = STREAM_SPARSE_DATA;
214    }
215
216    /* Encryption is only supported for file data */
217    if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
218          stream != STREAM_MACOS_FORK_DATA) {
219       ff_pkt->flags &= ~FO_ENCRYPT;
220    }
221
222    /* Compression is not supported for Mac fork data */
223    if (stream == STREAM_MACOS_FORK_DATA) {
224       ff_pkt->flags &= ~FO_COMPRESS;
225    }
226
227    /*
228     * Handle compression and encryption options
229     */
230 #if defined(HAVE_LIBZ) || defined(HAVE_LZO)
231    if (ff_pkt->flags & FO_COMPRESS) {
232       #ifdef HAVE_LIBZ
233          if(ff_pkt->Compress_algo == COMPRESS_GZIP) {
234             switch (stream) {
235             case STREAM_WIN32_DATA:
236                   stream = STREAM_WIN32_GZIP_DATA;
237                break;
238             case STREAM_SPARSE_DATA:
239                   stream = STREAM_SPARSE_GZIP_DATA;
240                break;
241             case STREAM_FILE_DATA:
242                   stream = STREAM_GZIP_DATA;
243                break;
244             default:
245                /*
246                 * All stream types that do not support compression should clear out
247                 * FO_COMPRESS above, and this code block should be unreachable.
248                 */
249                ASSERT(!(ff_pkt->flags & FO_COMPRESS));
250                return STREAM_NONE;
251             }
252          }
253       #endif
254       #ifdef HAVE_LZO
255          if(ff_pkt->Compress_algo == COMPRESS_LZO1X) {
256             switch (stream) {
257             case STREAM_WIN32_DATA:
258                   stream = STREAM_WIN32_COMPRESSED_DATA;
259                break;
260             case STREAM_SPARSE_DATA:
261                   stream = STREAM_SPARSE_COMPRESSED_DATA;
262                break;
263             case STREAM_FILE_DATA:
264                   stream = STREAM_COMPRESSED_DATA;
265                break;
266             default:
267                /*
268                 * All stream types that do not support compression should clear out
269                 * FO_COMPRESS above, and this code block should be unreachable.
270                 */
271                ASSERT(!(ff_pkt->flags & FO_COMPRESS));
272                return STREAM_NONE;
273             }
274          }
275       #endif
276    }
277 #endif
278 #ifdef HAVE_CRYPTO
279    if (ff_pkt->flags & FO_ENCRYPT) {
280       switch (stream) {
281       case STREAM_WIN32_DATA:
282          stream = STREAM_ENCRYPTED_WIN32_DATA;
283          break;
284       case STREAM_WIN32_GZIP_DATA:
285          stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
286          break;
287       case STREAM_WIN32_COMPRESSED_DATA:
288          stream = STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA;
289          break;
290       case STREAM_FILE_DATA:
291          stream = STREAM_ENCRYPTED_FILE_DATA;
292          break;
293       case STREAM_GZIP_DATA:
294          stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
295          break;
296       case STREAM_COMPRESSED_DATA:
297          stream = STREAM_ENCRYPTED_FILE_COMPRESSED_DATA;
298          break;
299       default:
300          /* All stream types that do not support encryption should clear out
301           * FO_ENCRYPT above, and this code block should be unreachable. */
302          ASSERT(!(ff_pkt->flags & FO_ENCRYPT));
303          return STREAM_NONE;
304       }
305    }
306 #endif
307
308    return stream;
309 }
310
311
312 /*
313  * Encode a stat structure into a base64 character string
314  *   All systems must create such a structure.
315  *   In addition, we tack on the LinkFI, which is non-zero in
316  *   the case of a hard linked file that has no data.  This
317  *   is a File Index pointing to the link that does have the
318  *   data (always the first one encountered in a save).
319  * You may piggyback attributes on this packet by encoding
320  *   them in the encode_attribsEx() subroutine, but this is
321  *   not recommended.
322  */
323 void encode_stat(char *buf, struct stat *statp, int stat_size, int32_t LinkFI, int data_stream)
324 {
325    char *p = buf;
326    /*
327     * We read the stat packet so make sure the caller's conception
328     *  is the same as ours.  They can be different if LARGEFILE is not
329     *  the same when compiling this library and the calling program.
330     */
331    ASSERT(stat_size == (int)sizeof(struct stat));
332
333    /*
334     *  Encode a stat packet.  I should have done this more intelligently
335     *   with a length so that it could be easily expanded.
336     */
337     p += to_base64((int64_t)statp->st_dev, p);
338    *p++ = ' ';                        /* separate fields with a space */
339     p += to_base64((int64_t)statp->st_ino, p);
340    *p++ = ' ';
341     p += to_base64((int64_t)statp->st_mode, p);
342    *p++ = ' ';
343     p += to_base64((int64_t)statp->st_nlink, p);
344    *p++ = ' ';
345     p += to_base64((int64_t)statp->st_uid, p);
346    *p++ = ' ';
347     p += to_base64((int64_t)statp->st_gid, p);
348    *p++ = ' ';
349     p += to_base64((int64_t)statp->st_rdev, p);
350    *p++ = ' ';
351     p += to_base64((int64_t)statp->st_size, p);
352    *p++ = ' ';
353 #ifndef HAVE_MINGW
354     p += to_base64((int64_t)statp->st_blksize, p);
355    *p++ = ' ';
356     p += to_base64((int64_t)statp->st_blocks, p);
357    *p++ = ' ';
358 #else
359     p += to_base64((int64_t)0, p); /* output place holder */
360    *p++ = ' ';
361     p += to_base64((int64_t)0, p); /* output place holder */
362    *p++ = ' ';
363 #endif
364     p += to_base64((int64_t)statp->st_atime, p);
365    *p++ = ' ';
366     p += to_base64((int64_t)statp->st_mtime, p);
367    *p++ = ' ';
368    p += to_base64((int64_t)statp->st_ctime, p);
369    *p++ = ' ';
370     p += to_base64((int64_t)LinkFI, p);
371    *p++ = ' ';
372
373 #ifdef HAVE_CHFLAGS
374    /* FreeBSD function */
375    p += to_base64((int64_t)statp->st_flags, p);  /* output st_flags */
376 #else
377    p += to_base64((int64_t)0, p);     /* output place holder */
378 #endif
379    *p++ = ' ';
380    p += to_base64((int64_t)data_stream, p);
381 #ifdef HAVE_MINGW
382    *p++ = ' ';
383    p += to_base64((int64_t)statp->st_fattrs, p);
384 #endif
385    *p = 0;
386    return;
387 }
388
389 /* Do casting according to unknown type to keep compiler happy */
390 #ifdef HAVE_TYPEOF
391   #define plug(st, val) st = (typeof st)val
392 #else
393   #if !HAVE_GCC & HAVE_SUN_OS
394     /* Sun compiler does not handle templates correctly */
395     #define plug(st, val) st = val
396   #elif __sgi
397     #define plug(st, val) st = val
398   #else
399     /* Use templates to do the casting */
400     template <class T> void plug(T &st, uint64_t val)
401       { st = static_cast<T>(val); }
402   #endif
403 #endif
404
405
406 /*
407  * Decode a stat packet from base64 characters
408  * returns: data_stream
409  */
410 int decode_stat(char *buf, struct stat *statp, int stat_size, int32_t *LinkFI)
411 {
412    char *p = buf;
413    int64_t val;
414    int data_stream;
415
416    /*
417     * We store into the stat packet so make sure the caller's conception
418     *  is the same as ours.  They can be different if LARGEFILE is not
419     *  the same when compiling this library and the calling program.
420     */
421    ASSERT(stat_size == (int)sizeof(struct stat));
422
423    p += from_base64(&val, p);
424    plug(statp->st_dev, val);
425    p++;
426    p += from_base64(&val, p);
427    plug(statp->st_ino, val);
428    p++;
429    p += from_base64(&val, p);
430    plug(statp->st_mode, val);
431    p++;
432    p += from_base64(&val, p);
433    plug(statp->st_nlink, val);
434    p++;
435    p += from_base64(&val, p);
436    plug(statp->st_uid, val);
437    p++;
438    p += from_base64(&val, p);
439    plug(statp->st_gid, val);
440    p++;
441    p += from_base64(&val, p);
442    plug(statp->st_rdev, val);
443    p++;
444    p += from_base64(&val, p);
445    plug(statp->st_size, val);
446    p++;
447 #ifndef HAVE_MINGW
448    p += from_base64(&val, p);
449    plug(statp->st_blksize, val);
450    p++;
451    p += from_base64(&val, p);
452    plug(statp->st_blocks, val);
453    p++;
454 #else
455    p += from_base64(&val, p);
456 //   plug(statp->st_blksize, val);
457    p++;
458    p += from_base64(&val, p);
459 //   plug(statp->st_blocks, val);
460    p++;
461 #endif
462    p += from_base64(&val, p);
463    plug(statp->st_atime, val);
464    p++;
465    p += from_base64(&val, p);
466    plug(statp->st_mtime, val);
467    p++;
468    p += from_base64(&val, p);
469    plug(statp->st_ctime, val);
470
471    /* Optional FileIndex of hard linked file data */
472    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
473       p++;
474       p += from_base64(&val, p);
475       *LinkFI = (uint32_t)val;
476    } else {
477       *LinkFI = 0;
478       return 0;
479    }
480
481    /* FreeBSD user flags */
482    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
483       p++;
484       p += from_base64(&val, p);
485 #ifdef HAVE_CHFLAGS
486       plug(statp->st_flags, val);
487    } else {
488       statp->st_flags  = 0;
489 #endif
490    }
491
492    /* Look for data stream id */
493    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
494       p++;
495       p += from_base64(&val, p);
496    } else {
497       val = 0;
498    }
499    data_stream = val;
500 #ifdef HAVE_MINGW
501    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
502       p++;
503       p += from_base64(&val, p);
504       plug(statp->st_fattrs, val);
505    } else {
506       statp->st_fattrs = 0;
507       val = 0;
508    }
509 #endif
510    return data_stream;
511 }
512
513 /* Decode a LinkFI field of encoded stat packet */
514 int32_t decode_LinkFI(char *buf, struct stat *statp, int stat_size)
515 {
516    char *p = buf;
517    int64_t val;
518    /*
519     * We store into the stat packet so make sure the caller's conception
520     *  is the same as ours.  They can be different if LARGEFILE is not
521     *  the same when compiling this library and the calling program.
522     */
523    ASSERT(stat_size == (int)sizeof(struct stat));
524
525    skip_nonspaces(&p);                /* st_dev */
526    p++;                               /* skip space */
527    skip_nonspaces(&p);                /* st_ino */
528    p++;
529    p += from_base64(&val, p);
530    plug(statp->st_mode, val);         /* st_mode */
531    p++;
532    skip_nonspaces(&p);                /* st_nlink */
533    p++;
534    skip_nonspaces(&p);                /* st_uid */
535    p++;
536    skip_nonspaces(&p);                /* st_gid */
537    p++;
538    skip_nonspaces(&p);                /* st_rdev */
539    p++;
540    skip_nonspaces(&p);                /* st_size */
541    p++;
542    skip_nonspaces(&p);                /* st_blksize */
543    p++;
544    skip_nonspaces(&p);                /* st_blocks */
545    p++;
546    skip_nonspaces(&p);                /* st_atime */
547    p++;
548    skip_nonspaces(&p);                /* st_mtime */
549    p++;
550    skip_nonspaces(&p);                /* st_ctime */
551
552    /* Optional FileIndex of hard linked file data */
553    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
554       p++;
555       p += from_base64(&val, p);
556       return (int32_t)val;
557    }
558    return 0;
559 }
560
561 /*
562  * Set file modes, permissions and times
563  *
564  *  fname is the original filename
565  *  ofile is the output filename (may be in a different directory)
566  *
567  * Returns:  true  on success
568  *           false on failure
569  */
570 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
571 {
572    mode_t old_mask;
573    bool ok = true;
574    boffset_t fsize;
575
576    if (!uid_set) {
577       my_uid = getuid();
578       my_gid = getgid();
579       uid_set = true;
580    }
581
582 #ifdef HAVE_WIN32
583    if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
584        set_win32_attributes(jcr, attr, ofd)) {
585        if (is_bopen(ofd)) {
586            bclose(ofd);
587        }
588        pm_strcpy(attr->ofname, "*none*");
589        return true;
590    }
591    if (attr->data_stream == STREAM_WIN32_DATA ||
592        attr->data_stream == STREAM_WIN32_GZIP_DATA ||
593        attr->data_stream == STREAM_WIN32_COMPRESSED_DATA) {
594       if (is_bopen(ofd)) {
595          bclose(ofd);
596       }
597       pm_strcpy(attr->ofname, "*none*");
598       return true;
599    }
600
601    /*
602     * If Windows stuff failed, e.g. attempt to restore Unix file
603     *  to Windows, simply fall through and we will do it the
604     *  universal way.
605     */
606 #endif /* HAVE_WIN32 */
607
608    old_mask = umask(0);
609    if (is_bopen(ofd)) {
610       char ec1[50], ec2[50];
611       fsize = blseek(ofd, 0, SEEK_END);
612       if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 &&
613                         fsize != (boffset_t)attr->statp.st_size) {
614          Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
615             attr->ofname, edit_uint64(attr->statp.st_size, ec1),
616             edit_uint64(fsize, ec2));
617       }
618    }
619
620    /*
621     * We do not restore sockets, so skip trying to restore their
622     *   attributes.
623     */
624    if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
625       goto bail_out;
626    }
627
628    /* ***FIXME**** optimize -- don't do if already correct */
629    /*
630     * For link, change owner of link using lchown, but don't
631     *   try to do a chmod as that will update the file behind it.
632     */
633    if (attr->type == FT_LNK) {
634 #ifdef HAVE_LCHOWN
635       /* Change owner of link, not of real file */
636       if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && print_error(jcr)) {
637          berrno be;
638          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
639             attr->ofname, be.bstrerror());
640          ok = false;
641       }
642 #endif
643 #ifdef HAVE_LUTIMES
644       struct timeval times[2];
645       times[0].tv_sec = attr->statp.st_atime;
646       times[0].tv_usec = 0;
647       times[1].tv_sec = attr->statp.st_mtime;
648       times[1].tv_usec = 0;
649       if (lutimes(attr->ofname, times) < 0 && print_error(jcr)) {
650          berrno be;
651          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
652             attr->ofname, be.bstrerror());
653          ok = false;
654       }
655 #endif
656    } else {
657       /*
658        * At this point, we have a file that is not a LINK
659        */
660       ok = set_mod_own_time(jcr, ofd, attr);
661
662 #ifdef HAVE_CHFLAGS
663       /*
664        * FreeBSD user flags
665        *
666        * Note, this should really be done before the utime() above,
667        *  but if the immutable bit is set, it will make the utimes()
668        *  fail.
669        */
670       if (chflags(attr->ofname, attr->statp.st_flags) < 0 && print_error(jcr)) {
671          berrno be;
672          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
673             attr->ofname, be.bstrerror());
674          ok = false;
675       }
676 #endif
677    }
678
679 bail_out:
680    if (is_bopen(ofd)) {
681       bclose(ofd);
682    }
683    pm_strcpy(attr->ofname, "*none*");
684    umask(old_mask);
685    return ok;
686 }
687
688
689 /*=============================================================*/
690 /*                                                             */
691 /*                 * * *  U n i x * * * *                      */
692 /*                                                             */
693 /*=============================================================*/
694
695 #ifndef HAVE_WIN32
696
697 /*
698  * It is possible to piggyback additional data e.g. ACLs on
699  *   the encode_stat() data by returning the extended attributes
700  *   here.  They must be "self-contained" (i.e. you keep track
701  *   of your own length), and they must be in ASCII string
702  *   format. Using this feature is not recommended.
703  * The code below shows how to return nothing.  See the Win32
704  *   code below for returning something in the attributes.
705  */
706 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
707 {
708 #ifdef HAVE_DARWIN_OS
709    /*
710     * We save the Mac resource fork length so that on a
711     * restore, we can be sure we put back the whole resource.
712     */
713    char *p;
714
715    *attribsEx = 0;                 /* no extended attributes (yet) */
716    if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
717       return STREAM_UNIX_ATTRIBUTES;
718    }
719    p = attribsEx;
720    if (ff_pkt->flags & FO_HFSPLUS) {
721       p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
722    }
723    *p = 0;
724 #else
725    *attribsEx = 0;                    /* no extended attributes */
726 #endif
727    return STREAM_UNIX_ATTRIBUTES;
728 }
729
730 #endif
731
732
733
734 /*=============================================================*/
735 /*                                                             */
736 /*                 * * *  W i n 3 2 * * * *                    */
737 /*                                                             */
738 /*=============================================================*/
739
740 #ifdef HAVE_WIN32
741
742 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
743 {
744    char *p = attribsEx;
745    WIN32_FILE_ATTRIBUTE_DATA atts;
746    ULARGE_INTEGER li;
747
748    attribsEx[0] = 0;                  /* no extended attributes */
749
750    if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
751       return STREAM_UNIX_ATTRIBUTES;
752    }
753
754    unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
755
756    /* try unicode version */
757    if (p_GetFileAttributesExW)  {
758       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
759       make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
760
761       BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
762                                     (LPVOID)&atts);
763       free_pool_memory(pwszBuf);
764
765       if (!b) {
766          win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
767          return STREAM_UNIX_ATTRIBUTES;
768       }
769    }
770    else {
771       if (!p_GetFileAttributesExA)
772          return STREAM_UNIX_ATTRIBUTES;
773
774       if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
775                               (LPVOID)&atts)) {
776          win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
777          return STREAM_UNIX_ATTRIBUTES;
778       }
779    }
780
781    p += to_base64((uint64_t)atts.dwFileAttributes, p);
782    *p++ = ' ';                        /* separate fields with a space */
783    li.LowPart = atts.ftCreationTime.dwLowDateTime;
784    li.HighPart = atts.ftCreationTime.dwHighDateTime;
785    p += to_base64((uint64_t)li.QuadPart, p);
786    *p++ = ' ';
787    li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
788    li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
789    p += to_base64((uint64_t)li.QuadPart, p);
790    *p++ = ' ';
791    li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
792    li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
793    p += to_base64((uint64_t)li.QuadPart, p);
794    *p++ = ' ';
795    p += to_base64((uint64_t)atts.nFileSizeHigh, p);
796    *p++ = ' ';
797    p += to_base64((uint64_t)atts.nFileSizeLow, p);
798    *p = 0;
799    return STREAM_UNIX_ATTRIBUTES_EX;
800 }
801
802 /* Define attributes that are legal to set with SetFileAttributes() */
803 #define SET_ATTRS ( \
804          FILE_ATTRIBUTE_ARCHIVE| \
805          FILE_ATTRIBUTE_HIDDEN| \
806          FILE_ATTRIBUTE_NORMAL| \
807          FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
808          FILE_ATTRIBUTE_OFFLINE| \
809          FILE_ATTRIBUTE_READONLY| \
810          FILE_ATTRIBUTE_SYSTEM| \
811          FILE_ATTRIBUTE_TEMPORARY)
812
813 /*
814  * Set Extended File Attributes for Win32
815  *
816  *  fname is the original filename
817  *  ofile is the output filename (may be in a different directory)
818  *
819  * Returns:  true  on success
820  *           false on failure
821  */
822 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
823 {
824    char *p = attr->attrEx;
825    int64_t val;
826    WIN32_FILE_ATTRIBUTE_DATA atts;
827    ULARGE_INTEGER li;
828    POOLMEM *win32_ofile;
829
830    /* if we have neither Win ansi nor wchar API, get out */
831    if (!(p_SetFileAttributesW || p_SetFileAttributesA)) {
832       return false;
833    }
834
835    if (!p || !*p) {                   /* we should have attributes */
836       Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
837       if (is_bopen(ofd)) {
838          bclose(ofd);
839       }
840       return false;
841    } else {
842       Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
843    }
844
845    p += from_base64(&val, p);
846    plug(atts.dwFileAttributes, val);
847    p++;                               /* skip space */
848    p += from_base64(&val, p);
849    li.QuadPart = val;
850    atts.ftCreationTime.dwLowDateTime = li.LowPart;
851    atts.ftCreationTime.dwHighDateTime = li.HighPart;
852    p++;                               /* skip space */
853    p += from_base64(&val, p);
854    li.QuadPart = val;
855    atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
856    atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
857    p++;                               /* skip space */
858    p += from_base64(&val, p);
859    li.QuadPart = val;
860    atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
861    atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
862    p++;
863    p += from_base64(&val, p);
864    plug(atts.nFileSizeHigh, val);
865    p++;
866    p += from_base64(&val, p);
867    plug(atts.nFileSizeLow, val);
868
869    /* Convert to Windows path format */
870    win32_ofile = get_pool_memory(PM_FNAME);
871    unix_name_to_win32(&win32_ofile, attr->ofname);
872
873    /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
874
875    if (!is_bopen(ofd)) {
876       Dmsg1(100, "File not open: %s\n", attr->ofname);
877       bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0);   /* attempt to open the file */
878    }
879
880    if (is_bopen(ofd)) {
881       Dmsg1(100, "SetFileTime %s\n", attr->ofname);
882       if (!SetFileTime(bget_handle(ofd),
883                          &atts.ftCreationTime,
884                          &atts.ftLastAccessTime,
885                          &atts.ftLastWriteTime)) {
886          win_error(jcr, M_WARNING, "SetFileTime:", win32_ofile);
887       }
888
889       /*
890        * Inform win32 api that the given file is a sparse file
891        */
892       if (atts.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) {
893          DWORD bytesReturned;
894
895          Dmsg1(100, "Set FILE_ATTRIBUTE_SPARSE_FILE on %s\n", attr->ofname);
896          if (!DeviceIoControl(bget_handle(ofd), FSCTL_SET_SPARSE,
897                               NULL, 0, NULL, 0, &bytesReturned, NULL))
898          {
899             /* Not sure we really want to have a Warning for such attribute  */
900             win_error(jcr, M_WARNING, "set SPARSE_FILE:", win32_ofile);
901          }
902       }
903
904       /*
905        * Restore the file as compressed.
906        */
907       if (atts.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
908          int fmt = COMPRESSION_FORMAT_DEFAULT;
909          DWORD bytesReturned;
910
911          Dmsg1(100, "Set FILE_ATTRIBUTE_COMPRESSED on %s\n", attr->ofname);
912          if (!DeviceIoControl(bget_handle(ofd), FSCTL_SET_COMPRESSION,
913                              &fmt, sizeof(fmt), NULL, 0, &bytesReturned, NULL))
914          {
915             /* Not sure we really want to have a Warning for such attribute  */
916             win_error(jcr, M_WARNING, "set COMPRESSED:", win32_ofile);
917          }
918       }
919
920       bclose(ofd);
921    }
922
923    Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
924    if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
925       if (p_SetFileAttributesW) {
926          POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
927          make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
928
929          BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
930          free_pool_memory(pwszBuf);
931
932          if (!b)
933             win_error(jcr, M_WARNING, "SetFileAttributesW:", win32_ofile);
934       }
935       else {
936          if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
937             win_error(jcr, M_WARNING, "SetFileAttributesA:", win32_ofile);
938          }
939       }
940    }
941    free_pool_memory(win32_ofile);
942    return true;
943 }
944
945 void win_error(JCR *jcr, int type, const char *prefix, POOLMEM *win32_ofile)
946 {
947    DWORD lerror = GetLastError();
948    LPTSTR msg;
949    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
950                  FORMAT_MESSAGE_FROM_SYSTEM,
951                  NULL,
952                  lerror,
953                  0,
954                  (LPTSTR)&msg,
955                  0,
956                  NULL);
957    Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
958    strip_trailing_junk(msg);
959    Jmsg3(jcr, type, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
960    LocalFree(msg);
961 }
962
963 void win_error(JCR *jcr, const char *prefix, POOLMEM *win32_ofile)
964 {
965    win_error(jcr, M_ERROR, prefix, win32_ofile);
966 }
967
968 void win_error(JCR *jcr, const char *prefix, DWORD lerror)
969 {
970    LPTSTR msg;
971    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
972                  FORMAT_MESSAGE_FROM_SYSTEM,
973                  NULL,
974                  lerror,
975                  0,
976                  (LPTSTR)&msg,
977                  0,
978                  NULL);
979    strip_trailing_junk(msg);
980    if (jcr) {
981       Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
982       MessageBox(NULL, msg, prefix, MB_OK);
983    }
984    LocalFree(msg);
985 }
986 #endif /* HAVE_WIN32 */