Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/cache/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 28 kB image not shown  

Quelle  FileUtils.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "FileUtilsImpl.h"

#include "CacheCipherKeyManager.h"
#include "DBSchema.h"
#include "mozilla/dom/InternalResponse.h"
#include "mozilla/dom/quota/DecryptingInputStream.h"
#include "mozilla/dom/quota/DecryptingInputStream_impl.h"
#include "mozilla/dom/quota/EncryptingOutputStream.h"
#include "mozilla/dom/quota/EncryptingOutputStream_impl.h"
#include "mozilla/dom/quota/FileStreams.h"
#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/quota/QuotaObject.h"
#include "mozilla/dom/quota/ResultExtensions.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/SnappyCompressOutputStream.h"
#include "mozilla/Unused.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsIFile.h"
#include "nsIUUIDGenerator.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "snappy/snappy.h"

namespace mozilla::dom::cache {

static_assert(SNAPPY_VERSION == 0x010200);

using mozilla::dom::quota::Client;
using mozilla::dom::quota::CloneFileAndAppend;
using mozilla::dom::quota::GetDirEntryKind;
using mozilla::dom::quota::nsIFileKind;
using mozilla::dom::quota::QuotaManager;
using mozilla::dom::quota::QuotaObject;

namespace {

// Const variable for generate padding size.
// XXX This will be tweaked to something more meaningful in Bug 1383656.
const int64_t kRoundUpNumber = 20480;

// At the moment, the encrypted stream block size is assumed to be unchangeable
// between encrypting and decrypting blobs. This assumptions holds as long as we
// only encrypt in private browsing mode, but when we support encryption for
// persistent storage, this needs to be changed.
constexpr uint32_t kEncryptedStreamBlockSize = 4096;

enum BodyFileType { BODY_FILE_FINAL, BODY_FILE_TMP };

Result<NotNull<nsCOMPtr<nsIFile>>, nsresult> BodyIdToFile(nsIFile& aBaseDir,
                                                          const nsID& aId,
                                                          BodyFileType aType);

int64_t RoundUp(int64_t aX, int64_t aY);

// The alogrithm for generating padding refers to the mitigation approach in
// https://github.com/whatwg/storage/issues/31.
// First, generate a random number between 0 and 100kB.
// Next, round up the sum of random number and response size to the nearest
// 20kB.
// Finally, the virtual padding size will be the result minus the response size.
int64_t BodyGeneratePadding(int64_t aBodyFileSize, uint32_t aPaddingInfo);

nsresult DirectoryPaddingWrite(nsIFile& aBaseDir,
                               DirPaddingFile aPaddingFileType,
                               int64_t aPaddingSize);

const auto kMorgueDirectory = u"morgue"_ns;

bool IsFileNotFoundError(const nsresult aRv) {
  return aRv == NS_ERROR_FILE_NOT_FOUND;
}

Result<NotNull<nsCOMPtr<nsIFile>>, nsresult> BodyGetCacheDir(nsIFile& aBaseDir,
                                                             const nsID& aId) {
  QM_TRY_UNWRAP(auto cacheDir, CloneFileAndAppend(aBaseDir, kMorgueDirectory));

  // Some file systems have poor performance when there are too many files
  // in a single directory.  Mitigate this issue by spreading the body
  // files out into sub-directories.  We use the last byte of the ID for
  // the name of the sub-directory.
  QM_TRY(MOZ_TO_RESULT(cacheDir->Append(IntToString(aId.m3[7]))));

  // Callers call this function without checking if the directory already
  // exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we
  // just want to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the
  // reports.
  QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
      // Expression.
      MOZ_TO_RESULT(cacheDir->Create(nsIFile::DIRECTORY_TYPE, 0755)),
      // Predicate.
      IsSpecificError<NS_ERROR_FILE_ALREADY_EXISTS>,
      // Fallback.
      ErrToDefaultOk<>));

  return WrapNotNullUnchecked(std::move(cacheDir));
}

}  // namespace

