Skip to content

Commit

Permalink
ksmbd: smb1: implement SMB_COM_QUERY_INFORMATION_DISK command
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Namjae Jeon <[email protected]>
  • Loading branch information
Marios Makassikis authored and namjaejeon committed Oct 14, 2023
1 parent c0fbd54 commit 57f08e6
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
1 change: 1 addition & 0 deletions smb1misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions smb1ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, },
Expand Down
137 changes: 137 additions & 0 deletions smb1pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions smb1pdu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */

0 comments on commit 57f08e6

Please sign in to comment.