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.
6 #include "base/memory/weak_ptr.h"
7 #include "base/message_loop.h"
8 #include "webkit/chromeos/fileapi/memory_file_util.h"
11 const int kDefaultReadDirectoryBufferSize
= 100;
16 // In-memory implementation of AsyncFileStream.
17 class MemoryFileUtilAsyncFileStream
: public AsyncFileStream
{
19 // |file_entry| is owned by MemoryFileUtil.
20 MemoryFileUtilAsyncFileStream(MemoryFileUtil::FileEntry
* file_entry
,
22 : file_entry_(file_entry
),
25 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
28 // AsyncFileStream override.
29 virtual void Read(char* buffer
,
31 const ReadWriteCallback
& callback
) OVERRIDE
{
32 MessageLoop::current()->PostTask(
34 base::Bind(&MemoryFileUtilAsyncFileStream::DoRead
,
35 weak_ptr_factory_
.GetWeakPtr(),
36 buffer
, length
, callback
));
39 // AsyncFileStream override.
40 virtual void Write(const char* buffer
,
42 const ReadWriteCallback
& callback
) OVERRIDE
{
43 MessageLoop::current()->PostTask(
45 base::Bind(&MemoryFileUtilAsyncFileStream::DoWrite
,
46 weak_ptr_factory_
.GetWeakPtr(),
47 buffer
, length
, callback
));
50 // AsyncFileStream override.
51 virtual void Seek(int64 offset
,
52 const SeekCallback
& callback
) OVERRIDE
{
53 MessageLoop::current()->PostTask(
55 base::Bind(&MemoryFileUtilAsyncFileStream::DoSeek
,
56 weak_ptr_factory_
.GetWeakPtr(),
61 // Callback used for Read().
62 void DoRead(char* buffer
,
64 const ReadWriteCallback
& callback
) {
66 if ((flags_
& base::PLATFORM_FILE_READ
) == 0) {
67 callback
.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION
, 0);
71 // Shorten the length so the read does not overrun.
72 length
= std::min(length
, file_size() - offset_
);
74 const std::string
& contents
= file_entry_
->contents
;
75 std::copy(contents
.begin() + offset_
,
76 contents
.begin() + offset_
+ length
,
80 callback
.Run(base::PLATFORM_FILE_OK
, length
);
83 // Callback used for Write().
84 void DoWrite(const char* buffer
,
86 const ReadWriteCallback
& callback
) {
87 if ((flags_
& base::PLATFORM_FILE_WRITE
) == 0) {
88 callback
.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION
, 0);
92 // Extend the contents if needed.
93 std::string
* contents
= &file_entry_
->contents
;
94 if (offset_
+ length
> file_size())
95 contents
->resize(offset_
+ length
, 0); // Fill with 0.
97 std::copy(buffer
, buffer
+ length
,
98 contents
->begin() + offset_
);
99 file_entry_
->last_modified
= base::Time::Now();
102 callback
.Run(base::PLATFORM_FILE_OK
, length
);
105 // Callback used for Seek().
106 void DoSeek(int64 offset
,
107 const SeekCallback
& callback
) {
108 if (offset
> file_size()) {
109 // Unlike lseek(2), we don't allow an offset larger than the file
110 // size for this file implementation.
111 callback
.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION
);
116 callback
.Run(base::PLATFORM_FILE_OK
);
119 // Returns the file size as int64.
120 int64
file_size() const {
121 return static_cast<int64
>(file_entry_
->contents
.size());
124 MemoryFileUtil::FileEntry
* file_entry_
;
127 base::WeakPtrFactory
<MemoryFileUtilAsyncFileStream
> weak_ptr_factory_
;
130 MemoryFileUtil::FileEntry::FileEntry()
131 : is_directory(false) {
134 MemoryFileUtil::FileEntry::~FileEntry() {
137 MemoryFileUtil::MemoryFileUtil(const base::FilePath
& root_path
)
138 : read_directory_buffer_size_(kDefaultReadDirectoryBufferSize
) {
140 root
.is_directory
= true;
141 root
.last_modified
= base::Time::Now();
143 files_
[root_path
] = root
;
146 MemoryFileUtil::~MemoryFileUtil() {
149 // Depending on the flags value the flow of file opening will be one of
152 // PLATFORM_FILE_OPEN:
154 // - DidGetFileInfoForOpen
155 // - OpenVerifiedFile
157 // PLATFORM_FILE_CREATE:
159 // - DidCreateOrTruncateForOpen
160 // - OpenVerifiedFile
162 // PLATFORM_FILE_OPEN_ALWAYS:
164 // - DidGetFileInfoForOpen
165 // - OpenVerifiedFile OR Create
166 // DidCreateOrTruncateForOpen
169 // PLATFORM_FILE_CREATE_ALWAYS:
171 // - OpenTruncatedFileOrCreate
172 // - OpenVerifiedFile OR Create
173 // DidCreateOrTruncateForOpen
176 // PLATFORM_FILE_OPEN_TRUNCATED:
178 // - DidCreateOrTruncateForOpen
179 // - OpenVerifiedFile
181 void MemoryFileUtil::Open(
182 const base::FilePath
& file_path
,
184 const OpenCallback
& callback
) {
185 int create_flag
= flags
& (base::PLATFORM_FILE_OPEN
|
186 base::PLATFORM_FILE_CREATE
|
187 base::PLATFORM_FILE_OPEN_ALWAYS
|
188 base::PLATFORM_FILE_CREATE_ALWAYS
|
189 base::PLATFORM_FILE_OPEN_TRUNCATED
);
190 switch (create_flag
) {
191 case base::PLATFORM_FILE_OPEN
:
192 case base::PLATFORM_FILE_OPEN_ALWAYS
:
195 base::Bind(&MemoryFileUtil::DidGetFileInfoForOpen
,
196 base::Unretained(this), file_path
, flags
, callback
));
200 case base::PLATFORM_FILE_CREATE
:
203 base::Bind(&MemoryFileUtil::DidCreateOrTruncateForOpen
,
204 base::Unretained(this), file_path
, flags
, 0, callback
));
207 case base::PLATFORM_FILE_CREATE_ALWAYS
:
211 base::Bind(&MemoryFileUtil::OpenTruncatedFileOrCreate
,
212 base::Unretained(this), file_path
, flags
, callback
));
215 case base::PLATFORM_FILE_OPEN_TRUNCATED
:
219 base::Bind(&MemoryFileUtil::DidCreateOrTruncateForOpen
,
220 base::Unretained(this), file_path
, flags
, 0, callback
));
224 MessageLoop::current()->PostTask(
227 base::PLATFORM_FILE_ERROR_INVALID_OPERATION
,
228 static_cast<AsyncFileStream
*>(NULL
)));
232 void MemoryFileUtil::GetFileInfo(
233 const base::FilePath
& file_path
,
234 const GetFileInfoCallback
& callback
) {
235 MessageLoop::current()->PostTask(
237 base::Bind(&MemoryFileUtil::DoGetFileInfo
, base::Unretained(this),
238 file_path
.StripTrailingSeparators(), callback
));
241 void MemoryFileUtil::Create(
242 const base::FilePath
& file_path
,
243 const StatusCallback
& callback
) {
244 MessageLoop::current()->PostTask(
246 base::Bind(&MemoryFileUtil::DoCreate
, base::Unretained(this),
247 file_path
.StripTrailingSeparators(), false, callback
));
250 void MemoryFileUtil::Truncate(
251 const base::FilePath
& file_path
,
253 const StatusCallback
& callback
) {
254 MessageLoop::current()->PostTask(
256 base::Bind(&MemoryFileUtil::DoTruncate
, base::Unretained(this),
257 file_path
.StripTrailingSeparators(), length
, callback
));
260 void MemoryFileUtil::Touch(
261 const base::FilePath
& file_path
,
262 const base::Time
& last_access_time
,
263 const base::Time
& last_modified_time
,
264 const StatusCallback
& callback
) {
265 MessageLoop::current()->PostTask(
267 base::Bind(&MemoryFileUtil::DoTouch
, base::Unretained(this),
268 file_path
.StripTrailingSeparators(),
269 last_modified_time
, callback
));
272 void MemoryFileUtil::Remove(
273 const base::FilePath
& file_path
,
275 const StatusCallback
& callback
) {
277 MessageLoop::current()->PostTask(
279 base::Bind(&MemoryFileUtil::DoRemoveRecursive
,
280 base::Unretained(this), file_path
.StripTrailingSeparators(),
283 MessageLoop::current()->PostTask(
285 base::Bind(&MemoryFileUtil::DoRemoveSingleFile
,
286 base::Unretained(this), file_path
.StripTrailingSeparators(),
291 void MemoryFileUtil::CreateDirectory(
292 const base::FilePath
& dir_path
,
293 const StatusCallback
& callback
) {
294 MessageLoop::current()->PostTask(
296 base::Bind(&MemoryFileUtil::DoCreate
,
297 base::Unretained(this), dir_path
.StripTrailingSeparators(),
301 void MemoryFileUtil::ReadDirectory(
302 const base::FilePath
& dir_path
,
303 const ReadDirectoryCallback
& callback
) {
304 MessageLoop::current()->PostTask(
306 base::Bind(&MemoryFileUtil::DoReadDirectory
,
307 base::Unretained(this), dir_path
.StripTrailingSeparators(),
308 base::FilePath(), callback
));
311 void MemoryFileUtil::DoGetFileInfo(const base::FilePath
& file_path
,
312 const GetFileInfoCallback
& callback
) {
313 base::PlatformFileInfo file_info
;
315 FileIterator file_it
= files_
.find(file_path
);
317 if (file_it
== files_
.end()) {
318 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND
, file_info
);
321 const FileEntry
& file_entry
= file_it
->second
;
323 file_info
.size
= file_entry
.contents
.size();
324 file_info
.is_directory
= file_entry
.is_directory
;
325 file_info
.is_symbolic_link
= false;
327 // In this file system implementation we store only one datetime. Many
328 // popular file systems do the same.
329 file_info
.last_modified
= file_entry
.last_modified
;
330 file_info
.last_accessed
= file_entry
.last_modified
;
331 file_info
.creation_time
= file_entry
.last_modified
;
333 callback
.Run(base::PLATFORM_FILE_OK
, file_info
);
336 void MemoryFileUtil::DoCreate(
337 const base::FilePath
& file_path
,
339 const StatusCallback
& callback
) {
340 if (FileExists(file_path
)) {
341 callback
.Run(base::PLATFORM_FILE_ERROR_EXISTS
);
345 if (!IsDirectory(file_path
.DirName())) {
346 callback
.Run(base::PLATFORM_FILE_ERROR_FAILED
);
351 file
.is_directory
= is_directory
;
352 file
.last_modified
= base::Time::Now();
354 files_
[file_path
] = file
;
355 callback
.Run(base::PLATFORM_FILE_OK
);
358 void MemoryFileUtil::DoTruncate(
359 const base::FilePath
& file_path
,
361 const StatusCallback
& callback
) {
362 FileIterator file_it
= files_
.find(file_path
);
363 if (file_it
== files_
.end()) {
364 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND
);
368 FileEntry
& file
= file_it
->second
;
370 // Fill the extended part with 0 if |length| is larger than the original
372 file
.contents
.resize(length
, 0);
373 callback
.Run(base::PLATFORM_FILE_OK
);
376 void MemoryFileUtil::DoTouch(
377 const base::FilePath
& file_path
,
378 const base::Time
& last_modified_time
,
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
);
386 FileEntry
& file
= file_it
->second
;
388 file
.last_modified
= last_modified_time
;
389 callback
.Run(base::PLATFORM_FILE_OK
);
392 void MemoryFileUtil::DoRemoveSingleFile(
393 const base::FilePath
& file_path
,
394 const StatusCallback
& callback
) {
395 FileIterator file_it
= files_
.find(file_path
);
396 if (file_it
== files_
.end()) {
397 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND
);
401 FileEntry
& file
= file_it
->second
;
402 if (file
.is_directory
) {
403 // Check if the directory is empty. This can be done by checking if
404 // the next file is present under the directory. Note that |files_| is
405 // a map hence the file names are sorted by names.
406 FileIterator tmp_it
= file_it
;
408 if (tmp_it
!= files_
.end() && file_path
.IsParent(tmp_it
->first
)) {
409 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE
);
414 files_
.erase(file_it
);
415 callback
.Run(base::PLATFORM_FILE_OK
);
418 void MemoryFileUtil::DoRemoveRecursive(
419 const base::FilePath
& file_path
,
420 const StatusCallback
& callback
) {
421 FileIterator file_it
= files_
.find(file_path
);
422 if (file_it
== files_
.end()) {
423 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND
);
427 FileEntry
& file
= file_it
->second
;
428 if (!file
.is_directory
) {
429 files_
.erase(file_it
);
430 callback
.Run(base::PLATFORM_FILE_OK
);
434 // Remove the directory itself.
435 files_
.erase(file_it
++);
436 // Remove files under the directory.
437 while (file_it
!= files_
.end()) {
438 if (file_path
.IsParent(file_it
->first
)) {
439 files_
.erase(file_it
++);
444 callback
.Run(base::PLATFORM_FILE_OK
);
447 void MemoryFileUtil::DoReadDirectory(
448 const base::FilePath
& dir_path
,
449 const base::FilePath
& in_from
,
450 const ReadDirectoryCallback
& callback
) {
451 base::FilePath from
= in_from
;
452 read_directory_buffer_
.clear();
454 if (!FileExists(dir_path
)) {
455 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND
,
456 read_directory_buffer_
, true);
460 if (!IsDirectory(dir_path
)) {
461 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
,
462 read_directory_buffer_
, true);
469 bool completed
= true;
471 // Here we iterate over all paths in files_ starting with the prefix |from|.
472 // It is not very efficient in case of a deep tree with many files in
473 // subdirectories. If ever we'll need efficiency from this implementation of
474 // FS, this should be changed.
475 for (ConstFileIterator it
= files_
.lower_bound(from
);
478 if (dir_path
== it
->first
) // Skip the directory itself.
480 if (!dir_path
.IsParent(it
->first
)) // Not in the directory.
482 if (it
->first
.DirName() != dir_path
) // a file in subdirectory
485 if (read_directory_buffer_
.size() == read_directory_buffer_size_
) {
491 const FileEntry
& file
= it
->second
;
492 DirectoryEntry entry
;
493 entry
.name
= it
->first
.BaseName().value();
494 entry
.is_directory
= file
.is_directory
;
495 entry
.size
= file
.contents
.size();
496 entry
.last_modified_time
= file
.last_modified
;
498 read_directory_buffer_
.push_back(entry
);
501 callback
.Run(base::PLATFORM_FILE_OK
, read_directory_buffer_
, completed
);
504 MessageLoop::current()->PostTask(
506 base::Bind(&MemoryFileUtil::DoReadDirectory
,
507 base::Unretained(this), dir_path
,
512 void MemoryFileUtil::OpenVerifiedFile(
513 const base::FilePath
& file_path
,
515 const OpenCallback
& callback
) {
516 FileIterator file_it
= files_
.find(file_path
);
517 // The existence of the file is guranteed here.
518 DCHECK(file_it
!= files_
.end());
520 FileEntry
* file_entry
= &file_it
->second
;
521 callback
.Run(base::PLATFORM_FILE_OK
,
522 new MemoryFileUtilAsyncFileStream(file_entry
, flags
));
525 void MemoryFileUtil::DidGetFileInfoForOpen(
526 const base::FilePath
& file_path
,
528 const OpenCallback
& callback
,
529 PlatformFileError get_info_result
,
530 const base::PlatformFileInfo
& file_info
) {
531 if (get_info_result
== base::PLATFORM_FILE_OK
&& file_info
.is_directory
) {
532 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE
, NULL
);
536 if (get_info_result
== base::PLATFORM_FILE_OK
) {
537 OpenVerifiedFile(file_path
, flags
, callback
);
541 if (get_info_result
== base::PLATFORM_FILE_ERROR_NOT_FOUND
&&
542 flags
& base::PLATFORM_FILE_CREATE_ALWAYS
) {
544 base::Bind(&MemoryFileUtil::DidCreateOrTruncateForOpen
,
545 base::Unretained(this), file_path
, flags
, 0, callback
));
549 callback
.Run(get_info_result
, NULL
);
552 void MemoryFileUtil::OpenTruncatedFileOrCreate(
553 const base::FilePath
& file_path
,
555 const OpenCallback
& callback
,
556 PlatformFileError result
) {
557 if (result
== base::PLATFORM_FILE_OK
) {
558 OpenVerifiedFile(file_path
, flags
, callback
);
562 if (result
== base::PLATFORM_FILE_ERROR_NOT_FOUND
) {
565 base::Bind(&MemoryFileUtil::DidCreateOrTruncateForOpen
,
566 base::Unretained(this), file_path
, flags
, 0, callback
));
570 callback
.Run(result
, NULL
);
573 void MemoryFileUtil::DidCreateOrTruncateForOpen(
574 const base::FilePath
& file_path
,
577 const OpenCallback
& callback
,
578 PlatformFileError result
) {
579 if (result
!= base::PLATFORM_FILE_OK
) {
580 callback
.Run(result
, NULL
);
584 OpenVerifiedFile(file_path
, flags
, callback
);
587 } // namespace fileapi