Skip to content

Commit bf4e708

Browse files
committed
Merge tag 'pull-rename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull rename updates from Al Viro: "Fix directory locking scheme on rename This was broken in 6.5; we really can't lock two unrelated directories without holding ->s_vfs_rename_mutex first and in case of same-parent rename of a subdirectory 6.5 ends up doing just that" * tag 'pull-rename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: rename(): avoid a deadlock in the case of parents having no common ancestor kill lock_two_inodes() rename(): fix the locking of subdirectories f2fs: Avoid reading renamed directory if parent does not change ext4: don't access the source subdirectory content on same-directory rename ext2: Avoid reading renamed directory if parent does not change udf_rename(): only access the child content on cross-directory rename ocfs2: Avoid touching renamed directory if parent does not change reiserfs: Avoid touching renamed directory if parent does not change
2 parents 2f44434 + a8b0026 commit bf4e708

File tree

20 files changed

+442
-232
lines changed

20 files changed

+442
-232
lines changed

Documentation/filesystems/directory-locking.rst

Lines changed: 244 additions & 105 deletions
Large diffs are not rendered by default.

Documentation/filesystems/locking.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ symlink: exclusive
101101
mkdir: exclusive
102102
unlink: exclusive (both)
103103
rmdir: exclusive (both)(see below)
104-
rename: exclusive (all) (see below)
104+
rename: exclusive (both parents, some children) (see below)
105105
readlink: no
106106
get_link: no
107107
setattr: exclusive
@@ -123,6 +123,9 @@ get_offset_ctx no
123123
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_rwsem
124124
exclusive on victim.
125125
cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem.
126+
->unlink() and ->rename() have ->i_rwsem exclusive on all non-directories
127+
involved.
128+
->rename() has ->i_rwsem exclusive on any subdirectory that changes parent.
126129

127130
See Documentation/filesystems/directory-locking.rst for more detailed discussion
128131
of the locking scheme for directory operations.

Documentation/filesystems/porting.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,33 @@ generic_encode_ino32_fh() explicitly.
10641064

10651065
---
10661066

1067+
**mandatory**
1068+
1069+
If ->rename() update of .. on cross-directory move needs an exclusion with
1070+
directory modifications, do *not* lock the subdirectory in question in your
1071+
->rename() - it's done by the caller now [that item should've been added in
1072+
28eceeda130f "fs: Lock moved directories"].
1073+
1074+
---
1075+
1076+
**mandatory**
1077+
1078+
On same-directory ->rename() the (tautological) update of .. is not protected
1079+
by any locks; just don't do it if the old parent is the same as the new one.
1080+
We really can't lock two subdirectories in same-directory rename - not without
1081+
deadlocks.
1082+
1083+
---
1084+
1085+
**mandatory**
1086+
1087+
lock_rename() and lock_rename_child() may fail in cross-directory case, if
1088+
their arguments do not have a common ancestor. In that case ERR_PTR(-EXDEV)
1089+
is returned, with no locks taken. In-tree users updated; out-of-tree ones
1090+
would need to do so.
1091+
1092+
---
1093+
10671094
**recommended**
10681095

10691096
Block device freezing and thawing have been moved to holder operations.

fs/cachefiles/namei.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,
305305

306306
/* do the multiway lock magic */
307307
trap = lock_rename(cache->graveyard, dir);
308+
if (IS_ERR(trap))
309+
return PTR_ERR(trap);
308310

