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

Side by Side Diff: services/files/directory_impl.cc

Issue 963093004: Files: Add basic implementation of Directory and some basic tests. (Closed) Base URL: https://github.com/domokit/mojo.git@file_man
Patch Set: oops Created 5 years, 9 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
« no previous file with comments | « services/files/BUILD.gn ('k') | services/files/file_impl.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "services/files/directory_impl.h" 5 #include "services/files/directory_impl.h"
6 6
7 #include <dirent.h>
7 #include <errno.h> 8 #include <errno.h>
8 #include <fcntl.h> 9 #include <fcntl.h>
9 #include <stdio.h> 10 #include <stdio.h>
10 #include <sys/stat.h> 11 #include <sys/stat.h>
11 #include <sys/types.h> 12 #include <sys/types.h>
12 #include <time.h>
13 #include <unistd.h> 13 #include <unistd.h>
14 14
15 #include "base/files/file_path.h"
16 #include "base/files/scoped_temp_dir.h" 15 #include "base/files/scoped_temp_dir.h"
17 #include "base/logging.h" 16 #include "base/logging.h"
18 #include "base/memory/scoped_ptr.h" 17 #include "base/memory/scoped_ptr.h"
19 #include "base/posix/eintr_wrapper.h" 18 #include "base/posix/eintr_wrapper.h"
20 #include "build/build_config.h" 19 #include "build/build_config.h"
21 #include "services/files/file_impl.h" 20 #include "services/files/file_impl.h"
21 #include "services/files/shared_impl.h"
22 #include "services/files/util.h" 22 #include "services/files/util.h"
23 23
24 namespace mojo { 24 namespace mojo {
25 namespace files { 25 namespace files {
26 26
27 namespace { 27 namespace {
28 28
29 // Calls |closedir()| on a |DIR*|.
30 struct DIRDeleter {
31 void operator()(DIR* dir) const { PCHECK(closedir(dir) == 0); }
32 };
33 typedef scoped_ptr<DIR, DIRDeleter> ScopedDIR;
34
29 Error ValidateOpenFlags(uint32_t open_flags, bool is_directory) { 35 Error ValidateOpenFlags(uint32_t open_flags, bool is_directory) {
30 // Treat unknown flags as "unimplemented". 36 // Treat unknown flags as "unimplemented".
31 if ((open_flags & 37 if ((open_flags &
32 ~(kOpenFlagRead | kOpenFlagWrite | kOpenFlagCreate | kOpenFlagExclusive | 38 ~(kOpenFlagRead | kOpenFlagWrite | kOpenFlagCreate | kOpenFlagExclusive |
33 kOpenFlagAppend | kOpenFlagTruncate))) 39 kOpenFlagAppend | kOpenFlagTruncate)))
34 return ERROR_UNIMPLEMENTED; 40 return ERROR_UNIMPLEMENTED;
35 41
36 // At least one of |kOpenFlagRead| or |kOpenFlagWrite| must be set. 42 // At least one of |kOpenFlagRead| or |kOpenFlagWrite| must be set.
37 if (!(open_flags & (kOpenFlagRead | kOpenFlagWrite))) 43 if (!(open_flags & (kOpenFlagRead | kOpenFlagWrite)))
38 return ERROR_INVALID_ARGUMENT; 44 return ERROR_INVALID_ARGUMENT;
(...skipping 19 matching lines...) Expand all
58 if ((open_flags & kOpenFlagAppend) && !(open_flags & kOpenFlagWrite)) 64 if ((open_flags & kOpenFlagAppend) && !(open_flags & kOpenFlagWrite))
59 return ERROR_INVALID_ARGUMENT; 65 return ERROR_INVALID_ARGUMENT;
60 66
61 // |kOpenFlagTruncate| requires |kOpenFlagWrite|. 67 // |kOpenFlagTruncate| requires |kOpenFlagWrite|.
62 if ((open_flags & kOpenFlagTruncate) && !(open_flags & kOpenFlagWrite)) 68 if ((open_flags & kOpenFlagTruncate) && !(open_flags & kOpenFlagWrite))
63 return ERROR_INVALID_ARGUMENT; 69 return ERROR_INVALID_ARGUMENT;
64 70
65 return ERROR_OK; 71 return ERROR_OK;
66 } 72 }
67 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
68 } // namespace 95 } // namespace
69 96
70 DirectoryImpl::DirectoryImpl(InterfaceRequest<Directory> request, 97 DirectoryImpl::DirectoryImpl(InterfaceRequest<Directory> request,
71 base::ScopedFD dir_fd, 98 base::ScopedFD dir_fd,
72 scoped_ptr<base::ScopedTempDir> temp_dir) 99 scoped_ptr<base::ScopedTempDir> temp_dir)
73 : binding_(this, request.Pass()), 100 : binding_(this, request.Pass()),
74 dir_fd_(dir_fd.Pass()), 101 dir_fd_(dir_fd.Pass()),
75 temp_dir_(temp_dir.Pass()) { 102 temp_dir_(temp_dir.Pass()) {
76 DCHECK(dir_fd_.is_valid()); 103 DCHECK(dir_fd_.is_valid());
77 } 104 }
78 105
79 DirectoryImpl::~DirectoryImpl() { 106 DirectoryImpl::~DirectoryImpl() {
80 } 107 }
81 108
82 void DirectoryImpl::Read( 109 void DirectoryImpl::Read(
83 const Callback<void(Error, Array<DirectoryEntryPtr>)>& callback) { 110 const Callback<void(Error, Array<DirectoryEntryPtr>)>& callback) {
84 // TODO(vtl): FIXME sooner 111 static const size_t kMaxReadCount = 1000;
85 NOTIMPLEMENTED(); 112
86 callback.Run(ERROR_UNIMPLEMENTED, Array<DirectoryEntryPtr>()); 113 DCHECK(dir_fd_.is_valid());
114
115 // |fdopendir()| takes ownership of the FD (giving it to the |DIR| --
116 // |closedir()| will close the FD)), so we need to |dup()| ours.
117 base::ScopedFD fd(dup(dir_fd_.get()));
118 if (!fd.is_valid()) {
119 callback.Run(ErrnoToError(errno), Array<DirectoryEntryPtr>());
120 return;
121 }
122
123 ScopedDIR dir(fdopendir(fd.release()));
124 if (!dir) {
125 callback.Run(ErrnoToError(errno), Array<DirectoryEntryPtr>());
126 return;
127 }
128
129 Array<DirectoryEntryPtr> result(0);
130
131 // Warning: This is not portable (per POSIX.1 -- |buffer| may not be large
132 // enough), but it's fine for Linux.
133 #if !defined(OS_ANDROID) && !defined(OS_LINUX)
134 #error "Use of struct dirent for readdir_r() buffer not portable; please check."
135 #endif
136 struct dirent buffer;
137 for (size_t n = 0;;) {
138 struct dirent* entry = nullptr;
139 if (int error = readdir_r(dir.get(), &buffer, &entry)) {
140 // |error| is effectively an errno (for |readdir_r()|), AFAICT.
141 callback.Run(ErrnoToError(error), Array<DirectoryEntryPtr>());
142 return;
143 }
144
145 if (!entry)
146 break;
147
148 n++;
149 if (n > kMaxReadCount) {
150 LOG(WARNING) << "Directory contents truncated";
151 callback.Run(ERROR_OUT_OF_RANGE, result.Pass());
152 return;
153 }
154
155 DirectoryEntryPtr e = DirectoryEntry::New();
156 switch (entry->d_type) {
157 case DT_DIR:
158 e->type = FILE_TYPE_DIRECTORY;
159 break;
160 case DT_REG:
161 e->type = FILE_TYPE_REGULAR_FILE;
162 break;
163 default:
164 e->type = FILE_TYPE_UNKNOWN;
165 break;
166 }
167 e->name = String(entry->d_name);
168 result.push_back(e.Pass());
169 }
170
171 callback.Run(ERROR_OK, result.Pass());
87 } 172 }
88 173
89 void DirectoryImpl::Stat( 174 void DirectoryImpl::Stat(
90 const Callback<void(Error, FileInformationPtr)>& callback) { 175 const Callback<void(Error, FileInformationPtr)>& callback) {
91 // TODO(vtl): FIXME sooner 176 DCHECK(dir_fd_.is_valid());
92 NOTIMPLEMENTED(); 177 StatFD(dir_fd_.get(), callback);
93 callback.Run(ERROR_UNIMPLEMENTED, nullptr);
94 } 178 }
95 179
96 void DirectoryImpl::Touch(TimespecOrNowPtr atime, 180 void DirectoryImpl::Touch(TimespecOrNowPtr atime,
97 TimespecOrNowPtr mtime, 181 TimespecOrNowPtr mtime,
98 const Callback<void(Error)>& callback) { 182 const Callback<void(Error)>& callback) {
99 // TODO(vtl): FIXME sooner 183 DCHECK(dir_fd_.is_valid());
100 NOTIMPLEMENTED(); 184 TouchFD(dir_fd_.get(), atime.Pass(), mtime.Pass(), callback);
101 callback.Run(ERROR_UNIMPLEMENTED);
102 } 185 }
103 186
104 // TODO(vtl): Move the implementation to a thread pool. 187 // TODO(vtl): Move the implementation to a thread pool.
105 void DirectoryImpl::OpenFile(const String& path, 188 void DirectoryImpl::OpenFile(const String& path,
106 InterfaceRequest<File> file, 189 InterfaceRequest<File> file,
107 uint32_t open_flags, 190 uint32_t open_flags,
108 const Callback<void(Error)>& callback) { 191 const Callback<void(Error)>& callback) {
109 DCHECK(!path.is_null()); 192 DCHECK(!path.is_null());
110 DCHECK(dir_fd_.is_valid()); 193 DCHECK(dir_fd_.is_valid());
111 194
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 227
145 if (file.is_pending()) 228 if (file.is_pending())
146 new FileImpl(file.Pass(), file_fd.Pass()); 229 new FileImpl(file.Pass(), file_fd.Pass());
147 callback.Run(ERROR_OK); 230 callback.Run(ERROR_OK);
148 } 231 }
149 232
150 void DirectoryImpl::OpenDirectory(const String& path, 233 void DirectoryImpl::OpenDirectory(const String& path,
151 InterfaceRequest<Directory> directory, 234 InterfaceRequest<Directory> directory,
152 uint32_t open_flags, 235 uint32_t open_flags,
153 const Callback<void(Error)>& callback) { 236 const Callback<void(Error)>& callback) {
154 // TODO(vtl): FIXME sooner 237 DCHECK(!path.is_null());
155 NOTIMPLEMENTED(); 238 DCHECK(dir_fd_.is_valid());
156 callback.Run(ERROR_UNIMPLEMENTED); 239
240 if (Error error = IsPathValid(path)) {
241 callback.Run(error);
242 return;
243 }
244 // TODO(vtl): Make sure the path doesn't exit this directory (if appropriate).
245 // TODO(vtl): Maybe allow absolute paths?
246
247 if (Error error = ValidateOpenFlags(open_flags, false)) {
248 callback.Run(error);
249 return;
250 }
251
252 // TODO(vtl): Implement read-only (whatever that means).
253 if (!(open_flags & kOpenFlagWrite)) {
254 callback.Run(ERROR_UNIMPLEMENTED);
255 return;
256 }
257
258 if ((open_flags & kOpenFlagCreate)) {
259 if (mkdirat(dir_fd_.get(), path.get().c_str(), 0700) != 0) {
260 // Allow |EEXIST| if |kOpenFlagExclusive| is not set. Note, however, that
261 // it does not guarantee that |path| is a directory.
262 // TODO(vtl): Hrm, ponder if we should check that |path| is a directory.
263 if (errno != EEXIST || !(open_flags & kOpenFlagExclusive)) {
264 callback.Run(ErrnoToError(errno));
265 return;
266 }
267 }
268 }
269
270 base::ScopedFD new_dir_fd(
271 HANDLE_EINTR(openat(dir_fd_.get(), path.get().c_str(), O_DIRECTORY, 0)));
272 if (!new_dir_fd.is_valid()) {
273 callback.Run(ErrnoToError(errno));
274 return;
275 }
276
277 if (directory.is_pending())
278 new DirectoryImpl(directory.Pass(), new_dir_fd.Pass(), nullptr);
279 callback.Run(ERROR_OK);
157 } 280 }
158 281
159 void DirectoryImpl::Rename(const String& path, 282 void DirectoryImpl::Rename(const String& path,
160 const String& new_path, 283 const String& new_path,
161 const Callback<void(Error)>& callback) { 284 const Callback<void(Error)>& callback) {
162 DCHECK(!path.is_null()); 285 DCHECK(!path.is_null());
163 DCHECK(!new_path.is_null()); 286 DCHECK(!new_path.is_null());
164 DCHECK(dir_fd_.is_valid()); 287 DCHECK(dir_fd_.is_valid());
165 288
166 if (Error error = IsPathValid(path)) { 289 if (Error error = IsPathValid(path)) {
167 callback.Run(error); 290 callback.Run(error);
168 return; 291 return;
169 } 292 }
170 if (Error error = IsPathValid(new_path)) { 293 if (Error error = IsPathValid(new_path)) {
171 callback.Run(error); 294 callback.Run(error);
172 return; 295 return;
173 } 296 }
174 // TODO(vtl): see TODOs about |path| in OpenFile() 297 // TODO(vtl): See TODOs about |path| in OpenFile().
175 298
176 if (renameat(dir_fd_.get(), path.get().c_str(), dir_fd_.get(), 299 if (renameat(dir_fd_.get(), path.get().c_str(), dir_fd_.get(),
177 new_path.get().c_str())) { 300 new_path.get().c_str())) {
178 callback.Run(ErrnoToError(errno)); 301 callback.Run(ErrnoToError(errno));
179 return; 302 return;
180 } 303 }
181 304
182 callback.Run(ERROR_OK); 305 callback.Run(ERROR_OK);
183 } 306 }
184 307
185 void DirectoryImpl::Delete(const String& path, 308 void DirectoryImpl::Delete(const String& path,
186 uint32_t delete_flags, 309 uint32_t delete_flags,
187 const Callback<void(Error)>& callback) { 310 const Callback<void(Error)>& callback) {
188 // TODO(vtl): FIXME sooner 311 DCHECK(!path.is_null());
189 NOTIMPLEMENTED(); 312 DCHECK(dir_fd_.is_valid());
190 callback.Run(ERROR_UNIMPLEMENTED); 313
314 if (Error error = IsPathValid(path)) {
315 callback.Run(error);
316 return;
317 }
318 // TODO(vtl): See TODOs about |path| in OpenFile().
319
320 if (Error error = ValidateDeleteFlags(delete_flags)) {
321 callback.Run(error);
322 return;
323 }
324
325 // TODO(vtl): Recursive not yet supported.
326 if ((delete_flags & kDeleteFlagRecursive)) {
327 callback.Run(ERROR_UNIMPLEMENTED);
328 return;
329 }
330
331 // First try deleting it as a file, unless we're told to do directory-only.
332 if (!(delete_flags & kDeleteFlagDirectoryOnly)) {
333 if (unlinkat(dir_fd_.get(), path.get().c_str(), 0) == 0) {
334 callback.Run(ERROR_OK);
335 return;
336 }
337
338 // If file-only, don't continue.
339 if ((delete_flags & kDeleteFlagFileOnly)) {
340 callback.Run(ErrnoToError(errno));
341 return;
342 }
343 }
344
345 // Try deleting it as a directory.
346 if (unlinkat(dir_fd_.get(), path.get().c_str(), AT_REMOVEDIR) == 0) {
347 callback.Run(ERROR_OK);
348 return;
349 }
350
351 callback.Run(ErrnoToError(errno));
191 } 352 }
192 353
193 } // namespace files 354 } // namespace files
194 } // namespace mojo 355 } // namespace mojo
OLDNEW
« no previous file with comments | « services/files/BUILD.gn ('k') | services/files/file_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698