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