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

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

Issue 1147083002: mandoline: Fork the files service from the mojo repository. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: I think the problem was in the mandoline build.gn 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "components/filesystem/directory_impl.h"
6
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.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"
16 #include "base/logging.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/posix/eintr_wrapper.h"
19 #include "build/build_config.h"
20 #include "components/filesystem/file_impl.h"
21 #include "components/filesystem/shared_impl.h"
22 #include "components/filesystem/util.h"
23
24 namespace mojo {
25 namespace files {
26
27 namespace {
28
29 // Calls |closedir()| on a |DIR*|.
30 struct DIRDeleter {
31 void operator()(DIR* dir) const { PCHECK(closedir(dir) == 0); }
32 };
33 using ScopedDIR = scoped_ptr<DIR, DIRDeleter>;
34
35 Error ValidateOpenFlags(uint32_t open_flags, bool is_directory) {
36 // Treat unknown flags as "unimplemented".
37 if ((open_flags &
38 ~(kOpenFlagRead | kOpenFlagWrite | kOpenFlagCreate | kOpenFlagExclusive |
39 kOpenFlagAppend | kOpenFlagTruncate)))
40 return ERROR_UNIMPLEMENTED;
41
42 // At least one of |kOpenFlagRead| or |kOpenFlagWrite| must be set.
43 if (!(open_flags & (kOpenFlagRead | kOpenFlagWrite)))
44 return ERROR_INVALID_ARGUMENT;
45
46 // |kOpenFlagCreate| requires |kOpenFlagWrite|.
47 if ((open_flags & kOpenFlagCreate) && !(open_flags & kOpenFlagWrite))
48 return ERROR_INVALID_ARGUMENT;
49
50 // |kOpenFlagExclusive| requires |kOpenFlagCreate|.
51 if ((open_flags & kOpenFlagExclusive) && !(open_flags & kOpenFlagCreate))
52 return ERROR_INVALID_ARGUMENT;
53
54 if (is_directory) {
55 // Check that file-only flags aren't set.
56 if ((open_flags & (kOpenFlagAppend | kOpenFlagTruncate)))
57 return ERROR_INVALID_ARGUMENT;
58 return ERROR_OK;
59 }
60
61 // File-only flags:
62
63 // |kOpenFlagAppend| requires |kOpenFlagWrite|.
64 if ((open_flags & kOpenFlagAppend) && !(open_flags & kOpenFlagWrite))
65 return ERROR_INVALID_ARGUMENT;
66
67 // |kOpenFlagTruncate| requires |kOpenFlagWrite|.
68 if ((open_flags & kOpenFlagTruncate) && !(open_flags & kOpenFlagWrite))
69 return ERROR_INVALID_ARGUMENT;
70
71 return ERROR_OK;
72 }
73
74 Error ValidateDeleteFlags(uint32_t delete_flags) {
75 // Treat unknown flags as "unimplemented".
76 if ((delete_flags &
77 ~(kDeleteFlagFileOnly | kDeleteFlagDirectoryOnly |
78 kDeleteFlagRecursive)))
79 return ERROR_UNIMPLEMENTED;
80
81 // Only one of the three currently-defined flags may be set.
82 if ((delete_flags & kDeleteFlagFileOnly) &&
83 (delete_flags & (kDeleteFlagDirectoryOnly | kDeleteFlagRecursive)))
84 return ERROR_INVALID_ARGUMENT;
85 if ((delete_flags & kDeleteFlagDirectoryOnly) &&
86 (delete_flags & (kDeleteFlagFileOnly | kDeleteFlagRecursive)))
87 return ERROR_INVALID_ARGUMENT;
88 if ((delete_flags & kDeleteFlagRecursive) &&
89 (delete_flags & (kDeleteFlagFileOnly | kDeleteFlagDirectoryOnly)))
90 return ERROR_INVALID_ARGUMENT;
91
92 return ERROR_OK;
93 }
94
95 } // namespace
96
97 DirectoryImpl::DirectoryImpl(InterfaceRequest<Directory> request,
98 base::ScopedFD dir_fd,
99 scoped_ptr<base::ScopedTempDir> temp_dir)
100 : binding_(this, request.Pass()),
101 dir_fd_(dir_fd.Pass()),
102 temp_dir_(temp_dir.Pass()) {
103 DCHECK(dir_fd_.is_valid());
104 }
105
106 DirectoryImpl::~DirectoryImpl() {
107 }
108
109 void DirectoryImpl::Read(const ReadCallback& callback) {
110 static const size_t kMaxReadCount = 1000;
111
112 DCHECK(dir_fd_.is_valid());
113
114 // |fdopendir()| takes ownership of the FD (giving it to the |DIR| --
115 // |closedir()| will close the FD)), so we need to |dup()| ours.
116 base::ScopedFD fd(dup(dir_fd_.get()));
117 if (!fd.is_valid()) {
118 callback.Run(ErrnoToError(errno), Array<DirectoryEntryPtr>());
119 return;
120 }
121
122 ScopedDIR dir(fdopendir(fd.release()));
123 if (!dir) {
124 callback.Run(ErrnoToError(errno), Array<DirectoryEntryPtr>());
125 return;
126 }
127
128 Array<DirectoryEntryPtr> result(0);
129
130 // Warning: This is not portable (per POSIX.1 -- |buffer| may not be large
131 // enough), but it's fine for Linux.
132 #if !defined(OS_ANDROID) && !defined(OS_LINUX)
133 #error "Use of struct dirent for readdir_r() buffer not portable; please check."
134 #endif
135 struct dirent buffer;
136 for (size_t n = 0;;) {
137 struct dirent* entry = nullptr;
138 if (int error = readdir_r(dir.get(), &buffer, &entry)) {
139 // |error| is effectively an errno (for |readdir_r()|), AFAICT.
140 callback.Run(ErrnoToError(error), Array<DirectoryEntryPtr>());
141 return;
142 }
143
144 if (!entry)
145 break;
146
147 n++;
148 if (n > kMaxReadCount) {
149 LOG(WARNING) << "Directory contents truncated";
150 callback.Run(ERROR_OUT_OF_RANGE, result.Pass());
151 return;
152 }
153
154 DirectoryEntryPtr e = DirectoryEntry::New();
155 switch (entry->d_type) {
156 case DT_DIR:
157 e->type = FILE_TYPE_DIRECTORY;
158 break;
159 case DT_REG:
160 e->type = FILE_TYPE_REGULAR_FILE;
161 break;
162 default:
163 e->type = FILE_TYPE_UNKNOWN;
164 break;
165 }
166 e->name = String(entry->d_name);
167 result.push_back(e.Pass());
168 }
169
170 callback.Run(ERROR_OK, result.Pass());
171 }
172
173 void DirectoryImpl::Stat(const StatCallback& callback) {
174 DCHECK(dir_fd_.is_valid());
175 StatFD(dir_fd_.get(), FILE_TYPE_DIRECTORY, callback);
176 }
177
178 void DirectoryImpl::Touch(TimespecOrNowPtr atime,
179 TimespecOrNowPtr mtime,
180 const TouchCallback& callback) {
181 DCHECK(dir_fd_.is_valid());
182 TouchFD(dir_fd_.get(), atime.Pass(), mtime.Pass(), callback);
183 }
184
185 // TODO(vtl): Move the implementation to a thread pool.
186 void DirectoryImpl::OpenFile(const String& path,
187 InterfaceRequest<File> file,
188 uint32_t open_flags,
189 const OpenFileCallback& callback) {
190 DCHECK(!path.is_null());
191 DCHECK(dir_fd_.is_valid());
192
193 if (Error error = IsPathValid(path)) {
194 callback.Run(error);
195 return;
196 }
197 // TODO(vtl): Make sure the path doesn't exit this directory (if appropriate).
198 // TODO(vtl): Maybe allow absolute paths?
199
200 if (Error error = ValidateOpenFlags(open_flags, false)) {
201 callback.Run(error);
202 return;
203 }
204
205 int flags = 0;
206 if ((open_flags & kOpenFlagRead))
207 flags |= (open_flags & kOpenFlagWrite) ? O_RDWR : O_RDONLY;
208 else
209 flags |= O_WRONLY;
210 if ((open_flags & kOpenFlagCreate))
211 flags |= O_CREAT;
212 if ((open_flags & kOpenFlagExclusive))
213 flags |= O_EXCL;
214 if ((open_flags & kOpenFlagAppend))
215 flags |= O_APPEND;
216 if ((open_flags & kOpenFlagTruncate))
217 flags |= O_TRUNC;
218
219 base::ScopedFD file_fd(
220 HANDLE_EINTR(openat(dir_fd_.get(), path.get().c_str(), flags, 0600)));
221 if (!file_fd.is_valid()) {
222 callback.Run(ErrnoToError(errno));
223 return;
224 }
225
226 if (file.is_pending())
227 new FileImpl(file.Pass(), file_fd.Pass());
228 callback.Run(ERROR_OK);
229 }
230
231 void DirectoryImpl::OpenDirectory(const String& path,
232 InterfaceRequest<Directory> directory,
233 uint32_t open_flags,
234 const OpenDirectoryCallback& callback) {
235 DCHECK(!path.is_null());
236 DCHECK(dir_fd_.is_valid());
237
238 if (Error error = IsPathValid(path)) {
239 callback.Run(error);
240 return;
241 }
242 // TODO(vtl): Make sure the path doesn't exit this directory (if appropriate).
243 // TODO(vtl): Maybe allow absolute paths?
244
245 if (Error error = ValidateOpenFlags(open_flags, false)) {
246 callback.Run(error);
247 return;
248 }
249
250 // TODO(vtl): Implement read-only (whatever that means).
251 if (!(open_flags & kOpenFlagWrite)) {
252 callback.Run(ERROR_UNIMPLEMENTED);
253 return;
254 }
255
256 if ((open_flags & kOpenFlagCreate)) {
257 if (mkdirat(dir_fd_.get(), path.get().c_str(), 0700) != 0) {
258 // Allow |EEXIST| if |kOpenFlagExclusive| is not set. Note, however, that
259 // it does not guarantee that |path| is a directory.
260 // TODO(vtl): Hrm, ponder if we should check that |path| is a directory.
261 if (errno != EEXIST || !(open_flags & kOpenFlagExclusive)) {
262 callback.Run(ErrnoToError(errno));
263 return;
264 }
265 }
266 }
267
268 base::ScopedFD new_dir_fd(
269 HANDLE_EINTR(openat(dir_fd_.get(), path.get().c_str(), O_DIRECTORY, 0)));
270 if (!new_dir_fd.is_valid()) {
271 callback.Run(ErrnoToError(errno));
272 return;
273 }
274
275 if (directory.is_pending())
276 new DirectoryImpl(directory.Pass(), new_dir_fd.Pass(), nullptr);
277 callback.Run(ERROR_OK);
278 }
279
280 void DirectoryImpl::Rename(const String& path,
281 const String& new_path,
282 const RenameCallback& callback) {
283 DCHECK(!path.is_null());
284 DCHECK(!new_path.is_null());
285 DCHECK(dir_fd_.is_valid());
286
287 if (Error error = IsPathValid(path)) {
288 callback.Run(error);
289 return;
290 }
291 if (Error error = IsPathValid(new_path)) {
292 callback.Run(error);
293 return;
294 }
295 // TODO(vtl): See TODOs about |path| in OpenFile().
296
297 if (renameat(dir_fd_.get(), path.get().c_str(), dir_fd_.get(),
298 new_path.get().c_str())) {
299 callback.Run(ErrnoToError(errno));
300 return;
301 }
302
303 callback.Run(ERROR_OK);
304 }
305
306 void DirectoryImpl::Delete(const String& path,
307 uint32_t delete_flags,
308 const DeleteCallback& callback) {
309 DCHECK(!path.is_null());
310 DCHECK(dir_fd_.is_valid());
311
312 if (Error error = IsPathValid(path)) {
313 callback.Run(error);
314 return;
315 }
316 // TODO(vtl): See TODOs about |path| in OpenFile().
317
318 if (Error error = ValidateDeleteFlags(delete_flags)) {
319 callback.Run(error);
320 return;
321 }
322
323 // TODO(vtl): Recursive not yet supported.
324 if ((delete_flags & kDeleteFlagRecursive)) {
325 callback.Run(ERROR_UNIMPLEMENTED);
326 return;
327 }
328
329 // First try deleting it as a file, unless we're told to do directory-only.
330 if (!(delete_flags & kDeleteFlagDirectoryOnly)) {
331 if (unlinkat(dir_fd_.get(), path.get().c_str(), 0) == 0) {
332 callback.Run(ERROR_OK);
333 return;
334 }
335
336 // If file-only, don't continue.
337 if ((delete_flags & kDeleteFlagFileOnly)) {
338 callback.Run(ErrnoToError(errno));
339 return;
340 }
341 }
342
343 // Try deleting it as a directory.
344 if (unlinkat(dir_fd_.get(), path.get().c_str(), AT_REMOVEDIR) == 0) {
345 callback.Run(ERROR_OK);
346 return;
347 }
348
349 callback.Run(ErrnoToError(errno));
350 }
351
352 } // namespace files
353 } // namespace mojo
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