blob: 1e7c16e41cc8e4447d75567ff4e648c47749181d [file] [log] [blame]
[email protected]d4905e2e2011-05-13 21:56:321// 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
9#include "base/file_util.h"
[email protected]6b931152011-05-20 21:02:3510#include "base/format_macros.h"
[email protected]d4905e2e2011-05-13 21:56:3211#include "base/logging.h"
[email protected]0a7328532011-05-13 23:54:4312#include "base/message_loop.h"
[email protected]d4905e2e2011-05-13 21:56:3213#include "base/string_number_conversions.h"
[email protected]6b931152011-05-20 21:02:3514#include "base/stringprintf.h"
[email protected]d4905e2e2011-05-13 21:56:3215#include "base/sys_string_conversions.h"
16#include "base/stl_util-inl.h"
17#include "googleurl/src/gurl.h"
18#include "webkit/fileapi/file_system_context.h"
19#include "webkit/fileapi/file_system_operation_context.h"
20#include "webkit/fileapi/file_system_path_manager.h"
21#include "webkit/fileapi/quota_file_util.h"
22#include "webkit/fileapi/sandbox_mount_point_provider.h"
23
24// TODO(ericu): Every instance of FileSystemFileUtil in this file should switch
25// to QuotaFileUtil as soon as I sort out FileSystemPathManager's and
26// SandboxMountPointProvider's lookups of the root path for a filesystem.
27namespace {
28
[email protected]0a7328532011-05-13 23:54:4329const int64 kFlushDelaySeconds = 10 * 60; // 10 minutes
30
[email protected]d4905e2e2011-05-13 21:56:3231const char kOriginDatabaseName[] = "Origins";
32const char kDirectoryDatabaseName[] = "Paths";
33
34void InitFileInfo(
35 fileapi::FileSystemDirectoryDatabase::FileInfo* file_info,
36 fileapi::FileSystemDirectoryDatabase::FileId parent_id,
[email protected]6b931152011-05-20 21:02:3537 const FilePath::StringType& file_name) {
[email protected]d4905e2e2011-05-13 21:56:3238 DCHECK(file_info);
39 file_info->parent_id = parent_id;
[email protected]d4905e2e2011-05-13 21:56:3240 file_info->name = file_name;
[email protected]d4905e2e2011-05-13 21:56:3241}
42
[email protected]6b931152011-05-20 21:02:3543const FilePath::CharType kLegacyDataDirectory[] = FILE_PATH_LITERAL("Legacy");
44
45const FilePath::CharType kTemporaryDirectoryName[] = FILE_PATH_LITERAL("t");
46const FilePath::CharType kPersistentDirectoryName[] = FILE_PATH_LITERAL("p");
47
[email protected]d4905e2e2011-05-13 21:56:3248} // namespace
49
50namespace fileapi {
51
52using base::PlatformFile;
53using base::PlatformFileError;
54
55ObfuscatedFileSystemFileUtil::ObfuscatedFileSystemFileUtil(
56 const FilePath& file_system_directory)
57 : file_system_directory_(file_system_directory) {
58}
59
60ObfuscatedFileSystemFileUtil::~ObfuscatedFileSystemFileUtil() {
61 DropDatabases();
62}
63
64PlatformFileError ObfuscatedFileSystemFileUtil::CreateOrOpen(
65 FileSystemOperationContext* context,
66 const FilePath& virtual_path, int file_flags,
67 PlatformFile* file_handle, bool* created) {
68 DCHECK(!(file_flags & (base::PLATFORM_FILE_DELETE_ON_CLOSE |
69 base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_EXCLUSIVE_READ |
70 base::PLATFORM_FILE_EXCLUSIVE_WRITE)));
71 FileSystemDirectoryDatabase* db =
72 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
73 if (!db)
74 return base::PLATFORM_FILE_ERROR_FAILED;
75 FileId file_id;
76 if (!db->GetFileWithPath(virtual_path, &file_id)) {
77 // The file doesn't exist.
78 if (!(file_flags & (base::PLATFORM_FILE_CREATE |
79 base::PLATFORM_FILE_CREATE_ALWAYS)))
80 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
81 FileId parent_id;
82 if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id))
83 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
84 FileInfo file_info;
[email protected]6b931152011-05-20 21:02:3585 InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value());
[email protected]d4905e2e2011-05-13 21:56:3286 PlatformFileError error = CreateFile(
[email protected]6b931152011-05-20 21:02:3587 context, context->src_origin_url(), context->src_type(), FilePath(),
88 &file_info, file_flags, file_handle);
[email protected]d4905e2e2011-05-13 21:56:3289 if (created && base::PLATFORM_FILE_OK == error)
90 *created = true;
91 return error;
92 }
93 if (file_flags & base::PLATFORM_FILE_CREATE)
94 return base::PLATFORM_FILE_ERROR_EXISTS;
95
96 FileInfo file_info;
97 if (!db->GetFileInfo(file_id, &file_info)) {
98 NOTREACHED();
99 return base::PLATFORM_FILE_ERROR_FAILED;
100 }
101 if (file_info.is_directory())
102 return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
[email protected]6b931152011-05-20 21:02:35103 FilePath data_path = DataPathToLocalPath(context->src_origin_url(),
104 context->src_type(), file_info.data_path);
[email protected]d4905e2e2011-05-13 21:56:32105 return FileSystemFileUtil::GetInstance()->CreateOrOpen(
[email protected]6b931152011-05-20 21:02:35106 context, data_path, file_flags, file_handle, created);
[email protected]d4905e2e2011-05-13 21:56:32107}
108
109PlatformFileError ObfuscatedFileSystemFileUtil::EnsureFileExists(
110 FileSystemOperationContext* context,
111 const FilePath& virtual_path,
112 bool* created) {
113 FileSystemDirectoryDatabase* db =
114 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
115 if (!db)
116 return base::PLATFORM_FILE_ERROR_FAILED;
117 FileId file_id;
118 if (db->GetFileWithPath(virtual_path, &file_id)) {
119 FileInfo file_info;
120 if (!db->GetFileInfo(file_id, &file_info)) {
121 NOTREACHED();
122 return base::PLATFORM_FILE_ERROR_FAILED;
123 }
124 if (file_info.is_directory())
125 return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
126 if (created)
127 *created = false;
128 return base::PLATFORM_FILE_OK;
129 }
130 FileId parent_id;
131 if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id))
132 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
133
134 FileInfo file_info;
[email protected]6b931152011-05-20 21:02:35135 InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value());
136 PlatformFileError error = CreateFile(context, context->src_origin_url(),
137 context->src_type(), FilePath(), &file_info, 0, NULL);
[email protected]d4905e2e2011-05-13 21:56:32138 if (created && base::PLATFORM_FILE_OK == error)
139 *created = true;
140 return error;
141}
142
143PlatformFileError ObfuscatedFileSystemFileUtil::GetLocalFilePath(
144 FileSystemOperationContext* context,
145 const FilePath& virtual_path,
146 FilePath* local_path) {
147 FilePath path =
148 GetLocalPath(context->src_origin_url(), context->src_type(),
149 virtual_path);
150 if (path.empty())
151 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
152
153 *local_path = path;
154 return base::PLATFORM_FILE_OK;
155}
156
157PlatformFileError ObfuscatedFileSystemFileUtil::GetFileInfo(
158 FileSystemOperationContext* context,
159 const FilePath& virtual_path,
160 base::PlatformFileInfo* file_info,
161 FilePath* platform_file_path) {
162 FileSystemDirectoryDatabase* db =
163 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
164 if (!db)
165 return base::PLATFORM_FILE_ERROR_FAILED;
166 FileId file_id;
167 if (!db->GetFileWithPath(virtual_path, &file_id))
168 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
169 FileInfo local_info;
170 if (!db->GetFileInfo(file_id, &local_info)) {
171 NOTREACHED();
172 return base::PLATFORM_FILE_ERROR_FAILED;
173 }
174 if (local_info.is_directory()) {
175 file_info->is_directory = true;
176 file_info->is_symbolic_link = false;
177 file_info->last_modified = local_info.modification_time;
178 *platform_file_path = FilePath();
179 // We don't fill in ctime or atime.
180 return base::PLATFORM_FILE_OK;
181 }
182 if (local_info.data_path.empty())
183 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
[email protected]6b931152011-05-20 21:02:35184 FilePath data_path = DataPathToLocalPath(context->src_origin_url(),
185 context->src_type(), local_info.data_path);
[email protected]d4905e2e2011-05-13 21:56:32186 return FileSystemFileUtil::GetInstance()->GetFileInfo(
[email protected]6b931152011-05-20 21:02:35187 context, data_path, file_info, platform_file_path);
[email protected]d4905e2e2011-05-13 21:56:32188}
189
190PlatformFileError ObfuscatedFileSystemFileUtil::ReadDirectory(
191 FileSystemOperationContext* context,
192 const FilePath& virtual_path,
193 std::vector<base::FileUtilProxy::Entry>* entries) {
194 // TODO(kkanetkar): Implement directory read in multiple chunks.
195 FileSystemDirectoryDatabase* db =
196 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
197 if (!db)
198 return base::PLATFORM_FILE_ERROR_FAILED;
199 FileId file_id;
200 if (!db->GetFileWithPath(virtual_path, &file_id))
201 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
202 FileInfo file_info;
203 if (!db->GetFileInfo(file_id, &file_info)) {
204 DCHECK(!file_id);
205 // It's the root directory and the database hasn't been initialized yet.
206 entries->clear();
207 return base::PLATFORM_FILE_OK;
208 }
209 if (!file_info.is_directory())
210 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
211 std::vector<FileId> children;
212 if (!db->ListChildren(file_id, &children)) {
213 NOTREACHED();
214 return base::PLATFORM_FILE_ERROR_FAILED;
215 }
216 std::vector<FileId>::iterator iter;
217 for (iter = children.begin(); iter != children.end(); ++iter) {
218 if (!db->GetFileInfo(*iter, &file_info)) {
219 NOTREACHED();
220 return base::PLATFORM_FILE_ERROR_FAILED;
221 }
222 base::FileUtilProxy::Entry entry;
[email protected]d4905e2e2011-05-13 21:56:32223 entry.name = file_info.name;
[email protected]d4905e2e2011-05-13 21:56:32224 entry.is_directory = file_info.is_directory();
225 entries->push_back(entry);
226 }
227 return base::PLATFORM_FILE_OK;
228}
229
230PlatformFileError ObfuscatedFileSystemFileUtil::CreateDirectory(
231 FileSystemOperationContext* context,
232 const FilePath& virtual_path,
233 bool exclusive,
234 bool recursive) {
235 FileSystemDirectoryDatabase* db =
236 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
237 if (!db)
238 return base::PLATFORM_FILE_ERROR_FAILED;
239 FileId file_id;
240 if (db->GetFileWithPath(virtual_path, &file_id)) {
241 FileInfo file_info;
242 if (exclusive)
243 return base::PLATFORM_FILE_ERROR_EXISTS;
244 if (!db->GetFileInfo(file_id, &file_info)) {
245 NOTREACHED();
246 return base::PLATFORM_FILE_ERROR_FAILED;
247 }
248 if (!file_info.is_directory())
249 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
250 return base::PLATFORM_FILE_OK;
251 }
252
253 std::vector<FilePath::StringType> components;
254 virtual_path.GetComponents(&components);
255 FileId parent_id = 0;
256 size_t index;
257 for (index = 0; index < components.size(); ++index) {
[email protected]89ee4272011-05-16 18:45:17258 FilePath::StringType name = components[index];
259 if (name == FILE_PATH_LITERAL("/"))
[email protected]d4905e2e2011-05-13 21:56:32260 continue;
261 if (!db->GetChildWithName(parent_id, name, &parent_id))
262 break;
263 }
264 if (!recursive && components.size() - index > 1)
265 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
266 for (; index < components.size(); ++index) {
267 FileInfo file_info;
[email protected]d4905e2e2011-05-13 21:56:32268 file_info.name = components[index];
[email protected]89ee4272011-05-16 18:45:17269 if (file_info.name == FILE_PATH_LITERAL("/"))
[email protected]d4905e2e2011-05-13 21:56:32270 continue;
271 file_info.modification_time = base::Time::Now();
272 file_info.parent_id = parent_id;
273 if (!db->AddFileInfo(file_info, &parent_id)) {
274 NOTREACHED();
275 return base::PLATFORM_FILE_ERROR_FAILED;
276 }
277 }
278 return base::PLATFORM_FILE_OK;
279}
280
281PlatformFileError ObfuscatedFileSystemFileUtil::CopyOrMoveFile(
282 FileSystemOperationContext* context,
283 const FilePath& src_file_path,
284 const FilePath& dest_file_path,
285 bool copy) {
286 // TODO(ericu): Handle multi-db move+copy, where src and dest aren't in the
287 // same database. Currently we'll just fail badly. This may get handled from
288 // higher-level code, though, and as we don't have cross-filesystem
289 // transactions that's not any less efficient than doing it here.
290 FileSystemDirectoryDatabase* db =
291 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
292 if (!db)
293 return base::PLATFORM_FILE_ERROR_FAILED;
294 FileId src_file_id;
295 if (!db->GetFileWithPath(src_file_path, &src_file_id))
296 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
297 FileId dest_file_id;
298 bool overwrite = db->GetFileWithPath(dest_file_path, &dest_file_id);
299 FileInfo src_file_info;
300 FileInfo dest_file_info;
301 if (!db->GetFileInfo(src_file_id, &src_file_info) ||
302 src_file_info.is_directory()) {
303 NOTREACHED();
304 return base::PLATFORM_FILE_ERROR_FAILED;
305 }
306 if (overwrite) {
307 if (!db->GetFileInfo(dest_file_id, &dest_file_info) ||
308 dest_file_info.is_directory()) {
309 NOTREACHED();
310 return base::PLATFORM_FILE_ERROR_FAILED;
311 }
312 }
313 /*
314 * Copy-with-overwrite
315 * Just overwrite data file
316 * Copy-without-overwrite
317 * Copy backing file
318 * Create new metadata pointing to new backing file.
319 * Move-with-overwrite
320 * transaction:
321 * Remove source entry.
322 * Point target entry to source entry's backing file.
323 * Delete target entry's old backing file
324 * Move-without-overwrite
325 * Just update metadata
326 */
327 if (copy) {
[email protected]6b931152011-05-20 21:02:35328 FilePath src_data_path = DataPathToLocalPath(context->src_origin_url(),
329 context->src_type(), src_file_info.data_path);
[email protected]d4905e2e2011-05-13 21:56:32330 if (overwrite) {
[email protected]6b931152011-05-20 21:02:35331 FilePath dest_data_path = DataPathToLocalPath(context->src_origin_url(),
332 context->src_type(), dest_file_info.data_path);
[email protected]d4905e2e2011-05-13 21:56:32333 return FileSystemFileUtil::GetInstance()->CopyOrMoveFile(context,
[email protected]6b931152011-05-20 21:02:35334 src_data_path, dest_data_path, copy);
[email protected]d4905e2e2011-05-13 21:56:32335 } else {
336 FileId dest_parent_id;
337 if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) {
338 NOTREACHED(); // We shouldn't be called in this case.
339 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
340 }
341 InitFileInfo(&dest_file_info, dest_parent_id,
[email protected]6b931152011-05-20 21:02:35342 dest_file_path.BaseName().value());
[email protected]d4905e2e2011-05-13 21:56:32343 return CreateFile(context, context->dest_origin_url(),
[email protected]6b931152011-05-20 21:02:35344 context->dest_type(), src_data_path, &dest_file_info, 0,
345 NULL);
[email protected]d4905e2e2011-05-13 21:56:32346 }
347 } else { // It's a move.
348 if (overwrite) {
349 if (!db->OverwritingMoveFile(src_file_id, dest_file_id))
350 return base::PLATFORM_FILE_ERROR_FAILED;
[email protected]6b931152011-05-20 21:02:35351 FilePath dest_data_path = DataPathToLocalPath(context->src_origin_url(),
352 context->src_type(), dest_file_info.data_path);
[email protected]d4905e2e2011-05-13 21:56:32353 if (base::PLATFORM_FILE_OK !=
354 FileSystemFileUtil::GetInstance()->DeleteFile(
[email protected]6b931152011-05-20 21:02:35355 context, dest_data_path))
[email protected]d4905e2e2011-05-13 21:56:32356 LOG(WARNING) << "Leaked a backing file.";
357 return base::PLATFORM_FILE_OK;
358 } else {
359 FileId dest_parent_id;
360 if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) {
361 NOTREACHED();
362 return base::PLATFORM_FILE_ERROR_FAILED;
363 }
364 src_file_info.parent_id = dest_parent_id;
[email protected]d4905e2e2011-05-13 21:56:32365 src_file_info.name = dest_file_path.BaseName().value();
[email protected]d4905e2e2011-05-13 21:56:32366 if (!db->UpdateFileInfo(src_file_id, src_file_info))
367 return base::PLATFORM_FILE_ERROR_FAILED;
368 return base::PLATFORM_FILE_OK;
369 }
370 }
371 NOTREACHED();
372 return base::PLATFORM_FILE_ERROR_FAILED;
373}
374
375PlatformFileError ObfuscatedFileSystemFileUtil::DeleteFile(
376 FileSystemOperationContext* context,
377 const FilePath& virtual_path) {
378 FileSystemDirectoryDatabase* db =
379 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
380 if (!db)
381 return base::PLATFORM_FILE_ERROR_FAILED;
382 FileId file_id;
383 if (!db->GetFileWithPath(virtual_path, &file_id))
384 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
385 FileInfo file_info;
386 if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) {
387 NOTREACHED();
388 return base::PLATFORM_FILE_ERROR_FAILED;
389 }
390 if (!db->RemoveFileInfo(file_id)) {
391 NOTREACHED();
392 return base::PLATFORM_FILE_ERROR_FAILED;
393 }
[email protected]6b931152011-05-20 21:02:35394 FilePath data_path = DataPathToLocalPath(context->src_origin_url(),
395 context->src_type(), file_info.data_path);
[email protected]d4905e2e2011-05-13 21:56:32396 if (base::PLATFORM_FILE_OK !=
[email protected]6b931152011-05-20 21:02:35397 FileSystemFileUtil::GetInstance()->DeleteFile(context, data_path))
[email protected]d4905e2e2011-05-13 21:56:32398 LOG(WARNING) << "Leaked a backing file.";
399 return base::PLATFORM_FILE_OK;
400}
401
402PlatformFileError ObfuscatedFileSystemFileUtil::DeleteSingleDirectory(
403 FileSystemOperationContext* context,
404 const FilePath& virtual_path) {
405 FileSystemDirectoryDatabase* db =
406 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
407 if (!db)
408 return base::PLATFORM_FILE_ERROR_FAILED;
409 FileId file_id;
410 if (!db->GetFileWithPath(virtual_path, &file_id))
411 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
412 FileInfo file_info;
413 if (!db->GetFileInfo(file_id, &file_info) || !file_info.is_directory()) {
414 NOTREACHED();
415 return base::PLATFORM_FILE_ERROR_FAILED;
416 }
417 if (!db->RemoveFileInfo(file_id))
[email protected]c7a4a10f2011-05-19 04:56:06418 return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
[email protected]d4905e2e2011-05-13 21:56:32419 return base::PLATFORM_FILE_OK;
420}
421
422PlatformFileError ObfuscatedFileSystemFileUtil::Touch(
423 FileSystemOperationContext* context,
424 const FilePath& virtual_path,
425 const base::Time& last_access_time,
426 const base::Time& last_modified_time) {
427 FileSystemDirectoryDatabase* db =
428 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
429 if (!db)
430 return base::PLATFORM_FILE_ERROR_FAILED;
431 FileId file_id;
432 if (db->GetFileWithPath(virtual_path, &file_id)) {
433 FileInfo file_info;
434 if (!db->GetFileInfo(file_id, &file_info)) {
435 NOTREACHED();
436 return base::PLATFORM_FILE_ERROR_FAILED;
437 }
438 if (file_info.is_directory()) {
439 file_info.modification_time = last_modified_time;
440 if (!db->UpdateFileInfo(file_id, file_info))
441 return base::PLATFORM_FILE_ERROR_FAILED;
442 return base::PLATFORM_FILE_OK;
443 }
[email protected]6b931152011-05-20 21:02:35444 FilePath data_path = DataPathToLocalPath(context->src_origin_url(),
445 context->src_type(), file_info.data_path);
[email protected]d4905e2e2011-05-13 21:56:32446 return FileSystemFileUtil::GetInstance()->Touch(
[email protected]6b931152011-05-20 21:02:35447 context, data_path, last_access_time, last_modified_time);
[email protected]d4905e2e2011-05-13 21:56:32448 }
449 FileId parent_id;
450 if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id))
451 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
452
453 FileInfo file_info;
[email protected]6b931152011-05-20 21:02:35454 InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value());
[email protected]d4905e2e2011-05-13 21:56:32455 // In the event of a sporadic underlying failure, we might create a new file,
456 // but fail to update its mtime + atime.
[email protected]6b931152011-05-20 21:02:35457 PlatformFileError error = CreateFile(context, context->src_origin_url(),
458 context->src_type(), FilePath(), &file_info, 0, NULL);
[email protected]d4905e2e2011-05-13 21:56:32459 if (base::PLATFORM_FILE_OK != error)
460 return error;
461
[email protected]6b931152011-05-20 21:02:35462 FilePath data_path = DataPathToLocalPath(context->src_origin_url(),
463 context->src_type(), file_info.data_path);
464 return FileSystemFileUtil::GetInstance()->Touch(context, data_path,
[email protected]d4905e2e2011-05-13 21:56:32465 last_access_time, last_modified_time);
466}
467
468PlatformFileError ObfuscatedFileSystemFileUtil::Truncate(
469 FileSystemOperationContext* context,
470 const FilePath& virtual_path,
471 int64 length) {
472 FilePath local_path =
473 GetLocalPath(context->src_origin_url(), context->src_type(),
474 virtual_path);
475 if (local_path.empty())
476 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
477 return FileSystemFileUtil::GetInstance()->Truncate(
478 context, local_path, length);
479}
480
481bool ObfuscatedFileSystemFileUtil::PathExists(
482 FileSystemOperationContext* context,
483 const FilePath& virtual_path) {
484 FileSystemDirectoryDatabase* db =
485 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
486 if (!db)
487 return false;
488 FileId file_id;
489 return db->GetFileWithPath(virtual_path, &file_id);
490}
491
492bool ObfuscatedFileSystemFileUtil::DirectoryExists(
493 FileSystemOperationContext* context,
494 const FilePath& virtual_path) {
495 FileSystemDirectoryDatabase* db =
496 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
497 if (!db)
498 return false;
499 FileId file_id;
500 if (!db->GetFileWithPath(virtual_path, &file_id))
501 return false;
502 FileInfo file_info;
503 if (!db->GetFileInfo(file_id, &file_info)) {
504 NOTREACHED();
505 return false;
506 }
507 return file_info.is_directory();
508}
509
510bool ObfuscatedFileSystemFileUtil::IsDirectoryEmpty(
511 FileSystemOperationContext* context,
512 const FilePath& virtual_path) {
513 FileSystemDirectoryDatabase* db =
514 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
515 if (!db)
516 return false;
517 FileId file_id;
518 if (!db->GetFileWithPath(virtual_path, &file_id))
519 return true; // Not a great answer, but it's what others do.
520 FileInfo file_info;
521 if (!db->GetFileInfo(file_id, &file_info)) {
522 DCHECK(!file_id);
523 // It's the root directory and the database hasn't been initialized yet.
524 return true;
525 }
526 if (!file_info.is_directory())
527 return true;
528 std::vector<FileId> children;
529 // TODO(ericu): This could easily be made faster with help from the database.
530 if (!db->ListChildren(file_id, &children))
531 return true;
532 return children.empty();
533}
534
535class ObfuscatedFileSystemFileEnumerator
536 : public FileSystemFileUtil::AbstractFileEnumerator {
537 public:
538 ObfuscatedFileSystemFileEnumerator(
539 FileSystemDirectoryDatabase* db, const FilePath& virtual_root_path)
540 : db_(db) {
541 FileId file_id;
542 FileInfo file_info;
543 if (!db_->GetFileWithPath(virtual_root_path, &file_id))
544 return;
545 if (!db_->GetFileInfo(file_id, &file_info))
546 return;
547 if (!file_info.is_directory())
548 return;
549 FileRecord record = { file_id, file_info, virtual_root_path };
550 display_queue_.push(record);
551 Next(); // Enumerators don't include the directory itself.
552 }
553
554 ~ObfuscatedFileSystemFileEnumerator() {}
555
556 virtual FilePath Next() {
557 ProcessRecurseQueue();
558 if (display_queue_.empty())
559 return FilePath();
560 current_ = display_queue_.front();
561 display_queue_.pop();
562 if (current_.file_info.is_directory())
563 recurse_queue_.push(current_);
564 return current_.file_path;
565 }
566
567 virtual bool IsDirectory() {
568 return current_.file_info.is_directory();
569 }
570
571 private:
572 typedef FileSystemDirectoryDatabase::FileId FileId;
573 typedef FileSystemDirectoryDatabase::FileInfo FileInfo;
574
575 struct FileRecord {
576 FileId file_id;
577 FileInfo file_info;
578 FilePath file_path;
579 };
580
581 void ProcessRecurseQueue() {
582 while (display_queue_.empty() && !recurse_queue_.empty()) {
583 FileRecord directory = recurse_queue_.front();
584 std::vector<FileId> children;
585 recurse_queue_.pop();
586 if (!db_->ListChildren(directory.file_id, &children))
587 return;
588 std::vector<FileId>::iterator iter;
589 for (iter = children.begin(); iter != children.end(); ++iter) {
590 FileRecord child;
591 child.file_id = *iter;
592 if (!db_->GetFileInfo(child.file_id, &child.file_info))
593 return;
[email protected]89ee4272011-05-16 18:45:17594 child.file_path = directory.file_path.Append(child.file_info.name);
[email protected]d4905e2e2011-05-13 21:56:32595 display_queue_.push(child);
596 }
597 }
598 }
599
600 std::queue<FileRecord> display_queue_;
601 std::queue<FileRecord> recurse_queue_;
602 FileRecord current_;
603 FileSystemDirectoryDatabase* db_;
604};
605
606FileSystemFileUtil::AbstractFileEnumerator*
607ObfuscatedFileSystemFileUtil::CreateFileEnumerator(
608 FileSystemOperationContext* context,
609 const FilePath& root_path) {
610 FileSystemDirectoryDatabase* db =
611 GetDirectoryDatabase(context->src_origin_url(), context->src_type());
612 if (!db)
613 return new FileSystemFileUtil::EmptyFileEnumerator();
614 return new ObfuscatedFileSystemFileEnumerator(db, root_path);
615}
616
617PlatformFileError ObfuscatedFileSystemFileUtil::CreateFile(
618 FileSystemOperationContext* context,
[email protected]6b931152011-05-20 21:02:35619 const GURL& origin_url, FileSystemType type, const FilePath& source_path,
[email protected]d4905e2e2011-05-13 21:56:32620 FileInfo* file_info, int file_flags, PlatformFile* handle) {
621 if (handle)
622 *handle = base::kInvalidPlatformFileValue;
623 FileSystemDirectoryDatabase* db = GetDirectoryDatabase(origin_url, type);
624 int64 number;
625 if (!db || !db->GetNextInteger(&number))
626 return base::PLATFORM_FILE_ERROR_FAILED;
627 // We use the third- and fourth-to-last digits as the directory.
628 int64 directory_number = number % 10000 / 100;
629 FilePath path =
[email protected]c7a4a10f2011-05-19 04:56:06630 GetDirectoryForOriginAndType(origin_url, type, false);
631 if (path.empty())
632 return base::PLATFORM_FILE_ERROR_FAILED;
[email protected]6b931152011-05-20 21:02:35633
634 path = path.AppendASCII(StringPrintf("%02" PRIu64, directory_number));
[email protected]d4905e2e2011-05-13 21:56:32635 PlatformFileError error;
636 error = FileSystemFileUtil::GetInstance()->CreateDirectory(
637 context, path, false /* exclusive */, false /* recursive */);
638 if (base::PLATFORM_FILE_OK != error)
639 return error;
[email protected]6b931152011-05-20 21:02:35640 path = path.AppendASCII(StringPrintf("%08" PRIu64, number));
641 FilePath data_path = LocalPathToDataPath(origin_url, type, path);
642 if (data_path.empty())
643 return base::PLATFORM_FILE_ERROR_FAILED;
[email protected]d4905e2e2011-05-13 21:56:32644 bool created = false;
[email protected]6b931152011-05-20 21:02:35645 if (!source_path.empty()) {
[email protected]d4905e2e2011-05-13 21:56:32646 DCHECK(!file_flags);
647 DCHECK(!handle);
648 error = FileSystemFileUtil::GetInstance()->CopyOrMoveFile(
[email protected]6b931152011-05-20 21:02:35649 context, source_path, path, true /* copy */);
[email protected]d4905e2e2011-05-13 21:56:32650 created = true;
651 } else {
652 if (handle) {
653 error = FileSystemFileUtil::GetInstance()->CreateOrOpen(
654 context, path, file_flags, handle, &created);
655 // If this succeeds, we must close handle on any subsequent error.
656 } else {
657 DCHECK(!file_flags); // file_flags is only used by CreateOrOpen.
658 error = FileSystemFileUtil::GetInstance()->EnsureFileExists(
659 context, path, &created);
660 }
661 }
662 if (error != base::PLATFORM_FILE_OK)
663 return error;
664
665 if (!created) {
666 NOTREACHED();
667 if (handle) {
668 base::ClosePlatformFile(*handle);
669 FileSystemFileUtil::GetInstance()->DeleteFile(context, path);
670 }
671 return base::PLATFORM_FILE_ERROR_FAILED;
672 }
[email protected]6b931152011-05-20 21:02:35673 file_info->data_path = data_path;
[email protected]d4905e2e2011-05-13 21:56:32674 FileId file_id;
675 if (!db->AddFileInfo(*file_info, &file_id)) {
676 if (handle)
677 base::ClosePlatformFile(*handle);
678 FileSystemFileUtil::GetInstance()->DeleteFile(context, path);
679 return base::PLATFORM_FILE_ERROR_FAILED;
680 }
681
682 return base::PLATFORM_FILE_OK;
683}
684
685FilePath ObfuscatedFileSystemFileUtil::GetLocalPath(
686 const GURL& origin_url,
687 FileSystemType type,
688 const FilePath& virtual_path) {
689 FileSystemDirectoryDatabase* db = GetDirectoryDatabase(origin_url, type);
690 if (!db)
691 return FilePath();
692 FileId file_id;
693 if (!db->GetFileWithPath(virtual_path, &file_id))
694 return FilePath();
695 FileInfo file_info;
696 if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) {
697 NOTREACHED();
698 return FilePath(); // Directories have no local path.
699 }
[email protected]6b931152011-05-20 21:02:35700 return DataPathToLocalPath(origin_url, type, file_info.data_path);
[email protected]d4905e2e2011-05-13 21:56:32701}
702
[email protected]c7a4a10f2011-05-19 04:56:06703FilePath ObfuscatedFileSystemFileUtil::GetDirectoryForOriginAndType(
704 const GURL& origin, FileSystemType type, bool create) {
705 FilePath origin_dir = GetDirectoryForOrigin(origin, create);
706 if (origin_dir.empty())
707 return FilePath();
[email protected]6b931152011-05-20 21:02:35708 FilePath::StringType type_string = GetDirectoryNameForType(type);
[email protected]c7a4a10f2011-05-19 04:56:06709 if (type_string.empty()) {
710 LOG(WARNING) << "Unknown filesystem type requested:" << type;
711 return FilePath();
712 }
[email protected]6b931152011-05-20 21:02:35713 return origin_dir.Append(type_string);
[email protected]c7a4a10f2011-05-19 04:56:06714}
715
716FilePath ObfuscatedFileSystemFileUtil::GetDirectoryForOrigin(
717 const GURL& origin, bool create) {
[email protected]d4905e2e2011-05-13 21:56:32718 if (!origin_database_.get()) {
[email protected]c7a4a10f2011-05-19 04:56:06719 if (!create && !file_util::DirectoryExists(file_system_directory_)) {
720 return FilePath();
721 }
[email protected]d4905e2e2011-05-13 21:56:32722 if (!file_util::CreateDirectory(file_system_directory_)) {
[email protected]c7a4a10f2011-05-19 04:56:06723 LOG(WARNING) << "Failed to create FileSystem directory: " <<
[email protected]d4905e2e2011-05-13 21:56:32724 file_system_directory_.value();
725 return FilePath();
726 }
727 origin_database_.reset(
728 new FileSystemOriginDatabase(
729 file_system_directory_.AppendASCII(kOriginDatabaseName)));
730 }
731 FilePath directory_name;
[email protected]c7a4a10f2011-05-19 04:56:06732 // TODO(ericu): This should probably be using GetOriginIdentifierFromURL from
733 // sandbox_mount_point_provider.cc, instead of just using origin.spec().
734 if (!create && !origin_database_->HasOriginPath(origin.spec()))
735 return FilePath();
[email protected]d4905e2e2011-05-13 21:56:32736 if (!origin_database_->GetPathForOrigin(origin.spec(), &directory_name))
737 return FilePath();
[email protected]c7a4a10f2011-05-19 04:56:06738 return file_system_directory_.Append(directory_name);
[email protected]d4905e2e2011-05-13 21:56:32739}
740
[email protected]6b931152011-05-20 21:02:35741bool ObfuscatedFileSystemFileUtil::MigrateFromOldSandbox(
742 const GURL& origin_url, FileSystemType type, const FilePath& src_root) {
743 if (!DestroyDirectoryDatabase(origin_url, type))
744 return false;
745 FilePath dest_root = GetDirectoryForOriginAndType(origin_url, type, true);
746 if (dest_root.empty())
747 return false;
748 FileSystemDirectoryDatabase* db = GetDirectoryDatabase(origin_url, type);
749 if (!db)
750 return false;
751
752 file_util::FileEnumerator file_enum(src_root, true,
753 static_cast<file_util::FileEnumerator::FILE_TYPE>(
754 file_util::FileEnumerator::FILES |
755 file_util::FileEnumerator::DIRECTORIES));
756 FilePath src_full_path;
757 size_t root_path_length = src_root.value().length() + 1; // +1 for the slash
758 while (!(src_full_path = file_enum.Next()).empty()) {
759 file_util::FileEnumerator::FindInfo info;
760 file_enum.GetFindInfo(&info);
761 FilePath relative_virtual_path =
762 FilePath(src_full_path.value().substr(root_path_length));
763 if (relative_virtual_path.empty()) {
764 LOG(WARNING) << "Failed to convert path to relative: " <<
765 src_full_path.value();
766 return false;
767 }
768 FileId file_id;
769 if (db->GetFileWithPath(relative_virtual_path, &file_id)) {
770 NOTREACHED(); // File already exists.
771 return false;
772 }
773 if (!db->GetFileWithPath(relative_virtual_path.DirName(), &file_id)) {
774 NOTREACHED(); // Parent doesn't exist.
775 return false;
776 }
777
778 FileInfo file_info;
779 file_info.name = src_full_path.BaseName().value();
780 if (file_util::FileEnumerator::IsDirectory(info)) {
781#if defined(OS_WIN)
782 file_info.modification_time =
783 base::Time::FromFileTime(info.ftLastWriteTime);
784#elif defined(OS_POSIX)
785 file_info.modification_time = base::Time::FromTimeT(info.stat.st_mtime);
786#endif
787 } else {
788 file_info.data_path =
789 FilePath(kLegacyDataDirectory).Append(relative_virtual_path);
790 }
791 file_info.parent_id = file_id;
792 if (!db->AddFileInfo(file_info, &file_id)) {
793 NOTREACHED();
794 return false;
795 }
796 }
797 // TODO(ericu): Should we adjust the mtime of the root directory to match as
798 // well?
799 FilePath legacy_dest_dir = dest_root.Append(kLegacyDataDirectory);
800 return file_util::Move(src_root, legacy_dest_dir);
801}
802
803FilePath::StringType ObfuscatedFileSystemFileUtil::GetDirectoryNameForType(
804 FileSystemType type) const {
805 switch (type) {
806 case kFileSystemTypeTemporary:
807 return kTemporaryDirectoryName;
808 case kFileSystemTypePersistent:
809 return kPersistentDirectoryName;
810 case kFileSystemTypeUnknown:
811 default:
812 return FilePath::StringType();
813 }
814}
815
816FilePath ObfuscatedFileSystemFileUtil::DataPathToLocalPath(
817 const GURL& origin, FileSystemType type, const FilePath& data_path) {
818 FilePath root = GetDirectoryForOriginAndType(origin, type, false);
819 if (root.empty())
820 return root;
821 return root.Append(data_path);
822}
823
824FilePath ObfuscatedFileSystemFileUtil::LocalPathToDataPath(
825 const GURL& origin, FileSystemType type, const FilePath& local_path) {
826 FilePath root = GetDirectoryForOriginAndType(origin, type, false);
827 if (root.empty())
828 return root;
829 // This removes the root, including the trailing slash, leaving a relative
830 // path.
831 return FilePath(local_path.value().substr(root.value().length() + 1));
832}
833
[email protected]d4905e2e2011-05-13 21:56:32834// TODO: How to do the whole validation-without-creation thing? We may not have
[email protected]c7a4a10f2011-05-19 04:56:06835// quota even to create the database. Ah, in that case don't even get here?
836// Still doesn't answer the quota issue, though.
[email protected]d4905e2e2011-05-13 21:56:32837FileSystemDirectoryDatabase* ObfuscatedFileSystemFileUtil::GetDirectoryDatabase(
838 const GURL& origin, FileSystemType type) {
839
[email protected]0a7328532011-05-13 23:54:43840 MarkUsed();
[email protected]d4905e2e2011-05-13 21:56:32841 std::string type_string =
842 FileSystemPathManager::GetFileSystemTypeString(type);
843 if (type_string.empty()) {
844 LOG(WARNING) << "Unknown filesystem type requested:" << type;
845 return NULL;
846 }
[email protected]c7a4a10f2011-05-19 04:56:06847 // TODO(ericu): This should probably be using GetOriginIdentifierFromURL from
848 // sandbox_mount_point_provider.cc, instead of just using origin.spec().
[email protected]d4905e2e2011-05-13 21:56:32849 std::string key = origin.spec() + type_string;
850 DirectoryMap::iterator iter = directories_.find(key);
851 if (iter != directories_.end())
852 return iter->second;
853
[email protected]c7a4a10f2011-05-19 04:56:06854 FilePath path = GetDirectoryForOriginAndType(origin, type, true);
855 if (path.empty())
856 return NULL;
[email protected]d4905e2e2011-05-13 21:56:32857 if (!file_util::DirectoryExists(path)) {
858 if (!file_util::CreateDirectory(path)) {
[email protected]c7a4a10f2011-05-19 04:56:06859 LOG(WARNING) << "Failed to origin+type directory: " << path.value();
[email protected]d4905e2e2011-05-13 21:56:32860 return NULL;
861 }
862 }
863 path = path.AppendASCII(kDirectoryDatabaseName);
864 FileSystemDirectoryDatabase* database = new FileSystemDirectoryDatabase(path);
865 directories_[key] = database;
866 return database;
867}
868
[email protected]0a7328532011-05-13 23:54:43869void ObfuscatedFileSystemFileUtil::MarkUsed() {
870 if (timer_.IsRunning())
871 timer_.Reset();
872 else
873 timer_.Start(base::TimeDelta::FromSeconds(kFlushDelaySeconds), this,
874 &ObfuscatedFileSystemFileUtil::DropDatabases);
875}
876
[email protected]d4905e2e2011-05-13 21:56:32877void ObfuscatedFileSystemFileUtil::DropDatabases() {
878 origin_database_.reset();
879 STLDeleteContainerPairSecondPointers(
880 directories_.begin(), directories_.end());
881 directories_.clear();
882}
883
[email protected]6b931152011-05-20 21:02:35884bool ObfuscatedFileSystemFileUtil::DestroyDirectoryDatabase(
885 const GURL& origin, FileSystemType type) {
886 std::string type_string =
887 FileSystemPathManager::GetFileSystemTypeString(type);
888 if (type_string.empty()) {
889 LOG(WARNING) << "Unknown filesystem type requested:" << type;
890 return true;
891 }
892 // TODO(ericu): This should probably be using GetOriginIdentifierFromURL from
893 // sandbox_mount_point_provider.cc, instead of just using origin.spec().
894 std::string key = origin.spec() + type_string;
895 DirectoryMap::iterator iter = directories_.find(key);
896 if (iter != directories_.end())
897 directories_.erase(iter);
898
899 FilePath path = GetDirectoryForOriginAndType(origin, type, false);
900 if (path.empty())
901 return true;
902 if (!file_util::DirectoryExists(path))
903 return true;
904 path = path.AppendASCII(kDirectoryDatabaseName);
905 return FileSystemDirectoryDatabase::DestroyDatabase(path);
906}
907
[email protected]d4905e2e2011-05-13 21:56:32908} // namespace fileapi