Skip to content

Commit bbf7a8a

Browse files
J. Bruce FieldsAl Viro
authored andcommitted
exportfs: move most of reconnect_path to helper function
Also replace 3 easily-confused three-letter acronyms by more helpful variable names. Just cleanup, no change in functionality, with one exception: the dentry_connected() check in the "out_reconnected" case will now only check the ancestors of the current dentry instead of checking all the way from target_dir. Since we've already verified connectivity up to this dentry, that should be sufficient. Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: J. Bruce Fields <bfields@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent e4b70eb commit bbf7a8a

File tree

1 file changed

+86
-78
lines changed

1 file changed

+86
-78
lines changed

fs/exportfs/expfs.c

Lines changed: 86 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,86 @@ static void clear_disconnected(struct dentry *dentry)
125125
dput(dentry);
126126
}
127127

128+
/*
129+
* Reconnect a directory dentry with its parent.
130+
*
131+
* This can return a dentry, or NULL, or an error.
132+
*
133+
* In the first case the returned dentry is the parent of the given
134+
* dentry, and may itself need to be reconnected to its parent.
135+
*
136+
* In the NULL case, a concurrent VFS operation has either renamed or
137+
* removed this directory. The concurrent operation has reconnected our
138+
* dentry, so we no longer need to.
139+
*/
140+
static struct dentry *reconnect_one(struct vfsmount *mnt,
141+
struct dentry *dentry, char *nbuf)
142+
{
143+
struct dentry *parent;
144+
struct dentry *tmp;
145+
int err;
146+
147+
parent = ERR_PTR(-EACCES);
148+
mutex_lock(&dentry->d_inode->i_mutex);
149+
if (mnt->mnt_sb->s_export_op->get_parent)
150+
parent = mnt->mnt_sb->s_export_op->get_parent(dentry);
151+
mutex_unlock(&dentry->d_inode->i_mutex);
152+
153+
if (IS_ERR(parent)) {
154+
dprintk("%s: get_parent of %ld failed, err %d\n",
155+
__func__, dentry->d_inode->i_ino, PTR_ERR(parent));
156+
return parent;
157+
}
158+
159+
dprintk("%s: find name of %lu in %lu\n", __func__,
160+
dentry->d_inode->i_ino, parent->d_inode->i_ino);
161+
err = exportfs_get_name(mnt, parent, nbuf, dentry);
162+
if (err == -ENOENT)
163+
goto out_reconnected;
164+
if (err)
165+
goto out_err;
166+
dprintk("%s: found name: %s\n", __func__, nbuf);
167+
mutex_lock(&parent->d_inode->i_mutex);
168+
tmp = lookup_one_len(nbuf, parent, strlen(nbuf));
169+
mutex_unlock(&parent->d_inode->i_mutex);
170+
if (IS_ERR(tmp)) {
171+
dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp));
172+
goto out_err;
173+
}
174+
if (tmp != dentry) {
175+
dput(tmp);
176+
goto out_reconnected;
177+
}
178+
dput(tmp);
179+
if (IS_ROOT(dentry)) {
180+
err = -ESTALE;
181+
goto out_err;
182+
}
183+
return parent;
184+
185+
out_err:
186+
dput(parent);
187+
return ERR_PTR(err);
188+
out_reconnected:
189+
dput(parent);
190+
/*
191+
* Someone must have renamed our entry into another parent, in
192+
* which case it has been reconnected by the rename.
193+
*
194+
* Or someone removed it entirely, in which case filehandle
195+
* lookup will succeed but the directory is now IS_DEAD and
196+
* subsequent operations on it will fail.
197+
*
198+
* Alternatively, maybe there was no race at all, and the
199+
* filesystem is just corrupt and gave us a parent that doesn't
200+
* actually contain any entry pointing to this inode. So,
201+
* double check that this worked and return -ESTALE if not:
202+
*/
203+
if (!dentry_connected(dentry))
204+
return ERR_PTR(-ESTALE);
205+
return NULL;
206+
}
207+
128208
/*
129209
* Make sure target_dir is fully connected to the dentry tree.
130210
*
@@ -158,76 +238,19 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
158238
dput(pd);
159239
break;
160240
} else {
241+
struct dentry *parent;
161242
/*
162243
* We have hit the top of a disconnected path, try to
163244
* find parent and connect.
164-
*
165-
* Racing with some other process renaming a directory
166-
* isn't much of a problem here. If someone renames
167-
* the directory, it will end up properly connected,
168-
* which is what we want
169-
*
170-
* Getting the parent can't be supported generically,
171-
* the locking is too icky.
172-
*
173-
* Instead we just return EACCES. If server reboots
174-
* or inodes get flushed, you lose
175-
*/
176-
struct dentry *ppd = ERR_PTR(-EACCES);
177-
struct dentry *npd;
178-
179-
mutex_lock(&pd->d_inode->i_mutex);
180-
if (mnt->mnt_sb->s_export_op->get_parent)
181-
ppd = mnt->mnt_sb->s_export_op->get_parent(pd);
182-
mutex_unlock(&pd->d_inode->i_mutex);
183-
184-
if (IS_ERR(ppd)) {
185-
err = PTR_ERR(ppd);
186-
dprintk("%s: get_parent of %ld failed, err %d\n",
187-
__func__, pd->d_inode->i_ino, err);
188-
dput(pd);
189-
break;
190-
}
191-
192-
dprintk("%s: find name of %lu in %lu\n", __func__,
193-
pd->d_inode->i_ino, ppd->d_inode->i_ino);
194-
err = exportfs_get_name(mnt, ppd, nbuf, pd);
195-
if (err) {
196-
dput(ppd);
197-
dput(pd);
198-
if (err == -ENOENT)
199-
/* some race between get_parent and
200-
* get_name?
201-
*/
202-
goto out_reconnected;
203-
break;
204-
}
205-
dprintk("%s: found name: %s\n", __func__, nbuf);
206-
mutex_lock(&ppd->d_inode->i_mutex);
207-
npd = lookup_one_len(nbuf, ppd, strlen(nbuf));
208-
mutex_unlock(&ppd->d_inode->i_mutex);
209-
if (IS_ERR(npd)) {
210-
err = PTR_ERR(npd);
211-
dprintk("%s: lookup failed: %d\n",
212-
__func__, err);
213-
dput(ppd);
214-
dput(pd);
215-
break;
216-
}
217-
/* we didn't really want npd, we really wanted
218-
* a side-effect of the lookup.
219-
* hopefully, npd == pd, though it isn't really
220-
* a problem if it isn't
221245
*/
222-
dput(npd);
223-
dput(ppd);
224-
if (npd != pd)
246+
parent = reconnect_one(mnt, pd, nbuf);
247+
if (!parent)
225248
goto out_reconnected;
226-
if (IS_ROOT(pd)) {
227-
/* something went wrong, we have to give up */
228-
dput(pd);
249+
if (IS_ERR(parent)) {
250+
err = PTR_ERR(parent);
229251
break;
230252
}
253+
dput(parent);
231254
}
232255
dput(pd);
233256
}
@@ -241,21 +264,6 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
241264

242265
return 0;
243266
out_reconnected:
244-
/*
245-
* Someone must have renamed our entry into another parent, in
246-
* which case it has been reconnected by the rename.
247-
*
248-
* Or someone removed it entirely, in which case filehandle
249-
* lookup will succeed but the directory is now IS_DEAD and
250-
* subsequent operations on it will fail.
251-
*
252-
* Alternatively, maybe there was no race at all, and the
253-
* filesystem is just corrupt and gave us a parent that doesn't
254-
* actually contain any entry pointing to this inode. So,
255-
* double check that this worked and return -ESTALE if not:
256-
*/
257-
if (!dentry_connected(target_dir))
258-
return -ESTALE;
259267
clear_disconnected(target_dir);
260268
return 0;
261269
}

0 commit comments

Comments
 (0)