[PATCH OLK-6.6 0/4] Fix file content inconsistency issue and incorporate subsequent fix patches

Li Lingfeng (1): fs: add fsnotify_open in backing_file_open NeilBrown (3): NFS: add atomic_open for NFSv3 to handle O_TRUNC correctly. NFS: abort nfs_atomic_open_v23 if name is too long. vfs: generate FS_CREATE before FS_OPEN when ->atomic_open used. fs/backing-file.c | 3 +++ fs/namei.c | 10 ++++++-- fs/nfs/dir.c | 57 +++++++++++++++++++++++++++++++++++++++--- fs/nfs/nfs3proc.c | 1 + fs/nfs/proc.c | 1 + fs/open.c | 22 ++++++++++------ include/linux/nfs_fs.h | 3 +++ 7 files changed, 85 insertions(+), 12 deletions(-) -- 2.31.1

From: NeilBrown <neilb@suse.de> mainline inclusion from mainline-v6.10-rc1 commit 7c6c5249f061b64fc6b5b90bc147169a048691bf category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBCU3L Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... ---------------------------------------------------------------------- With two clients, each with NFSv3 mounts of the same directory, the sequence: client1 client2 ls -l afile echo hello there > afile echo HELLO > afile cat afile will show HELLO there because the O_TRUNC requested in the final 'echo' doesn't take effect. This is because the "Negative dentry, just create a file" section in lookup_open() assumes that the file *does* get created since the dentry was negative, so it sets FMODE_CREATED, and this causes do_open() to clear O_TRUNC and so the file doesn't get truncated. Even mounting with -o lookupcache=none does not help as nfs_neg_need_reval() always returns false if LOOKUP_CREATE is set. This patch fixes the problem by providing an atomic_open inode operation for NFSv3 (and v2). The code is largely the code from the branch in lookup_open() when atomic_open is not provided. The significant change is that the O_TRUNC flag is passed a new nfs_do_create() which add 'trunc' handling to nfs_create(). With this change we also optimise away an unnecessary LOOKUP before the file is created. Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Li Lingfeng <lilingfeng3@huawei.com> --- fs/nfs/dir.c | 54 +++++++++++++++++++++++++++++++++++++++--- fs/nfs/nfs3proc.c | 1 + fs/nfs/proc.c | 1 + include/linux/nfs_fs.h | 3 +++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index d770fd24952b..78e5ff5b5a26 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -56,6 +56,8 @@ static int nfs_readdir(struct file *, struct dir_context *); static int nfs_fsync_dir(struct file *, loff_t, loff_t, int); static loff_t nfs_llseek_dir(struct file *, loff_t, int); static void nfs_readdir_clear_array(struct folio *); +static int nfs_do_create(struct inode *dir, struct dentry *dentry, + umode_t mode, int open_flags); const struct file_operations nfs_dir_operations = { .llseek = nfs_llseek_dir, @@ -2273,6 +2275,41 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags) #endif /* CONFIG_NFSV4 */ +int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry, + struct file *file, unsigned int open_flags, + umode_t mode) +{ + + /* Same as look+open from lookup_open(), but with different O_TRUNC + * handling. + */ + int error = 0; + + if (open_flags & O_CREAT) { + file->f_mode |= FMODE_CREATED; + error = nfs_do_create(dir, dentry, mode, open_flags); + if (error) + return error; + return finish_open(file, dentry, NULL); + } else if (d_in_lookup(dentry)) { + /* The only flags nfs_lookup considers are + * LOOKUP_EXCL and LOOKUP_RENAME_TARGET, and + * we want those to be zero so the lookup isn't skipped. + */ + struct dentry *res = nfs_lookup(dir, dentry, 0); + + d_lookup_done(dentry); + if (unlikely(res)) { + if (IS_ERR(res)) + return PTR_ERR(res); + return finish_no_open(file, res); + } + } + return finish_no_open(file, NULL); + +} +EXPORT_SYMBOL_GPL(nfs_atomic_open_v23); + struct dentry * nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle, struct nfs_fattr *fattr) @@ -2333,18 +2370,23 @@ EXPORT_SYMBOL_GPL(nfs_instantiate); * that the operation succeeded on the server, but an error in the * reply path made it appear to have failed. */ -int nfs_create(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, umode_t mode, bool excl) +static int nfs_do_create(struct inode *dir, struct dentry *dentry, + umode_t mode, int open_flags) { struct iattr attr; - int open_flags = excl ? O_CREAT | O_EXCL : O_CREAT; int error; + open_flags |= O_CREAT; + dfprintk(VFS, "NFS: create(%s/%lu), %pd\n", dir->i_sb->s_id, dir->i_ino, dentry); attr.ia_mode = mode; attr.ia_valid = ATTR_MODE; + if (open_flags & O_TRUNC) { + attr.ia_size = 0; + attr.ia_valid |= ATTR_SIZE; + } trace_nfs_create_enter(dir, dentry, open_flags); error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags); @@ -2356,6 +2398,12 @@ int nfs_create(struct mnt_idmap *idmap, struct inode *dir, d_drop(dentry); return error; } + +int nfs_create(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) +{ + return nfs_do_create(dir, dentry, mode, excl ? O_EXCL : 0); +} EXPORT_SYMBOL_GPL(nfs_create); /* diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 4bf208a0a8e9..3f212223cd73 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -985,6 +985,7 @@ static int nfs3_have_delegation(struct inode *inode, fmode_t flags) static const struct inode_operations nfs3_dir_inode_operations = { .create = nfs_create, + .atomic_open = nfs_atomic_open_v23, .lookup = nfs_lookup, .link = nfs_link, .unlink = nfs_unlink, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index e3570c656b0f..3bcc009c217b 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -694,6 +694,7 @@ static int nfs_have_delegation(struct inode *inode, fmode_t flags) static const struct inode_operations nfs_dir_inode_operations = { .create = nfs_create, .lookup = nfs_lookup, + .atomic_open = nfs_atomic_open_v23, .link = nfs_link, .unlink = nfs_unlink, .symlink = nfs_symlink, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 832b7e354b4e..aef98dd1b9e1 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -561,6 +561,9 @@ extern int nfs_may_open(struct inode *inode, const struct cred *cred, int openfl extern void nfs_access_zap_cache(struct inode *inode); extern int nfs_access_get_cached(struct inode *inode, const struct cred *cred, u32 *mask, bool may_block); +extern int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry, + struct file *file, unsigned int open_flags, + umode_t mode); /* * linux/fs/nfs/symlink.c -- 2.31.1

