From 57f08e6c71d6732904f5bdb6eb285e983ec63473 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Wed, 4 Oct 2023 13:31:00 +0200 Subject: [PATCH] ksmbd: smb1: implement SMB_COM_QUERY_INFORMATION_DISK command SMB_COM_QUERY_INFORMATION_DISK is marked as deprecated, but smbclient will use it if SMB Trans2/QUERY_FS_INFO fails. ksmbd will disconnect the client because the command is not implemented. The response to this command contains the same information as QUERY_FS_SIZE_INFO/QUERY_FS_FULL_SIZE_INFO infolevels. The difference is that the fields are u16, so they may not be large enough. Values are adjusted so that the client can determine free and used space in bytes (which is what it really cares about). Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon --- smb1misc.c | 1 + smb1ops.c | 1 + smb1pdu.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++ smb1pdu.h | 12 +++++ 4 files changed, 151 insertions(+) diff --git a/smb1misc.c b/smb1misc.c index e29581d92..bc86caa8f 100644 --- a/smb1misc.c +++ b/smb1misc.c @@ -54,6 +54,7 @@ static int smb1_req_struct_size(struct smb_hdr *hdr) case SMB_COM_NT_CANCEL: case SMB_COM_CHECK_DIRECTORY: case SMB_COM_PROCESS_EXIT: + case SMB_COM_QUERY_INFORMATION_DISK: if (wc != 0x0) return -EINVAL; break; diff --git a/smb1ops.c b/smb1ops.c index 43f45c196..21a963c4d 100644 --- a/smb1ops.c +++ b/smb1ops.c @@ -67,6 +67,7 @@ static struct smb_version_cmds smb1_server_cmds[256] = { [SMB_COM_SESSION_SETUP_ANDX] = { .proc = smb_session_setup_andx, }, [SMB_COM_LOGOFF_ANDX] = { .proc = smb_session_disconnect, }, [SMB_COM_TREE_CONNECT_ANDX] = { .proc = smb_tree_connect_andx, }, + [SMB_COM_QUERY_INFORMATION_DISK] = { .proc = smb_query_information_disk, }, [SMB_COM_NT_CREATE_ANDX] = { .proc = smb_nt_create_andx, }, [SMB_COM_NT_CANCEL] = { .proc = smb_nt_cancel, }, [SMB_COM_NT_RENAME] = { .proc = smb_nt_rename, }, diff --git a/smb1pdu.c b/smb1pdu.c index adc4c2f2e..6ee351c95 100644 --- a/smb1pdu.c +++ b/smb1pdu.c @@ -8405,6 +8405,143 @@ int smb_setattr(struct ksmbd_work *work) return 0; } +/** + * smb_query_information_disk() - determine capacity and remaining free space + * @work: smb work containing command + * + * Return: 0 on success, otherwise error + */ +int smb_query_information_disk(struct ksmbd_work *work) +{ + struct smb_hdr *req = work->request_buf; + struct smb_com_query_information_disk_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct ksmbd_tree_connect *tree_conn; + struct kstatfs stfs; + struct path path; + int err = 0; + + u16 blocks_per_unit, bytes_per_block, total_units, free_units; + u64 total_blocks, free_blocks; + u32 block_size, unit_size; + + if (req->WordCount) { + err = -EINVAL; + goto out; + } + + tree_conn = ksmbd_tree_conn_lookup(work->sess, le16_to_cpu(req->Tid)); + if (!tree_conn) { + err = -ENOENT; + goto out; + } + + share = tree_conn->share_conf; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + err = -ENOENT; + goto out; + } + + if (ksmbd_override_fsids(work)) { + err = -ENOMEM; + goto out; + } + + err = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); + if (err) + goto out_fsids; + + err = vfs_statfs(&path, &stfs); + if (err) { + pr_err("cannot do stat of path %s\n", share->path); + goto out_path; + } + + unit_size = stfs.f_bsize / stfs.f_frsize; + block_size = stfs.f_bsize; + total_blocks = stfs.f_blocks; + free_blocks = stfs.f_bavail; + + /* + * clamp block size to at most 512 KB for compatibility with + * older clients + */ + while (block_size > 512) { + block_size >>= 1; + unit_size <<= 1; + } + + /* adjust blocks and sizes until they fit into a u16 */ + while (total_blocks >= 0xFFFF) { + total_blocks >>= 1; + free_blocks >>= 1; + if ((unit_size <<= 1) > 0xFFFF) { + unit_size >>= 1; + total_blocks = 0xFFFF; + free_blocks <<= 1; + break; + } + } + + total_units = (total_blocks >= 0xFFFF) ? 0xFFFF : (u16)total_blocks; + free_units = (free_blocks >= 0xFFFF) ? 0xFFFF : (u16)free_blocks; + bytes_per_block = (u16)block_size; + blocks_per_unit = (u16)unit_size; + + rsp->hdr.WordCount = 5; + + rsp->TotalUnits = total_units; + rsp->BlocksPerUnit = blocks_per_unit; + rsp->BlockSize = bytes_per_block; + rsp->FreeUnits = free_units; + rsp->Pad = 0; + rsp->ByteCount = 0; + + inc_rfc1001_len(rsp, rsp->hdr.WordCount * 2); + +out_path: + path_put(&path); +out_fsids: + ksmbd_revert_fsids(work); +out: + if (err) { + switch (err) { + case -EINVAL: + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + break; + case -ENOMEM: + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + break; + case -ENOENT: + rsp->hdr.Status.CifsError = STATUS_NO_SUCH_FILE; + break; + case -EBUSY: + rsp->hdr.Status.CifsError = STATUS_DELETE_PENDING; + break; + case -EACCES: + case -EXDEV: + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + break; + case -EBADF: + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + break; + case -EFAULT: + rsp->hdr.Status.CifsError = STATUS_INVALID_LEVEL; + break; + case -EOPNOTSUPP: + rsp->hdr.Status.CifsError = STATUS_NOT_IMPLEMENTED; + break; + case -EIO: + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + break; + } + ksmbd_debug(SMB, "%s failed with error %d\n", __func__, err); + } + + return err; +} + /** * smb1_is_sign_req() - handler for checking packet signing status * @work: smb work containing notify command buffer diff --git a/smb1pdu.h b/smb1pdu.h index cd9887c56..11dbec862 100644 --- a/smb1pdu.h +++ b/smb1pdu.h @@ -139,6 +139,7 @@ #define SMB_COM_SESSION_SETUP_ANDX 0x73 #define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ #define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_QUERY_INFORMATION_DISK 0x80 #define SMB_COM_NT_TRANSACT 0xA0 #define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 #define SMB_COM_NT_CREATE_ANDX 0xA2 @@ -470,6 +471,16 @@ struct smb_com_lock_rsp { __le16 ByteCount; } __packed; +struct smb_com_query_information_disk_rsp { + struct smb_hdr hdr; /* wct = 5 */ + __le16 TotalUnits; + __le16 BlocksPerUnit; + __le16 BlockSize; + __le16 FreeUnits; + __le16 Pad; + __le16 ByteCount; +} __packed; + /* tree connect Flags */ #define DISCONNECT_TID 0x0001 #define TCON_EXTENDED_SIGNATURES 0x0004 @@ -1626,6 +1637,7 @@ extern int smb_closedir(struct ksmbd_work *work); extern int smb_open_andx(struct ksmbd_work *work); extern int smb_write(struct ksmbd_work *work); extern int smb_setattr(struct ksmbd_work *work); +extern int smb_query_information_disk(struct ksmbd_work *work); extern int smb_checkdir(struct ksmbd_work *work); extern int smb_process_exit(struct ksmbd_work *work); #endif /* __SMB1PDU_H */