nsFileStreams.cpp 31 KB

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