Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / chromeos / drive / resource_metadata.cc
blob5d2497e5685adc50ac8b172cd1488b1b481ff195
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 "chrome/browser/chromeos/drive/resource_metadata.h"
7 #include "base/guid.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/sys_info.h"
11 #include "chrome/browser/chromeos/drive/drive.pb.h"
12 #include "chrome/browser/chromeos/drive/file_system_util.h"
13 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
14 #include "content/public/browser/browser_thread.h"
16 using content::BrowserThread;
18 namespace drive {
19 namespace {
21 // Sets entry's base name from its title and other attributes.
22 void SetBaseNameFromTitle(ResourceEntry* entry) {
23 std::string base_name = entry->title();
24 if (entry->has_file_specific_info() &&
25 entry->file_specific_info().is_hosted_document()) {
26 base_name += entry->file_specific_info().document_extension();
28 entry->set_base_name(util::NormalizeFileName(base_name));
31 // Returns true if enough disk space is available for DB operation.
32 // TODO(hashimoto): Merge this with FileCache's FreeDiskSpaceGetterInterface.
33 bool EnoughDiskSpaceIsAvailableForDBOperation(const base::FilePath& path) {
34 const int64 kRequiredDiskSpaceInMB = 128; // 128 MB seems to be large enough.
35 return base::SysInfo::AmountOfFreeDiskSpace(path) >=
36 kRequiredDiskSpaceInMB * (1 << 20);
39 // Runs |callback| with arguments.
40 void RunGetResourceEntryCallback(const GetResourceEntryCallback& callback,
41 scoped_ptr<ResourceEntry> entry,
42 FileError error) {
43 DCHECK(!callback.is_null());
45 if (error != FILE_ERROR_OK)
46 entry.reset();
47 callback.Run(error, entry.Pass());
50 // Runs |callback| with arguments.
51 void RunReadDirectoryCallback(const ReadDirectoryCallback& callback,
52 scoped_ptr<ResourceEntryVector> entries,
53 FileError error) {
54 DCHECK(!callback.is_null());
56 if (error != FILE_ERROR_OK)
57 entries.reset();
58 callback.Run(error, entries.Pass());
61 } // namespace
63 namespace internal {
65 ResourceMetadata::ResourceMetadata(
66 ResourceMetadataStorage* storage,
67 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
68 : blocking_task_runner_(blocking_task_runner),
69 storage_(storage),
70 weak_ptr_factory_(this) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
74 FileError ResourceMetadata::Initialize() {
75 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
77 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
78 return FILE_ERROR_NO_LOCAL_SPACE;
80 if (!SetUpDefaultEntries())
81 return FILE_ERROR_FAILED;
83 return FILE_ERROR_OK;
86 void ResourceMetadata::Destroy() {
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
89 weak_ptr_factory_.InvalidateWeakPtrs();
90 blocking_task_runner_->PostTask(
91 FROM_HERE,
92 base::Bind(&ResourceMetadata::DestroyOnBlockingPool,
93 base::Unretained(this)));
96 FileError ResourceMetadata::Reset() {
97 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
99 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
100 return FILE_ERROR_NO_LOCAL_SPACE;
102 if (!storage_->SetLargestChangestamp(0) ||
103 !RemoveEntryRecursively(util::kDriveGrandRootLocalId) ||
104 !SetUpDefaultEntries())
105 return FILE_ERROR_FAILED;
107 return FILE_ERROR_OK;
110 ResourceMetadata::~ResourceMetadata() {
111 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
114 bool ResourceMetadata::SetUpDefaultEntries() {
115 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
117 // Initialize "/drive", "/drive/other", "drive/trash" and "drive/root".
118 ResourceEntry entry;
119 if (!storage_->GetEntry(util::kDriveGrandRootLocalId, &entry)) {
120 ResourceEntry root;
121 root.mutable_file_info()->set_is_directory(true);
122 root.set_local_id(util::kDriveGrandRootLocalId);
123 root.set_title(util::kDriveGrandRootDirName);
124 SetBaseNameFromTitle(&root);
125 if (!storage_->PutEntry(root))
126 return false;
127 } else if (!entry.resource_id().empty()) {
128 // Old implementations used kDriveGrandRootLocalId as a resource ID.
129 entry.clear_resource_id();
130 if (!storage_->PutEntry(entry))
131 return false;
133 if (!storage_->GetEntry(util::kDriveOtherDirLocalId, &entry)) {
134 ResourceEntry other_dir;
135 other_dir.mutable_file_info()->set_is_directory(true);
136 other_dir.set_local_id(util::kDriveOtherDirLocalId);
137 other_dir.set_parent_local_id(util::kDriveGrandRootLocalId);
138 other_dir.set_title(util::kDriveOtherDirName);
139 if (!PutEntryUnderDirectory(other_dir))
140 return false;
141 } else if (!entry.resource_id().empty()) {
142 // Old implementations used kDriveOtherDirLocalId as a resource ID.
143 entry.clear_resource_id();
144 if (!storage_->PutEntry(entry))
145 return false;
147 if (!storage_->GetEntry(util::kDriveTrashDirLocalId, &entry)) {
148 ResourceEntry trash_dir;
149 trash_dir.mutable_file_info()->set_is_directory(true);
150 trash_dir.set_local_id(util::kDriveTrashDirLocalId);
151 trash_dir.set_parent_local_id(util::kDriveGrandRootLocalId);
152 trash_dir.set_title(util::kDriveTrashDirName);
153 if (!PutEntryUnderDirectory(trash_dir))
154 return false;
156 if (storage_->GetChild(util::kDriveGrandRootLocalId,
157 util::kDriveMyDriveRootDirName).empty()) {
158 ResourceEntry mydrive;
159 mydrive.mutable_file_info()->set_is_directory(true);
160 mydrive.set_parent_local_id(util::kDriveGrandRootLocalId);
161 mydrive.set_title(util::kDriveMyDriveRootDirName);
163 std::string local_id;
164 if (AddEntry(mydrive, &local_id) != FILE_ERROR_OK)
165 return false;
167 return true;
170 void ResourceMetadata::DestroyOnBlockingPool() {
171 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
172 delete this;
175 int64 ResourceMetadata::GetLargestChangestamp() {
176 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
177 return storage_->GetLargestChangestamp();
180 FileError ResourceMetadata::SetLargestChangestamp(int64 value) {
181 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
183 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
184 return FILE_ERROR_NO_LOCAL_SPACE;
186 return storage_->SetLargestChangestamp(value) ?
187 FILE_ERROR_OK : FILE_ERROR_FAILED;
190 FileError ResourceMetadata::AddEntry(const ResourceEntry& entry,
191 std::string* out_id) {
192 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
193 DCHECK(entry.local_id().empty());
195 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
196 return FILE_ERROR_NO_LOCAL_SPACE;
198 ResourceEntry parent;
199 if (!storage_->GetEntry(entry.parent_local_id(), &parent) ||
200 !parent.file_info().is_directory())
201 return FILE_ERROR_NOT_FOUND;
203 // Multiple entries with the same resource ID should not be present.
204 std::string local_id;
205 ResourceEntry existing_entry;
206 if (!entry.resource_id().empty() &&
207 storage_->GetIdByResourceId(entry.resource_id(), &local_id) &&
208 storage_->GetEntry(local_id, &existing_entry))
209 return FILE_ERROR_EXISTS;
211 // Generate unique local ID when needed.
212 while (local_id.empty() || storage_->GetEntry(local_id, &existing_entry))
213 local_id = base::GenerateGUID();
215 ResourceEntry new_entry(entry);
216 new_entry.set_local_id(local_id);
218 if (!PutEntryUnderDirectory(new_entry))
219 return FILE_ERROR_FAILED;
221 *out_id = local_id;
222 return FILE_ERROR_OK;
225 FileError ResourceMetadata::RemoveEntry(const std::string& id) {
226 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
228 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
229 return FILE_ERROR_NO_LOCAL_SPACE;
231 // Disallow deletion of default entries.
232 if (id == util::kDriveGrandRootLocalId ||
233 id == util::kDriveOtherDirLocalId ||
234 id == util::kDriveTrashDirLocalId)
235 return FILE_ERROR_ACCESS_DENIED;
237 ResourceEntry entry;
238 if (!storage_->GetEntry(id, &entry))
239 return FILE_ERROR_NOT_FOUND;
241 if (!RemoveEntryRecursively(id))
242 return FILE_ERROR_FAILED;
243 return FILE_ERROR_OK;
246 FileError ResourceMetadata::GetResourceEntryById(const std::string& id,
247 ResourceEntry* out_entry) {
248 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
249 DCHECK(!id.empty());
250 DCHECK(out_entry);
252 return storage_->GetEntry(id, out_entry) ?
253 FILE_ERROR_OK : FILE_ERROR_NOT_FOUND;
256 void ResourceMetadata::GetResourceEntryByPathOnUIThread(
257 const base::FilePath& file_path,
258 const GetResourceEntryCallback& callback) {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
260 DCHECK(!callback.is_null());
262 scoped_ptr<ResourceEntry> entry(new ResourceEntry);
263 ResourceEntry* entry_ptr = entry.get();
264 base::PostTaskAndReplyWithResult(
265 blocking_task_runner_.get(),
266 FROM_HERE,
267 base::Bind(&ResourceMetadata::GetResourceEntryByPath,
268 base::Unretained(this),
269 file_path,
270 entry_ptr),
271 base::Bind(&RunGetResourceEntryCallback, callback, base::Passed(&entry)));
274 FileError ResourceMetadata::GetResourceEntryByPath(const base::FilePath& path,
275 ResourceEntry* out_entry) {
276 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
277 DCHECK(out_entry);
279 std::string id;
280 FileError error = GetIdByPath(path, &id);
281 if (error != FILE_ERROR_OK)
282 return error;
284 return GetResourceEntryById(id, out_entry);
287 void ResourceMetadata::ReadDirectoryByPathOnUIThread(
288 const base::FilePath& file_path,
289 const ReadDirectoryCallback& callback) {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
291 DCHECK(!callback.is_null());
293 scoped_ptr<ResourceEntryVector> entries(new ResourceEntryVector);
294 ResourceEntryVector* entries_ptr = entries.get();
295 base::PostTaskAndReplyWithResult(
296 blocking_task_runner_.get(),
297 FROM_HERE,
298 base::Bind(&ResourceMetadata::ReadDirectoryByPath,
299 base::Unretained(this),
300 file_path,
301 entries_ptr),
302 base::Bind(&RunReadDirectoryCallback, callback, base::Passed(&entries)));
305 FileError ResourceMetadata::ReadDirectoryByPath(
306 const base::FilePath& path,
307 ResourceEntryVector* out_entries) {
308 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
309 DCHECK(out_entries);
311 std::string id;
312 FileError error = GetIdByPath(path, &id);
313 if (error != FILE_ERROR_OK)
314 return error;
316 ResourceEntry entry;
317 error = GetResourceEntryById(id, &entry);
318 if (error != FILE_ERROR_OK)
319 return error;
321 if (!entry.file_info().is_directory())
322 return FILE_ERROR_NOT_A_DIRECTORY;
324 std::vector<std::string> children;
325 storage_->GetChildren(id, &children);
327 ResourceEntryVector entries(children.size());
328 for (size_t i = 0; i < children.size(); ++i) {
329 if (!storage_->GetEntry(children[i], &entries[i]))
330 return FILE_ERROR_FAILED;
332 out_entries->swap(entries);
333 return FILE_ERROR_OK;
336 FileError ResourceMetadata::RefreshEntry(const ResourceEntry& entry) {
337 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
338 // TODO(hashimoto): Return an error if the operation will result in having
339 // multiple entries with the same resource ID.
341 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
342 return FILE_ERROR_NO_LOCAL_SPACE;
344 ResourceEntry old_entry;
345 if (!storage_->GetEntry(entry.local_id(), &old_entry))
346 return FILE_ERROR_NOT_FOUND;
348 if (old_entry.parent_local_id().empty() || // Reject root.
349 old_entry.file_info().is_directory() != // Reject incompatible input.
350 entry.file_info().is_directory())
351 return FILE_ERROR_INVALID_OPERATION;
353 // Make sure that the new parent exists and it is a directory.
354 ResourceEntry new_parent;
355 if (!storage_->GetEntry(entry.parent_local_id(), &new_parent))
356 return FILE_ERROR_NOT_FOUND;
358 if (!new_parent.file_info().is_directory())
359 return FILE_ERROR_NOT_A_DIRECTORY;
361 // Remove from the old parent and add it to the new parent with the new data.
362 if (!PutEntryUnderDirectory(entry))
363 return FILE_ERROR_FAILED;
364 return FILE_ERROR_OK;
367 void ResourceMetadata::GetSubDirectoriesRecursively(
368 const std::string& id,
369 std::set<base::FilePath>* sub_directories) {
370 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
372 std::vector<std::string> children;
373 storage_->GetChildren(id, &children);
374 for (size_t i = 0; i < children.size(); ++i) {
375 ResourceEntry entry;
376 if (storage_->GetEntry(children[i], &entry) &&
377 entry.file_info().is_directory()) {
378 sub_directories->insert(GetFilePath(children[i]));
379 GetSubDirectoriesRecursively(children[i], sub_directories);
384 std::string ResourceMetadata::GetChildId(const std::string& parent_local_id,
385 const std::string& base_name) {
386 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
387 return storage_->GetChild(parent_local_id, base_name);
390 scoped_ptr<ResourceMetadata::Iterator> ResourceMetadata::GetIterator() {
391 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
393 return storage_->GetIterator();
396 base::FilePath ResourceMetadata::GetFilePath(const std::string& id) {
397 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
399 base::FilePath path;
400 ResourceEntry entry;
401 if (storage_->GetEntry(id, &entry)) {
402 if (!entry.parent_local_id().empty())
403 path = GetFilePath(entry.parent_local_id());
404 path = path.Append(base::FilePath::FromUTF8Unsafe(entry.base_name()));
406 return path;
409 FileError ResourceMetadata::GetIdByPath(const base::FilePath& file_path,
410 std::string* out_id) {
411 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
413 // Start from the root.
414 std::vector<base::FilePath::StringType> components;
415 file_path.GetComponents(&components);
416 if (components.empty() || components[0] != util::kDriveGrandRootDirName)
417 return FILE_ERROR_NOT_FOUND;
419 // Iterate over the remaining components.
420 std::string id = util::kDriveGrandRootLocalId;
421 for (size_t i = 1; i < components.size(); ++i) {
422 const std::string component = base::FilePath(components[i]).AsUTF8Unsafe();
423 id = storage_->GetChild(id, component);
424 if (id.empty())
425 return FILE_ERROR_NOT_FOUND;
427 *out_id = id;
428 return FILE_ERROR_OK;
431 FileError ResourceMetadata::GetIdByResourceId(const std::string& resource_id,
432 std::string* out_local_id) {
433 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
435 return storage_->GetIdByResourceId(resource_id, out_local_id) ?
436 FILE_ERROR_OK : FILE_ERROR_NOT_FOUND;
439 bool ResourceMetadata::PutEntryUnderDirectory(const ResourceEntry& entry) {
440 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
441 DCHECK(!entry.local_id().empty());
442 DCHECK(!entry.parent_local_id().empty());
444 ResourceEntry updated_entry(entry);
446 // The entry name may have been changed due to prior name de-duplication.
447 // We need to first restore the file name based on the title before going
448 // through name de-duplication again when it is added to another directory.
449 SetBaseNameFromTitle(&updated_entry);
451 // Do file name de-duplication - Keep changing |entry|'s name until there is
452 // no other entry with the same name under the parent.
453 int modifier = 0;
454 std::string new_base_name = updated_entry.base_name();
455 while (true) {
456 const std::string existing_entry_id =
457 storage_->GetChild(entry.parent_local_id(), new_base_name);
458 if (existing_entry_id.empty() || existing_entry_id == entry.local_id())
459 break;
461 base::FilePath new_path =
462 base::FilePath::FromUTF8Unsafe(updated_entry.base_name());
463 new_path =
464 new_path.InsertBeforeExtension(base::StringPrintf(" (%d)", ++modifier));
465 // The new filename must be different from the previous one.
466 DCHECK_NE(new_base_name, new_path.AsUTF8Unsafe());
467 new_base_name = new_path.AsUTF8Unsafe();
469 updated_entry.set_base_name(new_base_name);
471 // Add the entry to resource map.
472 return storage_->PutEntry(updated_entry);
475 bool ResourceMetadata::RemoveEntryRecursively(const std::string& id) {
476 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
478 ResourceEntry entry;
479 if (!storage_->GetEntry(id, &entry))
480 return false;
482 if (entry.file_info().is_directory()) {
483 std::vector<std::string> children;
484 storage_->GetChildren(id, &children);
485 for (size_t i = 0; i < children.size(); ++i) {
486 if (!RemoveEntryRecursively(children[i]))
487 return false;
490 return storage_->RemoveEntry(id);
493 } // namespace internal
494 } // namespace drive