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_CYGWIN) || 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);
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); }
222 /* Decode a stat packet from base64 characters */
223 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
228 p += from_base64(&val, p);
229 plug(statp->st_dev, val);
231 p += from_base64(&val, p);
232 plug(statp->st_ino, val);
234 p += from_base64(&val, p);
235 plug(statp->st_mode, val);
237 p += from_base64(&val, p);
238 plug(statp->st_nlink, val);
240 p += from_base64(&val, p);
241 plug(statp->st_uid, val);
243 p += from_base64(&val, p);
244 plug(statp->st_gid, val);
246 p += from_base64(&val, p);
247 plug(statp->st_rdev, val);
249 p += from_base64(&val, p);
250 plug(statp->st_size, val);
253 p += from_base64(&val, p);
254 plug(statp->st_blksize, val);
256 p += from_base64(&val, p);
257 plug(statp->st_blocks, val);
260 p += from_base64(&val, p);
261 // plug(statp->st_blksize, val);
263 p += from_base64(&val, p);
264 // plug(statp->st_blocks, val);
267 p += from_base64(&val, p);
268 plug(statp->st_atime, val);
270 p += from_base64(&val, p);
271 plug(statp->st_mtime, val);
273 p += from_base64(&val, p);
274 plug(statp->st_ctime, val);
276 /* Optional FileIndex of hard linked file data */
277 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
279 p += from_base64(&val, p);
280 *LinkFI = (uint32_t)val;
286 /* FreeBSD user flags */
287 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
289 p += from_base64(&val, p);
291 plug(statp->st_flags, val);
297 /* Look for data stream id */
298 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
300 p += from_base64(&val, p);
307 /* Decode a LinkFI field of encoded stat packet */
308 int32_t decode_LinkFI(char *buf, struct stat *statp)
313 skip_nonspaces(&p); /* st_dev */
314 p++; /* skip space */
315 skip_nonspaces(&p); /* st_ino */
317 p += from_base64(&val, p);
318 plug(statp->st_mode, val); /* st_mode */
320 skip_nonspaces(&p); /* st_nlink */
322 skip_nonspaces(&p); /* st_uid */
324 skip_nonspaces(&p); /* st_gid */
326 skip_nonspaces(&p); /* st_rdev */
328 skip_nonspaces(&p); /* st_size */
330 skip_nonspaces(&p); /* st_blksize */
332 skip_nonspaces(&p); /* st_blocks */
334 skip_nonspaces(&p); /* st_atime */
336 skip_nonspaces(&p); /* st_mtime */
338 skip_nonspaces(&p); /* st_ctime */
340 /* Optional FileIndex of hard linked file data */
341 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
343 p += from_base64(&val, p);
350 * Set file modes, permissions and times
352 * fname is the original filename
353 * ofile is the output filename (may be in a different directory)
355 * Returns: true on success
358 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
365 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
366 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
367 set_win32_attributes(jcr, attr, ofd)) {
371 pm_strcpy(attr->ofname, "*none*");
374 if (attr->data_stream == STREAM_WIN32_DATA ||
375 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
379 pm_strcpy(attr->ofname, "*none*");
385 * If Windows stuff failed, e.g. attempt to restore Unix file
386 * to Windows, simply fall through and we will do it the
393 char ec1[50], ec2[50];
394 fsize = blseek(ofd, 0, SEEK_END);
395 bclose(ofd); /* first close file */
396 if (fsize > 0 && fsize != (off_t)attr->statp.st_size) {
397 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
398 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
399 edit_uint64(fsize, ec2));
403 ut.actime = attr->statp.st_atime;
404 ut.modtime = attr->statp.st_mtime;
406 /* ***FIXME**** optimize -- don't do if already correct */
408 * For link, change owner of link using lchown, but don't
409 * try to do a chmod as that will update the file behind it.
411 if (attr->type == FT_LNK) {
412 /* Change owner of link, not of real file */
413 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
415 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
416 attr->ofname, be.strerror());
420 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
422 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
423 attr->ofname, be.strerror());
426 if (chmod(attr->ofname, attr->statp.st_mode) < 0) {
428 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
429 attr->ofname, be.strerror());
436 if (utime(attr->ofname, &ut) < 0) {
438 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
439 attr->ofname, be.strerror());
446 * Note, this should really be done before the utime() above,
447 * but if the immutable bit is set, it will make the utimes()
450 if (chflags(attr->ofname, attr->statp.st_flags) < 0) {
452 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
453 attr->ofname, be.strerror());
458 pm_strcpy(attr->ofname, "*none*");
464 /*=============================================================*/
466 /* * * * U n i x * * * * */
468 /*=============================================================*/
470 #if !defined(HAVE_CYGWIN) && !defined(HAVE_WIN32)
473 * It is possible to piggyback additional data e.g. ACLs on
474 * the encode_stat() data by returning the extended attributes
475 * here. They must be "self-contained" (i.e. you keep track
476 * of your own length), and they must be in ASCII string
477 * format. Using this feature is not recommended.
478 * The code below shows how to return nothing. See the Win32
479 * code below for returning something in the attributes.
481 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
483 #ifdef HAVE_DARWIN_OS
485 * We save the Mac resource fork length so that on a
486 * restore, we can be sure we put back the whole resource.
490 if (ff_pkt->flags & FO_HFSPLUS) {
491 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
495 *attribsEx = 0; /* no extended attributes */
497 return STREAM_UNIX_ATTRIBUTES;
504 /*=============================================================*/
506 /* * * * W i n 3 2 * * * * */
508 /*=============================================================*/
510 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
512 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
515 WIN32_FILE_ATTRIBUTE_DATA atts;
518 attribsEx[0] = 0; /* no extended attributes */
520 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
522 // try unicode version
523 if (p_GetFileAttributesExW) {
524 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
525 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
527 BOOL b=p_GetFileAttributesExW((LPCWSTR) pwszBuf, GetFileExInfoStandard, (LPVOID)&atts);
528 free_pool_memory(pwszBuf);
531 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
532 return STREAM_UNIX_ATTRIBUTES;
536 if (!p_GetFileAttributesExA)
537 return STREAM_UNIX_ATTRIBUTES;
539 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
541 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
542 return STREAM_UNIX_ATTRIBUTES;
546 p += to_base64((uint64_t)atts.dwFileAttributes, p);
547 *p++ = ' '; /* separate fields with a space */
548 li.LowPart = atts.ftCreationTime.dwLowDateTime;
549 li.HighPart = atts.ftCreationTime.dwHighDateTime;
550 p += to_base64((uint64_t)li.QuadPart, p);
552 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
553 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
554 p += to_base64((uint64_t)li.QuadPart, p);
556 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
557 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
558 p += to_base64((uint64_t)li.QuadPart, p);
560 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
562 p += to_base64((uint64_t)atts.nFileSizeLow, p);
564 return STREAM_UNIX_ATTRIBUTES_EX;
567 /* Define attributes that are legal to set with SetFileAttributes() */
568 #define SET_ATTRS ( \
569 FILE_ATTRIBUTE_ARCHIVE| \
570 FILE_ATTRIBUTE_HIDDEN| \
571 FILE_ATTRIBUTE_NORMAL| \
572 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
573 FILE_ATTRIBUTE_OFFLINE| \
574 FILE_ATTRIBUTE_READONLY| \
575 FILE_ATTRIBUTE_SYSTEM| \
576 FILE_ATTRIBUTE_TEMPORARY)
580 * Set Extended File Attributes for Win32
582 * fname is the original filename
583 * ofile is the output filename (may be in a different directory)
585 * Returns: true on success
588 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
590 char *p = attr->attrEx;
592 WIN32_FILE_ATTRIBUTE_DATA atts;
594 POOLMEM *win32_ofile;
596 // if we have neither ansi nor wchar version, we leave
597 if (!(p_SetFileAttributesW || p_SetFileAttributesA))
600 if (!p || !*p) { /* we should have attributes */
601 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
607 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
610 p += from_base64(&val, p);
611 plug(atts.dwFileAttributes, val);
612 p++; /* skip space */
613 p += from_base64(&val, p);
615 atts.ftCreationTime.dwLowDateTime = li.LowPart;
616 atts.ftCreationTime.dwHighDateTime = li.HighPart;
617 p++; /* skip space */
618 p += from_base64(&val, p);
620 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
621 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
622 p++; /* skip space */
623 p += from_base64(&val, p);
625 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
626 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
628 p += from_base64(&val, p);
629 plug(atts.nFileSizeHigh, val);
631 p += from_base64(&val, p);
632 plug(atts.nFileSizeLow, val);
634 /* Convert to Windows path format */
635 win32_ofile = get_pool_memory(PM_FNAME);
636 unix_name_to_win32(&win32_ofile, attr->ofname);
638 /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
640 if (!is_bopen(ofd)) {
641 Dmsg1(100, "File not open: %s\n", attr->ofname);
642 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
646 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
647 if (!SetFileTime(bget_handle(ofd),
648 &atts.ftCreationTime,
649 &atts.ftLastAccessTime,
650 &atts.ftLastWriteTime)) {
651 win_error(jcr, "SetFileTime:", win32_ofile);
656 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
657 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
659 if (p_SetFileAttributesW) {
660 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
661 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
663 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
664 free_pool_memory(pwszBuf);
667 win_error(jcr, "SetFileAttributesW:", win32_ofile);
670 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
671 win_error(jcr, "SetFileAttributesA:", win32_ofile);
675 free_pool_memory(win32_ofile);
679 void win_error(JCR *jcr, char *prefix, POOLMEM *win32_ofile)
681 DWORD lerror = GetLastError();
683 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
684 FORMAT_MESSAGE_FROM_SYSTEM,
691 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
692 strip_trailing_junk(msg);
693 Jmsg(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
697 void win_error(JCR *jcr, char *prefix, DWORD lerror)
700 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
701 FORMAT_MESSAGE_FROM_SYSTEM,
708 strip_trailing_junk(msg);
710 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
712 MessageBox(NULL, msg, prefix, MB_OK);
718 /* Conversion of a Unix filename to a Win32 filename */
719 extern void conv_unix_to_win32_path(const char *path, char *win32_path, DWORD dwSize);
720 void unix_name_to_win32(POOLMEM **win32_name, char *name)
722 /* One extra byte should suffice, but we double it */
723 /* add MAX_PATH bytes for VSS shadow copy name */
724 DWORD dwSize = 2*strlen(name)+MAX_PATH;
725 *win32_name = check_pool_memory_size(*win32_name, dwSize);
726 conv_unix_to_win32_path(name, *win32_name, dwSize);
729 #endif /* HAVE_CYGWIN */