|
21 | 21 | #include "cifsfs.h" |
22 | 22 | #ifdef CONFIG_CIFS_DFS_UPCALL |
23 | 23 | #include "dns_resolve.h" |
| 24 | +#include "dfs_cache.h" |
24 | 25 | #endif |
25 | 26 | #include "fs_context.h" |
26 | 27 | #include "cached_dir.h" |
@@ -1198,4 +1199,114 @@ int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix) |
1198 | 1199 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; |
1199 | 1200 | return 0; |
1200 | 1201 | } |
| 1202 | + |
| 1203 | +/* |
| 1204 | + * Handle weird Windows SMB server behaviour. It responds with |
| 1205 | + * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for |
| 1206 | + * "\<server>\<dfsname>\<linkpath>" DFS reference, where <dfsname> contains |
| 1207 | + * non-ASCII unicode symbols. |
| 1208 | + */ |
| 1209 | +int cifs_inval_name_dfs_link_error(const unsigned int xid, |
| 1210 | + struct cifs_tcon *tcon, |
| 1211 | + struct cifs_sb_info *cifs_sb, |
| 1212 | + const char *full_path, |
| 1213 | + bool *islink) |
| 1214 | +{ |
| 1215 | + struct cifs_ses *ses = tcon->ses; |
| 1216 | + size_t len; |
| 1217 | + char *path; |
| 1218 | + char *ref_path; |
| 1219 | + |
| 1220 | + *islink = false; |
| 1221 | + |
| 1222 | + /* |
| 1223 | + * Fast path - skip check when @full_path doesn't have a prefix path to |
| 1224 | + * look up or tcon is not DFS. |
| 1225 | + */ |
| 1226 | + if (strlen(full_path) < 2 || !cifs_sb || |
| 1227 | + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || |
| 1228 | + !is_tcon_dfs(tcon) || !ses->server->origin_fullpath) |
| 1229 | + return 0; |
| 1230 | + |
| 1231 | + /* |
| 1232 | + * Slow path - tcon is DFS and @full_path has prefix path, so attempt |
| 1233 | + * to get a referral to figure out whether it is an DFS link. |
| 1234 | + */ |
| 1235 | + len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1; |
| 1236 | + path = kmalloc(len, GFP_KERNEL); |
| 1237 | + if (!path) |
| 1238 | + return -ENOMEM; |
| 1239 | + |
| 1240 | + scnprintf(path, len, "%s%s", tcon->tree_name, full_path); |
| 1241 | + ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls, |
| 1242 | + cifs_remap(cifs_sb)); |
| 1243 | + kfree(path); |
| 1244 | + |
| 1245 | + if (IS_ERR(ref_path)) { |
| 1246 | + if (PTR_ERR(ref_path) != -EINVAL) |
| 1247 | + return PTR_ERR(ref_path); |
| 1248 | + } else { |
| 1249 | + struct dfs_info3_param *refs = NULL; |
| 1250 | + int num_refs = 0; |
| 1251 | + |
| 1252 | + /* |
| 1253 | + * XXX: we are not using dfs_cache_find() here because we might |
| 1254 | + * end filling all the DFS cache and thus potentially |
| 1255 | + * removing cached DFS targets that the client would eventually |
| 1256 | + * need during failover. |
| 1257 | + */ |
| 1258 | + if (ses->server->ops->get_dfs_refer && |
| 1259 | + !ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs, |
| 1260 | + &num_refs, cifs_sb->local_nls, |
| 1261 | + cifs_remap(cifs_sb))) |
| 1262 | + *islink = refs[0].server_type == DFS_TYPE_LINK; |
| 1263 | + free_dfs_info_array(refs, num_refs); |
| 1264 | + kfree(ref_path); |
| 1265 | + } |
| 1266 | + return 0; |
| 1267 | +} |
1201 | 1268 | #endif |
| 1269 | + |
| 1270 | +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry) |
| 1271 | +{ |
| 1272 | + int timeout = 10; |
| 1273 | + int rc; |
| 1274 | + |
| 1275 | + spin_lock(&server->srv_lock); |
| 1276 | + if (server->tcpStatus != CifsNeedReconnect) { |
| 1277 | + spin_unlock(&server->srv_lock); |
| 1278 | + return 0; |
| 1279 | + } |
| 1280 | + timeout *= server->nr_targets; |
| 1281 | + spin_unlock(&server->srv_lock); |
| 1282 | + |
| 1283 | + /* |
| 1284 | + * Give demultiplex thread up to 10 seconds to each target available for |
| 1285 | + * reconnect -- should be greater than cifs socket timeout which is 7 |
| 1286 | + * seconds. |
| 1287 | + * |
| 1288 | + * On "soft" mounts we wait once. Hard mounts keep retrying until |
| 1289 | + * process is killed or server comes back on-line. |
| 1290 | + */ |
| 1291 | + do { |
| 1292 | + rc = wait_event_interruptible_timeout(server->response_q, |
| 1293 | + (server->tcpStatus != CifsNeedReconnect), |
| 1294 | + timeout * HZ); |
| 1295 | + if (rc < 0) { |
| 1296 | + cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", |
| 1297 | + __func__); |
| 1298 | + return -ERESTARTSYS; |
| 1299 | + } |
| 1300 | + |
| 1301 | + /* are we still trying to reconnect? */ |
| 1302 | + spin_lock(&server->srv_lock); |
| 1303 | + if (server->tcpStatus != CifsNeedReconnect) { |
| 1304 | + spin_unlock(&server->srv_lock); |
| 1305 | + return 0; |
| 1306 | + } |
| 1307 | + spin_unlock(&server->srv_lock); |
| 1308 | + } while (retry); |
| 1309 | + |
| 1310 | + cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); |
| 1311 | + return -EHOSTDOWN; |
| 1312 | +} |
0 commit comments