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