aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifs_dfs_ref.c120
-rw-r--r--fs/cifs/cifsfs.h6
-rw-r--r--fs/cifs/dir.c2
-rw-r--r--fs/cifs/inode.c8
4 files changed, 66 insertions, 70 deletions
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index c68a056f27fd..7ed36536e754 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -255,35 +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 while (d_mountpoint(nd->path.dentry) &&
277 follow_down(&nd->path))
278 ;
279 err = 0;
280 default:
281 mntput(newmnt);
282 break;
283 }
284 return err;
285}
286
287static void dump_referral(const struct dfs_info3_param *ref) 258static void dump_referral(const struct dfs_info3_param *ref)
288{ 259{
289 cFYI(1, "DFS: ref path: %s", ref->path_name); 260 cFYI(1, "DFS: ref path: %s", ref->path_name);
@@ -293,45 +264,43 @@ static void dump_referral(const struct dfs_info3_param *ref)
293 ref->path_consumed); 264 ref->path_consumed);
294} 265}
295 266
296 267/*
297static void* 268 * Create a vfsmount that we can automount
298cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) 269 */
270static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
299{ 271{
300 struct dfs_info3_param *referrals = NULL; 272 struct dfs_info3_param *referrals = NULL;
301 unsigned int num_referrals = 0; 273 unsigned int num_referrals = 0;
302 struct cifs_sb_info *cifs_sb; 274 struct cifs_sb_info *cifs_sb;
303 struct cifsSesInfo *ses; 275 struct cifsSesInfo *ses;
304 char *full_path = NULL; 276 char *full_path;
305 int xid, i; 277 int xid, i;
306 int rc = 0; 278 int rc;
307 struct vfsmount *mnt = ERR_PTR(-ENOENT); 279 struct vfsmount *mnt;
308 struct tcon_link *tlink; 280 struct tcon_link *tlink;
309 281
310 cFYI(1, "in %s", __func__); 282 cFYI(1, "in %s", __func__);
311 BUG_ON(IS_ROOT(dentry)); 283 BUG_ON(IS_ROOT(mntpt));
312 284
313 xid = GetXid(); 285 xid = GetXid();
314 286
315 dput(nd->path.dentry);
316 nd->path.dentry = dget(dentry);
317
318 /* 287 /*
319 * The MSDFS spec states that paths in DFS referral requests and 288 * The MSDFS spec states that paths in DFS referral requests and
320 * responses must be prefixed by a single '\' character instead of 289 * responses must be prefixed by a single '\' character instead of
321 * the double backslashes usually used in the UNC. This function 290 * the double backslashes usually used in the UNC. This function
322 * gives us the latter, so we must adjust the result. 291 * gives us the latter, so we must adjust the result.
323 */ 292 */
324 full_path = build_path_from_dentry(dentry); 293 mnt = ERR_PTR(-ENOMEM);
325 if (full_path == NULL) { 294 full_path = build_path_from_dentry(mntpt);
326 rc = -ENOMEM; 295 if (full_path == NULL)
327 goto out_err; 296 goto free_xid;
328 }
329 297
330 cifs_sb = CIFS_SB(dentry->d_inode->i_sb); 298 cifs_sb = CIFS_SB(mntpt->d_inode->i_sb);
331 tlink = cifs_sb_tlink(cifs_sb); 299 tlink = cifs_sb_tlink(cifs_sb);
300 mnt = ERR_PTR(-EINVAL);
332 if (IS_ERR(tlink)) { 301 if (IS_ERR(tlink)) {
333 rc = PTR_ERR(tlink); 302 mnt = ERR_CAST(tlink);
334 goto out_err; 303 goto free_full_path;
335 } 304 }
336 ses = tlink_tcon(tlink)->ses; 305 ses = tlink_tcon(tlink)->ses;
337 306
@@ -341,46 +310,63 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
341 310
342 cifs_put_tlink(tlink); 311 cifs_put_tlink(tlink);
343 312
313 mnt = ERR_PTR(-ENOENT);
344 for (i = 0; i < num_referrals; i++) { 314 for (i = 0; i < num_referrals; i++) {
345 int len; 315 int len;
346 dump_referral(referrals+i); 316 dump_referral(referrals + i);
347 /* connect to a node */ 317 /* connect to a node */
348 len = strlen(referrals[i].node_name); 318 len = strlen(referrals[i].node_name);
349 if (len < 2) { 319 if (len < 2) {
350 cERROR(1, "%s: Net Address path too short: %s", 320 cERROR(1, "%s: Net Address path too short: %s",
351 __func__, referrals[i].node_name); 321 __func__, referrals[i].node_name);
352 rc = -EINVAL; 322 mnt = ERR_PTR(-EINVAL);
353 goto out_err; 323 break;
354 } 324 }
355 mnt = cifs_dfs_do_refmount(cifs_sb, 325 mnt = cifs_dfs_do_refmount(cifs_sb,
356 full_path, referrals + i); 326 full_path, referrals + i);
357 cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, 327 cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__,
358 referrals[i].node_name, mnt); 328 referrals[i].node_name, mnt);
359
360 /* complete mount procedure if we accured submount */
361 if (!IS_ERR(mnt)) 329 if (!IS_ERR(mnt))
362 break; 330 goto success;
363 } 331 }
364 332
365 /* we need it cause for() above could exit without valid submount */ 333 /* no valid submounts were found; return error from get_dfs_path() by
366 rc = PTR_ERR(mnt); 334 * preference */
367 if (IS_ERR(mnt)) 335 if (rc != 0)
368 goto out_err; 336 mnt = ERR_PTR(rc);
369
370 rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list);
371 337
372out: 338success:
373 FreeXid(xid);
374 free_dfs_info_array(referrals, num_referrals); 339 free_dfs_info_array(referrals, num_referrals);
340free_full_path:
375 kfree(full_path); 341 kfree(full_path);
342free_xid:
343 FreeXid(xid);
376 cFYI(1, "leaving %s" , __func__); 344 cFYI(1, "leaving %s" , __func__);
377 return ERR_PTR(rc); 345 return mnt;
378out_err: 346}
379 path_put(&nd->path); 347
380 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
355 cFYI(1, "in %s", __func__);
356
357 newmnt = cifs_dfs_do_automount(path->dentry);
358 if (IS_ERR(newmnt)) {
359 cFYI(1, "leaving %s [automount failed]" , __func__);
360 return newmnt;
361 }
362
363 mntget(newmnt); /* prevent immediate expiration */
364 mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
365 schedule_delayed_work(&cifs_dfs_automount_task,
366 cifs_dfs_mountpoint_expiry_timeout);
367 cFYI(1, "leaving %s [ok]" , __func__);
368 return newmnt;
381} 369}
382 370
383const struct inode_operations cifs_dfs_referral_inode_operations = { 371const struct inode_operations cifs_dfs_referral_inode_operations = {
384 .follow_link = cifs_dfs_follow_mountpoint,
385}; 372};
386
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