* [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting
@ 2026-06-26 13:47 Huiwen He
2026-06-26 13:47 ` [PATCH v4 1/7] smb/client: refresh allocation size after duplicate extents Huiwen He
` (7 more replies)
0 siblings, 8 replies; 11+ messages in thread
From: Huiwen He @ 2026-06-26 13:47 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
Changes in v4:
- Add new patch 1 to refresh i_blocks after successful duplicate-extents.
This fixes stale st_blocks after reflink and avoids the generic/370
swapon hole-check regression.
The following patches from v2 have already been merged into cifs-2.6.git for-next:
- smb/client: do not account EOF extension as allocation
- smb/client: preserve errors from smb2_set_sparse()
- smb/client: name the default fallocate mode
Link to v3:
https://lore.kernel.org/linux-cifs/20260625160154.104450-1-huiwen.he@linux.dev
Link to v2:
https://lore.kernel.org/linux-cifs/20260624021550.1548952-1-huiwen.he@linux.dev
Thanks,
Huiwen
Huiwen He (7):
smb/client: refresh allocation size after duplicate extents
smb/client: handle smb2_set_sparse() failure in EOF-extending
fallocate
smb/client: handle smb2_set_sparse() failure in non-extending
fallocate
smb/client: handle overlapping allocated ranges in fallocate
smb/client: reduce fallocate zero buffer allocation
smb/client: emulate small mode 0 fallocate ranges at or past EOF
smb/client: verify allocation after EOF-extending fallocate
fs/smb/client/smb2ops.c | 169 +++++++++++++++++++++++++++++++++-----
fs/smb/client/smb2pdu.c | 19 +++++
fs/smb/client/smb2proto.h | 3 +
fs/smb/common/fscc.h | 5 ++
4 files changed, 175 insertions(+), 21 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 1/7] smb/client: refresh allocation size after duplicate extents
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
@ 2026-06-26 13:47 ` Huiwen He
2026-06-26 13:47 ` [PATCH v4 2/7] smb/client: handle smb2_set_sparse() failure in EOF-extending fallocate Huiwen He
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Huiwen He @ 2026-06-26 13:47 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
FSCTL_DUPLICATE_EXTENTS_TO_FILE changes the target file extents on the
server, but the client does not refresh the target AllocationSize/i_blocks.
Callers can observe or use the wrong st_blocks value immediately after the
clone, before a later attribute revalidation corrects it.
For example, create a reflinked file with a leading hole:
xfs_io -f -c "pwrite -S 0x61 0 64k" src
touch dst
chmod 600 dst
xfs_io -c "reflink src 0 1m 64k" dst
mkswap dst
swapon dst
The file still has a hole after mkswap:
/mnt/scratch/dst:
[0..7]: allocated
[8..2047]: hole
[2048..2175]: allocated
The server also reports only the allocated ranges:
server dst size=1114112 blocks=144
but the client reported EOF-derived blocks:
client dst size=1114112 blocks=2176
and swapon succeeded:
swapon_result=success
/mnt/scratch/dst 1.1M 0B -1
So EOF-derived i_blocks can let a sparse reflinked file pass the CIFS
swapfile hole check.
Fix this by querying FILE_ALL_INFORMATION on the target handle after a
successful duplicate extents request and updating i_blocks from the
returned AllocationSize. If the query fails, invalidate the cached
inode attributes so a later getattr can refresh them.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 965a4d21dd43..f13e5d902244 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -2193,10 +2193,13 @@ smb2_duplicate_extents(const unsigned int xid,
u64 len, u64 dest_off)
{
int rc;
+ int qrc;
unsigned int ret_data_len;
struct inode *inode;
+ struct smb2_file_all_info file_inf;
struct duplicate_extents_to_file dup_ext_buf;
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
+ u64 asize;
/* server fileays advertise duplicate extent support with this flag */
if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) &
@@ -2232,6 +2235,19 @@ 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) {
+ qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
+ trgtfile->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 {
+ CIFS_I(inode)->time = 0; /* force reval */
+ }
+ spin_unlock(&inode->i_lock);
+ }
+
duplicate_extents_out:
if (rc)
trace_smb3_clone_err(xid, srcfile->fid.volatile_fid,
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 2/7] smb/client: handle smb2_set_sparse() failure in EOF-extending fallocate
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
2026-06-26 13:47 ` [PATCH v4 1/7] smb/client: refresh allocation size after duplicate extents Huiwen He
@ 2026-06-26 13:47 ` Huiwen He
2026-06-26 13:47 ` [PATCH v4 3/7] smb/client: handle smb2_set_sparse() failure in non-extending fallocate Huiwen He
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Huiwen He @ 2026-06-26 13:47 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 the smb2_set_sparse() error without extending EOF. 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 f13e5d902244..bbcd4533406c 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3707,8 +3707,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) {
+ rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
+ if (rc)
+ 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] 11+ messages in thread
* [PATCH v4 3/7] smb/client: handle smb2_set_sparse() failure in non-extending fallocate
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
2026-06-26 13:47 ` [PATCH v4 1/7] smb/client: refresh allocation size after duplicate extents Huiwen He
2026-06-26 13:47 ` [PATCH v4 2/7] smb/client: handle smb2_set_sparse() failure in EOF-extending fallocate Huiwen He
@ 2026-06-26 13:47 ` Huiwen He
2026-06-26 13:47 ` [PATCH v4 4/7] smb/client: handle overlapping allocated ranges in fallocate Huiwen He
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Huiwen He @ 2026-06-26 13:47 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 these requests by clearing the sparse attribute for the
whole file. However, it ignores failure from smb2_set_sparse() and returns
success unconditionally.
Return the smb2_set_sparse() error. 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 | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index bbcd4533406c..2017357efac5 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3786,8 +3786,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
}
}
- smb2_set_sparse(xid, tcon, cfile, inode, false);
- rc = 0;
+ rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
out:
if (rc)
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 4/7] smb/client: handle overlapping allocated ranges in fallocate
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
` (2 preceding siblings ...)
2026-06-26 13:47 ` [PATCH v4 3/7] smb/client: handle smb2_set_sparse() failure in non-extending fallocate Huiwen He
@ 2026-06-26 13:47 ` Huiwen He
2026-06-26 13:47 ` [PATCH v4 5/7] smb/client: reduce fallocate zero buffer allocation Huiwen He
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Huiwen He @ 2026-06-26 13:47 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_fallocate_range() can skip holes when an allocated range
returned by the server starts before the current fallocate offset. The
skipped hole is not zero-filled, but fallocate still returns success. A
later write to that hole may therefore fail with ENOSPC.
The function queries allocated ranges so that it can preserve existing
contents and write zeroes only into holes. However, the server may return
a range that starts before the current fallocate offset.
For example, assume the fallocate request is [100, 400) and the only
allocated range returned by the server is [0, 200):
Request: [100, 400)
Server range: [ 0, 200) allocated
Correct:
[100, 200) allocated data, skip
[200, 400) hole, zero-fill
Current:
[100, 300) skipped
[300, 400) zero-filled afterwards
The current code adds the full server range length, 200, to the current
offset 100 and moves to 300. As a result, the hole in [200, 300) is
skipped without being zero-filled.
Calculate the end of each allocated range and advance from the current
offset to that end. Ignore ranges ending before the current offset and
reject ranges whose end offset overflows.
Fixes: 966a3cb7c7db ("cifs: improve fallocate emulation")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 25 ++++++++++++++++++-------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 2017357efac5..4cd0e578e3ce 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3597,6 +3597,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
u32 out_data_len;
char *buf = NULL;
+ u64 range_start, range_len, range_end;
loff_t l;
int rc;
@@ -3633,13 +3634,21 @@ static int smb3_simple_fallocate_range(unsigned int xid,
goto out;
}
- if (off < le64_to_cpu(tmp_data->file_offset)) {
+ range_start = le64_to_cpu(tmp_data->file_offset);
+ range_len = le64_to_cpu(tmp_data->length);
+ if (check_add_overflow(range_start, range_len, &range_end) ||
+ range_end > S64_MAX) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (off < range_start) {
/*
* We are at a hole. Write until the end of the region
* or until the next allocated data,
* whichever comes next.
*/
- l = le64_to_cpu(tmp_data->file_offset) - off;
+ l = range_start - off;
if (len < l)
l = len;
rc = smb3_simple_fallocate_write_range(xid, tcon,
@@ -3656,11 +3665,13 @@ static int smb3_simple_fallocate_range(unsigned int xid,
* until the end of the data or the end of the region
* we are supposed to fallocate, whichever comes first.
*/
- l = le64_to_cpu(tmp_data->length);
- if (len < l)
- l = len;
- off += l;
- len -= l;
+ if (off < range_end) {
+ l = range_end - off;
+ if (len < l)
+ l = len;
+ off += l;
+ len -= l;
+ }
tmp_data = &tmp_data[1];
out_data_len -= sizeof(struct file_allocated_range_buffer);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 5/7] smb/client: reduce fallocate zero buffer allocation
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
` (3 preceding siblings ...)
2026-06-26 13:47 ` [PATCH v4 4/7] smb/client: handle overlapping allocated ranges in fallocate Huiwen He
@ 2026-06-26 13:47 ` Huiwen He
2026-06-26 13:47 ` [PATCH v4 6/7] smb/client: emulate small mode 0 fallocate ranges at or past EOF Huiwen He
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Huiwen He @ 2026-06-26 13:47 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 4cd0e578e3ce..311709d73798 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3559,7 +3559,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];
@@ -3580,9 +3580,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;
}
@@ -3612,7 +3613,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] 11+ messages in thread
* [PATCH v4 6/7] smb/client: emulate small mode 0 fallocate ranges at or past EOF
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
` (4 preceding siblings ...)
2026-06-26 13:47 ` [PATCH v4 5/7] smb/client: reduce fallocate zero buffer allocation Huiwen He
@ 2026-06-26 13:47 ` Huiwen He
2026-06-27 18:04 ` Steve French
2026-06-26 13:47 ` [PATCH v4 7/7] smb/client: verify allocation after EOF-extending fallocate Huiwen He
2026-06-27 18:06 ` [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Steve French
7 siblings, 1 reply; 11+ messages in thread
From: Huiwen He @ 2026-06-26 13:47 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 xfstest generic/213 has a mode 0 fallocate case like:
falloc 0 1G
falloc 2G 1M
truncate 3G
The expected layout is:
allocated [0, 1G)
hole [1G, 2G)
allocated [2G, 2G + 1M)
Current CIFS can pass this test while allocating the intervening hole.
Before this change, the server-side layout was effectively:
allocated [0, 1G)
allocated [1G, 2G + 1M)
so [1G, 2G) was allocated as part of the second extent.
Emulate small mode 0 fallocate ranges that start at or past EOF by writing
zeroes only to the requested range. For off > old_eof, this preserves the
hole between old_eof and off. Keep this emulation limited to 1 MiB so the
client-side I/O cost remains bounded; larger unsupported ranges remain
rejected.
After this change, the same pattern gives the intended layout:
allocated [0, 1G)
hole [1G, 2G)
allocated [2G, 2G + 1M)
This changes generic/213 from a false pass into a range-correct pass.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/client/smb2ops.c | 50 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 47 insertions(+), 3 deletions(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 311709d73798..02ecba985daa 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3693,18 +3693,22 @@ 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;
+ 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);
/* if file not oplocked can't be sure whether asking to extend size */
if (!CIFS_CACHE_READ(cifsi))
- if (keep_size == false) {
+ if (!keep_size) {
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, off, len, rc);
free_xid(xid);
@@ -3714,11 +3718,51 @@ 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;
+ /*
+ * A small range at or beyond EOF can be allocated by writing
+ * zeroes. For off > old_eof, this preserves the intervening
+ * hole instead of allocating from offset 0.
+ */
+ if (off > old_eof ||
+ (off == old_eof && old_eof != 0 &&
+ (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE))) {
+ 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;
+ }
+
if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) {
rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
if (rc)
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 7/7] smb/client: verify allocation after EOF-extending fallocate
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
` (5 preceding siblings ...)
2026-06-26 13:47 ` [PATCH v4 6/7] smb/client: emulate small mode 0 fallocate ranges at or past EOF Huiwen He
@ 2026-06-26 13:47 ` Huiwen He
2026-06-27 18:06 ` [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Steve French
7 siblings, 0 replies; 11+ messages in thread
From: Huiwen He @ 2026-06-26 13:47 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 allocating [0, off + len)
safely covers the requested range [off, off + len). For example, both
ranges are identical when the request starts at offset zero:
xfs_io -f -c "falloc 0 4m" file
An EOF-adjacent request is safe for a non-sparse file because the range
before EOF is already allocated:
xfs_io -f -c "pwrite 0 1m" file
xfs_io -c "falloc 1m 4m" file
Request and verify the resulting AllocationSize before extending EOF, so
an allocation failure leaves the file size unchanged. Reject other
non-zero-offset requests because AllocationSize cannot identify which
byte ranges were allocated.
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 | 63 +++++++++++++++++++++++++++++++++++----
fs/smb/client/smb2pdu.c | 19 ++++++++++++
fs/smb/client/smb2proto.h | 3 ++
fs/smb/common/fscc.h | 5 ++++
4 files changed, 85 insertions(+), 5 deletions(-)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 02ecba985daa..da846dbdd4ea 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3695,7 +3695,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
unsigned int xid;
loff_t old_eof, new_eof;
struct smb2_file_all_info file_inf;
- u64 asize;
+ u64 asize = 0;
int qrc;
xid = get_xid();
@@ -3763,6 +3763,19 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
goto out;
}
+ /*
+ * FILE_ALLOCATION_INFORMATION sets the allocation size for the whole
+ * file and cannot allocate an arbitrary byte range. Use it only for
+ * requests starting at offset zero or requests appending to a
+ * non-sparse file.
+ */
+ if (off != 0 && old_eof != 0 &&
+ (off != old_eof ||
+ (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE))) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) {
rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
if (rc)
@@ -3770,12 +3783,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] 11+ messages in thread
* Re: [PATCH v4 6/7] smb/client: emulate small mode 0 fallocate ranges at or past EOF
2026-06-26 13:47 ` [PATCH v4 6/7] smb/client: emulate small mode 0 fallocate ranges at or past EOF Huiwen He
@ 2026-06-27 18:04 ` Steve French
0 siblings, 0 replies; 11+ messages in thread
From: Steve French @ 2026-06-27 18:04 UTC (permalink / raw)
To: Huiwen He
Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong, linux-cifs
Running to Samba I have always been getting a failure with generic/213
(with or without this patch). Any ideas?
generic/213 - output mismatch (see
/home/smfrench/xfstests-dev/results//sambamfs/generic/213.out.bad)
--- tests/generic/213.out 2026-06-16 19:54:18.366737595 -0500
+++ /home/smfrench/xfstests-dev/results//sambamfs/generic/213.out.bad
2026-06-27 12:37:15.973294906 -0500
@@ -1,4 +1,3 @@
QA output created by 213
We should get: fallocate: No space left on device
Strangely, xfs_io sometimes says "Success" when something went wrong, FYI
-fallocate: No space left on device
On Fri, Jun 26, 2026 at 8:48 AM Huiwen He <huiwen.he@linux.dev> wrote:
>
> From: Huiwen He <hehuiwen@kylinos.cn>
>
> The xfstest generic/213 has a mode 0 fallocate case like:
>
> falloc 0 1G
> falloc 2G 1M
> truncate 3G
>
> The expected layout is:
>
> allocated [0, 1G)
> hole [1G, 2G)
> allocated [2G, 2G + 1M)
>
> Current CIFS can pass this test while allocating the intervening hole.
> Before this change, the server-side layout was effectively:
>
> allocated [0, 1G)
> allocated [1G, 2G + 1M)
>
> so [1G, 2G) was allocated as part of the second extent.
>
> Emulate small mode 0 fallocate ranges that start at or past EOF by writing
> zeroes only to the requested range. For off > old_eof, this preserves the
> hole between old_eof and off. Keep this emulation limited to 1 MiB so the
> client-side I/O cost remains bounded; larger unsupported ranges remain
> rejected.
>
> After this change, the same pattern gives the intended layout:
>
> allocated [0, 1G)
> hole [1G, 2G)
> allocated [2G, 2G + 1M)
>
> This changes generic/213 from a false pass into a range-correct pass.
>
> Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
> ---
> fs/smb/client/smb2ops.c | 50 ++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 47 insertions(+), 3 deletions(-)
>
> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
> index 311709d73798..02ecba985daa 100644
> --- a/fs/smb/client/smb2ops.c
> +++ b/fs/smb/client/smb2ops.c
> @@ -3693,18 +3693,22 @@ 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;
> + 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);
> /* if file not oplocked can't be sure whether asking to extend size */
> if (!CIFS_CACHE_READ(cifsi))
> - if (keep_size == false) {
> + if (!keep_size) {
> trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
> tcon->tid, tcon->ses->Suid, off, len, rc);
> free_xid(xid);
> @@ -3714,11 +3718,51 @@ 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;
>
> + /*
> + * A small range at or beyond EOF can be allocated by writing
> + * zeroes. For off > old_eof, this preserves the intervening
> + * hole instead of allocating from offset 0.
> + */
> + if (off > old_eof ||
> + (off == old_eof && old_eof != 0 &&
> + (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE))) {
> + 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;
> + }
> +
> if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) {
> rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
> if (rc)
> --
> 2.43.0
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
` (6 preceding siblings ...)
2026-06-26 13:47 ` [PATCH v4 7/7] smb/client: verify allocation after EOF-extending fallocate Huiwen He
@ 2026-06-27 18:06 ` Steve French
2026-06-27 18:13 ` Steve French
7 siblings, 1 reply; 11+ messages in thread
From: Steve French @ 2026-06-27 18:06 UTC (permalink / raw)
To: Huiwen He
Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong, linux-cifs
Everything passes with six of these seven as long as I leave out:
smb/client: verify allocation after EOF-extending fallocate
and revert the earlier patch:
smb/client: preserve errors from smb2_set_sparse()
On Fri, Jun 26, 2026 at 8:47 AM Huiwen He <huiwen.he@linux.dev> wrote:
>
> From: Huiwen He <hehuiwen@kylinos.cn>
>
> Changes in v4:
>
> - Add new patch 1 to refresh i_blocks after successful duplicate-extents.
> This fixes stale st_blocks after reflink and avoids the generic/370
> swapon hole-check regression.
>
> The following patches from v2 have already been merged into cifs-2.6.git for-next:
> - smb/client: do not account EOF extension as allocation
> - smb/client: preserve errors from smb2_set_sparse()
> - smb/client: name the default fallocate mode
>
> Link to v3:
> https://lore.kernel.org/linux-cifs/20260625160154.104450-1-huiwen.he@linux.dev
>
> Link to v2:
> https://lore.kernel.org/linux-cifs/20260624021550.1548952-1-huiwen.he@linux.dev
>
> Thanks,
> Huiwen
>
> Huiwen He (7):
> smb/client: refresh allocation size after duplicate extents
> smb/client: handle smb2_set_sparse() failure in EOF-extending
> fallocate
> smb/client: handle smb2_set_sparse() failure in non-extending
> fallocate
> smb/client: handle overlapping allocated ranges in fallocate
> smb/client: reduce fallocate zero buffer allocation
> smb/client: emulate small mode 0 fallocate ranges at or past EOF
> smb/client: verify allocation after EOF-extending fallocate
>
> fs/smb/client/smb2ops.c | 169 +++++++++++++++++++++++++++++++++-----
> fs/smb/client/smb2pdu.c | 19 +++++
> fs/smb/client/smb2proto.h | 3 +
> fs/smb/common/fscc.h | 5 ++
> 4 files changed, 175 insertions(+), 21 deletions(-)
>
> --
> 2.43.0
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting
2026-06-27 18:06 ` [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Steve French
@ 2026-06-27 18:13 ` Steve French
0 siblings, 0 replies; 11+ messages in thread
From: Steve French @ 2026-06-27 18:13 UTC (permalink / raw)
To: Huiwen He
Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong, linux-cifs
With all patches (including smb/client: preserve errors from
smb2_set_sparse) except
smb/client: verify allocation after EOF-extending fallocate
tests 496 and 701 fail
On Sat, Jun 27, 2026 at 1:06 PM Steve French <smfrench@gmail.com> wrote:
>
> Everything passes with six of these seven as long as I leave out:
>
> smb/client: verify allocation after EOF-extending fallocate
>
> and revert the earlier patch:
>
> smb/client: preserve errors from smb2_set_sparse()
>
> On Fri, Jun 26, 2026 at 8:47 AM Huiwen He <huiwen.he@linux.dev> wrote:
> >
> > From: Huiwen He <hehuiwen@kylinos.cn>
> >
> > Changes in v4:
> >
> > - Add new patch 1 to refresh i_blocks after successful duplicate-extents.
> > This fixes stale st_blocks after reflink and avoids the generic/370
> > swapon hole-check regression.
> >
> > The following patches from v2 have already been merged into cifs-2.6.git for-next:
> > - smb/client: do not account EOF extension as allocation
> > - smb/client: preserve errors from smb2_set_sparse()
> > - smb/client: name the default fallocate mode
> >
> > Link to v3:
> > https://lore.kernel.org/linux-cifs/20260625160154.104450-1-huiwen.he@linux.dev
> >
> > Link to v2:
> > https://lore.kernel.org/linux-cifs/20260624021550.1548952-1-huiwen.he@linux.dev
> >
> > Thanks,
> > Huiwen
> >
> > Huiwen He (7):
> > smb/client: refresh allocation size after duplicate extents
> > smb/client: handle smb2_set_sparse() failure in EOF-extending
> > fallocate
> > smb/client: handle smb2_set_sparse() failure in non-extending
> > fallocate
> > smb/client: handle overlapping allocated ranges in fallocate
> > smb/client: reduce fallocate zero buffer allocation
> > smb/client: emulate small mode 0 fallocate ranges at or past EOF
> > smb/client: verify allocation after EOF-extending fallocate
> >
> > fs/smb/client/smb2ops.c | 169 +++++++++++++++++++++++++++++++++-----
> > fs/smb/client/smb2pdu.c | 19 +++++
> > fs/smb/client/smb2proto.h | 3 +
> > fs/smb/common/fscc.h | 5 ++
> > 4 files changed, 175 insertions(+), 21 deletions(-)
> >
> > --
> > 2.43.0
> >
>
>
> --
> Thanks,
>
> Steve
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-27 18:13 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-26 13:47 [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Huiwen He
2026-06-26 13:47 ` [PATCH v4 1/7] smb/client: refresh allocation size after duplicate extents Huiwen He
2026-06-26 13:47 ` [PATCH v4 2/7] smb/client: handle smb2_set_sparse() failure in EOF-extending fallocate Huiwen He
2026-06-26 13:47 ` [PATCH v4 3/7] smb/client: handle smb2_set_sparse() failure in non-extending fallocate Huiwen He
2026-06-26 13:47 ` [PATCH v4 4/7] smb/client: handle overlapping allocated ranges in fallocate Huiwen He
2026-06-26 13:47 ` [PATCH v4 5/7] smb/client: reduce fallocate zero buffer allocation Huiwen He
2026-06-26 13:47 ` [PATCH v4 6/7] smb/client: emulate small mode 0 fallocate ranges at or past EOF Huiwen He
2026-06-27 18:04 ` Steve French
2026-06-26 13:47 ` [PATCH v4 7/7] smb/client: verify allocation after EOF-extending fallocate Huiwen He
2026-06-27 18:06 ` [PATCH v4 0/7] smb/client: fix fallocate and allocation accounting Steve French
2026-06-27 18:13 ` Steve French
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.