1*38c8a9a5SSteve French // SPDX-License-Identifier: GPL-2.0 2*38c8a9a5SSteve French /* 3*38c8a9a5SSteve French * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de> 4*38c8a9a5SSteve French */ 5*38c8a9a5SSteve French 6*38c8a9a5SSteve French #include <linux/namei.h> 7*38c8a9a5SSteve French #include "cifsproto.h" 8*38c8a9a5SSteve French #include "cifs_debug.h" 9*38c8a9a5SSteve French #include "dns_resolve.h" 10*38c8a9a5SSteve French #include "fs_context.h" 11*38c8a9a5SSteve French #include "dfs.h" 12*38c8a9a5SSteve French 13*38c8a9a5SSteve French /** 14*38c8a9a5SSteve French * dfs_parse_target_referral - set fs context for dfs target referral 15*38c8a9a5SSteve French * 16*38c8a9a5SSteve French * @full_path: full path in UNC format. 17*38c8a9a5SSteve French * @ref: dfs referral pointer. 18*38c8a9a5SSteve French * @ctx: smb3 fs context pointer. 19*38c8a9a5SSteve French * 20*38c8a9a5SSteve French * Return zero if dfs referral was parsed correctly, otherwise non-zero. 21*38c8a9a5SSteve French */ 22*38c8a9a5SSteve French int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, 23*38c8a9a5SSteve French struct smb3_fs_context *ctx) 24*38c8a9a5SSteve French { 25*38c8a9a5SSteve French int rc; 26*38c8a9a5SSteve French const char *prepath = NULL; 27*38c8a9a5SSteve French char *path; 28*38c8a9a5SSteve French 29*38c8a9a5SSteve French if (!full_path || !*full_path || !ref || !ctx) 30*38c8a9a5SSteve French return -EINVAL; 31*38c8a9a5SSteve French 32*38c8a9a5SSteve French if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0)) 33*38c8a9a5SSteve French return -EINVAL; 34*38c8a9a5SSteve French 35*38c8a9a5SSteve French if (strlen(full_path) - ref->path_consumed) { 36*38c8a9a5SSteve French prepath = full_path + ref->path_consumed; 37*38c8a9a5SSteve French /* skip initial delimiter */ 38*38c8a9a5SSteve French if (*prepath == '/' || *prepath == '\\') 39*38c8a9a5SSteve French prepath++; 40*38c8a9a5SSteve French } 41*38c8a9a5SSteve French 42*38c8a9a5SSteve French path = cifs_build_devname(ref->node_name, prepath); 43*38c8a9a5SSteve French if (IS_ERR(path)) 44*38c8a9a5SSteve French return PTR_ERR(path); 45*38c8a9a5SSteve French 46*38c8a9a5SSteve French rc = smb3_parse_devname(path, ctx); 47*38c8a9a5SSteve French if (rc) 48*38c8a9a5SSteve French goto out; 49*38c8a9a5SSteve French 50*38c8a9a5SSteve French rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL); 51*38c8a9a5SSteve French 52*38c8a9a5SSteve French out: 53*38c8a9a5SSteve French kfree(path); 54*38c8a9a5SSteve French return rc; 55*38c8a9a5SSteve French } 56*38c8a9a5SSteve French 57*38c8a9a5SSteve French /* 58*38c8a9a5SSteve French * cifs_build_path_to_root returns full path to root when we do not have an 59*38c8a9a5SSteve French * existing connection (tcon) 60*38c8a9a5SSteve French */ 61*38c8a9a5SSteve French static char *build_unc_path_to_root(const struct smb3_fs_context *ctx, 62*38c8a9a5SSteve French const struct cifs_sb_info *cifs_sb, bool useppath) 63*38c8a9a5SSteve French { 64*38c8a9a5SSteve French char *full_path, *pos; 65*38c8a9a5SSteve French unsigned int pplen = useppath && ctx->prepath ? strlen(ctx->prepath) + 1 : 0; 66*38c8a9a5SSteve French unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); 67*38c8a9a5SSteve French 68*38c8a9a5SSteve French if (unc_len > MAX_TREE_SIZE) 69*38c8a9a5SSteve French return ERR_PTR(-EINVAL); 70*38c8a9a5SSteve French 71*38c8a9a5SSteve French full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); 72*38c8a9a5SSteve French if (full_path == NULL) 73*38c8a9a5SSteve French return ERR_PTR(-ENOMEM); 74*38c8a9a5SSteve French 75*38c8a9a5SSteve French memcpy(full_path, ctx->UNC, unc_len); 76*38c8a9a5SSteve French pos = full_path + unc_len; 77*38c8a9a5SSteve French 78*38c8a9a5SSteve French if (pplen) { 79*38c8a9a5SSteve French *pos = CIFS_DIR_SEP(cifs_sb); 80*38c8a9a5SSteve French memcpy(pos + 1, ctx->prepath, pplen); 81*38c8a9a5SSteve French pos += pplen; 82*38c8a9a5SSteve French } 83*38c8a9a5SSteve French 84*38c8a9a5SSteve French *pos = '\0'; /* add trailing null */ 85*38c8a9a5SSteve French convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); 86*38c8a9a5SSteve French cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); 87*38c8a9a5SSteve French return full_path; 88*38c8a9a5SSteve French } 89*38c8a9a5SSteve French 90*38c8a9a5SSteve French static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) 91*38c8a9a5SSteve French { 92*38c8a9a5SSteve French struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 93*38c8a9a5SSteve French int rc; 94*38c8a9a5SSteve French 95*38c8a9a5SSteve French ctx->leaf_fullpath = (char *)full_path; 96*38c8a9a5SSteve French rc = cifs_mount_get_session(mnt_ctx); 97*38c8a9a5SSteve French ctx->leaf_fullpath = NULL; 98*38c8a9a5SSteve French 99*38c8a9a5SSteve French return rc; 100*38c8a9a5SSteve French } 101*38c8a9a5SSteve French 102*38c8a9a5SSteve French static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) 103*38c8a9a5SSteve French { 104*38c8a9a5SSteve French struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 105*38c8a9a5SSteve French struct dfs_root_ses *root_ses; 106*38c8a9a5SSteve French struct cifs_ses *ses = mnt_ctx->ses; 107*38c8a9a5SSteve French 108*38c8a9a5SSteve French if (ses) { 109*38c8a9a5SSteve French root_ses = kmalloc(sizeof(*root_ses), GFP_KERNEL); 110*38c8a9a5SSteve French if (!root_ses) 111*38c8a9a5SSteve French return -ENOMEM; 112*38c8a9a5SSteve French 113*38c8a9a5SSteve French INIT_LIST_HEAD(&root_ses->list); 114*38c8a9a5SSteve French 115*38c8a9a5SSteve French spin_lock(&cifs_tcp_ses_lock); 116*38c8a9a5SSteve French ses->ses_count++; 117*38c8a9a5SSteve French spin_unlock(&cifs_tcp_ses_lock); 118*38c8a9a5SSteve French root_ses->ses = ses; 119*38c8a9a5SSteve French list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list); 120*38c8a9a5SSteve French } 121*38c8a9a5SSteve French ctx->dfs_root_ses = ses; 122*38c8a9a5SSteve French return 0; 123*38c8a9a5SSteve French } 124*38c8a9a5SSteve French 125*38c8a9a5SSteve French static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path, 126*38c8a9a5SSteve French const struct dfs_cache_tgt_iterator *tit) 127*38c8a9a5SSteve French { 128*38c8a9a5SSteve French struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 129*38c8a9a5SSteve French struct dfs_info3_param ref = {}; 130*38c8a9a5SSteve French bool is_refsrv; 131*38c8a9a5SSteve French int rc, rc2; 132*38c8a9a5SSteve French 133*38c8a9a5SSteve French rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref); 134*38c8a9a5SSteve French if (rc) 135*38c8a9a5SSteve French return rc; 136*38c8a9a5SSteve French 137*38c8a9a5SSteve French rc = dfs_parse_target_referral(full_path + 1, &ref, ctx); 138*38c8a9a5SSteve French if (rc) 139*38c8a9a5SSteve French goto out; 140*38c8a9a5SSteve French 141*38c8a9a5SSteve French cifs_mount_put_conns(mnt_ctx); 142*38c8a9a5SSteve French rc = get_session(mnt_ctx, ref_path); 143*38c8a9a5SSteve French if (rc) 144*38c8a9a5SSteve French goto out; 145*38c8a9a5SSteve French 146*38c8a9a5SSteve French is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER); 147*38c8a9a5SSteve French 148*38c8a9a5SSteve French rc = -EREMOTE; 149*38c8a9a5SSteve French if (ref.flags & DFSREF_STORAGE_SERVER) { 150*38c8a9a5SSteve French rc = cifs_mount_get_tcon(mnt_ctx); 151*38c8a9a5SSteve French if (rc) 152*38c8a9a5SSteve French goto out; 153*38c8a9a5SSteve French 154*38c8a9a5SSteve French /* some servers may not advertise referral capability under ref.flags */ 155*38c8a9a5SSteve French is_refsrv |= is_tcon_dfs(mnt_ctx->tcon); 156*38c8a9a5SSteve French 157*38c8a9a5SSteve French rc = cifs_is_path_remote(mnt_ctx); 158*38c8a9a5SSteve French } 159*38c8a9a5SSteve French 160*38c8a9a5SSteve French dfs_cache_noreq_update_tgthint(ref_path + 1, tit); 161*38c8a9a5SSteve French 162*38c8a9a5SSteve French if (rc == -EREMOTE && is_refsrv) { 163*38c8a9a5SSteve French rc2 = add_root_smb_session(mnt_ctx); 164*38c8a9a5SSteve French if (rc2) 165*38c8a9a5SSteve French rc = rc2; 166*38c8a9a5SSteve French } 167*38c8a9a5SSteve French 168*38c8a9a5SSteve French out: 169*38c8a9a5SSteve French free_dfs_info_param(&ref); 170*38c8a9a5SSteve French return rc; 171*38c8a9a5SSteve French } 172*38c8a9a5SSteve French 173*38c8a9a5SSteve French static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) 174*38c8a9a5SSteve French { 175*38c8a9a5SSteve French struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 176*38c8a9a5SSteve French struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 177*38c8a9a5SSteve French char *ref_path = NULL, *full_path = NULL; 178*38c8a9a5SSteve French struct dfs_cache_tgt_iterator *tit; 179*38c8a9a5SSteve French struct TCP_Server_Info *server; 180*38c8a9a5SSteve French struct cifs_tcon *tcon; 181*38c8a9a5SSteve French char *origin_fullpath = NULL; 182*38c8a9a5SSteve French int num_links = 0; 183*38c8a9a5SSteve French int rc; 184*38c8a9a5SSteve French 185*38c8a9a5SSteve French ref_path = dfs_get_path(cifs_sb, ctx->UNC); 186*38c8a9a5SSteve French if (IS_ERR(ref_path)) 187*38c8a9a5SSteve French return PTR_ERR(ref_path); 188*38c8a9a5SSteve French 189*38c8a9a5SSteve French full_path = build_unc_path_to_root(ctx, cifs_sb, true); 190*38c8a9a5SSteve French if (IS_ERR(full_path)) { 191*38c8a9a5SSteve French rc = PTR_ERR(full_path); 192*38c8a9a5SSteve French full_path = NULL; 193*38c8a9a5SSteve French goto out; 194*38c8a9a5SSteve French } 195*38c8a9a5SSteve French 196*38c8a9a5SSteve French origin_fullpath = kstrdup(full_path, GFP_KERNEL); 197*38c8a9a5SSteve French if (!origin_fullpath) { 198*38c8a9a5SSteve French rc = -ENOMEM; 199*38c8a9a5SSteve French goto out; 200*38c8a9a5SSteve French } 201*38c8a9a5SSteve French 202*38c8a9a5SSteve French do { 203*38c8a9a5SSteve French struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); 204*38c8a9a5SSteve French 205*38c8a9a5SSteve French rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl); 206*38c8a9a5SSteve French if (rc) 207*38c8a9a5SSteve French break; 208*38c8a9a5SSteve French 209*38c8a9a5SSteve French tit = dfs_cache_get_tgt_iterator(&tl); 210*38c8a9a5SSteve French if (!tit) { 211*38c8a9a5SSteve French cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__, 212*38c8a9a5SSteve French ref_path + 1); 213*38c8a9a5SSteve French rc = -ENOENT; 214*38c8a9a5SSteve French dfs_cache_free_tgts(&tl); 215*38c8a9a5SSteve French break; 216*38c8a9a5SSteve French } 217*38c8a9a5SSteve French 218*38c8a9a5SSteve French do { 219*38c8a9a5SSteve French rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit); 220*38c8a9a5SSteve French if (!rc) 221*38c8a9a5SSteve French break; 222*38c8a9a5SSteve French if (rc == -EREMOTE) { 223*38c8a9a5SSteve French if (++num_links > MAX_NESTED_LINKS) { 224*38c8a9a5SSteve French rc = -ELOOP; 225*38c8a9a5SSteve French break; 226*38c8a9a5SSteve French } 227*38c8a9a5SSteve French kfree(ref_path); 228*38c8a9a5SSteve French kfree(full_path); 229*38c8a9a5SSteve French ref_path = full_path = NULL; 230*38c8a9a5SSteve French 231*38c8a9a5SSteve French full_path = build_unc_path_to_root(ctx, cifs_sb, true); 232*38c8a9a5SSteve French if (IS_ERR(full_path)) { 233*38c8a9a5SSteve French rc = PTR_ERR(full_path); 234*38c8a9a5SSteve French full_path = NULL; 235*38c8a9a5SSteve French } else { 236*38c8a9a5SSteve French ref_path = dfs_get_path(cifs_sb, full_path); 237*38c8a9a5SSteve French if (IS_ERR(ref_path)) { 238*38c8a9a5SSteve French rc = PTR_ERR(ref_path); 239*38c8a9a5SSteve French ref_path = NULL; 240*38c8a9a5SSteve French } 241*38c8a9a5SSteve French } 242*38c8a9a5SSteve French break; 243*38c8a9a5SSteve French } 244*38c8a9a5SSteve French } while ((tit = dfs_cache_get_next_tgt(&tl, tit))); 245*38c8a9a5SSteve French dfs_cache_free_tgts(&tl); 246*38c8a9a5SSteve French } while (rc == -EREMOTE); 247*38c8a9a5SSteve French 248*38c8a9a5SSteve French if (!rc) { 249*38c8a9a5SSteve French server = mnt_ctx->server; 250*38c8a9a5SSteve French tcon = mnt_ctx->tcon; 251*38c8a9a5SSteve French 252*38c8a9a5SSteve French mutex_lock(&server->refpath_lock); 253*38c8a9a5SSteve French spin_lock(&server->srv_lock); 254*38c8a9a5SSteve French if (!server->origin_fullpath) { 255*38c8a9a5SSteve French server->origin_fullpath = origin_fullpath; 256*38c8a9a5SSteve French origin_fullpath = NULL; 257*38c8a9a5SSteve French } 258*38c8a9a5SSteve French spin_unlock(&server->srv_lock); 259*38c8a9a5SSteve French mutex_unlock(&server->refpath_lock); 260*38c8a9a5SSteve French 261*38c8a9a5SSteve French if (list_empty(&tcon->dfs_ses_list)) { 262*38c8a9a5SSteve French list_replace_init(&mnt_ctx->dfs_ses_list, 263*38c8a9a5SSteve French &tcon->dfs_ses_list); 264*38c8a9a5SSteve French queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, 265*38c8a9a5SSteve French dfs_cache_get_ttl() * HZ); 266*38c8a9a5SSteve French } else { 267*38c8a9a5SSteve French dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list); 268*38c8a9a5SSteve French } 269*38c8a9a5SSteve French } 270*38c8a9a5SSteve French 271*38c8a9a5SSteve French out: 272*38c8a9a5SSteve French kfree(origin_fullpath); 273*38c8a9a5SSteve French kfree(ref_path); 274*38c8a9a5SSteve French kfree(full_path); 275*38c8a9a5SSteve French return rc; 276*38c8a9a5SSteve French } 277*38c8a9a5SSteve French 278*38c8a9a5SSteve French int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) 279*38c8a9a5SSteve French { 280*38c8a9a5SSteve French struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 281*38c8a9a5SSteve French struct cifs_ses *ses; 282*38c8a9a5SSteve French char *source = ctx->source; 283*38c8a9a5SSteve French bool nodfs = ctx->nodfs; 284*38c8a9a5SSteve French int rc; 285*38c8a9a5SSteve French 286*38c8a9a5SSteve French *isdfs = false; 287*38c8a9a5SSteve French /* Temporarily set @ctx->source to NULL as we're not matching DFS 288*38c8a9a5SSteve French * superblocks yet. See cifs_match_super() and match_server(). 289*38c8a9a5SSteve French */ 290*38c8a9a5SSteve French ctx->source = NULL; 291*38c8a9a5SSteve French rc = get_session(mnt_ctx, NULL); 292*38c8a9a5SSteve French if (rc) 293*38c8a9a5SSteve French goto out; 294*38c8a9a5SSteve French 295*38c8a9a5SSteve French ctx->dfs_root_ses = mnt_ctx->ses; 296*38c8a9a5SSteve French /* 297*38c8a9a5SSteve French * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally 298*38c8a9a5SSteve French * try to get an DFS referral (even cached) to determine whether it is an DFS mount. 299*38c8a9a5SSteve French * 300*38c8a9a5SSteve French * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem 301*38c8a9a5SSteve French * to respond with PATH_NOT_COVERED to requests that include the prefix. 302*38c8a9a5SSteve French */ 303*38c8a9a5SSteve French if (!nodfs) { 304*38c8a9a5SSteve French rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL); 305*38c8a9a5SSteve French if (rc) { 306*38c8a9a5SSteve French if (rc != -ENOENT && rc != -EOPNOTSUPP && rc != -EIO) 307*38c8a9a5SSteve French goto out; 308*38c8a9a5SSteve French nodfs = true; 309*38c8a9a5SSteve French } 310*38c8a9a5SSteve French } 311*38c8a9a5SSteve French if (nodfs) { 312*38c8a9a5SSteve French rc = cifs_mount_get_tcon(mnt_ctx); 313*38c8a9a5SSteve French if (!rc) 314*38c8a9a5SSteve French rc = cifs_is_path_remote(mnt_ctx); 315*38c8a9a5SSteve French goto out; 316*38c8a9a5SSteve French } 317*38c8a9a5SSteve French 318*38c8a9a5SSteve French *isdfs = true; 319*38c8a9a5SSteve French /* 320*38c8a9a5SSteve French * Prevent DFS root session of being put in the first call to 321*38c8a9a5SSteve French * cifs_mount_put_conns(). If another DFS root server was not found 322*38c8a9a5SSteve French * while chasing the referrals (@ctx->dfs_root_ses == @ses), then we 323*38c8a9a5SSteve French * can safely put extra refcount of @ses. 324*38c8a9a5SSteve French */ 325*38c8a9a5SSteve French ses = mnt_ctx->ses; 326*38c8a9a5SSteve French mnt_ctx->ses = NULL; 327*38c8a9a5SSteve French mnt_ctx->server = NULL; 328*38c8a9a5SSteve French rc = __dfs_mount_share(mnt_ctx); 329*38c8a9a5SSteve French if (ses == ctx->dfs_root_ses) 330*38c8a9a5SSteve French cifs_put_smb_ses(ses); 331*38c8a9a5SSteve French out: 332*38c8a9a5SSteve French /* 333*38c8a9a5SSteve French * Restore previous value of @ctx->source so DFS superblock can be 334*38c8a9a5SSteve French * matched in cifs_match_super(). 335*38c8a9a5SSteve French */ 336*38c8a9a5SSteve French ctx->source = source; 337*38c8a9a5SSteve French return rc; 338*38c8a9a5SSteve French } 339*38c8a9a5SSteve French 340*38c8a9a5SSteve French /* Update dfs referral path of superblock */ 341*38c8a9a5SSteve French static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, 342*38c8a9a5SSteve French const char *target) 343*38c8a9a5SSteve French { 344*38c8a9a5SSteve French int rc = 0; 345*38c8a9a5SSteve French size_t len = strlen(target); 346*38c8a9a5SSteve French char *refpath, *npath; 347*38c8a9a5SSteve French 348*38c8a9a5SSteve French if (unlikely(len < 2 || *target != '\\')) 349*38c8a9a5SSteve French return -EINVAL; 350*38c8a9a5SSteve French 351*38c8a9a5SSteve French if (target[1] == '\\') { 352*38c8a9a5SSteve French len += 1; 353*38c8a9a5SSteve French refpath = kmalloc(len, GFP_KERNEL); 354*38c8a9a5SSteve French if (!refpath) 355*38c8a9a5SSteve French return -ENOMEM; 356*38c8a9a5SSteve French 357*38c8a9a5SSteve French scnprintf(refpath, len, "%s", target); 358*38c8a9a5SSteve French } else { 359*38c8a9a5SSteve French len += sizeof("\\"); 360*38c8a9a5SSteve French refpath = kmalloc(len, GFP_KERNEL); 361*38c8a9a5SSteve French if (!refpath) 362*38c8a9a5SSteve French return -ENOMEM; 363*38c8a9a5SSteve French 364*38c8a9a5SSteve French scnprintf(refpath, len, "\\%s", target); 365*38c8a9a5SSteve French } 366*38c8a9a5SSteve French 367*38c8a9a5SSteve French npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); 368*38c8a9a5SSteve French kfree(refpath); 369*38c8a9a5SSteve French 370*38c8a9a5SSteve French if (IS_ERR(npath)) { 371*38c8a9a5SSteve French rc = PTR_ERR(npath); 372*38c8a9a5SSteve French } else { 373*38c8a9a5SSteve French mutex_lock(&server->refpath_lock); 374*38c8a9a5SSteve French spin_lock(&server->srv_lock); 375*38c8a9a5SSteve French kfree(server->leaf_fullpath); 376*38c8a9a5SSteve French server->leaf_fullpath = npath; 377*38c8a9a5SSteve French spin_unlock(&server->srv_lock); 378*38c8a9a5SSteve French mutex_unlock(&server->refpath_lock); 379*38c8a9a5SSteve French } 380*38c8a9a5SSteve French return rc; 381*38c8a9a5SSteve French } 382*38c8a9a5SSteve French 383*38c8a9a5SSteve French static int target_share_matches_server(struct TCP_Server_Info *server, char *share, 384*38c8a9a5SSteve French bool *target_match) 385*38c8a9a5SSteve French { 386*38c8a9a5SSteve French int rc = 0; 387*38c8a9a5SSteve French const char *dfs_host; 388*38c8a9a5SSteve French size_t dfs_host_len; 389*38c8a9a5SSteve French 390*38c8a9a5SSteve French *target_match = true; 391*38c8a9a5SSteve French extract_unc_hostname(share, &dfs_host, &dfs_host_len); 392*38c8a9a5SSteve French 393*38c8a9a5SSteve French /* Check if hostnames or addresses match */ 394*38c8a9a5SSteve French cifs_server_lock(server); 395*38c8a9a5SSteve French if (dfs_host_len != strlen(server->hostname) || 396*38c8a9a5SSteve French strncasecmp(dfs_host, server->hostname, dfs_host_len)) { 397*38c8a9a5SSteve French cifs_dbg(FYI, "%s: %.*s doesn't match %s\n", __func__, 398*38c8a9a5SSteve French (int)dfs_host_len, dfs_host, server->hostname); 399*38c8a9a5SSteve French rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); 400*38c8a9a5SSteve French if (rc) 401*38c8a9a5SSteve French cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); 402*38c8a9a5SSteve French } 403*38c8a9a5SSteve French cifs_server_unlock(server); 404*38c8a9a5SSteve French return rc; 405*38c8a9a5SSteve French } 406*38c8a9a5SSteve French 407*38c8a9a5SSteve French static void __tree_connect_ipc(const unsigned int xid, char *tree, 408*38c8a9a5SSteve French struct cifs_sb_info *cifs_sb, 409*38c8a9a5SSteve French struct cifs_ses *ses) 410*38c8a9a5SSteve French { 411*38c8a9a5SSteve French struct TCP_Server_Info *server = ses->server; 412*38c8a9a5SSteve French struct cifs_tcon *tcon = ses->tcon_ipc; 413*38c8a9a5SSteve French int rc; 414*38c8a9a5SSteve French 415*38c8a9a5SSteve French spin_lock(&ses->ses_lock); 416*38c8a9a5SSteve French spin_lock(&ses->chan_lock); 417*38c8a9a5SSteve French if (cifs_chan_needs_reconnect(ses, server) || 418*38c8a9a5SSteve French ses->ses_status != SES_GOOD) { 419*38c8a9a5SSteve French spin_unlock(&ses->chan_lock); 420*38c8a9a5SSteve French spin_unlock(&ses->ses_lock); 421*38c8a9a5SSteve French cifs_server_dbg(FYI, "%s: skipping ipc reconnect due to disconnected ses\n", 422*38c8a9a5SSteve French __func__); 423*38c8a9a5SSteve French return; 424*38c8a9a5SSteve French } 425*38c8a9a5SSteve French spin_unlock(&ses->chan_lock); 426*38c8a9a5SSteve French spin_unlock(&ses->ses_lock); 427*38c8a9a5SSteve French 428*38c8a9a5SSteve French cifs_server_lock(server); 429*38c8a9a5SSteve French scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); 430*38c8a9a5SSteve French cifs_server_unlock(server); 431*38c8a9a5SSteve French 432*38c8a9a5SSteve French rc = server->ops->tree_connect(xid, ses, tree, tcon, 433*38c8a9a5SSteve French cifs_sb->local_nls); 434*38c8a9a5SSteve French cifs_server_dbg(FYI, "%s: tree_reconnect %s: %d\n", __func__, tree, rc); 435*38c8a9a5SSteve French spin_lock(&tcon->tc_lock); 436*38c8a9a5SSteve French if (rc) { 437*38c8a9a5SSteve French tcon->status = TID_NEED_TCON; 438*38c8a9a5SSteve French } else { 439*38c8a9a5SSteve French tcon->status = TID_GOOD; 440*38c8a9a5SSteve French tcon->need_reconnect = false; 441*38c8a9a5SSteve French } 442*38c8a9a5SSteve French spin_unlock(&tcon->tc_lock); 443*38c8a9a5SSteve French } 444*38c8a9a5SSteve French 445*38c8a9a5SSteve French static void tree_connect_ipc(const unsigned int xid, char *tree, 446*38c8a9a5SSteve French struct cifs_sb_info *cifs_sb, 447*38c8a9a5SSteve French struct cifs_tcon *tcon) 448*38c8a9a5SSteve French { 449*38c8a9a5SSteve French struct cifs_ses *ses = tcon->ses; 450*38c8a9a5SSteve French 451*38c8a9a5SSteve French __tree_connect_ipc(xid, tree, cifs_sb, ses); 452*38c8a9a5SSteve French __tree_connect_ipc(xid, tree, cifs_sb, CIFS_DFS_ROOT_SES(ses)); 453*38c8a9a5SSteve French } 454*38c8a9a5SSteve French 455*38c8a9a5SSteve French static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, 456*38c8a9a5SSteve French struct cifs_sb_info *cifs_sb, char *tree, bool islink, 457*38c8a9a5SSteve French struct dfs_cache_tgt_list *tl) 458*38c8a9a5SSteve French { 459*38c8a9a5SSteve French int rc; 460*38c8a9a5SSteve French struct TCP_Server_Info *server = tcon->ses->server; 461*38c8a9a5SSteve French const struct smb_version_operations *ops = server->ops; 462*38c8a9a5SSteve French struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses); 463*38c8a9a5SSteve French char *share = NULL, *prefix = NULL; 464*38c8a9a5SSteve French struct dfs_cache_tgt_iterator *tit; 465*38c8a9a5SSteve French bool target_match; 466*38c8a9a5SSteve French 467*38c8a9a5SSteve French tit = dfs_cache_get_tgt_iterator(tl); 468*38c8a9a5SSteve French if (!tit) { 469*38c8a9a5SSteve French rc = -ENOENT; 470*38c8a9a5SSteve French goto out; 471*38c8a9a5SSteve French } 472*38c8a9a5SSteve French 473*38c8a9a5SSteve French /* Try to tree connect to all dfs targets */ 474*38c8a9a5SSteve French for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { 475*38c8a9a5SSteve French const char *target = dfs_cache_get_tgt_name(tit); 476*38c8a9a5SSteve French struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); 477*38c8a9a5SSteve French 478*38c8a9a5SSteve French kfree(share); 479*38c8a9a5SSteve French kfree(prefix); 480*38c8a9a5SSteve French share = prefix = NULL; 481*38c8a9a5SSteve French 482*38c8a9a5SSteve French /* Check if share matches with tcp ses */ 483*38c8a9a5SSteve French rc = dfs_cache_get_tgt_share(server->leaf_fullpath + 1, tit, &share, &prefix); 484*38c8a9a5SSteve French if (rc) { 485*38c8a9a5SSteve French cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); 486*38c8a9a5SSteve French break; 487*38c8a9a5SSteve French } 488*38c8a9a5SSteve French 489*38c8a9a5SSteve French rc = target_share_matches_server(server, share, &target_match); 490*38c8a9a5SSteve French if (rc) 491*38c8a9a5SSteve French break; 492*38c8a9a5SSteve French if (!target_match) { 493*38c8a9a5SSteve French rc = -EHOSTUNREACH; 494*38c8a9a5SSteve French continue; 495*38c8a9a5SSteve French } 496*38c8a9a5SSteve French 497*38c8a9a5SSteve French dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit); 498*38c8a9a5SSteve French tree_connect_ipc(xid, tree, cifs_sb, tcon); 499*38c8a9a5SSteve French 500*38c8a9a5SSteve French scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); 501*38c8a9a5SSteve French if (!islink) { 502*38c8a9a5SSteve French rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); 503*38c8a9a5SSteve French break; 504*38c8a9a5SSteve French } 505*38c8a9a5SSteve French 506*38c8a9a5SSteve French /* 507*38c8a9a5SSteve French * If no dfs referrals were returned from link target, then just do a TREE_CONNECT 508*38c8a9a5SSteve French * to it. Otherwise, cache the dfs referral and then mark current tcp ses for 509*38c8a9a5SSteve French * reconnect so either the demultiplex thread or the echo worker will reconnect to 510*38c8a9a5SSteve French * newly resolved target. 511*38c8a9a5SSteve French */ 512*38c8a9a5SSteve French if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, 513*38c8a9a5SSteve French NULL, &ntl)) { 514*38c8a9a5SSteve French rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); 515*38c8a9a5SSteve French if (rc) 516*38c8a9a5SSteve French continue; 517*38c8a9a5SSteve French 518*38c8a9a5SSteve French rc = cifs_update_super_prepath(cifs_sb, prefix); 519*38c8a9a5SSteve French } else { 520*38c8a9a5SSteve French /* Target is another dfs share */ 521*38c8a9a5SSteve French rc = update_server_fullpath(server, cifs_sb, target); 522*38c8a9a5SSteve French dfs_cache_free_tgts(tl); 523*38c8a9a5SSteve French 524*38c8a9a5SSteve French if (!rc) { 525*38c8a9a5SSteve French rc = -EREMOTE; 526*38c8a9a5SSteve French list_replace_init(&ntl.tl_list, &tl->tl_list); 527*38c8a9a5SSteve French } else 528*38c8a9a5SSteve French dfs_cache_free_tgts(&ntl); 529*38c8a9a5SSteve French } 530*38c8a9a5SSteve French break; 531*38c8a9a5SSteve French } 532*38c8a9a5SSteve French 533*38c8a9a5SSteve French out: 534*38c8a9a5SSteve French kfree(share); 535*38c8a9a5SSteve French kfree(prefix); 536*38c8a9a5SSteve French 537*38c8a9a5SSteve French return rc; 538*38c8a9a5SSteve French } 539*38c8a9a5SSteve French 540*38c8a9a5SSteve French static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, 541*38c8a9a5SSteve French struct cifs_sb_info *cifs_sb, char *tree, bool islink, 542*38c8a9a5SSteve French struct dfs_cache_tgt_list *tl) 543*38c8a9a5SSteve French { 544*38c8a9a5SSteve French int rc; 545*38c8a9a5SSteve French int num_links = 0; 546*38c8a9a5SSteve French struct TCP_Server_Info *server = tcon->ses->server; 547*38c8a9a5SSteve French char *old_fullpath = server->leaf_fullpath; 548*38c8a9a5SSteve French 549*38c8a9a5SSteve French do { 550*38c8a9a5SSteve French rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); 551*38c8a9a5SSteve French if (!rc || rc != -EREMOTE) 552*38c8a9a5SSteve French break; 553*38c8a9a5SSteve French } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); 554*38c8a9a5SSteve French /* 555*38c8a9a5SSteve French * If we couldn't tree connect to any targets from last referral path, then 556*38c8a9a5SSteve French * retry it from newly resolved dfs referral. 557*38c8a9a5SSteve French */ 558*38c8a9a5SSteve French if (rc && server->leaf_fullpath != old_fullpath) 559*38c8a9a5SSteve French cifs_signal_cifsd_for_reconnect(server, true); 560*38c8a9a5SSteve French 561*38c8a9a5SSteve French dfs_cache_free_tgts(tl); 562*38c8a9a5SSteve French return rc; 563*38c8a9a5SSteve French } 564*38c8a9a5SSteve French 565*38c8a9a5SSteve French int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) 566*38c8a9a5SSteve French { 567*38c8a9a5SSteve French int rc; 568*38c8a9a5SSteve French struct TCP_Server_Info *server = tcon->ses->server; 569*38c8a9a5SSteve French const struct smb_version_operations *ops = server->ops; 570*38c8a9a5SSteve French struct super_block *sb = NULL; 571*38c8a9a5SSteve French struct cifs_sb_info *cifs_sb; 572*38c8a9a5SSteve French struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); 573*38c8a9a5SSteve French char *tree; 574*38c8a9a5SSteve French struct dfs_info3_param ref = {0}; 575*38c8a9a5SSteve French 576*38c8a9a5SSteve French /* only send once per connect */ 577*38c8a9a5SSteve French spin_lock(&tcon->tc_lock); 578*38c8a9a5SSteve French if (tcon->status != TID_NEW && 579*38c8a9a5SSteve French tcon->status != TID_NEED_TCON) { 580*38c8a9a5SSteve French spin_unlock(&tcon->tc_lock); 581*38c8a9a5SSteve French return -EHOSTDOWN; 582*38c8a9a5SSteve French } 583*38c8a9a5SSteve French 584*38c8a9a5SSteve French if (tcon->status == TID_GOOD) { 585*38c8a9a5SSteve French spin_unlock(&tcon->tc_lock); 586*38c8a9a5SSteve French return 0; 587*38c8a9a5SSteve French } 588*38c8a9a5SSteve French tcon->status = TID_IN_TCON; 589*38c8a9a5SSteve French spin_unlock(&tcon->tc_lock); 590*38c8a9a5SSteve French 591*38c8a9a5SSteve French tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); 592*38c8a9a5SSteve French if (!tree) { 593*38c8a9a5SSteve French rc = -ENOMEM; 594*38c8a9a5SSteve French goto out; 595*38c8a9a5SSteve French } 596*38c8a9a5SSteve French 597*38c8a9a5SSteve French if (tcon->ipc) { 598*38c8a9a5SSteve French cifs_server_lock(server); 599*38c8a9a5SSteve French scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); 600*38c8a9a5SSteve French cifs_server_unlock(server); 601*38c8a9a5SSteve French rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); 602*38c8a9a5SSteve French goto out; 603*38c8a9a5SSteve French } 604*38c8a9a5SSteve French 605*38c8a9a5SSteve French sb = cifs_get_tcp_super(server); 606*38c8a9a5SSteve French if (IS_ERR(sb)) { 607*38c8a9a5SSteve French rc = PTR_ERR(sb); 608*38c8a9a5SSteve French cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); 609*38c8a9a5SSteve French goto out; 610*38c8a9a5SSteve French } 611*38c8a9a5SSteve French 612*38c8a9a5SSteve French cifs_sb = CIFS_SB(sb); 613*38c8a9a5SSteve French 614*38c8a9a5SSteve French /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ 615*38c8a9a5SSteve French if (!server->leaf_fullpath || 616*38c8a9a5SSteve French dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) { 617*38c8a9a5SSteve French rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls); 618*38c8a9a5SSteve French goto out; 619*38c8a9a5SSteve French } 620*38c8a9a5SSteve French 621*38c8a9a5SSteve French rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK, 622*38c8a9a5SSteve French &tl); 623*38c8a9a5SSteve French free_dfs_info_param(&ref); 624*38c8a9a5SSteve French 625*38c8a9a5SSteve French out: 626*38c8a9a5SSteve French kfree(tree); 627*38c8a9a5SSteve French cifs_put_tcp_super(sb); 628*38c8a9a5SSteve French 629*38c8a9a5SSteve French if (rc) { 630*38c8a9a5SSteve French spin_lock(&tcon->tc_lock); 631*38c8a9a5SSteve French if (tcon->status == TID_IN_TCON) 632*38c8a9a5SSteve French tcon->status = TID_NEED_TCON; 633*38c8a9a5SSteve French spin_unlock(&tcon->tc_lock); 634*38c8a9a5SSteve French } else { 635*38c8a9a5SSteve French spin_lock(&tcon->tc_lock); 636*38c8a9a5SSteve French if (tcon->status == TID_IN_TCON) 637*38c8a9a5SSteve French tcon->status = TID_GOOD; 638*38c8a9a5SSteve French spin_unlock(&tcon->tc_lock); 639*38c8a9a5SSteve French tcon->need_reconnect = false; 640*38c8a9a5SSteve French } 641*38c8a9a5SSteve French 642*38c8a9a5SSteve French return rc; 643*38c8a9a5SSteve French } 644