diff options
author | Igor Mammedov <niallain@gmail.com> | 2009-04-01 09:54:42 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2009-04-16 21:26:48 -0400 |
commit | 1bfe73c258addc388b90fe8c2c6bbc0f0c9c10dd (patch) | |
tree | 5561ad05bda869ce83525888ed8961f580d74844 /fs/cifs/connect.c | |
parent | 85a6dac54a7e28112488b02523202985edc7e639 (diff) |
Remote DFS root support.
Allows to mount share on a server that returns -EREMOTE
at the tree connect stage or at the check on a full path
accessibility.
Signed-off-by: Igor Mammedov <niallain@gmail.com>
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r-- | fs/cifs/connect.c | 152 |
1 files changed, 120 insertions, 32 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index b173b0171712..2e7a4ea26ab9 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -2214,9 +2214,56 @@ is_path_accessible(int xid, struct cifsTconInfo *tcon, | |||
2214 | return rc; | 2214 | return rc; |
2215 | } | 2215 | } |
2216 | 2216 | ||
2217 | static void | ||
2218 | cleanup_volume_info(struct smb_vol **pvolume_info) | ||
2219 | { | ||
2220 | struct smb_vol *volume_info; | ||
2221 | |||
2222 | if (!pvolume_info && !*pvolume_info) | ||
2223 | return; | ||
2224 | |||
2225 | volume_info = *pvolume_info; | ||
2226 | kzfree(volume_info->password); | ||
2227 | kfree(volume_info->UNC); | ||
2228 | kfree(volume_info->prepath); | ||
2229 | kfree(volume_info); | ||
2230 | *pvolume_info = NULL; | ||
2231 | return; | ||
2232 | } | ||
2233 | |||
2234 | /* build_path_to_root returns full path to root when | ||
2235 | * we do not have an exiting connection (tcon) */ | ||
2236 | static char * | ||
2237 | build_unc_path_to_root(const struct smb_vol *volume_info, | ||
2238 | const struct cifs_sb_info *cifs_sb) | ||
2239 | { | ||
2240 | char *full_path; | ||
2241 | |||
2242 | int unc_len = strnlen(volume_info->UNC, MAX_TREE_SIZE + 1); | ||
2243 | full_path = kmalloc(unc_len + cifs_sb->prepathlen + 1, GFP_KERNEL); | ||
2244 | if (full_path == NULL) | ||
2245 | return ERR_PTR(-ENOMEM); | ||
2246 | |||
2247 | strncpy(full_path, volume_info->UNC, unc_len); | ||
2248 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { | ||
2249 | int i; | ||
2250 | for (i = 0; i < unc_len; i++) { | ||
2251 | if (full_path[i] == '\\') | ||
2252 | full_path[i] = '/'; | ||
2253 | } | ||
2254 | } | ||
2255 | |||
2256 | if (cifs_sb->prepathlen) | ||
2257 | strncpy(full_path + unc_len, cifs_sb->prepath, | ||
2258 | cifs_sb->prepathlen); | ||
2259 | |||
2260 | full_path[unc_len + cifs_sb->prepathlen] = 0; /* add trailing null */ | ||
2261 | return full_path; | ||
2262 | } | ||
2263 | |||
2217 | int | 2264 | int |
2218 | cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | 2265 | cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, |
2219 | char *mount_data, const char *devname) | 2266 | char *mount_data_global, const char *devname) |
2220 | { | 2267 | { |
2221 | int rc = 0; | 2268 | int rc = 0; |
2222 | int xid; | 2269 | int xid; |
@@ -2225,6 +2272,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2225 | struct cifsTconInfo *tcon = NULL; | 2272 | struct cifsTconInfo *tcon = NULL; |
2226 | struct TCP_Server_Info *srvTcp = NULL; | 2273 | struct TCP_Server_Info *srvTcp = NULL; |
2227 | char *full_path; | 2274 | char *full_path; |
2275 | struct dfs_info3_param *referrals = NULL; | ||
2276 | unsigned int num_referrals = 0; | ||
2277 | |||
2278 | char *mount_data = mount_data_global; | ||
2279 | |||
2280 | try_mount_again: | ||
2281 | full_path = NULL; | ||
2228 | 2282 | ||
2229 | xid = GetXid(); | 2283 | xid = GetXid(); |
2230 | 2284 | ||
@@ -2371,11 +2425,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2371 | } | 2425 | } |
2372 | } | 2426 | } |
2373 | 2427 | ||
2374 | /* check for null share name ie connect to dfs root */ | ||
2375 | if ((strchr(volume_info->UNC + 3, '\\') == NULL) | 2428 | if ((strchr(volume_info->UNC + 3, '\\') == NULL) |
2376 | && (strchr(volume_info->UNC + 3, '/') == NULL)) { | 2429 | && (strchr(volume_info->UNC + 3, '/') == NULL)) { |
2377 | /* rc = connect_to_dfs_path(...) */ | 2430 | cERROR(1, ("Missing share name")); |
2378 | cFYI(1, ("DFS root not supported")); | ||
2379 | rc = -ENODEV; | 2431 | rc = -ENODEV; |
2380 | goto mount_fail_check; | 2432 | goto mount_fail_check; |
2381 | } else { | 2433 | } else { |
@@ -2392,7 +2444,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2392 | } | 2444 | } |
2393 | } | 2445 | } |
2394 | if (rc) | 2446 | if (rc) |
2395 | goto mount_fail_check; | 2447 | goto remote_path_check; |
2396 | tcon->seal = volume_info->seal; | 2448 | tcon->seal = volume_info->seal; |
2397 | write_lock(&cifs_tcp_ses_lock); | 2449 | write_lock(&cifs_tcp_ses_lock); |
2398 | list_add(&tcon->tcon_list, &pSesInfo->tcon_list); | 2450 | list_add(&tcon->tcon_list, &pSesInfo->tcon_list); |
@@ -2417,19 +2469,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2417 | /* BB FIXME fix time_gran to be larger for LANMAN sessions */ | 2469 | /* BB FIXME fix time_gran to be larger for LANMAN sessions */ |
2418 | sb->s_time_gran = 100; | 2470 | sb->s_time_gran = 100; |
2419 | 2471 | ||
2420 | mount_fail_check: | 2472 | if (rc) |
2421 | /* on error free sesinfo and tcon struct if needed */ | 2473 | goto remote_path_check; |
2422 | if (rc) { | 2474 | |
2423 | /* If find_unc succeeded then rc == 0 so we can not end */ | ||
2424 | /* up accidently freeing someone elses tcon struct */ | ||
2425 | if (tcon) | ||
2426 | cifs_put_tcon(tcon); | ||
2427 | else if (pSesInfo) | ||
2428 | cifs_put_smb_ses(pSesInfo); | ||
2429 | else | ||
2430 | cifs_put_tcp_session(srvTcp); | ||
2431 | goto out; | ||
2432 | } | ||
2433 | cifs_sb->tcon = tcon; | 2475 | cifs_sb->tcon = tcon; |
2434 | 2476 | ||
2435 | /* do not care if following two calls succeed - informational */ | 2477 | /* do not care if following two calls succeed - informational */ |
@@ -2461,7 +2503,9 @@ mount_fail_check: | |||
2461 | cifs_sb->rsize = min(cifs_sb->rsize, | 2503 | cifs_sb->rsize = min(cifs_sb->rsize, |
2462 | (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); | 2504 | (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); |
2463 | 2505 | ||
2464 | if (!rc && cifs_sb->prepathlen) { | 2506 | remote_path_check: |
2507 | /* check if a whole path (including prepath) is not remote */ | ||
2508 | if (!rc && cifs_sb->prepathlen && tcon) { | ||
2465 | /* build_path_to_root works only when we have a valid tcon */ | 2509 | /* build_path_to_root works only when we have a valid tcon */ |
2466 | full_path = cifs_build_path_to_root(cifs_sb); | 2510 | full_path = cifs_build_path_to_root(cifs_sb); |
2467 | if (full_path == NULL) { | 2511 | if (full_path == NULL) { |
@@ -2469,31 +2513,75 @@ mount_fail_check: | |||
2469 | goto mount_fail_check; | 2513 | goto mount_fail_check; |
2470 | } | 2514 | } |
2471 | rc = is_path_accessible(xid, tcon, cifs_sb, full_path); | 2515 | rc = is_path_accessible(xid, tcon, cifs_sb, full_path); |
2472 | if (rc) { | 2516 | if (rc != -EREMOTE) { |
2473 | cERROR(1, ("Path %s in not accessible: %d", | ||
2474 | full_path, rc)); | ||
2475 | kfree(full_path); | 2517 | kfree(full_path); |
2476 | goto mount_fail_check; | 2518 | goto mount_fail_check; |
2477 | } | 2519 | } |
2478 | kfree(full_path); | 2520 | kfree(full_path); |
2479 | } | 2521 | } |
2480 | 2522 | ||
2523 | /* get referral if needed */ | ||
2524 | if (rc == -EREMOTE) { | ||
2525 | /* convert forward to back slashes in prepath here if needed */ | ||
2526 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) | ||
2527 | convert_delimiter(cifs_sb->prepath, | ||
2528 | CIFS_DIR_SEP(cifs_sb)); | ||
2529 | full_path = build_unc_path_to_root(volume_info, cifs_sb); | ||
2530 | if (IS_ERR(full_path)) { | ||
2531 | rc = PTR_ERR(full_path); | ||
2532 | goto mount_fail_check; | ||
2533 | } | ||
2534 | |||
2535 | cFYI(1, ("Getting referral for: %s", full_path)); | ||
2536 | rc = get_dfs_path(xid, pSesInfo , full_path + 1, | ||
2537 | cifs_sb->local_nls, &num_referrals, &referrals, | ||
2538 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
2539 | if (!rc && num_referrals > 0) { | ||
2540 | char *fake_devname = NULL; | ||
2541 | |||
2542 | if (mount_data != mount_data_global) | ||
2543 | kfree(mount_data); | ||
2544 | mount_data = cifs_compose_mount_options( | ||
2545 | cifs_sb->mountdata, full_path + 1, | ||
2546 | referrals, &fake_devname); | ||
2547 | kfree(fake_devname); | ||
2548 | free_dfs_info_array(referrals, num_referrals); | ||
2549 | |||
2550 | if (tcon) | ||
2551 | cifs_put_tcon(tcon); | ||
2552 | else if (pSesInfo) | ||
2553 | cifs_put_smb_ses(pSesInfo); | ||
2554 | |||
2555 | cleanup_volume_info(&volume_info); | ||
2556 | FreeXid(xid); | ||
2557 | kfree(full_path); | ||
2558 | goto try_mount_again; | ||
2559 | } | ||
2560 | } | ||
2561 | |||
2562 | mount_fail_check: | ||
2563 | /* on error free sesinfo and tcon struct if needed */ | ||
2564 | if (rc) { | ||
2565 | if (mount_data != mount_data_global) | ||
2566 | kfree(mount_data); | ||
2567 | /* If find_unc succeeded then rc == 0 so we can not end */ | ||
2568 | /* up accidently freeing someone elses tcon struct */ | ||
2569 | if (tcon) | ||
2570 | cifs_put_tcon(tcon); | ||
2571 | else if (pSesInfo) | ||
2572 | cifs_put_smb_ses(pSesInfo); | ||
2573 | else | ||
2574 | cifs_put_tcp_session(srvTcp); | ||
2575 | goto out; | ||
2576 | } | ||
2577 | |||
2481 | /* volume_info->password is freed above when existing session found | 2578 | /* volume_info->password is freed above when existing session found |
2482 | (in which case it is not needed anymore) but when new sesion is created | 2579 | (in which case it is not needed anymore) but when new sesion is created |
2483 | the password ptr is put in the new session structure (in which case the | 2580 | the password ptr is put in the new session structure (in which case the |
2484 | password will be freed at unmount time) */ | 2581 | password will be freed at unmount time) */ |
2485 | out: | 2582 | out: |
2486 | /* zero out password before freeing */ | 2583 | /* zero out password before freeing */ |
2487 | if (volume_info) { | 2584 | cleanup_volume_info(&volume_info); |
2488 | if (volume_info->password != NULL) { | ||
2489 | memset(volume_info->password, 0, | ||
2490 | strlen(volume_info->password)); | ||
2491 | kfree(volume_info->password); | ||
2492 | } | ||
2493 | kfree(volume_info->UNC); | ||
2494 | kfree(volume_info->prepath); | ||
2495 | kfree(volume_info); | ||
2496 | } | ||
2497 | FreeXid(xid); | 2585 | FreeXid(xid); |
2498 | return rc; | 2586 | return rc; |
2499 | } | 2587 | } |