From: NeilBrown <neilb@suse.de> mainline inclusion from mainline-v6.10-rc4 commit 296f4ce81d08e73c22408c49f4938a85bd075e5c category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBCU3L Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... ---------------------------------------------------------------------- An attempt to open a file with a name longer than NFS3_MAXNAMLEN will trigger a WARN_ON_ONCE in encode_filename3() because nfs_atomic_open_v23() doesn't have the test on ->d_name.len that nfs_atomic_open() has. So add that test. Reported-by: James Clark <james.clark@arm.com> Closes: https://lore.kernel.org/all/20240528105249.69200-1-james.clark@arm.com/ Fixes: 7c6c5249f061 ("NFS: add atomic_open for NFSv3 to handle O_TRUNC correctly.") Signed-off-by: NeilBrown <neilb@suse.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Li Lingfeng <lilingfeng3@huawei.com> --- fs/nfs/dir.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 78e5ff5b5a26..a12c5ec1718f 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2285,6 +2285,9 @@ int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry, */ int error = 0; + if (dentry->d_name.len > NFS_SERVER(dir)->namelen) + return -ENAMETOOLONG; + if (open_flags & O_CREAT) { file->f_mode |= FMODE_CREATED; error = nfs_do_create(dir, dentry, mode, open_flags); -- 2.31.1