nsresult BodyCreateDir(nsIFile& aBaseDir) {
  QM_TRY_INSPECT(const auto& bodyDir,
                 CloneFileAndAppend(aBaseDir, kMorgueDirectory));

  // Callers call this function without checking if the directory already
  // exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we
  // just want to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the
  // reports.
  QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
      // Expression.
      MOZ_TO_RESULT(bodyDir->Create(nsIFile::DIRECTORY_TYPE, 0755)),
      // Predicate.
      IsSpecificError<NS_ERROR_FILE_ALREADY_EXISTS>,
      // Fallback.
      ErrToDefaultOk<>));

  return NS_OK;
}

nsresult BodyDeleteDir(const CacheDirectoryMetadata& aDirectoryMetadata,
                       nsIFile& aBaseDir) {
  QM_TRY_INSPECT(const auto& bodyDir,
                 CloneFileAndAppend(aBaseDir, kMorgueDirectory));

  QM_TRY(MOZ_TO_RESULT(RemoveNsIFileRecursively(aDirectoryMetadata, *bodyDir)));

  return NS_OK;
}

Result<nsCOMPtr<nsISupports>, nsresult> BodyStartWriteStream(
    const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
    const nsID& aBodyId, Maybe<CipherKey> aMaybeCipherKey,
    nsIInputStream& aSource, void* aClosure, nsAsyncCopyCallbackFun aCallback) {
  MOZ_DIAGNOSTIC_ASSERT(aClosure);
  MOZ_DIAGNOSTIC_ASSERT(aCallback);

  QM_TRY_INSPECT(const auto& finalFile,
                 BodyIdToFile(aBaseDir, aBodyId, BODY_FILE_FINAL));

  {
    QM_TRY_INSPECT(const bool& exists,
                   MOZ_TO_RESULT_INVOKE_MEMBER(*finalFile, Exists));

    QM_TRY(OkIf(!exists), Err(NS_ERROR_FILE_ALREADY_EXISTS));
  }

  QM_TRY_INSPECT(const auto& tmpFile,
                 BodyIdToFile(aBaseDir, aBodyId, BODY_FILE_TMP));

  QM_TRY_UNWRAP(nsCOMPtr<nsIOutputStream> fileStream,
                CreateFileOutputStream(aDirectoryMetadata.mPersistenceType,
                                       aDirectoryMetadata, Client::DOMCACHE,
                                       tmpFile.get()));

  const auto privateBody = aDirectoryMetadata.mIsPrivate;
  if (privateBody) {
    MOZ_DIAGNOSTIC_ASSERT(aMaybeCipherKey);

    fileStream = MakeRefPtr<quota::EncryptingOutputStream<CipherStrategy>>(
        std::move(fileStream), kEncryptedStreamBlockSize, *aMaybeCipherKey);
  }

  const auto compressed = MakeRefPtr<SnappyCompressOutputStream>(fileStream);

  const nsCOMPtr<nsIEventTarget> target =
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);

  nsCOMPtr<nsISupports> copyContext;
  QM_TRY(MOZ_TO_RESULT(
      NS_AsyncCopy(&aSource, compressed, target, NS_ASYNCCOPY_VIA_WRITESEGMENTS,
                   compressed->BlockSize(), aCallback, aClosure, true,
                   true,  // close streams
                   getter_AddRefs(copyContext))));

  return std::move(copyContext);
}

void BodyCancelWrite(nsISupports& aCopyContext) {
  QM_WARNONLY_TRY(
      QM_TO_RESULT(NS_CancelAsyncCopy(&aCopyContext, NS_ERROR_ABORT)));

  // TODO The partially written file must be cleaned up after the async copy
  // makes its callback.
}

