1919#include "cifs_debug.h"
2020#include "cifs_unicode.h"
2121#include "smb2glob.h"
22+ #include "dns_resolve.h"
2223
2324#include "dfs_cache.h"
2425
@@ -911,6 +912,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
911912
912913err_free_it :
913914 list_for_each_entry_safe (it , nit , head , it_list ) {
915+ list_del (& it -> it_list );
914916 kfree (it -> it_name );
915917 kfree (it );
916918 }
@@ -1293,6 +1295,194 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
12931295 return 0 ;
12941296}
12951297
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+
12961486/*
12971487 * Refresh all active dfs mounts regardless of whether they are in cache or not.
12981488 * (cache can be cleared)
@@ -1303,7 +1493,6 @@ static void refresh_mounts(struct cifs_ses **sessions)
13031493 struct cifs_ses * ses ;
13041494 struct cifs_tcon * tcon , * ntcon ;
13051495 struct list_head tcons ;
1306- unsigned int xid ;
13071496
13081497 INIT_LIST_HEAD (& tcons );
13091498
@@ -1321,44 +1510,8 @@ static void refresh_mounts(struct cifs_ses **sessions)
13211510 spin_unlock (& cifs_tcp_ses_lock );
13221511
13231512 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-
13311513 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);
13621515 cifs_put_tcon (tcon );
13631516 }
13641517}
0 commit comments