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

Side by Side Diff: components/filesystem/directory_impl.cc

Issue 1158253002: mandoline filesystem: Rewrite using base::File. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: More cleanup Created 5 years, 7 months 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
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/filesystem/directory_impl.h" 5 #include "components/filesystem/directory_impl.h"
6 6
7 #include <dirent.h> 7 #include "base/files/file.h"
8 #include <errno.h> 8 #include "base/files/file_enumerator.h"
9 #include <fcntl.h> 9 #include "base/files/file_util.h"
10 #include <stdio.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14
15 #include "base/files/scoped_temp_dir.h" 10 #include "base/files/scoped_temp_dir.h"
16 #include "base/logging.h" 11 #include "base/logging.h"
17 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
18 #include "base/posix/eintr_wrapper.h"
19 #include "build/build_config.h" 13 #include "build/build_config.h"
20 #include "components/filesystem/file_impl.h" 14 #include "components/filesystem/file_impl.h"
21 #include "components/filesystem/shared_impl.h"
22 #include "components/filesystem/util.h" 15 #include "components/filesystem/util.h"
23 16
24 namespace filesystem { 17 namespace filesystem {
25 18
26 namespace {
27
28 // Calls |closedir()| on a |DIR*|.
29 struct DIRDeleter {
30 void operator()(DIR* dir) const { PCHECK(closedir(dir) == 0); }
31 };
32 using ScopedDIR = scoped_ptr<DIR, DIRDeleter>;
33
34 } // namespace
35
36 DirectoryImpl::DirectoryImpl(mojo::InterfaceRequest<Directory> request, 19 DirectoryImpl::DirectoryImpl(mojo::InterfaceRequest<Directory> request,
37 base::ScopedFD dir_fd, 20 base::FilePath directory_path,
38 scoped_ptr<base::ScopedTempDir> temp_dir) 21 scoped_ptr<base::ScopedTempDir> temp_dir)
39 : binding_(this, request.Pass()), 22 : binding_(this, request.Pass()),
40 dir_fd_(dir_fd.Pass()), 23 directory_path_(directory_path),
41 temp_dir_(temp_dir.Pass()) { 24 temp_dir_(temp_dir.Pass()) {
42 DCHECK(dir_fd_.is_valid());
43 } 25 }
44 26
45 DirectoryImpl::~DirectoryImpl() { 27 DirectoryImpl::~DirectoryImpl() {
46 } 28 }
47 29
48 void DirectoryImpl::Read(const ReadCallback& callback) { 30 void DirectoryImpl::Read(const ReadCallback& callback) {
49 static const size_t kMaxReadCount = 1000; 31 mojo::Array<DirectoryEntryPtr> entries(0);
50 32 base::FileEnumerator directory_enumerator(directory_path_, false,
51 DCHECK(dir_fd_.is_valid()); 33 base::FileEnumerator::DIRECTORIES);
52 34 for (base::FilePath name = directory_enumerator.Next(); !name.empty();
jam 2015/05/28 06:40:33 why enumerate twice instead of once with the bitma
Elliot Glaysher 2015/05/28 17:46:58 Done.
53 // |fdopendir()| takes ownership of the FD (giving it to the |DIR| -- 35 name = directory_enumerator.Next()) {
54 // |closedir()| will close the FD)), so we need to |dup()| ours. 36 DirectoryEntryPtr entry = DirectoryEntry::New();
55 base::ScopedFD fd(dup(dir_fd_.get())); 37 entry->type = FILE_TYPE_DIRECTORY;
56 if (!fd.is_valid()) { 38 // TODO(erg): See the comments about when we can stop caring about non-utf8
57 callback.Run(ErrnoToError(errno), mojo::Array<DirectoryEntryPtr>()); 39 // names in base/files/file_path.h.
58 return; 40 entry->name = name.BaseName().AsUTF8Unsafe();
41 entries.push_back(entry.Pass());
59 } 42 }
60 43
61 ScopedDIR dir(fdopendir(fd.release())); 44 base::FileEnumerator file_enumerator(directory_path_, false,
62 if (!dir) { 45 base::FileEnumerator::FILES);
63 callback.Run(ErrnoToError(errno), mojo::Array<DirectoryEntryPtr>()); 46 for (base::FilePath name = file_enumerator.Next(); !name.empty();
64 return; 47 name = file_enumerator.Next()) {
48 DirectoryEntryPtr entry = DirectoryEntry::New();
49 entry->type = FILE_TYPE_REGULAR_FILE;
50 entry->name = name.BaseName().AsUTF8Unsafe();
51 entries.push_back(entry.Pass());
65 } 52 }
66 53
67 mojo::Array<DirectoryEntryPtr> result(0); 54 callback.Run(ERROR_OK, entries.Pass());
68
69 // Warning: This is not portable (per POSIX.1 -- |buffer| may not be large
70 // enough), but it's fine for Linux.
71 #if !defined(OS_ANDROID) && !defined(OS_LINUX)
72 #error "Use of struct dirent for readdir_r() buffer not portable; please check."
73 #endif
74 struct dirent buffer;
75 for (size_t n = 0;;) {
76 struct dirent* entry = nullptr;
77 if (int error = readdir_r(dir.get(), &buffer, &entry)) {
78 // |error| is effectively an errno (for |readdir_r()|), AFAICT.
79 callback.Run(ErrnoToError(error), mojo::Array<DirectoryEntryPtr>());
80 return;
81 }
82
83 if (!entry)
84 break;
85
86 n++;
87 if (n > kMaxReadCount) {
88 LOG(WARNING) << "Directory contents truncated";
89 callback.Run(ERROR_OUT_OF_RANGE, result.Pass());
90 return;
91 }
92
93 DirectoryEntryPtr e = DirectoryEntry::New();
94 switch (entry->d_type) {
95 case DT_DIR:
96 e->type = FILE_TYPE_DIRECTORY;
97 break;
98 case DT_REG:
99 e->type = FILE_TYPE_REGULAR_FILE;
100 break;
101 default:
102 e->type = FILE_TYPE_UNKNOWN;
103 break;
104 }
105 e->name = mojo::String(entry->d_name);
106 result.push_back(e.Pass());
107 }
108
109 callback.Run(ERROR_OK, result.Pass());
110 } 55 }
111 56
112 void DirectoryImpl::Stat(const StatCallback& callback) { 57 // TODO(erg): Consider adding an implementation of Stat()/Touch() to the
113 DCHECK(dir_fd_.is_valid()); 58 // directory, too. Right now, the base::File abstractions do not really deal
114 StatFD(dir_fd_.get(), FILE_TYPE_DIRECTORY, callback); 59 // with directories properly, so these are broken for now.
115 }
116
117 void DirectoryImpl::Touch(TimespecOrNowPtr atime,
118 TimespecOrNowPtr mtime,
119 const TouchCallback& callback) {
120 DCHECK(dir_fd_.is_valid());
121 TouchFD(dir_fd_.get(), atime.Pass(), mtime.Pass(), callback);
122 }
123 60
124 // TODO(vtl): Move the implementation to a thread pool. 61 // TODO(vtl): Move the implementation to a thread pool.
125 void DirectoryImpl::OpenFile(const mojo::String& path, 62 void DirectoryImpl::OpenFile(const mojo::String& raw_path,
126 mojo::InterfaceRequest<File> file, 63 mojo::InterfaceRequest<File> file,
127 uint32_t open_flags, 64 uint32_t open_flags,
128 const OpenFileCallback& callback) { 65 const OpenFileCallback& callback) {
129 DCHECK(!path.is_null()); 66 base::FilePath path;
130 DCHECK(dir_fd_.is_valid()); 67 if (Error error = ValidatePath(raw_path, directory_path_, &path)) {
131
132 if (Error error = IsPathValid(path)) {
133 callback.Run(error);
134 return;
135 }
136 // TODO(vtl): Make sure the path doesn't exit this directory (if appropriate).
137 // TODO(vtl): Maybe allow absolute paths?
138
139 if (Error error = ValidateOpenFlags(open_flags, false)) {
140 callback.Run(error); 68 callback.Run(error);
141 return; 69 return;
142 } 70 }
143 71
144 int flags = 0; 72 base::File base_file(path, open_flags);
145 if ((open_flags & kOpenFlagRead)) 73 if (!base_file.IsValid()) {
146 flags |= (open_flags & kOpenFlagWrite) ? O_RDWR : O_RDONLY; 74 callback.Run(ERROR_FAILED);
147 else
148 flags |= O_WRONLY;
149 if ((open_flags & kOpenFlagCreate))
150 flags |= O_CREAT;
151 if ((open_flags & kOpenFlagExclusive))
152 flags |= O_EXCL;
153 if ((open_flags & kOpenFlagAppend))
154 flags |= O_APPEND;
155 if ((open_flags & kOpenFlagTruncate))
156 flags |= O_TRUNC;
157
158 base::ScopedFD file_fd(
159 HANDLE_EINTR(openat(dir_fd_.get(), path.get().c_str(), flags, 0600)));
160 if (!file_fd.is_valid()) {
161 callback.Run(ErrnoToError(errno));
162 return; 75 return;
163 } 76 }
164 77
165 if (file.is_pending()) 78 if (file.is_pending()) {
166 new FileImpl(file.Pass(), file_fd.Pass()); 79 new FileImpl(file.Pass(), base_file.Pass());
80 }
167 callback.Run(ERROR_OK); 81 callback.Run(ERROR_OK);
168 } 82 }
169 83
170 void DirectoryImpl::OpenDirectory(const mojo::String& path, 84 void DirectoryImpl::OpenDirectory(const mojo::String& raw_path,
171 mojo::InterfaceRequest<Directory> directory, 85 mojo::InterfaceRequest<Directory> directory,
172 uint32_t open_flags, 86 uint32_t open_flags,
173 const OpenDirectoryCallback& callback) { 87 const OpenDirectoryCallback& callback) {
174 DCHECK(!path.is_null()); 88 base::FilePath path;
175 DCHECK(dir_fd_.is_valid()); 89 if (Error error = ValidatePath(raw_path, directory_path_, &path)) {
176
177 if (Error error = IsPathValid(path)) {
178 callback.Run(error);
179 return;
180 }
181 // TODO(vtl): Make sure the path doesn't exit this directory (if appropriate).
182 // TODO(vtl): Maybe allow absolute paths?
183
184 if (Error error = ValidateOpenFlags(open_flags, false)) {
185 callback.Run(error); 90 callback.Run(error);
186 return; 91 return;
187 } 92 }
188 93
189 // TODO(vtl): Implement read-only (whatever that means). 94 if (!base::DirectoryExists(path)) {
190 if (!(open_flags & kOpenFlagWrite)) { 95 if (base::PathExists(path)) {
191 callback.Run(ERROR_UNIMPLEMENTED); 96 callback.Run(ERROR_NOT_A_DIRECTORY);
97 return;
98 }
99
100 if (!(open_flags & kFlagOpenAlways || open_flags & kFlagCreate)) {
101 // The directory doesn't exist, and we weren't passed parameters to
102 // create it.
103 callback.Run(ERROR_NOT_FOUND);
104 return;
105 }
106
107 base::File::Error error;
108 if (!base::CreateDirectoryAndGetError(path, &error)) {
109 callback.Run(static_cast<filesystem::Error>(error));
110 return;
111 }
112 }
113
114 if (directory.is_pending())
115 new DirectoryImpl(directory.Pass(), path,
116 scoped_ptr<base::ScopedTempDir>());
117 callback.Run(ERROR_OK);
118 }
119
120 void DirectoryImpl::Rename(const mojo::String& raw_old_path,
121 const mojo::String& raw_new_path,
122 const RenameCallback& callback) {
123 base::FilePath old_path;
124 if (Error error = ValidatePath(raw_old_path, directory_path_, &old_path)) {
125 callback.Run(error);
192 return; 126 return;
193 } 127 }
194 128
195 if ((open_flags & kOpenFlagCreate)) { 129 base::FilePath new_path;
196 if (mkdirat(dir_fd_.get(), path.get().c_str(), 0700) != 0) { 130 if (Error error = ValidatePath(raw_new_path, directory_path_, &new_path)) {
197 // Allow |EEXIST| if |kOpenFlagExclusive| is not set. Note, however, that 131 callback.Run(error);
198 // it does not guarantee that |path| is a directory.
199 // TODO(vtl): Hrm, ponder if we should check that |path| is a directory.
200 if (errno != EEXIST || !(open_flags & kOpenFlagExclusive)) {
201 callback.Run(ErrnoToError(errno));
202 return;
203 }
204 }
205 }
206
207 base::ScopedFD new_dir_fd(
208 HANDLE_EINTR(openat(dir_fd_.get(), path.get().c_str(), O_DIRECTORY, 0)));
209 if (!new_dir_fd.is_valid()) {
210 callback.Run(ErrnoToError(errno));
211 return; 132 return;
212 } 133 }
213 134
214 if (directory.is_pending()) 135 if (!base::Move(old_path, new_path)) {
215 new DirectoryImpl(directory.Pass(), new_dir_fd.Pass(), nullptr); 136 callback.Run(ERROR_FAILED);
216 callback.Run(ERROR_OK);
217 }
218
219 void DirectoryImpl::Rename(const mojo::String& path,
220 const mojo::String& new_path,
221 const RenameCallback& callback) {
222 DCHECK(!path.is_null());
223 DCHECK(!new_path.is_null());
224 DCHECK(dir_fd_.is_valid());
225
226 if (Error error = IsPathValid(path)) {
227 callback.Run(error);
228 return;
229 }
230 if (Error error = IsPathValid(new_path)) {
231 callback.Run(error);
232 return;
233 }
234 // TODO(vtl): See TODOs about |path| in OpenFile().
235
236 if (renameat(dir_fd_.get(), path.get().c_str(), dir_fd_.get(),
237 new_path.get().c_str())) {
238 callback.Run(ErrnoToError(errno));
239 return; 137 return;
240 } 138 }
241 139
242 callback.Run(ERROR_OK); 140 callback.Run(ERROR_OK);
243 } 141 }
244 142
245 void DirectoryImpl::Delete(const mojo::String& path, 143 void DirectoryImpl::Delete(const mojo::String& raw_path,
246 uint32_t delete_flags, 144 uint32_t delete_flags,
247 const DeleteCallback& callback) { 145 const DeleteCallback& callback) {
248 DCHECK(!path.is_null()); 146 base::FilePath path;
249 DCHECK(dir_fd_.is_valid()); 147 if (Error error = ValidatePath(raw_path, directory_path_, &path)) {
250
251 if (Error error = IsPathValid(path)) {
252 callback.Run(error);
253 return;
254 }
255 // TODO(vtl): See TODOs about |path| in OpenFile().
256
257 if (Error error = ValidateDeleteFlags(delete_flags)) {
258 callback.Run(error); 148 callback.Run(error);
259 return; 149 return;
260 } 150 }
261 151
262 // TODO(vtl): Recursive not yet supported. 152 bool recursive = delete_flags & kDeleteFlagRecursive;
263 if ((delete_flags & kDeleteFlagRecursive)) { 153 if (!base::DeleteFile(path, recursive)) {
264 callback.Run(ERROR_UNIMPLEMENTED); 154 callback.Run(ERROR_FAILED);
265 return; 155 return;
266 } 156 }
267 157
268 // First try deleting it as a file, unless we're told to do directory-only. 158 callback.Run(ERROR_OK);
269 if (!(delete_flags & kDeleteFlagDirectoryOnly)) {
270 if (unlinkat(dir_fd_.get(), path.get().c_str(), 0) == 0) {
271 callback.Run(ERROR_OK);
272 return;
273 }
274
275 // If file-only, don't continue.
276 if ((delete_flags & kDeleteFlagFileOnly)) {
277 callback.Run(ErrnoToError(errno));
278 return;
279 }
280 }
281
282 // Try deleting it as a directory.
283 if (unlinkat(dir_fd_.get(), path.get().c_str(), AT_REMOVEDIR) == 0) {
284 callback.Run(ERROR_OK);
285 return;
286 }
287
288 callback.Run(ErrnoToError(errno));
289 } 159 }
290 160
291 } // namespace filesystem 161 } // namespace filesystem
OLDNEW
« no previous file with comments | « components/filesystem/directory_impl.h ('k') | components/filesystem/directory_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698