OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/browser/chromeos/drive/resource_metadata.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/guid.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 "chrome/browser/chromeos/drive/file_cache.h" | |
16 #include "chrome/browser/chromeos/drive/file_system_core_util.h" | |
17 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h" | |
18 #include "components/drive/drive.pb.h" | |
19 | |
20 namespace drive { | |
21 namespace internal { | |
22 namespace { | |
23 | |
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); | |
30 } | |
31 | |
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(); | |
38 } | |
39 | |
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, | |
46 bool* result) { | |
47 std::string existing_entry_id; | |
48 FileError error = storage->GetChild(parent_local_id, base_name, | |
49 &existing_entry_id); | |
50 if (error == FILE_ERROR_OK) | |
51 *result = existing_entry_id == local_id; | |
52 else if (error == FILE_ERROR_NOT_FOUND) | |
53 *result = true; | |
54 else | |
55 return error; | |
56 return FILE_ERROR_OK; | |
57 } | |
58 | |
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; | |
64 } | |
65 | |
66 } // namespace | |
67 | |
68 ResourceMetadata::ResourceMetadata( | |
69 ResourceMetadataStorage* storage, | |
70 FileCache* cache, | |
71 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) | |
72 : blocking_task_runner_(blocking_task_runner), | |
73 storage_(storage), | |
74 cache_(cache) { | |
75 } | |
76 | |
77 FileError ResourceMetadata::Initialize() { | |
78 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
79 return SetUpDefaultEntries(); | |
80 } | |
81 | |
82 void ResourceMetadata::Destroy() { | |
83 DCHECK(thread_checker_.CalledOnValidThread()); | |
84 | |
85 blocking_task_runner_->PostTask( | |
86 FROM_HERE, | |
87 base::Bind(&ResourceMetadata::DestroyOnBlockingPool, | |
88 base::Unretained(this))); | |
89 } | |
90 | |
91 FileError ResourceMetadata::Reset() { | |
92 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
93 | |
94 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) | |
95 return FILE_ERROR_NO_LOCAL_SPACE; | |
96 | |
97 FileError error = storage_->SetLargestChangestamp(0); | |
98 if (error != FILE_ERROR_OK) | |
99 return error; | |
100 | |
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) | |
107 return error; | |
108 } | |
109 } | |
110 if (it->HasError()) | |
111 return FILE_ERROR_FAILED; | |
112 | |
113 return SetUpDefaultEntries(); | |
114 } | |
115 | |
116 ResourceMetadata::~ResourceMetadata() { | |
117 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
118 } | |
119 | |
120 FileError ResourceMetadata::SetUpDefaultEntries() { | |
121 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
122 | |
123 // Initialize "/drive". | |
124 ResourceEntry entry; | |
125 FileError error = storage_->GetEntry(util::kDriveGrandRootLocalId, &entry); | |
126 if (error == FILE_ERROR_NOT_FOUND) { | |
127 ResourceEntry root; | |
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) | |
134 return error; | |
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) | |
141 return error; | |
142 } | |
143 } else { | |
144 return error; | |
145 } | |
146 | |
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) | |
157 return error; | |
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) | |
164 return error; | |
165 } | |
166 } else { | |
167 return error; | |
168 } | |
169 | |
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) | |
180 return error; | |
181 } else if (error != FILE_ERROR_OK) { | |
182 return error; | |
183 } | |
184 | |
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); | |
194 | |
195 std::string local_id; | |
196 error = AddEntry(mydrive, &local_id); | |
197 if (error != FILE_ERROR_OK) | |
198 return error; | |
199 } else if (error != FILE_ERROR_OK) { | |
200 return error; | |
201 } | |
202 return FILE_ERROR_OK; | |
203 } | |
204 | |
205 void ResourceMetadata::DestroyOnBlockingPool() { | |
206 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
207 delete this; | |
208 } | |
209 | |
210 FileError ResourceMetadata::GetLargestChangestamp(int64* out_value) { | |
211 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
212 return storage_->GetLargestChangestamp(out_value); | |
213 } | |
214 | |
215 FileError ResourceMetadata::SetLargestChangestamp(int64 value) { | |
216 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
217 | |
218 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) | |
219 return FILE_ERROR_NO_LOCAL_SPACE; | |
220 | |
221 return storage_->SetLargestChangestamp(value); | |
222 } | |
223 | |
224 FileError ResourceMetadata::AddEntry(const ResourceEntry& entry, | |
225 std::string* out_id) { | |
226 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
227 DCHECK(entry.local_id().empty()); | |
228 | |
229 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) | |
230 return FILE_ERROR_NO_LOCAL_SPACE; | |
231 | |
232 ResourceEntry parent; | |
233 FileError error = storage_->GetEntry(entry.parent_local_id(), &parent); | |
234 if (error != FILE_ERROR_OK) | |
235 return error; | |
236 if (!parent.file_info().is_directory()) | |
237 return FILE_ERROR_NOT_A_DIRECTORY; | |
238 | |
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); | |
246 | |
247 if (error == FILE_ERROR_OK) | |
248 return FILE_ERROR_EXISTS; | |
249 else if (error != FILE_ERROR_NOT_FOUND) | |
250 return error; | |
251 } | |
252 | |
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(); | |
257 | |
258 ResourceEntry new_entry(entry); | |
259 new_entry.set_local_id(local_id); | |
260 | |
261 error = PutEntryUnderDirectory(new_entry); | |
262 if (error != FILE_ERROR_OK) | |
263 return error; | |
264 | |
265 *out_id = local_id; | |
266 return FILE_ERROR_OK; | |
267 } | |
268 | |
269 FileError ResourceMetadata::RemoveEntry(const std::string& id) { | |
270 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
271 | |
272 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) | |
273 return FILE_ERROR_NO_LOCAL_SPACE; | |
274 | |
275 // Disallow deletion of default entries. | |
276 if (IsImmutableEntry(id)) | |
277 return FILE_ERROR_ACCESS_DENIED; | |
278 | |
279 ResourceEntry entry; | |
280 FileError error = storage_->GetEntry(id, &entry); | |
281 if (error != FILE_ERROR_OK) | |
282 return error; | |
283 | |
284 return RemoveEntryRecursively(id); | |
285 } | |
286 | |
287 FileError ResourceMetadata::GetResourceEntryById(const std::string& id, | |
288 ResourceEntry* out_entry) { | |
289 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
290 DCHECK(!id.empty()); | |
291 DCHECK(out_entry); | |
292 | |
293 return storage_->GetEntry(id, out_entry); | |
294 } | |
295 | |
296 FileError ResourceMetadata::GetResourceEntryByPath(const base::FilePath& path, | |
297 ResourceEntry* out_entry) { | |
298 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
299 DCHECK(out_entry); | |
300 | |
301 std::string id; | |
302 FileError error = GetIdByPath(path, &id); | |
303 if (error != FILE_ERROR_OK) | |
304 return error; | |
305 | |
306 return GetResourceEntryById(id, out_entry); | |
307 } | |
308 | |
309 FileError ResourceMetadata::ReadDirectoryByPath( | |
310 const base::FilePath& path, | |
311 ResourceEntryVector* out_entries) { | |
312 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
313 DCHECK(out_entries); | |
314 | |
315 std::string id; | |
316 FileError error = GetIdByPath(path, &id); | |
317 if (error != FILE_ERROR_OK) | |
318 return error; | |
319 return ReadDirectoryById(id, out_entries); | |
320 } | |
321 | |
322 FileError ResourceMetadata::ReadDirectoryById( | |
323 const std::string& id, | |
324 ResourceEntryVector* out_entries) { | |
325 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
326 DCHECK(out_entries); | |
327 | |
328 ResourceEntry entry; | |
329 FileError error = GetResourceEntryById(id, &entry); | |
330 if (error != FILE_ERROR_OK) | |
331 return error; | |
332 | |
333 if (!entry.file_info().is_directory()) | |
334 return FILE_ERROR_NOT_A_DIRECTORY; | |
335 | |
336 std::vector<std::string> children; | |
337 error = storage_->GetChildren(id, &children); | |
338 if (error != FILE_ERROR_OK) | |
339 return error; | |
340 | |
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) | |
345 return error; | |
346 } | |
347 out_entries->swap(entries); | |
348 return FILE_ERROR_OK; | |
349 } | |
350 | |
351 FileError ResourceMetadata::RefreshEntry(const ResourceEntry& entry) { | |
352 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
353 | |
354 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) | |
355 return FILE_ERROR_NO_LOCAL_SPACE; | |
356 | |
357 ResourceEntry old_entry; | |
358 FileError error = storage_->GetEntry(entry.local_id(), &old_entry); | |
359 if (error != FILE_ERROR_OK) | |
360 return error; | |
361 | |
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; | |
366 | |
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); | |
371 switch (error) { | |
372 case FILE_ERROR_OK: | |
373 if (local_id != entry.local_id()) | |
374 return FILE_ERROR_INVALID_OPERATION; | |
375 break; | |
376 | |
377 case FILE_ERROR_NOT_FOUND: | |
378 break; | |
379 | |
380 default: | |
381 return error; | |
382 } | |
383 } | |
384 | |
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) | |
389 return error; | |
390 | |
391 if (!new_parent.file_info().is_directory()) | |
392 return FILE_ERROR_NOT_A_DIRECTORY; | |
393 | |
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(); | |
402 } | |
403 // Remove from the old parent and add it to the new parent with the new data. | |
404 return PutEntryUnderDirectory(updated_entry); | |
405 } | |
406 | |
407 FileError ResourceMetadata::GetSubDirectoriesRecursively( | |
408 const std::string& id, | |
409 std::set<base::FilePath>* sub_directories) { | |
410 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
411 | |
412 std::vector<std::string> children; | |
413 FileError error = storage_->GetChildren(id, &children); | |
414 if (error != FILE_ERROR_OK) | |
415 return error; | |
416 for (size_t i = 0; i < children.size(); ++i) { | |
417 ResourceEntry entry; | |
418 error = storage_->GetEntry(children[i], &entry); | |
419 if (error != FILE_ERROR_OK) | |
420 return error; | |
421 if (entry.file_info().is_directory()) { | |
422 base::FilePath path; | |
423 error = GetFilePath(children[i], &path); | |
424 if (error != FILE_ERROR_OK) | |
425 return error; | |
426 sub_directories->insert(path); | |
427 GetSubDirectoriesRecursively(children[i], sub_directories); | |
428 } | |
429 } | |
430 return FILE_ERROR_OK; | |
431 } | |
432 | |
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); | |
438 } | |
439 | |
440 scoped_ptr<ResourceMetadata::Iterator> ResourceMetadata::GetIterator() { | |
441 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
442 | |
443 return storage_->GetIterator(); | |
444 } | |
445 | |
446 FileError ResourceMetadata::GetFilePath(const std::string& id, | |
447 base::FilePath* out_file_path) { | |
448 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
449 | |
450 ResourceEntry entry; | |
451 FileError error = storage_->GetEntry(id, &entry); | |
452 if (error != FILE_ERROR_OK) | |
453 return error; | |
454 | |
455 base::FilePath path; | |
456 if (!entry.parent_local_id().empty()) { | |
457 error = GetFilePath(entry.parent_local_id(), &path); | |
458 if (error != FILE_ERROR_OK) | |
459 return error; | |
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; | |
463 } | |
464 path = path.Append(base::FilePath::FromUTF8Unsafe(entry.base_name())); | |
465 *out_file_path = path; | |
466 return FILE_ERROR_OK; | |
467 } | |
468 | |
469 FileError ResourceMetadata::GetIdByPath(const base::FilePath& file_path, | |
470 std::string* out_id) { | |
471 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
472 | |
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; | |
479 | |
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) | |
487 return error; | |
488 id = child_id; | |
489 } | |
490 *out_id = id; | |
491 return FILE_ERROR_OK; | |
492 } | |
493 | |
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); | |
498 } | |
499 | |
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()); | |
504 | |
505 std::string base_name; | |
506 FileError error = GetDeduplicatedBaseName(entry, &base_name); | |
507 if (error != FILE_ERROR_OK) | |
508 return error; | |
509 ResourceEntry updated_entry(entry); | |
510 updated_entry.set_base_name(base_name); | |
511 return storage_->PutEntry(updated_entry); | |
512 } | |
513 | |
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()); | |
520 | |
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(); | |
528 } | |
529 *base_name = util::NormalizeFileName(*base_name); | |
530 | |
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, | |
535 &can_use_name); | |
536 if (error != FILE_ERROR_OK || can_use_name) | |
537 return error; | |
538 | |
539 // Find an unused number with binary search. | |
540 int smallest_known_unused_modifier = 1; | |
541 while (true) { | |
542 error = EntryCanUseName(storage_, entry.parent_local_id(), entry.local_id(), | |
543 GetUniquifiedName(*base_name, | |
544 smallest_known_unused_modifier), | |
545 &can_use_name); | |
546 if (error != FILE_ERROR_OK) | |
547 return error; | |
548 if (can_use_name) | |
549 break; | |
550 | |
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; | |
556 } | |
557 } | |
558 | |
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; | |
563 | |
564 error = EntryCanUseName(storage_, entry.parent_local_id(), entry.local_id(), | |
565 GetUniquifiedName(*base_name, modifier), | |
566 &can_use_name); | |
567 if (error != FILE_ERROR_OK) | |
568 return error; | |
569 if (can_use_name) { | |
570 smallest_known_unused_modifier = modifier; | |
571 } else { | |
572 largest_known_used_modifier = modifier; | |
573 } | |
574 } | |
575 *base_name = GetUniquifiedName(*base_name, smallest_known_unused_modifier); | |
576 return FILE_ERROR_OK; | |
577 } | |
578 | |
579 FileError ResourceMetadata::RemoveEntryRecursively(const std::string& id) { | |
580 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
581 | |
582 ResourceEntry entry; | |
583 FileError error = storage_->GetEntry(id, &entry); | |
584 if (error != FILE_ERROR_OK) | |
585 return error; | |
586 | |
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) | |
591 return error; | |
592 for (size_t i = 0; i < children.size(); ++i) { | |
593 error = RemoveEntryRecursively(children[i]); | |
594 if (error != FILE_ERROR_OK) | |
595 return error; | |
596 } | |
597 } | |
598 | |
599 error = cache_->Remove(id); | |
600 if (error != FILE_ERROR_OK) | |
601 return error; | |
602 | |
603 return storage_->RemoveEntry(id); | |
604 } | |
605 | |
606 } // namespace internal | |
607 } // namespace drive | |
OLD | NEW |