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

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 Read() and Write() from FileUtilAsync 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
12 } // namespace
13
14 namespace fileapi {
15
16 // In-memory implementation of AsyncFileStream.
17 class MemoryFileUtilAsyncFileStream : public AsyncFileStream {
18 public:
19 // |file_entry| is owened by MemoryFileUtil.
20 MemoryFileUtilAsyncFileStream(
21 MemoryFileUtil::FileEntry* file_entry, int flags)
22 : file_entry_(file_entry),
23 flags_(flags),
24 offset_(0) {
25 }
26
27 virtual void Read(char* buffer,
28 int64 length,
29 const ReadWriteCallback& callback) OVERRIDE {
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 virtual void Write(const char* buffer,
48 int64 length,
49 const ReadWriteCallback& callback) OVERRIDE {
50 if ((flags_ & base::PLATFORM_FILE_WRITE) == 0) {
51 callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, 0);
52 return;
53 }
54
55 // Extend the contents if needed.
56 std::string* contents = &file_entry_->contents;
57 if (offset_ + length > file_size())
58 contents->resize(offset_ + length, 0); // Fill with 0.
59
60 std::copy(buffer, buffer + length,
61 contents->begin() + offset_);
62 file_entry_->last_modified = base::Time::Now();
63 offset_ += length;
64
65 callback.Run(base::PLATFORM_FILE_OK, length);
66 }
67
68 virtual void Seek(int64 offset,
69 const SeekCallback& callback) OVERRIDE {
70 if (offset > file_size()) {
71 // Unlike lseek(2), we don't allow an offset larger than the file
72 // size for this file implementation.
73 callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
74 return;
75 }
76
77 offset_ = offset;
78 callback.Run(base::PLATFORM_FILE_OK);
79 }
80
81 private:
82 int64 file_size() const {
83 return static_cast<int64>(file_entry_->contents.size());
84 }
85
86 MemoryFileUtil::FileEntry* file_entry_;
87 const int flags_;
88 int64 offset_;
89 };
90
91 MemoryFileUtil::MemoryFileUtil(const FilePath& root_path)
92 : read_directory_buffer_size_(kDefaultReadDirectoryBufferSize) {
93 FileEntry root;
94 root.is_directory = true;
95 root.last_modified = base::Time::Now();
96
97 files_[root_path] = root;
98 }
99
100 void MemoryFileUtil::OpenVerifiedFile(
101 const FilePath& file_path,
102 int flags,
103 const OpenCallback& callback) {
104 FileIterator file_it = files_.find(file_path);
105 // The existence of the file is guranteed here.
106 DCHECK(file_it != files_.end());
107
108 FileEntry* file_entry = &file_it->second;
109 callback.Run(base::PLATFORM_FILE_OK,
110 new MemoryFileUtilAsyncFileStream(file_entry, flags));
111 }
112
113 void MemoryFileUtil::GetFileInfo(
114 const FilePath& file_path,
115 const GetFileInfoCallback& callback) {
116 MessageLoop::current()->PostTask(
117 FROM_HERE,
118 base::Bind(&MemoryFileUtil::DoGetFileInfo, base::Unretained(this),
119 file_path.StripTrailingSeparators(), callback));
120 }
121
122 void MemoryFileUtil::Create(
123 const FilePath& file_path,
124 const StatusCallback& callback) {
125 MessageLoop::current()->PostTask(
126 FROM_HERE,
127 base::Bind(&MemoryFileUtil::DoCreate, base::Unretained(this),
128 file_path.StripTrailingSeparators(), false, callback));
129 }
130
131 void MemoryFileUtil::Truncate(
132 const FilePath& file_path,
133 int64 length,
134 const StatusCallback& callback) {
135 MessageLoop::current()->PostTask(
136 FROM_HERE,
137 base::Bind(&MemoryFileUtil::DoTruncate, base::Unretained(this),
138 file_path.StripTrailingSeparators(), length, callback));
139 }
140
141 void MemoryFileUtil::Touch(
142 const FilePath& file_path,
143 const base::Time& last_access_time,
144 const base::Time& last_modified_time,
145 const StatusCallback& callback) {
146 MessageLoop::current()->PostTask(
147 FROM_HERE,
148 base::Bind(&MemoryFileUtil::DoTouch, base::Unretained(this),
149 file_path.StripTrailingSeparators(),
150 last_modified_time, callback));
151 }
152
153 void MemoryFileUtil::Remove(
154 const FilePath& file_path,
155 bool recursive,
156 const StatusCallback& callback) {
157 if (recursive) {
158 MessageLoop::current()->PostTask(
159 FROM_HERE,
160 base::Bind(&MemoryFileUtil::DoRemoveRecursive,
161 base::Unretained(this), file_path.StripTrailingSeparators(),
162 callback));
163 } else {
164 MessageLoop::current()->PostTask(
165 FROM_HERE,
166 base::Bind(&MemoryFileUtil::DoRemoveSingleFile,
167 base::Unretained(this), file_path.StripTrailingSeparators(),
168 callback));
169 }
170 }
171
172 void MemoryFileUtil::CreateDirectory(
173 const FilePath& dir_path,
174 const StatusCallback& callback) {
175 MessageLoop::current()->PostTask(
176 FROM_HERE,
177 base::Bind(&MemoryFileUtil::DoCreate,
178 base::Unretained(this), dir_path.StripTrailingSeparators(),
179 true, callback));
180 }
181
182 void MemoryFileUtil::ReadDirectory(
183 const FilePath& dir_path,
184 const ReadDirectoryCallback& callback) {
185 MessageLoop::current()->PostTask(
186 FROM_HERE,
187 base::Bind(&MemoryFileUtil::DoReadDirectory,
188 base::Unretained(this), dir_path.StripTrailingSeparators(),
189 FilePath(), callback));
190 }
191
192 bool MemoryFileUtil::IsDirectory(const FilePath& file_path) {
193 ConstFileIterator it = files_.find(file_path);
194 return it != files_.end() && it->second.is_directory;
195 }
196
197 void MemoryFileUtil::DoGetFileInfo(const FilePath& file_path,
198 const GetFileInfoCallback& callback) {
199 base::PlatformFileInfo file_info;
200
201 FileIterator file_it = files_.find(file_path);
202
203 if (file_it == files_.end()) {
204 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_info);
205 return;
206 }
207 const FileEntry& file_entry = file_it->second;
208
209 file_info.size = file_entry.contents.size();
210 file_info.is_directory = file_entry.is_directory;
211 file_info.is_symbolic_link = false;
212
213 // In this file system implementation we store only one datetime. Many
214 // popular file systems do the same.
215 file_info.last_modified = file_entry.last_modified;
216 file_info.last_accessed = file_entry.last_modified;
217 file_info.creation_time = file_entry.last_modified;
218
219 callback.Run(base::PLATFORM_FILE_OK, file_info);
220 }
221
222 void MemoryFileUtil::DoCreate(
223 const FilePath& file_path,
224 bool is_directory,
225 const StatusCallback& callback) {
226 if (FileExists(file_path)) {
227 callback.Run(base::PLATFORM_FILE_ERROR_EXISTS);
228 return;
229 }
230
231 if (!IsDirectory(file_path.DirName())) {
232 callback.Run(base::PLATFORM_FILE_ERROR_FAILED);
233 return;
234 }
235
236 FileEntry file;
237 file.is_directory = is_directory;
238 file.last_modified = base::Time::Now();
239
240 files_[file_path] = file;
241 callback.Run(base::PLATFORM_FILE_OK);
242 }
243
244 void MemoryFileUtil::DoTruncate(
245 const FilePath& file_path,
246 int64 length,
247 const StatusCallback& callback) {
248 FileIterator file_it = files_.find(file_path);
249 if (file_it == files_.end()) {
250 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
251 return;
252 }
253
254 FileEntry& file = file_it->second;
255
256 // Fill the extended part with 0 if |length| is larger than the original
257 // contents size.
258 file.contents.resize(length, 0);
259 callback.Run(base::PLATFORM_FILE_OK);
260 }
261
262 void MemoryFileUtil::DoTouch(
263 const FilePath& file_path,
264 const base::Time& last_modified_time,
265 const StatusCallback& callback) {
266 FileIterator file_it = files_.find(file_path);
267 if (file_it == files_.end()) {
268 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
269 return;
270 }
271
272 FileEntry& file = file_it->second;
273
274 file.last_modified = last_modified_time;
275 callback.Run(base::PLATFORM_FILE_OK);
276 }
277
278 void MemoryFileUtil::DoRemoveSingleFile(
279 const FilePath& file_path,
280 const StatusCallback& callback) {
281 FileIterator file_it = files_.find(file_path);
282 if (file_it == files_.end()) {
283 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
284 return;
285 }
286
287 FileEntry& file = file_it->second;
288 if (file.is_directory) {
289 // Check that directory is empty.
290 FilePath path_with_separator = file_it->first.Append("/");
291 FileIterator file_inside_dir = files_.lower_bound(path_with_separator);
292
293 if (file_it->first.IsParent(file_inside_dir->first)) {
294 callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE);
295 return;
296 }
297 }
298
299 files_.erase(file_it);
300 callback.Run(base::PLATFORM_FILE_OK);
301 }
302
303 void MemoryFileUtil::DoRemoveRecursive(
304 const FilePath& file_path,
305 const StatusCallback& callback) {
306 FileIterator file_it = files_.find(file_path);
307 if (file_it == files_.end()) {
308 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
309 return;
310 }
311
312 FileEntry& file = file_it->second;
313 if (!file.is_directory) {
314 files_.erase(file_it);
315 callback.Run(base::PLATFORM_FILE_OK);
316 return;
317 }
318
319 // In the following code we rely on the fact that the paths in files_
320 // are ordered lexicographically. We iterate over files_ and delete
321 // the files from files_, then delete in one go all the files from
322 // file_by_name_.
323 FilePath file_path_with_separator = file_path.Append("/");
324 FileIterator it_first = files_.lower_bound(file_path_with_separator);
325 FileIterator it_last = it_first;
326
327 while (it_last != files_.end() && file_path.IsParent(it_last->first)) {
328 ++it_last;
329 }
330 files_.erase(it_first, it_last);
331
332 // The initial path is stored without a separator in the end, so it
333 // has not been removed in the above loop.
334 files_.erase(file_it);
335 callback.Run(base::PLATFORM_FILE_OK);
336 }
337
338 void MemoryFileUtil::DoReadDirectory(
339 const FilePath& dir_path,
340 const FilePath& in_from,
341 const ReadDirectoryCallback& callback) {
342 FilePath from = in_from;
343 read_directory_buffer_.clear();
344
345 if (!FileExists(dir_path)) {
346 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND,
347 read_directory_buffer_, true);
348 return;
349 }
350
351 if (!IsDirectory(dir_path)) {
352 callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
353 read_directory_buffer_, true);
354 return;
355 }
356
357 if (from.empty())
358 from = dir_path.Append("/");
359
360 bool completed = true;
361
362 // Here we iterate over all paths starting with the prefix dir_path + '/'.
363 // It is not very efficient in case of a deep tree with many files in
364 // subdirectories. If ever we'll need efficiency from this implementation of
365 // FS, this should be changed. (It could be done by using lower_bound instead
366 // of ++ in case we've met a subdirectory path.)
367 for (ConstFileIterator it = files_.lower_bound(from);
368 it != files_.end() && dir_path.IsParent(it->first);
369 ++it) {
370 if (it->first.DirName() != dir_path) // a file in subdirectory
371 continue;
372
373 if (read_directory_buffer_.size() == read_directory_buffer_size_) {
374 from = it->first;
375 completed = false;
376 break;
377 }
378
379 const FileEntry& file = it->second;
380 DirectoryEntry entry;
381 entry.name = it->first.BaseName().value();
382 entry.is_directory = file.is_directory;
383 entry.size = file.contents.size();
384 entry.last_modified_time = file.last_modified;
385
386 read_directory_buffer_.push_back(entry);
387 }
388
389 callback.Run(base::PLATFORM_FILE_OK, read_directory_buffer_, completed);
390
391 if (!completed) {
392 MessageLoop::current()->PostTask(
393 FROM_HERE,
394 base::Bind(&MemoryFileUtil::DoReadDirectory,
395 base::Unretained(this), dir_path,
396 from, callback));
397 }
398 }
399
400 } // namespace file_api
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698