Result<int64_t, nsresult> BodyFinalizeWrite(nsIFile& aBaseDir,
                                            const nsID& aId) {
  QM_TRY_INSPECT(const auto& tmpFile,
                 BodyIdToFile(aBaseDir, aId, BODY_FILE_TMP));

  QM_TRY_INSPECT(const auto& finalFile,
                 BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL));

  nsAutoString finalFileName;
  QM_TRY(MOZ_TO_RESULT(finalFile->GetLeafName(finalFileName)));

  // It's fine to not notify the QuotaManager that the path has been changed,
  // because its path will be updated and its size will be recalculated when
  // opening file next time.
  QM_TRY(MOZ_TO_RESULT(tmpFile->RenameTo(nullptr, finalFileName)));

  QM_TRY_INSPECT(const int64_t& fileSize,
                 MOZ_TO_RESULT_INVOKE_MEMBER(*finalFile, GetFileSize));

  return fileSize;
}

Result<int64_t, nsresult> GetBodyDiskSize(nsIFile& aBaseDir, const nsID& aId) {
  QM_TRY_INSPECT(const auto& finalFile,
                 BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL));

  QM_TRY_INSPECT(const int64_t& fileSize,
                 MOZ_TO_RESULT_INVOKE_MEMBER(*finalFile, GetFileSize));

  return fileSize;
}

Result<MovingNotNull<nsCOMPtr<nsIInputStream>>, nsresult> BodyOpen(
    const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
    const nsID& aId, Maybe<CipherKey> aMaybeCipherKey) {
  QM_TRY_INSPECT(const auto& finalFile,
                 BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL));

  QM_TRY_UNWRAP(nsCOMPtr<nsIInputStream> fileInputStream,
                CreateFileInputStream(aDirectoryMetadata.mPersistenceType,
                                      aDirectoryMetadata, Client::DOMCACHE,
                                      finalFile.get()));

  auto privateBody = aDirectoryMetadata.mIsPrivate;
  if (privateBody) {
    MOZ_DIAGNOSTIC_ASSERT(aMaybeCipherKey);

    fileInputStream = new quota::DecryptingInputStream<CipherStrategy>(
        WrapNotNull(std::move(fileInputStream)), kEncryptedStreamBlockSize,
        *aMaybeCipherKey);
  }
  return WrapMovingNotNull(std::move(fileInputStream));
}

nsresult BodyMaybeUpdatePaddingSize(
    const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
    const nsID& aId, const uint32_t aPaddingInfo, int64_t* aPaddingSizeInOut) {
  MOZ_DIAGNOSTIC_ASSERT(aPaddingSizeInOut);

  QM_TRY_INSPECT(const auto& bodyFile,
                 BodyIdToFile(aBaseDir, aId, BODY_FILE_TMP));

  QuotaManager* quotaManager = QuotaManager::Get();
  MOZ_DIAGNOSTIC_ASSERT(quotaManager);

  int64_t fileSize = 0;
  RefPtr<QuotaObject> quotaObject = quotaManager->GetQuotaObject(
      aDirectoryMetadata.mPersistenceType, aDirectoryMetadata, Client::DOMCACHE,
      bodyFile.get(), -1, &fileSize);
  MOZ_DIAGNOSTIC_ASSERT(quotaObject);
  MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
  // XXXtt: bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1422815
  if (!quotaObject) {
    return NS_ERROR_UNEXPECTED;
  }

  if (*aPaddingSizeInOut == InternalResponse::UNKNOWN_PADDING_SIZE) {
    *aPaddingSizeInOut = BodyGeneratePadding(fileSize, aPaddingInfo);
  }

  MOZ_DIAGNOSTIC_ASSERT(*aPaddingSizeInOut >= 0);

  if (!quotaObject->IncreaseSize(*aPaddingSizeInOut)) {
    return NS_ERROR_FILE_NO_DEVICE_SPACE;
  }

  return NS_OK;
}

