Coverage Report

Created: 2018-09-25 14:53

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