mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-16 04:07:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
324
hal/gonk/GonkDiskSpaceWatcher.cpp
Normal file
324
hal/gonk/GonkDiskSpaceWatcher.cpp
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Hal.h"
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include "base/message_loop.h"
|
||||
#include "base/task.h"
|
||||
#include "DiskSpaceWatcher.h"
|
||||
#include "fanotify.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIDiskSpaceWatcher.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla { namespace hal_impl { class GonkDiskSpaceWatcher; } }
|
||||
|
||||
using namespace mozilla::hal_impl;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
// NOTE: this should be unnecessary once we no longer support ICS.
|
||||
#ifndef __NR_fanotify_init
|
||||
#if defined(__ARM_EABI__)
|
||||
#define __NR_fanotify_init 367
|
||||
#define __NR_fanotify_mark 368
|
||||
#elif defined(__i386__)
|
||||
#define __NR_fanotify_init 338
|
||||
#define __NR_fanotify_mark 339
|
||||
#else
|
||||
#error "Unhandled architecture"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// fanotify_init and fanotify_mark functions are syscalls.
|
||||
// The user space bits are not part of bionic so we add them here
|
||||
// as well as fanotify.h
|
||||
int fanotify_init (unsigned int flags, unsigned int event_f_flags)
|
||||
{
|
||||
return syscall(__NR_fanotify_init, flags, event_f_flags);
|
||||
}
|
||||
|
||||
// Add, remove, or modify an fanotify mark on a filesystem object.
|
||||
int fanotify_mark (int fanotify_fd, unsigned int flags,
|
||||
uint64_t mask, int dfd, const char *pathname)
|
||||
{
|
||||
|
||||
// On 32 bits platforms we have to convert the 64 bits mask into
|
||||
// two 32 bits ints.
|
||||
if (sizeof(void *) == 4) {
|
||||
union {
|
||||
uint64_t _64;
|
||||
uint32_t _32[2];
|
||||
} _mask;
|
||||
_mask._64 = mask;
|
||||
return syscall(__NR_fanotify_mark, fanotify_fd, flags,
|
||||
_mask._32[0], _mask._32[1], dfd, pathname);
|
||||
}
|
||||
|
||||
return syscall(__NR_fanotify_mark, fanotify_fd, flags, mask, dfd, pathname);
|
||||
}
|
||||
|
||||
class GonkDiskSpaceWatcher final : public MessageLoopForIO::Watcher
|
||||
{
|
||||
public:
|
||||
GonkDiskSpaceWatcher();
|
||||
~GonkDiskSpaceWatcher() {};
|
||||
|
||||
virtual void OnFileCanReadWithoutBlocking(int aFd);
|
||||
|
||||
// We should never write to the fanotify fd.
|
||||
virtual void OnFileCanWriteWithoutBlocking(int aFd)
|
||||
{
|
||||
MOZ_CRASH("Must not write to fanotify fd");
|
||||
}
|
||||
|
||||
void DoStart();
|
||||
void DoStop();
|
||||
|
||||
private:
|
||||
void NotifyUpdate();
|
||||
|
||||
uint64_t mLowThreshold;
|
||||
uint64_t mHighThreshold;
|
||||
TimeDuration mTimeout;
|
||||
TimeStamp mLastTimestamp;
|
||||
uint64_t mLastFreeSpace;
|
||||
uint32_t mSizeDelta;
|
||||
|
||||
bool mIsDiskFull;
|
||||
uint64_t mFreeSpace;
|
||||
|
||||
int mFd;
|
||||
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
|
||||
};
|
||||
|
||||
static GonkDiskSpaceWatcher* gHalDiskSpaceWatcher = nullptr;
|
||||
|
||||
#define WATCHER_PREF_LOW "disk_space_watcher.low_threshold"
|
||||
#define WATCHER_PREF_HIGH "disk_space_watcher.high_threshold"
|
||||
#define WATCHER_PREF_TIMEOUT "disk_space_watcher.timeout"
|
||||
#define WATCHER_PREF_SIZE_DELTA "disk_space_watcher.size_delta"
|
||||
|
||||
static const char kWatchedPath[] = "/data";
|
||||
|
||||
// Helper class to dispatch calls to xpcom on the main thread.
|
||||
class DiskSpaceNotifier : public Runnable
|
||||
{
|
||||
public:
|
||||
DiskSpaceNotifier(const bool aIsDiskFull, const uint64_t aFreeSpace) :
|
||||
mIsDiskFull(aIsDiskFull),
|
||||
mFreeSpace(aFreeSpace) {}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DiskSpaceWatcher::UpdateState(mIsDiskFull, mFreeSpace);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mIsDiskFull;
|
||||
uint64_t mFreeSpace;
|
||||
};
|
||||
|
||||
// Helper runnable to delete the watcher on the main thread.
|
||||
class DiskSpaceCleaner : public Runnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (gHalDiskSpaceWatcher) {
|
||||
delete gHalDiskSpaceWatcher;
|
||||
gHalDiskSpaceWatcher = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
GonkDiskSpaceWatcher::GonkDiskSpaceWatcher() :
|
||||
mLastFreeSpace(UINT64_MAX),
|
||||
mIsDiskFull(false),
|
||||
mFreeSpace(UINT64_MAX),
|
||||
mFd(-1)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(gHalDiskSpaceWatcher == nullptr);
|
||||
|
||||
// Default values: 5MB for low threshold, 10MB for high threshold, and
|
||||
// a timeout of 5 seconds.
|
||||
mLowThreshold = Preferences::GetInt(WATCHER_PREF_LOW, 5) * 1024 * 1024;
|
||||
mHighThreshold = Preferences::GetInt(WATCHER_PREF_HIGH, 10) * 1024 * 1024;
|
||||
mTimeout = TimeDuration::FromSeconds(Preferences::GetInt(WATCHER_PREF_TIMEOUT, 5));
|
||||
mSizeDelta = Preferences::GetInt(WATCHER_PREF_SIZE_DELTA, 1) * 1024 * 1024;
|
||||
}
|
||||
|
||||
void
|
||||
GonkDiskSpaceWatcher::DoStart()
|
||||
{
|
||||
NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
|
||||
"Not on the correct message loop");
|
||||
|
||||
mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC | O_LARGEFILE);
|
||||
if (mFd == -1) {
|
||||
if (errno == ENOSYS) {
|
||||
// Don't change these printf_stderr since we need these logs even
|
||||
// in opt builds.
|
||||
printf_stderr("Warning: No fanotify support in this device's kernel.\n");
|
||||
#if ANDROID_VERSION >= 19
|
||||
MOZ_CRASH("Fanotify support must be enabled in the kernel.");
|
||||
#endif
|
||||
} else {
|
||||
printf_stderr("Error calling fanotify_init()");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (fanotify_mark(mFd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_CLOSE,
|
||||
0, kWatchedPath) < 0) {
|
||||
NS_WARNING("Error calling fanotify_mark");
|
||||
close(mFd);
|
||||
mFd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MessageLoopForIO::current()->WatchFileDescriptor(
|
||||
mFd, /* persistent = */ true,
|
||||
MessageLoopForIO::WATCH_READ,
|
||||
&mReadWatcher, gHalDiskSpaceWatcher)) {
|
||||
NS_WARNING("Unable to watch fanotify fd.");
|
||||
close(mFd);
|
||||
mFd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkDiskSpaceWatcher::DoStop()
|
||||
{
|
||||
NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
|
||||
"Not on the correct message loop");
|
||||
|
||||
if (mFd != -1) {
|
||||
mReadWatcher.StopWatchingFileDescriptor();
|
||||
fanotify_mark(mFd, FAN_MARK_FLUSH, 0, 0, kWatchedPath);
|
||||
close(mFd);
|
||||
mFd = -1;
|
||||
}
|
||||
|
||||
// Dispatch the cleanup to the main thread.
|
||||
nsCOMPtr<nsIRunnable> runnable = new DiskSpaceCleaner();
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
// We are called off the main thread, so we proxy first to the main thread
|
||||
// before calling the xpcom object.
|
||||
void
|
||||
GonkDiskSpaceWatcher::NotifyUpdate()
|
||||
{
|
||||
mLastTimestamp = TimeStamp::Now();
|
||||
mLastFreeSpace = mFreeSpace;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new DiskSpaceNotifier(mIsDiskFull, mFreeSpace);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
void
|
||||
GonkDiskSpaceWatcher::OnFileCanReadWithoutBlocking(int aFd)
|
||||
{
|
||||
struct fanotify_event_metadata* fem = nullptr;
|
||||
char buf[4096];
|
||||
struct statfs sfs;
|
||||
int32_t len, rc;
|
||||
|
||||
do {
|
||||
len = read(aFd, buf, sizeof(buf));
|
||||
} while(len == -1 && errno == EINTR);
|
||||
|
||||
// Bail out if the file is busy.
|
||||
if (len < 0 && errno == ETXTBSY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We should get an exact multiple of fanotify_event_metadata
|
||||
if (len <= 0 || (len % FAN_EVENT_METADATA_LEN != 0)) {
|
||||
MOZ_CRASH("About to crash: fanotify_event_metadata read error.");
|
||||
}
|
||||
|
||||
fem = reinterpret_cast<fanotify_event_metadata *>(buf);
|
||||
|
||||
while (FAN_EVENT_OK(fem, len)) {
|
||||
rc = fstatfs(fem->fd, &sfs);
|
||||
if (rc < 0) {
|
||||
NS_WARNING("Unable to stat fan_notify fd");
|
||||
} else {
|
||||
bool firstRun = mFreeSpace == UINT64_MAX;
|
||||
mFreeSpace = sfs.f_bavail * sfs.f_bsize;
|
||||
// We change from full <-> free depending on the free space and the
|
||||
// low and high thresholds.
|
||||
// Once we are in 'full' mode we send updates for all size changes with
|
||||
// a minimum of time between messages or when we cross a size change
|
||||
// threshold.
|
||||
if (firstRun) {
|
||||
mIsDiskFull = mFreeSpace <= mLowThreshold;
|
||||
// Always notify the current state at first run.
|
||||
NotifyUpdate();
|
||||
} else if (!mIsDiskFull && (mFreeSpace <= mLowThreshold)) {
|
||||
mIsDiskFull = true;
|
||||
NotifyUpdate();
|
||||
} else if (mIsDiskFull && (mFreeSpace > mHighThreshold)) {
|
||||
mIsDiskFull = false;
|
||||
NotifyUpdate();
|
||||
} else if (mIsDiskFull) {
|
||||
if (mTimeout < TimeStamp::Now() - mLastTimestamp ||
|
||||
mSizeDelta < llabs(mFreeSpace - mLastFreeSpace)) {
|
||||
NotifyUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
close(fem->fd);
|
||||
fem = FAN_EVENT_NEXT(fem, len);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StartDiskSpaceWatcher()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Bail out if called several times.
|
||||
if (gHalDiskSpaceWatcher != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
gHalDiskSpaceWatcher = new GonkDiskSpaceWatcher();
|
||||
|
||||
XRE_GetIOMessageLoop()->PostTask(
|
||||
NewNonOwningRunnableMethod(gHalDiskSpaceWatcher, &GonkDiskSpaceWatcher::DoStart));
|
||||
}
|
||||
|
||||
void
|
||||
StopDiskSpaceWatcher()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!gHalDiskSpaceWatcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
XRE_GetIOMessageLoop()->PostTask(
|
||||
NewNonOwningRunnableMethod(gHalDiskSpaceWatcher, &GonkDiskSpaceWatcher::DoStop));
|
||||
}
|
||||
|
||||
} // namespace hal_impl
|
||||
} // namespace mozilla
|
||||
Loading…
Add table
Add a link
Reference in a new issue