mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 05:41:55 +00:00
5 cifs/smb3 fixes, including a DFS failover fix, 2 fallocate fixes, and 2 trivial coverity cleanups
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmD8kXkACgkQiiy9cAdy T1F4nQwAnElfJhx0iBMXWW89Erzr8Hr+aOS7xEtattvT+/YS+2vtza3Q3IOmZEKG PL5U6q7G9TnaB3ZvTXdZeH3K57QU5DL1KUPhDpoTcylL2uPKyhEG8zreY1X0sur+ nzNNIUaix7iCrWELtA21Wxiwpzf960JBdpYkCjronQdAvWQt76vtj0dZ+5rt6nac 9Zw4wso0OmaCC956MXQz8+Cy3Piy+eqT+IiZQQbpjZ+e4q9JZ5yLVChfq8sSfOHs 30tJVgE/RHTgYX6IxmFqxD1AeZ3+/D3HDlXYBTVRQCY380b/Oo2lI5XkDOJdzRT6 BJBNXdhFwF7J5gsDD2TcQjxXI762mD7OdtXTbha3mQuNQy9HlSNz7Eoqr63yryrU 7zwQOUJPP4V0v8/rlAJkBcXAEdkZXHKFwWsYy9YL4/LQ2JAHZVnUpyKDwXnlB8E2 HrER/Z5kzzl2Qlgafk4Iwaq2hIkhgvyvBXPrwUrAoqXT+gj41qMUNMg5DoBbL+ax K5Caf1fi =Ngut -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
d8079fac16
@ -873,8 +873,11 @@ PsxDelete:
|
||||
InformationLevel) - 4;
|
||||
offset = param_offset + params;
|
||||
|
||||
/* Setup pointer to Request Data (inode type) */
|
||||
pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
|
||||
/* Setup pointer to Request Data (inode type).
|
||||
* Note that SMB offsets are from the beginning of SMB which is 4 bytes
|
||||
* in, after RFC1001 field
|
||||
*/
|
||||
pRqD = (struct unlink_psx_rq *)((char *)(pSMB) + offset + 4);
|
||||
pRqD->type = cpu_to_le16(type);
|
||||
pSMB->ParameterOffset = cpu_to_le16(param_offset);
|
||||
pSMB->DataOffset = cpu_to_le16(offset);
|
||||
@ -1081,7 +1084,8 @@ PsxCreat:
|
||||
param_offset = offsetof(struct smb_com_transaction2_spi_req,
|
||||
InformationLevel) - 4;
|
||||
offset = param_offset + params;
|
||||
pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
|
||||
/* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
|
||||
pdata = (OPEN_PSX_REQ *)((char *)(pSMB) + offset + 4);
|
||||
pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
|
||||
pdata->Permissions = cpu_to_le64(mode);
|
||||
pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
|
||||
|
@ -220,7 +220,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
struct super_block *sb = NULL;
|
||||
struct cifs_sb_info *cifs_sb = NULL;
|
||||
struct dfs_cache_tgt_list tgt_list = {0};
|
||||
struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
|
||||
struct dfs_cache_tgt_iterator *tgt_it = NULL;
|
||||
#endif
|
||||
|
||||
@ -3130,7 +3130,7 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
|
||||
{
|
||||
int rc;
|
||||
char *npath = NULL;
|
||||
struct dfs_cache_tgt_list tgt_list = {0};
|
||||
struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
|
||||
struct dfs_cache_tgt_iterator *tgt_it = NULL;
|
||||
struct smb3_fs_context tmp_ctx = {NULL};
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "cifs_debug.h"
|
||||
#include "cifs_unicode.h"
|
||||
#include "smb2glob.h"
|
||||
#include "dns_resolve.h"
|
||||
|
||||
#include "dfs_cache.h"
|
||||
|
||||
@ -911,6 +912,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
|
||||
|
||||
err_free_it:
|
||||
list_for_each_entry_safe(it, nit, head, it_list) {
|
||||
list_del(&it->it_list);
|
||||
kfree(it->it_name);
|
||||
kfree(it);
|
||||
}
|
||||
@ -1293,6 +1295,194 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2)
|
||||
{
|
||||
char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0};
|
||||
const char *host;
|
||||
size_t hostlen;
|
||||
char *ip = NULL;
|
||||
struct sockaddr sa;
|
||||
bool match;
|
||||
int rc;
|
||||
|
||||
if (strcasecmp(s1, s2))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Resolve share's hostname and check if server address matches. Otherwise just ignore it
|
||||
* as we could not have upcall to resolve hostname or failed to convert ip address.
|
||||
*/
|
||||
match = true;
|
||||
extract_unc_hostname(s1, &host, &hostlen);
|
||||
scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host);
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(unc, &ip, NULL);
|
||||
if (rc < 0) {
|
||||
cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n",
|
||||
__func__, (int)hostlen, host);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!cifs_convert_address(&sa, ip, strlen(ip))) {
|
||||
cifs_dbg(VFS, "%s: failed to convert address \'%s\'. skip address matching.\n",
|
||||
__func__, ip);
|
||||
} else {
|
||||
mutex_lock(&server->srv_mutex);
|
||||
match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, &sa);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
}
|
||||
|
||||
kfree(ip);
|
||||
return match;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new
|
||||
* target shares in @refs.
|
||||
*/
|
||||
static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cache_tgt_list *tl,
|
||||
const struct dfs_info3_param *refs, int numrefs)
|
||||
{
|
||||
struct dfs_cache_tgt_iterator *it;
|
||||
int i;
|
||||
|
||||
for (it = dfs_cache_get_tgt_iterator(tl); it; it = dfs_cache_get_next_tgt(tl, it)) {
|
||||
for (i = 0; i < numrefs; i++) {
|
||||
if (target_share_equal(tcon->ses->server, dfs_cache_get_tgt_name(it),
|
||||
refs[i].node_name))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__);
|
||||
for (i = 0; i < tcon->ses->chan_count; i++) {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (tcon->ses->chans[i].server->tcpStatus != CifsExiting)
|
||||
tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Refresh dfs referral of tcon and mark it for reconnect if needed */
|
||||
static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh)
|
||||
{
|
||||
const char *path = tcon->dfs_path + 1;
|
||||
struct cifs_ses *ses;
|
||||
struct cache_entry *ce;
|
||||
struct dfs_info3_param *refs = NULL;
|
||||
int numrefs = 0;
|
||||
bool needs_refresh = false;
|
||||
struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
|
||||
int rc = 0;
|
||||
unsigned int xid;
|
||||
|
||||
ses = find_ipc_from_server_path(sessions, path);
|
||||
if (IS_ERR(ses)) {
|
||||
cifs_dbg(FYI, "%s: could not find ipc session\n", __func__);
|
||||
return PTR_ERR(ses);
|
||||
}
|
||||
|
||||
down_read(&htable_rw_lock);
|
||||
ce = lookup_cache_entry(path);
|
||||
needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce);
|
||||
if (!IS_ERR(ce)) {
|
||||
rc = get_targets(ce, &tl);
|
||||
if (rc)
|
||||
cifs_dbg(FYI, "%s: could not get dfs targets: %d\n", __func__, rc);
|
||||
}
|
||||
up_read(&htable_rw_lock);
|
||||
|
||||
if (!needs_refresh) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
xid = get_xid();
|
||||
rc = get_dfs_referral(xid, ses, path, &refs, &numrefs);
|
||||
free_xid(xid);
|
||||
|
||||
/* Create or update a cache entry with the new referral */
|
||||
if (!rc) {
|
||||
dump_refs(refs, numrefs);
|
||||
|
||||
down_write(&htable_rw_lock);
|
||||
ce = lookup_cache_entry(path);
|
||||
if (IS_ERR(ce))
|
||||
add_cache_entry_locked(refs, numrefs);
|
||||
else if (force_refresh || cache_entry_expired(ce))
|
||||
update_cache_entry_locked(ce, refs, numrefs);
|
||||
up_write(&htable_rw_lock);
|
||||
|
||||
mark_for_reconnect_if_needed(tcon, &tl, refs, numrefs);
|
||||
}
|
||||
|
||||
out:
|
||||
dfs_cache_free_tgts(&tl);
|
||||
free_dfs_info_array(refs, numrefs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfs_cache_remount_fs - remount a DFS share
|
||||
*
|
||||
* Reconfigure dfs mount by forcing a new DFS referral and if the currently cached targets do not
|
||||
* match any of the new targets, mark it for reconnect.
|
||||
*
|
||||
* @cifs_sb: cifs superblock.
|
||||
*
|
||||
* Return zero if remounted, otherwise non-zero.
|
||||
*/
|
||||
int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
struct cifs_tcon *tcon;
|
||||
struct mount_group *mg;
|
||||
struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL};
|
||||
int rc;
|
||||
|
||||
if (!cifs_sb || !cifs_sb->master_tlink)
|
||||
return -EINVAL;
|
||||
|
||||
tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
if (!tcon->dfs_path) {
|
||||
cifs_dbg(FYI, "%s: not a dfs tcon\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (uuid_is_null(&cifs_sb->dfs_mount_id)) {
|
||||
cifs_dbg(FYI, "%s: tcon has no dfs mount group id\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&mount_group_list_lock);
|
||||
mg = find_mount_group_locked(&cifs_sb->dfs_mount_id);
|
||||
if (IS_ERR(mg)) {
|
||||
mutex_unlock(&mount_group_list_lock);
|
||||
cifs_dbg(FYI, "%s: tcon has ipc session to refresh referral\n", __func__);
|
||||
return PTR_ERR(mg);
|
||||
}
|
||||
kref_get(&mg->refcount);
|
||||
mutex_unlock(&mount_group_list_lock);
|
||||
|
||||
spin_lock(&mg->lock);
|
||||
memcpy(&sessions, mg->sessions, mg->num_sessions * sizeof(mg->sessions[0]));
|
||||
spin_unlock(&mg->lock);
|
||||
|
||||
/*
|
||||
* After reconnecting to a different server, unique ids won't match anymore, so we disable
|
||||
* serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
|
||||
*/
|
||||
cifs_autodisable_serverino(cifs_sb);
|
||||
/*
|
||||
* Force the use of prefix path to support failover on DFS paths that resolve to targets
|
||||
* that have different prefix paths.
|
||||
*/
|
||||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
|
||||
rc = refresh_tcon(sessions, tcon, true);
|
||||
|
||||
kref_put(&mg->refcount, mount_group_release);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Refresh all active dfs mounts regardless of whether they are in cache or not.
|
||||
* (cache can be cleared)
|
||||
@ -1303,7 +1493,6 @@ static void refresh_mounts(struct cifs_ses **sessions)
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon, *ntcon;
|
||||
struct list_head tcons;
|
||||
unsigned int xid;
|
||||
|
||||
INIT_LIST_HEAD(&tcons);
|
||||
|
||||
@ -1321,44 +1510,8 @@ static void refresh_mounts(struct cifs_ses **sessions)
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
|
||||
const char *path = tcon->dfs_path + 1;
|
||||
struct cache_entry *ce;
|
||||
struct dfs_info3_param *refs = NULL;
|
||||
int numrefs = 0;
|
||||
bool needs_refresh = false;
|
||||
int rc = 0;
|
||||
|
||||
list_del_init(&tcon->ulist);
|
||||
|
||||
ses = find_ipc_from_server_path(sessions, path);
|
||||
if (IS_ERR(ses))
|
||||
goto next_tcon;
|
||||
|
||||
down_read(&htable_rw_lock);
|
||||
ce = lookup_cache_entry(path);
|
||||
needs_refresh = IS_ERR(ce) || cache_entry_expired(ce);
|
||||
up_read(&htable_rw_lock);
|
||||
|
||||
if (!needs_refresh)
|
||||
goto next_tcon;
|
||||
|
||||
xid = get_xid();
|
||||
rc = get_dfs_referral(xid, ses, path, &refs, &numrefs);
|
||||
free_xid(xid);
|
||||
|
||||
/* Create or update a cache entry with the new referral */
|
||||
if (!rc) {
|
||||
down_write(&htable_rw_lock);
|
||||
ce = lookup_cache_entry(path);
|
||||
if (IS_ERR(ce))
|
||||
add_cache_entry_locked(refs, numrefs);
|
||||
else if (cache_entry_expired(ce))
|
||||
update_cache_entry_locked(ce, refs, numrefs);
|
||||
up_write(&htable_rw_lock);
|
||||
}
|
||||
|
||||
next_tcon:
|
||||
free_dfs_info_array(refs, numrefs);
|
||||
refresh_tcon(sessions, tcon, false);
|
||||
cifs_put_tcon(tcon);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <linux/uuid.h>
|
||||
#include "cifsglob.h"
|
||||
|
||||
#define DFS_CACHE_TGT_LIST_INIT(var) { .tl_numtgts = 0, .tl_list = LIST_HEAD_INIT((var).tl_list), }
|
||||
|
||||
struct dfs_cache_tgt_list {
|
||||
int tl_numtgts;
|
||||
struct list_head tl_list;
|
||||
@ -44,6 +46,7 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
|
||||
void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id);
|
||||
void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses);
|
||||
char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap);
|
||||
int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb);
|
||||
|
||||
static inline struct dfs_cache_tgt_iterator *
|
||||
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include <linux/magic.h>
|
||||
#include <linux/security.h>
|
||||
#include <net/net_namespace.h>
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
#include "dfs_cache.h"
|
||||
#endif
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
@ -779,6 +782,10 @@ static int smb3_reconfigure(struct fs_context *fc)
|
||||
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
|
||||
rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
|
||||
smb3_update_mnt_flags(cifs_sb);
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
if (!rc)
|
||||
rc = dfs_cache_remount_fs(cifs_sb);
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -3617,7 +3617,7 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
|
||||
char *buf)
|
||||
{
|
||||
struct cifs_io_parms io_parms = {0};
|
||||
int nbytes;
|
||||
int rc, nbytes;
|
||||
struct kvec iov[2];
|
||||
|
||||
io_parms.netfid = cfile->fid.netfid;
|
||||
@ -3625,13 +3625,25 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
|
||||
io_parms.tcon = tcon;
|
||||
io_parms.persistent_fid = cfile->fid.persistent_fid;
|
||||
io_parms.volatile_fid = cfile->fid.volatile_fid;
|
||||
io_parms.offset = off;
|
||||
io_parms.length = len;
|
||||
|
||||
/* iov[0] is reserved for smb header */
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_len = io_parms.length;
|
||||
return SMB2_write(xid, &io_parms, &nbytes, iov, 1);
|
||||
while (len) {
|
||||
io_parms.offset = off;
|
||||
io_parms.length = len;
|
||||
if (io_parms.length > SMB2_MAX_BUFFER_SIZE)
|
||||
io_parms.length = SMB2_MAX_BUFFER_SIZE;
|
||||
/* iov[0] is reserved for smb header */
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_len = io_parms.length;
|
||||
rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1);
|
||||
if (rc)
|
||||
break;
|
||||
if (nbytes > len)
|
||||
return -EINVAL;
|
||||
buf += nbytes;
|
||||
off += nbytes;
|
||||
len -= nbytes;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int smb3_simple_fallocate_range(unsigned int xid,
|
||||
@ -3655,11 +3667,6 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||
(char **)&out_data, &out_data_len);
|
||||
if (rc)
|
||||
goto out;
|
||||
/*
|
||||
* It is already all allocated
|
||||
*/
|
||||
if (out_data_len == 0)
|
||||
goto out;
|
||||
|
||||
buf = kzalloc(1024 * 1024, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
@ -3782,6 +3789,24 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (keep_size == true) {
|
||||
/*
|
||||
* We can not preallocate pages beyond the end of the file
|
||||
* in SMB2
|
||||
*/
|
||||
if (off >= i_size_read(inode)) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* For fallocates that are partially beyond the end of file,
|
||||
* clamp len so we only fallocate up to the end of file.
|
||||
*/
|
||||
if (off + len > i_size_read(inode)) {
|
||||
len = i_size_read(inode) - off;
|
||||
}
|
||||
}
|
||||
|
||||
if ((keep_size == true) || (i_size_read(inode) >= off + len)) {
|
||||
/*
|
||||
* At this point, we are trying to fallocate an internal
|
||||
|
Loading…
Reference in New Issue
Block a user