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