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