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:
@@ -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--;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user