mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
smb/client: refresh allocation after EOF-extending fallocate
Before this change, xfstests generic/496 was not supported on ksmbd:
generic/496 ... [not run] fallocated swap not supported here
ksmbd handles SetEOF as truncate, so EOF extension alone does not
allocate backing blocks. A fallocated swapfile can therefore still
look sparse to swapon.
Request allocation for EOF-extending fallocate ranges that can be
represented by FILE_ALLOCATION_INFORMATION, and refresh the allocation
state afterwards.
With this change, xfstests generic/496 and generic/701 pass on ksmbd.
However, Samba "strict allocate = no" now exposes the real generic/701
failure: the old pass came from inflated local i_blocks, not from
server allocation. generic/213 also fails in that case because an
oversized allocation request may not return ENOSPC.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
@@ -3788,12 +3788,49 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||
smb2_set_sparse(xid, tcon, cfile, inode, false);
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* FILE_ALLOCATION_INFORMATION can only describe allocation up to
|
||||
* new_eof. Some servers may accept it without allocating blocks,
|
||||
* so refresh AllocationSize before updating i_blocks.
|
||||
*/
|
||||
if (off == 0 || off == old_eof) {
|
||||
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 out;
|
||||
}
|
||||
}
|
||||
|
||||
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 out;
|
||||
|
||||
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);
|
||||
if (asize >= new_eof)
|
||||
inode->i_blocks = CIFS_INO_BLOCKS(asize);
|
||||
} else {
|
||||
cifsi->time = 0;
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -5949,6 +5949,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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -283,6 +283,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;
|
||||
|
||||
@@ -212,10 +212,6 @@ struct smb2_file_ea_info {
|
||||
__le32 EASize;
|
||||
} __packed;
|
||||
|
||||
struct smb2_file_alloc_info {
|
||||
__le64 AllocationSize;
|
||||
} __packed;
|
||||
|
||||
struct smb2_file_disposition_info {
|
||||
__u8 DeletePending;
|
||||
} __packed;
|
||||
|
||||
Reference in New Issue
Block a user