mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
six client fixes
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmpiexAACgkQiiy9cAdy T1GDPwv9HWIwZSpoOs1ZPneXk4Sj9S1HkkEE+xT4edgKl63O8Wly4DQexZzrWaYg QjCJ6gvYDjzL1NyGOfhtBxz695kGf2Cliq3IgUxWrSyB9S8o2qahJrvaF6kjckWB cu4+U650GxLXygvgx+YXDWrDp03GcP8Dk/iAwq5BdcdaMpPMv7FFSEtf0U4UWERG 1LgzBBVdBHibGIsrZGc6zBxBt20cLdJSc4X7UycSMvfudsc3PDnCvgQVorSROeIE Kgk9gUj4Yv6x+qRHCiFCrPRpNz8nqrEQ+jv3lOhsFEIrvflxX5IVl+NYjufuovgY ERTQ3GwLYmjX4WNg0774jdifUOWducqVVSrp9eQx8ueKgy4FakCm6/ko/jDzzGfn WZ5rCZs0GFtZ8pjlhxINOzkq1wjsZxueLHCQUxp1Ty0I5Sy8q0e8epv0J66iXAwa /doMyvw5COvWEo/ypY3LQ0HcmYwojc3VrGrcabTvgnBHygjFM4UQnqljZejGWy3R tWBjCPxe =ujbz -----END PGP SIGNATURE----- Merge tag 'v7.2-rc4-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6 Pull smb client fixes from Steve French: - Fix leak in cifs_close_deferred_file() - Fix resolving MacOS symlinks - Fix stale file size in readdir - Update git branches in MAINTAINERS file - Fix bounds check in cifs_filldir - Fix checks in parse_dfs_referrals() - Fix DFS referral checks for malformed packet * tag 'v7.2-rc4-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: fix cifsFileInfo leak on kmalloc failure in deferred close drain paths cifs: prevent readdir from changing file size due to stale directory metadata smb: client: handle STATUS_STOPPED_ON_SYMLINK responses without a symlink target Add missing git branch info for cifs and ksmbd to MAINTAINERS file smb: client: bound dirent name against end of SMB response in cifs_filldir smb: client: validate DFS referral PathConsumed
This commit is contained in:
commit
48a5a7ab8d
|
|
@ -6478,7 +6478,7 @@ L: linux-cifs@vger.kernel.org
|
|||
L: samba-technical@lists.samba.org (moderated for non-subscribers)
|
||||
S: Supported
|
||||
W: https://wiki.samba.org/index.php/LinuxCIFS
|
||||
T: git https://git.samba.org/sfrench/cifs-2.6.git
|
||||
T: git https://git.samba.org/sfrench/cifs-2.6.git for-next
|
||||
F: Documentation/admin-guide/cifs/
|
||||
F: fs/smb/client/
|
||||
F: fs/smb/common/
|
||||
|
|
@ -14142,7 +14142,7 @@ R: Sergey Senozhatsky <senozhatsky@chromium.org>
|
|||
R: Tom Talpey <tom@talpey.com>
|
||||
L: linux-cifs@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git https://git.samba.org/ksmbd.git
|
||||
T: git https://git.samba.org/ksmbd.git ksmbd-for-next
|
||||
F: Documentation/filesystems/smb/ksmbd.rst
|
||||
F: fs/smb/common/
|
||||
F: fs/smb/server/
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ cifs_alloc_inode(struct super_block *sb)
|
|||
return NULL;
|
||||
cifs_inode->cifsAttrs = ATTR_ARCHIVE; /* default */
|
||||
cifs_inode->time = 0;
|
||||
cifs_inode->time_last_write = 0;
|
||||
/*
|
||||
* Until the file is open and we have gotten oplock info back from the
|
||||
* server, can not assume caching of file data or metadata.
|
||||
|
|
|
|||
|
|
@ -1566,6 +1566,7 @@ struct cifsInodeInfo {
|
|||
spinlock_t writers_lock;
|
||||
unsigned int writers; /* Number of writers on this inode */
|
||||
unsigned long time; /* jiffies of last update of inode */
|
||||
unsigned long time_last_write; /* jiffies of last writable close or truncate */
|
||||
u64 uniqueid; /* server inode number */
|
||||
u64 createtime; /* creation time on server */
|
||||
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */
|
||||
|
|
|
|||
|
|
@ -1423,11 +1423,21 @@ void smb2_deferred_work_close(struct work_struct *work)
|
|||
{
|
||||
struct cifsFileInfo *cfile = container_of(work,
|
||||
struct cifsFileInfo, deferred.work);
|
||||
struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
|
||||
|
||||
spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
|
||||
spin_lock(&cinode->deferred_lock);
|
||||
cifs_del_deferred_close(cfile);
|
||||
cfile->deferred_close_scheduled = false;
|
||||
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
|
||||
spin_unlock(&cinode->deferred_lock);
|
||||
/*
|
||||
* Refresh time_last_write immediately before the actual server close
|
||||
* so the protection window is anchored to the real close time, not
|
||||
* the earlier userspace close time stored by cifs_close().
|
||||
*/
|
||||
if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&cinode->time_last_write, jiffies);
|
||||
}
|
||||
_cifsFileInfo_put(cfile, true, false);
|
||||
}
|
||||
|
||||
|
|
@ -1457,6 +1467,10 @@ int cifs_close(struct inode *inode, struct file *file)
|
|||
if (file->private_data != NULL) {
|
||||
cfile = file->private_data;
|
||||
file->private_data = NULL;
|
||||
if (file->f_mode & FMODE_WRITE) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&cinode->time_last_write, jiffies);
|
||||
}
|
||||
dclose = kmalloc_obj(struct cifs_deferred_close);
|
||||
if ((cfile->status_file_deleted == false) &&
|
||||
(smb2_can_defer_close(inode, dclose))) {
|
||||
|
|
@ -3225,13 +3239,26 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
|
|||
bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
|
||||
bool from_readdir)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
unsigned long tlw;
|
||||
|
||||
if (!cifsInode)
|
||||
return true;
|
||||
|
||||
cifs_sb = CIFS_SB(cifsInode);
|
||||
|
||||
if (is_inode_writable(cifsInode) ||
|
||||
((cifsInode->oplock & CIFS_CACHE_RW_FLG) != 0 && from_readdir)) {
|
||||
/* This inode is open for write at least once */
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(cifsInode);
|
||||
|
||||
/*
|
||||
* Readdir data is unreliable when we have writable handles or
|
||||
* an exclusive lease -- never allow it to change i_size, even
|
||||
* on direct-IO mounts where the server's directory metadata
|
||||
* can still lag behind the actual file state.
|
||||
*/
|
||||
if (from_readdir)
|
||||
return false;
|
||||
|
||||
if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_DIRECT_IO) {
|
||||
/* since no page cache to corrupt on directio
|
||||
|
|
@ -3243,8 +3270,40 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
|
|||
return true;
|
||||
|
||||
return false;
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* No writable handles open. Check whether we are within the attribute
|
||||
* cache validity window of a recent local modification.
|
||||
*
|
||||
* For the close() path: cifs_close() calls smp_store_release() on
|
||||
* time_last_write before _cifsFileInfo_put() removes the handle under
|
||||
* open_file_lock. That spin_unlock() is a store-release that pairs
|
||||
* with the spin_lock() (load-acquire) in is_inode_writable() above,
|
||||
* so if is_inode_writable() returned false the smp_load_acquire()
|
||||
* below is guaranteed to observe any time_last_write update from a
|
||||
* concurrent close().
|
||||
*
|
||||
* For the setattr/truncate paths: those callers use smp_store_release()
|
||||
* directly; the smp_load_acquire() below pairs with that store. There
|
||||
* is no shared lock between setattr and readdir, so this relies on
|
||||
* acquire-release semantics alone. The store propagation latency on
|
||||
* weakly-ordered architectures (nanoseconds) is negligible relative to
|
||||
* the acregmax window (seconds) and the readdir RPC round-trip
|
||||
* (milliseconds), making this a sound design choice in practice.
|
||||
*
|
||||
* time_last_write == 0 means the inode has never been written locally;
|
||||
* skip the window check to avoid false positives near boot time when
|
||||
* jiffies is still close to INITIAL_JIFFIES on 32-bit systems.
|
||||
*/
|
||||
if (from_readdir) {
|
||||
/* Pairs with smp_store_release() at close and truncate sites. */
|
||||
tlw = smp_load_acquire(&cifsInode->time_last_write);
|
||||
if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cifs_oplock_break(struct work_struct *work)
|
||||
|
|
|
|||
|
|
@ -237,6 +237,8 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
|
|||
if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) {
|
||||
i_size_write(inode, fattr->cf_eof);
|
||||
inode->i_blocks = CIFS_INO_BLOCKS(fattr->cf_bytes);
|
||||
} else if (from_readdir && i_size_read(inode) != fattr->cf_eof) {
|
||||
cifs_i->time = 0;
|
||||
}
|
||||
|
||||
if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) {
|
||||
|
|
@ -3277,6 +3279,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
|
|||
|
||||
if ((attrs->ia_valid & ATTR_SIZE) &&
|
||||
attrs->ia_size != i_size_read(inode)) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&cifsInode->time_last_write, jiffies);
|
||||
truncate_setsize(inode, attrs->ia_size);
|
||||
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
|
||||
|
|
@ -3478,6 +3482,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
|
|||
|
||||
if ((attrs->ia_valid & ATTR_SIZE) &&
|
||||
attrs->ia_size != i_size_read(inode)) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&cifsInode->time_last_write, jiffies);
|
||||
truncate_setsize(inode, attrs->ia_size);
|
||||
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
|
||||
|
|
|
|||
|
|
@ -497,7 +497,7 @@ cifs_del_deferred_close(struct cifsFileInfo *cfile)
|
|||
void
|
||||
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
|
||||
{
|
||||
struct cifsFileInfo *cfile = NULL;
|
||||
struct cifsFileInfo *cfile = NULL, *failed_cfile = NULL;
|
||||
struct file_list *tmp_list, *tmp_next_list;
|
||||
LIST_HEAD(file_head);
|
||||
|
||||
|
|
@ -514,8 +514,10 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
|
|||
|
||||
tmp_list = kmalloc_obj(struct file_list,
|
||||
GFP_ATOMIC);
|
||||
if (tmp_list == NULL)
|
||||
if (tmp_list == NULL) {
|
||||
failed_cfile = cfile;
|
||||
break;
|
||||
}
|
||||
tmp_list->cfile = cfile;
|
||||
list_add_tail(&tmp_list->list, &file_head);
|
||||
}
|
||||
|
|
@ -523,8 +525,24 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
|
|||
}
|
||||
spin_unlock(&cifs_inode->open_file_lock);
|
||||
|
||||
if (failed_cfile) {
|
||||
if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
|
||||
jiffies);
|
||||
}
|
||||
_cifsFileInfo_put(failed_cfile, false, false);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
|
||||
_cifsFileInfo_put(tmp_list->cfile, false, false);
|
||||
struct cifsFileInfo *cfile = tmp_list->cfile;
|
||||
|
||||
if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
|
||||
jiffies);
|
||||
}
|
||||
_cifsFileInfo_put(cfile, false, false);
|
||||
list_del(&tmp_list->list);
|
||||
kfree(tmp_list);
|
||||
}
|
||||
|
|
@ -533,7 +551,7 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
|
|||
void
|
||||
cifs_close_all_deferred_files(struct cifs_tcon *tcon)
|
||||
{
|
||||
struct cifsFileInfo *cfile;
|
||||
struct cifsFileInfo *cfile, *failed_cfile = NULL;
|
||||
struct file_list *tmp_list, *tmp_next_list;
|
||||
LIST_HEAD(file_head);
|
||||
|
||||
|
|
@ -547,8 +565,10 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
|
|||
|
||||
tmp_list = kmalloc_obj(struct file_list,
|
||||
GFP_ATOMIC);
|
||||
if (tmp_list == NULL)
|
||||
if (tmp_list == NULL) {
|
||||
failed_cfile = cfile;
|
||||
break;
|
||||
}
|
||||
tmp_list->cfile = cfile;
|
||||
list_add_tail(&tmp_list->list, &file_head);
|
||||
}
|
||||
|
|
@ -556,8 +576,24 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
|
|||
}
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
|
||||
if (failed_cfile) {
|
||||
if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
|
||||
jiffies);
|
||||
}
|
||||
_cifsFileInfo_put(failed_cfile, true, false);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
|
||||
_cifsFileInfo_put(tmp_list->cfile, true, false);
|
||||
struct cifsFileInfo *cfile = tmp_list->cfile;
|
||||
|
||||
if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
|
||||
jiffies);
|
||||
}
|
||||
_cifsFileInfo_put(cfile, true, false);
|
||||
list_del(&tmp_list->list);
|
||||
kfree(tmp_list);
|
||||
}
|
||||
|
|
@ -604,7 +640,7 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
|
|||
struct dentry *dentry)
|
||||
{
|
||||
struct file_list *tmp_list, *tmp_next_list;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct cifsFileInfo *cfile, *failed_cfile = NULL;
|
||||
LIST_HEAD(file_head);
|
||||
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
|
|
@ -617,16 +653,34 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
|
|||
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
|
||||
|
||||
tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC);
|
||||
if (tmp_list == NULL)
|
||||
if (tmp_list == NULL) {
|
||||
failed_cfile = cfile;
|
||||
break;
|
||||
}
|
||||
tmp_list->cfile = cfile;
|
||||
list_add_tail(&tmp_list->list, &file_head);
|
||||
}
|
||||
}
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
|
||||
if (failed_cfile) {
|
||||
if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
|
||||
jiffies);
|
||||
}
|
||||
_cifsFileInfo_put(failed_cfile, true, false);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
|
||||
_cifsFileInfo_put(tmp_list->cfile, true, false);
|
||||
struct cifsFileInfo *cfile = tmp_list->cfile;
|
||||
|
||||
if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
|
||||
/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
|
||||
smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
|
||||
jiffies);
|
||||
}
|
||||
_cifsFileInfo_put(cfile, true, false);
|
||||
list_del(&tmp_list->list);
|
||||
kfree(tmp_list);
|
||||
}
|
||||
|
|
@ -682,6 +736,8 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
|||
int i, rc = 0;
|
||||
char *data_end;
|
||||
struct dfs_referral_level_3 *ref;
|
||||
unsigned int path_consumed;
|
||||
size_t search_name_len;
|
||||
|
||||
if (rsp_size < sizeof(*rsp)) {
|
||||
cifs_dbg(VFS | ONCE,
|
||||
|
|
@ -728,6 +784,7 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
|||
rc = -ENOMEM;
|
||||
goto parse_DFS_referrals_exit;
|
||||
}
|
||||
search_name_len = strlen(searchName);
|
||||
|
||||
/* collect necessary data from referrals */
|
||||
for (i = 0; i < *num_of_nodes; i++) {
|
||||
|
|
@ -736,21 +793,34 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
|||
struct dfs_info3_param *node = (*target_nodes)+i;
|
||||
|
||||
node->flags = le32_to_cpu(rsp->DFSFlags);
|
||||
path_consumed = le16_to_cpu(rsp->PathConsumed);
|
||||
if (is_unicode) {
|
||||
__le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
|
||||
GFP_KERNEL);
|
||||
if (tmp == NULL) {
|
||||
size_t search_name_utf16_len = search_name_len * 2 + 2;
|
||||
__le16 *tmp;
|
||||
|
||||
if (path_consumed > search_name_utf16_len) {
|
||||
rc = -EINVAL;
|
||||
goto parse_DFS_referrals_exit;
|
||||
}
|
||||
|
||||
tmp = kmalloc(search_name_utf16_len, GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
rc = -ENOMEM;
|
||||
goto parse_DFS_referrals_exit;
|
||||
}
|
||||
cifsConvertToUTF16((__le16 *) tmp, searchName,
|
||||
cifsConvertToUTF16((__le16 *)tmp, searchName,
|
||||
PATH_MAX, nls_codepage, remap);
|
||||
node->path_consumed = cifs_utf16_bytes(tmp,
|
||||
le16_to_cpu(rsp->PathConsumed),
|
||||
nls_codepage);
|
||||
node->path_consumed = cifs_utf16_bytes(tmp, path_consumed,
|
||||
nls_codepage);
|
||||
kfree(tmp);
|
||||
} else
|
||||
node->path_consumed = le16_to_cpu(rsp->PathConsumed);
|
||||
} else {
|
||||
if (path_consumed > search_name_len) {
|
||||
rc = -EINVAL;
|
||||
goto parse_DFS_referrals_exit;
|
||||
}
|
||||
|
||||
node->path_consumed = path_consumed;
|
||||
}
|
||||
|
||||
node->server_type = le16_to_cpu(ref->ServerType);
|
||||
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
|
||||
|
|
|
|||
|
|
@ -952,7 +952,7 @@ static bool cifs_dir_emit(struct dir_context *ctx,
|
|||
static int cifs_filldir(char *find_entry, struct file *file,
|
||||
struct dir_context *ctx,
|
||||
char *scratch_buf, unsigned int max_len,
|
||||
struct cached_fid *cfid)
|
||||
char *end_of_smb, struct cached_fid *cfid)
|
||||
{
|
||||
struct cifsFileInfo *file_info = file->private_data;
|
||||
struct super_block *sb = file_inode(file)->i_sb;
|
||||
|
|
@ -974,6 +974,11 @@ static int cifs_filldir(char *find_entry, struct file *file,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (de.name + de.namelen > end_of_smb) {
|
||||
cifs_dbg(VFS, "search entry name extends past end of SMB\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* skip . and .. since we added them first */
|
||||
if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
|
||||
return 0;
|
||||
|
|
@ -1194,7 +1199,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
|
|||
*/
|
||||
*tmp_buf = 0;
|
||||
rc = cifs_filldir(current_entry, file, ctx,
|
||||
tmp_buf, max_len, cfid);
|
||||
tmp_buf, max_len, end_of_smb, cfid);
|
||||
if (rc) {
|
||||
if (rc > 0)
|
||||
rc = 0;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,19 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
|||
u8 *end = (u8 *)err + iov->iov_len;
|
||||
u32 len;
|
||||
|
||||
/*
|
||||
* Per [MS-SMB2] section 2.2.2, a STATUS_STOPPED_ON_SYMLINK response has to
|
||||
* carry a Symbolic Link Error Response, so ByteCount cannot be zero. Some
|
||||
* servers (e.g. the macOS built-in SMB server) violate this and return an
|
||||
* empty error response, with both ErrorContextCount and ByteCount set to
|
||||
* zero, i.e. without the symlink target. Detect this and return -ENODATA
|
||||
* so that callers can tell "server did not send the target" apart from a
|
||||
* malformed response, and retrieve the target with FSCTL_GET_REPARSE_POINT
|
||||
* instead.
|
||||
*/
|
||||
if (!err->ErrorContextCount && !le32_to_cpu(err->ByteCount))
|
||||
return ERR_PTR(-ENODATA);
|
||||
|
||||
if (err->ErrorContextCount) {
|
||||
struct smb2_error_context_rsp *p;
|
||||
|
||||
|
|
@ -199,6 +212,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
|
|||
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
|
||||
oparms->path,
|
||||
&data->symlink_target);
|
||||
/*
|
||||
* If smb2_parse_symlink_response returned -ENODATA then the
|
||||
* symlink_target was not sent. Treat this as if the SMB2_open()
|
||||
* failed with STATUS_IO_REPARSE_TAG_NOT_HANDLED status, which is
|
||||
* indicated by the -EIO errno.
|
||||
*/
|
||||
if (rc == -ENODATA)
|
||||
rc = -EIO;
|
||||
if (!rc) {
|
||||
memset(&data->fi, 0, sizeof(data->fi));
|
||||
oparms->create_options |= OPEN_REPARSE_POINT;
|
||||
|
|
|
|||
|
|
@ -792,9 +792,19 @@ static int parse_create_response(struct cifs_open_info_data *data,
|
|||
rc = smb2_parse_symlink_response(cifs_sb, iov,
|
||||
full_path,
|
||||
&data->symlink_target);
|
||||
if (rc)
|
||||
if (rc != 0 && rc != -ENODATA)
|
||||
return rc;
|
||||
tag = IO_REPARSE_TAG_SYMLINK;
|
||||
/*
|
||||
* -ENODATA means that the response was parsed but did not contain
|
||||
* the symlink target at all (see symlink_data()). Treat it like
|
||||
* STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not contain it
|
||||
* either: leave the tag unset and clear rc, so that the caller
|
||||
* retrieves the target with SMB2_OP_GET_REPARSE.
|
||||
*/
|
||||
if (rc == -ENODATA)
|
||||
rc = 0;
|
||||
else
|
||||
tag = IO_REPARSE_TAG_SYMLINK;
|
||||
reparse_point = true;
|
||||
break;
|
||||
case STATUS_SUCCESS:
|
||||
|
|
@ -987,7 +997,14 @@ int smb2_query_path_info(const unsigned int xid,
|
|||
rc = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
|
||||
/*
|
||||
* If the symlink was already parsed in create response then it is needed to fix
|
||||
* its type now (after the second call with OPEN_REPARSE_POINT which filled the
|
||||
* data->fi.Attributes). If the symlink was not parsed in create response then
|
||||
* the data->symlink_target was not filled yet and then the type will be fixed
|
||||
* later after data->symlink_target is filled.
|
||||
*/
|
||||
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) {
|
||||
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
|
||||
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user