nsDiskCacheStreams.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. *
  3. * This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. #include "nsCache.h"
  7. #include "nsDiskCache.h"
  8. #include "nsDiskCacheDevice.h"
  9. #include "nsDiskCacheStreams.h"
  10. #include "nsCacheService.h"
  11. #include "mozilla/FileUtils.h"
  12. #include "nsThreadUtils.h"
  13. #include "mozilla/MemoryReporting.h"
  14. #include "mozilla/Telemetry.h"
  15. #include "mozilla/TimeStamp.h"
  16. #include <algorithm>
  17. // we pick 16k as the max buffer size because that is the threshold above which
  18. // we are unable to store the data in the cache block files
  19. // see nsDiskCacheMap.[cpp,h]
  20. #define kMaxBufferSize (16 * 1024)
  21. // Assumptions:
  22. // - cache descriptors live for life of streams
  23. // - streams will only be used by FileTransport,
  24. // they will not be directly accessible to clients
  25. // - overlapped I/O is NOT supported
  26. /******************************************************************************
  27. * nsDiskCacheInputStream
  28. *****************************************************************************/
  29. class nsDiskCacheInputStream : public nsIInputStream {
  30. public:
  31. nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
  32. PRFileDesc * fileDesc,
  33. const char * buffer,
  34. uint32_t endOfStream);
  35. NS_DECL_THREADSAFE_ISUPPORTS
  36. NS_DECL_NSIINPUTSTREAM
  37. private:
  38. virtual ~nsDiskCacheInputStream();
  39. nsDiskCacheStreamIO * mStreamIO; // backpointer to parent
  40. PRFileDesc * mFD;
  41. const char * mBuffer;
  42. uint32_t mStreamEnd;
  43. uint32_t mPos; // stream position
  44. bool mClosed;
  45. };
  46. NS_IMPL_ISUPPORTS(nsDiskCacheInputStream, nsIInputStream)
  47. nsDiskCacheInputStream::nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
  48. PRFileDesc * fileDesc,
  49. const char * buffer,
  50. uint32_t endOfStream)
  51. : mStreamIO(parent)
  52. , mFD(fileDesc)
  53. , mBuffer(buffer)
  54. , mStreamEnd(endOfStream)
  55. , mPos(0)
  56. , mClosed(false)
  57. {
  58. NS_ADDREF(mStreamIO);
  59. mStreamIO->IncrementInputStreamCount();
  60. }
  61. nsDiskCacheInputStream::~nsDiskCacheInputStream()
  62. {
  63. Close();
  64. mStreamIO->DecrementInputStreamCount();
  65. NS_RELEASE(mStreamIO);
  66. }
  67. NS_IMETHODIMP
  68. nsDiskCacheInputStream::Close()
  69. {
  70. if (!mClosed) {
  71. if (mFD) {
  72. (void) PR_Close(mFD);
  73. mFD = nullptr;
  74. }
  75. mClosed = true;
  76. }
  77. return NS_OK;
  78. }
  79. NS_IMETHODIMP
  80. nsDiskCacheInputStream::Available(uint64_t * bytesAvailable)
  81. {
  82. if (mClosed) return NS_BASE_STREAM_CLOSED;
  83. if (mStreamEnd < mPos) return NS_ERROR_UNEXPECTED;
  84. *bytesAvailable = mStreamEnd - mPos;
  85. return NS_OK;
  86. }
  87. NS_IMETHODIMP
  88. nsDiskCacheInputStream::Read(char * buffer, uint32_t count, uint32_t * bytesRead)
  89. {
  90. *bytesRead = 0;
  91. if (mClosed) {
  92. CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
  93. "[stream=%p] stream was closed",
  94. this, buffer, count));
  95. return NS_OK;
  96. }
  97. if (mPos == mStreamEnd) {
  98. CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
  99. "[stream=%p] stream at end of file",
  100. this, buffer, count));
  101. return NS_OK;
  102. }
  103. if (mPos > mStreamEnd) {
  104. CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
  105. "[stream=%p] stream past end of file (!)",
  106. this, buffer, count));
  107. return NS_ERROR_UNEXPECTED;
  108. }
  109. if (count > mStreamEnd - mPos)
  110. count = mStreamEnd - mPos;
  111. if (mFD) {
  112. // just read from file
  113. int32_t result = PR_Read(mFD, buffer, count);
  114. if (result < 0) {
  115. nsresult rv = NS_ErrorAccordingToNSPR();
  116. CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read PR_Read failed"
  117. "[stream=%p, rv=%d, NSPR error %s",
  118. this, int(rv), PR_ErrorToName(PR_GetError())));
  119. return rv;
  120. }
  121. mPos += (uint32_t)result;
  122. *bytesRead = (uint32_t)result;
  123. } else if (mBuffer) {
  124. // read data from mBuffer
  125. memcpy(buffer, mBuffer + mPos, count);
  126. mPos += count;
  127. *bytesRead = count;
  128. } else {
  129. // no data source for input stream
  130. }
  131. CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
  132. "[stream=%p, count=%ud, byteRead=%ud] ",
  133. this, unsigned(count), unsigned(*bytesRead)));
  134. return NS_OK;
  135. }
  136. NS_IMETHODIMP
  137. nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer,
  138. void * closure,
  139. uint32_t count,
  140. uint32_t * bytesRead)
  141. {
  142. return NS_ERROR_NOT_IMPLEMENTED;
  143. }
  144. NS_IMETHODIMP
  145. nsDiskCacheInputStream::IsNonBlocking(bool * nonBlocking)
  146. {
  147. *nonBlocking = false;
  148. return NS_OK;
  149. }
  150. /******************************************************************************
  151. * nsDiskCacheStreamIO
  152. *****************************************************************************/
  153. NS_IMPL_ISUPPORTS(nsDiskCacheStreamIO, nsIOutputStream)
  154. nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding)
  155. : mBinding(binding)
  156. , mInStreamCount(0)
  157. , mFD(nullptr)
  158. , mStreamEnd(0)
  159. , mBufSize(0)
  160. , mBuffer(nullptr)
  161. , mOutputStreamIsOpen(false)
  162. {
  163. mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice();
  164. // acquire "death grip" on cache service
  165. nsCacheService *service = nsCacheService::GlobalInstance();
  166. NS_ADDREF(service);
  167. }
  168. nsDiskCacheStreamIO::~nsDiskCacheStreamIO()
  169. {
  170. nsCacheService::AssertOwnsLock();
  171. // Close the outputstream
  172. if (mBinding && mOutputStreamIsOpen) {
  173. (void)CloseOutputStream();
  174. }
  175. // release "death grip" on cache service
  176. nsCacheService *service = nsCacheService::GlobalInstance();
  177. NS_RELEASE(service);
  178. // assert streams closed
  179. NS_ASSERTION(!mOutputStreamIsOpen, "output stream still open");
  180. NS_ASSERTION(mInStreamCount == 0, "input stream still open");
  181. NS_ASSERTION(!mFD, "file descriptor not closed");
  182. DeleteBuffer();
  183. }
  184. // NOTE: called with service lock held
  185. nsresult
  186. nsDiskCacheStreamIO::GetInputStream(uint32_t offset, nsIInputStream ** inputStream)
  187. {
  188. NS_ENSURE_ARG_POINTER(inputStream);
  189. NS_ENSURE_TRUE(offset == 0, NS_ERROR_NOT_IMPLEMENTED);
  190. *inputStream = nullptr;
  191. if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
  192. if (mOutputStreamIsOpen) {
  193. NS_WARNING("already have an output stream open");
  194. return NS_ERROR_NOT_AVAILABLE;
  195. }
  196. nsresult rv;
  197. PRFileDesc * fd = nullptr;
  198. mStreamEnd = mBinding->mCacheEntry->DataSize();
  199. if (mStreamEnd == 0) {
  200. // there's no data to read
  201. NS_ASSERTION(!mBinding->mRecord.DataLocationInitialized(), "storage allocated for zero data size");
  202. } else if (mBinding->mRecord.DataFile() == 0) {
  203. // open file desc for data
  204. rv = OpenCacheFile(PR_RDONLY, &fd);
  205. if (NS_FAILED(rv)) return rv; // unable to open file
  206. NS_ASSERTION(fd, "cache stream lacking open file.");
  207. } else if (!mBuffer) {
  208. // read block file for data
  209. rv = ReadCacheBlocks(mStreamEnd);
  210. if (NS_FAILED(rv)) return rv;
  211. }
  212. // else, mBuffer already contains all of the data (left over from a
  213. // previous block-file read or write).
  214. NS_ASSERTION(!(fd && mBuffer), "ambiguous data sources for input stream");
  215. // create a new input stream
  216. nsDiskCacheInputStream * inStream = new nsDiskCacheInputStream(this, fd, mBuffer, mStreamEnd);
  217. if (!inStream) return NS_ERROR_OUT_OF_MEMORY;
  218. NS_ADDREF(*inputStream = inStream);
  219. return NS_OK;
  220. }
  221. // NOTE: called with service lock held
  222. nsresult
  223. nsDiskCacheStreamIO::GetOutputStream(uint32_t offset, nsIOutputStream ** outputStream)
  224. {
  225. NS_ENSURE_ARG_POINTER(outputStream);
  226. *outputStream = nullptr;
  227. if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
  228. NS_ASSERTION(!mOutputStreamIsOpen, "already have an output stream open");
  229. NS_ASSERTION(mInStreamCount == 0, "we already have input streams open");
  230. if (mOutputStreamIsOpen || mInStreamCount) return NS_ERROR_NOT_AVAILABLE;
  231. mStreamEnd = mBinding->mCacheEntry->DataSize();
  232. // Inits file or buffer and truncate at the desired offset
  233. nsresult rv = SeekAndTruncate(offset);
  234. if (NS_FAILED(rv)) return rv;
  235. mOutputStreamIsOpen = true;
  236. NS_ADDREF(*outputStream = this);
  237. return NS_OK;
  238. }
  239. nsresult
  240. nsDiskCacheStreamIO::ClearBinding()
  241. {
  242. nsresult rv = NS_OK;
  243. if (mBinding && mOutputStreamIsOpen)
  244. rv = CloseOutputStream();
  245. mBinding = nullptr;
  246. return rv;
  247. }
  248. NS_IMETHODIMP
  249. nsDiskCacheStreamIO::Close()
  250. {
  251. if (!mOutputStreamIsOpen) return NS_OK;
  252. // grab service lock
  253. nsCacheServiceAutoLock lock;
  254. if (!mBinding) { // if we're severed, just clear member variables
  255. mOutputStreamIsOpen = false;
  256. return NS_ERROR_NOT_AVAILABLE;
  257. }
  258. nsresult rv = CloseOutputStream();
  259. if (NS_FAILED(rv))
  260. NS_WARNING("CloseOutputStream() failed");
  261. return rv;
  262. }
  263. nsresult
  264. nsDiskCacheStreamIO::CloseOutputStream()
  265. {
  266. NS_ASSERTION(mBinding, "oops");
  267. CACHE_LOG_DEBUG(("CACHE: CloseOutputStream [%x doomed=%u]\n",
  268. mBinding->mRecord.HashNumber(), mBinding->mDoomed));
  269. // Mark outputstream as closed, even if saving the stream fails
  270. mOutputStreamIsOpen = false;
  271. // When writing to a file, just close the file
  272. if (mFD) {
  273. (void) PR_Close(mFD);
  274. mFD = nullptr;
  275. return NS_OK;
  276. }
  277. // write data to cache blocks, or flush mBuffer to file
  278. NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "stream is bigger than buffer");
  279. nsDiskCacheMap *cacheMap = mDevice->CacheMap(); // get map reference
  280. nsDiskCacheRecord * record = &mBinding->mRecord;
  281. nsresult rv = NS_OK;
  282. // delete existing storage
  283. if (record->DataLocationInitialized()) {
  284. rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
  285. NS_ENSURE_SUCCESS(rv, rv);
  286. // Only call UpdateRecord when there is no data to write,
  287. // because WriteDataCacheBlocks / FlushBufferToFile calls it.
  288. if ((mStreamEnd == 0) && (!mBinding->mDoomed)) {
  289. rv = cacheMap->UpdateRecord(record);
  290. if (NS_FAILED(rv)) {
  291. NS_WARNING("cacheMap->UpdateRecord() failed.");
  292. return rv; // XXX doom cache entry
  293. }
  294. }
  295. }
  296. if (mStreamEnd == 0) return NS_OK; // nothing to write
  297. // try to write to the cache blocks
  298. rv = cacheMap->WriteDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
  299. if (NS_FAILED(rv)) {
  300. NS_WARNING("WriteDataCacheBlocks() failed.");
  301. // failed to store in cacheblocks, save as separate file
  302. rv = FlushBufferToFile(); // initializes DataFileLocation() if necessary
  303. if (mFD) {
  304. UpdateFileSize();
  305. (void) PR_Close(mFD);
  306. mFD = nullptr;
  307. }
  308. else
  309. NS_WARNING("no file descriptor");
  310. }
  311. return rv;
  312. }
  313. // assumptions:
  314. // only one thread writing at a time
  315. // never have both output and input streams open
  316. // OnDataSizeChanged() will have already been called to update entry->DataSize()
  317. NS_IMETHODIMP
  318. nsDiskCacheStreamIO::Write( const char * buffer,
  319. uint32_t count,
  320. uint32_t * bytesWritten)
  321. {
  322. NS_ENSURE_ARG_POINTER(buffer);
  323. NS_ENSURE_ARG_POINTER(bytesWritten);
  324. if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED;
  325. *bytesWritten = 0; // always initialize to zero in case of errors
  326. NS_ASSERTION(count, "Write called with count of zero");
  327. if (count == 0) {
  328. return NS_OK; // nothing to write
  329. }
  330. // grab service lock
  331. nsCacheServiceAutoLock lock;
  332. if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
  333. if (mInStreamCount) {
  334. // we have open input streams already
  335. // this is an error until we support overlapped I/O
  336. NS_WARNING("Attempting to write to cache entry with open input streams.\n");
  337. return NS_ERROR_NOT_AVAILABLE;
  338. }
  339. // Not writing to file, and it will fit in the cachedatablocks?
  340. if (!mFD && (mStreamEnd + count <= kMaxBufferSize)) {
  341. // We have more data than the current buffer size?
  342. if ((mStreamEnd + count > mBufSize) && (mBufSize < kMaxBufferSize)) {
  343. // Increase buffer to the maximum size.
  344. mBuffer = (char *) moz_xrealloc(mBuffer, kMaxBufferSize);
  345. mBufSize = kMaxBufferSize;
  346. }
  347. // Store in the buffer but only if it fits
  348. if (mStreamEnd + count <= mBufSize) {
  349. memcpy(mBuffer + mStreamEnd, buffer, count);
  350. mStreamEnd += count;
  351. *bytesWritten = count;
  352. return NS_OK;
  353. }
  354. }
  355. // There are more bytes than fit in the buffer/cacheblocks, switch to file
  356. if (!mFD) {
  357. // Opens a cache file and write the buffer to it
  358. nsresult rv = FlushBufferToFile();
  359. if (NS_FAILED(rv)) {
  360. return rv;
  361. }
  362. }
  363. // Write directly to the file
  364. if (PR_Write(mFD, buffer, count) != (int32_t)count) {
  365. NS_WARNING("failed to write all data");
  366. return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR()
  367. }
  368. mStreamEnd += count;
  369. *bytesWritten = count;
  370. UpdateFileSize();
  371. NS_ASSERTION(mBinding->mCacheEntry->DataSize() == mStreamEnd, "bad stream");
  372. return NS_OK;
  373. }
  374. void
  375. nsDiskCacheStreamIO::UpdateFileSize()
  376. {
  377. NS_ASSERTION(mFD, "nsDiskCacheStreamIO::UpdateFileSize should not have been called");
  378. nsDiskCacheRecord * record = &mBinding->mRecord;
  379. const uint32_t oldSizeK = record->DataFileSize();
  380. uint32_t newSizeK = (mStreamEnd + 0x03FF) >> 10;
  381. // make sure the size won't overflow (bug #651100)
  382. if (newSizeK > kMaxDataSizeK)
  383. newSizeK = kMaxDataSizeK;
  384. if (newSizeK == oldSizeK) return;
  385. record->SetDataFileSize(newSizeK);
  386. // update cache size totals
  387. nsDiskCacheMap * cacheMap = mDevice->CacheMap();
  388. cacheMap->DecrementTotalSize(oldSizeK); // decrement old size
  389. cacheMap->IncrementTotalSize(newSizeK); // increment new size
  390. if (!mBinding->mDoomed) {
  391. nsresult rv = cacheMap->UpdateRecord(record);
  392. if (NS_FAILED(rv)) {
  393. NS_WARNING("cacheMap->UpdateRecord() failed.");
  394. // XXX doom cache entry?
  395. }
  396. }
  397. }
  398. nsresult
  399. nsDiskCacheStreamIO::OpenCacheFile(int flags, PRFileDesc ** fd)
  400. {
  401. NS_ENSURE_ARG_POINTER(fd);
  402. CACHE_LOG_DEBUG(("nsDiskCacheStreamIO::OpenCacheFile"));
  403. nsresult rv;
  404. nsDiskCacheMap * cacheMap = mDevice->CacheMap();
  405. nsCOMPtr<nsIFile> localFile;
  406. rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord,
  407. nsDiskCache::kData,
  408. !!(flags & PR_CREATE_FILE),
  409. getter_AddRefs(localFile));
  410. if (NS_FAILED(rv)) return rv;
  411. // create PRFileDesc for input stream - the 00600 is just for consistency
  412. return localFile->OpenNSPRFileDesc(flags, 00600, fd);
  413. }
  414. nsresult
  415. nsDiskCacheStreamIO::ReadCacheBlocks(uint32_t bufferSize)
  416. {
  417. NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream");
  418. NS_ASSERTION(bufferSize <= kMaxBufferSize, "bufferSize too large for buffer");
  419. NS_ASSERTION(mStreamEnd <= bufferSize, "data too large for buffer");
  420. nsDiskCacheRecord * record = &mBinding->mRecord;
  421. if (!record->DataLocationInitialized()) return NS_OK;
  422. NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file");
  423. if (!mBuffer) {
  424. mBuffer = (char *) moz_xmalloc(bufferSize);
  425. mBufSize = bufferSize;
  426. }
  427. // read data stored in cache block files
  428. nsDiskCacheMap *map = mDevice->CacheMap(); // get map reference
  429. return map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
  430. }
  431. nsresult
  432. nsDiskCacheStreamIO::FlushBufferToFile()
  433. {
  434. nsresult rv;
  435. nsDiskCacheRecord * record = &mBinding->mRecord;
  436. if (!mFD) {
  437. if (record->DataLocationInitialized() && (record->DataFile() > 0)) {
  438. // remove cache block storage
  439. nsDiskCacheMap * cacheMap = mDevice->CacheMap();
  440. rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
  441. if (NS_FAILED(rv)) return rv;
  442. }
  443. record->SetDataFileGeneration(mBinding->mGeneration);
  444. // allocate file
  445. rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
  446. if (NS_FAILED(rv)) return rv;
  447. int64_t dataSize = mBinding->mCacheEntry->PredictedDataSize();
  448. if (dataSize != -1)
  449. mozilla::fallocate(mFD, std::min<int64_t>(dataSize, kPreallocateLimit));
  450. }
  451. // write buffer to the file when there is data in it
  452. if (mStreamEnd > 0) {
  453. if (!mBuffer) {
  454. NS_RUNTIMEABORT("Fix me!");
  455. }
  456. if (PR_Write(mFD, mBuffer, mStreamEnd) != (int32_t)mStreamEnd) {
  457. NS_WARNING("failed to flush all data");
  458. return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR()
  459. }
  460. }
  461. // buffer is no longer valid
  462. DeleteBuffer();
  463. return NS_OK;
  464. }
  465. void
  466. nsDiskCacheStreamIO::DeleteBuffer()
  467. {
  468. if (mBuffer) {
  469. free(mBuffer);
  470. mBuffer = nullptr;
  471. mBufSize = 0;
  472. }
  473. }
  474. size_t
  475. nsDiskCacheStreamIO::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
  476. {
  477. size_t usage = aMallocSizeOf(this);
  478. usage += aMallocSizeOf(mFD);
  479. usage += aMallocSizeOf(mBuffer);
  480. return usage;
  481. }
  482. nsresult
  483. nsDiskCacheStreamIO::SeekAndTruncate(uint32_t offset)
  484. {
  485. if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
  486. if (uint32_t(offset) > mStreamEnd) return NS_ERROR_FAILURE;
  487. // Set the current end to the desired offset
  488. mStreamEnd = offset;
  489. // Currently stored in file?
  490. if (mBinding->mRecord.DataLocationInitialized() &&
  491. (mBinding->mRecord.DataFile() == 0)) {
  492. if (!mFD) {
  493. // we need an mFD, we better open it now
  494. nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
  495. if (NS_FAILED(rv)) return rv;
  496. }
  497. if (offset) {
  498. if (PR_Seek(mFD, offset, PR_SEEK_SET) == -1)
  499. return NS_ErrorAccordingToNSPR();
  500. }
  501. nsDiskCache::Truncate(mFD, offset);
  502. UpdateFileSize();
  503. // When we starting at zero again, close file and start with buffer.
  504. // If offset is non-zero (and within buffer) an option would be
  505. // to read the file into the buffer, but chance is high that it is
  506. // rewritten to the file anyway.
  507. if (offset == 0) {
  508. // close file descriptor
  509. (void) PR_Close(mFD);
  510. mFD = nullptr;
  511. }
  512. return NS_OK;
  513. }
  514. // read data into mBuffer if not read yet.
  515. if (offset && !mBuffer) {
  516. nsresult rv = ReadCacheBlocks(kMaxBufferSize);
  517. if (NS_FAILED(rv)) return rv;
  518. }
  519. // stream buffer sanity check
  520. NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "bad stream");
  521. return NS_OK;
  522. }
  523. NS_IMETHODIMP
  524. nsDiskCacheStreamIO::Flush()
  525. {
  526. if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED;
  527. return NS_OK;
  528. }
  529. NS_IMETHODIMP
  530. nsDiskCacheStreamIO::WriteFrom(nsIInputStream *inStream, uint32_t count, uint32_t *bytesWritten)
  531. {
  532. NS_NOTREACHED("WriteFrom");
  533. return NS_ERROR_NOT_IMPLEMENTED;
  534. }
  535. NS_IMETHODIMP
  536. nsDiskCacheStreamIO::WriteSegments( nsReadSegmentFun reader,
  537. void * closure,
  538. uint32_t count,
  539. uint32_t * bytesWritten)
  540. {
  541. NS_NOTREACHED("WriteSegments");
  542. return NS_ERROR_NOT_IMPLEMENTED;
  543. }
  544. NS_IMETHODIMP
  545. nsDiskCacheStreamIO::IsNonBlocking(bool * nonBlocking)
  546. {
  547. *nonBlocking = false;
  548. return NS_OK;
  549. }