Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(551)

Side by Side Diff: webkit/chromeos/fileapi/memory_file_util.cc

Issue 8907014: Implement async verision of FileSystemFileUtil and in-memory file system for testing purposes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove file_util_async.cc Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/bind.h"
6 #include "base/message_loop.h"
7 #include "webkit/chromeos/fileapi/memory_file_util.h"
8
9 namespace {
10 const int kDefaultReadDirectoryBufferSize = 100;
11 } // namespace
12
13 namespace fileapi {
14
15 // In-memory implementation of AsyncFileStream.
16 class MemoryFileUtilAsyncFileStream : public AsyncFileStream {
17 public:
18 // |file_entry| is owened by MemoryFileUtil.
kinuko 2011/12/14 11:19:13 nit: s/owened/owned/
satorux1 2011/12/15 04:38:05 Done.
19 MemoryFileUtilAsyncFileStream(
20 MemoryFileUtil::FileEntry* file_entry, int flags)
kinuko 2011/12/14 11:19:13 style-nit: MemoryFileUtilAsyncFileStream(MemoryFi
satorux1 2011/12/15 04:38:05 Done.
21 : file_entry_(file_entry),
22 flags_(flags),
23 offset_(0) {
24 }
25
26 // AsyncFileStream override.
27 virtual void Read(char* buffer,
28 int64 length,
29 const ReadWriteCallback& callback) OVERRIDE {
kinuko 2011/12/14 11:19:13 Just to make sure, we're dispatching callbacks syn
Oleg Eterevsky 2011/12/14 15:20:59 I believe the callback must be called _after_ the
satorux1 2011/12/15 04:38:05 Done.
30 if ((flags_ & base::PLATFORM_FILE_READ) == 0) {
31 callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, 0);
32 return;
33 }
34
35 // Shorten the length so the read does not overrun.
36 length = std::min(length, file_size() - offset_);
37
38 const std::string& contents = file_entry_->contents;
39 std::copy(contents.begin() + offset_,
40 contents.begin() + offset_ + length,
41 buffer);
42 offset_ += length;
43
44 callback.Run(base::PLATFORM_FILE_OK, length);
45 }
46
47 // AsyncFileStream override.
48 virtual void Write(const char* buffer,
49 int64 length,
50 const ReadWriteCallback& callback) OVERRIDE {
51 if ((flags_ & base::PLATFORM_FILE_WRITE) == 0) {
52 callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, 0);
53 return;
54 }
55
56 // Extend the contents if needed.
57 std::string* contents = &file_entry_->contents;
58 if (offset_ + length > file_size())
59 contents->resize(offset_ + length, 0); // Fill with 0.
60
61 std::copy(buffer, buffer + length,
62 contents->begin() + offset_);
63 file_entry_->last_modified = base::Time::Now();
64 offset_ += length;
65
66 callback.Run(base::PLATFORM_FILE_OK, length);
67 }
68
69 // AsyncFileStream override.
70 virtual void Seek(int64 offset,
71 const SeekCallback& callback) OVERRIDE {
72 if (offset > file_size()) {
73 // Unlike lseek(2), we don't allow an offset larger than the file
74 // size for this file implementation.
75 callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
76 return;
77 }
78
79 offset_ = offset;
80 callback.Run(base::PLATFORM_FILE_OK);
81 }
82
83 private:
84 // Returns the file size as int64.
85 int64 file_size() const {
86 return static_cast<int64>(file_entry_->contents.size());
87 }
88
89 MemoryFileUtil::FileEntry* file_entry_;
90 const int flags_;
91 int64 offset_;
92 };
93
94 MemoryFileUtil::FileEntry::FileEntry()
95 : is_directory(false) {
96 }
97
98 MemoryFileUtil::FileEntry::~FileEntry() {
99 }
100
101 MemoryFileUtil::MemoryFileUtil(const FilePath& root_path)
102 : read_directory_buffer_size_(kDefaultReadDirectoryBufferSize) {
103 FileEntry root;
104 root.is_directory = true;
105 root.last_modified = base::Time::Now();
106
107 files_[root_path] = root;
108 }
109
110 // Depending on the flags value the flow of file opening will be one of
111 // the following:
112 //
113 // PLATFORM_FILE_OPEN:
114 // - GetFileInfo
115 // - DidGetFileInfoForOpen
116 // - OpenVerifiedFile
117 //
118 // PLATFORM_FILE_CREATE:
119 // - Create
120 // - DidCreateOrTruncateForOpen
121 // - OpenVerifiedFile
122 //
123 // PLATFORM_FILE_OPEN_ALWAYS:
124 // - GetFileInfo
125 // - DidGetFileInfoForOpen
126 // - OpenVerifiedFile OR Create
127 // DidCreateOrTruncateForOpen
128 // OpenVerifiedFile
129 //
130 // PLATFORM_FILE_CREATE_ALWAYS:
131 // - Truncate
132 // - OpenTruncatedFileOrCreate
133 // - OpenVerifiedFile OR Create
134 // DidCreateOrTruncateForOpen
135 // OpenVerifiedFile
136 //
137 // PLATFORM_FILE_OPEN_TRUNCATED:
138 // - Truncate
139 // - DidCreateOrTruncateForOpen
140 // - OpenVerifiedFile
141 //
142 void MemoryFileUtil::Open(
143 const FilePath& file_path,
144 int flags,
145 const OpenCallback& callback) {
146 int create_flag = flags & (base::PLATFORM_FILE_OPEN |
147 base::PLATFORM_FILE_CREATE |
148 base::PLATFORM_FILE_OPEN_ALWAYS |
149 base::PLATFORM_FILE_CREATE_ALWAYS |
150 base::PLATFORM_FILE_OPEN_TRUNCATED);
151 switch (create_flag) {
152 case base::PLATFORM_FILE_OPEN:
153 case base::PLATFORM_FILE_OPEN_ALWAYS:
154 GetFileInfo(
155 file_path,
156 base::Bind(&MemoryFileUtil::DidGetFileInfoForOpen,
157 base::Unretained(this), file_path, flags, callback));
158
159 break;
160
161 case base::PLATFORM_FILE_CREATE:
162 Create(
163 file_path,
164 base::Bind(&MemoryFileUtil::DidCreateOrTruncateForOpen,
165 base::Unretained(this), file_path, flags, 0, callback));
166 break;
167
168 case base::PLATFORM_FILE_CREATE_ALWAYS:
169 Truncate(
170 file_path,
171 0,
172 base::Bind(&MemoryFileUtil::OpenTruncatedFileOrCreate,
173 base::Unretained(this), file_path, flags, callback));
174
175 case base::PLATFORM_FILE_OPEN_TRUNCATED:
176 Truncate(
177 file_path,
178 0,
179 base::Bind(&MemoryFileUtil::DidCreateOrTruncateForOpen,
180 base::Unretained(this), file_path, flags, 0, callback));
181 break;
182
183 default:
184 MessageLoop::current()->PostTask(
185 FROM_HERE,
186 base::Bind(callback,
187 base::PLATFORM_FILE_ERROR_INVALID_OPERATION,
188 static_cast<AsyncFileStream*>(NULL)));
189 }
190 }
191
192 void MemoryFileUtil::GetFileInfo(
193 const FilePath& file_path,
194 const GetFileInfoCallback& callback) {
195 MessageLoop::current()->PostTask(
196 FROM_HERE,
197 base::Bind(&MemoryFileUtil::DoGetFileInfo, base::Unretained(this),
198 file_path.StripTrailingSeparators(), callback));
199 }
200
201 void MemoryFileUtil::Create(
202 const FilePath& file_path,
203 const StatusCallback& callback) {
204 MessageLoop::current()->PostTask(
205 FROM_HERE,
206 base::Bind(&MemoryFileUtil::DoCreate, base::Unretained(this),
207 file_path.StripTrailingSeparators(), false, callback));
208 }
209
210 void MemoryFileUtil::Truncate(
211 const FilePath& file_path,
212 int64 length,
213 const StatusCallback& callback) {
214 MessageLoop::current()->PostTask(
215 FROM_HERE,
216 base::Bind(&MemoryFileUtil::DoTruncate, base::Unretained(this),
217 file_path.StripTrailingSeparators(), length, callback));
218 }
219
220 void MemoryFileUtil::Touch(
221 const FilePath& file_path,
222 const base::Time& last_access_time,
223 const base::Time& last_modified_time,
224 const StatusCallback& callback) {
225 MessageLoop::current()->PostTask(
226 FROM_HERE,
227 base::Bind(&MemoryFileUtil::DoTouch, base::Unretained(this),
228 file_path.StripTrailingSeparators(),
229 last_modified_time, callback));
230 }
231
232 void MemoryFileUtil::Remove(
233 const FilePath& file_path,
234 RemoveMode mode,
235 const StatusCallback& callback) {
236 if (mode == RECURSIVE) {
kinuko 2011/12/14 11:19:13 changing this into switch/case and having default
satorux1 2011/12/15 07:04:33 Done.
237 MessageLoop::current()->PostTask(
238 FROM_HERE,
239 base::Bind(&MemoryFileUtil::DoRemoveRecursive,
240 base::Unretained(this), file_path.StripTrailingSeparators(),
241 callback));
242 } else {
243 MessageLoop::current()->PostTask(
244 FROM_HERE,
245 base::Bind(&MemoryFileUtil::DoRemoveSingleFile,
246 base::Unretained(this), file_path.StripTrailingSeparators(),
247 callback));
248 }
249 }
250
251 void MemoryFileUtil::CreateDirectory(
252 const FilePath& dir_path,
253 const StatusCallback& callback) {
254 MessageLoop::current()->PostTask(
255 FROM_HERE,
256 base::Bind(&MemoryFileUtil::DoCreate,
257 base::Unretained(this), dir_path.StripTrailingSeparators(),
258 true, callback));
259 }
260
261 void MemoryFileUtil::ReadDirectory(
262 const FilePath& dir_path,
263 const ReadDirectoryCallback& callback) {
264 MessageLoop::current()->PostTask(
265 FROM_HERE,
266 base::Bind(&MemoryFileUtil::DoReadDirectory,
267 base::Unretained(this), dir_path.StripTrailingSeparators(),
268 FilePath(), callback));
269 }
270
271 void MemoryFileUtil::DoGetFileInfo(const FilePath& file_path,
272 const GetFileInfoCallback& callback) {
273 base::PlatformFileInfo file_info;
274
275 FileIterator file_it = files_.find(file_path);
276
277 if (file_it == files_.end()) {
278 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_info);
279 return;
280 }
281 const FileEntry& file_entry = file_it->second;
282
283 file_info.size = file_entry.contents.size();
284 file_info.is_directory = file_entry.is_directory;
285 file_info.is_symbolic_link = false;
286
287 // In this file system implementation we store only one datetime. Many
288 // popular file systems do the same.
289 file_info.last_modified = file_entry.last_modified;
290 file_info.last_accessed = file_entry.last_modified;
291 file_info.creation_time = file_entry.last_modified;
292
293 callback.Run(base::PLATFORM_FILE_OK, file_info);
294 }
295
296 void MemoryFileUtil::DoCreate(
297 const FilePath& file_path,
298 bool is_directory,
299 const StatusCallback& callback) {
300 if (FileExists(file_path)) {
301 callback.Run(base::PLATFORM_FILE_ERROR_EXISTS);
302 return;
303 }
304
305 if (!IsDirectory(file_path.DirName())) {
306 callback.Run(base::PLATFORM_FILE_ERROR_FAILED);
307 return;
308 }
309
310 FileEntry file;
311 file.is_directory = is_directory;
312 file.last_modified = base::Time::Now();
313
314 files_[file_path] = file;
315 callback.Run(base::PLATFORM_FILE_OK);
316 }
317
318 void MemoryFileUtil::DoTruncate(
319 const FilePath& file_path,
320 int64 length,
321 const StatusCallback& callback) {
322 FileIterator file_it = files_.find(file_path);
323 if (file_it == files_.end()) {
324 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
325 return;
326 }
327
328 FileEntry& file = file_it->second;
329
330 // Fill the extended part with 0 if |length| is larger than the original
331 // contents size.
332 file.contents.resize(length, 0);
333 callback.Run(base::PLATFORM_FILE_OK);
334 }
335
336 void MemoryFileUtil::DoTouch(
337 const FilePath& file_path,
338 const base::Time& last_modified_time,
339 const StatusCallback& callback) {
340 FileIterator file_it = files_.find(file_path);
341 if (file_it == files_.end()) {
342 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
343 return;
344 }
345
346 FileEntry& file = file_it->second;
347
348 file.last_modified = last_modified_time;
349 callback.Run(base::PLATFORM_FILE_OK);
350 }
351
352 void MemoryFileUtil::DoRemoveSingleFile(
353 const FilePath& file_path,
354 const StatusCallback& callback) {
355 FileIterator file_it = files_.find(file_path);
356 if (file_it == files_.end()) {
357 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
358 return;
359 }
360
361 FileEntry& file = file_it->second;
362 if (file.is_directory) {
363 // Check that directory is empty.
364 FilePath path_with_separator = file_it->first.Append("/");
kinuko 2011/12/14 11:19:13 Does this (path.Append("/")) work? This looks stra
satorux1 2011/12/15 07:04:33 My bad, you are absolutely right. Besides, relying
365 FileIterator file_inside_dir = files_.lower_bound(path_with_separator);
366
367 if (file_it->first.IsParent(file_inside_dir->first)) {
368 callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE);
369 return;
370 }
371 }
372
373 files_.erase(file_it);
374 callback.Run(base::PLATFORM_FILE_OK);
375 }
376
377 void MemoryFileUtil::DoRemoveRecursive(
378 const FilePath& file_path,
379 const StatusCallback& callback) {
380 FileIterator file_it = files_.find(file_path);
381 if (file_it == files_.end()) {
382 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
383 return;
384 }
385
386 FileEntry& file = file_it->second;
387 if (!file.is_directory) {
388 files_.erase(file_it);
389 callback.Run(base::PLATFORM_FILE_OK);
390 return;
391 }
392
393 // In the following code we rely on the fact that the paths in files_
394 // are ordered lexicographically. We iterate over files_ and delete
395 // the files from files_, then delete in one go all the files from
396 // file_by_name_.
397 FilePath file_path_with_separator = file_path.Append("/");
398 FileIterator it_first = files_.lower_bound(file_path_with_separator);
399 FileIterator it_last = it_first;
400
401 while (it_last != files_.end() && file_path.IsParent(it_last->first)) {
402 ++it_last;
403 }
404 files_.erase(it_first, it_last);
405
406 // The initial path is stored without a separator in the end, so it
407 // has not been removed in the above loop.
408 files_.erase(file_it);
409 callback.Run(base::PLATFORM_FILE_OK);
410 }
411
412 void MemoryFileUtil::DoReadDirectory(
413 const FilePath& dir_path,
414 const FilePath& in_from,
415 const ReadDirectoryCallback& callback) {
416 FilePath from = in_from;
417 read_directory_buffer_.clear();
418
419 if (!FileExists(dir_path)) {
420 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND,
421 read_directory_buffer_, true);
422 return;
423 }
424
425 if (!IsDirectory(dir_path)) {
426 callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
427 read_directory_buffer_, true);
428 return;
429 }
430
431 if (from.empty())
432 from = dir_path.Append("/");
433
434 bool completed = true;
435
436 // Here we iterate over all paths starting with the prefix dir_path + '/'.
437 // It is not very efficient in case of a deep tree with many files in
438 // subdirectories. If ever we'll need efficiency from this implementation of
439 // FS, this should be changed. (It could be done by using lower_bound instead
440 // of ++ in case we've met a subdirectory path.)
441 for (ConstFileIterator it = files_.lower_bound(from);
442 it != files_.end() && dir_path.IsParent(it->first);
443 ++it) {
444 if (it->first.DirName() != dir_path) // a file in subdirectory
445 continue;
446
447 if (read_directory_buffer_.size() == read_directory_buffer_size_) {
448 from = it->first;
449 completed = false;
450 break;
451 }
452
453 const FileEntry& file = it->second;
454 DirectoryEntry entry;
455 entry.name = it->first.BaseName().value();
456 entry.is_directory = file.is_directory;
457 entry.size = file.contents.size();
458 entry.last_modified_time = file.last_modified;
459
460 read_directory_buffer_.push_back(entry);
461 }
462
463 callback.Run(base::PLATFORM_FILE_OK, read_directory_buffer_, completed);
464
465 if (!completed) {
466 MessageLoop::current()->PostTask(
467 FROM_HERE,
468 base::Bind(&MemoryFileUtil::DoReadDirectory,
469 base::Unretained(this), dir_path,
470 from, callback));
471 }
472 }
473
474 void MemoryFileUtil::OpenVerifiedFile(
475 const FilePath& file_path,
476 int flags,
477 const OpenCallback& callback) {
478 FileIterator file_it = files_.find(file_path);
479 // The existence of the file is guranteed here.
480 DCHECK(file_it != files_.end());
481
482 FileEntry* file_entry = &file_it->second;
483 callback.Run(base::PLATFORM_FILE_OK,
484 new MemoryFileUtilAsyncFileStream(file_entry, flags));
485 }
486
487 void MemoryFileUtil::DidGetFileInfoForOpen(
488 const FilePath& file_path,
489 int flags,
490 const OpenCallback& callback,
491 PlatformFileError get_info_result,
492 const base::PlatformFileInfo& file_info) {
493 if (get_info_result == base::PLATFORM_FILE_OK && file_info.is_directory) {
494 callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE, NULL);
495 return;
496 }
497
498 if (get_info_result == base::PLATFORM_FILE_OK) {
499 OpenVerifiedFile(file_path, flags, callback);
500 return;
501 }
502
503 if (get_info_result == base::PLATFORM_FILE_ERROR_NOT_FOUND &&
504 flags & base::PLATFORM_FILE_CREATE_ALWAYS) {
505 Create(file_path,
506 base::Bind(&MemoryFileUtil::DidCreateOrTruncateForOpen,
507 base::Unretained(this), file_path, flags, 0, callback));
508 return;
509 }
510
511 callback.Run(get_info_result, NULL);
512 }
513
514 void MemoryFileUtil::OpenTruncatedFileOrCreate(
515 const FilePath& file_path,
516 int flags,
517 const OpenCallback& callback,
518 PlatformFileError result) {
519 if (result == base::PLATFORM_FILE_OK) {
520 OpenVerifiedFile(file_path, flags, callback);
521 return;
522 }
523
524 if (result == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
525 Create(
526 file_path,
527 base::Bind(&MemoryFileUtil::DidCreateOrTruncateForOpen,
528 base::Unretained(this), file_path, flags, 0, callback));
529 return;
530 }
531
532 callback.Run(result, NULL);
533 }
534
535 void MemoryFileUtil::DidCreateOrTruncateForOpen(
536 const FilePath& file_path,
537 int flags,
538 int64 size,
539 const OpenCallback& callback,
540 PlatformFileError result) {
541 if (result != base::PLATFORM_FILE_OK) {
542 callback.Run(result, NULL);
543 return;
544 }
545
546 OpenVerifiedFile(file_path, flags, callback);
547 }
548
549 } // namespace file_api
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698