aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@infradead.org>2007-07-17 07:04:33 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-17 13:23:06 -0400
commit019ab801cf32381b90cbe0144cc5695aed0e408c (patch)
treecbd8de21b26bc716bd4bb4d70014193e47490310
parentdd90b50906db2c03e236e046f2fc7f7290efe4b4 (diff)
knfsd: exportfs: split out reconnecting a dentry from find_exported_dentry
There's a clear subfunctionality of reconnecting a given dentry to the main dentry tree in find_exported_dentry, that can be called both for the dentry we're looking for or it's parent directory. This patch splits the subfunctionality out into a separate helper to make the code more readable and document it's intent. As a nice side-optimization we can avoid getting a superfluous dentry reference count in the case we need to reconnect a directory on it's own. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Neil Brown <neilb@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/exportfs/expfs.c213
1 files changed, 113 insertions, 100 deletions
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index 166d8ca9eaea..8adb32a9387a 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -91,101 +91,27 @@ find_disconnected_root(struct dentry *dentry)
91 return dentry; 91 return dentry;
92} 92}
93 93
94/** 94
95 * find_exported_dentry - helper routine to implement export_operations->decode_fh 95/*
96 * @sb: The &super_block identifying the filesystem 96 * Make sure target_dir is fully connected to the dentry tree.
97 * @obj: An opaque identifier of the object to be found - passed to
98 * get_inode
99 * @parent: An optional opqaue identifier of the parent of the object.
100 * @acceptable: A function used to test possible &dentries to see if they are
101 * acceptable
102 * @context: A parameter to @acceptable so that it knows on what basis to
103 * judge.
104 *
105 * find_exported_dentry is the central helper routine to enable file systems
106 * to provide the decode_fh() export_operation. It's main task is to take
107 * an &inode, find or create an appropriate &dentry structure, and possibly
108 * splice this into the dcache in the correct place.
109 *
110 * The decode_fh() operation provided by the filesystem should call
111 * find_exported_dentry() with the same parameters that it received except
112 * that instead of the file handle fragment, pointers to opaque identifiers
113 * for the object and optionally its parent are passed. The default decode_fh
114 * routine passes one pointer to the start of the filehandle fragment, and
115 * one 8 bytes into the fragment. It is expected that most filesystems will
116 * take this approach, though the offset to the parent identifier may well be
117 * different.
118 * 97 *
119 * find_exported_dentry() will call get_dentry to get an dentry pointer from 98 * It may already be, as the flag isn't always updated when connection happens.
120 * the file system. If any &dentry in the d_alias list is acceptable, it will
121 * be returned. Otherwise find_exported_dentry() will attempt to splice a new
122 * &dentry into the dcache using get_name() and get_parent() to find the
123 * appropriate place.
124 */ 99 */
125 100static int
126struct dentry * 101reconnect_path(struct super_block *sb, struct dentry *target_dir)
127find_exported_dentry(struct super_block *sb, void *obj, void *parent,
128 int (*acceptable)(void *context, struct dentry *de),
129 void *context)
130{ 102{
131 struct dentry *result = NULL;
132 struct dentry *target_dir;
133 int err = -ESTALE;
134 struct export_operations *nops = sb->s_export_op;
135 struct dentry *alias;
136 int noprogress;
137 char nbuf[NAME_MAX+1]; 103 char nbuf[NAME_MAX+1];
104 int noprogress = 0;
105 int err = -ESTALE;
138 106
139 /* 107 /*
140 * Attempt to find the inode. 108 * It is possible that a confused file system might not let us complete
141 */
142 result = exportfs_get_dentry(sb, obj);
143 if (IS_ERR(result))
144 return result;
145
146 if (S_ISDIR(result->d_inode->i_mode)) {
147 if (!(result->d_flags & DCACHE_DISCONNECTED)) {
148 if (acceptable(context, result))
149 return result;
150 err = -EACCES;
151 goto err_result;
152 }
153
154 target_dir = dget(result);
155 } else {
156 alias = find_acceptable_alias(result, acceptable, context);
157 if (alias)
158 return alias;
159
160 if (parent == NULL)
161 goto err_result;
162
163 target_dir = exportfs_get_dentry(sb,parent);
164 if (IS_ERR(target_dir)) {
165 err = PTR_ERR(target_dir);
166 goto err_result;
167 }
168 }
169
170 /*
171 * Now we need to make sure that target_dir is properly connected.
172 * It may already be, as the flag isn't always updated when connection
173 * happens.
174 * So, we walk up parent links until we find a connected directory,
175 * or we run out of directories. Then we find the parent, find
176 * the name of the child in that parent, and do a lookup.
177 * This should connect the child into the parent
178 * We then repeat.
179 */
180
181 /* it is possible that a confused file system might not let us complete
182 * the path to the root. For example, if get_parent returns a directory 109 * the path to the root. For example, if get_parent returns a directory
183 * in which we cannot find a name for the child. While this implies a 110 * in which we cannot find a name for the child. While this implies a
184 * very sick filesystem we don't want it to cause knfsd to spin. Hence 111 * very sick filesystem we don't want it to cause knfsd to spin. Hence
185 * the noprogress counter. If we go through the loop 10 times (2 is 112 * the noprogress counter. If we go through the loop 10 times (2 is
186 * probably enough) without getting anywhere, we just give up 113 * probably enough) without getting anywhere, we just give up
187 */ 114 */
188 noprogress = 0;
189 while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) { 115 while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) {
190 struct dentry *pd = find_disconnected_root(target_dir); 116 struct dentry *pd = find_disconnected_root(target_dir);
191 117
@@ -221,18 +147,20 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
221 struct dentry *npd; 147 struct dentry *npd;
222 148
223 mutex_lock(&pd->d_inode->i_mutex); 149 mutex_lock(&pd->d_inode->i_mutex);
224 if (nops->get_parent) 150 if (sb->s_export_op->get_parent)
225 ppd = nops->get_parent(pd); 151 ppd = sb->s_export_op->get_parent(pd);
226 mutex_unlock(&pd->d_inode->i_mutex); 152 mutex_unlock(&pd->d_inode->i_mutex);
227 153
228 if (IS_ERR(ppd)) { 154 if (IS_ERR(ppd)) {
229 err = PTR_ERR(ppd); 155 err = PTR_ERR(ppd);
230 dprintk("find_exported_dentry: get_parent of %ld failed, err %d\n", 156 dprintk("%s: get_parent of %ld failed, err %d\n",
231 pd->d_inode->i_ino, err); 157 __FUNCTION__, pd->d_inode->i_ino, err);
232 dput(pd); 158 dput(pd);
233 break; 159 break;
234 } 160 }
235 dprintk("find_exported_dentry: find name of %lu in %lu\n", pd->d_inode->i_ino, ppd->d_inode->i_ino); 161
162 dprintk("%s: find name of %lu in %lu\n", __FUNCTION__,
163 pd->d_inode->i_ino, ppd->d_inode->i_ino);
236 err = exportfs_get_name(ppd, nbuf, pd); 164 err = exportfs_get_name(ppd, nbuf, pd);
237 if (err) { 165 if (err) {
238 dput(ppd); 166 dput(ppd);
@@ -244,13 +172,14 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
244 continue; 172 continue;
245 break; 173 break;
246 } 174 }
247 dprintk("find_exported_dentry: found name: %s\n", nbuf); 175 dprintk("%s: found name: %s\n", __FUNCTION__, nbuf);
248 mutex_lock(&ppd->d_inode->i_mutex); 176 mutex_lock(&ppd->d_inode->i_mutex);
249 npd = lookup_one_len(nbuf, ppd, strlen(nbuf)); 177 npd = lookup_one_len(nbuf, ppd, strlen(nbuf));
250 mutex_unlock(&ppd->d_inode->i_mutex); 178 mutex_unlock(&ppd->d_inode->i_mutex);
251 if (IS_ERR(npd)) { 179 if (IS_ERR(npd)) {
252 err = PTR_ERR(npd); 180 err = PTR_ERR(npd);
253 dprintk("find_exported_dentry: lookup failed: %d\n", err); 181 dprintk("%s: lookup failed: %d\n",
182 __FUNCTION__, err);
254 dput(ppd); 183 dput(ppd);
255 dput(pd); 184 dput(pd);
256 break; 185 break;
@@ -263,7 +192,7 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
263 if (npd == pd) 192 if (npd == pd)
264 noprogress = 0; 193 noprogress = 0;
265 else 194 else
266 printk("find_exported_dentry: npd != pd\n"); 195 printk("%s: npd != pd\n", __FUNCTION__);
267 dput(npd); 196 dput(npd);
268 dput(ppd); 197 dput(ppd);
269 if (IS_ROOT(pd)) { 198 if (IS_ROOT(pd)) {
@@ -279,15 +208,101 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
279 /* something went wrong - oh-well */ 208 /* something went wrong - oh-well */
280 if (!err) 209 if (!err)
281 err = -ESTALE; 210 err = -ESTALE;
282 goto err_target; 211 return err;
283 } 212 }
284 /* if we weren't after a directory, have one more step to go */ 213
285 if (result != target_dir) { 214 return 0;
286 struct dentry *nresult; 215}
216
217/**
218 * find_exported_dentry - helper routine to implement export_operations->decode_fh
219 * @sb: The &super_block identifying the filesystem
220 * @obj: An opaque identifier of the object to be found - passed to
221 * get_inode
222 * @parent: An optional opqaue identifier of the parent of the object.
223 * @acceptable: A function used to test possible &dentries to see if they are
224 * acceptable
225 * @context: A parameter to @acceptable so that it knows on what basis to
226 * judge.
227 *
228 * find_exported_dentry is the central helper routine to enable file systems
229 * to provide the decode_fh() export_operation. It's main task is to take
230 * an &inode, find or create an appropriate &dentry structure, and possibly
231 * splice this into the dcache in the correct place.
232 *
233 * The decode_fh() operation provided by the filesystem should call
234 * find_exported_dentry() with the same parameters that it received except
235 * that instead of the file handle fragment, pointers to opaque identifiers
236 * for the object and optionally its parent are passed. The default decode_fh
237 * routine passes one pointer to the start of the filehandle fragment, and
238 * one 8 bytes into the fragment. It is expected that most filesystems will
239 * take this approach, though the offset to the parent identifier may well be
240 * different.
241 *
242 * find_exported_dentry() will call get_dentry to get an dentry pointer from
243 * the file system. If any &dentry in the d_alias list is acceptable, it will
244 * be returned. Otherwise find_exported_dentry() will attempt to splice a new
245 * &dentry into the dcache using get_name() and get_parent() to find the
246 * appropriate place.
247 */
248
249struct dentry *
250find_exported_dentry(struct super_block *sb, void *obj, void *parent,
251 int (*acceptable)(void *context, struct dentry *de),
252 void *context)
253{
254 struct dentry *result, *alias;
255 int err = -ESTALE;
256
257 /*
258 * Attempt to find the inode.
259 */
260 result = exportfs_get_dentry(sb, obj);
261 if (IS_ERR(result))
262 return result;
263
264 if (S_ISDIR(result->d_inode->i_mode)) {
265 if (!(result->d_flags & DCACHE_DISCONNECTED)) {
266 if (acceptable(context, result))
267 return result;
268 err = -EACCES;
269 goto err_result;
270 }
271
272 err = reconnect_path(sb, result);
273 if (err)
274 goto err_result;
275 } else {
276 struct dentry *target_dir, *nresult;
277 char nbuf[NAME_MAX+1];
278
279 alias = find_acceptable_alias(result, acceptable, context);
280 if (alias)
281 return alias;
282
283 if (parent == NULL)
284 goto err_result;
285
286 target_dir = exportfs_get_dentry(sb,parent);
287 if (IS_ERR(target_dir)) {
288 err = PTR_ERR(target_dir);
289 goto err_result;
290 }
291
292 err = reconnect_path(sb, target_dir);
293 if (err) {
294 dput(target_dir);
295 goto err_result;
296 }
297
298 /*
299 * As we weren't after a directory, have one more step to go.
300 */
287 err = exportfs_get_name(target_dir, nbuf, result); 301 err = exportfs_get_name(target_dir, nbuf, result);
288 if (!err) { 302 if (!err) {
289 mutex_lock(&target_dir->d_inode->i_mutex); 303 mutex_lock(&target_dir->d_inode->i_mutex);
290 nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf)); 304 nresult = lookup_one_len(nbuf, target_dir,
305 strlen(nbuf));
291 mutex_unlock(&target_dir->d_inode->i_mutex); 306 mutex_unlock(&target_dir->d_inode->i_mutex);
292 if (!IS_ERR(nresult)) { 307 if (!IS_ERR(nresult)) {
293 if (nresult->d_inode) { 308 if (nresult->d_inode) {
@@ -297,8 +312,8 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
297 dput(nresult); 312 dput(nresult);
298 } 313 }
299 } 314 }
315 dput(target_dir);
300 } 316 }
301 dput(target_dir);
302 317
303 alias = find_acceptable_alias(result, acceptable, context); 318 alias = find_acceptable_alias(result, acceptable, context);
304 if (alias) 319 if (alias)
@@ -308,13 +323,11 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
308 dput(result); 323 dput(result);
309 /* It might be justifiable to return ESTALE here, 324 /* It might be justifiable to return ESTALE here,
310 * but the filehandle at-least looks reasonable good 325 * but the filehandle at-least looks reasonable good
311 * and it just be a permission problem, so returning 326 * and it may just be a permission problem, so returning
312 * -EACCESS is safer 327 * -EACCESS is safer
313 */ 328 */
314 return ERR_PTR(-EACCES); 329 return ERR_PTR(-EACCES);
315 330
316 err_target:
317 dput(target_dir);
318 err_result: 331 err_result:
319 dput(result); 332 dput(result);
320 return ERR_PTR(err); 333 return ERR_PTR(err);