diff options
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 131 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 6 | ||||
-rw-r--r-- | fs/cifs/dir.c | 2 | ||||
-rw-r--r-- | fs/cifs/inode.c | 8 |
4 files changed, 80 insertions, 67 deletions
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index 83479cf63f96..0fc163808de3 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c | |||
@@ -255,32 +255,6 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb, | |||
255 | 255 | ||
256 | } | 256 | } |
257 | 257 | ||
258 | static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, | ||
259 | struct list_head *mntlist) | ||
260 | { | ||
261 | /* stolen from afs code */ | ||
262 | int err; | ||
263 | |||
264 | mntget(newmnt); | ||
265 | err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags | MNT_SHRINKABLE, mntlist); | ||
266 | switch (err) { | ||
267 | case 0: | ||
268 | path_put(&nd->path); | ||
269 | nd->path.mnt = newmnt; | ||
270 | nd->path.dentry = dget(newmnt->mnt_root); | ||
271 | schedule_delayed_work(&cifs_dfs_automount_task, | ||
272 | cifs_dfs_mountpoint_expiry_timeout); | ||
273 | break; | ||
274 | case -EBUSY: | ||
275 | /* someone else made a mount here whilst we were busy */ | ||
276 | err = follow_down(&nd->path, false); | ||
277 | default: | ||
278 | mntput(newmnt); | ||
279 | break; | ||
280 | } | ||
281 | return err; | ||
282 | } | ||
283 | |||
284 | static void dump_referral(const struct dfs_info3_param *ref) | 258 | static void dump_referral(const struct dfs_info3_param *ref) |
285 | { | 259 | { |
286 | cFYI(1, "DFS: ref path: %s", ref->path_name); | 260 | cFYI(1, "DFS: ref path: %s", ref->path_name); |
@@ -290,45 +264,43 @@ static void dump_referral(const struct dfs_info3_param *ref) | |||
290 | ref->path_consumed); | 264 | ref->path_consumed); |
291 | } | 265 | } |
292 | 266 | ||
293 | 267 | /* | |
294 | static void* | 268 | * Create a vfsmount that we can automount |
295 | cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | 269 | */ |
270 | static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) | ||
296 | { | 271 | { |
297 | struct dfs_info3_param *referrals = NULL; | 272 | struct dfs_info3_param *referrals = NULL; |
298 | unsigned int num_referrals = 0; | 273 | unsigned int num_referrals = 0; |
299 | struct cifs_sb_info *cifs_sb; | 274 | struct cifs_sb_info *cifs_sb; |
300 | struct cifsSesInfo *ses; | 275 | struct cifsSesInfo *ses; |
301 | char *full_path = NULL; | 276 | char *full_path; |
302 | int xid, i; | 277 | int xid, i; |
303 | int rc = 0; | 278 | int rc; |
304 | struct vfsmount *mnt = ERR_PTR(-ENOENT); | 279 | struct vfsmount *mnt; |
305 | struct tcon_link *tlink; | 280 | struct tcon_link *tlink; |
306 | 281 | ||
307 | cFYI(1, "in %s", __func__); | 282 | cFYI(1, "in %s", __func__); |
308 | BUG_ON(IS_ROOT(dentry)); | 283 | BUG_ON(IS_ROOT(mntpt)); |
309 | 284 | ||
310 | xid = GetXid(); | 285 | xid = GetXid(); |
311 | 286 | ||
312 | dput(nd->path.dentry); | ||
313 | nd->path.dentry = dget(dentry); | ||
314 | |||
315 | /* | 287 | /* |
316 | * The MSDFS spec states that paths in DFS referral requests and | 288 | * The MSDFS spec states that paths in DFS referral requests and |
317 | * responses must be prefixed by a single '\' character instead of | 289 | * responses must be prefixed by a single '\' character instead of |
318 | * the double backslashes usually used in the UNC. This function | 290 | * the double backslashes usually used in the UNC. This function |
319 | * gives us the latter, so we must adjust the result. | 291 | * gives us the latter, so we must adjust the result. |
320 | */ | 292 | */ |
321 | full_path = build_path_from_dentry(dentry); | 293 | mnt = ERR_PTR(-ENOMEM); |
322 | if (full_path == NULL) { | 294 | full_path = build_path_from_dentry(mntpt); |
323 | rc = -ENOMEM; | 295 | if (full_path == NULL) |
324 | goto out_err; | 296 | goto free_xid; |
325 | } | ||
326 | 297 | ||
327 | cifs_sb = CIFS_SB(dentry->d_inode->i_sb); | 298 | cifs_sb = CIFS_SB(mntpt->d_inode->i_sb); |
328 | tlink = cifs_sb_tlink(cifs_sb); | 299 | tlink = cifs_sb_tlink(cifs_sb); |
300 | mnt = ERR_PTR(-EINVAL); | ||
329 | if (IS_ERR(tlink)) { | 301 | if (IS_ERR(tlink)) { |
330 | rc = PTR_ERR(tlink); | 302 | mnt = ERR_CAST(tlink); |
331 | goto out_err; | 303 | goto free_full_path; |
332 | } | 304 | } |
333 | ses = tlink_tcon(tlink)->ses; | 305 | ses = tlink_tcon(tlink)->ses; |
334 | 306 | ||
@@ -338,46 +310,77 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | |||
338 | 310 | ||
339 | cifs_put_tlink(tlink); | 311 | cifs_put_tlink(tlink); |
340 | 312 | ||
313 | mnt = ERR_PTR(-ENOENT); | ||
341 | for (i = 0; i < num_referrals; i++) { | 314 | for (i = 0; i < num_referrals; i++) { |
342 | int len; | 315 | int len; |
343 | dump_referral(referrals+i); | 316 | dump_referral(referrals + i); |
344 | /* connect to a node */ | 317 | /* connect to a node */ |
345 | len = strlen(referrals[i].node_name); | 318 | len = strlen(referrals[i].node_name); |
346 | if (len < 2) { | 319 | if (len < 2) { |
347 | cERROR(1, "%s: Net Address path too short: %s", | 320 | cERROR(1, "%s: Net Address path too short: %s", |
348 | __func__, referrals[i].node_name); | 321 | __func__, referrals[i].node_name); |
349 | rc = -EINVAL; | 322 | mnt = ERR_PTR(-EINVAL); |
350 | goto out_err; | 323 | break; |
351 | } | 324 | } |
352 | mnt = cifs_dfs_do_refmount(cifs_sb, | 325 | mnt = cifs_dfs_do_refmount(cifs_sb, |
353 | full_path, referrals + i); | 326 | full_path, referrals + i); |
354 | cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, | 327 | cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, |
355 | referrals[i].node_name, mnt); | 328 | referrals[i].node_name, mnt); |
356 | |||
357 | /* complete mount procedure if we accured submount */ | ||
358 | if (!IS_ERR(mnt)) | 329 | if (!IS_ERR(mnt)) |
359 | break; | 330 | goto success; |
360 | } | 331 | } |
361 | 332 | ||
362 | /* we need it cause for() above could exit without valid submount */ | 333 | /* no valid submounts were found; return error from get_dfs_path() by |
363 | rc = PTR_ERR(mnt); | 334 | * preference */ |
364 | if (IS_ERR(mnt)) | 335 | if (rc != 0) |
365 | goto out_err; | 336 | mnt = ERR_PTR(rc); |
366 | |||
367 | rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list); | ||
368 | 337 | ||
369 | out: | 338 | success: |
370 | FreeXid(xid); | ||
371 | free_dfs_info_array(referrals, num_referrals); | 339 | free_dfs_info_array(referrals, num_referrals); |
340 | free_full_path: | ||
372 | kfree(full_path); | 341 | kfree(full_path); |
342 | free_xid: | ||
343 | FreeXid(xid); | ||
373 | cFYI(1, "leaving %s" , __func__); | 344 | cFYI(1, "leaving %s" , __func__); |
374 | return ERR_PTR(rc); | 345 | return mnt; |
375 | out_err: | 346 | } |
376 | path_put(&nd->path); | 347 | |
377 | goto out; | 348 | /* |
349 | * Attempt to automount the referral | ||
350 | */ | ||
351 | struct vfsmount *cifs_dfs_d_automount(struct path *path) | ||
352 | { | ||
353 | struct vfsmount *newmnt; | ||
354 | int err; | ||
355 | |||
356 | cFYI(1, "in %s", __func__); | ||
357 | |||
358 | newmnt = cifs_dfs_do_automount(path->dentry); | ||
359 | if (IS_ERR(newmnt)) { | ||
360 | cFYI(1, "leaving %s [automount failed]" , __func__); | ||
361 | return newmnt; | ||
362 | } | ||
363 | |||
364 | mntget(newmnt); | ||
365 | err = do_add_mount(newmnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE, | ||
366 | &cifs_dfs_automount_list); | ||
367 | switch (err) { | ||
368 | case 0: | ||
369 | schedule_delayed_work(&cifs_dfs_automount_task, | ||
370 | cifs_dfs_mountpoint_expiry_timeout); | ||
371 | cFYI(1, "leaving %s [ok]" , __func__); | ||
372 | return newmnt; | ||
373 | case -EBUSY: | ||
374 | /* someone else made a mount here whilst we were busy */ | ||
375 | mntput(newmnt); | ||
376 | cFYI(1, "leaving %s [EBUSY]" , __func__); | ||
377 | return NULL; | ||
378 | default: | ||
379 | mntput(newmnt); | ||
380 | cFYI(1, "leaving %s [error %d]" , __func__, err); | ||
381 | return ERR_PTR(err); | ||
382 | } | ||
378 | } | 383 | } |
379 | 384 | ||
380 | const struct inode_operations cifs_dfs_referral_inode_operations = { | 385 | const struct inode_operations cifs_dfs_referral_inode_operations = { |
381 | .follow_link = cifs_dfs_follow_mountpoint, | ||
382 | }; | 386 | }; |
383 | |||
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 897b2b2b28b5..851030f74939 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h | |||
@@ -93,6 +93,12 @@ extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir); | |||
93 | extern const struct dentry_operations cifs_dentry_ops; | 93 | extern const struct dentry_operations cifs_dentry_ops; |
94 | extern const struct dentry_operations cifs_ci_dentry_ops; | 94 | extern const struct dentry_operations cifs_ci_dentry_ops; |
95 | 95 | ||
96 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
97 | extern struct vfsmount *cifs_dfs_d_automount(struct path *path); | ||
98 | #else | ||
99 | #define cifs_dfs_d_automount NULL | ||
100 | #endif | ||
101 | |||
96 | /* Functions related to symlinks */ | 102 | /* Functions related to symlinks */ |
97 | extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); | 103 | extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); |
98 | extern void cifs_put_link(struct dentry *direntry, | 104 | extern void cifs_put_link(struct dentry *direntry, |
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 1e95dd635632..dd5f22918c33 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c | |||
@@ -675,6 +675,7 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) | |||
675 | 675 | ||
676 | const struct dentry_operations cifs_dentry_ops = { | 676 | const struct dentry_operations cifs_dentry_ops = { |
677 | .d_revalidate = cifs_d_revalidate, | 677 | .d_revalidate = cifs_d_revalidate, |
678 | .d_automount = cifs_dfs_d_automount, | ||
678 | /* d_delete: cifs_d_delete, */ /* not needed except for debugging */ | 679 | /* d_delete: cifs_d_delete, */ /* not needed except for debugging */ |
679 | }; | 680 | }; |
680 | 681 | ||
@@ -711,4 +712,5 @@ const struct dentry_operations cifs_ci_dentry_ops = { | |||
711 | .d_revalidate = cifs_d_revalidate, | 712 | .d_revalidate = cifs_d_revalidate, |
712 | .d_hash = cifs_ci_hash, | 713 | .d_hash = cifs_ci_hash, |
713 | .d_compare = cifs_ci_compare, | 714 | .d_compare = cifs_ci_compare, |
715 | .d_automount = cifs_dfs_d_automount, | ||
714 | }; | 716 | }; |
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index b06b60620240..6c9ee8014ff0 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -32,7 +32,7 @@ | |||
32 | #include "fscache.h" | 32 | #include "fscache.h" |
33 | 33 | ||
34 | 34 | ||
35 | static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) | 35 | static void cifs_set_ops(struct inode *inode) |
36 | { | 36 | { |
37 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | 37 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
38 | 38 | ||
@@ -60,7 +60,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) | |||
60 | break; | 60 | break; |
61 | case S_IFDIR: | 61 | case S_IFDIR: |
62 | #ifdef CONFIG_CIFS_DFS_UPCALL | 62 | #ifdef CONFIG_CIFS_DFS_UPCALL |
63 | if (is_dfs_referral) { | 63 | if (IS_AUTOMOUNT(inode)) { |
64 | inode->i_op = &cifs_dfs_referral_inode_operations; | 64 | inode->i_op = &cifs_dfs_referral_inode_operations; |
65 | } else { | 65 | } else { |
66 | #else /* NO DFS support, treat as a directory */ | 66 | #else /* NO DFS support, treat as a directory */ |
@@ -167,7 +167,9 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) | |||
167 | } | 167 | } |
168 | spin_unlock(&inode->i_lock); | 168 | spin_unlock(&inode->i_lock); |
169 | 169 | ||
170 | cifs_set_ops(inode, fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL); | 170 | if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL) |
171 | inode->i_flags |= S_AUTOMOUNT; | ||
172 | cifs_set_ops(inode); | ||
171 | } | 173 | } |
172 | 174 | ||
173 | void | 175 | void |