| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/browser/fileapi/sandbox_directory_database.h" | 5 #include "storage/browser/fileapi/sandbox_directory_database.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 #include <set> | 9 #include <set> |
| 10 #include <stack> | 10 #include <stack> |
| 11 | 11 |
| 12 #include "base/file_util.h" | 12 #include "base/file_util.h" |
| 13 #include "base/files/file_enumerator.h" | 13 #include "base/files/file_enumerator.h" |
| 14 #include "base/location.h" | 14 #include "base/location.h" |
| 15 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
| 16 #include "base/pickle.h" | 16 #include "base/pickle.h" |
| 17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 19 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 19 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
| 20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
| 21 #include "webkit/browser/fileapi/file_system_usage_cache.h" | 21 #include "storage/browser/fileapi/file_system_usage_cache.h" |
| 22 #include "webkit/common/fileapi/file_system_util.h" | 22 #include "storage/common/fileapi/file_system_util.h" |
| 23 | 23 |
| 24 namespace { | 24 namespace { |
| 25 | 25 |
| 26 bool PickleFromFileInfo( | 26 bool PickleFromFileInfo(const storage::SandboxDirectoryDatabase::FileInfo& info, |
| 27 const fileapi::SandboxDirectoryDatabase::FileInfo& info, | 27 Pickle* pickle) { |
| 28 Pickle* pickle) { | |
| 29 DCHECK(pickle); | 28 DCHECK(pickle); |
| 30 std::string data_path; | 29 std::string data_path; |
| 31 // Round off here to match the behavior of the filesystem on real files. | 30 // Round off here to match the behavior of the filesystem on real files. |
| 32 base::Time time = | 31 base::Time time = |
| 33 base::Time::FromDoubleT(floor(info.modification_time.ToDoubleT())); | 32 base::Time::FromDoubleT(floor(info.modification_time.ToDoubleT())); |
| 34 std::string name; | 33 std::string name; |
| 35 | 34 |
| 36 data_path = fileapi::FilePathToString(info.data_path); | 35 data_path = storage::FilePathToString(info.data_path); |
| 37 name = fileapi::FilePathToString(base::FilePath(info.name)); | 36 name = storage::FilePathToString(base::FilePath(info.name)); |
| 38 | 37 |
| 39 if (pickle->WriteInt64(info.parent_id) && | 38 if (pickle->WriteInt64(info.parent_id) && pickle->WriteString(data_path) && |
| 40 pickle->WriteString(data_path) && | 39 pickle->WriteString(name) && pickle->WriteInt64(time.ToInternalValue())) |
| 41 pickle->WriteString(name) && | |
| 42 pickle->WriteInt64(time.ToInternalValue())) | |
| 43 return true; | 40 return true; |
| 44 | 41 |
| 45 NOTREACHED(); | 42 NOTREACHED(); |
| 46 return false; | 43 return false; |
| 47 } | 44 } |
| 48 | 45 |
| 49 bool FileInfoFromPickle( | 46 bool FileInfoFromPickle(const Pickle& pickle, |
| 50 const Pickle& pickle, | 47 storage::SandboxDirectoryDatabase::FileInfo* info) { |
| 51 fileapi::SandboxDirectoryDatabase::FileInfo* info) { | |
| 52 PickleIterator iter(pickle); | 48 PickleIterator iter(pickle); |
| 53 std::string data_path; | 49 std::string data_path; |
| 54 std::string name; | 50 std::string name; |
| 55 int64 internal_time; | 51 int64 internal_time; |
| 56 | 52 |
| 57 if (pickle.ReadInt64(&iter, &info->parent_id) && | 53 if (pickle.ReadInt64(&iter, &info->parent_id) && |
| 58 pickle.ReadString(&iter, &data_path) && | 54 pickle.ReadString(&iter, &data_path) && pickle.ReadString(&iter, &name) && |
| 59 pickle.ReadString(&iter, &name) && | |
| 60 pickle.ReadInt64(&iter, &internal_time)) { | 55 pickle.ReadInt64(&iter, &internal_time)) { |
| 61 info->data_path = fileapi::StringToFilePath(data_path); | 56 info->data_path = storage::StringToFilePath(data_path); |
| 62 info->name = fileapi::StringToFilePath(name).value(); | 57 info->name = storage::StringToFilePath(name).value(); |
| 63 info->modification_time = base::Time::FromInternalValue(internal_time); | 58 info->modification_time = base::Time::FromInternalValue(internal_time); |
| 64 return true; | 59 return true; |
| 65 } | 60 } |
| 66 LOG(ERROR) << "Pickle could not be digested!"; | 61 LOG(ERROR) << "Pickle could not be digested!"; |
| 67 return false; | 62 return false; |
| 68 } | 63 } |
| 69 | 64 |
| 70 const base::FilePath::CharType kDirectoryDatabaseName[] = | 65 const base::FilePath::CharType kDirectoryDatabaseName[] = |
| 71 FILE_PATH_LITERAL("Paths"); | 66 FILE_PATH_LITERAL("Paths"); |
| 72 const char kChildLookupPrefix[] = "CHILD_OF:"; | 67 const char kChildLookupPrefix[] = "CHILD_OF:"; |
| 73 const char kChildLookupSeparator[] = ":"; | 68 const char kChildLookupSeparator[] = ":"; |
| 74 const char kLastFileIdKey[] = "LAST_FILE_ID"; | 69 const char kLastFileIdKey[] = "LAST_FILE_ID"; |
| 75 const char kLastIntegerKey[] = "LAST_INTEGER"; | 70 const char kLastIntegerKey[] = "LAST_INTEGER"; |
| 76 const int64 kMinimumReportIntervalHours = 1; | 71 const int64 kMinimumReportIntervalHours = 1; |
| 77 const char kInitStatusHistogramLabel[] = "FileSystem.DirectoryDatabaseInit"; | 72 const char kInitStatusHistogramLabel[] = "FileSystem.DirectoryDatabaseInit"; |
| 78 const char kDatabaseRepairHistogramLabel[] = | 73 const char kDatabaseRepairHistogramLabel[] = |
| 79 "FileSystem.DirectoryDatabaseRepair"; | 74 "FileSystem.DirectoryDatabaseRepair"; |
| 80 | 75 |
| 81 enum InitStatus { | 76 enum InitStatus { |
| 82 INIT_STATUS_OK = 0, | 77 INIT_STATUS_OK = 0, |
| 83 INIT_STATUS_CORRUPTION, | 78 INIT_STATUS_CORRUPTION, |
| 84 INIT_STATUS_IO_ERROR, | 79 INIT_STATUS_IO_ERROR, |
| 85 INIT_STATUS_UNKNOWN_ERROR, | 80 INIT_STATUS_UNKNOWN_ERROR, |
| 86 INIT_STATUS_MAX | 81 INIT_STATUS_MAX |
| 87 }; | 82 }; |
| 88 | 83 |
| 89 enum RepairResult { | 84 enum RepairResult { DB_REPAIR_SUCCEEDED = 0, DB_REPAIR_FAILED, DB_REPAIR_MAX }; |
| 90 DB_REPAIR_SUCCEEDED = 0, | |
| 91 DB_REPAIR_FAILED, | |
| 92 DB_REPAIR_MAX | |
| 93 }; | |
| 94 | 85 |
| 95 std::string GetChildLookupKey( | 86 std::string GetChildLookupKey( |
| 96 fileapi::SandboxDirectoryDatabase::FileId parent_id, | 87 storage::SandboxDirectoryDatabase::FileId parent_id, |
| 97 const base::FilePath::StringType& child_name) { | 88 const base::FilePath::StringType& child_name) { |
| 98 std::string name; | 89 std::string name; |
| 99 name = fileapi::FilePathToString(base::FilePath(child_name)); | 90 name = storage::FilePathToString(base::FilePath(child_name)); |
| 100 return std::string(kChildLookupPrefix) + base::Int64ToString(parent_id) + | 91 return std::string(kChildLookupPrefix) + base::Int64ToString(parent_id) + |
| 101 std::string(kChildLookupSeparator) + name; | 92 std::string(kChildLookupSeparator) + name; |
| 102 } | 93 } |
| 103 | 94 |
| 104 std::string GetChildListingKeyPrefix( | 95 std::string GetChildListingKeyPrefix( |
| 105 fileapi::SandboxDirectoryDatabase::FileId parent_id) { | 96 storage::SandboxDirectoryDatabase::FileId parent_id) { |
| 106 return std::string(kChildLookupPrefix) + base::Int64ToString(parent_id) + | 97 return std::string(kChildLookupPrefix) + base::Int64ToString(parent_id) + |
| 107 std::string(kChildLookupSeparator); | 98 std::string(kChildLookupSeparator); |
| 108 } | 99 } |
| 109 | 100 |
| 110 const char* LastFileIdKey() { | 101 const char* LastFileIdKey() { |
| 111 return kLastFileIdKey; | 102 return kLastFileIdKey; |
| 112 } | 103 } |
| 113 | 104 |
| 114 const char* LastIntegerKey() { | 105 const char* LastIntegerKey() { |
| 115 return kLastIntegerKey; | 106 return kLastIntegerKey; |
| 116 } | 107 } |
| 117 | 108 |
| 118 std::string GetFileLookupKey( | 109 std::string GetFileLookupKey( |
| 119 fileapi::SandboxDirectoryDatabase::FileId file_id) { | 110 storage::SandboxDirectoryDatabase::FileId file_id) { |
| 120 return base::Int64ToString(file_id); | 111 return base::Int64ToString(file_id); |
| 121 } | 112 } |
| 122 | 113 |
| 123 // Assumptions: | 114 // Assumptions: |
| 124 // - Any database entry is one of: | 115 // - Any database entry is one of: |
| 125 // - ("CHILD_OF:|parent_id|:<name>", "|file_id|"), | 116 // - ("CHILD_OF:|parent_id|:<name>", "|file_id|"), |
| 126 // - ("LAST_FILE_ID", "|last_file_id|"), | 117 // - ("LAST_FILE_ID", "|last_file_id|"), |
| 127 // - ("LAST_INTEGER", "|last_integer|"), | 118 // - ("LAST_INTEGER", "|last_integer|"), |
| 128 // - ("|file_id|", "pickled FileInfo") | 119 // - ("|file_id|", "pickled FileInfo") |
| 129 // where FileInfo has |parent_id|, |data_path|, |name| and | 120 // where FileInfo has |parent_id|, |data_path|, |name| and |
| 130 // |modification_time|, | 121 // |modification_time|, |
| 131 // Constraints: | 122 // Constraints: |
| 132 // - Each file in the database has unique backing file. | 123 // - Each file in the database has unique backing file. |
| 133 // - Each file in |filesystem_data_directory_| has a database entry. | 124 // - Each file in |filesystem_data_directory_| has a database entry. |
| 134 // - Directory structure is tree, i.e. connected and acyclic. | 125 // - Directory structure is tree, i.e. connected and acyclic. |
| 135 class DatabaseCheckHelper { | 126 class DatabaseCheckHelper { |
| 136 public: | 127 public: |
| 137 typedef fileapi::SandboxDirectoryDatabase::FileId FileId; | 128 typedef storage::SandboxDirectoryDatabase::FileId FileId; |
| 138 typedef fileapi::SandboxDirectoryDatabase::FileInfo FileInfo; | 129 typedef storage::SandboxDirectoryDatabase::FileInfo FileInfo; |
| 139 | 130 |
| 140 DatabaseCheckHelper(fileapi::SandboxDirectoryDatabase* dir_db, | 131 DatabaseCheckHelper(storage::SandboxDirectoryDatabase* dir_db, |
| 141 leveldb::DB* db, | 132 leveldb::DB* db, |
| 142 const base::FilePath& path); | 133 const base::FilePath& path); |
| 143 | 134 |
| 144 bool IsFileSystemConsistent() { | 135 bool IsFileSystemConsistent() { |
| 145 return IsDatabaseEmpty() || | 136 return IsDatabaseEmpty() || |
| 146 (ScanDatabase() && ScanDirectory() && ScanHierarchy()); | 137 (ScanDatabase() && ScanDirectory() && ScanHierarchy()); |
| 147 } | 138 } |
| 148 | 139 |
| 149 private: | 140 private: |
| 150 bool IsDatabaseEmpty(); | 141 bool IsDatabaseEmpty(); |
| 151 // These 3 methods need to be called in the order. Each method requires its | 142 // These 3 methods need to be called in the order. Each method requires its |
| 152 // previous method finished successfully. They also require the database is | 143 // previous method finished successfully. They also require the database is |
| 153 // not empty. | 144 // not empty. |
| 154 bool ScanDatabase(); | 145 bool ScanDatabase(); |
| 155 bool ScanDirectory(); | 146 bool ScanDirectory(); |
| 156 bool ScanHierarchy(); | 147 bool ScanHierarchy(); |
| 157 | 148 |
| 158 fileapi::SandboxDirectoryDatabase* dir_db_; | 149 storage::SandboxDirectoryDatabase* dir_db_; |
| 159 leveldb::DB* db_; | 150 leveldb::DB* db_; |
| 160 base::FilePath path_; | 151 base::FilePath path_; |
| 161 | 152 |
| 162 std::set<base::FilePath> files_in_db_; | 153 std::set<base::FilePath> files_in_db_; |
| 163 | 154 |
| 164 size_t num_directories_in_db_; | 155 size_t num_directories_in_db_; |
| 165 size_t num_files_in_db_; | 156 size_t num_files_in_db_; |
| 166 size_t num_hierarchy_links_in_db_; | 157 size_t num_hierarchy_links_in_db_; |
| 167 | 158 |
| 168 FileId last_file_id_; | 159 FileId last_file_id_; |
| 169 FileId last_integer_; | 160 FileId last_integer_; |
| 170 }; | 161 }; |
| 171 | 162 |
| 172 DatabaseCheckHelper::DatabaseCheckHelper( | 163 DatabaseCheckHelper::DatabaseCheckHelper( |
| 173 fileapi::SandboxDirectoryDatabase* dir_db, | 164 storage::SandboxDirectoryDatabase* dir_db, |
| 174 leveldb::DB* db, | 165 leveldb::DB* db, |
| 175 const base::FilePath& path) | 166 const base::FilePath& path) |
| 176 : dir_db_(dir_db), db_(db), path_(path), | 167 : dir_db_(dir_db), |
| 168 db_(db), |
| 169 path_(path), |
| 177 num_directories_in_db_(0), | 170 num_directories_in_db_(0), |
| 178 num_files_in_db_(0), | 171 num_files_in_db_(0), |
| 179 num_hierarchy_links_in_db_(0), | 172 num_hierarchy_links_in_db_(0), |
| 180 last_file_id_(-1), last_integer_(-1) { | 173 last_file_id_(-1), |
| 174 last_integer_(-1) { |
| 181 DCHECK(dir_db_); | 175 DCHECK(dir_db_); |
| 182 DCHECK(db_); | 176 DCHECK(db_); |
| 183 DCHECK(!path_.empty() && base::DirectoryExists(path_)); | 177 DCHECK(!path_.empty() && base::DirectoryExists(path_)); |
| 184 } | 178 } |
| 185 | 179 |
| 186 bool DatabaseCheckHelper::IsDatabaseEmpty() { | 180 bool DatabaseCheckHelper::IsDatabaseEmpty() { |
| 187 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 181 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
| 188 itr->SeekToFirst(); | 182 itr->SeekToFirst(); |
| 189 return !itr->Valid(); | 183 return !itr->Valid(); |
| 190 } | 184 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 214 } else if (key == kLastIntegerKey) { | 208 } else if (key == kLastIntegerKey) { |
| 215 // key: "LAST_INTEGER" | 209 // key: "LAST_INTEGER" |
| 216 // value: "<last_integer>" | 210 // value: "<last_integer>" |
| 217 if (last_integer_ >= 0 || | 211 if (last_integer_ >= 0 || |
| 218 !base::StringToInt64(itr->value().ToString(), &last_integer_)) | 212 !base::StringToInt64(itr->value().ToString(), &last_integer_)) |
| 219 return false; | 213 return false; |
| 220 } else { | 214 } else { |
| 221 // key: "<entry_id>" | 215 // key: "<entry_id>" |
| 222 // value: "<pickled FileInfo>" | 216 // value: "<pickled FileInfo>" |
| 223 FileInfo file_info; | 217 FileInfo file_info; |
| 224 if (!FileInfoFromPickle( | 218 if (!FileInfoFromPickle(Pickle(itr->value().data(), itr->value().size()), |
| 225 Pickle(itr->value().data(), itr->value().size()), &file_info)) | 219 &file_info)) |
| 226 return false; | 220 return false; |
| 227 | 221 |
| 228 FileId file_id = -1; | 222 FileId file_id = -1; |
| 229 if (!base::StringToInt64(key, &file_id) || file_id < 0) | 223 if (!base::StringToInt64(key, &file_id) || file_id < 0) |
| 230 return false; | 224 return false; |
| 231 | 225 |
| 232 if (max_file_id < file_id) | 226 if (max_file_id < file_id) |
| 233 max_file_id = file_id; | 227 max_file_id = file_id; |
| 234 if (!file_ids.insert(file_id).second) | 228 if (!file_ids.insert(file_id).second) |
| 235 return false; | 229 return false; |
| 236 | 230 |
| 237 if (file_info.is_directory()) { | 231 if (file_info.is_directory()) { |
| 238 ++num_directories_in_db_; | 232 ++num_directories_in_db_; |
| 239 DCHECK(file_info.data_path.empty()); | 233 DCHECK(file_info.data_path.empty()); |
| 240 } else { | 234 } else { |
| 241 // Ensure any pair of file entry don't share their data_path. | 235 // Ensure any pair of file entry don't share their data_path. |
| 242 if (!files_in_db_.insert(file_info.data_path).second) | 236 if (!files_in_db_.insert(file_info.data_path).second) |
| 243 return false; | 237 return false; |
| 244 | 238 |
| 245 // Ensure the backing file exists as a normal file. | 239 // Ensure the backing file exists as a normal file. |
| 246 base::File::Info platform_file_info; | 240 base::File::Info platform_file_info; |
| 247 if (!base::GetFileInfo( | 241 if (!base::GetFileInfo(path_.Append(file_info.data_path), |
| 248 path_.Append(file_info.data_path), &platform_file_info) || | 242 &platform_file_info) || |
| 249 platform_file_info.is_directory || | 243 platform_file_info.is_directory || |
| 250 platform_file_info.is_symbolic_link) { | 244 platform_file_info.is_symbolic_link) { |
| 251 // leveldb::Iterator iterates a snapshot of the database. | 245 // leveldb::Iterator iterates a snapshot of the database. |
| 252 // So even after RemoveFileInfo() call, we'll visit hierarchy link | 246 // So even after RemoveFileInfo() call, we'll visit hierarchy link |
| 253 // from |parent_id| to |file_id|. | 247 // from |parent_id| to |file_id|. |
| 254 if (!dir_db_->RemoveFileInfo(file_id)) | 248 if (!dir_db_->RemoveFileInfo(file_id)) |
| 255 return false; | 249 return false; |
| 256 --num_hierarchy_links_in_db_; | 250 --num_hierarchy_links_in_db_; |
| 257 files_in_db_.erase(file_info.data_path); | 251 files_in_db_.erase(file_info.data_path); |
| 258 } else { | 252 } else { |
| 259 ++num_files_in_db_; | 253 ++num_files_in_db_; |
| 260 } | 254 } |
| 261 } | 255 } |
| 262 } | 256 } |
| 263 } | 257 } |
| 264 | 258 |
| 265 // TODO(tzik): Add constraint for |last_integer_| to avoid possible | 259 // TODO(tzik): Add constraint for |last_integer_| to avoid possible |
| 266 // data path confliction on ObfuscatedFileUtil. | 260 // data path confliction on ObfuscatedFileUtil. |
| 267 return max_file_id <= last_file_id_; | 261 return max_file_id <= last_file_id_; |
| 268 } | 262 } |
| 269 | 263 |
| 270 bool DatabaseCheckHelper::ScanDirectory() { | 264 bool DatabaseCheckHelper::ScanDirectory() { |
| 271 // TODO(kinuko): Scans all local file system entries to verify each of them | 265 // TODO(kinuko): Scans all local file system entries to verify each of them |
| 272 // has a database entry. | 266 // has a database entry. |
| 273 const base::FilePath kExcludes[] = { | 267 const base::FilePath kExcludes[] = { |
| 274 base::FilePath(kDirectoryDatabaseName), | 268 base::FilePath(kDirectoryDatabaseName), |
| 275 base::FilePath(fileapi::FileSystemUsageCache::kUsageFileName), | 269 base::FilePath(storage::FileSystemUsageCache::kUsageFileName), |
| 276 }; | 270 }; |
| 277 | 271 |
| 278 // Any path in |pending_directories| is relative to |path_|. | 272 // Any path in |pending_directories| is relative to |path_|. |
| 279 std::stack<base::FilePath> pending_directories; | 273 std::stack<base::FilePath> pending_directories; |
| 280 pending_directories.push(base::FilePath()); | 274 pending_directories.push(base::FilePath()); |
| 281 | 275 |
| 282 while (!pending_directories.empty()) { | 276 while (!pending_directories.empty()) { |
| 283 base::FilePath dir_path = pending_directories.top(); | 277 base::FilePath dir_path = pending_directories.top(); |
| 284 pending_directories.pop(); | 278 pending_directories.pop(); |
| 285 | 279 |
| 286 base::FileEnumerator file_enum( | 280 base::FileEnumerator file_enum( |
| 287 dir_path.empty() ? path_ : path_.Append(dir_path), | 281 dir_path.empty() ? path_ : path_.Append(dir_path), |
| 288 false /* not recursive */, | 282 false /* not recursive */, |
| 289 base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES); | 283 base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES); |
| 290 | 284 |
| 291 base::FilePath absolute_file_path; | 285 base::FilePath absolute_file_path; |
| 292 while (!(absolute_file_path = file_enum.Next()).empty()) { | 286 while (!(absolute_file_path = file_enum.Next()).empty()) { |
| 293 base::FileEnumerator::FileInfo find_info = file_enum.GetInfo(); | 287 base::FileEnumerator::FileInfo find_info = file_enum.GetInfo(); |
| 294 | 288 |
| 295 base::FilePath relative_file_path; | 289 base::FilePath relative_file_path; |
| 296 if (!path_.AppendRelativePath(absolute_file_path, &relative_file_path)) | 290 if (!path_.AppendRelativePath(absolute_file_path, &relative_file_path)) |
| 297 return false; | 291 return false; |
| 298 | 292 |
| 299 if (std::find(kExcludes, kExcludes + arraysize(kExcludes), | 293 if (std::find(kExcludes, |
| 294 kExcludes + arraysize(kExcludes), |
| 300 relative_file_path) != kExcludes + arraysize(kExcludes)) | 295 relative_file_path) != kExcludes + arraysize(kExcludes)) |
| 301 continue; | 296 continue; |
| 302 | 297 |
| 303 if (find_info.IsDirectory()) { | 298 if (find_info.IsDirectory()) { |
| 304 pending_directories.push(relative_file_path); | 299 pending_directories.push(relative_file_path); |
| 305 continue; | 300 continue; |
| 306 } | 301 } |
| 307 | 302 |
| 308 // Check if the file has a database entry. | 303 // Check if the file has a database entry. |
| 309 std::set<base::FilePath>::iterator itr = | 304 std::set<base::FilePath>::iterator itr = |
| (...skipping 15 matching lines...) Expand all Loading... |
| 325 size_t visited_files = 0; | 320 size_t visited_files = 0; |
| 326 size_t visited_links = 0; | 321 size_t visited_links = 0; |
| 327 | 322 |
| 328 std::stack<FileId> directories; | 323 std::stack<FileId> directories; |
| 329 directories.push(0); | 324 directories.push(0); |
| 330 | 325 |
| 331 // Check if the root directory exists as a directory. | 326 // Check if the root directory exists as a directory. |
| 332 FileInfo file_info; | 327 FileInfo file_info; |
| 333 if (!dir_db_->GetFileInfo(0, &file_info)) | 328 if (!dir_db_->GetFileInfo(0, &file_info)) |
| 334 return false; | 329 return false; |
| 335 if (file_info.parent_id != 0 || | 330 if (file_info.parent_id != 0 || !file_info.is_directory()) |
| 336 !file_info.is_directory()) | |
| 337 return false; | 331 return false; |
| 338 | 332 |
| 339 while (!directories.empty()) { | 333 while (!directories.empty()) { |
| 340 ++visited_directories; | 334 ++visited_directories; |
| 341 FileId dir_id = directories.top(); | 335 FileId dir_id = directories.top(); |
| 342 directories.pop(); | 336 directories.pop(); |
| 343 | 337 |
| 344 std::vector<FileId> children; | 338 std::vector<FileId> children; |
| 345 if (!dir_db_->ListChildren(dir_id, &children)) | 339 if (!dir_db_->ListChildren(dir_id, &children)) |
| 346 return false; | 340 return false; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 367 if (file_info.is_directory()) | 361 if (file_info.is_directory()) |
| 368 directories.push(*itr); | 362 directories.push(*itr); |
| 369 else | 363 else |
| 370 ++visited_files; | 364 ++visited_files; |
| 371 ++visited_links; | 365 ++visited_links; |
| 372 } | 366 } |
| 373 } | 367 } |
| 374 | 368 |
| 375 // Check if we've visited all database entries. | 369 // Check if we've visited all database entries. |
| 376 return num_directories_in_db_ == visited_directories && | 370 return num_directories_in_db_ == visited_directories && |
| 377 num_files_in_db_ == visited_files && | 371 num_files_in_db_ == visited_files && |
| 378 num_hierarchy_links_in_db_ == visited_links; | 372 num_hierarchy_links_in_db_ == visited_links; |
| 379 } | 373 } |
| 380 | 374 |
| 381 // Returns true if the given |data_path| contains no parent references ("..") | 375 // Returns true if the given |data_path| contains no parent references ("..") |
| 382 // and does not refer to special system files. | 376 // and does not refer to special system files. |
| 383 // This is called in GetFileInfo, AddFileInfo and UpdateFileInfo to | 377 // This is called in GetFileInfo, AddFileInfo and UpdateFileInfo to |
| 384 // ensure we're only dealing with valid data paths. | 378 // ensure we're only dealing with valid data paths. |
| 385 bool VerifyDataPath(const base::FilePath& data_path) { | 379 bool VerifyDataPath(const base::FilePath& data_path) { |
| 386 // |data_path| should not contain any ".." and should be a relative path | 380 // |data_path| should not contain any ".." and should be a relative path |
| 387 // (to the filesystem_data_directory_). | 381 // (to the filesystem_data_directory_). |
| 388 if (data_path.ReferencesParent() || data_path.IsAbsolute()) | 382 if (data_path.ReferencesParent() || data_path.IsAbsolute()) |
| 389 return false; | 383 return false; |
| 390 // See if it's not pointing to the special system paths. | 384 // See if it's not pointing to the special system paths. |
| 391 const base::FilePath kExcludes[] = { | 385 const base::FilePath kExcludes[] = { |
| 392 base::FilePath(kDirectoryDatabaseName), | 386 base::FilePath(kDirectoryDatabaseName), |
| 393 base::FilePath(fileapi::FileSystemUsageCache::kUsageFileName), | 387 base::FilePath(storage::FileSystemUsageCache::kUsageFileName), |
| 394 }; | 388 }; |
| 395 for (size_t i = 0; i < arraysize(kExcludes); ++i) { | 389 for (size_t i = 0; i < arraysize(kExcludes); ++i) { |
| 396 if (data_path == kExcludes[i] || kExcludes[i].IsParent(data_path)) | 390 if (data_path == kExcludes[i] || kExcludes[i].IsParent(data_path)) |
| 397 return false; | 391 return false; |
| 398 } | 392 } |
| 399 return true; | 393 return true; |
| 400 } | 394 } |
| 401 | 395 |
| 402 } // namespace | 396 } // namespace |
| 403 | 397 |
| 404 namespace fileapi { | 398 namespace storage { |
| 405 | 399 |
| 406 SandboxDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) { | 400 SandboxDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) { |
| 407 } | 401 } |
| 408 | 402 |
| 409 SandboxDirectoryDatabase::FileInfo::~FileInfo() { | 403 SandboxDirectoryDatabase::FileInfo::~FileInfo() { |
| 410 } | 404 } |
| 411 | 405 |
| 412 SandboxDirectoryDatabase::SandboxDirectoryDatabase( | 406 SandboxDirectoryDatabase::SandboxDirectoryDatabase( |
| 413 const base::FilePath& filesystem_data_directory, | 407 const base::FilePath& filesystem_data_directory, |
| 414 leveldb::Env* env_override) | 408 leveldb::Env* env_override) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 436 if (!base::StringToInt64(child_id_string, child_id)) { | 430 if (!base::StringToInt64(child_id_string, child_id)) { |
| 437 LOG(ERROR) << "Hit database corruption!"; | 431 LOG(ERROR) << "Hit database corruption!"; |
| 438 return false; | 432 return false; |
| 439 } | 433 } |
| 440 return true; | 434 return true; |
| 441 } | 435 } |
| 442 HandleError(FROM_HERE, status); | 436 HandleError(FROM_HERE, status); |
| 443 return false; | 437 return false; |
| 444 } | 438 } |
| 445 | 439 |
| 446 bool SandboxDirectoryDatabase::GetFileWithPath( | 440 bool SandboxDirectoryDatabase::GetFileWithPath(const base::FilePath& path, |
| 447 const base::FilePath& path, FileId* file_id) { | 441 FileId* file_id) { |
| 448 std::vector<base::FilePath::StringType> components; | 442 std::vector<base::FilePath::StringType> components; |
| 449 VirtualPath::GetComponents(path, &components); | 443 VirtualPath::GetComponents(path, &components); |
| 450 FileId local_id = 0; | 444 FileId local_id = 0; |
| 451 std::vector<base::FilePath::StringType>::iterator iter; | 445 std::vector<base::FilePath::StringType>::iterator iter; |
| 452 for (iter = components.begin(); iter != components.end(); ++iter) { | 446 for (iter = components.begin(); iter != components.end(); ++iter) { |
| 453 base::FilePath::StringType name; | 447 base::FilePath::StringType name; |
| 454 name = *iter; | 448 name = *iter; |
| 455 if (name == FILE_PATH_LITERAL("/")) | 449 if (name == FILE_PATH_LITERAL("/")) |
| 456 continue; | 450 continue; |
| 457 if (!GetChildWithName(local_id, name, &local_id)) | 451 if (!GetChildWithName(local_id, name, &local_id)) |
| 458 return false; | 452 return false; |
| 459 } | 453 } |
| 460 *file_id = local_id; | 454 *file_id = local_id; |
| 461 return true; | 455 return true; |
| 462 } | 456 } |
| 463 | 457 |
| 464 bool SandboxDirectoryDatabase::ListChildren( | 458 bool SandboxDirectoryDatabase::ListChildren(FileId parent_id, |
| 465 FileId parent_id, std::vector<FileId>* children) { | 459 std::vector<FileId>* children) { |
| 466 // Check to add later: fail if parent is a file, at least in debug builds. | 460 // Check to add later: fail if parent is a file, at least in debug builds. |
| 467 if (!Init(REPAIR_ON_CORRUPTION)) | 461 if (!Init(REPAIR_ON_CORRUPTION)) |
| 468 return false; | 462 return false; |
| 469 DCHECK(children); | 463 DCHECK(children); |
| 470 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); | 464 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); |
| 471 | 465 |
| 472 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); | 466 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); |
| 473 iter->Seek(child_key_prefix); | 467 iter->Seek(child_key_prefix); |
| 474 children->clear(); | 468 children->clear(); |
| 475 while (iter->Valid() && | 469 while (iter->Valid() && |
| 476 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { | 470 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { |
| 477 std::string child_id_string = iter->value().ToString(); | 471 std::string child_id_string = iter->value().ToString(); |
| 478 FileId child_id; | 472 FileId child_id; |
| 479 if (!base::StringToInt64(child_id_string, &child_id)) { | 473 if (!base::StringToInt64(child_id_string, &child_id)) { |
| 480 LOG(ERROR) << "Hit database corruption!"; | 474 LOG(ERROR) << "Hit database corruption!"; |
| 481 return false; | 475 return false; |
| 482 } | 476 } |
| 483 children->push_back(child_id); | 477 children->push_back(child_id); |
| 484 iter->Next(); | 478 iter->Next(); |
| 485 } | 479 } |
| 486 return true; | 480 return true; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 513 info->name = base::FilePath::StringType(); | 507 info->name = base::FilePath::StringType(); |
| 514 info->data_path = base::FilePath(); | 508 info->data_path = base::FilePath(); |
| 515 info->modification_time = base::Time::Now(); | 509 info->modification_time = base::Time::Now(); |
| 516 info->parent_id = 0; | 510 info->parent_id = 0; |
| 517 return true; | 511 return true; |
| 518 } | 512 } |
| 519 HandleError(FROM_HERE, status); | 513 HandleError(FROM_HERE, status); |
| 520 return false; | 514 return false; |
| 521 } | 515 } |
| 522 | 516 |
| 523 base::File::Error SandboxDirectoryDatabase::AddFileInfo( | 517 base::File::Error SandboxDirectoryDatabase::AddFileInfo(const FileInfo& info, |
| 524 const FileInfo& info, FileId* file_id) { | 518 FileId* file_id) { |
| 525 if (!Init(REPAIR_ON_CORRUPTION)) | 519 if (!Init(REPAIR_ON_CORRUPTION)) |
| 526 return base::File::FILE_ERROR_FAILED; | 520 return base::File::FILE_ERROR_FAILED; |
| 527 DCHECK(file_id); | 521 DCHECK(file_id); |
| 528 std::string child_key = GetChildLookupKey(info.parent_id, info.name); | 522 std::string child_key = GetChildLookupKey(info.parent_id, info.name); |
| 529 std::string child_id_string; | 523 std::string child_id_string; |
| 530 leveldb::Status status = | 524 leveldb::Status status = |
| 531 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); | 525 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); |
| 532 if (status.ok()) { | 526 if (status.ok()) { |
| 533 LOG(ERROR) << "File exists already!"; | 527 LOG(ERROR) << "File exists already!"; |
| 534 return base::File::FILE_ERROR_EXISTS; | 528 return base::File::FILE_ERROR_EXISTS; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 572 if (!RemoveFileInfoHelper(file_id, &batch)) | 566 if (!RemoveFileInfoHelper(file_id, &batch)) |
| 573 return false; | 567 return false; |
| 574 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); | 568 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| 575 if (!status.ok()) { | 569 if (!status.ok()) { |
| 576 HandleError(FROM_HERE, status); | 570 HandleError(FROM_HERE, status); |
| 577 return false; | 571 return false; |
| 578 } | 572 } |
| 579 return true; | 573 return true; |
| 580 } | 574 } |
| 581 | 575 |
| 582 bool SandboxDirectoryDatabase::UpdateFileInfo( | 576 bool SandboxDirectoryDatabase::UpdateFileInfo(FileId file_id, |
| 583 FileId file_id, const FileInfo& new_info) { | 577 const FileInfo& new_info) { |
| 584 // TODO(ericu): We should also check to see that this doesn't create a loop, | 578 // TODO(ericu): We should also check to see that this doesn't create a loop, |
| 585 // but perhaps only in a debug build. | 579 // but perhaps only in a debug build. |
| 586 if (!Init(REPAIR_ON_CORRUPTION)) | 580 if (!Init(REPAIR_ON_CORRUPTION)) |
| 587 return false; | 581 return false; |
| 588 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. | 582 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. |
| 589 FileInfo old_info; | 583 FileInfo old_info; |
| 590 if (!GetFileInfo(file_id, &old_info)) | 584 if (!GetFileInfo(file_id, &old_info)) |
| 591 return false; | 585 return false; |
| 592 if (old_info.parent_id != new_info.parent_id && | 586 if (old_info.parent_id != new_info.parent_id && |
| 593 !IsDirectory(new_info.parent_id)) | 587 !IsDirectory(new_info.parent_id)) |
| (...skipping 13 matching lines...) Expand all Loading... |
| 607 return false; | 601 return false; |
| 608 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); | 602 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| 609 if (!status.ok()) { | 603 if (!status.ok()) { |
| 610 HandleError(FROM_HERE, status); | 604 HandleError(FROM_HERE, status); |
| 611 return false; | 605 return false; |
| 612 } | 606 } |
| 613 return true; | 607 return true; |
| 614 } | 608 } |
| 615 | 609 |
| 616 bool SandboxDirectoryDatabase::UpdateModificationTime( | 610 bool SandboxDirectoryDatabase::UpdateModificationTime( |
| 617 FileId file_id, const base::Time& modification_time) { | 611 FileId file_id, |
| 612 const base::Time& modification_time) { |
| 618 FileInfo info; | 613 FileInfo info; |
| 619 if (!GetFileInfo(file_id, &info)) | 614 if (!GetFileInfo(file_id, &info)) |
| 620 return false; | 615 return false; |
| 621 info.modification_time = modification_time; | 616 info.modification_time = modification_time; |
| 622 Pickle pickle; | 617 Pickle pickle; |
| 623 if (!PickleFromFileInfo(info, &pickle)) | 618 if (!PickleFromFileInfo(info, &pickle)) |
| 624 return false; | 619 return false; |
| 625 leveldb::Status status = db_->Put( | 620 leveldb::Status status = |
| 626 leveldb::WriteOptions(), | 621 db_->Put(leveldb::WriteOptions(), |
| 627 GetFileLookupKey(file_id), | 622 GetFileLookupKey(file_id), |
| 628 leveldb::Slice(reinterpret_cast<const char *>(pickle.data()), | 623 leveldb::Slice(reinterpret_cast<const char*>(pickle.data()), |
| 629 pickle.size())); | 624 pickle.size())); |
| 630 if (!status.ok()) { | 625 if (!status.ok()) { |
| 631 HandleError(FROM_HERE, status); | 626 HandleError(FROM_HERE, status); |
| 632 return false; | 627 return false; |
| 633 } | 628 } |
| 634 return true; | 629 return true; |
| 635 } | 630 } |
| 636 | 631 |
| 637 bool SandboxDirectoryDatabase::OverwritingMoveFile( | 632 bool SandboxDirectoryDatabase::OverwritingMoveFile(FileId src_file_id, |
| 638 FileId src_file_id, FileId dest_file_id) { | 633 FileId dest_file_id) { |
| 639 FileInfo src_file_info; | 634 FileInfo src_file_info; |
| 640 FileInfo dest_file_info; | 635 FileInfo dest_file_info; |
| 641 | 636 |
| 642 if (!GetFileInfo(src_file_id, &src_file_info)) | 637 if (!GetFileInfo(src_file_id, &src_file_info)) |
| 643 return false; | 638 return false; |
| 644 if (!GetFileInfo(dest_file_id, &dest_file_info)) | 639 if (!GetFileInfo(dest_file_id, &dest_file_info)) |
| 645 return false; | 640 return false; |
| 646 if (src_file_info.is_directory() || dest_file_info.is_directory()) | 641 if (src_file_info.is_directory() || dest_file_info.is_directory()) |
| 647 return false; | 642 return false; |
| 648 leveldb::WriteBatch batch; | 643 leveldb::WriteBatch batch; |
| 649 // This is the only field that really gets moved over; if you add fields to | 644 // This is the only field that really gets moved over; if you add fields to |
| 650 // FileInfo, e.g. ctime, they might need to be copied here. | 645 // FileInfo, e.g. ctime, they might need to be copied here. |
| 651 dest_file_info.data_path = src_file_info.data_path; | 646 dest_file_info.data_path = src_file_info.data_path; |
| 652 if (!RemoveFileInfoHelper(src_file_id, &batch)) | 647 if (!RemoveFileInfoHelper(src_file_id, &batch)) |
| 653 return false; | 648 return false; |
| 654 Pickle pickle; | 649 Pickle pickle; |
| 655 if (!PickleFromFileInfo(dest_file_info, &pickle)) | 650 if (!PickleFromFileInfo(dest_file_info, &pickle)) |
| 656 return false; | 651 return false; |
| 657 batch.Put( | 652 batch.Put(GetFileLookupKey(dest_file_id), |
| 658 GetFileLookupKey(dest_file_id), | 653 leveldb::Slice(reinterpret_cast<const char*>(pickle.data()), |
| 659 leveldb::Slice(reinterpret_cast<const char *>(pickle.data()), | 654 pickle.size())); |
| 660 pickle.size())); | |
| 661 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); | 655 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| 662 if (!status.ok()) { | 656 if (!status.ok()) { |
| 663 HandleError(FROM_HERE, status); | 657 HandleError(FROM_HERE, status); |
| 664 return false; | 658 return false; |
| 665 } | 659 } |
| 666 return true; | 660 return true; |
| 667 } | 661 } |
| 668 | 662 |
| 669 bool SandboxDirectoryDatabase::GetNextInteger(int64* next) { | 663 bool SandboxDirectoryDatabase::GetNextInteger(int64* next) { |
| 670 if (!Init(REPAIR_ON_CORRUPTION)) | 664 if (!Init(REPAIR_ON_CORRUPTION)) |
| 671 return false; | 665 return false; |
| 672 DCHECK(next); | 666 DCHECK(next); |
| 673 std::string int_string; | 667 std::string int_string; |
| 674 leveldb::Status status = | 668 leveldb::Status status = |
| 675 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); | 669 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); |
| 676 if (status.ok()) { | 670 if (status.ok()) { |
| 677 int64 temp; | 671 int64 temp; |
| 678 if (!base::StringToInt64(int_string, &temp)) { | 672 if (!base::StringToInt64(int_string, &temp)) { |
| 679 LOG(ERROR) << "Hit database corruption!"; | 673 LOG(ERROR) << "Hit database corruption!"; |
| 680 return false; | 674 return false; |
| 681 } | 675 } |
| 682 ++temp; | 676 ++temp; |
| 683 status = db_->Put(leveldb::WriteOptions(), LastIntegerKey(), | 677 status = db_->Put( |
| 684 base::Int64ToString(temp)); | 678 leveldb::WriteOptions(), LastIntegerKey(), base::Int64ToString(temp)); |
| 685 if (!status.ok()) { | 679 if (!status.ok()) { |
| 686 HandleError(FROM_HERE, status); | 680 HandleError(FROM_HERE, status); |
| 687 return false; | 681 return false; |
| 688 } | 682 } |
| 689 *next = temp; | 683 *next = temp; |
| 690 return true; | 684 return true; |
| 691 } | 685 } |
| 692 if (!status.IsNotFound()) { | 686 if (!status.IsNotFound()) { |
| 693 HandleError(FROM_HERE, status); | 687 HandleError(FROM_HERE, status); |
| 694 return false; | 688 return false; |
| 695 } | 689 } |
| 696 // The database must not yet exist; initialize it. | 690 // The database must not yet exist; initialize it. |
| 697 if (!StoreDefaultValues()) | 691 if (!StoreDefaultValues()) |
| 698 return false; | 692 return false; |
| 699 | 693 |
| 700 return GetNextInteger(next); | 694 return GetNextInteger(next); |
| 701 } | 695 } |
| 702 | 696 |
| 703 // static | 697 // static |
| 704 bool SandboxDirectoryDatabase::DestroyDatabase(const base::FilePath& path, | 698 bool SandboxDirectoryDatabase::DestroyDatabase(const base::FilePath& path, |
| 705 leveldb::Env* env_override) { | 699 leveldb::Env* env_override) { |
| 706 std::string name = FilePathToString(path.Append(kDirectoryDatabaseName)); | 700 std::string name = FilePathToString(path.Append(kDirectoryDatabaseName)); |
| 707 leveldb::Options options; | 701 leveldb::Options options; |
| 708 if (env_override) | 702 if (env_override) |
| 709 options.env = env_override; | 703 options.env = env_override; |
| 710 leveldb::Status status = leveldb::DestroyDB(name, options); | 704 leveldb::Status status = leveldb::DestroyDB(name, options); |
| 711 if (status.ok()) | 705 if (status.ok()) |
| 712 return true; | 706 return true; |
| 713 LOG(WARNING) << "Failed to destroy a database with status " << | 707 LOG(WARNING) << "Failed to destroy a database with status " |
| 714 status.ToString(); | 708 << status.ToString(); |
| 715 return false; | 709 return false; |
| 716 } | 710 } |
| 717 | 711 |
| 718 bool SandboxDirectoryDatabase::Init(RecoveryOption recovery_option) { | 712 bool SandboxDirectoryDatabase::Init(RecoveryOption recovery_option) { |
| 719 if (db_) | 713 if (db_) |
| 720 return true; | 714 return true; |
| 721 | 715 |
| 722 std::string path = | 716 std::string path = FilePathToString( |
| 723 FilePathToString(filesystem_data_directory_.Append( | 717 filesystem_data_directory_.Append(kDirectoryDatabaseName)); |
| 724 kDirectoryDatabaseName)); | |
| 725 leveldb::Options options; | 718 leveldb::Options options; |
| 726 options.max_open_files = 0; // Use minimum. | 719 options.max_open_files = 0; // Use minimum. |
| 727 options.create_if_missing = true; | 720 options.create_if_missing = true; |
| 728 if (env_override_) | 721 if (env_override_) |
| 729 options.env = env_override_; | 722 options.env = env_override_; |
| 730 leveldb::DB* db; | 723 leveldb::DB* db; |
| 731 leveldb::Status status = leveldb::DB::Open(options, path, &db); | 724 leveldb::Status status = leveldb::DB::Open(options, path, &db); |
| 732 ReportInitStatus(status); | 725 ReportInitStatus(status); |
| 733 if (status.ok()) { | 726 if (status.ok()) { |
| 734 db_.reset(db); | 727 db_.reset(db); |
| 735 return true; | 728 return true; |
| 736 } | 729 } |
| 737 HandleError(FROM_HERE, status); | 730 HandleError(FROM_HERE, status); |
| 738 | 731 |
| 739 // Corruption due to missing necessary MANIFEST-* file causes IOError instead | 732 // Corruption due to missing necessary MANIFEST-* file causes IOError instead |
| 740 // of Corruption error. | 733 // of Corruption error. |
| 741 // Try to repair database even when IOError case. | 734 // Try to repair database even when IOError case. |
| 742 if (!status.IsCorruption() && !status.IsIOError()) | 735 if (!status.IsCorruption() && !status.IsIOError()) |
| 743 return false; | 736 return false; |
| 744 | 737 |
| 745 switch (recovery_option) { | 738 switch (recovery_option) { |
| 746 case FAIL_ON_CORRUPTION: | 739 case FAIL_ON_CORRUPTION: |
| 747 return false; | 740 return false; |
| 748 case REPAIR_ON_CORRUPTION: | 741 case REPAIR_ON_CORRUPTION: |
| 749 LOG(WARNING) << "Corrupted SandboxDirectoryDatabase detected." | 742 LOG(WARNING) << "Corrupted SandboxDirectoryDatabase detected." |
| 750 << " Attempting to repair."; | 743 << " Attempting to repair."; |
| 751 if (RepairDatabase(path)) { | 744 if (RepairDatabase(path)) { |
| 752 UMA_HISTOGRAM_ENUMERATION(kDatabaseRepairHistogramLabel, | 745 UMA_HISTOGRAM_ENUMERATION( |
| 753 DB_REPAIR_SUCCEEDED, DB_REPAIR_MAX); | 746 kDatabaseRepairHistogramLabel, DB_REPAIR_SUCCEEDED, DB_REPAIR_MAX); |
| 754 return true; | 747 return true; |
| 755 } | 748 } |
| 756 UMA_HISTOGRAM_ENUMERATION(kDatabaseRepairHistogramLabel, | 749 UMA_HISTOGRAM_ENUMERATION( |
| 757 DB_REPAIR_FAILED, DB_REPAIR_MAX); | 750 kDatabaseRepairHistogramLabel, DB_REPAIR_FAILED, DB_REPAIR_MAX); |
| 758 LOG(WARNING) << "Failed to repair SandboxDirectoryDatabase."; | 751 LOG(WARNING) << "Failed to repair SandboxDirectoryDatabase."; |
| 759 // fall through | 752 // fall through |
| 760 case DELETE_ON_CORRUPTION: | 753 case DELETE_ON_CORRUPTION: |
| 761 LOG(WARNING) << "Clearing SandboxDirectoryDatabase."; | 754 LOG(WARNING) << "Clearing SandboxDirectoryDatabase."; |
| 762 if (!base::DeleteFile(filesystem_data_directory_, true)) | 755 if (!base::DeleteFile(filesystem_data_directory_, true)) |
| 763 return false; | 756 return false; |
| 764 if (!base::CreateDirectory(filesystem_data_directory_)) | 757 if (!base::CreateDirectory(filesystem_data_directory_)) |
| 765 return false; | 758 return false; |
| 766 return Init(FAIL_ON_CORRUPTION); | 759 return Init(FAIL_ON_CORRUPTION); |
| 767 } | 760 } |
| 768 | 761 |
| 769 NOTREACHED(); | 762 NOTREACHED(); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 797 return true; | 790 return true; |
| 798 } | 791 } |
| 799 | 792 |
| 800 bool SandboxDirectoryDatabase::IsFileSystemConsistent() { | 793 bool SandboxDirectoryDatabase::IsFileSystemConsistent() { |
| 801 if (!Init(FAIL_ON_CORRUPTION)) | 794 if (!Init(FAIL_ON_CORRUPTION)) |
| 802 return false; | 795 return false; |
| 803 DatabaseCheckHelper helper(this, db_.get(), filesystem_data_directory_); | 796 DatabaseCheckHelper helper(this, db_.get(), filesystem_data_directory_); |
| 804 return helper.IsFileSystemConsistent(); | 797 return helper.IsFileSystemConsistent(); |
| 805 } | 798 } |
| 806 | 799 |
| 807 void SandboxDirectoryDatabase::ReportInitStatus( | 800 void SandboxDirectoryDatabase::ReportInitStatus(const leveldb::Status& status) { |
| 808 const leveldb::Status& status) { | |
| 809 base::Time now = base::Time::Now(); | 801 base::Time now = base::Time::Now(); |
| 810 const base::TimeDelta minimum_interval = | 802 const base::TimeDelta minimum_interval = |
| 811 base::TimeDelta::FromHours(kMinimumReportIntervalHours); | 803 base::TimeDelta::FromHours(kMinimumReportIntervalHours); |
| 812 if (last_reported_time_ + minimum_interval >= now) | 804 if (last_reported_time_ + minimum_interval >= now) |
| 813 return; | 805 return; |
| 814 last_reported_time_ = now; | 806 last_reported_time_ = now; |
| 815 | 807 |
| 816 if (status.ok()) { | 808 if (status.ok()) { |
| 817 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel, | 809 UMA_HISTOGRAM_ENUMERATION( |
| 818 INIT_STATUS_OK, INIT_STATUS_MAX); | 810 kInitStatusHistogramLabel, INIT_STATUS_OK, INIT_STATUS_MAX); |
| 819 } else if (status.IsCorruption()) { | 811 } else if (status.IsCorruption()) { |
| 820 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel, | 812 UMA_HISTOGRAM_ENUMERATION( |
| 821 INIT_STATUS_CORRUPTION, INIT_STATUS_MAX); | 813 kInitStatusHistogramLabel, INIT_STATUS_CORRUPTION, INIT_STATUS_MAX); |
| 822 } else if (status.IsIOError()) { | 814 } else if (status.IsIOError()) { |
| 823 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel, | 815 UMA_HISTOGRAM_ENUMERATION( |
| 824 INIT_STATUS_IO_ERROR, INIT_STATUS_MAX); | 816 kInitStatusHistogramLabel, INIT_STATUS_IO_ERROR, INIT_STATUS_MAX); |
| 825 } else { | 817 } else { |
| 826 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel, | 818 UMA_HISTOGRAM_ENUMERATION( |
| 827 INIT_STATUS_UNKNOWN_ERROR, INIT_STATUS_MAX); | 819 kInitStatusHistogramLabel, INIT_STATUS_UNKNOWN_ERROR, INIT_STATUS_MAX); |
| 828 } | 820 } |
| 829 } | 821 } |
| 830 | 822 |
| 831 bool SandboxDirectoryDatabase::StoreDefaultValues() { | 823 bool SandboxDirectoryDatabase::StoreDefaultValues() { |
| 832 // Verify that this is a totally new database, and initialize it. | 824 // Verify that this is a totally new database, and initialize it. |
| 833 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); | 825 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); |
| 834 iter->SeekToFirst(); | 826 iter->SeekToFirst(); |
| 835 if (iter->Valid()) { // DB was not empty--we shouldn't have been called. | 827 if (iter->Valid()) { // DB was not empty--we shouldn't have been called. |
| 836 LOG(ERROR) << "File system origin database is corrupt!"; | 828 LOG(ERROR) << "File system origin database is corrupt!"; |
| 837 return false; | 829 return false; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 873 return false; | 865 return false; |
| 874 } | 866 } |
| 875 // The database must not yet exist; initialize it. | 867 // The database must not yet exist; initialize it. |
| 876 if (!StoreDefaultValues()) | 868 if (!StoreDefaultValues()) |
| 877 return false; | 869 return false; |
| 878 *file_id = 0; | 870 *file_id = 0; |
| 879 return true; | 871 return true; |
| 880 } | 872 } |
| 881 | 873 |
| 882 // This does very few safety checks! | 874 // This does very few safety checks! |
| 883 bool SandboxDirectoryDatabase::AddFileInfoHelper( | 875 bool SandboxDirectoryDatabase::AddFileInfoHelper(const FileInfo& info, |
| 884 const FileInfo& info, FileId file_id, leveldb::WriteBatch* batch) { | 876 FileId file_id, |
| 877 leveldb::WriteBatch* batch) { |
| 885 if (!VerifyDataPath(info.data_path)) { | 878 if (!VerifyDataPath(info.data_path)) { |
| 886 LOG(ERROR) << "Invalid data path is given: " << info.data_path.value(); | 879 LOG(ERROR) << "Invalid data path is given: " << info.data_path.value(); |
| 887 return false; | 880 return false; |
| 888 } | 881 } |
| 889 std::string id_string = GetFileLookupKey(file_id); | 882 std::string id_string = GetFileLookupKey(file_id); |
| 890 if (!file_id) { | 883 if (!file_id) { |
| 891 // The root directory doesn't need to be looked up by path from its parent. | 884 // The root directory doesn't need to be looked up by path from its parent. |
| 892 DCHECK(!info.parent_id); | 885 DCHECK(!info.parent_id); |
| 893 DCHECK(info.data_path.empty()); | 886 DCHECK(info.data_path.empty()); |
| 894 } else { | 887 } else { |
| 895 std::string child_key = GetChildLookupKey(info.parent_id, info.name); | 888 std::string child_key = GetChildLookupKey(info.parent_id, info.name); |
| 896 batch->Put(child_key, id_string); | 889 batch->Put(child_key, id_string); |
| 897 } | 890 } |
| 898 Pickle pickle; | 891 Pickle pickle; |
| 899 if (!PickleFromFileInfo(info, &pickle)) | 892 if (!PickleFromFileInfo(info, &pickle)) |
| 900 return false; | 893 return false; |
| 901 batch->Put( | 894 batch->Put(id_string, |
| 902 id_string, | 895 leveldb::Slice(reinterpret_cast<const char*>(pickle.data()), |
| 903 leveldb::Slice(reinterpret_cast<const char *>(pickle.data()), | 896 pickle.size())); |
| 904 pickle.size())); | |
| 905 return true; | 897 return true; |
| 906 } | 898 } |
| 907 | 899 |
| 908 // This does very few safety checks! | 900 // This does very few safety checks! |
| 909 bool SandboxDirectoryDatabase::RemoveFileInfoHelper( | 901 bool SandboxDirectoryDatabase::RemoveFileInfoHelper( |
| 910 FileId file_id, leveldb::WriteBatch* batch) { | 902 FileId file_id, |
| 903 leveldb::WriteBatch* batch) { |
| 911 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. | 904 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. |
| 912 FileInfo info; | 905 FileInfo info; |
| 913 if (!GetFileInfo(file_id, &info)) | 906 if (!GetFileInfo(file_id, &info)) |
| 914 return false; | 907 return false; |
| 915 if (info.data_path.empty()) { // It's a directory | 908 if (info.data_path.empty()) { // It's a directory |
| 916 std::vector<FileId> children; | 909 std::vector<FileId> children; |
| 917 // TODO(ericu): Make a faster is-the-directory-empty check. | 910 // TODO(ericu): Make a faster is-the-directory-empty check. |
| 918 if (!ListChildren(file_id, &children)) | 911 if (!ListChildren(file_id, &children)) |
| 919 return false; | 912 return false; |
| 920 if (children.size()) { | 913 if (children.size()) { |
| 921 LOG(ERROR) << "Can't remove a directory with children."; | 914 LOG(ERROR) << "Can't remove a directory with children."; |
| 922 return false; | 915 return false; |
| 923 } | 916 } |
| 924 } | 917 } |
| 925 batch->Delete(GetChildLookupKey(info.parent_id, info.name)); | 918 batch->Delete(GetChildLookupKey(info.parent_id, info.name)); |
| 926 batch->Delete(GetFileLookupKey(file_id)); | 919 batch->Delete(GetFileLookupKey(file_id)); |
| 927 return true; | 920 return true; |
| 928 } | 921 } |
| 929 | 922 |
| 930 void SandboxDirectoryDatabase::HandleError( | 923 void SandboxDirectoryDatabase::HandleError( |
| 931 const tracked_objects::Location& from_here, | 924 const tracked_objects::Location& from_here, |
| 932 const leveldb::Status& status) { | 925 const leveldb::Status& status) { |
| 933 LOG(ERROR) << "SandboxDirectoryDatabase failed at: " | 926 LOG(ERROR) << "SandboxDirectoryDatabase failed at: " << from_here.ToString() |
| 934 << from_here.ToString() << " with error: " << status.ToString(); | 927 << " with error: " << status.ToString(); |
| 935 db_.reset(); | 928 db_.reset(); |
| 936 } | 929 } |
| 937 | 930 |
| 938 } // namespace fileapi | 931 } // namespace storage |
| OLD | NEW |