diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 120 | ||||
-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, 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 | ||
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 | 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 | |||
287 | static void dump_referral(const struct dfs_info3_param *ref) | 258 | static 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 | /* | |
297 | static void* | 268 | * Create a vfsmount that we can automount |
298 | cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | 269 | */ |
270 | static 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 | ||
372 | out: | 338 | success: |
373 | FreeXid(xid); | ||
374 | free_dfs_info_array(referrals, num_referrals); | 339 | free_dfs_info_array(referrals, num_referrals); |
340 | free_full_path: | ||
375 | kfree(full_path); | 341 | kfree(full_path); |
342 | free_xid: | ||
343 | FreeXid(xid); | ||
376 | cFYI(1, "leaving %s" , __func__); | 344 | cFYI(1, "leaving %s" , __func__); |
377 | return ERR_PTR(rc); | 345 | return mnt; |
378 | out_err: | 346 | } |
379 | path_put(&nd->path); | 347 | |
380 | 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 | |||
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 | ||
383 | const struct inode_operations cifs_dfs_referral_inode_operations = { | 371 | const 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); | |||
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 |