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