Skip to content

Commit dda89e2

Browse files
committed
Merge tag '9p-for-6.9-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs
Pull 9p fix from Eric Van Hensbergen: "This contains a single mitigation to help deal with an apparent race condition between client and server having to deal with inode number collisions" * tag '9p-for-6.9-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs: fs/9p: mitigate inode collisions
2 parents a93289b + d05dcfd commit dda89e2

File tree

4 files changed

+56
-22
lines changed

4 files changed

+56
-22
lines changed

fs/9p/v9fs.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,14 @@ extern int v9fs_vfs_rename(struct mnt_idmap *idmap,
179179
struct inode *old_dir, struct dentry *old_dentry,
180180
struct inode *new_dir, struct dentry *new_dentry,
181181
unsigned int flags);
182-
extern struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid);
182+
extern struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid,
183+
bool new);
183184
extern const struct inode_operations v9fs_dir_inode_operations_dotl;
184185
extern const struct inode_operations v9fs_file_inode_operations_dotl;
185186
extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
186187
extern const struct netfs_request_ops v9fs_req_ops;
187188
extern struct inode *v9fs_fid_iget_dotl(struct super_block *sb,
188-
struct p9_fid *fid);
189+
struct p9_fid *fid, bool new);
189190

190191
/* other default globals */
191192
#define V9FS_PORT 564
@@ -224,12 +225,12 @@ static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses)
224225
*/
225226
static inline struct inode *
226227
v9fs_get_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
227-
struct super_block *sb)
228+
struct super_block *sb, bool new)
228229
{
229230
if (v9fs_proto_dotl(v9ses))
230-
return v9fs_fid_iget_dotl(sb, fid);
231+
return v9fs_fid_iget_dotl(sb, fid, new);
231232
else
232-
return v9fs_fid_iget(sb, fid);
233+
return v9fs_fid_iget(sb, fid, new);
233234
}
234235

235236
#endif

fs/9p/vfs_inode.c

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,8 @@ void v9fs_evict_inode(struct inode *inode)
364364
clear_inode(inode);
365365
}
366366

367-
struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid)
367+
struct inode *
368+
v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid, bool new)
368369
{
369370
dev_t rdev;
370371
int retval;
@@ -376,8 +377,18 @@ struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid)
376377
inode = iget_locked(sb, QID2INO(&fid->qid));
377378
if (unlikely(!inode))
378379
return ERR_PTR(-ENOMEM);
379-
if (!(inode->i_state & I_NEW))
380-
return inode;
380+
if (!(inode->i_state & I_NEW)) {
381+
if (!new) {
382+
goto done;
383+
} else {
384+
p9_debug(P9_DEBUG_VFS, "WARNING: Inode collision %ld\n",
385+
inode->i_ino);
386+
iput(inode);
387+
remove_inode_hash(inode);
388+
inode = iget_locked(sb, QID2INO(&fid->qid));
389+
WARN_ON(!(inode->i_state & I_NEW));
390+
}
391+
}
381392

382393
/*
383394
* initialize the inode with the stat info
@@ -401,11 +412,11 @@ struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid)
401412
v9fs_set_netfs_context(inode);
402413
v9fs_cache_inode_get_cookie(inode);
403414
unlock_new_inode(inode);
415+
done:
404416
return inode;
405417
error:
406418
iget_failed(inode);
407419
return ERR_PTR(retval);
408-
409420
}
410421

411422
/**
@@ -437,8 +448,15 @@ static int v9fs_at_to_dotl_flags(int flags)
437448
*/
438449
static void v9fs_dec_count(struct inode *inode)
439450
{
440-
if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2)
441-
drop_nlink(inode);
451+
if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) {
452+
if (inode->i_nlink) {
453+
drop_nlink(inode);
454+
} else {
455+
p9_debug(P9_DEBUG_VFS,
456+
"WARNING: unexpected i_nlink zero %d inode %ld\n",
457+
inode->i_nlink, inode->i_ino);
458+
}
459+
}
442460
}
443461

