Merge tag 'v7.2-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:
 - DFS cache allocation fix
 - DFS referral bounds check fix
 - Fix absolute symlinks when mounting with POSIX extensions
 - Fixes for incorrect nlink returned by fstat
 - Fix atime in read completion
 - Fix busy dentry on umount
 - ioctl_query_info buffer overflow fix
 - Two fixes for creating special files with SFU
 - Fix mode mask in parse_dacl
 - SMB1 is_path_accessible wildcard fix and minor SMB1 cleanup
 - smb2_check_message fix
 - Debug message improvement
 - Minor cleanup

* tag 'v7.2-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: Remove CIFSSMBSetPathInfoFB() fallback function
  cifs: Fix and improve cifs_is_path_accessible() function
  smb: client: mask server-provided mode to 07777 in modefromsid
  cifs: Show reason why autodisabling serverino support
  smb/client: fix incorrect nlink returned by fstat()
  smb/client: zero-initialize stack-allocated cifs_open_info_data
  smb/client: pass cifs_open_info_data to SMB2_open()
  smb/client: use stack-allocated smb2_file_all_info in smb3_query_mf_symlink()
  smb: client: fix overflow in passthrough ioctl bounds check
  smb: client: fix busy dentry warning on unmount after DIO
  cifs: Fix support for creating SFU fifo
  cifs: Fix support for creating SFU socket
  smb: client: fix atime clamp check in read completion
  cifs: validate DFS referral string offsets
  smb: client: use GFP_KERNEL for DFS cache allocations
  smb: client: restrict implied bcc[0] exemption to responses without data area
  smb: client: preserve leading slash for POSIX absolute symlink targets
  smb: client: refactor cifs_revalidate_mapping() to use clear_and_wake_up_bit()
This commit is contained in:
Linus Torvalds
2026-07-10 18:11:20 -07:00
20 changed files with 146 additions and 121 deletions

View File

@@ -56,6 +56,7 @@ struct cifs_sb_info {
struct smb3_fs_context *ctx;
atomic_t active;
atomic_t mnt_cifs_flags;
atomic_t outstanding_rreq; /* nr of rreqs not yet fully deinitialized */
struct delayed_work prune_tlinks;
struct rcu_head rcu;

View File

@@ -962,7 +962,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
*/
fattr->cf_mode &= ~07777;
fattr->cf_mode |=
le32_to_cpu(ppace[i]->sid.sub_auth[2]);
le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777;
break;
} else {
if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {

View File

@@ -311,6 +311,18 @@ static void cifs_kill_sb(struct super_block *sb)
/* Wait for all opened files to release */
flush_workqueue(deferredclose_wq);
/*
* Wait for all in-flight netfs I/O requests to finish their
* cleanup_work so that any cifsFileInfo final puts they queue
* to fileinfo_put_wq/serverclose_wq have been queued, then
* drain the workqueue so the cfile dentry refs are dropped to
* avoid the busy dentry warning.
*/
wait_var_event(&cifs_sb->outstanding_rreq,
!atomic_read(&cifs_sb->outstanding_rreq));
flush_workqueue(serverclose_wq);
flush_workqueue(fileinfo_put_wq);
/* finally release root dentry */
dput(cifs_sb->root);
cifs_sb->root = NULL;

View File

@@ -250,6 +250,7 @@ struct cifs_open_info_data {
bool adjust_tz;
bool reparse_point;
bool contains_posix_file_info;
bool unknown_nlink;
struct {
/* ioctl response buffer */
struct {

View File

@@ -317,7 +317,7 @@ int generate_smb311signingkey(struct cifs_ses *ses,
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb);
void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb, const char *reason, int rc);
bool couldbe_mf_symlink(const struct cifs_fattr *fattr);
int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,

View File

@@ -5829,38 +5829,6 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
static int
CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb)
{
int oplock = 0;
struct cifs_open_parms oparms;
struct cifs_fid fid;
int rc;
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
.desired_access = GENERIC_WRITE,
.create_options = cifs_create_options(cifs_sb, 0),
.disposition = FILE_OPEN,
.path = fileName,
.fid = &fid,
};
rc = CIFS_open(xid, &oparms, &oplock, NULL);
if (rc)
goto out;
rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid);
CIFSSMBClose(xid, tcon, fid.netfid);
out:
return rc;
}
int
CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
@@ -5939,10 +5907,6 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
if (rc == -EAGAIN)
goto SetTimesRetry;
if (rc == -EOPNOTSUPP)
return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data,
nls_codepage, cifs_sb);
return rc;
}

