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