local_file_change_tracker.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
1c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// Copyright 2013 The Chromium Authors. All rights reserved. 2c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// Use of this source code is governed by a BSD-style license that can be 3c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// found in the LICENSE file. 4c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 5c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/sync_file_system/local/local_file_change_tracker.h" 6c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 7c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include <queue> 8c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 9c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "base/location.h" 10c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "base/logging.h" 11c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "base/sequenced_task_runner.h" 12c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "base/stl_util.h" 13c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/sync_file_system/local/local_file_sync_status.h" 14c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/sync_file_system/syncable_file_system_util.h" 15c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "third_party/leveldatabase/src/include/leveldb/db.h" 16c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "webkit/browser/fileapi/file_system_context.h" 17c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "webkit/browser/fileapi/file_system_file_util.h" 18c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "webkit/browser/fileapi/file_system_operation_context.h" 19c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "webkit/common/fileapi/file_system_util.h" 20c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 21c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathusing fileapi::FileSystemContext; 22c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathusing fileapi::FileSystemFileUtil; 23c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathusing fileapi::FileSystemOperationContext; 24c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathusing fileapi::FileSystemURL; 25c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathusing fileapi::FileSystemURLSet; 26c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 27c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathnamespace sync_file_system { 28c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 29c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathnamespace { 30c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathconst base::FilePath::CharType kDatabaseName[] = 31c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath FILE_PATH_LITERAL("LocalFileChangeTracker"); 32c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathconst char kMark[] = "d"; 33c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} // namespace 34c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 35c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// A database class that stores local file changes in a local database. This 36c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// object must be destructed on file_task_runner. 37c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathclass LocalFileChangeTracker::TrackerDB { 38c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath public: 39c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath explicit TrackerDB(const base::FilePath& base_path); 40c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 41c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SyncStatusCode MarkDirty(const std::string& url); 42c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SyncStatusCode ClearDirty(const std::string& url); 43c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SyncStatusCode GetDirtyEntries( 44c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath std::queue<FileSystemURL>* dirty_files); 45c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 46c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath private: 47c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath enum RecoveryOption { 48c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath REPAIR_ON_CORRUPTION, 49c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath FAIL_ON_CORRUPTION, 50c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath }; 51c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 52c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SyncStatusCode Init(RecoveryOption recovery_option); 53c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SyncStatusCode Repair(const std::string& db_path); 54c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath void HandleError(const tracked_objects::Location& from_here, 55c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath const leveldb::Status& status); 56c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 57c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath const base::FilePath base_path_; 58c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath scoped_ptr<leveldb::DB> db_; 59c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SyncStatusCode db_status_; 60c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 61c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath DISALLOW_COPY_AND_ASSIGN(TrackerDB); 62c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath}; 63c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 64c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan KamathLocalFileChangeTracker::ChangeInfo::ChangeInfo() : change_seq(-1) {} 65c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan KamathLocalFileChangeTracker::ChangeInfo::~ChangeInfo() {} 66c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 67c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// LocalFileChangeTracker ------------------------------------------------------ 68c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 69c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan KamathLocalFileChangeTracker::LocalFileChangeTracker( 70c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath const base::FilePath& base_path, 71c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath base::SequencedTaskRunner* file_task_runner) 72c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath : initialized_(false), 73c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath file_task_runner_(file_task_runner), 74c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath tracker_db_(new TrackerDB(base_path)), 75c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath current_change_seq_(0), 76c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath num_changes_(0) { 77c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} 78c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 79c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan KamathLocalFileChangeTracker::~LocalFileChangeTracker() { 80c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); 81c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath tracker_db_.reset(); 82c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} 83c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 84c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::OnStartUpdate(const FileSystemURL& url) { 85c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); 86c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath if (ContainsKey(changes_, url)) 87c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath return; 88c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath // TODO(nhiroki): propagate the error code (see http://crbug.com/152127). 89c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath MarkDirtyOnDatabase(url); 90c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} 91c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 92c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::OnEndUpdate(const FileSystemURL& url) {} 93c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 94c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::OnCreateFile(const FileSystemURL& url) { 95c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 96c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SYNC_FILE_TYPE_FILE)); 97c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} 98c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 99c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::OnCreateFileFrom(const FileSystemURL& url, 100c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath const FileSystemURL& src) { 101c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 102c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SYNC_FILE_TYPE_FILE)); 103c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} 104c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 105c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::OnRemoveFile(const FileSystemURL& url) { 106c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath RecordChange(url, FileChange(FileChange::FILE_CHANGE_DELETE, 107c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SYNC_FILE_TYPE_FILE)); 108c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} 109c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 110c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::OnModifyFile(const FileSystemURL& url) { 111c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 112c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SYNC_FILE_TYPE_FILE)); 1137faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez} 114c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 115c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::OnCreateDirectory(const FileSystemURL& url) { 116c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 117c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SYNC_FILE_TYPE_DIRECTORY)); 118c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} 119c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 120c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::OnRemoveDirectory(const FileSystemURL& url) { 121c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath RecordChange(url, FileChange(FileChange::FILE_CHANGE_DELETE, 122c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath SYNC_FILE_TYPE_DIRECTORY)); 123c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath} 124c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath 125c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::GetNextChangedURLs( 126c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath std::deque<FileSystemURL>* urls, int max_urls) { 127c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath DCHECK(urls); 128c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); 129c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath urls->clear(); 130c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath // Mildly prioritizes the URLs that older changes and have not been updated 131c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath // for a while. 132c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath for (ChangeSeqMap::iterator iter = change_seqs_.begin(); 133c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath iter != change_seqs_.end() && 134c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath (max_urls == 0 || urls->size() < static_cast<size_t>(max_urls)); 135c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath ++iter) { 136c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath urls->push_back(iter->second); 137c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath } 1387faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez} 1397faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez 140c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathvoid LocalFileChangeTracker::GetChangesForURL( 1417faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez const FileSystemURL& url, FileChangeList* changes) { 142c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); 143c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath DCHECK(changes); 144 changes->clear(); 145 FileChangeMap::iterator found = changes_.find(url); 146 if (found == changes_.end()) 147 return; 148 *changes = found->second.change_list; 149} 150 151void LocalFileChangeTracker::ClearChangesForURL(const FileSystemURL& url) { 152 DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); 153 // TODO(nhiroki): propagate the error code (see http://crbug.com/152127). 154 ClearDirtyOnDatabase(url); 155 156 FileChangeMap::iterator found = changes_.find(url); 157 if (found == changes_.end()) 158 return; 159 change_seqs_.erase(found->second.change_seq); 160 changes_.erase(found); 161 UpdateNumChanges(); 162} 163 164SyncStatusCode LocalFileChangeTracker::Initialize( 165 FileSystemContext* file_system_context) { 166 DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); 167 DCHECK(!initialized_); 168 DCHECK(file_system_context); 169 170 SyncStatusCode status = CollectLastDirtyChanges(file_system_context); 171 if (status == SYNC_STATUS_OK) 172 initialized_ = true; 173 return status; 174} 175 176void LocalFileChangeTracker::UpdateNumChanges() { 177 base::AutoLock lock(num_changes_lock_); 178 num_changes_ = static_cast<int64>(change_seqs_.size()); 179} 180 181void LocalFileChangeTracker::GetAllChangedURLs(FileSystemURLSet* urls) { 182 std::deque<FileSystemURL> url_deque; 183 GetNextChangedURLs(&url_deque, 0); 184 urls->clear(); 185 urls->insert(url_deque.begin(), url_deque.end()); 186} 187 188void LocalFileChangeTracker::DropAllChanges() { 189 changes_.clear(); 190 change_seqs_.clear(); 191} 192 193SyncStatusCode LocalFileChangeTracker::MarkDirtyOnDatabase( 194 const FileSystemURL& url) { 195 std::string serialized_url; 196 if (!SerializeSyncableFileSystemURL(url, &serialized_url)) 197 return SYNC_FILE_ERROR_INVALID_URL; 198 199 return tracker_db_->MarkDirty(serialized_url); 200} 201 202SyncStatusCode LocalFileChangeTracker::ClearDirtyOnDatabase( 203 const FileSystemURL& url) { 204 std::string serialized_url; 205 if (!SerializeSyncableFileSystemURL(url, &serialized_url)) 206 return SYNC_FILE_ERROR_INVALID_URL; 207 208 return tracker_db_->ClearDirty(serialized_url); 209} 210 211SyncStatusCode LocalFileChangeTracker::CollectLastDirtyChanges( 212 FileSystemContext* file_system_context) { 213 DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); 214 215 std::queue<FileSystemURL> dirty_files; 216 const SyncStatusCode status = tracker_db_->GetDirtyEntries(&dirty_files); 217 if (status != SYNC_STATUS_OK) 218 return status; 219 220 FileSystemFileUtil* file_util = 221 file_system_context->sandbox_delegate()->sync_file_util(); 222 DCHECK(file_util); 223 scoped_ptr<FileSystemOperationContext> context( 224 new FileSystemOperationContext(file_system_context)); 225 226 base::PlatformFileInfo file_info; 227 base::FilePath platform_path; 228 229 while (!dirty_files.empty()) { 230 const FileSystemURL url = dirty_files.front(); 231 dirty_files.pop(); 232 DCHECK_EQ(url.type(), fileapi::kFileSystemTypeSyncable); 233 234 switch (file_util->GetFileInfo(context.get(), url, 235 &file_info, &platform_path)) { 236 case base::PLATFORM_FILE_OK: { 237 if (!file_info.is_directory) { 238 RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 239 SYNC_FILE_TYPE_FILE)); 240 break; 241 } 242 243 RecordChange(url, FileChange( 244 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 245 SYNC_FILE_TYPE_DIRECTORY)); 246 247 // Push files and directories in this directory into |dirty_files|. 248 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator( 249 file_util->CreateFileEnumerator(context.get(), url)); 250 base::FilePath path_each; 251 while (!(path_each = enumerator->Next()).empty()) { 252 dirty_files.push(CreateSyncableFileSystemURL( 253 url.origin(), path_each)); 254 } 255 break; 256 } 257 case base::PLATFORM_FILE_ERROR_NOT_FOUND: { 258 // File represented by |url| has already been deleted. Since we cannot 259 // figure out if this file was directory or not from the URL, file 260 // type is treated as SYNC_FILE_TYPE_UNKNOWN. 261 // 262 // NOTE: Directory to have been reverted (that is, ADD -> DELETE) is 263 // also treated as FILE_CHANGE_DELETE. 264 RecordChange(url, FileChange(FileChange::FILE_CHANGE_DELETE, 265 SYNC_FILE_TYPE_UNKNOWN)); 266 break; 267 } 268 case base::PLATFORM_FILE_ERROR_FAILED: 269 default: 270 // TODO(nhiroki): handle file access error (http://crbug.com/155251). 271 LOG(WARNING) << "Failed to access local file."; 272 break; 273 } 274 } 275 return SYNC_STATUS_OK; 276} 277 278void LocalFileChangeTracker::RecordChange( 279 const FileSystemURL& url, const FileChange& change) { 280 DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); 281 ChangeInfo& info = changes_[url]; 282 if (info.change_seq >= 0) 283 change_seqs_.erase(info.change_seq); 284 info.change_list.Update(change); 285 if (info.change_list.empty()) { 286 changes_.erase(url); 287 UpdateNumChanges(); 288 return; 289 } 290 info.change_seq = current_change_seq_++; 291 change_seqs_[info.change_seq] = url; 292 UpdateNumChanges(); 293} 294 295// TrackerDB ------------------------------------------------------------------- 296 297LocalFileChangeTracker::TrackerDB::TrackerDB(const base::FilePath& base_path) 298 : base_path_(base_path), 299 db_status_(SYNC_STATUS_OK) {} 300 301SyncStatusCode LocalFileChangeTracker::TrackerDB::Init( 302 RecoveryOption recovery_option) { 303 if (db_.get() && db_status_ == SYNC_STATUS_OK) 304 return SYNC_STATUS_OK; 305 306 std::string path = fileapi::FilePathToString( 307 base_path_.Append(kDatabaseName)); 308 leveldb::Options options; 309 options.max_open_files = 0; // Use minimum. 310 options.create_if_missing = true; 311 leveldb::DB* db; 312 leveldb::Status status = leveldb::DB::Open(options, path, &db); 313 if (status.ok()) { 314 db_.reset(db); 315 return SYNC_STATUS_OK; 316 } 317 318 HandleError(FROM_HERE, status); 319 if (!status.IsCorruption()) 320 return LevelDBStatusToSyncStatusCode(status); 321 322 // Try to repair the corrupted DB. 323 switch (recovery_option) { 324 case FAIL_ON_CORRUPTION: 325 return SYNC_DATABASE_ERROR_CORRUPTION; 326 case REPAIR_ON_CORRUPTION: 327 return Repair(path); 328 } 329 NOTREACHED(); 330 return SYNC_DATABASE_ERROR_FAILED; 331} 332 333SyncStatusCode LocalFileChangeTracker::TrackerDB::Repair( 334 const std::string& db_path) { 335 DCHECK(!db_.get()); 336 LOG(WARNING) << "Attempting to repair TrackerDB."; 337 338 leveldb::Options options; 339 options.max_open_files = 0; // Use minimum. 340 if (leveldb::RepairDB(db_path, options).ok() && 341 Init(FAIL_ON_CORRUPTION) == SYNC_STATUS_OK) { 342 // TODO(nhiroki): perform some consistency checks between TrackerDB and 343 // syncable file system. 344 LOG(WARNING) << "Repairing TrackerDB completed."; 345 return SYNC_STATUS_OK; 346 } 347 348 LOG(WARNING) << "Failed to repair TrackerDB."; 349 return SYNC_DATABASE_ERROR_CORRUPTION; 350} 351 352// TODO(nhiroki): factor out the common methods into somewhere else. 353void LocalFileChangeTracker::TrackerDB::HandleError( 354 const tracked_objects::Location& from_here, 355 const leveldb::Status& status) { 356 LOG(ERROR) << "LocalFileChangeTracker::TrackerDB failed at: " 357 << from_here.ToString() << " with error: " << status.ToString(); 358} 359 360SyncStatusCode LocalFileChangeTracker::TrackerDB::MarkDirty( 361 const std::string& url) { 362 if (db_status_ != SYNC_STATUS_OK) 363 return db_status_; 364 365 db_status_ = Init(REPAIR_ON_CORRUPTION); 366 if (db_status_ != SYNC_STATUS_OK) { 367 db_.reset(); 368 return db_status_; 369 } 370 371 leveldb::Status status = db_->Put(leveldb::WriteOptions(), url, kMark); 372 if (!status.ok()) { 373 HandleError(FROM_HERE, status); 374 db_status_ = LevelDBStatusToSyncStatusCode(status); 375 db_.reset(); 376 return db_status_; 377 } 378 return SYNC_STATUS_OK; 379} 380 381SyncStatusCode LocalFileChangeTracker::TrackerDB::ClearDirty( 382 const std::string& url) { 383 if (db_status_ != SYNC_STATUS_OK) 384 return db_status_; 385 386 // Should not reach here before initializing the database. The database should 387 // be cleared after read, and should be initialized during read if 388 // uninitialized. 389 DCHECK(db_.get()); 390 391 leveldb::Status status = db_->Delete(leveldb::WriteOptions(), url); 392 if (!status.ok() && !status.IsNotFound()) { 393 HandleError(FROM_HERE, status); 394 db_status_ = LevelDBStatusToSyncStatusCode(status); 395 db_.reset(); 396 return db_status_; 397 } 398 return SYNC_STATUS_OK; 399} 400 401SyncStatusCode LocalFileChangeTracker::TrackerDB::GetDirtyEntries( 402 std::queue<FileSystemURL>* dirty_files) { 403 if (db_status_ != SYNC_STATUS_OK) 404 return db_status_; 405 406 db_status_ = Init(REPAIR_ON_CORRUPTION); 407 if (db_status_ != SYNC_STATUS_OK) { 408 db_.reset(); 409 return db_status_; 410 } 411 412 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); 413 iter->SeekToFirst(); 414 FileSystemURL url; 415 while (iter->Valid()) { 416 if (!DeserializeSyncableFileSystemURL(iter->key().ToString(), &url)) { 417 LOG(WARNING) << "Failed to deserialize an URL. " 418 << "TrackerDB might be corrupted."; 419 db_status_ = SYNC_DATABASE_ERROR_CORRUPTION; 420 db_.reset(); 421 return db_status_; 422 } 423 dirty_files->push(url); 424 iter->Next(); 425 } 426 return SYNC_STATUS_OK; 427} 428 429} // namespace sync_file_system 430