View File

@@ -3485,6 +3485,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
spin_lock_init(&cifs_sb->tlink_tree_lock);
cifs_sb->tlink_tree = RB_ROOT;
atomic_set(&cifs_sb->outstanding_rreq, 0);
cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n",
ctx->file_mode, ctx->dir_mode);
@@ -3875,7 +3876,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
* 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);
cifs_autodisable_serverino(cifs_sb, "DFS failover may potentially connect to a different server, inode numbers won't match anymore", 0);
/*
* Force the use of prefix path to support failover on DFS paths that resolve to targets
* that have different prefix paths.

View File

@@ -363,10 +363,10 @@ static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
{
struct cache_dfs_tgt *t;
t = kmalloc_obj(*t, GFP_ATOMIC);
t = kmalloc_obj(*t, GFP_KERNEL);
if (!t)
return ERR_PTR(-ENOMEM);
t->name = kstrdup(name, GFP_ATOMIC);
t->name = kstrdup(name, GFP_KERNEL);
if (!t->name) {
kfree(t);
return ERR_PTR(-ENOMEM);
@@ -626,7 +626,7 @@ static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_in
target = READ_ONCE(ce->tgthint);
if (target) {
th = kstrdup(target->name, GFP_ATOMIC);
th = kstrdup(target->name, GFP_KERNEL);
if (!th)
return -ENOMEM;
}
@@ -760,11 +760,11 @@ static int setup_referral(const char *path, struct cache_entry *ce,
memset(ref, 0, sizeof(*ref));
ref->path_name = kstrdup(path, GFP_ATOMIC);
ref->path_name = kstrdup(path, GFP_KERNEL);
if (!ref->path_name)
return -ENOMEM;
ref->node_name = kstrdup(target, GFP_ATOMIC);
ref->node_name = kstrdup(target, GFP_KERNEL);
if (!ref->node_name) {
rc = -ENOMEM;
goto err_free_path;
@@ -1328,7 +1328,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
* 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);
cifs_autodisable_serverino(cifs_sb, "DFS failover may potentially connect to a different server, inode numbers won't match anymore", 0);
/*
* Force the use of prefix path to support failover on DFS paths that resolve to targets
* that have different prefix paths.

View File

@@ -288,6 +288,7 @@ static int cifs_init_request(struct netfs_io_request *rreq, struct file *file)
return smb_EIO1(smb_eio_trace_not_netfs_writeback, rreq->origin);
}
atomic_inc(&cifs_sb->outstanding_rreq);
return 0;
}
@@ -302,16 +303,20 @@ static void cifs_rreq_done(struct netfs_io_request *rreq)
/* we do not want atime to be less than mtime, it broke some apps */
atime = inode_set_atime_to_ts(inode, current_time(inode));
mtime = inode_get_mtime(inode);
if (timespec64_compare(&atime, &mtime))
if (timespec64_compare(&atime, &mtime) < 0)
inode_set_atime_to_ts(inode, inode_get_mtime(inode));
}
static void cifs_free_request(struct netfs_io_request *rreq)
{
struct cifs_io_request *req = container_of(rreq, struct cifs_io_request, rreq);
struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb);
if (req->cfile)
cifsFileInfo_put(req->cfile);
if (atomic_dec_and_test(&cifs_sb->outstanding_rreq))
wake_up_var(&cifs_sb->outstanding_rreq);
}
static void cifs_free_subrequest(struct netfs_io_subrequest *subreq)

