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>

authored by Igor Mammedov and committed by Steve French 1bfe73c2 85a6dac5

+120 -32
+120 -32
fs/cifs/connect.c
··· 2214 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 2264 int 2218 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 2268 int rc = 0; 2222 2269 int xid; ··· 2272 2225 struct cifsTconInfo *tcon = NULL; 2273 2226 struct TCP_Server_Info *srvTcp = NULL; 2274 2227 char *full_path; 2228 + struct dfs_info3_param *referrals = NULL; 2229 + unsigned int num_referrals = 0; 2230 + 2231 + char *mount_data = mount_data_global; 2232 + 2233 + try_mount_again: 2234 + full_path = NULL; 2275 2235 2276 2236 xid = GetXid(); 2277 2237 ··· 2425 2371 } 2426 2372 } 2427 2373 2428 - /* check for null share name ie connect to dfs root */ 2429 2374 if ((strchr(volume_info->UNC + 3, '\\') == NULL) 2430 2375 && (strchr(volume_info->UNC + 3, '/') == NULL)) { 2431 - /* rc = connect_to_dfs_path(...) */ 2432 - cFYI(1, ("DFS root not supported")); 2376 + cERROR(1, ("Missing share name")); 2433 2377 rc = -ENODEV; 2434 2378 goto mount_fail_check; 2435 2379 } else { ··· 2444 2392 } 2445 2393 } 2446 2394 if (rc) 2447 - goto mount_fail_check; 2395 + goto remote_path_check; 2448 2396 tcon->seal = volume_info->seal; 2449 2397 write_lock(&cifs_tcp_ses_lock); 2450 2398 list_add(&tcon->tcon_list, &pSesInfo->tcon_list); ··· 2469 2417 /* BB FIXME fix time_gran to be larger for LANMAN sessions */ 2470 2418 sb->s_time_gran = 100; 2471 2419 2472 - mount_fail_check: 2473 - /* on error free sesinfo and tcon struct if needed */ 2474 - if (rc) { 2475 - /* If find_unc succeeded then rc == 0 so we can not end */ 2476 - /* up accidently freeing someone elses tcon struct */ 2477 - if (tcon) 2478 - cifs_put_tcon(tcon); 2479 - else if (pSesInfo) 2480 - cifs_put_smb_ses(pSesInfo); 2481 - else 2482 - cifs_put_tcp_session(srvTcp); 2483 - goto out; 2484 - } 2420 + if (rc) 2421 + goto remote_path_check; 2422 + 2485 2423 cifs_sb->tcon = tcon; 2486 2424 2487 2425 /* do not care if following two calls succeed - informational */ ··· 2503 2461 cifs_sb->rsize = min(cifs_sb->rsize, 2504 2462 (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); 2505 2463 2506 - if (!rc && cifs_sb->prepathlen) { 2464 + remote_path_check: 2465 + /* check if a whole path (including prepath) is not remote */ 2466 + if (!rc && cifs_sb->prepathlen && tcon) { 2507 2467 /* build_path_to_root works only when we have a valid tcon */ 2508 2468 full_path = cifs_build_path_to_root(cifs_sb); 2509 2469 if (full_path == NULL) { ··· 2513 2469 goto mount_fail_check; 2514 2470 } 2515 2471 rc = is_path_accessible(xid, tcon, cifs_sb, full_path); 2516 - if (rc) { 2517 - cERROR(1, ("Path %s in not accessible: %d", 2518 - full_path, rc)); 2472 + if (rc != -EREMOTE) { 2519 2473 kfree(full_path); 2520 2474 goto mount_fail_check; 2521 2475 } 2522 2476 kfree(full_path); 2477 + } 2478 + 2479 + /* get referral if needed */ 2480 + if (rc == -EREMOTE) { 2481 + /* convert forward to back slashes in prepath here if needed */ 2482 + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) 2483 + convert_delimiter(cifs_sb->prepath, 2484 + CIFS_DIR_SEP(cifs_sb)); 2485 + full_path = build_unc_path_to_root(volume_info, cifs_sb); 2486 + if (IS_ERR(full_path)) { 2487 + rc = PTR_ERR(full_path); 2488 + goto mount_fail_check; 2489 + } 2490 + 2491 + cFYI(1, ("Getting referral for: %s", full_path)); 2492 + rc = get_dfs_path(xid, pSesInfo , full_path + 1, 2493 + cifs_sb->local_nls, &num_referrals, &referrals, 2494 + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); 2495 + if (!rc && num_referrals > 0) { 2496 + char *fake_devname = NULL; 2497 + 2498 + if (mount_data != mount_data_global) 2499 + kfree(mount_data); 2500 + mount_data = cifs_compose_mount_options( 2501 + cifs_sb->mountdata, full_path + 1, 2502 + referrals, &fake_devname); 2503 + kfree(fake_devname); 2504 + free_dfs_info_array(referrals, num_referrals); 2505 + 2506 + if (tcon) 2507 + cifs_put_tcon(tcon); 2508 + else if (pSesInfo) 2509 + cifs_put_smb_ses(pSesInfo); 2510 + 2511 + cleanup_volume_info(&volume_info); 2512 + FreeXid(xid); 2513 + kfree(full_path); 2514 + goto try_mount_again; 2515 + } 2516 + } 2517 + 2518 + mount_fail_check: 2519 + /* on error free sesinfo and tcon struct if needed */ 2520 + if (rc) { 2521 + if (mount_data != mount_data_global) 2522 + kfree(mount_data); 2523 + /* If find_unc succeeded then rc == 0 so we can not end */ 2524 + /* up accidently freeing someone elses tcon struct */ 2525 + if (tcon) 2526 + cifs_put_tcon(tcon); 2527 + else if (pSesInfo) 2528 + cifs_put_smb_ses(pSesInfo); 2529 + else 2530 + cifs_put_tcp_session(srvTcp); 2531 + goto out; 2523 2532 } 2524 2533 2525 2534 /* volume_info->password is freed above when existing session found ··· 2581 2484 password will be freed at unmount time) */ 2582 2485 out: 2583 2486 /* zero out password before freeing */ 2584 - if (volume_info) { 2585 - if (volume_info->password != NULL) { 2586 - memset(volume_info->password, 0, 2587 - strlen(volume_info->password)); 2588 - kfree(volume_info->password); 2589 - } 2590 - kfree(volume_info->UNC); 2591 - kfree(volume_info->prepath); 2592 - kfree(volume_info); 2593 - } 2487 + cleanup_volume_info(&volume_info); 2594 2488 FreeXid(xid); 2595 2489 return rc; 2596 2490 }