int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt) { char *p = attribsEx; WIN32_FILE_ATTRIBUTE_DATA atts; ULARGE_INTEGER li; attribsEx[0] = 0; /* no extended attributes */ if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) { return STREAM_UNIX_ATTRIBUTES; } unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname); /** try unicode version */ if (p_GetFileAttributesExW) { POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); make_win32_path_UTF8_2_wchar(&pwszBuf, ff_pkt->fname); BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, (LPVOID)&atts); free_pool_memory(pwszBuf); if (!b) { win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname); return STREAM_UNIX_ATTRIBUTES; } } else { if (!p_GetFileAttributesExA) return STREAM_UNIX_ATTRIBUTES; if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard, (LPVOID)&atts)) { win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname); return STREAM_UNIX_ATTRIBUTES; } } p += to_base64((uint64_t)atts.dwFileAttributes, p); *p++ = ' '; /* separate fields with a space */ li.LowPart = atts.ftCreationTime.dwLowDateTime; li.HighPart = atts.ftCreationTime.dwHighDateTime; p += to_base64((uint64_t)li.QuadPart, p); *p++ = ' '; li.LowPart = atts.ftLastAccessTime.dwLowDateTime; li.HighPart = atts.ftLastAccessTime.dwHighDateTime; p += to_base64((uint64_t)li.QuadPart, p); *p++ = ' '; li.LowPart = atts.ftLastWriteTime.dwLowDateTime; li.HighPart = atts.ftLastWriteTime.dwHighDateTime; p += to_base64((uint64_t)li.QuadPart, p); *p++ = ' '; p += to_base64((uint64_t)atts.nFileSizeHigh, p); *p++ = ' '; p += to_base64((uint64_t)atts.nFileSizeLow, p); *p = 0; return STREAM_UNIX_ATTRIBUTES_EX; }
static int do_stat(const char *file, struct stat *sb, uint64_t *winattr) { WIN32_FILE_ATTRIBUTE_DATA data; errno=0; memset(sb, 0, sizeof(*sb)); memset(winattr, 0, sizeof(*winattr)); if(p_GetFileAttributesExW) { // Dynamically allocate enough space for UCS2 filename. char *pwszBuf=sm_get_pool_memory(PM_FNAME); make_win32_path_UTF8_2_wchar(&pwszBuf, file); BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data); sm_free_pool_memory(pwszBuf); if(!b) return stat2(file, sb, winattr); } else if(p_GetFileAttributesExA) { if(!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) return stat2(file, sb, winattr); } else return stat2(file, sb, winattr); *winattr=(int64_t)data.dwFileAttributes; /* Graham says: all the following stuff seems rather complicated. It is probably not all needed anymore, since I have added *winattr above, which bacula did not do. One reason for keeping it is that some of the values get converted to unix-style permissions that show up in the long list functionality. I think I would prefer to remove it all though. */ sb->st_mode = 0777; // Start with everything. if(data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH); if(data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) sb->st_mode &= ~S_IRWXO; // Remove everything for other. if(data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) sb->st_mode |= S_ISVTX; // use sticky bit -> hidden. if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) sb->st_mode |= S_IFDIR; else sb->st_mode |= S_IFREG; // Use st_rdev to store reparse attribute. sb->st_rdev=(data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)?1:0; sb->st_nlink=1; sb->st_size=data.nFileSizeHigh; sb->st_size<<=32; sb->st_size|=data.nFileSizeLow; sb->st_blksize=4096; sb->st_blocks=(uint32_t)(sb->st_size + 4095)/4096; sb->st_atime=cvt_ftime_to_utime(data.ftLastAccessTime); sb->st_mtime=cvt_ftime_to_utime(data.ftLastWriteTime); sb->st_ctime=cvt_ftime_to_utime(data.ftCreationTime); /* If we are not at the root, then to distinguish a reparse point from a mount point, we must call FindFirstFile() to get the WIN32_FIND_DATA, which has the bit that indicates that this directory is a mount point -- aren't Win32 APIs wonderful? (sarcasm). The code exists in the statDir subroutine. */ if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && file[1]==':' && file[2]) statDir(file, sb, winattr); return 0; }
/* * Restore all file attributes like owner, mode and file times. */ static inline bool restore_file_attributes(JCR *jcr, ATTR *attr, BFILE *ofd) { bool ok = true; bool suppress_errors; #if defined(HAVE_FCHOWN) || defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(FUTIMENS) bool file_is_open; /* * Save if we are working on an open file. */ file_is_open = is_bopen(ofd); #endif /* * See if we want to print errors. */ suppress_errors = (debug_level >= 100 || my_uid != 0); /* * Restore owner and group. */ #ifdef HAVE_FCHOWN if (file_is_open) { if (fchown(ofd->fid, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } } else { #else { #endif if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } } /* * Restore filemode. */ #ifdef HAVE_FCHMOD if (file_is_open) { if (fchmod(ofd->fid, attr->statp.st_mode) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } } else { #else { #endif #if defined(HAVE_WIN32) if (win32_chmod(attr->ofname, attr->statp.st_mode, attr->statp.st_rdev) < 0 && !suppress_errors) { #else if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && !suppress_errors) { #endif berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } } /* * Reset file times. */ #if defined(HAVE_FUTIMES) if (file_is_open) { struct timeval restore_times[2]; restore_times[0].tv_sec = attr->statp.st_atime; restore_times[0].tv_usec = 0; restore_times[1].tv_sec = attr->statp.st_mtime; restore_times[1].tv_usec = 0; if (futimes(ofd->fid, restore_times) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } } else { #elif defined(HAVE_FUTIMENS) if (file_is_open) { struct timespec restore_times[2]; restore_times[0].tv_sec = attr->statp.st_atime; restore_times[0].tv_nsec = 0; restore_times[1].tv_sec = attr->statp.st_mtime; restore_times[1].tv_nsec = 0; if (futimens(ofd->fid, restore_times) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } } else { #else { #endif #if defined(HAVE_LUTIMES) struct timeval restore_times[2]; restore_times[0].tv_sec = attr->statp.st_atime; restore_times[0].tv_usec = 0; restore_times[1].tv_sec = attr->statp.st_mtime; restore_times[1].tv_usec = 0; if (lutimes(attr->ofname, restore_times) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } #elif defined(HAVE_UTIMES) struct timeval restore_times[2]; restore_times[0].tv_sec = attr->statp.st_atime; restore_times[0].tv_usec = 0; restore_times[1].tv_sec = attr->statp.st_mtime; restore_times[1].tv_usec = 0; if (utimes(attr->ofname, restore_times) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } #else struct utimbuf restore_times; restore_times.actime = attr->statp.st_atime; restore_times.modtime = attr->statp.st_mtime; if (utime(attr->ofname, &restore_times) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } #endif /* HAVE_LUTIMES */ } return ok; } /** * Set file modes, permissions and times * * fname is the original filename * ofile is the output filename (may be in a different directory) * * Returns: true on success * false on failure */ bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd) { mode_t old_mask; bool ok = true; bool suppress_errors; if (uid_set) { my_uid = getuid(); my_gid = getgid(); uid_set = true; } /* * See if we want to print errors. */ suppress_errors = (debug_level >= 100 || my_uid != 0); #if defined(HAVE_WIN32) if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX && set_win32_attributes(jcr, attr, ofd)) { if (is_bopen(ofd)) { bclose(ofd); } pm_strcpy(attr->ofname, "*None*"); return true; } if (attr->data_stream == STREAM_WIN32_DATA || attr->data_stream == STREAM_WIN32_GZIP_DATA || attr->data_stream == STREAM_WIN32_COMPRESSED_DATA) { if (is_bopen(ofd)) { bclose(ofd); } pm_strcpy(attr->ofname, "*None*"); return true; } /** * If Windows stuff failed, e.g. attempt to restore Unix file to Windows, simply fall * through and we will do it the universal way. */ #endif old_mask = umask(0); if (is_bopen(ofd)) { boffset_t fsize; char ec1[50], ec2[50]; fsize = blseek(ofd, 0, SEEK_END); if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 && fsize != (boffset_t)attr->statp.st_size) { Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"), attr->ofname, edit_uint64(attr->statp.st_size, ec1), edit_uint64(fsize, ec2)); } } else { struct stat st; char ec1[50], ec2[50]; if (lstat(attr->ofname, &st) == 0) { if (attr->type == FT_REG && st.st_size > 0 && attr->statp.st_size > 0 && st.st_size != attr->statp.st_size) { Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"), attr->ofname, edit_uint64(attr->statp.st_size, ec1), edit_uint64(st.st_size, ec2)); } } } /** * We do not restore sockets, so skip trying to restore their attributes. */ if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) { goto bail_out; } /* ***FIXME**** optimize -- don't do if already correct */ /** * For link, change owner of link using lchown, but don't try to do a chmod as that will update the file behind it. */ if (attr->type == FT_LNK) { /* * Change owner of link, not of real file */ if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } #ifdef HAVE_LCHMOD if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } #endif } else { if (!ofd->cmd_plugin) { ok = restore_file_attributes(jcr, attr, ofd); #ifdef HAVE_CHFLAGS /** * FreeBSD user flags * * Note, this should really be done before the utime() above, * but if the immutable bit is set, it will make the utimes() * fail. */ if (chflags(attr->ofname, attr->statp.st_flags) < 0 && !suppress_errors) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } #endif } } bail_out: if (is_bopen(ofd)) { bclose(ofd); } pm_strcpy(attr->ofname, "*None*"); umask(old_mask); return ok; } #if !defined(HAVE_WIN32) /*=============================================================*/ /* */ /* * * * U n i x * * * * */ /* */ /*=============================================================*/ /** * It is possible to piggyback additional data e.g. ACLs on * the encode_stat() data by returning the extended attributes * here. They must be "self-contained" (i.e. you keep track * of your own length), and they must be in ASCII string * format. Using this feature is not recommended. * The code below shows how to return nothing. See the Win32 * code below for returning something in the attributes. */ int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt) { #ifdef HAVE_DARWIN_OS /** * We save the Mac resource fork length so that on a * restore, we can be sure we put back the whole resource. */ char *p; *attribsEx = 0; /* no extended attributes (yet) */ if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) { return STREAM_UNIX_ATTRIBUTES; } p = attribsEx; if (bit_is_set(FO_HFSPLUS, ff_pkt->flags)) { p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p); } *p = 0; #else *attribsEx = 0; /* no extended attributes */ #endif return STREAM_UNIX_ATTRIBUTES; } #else /*=============================================================*/ /* */ /* * * * W i n 3 2 * * * * */ /* */ /*=============================================================*/ int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt) { char *p = attribsEx; WIN32_FILE_ATTRIBUTE_DATA atts; ULARGE_INTEGER li; attribsEx[0] = 0; /* no extended attributes */ if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) { return STREAM_UNIX_ATTRIBUTES; } unix_name_to_win32(ff_pkt->sys_fname, ff_pkt->fname); if (p_GetFileAttributesExW) { /** * Try unicode version */ POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); make_win32_path_UTF8_2_wchar(pwszBuf, ff_pkt->fname); BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, (LPVOID)&atts); free_pool_memory(pwszBuf); if (!b) { win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname); return STREAM_UNIX_ATTRIBUTES; } } else { if (!p_GetFileAttributesExA) return STREAM_UNIX_ATTRIBUTES; if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard, (LPVOID)&atts)) { win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname); return STREAM_UNIX_ATTRIBUTES; } } /* * Instead of using the current dwFileAttributes use the * ff_pkt->statp.st_rdev which contains the actual fileattributes we * want to save for this file. */ atts.dwFileAttributes = ff_pkt->statp.st_rdev; p += to_base64((uint64_t)atts.dwFileAttributes, p); *p++ = ' '; /* separate fields with a space */ li.LowPart = atts.ftCreationTime.dwLowDateTime; li.HighPart = atts.ftCreationTime.dwHighDateTime; p += to_base64((uint64_t)li.QuadPart, p); *p++ = ' '; li.LowPart = atts.ftLastAccessTime.dwLowDateTime; li.HighPart = atts.ftLastAccessTime.dwHighDateTime; p += to_base64((uint64_t)li.QuadPart, p); *p++ = ' '; li.LowPart = atts.ftLastWriteTime.dwLowDateTime; li.HighPart = atts.ftLastWriteTime.dwHighDateTime; p += to_base64((uint64_t)li.QuadPart, p); *p++ = ' '; p += to_base64((uint64_t)atts.nFileSizeHigh, p); *p++ = ' '; p += to_base64((uint64_t)atts.nFileSizeLow, p); *p = 0; return STREAM_UNIX_ATTRIBUTES_EX; }