Skip to content

Commit be2ca38

Browse files
committed
Revert "fs/9p: simplify iget to remove unnecessary paths"
This reverts commit 724a084. This code simplification introduced significant regressions on servers that do not remap inode numbers when exporting multiple underlying filesystems with colliding inodes, as can be illustrated with simple tmpfs exports in qemu with remapping disabled: ``` # host side cd /tmp/linux-test mkdir m1 m2 mount -t tmpfs tmpfs m1 mount -t tmpfs tmpfs m2 mkdir m1/dir m2/dir echo foo > m1/dir/foo echo bar > m2/dir/bar # guest side # started with -virtfs local,path=/tmp/linux-test,mount_tag=tmp,security_model=mapped-file mount -t 9p -o trans=virtio,debug=1 tmp /mnt/t ls /mnt/t/m1/dir # foo ls /mnt/t/m2/dir # bar (works ok if directry isn't open) # cd to keep first dir's inode alive cd /mnt/t/m1/dir ls /mnt/t/m2/dir # foo (should be bar) ``` Other examples can be crafted with regular files with fscache enabled, in which case I/Os just happen to the wrong file leading to corruptions, or guest failing to boot with: | VFS: Lookup of 'com.android.runtime' in 9p 9p would have caused loop In theory, we'd want the servers to be smart enough and ensure they never send us two different files with the same 'qid.path', but while qemu has an option to remap that is recommended (and qemu prints a warning if this case happens), there are many other servers which do not (kvmtool, nfs-ganesha, probably diod...), we should at least ensure we don't cause regressions on this: - assume servers can't be trusted and operations that should get a 'new' inode properly do so. commit d05dcfd (" fs/9p: mitigate inode collisions") attempted to do this, but v9fs_fid_iget_dotl() was not called so some higher level of caching got in the way; this needs to be fixed properly before we can re-apply the patches. - if we ever want to really simplify this code, we will need to add some negotiation with the server at mount time where the server could claim they handle this properly, at which point we could optimize this out. (but that might not be needed at all if we properly handle the 'new' check?) Fixes: 724a084 ("fs/9p: simplify iget to remove unnecessary paths") Reported-by: Will Deacon <[email protected]> Link: https://lore.kernel.org/all/[email protected]/ Link: https://lkml.kernel.org/r/20240923100508.GA32066@willie-the-truck Cc: [email protected] # v6.9+ Message-ID: <[email protected]> Signed-off-by: Dominique Martinet <[email protected]>
1 parent 26f8dd2 commit be2ca38

File tree

5 files changed

+180
-45
lines changed

5 files changed

+180
-45
lines changed

fs/9p/v9fs.h

+26-5
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,16 @@ 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_inode_from_fid(struct v9fs_session_info *v9ses,
183+
struct p9_fid *fid,
184+
struct super_block *sb, int new);
183185
extern const struct inode_operations v9fs_dir_inode_operations_dotl;
184186
extern const struct inode_operations v9fs_file_inode_operations_dotl;
185187
extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
186188
extern const struct netfs_request_ops v9fs_req_ops;
187-
extern struct inode *v9fs_fid_iget_dotl(struct super_block *sb,
188-
struct p9_fid *fid);
189+
extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses,
190+
struct p9_fid *fid,
191+
struct super_block *sb, int new);
189192

190193
/* other default globals */
191194
#define V9FS_PORT 564
@@ -227,9 +230,27 @@ v9fs_get_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
227230
struct super_block *sb)
228231
{
229232
if (v9fs_proto_dotl(v9ses))
230-
return v9fs_fid_iget_dotl(sb, fid);
233+
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 0);
231234
else
232-
return v9fs_fid_iget(sb, fid);
235+
return v9fs_inode_from_fid(v9ses, fid, sb, 0);
236+
}
237+
238+
/**
239+
* v9fs_get_new_inode_from_fid - Helper routine to populate an inode by
240+
* issuing a attribute request
241+
* @v9ses: session information
242+
* @fid: fid to issue attribute request for
243+
* @sb: superblock on which to create inode
244+
*
245+
*/
246+
static inline struct inode *
247+
v9fs_get_new_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
248+
struct super_block *sb)
249+
{
250+
if (v9fs_proto_dotl(v9ses))
251+
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 1);
252+
else
253+
return v9fs_inode_from_fid(v9ses, fid, sb, 1);
233254
}
234255

