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