| // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_ | 
 | #define NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_ | 
 |  | 
 | #include <algorithm> | 
 | #include <map> | 
 | #include <memory> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "base/feature_list.h" | 
 | #include "base/files/file.h" | 
 | #include "base/files/file_path.h" | 
 | #include "base/gtest_prod_util.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "base/strings/string_piece_forward.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" | 
 | #include "net/disk_cache/simple/simple_file_tracker.h" | 
 | #include "starboard/types.h" | 
 |  | 
 | namespace net { | 
 | class GrowableIOBuffer; | 
 | class IOBuffer; | 
 | } | 
 |  | 
 | FORWARD_DECLARE_TEST(DiskCacheBackendTest, SimpleCacheEnumerationLongKeys); | 
 |  | 
 | namespace disk_cache { | 
 |  | 
 | NET_EXPORT_PRIVATE extern const base::Feature kSimpleCachePrefetchExperiment; | 
 | NET_EXPORT_PRIVATE extern const char kSimplePrefetchBytesParam[]; | 
 |  | 
 | // Returns how large a file would get prefetched on reading the entry. | 
 | // If the experiment is disabled, returns 0. | 
 | NET_EXPORT_PRIVATE int GetSimpleCachePrefetchSize(); | 
 |  | 
 | class SimpleSynchronousEntry; | 
 |  | 
 | // 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_t data_size[], | 
 |                   const int32_t sparse_data_size); | 
 |  | 
 |   int GetOffsetInFile(size_t key_length, int offset, int stream_index) const; | 
 |   int GetEOFOffsetInFile(size_t key_length, int stream_index) const; | 
 |   int GetLastEOFOffsetInFile(size_t key_length, int file_index) const; | 
 |   int64_t GetFileSize(size_t key_length, 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_t 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; | 
 |   } | 
 |  | 
 |   int32_t sparse_data_size() const { return sparse_data_size_; } | 
 |   void set_sparse_data_size(int32_t sparse_data_size) { | 
 |     sparse_data_size_ = sparse_data_size; | 
 |   } | 
 |  | 
 |  private: | 
 |   base::Time last_used_; | 
 |   base::Time last_modified_; | 
 |   int32_t data_size_[kSimpleEntryStreamCount]; | 
 |   int32_t sparse_data_size_; | 
 | }; | 
 |  | 
 | struct SimpleStreamPrefetchData { | 
 |   SimpleStreamPrefetchData(); | 
 |   ~SimpleStreamPrefetchData(); | 
 |  | 
 |   scoped_refptr<net::GrowableIOBuffer> data; | 
 |   uint32_t stream_crc32; | 
 | }; | 
 |  | 
 | struct SimpleEntryCreationResults { | 
 |   explicit SimpleEntryCreationResults(SimpleEntryStat entry_stat); | 
 |   ~SimpleEntryCreationResults(); | 
 |  | 
 |   SimpleSynchronousEntry* sync_entry; | 
 |  | 
 |   // Expectation is that [0] will always be filled in, but [1] might not be. | 
 |   SimpleStreamPrefetchData stream_prefetch_data[2]; | 
 |  | 
 |   SimpleEntryStat entry_stat; | 
 |   int result; | 
 | }; | 
 |  | 
 | // Worker thread interface to the very simple cache. This interface is not | 
 | // thread safe, and callers must ensure that it is only ever accessed from | 
 | // a single thread between synchronization points. | 
 | class SimpleSynchronousEntry { | 
 |  public: | 
 |   struct CRCRecord { | 
 |     CRCRecord(); | 
 |     CRCRecord(int index_p, bool has_crc32_p, uint32_t data_crc32_p); | 
 |  | 
 |     int index; | 
 |     bool has_crc32; | 
 |     uint32_t data_crc32; | 
 |   }; | 
 |  | 
 |   struct ReadRequest { | 
 |     // Also sets request_update_crc to false. | 
 |     ReadRequest(int index_p, int offset_p, int buf_len_p); | 
 |     int index; | 
 |     int offset; | 
 |     int buf_len; | 
 |  | 
 |     // Partial CRC of data immediately preceeding this read. Only relevant if | 
 |     // request_update_crc is set. | 
 |     uint32_t previous_crc32; | 
 |     bool request_update_crc; | 
 |     bool request_verify_crc;  // only relevant if request_update_crc is set | 
 |   }; | 
 |  | 
 |   struct ReadResult { | 
 |     ReadResult() | 
 |         : crc_updated(false), | 
 |           crc_performed_verify(false), | 
 |           crc_verify_ok(false) {} | 
 |     int result; | 
 |     uint32_t updated_crc32;  // only relevant if crc_updated set | 
 |     bool crc_updated; | 
 |     bool crc_performed_verify;  // only relevant if crc_updated set | 
 |     bool crc_verify_ok;         // only relevant if crc_updated set | 
 |   }; | 
 |  | 
 |   struct WriteRequest { | 
 |     WriteRequest(int index_p, | 
 |                  int offset_p, | 
 |                  int buf_len_p, | 
 |                  uint32_t previous_crc32_p, | 
 |                  bool truncate_p, | 
 |                  bool doomed_p, | 
 |                  bool request_update_crc_p); | 
 |     int index; | 
 |     int offset; | 
 |     int buf_len; | 
 |     uint32_t previous_crc32; | 
 |     bool truncate; | 
 |     bool doomed; | 
 |     bool request_update_crc; | 
 |   }; | 
 |  | 
 |   struct WriteResult { | 
 |     WriteResult() : crc_updated(false) {} | 
 |     int result; | 
 |     uint32_t updated_crc32;  // only relevant if crc_updated set | 
 |     bool crc_updated; | 
 |   }; | 
 |  | 
 |   struct SparseRequest { | 
 |     SparseRequest(int64_t sparse_offset_p, int buf_len_p); | 
 |  | 
 |     int64_t sparse_offset; | 
 |     int buf_len; | 
 |   }; | 
 |  | 
 |   // Opens a disk cache entry on disk. The |key| parameter is optional, if empty | 
 |   // the operation may be slower. The |entry_hash| parameter is required. | 
 |   // |had_index| is provided only for histograms. | 
 |   // |time_enqueued| is when this operation was added to the I/O thread pool, | 
 |   //  and is provided only for histograms. | 
 |   static void OpenEntry(net::CacheType cache_type, | 
 |                         const base::FilePath& path, | 
 |                         const std::string& key, | 
 |                         uint64_t entry_hash, | 
 |                         bool had_index, | 
 |                         const base::TimeTicks& time_enqueued, | 
 |                         SimpleFileTracker* file_tracker, | 
 |                         SimpleEntryCreationResults* out_results); | 
 |  | 
 |   static void CreateEntry(net::CacheType cache_type, | 
 |                           const base::FilePath& path, | 
 |                           const std::string& key, | 
 |                           uint64_t entry_hash, | 
 |                           bool had_index, | 
 |                           const base::TimeTicks& time_enqueued, | 
 |                           SimpleFileTracker* file_tracker, | 
 |                           SimpleEntryCreationResults* out_results); | 
 |  | 
 |   // Renames the entry on the file system, making it no longer possible to open | 
 |   // it again, but allowing operations to continue to be executed through that | 
 |   // instance. The renamed file will be removed once the entry is closed. | 
 |   // Returns a net error code. | 
 |   int Doom(); | 
 |  | 
 |   // Deletes an entry from the file system.  This variant should only be used | 
 |   // if there is no actual open instance around, as it doesn't account for | 
 |   // possibility of it having been renamed to a non-standard name. | 
 |   static int DeleteEntryFiles(const base::FilePath& path, | 
 |                               net::CacheType cache_type, | 
 |                               uint64_t entry_hash); | 
 |  | 
 |   // Like |DeleteEntryFiles()| above, except that it truncates the entry files | 
 |   // rather than deleting them. Used when dooming entries after the backend has | 
 |   // shutdown. See implementation of |SimpleEntryImpl::DoomEntryInternal()| for | 
 |   // more. | 
 |   static int TruncateEntryFiles(const base::FilePath& path, | 
 |                                 uint64_t entry_hash); | 
 |  | 
 |   // Like |DeleteEntryFiles()| above. Deletes all entries corresponding to the | 
 |   // |key_hashes|. Succeeds only when all entries are deleted. Returns a net | 
 |   // error code. | 
 |   static int DeleteEntrySetFiles(const std::vector<uint64_t>* key_hashes, | 
 |                                  const base::FilePath& path); | 
 |  | 
 |   // N.B. ReadData(), WriteData(), CheckEOFRecord(), ReadSparseData(), | 
 |   // WriteSparseData() and Close() may block on IO. | 
 |   // | 
 |   // All of these methods will put the //net return value into |*out_result|. | 
 |  | 
 |   void ReadData(const ReadRequest& in_entry_op, | 
 |                 SimpleEntryStat* entry_stat, | 
 |                 net::IOBuffer* out_buf, | 
 |                 ReadResult* out_result); | 
 |   void WriteData(const WriteRequest& in_entry_op, | 
 |                  net::IOBuffer* in_buf, | 
 |                  SimpleEntryStat* out_entry_stat, | 
 |                  WriteResult* out_write_result); | 
 |   int CheckEOFRecord(base::File* file, | 
 |                      int stream_index, | 
 |                      const SimpleEntryStat& entry_stat, | 
 |                      uint32_t expected_crc32); | 
 |  | 
 |   void ReadSparseData(const SparseRequest& in_entry_op, | 
 |                       net::IOBuffer* out_buf, | 
 |                       base::Time* out_last_used, | 
 |                       int* out_result); | 
 |   void WriteSparseData(const SparseRequest& in_entry_op, | 
 |                        net::IOBuffer* in_buf, | 
 |                        uint64_t max_sparse_data_size, | 
 |                        SimpleEntryStat* out_entry_stat, | 
 |                        int* out_result); | 
 |   void GetAvailableRange(const SparseRequest& in_entry_op, | 
 |                          int64_t* out_start, | 
 |                          int* out_result); | 
 |  | 
 |   // 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, | 
 |              std::unique_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_; } | 
 |   const SimpleFileTracker::EntryFileKey& entry_file_key() const { | 
 |     return entry_file_key_; | 
 |   } | 
 |  | 
 |   NET_EXPORT_PRIVATE base::FilePath GetFilenameForSubfile( | 
 |       SimpleFileTracker::SubFile sub_file) const; | 
 |  | 
 |  private: | 
 |   FRIEND_TEST_ALL_PREFIXES(::DiskCacheBackendTest, | 
 |                            SimpleCacheEnumerationLongKeys); | 
 |   friend class SimpleFileTrackerTest; | 
 |  | 
 |   enum CreateEntryResult { | 
 |     CREATE_ENTRY_SUCCESS = 0, | 
 |     CREATE_ENTRY_PLATFORM_FILE_ERROR = 1, | 
 |     CREATE_ENTRY_CANT_WRITE_HEADER = 2, | 
 |     CREATE_ENTRY_CANT_WRITE_KEY = 3, | 
 |     CREATE_ENTRY_MAX = 4, | 
 |   }; | 
 |  | 
 |   enum FileRequired { | 
 |     FILE_NOT_REQUIRED, | 
 |     FILE_REQUIRED | 
 |   }; | 
 |  | 
 |   struct SparseRange { | 
 |     int64_t offset; | 
 |     int64_t length; | 
 |     uint32_t data_crc32; | 
 |     int64_t file_offset; | 
 |  | 
 |     bool operator<(const SparseRange& other) const { | 
 |       return offset < other.offset; | 
 |     } | 
 |   }; | 
 |  | 
 |   // When opening an entry without knowing the key, the header must be read | 
 |   // without knowing the size of the key. This is how much to read initially, to | 
 |   // make it likely the entire key is read. | 
 |   static const size_t kInitialHeaderRead = 64 * 1024; | 
 |  | 
 |   NET_EXPORT_PRIVATE SimpleSynchronousEntry( | 
 |       net::CacheType cache_type, | 
 |       const base::FilePath& path, | 
 |       const std::string& key, | 
 |       uint64_t entry_hash, | 
 |       bool had_index, | 
 |       SimpleFileTracker* simple_file_tracker); | 
 |  | 
 |   // Like Entry, the SimpleSynchronousEntry self releases when Close() is | 
 |   // called. | 
 |   NET_EXPORT_PRIVATE ~SimpleSynchronousEntry(); | 
 |  | 
 |   // Tries to open one of the cache entry files. Succeeds if the open succeeds | 
 |   // or if the file was not found and is allowed to be omitted if the | 
 |   // corresponding stream is empty. | 
 |   bool MaybeOpenFile(int file_index, | 
 |                      base::File::Error* out_error); | 
 |   // Creates one of the cache entry files if necessary. If the file is allowed | 
 |   // to be omitted if the corresponding stream is empty, and if |file_required| | 
 |   // is FILE_NOT_REQUIRED, then the file is not created; otherwise, it is. | 
 |   bool MaybeCreateFile(int file_index, | 
 |                        FileRequired file_required, | 
 |                        base::File::Error* out_error); | 
 |   bool OpenFiles(SimpleEntryStat* out_entry_stat); | 
 |   bool CreateFiles(SimpleEntryStat* out_entry_stat); | 
 |   void CloseFile(int index); | 
 |   void CloseFiles(); | 
 |  | 
 |   // Read the header and key at the beginning of the file, and validate that | 
 |   // they are correct. If this entry was opened with a key, the key is checked | 
 |   // for a match. If not, then the |key_| member is set based on the value in | 
 |   // this header. Records histograms if any check is failed. | 
 |   bool CheckHeaderAndKey(base::File* file, int file_index); | 
 |  | 
 |   // Returns a net error, i.e. net::OK on success. | 
 |   int InitializeForOpen(SimpleEntryStat* out_entry_stat, | 
 |                         SimpleStreamPrefetchData stream_prefetch_data[2]); | 
 |  | 
 |   // Writes the header and key to a newly-created stream file. |index| is the | 
 |   // index of the stream. Returns true on success; returns false and sets | 
 |   // |*out_result| on failure. | 
 |   bool InitializeCreatedFile(int index, CreateEntryResult* out_result); | 
 |  | 
 |   // Returns a net error, including net::OK on success and net::FILE_EXISTS | 
 |   // when the entry already exists. | 
 |   int InitializeForCreate(SimpleEntryStat* out_entry_stat); | 
 |  | 
 |   // Allocates and fills a buffer with stream 0 data in |stream_0_data|, then | 
 |   // checks its crc32. May also optionally read in |stream_1_data| and its | 
 |   // crc, but might decide not to. | 
 |   int ReadAndValidateStream0AndMaybe1( | 
 |       int file_size, | 
 |       SimpleEntryStat* out_entry_stat, | 
 |       SimpleStreamPrefetchData stream_prefetch_data[2]); | 
 |  | 
 |   // Reads the EOF record located at |file_offset| in file |file_index|, | 
 |   // with |file_0_prefetch| potentially having prefetched file 0 content. | 
 |   // Puts the result into |*eof_record| and sanity-checks it. | 
 |   // Returns net status, and records any failures to UMA. | 
 |   int GetEOFRecordData(base::File* file, | 
 |                        base::StringPiece file_0_prefetch, | 
 |                        int file_index, | 
 |                        int file_offset, | 
 |                        SimpleFileEOF* eof_record); | 
 |  | 
 |   // Reads either from |file_0_prefetch| or |file|. | 
 |   // Range-checks all the in-memory reads. | 
 |   bool ReadFromFileOrPrefetched(base::File* file, | 
 |                                 base::StringPiece file_0_prefetch, | 
 |                                 int file_index, | 
 |                                 int offset, | 
 |                                 int size, | 
 |                                 char* dest); | 
 |  | 
 |   // Extracts out the payload of stream |stream_index|, reading either from | 
 |   // |file_0_prefetch|, if available, or |file|. |entry_stat| will be used to | 
 |   // determine file layout, though |extra_size| additional bytes will be read | 
 |   // past the stream payload end. | 
 |   // | 
 |   // |*stream_data| will be pointed to a fresh buffer with the results, | 
 |   // and |*out_crc32| will get the checksum, which will be verified against | 
 |   // |eof_record|. | 
 |   int PreReadStreamPayload(base::File* file, | 
 |                            base::StringPiece file_0_prefetch, | 
 |                            int stream_index, | 
 |                            int extra_size, | 
 |                            const SimpleEntryStat& entry_stat, | 
 |                            const SimpleFileEOF& eof_record, | 
 |                            SimpleStreamPrefetchData* out); | 
 |  | 
 |   // Opens the sparse data file and scans it if it exists. | 
 |   bool OpenSparseFileIfExists(int32_t* out_sparse_data_size); | 
 |  | 
 |   // Creates and initializes the sparse data file. | 
 |   bool CreateSparseFile(); | 
 |  | 
 |   // Closes the sparse data file. | 
 |   void CloseSparseFile(); | 
 |  | 
 |   // Writes the header to the (newly-created) sparse file. | 
 |   bool InitializeSparseFile(base::File* file); | 
 |  | 
 |   // Removes all but the header of the sparse file. | 
 |   bool TruncateSparseFile(base::File* sparse_file); | 
 |  | 
 |   // Scans the existing ranges in the sparse file. Populates |sparse_ranges_| | 
 |   // and sets |*out_sparse_data_size| to the total size of all the ranges (not | 
 |   // including headers). | 
 |   bool ScanSparseFile(base::File* sparse_file, int32_t* out_sparse_data_size); | 
 |  | 
 |   // Reads from a single sparse range. If asked to read the entire range, also | 
 |   // verifies the CRC32. | 
 |   bool ReadSparseRange(base::File* sparse_file, | 
 |                        const SparseRange* range, | 
 |                        int offset, | 
 |                        int len, | 
 |                        char* buf); | 
 |  | 
 |   // Writes to a single (existing) sparse range. If asked to write the entire | 
 |   // range, also updates the CRC32; otherwise, invalidates it. | 
 |   bool WriteSparseRange(base::File* sparse_file, | 
 |                         SparseRange* range, | 
 |                         int offset, | 
 |                         int len, | 
 |                         const char* buf); | 
 |  | 
 |   // Appends a new sparse range to the sparse data file. | 
 |   bool AppendSparseRange(base::File* sparse_file, | 
 |                          int64_t offset, | 
 |                          int len, | 
 |                          const char* buf); | 
 |  | 
 |   static bool DeleteFileForEntryHash(const base::FilePath& path, | 
 |                                      uint64_t entry_hash, | 
 |                                      int file_index); | 
 |   static bool DeleteFilesForEntryHash(const base::FilePath& path, | 
 |                                       uint64_t entry_hash); | 
 |   static bool TruncateFilesForEntryHash(const base::FilePath& path, | 
 |                                         uint64_t entry_hash); | 
 |  | 
 |   void RecordSyncCreateResult(CreateEntryResult result, bool had_index); | 
 |  | 
 |   base::FilePath GetFilenameFromFileIndex(int file_index) const; | 
 |  | 
 |   bool sparse_file_open() const { return sparse_file_open_; } | 
 |  | 
 |   const net::CacheType cache_type_; | 
 |   const base::FilePath path_; | 
 |   SimpleFileTracker::EntryFileKey entry_file_key_; | 
 |   const bool had_index_; | 
 |   std::string key_; | 
 |  | 
 |   bool have_open_files_; | 
 |   bool initialized_; | 
 |  | 
 |   // Normally false. This is set to true when an entry is opened without | 
 |   // checking the file headers. Any subsequent read will perform the check | 
 |   // before completing. | 
 |   bool header_and_key_check_needed_[kSimpleEntryNormalFileCount] = { | 
 |       false, | 
 |   }; | 
 |  | 
 |   SimpleFileTracker* file_tracker_; | 
 |  | 
 |   // True if the corresponding stream is empty and therefore no on-disk file | 
 |   // was created to store it. | 
 |   bool empty_file_omitted_[kSimpleEntryNormalFileCount]; | 
 |  | 
 |   typedef std::map<int64_t, SparseRange> SparseRangeOffsetMap; | 
 |   typedef SparseRangeOffsetMap::iterator SparseRangeIterator; | 
 |   SparseRangeOffsetMap sparse_ranges_; | 
 |   bool sparse_file_open_; | 
 |  | 
 |   // Offset of the end of the sparse file (where the next sparse range will be | 
 |   // written). | 
 |   int64_t sparse_tail_offset_; | 
 | }; | 
 |  | 
 | }  // namespace disk_cache | 
 |  | 
 | #endif  // NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_ |