OLD | NEW |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |