Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/base/nsFileStreams.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "ipc/IPCMessageUtils.h"
7
8
#if defined(XP_UNIX) || defined(XP_BEOS)
9
#include <unistd.h>
10
#elif defined(XP_WIN)
11
#include <windows.h>
12
#include "nsILocalFileWin.h"
13
#else
14
// XXX add necessary include file for ftruncate (or equivalent)
15
#endif
16
17
#include "private/pprio.h"
18
#include "prerror.h"
19
20
#include "IOActivityMonitor.h"
21
#include "nsFileStreams.h"
22
#include "nsIFile.h"
23
#include "nsReadLine.h"
24
#include "nsIClassInfoImpl.h"
25
#include "nsLiteralString.h"
26
#include "nsSocketTransport2.h"    // for ErrorAccordingToNSPR()
27
#include "mozilla/ipc/InputStreamUtils.h"
28
#include "mozilla/Unused.h"
29
#include "mozilla/FileUtils.h"
30
#include "nsNetCID.h"
31
#include "nsXULAppAPI.h"
32
33
typedef mozilla::ipc::FileDescriptor::PlatformHandleType FileHandleType;
34
35
using namespace mozilla::ipc;
36
using namespace mozilla::net;
37
38
using mozilla::DebugOnly;
39
using mozilla::Maybe;
40
using mozilla::Nothing;
41
using mozilla::Some;
42
43
////////////////////////////////////////////////////////////////////////////////
44
// nsFileStreamBase
45
46
nsFileStreamBase::nsFileStreamBase()
47
    : mFD(nullptr)
48
    , mBehaviorFlags(0)
49
    , mState(eUnitialized)
50
    , mErrorValue(NS_ERROR_FAILURE)
51
12
{
52
12
}
53
54
nsFileStreamBase::~nsFileStreamBase()
55
12
{
56
12
    // We don't want to try to rewrind the stream when shutting down.
57
12
    mBehaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
58
12
59
12
    Close();
60
12
}
61
62
NS_IMPL_ISUPPORTS(nsFileStreamBase,
63
                  nsISeekableStream,
64
                  nsIFileMetadata)
65
66
NS_IMETHODIMP
67
nsFileStreamBase::Seek(int32_t whence, int64_t offset)
68
0
{
69
0
    nsresult rv = DoPendingOpen();
70
0
    NS_ENSURE_SUCCESS(rv, rv);
71
0
72
0
    int64_t cnt = PR_Seek64(mFD, offset, (PRSeekWhence)whence);
73
0
    if (cnt == int64_t(-1)) {
74
0
        return NS_ErrorAccordingToNSPR();
75
0
    }
76
0
    return NS_OK;
77
0
}
78
79
NS_IMETHODIMP
80
nsFileStreamBase::Tell(int64_t *result)
81
0
{
82
0
    nsresult rv = DoPendingOpen();
83
0
    NS_ENSURE_SUCCESS(rv, rv);
84
0
85
0
    int64_t cnt = PR_Seek64(mFD, 0, PR_SEEK_CUR);
86
0
    if (cnt == int64_t(-1)) {
87
0
        return NS_ErrorAccordingToNSPR();
88
0
    }
89
0
    *result = cnt;
90
0
    return NS_OK;
91
0
}
92
93
NS_IMETHODIMP
94
nsFileStreamBase::SetEOF()
95
0
{
96
0
    nsresult rv = DoPendingOpen();
97
0
    NS_ENSURE_SUCCESS(rv, rv);
98
0
99
0
#if defined(XP_UNIX) || defined(XP_BEOS)
100
0
    // Some system calls require an EOF offset.
101
0
    int64_t offset;
102
0
    rv = Tell(&offset);
103
0
    if (NS_FAILED(rv)) return rv;
104
0
#endif
105
0
106
0
#if defined(XP_UNIX) || defined(XP_BEOS)
107
0
    if (ftruncate(PR_FileDesc2NativeHandle(mFD), offset) != 0) {
108
0
        NS_ERROR("ftruncate failed");
109
0
        return NS_ERROR_FAILURE;
110
0
    }
111
#elif defined(XP_WIN)
112
    if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(mFD))) {
113
        NS_ERROR("SetEndOfFile failed");
114
        return NS_ERROR_FAILURE;
115
    }
116
#else
117
    // XXX not implemented
118
#endif
119
120
0
    return NS_OK;
121
0
}
122
123
NS_IMETHODIMP
124
nsFileStreamBase::GetSize(int64_t* _retval)
125
0
{
126
0
    nsresult rv = DoPendingOpen();
127
0
    NS_ENSURE_SUCCESS(rv, rv);
128
0
129
0
    PRFileInfo64 info;
130
0
    if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
131
0
        return NS_BASE_STREAM_OSERROR;
132
0
    }
133
0
134
0
    *_retval = int64_t(info.size);
135
0
136
0
    return NS_OK;
137
0
}
138
139
NS_IMETHODIMP
140
nsFileStreamBase::GetLastModified(int64_t* _retval)
141
0
{
142
0
    nsresult rv = DoPendingOpen();
143
0
    NS_ENSURE_SUCCESS(rv, rv);
144
0
145
0
    PRFileInfo64 info;
146
0
    if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
147
0
        return NS_BASE_STREAM_OSERROR;
148
0
    }
149
0
150
0
    int64_t modTime = int64_t(info.modifyTime);
151
0
    if (modTime == 0) {
152
0
        *_retval = 0;
153
0
    }
154
0
    else {
155
0
        *_retval = modTime / int64_t(PR_USEC_PER_MSEC);
156
0
    }
157
0
158
0
    return NS_OK;
159
0
}
160
161
NS_IMETHODIMP
162
nsFileStreamBase::GetFileDescriptor(PRFileDesc** _retval)
163
0
{
164
0
    nsresult rv = DoPendingOpen();
165
0
    NS_ENSURE_SUCCESS(rv, rv);
166
0
167
0
    *_retval = mFD;
168
0
    return NS_OK;
169
0
}
170
171
nsresult
172
nsFileStreamBase::Close()
173
12
{
174
12
    CleanUpOpen();
175
12
176
12
    nsresult rv = NS_OK;
177
12
    if (mFD) {
178
12
        if (PR_Close(mFD) == PR_FAILURE)
179
0
            rv = NS_BASE_STREAM_OSERROR;
180
12
        mFD = nullptr;
181
12
        mState = eClosed;
182
12
    }
183
12
    return rv;
184
12
}
185
186
nsresult
187
nsFileStreamBase::Available(uint64_t* aResult)
188
0
{
189
0
    nsresult rv = DoPendingOpen();
190
0
    NS_ENSURE_SUCCESS(rv, rv);
191
0
192
0
    // PR_Available with files over 4GB returns an error, so we have to
193
0
    // use the 64-bit version of PR_Available.
194
0
    int64_t avail = PR_Available64(mFD);
195
0
    if (avail == -1) {
196
0
        return NS_ErrorAccordingToNSPR();
197
0
    }
198
0
199
0
    // If available is greater than 4GB, return 4GB
200
0
    *aResult = (uint64_t)avail;
201
0
    return NS_OK;
202
0
}
203
204
nsresult
205
nsFileStreamBase::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
206
24
{
207
24
    nsresult rv = DoPendingOpen();
208
24
    if (rv == NS_BASE_STREAM_CLOSED) {
209
0
        *aResult = 0;
210
0
        return NS_OK;
211
0
    }
212
24
213
24
    if (NS_FAILED(rv)) {
214
0
        return rv;
215
0
    }
216
24
217
24
    int32_t bytesRead = PR_Read(mFD, aBuf, aCount);
218
24
    if (bytesRead == -1) {
219
0
        return NS_ErrorAccordingToNSPR();
220
0
    }
221
24
222
24
    *aResult = bytesRead;
223
24
    return NS_OK;
224
24
}
225
226
nsresult
227
nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
228
                               uint32_t aCount, uint32_t* aResult)
229
0
{
230
0
    // ReadSegments is not implemented because it would be inefficient when
231
0
    // the writer does not consume all data.  If you want to call ReadSegments,
232
0
    // wrap a BufferedInputStream around the file stream.  That will call
233
0
    // Read().
234
0
235
0
    return NS_ERROR_NOT_IMPLEMENTED;
236
0
}
237
238
nsresult
239
nsFileStreamBase::IsNonBlocking(bool *aNonBlocking)
240
0
{
241
0
    *aNonBlocking = false;
242
0
    return NS_OK;
243
0
}
244
245
nsresult
246
nsFileStreamBase::Flush(void)
247
0
{
248
0
    nsresult rv = DoPendingOpen();
249
0
    NS_ENSURE_SUCCESS(rv, rv);
250
0
251
0
    int32_t cnt = PR_Sync(mFD);
252
0
    if (cnt == -1) {
253
0
        return NS_ErrorAccordingToNSPR();
254
0
    }
255
0
    return NS_OK;
256
0
}
257
258
nsresult
259
nsFileStreamBase::Write(const char *buf, uint32_t count, uint32_t *result)
260
0
{
261
0
    nsresult rv = DoPendingOpen();
262
0
    NS_ENSURE_SUCCESS(rv, rv);
263
0
264
0
    int32_t cnt = PR_Write(mFD, buf, count);
265
0
    if (cnt == -1) {
266
0
        return NS_ErrorAccordingToNSPR();
267
0
    }
268
0
    *result = cnt;
269
0
    return NS_OK;
270
0
}
271
272
nsresult
273
nsFileStreamBase::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
274
0
{
275
0
    MOZ_ASSERT_UNREACHABLE("WriteFrom (see source comment)");
276
0
    return NS_ERROR_NOT_IMPLEMENTED;
277
0
    // File streams intentionally do not support this method.
278
0
    // If you need something like this, then you should wrap
279
0
    // the file stream using nsIBufferedOutputStream
280
0
}
281
282
nsresult
283
nsFileStreamBase::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
284
0
{
285
0
    return NS_ERROR_NOT_IMPLEMENTED;
286
0
    // File streams intentionally do not support this method.
287
0
    // If you need something like this, then you should wrap
288
0
    // the file stream using nsIBufferedOutputStream
289
0
}
290
291
nsresult
292
nsFileStreamBase::MaybeOpen(nsIFile* aFile, int32_t aIoFlags,
293
                            int32_t aPerm, bool aDeferred)
294
12
{
295
12
    NS_ENSURE_STATE(aFile);
296
12
297
12
    mOpenParams.ioFlags = aIoFlags;
298
12
    mOpenParams.perm = aPerm;
299
12
300
12
    if (aDeferred) {
301
0
        // Clone the file, as it may change between now and the deferred open
302
0
        nsCOMPtr<nsIFile> file;
303
0
        nsresult rv = aFile->Clone(getter_AddRefs(file));
304
0
        NS_ENSURE_SUCCESS(rv, rv);
305
0
306
0
        mOpenParams.localFile = file.forget();
307
0
        NS_ENSURE_TRUE(mOpenParams.localFile, NS_ERROR_UNEXPECTED);
308
0
309
0
        mState = eDeferredOpen;
310
0
        return NS_OK;
311
12
    }
312
12
313
12
    mOpenParams.localFile = aFile;
314
12
315
12
    // Following call open() at main thread.
316
12
    // Main thread might be blocked, while open a remote file.
317
12
    return DoOpen();
318
12
}
319
320
void
321
nsFileStreamBase::CleanUpOpen()
322
24
{
323
24
    mOpenParams.localFile = nullptr;
324
24
}
325
326
nsresult
327
nsFileStreamBase::DoOpen()
328
12
{
329
12
    MOZ_ASSERT(mState == eDeferredOpen || mState == eUnitialized ||
330
12
               mState == eClosed);
331
12
    NS_ASSERTION(!mFD, "Already have a file descriptor!");
332
12
    NS_ASSERTION(mOpenParams.localFile, "Must have a file to open");
333
12
334
12
    PRFileDesc* fd;
335
12
    nsresult rv;
336
12
337
12
    if (mOpenParams.ioFlags & PR_CREATE_FILE) {
338
0
        nsCOMPtr<nsIFile> parent;
339
0
        mOpenParams.localFile->GetParent(getter_AddRefs(parent));
340
0
341
0
        // Result doesn't need to be checked. If the file's parent path does not
342
0
        // exist, make it. If it does exist, do nothing.
343
0
        if (parent) {
344
0
            Unused << parent->Create(nsIFile::DIRECTORY_TYPE, 0755);
345
0
        }
346
0
    }
347
12
348
#ifdef XP_WIN
349
    if (mBehaviorFlags & nsIFileInputStream::SHARE_DELETE) {
350
      nsCOMPtr<nsILocalFileWin> file = do_QueryInterface(mOpenParams.localFile);
351
      MOZ_ASSERT(file);
352
353
      rv = file->OpenNSPRFileDescShareDelete(mOpenParams.ioFlags,
354
                                             mOpenParams.perm,
355
                                             &fd);
356
    } else
357
#endif // XP_WIN
358
    {
359
12
      rv = mOpenParams.localFile->OpenNSPRFileDesc(mOpenParams.ioFlags,
360
12
                                                   mOpenParams.perm,
361
12
                                                   &fd);
362
12
    }
363
12
364
12
    if (rv == NS_OK && IOActivityMonitor::IsActive()) {
365
0
      auto nativePath = mOpenParams.localFile->NativePath();
366
0
      if (!nativePath.IsEmpty()) {
367
0
        // registering the file to the activity monitor
368
        #ifdef XP_WIN
369
        // 16 bits unicode
370
        IOActivityMonitor::MonitorFile(fd, NS_ConvertUTF16toUTF8(nativePath.get()).get());
371
        #else
372
        // 8 bit unicode
373
0
        IOActivityMonitor::MonitorFile(fd, nativePath.get());
374
0
        #endif
375
0
      }
376
0
    }
377
12
378
12
    CleanUpOpen();
379
12
380
12
    if (NS_FAILED(rv)) {
381
0
        mState = eError;
382
0
        mErrorValue = rv;
383
0
        return rv;
384
0
    }
385
12
386
12
    mFD = fd;
387
12
    mState = eOpened;
388
12
389
12
    return NS_OK;
390
12
}
391
392
nsresult
393
nsFileStreamBase::DoPendingOpen()
394
24
{
395
24
    switch (mState) {
396
24
    case eUnitialized:
397
0
        MOZ_CRASH("This should not happen.");
398
0
        return NS_ERROR_FAILURE;
399
0
400
0
    case eDeferredOpen:
401
0
        return DoOpen();
402
0
403
24
    case eOpened:
404
24
        MOZ_ASSERT(mFD);
405
24
        if (NS_WARN_IF(!mFD)) {
406
0
          return NS_ERROR_FAILURE;
407
0
        }
408
24
        return NS_OK;
409
24
410
24
    case eClosed:
411
0
        MOZ_ASSERT(!mFD);
412
0
        return NS_BASE_STREAM_CLOSED;
413
24
414
24
    case eError:
415
0
        return mErrorValue;
416
0
    }
417
0
418
0
    MOZ_CRASH("Invalid mState value.");
419
0
    return NS_ERROR_FAILURE;
420
0
}
421
422
423
////////////////////////////////////////////////////////////////////////////////
424
// nsFileInputStream
425
426
NS_IMPL_ADDREF_INHERITED(nsFileInputStream, nsFileStreamBase)
427
NS_IMPL_RELEASE_INHERITED(nsFileInputStream, nsFileStreamBase)
428
429
NS_IMPL_CLASSINFO(nsFileInputStream, nullptr, nsIClassInfo::THREADSAFE,
430
                  NS_LOCALFILEINPUTSTREAM_CID)
431
432
24
NS_INTERFACE_MAP_BEGIN(nsFileInputStream)
433
24
    NS_INTERFACE_MAP_ENTRY(nsIInputStream)
434
24
    NS_INTERFACE_MAP_ENTRY(nsIFileInputStream)
435
24
    NS_INTERFACE_MAP_ENTRY(nsILineInputStream)
436
12
    NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
437
0
    NS_IMPL_QUERY_CLASSINFO(nsFileInputStream)
438
0
    NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, IsCloneable())
439
0
NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase)
440
441
NS_IMPL_CI_INTERFACE_GETTER(nsFileInputStream,
442
                            nsIInputStream,
443
                            nsIFileInputStream,
444
                            nsISeekableStream,
445
                            nsILineInputStream)
446
447
nsresult
448
nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
449
12
{
450
12
    NS_ENSURE_NO_AGGREGATION(aOuter);
451
12
452
12
    RefPtr<nsFileInputStream> stream = new nsFileInputStream();
453
12
    return stream->QueryInterface(aIID, aResult);
454
12
}
455
456
nsresult
457
nsFileInputStream::Open(nsIFile* aFile, int32_t aIOFlags, int32_t aPerm)
458
12
{
459
12
    nsresult rv = NS_OK;
460
12
461
12
    // If the previous file is open, close it
462
12
    if (mFD) {
463
0
        rv = Close();
464
0
        if (NS_FAILED(rv)) return rv;
465
12
    }
466
12
467
12
    // Open the file
468
12
    if (aIOFlags == -1)
469
12
        aIOFlags = PR_RDONLY;
470
12
    if (aPerm == -1)
471
12
        aPerm = 0;
472
12
473
12
    return MaybeOpen(aFile, aIOFlags, aPerm,
474
12
                     mBehaviorFlags & nsIFileInputStream::DEFER_OPEN);
475
12
}
476
477
NS_IMETHODIMP
478
nsFileInputStream::Init(nsIFile* aFile, int32_t aIOFlags, int32_t aPerm,
479
                        int32_t aBehaviorFlags)
480
12
{
481
12
    NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED);
482
12
    NS_ENSURE_TRUE(mState == eUnitialized || mState == eClosed,
483
12
                   NS_ERROR_ALREADY_INITIALIZED);
484
12
485
12
    mBehaviorFlags = aBehaviorFlags;
486
12
    mState = eUnitialized;
487
12
488
12
    mFile = aFile;
489
12
    mIOFlags = aIOFlags;
490
12
    mPerm = aPerm;
491
12
492
12
    return Open(aFile, aIOFlags, aPerm);
