2 * Encode and decode standard Unix attributes and
3 * Extended attributes for Win32 and
4 * other non-Unix systems, or Unix systems with ACLs, ...
6 * Kern Sibbald, October MMII
12 Copyright (C) 2000-2005 Kern Sibbald
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.
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.
29 #if defined(HAVE_WIN32)
31 #include "../lib/winapi.h"
34 /* Forward referenced subroutines */
35 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd);
36 void unix_name_to_win32(POOLMEM **win32_name, char *name);
37 void win_error(JCR *jcr, char *prefix, POOLMEM *ofile);
38 HANDLE bget_handle(BFILE *bfd);
39 #endif /* HAVE_WIN32 */
41 /* For old systems that don't have lchown() use chown() */
46 /*=============================================================*/
48 /* *** A l l S y s t e m s *** */
50 /*=============================================================*/
53 * Return the data stream that will be used
55 int select_data_stream(FF_PKT *ff_pkt)
60 * Fix all incompatible options
63 /* No sparse option for encrypted data */
64 if (ff_pkt->flags & FO_ENCRYPT) {
65 ff_pkt->flags &= ~FO_SPARSE;
68 /* Note, no sparse option for win32_data */
69 if (!is_portable_backup(&ff_pkt->bfd)) {
70 stream = STREAM_WIN32_DATA;
71 ff_pkt->flags &= ~FO_SPARSE;
72 } else if (ff_pkt->flags & FO_SPARSE) {
73 stream = STREAM_SPARSE_DATA;
75 stream = STREAM_FILE_DATA;
78 /* Encryption is only supported for file data */
79 if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
80 stream != STREAM_MACOS_FORK_DATA) {
81 ff_pkt->flags &= ~FO_ENCRYPT;
84 /* Compression is not supported for Mac fork data */
85 if (stream == STREAM_MACOS_FORK_DATA) {
86 ff_pkt->flags &= ~FO_GZIP;
90 * Handle compression and encryption options
93 if (ff_pkt->flags & FO_GZIP) {
95 case STREAM_WIN32_DATA:
96 stream = STREAM_WIN32_GZIP_DATA;
98 case STREAM_SPARSE_DATA:
99 stream = STREAM_SPARSE_GZIP_DATA;
101 case STREAM_FILE_DATA:
102 stream = STREAM_GZIP_DATA;
105 /* All stream types that do not support gzip should clear out
106 * FO_GZIP above, and this code block should be unreachable. */
107 ASSERT(!(ff_pkt->flags & FO_GZIP));
113 if (ff_pkt->flags & FO_ENCRYPT) {
115 case STREAM_WIN32_DATA:
116 stream = STREAM_ENCRYPTED_WIN32_DATA;
118 case STREAM_WIN32_GZIP_DATA:
119 stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
121 case STREAM_FILE_DATA:
122 stream = STREAM_ENCRYPTED_FILE_DATA;
124 case STREAM_GZIP_DATA:
125 stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
128 /* All stream types that do not support encryption should clear out
129 * FO_ENCRYPT above, and this code block should be unreachable. */
130 ASSERT(!ff_pkt->flags & FO_ENCRYPT);
141 * Encode a stat structure into a base64 character string
142 * All systems must create such a structure.
143 * In addition, we tack on the LinkFI, which is non-zero in
144 * the case of a hard linked file that has no data. This
145 * is a File Index pointing to the link that does have the
146 * data (always the first one encountered in a save).
147 * You may piggyback attributes on this packet by encoding
148 * them in the encode_attribsEx() subroutine, but this is
151 void encode_stat(char *buf, FF_PKT *ff_pkt, int data_stream)
154 struct stat *statp = &ff_pkt->statp;
156 * Encode a stat packet. I should have done this more intelligently
157 * with a length so that it could be easily expanded.
159 p += to_base64((int64_t)statp->st_dev, p);
160 *p++ = ' '; /* separate fields with a space */
161 p += to_base64((int64_t)statp->st_ino, p);
163 p += to_base64((int64_t)statp->st_mode, p);
165 p += to_base64((int64_t)statp->st_nlink, p);
167 p += to_base64((int64_t)statp->st_uid, p);
169 p += to_base64((int64_t)statp->st_gid, p);
171 p += to_base64((int64_t)statp->st_rdev, p);
173 p += to_base64((int64_t)statp->st_size, p);
176 p += to_base64((int64_t)statp->st_blksize, p);
178 p += to_base64((int64_t)statp->st_blocks, p);
181 p += to_base64((int64_t)0, p); /* output place holder */
183 p += to_base64((int64_t)0, p); /* output place holder */
186 p += to_base64((int64_t)statp->st_atime, p);
188 p += to_base64((int64_t)statp->st_mtime, p);
190 p += to_base64((int64_t)statp->st_ctime, p);
192 p += to_base64((int64_t)ff_pkt->LinkFI, p);
196 /* FreeBSD function */
197 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
199 p += to_base64((int64_t)0, p); /* output place holder */
202 p += to_base64((int64_t)data_stream, p);
208 /* Do casting according to unknown type to keep compiler happy */
210 #define plug(st, val) st = (typeof st)val
212 #if !HAVE_GCC & HAVE_SUN_OS
213 /* Sun compiler does not handle templates correctly */
214 #define plug(st, val) st = val
216 /* Use templates to do the casting */
217 template <class T> void plug(T &st, uint64_t val)
218 { st = static_cast<T>(val); }
223 /* Decode a stat packet from base64 characters */
224 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
229 p += from_base64(&val, p);
230 plug(statp->st_dev, val);
232 p += from_base64(&val, p);
233 plug(statp->st_ino, val);
235 p += from_base64(&val, p);
236 plug(statp->st_mode, val);
238 p += from_base64(&val, p);
239 plug(statp->st_nlink, val);
241 p += from_base64(&val, p);
242 plug(statp->st_uid, val);
244 p += from_base64(&val, p);
245 plug(statp->st_gid, val);
247 p += from_base64(&val, p);
248 plug(statp->st_rdev, val);
250 p += from_base64(&val, p);
251 plug(statp->st_size, val);
254 p += from_base64(&val, p);
255 plug(statp->st_blksize, val);
257 p += from_base64(&val, p);
258 plug(statp->st_blocks, val);
261 p += from_base64(&val, p);
262 // plug(statp->st_blksize, val);
264 p += from_base64(&val, p);
265 // plug(statp->st_blocks, val);
268 p += from_base64(&val, p);
269 plug(statp->st_atime, val);
271 p += from_base64(&val, p);
272 plug(statp->st_mtime, val);
274 p += from_base64(&val, p);
275 plug(statp->st_ctime, val);
277 /* Optional FileIndex of hard linked file data */
278 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
280 p += from_base64(&val, p);
281 *LinkFI = (uint32_t)val;
287 /* FreeBSD user flags */
288 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
290 p += from_base64(&val, p);
292 plug(statp->st_flags, val);
298 /* Look for data stream id */
299 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
301 p += from_base64(&val, p);
308 /* Decode a LinkFI field of encoded stat packet */
309 int32_t decode_LinkFI(char *buf, struct stat *statp)
314 skip_nonspaces(&p); /* st_dev */
315 p++; /* skip space */
316 skip_nonspaces(&p); /* st_ino */
318 p += from_base64(&val, p);
319 plug(statp->st_mode, val); /* st_mode */
321 skip_nonspaces(&p); /* st_nlink */
323 skip_nonspaces(&p); /* st_uid */
325 skip_nonspaces(&p); /* st_gid */
327 skip_nonspaces(&p); /* st_rdev */
329 skip_nonspaces(&p); /* st_size */
331 skip_nonspaces(&p); /* st_blksize */
333 skip_nonspaces(&p); /* st_blocks */
335 skip_nonspaces(&p); /* st_atime */
337 skip_nonspaces(&p); /* st_mtime */
339 skip_nonspaces(&p); /* st_ctime */
341 /* Optional FileIndex of hard linked file data */
342 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
344 p += from_base64(&val, p);
351 * Set file modes, permissions and times
353 * fname is the original filename
354 * ofile is the output filename (may be in a different directory)
356 * Returns: true on success
359 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
366 #if defined(HAVE_WIN32)
367 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
368 set_win32_attributes(jcr, attr, ofd)) {
372 pm_strcpy(attr->ofname, "*none*");
375 if (attr->data_stream == STREAM_WIN32_DATA ||
376 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
380 pm_strcpy(attr->ofname, "*none*");
386 * If Windows stuff failed, e.g. attempt to restore Unix file
387 * to Windows, simply fall through and we will do it the
394 char ec1[50], ec2[50];
395 fsize = blseek(ofd, 0, SEEK_END);
396 bclose(ofd); /* first close file */
397 if (fsize > 0 && fsize != (off_t)attr->statp.st_size) {
398 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
399 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
400 edit_uint64(fsize, ec2));
404 ut.actime = attr->statp.st_atime;
405 ut.modtime = attr->statp.st_mtime;
407 /* ***FIXME**** optimize -- don't do if already correct */
409 * For link, change owner of link using lchown, but don't
410 * try to do a chmod as that will update the file behind it.
412 if (attr->type == FT_LNK) {
413 /* Change owner of link, not of real file */
414 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
416 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
417 attr->ofname, be.strerror());
421 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
423 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
424 attr->ofname, be.strerror());
427 if (chmod(attr->ofname, attr->statp.st_mode) < 0) {
429 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
430 attr->ofname, be.strerror());
437 if (utime(attr->ofname, &ut) < 0) {
439 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
440 attr->ofname, be.strerror());
447 * Note, this should really be done before the utime() above,
448 * but if the immutable bit is set, it will make the utimes()
451 if (chflags(attr->ofname, attr->statp.st_flags) < 0) {
453 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
454 attr->ofname, be.strerror());
459 pm_strcpy(attr->ofname, "*none*");
465 /*=============================================================*/
467 /* * * * U n i x * * * * */
469 /*=============================================================*/
471 #if !defined(HAVE_WIN32)
474 * It is possible to piggyback additional data e.g. ACLs on
475 * the encode_stat() data by returning the extended attributes
476 * here. They must be "self-contained" (i.e. you keep track
477 * of your own length), and they must be in ASCII string
478 * format. Using this feature is not recommended.
479 * The code below shows how to return nothing. See the Win32
480 * code below for returning something in the attributes.
482 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
484 #ifdef HAVE_DARWIN_OS
486 * We save the Mac resource fork length so that on a
487 * restore, we can be sure we put back the whole resource.
491 if (ff_pkt->flags & FO_HFSPLUS) {
492 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
496 *attribsEx = 0; /* no extended attributes */
498 return STREAM_UNIX_ATTRIBUTES;
505 /*=============================================================*/
507 /* * * * W i n 3 2 * * * * */
509 /*=============================================================*/
511 #if defined(HAVE_WIN32)
513 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
516 WIN32_FILE_ATTRIBUTE_DATA atts;
519 attribsEx[0] = 0; /* no extended attributes */
521 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
523 // try unicode version
524 if (p_GetFileAttributesExW) {
525 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
526 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
528 BOOL b=p_GetFileAttributesExW((LPCWSTR) pwszBuf, GetFileExInfoStandard, (LPVOID)&atts);
529 free_pool_memory(pwszBuf);
532 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
533 return STREAM_UNIX_ATTRIBUTES;
537 if (!p_GetFileAttributesExA)
538 return STREAM_UNIX_ATTRIBUTES;
540 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
542 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
543 return STREAM_UNIX_ATTRIBUTES;
547 p += to_base64((uint64_t)atts.dwFileAttributes, p);
548 *p++ = ' '; /* separate fields with a space */
549 li.LowPart = atts.ftCreationTime.dwLowDateTime;
550 li.HighPart = atts.ftCreationTime.dwHighDateTime;
551 p += to_base64((uint64_t)li.QuadPart, p);
553 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
554 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
555 p += to_base64((uint64_t)li.QuadPart, p);
557 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
558 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
559 p += to_base64((uint64_t)li.QuadPart, p);
561 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
563 p += to_base64((uint64_t)atts.nFileSizeLow, p);
565 return STREAM_UNIX_ATTRIBUTES_EX;
568 /* Define attributes that are legal to set with SetFileAttributes() */
569 #define SET_ATTRS ( \
570 FILE_ATTRIBUTE_ARCHIVE| \
571 FILE_ATTRIBUTE_HIDDEN| \
572 FILE_ATTRIBUTE_NORMAL| \
573 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
574 FILE_ATTRIBUTE_OFFLINE| \
575 FILE_ATTRIBUTE_READONLY| \
576 FILE_ATTRIBUTE_SYSTEM| \
577 FILE_ATTRIBUTE_TEMPORARY)
581 * Set Extended File Attributes for Win32
583 * fname is the original filename
584 * ofile is the output filename (may be in a different directory)
586 * Returns: true on success
589 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
591 char *p = attr->attrEx;
593 WIN32_FILE_ATTRIBUTE_DATA atts;
595 POOLMEM *win32_ofile;
597 // if we have neither ansi nor wchar version, we leave
598 if (!(p_SetFileAttributesW || p_SetFileAttributesA))
601 if (!p || !*p) { /* we should have attributes */
602 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
608 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
611 p += from_base64(&val, p);
612 plug(atts.dwFileAttributes, val);
613 p++; /* skip space */
614 p += from_base64(&val, p);
616 atts.ftCreationTime.dwLowDateTime = li.LowPart;
617 atts.ftCreationTime.dwHighDateTime = li.HighPart;
618 p++; /* skip space */
619 p += from_base64(&val, p);
621 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
622 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
623 p++; /* skip space */
624 p += from_base64(&val, p);
626 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
627 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
629 p += from_base64(&val, p);
630 plug(atts.nFileSizeHigh, val);
632 p += from_base64(&val, p);
633 plug(atts.nFileSizeLow, val);
635 /* Convert to Windows path format */
636 win32_ofile = get_pool_memory(PM_FNAME);
637 unix_name_to_win32(&win32_ofile, attr->ofname);
639 /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
641 if (!is_bopen(ofd)) {
642 Dmsg1(100, "File not open: %s\n", attr->ofname);
643 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
647 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
648 if (!SetFileTime(bget_handle(ofd),
649 &atts.ftCreationTime,
650 &atts.ftLastAccessTime,
651 &atts.ftLastWriteTime)) {
652 win_error(jcr, "SetFileTime:", win32_ofile);
657 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
658 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
660 if (p_SetFileAttributesW) {
661 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
662 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
664 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
665 free_pool_memory(pwszBuf);
668 win_error(jcr, "SetFileAttributesW:", win32_ofile);
671 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
672 win_error(jcr, "SetFileAttributesA:", win32_ofile);
676 free_pool_memory(win32_ofile);
680 void win_error(JCR *jcr, char *prefix, POOLMEM *win32_ofile)
682 DWORD lerror = GetLastError();
684 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
685 FORMAT_MESSAGE_FROM_SYSTEM,
692 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
693 strip_trailing_junk(msg);
694 Jmsg(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
698 void win_error(JCR *jcr, char *prefix, DWORD lerror)
701 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
702 FORMAT_MESSAGE_FROM_SYSTEM,
709 strip_trailing_junk(msg);
711 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
713 MessageBox(NULL, msg, prefix, MB_OK);
719 /* Conversion of a Unix filename to a Win32 filename */
720 extern void conv_unix_to_win32_path(const char *path, char *win32_path, DWORD dwSize);
721 void unix_name_to_win32(POOLMEM **win32_name, char *name)
723 /* One extra byte should suffice, but we double it */
724 /* add MAX_PATH bytes for VSS shadow copy name */
725 DWORD dwSize = 2*strlen(name)+MAX_PATH;
726 *win32_name = check_pool_memory_size(*win32_name, dwSize);
727 conv_unix_to_win32_path(name, *win32_name, dwSize);
730 #endif /* HAVE_WIN32 */