OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "webkit/fileapi/obfuscated_file_system_file_util.h" | |
6 | |
7 #include <queue> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/file_util.h" | |
12 #include "base/format_macros.h" | |
13 #include "base/logging.h" | |
14 #include "base/message_loop.h" | |
15 #include "base/stl_util.h" | |
16 #include "base/string_number_conversions.h" | |
17 #include "base/stringprintf.h" | |
18 #include "base/sys_string_conversions.h" | |
19 #include "googleurl/src/gurl.h" | |
20 #include "webkit/fileapi/file_system_context.h" | |
21 #include "webkit/fileapi/file_system_operation_context.h" | |
22 #include "webkit/fileapi/file_system_path_manager.h" | |
23 #include "webkit/fileapi/file_system_quota_util.h" | |
24 #include "webkit/fileapi/file_system_util.h" | |
25 #include "webkit/fileapi/sandbox_mount_point_provider.h" | |
26 #include "webkit/quota/quota_manager.h" | |
27 | |
28 namespace { | |
29 | |
30 const int64 kFlushDelaySeconds = 10 * 60; // 10 minutes | |
31 | |
32 const char kOriginDatabaseName[] = "Origins"; | |
33 const char kDirectoryDatabaseName[] = "Paths"; | |
34 | |
35 void InitFileInfo( | |
36 fileapi::FileSystemDirectoryDatabase::FileInfo* file_info, | |
37 fileapi::FileSystemDirectoryDatabase::FileId parent_id, | |
38 const FilePath::StringType& file_name) { | |
39 DCHECK(file_info); | |
40 file_info->parent_id = parent_id; | |
41 file_info->name = file_name; | |
42 } | |
43 | |
44 bool IsRootDirectory(const FilePath& virtual_path) { | |
45 return (virtual_path.empty() || | |
46 virtual_path.value() == FILE_PATH_LITERAL("/")); | |
47 } | |
48 | |
49 // Costs computed as per crbug.com/86114, based on the LevelDB implementation of | |
50 // path storage under Linux. It's not clear if that will differ on Windows, on | |
51 // which FilePath uses wide chars [since they're converted to UTF-8 for storage | |
52 // anyway], but as long as the cost is high enough that one can't cheat on quota | |
53 // by storing data in paths, it doesn't need to be all that accurate. | |
54 const int64 kPathCreationQuotaCost = 146; // Bytes per inode, basically. | |
55 const int64 kPathByteQuotaCost = 2; // Bytes per byte of path length in UTF-8. | |
56 | |
57 int64 GetPathQuotaUsage( | |
58 int growth_in_number_of_paths, | |
59 int64 growth_in_bytes_of_path_length) { | |
60 return growth_in_number_of_paths * kPathCreationQuotaCost + | |
61 growth_in_bytes_of_path_length * kPathByteQuotaCost; | |
62 } | |
63 | |
64 bool AllocateQuotaForPath( | |
65 fileapi::FileSystemOperationContext* context, | |
66 int growth_in_number_of_paths, | |
67 int64 growth_in_bytes_of_path_length) { | |
68 int64 growth = GetPathQuotaUsage(growth_in_number_of_paths, | |
69 growth_in_bytes_of_path_length); | |
70 int64 new_quota = context->allowed_bytes_growth() - growth; | |
71 | |
72 if (growth <= 0 || new_quota >= 0) { | |
73 context->set_allowed_bytes_growth(new_quota); | |
74 return true; | |
75 } | |
76 return false; | |
77 } | |
78 | |
79 void UpdatePathQuotaUsage( | |
80 fileapi::FileSystemOperationContext* context, | |
81 const GURL& origin_url, | |
82 fileapi::FileSystemType type, | |
83 int growth_in_number_of_paths, // -1, 0, or 1 | |
84 int64 growth_in_bytes_of_path_length) { | |
85 int64 growth = GetPathQuotaUsage(growth_in_number_of_paths, | |
86 growth_in_bytes_of_path_length); | |
87 fileapi::FileSystemQuotaUtil* quota_util = | |
88 context->file_system_context()->GetQuotaUtil(type); | |
89 quota::QuotaManagerProxy* quota_manager_proxy = | |
90 context->file_system_context()->quota_manager_proxy(); | |
91 quota_util->UpdateOriginUsageOnFileThread(quota_manager_proxy, origin_url, | |
92 type, growth); | |
93 } | |
94 | |
95 const FilePath::CharType kLegacyDataDirectory[] = FILE_PATH_LITERAL("Legacy"); | |
96 | |
97 const FilePath::CharType kTemporaryDirectoryName[] = FILE_PATH_LITERAL("t"); | |
98 const FilePath::CharType kPersistentDirectoryName[] = FILE_PATH_LITERAL("p"); | |
99 | |
100 } // namespace | |
101 | |
102 namespace fileapi { | |
103 | |
104 using base::PlatformFile; | |
105 using base::PlatformFileError; | |
106 | |
107 ObfuscatedFileSystemFileUtil::ObfuscatedFileSystemFileUtil( | |
108 const FilePath& file_system_directory, | |
109 FileSystemFileUtil* underlying_file_util) | |
110 : file_system_directory_(file_system_directory), | |
111 underlying_file_util_(underlying_file_util) { | |
112 } | |
113 | |
114 ObfuscatedFileSystemFileUtil::~ObfuscatedFileSystemFileUtil() { | |
115 DropDatabases(); | |
116 } | |
117 | |
118 PlatformFileError ObfuscatedFileSystemFileUtil::CreateOrOpen( | |
119 FileSystemOperationContext* context, | |
120 const FilePath& virtual_path, int file_flags, | |
121 PlatformFile* file_handle, bool* created) { | |
122 DCHECK(!(file_flags & (base::PLATFORM_FILE_DELETE_ON_CLOSE | | |
123 base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_EXCLUSIVE_READ | | |
124 base::PLATFORM_FILE_EXCLUSIVE_WRITE))); | |
125 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
126 context->src_origin_url(), context->src_type(), true); | |
127 if (!db) | |
128 return base::PLATFORM_FILE_ERROR_FAILED; | |
129 FileId file_id; | |
130 if (!db->GetFileWithPath(virtual_path, &file_id)) { | |
131 // The file doesn't exist. | |
132 if (!(file_flags & (base::PLATFORM_FILE_CREATE | | |
133 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_OPEN_ALWAYS))) | |
134 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
135 FileId parent_id; | |
136 if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id)) | |
137 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
138 FileInfo file_info; | |
139 InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value()); | |
140 if (!AllocateQuotaForPath(context, 1, file_info.name.size())) | |
141 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
142 PlatformFileError error = CreateFile( | |
143 context, context->src_origin_url(), context->src_type(), FilePath(), | |
144 &file_info, file_flags, file_handle); | |
145 if (created && base::PLATFORM_FILE_OK == error) | |
146 *created = true; | |
147 return error; | |
148 } | |
149 if (file_flags & base::PLATFORM_FILE_CREATE) | |
150 return base::PLATFORM_FILE_ERROR_EXISTS; | |
151 | |
152 FileInfo file_info; | |
153 if (!db->GetFileInfo(file_id, &file_info)) { | |
154 NOTREACHED(); | |
155 return base::PLATFORM_FILE_ERROR_FAILED; | |
156 } | |
157 if (file_info.is_directory()) | |
158 return base::PLATFORM_FILE_ERROR_NOT_A_FILE; | |
159 FilePath local_path = DataPathToLocalPath(context->src_origin_url(), | |
160 context->src_type(), file_info.data_path); | |
161 base::PlatformFileError error = underlying_file_util_->CreateOrOpen( | |
162 context, local_path, file_flags, file_handle, created); | |
163 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) { | |
164 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker. | |
165 // TODO(tzik): Delete database entry after ensuring the file lost. | |
166 context->file_system_context()->GetQuotaUtil(context->src_type())-> | |
167 InvalidateUsageCache(context->src_origin_url(), | |
168 context->src_type()); | |
169 LOG(WARNING) << "Lost a backing file."; | |
170 error = base::PLATFORM_FILE_ERROR_FAILED; | |
171 } | |
172 return error; | |
173 } | |
174 | |
175 PlatformFileError ObfuscatedFileSystemFileUtil::EnsureFileExists( | |
176 FileSystemOperationContext* context, | |
177 const FilePath& virtual_path, | |
178 bool* created) { | |
179 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
180 context->src_origin_url(), context->src_type(), true); | |
181 if (!db) | |
182 return base::PLATFORM_FILE_ERROR_FAILED; | |
183 FileId file_id; | |
184 if (db->GetFileWithPath(virtual_path, &file_id)) { | |
185 FileInfo file_info; | |
186 if (!db->GetFileInfo(file_id, &file_info)) { | |
187 NOTREACHED(); | |
188 return base::PLATFORM_FILE_ERROR_FAILED; | |
189 } | |
190 if (file_info.is_directory()) | |
191 return base::PLATFORM_FILE_ERROR_NOT_A_FILE; | |
192 if (created) | |
193 *created = false; | |
194 return base::PLATFORM_FILE_OK; | |
195 } | |
196 FileId parent_id; | |
197 if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id)) | |
198 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
199 | |
200 FileInfo file_info; | |
201 InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value()); | |
202 if (!AllocateQuotaForPath(context, 1, file_info.name.size())) | |
203 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
204 PlatformFileError error = CreateFile(context, context->src_origin_url(), | |
205 context->src_type(), FilePath(), &file_info, 0, NULL); | |
206 if (created && base::PLATFORM_FILE_OK == error) | |
207 *created = true; | |
208 return error; | |
209 } | |
210 | |
211 PlatformFileError ObfuscatedFileSystemFileUtil::GetLocalFilePath( | |
212 FileSystemOperationContext* context, | |
213 const FilePath& virtual_path, | |
214 FilePath* local_path) { | |
215 FilePath path = | |
216 GetLocalPath(context->src_origin_url(), context->src_type(), | |
217 virtual_path); | |
218 if (path.empty()) | |
219 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
220 | |
221 *local_path = path; | |
222 return base::PLATFORM_FILE_OK; | |
223 } | |
224 | |
225 PlatformFileError ObfuscatedFileSystemFileUtil::GetFileInfo( | |
226 FileSystemOperationContext* context, | |
227 const FilePath& virtual_path, | |
228 base::PlatformFileInfo* file_info, | |
229 FilePath* platform_file_path) { | |
230 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
231 context->src_origin_url(), context->src_type(), false); | |
232 if (!db) | |
233 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
234 FileId file_id; | |
235 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
236 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
237 FileInfo local_info; | |
238 return GetFileInfoInternal(db, context, file_id, | |
239 &local_info, file_info, platform_file_path); | |
240 } | |
241 | |
242 PlatformFileError ObfuscatedFileSystemFileUtil::ReadDirectory( | |
243 FileSystemOperationContext* context, | |
244 const FilePath& virtual_path, | |
245 std::vector<base::FileUtilProxy::Entry>* entries) { | |
246 // TODO(kkanetkar): Implement directory read in multiple chunks. | |
247 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
248 context->src_origin_url(), context->src_type(), false); | |
249 if (!db) { | |
250 if (IsRootDirectory(virtual_path)) { | |
251 // It's the root directory and the database hasn't been initialized yet. | |
252 entries->clear(); | |
253 return base::PLATFORM_FILE_OK; | |
254 } | |
255 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
256 } | |
257 FileId file_id; | |
258 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
259 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
260 FileInfo file_info; | |
261 if (!db->GetFileInfo(file_id, &file_info)) { | |
262 DCHECK(!file_id); | |
263 // It's the root directory and the database hasn't been initialized yet. | |
264 entries->clear(); | |
265 return base::PLATFORM_FILE_OK; | |
266 } | |
267 if (!file_info.is_directory()) | |
268 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
269 std::vector<FileId> children; | |
270 if (!db->ListChildren(file_id, &children)) { | |
271 NOTREACHED(); | |
272 return base::PLATFORM_FILE_ERROR_FAILED; | |
273 } | |
274 std::vector<FileId>::iterator iter; | |
275 for (iter = children.begin(); iter != children.end(); ++iter) { | |
276 base::PlatformFileInfo platform_file_info; | |
277 FilePath file_path; | |
278 if (GetFileInfoInternal(db, context, *iter, | |
279 &file_info, &platform_file_info, &file_path) != | |
280 base::PLATFORM_FILE_OK) { | |
281 LOG(WARNING) << "Lost a backing file."; | |
282 // TODO(tzik): We found a file entry in directory database without | |
283 // backing file here. Track the inconsistency and remove the database | |
284 // entry if we can't recover it. | |
285 continue; | |
286 } | |
287 | |
288 base::FileUtilProxy::Entry entry; | |
289 entry.name = file_info.name; | |
290 entry.is_directory = file_info.is_directory(); | |
291 entry.size = entry.is_directory ? 0 : platform_file_info.size; | |
292 entry.last_modified_time = platform_file_info.last_modified; | |
293 entries->push_back(entry); | |
294 } | |
295 return base::PLATFORM_FILE_OK; | |
296 } | |
297 | |
298 PlatformFileError ObfuscatedFileSystemFileUtil::CreateDirectory( | |
299 FileSystemOperationContext* context, | |
300 const FilePath& virtual_path, | |
301 bool exclusive, | |
302 bool recursive) { | |
303 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
304 context->src_origin_url(), context->src_type(), true); | |
305 if (!db) | |
306 return base::PLATFORM_FILE_ERROR_FAILED; | |
307 FileId file_id; | |
308 if (db->GetFileWithPath(virtual_path, &file_id)) { | |
309 FileInfo file_info; | |
310 if (exclusive) | |
311 return base::PLATFORM_FILE_ERROR_EXISTS; | |
312 if (!db->GetFileInfo(file_id, &file_info)) { | |
313 NOTREACHED(); | |
314 return base::PLATFORM_FILE_ERROR_FAILED; | |
315 } | |
316 if (!file_info.is_directory()) | |
317 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; | |
318 return base::PLATFORM_FILE_OK; | |
319 } | |
320 | |
321 std::vector<FilePath::StringType> components; | |
322 virtual_path.GetComponents(&components); | |
323 FileId parent_id = 0; | |
324 size_t index; | |
325 for (index = 0; index < components.size(); ++index) { | |
326 FilePath::StringType name = components[index]; | |
327 if (name == FILE_PATH_LITERAL("/")) | |
328 continue; | |
329 if (!db->GetChildWithName(parent_id, name, &parent_id)) | |
330 break; | |
331 } | |
332 if (!recursive && components.size() - index > 1) | |
333 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
334 for (; index < components.size(); ++index) { | |
335 FileInfo file_info; | |
336 file_info.name = components[index]; | |
337 if (file_info.name == FILE_PATH_LITERAL("/")) | |
338 continue; | |
339 file_info.modification_time = base::Time::Now(); | |
340 file_info.parent_id = parent_id; | |
341 if (!AllocateQuotaForPath(context, 1, file_info.name.size())) | |
342 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
343 if (!db->AddFileInfo(file_info, &parent_id)) { | |
344 NOTREACHED(); | |
345 return base::PLATFORM_FILE_ERROR_FAILED; | |
346 } | |
347 UpdatePathQuotaUsage(context, context->src_origin_url(), | |
348 context->src_type(), 1, file_info.name.size()); | |
349 } | |
350 return base::PLATFORM_FILE_OK; | |
351 } | |
352 | |
353 PlatformFileError ObfuscatedFileSystemFileUtil::CopyOrMoveFile( | |
354 FileSystemOperationContext* context, | |
355 const FilePath& src_file_path, | |
356 const FilePath& dest_file_path, | |
357 bool copy) { | |
358 // Cross-filesystem copies and moves should be handled via CopyInForeignFile. | |
359 DCHECK(context->src_origin_url() == context->dest_origin_url()); | |
360 DCHECK(context->src_type() == context->dest_type()); | |
361 | |
362 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
363 context->src_origin_url(), context->src_type(), true); | |
364 if (!db) | |
365 return base::PLATFORM_FILE_ERROR_FAILED; | |
366 FileId src_file_id; | |
367 if (!db->GetFileWithPath(src_file_path, &src_file_id)) | |
368 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
369 FileId dest_file_id; | |
370 bool overwrite = db->GetFileWithPath(dest_file_path, &dest_file_id); | |
371 FileInfo src_file_info; | |
372 FileInfo dest_file_info; | |
373 if (!db->GetFileInfo(src_file_id, &src_file_info) || | |
374 src_file_info.is_directory()) { | |
375 NOTREACHED(); | |
376 return base::PLATFORM_FILE_ERROR_FAILED; | |
377 } | |
378 if (overwrite) { | |
379 if (!db->GetFileInfo(dest_file_id, &dest_file_info) || | |
380 dest_file_info.is_directory()) { | |
381 NOTREACHED(); | |
382 return base::PLATFORM_FILE_ERROR_FAILED; | |
383 } | |
384 } | |
385 /* | |
386 * Copy-with-overwrite | |
387 * Just overwrite data file | |
388 * Copy-without-overwrite | |
389 * Copy backing file | |
390 * Create new metadata pointing to new backing file. | |
391 * Move-with-overwrite | |
392 * transaction: | |
393 * Remove source entry. | |
394 * Point target entry to source entry's backing file. | |
395 * Delete target entry's old backing file | |
396 * Move-without-overwrite | |
397 * Just update metadata | |
398 */ | |
399 if (copy) { | |
400 FilePath src_data_path = DataPathToLocalPath(context->src_origin_url(), | |
401 context->src_type(), src_file_info.data_path); | |
402 if (!underlying_file_util_->PathExists(context, src_data_path)) { | |
403 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker. | |
404 context->file_system_context()->GetQuotaUtil(context->src_type())-> | |
405 InvalidateUsageCache(context->src_origin_url(), | |
406 context->src_type()); | |
407 LOG(WARNING) << "Lost a backing file."; | |
408 return base::PLATFORM_FILE_ERROR_FAILED; | |
409 } | |
410 | |
411 if (overwrite) { | |
412 FilePath dest_data_path = DataPathToLocalPath(context->src_origin_url(), | |
413 context->src_type(), dest_file_info.data_path); | |
414 return underlying_file_util_->CopyOrMoveFile(context, | |
415 src_data_path, dest_data_path, copy); | |
416 } else { | |
417 FileId dest_parent_id; | |
418 if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) { | |
419 NOTREACHED(); // We shouldn't be called in this case. | |
420 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
421 } | |
422 InitFileInfo(&dest_file_info, dest_parent_id, | |
423 dest_file_path.BaseName().value()); | |
424 if (!AllocateQuotaForPath(context, 1, dest_file_info.name.size())) | |
425 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
426 return CreateFile(context, context->dest_origin_url(), | |
427 context->dest_type(), src_data_path, &dest_file_info, 0, | |
428 NULL); | |
429 } | |
430 } else { // It's a move. | |
431 if (overwrite) { | |
432 AllocateQuotaForPath(context, -1, | |
433 -static_cast<int64>(src_file_info.name.size())); | |
434 if (!db->OverwritingMoveFile(src_file_id, dest_file_id)) | |
435 return base::PLATFORM_FILE_ERROR_FAILED; | |
436 FilePath dest_data_path = DataPathToLocalPath(context->src_origin_url(), | |
437 context->src_type(), dest_file_info.data_path); | |
438 if (base::PLATFORM_FILE_OK != | |
439 underlying_file_util_->DeleteFile(context, dest_data_path)) | |
440 LOG(WARNING) << "Leaked a backing file."; | |
441 UpdatePathQuotaUsage(context, context->src_origin_url(), | |
442 context->src_type(), -1, | |
443 -static_cast<int64>(src_file_info.name.size())); | |
444 return base::PLATFORM_FILE_OK; | |
445 } else { | |
446 FileId dest_parent_id; | |
447 if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) { | |
448 NOTREACHED(); | |
449 return base::PLATFORM_FILE_ERROR_FAILED; | |
450 } | |
451 if (!AllocateQuotaForPath( | |
452 context, 0, | |
453 static_cast<int64>(dest_file_path.BaseName().value().size()) | |
454 - static_cast<int64>(src_file_info.name.size()))) | |
455 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
456 src_file_info.parent_id = dest_parent_id; | |
457 src_file_info.name = dest_file_path.BaseName().value(); | |
458 if (!db->UpdateFileInfo(src_file_id, src_file_info)) | |
459 return base::PLATFORM_FILE_ERROR_FAILED; | |
460 UpdatePathQuotaUsage( | |
461 context, context->src_origin_url(), context->src_type(), 0, | |
462 static_cast<int64>(dest_file_path.BaseName().value().size()) - | |
463 static_cast<int64>(src_file_path.BaseName().value().size())); | |
464 return base::PLATFORM_FILE_OK; | |
465 } | |
466 } | |
467 NOTREACHED(); | |
468 return base::PLATFORM_FILE_ERROR_FAILED; | |
469 } | |
470 | |
471 PlatformFileError ObfuscatedFileSystemFileUtil::CopyInForeignFile( | |
472 FileSystemOperationContext* context, | |
473 const FilePath& src_file_path, | |
474 const FilePath& dest_file_path) { | |
475 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
476 context->dest_origin_url(), context->dest_type(), true); | |
477 if (!db) | |
478 return base::PLATFORM_FILE_ERROR_FAILED; | |
479 FileId dest_file_id; | |
480 bool overwrite = db->GetFileWithPath(dest_file_path, &dest_file_id); | |
481 FileInfo dest_file_info; | |
482 if (overwrite) { | |
483 if (!db->GetFileInfo(dest_file_id, &dest_file_info) || | |
484 dest_file_info.is_directory()) { | |
485 NOTREACHED(); | |
486 return base::PLATFORM_FILE_ERROR_FAILED; | |
487 } | |
488 FilePath dest_data_path = DataPathToLocalPath(context->dest_origin_url(), | |
489 context->dest_type(), dest_file_info.data_path); | |
490 return underlying_file_util_->CopyOrMoveFile(context, | |
491 src_file_path, dest_data_path, true /* copy */); | |
492 } else { | |
493 FileId dest_parent_id; | |
494 if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) { | |
495 NOTREACHED(); // We shouldn't be called in this case. | |
496 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
497 } | |
498 InitFileInfo(&dest_file_info, dest_parent_id, | |
499 dest_file_path.BaseName().value()); | |
500 if (!AllocateQuotaForPath(context, 1, dest_file_info.name.size())) | |
501 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
502 return CreateFile(context, context->dest_origin_url(), | |
503 context->dest_type(), src_file_path, &dest_file_info, 0, NULL); | |
504 } | |
505 return base::PLATFORM_FILE_ERROR_FAILED; | |
506 } | |
507 | |
508 PlatformFileError ObfuscatedFileSystemFileUtil::DeleteFile( | |
509 FileSystemOperationContext* context, | |
510 const FilePath& virtual_path) { | |
511 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
512 context->src_origin_url(), context->src_type(), true); | |
513 if (!db) | |
514 return base::PLATFORM_FILE_ERROR_FAILED; | |
515 FileId file_id; | |
516 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
517 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
518 FileInfo file_info; | |
519 if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) { | |
520 NOTREACHED(); | |
521 return base::PLATFORM_FILE_ERROR_FAILED; | |
522 } | |
523 if (!db->RemoveFileInfo(file_id)) { | |
524 NOTREACHED(); | |
525 return base::PLATFORM_FILE_ERROR_FAILED; | |
526 } | |
527 AllocateQuotaForPath(context, -1, -static_cast<int64>(file_info.name.size())); | |
528 UpdatePathQuotaUsage(context, context->src_origin_url(), context->src_type(), | |
529 -1, -static_cast<int64>(file_info.name.size())); | |
530 FilePath data_path = DataPathToLocalPath(context->src_origin_url(), | |
531 context->src_type(), file_info.data_path); | |
532 if (base::PLATFORM_FILE_OK != | |
533 underlying_file_util_->DeleteFile(context, data_path)) | |
534 LOG(WARNING) << "Leaked a backing file."; | |
535 return base::PLATFORM_FILE_OK; | |
536 } | |
537 | |
538 PlatformFileError ObfuscatedFileSystemFileUtil::DeleteSingleDirectory( | |
539 FileSystemOperationContext* context, | |
540 const FilePath& virtual_path) { | |
541 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
542 context->src_origin_url(), context->src_type(), true); | |
543 if (!db) | |
544 return base::PLATFORM_FILE_ERROR_FAILED; | |
545 FileId file_id; | |
546 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
547 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
548 FileInfo file_info; | |
549 if (!db->GetFileInfo(file_id, &file_info) || !file_info.is_directory()) { | |
550 NOTREACHED(); | |
551 return base::PLATFORM_FILE_ERROR_FAILED; | |
552 } | |
553 if (!db->RemoveFileInfo(file_id)) | |
554 return base::PLATFORM_FILE_ERROR_NOT_EMPTY; | |
555 AllocateQuotaForPath(context, -1, -static_cast<int64>(file_info.name.size())); | |
556 UpdatePathQuotaUsage(context, context->src_origin_url(), context->src_type(), | |
557 -1, -static_cast<int64>(file_info.name.size())); | |
558 return base::PLATFORM_FILE_OK; | |
559 } | |
560 | |
561 PlatformFileError ObfuscatedFileSystemFileUtil::Touch( | |
562 FileSystemOperationContext* context, | |
563 const FilePath& virtual_path, | |
564 const base::Time& last_access_time, | |
565 const base::Time& last_modified_time) { | |
566 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
567 context->src_origin_url(), context->src_type(), false); | |
568 if (!db) | |
569 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
570 FileId file_id; | |
571 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
572 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
573 | |
574 FileInfo file_info; | |
575 if (!db->GetFileInfo(file_id, &file_info)) { | |
576 NOTREACHED(); | |
577 return base::PLATFORM_FILE_ERROR_FAILED; | |
578 } | |
579 if (file_info.is_directory()) { | |
580 file_info.modification_time = last_modified_time; | |
581 if (!db->UpdateFileInfo(file_id, file_info)) | |
582 return base::PLATFORM_FILE_ERROR_FAILED; | |
583 return base::PLATFORM_FILE_OK; | |
584 } | |
585 FilePath data_path = DataPathToLocalPath(context->src_origin_url(), | |
586 context->src_type(), file_info.data_path); | |
587 return underlying_file_util_->Touch( | |
588 context, data_path, last_access_time, last_modified_time); | |
589 } | |
590 | |
591 PlatformFileError ObfuscatedFileSystemFileUtil::Truncate( | |
592 FileSystemOperationContext* context, | |
593 const FilePath& virtual_path, | |
594 int64 length) { | |
595 FilePath local_path = | |
596 GetLocalPath(context->src_origin_url(), context->src_type(), | |
597 virtual_path); | |
598 if (local_path.empty()) | |
599 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
600 return underlying_file_util_->Truncate( | |
601 context, local_path, length); | |
602 } | |
603 | |
604 bool ObfuscatedFileSystemFileUtil::PathExists( | |
605 FileSystemOperationContext* context, | |
606 const FilePath& virtual_path) { | |
607 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
608 context->src_origin_url(), context->src_type(), false); | |
609 if (!db) | |
610 return false; | |
611 FileId file_id; | |
612 return db->GetFileWithPath(virtual_path, &file_id); | |
613 } | |
614 | |
615 bool ObfuscatedFileSystemFileUtil::DirectoryExists( | |
616 FileSystemOperationContext* context, | |
617 const FilePath& virtual_path) { | |
618 if (IsRootDirectory(virtual_path)) { | |
619 // It's questionable whether we should return true or false for the | |
620 // root directory of nonexistent origin, but here we return true | |
621 // as the current implementation of ReadDirectory always returns an empty | |
622 // array (rather than erroring out with NOT_FOUND_ERR even) for | |
623 // nonexistent origins. | |
624 // Note: if you're going to change this behavior please also consider | |
625 // changiing the ReadDirectory's behavior! | |
626 return true; | |
627 } | |
628 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
629 context->src_origin_url(), context->src_type(), false); | |
630 if (!db) | |
631 return false; | |
632 FileId file_id; | |
633 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
634 return false; | |
635 FileInfo file_info; | |
636 if (!db->GetFileInfo(file_id, &file_info)) { | |
637 NOTREACHED(); | |
638 return false; | |
639 } | |
640 return file_info.is_directory(); | |
641 } | |
642 | |
643 bool ObfuscatedFileSystemFileUtil::IsDirectoryEmpty( | |
644 FileSystemOperationContext* context, | |
645 const FilePath& virtual_path) { | |
646 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
647 context->src_origin_url(), context->src_type(), false); | |
648 if (!db) | |
649 return true; // Not a great answer, but it's what others do. | |
650 FileId file_id; | |
651 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
652 return true; // Ditto. | |
653 FileInfo file_info; | |
654 if (!db->GetFileInfo(file_id, &file_info)) { | |
655 DCHECK(!file_id); | |
656 // It's the root directory and the database hasn't been initialized yet. | |
657 return true; | |
658 } | |
659 if (!file_info.is_directory()) | |
660 return true; | |
661 std::vector<FileId> children; | |
662 // TODO(ericu): This could easily be made faster with help from the database. | |
663 if (!db->ListChildren(file_id, &children)) | |
664 return true; | |
665 return children.empty(); | |
666 } | |
667 | |
668 class ObfuscatedFileSystemFileEnumerator | |
669 : public FileSystemFileUtil::AbstractFileEnumerator { | |
670 public: | |
671 ObfuscatedFileSystemFileEnumerator( | |
672 const FilePath& base_path, | |
673 FileSystemDirectoryDatabase* db, | |
674 FileSystemOperationContext* context, | |
675 FileSystemFileUtil* underlying_file_util, | |
676 const FilePath& virtual_root_path) | |
677 : base_path_(base_path), | |
678 db_(db), | |
679 context_(context), | |
680 underlying_file_util_(underlying_file_util) { | |
681 DCHECK(db_); | |
682 DCHECK(context_); | |
683 DCHECK(underlying_file_util_); | |
684 | |
685 FileId file_id; | |
686 FileInfo file_info; | |
687 if (!db_->GetFileWithPath(virtual_root_path, &file_id)) | |
688 return; | |
689 if (!db_->GetFileInfo(file_id, &file_info)) | |
690 return; | |
691 if (!file_info.is_directory()) | |
692 return; | |
693 FileRecord record = { file_id, file_info, virtual_root_path }; | |
694 display_queue_.push(record); | |
695 Next(); // Enumerators don't include the directory itself. | |
696 } | |
697 | |
698 ~ObfuscatedFileSystemFileEnumerator() {} | |
699 | |
700 virtual FilePath Next() OVERRIDE { | |
701 ProcessRecurseQueue(); | |
702 if (display_queue_.empty()) | |
703 return FilePath(); | |
704 current_ = display_queue_.front(); | |
705 display_queue_.pop(); | |
706 if (current_.file_info.is_directory()) | |
707 recurse_queue_.push(current_); | |
708 return current_.file_path; | |
709 } | |
710 | |
711 virtual int64 Size() OVERRIDE { | |
712 if (IsDirectory()) | |
713 return 0; | |
714 | |
715 base::PlatformFileInfo file_info; | |
716 FilePath platform_file_path; | |
717 | |
718 FilePath local_path = base_path_.Append(current_.file_info.data_path); | |
719 base::PlatformFileError error = underlying_file_util_->GetFileInfo( | |
720 context_, local_path, &file_info, &platform_file_path); | |
721 if (error != base::PLATFORM_FILE_OK) { | |
722 LOG(WARNING) << "Lost a backing file."; | |
723 return 0; | |
724 } | |
725 return file_info.size; | |
726 } | |
727 | |
728 virtual bool IsDirectory() OVERRIDE { | |
729 return current_.file_info.is_directory(); | |
730 } | |
731 | |
732 private: | |
733 typedef FileSystemDirectoryDatabase::FileId FileId; | |
734 typedef FileSystemDirectoryDatabase::FileInfo FileInfo; | |
735 | |
736 struct FileRecord { | |
737 FileId file_id; | |
738 FileInfo file_info; | |
739 FilePath file_path; | |
740 }; | |
741 | |
742 void ProcessRecurseQueue() { | |
743 while (display_queue_.empty() && !recurse_queue_.empty()) { | |
744 FileRecord directory = recurse_queue_.front(); | |
745 std::vector<FileId> children; | |
746 recurse_queue_.pop(); | |
747 if (!db_->ListChildren(directory.file_id, &children)) | |
748 return; | |
749 std::vector<FileId>::iterator iter; | |
750 for (iter = children.begin(); iter != children.end(); ++iter) { | |
751 FileRecord child; | |
752 child.file_id = *iter; | |
753 if (!db_->GetFileInfo(child.file_id, &child.file_info)) | |
754 return; | |
755 child.file_path = directory.file_path.Append(child.file_info.name); | |
756 display_queue_.push(child); | |
757 } | |
758 } | |
759 } | |
760 | |
761 std::queue<FileRecord> display_queue_; | |
762 std::queue<FileRecord> recurse_queue_; | |
763 FileRecord current_; | |
764 FilePath base_path_; | |
765 FileSystemDirectoryDatabase* db_; | |
766 FileSystemOperationContext* context_; | |
767 FileSystemFileUtil* underlying_file_util_; | |
768 }; | |
769 | |
770 class ObfuscatedFileSystemOriginEnumerator | |
771 : public ObfuscatedFileSystemFileUtil::AbstractOriginEnumerator { | |
772 public: | |
773 typedef FileSystemOriginDatabase::OriginRecord OriginRecord; | |
774 ObfuscatedFileSystemOriginEnumerator( | |
775 FileSystemOriginDatabase* origin_database, | |
776 const FilePath& base_path) | |
777 : base_path_(base_path) { | |
778 if (origin_database) | |
779 origin_database->ListAllOrigins(&origins_); | |
780 } | |
781 | |
782 ~ObfuscatedFileSystemOriginEnumerator() {} | |
783 | |
784 // Returns the next origin. Returns empty if there are no more origins. | |
785 virtual GURL Next() OVERRIDE { | |
786 OriginRecord record; | |
787 if (!origins_.empty()) { | |
788 record = origins_.back(); | |
789 origins_.pop_back(); | |
790 } | |
791 current_ = record; | |
792 return GetOriginURLFromIdentifier(record.origin); | |
793 } | |
794 | |
795 // Returns the current origin's information. | |
796 virtual bool HasFileSystemType(FileSystemType type) const OVERRIDE { | |
797 if (current_.path.empty()) | |
798 return false; | |
799 FilePath::StringType type_string = | |
800 ObfuscatedFileSystemFileUtil::GetDirectoryNameForType(type); | |
801 if (type_string.empty()) { | |
802 NOTREACHED(); | |
803 return false; | |
804 } | |
805 FilePath path = base_path_.Append(current_.path).Append(type_string); | |
806 return file_util::DirectoryExists(path); | |
807 } | |
808 | |
809 private: | |
810 std::vector<OriginRecord> origins_; | |
811 OriginRecord current_; | |
812 FilePath base_path_; | |
813 }; | |
814 | |
815 ObfuscatedFileSystemFileUtil::AbstractOriginEnumerator* | |
816 ObfuscatedFileSystemFileUtil::CreateOriginEnumerator() { | |
817 std::vector<FileSystemOriginDatabase::OriginRecord> origins; | |
818 | |
819 InitOriginDatabase(false); | |
820 return new ObfuscatedFileSystemOriginEnumerator( | |
821 origin_database_.get(), file_system_directory_); | |
822 } | |
823 | |
824 FileSystemFileUtil::AbstractFileEnumerator* | |
825 ObfuscatedFileSystemFileUtil::CreateFileEnumerator( | |
826 FileSystemOperationContext* context, | |
827 const FilePath& root_path) { | |
828 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
829 context->src_origin_url(), context->src_type(), false); | |
830 if (!db) | |
831 return new FileSystemFileUtil::EmptyFileEnumerator(); | |
832 return new ObfuscatedFileSystemFileEnumerator( | |
833 GetDirectoryForOriginAndType(context->src_origin_url(), | |
834 context->src_type(), false), | |
835 db, | |
836 context, | |
837 underlying_file_util_.get(), | |
838 root_path); | |
839 } | |
840 | |
841 PlatformFileError ObfuscatedFileSystemFileUtil::GetFileInfoInternal( | |
842 FileSystemDirectoryDatabase* db, | |
843 FileSystemOperationContext* context, | |
844 FileId file_id, | |
845 FileInfo* local_info, | |
846 base::PlatformFileInfo* file_info, | |
847 FilePath* platform_file_path) { | |
848 DCHECK(db); | |
849 DCHECK(context); | |
850 DCHECK(file_info); | |
851 DCHECK(platform_file_path); | |
852 | |
853 if (!db->GetFileInfo(file_id, local_info)) { | |
854 NOTREACHED(); | |
855 return base::PLATFORM_FILE_ERROR_FAILED; | |
856 } | |
857 | |
858 if (local_info->is_directory()) { | |
859 file_info->is_directory = true; | |
860 file_info->is_symbolic_link = false; | |
861 file_info->last_modified = local_info->modification_time; | |
862 *platform_file_path = FilePath(); | |
863 // We don't fill in ctime or atime. | |
864 return base::PLATFORM_FILE_OK; | |
865 } | |
866 if (local_info->data_path.empty()) | |
867 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; | |
868 FilePath data_path = DataPathToLocalPath(context->src_origin_url(), | |
869 context->src_type(), local_info->data_path); | |
870 return underlying_file_util_->GetFileInfo( | |
871 context, data_path, file_info, platform_file_path); | |
872 } | |
873 | |
874 PlatformFileError ObfuscatedFileSystemFileUtil::CreateFile( | |
875 FileSystemOperationContext* context, | |
876 const GURL& origin_url, FileSystemType type, const FilePath& source_path, | |
877 FileInfo* file_info, int file_flags, PlatformFile* handle) { | |
878 if (handle) | |
879 *handle = base::kInvalidPlatformFileValue; | |
880 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
881 origin_url, type, true); | |
882 int64 number; | |
883 if (!db || !db->GetNextInteger(&number)) | |
884 return base::PLATFORM_FILE_ERROR_FAILED; | |
885 // We use the third- and fourth-to-last digits as the directory. | |
886 int64 directory_number = number % 10000 / 100; | |
887 // TODO(ericu): local_path is an OS path; underlying_file_util_ isn't | |
888 // guaranteed to understand OS paths. | |
889 FilePath local_path = | |
890 GetDirectoryForOriginAndType(origin_url, type, false); | |
891 if (local_path.empty()) | |
892 return base::PLATFORM_FILE_ERROR_FAILED; | |
893 | |
894 local_path = local_path.AppendASCII(StringPrintf("%02" PRIu64, | |
895 directory_number)); | |
896 PlatformFileError error; | |
897 error = underlying_file_util_->CreateDirectory( | |
898 context, local_path, false /* exclusive */, false /* recursive */); | |
899 if (base::PLATFORM_FILE_OK != error) | |
900 return error; | |
901 local_path = local_path.AppendASCII(StringPrintf("%08" PRIu64, number)); | |
902 FilePath data_path = LocalPathToDataPath(origin_url, type, local_path); | |
903 if (data_path.empty()) | |
904 return base::PLATFORM_FILE_ERROR_FAILED; | |
905 bool created = false; | |
906 if (!source_path.empty()) { | |
907 DCHECK(!file_flags); | |
908 DCHECK(!handle); | |
909 error = underlying_file_util_->CopyOrMoveFile( | |
910 context, source_path, local_path, true /* copy */); | |
911 created = true; | |
912 } else { | |
913 FilePath path; | |
914 underlying_file_util_->GetLocalFilePath(context, local_path, &path); | |
915 if (file_util::PathExists(path)) { | |
916 if (!file_util::Delete(path, true)) { | |
917 NOTREACHED(); | |
918 return base::PLATFORM_FILE_ERROR_FAILED; | |
919 } | |
920 LOG(WARNING) << "A stray file detected"; | |
921 context->file_system_context()->GetQuotaUtil(context->src_type())-> | |
922 InvalidateUsageCache(context->src_origin_url(), context->src_type()); | |
923 } | |
924 | |
925 if (handle) { | |
926 error = underlying_file_util_->CreateOrOpen( | |
927 context, local_path, file_flags, handle, &created); | |
928 // If this succeeds, we must close handle on any subsequent error. | |
929 } else { | |
930 DCHECK(!file_flags); // file_flags is only used by CreateOrOpen. | |
931 error = underlying_file_util_->EnsureFileExists( | |
932 context, local_path, &created); | |
933 } | |
934 } | |
935 if (error != base::PLATFORM_FILE_OK) | |
936 return error; | |
937 | |
938 if (!created) { | |
939 NOTREACHED(); | |
940 if (handle) { | |
941 DCHECK_NE(base::kInvalidPlatformFileValue, *handle); | |
942 base::ClosePlatformFile(*handle); | |
943 underlying_file_util_->DeleteFile(context, local_path); | |
944 } | |
945 return base::PLATFORM_FILE_ERROR_FAILED; | |
946 } | |
947 file_info->data_path = data_path; | |
948 FileId file_id; | |
949 if (!db->AddFileInfo(*file_info, &file_id)) { | |
950 if (handle) { | |
951 DCHECK_NE(base::kInvalidPlatformFileValue, *handle); | |
952 base::ClosePlatformFile(*handle); | |
953 } | |
954 underlying_file_util_->DeleteFile(context, local_path); | |
955 return base::PLATFORM_FILE_ERROR_FAILED; | |
956 } | |
957 UpdatePathQuotaUsage(context, origin_url, type, 1, file_info->name.size()); | |
958 | |
959 return base::PLATFORM_FILE_OK; | |
960 } | |
961 | |
962 FilePath ObfuscatedFileSystemFileUtil::GetLocalPath( | |
963 const GURL& origin_url, | |
964 FileSystemType type, | |
965 const FilePath& virtual_path) { | |
966 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
967 origin_url, type, false); | |
968 if (!db) | |
969 return FilePath(); | |
970 FileId file_id; | |
971 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
972 return FilePath(); | |
973 FileInfo file_info; | |
974 if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) { | |
975 NOTREACHED(); | |
976 return FilePath(); // Directories have no local path. | |
977 } | |
978 return DataPathToLocalPath(origin_url, type, file_info.data_path); | |
979 } | |
980 | |
981 FilePath ObfuscatedFileSystemFileUtil::GetDirectoryForOriginAndType( | |
982 const GURL& origin, FileSystemType type, bool create) { | |
983 FilePath origin_dir = GetDirectoryForOrigin(origin, create); | |
984 if (origin_dir.empty()) | |
985 return FilePath(); | |
986 FilePath::StringType type_string = GetDirectoryNameForType(type); | |
987 if (type_string.empty()) { | |
988 LOG(WARNING) << "Unknown filesystem type requested:" << type; | |
989 return FilePath(); | |
990 } | |
991 FilePath path = origin_dir.Append(type_string); | |
992 if (!file_util::DirectoryExists(path) && | |
993 (!create || !file_util::CreateDirectory(path))) | |
994 return FilePath(); | |
995 return path; | |
996 } | |
997 | |
998 bool ObfuscatedFileSystemFileUtil::DeleteDirectoryForOriginAndType( | |
999 const GURL& origin, FileSystemType type) { | |
1000 FilePath origin_type_path = GetDirectoryForOriginAndType(origin, type, false); | |
1001 if (!file_util::PathExists(origin_type_path)) | |
1002 return true; | |
1003 | |
1004 // TODO(dmikurube): Consider the return value of DestroyDirectoryDatabase. | |
1005 // We ignore its error now since 1) it doesn't matter the final result, and | |
1006 // 2) it always returns false in Windows because of LevelDB's implementation. | |
1007 // Information about failure would be useful for debugging. | |
1008 DestroyDirectoryDatabase(origin, type); | |
1009 if (!file_util::Delete(origin_type_path, true /* recursive */)) | |
1010 return false; | |
1011 | |
1012 FilePath origin_path = origin_type_path.DirName(); | |
1013 DCHECK_EQ(origin_path.value(), GetDirectoryForOrigin(origin, false).value()); | |
1014 | |
1015 // Delete the origin directory if the deleted one was the last remaining | |
1016 // type for the origin. | |
1017 if (file_util::Delete(origin_path, false /* recursive */)) { | |
1018 InitOriginDatabase(false); | |
1019 if (origin_database_.get()) | |
1020 origin_database_->RemovePathForOrigin(GetOriginIdentifierFromURL(origin)); | |
1021 } | |
1022 | |
1023 // At this point we are sure we had successfully deleted the origin/type | |
1024 // directory, so just returning true here. | |
1025 return true; | |
1026 } | |
1027 | |
1028 bool ObfuscatedFileSystemFileUtil::MigrateFromOldSandbox( | |
1029 const GURL& origin_url, FileSystemType type, const FilePath& src_root) { | |
1030 if (!DestroyDirectoryDatabase(origin_url, type)) | |
1031 return false; | |
1032 FilePath dest_root = GetDirectoryForOriginAndType(origin_url, type, true); | |
1033 if (dest_root.empty()) | |
1034 return false; | |
1035 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
1036 origin_url, type, true); | |
1037 if (!db) | |
1038 return false; | |
1039 | |
1040 file_util::FileEnumerator file_enum(src_root, true, | |
1041 static_cast<file_util::FileEnumerator::FileType>( | |
1042 file_util::FileEnumerator::FILES | | |
1043 file_util::FileEnumerator::DIRECTORIES)); | |
1044 FilePath src_full_path; | |
1045 size_t root_path_length = src_root.value().length() + 1; // +1 for the slash | |
1046 while (!(src_full_path = file_enum.Next()).empty()) { | |
1047 file_util::FileEnumerator::FindInfo info; | |
1048 file_enum.GetFindInfo(&info); | |
1049 FilePath relative_virtual_path = | |
1050 FilePath(src_full_path.value().substr(root_path_length)); | |
1051 if (relative_virtual_path.empty()) { | |
1052 LOG(WARNING) << "Failed to convert path to relative: " << | |
1053 src_full_path.value(); | |
1054 return false; | |
1055 } | |
1056 FileId file_id; | |
1057 if (db->GetFileWithPath(relative_virtual_path, &file_id)) { | |
1058 NOTREACHED(); // File already exists. | |
1059 return false; | |
1060 } | |
1061 if (!db->GetFileWithPath(relative_virtual_path.DirName(), &file_id)) { | |
1062 NOTREACHED(); // Parent doesn't exist. | |
1063 return false; | |
1064 } | |
1065 | |
1066 FileInfo file_info; | |
1067 file_info.name = src_full_path.BaseName().value(); | |
1068 if (file_util::FileEnumerator::IsDirectory(info)) { | |
1069 #if defined(OS_WIN) | |
1070 file_info.modification_time = | |
1071 base::Time::FromFileTime(info.ftLastWriteTime); | |
1072 #elif defined(OS_POSIX) | |
1073 file_info.modification_time = base::Time::FromTimeT(info.stat.st_mtime); | |
1074 #endif | |
1075 } else { | |
1076 file_info.data_path = | |
1077 FilePath(kLegacyDataDirectory).Append(relative_virtual_path); | |
1078 } | |
1079 file_info.parent_id = file_id; | |
1080 if (!db->AddFileInfo(file_info, &file_id)) { | |
1081 NOTREACHED(); | |
1082 return false; | |
1083 } | |
1084 } | |
1085 // TODO(ericu): Should we adjust the mtime of the root directory to match as | |
1086 // well? | |
1087 FilePath legacy_dest_dir = dest_root.Append(kLegacyDataDirectory); | |
1088 | |
1089 if (!file_util::Move(src_root, legacy_dest_dir)) { | |
1090 LOG(WARNING) << | |
1091 "The final step of a migration failed; I'll try to clean up."; | |
1092 db = NULL; | |
1093 DestroyDirectoryDatabase(origin_url, type); | |
1094 return false; | |
1095 } | |
1096 return true; | |
1097 } | |
1098 | |
1099 // static | |
1100 FilePath::StringType ObfuscatedFileSystemFileUtil::GetDirectoryNameForType( | |
1101 FileSystemType type) { | |
1102 switch (type) { | |
1103 case kFileSystemTypeTemporary: | |
1104 return kTemporaryDirectoryName; | |
1105 case kFileSystemTypePersistent: | |
1106 return kPersistentDirectoryName; | |
1107 case kFileSystemTypeUnknown: | |
1108 default: | |
1109 return FilePath::StringType(); | |
1110 } | |
1111 } | |
1112 | |
1113 FilePath ObfuscatedFileSystemFileUtil::DataPathToLocalPath( | |
1114 const GURL& origin, FileSystemType type, const FilePath& data_path) { | |
1115 FilePath root = GetDirectoryForOriginAndType(origin, type, false); | |
1116 if (root.empty()) | |
1117 return root; | |
1118 return root.Append(data_path); | |
1119 } | |
1120 | |
1121 FilePath ObfuscatedFileSystemFileUtil::LocalPathToDataPath( | |
1122 const GURL& origin, FileSystemType type, const FilePath& local_path) { | |
1123 FilePath root = GetDirectoryForOriginAndType(origin, type, false); | |
1124 if (root.empty()) | |
1125 return root; | |
1126 // This removes the root, including the trailing slash, leaving a relative | |
1127 // path. | |
1128 return FilePath(local_path.value().substr(root.value().length() + 1)); | |
1129 } | |
1130 | |
1131 // TODO: How to do the whole validation-without-creation thing? We may not have | |
1132 // quota even to create the database. Ah, in that case don't even get here? | |
1133 // Still doesn't answer the quota issue, though. | |
1134 FileSystemDirectoryDatabase* ObfuscatedFileSystemFileUtil::GetDirectoryDatabase( | |
1135 const GURL& origin, FileSystemType type, bool create) { | |
1136 std::string type_string = | |
1137 FileSystemPathManager::GetFileSystemTypeString(type); | |
1138 if (type_string.empty()) { | |
1139 LOG(WARNING) << "Unknown filesystem type requested:" << type; | |
1140 return NULL; | |
1141 } | |
1142 std::string key = GetOriginIdentifierFromURL(origin) + type_string; | |
1143 DirectoryMap::iterator iter = directories_.find(key); | |
1144 if (iter != directories_.end()) { | |
1145 MarkUsed(); | |
1146 return iter->second; | |
1147 } | |
1148 | |
1149 FilePath path = GetDirectoryForOriginAndType(origin, type, create); | |
1150 if (path.empty()) | |
1151 return NULL; | |
1152 if (!file_util::DirectoryExists(path)) { | |
1153 if (!file_util::CreateDirectory(path)) { | |
1154 LOG(WARNING) << "Failed to origin+type directory: " << path.value(); | |
1155 return NULL; | |
1156 } | |
1157 } | |
1158 MarkUsed(); | |
1159 path = path.AppendASCII(kDirectoryDatabaseName); | |
1160 FileSystemDirectoryDatabase* database = new FileSystemDirectoryDatabase(path); | |
1161 directories_[key] = database; | |
1162 return database; | |
1163 } | |
1164 | |
1165 FilePath ObfuscatedFileSystemFileUtil::GetDirectoryForOrigin( | |
1166 const GURL& origin, bool create) { | |
1167 if (!InitOriginDatabase(create)) | |
1168 return FilePath(); | |
1169 FilePath directory_name; | |
1170 std::string id = GetOriginIdentifierFromURL(origin); | |
1171 | |
1172 bool exists_in_db = origin_database_->HasOriginPath(id); | |
1173 if (!exists_in_db && !create) | |
1174 return FilePath(); | |
1175 if (!origin_database_->GetPathForOrigin(id, &directory_name)) | |
1176 return FilePath(); | |
1177 | |
1178 FilePath path = file_system_directory_.Append(directory_name); | |
1179 bool exists_in_fs = file_util::DirectoryExists(path); | |
1180 if (!exists_in_db && exists_in_fs) { | |
1181 if (!file_util::Delete(path, true)) | |
1182 return FilePath(); | |
1183 exists_in_fs = false; | |
1184 } | |
1185 | |
1186 if (!exists_in_fs) { | |
1187 if (!create || !file_util::CreateDirectory(path)) | |
1188 return FilePath(); | |
1189 } | |
1190 | |
1191 return path; | |
1192 } | |
1193 | |
1194 void ObfuscatedFileSystemFileUtil::MarkUsed() { | |
1195 if (timer_.IsRunning()) | |
1196 timer_.Reset(); | |
1197 else | |
1198 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kFlushDelaySeconds), | |
1199 this, &ObfuscatedFileSystemFileUtil::DropDatabases); | |
1200 } | |
1201 | |
1202 void ObfuscatedFileSystemFileUtil::DropDatabases() { | |
1203 origin_database_.reset(); | |
1204 STLDeleteContainerPairSecondPointers( | |
1205 directories_.begin(), directories_.end()); | |
1206 directories_.clear(); | |
1207 } | |
1208 | |
1209 // static | |
1210 int64 ObfuscatedFileSystemFileUtil::ComputeFilePathCost(const FilePath& path) { | |
1211 return GetPathQuotaUsage(1, path.BaseName().value().size()); | |
1212 } | |
1213 | |
1214 bool ObfuscatedFileSystemFileUtil::DestroyDirectoryDatabase( | |
1215 const GURL& origin, FileSystemType type) { | |
1216 std::string type_string = | |
1217 FileSystemPathManager::GetFileSystemTypeString(type); | |
1218 if (type_string.empty()) { | |
1219 LOG(WARNING) << "Unknown filesystem type requested:" << type; | |
1220 return true; | |
1221 } | |
1222 std::string key = GetOriginIdentifierFromURL(origin) + type_string; | |
1223 DirectoryMap::iterator iter = directories_.find(key); | |
1224 if (iter != directories_.end()) { | |
1225 FileSystemDirectoryDatabase* database = iter->second; | |
1226 directories_.erase(iter); | |
1227 delete database; | |
1228 } | |
1229 | |
1230 FilePath path = GetDirectoryForOriginAndType(origin, type, false); | |
1231 if (path.empty()) | |
1232 return true; | |
1233 if (!file_util::DirectoryExists(path)) | |
1234 return true; | |
1235 path = path.AppendASCII(kDirectoryDatabaseName); | |
1236 return FileSystemDirectoryDatabase::DestroyDatabase(path); | |
1237 } | |
1238 | |
1239 bool ObfuscatedFileSystemFileUtil::InitOriginDatabase(bool create) { | |
1240 if (!origin_database_.get()) { | |
1241 if (!create && !file_util::DirectoryExists(file_system_directory_)) | |
1242 return false; | |
1243 if (!file_util::CreateDirectory(file_system_directory_)) { | |
1244 LOG(WARNING) << "Failed to create FileSystem directory: " << | |
1245 file_system_directory_.value(); | |
1246 return false; | |
1247 } | |
1248 origin_database_.reset( | |
1249 new FileSystemOriginDatabase( | |
1250 file_system_directory_.AppendASCII(kOriginDatabaseName))); | |
1251 } | |
1252 return true; | |
1253 } | |
1254 | |
1255 } // namespace fileapi | |
OLD | NEW |