493
12
}
494
495
NS_IMETHODIMP
496
nsFileInputStream::Close()
497
0
{
498
0
    // Get the cache position at the time the file was close. This allows
499
0
    // NS_SEEK_CUR on a closed file that has been opened with
500
0
    // REOPEN_ON_REWIND.
501
0
    if (mBehaviorFlags & REOPEN_ON_REWIND) {
502
0
        // Get actual position. Not one modified by subclasses
503
0
        nsFileStreamBase::Tell(&mCachedPosition);
504
0
    }
505
0
506
0
    // null out mLineBuffer in case Close() is called again after failing
507
0
    mLineBuffer = nullptr;
508
0
    return nsFileStreamBase::Close();
509
0
}
510
511
NS_IMETHODIMP
512
nsFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
513
24
{
514
24
    nsresult rv = nsFileStreamBase::Read(aBuf, aCount, _retval);
515
24
    if (rv == NS_ERROR_FILE_NOT_FOUND) {
516
0
      // Don't warn if this is a deffered file not found.
517
0
      return rv;
518
0
    }
519
24
520
24
    NS_ENSURE_SUCCESS(rv, rv);
521
24
522
24
    // Check if we're at the end of file and need to close
523
24
    if (mBehaviorFlags & CLOSE_ON_EOF && *_retval == 0) {
524
0
        Close();
525
0
    }
526
24
527
24
    return NS_OK;
528
24
}
529
530
NS_IMETHODIMP
531
nsFileInputStream::ReadLine(nsACString& aLine, bool* aResult)
532
24
{
533
24
    if (!mLineBuffer) {
534
12
      mLineBuffer = new nsLineBuffer<char>;
535
12
    }
536
24
    return NS_ReadLine(this, mLineBuffer.get(), aLine, aResult);
537
24
}
538
539
NS_IMETHODIMP
540
nsFileInputStream::Seek(int32_t aWhence, int64_t aOffset)
541
0
{
542
0
  return SeekInternal(aWhence, aOffset);
543
0
}
544
545
nsresult
546
nsFileInputStream::SeekInternal(int32_t aWhence, int64_t aOffset, bool aClearBuf)
547
0
{
548
0
    nsresult rv = DoPendingOpen();
549
0
    if (rv != NS_OK && rv != NS_BASE_STREAM_CLOSED) {
550
0
        return rv;
551
0
    }
552
0
553
0
    if (aClearBuf) {
554
0
        mLineBuffer = nullptr;
555
0
    }
556
0
557
0
    if (rv == NS_BASE_STREAM_CLOSED) {
558
0
        if (mBehaviorFlags & REOPEN_ON_REWIND) {
559
0
            rv = Open(mFile, mIOFlags, mPerm);
560
0
            NS_ENSURE_SUCCESS(rv, rv);
561
0
562
0
            // If the file was closed, and we do a relative seek, use the
563
0
            // position we cached when we closed the file to seek to the right
564
0
            // location.
565
0
            if (aWhence == NS_SEEK_CUR) {
566
0
                aWhence = NS_SEEK_SET;
567
0
                aOffset += mCachedPosition;
568
0
            }
569
0
        } else {
570
0
            return NS_BASE_STREAM_CLOSED;
571
0
        }
572
0
    }
573
0
574
0
    return nsFileStreamBase::Seek(aWhence, aOffset);
575
0
}
576
577
NS_IMETHODIMP
578
nsFileInputStream::Tell(int64_t *aResult)
579
0
{
580
0
    return nsFileStreamBase::Tell(aResult);
581
0
}
582
583
NS_IMETHODIMP
584
nsFileInputStream::Available(uint64_t *aResult)
585
0
{
586
0
    return nsFileStreamBase::Available(aResult);
587
0
}
588
589
void
590
nsFileInputStream::Serialize(InputStreamParams& aParams,
591
                             FileDescriptorArray& aFileDescriptors)
592
0
{
593
0
    FileInputStreamParams params;
594
0
595
0
    if (NS_SUCCEEDED(DoPendingOpen())) {
596
0
        MOZ_ASSERT(mFD);
597
0
        FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD));
598
0
        NS_ASSERTION(fd, "This should never be null!");
599
0
600
0
        DebugOnly<FileDescriptor*> dbgFD = aFileDescriptors.AppendElement(fd);
601
0
        NS_ASSERTION(dbgFD->IsValid(), "Sending an invalid file descriptor!");
602
0
603
0
        params.fileDescriptorIndex() = aFileDescriptors.Length() - 1;
604
0
605
0
        Close();
606
0
    } else {
607
0
        NS_WARNING("This file has not been opened (or could not be opened). "
608
0
                   "Sending an invalid file descriptor to the other process!");
609
0
610
0
        params.fileDescriptorIndex() = UINT32_MAX;
611
0
    }
612
0
613
0
    int32_t behaviorFlags = mBehaviorFlags;
614
0
615
0
    // The receiving process (or thread) is going to have an open file
