Source code

Revision control

Other Tools

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