From 1a3d2b084c68fdbd224df01c4650a54ee335f102 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 8 Dec 2021 14:41:38 +0000 Subject: [PATCH] Bug 1735717 - Add a way for getting information about total disk space (disk capacity); r=dom-storage-reviewers,jari,xpcom-reviewers,nika Differential Revision: https://phabricator.services.mozilla.com/D130640 --- xpcom/io/FileDescriptorFile.cpp | 5 ++ xpcom/io/nsIFile.idl | 3 ++ xpcom/io/nsLocalFileUnix.cpp | 94 +++++++++++++++++++++++++-------- xpcom/io/nsLocalFileUnix.h | 13 +++++ xpcom/io/nsLocalFileWin.cpp | 6 +++ xpcom/tests/gtest/TestFile.cpp | 31 +++++++++++ 6 files changed, 130 insertions(+), 22 deletions(-) diff --git a/xpcom/io/FileDescriptorFile.cpp b/xpcom/io/FileDescriptorFile.cpp index 390ef96a2a0f..c1a3ef860175 100644 --- a/xpcom/io/FileDescriptorFile.cpp +++ b/xpcom/io/FileDescriptorFile.cpp @@ -401,6 +401,11 @@ FileDescriptorFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) { return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +FileDescriptorFile::GetDiskCapacity(int64_t* aDiskCapacity) { + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP FileDescriptorFile::Reveal() { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/xpcom/io/nsIFile.idl b/xpcom/io/nsIFile.idl index 03f256a1d032..4192f943bef2 100644 --- a/xpcom/io/nsIFile.idl +++ b/xpcom/io/nsIFile.idl @@ -453,6 +453,9 @@ interface nsIFile : nsISupports // number of bytes available on disk to non-superuser [must_use] readonly attribute int64_t diskSpaceAvailable; + // disk capacity in bytes + [must_use] readonly attribute int64_t diskCapacity; + /** * appendRelative[Native]Path * diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp index 37b11fd2cef1..36969724d889 100644 --- a/xpcom/io/nsLocalFileUnix.cpp +++ b/xpcom/io/nsLocalFileUnix.cpp @@ -8,8 +8,11 @@ * Implementation of nsIFile for "unixy" systems. */ +#include "nsLocalFile.h" + #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" +#include "mozilla/CheckedInt.h" #include "mozilla/DebugOnly.h" #include "mozilla/Sprintf.h" #include "mozilla/FilePreferences.h" @@ -32,8 +35,7 @@ # include #endif -#if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H) -# define USE_LINUX_QUOTACTL +#if defined(USE_LINUX_QUOTACTL) # include # include # include @@ -49,7 +51,6 @@ #include "nsIFile.h" #include "nsString.h" #include "nsReadableUtils.h" -#include "nsLocalFile.h" #include "prproces.h" #include "nsIDirectoryEnumerator.h" #include "nsSimpleEnumerator.h" @@ -1439,9 +1440,18 @@ static bool GetDeviceName(unsigned int aDeviceMajor, unsigned int aDeviceMinor, } #endif -NS_IMETHODIMP -nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) { - if (NS_WARN_IF(!aDiskSpaceAvailable)) { +#if defined(USE_LINUX_QUOTACTL) +template +nsresult nsLocalFile::GetDiskInfo(StatInfoFunc&& aStatInfoFunc, + QuotaInfoFunc&& aQuotaInfoFunc, + int64_t* aResult) +#else +template +nsresult nsLocalFile::GetDiskInfo(StatInfoFunc&& aStatInfoFunc, + int64_t* aResult) +#endif +{ + if (NS_WARN_IF(!aResult)) { return NS_ERROR_INVALID_ARG; } @@ -1459,26 +1469,34 @@ nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) { * F_BSIZE = block size on disk. * f_bavail = number of free blocks available to a non-superuser. * f_bfree = number of total free blocks in file system. + * f_blocks = number of total used or free blocks in file system. */ if (STATFS(mPath.get(), &fs_buf) < 0) { // The call to STATFS failed. # ifdef DEBUG - printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n"); + printf("ERROR: GetDiskInfo: STATFS call FAILED. \n"); # endif return NS_ERROR_FAILURE; } - *aDiskSpaceAvailable = (int64_t)fs_buf.F_BSIZE * fs_buf.f_bavail; + CheckedInt64 checkedResult; + + checkedResult = std::forward(aStatInfoFunc)(fs_buf); + if (!checkedResult.isValid()) { + return NS_ERROR_FAILURE; + } + + *aResult = checkedResult.value(); # ifdef DEBUG_DISK_SPACE - printf("DiskSpaceAvailable: %lu bytes\n", *aDiskSpaceAvailable); + printf("DiskInfo: %lu bytes\n", *aResult); # endif # if defined(USE_LINUX_QUOTACTL) if (!FillStatCache()) { - // Return available size from statfs + // Return info from statfs return NS_OK; } @@ -1495,13 +1513,13 @@ nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) { && dq.dqb_valid & QIF_BLIMITS # endif && dq.dqb_bhardlimit) { - int64_t QuotaSpaceAvailable = 0; - // dqb_bhardlimit is count of BLOCK_SIZE blocks, dqb_curspace is bytes - if ((BLOCK_SIZE * dq.dqb_bhardlimit) > dq.dqb_curspace) - QuotaSpaceAvailable = - int64_t(BLOCK_SIZE * dq.dqb_bhardlimit - dq.dqb_curspace); - if (QuotaSpaceAvailable < *aDiskSpaceAvailable) { - *aDiskSpaceAvailable = QuotaSpaceAvailable; + checkedResult = std::forward(aQuotaInfoFunc)(dq); + if (!checkedResult.isValid()) { + return NS_ERROR_FAILURE; + } + + if (checkedResult.value() < *aResult) { + *aResult = checkedResult.value(); } } # endif @@ -1511,22 +1529,54 @@ nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) { #else /* * This platform doesn't have statfs or statvfs. I'm sure that there's - * a way to check for free disk space on platforms that don't have statfs - * (I'm SURE they have df, for example). + * a way to check for free disk space and disk capacity on platforms that + * don't have statfs (I'm SURE they have df, for example). * * Until we figure out how to do that, lets be honest and say that this * command isn't implemented properly for these platforms yet. */ # ifdef DEBUG - printf( - "ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without " - "statfs.\n"); + printf("ERROR: GetDiskInfo: Not implemented for plaforms without statfs.\n"); # endif return NS_ERROR_NOT_IMPLEMENTED; #endif /* STATFS */ } +NS_IMETHODIMP +nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) { + return GetDiskInfo( + [](const struct STATFS& aStatInfo) { + return aStatInfo.f_bavail * static_cast(aStatInfo.F_BSIZE); + }, +#if defined(USE_LINUX_QUOTACTL) + [](const struct dqblk& aQuotaInfo) -> uint64_t { + // dqb_bhardlimit is count of BLOCK_SIZE blocks, dqb_curspace is bytes + const uint64_t hardlimit = aQuotaInfo.dqb_bhardlimit * BLOCK_SIZE; + if (hardlimit > aQuotaInfo.dqb_curspace) { + return hardlimit - aQuotaInfo.dqb_curspace; + } + return 0; + }, +#endif + aDiskSpaceAvailable); +} + +NS_IMETHODIMP +nsLocalFile::GetDiskCapacity(int64_t* aDiskCapacity) { + return GetDiskInfo( + [](const struct STATFS& aStatInfo) { + return aStatInfo.f_blocks * static_cast(aStatInfo.F_BSIZE); + }, +#if defined(USE_LINUX_QUOTACTL) + [](const struct dqblk& aQuotaInfo) { + // dqb_bhardlimit is count of BLOCK_SIZE blocks + return aQuotaInfo.dqb_bhardlimit * BLOCK_SIZE; + }, +#endif + aDiskCapacity); +} + NS_IMETHODIMP nsLocalFile::GetParent(nsIFile** aParent) { CHECK_mPath(); diff --git a/xpcom/io/nsLocalFileUnix.h b/xpcom/io/nsLocalFileUnix.h index 8c74f32bf105..6380fea92ec0 100644 --- a/xpcom/io/nsLocalFileUnix.h +++ b/xpcom/io/nsLocalFileUnix.h @@ -40,6 +40,10 @@ # define LSTAT lstat #endif +#if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H) +# define USE_LINUX_QUOTACTL +#endif + class nsLocalFile final #ifdef MOZ_WIDGET_COCOA : public nsILocalFileMac @@ -89,6 +93,15 @@ class nsLocalFile final nsresult SetLastModifiedTimeImpl(PRTime aLastModTime, bool aFollowLinks); nsresult GetLastModifiedTimeImpl(PRTime* aLastModTime, bool aFollowLinks); nsresult GetCreationTimeImpl(PRTime* aCreationTime, bool aFollowLinks); + +#if defined(USE_LINUX_QUOTACTL) + template + nsresult GetDiskInfo(StatInfoFunc&& aStatInfoFunc, + QuotaInfoFunc&& aQuotaInfoFunc, int64_t* aResult); +#else + template + nsresult GetDiskInfo(StatInfoFunc&& aStatInfoFunc, int64_t* aResult); +#endif }; #endif /* _nsLocalFileUNIX_H_ */ diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index 535725828327..677a454783a2 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -2676,6 +2676,12 @@ nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) { return NS_OK; } +NS_IMETHODIMP +nsLocalFile::GetDiskCapacity(int64_t* aDiskCapacity) { + *aDiskCapacity = 0; + return NS_OK; +} + NS_IMETHODIMP nsLocalFile::GetParent(nsIFile** aParent) { // Check we are correctly initialized. diff --git a/xpcom/tests/gtest/TestFile.cpp b/xpcom/tests/gtest/TestFile.cpp index 38c93fedd247..e5babebdc57f 100644 --- a/xpcom/tests/gtest/TestFile.cpp +++ b/xpcom/tests/gtest/TestFile.cpp @@ -392,6 +392,34 @@ static bool TestNormalizeNativePath(nsIFile* aBase, nsIFile* aStart) { return true; } +// Test nsIFile::GetDiskSpaceAvailable +static bool TestDiskSpaceAvailable(nsIFile* aBase) { + nsCOMPtr file = NewFile(aBase); + if (!file) return false; + + int64_t diskSpaceAvailable = 0; + nsresult rv = file->GetDiskSpaceAvailable(&diskSpaceAvailable); + VerifyResult(rv, "GetDiskSpaceAvailable"); + + EXPECT_GE(diskSpaceAvailable, 0); + + return true; +} + +// Test nsIFile::GetDiskCapacity +static bool TestDiskCapacity(nsIFile* aBase) { + nsCOMPtr file = NewFile(aBase); + if (!file) return false; + + int64_t diskCapacity = 0; + nsresult rv = file->GetDiskCapacity(&diskCapacity); + VerifyResult(rv, "GetDiskCapacity"); + + EXPECT_GE(diskCapacity, 0); + + return true; +} + static void SetupAndTestFunctions(const nsAString& aDirName, bool aTestCreateUnique, bool aTestNormalize) { nsCOMPtr base; @@ -484,6 +512,9 @@ static void SetupAndTestFunctions(const nsAString& aDirName, ASSERT_TRUE( TestDeleteOnClose(base, "file7.txt", PR_RDWR | PR_CREATE_FILE, 0600)); + ASSERT_TRUE(TestDiskSpaceAvailable(base)); + ASSERT_TRUE(TestDiskCapacity(base)); + // Clean up temporary stuff rv = base->Remove(true); VerifyResult(rv, "Cleaning up temp directory");