tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FileSystemManagerParent.cpp (17503B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "FileSystemManagerParent.h"
      8 
      9 #include "FileSystemDatabaseManager.h"
     10 #include "FileSystemParentTypes.h"
     11 #include "mozilla/Maybe.h"
     12 #include "mozilla/dom/FileBlobImpl.h"
     13 #include "mozilla/dom/FileSystemAccessHandle.h"
     14 #include "mozilla/dom/FileSystemAccessHandleControlParent.h"
     15 #include "mozilla/dom/FileSystemAccessHandleParent.h"
     16 #include "mozilla/dom/FileSystemDataManager.h"
     17 #include "mozilla/dom/FileSystemLog.h"
     18 #include "mozilla/dom/FileSystemTypes.h"
     19 #include "mozilla/dom/FileSystemWritableFileStreamParent.h"
     20 #include "mozilla/dom/IPCBlobUtils.h"
     21 #include "mozilla/dom/QMResult.h"
     22 #include "mozilla/dom/quota/FileStreams.h"
     23 #include "mozilla/dom/quota/ForwardDecls.h"
     24 #include "mozilla/dom/quota/QuotaCommon.h"
     25 #include "mozilla/dom/quota/ResultExtensions.h"
     26 #include "mozilla/ipc/BackgroundParent.h"
     27 #include "mozilla/ipc/FileDescriptorUtils.h"
     28 #include "mozilla/ipc/RandomAccessStreamUtils.h"
     29 #include "nsNetUtil.h"
     30 #include "nsString.h"
     31 #include "nsTArray.h"
     32 
     33 using IPCResult = mozilla::ipc::IPCResult;
     34 
     35 namespace mozilla::dom {
     36 
     37 FileSystemManagerParent::FileSystemManagerParent(
     38    RefPtr<fs::data::FileSystemDataManager> aDataManager,
     39    const EntryId& aRootEntry)
     40    : mDataManager(std::move(aDataManager)), mRootResponse(aRootEntry) {}
     41 
     42 FileSystemManagerParent::~FileSystemManagerParent() {
     43  LOG(("Destroying FileSystemManagerParent %p", this));
     44  MOZ_ASSERT(!mRegistered);
     45 }
     46 
     47 void FileSystemManagerParent::AssertIsOnIOTarget() const {
     48  MOZ_ASSERT(mDataManager);
     49 
     50  mDataManager->AssertIsOnIOTarget();
     51 }
     52 
     53 bool FileSystemManagerParent::IsAlive() const {
     54  mozilla::ipc::AssertIsOnBackgroundThread();
     55 
     56  return !!mDataManager;
     57 }
     58 
     59 const RefPtr<fs::data::FileSystemDataManager>&
     60 FileSystemManagerParent::DataManagerStrongRef() const {
     61  MOZ_ASSERT(!mActorDestroyed);
     62  MOZ_ASSERT(mDataManager);
     63 
     64  return mDataManager;
     65 }
     66 
     67 IPCResult FileSystemManagerParent::RecvGetRootHandle(
     68    GetRootHandleResolver&& aResolver) {
     69  AssertIsOnIOTarget();
     70 
     71  aResolver(mRootResponse);
     72 
     73  return IPC_OK();
     74 }
     75 
     76 IPCResult FileSystemManagerParent::RecvGetDirectoryHandle(
     77    FileSystemGetHandleRequest&& aRequest,
     78    GetDirectoryHandleResolver&& aResolver) {
     79  LOG(("GetDirectoryHandle %s ",
     80       NS_ConvertUTF16toUTF8(aRequest.handle().childName()).get()));
     81  AssertIsOnIOTarget();
     82  MOZ_ASSERT(!aRequest.handle().parentId().IsEmpty());
     83  MOZ_ASSERT(mDataManager);
     84 
     85  auto reportError = [&aResolver](const QMResult& aRv) {
     86    FileSystemGetHandleResponse response(ToNSResult(aRv));
     87    aResolver(response);
     88  };
     89 
     90  QM_TRY_UNWRAP(fs::EntryId entryId,
     91                mDataManager->MutableDatabaseManagerPtr()->GetOrCreateDirectory(
     92                    aRequest.handle(), aRequest.create()),
     93                IPC_OK(), reportError);
     94  MOZ_ASSERT(!entryId.IsEmpty());
     95 
     96  FileSystemGetHandleResponse response(entryId);
     97  aResolver(response);
     98 
     99  return IPC_OK();
    100 }
    101 
    102 IPCResult FileSystemManagerParent::RecvGetFileHandle(
    103    FileSystemGetHandleRequest&& aRequest, GetFileHandleResolver&& aResolver) {
    104  AssertIsOnIOTarget();
    105  MOZ_ASSERT(!aRequest.handle().parentId().IsEmpty());
    106  MOZ_ASSERT(mDataManager);
    107 
    108  auto reportError = [&aResolver](const QMResult& aRv) {
    109    FileSystemGetHandleResponse response(ToNSResult(aRv));
    110    aResolver(response);
    111  };
    112 
    113  QM_TRY_UNWRAP(fs::EntryId entryId,
    114                mDataManager->MutableDatabaseManagerPtr()->GetOrCreateFile(
    115                    aRequest.handle(), aRequest.create()),
    116                IPC_OK(), reportError);
    117  MOZ_ASSERT(!entryId.IsEmpty());
    118 
    119  FileSystemGetHandleResponse response(entryId);
    120  aResolver(response);
    121  return IPC_OK();
    122 }
    123 
    124 // Could use a template, but you need several types
    125 mozilla::ipc::IPCResult FileSystemManagerParent::RecvGetAccessHandle(
    126    FileSystemGetAccessHandleRequest&& aRequest,
    127    GetAccessHandleResolver&& aResolver) {
    128  AssertIsOnIOTarget();
    129  MOZ_ASSERT(mDataManager);
    130 
    131  EntryId entryId = aRequest.entryId();
    132 
    133  FileSystemAccessHandle::Create(mDataManager, entryId)
    134      ->Then(
    135          GetCurrentSerialEventTarget(), __func__,
    136          [self = RefPtr(this), request = std::move(aRequest),
    137           resolver = std::move(aResolver)](
    138              FileSystemAccessHandle::CreatePromise::ResolveOrRejectValue&&
    139                  aValue) {
    140            if (!self->CanSend()) {
    141              return;
    142            }
    143 
    144            if (aValue.IsReject()) {
    145              resolver(aValue.RejectValue());
    146              return;
    147            }
    148 
    149            FileSystemAccessHandle::CreateResult result =
    150                std::move(aValue.ResolveValue());
    151 
    152            fs::Registered<FileSystemAccessHandle> accessHandle =
    153                std::move(result.first);
    154 
    155            RandomAccessStreamParams streamParams = std::move(result.second);
    156 
    157            auto accessHandleParent = MakeRefPtr<FileSystemAccessHandleParent>(
    158                accessHandle.inspect());
    159 
    160            auto resolveAndReturn = [&resolver](nsresult rv) { resolver(rv); };
    161 
    162            ManagedEndpoint<PFileSystemAccessHandleChild>
    163                accessHandleChildEndpoint =
    164                    self->OpenPFileSystemAccessHandleEndpoint(
    165                        accessHandleParent);
    166            QM_TRY(MOZ_TO_RESULT(accessHandleChildEndpoint.IsValid()),
    167                   resolveAndReturn);
    168 
    169            accessHandle->RegisterActor(WrapNotNull(accessHandleParent));
    170 
    171            auto accessHandleControlParent =
    172                MakeRefPtr<FileSystemAccessHandleControlParent>(
    173                    accessHandle.inspect());
    174 
    175            Endpoint<PFileSystemAccessHandleControlParent>
    176                accessHandleControlParentEndpoint;
    177            Endpoint<PFileSystemAccessHandleControlChild>
    178                accessHandleControlChildEndpoint;
    179            MOZ_ALWAYS_SUCCEEDS(PFileSystemAccessHandleControl::CreateEndpoints(
    180                &accessHandleControlParentEndpoint,
    181                &accessHandleControlChildEndpoint));
    182 
    183            accessHandleControlParentEndpoint.Bind(accessHandleControlParent);
    184 
    185            accessHandle->RegisterControlActor(
    186                WrapNotNull(accessHandleControlParent));
    187 
    188            resolver(FileSystemAccessHandleProperties(
    189                std::move(streamParams), std::move(accessHandleChildEndpoint),
    190                std::move(accessHandleControlChildEndpoint)));
    191          });
    192 
    193  return IPC_OK();
    194 }
    195 
    196 mozilla::ipc::IPCResult FileSystemManagerParent::RecvGetWritable(
    197    FileSystemGetWritableRequest&& aRequest, GetWritableResolver&& aResolver) {
    198  AssertIsOnIOTarget();
    199  MOZ_ASSERT(mDataManager);
    200 
    201  const fs::FileMode mode = mDataManager->GetMode(aRequest.keepData());
    202 
    203  auto reportError = [aResolver](const auto& aRv) {
    204    aResolver(ToNSResult(aRv));
    205  };
    206 
    207  // TODO: Get rid of mode and switching based on it, have the right unlocking
    208  // automatically
    209  const fs::EntryId& entryId = aRequest.entryId();
    210  QM_TRY_UNWRAP(
    211      fs::FileId fileId,
    212      (mode == fs::FileMode::EXCLUSIVE ? mDataManager->LockExclusive(entryId)
    213                                       : mDataManager->LockShared(entryId)),
    214      IPC_OK(), reportError);
    215  MOZ_ASSERT(!fileId.IsEmpty());
    216 
    217  auto autoUnlock = MakeScopeExit(
    218      [self = RefPtr<FileSystemManagerParent>(this), &entryId, &fileId, mode] {
    219        if (mode == fs::FileMode::EXCLUSIVE) {
    220          self->mDataManager->UnlockExclusive(entryId);
    221        } else {
    222          self->mDataManager->UnlockShared(entryId, fileId, /* aAbort */ true);
    223        }
    224      });
    225 
    226  fs::ContentType type;
    227  fs::TimeStamp lastModifiedMilliSeconds;
    228  fs::Path path;
    229  nsCOMPtr<nsIFile> file;
    230  QM_TRY(
    231      MOZ_TO_RESULT(mDataManager->MutableDatabaseManagerPtr()->GetFile(
    232          entryId, fileId, mode, type, lastModifiedMilliSeconds, path, file)),
    233      IPC_OK(), reportError);
    234 
    235  if (LOG_ENABLED()) {
    236    nsAutoString path;
    237    if (NS_SUCCEEDED(file->GetPath(path))) {
    238      LOG(("Opening Writable %s", NS_ConvertUTF16toUTF8(path).get()));
    239    }
    240  }
    241 
    242  auto writableFileStreamParent =
    243      MakeNotNull<RefPtr<FileSystemWritableFileStreamParent>>(
    244          this, aRequest.entryId(), fileId, mode == fs::FileMode::EXCLUSIVE);
    245 
    246  QM_TRY_UNWRAP(
    247      nsCOMPtr<nsIRandomAccessStream> stream,
    248      CreateFileRandomAccessStream(quota::PERSISTENCE_TYPE_DEFAULT,
    249                                   mDataManager->OriginMetadataRef(),
    250                                   quota::Client::FILESYSTEM, file, -1, -1,
    251                                   nsIFileRandomAccessStream::DEFER_OPEN),
    252      IPC_OK(), reportError);
    253 
    254  RandomAccessStreamParams streamParams =
    255      mozilla::ipc::SerializeRandomAccessStream(
    256          WrapMovingNotNullUnchecked(std::move(stream)),
    257          writableFileStreamParent->GetOrCreateStreamCallbacks());
    258 
    259  // Release the auto unlock helper just before calling
    260  // SendPFileSystemWritableFileStreamConstructor which is responsible for
    261  // destroying the actor if the sending fails (we call `UnlockExclusive` when
    262  // the actor is destroyed).
    263  autoUnlock.release();
    264 
    265  if (!SendPFileSystemWritableFileStreamConstructor(writableFileStreamParent)) {
    266    aResolver(NS_ERROR_FAILURE);
    267    return IPC_OK();
    268  }
    269 
    270  aResolver(FileSystemWritableFileStreamProperties(std::move(streamParams),
    271                                                   writableFileStreamParent));
    272 
    273  return IPC_OK();
    274 }
    275 
    276 IPCResult FileSystemManagerParent::RecvGetFile(
    277    FileSystemGetFileRequest&& aRequest, GetFileResolver&& aResolver) {
    278  AssertIsOnIOTarget();
    279 
    280  // XXX Spec https://www.w3.org/TR/FileAPI/#dfn-file wants us to snapshot the
    281  // state of the file at getFile() time
    282 
    283  // You can create a File with getFile() even if the file is locked
    284  // XXX factor out this part of the code for accesshandle/ and getfile
    285  auto reportError = [aResolver](const auto& rv) {
    286    LOG(("getFile() Failed!"));
    287    aResolver(ToNSResult(rv));
    288  };
    289 
    290  const auto& entryId = aRequest.entryId();
    291 
    292  QM_TRY_INSPECT(
    293      const fs::FileId& fileId,
    294      mDataManager->MutableDatabaseManagerPtr()->EnsureFileId(entryId),
    295      IPC_OK(), reportError);
    296 
    297  fs::ContentType type;
    298  fs::TimeStamp lastModifiedMilliSeconds;
    299  fs::Path path;
    300  nsCOMPtr<nsIFile> fileObject;
    301  QM_TRY(MOZ_TO_RESULT(mDataManager->MutableDatabaseManagerPtr()->GetFile(
    302             entryId, fileId, fs::FileMode::EXCLUSIVE, type,
    303             lastModifiedMilliSeconds, path, fileObject)),
    304         IPC_OK(), reportError);
    305 
    306  if (LOG_ENABLED()) {
    307    nsAutoString path;
    308    if (NS_SUCCEEDED(fileObject->GetPath(path))) {
    309      LOG(("Opening File as blob: %s", NS_ConvertUTF16toUTF8(path).get()));
    310    }
    311  }
    312 
    313  // TODO: Currently, there is no way to assign type and it is empty.
    314  // See bug 1826780.
    315  RefPtr<BlobImpl> blob = MakeRefPtr<FileBlobImpl>(
    316      fileObject, path.LastElement(), NS_ConvertUTF8toUTF16(type));
    317 
    318  IPCBlob ipcBlob;
    319  QM_TRY(MOZ_TO_RESULT(IPCBlobUtils::Serialize(blob, ipcBlob)), IPC_OK(),
    320         reportError);
    321 
    322  aResolver(
    323      FileSystemFileProperties(lastModifiedMilliSeconds, ipcBlob, type, path));
    324  return IPC_OK();
    325 }
    326 
    327 IPCResult FileSystemManagerParent::RecvResolve(
    328    FileSystemResolveRequest&& aRequest, ResolveResolver&& aResolver) {
    329  AssertIsOnIOTarget();
    330  MOZ_ASSERT(!aRequest.endpoints().parentId().IsEmpty());
    331  MOZ_ASSERT(!aRequest.endpoints().childId().IsEmpty());
    332  MOZ_ASSERT(mDataManager);
    333 
    334  fs::Path filePath;
    335  if (aRequest.endpoints().parentId() == aRequest.endpoints().childId()) {
    336    FileSystemResolveResponse response(Some(FileSystemPath(filePath)));
    337    aResolver(response);
    338 
    339    return IPC_OK();
    340  }
    341 
    342  auto reportError = [&aResolver](const QMResult& aRv) {
    343    FileSystemResolveResponse response(ToNSResult(aRv));
    344    aResolver(response);
    345  };
    346 
    347  QM_TRY_UNWRAP(
    348      filePath,
    349      mDataManager->MutableDatabaseManagerPtr()->Resolve(aRequest.endpoints()),
    350      IPC_OK(), reportError);
    351 
    352  if (LOG_ENABLED()) {
    353    nsString path;
    354    for (auto& entry : filePath) {
    355      path.Append(entry);
    356    }
    357    LOG(("Resolve path: %s", NS_ConvertUTF16toUTF8(path).get()));
    358  }
    359 
    360  if (filePath.IsEmpty()) {
    361    FileSystemResolveResponse response(Nothing{});
    362    aResolver(response);
    363 
    364    return IPC_OK();
    365  }
    366 
    367  FileSystemResolveResponse response(Some(FileSystemPath(filePath)));
    368  aResolver(response);
    369 
    370  return IPC_OK();
    371 }
    372 
    373 IPCResult FileSystemManagerParent::RecvGetEntries(
    374    FileSystemGetEntriesRequest&& aRequest, GetEntriesResolver&& aResolver) {
    375  AssertIsOnIOTarget();
    376  MOZ_ASSERT(!aRequest.parentId().IsEmpty());
    377  MOZ_ASSERT(mDataManager);
    378 
    379  auto reportError = [&aResolver](const QMResult& aRv) {
    380    FileSystemGetEntriesResponse response(ToNSResult(aRv));
    381    aResolver(response);
    382  };
    383 
    384  QM_TRY_UNWRAP(FileSystemDirectoryListing entries,
    385                mDataManager->MutableDatabaseManagerPtr()->GetDirectoryEntries(
    386                    aRequest.parentId(), aRequest.page()),
    387                IPC_OK(), reportError);
    388 
    389  FileSystemGetEntriesResponse response(entries);
    390  aResolver(response);
    391 
    392  return IPC_OK();
    393 }
    394 
    395 IPCResult FileSystemManagerParent::RecvRemoveEntry(
    396    FileSystemRemoveEntryRequest&& aRequest, RemoveEntryResolver&& aResolver) {
    397  LOG(("RemoveEntry %s",
    398       NS_ConvertUTF16toUTF8(aRequest.handle().childName()).get()));
    399  AssertIsOnIOTarget();
    400  MOZ_ASSERT(!aRequest.handle().parentId().IsEmpty());
    401  MOZ_ASSERT(mDataManager);
    402 
    403  auto reportError = [&aResolver](const QMResult& aRv) {
    404    FileSystemRemoveEntryResponse response(ToNSResult(aRv));
    405    aResolver(response);
    406  };
    407 
    408  QM_TRY_UNWRAP(
    409      bool isDeleted,
    410      mDataManager->MutableDatabaseManagerPtr()->RemoveFile(aRequest.handle()),
    411      IPC_OK(), reportError);
    412 
    413  if (isDeleted) {
    414    FileSystemRemoveEntryResponse response(void_t{});
    415    aResolver(response);
    416 
    417    return IPC_OK();
    418  }
    419 
    420  QM_TRY_UNWRAP(isDeleted,
    421                mDataManager->MutableDatabaseManagerPtr()->RemoveDirectory(
    422                    aRequest.handle(), aRequest.recursive()),
    423                IPC_OK(), reportError);
    424 
    425  if (!isDeleted) {
    426    FileSystemRemoveEntryResponse response(NS_ERROR_DOM_NOT_FOUND_ERR);
    427    aResolver(response);
    428 
    429    return IPC_OK();
    430  }
    431 
    432  FileSystemRemoveEntryResponse response(void_t{});
    433  aResolver(response);
    434 
    435  return IPC_OK();
    436 }
    437 
    438 IPCResult FileSystemManagerParent::RecvMoveEntry(
    439    FileSystemMoveEntryRequest&& aRequest, MoveEntryResolver&& aResolver) {
    440  LOG(("MoveEntry %s to %s",
    441       NS_ConvertUTF16toUTF8(aRequest.handle().entryName()).get(),
    442       NS_ConvertUTF16toUTF8(aRequest.destHandle().childName()).get()));
    443  MOZ_ASSERT(!aRequest.handle().entryId().IsEmpty());
    444  MOZ_ASSERT(!aRequest.destHandle().parentId().IsEmpty());
    445  MOZ_ASSERT(mDataManager);
    446 
    447  auto reportError = [&aResolver](const QMResult& aRv) {
    448    FileSystemMoveEntryResponse response(ToNSResult(aRv));
    449    aResolver(response);
    450  };
    451 
    452  QM_TRY_INSPECT(const EntryId& newId,
    453                 mDataManager->MutableDatabaseManagerPtr()->MoveEntry(
    454                     aRequest.handle(), aRequest.destHandle()),
    455                 IPC_OK(), reportError);
    456 
    457  fs::FileSystemMoveEntryResponse response(newId);
    458  aResolver(response);
    459  return IPC_OK();
    460 }
    461 
    462 IPCResult FileSystemManagerParent::RecvRenameEntry(
    463    FileSystemRenameEntryRequest&& aRequest, MoveEntryResolver&& aResolver) {
    464  // if destHandle's parentId is empty, then we're renaming in the same
    465  // directory
    466  LOG(("RenameEntry %s to %s",
    467       NS_ConvertUTF16toUTF8(aRequest.handle().entryName()).get(),
    468       NS_ConvertUTF16toUTF8(aRequest.name()).get()));
    469  MOZ_ASSERT(!aRequest.handle().entryId().IsEmpty());
    470  MOZ_ASSERT(mDataManager);
    471 
    472  auto reportError = [&aResolver](const QMResult& aRv) {
    473    FileSystemMoveEntryResponse response(ToNSResult(aRv));
    474    aResolver(response);
    475  };
    476 
    477  QM_TRY_INSPECT(const EntryId& newId,
    478                 mDataManager->MutableDatabaseManagerPtr()->RenameEntry(
    479                     aRequest.handle(), aRequest.name()),
    480                 IPC_OK(), reportError);
    481 
    482  fs::FileSystemMoveEntryResponse response(newId);
    483  aResolver(response);
    484  return IPC_OK();
    485 }
    486 
    487 void FileSystemManagerParent::RequestAllowToClose() {
    488  ::mozilla::ipc::AssertIsOnBackgroundThread();
    489 
    490  if (mRequestedAllowToClose) {
    491    return;
    492  }
    493 
    494  mRequestedAllowToClose.Flip();
    495 
    496  InvokeAsync(mDataManager->MutableIOTaskQueuePtr(), __func__,
    497              [self = RefPtr<FileSystemManagerParent>(this)]() {
    498                return self->SendCloseAll();
    499              })
    500      ->Then(mDataManager->MutableIOTaskQueuePtr(), __func__,
    501             [self = RefPtr<FileSystemManagerParent>(this)](
    502                 const CloseAllPromise::ResolveOrRejectValue& aValue) {
    503               self->Close();
    504 
    505               return BoolPromise::CreateAndResolve(true, __func__);
    506             });
    507 }
    508 
    509 void FileSystemManagerParent::ActorDestroy(ActorDestroyReason aWhy) {
    510  AssertIsOnIOTarget();
    511  MOZ_ASSERT(!mActorDestroyed);
    512 
    513  DEBUGONLY(mActorDestroyed = true);
    514 
    515  InvokeAsync(mDataManager->MutableBackgroundTargetPtr(), __func__,
    516              [self = RefPtr<FileSystemManagerParent>(this)]() {
    517                if (self->mRegistered) {
    518                  self->mDataManager->UnregisterActor(WrapNotNull(self));
    519                }
    520 
    521                self->mDataManager = nullptr;
    522 
    523                return BoolPromise::CreateAndResolve(true, __func__);
    524              });
    525 }
    526 
    527 }  // namespace mozilla::dom