1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "IPC/IPCMessageUtils.h"
39 :
40 : #if defined(XP_UNIX) || defined(XP_BEOS)
41 : #include <unistd.h>
42 : #elif defined(XP_WIN)
43 : #include <windows.h>
44 : #elif defined(XP_OS2)
45 : #define INCL_DOSERRORS
46 : #include <os2.h>
47 : #else
48 : // XXX add necessary include file for ftruncate (or equivalent)
49 : #endif
50 :
51 : #include "private/pprio.h"
52 :
53 : #include "nsFileStreams.h"
54 : #include "nsILocalFile.h"
55 : #include "nsXPIDLString.h"
56 : #include "prerror.h"
57 : #include "nsCRT.h"
58 : #include "nsIFile.h"
59 : #include "nsDirectoryIndexStream.h"
60 : #include "nsMimeTypes.h"
61 : #include "nsReadLine.h"
62 : #include "nsNetUtil.h"
63 : #include "nsIClassInfoImpl.h"
64 :
65 : #define NS_NO_INPUT_BUFFERING 1 // see http://bugzilla.mozilla.org/show_bug.cgi?id=41067
66 :
67 : ////////////////////////////////////////////////////////////////////////////////
68 : // nsFileStream
69 :
70 19607 : nsFileStream::nsFileStream()
71 : : mFD(nsnull)
72 : , mBehaviorFlags(0)
73 19607 : , mDeferredOpen(false)
74 : {
75 19607 : }
76 :
77 39214 : nsFileStream::~nsFileStream()
78 : {
79 19607 : Close();
80 39214 : }
81 :
82 310061 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileStream, nsISeekableStream)
83 :
84 : nsresult
85 49595 : nsFileStream::Close()
86 : {
87 49595 : CleanUpOpen();
88 :
89 49595 : nsresult rv = NS_OK;
90 49595 : if (mFD) {
91 19186 : if (PR_Close(mFD) == PR_FAILURE)
92 0 : rv = NS_BASE_STREAM_OSERROR;
93 19186 : mFD = nsnull;
94 : }
95 49595 : return rv;
96 : }
97 :
98 : NS_IMETHODIMP
99 6005 : nsFileStream::Seek(PRInt32 whence, PRInt64 offset)
100 : {
101 6005 : nsresult rv = DoPendingOpen();
102 6005 : NS_ENSURE_SUCCESS(rv, rv);
103 :
104 6005 : if (mFD == nsnull)
105 0 : return NS_BASE_STREAM_CLOSED;
106 :
107 6005 : PRInt64 cnt = PR_Seek64(mFD, offset, (PRSeekWhence)whence);
108 6005 : if (cnt == PRInt64(-1)) {
109 0 : return NS_ErrorAccordingToNSPR();
110 : }
111 6005 : return NS_OK;
112 : }
113 :
114 : NS_IMETHODIMP
115 4228 : nsFileStream::Tell(PRInt64 *result)
116 : {
117 4228 : nsresult rv = DoPendingOpen();
118 4228 : NS_ENSURE_SUCCESS(rv, rv);
119 :
120 4228 : if (mFD == nsnull)
121 0 : return NS_BASE_STREAM_CLOSED;
122 :
123 4228 : PRInt64 cnt = PR_Seek64(mFD, 0, PR_SEEK_CUR);
124 4228 : if (cnt == PRInt64(-1)) {
125 0 : return NS_ErrorAccordingToNSPR();
126 : }
127 4228 : *result = cnt;
128 4228 : return NS_OK;
129 : }
130 :
131 : NS_IMETHODIMP
132 964 : nsFileStream::SetEOF()
133 : {
134 964 : if (mFD == nsnull)
135 0 : return NS_BASE_STREAM_CLOSED;
136 :
137 : #if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
138 : // Some system calls require an EOF offset.
139 : PRInt64 offset;
140 964 : nsresult rv = Tell(&offset);
141 964 : if (NS_FAILED(rv)) return rv;
142 : #endif
143 :
144 : #if defined(XP_UNIX) || defined(XP_BEOS)
145 964 : if (ftruncate(PR_FileDesc2NativeHandle(mFD), offset) != 0) {
146 0 : NS_ERROR("ftruncate failed");
147 0 : return NS_ERROR_FAILURE;
148 : }
149 : #elif defined(XP_WIN)
150 : if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(mFD))) {
151 : NS_ERROR("SetEndOfFile failed");
152 : return NS_ERROR_FAILURE;
153 : }
154 : #elif defined(XP_OS2)
155 : if (DosSetFileSize((HFILE) PR_FileDesc2NativeHandle(mFD), offset) != NO_ERROR) {
156 : NS_ERROR("DosSetFileSize failed");
157 : return NS_ERROR_FAILURE;
158 : }
159 : #else
160 : // XXX not implemented
161 : #endif
162 :
163 964 : return NS_OK;
164 : }
165 :
166 : nsresult
167 19853 : nsFileStream::MaybeOpen(nsILocalFile* aFile, PRInt32 aIoFlags, PRInt32 aPerm,
168 : bool aDeferred)
169 : {
170 19853 : mOpenParams.ioFlags = aIoFlags;
171 19853 : mOpenParams.perm = aPerm;
172 :
173 19853 : if (aDeferred) {
174 : // Clone the file, as it may change between now and the deferred open
175 2208 : nsCOMPtr<nsIFile> file;
176 1104 : nsresult rv = aFile->Clone(getter_AddRefs(file));
177 1104 : NS_ENSURE_SUCCESS(rv, rv);
178 :
179 1104 : mOpenParams.localFile = do_QueryInterface(file);
180 1104 : NS_ENSURE_TRUE(mOpenParams.localFile, NS_ERROR_UNEXPECTED);
181 :
182 1104 : mDeferredOpen = true;
183 1104 : return NS_OK;
184 : }
185 :
186 18749 : mOpenParams.localFile = aFile;
187 :
188 18749 : return DoOpen();
189 : }
190 :
191 : void
192 69441 : nsFileStream::CleanUpOpen()
193 : {
194 69441 : mOpenParams.localFile = nsnull;
195 69441 : mDeferredOpen = false;
196 69441 : }
197 :
198 : nsresult
199 19846 : nsFileStream::DoOpen()
200 : {
201 19846 : NS_PRECONDITION(mOpenParams.localFile, "Must have a file to open");
202 :
203 : PRFileDesc* fd;
204 19846 : nsresult rv = mOpenParams.localFile->OpenNSPRFileDesc(mOpenParams.ioFlags, mOpenParams.perm, &fd);
205 19846 : CleanUpOpen();
206 19846 : if (NS_FAILED(rv)) return rv;
207 19186 : mFD = fd;
208 :
209 19186 : return NS_OK;
210 : }
211 :
212 : nsresult
213 564795 : nsFileStream::DoPendingOpen()
214 : {
215 564795 : if (!mDeferredOpen) {
216 563698 : return NS_OK;
217 : }
218 :
219 1097 : return DoOpen();
220 : }
221 :
222 : ////////////////////////////////////////////////////////////////////////////////
223 : // nsFileInputStream
224 :
225 94218 : NS_IMPL_ADDREF_INHERITED(nsFileInputStream, nsFileStream)
226 94218 : NS_IMPL_RELEASE_INHERITED(nsFileInputStream, nsFileStream)
227 :
228 : NS_IMPL_CLASSINFO(nsFileInputStream, NULL, nsIClassInfo::THREADSAFE,
229 : NS_LOCALFILEINPUTSTREAM_CID)
230 :
231 87825 : NS_INTERFACE_MAP_BEGIN(nsFileInputStream)
232 87825 : NS_INTERFACE_MAP_ENTRY(nsFileStream)
233 87746 : NS_INTERFACE_MAP_ENTRY(nsIInputStream)
234 63010 : NS_INTERFACE_MAP_ENTRY(nsIFileInputStream)
235 31586 : NS_INTERFACE_MAP_ENTRY(nsILineInputStream)
236 30688 : NS_INTERFACE_MAP_ENTRY(nsIIPCSerializable)
237 30688 : NS_IMPL_QUERY_CLASSINFO(nsFileInputStream)
238 27209 : NS_INTERFACE_MAP_END_INHERITING(nsFileStream)
239 :
240 924 : NS_IMPL_CI_INTERFACE_GETTER5(nsFileInputStream,
241 : nsIInputStream,
242 : nsIFileInputStream,
243 : nsISeekableStream,
244 : nsILineInputStream,
245 924 : nsIIPCSerializable)
246 :
247 : nsresult
248 15676 : nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
249 : {
250 15676 : NS_ENSURE_NO_AGGREGATION(aOuter);
251 :
252 15676 : nsFileInputStream* stream = new nsFileInputStream();
253 15676 : if (stream == nsnull)
254 0 : return NS_ERROR_OUT_OF_MEMORY;
255 15676 : NS_ADDREF(stream);
256 15676 : nsresult rv = stream->QueryInterface(aIID, aResult);
257 15676 : NS_RELEASE(stream);
258 15676 : return rv;
259 : }
260 :
261 : nsresult
262 15728 : nsFileInputStream::Open(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm)
263 : {
264 15728 : nsresult rv = NS_OK;
265 :
266 : // If the previous file is open, close it
267 15728 : if (mFD) {
268 0 : rv = Close();
269 0 : if (NS_FAILED(rv)) return rv;
270 : }
271 :
272 : // Open the file
273 31456 : nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aFile, &rv);
274 15728 : if (NS_FAILED(rv)) return rv;
275 15728 : if (aIOFlags == -1)
276 13632 : aIOFlags = PR_RDONLY;
277 15728 : if (aPerm == -1)
278 13520 : aPerm = 0;
279 :
280 : rv = MaybeOpen(localFile, aIOFlags, aPerm,
281 15728 : mBehaviorFlags & nsIFileInputStream::DEFER_OPEN);
282 15728 : if (NS_FAILED(rv)) return rv;
283 :
284 15074 : if (mBehaviorFlags & DELETE_ON_CLOSE) {
285 : // POSIX compatible filesystems allow a file to be unlinked while a
286 : // file descriptor is still referencing the file. since we've already
287 : // opened the file descriptor, we'll try to remove the file. if that
288 : // fails, then we'll just remember the nsIFile and remove it after we
289 : // close the file descriptor.
290 1 : rv = aFile->Remove(false);
291 1 : if (NS_SUCCEEDED(rv)) {
292 : // No need to remove it later. Clear the flag.
293 1 : mBehaviorFlags &= ~DELETE_ON_CLOSE;
294 : }
295 : }
296 :
297 15074 : return NS_OK;
298 : }
299 :
300 : NS_IMETHODIMP
301 15724 : nsFileInputStream::Init(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm,
302 : PRInt32 aBehaviorFlags)
303 : {
304 15724 : NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED);
305 15724 : NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED);
306 :
307 15724 : mBehaviorFlags = aBehaviorFlags;
308 :
309 15724 : mFile = aFile;
310 15724 : mIOFlags = aIOFlags;
311 15724 : mPerm = aPerm;
312 :
313 15724 : return Open(aFile, aIOFlags, aPerm);
314 : }
315 :
316 : NS_IMETHODIMP
317 20354 : nsFileInputStream::Close()
318 : {
319 : // null out mLineBuffer in case Close() is called again after failing
320 20354 : PR_FREEIF(mLineBuffer);
321 20354 : nsresult rv = nsFileStream::Close();
322 20354 : if (NS_FAILED(rv)) return rv;
323 20354 : if (mFile && (mBehaviorFlags & DELETE_ON_CLOSE)) {
324 0 : rv = mFile->Remove(false);
325 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "failed to delete file");
326 : // If we don't need to save the file for reopening, free it up
327 0 : if (!(mBehaviorFlags & REOPEN_ON_REWIND)) {
328 0 : mFile = nsnull;
329 : }
330 : }
331 20354 : return rv;
332 : }
333 :
334 : NS_IMETHODIMP
335 15603 : nsFileInputStream::Available(PRUint32* aResult)
336 : {
337 15603 : nsresult rv = DoPendingOpen();
338 15603 : NS_ENSURE_SUCCESS(rv, rv);
339 :
340 15603 : if (!mFD) {
341 2 : return NS_BASE_STREAM_CLOSED;
342 : }
343 :
344 : // PR_Available with files over 4GB returns an error, so we have to
345 : // use the 64-bit version of PR_Available.
346 15601 : PRInt64 avail = PR_Available64(mFD);
347 15601 : if (avail == -1) {
348 0 : return NS_ErrorAccordingToNSPR();
349 : }
350 :
351 : // If available is greater than 4GB, return 4GB
352 15601 : *aResult = avail > PR_UINT32_MAX ? PR_UINT32_MAX : (PRUint32)avail;
353 15601 : return NS_OK;
354 : }
355 :
356 : NS_IMETHODIMP
357 32932 : nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
358 : {
359 32932 : nsresult rv = DoPendingOpen();
360 32932 : NS_ENSURE_SUCCESS(rv, rv);
361 :
362 32932 : if (!mFD) {
363 13 : *aResult = 0;
364 13 : return NS_OK;
365 : }
366 :
367 32919 : PRInt32 bytesRead = PR_Read(mFD, aBuf, aCount);
368 32919 : if (bytesRead == -1) {
369 0 : return NS_ErrorAccordingToNSPR();
370 : }
371 : // Check if we're at the end of file and need to close
372 32919 : if (mBehaviorFlags & CLOSE_ON_EOF) {
373 1284 : if (bytesRead == 0) {
374 213 : Close();
375 : }
376 : }
377 :
378 32919 : *aResult = bytesRead;
379 32919 : return NS_OK;
380 : }
381 :
382 : NS_IMETHODIMP
383 50011 : nsFileInputStream::ReadLine(nsACString& aLine, bool* aResult)
384 : {
385 50011 : nsresult rv = DoPendingOpen();
386 50011 : NS_ENSURE_SUCCESS(rv, rv);
387 :
388 50011 : if (!mLineBuffer) {
389 556 : nsresult rv = NS_InitLineBuffer(&mLineBuffer);
390 556 : if (NS_FAILED(rv)) return rv;
391 : }
392 50011 : return NS_ReadLine(this, mLineBuffer, aLine, aResult);
393 : }
394 :
395 : NS_IMETHODIMP
396 527 : nsFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
397 : PRUint32 aCount, PRUint32* aResult)
398 : {
399 : // ReadSegments is not implemented because it would be inefficient when
400 : // the writer does not consume all data. If you want to call ReadSegments,
401 : // wrap a BufferedInputStream around the file stream. That will call
402 : // Read().
403 :
404 : // If this is ever implemented you might need to modify
405 : // nsPartialFileInputStream::ReadSegments
406 :
407 527 : return NS_ERROR_NOT_IMPLEMENTED;
408 : }
409 :
410 : NS_IMETHODIMP
411 46 : nsFileInputStream::IsNonBlocking(bool *aNonBlocking)
412 : {
413 46 : *aNonBlocking = false;
414 46 : return NS_OK;
415 : }
416 :
417 : NS_IMETHODIMP
418 515 : nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
419 : {
420 515 : nsresult rv = DoPendingOpen();
421 515 : NS_ENSURE_SUCCESS(rv, rv);
422 :
423 515 : PR_FREEIF(mLineBuffer); // this invalidates the line buffer
424 515 : if (!mFD) {
425 4 : if (mBehaviorFlags & REOPEN_ON_REWIND) {
426 4 : nsresult rv = Reopen();
427 4 : if (NS_FAILED(rv)) {
428 1 : return rv;
429 : }
430 : } else {
431 0 : return NS_BASE_STREAM_CLOSED;
432 : }
433 : }
434 :
435 514 : return nsFileStream::Seek(aWhence, aOffset);
436 : }
437 :
438 : bool
439 0 : nsFileInputStream::Read(const IPC::Message *aMsg, void **aIter)
440 : {
441 : using IPC::ReadParam;
442 :
443 0 : nsCString path;
444 : bool followLinks;
445 : PRInt32 flags;
446 0 : if (!ReadParam(aMsg, aIter, &path) ||
447 0 : !ReadParam(aMsg, aIter, &followLinks) ||
448 0 : !ReadParam(aMsg, aIter, &flags))
449 0 : return false;
450 :
451 0 : nsCOMPtr<nsILocalFile> file;
452 0 : nsresult rv = NS_NewNativeLocalFile(path, followLinks, getter_AddRefs(file));
453 0 : if (NS_FAILED(rv))
454 0 : return false;
455 :
456 : // IO flags = -1 means readonly, and
457 : // permissions are unimportant since we're reading
458 0 : rv = Init(file, -1, -1, flags);
459 0 : if (NS_FAILED(rv))
460 0 : return false;
461 :
462 0 : return true;
463 : }
464 :
465 : void
466 0 : nsFileInputStream::Write(IPC::Message *aMsg)
467 : {
468 : using IPC::WriteParam;
469 :
470 0 : nsCString path;
471 0 : mFile->GetNativePath(path);
472 0 : WriteParam(aMsg, path);
473 0 : nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(mFile);
474 : bool followLinks;
475 0 : localFile->GetFollowLinks(&followLinks);
476 0 : WriteParam(aMsg, followLinks);
477 0 : WriteParam(aMsg, mBehaviorFlags);
478 0 : }
479 :
480 : ////////////////////////////////////////////////////////////////////////////////
481 : // nsPartialFileInputStream
482 :
483 : // Don't forward to nsFileInputStream as we don't want to QI to
484 : // nsIFileInputStream
485 2920 : NS_IMPL_ISUPPORTS_INHERITED3(nsPartialFileInputStream,
486 : nsFileStream,
487 : nsIInputStream,
488 : nsIPartialFileInputStream,
489 : nsILineInputStream)
490 :
491 : nsresult
492 62 : nsPartialFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID,
493 : void **aResult)
494 : {
495 62 : NS_ENSURE_NO_AGGREGATION(aOuter);
496 :
497 62 : nsPartialFileInputStream* stream = new nsPartialFileInputStream();
498 :
499 62 : NS_ADDREF(stream);
500 62 : nsresult rv = stream->QueryInterface(aIID, aResult);
501 62 : NS_RELEASE(stream);
502 62 : return rv;
503 : }
504 :
505 : NS_IMETHODIMP
506 62 : nsPartialFileInputStream::Init(nsIFile* aFile, PRUint64 aStart,
507 : PRUint64 aLength, PRInt32 aIOFlags,
508 : PRInt32 aPerm, PRInt32 aBehaviorFlags)
509 : {
510 62 : mStart = aStart;
511 62 : mLength = aLength;
512 62 : mPosition = 0;
513 :
514 : nsresult rv = nsFileInputStream::Init(aFile, aIOFlags, aPerm,
515 62 : aBehaviorFlags);
516 62 : NS_ENSURE_SUCCESS(rv, rv);
517 :
518 62 : return nsFileInputStream::Seek(NS_SEEK_SET, mStart);
519 : }
520 :
521 : NS_IMETHODIMP
522 1889 : nsPartialFileInputStream::Tell(PRInt64 *aResult)
523 : {
524 : PRInt64 tell;
525 1889 : nsresult rv = nsFileInputStream::Tell(&tell);
526 1889 : if (NS_SUCCEEDED(rv)) {
527 1889 : *aResult = tell - mStart;
528 : }
529 1889 : return rv;
530 : }
531 :
532 : NS_IMETHODIMP
533 1889 : nsPartialFileInputStream::Available(PRUint32* aResult)
534 : {
535 : PRUint32 available;
536 1889 : nsresult rv = nsFileInputStream::Available(&available);
537 1889 : if (NS_SUCCEEDED(rv)) {
538 1889 : *aResult = TruncateSize(available);
539 : }
540 1889 : return rv;
541 : }
542 :
543 : NS_IMETHODIMP
544 313 : nsPartialFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
545 : {
546 313 : PRUint32 readsize = TruncateSize(aCount);
547 313 : if (readsize == 0 && mBehaviorFlags & CLOSE_ON_EOF) {
548 4 : Close();
549 4 : *aResult = 0;
550 4 : return NS_OK;
551 : }
552 :
553 309 : nsresult rv = nsFileInputStream::Read(aBuf, readsize, aResult);
554 309 : if (NS_SUCCEEDED(rv)) {
555 309 : mPosition += readsize;
556 : }
557 309 : return rv;
558 : }
559 :
560 : NS_IMETHODIMP
561 614 : nsPartialFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
562 : {
563 : PRInt64 offset;
564 614 : switch (aWhence) {
565 : case NS_SEEK_SET:
566 166 : offset = mStart + aOffset;
567 166 : break;
568 : case NS_SEEK_CUR:
569 309 : offset = mStart + mPosition + aOffset;
570 309 : break;
571 : case NS_SEEK_END:
572 139 : offset = mStart + mLength + aOffset;
573 139 : break;
574 : default:
575 0 : return NS_ERROR_ILLEGAL_VALUE;
576 : }
577 :
578 614 : if (offset < (PRInt64)mStart || offset > (PRInt64)(mStart + mLength)) {
579 271 : return NS_ERROR_INVALID_ARG;
580 : }
581 :
582 343 : nsresult rv = nsFileInputStream::Seek(NS_SEEK_SET, offset);
583 343 : if (NS_SUCCEEDED(rv)) {
584 342 : mPosition = offset - mStart;
585 : }
586 343 : return rv;
587 : }
588 :
589 : ////////////////////////////////////////////////////////////////////////////////
590 : // nsFileOutputStream
591 :
592 100229 : NS_IMPL_ISUPPORTS_INHERITED2(nsFileOutputStream,
593 : nsFileStream,
594 : nsIOutputStream,
595 : nsIFileOutputStream)
596 :
597 : nsresult
598 2632 : nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
599 : {
600 2632 : NS_ENSURE_NO_AGGREGATION(aOuter);
601 :
602 2632 : nsFileOutputStream* stream = new nsFileOutputStream();
603 2632 : if (stream == nsnull)
604 0 : return NS_ERROR_OUT_OF_MEMORY;
605 2632 : NS_ADDREF(stream);
606 2632 : nsresult rv = stream->QueryInterface(aIID, aResult);
607 2632 : NS_RELEASE(stream);
608 2632 : return rv;
609 : }
610 :
611 : NS_IMETHODIMP
612 4125 : nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
613 : PRInt32 behaviorFlags)
614 : {
615 4125 : NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
616 4125 : NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED);
617 :
618 4125 : mBehaviorFlags = behaviorFlags;
619 :
620 : nsresult rv;
621 8250 : nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
622 4125 : if (NS_FAILED(rv)) return rv;
623 4125 : if (ioFlags == -1)
624 38 : ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
625 4125 : if (perm <= 0)
626 989 : perm = 0664;
627 :
628 : return MaybeOpen(localFile, ioFlags, perm,
629 4125 : mBehaviorFlags & nsIFileOutputStream::DEFER_OPEN);
630 : }
631 :
632 : NS_IMETHODIMP
633 9634 : nsFileOutputStream::Close()
634 : {
635 9634 : return nsFileStream::Close();
636 : }
637 :
638 : NS_IMETHODIMP
639 454268 : nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
640 : {
641 454268 : nsresult rv = DoPendingOpen();
642 454268 : NS_ENSURE_SUCCESS(rv, rv);
643 :
644 454265 : if (mFD == nsnull)
645 2 : return NS_BASE_STREAM_CLOSED;
646 :
647 454263 : PRInt32 cnt = PR_Write(mFD, buf, count);
648 454263 : if (cnt == -1) {
649 0 : return NS_ErrorAccordingToNSPR();
650 : }
651 454263 : *result = cnt;
652 454263 : return NS_OK;
653 : }
654 :
655 : NS_IMETHODIMP
656 1233 : nsFileOutputStream::Flush(void)
657 : {
658 1233 : nsresult rv = DoPendingOpen();
659 1233 : NS_ENSURE_SUCCESS(rv, rv);
660 :
661 1233 : if (mFD == nsnull)
662 0 : return NS_BASE_STREAM_CLOSED;
663 :
664 1233 : PRInt32 cnt = PR_Sync(mFD);
665 1233 : if (cnt == -1) {
666 0 : return NS_ErrorAccordingToNSPR();
667 : }
668 1233 : return NS_OK;
669 : }
670 :
671 : NS_IMETHODIMP
672 0 : nsFileOutputStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
673 : {
674 0 : NS_NOTREACHED("WriteFrom (see source comment)");
675 0 : return NS_ERROR_NOT_IMPLEMENTED;
676 : // File streams intentionally do not support this method.
677 : // If you need something like this, then you should wrap
678 : // the file stream using nsIBufferedOutputStream
679 : }
680 :
681 : NS_IMETHODIMP
682 386 : nsFileOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
683 : {
684 386 : return NS_ERROR_NOT_IMPLEMENTED;
685 : // File streams intentionally do not support this method.
686 : // If you need something like this, then you should wrap
687 : // the file stream using nsIBufferedOutputStream
688 : }
689 :
690 : NS_IMETHODIMP
691 248 : nsFileOutputStream::IsNonBlocking(bool *aNonBlocking)
692 : {
693 248 : *aNonBlocking = false;
694 248 : return NS_OK;
695 : }
696 :
697 : ////////////////////////////////////////////////////////////////////////////////
698 : // nsSafeFileOutputStream
699 :
700 32327 : NS_IMPL_ISUPPORTS_INHERITED3(nsSafeFileOutputStream,
701 : nsFileOutputStream,
702 : nsISafeOutputStream,
703 : nsIOutputStream,
704 : nsIFileOutputStream)
705 :
706 : NS_IMETHODIMP
707 1237 : nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
708 : PRInt32 behaviorFlags)
709 : {
710 1237 : return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
711 : }
712 :
713 : nsresult
714 1234 : nsSafeFileOutputStream::DoOpen()
715 : {
716 : // Make sure mOpenParams.localFile will be empty if we bail somewhere in
717 : // this function
718 2468 : nsCOMPtr<nsILocalFile> file;
719 1234 : file.swap(mOpenParams.localFile);
720 :
721 1234 : nsresult rv = file->Exists(&mTargetFileExists);
722 1234 : if (NS_FAILED(rv)) {
723 0 : NS_ERROR("Can't tell if target file exists");
724 0 : mTargetFileExists = true; // Safer to assume it exists - we just do more work.
725 : }
726 :
727 : // follow symlinks, for two reasons:
728 : // 1) if a user has deliberately set up a profile file as a symlink, we honor it
729 : // 2) to make the MoveToNative() in Finish() an atomic operation (which may not
730 : // be the case if moving across directories on different filesystems).
731 2468 : nsCOMPtr<nsIFile> tempResult;
732 1234 : rv = file->Clone(getter_AddRefs(tempResult));
733 1234 : if (NS_SUCCEEDED(rv)) {
734 2468 : nsCOMPtr<nsILocalFile> tempLocal = do_QueryInterface(tempResult);
735 1234 : if (tempLocal)
736 1234 : tempLocal->SetFollowLinks(true);
737 :
738 : // XP_UNIX ignores SetFollowLinks(), so we have to normalize.
739 1234 : tempResult->Normalize();
740 : }
741 :
742 1234 : if (NS_SUCCEEDED(rv) && mTargetFileExists) {
743 : PRUint32 origPerm;
744 749 : if (NS_FAILED(file->GetPermissions(&origPerm))) {
745 0 : NS_ERROR("Can't get permissions of target file");
746 0 : origPerm = mOpenParams.perm;
747 : }
748 : // XXX What if |perm| is more restrictive then |origPerm|?
749 : // This leaves the user supplied permissions as they were.
750 749 : rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm);
751 : }
752 1234 : if (NS_SUCCEEDED(rv)) {
753 : // nsFileOutputStream::DoOpen will work on the temporary file, so we
754 : // prepare it and place it in mOpenParams.localFile.
755 2468 : nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(tempResult, &rv);
756 1234 : NS_ENSURE_SUCCESS(rv, rv);
757 1234 : mOpenParams.localFile = localFile;
758 1234 : mTempFile = tempResult;
759 1234 : mTargetFile = file;
760 2468 : rv = nsFileOutputStream::DoOpen();
761 : }
762 1234 : return rv;
763 : }
764 :
765 : NS_IMETHODIMP
766 1243 : nsSafeFileOutputStream::Close()
767 : {
768 1243 : nsresult rv = nsFileOutputStream::Close();
769 :
770 : // the consumer doesn't want the original file overwritten -
771 : // so clean up by removing the temp file.
772 1243 : if (mTempFile) {
773 6 : mTempFile->Remove(false);
774 6 : mTempFile = nsnull;
775 : }
776 :
777 1243 : return rv;
778 : }
779 :
780 : NS_IMETHODIMP
781 1228 : nsSafeFileOutputStream::Finish()
782 : {
783 1228 : Flush();
784 1228 : nsresult rv = nsFileOutputStream::Close();
785 :
786 : // if there is no temp file, don't try to move it over the original target.
787 : // It would destroy the targetfile if close() is called twice.
788 1228 : if (!mTempFile)
789 0 : return rv;
790 :
791 : // Only overwrite if everything was ok, and the temp file could be closed.
792 1228 : if (NS_SUCCEEDED(mWriteResult) && NS_SUCCEEDED(rv)) {
793 1228 : NS_ENSURE_STATE(mTargetFile);
794 :
795 1228 : if (!mTargetFileExists) {
796 : // If the target file did not exist when we were initialized, then the
797 : // temp file we gave out was actually a reference to the target file.
798 : // since we succeeded in writing to the temp file (and hence succeeded
799 : // in writing to the target file), there is nothing more to do.
800 : #ifdef DEBUG
801 : bool equal;
802 479 : if (NS_FAILED(mTargetFile->Equals(mTempFile, &equal)) || !equal)
803 0 : NS_ERROR("mTempFile not equal to mTargetFile");
804 : #endif
805 : }
806 : else {
807 1498 : nsAutoString targetFilename;
808 749 : rv = mTargetFile->GetLeafName(targetFilename);
809 749 : if (NS_SUCCEEDED(rv)) {
810 : // This will replace target.
811 749 : rv = mTempFile->MoveTo(nsnull, targetFilename);
812 749 : if (NS_FAILED(rv))
813 0 : mTempFile->Remove(false);
814 : }
815 1228 : }
816 : }
817 : else {
818 0 : mTempFile->Remove(false);
819 :
820 : // if writing failed, propagate the failure code to the caller.
821 0 : if (NS_FAILED(mWriteResult))
822 0 : rv = mWriteResult;
823 : }
824 1228 : mTempFile = nsnull;
825 1228 : return rv;
826 : }
827 :
828 : NS_IMETHODIMP
829 1253 : nsSafeFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
830 : {
831 1253 : nsresult rv = nsFileOutputStream::Write(buf, count, result);
832 1253 : if (NS_SUCCEEDED(mWriteResult)) {
833 1253 : if (NS_FAILED(rv))
834 2 : mWriteResult = rv;
835 1251 : else if (count != *result)
836 0 : mWriteResult = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
837 :
838 1253 : if (NS_FAILED(mWriteResult) && count > 0)
839 2 : NS_WARNING("writing to output stream failed! data may be lost");
840 : }
841 1253 : return rv;
842 : }
843 :
844 : ////////////////////////////////////////////////////////////////////////////////
|