// Copyright (c) 2012 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 CHROME_BROWSER_CHROMEOS_DRIVE_FILE_CACHE_H_ #define CHROME_BROWSER_CHROMEOS_DRIVE_FILE_CACHE_H_ #include <string> #include <vector> #include "base/callback_forward.h" #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "chrome/browser/chromeos/drive/file_cache_metadata.h" #include "chrome/browser/chromeos/drive/file_errors.h" class Profile; namespace base { class SequencedTaskRunner; } // namespace base namespace drive { class FileCacheEntry; // Callback for GetCacheEntry. // |success| indicates if the operation was successful. // |cache_entry| is the obtained cache entry. On failure, |cache_state| is // set to TEST_CACHE_STATE_NONE. typedef base::Callback<void(bool success, const FileCacheEntry& cache_entry)> GetCacheEntryCallback; namespace internal { class FileCacheMetadata; class FileCacheObserver; // Callback for GetFileFromCache. typedef base::Callback<void(FileError error, const base::FilePath& cache_file_path)> GetFileFromCacheCallback; // Callback for RequestInitialize. // |success| indicates if the operation was successful. // TODO(satorux): Change this to FileError when it becomes necessary. typedef base::Callback<void(bool success)> InitializeCacheCallback; // Interface class used for getting the free disk space. Tests can inject an // implementation that reports fake free disk space. class FreeDiskSpaceGetterInterface { public: virtual ~FreeDiskSpaceGetterInterface() {} virtual int64 AmountOfFreeDiskSpace() = 0; }; // FileCache is used to maintain cache states of FileSystem. // // All non-static public member functions, unless mentioned otherwise (see // GetCacheFilePath() for example), should be called from the UI thread. class FileCache { public: // Enum defining GCache subdirectory location. // This indexes into |FileCache::cache_paths_| vector. enum CacheSubDirectoryType { CACHE_TYPE_META = 0, // Downloaded feeds. CACHE_TYPE_OUTGOING, // Symlinks to files in persistent or tmp dir to // be uploaded. CACHE_TYPE_PERSISTENT, // Files that are pinned or modified locally, // not evictable, hopefully. CACHE_TYPE_TMP, // Files that don't meet criteria to be in // persistent dir, and hence evictable. CACHE_TYPE_TMP_DOWNLOADS, // Downloaded files. CACHE_TYPE_TMP_DOCUMENTS, // Temporary JSON files for hosted documents. NUM_CACHE_TYPES, // This must be at the end. }; // Enum defining type of file operation e.g. copy or move, etc. enum FileOperationType { FILE_OPERATION_MOVE = 0, FILE_OPERATION_COPY, }; // |cache_root_path| specifies the root directory for the cache. Sub // directories will be created under the root directory. // // |blocking_task_runner| is used to post a task to the blocking worker // pool for file operations. Must not be null. // // |free_disk_space_getter| is used to inject a custom free disk space // getter for testing. NULL must be passed for production code. FileCache(const base::FilePath& cache_root_path, base::SequencedTaskRunner* blocking_task_runner, FreeDiskSpaceGetterInterface* free_disk_space_getter); // Returns the sub-directory under drive cache directory for the given sub // directory type. Example: <user_profile_dir>/GCache/v1/tmp // // Can be called on any thread. base::FilePath GetCacheDirectoryPath( CacheSubDirectoryType sub_dir_type) const; // Returns true if the given path is under drive cache directory, i.e. // <user_profile_dir>/GCache/v1 // // Can be called on any thread. bool IsUnderFileCacheDirectory(const base::FilePath& path) const; // Adds observer. void AddObserver(FileCacheObserver* observer); // Removes observer. void RemoveObserver(FileCacheObserver* observer); // Gets the cache entry for file corresponding to |resource_id| and |md5| // and runs |callback| with true and the entry found if entry exists in cache // map. Otherwise, runs |callback| with false. // |md5| can be empty if only matching |resource_id| is desired, which may // happen when looking for pinned entries where symlinks' filenames have no // extension and hence no md5. // |callback| must not be null. void GetCacheEntry(const std::string& resource_id, const std::string& md5, const GetCacheEntryCallback& callback); // Iterates all files in the cache and calls |iteration_callback| for each // file. |completion_callback| is run upon completion. void Iterate(const CacheIterateCallback& iteration_callback, const base::Closure& completion_callback); // Frees up disk space to store the given number of bytes, while keeping // kMinFreeSpace bytes on the disk, if needed. // Runs |callback| with true when we successfully manage to have enough space. void FreeDiskSpaceIfNeededFor(int64 num_bytes, const InitializeCacheCallback& callback); // Checks if file corresponding to |resource_id| and |md5| exists in cache. // |callback| must not be null. void GetFile(const std::string& resource_id, const std::string& md5, const GetFileFromCacheCallback& callback); // Stores |source_path| as a cache of the remote content of the file // identified by |resource_id| and |md5|. // |callback| must not be null. void Store(const std::string& resource_id, const std::string& md5, const base::FilePath& source_path, FileOperationType file_operation_type, const FileOperationCallback& callback); // Stores |source_path| to the cache and mark it as dirty, i.e., needs to be // uploaded to the remove server for syncing. // |callback| must not be null. void StoreLocallyModified(const std::string& resource_id, const std::string& md5, const base::FilePath& source_path, FileOperationType file_operation_type, const FileOperationCallback& callback); // Modifies cache state, which involves the following: // - moves |source_path| to |dest_path| in persistent dir if // file is not dirty // - creates symlink in pinned dir that references downloaded or locally // modified file // |callback| must not be null. void Pin(const std::string& resource_id, const std::string& md5, const FileOperationCallback& callback); // Modifies cache state, which involves the following: // - moves |source_path| to |dest_path| in tmp dir if file is not dirty // - deletes symlink from pinned dir // |callback| must not be null. void Unpin(const std::string& resource_id, const std::string& md5, const FileOperationCallback& callback); // Sets the state of the cache entry corresponding to |resource_id| and |md5| // as mounted. // |callback| must not be null. void MarkAsMounted(const std::string& resource_id, const std::string& md5, const GetFileFromCacheCallback& callback); // Set the state of the cache entry corresponding to file_path as unmounted. // |callback| must not be null. void MarkAsUnmounted(const base::FilePath& file_path, const FileOperationCallback& callback); // Modifies cache state, which involves the following: // - moves |source_path| to |dest_path| in persistent dir, where // |source_path| has .<md5> extension and |dest_path| has .local extension // - if file is pinned, updates symlink in pinned dir to reference dirty file // |callback| must not be null. void MarkDirty(const std::string& resource_id, const std::string& md5, const FileOperationCallback& callback); // Modifies cache state, i.e. creates symlink in outgoing // dir to reference dirty file in persistent dir. // |callback| must not be null. void CommitDirty(const std::string& resource_id, const std::string& md5, const FileOperationCallback& callback); // Modifies cache state, which involves the following: // - moves |source_path| to |dest_path| in persistent dir if // file is pinned or tmp dir otherwise, where |source_path| has .local // extension and |dest_path| has .<md5> extension // - deletes symlink in outgoing dir // - if file is pinned, updates symlink in pinned dir to reference // |dest_path| // |callback| must not be null. void ClearDirty(const std::string& resource_id, const std::string& md5, const FileOperationCallback& callback); // Does the following: // - remove all delete stale cache versions corresponding to |resource_id| in // persistent, tmp and pinned directories // - remove entry corresponding to |resource_id| from cache map. // |callback| must not be null. void Remove(const std::string& resource_id, const FileOperationCallback& callback); // Does the following: // - remove all the files in the cache directory. // - re-create the |metadata_| instance. // |callback| must not be null. void ClearAll(const InitializeCacheCallback& callback); // Utility method to call Initialize on UI thread. |callback| is called on // UI thread when the initialization is complete. // |callback| must not be null. void RequestInitialize(const InitializeCacheCallback& callback); // Utility method to call InitializeForTesting on UI thread. void RequestInitializeForTesting(); // Destroys this cache. This function posts a task to the blocking task // runner to safely delete the object. void Destroy(); // Returns file paths for all the cache sub directories under // |cache_root_path|. static std::vector<base::FilePath> GetCachePaths( const base::FilePath& cache_root_path); // Creates cache directory and its sub-directories if they don't exist. // TODO(glotov): take care of this when the setup and cleanup part is // landed, noting that these directories need to be created for development // in linux box and unittest. (http://crosbug.com/27577) static bool CreateCacheDirectories( const std::vector<base::FilePath>& paths_to_create); // Returns the type of the sub directory where the cache file is stored. static CacheSubDirectoryType GetSubDirectoryType( const FileCacheEntry& cache_entry); private: friend class FileCacheTest; typedef std::pair<FileError, base::FilePath> GetFileResult; // Enum defining origin of a cached file. enum CachedFileOrigin { CACHED_FILE_FROM_SERVER = 0, CACHED_FILE_LOCALLY_MODIFIED, CACHED_FILE_MOUNTED, }; virtual ~FileCache(); // Returns absolute path of the file if it were cached or to be cached. // // Can be called on any thread. base::FilePath GetCacheFilePath(const std::string& resource_id, const std::string& md5, CacheSubDirectoryType sub_dir_type, CachedFileOrigin file_origin) const; // Checks whether the current thread is on the right sequenced worker pool // with the right sequence ID. If not, DCHECK will fail. void AssertOnSequencedWorkerPool(); // Initializes the cache. Returns true on success. bool InitializeOnBlockingPool(); // Initializes the cache with in-memory cache for testing. // The in-memory cache is used since it's faster than the db. void InitializeOnBlockingPoolForTesting(); // Destroys the cache on the blocking pool. void DestroyOnBlockingPool(); // Gets the cache entry by the given resource ID and MD5. // See also GetCacheEntry(). bool GetCacheEntryOnBlockingPool(const std::string& resource_id, const std::string& md5, FileCacheEntry* entry); // Used to implement Iterate(). void IterateOnBlockingPool(const CacheIterateCallback& iteration_callback); // Used to implement FreeDiskSpaceIfNeededFor(). bool FreeDiskSpaceOnBlockingPoolIfNeededFor(int64 num_bytes); // Used to implement GetFile. scoped_ptr<GetFileResult> GetFileOnBlockingPool( const std::string& resource_id, const std::string& md5); // Used to implement Store. FileError StoreOnBlockingPool(const std::string& resource_id, const std::string& md5, const base::FilePath& source_path, FileOperationType file_operation_type, CachedFileOrigin origin); // Used to implement Pin. FileError PinOnBlockingPool(const std::string& resource_id, const std::string& md5); // Used to implement Unpin. FileError UnpinOnBlockingPool(const std::string& resource_id, const std::string& md5); // Used to implement MarkAsMounted. scoped_ptr<GetFileResult> MarkAsMountedOnBlockingPool( const std::string& resource_id, const std::string& md5); // Used to implement MarkAsUnmounted. FileError MarkAsUnmountedOnBlockingPool(const base::FilePath& file_path); // Used to implement MarkDirty. FileError MarkDirtyOnBlockingPool(const std::string& resource_id, const std::string& md5); // Used to implement CommitDirty. FileError CommitDirtyOnBlockingPool(const std::string& resource_id, const std::string& md5); // Used to implement ClearDirty. FileError ClearDirtyOnBlockingPool(const std::string& resource_id, const std::string& md5); // Used to implement Remove. FileError RemoveOnBlockingPool(const std::string& resource_id); // Used to implement ClearAll. bool ClearAllOnBlockingPool(); // Runs callback and notifies the observers when file is pinned. void OnPinned(const std::string& resource_id, const std::string& md5, const FileOperationCallback& callback, FileError error); // Runs callback and notifies the observers when file is unpinned. void OnUnpinned(const std::string& resource_id, const std::string& md5, const FileOperationCallback& callback, FileError error); // Runs callback and notifies the observers when file is committed. void OnCommitDirty(const std::string& resource_id, const FileOperationCallback& callback, FileError error); // Returns true if we have sufficient space to store the given number of // bytes, while keeping kMinFreeSpace bytes on the disk. bool HasEnoughSpaceFor(int64 num_bytes, const base::FilePath& path); // The root directory of the cache (i.e. <user_profile_dir>/GCache/v1). const base::FilePath cache_root_path_; // Paths for all subdirectories of GCache, one for each // FileCache::CacheSubDirectoryType enum. const std::vector<base::FilePath> cache_paths_; scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; // The cache state data. This member must be access only on the blocking pool. scoped_ptr<FileCacheMetadata> metadata_; // List of observers, this member must be accessed on UI thread. ObserverList<FileCacheObserver> observers_; FreeDiskSpaceGetterInterface* free_disk_space_getter_; // Not owned. // Note: This should remain the last member so it'll be destroyed and // invalidate its weak pointers before any other members are destroyed. base::WeakPtrFactory<FileCache> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(FileCache); }; // The minimum free space to keep. FileSystem::GetFileByPath() returns // GDATA_FILE_ERROR_NO_SPACE if the available space is smaller than // this value. // // Copied from cryptohome/homedirs.h. // TODO(satorux): Share the constant. const int64 kMinFreeSpace = 512 * 1LL << 20; } // namespace internal } // namespace drive #endif // CHROME_BROWSER_CHROMEOS_DRIVE_FILE_CACHE_H_