History

9p: Fix refcounting during full path walks for fid lookups

Decrement the refcount of the parent dentry's fid after walking each path component during a full path walk for a lookup. Failure to do so can lead to fids that are not clunked until the filesystem is unmounted, as indicated by this warning: 9pnet: found fid 3 not clunked The improper refcounting after walking resulted in open(2) returning -EIO on any directories underneath the mount point when using the virtio transport. When using the fd transport, there's no apparent issue until the filesytem is unmounted and the warning above is emitted to the logs. In some cases, the user may not yet be attached to the filesystem and a new root fid, associated with the user, is created and attached to the root dentry before the full path walk is performed. Increment the new root fid's refcount to two in that situation so that it can be safely decremented to one after it is used for the walk operation. The new fid will still be attached to the root dentry when v9fs_fid_lookup_with_uid() returns so a final refcount of one is correct/expected.

Tyler Hicks committed 4 years ago

Commit 2a3dcbc1 parent e5690f21 file · +9 −13Browse files

Unified · Split

fs/9p/fid.c+9−13
@@ -152,7 +152,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, · 151 unmodified lines
152 const unsigned char **wnames, *uname;152 const unsigned char **wnames, *uname;
153 int i, n, l, clone, access;153 int i, n, l, clone, access;
154 struct v9fs_session_info *v9ses;154 struct v9fs_session_info *v9ses;
155 struct p9_fid *fid, *old_fid = NULL;155 struct p9_fid *fid, *old_fid;
156 156
157 v9ses = v9fs_dentry2v9ses(dentry);157 v9ses = v9fs_dentry2v9ses(dentry);
158 access = v9ses->flags & V9FS_ACCESS_MASK;158 access = v9ses->flags & V9FS_ACCESS_MASK;
@@ -194,13 +194,12 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, · 35 unmodified lines
194 if (IS_ERR(fid))194 if (IS_ERR(fid))
195 return fid;195 return fid;
196 196
197 refcount_inc(&fid->count);
197 v9fs_fid_add(dentry->d_sb->s_root, fid);198 v9fs_fid_add(dentry->d_sb->s_root, fid);
198 }199 }
199 /* If we are root ourself just return that */200 /* If we are root ourself just return that */
200 if (dentry->d_sb->s_root == dentry) {201 if (dentry->d_sb->s_root == dentry)
201 refcount_inc(&fid->count);
202 return fid;202 return fid;
203 }
204 /*203 /*
205 * Do a multipath walk with attached root.204 * Do a multipath walk with attached root.
206 * When walking parent we need to make sure we205 * When walking parent we need to make sure we
@@ -212,6 +211,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, · 5 unmodified lines
212 fid = ERR_PTR(n);211 fid = ERR_PTR(n);
213 goto err_out;212 goto err_out;
214 }213 }
214 old_fid = fid;
215 clone = 1;215 clone = 1;
216 i = 0;216 i = 0;
217 while (i < n) {217 while (i < n) {
@@ -221,19 +221,15 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, · 3 unmodified lines
221 * walk to ensure none of the patch component change221 * walk to ensure none of the patch component change
222 */222 */
223 fid = p9_client_walk(fid, l, &wnames[i], clone);223 fid = p9_client_walk(fid, l, &wnames[i], clone);
224 /* non-cloning walk will return the same fid */
225 if (fid != old_fid) {
226 p9_client_clunk(old_fid);
227 old_fid = fid;
228 }
224 if (IS_ERR(fid)) {229 if (IS_ERR(fid)) {
225 if (old_fid) {
226 /*
227 * If we fail, clunk fid which are mapping
228 * to path component and not the last component
229 * of the path.
230 */
231 p9_client_clunk(old_fid);
232 }
233 kfree(wnames);230 kfree(wnames);
234 goto err_out;231 goto err_out;
235 }232 }
236 old_fid = fid;
237 i += l;233 i += l;
238 clone = 0;234 clone = 0;
239 }235 }