netwerk/base/src/nsFileStreams.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial