mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 14:30:06 -04:00
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:
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user