diff options
Diffstat (limited to 'fs/smb/client/misc.c')
| -rw-r--r-- | fs/smb/client/misc.c | 120 |
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, |