View File

@@ -909,6 +909,8 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
memset(fattr, 0, sizeof(*fattr));
if (data->unknown_nlink)
fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
if (info->DeletePending)
fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING;
@@ -1145,7 +1147,7 @@ static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_blo
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
else {
fattr->cf_uniqueid = iunique(sb, ROOT_I);
cifs_autodisable_serverino(cifs_sb);
cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via get_srv_inum", rc);
}
return;
}
@@ -1642,7 +1644,7 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
if (inode_has_hashed_dentries(inode)) {
cifs_autodisable_serverino(CIFS_SB(sb));
cifs_autodisable_serverino(CIFS_SB(sb), "Inode number collision detected", 0);
iput(inode);
fattr->cf_uniqueid = iunique(sb, ROOT_I);
goto retry_iget5_locked;
@@ -1708,8 +1710,9 @@ struct inode *cifs_root_iget(struct super_block *sb)
iget_root:
if (!rc) {
if (fattr.cf_flags & CIFS_FATTR_JUNCTION) {
cifs_dbg(VFS, "Removing junction mark and disabling 'serverino' to prevent inode collisions\n");
fattr.cf_flags &= ~CIFS_FATTR_JUNCTION;
cifs_autodisable_serverino(cifs_sb);
cifs_autodisable_serverino(cifs_sb, "Cannot retrieve attributes for junction point", rc);
}
inode = cifs_iget(sb, &fattr);
}
@@ -2812,9 +2815,7 @@ cifs_revalidate_mapping(struct inode *inode)
}
skip_invalidate:
clear_bit_unlock(CIFS_INO_LOCK, flags);
smp_mb__after_atomic();
wake_up_bit(flags, CIFS_INO_LOCK);
clear_and_wake_up_bit(CIFS_INO_LOCK, flags);
return rc;
}

View File

@@ -234,7 +234,7 @@ cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_open_parms oparms;
struct cifs_io_parms io_parms = {0};
int buf_type = CIFS_NO_BUFFER;
struct cifs_open_info_data query_data;
struct cifs_open_info_data query_data = {};
oparms = (struct cifs_open_parms) {
.tcon = tcon,
@@ -320,7 +320,7 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
int buf_type = CIFS_NO_BUFFER;
__le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct smb2_file_all_info *pfile_info = NULL;
struct cifs_open_info_data data = {};
oparms = (struct cifs_open_parms) {
.tcon = tcon,
@@ -336,20 +336,12 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
if (utf16_path == NULL)
return -ENOMEM;
pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
GFP_KERNEL);
if (pfile_info == NULL) {
kfree(utf16_path);
return -ENOMEM;
}
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, &data, NULL,
NULL, NULL);
if (rc)
goto qmf_out_open_fail;
if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
if (data.fi.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
/* it's not a symlink */
rc = -ENOENT; /* Is there a better rc to return? */
goto qmf_out;
@@ -367,7 +359,6 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
qmf_out_open_fail:
kfree(utf16_path);
kfree(pfile_info);
return rc;
}

View File

@@ -278,7 +278,7 @@ dump_smb(void *buf, int smb_buf_length)
}
void
cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb, const char *reason, int rc)
{
unsigned int sbflags = cifs_sb_flags(cifs_sb);
@@ -290,6 +290,10 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
atomic_andnot(CIFS_MOUNT_SERVER_INUM, &cifs_sb->mnt_cifs_flags);
cifs_sb->mnt_cifs_serverino_autodisabled = true;
if (rc)
cifs_dbg(VFS, "%s: %d\n", reason, rc);
else
cifs_dbg(VFS, "%s\n", reason);
cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n",
tcon ? tcon->tree_name : "new server");
cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n");
@@ -752,6 +756,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
/* copy DfsPath */
if (le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) {
rc = -EINVAL;
goto parse_DFS_referrals_exit;
}
temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
max_len = data_end - temp;
node->path_name = cifs_strndup_from_utf16(temp, max_len,
@@ -762,6 +770,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
}
/* copy link target UNC */
if (le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) {
rc = -EINVAL;
goto parse_DFS_referrals_exit;
}
temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
max_len = data_end - temp;
node->node_name = cifs_strndup_from_utf16(temp, max_len,

View File

@@ -415,7 +415,7 @@ _initiate_cifs_search(const unsigned int xid, struct file *file,
if (rc == 0) {
cifsFile->invalidHandle = false;
} else if (rc == -EOPNOTSUPP && (sbflags & CIFS_MOUNT_SERVER_INUM)) {
cifs_autodisable_serverino(cifs_sb);
cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via query_dir_first", rc);
goto ffirst_retry;
}
error_exit:
@@ -1029,7 +1029,7 @@ static int cifs_filldir(char *find_entry, struct file *file,
fattr.cf_uniqueid = de.ino;
} else {
fattr.cf_uniqueid = iunique(sb, ROOT_I);
cifs_autodisable_serverino(cifs_sb);
cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number from readdir", 0);
}
if ((sbflags & CIFS_MOUNT_MF_SYMLINKS) && couldbe_mf_symlink(&fattr))

View File

@@ -67,6 +67,7 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
char *sym = NULL;
struct kvec iov;
bool directory;
int path_len;
int rc = 0;
if (strlen(symname) > REPARSE_SYM_PATH_MAX)
@@ -168,7 +169,21 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
if (!(sbflags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/')
sym[0] = sym[1] = sym[2] = sym[5] = '_';
path = cifs_convert_path_to_utf16(sym, cifs_sb);
/*
* On a POSIX paths mount the symlink target is stored verbatim, so
* convert it with cifs_strndup_to_utf16(). cifs_convert_path_to_utf16()
* must not be used here: it strips a leading path separator (it is
* meant for share-relative SMB paths), which would corrupt an absolute
* POSIX symlink target such as "/foo/bar". Using NO_MAP_UNI_RSVD also
* matches the readback path in smb2_parse_native_symlink().
*/
if (sbflags & CIFS_MOUNT_POSIX_PATHS)
path = cifs_strndup_to_utf16(sym, strlen(sym), &path_len,
cifs_sb->local_nls,
NO_MAP_UNI_RSVD);
else
path = cifs_convert_path_to_utf16(sym, cifs_sb);
if (!path) {
rc = -ENOMEM;
goto out;

View File

@@ -505,21 +505,27 @@ static int
cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path)
{
int rc;
FILE_ALL_INFO *file_info;
int rc = -EOPNOTSUPP;
FILE_ALL_INFO file_info;
file_info = kmalloc_obj(FILE_ALL_INFO);
if (file_info == NULL)
return -ENOMEM;
if (tcon->ses->capabilities & CAP_NT_SMBS)
rc = CIFSSMBQPathInfo(xid, tcon, full_path, &file_info,
0 /* not legacy */, cifs_sb->local_nls,
cifs_remap(cifs_sb));
rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info,
0 /* not legacy */, cifs_sb->local_nls,
cifs_remap(cifs_sb));
/*
* Non-UNICODE variant of fallback functions below expands wildcards,
* so they cannot be used for querying paths with wildcard characters.
* Therefore for such paths returns -ENOENT as they cannot exist.
*/
if ((rc == -EOPNOTSUPP || rc == -EINVAL) &&
!(tcon->ses->capabilities & CAP_UNICODE) &&
strpbrk(full_path, "*?\"><"))
rc = -ENOENT;
if (rc == -EOPNOTSUPP || rc == -EINVAL)
rc = SMBQueryInformation(xid, tcon, full_path, file_info,
rc = SMBQueryInformation(xid, tcon, full_path, &file_info,
cifs_sb->local_nls, cifs_remap(cifs_sb));
kfree(file_info);
return rc;
}
@@ -949,7 +955,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
struct cifs_open_parms oparms;
struct cifsFileInfo *open_file;
FILE_BASIC_INFO new_buf;
struct cifs_open_info_data query_data;
struct cifs_open_info_data query_data = {};
__le64 write_time = buf->LastWriteTime;
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);

View File

@@ -154,8 +154,6 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *smb2_path;
__u8 smb2_oplock;
struct cifs_open_info_data *data = buf;
struct smb2_file_all_info file_info = {};
struct smb2_file_all_info *smb2_data = data ? &file_info : NULL;
struct kvec err_iov = {};
int err_buftype = CIFS_NO_BUFFER;
struct cifs_fid *fid = oparms->fid;
@@ -182,14 +180,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
}
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, &err_iov,
&err_buftype);
if (rc == -EACCES && retry_without_read_attributes) {
free_rsp_buf(err_buftype, err_iov.iov_base);
memset(&err_iov, 0, sizeof(err_iov));
err_buftype = CIFS_NO_BUFFER;
oparms->desired_access &= ~FILE_READ_ATTRIBUTES;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, &err_iov,
&err_buftype);
}
if (rc && data) {
@@ -202,9 +200,9 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
oparms->path,
&data->symlink_target);
if (!rc) {
memset(smb2_data, 0, sizeof(*smb2_data));
memset(&data->fi, 0, sizeof(data->fi));
oparms->create_options |= OPEN_REPARSE_POINT;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data,
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data,
NULL, NULL, NULL);
oparms->create_options &= ~OPEN_REPARSE_POINT;
}
@@ -238,23 +236,22 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
rc = 0;
}
if (smb2_data) {
if (data) {
/* if open response does not have IndexNumber field - get it */
if (smb2_data->IndexNumber == 0) {
if (data->fi.IndexNumber == 0) {
rc = SMB2_get_srv_num(xid, oparms->tcon,
fid->persistent_fid,
fid->volatile_fid,
&smb2_data->IndexNumber);
&data->fi.IndexNumber);
if (rc) {
/*
* let get_inode_info disable server inode
* numbers
*/
smb2_data->IndexNumber = 0;
data->fi.IndexNumber = 0;
rc = 0;
}
}
memcpy(&data->fi, smb2_data, sizeof(data->fi));
}
*oplock = smb2_oplock;

View File

@@ -19,6 +19,8 @@
#include "nterr.h"
#include "cached_dir.h"
static unsigned int __smb2_calc_size(void *buf, bool *have_data);
static int
check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
{
@@ -145,6 +147,7 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
int command;
__u32 calc_len; /* calculated length */
__u64 mid;
bool have_data;
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
@@ -228,7 +231,8 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
}
}
calc_len = smb2_calc_size(buf);
have_data = false;
calc_len = __smb2_calc_size(buf, &have_data);
/* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might
* be 0, and not a real miscalculation */
@@ -247,8 +251,13 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
/* Windows 7 server returns 24 bytes more */
if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)
return 0;
/* server can return one byte more due to implied bcc[0] */
if (calc_len == len + 1)
/*
* Server can return one byte more due to implied bcc[0].
* Allow it only when there is no data area; if data_length > 0
* the +1 gap indicates an overreported data length rather than
* the bcc[0] omission.
*/
if (calc_len == len + 1 && !have_data)
return 0;
/*
@@ -409,14 +418,17 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr)
/*
* Calculate the size of the SMB message based on the fixed header
* portion, the number of word parameters and the data portion of the message.
* If have_data is non-NULL, it is set to true when a non-empty data area was
* found (data_length > 0), allowing callers to distinguish the implied bcc[0]
* case (no data area) from an overreported data length.
*/
unsigned int
smb2_calc_size(void *buf)
static unsigned int
__smb2_calc_size(void *buf, bool *have_data)
{
struct smb2_pdu *pdu = buf;
struct smb2_hdr *shdr = &pdu->hdr;
int offset; /* the offset from the beginning of SMB to data area */
int data_length; /* the length of the variable length data area */
int data_length = 0; /* the length of the variable length data area */
/* Structure Size has already been checked to make sure it is 64 */
int len = le16_to_cpu(shdr->StructureSize);
@@ -449,9 +461,17 @@ smb2_calc_size(void *buf)
}
calc_size_exit:
cifs_dbg(FYI, "SMB2 len %d\n", len);
if (have_data)
*have_data = (data_length > 0);
return len;
}
unsigned int
smb2_calc_size(void *buf)
{
return __smb2_calc_size(buf, NULL);
}
/* Note: caller must free return buffer */
__le16 *
cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)

