ksmbd: support file compression attributes

Advertise file compression support and keep the compression state in the
per-file DOS attributes when the backing filesystem cannot apply the
compression flag directly.  FSCTL_SET_COMPRESSION should still update the
state returned by FSCTL_GET_COMPRESSION and file compression information in
that case.

When a new object is created under a compressed directory, inherit the
compression attribute from the parent.  If FILE_NO_COMPRESSION is
specified, clear the compression state after creation and let it override
inheritance for both files and directories.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Namjae Jeon 2026-07-08 21:40:40 +09:00
parent 49f6a48586
commit 0fb327626a
3 changed files with 91 additions and 15 deletions

View File

@ -2960,6 +2960,34 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *
ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
}
static bool smb2_parent_compressed(struct ksmbd_tree_connect *tcon,
const struct path *path)
{
struct dentry *parent = dget_parent(path->dentry);
struct file_kattr fa = { .flags_valid = true };
struct xattr_dos_attrib da;
bool compressed = false;
int rc;
rc = vfs_fileattr_get(parent, &fa);
if (!rc && fa.flags & FS_COMPR_FL) {
compressed = true;
goto out;
}
if (!test_share_config_flag(tcon->share_conf,
KSMBD_SHARE_FLAG_STORE_DOS_ATTRS))
goto out;
rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_idmap(path->mnt), parent, &da);
if (rc > 0 && da.attr & FILE_ATTRIBUTE_COMPRESSED)
compressed = true;
out:
dput(parent);
return compressed;
}
static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon,
const struct path *path, struct ksmbd_file *fp)
{
@ -3520,8 +3548,6 @@ int smb2_open(struct ksmbd_work *work)
if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) {
rc = -EINVAL;
goto err_out2;
} else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) {
req->CreateOptions &= ~FILE_NO_COMPRESSION_LE;
}
}
}
@ -4128,6 +4154,22 @@ int smb2_open(struct ksmbd_work *work)
ksmbd_vfs_update_compressed_fattr(path.dentry, &fp->f_ci->m_fattr);
if (created) {
if (fp->coption & FILE_NO_COMPRESSION_LE) {
rc = ksmbd_vfs_set_compression_create(work, fp,
COMPRESSION_FORMAT_NONE);
if (rc)
fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_COMPRESSED_LE;
rc = 0;
} else if (smb2_parent_compressed(tcon, &path)) {
rc = ksmbd_vfs_set_compression_create(work, fp,
COMPRESSION_FORMAT_LZNT1);
if (rc)
fp->f_ci->m_fattr |= FILE_ATTRIBUTE_COMPRESSED_LE;
rc = 0;
}
}
if (created)
smb2_new_xattrs(tcon, &path, fp);
@ -6271,6 +6313,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
attrs = FILE_SUPPORTS_OBJECT_IDS |
FILE_PERSISTENT_ACLS |
FILE_UNICODE_ON_DISK |
FILE_FILE_COMPRESSION |
FILE_SUPPORTS_BLOCK_REFCOUNTING;
err = vfs_fileattr_get(path.dentry, &fa);

View File

