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)
72 * Fix all incompatible options
74 /** No sparse option for encrypted data */
75 if (ff_pkt->flags & FO_ENCRYPT) {
76 ff_pkt->flags &= ~FO_SPARSE;
79 /** Note, no sparse option for win32_data */
80 if (!is_portable_backup(&ff_pkt->bfd)) {
81 stream = STREAM_WIN32_DATA;
82 ff_pkt->flags &= ~FO_SPARSE;
83 } else if (ff_pkt->flags & FO_SPARSE) {
84 stream = STREAM_SPARSE_DATA;
86 stream = STREAM_FILE_DATA;
89 /** Encryption is only supported for file data */
90 if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
91 stream != STREAM_MACOS_FORK_DATA) {
92 ff_pkt->flags &= ~FO_ENCRYPT;
95 /** Compression is not supported for Mac fork data */
96 if (stream == STREAM_MACOS_FORK_DATA) {
97 ff_pkt->flags &= ~FO_GZIP;
101 * Handle compression and encryption options
104 if (ff_pkt->flags & FO_GZIP) {
106 case STREAM_WIN32_DATA:
107 stream = STREAM_WIN32_GZIP_DATA;
109 case STREAM_SPARSE_DATA:
110 stream = STREAM_SPARSE_GZIP_DATA;
112 case STREAM_FILE_DATA:
113 stream = STREAM_GZIP_DATA;
117 * All stream types that do not support gzip should clear out
118 * FO_GZIP above, and this code block should be unreachable.
120 ASSERT(!(ff_pkt->flags & FO_GZIP));
126 if (ff_pkt->flags & FO_ENCRYPT) {
128 case STREAM_WIN32_DATA:
129 stream = STREAM_ENCRYPTED_WIN32_DATA;
131 case STREAM_WIN32_GZIP_DATA:
132 stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
134 case STREAM_FILE_DATA:
135 stream = STREAM_ENCRYPTED_FILE_DATA;
137 case STREAM_GZIP_DATA:
138 stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
141 /* All stream types that do not support encryption should clear out
142 * FO_ENCRYPT above, and this code block should be unreachable. */
143 ASSERT(!(ff_pkt->flags & FO_ENCRYPT));
154 * Encode a stat structure into a base64 character string
155 * All systems must create such a structure.
156 * In addition, we tack on the LinkFI, which is non-zero in
157 * the case of a hard linked file that has no data. This
158 * is a File Index pointing to the link that does have the
159 * data (always the first one encountered in a save).
160 * You may piggyback attributes on this packet by encoding
161 * them in the encode_attribsEx() subroutine, but this is
164 void encode_stat(char *buf, struct stat *statp, int32_t LinkFI, int data_stream)
169 * Encode a stat packet. I should have done this more intelligently
170 * with a length so that it could be easily expanded.
172 p += to_base64((int64_t)statp->st_dev, p);
173 *p++ = ' '; /* separate fields with a space */
174 p += to_base64((int64_t)statp->st_ino, p);
176 p += to_base64((int64_t)statp->st_mode, p);
178 p += to_base64((int64_t)statp->st_nlink, p);
180 p += to_base64((int64_t)statp->st_uid, p);
182 p += to_base64((int64_t)statp->st_gid, p);
184 p += to_base64((int64_t)statp->st_rdev, p);
186 p += to_base64((int64_t)statp->st_size, p);
189 p += to_base64((int64_t)statp->st_blksize, p);
191 p += to_base64((int64_t)statp->st_blocks, p);
194 p += to_base64((int64_t)0, p); /* output place holder */
196 p += to_base64((int64_t)0, p); /* output place holder */
199 p += to_base64((int64_t)statp->st_atime, p);
201 p += to_base64((int64_t)statp->st_mtime, p);
203 p += to_base64((int64_t)statp->st_ctime, p);
205 p += to_base64((int64_t)LinkFI, p);
209 /* FreeBSD function */
210 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
212 p += to_base64((int64_t)0, p); /* output place holder */
215 p += to_base64((int64_t)data_stream, p);
221 /* Do casting according to unknown type to keep compiler happy */
223 #define plug(st, val) st = (typeof st)val
225 #if !HAVE_GCC & HAVE_SUN_OS
226 /* Sun compiler does not handle templates correctly */
227 #define plug(st, val) st = val
229 #define plug(st, val) st = val
231 /* Use templates to do the casting */
232 template <class T> void plug(T &st, uint64_t val)
233 { st = static_cast<T>(val); }
238 /** Decode a stat packet from base64 characters */
239 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
244 p += from_base64(&val, p);
245 plug(statp->st_dev, val);
247 p += from_base64(&val, p);
248 plug(statp->st_ino, val);
250 p += from_base64(&val, p);
251 plug(statp->st_mode, val);
253 p += from_base64(&val, p);
254 plug(statp->st_nlink, val);
256 p += from_base64(&val, p);
257 plug(statp->st_uid, val);
259 p += from_base64(&val, p);
260 plug(statp->st_gid, val);
262 p += from_base64(&val, p);
263 plug(statp->st_rdev, val);
265 p += from_base64(&val, p);
266 plug(statp->st_size, val);
269 p += from_base64(&val, p);
270 plug(statp->st_blksize, val);
272 p += from_base64(&val, p);
273 plug(statp->st_blocks, val);
276 p += from_base64(&val, p);
277 // plug(statp->st_blksize, val);
279 p += from_base64(&val, p);
280 // plug(statp->st_blocks, val);
283 p += from_base64(&val, p);
284 plug(statp->st_atime, val);
286 p += from_base64(&val, p);
287 plug(statp->st_mtime, val);
289 p += from_base64(&val, p);
290 plug(statp->st_ctime, val);
292 /* Optional FileIndex of hard linked file data */
293 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
295 p += from_base64(&val, p);
296 *LinkFI = (uint32_t)val;
302 /* FreeBSD user flags */
303 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
305 p += from_base64(&val, p);
307 plug(statp->st_flags, val);
313 /* Look for data stream id */
314 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
316 p += from_base64(&val, p);
323 /** Decode a LinkFI field of encoded stat packet */
324 int32_t decode_LinkFI(char *buf, struct stat *statp)
329 skip_nonspaces(&p); /* st_dev */
330 p++; /* skip space */
331 skip_nonspaces(&p); /* st_ino */
333 p += from_base64(&val, p);
334 plug(statp->st_mode, val); /* st_mode */
336 skip_nonspaces(&p); /* st_nlink */
338 skip_nonspaces(&p); /* st_uid */
340 skip_nonspaces(&p); /* st_gid */
342 skip_nonspaces(&p); /* st_rdev */
344 skip_nonspaces(&p); /* st_size */
346 skip_nonspaces(&p); /* st_blksize */
348 skip_nonspaces(&p); /* st_blocks */
350 skip_nonspaces(&p); /* st_atime */
352 skip_nonspaces(&p); /* st_mtime */
354 skip_nonspaces(&p); /* st_ctime */
356 /* Optional FileIndex of hard linked file data */
357 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
359 p += from_base64(&val, p);
366 * Set file modes, permissions and times
368 * fname is the original filename
369 * ofile is the output filename (may be in a different directory)
371 * Returns: true on success
374 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
387 #if defined(HAVE_WIN32)
388 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
389 set_win32_attributes(jcr, attr, ofd)) {
393 pm_strcpy(attr->ofname, "*none*");
396 if (attr->data_stream == STREAM_WIN32_DATA ||
397 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
401 pm_strcpy(attr->ofname, "*none*");
407 * If Windows stuff failed, e.g. attempt to restore Unix file
408 * to Windows, simply fall through and we will do it the
415 char ec1[50], ec2[50];
416 fsize = blseek(ofd, 0, SEEK_END);
417 bclose(ofd); /* first close file */
418 if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 &&
419 fsize != (boffset_t)attr->statp.st_size) {
420 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
421 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
422 edit_uint64(fsize, ec2));
427 * We do not restore sockets, so skip trying to restore their
430 if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
434 ut.actime = attr->statp.st_atime;
435 ut.modtime = attr->statp.st_mtime;
437 /* ***FIXME**** optimize -- don't do if already correct */
439 * For link, change owner of link using lchown, but don't
440 * try to do a chmod as that will update the file behind it.
442 if (attr->type == FT_LNK) {
443 /** Change owner of link, not of real file */
444 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
446 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
447 attr->ofname, be.bstrerror());
451 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
453 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
454 attr->ofname, be.bstrerror());
457 if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) {
459 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
460 attr->ofname, be.bstrerror());
467 if (utime(attr->ofname, &ut) < 0 && my_uid == 0) {
469 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
470 attr->ofname, be.bstrerror());
477 * Note, this should really be done before the utime() above,
478 * but if the immutable bit is set, it will make the utimes()
481 if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) {
483 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
484 attr->ofname, be.bstrerror());
491 pm_strcpy(attr->ofname, "*none*");
497 /*=============================================================*/
499 /* * * * U n i x * * * * */
501 /*=============================================================*/
503 #if !defined(HAVE_WIN32)
506 * It is possible to piggyback additional data e.g. ACLs on
507 * the encode_stat() data by returning the extended attributes
508 * here. They must be "self-contained" (i.e. you keep track
509 * of your own length), and they must be in ASCII string
510 * format. Using this feature is not recommended.
511 * The code below shows how to return nothing. See the Win32
512 * code below for returning something in the attributes.
514 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
516 #ifdef HAVE_DARWIN_OS
518 * We save the Mac resource fork length so that on a
519 * restore, we can be sure we put back the whole resource.
523 *attribsEx = 0; /* no extended attributes (yet) */
524 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
525 return STREAM_UNIX_ATTRIBUTES;
528 if (ff_pkt->flags & FO_HFSPLUS) {
529 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
533 *attribsEx = 0; /* no extended attributes */
535 return STREAM_UNIX_ATTRIBUTES;
542 /*=============================================================*/
544 /* * * * W i n 3 2 * * * * */
546 /*=============================================================*/
548 #if defined(HAVE_WIN32)
550 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
553 WIN32_FILE_ATTRIBUTE_DATA atts;
556 attribsEx[0] = 0; /* no extended attributes */
558 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
559 return STREAM_UNIX_ATTRIBUTES;
562 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
564 /** try unicode version */
565 if (p_GetFileAttributesExW) {
566 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
567 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
569 BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
571 free_pool_memory(pwszBuf);
574 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
575 return STREAM_UNIX_ATTRIBUTES;
579 if (!p_GetFileAttributesExA)
580 return STREAM_UNIX_ATTRIBUTES;
582 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
584 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
585 return STREAM_UNIX_ATTRIBUTES;
589 p += to_base64((uint64_t)atts.dwFileAttributes, p);
590 *p++ = ' '; /* separate fields with a space */
591 li.LowPart = atts.ftCreationTime.dwLowDateTime;
592 li.HighPart = atts.ftCreationTime.dwHighDateTime;
593 p += to_base64((uint64_t)li.QuadPart, p);
595 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
596 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
597 p += to_base64((uint64_t)li.QuadPart, p);
599 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
600 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
601 p += to_base64((uint64_t)li.QuadPart, p);
603 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
605 p += to_base64((uint64_t)atts.nFileSizeLow, p);
607 return STREAM_UNIX_ATTRIBUTES_EX;
610 /** Define attributes that are legal to set with SetFileAttributes() */
611 #define SET_ATTRS ( \
612 FILE_ATTRIBUTE_ARCHIVE| \
613 FILE_ATTRIBUTE_HIDDEN| \
614 FILE_ATTRIBUTE_NORMAL| \
615 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
616 FILE_ATTRIBUTE_OFFLINE| \
617 FILE_ATTRIBUTE_READONLY| \
618 FILE_ATTRIBUTE_SYSTEM| \
619 FILE_ATTRIBUTE_TEMPORARY)
623 * Set Extended File Attributes for Win32
625 * fname is the original filename
626 * ofile is the output filename (may be in a different directory)
628 * Returns: true on success
631 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
633 char *p = attr->attrEx;
635 WIN32_FILE_ATTRIBUTE_DATA atts;
637 POOLMEM *win32_ofile;
639 /** if we have neither Win ansi nor wchar API, get out */
640 if (!(p_SetFileAttributesW || p_SetFileAttributesA)) {
644 if (!p || !*p) { /* we should have attributes */
645 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
651 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
654 p += from_base64(&val, p);
655 plug(atts.dwFileAttributes, val);
656 p++; /* skip space */
657 p += from_base64(&val, p);
659 atts.ftCreationTime.dwLowDateTime = li.LowPart;
660 atts.ftCreationTime.dwHighDateTime = li.HighPart;
661 p++; /* skip space */
662 p += from_base64(&val, p);
664 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
665 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
666 p++; /* skip space */
667 p += from_base64(&val, p);
669 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
670 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
672 p += from_base64(&val, p);
673 plug(atts.nFileSizeHigh, val);
675 p += from_base64(&val, p);
676 plug(atts.nFileSizeLow, val);
678 /** Convert to Windows path format */
679 win32_ofile = get_pool_memory(PM_FNAME);
680 unix_name_to_win32(&win32_ofile, attr->ofname);
682 /** At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
684 if (!is_bopen(ofd)) {
685 Dmsg1(100, "File not open: %s\n", attr->ofname);
686 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
690 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
691 if (!SetFileTime(bget_handle(ofd),
692 &atts.ftCreationTime,
693 &atts.ftLastAccessTime,
694 &atts.ftLastWriteTime)) {
695 win_error(jcr, "SetFileTime:", win32_ofile);
700 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
701 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
702 if (p_SetFileAttributesW) {
703 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
704 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
706 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
707 free_pool_memory(pwszBuf);
710 win_error(jcr, "SetFileAttributesW:", win32_ofile);
713 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
714 win_error(jcr, "SetFileAttributesA:", win32_ofile);
718 free_pool_memory(win32_ofile);
722 void win_error(JCR *jcr, const char *prefix, POOLMEM *win32_ofile)
724 DWORD lerror = GetLastError();
726 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
727 FORMAT_MESSAGE_FROM_SYSTEM,
734 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
735 strip_trailing_junk(msg);
736 Jmsg3(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
740 void win_error(JCR *jcr, const char *prefix, DWORD lerror)
743 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
744 FORMAT_MESSAGE_FROM_SYSTEM,
751 strip_trailing_junk(msg);
753 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
755 MessageBox(NULL, msg, prefix, MB_OK);
759 #endif /* HAVE_WIN32 */