diff options
author | clamy@chromium.org <clamy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-18 23:33:01 +0000 |
---|---|---|
committer | clamy@chromium.org <clamy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-18 23:33:01 +0000 |
commit | b6b0551fa5cb75a65124acf005a85f0fa57a7eaf (patch) | |
tree | 041ca127423fdc069af02afeba37f624b85531fb /net/disk_cache | |
parent | bf1599c4174cc5fc2a85eccd4ba99d08d258436a (diff) | |
download | chromium_src-b6b0551fa5cb75a65124acf005a85f0fa57a7eaf.zip chromium_src-b6b0551fa5cb75a65124acf005a85f0fa57a7eaf.tar.gz chromium_src-b6b0551fa5cb75a65124acf005a85f0fa57a7eaf.tar.bz2 |
SimpleCache: merge the first and second stream in one file
This CL merges the two first streams in a single file in order to reduce the
disk space consumption of the Simple Cache.
BUG=173398
Review URL: https://chromiumcodereview.appspot.com/23983005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223989 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/disk_cache')
-rw-r--r-- | net/disk_cache/backend_unittest.cc | 11 | ||||
-rw-r--r-- | net/disk_cache/entry_unittest.cc | 164 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_entry_format.h | 20 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_entry_impl.cc | 182 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_entry_impl.h | 46 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_index_file.cc | 6 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_synchronous_entry.cc | 348 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_synchronous_entry.h | 69 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_test_util.cc | 2 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_util.cc | 18 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_util.h | 15 |
11 files changed, 635 insertions, 246 deletions
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc index ed07129..ab00178 100644 --- a/net/disk_cache/backend_unittest.cc +++ b/net/disk_cache/backend_unittest.cc @@ -3250,7 +3250,7 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenMissingFile) { // Delete one of the files in the entry. base::FilePath to_delete_file = cache_path_.AppendASCII( - disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0)); + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0)); EXPECT_TRUE(base::PathExists(to_delete_file)); EXPECT_TRUE(disk_cache::DeleteCacheFile(to_delete_file)); @@ -3259,9 +3259,8 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenMissingFile) { // Confirm the rest of the files are gone. for (int i = 1; i < disk_cache::kSimpleEntryFileCount; ++i) { - base::FilePath - should_be_gone_file(cache_path_.AppendASCII( - disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, i))); + base::FilePath should_be_gone_file(cache_path_.AppendASCII( + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, i))); EXPECT_FALSE(base::PathExists(should_be_gone_file)); } } @@ -3286,9 +3285,9 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenBadFile) { entry->Close(); entry = NULL; - // Write an invalid header on stream 1. + // Write an invalid header for stream 0 and stream 1. base::FilePath entry_file1_path = cache_path_.AppendASCII( - disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 1)); + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0)); disk_cache::SimpleFileHeader header; header.initial_magic_number = GG_UINT64_C(0xbadf00d); diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc index 8de30f7..705e6b2 100644 --- a/net/disk_cache/entry_unittest.cc +++ b/net/disk_cache/entry_unittest.cc @@ -21,6 +21,7 @@ #include "net/disk_cache/mem_entry_impl.h" #include "net/disk_cache/simple/simple_entry_format.h" #include "net/disk_cache/simple/simple_entry_impl.h" +#include "net/disk_cache/simple/simple_synchronous_entry.h" #include "net/disk_cache/simple/simple_test_util.h" #include "net/disk_cache/simple/simple_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -62,7 +63,7 @@ class DiskCacheEntryTest : public DiskCacheTestWithCache { void UpdateSparseEntry(); void DoomSparseEntry(); void PartialSparseEntry(); - bool SimpleCacheMakeBadChecksumEntry(const char* key, int* data_size); + bool SimpleCacheMakeBadChecksumEntry(const std::string& key, int* data_size); }; // This part of the test runs on the background thread. @@ -2414,7 +2415,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheDoomedEntry) { // Creates an entry with corrupted last byte in stream 0. // Requires SimpleCacheMode. -bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const char* key, +bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const std::string& key, int* data_size) { disk_cache::Entry* entry = NULL; @@ -2428,21 +2429,21 @@ bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const char* key, scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kDataSize)); base::strlcpy(buffer->data(), data, kDataSize); - EXPECT_EQ(kDataSize, WriteData(entry, 0, 0, buffer.get(), kDataSize, false)); + EXPECT_EQ(kDataSize, WriteData(entry, 1, 0, buffer.get(), kDataSize, false)); entry->Close(); entry = NULL; // Corrupt the last byte of the data. base::FilePath entry_file0_path = cache_path_.AppendASCII( - disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0)); + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0)); int flags = base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_OPEN; base::PlatformFile entry_file0 = base::CreatePlatformFile(entry_file0_path, flags, NULL, NULL); if (entry_file0 == base::kInvalidPlatformFileValue) return false; + int64 file_offset = - disk_cache::simple_util::GetFileOffsetFromKeyAndDataOffset( - key, kDataSize - 2); + sizeof(disk_cache::SimpleFileHeader) + key.size() + kDataSize - 2; EXPECT_EQ(1, base::WritePlatformFile(entry_file0, file_offset, "X", 1)); if (!base::ClosePlatformFile(entry_file0)) return false; @@ -2466,10 +2467,10 @@ TEST_F(DiskCacheEntryTest, SimpleCacheBadChecksum) { ScopedEntryPtr entry_closer(entry); const int kReadBufferSize = 200; - EXPECT_GE(kReadBufferSize, entry->GetDataSize(0)); + EXPECT_GE(kReadBufferSize, entry->GetDataSize(1)); scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize)); EXPECT_EQ(net::ERR_CACHE_CHECKSUM_MISMATCH, - ReadData(entry, 0, 0, read_buffer.get(), kReadBufferSize)); + ReadData(entry, 1, 0, read_buffer.get(), kReadBufferSize)); } // Tests that an entry that has had an IO error occur can still be Doomed(). @@ -2488,10 +2489,10 @@ TEST_F(DiskCacheEntryTest, SimpleCacheErrorThenDoom) { ScopedEntryPtr entry_closer(entry); const int kReadBufferSize = 200; - EXPECT_GE(kReadBufferSize, entry->GetDataSize(0)); + EXPECT_GE(kReadBufferSize, entry->GetDataSize(1)); scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize)); EXPECT_EQ(net::ERR_CACHE_CHECKSUM_MISMATCH, - ReadData(entry, 0, 0, read_buffer.get(), kReadBufferSize)); + ReadData(entry, 1, 0, read_buffer.get(), kReadBufferSize)); entry->Doom(); // Should not crash. } @@ -2530,7 +2531,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNoEOF) { // record. int kTruncationBytes = -implicit_cast<int>(sizeof(disk_cache::SimpleFileEOF)); const base::FilePath entry_path = cache_path_.AppendASCII( - disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0)); + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0)); const int64 invalid_size = disk_cache::simple_util::GetFileSizeFromKeyAndDataSize(key, kTruncationBytes); @@ -2558,13 +2559,12 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsBasic) { CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false); EXPECT_EQ( write_buffer->size(), - WriteData(entry, 0, 0, write_buffer.get(), write_buffer->size(), false)); + WriteData(entry, 1, 0, write_buffer.get(), write_buffer->size(), false)); scoped_refptr<net::IOBufferWithSize> read_buffer( new net::IOBufferWithSize(kBufferSize)); - EXPECT_EQ( - read_buffer->size(), - ReadData(entry, 0, 0, read_buffer.get(), read_buffer->size())); + EXPECT_EQ(read_buffer->size(), + ReadData(entry, 1, 0, read_buffer.get(), read_buffer->size())); } TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsDontBlock) { @@ -2591,7 +2591,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsDontBlock) { CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false); CallbackTest write_callback(&helper, false); int ret = entry->WriteData( - 0, + 1, 0, write_buffer.get(), write_buffer->size(), @@ -2624,7 +2624,7 @@ TEST_F(DiskCacheEntryTest, CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false); CallbackTest write_callback(&helper, false); int ret = entry->WriteData( - 0, + 1, 0, write_buffer.get(), write_buffer->size(), @@ -2637,7 +2637,7 @@ TEST_F(DiskCacheEntryTest, new net::IOBufferWithSize(kBufferSize)); CallbackTest read_callback(&helper, false); ret = entry->ReadData( - 0, + 1, 0, read_buffer.get(), read_buffer->size(), @@ -2689,7 +2689,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) { // This write may or may not be optimistic (it depends if the previous // optimistic create already finished by the time we call the write here). int ret = entry->WriteData( - 0, + 1, 0, buffer1.get(), kSize1, @@ -2702,7 +2702,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) { // This Read must not be optimistic, since we don't support that yet. EXPECT_EQ(net::ERR_IO_PENDING, entry->ReadData( - 0, + 1, 0, buffer1_read.get(), kSize1, @@ -2715,7 +2715,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) { // should be empty, so the next Write operation must run as optimistic. EXPECT_EQ(kSize2, entry->WriteData( - 0, + 1, 0, buffer2.get(), kSize2, @@ -2726,7 +2726,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) { // operation finishes and we can then test for HasOneRef() below. EXPECT_EQ(net::ERR_IO_PENDING, entry->ReadData( - 0, + 1, 0, buffer2_read.get(), kSize2, @@ -2832,7 +2832,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) { // operation finishes. Write must fail since we are writing in a closed entry. EXPECT_EQ( net::ERR_IO_PENDING, - entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false)); + entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false)); EXPECT_EQ(net::ERR_FAILED, cb.GetResult(net::ERR_IO_PENDING)); // Finish running the pending tasks so that we fully complete the close @@ -2861,12 +2861,12 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) { // entry. EXPECT_EQ(kSize1, entry2->WriteData( - 0, 0, buffer1.get(), kSize1, net::CompletionCallback(), false)); + 1, 0, buffer1.get(), kSize1, net::CompletionCallback(), false)); // Lets do another read so we block until both the write and the read // operation finishes and we can then test for HasOneRef() below. EXPECT_EQ(net::ERR_IO_PENDING, - entry2->ReadData(0, 0, buffer1.get(), kSize1, cb.callback())); + entry2->ReadData(1, 0, buffer1.get(), kSize1, cb.callback())); EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); // Check that we are not leaking. @@ -2897,11 +2897,11 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic5) { EXPECT_EQ( net::ERR_IO_PENDING, - entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false)); + entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false)); EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); EXPECT_EQ(net::ERR_IO_PENDING, - entry->ReadData(0, 0, buffer1.get(), kSize1, cb.callback())); + entry->ReadData(1, 0, buffer1.get(), kSize1, cb.callback())); EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); // Check that we are not leaking. @@ -2931,7 +2931,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic6) { EXPECT_EQ( net::ERR_IO_PENDING, - entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false)); + entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false)); EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); entry->Doom(); @@ -2939,7 +2939,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic6) { // This Read must not be optimistic, since we don't support that yet. EXPECT_EQ(net::ERR_IO_PENDING, - entry->ReadData(0, 0, buffer1_read.get(), kSize1, cb.callback())); + entry->ReadData(1, 0, buffer1_read.get(), kSize1, cb.callback())); EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read->data(), kSize1)); @@ -2970,7 +2970,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimisticWriteReleases) { // operations. To ensure the queue is empty, we issue a write and wait until // it completes. EXPECT_EQ(kWriteSize, - WriteData(entry, 0, 0, buffer1.get(), kWriteSize, false)); + WriteData(entry, 1, 0, buffer1.get(), kWriteSize, false)); EXPECT_TRUE(buffer1->HasOneRef()); // Finally, we should perform an optimistic write and confirm that all @@ -3015,7 +3015,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheCreateDoomRace) { for (int i = 0; i < disk_cache::kSimpleEntryFileCount; ++i) { base::FilePath entry_file_path = cache_path_.AppendASCII( - disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, i)); + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, i)); base::PlatformFileInfo info; EXPECT_FALSE(file_util::GetFileInfo(entry_file_path, &info)); } @@ -3157,7 +3157,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) { scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kWriteSize)); CacheTestFillBuffer(buffer->data(), kWriteSize, false); EXPECT_EQ(kWriteSize, - WriteData(entry, 0, 0, buffer.get(), kWriteSize, false)); + WriteData(entry, 1, 0, buffer.get(), kWriteSize, false)); entry->Close(); AddDelay(); @@ -3166,7 +3166,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) { ASSERT_EQ(net::OK, CreateEntry(key2 + base::StringPrintf("%d", i), &entry)); ScopedEntryPtr entry_closer(entry); EXPECT_EQ(kWriteSize, - WriteData(entry, 0, 0, buffer.get(), kWriteSize, false)); + WriteData(entry, 1, 0, buffer.get(), kWriteSize, false)); } // TODO(pasko): Find a way to wait for the eviction task(s) to finish by using @@ -3202,7 +3202,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) { ASSERT_EQ(net::OK, CreateEntry(key, &entry)); EXPECT_EQ(kBufferSize, - WriteData(entry, 0, 0, write_buffer.get(), kBufferSize, false)); + WriteData(entry, 1, 0, write_buffer.get(), kBufferSize, false)); entry->Close(); entry = NULL; @@ -3217,7 +3217,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) { scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize)); CallbackTest read_callback(&helper, false); EXPECT_EQ(net::ERR_IO_PENDING, - entry->ReadData(0, + entry->ReadData(1, 0, read_buffer.get(), kReadBufferSize, @@ -3231,7 +3231,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) { CacheTestFillBuffer(truncate_buffer->data(), kReadBufferSize, false); CallbackTest truncate_callback(&helper, false); EXPECT_EQ(net::ERR_IO_PENDING, - entry->WriteData(0, + entry->WriteData(1, 0, truncate_buffer.get(), kReadBufferSize, @@ -3271,7 +3271,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightRead) { CallbackTest write_callback(&helper, false); EXPECT_EQ(net::ERR_IO_PENDING, - entry->WriteData(0, + entry->WriteData(1, 0, write_buffer.get(), kBufferSize, @@ -3283,7 +3283,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightRead) { scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kBufferSize)); CallbackTest read_callback(&helper, false); EXPECT_EQ(net::ERR_IO_PENDING, - entry->ReadData(0, + entry->ReadData(1, 0, read_buffer.get(), kBufferSize, @@ -3367,19 +3367,19 @@ TEST_F(DiskCacheEntryTest, SimpleCacheMultipleReadersCheckCRC2) { disk_cache::Entry* entry = NULL; ASSERT_EQ(net::OK, OpenEntry(key, &entry)); ScopedEntryPtr entry_closer(entry); - EXPECT_EQ(1, ReadData(entry, 0, 0, read_buffer1.get(), 1)); + EXPECT_EQ(1, ReadData(entry, 1, 0, read_buffer1.get(), 1)); // Advance the 2nd reader by the same amount. disk_cache::Entry* entry2 = NULL; EXPECT_EQ(net::OK, OpenEntry(key, &entry2)); ScopedEntryPtr entry2_closer(entry2); - EXPECT_EQ(1, ReadData(entry2, 0, 0, read_buffer2.get(), 1)); + EXPECT_EQ(1, ReadData(entry2, 1, 0, read_buffer2.get(), 1)); // Continue reading 1st. - EXPECT_GT(0, ReadData(entry, 0, 1, read_buffer1.get(), size)); + EXPECT_GT(0, ReadData(entry, 1, 1, read_buffer1.get(), size)); // This read should fail as well because we have previous read failures. - EXPECT_GT(0, ReadData(entry2, 0, 1, read_buffer2.get(), 1)); + EXPECT_GT(0, ReadData(entry2, 1, 1, read_buffer2.get(), 1)); DisableIntegrityCheck(); } @@ -3403,7 +3403,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheReadCombineCRC) { ASSERT_EQ(net::OK, CreateEntry(key, &entry)); EXPECT_NE(null, entry); - EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer1.get(), kSize, false)); + EXPECT_EQ(kSize, WriteData(entry, 1, 0, buffer1.get(), kSize, false)); entry->Close(); disk_cache::Entry* entry2 = NULL; @@ -3414,14 +3414,14 @@ TEST_F(DiskCacheEntryTest, SimpleCacheReadCombineCRC) { int offset = 0; int buf_len = kHalfSize; scoped_refptr<net::IOBuffer> buffer1_read1(new net::IOBuffer(buf_len)); - EXPECT_EQ(buf_len, ReadData(entry2, 0, offset, buffer1_read1.get(), buf_len)); + EXPECT_EQ(buf_len, ReadData(entry2, 1, offset, buffer1_read1.get(), buf_len)); EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read1->data(), buf_len)); // Read the second half of the data. offset = buf_len; buf_len = kHalfSize; scoped_refptr<net::IOBuffer> buffer1_read2(new net::IOBuffer(buf_len)); - EXPECT_EQ(buf_len, ReadData(entry2, 0, offset, buffer1_read2.get(), buf_len)); + EXPECT_EQ(buf_len, ReadData(entry2, 1, offset, buffer1_read2.get(), buf_len)); char* buffer1_data = buffer1->data() + offset; EXPECT_EQ(0, memcmp(buffer1_data, buffer1_read2->data(), buf_len)); @@ -3483,4 +3483,78 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonSequentialWrite) { entry = NULL; } +// Test that changing stream1 size does not affect stream0 (stream0 and stream1 +// are stored in the same file in Simple Cache). +TEST_F(DiskCacheEntryTest, SimpleCacheStream1SizeChanges) { + SetSimpleCacheMode(); + InitCache(); + disk_cache::Entry* entry = NULL; + const char key[] = "the key"; + const int kSize = 100; + scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize)); + scoped_refptr<net::IOBuffer> buffer_read(new net::IOBuffer(kSize)); + CacheTestFillBuffer(buffer->data(), kSize, false); + + ASSERT_EQ(net::OK, CreateEntry(key, &entry)); + EXPECT_TRUE(entry); + + // Write something into stream0. + EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer.get(), kSize, false)); + EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize)); + EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize)); + entry->Close(); + + // Extend stream1. + ASSERT_EQ(net::OK, OpenEntry(key, &entry)); + int stream1_size = 100; + EXPECT_EQ(0, WriteData(entry, 1, stream1_size, buffer.get(), 0, false)); + EXPECT_EQ(stream1_size, entry->GetDataSize(1)); + entry->Close(); + + // Check that stream0 data has not been modified and that the EOF record for + // stream 0 contains a crc. + // The entry needs to be reopened before checking the crc: Open will perform + // the synchronization with the previous Close. This ensures the EOF records + // have been written to disk before we attempt to read them independently. + ASSERT_EQ(net::OK, OpenEntry(key, &entry)); + base::FilePath entry_file0_path = cache_path_.AppendASCII( + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0)); + int flags = base::PLATFORM_FILE_READ | base::PLATFORM_FILE_OPEN; + base::PlatformFile entry_file0 = + base::CreatePlatformFile(entry_file0_path, flags, NULL, NULL); + ASSERT_TRUE(entry_file0 != base::kInvalidPlatformFileValue); + + int data_size[disk_cache::kSimpleEntryStreamCount] = {kSize, stream1_size, 0}; + disk_cache::SimpleEntryStat entry_stat( + base::Time::Now(), base::Time::Now(), data_size); + int eof_offset = entry_stat.GetEOFOffsetInFile(key, 0); + disk_cache::SimpleFileEOF eof_record; + ASSERT_EQ(static_cast<int>(sizeof(eof_record)), base::ReadPlatformFile( + entry_file0, + eof_offset, + reinterpret_cast<char*>(&eof_record), + sizeof(eof_record))); + EXPECT_EQ(disk_cache::kSimpleFinalMagicNumber, eof_record.final_magic_number); + EXPECT_TRUE((eof_record.flags & disk_cache::SimpleFileEOF::FLAG_HAS_CRC32) == + disk_cache::SimpleFileEOF::FLAG_HAS_CRC32); + + buffer_read = new net::IOBuffer(kSize); + EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize)); + EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize)); + + // Shrink stream1. + stream1_size = 50; + EXPECT_EQ(0, WriteData(entry, 1, stream1_size, buffer.get(), 0, true)); + EXPECT_EQ(stream1_size, entry->GetDataSize(1)); + entry->Close(); + + // Check that stream0 data has not been modified. + buffer_read = new net::IOBuffer(kSize); + ASSERT_EQ(net::OK, OpenEntry(key, &entry)); + EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize)); + EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize)); + entry->Close(); + entry = NULL; +} + #endif // defined(OS_POSIX) diff --git a/net/disk_cache/simple/simple_entry_format.h b/net/disk_cache/simple/simple_entry_format.h index d06ab11..cfd598db 100644 --- a/net/disk_cache/simple/simple_entry_format.h +++ b/net/disk_cache/simple/simple_entry_format.h @@ -19,17 +19,23 @@ namespace disk_cache { const uint64 kSimpleInitialMagicNumber = GG_UINT64_C(0xfcfb6d1ba7725c30); const uint64 kSimpleFinalMagicNumber = GG_UINT64_C(0xf4fa6f45970d41d8); -// A file in the Simple cache consists of a SimpleFileHeader followed -// by data. +// A file containing stream 0 and stream 1 in the Simple cache consists of: +// - a SimpleFileHeader. +// - the key. +// - the data from stream 1. +// - a SimpleFileEOF record for stream 1. +// - the data from stream 0. +// - a SimpleFileEOF record for stream 0. -// A file in the Simple cache consists of: +// A file containing stream 2 in the Simple cache consists of: // - a SimpleFileHeader. // - the key. // - the data. // - at the end, a SimpleFileEOF record. -const uint32 kSimpleVersion = 4; +const uint32 kSimpleVersion = 5; -static const int kSimpleEntryFileCount = 3; +static const int kSimpleEntryFileCount = 2; +static const int kSimpleEntryStreamCount = 3; struct NET_EXPORT_PRIVATE SimpleFileHeader { SimpleFileHeader(); @@ -40,7 +46,7 @@ struct NET_EXPORT_PRIVATE SimpleFileHeader { uint32 key_hash; }; -struct SimpleFileEOF { +struct NET_EXPORT_PRIVATE SimpleFileEOF { enum Flags { FLAG_HAS_CRC32 = (1U << 0), }; @@ -50,6 +56,8 @@ struct SimpleFileEOF { uint64 final_magic_number; uint32 flags; uint32 data_crc32; + // |stream_size| is only used in the EOF record for stream 0. + uint32 stream_size; }; } // namespace disk_cache diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc index c4cd1d3..5f24564 100644 --- a/net/disk_cache/simple/simple_entry_impl.cc +++ b/net/disk_cache/simple/simple_entry_impl.cc @@ -174,7 +174,8 @@ SimpleEntryImpl::SimpleEntryImpl(net::CacheType cache_type, state_(STATE_UNINITIALIZED), synchronous_entry_(NULL), net_log_(net::BoundNetLog::Make( - net_log, net::NetLog::SOURCE_DISK_CACHE_ENTRY)) { + net_log, net::NetLog::SOURCE_DISK_CACHE_ENTRY)), + stream_0_data_(new net::GrowableIOBuffer()) { COMPILE_ASSERT(arraysize(data_size_) == arraysize(crc32s_end_offset_), arrays_should_be_same_size); COMPILE_ASSERT(arraysize(data_size_) == arraysize(crc32s_), @@ -337,7 +338,7 @@ int SimpleEntryImpl::ReadData(int stream_index, false)); } - if (stream_index < 0 || stream_index >= kSimpleEntryFileCount || + if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount || buf_len < 0) { if (net_log_.IsLoggingAllEvents()) { net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END, @@ -358,6 +359,8 @@ int SimpleEntryImpl::ReadData(int stream_index, return 0; } + // TODO(clamy): return immediatly when reading from stream 0. + // TODO(felipeg): Optimization: Add support for truly parallel read // operations. bool alone_in_queue = @@ -383,8 +386,8 @@ int SimpleEntryImpl::WriteData(int stream_index, truncate)); } - if (stream_index < 0 || stream_index >= kSimpleEntryFileCount || offset < 0 || - buf_len < 0) { + if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount || + offset < 0 || buf_len < 0) { if (net_log_.IsLoggingAllEvents()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END, @@ -404,16 +407,11 @@ int SimpleEntryImpl::WriteData(int stream_index, } ScopedOperationRunner operation_runner(this); - // Currently, Simple Cache is only used for HTTP, which stores the headers in - // stream 0 and always writes them with a single, truncating write. Detect - // these writes and record the size and size changes of the headers. Also, - // note writes to stream 0 that violate those assumptions. - if (stream_index == 0) { - if (offset == 0 && truncate) - RecordHeaderSizeChange(cache_type_, data_size_[0], buf_len); - else - RecordUnexpectedStream0Write(cache_type_); - } + // Stream 0 data is kept in memory, so can be written immediatly if there are + // no IO operations pending. + if (stream_index == 0 && state_ == STATE_READY && + pending_operations_.size() == 0) + return SetStream0Data(buf, offset, buf_len, truncate); // We can only do optimistic Write if there is no pending operations, so // that we are sure that the next call to RunNextOperationIfNeeded will @@ -694,7 +692,7 @@ void SimpleEntryImpl::CreateEntryInternal(bool have_index, last_used_ = last_modified_ = base::Time::Now(); // If creation succeeds, we should mark all streams to be saved on close. - for (int i = 0; i < kSimpleEntryFileCount; ++i) + for (int i = 0; i < kSimpleEntryStreamCount; ++i) have_written_[i] = true; const base::TimeTicks start_time = base::TimeTicks::Now(); @@ -729,7 +727,7 @@ void SimpleEntryImpl::CloseInternal() { if (state_ == STATE_READY) { DCHECK(synchronous_entry_); state_ = STATE_IO_PENDING; - for (int i = 0; i < kSimpleEntryFileCount; ++i) { + for (int i = 0; i < kSimpleEntryStreamCount; ++i) { if (have_written_[i]) { if (GetDataSize(i) == crc32s_end_offset_[i]) { int32 crc = GetDataSize(i) == 0 ? crc32(0, Z_NULL, 0) : crc32s_[i]; @@ -748,12 +746,13 @@ void SimpleEntryImpl::CloseInternal() { base::Bind(&SimpleSynchronousEntry::Close, base::Unretained(synchronous_entry_), SimpleEntryStat(last_used_, last_modified_, data_size_), - base::Passed(&crc32s_to_write)); + base::Passed(&crc32s_to_write), + stream_0_data_); Closure reply = base::Bind(&SimpleEntryImpl::CloseOperationComplete, this); synchronous_entry_ = NULL; worker_pool_->PostTaskAndReply(FROM_HERE, task, reply); - for (int i = 0; i < kSimpleEntryFileCount; ++i) { + for (int i = 0; i < kSimpleEntryStreamCount; ++i) { if (!have_written_[i]) { SIMPLE_CACHE_UMA(ENUMERATION, "CheckCRCResult", cache_type_, @@ -808,20 +807,31 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index, buf_len = std::min(buf_len, GetDataSize(stream_index) - offset); + // Since stream 0 data is kept in memory, it is read immediately. + if (stream_index == 0) { + int ret_value = ReadStream0Data(buf, offset, buf_len); + if (!callback.is_null()) { + MessageLoopProxy::current()->PostTask(FROM_HERE, + base::Bind(callback, ret_value)); + } + return; + } + state_ = STATE_IO_PENDING; if (!doomed_ && backend_.get()) backend_->index()->UseIfExists(entry_hash_); scoped_ptr<uint32> read_crc32(new uint32()); scoped_ptr<int> result(new int()); - scoped_ptr<base::Time> last_used(new base::Time()); + scoped_ptr<SimpleEntryStat> entry_stat( + new SimpleEntryStat(last_used_, last_modified_, data_size_)); Closure task = base::Bind( &SimpleSynchronousEntry::ReadData, base::Unretained(synchronous_entry_), SimpleSynchronousEntry::EntryOperationData(stream_index, offset, buf_len), make_scoped_refptr(buf), read_crc32.get(), - last_used.get(), + entry_stat.get(), result.get()); Closure reply = base::Bind(&SimpleEntryImpl::ReadOperationComplete, this, @@ -829,7 +839,7 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index, offset, callback, base::Passed(&read_crc32), - base::Passed(&last_used), + base::Passed(&entry_stat), base::Passed(&result)); worker_pool_->PostTaskAndReply(FROM_HERE, task, reply); } @@ -866,24 +876,22 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index, } DCHECK_EQ(STATE_READY, state_); + + // Since stream 0 data is kept in memory, it will be written immediatly. + if (stream_index == 0) { + int ret_value = SetStream0Data(buf, offset, buf_len, truncate); + if (!callback.is_null()) { + MessageLoopProxy::current()->PostTask(FROM_HERE, + base::Bind(callback, ret_value)); + } + return; + } + state_ = STATE_IO_PENDING; if (!doomed_ && backend_.get()) backend_->index()->UseIfExists(entry_hash_); - // It is easy to incrementally compute the CRC from [0 .. |offset + buf_len|) - // if |offset == 0| or we have already computed the CRC for [0 .. offset). - // We rely on most write operations being sequential, start to end to compute - // the crc of the data. When we write to an entry and close without having - // done a sequential write, we don't check the CRC on read. - if (offset == 0 || crc32s_end_offset_[stream_index] == offset) { - uint32 initial_crc = (offset != 0) ? crc32s_[stream_index] - : crc32(0, Z_NULL, 0); - if (buf_len > 0) { - crc32s_[stream_index] = crc32(initial_crc, - reinterpret_cast<const Bytef*>(buf->data()), - buf_len); - } - crc32s_end_offset_[stream_index] = offset + buf_len; - } + + AdvanceCrc(buf, offset, buf_len, stream_index); // |entry_stat| needs to be initialized before modifying |data_size_|. scoped_ptr<SimpleEntryStat> entry_stat( @@ -900,6 +908,10 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index, last_used_ = last_modified_ = base::Time::Now(); have_written_[stream_index] = true; + // Writing on stream 1 affects the placement of stream 0 in the file, the EOF + // record will have to be rewritten. + if (stream_index == 1) + have_written_[0] = true; scoped_ptr<int> result(new int()); Closure task = base::Bind(&SimpleSynchronousEntry::WriteData, @@ -956,6 +968,13 @@ void SimpleEntryImpl::CreationOperationComplete( state_ = STATE_READY; synchronous_entry_ = in_results->sync_entry; + if (in_results->stream_0_data) { + stream_0_data_ = in_results->stream_0_data; + // The crc was read in SimpleSynchronousEntry. + crc_check_state_[0] = CRC_CHECK_DONE; + crc32s_[0] = in_results->stream_0_crc32; + crc32s_end_offset_[0] = in_results->entry_stat.data_size(0); + } if (key_.empty()) { SetKey(synchronous_entry_->key()); } else { @@ -1003,7 +1022,7 @@ void SimpleEntryImpl::ReadOperationComplete( int offset, const CompletionCallback& completion_callback, scoped_ptr<uint32> read_crc32, - scoped_ptr<base::Time> last_used, + scoped_ptr<SimpleEntryStat> entry_stat, scoped_ptr<int> result) { DCHECK(io_thread_checker_.CalledOnValidThread()); DCHECK(synchronous_entry_); @@ -1039,7 +1058,7 @@ void SimpleEntryImpl::ReadOperationComplete( Closure task = base::Bind(&SimpleSynchronousEntry::CheckEOFRecord, base::Unretained(synchronous_entry_), stream_index, - data_size_[stream_index], + *entry_stat, crc32s_[stream_index], new_result.get()); Closure reply = base::Bind(&SimpleEntryImpl::ChecksumOperationComplete, @@ -1068,10 +1087,7 @@ void SimpleEntryImpl::ReadOperationComplete( } EntryOperationComplete( - stream_index, - completion_callback, - SimpleEntryStat(*last_used, last_modified_, data_size_), - result.Pass()); + stream_index, completion_callback, *entry_stat, result.Pass()); } void SimpleEntryImpl::WriteOperationComplete( @@ -1158,10 +1174,10 @@ void SimpleEntryImpl::UpdateDataFromEntryStat( DCHECK(synchronous_entry_); DCHECK_EQ(STATE_READY, state_); - last_used_ = entry_stat.last_used; - last_modified_ = entry_stat.last_modified; - for (int i = 0; i < kSimpleEntryFileCount; ++i) { - data_size_[i] = entry_stat.data_size[i]; + last_used_ = entry_stat.last_used(); + last_modified_ = entry_stat.last_modified(); + for (int i = 0; i < kSimpleEntryStreamCount; ++i) { + data_size_[i] = entry_stat.data_size(i); } if (!doomed_ && backend_.get()) backend_->index()->UpdateEntrySize(entry_hash_, GetDiskUsage()); @@ -1169,7 +1185,7 @@ void SimpleEntryImpl::UpdateDataFromEntryStat( int64 SimpleEntryImpl::GetDiskUsage() const { int64 file_size = 0; - for (int i = 0; i < kSimpleEntryFileCount; ++i) { + for (int i = 0; i < kSimpleEntryStreamCount; ++i) { file_size += simple_util::GetFileSizeFromKeyAndDataSize(key_, data_size_[i]); } @@ -1247,4 +1263,76 @@ void SimpleEntryImpl::RecordWriteDependencyType( type, WRITE_DEPENDENCY_TYPE_MAX); } +int SimpleEntryImpl::ReadStream0Data(net::IOBuffer* buf, + int offset, + int buf_len) { + if (buf_len < 0) { + RecordReadResult(cache_type_, READ_RESULT_SYNC_READ_FAILURE); + return 0; + } + memcpy(buf->data(), stream_0_data_->data() + offset, buf_len); + UpdateDataFromEntryStat( + SimpleEntryStat(base::Time::Now(), last_modified_, data_size_)); + RecordReadResult(cache_type_, READ_RESULT_SUCCESS); + return buf_len; +} + +int SimpleEntryImpl::SetStream0Data(net::IOBuffer* buf, + int offset, + int buf_len, + bool truncate) { + // Currently, stream 0 is only used for HTTP headers, and always writes them + // with a single, truncating write. Detect these writes and record the size + // changes of the headers. Also, support writes to stream 0 that have + // different access patterns, as required by the API contract. + // All other clients of the Simple Cache are encouraged to use stream 1. + have_written_[0] = true; + int data_size = GetDataSize(0); + if (offset == 0 && truncate) { + RecordHeaderSizeChange(cache_type_, data_size, buf_len); + stream_0_data_->SetCapacity(buf_len); + memcpy(stream_0_data_->data(), buf->data(), buf_len); + data_size_[0] = buf_len; + } else { + RecordUnexpectedStream0Write(cache_type_); + const int buffer_size = + truncate ? offset + buf_len : std::max(offset + buf_len, data_size); + stream_0_data_->SetCapacity(buffer_size); + // If |stream_0_data_| was extended, the extension until offset needs to be + // zero-filled. + const int fill_size = offset <= data_size ? 0 : offset - data_size; + if (fill_size > 0) + memset(stream_0_data_->data() + data_size, 0, fill_size); + if (buf) + memcpy(stream_0_data_->data() + offset, buf->data(), buf_len); + data_size_[0] = buffer_size; + } + base::Time modification_time = base::Time::Now(); + AdvanceCrc(buf, offset, buf_len, 0); + UpdateDataFromEntryStat( + SimpleEntryStat(modification_time, modification_time, data_size_)); + RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS); + return buf_len; +} + +void SimpleEntryImpl::AdvanceCrc(net::IOBuffer* buffer, + int offset, + int length, + int stream_index) { + // It is easy to incrementally compute the CRC from [0 .. |offset + buf_len|) + // if |offset == 0| or we have already computed the CRC for [0 .. offset). + // We rely on most write operations being sequential, start to end to compute + // the crc of the data. When we write to an entry and close without having + // done a sequential write, we don't check the CRC on read. + if (offset == 0 || crc32s_end_offset_[stream_index] == offset) { + uint32 initial_crc = + (offset != 0) ? crc32s_[stream_index] : crc32(0, Z_NULL, 0); + if (length > 0) { + crc32s_[stream_index] = crc32( + initial_crc, reinterpret_cast<const Bytef*>(buffer->data()), length); + } + crc32s_end_offset_[stream_index] = offset + length; + } +} + } // namespace disk_cache diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h index d7d9b8f..e2f0c63 100644 --- a/net/disk_cache/simple/simple_entry_impl.h +++ b/net/disk_cache/simple/simple_entry_impl.h @@ -25,6 +25,7 @@ class TaskRunner; } namespace net { +class GrowableIOBuffer; class IOBuffer; } @@ -32,7 +33,7 @@ namespace disk_cache { class SimpleBackendImpl; class SimpleSynchronousEntry; -struct SimpleEntryStat; +class SimpleEntryStat; struct SimpleEntryCreationResults; // SimpleEntryImpl is the IO thread interface to an entry in the very simple @@ -217,7 +218,7 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, int offset, const CompletionCallback& completion_callback, scoped_ptr<uint32> read_crc32, - scoped_ptr<base::Time> last_used, + scoped_ptr<SimpleEntryStat> entry_stat, scoped_ptr<int> result); // Called after an asynchronous write completes. @@ -251,6 +252,23 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, void RecordReadIsParallelizable(const SimpleEntryOperation& operation) const; void RecordWriteDependencyType(const SimpleEntryOperation& operation) const; + // Reads from the stream 0 data kept in memory. + int ReadStream0Data(net::IOBuffer* buf, int offset, int buf_len); + + // Copies data from |buf| to the internal in-memory buffer for stream 0. If + // |truncate| is set to true, the target buffer will be truncated at |offset| + // + |buf_len| before being written. + int SetStream0Data(net::IOBuffer* buf, + int offset, int buf_len, + bool truncate); + + // Updates |crc32s_| and |crc32s_end_offset_| for a write of the data in + // |buffer| on |stream_index|, starting at |offset| and of length |length|. + void AdvanceCrc(net::IOBuffer* buffer, + int offset, + int length, + int stream_index); + // All nonstatic SimpleEntryImpl methods should always be called on the IO // thread, in all cases. |io_thread_checker_| documents and enforces this. base::ThreadChecker io_thread_checker_; @@ -268,7 +286,7 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, // TODO(clamy): Unify last_used_ with data in the index. base::Time last_used_; base::Time last_modified_; - int32 data_size_[kSimpleEntryFileCount]; + int32 data_size_[kSimpleEntryStreamCount]; // Number of times this object has been returned from Backend::OpenEntry() and // Backend::CreateEntry() without subsequent Entry::Close() calls. Used to @@ -283,15 +301,16 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, // write. For each stream, |crc32s_[index]| is the crc32 of that stream from // [0 .. |crc32s_end_offset_|). If |crc32s_end_offset_[index] == 0| then the // value of |crc32s_[index]| is undefined. - int32 crc32s_end_offset_[kSimpleEntryFileCount]; - uint32 crc32s_[kSimpleEntryFileCount]; + int32 crc32s_end_offset_[kSimpleEntryStreamCount]; + uint32 crc32s_[kSimpleEntryStreamCount]; - // If |have_written_[index]| is true, we have written to the stream |index|. - bool have_written_[kSimpleEntryFileCount]; + // If |have_written_[index]| is true, we have written to the file that + // contains stream |index|. + bool have_written_[kSimpleEntryStreamCount]; // Reflects how much CRC checking has been done with the entry. This state is // reported on closing each entry stream. - CheckCrcResult crc_check_state_[kSimpleEntryFileCount]; + CheckCrcResult crc_check_state_[kSimpleEntryStreamCount]; // The |synchronous_entry_| is the worker thread object that performs IO on // entries. It's owned by this SimpleEntryImpl whenever |executing_operation_| @@ -306,6 +325,17 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, net::BoundNetLog net_log_; scoped_ptr<SimpleEntryOperation> executing_operation_; + + // Unlike other streams, stream 0 data is read from the disk when the entry is + // opened, and then kept in memory. All read/write operations on stream 0 + // affect the |stream_0_data_| buffer. When the entry is closed, + // |stream_0_data_| is written to the disk. + // Stream 0 is kept in memory because it is stored in the same file as stream + // 1 on disk, to reduce the number of file descriptors and save disk space. + // This strategy allows stream 1 to change size easily. Since stream 0 is only + // used to write HTTP headers, the memory consumption of keeping it in memory + // is acceptable. + scoped_refptr<net::GrowableIOBuffer> stream_0_data_; }; } // namespace disk_cache diff --git a/net/disk_cache/simple/simple_index_file.cc b/net/disk_cache/simple/simple_index_file.cc index 61318c8..488d645 100644 --- a/net/disk_cache/simple/simple_index_file.cc +++ b/net/disk_cache/simple/simple_index_file.cc @@ -117,7 +117,7 @@ void ProcessEntryFile(SimpleIndex::EntrySet* entries, EntryMetadata(last_used_time, file_size), entries); } else { - // Summing up the total size of the entry through all the *_[0-2] files + // Summing up the total size of the entry through all the *_[0-1] files it->second.SetEntrySize(it->second.GetEntrySize() + file_size); } } @@ -411,10 +411,6 @@ void SimpleIndexFile::SyncRestoreFromDisk( out_result->Reset(); SimpleIndex::EntrySet* entries = &out_result->entries; - // TODO(felipeg,gavinp): Fix this once we have a one-file per entry format. - COMPILE_ASSERT(kSimpleEntryFileCount == 3, - file_pattern_must_match_file_count); - const bool did_succeed = TraverseCacheDirectory( cache_directory, base::Bind(&ProcessEntryFile, entries)); if (!did_succeed) { diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc index a1ff1ac..502264d 100644 --- a/net/disk_cache/simple/simple_synchronous_entry.cc +++ b/net/disk_cache/simple/simple_synchronous_entry.cc @@ -141,27 +141,55 @@ void RecordCloseResult(net::CacheType cache_type, CloseResult result) { namespace disk_cache { -using simple_util::ConvertEntryHashKeyToHexString; using simple_util::GetEntryHashKey; -using simple_util::GetFilenameFromEntryHashAndIndex; +using simple_util::GetFilenameFromEntryHashAndFileIndex; using simple_util::GetDataSizeFromKeyAndFileSize; using simple_util::GetFileSizeFromKeyAndDataSize; -using simple_util::GetFileOffsetFromKeyAndDataOffset; +using simple_util::GetFileIndexFromStreamIndex; + +SimpleEntryStat::SimpleEntryStat(base::Time last_used, + base::Time last_modified, + const int32 data_size[]) + : last_used_(last_used), + last_modified_(last_modified) { + memcpy(data_size_, data_size, sizeof(data_size_)); +} -SimpleEntryStat::SimpleEntryStat() {} +int SimpleEntryStat::GetOffsetInFile(const std::string& key, + int offset, + int stream_index) const { + const int64 headers_size = sizeof(SimpleFileHeader) + key.size(); + const int64 additional_offset = + stream_index == 0 ? data_size_[1] + sizeof(SimpleFileEOF) : 0; + return headers_size + offset + additional_offset; +} -SimpleEntryStat::SimpleEntryStat(base::Time last_used_p, - base::Time last_modified_p, - const int32 data_size_p[]) - : last_used(last_used_p), - last_modified(last_modified_p) { - memcpy(data_size, data_size_p, sizeof(data_size)); +int SimpleEntryStat::GetEOFOffsetInFile(const std::string& key, + int stream_index) const { + return GetOffsetInFile(key, data_size_[stream_index], stream_index); +} + +int SimpleEntryStat::GetLastEOFOffsetInFile(const std::string& key, + int stream_index) const { + const int file_index = GetFileIndexFromStreamIndex(stream_index); + const int eof_data_offset = + file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF) + : data_size_[2]; + return GetOffsetInFile(key, eof_data_offset, stream_index); +} + +int SimpleEntryStat::GetFileSize(const std::string& key, int file_index) const { + const int total_data_size = + file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF) + : data_size_[2]; + return GetFileSizeFromKeyAndDataSize(key, total_data_size); } SimpleEntryCreationResults::SimpleEntryCreationResults( SimpleEntryStat entry_stat) : sync_entry(NULL), entry_stat(entry_stat), + stream_0_crc32(crc32(0, Z_NULL, 0)), result(net::OK) { } @@ -205,12 +233,16 @@ void SimpleSynchronousEntry::OpenEntry( SimpleEntryCreationResults *out_results) { SimpleSynchronousEntry* sync_entry = new SimpleSynchronousEntry(cache_type, path, "", entry_hash); - out_results->result = sync_entry->InitializeForOpen( - had_index, &out_results->entry_stat); + out_results->result = + sync_entry->InitializeForOpen(had_index, + &out_results->entry_stat, + &out_results->stream_0_data, + &out_results->stream_0_crc32); if (out_results->result != net::OK) { sync_entry->Doom(); delete sync_entry; out_results->sync_entry = NULL; + out_results->stream_0_data = NULL; return; } out_results->sync_entry = sync_entry; @@ -246,8 +278,8 @@ bool SimpleSynchronousEntry::DeleteFilesForEntryHash( const uint64 entry_hash) { bool result = true; for (int i = 0; i < kSimpleEntryFileCount; ++i) { - FilePath to_delete = path.AppendASCII( - GetFilenameFromEntryHashAndIndex(entry_hash, i)); + FilePath to_delete = + path.AppendASCII(GetFilenameFromEntryHashAndFileIndex(entry_hash, i)); if (!base::DeleteFile(to_delete, false)) { result = false; DLOG(ERROR) << "Could not delete " << to_delete.MaybeAsASCII(); @@ -279,17 +311,17 @@ int SimpleSynchronousEntry::DoomEntrySet( void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op, net::IOBuffer* out_buf, uint32* out_crc32, - base::Time* out_last_used, + SimpleEntryStat* entry_stat, int* out_result) const { DCHECK(initialized_); - int64 file_offset = - GetFileOffsetFromKeyAndDataOffset(key_, in_entry_op.offset); - int bytes_read = ReadPlatformFile(files_[in_entry_op.index], - file_offset, - out_buf->data(), - in_entry_op.buf_len); + DCHECK_NE(0, in_entry_op.index); + const int64 file_offset = + entry_stat->GetOffsetInFile(key_, in_entry_op.offset, in_entry_op.index); + int file_index = GetFileIndexFromStreamIndex(in_entry_op.index); + int bytes_read = ReadPlatformFile( + files_[file_index], file_offset, out_buf->data(), in_entry_op.buf_len); if (bytes_read > 0) { - *out_last_used = Time::Now(); + entry_stat->set_last_used(Time::Now()); *out_crc32 = crc32(crc32(0L, Z_NULL, 0), reinterpret_cast<const Bytef*>(out_buf->data()), bytes_read); @@ -307,27 +339,30 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op, SimpleEntryStat* out_entry_stat, int* out_result) const { DCHECK(initialized_); + DCHECK_NE(0, in_entry_op.index); int index = in_entry_op.index; + int file_index = GetFileIndexFromStreamIndex(index); int offset = in_entry_op.offset; int buf_len = in_entry_op.buf_len; int truncate = in_entry_op.truncate; - - bool extending_by_write = offset + buf_len > out_entry_stat->data_size[index]; + const int64 file_offset = out_entry_stat->GetOffsetInFile( + key_, in_entry_op.offset, in_entry_op.index); + bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index); if (extending_by_write) { - // We are extending the file, and need to insure the EOF record is zeroed. - const int64 file_eof_offset = GetFileOffsetFromKeyAndDataOffset( - key_, out_entry_stat->data_size[index]); - if (!TruncatePlatformFile(files_[index], file_eof_offset)) { + // The EOF record and the eventual stream afterward need to be zeroed out. + const int64 file_eof_offset = + out_entry_stat->GetEOFOffsetInFile(key_, index); + if (!TruncatePlatformFile(files_[file_index], file_eof_offset)) { RecordWriteResult(cache_type_, WRITE_RESULT_PRETRUNCATE_FAILURE); Doom(); *out_result = net::ERR_CACHE_WRITE_FAILURE; return; } } - const int64 file_offset = GetFileOffsetFromKeyAndDataOffset(key_, offset); if (buf_len > 0) { if (WritePlatformFile( - files_[index], file_offset, in_buf->data(), buf_len) != buf_len) { + files_[file_index], file_offset, in_buf->data(), buf_len) != + buf_len) { RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE); Doom(); *out_result = net::ERR_CACHE_WRITE_FAILURE; @@ -335,79 +370,89 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op, } } if (!truncate && (buf_len > 0 || !extending_by_write)) { - out_entry_stat->data_size[index] = - std::max(out_entry_stat->data_size[index], offset + buf_len); + out_entry_stat->set_data_size( + index, std::max(out_entry_stat->data_size(index), offset + buf_len)); } else { - if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) { + out_entry_stat->set_data_size(index, offset + buf_len); + int file_eof_offset = out_entry_stat->GetLastEOFOffsetInFile(key_, index); + if (!TruncatePlatformFile(files_[file_index], file_eof_offset)) { RecordWriteResult(cache_type_, WRITE_RESULT_TRUNCATE_FAILURE); Doom(); *out_result = net::ERR_CACHE_WRITE_FAILURE; return; } - out_entry_stat->data_size[index] = offset + buf_len; } RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS); - out_entry_stat->last_used = out_entry_stat->last_modified = Time::Now(); + base::Time modification_time = Time::Now(); + out_entry_stat->set_last_used(modification_time); + out_entry_stat->set_last_modified(modification_time); *out_result = buf_len; } void SimpleSynchronousEntry::CheckEOFRecord(int index, - int32 data_size, + const SimpleEntryStat& entry_stat, uint32 expected_crc32, int* out_result) const { DCHECK(initialized_); - - SimpleFileEOF eof_record; - int64 file_offset = GetFileOffsetFromKeyAndDataOffset(key_, data_size); - if (ReadPlatformFile(files_[index], - file_offset, - reinterpret_cast<char*>(&eof_record), - sizeof(eof_record)) != sizeof(eof_record)) { - RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE); - Doom(); - *out_result = net::ERR_CACHE_CHECKSUM_READ_FAILURE; - return; - } - - if (eof_record.final_magic_number != kSimpleFinalMagicNumber) { - RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH); - DLOG(INFO) << "eof record had bad magic number."; + uint32 crc32; + bool has_crc32; + int stream_size; + *out_result = + GetEOFRecordData(index, entry_stat, &has_crc32, &crc32, &stream_size); + if (*out_result != net::OK) { Doom(); - *out_result = net::ERR_CACHE_CHECKSUM_READ_FAILURE; return; } - - const bool has_crc = (eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) == - SimpleFileEOF::FLAG_HAS_CRC32; - SIMPLE_CACHE_UMA(BOOLEAN, "SyncCheckEOFHasCrc", cache_type_, has_crc); - if (has_crc && eof_record.data_crc32 != expected_crc32) { + if (has_crc32 && crc32 != expected_crc32) { + DLOG(INFO) << "EOF record had bad crc."; + *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH; RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH); - DLOG(INFO) << "eof record had bad crc."; Doom(); - *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH; return; } - RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS); - *out_result = net::OK; } void SimpleSynchronousEntry::Close( const SimpleEntryStat& entry_stat, - scoped_ptr<std::vector<CRCRecord> > crc32s_to_write) { + scoped_ptr<std::vector<CRCRecord> > crc32s_to_write, + net::GrowableIOBuffer* stream_0_data) { + DCHECK(stream_0_data); + // Write stream 0 data. + int stream_0_offset = entry_stat.GetOffsetInFile(key_, 0, 0); + if (WritePlatformFile(files_[0], + stream_0_offset, + stream_0_data->data(), + entry_stat.data_size(0)) != entry_stat.data_size(0)) { + RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); + DLOG(INFO) << "Could not write stream 0 data."; + Doom(); + } + for (std::vector<CRCRecord>::const_iterator it = crc32s_to_write->begin(); it != crc32s_to_write->end(); ++it) { SimpleFileEOF eof_record; + int index = it->index; + eof_record.stream_size = entry_stat.data_size(index); eof_record.final_magic_number = kSimpleFinalMagicNumber; eof_record.flags = 0; if (it->has_crc32) eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32; eof_record.data_crc32 = it->data_crc32; - int64 file_offset = GetFileOffsetFromKeyAndDataOffset( - key_, entry_stat.data_size[it->index]); - if (WritePlatformFile(files_[it->index], - file_offset, + int file_index = GetFileIndexFromStreamIndex(index); + int eof_offset = entry_stat.GetEOFOffsetInFile(key_, index); + // If stream 0 changed size, the file needs to be resized, otherwise the + // next open will yield wrong stream sizes. On stream 1 and stream 2 proper + // resizing of the file is handled in SimpleSynchronousEntry::WriteData(). + if (index == 0 && !TruncatePlatformFile(files_[file_index], eof_offset)) { + RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); + DLOG(INFO) << "Could not truncate stream 0 file."; + Doom(); + break; + } + if (WritePlatformFile(files_[file_index], + eof_offset, reinterpret_cast<const char*>(&eof_record), sizeof(eof_record)) != sizeof(eof_record)) { RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); @@ -415,7 +460,11 @@ void SimpleSynchronousEntry::Close( Doom(); break; } - const int64 file_size = file_offset + sizeof(eof_record); + } + for (int i = 0; i < kSimpleEntryFileCount; ++i) { + bool did_close_file = ClosePlatformFile(files_[i]); + DCHECK(did_close_file); + const int64 file_size = entry_stat.GetFileSize(key_, i); SIMPLE_CACHE_UMA(CUSTOM_COUNTS, "LastClusterSize", cache_type_, file_size % 4096, 0, 4097, 50); @@ -424,11 +473,6 @@ void SimpleSynchronousEntry::Close( "LastClusterLossPercent", cache_type_, cluster_loss * 100 / (cluster_loss + file_size)); } - - for (int i = 0; i < kSimpleEntryFileCount; ++i) { - bool did_close_file = ClosePlatformFile(files_[i]); - CHECK(did_close_file); - } RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS); have_open_files_ = false; delete this; @@ -460,8 +504,8 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles( bool had_index, SimpleEntryStat* out_entry_stat) { for (int i = 0; i < kSimpleEntryFileCount; ++i) { - FilePath filename = path_.AppendASCII( - GetFilenameFromEntryHashAndIndex(entry_hash_, i)); + FilePath filename = + path_.AppendASCII(GetFilenameFromEntryHashAndFileIndex(entry_hash_, i)); int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE; if (create) flags |= PLATFORM_FILE_CREATE; @@ -516,9 +560,11 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles( have_open_files_ = true; if (create) { - out_entry_stat->last_modified = out_entry_stat->last_used = Time::Now(); - for (int i = 0; i < kSimpleEntryFileCount; ++i) - out_entry_stat->data_size[i] = 0; + base::Time creation_time = Time::Now(); + out_entry_stat->set_last_modified(creation_time); + out_entry_stat->set_last_used(creation_time); + for (int i = 0; i < kSimpleEntryStreamCount; ++i) + out_entry_stat->set_data_size(i, 0); } else { base::TimeDelta entry_age = base::Time::Now() - base::Time::UnixEpoch(); for (int i = 0; i < kSimpleEntryFileCount; ++i) { @@ -529,20 +575,31 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles( DLOG(WARNING) << "Could not get platform file info."; continue; } - out_entry_stat->last_used = file_info.last_accessed; + out_entry_stat->set_last_used(file_info.last_accessed); if (simple_util::GetMTime(path_, &file_last_modified)) - out_entry_stat->last_modified = file_last_modified; + out_entry_stat->set_last_modified(file_last_modified); else - out_entry_stat->last_modified = file_info.last_modified; + out_entry_stat->set_last_modified(file_info.last_modified); base::TimeDelta stream_age = - base::Time::Now() - out_entry_stat->last_modified; + base::Time::Now() - out_entry_stat->last_modified(); if (stream_age < entry_age) entry_age = stream_age; - // Keep the file size in |data size_| briefly until the key is initialized - // properly. - out_entry_stat->data_size[i] = file_info.size; + // Two things prevent from knowing the right values for |data_size|: + // 1) The key is not known, hence its length is unknown. + // 2) Stream 0 and stream 1 are in the same file, and the exact size for + // each will only be known when reading the EOF record for stream 0. + // + // The size for file 0 and 1 is temporarily kept in + // |data_size(1)| and |data_size(2)| respectively. Reading the key in + // InitializeForOpen yields the data size for each file. In the case of + // file hash_1, this is the total size of stream 2, and is assigned to + // data_size(2). In the case of file 0, it is the combined size of stream + // 0, stream 1 and one EOF record. The exact distribution of sizes between + // stream 1 and stream 0 is only determined after reading the EOF record + // for stream 0 in ReadAndValidateStream0. + out_entry_stat->set_data_size(i + 1, file_info.size); } SIMPLE_CACHE_UMA(CUSTOM_COUNTS, "SyncOpenEntryAge", cache_type_, @@ -560,12 +617,14 @@ void SimpleSynchronousEntry::CloseFiles() { } } -int SimpleSynchronousEntry::InitializeForOpen(bool had_index, - SimpleEntryStat* out_entry_stat) { +int SimpleSynchronousEntry::InitializeForOpen( + bool had_index, + SimpleEntryStat* out_entry_stat, + scoped_refptr<net::GrowableIOBuffer>* stream_0_data, + uint32* out_stream_0_crc32) { DCHECK(!initialized_); if (!OpenOrCreateFiles(false, had_index, out_entry_stat)) return net::ERR_FAILED; - for (int i = 0; i < kSimpleEntryFileCount; ++i) { SimpleFileHeader header; int header_read_result = @@ -602,12 +661,19 @@ int SimpleSynchronousEntry::InitializeForOpen(bool had_index, } key_ = std::string(key.get(), header.key_length); - out_entry_stat->data_size[i] = - GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size[i]); - if (out_entry_stat->data_size[i] < 0) { - // This entry can't possibly be valid, as it does not have enough space to - // store a valid SimpleFileEOF record. - return net::ERR_FAILED; + if (i == 0) { + // File size for stream 0 has been stored temporarily in data_size[1]. + int total_data_size = + GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(1)); + int ret_value_stream_0 = ReadAndValidateStream0( + total_data_size, out_entry_stat, stream_0_data, out_stream_0_crc32); + if (ret_value_stream_0 != net::OK) + return ret_value_stream_0; + } else { + out_entry_stat->set_data_size( + 2, GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(2))); + if (out_entry_stat->data_size(2) < 0) + return net::ERR_FAILED; } if (base::Hash(key.get(), header.key_length) != header.key_hash) { @@ -638,16 +704,18 @@ int SimpleSynchronousEntry::InitializeForCreate( header.key_length = key_.size(); header.key_hash = base::Hash(key_); - if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), - sizeof(header)) != sizeof(header)) { - DLOG(WARNING) << "Could not write headers to new cache entry."; + if (WritePlatformFile( + files_[i], 0, reinterpret_cast<char*>(&header), sizeof(header)) != + sizeof(header)) { + DLOG(WARNING) << "Could not write cache file header to cache entry."; RecordSyncCreateResult( cache_type_, CREATE_ENTRY_CANT_WRITE_HEADER, had_index); return net::ERR_FAILED; } - if (WritePlatformFile(files_[i], sizeof(header), key_.data(), - key_.size()) != implicit_cast<int>(key_.size())) { + if (WritePlatformFile( + files_[i], sizeof(SimpleFileHeader), key_.data(), key_.size()) != + implicit_cast<int>(key_.size())) { DLOG(WARNING) << "Could not write keys to new cache entry."; RecordSyncCreateResult( cache_type_, CREATE_ENTRY_CANT_WRITE_KEY, had_index); @@ -659,6 +727,88 @@ int SimpleSynchronousEntry::InitializeForCreate( return net::OK; } +int SimpleSynchronousEntry::ReadAndValidateStream0( + int total_data_size, + SimpleEntryStat* out_entry_stat, + scoped_refptr<net::GrowableIOBuffer>* stream_0_data, + uint32* out_stream_0_crc32) const { + // Temporarily assign all the data size to stream 1 in order to read the + // EOF record for stream 0, which contains the size of stream 0. + out_entry_stat->set_data_size(0, 0); + out_entry_stat->set_data_size(1, total_data_size - sizeof(SimpleFileEOF)); + + bool has_crc32; + uint32 read_crc32; + int stream_0_size; + int ret_value_crc32 = GetEOFRecordData( + 0, *out_entry_stat, &has_crc32, &read_crc32, &stream_0_size); + if (ret_value_crc32 != net::OK) + return ret_value_crc32; + + if (stream_0_size > out_entry_stat->data_size(1)) + return net::ERR_FAILED; + + // These are the real values of data size. + out_entry_stat->set_data_size(0, stream_0_size); + out_entry_stat->set_data_size( + 1, out_entry_stat->data_size(1) - stream_0_size); + + // Put stream 0 data in memory. + *stream_0_data = new net::GrowableIOBuffer(); + (*stream_0_data)->SetCapacity(stream_0_size); + int file_offset = out_entry_stat->GetOffsetInFile(key_, 0, 0); + int bytes_read = ReadPlatformFile( + files_[0], file_offset, (*stream_0_data)->data(), stream_0_size); + if (bytes_read != stream_0_size) + return net::ERR_FAILED; + + // Check the CRC32. + uint32 expected_crc32 = + stream_0_size == 0 + ? crc32(0, Z_NULL, 0) + : crc32(crc32(0, Z_NULL, 0), + reinterpret_cast<const Bytef*>((*stream_0_data)->data()), + stream_0_size); + if (has_crc32 && read_crc32 != expected_crc32) { + DLOG(INFO) << "EOF record had bad crc."; + RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH); + return net::ERR_FAILED; + } + *out_stream_0_crc32 = read_crc32; + RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS); + return net::OK; +} + +int SimpleSynchronousEntry::GetEOFRecordData(int index, + const SimpleEntryStat& entry_stat, + bool* out_has_crc32, + uint32* out_crc32, + int* out_data_size) const { + SimpleFileEOF eof_record; + int file_offset = entry_stat.GetEOFOffsetInFile(key_, index); + int file_index = GetFileIndexFromStreamIndex(index); + if (ReadPlatformFile(files_[file_index], + file_offset, + reinterpret_cast<char*>(&eof_record), + sizeof(eof_record)) != sizeof(eof_record)) { + RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE); + return net::ERR_CACHE_CHECKSUM_READ_FAILURE; + } + + if (eof_record.final_magic_number != kSimpleFinalMagicNumber) { + RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH); + DLOG(INFO) << "EOF record had bad magic number."; + return net::ERR_CACHE_CHECKSUM_READ_FAILURE; + } + + *out_has_crc32 = (eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) == + SimpleFileEOF::FLAG_HAS_CRC32; + *out_crc32 = eof_record.data_crc32; + *out_data_size = eof_record.stream_size; + SIMPLE_CACHE_UMA(BOOLEAN, "SyncCheckEOFHasCrc", cache_type_, *out_has_crc32); + return net::OK; +} + void SimpleSynchronousEntry::Doom() const { // TODO(gavinp): Consider if we should guard against redundant Doom() calls. DeleteFilesForEntryHash(path_, entry_hash_); diff --git a/net/disk_cache/simple/simple_synchronous_entry.h b/net/disk_cache/simple/simple_synchronous_entry.h index f4b5ed8..ae270c3 100644 --- a/net/disk_cache/simple/simple_synchronous_entry.h +++ b/net/disk_cache/simple/simple_synchronous_entry.h @@ -11,13 +11,16 @@ #include <vector> #include "base/files/file_path.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/platform_file.h" #include "base/time/time.h" #include "net/base/cache_type.h" +#include "net/base/net_export.h" #include "net/disk_cache/simple/simple_entry_format.h" namespace net { +class GrowableIOBuffer; class IOBuffer; } @@ -25,15 +28,38 @@ namespace disk_cache { class SimpleSynchronousEntry; -struct SimpleEntryStat { - SimpleEntryStat(); - SimpleEntryStat(base::Time last_used_p, - base::Time last_modified_p, - const int32 data_size_p[]); +// This class handles the passing of data about the entry between +// SimpleEntryImplementation and SimpleSynchronousEntry and the computation of +// file offsets based on the data size for all streams. +class NET_EXPORT_PRIVATE SimpleEntryStat { + public: + SimpleEntryStat(base::Time last_used, + base::Time last_modified, + const int32 data_size[]); + + int GetOffsetInFile(const std::string& key, + int offset, + int stream_index) const; + int GetEOFOffsetInFile(const std::string& key, int stream_index) const; + int GetLastEOFOffsetInFile(const std::string& key, int file_index) const; + int GetFileSize(const std::string& key, int file_index) const; + + base::Time last_used() const { return last_used_; } + base::Time last_modified() const { return last_modified_; } + void set_last_used(base::Time last_used) { last_used_ = last_used; } + void set_last_modified(base::Time last_modified) { + last_modified_ = last_modified; + } + + int32 data_size(int stream_index) const { return data_size_[stream_index]; } + void set_data_size(int stream_index, int data_size) { + data_size_[stream_index] = data_size; + } - base::Time last_used; - base::Time last_modified; - int32 data_size[kSimpleEntryFileCount]; + private: + base::Time last_used_; + base::Time last_modified_; + int32 data_size_[kSimpleEntryStreamCount]; }; struct SimpleEntryCreationResults { @@ -41,7 +67,9 @@ struct SimpleEntryCreationResults { ~SimpleEntryCreationResults(); SimpleSynchronousEntry* sync_entry; + scoped_refptr<net::GrowableIOBuffer> stream_0_data; SimpleEntryStat entry_stat; + uint32 stream_0_crc32; int result; }; @@ -102,21 +130,22 @@ class SimpleSynchronousEntry { void ReadData(const EntryOperationData& in_entry_op, net::IOBuffer* out_buf, uint32* out_crc32, - base::Time* out_last_used, + SimpleEntryStat* entry_stat, int* out_result) const; void WriteData(const EntryOperationData& in_entry_op, net::IOBuffer* in_buf, SimpleEntryStat* out_entry_stat, int* out_result) const; void CheckEOFRecord(int index, - int data_size, + const SimpleEntryStat& entry_stat, uint32 expected_crc32, int* out_result) const; // Close all streams, and add write EOF records to streams indicated by the // CRCRecord entries in |crc32s_to_write|. void Close(const SimpleEntryStat& entry_stat, - scoped_ptr<std::vector<CRCRecord> > crc32s_to_write); + scoped_ptr<std::vector<CRCRecord> > crc32s_to_write, + net::GrowableIOBuffer* stream_0_data); const base::FilePath& path() const { return path_; } std::string key() const { return key_; } @@ -140,7 +169,10 @@ class SimpleSynchronousEntry { // Returns a net error, i.e. net::OK on success. |had_index| is passed // from the main entry for metrics purposes, and is true if the index was // initialized when the open operation began. - int InitializeForOpen(bool had_index, SimpleEntryStat* out_entry_stat); + int InitializeForOpen(bool had_index, + SimpleEntryStat* out_entry_stat, + scoped_refptr<net::GrowableIOBuffer>* stream_0_data, + uint32* out_stream_0_crc32); // Returns a net error, including net::OK on success and net::FILE_EXISTS // when the entry already exists. |had_index| is passed from the main entry @@ -148,6 +180,19 @@ class SimpleSynchronousEntry { // create operation began. int InitializeForCreate(bool had_index, SimpleEntryStat* out_entry_stat); + // Allocates and fills a buffer with stream 0 data in |stream_0_data|, then + // checks its crc32. + int ReadAndValidateStream0( + int total_data_size, + SimpleEntryStat* out_entry_stat, + scoped_refptr<net::GrowableIOBuffer>* stream_0_data, + uint32* out_stream_0_crc32) const; + + int GetEOFRecordData(int index, + const SimpleEntryStat& entry_stat, + bool* out_has_crc32, + uint32* out_crc32, + int* out_data_size) const; void Doom() const; static bool DeleteFilesForEntryHash(const base::FilePath& path, diff --git a/net/disk_cache/simple/simple_test_util.cc b/net/disk_cache/simple/simple_test_util.cc index 9f09974..9a27558 100644 --- a/net/disk_cache/simple/simple_test_util.cc +++ b/net/disk_cache/simple/simple_test_util.cc @@ -13,7 +13,7 @@ namespace simple_util { bool CreateCorruptFileForTests(const std::string& key, const base::FilePath& cache_path) { base::FilePath entry_file_path = cache_path.AppendASCII( - disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0)); + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0)); int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE; base::PlatformFile entry_file = base::CreatePlatformFile(entry_file_path, flags, NULL, NULL); diff --git a/net/disk_cache/simple/simple_util.cc b/net/disk_cache/simple/simple_util.cc index 4a89f9b..4291b1f 100644 --- a/net/disk_cache/simple/simple_util.cc +++ b/net/disk_cache/simple/simple_util.cc @@ -77,13 +77,15 @@ uint64 GetEntryHashKey(const std::string& key) { return u.key_hash; } -std::string GetFilenameFromEntryHashAndIndex(uint64 entry_hash, - int index) { - return base::StringPrintf("%016" PRIx64 "_%1d", entry_hash, index); +std::string GetFilenameFromEntryHashAndFileIndex(uint64 entry_hash, + int file_index) { + return base::StringPrintf("%016" PRIx64 "_%1d", entry_hash, file_index); } -std::string GetFilenameFromKeyAndIndex(const std::string& key, int index) { - return GetEntryHashKeyAsHexString(key) + base::StringPrintf("_%1d", index); +std::string GetFilenameFromKeyAndFileIndex(const std::string& key, + int file_index) { + return GetEntryHashKeyAsHexString(key) + + base::StringPrintf("_%1d", file_index); } int32 GetDataSizeFromKeyAndFileSize(const std::string& key, int64 file_size) { @@ -98,10 +100,8 @@ int64 GetFileSizeFromKeyAndDataSize(const std::string& key, int32 data_size) { sizeof(SimpleFileEOF); } -int64 GetFileOffsetFromKeyAndDataOffset(const std::string& key, - int data_offset) { - const int64 headers_size = sizeof(disk_cache::SimpleFileHeader) + key.size(); - return headers_size + data_offset; +int GetFileIndexFromStreamIndex(int stream_index) { + return (stream_index == 2) ? 1 : 0; } // TODO(clamy, gavinp): this should go in base diff --git a/net/disk_cache/simple/simple_util.h b/net/disk_cache/simple/simple_util.h index ef51869..60a237e 100644 --- a/net/disk_cache/simple/simple_util.h +++ b/net/disk_cache/simple/simple_util.h @@ -40,12 +40,13 @@ NET_EXPORT_PRIVATE bool GetEntryHashKeyFromHexString( // Given a |key| for a (potential) entry in the simple backend and the |index| // of a stream on that entry, returns the filename in which that stream would be // stored. -NET_EXPORT_PRIVATE std::string GetFilenameFromKeyAndIndex( +NET_EXPORT_PRIVATE std::string GetFilenameFromKeyAndFileIndex( const std::string& key, - int index); + int file_index); // Same as |GetFilenameFromKeyAndIndex| above, but using a hex string. -std::string GetFilenameFromEntryHashAndIndex(uint64 entry_hash, int index); +std::string GetFilenameFromEntryHashAndFileIndex(uint64 entry_hash, + int file_index); // Given the size of a file holding a stream in the simple backend and the key // to an entry, returns the number of bytes in the stream. @@ -57,11 +58,9 @@ NET_EXPORT_PRIVATE int32 GetDataSizeFromKeyAndFileSize(const std::string& key, NET_EXPORT_PRIVATE int64 GetFileSizeFromKeyAndDataSize(const std::string& key, int32 data_size); -// Given the key to an entry, and an offset into a stream on that entry, returns -// the file offset corresponding to |data_offset|. -NET_EXPORT_PRIVATE int64 GetFileOffsetFromKeyAndDataOffset( - const std::string& key, - int data_offset); +// Given the stream index, returns the number of the file the stream is stored +// in. +NET_EXPORT_PRIVATE int GetFileIndexFromStreamIndex(int stream_index); // Fills |out_time| with the time the file last modified time. Unlike the // functions in platform_file.h, the time resolution is milliseconds. |