2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2009 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
42 static uid_t my_uid = 1;
43 static gid_t my_gid = 1;
44 static bool uid_set = false;
47 #if defined(HAVE_WIN32)
48 /* Forward referenced subroutines */
49 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd);
50 void unix_name_to_win32(POOLMEM **win32_name, char *name);
51 void win_error(JCR *jcr, char *prefix, POOLMEM *ofile);
52 HANDLE bget_handle(BFILE *bfd);
53 #endif /* HAVE_WIN32 */
55 /* For old systems that don't have lchown() use chown() */
60 /*=============================================================*/
62 /* *** A l l S y s t e m s *** */
64 /*=============================================================*/
67 * Return the data stream that will be used
69 int select_data_stream(FF_PKT *ff_pkt)
74 * Fix all incompatible options
77 /* No sparse option for encrypted data */
78 if (ff_pkt->flags & FO_ENCRYPT) {
79 ff_pkt->flags &= ~FO_SPARSE;
82 /* Note, no sparse option for win32_data */
83 if (!is_portable_backup(&ff_pkt->bfd)) {
84 stream = STREAM_WIN32_DATA;
85 ff_pkt->flags &= ~FO_SPARSE;
86 } else if (ff_pkt->flags & FO_SPARSE) {
87 stream = STREAM_SPARSE_DATA;
89 stream = STREAM_FILE_DATA;
92 /* Encryption is only supported for file data */
93 if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
94 stream != STREAM_MACOS_FORK_DATA) {
95 ff_pkt->flags &= ~FO_ENCRYPT;
98 /* Compression is not supported for Mac fork data */
99 if (stream == STREAM_MACOS_FORK_DATA) {
100 ff_pkt->flags &= ~FO_GZIP;
104 * Handle compression and encryption options
107 if (ff_pkt->flags & FO_GZIP) {
109 case STREAM_WIN32_DATA:
110 stream = STREAM_WIN32_GZIP_DATA;
112 case STREAM_SPARSE_DATA:
113 stream = STREAM_SPARSE_GZIP_DATA;
115 case STREAM_FILE_DATA:
116 stream = STREAM_GZIP_DATA;
119 /* All stream types that do not support gzip should clear out
120 * FO_GZIP above, and this code block should be unreachable. */
121 ASSERT(!(ff_pkt->flags & FO_GZIP));
127 if (ff_pkt->flags & FO_ENCRYPT) {
129 case STREAM_WIN32_DATA:
130 stream = STREAM_ENCRYPTED_WIN32_DATA;
132 case STREAM_WIN32_GZIP_DATA:
133 stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
135 case STREAM_FILE_DATA:
136 stream = STREAM_ENCRYPTED_FILE_DATA;
138 case STREAM_GZIP_DATA:
139 stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
142 /* All stream types that do not support encryption should clear out
143 * FO_ENCRYPT above, and this code block should be unreachable. */
144 ASSERT(!(ff_pkt->flags & FO_ENCRYPT));
155 * Encode a stat structure into a base64 character string
156 * All systems must create such a structure.
157 * In addition, we tack on the LinkFI, which is non-zero in
158 * the case of a hard linked file that has no data. This
159 * is a File Index pointing to the link that does have the
160 * data (always the first one encountered in a save).
161 * You may piggyback attributes on this packet by encoding
162 * them in the encode_attribsEx() subroutine, but this is
165 void encode_stat(char *buf, struct stat *statp, int32_t LinkFI, int data_stream)
170 * Encode a stat packet. I should have done this more intelligently
171 * with a length so that it could be easily expanded.
173 p += to_base64((int64_t)statp->st_dev, p);
174 *p++ = ' '; /* separate fields with a space */
175 p += to_base64((int64_t)statp->st_ino, p);
177 p += to_base64((int64_t)statp->st_mode, p);
179 p += to_base64((int64_t)statp->st_nlink, p);
181 p += to_base64((int64_t)statp->st_uid, p);
183 p += to_base64((int64_t)statp->st_gid, p);
185 p += to_base64((int64_t)statp->st_rdev, p);
187 p += to_base64((int64_t)statp->st_size, p);
190 p += to_base64((int64_t)statp->st_blksize, p);
192 p += to_base64((int64_t)statp->st_blocks, p);
195 p += to_base64((int64_t)0, p); /* output place holder */
197 p += to_base64((int64_t)0, p); /* output place holder */
200 p += to_base64((int64_t)statp->st_atime, p);
202 p += to_base64((int64_t)statp->st_mtime, p);
204 p += to_base64((int64_t)statp->st_ctime, p);
206 p += to_base64((int64_t)LinkFI, p);
210 /* FreeBSD function */
211 p += to_base64((int64_t)statp->st_flags, p); /* output st_flags */
213 p += to_base64((int64_t)0, p); /* output place holder */
216 p += to_base64((int64_t)data_stream, p);
222 /* Do casting according to unknown type to keep compiler happy */
224 #define plug(st, val) st = (typeof st)val
226 #if !HAVE_GCC & HAVE_SUN_OS
227 /* Sun compiler does not handle templates correctly */
228 #define plug(st, val) st = val
230 #define plug(st, val) st = val
232 /* Use templates to do the casting */
233 template <class T> void plug(T &st, uint64_t val)
234 { st = static_cast<T>(val); }
239 /* Decode a stat packet from base64 characters */
240 int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI)
245 p += from_base64(&val, p);
246 plug(statp->st_dev, val);
248 p += from_base64(&val, p);
249 plug(statp->st_ino, val);
251 p += from_base64(&val, p);
252 plug(statp->st_mode, val);
254 p += from_base64(&val, p);
255 plug(statp->st_nlink, val);
257 p += from_base64(&val, p);
258 plug(statp->st_uid, val);
260 p += from_base64(&val, p);
261 plug(statp->st_gid, val);
263 p += from_base64(&val, p);
264 plug(statp->st_rdev, val);
266 p += from_base64(&val, p);
267 plug(statp->st_size, val);
270 p += from_base64(&val, p);
271 plug(statp->st_blksize, val);
273 p += from_base64(&val, p);
274 plug(statp->st_blocks, val);
277 p += from_base64(&val, p);
278 // plug(statp->st_blksize, val);
280 p += from_base64(&val, p);
281 // plug(statp->st_blocks, val);
284 p += from_base64(&val, p);
285 plug(statp->st_atime, val);
287 p += from_base64(&val, p);
288 plug(statp->st_mtime, val);
290 p += from_base64(&val, p);
291 plug(statp->st_ctime, val);
293 /* Optional FileIndex of hard linked file data */
294 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
296 p += from_base64(&val, p);
297 *LinkFI = (uint32_t)val;
303 /* FreeBSD user flags */
304 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
306 p += from_base64(&val, p);
308 plug(statp->st_flags, val);
314 /* Look for data stream id */
315 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
317 p += from_base64(&val, p);
324 /* Decode a LinkFI field of encoded stat packet */
325 int32_t decode_LinkFI(char *buf, struct stat *statp)
330 skip_nonspaces(&p); /* st_dev */
331 p++; /* skip space */
332 skip_nonspaces(&p); /* st_ino */
334 p += from_base64(&val, p);
335 plug(statp->st_mode, val); /* st_mode */
337 skip_nonspaces(&p); /* st_nlink */
339 skip_nonspaces(&p); /* st_uid */
341 skip_nonspaces(&p); /* st_gid */
343 skip_nonspaces(&p); /* st_rdev */
345 skip_nonspaces(&p); /* st_size */
347 skip_nonspaces(&p); /* st_blksize */
349 skip_nonspaces(&p); /* st_blocks */
351 skip_nonspaces(&p); /* st_atime */
353 skip_nonspaces(&p); /* st_mtime */
355 skip_nonspaces(&p); /* st_ctime */
357 /* Optional FileIndex of hard linked file data */
358 if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
360 p += from_base64(&val, p);
367 * Set file modes, permissions and times
369 * fname is the original filename
370 * ofile is the output filename (may be in a different directory)
372 * Returns: true on success
375 bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
388 #if defined(HAVE_WIN32)
389 if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
390 set_win32_attributes(jcr, attr, ofd)) {
394 pm_strcpy(attr->ofname, "*none*");
397 if (attr->data_stream == STREAM_WIN32_DATA ||
398 attr->data_stream == STREAM_WIN32_GZIP_DATA) {
402 pm_strcpy(attr->ofname, "*none*");
408 * If Windows stuff failed, e.g. attempt to restore Unix file
409 * to Windows, simply fall through and we will do it the
416 char ec1[50], ec2[50];
417 fsize = blseek(ofd, 0, SEEK_END);
418 bclose(ofd); /* first close file */
419 if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 &&
420 fsize != (boffset_t)attr->statp.st_size) {
421 Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
422 attr->ofname, edit_uint64(attr->statp.st_size, ec1),
423 edit_uint64(fsize, ec2));
428 * We do not restore sockets, so skip trying to restore their
431 if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
435 ut.actime = attr->statp.st_atime;
436 ut.modtime = attr->statp.st_mtime;
438 /* ***FIXME**** optimize -- don't do if already correct */
440 * For link, change owner of link using lchown, but don't
441 * try to do a chmod as that will update the file behind it.
443 if (attr->type == FT_LNK) {
444 /* Change owner of link, not of real file */
445 if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
447 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
448 attr->ofname, be.bstrerror());
452 if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
454 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
455 attr->ofname, be.bstrerror());
458 if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) {
460 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
461 attr->ofname, be.bstrerror());
468 if (utime(attr->ofname, &ut) < 0 && my_uid == 0) {
470 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
471 attr->ofname, be.bstrerror());
478 * Note, this should really be done before the utime() above,
479 * but if the immutable bit is set, it will make the utimes()
482 if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) {
484 Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
485 attr->ofname, be.bstrerror());
492 pm_strcpy(attr->ofname, "*none*");
498 /*=============================================================*/
500 /* * * * U n i x * * * * */
502 /*=============================================================*/
504 #if !defined(HAVE_WIN32)
507 * It is possible to piggyback additional data e.g. ACLs on
508 * the encode_stat() data by returning the extended attributes
509 * here. They must be "self-contained" (i.e. you keep track
510 * of your own length), and they must be in ASCII string
511 * format. Using this feature is not recommended.
512 * The code below shows how to return nothing. See the Win32
513 * code below for returning something in the attributes.
515 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
517 #ifdef HAVE_DARWIN_OS
519 * We save the Mac resource fork length so that on a
520 * restore, we can be sure we put back the whole resource.
524 *attribsEx = 0; /* no extended attributes (yet) */
525 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
526 return STREAM_UNIX_ATTRIBUTES;
529 if (ff_pkt->flags & FO_HFSPLUS) {
530 p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
534 *attribsEx = 0; /* no extended attributes */
536 return STREAM_UNIX_ATTRIBUTES;
543 /*=============================================================*/
545 /* * * * W i n 3 2 * * * * */
547 /*=============================================================*/
549 #if defined(HAVE_WIN32)
551 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
554 WIN32_FILE_ATTRIBUTE_DATA atts;
557 attribsEx[0] = 0; /* no extended attributes */
559 if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
560 return STREAM_UNIX_ATTRIBUTES;
563 unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
565 // try unicode version
566 if (p_GetFileAttributesExW) {
567 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
568 make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname);
570 BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
572 free_pool_memory(pwszBuf);
575 win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
576 return STREAM_UNIX_ATTRIBUTES;
580 if (!p_GetFileAttributesExA)
581 return STREAM_UNIX_ATTRIBUTES;
583 if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
585 win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
586 return STREAM_UNIX_ATTRIBUTES;
590 p += to_base64((uint64_t)atts.dwFileAttributes, p);
591 *p++ = ' '; /* separate fields with a space */
592 li.LowPart = atts.ftCreationTime.dwLowDateTime;
593 li.HighPart = atts.ftCreationTime.dwHighDateTime;
594 p += to_base64((uint64_t)li.QuadPart, p);
596 li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
597 li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
598 p += to_base64((uint64_t)li.QuadPart, p);
600 li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
601 li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
602 p += to_base64((uint64_t)li.QuadPart, p);
604 p += to_base64((uint64_t)atts.nFileSizeHigh, p);
606 p += to_base64((uint64_t)atts.nFileSizeLow, p);
608 return STREAM_UNIX_ATTRIBUTES_EX;
611 /* Define attributes that are legal to set with SetFileAttributes() */
612 #define SET_ATTRS ( \
613 FILE_ATTRIBUTE_ARCHIVE| \
614 FILE_ATTRIBUTE_HIDDEN| \
615 FILE_ATTRIBUTE_NORMAL| \
616 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
617 FILE_ATTRIBUTE_OFFLINE| \
618 FILE_ATTRIBUTE_READONLY| \
619 FILE_ATTRIBUTE_SYSTEM| \
620 FILE_ATTRIBUTE_TEMPORARY)
624 * Set Extended File Attributes for Win32
626 * fname is the original filename
627 * ofile is the output filename (may be in a different directory)
629 * Returns: true on success
632 static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
634 char *p = attr->attrEx;
636 WIN32_FILE_ATTRIBUTE_DATA atts;
638 POOLMEM *win32_ofile;
640 // if we have neither ansi nor wchar version, we leave
641 if (!(p_SetFileAttributesW || p_SetFileAttributesA)) {
645 if (!p || !*p) { /* we should have attributes */
646 Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
652 Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
655 p += from_base64(&val, p);
656 plug(atts.dwFileAttributes, val);
657 p++; /* skip space */
658 p += from_base64(&val, p);
660 atts.ftCreationTime.dwLowDateTime = li.LowPart;
661 atts.ftCreationTime.dwHighDateTime = li.HighPart;
662 p++; /* skip space */
663 p += from_base64(&val, p);
665 atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
666 atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
667 p++; /* skip space */
668 p += from_base64(&val, p);
670 atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
671 atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
673 p += from_base64(&val, p);
674 plug(atts.nFileSizeHigh, val);
676 p += from_base64(&val, p);
677 plug(atts.nFileSizeLow, val);
679 /* Convert to Windows path format */
680 win32_ofile = get_pool_memory(PM_FNAME);
681 unix_name_to_win32(&win32_ofile, attr->ofname);
683 /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
685 if (!is_bopen(ofd)) {
686 Dmsg1(100, "File not open: %s\n", attr->ofname);
687 bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0); /* attempt to open the file */
691 Dmsg1(100, "SetFileTime %s\n", attr->ofname);
692 if (!SetFileTime(bget_handle(ofd),
693 &atts.ftCreationTime,
694 &atts.ftLastAccessTime,
695 &atts.ftLastWriteTime)) {
696 win_error(jcr, "SetFileTime:", win32_ofile);
701 Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
702 if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
703 if (p_SetFileAttributesW) {
704 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
705 make_win32_path_UTF8_2_wchar(&pwszBuf, attr->ofname);
707 BOOL b=p_SetFileAttributesW((LPCWSTR)pwszBuf, atts.dwFileAttributes & SET_ATTRS);
708 free_pool_memory(pwszBuf);
711 win_error(jcr, "SetFileAttributesW:", win32_ofile);
714 if (!p_SetFileAttributesA(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
715 win_error(jcr, "SetFileAttributesA:", win32_ofile);
719 free_pool_memory(win32_ofile);
723 void win_error(JCR *jcr, char *prefix, POOLMEM *win32_ofile)
725 DWORD lerror = GetLastError();
727 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
728 FORMAT_MESSAGE_FROM_SYSTEM,
735 Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
736 strip_trailing_junk(msg);
737 Jmsg3(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
741 void win_error(JCR *jcr, char *prefix, DWORD lerror)
744 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
745 FORMAT_MESSAGE_FROM_SYSTEM,
752 strip_trailing_junk(msg);
754 Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
756 MessageBox(NULL, msg, prefix, MB_OK);
760 #endif /* HAVE_WIN32 */