From: NeilBrown <neilb@suse.de> mainline inclusion from mainline-v6.10-rc7 commit 7d1cf5e624ef5d81b933e8b7f4927531166c0f7a category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBCU3L Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... ---------------------------------------------------------------------- When a file is opened and created with open(..., O_CREAT) we get both the CREATE and OPEN fsnotify events and would expect them in that order. For most filesystems we get them in that order because open_last_lookups() calls fsnofify_create() and then do_open() (from path_openat()) calls vfs_open()->do_dentry_open() which calls fsnotify_open(). However when ->atomic_open is used, the do_dentry_open() -> fsnotify_open() call happens from finish_open() which is called from the ->atomic_open handler in lookup_open() which is called *before* open_last_lookups() calls fsnotify_create. So we get the "open" notification before "create" - which is backwards. ltp testcase inotify02 tests this and reports the inconsistency. This patch lifts the fsnotify_open() call out of do_dentry_open() and places it higher up the call stack. There are three callers of do_dentry_open(). For vfs_open() and kernel_file_open() the fsnotify_open() is placed directly in that caller so there should be no behavioural change. For finish_open() there are two cases: - finish_open is used in ->atomic_open handlers. For these we add a call to fsnotify_open() at open_last_lookups() if FMODE_OPENED is set - which means do_dentry_open() has been called. - finish_open is used in ->tmpfile() handlers. For these a similar call to fsnotify_open() is added to vfs_tmpfile() With this patch NFSv3 is restored to its previous behaviour (before ->atomic_open support was added) of generating CREATE notifications before OPEN, and NFSv4 now has that same correct ordering that is has not had before. I haven't tested other filesystems. Fixes: 7c6c5249f061 ("NFS: add atomic_open for NFSv3 to handle O_TRUNC correctly.") Reported-by: James Clark <james.clark@arm.com> Closes: https://lore.kernel.org/all/01c3bf2e-eb1f-4b7f-a54f-d2a05dd3d8c8@arm.com Signed-off-by: NeilBrown <neilb@suse.de> Link: https://lore.kernel.org/r/171817619547.14261.975798725161704336@noble.neil.b... Fixes: 7b8c9d7bb457 ("fsnotify: move fsnotify_open() hook into do_dentry_open()") Tested-by: James Clark <james.clark@arm.com> Signed-off-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20240617162303.1596-2-jack@suse.cz Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Christian Brauner <brauner@kernel.org> Conflicts: fs/open.c [Commit 0f4a2cebe256 ("do_dentry_open(): kill inode argument") remove inode from the parameter list.] Signed-off-by: Li Lingfeng <lilingfeng3@huawei.com> --- fs/namei.c | 10 ++++++++-- fs/open.c | 22 +++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 13d1ca4f6842..19199d81bf17 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3589,8 +3589,12 @@ static const char *open_last_lookups(struct nameidata *nd, else inode_lock_shared(dir->d_inode); dentry = lookup_open(nd, file, op, got_write); - if (!IS_ERR(dentry) && (file->f_mode & FMODE_CREATED)) - fsnotify_create(dir->d_inode, dentry); + if (!IS_ERR(dentry)) { + if (file->f_mode & FMODE_CREATED) + fsnotify_create(dir->d_inode, dentry); + if (file->f_mode & FMODE_OPENED) + fsnotify_open(file); + } if (open_flag & O_CREAT) inode_unlock(dir->d_inode); else @@ -3717,6 +3721,8 @@ static int vfs_tmpfile(struct mnt_idmap *idmap, mode = vfs_prepare_mode(idmap, dir, mode, mode, mode); error = dir->i_op->tmpfile(idmap, dir, file, mode); dput(child); + if (file->f_mode & FMODE_OPENED) + fsnotify_open(file); if (error) return error; /* Don't check for other permissions, the inode was just created */ diff --git a/fs/open.c b/fs/open.c index 4679db501d43..ae3d152a770f 100644 --- a/fs/open.c +++ b/fs/open.c @@ -997,11 +997,6 @@ static int do_dentry_open(struct file *f, } } - /* - * Once we return a file with FMODE_OPENED, __fput() will call - * fsnotify_close(), so we need fsnotify_open() here for symmetry. - */ - fsnotify_open(f); return 0; cleanup_all: @@ -1078,8 +1073,19 @@ EXPORT_SYMBOL(file_path); */ int vfs_open(const struct path *path, struct file *file) { + int ret; + file->f_path = *path; - return do_dentry_open(file, d_backing_inode(path->dentry), NULL); + ret = do_dentry_open(file, d_backing_inode(path->dentry), NULL); + if (!ret) { + /* + * Once we return a file with FMODE_OPENED, __fput() will call + * fsnotify_close(), so we need fsnotify_open() here for + * symmetry. + */ + fsnotify_open(file); + } + return ret; } struct file *dentry_open(const struct path *path, int flags, @@ -1171,8 +1177,10 @@ struct file *kernel_file_open(const struct path *path, int flags, error = do_dentry_open(f, inode, NULL); if (error) { fput(f); - f = ERR_PTR(error); + return ERR_PTR(error); } + + fsnotify_open(f); return f; } EXPORT_SYMBOL_GPL(kernel_file_open); -- 2.31.1

hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBCU3L -------------------------------- Commit 7d1cf5e624ef ("vfs: generate FS_CREATE before FS_OPEN when ->atomic_open used.") removed fsnotify_open from do_dentry_open, which results in fsnotify_open not being called anymore when invoking backing_file_open. As a result, the fanotify13 test case in the ltp test suite fails: fanotify13.c:222: TFAIL: Unexpected mask received: 10 (expected: 30) in event Add a call to fsnotify_open after a successful do_dentry_open invocation in backing_file_open. Fixes: 7d1cf5e624ef ("vfs: generate FS_CREATE before FS_OPEN when ->atomic_open used.") Signed-off-by: Li Lingfeng <lilingfeng3@huawei.com> --- fs/backing-file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/backing-file.c b/fs/backing-file.c index e1a4b23b255b..184bfce0ea75 100644 --- a/fs/backing-file.c +++ b/fs/backing-file.c @@ -12,6 +12,7 @@ #include <linux/backing-file.h> #include <linux/splice.h> #include <linux/mm.h> +#include <linux/fsnotify.h> #include "internal.h" @@ -46,6 +47,8 @@ struct file *backing_file_open(const struct path *user_path, int flags, if (error) { fput(f); f = ERR_PTR(error); + } else { + fsnotify_open(f); } return f; -- 2.31.1

反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/16684 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/2XO... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/16684 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/2XO...
participants (2)
-
Li Lingfeng
-
patchwork bot