2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2010 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 three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
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 Affero 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 Kern Sibbald.
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
40 static uid_t my_uid = 1;
41 static gid_t my_gid = 1;
42 static bool uid_set = false;
45 #if defined(HAVE_WIN32)
46 /* Forward referenced subroutines */
47 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd);
48 void unix_name_to_win32(POOLMEM **win32_name, char *name);
49 void win_error(JCR *jcr, const char *prefix, POOLMEM *ofile);
50 HANDLE bget_handle(BFILE *bfd);
51 #endif /* HAVE_WIN32 */
53 /* For old systems that don't have lchown() use chown() */
58 /*=============================================================*/
60 /* *** A l l S y s t e m s *** */
62 /*=============================================================*/
65 * Return the data stream that will be used
67 int select_data_stream(FF_PKT *ff_pkt)
71 /* This is a plugin special restore object */
72 if (ff_pkt->type == FT_RESTORE_FIRST) {
74 return STREAM_FILE_DATA;
78 * Fix all incompatible options
80 /** No sparse option for encrypted data */
81 if (ff_pkt->flags & FO_ENCRYPT) {
82 ff_pkt->flags &= ~FO_SPARSE;
85 /** Note, no sparse option for win32_data */
86 if (!is_portable_backup(&ff_pkt->bfd)) {
87 stream = STREAM_WIN32_DATA;
88 ff_pkt->flags &= ~FO_SPARSE;
89 } else if (ff_pkt->flags & FO_SPARSE) {
90 stream = STREAM_SPARSE_DATA;
92 stream = STREAM_FILE_DATA;
94 if (ff_pkt->flags & FO_OFFSETS) {
95 stream = STREAM_SPARSE_DATA;
98 /** Encryption is only supported for file data */
99 if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
100 stream != STREAM_MACOS_FORK_DATA) {
101 ff_pkt->flags &= ~FO_ENCRYPT;
104 /** Compression is not supported for Mac fork data */
105 if (stream == STREAM_MACOS_FORK_DATA) {
106 ff_pkt->flags &= ~FO_GZIP;
110 * Handle compression and encryption options
113 if (ff_pkt->flags & FO_GZIP) {
115 case STREAM_WIN32_DATA:
116 stream = STREAM_WIN32_GZIP_DATA;
118 case STREAM_SPARSE_DATA:
119 stream = STREAM_SPARSE_GZIP_DATA;
121 case STREAM_FILE_DATA:
122 stream = STREAM_GZIP_DATA;
126 * All stream types that do not support gzip should clear out
127 * FO_GZIP above, and this code block should be unreachable.
129 ASSERT(!(ff_pkt->flags & FO_GZIP));
135 if (ff_pkt->flags & FO_ENCRYPT) {
137 case STREAM_WIN32_DATA:
138 stream = STREAM_ENCRYPTED_WIN32_DATA;
140 case STREAM_WIN32_GZIP_DATA:
141 stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
143 case STREAM_FILE_DATA:
144 stream = STREAM_ENCRYPTED_FILE_DATA;
146 case STREAM_GZIP_DATA:
147 stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
150 /* All stream types that do not support encryption should clear out
151 * FO_ENCRYPT above, and this code block should be unreachable. */
152 ASSERT(!(ff_pkt->flags & FO_ENCRYPT));
163 * Encode a stat structure into a base64 character string
164 * All systems must create such a structure.
165 * In addition, we tack on the LinkFI, which is non-zero in
166 * the case of a hard linked file that has no data. This
167 * is a File Index pointing to the link that does have the
168 * data (always the first one encountered in a save).
169 * You may piggyback attributes on this packet by encoding
170 * them in the encode_attribsEx() subroutine, but this is
173 void encode_stat(char *buf, struct stat *statp, int32_t LinkFI, int data_stream)
178 * Encode a stat packet. I should have done this more intelligently
179 * with a length so that it could be easily expanded.
181 p += to_base64((int64_t)statp->st_dev, p);
182 *p++ = ' '; /* separate fields with a space */
183 p += to_base64((int64_t)statp->st_ino, p);
185 p += to_base64((int64_t)statp->st_mode, p);
187 p += to_base64((int64_t)statp->st_nlink, p);
189 p += to_base64((int64_t)statp->st_uid, p);
191 p += to_base64((int64_t)statp->st_gid, p);
193 p += to_base64((int64_t)statp->st_rdev, p);
195 p += to_base64((int64_t)statp->st_size, p);
198 p += to_base64((int64_t)statp->st_blksize, p);
200 p += to_base64((int64_t)statp->st_blocks, p);
203 p += to_base64((int64_t)0, p); /* output place holder */
205 p += to_base64((int64_t)0, p); /* output place holder */
208 p += to_base64((int64_t)statp->st_atime, p);
210 p += to_base64((int64_t)statp->st_mtime, p);
212 p += to_base64((int64_t)statp->st_ctime, p);
214 p += to_base64((int64_t)LinkFI, p);
218 /* FreeBSD function */
219 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
221 p += to_base64((int64_t)0, p); /* output place holder */
224 p += to_base64((int64_t)data_stream, p);
230 /* Do casting according to unknown type to keep compiler happy */
232 #define plug(st, val) st = (typeof st)val
234 #if !HAVE_GCC & HAVE_SUN_OS
235 /* Sun compiler does not handle templates correctly */
236 #define plug(st, val) st = val
238 #define plug(st, val) st = val
240 /* Use templates to do the casting */
241 template <class T> void plug(T &st, uint64_t val)
242 { st = static_cast<T>(val); }
247 /** Decode a stat packet from base64 characters */
248 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
253 p += from_base64(&val, p);
254 plug(statp->st_dev, val);
256 p += from_base64(&val, p);
257 plug(statp->st_ino, val);
259 p += from_base64(&val, p);
260 plug(statp->st_mode, val);
262 p += from_base64(&val, p);
263 plug(statp->st_nlink, val);
265 p += from_base64(&val, p);
266 plug(statp->st_uid, val);
268 p += from_base64(&val, p);
269 plug(statp->st_gid, val);
271 p += from_base64(&val, p);
272 plug(statp->st_rdev, val);
274 p += from_base64(&val, p);
275 plug(statp->st_size, val);
278 p += from_base64(&val, p);
279 plug(statp->st_blksize, val);
281 p += from_base64(&val, p);
282 plug(statp->st_blocks, val);
285 p += from_base64(&val, p);
286 // plug(statp->st_blksize, val);
288 p += from_base64(&val, p);
289 // plug(statp->st_blocks, val);
292 p += from_base64(&val, p);
293 plug(statp->st_atime, val);
295 p += from_base64(&val, p);
296 plug(statp->st_mtime, val);
298 p += from_base64(&val, p);
299 plug(statp->st_ctime, val);
301 /* Optional FileIndex of hard linked file data */
302 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
304 p += from_base64(&val, p);
305 *LinkFI = (uint32_t)val;
311 /* FreeBSD user flags */
312 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
314 p += from_base64(&val, p);
316 plug(statp->st_flags, val);
322 /* Look for data stream id */
323 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
325 p += from_base64(&val, p);
332 /** Decode a LinkFI field of encoded stat packet */
333 int32_t decode_LinkFI(char *buf, struct stat *statp)
338 skip_nonspaces(&p); /* st_dev */
339 p++; /* skip space */
340 skip_nonspaces(&p); /* st_ino */
342 p += from_base64(&val, p);
343 plug(statp->st_mode, val); /* st_mode */
345 skip_nonspaces(&p); /* st_nlink */
347 skip_nonspaces(&p); /* st_uid */
349 skip_nonspaces(&p); /* st_gid */
351 skip_nonspaces(&p); /* st_rdev */
353 skip_nonspaces(&p); /* st_size */
355 skip_nonspaces(&p); /* st_blksize */
357 skip_nonspaces(&p); /* st_blocks */
359 skip_nonspaces(&p); /* st_atime */
361 skip_nonspaces(&p); /* st_mtime */
363 skip_nonspaces(&p); /* st_ctime */
365 /* Optional FileIndex of hard linked file data */
366 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
368 p += from_base64(&val, p);
375 * Set file modes, permissions and times
377 * fname is the original filename
378 * ofile is the output filename (may be in a different directory)
380 * Returns: true on success
383 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
396 #if defined(HAVE_WIN32)
397 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
398 set_win32_attributes(jcr, attr, ofd)) {
402 pm_strcpy(attr->ofname, "*none*");
405 if (attr->data_stream == STREAM_WIN32_DATA ||
406 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
410 pm_strcpy(attr->ofname, "*none*");
416 * If Windows stuff failed, e.g. attempt to restore Unix file
417 * to Windows, simply fall through and we will do it the
424 char ec1[50], ec2[50];
425 fsize = blseek(ofd, 0, SEEK_END);
426 bclose(ofd); /* first close file */
427 if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 &&
428 fsize != (boffset_t)attr->statp.st_size) {
429 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
430 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
431 edit_uint64(fsize, ec2));
436 * We do not restore sockets, so skip trying to restore their
439 if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
443 ut.actime = attr->statp.st_atime;
444 ut.modtime = attr->statp.st_mtime;
446 /* ***FIXME**** optimize -- don't do if already correct */
448 * For link, change owner of link using lchown, but don't
449 * try to do a chmod as that will update the file behind it.
451 if (attr->type == FT_LNK) {
452 /** Change owner of link, not of real file */
453 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
455 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
456 attr->ofname, be.bstrerror());
460 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
462 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
463 attr->ofname, be.bstrerror());
466 if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) {
468 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
469 attr->ofname, be.bstrerror());
476 if (utime(attr->ofname, &ut) < 0 && my_uid == 0) {
478 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
479 attr->ofname, be.bstrerror());
486 * Note, this should really be done before the utime() above,
487 * but if the immutable bit is set, it will make the utimes()
490 if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) {
492 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
493 attr->ofname, be.bstrerror());
500 pm_strcpy(attr->ofname, "*none*");
506 /*=============================================================*/
508 /* * * * U n i x * * * * */
510 /*=============================================================*/
512 #if !defined(HAVE_WIN32)
515 * It is possible to piggyback additional data e.g. ACLs on
516 * the encode_stat() data by returning the extended attributes
517 * here. They must be "self-contained" (i.e. you keep track
518 * of your own length), and they must be in ASCII string
519 * format. Using this feature is not recommended.
520 * The code below shows how to return nothing. See the Win32
521 * code below for returning something in the attributes.
523 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
525 #ifdef HAVE_DARWIN_OS
527 * We save the Mac resource fork length so that on a
528 * restore, we can be sure we put back the whole resource.
532 *attribsEx = 0; /* no extended attributes (yet) */
533 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
534 return STREAM_UNIX_ATTRIBUTES;
537 if (ff_pkt->flags & FO_HFSPLUS) {
538 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
542 *attribsEx = 0; /* no extended attributes */
544 return STREAM_UNIX_ATTRIBUTES;
551 /*=============================================================*/
553 /* * * * W i n 3 2 * * * * */
555 /*=============================================================*/
557 #if defined(HAVE_WIN32)
559 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
562 WIN32_FILE_ATTRIBUTE_DATA atts;
565 attribsEx[0] = 0; /* no extended attributes */
567 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
568 return STREAM_UNIX_ATTRIBUTES;
571 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
573 /** try unicode version */
574 if (p_GetFileAttributesExW) {
575 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
576 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
578 BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
580 free_pool_memory(pwszBuf);
583 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
584 return STREAM_UNIX_ATTRIBUTES;
588 if (!p_GetFileAttributesExA)
589 return STREAM_UNIX_ATTRIBUTES;
591 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
593 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
594 return STREAM_UNIX_ATTRIBUTES;
598 p += to_base64((uint64_t)atts.dwFileAttributes, p);
599 *p++ = ' '; /* separate fields with a space */
600 li.LowPart = atts.ftCreationTime.dwLowDateTime;
601 li.HighPart = atts.ftCreationTime.dwHighDateTime;
602 p += to_base64((uint64_t)li.QuadPart, p);
604 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
605 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
606 p += to_base64((uint64_t)li.QuadPart, p);
608 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
609 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
610 p += to_base64((uint64_t)li.QuadPart, p);
612 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
614 p += to_base64((uint64_t)atts.nFileSizeLow, p);
616 return STREAM_UNIX_ATTRIBUTES_EX;
619 /** Define attributes that are legal to set with SetFileAttributes() */
620 #define SET_ATTRS ( \
621 FILE_ATTRIBUTE_ARCHIVE| \
622 FILE_ATTRIBUTE_HIDDEN| \
623 FILE_ATTRIBUTE_NORMAL| \
624 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
625 FILE_ATTRIBUTE_OFFLINE| \
626 FILE_ATTRIBUTE_READONLY| \
627 FILE_ATTRIBUTE_SYSTEM| \
628 FILE_ATTRIBUTE_TEMPORARY)
632 * Set Extended File Attributes for Win32
634 * fname is the original filename
635 * ofile is the output filename (may be in a different directory)
637 * Returns: true on success
640 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
642 char *p = attr->attrEx;
644 WIN32_FILE_ATTRIBUTE_DATA atts;
646 POOLMEM *win32_ofile;
648 /** if we have neither Win ansi nor wchar API, get out */
649 if (!(p_SetFileAttributesW || p_SetFileAttributesA)) {
653 if (!p || !*p) { /* we should have attributes */
654 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
660 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
663 p += from_base64(&val, p);
664 plug(atts.dwFileAttributes, val);
665 p++; /* skip space */
666 p += from_base64(&val, p);
668 atts.ftCreationTime.dwLowDateTime = li.LowPart;
669 atts.ftCreationTime.dwHighDateTime = li.HighPart;
670 p++; /* skip space */
671 p += from_base64(&val, p);
673 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
674 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
675 p++; /* skip space */
676 p += from_base64(&val, p);
678 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
679 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
681 p += from_base64(&val, p);
682 plug(atts.nFileSizeHigh, val);
684 p += from_base64(&val, p);
685 plug(atts.nFileSizeLow, val);
687 /** Convert to Windows path format */
688 win32_ofile = get_pool_memory(PM_FNAME);
689 unix_name_to_win32(&win32_ofile, attr->ofname);
691 /** At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
693 if (!is_bopen(ofd)) {
694 Dmsg1(100, "File not open: %s\n", attr->ofname);
695 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
699 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
700 if (!SetFileTime(bget_handle(ofd),
701 &atts.ftCreationTime,
702 &atts.ftLastAccessTime,
703 &atts.ftLastWriteTime)) {
704 win_error(jcr, "SetFileTime:", win32_ofile);
709 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
710 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
711 if (p_SetFileAttributesW) {
712 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
713 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
715 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
716 free_pool_memory(pwszBuf);
719 win_error(jcr, "SetFileAttributesW:", win32_ofile);
722 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
723 win_error(jcr, "SetFileAttributesA:", win32_ofile);
727 free_pool_memory(win32_ofile);
731 void win_error(JCR *jcr, const char *prefix, POOLMEM *win32_ofile)
733 DWORD lerror = GetLastError();
735 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
736 FORMAT_MESSAGE_FROM_SYSTEM,
743 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
744 strip_trailing_junk(msg);
745 Jmsg3(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
749 void win_error(JCR *jcr, const char *prefix, DWORD lerror)
752 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
753 FORMAT_MESSAGE_FROM_SYSTEM,
760 strip_trailing_junk(msg);
762 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
764 MessageBox(NULL, msg, prefix, MB_OK);
768 #endif /* HAVE_WIN32 */