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
3 // found in the LICENSE file.
5 #include "components/drive/resource_metadata.h"
8 #include "base/bind_helpers.h"
10 #include "base/location.h"
11 #include "base/rand_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/sys_info.h"
15 #include "components/drive/drive.pb.h"
16 #include "components/drive/file_cache.h"
17 #include "components/drive/file_system_core_util.h"
18 #include "components/drive/resource_metadata_storage.h"
24 // Returns true if enough disk space is available for DB operation.
25 // TODO(hashimoto): Merge this with FileCache's FreeDiskSpaceGetterInterface.
26 bool EnoughDiskSpaceIsAvailableForDBOperation(const base::FilePath
& path
) {
27 const int64 kRequiredDiskSpaceInMB
= 128; // 128 MB seems to be large enough.
28 return base::SysInfo::AmountOfFreeDiskSpace(path
) >=
29 kRequiredDiskSpaceInMB
* (1 << 20);
32 // Returns a file name with a uniquifier appended. (e.g. "File (1).txt")
33 std::string
GetUniquifiedName(const std::string
& name
, int uniquifier
) {
34 base::FilePath name_path
= base::FilePath::FromUTF8Unsafe(name
);
35 name_path
= name_path
.InsertBeforeExtensionASCII(
36 base::StringPrintf(" (%d)", uniquifier
));
37 return name_path
.AsUTF8Unsafe();
40 // Returns true when there is no entry with the specified name under the parent
41 // other than the specified entry.
42 FileError
EntryCanUseName(ResourceMetadataStorage
* storage
,
43 const std::string
& parent_local_id
,
44 const std::string
& local_id
,
45 const std::string
& base_name
,
47 std::string existing_entry_id
;
48 FileError error
= storage
->GetChild(parent_local_id
, base_name
,
50 if (error
== FILE_ERROR_OK
)
51 *result
= existing_entry_id
== local_id
;
52 else if (error
== FILE_ERROR_NOT_FOUND
)
59 // Returns true when the ID is used by an immutable entry.
60 bool IsImmutableEntry(const std::string
& id
) {
61 return id
== util::kDriveGrandRootLocalId
||
62 id
== util::kDriveOtherDirLocalId
||
63 id
== util::kDriveTrashDirLocalId
;
68 ResourceMetadata::ResourceMetadata(
69 ResourceMetadataStorage
* storage
,
71 scoped_refptr
<base::SequencedTaskRunner
> blocking_task_runner
)
72 : blocking_task_runner_(blocking_task_runner
),
77 FileError
ResourceMetadata::Initialize() {
78 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
79 return SetUpDefaultEntries();
82 void ResourceMetadata::Destroy() {
83 DCHECK(thread_checker_
.CalledOnValidThread());
85 blocking_task_runner_
->PostTask(
87 base::Bind(&ResourceMetadata::DestroyOnBlockingPool
,
88 base::Unretained(this)));
91 FileError
ResourceMetadata::Reset() {
92 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
94 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_
->directory_path()))
95 return FILE_ERROR_NO_LOCAL_SPACE
;
97 FileError error
= storage_
->SetLargestChangestamp(0);
98 if (error
!= FILE_ERROR_OK
)
101 // Remove all root entries.
102 scoped_ptr
<Iterator
> it
= GetIterator();
103 for (; !it
->IsAtEnd(); it
->Advance()) {
104 if (it
->GetValue().parent_local_id().empty()) {
105 error
= RemoveEntryRecursively(it
->GetID());
106 if (error
!= FILE_ERROR_OK
)
111 return FILE_ERROR_FAILED
;
113 return SetUpDefaultEntries();
116 ResourceMetadata::~ResourceMetadata() {
117 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
120 FileError
ResourceMetadata::SetUpDefaultEntries() {
121 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
123 // Initialize "/drive".
125 FileError error
= storage_
->GetEntry(util::kDriveGrandRootLocalId
, &entry
);
126 if (error
== FILE_ERROR_NOT_FOUND
) {
128 root
.mutable_file_info()->set_is_directory(true);
129 root
.set_local_id(util::kDriveGrandRootLocalId
);
130 root
.set_title(util::kDriveGrandRootDirName
);
131 root
.set_base_name(util::kDriveGrandRootDirName
);
132 error
= storage_
->PutEntry(root
);
133 if (error
!= FILE_ERROR_OK
)
135 } else if (error
== FILE_ERROR_OK
) {
136 if (!entry
.resource_id().empty()) {
137 // Old implementations used kDriveGrandRootLocalId as a resource ID.
138 entry
.clear_resource_id();
139 error
= storage_
->PutEntry(entry
);
140 if (error
!= FILE_ERROR_OK
)
147 // Initialize "/drive/other".
148 error
= storage_
->GetEntry(util::kDriveOtherDirLocalId
, &entry
);
149 if (error
== FILE_ERROR_NOT_FOUND
) {
150 ResourceEntry other_dir
;
151 other_dir
.mutable_file_info()->set_is_directory(true);
152 other_dir
.set_local_id(util::kDriveOtherDirLocalId
);
153 other_dir
.set_parent_local_id(util::kDriveGrandRootLocalId
);
154 other_dir
.set_title(util::kDriveOtherDirName
);
155 error
= PutEntryUnderDirectory(other_dir
);
156 if (error
!= FILE_ERROR_OK
)
158 } else if (error
== FILE_ERROR_OK
) {
159 if (!entry
.resource_id().empty()) {
160 // Old implementations used kDriveOtherDirLocalId as a resource ID.
161 entry
.clear_resource_id();
162 error
= storage_
->PutEntry(entry
);
163 if (error
!= FILE_ERROR_OK
)
170 // Initialize "drive/trash".
171 error
= storage_
->GetEntry(util::kDriveTrashDirLocalId
, &entry
);
172 if (error
== FILE_ERROR_NOT_FOUND
) {
173 ResourceEntry trash_dir
;
174 trash_dir
.mutable_file_info()->set_is_directory(true);
175 trash_dir
.set_local_id(util::kDriveTrashDirLocalId
);
176 trash_dir
.set_parent_local_id(util::kDriveGrandRootLocalId
);
177 trash_dir
.set_title(util::kDriveTrashDirName
);
178 error
= PutEntryUnderDirectory(trash_dir
);
179 if (error
!= FILE_ERROR_OK
)
181 } else if (error
!= FILE_ERROR_OK
) {
185 // Initialize "drive/root".
186 std::string child_id
;
187 error
= storage_
->GetChild(
188 util::kDriveGrandRootLocalId
, util::kDriveMyDriveRootDirName
, &child_id
);
189 if (error
== FILE_ERROR_NOT_FOUND
) {
190 ResourceEntry mydrive
;
191 mydrive
.mutable_file_info()->set_is_directory(true);
192 mydrive
.set_parent_local_id(util::kDriveGrandRootLocalId
);
193 mydrive
.set_title(util::kDriveMyDriveRootDirName
);
195 std::string local_id
;
196 error
= AddEntry(mydrive
, &local_id
);
197 if (error
!= FILE_ERROR_OK
)
199 } else if (error
!= FILE_ERROR_OK
) {
202 return FILE_ERROR_OK
;
205 void ResourceMetadata::DestroyOnBlockingPool() {
206 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
210 FileError
ResourceMetadata::GetLargestChangestamp(int64
* out_value
) {
211 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
212 return storage_
->GetLargestChangestamp(out_value
);
215 FileError
ResourceMetadata::SetLargestChangestamp(int64 value
) {
216 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
218 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_
->directory_path()))
219 return FILE_ERROR_NO_LOCAL_SPACE
;
221 return storage_
->SetLargestChangestamp(value
);
224 FileError
ResourceMetadata::AddEntry(const ResourceEntry
& entry
,
225 std::string
* out_id
) {
226 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
227 DCHECK(entry
.local_id().empty());
229 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_
->directory_path()))
230 return FILE_ERROR_NO_LOCAL_SPACE
;
232 ResourceEntry parent
;
233 FileError error
= storage_
->GetEntry(entry
.parent_local_id(), &parent
);
234 if (error
!= FILE_ERROR_OK
)
236 if (!parent
.file_info().is_directory())
237 return FILE_ERROR_NOT_A_DIRECTORY
;
239 // Multiple entries with the same resource ID should not be present.
240 std::string local_id
;
241 ResourceEntry existing_entry
;
242 if (!entry
.resource_id().empty()) {
243 error
= storage_
->GetIdByResourceId(entry
.resource_id(), &local_id
);
244 if (error
== FILE_ERROR_OK
)
245 error
= storage_
->GetEntry(local_id
, &existing_entry
);
247 if (error
== FILE_ERROR_OK
)
248 return FILE_ERROR_EXISTS
;
249 else if (error
!= FILE_ERROR_NOT_FOUND
)
253 // Generate unique local ID when needed.
254 // We don't check for ID collisions as its probability is extremely low.
255 if (local_id
.empty())
256 local_id
= base::GenerateGUID();
258 ResourceEntry
new_entry(entry
);
259 new_entry
.set_local_id(local_id
);
261 error
= PutEntryUnderDirectory(new_entry
);
262 if (error
!= FILE_ERROR_OK
)
266 return FILE_ERROR_OK
;
269 FileError
ResourceMetadata::RemoveEntry(const std::string
& id
) {
270 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
272 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_
->directory_path()))
273 return FILE_ERROR_NO_LOCAL_SPACE
;
275 // Disallow deletion of default entries.
276 if (IsImmutableEntry(id
))
277 return FILE_ERROR_ACCESS_DENIED
;
280 FileError error
= storage_
->GetEntry(id
, &entry
);
281 if (error
!= FILE_ERROR_OK
)
284 return RemoveEntryRecursively(id
);
287 FileError
ResourceMetadata::GetResourceEntryById(const std::string
& id
,
288 ResourceEntry
* out_entry
) {
289 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
293 return storage_
->GetEntry(id
, out_entry
);
296 FileError
ResourceMetadata::GetResourceEntryByPath(const base::FilePath
& path
,
297 ResourceEntry
* out_entry
) {
298 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
302 FileError error
= GetIdByPath(path
, &id
);
303 if (error
!= FILE_ERROR_OK
)
306 return GetResourceEntryById(id
, out_entry
);
309 FileError
ResourceMetadata::ReadDirectoryByPath(
310 const base::FilePath
& path
,
311 ResourceEntryVector
* out_entries
) {
312 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
316 FileError error
= GetIdByPath(path
, &id
);
317 if (error
!= FILE_ERROR_OK
)
319 return ReadDirectoryById(id
, out_entries
);
322 FileError
ResourceMetadata::ReadDirectoryById(
323 const std::string
& id
,
324 ResourceEntryVector
* out_entries
) {
325 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
329 FileError error
= GetResourceEntryById(id
, &entry
);
330 if (error
!= FILE_ERROR_OK
)
333 if (!entry
.file_info().is_directory())
334 return FILE_ERROR_NOT_A_DIRECTORY
;
336 std::vector
<std::string
> children
;
337 error
= storage_
->GetChildren(id
, &children
);
338 if (error
!= FILE_ERROR_OK
)
341 ResourceEntryVector
entries(children
.size());
342 for (size_t i
= 0; i
< children
.size(); ++i
) {
343 error
= storage_
->GetEntry(children
[i
], &entries
[i
]);
344 if (error
!= FILE_ERROR_OK
)
347 out_entries
->swap(entries
);
348 return FILE_ERROR_OK
;
351 FileError
ResourceMetadata::RefreshEntry(const ResourceEntry
& entry
) {
352 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
354 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_
->directory_path()))
355 return FILE_ERROR_NO_LOCAL_SPACE
;
357 ResourceEntry old_entry
;
358 FileError error
= storage_
->GetEntry(entry
.local_id(), &old_entry
);
359 if (error
!= FILE_ERROR_OK
)
362 if (IsImmutableEntry(entry
.local_id()) ||
363 old_entry
.file_info().is_directory() != // Reject incompatible input.
364 entry
.file_info().is_directory())
365 return FILE_ERROR_INVALID_OPERATION
;
367 if (!entry
.resource_id().empty()) {
368 // Multiple entries cannot share the same resource ID.
369 std::string local_id
;
370 FileError error
= GetIdByResourceId(entry
.resource_id(), &local_id
);
373 if (local_id
!= entry
.local_id())
374 return FILE_ERROR_INVALID_OPERATION
;
377 case FILE_ERROR_NOT_FOUND
:
385 // Make sure that the new parent exists and it is a directory.
386 ResourceEntry new_parent
;
387 error
= storage_
->GetEntry(entry
.parent_local_id(), &new_parent
);
388 if (error
!= FILE_ERROR_OK
)
391 if (!new_parent
.file_info().is_directory())
392 return FILE_ERROR_NOT_A_DIRECTORY
;
394 // Do not overwrite cache states.
395 // Cache state should be changed via FileCache.
396 ResourceEntry
updated_entry(entry
);
397 if (old_entry
.file_specific_info().has_cache_state()) {
398 *updated_entry
.mutable_file_specific_info()->mutable_cache_state() =
399 old_entry
.file_specific_info().cache_state();
400 } else if (updated_entry
.file_specific_info().has_cache_state()) {
401 updated_entry
.mutable_file_specific_info()->clear_cache_state();
403 // Remove from the old parent and add it to the new parent with the new data.
404 return PutEntryUnderDirectory(updated_entry
);
407 FileError
ResourceMetadata::GetSubDirectoriesRecursively(
408 const std::string
& id
,
409 std::set
<base::FilePath
>* sub_directories
) {
410 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
412 std::vector
<std::string
> children
;
413 FileError error
= storage_
->GetChildren(id
, &children
);
414 if (error
!= FILE_ERROR_OK
)
416 for (size_t i
= 0; i
< children
.size(); ++i
) {
418 error
= storage_
->GetEntry(children
[i
], &entry
);
419 if (error
!= FILE_ERROR_OK
)
421 if (entry
.file_info().is_directory()) {
423 error
= GetFilePath(children
[i
], &path
);
424 if (error
!= FILE_ERROR_OK
)
426 sub_directories
->insert(path
);
427 GetSubDirectoriesRecursively(children
[i
], sub_directories
);
430 return FILE_ERROR_OK
;
433 FileError
ResourceMetadata::GetChildId(const std::string
& parent_local_id
,
434 const std::string
& base_name
,
435 std::string
* out_child_id
) {
436 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
437 return storage_
->GetChild(parent_local_id
, base_name
, out_child_id
);
440 scoped_ptr
<ResourceMetadata::Iterator
> ResourceMetadata::GetIterator() {
441 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
443 return storage_
->GetIterator();
446 FileError
ResourceMetadata::GetFilePath(const std::string
& id
,
447 base::FilePath
* out_file_path
) {
448 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
451 FileError error
= storage_
->GetEntry(id
, &entry
);
452 if (error
!= FILE_ERROR_OK
)
456 if (!entry
.parent_local_id().empty()) {
457 error
= GetFilePath(entry
.parent_local_id(), &path
);
458 if (error
!= FILE_ERROR_OK
)
460 } else if (entry
.local_id() != util::kDriveGrandRootLocalId
) {
461 DVLOG(1) << "Entries not under the grand root don't have paths.";
462 return FILE_ERROR_NOT_FOUND
;
464 path
= path
.Append(base::FilePath::FromUTF8Unsafe(entry
.base_name()));
465 *out_file_path
= path
;
466 return FILE_ERROR_OK
;
469 FileError
ResourceMetadata::GetIdByPath(const base::FilePath
& file_path
,
470 std::string
* out_id
) {
471 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
473 // Start from the root.
474 std::vector
<base::FilePath::StringType
> components
;
475 file_path
.GetComponents(&components
);
476 if (components
.empty() ||
477 components
[0] != util::GetDriveGrandRootPath().value())
478 return FILE_ERROR_NOT_FOUND
;
480 // Iterate over the remaining components.
481 std::string id
= util::kDriveGrandRootLocalId
;
482 for (size_t i
= 1; i
< components
.size(); ++i
) {
483 const std::string component
= base::FilePath(components
[i
]).AsUTF8Unsafe();
484 std::string child_id
;
485 FileError error
= storage_
->GetChild(id
, component
, &child_id
);
486 if (error
!= FILE_ERROR_OK
)
491 return FILE_ERROR_OK
;
494 FileError
ResourceMetadata::GetIdByResourceId(const std::string
& resource_id
,
495 std::string
* out_local_id
) {
496 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
497 return storage_
->GetIdByResourceId(resource_id
, out_local_id
);
500 FileError
ResourceMetadata::PutEntryUnderDirectory(const ResourceEntry
& entry
) {
501 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
502 DCHECK(!entry
.local_id().empty());
503 DCHECK(!entry
.parent_local_id().empty());
505 std::string base_name
;
506 FileError error
= GetDeduplicatedBaseName(entry
, &base_name
);
507 if (error
!= FILE_ERROR_OK
)
509 ResourceEntry
updated_entry(entry
);
510 updated_entry
.set_base_name(base_name
);
511 return storage_
->PutEntry(updated_entry
);
514 FileError
ResourceMetadata::GetDeduplicatedBaseName(
515 const ResourceEntry
& entry
,
516 std::string
* base_name
) {
517 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
518 DCHECK(!entry
.parent_local_id().empty());
519 DCHECK(!entry
.title().empty());
521 // The entry name may have been changed due to prior name de-duplication.
522 // We need to first restore the file name based on the title before going
523 // through name de-duplication again when it is added to another directory.
524 *base_name
= entry
.title();
525 if (entry
.has_file_specific_info() &&
526 entry
.file_specific_info().is_hosted_document()) {
527 *base_name
+= entry
.file_specific_info().document_extension();
529 *base_name
= util::NormalizeFileName(*base_name
);
531 // If |base_name| is not used, just return it.
532 bool can_use_name
= false;
533 FileError error
= EntryCanUseName(storage_
, entry
.parent_local_id(),
534 entry
.local_id(), *base_name
,
536 if (error
!= FILE_ERROR_OK
|| can_use_name
)
539 // Find an unused number with binary search.
540 int smallest_known_unused_modifier
= 1;
542 error
= EntryCanUseName(storage_
, entry
.parent_local_id(), entry
.local_id(),
543 GetUniquifiedName(*base_name
,
544 smallest_known_unused_modifier
),
546 if (error
!= FILE_ERROR_OK
)
551 const int delta
= base::RandInt(1, smallest_known_unused_modifier
);
552 if (smallest_known_unused_modifier
<= INT_MAX
- delta
) {
553 smallest_known_unused_modifier
+= delta
;
554 } else { // No luck finding an unused number. Try again.
555 smallest_known_unused_modifier
= 1;
559 int largest_known_used_modifier
= 1;
560 while (smallest_known_unused_modifier
- largest_known_used_modifier
> 1) {
561 const int modifier
= largest_known_used_modifier
+
562 (smallest_known_unused_modifier
- largest_known_used_modifier
) / 2;
564 error
= EntryCanUseName(storage_
, entry
.parent_local_id(), entry
.local_id(),
565 GetUniquifiedName(*base_name
, modifier
),
567 if (error
!= FILE_ERROR_OK
)
570 smallest_known_unused_modifier
= modifier
;
572 largest_known_used_modifier
= modifier
;
575 *base_name
= GetUniquifiedName(*base_name
, smallest_known_unused_modifier
);
576 return FILE_ERROR_OK
;
579 FileError
ResourceMetadata::RemoveEntryRecursively(const std::string
& id
) {
580 DCHECK(blocking_task_runner_
->RunsTasksOnCurrentThread());
583 FileError error
= storage_
->GetEntry(id
, &entry
);
584 if (error
!= FILE_ERROR_OK
)
587 if (entry
.file_info().is_directory()) {
588 std::vector
<std::string
> children
;
589 error
= storage_
->GetChildren(id
, &children
);
590 if (error
!= FILE_ERROR_OK
)
592 for (size_t i
= 0; i
< children
.size(); ++i
) {
593 error
= RemoveEntryRecursively(children
[i
]);
594 if (error
!= FILE_ERROR_OK
)
599 error
= cache_
->Remove(id
);
600 if (error
!= FILE_ERROR_OK
)
603 return storage_
->RemoveEntry(id
);
606 } // namespace internal