2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Encode and decode standard Unix attributes and
30 * Extended attributes for Win32 and
31 * other non-Unix systems, or Unix systems with ACLs, ...
33 * Kern Sibbald, October MMII
42 #if defined(HAVE_WIN32)
43 /* Forward referenced subroutines */
44 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd);
45 void unix_name_to_win32(POOLMEM **win32_name, char *name);
46 void win_error(JCR *jcr, char *prefix, POOLMEM *ofile);
47 HANDLE bget_handle(BFILE *bfd);
48 #endif /* HAVE_WIN32 */
50 /* For old systems that don't have lchown() use chown() */
55 /*=============================================================*/
57 /* *** A l l S y s t e m s *** */
59 /*=============================================================*/
62 * Return the data stream that will be used
64 int select_data_stream(FF_PKT *ff_pkt)
69 * Fix all incompatible options
72 /* No sparse option for encrypted data */
73 if (ff_pkt->flags & FO_ENCRYPT) {
74 ff_pkt->flags &= ~FO_SPARSE;
77 /* Note, no sparse option for win32_data */
78 if (!is_portable_backup(&ff_pkt->bfd)) {
79 stream = STREAM_WIN32_DATA;
80 ff_pkt->flags &= ~FO_SPARSE;
81 } else if (ff_pkt->flags & FO_SPARSE) {
82 stream = STREAM_SPARSE_DATA;
84 stream = STREAM_FILE_DATA;
87 /* Encryption is only supported for file data */
88 if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
89 stream != STREAM_MACOS_FORK_DATA) {
90 ff_pkt->flags &= ~FO_ENCRYPT;
93 /* Compression is not supported for Mac fork data */
94 if (stream == STREAM_MACOS_FORK_DATA) {
95 ff_pkt->flags &= ~FO_GZIP;
99 * Handle compression and encryption options
102 if (ff_pkt->flags & FO_GZIP) {
104 case STREAM_WIN32_DATA:
105 stream = STREAM_WIN32_GZIP_DATA;
107 case STREAM_SPARSE_DATA:
108 stream = STREAM_SPARSE_GZIP_DATA;
110 case STREAM_FILE_DATA:
111 stream = STREAM_GZIP_DATA;
114 /* All stream types that do not support gzip should clear out
115 * FO_GZIP above, and this code block should be unreachable. */
116 ASSERT(!(ff_pkt->flags & FO_GZIP));
122 if (ff_pkt->flags & FO_ENCRYPT) {
124 case STREAM_WIN32_DATA:
125 stream = STREAM_ENCRYPTED_WIN32_DATA;
127 case STREAM_WIN32_GZIP_DATA:
128 stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
130 case STREAM_FILE_DATA:
131 stream = STREAM_ENCRYPTED_FILE_DATA;
133 case STREAM_GZIP_DATA:
134 stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
137 /* All stream types that do not support encryption should clear out
138 * FO_ENCRYPT above, and this code block should be unreachable. */
139 ASSERT(!(ff_pkt->flags & FO_ENCRYPT));
150 * Encode a stat structure into a base64 character string
151 * All systems must create such a structure.
152 * In addition, we tack on the LinkFI, which is non-zero in
153 * the case of a hard linked file that has no data. This
154 * is a File Index pointing to the link that does have the
155 * data (always the first one encountered in a save).
156 * You may piggyback attributes on this packet by encoding
157 * them in the encode_attribsEx() subroutine, but this is
160 void encode_stat(char *buf, FF_PKT *ff_pkt, int data_stream)
163 struct stat *statp = &ff_pkt->statp;
165 * Encode a stat packet. I should have done this more intelligently
166 * with a length so that it could be easily expanded.
168 p += to_base64((int64_t)statp->st_dev, p);
169 *p++ = ' '; /* separate fields with a space */
170 p += to_base64((int64_t)statp->st_ino, p);
172 p += to_base64((int64_t)statp->st_mode, p);
174 p += to_base64((int64_t)statp->st_nlink, p);
176 p += to_base64((int64_t)statp->st_uid, p);
178 p += to_base64((int64_t)statp->st_gid, p);
180 p += to_base64((int64_t)statp->st_rdev, p);
182 p += to_base64((int64_t)statp->st_size, p);
185 p += to_base64((int64_t)statp->st_blksize, p);
187 p += to_base64((int64_t)statp->st_blocks, p);
190 p += to_base64((int64_t)0, p); /* output place holder */
192 p += to_base64((int64_t)0, p); /* output place holder */
195 p += to_base64((int64_t)statp->st_atime, p);
197 p += to_base64((int64_t)statp->st_mtime, p);
199 p += to_base64((int64_t)statp->st_ctime, p);
201 p += to_base64((int64_t)ff_pkt->LinkFI, p);
205 /* FreeBSD function */
206 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
208 p += to_base64((int64_t)0, p); /* output place holder */
211 p += to_base64((int64_t)data_stream, p);
217 /* Do casting according to unknown type to keep compiler happy */
219 #define plug(st, val) st = (typeof st)val
221 #if !HAVE_GCC & HAVE_SUN_OS
222 /* Sun compiler does not handle templates correctly */
223 #define plug(st, val) st = val
225 /* Use templates to do the casting */
226 template <class T> void plug(T &st, uint64_t val)
227 { st = static_cast<T>(val); }
232 /* Decode a stat packet from base64 characters */
233 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
238 p += from_base64(&val, p);
239 plug(statp->st_dev, val);
241 p += from_base64(&val, p);
242 plug(statp->st_ino, val);
244 p += from_base64(&val, p);
245 plug(statp->st_mode, val);
247 p += from_base64(&val, p);
248 plug(statp->st_nlink, val);
250 p += from_base64(&val, p);
251 plug(statp->st_uid, val);
253 p += from_base64(&val, p);
254 plug(statp->st_gid, val);
256 p += from_base64(&val, p);
257 plug(statp->st_rdev, val);
259 p += from_base64(&val, p);
260 plug(statp->st_size, val);
263 p += from_base64(&val, p);
264 plug(statp->st_blksize, val);
266 p += from_base64(&val, p);
267 plug(statp->st_blocks, val);
270 p += from_base64(&val, p);
271 // plug(statp->st_blksize, val);
273 p += from_base64(&val, p);
274 // plug(statp->st_blocks, val);
277 p += from_base64(&val, p);
278 plug(statp->st_atime, val);
280 p += from_base64(&val, p);
281 plug(statp->st_mtime, val);
283 p += from_base64(&val, p);
284 plug(statp->st_ctime, val);
286 /* Optional FileIndex of hard linked file data */
287 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
289 p += from_base64(&val, p);
290 *LinkFI = (uint32_t)val;
296 /* FreeBSD user flags */
297 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
299 p += from_base64(&val, p);
301 plug(statp->st_flags, val);
307 /* Look for data stream id */
308 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
310 p += from_base64(&val, p);
317 /* Decode a LinkFI field of encoded stat packet */
318 int32_t decode_LinkFI(char *buf, struct stat *statp)
323 skip_nonspaces(&p); /* st_dev */
324 p++; /* skip space */
325 skip_nonspaces(&p); /* st_ino */
327 p += from_base64(&val, p);
328 plug(statp->st_mode, val); /* st_mode */
330 skip_nonspaces(&p); /* st_nlink */
332 skip_nonspaces(&p); /* st_uid */
334 skip_nonspaces(&p); /* st_gid */
336 skip_nonspaces(&p); /* st_rdev */
338 skip_nonspaces(&p); /* st_size */
340 skip_nonspaces(&p); /* st_blksize */
342 skip_nonspaces(&p); /* st_blocks */
344 skip_nonspaces(&p); /* st_atime */
346 skip_nonspaces(&p); /* st_mtime */
348 skip_nonspaces(&p); /* st_ctime */
350 /* Optional FileIndex of hard linked file data */
351 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
353 p += from_base64(&val, p);
360 * Set file modes, permissions and times
362 * fname is the original filename
363 * ofile is the output filename (may be in a different directory)
365 * Returns: true on success
368 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
375 #if defined(HAVE_WIN32)
376 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
377 set_win32_attributes(jcr, attr, ofd)) {
381 pm_strcpy(attr->ofname, "*none*");
384 if (attr->data_stream == STREAM_WIN32_DATA ||
385 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
389 pm_strcpy(attr->ofname, "*none*");
395 * If Windows stuff failed, e.g. attempt to restore Unix file
396 * to Windows, simply fall through and we will do it the
403 char ec1[50], ec2[50];
404 fsize = blseek(ofd, 0, SEEK_END);
405 bclose(ofd); /* first close file */
406 if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 &&
407 fsize != (boffset_t)attr->statp.st_size) {
408 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
409 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
410 edit_uint64(fsize, ec2));
415 * We do not restore sockets, so skip trying to restore their
418 if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
422 ut.actime = attr->statp.st_atime;
423 ut.modtime = attr->statp.st_mtime;
425 /* ***FIXME**** optimize -- don't do if already correct */
427 * For link, change owner of link using lchown, but don't
428 * try to do a chmod as that will update the file behind it.
430 if (attr->type == FT_LNK) {
431 /* Change owner of link, not of real file */
432 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
434 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
435 attr->ofname, be.bstrerror());
439 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
441 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
442 attr->ofname, be.bstrerror());
445 if (chmod(attr->ofname, attr->statp.st_mode) < 0) {
447 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
448 attr->ofname, be.bstrerror());
455 if (utime(attr->ofname, &ut) < 0) {
457 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
458 attr->ofname, be.bstrerror());
465 * Note, this should really be done before the utime() above,
466 * but if the immutable bit is set, it will make the utimes()
469 if (chflags(attr->ofname, attr->statp.st_flags) < 0) {
471 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
472 attr->ofname, be.bstrerror());
479 pm_strcpy(attr->ofname, "*none*");
485 /*=============================================================*/
487 /* * * * U n i x * * * * */
489 /*=============================================================*/
491 #if !defined(HAVE_WIN32)
494 * It is possible to piggyback additional data e.g. ACLs on
495 * the encode_stat() data by returning the extended attributes
496 * here. They must be "self-contained" (i.e. you keep track
497 * of your own length), and they must be in ASCII string
498 * format. Using this feature is not recommended.
499 * The code below shows how to return nothing. See the Win32
500 * code below for returning something in the attributes.
502 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
504 #ifdef HAVE_DARWIN_OS
506 * We save the Mac resource fork length so that on a
507 * restore, we can be sure we put back the whole resource.
511 if (ff_pkt->flags & FO_HFSPLUS) {
512 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
516 *attribsEx = 0; /* no extended attributes */
518 return STREAM_UNIX_ATTRIBUTES;
525 /*=============================================================*/
527 /* * * * W i n 3 2 * * * * */
529 /*=============================================================*/
531 #if defined(HAVE_WIN32)
533 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
536 WIN32_FILE_ATTRIBUTE_DATA atts;
539 attribsEx[0] = 0; /* no extended attributes */
541 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
543 // try unicode version
544 if (p_GetFileAttributesExW) {
545 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
546 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
548 BOOL b=p_GetFileAttributesExW((LPCWSTR) pwszBuf, GetFileExInfoStandard, (LPVOID)&atts);
549 free_pool_memory(pwszBuf);
552 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
553 return STREAM_UNIX_ATTRIBUTES;
557 if (!p_GetFileAttributesExA)
558 return STREAM_UNIX_ATTRIBUTES;
560 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
562 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
563 return STREAM_UNIX_ATTRIBUTES;
567 p += to_base64((uint64_t)atts.dwFileAttributes, p);
568 *p++ = ' '; /* separate fields with a space */
569 li.LowPart = atts.ftCreationTime.dwLowDateTime;
570 li.HighPart = atts.ftCreationTime.dwHighDateTime;
571 p += to_base64((uint64_t)li.QuadPart, p);
573 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
574 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
575 p += to_base64((uint64_t)li.QuadPart, p);
577 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
578 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
579 p += to_base64((uint64_t)li.QuadPart, p);
581 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
583 p += to_base64((uint64_t)atts.nFileSizeLow, p);
585 return STREAM_UNIX_ATTRIBUTES_EX;
588 /* Define attributes that are legal to set with SetFileAttributes() */
589 #define SET_ATTRS ( \
590 FILE_ATTRIBUTE_ARCHIVE| \
591 FILE_ATTRIBUTE_HIDDEN| \
592 FILE_ATTRIBUTE_NORMAL| \
593 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
594 FILE_ATTRIBUTE_OFFLINE| \
595 FILE_ATTRIBUTE_READONLY| \
596 FILE_ATTRIBUTE_SYSTEM| \
597 FILE_ATTRIBUTE_TEMPORARY)
601 * Set Extended File Attributes for Win32
603 * fname is the original filename
604 * ofile is the output filename (may be in a different directory)
606 * Returns: true on success
609 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
611 char *p = attr->attrEx;
613 WIN32_FILE_ATTRIBUTE_DATA atts;
615 POOLMEM *win32_ofile;
617 // if we have neither ansi nor wchar version, we leave
618 if (!(p_SetFileAttributesW || p_SetFileAttributesA))
621 if (!p || !*p) { /* we should have attributes */
622 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
628 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
631 p += from_base64(&val, p);
632 plug(atts.dwFileAttributes, val);
633 p++; /* skip space */
634 p += from_base64(&val, p);
636 atts.ftCreationTime.dwLowDateTime = li.LowPart;
637 atts.ftCreationTime.dwHighDateTime = li.HighPart;
638 p++; /* skip space */
639 p += from_base64(&val, p);
641 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
642 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
643 p++; /* skip space */
644 p += from_base64(&val, p);
646 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
647 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
649 p += from_base64(&val, p);
650 plug(atts.nFileSizeHigh, val);
652 p += from_base64(&val, p);
653 plug(atts.nFileSizeLow, val);
655 /* Convert to Windows path format */
656 win32_ofile = get_pool_memory(PM_FNAME);
657 unix_name_to_win32(&win32_ofile, attr->ofname);
659 /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
661 if (!is_bopen(ofd)) {
662 Dmsg1(100, "File not open: %s\n", attr->ofname);
663 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
667 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
668 if (!SetFileTime(bget_handle(ofd),
669 &atts.ftCreationTime,
670 &atts.ftLastAccessTime,
671 &atts.ftLastWriteTime)) {
672 win_error(jcr, "SetFileTime:", win32_ofile);
677 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
678 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
680 if (p_SetFileAttributesW) {
681 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
682 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
684 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
685 free_pool_memory(pwszBuf);
688 win_error(jcr, "SetFileAttributesW:", win32_ofile);
691 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
692 win_error(jcr, "SetFileAttributesA:", win32_ofile);
696 free_pool_memory(win32_ofile);
700 void win_error(JCR *jcr, char *prefix, POOLMEM *win32_ofile)
702 DWORD lerror = GetLastError();
704 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
705 FORMAT_MESSAGE_FROM_SYSTEM,
712 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
713 strip_trailing_junk(msg);
714 Jmsg(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
718 void win_error(JCR *jcr, char *prefix, DWORD lerror)
721 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
722 FORMAT_MESSAGE_FROM_SYSTEM,
729 strip_trailing_junk(msg);
731 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
733 MessageBox(NULL, msg, prefix, MB_OK);
737 #endif /* HAVE_WIN32 */