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