blob: 3309cdb5adae6d2983fdf6061e238388b335cd08 [file] [log] [blame]
Viet-Trung Luu2a394492015-03-04 08:03:20 -08001// 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 "services/files/directory_impl.h"
6
Viet-Trung Luu451ed412015-03-09 11:11:11 -07007#include <dirent.h>
Viet-Trung Luu2a394492015-03-04 08:03:20 -08008#include <errno.h>
9#include <fcntl.h>
10#include <stdio.h>
11#include <sys/stat.h>
12#include <sys/types.h>
Viet-Trung Luu2a394492015-03-04 08:03:20 -080013#include <unistd.h>
14
Viet-Trung Luu2a394492015-03-04 08:03:20 -080015#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 "services/files/file_impl.h"
Viet-Trung Luu451ed412015-03-09 11:11:11 -070021#include "services/files/shared_impl.h"
Viet-Trung Luu2a394492015-03-04 08:03:20 -080022#include "services/files/util.h"
23
24namespace mojo {
25namespace files {
26
27namespace {
28
Viet-Trung Luu451ed412015-03-09 11:11:11 -070029// Calls |closedir()| on a |DIR*|.
30struct DIRDeleter {
31 void operator()(DIR* dir) const { PCHECK(closedir(dir) == 0); }
32};
Viet-Trung Luu06acf5f2015-03-13 11:05:31 -070033using ScopedDIR = scoped_ptr<DIR, DIRDeleter>;
Viet-Trung Luu451ed412015-03-09 11:11:11 -070034
Viet-Trung Luu2a394492015-03-04 08:03:20 -080035Error 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)))
John Grossman87fe5142015-10-02 10:11:43 -070040 return Error::UNIMPLEMENTED;
Viet-Trung Luu2a394492015-03-04 08:03:20 -080041
42 // At least one of |kOpenFlagRead| or |kOpenFlagWrite| must be set.
43 if (!(open_flags & (kOpenFlagRead | kOpenFlagWrite)))
John Grossman87fe5142015-10-02 10:11:43 -070044 return Error::INVALID_ARGUMENT;
Viet-Trung Luu2a394492015-03-04 08:03:20 -080045
46 // |kOpenFlagCreate| requires |kOpenFlagWrite|.
47 if ((open_flags & kOpenFlagCreate) && !(open_flags & kOpenFlagWrite))
John Grossman87fe5142015-10-02 10:11:43 -070048 return Error::INVALID_ARGUMENT;
Viet-Trung Luu2a394492015-03-04 08:03:20 -080049
50 // |kOpenFlagExclusive| requires |kOpenFlagCreate|.
51 if ((open_flags & kOpenFlagExclusive) && !(open_flags & kOpenFlagCreate))
John Grossman87fe5142015-10-02 10:11:43 -070052 return Error::INVALID_ARGUMENT;
Viet-Trung Luu2a394492015-03-04 08:03:20 -080053
54 if (is_directory) {
55 // Check that file-only flags aren't set.
56 if ((open_flags & (kOpenFlagAppend | kOpenFlagTruncate)))
John Grossman87fe5142015-10-02 10:11:43 -070057 return Error::INVALID_ARGUMENT;
58 return Error::OK;
Viet-Trung Luu2a394492015-03-04 08:03:20 -080059 }
60
61 // File-only flags:
62
63 // |kOpenFlagAppend| requires |kOpenFlagWrite|.
64 if ((open_flags & kOpenFlagAppend) && !(open_flags & kOpenFlagWrite))
John Grossman87fe5142015-10-02 10:11:43 -070065 return Error::INVALID_ARGUMENT;
Viet-Trung Luu2a394492015-03-04 08:03:20 -080066
67 // |kOpenFlagTruncate| requires |kOpenFlagWrite|.
68 if ((open_flags & kOpenFlagTruncate) && !(open_flags & kOpenFlagWrite))
John Grossman87fe5142015-10-02 10:11:43 -070069 return Error::INVALID_ARGUMENT;
Viet-Trung Luu2a394492015-03-04 08:03:20 -080070
John Grossman87fe5142015-10-02 10:11:43 -070071 return Error::OK;
Viet-Trung Luu2a394492015-03-04 08:03:20 -080072}
73
Viet-Trung Luu451ed412015-03-09 11:11:11 -070074Error ValidateDeleteFlags(uint32_t delete_flags) {
75 // Treat unknown flags as "unimplemented".
76 if ((delete_flags &
77 ~(kDeleteFlagFileOnly | kDeleteFlagDirectoryOnly |
78 kDeleteFlagRecursive)))
John Grossman87fe5142015-10-02 10:11:43 -070079 return Error::UNIMPLEMENTED;
Viet-Trung Luu451ed412015-03-09 11:11:11 -070080
81 // Only one of the three currently-defined flags may be set.
82 if ((delete_flags & kDeleteFlagFileOnly) &&
83 (delete_flags & (kDeleteFlagDirectoryOnly | kDeleteFlagRecursive)))
John Grossman87fe5142015-10-02 10:11:43 -070084 return Error::INVALID_ARGUMENT;
Viet-Trung Luu451ed412015-03-09 11:11:11 -070085 if ((delete_flags & kDeleteFlagDirectoryOnly) &&
86 (delete_flags & (kDeleteFlagFileOnly | kDeleteFlagRecursive)))
John Grossman87fe5142015-10-02 10:11:43 -070087 return Error::INVALID_ARGUMENT;
Viet-Trung Luu451ed412015-03-09 11:11:11 -070088 if ((delete_flags & kDeleteFlagRecursive) &&
89 (delete_flags & (kDeleteFlagFileOnly | kDeleteFlagDirectoryOnly)))
John Grossman87fe5142015-10-02 10:11:43 -070090 return Error::INVALID_ARGUMENT;
Viet-Trung Luu451ed412015-03-09 11:11:11 -070091
John Grossman87fe5142015-10-02 10:11:43 -070092 return Error::OK;
Viet-Trung Luu451ed412015-03-09 11:11:11 -070093}
94
Viet-Trung Luu2a394492015-03-04 08:03:20 -080095} // namespace
96
97DirectoryImpl::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
106DirectoryImpl::~DirectoryImpl() {
107}
108
Viet-Trung Luu05806242015-04-03 10:17:37 -0700109void DirectoryImpl::Read(const ReadCallback& callback) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700110 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()) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800118 callback.Run(ErrnoToError(errno), nullptr);
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700119 return;
120 }
121
122 ScopedDIR dir(fdopendir(fd.release()));
123 if (!dir) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800124 callback.Run(ErrnoToError(errno), nullptr);
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700125 return;
126 }
127
James Robinsondc335c02015-10-09 17:23:30 -0700128 auto result = Array<DirectoryEntryPtr>::New(0);
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700129
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.
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800140 callback.Run(ErrnoToError(error), nullptr);
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700141 return;
142 }
143
144 if (!entry)
145 break;
146
147 n++;
148 if (n > kMaxReadCount) {
149 LOG(WARNING) << "Directory contents truncated";
John Grossman87fe5142015-10-02 10:11:43 -0700150 callback.Run(Error::OUT_OF_RANGE, result.Pass());
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700151 return;
152 }
153
154 DirectoryEntryPtr e = DirectoryEntry::New();
155 switch (entry->d_type) {
156 case DT_DIR:
John Grossman87fe5142015-10-02 10:11:43 -0700157 e->type = FileType::DIRECTORY;
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700158 break;
159 case DT_REG:
John Grossman87fe5142015-10-02 10:11:43 -0700160 e->type = FileType::REGULAR_FILE;
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700161 break;
162 default:
John Grossman87fe5142015-10-02 10:11:43 -0700163 e->type = FileType::UNKNOWN;
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700164 break;
165 }
166 e->name = String(entry->d_name);
167 result.push_back(e.Pass());
168 }
169
John Grossman87fe5142015-10-02 10:11:43 -0700170 callback.Run(Error::OK, result.Pass());
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800171}
172
Viet-Trung Luu05806242015-04-03 10:17:37 -0700173void DirectoryImpl::Stat(const StatCallback& callback) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700174 DCHECK(dir_fd_.is_valid());
John Grossman87fe5142015-10-02 10:11:43 -0700175 StatFD(dir_fd_.get(), FileType::DIRECTORY, callback);
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800176}
177
178void DirectoryImpl::Touch(TimespecOrNowPtr atime,
179 TimespecOrNowPtr mtime,
Viet-Trung Luu05806242015-04-03 10:17:37 -0700180 const TouchCallback& callback) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700181 DCHECK(dir_fd_.is_valid());
182 TouchFD(dir_fd_.get(), atime.Pass(), mtime.Pass(), callback);
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800183}
184
185// TODO(vtl): Move the implementation to a thread pool.
186void DirectoryImpl::OpenFile(const String& path,
187 InterfaceRequest<File> file,
188 uint32_t open_flags,
Viet-Trung Luu05806242015-04-03 10:17:37 -0700189 const OpenFileCallback& callback) {
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800190 DCHECK(!path.is_null());
191 DCHECK(dir_fd_.is_valid());
192
John Grossman08f574e2015-09-22 16:57:57 -0700193 Error error = IsPathValid(path);
John Grossman87fe5142015-10-02 10:11:43 -0700194 if (error != Error::OK) {
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800195 callback.Run(error);
196 return;
197 }
198 // TODO(vtl): Make sure the path doesn't exit this directory (if appropriate).
199 // TODO(vtl): Maybe allow absolute paths?
200
John Grossman08f574e2015-09-22 16:57:57 -0700201 error = ValidateOpenFlags(open_flags, false);
John Grossman87fe5142015-10-02 10:11:43 -0700202 if (error != Error::OK) {
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800203 callback.Run(error);
204 return;
205 }
206
207 int flags = 0;
208 if ((open_flags & kOpenFlagRead))
209 flags |= (open_flags & kOpenFlagWrite) ? O_RDWR : O_RDONLY;
210 else
211 flags |= O_WRONLY;
212 if ((open_flags & kOpenFlagCreate))
213 flags |= O_CREAT;
214 if ((open_flags & kOpenFlagExclusive))
215 flags |= O_EXCL;
216 if ((open_flags & kOpenFlagAppend))
217 flags |= O_APPEND;
218 if ((open_flags & kOpenFlagTruncate))
219 flags |= O_TRUNC;
220
221 base::ScopedFD file_fd(
222 HANDLE_EINTR(openat(dir_fd_.get(), path.get().c_str(), flags, 0600)));
223 if (!file_fd.is_valid()) {
224 callback.Run(ErrnoToError(errno));
225 return;
226 }
227
228 if (file.is_pending())
229 new FileImpl(file.Pass(), file_fd.Pass());
John Grossman87fe5142015-10-02 10:11:43 -0700230 callback.Run(Error::OK);
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800231}
232
233void DirectoryImpl::OpenDirectory(const String& path,
234 InterfaceRequest<Directory> directory,
235 uint32_t open_flags,
Viet-Trung Luu05806242015-04-03 10:17:37 -0700236 const OpenDirectoryCallback& callback) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700237 DCHECK(!path.is_null());
238 DCHECK(dir_fd_.is_valid());
239
John Grossman08f574e2015-09-22 16:57:57 -0700240 Error error = IsPathValid(path);
John Grossman87fe5142015-10-02 10:11:43 -0700241 if (error != Error::OK) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700242 callback.Run(error);
243 return;
244 }
245 // TODO(vtl): Make sure the path doesn't exit this directory (if appropriate).
246 // TODO(vtl): Maybe allow absolute paths?
247
John Grossman08f574e2015-09-22 16:57:57 -0700248 error = ValidateOpenFlags(open_flags, false);
John Grossman87fe5142015-10-02 10:11:43 -0700249 if (error != Error::OK) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700250 callback.Run(error);
251 return;
252 }
253
254 // TODO(vtl): Implement read-only (whatever that means).
255 if (!(open_flags & kOpenFlagWrite)) {
John Grossman87fe5142015-10-02 10:11:43 -0700256 callback.Run(Error::UNIMPLEMENTED);
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700257 return;
258 }
259
260 if ((open_flags & kOpenFlagCreate)) {
261 if (mkdirat(dir_fd_.get(), path.get().c_str(), 0700) != 0) {
262 // Allow |EEXIST| if |kOpenFlagExclusive| is not set. Note, however, that
263 // it does not guarantee that |path| is a directory.
264 // TODO(vtl): Hrm, ponder if we should check that |path| is a directory.
John McCutchan2c2b5c82015-12-18 15:13:45 -0800265 if ((errno != EEXIST) || (open_flags & kOpenFlagExclusive)) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700266 callback.Run(ErrnoToError(errno));
267 return;
268 }
269 }
270 }
271
272 base::ScopedFD new_dir_fd(
273 HANDLE_EINTR(openat(dir_fd_.get(), path.get().c_str(), O_DIRECTORY, 0)));
274 if (!new_dir_fd.is_valid()) {
275 callback.Run(ErrnoToError(errno));
276 return;
277 }
278
279 if (directory.is_pending())
280 new DirectoryImpl(directory.Pass(), new_dir_fd.Pass(), nullptr);
John Grossman87fe5142015-10-02 10:11:43 -0700281 callback.Run(Error::OK);
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800282}
283
284void DirectoryImpl::Rename(const String& path,
285 const String& new_path,
Viet-Trung Luu05806242015-04-03 10:17:37 -0700286 const RenameCallback& callback) {
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800287 DCHECK(!path.is_null());
288 DCHECK(!new_path.is_null());
289 DCHECK(dir_fd_.is_valid());
290
John Grossman08f574e2015-09-22 16:57:57 -0700291 Error error = IsPathValid(path);
John Grossman87fe5142015-10-02 10:11:43 -0700292 if (error != Error::OK) {
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800293 callback.Run(error);
294 return;
295 }
John Grossman08f574e2015-09-22 16:57:57 -0700296
297 error = IsPathValid(new_path);
John Grossman87fe5142015-10-02 10:11:43 -0700298 if (error != Error::OK) {
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800299 callback.Run(error);
300 return;
301 }
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700302 // TODO(vtl): See TODOs about |path| in OpenFile().
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800303
304 if (renameat(dir_fd_.get(), path.get().c_str(), dir_fd_.get(),
305 new_path.get().c_str())) {
306 callback.Run(ErrnoToError(errno));
307 return;
308 }
309
John Grossman87fe5142015-10-02 10:11:43 -0700310 callback.Run(Error::OK);
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800311}
312
313void DirectoryImpl::Delete(const String& path,
314 uint32_t delete_flags,
Viet-Trung Luu05806242015-04-03 10:17:37 -0700315 const DeleteCallback& callback) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700316 DCHECK(!path.is_null());
317 DCHECK(dir_fd_.is_valid());
318
John Grossman08f574e2015-09-22 16:57:57 -0700319 Error error = IsPathValid(path);
John Grossman87fe5142015-10-02 10:11:43 -0700320 if (error != Error::OK) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700321 callback.Run(error);
322 return;
323 }
324 // TODO(vtl): See TODOs about |path| in OpenFile().
325
John Grossman08f574e2015-09-22 16:57:57 -0700326 error = ValidateDeleteFlags(delete_flags);
John Grossman87fe5142015-10-02 10:11:43 -0700327 if (error != Error::OK) {
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700328 callback.Run(error);
329 return;
330 }
331
332 // TODO(vtl): Recursive not yet supported.
333 if ((delete_flags & kDeleteFlagRecursive)) {
John Grossman87fe5142015-10-02 10:11:43 -0700334 callback.Run(Error::UNIMPLEMENTED);
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700335 return;
336 }
337
338 // First try deleting it as a file, unless we're told to do directory-only.
339 if (!(delete_flags & kDeleteFlagDirectoryOnly)) {
340 if (unlinkat(dir_fd_.get(), path.get().c_str(), 0) == 0) {
John Grossman87fe5142015-10-02 10:11:43 -0700341 callback.Run(Error::OK);
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700342 return;
343 }
344
345 // If file-only, don't continue.
346 if ((delete_flags & kDeleteFlagFileOnly)) {
347 callback.Run(ErrnoToError(errno));
348 return;
349 }
350 }
351
352 // Try deleting it as a directory.
353 if (unlinkat(dir_fd_.get(), path.get().c_str(), AT_REMOVEDIR) == 0) {
John Grossman87fe5142015-10-02 10:11:43 -0700354 callback.Run(Error::OK);
Viet-Trung Luu451ed412015-03-09 11:11:11 -0700355 return;
356 }
357
358 callback.Run(ErrnoToError(errno));
Viet-Trung Luu2a394492015-03-04 08:03:20 -0800359}
360
361} // namespace files
362} // namespace mojo