1
0

Merge tag 'v6.17-rc-part1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client updates from Steve French:

 - Fix network namespace refcount leak

 - Multichannel reconnect fix

 - Perf improvement to not do unneeded EA query on native symlinks

 - Performance improvement for directory leases to allow extending lease
   for actively queried directories

 - Improve debugging of directory leases by adding pseudofile to show
   them

 - Five minor mount cleanup patches

 - Minor directory lease cleanup patch

 - Allow creating special files via reparse points over SMB1

 - Two minor improvements to FindFirst over SMB1

 - Two NTLMSSP session setup fixes

* tag 'v6.17-rc-part1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb3 client: add way to show directory leases for improved debugging
  smb: client: get rid of kstrdup() when parsing iocharset mount option
  smb: client: get rid of kstrdup() when parsing domain mount option
  smb: client: get rid of kstrdup() when parsing pass2 mount option
  smb: client: get rid of kstrdup() when parsing pass mount option
  smb: client: get rid of kstrdup() when parsing user mount option
  cifs: Add support for creating reparse points over SMB1
  cifs: Do not query WSL EAs for native SMB symlink
  cifs: Optimize CIFSFindFirst() response when not searching
  cifs: Fix calling CIFSFindFirst() for root path without msearch
  smb: client: fix session setup against servers that require SPN
  smb: client: allow parsing zero-length AV pairs
  cifs: add new field to track the last access time of cfid
  smb: change return type of cached_dir_lease_break() to bool
  cifs: reset iface weights when we cannot find a candidate
  smb: client: fix netns refcount leak after net_passive changes
This commit is contained in:
Linus Torvalds
2025-07-31 21:22:04 -07:00
17 changed files with 343 additions and 105 deletions

View File

