summaryrefslogtreecommitdiff
path: root/fs/smb/client/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb/client/misc.c')
-rw-r--r--fs/smb/client/misc.c120
1 files changed, 101 insertions, 19 deletions
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index 0c54b9b79a2c..6edebc0807ea 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -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");
@@ -493,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);
@@ -510,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);
}
@@ -519,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);
}
@@ -529,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);
@@ -543,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);
}
@@ -552,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);
}
@@ -600,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);
@@ -613,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);
}
@@ -678,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,
@@ -724,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++) {
@@ -732,26 +793,43 @@ 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);
/* 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 +840,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,