mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:19:30 +02:00
Merge tag 'cifs-fixes-7.3-rc2' of https://git.manguebit.org/linux
Pull smb client fixes from Paulo Alcantara: - Fixes for fallocate range operations (insert, collapse, zero, punch hole) The insert range implementation copied overlapping chunks in the wrong direction, corrupting file data on every server except Windows. Several related issues in the same area are also addressed — stale page cache and FS-Cache readback, an integer truncation on large files, missing RLIMIT_FSIZE validation and missing sparse file marking. - Data corruption fixes in the O_TRUNC open path: one where i_size was zeroed before the server confirmed the truncate and another where the lack of locking allowed concurrent buffered writes to be silently discarded - Heap overflow fixes in legacy SMB1 paths: one in extended attribute writes and one in POSIX ACL handling, both exploitable via unprivileged setxattr(2) - Fix for multiuser mount with krb5 failing because the username option was not propagated to new per-user connections - Fix for split debug message in __release_mid() after a printk conversion * tag 'cifs-fixes-7.3-rc2' of https://git.manguebit.org/linux: smb: client: reject SetEA requests that do not fit the request buffer smb: client: fix data corruption with concurrent writes and O_TRUNC cifs: don't update i_size in cifs_do_truncate without a cached handle smb: client: fix heap overflow in cifs_do_set_acl() smb: client: fix multiuser mount with krb5 smb: client: transport: Fix debug printing in __release_mid() smb/client: invalidate fscache for fallocate range operations smb/client: fix stale page cache in insert/collapse range smb/client: fix integer truncation in collapse range smb/client: fix data corruption in emulated insert range smb/client: mark file sparse before emulating insert range smb/client: validate new EOF for zero range smb/client: validate new EOF for insert range cifs: add revalidation on FSCTL failure in smb2_duplicate_extents()
This commit is contained in:
+21
-3
@@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
int rc = 0;
|
||||
int bytes_returned = 0;
|
||||
__u16 params, byte_count, data_count, param_offset, offset;
|
||||
size_t cifs_acl_size, bytes_available;
|
||||
|
||||
cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
|
||||
setAclRetry:
|
||||
@@ -3574,8 +3575,7 @@ setAclRetry:
|
||||
}
|
||||
params = 6 + name_len;
|
||||
pSMB->MaxParameterCount = cpu_to_le16(2);
|
||||
/* BB find max SMB size from sess */
|
||||
pSMB->MaxDataCount = cpu_to_le16(1000);
|
||||
pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
|
||||
pSMB->MaxSetupCount = 0;
|
||||
pSMB->Reserved = 0;
|
||||
pSMB->Flags = 0;
|
||||
@@ -3587,6 +3587,15 @@ setAclRetry:
|
||||
parm_data = ((char *)pSMB) + offset;
|
||||
pSMB->ParameterOffset = cpu_to_le16(param_offset);
|
||||
|
||||
/* make sure we can fit the larger cifs_posix_aces in the buffer */
|
||||
cifs_acl_size = sizeof(struct cifs_posix_acl) +
|
||||
(acl->a_count * sizeof(struct cifs_posix_ace));
|
||||
bytes_available = (CIFSMaxBufSize + MAX_HEADER_SIZE(tcon->ses->server)) - offset;
|
||||
if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) {
|
||||
rc = -E2BIG;
|
||||
goto setACLerrorExit;
|
||||
}
|
||||
|
||||
/* convert to on the wire format for POSIX ACL */
|
||||
data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
|
||||
|
||||
@@ -6325,8 +6334,10 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
int name_len;
|
||||
int rc = 0;
|
||||
int bytes_returned = 0;
|
||||
__u16 params, param_offset, byte_count, offset, count;
|
||||
__u16 params, param_offset;
|
||||
unsigned int byte_count, offset, count;
|
||||
int remap = cifs_remap(cifs_sb);
|
||||
unsigned int total_len;
|
||||
|
||||
cifs_dbg(FYI, "In SetEA\n");
|
||||
SetEARetry:
|
||||
@@ -6378,6 +6389,13 @@ SetEARetry:
|
||||
pSMB->Reserved3 = 0;
|
||||
pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
|
||||
byte_count = 3 /* pad */ + params + count;
|
||||
if (check_add_overflow(in_len, byte_count, &total_len) ||
|
||||
byte_count > U16_MAX ||
|
||||
total_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
|
||||
cifs_dbg(VFS, "EA request too large: %u bytes\n", total_len);
|
||||
cifs_buf_release(pSMB);
|
||||
return -E2BIG;
|
||||
}
|
||||
pSMB->DataCount = cpu_to_le16(count);
|
||||
parm_data->list_len = cpu_to_le32(count);
|
||||
parm_data->list.EA_flags = 0;
|
||||
|
||||
+16
-5
@@ -4189,14 +4189,25 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
|
||||
static int set_fs_context_auth(struct smb3_fs_context *ctx,
|
||||
struct cifs_ses *ses)
|
||||
{
|
||||
ctx->sectype = ses->sectype;
|
||||
|
||||
/* krb5 is special, since we don't need username or pw */
|
||||
if (ctx->sectype == Kerberos)
|
||||
/*
|
||||
* krb5 is special as we might need to pass username (passwordless) down
|
||||
* to cifs.upcall(8) for keytab.
|
||||
*/
|
||||
if (ctx->sectype == Kerberos) {
|
||||
if (ses->user_name && ses->user_name[0]) {
|
||||
ctx->username = kstrndup(ses->user_name,
|
||||
CIFS_MAX_USERNAME_LEN,
|
||||
GFP_KERNEL);
|
||||
if (!ctx->username)
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cifs_set_cifscreds(ctx, ses);
|
||||
}
|
||||
@@ -4236,7 +4247,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
|
||||
ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses;
|
||||
ctx->unicode = master_tcon->ses->unicode;
|
||||
|
||||
rc = cifs_set_vol_auth(ctx, master_tcon->ses);
|
||||
rc = set_fs_context_auth(ctx, master_tcon->ses);
|
||||
if (rc) {
|
||||
tcon = ERR_PTR(rc);
|
||||
goto out;
|
||||
|
||||
+30
-6
@@ -999,26 +999,50 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
|
||||
struct cifs_tcon *tcon;
|
||||
int rc;
|
||||
|
||||
rc = filemap_write_and_wait(inode->i_mapping);
|
||||
if (is_interrupt_error(rc))
|
||||
rc = inode_lock_killable(inode);
|
||||
if (rc)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
|
||||
rc = filemap_write_and_wait(inode->i_mapping);
|
||||
if (is_interrupt_error(rc)) {
|
||||
rc = -ERESTARTSYS;
|
||||
goto out;
|
||||
}
|
||||
mapping_set_error(inode->i_mapping, rc);
|
||||
|
||||
cfile = find_writable_file(cinode, FIND_FSUID_ONLY);
|
||||
rc = cifs_file_flush(xid, inode, cfile);
|
||||
if (!rc) {
|
||||
if (cfile) {
|
||||
struct netfs_inode *ictx = netfs_inode(inode);
|
||||
|
||||
tcon = tlink_tcon(cfile->tlink);
|
||||
server = tcon->ses->server;
|
||||
netfs_wb_begin(ictx, false);
|
||||
rc = server->ops->set_file_size(xid, tcon,
|
||||
cfile, 0, false);
|
||||
}
|
||||
if (!rc) {
|
||||
netfs_resize_file(&cinode->netfs, 0, true);
|
||||
cifs_setsize(inode, 0);
|
||||
if (!rc) {
|
||||
netfs_resize_file(&cinode->netfs, 0, true);
|
||||
cifs_setsize(inode, 0);
|
||||
cifs_invalidate_cache(inode, 0);
|
||||
}
|
||||
netfs_wb_end(ictx);
|
||||
} else {
|
||||
/*
|
||||
* No cached handle; evict stale pages so they can't
|
||||
* be served after the file is later extended; let
|
||||
* the server's O_TRUNC open response set the i_size
|
||||
*/
|
||||
truncate_inode_pages(inode->i_mapping, 0);
|
||||
cifs_invalidate_cache(inode, 0);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
filemap_invalidate_unlock(inode->i_mapping);
|
||||
inode_unlock(inode);
|
||||
if (cfile)
|
||||
cifsFileInfo_put(cfile);
|
||||
return rc;
|
||||
|
||||
+159
-36
@@ -1839,31 +1839,31 @@ free_vars:
|
||||
*
|
||||
* @tcon: destination file tcon
|
||||
* @bytes_left: how many bytes are left to copy
|
||||
* @chunk_size: maximum size of a single chunk
|
||||
*
|
||||
* Return: maximum number of chunks with which Chunks[] can be filled.
|
||||
*/
|
||||
static inline u32
|
||||
calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
|
||||
calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left, u32 chunk_size)
|
||||
{
|
||||
u32 max_chunks = READ_ONCE(tcon->max_chunks);
|
||||
u32 max_bytes_copy = READ_ONCE(tcon->max_bytes_copy);
|
||||
u32 max_bytes_chunk = READ_ONCE(tcon->max_bytes_chunk);
|
||||
u64 need;
|
||||
u32 allowed;
|
||||
|
||||
if (!max_bytes_chunk || !max_bytes_copy || !max_chunks)
|
||||
if (!chunk_size || !max_bytes_copy || !max_chunks)
|
||||
return 0;
|
||||
|
||||
/* chunks needed for the remaining bytes */
|
||||
need = DIV_ROUND_UP_ULL(bytes_left, max_bytes_chunk);
|
||||
need = DIV_ROUND_UP_ULL(bytes_left, chunk_size);
|
||||
/* chunks allowed per cc request */
|
||||
allowed = DIV_ROUND_UP(max_bytes_copy, max_bytes_chunk);
|
||||
allowed = DIV_ROUND_UP(max_bytes_copy, chunk_size);
|
||||
|
||||
return (u32)umin(need, umin(max_chunks, allowed));
|
||||
}
|
||||
|
||||
/**
|
||||
* smb2_copychunk_range - server-side copy of data range
|
||||
* __smb2_copychunk_range - server-side copy of data range
|
||||
*
|
||||
* @xid: transaction id
|
||||
* @src_file: source file
|
||||
@@ -1875,15 +1875,15 @@ calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
|
||||
* Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE
|
||||
* IOCTLs, splitting the request into chunks limited by tcon->max_*.
|
||||
*
|
||||
* Return: @len on success; negative errno on failure.
|
||||
* Return: 0 on success; negative errno on failure.
|
||||
*/
|
||||
static ssize_t
|
||||
smb2_copychunk_range(const unsigned int xid,
|
||||
struct cifsFileInfo *src_file,
|
||||
struct cifsFileInfo *dst_file,
|
||||
u64 src_off,
|
||||
u64 len,
|
||||
u64 dst_off)
|
||||
static int
|
||||
__smb2_copychunk_range(const unsigned int xid,
|
||||
struct cifsFileInfo *src_file,
|
||||
struct cifsFileInfo *dst_file,
|
||||
u64 src_off,
|
||||
u64 len,
|
||||
u64 dst_off)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned int ret_data_len = 0;
|
||||
@@ -1891,12 +1891,14 @@ smb2_copychunk_range(const unsigned int xid,
|
||||
struct copychunk_ioctl_rsp *cc_rsp = NULL;
|
||||
struct cifs_tcon *tcon;
|
||||
struct srv_copychunk *chunk;
|
||||
u32 chunks, chunk_count, chunk_bytes;
|
||||
u32 chunks, chunk_count, chunk_bytes, chunk_size;
|
||||
u32 copy_bytes, copy_bytes_left;
|
||||
u32 chunks_written, bytes_written;
|
||||
u64 total_bytes_left = len;
|
||||
u64 src_off_prev, dst_off_prev;
|
||||
u64 max_chunk = 0;
|
||||
u32 retries = 0;
|
||||
bool reverse = false;
|
||||
|
||||
tcon = tlink_tcon(dst_file->tlink);
|
||||
|
||||
@@ -1904,8 +1906,50 @@ smb2_copychunk_range(const unsigned int xid,
|
||||
dst_file->fid.volatile_fid, tcon->tid,
|
||||
tcon->ses->Suid, src_off, dst_off, len);
|
||||
|
||||
/*
|
||||
* Same-file left shifts are safe in forward order. For a right shift,
|
||||
* let L be the copy length, delta the distance between the source and
|
||||
* destination, and C the normal chunk size:
|
||||
*
|
||||
* delta >= L: copy forwards using C
|
||||
* delta < L:
|
||||
* delta >= C: copy backwards using C
|
||||
* delta < C: copy backwards with chunks limited to delta
|
||||
*
|
||||
* Copying backwards prevents one chunk from overwriting data needed by
|
||||
* a later chunk. Limiting the chunk size to delta prevents an individual
|
||||
* chunk from overlapping itself.
|
||||
* This limit can be removed once all supported servers handle overlapping
|
||||
* descriptors safely.
|
||||
*
|
||||
* A small right shift over a large range may therefore require many
|
||||
* chunks.
|
||||
*/
|
||||
if (src_file == dst_file && dst_off > src_off) {
|
||||
u64 delta = dst_off - src_off;
|
||||
|
||||
if (delta < len) {
|
||||
reverse = true;
|
||||
max_chunk = delta;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A backward copy walks the offsets down from the end of the range.
|
||||
* Do this once, outside the retry loop, so a retry does not move the
|
||||
* offsets again.
|
||||
*/
|
||||
if (reverse) {
|
||||
src_off += len;
|
||||
dst_off += len;
|
||||
}
|
||||
|
||||
retry:
|
||||
chunk_count = calc_chunk_count(tcon, total_bytes_left);
|
||||
chunk_size = READ_ONCE(tcon->max_bytes_chunk);
|
||||
if (max_chunk && max_chunk < chunk_size)
|
||||
chunk_size = (u32)max_chunk;
|
||||
|
||||
chunk_count = calc_chunk_count(tcon, total_bytes_left, chunk_size);
|
||||
if (!chunk_count) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
@@ -1946,16 +1990,21 @@ retry:
|
||||
while (copy_bytes_left > 0 && chunks < chunk_count) {
|
||||
chunk = &cc_req->Chunks[chunks++];
|
||||
|
||||
chunk_bytes = umin(copy_bytes_left, chunk_size);
|
||||
if (reverse) {
|
||||
src_off -= chunk_bytes;
|
||||
dst_off -= chunk_bytes;
|
||||
}
|
||||
|
||||
chunk->SourceOffset = cpu_to_le64(src_off);
|
||||
chunk->TargetOffset = cpu_to_le64(dst_off);
|
||||
|
||||
chunk_bytes = umin(copy_bytes_left, tcon->max_bytes_chunk);
|
||||
|
||||
chunk->Length = cpu_to_le32(chunk_bytes);
|
||||
/* Buffer is zeroed, no need to set chunk->Reserved = 0 */
|
||||
|
||||
src_off += chunk_bytes;
|
||||
dst_off += chunk_bytes;
|
||||
if (!reverse) {
|
||||
src_off += chunk_bytes;
|
||||
dst_off += chunk_bytes;
|
||||
}
|
||||
|
||||
copy_bytes_left -= chunk_bytes;
|
||||
copy_bytes += chunk_bytes;
|
||||
@@ -2003,6 +2052,18 @@ retry:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* A successful COPYCHUNK should copy every descriptor (MS-SMB2
|
||||
* 3.3.5.15.6). Reject a short backward copy because the rewind
|
||||
* below only supports forward copying.
|
||||
*/
|
||||
if (unlikely(reverse && bytes_written < copy_bytes)) {
|
||||
cifs_tcon_dbg(VFS, "Copychunk short write %u/%u (reverse)\n",
|
||||
bytes_written, copy_bytes);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Partial write: rewind */
|
||||
if (bytes_written < copy_bytes) {
|
||||
u32 delta = copy_bytes - bytes_written;
|
||||
@@ -2064,10 +2125,27 @@ out:
|
||||
trace_smb3_copychunk_done(xid, src_file->fid.volatile_fid,
|
||||
dst_file->fid.volatile_fid, tcon->tid,
|
||||
tcon->ses->Suid, src_off, dst_off, len);
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
smb2_copychunk_range(const unsigned int xid,
|
||||
struct cifsFileInfo *src_file,
|
||||
struct cifsFileInfo *dst_file,
|
||||
u64 src_off,
|
||||
u64 len,
|
||||
u64 dst_off)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = __smb2_copychunk_range(xid, src_file, dst_file, src_off, len,
|
||||
dst_off);
|
||||
if (rc)
|
||||
return rc;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_fid *fid)
|
||||
@@ -2218,7 +2296,7 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||
trgtfile->fid.volatile_fid, tcon->tid,
|
||||
tcon->ses->Suid, src_off, dest_off, len);
|
||||
inode = d_inode(trgtfile->dentry);
|
||||
if (inode->i_size < dest_off + len) {
|
||||
if (i_size_read(inode) < dest_off + len) {
|
||||
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
|
||||
if (rc)
|
||||
goto duplicate_extents_out;
|
||||
@@ -2235,7 +2313,10 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||
if (ret_data_len > 0)
|
||||
cifs_dbg(FYI, "Non-zero response length in duplicate extents\n");
|
||||
|
||||
if (rc == 0) {
|
||||
if (rc) {
|
||||
CIFS_I(inode)->time = 0; /* force reval */
|
||||
cifs_invalidate_cache(inode, 0);
|
||||
} else {
|
||||
qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
|
||||
trgtfile->fid.volatile_fid, &file_inf);
|
||||
spin_lock(&inode->i_lock);
|
||||
@@ -3441,6 +3522,13 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
ses->Suid, offset, len);
|
||||
|
||||
new_size = offset + len;
|
||||
if (!keep_size && i_size_read(inode) < new_size) {
|
||||
rc = inode_newsize_ok(inode, new_size);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
|
||||
netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
|
||||
@@ -3464,6 +3552,9 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
if (keep_size == false && !CIFS_CACHE_READ(cifsi))
|
||||
goto zero_range_exit;
|
||||
|
||||
fscache_invalidate(cifs_inode_cookie(inode), NULL,
|
||||
i_size_read(inode), 0);
|
||||
|
||||
rc = smb3_zero_data(file, tcon, offset, len, xid);
|
||||
if (rc < 0)
|
||||
goto zero_range_exit;
|
||||
@@ -3471,7 +3562,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
/*
|
||||
* do we also need to change the size of the file?
|
||||
*/
|
||||
new_size = offset + len;
|
||||
if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, new_size);
|
||||
@@ -3488,6 +3578,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
|
||||
zero_range_exit:
|
||||
filemap_invalidate_unlock(inode->i_mapping);
|
||||
out:
|
||||
free_xid(xid);
|
||||
if (rc)
|
||||
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
@@ -3533,6 +3624,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
|
||||
*/
|
||||
truncate_pagecache_range(inode, offset, offset + len - 1);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
fscache_invalidate(cifs_inode_cookie(inode), NULL,
|
||||
i_size_read(inode), 0);
|
||||
|
||||
cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
|
||||
|
||||
@@ -3938,18 +4031,26 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
|
||||
}
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping,
|
||||
round_down(off, PAGE_SIZE),
|
||||
old_eof - 1);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
|
||||
truncate_pagecache_range(inode, off, old_eof);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
/*
|
||||
* Invalidate cached folios from the page containing off to EOF before
|
||||
* moving data on the server, so subsequent reads do not see stale data.
|
||||
*/
|
||||
truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
|
||||
fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
netfs_write_zero_point(inode, old_eof);
|
||||
spin_unlock(&inode->i_lock);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
|
||||
rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
|
||||
old_eof - off - len, off);
|
||||
rc = __smb2_copychunk_range(xid, cfile, cfile, off + len,
|
||||
old_eof - off - len, off);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
|
||||
@@ -3982,7 +4083,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||
__u64 count, old_eof, new_eof;
|
||||
loff_t old_eof, new_eof;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
@@ -3992,15 +4093,32 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
|
||||
goto out;
|
||||
}
|
||||
|
||||
count = old_eof - off;
|
||||
new_eof = old_eof + len;
|
||||
if (check_add_overflow(old_eof, len, &new_eof)) {
|
||||
rc = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
rc = inode_newsize_ok(inode, new_eof);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* SET_ZERO_DATA creates a hole only in a sparse file. */
|
||||
rc = smb2_set_sparse(xid, tcon, cfile, inode, true);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping,
|
||||
round_down(off, PAGE_SIZE),
|
||||
old_eof - 1);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
truncate_pagecache_range(inode, off, old_eof);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
/*
|
||||
* Invalidate cached folios from the page containing off to EOF before
|
||||
* moving data on the server, so subsequent reads do not see stale data.
|
||||
*/
|
||||
truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
|
||||
fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
|
||||
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, new_eof);
|
||||
@@ -4013,7 +4131,12 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
|
||||
spin_unlock(&inode->i_lock);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode));
|
||||
|
||||
rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
|
||||
/*
|
||||
* Move [off, old_eof) right by len. The helper copies backwards if the
|
||||
* source and destination ranges overlap.
|
||||
*/
|
||||
rc = __smb2_copychunk_range(xid, cfile, cfile, off, old_eof - off,
|
||||
off + len);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
spin_lock(&inode->i_lock);
|
||||
|
||||
@@ -101,12 +101,11 @@ void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *midEntry)
|
||||
trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid,
|
||||
midEntry->when_sent, midEntry->when_received);
|
||||
if (cifsFYI & CIFS_TIMER) {
|
||||
pr_debug("slow rsp: cmd %d mid %llu",
|
||||
midEntry->command, midEntry->mid);
|
||||
cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n",
|
||||
now - midEntry->when_alloc,
|
||||
now - midEntry->when_sent,
|
||||
now - midEntry->when_received);
|
||||
pr_debug("slow rsp: cmd %d mid %llu A: 0x%lx S: 0x%lx R: 0x%lx\n",
|
||||
midEntry->command, midEntry->mid,
|
||||
now - midEntry->when_alloc,
|
||||
now - midEntry->when_sent,
|
||||
now - midEntry->when_received);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user