@@ -195,6 +195,7 @@ replay_again:
* from @cfids->entries. Caller will put last reference if the latter.
*/
if (cfid->has_lease && cfid->time) {
cfid->last_access_time = jiffies;
spin_unlock(&cfids->cfid_list_lock);
*ret_cfid = cfid;
kfree(utf16_path);
@@ -363,6 +364,7 @@ replay_again:
cfid->file_all_info_is_valid = true;
cfid->time = jiffies;
cfid->last_access_time = jiffies;
spin_unlock(&cfids->cfid_list_lock);
/* At this point the directory handle is fully cached */
rc = 0;
@@ -617,7 +619,7 @@ static void cached_dir_put_work(struct work_struct *work)
queue_work(serverclose_wq, &cfid->close_work);
}
int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
{
struct cached_fids *cfids = tcon->cfids;
struct cached_fid *cfid;
@@ -730,8 +732,8 @@ static void cfids_laundromat_worker(struct work_struct *work)
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
if (cfid->time &&
time_after(jiffies, cfid->time + HZ * dir_cache_timeout)) {
if (cfid->last_access_time &&
time_after(jiffies, cfid->last_access_time + HZ * dir_cache_timeout)) {
cfid->on_list = false;
list_move(&cfid->entry, &entry);
cfids->num_entries--;

View File

@@ -14,7 +14,6 @@ struct cached_dirent {
char *name;
int namelen;
loff_t pos;
struct cifs_fattr fattr;
};
@@ -39,6 +38,7 @@ struct cached_fid {
bool on_list:1;
bool file_all_info_is_valid:1;
unsigned long time; /* jiffies of when lease was taken */
unsigned long last_access_time; /* jiffies of when last accessed */
struct kref refcount;
struct cifs_fid fid;
spinlock_t fid_lock;
@@ -80,6 +80,6 @@ extern void drop_cached_dir_by_name(const unsigned int xid,
struct cifs_sb_info *cifs_sb);
extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
extern bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
#endif /* _CACHED_DIR_H */

View File

@@ -26,6 +26,7 @@
#include "smbdirect.h"
#endif
#include "cifs_swn.h"
#include "cached_dir.h"
void
cifs_dump_mem(char *label, void *data, int length)
@@ -280,6 +281,54 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
return 0;
}
static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
{
struct list_head *stmp, *tmp, *tmp1;
struct TCP_Server_Info *server;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct cached_fids *cfids;
struct cached_fid *cfid;
LIST_HEAD(entry);
seq_puts(m, "# Version:1\n");
seq_puts(m, "# Format:\n");
seq_puts(m, "# <tree id> <sess id> <persistent fid> <path>\n");
spin_lock(&cifs_tcp_ses_lock);
list_for_each(stmp, &cifs_tcp_ses_list) {
server = list_entry(stmp, struct TCP_Server_Info,
tcp_ses_list);
list_for_each(tmp, &server->smb_ses_list) {
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
list_for_each(tmp1, &ses->tcon_list) {
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
cfids = tcon->cfids;
spin_lock(&cfids->cfid_list_lock); /* check lock ordering */
seq_printf(m, "Num entries: %d\n", cfids->num_entries);
list_for_each_entry(cfid, &cfids->entries, entry) {
seq_printf(m, "0x%x 0x%llx 0x%llx %s",
tcon->tid,
ses->Suid,
cfid->fid.persistent_fid,
cfid->path);
if (cfid->file_all_info_is_valid)
seq_printf(m, "\tvalid file info");
if (cfid->dirents.is_valid)
seq_printf(m, ", valid dirents");
seq_printf(m, "\n");
}
spin_unlock(&cfids->cfid_list_lock);
}
}
}
spin_unlock(&cifs_tcp_ses_lock);
seq_putc(m, '\n');
return 0;
}
static __always_inline const char *compression_alg_str(__le16 alg)
{
switch (alg) {
@@ -863,6 +912,9 @@ cifs_proc_init(void)
proc_create_single("open_files", 0400, proc_fs_cifs,
cifs_debug_files_proc_show);
proc_create_single("open_dirs", 0400, proc_fs_cifs,
cifs_debug_dirs_proc_show);
proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops);
proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops);
proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops);
@@ -907,6 +959,7 @@ cifs_proc_clean(void)
remove_proc_entry("DebugData", proc_fs_cifs);
remove_proc_entry("open_files", proc_fs_cifs);
remove_proc_entry("open_dirs", proc_fs_cifs);
remove_proc_entry("cifsFYI", proc_fs_cifs);
remove_proc_entry("traceSMB", proc_fs_cifs);
remove_proc_entry("Stats", proc_fs_cifs);

View File

@@ -343,7 +343,7 @@ static struct ntlmssp2_name *find_next_av(struct cifs_ses *ses,
len = AV_LEN(av);
if (AV_TYPE(av) == NTLMSSP_AV_EOL)
return NULL;
if (!len || (u8 *)av + sizeof(*av) + len > end)
if ((u8 *)av + sizeof(*av) + len > end)
return NULL;
return av;
}
@@ -363,7 +363,7 @@ static int find_av_name(struct cifs_ses *ses, u16 type, char **name, u16 maxlen)
av_for_each_entry(ses, av) {
len = AV_LEN(av);
if (AV_TYPE(av) != type)
if (AV_TYPE(av) != type || !len)
continue;
if (!IS_ALIGNED(len, sizeof(__le16))) {
cifs_dbg(VFS | ONCE, "%s: bad length(%u) for type %u\n",
@@ -532,17 +532,67 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash, struct shash_
return rc;
}
/*
* Set up NTLMv2 response blob with SPN (cifs/<hostname>) appended to the
* existing list of AV pairs.
*/
static int set_auth_key_response(struct cifs_ses *ses)
{
size_t baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
size_t len, spnlen, tilen = 0, num_avs = 2 /* SPN + EOL */;
struct TCP_Server_Info *server = ses->server;
char *spn __free(kfree) = NULL;
struct ntlmssp2_name *av;
char *rsp = NULL;
int rc;
spnlen = strlen(server->hostname);
len = sizeof("cifs/") + spnlen;
spn = kmalloc(len, GFP_KERNEL);
if (!spn) {
rc = -ENOMEM;
goto out;
}
spnlen = scnprintf(spn, len, "cifs/%.*s",
(int)spnlen, server->hostname);
av_for_each_entry(ses, av)
tilen += sizeof(*av) + AV_LEN(av);
len = baselen + tilen + spnlen * sizeof(__le16) + num_avs * sizeof(*av);
rsp = kmalloc(len, GFP_KERNEL);
if (!rsp) {
rc = -ENOMEM;
goto out;
}
memcpy(rsp + baselen, ses->auth_key.response, tilen);
av = (void *)(rsp + baselen + tilen);
av->type = cpu_to_le16(NTLMSSP_AV_TARGET_NAME);
av->length = cpu_to_le16(spnlen * sizeof(__le16));
cifs_strtoUTF16((__le16 *)av->data, spn, spnlen, ses->local_nls);
av = (void *)((__u8 *)av + sizeof(*av) + AV_LEN(av));
av->type = cpu_to_le16(NTLMSSP_AV_EOL);
av->length = 0;
rc = 0;
ses->auth_key.len = len;
out:
ses->auth_key.response = rsp;
return rc;
}
int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
struct shash_desc *hmacmd5 = NULL;
int rc;
int baselen;
unsigned int tilen;
unsigned char *tiblob = NULL; /* target info blob */
struct ntlmv2_resp *ntlmv2;
char ntlmv2_hash[16];
unsigned char *tiblob = NULL; /* target info blob */
__le64 rsp_timestamp;
__u64 cc;
int rc;
if (nls_cp == NULL) {
cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__);
@@ -588,32 +638,25 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
* (as Windows 7 does)
*/
rsp_timestamp = find_timestamp(ses);
get_random_bytes(&cc, sizeof(cc));
cifs_server_lock(ses->server);
baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
tilen = ses->auth_key.len;
tiblob = ses->auth_key.response;
ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL);
if (!ses->auth_key.response) {
rc = -ENOMEM;
rc = set_auth_key_response(ses);
if (rc) {
ses->auth_key.len = 0;
goto setup_ntlmv2_rsp_ret;
goto unlock;
}
ses->auth_key.len += baselen;
ntlmv2 = (struct ntlmv2_resp *)
(ses->auth_key.response + CIFS_SESS_KEY_SIZE);
ntlmv2->blob_signature = cpu_to_le32(0x00000101);
ntlmv2->reserved = 0;
ntlmv2->time = rsp_timestamp;
get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal));
ntlmv2->client_chal = cc;
ntlmv2->reserved2 = 0;
memcpy(ses->auth_key.response + baselen, tiblob, tilen);
cifs_server_lock(ses->server);
rc = cifs_alloc_hash("hmac(md5)", &hmacmd5);
if (rc) {
cifs_dbg(VFS, "Could not allocate HMAC-MD5, rc=%d\n", rc);

View File

@@ -627,12 +627,14 @@ struct smb_version_operations {
bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
struct reparse_data_buffer * (*get_reparse_point_buffer)(const struct kvec *rsp_iov,
u32 *plen);
int (*create_reparse_symlink)(const unsigned int xid,
struct inode *inode,
struct dentry *dentry,
struct cifs_tcon *tcon,
const char *full_path,
const char *symname);
struct inode * (*create_reparse_inode)(struct cifs_open_info_data *data,
struct super_block *sb,
const unsigned int xid,
struct cifs_tcon *tcon,
const char *full_path,
bool directory,
struct kvec *reparse_iov,
struct kvec *xattr_iov);
};
struct smb_version_values {

View File

@@ -483,6 +483,14 @@ extern int cifs_query_reparse_point(const unsigned int xid,
const char *full_path,
u32 *tag, struct kvec *rsp,
int *rsp_buftype);
extern struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
struct super_block *sb,
const unsigned int xid,
struct cifs_tcon *tcon,
const char *full_path,
bool directory,
struct kvec *reparse_iov,
struct kvec *xattr_iov);
extern int CIFSSMB_set_compression(const unsigned int xid,
struct cifs_tcon *tcon, __u16 fid);
extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms,

View File

@@ -2851,6 +2851,134 @@ error:
return rc;
}
struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
struct super_block *sb,
const unsigned int xid,
struct cifs_tcon *tcon,
const char *full_path,
bool directory,
struct kvec *reparse_iov,
struct kvec *xattr_iov)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_open_parms oparms;
TRANSACT_IOCTL_REQ *io_req;
struct inode *new = NULL;
struct kvec in_iov[2];
struct kvec out_iov;
struct cifs_fid fid;
int io_req_len;
int oplock = 0;
int buf_type = 0;
int rc;
cifs_tcon_dbg(FYI, "%s: path=%s\n", __func__, full_path);
/*
* If server filesystem does not support reparse points then do not
* attempt to create reparse point. This will prevent creating unusable
* empty object on the server.
*/
if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS))
return ERR_PTR(-EOPNOTSUPP);
#ifndef CONFIG_CIFS_XATTR
if (xattr_iov)
return ERR_PTR(-EOPNOTSUPP);
#endif
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
FILE_READ_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA,
FILE_CREATE,
(directory ? CREATE_NOT_FILE : CREATE_NOT_DIR) | OPEN_REPARSE_POINT,
ACL_NO_MODE);
oparms.fid = &fid;
rc = CIFS_open(xid, &oparms, &oplock, NULL);
if (rc)
return ERR_PTR(rc);
#ifdef CONFIG_CIFS_XATTR
if (xattr_iov) {
struct smb2_file_full_ea_info *ea;
ea = &((struct smb2_create_ea_ctx *)xattr_iov->iov_base)->ea;
while (1) {
rc = CIFSSMBSetEA(xid,
tcon,
full_path,
&ea->ea_data[0],
&ea->ea_data[ea->ea_name_length+1],
le16_to_cpu(ea->ea_value_length),
cifs_sb->local_nls,
cifs_sb);
if (rc)
goto out_close;
if (le32_to_cpu(ea->next_entry_offset) == 0)
break;
ea = (struct smb2_file_full_ea_info *)((u8 *)ea +
le32_to_cpu(ea->next_entry_offset));
}
}
#endif
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **)&io_req, NULL);
if (rc)
goto out_close;
inc_rfc1001_len(io_req, sizeof(io_req->Pad));
io_req_len = be32_to_cpu(io_req->hdr.smb_buf_length) + sizeof(io_req->hdr.smb_buf_length);
/* NT IOCTL response contains one-word long output setup buffer with size of output data. */
io_req->MaxSetupCount = 1;
/* NT IOCTL response does not contain output parameters. */
io_req->MaxParameterCount = cpu_to_le32(0);
/* FSCTL_SET_REPARSE_POINT response contains empty output data. */
io_req->MaxDataCount = cpu_to_le32(0);
io_req->TotalParameterCount = cpu_to_le32(0);
io_req->TotalDataCount = cpu_to_le32(reparse_iov->iov_len);
io_req->ParameterCount = io_req->TotalParameterCount;
io_req->ParameterOffset = cpu_to_le32(0);
io_req->DataCount = io_req->TotalDataCount;
io_req->DataOffset = cpu_to_le32(offsetof(typeof(*io_req), Data) -
sizeof(io_req->hdr.smb_buf_length));
io_req->SetupCount = 4;
io_req->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
io_req->FunctionCode = cpu_to_le32(FSCTL_SET_REPARSE_POINT);
io_req->Fid = fid.netfid;
io_req->IsFsctl = 1;
io_req->IsRootFlag = 0;
io_req->ByteCount = cpu_to_le16(le32_to_cpu(io_req->DataCount) + sizeof(io_req->Pad));
inc_rfc1001_len(io_req, reparse_iov->iov_len);
in_iov[0].iov_base = (char *)io_req;
in_iov[0].iov_len = io_req_len;
in_iov[1] = *reparse_iov;
rc = SendReceive2(xid, tcon->ses, in_iov, ARRAY_SIZE(in_iov), &buf_type,
CIFS_NO_RSP_BUF, &out_iov);
cifs_buf_release(io_req);
if (!rc)
rc = cifs_get_inode_info(&new, full_path, data, sb, xid, NULL);
out_close:
CIFSSMBClose(xid, tcon, fid.netfid);
/*
* If CREATE was successful but FSCTL_SET_REPARSE_POINT failed then
* remove the intermediate object created by CREATE. Otherwise
* empty object stay on the server when reparse call failed.
*/
if (rc)
CIFSSMBDelFile(xid, tcon, full_path, cifs_sb, NULL);
return rc ? ERR_PTR(rc) : new;
}
int
CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
__u16 fid)
@@ -4020,6 +4148,12 @@ findFirstRetry:
pSMB->FileName[name_len] = 0;
pSMB->FileName[name_len+1] = 0;
name_len += 2;
} else if (!searchName[0]) {
pSMB->FileName[0] = CIFS_DIR_SEP(cifs_sb);
pSMB->FileName[1] = 0;
pSMB->FileName[2] = 0;
pSMB->FileName[3] = 0;
name_len = 4;
}
} else {
name_len = copy_path_name(pSMB->FileName, searchName);
@@ -4031,6 +4165,10 @@ findFirstRetry:
pSMB->FileName[name_len] = '*';
pSMB->FileName[name_len+1] = 0;
name_len += 2;
} else if (!searchName[0]) {
pSMB->FileName[0] = CIFS_DIR_SEP(cifs_sb);
pSMB->FileName[1] = 0;
name_len = 2;
}
}
@@ -4057,7 +4195,7 @@ findFirstRetry:
pSMB->SearchAttributes =
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
ATTR_DIRECTORY);
pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
pSMB->SearchCount = cpu_to_le16(msearch ? CIFSMaxBufSize/sizeof(FILE_UNIX_INFO) : 1);
pSMB->SearchFlags = cpu_to_le16(search_flags);
pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);