nsresult BodyDeleteFiles(const CacheDirectoryMetadata& aDirectoryMetadata,
                         nsIFile& aBaseDir, const nsTArray<nsID>& aIdList) {
  for (const auto id : aIdList) {
    QM_TRY_INSPECT(const auto& bodyDir, BodyGetCacheDir(aBaseDir, id));

    const auto removeFileForId =
        [&aDirectoryMetadata, &id](
            nsIFile& bodyFile,
            const nsACString& leafName) -> Result<bool, nsresult> {
      nsID fileId{};
      QM_TRY(OkIf(fileId.Parse(leafName.BeginReading())), true,
             ([&aDirectoryMetadata, &bodyFile](const auto) {
               DebugOnly<nsresult> result = RemoveNsIFile(
                   aDirectoryMetadata, bodyFile, /* aTrackQuota */ false);
               MOZ_ASSERT(NS_SUCCEEDED(result));
             }));

      if (id.Equals(fileId)) {
        DebugOnly<nsresult> result =
            RemoveNsIFile(aDirectoryMetadata, bodyFile);
        MOZ_ASSERT(NS_SUCCEEDED(result));
        return true;
      }

      return false;
    };
    QM_TRY(MOZ_TO_RESULT(BodyTraverseFiles(aDirectoryMetadata, *bodyDir,
                                           removeFileForId,
                                           /* aCanRemoveFiles */ false,
                                           /* aTrackQuota */ true)));
  }

  return NS_OK;
}

namespace {

Result<NotNull<nsCOMPtr<nsIFile>>, nsresult> BodyIdToFile(
    nsIFile& aBaseDir, const nsID& aId, const BodyFileType aType) {
  QM_TRY_UNWRAP(auto bodyFile, BodyGetCacheDir(aBaseDir, aId));

  char idString[NSID_LENGTH];
  aId.ToProvidedString(idString);

  NS_ConvertASCIItoUTF16 fileName(idString);

  if (aType == BODY_FILE_FINAL) {
    fileName.AppendLiteral(".final");
  } else {
    fileName.AppendLiteral(".tmp");
  }

  QM_TRY(MOZ_TO_RESULT(bodyFile->Append(fileName)));

  return bodyFile;
}

int64_t RoundUp(const int64_t aX, const int64_t aY) {
  MOZ_DIAGNOSTIC_ASSERT(aX >= 0);
  MOZ_DIAGNOSTIC_ASSERT(aY > 0);

  MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - ((aX - 1) / aY) * aY >= aY);
  return aY + ((aX - 1) / aY) * aY;
}

int64_t BodyGeneratePadding(const int64_t aBodyFileSize,
                            const uint32_t aPaddingInfo) {
  // Generate padding
  int64_t randomSize = static_cast<int64_t>(aPaddingInfo);
  MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - aBodyFileSize >= randomSize);
  randomSize += aBodyFileSize;

  return RoundUp(randomSize, kRoundUpNumber) - aBodyFileSize;
}

nsresult DirectoryPaddingWrite(nsIFile& aBaseDir,
                               DirPaddingFile aPaddingFileType,
                               int64_t aPaddingSize) {
  MOZ_DIAGNOSTIC_ASSERT(aPaddingSize >= 0);

  QM_TRY_INSPECT(
      const auto& file,
      CloneFileAndAppend(aBaseDir, aPaddingFileType == DirPaddingFile::TMP_FILE
                                       ? nsLiteralString(PADDING_TMP_FILE_NAME)
                                       : nsLiteralString(PADDING_FILE_NAME)));

  QM_TRY_INSPECT(const auto& outputStream, NS_NewLocalFileOutputStream(file));

  nsCOMPtr<nsIObjectOutputStream> objectStream =
      NS_NewObjectOutputStream(outputStream);

  QM_TRY(MOZ_TO_RESULT(objectStream->Write64(aPaddingSize)));

  return NS_OK;
}

}  // namespace