444462
/**
@@ -489,6 +507,9 @@ static int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags)
489507
} else
490508
v9fs_dec_count(inode);
491509

510+
if (inode->i_nlink <= 0) /* no more refs unhash it */
511+
remove_inode_hash(inode);
512+
492513
v9fs_invalidate_inode_attr(inode);
493514
v9fs_invalidate_inode_attr(dir);
494515

@@ -554,7 +575,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
554575
/*
555576
* instantiate inode and assign the unopened fid to the dentry
556577
*/
557-
inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
578+
inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb, true);
558579
if (IS_ERR(inode)) {
559580
err = PTR_ERR(inode);
560581
p9_debug(P9_DEBUG_VFS,
@@ -683,7 +704,7 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
683704
else if (IS_ERR(fid))
684705
inode = ERR_CAST(fid);
685706
else
686-
inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
707+
inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb, false);
687708
/*
688709
* If we had a rename on the server and a parallel lookup
689710
* for the new name, then make sure we instantiate with

fs/9p/vfs_inode_dotl.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ static kgid_t v9fs_get_fsgid_for_create(struct inode *dir_inode)
5252
return current_fsgid();
5353
}
5454

55-
struct inode *v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid)
55+
56+
57+
struct inode *
58+
v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid, bool new)
5659
{
5760
int retval;
5861
struct inode *inode;
@@ -62,8 +65,18 @@ struct inode *v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid)
6265
inode = iget_locked(sb, QID2INO(&fid->qid));
6366
if (unlikely(!inode))
6467
return ERR_PTR(-ENOMEM);
65-
if (!(inode->i_state & I_NEW))
66-
return inode;
68+
if (!(inode->i_state & I_NEW)) {
69+
if (!new) {
70+
goto done;
71+
} else { /* deal with race condition in inode number reuse */
72+
p9_debug(P9_DEBUG_ERROR, "WARNING: Inode collision %lx\n",
73+
inode->i_ino);
74+
iput(inode);
75+
remove_inode_hash(inode);
76+
inode = iget_locked(sb, QID2INO(&fid->qid));
77+
WARN_ON(!(inode->i_state & I_NEW));
78+
}
79+
}
6780

6881
/*
6982
* initialize the inode with the stat info
@@ -90,12 +103,11 @@ struct inode *v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid)
90103
goto error;
91104

92105
unlock_new_inode(inode);
93-
106+
done:
94107
return inode;
95108
error:
96109
iget_failed(inode);
97110
return ERR_PTR(retval);
98-
99111
}
100112

101113
struct dotl_openflag_map {
@@ -247,7 +259,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
247259
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
248260
goto out;
249261
}
250-
inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
262+
inode = v9fs_fid_iget_dotl(dir->i_sb, fid, true);
251263
if (IS_ERR(inode)) {
252264
err = PTR_ERR(inode);
253265
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", err);
@@ -340,7 +352,7 @@ static int v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap,
340352
}
341353

342354
/* instantiate inode and assign the unopened fid to the dentry */
343-
inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
355+
inode = v9fs_fid_iget_dotl(dir->i_sb, fid, true);
344356
if (IS_ERR(inode)) {
345357
err = PTR_ERR(inode);
346358
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
@@ -776,7 +788,7 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct inode *dir,
776788
err);
777789
goto error;
778790
}
779-
inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
791+
inode = v9fs_fid_iget_dotl(dir->i_sb, fid, true);
780792
if (IS_ERR(inode)) {
781793
err = PTR_ERR(inode);
782794
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",

fs/9p/vfs_super.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
139139
else
140140
sb->s_d_op = &v9fs_dentry_operations;
141141

142-
inode = v9fs_get_inode_from_fid(v9ses, fid, sb);
142+
inode = v9fs_get_inode_from_fid(v9ses, fid, sb, true);
143143
if (IS_ERR(inode)) {
144144
retval = PTR_ERR(inode);
145145
goto release_sb;

0 commit comments

Comments
 (0)