aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/exportfs/expfs.c45
1 files changed, 40 insertions, 5 deletions
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index c65b748688ff..6b5ddd5492bc 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -90,6 +90,23 @@ find_disconnected_root(struct dentry *dentry)
90 return dentry; 90 return dentry;
91} 91}
92 92
93static bool dentry_connected(struct dentry *dentry)
94{
95 dget(dentry);
96 while (dentry->d_flags & DCACHE_DISCONNECTED) {
97 struct dentry *parent = dget_parent(dentry);
98
99 dput(dentry);
100 if (IS_ROOT(dentry)) {
101 dput(parent);
102 return false;
103 }
104 dentry = parent;
105 }
106 dput(dentry);
107 return true;
108}
109
93static void clear_disconnected(struct dentry *dentry) 110static void clear_disconnected(struct dentry *dentry)
94{ 111{
95 dget(dentry); 112 dget(dentry);
@@ -189,9 +206,9 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
189 dput(pd); 206 dput(pd);
190 if (err == -ENOENT) 207 if (err == -ENOENT)
191 /* some race between get_parent and 208 /* some race between get_parent and
192 * get_name? just try again 209 * get_name?
193 */ 210 */
194 continue; 211 goto out_reconnected;
195 break; 212 break;
196 } 213 }
197 dprintk("%s: found name: %s\n", __func__, nbuf); 214 dprintk("%s: found name: %s\n", __func__, nbuf);
@@ -211,12 +228,12 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
211 * hopefully, npd == pd, though it isn't really 228 * hopefully, npd == pd, though it isn't really
212 * a problem if it isn't 229 * a problem if it isn't
213 */ 230 */
231 dput(npd);
232 dput(ppd);
214 if (npd == pd) 233 if (npd == pd)
215 noprogress = 0; 234 noprogress = 0;
216 else 235 else
217 printk("%s: npd != pd\n", __func__); 236 goto out_reconnected;
218 dput(npd);
219 dput(ppd);
220 if (IS_ROOT(pd)) { 237 if (IS_ROOT(pd)) {
221 /* something went wrong, we have to give up */ 238 /* something went wrong, we have to give up */
222 dput(pd); 239 dput(pd);
@@ -234,6 +251,24 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
234 } 251 }
235 252
236 return 0; 253 return 0;
254out_reconnected:
255 /*
256 * Someone must have renamed our entry into another parent, in
257 * which case it has been reconnected by the rename.
258 *
259 * Or someone removed it entirely, in which case filehandle
260 * lookup will succeed but the directory is now IS_DEAD and
261 * subsequent operations on it will fail.
262 *
263 * Alternatively, maybe there was no race at all, and the
264 * filesystem is just corrupt and gave us a parent that doesn't
265 * actually contain any entry pointing to this inode. So,
266 * double check that this worked and return -ESTALE if not:
267 */
268 if (!dentry_connected(target_dir))
269 return -ESTALE;
270 clear_disconnected(target_dir);
271 return 0;
237} 272}
238 273
239struct getdents_callback { 274struct getdents_callback {