nsresult BodyDeleteOrphanedFiles(
    const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
    const nsTArray<nsID>& aKnownBodyIdList) {
  // body files are stored in a directory structure like:
  //
  //  /morgue/01/{01fdddb2-884d-4c3d-95ba-0c8062f6c325}.final
  //  /morgue/02/{02fdddb2-884d-4c3d-95ba-0c8062f6c325}.tmp

  QM_TRY_INSPECT(const auto& dir,
                 CloneFileAndAppend(aBaseDir, kMorgueDirectory));

  // Iterate over all the intermediate morgue subdirs
  QM_TRY(quota::CollectEachFile(
      *dir,
      [&aDirectoryMetadata, &aKnownBodyIdList](
          const nsCOMPtr<nsIFile>& subdir) -> Result<Ok, nsresult> {
        QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*subdir));

        switch (dirEntryKind) {
          case nsIFileKind::ExistsAsDirectory: {
            const auto removeOrphanedFiles =
                [&aDirectoryMetadata, &aKnownBodyIdList](
                    nsIFile& bodyFile,
                    const nsACString& leafName) -> Result<bool, nsresult> {
              // Finally, parse the uuid out of the name.  If it fails to parse,
              // then ignore the file.
              auto cleanup = MakeScopeExit([&aDirectoryMetadata, &bodyFile] {
                DebugOnly<nsresult> result =
                    RemoveNsIFile(aDirectoryMetadata, bodyFile);
                MOZ_ASSERT(NS_SUCCEEDED(result));
              });

              nsID id;
              QM_TRY(OkIf(id.Parse(leafName.BeginReading())), true);

              if (!aKnownBodyIdList.Contains(id)) {
                return true;
              }

              cleanup.release();

              return false;
            };

            // QM_OR_ELSE_WARN_IF is not used here since we just want to log
            // NS_ERROR_FILE_FS_CORRUPTED result and not spam the reports (even
            // a warning in the reports is not desired).
            QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
                // Expression.
                MOZ_TO_RESULT(BodyTraverseFiles(aDirectoryMetadata, *subdir,
                                                removeOrphanedFiles,
                                                /* aCanRemoveFiles */ true,
                                                /* aTrackQuota */ true)),
                // Predicate.
                IsSpecificError<NS_ERROR_FILE_FS_CORRUPTED>,
                // Fallback. We treat NS_ERROR_FILE_FS_CORRUPTED as if the
                // directory did not exist at all.
                ErrToDefaultOk<>));
            break;
          }

          case nsIFileKind::ExistsAsFile: {
            // If a file got in here somehow, try to remove it and move on
            DebugOnly<nsresult> result =
                RemoveNsIFile(aDirectoryMetadata, *subdir,
                              /* aTrackQuota */ false);
            MOZ_ASSERT(NS_SUCCEEDED(result));
            break;
          }

          case nsIFileKind::DoesNotExist:
            // Ignore files that got removed externally while iterating.
            break;
        }

        return Ok{};
      }));

  return NS_OK;
}

namespace {

Result<nsCOMPtr<nsIFile>, nsresult> GetMarkerFileHandle(
    const CacheDirectoryMetadata& aDirectoryMetadata) {
  QM_TRY_UNWRAP(auto marker,
                CloneFileAndAppend(*aDirectoryMetadata.mDir, u"cache"_ns));

  QM_TRY(MOZ_TO_RESULT(marker->Append(u"context_open.marker"_ns)));

  return marker;
}

}  // namespace

nsresult CreateMarkerFile(const CacheDirectoryMetadata& aDirectoryMetadata) {
  QM_TRY_INSPECT(const auto& marker, GetMarkerFileHandle(aDirectoryMetadata));

  // Callers call this function without checking if the file already exists
  // (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we just want
  // to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the reports.
  //
  // TODO: In theory if this file exists, then Context::~Context should have
  // cleaned it up, but obviously we can crash and not clean it up, which is
  // the whole point of the marker file. In that case, we'll realize the marker
  // file exists in SetupAction::RunSyncWithDBOnTarget and do some cleanup, but
  // we won't delete the marker file, so if we see this marker file, it is part
  // of our standard operating procedure to redundantly try and create the
  // marker here. We currently treat this as idempotent usage, but we could
  // make sure to delete the marker file when handling the existing marker
  // file in SetupAction::RunSyncWithDBOnTarget and change
  // QM_OR_ELSE_LOG_VERBOSE_IF to QM_OR_ELSE_WARN_IF in the end.
  QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
      // Expression.
      MOZ_TO_RESULT(marker->Create(nsIFile::NORMAL_FILE_TYPE, 0644)),
      // Predicate.
      IsSpecificError<NS_ERROR_FILE_ALREADY_EXISTS>,
      // Fallback.
      ErrToDefaultOk<>));

  // Note, we don't need to fsync here.  We only care about actually
  // writing the marker if later modifications to the Cache are
  // actually flushed to the disk.  If the OS crashes before the marker
  // is written then we are ensured no other changes to the Cache were
  // flushed either.

  return NS_OK;
}

