int get_extrameta(struct asfd *asfd, BFILE *bfd, struct sbuf *sb, char **extrameta, size_t *elen, struct conf **confs) { #if defined (WIN32_VSS) if(get_vss(bfd, sb, extrameta, elen, confs)) return -1; #endif // Important to do xattr directly after acl, because xattr is excluding // some entries if acls were set. #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_OPENBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_ACL if(get_acl(asfd, sb, extrameta, elen, confs)) return -1; #endif #endif #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_OPENBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_XATTR if(get_xattr(asfd, sb, extrameta, elen, confs)) return -1; #endif #endif return 0; }
static void test_xattr(struct xattrdata x) { size_t rlen=0; char metasymbol=META_XATTR; char *retrieved=NULL; const char *path=NULL; const char *myfile=BASE "/myfile"; char expected[256]; fail_unless(recursive_delete(BASE)==0); build_file(myfile, NULL); if(x.do_dir) path=BASE; else path=myfile; #if defined(HAVE_FREEBSD_OS) || \ defined(HAVE_NETBSD_OS) metasymbol=META_XATTR_BSD; #endif #if defined(HAVE_LINUX_OS) || \ defined(HAVE_DARWIN_OS) metasymbol=META_XATTR; #endif snprintf(expected, sizeof(expected), "%s", x.expected_read); expected[0]=metasymbol; fail_unless(has_xattr(path)==0); fail_unless(!set_xattr( NULL, // asfd path, x.write, strlen(x.write), metasymbol, NULL // cntr )); fail_unless(!get_xattr( NULL, // asfd path, &retrieved, &rlen, NULL // cntr )); fail_unless(rlen==strlen(expected)); assert_xattr(expected, retrieved, rlen); free_w(&retrieved); tear_down(); }
static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG length, ULONG* pbr) { UINT8* xattrdata; UINT16 xattrlen; ULONG readlen; NTSTATUS Status; TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr); if (pbr) *pbr = 0; if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &xattrdata, &xattrlen)) { ERR("get_xattr failed\n"); return STATUS_OBJECT_NAME_NOT_FOUND; } if (start >= xattrlen) { TRACE("tried to read beyond end of stream\n"); Status = STATUS_END_OF_FILE; goto end; } if (length == 0) { WARN("tried to read zero bytes\n"); Status = STATUS_SUCCESS; goto end; } if (start + length < xattrlen) readlen = length; else readlen = (ULONG)xattrlen - (ULONG)start; RtlCopyMemory(data + start, xattrdata, readlen); if (pbr) *pbr = readlen; Status = STATUS_SUCCESS; end: ExFreePool(xattrdata); return Status; }
int get_extrameta(const char *path, struct stat *statp, char **extrameta, size_t *elen, struct cntr *cntr) { // Important to do xattr directly after acl, because xattr is excluding // some entries if acls were set. #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_OPENBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_ACL if(get_acl(path, statp, extrameta, elen, cntr)) return -1; #endif #endif #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_OPENBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_XATTR if(get_xattr(path, statp, extrameta, elen, cntr)) return -1; #endif #endif return 0; }
/* * Add the existing xattr ids and xattr metadata in the file system being * appended to, to the in-memory xattr cache. This allows duplicate checking to * take place against the xattrs already in the file system being appended to, * and ensures the pre-existing xattrs are written out along with any new xattrs */ int get_xattrs(int fd, struct squashfs_super_block *sBlk) { int ids, res, i, id; unsigned int count; TRACE("get_xattrs\n"); res = read_xattrs_from_disk(fd, sBlk); if(res == SQUASHFS_INVALID_BLK || res == 0) goto done; ids = res; /* * for each xattr id read and construct its list of xattr * name:value pairs, and add them to the in-memory xattr cache */ for(i = 0; i < ids; i++) { struct xattr_list *xattr_list = get_xattr(i, &count); if(xattr_list == NULL) { res = 0; goto done; } id = generate_xattrs(count, xattr_list); /* * Sanity check, the new xattr id should be the same as the * xattr id in the original file system */ if(id != i) { ERROR("BUG, different xattr_id in get_xattrs\n"); res = 0; goto done; } } done: return res; }
static ULONG get_ea_len(device_extension* Vcb, root* subvol, UINT64 inode, PIRP Irp) { UINT8* eadata; UINT16 len; if (get_xattr(Vcb, subvol, inode, EA_EA, EA_EA_HASH, &eadata, &len, Irp)) { ULONG offset; NTSTATUS Status; Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)eadata, len, &offset); if (!NT_SUCCESS(Status)) { WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset); ExFreePool(eadata); return 0; } else { FILE_FULL_EA_INFORMATION* eainfo; ULONG ealen; ealen = 4; eainfo = (FILE_FULL_EA_INFORMATION*)eadata; do { ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength; if (eainfo->NextEntryOffset == 0) break; eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset); } while (TRUE); ExFreePool(eadata); return ealen; } } else return 0; }
/**************************************************************************** Create a directory given an absolute path, perms based upon another directory path ****************************************************************************/ int make_bak_dir(const char *fullpath) { char fbuf[MAXPATHLEN], *rel, *end, *p; struct file_struct *file; int len = backup_dir_len; stat_x sx; while (*fullpath == '.' && fullpath[1] == '/') { fullpath += 2; len -= 2; } if (strlcpy(fbuf, fullpath, sizeof fbuf) >= sizeof fbuf) return -1; rel = fbuf + len; end = p = rel + strlen(rel); /* Try to find an existing dir, starting from the deepest dir. */ while (1) { if (--p == fbuf) return -1; if (*p == '/') { *p = '\0'; if (mkdir_defmode(fbuf) == 0) break; if (errno != ENOENT) { rsyserr(FERROR, errno, "make_bak_dir mkdir %s failed", full_fname(fbuf)); return -1; } } } /* Make all the dirs that we didn't find on the way here. */ while (1) { if (p >= rel) { /* Try to transfer the directory settings of the * actual dir that the files are coming from. */ if (x_stat(rel, &sx.st, NULL) < 0) { rsyserr(FERROR, errno, "make_bak_dir stat %s failed", full_fname(rel)); } else { #ifdef SUPPORT_ACLS sx.acc_acl = sx.def_acl = NULL; #endif #ifdef SUPPORT_XATTRS sx.xattr = NULL; #endif if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS))) continue; #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { get_acl(rel, &sx); cache_tmp_acl(file, &sx); free_acl(&sx); } #endif #ifdef SUPPORT_XATTRS if (preserve_xattrs) { get_xattr(rel, &sx); cache_tmp_xattr(file, &sx); free_xattr(&sx); } #endif set_file_attrs(fbuf, file, NULL, NULL, 0); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif } } *p = '/'; p += strlen(p); if (p == end) break; if (mkdir_defmode(fbuf) < 0) { rsyserr(FERROR, errno, "make_bak_dir mkdir %s failed", full_fname(fbuf)); return -1; } } return 0; }
/* If we have a --backup-dir, then we get here from make_backup(). * We will move the file to be deleted into a parallel directory tree. */ static int keep_backup(const char *fname) { stat_x sx; struct file_struct *file; char *buf; int save_preserve_xattrs = preserve_xattrs; int kept = 0; int ret_code; /* return if no file to keep */ if (x_lstat(fname, &sx.st, NULL) < 0) return 1; #ifdef SUPPORT_ACLS sx.acc_acl = sx.def_acl = NULL; #endif #ifdef SUPPORT_XATTRS sx.xattr = NULL; #endif if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) return 1; /* the file could have disappeared */ if (!(buf = get_backup_name(fname))) { unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 0; } #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { get_acl(fname, &sx); cache_tmp_acl(file, &sx); free_acl(&sx); } #endif #ifdef SUPPORT_XATTRS if (preserve_xattrs) { get_xattr(fname, &sx); cache_tmp_xattr(file, &sx); free_xattr(&sx); } #endif /* Check to see if this is a device file, or link */ if ((am_root && preserve_devices && IS_DEVICE(file->mode)) || (preserve_specials && IS_SPECIAL(file->mode))) { int save_errno; do_unlink(buf); if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) { save_errno = errno ? errno : EINVAL; /* 0 paranoia */ if (errno == ENOENT && make_bak_dir(buf) == 0) { if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) save_errno = errno ? errno : save_errno; else save_errno = 0; } if (save_errno) { rsyserr(FERROR, save_errno, "mknod %s failed", full_fname(buf)); } } else save_errno = 0; if (verbose > 2 && save_errno == 0) { rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname); } kept = 1; do_unlink(fname); } if (!kept && S_ISDIR(file->mode)) { /* make an empty directory */ if (do_mkdir(buf, file->mode) < 0) { int save_errno = errno ? errno : EINVAL; /* 0 paranoia */ if (errno == ENOENT && make_bak_dir(buf) == 0) { if (do_mkdir(buf, file->mode) < 0) save_errno = errno ? errno : save_errno; else save_errno = 0; } if (save_errno) { rsyserr(FINFO, save_errno, "mkdir %s failed", full_fname(buf)); } } ret_code = do_rmdir(fname); if (verbose > 2) { rprintf(FINFO, "make_backup: RMDIR %s returns %i\n", full_fname(fname), ret_code); } kept = 1; } #ifdef SUPPORT_LINKS if (!kept && preserve_links && S_ISLNK(file->mode)) { const char *sl = F_SYMLINK(file); if (safe_symlinks && unsafe_symlink(sl, fname)) { if (verbose) { rprintf(FINFO, "ignoring unsafe symlink %s -> %s\n", full_fname(buf), sl); } kept = 1; } else { do_unlink(buf); if (do_symlink(sl, buf) < 0) { int save_errno = errno ? errno : EINVAL; /* 0 paranoia */ if (errno == ENOENT && make_bak_dir(buf) == 0) { if (do_symlink(sl, buf) < 0) save_errno = errno ? errno : save_errno; else save_errno = 0; } if (save_errno) { rsyserr(FERROR, save_errno, "link %s -> \"%s\"", full_fname(buf), sl); } } do_unlink(fname); kept = 1; } } #endif if (!kept && !S_ISREG(file->mode)) { rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 1; } /* move to keep tree if a file */ if (!kept) { if (robust_move(fname, buf) != 0) { rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"", full_fname(fname), buf); } else if (sx.st.st_nlink > 1) { /* If someone has hard-linked the file into the backup * dir, rename() might return success but do nothing! */ robust_unlink(fname); /* Just in case... */ } } preserve_xattrs = 0; set_file_attrs(buf, file, NULL, fname, 0); preserve_xattrs = save_preserve_xattrs; unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif if (verbose > 1) { rprintf(FINFO, "backed up %s to %s\n", fname, buf); } return 1; }
int main(int argc, char *argv[]) { unsigned abits = DATA_BTREE_BIT|CTIME_SIZE_BIT|MODE_OWNER_BIT|LINK_COUNT_BIT|MTIME_BIT; struct dev *dev = &(struct dev){ .bits = 8, .fd = open(argv[1], O_CREAT|O_RDWR, S_IRUSR|S_IWUSR) }; assert(!ftruncate(dev->fd, 1 << 24)); init_buffers(dev, 1 << 20, 0); struct sb *sb = rapid_sb(dev, .version = 0, .atomref_base = 1 << 10, .unatom_base = 1 << 11, .atomgen = 1); struct inode *inode = rapid_open_inode(sb, NULL, S_IFDIR | 0x666, .present = abits, .i_uid = 0x12121212, .i_gid = 0x34343434, .i_ctime = spectime(0xdec0debeadULL), .i_mtime = spectime(0xbadfaced00dULL)); inode->btree = (struct btree){ .root = { .block = 0xcaba1f00dULL, .depth = 3 } }; sb->atable = inode; for (int i = 0; i < 2; i++) { struct buffer_head *buffer = blockget(mapping(inode), tux_sb(inode->i_sb)->atomref_base + i); memset(bufdata(buffer), 0, sb->blocksize); blockput_dirty(buffer); } if (1) { warn("---- test positive and negative refcount carry ----"); use_atom(inode, 6, 1 << 15); use_atom(inode, 6, (1 << 15)); use_atom(inode, 6, -(1 << 15)); use_atom(inode, 6, -(1 << 15)); } warn("---- test atom table ----"); printf("atom = %Lx\n", (L)make_atom(inode, "foo", 3)); printf("atom = %Lx\n", (L)make_atom(inode, "foo", 3)); printf("atom = %Lx\n", (L)make_atom(inode, "bar", 3)); printf("atom = %Lx\n", (L)make_atom(inode, "foo", 3)); printf("atom = %Lx\n", (L)make_atom(inode, "bar", 3)); warn("---- test inode xattr cache ----"); int err; err = xcache_update(inode, 0x666, "hello", 5, 0); if (err) printf("err %d\n", err); err = xcache_update(inode, 0x777, "world!", 6, 0); if (err) printf("err %d\n", err); xcache_dump(inode); struct xattr *xattr = xcache_lookup(tux_inode(inode)->xcache, 0x777); if (!IS_ERR(xattr)) printf("atom %x => %.*s\n", xattr->atom, xattr->size, xattr->body); err = xcache_update(inode, 0x111, "class", 5, 0); if (err) printf("err %d\n", err); err = xcache_update(inode, 0x666, NULL, 0, 0); if (err) printf("err %d\n", err); err = xcache_update(inode, 0x222, "boooyah", 7, 0); if (err) printf("err %d\n", err); xcache_dump(inode); warn("---- test xattr inode table encode and decode ----"); char attrs[1000] = { }; char *top = encode_xattrs(inode, attrs, sizeof(attrs)); hexdump(attrs, top - attrs); printf("predicted size = %x, encoded size = %Lx\n", encode_xsize(inode), (L)(top - attrs)); inode->xcache->size = offsetof(struct xcache, xattrs); char *newtop = decode_attrs(inode, attrs, top - attrs); printf("predicted size = %x, xcache size = %x\n", decode_xsize(inode, attrs, top - attrs), inode->xcache->size); assert(top == newtop); xcache_dump(inode); free(inode->xcache); inode->xcache = NULL; warn("---- xattr update ----"); set_xattr(inode, "hello", 5, "world!", 6, 0); set_xattr(inode, "empty", 5, "zot", 0, 0); set_xattr(inode, "foo", 3, "foobar", 6, 0); xcache_dump(inode); warn("---- xattr remove ----"); // del_xattr(inode, "hello", 5); xcache_dump(inode); warn("---- xattr lookup ----"); for (int i = 0, len; i < 3; i++) { char *namelist[] = { "hello", "foo", "world" }, *name = namelist[i]; char data[100]; int size = get_xattr(inode, name, len = strlen(name), data, sizeof(data)); if (size < 0) printf("xattr %.*s not found (%s)\n", len, name, strerror(-size)); else printf("found xattr %.*s => %.*s\n", len, name, size, data); } warn("---- list xattrs ----"); int len = xattr_list(inode, attrs, sizeof(attrs)); printf("xattr list length = %i\n", xattr_list(inode, NULL, 0)); hexdump(attrs, len); warn("---- atom reverse map ----"); for (int i = 0; i < 5; i++) { unsigned atom = i, offset; struct buffer_head *buffer = blockread_unatom(inode, atom, &offset); loff_t where = from_be_u64(((be_u64 *)bufdata(buffer))[offset]); blockput_dirty(buffer); buffer = blockread(mapping(inode), where >> sb->blockbits); printf("atom %.3Lx at dirent %.4Lx, ", (L)atom, (L)where); hexdump(bufdata(buffer) + (where & sb->blockmask), 16); blockput(buffer); } warn("---- atom recycle ----"); set_xattr(inode, "hello", 5, NULL, 0, 0); show_freeatoms(sb); printf("got free atom %x\n", get_freeatom(inode)); printf("got free atom %x\n", get_freeatom(inode)); printf("got free atom %x\n", get_freeatom(inode)); warn("---- dump atom table ----"); dump_atoms(inode); show_buffers(inode->map); exit(0); }
/* Create a backup path from the given fname, putting the result into * backup_dir_buf. Any new directories (compared to the prior backup * path) are ensured to exist as directories, replacing anything else * that may be in the way (e.g. a symlink). */ static BOOL copy_valid_path(const char *fname) { const char *f; int val; BOOL ret = True; stat_x sx; char *b, *rel = backup_dir_buf + backup_dir_len, *name = rel; for (f = fname, b = rel; *f && *f == *b; f++, b++) { if (*b == '/') name = b + 1; } if (stringjoin(rel, backup_dir_remainder, fname, backup_suffix, NULL) >= backup_dir_remainder) { rprintf(FERROR, "backup filename too long\n"); *name = '\0'; return False; } for ( ; ; name = b + 1) { if ((b = strchr(name, '/')) == NULL) return True; *b = '\0'; val = validate_backup_dir(); if (val == 0) break; if (val < 0) { *name = '\0'; return False; } *b = '/'; } init_stat_x(&sx); for ( ; b; name = b + 1, b = strchr(name, '/')) { *b = '\0'; while (do_mkdir(backup_dir_buf, ACCESSPERMS) < 0) { if (errno == EEXIST) { val = validate_backup_dir(); if (val > 0) break; if (val == 0) continue; } else rsyserr(FERROR, errno, "backup mkdir %s failed", backup_dir_buf); *name = '\0'; ret = False; goto cleanup; } /* Try to transfer the directory settings of the actual dir * that the files are coming from. */ if (x_stat(rel, &sx.st, NULL) < 0) rsyserr(FERROR, errno, "backup stat %s failed", full_fname(rel)); else { struct file_struct *file; if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS))) continue; #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { get_acl(rel, &sx); cache_tmp_acl(file, &sx); free_acl(&sx); } #endif #ifdef SUPPORT_XATTRS if (preserve_xattrs) { get_xattr(rel, &sx); cache_tmp_xattr(file, &sx); free_xattr(&sx); } #endif set_file_attrs(backup_dir_buf, file, NULL, NULL, 0); unmake_file(file); } *b = '/'; } cleanup: #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return ret; }
/* Hard-link, rename, or copy an item to the backup name. Returns 0 for * failure, 1 if item was moved, 2 if item was duplicated or hard linked * into backup area, or 3 if item doesn't exist or isn't a regular file. */ int make_backup(const char *fname, BOOL prefer_rename) { stat_x sx; struct file_struct *file; int save_preserve_xattrs; char *buf; int ret = 0; init_stat_x(&sx); /* Return success if no file to keep. */ if (x_lstat(fname, &sx.st, NULL) < 0) return 3; if (!(buf = get_backup_name(fname))) return 0; /* Try a hard-link or a rename first. Using rename is not atomic, but * is more efficient than forcing a copy for larger files when no hard- * linking is possible. */ if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0) goto success; if (errno == EEXIST || errno == EISDIR) { STRUCT_STAT bakst; if (do_lstat(buf, &bakst) == 0) { int flags = get_del_for_flag(bakst.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE; if (delete_item(buf, bakst.st_mode, flags) != 0) return 0; } if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0) goto success; } /* Fall back to making a copy. */ if (!(file = make_file(fname, NULL, &sx.st, 0, NO_FILTERS))) return 3; /* the file could have disappeared */ #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { get_acl(fname, &sx); cache_tmp_acl(file, &sx); free_acl(&sx); } #endif #ifdef SUPPORT_XATTRS if (preserve_xattrs) { get_xattr(fname, &sx); cache_tmp_xattr(file, &sx); free_xattr(&sx); } #endif /* Check to see if this is a device file, or link */ if ((am_root && preserve_devices && IS_DEVICE(file->mode)) || (preserve_specials && IS_SPECIAL(file->mode))) { if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf)); else if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname); ret = 2; } #ifdef SUPPORT_LINKS if (!ret && preserve_links && S_ISLNK(file->mode)) { const char *sl = F_SYMLINK(file); if (safe_symlinks && unsafe_symlink(sl, fname)) { if (INFO_GTE(SYMSAFE, 1)) { rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n", fname, sl); } ret = 2; } else { if (do_symlink(sl, buf) < 0) rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl); else if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname); ret = 2; } } #endif if (!ret && !S_ISREG(file->mode)) { rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 3; } /* Copy to backup tree if a file. */ if (!ret) { if (copy_file(fname, buf, -1, file->mode) < 0) { rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"", full_fname(fname), buf); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 0; } if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: COPY %s successful.\n", fname); ret = 2; } save_preserve_xattrs = preserve_xattrs; preserve_xattrs = 0; set_file_attrs(buf, file, NULL, fname, ATTRS_SET_NANO); preserve_xattrs = save_preserve_xattrs; unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif success: if (INFO_GTE(BACKUP, 1)) rprintf(FINFO, "backed up %s to %s\n", fname, buf); return ret; }