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
This commit is contained in:
Jan Varga 2021-12-08 14:41:38 +00:00
parent 05f4d5a4aa
commit 1a3d2b084c
6 changed files with 130 additions and 22 deletions

View File

@ -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; }

View File

@ -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
*

View File

@ -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 <sys/xattr.h>
#endif
#if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
# define USE_LINUX_QUOTACTL
#if defined(USE_LINUX_QUOTACTL)
# include <sys/mount.h>
# include <sys/quota.h>
# include <sys/sysmacros.h>
@ -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 <typename StatInfoFunc, typename QuotaInfoFunc>
nsresult nsLocalFile::GetDiskInfo(StatInfoFunc&& aStatInfoFunc,
QuotaInfoFunc&& aQuotaInfoFunc,
int64_t* aResult)
#else
template <typename StatInfoFunc>
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<StatInfoFunc>(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<QuotaInfoFunc>(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<uint64_t>(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<uint64_t>(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();

View File

@ -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 <typename StatInfoFunc, typename QuotaInfoFunc>
nsresult GetDiskInfo(StatInfoFunc&& aStatInfoFunc,
QuotaInfoFunc&& aQuotaInfoFunc, int64_t* aResult);
#else
template <typename StatInfoFunc>
nsresult GetDiskInfo(StatInfoFunc&& aStatInfoFunc, int64_t* aResult);
#endif
};
#endif /* _nsLocalFileUNIX_H_ */

View File

@ -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.

View File

@ -392,6 +392,34 @@ static bool TestNormalizeNativePath(nsIFile* aBase, nsIFile* aStart) {
return true;
}
// Test nsIFile::GetDiskSpaceAvailable
static bool TestDiskSpaceAvailable(nsIFile* aBase) {
nsCOMPtr<nsIFile> 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<nsIFile> 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<nsIFile> 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");