235256
#endif

fs/9p/v9fs_vfs.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ struct inode *v9fs_alloc_inode(struct super_block *sb);
4242
void v9fs_free_inode(struct inode *inode);
4343
void v9fs_set_netfs_context(struct inode *inode);
4444
int v9fs_init_inode(struct v9fs_session_info *v9ses,
45-
struct inode *inode, struct p9_qid *qid, umode_t mode, dev_t rdev);
45+
struct inode *inode, umode_t mode, dev_t rdev);
4646
void v9fs_evict_inode(struct inode *inode);
4747
#if (BITS_PER_LONG == 32)
4848
#define QID2INO(q) ((ino_t) (((q)->path+2) ^ (((q)->path) >> 32)))

fs/9p/vfs_inode.c

+77-21
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,9 @@ void v9fs_set_netfs_context(struct inode *inode)
256256
}
257257

258258
int v9fs_init_inode(struct v9fs_session_info *v9ses,
259-
struct inode *inode, struct p9_qid *qid, umode_t mode, dev_t rdev)
259+
struct inode *inode, umode_t mode, dev_t rdev)
260260
{
261261
int err = 0;
262-
struct v9fs_inode *v9inode = V9FS_I(inode);
263-
264-
memcpy(&v9inode->qid, qid, sizeof(struct p9_qid));
265262

266263
inode_init_owner(&nop_mnt_idmap, inode, NULL, mode);
267264
inode->i_blocks = 0;
@@ -366,40 +363,80 @@ void v9fs_evict_inode(struct inode *inode)
366363
clear_inode(inode);
367364
}
368365

369-
struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid)
366+
static int v9fs_test_inode(struct inode *inode, void *data)
367+
{
368+
int umode;
369+
dev_t rdev;
370+
struct v9fs_inode *v9inode = V9FS_I(inode);
371+
struct p9_wstat *st = (struct p9_wstat *)data;
372+
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
373+
374+
umode = p9mode2unixmode(v9ses, st, &rdev);
375+
/* don't match inode of different type */
376+
if (inode_wrong_type(inode, umode))
377+
return 0;
378+
379+
/* compare qid details */
380+
if (memcmp(&v9inode->qid.version,
381+
&st->qid.version, sizeof(v9inode->qid.version)))
382+
return 0;
383+
384+
if (v9inode->qid.type != st->qid.type)
385+
return 0;
386+
387+
if (v9inode->qid.path != st->qid.path)
388+
return 0;
389+
return 1;
390+
}
391+
392+
static int v9fs_test_new_inode(struct inode *inode, void *data)
393+
{
394+
return 0;
395+
}
396+
397+
static int v9fs_set_inode(struct inode *inode, void *data)
398+
{
399+
struct v9fs_inode *v9inode = V9FS_I(inode);
400+
struct p9_wstat *st = (struct p9_wstat *)data;
401+
402+
memcpy(&v9inode->qid, &st->qid, sizeof(st->qid));
403+
return 0;
404+
}
405+
406+
static struct inode *v9fs_qid_iget(struct super_block *sb,
407+
struct p9_qid *qid,
408+
struct p9_wstat *st,
409+
int new)
370410
{
371411
dev_t rdev;
372412
int retval;
373413
umode_t umode;
374414
struct inode *inode;
375-
struct p9_wstat *st;
376415
struct v9fs_session_info *v9ses = sb->s_fs_info;
416+
int (*test)(struct inode *inode, void *data);
417+
418+
if (new)
419+
test = v9fs_test_new_inode;
420+
else
421+
test = v9fs_test_inode;
377422

378-
inode = iget_locked(sb, QID2INO(&fid->qid));
379-
if (unlikely(!inode))
423+
inode = iget5_locked(sb, QID2INO(qid), test, v9fs_set_inode, st);
424+
if (!inode)
380425
return ERR_PTR(-ENOMEM);
381426
if (!(inode->i_state & I_NEW))
382427
return inode;
383-
384428
/*
385429
* initialize the inode with the stat info
386430
* FIXME!! we may need support for stale inodes
387431
* later.
388432
*/
389-
st = p9_client_stat(fid);
390-
if (IS_ERR(st)) {
391-
retval = PTR_ERR(st);
392-
goto error;
393-
}
394-
433+
inode->i_ino = QID2INO(qid);
395434
umode = p9mode2unixmode(v9ses, st, &rdev);
396-
retval = v9fs_init_inode(v9ses, inode, &fid->qid, umode, rdev);
397-
v9fs_stat2inode(st, inode, sb, 0);
398-
p9stat_free(st);
399-
kfree(st);
435+
retval = v9fs_init_inode(v9ses, inode, umode, rdev);
400436
if (retval)
401437
goto error;
402438

439+
v9fs_stat2inode(st, inode, sb, 0);
403440
v9fs_set_netfs_context(inode);
404441
v9fs_cache_inode_get_cookie(inode);
405442
unlock_new_inode(inode);
@@ -410,6 +447,23 @@ struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid)
410447

