18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Contains the CIFS DFS referral mounting routines used for handling 48c2ecf20Sopenharmony_ci * traversal via DFS junction point 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2007 Igor Mammedov 78c2ecf20Sopenharmony_ci * Copyright (C) International Business Machines Corp., 2008 88c2ecf20Sopenharmony_ci * Author(s): Igor Mammedov (niallain@gmail.com) 98c2ecf20Sopenharmony_ci * Steve French (sfrench@us.ibm.com) 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/dcache.h> 138c2ecf20Sopenharmony_ci#include <linux/mount.h> 148c2ecf20Sopenharmony_ci#include <linux/namei.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/vfs.h> 178c2ecf20Sopenharmony_ci#include <linux/fs.h> 188c2ecf20Sopenharmony_ci#include <linux/inet.h> 198c2ecf20Sopenharmony_ci#include "cifsglob.h" 208c2ecf20Sopenharmony_ci#include "cifsproto.h" 218c2ecf20Sopenharmony_ci#include "cifsfs.h" 228c2ecf20Sopenharmony_ci#include "dns_resolve.h" 238c2ecf20Sopenharmony_ci#include "cifs_debug.h" 248c2ecf20Sopenharmony_ci#include "cifs_unicode.h" 258c2ecf20Sopenharmony_ci#include "dfs_cache.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic LIST_HEAD(cifs_dfs_automount_list); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void cifs_dfs_expire_automounts(struct work_struct *work); 308c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(cifs_dfs_automount_task, 318c2ecf20Sopenharmony_ci cifs_dfs_expire_automounts); 328c2ecf20Sopenharmony_cistatic int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void cifs_dfs_expire_automounts(struct work_struct *work) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct list_head *list = &cifs_dfs_automount_list; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci mark_mounts_for_expiry(list); 398c2ecf20Sopenharmony_ci if (!list_empty(list)) 408c2ecf20Sopenharmony_ci schedule_delayed_work(&cifs_dfs_automount_task, 418c2ecf20Sopenharmony_ci cifs_dfs_mountpoint_expiry_timeout); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_civoid cifs_dfs_release_automount_timer(void) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&cifs_dfs_automount_list)); 478c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&cifs_dfs_automount_task); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/** 518c2ecf20Sopenharmony_ci * cifs_build_devname - build a devicename from a UNC and optional prepath 528c2ecf20Sopenharmony_ci * @nodename: pointer to UNC string 538c2ecf20Sopenharmony_ci * @prepath: pointer to prefixpath (or NULL if there isn't one) 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer 568c2ecf20Sopenharmony_ci * big enough to hold the final thing. Copy the UNC from the nodename, and 578c2ecf20Sopenharmony_ci * concatenate the prepath onto the end of it if there is one. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * Returns pointer to the built string, or a ERR_PTR. Caller is responsible 608c2ecf20Sopenharmony_ci * for freeing the returned string. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic char * 638c2ecf20Sopenharmony_cicifs_build_devname(char *nodename, const char *prepath) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci size_t pplen; 668c2ecf20Sopenharmony_ci size_t unclen; 678c2ecf20Sopenharmony_ci char *dev; 688c2ecf20Sopenharmony_ci char *pos; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* skip over any preceding delimiters */ 718c2ecf20Sopenharmony_ci nodename += strspn(nodename, "\\"); 728c2ecf20Sopenharmony_ci if (!*nodename) 738c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* get length of UNC and set pos to last char */ 768c2ecf20Sopenharmony_ci unclen = strlen(nodename); 778c2ecf20Sopenharmony_ci pos = nodename + unclen - 1; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* trim off any trailing delimiters */ 808c2ecf20Sopenharmony_ci while (*pos == '\\') { 818c2ecf20Sopenharmony_ci --pos; 828c2ecf20Sopenharmony_ci --unclen; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* allocate a buffer: 868c2ecf20Sopenharmony_ci * +2 for preceding "//" 878c2ecf20Sopenharmony_ci * +1 for delimiter between UNC and prepath 888c2ecf20Sopenharmony_ci * +1 for trailing NULL 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci pplen = prepath ? strlen(prepath) : 0; 918c2ecf20Sopenharmony_ci dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL); 928c2ecf20Sopenharmony_ci if (!dev) 938c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci pos = dev; 968c2ecf20Sopenharmony_ci /* add the initial "//" */ 978c2ecf20Sopenharmony_ci *pos = '/'; 988c2ecf20Sopenharmony_ci ++pos; 998c2ecf20Sopenharmony_ci *pos = '/'; 1008c2ecf20Sopenharmony_ci ++pos; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* copy in the UNC portion from referral */ 1038c2ecf20Sopenharmony_ci memcpy(pos, nodename, unclen); 1048c2ecf20Sopenharmony_ci pos += unclen; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* copy the prefixpath remainder (if there is one) */ 1078c2ecf20Sopenharmony_ci if (pplen) { 1088c2ecf20Sopenharmony_ci *pos = '/'; 1098c2ecf20Sopenharmony_ci ++pos; 1108c2ecf20Sopenharmony_ci memcpy(pos, prepath, pplen); 1118c2ecf20Sopenharmony_ci pos += pplen; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* NULL terminator */ 1158c2ecf20Sopenharmony_ci *pos = '\0'; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci convert_delimiter(dev, '/'); 1188c2ecf20Sopenharmony_ci return dev; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/** 1238c2ecf20Sopenharmony_ci * cifs_compose_mount_options - creates mount options for referral 1248c2ecf20Sopenharmony_ci * @sb_mountdata: parent/root DFS mount options (template) 1258c2ecf20Sopenharmony_ci * @fullpath: full path in UNC format 1268c2ecf20Sopenharmony_ci * @ref: optional server's referral 1278c2ecf20Sopenharmony_ci * @devname: optional pointer for saving device name 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * creates mount options for submount based on template options sb_mountdata 1308c2ecf20Sopenharmony_ci * and replacing unc,ip,prefixpath options with ones we've got form ref_unc. 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Returns: pointer to new mount options or ERR_PTR. 1338c2ecf20Sopenharmony_ci * Caller is responsible for freeing returned value if it is not error. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_cichar *cifs_compose_mount_options(const char *sb_mountdata, 1368c2ecf20Sopenharmony_ci const char *fullpath, 1378c2ecf20Sopenharmony_ci const struct dfs_info3_param *ref, 1388c2ecf20Sopenharmony_ci char **devname) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int rc; 1418c2ecf20Sopenharmony_ci char *name; 1428c2ecf20Sopenharmony_ci char *mountdata = NULL; 1438c2ecf20Sopenharmony_ci const char *prepath = NULL; 1448c2ecf20Sopenharmony_ci int md_len; 1458c2ecf20Sopenharmony_ci char *tkn_e; 1468c2ecf20Sopenharmony_ci char *srvIP = NULL; 1478c2ecf20Sopenharmony_ci char sep = ','; 1488c2ecf20Sopenharmony_ci int off, noff; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (sb_mountdata == NULL) 1518c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (ref) { 1548c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0)) 1558c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (strlen(fullpath) - ref->path_consumed) { 1588c2ecf20Sopenharmony_ci prepath = fullpath + ref->path_consumed; 1598c2ecf20Sopenharmony_ci /* skip initial delimiter */ 1608c2ecf20Sopenharmony_ci if (*prepath == '/' || *prepath == '\\') 1618c2ecf20Sopenharmony_ci prepath++; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci name = cifs_build_devname(ref->node_name, prepath); 1658c2ecf20Sopenharmony_ci if (IS_ERR(name)) { 1668c2ecf20Sopenharmony_ci rc = PTR_ERR(name); 1678c2ecf20Sopenharmony_ci name = NULL; 1688c2ecf20Sopenharmony_ci goto compose_mount_options_err; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci } else { 1718c2ecf20Sopenharmony_ci name = cifs_build_devname((char *)fullpath, NULL); 1728c2ecf20Sopenharmony_ci if (IS_ERR(name)) { 1738c2ecf20Sopenharmony_ci rc = PTR_ERR(name); 1748c2ecf20Sopenharmony_ci name = NULL; 1758c2ecf20Sopenharmony_ci goto compose_mount_options_err; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci rc = dns_resolve_server_name_to_ip(name, &srvIP); 1808c2ecf20Sopenharmony_ci if (rc < 0) { 1818c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n", 1828c2ecf20Sopenharmony_ci __func__, name, rc); 1838c2ecf20Sopenharmony_ci goto compose_mount_options_err; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* 1878c2ecf20Sopenharmony_ci * In most cases, we'll be building a shorter string than the original, 1888c2ecf20Sopenharmony_ci * but we do have to assume that the address in the ip= option may be 1898c2ecf20Sopenharmony_ci * much longer than the original. Add the max length of an address 1908c2ecf20Sopenharmony_ci * string to the length of the original string to allow for worst case. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN; 1938c2ecf20Sopenharmony_ci mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL); 1948c2ecf20Sopenharmony_ci if (mountdata == NULL) { 1958c2ecf20Sopenharmony_ci rc = -ENOMEM; 1968c2ecf20Sopenharmony_ci goto compose_mount_options_err; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* copy all options except of unc,ip,prefixpath */ 2008c2ecf20Sopenharmony_ci off = 0; 2018c2ecf20Sopenharmony_ci if (strncmp(sb_mountdata, "sep=", 4) == 0) { 2028c2ecf20Sopenharmony_ci sep = sb_mountdata[4]; 2038c2ecf20Sopenharmony_ci strncpy(mountdata, sb_mountdata, 5); 2048c2ecf20Sopenharmony_ci off += 5; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci do { 2088c2ecf20Sopenharmony_ci tkn_e = strchr(sb_mountdata + off, sep); 2098c2ecf20Sopenharmony_ci if (tkn_e == NULL) 2108c2ecf20Sopenharmony_ci noff = strlen(sb_mountdata + off); 2118c2ecf20Sopenharmony_ci else 2128c2ecf20Sopenharmony_ci noff = tkn_e - (sb_mountdata + off) + 1; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) { 2158c2ecf20Sopenharmony_ci off += noff; 2168c2ecf20Sopenharmony_ci continue; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) { 2198c2ecf20Sopenharmony_ci off += noff; 2208c2ecf20Sopenharmony_ci continue; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) { 2238c2ecf20Sopenharmony_ci off += noff; 2248c2ecf20Sopenharmony_ci continue; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci strncat(mountdata, sb_mountdata + off, noff); 2278c2ecf20Sopenharmony_ci off += noff; 2288c2ecf20Sopenharmony_ci } while (tkn_e); 2298c2ecf20Sopenharmony_ci strcat(mountdata, sb_mountdata + off); 2308c2ecf20Sopenharmony_ci mountdata[md_len] = '\0'; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* copy new IP and ref share name */ 2338c2ecf20Sopenharmony_ci if (mountdata[strlen(mountdata) - 1] != sep) 2348c2ecf20Sopenharmony_ci strncat(mountdata, &sep, 1); 2358c2ecf20Sopenharmony_ci strcat(mountdata, "ip="); 2368c2ecf20Sopenharmony_ci strcat(mountdata, srvIP); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (devname) 2398c2ecf20Sopenharmony_ci *devname = name; 2408c2ecf20Sopenharmony_ci else 2418c2ecf20Sopenharmony_ci kfree(name); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ 2448c2ecf20Sopenharmony_ci /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cicompose_mount_options_out: 2478c2ecf20Sopenharmony_ci kfree(srvIP); 2488c2ecf20Sopenharmony_ci return mountdata; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cicompose_mount_options_err: 2518c2ecf20Sopenharmony_ci kfree(mountdata); 2528c2ecf20Sopenharmony_ci mountdata = ERR_PTR(rc); 2538c2ecf20Sopenharmony_ci kfree(name); 2548c2ecf20Sopenharmony_ci goto compose_mount_options_out; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/** 2588c2ecf20Sopenharmony_ci * cifs_dfs_do_mount - mounts specified path using DFS full path 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * Always pass down @fullpath to smb3_do_mount() so we can use the root server 2618c2ecf20Sopenharmony_ci * to perform failover in case we failed to connect to the first target in the 2628c2ecf20Sopenharmony_ci * referral. 2638c2ecf20Sopenharmony_ci * 2648c2ecf20Sopenharmony_ci * @cifs_sb: parent/root superblock 2658c2ecf20Sopenharmony_ci * @fullpath: full path in UNC format 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_cistatic struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt, 2688c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, 2698c2ecf20Sopenharmony_ci const char *fullpath) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct vfsmount *mnt; 2728c2ecf20Sopenharmony_ci char *mountdata; 2738c2ecf20Sopenharmony_ci char *devname; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci devname = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL); 2768c2ecf20Sopenharmony_ci if (!devname) 2778c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci convert_delimiter(devname, '/'); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* strip first '\' from fullpath */ 2828c2ecf20Sopenharmony_ci mountdata = cifs_compose_mount_options(cifs_sb->mountdata, 2838c2ecf20Sopenharmony_ci fullpath + 1, NULL, NULL); 2848c2ecf20Sopenharmony_ci if (IS_ERR(mountdata)) { 2858c2ecf20Sopenharmony_ci kfree(devname); 2868c2ecf20Sopenharmony_ci return (struct vfsmount *)mountdata; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata); 2908c2ecf20Sopenharmony_ci kfree(mountdata); 2918c2ecf20Sopenharmony_ci kfree(devname); 2928c2ecf20Sopenharmony_ci return mnt; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* 2968c2ecf20Sopenharmony_ci * Create a vfsmount that we can automount 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_cistatic struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb; 3018c2ecf20Sopenharmony_ci struct cifs_ses *ses; 3028c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 3038c2ecf20Sopenharmony_ci char *full_path, *root_path; 3048c2ecf20Sopenharmony_ci unsigned int xid; 3058c2ecf20Sopenharmony_ci int rc; 3068c2ecf20Sopenharmony_ci struct vfsmount *mnt; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci cifs_dbg(FYI, "in %s\n", __func__); 3098c2ecf20Sopenharmony_ci BUG_ON(IS_ROOT(mntpt)); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* 3128c2ecf20Sopenharmony_ci * The MSDFS spec states that paths in DFS referral requests and 3138c2ecf20Sopenharmony_ci * responses must be prefixed by a single '\' character instead of 3148c2ecf20Sopenharmony_ci * the double backslashes usually used in the UNC. This function 3158c2ecf20Sopenharmony_ci * gives us the latter, so we must adjust the result. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci mnt = ERR_PTR(-ENOMEM); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci cifs_sb = CIFS_SB(mntpt->d_sb); 3208c2ecf20Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { 3218c2ecf20Sopenharmony_ci mnt = ERR_PTR(-EREMOTE); 3228c2ecf20Sopenharmony_ci goto cdda_exit; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* always use tree name prefix */ 3268c2ecf20Sopenharmony_ci full_path = build_path_from_dentry_optional_prefix(mntpt, true); 3278c2ecf20Sopenharmony_ci if (full_path == NULL) 3288c2ecf20Sopenharmony_ci goto cdda_exit; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci convert_delimiter(full_path, '\\'); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!cifs_sb_master_tlink(cifs_sb)) { 3358c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__); 3368c2ecf20Sopenharmony_ci goto free_full_path; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci tcon = cifs_sb_master_tcon(cifs_sb); 3408c2ecf20Sopenharmony_ci if (!tcon) { 3418c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__); 3428c2ecf20Sopenharmony_ci goto free_full_path; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci root_path = kstrdup(tcon->treeName, GFP_KERNEL); 3468c2ecf20Sopenharmony_ci if (!root_path) { 3478c2ecf20Sopenharmony_ci mnt = ERR_PTR(-ENOMEM); 3488c2ecf20Sopenharmony_ci goto free_full_path; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ses = tcon->ses; 3538c2ecf20Sopenharmony_ci xid = get_xid(); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * If DFS root has been expired, then unconditionally fetch it again to 3578c2ecf20Sopenharmony_ci * refresh DFS referral cache. 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), 3608c2ecf20Sopenharmony_ci root_path + 1, NULL, NULL); 3618c2ecf20Sopenharmony_ci if (!rc) { 3628c2ecf20Sopenharmony_ci rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, 3638c2ecf20Sopenharmony_ci cifs_remap(cifs_sb), full_path + 1, 3648c2ecf20Sopenharmony_ci NULL, NULL); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci free_xid(xid); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (rc) { 3708c2ecf20Sopenharmony_ci mnt = ERR_PTR(rc); 3718c2ecf20Sopenharmony_ci goto free_root_path; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci /* 3748c2ecf20Sopenharmony_ci * OK - we were able to get and cache a referral for @full_path. 3758c2ecf20Sopenharmony_ci * 3768c2ecf20Sopenharmony_ci * Now, pass it down to cifs_mount() and it will retry every available 3778c2ecf20Sopenharmony_ci * node server in case of failures - no need to do it here. 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path); 3808c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, 3818c2ecf20Sopenharmony_ci full_path + 1, mnt); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cifree_root_path: 3848c2ecf20Sopenharmony_ci kfree(root_path); 3858c2ecf20Sopenharmony_cifree_full_path: 3868c2ecf20Sopenharmony_ci kfree(full_path); 3878c2ecf20Sopenharmony_cicdda_exit: 3888c2ecf20Sopenharmony_ci cifs_dbg(FYI, "leaving %s\n" , __func__); 3898c2ecf20Sopenharmony_ci return mnt; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/* 3938c2ecf20Sopenharmony_ci * Attempt to automount the referral 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_cistruct vfsmount *cifs_dfs_d_automount(struct path *path) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct vfsmount *newmnt; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci cifs_dbg(FYI, "in %s\n", __func__); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci newmnt = cifs_dfs_do_automount(path->dentry); 4028c2ecf20Sopenharmony_ci if (IS_ERR(newmnt)) { 4038c2ecf20Sopenharmony_ci cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); 4048c2ecf20Sopenharmony_ci return newmnt; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci mntget(newmnt); /* prevent immediate expiration */ 4088c2ecf20Sopenharmony_ci mnt_set_expiry(newmnt, &cifs_dfs_automount_list); 4098c2ecf20Sopenharmony_ci schedule_delayed_work(&cifs_dfs_automount_task, 4108c2ecf20Sopenharmony_ci cifs_dfs_mountpoint_expiry_timeout); 4118c2ecf20Sopenharmony_ci cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); 4128c2ecf20Sopenharmony_ci return newmnt; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ciconst struct inode_operations cifs_dfs_referral_inode_operations = { 4168c2ecf20Sopenharmony_ci}; 417