mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-26 04:34:41 -04:00
smb/client: allow FS_IOC_SETFLAGS to clear compression
The CIFS FS_IOC_SETFLAGS path can set FS_COMPR_FL now, but it cannot clear it again. This can be reproduced on a share backed by a filesystem that supports compression, for example btrfs exported by Samba: [compress_share] vfs objects = btrfs $ touch test.bin $ chattr +c test.bin $ lsattr test.bin $ chattr -c test.bin The final chattr -c fails with EOPNOTSUPP, and leaves the remote object with the compressed attribute still set, because the client always sends FSCTL_SET_COMPRESSION with COMPRESSION_FORMAT_DEFAULT. That is correct for setting FS_COMPR_FL, but clearing FS_COMPR_FL requires sending COMPRESSION_FORMAT_NONE. Fix this by passing the requested compression state through the set_compression operation. The SMB1 and SMB2 helpers no longer hard-code COMPRESSION_FORMAT_DEFAULT. When FS_COMPR_FL is set, send COMPRESSION_FORMAT_DEFAULT. When it is cleared, send COMPRESSION_FORMAT_NONE. If the server accepts the request, update the cached FILE_ATTRIBUTE_COMPRESSED bit under i_lock so FS_IOC_GETFLAGS reports the new state. 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:
@@ -425,7 +425,7 @@ struct smb_version_operations {
|
||||
int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,
|
||||
const unsigned int);
|
||||
int (*set_compression)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifsFileInfo *);
|
||||
struct cifsFileInfo *, __u16);
|
||||
/* check if we can send an echo or nor */
|
||||
bool (*can_echo)(struct TCP_Server_Info *);
|
||||
/* send echo request */
|
||||
|
||||
@@ -3207,7 +3207,7 @@ struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
|
||||
|
||||
int
|
||||
CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
__u16 fid)
|
||||
__u16 fid, __u16 compression_state)
|
||||
{
|
||||
int rc = 0;
|
||||
int bytes_returned;
|
||||
@@ -3222,7 +3222,7 @@ CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
return rc;
|
||||
in_len = rc;
|
||||
|
||||
pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
|
||||
pSMB->compression_state = cpu_to_le16(compression_state);
|
||||
|
||||
pSMB->TotalParameterCount = 0;
|
||||
pSMB->TotalDataCount = cpu_to_le32(2);
|
||||
|
||||
@@ -68,7 +68,8 @@ static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
|
||||
}
|
||||
|
||||
static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
|
||||
struct cifs_tcon *tcon)
|
||||
struct cifs_tcon *tcon,
|
||||
__u16 compression_state)
|
||||
{
|
||||
struct inode *inode = file_inode(filep);
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
@@ -127,7 +128,8 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
|
||||
goto close;
|
||||
}
|
||||
|
||||
rc = server->ops->set_compression(xid, tcon, tmp_cfile);
|
||||
rc = server->ops->set_compression(xid, tcon, tmp_cfile,
|
||||
compression_state);
|
||||
|
||||
close:
|
||||
server->ops->close(xid, tcon, &fid);
|
||||
@@ -141,7 +143,8 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
|
||||
|
||||
static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
|
||||
struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile)
|
||||
struct cifsFileInfo *cfile,
|
||||
__u16 compression_state)
|
||||
{
|
||||
struct cifsFileInfo *wfile;
|
||||
struct cifs_tcon *wtcon;
|
||||
@@ -152,7 +155,8 @@ static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (cfile && (cfile->fid.access & FILE_WRITE_DATA)) {
|
||||
rc = tcon->ses->server->ops->set_compression(xid, tcon, cfile);
|
||||
rc = tcon->ses->server->ops->set_compression(xid, tcon, cfile,
|
||||
compression_state);
|
||||
if (rc != -EACCES)
|
||||
return rc;
|
||||
}
|
||||
@@ -160,7 +164,8 @@ static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
|
||||
rc = cifs_get_writable_file(CIFS_I(inode), FIND_FSUID_ONLY, &wfile);
|
||||
if (!rc) {
|
||||
wtcon = tlink_tcon(wfile->tlink);
|
||||
rc = wtcon->ses->server->ops->set_compression(xid, wtcon, wfile);
|
||||
rc = wtcon->ses->server->ops->set_compression(xid, wtcon, wfile,
|
||||
compression_state);
|
||||
cifsFileInfo_put(wfile);
|
||||
if (rc != -EACCES)
|
||||
return rc;
|
||||
@@ -168,7 +173,8 @@ static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
|
||||
return rc;
|
||||
}
|
||||
|
||||
return cifs_set_compression_by_path(xid, filep, tcon);
|
||||
return cifs_set_compression_by_path(xid, filep, tcon,
|
||||
compression_state);
|
||||
}
|
||||
|
||||
static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
|
||||
@@ -460,6 +466,8 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
__u64 ExtAttrBits = 0;
|
||||
bool enable_compression;
|
||||
__u16 compression_state;
|
||||
#ifdef CONFIG_CIFS_POSIX
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
__u64 caps;
|
||||
@@ -523,17 +531,28 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||
* break;
|
||||
*/
|
||||
|
||||
/* Currently only flag we can set is compressed flag */
|
||||
if ((ExtAttrBits & FS_COMPR_FL) == 0)
|
||||
/* Currently only flag we can set or clear is compressed. */
|
||||
if (ExtAttrBits & ~FS_COMPR_FL) {
|
||||
rc = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
enable_compression = ExtAttrBits & FS_COMPR_FL;
|
||||
compression_state = enable_compression ?
|
||||
COMPRESSION_FORMAT_DEFAULT :
|
||||
COMPRESSION_FORMAT_NONE;
|
||||
|
||||
/* Try to set compress flag */
|
||||
rc = cifs_ioctl_set_compression(xid, filep, tcon,
|
||||
pSMBFile);
|
||||
pSMBFile,
|
||||
compression_state);
|
||||
if (rc == 0) {
|
||||
spin_lock(&inode->i_lock);
|
||||
CIFS_I(inode)->cifsAttrs |=
|
||||
FILE_ATTRIBUTE_COMPRESSED;
|
||||
if (enable_compression)
|
||||
CIFS_I(inode)->cifsAttrs |=
|
||||
FILE_ATTRIBUTE_COMPRESSED;
|
||||
else
|
||||
CIFS_I(inode)->cifsAttrs &=
|
||||
~FILE_ATTRIBUTE_COMPRESSED;
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
cifs_dbg(FYI, "set compress flag rc %d\n", rc);
|
||||
|
||||
@@ -1110,9 +1110,10 @@ smb_set_file_info(struct inode *inode, const char *full_path,
|
||||
|
||||
static int
|
||||
cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile)
|
||||
struct cifsFileInfo *cfile, __u16 compression_state)
|
||||
{
|
||||
return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid);
|
||||
return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid,
|
||||
compression_state);
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -117,7 +117,7 @@ struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
|
||||
struct kvec *reparse_iov,
|
||||
struct kvec *xattr_iov);
|
||||
int CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
__u16 fid);
|
||||
__u16 fid, __u16 compression_state);
|
||||
int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const unsigned char *searchName, struct posix_acl **acl,
|
||||
const int acl_type, const struct nls_table *nls_codepage,
|
||||
|
||||
@@ -2246,10 +2246,10 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||
|
||||
static int
|
||||
smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile)
|
||||
struct cifsFileInfo *cfile, __u16 compression_state)
|
||||
{
|
||||
return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid);
|
||||
cfile->fid.volatile_fid, compression_state);
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -3626,14 +3626,14 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||
|
||||
int
|
||||
SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid)
|
||||
u64 persistent_fid, u64 volatile_fid,
|
||||
__u16 compression_state)
|
||||
{
|
||||
int rc;
|
||||
struct compress_ioctl fsctl_input;
|
||||
char *ret_data = NULL;
|
||||
|
||||
fsctl_input.CompressionState =
|
||||
cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
|
||||
fsctl_input.CompressionState = cpu_to_le16(compression_state);
|
||||
|
||||
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
|
||||
FSCTL_SET_COMPRESSION,
|
||||
|
||||
@@ -216,7 +216,8 @@ int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid,
|
||||
struct smb2_file_full_ea_info *buf, int len);
|
||||
int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid);
|
||||
u64 persistent_fid, u64 volatile_fid,
|
||||
__u16 compression_state);
|
||||
int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const u64 persistent_fid, const u64 volatile_fid,
|
||||
__u8 oplock_level);
|
||||
|
||||
Reference in New Issue
Block a user