411448
}
412449

450+
struct inode *
451+
v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
452+
struct super_block *sb, int new)
453+
{
454+
struct p9_wstat *st;
455+
struct inode *inode = NULL;
456+
457+
st = p9_client_stat(fid);
458+
if (IS_ERR(st))
459+
return ERR_CAST(st);
460+
461+
inode = v9fs_qid_iget(sb, &st->qid, st, new);
462+
p9stat_free(st);
463+
kfree(st);
464+
return inode;
465+
}
466+
413467
/**
414468
* v9fs_at_to_dotl_flags- convert Linux specific AT flags to
415469
* plan 9 AT flag.
@@ -556,7 +610,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
556610
/*
557611
* instantiate inode and assign the unopened fid to the dentry
558612
*/
559-
inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
613+
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
560614
if (IS_ERR(inode)) {
561615
err = PTR_ERR(inode);
562616
p9_debug(P9_DEBUG_VFS,
@@ -684,8 +738,10 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
684738
inode = NULL;
685739
else if (IS_ERR(fid))
686740
inode = ERR_CAST(fid);
687-
else
741+
else if (v9ses->cache & (CACHE_META|CACHE_LOOSE))
688742
inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
743+
else
744+
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
689745
/*
690746
* If we had a rename on the server and a parallel lookup
691747
* for the new name, then make sure we instantiate with

fs/9p/vfs_inode_dotl.c

+75-17
Original file line numberDiff line numberDiff line change
@@ -52,33 +52,76 @@ 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+
static int v9fs_test_inode_dotl(struct inode *inode, void *data)
56+
{
57+
struct v9fs_inode *v9inode = V9FS_I(inode);
58+
struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
59+
60+
/* don't match inode of different type */
61+
if (inode_wrong_type(inode, st->st_mode))
62+
return 0;
63+
64+
if (inode->i_generation != st->st_gen)
65+
return 0;
66+
67+
/* compare qid details */
68+
if (memcmp(&v9inode->qid.version,
69+
&st->qid.version, sizeof(v9inode->qid.version)))
70+
return 0;
71+
72+
if (v9inode->qid.type != st->qid.type)
73+
return 0;
74+
75+
if (v9inode->qid.path != st->qid.path)
76+
return 0;
77+
return 1;
78+
}
79+
80+
/* Always get a new inode */
81+
static int v9fs_test_new_inode_dotl(struct inode *inode, void *data)
82+
{
83+
return 0;
84+
}
85+
86+
static int v9fs_set_inode_dotl(struct inode *inode, void *data)
87+
{
88+
struct v9fs_inode *v9inode = V9FS_I(inode);
89+
struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
90+
91+
memcpy(&v9inode->qid, &st->qid, sizeof(st->qid));
92+
inode->i_generation = st->st_gen;
93+
return 0;
94+
}
95+
96+
static struct inode *v9fs_qid_iget_dotl(struct super_block *sb,
97+
struct p9_qid *qid,
98+
struct p9_fid *fid,
99+
struct p9_stat_dotl *st,
100+
int new)
56101
{
57102
int retval;
58103
struct inode *inode;
59-
struct p9_stat_dotl *st;
60104
struct v9fs_session_info *v9ses = sb->s_fs_info;
105+
int (*test)(struct inode *inode, void *data);
106+
107+
if (new)
108+
test = v9fs_test_new_inode_dotl;
109+
else
110+
test = v9fs_test_inode_dotl;
61111

62-
inode = iget_locked(sb, QID2INO(&fid->qid));
63-
if (unlikely(!inode))
112+
inode = iget5_locked(sb, QID2INO(qid), test, v9fs_set_inode_dotl, st);
113+
if (!inode)
64114
return ERR_PTR(-ENOMEM);
65115
if (!(inode->i_state & I_NEW))
66116
return inode;
67-
68117
/*
69118
* initialize the inode with the stat info
70119
* FIXME!! we may need support for stale inodes
71120
* later.
72121
*/
73-
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC | P9_STATS_GEN);
74-
if (IS_ERR(st)) {
75-
retval = PTR_ERR(st);
76-
goto error;
77-
}
78-
79-
retval = v9fs_init_inode(v9ses, inode, &fid->qid,
122+
inode->i_ino = QID2INO(qid);
123+
retval = v9fs_init_inode(v9ses, inode,
80124
st->st_mode, new_decode_dev(st->st_rdev));
81-
kfree(st);
82125
if (retval)
83126
goto error;
84127

@@ -90,14 +133,29 @@ struct inode *v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid)
90133
goto error;
91134

92135
unlock_new_inode(inode);
93-
94136
return inode;
95137
error:
96138
iget_failed(inode);
97139
return ERR_PTR(retval);
98140

99141
}
100142

