aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/cifs_dfs_ref.c131
-rw-r--r--fs/cifs/cifsfs.h6
-rw-r--r--fs/cifs/dir.c2
-rw-r--r--fs/cifs/inode.c8
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
258static 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
284static void dump_referral(const struct dfs_info3_param *ref) 258static 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/*
294static void* 268 * Create a vfsmount that we can automount
295cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) 269 */
270static 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
369out: 338success:
370 FreeXid(xid);
371 free_dfs_info_array(referrals, num_referrals); 339 free_dfs_info_array(referrals, num_referrals);
340free_full_path:
372 kfree(full_path); 341 kfree(full_path);
342free_xid:
343 FreeXid(xid);
373 cFYI(1, "leaving %s" , __func__); 344 cFYI(1, "leaving %s" , __func__);
374 return ERR_PTR(rc); 345 return mnt;
375out_err: 346}
376 path_put(&nd->path); 347
377 goto out; 348/*
349 * Attempt to automount the referral
350 */
351struct 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
380const struct inode_operations cifs_dfs_referral_inode_operations = { 385const 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);
93extern const struct dentry_operations cifs_dentry_ops; 93extern const struct dentry_operations cifs_dentry_ops;
94extern const struct dentry_operations cifs_ci_dentry_ops; 94extern const struct dentry_operations cifs_ci_dentry_ops;
95 95
96#ifdef CONFIG_CIFS_DFS_UPCALL
97extern 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 */
97extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); 103extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
98extern void cifs_put_link(struct dentry *direntry, 104extern 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
676const struct dentry_operations cifs_dentry_ops = { 676const 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
35static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) 35static 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
173void 175void