Skip to content

Commit 7c6c524

Browse files
neilbrownTrond Myklebust
authored andcommitted
NFS: add atomic_open for NFSv3 to handle O_TRUNC correctly.
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 <[email protected]> Signed-off-by: Trond Myklebust <[email protected]>
1 parent 464b424 commit 7c6c524

File tree

4 files changed

+56
-3
lines changed

4 files changed

+56
-3
lines changed

fs/nfs/dir.c

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ static int nfs_readdir(struct file *, struct dir_context *);
5656
static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
5757
static loff_t nfs_llseek_dir(struct file *, loff_t, int);
5858
static void nfs_readdir_clear_array(struct folio *);
59+
static int nfs_do_create(struct inode *dir, struct dentry *dentry,
60+
umode_t mode, int open_flags);
5961

6062
const struct file_operations nfs_dir_operations = {
6163
.llseek = nfs_llseek_dir,
@@ -2243,6 +2245,41 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
22432245

22442246
#endif /* CONFIG_NFSV4 */
22452247

2248+
int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry,
2249+
struct file *file, unsigned int open_flags,
2250+
umode_t mode)
2251+
{
2252+
2253+
/* Same as look+open from lookup_open(), but with different O_TRUNC
2254+
* handling.
2255+
*/
2256+
int error = 0;
2257+
2258+
if (open_flags & O_CREAT) {
2259+
file->f_mode |= FMODE_CREATED;
2260+
error = nfs_do_create(dir, dentry, mode, open_flags);
2261+
if (error)
2262+
return error;
2263+
return finish_open(file, dentry, NULL);
2264+
} else if (d_in_lookup(dentry)) {
2265+
/* The only flags nfs_lookup considers are
2266+
* LOOKUP_EXCL and LOOKUP_RENAME_TARGET, and
2267+
* we want those to be zero so the lookup isn't skipped.
2268+
*/
2269+
struct dentry *res = nfs_lookup(dir, dentry, 0);
2270+
2271+
d_lookup_done(dentry);
2272+
if (unlikely(res)) {
2273+
if (IS_ERR(res))
2274+
return PTR_ERR(res);
2275+
return finish_no_open(file, res);
2276+
}
2277+
}
2278+
return finish_no_open(file, NULL);
2279+
2280+
}
2281+
EXPORT_SYMBOL_GPL(nfs_atomic_open_v23);
2282+
22462283
struct dentry *
22472284
nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
22482285
struct nfs_fattr *fattr)
@@ -2303,18 +2340,23 @@ EXPORT_SYMBOL_GPL(nfs_instantiate);
23032340
* that the operation succeeded on the server, but an error in the
23042341
* reply path made it appear to have failed.
23052342
*/
2306-
int nfs_create(struct mnt_idmap *idmap, struct inode *dir,
2307-
struct dentry *dentry, umode_t mode, bool excl)
2343+
static int nfs_do_create(struct inode *dir, struct dentry *dentry,
2344+
umode_t mode, int open_flags)
23082345
{
23092346
struct iattr attr;
2310-
int open_flags = excl ? O_CREAT | O_EXCL : O_CREAT;
23112347
int error;
23122348

2349+
open_flags |= O_CREAT;
2350+
23132351
dfprintk(VFS, "NFS: create(%s/%lu), %pd\n",
23142352
dir->i_sb->s_id, dir->i_ino, dentry);
23152353

23162354
attr.ia_mode = mode;
23172355
attr.ia_valid = ATTR_MODE;
2356+
if (open_flags & O_TRUNC) {
2357+
attr.ia_size = 0;
2358+
attr.ia_valid |= ATTR_SIZE;
2359+
}
23182360

23192361
trace_nfs_create_enter(dir, dentry, open_flags);
23202362
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags);
@@ -2326,6 +2368,12 @@ int nfs_create(struct mnt_idmap *idmap, struct inode *dir,
23262368
d_drop(dentry);
23272369
return error;
23282370
}
2371+
2372+
int nfs_create(struct mnt_idmap *idmap, struct inode *dir,
2373+
struct dentry *dentry, umode_t mode, bool excl)
2374+
{
2375+
return nfs_do_create(dir, dentry, mode, excl ? O_EXCL : 0);
2376+
}
23292377
EXPORT_SYMBOL_GPL(nfs_create);
23302378

23312379
/*

fs/nfs/nfs3proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,7 @@ static int nfs3_have_delegation(struct inode *inode, fmode_t flags)
986986

987987
static const struct inode_operations nfs3_dir_inode_operations = {
988988
.create = nfs_create,
989+
.atomic_open = nfs_atomic_open_v23,
989990
.lookup = nfs_lookup,
990991
.link = nfs_link,
991992
.unlink = nfs_unlink,

fs/nfs/proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ static int nfs_have_delegation(struct inode *inode, fmode_t flags)
695695
static const struct inode_operations nfs_dir_inode_operations = {
696696
.create = nfs_create,
697697
.lookup = nfs_lookup,
698+
.atomic_open = nfs_atomic_open_v23,
698699
.link = nfs_link,
699700
.unlink = nfs_unlink,
700701
.symlink = nfs_symlink,

include/linux/nfs_fs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ extern int nfs_may_open(struct inode *inode, const struct cred *cred, int openfl
561561
extern void nfs_access_zap_cache(struct inode *inode);
562562
extern int nfs_access_get_cached(struct inode *inode, const struct cred *cred,
563563
u32 *mask, bool may_block);
564+
extern int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry,
565+
struct file *file, unsigned int open_flags,
566+
umode_t mode);
564567

565568
/*
566569
* linux/fs/nfs/symlink.c

0 commit comments

Comments
 (0)