|
3 | 3 | * Copyright (c) 2022 Paulo Alcantara <[email protected]>
|
4 | 4 | */
|
5 | 5 |
|
6 |
| -#include <linux/namei.h> |
7 | 6 | #include "cifsproto.h"
|
8 | 7 | #include "cifs_debug.h"
|
9 | 8 | #include "dns_resolve.h"
|
@@ -96,157 +95,171 @@ static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
|
96 | 95 | return 0;
|
97 | 96 | }
|
98 | 97 |
|
99 |
| -static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path, |
100 |
| - const struct dfs_cache_tgt_iterator *tit) |
| 98 | +static inline int parse_dfs_target(struct smb3_fs_context *ctx, |
| 99 | + struct dfs_ref_walk *rw, |
| 100 | + struct dfs_info3_param *tgt) |
| 101 | +{ |
| 102 | + int rc; |
| 103 | + const char *fpath = ref_walk_fpath(rw) + 1; |
| 104 | + |
| 105 | + rc = ref_walk_get_tgt(rw, tgt); |
| 106 | + if (!rc) |
| 107 | + rc = dfs_parse_target_referral(fpath, tgt, ctx); |
| 108 | + return rc; |
| 109 | +} |
| 110 | + |
| 111 | +static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx, |
| 112 | + struct dfs_info3_param *tgt, |
| 113 | + struct dfs_ref_walk *rw) |
101 | 114 | {
|
102 | 115 | struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
|
103 |
| - struct dfs_info3_param ref = {}; |
104 |
| - bool is_refsrv; |
105 |
| - int rc, rc2; |
| 116 | + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; |
| 117 | + char *ref_path, *full_path; |
| 118 | + int rc; |
106 | 119 |
|
107 |
| - rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref); |
108 |
| - if (rc) |
| 120 | + full_path = smb3_fs_context_fullpath(ctx, CIFS_DIR_SEP(cifs_sb)); |
| 121 | + if (IS_ERR(full_path)) |
| 122 | + return PTR_ERR(full_path); |
| 123 | + |
| 124 | + if (!tgt || (tgt->server_type == DFS_TYPE_LINK && |
| 125 | + DFS_INTERLINK(tgt->flags))) |
| 126 | + ref_path = dfs_get_path(cifs_sb, ctx->UNC); |
| 127 | + else |
| 128 | + ref_path = dfs_get_path(cifs_sb, full_path); |
| 129 | + if (IS_ERR(ref_path)) { |
| 130 | + rc = PTR_ERR(ref_path); |
| 131 | + kfree(full_path); |
109 | 132 | return rc;
|
| 133 | + } |
| 134 | + ref_walk_path(rw) = ref_path; |
| 135 | + ref_walk_fpath(rw) = full_path; |
| 136 | + return 0; |
| 137 | +} |
110 | 138 |
|
111 |
| - rc = dfs_parse_target_referral(full_path + 1, &ref, ctx); |
112 |
| - if (rc) |
113 |
| - goto out; |
| 139 | +static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, |
| 140 | + struct dfs_ref_walk *rw) |
| 141 | +{ |
| 142 | + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; |
| 143 | + struct dfs_info3_param tgt = {}; |
| 144 | + bool is_refsrv; |
| 145 | + int rc = -ENOENT; |
114 | 146 |
|
115 |
| - cifs_mount_put_conns(mnt_ctx); |
116 |
| - rc = get_session(mnt_ctx, ref_path); |
117 |
| - if (rc) |
118 |
| - goto out; |
| 147 | +again: |
| 148 | + do { |
| 149 | + if (ref_walk_empty(rw)) { |
| 150 | + rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1, |
| 151 | + NULL, ref_walk_tl(rw)); |
| 152 | + if (rc) { |
| 153 | + rc = cifs_mount_get_tcon(mnt_ctx); |
| 154 | + if (!rc) |
| 155 | + rc = cifs_is_path_remote(mnt_ctx); |
| 156 | + continue; |
| 157 | + } |
| 158 | + if (!ref_walk_num_tgts(rw)) { |
| 159 | + rc = -ENOENT; |
| 160 | + continue; |
| 161 | + } |
| 162 | + } |
119 | 163 |
|
120 |
| - is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER); |
| 164 | + while (ref_walk_next_tgt(rw)) { |
| 165 | + rc = parse_dfs_target(ctx, rw, &tgt); |
| 166 | + if (rc) |
| 167 | + continue; |
121 | 168 |
|
122 |
| - rc = -EREMOTE; |
123 |
| - if (ref.flags & DFSREF_STORAGE_SERVER) { |
124 |
| - rc = cifs_mount_get_tcon(mnt_ctx); |
125 |
| - if (rc) |
126 |
| - goto out; |
| 169 | + cifs_mount_put_conns(mnt_ctx); |
| 170 | + rc = get_session(mnt_ctx, ref_walk_path(rw)); |
| 171 | + if (rc) |
| 172 | + continue; |
127 | 173 |
|
128 |
| - /* some servers may not advertise referral capability under ref.flags */ |
129 |
| - is_refsrv |= is_tcon_dfs(mnt_ctx->tcon); |
| 174 | + is_refsrv = tgt.server_type == DFS_TYPE_ROOT || |
| 175 | + DFS_INTERLINK(tgt.flags); |
| 176 | + ref_walk_set_tgt_hint(rw); |
130 | 177 |
|
131 |
| - rc = cifs_is_path_remote(mnt_ctx); |
132 |
| - } |
| 178 | + if (tgt.flags & DFSREF_STORAGE_SERVER) { |
| 179 | + rc = cifs_mount_get_tcon(mnt_ctx); |
| 180 | + if (!rc) |
| 181 | + rc = cifs_is_path_remote(mnt_ctx); |
| 182 | + if (!rc) |
| 183 | + break; |
| 184 | + if (rc != -EREMOTE) |
| 185 | + continue; |
| 186 | + } |
133 | 187 |
|
134 |
| - dfs_cache_noreq_update_tgthint(ref_path + 1, tit); |
| 188 | + if (is_refsrv) { |
| 189 | + rc = add_root_smb_session(mnt_ctx); |
| 190 | + if (rc) |
| 191 | + goto out; |
| 192 | + } |
135 | 193 |
|
136 |
| - if (rc == -EREMOTE && is_refsrv) { |
137 |
| - rc2 = add_root_smb_session(mnt_ctx); |
138 |
| - if (rc2) |
139 |
| - rc = rc2; |
140 |
| - } |
| 194 | + rc = ref_walk_advance(rw); |
| 195 | + if (!rc) { |
| 196 | + rc = set_ref_paths(mnt_ctx, &tgt, rw); |
| 197 | + if (!rc) { |
| 198 | + rc = -EREMOTE; |
| 199 | + goto again; |
| 200 | + } |
| 201 | + } |
| 202 | + if (rc != -ELOOP) |
| 203 | + goto out; |
| 204 | + } |
| 205 | + } while (rc && ref_walk_descend(rw)); |
141 | 206 |
|
142 | 207 | out:
|
143 |
| - free_dfs_info_param(&ref); |
| 208 | + free_dfs_info_param(&tgt); |
| 209 | + return rc; |
| 210 | +} |
| 211 | + |
| 212 | +static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx) |
| 213 | +{ |
| 214 | + struct dfs_ref_walk *rw; |
| 215 | + int rc; |
| 216 | + |
| 217 | + rw = ref_walk_alloc(); |
| 218 | + if (IS_ERR(rw)) |
| 219 | + return PTR_ERR(rw); |
| 220 | + |
| 221 | + ref_walk_init(rw); |
| 222 | + rc = set_ref_paths(mnt_ctx, NULL, rw); |
| 223 | + if (!rc) |
| 224 | + rc = __dfs_referral_walk(mnt_ctx, rw); |
| 225 | + ref_walk_free(rw); |
144 | 226 | return rc;
|
145 | 227 | }
|
146 | 228 |
|
147 | 229 | static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
|
148 | 230 | {
|
149 | 231 | struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
|
150 | 232 | struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
|
151 |
| - char *ref_path = NULL, *full_path = NULL; |
152 |
| - struct dfs_cache_tgt_iterator *tit; |
153 | 233 | struct cifs_tcon *tcon;
|
154 |
| - char *origin_fullpath = NULL; |
155 |
| - char sep = CIFS_DIR_SEP(cifs_sb); |
156 |
| - int num_links = 0; |
| 234 | + char *origin_fullpath; |
157 | 235 | int rc;
|
158 | 236 |
|
159 |
| - ref_path = dfs_get_path(cifs_sb, ctx->UNC); |
160 |
| - if (IS_ERR(ref_path)) |
161 |
| - return PTR_ERR(ref_path); |
| 237 | + origin_fullpath = dfs_get_path(cifs_sb, ctx->source); |
| 238 | + if (IS_ERR(origin_fullpath)) |
| 239 | + return PTR_ERR(origin_fullpath); |
162 | 240 |
|
163 |
| - full_path = smb3_fs_context_fullpath(ctx, sep); |
164 |
| - if (IS_ERR(full_path)) { |
165 |
| - rc = PTR_ERR(full_path); |
166 |
| - full_path = NULL; |
| 241 | + rc = dfs_referral_walk(mnt_ctx); |
| 242 | + if (rc) |
167 | 243 | goto out;
|
168 |
| - } |
169 | 244 |
|
170 |
| - origin_fullpath = kstrdup(full_path, GFP_KERNEL); |
171 |
| - if (!origin_fullpath) { |
172 |
| - rc = -ENOMEM; |
173 |
| - goto out; |
| 245 | + tcon = mnt_ctx->tcon; |
| 246 | + spin_lock(&tcon->tc_lock); |
| 247 | + if (!tcon->origin_fullpath) { |
| 248 | + tcon->origin_fullpath = origin_fullpath; |
| 249 | + origin_fullpath = NULL; |
174 | 250 | }
|
| 251 | + spin_unlock(&tcon->tc_lock); |
175 | 252 |
|
176 |
| - do { |
177 |
| - DFS_CACHE_TGT_LIST(tl); |
178 |
| - |
179 |
| - rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl); |
180 |
| - if (rc) { |
181 |
| - rc = cifs_mount_get_tcon(mnt_ctx); |
182 |
| - if (!rc) |
183 |
| - rc = cifs_is_path_remote(mnt_ctx); |
184 |
| - break; |
185 |
| - } |
186 |
| - |
187 |
| - tit = dfs_cache_get_tgt_iterator(&tl); |
188 |
| - if (!tit) { |
189 |
| - cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__, |
190 |
| - ref_path + 1); |
191 |
| - rc = -ENOENT; |
192 |
| - dfs_cache_free_tgts(&tl); |
193 |
| - break; |
194 |
| - } |
195 |
| - |
196 |
| - do { |
197 |
| - rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit); |
198 |
| - if (!rc) |
199 |
| - break; |
200 |
| - if (rc == -EREMOTE) { |
201 |
| - if (++num_links > MAX_NESTED_LINKS) { |
202 |
| - rc = -ELOOP; |
203 |
| - break; |
204 |
| - } |
205 |
| - kfree(ref_path); |
206 |
| - kfree(full_path); |
207 |
| - ref_path = full_path = NULL; |
208 |
| - |
209 |
| - full_path = smb3_fs_context_fullpath(ctx, sep); |
210 |
| - if (IS_ERR(full_path)) { |
211 |
| - rc = PTR_ERR(full_path); |
212 |
| - full_path = NULL; |
213 |
| - } else { |
214 |
| - ref_path = dfs_get_path(cifs_sb, full_path); |
215 |
| - if (IS_ERR(ref_path)) { |
216 |
| - rc = PTR_ERR(ref_path); |
217 |
| - ref_path = NULL; |
218 |
| - } |
219 |
| - } |
220 |
| - break; |
221 |
| - } |
222 |
| - } while ((tit = dfs_cache_get_next_tgt(&tl, tit))); |
223 |
| - dfs_cache_free_tgts(&tl); |
224 |
| - } while (rc == -EREMOTE); |
225 |
| - |
226 |
| - if (!rc) { |
227 |
| - tcon = mnt_ctx->tcon; |
228 |
| - |
229 |
| - spin_lock(&tcon->tc_lock); |
230 |
| - if (!tcon->origin_fullpath) { |
231 |
| - tcon->origin_fullpath = origin_fullpath; |
232 |
| - origin_fullpath = NULL; |
233 |
| - } |
234 |
| - spin_unlock(&tcon->tc_lock); |
235 |
| - |
236 |
| - if (list_empty(&tcon->dfs_ses_list)) { |
237 |
| - list_replace_init(&mnt_ctx->dfs_ses_list, |
238 |
| - &tcon->dfs_ses_list); |
239 |
| - queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, |
240 |
| - dfs_cache_get_ttl() * HZ); |
241 |
| - } else { |
242 |
| - dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list); |
243 |
| - } |
| 253 | + if (list_empty(&tcon->dfs_ses_list)) { |
| 254 | + list_replace_init(&mnt_ctx->dfs_ses_list, &tcon->dfs_ses_list); |
| 255 | + queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, |
| 256 | + dfs_cache_get_ttl() * HZ); |
| 257 | + } else { |
| 258 | + dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list); |
244 | 259 | }
|
245 | 260 |
|
246 | 261 | out:
|
247 | 262 | kfree(origin_fullpath);
|
248 |
| - kfree(ref_path); |
249 |
| - kfree(full_path); |
250 | 263 | return rc;
|
251 | 264 | }
|
252 | 265 |
|
|
0 commit comments