616
0
    // descriptor automatically so transferring this flag is meaningless.
617
0
    behaviorFlags &= ~nsIFileInputStream::DEFER_OPEN;
618
0
619
0
    params.behaviorFlags() = behaviorFlags;
620
0
    params.ioFlags() = mIOFlags;
621
0
622
0
    aParams = params;
623
0
}
624
625
bool
626
nsFileInputStream::Deserialize(const InputStreamParams& aParams,
627
                               const FileDescriptorArray& aFileDescriptors)
628
0
{
629
0
    NS_ASSERTION(!mFD, "Already have a file descriptor?!");
630
0
    NS_ASSERTION(mState == nsFileStreamBase::eUnitialized, "Deferring open?!");
631
0
    NS_ASSERTION(!mFile, "Should never have a file here!");
632
0
    NS_ASSERTION(!mPerm, "This should always be 0!");
633
0
634
0
    if (aParams.type() != InputStreamParams::TFileInputStreamParams) {
635
0
        NS_WARNING("Received unknown parameters from the other process!");
636
0
        return false;
637
0
    }
638
0
639
0
    const FileInputStreamParams& params = aParams.get_FileInputStreamParams();
640
0
641
0
    uint32_t fileDescriptorIndex = params.fileDescriptorIndex();
642
0
643
0
    FileDescriptor fd;
644
0
    if (fileDescriptorIndex < aFileDescriptors.Length()) {
645
0
        fd = aFileDescriptors[fileDescriptorIndex];
646
0
        NS_WARNING_ASSERTION(fd.IsValid(),
647
0
                             "Received an invalid file descriptor!");
648
0
    } else {
649
0
        NS_WARNING("Received a bad file descriptor index!");
650
0
    }
651
0
652
0
    if (fd.IsValid()) {
653
0
        auto rawFD = fd.ClonePlatformHandle();
654
0
        PRFileDesc* fileDesc = PR_ImportFile(PROsfd(rawFD.release()));
655
0
        if (!fileDesc) {
656
0
            NS_WARNING("Failed to import file handle!");
657
0
            return false;
658
0
        }
659
0
        mFD = fileDesc;
660
0
        mState = eOpened;
661
0
    } else {
662
0
      mState = eError;
663
0
      mErrorValue = NS_ERROR_FILE_NOT_FOUND;
664
0
    }
665
0
666
0
    mBehaviorFlags = params.behaviorFlags();
667
0
668
0
    if (!XRE_IsParentProcess()) {
669
0
        // A child process shouldn't close when it reads the end because it will
670
0
        // not be able to reopen the file later.
671
0
        mBehaviorFlags &= ~nsIFileInputStream::CLOSE_ON_EOF;
672
0
673
0
        // A child process will not be able to reopen the file so this flag is
674
0
        // meaningless.
675
0
        mBehaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
676
0
    }
677
0
678
0
    mIOFlags = params.ioFlags();
679
0
680
0
    return true;
681
0
}
682
683
Maybe<uint64_t>
684
nsFileInputStream::ExpectedSerializedLength()
685
0
{
686
0
    return Nothing();
687
0
}
688
689
bool
690
nsFileInputStream::IsCloneable() const
691
0
{
692
0
    // This inputStream is cloneable only if has been created using Init() and
693
0
    // it owns a nsIFile. This is not true when it is deserialized from IPC.
694
0
    return XRE_IsParentProcess() && mFile;
695
0
}
696
697
NS_IMETHODIMP
698
nsFileInputStream::GetCloneable(bool* aCloneable)
699
0
{
700
0
    *aCloneable = IsCloneable();
701
0
    return NS_OK;
702
0
}
703
704
NS_IMETHODIMP
705
nsFileInputStream::Clone(nsIInputStream** aResult)
706
0
{
707
0
    MOZ_ASSERT(IsCloneable());
708
0
    return NS_NewLocalFileInputStream(aResult, mFile, mIOFlags, mPerm,
709
0
                                      mBehaviorFlags);
710
0
}
711
712
////////////////////////////////////////////////////////////////////////////////
713
// nsFileOutputStream
714
715
NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream,
716
                            nsFileStreamBase,
717
                            nsIOutputStream,
718
                            nsIFileOutputStream)
719
720
nsresult
721
nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
722
0
{
723
0
    NS_ENSURE_NO_AGGREGATION(aOuter);
724
0
725
0
    RefPtr<nsFileOutputStream> stream = new nsFileOutputStream();
726
0
    return stream->QueryInterface(aIID, aResult);
727
0
}
728
729
NS_IMETHODIMP
730
nsFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
731
                         int32_t behaviorFlags)