View File

@@ -1772,8 +1772,8 @@ smb2_ioctl_query_info(const unsigned int xid,
if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
if (qi.input_buffer_length > 0 &&
le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length
> rsp_iov[1].iov_len) {
size_add(le32_to_cpu(io_rsp->OutputOffset),
qi.input_buffer_length) > rsp_iov[1].iov_len) {
rc = -EFAULT;
goto out;
}
@@ -5237,7 +5237,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
{
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
struct cifs_open_info_data idata;
struct cifs_open_info_data idata = {};
struct cifs_io_parms io_parms = {};
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_fid fid;
@@ -5286,16 +5286,13 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
data = (u8 *)symname_utf16;
break;
case S_IFSOCK:
type_len = 8;
strscpy(type, "LnxSOCK");
data = (u8 *)&pdev;
data_len = sizeof(pdev);
/* SFU socket is system file with one zero byte */
type_len = 1;
type[0] = '\0';
break;
case S_IFIFO:
type_len = 8;
strscpy(type, "LnxFIFO");
data = (u8 *)&pdev;
data_len = sizeof(pdev);
/* SFU fifo is system file which is empty */
type_len = 0;
break;
default:
rc = -EPERM;

View File

@@ -3287,7 +3287,7 @@ SMB2_open_free(struct smb_rqst *rqst)
int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf,
__u8 *oplock, struct cifs_open_info_data *buf,
struct create_posix_rsp *posix,
struct kvec *err_iov, int *buftype)
{
@@ -3302,6 +3302,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
int rc = 0;
int flags = 0;
int retries = 0, cur_sleep = 0;
struct smb2_file_all_info *file_info = buf ? &buf->fi : NULL;
replay_again:
/* reinitialize for possible replay */
@@ -3370,21 +3371,22 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId);
#endif /* CIFS_DEBUG2 */
if (buf) {
buf->CreationTime = rsp->CreationTime;
buf->LastAccessTime = rsp->LastAccessTime;
buf->LastWriteTime = rsp->LastWriteTime;
buf->ChangeTime = rsp->ChangeTime;
buf->AllocationSize = rsp->AllocationSize;
buf->EndOfFile = rsp->EndofFile;
buf->Attributes = rsp->FileAttributes;
buf->NumberOfLinks = cpu_to_le32(1);
buf->DeletePending = 0; /* successful open = not delete pending */
if (file_info) {
file_info->CreationTime = rsp->CreationTime;
file_info->LastAccessTime = rsp->LastAccessTime;
file_info->LastWriteTime = rsp->LastWriteTime;
file_info->ChangeTime = rsp->ChangeTime;
file_info->AllocationSize = rsp->AllocationSize;
file_info->EndOfFile = rsp->EndofFile;
file_info->Attributes = rsp->FileAttributes;
file_info->NumberOfLinks = cpu_to_le32(1);
buf->unknown_nlink = true;
file_info->DeletePending = 0; /* successful open = not delete pending */
}
rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch,
oparms->fid->lease_key, oplock, buf, posix);
oparms->fid->lease_key, oplock, file_info, posix);
trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
oparms->create_options, oparms->desired_access,

View File

@@ -136,7 +136,7 @@ int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
struct cifs_tcon *tcon, const struct nls_table *cp);
int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *path, __u8 *oplock, struct smb2_file_all_info *buf,
__le16 *path, __u8 *oplock, struct cifs_open_info_data *buf,
struct create_posix_rsp *posix, struct kvec *err_iov,
int *buftype);
int SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,