From bda2795a630b2f6c417675bfbf4d90ef7503dfc7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 8 May 2023 07:44:05 -0700 Subject: fs: remove the special !CONFIG_BLOCK def_blk_fops def_blk_fops always returns -ENODEV, which dosn't match the return value of a non-existing block device with CONFIG_BLOCK, which is -ENXIO. Just remove the extra implementation and fall back to the default no_open_fops that always returns -ENXIO. Fixes: 9361401eb761 ("[PATCH] BLOCK: Make it possible to disable the block layer [try #6]") Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230508144405.41792-1-hch@lst.de Signed-off-by: Jens Axboe --- fs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs/inode.c') diff --git a/fs/inode.c b/fs/inode.c index 577799b7855f..4d6a1544e95b 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2264,7 +2264,8 @@ void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) inode->i_fop = &def_chr_fops; inode->i_rdev = rdev; } else if (S_ISBLK(mode)) { - inode->i_fop = &def_blk_fops; + if (IS_ENABLED(CONFIG_BLOCK)) + inode->i_fop = &def_blk_fops; inode->i_rdev = rdev; } else if (S_ISFIFO(mode)) inode->i_fop = &pipefifo_fops; -- cgit v1.2.3 From f23ce757185319886ca80c4864ce5f81ac6cc9e9 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 1 Jun 2023 12:58:24 +0200 Subject: fs: Establish locking order for unrelated directories Currently the locking order of inode locks for directories that are not in ancestor relationship is not defined because all operations that needed to lock two directories like this were serialized by sb->s_vfs_rename_mutex. However some filesystems need to lock two subdirectories for RENAME_EXCHANGE operations and for this we need the locking order established even for two tree-unrelated directories. Provide a helper function lock_two_inodes() that establishes lock ordering for any two inodes and use it in lock_two_directories(). CC: stable@vger.kernel.org Signed-off-by: Jan Kara Message-Id: <20230601105830.13168-4-jack@suse.cz> Signed-off-by: Christian Brauner --- fs/inode.c | 42 ++++++++++++++++++++++++++++++++++++++++++ fs/internal.h | 2 ++ fs/namei.c | 4 ++-- 3 files changed, 46 insertions(+), 2 deletions(-) (limited to 'fs/inode.c') diff --git a/fs/inode.c b/fs/inode.c index 577799b7855f..b9d498032270 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1103,6 +1103,48 @@ void discard_new_inode(struct inode *inode) } EXPORT_SYMBOL(discard_new_inode); +/** + * lock_two_inodes - lock two inodes (may be regular files but also dirs) + * + * Lock any non-NULL argument. The caller must make sure that if he is passing + * in two directories, one is not ancestor of the other. Zero, one or two + * objects may be locked by this function. + * + * @inode1: first inode to lock + * @inode2: second inode to lock + * @subclass1: inode lock subclass for the first lock obtained + * @subclass2: inode lock subclass for the second lock obtained + */ +void lock_two_inodes(struct inode *inode1, struct inode *inode2, + unsigned subclass1, unsigned subclass2) +{ + if (!inode1 || !inode2) { + /* + * Make sure @subclass1 will be used for the acquired lock. + * This is not strictly necessary (no current caller cares) but + * let's keep things consistent. + */ + if (!inode1) + swap(inode1, inode2); + goto lock; + } + + /* + * If one object is directory and the other is not, we must make sure + * to lock directory first as the other object may be its child. + */ + if (S_ISDIR(inode2->i_mode) == S_ISDIR(inode1->i_mode)) { + if (inode1 > inode2) + swap(inode1, inode2); + } else if (!S_ISDIR(inode1->i_mode)) + swap(inode1, inode2); +lock: + if (inode1) + inode_lock_nested(inode1, subclass1); + if (inode2 && inode2 != inode1) + inode_lock_nested(inode2, subclass2); +} + /** * lock_two_nondirectories - take two i_mutexes on non-directory objects * diff --git a/fs/internal.h b/fs/internal.h index bd3b2810a36b..377030a50aca 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -152,6 +152,8 @@ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc); int dentry_needs_remove_privs(struct mnt_idmap *, struct dentry *dentry); bool in_group_or_capable(struct mnt_idmap *idmap, const struct inode *inode, vfsgid_t vfsgid); +void lock_two_inodes(struct inode *inode1, struct inode *inode2, + unsigned subclass1, unsigned subclass2); /* * fs-writeback.c diff --git a/fs/namei.c b/fs/namei.c index e4fe0879ae55..148570aabe74 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3028,8 +3028,8 @@ static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2) return p; } - inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); - inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2); + lock_two_inodes(p1->d_inode, p2->d_inode, + I_MUTEX_PARENT, I_MUTEX_PARENT2); return NULL; } -- cgit v1.2.3 From 2454ad83b90afbc6ed2c22ec1310b624c40bf0d3 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 1 Jun 2023 12:58:26 +0200 Subject: fs: Restrict lock_two_nondirectories() to non-directory inodes Currently lock_two_nondirectories() is skipping any passed directories. After vfs_rename() uses lock_two_inodes(), all the remaining four users of this function pass only regular files to it. So drop the somewhat unusual "skip directory" logic and instead warn if anybody passes directory to it. This also allows us to use lock_two_inodes() in lock_two_nondirectories() to concentrate the lock ordering logic in less places. Signed-off-by: Jan Kara Message-Id: <20230601105830.13168-6-jack@suse.cz> Signed-off-by: Christian Brauner --- fs/inode.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'fs/inode.c') diff --git a/fs/inode.c b/fs/inode.c index b9d498032270..53ae3b76d232 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1148,7 +1148,7 @@ lock: /** * lock_two_nondirectories - take two i_mutexes on non-directory objects * - * Lock any non-NULL argument that is not a directory. + * Lock any non-NULL argument. Passed objects must not be directories. * Zero, one or two objects may be locked by this function. * * @inode1: first inode to lock @@ -1156,13 +1156,9 @@ lock: */ void lock_two_nondirectories(struct inode *inode1, struct inode *inode2) { - if (inode1 > inode2) - swap(inode1, inode2); - - if (inode1 && !S_ISDIR(inode1->i_mode)) - inode_lock(inode1); - if (inode2 && !S_ISDIR(inode2->i_mode) && inode2 != inode1) - inode_lock_nested(inode2, I_MUTEX_NONDIR2); + WARN_ON_ONCE(S_ISDIR(inode1->i_mode)); + WARN_ON_ONCE(S_ISDIR(inode2->i_mode)); + lock_two_inodes(inode1, inode2, I_MUTEX_NORMAL, I_MUTEX_NONDIR2); } EXPORT_SYMBOL(lock_two_nondirectories); @@ -1173,10 +1169,14 @@ EXPORT_SYMBOL(lock_two_nondirectories); */ void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2) { - if (inode1 && !S_ISDIR(inode1->i_mode)) + if (inode1) { + WARN_ON_ONCE(S_ISDIR(inode1->i_mode)); inode_unlock(inode1); - if (inode2 && !S_ISDIR(inode2->i_mode) && inode2 != inode1) + } + if (inode2 && inode2 != inode1) { + WARN_ON_ONCE(S_ISDIR(inode2->i_mode)); inode_unlock(inode2); + } } EXPORT_SYMBOL(unlock_two_nondirectories); -- cgit v1.2.3 From 33ab231f83cc12d0157711bbf84e180c3be7d7bc Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 3 Jul 2023 16:49:12 +0200 Subject: fs: don't assume arguments are non-NULL The helper is explicitly documented as locking zero, one, or two arguments. While all current callers do pass non-NULL arguments there's no need or requirement for them to do so according to the code and the unlock_two_nondirectories() helper is pretty clear about it as well. So only call WARN_ON_ONCE() if the checked inode is valid. Fixes: 2454ad83b90a ("fs: Restrict lock_two_nondirectories() to non-directory inodes") Reviewed-by: Jan Kara Cc: Jan Kara Message-Id: <20230703-vfs-rename-source-v1-2-37eebb29b65b@kernel.org> Signed-off-by: Christian Brauner --- fs/inode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs/inode.c') diff --git a/fs/inode.c b/fs/inode.c index d37fad91c8da..8fefb69e1f84 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1156,8 +1156,10 @@ lock: */ void lock_two_nondirectories(struct inode *inode1, struct inode *inode2) { - WARN_ON_ONCE(S_ISDIR(inode1->i_mode)); - WARN_ON_ONCE(S_ISDIR(inode2->i_mode)); + if (inode1) + WARN_ON_ONCE(S_ISDIR(inode1->i_mode)); + if (inode2) + WARN_ON_ONCE(S_ISDIR(inode2->i_mode)); lock_two_inodes(inode1, inode2, I_MUTEX_NORMAL, I_MUTEX_NONDIR2); } EXPORT_SYMBOL(lock_two_nondirectories); -- cgit v1.2.3