Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cifs: share dfs connections and supers

When matching DFS superblocks we can't rely on either the server's
address or tcon's UNC name from mount(2) as the existing servers and
tcons might be connected to somewhere else. Instead, check if
superblock is dfs, and if so, match its original source pathname with
the new mount's source pathname.

For DFS connections, instead of checking server's address, match its
referral path as it could be connected to different targets.

Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Paulo Alcantara and committed by
Steve French
a1c0d005 a73a26d9

+323 -369
+8
fs/cifs/cifs_debug.c
··· 372 372 seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d", 373 373 atomic_read(&server->in_send), 374 374 atomic_read(&server->num_waiters)); 375 + if (IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)) { 376 + if (server->origin_fullpath) 377 + seq_printf(m, "\nDFS origin full path: %s", 378 + server->origin_fullpath); 379 + if (server->leaf_fullpath) 380 + seq_printf(m, "\nDFS leaf full path: %s", 381 + server->leaf_fullpath); 382 + } 375 383 376 384 seq_printf(m, "\n\n\tSessions: "); 377 385 i = 0;
-5
fs/cifs/cifsglob.h
··· 738 738 bool use_swn_dstaddr; 739 739 struct sockaddr_storage swn_dstaddr; 740 740 #endif 741 - #ifdef CONFIG_CIFS_DFS_UPCALL 742 - bool is_dfs_conn; /* if a dfs connection */ 743 741 struct mutex refpath_lock; /* protects leaf_fullpath */ 744 742 /* 745 743 * Canonical DFS full paths that were used to chase referrals in mount and reconnect. ··· 751 753 * format: \\HOST\SHARE\[OPTIONAL PATH] 752 754 */ 753 755 char *origin_fullpath, *leaf_fullpath, *current_fullpath; 754 - #endif 755 756 }; 756 757 757 758 static inline bool is_smb1(struct TCP_Server_Info *server) ··· 1764 1767 struct TCP_Server_Info *server; 1765 1768 struct cifs_ses *ses; 1766 1769 struct cifs_tcon *tcon; 1767 - #ifdef CONFIG_CIFS_DFS_UPCALL 1768 1770 struct cifs_ses *root_ses; 1769 1771 uuid_t mount_id; 1770 1772 char *origin_fullpath, *leaf_fullpath; 1771 - #endif 1772 1773 }; 1773 1774 1774 1775 static inline void free_dfs_info_param(struct dfs_info3_param *param)
+2
fs/cifs/cifsproto.h
··· 242 242 unsigned int page_offset, 243 243 unsigned int to_read); 244 244 extern int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb); 245 + void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx); 245 246 int cifs_mount_get_session(struct cifs_mount_ctx *mnt_ctx); 247 + int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx); 246 248 int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx); 247 249 extern int cifs_match_super(struct super_block *, void *); 248 250 extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx);
+64 -353
fs/cifs/connect.c
··· 546 546 547 547 int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) 548 548 { 549 - /* If tcp session is not an dfs connection, then reconnect to last target server */ 550 - spin_lock(&server->srv_lock); 551 - if (!server->is_dfs_conn) { 552 - spin_unlock(&server->srv_lock); 553 - return __cifs_reconnect(server, mark_smb_session); 554 - } 555 - spin_unlock(&server->srv_lock); 556 - 557 549 mutex_lock(&server->refpath_lock); 558 - if (!server->origin_fullpath || !server->leaf_fullpath) { 550 + if (!server->leaf_fullpath) { 559 551 mutex_unlock(&server->refpath_lock); 560 552 return __cifs_reconnect(server, mark_smb_session); 561 553 } ··· 1359 1367 return port == *sport; 1360 1368 } 1361 1369 1362 - static bool 1363 - match_address(struct TCP_Server_Info *server, struct sockaddr *addr, 1364 - struct sockaddr *srcaddr) 1370 + static bool match_server_address(struct TCP_Server_Info *server, struct sockaddr *addr) 1365 1371 { 1366 1372 switch (addr->sa_family) { 1367 1373 case AF_INET: { ··· 1388 1398 return false; /* don't expect to be here */ 1389 1399 } 1390 1400 1391 - if (!cifs_match_ipaddr(srcaddr, (struct sockaddr *)&server->srcaddr)) 1392 - return false; 1393 - 1394 1401 return true; 1395 1402 } 1396 1403 ··· 1415 1428 } 1416 1429 1417 1430 /* this function must be called with srv_lock held */ 1418 - static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) 1431 + static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx, 1432 + bool dfs_super_cmp) 1419 1433 { 1420 1434 struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; 1421 1435 ··· 1441 1453 if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns)) 1442 1454 return 0; 1443 1455 1444 - if (strcasecmp(server->hostname, ctx->server_hostname)) 1456 + if (!cifs_match_ipaddr((struct sockaddr *)&ctx->srcaddr, 1457 + (struct sockaddr *)&server->srcaddr)) 1445 1458 return 0; 1446 - 1447 - if (!match_address(server, addr, 1448 - (struct sockaddr *)&ctx->srcaddr)) 1449 - return 0; 1450 - 1451 - if (!match_port(server, addr)) 1452 - return 0; 1459 + /* 1460 + * When matching DFS superblocks, we only check for original source pathname as the 1461 + * currently connected target might be different than the one parsed earlier in i.e. 1462 + * mount.cifs(8). 1463 + */ 1464 + if (dfs_super_cmp) { 1465 + if (!ctx->source || !server->origin_fullpath || 1466 + strcasecmp(server->origin_fullpath, ctx->source)) 1467 + return 0; 1468 + } else { 1469 + /* Skip addr, hostname and port matching for DFS connections */ 1470 + if (server->leaf_fullpath) { 1471 + if (!ctx->leaf_fullpath || 1472 + strcasecmp(server->leaf_fullpath, ctx->leaf_fullpath)) 1473 + return 0; 1474 + } else if (strcasecmp(server->hostname, ctx->server_hostname) || 1475 + !match_server_address(server, addr) || 1476 + !match_port(server, addr)) { 1477 + return 0; 1478 + } 1479 + } 1453 1480 1454 1481 if (!match_security(server, ctx)) 1455 1482 return 0; ··· 1492 1489 spin_lock(&cifs_tcp_ses_lock); 1493 1490 list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { 1494 1491 spin_lock(&server->srv_lock); 1495 - #ifdef CONFIG_CIFS_DFS_UPCALL 1496 - /* 1497 - * DFS failover implementation in cifs_reconnect() requires unique tcp sessions for 1498 - * DFS connections to do failover properly, so avoid sharing them with regular 1499 - * shares or even links that may connect to same server but having completely 1500 - * different failover targets. 1501 - */ 1502 - if (server->is_dfs_conn) { 1503 - spin_unlock(&server->srv_lock); 1504 - continue; 1505 - } 1506 - #endif 1507 1492 /* 1508 1493 * Skip ses channels since they're only handled in lower layers 1509 1494 * (e.g. cifs_send_recv). 1510 1495 */ 1511 - if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) { 1496 + if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx, false)) { 1512 1497 spin_unlock(&server->srv_lock); 1513 1498 continue; 1514 1499 } ··· 1589 1598 if (!tcp_ses->hostname) { 1590 1599 rc = -ENOMEM; 1591 1600 goto out_err; 1601 + } 1602 + 1603 + if (ctx->leaf_fullpath) { 1604 + tcp_ses->leaf_fullpath = kstrdup(ctx->leaf_fullpath, GFP_KERNEL); 1605 + if (!tcp_ses->leaf_fullpath) { 1606 + rc = -ENOMEM; 1607 + goto out_err; 1608 + } 1609 + tcp_ses->current_fullpath = tcp_ses->leaf_fullpath; 1592 1610 } 1593 1611 1594 1612 if (ctx->nosharesock) ··· 1748 1748 if (CIFS_SERVER_IS_CHAN(tcp_ses)) 1749 1749 cifs_put_tcp_session(tcp_ses->primary_server, false); 1750 1750 kfree(tcp_ses->hostname); 1751 + kfree(tcp_ses->leaf_fullpath); 1751 1752 if (tcp_ses->ssocket) 1752 1753 sock_release(tcp_ses->ssocket); 1753 1754 kfree(tcp_ses); ··· 2278 2277 } 2279 2278 2280 2279 /* this function must be called with tc_lock held */ 2281 - static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) 2280 + static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx, bool dfs_super_cmp) 2282 2281 { 2283 2282 if (tcon->status == TID_EXITING) 2284 2283 return 0; 2285 - if (strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE)) 2284 + /* Skip UNC validation when matching DFS superblocks */ 2285 + if (!dfs_super_cmp && strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE)) 2286 2286 return 0; 2287 2287 if (tcon->seal != ctx->seal) 2288 2288 return 0; ··· 2306 2304 spin_lock(&cifs_tcp_ses_lock); 2307 2305 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { 2308 2306 spin_lock(&tcon->tc_lock); 2309 - if (!match_tcon(tcon, ctx)) { 2307 + if (!match_tcon(tcon, ctx, false)) { 2310 2308 spin_unlock(&tcon->tc_lock); 2311 2309 continue; 2312 2310 } ··· 2701 2699 struct cifs_ses *ses; 2702 2700 struct cifs_tcon *tcon; 2703 2701 struct tcon_link *tlink; 2702 + bool dfs_super_cmp; 2704 2703 int rc = 0; 2705 2704 2706 2705 spin_lock(&cifs_tcp_ses_lock); ··· 2716 2713 ses = tcon->ses; 2717 2714 tcp_srv = ses->server; 2718 2715 2716 + dfs_super_cmp = IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && tcp_srv->origin_fullpath; 2717 + 2719 2718 ctx = mnt_data->ctx; 2720 2719 2721 2720 spin_lock(&tcp_srv->srv_lock); 2722 2721 spin_lock(&ses->ses_lock); 2723 2722 spin_lock(&tcon->tc_lock); 2724 - if (!match_server(tcp_srv, ctx) || 2723 + if (!match_server(tcp_srv, ctx, dfs_super_cmp) || 2725 2724 !match_session(ses, ctx) || 2726 - !match_tcon(tcon, ctx) || 2725 + !match_tcon(tcon, ctx, dfs_super_cmp) || 2727 2726 !match_prepath(sb, mnt_data)) { 2728 2727 rc = 0; 2729 2728 goto out; ··· 3182 3177 } 3183 3178 3184 3179 /* Release all succeed connections */ 3185 - static inline void mount_put_conns(struct cifs_mount_ctx *mnt_ctx) 3180 + void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx) 3186 3181 { 3187 3182 int rc = 0; 3188 3183 ··· 3332 3327 return rc; 3333 3328 } 3334 3329 3335 - /* Get connections for tcp, ses and tcon */ 3336 - static int mount_get_conns(struct cifs_mount_ctx *mnt_ctx) 3337 - { 3338 - int rc; 3339 - 3340 - rc = cifs_mount_get_session(mnt_ctx); 3341 - if (rc) 3342 - return rc; 3343 - 3344 - return cifs_mount_get_tcon(mnt_ctx); 3345 - } 3346 - 3347 3330 static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, 3348 3331 struct cifs_tcon *tcon) 3349 3332 { ··· 3357 3364 TLINK_IDLE_EXPIRE); 3358 3365 return 0; 3359 3366 } 3360 - 3361 - #ifdef CONFIG_CIFS_DFS_UPCALL 3362 - /* Get unique dfs connections */ 3363 - static int mount_get_dfs_conns(struct cifs_mount_ctx *mnt_ctx) 3364 - { 3365 - int rc; 3366 - 3367 - mnt_ctx->fs_ctx->nosharesock = true; 3368 - rc = mount_get_conns(mnt_ctx); 3369 - if (mnt_ctx->server) { 3370 - cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__); 3371 - spin_lock(&mnt_ctx->server->srv_lock); 3372 - mnt_ctx->server->is_dfs_conn = true; 3373 - spin_unlock(&mnt_ctx->server->srv_lock); 3374 - } 3375 - return rc; 3376 - } 3377 - 3378 - /* 3379 - * cifs_build_path_to_root returns full path to root when we do not have an 3380 - * existing connection (tcon) 3381 - */ 3382 - static char * 3383 - build_unc_path_to_root(const struct smb3_fs_context *ctx, 3384 - const struct cifs_sb_info *cifs_sb, bool useppath) 3385 - { 3386 - char *full_path, *pos; 3387 - unsigned int pplen = useppath && ctx->prepath ? 3388 - strlen(ctx->prepath) + 1 : 0; 3389 - unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); 3390 - 3391 - if (unc_len > MAX_TREE_SIZE) 3392 - return ERR_PTR(-EINVAL); 3393 - 3394 - full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); 3395 - if (full_path == NULL) 3396 - return ERR_PTR(-ENOMEM); 3397 - 3398 - memcpy(full_path, ctx->UNC, unc_len); 3399 - pos = full_path + unc_len; 3400 - 3401 - if (pplen) { 3402 - *pos = CIFS_DIR_SEP(cifs_sb); 3403 - memcpy(pos + 1, ctx->prepath, pplen); 3404 - pos += pplen; 3405 - } 3406 - 3407 - *pos = '\0'; /* add trailing null */ 3408 - convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); 3409 - cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); 3410 - return full_path; 3411 - } 3412 - #endif 3413 3367 3414 3368 static int 3415 3369 cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, ··· 3410 3470 * 3411 3471 * Return -EREMOTE if it is, otherwise 0 or -errno. 3412 3472 */ 3413 - static int is_path_remote(struct cifs_mount_ctx *mnt_ctx) 3473 + int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx) 3414 3474 { 3415 3475 int rc; 3416 3476 struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ··· 3454 3514 } 3455 3515 3456 3516 #ifdef CONFIG_CIFS_DFS_UPCALL 3457 - static void set_root_ses(struct cifs_mount_ctx *mnt_ctx) 3458 - { 3459 - if (mnt_ctx->ses) { 3460 - spin_lock(&cifs_tcp_ses_lock); 3461 - mnt_ctx->ses->ses_count++; 3462 - spin_unlock(&cifs_tcp_ses_lock); 3463 - dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses); 3464 - } 3465 - mnt_ctx->root_ses = mnt_ctx->ses; 3466 - } 3467 - 3468 - static int is_dfs_mount(struct cifs_mount_ctx *mnt_ctx, bool *isdfs, 3469 - struct dfs_cache_tgt_list *root_tl) 3470 - { 3471 - int rc; 3472 - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 3473 - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 3474 - 3475 - *isdfs = true; 3476 - 3477 - rc = mount_get_conns(mnt_ctx); 3478 - /* 3479 - * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally 3480 - * try to get an DFS referral (even cached) to determine whether it is an DFS mount. 3481 - * 3482 - * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem 3483 - * to respond with PATH_NOT_COVERED to requests that include the prefix. 3484 - */ 3485 - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || 3486 - dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), 3487 - ctx->UNC + 1, NULL, root_tl)) { 3488 - if (rc) 3489 - return rc; 3490 - /* Check if it is fully accessible and then mount it */ 3491 - rc = is_path_remote(mnt_ctx); 3492 - if (!rc) 3493 - *isdfs = false; 3494 - else if (rc != -EREMOTE) 3495 - return rc; 3496 - } 3497 - return 0; 3498 - } 3499 - 3500 - static int connect_dfs_target(struct cifs_mount_ctx *mnt_ctx, const char *full_path, 3501 - const char *ref_path, struct dfs_cache_tgt_iterator *tit) 3502 - { 3503 - int rc; 3504 - struct dfs_info3_param ref = {}; 3505 - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 3506 - 3507 - cifs_dbg(FYI, "%s: full_path=%s ref_path=%s target=%s\n", __func__, full_path, ref_path, 3508 - dfs_cache_get_tgt_name(tit)); 3509 - 3510 - rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref); 3511 - if (rc) 3512 - goto out; 3513 - 3514 - rc = dfs_parse_target_referral(full_path + 1, &ref, mnt_ctx->fs_ctx); 3515 - if (rc) 3516 - goto out; 3517 - 3518 - /* XXX: maybe check if we were actually redirected and avoid reconnecting? */ 3519 - mount_put_conns(mnt_ctx); 3520 - rc = mount_get_dfs_conns(mnt_ctx); 3521 - 3522 - if (!rc) { 3523 - if (cifs_is_referral_server(mnt_ctx->tcon, &ref)) 3524 - set_root_ses(mnt_ctx); 3525 - rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, 3526 - cifs_remap(cifs_sb), ref_path, tit); 3527 - } 3528 - 3529 - out: 3530 - free_dfs_info_param(&ref); 3531 - return rc; 3532 - } 3533 - 3534 - static int connect_dfs_root(struct cifs_mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl) 3535 - { 3536 - int rc; 3537 - char *full_path; 3538 - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 3539 - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 3540 - struct dfs_cache_tgt_iterator *tit; 3541 - 3542 - /* Put initial connections as they might be shared with other mounts. We need unique dfs 3543 - * connections per mount to properly failover, so mount_get_dfs_conns() must be used from 3544 - * now on. 3545 - */ 3546 - mount_put_conns(mnt_ctx); 3547 - mount_get_dfs_conns(mnt_ctx); 3548 - set_root_ses(mnt_ctx); 3549 - 3550 - full_path = build_unc_path_to_root(ctx, cifs_sb, true); 3551 - if (IS_ERR(full_path)) 3552 - return PTR_ERR(full_path); 3553 - 3554 - mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls, 3555 - cifs_remap(cifs_sb)); 3556 - if (IS_ERR(mnt_ctx->origin_fullpath)) { 3557 - rc = PTR_ERR(mnt_ctx->origin_fullpath); 3558 - mnt_ctx->origin_fullpath = NULL; 3559 - goto out; 3560 - } 3561 - 3562 - /* Try all dfs root targets */ 3563 - for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl); 3564 - tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) { 3565 - rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit); 3566 - if (!rc) { 3567 - mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL); 3568 - if (!mnt_ctx->leaf_fullpath) 3569 - rc = -ENOMEM; 3570 - break; 3571 - } 3572 - } 3573 - 3574 - out: 3575 - kfree(full_path); 3576 - return rc; 3577 - } 3578 - 3579 - static int __follow_dfs_link(struct cifs_mount_ctx *mnt_ctx) 3580 - { 3581 - int rc; 3582 - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 3583 - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 3584 - char *full_path; 3585 - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); 3586 - struct dfs_cache_tgt_iterator *tit; 3587 - 3588 - full_path = build_unc_path_to_root(ctx, cifs_sb, true); 3589 - if (IS_ERR(full_path)) 3590 - return PTR_ERR(full_path); 3591 - 3592 - kfree(mnt_ctx->leaf_fullpath); 3593 - mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, 3594 - cifs_remap(cifs_sb)); 3595 - if (IS_ERR(mnt_ctx->leaf_fullpath)) { 3596 - rc = PTR_ERR(mnt_ctx->leaf_fullpath); 3597 - mnt_ctx->leaf_fullpath = NULL; 3598 - goto out; 3599 - } 3600 - 3601 - /* Get referral from dfs link */ 3602 - rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, 3603 - cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl); 3604 - if (rc) 3605 - goto out; 3606 - 3607 - /* Try all dfs link targets. If an I/O fails from currently connected DFS target with an 3608 - * error other than STATUS_PATH_NOT_COVERED (-EREMOTE), then retry it from other targets as 3609 - * specified in MS-DFSC "3.1.5.2 I/O Operation to Target Fails with an Error Other Than 3610 - * STATUS_PATH_NOT_COVERED." 3611 - */ 3612 - for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl); 3613 - tit; tit = dfs_cache_get_next_tgt(&tl, tit)) { 3614 - rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit); 3615 - if (!rc) { 3616 - rc = is_path_remote(mnt_ctx); 3617 - if (!rc || rc == -EREMOTE) 3618 - break; 3619 - } 3620 - } 3621 - 3622 - out: 3623 - kfree(full_path); 3624 - dfs_cache_free_tgts(&tl); 3625 - return rc; 3626 - } 3627 - 3628 - static int follow_dfs_link(struct cifs_mount_ctx *mnt_ctx) 3629 - { 3630 - int rc; 3631 - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 3632 - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 3633 - char *full_path; 3634 - int num_links = 0; 3635 - 3636 - full_path = build_unc_path_to_root(ctx, cifs_sb, true); 3637 - if (IS_ERR(full_path)) 3638 - return PTR_ERR(full_path); 3639 - 3640 - kfree(mnt_ctx->origin_fullpath); 3641 - mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, 3642 - cifs_remap(cifs_sb)); 3643 - kfree(full_path); 3644 - 3645 - if (IS_ERR(mnt_ctx->origin_fullpath)) { 3646 - rc = PTR_ERR(mnt_ctx->origin_fullpath); 3647 - mnt_ctx->origin_fullpath = NULL; 3648 - return rc; 3649 - } 3650 - 3651 - do { 3652 - rc = __follow_dfs_link(mnt_ctx); 3653 - if (!rc || rc != -EREMOTE) 3654 - break; 3655 - } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); 3656 - 3657 - return rc; 3658 - } 3659 - 3660 - /* Set up DFS referral paths for failover */ 3661 - static void setup_server_referral_paths(struct cifs_mount_ctx *mnt_ctx) 3662 - { 3663 - struct TCP_Server_Info *server = mnt_ctx->server; 3664 - 3665 - mutex_lock(&server->refpath_lock); 3666 - server->origin_fullpath = mnt_ctx->origin_fullpath; 3667 - server->leaf_fullpath = mnt_ctx->leaf_fullpath; 3668 - server->current_fullpath = mnt_ctx->leaf_fullpath; 3669 - mutex_unlock(&server->refpath_lock); 3670 - mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL; 3671 - } 3672 - 3673 3517 int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) 3674 3518 { 3675 - int rc; 3676 3519 struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; 3677 - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); 3678 3520 bool isdfs; 3521 + int rc; 3679 3522 3680 - rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl); 3523 + uuid_gen(&mnt_ctx.mount_id); 3524 + rc = dfs_mount_share(&mnt_ctx, &isdfs); 3681 3525 if (rc) 3682 3526 goto error; 3683 3527 if (!isdfs) 3684 3528 goto out; 3685 3529 3686 - /* proceed as DFS mount */ 3687 - uuid_gen(&mnt_ctx.mount_id); 3688 - rc = connect_dfs_root(&mnt_ctx, &tl); 3689 - dfs_cache_free_tgts(&tl); 3690 - 3691 - if (rc) 3692 - goto error; 3693 - 3694 - rc = is_path_remote(&mnt_ctx); 3695 - if (rc) 3696 - rc = follow_dfs_link(&mnt_ctx); 3697 - if (rc) 3698 - goto error; 3699 - 3700 - setup_server_referral_paths(&mnt_ctx); 3701 3530 /* 3702 3531 * After reconnecting to a different server, unique ids won't match anymore, so we disable 3703 3532 * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). ··· 3495 3786 dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id); 3496 3787 kfree(mnt_ctx.origin_fullpath); 3497 3788 kfree(mnt_ctx.leaf_fullpath); 3498 - mount_put_conns(&mnt_ctx); 3789 + cifs_mount_put_conns(&mnt_ctx); 3499 3790 return rc; 3500 3791 } 3501 3792 #else ··· 3504 3795 int rc = 0; 3505 3796 struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; 3506 3797 3507 - rc = mount_get_conns(&mnt_ctx); 3798 + rc = cifs_mount_get_session(&mnt_ctx); 3508 3799 if (rc) 3509 3800 goto error; 3510 3801 3511 - if (mnt_ctx.tcon) { 3512 - rc = is_path_remote(&mnt_ctx); 3513 - if (rc == -EREMOTE) 3514 - rc = -EOPNOTSUPP; 3515 - if (rc) 3516 - goto error; 3517 - } 3802 + rc = cifs_mount_get_tcon(&mnt_ctx); 3803 + if (rc) 3804 + goto error; 3805 + 3806 + rc = cifs_is_path_remote(&mnt_ctx); 3807 + if (rc == -EREMOTE) 3808 + rc = -EOPNOTSUPP; 3809 + if (rc) 3810 + goto error; 3518 3811 3519 3812 rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); 3520 3813 if (rc) ··· 3526 3815 return rc; 3527 3816 3528 3817 error: 3529 - mount_put_conns(&mnt_ctx); 3818 + cifs_mount_put_conns(&mnt_ctx); 3530 3819 return rc; 3531 3820 } 3532 3821 #endif
+226
fs/cifs/dfs.c
··· 3 3 * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de> 4 4 */ 5 5 6 + #include <linux/namei.h> 6 7 #include "cifsproto.h" 7 8 #include "cifs_debug.h" 8 9 #include "dns_resolve.h" ··· 52 51 out: 53 52 kfree(path); 54 53 return rc; 54 + } 55 + 56 + /* 57 + * cifs_build_path_to_root returns full path to root when we do not have an 58 + * existing connection (tcon) 59 + */ 60 + static char *build_unc_path_to_root(const struct smb3_fs_context *ctx, 61 + const struct cifs_sb_info *cifs_sb, bool useppath) 62 + { 63 + char *full_path, *pos; 64 + unsigned int pplen = useppath && ctx->prepath ? strlen(ctx->prepath) + 1 : 0; 65 + unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); 66 + 67 + if (unc_len > MAX_TREE_SIZE) 68 + return ERR_PTR(-EINVAL); 69 + 70 + full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); 71 + if (full_path == NULL) 72 + return ERR_PTR(-ENOMEM); 73 + 74 + memcpy(full_path, ctx->UNC, unc_len); 75 + pos = full_path + unc_len; 76 + 77 + if (pplen) { 78 + *pos = CIFS_DIR_SEP(cifs_sb); 79 + memcpy(pos + 1, ctx->prepath, pplen); 80 + pos += pplen; 81 + } 82 + 83 + *pos = '\0'; /* add trailing null */ 84 + convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); 85 + cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); 86 + return full_path; 87 + } 88 + 89 + static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) 90 + { 91 + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 92 + int rc; 93 + 94 + ctx->leaf_fullpath = (char *)full_path; 95 + rc = cifs_mount_get_session(mnt_ctx); 96 + ctx->leaf_fullpath = NULL; 97 + 98 + return rc; 99 + } 100 + 101 + static void set_root_ses(struct cifs_mount_ctx *mnt_ctx) 102 + { 103 + if (mnt_ctx->ses) { 104 + spin_lock(&cifs_tcp_ses_lock); 105 + mnt_ctx->ses->ses_count++; 106 + spin_unlock(&cifs_tcp_ses_lock); 107 + dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses); 108 + } 109 + mnt_ctx->root_ses = mnt_ctx->ses; 110 + } 111 + 112 + static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path, 113 + const struct dfs_cache_tgt_iterator *tit) 114 + { 115 + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 116 + struct dfs_info3_param ref = {}; 117 + int rc; 118 + 119 + rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref); 120 + if (rc) 121 + return rc; 122 + 123 + rc = dfs_parse_target_referral(full_path + 1, &ref, ctx); 124 + if (rc) 125 + goto out; 126 + 127 + cifs_mount_put_conns(mnt_ctx); 128 + rc = get_session(mnt_ctx, ref_path); 129 + if (rc) 130 + goto out; 131 + 132 + if (ref.flags & DFSREF_REFERRAL_SERVER) 133 + set_root_ses(mnt_ctx); 134 + 135 + rc = -EREMOTE; 136 + if (ref.flags & DFSREF_STORAGE_SERVER) { 137 + rc = cifs_mount_get_tcon(mnt_ctx); 138 + if (rc) 139 + goto out; 140 + 141 + /* some servers may not advertise referral capability under ref.flags */ 142 + if (!(ref.flags & DFSREF_REFERRAL_SERVER) && 143 + is_tcon_dfs(mnt_ctx->tcon)) 144 + set_root_ses(mnt_ctx); 145 + 146 + rc = cifs_is_path_remote(mnt_ctx); 147 + } 148 + 149 + out: 150 + free_dfs_info_param(&ref); 151 + return rc; 152 + } 153 + 154 + static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) 155 + { 156 + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 157 + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 158 + char *ref_path = NULL, *full_path = NULL; 159 + struct dfs_cache_tgt_iterator *tit; 160 + struct TCP_Server_Info *server; 161 + char *origin_fullpath = NULL; 162 + int num_links = 0; 163 + int rc; 164 + 165 + ref_path = dfs_get_path(cifs_sb, ctx->UNC); 166 + if (IS_ERR(ref_path)) 167 + return PTR_ERR(ref_path); 168 + 169 + full_path = build_unc_path_to_root(ctx, cifs_sb, true); 170 + if (IS_ERR(full_path)) { 171 + rc = PTR_ERR(full_path); 172 + full_path = NULL; 173 + goto out; 174 + } 175 + 176 + origin_fullpath = kstrdup(full_path, GFP_KERNEL); 177 + if (!origin_fullpath) { 178 + rc = -ENOMEM; 179 + goto out; 180 + } 181 + 182 + do { 183 + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); 184 + 185 + rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl); 186 + if (rc) 187 + break; 188 + 189 + tit = dfs_cache_get_tgt_iterator(&tl); 190 + if (!tit) { 191 + cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__, 192 + ref_path + 1); 193 + rc = -ENOENT; 194 + dfs_cache_free_tgts(&tl); 195 + break; 196 + } 197 + 198 + do { 199 + rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit); 200 + if (!rc) 201 + break; 202 + if (rc == -EREMOTE) { 203 + if (++num_links > MAX_NESTED_LINKS) { 204 + rc = -ELOOP; 205 + break; 206 + } 207 + kfree(ref_path); 208 + kfree(full_path); 209 + ref_path = full_path = NULL; 210 + 211 + full_path = build_unc_path_to_root(ctx, cifs_sb, true); 212 + if (IS_ERR(full_path)) { 213 + rc = PTR_ERR(full_path); 214 + full_path = NULL; 215 + } else { 216 + ref_path = dfs_get_path(cifs_sb, full_path); 217 + if (IS_ERR(ref_path)) { 218 + rc = PTR_ERR(ref_path); 219 + ref_path = NULL; 220 + } 221 + } 222 + break; 223 + } 224 + } while ((tit = dfs_cache_get_next_tgt(&tl, tit))); 225 + dfs_cache_free_tgts(&tl); 226 + } while (rc == -EREMOTE); 227 + 228 + if (!rc) { 229 + server = mnt_ctx->server; 230 + 231 + mutex_lock(&server->refpath_lock); 232 + server->origin_fullpath = origin_fullpath; 233 + server->current_fullpath = server->leaf_fullpath; 234 + mutex_unlock(&server->refpath_lock); 235 + origin_fullpath = NULL; 236 + } 237 + 238 + out: 239 + kfree(origin_fullpath); 240 + kfree(ref_path); 241 + kfree(full_path); 242 + return rc; 243 + } 244 + 245 + int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) 246 + { 247 + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 248 + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 249 + int rc; 250 + 251 + *isdfs = false; 252 + 253 + rc = get_session(mnt_ctx, NULL); 254 + if (rc) 255 + return rc; 256 + mnt_ctx->root_ses = mnt_ctx->ses; 257 + /* 258 + * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally 259 + * try to get an DFS referral (even cached) to determine whether it is an DFS mount. 260 + * 261 + * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem 262 + * to respond with PATH_NOT_COVERED to requests that include the prefix. 263 + */ 264 + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || 265 + dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL)) { 266 + rc = cifs_mount_get_tcon(mnt_ctx); 267 + if (rc) 268 + return rc; 269 + 270 + rc = cifs_is_path_remote(mnt_ctx); 271 + if (!rc || rc != -EREMOTE) 272 + return rc; 273 + } 274 + 275 + *isdfs = true; 276 + set_root_ses(mnt_ctx); 277 + 278 + return __dfs_mount_share(mnt_ctx); 55 279 }
+15
fs/cifs/dfs.h
··· 8 8 9 9 #include "cifsglob.h" 10 10 #include "fs_context.h" 11 + #include "cifs_unicode.h" 11 12 12 13 int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, 13 14 struct smb3_fs_context *ctx); 15 + int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs); 14 16 17 + static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path) 18 + { 19 + return dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb)); 20 + } 21 + 22 + static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *path, 23 + struct dfs_info3_param *ref, struct dfs_cache_tgt_list *tl) 24 + { 25 + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 26 + 27 + return dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, 28 + cifs_remap(cifs_sb), path, ref, tl); 29 + } 15 30 16 31 #endif /* _CIFS_DFS_H */
+3 -11
fs/cifs/dfs_cache.c
··· 1519 1519 1520 1520 spin_lock(&cifs_tcp_ses_lock); 1521 1521 list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { 1522 - spin_lock(&server->srv_lock); 1523 - if (!server->is_dfs_conn) { 1524 - spin_unlock(&server->srv_lock); 1522 + if (!server->leaf_fullpath) 1525 1523 continue; 1526 - } 1527 - spin_unlock(&server->srv_lock); 1528 1524 1529 1525 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 1530 1526 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { ··· 1541 1545 list_del_init(&tcon->ulist); 1542 1546 1543 1547 mutex_lock(&server->refpath_lock); 1544 - if (server->origin_fullpath) { 1545 - if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, 1546 - server->origin_fullpath)) 1547 - __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); 1548 - __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false); 1549 - } 1548 + if (server->leaf_fullpath) 1549 + __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); 1550 1550 mutex_unlock(&server->refpath_lock); 1551 1551 1552 1552 cifs_put_tcon(tcon);
+4
fs/cifs/fs_context.c
··· 316 316 new_ctx->UNC = NULL; 317 317 new_ctx->source = NULL; 318 318 new_ctx->iocharset = NULL; 319 + new_ctx->leaf_fullpath = NULL; 319 320 /* 320 321 * Make sure to stay in sync with smb3_cleanup_fs_context_contents() 321 322 */ ··· 329 328 DUP_CTX_STR(domainname); 330 329 DUP_CTX_STR(nodename); 331 330 DUP_CTX_STR(iocharset); 331 + DUP_CTX_STR(leaf_fullpath); 332 332 333 333 return 0; 334 334 } ··· 1594 1592 ctx->iocharset = NULL; 1595 1593 kfree(ctx->prepath); 1596 1594 ctx->prepath = NULL; 1595 + kfree(ctx->leaf_fullpath); 1596 + ctx->leaf_fullpath = NULL; 1597 1597 } 1598 1598 1599 1599 void
+1
fs/cifs/fs_context.h
··· 264 264 __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ 265 265 bool rootfs:1; /* if it's a SMB root file system */ 266 266 bool witness:1; /* use witness protocol */ 267 + char *leaf_fullpath; 267 268 }; 268 269 269 270 extern const struct fs_parameter_spec smb3_fs_parameters[];