fs: introduce lock_rename_child() helper
Pass the dentry of a source file and the dentry of a destination directory to lock parent inodes for rename. As soon as this function returns, ->d_parent of the source file dentry is stable and inodes are properly locked for calling vfs-rename. This helper is needed for ksmbd server. rename request of SMB protocol has to rename an opened file, no matter which directory it's in. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Родитель
211db0ac9e
Коммит
9bc37e0482
68
fs/namei.c
68
fs/namei.c
|
@ -2980,20 +2980,10 @@ static inline int may_create(struct mnt_idmap *idmap,
|
|||
return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* p1 and p2 should be directories on the same fs.
|
||||
*/
|
||||
struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
|
||||
static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2)
|
||||
{
|
||||
struct dentry *p;
|
||||
|
||||
if (p1 == p2) {
|
||||
inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
|
||||
|
||||
p = d_ancestor(p2, p1);
|
||||
if (p) {
|
||||
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
|
||||
|
@ -3012,8 +3002,64 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
|
|||
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* p1 and p2 should be directories on the same fs.
|
||||
*/
|
||||
struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
|
||||
{
|
||||
if (p1 == p2) {
|
||||
inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
|
||||
return lock_two_directories(p1, p2);
|
||||
}
|
||||
EXPORT_SYMBOL(lock_rename);
|
||||
|
||||
/*
|
||||
* c1 and p2 should be on the same fs.
|
||||
*/
|
||||
struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2)
|
||||
{
|
||||
if (READ_ONCE(c1->d_parent) == p2) {
|
||||
/*
|
||||
* hopefully won't need to touch ->s_vfs_rename_mutex at all.
|
||||
*/
|
||||
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
|
||||
/*
|
||||
* now that p2 is locked, nobody can move in or out of it,
|
||||
* so the test below is safe.
|
||||
*/
|
||||
if (likely(c1->d_parent == p2))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* c1 got moved out of p2 while we'd been taking locks;
|
||||
* unlock and fall back to slow case.
|
||||
*/
|
||||
inode_unlock(p2->d_inode);
|
||||
}
|
||||
|
||||
mutex_lock(&c1->d_sb->s_vfs_rename_mutex);
|
||||
/*
|
||||
* nobody can move out of any directories on this fs.
|
||||
*/
|
||||
if (likely(c1->d_parent != p2))
|
||||
return lock_two_directories(c1->d_parent, p2);
|
||||
|
||||
/*
|
||||
* c1 got moved into p2 while we were taking locks;
|
||||
* we need p2 locked and ->s_vfs_rename_mutex unlocked,
|
||||
* for consistency with lock_rename().
|
||||
*/
|
||||
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
|
||||
mutex_unlock(&c1->d_sb->s_vfs_rename_mutex);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(lock_rename_child);
|
||||
|
||||
void unlock_rename(struct dentry *p1, struct dentry *p2)
|
||||
{
|
||||
inode_unlock(p1->d_inode);
|
||||
|
|
|
@ -83,6 +83,7 @@ extern int follow_down(struct path *path, unsigned int flags);
|
|||
extern int follow_up(struct path *);
|
||||
|
||||
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
|
||||
extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
|
||||
extern void unlock_rename(struct dentry *, struct dentry *);
|
||||
|
||||
extern int __must_check nd_jump_link(const struct path *path);
|
||||
|
|
Загрузка…
Ссылка в новой задаче