hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/fs/cifs/cifs_dfs_ref.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * Contains the CIFS DFS referral mounting routines used for handling
34 * traversal via DFS junction point
....@@ -6,10 +7,6 @@
67 * Copyright (C) International Business Machines Corp., 2008
78 * Author(s): Igor Mammedov (niallain@gmail.com)
89 * 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.
1310 */
1411
1512 #include <linux/dcache.h>
....@@ -25,6 +22,7 @@
2522 #include "dns_resolve.h"
2623 #include "cifs_debug.h"
2724 #include "cifs_unicode.h"
25
+#include "dfs_cache.h"
2826
2927 static LIST_HEAD(cifs_dfs_automount_list);
3028
....@@ -122,17 +120,17 @@
122120
123121
124122 /**
125
- * cifs_compose_mount_options - creates mount options for refferral
123
+ * cifs_compose_mount_options - creates mount options for referral
126124 * @sb_mountdata: parent/root DFS mount options (template)
127125 * @fullpath: full path in UNC format
128
- * @ref: server's referral
129
- * @devname: pointer for saving device name
126
+ * @ref: optional server's referral
127
+ * @devname: optional pointer for saving device name
130128 *
131129 * creates mount options for submount based on template options sb_mountdata
132130 * and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
133131 *
134132 * Returns: pointer to new mount options or ERR_PTR.
135
- * Caller is responcible for freeing retunrned value if it is not error.
133
+ * Caller is responsible for freeing returned value if it is not error.
136134 */
137135 char *cifs_compose_mount_options(const char *sb_mountdata,
138136 const char *fullpath,
....@@ -140,6 +138,7 @@
140138 char **devname)
141139 {
142140 int rc;
141
+ char *name;
143142 char *mountdata = NULL;
144143 const char *prepath = NULL;
145144 int md_len;
....@@ -151,24 +150,36 @@
151150 if (sb_mountdata == NULL)
152151 return ERR_PTR(-EINVAL);
153152
154
- if (strlen(fullpath) - ref->path_consumed) {
155
- prepath = fullpath + ref->path_consumed;
156
- /* skip initial delimiter */
157
- if (*prepath == '/' || *prepath == '\\')
158
- prepath++;
153
+ if (ref) {
154
+ if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0))
155
+ return ERR_PTR(-EINVAL);
156
+
157
+ if (strlen(fullpath) - ref->path_consumed) {
158
+ prepath = fullpath + ref->path_consumed;
159
+ /* skip initial delimiter */
160
+ if (*prepath == '/' || *prepath == '\\')
161
+ prepath++;
162
+ }
163
+
164
+ name = cifs_build_devname(ref->node_name, prepath);
165
+ if (IS_ERR(name)) {
166
+ rc = PTR_ERR(name);
167
+ name = NULL;
168
+ goto compose_mount_options_err;
169
+ }
170
+ } else {
171
+ name = cifs_build_devname((char *)fullpath, NULL);
172
+ if (IS_ERR(name)) {
173
+ rc = PTR_ERR(name);
174
+ name = NULL;
175
+ goto compose_mount_options_err;
176
+ }
159177 }
160178
161
- *devname = cifs_build_devname(ref->node_name, prepath);
162
- if (IS_ERR(*devname)) {
163
- rc = PTR_ERR(*devname);
164
- *devname = NULL;
165
- goto compose_mount_options_err;
166
- }
167
-
168
- rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
179
+ rc = dns_resolve_server_name_to_ip(name, &srvIP);
169180 if (rc < 0) {
170181 cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
171
- __func__, *devname, rc);
182
+ __func__, name, rc);
172183 goto compose_mount_options_err;
173184 }
174185
....@@ -224,6 +235,11 @@
224235 strcat(mountdata, "ip=");
225236 strcat(mountdata, srvIP);
226237
238
+ if (devname)
239
+ *devname = name;
240
+ else
241
+ kfree(name);
242
+
227243 /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
228244 /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
229245
....@@ -234,47 +250,46 @@
234250 compose_mount_options_err:
235251 kfree(mountdata);
236252 mountdata = ERR_PTR(rc);
237
- kfree(*devname);
238
- *devname = NULL;
253
+ kfree(name);
239254 goto compose_mount_options_out;
240255 }
241256
242257 /**
243
- * cifs_dfs_do_refmount - mounts specified path using provided refferal
258
+ * cifs_dfs_do_mount - mounts specified path using DFS full path
259
+ *
260
+ * Always pass down @fullpath to smb3_do_mount() so we can use the root server
261
+ * to perform failover in case we failed to connect to the first target in the
262
+ * referral.
263
+ *
244264 * @cifs_sb: parent/root superblock
245265 * @fullpath: full path in UNC format
246
- * @ref: server's referral
247266 */
248
-static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
249
- struct cifs_sb_info *cifs_sb,
250
- const char *fullpath, const struct dfs_info3_param *ref)
267
+static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
268
+ struct cifs_sb_info *cifs_sb,
269
+ const char *fullpath)
251270 {
252271 struct vfsmount *mnt;
253272 char *mountdata;
254
- char *devname = NULL;
273
+ char *devname;
274
+
275
+ devname = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL);
276
+ if (!devname)
277
+ return ERR_PTR(-ENOMEM);
278
+
279
+ convert_delimiter(devname, '/');
255280
256281 /* strip first '\' from fullpath */
257282 mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
258
- fullpath + 1, ref, &devname);
259
-
260
- if (IS_ERR(mountdata))
283
+ fullpath + 1, NULL, NULL);
284
+ if (IS_ERR(mountdata)) {
285
+ kfree(devname);
261286 return (struct vfsmount *)mountdata;
287
+ }
262288
263289 mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata);
264290 kfree(mountdata);
265291 kfree(devname);
266292 return mnt;
267
-
268
-}
269
-
270
-static void dump_referral(const struct dfs_info3_param *ref)
271
-{
272
- cifs_dbg(FYI, "DFS: ref path: %s\n", ref->path_name);
273
- cifs_dbg(FYI, "DFS: node path: %s\n", ref->node_name);
274
- cifs_dbg(FYI, "DFS: fl: %d, srv_type: %d\n",
275
- ref->flags, ref->server_type);
276
- cifs_dbg(FYI, "DFS: ref_flags: %d, path_consumed: %d\n",
277
- ref->ref_flag, ref->path_consumed);
278293 }
279294
280295 /*
....@@ -282,16 +297,13 @@
282297 */
283298 static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
284299 {
285
- struct dfs_info3_param *referrals = NULL;
286
- unsigned int num_referrals = 0;
287300 struct cifs_sb_info *cifs_sb;
288301 struct cifs_ses *ses;
289
- char *full_path;
302
+ struct cifs_tcon *tcon;
303
+ char *full_path, *root_path;
290304 unsigned int xid;
291
- int i;
292305 int rc;
293306 struct vfsmount *mnt;
294
- struct tcon_link *tlink;
295307
296308 cifs_dbg(FYI, "in %s\n", __func__);
297309 BUG_ON(IS_ROOT(mntpt));
....@@ -304,54 +316,72 @@
304316 */
305317 mnt = ERR_PTR(-ENOMEM);
306318
319
+ cifs_sb = CIFS_SB(mntpt->d_sb);
320
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
321
+ mnt = ERR_PTR(-EREMOTE);
322
+ goto cdda_exit;
323
+ }
324
+
307325 /* always use tree name prefix */
308326 full_path = build_path_from_dentry_optional_prefix(mntpt, true);
309327 if (full_path == NULL)
310328 goto cdda_exit;
311329
312
- cifs_sb = CIFS_SB(mntpt->d_sb);
313
- tlink = cifs_sb_tlink(cifs_sb);
314
- if (IS_ERR(tlink)) {
315
- mnt = ERR_CAST(tlink);
330
+ convert_delimiter(full_path, '\\');
331
+
332
+ cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
333
+
334
+ if (!cifs_sb_master_tlink(cifs_sb)) {
335
+ cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__);
316336 goto free_full_path;
317337 }
318
- ses = tlink_tcon(tlink)->ses;
319338
320
- xid = get_xid();
321
- rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls,
322
- &num_referrals, &referrals,
323
- cifs_remap(cifs_sb));
324
- free_xid(xid);
325
-
326
- cifs_put_tlink(tlink);
327
-
328
- mnt = ERR_PTR(-ENOENT);
329
- for (i = 0; i < num_referrals; i++) {
330
- int len;
331
- dump_referral(referrals + i);
332
- /* connect to a node */
333
- len = strlen(referrals[i].node_name);
334
- if (len < 2) {
335
- cifs_dbg(VFS, "%s: Net Address path too short: %s\n",
336
- __func__, referrals[i].node_name);
337
- mnt = ERR_PTR(-EINVAL);
338
- break;
339
- }
340
- mnt = cifs_dfs_do_refmount(mntpt, cifs_sb,
341
- full_path, referrals + i);
342
- cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n",
343
- __func__, referrals[i].node_name, mnt);
344
- if (!IS_ERR(mnt))
345
- goto success;
339
+ tcon = cifs_sb_master_tcon(cifs_sb);
340
+ if (!tcon) {
341
+ cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__);
342
+ goto free_full_path;
346343 }
347344
348
- /* no valid submounts were found; return error from get_dfs_path() by
349
- * preference */
350
- if (rc != 0)
351
- mnt = ERR_PTR(rc);
345
+ root_path = kstrdup(tcon->treeName, GFP_KERNEL);
346
+ if (!root_path) {
347
+ mnt = ERR_PTR(-ENOMEM);
348
+ goto free_full_path;
349
+ }
350
+ cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path);
352351
353
-success:
354
- free_dfs_info_array(referrals, num_referrals);
352
+ ses = tcon->ses;
353
+ xid = get_xid();
354
+
355
+ /*
356
+ * If DFS root has been expired, then unconditionally fetch it again to
357
+ * refresh DFS referral cache.
358
+ */
359
+ rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
360
+ root_path + 1, NULL, NULL);
361
+ if (!rc) {
362
+ rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
363
+ cifs_remap(cifs_sb), full_path + 1,
364
+ NULL, NULL);
365
+ }
366
+
367
+ free_xid(xid);
368
+
369
+ if (rc) {
370
+ mnt = ERR_PTR(rc);
371
+ goto free_root_path;
372
+ }
373
+ /*
374
+ * OK - we were able to get and cache a referral for @full_path.
375
+ *
376
+ * Now, pass it down to cifs_mount() and it will retry every available
377
+ * node server in case of failures - no need to do it here.
378
+ */
379
+ mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
380
+ cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__,
381
+ full_path + 1, mnt);
382
+
383
+free_root_path:
384
+ kfree(root_path);
355385 free_full_path:
356386 kfree(full_path);
357387 cdda_exit: