* [PATCH 1/7] smb/client: name the default fallocate mode
2026-06-23 2:46 [PATCH 0/7] smb/client: fix fallocate allocation handling Huiwen He
@ 2026-06-23 2:46 ` Huiwen He
2026-06-23 3:50 ` Steve French
2026-06-23 2:46 ` [PATCH 2/7] smb/client: handle smb2_set_sparse() failure in EOF-extending fallocate Huiwen He
` (5 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Huiwen He @ 2026-06-23 2:46 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
FALLOC_FL_ALLOCATE_RANGE identifies the default fallocate
allocation mode and is defined as zero.
Use the symbolic name instead of a literal zero in
smb3_fallocate() to make the mode dispatch clearer. This
does not change behavior.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index a8f8feeeccb5..2964f461fc84 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -4071,7 +4071,7 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
return smb3_collapse_range(file, tcon, off, len);
else if (mode == FALLOC_FL_INSERT_RANGE)
return smb3_insert_range(file, tcon, off, len);
- else if (mode == 0)
+ else if (mode == FALLOC_FL_ALLOCATE_RANGE)
return smb3_simple_falloc(file, tcon, off, len, false);
return -EOPNOTSUPP;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH 1/7] smb/client: name the default fallocate mode
2026-06-23 2:46 ` [PATCH 1/7] smb/client: name the default fallocate mode Huiwen He
@ 2026-06-23 3:50 ` Steve French
0 siblings, 0 replies; 9+ messages in thread
From: Steve French @ 2026-06-23 3:50 UTC (permalink / raw)
To: Huiwen He
Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong, linux-cifs
merged patch 1 into cifs-2.6.git for-next
Holding off on the others until you get a chance to review Sashiko AI
review of the other patches
On Mon, Jun 22, 2026 at 9:48 PM Huiwen He <huiwen.he@linux.dev> wrote:
>
> From: Huiwen He <hehuiwen@kylinos.cn>
>
> FALLOC_FL_ALLOCATE_RANGE identifies the default fallocate
> allocation mode and is defined as zero.
>
> Use the symbolic name instead of a literal zero in
> smb3_fallocate() to make the mode dispatch clearer. This
> does not change behavior.
>
> Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
> ---
> fs/smb/client/smb2ops.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
> index a8f8feeeccb5..2964f461fc84 100644
> --- a/fs/smb/client/smb2ops.c
> +++ b/fs/smb/client/smb2ops.c
> @@ -4071,7 +4071,7 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
> return smb3_collapse_range(file, tcon, off, len);
> else if (mode == FALLOC_FL_INSERT_RANGE)
> return smb3_insert_range(file, tcon, off, len);
> - else if (mode == 0)
> + else if (mode == FALLOC_FL_ALLOCATE_RANGE)
> return smb3_simple_falloc(file, tcon, off, len, false);
>
> return -EOPNOTSUPP;
> --
> 2.43.0
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/7] smb/client: handle smb2_set_sparse() failure in EOF-extending fallocate
2026-06-23 2:46 [PATCH 0/7] smb/client: fix fallocate allocation handling Huiwen He
2026-06-23 2:46 ` [PATCH 1/7] smb/client: name the default fallocate mode Huiwen He
@ 2026-06-23 2:46 ` Huiwen He
2026-06-23 2:46 ` [PATCH 3/7] smb/client: handle smb2_set_sparse() failure in non-extending fallocate Huiwen He
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Huiwen He @ 2026-06-23 2:46 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
An EOF-extending fallocate(mode=0) can return success while the newly
extended range remains sparse. A later write into that range may
therefore still fail with ENOSPC despite fallocate having succeeded.
CIFS clears the sparse attribute before extending EOF so that the
requested range is allocated. However, it ignores failure from
smb2_set_sparse() and continues to SetEOF.
Return -EOPNOTSUPP without extending EOF when the sparse attribute cannot
be cleared. This prevents CIFS from reporting successful preallocation
when the server rejected FSCTL_SET_SPARSE.
Fixes: 8bd0d701445e ("cifs: add support for fallocate mode 0 for non-sparse files")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 2964f461fc84..554c1f51871d 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3691,8 +3691,11 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
if (rc)
goto out;
- if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
- smb2_set_sparse(xid, tcon, cfile, inode, false);
+ if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) &&
+ !smb2_set_sparse(xid, tcon, cfile, inode, false)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
new_eof = off + len;
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/7] smb/client: handle smb2_set_sparse() failure in non-extending fallocate
2026-06-23 2:46 [PATCH 0/7] smb/client: fix fallocate allocation handling Huiwen He
2026-06-23 2:46 ` [PATCH 1/7] smb/client: name the default fallocate mode Huiwen He
2026-06-23 2:46 ` [PATCH 2/7] smb/client: handle smb2_set_sparse() failure in EOF-extending fallocate Huiwen He
@ 2026-06-23 2:46 ` Huiwen He
2026-06-23 2:46 ` [PATCH 4/7] smb/client: do not account EOF extension as allocation Huiwen He
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Huiwen He @ 2026-06-23 2:46 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
A non-extending fallocate on a sparse file can return success while the
requested range still contains holes. This affects FALLOC_FL_ALLOCATE_RANGE
within EOF and FALLOC_FL_KEEP_SIZE when CIFS clears the sparse attribute.
Later writes into the range may still fail.
CIFS emulates this operation by clearing the sparse attribute for the
whole file. However, it ignores failure from smb2_set_sparse() and sets
the return value to zero unconditionally.
Return -EOPNOTSUPP when the sparse attribute cannot be cleared. This
prevents CIFS from reporting successful preallocation when the server
rejected FSCTL_SET_SPARSE.
Fixes: f16994797ea8 ("cifs: fix incorrect handling of smb2_set_sparse() return in smb3_simple_falloc")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 554c1f51871d..61b581d86686 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3770,8 +3770,10 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
}
}
- smb2_set_sparse(xid, tcon, cfile, inode, false);
- rc = 0;
+ if (!smb2_set_sparse(xid, tcon, cfile, inode, false))
+ rc = -EOPNOTSUPP;
+ else
+ rc = 0;
out:
if (rc)
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/7] smb/client: do not account EOF extension as allocation
2026-06-23 2:46 [PATCH 0/7] smb/client: fix fallocate allocation handling Huiwen He
` (2 preceding siblings ...)
2026-06-23 2:46 ` [PATCH 3/7] smb/client: handle smb2_set_sparse() failure in non-extending fallocate Huiwen He
@ 2026-06-23 2:46 ` Huiwen He
2026-06-23 2:46 ` [PATCH 5/7] smb/client: verify allocation after EOF-extending fallocate Huiwen He
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Huiwen He @ 2026-06-23 2:46 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
cifs_setsize() updates the local inode size after SetEOF succeeds. It also
used the new EOF as a local i_blocks estimate, but extending EOF does not
prove that the intervening range was allocated.
For example, after writing 1 MiB and then extending EOF to 10 MiB, the
client can report the file as fully allocated even though the server still
reports a much smaller AllocationSize:
$ dd if=/dev/zero of=test bs=1M count=1
$ truncate -s 10M test && stat -c 'size=%s blocks=%b' test
$ stat --cached=never -c 'size=%s blocks=%b' test
client stat: size=10485760 blocks=20480
server stat: size=10485760 blocks=2056
client stat after revalidation: size=10485760 blocks=2056
A later attribute revalidation may correct i_blocks, but callers such as
xfstests generic/495 invoke swapon immediately after truncate. The swapfile
hole check can therefore observe the inflated local i_blocks value and
accept a sparse file.
Do not grow i_blocks from cifs_setsize() on EOF extension. Only clamp it
on shrink; allocation growth must come from write completion or from
server-reported AllocationSize.
With this change, EOF extension no longer makes a sparse file appear
fully allocated before the next attribute revalidation, and xfstests
generic/495 no longer accepts it through the inflated local i_blocks value.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/inode.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 0af93e881608..84d95d2c3ed9 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -3038,13 +3038,20 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
void cifs_setsize(struct inode *inode, loff_t offset)
{
+ loff_t old_size;
+ u64 blocks = CIFS_INO_BLOCKS(offset);
+
spin_lock(&inode->i_lock);
+ old_size = i_size_read(inode);
i_size_write(inode, offset);
+
/*
- * Until we can query the server for actual allocation size,
- * this is best estimate we have for blocks allocated for a file.
+ * Extending EOF does not allocate the intervening range. Only clamp
+ * i_blocks on shrink; allocation growth comes from writes or from the
+ * server-reported AllocationSize.
*/
- inode->i_blocks = CIFS_INO_BLOCKS(offset);
+ if (offset < old_size && (u64)inode->i_blocks > blocks)
+ inode->i_blocks = blocks;
spin_unlock(&inode->i_lock);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
truncate_pagecache(inode, offset);
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 5/7] smb/client: verify allocation after EOF-extending fallocate
2026-06-23 2:46 [PATCH 0/7] smb/client: fix fallocate allocation handling Huiwen He
` (3 preceding siblings ...)
2026-06-23 2:46 ` [PATCH 4/7] smb/client: do not account EOF extension as allocation Huiwen He
@ 2026-06-23 2:46 ` Huiwen He
2026-06-23 2:46 ` [PATCH 6/7] smb/client: reduce fallocate zero buffer allocation Huiwen He
2026-06-23 2:46 ` [PATCH 7/7] smb/client: emulate small fallocate ranges at EOF Huiwen He
6 siblings, 0 replies; 9+ messages in thread
From: Huiwen He @ 2026-06-23 2:46 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
EOF-extending fallocate(mode=0) currently returns success after SetEOF
even if the server has not allocated the requested space. This gives
userspace a false preallocation guarantee and can cause a later write
into that range to fail with ENOSPC.
This happens because SMB2 has no operation that directly matches Linux
fallocate for an arbitrary byte range. SetEOF changes only the logical
file size, while FILE_ALLOCATION_INFORMATION can request allocation only
from offset 0 up to a specified allocation size.
Use FILE_ALLOCATION_INFORMATION only when allocation from offset 0 can
cover the requested range, and verify the resulting AllocationSize before
returning success. Arbitrary non-zero-offset ranges remain unsupported.
Request and verify allocation before changing EOF, so a failed allocation
leaves EOF unchanged. Invalidate the cached inode attributes when the
operation fails.
With Samba `strict allocate=yes`, this allows generic/496, generic/568 and
generic/701 to pass after verifying the server allocation. With
`strict allocate=no`, the requests fail instead of reporting false
preallocation success.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 69 +++++++++++++++++++++++++++++++++++----
fs/smb/client/smb2pdu.c | 19 +++++++++++
fs/smb/client/smb2proto.h | 3 ++
fs/smb/common/fscc.h | 5 +++
4 files changed, 90 insertions(+), 6 deletions(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 61b581d86686..8b931581870a 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3665,12 +3665,16 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile = file->private_data;
long rc = -EOPNOTSUPP;
unsigned int xid;
- loff_t new_eof;
+ loff_t old_eof, new_eof;
+ struct smb2_file_all_info file_inf;
+ u64 asize = 0;
+ int qrc;
xid = get_xid();
inode = d_inode(cfile->dentry);
cifsi = CIFS_I(inode);
+ old_eof = i_size_read(inode);
trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid,
tcon->ses->Suid, off, len);
@@ -3686,11 +3690,24 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
/*
* Extending the file
*/
- if ((keep_size == false) && i_size_read(inode) < off + len) {
+ if (!keep_size && old_eof < off + len) {
rc = inode_newsize_ok(inode, off + len);
if (rc)
goto out;
+ /*
+ * FILE_ALLOCATION_INFORMATION sets the file allocation size. It
+ * cannot allocate an arbitrary byte range. Use it only when
+ * allocating blocks from offset 0 to the new EOF is sufficient.
+ *
+ * A non-zero offset is allowed only for an empty file, where this
+ * covers all blocks touched by the requested range.
+ */
+ if (off != 0 && old_eof != 0) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) &&
!smb2_set_sparse(xid, tcon, cfile, inode, false)) {
rc = -EOPNOTSUPP;
@@ -3698,12 +3715,52 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
}
new_eof = off + len;
+
+ qrc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ if (qrc == 0)
+ asize = le64_to_cpu(file_inf.AllocationSize);
+
+ if (qrc || asize < new_eof) {
+ rc = SMB2_set_allocation(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ cfile->pid, new_eof);
+ if (rc)
+ goto invalidate_attrs;
+
+ qrc = SMB2_query_info(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ if (qrc) {
+ rc = qrc;
+ goto invalidate_attrs;
+ }
+
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ if (asize < new_eof) {
+ rc = -EOPNOTSUPP;
+ goto invalidate_attrs;
+ }
+ }
+
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_eof);
- if (rc == 0) {
- netfs_resize_file(&cifsi->netfs, new_eof, true);
- cifs_setsize(inode, new_eof);
- }
+ if (rc)
+ goto invalidate_attrs;
+
+ netfs_resize_file(&cifsi->netfs, new_eof, true);
+ cifs_setsize(inode, new_eof);
+
+ spin_lock(&inode->i_lock);
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
+ spin_unlock(&inode->i_lock);
+ goto out;
+
+invalidate_attrs:
+ spin_lock(&inode->i_lock);
+ cifsi->time = 0;
+ spin_unlock(&inode->i_lock);
goto out;
}
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 4972cfe249f6..1c6fadb7fbf3 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -5899,6 +5899,25 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
0, 1, &data, &size);
}
+int
+SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+ loff_t allocation_size)
+{
+ struct smb2_file_alloc_info info;
+ void *data;
+ unsigned int size;
+
+ info.AllocationSize = cpu_to_le64(allocation_size);
+
+ data = &info;
+ size = sizeof(struct smb2_file_alloc_info);
+
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ pid, FILE_ALLOCATION_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 1, &data, &size);
+}
+
int
SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 78a4e1c340f9..16a02c1eb0a1 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -204,6 +204,9 @@ void SMB2_query_directory_free(struct smb_rqst *rqst);
int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 pid,
loff_t new_eof);
+int SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+ loff_t allocation_size);
int SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
struct smb_rqst *rqst, u64 persistent_fid,
u64 volatile_fid, u32 pid, u8 info_class, u8 info_type,
diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h
index bc3012cc295d..c9d5aa94727f 100644
--- a/fs/smb/common/fscc.h
+++ b/fs/smb/common/fscc.h
@@ -265,6 +265,11 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */
__le64 EndOfFile; /* new end of file value */
} __packed; /* level 20 Set */
+/* See MS-FSCC 2.4.4 */
+struct smb2_file_alloc_info { /* encoding of request for level 19 */
+ __le64 AllocationSize;
+} __packed;
+
/* See MS-FSCC 2.4.15 */
typedef struct {
__le32 NextEntryOffset;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 6/7] smb/client: reduce fallocate zero buffer allocation
2026-06-23 2:46 [PATCH 0/7] smb/client: fix fallocate allocation handling Huiwen He
` (4 preceding siblings ...)
2026-06-23 2:46 ` [PATCH 5/7] smb/client: verify allocation after EOF-extending fallocate Huiwen He
@ 2026-06-23 2:46 ` Huiwen He
2026-06-23 2:46 ` [PATCH 7/7] smb/client: emulate small fallocate ranges at EOF Huiwen He
6 siblings, 0 replies; 9+ messages in thread
From: Huiwen He @ 2026-06-23 2:46 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
The fallocate emulation allocates a 1 MiB zero-filled buffer even though
each SMB2_write request is limited to SMB2_MAX_BUFFER_SIZE, which is
64 KiB. A high-order 1 MiB allocation is more likely to fail on a
fragmented system.
Allocate only the smaller of the requested range and
SMB2_MAX_BUFFER_SIZE, and reuse that zero-filled buffer for every write
request. Also reject a successful write that makes no progress to avoid
looping indefinitely.
This reduces the contiguous allocation required by fallocate emulation
without changing the written data or range semantics.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 8b931581870a..4cab652d9696 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3543,7 +3543,7 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
char *buf)
{
struct cifs_io_parms io_parms = {0};
- int nbytes;
+ unsigned int nbytes;
int rc = 0;
struct kvec iov[2];
@@ -3564,9 +3564,10 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1);
if (rc)
break;
+ if (!nbytes)
+ return -EIO;
if (nbytes > len)
return -EINVAL;
- buf += nbytes;
off += nbytes;
len -= nbytes;
}
@@ -3595,7 +3596,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
if (rc)
goto out;
- buf = kzalloc(1024 * 1024, GFP_KERNEL);
+ buf = kzalloc(min_t(loff_t, len, SMB2_MAX_BUFFER_SIZE), GFP_KERNEL);
if (buf == NULL) {
rc = -ENOMEM;
goto out;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7/7] smb/client: emulate small fallocate ranges at EOF
2026-06-23 2:46 [PATCH 0/7] smb/client: fix fallocate allocation handling Huiwen He
` (5 preceding siblings ...)
2026-06-23 2:46 ` [PATCH 6/7] smb/client: reduce fallocate zero buffer allocation Huiwen He
@ 2026-06-23 2:46 ` Huiwen He
6 siblings, 0 replies; 9+ messages in thread
From: Huiwen He @ 2026-06-23 2:46 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
smb3_simple_falloc() now returns -EOPNOTSUPP for mode 0 requests that
start at EOF of a non-empty file because FILE_ALLOCATION_INFORMATION
cannot allocate an arbitrary byte range.
However, one small case can be handled safely: a range that starts exactly
at the current EOF. For example:
xfs_io -f -c "pwrite 0 1m" file
xfs_io -c "falloc 1m 4k" file
This does not create an intervening hole or overwrite existing data. Write
zeroes to the requested range so that it is allocated and EOF is extended
by the write. Limit this zero-write emulation to 1 MiB to keep the cost
bounded.
This allows small EOF-adjacent fallocate requests to succeed. After the
write, update the local file size and refresh i_blocks from the
server-reported AllocationSize. Larger ranges remain unsupported.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 4cab652d9696..2922373ab478 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3696,6 +3696,43 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
if (rc)
goto out;
+ /*
+ * A small range immediately after EOF can be allocated by
+ * writing zeroes without creating an intervening hole.
+ */
+ if (off == old_eof && old_eof != 0) {
+ if (len > 1024 * 1024) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ rc = smb3_simple_fallocate_range(xid, tcon, cfile,
+ off, len);
+ if (rc) {
+ spin_lock(&inode->i_lock);
+ cifsi->time = 0;
+ spin_unlock(&inode->i_lock);
+ goto out;
+ }
+
+ new_eof = off + len;
+ netfs_resize_file(&cifsi->netfs, new_eof, true);
+ cifs_setsize(inode, new_eof);
+
+ qrc = SMB2_query_info(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ spin_lock(&inode->i_lock);
+ if (qrc == 0) {
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
+ } else {
+ cifsi->time = 0;
+ }
+ spin_unlock(&inode->i_lock);
+ goto out;
+ }
+
/*
* FILE_ALLOCATION_INFORMATION sets the file allocation size. It
* cannot allocate an arbitrary byte range. Use it only when
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread