2018-09-04 15:46:30 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2009-04-06 19:01:34 -07:00
|
|
|
/*
|
2021-11-08 18:35:01 -08:00
|
|
|
* NILFS pathname lookup operations.
|
2009-04-06 19:01:34 -07:00
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
|
|
|
|
*
|
2016-05-23 16:23:09 -07:00
|
|
|
* Modified for NILFS by Amagai Yoshiji and Ryusuke Konishi.
|
2009-04-06 19:01:34 -07:00
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* linux/fs/ext2/namei.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1992, 1993, 1994, 1995
|
|
|
|
* Remy Card (card@masi.ibp.fr)
|
|
|
|
* Laboratoire MASI - Institut Blaise Pascal
|
|
|
|
* Universite Pierre et Marie Curie (Paris VI)
|
|
|
|
*
|
|
|
|
* from
|
|
|
|
*
|
|
|
|
* linux/fs/minix/namei.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
|
|
*
|
|
|
|
* Big-endian to little-endian byte-swapping/bitmaps by
|
|
|
|
* David S. Miller (davem@caip.rutgers.edu), 1995
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include "nilfs.h"
|
2010-08-27 00:23:02 +09:00
|
|
|
#include "export.h"
|
2009-04-06 19:01:34 -07:00
|
|
|
|
2010-08-27 00:23:02 +09:00
|
|
|
#define NILFS_FID_SIZE_NON_CONNECTABLE \
|
|
|
|
(offsetof(struct nilfs_fid, parent_gen) / 4)
|
|
|
|
#define NILFS_FID_SIZE_CONNECTABLE (sizeof(struct nilfs_fid) / 4)
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
static inline int nilfs_add_nondir(struct dentry *dentry, struct inode *inode)
|
|
|
|
{
|
|
|
|
int err = nilfs_add_link(dentry, inode);
|
2016-05-23 16:23:25 -07:00
|
|
|
|
2009-04-06 19:01:34 -07:00
|
|
|
if (!err) {
|
2018-05-04 08:23:01 -04:00
|
|
|
d_instantiate_new(dentry, inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
inode_dec_link_count(inode);
|
2014-12-10 15:54:34 -08:00
|
|
|
unlock_new_inode(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
iput(inode);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Methods themselves.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct dentry *
|
2012-06-10 17:13:09 -04:00
|
|
|
nilfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
2009-04-06 19:01:34 -07:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
ino_t ino;
|
2024-10-04 12:35:31 +09:00
|
|
|
int res;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
if (dentry->d_name.len > NILFS_NAME_LEN)
|
|
|
|
return ERR_PTR(-ENAMETOOLONG);
|
|
|
|
|
2024-10-04 12:35:31 +09:00
|
|
|
res = nilfs_inode_by_name(dir, &dentry->d_name, &ino);
|
|
|
|
if (res) {
|
|
|
|
if (res != -ENOENT)
|
|
|
|
return ERR_PTR(res);
|
|
|
|
inode = NULL;
|
|
|
|
} else {
|
|
|
|
inode = nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino);
|
2024-12-09 15:56:52 +09:00
|
|
|
if (inode == ERR_PTR(-ESTALE)) {
|
|
|
|
nilfs_error(dir->i_sb,
|
|
|
|
"deleted inode referenced: %lu", ino);
|
|
|
|
return ERR_PTR(-EIO);
|
|
|
|
}
|
2024-10-04 12:35:31 +09:00
|
|
|
}
|
|
|
|
|
2009-04-06 19:01:34 -07:00
|
|
|
return d_splice_alias(inode, dentry);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* By the time this is called, we already have created
|
|
|
|
* the directory cache entry for the new file, but it
|
|
|
|
* is so far negative - it has no inode.
|
|
|
|
*
|
|
|
|
* If the create succeeds, we fill in the inode information
|
|
|
|
* with d_instantiate().
|
|
|
|
*/
|
2023-01-13 12:49:13 +01:00
|
|
|
static int nilfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
2021-01-21 14:19:43 +01:00
|
|
|
struct dentry *dentry, umode_t mode, bool excl)
|
2009-04-06 19:01:34 -07:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct nilfs_transaction_info ti;
|
2009-04-06 19:01:45 -07:00
|
|
|
int err;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
err = nilfs_transaction_begin(dir->i_sb, &ti, 1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
inode = nilfs_new_inode(dir, mode);
|
|
|
|
err = PTR_ERR(inode);
|
|
|
|
if (!IS_ERR(inode)) {
|
|
|
|
inode->i_op = &nilfs_file_inode_operations;
|
|
|
|
inode->i_fop = &nilfs_file_operations;
|
|
|
|
inode->i_mapping->a_ops = &nilfs_aops;
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
err = nilfs_add_nondir(dentry, inode);
|
|
|
|
}
|
2009-04-06 19:01:45 -07:00
|
|
|
if (!err)
|
|
|
|
err = nilfs_transaction_commit(dir->i_sb);
|
|
|
|
else
|
|
|
|
nilfs_transaction_abort(dir->i_sb);
|
|
|
|
|
|
|
|
return err;
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2023-01-13 12:49:16 +01:00
|
|
|
nilfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
2021-01-21 14:19:43 +01:00
|
|
|
struct dentry *dentry, umode_t mode, dev_t rdev)
|
2009-04-06 19:01:34 -07:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct nilfs_transaction_info ti;
|
2009-04-06 19:01:45 -07:00
|
|
|
int err;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
err = nilfs_transaction_begin(dir->i_sb, &ti, 1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
inode = nilfs_new_inode(dir, mode);
|
|
|
|
err = PTR_ERR(inode);
|
|
|
|
if (!IS_ERR(inode)) {
|
|
|
|
init_special_inode(inode, inode->i_mode, rdev);
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
err = nilfs_add_nondir(dentry, inode);
|
|
|
|
}
|
2009-04-06 19:01:45 -07:00
|
|
|
if (!err)
|
|
|
|
err = nilfs_transaction_commit(dir->i_sb);
|
|
|
|
else
|
|
|
|
nilfs_transaction_abort(dir->i_sb);
|
|
|
|
|
|
|
|
return err;
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
|
|
|
|
2023-01-13 12:49:14 +01:00
|
|
|
static int nilfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
2021-01-21 14:19:43 +01:00
|
|
|
struct dentry *dentry, const char *symname)
|
2009-04-06 19:01:34 -07:00
|
|
|
{
|
|
|
|
struct nilfs_transaction_info ti;
|
|
|
|
struct super_block *sb = dir->i_sb;
|
2016-05-23 16:23:39 -07:00
|
|
|
unsigned int l = strlen(symname) + 1;
|
2009-04-06 19:01:34 -07:00
|
|
|
struct inode *inode;
|
2009-04-06 19:01:45 -07:00
|
|
|
int err;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
if (l > sb->s_blocksize)
|
|
|
|
return -ENAMETOOLONG;
|
|
|
|
|
|
|
|
err = nilfs_transaction_begin(dir->i_sb, &ti, 1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-11-17 15:29:46 -08:00
|
|
|
inode = nilfs_new_inode(dir, S_IFLNK | 0777);
|
2009-04-06 19:01:34 -07:00
|
|
|
err = PTR_ERR(inode);
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* slow symlink */
|
|
|
|
inode->i_op = &nilfs_symlink_inode_operations;
|
2015-11-17 01:07:57 -05:00
|
|
|
inode_nohighmem(inode);
|
nilfs2: fix potential deadlock with newly created symlinks
Syzbot reported that page_symlink(), called by nilfs_symlink(), triggers
memory reclamation involving the filesystem layer, which can result in
circular lock dependencies among the reader/writer semaphore
nilfs->ns_segctor_sem, s_writers percpu_rwsem (intwrite) and the
fs_reclaim pseudo lock.
This is because after commit 21fc61c73c39 ("don't put symlink bodies in
pagecache into highmem"), the gfp flags of the page cache for symbolic
links are overwritten to GFP_KERNEL via inode_nohighmem().
This is not a problem for symlinks read from the backing device, because
the __GFP_FS flag is dropped after inode_nohighmem() is called. However,
when a new symlink is created with nilfs_symlink(), the gfp flags remain
overwritten to GFP_KERNEL. Then, memory allocation called from
page_symlink() etc. triggers memory reclamation including the FS layer,
which may call nilfs_evict_inode() or nilfs_dirty_inode(). And these can
cause a deadlock if they are called while nilfs->ns_segctor_sem is held:
Fix this issue by dropping the __GFP_FS flag from the page cache GFP flags
of newly created symlinks in the same way that nilfs_new_inode() and
__nilfs_read_inode() do, as a workaround until we adopt nofs allocation
scope consistently or improve the locking constraints.
Link: https://lkml.kernel.org/r/20241020050003.4308-1-konishi.ryusuke@gmail.com
Fixes: 21fc61c73c39 ("don't put symlink bodies in pagecache into highmem")
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+9ef37ac20608f4836256@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9ef37ac20608f4836256
Tested-by: syzbot+9ef37ac20608f4836256@syzkaller.appspotmail.com
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2024-10-20 13:51:28 +09:00
|
|
|
mapping_set_gfp_mask(inode->i_mapping,
|
|
|
|
mapping_gfp_constraint(inode->i_mapping,
|
|
|
|
~__GFP_FS));
|
2009-04-06 19:01:34 -07:00
|
|
|
inode->i_mapping->a_ops = &nilfs_aops;
|
|
|
|
err = page_symlink(inode, symname, l);
|
|
|
|
if (err)
|
|
|
|
goto out_fail;
|
|
|
|
|
|
|
|
/* mark_inode_dirty(inode); */
|
2009-11-27 19:41:06 +09:00
|
|
|
/* page_symlink() do this */
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
err = nilfs_add_nondir(dentry, inode);
|
|
|
|
out:
|
2009-04-06 19:01:45 -07:00
|
|
|
if (!err)
|
|
|
|
err = nilfs_transaction_commit(dir->i_sb);
|
|
|
|
else
|
|
|
|
nilfs_transaction_abort(dir->i_sb);
|
|
|
|
|
|
|
|
return err;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
out_fail:
|
2009-11-27 19:41:08 +09:00
|
|
|
drop_nlink(inode);
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(inode);
|
2014-12-10 15:54:34 -08:00
|
|
|
unlock_new_inode(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
iput(inode);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nilfs_link(struct dentry *old_dentry, struct inode *dir,
|
|
|
|
struct dentry *dentry)
|
|
|
|
{
|
2015-03-17 22:25:59 +00:00
|
|
|
struct inode *inode = d_inode(old_dentry);
|
2009-04-06 19:01:34 -07:00
|
|
|
struct nilfs_transaction_info ti;
|
2009-04-06 19:01:45 -07:00
|
|
|
int err;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
err = nilfs_transaction_begin(dir->i_sb, &ti, 1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2023-07-05 15:01:24 -04:00
|
|
|
inode_set_ctime_current(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
inode_inc_link_count(inode);
|
2010-10-23 11:11:40 -04:00
|
|
|
ihold(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
|
2014-12-10 15:54:34 -08:00
|
|
|
err = nilfs_add_link(dentry, inode);
|
|
|
|
if (!err) {
|
|
|
|
d_instantiate(dentry, inode);
|
2009-04-06 19:01:45 -07:00
|
|
|
err = nilfs_transaction_commit(dir->i_sb);
|
2014-12-10 15:54:34 -08:00
|
|
|
} else {
|
|
|
|
inode_dec_link_count(inode);
|
|
|
|
iput(inode);
|
2009-04-06 19:01:45 -07:00
|
|
|
nilfs_transaction_abort(dir->i_sb);
|
2014-12-10 15:54:34 -08:00
|
|
|
}
|
2009-04-06 19:01:45 -07:00
|
|
|
|
|
|
|
return err;
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
|
|
|
|
Change inode_operations.mkdir to return struct dentry *
Some filesystems, such as NFS, cifs, ceph, and fuse, do not have
complete control of sequencing on the actual filesystem (e.g. on a
different server) and may find that the inode created for a mkdir
request already exists in the icache and dcache by the time the mkdir
request returns. For example, if the filesystem is mounted twice the
directory could be visible on the other mount before it is on the
original mount, and a pair of name_to_handle_at(), open_by_handle_at()
calls could instantiate the directory inode with an IS_ROOT() dentry
before the first mkdir returns.
This means that the dentry passed to ->mkdir() may not be the one that
is associated with the inode after the ->mkdir() completes. Some
callers need to interact with the inode after the ->mkdir completes and
they currently need to perform a lookup in the (rare) case that the
dentry is no longer hashed.
This lookup-after-mkdir requires that the directory remains locked to
avoid races. Planned future patches to lock the dentry rather than the
directory will mean that this lookup cannot be performed atomically with
the mkdir.
To remove this barrier, this patch changes ->mkdir to return the
resulting dentry if it is different from the one passed in.
Possible returns are:
NULL - the directory was created and no other dentry was used
ERR_PTR() - an error occurred
non-NULL - this other dentry was spliced in
This patch only changes file-systems to return "ERR_PTR(err)" instead of
"err" or equivalent transformations. Subsequent patches will make
further changes to some file-systems to return a correct dentry.
Not all filesystems reliably result in a positive hashed dentry:
- NFS, cifs, hostfs will sometimes need to perform a lookup of
the name to get inode information. Races could result in this
returning something different. Note that this lookup is
non-atomic which is what we are trying to avoid. Placing the
lookup in filesystem code means it only happens when the filesystem
has no other option.
- kernfs and tracefs leave the dentry negative and the ->revalidate
operation ensures that lookup will be called to correctly populate
the dentry. This could be fixed but I don't think it is important
to any of the users of vfs_mkdir() which look at the dentry.
The recommendation to use
d_drop();d_splice_alias()
is ugly but fits with current practice. A planned future patch will
change this.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: NeilBrown <neilb@suse.de>
Link: https://lore.kernel.org/r/20250227013949.536172-2-neilb@suse.de
Signed-off-by: Christian Brauner <brauner@kernel.org>
2025-02-27 12:32:53 +11:00
|
|
|
static struct dentry *nilfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
|
|
|
struct dentry *dentry, umode_t mode)
|
2009-04-06 19:01:34 -07:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct nilfs_transaction_info ti;
|
2009-04-06 19:01:45 -07:00
|
|
|
int err;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
err = nilfs_transaction_begin(dir->i_sb, &ti, 1);
|
|
|
|
if (err)
|
Change inode_operations.mkdir to return struct dentry *
Some filesystems, such as NFS, cifs, ceph, and fuse, do not have
complete control of sequencing on the actual filesystem (e.g. on a
different server) and may find that the inode created for a mkdir
request already exists in the icache and dcache by the time the mkdir
request returns. For example, if the filesystem is mounted twice the
directory could be visible on the other mount before it is on the
original mount, and a pair of name_to_handle_at(), open_by_handle_at()
calls could instantiate the directory inode with an IS_ROOT() dentry
before the first mkdir returns.
This means that the dentry passed to ->mkdir() may not be the one that
is associated with the inode after the ->mkdir() completes. Some
callers need to interact with the inode after the ->mkdir completes and
they currently need to perform a lookup in the (rare) case that the
dentry is no longer hashed.
This lookup-after-mkdir requires that the directory remains locked to
avoid races. Planned future patches to lock the dentry rather than the
directory will mean that this lookup cannot be performed atomically with
the mkdir.
To remove this barrier, this patch changes ->mkdir to return the
resulting dentry if it is different from the one passed in.
Possible returns are:
NULL - the directory was created and no other dentry was used
ERR_PTR() - an error occurred
non-NULL - this other dentry was spliced in
This patch only changes file-systems to return "ERR_PTR(err)" instead of
"err" or equivalent transformations. Subsequent patches will make
further changes to some file-systems to return a correct dentry.
Not all filesystems reliably result in a positive hashed dentry:
- NFS, cifs, hostfs will sometimes need to perform a lookup of
the name to get inode information. Races could result in this
returning something different. Note that this lookup is
non-atomic which is what we are trying to avoid. Placing the
lookup in filesystem code means it only happens when the filesystem
has no other option.
- kernfs and tracefs leave the dentry negative and the ->revalidate
operation ensures that lookup will be called to correctly populate
the dentry. This could be fixed but I don't think it is important
to any of the users of vfs_mkdir() which look at the dentry.
The recommendation to use
d_drop();d_splice_alias()
is ugly but fits with current practice. A planned future patch will
change this.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: NeilBrown <neilb@suse.de>
Link: https://lore.kernel.org/r/20250227013949.536172-2-neilb@suse.de
Signed-off-by: Christian Brauner <brauner@kernel.org>
2025-02-27 12:32:53 +11:00
|
|
|
return ERR_PTR(err);
|
2009-04-06 19:01:34 -07:00
|
|
|
|
2009-11-27 19:41:08 +09:00
|
|
|
inc_nlink(dir);
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
inode = nilfs_new_inode(dir, S_IFDIR | mode);
|
|
|
|
err = PTR_ERR(inode);
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
goto out_dir;
|
|
|
|
|
|
|
|
inode->i_op = &nilfs_dir_inode_operations;
|
|
|
|
inode->i_fop = &nilfs_dir_operations;
|
|
|
|
inode->i_mapping->a_ops = &nilfs_aops;
|
|
|
|
|
2009-11-27 19:41:08 +09:00
|
|
|
inc_nlink(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
err = nilfs_make_empty(inode, dir);
|
|
|
|
if (err)
|
|
|
|
goto out_fail;
|
|
|
|
|
|
|
|
err = nilfs_add_link(dentry, inode);
|
|
|
|
if (err)
|
|
|
|
goto out_fail;
|
|
|
|
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(inode);
|
2018-05-04 08:23:01 -04:00
|
|
|
d_instantiate_new(dentry, inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
out:
|
2009-04-06 19:01:45 -07:00
|
|
|
if (!err)
|
|
|
|
err = nilfs_transaction_commit(dir->i_sb);
|
|
|
|
else
|
|
|
|
nilfs_transaction_abort(dir->i_sb);
|
|
|
|
|
Change inode_operations.mkdir to return struct dentry *
Some filesystems, such as NFS, cifs, ceph, and fuse, do not have
complete control of sequencing on the actual filesystem (e.g. on a
different server) and may find that the inode created for a mkdir
request already exists in the icache and dcache by the time the mkdir
request returns. For example, if the filesystem is mounted twice the
directory could be visible on the other mount before it is on the
original mount, and a pair of name_to_handle_at(), open_by_handle_at()
calls could instantiate the directory inode with an IS_ROOT() dentry
before the first mkdir returns.
This means that the dentry passed to ->mkdir() may not be the one that
is associated with the inode after the ->mkdir() completes. Some
callers need to interact with the inode after the ->mkdir completes and
they currently need to perform a lookup in the (rare) case that the
dentry is no longer hashed.
This lookup-after-mkdir requires that the directory remains locked to
avoid races. Planned future patches to lock the dentry rather than the
directory will mean that this lookup cannot be performed atomically with
the mkdir.
To remove this barrier, this patch changes ->mkdir to return the
resulting dentry if it is different from the one passed in.
Possible returns are:
NULL - the directory was created and no other dentry was used
ERR_PTR() - an error occurred
non-NULL - this other dentry was spliced in
This patch only changes file-systems to return "ERR_PTR(err)" instead of
"err" or equivalent transformations. Subsequent patches will make
further changes to some file-systems to return a correct dentry.
Not all filesystems reliably result in a positive hashed dentry:
- NFS, cifs, hostfs will sometimes need to perform a lookup of
the name to get inode information. Races could result in this
returning something different. Note that this lookup is
non-atomic which is what we are trying to avoid. Placing the
lookup in filesystem code means it only happens when the filesystem
has no other option.
- kernfs and tracefs leave the dentry negative and the ->revalidate
operation ensures that lookup will be called to correctly populate
the dentry. This could be fixed but I don't think it is important
to any of the users of vfs_mkdir() which look at the dentry.
The recommendation to use
d_drop();d_splice_alias()
is ugly but fits with current practice. A planned future patch will
change this.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: NeilBrown <neilb@suse.de>
Link: https://lore.kernel.org/r/20250227013949.536172-2-neilb@suse.de
Signed-off-by: Christian Brauner <brauner@kernel.org>
2025-02-27 12:32:53 +11:00
|
|
|
return ERR_PTR(err);
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
out_fail:
|
2009-11-27 19:41:08 +09:00
|
|
|
drop_nlink(inode);
|
|
|
|
drop_nlink(inode);
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(inode);
|
2014-12-10 15:54:34 -08:00
|
|
|
unlock_new_inode(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
iput(inode);
|
|
|
|
out_dir:
|
2009-11-27 19:41:08 +09:00
|
|
|
drop_nlink(dir);
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(dir);
|
2009-04-06 19:01:34 -07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-11-27 19:41:10 +09:00
|
|
|
static int nilfs_do_unlink(struct inode *dir, struct dentry *dentry)
|
2009-04-06 19:01:34 -07:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct nilfs_dir_entry *de;
|
2023-11-27 23:30:31 +09:00
|
|
|
struct folio *folio;
|
2009-04-06 19:01:45 -07:00
|
|
|
int err;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
2023-11-27 23:30:31 +09:00
|
|
|
de = nilfs_find_entry(dir, &dentry->d_name, &folio);
|
2024-10-04 12:35:31 +09:00
|
|
|
if (IS_ERR(de)) {
|
|
|
|
err = PTR_ERR(de);
|
2009-04-06 19:01:34 -07:00
|
|
|
goto out;
|
2024-10-04 12:35:31 +09:00
|
|
|
}
|
2009-04-06 19:01:34 -07:00
|
|
|
|
2015-03-17 22:25:59 +00:00
|
|
|
inode = d_inode(dentry);
|
2009-04-06 19:01:34 -07:00
|
|
|
err = -EIO;
|
|
|
|
if (le64_to_cpu(de->inode) != inode->i_ino)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!inode->i_nlink) {
|
2020-08-11 18:35:49 -07:00
|
|
|
nilfs_warn(inode->i_sb,
|
|
|
|
"deleting nonexistent file (ino=%lu), %d",
|
|
|
|
inode->i_ino, inode->i_nlink);
|
2011-10-28 14:13:29 +02:00
|
|
|
set_nlink(inode, 1);
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
2023-11-27 23:30:31 +09:00
|
|
|
err = nilfs_delete_entry(de, folio);
|
|
|
|
folio_release_kmap(folio, de);
|
2009-04-06 19:01:34 -07:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2023-07-05 15:01:24 -04:00
|
|
|
inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
|
2009-11-27 19:41:08 +09:00
|
|
|
drop_nlink(inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
err = 0;
|
|
|
|
out:
|
2009-11-27 19:41:10 +09:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nilfs_unlink(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct nilfs_transaction_info ti;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = nilfs_transaction_begin(dir->i_sb, &ti, 0);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = nilfs_do_unlink(dir, dentry);
|
|
|
|
|
|
|
|
if (!err) {
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(dir);
|
2015-03-17 22:25:59 +00:00
|
|
|
nilfs_mark_inode_dirty(d_inode(dentry));
|
2009-04-06 19:01:45 -07:00
|
|
|
err = nilfs_transaction_commit(dir->i_sb);
|
2009-11-27 19:41:10 +09:00
|
|
|
} else
|
2009-04-06 19:01:45 -07:00
|
|
|
nilfs_transaction_abort(dir->i_sb);
|
|
|
|
|
|
|
|
return err;
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nilfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
2015-03-17 22:25:59 +00:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2009-04-06 19:01:34 -07:00
|
|
|
struct nilfs_transaction_info ti;
|
2009-04-06 19:01:45 -07:00
|
|
|
int err;
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
err = nilfs_transaction_begin(dir->i_sb, &ti, 0);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = -ENOTEMPTY;
|
|
|
|
if (nilfs_empty_dir(inode)) {
|
2009-11-27 19:41:10 +09:00
|
|
|
err = nilfs_do_unlink(dir, dentry);
|
2009-04-06 19:01:34 -07:00
|
|
|
if (!err) {
|
|
|
|
inode->i_size = 0;
|
2009-11-27 19:41:08 +09:00
|
|
|
drop_nlink(inode);
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(inode);
|
2009-11-27 19:41:08 +09:00
|
|
|
drop_nlink(dir);
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(dir);
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
|
|
|
}
|
2009-04-06 19:01:45 -07:00
|
|
|
if (!err)
|
|
|
|
err = nilfs_transaction_commit(dir->i_sb);
|
|
|
|
else
|
|
|
|
nilfs_transaction_abort(dir->i_sb);
|
|
|
|
|
|
|
|
return err;
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
|
|
|
|
2023-01-13 12:49:17 +01:00
|
|
|
static int nilfs_rename(struct mnt_idmap *idmap,
|
2021-01-21 14:19:43 +01:00
|
|
|
struct inode *old_dir, struct dentry *old_dentry,
|
|
|
|
struct inode *new_dir, struct dentry *new_dentry,
|
fs: support RENAME_NOREPLACE for local filesystems
This is trivial to do:
- add flags argument to foo_rename()
- check if flags doesn't have any other than RENAME_NOREPLACE
- assign foo_rename() to .rename2 instead of .rename
Filesystems converted:
affs, bfs, exofs, ext2, hfs, hfsplus, jffs2, jfs, logfs, minix, msdos,
nilfs2, omfs, reiserfs, sysvfs, ubifs, udf, ufs, vfat.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Acked-by: Boaz Harrosh <ooo@electrozaur.com>
Acked-by: Richard Weinberger <richard@nod.at>
Acked-by: Bob Copeland <me@bobcopeland.com>
Acked-by: Jan Kara <jack@suse.cz>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Jaegeuk Kim <jaegeuk@kernel.org>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Mikulas Patocka <mpatocka@redhat.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Dave Kleikamp <shaggy@kernel.org>
Cc: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Cc: Christoph Hellwig <hch@infradead.org>
2016-09-27 11:03:57 +02:00
|
|
|
unsigned int flags)
|
2009-04-06 19:01:34 -07:00
|
|
|
{
|
2015-03-17 22:25:59 +00:00
|
|
|
struct inode *old_inode = d_inode(old_dentry);
|
|
|
|
struct inode *new_inode = d_inode(new_dentry);
|
2023-11-27 23:30:31 +09:00
|
|
|
struct folio *dir_folio = NULL;
|
2009-04-06 19:01:34 -07:00
|
|
|
struct nilfs_dir_entry *dir_de = NULL;
|
2023-11-27 23:30:31 +09:00
|
|
|
struct folio *old_folio;
|
2009-04-06 19:01:34 -07:00
|
|
|
struct nilfs_dir_entry *old_de;
|
|
|
|
struct nilfs_transaction_info ti;
|
nilfs2: do not update mtime of renamed directory that is not moved
A minor issue with nilfs_rename, originating from an old ext2
implementation, is that the mtime is updated even if the rename target is
a directory and it is renamed within the same directory, rather than moved
to a different directory.
In this case, the child directory being renamed does not change in any
way, so changing its mtime is unnecessary according to the specification,
and can unnecessarily confuse backup tools.
In ext2, this issue was fixed by commit 39fe7557b4d6 ("ext2: Do not update
mtime of a moved directory") and a few subsequent fixes, but it remained
in nilfs2.
Fix this issue by not calling nilfs_set_link(), which rewrites the inode
number of the directory entry that refers to the parent directory, when
the move target is a directory and the source and destination are the same
directory.
Here, the directory to be moved only needs to be read if the inode number
of the parent directory is rewritten with nilfs_set_link, so also adjust
the execution conditions of the preparation work to avoid unnecessary
directory reads.
Link: https://lkml.kernel.org/r/20250111143518.7901-3-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:36 +09:00
|
|
|
bool old_is_dir = S_ISDIR(old_inode->i_mode);
|
2009-04-06 19:01:34 -07:00
|
|
|
int err;
|
|
|
|
|
fs: support RENAME_NOREPLACE for local filesystems
This is trivial to do:
- add flags argument to foo_rename()
- check if flags doesn't have any other than RENAME_NOREPLACE
- assign foo_rename() to .rename2 instead of .rename
Filesystems converted:
affs, bfs, exofs, ext2, hfs, hfsplus, jffs2, jfs, logfs, minix, msdos,
nilfs2, omfs, reiserfs, sysvfs, ubifs, udf, ufs, vfat.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Acked-by: Boaz Harrosh <ooo@electrozaur.com>
Acked-by: Richard Weinberger <richard@nod.at>
Acked-by: Bob Copeland <me@bobcopeland.com>
Acked-by: Jan Kara <jack@suse.cz>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Jaegeuk Kim <jaegeuk@kernel.org>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Mikulas Patocka <mpatocka@redhat.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Dave Kleikamp <shaggy@kernel.org>
Cc: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Cc: Christoph Hellwig <hch@infradead.org>
2016-09-27 11:03:57 +02:00
|
|
|
if (flags & ~RENAME_NOREPLACE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-04-06 19:01:34 -07:00
|
|
|
err = nilfs_transaction_begin(old_dir->i_sb, &ti, 1);
|
|
|
|
if (unlikely(err))
|
|
|
|
return err;
|
|
|
|
|
2023-11-27 23:30:31 +09:00
|
|
|
old_de = nilfs_find_entry(old_dir, &old_dentry->d_name, &old_folio);
|
2024-10-04 12:35:31 +09:00
|
|
|
if (IS_ERR(old_de)) {
|
|
|
|
err = PTR_ERR(old_de);
|
2009-04-06 19:01:34 -07:00
|
|
|
goto out;
|
2024-10-04 12:35:31 +09:00
|
|
|
}
|
2009-04-06 19:01:34 -07:00
|
|
|
|
nilfs2: do not update mtime of renamed directory that is not moved
A minor issue with nilfs_rename, originating from an old ext2
implementation, is that the mtime is updated even if the rename target is
a directory and it is renamed within the same directory, rather than moved
to a different directory.
In this case, the child directory being renamed does not change in any
way, so changing its mtime is unnecessary according to the specification,
and can unnecessarily confuse backup tools.
In ext2, this issue was fixed by commit 39fe7557b4d6 ("ext2: Do not update
mtime of a moved directory") and a few subsequent fixes, but it remained
in nilfs2.
Fix this issue by not calling nilfs_set_link(), which rewrites the inode
number of the directory entry that refers to the parent directory, when
the move target is a directory and the source and destination are the same
directory.
Here, the directory to be moved only needs to be read if the inode number
of the parent directory is rewritten with nilfs_set_link, so also adjust
the execution conditions of the preparation work to avoid unnecessary
directory reads.
Link: https://lkml.kernel.org/r/20250111143518.7901-3-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:36 +09:00
|
|
|
if (old_is_dir && old_dir != new_dir) {
|
2009-04-06 19:01:34 -07:00
|
|
|
err = -EIO;
|
2023-11-27 23:30:31 +09:00
|
|
|
dir_de = nilfs_dotdot(old_inode, &dir_folio);
|
2009-04-06 19:01:34 -07:00
|
|
|
if (!dir_de)
|
|
|
|
goto out_old;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_inode) {
|
2023-11-27 23:30:31 +09:00
|
|
|
struct folio *new_folio;
|
2009-04-06 19:01:34 -07:00
|
|
|
struct nilfs_dir_entry *new_de;
|
|
|
|
|
|
|
|
err = -ENOTEMPTY;
|
nilfs2: do not update mtime of renamed directory that is not moved
A minor issue with nilfs_rename, originating from an old ext2
implementation, is that the mtime is updated even if the rename target is
a directory and it is renamed within the same directory, rather than moved
to a different directory.
In this case, the child directory being renamed does not change in any
way, so changing its mtime is unnecessary according to the specification,
and can unnecessarily confuse backup tools.
In ext2, this issue was fixed by commit 39fe7557b4d6 ("ext2: Do not update
mtime of a moved directory") and a few subsequent fixes, but it remained
in nilfs2.
Fix this issue by not calling nilfs_set_link(), which rewrites the inode
number of the directory entry that refers to the parent directory, when
the move target is a directory and the source and destination are the same
directory.
Here, the directory to be moved only needs to be read if the inode number
of the parent directory is rewritten with nilfs_set_link, so also adjust
the execution conditions of the preparation work to avoid unnecessary
directory reads.
Link: https://lkml.kernel.org/r/20250111143518.7901-3-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:36 +09:00
|
|
|
if (old_is_dir && !nilfs_empty_dir(new_inode))
|
2009-04-06 19:01:34 -07:00
|
|
|
goto out_dir;
|
|
|
|
|
2024-10-04 12:35:31 +09:00
|
|
|
new_de = nilfs_find_entry(new_dir, &new_dentry->d_name,
|
|
|
|
&new_folio);
|
|
|
|
if (IS_ERR(new_de)) {
|
|
|
|
err = PTR_ERR(new_de);
|
2009-04-06 19:01:34 -07:00
|
|
|
goto out_dir;
|
2024-10-04 12:35:31 +09:00
|
|
|
}
|
nilfs2: handle errors that nilfs_prepare_chunk() may return
Patch series "nilfs2: fix issues with rename operations".
This series fixes BUG_ON check failures reported by syzbot around rename
operations, and a minor behavioral issue where the mtime of a child
directory changes when it is renamed instead of moved.
This patch (of 2):
The directory manipulation routines nilfs_set_link() and
nilfs_delete_entry() rewrite the directory entry in the folio/page
previously read by nilfs_find_entry(), so error handling is omitted on the
assumption that nilfs_prepare_chunk(), which prepares the buffer for
rewriting, will always succeed for these. And if an error is returned, it
triggers the legacy BUG_ON() checks in each routine.
This assumption is wrong, as proven by syzbot: the buffer layer called by
nilfs_prepare_chunk() may call nilfs_get_block() if necessary, which may
fail due to metadata corruption or other reasons. This has been there all
along, but improved sanity checks and error handling may have made it more
reproducible in fuzzing tests.
Fix this issue by adding missing error paths in nilfs_set_link(),
nilfs_delete_entry(), and their caller nilfs_rename().
Link: https://lkml.kernel.org/r/20250111143518.7901-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20250111143518.7901-2-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+32c3706ebf5d95046ea1@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=32c3706ebf5d95046ea1
Reported-by: syzbot+1097e95f134f37d9395c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1097e95f134f37d9395c
Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations")
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:35 +09:00
|
|
|
err = nilfs_set_link(new_dir, new_de, new_folio, old_inode);
|
2023-11-27 23:30:31 +09:00
|
|
|
folio_release_kmap(new_folio, new_de);
|
nilfs2: handle errors that nilfs_prepare_chunk() may return
Patch series "nilfs2: fix issues with rename operations".
This series fixes BUG_ON check failures reported by syzbot around rename
operations, and a minor behavioral issue where the mtime of a child
directory changes when it is renamed instead of moved.
This patch (of 2):
The directory manipulation routines nilfs_set_link() and
nilfs_delete_entry() rewrite the directory entry in the folio/page
previously read by nilfs_find_entry(), so error handling is omitted on the
assumption that nilfs_prepare_chunk(), which prepares the buffer for
rewriting, will always succeed for these. And if an error is returned, it
triggers the legacy BUG_ON() checks in each routine.
This assumption is wrong, as proven by syzbot: the buffer layer called by
nilfs_prepare_chunk() may call nilfs_get_block() if necessary, which may
fail due to metadata corruption or other reasons. This has been there all
along, but improved sanity checks and error handling may have made it more
reproducible in fuzzing tests.
Fix this issue by adding missing error paths in nilfs_set_link(),
nilfs_delete_entry(), and their caller nilfs_rename().
Link: https://lkml.kernel.org/r/20250111143518.7901-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20250111143518.7901-2-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+32c3706ebf5d95046ea1@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=32c3706ebf5d95046ea1
Reported-by: syzbot+1097e95f134f37d9395c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1097e95f134f37d9395c
Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations")
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:35 +09:00
|
|
|
if (unlikely(err))
|
|
|
|
goto out_dir;
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(new_dir);
|
2023-07-05 15:01:24 -04:00
|
|
|
inode_set_ctime_current(new_inode);
|
nilfs2: do not update mtime of renamed directory that is not moved
A minor issue with nilfs_rename, originating from an old ext2
implementation, is that the mtime is updated even if the rename target is
a directory and it is renamed within the same directory, rather than moved
to a different directory.
In this case, the child directory being renamed does not change in any
way, so changing its mtime is unnecessary according to the specification,
and can unnecessarily confuse backup tools.
In ext2, this issue was fixed by commit 39fe7557b4d6 ("ext2: Do not update
mtime of a moved directory") and a few subsequent fixes, but it remained
in nilfs2.
Fix this issue by not calling nilfs_set_link(), which rewrites the inode
number of the directory entry that refers to the parent directory, when
the move target is a directory and the source and destination are the same
directory.
Here, the directory to be moved only needs to be read if the inode number
of the parent directory is rewritten with nilfs_set_link, so also adjust
the execution conditions of the preparation work to avoid unnecessary
directory reads.
Link: https://lkml.kernel.org/r/20250111143518.7901-3-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:36 +09:00
|
|
|
if (old_is_dir)
|
2009-04-06 19:01:34 -07:00
|
|
|
drop_nlink(new_inode);
|
2009-11-27 19:41:08 +09:00
|
|
|
drop_nlink(new_inode);
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(new_inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
} else {
|
|
|
|
err = nilfs_add_link(new_dentry, old_inode);
|
2011-03-02 12:01:13 -05:00
|
|
|
if (err)
|
2009-04-06 19:01:34 -07:00
|
|
|
goto out_dir;
|
nilfs2: do not update mtime of renamed directory that is not moved
A minor issue with nilfs_rename, originating from an old ext2
implementation, is that the mtime is updated even if the rename target is
a directory and it is renamed within the same directory, rather than moved
to a different directory.
In this case, the child directory being renamed does not change in any
way, so changing its mtime is unnecessary according to the specification,
and can unnecessarily confuse backup tools.
In ext2, this issue was fixed by commit 39fe7557b4d6 ("ext2: Do not update
mtime of a moved directory") and a few subsequent fixes, but it remained
in nilfs2.
Fix this issue by not calling nilfs_set_link(), which rewrites the inode
number of the directory entry that refers to the parent directory, when
the move target is a directory and the source and destination are the same
directory.
Here, the directory to be moved only needs to be read if the inode number
of the parent directory is rewritten with nilfs_set_link, so also adjust
the execution conditions of the preparation work to avoid unnecessary
directory reads.
Link: https://lkml.kernel.org/r/20250111143518.7901-3-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:36 +09:00
|
|
|
if (old_is_dir) {
|
2009-11-27 19:41:08 +09:00
|
|
|
inc_nlink(new_dir);
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(new_dir);
|
2009-11-27 19:41:08 +09:00
|
|
|
}
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like most other Unix systems, set the ctime for inodes on a
|
|
|
|
* rename.
|
|
|
|
*/
|
2023-07-05 15:01:24 -04:00
|
|
|
inode_set_ctime_current(old_inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
|
nilfs2: handle errors that nilfs_prepare_chunk() may return
Patch series "nilfs2: fix issues with rename operations".
This series fixes BUG_ON check failures reported by syzbot around rename
operations, and a minor behavioral issue where the mtime of a child
directory changes when it is renamed instead of moved.
This patch (of 2):
The directory manipulation routines nilfs_set_link() and
nilfs_delete_entry() rewrite the directory entry in the folio/page
previously read by nilfs_find_entry(), so error handling is omitted on the
assumption that nilfs_prepare_chunk(), which prepares the buffer for
rewriting, will always succeed for these. And if an error is returned, it
triggers the legacy BUG_ON() checks in each routine.
This assumption is wrong, as proven by syzbot: the buffer layer called by
nilfs_prepare_chunk() may call nilfs_get_block() if necessary, which may
fail due to metadata corruption or other reasons. This has been there all
along, but improved sanity checks and error handling may have made it more
reproducible in fuzzing tests.
Fix this issue by adding missing error paths in nilfs_set_link(),
nilfs_delete_entry(), and their caller nilfs_rename().
Link: https://lkml.kernel.org/r/20250111143518.7901-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20250111143518.7901-2-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+32c3706ebf5d95046ea1@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=32c3706ebf5d95046ea1
Reported-by: syzbot+1097e95f134f37d9395c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1097e95f134f37d9395c
Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations")
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:35 +09:00
|
|
|
err = nilfs_delete_entry(old_de, old_folio);
|
|
|
|
if (likely(!err)) {
|
nilfs2: do not update mtime of renamed directory that is not moved
A minor issue with nilfs_rename, originating from an old ext2
implementation, is that the mtime is updated even if the rename target is
a directory and it is renamed within the same directory, rather than moved
to a different directory.
In this case, the child directory being renamed does not change in any
way, so changing its mtime is unnecessary according to the specification,
and can unnecessarily confuse backup tools.
In ext2, this issue was fixed by commit 39fe7557b4d6 ("ext2: Do not update
mtime of a moved directory") and a few subsequent fixes, but it remained
in nilfs2.
Fix this issue by not calling nilfs_set_link(), which rewrites the inode
number of the directory entry that refers to the parent directory, when
the move target is a directory and the source and destination are the same
directory.
Here, the directory to be moved only needs to be read if the inode number
of the parent directory is rewritten with nilfs_set_link, so also adjust
the execution conditions of the preparation work to avoid unnecessary
directory reads.
Link: https://lkml.kernel.org/r/20250111143518.7901-3-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:36 +09:00
|
|
|
if (old_is_dir) {
|
|
|
|
if (old_dir != new_dir)
|
|
|
|
err = nilfs_set_link(old_inode, dir_de,
|
|
|
|
dir_folio, new_dir);
|
nilfs2: handle errors that nilfs_prepare_chunk() may return
Patch series "nilfs2: fix issues with rename operations".
This series fixes BUG_ON check failures reported by syzbot around rename
operations, and a minor behavioral issue where the mtime of a child
directory changes when it is renamed instead of moved.
This patch (of 2):
The directory manipulation routines nilfs_set_link() and
nilfs_delete_entry() rewrite the directory entry in the folio/page
previously read by nilfs_find_entry(), so error handling is omitted on the
assumption that nilfs_prepare_chunk(), which prepares the buffer for
rewriting, will always succeed for these. And if an error is returned, it
triggers the legacy BUG_ON() checks in each routine.
This assumption is wrong, as proven by syzbot: the buffer layer called by
nilfs_prepare_chunk() may call nilfs_get_block() if necessary, which may
fail due to metadata corruption or other reasons. This has been there all
along, but improved sanity checks and error handling may have made it more
reproducible in fuzzing tests.
Fix this issue by adding missing error paths in nilfs_set_link(),
nilfs_delete_entry(), and their caller nilfs_rename().
Link: https://lkml.kernel.org/r/20250111143518.7901-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20250111143518.7901-2-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+32c3706ebf5d95046ea1@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=32c3706ebf5d95046ea1
Reported-by: syzbot+1097e95f134f37d9395c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1097e95f134f37d9395c
Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations")
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:35 +09:00
|
|
|
drop_nlink(old_dir);
|
|
|
|
}
|
|
|
|
nilfs_mark_inode_dirty(old_dir);
|
2009-04-06 19:01:34 -07:00
|
|
|
}
|
2009-11-27 19:41:14 +09:00
|
|
|
nilfs_mark_inode_dirty(old_inode);
|
2009-04-06 19:01:34 -07:00
|
|
|
|
|
|
|
out_dir:
|
nilfs2: move page release outside of nilfs_delete_entry and nilfs_set_link
Patch series "nilfs2: Folio conversions for directory paths".
This series applies page->folio conversions to nilfs2 directory
operations. This reduces hidden compound_head() calls and also converts
deprecated kmap calls to kmap_local in the directory code.
Although nilfs2 does not yet support large folios, Matthew has done his
best here to include support for large folios, which will be needed for
devices with large block sizes.
This series corresponds to the second half of the original post [1], but
with two complementary patches inserted at the beginning and some
adjustments, to prevent a kmap_local constraint violation found during
testing with highmem mapping.
[1] https://lkml.kernel.org/r/20231106173903.1734114-1-willy@infradead.org
I have reviewed all changes and tested this for regular and small block
sizes, both on machines with and without highmem mapping. No issues
found.
This patch (of 17):
In a few directory operations, the call to nilfs_put_page() for a page
obtained using nilfs_find_entry() or nilfs_dotdot() is hidden in
nilfs_set_link() and nilfs_delete_entry(), making it difficult to track
page release and preventing change of its call position.
By moving nilfs_put_page() out of these functions, this makes the page
get/put correspondence clearer and makes it easier to swap
nilfs_put_page() calls (and kunmap calls within them) when modifying
multiple directory entries simultaneously in nilfs_rename().
Also, update comments for nilfs_set_link() and nilfs_delete_entry() to
reflect changes in their behavior.
To make nilfs_put_page() visible from namei.c, this moves its definition
to nilfs.h and replaces existing equivalents to use it, but the exposure
of that definition is temporary and will be removed on a later kmap ->
kmap_local conversion.
Link: https://lkml.kernel.org/r/20231127143036.2425-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20231127143036.2425-2-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-11-27 23:30:20 +09:00
|
|
|
if (dir_de)
|
2023-11-27 23:30:31 +09:00
|
|
|
folio_release_kmap(dir_folio, dir_de);
|
2009-04-06 19:01:34 -07:00
|
|
|
out_old:
|
2023-11-27 23:30:31 +09:00
|
|
|
folio_release_kmap(old_folio, old_de);
|
2009-04-06 19:01:34 -07:00
|
|
|
out:
|
nilfs2: handle errors that nilfs_prepare_chunk() may return
Patch series "nilfs2: fix issues with rename operations".
This series fixes BUG_ON check failures reported by syzbot around rename
operations, and a minor behavioral issue where the mtime of a child
directory changes when it is renamed instead of moved.
This patch (of 2):
The directory manipulation routines nilfs_set_link() and
nilfs_delete_entry() rewrite the directory entry in the folio/page
previously read by nilfs_find_entry(), so error handling is omitted on the
assumption that nilfs_prepare_chunk(), which prepares the buffer for
rewriting, will always succeed for these. And if an error is returned, it
triggers the legacy BUG_ON() checks in each routine.
This assumption is wrong, as proven by syzbot: the buffer layer called by
nilfs_prepare_chunk() may call nilfs_get_block() if necessary, which may
fail due to metadata corruption or other reasons. This has been there all
along, but improved sanity checks and error handling may have made it more
reproducible in fuzzing tests.
Fix this issue by adding missing error paths in nilfs_set_link(),
nilfs_delete_entry(), and their caller nilfs_rename().
Link: https://lkml.kernel.org/r/20250111143518.7901-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20250111143518.7901-2-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+32c3706ebf5d95046ea1@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=32c3706ebf5d95046ea1
Reported-by: syzbot+1097e95f134f37d9395c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1097e95f134f37d9395c
Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations")
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-01-11 23:26:35 +09:00
|
|
|
if (likely(!err))
|
|
|
|
err = nilfs_transaction_commit(old_dir->i_sb);
|
|
|
|
else
|
|
|
|
nilfs_transaction_abort(old_dir->i_sb);
|
2009-04-06 19:01:34 -07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-08-27 00:23:02 +09:00
|
|
|
/*
|
|
|
|
* Export operations
|
|
|
|
*/
|
|
|
|
static struct dentry *nilfs_get_parent(struct dentry *child)
|
|
|
|
{
|
2024-10-04 12:35:31 +09:00
|
|
|
ino_t ino;
|
|
|
|
int res;
|
2010-08-27 00:23:02 +09:00
|
|
|
struct nilfs_root *root;
|
|
|
|
|
2024-10-04 12:35:31 +09:00
|
|
|
res = nilfs_inode_by_name(d_inode(child), &dotdot_name, &ino);
|
|
|
|
if (res)
|
|
|
|
return ERR_PTR(res);
|
2010-08-27 00:23:02 +09:00
|
|
|
|
2015-03-17 22:25:59 +00:00
|
|
|
root = NILFS_I(d_inode(child))->i_root;
|
2010-08-27 00:23:02 +09:00
|
|
|
|
2023-11-11 01:55:03 -05:00
|
|
|
return d_obtain_alias(nilfs_iget(child->d_sb, root, ino));
|
2010-08-27 00:23:02 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct dentry *nilfs_get_dentry(struct super_block *sb, u64 cno,
|
|
|
|
u64 ino, u32 gen)
|
|
|
|
{
|
|
|
|
struct nilfs_root *root;
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO)
|
|
|
|
return ERR_PTR(-ESTALE);
|
|
|
|
|
2011-03-09 11:05:08 +09:00
|
|
|
root = nilfs_lookup_root(sb->s_fs_info, cno);
|
2010-08-27 00:23:02 +09:00
|
|
|
if (!root)
|
|
|
|
return ERR_PTR(-ESTALE);
|
|
|
|
|
|
|
|
inode = nilfs_iget(sb, root, ino);
|
|
|
|
nilfs_put_root(root);
|
|
|
|
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
return ERR_CAST(inode);
|
|
|
|
if (gen && inode->i_generation != gen) {
|
|
|
|
iput(inode);
|
|
|
|
return ERR_PTR(-ESTALE);
|
|
|
|
}
|
|
|
|
return d_obtain_alias(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
|
|
|
|
int fh_len, int fh_type)
|
|
|
|
{
|
|
|
|
struct nilfs_fid *fid = (struct nilfs_fid *)fh;
|
|
|
|
|
2015-06-25 15:03:45 -07:00
|
|
|
if (fh_len < NILFS_FID_SIZE_NON_CONNECTABLE ||
|
2010-08-27 00:23:02 +09:00
|
|
|
(fh_type != FILEID_NILFS_WITH_PARENT &&
|
|
|
|
fh_type != FILEID_NILFS_WITHOUT_PARENT))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return nilfs_get_dentry(sb, fid->cno, fid->ino, fid->gen);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh,
|
|
|
|
int fh_len, int fh_type)
|
|
|
|
{
|
|
|
|
struct nilfs_fid *fid = (struct nilfs_fid *)fh;
|
|
|
|
|
2015-06-25 15:03:45 -07:00
|
|
|
if (fh_len < NILFS_FID_SIZE_CONNECTABLE ||
|
2010-08-27 00:23:02 +09:00
|
|
|
fh_type != FILEID_NILFS_WITH_PARENT)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return nilfs_get_dentry(sb, fid->cno, fid->parent_ino, fid->parent_gen);
|
|
|
|
}
|
|
|
|
|
2012-04-02 14:34:06 -04:00
|
|
|
static int nilfs_encode_fh(struct inode *inode, __u32 *fh, int *lenp,
|
|
|
|
struct inode *parent)
|
2010-08-27 00:23:02 +09:00
|
|
|
{
|
|
|
|
struct nilfs_fid *fid = (struct nilfs_fid *)fh;
|
|
|
|
struct nilfs_root *root = NILFS_I(inode)->i_root;
|
|
|
|
int type;
|
|
|
|
|
2012-04-02 14:34:06 -04:00
|
|
|
if (parent && *lenp < NILFS_FID_SIZE_CONNECTABLE) {
|
|
|
|
*lenp = NILFS_FID_SIZE_CONNECTABLE;
|
2013-02-17 15:48:11 +09:00
|
|
|
return FILEID_INVALID;
|
2012-04-02 14:34:06 -04:00
|
|
|
}
|
|
|
|
if (*lenp < NILFS_FID_SIZE_NON_CONNECTABLE) {
|
|
|
|
*lenp = NILFS_FID_SIZE_NON_CONNECTABLE;
|
2013-02-17 15:48:11 +09:00
|
|
|
return FILEID_INVALID;
|
2012-04-02 14:34:06 -04:00
|
|
|
}
|
2010-08-27 00:23:02 +09:00
|
|
|
|
|
|
|
fid->cno = root->cno;
|
|
|
|
fid->ino = inode->i_ino;
|
|
|
|
fid->gen = inode->i_generation;
|
|
|
|
|
2012-04-02 14:34:06 -04:00
|
|
|
if (parent) {
|
2010-08-27 00:23:02 +09:00
|
|
|
fid->parent_ino = parent->i_ino;
|
|
|
|
fid->parent_gen = parent->i_generation;
|
|
|
|
type = FILEID_NILFS_WITH_PARENT;
|
|
|
|
*lenp = NILFS_FID_SIZE_CONNECTABLE;
|
|
|
|
} else {
|
|
|
|
type = FILEID_NILFS_WITHOUT_PARENT;
|
|
|
|
*lenp = NILFS_FID_SIZE_NON_CONNECTABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2009-09-21 17:01:11 -07:00
|
|
|
const struct inode_operations nilfs_dir_inode_operations = {
|
2009-04-06 19:01:34 -07:00
|
|
|
.create = nilfs_create,
|
|
|
|
.lookup = nilfs_lookup,
|
|
|
|
.link = nilfs_link,
|
|
|
|
.unlink = nilfs_unlink,
|
|
|
|
.symlink = nilfs_symlink,
|
|
|
|
.mkdir = nilfs_mkdir,
|
|
|
|
.rmdir = nilfs_rmdir,
|
|
|
|
.mknod = nilfs_mknod,
|
|
|
|
.rename = nilfs_rename,
|
|
|
|
.setattr = nilfs_setattr,
|
|
|
|
.permission = nilfs_permission,
|
2010-12-26 16:38:43 +09:00
|
|
|
.fiemap = nilfs_fiemap,
|
2021-04-07 14:36:44 +02:00
|
|
|
.fileattr_get = nilfs_fileattr_get,
|
|
|
|
.fileattr_set = nilfs_fileattr_set,
|
2009-04-06 19:01:34 -07:00
|
|
|
};
|
|
|
|
|
2009-09-21 17:01:11 -07:00
|
|
|
const struct inode_operations nilfs_special_inode_operations = {
|
2009-04-06 19:01:34 -07:00
|
|
|
.setattr = nilfs_setattr,
|
|
|
|
.permission = nilfs_permission,
|
|
|
|
};
|
|
|
|
|
2009-09-21 17:01:11 -07:00
|
|
|
const struct inode_operations nilfs_symlink_inode_operations = {
|
2015-11-17 10:20:54 -05:00
|
|
|
.get_link = page_get_link,
|
2010-08-15 23:33:57 +09:00
|
|
|
.permission = nilfs_permission,
|
2009-04-06 19:01:34 -07:00
|
|
|
};
|
2010-08-27 00:23:02 +09:00
|
|
|
|
|
|
|
const struct export_operations nilfs_export_ops = {
|
|
|
|
.encode_fh = nilfs_encode_fh,
|
|
|
|
.fh_to_dentry = nilfs_fh_to_dentry,
|
|
|
|
.fh_to_parent = nilfs_fh_to_parent,
|
|
|
|
.get_parent = nilfs_get_parent,
|
|
|
|
};
|