143+
struct inode *
144+
v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid,
145+
struct super_block *sb, int new)
146+
{
147+
struct p9_stat_dotl *st;
148+
struct inode *inode = NULL;
149+
150+
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC | P9_STATS_GEN);
151+
if (IS_ERR(st))
152+
return ERR_CAST(st);
153+
154+
inode = v9fs_qid_iget_dotl(sb, &st->qid, fid, st, new);
155+
kfree(st);
156+
return inode;
157+
}
158+
101159
struct dotl_openflag_map {
102160
int open_flag;
103161
int dotl_flag;
@@ -247,7 +305,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
247305
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
248306
goto out;
249307
}
250-
inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
308+
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
251309
if (IS_ERR(inode)) {
252310
err = PTR_ERR(inode);
253311
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", err);
@@ -342,7 +400,7 @@ static int v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap,
342400
}
343401

344402
/* instantiate inode and assign the unopened fid to the dentry */
345-
inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
403+
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
346404
if (IS_ERR(inode)) {
347405
err = PTR_ERR(inode);
348406
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
@@ -780,7 +838,7 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct inode *dir,
780838
err);
781839
goto error;
782840
}
783-
inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
841+
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
784842
if (IS_ERR(inode)) {
785843
err = PTR_ERR(inode);
786844
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",

fs/9p/vfs_super.c

+1-1
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_new_inode_from_fid(v9ses, fid, sb);
143143
if (IS_ERR(inode)) {
144144
retval = PTR_ERR(inode);
145145
goto release_sb;

0 commit comments

Comments
 (0)