Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsFileStreams.cpp
blobf82adcb2e070001173a100614a0877a3e5107e70
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
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/
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.
15 * The Original Code is mozilla.org code.
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.
22 * Contributor(s):
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.
36 * ***** END LICENSE BLOCK ***** */
38 #if defined(XP_UNIX) || defined(XP_BEOS)
39 #include <unistd.h>
40 #elif defined(XP_WIN)
41 #include <windows.h>
42 #elif defined(XP_OS2)
43 #define INCL_DOSERRORS
44 #include <os2.h>
45 #else
46 // XXX add necessary include file for ftruncate (or equivalent)
47 #endif
49 #include "private/pprio.h"
51 #include "nsFileStreams.h"
52 #include "nsILocalFile.h"
53 #include "nsXPIDLString.h"
54 #include "prerror.h"
55 #include "nsCRT.h"
56 #include "nsInt64.h"
57 #include "nsIFile.h"
58 #include "nsDirectoryIndexStream.h"
59 #include "nsMimeTypes.h"
60 #include "nsReadLine.h"
61 #include "nsNetUtil.h"
62 //#include "nsFileTransportService.h"
64 #define NS_NO_INPUT_BUFFERING 1 // see http://bugzilla.mozilla.org/show_bug.cgi?id=41067
66 ////////////////////////////////////////////////////////////////////////////////
67 // nsFileStream
69 nsFileStream::nsFileStream()
70 : mFD(nsnull)
71 , mCloseFD(PR_TRUE)
75 nsFileStream::~nsFileStream()
77 if (mCloseFD)
78 Close();
81 NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileStream, nsISeekableStream)
83 nsresult
84 nsFileStream::InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent)
86 NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
88 // this file stream is dependent on its parent to keep the
89 // file descriptor valid. an owning reference to the parent
90 // prevents the file descriptor from going away prematurely.
92 mFD = fd;
93 mCloseFD = PR_FALSE;
94 mParent = parent;
95 return NS_OK;
98 nsresult
99 nsFileStream::Close()
101 nsresult rv = NS_OK;
102 if (mFD) {
103 if (mCloseFD)
104 if (PR_Close(mFD) == PR_FAILURE)
105 rv = NS_BASE_STREAM_OSERROR;
106 mFD = nsnull;
108 return rv;
111 NS_IMETHODIMP
112 nsFileStream::Seek(PRInt32 whence, PRInt64 offset)
114 if (mFD == nsnull)
115 return NS_BASE_STREAM_CLOSED;
117 nsInt64 cnt = PR_Seek64(mFD, offset, (PRSeekWhence)whence);
118 if (cnt == nsInt64(-1)) {
119 return NS_ErrorAccordingToNSPR();
121 return NS_OK;
124 NS_IMETHODIMP
125 nsFileStream::Tell(PRInt64 *result)
127 if (mFD == nsnull)
128 return NS_BASE_STREAM_CLOSED;
130 nsInt64 cnt = PR_Seek64(mFD, 0, PR_SEEK_CUR);
131 if (cnt == nsInt64(-1)) {
132 return NS_ErrorAccordingToNSPR();
134 *result = cnt;
135 return NS_OK;
138 NS_IMETHODIMP
139 nsFileStream::SetEOF()
141 if (mFD == nsnull)
142 return NS_BASE_STREAM_CLOSED;
144 #if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
145 // Some system calls require an EOF offset.
146 PRInt64 offset;
147 nsresult rv = Tell(&offset);
148 if (NS_FAILED(rv)) return rv;
149 #endif
151 #if defined(XP_UNIX) || defined(XP_BEOS)
152 if (ftruncate(PR_FileDesc2NativeHandle(mFD), offset) != 0) {
153 NS_ERROR("ftruncate failed");
154 return NS_ERROR_FAILURE;
156 #elif defined(XP_WIN)
157 if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(mFD))) {
158 NS_ERROR("SetEndOfFile failed");
159 return NS_ERROR_FAILURE;
161 #elif defined(XP_OS2)
162 if (DosSetFileSize((HFILE) PR_FileDesc2NativeHandle(mFD), offset) != NO_ERROR) {
163 NS_ERROR("DosSetFileSize failed");
164 return NS_ERROR_FAILURE;
166 #else
167 // XXX not implemented
168 #endif
170 return NS_OK;
173 ////////////////////////////////////////////////////////////////////////////////
174 // nsFileInputStream
176 NS_IMPL_ISUPPORTS_INHERITED3(nsFileInputStream,
177 nsFileStream,
178 nsIInputStream,
179 nsIFileInputStream,
180 nsILineInputStream)
182 NS_METHOD
183 nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
185 NS_ENSURE_NO_AGGREGATION(aOuter);
187 nsFileInputStream* stream = new nsFileInputStream();
188 if (stream == nsnull)
189 return NS_ERROR_OUT_OF_MEMORY;
190 NS_ADDREF(stream);
191 nsresult rv = stream->QueryInterface(aIID, aResult);
192 NS_RELEASE(stream);
193 return rv;
196 nsresult
197 nsFileInputStream::Open(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm)
199 nsresult rv = NS_OK;
201 // If the previous file is open, close it
202 if (mFD) {
203 rv = Close();
204 if (NS_FAILED(rv)) return rv;
207 // Open the file
208 nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aFile, &rv);
209 if (NS_FAILED(rv)) return rv;
210 if (aIOFlags == -1)
211 aIOFlags = PR_RDONLY;
212 if (aPerm == -1)
213 aPerm = 0;
215 PRFileDesc* fd;
216 rv = localFile->OpenNSPRFileDesc(aIOFlags, aPerm, &fd);
217 if (NS_FAILED(rv)) return rv;
219 mFD = fd;
221 if (mBehaviorFlags & DELETE_ON_CLOSE) {
222 // POSIX compatible filesystems allow a file to be unlinked while a
223 // file descriptor is still referencing the file. since we've already
224 // opened the file descriptor, we'll try to remove the file. if that
225 // fails, then we'll just remember the nsIFile and remove it after we
226 // close the file descriptor.
227 rv = aFile->Remove(PR_FALSE);
228 if (NS_FAILED(rv) && !(mBehaviorFlags & REOPEN_ON_REWIND)) {
229 // If REOPEN_ON_REWIND is not happenin', we haven't saved the file yet
230 mFile = aFile;
234 return NS_OK;
237 NS_IMETHODIMP
238 nsFileInputStream::Init(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm,
239 PRInt32 aBehaviorFlags)
241 NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED);
242 NS_ENSURE_TRUE(!mParent, NS_ERROR_ALREADY_INITIALIZED);
244 mBehaviorFlags = aBehaviorFlags;
246 // If the file will be reopened on rewind, save the info to open the file
247 if (mBehaviorFlags & REOPEN_ON_REWIND) {
248 mFile = aFile;
249 mIOFlags = aIOFlags;
250 mPerm = aPerm;
253 return Open(aFile, aIOFlags, aPerm);
256 NS_IMETHODIMP
257 nsFileInputStream::Close()
259 // null out mLineBuffer in case Close() is called again after failing
260 PR_FREEIF(mLineBuffer);
261 nsresult rv = nsFileStream::Close();
262 if (NS_FAILED(rv)) return rv;
263 if (mFile && (mBehaviorFlags & DELETE_ON_CLOSE)) {
264 rv = mFile->Remove(PR_FALSE);
265 NS_ASSERTION(NS_SUCCEEDED(rv), "failed to delete file");
266 // If we don't need to save the file for reopening, free it up
267 if (!(mBehaviorFlags & REOPEN_ON_REWIND)) {
268 mFile = nsnull;
271 return rv;
274 NS_IMETHODIMP
275 nsFileInputStream::Available(PRUint32* aResult)
277 if (!mFD) {
278 return NS_BASE_STREAM_CLOSED;
281 PRInt32 avail = PR_Available(mFD);
282 if (avail == -1) {
283 return NS_ErrorAccordingToNSPR();
285 *aResult = avail;
286 return NS_OK;
289 NS_IMETHODIMP
290 nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
292 if (!mFD) {
293 *aResult = 0;
294 return NS_OK;
297 PRInt32 bytesRead = PR_Read(mFD, aBuf, aCount);
298 if (bytesRead == -1) {
299 return NS_ErrorAccordingToNSPR();
301 // Check if we're at the end of file and need to close
302 if (mBehaviorFlags & CLOSE_ON_EOF) {
303 if (bytesRead == 0) {
304 Close();
308 *aResult = bytesRead;
309 return NS_OK;
312 NS_IMETHODIMP
313 nsFileInputStream::ReadLine(nsACString& aLine, PRBool* aResult)
315 if (!mLineBuffer) {
316 nsresult rv = NS_InitLineBuffer(&mLineBuffer);
317 if (NS_FAILED(rv)) return rv;
319 return NS_ReadLine(this, mLineBuffer, aLine, aResult);
322 NS_IMETHODIMP
323 nsFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
324 PRUint32 aCount, PRUint32* aResult)
326 // ReadSegments is not implemented because it would be inefficient when
327 // the writer does not consume all data. If you want to call ReadSegments,
328 // wrap a BufferedInputStream around the file stream. That will call
329 // Read().
330 return NS_ERROR_NOT_IMPLEMENTED;
333 NS_IMETHODIMP
334 nsFileInputStream::IsNonBlocking(PRBool *aNonBlocking)
336 *aNonBlocking = PR_FALSE;
337 return NS_OK;
340 NS_IMETHODIMP
341 nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
343 PR_FREEIF(mLineBuffer); // this invalidates the line buffer
344 if (!mFD) {
345 if (mBehaviorFlags & REOPEN_ON_REWIND) {
346 nsresult rv = Reopen();
347 if (NS_FAILED(rv)) {
348 return rv;
350 } else {
351 return NS_BASE_STREAM_CLOSED;
355 return nsFileStream::Seek(aWhence, aOffset);
358 ////////////////////////////////////////////////////////////////////////////////
359 // nsFileOutputStream
361 NS_IMPL_ISUPPORTS_INHERITED2(nsFileOutputStream,
362 nsFileStream,
363 nsIOutputStream,
364 nsIFileOutputStream)
366 NS_METHOD
367 nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
369 NS_ENSURE_NO_AGGREGATION(aOuter);
371 nsFileOutputStream* stream = new nsFileOutputStream();
372 if (stream == nsnull)
373 return NS_ERROR_OUT_OF_MEMORY;
374 NS_ADDREF(stream);
375 nsresult rv = stream->QueryInterface(aIID, aResult);
376 NS_RELEASE(stream);
377 return rv;
380 NS_IMETHODIMP
381 nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
382 PRInt32 behaviorFlags)
384 NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
386 nsresult rv;
387 nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
388 if (NS_FAILED(rv)) return rv;
389 if (ioFlags == -1)
390 ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
391 if (perm <= 0)
392 perm = 0664;
394 PRFileDesc* fd;
395 rv = localFile->OpenNSPRFileDesc(ioFlags, perm, &fd);
396 if (NS_FAILED(rv)) return rv;
398 mFD = fd;
399 return NS_OK;
402 NS_IMETHODIMP
403 nsFileOutputStream::Close()
405 return nsFileStream::Close();
408 NS_IMETHODIMP
409 nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
411 if (mFD == nsnull)
412 return NS_BASE_STREAM_CLOSED;
414 PRInt32 cnt = PR_Write(mFD, buf, count);
415 if (cnt == -1) {
416 return NS_ErrorAccordingToNSPR();
418 *result = cnt;
419 return NS_OK;
422 NS_IMETHODIMP
423 nsFileOutputStream::Flush(void)
425 if (mFD == nsnull)
426 return NS_BASE_STREAM_CLOSED;
428 PRInt32 cnt = PR_Sync(mFD);
429 if (cnt == -1) {
430 return NS_ErrorAccordingToNSPR();
432 return NS_OK;
435 NS_IMETHODIMP
436 nsFileOutputStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
438 NS_NOTREACHED("WriteFrom (see source comment)");
439 return NS_ERROR_NOT_IMPLEMENTED;
440 // File streams intentionally do not support this method.
441 // If you need something like this, then you should wrap
442 // the file stream using nsIBufferedOutputStream
445 NS_IMETHODIMP
446 nsFileOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
448 NS_NOTREACHED("WriteSegments (see source comment)");
449 return NS_ERROR_NOT_IMPLEMENTED;
450 // File streams intentionally do not support this method.
451 // If you need something like this, then you should wrap
452 // the file stream using nsIBufferedOutputStream
455 NS_IMETHODIMP
456 nsFileOutputStream::IsNonBlocking(PRBool *aNonBlocking)
458 *aNonBlocking = PR_FALSE;
459 return NS_OK;
462 ////////////////////////////////////////////////////////////////////////////////
463 // nsSafeFileOutputStream
465 NS_IMPL_ISUPPORTS_INHERITED3(nsSafeFileOutputStream,
466 nsFileOutputStream,
467 nsISafeOutputStream,
468 nsIOutputStream,
469 nsIFileOutputStream)
471 NS_IMETHODIMP
472 nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
473 PRInt32 behaviorFlags)
475 NS_ENSURE_ARG(file);
477 nsresult rv = file->Exists(&mTargetFileExists);
478 if (NS_FAILED(rv)) {
479 NS_ERROR("Can't tell if target file exists");
480 mTargetFileExists = PR_TRUE; // Safer to assume it exists - we just do more work.
483 // follow symlinks, for two reasons:
484 // 1) if a user has deliberately set up a profile file as a symlink, we honor it
485 // 2) to make the MoveToNative() in Finish() an atomic operation (which may not
486 // be the case if moving across directories on different filesystems).
487 nsCOMPtr<nsIFile> tempResult;
488 rv = file->Clone(getter_AddRefs(tempResult));
489 if (NS_SUCCEEDED(rv)) {
490 nsCOMPtr<nsILocalFile> tempLocal = do_QueryInterface(tempResult);
491 if (tempLocal)
492 tempLocal->SetFollowLinks(PR_TRUE);
494 // XP_UNIX ignores SetFollowLinks(), so we have to normalize.
495 tempResult->Normalize();
498 if (NS_SUCCEEDED(rv) && mTargetFileExists) {
499 PRUint32 origPerm;
500 if (NS_FAILED(file->GetPermissions(&origPerm))) {
501 NS_ERROR("Can't get permissions of target file");
502 origPerm = perm;
504 // XXX What if |perm| is more restrictive then |origPerm|?
505 // This leaves the user supplied permissions as they were.
506 rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm);
508 if (NS_SUCCEEDED(rv)) {
509 mTempFile = tempResult;
510 mTargetFile = file;
511 rv = nsFileOutputStream::Init(mTempFile, ioFlags, perm, behaviorFlags);
513 return rv;
516 NS_IMETHODIMP
517 nsSafeFileOutputStream::Close()
519 nsresult rv = nsFileOutputStream::Close();
521 // the consumer doesn't want the original file overwritten -
522 // so clean up by removing the temp file.
523 if (mTempFile) {
524 mTempFile->Remove(PR_FALSE);
525 mTempFile = nsnull;
528 return rv;
531 NS_IMETHODIMP
532 nsSafeFileOutputStream::Finish()
534 nsresult rv = nsFileOutputStream::Close();
536 // if there is no temp file, don't try to move it over the original target.
537 // It would destroy the targetfile if close() is called twice.
538 if (!mTempFile)
539 return rv;
541 // Only overwrite if everything was ok, and the temp file could be closed.
542 if (NS_SUCCEEDED(mWriteResult) && NS_SUCCEEDED(rv)) {
543 NS_ENSURE_STATE(mTargetFile);
545 if (!mTargetFileExists) {
546 // If the target file did not exist when we were initialized, then the
547 // temp file we gave out was actually a reference to the target file.
548 // since we succeeded in writing to the temp file (and hence succeeded
549 // in writing to the target file), there is nothing more to do.
550 #ifdef DEBUG
551 PRBool equal;
552 if (NS_FAILED(mTargetFile->Equals(mTempFile, &equal)) || !equal)
553 NS_ERROR("mTempFile not equal to mTargetFile");
554 #endif
556 else {
557 nsCAutoString targetFilename;
558 rv = mTargetFile->GetNativeLeafName(targetFilename);
559 if (NS_SUCCEEDED(rv)) {
560 // This will replace target.
561 rv = mTempFile->MoveToNative(nsnull, targetFilename);
562 if (NS_FAILED(rv))
563 mTempFile->Remove(PR_FALSE);
567 else {
568 mTempFile->Remove(PR_FALSE);
570 // if writing failed, propagate the failure code to the caller.
571 if (NS_FAILED(mWriteResult))
572 rv = mWriteResult;
574 mTempFile = nsnull;
575 return rv;
578 NS_IMETHODIMP
579 nsSafeFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
581 nsresult rv = nsFileOutputStream::Write(buf, count, result);
582 if (NS_SUCCEEDED(mWriteResult)) {
583 if (NS_FAILED(rv))
584 mWriteResult = rv;
585 else if (count != *result)
586 mWriteResult = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
588 if (NS_FAILED(mWriteResult) && count > 0)
589 NS_WARNING("writing to output stream failed! data may be lost");
591 return rv;
594 ////////////////////////////////////////////////////////////////////////////////