Beispiel #1
0
/*
 * Get the Access and/or Default ACL of a file.
 */
static __be32 nfsacld_proc_getacl(struct svc_rqst * rqstp,
		struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp)
{
	struct posix_acl *acl;
	struct inode *inode;
	svc_fh *fh;
	__be32 nfserr = 0;

	dprintk("nfsd: GETACL(2acl)   %s\n", SVCFH_fmt(&argp->fh));

	fh = fh_copy(&resp->fh, &argp->fh);
	nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
	if (nfserr)
		RETURN_STATUS(nfserr);

	inode = fh->fh_dentry->d_inode;

	if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
		RETURN_STATUS(nfserr_inval);
	resp->mask = argp->mask;

	nfserr = fh_getattr(fh, &resp->stat);
	if (nfserr)
		goto fail;

	if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
		acl = get_acl(inode, ACL_TYPE_ACCESS);
		if (IS_ERR(acl)) {
			nfserr = nfserrno(PTR_ERR(acl));
			goto fail;
		}
		if (acl == NULL) {
			/* Solaris returns the inode's minimum ACL. */
			acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
		}
		resp->acl_access = acl;
	}
	if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
		/* Check how Solaris handles requests for the Default ACL
		   of a non-directory! */
		acl = get_acl(inode, ACL_TYPE_DEFAULT);
		if (IS_ERR(acl)) {
			nfserr = nfserrno(PTR_ERR(acl));
			goto fail;
		}
		resp->acl_default = acl;
	}

	/* resp->acl_{access,default} are released in nfssvc_release_getacl. */
	RETURN_STATUS(0);

fail:
	posix_acl_release(resp->acl_access);
	posix_acl_release(resp->acl_default);
	RETURN_STATUS(nfserr);
}
Beispiel #2
0
int
nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
		struct nfs4_acl **acl)
{
	struct inode *inode = d_inode(dentry);
	int error = 0;
	struct posix_acl *pacl = NULL, *dpacl = NULL;
	unsigned int flags = 0;
	int size = 0;

	pacl = get_acl(inode, ACL_TYPE_ACCESS);
	if (!pacl)
		pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);

	if (IS_ERR(pacl))
		return PTR_ERR(pacl);

	/* allocate for worst case: one (deny, allow) pair each: */
	size += 2 * pacl->a_count;

	if (S_ISDIR(inode->i_mode)) {
		flags = NFS4_ACL_DIR;
		dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
		if (IS_ERR(dpacl)) {
			error = PTR_ERR(dpacl);
			goto rel_pacl;
		}

		if (dpacl)
			size += 2 * dpacl->a_count;
	}

	*acl = kmalloc(nfs4_acl_bytes(size), GFP_KERNEL);
	if (*acl == NULL) {
		error = -ENOMEM;
		goto out;
	}
	(*acl)->naces = 0;

	_posix_to_nfsv4_one(pacl, *acl, flags & ~NFS4_ACL_TYPE_DEFAULT);

	if (dpacl)
		_posix_to_nfsv4_one(dpacl, *acl, flags | NFS4_ACL_TYPE_DEFAULT);

out:
	posix_acl_release(dpacl);
rel_pacl:
	posix_acl_release(pacl);
	return error;
}
Beispiel #3
0
/* This is used to cache the default acl before a new object is created.
 * The biggest reason for this is to get an idea of how many blocks will
 * actually be required for the create operation if we must inherit an ACL.
 * An ACL write can add up to 3 object creations and an additional file write
 * so we'd prefer not to reserve that many blocks in the journal if we can.
 * It also has the advantage of not loading the ACL with a transaction open,
 * this may seem silly, but if the owner of the directory is doing the
 * creation, the ACL may not be loaded since the permissions wouldn't require
 * it.
 * We return the number of blocks required for the transaction.
 */
int reiserfs_cache_default_acl(struct inode *inode)
{
	struct posix_acl *acl;
	int nblocks = 0;

	if (IS_PRIVATE(inode))
		return 0;

	acl = get_acl(inode, ACL_TYPE_DEFAULT);

	if (acl && !IS_ERR(acl)) {
		int size = reiserfs_acl_size(acl->a_count);

		/* Other xattrs can be created during inode creation. We don't
		 * want to claim too many blocks, so we check to see if we
		 * we need to create the tree to the xattrs, and then we
		 * just want two files. */
		nblocks = reiserfs_xattr_jcreate_nblocks(inode);
		nblocks += JOURNAL_BLOCKS_PER_OBJECT(inode->i_sb);

		REISERFS_I(inode)->i_flags |= i_has_xattr_dir;

		/* We need to account for writes + bitmaps for two files */
		nblocks += reiserfs_xattr_nblocks(inode, size) * 4;
		posix_acl_release(acl);
	}

	return nblocks;
}
Beispiel #4
0
void		init_tree(t_tree *tree, t_tree *ref, t_data *data)
{
	char	*str;

	tree->type = '-';
	tree->acl = get_acl(tree->full_path);
	tree->rights[0] = (tree->stat.st_mode & S_IRUSR) ? 'r' : '-';
	tree->rights[1] = (tree->stat.st_mode & S_IWUSR) ? 'w' : '-';
	tree->rights[2] = (tree->stat.st_mode & S_IXUSR) ? 'x' : '-';
	tree->rights[3] = (tree->stat.st_mode & S_IRGRP) ? 'r' : '-';
	tree->rights[4] = (tree->stat.st_mode & S_IWGRP) ? 'w' : '-';
	tree->rights[5] = (tree->stat.st_mode & S_IXGRP) ? 'x' : '-';
	tree->rights[6] = (tree->stat.st_mode & S_IROTH) ? 'r' : '-';
	tree->rights[7] = (tree->stat.st_mode & S_IWOTH) ? 'w' : '-';
	tree->rights[8] = (tree->stat.st_mode & S_IXOTH) ? 'x' : '-';
	tree->type = (S_ISDIR(tree->stat.st_mode) ? 'd' : tree->type);
	tree->type = (S_ISSOCK(tree->stat.st_mode) ? 's' : tree->type);
	tree->type = (S_ISFIFO(tree->stat.st_mode) ? 'p' : tree->type);
	tree->type = (S_ISCHR(tree->stat.st_mode) ? 'c' : tree->type);
	tree->type = (S_ISBLK(tree->stat.st_mode) ? 'b' : tree->type);
	tree->type = (S_ISLNK(tree->stat.st_mode) ? 'l' : tree->type);
	str = ft_itoa(tree->stat.st_nlink);
	if (ft_strlen(str) > (size_t)ref->physical_link_len)
		ref->physical_link_len = ft_strlen(str);
	free(str);
	get_id(tree, ref, data);
}
Beispiel #5
0
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;
}
Beispiel #6
0
struct posix_acl *ovl_get_acl(struct inode *inode, int type)
{
	struct inode *realinode = ovl_inode_real(inode);
	const struct cred *old_cred;
	struct posix_acl *acl;

	if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
		return NULL;

	old_cred = ovl_override_creds(inode->i_sb);
	acl = get_acl(realinode, type);
	revert_creds(old_cred);

	return acl;
}
Beispiel #7
0
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;
}
Beispiel #8
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;
}
Beispiel #9
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;
}
Beispiel #10
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;
}
Beispiel #11
0
/* 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;
}