732
0
{
733
0
    NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
734
0
    NS_ENSURE_TRUE(mState == eUnitialized || mState == eClosed,
735
0
                   NS_ERROR_ALREADY_INITIALIZED);
736
0
737
0
    mBehaviorFlags = behaviorFlags;
738
0
    mState = eUnitialized;
739
0
740
0
    if (ioFlags == -1)
741
0
        ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
742
0
    if (perm <= 0)
743
0
        perm = 0664;
744
0
745
0
    return MaybeOpen(file, ioFlags, perm,
746
0
                     mBehaviorFlags & nsIFileOutputStream::DEFER_OPEN);
747
0
}
748
749
NS_IMETHODIMP
750
nsFileOutputStream::Preallocate(int64_t aLength)
751
0
{
752
0
    if (!mFD) {
753
0
        return NS_ERROR_NOT_INITIALIZED;
754
0
    }
755
0
756
0
    if (!mozilla::fallocate(mFD, aLength)) {
757
0
        return NS_ERROR_FAILURE;
758
0
    }
759
0
760
0
    return NS_OK;
761
0
}
762
763
////////////////////////////////////////////////////////////////////////////////
764
// nsAtomicFileOutputStream
765
766
NS_IMPL_ISUPPORTS_INHERITED(nsAtomicFileOutputStream,
767
                            nsFileOutputStream,
768
                            nsISafeOutputStream,
769
                            nsIOutputStream,
770
                            nsIFileOutputStream)
771
772
NS_IMETHODIMP
773
nsAtomicFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
774
                             int32_t behaviorFlags)
775
0
{
776
0
    // While `PR_APPEND` is not supported, `-1` is used as `ioFlags` parameter
777
0
    // in some places, and `PR_APPEND | PR_TRUNCATE` does not require appending
778
0
    // to existing file. So, throw an exception only if `PR_APPEND` is
779
0
    // explicitly specified without `PR_TRUNCATE`.
780
0
    if ((ioFlags & PR_APPEND) && !(ioFlags & PR_TRUNCATE)) {
781
0
        return NS_ERROR_INVALID_ARG;
782
0
    }
783
0
    return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
784
0
}
785
786
nsresult
787
nsAtomicFileOutputStream::DoOpen()
788
0
{
789
0
    // Make sure mOpenParams.localFile will be empty if we bail somewhere in
790
0
    // this function
791
0
    nsCOMPtr<nsIFile> file;
792
0
    file.swap(mOpenParams.localFile);
793
0
794
0
    if (!file) {
795
0
        return NS_ERROR_NOT_INITIALIZED;
796
0
    }
797
0
    nsresult rv = file->Exists(&mTargetFileExists);
798
0
    if (NS_FAILED(rv)) {
799
0
        NS_ERROR("Can't tell if target file exists");
800
0
        mTargetFileExists = true; // Safer to assume it exists - we just do more work.
801
0
    }
802
0
803
0
    // follow symlinks, for two reasons:
804
0
    // 1) if a user has deliberately set up a profile file as a symlink, we honor it
805
0
    // 2) to make the MoveToNative() in Finish() an atomic operation (which may not
806
0
    //    be the case if moving across directories on different filesystems).
807
0
    nsCOMPtr<nsIFile> tempResult;
808
0
    rv = file->Clone(getter_AddRefs(tempResult));
809
0
    if (NS_SUCCEEDED(rv)) {
810
0
        tempResult->SetFollowLinks(true);
811
0
812
0
        // XP_UNIX ignores SetFollowLinks(), so we have to normalize.
813
0
        if (mTargetFileExists) {
814
0
            tempResult->Normalize();
815
0
        }
816
0
    }
817
0
818
0
    if (NS_SUCCEEDED(rv) && mTargetFileExists) {
819
0
        // Abort if |file| is not writable; it won't work as an output stream.
820
0
        bool isWritable;
821
0
        if (NS_SUCCEEDED(file->IsWritable(&isWritable)) && !isWritable) {
822
0
            return NS_ERROR_FILE_ACCESS_DENIED;
823
0
        }
824
0
825
0
        uint32_t origPerm;
826
0
        if (NS_FAILED(file->GetPermissions(&origPerm))) {
827
0
            NS_ERROR("Can't get permissions of target file");
828
0
            origPerm = mOpenParams.perm;
829
0
        }
830
0
831
0
        // XXX What if |perm| is more restrictive then |origPerm|?
832
0
        // This leaves the user supplied permissions as they were.
833
0
        rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm);
834
0
    }
835
0
    if (NS_SUCCEEDED(rv)) {
836
0
        // nsFileOutputStream::DoOpen will work on the temporary file, so we
837
0
        // prepare it and place it in mOpenParams.localFile.
838
0
        mOpenParams.localFile = tempResult;
839
0
        mTempFile = tempResult;
840
0
        mTargetFile = file;
841
0
        rv = nsFileOutputStream::DoOpen();
842
0
    }
