2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2011 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, int stat_size, int32_t LinkFI, int data_stream)
178 * We read the stat packet so make sure the caller's conception
179 * is the same as ours. They can be different if LARGEFILE is not
180 * the same when compiling this library and the calling program.
182 ASSERT(stat_size == (int)sizeof(struct stat));
185 * Encode a stat packet. I should have done this more intelligently
186 * with a length so that it could be easily expanded.
188 p += to_base64((int64_t)statp->st_dev, p);
189 *p++ = ' '; /* separate fields with a space */
190 p += to_base64((int64_t)statp->st_ino, p);
192 p += to_base64((int64_t)statp->st_mode, p);
194 p += to_base64((int64_t)statp->st_nlink, p);
196 p += to_base64((int64_t)statp->st_uid, p);
198 p += to_base64((int64_t)statp->st_gid, p);
200 p += to_base64((int64_t)statp->st_rdev, p);
202 p += to_base64((int64_t)statp->st_size, p);
205 p += to_base64((int64_t)statp->st_blksize, p);
207 p += to_base64((int64_t)statp->st_blocks, p);
210 p += to_base64((int64_t)0, p); /* output place holder */
212 p += to_base64((int64_t)0, p); /* output place holder */
215 p += to_base64((int64_t)statp->st_atime, p);
217 p += to_base64((int64_t)statp->st_mtime, p);
219 p += to_base64((int64_t)statp->st_ctime, p);
221 p += to_base64((int64_t)LinkFI, p);
225 /* FreeBSD function */
226 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
228 p += to_base64((int64_t)0, p); /* output place holder */
231 p += to_base64((int64_t)data_stream, p);
237 /* Do casting according to unknown type to keep compiler happy */
239 #define plug(st, val) st = (typeof st)val
241 #if !HAVE_GCC & HAVE_SUN_OS
242 /* Sun compiler does not handle templates correctly */
243 #define plug(st, val) st = val
245 #define plug(st, val) st = val
247 /* Use templates to do the casting */
248 template <class T> void plug(T &st, uint64_t val)
249 { st = static_cast<T>(val); }
254 /** Decode a stat packet from base64 characters */
255 int decode_stat(char *buf, struct stat *statp, int stat_size, int32_t *LinkFI)
261 * We store into the stat packet so make sure the caller's conception
262 * is the same as ours. They can be different if LARGEFILE is not
263 * the same when compiling this library and the calling program.
265 ASSERT(stat_size == (int)sizeof(struct stat));
267 p += from_base64(&val, p);
268 plug(statp->st_dev, val);
270 p += from_base64(&val, p);
271 plug(statp->st_ino, val);
273 p += from_base64(&val, p);
274 plug(statp->st_mode, val);
276 p += from_base64(&val, p);
277 plug(statp->st_nlink, val);
279 p += from_base64(&val, p);
280 plug(statp->st_uid, val);
282 p += from_base64(&val, p);
283 plug(statp->st_gid, val);
285 p += from_base64(&val, p);
286 plug(statp->st_rdev, val);
288 p += from_base64(&val, p);
289 plug(statp->st_size, val);
292 p += from_base64(&val, p);
293 plug(statp->st_blksize, val);
295 p += from_base64(&val, p);
296 plug(statp->st_blocks, val);
299 p += from_base64(&val, p);
300 // plug(statp->st_blksize, val);
302 p += from_base64(&val, p);
303 // plug(statp->st_blocks, val);
306 p += from_base64(&val, p);
307 plug(statp->st_atime, val);
309 p += from_base64(&val, p);
310 plug(statp->st_mtime, val);
312 p += from_base64(&val, p);
313 plug(statp->st_ctime, val);
315 /* Optional FileIndex of hard linked file data */
316 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
318 p += from_base64(&val, p);
319 *LinkFI = (uint32_t)val;
325 /* FreeBSD user flags */
326 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
328 p += from_base64(&val, p);
330 plug(statp->st_flags, val);
336 /* Look for data stream id */
337 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
339 p += from_base64(&val, p);
346 /** Decode a LinkFI field of encoded stat packet */
347 int32_t decode_LinkFI(char *buf, struct stat *statp, int stat_size)
352 * We store into the stat packet so make sure the caller's conception
353 * is the same as ours. They can be different if LARGEFILE is not
354 * the same when compiling this library and the calling program.
356 ASSERT(stat_size == (int)sizeof(struct stat));
358 skip_nonspaces(&p); /* st_dev */
359 p++; /* skip space */
360 skip_nonspaces(&p); /* st_ino */
362 p += from_base64(&val, p);
363 plug(statp->st_mode, val); /* st_mode */
365 skip_nonspaces(&p); /* st_nlink */
367 skip_nonspaces(&p); /* st_uid */
369 skip_nonspaces(&p); /* st_gid */
371 skip_nonspaces(&p); /* st_rdev */
373 skip_nonspaces(&p); /* st_size */
375 skip_nonspaces(&p); /* st_blksize */
377 skip_nonspaces(&p); /* st_blocks */
379 skip_nonspaces(&p); /* st_atime */
381 skip_nonspaces(&p); /* st_mtime */
383 skip_nonspaces(&p); /* st_ctime */
385 /* Optional FileIndex of hard linked file data */
386 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
388 p += from_base64(&val, p);
395 * Set file modes, permissions and times
397 * fname is the original filename
398 * ofile is the output filename (may be in a different directory)
400 * Returns: true on success
403 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
416 #if defined(HAVE_WIN32)
417 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
418 set_win32_attributes(jcr, attr, ofd)) {
422 pm_strcpy(attr->ofname, "*none*");
425 if (attr->data_stream == STREAM_WIN32_DATA ||
426 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
430 pm_strcpy(attr->ofname, "*none*");
436 * If Windows stuff failed, e.g. attempt to restore Unix file
437 * to Windows, simply fall through and we will do it the
444 char ec1[50], ec2[50];
445 fsize = blseek(ofd, 0, SEEK_END);
446 bclose(ofd); /* first close file */
447 if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 &&
448 fsize != (boffset_t)attr->statp.st_size) {
449 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
450 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
451 edit_uint64(fsize, ec2));
456 * We do not restore sockets, so skip trying to restore their
459 if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
463 ut.actime = attr->statp.st_atime;
464 ut.modtime = attr->statp.st_mtime;
466 /* ***FIXME**** optimize -- don't do if already correct */
468 * For link, change owner of link using lchown, but don't
469 * try to do a chmod as that will update the file behind it.
471 if (attr->type == FT_LNK) {
472 /** Change owner of link, not of real file */
473 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
475 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
476 attr->ofname, be.bstrerror());
480 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
482 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
483 attr->ofname, be.bstrerror());
486 if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) {
488 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
489 attr->ofname, be.bstrerror());
496 if (utime(attr->ofname, &ut) < 0 && my_uid == 0) {
498 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
499 attr->ofname, be.bstrerror());
506 * Note, this should really be done before the utime() above,
507 * but if the immutable bit is set, it will make the utimes()
510 if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) {
512 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
513 attr->ofname, be.bstrerror());
520 pm_strcpy(attr->ofname, "*none*");
526 /*=============================================================*/
528 /* * * * U n i x * * * * */
530 /*=============================================================*/
532 #if !defined(HAVE_WIN32)
535 * It is possible to piggyback additional data e.g. ACLs on
536 * the encode_stat() data by returning the extended attributes
537 * here. They must be "self-contained" (i.e. you keep track
538 * of your own length), and they must be in ASCII string
539 * format. Using this feature is not recommended.
540 * The code below shows how to return nothing. See the Win32
541 * code below for returning something in the attributes.
543 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
545 #ifdef HAVE_DARWIN_OS
547 * We save the Mac resource fork length so that on a
548 * restore, we can be sure we put back the whole resource.
552 *attribsEx = 0; /* no extended attributes (yet) */
553 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
554 return STREAM_UNIX_ATTRIBUTES;
557 if (ff_pkt->flags & FO_HFSPLUS) {
558 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
562 *attribsEx = 0; /* no extended attributes */
564 return STREAM_UNIX_ATTRIBUTES;
571 /*=============================================================*/
573 /* * * * W i n 3 2 * * * * */
575 /*=============================================================*/
577 #if defined(HAVE_WIN32)
579 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
582 WIN32_FILE_ATTRIBUTE_DATA atts;
585 attribsEx[0] = 0; /* no extended attributes */
587 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
588 return STREAM_UNIX_ATTRIBUTES;
591 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
593 /** try unicode version */
594 if (p_GetFileAttributesExW) {
595 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
596 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
598 BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
600 free_pool_memory(pwszBuf);
603 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
604 return STREAM_UNIX_ATTRIBUTES;
608 if (!p_GetFileAttributesExA)
609 return STREAM_UNIX_ATTRIBUTES;
611 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
613 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
614 return STREAM_UNIX_ATTRIBUTES;
618 p += to_base64((uint64_t)atts.dwFileAttributes, p);
619 *p++ = ' '; /* separate fields with a space */
620 li.LowPart = atts.ftCreationTime.dwLowDateTime;
621 li.HighPart = atts.ftCreationTime.dwHighDateTime;
622 p += to_base64((uint64_t)li.QuadPart, p);
624 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
625 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
626 p += to_base64((uint64_t)li.QuadPart, p);
628 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
629 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
630 p += to_base64((uint64_t)li.QuadPart, p);
632 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
634 p += to_base64((uint64_t)atts.nFileSizeLow, p);
636 return STREAM_UNIX_ATTRIBUTES_EX;
639 /** Define attributes that are legal to set with SetFileAttributes() */
640 #define SET_ATTRS ( \
641 FILE_ATTRIBUTE_ARCHIVE| \
642 FILE_ATTRIBUTE_HIDDEN| \
643 FILE_ATTRIBUTE_NORMAL| \
644 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
645 FILE_ATTRIBUTE_OFFLINE| \
646 FILE_ATTRIBUTE_READONLY| \
647 FILE_ATTRIBUTE_SYSTEM| \
648 FILE_ATTRIBUTE_TEMPORARY)
652 * Set Extended File Attributes for Win32
654 * fname is the original filename
655 * ofile is the output filename (may be in a different directory)
657 * Returns: true on success
660 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
662 char *p = attr->attrEx;
664 WIN32_FILE_ATTRIBUTE_DATA atts;
666 POOLMEM *win32_ofile;
668 /** if we have neither Win ansi nor wchar API, get out */
669 if (!(p_SetFileAttributesW || p_SetFileAttributesA)) {
673 if (!p || !*p) { /* we should have attributes */
674 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
680 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
683 p += from_base64(&val, p);
684 plug(atts.dwFileAttributes, val);
685 p++; /* skip space */
686 p += from_base64(&val, p);
688 atts.ftCreationTime.dwLowDateTime = li.LowPart;
689 atts.ftCreationTime.dwHighDateTime = li.HighPart;
690 p++; /* skip space */
691 p += from_base64(&val, p);
693 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
694 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
695 p++; /* skip space */
696 p += from_base64(&val, p);
698 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
699 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
701 p += from_base64(&val, p);
702 plug(atts.nFileSizeHigh, val);
704 p += from_base64(&val, p);
705 plug(atts.nFileSizeLow, val);
707 /** Convert to Windows path format */
708 win32_ofile = get_pool_memory(PM_FNAME);
709 unix_name_to_win32(&win32_ofile, attr->ofname);
711 /** At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
713 if (!is_bopen(ofd)) {
714 Dmsg1(100, "File not open: %s\n", attr->ofname);
715 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
719 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
720 if (!SetFileTime(bget_handle(ofd),
721 &atts.ftCreationTime,
722 &atts.ftLastAccessTime,
723 &atts.ftLastWriteTime)) {
724 win_error(jcr, "SetFileTime:", win32_ofile);
729 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
730 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
731 if (p_SetFileAttributesW) {
732 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
733 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
735 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
736 free_pool_memory(pwszBuf);
739 win_error(jcr, "SetFileAttributesW:", win32_ofile);
742 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
743 win_error(jcr, "SetFileAttributesA:", win32_ofile);
747 free_pool_memory(win32_ofile);
751 void win_error(JCR *jcr, const char *prefix, POOLMEM *win32_ofile)
753 DWORD lerror = GetLastError();
755 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
756 FORMAT_MESSAGE_FROM_SYSTEM,
763 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
764 strip_trailing_junk(msg);
765 Jmsg3(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
769 void win_error(JCR *jcr, const char *prefix, DWORD lerror)
772 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
773 FORMAT_MESSAGE_FROM_SYSTEM,
780 strip_trailing_junk(msg);
782 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
784 MessageBox(NULL, msg, prefix, MB_OK);
788 #endif /* HAVE_WIN32 */