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 two of the GNU 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 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;
95 /** Encryption is only supported for file data */
96 if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
97 stream != STREAM_MACOS_FORK_DATA) {
98 ff_pkt->flags &= ~FO_ENCRYPT;
101 /** Compression is not supported for Mac fork data */
102 if (stream == STREAM_MACOS_FORK_DATA) {
103 ff_pkt->flags &= ~FO_GZIP;
107 * Handle compression and encryption options
110 if (ff_pkt->flags & FO_GZIP) {
112 case STREAM_WIN32_DATA:
113 stream = STREAM_WIN32_GZIP_DATA;
115 case STREAM_SPARSE_DATA:
116 stream = STREAM_SPARSE_GZIP_DATA;
118 case STREAM_FILE_DATA:
119 stream = STREAM_GZIP_DATA;
123 * All stream types that do not support gzip should clear out
124 * FO_GZIP above, and this code block should be unreachable.
126 ASSERT(!(ff_pkt->flags & FO_GZIP));
132 if (ff_pkt->flags & FO_ENCRYPT) {
134 case STREAM_WIN32_DATA:
135 stream = STREAM_ENCRYPTED_WIN32_DATA;
137 case STREAM_WIN32_GZIP_DATA:
138 stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
140 case STREAM_FILE_DATA:
141 stream = STREAM_ENCRYPTED_FILE_DATA;
143 case STREAM_GZIP_DATA:
144 stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
147 /* All stream types that do not support encryption should clear out
148 * FO_ENCRYPT above, and this code block should be unreachable. */
149 ASSERT(!(ff_pkt->flags & FO_ENCRYPT));
160 * Encode a stat structure into a base64 character string
161 * All systems must create such a structure.
162 * In addition, we tack on the LinkFI, which is non-zero in
163 * the case of a hard linked file that has no data. This
164 * is a File Index pointing to the link that does have the
165 * data (always the first one encountered in a save).
166 * You may piggyback attributes on this packet by encoding
167 * them in the encode_attribsEx() subroutine, but this is
170 void encode_stat(char *buf, struct stat *statp, int32_t LinkFI, int data_stream)
175 * Encode a stat packet. I should have done this more intelligently
176 * with a length so that it could be easily expanded.
178 p += to_base64((int64_t)statp->st_dev, p);
179 *p++ = ' '; /* separate fields with a space */
180 p += to_base64((int64_t)statp->st_ino, p);
182 p += to_base64((int64_t)statp->st_mode, p);
184 p += to_base64((int64_t)statp->st_nlink, p);
186 p += to_base64((int64_t)statp->st_uid, p);
188 p += to_base64((int64_t)statp->st_gid, p);
190 p += to_base64((int64_t)statp->st_rdev, p);
192 p += to_base64((int64_t)statp->st_size, p);
195 p += to_base64((int64_t)statp->st_blksize, p);
197 p += to_base64((int64_t)statp->st_blocks, p);
200 p += to_base64((int64_t)0, p); /* output place holder */
202 p += to_base64((int64_t)0, p); /* output place holder */
205 p += to_base64((int64_t)statp->st_atime, p);
207 p += to_base64((int64_t)statp->st_mtime, p);
209 p += to_base64((int64_t)statp->st_ctime, p);
211 p += to_base64((int64_t)LinkFI, p);
215 /* FreeBSD function */
216 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
218 p += to_base64((int64_t)0, p); /* output place holder */
221 p += to_base64((int64_t)data_stream, p);
227 /* Do casting according to unknown type to keep compiler happy */
229 #define plug(st, val) st = (typeof st)val
231 #if !HAVE_GCC & HAVE_SUN_OS
232 /* Sun compiler does not handle templates correctly */
233 #define plug(st, val) st = val
235 #define plug(st, val) st = val
237 /* Use templates to do the casting */
238 template <class T> void plug(T &st, uint64_t val)
239 { st = static_cast<T>(val); }
244 /** Decode a stat packet from base64 characters */
245 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
250 p += from_base64(&val, p);
251 plug(statp->st_dev, val);
253 p += from_base64(&val, p);
254 plug(statp->st_ino, val);
256 p += from_base64(&val, p);
257 plug(statp->st_mode, val);
259 p += from_base64(&val, p);
260 plug(statp->st_nlink, val);
262 p += from_base64(&val, p);
263 plug(statp->st_uid, val);
265 p += from_base64(&val, p);
266 plug(statp->st_gid, val);
268 p += from_base64(&val, p);
269 plug(statp->st_rdev, val);
271 p += from_base64(&val, p);
272 plug(statp->st_size, val);
275 p += from_base64(&val, p);
276 plug(statp->st_blksize, val);
278 p += from_base64(&val, p);
279 plug(statp->st_blocks, val);
282 p += from_base64(&val, p);
283 // plug(statp->st_blksize, val);
285 p += from_base64(&val, p);
286 // plug(statp->st_blocks, val);
289 p += from_base64(&val, p);
290 plug(statp->st_atime, val);
292 p += from_base64(&val, p);
293 plug(statp->st_mtime, val);
295 p += from_base64(&val, p);
296 plug(statp->st_ctime, val);
298 /* Optional FileIndex of hard linked file data */
299 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
301 p += from_base64(&val, p);
302 *LinkFI = (uint32_t)val;
308 /* FreeBSD user flags */
309 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
311 p += from_base64(&val, p);
313 plug(statp->st_flags, val);
319 /* Look for data stream id */
320 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
322 p += from_base64(&val, p);
329 /** Decode a LinkFI field of encoded stat packet */
330 int32_t decode_LinkFI(char *buf, struct stat *statp)
335 skip_nonspaces(&p); /* st_dev */
336 p++; /* skip space */
337 skip_nonspaces(&p); /* st_ino */
339 p += from_base64(&val, p);
340 plug(statp->st_mode, val); /* st_mode */
342 skip_nonspaces(&p); /* st_nlink */
344 skip_nonspaces(&p); /* st_uid */
346 skip_nonspaces(&p); /* st_gid */
348 skip_nonspaces(&p); /* st_rdev */
350 skip_nonspaces(&p); /* st_size */
352 skip_nonspaces(&p); /* st_blksize */
354 skip_nonspaces(&p); /* st_blocks */
356 skip_nonspaces(&p); /* st_atime */
358 skip_nonspaces(&p); /* st_mtime */
360 skip_nonspaces(&p); /* st_ctime */
362 /* Optional FileIndex of hard linked file data */
363 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
365 p += from_base64(&val, p);
372 * Set file modes, permissions and times
374 * fname is the original filename
375 * ofile is the output filename (may be in a different directory)
377 * Returns: true on success
380 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
393 #if defined(HAVE_WIN32)
394 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
395 set_win32_attributes(jcr, attr, ofd)) {
399 pm_strcpy(attr->ofname, "*none*");
402 if (attr->data_stream == STREAM_WIN32_DATA ||
403 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
407 pm_strcpy(attr->ofname, "*none*");
413 * If Windows stuff failed, e.g. attempt to restore Unix file
414 * to Windows, simply fall through and we will do it the
421 char ec1[50], ec2[50];
422 fsize = blseek(ofd, 0, SEEK_END);
423 bclose(ofd); /* first close file */
424 if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 &&
425 fsize != (boffset_t)attr->statp.st_size) {
426 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
427 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
428 edit_uint64(fsize, ec2));
433 * We do not restore sockets, so skip trying to restore their
436 if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
440 ut.actime = attr->statp.st_atime;
441 ut.modtime = attr->statp.st_mtime;
443 /* ***FIXME**** optimize -- don't do if already correct */
445 * For link, change owner of link using lchown, but don't
446 * try to do a chmod as that will update the file behind it.
448 if (attr->type == FT_LNK) {
449 /** Change owner of link, not of real file */
450 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
452 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
453 attr->ofname, be.bstrerror());
457 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
459 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
460 attr->ofname, be.bstrerror());
463 if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) {
465 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
466 attr->ofname, be.bstrerror());
473 if (utime(attr->ofname, &ut) < 0 && my_uid == 0) {
475 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
476 attr->ofname, be.bstrerror());
483 * Note, this should really be done before the utime() above,
484 * but if the immutable bit is set, it will make the utimes()
487 if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) {
489 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
490 attr->ofname, be.bstrerror());
497 pm_strcpy(attr->ofname, "*none*");
503 /*=============================================================*/
505 /* * * * U n i x * * * * */
507 /*=============================================================*/
509 #if !defined(HAVE_WIN32)
512 * It is possible to piggyback additional data e.g. ACLs on
513 * the encode_stat() data by returning the extended attributes
514 * here. They must be "self-contained" (i.e. you keep track
515 * of your own length), and they must be in ASCII string
516 * format. Using this feature is not recommended.
517 * The code below shows how to return nothing. See the Win32
518 * code below for returning something in the attributes.
520 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
522 #ifdef HAVE_DARWIN_OS
524 * We save the Mac resource fork length so that on a
525 * restore, we can be sure we put back the whole resource.
529 *attribsEx = 0; /* no extended attributes (yet) */
530 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
531 return STREAM_UNIX_ATTRIBUTES;
534 if (ff_pkt->flags & FO_HFSPLUS) {
535 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
539 *attribsEx = 0; /* no extended attributes */
541 return STREAM_UNIX_ATTRIBUTES;
548 /*=============================================================*/
550 /* * * * W i n 3 2 * * * * */
552 /*=============================================================*/
554 #if defined(HAVE_WIN32)
556 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
559 WIN32_FILE_ATTRIBUTE_DATA atts;
562 attribsEx[0] = 0; /* no extended attributes */
564 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
565 return STREAM_UNIX_ATTRIBUTES;
568 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
570 /** try unicode version */
571 if (p_GetFileAttributesExW) {
572 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
573 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
575 BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
577 free_pool_memory(pwszBuf);
580 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
581 return STREAM_UNIX_ATTRIBUTES;
585 if (!p_GetFileAttributesExA)
586 return STREAM_UNIX_ATTRIBUTES;
588 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
590 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
591 return STREAM_UNIX_ATTRIBUTES;
595 p += to_base64((uint64_t)atts.dwFileAttributes, p);
596 *p++ = ' '; /* separate fields with a space */
597 li.LowPart = atts.ftCreationTime.dwLowDateTime;
598 li.HighPart = atts.ftCreationTime.dwHighDateTime;
599 p += to_base64((uint64_t)li.QuadPart, p);
601 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
602 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
603 p += to_base64((uint64_t)li.QuadPart, p);
605 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
606 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
607 p += to_base64((uint64_t)li.QuadPart, p);
609 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
611 p += to_base64((uint64_t)atts.nFileSizeLow, p);
613 return STREAM_UNIX_ATTRIBUTES_EX;
616 /** Define attributes that are legal to set with SetFileAttributes() */
617 #define SET_ATTRS ( \
618 FILE_ATTRIBUTE_ARCHIVE| \
619 FILE_ATTRIBUTE_HIDDEN| \
620 FILE_ATTRIBUTE_NORMAL| \
621 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
622 FILE_ATTRIBUTE_OFFLINE| \
623 FILE_ATTRIBUTE_READONLY| \
624 FILE_ATTRIBUTE_SYSTEM| \
625 FILE_ATTRIBUTE_TEMPORARY)
629 * Set Extended File Attributes for Win32
631 * fname is the original filename
632 * ofile is the output filename (may be in a different directory)
634 * Returns: true on success
637 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
639 char *p = attr->attrEx;
641 WIN32_FILE_ATTRIBUTE_DATA atts;
643 POOLMEM *win32_ofile;
645 /** if we have neither Win ansi nor wchar API, get out */
646 if (!(p_SetFileAttributesW || p_SetFileAttributesA)) {
650 if (!p || !*p) { /* we should have attributes */
651 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
657 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
660 p += from_base64(&val, p);
661 plug(atts.dwFileAttributes, val);
662 p++; /* skip space */
663 p += from_base64(&val, p);
665 atts.ftCreationTime.dwLowDateTime = li.LowPart;
666 atts.ftCreationTime.dwHighDateTime = li.HighPart;
667 p++; /* skip space */
668 p += from_base64(&val, p);
670 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
671 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
672 p++; /* skip space */
673 p += from_base64(&val, p);
675 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
676 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
678 p += from_base64(&val, p);
679 plug(atts.nFileSizeHigh, val);
681 p += from_base64(&val, p);
682 plug(atts.nFileSizeLow, val);
684 /** Convert to Windows path format */
685 win32_ofile = get_pool_memory(PM_FNAME);
686 unix_name_to_win32(&win32_ofile, attr->ofname);
688 /** At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
690 if (!is_bopen(ofd)) {
691 Dmsg1(100, "File not open: %s\n", attr->ofname);
692 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
696 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
697 if (!SetFileTime(bget_handle(ofd),
698 &atts.ftCreationTime,
699 &atts.ftLastAccessTime,
700 &atts.ftLastWriteTime)) {
701 win_error(jcr, "SetFileTime:", win32_ofile);
706 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
707 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
708 if (p_SetFileAttributesW) {
709 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
710 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
712 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
713 free_pool_memory(pwszBuf);
716 win_error(jcr, "SetFileAttributesW:", win32_ofile);
719 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
720 win_error(jcr, "SetFileAttributesA:", win32_ofile);
724 free_pool_memory(win32_ofile);
728 void win_error(JCR *jcr, const char *prefix, POOLMEM *win32_ofile)
730 DWORD lerror = GetLastError();
732 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
733 FORMAT_MESSAGE_FROM_SYSTEM,
740 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
741 strip_trailing_junk(msg);
742 Jmsg3(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
746 void win_error(JCR *jcr, const char *prefix, DWORD lerror)
749 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
750 FORMAT_MESSAGE_FROM_SYSTEM,
757 strip_trailing_junk(msg);
759 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
761 MessageBox(NULL, msg, prefix, MB_OK);
765 #endif /* HAVE_WIN32 */