19
19
#include "cifs_debug.h"
20
20
#include "cifs_unicode.h"
21
21
#include "smb2glob.h"
22
+ #include "dns_resolve.h"
22
23
23
24
#include "dfs_cache.h"
24
25
@@ -911,6 +912,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
911
912
912
913
err_free_it :
913
914
list_for_each_entry_safe (it , nit , head , it_list ) {
915
+ list_del (& it -> it_list );
914
916
kfree (it -> it_name );
915
917
kfree (it );
916
918
}
@@ -1293,6 +1295,194 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
1293
1295
return 0 ;
1294
1296
}
1295
1297
1298
+ static bool target_share_equal (struct TCP_Server_Info * server , const char * s1 , const char * s2 )
1299
+ {
1300
+ char unc [sizeof ("\\\\" ) + SERVER_NAME_LENGTH ] = {0 };
1301
+ const char * host ;
1302
+ size_t hostlen ;
1303
+ char * ip = NULL ;
1304
+ struct sockaddr sa ;
1305
+ bool match ;
1306
+ int rc ;
1307
+
1308
+ if (strcasecmp (s1 , s2 ))
1309
+ return false;
1310
+
1311
+ /*
1312
+ * Resolve share's hostname and check if server address matches. Otherwise just ignore it
1313
+ * as we could not have upcall to resolve hostname or failed to convert ip address.
1314
+ */
1315
+ match = true;
1316
+ extract_unc_hostname (s1 , & host , & hostlen );
1317
+ scnprintf (unc , sizeof (unc ), "\\\\%.*s" , (int )hostlen , host );
1318
+
1319
+ rc = dns_resolve_server_name_to_ip (unc , & ip , NULL );
1320
+ if (rc < 0 ) {
1321
+ cifs_dbg (FYI , "%s: could not resolve %.*s. assuming server address matches.\n" ,
1322
+ __func__ , (int )hostlen , host );
1323
+ return true;
1324
+ }
1325
+
1326
+ if (!cifs_convert_address (& sa , ip , strlen (ip ))) {
1327
+ cifs_dbg (VFS , "%s: failed to convert address \'%s\'. skip address matching.\n" ,
1328
+ __func__ , ip );
1329
+ } else {
1330
+ mutex_lock (& server -> srv_mutex );
1331
+ match = cifs_match_ipaddr ((struct sockaddr * )& server -> dstaddr , & sa );
1332
+ mutex_unlock (& server -> srv_mutex );
1333
+ }
1334
+
1335
+ kfree (ip );
1336
+ return match ;
1337
+ }
1338
+
1339
+ /*
1340
+ * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new
1341
+ * target shares in @refs.
1342
+ */
1343
+ static void mark_for_reconnect_if_needed (struct cifs_tcon * tcon , struct dfs_cache_tgt_list * tl ,
1344
+ const struct dfs_info3_param * refs , int numrefs )
1345
+ {
1346
+ struct dfs_cache_tgt_iterator * it ;
1347
+ int i ;
1348
+
1349
+ for (it = dfs_cache_get_tgt_iterator (tl ); it ; it = dfs_cache_get_next_tgt (tl , it )) {
1350
+ for (i = 0 ; i < numrefs ; i ++ ) {
1351
+ if (target_share_equal (tcon -> ses -> server , dfs_cache_get_tgt_name (it ),
1352
+ refs [i ].node_name ))
1353
+ return ;
1354
+ }
1355
+ }
1356
+
1357
+ cifs_dbg (FYI , "%s: no cached or matched targets. mark dfs share for reconnect.\n" , __func__ );
1358
+ for (i = 0 ; i < tcon -> ses -> chan_count ; i ++ ) {
1359
+ spin_lock (& GlobalMid_Lock );
1360
+ if (tcon -> ses -> chans [i ].server -> tcpStatus != CifsExiting )
1361
+ tcon -> ses -> chans [i ].server -> tcpStatus = CifsNeedReconnect ;
1362
+ spin_unlock (& GlobalMid_Lock );
1363
+ }
1364
+ }
1365
+
1366
+ /* Refresh dfs referral of tcon and mark it for reconnect if needed */
1367
+ static int refresh_tcon (struct cifs_ses * * sessions , struct cifs_tcon * tcon , bool force_refresh )
1368
+ {
1369
+ const char * path = tcon -> dfs_path + 1 ;
1370
+ struct cifs_ses * ses ;
1371
+ struct cache_entry * ce ;
1372
+ struct dfs_info3_param * refs = NULL ;
1373
+ int numrefs = 0 ;
1374
+ bool needs_refresh = false;
1375
+ struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT (tl );
1376
+ int rc = 0 ;
1377
+ unsigned int xid ;
1378
+
1379
+ ses = find_ipc_from_server_path (sessions , path );
1380
+ if (IS_ERR (ses )) {
1381
+ cifs_dbg (FYI , "%s: could not find ipc session\n" , __func__ );
1382
+ return PTR_ERR (ses );
1383
+ }
1384
+
1385
+ down_read (& htable_rw_lock );
1386
+ ce = lookup_cache_entry (path );
1387
+ needs_refresh = force_refresh || IS_ERR (ce ) || cache_entry_expired (ce );
1388
+ if (!IS_ERR (ce )) {
1389
+ rc = get_targets (ce , & tl );
1390
+ if (rc )
1391
+ cifs_dbg (FYI , "%s: could not get dfs targets: %d\n" , __func__ , rc );
1392
+ }
1393
+ up_read (& htable_rw_lock );
1394
+
1395
+ if (!needs_refresh ) {
1396
+ rc = 0 ;
1397
+ goto out ;
1398
+ }
1399
+
1400
+ xid = get_xid ();
1401
+ rc = get_dfs_referral (xid , ses , path , & refs , & numrefs );
1402
+ free_xid (xid );
1403
+
1404
+ /* Create or update a cache entry with the new referral */
1405
+ if (!rc ) {
1406
+ dump_refs (refs , numrefs );
1407
+
1408
+ down_write (& htable_rw_lock );
1409
+ ce = lookup_cache_entry (path );
1410
+ if (IS_ERR (ce ))
1411
+ add_cache_entry_locked (refs , numrefs );
1412
+ else if (force_refresh || cache_entry_expired (ce ))
1413
+ update_cache_entry_locked (ce , refs , numrefs );
1414
+ up_write (& htable_rw_lock );
1415
+
1416
+ mark_for_reconnect_if_needed (tcon , & tl , refs , numrefs );
1417
+ }
1418
+
1419
+ out :
1420
+ dfs_cache_free_tgts (& tl );
1421
+ free_dfs_info_array (refs , numrefs );
1422
+ return rc ;
1423
+ }
1424
+
1425
+ /**
1426
+ * dfs_cache_remount_fs - remount a DFS share
1427
+ *
1428
+ * Reconfigure dfs mount by forcing a new DFS referral and if the currently cached targets do not
1429
+ * match any of the new targets, mark it for reconnect.
1430
+ *
1431
+ * @cifs_sb: cifs superblock.
1432
+ *
1433
+ * Return zero if remounted, otherwise non-zero.
1434
+ */
1435
+ int dfs_cache_remount_fs (struct cifs_sb_info * cifs_sb )
1436
+ {
1437
+ struct cifs_tcon * tcon ;
1438
+ struct mount_group * mg ;
1439
+ struct cifs_ses * sessions [CACHE_MAX_ENTRIES + 1 ] = {NULL };
1440
+ int rc ;
1441
+
1442
+ if (!cifs_sb || !cifs_sb -> master_tlink )
1443
+ return - EINVAL ;
1444
+
1445
+ tcon = cifs_sb_master_tcon (cifs_sb );
1446
+ if (!tcon -> dfs_path ) {
1447
+ cifs_dbg (FYI , "%s: not a dfs tcon\n" , __func__ );
1448
+ return 0 ;
1449
+ }
1450
+
1451
+ if (uuid_is_null (& cifs_sb -> dfs_mount_id )) {
1452
+ cifs_dbg (FYI , "%s: tcon has no dfs mount group id\n" , __func__ );
1453
+ return - EINVAL ;
1454
+ }
1455
+
1456
+ mutex_lock (& mount_group_list_lock );
1457
+ mg = find_mount_group_locked (& cifs_sb -> dfs_mount_id );
1458
+ if (IS_ERR (mg )) {
1459
+ mutex_unlock (& mount_group_list_lock );
1460
+ cifs_dbg (FYI , "%s: tcon has ipc session to refresh referral\n" , __func__ );
1461
+ return PTR_ERR (mg );
1462
+ }
1463
+ kref_get (& mg -> refcount );
1464
+ mutex_unlock (& mount_group_list_lock );
1465
+
1466
+ spin_lock (& mg -> lock );
1467
+ memcpy (& sessions , mg -> sessions , mg -> num_sessions * sizeof (mg -> sessions [0 ]));
1468
+ spin_unlock (& mg -> lock );
1469
+
1470
+ /*
1471
+ * After reconnecting to a different server, unique ids won't match anymore, so we disable
1472
+ * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
1473
+ */
1474
+ cifs_autodisable_serverino (cifs_sb );
1475
+ /*
1476
+ * Force the use of prefix path to support failover on DFS paths that resolve to targets
1477
+ * that have different prefix paths.
1478
+ */
1479
+ cifs_sb -> mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH ;
1480
+ rc = refresh_tcon (sessions , tcon , true);
1481
+
1482
+ kref_put (& mg -> refcount , mount_group_release );
1483
+ return rc ;
1484
+ }
1485
+
1296
1486
/*
1297
1487
* Refresh all active dfs mounts regardless of whether they are in cache or not.
1298
1488
* (cache can be cleared)
@@ -1303,7 +1493,6 @@ static void refresh_mounts(struct cifs_ses **sessions)
1303
1493
struct cifs_ses * ses ;
1304
1494
struct cifs_tcon * tcon , * ntcon ;
1305
1495
struct list_head tcons ;
1306
- unsigned int xid ;
1307
1496
1308
1497
INIT_LIST_HEAD (& tcons );
1309
1498
@@ -1321,44 +1510,8 @@ static void refresh_mounts(struct cifs_ses **sessions)
1321
1510
spin_unlock (& cifs_tcp_ses_lock );
1322
1511
1323
1512
list_for_each_entry_safe (tcon , ntcon , & tcons , ulist ) {
1324
- const char * path = tcon -> dfs_path + 1 ;
1325
- struct cache_entry * ce ;
1326
- struct dfs_info3_param * refs = NULL ;
1327
- int numrefs = 0 ;
1328
- bool needs_refresh = false;
1329
- int rc = 0 ;
1330
-
1331
1513
list_del_init (& tcon -> ulist );
1332
-
1333
- ses = find_ipc_from_server_path (sessions , path );
1334
- if (IS_ERR (ses ))
1335
- goto next_tcon ;
1336
-
1337
- down_read (& htable_rw_lock );
1338
- ce = lookup_cache_entry (path );
1339
- needs_refresh = IS_ERR (ce ) || cache_entry_expired (ce );
1340
- up_read (& htable_rw_lock );
1341
-
1342
- if (!needs_refresh )
1343
- goto next_tcon ;
1344
-
1345
- xid = get_xid ();
1346
- rc = get_dfs_referral (xid , ses , path , & refs , & numrefs );
1347
- free_xid (xid );
1348
-
1349
- /* Create or update a cache entry with the new referral */
1350
- if (!rc ) {
1351
- down_write (& htable_rw_lock );
1352
- ce = lookup_cache_entry (path );
1353
- if (IS_ERR (ce ))
1354
- add_cache_entry_locked (refs , numrefs );
1355
- else if (cache_entry_expired (ce ))
1356
- update_cache_entry_locked (ce , refs , numrefs );
1357
- up_write (& htable_rw_lock );
1358
- }
1359
-
1360
- next_tcon :
1361
- free_dfs_info_array (refs , numrefs );
1514
+ refresh_tcon (sessions , tcon , false);
1362
1515
cifs_put_tcon (tcon );
1363
1516
}
1364
1517
}
0 commit comments