View File

@@ -3362,18 +3362,15 @@ generic_ip_connect(struct TCP_Server_Info *server)
struct net *net = cifs_net_ns(server);
struct sock *sk;
rc = __sock_create(net, sfamily, SOCK_STREAM,
IPPROTO_TCP, &server->ssocket, 1);
rc = sock_create_kern(net, sfamily, SOCK_STREAM,
IPPROTO_TCP, &server->ssocket);
if (rc < 0) {
cifs_server_dbg(VFS, "Error %d creating socket\n", rc);
return rc;
}
sk = server->ssocket->sk;
__netns_tracker_free(net, &sk->ns_tracker, false);
sk->sk_net_refcnt = 1;
get_net_track(net, &sk->ns_tracker, GFP_KERNEL);
sock_inuse_add(net, 1);
sk_net_refcnt_upgrade(sk);
/* BB other socket options to set KEEPALIVE, NODELAY? */
cifs_dbg(FYI, "Socket created\n");

View File

@@ -1475,35 +1475,21 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
pr_warn("username too long\n");
goto cifs_parse_mount_err;
}
ctx->username = kstrdup(param->string, GFP_KERNEL);
if (ctx->username == NULL) {
cifs_errorf(fc, "OOM when copying username string\n");
goto cifs_parse_mount_err;
}
ctx->username = no_free_ptr(param->string);
break;
case Opt_pass:
kfree_sensitive(ctx->password);
ctx->password = NULL;
if (strlen(param->string) == 0)
break;
ctx->password = kstrdup(param->string, GFP_KERNEL);
if (ctx->password == NULL) {
cifs_errorf(fc, "OOM when copying password string\n");
goto cifs_parse_mount_err;
}
ctx->password = no_free_ptr(param->string);
break;
case Opt_pass2:
kfree_sensitive(ctx->password2);
ctx->password2 = NULL;
if (strlen(param->string) == 0)
break;
ctx->password2 = kstrdup(param->string, GFP_KERNEL);
if (ctx->password2 == NULL) {
cifs_errorf(fc, "OOM when copying password2 string\n");
goto cifs_parse_mount_err;
}
ctx->password2 = no_free_ptr(param->string);
break;
case Opt_ip:
if (strlen(param->string) == 0) {
@@ -1526,11 +1512,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
}
kfree(ctx->domainname);
ctx->domainname = kstrdup(param->string, GFP_KERNEL);
if (ctx->domainname == NULL) {
cifs_errorf(fc, "OOM when copying domainname string\n");
goto cifs_parse_mount_err;
}
ctx->domainname = no_free_ptr(param->string);
cifs_dbg(FYI, "Domain name set\n");
break;
case Opt_srcaddr:
@@ -1550,11 +1532,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
if (strncasecmp(param->string, "default", 7) != 0) {
kfree(ctx->iocharset);
ctx->iocharset = kstrdup(param->string, GFP_KERNEL);
if (ctx->iocharset == NULL) {
cifs_errorf(fc, "OOM when copying iocharset string\n");
goto cifs_parse_mount_err;
}
ctx->iocharset = no_free_ptr(param->string);
}
/* if iocharset not set then load_nls_default
* is used by caller

View File

@@ -19,6 +19,7 @@
#include "smb2proto.h"
#include "cifs_ioctl.h"
#include "fs_context.h"
#include "reparse.h"
/*
* M-F Symlink Functions - Begin
@@ -570,7 +571,6 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
int rc = -EOPNOTSUPP;
unsigned int xid;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct TCP_Server_Info *server;
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
const char *full_path;
@@ -593,7 +593,6 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
goto symlink_exit;
}
pTcon = tlink_tcon(tlink);
server = cifs_pick_channel(pTcon->ses);
full_path = build_path_from_dentry(direntry, page);
if (IS_ERR(full_path)) {
@@ -643,13 +642,9 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
case CIFS_SYMLINK_TYPE_NATIVE:
case CIFS_SYMLINK_TYPE_NFS:
case CIFS_SYMLINK_TYPE_WSL:
if (server->ops->create_reparse_symlink &&
(le32_to_cpu(pTcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS)) {
rc = server->ops->create_reparse_symlink(xid, inode,
direntry,
pTcon,
full_path,
symname);
if (le32_to_cpu(pTcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS) {
rc = create_reparse_symlink(xid, inode, direntry, pTcon,
full_path, symname);
goto symlink_exit;
}
break;

View File

@@ -34,7 +34,7 @@ static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
const char *symname,
bool *directory);
int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
int create_reparse_symlink(const unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, const char *symname)
{
@@ -227,7 +227,8 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
iov.iov_base = buf;
iov.iov_len = len;
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
new = tcon->ses->server->ops->create_reparse_inode(
&data, inode->i_sb, xid,
tcon, full_path, directory,
&iov, NULL);
if (!IS_ERR(new))
@@ -399,7 +400,8 @@ static int create_native_socket(const unsigned int xid, struct inode *inode,
struct inode *new;
int rc = 0;
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
new = tcon->ses->server->ops->create_reparse_inode(
&data, inode->i_sb, xid,
tcon, full_path, false, &iov, NULL);
if (!IS_ERR(new))
d_instantiate(dentry, new);
@@ -492,7 +494,8 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
.symlink_target = kstrdup(symname, GFP_KERNEL),
};
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
new = tcon->ses->server->ops->create_reparse_inode(
&data, inode->i_sb, xid,
tcon, full_path, false, &iov, NULL);
if (!IS_ERR(new))
d_instantiate(dentry, new);
@@ -685,7 +688,8 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
memcpy(data.wsl.eas, &cc->ea, len);
data.wsl.eas_len = len;
new = smb2_get_reparse_inode(&data, inode->i_sb,
new = tcon->ses->server->ops->create_reparse_inode(
&data, inode->i_sb,
xid, tcon, full_path, false,
&reparse_iov, &xattr_iov);
if (!IS_ERR(new))
@@ -698,7 +702,7 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
return rc;
}
int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
int mknod_reparse(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev)
{

View File

@@ -129,10 +129,10 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr,
struct cifs_open_info_data *data);
int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
int create_reparse_symlink(const unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, const char *symname);
int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
int mknod_reparse(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev);
struct reparse_data_buffer *smb2_get_reparse_point_buffer(const struct kvec *rsp_iov, u32 *len);

View File

@@ -332,6 +332,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
struct cifs_server_iface *old_iface = NULL;
struct cifs_server_iface *last_iface = NULL;
struct sockaddr_storage ss;
int retry = 0;
spin_lock(&ses->chan_lock);
chan_index = cifs_ses_get_chan_index(ses, server);
@@ -360,6 +361,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
return;
}
try_again:
last_iface = list_last_entry(&ses->iface_list, struct cifs_server_iface,
iface_head);
iface_min_speed = last_iface->speed;
@@ -397,6 +399,13 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
}
if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
list_for_each_entry(iface, &ses->iface_list, iface_head)
iface->weight_fulfilled = 0;
/* see if it can be satisfied in second attempt */
if (!retry++)
goto try_again;
iface = NULL;
cifs_dbg(FYI, "unable to find a suitable iface\n");
}

