Coverage Report

Created: 2018-09-25 14:53

/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
}