aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorIgor Mammedov <niallain@gmail.com>2009-04-01 09:54:42 -0400
committerSteve French <sfrench@us.ibm.com>2009-04-16 21:26:48 -0400
commit1bfe73c258addc388b90fe8c2c6bbc0f0c9c10dd (patch)
tree5561ad05bda869ce83525888ed8961f580d74844 /fs
parent85a6dac54a7e28112488b02523202985edc7e639 (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')
-rw-r--r--fs/cifs/connect.c152
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
2217static void
2218cleanup_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) */
2236static char *
2237build_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
2217int 2264int
2218cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 2265cifs_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
2280try_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
2420mount_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) { 2506remote_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
2562mount_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) */
2485out: 2582out:
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}