View File

@@ -16,6 +16,7 @@
#include "fs_context.h"
#include "nterr.h"
#include "smberr.h"
#include "reparse.h"
/*
* An NT cancel request header looks just like the original request except:
@@ -1263,17 +1264,26 @@ cifs_make_node(unsigned int xid, struct inode *inode,
if (rc == 0)
d_instantiate(dentry, newinode);
return rc;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
/*
* Check if mounted with mount parm 'sfu' mount parm.
* SFU emulation should work with all servers
* and was used by default in earlier versions of Windows.
*/
return cifs_sfu_make_node(xid, inode, dentry, tcon,
full_path, mode, dev);
} else if (le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS) {
/*
* mknod via reparse points requires server support for
* storing reparse points, which is available since
* Windows 2000, but was not widely used until release
* of Windows Server 2012 by the Windows NFS server.
*/
return mknod_reparse(xid, inode, dentry, tcon,
full_path, mode, dev);
} else {
return -EOPNOTSUPP;
}
/*
* Check if mounted with mount parm 'sfu' mount parm.
* SFU emulation should work with all servers, but only
* supports block and char device, socket & fifo,
* and was used by default in earlier versions of Windows
*/
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
return -EPERM;
return cifs_sfu_make_node(xid, inode, dentry, tcon,
full_path, mode, dev);
}
static bool
@@ -1370,6 +1380,7 @@ struct smb_version_operations smb1_operations = {
.create_hardlink = CIFSCreateHardLink,
.query_symlink = cifs_query_symlink,
.get_reparse_point_buffer = cifs_get_reparse_point_buffer,
.create_reparse_inode = cifs_create_reparse_inode,
.open = cifs_open_file,
.set_fid = cifs_set_fid,
.close = cifs_close_file,

View File

@@ -1058,10 +1058,11 @@ int smb2_query_path_info(const unsigned int xid,
* Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
* response.
*/
if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK) {
cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
if (!tcon->posix_extensions)
cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
if (!tcon->posix_extensions)
cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
}
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
FILE_READ_ATTRIBUTES |
@@ -1320,7 +1321,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
return rc;
}
struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
struct super_block *sb,
const unsigned int xid,
struct cifs_tcon *tcon,

View File

@@ -5262,7 +5262,7 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
full_path, mode, dev);
} else if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS)
|| (tcon->posix_extensions)) {
rc = smb2_mknod_reparse(xid, inode, dentry, tcon,
rc = mknod_reparse(xid, inode, dentry, tcon,
full_path, mode, dev);
}
return rc;
@@ -5321,7 +5321,7 @@ struct smb_version_operations smb20_operations = {
.get_reparse_point_buffer = smb2_get_reparse_point_buffer,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
.create_reparse_symlink = smb2_create_reparse_symlink,
.create_reparse_inode = smb2_create_reparse_inode,
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
@@ -5424,7 +5424,7 @@ struct smb_version_operations smb21_operations = {
.get_reparse_point_buffer = smb2_get_reparse_point_buffer,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
.create_reparse_symlink = smb2_create_reparse_symlink,
.create_reparse_inode = smb2_create_reparse_inode,
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
@@ -5531,7 +5531,7 @@ struct smb_version_operations smb30_operations = {
.get_reparse_point_buffer = smb2_get_reparse_point_buffer,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
.create_reparse_symlink = smb2_create_reparse_symlink,
.create_reparse_inode = smb2_create_reparse_inode,
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
@@ -5647,7 +5647,7 @@ struct smb_version_operations smb311_operations = {
.get_reparse_point_buffer = smb2_get_reparse_point_buffer,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
.create_reparse_symlink = smb2_create_reparse_symlink,
.create_reparse_inode = smb2_create_reparse_inode,
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,

View File

@@ -54,7 +54,7 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server,
extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *path,
__u32 *reparse_tag);
struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
struct super_block *sb,
const unsigned int xid,
struct cifs_tcon *tcon,
@@ -314,9 +314,6 @@ int smb311_posix_query_path_info(const unsigned int xid,
int posix_info_parse(const void *beg, const void *end,
struct smb2_posix_info_parsed *out);
int posix_info_sid_size(const void *beg, const void *end);
int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, const char *symname);
int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev);