aboutsummaryrefslogtreecommitdiffstats
path: root/fs/exportfs/expfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/exportfs/expfs.c')
-rw-r--r--fs/exportfs/expfs.c164
1 files changed, 86 insertions, 78 deletions
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index d8ba88ac10e5..d32ead9026f0 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -126,6 +126,86 @@ static void clear_disconnected(struct dentry *dentry)
126} 126}
127 127
128/* 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 */
140static 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
185out_err:
186 dput(parent);
187 return ERR_PTR(err);
188out_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
208/*
129 * Make sure target_dir is fully connected to the dentry tree. 209 * Make sure target_dir is fully connected to the dentry tree.
130 * 210 *
131 * On successful return, DCACHE_DISCONNECTED will be cleared on 211 * On successful return, DCACHE_DISCONNECTED will be cleared on
@@ -158,76 +238,19 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
158 dput(pd); 238 dput(pd);
159 break; 239 break;
160 } else { 240 } else {
241 struct dentry *parent;
161 /* 242 /*
162 * We have hit the top of a disconnected path, try to 243 * We have hit the top of a disconnected path, try to
163 * find parent and connect. 244 * 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
221 */ 245 */
222 dput(npd); 246 parent = reconnect_one(mnt, pd, nbuf);
223 dput(ppd); 247 if (!parent)
224 if (npd != pd)
225 goto out_reconnected; 248 goto out_reconnected;
226 if (IS_ROOT(pd)) { 249 if (IS_ERR(parent)) {
227 /* something went wrong, we have to give up */ 250 err = PTR_ERR(parent);
228 dput(pd);
229 break; 251 break;
230 } 252 }
253 dput(parent);
231 } 254 }
232 dput(pd); 255 dput(pd);
233 } 256 }
@@ -241,21 +264,6 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
241 264
242 return 0; 265 return 0;
243out_reconnected: 266out_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;
259 clear_disconnected(target_dir); 267 clear_disconnected(target_dir);
260 return 0; 268 return 0;
261} 269}