@@ -2153,6 +2153,14 @@ struct aliasInfo92 {
typedef struct {
__le64 TotalAllocationUnits;
+ __le64 CallerAvailableUnits;
+ __le64 ActualAvailableUnits;
+ __le32 SectorsPerAllocationUnit;
+ __le32 BytesPerSector;
+} __attribute__((packed)) FILE_SYSTEM_FULL_INFO; /* size info, level 0x3ef */
+
+typedef struct {
+ __le64 TotalAllocationUnits;
__le64 FreeAllocationUnits;
__le32 SectorsPerAllocationUnit;
__le32 BytesPerSector;
@@ -298,6 +298,8 @@ extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
struct smb_vol *vol);
+extern int CIFSSMBQFSFullInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *FSData);
extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
struct kstatfs *FSData);
extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
@@ -5015,6 +5015,86 @@ SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
}
int
+CIFSSMBQFSFullInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *FSData)
+{
+/* level 0x3ef SMB_QUERY_FILE_SYSTEM_FULL_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_FULL_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In QFSFullInfo\n");
+QFSInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_FULL_SIZE_INFO);
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QFSFullInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < 24)
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+
+ response_data =
+ (FILE_SYSTEM_FULL_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ FSData->f_bsize =
+ le32_to_cpu(response_data->BytesPerSector) *
+ le32_to_cpu(response_data->
+ SectorsPerAllocationUnit);
+ FSData->f_blocks =
+ le64_to_cpu(response_data->TotalAllocationUnits);
+ FSData->f_bfree = FSData->f_bavail =
+ le64_to_cpu(response_data->CallerAvailableUnits);
+ cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n",
+ (unsigned long long)FSData->f_blocks,
+ (unsigned long long)FSData->f_bfree,
+ FSData->f_bsize);
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSInfoRetry;
+
+ return rc;
+}
+
+int
CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
struct kstatfs *FSData)
{
@@ -900,6 +900,12 @@ cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
rc = CIFSSMBQFSPosixInfo(xid, tcon, buf);
/*
+ * Try FsFullSizeInformation (as it is quota aware)
+ */
+ if (rc && (tcon->ses->capabilities & CAP_NT_SMBS))
+ rc = CIFSSMBQFSFullInfo(xid, tcon, buf);
+
+ /*
* Only need to call the old QFSInfo if failed on newer one,
* e.g. by OS/2.
**/
RHBZ:1505772 In SMB2 we already query FsFullSizeInfo for queryfs since this infolevel is quota-aware. SMB1 used FsSizeInfo but this infolevel does not provide info about quota and thus it will cause 'df' and similar commands to show the wrong amount of data for the share if quotas have been set. This patch changes cifs_queryfs() to first try FsFullSizeInfo and only fallback to FsSizeInfo if FsFullSizeInfo is not available. This makes SMB1 'df' show the correct amounts for quota enabled shares. Reported-by: Xiaoli Feng <xifeng@redhat.com> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com> --- fs/cifs/cifspdu.h | 8 ++++++ fs/cifs/cifsproto.h | 2 ++ fs/cifs/cifssmb.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb1ops.c | 6 ++++ 4 files changed, 96 insertions(+)