843
0
    return rv;
844
0
}
845
846
NS_IMETHODIMP
847
nsAtomicFileOutputStream::Close()
848
0
{
849
0
    nsresult rv = nsFileOutputStream::Close();
850
0
851
0
    // the consumer doesn't want the original file overwritten -
852
0
    // so clean up by removing the temp file.
853
0
    if (mTempFile) {
854
0
        mTempFile->Remove(false);
855
0
        mTempFile = nullptr;
856
0
    }
857
0
858
0
    return rv;
859
0
}
860
861
NS_IMETHODIMP
862
nsAtomicFileOutputStream::Finish()
863
0
{
864
0
    nsresult rv = nsFileOutputStream::Close();
865
0
866
0
    // if there is no temp file, don't try to move it over the original target.
867
0
    // It would destroy the targetfile if close() is called twice.
868
0
    if (!mTempFile)
869
0
        return rv;
870
0
871
0
    // Only overwrite if everything was ok, and the temp file could be closed.
872
0
    if (NS_SUCCEEDED(mWriteResult) && NS_SUCCEEDED(rv)) {
873
0
        NS_ENSURE_STATE(mTargetFile);
874
0
875
0
        if (!mTargetFileExists) {
876
0
            // If the target file did not exist when we were initialized, then the
877
0
            // temp file we gave out was actually a reference to the target file.
878
0
            // since we succeeded in writing to the temp file (and hence succeeded
879
0
            // in writing to the target file), there is nothing more to do.
880
#ifdef DEBUG
881
            bool equal;
882
            if (NS_FAILED(mTargetFile->Equals(mTempFile, &equal)) || !equal)
883
                NS_WARNING("mTempFile not equal to mTargetFile");
884
#endif
885
        }
886
0
        else {
887
0
            nsAutoString targetFilename;
888
0
            rv = mTargetFile->GetLeafName(targetFilename);
889
0
            if (NS_SUCCEEDED(rv)) {
890
0
                // This will replace target.
891
0
                rv = mTempFile->MoveTo(nullptr, targetFilename);
892
0
                if (NS_FAILED(rv))
893
0
                    mTempFile->Remove(false);
894
0
            }
895
0
        }
896
0
    }
897
0
    else {
898
0
        mTempFile->Remove(false);
899
0
900
0
        // if writing failed, propagate the failure code to the caller.
901
0
        if (NS_FAILED(mWriteResult))
902
0
            rv = mWriteResult;
903
0
    }
904
0
    mTempFile = nullptr;
905
0
    return rv;
906
0
}
907
908
NS_IMETHODIMP
909
nsAtomicFileOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
910
0
{
911
0
    nsresult rv = nsFileOutputStream::Write(buf, count, result);
912
0
    if (NS_SUCCEEDED(mWriteResult)) {
913
0
        if (NS_FAILED(rv))
914
0
            mWriteResult = rv;
915
0
        else if (count != *result)
916
0
            mWriteResult = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
917
0
918
0
        if (NS_FAILED(mWriteResult) && count > 0)
919
0
            NS_WARNING("writing to output stream failed! data may be lost");
920
0
    }
921
0
    return rv;
922
0
}
923
924
////////////////////////////////////////////////////////////////////////////////
925
// nsSafeFileOutputStream
926
927
NS_IMETHODIMP
928
nsSafeFileOutputStream::Finish()
929
0
{
930
0
    (void) Flush();
931
0
    return nsAtomicFileOutputStream::Finish();
932
0
}
933
934
////////////////////////////////////////////////////////////////////////////////
935
// nsFileStream
936
937
NS_IMPL_ISUPPORTS_INHERITED(nsFileStream,
938
                            nsFileStreamBase,
939
                            nsIInputStream,
940
                            nsIOutputStream,
941
                            nsIFileStream)
942
943
NS_IMETHODIMP
944
nsFileStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
945
                   int32_t behaviorFlags)
946
0
{
947
0
    NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
948
0
    NS_ENSURE_TRUE(mState == eUnitialized || mState == eClosed,
949
0
                   NS_ERROR_ALREADY_INITIALIZED);
950
0
951
0
    mBehaviorFlags = behaviorFlags;
952
0
    mState = eUnitialized;
953
0
954
0
    if (ioFlags == -1)
955
0
        ioFlags = PR_RDWR;
956
0
    if (perm <= 0)
957
0
        perm = 0;
958
0
959
0
    return MaybeOpen(file, ioFlags, perm,
960
0
                     mBehaviorFlags & nsIFileStream::DEFER_OPEN);
961
0
}
962
963
////////////////////////////////////////////////////////////////////////////////