Thu, 22 Jan 2015 13:21:57 +0100
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 | //////////////////////////////////////////////////////////////////////////////// |