nsresult DeleteMarkerFile(const CacheDirectoryMetadata& aDirectoryMetadata) {
  QM_TRY_INSPECT(const auto& marker, GetMarkerFileHandle(aDirectoryMetadata));

  DebugOnly<nsresult> result =
      RemoveNsIFile(aDirectoryMetadata, *marker, /* aTrackQuota */ false);
  MOZ_ASSERT(NS_SUCCEEDED(result));

  // Again, no fsync is necessary.  If the OS crashes before the file
  // removal is flushed, then the Cache will search for stale data on
  // startup.  This will cause the next Cache access to be a bit slow, but
  // it seems appropriate after an OS crash.

  return NS_OK;
}

bool MarkerFileExists(const CacheDirectoryMetadata& aDirectoryMetadata) {
  QM_TRY_INSPECT(const auto& marker, GetMarkerFileHandle(aDirectoryMetadata),
                 false);

  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(marker, Exists), false);
}

nsresult RemoveNsIFileRecursively(
    const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata, nsIFile& aFile,
    const bool aTrackQuota) {
  // XXX This assertion proves that we can remove aTrackQuota and just check
  // aClientMetadata
  MOZ_DIAGNOSTIC_ASSERT_IF(aTrackQuota, aDirectoryMetadata);

  QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(aFile));

  switch (dirEntryKind) {
    case nsIFileKind::ExistsAsDirectory:
      // Unfortunately, we need to traverse all the entries and delete files one
      // by
      // one to update their usages to the QuotaManager.
      QM_TRY(quota::CollectEachFile(
          aFile,
          [&aDirectoryMetadata, &aTrackQuota](
              const nsCOMPtr<nsIFile>& file) -> Result<Ok, nsresult> {
            QM_TRY(MOZ_TO_RESULT(RemoveNsIFileRecursively(aDirectoryMetadata,
                                                          *file, aTrackQuota)));

            return Ok{};
          }));

      // In the end, remove the folder
      QM_TRY(MOZ_TO_RESULT(aFile.Remove(/* recursive */ false)));

      break;

    case nsIFileKind::ExistsAsFile:
      return RemoveNsIFile(aDirectoryMetadata, aFile, aTrackQuota);

    case nsIFileKind::DoesNotExist:
      // Ignore files that got removed externally while iterating.
      break;
  }

  return NS_OK;
}

nsresult RemoveNsIFile(const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
                       nsIFile& aFile, const bool aTrackQuota) {
  // XXX This assertion proves that we can remove aTrackQuota and just check
  // aClientMetadata
  MOZ_DIAGNOSTIC_ASSERT_IF(aTrackQuota, aDirectoryMetadata);

  int64_t fileSize = 0;
  if (aTrackQuota) {
    QM_TRY_INSPECT(
        const auto& maybeFileSize,
        QM_OR_ELSE_WARN_IF(
            // Expression.
            MOZ_TO_RESULT_INVOKE_MEMBER(aFile, GetFileSize).map(Some<int64_t>),
            // Predicate.
            IsFileNotFoundError,
            // Fallback.
            ErrToDefaultOk<Maybe<int64_t>>));

    if (!maybeFileSize) {
      return NS_OK;
    }

    fileSize = *maybeFileSize;
  }

  QM_TRY(QM_OR_ELSE_WARN_IF(
      // Expression.
      MOZ_TO_RESULT(aFile.Remove(/* recursive */ false)),
      // Predicate.
      IsFileNotFoundError,
      // Fallback.
      ErrToDefaultOk<>));

  if (fileSize > 0) {
    MOZ_ASSERT(aTrackQuota);
    DecreaseUsageForDirectoryMetadata(*aDirectoryMetadata, fileSize);
  }

  return NS_OK;
}

