PageRenderTime 90ms CodeModel.GetById 63ms RepoModel.GetById 1ms app.codeStats 0ms

/fs/cifs/cifs_dfs_ref.c

https://gitlab.com/freesoftware/linux
C | 425 lines | 284 code | 55 blank | 86 comment | 42 complexity | 07808a4912075d6792e0b78c27f654fa MD5 | raw file
  1. /*
  2. * Contains the CIFS DFS referral mounting routines used for handling
  3. * traversal via DFS junction point
  4. *
  5. * Copyright (c) 2007 Igor Mammedov
  6. * Copyright (C) International Business Machines Corp., 2008
  7. * Author(s): Igor Mammedov (niallain@gmail.com)
  8. * Steve French (sfrench@us.ibm.com)
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version
  12. * 2 of the License, or (at your option) any later version.
  13. */
  14. #include <linux/dcache.h>
  15. #include <linux/mount.h>
  16. #include <linux/namei.h>
  17. #include <linux/slab.h>
  18. #include <linux/vfs.h>
  19. #include <linux/fs.h>
  20. #include <linux/inet.h>
  21. #include "cifsglob.h"
  22. #include "cifsproto.h"
  23. #include "cifsfs.h"
  24. #include "dns_resolve.h"
  25. #include "cifs_debug.h"
  26. #include "cifs_unicode.h"
  27. #include "dfs_cache.h"
  28. static LIST_HEAD(cifs_dfs_automount_list);
  29. static void cifs_dfs_expire_automounts(struct work_struct *work);
  30. static DECLARE_DELAYED_WORK(cifs_dfs_automount_task,
  31. cifs_dfs_expire_automounts);
  32. static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ;
  33. static void cifs_dfs_expire_automounts(struct work_struct *work)
  34. {
  35. struct list_head *list = &cifs_dfs_automount_list;
  36. mark_mounts_for_expiry(list);
  37. if (!list_empty(list))
  38. schedule_delayed_work(&cifs_dfs_automount_task,
  39. cifs_dfs_mountpoint_expiry_timeout);
  40. }
  41. void cifs_dfs_release_automount_timer(void)
  42. {
  43. BUG_ON(!list_empty(&cifs_dfs_automount_list));
  44. cancel_delayed_work_sync(&cifs_dfs_automount_task);
  45. }
  46. /**
  47. * cifs_build_devname - build a devicename from a UNC and optional prepath
  48. * @nodename: pointer to UNC string
  49. * @prepath: pointer to prefixpath (or NULL if there isn't one)
  50. *
  51. * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer
  52. * big enough to hold the final thing. Copy the UNC from the nodename, and
  53. * concatenate the prepath onto the end of it if there is one.
  54. *
  55. * Returns pointer to the built string, or a ERR_PTR. Caller is responsible
  56. * for freeing the returned string.
  57. */
  58. static char *
  59. cifs_build_devname(char *nodename, const char *prepath)
  60. {
  61. size_t pplen;
  62. size_t unclen;
  63. char *dev;
  64. char *pos;
  65. /* skip over any preceding delimiters */
  66. nodename += strspn(nodename, "\\");
  67. if (!*nodename)
  68. return ERR_PTR(-EINVAL);
  69. /* get length of UNC and set pos to last char */
  70. unclen = strlen(nodename);
  71. pos = nodename + unclen - 1;
  72. /* trim off any trailing delimiters */
  73. while (*pos == '\\') {
  74. --pos;
  75. --unclen;
  76. }
  77. /* allocate a buffer:
  78. * +2 for preceding "//"
  79. * +1 for delimiter between UNC and prepath
  80. * +1 for trailing NULL
  81. */
  82. pplen = prepath ? strlen(prepath) : 0;
  83. dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL);
  84. if (!dev)
  85. return ERR_PTR(-ENOMEM);
  86. pos = dev;
  87. /* add the initial "//" */
  88. *pos = '/';
  89. ++pos;
  90. *pos = '/';
  91. ++pos;
  92. /* copy in the UNC portion from referral */
  93. memcpy(pos, nodename, unclen);
  94. pos += unclen;
  95. /* copy the prefixpath remainder (if there is one) */
  96. if (pplen) {
  97. *pos = '/';
  98. ++pos;
  99. memcpy(pos, prepath, pplen);
  100. pos += pplen;
  101. }
  102. /* NULL terminator */
  103. *pos = '\0';
  104. convert_delimiter(dev, '/');
  105. return dev;
  106. }
  107. /**
  108. * cifs_compose_mount_options - creates mount options for refferral
  109. * @sb_mountdata: parent/root DFS mount options (template)
  110. * @fullpath: full path in UNC format
  111. * @ref: server's referral
  112. * @devname: optional pointer for saving device name
  113. *
  114. * creates mount options for submount based on template options sb_mountdata
  115. * and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
  116. *
  117. * Returns: pointer to new mount options or ERR_PTR.
  118. * Caller is responcible for freeing retunrned value if it is not error.
  119. */
  120. char *cifs_compose_mount_options(const char *sb_mountdata,
  121. const char *fullpath,
  122. const struct dfs_info3_param *ref,
  123. char **devname)
  124. {
  125. int rc;
  126. char *name;
  127. char *mountdata = NULL;
  128. const char *prepath = NULL;
  129. int md_len;
  130. char *tkn_e;
  131. char *srvIP = NULL;
  132. char sep = ',';
  133. int off, noff;
  134. if (sb_mountdata == NULL)
  135. return ERR_PTR(-EINVAL);
  136. if (strlen(fullpath) - ref->path_consumed) {
  137. prepath = fullpath + ref->path_consumed;
  138. /* skip initial delimiter */
  139. if (*prepath == '/' || *prepath == '\\')
  140. prepath++;
  141. }
  142. name = cifs_build_devname(ref->node_name, prepath);
  143. if (IS_ERR(name)) {
  144. rc = PTR_ERR(name);
  145. name = NULL;
  146. goto compose_mount_options_err;
  147. }
  148. rc = dns_resolve_server_name_to_ip(name, &srvIP);
  149. if (rc < 0) {
  150. cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
  151. __func__, name, rc);
  152. goto compose_mount_options_err;
  153. }
  154. /*
  155. * In most cases, we'll be building a shorter string than the original,
  156. * but we do have to assume that the address in the ip= option may be
  157. * much longer than the original. Add the max length of an address
  158. * string to the length of the original string to allow for worst case.
  159. */
  160. md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
  161. mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL);
  162. if (mountdata == NULL) {
  163. rc = -ENOMEM;
  164. goto compose_mount_options_err;
  165. }
  166. /* copy all options except of unc,ip,prefixpath */
  167. off = 0;
  168. if (strncmp(sb_mountdata, "sep=", 4) == 0) {
  169. sep = sb_mountdata[4];
  170. strncpy(mountdata, sb_mountdata, 5);
  171. off += 5;
  172. }
  173. do {
  174. tkn_e = strchr(sb_mountdata + off, sep);
  175. if (tkn_e == NULL)
  176. noff = strlen(sb_mountdata + off);
  177. else
  178. noff = tkn_e - (sb_mountdata + off) + 1;
  179. if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) {
  180. off += noff;
  181. continue;
  182. }
  183. if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) {
  184. off += noff;
  185. continue;
  186. }
  187. if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) {
  188. off += noff;
  189. continue;
  190. }
  191. strncat(mountdata, sb_mountdata + off, noff);
  192. off += noff;
  193. } while (tkn_e);
  194. strcat(mountdata, sb_mountdata + off);
  195. mountdata[md_len] = '\0';
  196. /* copy new IP and ref share name */
  197. if (mountdata[strlen(mountdata) - 1] != sep)
  198. strncat(mountdata, &sep, 1);
  199. strcat(mountdata, "ip=");
  200. strcat(mountdata, srvIP);
  201. if (devname)
  202. *devname = name;
  203. /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
  204. /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
  205. compose_mount_options_out:
  206. kfree(srvIP);
  207. return mountdata;
  208. compose_mount_options_err:
  209. kfree(mountdata);
  210. mountdata = ERR_PTR(rc);
  211. kfree(name);
  212. goto compose_mount_options_out;
  213. }
  214. /**
  215. * cifs_dfs_do_refmount - mounts specified path using provided refferal
  216. * @cifs_sb: parent/root superblock
  217. * @fullpath: full path in UNC format
  218. * @ref: server's referral
  219. */
  220. static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
  221. struct cifs_sb_info *cifs_sb,
  222. const char *fullpath, const struct dfs_info3_param *ref)
  223. {
  224. struct vfsmount *mnt;
  225. char *mountdata;
  226. char *devname;
  227. /*
  228. * Always pass down the DFS full path to smb3_do_mount() so we
  229. * can use it later for failover.
  230. */
  231. devname = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL);
  232. if (!devname)
  233. return ERR_PTR(-ENOMEM);
  234. convert_delimiter(devname, '/');
  235. /* strip first '\' from fullpath */
  236. mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
  237. fullpath + 1, ref, NULL);
  238. if (IS_ERR(mountdata)) {
  239. kfree(devname);
  240. return (struct vfsmount *)mountdata;
  241. }
  242. mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata);
  243. kfree(mountdata);
  244. kfree(devname);
  245. return mnt;
  246. }
  247. static void dump_referral(const struct dfs_info3_param *ref)
  248. {
  249. cifs_dbg(FYI, "DFS: ref path: %s\n", ref->path_name);
  250. cifs_dbg(FYI, "DFS: node path: %s\n", ref->node_name);
  251. cifs_dbg(FYI, "DFS: fl: %d, srv_type: %d\n",
  252. ref->flags, ref->server_type);
  253. cifs_dbg(FYI, "DFS: ref_flags: %d, path_consumed: %d\n",
  254. ref->ref_flag, ref->path_consumed);
  255. }
  256. /*
  257. * Create a vfsmount that we can automount
  258. */
  259. static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
  260. {
  261. struct dfs_info3_param referral = {0};
  262. struct cifs_sb_info *cifs_sb;
  263. struct cifs_ses *ses;
  264. struct cifs_tcon *tcon;
  265. char *full_path, *root_path;
  266. unsigned int xid;
  267. int len;
  268. int rc;
  269. struct vfsmount *mnt;
  270. cifs_dbg(FYI, "in %s\n", __func__);
  271. BUG_ON(IS_ROOT(mntpt));
  272. /*
  273. * The MSDFS spec states that paths in DFS referral requests and
  274. * responses must be prefixed by a single '\' character instead of
  275. * the double backslashes usually used in the UNC. This function
  276. * gives us the latter, so we must adjust the result.
  277. */
  278. mnt = ERR_PTR(-ENOMEM);
  279. cifs_sb = CIFS_SB(mntpt->d_sb);
  280. if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
  281. mnt = ERR_PTR(-EREMOTE);
  282. goto cdda_exit;
  283. }
  284. /* always use tree name prefix */
  285. full_path = build_path_from_dentry_optional_prefix(mntpt, true);
  286. if (full_path == NULL)
  287. goto cdda_exit;
  288. cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
  289. if (!cifs_sb_master_tlink(cifs_sb)) {
  290. cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__);
  291. goto free_full_path;
  292. }
  293. tcon = cifs_sb_master_tcon(cifs_sb);
  294. if (!tcon) {
  295. cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__);
  296. goto free_full_path;
  297. }
  298. root_path = kstrdup(tcon->treeName, GFP_KERNEL);
  299. if (!root_path) {
  300. mnt = ERR_PTR(-ENOMEM);
  301. goto free_full_path;
  302. }
  303. cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path);
  304. ses = tcon->ses;
  305. xid = get_xid();
  306. /*
  307. * If DFS root has been expired, then unconditionally fetch it again to
  308. * refresh DFS referral cache.
  309. */
  310. rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
  311. root_path + 1, NULL, NULL);
  312. if (!rc) {
  313. rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
  314. cifs_remap(cifs_sb), full_path + 1,
  315. &referral, NULL);
  316. }
  317. free_xid(xid);
  318. if (rc) {
  319. mnt = ERR_PTR(rc);
  320. goto free_root_path;
  321. }
  322. dump_referral(&referral);
  323. len = strlen(referral.node_name);
  324. if (len < 2) {
  325. cifs_dbg(VFS, "%s: Net Address path too short: %s\n",
  326. __func__, referral.node_name);
  327. mnt = ERR_PTR(-EINVAL);
  328. goto free_dfs_ref;
  329. }
  330. /*
  331. * cifs_mount() will retry every available node server in case
  332. * of failures.
  333. */
  334. mnt = cifs_dfs_do_refmount(mntpt, cifs_sb, full_path, &referral);
  335. cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n", __func__,
  336. referral.node_name, mnt);
  337. free_dfs_ref:
  338. free_dfs_info_param(&referral);
  339. free_root_path:
  340. kfree(root_path);
  341. free_full_path:
  342. kfree(full_path);
  343. cdda_exit:
  344. cifs_dbg(FYI, "leaving %s\n" , __func__);
  345. return mnt;
  346. }
  347. /*
  348. * Attempt to automount the referral
  349. */
  350. struct vfsmount *cifs_dfs_d_automount(struct path *path)
  351. {
  352. struct vfsmount *newmnt;
  353. cifs_dbg(FYI, "in %s\n", __func__);
  354. newmnt = cifs_dfs_do_automount(path->dentry);
  355. if (IS_ERR(newmnt)) {
  356. cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__);
  357. return newmnt;
  358. }
  359. mntget(newmnt); /* prevent immediate expiration */
  360. mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
  361. schedule_delayed_work(&cifs_dfs_automount_task,
  362. cifs_dfs_mountpoint_expiry_timeout);
  363. cifs_dbg(FYI, "leaving %s [ok]\n" , __func__);
  364. return newmnt;
  365. }
  366. const struct inode_operations cifs_dfs_referral_inode_operations = {
  367. };