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