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