309311
/* do some checks before getting the grave dentry */
310312
if (rep->d_parent != dir || IS_DEADDIR(d_inode(rep))) {

fs/ecryptfs/inode.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,8 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
607607
target_inode = d_inode(new_dentry);
608608

609609
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
610+
if (IS_ERR(trap))
611+
return PTR_ERR(trap);
610612
dget(lower_new_dentry);
611613
rc = -EINVAL;
612614
if (lower_old_dentry->d_parent != lower_old_dir_dentry)

fs/ext2/namei.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ static int ext2_rename (struct mnt_idmap * idmap,
325325
struct ext2_dir_entry_2 * dir_de = NULL;
326326
struct folio * old_folio;
327327
struct ext2_dir_entry_2 * old_de;
328+
bool old_is_dir = S_ISDIR(old_inode->i_mode);
328329
int err;
329330

330331
if (flags & ~RENAME_NOREPLACE)
@@ -342,7 +343,7 @@ static int ext2_rename (struct mnt_idmap * idmap,
342343
if (IS_ERR(old_de))
343344
return PTR_ERR(old_de);
344345

345-
if (S_ISDIR(old_inode->i_mode)) {
346+
if (old_is_dir && old_dir != new_dir) {
346347
err = -EIO;
347348
dir_de = ext2_dotdot(old_inode, &dir_folio);
348349
if (!dir_de)
@@ -354,7 +355,7 @@ static int ext2_rename (struct mnt_idmap * idmap,
354355
struct ext2_dir_entry_2 *new_de;
355356

356357
err = -ENOTEMPTY;
357-
if (dir_de && !ext2_empty_dir (new_inode))
358+
if (old_is_dir && !ext2_empty_dir(new_inode))
358359
goto out_dir;
359360

360361
new_de = ext2_find_entry(new_dir, &new_dentry->d_name,
@@ -368,14 +369,14 @@ static int ext2_rename (struct mnt_idmap * idmap,
368369
if (err)
369370
goto out_dir;
370371
inode_set_ctime_current(new_inode);
371-
if (dir_de)
372+
if (old_is_dir)
372373
drop_nlink(new_inode);
373374
inode_dec_link_count(new_inode);
374375
} else {
375376
err = ext2_add_link(new_dentry, old_inode);
376377
if (err)
377378
goto out_dir;
378-
if (dir_de)
379+
if (old_is_dir)
379380
inode_inc_link_count(new_dir);
380381
}
381382

@@ -387,7 +388,7 @@ static int ext2_rename (struct mnt_idmap * idmap,
387388
mark_inode_dirty(old_inode);
388389

389390
err = ext2_delete_entry(old_de, old_folio);
390-
if (!err && dir_de) {
391+
if (!err && old_is_dir) {
391392
if (old_dir != new_dir)
392393
err = ext2_set_link(old_inode, dir_de, dir_folio,
393394
new_dir, false);

fs/ext4/namei.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3591,10 +3591,14 @@ struct ext4_renament {
35913591
int dir_inlined;
35923592
};
35933593

3594-
static int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent)
3594+
static int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent, bool is_cross)
35953595
{
35963596
int retval;
35973597

3598+
ent->is_dir = true;
3599+
if (!is_cross)
3600+
return 0;
3601+
35983602
ent->dir_bh = ext4_get_first_dir_block(handle, ent->inode,
35993603
&retval, &ent->parent_de,
36003604
&ent->dir_inlined);
@@ -3612,6 +3616,9 @@ static int ext4_rename_dir_finish(handle_t *handle, struct ext4_renament *ent,
36123616
{
36133617
int retval;
36143618

3619+
if (!ent->dir_bh)
3620+
return 0;
3621+
36153622
ent->parent_de->inode = cpu_to_le32(dir_ino);
36163623
BUFFER_TRACE(ent->dir_bh, "call ext4_handle_dirty_metadata");
36173624
if (!ent->dir_inlined) {
@@ -3900,7 +3907,7 @@ static int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir,
39003907
if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
39013908
goto end_rename;
39023909
}
3903-
retval = ext4_rename_dir_prepare(handle, &old);
3910+
retval = ext4_rename_dir_prepare(handle, &old, new.dir != old.dir);
39043911
if (retval)
39053912
goto end_rename;
39063913
}
@@ -3964,7 +3971,7 @@ static int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir,
39643971
}
39653972
inode_set_mtime_to_ts(old.dir, inode_set_ctime_current(old.dir));
39663973
ext4_update_dx_flag(old.dir);
3967-
if (old.dir_bh) {
3974+
if (old.is_dir) {
39683975
retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
39693976
if (retval)
39703977
goto end_rename;
@@ -3987,7 +3994,7 @@ static int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir,
39873994
if (unlikely(retval))
39883995
goto end_rename;
39893996

3990-
if (S_ISDIR(old.inode->i_mode)) {
3997+
if (old.is_dir) {
39913998
/*
39923999
* We disable fast commits here that's because the
39934000
* replay code is not yet capable of changing dot dot
@@ -4114,14 +4121,12 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
41144121
ext4_handle_sync(handle);
41154122

41164123
if (S_ISDIR(old.inode->i_mode)) {
4117-
old.is_dir = true;
4118-
retval = ext4_rename_dir_prepare(handle, &old);
4124+
retval = ext4_rename_dir_prepare(handle, &old, new.dir != old.dir);
41194125
if (retval)
41204126
goto end_rename;
41214127
}
41224128
if (S_ISDIR(new.inode->i_mode)) {
4123-
new.is_dir = true;
4124-
retval = ext4_rename_dir_prepare(handle, &new);
4129+
retval = ext4_rename_dir_prepare(handle, &new, new.dir != old.dir);
41254130
if (retval)
41264131
goto end_rename;
41274132
}

fs/f2fs/namei.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
963963
struct f2fs_dir_entry *old_dir_entry = NULL;
964964
struct f2fs_dir_entry *old_entry;
965965
struct f2fs_dir_entry *new_entry;
966+
bool old_is_dir = S_ISDIR(old_inode->i_mode);
966967
int err;
967968

968969
if (unlikely(f2fs_cp_error(sbi)))
@@ -1017,7 +1018,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
10171018
goto out;
10181019
}
10191020

1020-
if (S_ISDIR(old_inode->i_mode)) {
1021+
if (old_is_dir && old_dir != new_dir) {
10211022
old_dir_entry = f2fs_parent_dir(old_inode, &old_dir_page);
10221023
if (!old_dir_entry) {
10231024
if (IS_ERR(old_dir_page))
@@ -1029,7 +1030,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
10291030
if (new_inode) {
10301031

10311032
err = -ENOTEMPTY;
1032-
if (old_dir_entry && !f2fs_empty_dir(new_inode))
1033+
if (old_is_dir && !f2fs_empty_dir(new_inode))
10331034
goto out_dir;
10341035

10351036
err = -ENOENT;
@@ -1054,7 +1055,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
10541055

10551056
inode_set_ctime_current(new_inode);
10561057
f2fs_down_write(&F2FS_I(new_inode)->i_sem);
1057-
if (old_dir_entry)
1058+
if (old_is_dir)
10581059
f2fs_i_links_write(new_inode, false);
10591060
f2fs_i_links_write(new_inode, false);
10601061
f2fs_up_write(&F2FS_I(new_inode)->i_sem);
@@ -1074,12 +1075,12 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
10741075
goto out_dir;
10751076
}
10761077

1077-
if (old_dir_entry)
1078+
if (old_is_dir)
10781079
f2fs_i_links_write(new_dir, true);
10791080
}
10801081

10811082
f2fs_down_write(&F2FS_I(old_inode)->i_sem);
1082-
if (!old_dir_entry || whiteout)
1083+
if (!old_is_dir || whiteout)
10831084
file_lost_pino(old_inode);
10841085
else
10851086
/* adjust dir's i_pino to pass fsck check */
@@ -1105,8 +1106,8 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
11051106
iput(whiteout);
11061107
}
11071108

1108-
if (old_dir_entry) {
1109-
if (old_dir != new_dir && !whiteout)
1109+
if (old_is_dir) {
1110+
if (old_dir_entry && !whiteout)
11101111
f2fs_set_link(old_inode, old_dir_entry,
11111112
old_dir_page, new_dir);
11121113
else

fs/inode.c

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,48 +1088,6 @@ void discard_new_inode(struct inode *inode)
10881088
}
10891089
EXPORT_SYMBOL(discard_new_inode);
10901090

1091-
/**
1092-
* lock_two_inodes - lock two inodes (may be regular files but also dirs)
1093-
*
1094-
* Lock any non-NULL argument. The caller must make sure that if he is passing
1095-
* in two directories, one is not ancestor of the other. Zero, one or two
1096-
* objects may be locked by this function.
1097-
*
1098-
* @inode1: first inode to lock
1099-
* @inode2: second inode to lock
1100-
* @subclass1: inode lock subclass for the first lock obtained
1101-
* @subclass2: inode lock subclass for the second lock obtained
1102-
*/
1103-
void lock_two_inodes(struct inode *inode1, struct inode *inode2,
1104-
unsigned subclass1, unsigned subclass2)
1105-
{
1106-
if (!inode1 || !inode2) {
1107-
/*
1108-
* Make sure @subclass1 will be used for the acquired lock.
1109-
* This is not strictly necessary (no current caller cares) but
1110-
* let's keep things consistent.
1111-
*/
1112-
if (!inode1)
1113-
swap(inode1, inode2);
1114-
goto lock;
1115-
}
1116-
1117-
/*
1118-
* If one object is directory and the other is not, we must make sure
1119-
* to lock directory first as the other object may be its child.
1120-
*/
1121-
if (S_ISDIR(inode2->i_mode) == S_ISDIR(inode1->i_mode)) {
1122-
if (inode1 > inode2)
1123-
swap(inode1, inode2);
1124-
} else if (!S_ISDIR(inode1->i_mode))
1125-
swap(inode1, inode2);
1126-
lock:
1127-
if (inode1)
1128-
inode_lock_nested(inode1, subclass1);
1129-
if (inode2 && inode2 != inode1)
1130-
inode_lock_nested(inode2, subclass2);
1131-
}
1132-
11331091
/**
11341092
* lock_two_nondirectories - take two i_mutexes on non-directory objects
11351093
*
@@ -1145,7 +1103,12 @@ void lock_two_nondirectories(struct inode *inode1, struct inode *inode2)
11451103
WARN_ON_ONCE(S_ISDIR(inode1->i_mode));
11461104
if (inode2)
11471105
WARN_ON_ONCE(S_ISDIR(inode2->i_mode));
1148-
lock_two_inodes(inode1, inode2, I_MUTEX_NORMAL, I_MUTEX_NONDIR2);
1106+
if (inode1 > inode2)
1107+
swap(inode1, inode2);
1108+
if (inode1)
1109+
inode_lock(inode1);
1110+
if (inode2 && inode2 != inode1)
1111+
inode_lock_nested(inode2, I_MUTEX_NONDIR2);
11491112
}
11501113
EXPORT_SYMBOL(lock_two_nondirectories);
11511114

fs/internal.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,6 @@ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc);
197197
int dentry_needs_remove_privs(struct mnt_idmap *, struct dentry *dentry);
198198
bool in_group_or_capable(struct mnt_idmap *idmap,
199199
const struct inode *inode, vfsgid_t vfsgid);
200-
void lock_two_inodes(struct inode *inode1, struct inode *inode2,
201-
unsigned subclass1, unsigned subclass2);
202200

203201
/*
204202
* fs-writeback.c

0 commit comments

Comments
 (0)