提交 8e6d782c 编写于 作者: J J. Bruce Fields 提交者: Al Viro

locks: break delegations on rename

Cc: David Howells <dhowells@redhat.com>
Acked-by: NJeff Layton <jlayton@redhat.com>
Signed-off-by: NJ. Bruce Fields <bfields@redhat.com>
Signed-off-by: NAl Viro <viro@zeniv.linux.org.uk>
上级 5a14696c
...@@ -105,8 +105,8 @@ static inline void ll_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt, ...@@ -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_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_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_security_inode_unlink(dir,entry,mnt) security_inode_unlink(dir,entry)
#define ll_vfs_rename(old,old_dir,mnt,new,new_dir,mnt1) \ #define ll_vfs_rename(old,old_dir,mnt,new,new_dir,mnt1,delegated_inode) \
vfs_rename(old,old_dir,new,new_dir) vfs_rename(old,old_dir,new,new_dir,delegated_inode)
#define cfs_bio_io_error(a,b) bio_io_error((a)) #define cfs_bio_io_error(a,b) bio_io_error((a))
#define cfs_bio_endio(a,b,c) bio_endio((a),(c)) #define cfs_bio_endio(a,b,c) bio_endio((a),(c))
......
...@@ -220,7 +220,7 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt, ...@@ -220,7 +220,7 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt,
GOTO(put_old, err = PTR_ERR(dchild_new)); GOTO(put_old, err = PTR_ERR(dchild_new));
err = ll_vfs_rename(dir->d_inode, dchild_old, mnt, 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); dput(dchild_new);
put_old: put_old:
......
...@@ -396,7 +396,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache, ...@@ -396,7 +396,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
cachefiles_io_error(cache, "Rename security error %d", ret); cachefiles_io_error(cache, "Rename security error %d", ret);
} else { } else {
ret = vfs_rename(dir->d_inode, rep, ret = vfs_rename(dir->d_inode, rep,
cache->graveyard->d_inode, grave); cache->graveyard->d_inode, grave, NULL);
if (ret != 0 && ret != -ENOMEM) if (ret != 0 && ret != -ENOMEM)
cachefiles_io_error(cache, cachefiles_io_error(cache,
"Rename failed with error %d", ret); "Rename failed with error %d", ret);
......
...@@ -640,7 +640,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -640,7 +640,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto out_lock; goto out_lock;
} }
rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, 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) if (rc)
goto out_lock; goto out_lock;
if (target_inode) if (target_inode)
......
...@@ -4022,7 +4022,8 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, ...@@ -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, 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 *target = new_dentry->d_inode;
struct inode *source = old_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, ...@@ -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)) if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
goto out; 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); error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
if (error) if (error)
goto out; goto out;
...@@ -4053,8 +4062,30 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, ...@@ -4053,8 +4062,30 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
return error; 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, 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 error;
int is_dir = d_is_directory(old_dentry) || d_is_autodir(old_dentry); 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, ...@@ -4082,7 +4113,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (is_dir) if (is_dir)
error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
else 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) if (!error)
fsnotify_move(old_dir, new_dir, old_name, is_dir, fsnotify_move(old_dir, new_dir, old_name, is_dir,
new_dentry->d_inode, old_dentry); new_dentry->d_inode, old_dentry);
...@@ -4098,6 +4129,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, ...@@ -4098,6 +4129,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
struct dentry *old_dentry, *new_dentry; struct dentry *old_dentry, *new_dentry;
struct dentry *trap; struct dentry *trap;
struct nameidata oldnd, newnd; struct nameidata oldnd, newnd;
struct inode *delegated_inode = NULL;
struct filename *from; struct filename *from;
struct filename *to; struct filename *to;
unsigned int lookup_flags = 0; unsigned int lookup_flags = 0;
...@@ -4137,6 +4169,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, ...@@ -4137,6 +4169,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
newnd.flags &= ~LOOKUP_PARENT; newnd.flags &= ~LOOKUP_PARENT;
newnd.flags |= LOOKUP_RENAME_TARGET; newnd.flags |= LOOKUP_RENAME_TARGET;
retry_deleg:
trap = lock_rename(new_dir, old_dir); trap = lock_rename(new_dir, old_dir);
old_dentry = lookup_hash(&oldnd); old_dentry = lookup_hash(&oldnd);
...@@ -4173,13 +4206,19 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, ...@@ -4173,13 +4206,19 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
if (error) if (error)
goto exit5; goto exit5;
error = vfs_rename(old_dir->d_inode, old_dentry, error = vfs_rename(old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry); new_dir->d_inode, new_dentry,
&delegated_inode);
exit5: exit5:
dput(new_dentry); dput(new_dentry);
exit4: exit4:
dput(old_dentry); dput(old_dentry);
exit3: exit3:
unlock_rename(new_dir, old_dir); 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); mnt_drop_write(oldnd.path.mnt);
exit2: exit2:
if (retry_estale(error, lookup_flags)) if (retry_estale(error, lookup_flags))
......
...@@ -1837,7 +1837,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, ...@@ -1837,7 +1837,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (host_err) if (host_err)
goto out_dput_new; goto out_dput_new;
} }
host_err = vfs_rename(fdir, odentry, tdir, ndentry); host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL);
if (!host_err) { if (!host_err) {
host_err = commit_metadata(tfhp); host_err = commit_metadata(tfhp);
if (!host_err) if (!host_err)
......
...@@ -1456,7 +1456,7 @@ extern int vfs_symlink(struct inode *, struct dentry *, const char *); ...@@ -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_link(struct dentry *, struct inode *, struct dentry *);
extern int vfs_rmdir(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_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. * VFS dentry helper functions.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册