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
41 static uid_t my_uid = 1;
42 static gid_t my_gid = 1;
43 static bool uid_set = false;
46 #if defined(HAVE_WIN32)
47 /* Forward referenced subroutines */
48 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd);
49 void unix_name_to_win32(POOLMEM **win32_name, char *name);
50 void win_error(JCR *jcr, const char *prefix, POOLMEM *ofile);
51 HANDLE bget_handle(BFILE *bfd);
52 #endif /* HAVE_WIN32 */
54 /* For old systems that don't have lchown() use chown() */
59 /*=============================================================*/
61 /* *** A l l S y s t e m s *** */
63 /*=============================================================*/
66 * Return the data stream that will be used
68 int select_data_stream(FF_PKT *ff_pkt)
72 /* This is a plugin special restore object */
73 if (ff_pkt->type == FT_RESTORE_FIRST) {
75 return STREAM_FILE_DATA;
79 * Fix all incompatible options
81 /** No sparse option for encrypted data */
82 if (ff_pkt->flags & FO_ENCRYPT) {
83 ff_pkt->flags &= ~FO_SPARSE;
86 /** Note, no sparse option for win32_data */
87 if (!is_portable_backup(&ff_pkt->bfd)) {
88 stream = STREAM_WIN32_DATA;
89 ff_pkt->flags &= ~FO_SPARSE;
90 } else if (ff_pkt->flags & FO_SPARSE) {
91 stream = STREAM_SPARSE_DATA;
93 stream = STREAM_FILE_DATA;
95 if (ff_pkt->flags & FO_OFFSETS) {
96 stream = STREAM_SPARSE_DATA;
99 /** Encryption is only supported for file data */
100 if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
101 stream != STREAM_MACOS_FORK_DATA) {
102 ff_pkt->flags &= ~FO_ENCRYPT;
105 /** Compression is not supported for Mac fork data */
106 if (stream == STREAM_MACOS_FORK_DATA) {
107 ff_pkt->flags &= ~FO_COMPRESS;
111 * Handle compression and encryption options
113 #if defined(HAVE_LIBZ) || defined(HAVE_LZO)
114 if (ff_pkt->flags & FO_COMPRESS) {
116 if(ff_pkt->Compress_algo == COMPRESS_GZIP) {
118 case STREAM_WIN32_DATA:
119 stream = STREAM_WIN32_GZIP_DATA;
121 case STREAM_SPARSE_DATA:
122 stream = STREAM_SPARSE_GZIP_DATA;
124 case STREAM_FILE_DATA:
125 stream = STREAM_GZIP_DATA;
129 * All stream types that do not support compression should clear out
130 * FO_COMPRESS above, and this code block should be unreachable.
132 ASSERT(!(ff_pkt->flags & FO_COMPRESS));
138 if(ff_pkt->Compress_algo == COMPRESS_LZO1X) {
140 case STREAM_WIN32_DATA:
141 stream = STREAM_WIN32_COMPRESSED_DATA;
143 case STREAM_SPARSE_DATA:
144 stream = STREAM_SPARSE_COMPRESSED_DATA;
146 case STREAM_FILE_DATA:
147 stream = STREAM_COMPRESSED_DATA;
151 * All stream types that do not support compression should clear out
152 * FO_COMPRESS above, and this code block should be unreachable.
154 ASSERT(!(ff_pkt->flags & FO_COMPRESS));
162 if (ff_pkt->flags & FO_ENCRYPT) {
164 case STREAM_WIN32_DATA:
165 stream = STREAM_ENCRYPTED_WIN32_DATA;
167 case STREAM_WIN32_GZIP_DATA:
168 stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
170 case STREAM_WIN32_COMPRESSED_DATA:
171 stream = STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA;
173 case STREAM_FILE_DATA:
174 stream = STREAM_ENCRYPTED_FILE_DATA;
176 case STREAM_GZIP_DATA:
177 stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
179 case STREAM_COMPRESSED_DATA:
180 stream = STREAM_ENCRYPTED_FILE_COMPRESSED_DATA;
183 /* All stream types that do not support encryption should clear out
184 * FO_ENCRYPT above, and this code block should be unreachable. */
185 ASSERT(!(ff_pkt->flags & FO_ENCRYPT));
196 * Encode a stat structure into a base64 character string
197 * All systems must create such a structure.
198 * In addition, we tack on the LinkFI, which is non-zero in
199 * the case of a hard linked file that has no data. This
200 * is a File Index pointing to the link that does have the
201 * data (always the first one encountered in a save).
202 * You may piggyback attributes on this packet by encoding
203 * them in the encode_attribsEx() subroutine, but this is
206 void encode_stat(char *buf, struct stat *statp, int stat_size, int32_t LinkFI, int data_stream)
211 * We read the stat packet so make sure the caller's conception
212 * is the same as ours. They can be different if LARGEFILE is not
213 * the same when compiling this library and the calling program.
215 ASSERT(stat_size == (int)sizeof(struct stat));
218 * Encode a stat packet. I should have done this more intelligently
219 * with a length so that it could be easily expanded.
221 p += to_base64((int64_t)statp->st_dev, p);
222 *p++ = ' '; /* separate fields with a space */
223 p += to_base64((int64_t)statp->st_ino, p);
225 p += to_base64((int64_t)statp->st_mode, p);
227 p += to_base64((int64_t)statp->st_nlink, p);
229 p += to_base64((int64_t)statp->st_uid, p);
231 p += to_base64((int64_t)statp->st_gid, p);
233 p += to_base64((int64_t)statp->st_rdev, p);
235 p += to_base64((int64_t)statp->st_size, p);
238 p += to_base64((int64_t)statp->st_blksize, p);
240 p += to_base64((int64_t)statp->st_blocks, p);
243 p += to_base64((int64_t)0, p); /* output place holder */
245 p += to_base64((int64_t)0, p); /* output place holder */
248 p += to_base64((int64_t)statp->st_atime, p);
250 p += to_base64((int64_t)statp->st_mtime, p);
252 p += to_base64((int64_t)statp->st_ctime, p);
254 p += to_base64((int64_t)LinkFI, p);
258 /* FreeBSD function */
259 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
261 p += to_base64((int64_t)0, p); /* output place holder */
264 p += to_base64((int64_t)data_stream, p);
270 /* Do casting according to unknown type to keep compiler happy */
272 #define plug(st, val) st = (typeof st)val
274 #if !HAVE_GCC & HAVE_SUN_OS
275 /* Sun compiler does not handle templates correctly */
276 #define plug(st, val) st = val
278 #define plug(st, val) st = val
280 /* Use templates to do the casting */
281 template <class T> void plug(T &st, uint64_t val)
282 { st = static_cast<T>(val); }
287 /** Decode a stat packet from base64 characters */
288 int decode_stat(char *buf, struct stat *statp, int stat_size, int32_t *LinkFI)
294 * We store into the stat packet so make sure the caller's conception
295 * is the same as ours. They can be different if LARGEFILE is not
296 * the same when compiling this library and the calling program.
298 ASSERT(stat_size == (int)sizeof(struct stat));
300 p += from_base64(&val, p);
301 plug(statp->st_dev, val);
303 p += from_base64(&val, p);
304 plug(statp->st_ino, val);
306 p += from_base64(&val, p);
307 plug(statp->st_mode, val);
309 p += from_base64(&val, p);
310 plug(statp->st_nlink, val);
312 p += from_base64(&val, p);
313 plug(statp->st_uid, val);
315 p += from_base64(&val, p);
316 plug(statp->st_gid, val);
318 p += from_base64(&val, p);
319 plug(statp->st_rdev, val);
321 p += from_base64(&val, p);
322 plug(statp->st_size, val);
325 p += from_base64(&val, p);
326 plug(statp->st_blksize, val);
328 p += from_base64(&val, p);
329 plug(statp->st_blocks, val);
332 p += from_base64(&val, p);
333 // plug(statp->st_blksize, val);
335 p += from_base64(&val, p);
336 // plug(statp->st_blocks, val);
339 p += from_base64(&val, p);
340 plug(statp->st_atime, val);
342 p += from_base64(&val, p);
343 plug(statp->st_mtime, val);
345 p += from_base64(&val, p);
346 plug(statp->st_ctime, val);
348 /* Optional FileIndex of hard linked file data */
349 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
351 p += from_base64(&val, p);
352 *LinkFI = (uint32_t)val;
358 /* FreeBSD user flags */
359 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
361 p += from_base64(&val, p);
363 plug(statp->st_flags, val);
369 /* Look for data stream id */
370 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
372 p += from_base64(&val, p);
379 /** Decode a LinkFI field of encoded stat packet */
380 int32_t decode_LinkFI(char *buf, struct stat *statp, int stat_size)
385 * We store into the stat packet so make sure the caller's conception
386 * is the same as ours. They can be different if LARGEFILE is not
387 * the same when compiling this library and the calling program.
389 ASSERT(stat_size == (int)sizeof(struct stat));
391 skip_nonspaces(&p); /* st_dev */
392 p++; /* skip space */
393 skip_nonspaces(&p); /* st_ino */
395 p += from_base64(&val, p);
396 plug(statp->st_mode, val); /* st_mode */
398 skip_nonspaces(&p); /* st_nlink */
400 skip_nonspaces(&p); /* st_uid */
402 skip_nonspaces(&p); /* st_gid */
404 skip_nonspaces(&p); /* st_rdev */
406 skip_nonspaces(&p); /* st_size */
408 skip_nonspaces(&p); /* st_blksize */
410 skip_nonspaces(&p); /* st_blocks */
412 skip_nonspaces(&p); /* st_atime */
414 skip_nonspaces(&p); /* st_mtime */
416 skip_nonspaces(&p); /* st_ctime */
418 /* Optional FileIndex of hard linked file data */
419 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
421 p += from_base64(&val, p);
428 * Set file modes, permissions and times
430 * fname is the original filename
431 * ofile is the output filename (may be in a different directory)
433 * Returns: true on success
436 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
449 #if defined(HAVE_WIN32)
450 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
451 set_win32_attributes(jcr, attr, ofd)) {
455 pm_strcpy(attr->ofname, "*none*");
458 if (attr->data_stream == STREAM_WIN32_DATA ||
459 attr->data_stream == STREAM_WIN32_GZIP_DATA ||
460 attr->data_stream == STREAM_WIN32_COMPRESSED_DATA) {
464 pm_strcpy(attr->ofname, "*none*");
470 * If Windows stuff failed, e.g. attempt to restore Unix file
471 * to Windows, simply fall through and we will do it the
478 char ec1[50], ec2[50];
479 fsize = blseek(ofd, 0, SEEK_END);
480 bclose(ofd); /* first close file */
481 if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 &&
482 fsize != (boffset_t)attr->statp.st_size) {
483 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
484 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
485 edit_uint64(fsize, ec2));
490 * We do not restore sockets, so skip trying to restore their
493 if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
497 ut.actime = attr->statp.st_atime;
498 ut.modtime = attr->statp.st_mtime;
500 /* ***FIXME**** optimize -- don't do if already correct */
502 * For link, change owner of link using lchown, but don't
503 * try to do a chmod as that will update the file behind it.
505 if (attr->type == FT_LNK) {
506 /** Change owner of link, not of real file */
507 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
509 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
510 attr->ofname, be.bstrerror());
514 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
516 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
517 attr->ofname, be.bstrerror());
520 if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) {
522 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
523 attr->ofname, be.bstrerror());
530 if (utime(attr->ofname, &ut) < 0 && my_uid == 0) {
532 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
533 attr->ofname, be.bstrerror());
540 * Note, this should really be done before the utime() above,
541 * but if the immutable bit is set, it will make the utimes()
544 if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) {
546 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
547 attr->ofname, be.bstrerror());
554 pm_strcpy(attr->ofname, "*none*");
560 /*=============================================================*/
562 /* * * * U n i x * * * * */
564 /*=============================================================*/
566 #if !defined(HAVE_WIN32)
569 * It is possible to piggyback additional data e.g. ACLs on
570 * the encode_stat() data by returning the extended attributes
571 * here. They must be "self-contained" (i.e. you keep track
572 * of your own length), and they must be in ASCII string
573 * format. Using this feature is not recommended.
574 * The code below shows how to return nothing. See the Win32
575 * code below for returning something in the attributes.
577 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
579 #ifdef HAVE_DARWIN_OS
581 * We save the Mac resource fork length so that on a
582 * restore, we can be sure we put back the whole resource.
586 *attribsEx = 0; /* no extended attributes (yet) */
587 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
588 return STREAM_UNIX_ATTRIBUTES;
591 if (ff_pkt->flags & FO_HFSPLUS) {
592 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
596 *attribsEx = 0; /* no extended attributes */
598 return STREAM_UNIX_ATTRIBUTES;
605 /*=============================================================*/
607 /* * * * W i n 3 2 * * * * */
609 /*=============================================================*/
611 #if defined(HAVE_WIN32)
613 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
616 WIN32_FILE_ATTRIBUTE_DATA atts;
619 attribsEx[0] = 0; /* no extended attributes */
621 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
622 return STREAM_UNIX_ATTRIBUTES;
625 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
627 /** try unicode version */
628 if (p_GetFileAttributesExW) {
629 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
630 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
632 BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
634 free_pool_memory(pwszBuf);
637 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
638 return STREAM_UNIX_ATTRIBUTES;
642 if (!p_GetFileAttributesExA)
643 return STREAM_UNIX_ATTRIBUTES;
645 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
647 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
648 return STREAM_UNIX_ATTRIBUTES;
652 p += to_base64((uint64_t)atts.dwFileAttributes, p);
653 *p++ = ' '; /* separate fields with a space */
654 li.LowPart = atts.ftCreationTime.dwLowDateTime;
655 li.HighPart = atts.ftCreationTime.dwHighDateTime;
656 p += to_base64((uint64_t)li.QuadPart, p);
658 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
659 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
660 p += to_base64((uint64_t)li.QuadPart, p);
662 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
663 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
664 p += to_base64((uint64_t)li.QuadPart, p);
666 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
668 p += to_base64((uint64_t)atts.nFileSizeLow, p);
670 return STREAM_UNIX_ATTRIBUTES_EX;
673 /** Define attributes that are legal to set with SetFileAttributes() */
674 #define SET_ATTRS ( \
675 FILE_ATTRIBUTE_ARCHIVE| \
676 FILE_ATTRIBUTE_HIDDEN| \
677 FILE_ATTRIBUTE_NORMAL| \
678 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
679 FILE_ATTRIBUTE_OFFLINE| \
680 FILE_ATTRIBUTE_READONLY| \
681 FILE_ATTRIBUTE_SYSTEM| \
682 FILE_ATTRIBUTE_TEMPORARY)
686 * Set Extended File Attributes for Win32
688 * fname is the original filename
689 * ofile is the output filename (may be in a different directory)
691 * Returns: true on success
694 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
696 char *p = attr->attrEx;
698 WIN32_FILE_ATTRIBUTE_DATA atts;
700 POOLMEM *win32_ofile;
702 /** if we have neither Win ansi nor wchar API, get out */
703 if (!(p_SetFileAttributesW || p_SetFileAttributesA)) {
707 if (!p || !*p) { /* we should have attributes */
708 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
714 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
717 p += from_base64(&val, p);
718 plug(atts.dwFileAttributes, val);
719 p++; /* skip space */
720 p += from_base64(&val, p);
722 atts.ftCreationTime.dwLowDateTime = li.LowPart;
723 atts.ftCreationTime.dwHighDateTime = li.HighPart;
724 p++; /* skip space */
725 p += from_base64(&val, p);
727 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
728 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
729 p++; /* skip space */
730 p += from_base64(&val, p);
732 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
733 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
735 p += from_base64(&val, p);
736 plug(atts.nFileSizeHigh, val);
738 p += from_base64(&val, p);
739 plug(atts.nFileSizeLow, val);
741 /** Convert to Windows path format */
742 win32_ofile = get_pool_memory(PM_FNAME);
743 unix_name_to_win32(&win32_ofile, attr->ofname);
745 /** At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
747 if (!is_bopen(ofd)) {
748 Dmsg1(100, "File not open: %s\n", attr->ofname);
749 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
753 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
754 if (!SetFileTime(bget_handle(ofd),
755 &atts.ftCreationTime,
756 &atts.ftLastAccessTime,
757 &atts.ftLastWriteTime)) {
758 win_error(jcr, "SetFileTime:", win32_ofile);
763 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
764 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
765 if (p_SetFileAttributesW) {
766 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
767 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
769 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
770 free_pool_memory(pwszBuf);
773 win_error(jcr, "SetFileAttributesW:", win32_ofile);
776 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
777 win_error(jcr, "SetFileAttributesA:", win32_ofile);
781 free_pool_memory(win32_ofile);
785 void win_error(JCR *jcr, const char *prefix, POOLMEM *win32_ofile)
787 DWORD lerror = GetLastError();
789 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
790 FORMAT_MESSAGE_FROM_SYSTEM,
797 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
798 strip_trailing_junk(msg);
799 Jmsg3(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
803 void win_error(JCR *jcr, const char *prefix, DWORD lerror)
806 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
807 FORMAT_MESSAGE_FROM_SYSTEM,
814 strip_trailing_junk(msg);
816 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
818 MessageBox(NULL, msg, prefix, MB_OK);
822 #endif /* HAVE_WIN32 */