void DecreaseUsageForDirectoryMetadata(
    const CacheDirectoryMetadata& aDirectoryMetadata,
    const int64_t aUpdatingSize) {
  MOZ_DIAGNOSTIC_ASSERT(aUpdatingSize > 0);

  QuotaManager* quotaManager = QuotaManager::Get();
  MOZ_DIAGNOSTIC_ASSERT(quotaManager);

  quotaManager->DecreaseUsageForClient(
      quota::ClientMetadata{aDirectoryMetadata, Client::DOMCACHE},
      aUpdatingSize);
}

bool DirectoryPaddingFileExists(nsIFile& aBaseDir,
                                DirPaddingFile aPaddingFileType) {
  QM_TRY_INSPECT(
      const auto& file,
      CloneFileAndAppend(aBaseDir, aPaddingFileType == DirPaddingFile::TMP_FILE
                                       ? nsLiteralString(PADDING_TMP_FILE_NAME)
                                       : nsLiteralString(PADDING_FILE_NAME)),
      false);

  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists), false);
}

Result<int64_t, nsresult> DirectoryPaddingGet(nsIFile& aBaseDir) {
  MOZ_DIAGNOSTIC_ASSERT(
      !DirectoryPaddingFileExists(aBaseDir, DirPaddingFile::TMP_FILE));

  QM_TRY_INSPECT(
      const auto& file,
      CloneFileAndAppend(aBaseDir, nsLiteralString(PADDING_FILE_NAME)));

  QM_TRY_UNWRAP(auto stream, NS_NewLocalFileInputStream(file));

  QM_TRY_INSPECT(const auto& bufferedStream,
                 NS_NewBufferedInputStream(stream.forget(), 512));

  const nsCOMPtr<nsIObjectInputStream> objectStream =
      NS_NewObjectInputStream(bufferedStream);

  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(objectStream, Read64)
                    .map([](const uint64_t val) { return int64_t(val); }));
}

nsresult DirectoryPaddingInit(nsIFile& aBaseDir) {
  QM_TRY(
      MOZ_TO_RESULT(DirectoryPaddingWrite(aBaseDir, DirPaddingFile::FILE, 0)));

  return NS_OK;
}

nsresult UpdateDirectoryPaddingFile(nsIFile& aBaseDir,
                                    mozIStorageConnection& aConn,
                                    const int64_t aIncreaseSize,
                                    const int64_t aDecreaseSize,
                                    const bool aTemporaryFileExist) {
  MOZ_DIAGNOSTIC_ASSERT(aIncreaseSize >= 0);
  MOZ_DIAGNOSTIC_ASSERT(aDecreaseSize >= 0);

  const auto directoryPaddingGetResult =
      aTemporaryFileExist ? Maybe<int64_t>{} : [&aBaseDir] {
        QM_TRY_RETURN(QM_OR_ELSE_WARN_IF(
                          // Expression.
                          DirectoryPaddingGet(aBaseDir).map(Some<int64_t>),
                          // Predicate.
                          IsFileNotFoundError,
                          // Fallback.
                          ErrToDefaultOk<Maybe<int64_t>>),
                      Maybe<int64_t>{});
      }();

  QM_TRY_INSPECT(
      const int64_t& currentPaddingSize,
      ([directoryPaddingGetResult, &aBaseDir, &aConn, aIncreaseSize,
        aDecreaseSize]() -> Result<int64_t, nsresult> {
        if (!directoryPaddingGetResult) {
          // Fail to read padding size from the dir padding file, so try to
          // restore.

          // Not delete the temporary padding file here, because we're going
          // to overwrite it below anyway.
          QM_TRY(MOZ_TO_RESULT(
              DirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE)));

          // We don't need to add the aIncreaseSize or aDecreaseSize here,
          // because it's already encompassed within the database.
          QM_TRY_RETURN(db::FindOverallPaddingSize(aConn));
        }

        int64_t currentPaddingSize = directoryPaddingGetResult.value();
        bool shouldRevise = false;

        if (aIncreaseSize > 0) {
          if (INT64_MAX - currentPaddingSize < aDecreaseSize) {
            shouldRevise = true;
          } else {
            currentPaddingSize += aIncreaseSize;
          }
        }

        if (aDecreaseSize > 0) {
          if (currentPaddingSize < aDecreaseSize) {
            shouldRevise = true;
          } else if (!shouldRevise) {
            currentPaddingSize -= aDecreaseSize;
          }
        }

        if (shouldRevise) {
          // If somehow runing into this condition, the tracking padding size is
          // incorrect.
          // Delete padding file to indicate the padding size is incorrect for
          // avoiding error happening in the following lines.
          QM_TRY(MOZ_TO_RESULT(
              DirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE)));

          QM_TRY_UNWRAP(currentPaddingSize, db::FindOverallPaddingSize(aConn));

          // XXXtt: we should have an easy way to update (increase or
          // recalulate) padding size in the QM. For now, only correct the
          // padding size in padding file and make QM be able to get the correct
          // size in the next QM initialization. We still want to catch this in
          // the debug build.
          MOZ_ASSERT(false"The padding size is unsync with QM");
        }

#ifdef DEBUG
        const int64_t lastPaddingSize = currentPaddingSize;
        QM_TRY_UNWRAP(currentPaddingSize, db::FindOverallPaddingSize(aConn));

        MOZ_DIAGNOSTIC_ASSERT(currentPaddingSize == lastPaddingSize);
#endif  // DEBUG

        return currentPaddingSize;
      }()));

  MOZ_DIAGNOSTIC_ASSERT(currentPaddingSize >= 0);

  QM_TRY(MOZ_TO_RESULT(DirectoryPaddingWrite(aBaseDir, DirPaddingFile::TMP_FILE,
                                             currentPaddingSize)));

  return NS_OK;
}

