diff options
Diffstat (limited to 'fs/cifs/cifs_dfs_ref.c')
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 142 |
1 files changed, 64 insertions, 78 deletions
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index d6ced7aa23cf..8d8f28c94c0f 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c | |||
@@ -44,8 +44,7 @@ static void cifs_dfs_expire_automounts(struct work_struct *work) | |||
44 | void cifs_dfs_release_automount_timer(void) | 44 | void cifs_dfs_release_automount_timer(void) |
45 | { | 45 | { |
46 | BUG_ON(!list_empty(&cifs_dfs_automount_list)); | 46 | BUG_ON(!list_empty(&cifs_dfs_automount_list)); |
47 | cancel_delayed_work(&cifs_dfs_automount_task); | 47 | cancel_delayed_work_sync(&cifs_dfs_automount_task); |
48 | flush_scheduled_work(); | ||
49 | } | 48 | } |
50 | 49 | ||
51 | /** | 50 | /** |
@@ -54,7 +53,7 @@ void cifs_dfs_release_automount_timer(void) | |||
54 | * | 53 | * |
55 | * Extracts sharename form full UNC. | 54 | * Extracts sharename form full UNC. |
56 | * i.e. strips from UNC trailing path that is not part of share | 55 | * i.e. strips from UNC trailing path that is not part of share |
57 | * name and fixup missing '\' in the begining of DFS node refferal | 56 | * name and fixup missing '\' in the beginning of DFS node refferal |
58 | * if necessary. | 57 | * if necessary. |
59 | * Returns pointer to share name on success or ERR_PTR on error. | 58 | * Returns pointer to share name on success or ERR_PTR on error. |
60 | * Caller is responsible for freeing returned string. | 59 | * Caller is responsible for freeing returned string. |
@@ -256,35 +255,6 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb, | |||
256 | 255 | ||
257 | } | 256 | } |
258 | 257 | ||
259 | static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, | ||
260 | struct list_head *mntlist) | ||
261 | { | ||
262 | /* stolen from afs code */ | ||
263 | int err; | ||
264 | |||
265 | mntget(newmnt); | ||
266 | err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags | MNT_SHRINKABLE, mntlist); | ||
267 | switch (err) { | ||
268 | case 0: | ||
269 | path_put(&nd->path); | ||
270 | nd->path.mnt = newmnt; | ||
271 | nd->path.dentry = dget(newmnt->mnt_root); | ||
272 | schedule_delayed_work(&cifs_dfs_automount_task, | ||
273 | cifs_dfs_mountpoint_expiry_timeout); | ||
274 | break; | ||
275 | case -EBUSY: | ||
276 | /* someone else made a mount here whilst we were busy */ | ||
277 | while (d_mountpoint(nd->path.dentry) && | ||
278 | follow_down(&nd->path)) | ||
279 | ; | ||
280 | err = 0; | ||
281 | default: | ||
282 | mntput(newmnt); | ||
283 | break; | ||
284 | } | ||
285 | return err; | ||
286 | } | ||
287 | |||
288 | static void dump_referral(const struct dfs_info3_param *ref) | 258 | static void dump_referral(const struct dfs_info3_param *ref) |
289 | { | 259 | { |
290 | cFYI(1, "DFS: ref path: %s", ref->path_name); | 260 | cFYI(1, "DFS: ref path: %s", ref->path_name); |
@@ -294,34 +264,23 @@ static void dump_referral(const struct dfs_info3_param *ref) | |||
294 | ref->path_consumed); | 264 | ref->path_consumed); |
295 | } | 265 | } |
296 | 266 | ||
297 | 267 | /* | |
298 | static void* | 268 | * Create a vfsmount that we can automount |
299 | cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | 269 | */ |
270 | static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) | ||
300 | { | 271 | { |
301 | struct dfs_info3_param *referrals = NULL; | 272 | struct dfs_info3_param *referrals = NULL; |
302 | unsigned int num_referrals = 0; | 273 | unsigned int num_referrals = 0; |
303 | struct cifs_sb_info *cifs_sb; | 274 | struct cifs_sb_info *cifs_sb; |
304 | struct cifsSesInfo *ses; | 275 | struct cifs_ses *ses; |
305 | char *full_path = NULL; | 276 | char *full_path; |
306 | int xid, i; | 277 | int xid, i; |
307 | int rc = 0; | 278 | int rc; |
308 | struct vfsmount *mnt = ERR_PTR(-ENOENT); | 279 | struct vfsmount *mnt; |
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 | |||
313 | xid = GetXid(); | ||
314 | |||
315 | dput(nd->path.dentry); | ||
316 | nd->path.dentry = dget(dentry); | ||
317 | |||
318 | cifs_sb = CIFS_SB(dentry->d_inode->i_sb); | ||
319 | ses = cifs_sb->tcon->ses; | ||
320 | |||
321 | if (!ses) { | ||
322 | rc = -EINVAL; | ||
323 | goto out_err; | ||
324 | } | ||
325 | 284 | ||
326 | /* | 285 | /* |
327 | * The MSDFS spec states that paths in DFS referral requests and | 286 | * The MSDFS spec states that paths in DFS referral requests and |
@@ -329,56 +288,83 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | |||
329 | * the double backslashes usually used in the UNC. This function | 288 | * the double backslashes usually used in the UNC. This function |
330 | * gives us the latter, so we must adjust the result. | 289 | * gives us the latter, so we must adjust the result. |
331 | */ | 290 | */ |
332 | full_path = build_path_from_dentry(dentry); | 291 | mnt = ERR_PTR(-ENOMEM); |
333 | if (full_path == NULL) { | 292 | full_path = build_path_from_dentry(mntpt); |
334 | rc = -ENOMEM; | 293 | if (full_path == NULL) |
335 | goto out_err; | 294 | goto cdda_exit; |
295 | |||
296 | cifs_sb = CIFS_SB(mntpt->d_inode->i_sb); | ||
297 | tlink = cifs_sb_tlink(cifs_sb); | ||
298 | if (IS_ERR(tlink)) { | ||
299 | mnt = ERR_CAST(tlink); | ||
300 | goto free_full_path; | ||
336 | } | 301 | } |
302 | ses = tlink_tcon(tlink)->ses; | ||
337 | 303 | ||
338 | rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb->local_nls, | 304 | xid = GetXid(); |
305 | rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls, | ||
339 | &num_referrals, &referrals, | 306 | &num_referrals, &referrals, |
340 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 307 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); |
308 | FreeXid(xid); | ||
309 | |||
310 | cifs_put_tlink(tlink); | ||
341 | 311 | ||
312 | mnt = ERR_PTR(-ENOENT); | ||
342 | for (i = 0; i < num_referrals; i++) { | 313 | for (i = 0; i < num_referrals; i++) { |
343 | int len; | 314 | int len; |
344 | dump_referral(referrals+i); | 315 | dump_referral(referrals + i); |
345 | /* connect to a node */ | 316 | /* connect to a node */ |
346 | len = strlen(referrals[i].node_name); | 317 | len = strlen(referrals[i].node_name); |
347 | if (len < 2) { | 318 | if (len < 2) { |
348 | cERROR(1, "%s: Net Address path too short: %s", | 319 | cERROR(1, "%s: Net Address path too short: %s", |
349 | __func__, referrals[i].node_name); | 320 | __func__, referrals[i].node_name); |
350 | rc = -EINVAL; | 321 | mnt = ERR_PTR(-EINVAL); |
351 | goto out_err; | 322 | break; |
352 | } | 323 | } |
353 | mnt = cifs_dfs_do_refmount(cifs_sb, | 324 | mnt = cifs_dfs_do_refmount(cifs_sb, |
354 | full_path, referrals + i); | 325 | full_path, referrals + i); |
355 | cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, | 326 | cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, |
356 | referrals[i].node_name, mnt); | 327 | referrals[i].node_name, mnt); |
357 | |||
358 | /* complete mount procedure if we accured submount */ | ||
359 | if (!IS_ERR(mnt)) | 328 | if (!IS_ERR(mnt)) |
360 | break; | 329 | goto success; |
361 | } | 330 | } |
362 | 331 | ||
363 | /* we need it cause for() above could exit without valid submount */ | 332 | /* no valid submounts were found; return error from get_dfs_path() by |
364 | rc = PTR_ERR(mnt); | 333 | * preference */ |
365 | if (IS_ERR(mnt)) | 334 | if (rc != 0) |
366 | goto out_err; | 335 | mnt = ERR_PTR(rc); |
367 | 336 | ||
368 | rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list); | 337 | success: |
369 | |||
370 | out: | ||
371 | FreeXid(xid); | ||
372 | free_dfs_info_array(referrals, num_referrals); | 338 | free_dfs_info_array(referrals, num_referrals); |
339 | free_full_path: | ||
373 | kfree(full_path); | 340 | kfree(full_path); |
341 | cdda_exit: | ||
374 | cFYI(1, "leaving %s" , __func__); | 342 | cFYI(1, "leaving %s" , __func__); |
375 | return ERR_PTR(rc); | 343 | return mnt; |
376 | out_err: | 344 | } |
377 | path_put(&nd->path); | 345 | |
378 | goto out; | 346 | /* |
347 | * Attempt to automount the referral | ||
348 | */ | ||
349 | struct vfsmount *cifs_dfs_d_automount(struct path *path) | ||
350 | { | ||
351 | struct vfsmount *newmnt; | ||
352 | |||
353 | cFYI(1, "in %s", __func__); | ||
354 | |||
355 | newmnt = cifs_dfs_do_automount(path->dentry); | ||
356 | if (IS_ERR(newmnt)) { | ||
357 | cFYI(1, "leaving %s [automount failed]" , __func__); | ||
358 | return newmnt; | ||
359 | } | ||
360 | |||
361 | mntget(newmnt); /* prevent immediate expiration */ | ||
362 | mnt_set_expiry(newmnt, &cifs_dfs_automount_list); | ||
363 | schedule_delayed_work(&cifs_dfs_automount_task, | ||
364 | cifs_dfs_mountpoint_expiry_timeout); | ||
365 | cFYI(1, "leaving %s [ok]" , __func__); | ||
366 | return newmnt; | ||
379 | } | 367 | } |
380 | 368 | ||
381 | const struct inode_operations cifs_dfs_referral_inode_operations = { | 369 | const struct inode_operations cifs_dfs_referral_inode_operations = { |
382 | .follow_link = cifs_dfs_follow_mountpoint, | ||
383 | }; | 370 | }; |
384 | |||