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)
59 /* Note, no sparse option for win32_data */
60 if (!is_portable_backup(&ff_pkt->bfd)) {
61 stream = STREAM_WIN32_DATA;
62 ff_pkt->flags &= ~FO_SPARSE;
63 } else if (ff_pkt->flags & FO_SPARSE) {
64 stream = STREAM_SPARSE_DATA;
66 stream = STREAM_FILE_DATA;
69 if (ff_pkt->flags & FO_GZIP) {
70 if (stream == STREAM_WIN32_DATA) {
71 stream = STREAM_WIN32_GZIP_DATA;
72 } else if (stream == STREAM_FILE_DATA) {
73 stream = STREAM_GZIP_DATA;
75 stream = STREAM_SPARSE_GZIP_DATA;
84 * Encode a stat structure into a base64 character string
85 * All systems must create such a structure.
86 * In addition, we tack on the LinkFI, which is non-zero in
87 * the case of a hard linked file that has no data. This
88 * is a File Index pointing to the link that does have the
89 * data (always the first one encountered in a save).
90 * You may piggyback attributes on this packet by encoding
91 * them in the encode_attribsEx() subroutine, but this is
94 void encode_stat(char *buf, FF_PKT *ff_pkt, int data_stream)
97 struct stat *statp = &ff_pkt->statp;
99 * Encode a stat packet. I should have done this more intelligently
100 * with a length so that it could be easily expanded.
102 p += to_base64((int64_t)statp->st_dev, p);
103 *p++ = ' '; /* separate fields with a space */
104 p += to_base64((int64_t)statp->st_ino, p);
106 p += to_base64((int64_t)statp->st_mode, p);
108 p += to_base64((int64_t)statp->st_nlink, p);
110 p += to_base64((int64_t)statp->st_uid, p);
112 p += to_base64((int64_t)statp->st_gid, p);
114 p += to_base64((int64_t)statp->st_rdev, p);
116 p += to_base64((int64_t)statp->st_size, p);
119 p += to_base64((int64_t)statp->st_blksize, p);
121 p += to_base64((int64_t)statp->st_blocks, p);
124 p += to_base64((int64_t)0, p); /* output place holder */
126 p += to_base64((int64_t)0, p); /* output place holder */
129 p += to_base64((int64_t)statp->st_atime, p);
131 p += to_base64((int64_t)statp->st_mtime, p);
133 p += to_base64((int64_t)statp->st_ctime, p);
135 p += to_base64((int64_t)ff_pkt->LinkFI, p);
139 /* FreeBSD function */
140 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
142 p += to_base64((int64_t)0, p); /* output place holder */
145 p += to_base64((int64_t)data_stream, p);
151 /* Do casting according to unknown type to keep compiler happy */
152 #if !HAVE_GCC & HAVE_SUN_OS
153 #define plug(st, val) st = val /* brain damaged compiler */
155 template <class T> void plug(T &st, uint64_t val)
156 { st = static_cast<T>(val); }
160 /* Decode a stat packet from base64 characters */
161 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
166 p += from_base64(&val, p);
167 plug(statp->st_dev, val);
169 p += from_base64(&val, p);
170 plug(statp->st_ino, val);
172 p += from_base64(&val, p);
173 plug(statp->st_mode, val);
175 p += from_base64(&val, p);
176 plug(statp->st_nlink, val);
178 p += from_base64(&val, p);
179 plug(statp->st_uid, val);
181 p += from_base64(&val, p);
182 plug(statp->st_gid, val);
184 p += from_base64(&val, p);
185 plug(statp->st_rdev, val);
187 p += from_base64(&val, p);
188 plug(statp->st_size, val);
191 p += from_base64(&val, p);
192 plug(statp->st_blksize, val);
194 p += from_base64(&val, p);
195 plug(statp->st_blocks, val);
198 p += from_base64(&val, p);
199 // plug(statp->st_blksize, val);
201 p += from_base64(&val, p);
202 // plug(statp->st_blocks, val);
205 p += from_base64(&val, p);
206 plug(statp->st_atime, val);
208 p += from_base64(&val, p);
209 plug(statp->st_mtime, val);
211 p += from_base64(&val, p);
212 plug(statp->st_ctime, val);
214 /* Optional FileIndex of hard linked file data */
215 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
217 p += from_base64(&val, p);
218 *LinkFI = (uint32_t)val;
224 /* FreeBSD user flags */
225 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
227 p += from_base64(&val, p);
229 plug(statp->st_flags, val);
235 /* Look for data stream id */
236 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
238 p += from_base64(&val, p);
245 /* Decode a LinkFI field of encoded stat packet */
246 int32_t decode_LinkFI(char *buf, struct stat *statp)
251 skip_nonspaces(&p); /* st_dev */
252 p++; /* skip space */
253 skip_nonspaces(&p); /* st_ino */
255 p += from_base64(&val, p);
256 plug(statp->st_mode, val); /* st_mode */
258 skip_nonspaces(&p); /* st_nlink */
260 skip_nonspaces(&p); /* st_uid */
262 skip_nonspaces(&p); /* st_gid */
264 skip_nonspaces(&p); /* st_rdev */
266 skip_nonspaces(&p); /* st_size */
268 skip_nonspaces(&p); /* st_blksize */
270 skip_nonspaces(&p); /* st_blocks */
272 skip_nonspaces(&p); /* st_atime */
274 skip_nonspaces(&p); /* st_mtime */
276 skip_nonspaces(&p); /* st_ctime */
278 /* Optional FileIndex of hard linked file data */
279 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
281 p += from_base64(&val, p);
288 * Set file modes, permissions and times
290 * fname is the original filename
291 * ofile is the output filename (may be in a different directory)
293 * Returns: true on success
296 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
303 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
304 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
305 set_win32_attributes(jcr, attr, ofd)) {
309 pm_strcpy(attr->ofname, "*none*");
312 if (attr->data_stream == STREAM_WIN32_DATA ||
313 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
317 pm_strcpy(attr->ofname, "*none*");
323 * If Windows stuff failed, e.g. attempt to restore Unix file
324 * to Windows, simply fall through and we will do it the
331 char ec1[50], ec2[50];
332 fsize = blseek(ofd, 0, SEEK_END);
333 bclose(ofd); /* first close file */
334 if (fsize > 0 && fsize != (off_t)attr->statp.st_size) {
335 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
336 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
337 edit_uint64(fsize, ec2));
341 ut.actime = attr->statp.st_atime;
342 ut.modtime = attr->statp.st_mtime;
344 /* ***FIXME**** optimize -- don't do if already correct */
346 * For link, change owner of link using lchown, but don't
347 * try to do a chmod as that will update the file behind it.
349 if (attr->type == FT_LNK) {
350 /* Change owner of link, not of real file */
351 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
353 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
354 attr->ofname, be.strerror());
358 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
360 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
361 attr->ofname, be.strerror());
364 if (chmod(attr->ofname, attr->statp.st_mode) < 0) {
366 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
367 attr->ofname, be.strerror());
374 if (utime(attr->ofname, &ut) < 0) {
376 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
377 attr->ofname, be.strerror());
384 * Note, this should really be done before the utime() above,
385 * but if the immutable bit is set, it will make the utimes()
388 if (chflags(attr->ofname, attr->statp.st_flags) < 0) {
390 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
391 attr->ofname, be.strerror());
396 pm_strcpy(attr->ofname, "*none*");
402 /*=============================================================*/
404 /* * * * U n i x * * * * */
406 /*=============================================================*/
408 #if !defined(HAVE_CYGWIN) && !defined(HAVE_WIN32)
411 * It is possible to piggyback additional data e.g. ACLs on
412 * the encode_stat() data by returning the extended attributes
413 * here. They must be "self-contained" (i.e. you keep track
414 * of your own length), and they must be in ASCII string
415 * format. Using this feature is not recommended.
416 * The code below shows how to return nothing. See the Win32
417 * code below for returning something in the attributes.
419 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
421 #ifdef HAVE_DARWIN_OS
423 * We save the Mac resource fork length so that on a
424 * restore, we can be sure we put back the whole resource.
428 if (ff_pkt->flags & FO_HFSPLUS) {
429 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
433 *attribsEx = 0; /* no extended attributes */
435 return STREAM_UNIX_ATTRIBUTES;
442 /*=============================================================*/
444 /* * * * W i n 3 2 * * * * */
446 /*=============================================================*/
448 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
450 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
453 WIN32_FILE_ATTRIBUTE_DATA atts;
456 attribsEx[0] = 0; /* no extended attributes */
458 // try unicode version
459 if (p_GetFileAttributesExW) {
460 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
462 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
463 UTF8_2_wchar(&pwszBuf, ff_pkt->sys_fname);
465 BOOL b=p_GetFileAttributesExW((LPCWSTR) pwszBuf, GetFileExInfoStandard, (LPVOID)&atts);
466 free_pool_memory(pwszBuf);
469 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
470 return STREAM_UNIX_ATTRIBUTES;
474 if (!p_GetFileAttributesExA)
475 return STREAM_UNIX_ATTRIBUTES;
477 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
479 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
481 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
482 return STREAM_UNIX_ATTRIBUTES;
486 p += to_base64((uint64_t)atts.dwFileAttributes, p);
487 *p++ = ' '; /* separate fields with a space */
488 li.LowPart = atts.ftCreationTime.dwLowDateTime;
489 li.HighPart = atts.ftCreationTime.dwHighDateTime;
490 p += to_base64((uint64_t)li.QuadPart, p);
492 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
493 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
494 p += to_base64((uint64_t)li.QuadPart, p);
496 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
497 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
498 p += to_base64((uint64_t)li.QuadPart, p);
500 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
502 p += to_base64((uint64_t)atts.nFileSizeLow, p);
504 return STREAM_UNIX_ATTRIBUTES_EX;
507 /* Define attributes that are legal to set with SetFileAttributes() */
508 #define SET_ATTRS ( \
509 FILE_ATTRIBUTE_ARCHIVE| \
510 FILE_ATTRIBUTE_HIDDEN| \
511 FILE_ATTRIBUTE_NORMAL| \
512 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
513 FILE_ATTRIBUTE_OFFLINE| \
514 FILE_ATTRIBUTE_READONLY| \
515 FILE_ATTRIBUTE_SYSTEM| \
516 FILE_ATTRIBUTE_TEMPORARY)
520 * Set Extended File Attributes for Win32
522 * fname is the original filename
523 * ofile is the output filename (may be in a different directory)
525 * Returns: true on success
528 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
530 char *p = attr->attrEx;
532 WIN32_FILE_ATTRIBUTE_DATA atts;
534 POOLMEM *win32_ofile;
536 // if we have neither ansi nor wchar version, we leave
537 if (!(p_SetFileAttributesW || p_SetFileAttributesA))
540 if (!p || !*p) { /* we should have attributes */
541 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
547 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
550 p += from_base64(&val, p);
551 plug(atts.dwFileAttributes, val);
552 p++; /* skip space */
553 p += from_base64(&val, p);
555 atts.ftCreationTime.dwLowDateTime = li.LowPart;
556 atts.ftCreationTime.dwHighDateTime = li.HighPart;
557 p++; /* skip space */
558 p += from_base64(&val, p);
560 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
561 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
562 p++; /* skip space */
563 p += from_base64(&val, p);
565 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
566 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
568 p += from_base64(&val, p);
569 plug(atts.nFileSizeHigh, val);
571 p += from_base64(&val, p);
572 plug(atts.nFileSizeLow, val);
574 /* Convert to Windows path format */
575 win32_ofile = get_pool_memory(PM_FNAME);
576 unix_name_to_win32(&win32_ofile, attr->ofname);
580 /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
582 if (!is_bopen(ofd)) {
583 Dmsg1(100, "File not open: %s\n", attr->ofname);
584 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
588 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
589 if (!SetFileTime(bget_handle(ofd),
590 &atts.ftCreationTime,
591 &atts.ftLastAccessTime,
592 &atts.ftLastWriteTime)) {
593 win_error(jcr, "SetFileTime:", win32_ofile);
598 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
599 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
601 if (p_SetFileAttributesW) {
602 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
603 UTF8_2_wchar(&pwszBuf, win32_ofile);
605 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
606 free_pool_memory(pwszBuf);
609 win_error(jcr, "SetFileAttributesW:", win32_ofile);
612 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
613 win_error(jcr, "SetFileAttributesA:", win32_ofile);
617 free_pool_memory(win32_ofile);
621 void win_error(JCR *jcr, char *prefix, POOLMEM *win32_ofile)
623 DWORD lerror = GetLastError();
625 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
626 FORMAT_MESSAGE_FROM_SYSTEM,
633 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
634 strip_trailing_junk(msg);
635 Jmsg(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
639 void win_error(JCR *jcr, char *prefix, DWORD lerror)
642 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
643 FORMAT_MESSAGE_FROM_SYSTEM,
650 strip_trailing_junk(msg);
652 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
654 MessageBox(NULL, msg, prefix, MB_OK);
660 /* Conversion of a Unix filename to a Win32 filename */
661 extern void conv_unix_to_win32_path(const char *path, char *win32_path, DWORD dwSize);
662 void unix_name_to_win32(POOLMEM **win32_name, char *name)
664 /* One extra byte should suffice, but we double it */
665 /* add MAX_PATH bytes for VSS shadow copy name */
666 DWORD dwSize = 2*strlen(name)+MAX_PATH;
667 *win32_name = check_pool_memory_size(*win32_name, dwSize);
668 conv_unix_to_win32_path(name, *win32_name, dwSize);
671 #endif /* HAVE_CYGWIN */