Skip to content

Commit d8079fa

Browse files
committed
Merge tag '5.14-rc2-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs fixes from Steve French: "Five cifs/smb3 fixes, including a DFS failover fix, two fallocate fixes, and two trivial coverity cleanups" * tag '5.14-rc2-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: fix fallocate when trying to allocate a hole. CIFS: Clarify SMB1 code for POSIX delete file CIFS: Clarify SMB1 code for POSIX Create cifs: support share failover when remounting cifs: only write 64kb at a time when fallocating a small region of a file
2 parents 6498f61 + 488968a commit d8079fa

File tree

6 files changed

+247
-55
lines changed

6 files changed

+247
-55
lines changed

fs/cifs/cifssmb.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -873,8 +873,11 @@ CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
873873
InformationLevel) - 4;
874874
offset = param_offset + params;
875875

876-
/* Setup pointer to Request Data (inode type) */
877-
pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
876+
/* Setup pointer to Request Data (inode type).
877+
* Note that SMB offsets are from the beginning of SMB which is 4 bytes
878+
* in, after RFC1001 field
879+
*/
880+
pRqD = (struct unlink_psx_rq *)((char *)(pSMB) + offset + 4);
878881
pRqD->type = cpu_to_le16(type);
879882
pSMB->ParameterOffset = cpu_to_le16(param_offset);
880883
pSMB->DataOffset = cpu_to_le16(offset);
@@ -1081,7 +1084,8 @@ CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon,
10811084
param_offset = offsetof(struct smb_com_transaction2_spi_req,
10821085
InformationLevel) - 4;
10831086
offset = param_offset + params;
1084-
pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
1087+
/* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
1088+
pdata = (OPEN_PSX_REQ *)((char *)(pSMB) + offset + 4);
10851089
pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
10861090
pdata->Permissions = cpu_to_le64(mode);
10871091
pdata->PosixOpenFlags = cpu_to_le32(posix_flags);

fs/cifs/connect.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
220220
#ifdef CONFIG_CIFS_DFS_UPCALL
221221
struct super_block *sb = NULL;
222222
struct cifs_sb_info *cifs_sb = NULL;
223-
struct dfs_cache_tgt_list tgt_list = {0};
223+
struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
224224
struct dfs_cache_tgt_iterator *tgt_it = NULL;
225225
#endif
226226

@@ -3130,7 +3130,7 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
31303130
{
31313131
int rc;
31323132
char *npath = NULL;
3133-
struct dfs_cache_tgt_list tgt_list = {0};
3133+
struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
31343134
struct dfs_cache_tgt_iterator *tgt_it = NULL;
31353135
struct smb3_fs_context tmp_ctx = {NULL};
31363136

fs/cifs/dfs_cache.c

Lines changed: 191 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
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

912913
err_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
}

fs/cifs/dfs_cache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <linux/uuid.h>
1414
#include "cifsglob.h"
1515

16+
#define DFS_CACHE_TGT_LIST_INIT(var) { .tl_numtgts = 0, .tl_list = LIST_HEAD_INIT((var).tl_list), }
17+
1618
struct dfs_cache_tgt_list {
1719
int tl_numtgts;
1820
struct list_head tl_list;
@@ -44,6 +46,7 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
4446
void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id);
4547
void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses);
4648
char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap);
49+
int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb);
4750

4851
static inline struct dfs_cache_tgt_iterator *
4952
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,

fs/cifs/fs_context.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include <linux/magic.h>
1414
#include <linux/security.h>
1515
#include <net/net_namespace.h>
16+
#ifdef CONFIG_CIFS_DFS_UPCALL
17+
#include "dfs_cache.h"
18+
#endif
1619
*/
1720

1821
#include <linux/ctype.h>
@@ -779,6 +782,10 @@ static int smb3_reconfigure(struct fs_context *fc)
779782
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
780783
rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
781784
smb3_update_mnt_flags(cifs_sb);
785+
#ifdef CONFIG_CIFS_DFS_UPCALL
786+
if (!rc)
787+
rc = dfs_cache_remount_fs(cifs_sb);
788+
#endif
782789

783790
return rc;
784791
}

0 commit comments

Comments
 (0)