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)
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 */
37 /* For old systems that don't have lchown() use chown() */
42 /*=============================================================*/
44 /* *** A l l S y s t e m s *** */
46 /*=============================================================*/
49 * Return the data stream that will be used
51 int select_data_stream(FF_PKT *ff_pkt)
56 * Fix all incompatible options
59 /* No sparse option for encrypted data */
60 if (ff_pkt->flags & FO_ENCRYPT) {
61 ff_pkt->flags &= ~FO_SPARSE;
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;
71 stream = STREAM_FILE_DATA;
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;
80 /* Compression is not supported for Mac fork data */
81 if (stream == STREAM_MACOS_FORK_DATA) {
82 ff_pkt->flags &= ~FO_GZIP;
86 * Handle compression and encryption options
89 if (ff_pkt->flags & FO_GZIP) {
91 case STREAM_WIN32_DATA:
92 stream = STREAM_WIN32_GZIP_DATA;
94 case STREAM_SPARSE_DATA:
95 stream = STREAM_SPARSE_GZIP_DATA;
97 case STREAM_FILE_DATA:
98 stream = STREAM_GZIP_DATA;
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));
109 if (ff_pkt->flags & FO_ENCRYPT) {
111 case STREAM_WIN32_DATA:
112 stream = STREAM_ENCRYPTED_WIN32_DATA;
114 case STREAM_WIN32_GZIP_DATA:
115 stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
117 case STREAM_FILE_DATA:
118 stream = STREAM_ENCRYPTED_FILE_DATA;
120 case STREAM_GZIP_DATA:
121 stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
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));
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
147 void encode_stat(char *buf, FF_PKT *ff_pkt, int data_stream)
150 struct stat *statp = &ff_pkt->statp;
152 * Encode a stat packet. I should have done this more intelligently
153 * with a length so that it could be easily expanded.
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);
159 p += to_base64((int64_t)statp->st_mode, p);
161 p += to_base64((int64_t)statp->st_nlink, p);
163 p += to_base64((int64_t)statp->st_uid, p);
165 p += to_base64((int64_t)statp->st_gid, p);
167 p += to_base64((int64_t)statp->st_rdev, p);
169 p += to_base64((int64_t)statp->st_size, p);
172 p += to_base64((int64_t)statp->st_blksize, p);
174 p += to_base64((int64_t)statp->st_blocks, p);
177 p += to_base64((int64_t)0, p); /* output place holder */
179 p += to_base64((int64_t)0, p); /* output place holder */
182 p += to_base64((int64_t)statp->st_atime, p);
184 p += to_base64((int64_t)statp->st_mtime, p);
186 p += to_base64((int64_t)statp->st_ctime, p);
188 p += to_base64((int64_t)ff_pkt->LinkFI, p);
192 /* FreeBSD function */
193 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
195 p += to_base64((int64_t)0, p); /* output place holder */
198 p += to_base64((int64_t)data_stream, p);
204 /* Do casting according to unknown type to keep compiler happy */
206 #define plug(st, val) st = (typeof st)val
208 #if !HAVE_GCC & HAVE_SUN_OS
209 /* Sun compiler does not handle templates correctly */
210 #define plug(st, val) st = val
212 /* Use templates to do the casting */
213 template <class T> void plug(T &st, uint64_t val)
214 { st = static_cast<T>(val); }
219 /* Decode a stat packet from base64 characters */
220 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
225 p += from_base64(&val, p);
226 plug(statp->st_dev, val);
228 p += from_base64(&val, p);
229 plug(statp->st_ino, val);
231 p += from_base64(&val, p);
232 plug(statp->st_mode, val);
234 p += from_base64(&val, p);
235 plug(statp->st_nlink, val);
237 p += from_base64(&val, p);
238 plug(statp->st_uid, val);
240 p += from_base64(&val, p);
241 plug(statp->st_gid, val);
243 p += from_base64(&val, p);
244 plug(statp->st_rdev, val);
246 p += from_base64(&val, p);
247 plug(statp->st_size, val);
250 p += from_base64(&val, p);
251 plug(statp->st_blksize, val);
253 p += from_base64(&val, p);
254 plug(statp->st_blocks, val);
257 p += from_base64(&val, p);
258 // plug(statp->st_blksize, val);
260 p += from_base64(&val, p);
261 // plug(statp->st_blocks, val);
264 p += from_base64(&val, p);
265 plug(statp->st_atime, val);
267 p += from_base64(&val, p);
268 plug(statp->st_mtime, val);
270 p += from_base64(&val, p);
271 plug(statp->st_ctime, val);
273 /* Optional FileIndex of hard linked file data */
274 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
276 p += from_base64(&val, p);
277 *LinkFI = (uint32_t)val;
283 /* FreeBSD user flags */
284 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
286 p += from_base64(&val, p);
288 plug(statp->st_flags, val);
294 /* Look for data stream id */
295 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
297 p += from_base64(&val, p);
304 /* Decode a LinkFI field of encoded stat packet */
305 int32_t decode_LinkFI(char *buf, struct stat *statp)
310 skip_nonspaces(&p); /* st_dev */
311 p++; /* skip space */
312 skip_nonspaces(&p); /* st_ino */
314 p += from_base64(&val, p);
315 plug(statp->st_mode, val); /* st_mode */
317 skip_nonspaces(&p); /* st_nlink */
319 skip_nonspaces(&p); /* st_uid */
321 skip_nonspaces(&p); /* st_gid */
323 skip_nonspaces(&p); /* st_rdev */
325 skip_nonspaces(&p); /* st_size */
327 skip_nonspaces(&p); /* st_blksize */
329 skip_nonspaces(&p); /* st_blocks */
331 skip_nonspaces(&p); /* st_atime */
333 skip_nonspaces(&p); /* st_mtime */
335 skip_nonspaces(&p); /* st_ctime */
337 /* Optional FileIndex of hard linked file data */
338 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
340 p += from_base64(&val, p);
347 * Set file modes, permissions and times
349 * fname is the original filename
350 * ofile is the output filename (may be in a different directory)
352 * Returns: true on success
355 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
362 #if defined(HAVE_WIN32)
363 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
364 set_win32_attributes(jcr, attr, ofd)) {
368 pm_strcpy(attr->ofname, "*none*");
371 if (attr->data_stream == STREAM_WIN32_DATA ||
372 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
376 pm_strcpy(attr->ofname, "*none*");
382 * If Windows stuff failed, e.g. attempt to restore Unix file
383 * to Windows, simply fall through and we will do it the
390 char ec1[50], ec2[50];
391 fsize = blseek(ofd, 0, SEEK_END);
392 bclose(ofd); /* first close file */
393 if (fsize > 0 && fsize != (off_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));
400 ut.actime = attr->statp.st_atime;
401 ut.modtime = attr->statp.st_mtime;
403 /* ***FIXME**** optimize -- don't do if already correct */
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.
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) {
412 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
413 attr->ofname, be.strerror());
417 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
419 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
420 attr->ofname, be.strerror());
423 if (chmod(attr->ofname, attr->statp.st_mode) < 0) {
425 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
426 attr->ofname, be.strerror());
433 if (utime(attr->ofname, &ut) < 0) {
435 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
436 attr->ofname, be.strerror());
443 * Note, this should really be done before the utime() above,
444 * but if the immutable bit is set, it will make the utimes()
447 if (chflags(attr->ofname, attr->statp.st_flags) < 0) {
449 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
450 attr->ofname, be.strerror());
455 pm_strcpy(attr->ofname, "*none*");
461 /*=============================================================*/
463 /* * * * U n i x * * * * */
465 /*=============================================================*/
467 #if !defined(HAVE_WIN32)
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.
478 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
480 #ifdef HAVE_DARWIN_OS
482 * We save the Mac resource fork length so that on a
483 * restore, we can be sure we put back the whole resource.
487 if (ff_pkt->flags & FO_HFSPLUS) {
488 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
492 *attribsEx = 0; /* no extended attributes */
494 return STREAM_UNIX_ATTRIBUTES;
501 /*=============================================================*/
503 /* * * * W i n 3 2 * * * * */
505 /*=============================================================*/
507 #if defined(HAVE_WIN32)
509 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
512 WIN32_FILE_ATTRIBUTE_DATA atts;
515 attribsEx[0] = 0; /* no extended attributes */
517 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
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);
524 BOOL b=p_GetFileAttributesExW((LPCWSTR) pwszBuf, GetFileExInfoStandard, (LPVOID)&atts);
525 free_pool_memory(pwszBuf);
528 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
529 return STREAM_UNIX_ATTRIBUTES;
533 if (!p_GetFileAttributesExA)
534 return STREAM_UNIX_ATTRIBUTES;
536 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
538 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
539 return STREAM_UNIX_ATTRIBUTES;
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);
549 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
550 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
551 p += to_base64((uint64_t)li.QuadPart, p);
553 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
554 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
555 p += to_base64((uint64_t)li.QuadPart, p);
557 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
559 p += to_base64((uint64_t)atts.nFileSizeLow, p);
561 return STREAM_UNIX_ATTRIBUTES_EX;
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)
577 * Set Extended File Attributes for Win32
579 * fname is the original filename
580 * ofile is the output filename (may be in a different directory)
582 * Returns: true on success
585 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
587 char *p = attr->attrEx;
589 WIN32_FILE_ATTRIBUTE_DATA atts;
591 POOLMEM *win32_ofile;
593 // if we have neither ansi nor wchar version, we leave
594 if (!(p_SetFileAttributesW || p_SetFileAttributesA))
597 if (!p || !*p) { /* we should have attributes */
598 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
604 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
607 p += from_base64(&val, p);
608 plug(atts.dwFileAttributes, val);
609 p++; /* skip space */
610 p += from_base64(&val, p);
612 atts.ftCreationTime.dwLowDateTime = li.LowPart;
613 atts.ftCreationTime.dwHighDateTime = li.HighPart;
614 p++; /* skip space */
615 p += from_base64(&val, p);
617 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
618 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
619 p++; /* skip space */
620 p += from_base64(&val, p);
622 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
623 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
625 p += from_base64(&val, p);
626 plug(atts.nFileSizeHigh, val);
628 p += from_base64(&val, p);
629 plug(atts.nFileSizeLow, val);
631 /* Convert to Windows path format */
632 win32_ofile = get_pool_memory(PM_FNAME);
633 unix_name_to_win32(&win32_ofile, attr->ofname);
635 /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
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 */
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);
653 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
654 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
656 if (p_SetFileAttributesW) {
657 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
658 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
660 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
661 free_pool_memory(pwszBuf);
664 win_error(jcr, "SetFileAttributesW:", win32_ofile);
667 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
668 win_error(jcr, "SetFileAttributesA:", win32_ofile);
672 free_pool_memory(win32_ofile);
676 void win_error(JCR *jcr, char *prefix, POOLMEM *win32_ofile)
678 DWORD lerror = GetLastError();
680 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
681 FORMAT_MESSAGE_FROM_SYSTEM,
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);
694 void win_error(JCR *jcr, char *prefix, DWORD lerror)
697 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
698 FORMAT_MESSAGE_FROM_SYSTEM,
705 strip_trailing_junk(msg);
707 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
709 MessageBox(NULL, msg, prefix, MB_OK);
713 #endif /* HAVE_WIN32 */