nsresult DirectoryPaddingFinalizeWrite(nsIFile& aBaseDir) {
  MOZ_DIAGNOSTIC_ASSERT(
      DirectoryPaddingFileExists(aBaseDir, DirPaddingFile::TMP_FILE));

  QM_TRY_INSPECT(
      const auto& file,
      CloneFileAndAppend(aBaseDir, nsLiteralString(PADDING_TMP_FILE_NAME)));

  QM_TRY(MOZ_TO_RESULT(
      file->RenameTo(nullptr, nsLiteralString(PADDING_FILE_NAME))));

  return NS_OK;
}

Result<int64_t, nsresult> DirectoryPaddingRestore(nsIFile& aBaseDir,
                                                  mozIStorageConnection& aConn,
                                                  const bool aMustRestore) {
  // The content of padding file is untrusted, so remove it here.
  QM_TRY(MOZ_TO_RESULT(
      DirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE)));

  QM_TRY_INSPECT(const int64_t& paddingSize, db::FindOverallPaddingSize(aConn));
  MOZ_DIAGNOSTIC_ASSERT(paddingSize >= 0);

  QM_TRY(MOZ_TO_RESULT(DirectoryPaddingWrite(aBaseDir, DirPaddingFile::FILE,
                                             paddingSize)),
         (aMustRestore ? Err(tryTempError)
                       : Result<int64_t, nsresult>{paddingSize}));

  QM_TRY(MOZ_TO_RESULT(
      DirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::TMP_FILE)));

  return paddingSize;
}

nsresult DirectoryPaddingDeleteFile(nsIFile& aBaseDir,
                                    DirPaddingFile aPaddingFileType) {
  QM_TRY_INSPECT(
      const auto& file,
      CloneFileAndAppend(aBaseDir, aPaddingFileType == DirPaddingFile::TMP_FILE
                                       ? nsLiteralString(PADDING_TMP_FILE_NAME)
                                       : nsLiteralString(PADDING_FILE_NAME)));

  QM_TRY(QM_OR_ELSE_WARN_IF(
      // Expression.
      MOZ_TO_RESULT(file->Remove(/* recursive */ false)),
      // Predicate.
      IsFileNotFoundError,
      // Fallback.
      ErrToDefaultOk<>));

  return NS_OK;
}

}  // namespace mozilla::dom::cache

100%


¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.