@ -2008,8 +2008,6 @@ void ksmbd_vfs_update_compressed_fattr(struct dentry *dentry, __le32 *fattr)
struct file_kattr fa = { .flags_valid = true };
rc = vfs_fileattr_get(dentry, &fa);
if (rc == -ENOIOCTLCMD)
*fattr &= ~FILE_ATTRIBUTE_COMPRESSED_LE;
if (rc)
return;
@ -2025,15 +2023,19 @@ int ksmbd_vfs_get_compression(struct ksmbd_file *fp, u16 *fmt)
int rc;
rc = vfs_fileattr_get(fp->filp->f_path.dentry, &fa);
if (rc == -ENOIOCTLCMD) {
*fmt = COMPRESSION_FORMAT_NONE;
if (rc == -ENOIOCTLCMD || rc == -ENOTTY || rc == -EINVAL ||
rc == -EOPNOTSUPP) {
if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_COMPRESSED_LE)
*fmt = COMPRESSION_FORMAT_LZNT1;
else
*fmt = COMPRESSION_FORMAT_NONE;
rc = 0;
goto out;
}
if (rc)
goto out;
if (fa.flags & FS_COMPR_FL)
if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_COMPRESSED_LE)
*fmt = COMPRESSION_FORMAT_LZNT1;
else
*fmt = COMPRESSION_FORMAT_NONE;
@ -2042,7 +2044,9 @@ int ksmbd_vfs_get_compression(struct ksmbd_file *fp, u16 *fmt)
return rc;
}
int ksmbd_vfs_set_compression(struct ksmbd_work *work, struct ksmbd_file *fp, u16 fmt)
static int __ksmbd_vfs_set_compression(struct ksmbd_work *work,
struct ksmbd_file *fp, u16 fmt,
bool check_access)
{
const struct cred *saved_cred = NULL;
struct file_kattr fa;
@ -2052,13 +2056,23 @@ int ksmbd_vfs_set_compression(struct ksmbd_work *work, struct ksmbd_file *fp, u1
__le32 old_fattr;
int rc;
if (!(fp->daccess & FILE_WRITE_DATA_LE)) {
if (check_access && !(fp->daccess & FILE_WRITE_DATA_LE)) {
rc = -EACCES;
goto out;
}
if (fmt != COMPRESSION_FORMAT_NONE &&
fmt != COMPRESSION_FORMAT_DEFAULT &&
fmt != COMPRESSION_FORMAT_LZNT1) {
rc = -EINVAL;
goto out;
}
saved_cred = override_creds(fp->filp->f_cred);
rc = vfs_fileattr_get(dentry, &fa);
if (rc == -ENOIOCTLCMD || rc == -ENOTTY || rc == -EINVAL ||
rc == -EOPNOTSUPP)
goto update_fattr;
if (rc)
goto out;
@ -2068,9 +2082,6 @@ int ksmbd_vfs_set_compression(struct ksmbd_work *work, struct ksmbd_file *fp, u1
} else if (fmt == COMPRESSION_FORMAT_DEFAULT ||
fmt == COMPRESSION_FORMAT_LZNT1) {
flags |= FS_COMPR_FL;
} else {
rc = -EINVAL;
goto out;
}
if (flags != fa.flags) {
@ -2081,10 +2092,14 @@ int ksmbd_vfs_set_compression(struct ksmbd_work *work, struct ksmbd_file *fp, u1
rc = vfs_fileattr_set(idmap, dentry, &fa);
mnt_drop_write_file(fp->filp);
if (rc == -ENOIOCTLCMD || rc == -ENOTTY || rc == -EINVAL ||
rc == -EOPNOTSUPP)
goto update_fattr;
if (rc)
goto out;
}
update_fattr:
old_fattr = fp->f_ci->m_fattr;
if (fmt == COMPRESSION_FORMAT_NONE)
fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_COMPRESSED_LE;
@ -2094,15 +2109,19 @@ int ksmbd_vfs_set_compression(struct ksmbd_work *work, struct ksmbd_file *fp, u1
if (fp->f_ci->m_fattr != old_fattr &&
test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) {
struct xattr_dos_attrib da;
struct xattr_dos_attrib da = {0};
rc = ksmbd_vfs_get_dos_attrib_xattr(idmap, dentry, &da);
if (rc <= 0) {
rc = 0;
goto out;
da.version = 4;
da.itime = fp->itime;
da.create_time = fp->create_time;
da.flags = XATTR_DOSINFO_CREATE_TIME |
XATTR_DOSINFO_ITIME;
}
da.attr = le32_to_cpu(fp->f_ci->m_fattr);
da.flags |= XATTR_DOSINFO_ATTRIB;
rc = ksmbd_vfs_set_dos_attrib_xattr(idmap,
&fp->filp->f_path,
&da, true);
@ -2115,3 +2134,15 @@ int ksmbd_vfs_set_compression(struct ksmbd_work *work, struct ksmbd_file *fp, u1
revert_creds(saved_cred);
return rc;
}
int ksmbd_vfs_set_compression(struct ksmbd_work *work,
struct ksmbd_file *fp, u16 fmt)
{
return __ksmbd_vfs_set_compression(work, fp, fmt, true);
}
int ksmbd_vfs_set_compression_create(struct ksmbd_work *work,
struct ksmbd_file *fp, u16 fmt)
{
return __ksmbd_vfs_set_compression(work, fp, fmt, false);
}

View File

@ -171,4 +171,6 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
void ksmbd_vfs_update_compressed_fattr(struct dentry *dentry, __le32 *fattr);
int ksmbd_vfs_get_compression(struct ksmbd_file *fp, u16 *fmt);
int ksmbd_vfs_set_compression(struct ksmbd_work *work, struct ksmbd_file *fp, u16 fmt);
int ksmbd_vfs_set_compression_create(struct ksmbd_work *work,
struct ksmbd_file *fp, u16 fmt);
#endif /* __KSMBD_VFS_H__ */