Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "ActorsParent.h"
6 :
7 : #include "mozilla/Assertions.h"
8 : #include "mozilla/Atomics.h"
9 : #include "mozilla/Attributes.h"
10 : #include "mozilla/Unused.h"
11 : #include "mozilla/dom/File.h"
12 : #include "mozilla/dom/PBackgroundFileHandleParent.h"
13 : #include "mozilla/dom/PBackgroundFileRequestParent.h"
14 : #include "mozilla/dom/indexedDB/ActorsParent.h"
15 : #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
16 : #include "mozilla/dom/IPCBlobUtils.h"
17 : #include "mozilla/dom/ipc/PendingIPCBlobParent.h"
18 : #include "nsAutoPtr.h"
19 : #include "nsComponentManagerUtils.h"
20 : #include "nsDebug.h"
21 : #include "nsError.h"
22 : #include "nsIEventTarget.h"
23 : #include "nsIFileStreams.h"
24 : #include "nsIInputStream.h"
25 : #include "nsIOutputStream.h"
26 : #include "nsIRunnable.h"
27 : #include "nsISeekableStream.h"
28 : #include "nsIThread.h"
29 : #include "nsIThreadPool.h"
30 : #include "nsNetUtil.h"
31 : #include "nsStreamUtils.h"
32 : #include "nsStringStream.h"
33 : #include "nsTArray.h"
34 : #include "nsThreadPool.h"
35 : #include "nsThreadUtils.h"
36 : #include "nsXPCOMCIDInternal.h"
37 :
38 : #define DISABLE_ASSERTS_FOR_FUZZING 0
39 :
40 : #if DISABLE_ASSERTS_FOR_FUZZING
41 : #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
42 : #else
43 : #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
44 : #endif
45 :
46 : namespace mozilla {
47 : namespace dom {
48 :
49 : using namespace mozilla::ipc;
50 :
51 : namespace {
52 :
53 : /******************************************************************************
54 : * Constants
55 : ******************************************************************************/
56 :
57 : const uint32_t kThreadLimit = 5;
58 : const uint32_t kIdleThreadLimit = 1;
59 : const uint32_t kIdleThreadTimeoutMs = 30000;
60 :
61 : const uint32_t kStreamCopyBlockSize = 32768;
62 :
63 : } // namespace
64 :
65 : class FileHandleThreadPool::FileHandleQueue final
66 : : public Runnable
67 : {
68 : friend class FileHandleThreadPool;
69 :
70 : RefPtr<FileHandleThreadPool> mOwningFileHandleThreadPool;
71 : RefPtr<FileHandle> mFileHandle;
72 : nsTArray<RefPtr<FileHandleOp>> mQueue;
73 : RefPtr<FileHandleOp> mCurrentOp;
74 : bool mShouldFinish;
75 :
76 : public:
77 : explicit
78 : FileHandleQueue(FileHandleThreadPool* aFileHandleThreadPool,
79 : FileHandle* aFileHandle);
80 :
81 : void
82 : Enqueue(FileHandleOp* aFileHandleOp);
83 :
84 : void
85 : Finish();
86 :
87 : void
88 : ProcessQueue();
89 :
90 : private:
91 0 : ~FileHandleQueue() {}
92 :
93 : NS_DECL_NSIRUNNABLE
94 : };
95 :
96 0 : struct FileHandleThreadPool::DelayedEnqueueInfo
97 : {
98 : RefPtr<FileHandle> mFileHandle;
99 : RefPtr<FileHandleOp> mFileHandleOp;
100 : bool mFinish;
101 : };
102 :
103 0 : class FileHandleThreadPool::DirectoryInfo
104 : {
105 : friend class FileHandleThreadPool;
106 :
107 : RefPtr<FileHandleThreadPool> mOwningFileHandleThreadPool;
108 : nsTArray<RefPtr<FileHandleQueue>> mFileHandleQueues;
109 : nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
110 : nsTHashtable<nsStringHashKey> mFilesReading;
111 : nsTHashtable<nsStringHashKey> mFilesWriting;
112 :
113 : public:
114 : FileHandleQueue*
115 : CreateFileHandleQueue(FileHandle* aFileHandle);
116 :
117 : FileHandleQueue*
118 : GetFileHandleQueue(FileHandle* aFileHandle);
119 :
120 : void
121 : RemoveFileHandleQueue(FileHandle* aFileHandle);
122 :
123 : bool
124 0 : HasRunningFileHandles()
125 : {
126 0 : return !mFileHandleQueues.IsEmpty();
127 : }
128 :
129 : DelayedEnqueueInfo*
130 : CreateDelayedEnqueueInfo(FileHandle* aFileHandle,
131 : FileHandleOp* aFileHandleOp,
132 : bool aFinish);
133 :
134 : void
135 0 : LockFileForReading(const nsAString& aFileName)
136 : {
137 0 : mFilesReading.PutEntry(aFileName);
138 0 : }
139 :
140 : void
141 0 : LockFileForWriting(const nsAString& aFileName)
142 : {
143 0 : mFilesWriting.PutEntry(aFileName);
144 0 : }
145 :
146 : bool
147 0 : IsFileLockedForReading(const nsAString& aFileName)
148 : {
149 0 : return mFilesReading.Contains(aFileName);
150 : }
151 :
152 : bool
153 0 : IsFileLockedForWriting(const nsAString& aFileName)
154 : {
155 0 : return mFilesWriting.Contains(aFileName);
156 : }
157 :
158 : private:
159 0 : explicit DirectoryInfo(FileHandleThreadPool* aFileHandleThreadPool)
160 0 : : mOwningFileHandleThreadPool(aFileHandleThreadPool)
161 0 : { }
162 : };
163 :
164 : struct FileHandleThreadPool::StoragesCompleteCallback final
165 : {
166 : friend class nsAutoPtr<StoragesCompleteCallback>;
167 :
168 : nsTArray<nsCString> mDirectoryIds;
169 : nsCOMPtr<nsIRunnable> mCallback;
170 :
171 : StoragesCompleteCallback(nsTArray<nsCString>&& aDatabaseIds,
172 : nsIRunnable* aCallback);
173 :
174 : private:
175 : ~StoragesCompleteCallback();
176 : };
177 :
178 : /******************************************************************************
179 : * Actor class declarations
180 : ******************************************************************************/
181 :
182 : class FileHandle
183 : : public PBackgroundFileHandleParent
184 : {
185 : friend class BackgroundMutableFileParentBase;
186 :
187 : class FinishOp;
188 :
189 : RefPtr<BackgroundMutableFileParentBase> mMutableFile;
190 : nsCOMPtr<nsISupports> mStream;
191 : uint64_t mActiveRequestCount;
192 : FileHandleStorage mStorage;
193 : Atomic<bool> mInvalidatedOnAnyThread;
194 : FileMode mMode;
195 : bool mHasBeenActive;
196 : bool mActorDestroyed;
197 : bool mInvalidated;
198 : bool mAborted;
199 : bool mFinishOrAbortReceived;
200 : bool mFinishedOrAborted;
201 : bool mForceAborted;
202 :
203 : #ifdef DEBUG
204 : nsCOMPtr<nsIEventTarget> mThreadPoolEventTarget;
205 : #endif
206 :
207 : public:
208 : void
209 : AssertIsOnThreadPool() const;
210 :
211 : bool
212 0 : IsActorDestroyed() const
213 : {
214 0 : AssertIsOnBackgroundThread();
215 :
216 0 : return mActorDestroyed;
217 : }
218 :
219 : // Must be called on the background thread.
220 : bool
221 0 : IsInvalidated() const
222 : {
223 0 : MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
224 0 : MOZ_ASSERT_IF(mInvalidated, mAborted);
225 :
226 0 : return mInvalidated;
227 : }
228 :
229 : // May be called on any thread, but is more expensive than IsInvalidated().
230 : bool
231 0 : IsInvalidatedOnAnyThread() const
232 : {
233 0 : return mInvalidatedOnAnyThread;
234 : }
235 :
236 : void
237 0 : SetActive()
238 : {
239 0 : AssertIsOnBackgroundThread();
240 :
241 0 : mHasBeenActive = true;
242 0 : }
243 :
244 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::FileHandle)
245 :
246 : nsresult
247 : GetOrCreateStream(nsISupports** aStream);
248 :
249 : void
250 : Abort(bool aForce);
251 :
252 : FileHandleStorage
253 0 : Storage() const
254 : {
255 0 : return mStorage;
256 : }
257 :
258 : FileMode
259 0 : Mode() const
260 : {
261 0 : return mMode;
262 : }
263 :
264 : BackgroundMutableFileParentBase*
265 0 : GetMutableFile() const
266 : {
267 0 : AssertIsOnBackgroundThread();
268 0 : MOZ_ASSERT(mMutableFile);
269 :
270 0 : return mMutableFile;
271 : }
272 :
273 : bool
274 0 : IsAborted() const
275 : {
276 0 : AssertIsOnBackgroundThread();
277 :
278 0 : return mAborted;
279 : }
280 :
281 : PBackgroundParent*
282 0 : GetBackgroundParent() const
283 : {
284 0 : AssertIsOnBackgroundThread();
285 0 : MOZ_ASSERT(!IsActorDestroyed());
286 :
287 0 : return GetMutableFile()->GetBackgroundParent();
288 : }
289 :
290 : void
291 : NoteActiveRequest();
292 :
293 : void
294 : NoteFinishedRequest();
295 :
296 : void
297 : Invalidate();
298 :
299 : private:
300 : // This constructor is only called by BackgroundMutableFileParentBase.
301 : FileHandle(BackgroundMutableFileParentBase* aMutableFile,
302 : FileMode aMode);
303 :
304 : // Reference counted.
305 : ~FileHandle();
306 :
307 : void
308 0 : MaybeFinishOrAbort()
309 : {
310 0 : AssertIsOnBackgroundThread();
311 :
312 : // If we've already finished or aborted then there's nothing else to do.
313 0 : if (mFinishedOrAborted) {
314 0 : return;
315 : }
316 :
317 : // If there are active requests then we have to wait for those requests to
318 : // complete (see NoteFinishedRequest).
319 0 : if (mActiveRequestCount) {
320 0 : return;
321 : }
322 :
323 : // If we haven't yet received a finish or abort message then there could be
324 : // additional requests coming so we should wait unless we're being forced to
325 : // abort.
326 0 : if (!mFinishOrAbortReceived && !mForceAborted) {
327 0 : return;
328 : }
329 :
330 0 : FinishOrAbort();
331 : }
332 :
333 : void
334 : SendCompleteNotification(bool aAborted);
335 :
336 : bool
337 : VerifyRequestParams(const FileRequestParams& aParams) const;
338 :
339 : bool
340 : VerifyRequestData(const FileRequestData& aData) const;
341 :
342 : void
343 : FinishOrAbort();
344 :
345 : // IPDL methods are only called by IPDL.
346 : virtual void
347 : ActorDestroy(ActorDestroyReason aWhy) override;
348 :
349 : virtual mozilla::ipc::IPCResult
350 : RecvDeleteMe() override;
351 :
352 : virtual mozilla::ipc::IPCResult
353 : RecvFinish() override;
354 :
355 : virtual mozilla::ipc::IPCResult
356 : RecvAbort() override;
357 :
358 : virtual PBackgroundFileRequestParent*
359 : AllocPBackgroundFileRequestParent(const FileRequestParams& aParams) override;
360 :
361 : virtual mozilla::ipc::IPCResult
362 : RecvPBackgroundFileRequestConstructor(PBackgroundFileRequestParent* aActor,
363 : const FileRequestParams& aParams)
364 : override;
365 :
366 : virtual bool
367 : DeallocPBackgroundFileRequestParent(PBackgroundFileRequestParent* aActor)
368 : override;
369 : };
370 :
371 : class FileHandleOp
372 : {
373 : protected:
374 : nsCOMPtr<nsIEventTarget> mOwningEventTarget;
375 : RefPtr<FileHandle> mFileHandle;
376 :
377 : public:
378 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileHandleOp)
379 :
380 : void
381 0 : AssertIsOnOwningThread() const
382 : {
383 0 : AssertIsOnBackgroundThread();
384 0 : MOZ_ASSERT(mOwningEventTarget);
385 0 : DebugOnly<bool> current;
386 0 : MOZ_ASSERT(NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)));
387 0 : MOZ_ASSERT(current);
388 0 : }
389 :
390 : nsIEventTarget*
391 0 : OwningThread() const
392 : {
393 0 : return mOwningEventTarget;
394 : }
395 :
396 : void
397 0 : AssertIsOnThreadPool() const
398 : {
399 0 : MOZ_ASSERT(mFileHandle);
400 0 : mFileHandle->AssertIsOnThreadPool();
401 0 : }
402 :
403 : void
404 : Enqueue();
405 :
406 : virtual void
407 : RunOnThreadPool() = 0;
408 :
409 : virtual void
410 : RunOnOwningThread() = 0;
411 :
412 : protected:
413 0 : FileHandleOp(FileHandle* aFileHandle)
414 0 : : mOwningEventTarget(GetCurrentThreadSerialEventTarget())
415 0 : , mFileHandle(aFileHandle)
416 : {
417 0 : AssertIsOnOwningThread();
418 0 : MOZ_ASSERT(aFileHandle);
419 0 : }
420 :
421 : virtual
422 0 : ~FileHandleOp()
423 0 : { }
424 : };
425 :
426 : class FileHandle::FinishOp
427 : : public FileHandleOp
428 : {
429 : friend class FileHandle;
430 :
431 : bool mAborted;
432 :
433 : private:
434 0 : FinishOp(FileHandle* aFileHandle,
435 : bool aAborted)
436 0 : : FileHandleOp(aFileHandle)
437 0 : , mAborted(aAborted)
438 : {
439 0 : MOZ_ASSERT(aFileHandle);
440 0 : }
441 :
442 0 : ~FinishOp()
443 0 : { }
444 :
445 : virtual void
446 : RunOnThreadPool() override;
447 :
448 : virtual void
449 : RunOnOwningThread() override;
450 : };
451 :
452 : class NormalFileHandleOp
453 : : public FileHandleOp
454 : , public PBackgroundFileRequestParent
455 : {
456 : nsresult mResultCode;
457 : Atomic<bool> mOperationMayProceed;
458 : bool mActorDestroyed;
459 : const bool mFileHandleIsAborted;
460 :
461 : #ifdef DEBUG
462 : bool mResponseSent;
463 : #endif
464 :
465 : protected:
466 : nsCOMPtr<nsISupports> mFileStream;
467 :
468 : public:
469 : void
470 0 : NoteActorDestroyed()
471 : {
472 0 : AssertIsOnOwningThread();
473 :
474 0 : mActorDestroyed = true;
475 0 : mOperationMayProceed = false;
476 0 : }
477 :
478 : bool
479 0 : IsActorDestroyed() const
480 : {
481 0 : AssertIsOnOwningThread();
482 :
483 0 : return mActorDestroyed;
484 : }
485 :
486 : // May be called on any thread, but you should call IsActorDestroyed() if
487 : // you know you're on the background thread because it is slightly faster.
488 : bool
489 0 : OperationMayProceed() const
490 : {
491 0 : return mOperationMayProceed;
492 : }
493 :
494 : // May be overridden by subclasses if they need to perform work on the
495 : // background thread before being enqueued. Returning false will kill the
496 : // child actors and prevent enqueue.
497 : virtual bool
498 : Init(FileHandle* aFileHandle);
499 :
500 : // This callback will be called on the background thread before releasing the
501 : // final reference to this request object. Subclasses may perform any
502 : // additional cleanup here but must always call the base class implementation.
503 : virtual void
504 : Cleanup();
505 :
506 : protected:
507 0 : NormalFileHandleOp(FileHandle* aFileHandle)
508 0 : : FileHandleOp(aFileHandle)
509 : , mResultCode(NS_OK)
510 : , mOperationMayProceed(true)
511 : , mActorDestroyed(false)
512 0 : , mFileHandleIsAborted(aFileHandle->IsAborted())
513 : #ifdef DEBUG
514 0 : , mResponseSent(false)
515 : #endif
516 : {
517 0 : MOZ_ASSERT(aFileHandle);
518 0 : }
519 :
520 : virtual
521 : ~NormalFileHandleOp();
522 :
523 : // Must be overridden in subclasses. Called on the target thread to allow the
524 : // subclass to perform necessary file operations. A successful return value
525 : // will trigger a SendSuccessResult callback on the background thread while
526 : // a failure value will trigger a SendFailureResult callback.
527 : virtual nsresult
528 : DoFileWork(FileHandle* aFileHandle) = 0;
529 :
530 : // Subclasses use this override to set the IPDL response value.
531 : virtual void
532 : GetResponse(FileRequestResponse& aResponse) = 0;
533 :
534 : private:
535 : nsresult
536 : SendSuccessResult();
537 :
538 : bool
539 : SendFailureResult(nsresult aResultCode);
540 :
541 : virtual void
542 : RunOnThreadPool() override;
543 :
544 : virtual void
545 : RunOnOwningThread() override;
546 :
547 : // IPDL methods.
548 : virtual void
549 : ActorDestroy(ActorDestroyReason aWhy) override;
550 : };
551 :
552 0 : class CopyFileHandleOp
553 : : public NormalFileHandleOp
554 : {
555 : class ProgressRunnable;
556 :
557 : protected:
558 : nsCOMPtr<nsISupports> mBufferStream;
559 :
560 : uint64_t mOffset;
561 : uint64_t mSize;
562 :
563 : bool mRead;
564 :
565 : protected:
566 0 : CopyFileHandleOp(FileHandle* aFileHandle)
567 0 : : NormalFileHandleOp(aFileHandle)
568 : , mOffset(0)
569 : , mSize(0)
570 0 : , mRead(true)
571 0 : { }
572 :
573 : virtual nsresult
574 : DoFileWork(FileHandle* aFileHandle) override;
575 :
576 : virtual void
577 : Cleanup() override;
578 : };
579 :
580 : class CopyFileHandleOp::ProgressRunnable final
581 : : public Runnable
582 : {
583 : RefPtr<CopyFileHandleOp> mCopyFileHandleOp;
584 : uint64_t mProgress;
585 : uint64_t mProgressMax;
586 :
587 : public:
588 0 : ProgressRunnable(CopyFileHandleOp* aCopyFileHandleOp,
589 : uint64_t aProgress,
590 : uint64_t aProgressMax)
591 0 : : Runnable("dom::CopyFileHandleOp::ProgressRunnable")
592 : , mCopyFileHandleOp(aCopyFileHandleOp)
593 : , mProgress(aProgress)
594 0 : , mProgressMax(aProgressMax)
595 0 : { }
596 :
597 : private:
598 0 : ~ProgressRunnable() {}
599 :
600 : NS_DECL_NSIRUNNABLE
601 : };
602 :
603 : class GetMetadataOp
604 : : public NormalFileHandleOp
605 : {
606 : friend class FileHandle;
607 :
608 : const FileRequestGetMetadataParams mParams;
609 :
610 : protected:
611 : FileRequestMetadata mMetadata;
612 :
613 : protected:
614 : // Only created by FileHandle.
615 : GetMetadataOp(FileHandle* aFileHandle,
616 : const FileRequestParams& aParams);
617 :
618 0 : ~GetMetadataOp()
619 0 : { }
620 :
621 : virtual nsresult
622 : DoFileWork(FileHandle* aFileHandle) override;
623 :
624 : virtual void
625 : GetResponse(FileRequestResponse& aResponse) override;
626 : };
627 :
628 : class ReadOp final
629 : : public CopyFileHandleOp
630 : {
631 : friend class FileHandle;
632 :
633 : class MemoryOutputStream;
634 :
635 : const FileRequestReadParams mParams;
636 :
637 : private:
638 : // Only created by FileHandle.
639 : ReadOp(FileHandle* aFileHandle,
640 : const FileRequestParams& aParams);
641 :
642 0 : ~ReadOp()
643 0 : { }
644 :
645 : virtual bool
646 : Init(FileHandle* aFileHandle) override;
647 :
648 : virtual void
649 : GetResponse(FileRequestResponse& aResponse) override;
650 : };
651 :
652 : class ReadOp::MemoryOutputStream final
653 : : public nsIOutputStream
654 : {
655 : nsCString mData;
656 : uint64_t mOffset;
657 :
658 : public:
659 : static already_AddRefed<MemoryOutputStream>
660 : Create(uint64_t aSize);
661 :
662 : const nsCString&
663 0 : Data() const
664 : {
665 0 : return mData;
666 : }
667 :
668 : private:
669 0 : MemoryOutputStream()
670 0 : : mOffset(0)
671 0 : { }
672 :
673 0 : virtual ~MemoryOutputStream()
674 0 : { }
675 :
676 : NS_DECL_THREADSAFE_ISUPPORTS
677 : NS_DECL_NSIOUTPUTSTREAM
678 : };
679 :
680 : class WriteOp final
681 : : public CopyFileHandleOp
682 : {
683 : friend class FileHandle;
684 :
685 : const FileRequestWriteParams mParams;
686 :
687 : private:
688 : // Only created by FileHandle.
689 : WriteOp(FileHandle* aFileHandle,
690 : const FileRequestParams& aParams);
691 :
692 0 : ~WriteOp()
693 0 : { }
694 :
695 : virtual bool
696 : Init(FileHandle* aFileHandle) override;
697 :
698 : virtual void
699 : GetResponse(FileRequestResponse& aResponse) override;
700 : };
701 :
702 : class TruncateOp final
703 : : public NormalFileHandleOp
704 : {
705 : friend class FileHandle;
706 :
707 : const FileRequestTruncateParams mParams;
708 :
709 : private:
710 : // Only created by FileHandle.
711 : TruncateOp(FileHandle* aFileHandle,
712 : const FileRequestParams& aParams);
713 :
714 0 : ~TruncateOp()
715 0 : { }
716 :
717 : virtual nsresult
718 : DoFileWork(FileHandle* aFileHandle) override;
719 :
720 : virtual void
721 : GetResponse(FileRequestResponse& aResponse) override;
722 : };
723 :
724 : class FlushOp final
725 : : public NormalFileHandleOp
726 : {
727 : friend class FileHandle;
728 :
729 : const FileRequestFlushParams mParams;
730 :
731 : private:
732 : // Only created by FileHandle.
733 : FlushOp(FileHandle* aFileHandle,
734 : const FileRequestParams& aParams);
735 :
736 0 : ~FlushOp()
737 0 : { }
738 :
739 : virtual nsresult
740 : DoFileWork(FileHandle* aFileHandle) override;
741 :
742 : virtual void
743 : GetResponse(FileRequestResponse& aResponse) override;
744 : };
745 :
746 : class GetFileOp final
747 : : public GetMetadataOp
748 : {
749 : friend class FileHandle;
750 :
751 : PBackgroundParent* mBackgroundParent;
752 :
753 : private:
754 : // Only created by FileHandle.
755 : GetFileOp(FileHandle* aFileHandle,
756 : const FileRequestParams& aParams);
757 :
758 0 : ~GetFileOp()
759 0 : { }
760 :
761 : virtual void
762 : GetResponse(FileRequestResponse& aResponse) override;
763 : };
764 :
765 : namespace {
766 :
767 : /*******************************************************************************
768 : * Helper Functions
769 : ******************************************************************************/
770 :
771 : FileHandleThreadPool*
772 0 : GetFileHandleThreadPoolFor(FileHandleStorage aStorage)
773 : {
774 0 : switch (aStorage) {
775 : case FILE_HANDLE_STORAGE_IDB:
776 0 : return mozilla::dom::indexedDB::GetFileHandleThreadPool();
777 :
778 : default:
779 0 : MOZ_CRASH("Bad file handle storage value!");
780 : }
781 : }
782 :
783 : } // namespace
784 :
785 : /*******************************************************************************
786 : * FileHandleThreadPool implementation
787 : ******************************************************************************/
788 :
789 0 : FileHandleThreadPool::FileHandleThreadPool()
790 0 : : mOwningEventTarget(GetCurrentThreadSerialEventTarget())
791 : , mShutdownRequested(false)
792 0 : , mShutdownComplete(false)
793 : {
794 0 : AssertIsOnBackgroundThread();
795 0 : MOZ_ASSERT(mOwningEventTarget);
796 0 : AssertIsOnOwningThread();
797 0 : }
798 :
799 0 : FileHandleThreadPool::~FileHandleThreadPool()
800 : {
801 0 : AssertIsOnOwningThread();
802 0 : MOZ_ASSERT(!mDirectoryInfos.Count());
803 0 : MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
804 0 : MOZ_ASSERT(mShutdownRequested);
805 0 : MOZ_ASSERT(mShutdownComplete);
806 0 : }
807 :
808 : // static
809 : already_AddRefed<FileHandleThreadPool>
810 0 : FileHandleThreadPool::Create()
811 : {
812 0 : AssertIsOnBackgroundThread();
813 :
814 : RefPtr<FileHandleThreadPool> fileHandleThreadPool =
815 0 : new FileHandleThreadPool();
816 0 : fileHandleThreadPool->AssertIsOnOwningThread();
817 :
818 0 : if (NS_WARN_IF(NS_FAILED(fileHandleThreadPool->Init()))) {
819 0 : return nullptr;
820 : }
821 :
822 0 : return fileHandleThreadPool.forget();
823 : }
824 :
825 : #ifdef DEBUG
826 :
827 : void
828 0 : FileHandleThreadPool::AssertIsOnOwningThread() const
829 : {
830 0 : MOZ_ASSERT(mOwningEventTarget);
831 :
832 : bool current;
833 0 : MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->IsOnCurrentThread(¤t));
834 0 : MOZ_ASSERT(current);
835 0 : }
836 :
837 : nsIEventTarget*
838 0 : FileHandleThreadPool::GetThreadPoolEventTarget() const
839 : {
840 0 : AssertIsOnOwningThread();
841 0 : MOZ_ASSERT(mThreadPool);
842 :
843 0 : return mThreadPool;
844 : }
845 :
846 : #endif // DEBUG
847 :
848 : void
849 0 : FileHandleThreadPool::Enqueue(FileHandle* aFileHandle,
850 : FileHandleOp* aFileHandleOp,
851 : bool aFinish)
852 : {
853 0 : AssertIsOnOwningThread();
854 0 : MOZ_ASSERT(aFileHandle);
855 0 : MOZ_ASSERT(!mShutdownRequested);
856 :
857 0 : BackgroundMutableFileParentBase* mutableFile = aFileHandle->GetMutableFile();
858 :
859 0 : const nsACString& directoryId = mutableFile->DirectoryId();
860 0 : const nsAString& fileName = mutableFile->FileName();
861 0 : bool modeIsWrite = aFileHandle->Mode() == FileMode::Readwrite;
862 :
863 : DirectoryInfo* directoryInfo;
864 0 : if (!mDirectoryInfos.Get(directoryId, &directoryInfo)) {
865 0 : nsAutoPtr<DirectoryInfo> newDirectoryInfo(new DirectoryInfo(this));
866 :
867 0 : mDirectoryInfos.Put(directoryId, newDirectoryInfo);
868 :
869 0 : directoryInfo = newDirectoryInfo.forget();
870 : }
871 :
872 : FileHandleQueue* existingFileHandleQueue =
873 0 : directoryInfo->GetFileHandleQueue(aFileHandle);
874 :
875 0 : if (existingFileHandleQueue) {
876 0 : existingFileHandleQueue->Enqueue(aFileHandleOp);
877 0 : if (aFinish) {
878 0 : existingFileHandleQueue->Finish();
879 : }
880 0 : return;
881 : }
882 :
883 0 : bool lockedForReading = directoryInfo->IsFileLockedForReading(fileName);
884 0 : bool lockedForWriting = directoryInfo->IsFileLockedForWriting(fileName);
885 :
886 0 : if (modeIsWrite) {
887 0 : if (!lockedForWriting) {
888 0 : directoryInfo->LockFileForWriting(fileName);
889 : }
890 : }
891 : else {
892 0 : if (!lockedForReading) {
893 0 : directoryInfo->LockFileForReading(fileName);
894 : }
895 : }
896 :
897 0 : if (lockedForWriting || (lockedForReading && modeIsWrite)) {
898 0 : directoryInfo->CreateDelayedEnqueueInfo(aFileHandle,
899 : aFileHandleOp,
900 0 : aFinish);
901 : }
902 : else {
903 : FileHandleQueue* fileHandleQueue =
904 0 : directoryInfo->CreateFileHandleQueue(aFileHandle);
905 :
906 0 : if (aFileHandleOp) {
907 0 : fileHandleQueue->Enqueue(aFileHandleOp);
908 0 : if (aFinish) {
909 0 : fileHandleQueue->Finish();
910 : }
911 : }
912 : }
913 : }
914 :
915 : void
916 0 : FileHandleThreadPool::WaitForDirectoriesToComplete(
917 : nsTArray<nsCString>&& aDirectoryIds,
918 : nsIRunnable* aCallback)
919 : {
920 0 : AssertIsOnOwningThread();
921 0 : MOZ_ASSERT(!aDirectoryIds.IsEmpty());
922 0 : MOZ_ASSERT(aCallback);
923 :
924 : nsAutoPtr<StoragesCompleteCallback> callback(
925 0 : new StoragesCompleteCallback(Move(aDirectoryIds), aCallback));
926 :
927 0 : if (!MaybeFireCallback(callback)) {
928 0 : mCompleteCallbacks.AppendElement(callback.forget());
929 : }
930 0 : }
931 :
932 : void
933 0 : FileHandleThreadPool::Shutdown()
934 : {
935 0 : AssertIsOnOwningThread();
936 0 : MOZ_ASSERT(!mShutdownRequested);
937 0 : MOZ_ASSERT(!mShutdownComplete);
938 :
939 0 : mShutdownRequested = true;
940 :
941 0 : if (!mThreadPool) {
942 0 : MOZ_ASSERT(!mDirectoryInfos.Count());
943 0 : MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
944 :
945 0 : mShutdownComplete = true;
946 0 : return;
947 : }
948 :
949 0 : if (!mDirectoryInfos.Count()) {
950 0 : Cleanup();
951 :
952 0 : MOZ_ASSERT(mShutdownComplete);
953 0 : return;
954 : }
955 :
956 0 : MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return mShutdownComplete; }));
957 : }
958 :
959 : nsresult
960 0 : FileHandleThreadPool::Init()
961 : {
962 0 : AssertIsOnOwningThread();
963 :
964 0 : mThreadPool = new nsThreadPool();
965 :
966 0 : nsresult rv = mThreadPool->SetName(NS_LITERAL_CSTRING("FileHandles"));
967 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
968 0 : return rv;
969 : }
970 :
971 0 : rv = mThreadPool->SetThreadLimit(kThreadLimit);
972 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
973 0 : return rv;
974 : }
975 :
976 0 : rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
977 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
978 0 : return rv;
979 : }
980 :
981 0 : rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
982 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
983 0 : return rv;
984 : }
985 :
986 0 : return NS_OK;
987 : }
988 :
989 : void
990 0 : FileHandleThreadPool::Cleanup()
991 : {
992 0 : AssertIsOnOwningThread();
993 0 : MOZ_ASSERT(mThreadPool);
994 0 : MOZ_ASSERT(mShutdownRequested);
995 0 : MOZ_ASSERT(!mShutdownComplete);
996 0 : MOZ_ASSERT(!mDirectoryInfos.Count());
997 :
998 0 : MOZ_ALWAYS_SUCCEEDS(mThreadPool->Shutdown());
999 :
1000 0 : if (!mCompleteCallbacks.IsEmpty()) {
1001 : // Run all callbacks manually now.
1002 0 : for (uint32_t count = mCompleteCallbacks.Length(), index = 0;
1003 0 : index < count;
1004 : index++) {
1005 : nsAutoPtr<StoragesCompleteCallback> completeCallback(
1006 0 : mCompleteCallbacks[index].forget());
1007 0 : MOZ_ASSERT(completeCallback);
1008 0 : MOZ_ASSERT(completeCallback->mCallback);
1009 :
1010 0 : Unused << completeCallback->mCallback->Run();
1011 : }
1012 :
1013 0 : mCompleteCallbacks.Clear();
1014 :
1015 : // And make sure they get processed.
1016 0 : nsIThread* currentThread = NS_GetCurrentThread();
1017 0 : MOZ_ASSERT(currentThread);
1018 :
1019 0 : MOZ_ALWAYS_SUCCEEDS(NS_ProcessPendingEvents(currentThread));
1020 : }
1021 :
1022 0 : mShutdownComplete = true;
1023 0 : }
1024 :
1025 : void
1026 0 : FileHandleThreadPool::FinishFileHandle(FileHandle* aFileHandle)
1027 : {
1028 0 : AssertIsOnOwningThread();
1029 0 : MOZ_ASSERT(aFileHandle);
1030 :
1031 0 : BackgroundMutableFileParentBase* mutableFile = aFileHandle->GetMutableFile();
1032 0 : const nsACString& directoryId = mutableFile->DirectoryId();
1033 :
1034 : DirectoryInfo* directoryInfo;
1035 0 : if (!mDirectoryInfos.Get(directoryId, &directoryInfo)) {
1036 0 : NS_ERROR("We don't know anyting about this directory?!");
1037 0 : return;
1038 : }
1039 :
1040 0 : directoryInfo->RemoveFileHandleQueue(aFileHandle);
1041 :
1042 0 : if (!directoryInfo->HasRunningFileHandles()) {
1043 0 : mDirectoryInfos.Remove(directoryId);
1044 :
1045 : // See if we need to fire any complete callbacks.
1046 0 : uint32_t index = 0;
1047 0 : while (index < mCompleteCallbacks.Length()) {
1048 0 : if (MaybeFireCallback(mCompleteCallbacks[index])) {
1049 0 : mCompleteCallbacks.RemoveElementAt(index);
1050 : }
1051 : else {
1052 0 : index++;
1053 : }
1054 : }
1055 :
1056 0 : if (mShutdownRequested && !mDirectoryInfos.Count()) {
1057 0 : Cleanup();
1058 : }
1059 : }
1060 : }
1061 :
1062 : bool
1063 0 : FileHandleThreadPool::MaybeFireCallback(StoragesCompleteCallback* aCallback)
1064 : {
1065 0 : AssertIsOnOwningThread();
1066 0 : MOZ_ASSERT(aCallback);
1067 0 : MOZ_ASSERT(!aCallback->mDirectoryIds.IsEmpty());
1068 0 : MOZ_ASSERT(aCallback->mCallback);
1069 :
1070 0 : for (uint32_t count = aCallback->mDirectoryIds.Length(), index = 0;
1071 0 : index < count;
1072 : index++) {
1073 0 : const nsCString& directoryId = aCallback->mDirectoryIds[index];
1074 0 : MOZ_ASSERT(!directoryId.IsEmpty());
1075 :
1076 0 : if (mDirectoryInfos.Get(directoryId, nullptr)) {
1077 0 : return false;
1078 : }
1079 : }
1080 :
1081 0 : aCallback->mCallback->Run();
1082 0 : return true;
1083 : }
1084 :
1085 0 : FileHandleThreadPool::FileHandleQueue::FileHandleQueue(
1086 : FileHandleThreadPool* aFileHandleThreadPool,
1087 0 : FileHandle* aFileHandle)
1088 : : Runnable("dom::FileHandleThreadPool::FileHandleQueue")
1089 : , mOwningFileHandleThreadPool(aFileHandleThreadPool)
1090 : , mFileHandle(aFileHandle)
1091 0 : , mShouldFinish(false)
1092 : {
1093 0 : MOZ_ASSERT(aFileHandleThreadPool);
1094 0 : aFileHandleThreadPool->AssertIsOnOwningThread();
1095 0 : MOZ_ASSERT(aFileHandle);
1096 0 : }
1097 :
1098 : void
1099 0 : FileHandleThreadPool::
1100 : FileHandleQueue::Enqueue(FileHandleOp* aFileHandleOp)
1101 : {
1102 0 : MOZ_ASSERT(!mShouldFinish, "Enqueue called after Finish!");
1103 :
1104 0 : mQueue.AppendElement(aFileHandleOp);
1105 :
1106 0 : ProcessQueue();
1107 0 : }
1108 :
1109 : void
1110 0 : FileHandleThreadPool::
1111 : FileHandleQueue::Finish()
1112 : {
1113 0 : MOZ_ASSERT(!mShouldFinish, "Finish called more than once!");
1114 :
1115 0 : mShouldFinish = true;
1116 0 : }
1117 :
1118 : void
1119 0 : FileHandleThreadPool::
1120 : FileHandleQueue::ProcessQueue()
1121 : {
1122 0 : if (mCurrentOp) {
1123 0 : return;
1124 : }
1125 :
1126 0 : if (mQueue.IsEmpty()) {
1127 0 : if (mShouldFinish) {
1128 0 : mOwningFileHandleThreadPool->FinishFileHandle(mFileHandle);
1129 :
1130 : // Make sure this is released on this thread.
1131 0 : mOwningFileHandleThreadPool = nullptr;
1132 : }
1133 :
1134 0 : return;
1135 : }
1136 :
1137 0 : mCurrentOp = mQueue[0];
1138 0 : mQueue.RemoveElementAt(0);
1139 :
1140 0 : nsCOMPtr<nsIThreadPool> threadPool = mOwningFileHandleThreadPool->mThreadPool;
1141 0 : MOZ_ASSERT(threadPool);
1142 :
1143 0 : MOZ_ALWAYS_SUCCEEDS(threadPool->Dispatch(this, NS_DISPATCH_NORMAL));
1144 : }
1145 :
1146 : NS_IMETHODIMP
1147 0 : FileHandleThreadPool::
1148 : FileHandleQueue::Run()
1149 : {
1150 0 : MOZ_ASSERT(mCurrentOp);
1151 :
1152 0 : if (IsOnBackgroundThread()) {
1153 0 : RefPtr<FileHandleOp> currentOp;
1154 :
1155 0 : mCurrentOp.swap(currentOp);
1156 0 : ProcessQueue();
1157 :
1158 0 : currentOp->RunOnOwningThread();
1159 : } else {
1160 0 : mCurrentOp->RunOnThreadPool();
1161 :
1162 0 : nsCOMPtr<nsIEventTarget> backgroundThread = mCurrentOp->OwningThread();
1163 :
1164 0 : MOZ_ALWAYS_SUCCEEDS(
1165 : backgroundThread->Dispatch(this, NS_DISPATCH_NORMAL));
1166 : }
1167 :
1168 0 : return NS_OK;
1169 : }
1170 :
1171 : auto
1172 0 : FileHandleThreadPool::
1173 : DirectoryInfo::CreateFileHandleQueue(FileHandle* aFileHandle)
1174 : -> FileHandleQueue*
1175 : {
1176 : RefPtr<FileHandleQueue>* fileHandleQueue =
1177 0 : mFileHandleQueues.AppendElement();
1178 : *fileHandleQueue = new FileHandleQueue(mOwningFileHandleThreadPool,
1179 0 : aFileHandle);
1180 0 : return fileHandleQueue->get();
1181 : }
1182 :
1183 : auto
1184 0 : FileHandleThreadPool::
1185 : DirectoryInfo::GetFileHandleQueue(FileHandle* aFileHandle) -> FileHandleQueue*
1186 : {
1187 0 : uint32_t count = mFileHandleQueues.Length();
1188 0 : for (uint32_t index = 0; index < count; index++) {
1189 0 : RefPtr<FileHandleQueue>& fileHandleQueue = mFileHandleQueues[index];
1190 0 : if (fileHandleQueue->mFileHandle == aFileHandle) {
1191 0 : return fileHandleQueue;
1192 : }
1193 : }
1194 0 : return nullptr;
1195 : }
1196 :
1197 : void
1198 0 : FileHandleThreadPool::
1199 : DirectoryInfo::RemoveFileHandleQueue(FileHandle* aFileHandle)
1200 : {
1201 0 : for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
1202 0 : if (mDelayedEnqueueInfos[index].mFileHandle == aFileHandle) {
1203 0 : MOZ_ASSERT(!mDelayedEnqueueInfos[index].mFileHandleOp, "Should be null!");
1204 0 : mDelayedEnqueueInfos.RemoveElementAt(index);
1205 0 : return;
1206 : }
1207 : }
1208 :
1209 0 : uint32_t fileHandleCount = mFileHandleQueues.Length();
1210 :
1211 : // We can't just remove entries from lock hash tables, we have to rebuild
1212 : // them instead. Multiple FileHandle objects may lock the same file
1213 : // (one entry can represent multiple locks).
1214 :
1215 0 : mFilesReading.Clear();
1216 0 : mFilesWriting.Clear();
1217 :
1218 0 : for (uint32_t index = 0, count = fileHandleCount; index < count; index++) {
1219 0 : FileHandle* fileHandle = mFileHandleQueues[index]->mFileHandle;
1220 0 : if (fileHandle == aFileHandle) {
1221 0 : MOZ_ASSERT(count == fileHandleCount, "More than one match?!");
1222 :
1223 0 : mFileHandleQueues.RemoveElementAt(index);
1224 0 : index--;
1225 0 : count--;
1226 :
1227 0 : continue;
1228 : }
1229 :
1230 0 : const nsAString& fileName = fileHandle->GetMutableFile()->FileName();
1231 :
1232 0 : if (fileHandle->Mode() == FileMode::Readwrite) {
1233 0 : if (!IsFileLockedForWriting(fileName)) {
1234 0 : LockFileForWriting(fileName);
1235 : }
1236 : }
1237 : else {
1238 0 : if (!IsFileLockedForReading(fileName)) {
1239 0 : LockFileForReading(fileName);
1240 : }
1241 : }
1242 : }
1243 :
1244 0 : MOZ_ASSERT(mFileHandleQueues.Length() == fileHandleCount - 1,
1245 : "Didn't find the file handle we were looking for!");
1246 :
1247 0 : nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
1248 0 : delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
1249 :
1250 0 : for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) {
1251 0 : DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
1252 0 : mOwningFileHandleThreadPool->Enqueue(delayedEnqueueInfo.mFileHandle,
1253 : delayedEnqueueInfo.mFileHandleOp,
1254 0 : delayedEnqueueInfo.mFinish);
1255 : }
1256 : }
1257 :
1258 : auto
1259 0 : FileHandleThreadPool::
1260 : DirectoryInfo::CreateDelayedEnqueueInfo(FileHandle* aFileHandle,
1261 : FileHandleOp* aFileHandleOp,
1262 : bool aFinish) -> DelayedEnqueueInfo*
1263 : {
1264 0 : DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
1265 0 : info->mFileHandle = aFileHandle;
1266 0 : info->mFileHandleOp = aFileHandleOp;
1267 0 : info->mFinish = aFinish;
1268 0 : return info;
1269 : }
1270 :
1271 0 : FileHandleThreadPool::
1272 : StoragesCompleteCallback::StoragesCompleteCallback(
1273 : nsTArray<nsCString>&& aDirectoryIds,
1274 0 : nsIRunnable* aCallback)
1275 0 : : mDirectoryIds(Move(aDirectoryIds))
1276 0 : , mCallback(aCallback)
1277 : {
1278 0 : AssertIsOnBackgroundThread();
1279 0 : MOZ_ASSERT(!mDirectoryIds.IsEmpty());
1280 0 : MOZ_ASSERT(aCallback);
1281 :
1282 0 : MOZ_COUNT_CTOR(FileHandleThreadPool::StoragesCompleteCallback);
1283 0 : }
1284 :
1285 0 : FileHandleThreadPool::
1286 0 : StoragesCompleteCallback::~StoragesCompleteCallback()
1287 : {
1288 0 : AssertIsOnBackgroundThread();
1289 :
1290 0 : MOZ_COUNT_DTOR(FileHandleThreadPool::StoragesCompleteCallback);
1291 0 : }
1292 :
1293 : /*******************************************************************************
1294 : * BackgroundMutableFileParentBase
1295 : ******************************************************************************/
1296 :
1297 0 : BackgroundMutableFileParentBase::BackgroundMutableFileParentBase(
1298 : FileHandleStorage aStorage,
1299 : const nsACString& aDirectoryId,
1300 : const nsAString& aFileName,
1301 0 : nsIFile* aFile)
1302 : : mDirectoryId(aDirectoryId)
1303 : , mFileName(aFileName)
1304 : , mStorage(aStorage)
1305 : , mInvalidated(false)
1306 : , mActorWasAlive(false)
1307 : , mActorDestroyed(false)
1308 0 : , mFile(aFile)
1309 : {
1310 0 : AssertIsOnBackgroundThread();
1311 0 : MOZ_ASSERT(aStorage != FILE_HANDLE_STORAGE_MAX);
1312 0 : MOZ_ASSERT(!aDirectoryId.IsEmpty());
1313 0 : MOZ_ASSERT(!aFileName.IsEmpty());
1314 0 : MOZ_ASSERT(aFile);
1315 0 : }
1316 :
1317 0 : BackgroundMutableFileParentBase::~BackgroundMutableFileParentBase()
1318 : {
1319 0 : MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
1320 0 : }
1321 :
1322 : void
1323 0 : BackgroundMutableFileParentBase::Invalidate()
1324 : {
1325 0 : AssertIsOnBackgroundThread();
1326 :
1327 : class MOZ_STACK_CLASS Helper final
1328 : {
1329 : public:
1330 : static bool
1331 0 : InvalidateFileHandles(nsTHashtable<nsPtrHashKey<FileHandle>>& aTable)
1332 : {
1333 0 : AssertIsOnBackgroundThread();
1334 :
1335 0 : const uint32_t count = aTable.Count();
1336 0 : if (!count) {
1337 0 : return true;
1338 : }
1339 :
1340 0 : FallibleTArray<RefPtr<FileHandle>> fileHandles;
1341 0 : if (NS_WARN_IF(!fileHandles.SetCapacity(count, fallible))) {
1342 0 : return false;
1343 : }
1344 :
1345 0 : for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
1346 0 : if (NS_WARN_IF(!fileHandles.AppendElement(iter.Get()->GetKey(),
1347 : fallible))) {
1348 0 : return false;
1349 : }
1350 : }
1351 :
1352 0 : if (count) {
1353 0 : for (uint32_t index = 0; index < count; index++) {
1354 0 : RefPtr<FileHandle> fileHandle = fileHandles[index].forget();
1355 0 : MOZ_ASSERT(fileHandle);
1356 :
1357 0 : fileHandle->Invalidate();
1358 : }
1359 : }
1360 :
1361 0 : return true;
1362 : }
1363 : };
1364 :
1365 0 : if (mInvalidated) {
1366 0 : return;
1367 : }
1368 :
1369 0 : mInvalidated = true;
1370 :
1371 0 : if (!Helper::InvalidateFileHandles(mFileHandles)) {
1372 0 : NS_WARNING("Failed to abort all file handles!");
1373 : }
1374 : }
1375 :
1376 : bool
1377 0 : BackgroundMutableFileParentBase::RegisterFileHandle(FileHandle* aFileHandle)
1378 : {
1379 0 : AssertIsOnBackgroundThread();
1380 0 : MOZ_ASSERT(aFileHandle);
1381 0 : MOZ_ASSERT(!mFileHandles.GetEntry(aFileHandle));
1382 0 : MOZ_ASSERT(!mInvalidated);
1383 :
1384 0 : if (NS_WARN_IF(!mFileHandles.PutEntry(aFileHandle, fallible))) {
1385 0 : return false;
1386 : }
1387 :
1388 0 : if (mFileHandles.Count() == 1) {
1389 0 : NoteActiveState();
1390 : }
1391 :
1392 0 : return true;
1393 : }
1394 :
1395 : void
1396 0 : BackgroundMutableFileParentBase::UnregisterFileHandle(FileHandle* aFileHandle)
1397 : {
1398 0 : AssertIsOnBackgroundThread();
1399 0 : MOZ_ASSERT(aFileHandle);
1400 0 : MOZ_ASSERT(mFileHandles.GetEntry(aFileHandle));
1401 :
1402 0 : mFileHandles.RemoveEntry(aFileHandle);
1403 :
1404 0 : if (!mFileHandles.Count()) {
1405 0 : NoteInactiveState();
1406 : }
1407 0 : }
1408 :
1409 : void
1410 0 : BackgroundMutableFileParentBase::SetActorAlive()
1411 : {
1412 0 : AssertIsOnBackgroundThread();
1413 0 : MOZ_ASSERT(!mActorWasAlive);
1414 0 : MOZ_ASSERT(!mActorDestroyed);
1415 :
1416 0 : mActorWasAlive = true;
1417 :
1418 : // This reference will be absorbed by IPDL and released when the actor is
1419 : // destroyed.
1420 0 : AddRef();
1421 0 : }
1422 :
1423 : already_AddRefed<nsISupports>
1424 0 : BackgroundMutableFileParentBase::CreateStream(bool aReadOnly)
1425 : {
1426 0 : AssertIsOnBackgroundThread();
1427 :
1428 : nsresult rv;
1429 :
1430 0 : if (aReadOnly) {
1431 0 : nsCOMPtr<nsIInputStream> stream;
1432 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1,
1433 0 : nsIFileInputStream::DEFER_OPEN);
1434 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1435 0 : return nullptr;
1436 : }
1437 0 : return stream.forget();
1438 : }
1439 :
1440 0 : nsCOMPtr<nsIFileStream> stream;
1441 0 : rv = NS_NewLocalFileStream(getter_AddRefs(stream), mFile, -1, -1,
1442 0 : nsIFileStream::DEFER_OPEN);
1443 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1444 0 : return nullptr;
1445 : }
1446 0 : return stream.forget();
1447 : }
1448 :
1449 : void
1450 0 : BackgroundMutableFileParentBase::ActorDestroy(ActorDestroyReason aWhy)
1451 : {
1452 0 : AssertIsOnBackgroundThread();
1453 0 : MOZ_ASSERT(!mActorDestroyed);
1454 :
1455 0 : mActorDestroyed = true;
1456 :
1457 0 : if (!IsInvalidated()) {
1458 0 : Invalidate();
1459 : }
1460 0 : }
1461 :
1462 : PBackgroundFileHandleParent*
1463 0 : BackgroundMutableFileParentBase::AllocPBackgroundFileHandleParent(
1464 : const FileMode& aMode)
1465 : {
1466 0 : AssertIsOnBackgroundThread();
1467 :
1468 0 : if (NS_WARN_IF(aMode != FileMode::Readonly &&
1469 : aMode != FileMode::Readwrite)) {
1470 0 : ASSERT_UNLESS_FUZZING();
1471 : return nullptr;
1472 : }
1473 :
1474 0 : RefPtr<FileHandle> fileHandle = new FileHandle(this, aMode);
1475 :
1476 0 : return fileHandle.forget().take();
1477 : }
1478 :
1479 : mozilla::ipc::IPCResult
1480 0 : BackgroundMutableFileParentBase::RecvPBackgroundFileHandleConstructor(
1481 : PBackgroundFileHandleParent* aActor,
1482 : const FileMode& aMode)
1483 : {
1484 0 : AssertIsOnBackgroundThread();
1485 0 : MOZ_ASSERT(aActor);
1486 0 : MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
1487 :
1488 : FileHandleThreadPool* fileHandleThreadPool =
1489 0 : GetFileHandleThreadPoolFor(mStorage);
1490 0 : MOZ_ASSERT(fileHandleThreadPool);
1491 :
1492 0 : auto* fileHandle = static_cast<FileHandle*>(aActor);
1493 :
1494 : // Add a placeholder for this file handle immediately.
1495 0 : fileHandleThreadPool->Enqueue(fileHandle, nullptr, false);
1496 :
1497 0 : fileHandle->SetActive();
1498 :
1499 0 : if (NS_WARN_IF(!RegisterFileHandle(fileHandle))) {
1500 0 : fileHandle->Abort(/* aForce */ false);
1501 0 : return IPC_OK();
1502 : }
1503 :
1504 0 : return IPC_OK();
1505 : }
1506 :
1507 : bool
1508 0 : BackgroundMutableFileParentBase::DeallocPBackgroundFileHandleParent(
1509 : PBackgroundFileHandleParent* aActor)
1510 : {
1511 0 : AssertIsOnBackgroundThread();
1512 0 : MOZ_ASSERT(aActor);
1513 :
1514 : RefPtr<FileHandle> fileHandle =
1515 0 : dont_AddRef(static_cast<FileHandle*>(aActor));
1516 0 : return true;
1517 : }
1518 :
1519 : mozilla::ipc::IPCResult
1520 0 : BackgroundMutableFileParentBase::RecvDeleteMe()
1521 : {
1522 0 : AssertIsOnBackgroundThread();
1523 0 : MOZ_ASSERT(!mActorDestroyed);
1524 :
1525 0 : IProtocol* mgr = Manager();
1526 0 : if (!PBackgroundMutableFileParent::Send__delete__(this)) {
1527 0 : return IPC_FAIL_NO_REASON(mgr);
1528 : }
1529 0 : return IPC_OK();
1530 : }
1531 :
1532 : mozilla::ipc::IPCResult
1533 0 : BackgroundMutableFileParentBase::RecvGetFileId(int64_t* aFileId)
1534 : {
1535 0 : AssertIsOnBackgroundThread();
1536 :
1537 0 : *aFileId = -1;
1538 0 : return IPC_OK();
1539 : }
1540 :
1541 : /*******************************************************************************
1542 : * FileHandle
1543 : ******************************************************************************/
1544 :
1545 0 : FileHandle::FileHandle(BackgroundMutableFileParentBase* aMutableFile,
1546 0 : FileMode aMode)
1547 : : mMutableFile(aMutableFile)
1548 : , mActiveRequestCount(0)
1549 0 : , mStorage(aMutableFile->Storage())
1550 : , mInvalidatedOnAnyThread(false)
1551 : , mMode(aMode)
1552 : , mHasBeenActive(false)
1553 : , mActorDestroyed(false)
1554 : , mInvalidated(false)
1555 : , mAborted(false)
1556 : , mFinishOrAbortReceived(false)
1557 : , mFinishedOrAborted(false)
1558 0 : , mForceAborted(false)
1559 : {
1560 0 : AssertIsOnBackgroundThread();
1561 0 : MOZ_ASSERT(aMutableFile);
1562 :
1563 : #ifdef DEBUG
1564 : FileHandleThreadPool* fileHandleThreadPool =
1565 0 : GetFileHandleThreadPoolFor(mStorage);
1566 0 : MOZ_ASSERT(fileHandleThreadPool);
1567 :
1568 0 : mThreadPoolEventTarget = fileHandleThreadPool->GetThreadPoolEventTarget();
1569 : #endif
1570 0 : }
1571 :
1572 0 : FileHandle::~FileHandle()
1573 : {
1574 0 : MOZ_ASSERT(!mActiveRequestCount);
1575 0 : MOZ_ASSERT(mActorDestroyed);
1576 0 : MOZ_ASSERT_IF(mHasBeenActive, mFinishedOrAborted);
1577 0 : }
1578 :
1579 : void
1580 0 : FileHandle::AssertIsOnThreadPool() const
1581 : {
1582 0 : MOZ_ASSERT(mThreadPoolEventTarget);
1583 0 : DebugOnly<bool> current;
1584 0 : MOZ_ASSERT(NS_SUCCEEDED(mThreadPoolEventTarget->IsOnCurrentThread(¤t)));
1585 0 : MOZ_ASSERT(current);
1586 0 : }
1587 :
1588 : nsresult
1589 0 : FileHandle::GetOrCreateStream(nsISupports** aStream)
1590 : {
1591 0 : AssertIsOnBackgroundThread();
1592 :
1593 0 : if (!mStream) {
1594 : nsCOMPtr<nsISupports> stream =
1595 0 : mMutableFile->CreateStream(mMode == FileMode::Readonly);
1596 0 : if (NS_WARN_IF(!stream)) {
1597 0 : return NS_ERROR_FAILURE;
1598 : }
1599 :
1600 0 : stream.swap(mStream);
1601 : }
1602 :
1603 0 : nsCOMPtr<nsISupports> stream(mStream);
1604 0 : stream.forget(aStream);
1605 :
1606 0 : return NS_OK;
1607 : }
1608 :
1609 : void
1610 0 : FileHandle::Abort(bool aForce)
1611 : {
1612 0 : AssertIsOnBackgroundThread();
1613 :
1614 0 : mAborted = true;
1615 :
1616 0 : if (aForce) {
1617 0 : mForceAborted = true;
1618 : }
1619 :
1620 0 : MaybeFinishOrAbort();
1621 0 : }
1622 :
1623 : void
1624 0 : FileHandle::NoteActiveRequest()
1625 : {
1626 0 : AssertIsOnBackgroundThread();
1627 0 : MOZ_ASSERT(mActiveRequestCount < UINT64_MAX);
1628 :
1629 0 : mActiveRequestCount++;
1630 0 : }
1631 :
1632 : void
1633 0 : FileHandle::NoteFinishedRequest()
1634 : {
1635 0 : AssertIsOnBackgroundThread();
1636 0 : MOZ_ASSERT(mActiveRequestCount);
1637 :
1638 0 : mActiveRequestCount--;
1639 :
1640 0 : MaybeFinishOrAbort();
1641 0 : }
1642 :
1643 : void
1644 0 : FileHandle::Invalidate()
1645 : {
1646 0 : AssertIsOnBackgroundThread();
1647 0 : MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread);
1648 :
1649 0 : if (!mInvalidated) {
1650 0 : mInvalidated = true;
1651 0 : mInvalidatedOnAnyThread = true;
1652 :
1653 0 : Abort(/* aForce */ true);
1654 : }
1655 0 : }
1656 :
1657 : void
1658 0 : FileHandle::SendCompleteNotification(bool aAborted)
1659 : {
1660 0 : AssertIsOnBackgroundThread();
1661 :
1662 0 : if (!IsActorDestroyed()) {
1663 0 : Unused << SendComplete(aAborted);
1664 : }
1665 0 : }
1666 :
1667 : bool
1668 0 : FileHandle::VerifyRequestParams(const FileRequestParams& aParams) const
1669 : {
1670 0 : AssertIsOnBackgroundThread();
1671 0 : MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
1672 :
1673 0 : switch (aParams.type()) {
1674 : case FileRequestParams::TFileRequestGetMetadataParams: {
1675 : const FileRequestGetMetadataParams& params =
1676 0 : aParams.get_FileRequestGetMetadataParams();
1677 :
1678 0 : if (NS_WARN_IF(!params.size() && !params.lastModified())) {
1679 0 : ASSERT_UNLESS_FUZZING();
1680 : return false;
1681 : }
1682 :
1683 0 : break;
1684 : }
1685 :
1686 : case FileRequestParams::TFileRequestReadParams: {
1687 : const FileRequestReadParams& params =
1688 0 : aParams.get_FileRequestReadParams();
1689 :
1690 0 : if (NS_WARN_IF(params.offset() == UINT64_MAX)) {
1691 0 : ASSERT_UNLESS_FUZZING();
1692 : return false;
1693 : }
1694 :
1695 0 : if (NS_WARN_IF(!params.size())) {
1696 0 : ASSERT_UNLESS_FUZZING();
1697 : return false;
1698 : }
1699 :
1700 0 : break;
1701 : }
1702 :
1703 : case FileRequestParams::TFileRequestWriteParams: {
1704 0 : if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
1705 0 : ASSERT_UNLESS_FUZZING();
1706 : return false;
1707 : }
1708 :
1709 : const FileRequestWriteParams& params =
1710 0 : aParams.get_FileRequestWriteParams();
1711 :
1712 :
1713 0 : if (NS_WARN_IF(!params.dataLength())) {
1714 0 : ASSERT_UNLESS_FUZZING();
1715 : return false;
1716 : }
1717 :
1718 0 : if (NS_WARN_IF(!VerifyRequestData(params.data()))) {
1719 0 : ASSERT_UNLESS_FUZZING();
1720 : return false;
1721 : }
1722 :
1723 0 : break;
1724 : }
1725 :
1726 : case FileRequestParams::TFileRequestTruncateParams: {
1727 0 : if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
1728 0 : ASSERT_UNLESS_FUZZING();
1729 : return false;
1730 : }
1731 :
1732 : const FileRequestTruncateParams& params =
1733 0 : aParams.get_FileRequestTruncateParams();
1734 :
1735 0 : if (NS_WARN_IF(params.offset() == UINT64_MAX)) {
1736 0 : ASSERT_UNLESS_FUZZING();
1737 : return false;
1738 : }
1739 :
1740 0 : break;
1741 : }
1742 :
1743 : case FileRequestParams::TFileRequestFlushParams: {
1744 0 : if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
1745 0 : ASSERT_UNLESS_FUZZING();
1746 : return false;
1747 : }
1748 :
1749 0 : break;
1750 : }
1751 :
1752 : case FileRequestParams::TFileRequestGetFileParams: {
1753 0 : break;
1754 : }
1755 :
1756 : default:
1757 0 : MOZ_CRASH("Should never get here!");
1758 : }
1759 :
1760 0 : return true;
1761 : }
1762 :
1763 : bool
1764 0 : FileHandle::VerifyRequestData(const FileRequestData& aData) const
1765 : {
1766 0 : AssertIsOnBackgroundThread();
1767 0 : MOZ_ASSERT(aData.type() != FileRequestData::T__None);
1768 :
1769 0 : switch (aData.type()) {
1770 : case FileRequestData::TFileRequestStringData: {
1771 : const FileRequestStringData& data =
1772 0 : aData.get_FileRequestStringData();
1773 :
1774 0 : if (NS_WARN_IF(data.string().IsEmpty())) {
1775 0 : ASSERT_UNLESS_FUZZING();
1776 : return false;
1777 : }
1778 :
1779 0 : break;
1780 : }
1781 :
1782 : case FileRequestData::TFileRequestBlobData: {
1783 0 : break;
1784 : }
1785 :
1786 : default:
1787 0 : MOZ_CRASH("Should never get here!");
1788 : }
1789 :
1790 0 : return true;
1791 : }
1792 :
1793 : void
1794 0 : FileHandle::FinishOrAbort()
1795 : {
1796 0 : AssertIsOnBackgroundThread();
1797 0 : MOZ_ASSERT(!mFinishedOrAborted);
1798 :
1799 0 : mFinishedOrAborted = true;
1800 :
1801 0 : if (!mHasBeenActive) {
1802 0 : return;
1803 : }
1804 :
1805 0 : RefPtr<FinishOp> finishOp = new FinishOp(this, mAborted);
1806 :
1807 : FileHandleThreadPool* fileHandleThreadPool =
1808 0 : GetFileHandleThreadPoolFor(mStorage);
1809 0 : MOZ_ASSERT(fileHandleThreadPool);
1810 :
1811 0 : fileHandleThreadPool->Enqueue(this, finishOp, true);
1812 : }
1813 :
1814 : void
1815 0 : FileHandle::ActorDestroy(ActorDestroyReason aWhy)
1816 : {
1817 0 : AssertIsOnBackgroundThread();
1818 :
1819 0 : MOZ_ASSERT(!mActorDestroyed);
1820 :
1821 0 : mActorDestroyed = true;
1822 :
1823 0 : if (!mFinishedOrAborted) {
1824 0 : mAborted = true;
1825 :
1826 0 : mForceAborted = true;
1827 :
1828 0 : MaybeFinishOrAbort();
1829 : }
1830 0 : }
1831 :
1832 : mozilla::ipc::IPCResult
1833 0 : FileHandle::RecvDeleteMe()
1834 : {
1835 0 : AssertIsOnBackgroundThread();
1836 0 : MOZ_ASSERT(!IsActorDestroyed());
1837 :
1838 0 : IProtocol* mgr = Manager();
1839 0 : if (!PBackgroundFileHandleParent::Send__delete__(this)) {
1840 0 : return IPC_FAIL_NO_REASON(mgr);
1841 : }
1842 0 : return IPC_OK();
1843 : }
1844 :
1845 : mozilla::ipc::IPCResult
1846 0 : FileHandle::RecvFinish()
1847 : {
1848 0 : AssertIsOnBackgroundThread();
1849 :
1850 0 : if (NS_WARN_IF(mFinishOrAbortReceived)) {
1851 0 : ASSERT_UNLESS_FUZZING();
1852 : return IPC_FAIL_NO_REASON(this);
1853 : }
1854 :
1855 0 : mFinishOrAbortReceived = true;
1856 :
1857 0 : MaybeFinishOrAbort();
1858 0 : return IPC_OK();
1859 : }
1860 :
1861 : mozilla::ipc::IPCResult
1862 0 : FileHandle::RecvAbort()
1863 : {
1864 0 : AssertIsOnBackgroundThread();
1865 :
1866 0 : if (NS_WARN_IF(mFinishOrAbortReceived)) {
1867 0 : ASSERT_UNLESS_FUZZING();
1868 : return IPC_FAIL_NO_REASON(this);
1869 : }
1870 :
1871 0 : mFinishOrAbortReceived = true;
1872 :
1873 0 : Abort(/* aForce */ false);
1874 0 : return IPC_OK();
1875 : }
1876 :
1877 : PBackgroundFileRequestParent*
1878 0 : FileHandle::AllocPBackgroundFileRequestParent(const FileRequestParams& aParams)
1879 : {
1880 0 : AssertIsOnBackgroundThread();
1881 0 : MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
1882 :
1883 : #ifdef DEBUG
1884 : // Always verify parameters in DEBUG builds!
1885 0 : bool trustParams = false;
1886 : #else
1887 : PBackgroundParent* backgroundActor = GetBackgroundParent();
1888 : MOZ_ASSERT(backgroundActor);
1889 :
1890 : bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
1891 : #endif
1892 :
1893 0 : if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
1894 0 : ASSERT_UNLESS_FUZZING();
1895 : return nullptr;
1896 : }
1897 :
1898 0 : if (NS_WARN_IF(mFinishOrAbortReceived)) {
1899 0 : ASSERT_UNLESS_FUZZING();
1900 : return nullptr;
1901 : }
1902 :
1903 0 : RefPtr<NormalFileHandleOp> actor;
1904 :
1905 0 : switch (aParams.type()) {
1906 : case FileRequestParams::TFileRequestGetMetadataParams:
1907 0 : actor = new GetMetadataOp(this, aParams);
1908 0 : break;
1909 :
1910 : case FileRequestParams::TFileRequestReadParams:
1911 0 : actor = new ReadOp(this, aParams);
1912 0 : break;
1913 :
1914 : case FileRequestParams::TFileRequestWriteParams:
1915 0 : actor = new WriteOp(this, aParams);
1916 0 : break;
1917 :
1918 : case FileRequestParams::TFileRequestTruncateParams:
1919 0 : actor = new TruncateOp(this, aParams);
1920 0 : break;
1921 :
1922 : case FileRequestParams::TFileRequestFlushParams:
1923 0 : actor = new FlushOp(this, aParams);
1924 0 : break;
1925 :
1926 : case FileRequestParams::TFileRequestGetFileParams:
1927 0 : actor = new GetFileOp(this, aParams);
1928 0 : break;
1929 :
1930 : default:
1931 0 : MOZ_CRASH("Should never get here!");
1932 : }
1933 :
1934 0 : MOZ_ASSERT(actor);
1935 :
1936 : // Transfer ownership to IPDL.
1937 0 : return actor.forget().take();
1938 : }
1939 :
1940 : mozilla::ipc::IPCResult
1941 0 : FileHandle::RecvPBackgroundFileRequestConstructor(
1942 : PBackgroundFileRequestParent* aActor,
1943 : const FileRequestParams& aParams)
1944 : {
1945 0 : AssertIsOnBackgroundThread();
1946 0 : MOZ_ASSERT(aActor);
1947 0 : MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
1948 :
1949 0 : auto* op = static_cast<NormalFileHandleOp*>(aActor);
1950 :
1951 0 : if (NS_WARN_IF(!op->Init(this))) {
1952 0 : op->Cleanup();
1953 0 : return IPC_FAIL_NO_REASON(this);
1954 : }
1955 :
1956 0 : op->Enqueue();
1957 0 : return IPC_OK();
1958 : }
1959 :
1960 : bool
1961 0 : FileHandle::DeallocPBackgroundFileRequestParent(
1962 : PBackgroundFileRequestParent* aActor)
1963 : {
1964 0 : AssertIsOnBackgroundThread();
1965 0 : MOZ_ASSERT(aActor);
1966 :
1967 : // Transfer ownership back from IPDL.
1968 : RefPtr<NormalFileHandleOp> actor =
1969 0 : dont_AddRef(static_cast<NormalFileHandleOp*>(aActor));
1970 0 : return true;
1971 : }
1972 :
1973 : /*******************************************************************************
1974 : * Local class implementations
1975 : ******************************************************************************/
1976 :
1977 : void
1978 0 : FileHandleOp::Enqueue()
1979 : {
1980 0 : AssertIsOnOwningThread();
1981 :
1982 : FileHandleThreadPool* fileHandleThreadPool =
1983 0 : GetFileHandleThreadPoolFor(mFileHandle->Storage());
1984 0 : MOZ_ASSERT(fileHandleThreadPool);
1985 :
1986 0 : fileHandleThreadPool->Enqueue(mFileHandle, this, false);
1987 :
1988 0 : mFileHandle->NoteActiveRequest();
1989 0 : }
1990 :
1991 : void
1992 0 : FileHandle::
1993 : FinishOp::RunOnThreadPool()
1994 : {
1995 0 : AssertIsOnThreadPool();
1996 0 : MOZ_ASSERT(mFileHandle);
1997 :
1998 0 : nsCOMPtr<nsISupports>& stream = mFileHandle->mStream;
1999 :
2000 0 : if (!stream) {
2001 0 : return;
2002 : }
2003 :
2004 0 : nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(stream);
2005 0 : MOZ_ASSERT(inputStream);
2006 :
2007 0 : MOZ_ALWAYS_SUCCEEDS(inputStream->Close());
2008 :
2009 0 : stream = nullptr;
2010 : }
2011 :
2012 : void
2013 0 : FileHandle::
2014 : FinishOp::RunOnOwningThread()
2015 : {
2016 0 : AssertIsOnOwningThread();
2017 0 : MOZ_ASSERT(mFileHandle);
2018 :
2019 0 : mFileHandle->SendCompleteNotification(mAborted);
2020 :
2021 0 : mFileHandle->GetMutableFile()->UnregisterFileHandle(mFileHandle);
2022 :
2023 0 : mFileHandle = nullptr;
2024 0 : }
2025 :
2026 0 : NormalFileHandleOp::~NormalFileHandleOp()
2027 : {
2028 0 : MOZ_ASSERT(!mFileHandle,
2029 : "NormalFileHandleOp::Cleanup() was not called by a subclass!");
2030 0 : }
2031 :
2032 : bool
2033 0 : NormalFileHandleOp::Init(FileHandle* aFileHandle)
2034 : {
2035 0 : AssertIsOnOwningThread();
2036 0 : MOZ_ASSERT(aFileHandle);
2037 :
2038 0 : nsresult rv = aFileHandle->GetOrCreateStream(getter_AddRefs(mFileStream));
2039 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2040 0 : return false;
2041 : }
2042 :
2043 0 : return true;
2044 : }
2045 :
2046 : void
2047 0 : NormalFileHandleOp::Cleanup()
2048 : {
2049 0 : AssertIsOnOwningThread();
2050 0 : MOZ_ASSERT(mFileHandle);
2051 0 : MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent);
2052 :
2053 0 : mFileHandle = nullptr;
2054 0 : }
2055 :
2056 : nsresult
2057 0 : NormalFileHandleOp::SendSuccessResult()
2058 : {
2059 0 : AssertIsOnOwningThread();
2060 :
2061 0 : if (!IsActorDestroyed()) {
2062 0 : FileRequestResponse response;
2063 0 : GetResponse(response);
2064 :
2065 0 : MOZ_ASSERT(response.type() != FileRequestResponse::T__None);
2066 :
2067 0 : if (response.type() == FileRequestResponse::Tnsresult) {
2068 0 : MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
2069 :
2070 0 : return response.get_nsresult();
2071 : }
2072 :
2073 0 : if (NS_WARN_IF(!PBackgroundFileRequestParent::Send__delete__(this,
2074 : response))) {
2075 0 : return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
2076 : }
2077 : }
2078 :
2079 : #ifdef DEBUG
2080 0 : mResponseSent = true;
2081 : #endif
2082 :
2083 0 : return NS_OK;
2084 : }
2085 :
2086 : bool
2087 0 : NormalFileHandleOp::SendFailureResult(nsresult aResultCode)
2088 : {
2089 0 : AssertIsOnBackgroundThread();
2090 0 : MOZ_ASSERT(NS_FAILED(aResultCode));
2091 :
2092 0 : bool result = false;
2093 :
2094 0 : if (!IsActorDestroyed()) {
2095 : result =
2096 0 : PBackgroundFileRequestParent::Send__delete__(this, aResultCode);
2097 : }
2098 :
2099 : #ifdef DEBUG
2100 0 : mResponseSent = true;
2101 : #endif
2102 :
2103 0 : return result;
2104 : }
2105 :
2106 : void
2107 0 : NormalFileHandleOp::RunOnThreadPool()
2108 : {
2109 0 : AssertIsOnThreadPool();
2110 0 : MOZ_ASSERT(mFileHandle);
2111 0 : MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
2112 :
2113 : // There are several cases where we don't actually have to to any work here.
2114 :
2115 0 : if (mFileHandleIsAborted) {
2116 : // This transaction is already set to be aborted.
2117 0 : mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
2118 0 : } else if (mFileHandle->IsInvalidatedOnAnyThread()) {
2119 : // This file handle is being invalidated.
2120 0 : mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
2121 0 : } else if (!OperationMayProceed()) {
2122 : // The operation was canceled in some way, likely because the child process
2123 : // has crashed.
2124 0 : mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
2125 : } else {
2126 0 : nsresult rv = DoFileWork(mFileHandle);
2127 0 : if (NS_FAILED(rv)) {
2128 0 : mResultCode = rv;
2129 : }
2130 : }
2131 0 : }
2132 :
2133 : void
2134 0 : NormalFileHandleOp::RunOnOwningThread()
2135 : {
2136 0 : AssertIsOnOwningThread();
2137 0 : MOZ_ASSERT(mFileHandle);
2138 :
2139 0 : if (NS_WARN_IF(IsActorDestroyed())) {
2140 : // Don't send any notifications if the actor was destroyed already.
2141 0 : if (NS_SUCCEEDED(mResultCode)) {
2142 0 : mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
2143 : }
2144 : } else {
2145 0 : if (mFileHandle->IsInvalidated()) {
2146 0 : mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
2147 0 : } else if (mFileHandle->IsAborted()) {
2148 : // Aborted file handles always see their requests fail with ABORT_ERR,
2149 : // even if the request succeeded or failed with another error.
2150 0 : mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
2151 0 : } else if (NS_SUCCEEDED(mResultCode)) {
2152 : // This may release the IPDL reference.
2153 0 : mResultCode = SendSuccessResult();
2154 : }
2155 :
2156 0 : if (NS_FAILED(mResultCode)) {
2157 : // This should definitely release the IPDL reference.
2158 0 : if (!SendFailureResult(mResultCode)) {
2159 : // Abort the file handle.
2160 0 : mFileHandle->Abort(/* aForce */ false);
2161 : }
2162 : }
2163 : }
2164 :
2165 0 : mFileHandle->NoteFinishedRequest();
2166 :
2167 0 : Cleanup();
2168 0 : }
2169 :
2170 : void
2171 0 : NormalFileHandleOp::ActorDestroy(ActorDestroyReason aWhy)
2172 : {
2173 0 : AssertIsOnOwningThread();
2174 :
2175 0 : NoteActorDestroyed();
2176 0 : }
2177 :
2178 : nsresult
2179 0 : CopyFileHandleOp::DoFileWork(FileHandle* aFileHandle)
2180 : {
2181 0 : AssertIsOnThreadPool();
2182 :
2183 0 : nsCOMPtr<nsIInputStream> inputStream;
2184 0 : nsCOMPtr<nsIOutputStream> outputStream;
2185 :
2186 0 : if (mRead) {
2187 0 : inputStream = do_QueryInterface(mFileStream);
2188 0 : outputStream = do_QueryInterface(mBufferStream);
2189 : } else {
2190 0 : inputStream = do_QueryInterface(mBufferStream);
2191 0 : outputStream = do_QueryInterface(mFileStream);
2192 : }
2193 :
2194 0 : MOZ_ASSERT(inputStream);
2195 0 : MOZ_ASSERT(outputStream);
2196 :
2197 : nsCOMPtr<nsISeekableStream> seekableStream =
2198 0 : do_QueryInterface(mFileStream);
2199 :
2200 : nsresult rv;
2201 :
2202 0 : if (seekableStream) {
2203 0 : if (mOffset == UINT64_MAX) {
2204 0 : rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
2205 : }
2206 : else {
2207 0 : rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
2208 : }
2209 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2210 0 : return rv;
2211 : }
2212 : }
2213 :
2214 0 : mOffset = 0;
2215 :
2216 : do {
2217 : char copyBuffer[kStreamCopyBlockSize];
2218 :
2219 0 : uint64_t max = mSize - mOffset;
2220 0 : if (max == 0) {
2221 0 : break;
2222 : }
2223 :
2224 0 : uint32_t count = sizeof(copyBuffer);
2225 0 : if (count > max) {
2226 0 : count = max;
2227 : }
2228 :
2229 : uint32_t numRead;
2230 0 : rv = inputStream->Read(copyBuffer, count, &numRead);
2231 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2232 0 : return rv;
2233 : }
2234 :
2235 0 : if (!numRead) {
2236 0 : break;
2237 : }
2238 :
2239 : uint32_t numWrite;
2240 0 : rv = outputStream->Write(copyBuffer, numRead, &numWrite);
2241 0 : if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
2242 0 : rv = NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR;
2243 : }
2244 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2245 0 : return rv;
2246 : }
2247 :
2248 0 : if (NS_WARN_IF(numWrite != numRead)) {
2249 0 : return NS_ERROR_FAILURE;
2250 : }
2251 :
2252 0 : mOffset += numWrite;
2253 :
2254 : nsCOMPtr<nsIRunnable> runnable =
2255 0 : new ProgressRunnable(this, mOffset, mSize);
2256 :
2257 0 : mOwningEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
2258 : } while (true);
2259 :
2260 0 : MOZ_ASSERT(mOffset == mSize);
2261 :
2262 0 : if (mRead) {
2263 0 : MOZ_ALWAYS_SUCCEEDS(outputStream->Close());
2264 : } else {
2265 0 : MOZ_ALWAYS_SUCCEEDS(inputStream->Close());
2266 : }
2267 :
2268 0 : return NS_OK;
2269 : }
2270 :
2271 : void
2272 0 : CopyFileHandleOp::Cleanup()
2273 : {
2274 0 : AssertIsOnOwningThread();
2275 :
2276 0 : mBufferStream = nullptr;
2277 :
2278 0 : NormalFileHandleOp::Cleanup();
2279 0 : }
2280 :
2281 : NS_IMETHODIMP
2282 0 : CopyFileHandleOp::
2283 : ProgressRunnable::Run()
2284 : {
2285 0 : AssertIsOnBackgroundThread();
2286 :
2287 0 : Unused << mCopyFileHandleOp->SendProgress(mProgress, mProgressMax);
2288 :
2289 0 : mCopyFileHandleOp = nullptr;
2290 :
2291 0 : return NS_OK;
2292 : }
2293 :
2294 0 : GetMetadataOp::GetMetadataOp(FileHandle* aFileHandle,
2295 0 : const FileRequestParams& aParams)
2296 : : NormalFileHandleOp(aFileHandle)
2297 0 : , mParams(aParams.get_FileRequestGetMetadataParams())
2298 : {
2299 0 : MOZ_ASSERT(aParams.type() ==
2300 : FileRequestParams::TFileRequestGetMetadataParams);
2301 0 : }
2302 :
2303 : nsresult
2304 0 : GetMetadataOp::DoFileWork(FileHandle* aFileHandle)
2305 : {
2306 0 : AssertIsOnThreadPool();
2307 :
2308 : nsresult rv;
2309 :
2310 0 : if (mFileHandle->Mode() == FileMode::Readwrite) {
2311 : // Force a flush (so all pending writes are flushed to the disk and file
2312 : // metadata is updated too).
2313 :
2314 0 : nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mFileStream);
2315 0 : MOZ_ASSERT(ostream);
2316 :
2317 0 : rv = ostream->Flush();
2318 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2319 0 : return rv;
2320 : }
2321 : }
2322 :
2323 0 : nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(mFileStream);
2324 0 : MOZ_ASSERT(metadata);
2325 :
2326 0 : if (mParams.size()) {
2327 : int64_t size;
2328 0 : rv = metadata->GetSize(&size);
2329 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2330 0 : return rv;
2331 : }
2332 :
2333 0 : if (NS_WARN_IF(size < 0)) {
2334 0 : return NS_ERROR_FAILURE;
2335 : }
2336 :
2337 0 : mMetadata.size() = uint64_t(size);
2338 : } else {
2339 0 : mMetadata.size() = void_t();
2340 : }
2341 :
2342 0 : if (mParams.lastModified()) {
2343 : int64_t lastModified;
2344 0 : rv = metadata->GetLastModified(&lastModified);
2345 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2346 0 : return rv;
2347 : }
2348 :
2349 0 : mMetadata.lastModified() = lastModified;
2350 : } else {
2351 0 : mMetadata.lastModified() = void_t();
2352 : }
2353 :
2354 0 : return NS_OK;
2355 : }
2356 :
2357 : void
2358 0 : GetMetadataOp::GetResponse(FileRequestResponse& aResponse)
2359 : {
2360 0 : AssertIsOnOwningThread();
2361 :
2362 0 : aResponse = FileRequestGetMetadataResponse(mMetadata);
2363 0 : }
2364 :
2365 0 : ReadOp::ReadOp(FileHandle* aFileHandle,
2366 0 : const FileRequestParams& aParams)
2367 : : CopyFileHandleOp(aFileHandle)
2368 0 : , mParams(aParams.get_FileRequestReadParams())
2369 : {
2370 0 : MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestReadParams);
2371 0 : }
2372 :
2373 : bool
2374 0 : ReadOp::Init(FileHandle* aFileHandle)
2375 : {
2376 0 : AssertIsOnOwningThread();
2377 0 : MOZ_ASSERT(aFileHandle);
2378 :
2379 0 : if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle))) {
2380 0 : return false;
2381 : }
2382 :
2383 0 : mBufferStream = MemoryOutputStream::Create(mParams.size());
2384 0 : if (NS_WARN_IF(!mBufferStream)) {
2385 0 : return false;
2386 : }
2387 :
2388 0 : mOffset = mParams.offset();
2389 0 : mSize = mParams.size();
2390 0 : mRead = true;
2391 :
2392 0 : return true;
2393 : }
2394 :
2395 : void
2396 0 : ReadOp::GetResponse(FileRequestResponse& aResponse)
2397 : {
2398 0 : AssertIsOnOwningThread();
2399 :
2400 0 : auto* stream = static_cast<MemoryOutputStream*>(mBufferStream.get());
2401 :
2402 0 : aResponse = FileRequestReadResponse(stream->Data());
2403 0 : }
2404 :
2405 : // static
2406 : already_AddRefed<ReadOp::MemoryOutputStream>
2407 0 : ReadOp::
2408 : MemoryOutputStream::Create(uint64_t aSize)
2409 : {
2410 0 : MOZ_ASSERT(aSize, "Passed zero size!");
2411 :
2412 0 : if (NS_WARN_IF(aSize > UINT32_MAX)) {
2413 0 : return nullptr;
2414 : }
2415 :
2416 0 : RefPtr<MemoryOutputStream> stream = new MemoryOutputStream();
2417 :
2418 : char* dummy;
2419 0 : uint32_t length = stream->mData.GetMutableData(&dummy, aSize, fallible);
2420 0 : if (NS_WARN_IF(length != aSize)) {
2421 0 : return nullptr;
2422 : }
2423 :
2424 0 : return stream.forget();
2425 : }
2426 :
2427 0 : NS_IMPL_ISUPPORTS(ReadOp::MemoryOutputStream, nsIOutputStream)
2428 :
2429 : NS_IMETHODIMP
2430 0 : ReadOp::
2431 : MemoryOutputStream::Close()
2432 : {
2433 0 : mData.Truncate(mOffset);
2434 0 : return NS_OK;
2435 : }
2436 :
2437 : NS_IMETHODIMP
2438 0 : ReadOp::
2439 : MemoryOutputStream::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval)
2440 : {
2441 0 : return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval);
2442 : }
2443 :
2444 : NS_IMETHODIMP
2445 0 : ReadOp::
2446 : MemoryOutputStream::Flush()
2447 : {
2448 0 : return NS_OK;
2449 : }
2450 :
2451 : NS_IMETHODIMP
2452 0 : ReadOp::
2453 : MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
2454 : uint32_t* _retval)
2455 : {
2456 0 : return NS_ERROR_NOT_IMPLEMENTED;
2457 : }
2458 :
2459 : NS_IMETHODIMP
2460 0 : ReadOp::
2461 : MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
2462 : uint32_t aCount, uint32_t* _retval)
2463 : {
2464 0 : NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!");
2465 :
2466 0 : uint32_t maxCount = mData.Length() - mOffset;
2467 0 : if (maxCount == 0) {
2468 0 : *_retval = 0;
2469 0 : return NS_OK;
2470 : }
2471 :
2472 0 : if (aCount > maxCount) {
2473 0 : aCount = maxCount;
2474 : }
2475 :
2476 0 : nsresult rv = aReader(this, aClosure, mData.BeginWriting() + mOffset, 0,
2477 0 : aCount, _retval);
2478 0 : if (NS_SUCCEEDED(rv)) {
2479 0 : NS_ASSERTION(*_retval <= aCount,
2480 : "Reader should not read more than we asked it to read!");
2481 0 : mOffset += *_retval;
2482 : }
2483 :
2484 0 : return NS_OK;
2485 : }
2486 :
2487 : NS_IMETHODIMP
2488 0 : ReadOp::
2489 : MemoryOutputStream::IsNonBlocking(bool* _retval)
2490 : {
2491 0 : *_retval = false;
2492 0 : return NS_OK;
2493 : }
2494 :
2495 0 : WriteOp::WriteOp(FileHandle* aFileHandle,
2496 0 : const FileRequestParams& aParams)
2497 : : CopyFileHandleOp(aFileHandle)
2498 0 : , mParams(aParams.get_FileRequestWriteParams())
2499 : {
2500 0 : MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestWriteParams);
2501 0 : }
2502 :
2503 : bool
2504 0 : WriteOp::Init(FileHandle* aFileHandle)
2505 : {
2506 0 : AssertIsOnOwningThread();
2507 0 : MOZ_ASSERT(aFileHandle);
2508 :
2509 0 : if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle))) {
2510 0 : return false;
2511 : }
2512 :
2513 0 : nsCOMPtr<nsIInputStream> inputStream;
2514 :
2515 0 : const FileRequestData& data = mParams.data();
2516 0 : switch (data.type()) {
2517 : case FileRequestData::TFileRequestStringData: {
2518 : const FileRequestStringData& stringData =
2519 0 : data.get_FileRequestStringData();
2520 :
2521 0 : const nsCString& string = stringData.string();
2522 :
2523 : nsresult rv =
2524 0 : NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
2525 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2526 0 : return false;
2527 : }
2528 :
2529 0 : break;
2530 : }
2531 : case FileRequestData::TFileRequestBlobData: {
2532 : const FileRequestBlobData& blobData =
2533 0 : data.get_FileRequestBlobData();
2534 :
2535 0 : RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobData.blob());
2536 0 : if (NS_WARN_IF(!blobImpl)) {
2537 0 : return false;
2538 : }
2539 :
2540 0 : IgnoredErrorResult rv;
2541 0 : blobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
2542 0 : if (NS_WARN_IF(rv.Failed())) {
2543 0 : return false;
2544 : }
2545 :
2546 0 : break;
2547 : }
2548 :
2549 : default:
2550 0 : MOZ_CRASH("Should never get here!");
2551 : }
2552 :
2553 0 : mBufferStream = inputStream;
2554 0 : mOffset = mParams.offset();
2555 0 : mSize = mParams.dataLength();
2556 0 : mRead = false;
2557 :
2558 0 : return true;
2559 : }
2560 :
2561 : void
2562 0 : WriteOp::GetResponse(FileRequestResponse& aResponse)
2563 : {
2564 0 : AssertIsOnOwningThread();
2565 0 : aResponse = FileRequestWriteResponse();
2566 0 : }
2567 :
2568 0 : TruncateOp::TruncateOp(FileHandle* aFileHandle,
2569 0 : const FileRequestParams& aParams)
2570 : : NormalFileHandleOp(aFileHandle)
2571 0 : , mParams(aParams.get_FileRequestTruncateParams())
2572 : {
2573 0 : MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestTruncateParams);
2574 0 : }
2575 :
2576 : nsresult
2577 0 : TruncateOp::DoFileWork(FileHandle* aFileHandle)
2578 : {
2579 0 : AssertIsOnThreadPool();
2580 :
2581 0 : nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mFileStream);
2582 0 : MOZ_ASSERT(sstream);
2583 :
2584 0 : nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mParams.offset());
2585 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2586 0 : return rv;
2587 : }
2588 :
2589 0 : rv = sstream->SetEOF();
2590 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2591 0 : return rv;
2592 : }
2593 :
2594 0 : return NS_OK;
2595 : }
2596 :
2597 : void
2598 0 : TruncateOp::GetResponse(FileRequestResponse& aResponse)
2599 : {
2600 0 : AssertIsOnOwningThread();
2601 0 : aResponse = FileRequestTruncateResponse();
2602 0 : }
2603 :
2604 0 : FlushOp::FlushOp(FileHandle* aFileHandle,
2605 0 : const FileRequestParams& aParams)
2606 : : NormalFileHandleOp(aFileHandle)
2607 0 : , mParams(aParams.get_FileRequestFlushParams())
2608 : {
2609 0 : MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestFlushParams);
2610 0 : }
2611 :
2612 : nsresult
2613 0 : FlushOp::DoFileWork(FileHandle* aFileHandle)
2614 : {
2615 0 : AssertIsOnThreadPool();
2616 :
2617 0 : nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mFileStream);
2618 0 : MOZ_ASSERT(ostream);
2619 :
2620 0 : nsresult rv = ostream->Flush();
2621 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2622 0 : return rv;
2623 : }
2624 :
2625 0 : return NS_OK;
2626 : }
2627 :
2628 : void
2629 0 : FlushOp::GetResponse(FileRequestResponse& aResponse)
2630 : {
2631 0 : AssertIsOnOwningThread();
2632 0 : aResponse = FileRequestFlushResponse();
2633 0 : }
2634 :
2635 0 : GetFileOp::GetFileOp(FileHandle* aFileHandle,
2636 0 : const FileRequestParams& aParams)
2637 : : GetMetadataOp(aFileHandle,
2638 0 : FileRequestGetMetadataParams(true, true))
2639 0 : , mBackgroundParent(aFileHandle->GetBackgroundParent())
2640 : {
2641 0 : MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestGetFileParams);
2642 0 : MOZ_ASSERT(mBackgroundParent);
2643 0 : }
2644 :
2645 : void
2646 0 : GetFileOp::GetResponse(FileRequestResponse& aResponse)
2647 : {
2648 0 : AssertIsOnOwningThread();
2649 :
2650 0 : RefPtr<BlobImpl> blobImpl = mFileHandle->GetMutableFile()->CreateBlobImpl();
2651 0 : MOZ_ASSERT(blobImpl);
2652 :
2653 : PendingIPCBlobParent* actor =
2654 0 : PendingIPCBlobParent::Create(mBackgroundParent, blobImpl);
2655 0 : if (NS_WARN_IF(!actor)) {
2656 : // This can only fail if the child has crashed.
2657 0 : aResponse = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
2658 0 : return;
2659 : }
2660 :
2661 0 : FileRequestGetFileResponse response;
2662 0 : response.fileParent() = actor;
2663 0 : response.metadata() = mMetadata;
2664 :
2665 0 : aResponse = response;
2666 : }
2667 :
2668 : } // namespace dom
2669 : } // namespace mozilla
|