diff options
Diffstat (limited to 'fs/cifs/smb2ops.c')
-rw-r--r-- | fs/cifs/smb2ops.c | 134 |
1 files changed, 111 insertions, 23 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index a930c8965e5c..e921e6511728 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SMB2 version specific operations * @@ -282,7 +283,7 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf) __u64 wire_mid = le64_to_cpu(shdr->MessageId); if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { - cifs_dbg(VFS, "encrypted frame parsing not supported yet"); + cifs_dbg(VFS, "Encrypted frame parsing not supported yet\n"); return NULL; } @@ -324,6 +325,7 @@ static int smb2_negotiate(const unsigned int xid, struct cifs_ses *ses) { int rc; + ses->server->CurrentMid = 0; rc = SMB2_negotiate(xid, ses); /* BB we probably don't need to retry with modern servers */ @@ -789,8 +791,6 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); else close_shroot(&tcon->crfid); - - return; } static void @@ -818,7 +818,6 @@ smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, FS_DEVICE_INFORMATION); SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - return; } static int @@ -906,9 +905,8 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size, value = &src->ea_data[src->ea_name_length + 1]; value_len = (size_t)le16_to_cpu(src->ea_value_length); - if (name_len == 0) { + if (name_len == 0) break; - } if (src_size < 8 + name_len + 1 + value_len) { cifs_dbg(FYI, "EA entry goes beyond length of list\n"); @@ -1161,6 +1159,7 @@ static void smb2_clear_stats(struct cifs_tcon *tcon) { int i; + for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); @@ -1529,7 +1528,7 @@ smb2_copychunk_range(const unsigned int xid, if (pcchunk == NULL) return -ENOMEM; - cifs_dbg(FYI, "in smb2_copychunk_range - about to call request res key\n"); + cifs_dbg(FYI, "%s: about to call request res key\n", __func__); /* Request a key from the server to identify the source of the copy */ rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), srcfile->fid.persistent_fid, @@ -1649,6 +1648,7 @@ static unsigned int smb2_read_data_offset(char *buf) { struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; + return rsp->DataOffset; } @@ -1777,7 +1777,7 @@ smb2_duplicate_extents(const unsigned int xid, dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off); dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off); dup_ext_buf.ByteCount = cpu_to_le64(len); - cifs_dbg(FYI, "duplicate extents: src off %lld dst off %lld len %lld", + cifs_dbg(FYI, "Duplicate extents: src off %lld dst off %lld len %lld\n", src_off, dest_off, len); rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); @@ -1794,7 +1794,7 @@ smb2_duplicate_extents(const unsigned int xid, &ret_data_len); if (ret_data_len > 0) - cifs_dbg(FYI, "non-zero response length in duplicate extents"); + cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); duplicate_extents_out: return rc; @@ -1983,9 +1983,9 @@ smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, } /* -* If we negotiate SMB2 protocol and get STATUS_PENDING - update -* the number of credits and return true. Otherwise - return false. -*/ + * If we negotiate SMB2 protocol and get STATUS_PENDING - update + * the number of credits and return true. Otherwise - return false. + */ static bool smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) { @@ -2306,7 +2306,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, struct get_dfs_referral_rsp *dfs_rsp = NULL; u32 dfs_req_size = 0, dfs_rsp_size = 0; - cifs_dbg(FYI, "smb2_get_dfs_refer path <%s>\n", search_name); + cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); /* * Try to use the IPC tcon, otherwise just use any @@ -2360,7 +2360,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, if (rc) { if ((rc != -ENOENT) && (rc != -EOPNOTSUPP)) - cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc); + cifs_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc); goto out; } @@ -2369,7 +2369,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, nls_codepage, remap, search_name, true /* is_unicode */); if (rc) { - cifs_dbg(VFS, "parse error in smb2_get_dfs_refer rc=%d\n", rc); + cifs_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); goto out; } @@ -2745,7 +2745,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, inode = d_inode(cfile->dentry); cifsi = CIFS_I(inode); - trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, + trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, ses->Suid, offset, len); @@ -2759,7 +2759,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, return rc; } - cifs_dbg(FYI, "offset %lld len %lld", offset, len); + cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); fsctl_buf.FileOffset = cpu_to_le64(offset); fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); @@ -2816,7 +2816,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, return rc; } - cifs_dbg(FYI, "offset %lld len %lld", offset, len); + cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); fsctl_buf.FileOffset = cpu_to_le64(offset); fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); @@ -2922,6 +2922,90 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, return rc; } +static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence) +{ + struct cifsFileInfo *wrcfile, *cfile = file->private_data; + struct cifsInodeInfo *cifsi; + struct inode *inode; + int rc = 0; + struct file_allocated_range_buffer in_data, *out_data = NULL; + u32 out_data_len; + unsigned int xid; + + if (whence != SEEK_HOLE && whence != SEEK_DATA) + return generic_file_llseek(file, offset, whence); + + inode = d_inode(cfile->dentry); + cifsi = CIFS_I(inode); + + if (offset < 0 || offset >= i_size_read(inode)) + return -ENXIO; + + xid = get_xid(); + /* + * We need to be sure that all dirty pages are written as they + * might fill holes on the server. + * Note that we also MUST flush any written pages since at least + * some servers (Windows2016) will not reflect recent writes in + * QUERY_ALLOCATED_RANGES until SMB2_flush is called. + */ + wrcfile = find_writable_file(cifsi, false); + if (wrcfile) { + filemap_write_and_wait(inode->i_mapping); + smb2_flush_file(xid, tcon, &wrcfile->fid); + cifsFileInfo_put(wrcfile); + } + + if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) { + if (whence == SEEK_HOLE) + offset = i_size_read(inode); + goto lseek_exit; + } + + in_data.file_offset = cpu_to_le64(offset); + in_data.length = cpu_to_le64(i_size_read(inode)); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_QUERY_ALLOCATED_RANGES, true, + (char *)&in_data, sizeof(in_data), + sizeof(struct file_allocated_range_buffer), + (char **)&out_data, &out_data_len); + if (rc == -E2BIG) + rc = 0; + if (rc) + goto lseek_exit; + + if (whence == SEEK_HOLE && out_data_len == 0) + goto lseek_exit; + + if (whence == SEEK_DATA && out_data_len == 0) { + rc = -ENXIO; + goto lseek_exit; + } + + if (out_data_len < sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto lseek_exit; + } + if (whence == SEEK_DATA) { + offset = le64_to_cpu(out_data->file_offset); + goto lseek_exit; + } + if (offset < le64_to_cpu(out_data->file_offset)) + goto lseek_exit; + + offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length); + + lseek_exit: + free_xid(xid); + kfree(out_data); + if (!rc) + return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); + else + return rc; +} + static int smb3_fiemap(struct cifs_tcon *tcon, struct cifsFileInfo *cfile, struct fiemap_extent_info *fei, u64 start, u64 len) @@ -3384,7 +3468,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst, req = aead_request_alloc(tfm, GFP_KERNEL); if (!req) { - cifs_dbg(VFS, "%s: Failed to alloc aead request", __func__); + cifs_dbg(VFS, "%s: Failed to alloc aead request\n", __func__); return -ENOMEM; } @@ -3395,7 +3479,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst, sg = init_sg(num_rqst, rqst, sign); if (!sg) { - cifs_dbg(VFS, "%s: Failed to init sg", __func__); + cifs_dbg(VFS, "%s: Failed to init sg\n", __func__); rc = -ENOMEM; goto free_req; } @@ -3403,7 +3487,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst, iv_len = crypto_aead_ivsize(tfm); iv = kzalloc(iv_len, GFP_KERNEL); if (!iv) { - cifs_dbg(VFS, "%s: Failed to alloc IV", __func__); + cifs_dbg(VFS, "%s: Failed to alloc iv\n", __func__); rc = -ENOMEM; goto free_sg; } @@ -3511,7 +3595,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, fill_transform_hdr(tr_hdr, orig_len, old_rq); rc = crypt_message(server, num_rqst, new_rq, 1); - cifs_dbg(FYI, "encrypt message returned %d", rc); + cifs_dbg(FYI, "Encrypt message returned %d\n", rc); if (rc) goto err_free; @@ -3552,7 +3636,7 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf, rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE; rc = crypt_message(server, 1, &rqst, 0); - cifs_dbg(FYI, "decrypt message returned %d\n", rc); + cifs_dbg(FYI, "Decrypt message returned %d\n", rc); if (rc) return rc; @@ -4166,6 +4250,7 @@ struct smb_version_operations smb20_operations = { .ioctl_query_info = smb2_ioctl_query_info, .make_node = smb2_make_node, .fiemap = smb3_fiemap, + .llseek = smb3_llseek, }; struct smb_version_operations smb21_operations = { @@ -4266,6 +4351,7 @@ struct smb_version_operations smb21_operations = { .ioctl_query_info = smb2_ioctl_query_info, .make_node = smb2_make_node, .fiemap = smb3_fiemap, + .llseek = smb3_llseek, }; struct smb_version_operations smb30_operations = { @@ -4375,6 +4461,7 @@ struct smb_version_operations smb30_operations = { .ioctl_query_info = smb2_ioctl_query_info, .make_node = smb2_make_node, .fiemap = smb3_fiemap, + .llseek = smb3_llseek, }; struct smb_version_operations smb311_operations = { @@ -4485,6 +4572,7 @@ struct smb_version_operations smb311_operations = { .ioctl_query_info = smb2_ioctl_query_info, .make_node = smb2_make_node, .fiemap = smb3_fiemap, + .llseek = smb3_llseek, }; struct smb_version_values smb20_values = { |