mirror of https://gitee.com/openkylin/linux.git
locks: break delegations on rename
Cc: David Howells <dhowells@redhat.com> Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
5a14696c17
commit
8e6d782cab
|
@ -105,8 +105,8 @@ static inline void ll_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt,
|
|||
#define ll_vfs_unlink(inode,entry,mnt) vfs_unlink(inode,entry)
|
||||
#define ll_vfs_mknod(dir,entry,mnt,mode,dev) vfs_mknod(dir,entry,mode,dev)
|
||||
#define ll_security_inode_unlink(dir,entry,mnt) security_inode_unlink(dir,entry)
|
||||
#define ll_vfs_rename(old,old_dir,mnt,new,new_dir,mnt1) \
|
||||
vfs_rename(old,old_dir,new,new_dir)
|
||||
#define ll_vfs_rename(old,old_dir,mnt,new,new_dir,mnt1,delegated_inode) \
|
||||
vfs_rename(old,old_dir,new,new_dir,delegated_inode)
|
||||
|
||||
#define cfs_bio_io_error(a,b) bio_io_error((a))
|
||||
#define cfs_bio_endio(a,b,c) bio_endio((a),(c))
|
||||
|
|
|
@ -220,7 +220,7 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt,
|
|||
GOTO(put_old, err = PTR_ERR(dchild_new));
|
||||
|
||||
err = ll_vfs_rename(dir->d_inode, dchild_old, mnt,
|
||||
dir->d_inode, dchild_new, mnt);
|
||||
dir->d_inode, dchild_new, mnt, NULL);
|
||||
|
||||
dput(dchild_new);
|
||||
put_old:
|
||||
|
|
|
@ -396,7 +396,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
|
|||
cachefiles_io_error(cache, "Rename security error %d", ret);
|
||||
} else {
|
||||
ret = vfs_rename(dir->d_inode, rep,
|
||||
cache->graveyard->d_inode, grave);
|
||||
cache->graveyard->d_inode, grave, NULL);
|
||||
if (ret != 0 && ret != -ENOMEM)
|
||||
cachefiles_io_error(cache,
|
||||
"Rename failed with error %d", ret);
|
||||
|
|
|
@ -640,7 +640,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
goto out_lock;
|
||||
}
|
||||
rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
|
||||
lower_new_dir_dentry->d_inode, lower_new_dentry);
|
||||
lower_new_dir_dentry->d_inode, lower_new_dentry,
|
||||
NULL);
|
||||
if (rc)
|
||||
goto out_lock;
|
||||
if (target_inode)
|
||||
|
|
47
fs/namei.c
47
fs/namei.c
|
@ -4022,7 +4022,8 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
|
|||
}
|
||||
|
||||
static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
struct inode **delegated_inode)
|
||||
{
|
||||
struct inode *target = new_dentry->d_inode;
|
||||
struct inode *source = old_dentry->d_inode;
|
||||
|
@ -4039,6 +4040,14 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
|
|||
if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
|
||||
goto out;
|
||||
|
||||
error = try_break_deleg(source, delegated_inode);
|
||||
if (error)
|
||||
goto out;
|
||||
if (target) {
|
||||
error = try_break_deleg(target, delegated_inode);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
|
||||
if (error)
|
||||
goto out;
|
||||
|
@ -4053,8 +4062,30 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* vfs_rename - rename a filesystem object
|
||||
* @old_dir: parent of source
|
||||
* @old_dentry: source
|
||||
* @new_dir: parent of destination
|
||||
* @new_dentry: destination
|
||||
* @delegated_inode: returns an inode needing a delegation break
|
||||
*
|
||||
* The caller must hold multiple mutexes--see lock_rename()).
|
||||
*
|
||||
* If vfs_rename discovers a delegation in need of breaking at either
|
||||
* the source or destination, it will return -EWOULDBLOCK and return a
|
||||
* reference to the inode in delegated_inode. The caller should then
|
||||
* break the delegation and retry. Because breaking a delegation may
|
||||
* take a long time, the caller should drop all locks before doing
|
||||
* so.
|
||||
*
|
||||
* Alternatively, a caller may pass NULL for delegated_inode. This may
|
||||
* be appropriate for callers that expect the underlying filesystem not
|
||||
* to be NFS exported.
|
||||
*/
|
||||
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
struct inode **delegated_inode)
|
||||
{
|
||||
int error;
|
||||
int is_dir = d_is_directory(old_dentry) || d_is_autodir(old_dentry);
|
||||
|
@ -4082,7 +4113,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
if (is_dir)
|
||||
error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
|
||||
else
|
||||
error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
|
||||
error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry,delegated_inode);
|
||||
if (!error)
|
||||
fsnotify_move(old_dir, new_dir, old_name, is_dir,
|
||||
new_dentry->d_inode, old_dentry);
|
||||
|
@ -4098,6 +4129,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
|
|||
struct dentry *old_dentry, *new_dentry;
|
||||
struct dentry *trap;
|
||||
struct nameidata oldnd, newnd;
|
||||
struct inode *delegated_inode = NULL;
|
||||
struct filename *from;
|
||||
struct filename *to;
|
||||
unsigned int lookup_flags = 0;
|
||||
|
@ -4137,6 +4169,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
|
|||
newnd.flags &= ~LOOKUP_PARENT;
|
||||
newnd.flags |= LOOKUP_RENAME_TARGET;
|
||||
|
||||
retry_deleg:
|
||||
trap = lock_rename(new_dir, old_dir);
|
||||
|
||||
old_dentry = lookup_hash(&oldnd);
|
||||
|
@ -4173,13 +4206,19 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
|
|||
if (error)
|
||||
goto exit5;
|
||||
error = vfs_rename(old_dir->d_inode, old_dentry,
|
||||
new_dir->d_inode, new_dentry);
|
||||
new_dir->d_inode, new_dentry,
|
||||
&delegated_inode);
|
||||
exit5:
|
||||
dput(new_dentry);
|
||||
exit4:
|
||||
dput(old_dentry);
|
||||
exit3:
|
||||
unlock_rename(new_dir, old_dir);
|
||||
if (delegated_inode) {
|
||||
error = break_deleg_wait(&delegated_inode);
|
||||
if (!error)
|
||||
goto retry_deleg;
|
||||
}
|
||||
mnt_drop_write(oldnd.path.mnt);
|
||||
exit2:
|
||||
if (retry_estale(error, lookup_flags))
|
||||
|
|
|
@ -1837,7 +1837,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
|||
if (host_err)
|
||||
goto out_dput_new;
|
||||
}
|
||||
host_err = vfs_rename(fdir, odentry, tdir, ndentry);
|
||||
host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL);
|
||||
if (!host_err) {
|
||||
host_err = commit_metadata(tfhp);
|
||||
if (!host_err)
|
||||
|
|
|
@ -1456,7 +1456,7 @@ extern int vfs_symlink(struct inode *, struct dentry *, const char *);
|
|||
extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
|
||||
extern int vfs_rmdir(struct inode *, struct dentry *);
|
||||
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
|
||||
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
|
||||
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **);
|
||||
|
||||
/*
|
||||
* VFS dentry helper functions.
|
||||
|
|
Loading…
Reference in New Issue