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

Side by Side Diff: runtime/bin/file_android.cc

Issue 3007703002: [dart:io] Namespaces for file IO (Closed)
Patch Set: Remove namespace_unsupported.cc Created 3 years, 3 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 | « runtime/bin/file.cc ('k') | runtime/bin/file_fuchsia.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 (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "platform/globals.h" 5 #include "platform/globals.h"
6 #if defined(HOST_OS_ANDROID) 6 #if defined(HOST_OS_ANDROID)
7 7
8 #include "bin/file.h" 8 #include "bin/file.h"
9 9
10 #include <errno.h> // NOLINT 10 #include <errno.h> // NOLINT
11 #include <fcntl.h> // NOLINT 11 #include <fcntl.h> // NOLINT
12 #include <libgen.h> // NOLINT 12 #include <libgen.h> // NOLINT
13 #include <sys/mman.h> // NOLINT 13 #include <sys/mman.h> // NOLINT
14 #include <sys/sendfile.h> // NOLINT 14 #include <sys/sendfile.h> // NOLINT
15 #include <sys/stat.h> // NOLINT 15 #include <sys/stat.h> // NOLINT
16 #include <sys/syscall.h> // NOLINT
16 #include <sys/types.h> // NOLINT 17 #include <sys/types.h> // NOLINT
17 #include <unistd.h> // NOLINT 18 #include <unistd.h> // NOLINT
18 #include <utime.h> // NOLINT 19 #include <utime.h> // NOLINT
19 20
20 #include "bin/builtin.h" 21 #include "bin/builtin.h"
21 #include "bin/fdutils.h" 22 #include "bin/fdutils.h"
22 #include "bin/log.h" 23 #include "bin/log.h"
24 #include "bin/namespace.h"
23 #include "platform/signal_blocker.h" 25 #include "platform/signal_blocker.h"
24 #include "platform/utils.h" 26 #include "platform/utils.h"
25 27
26 namespace dart { 28 namespace dart {
27 namespace bin { 29 namespace bin {
28 30
29 class FileHandle { 31 class FileHandle {
30 public: 32 public:
31 explicit FileHandle(int fd) : fd_(fd) {} 33 explicit FileHandle(int fd) : fd_(fd) {}
32 ~FileHandle() {} 34 ~FileHandle() {}
33 int fd() const { return fd_; } 35 int fd() const { return fd_; }
34 void set_fd(int fd) { fd_ = fd; } 36 void set_fd(int fd) { fd_ = fd; }
35 37
36 private: 38 private:
37 int fd_; 39 int fd_;
38 40
39 DISALLOW_COPY_AND_ASSIGN(FileHandle); 41 DISALLOW_COPY_AND_ASSIGN(FileHandle);
40 }; 42 };
41 43
42 File::~File() { 44 File::~File() {
43 if (!IsClosed()) { 45 if (!IsClosed() && (handle_->fd() != STDOUT_FILENO) &&
46 (handle_->fd() != STDERR_FILENO)) {
44 Close(); 47 Close();
45 } 48 }
46 delete handle_; 49 delete handle_;
47 } 50 }
48 51
49 void File::Close() { 52 void File::Close() {
50 ASSERT(handle_->fd() >= 0); 53 ASSERT(handle_->fd() >= 0);
51 if (handle_->fd() == STDOUT_FILENO) { 54 if (handle_->fd() == STDOUT_FILENO) {
52 // If stdout, redirect fd to /dev/null. 55 // If stdout, redirect fd to /dev/null.
53 int null_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY)); 56 int null_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
54 ASSERT(null_fd >= 0); 57 ASSERT(null_fd >= 0);
55 VOID_TEMP_FAILURE_RETRY(dup2(null_fd, handle_->fd())); 58 VOID_TEMP_FAILURE_RETRY(dup2(null_fd, handle_->fd()));
56 VOID_TEMP_FAILURE_RETRY(close(null_fd)); 59 VOID_TEMP_FAILURE_RETRY(close(null_fd));
57 } else { 60 } else {
58 int err = TEMP_FAILURE_RETRY(close(handle_->fd())); 61 int err = TEMP_FAILURE_RETRY(close(handle_->fd()));
59 if (err != 0) { 62 if (err != 0) {
60 const int kBufferSize = 1024; 63 const int kBufferSize = 1024;
61 char error_message[kBufferSize]; 64 char error_buf[kBufferSize];
62 Utils::StrError(errno, error_message, kBufferSize); 65 Log::PrintErr("%s\n", Utils::StrError(errno, error_buf, kBufferSize));
63 Log::PrintErr("%s\n", error_message);
64 } 66 }
65 } 67 }
66 handle_->set_fd(kClosedFd); 68 handle_->set_fd(kClosedFd);
67 } 69 }
68 70
69 intptr_t File::GetFD() { 71 intptr_t File::GetFD() {
70 return handle_->fd(); 72 return handle_->fd();
71 } 73 }
72 74
73 bool File::IsClosed() { 75 bool File::IsClosed() {
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 return st.st_size; 191 return st.st_size;
190 } 192 }
191 return -1; 193 return -1;
192 } 194 }
193 195
194 File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) { 196 File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) {
195 UNREACHABLE(); 197 UNREACHABLE();
196 return NULL; 198 return NULL;
197 } 199 }
198 200
199 File* File::Open(const char* name, FileOpenMode mode) { 201 File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) {
202 NamespaceScope ns(namespc, name);
200 // Report errors for non-regular files. 203 // Report errors for non-regular files.
201 struct stat st; 204 struct stat st;
202 if (NO_RETRY_EXPECTED(stat(name, &st)) == 0) { 205 if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &st, 0)) == 0) {
203 if (!S_ISREG(st.st_mode)) { 206 // Only accept regular files, character devices, and pipes.
207 if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode) && !S_ISFIFO(st.st_mode)) {
204 errno = (S_ISDIR(st.st_mode)) ? EISDIR : ENOENT; 208 errno = (S_ISDIR(st.st_mode)) ? EISDIR : ENOENT;
205 return NULL; 209 return NULL;
206 } 210 }
207 } 211 }
208 int flags = O_RDONLY; 212 int flags = O_RDONLY;
209 if ((mode & kWrite) != 0) { 213 if ((mode & kWrite) != 0) {
210 ASSERT((mode & kWriteOnly) == 0); 214 ASSERT((mode & kWriteOnly) == 0);
211 flags = (O_RDWR | O_CREAT); 215 flags = (O_RDWR | O_CREAT);
212 } 216 }
213 if ((mode & kWriteOnly) != 0) { 217 if ((mode & kWriteOnly) != 0) {
214 ASSERT((mode & kWrite) == 0); 218 ASSERT((mode & kWrite) == 0);
215 flags = (O_WRONLY | O_CREAT); 219 flags = (O_WRONLY | O_CREAT);
216 } 220 }
217 if ((mode & kTruncate) != 0) { 221 if ((mode & kTruncate) != 0) {
218 flags = flags | O_TRUNC; 222 flags = flags | O_TRUNC;
219 } 223 }
220 flags |= O_CLOEXEC; 224 flags |= O_CLOEXEC;
221 int fd = TEMP_FAILURE_RETRY(open(name, flags, 0666)); 225 const int fd = TEMP_FAILURE_RETRY(openat(ns.fd(), ns.path(), flags, 0666));
222 if (fd < 0) { 226 if (fd < 0) {
223 return NULL; 227 return NULL;
224 } 228 }
225 if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) || 229 if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
226 (((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) { 230 (((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
227 int64_t position = lseek64(fd, 0, SEEK_END); 231 int64_t position = NO_RETRY_EXPECTED(lseek(fd, 0, SEEK_END));
228 if (position < 0) { 232 if (position < 0) {
229 return NULL; 233 return NULL;
230 } 234 }
231 } 235 }
232 return new File(new FileHandle(fd)); 236 return new File(new FileHandle(fd));
233 } 237 }
234 238
235 File* File::OpenStdio(int fd) { 239 File* File::OpenStdio(int fd) {
236 return ((fd < 0) || (2 < fd)) ? NULL : new File(new FileHandle(fd)); 240 return ((fd < 0) || (2 < fd)) ? NULL : new File(new FileHandle(fd));
237 } 241 }
238 242
239 bool File::Exists(const char* name) { 243 bool File::Exists(Namespace* namespc, const char* name) {
244 NamespaceScope ns(namespc, name);
240 struct stat st; 245 struct stat st;
241 if (NO_RETRY_EXPECTED(stat(name, &st)) == 0) { 246 if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &st, 0)) == 0) {
242 // Everything but a directory and a link is a file to Dart. 247 // Everything but a directory and a link is a file to Dart.
243 return !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode); 248 return !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode);
244 } else { 249 } else {
245 return false; 250 return false;
246 } 251 }
247 } 252 }
248 253
249 bool File::Create(const char* name) { 254 bool File::Create(Namespace* namespc, const char* name) {
250 int fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CREAT | O_CLOEXEC, 0666)); 255 NamespaceScope ns(namespc, name);
256 const int fd = TEMP_FAILURE_RETRY(
257 openat(ns.fd(), ns.path(), O_RDONLY | O_CREAT | O_CLOEXEC, 0666));
251 if (fd < 0) { 258 if (fd < 0) {
252 return false; 259 return false;
253 } 260 }
254 // File.create returns a File, so we shouldn't be giving the illusion that the 261 // File.create returns a File, so we shouldn't be giving the illusion that the
255 // call has created a file or that a file already exists if there is already 262 // call has created a file or that a file already exists if there is already
256 // an entity at the same path that is a directory or a link. 263 // an entity at the same path that is a directory or a link.
257 bool is_file = true; 264 bool is_file = true;
258 struct stat st; 265 struct stat st;
259 if (NO_RETRY_EXPECTED(fstat(fd, &st)) == 0) { 266 if (TEMP_FAILURE_RETRY(fstat(fd, &st)) == 0) {
260 if (S_ISDIR(st.st_mode)) { 267 if (S_ISDIR(st.st_mode)) {
261 errno = EISDIR; 268 errno = EISDIR;
262 is_file = false; 269 is_file = false;
263 } else if (S_ISLNK(st.st_mode)) { 270 } else if (S_ISLNK(st.st_mode)) {
264 errno = ENOENT; 271 errno = ENOENT;
265 is_file = false; 272 is_file = false;
266 } 273 }
267 } 274 }
268 FDUtils::SaveErrorAndClose(fd); 275 FDUtils::SaveErrorAndClose(fd);
269 return is_file; 276 return is_file;
270 } 277 }
271 278
272 bool File::CreateLink(const char* name, const char* target) { 279 // symlinkat is added to Android libc in android-21.
273 int status = NO_RETRY_EXPECTED(symlink(target, name)); 280 static int SymlinkAt(const char* oldpath, int newdirfd, const char* newpath) {
274 return (status == 0); 281 return syscall(__NR_symlinkat, oldpath, newdirfd, newpath);
275 } 282 }
276 283
277 File::Type File::GetType(const char* pathname, bool follow_links) { 284 bool File::CreateLink(Namespace* namespc,
285 const char* name,
286 const char* target) {
287 NamespaceScope ns(namespc, name);
288 return NO_RETRY_EXPECTED(SymlinkAt(target, ns.fd(), ns.path())) == 0;
289 }
290
291 File::Type File::GetType(Namespace* namespc,
292 const char* name,
293 bool follow_links) {
294 NamespaceScope ns(namespc, name);
278 struct stat entry_info; 295 struct stat entry_info;
279 int stat_success; 296 int stat_success;
280 if (follow_links) { 297 if (follow_links) {
281 stat_success = NO_RETRY_EXPECTED(stat(pathname, &entry_info)); 298 stat_success =
299 TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &entry_info, 0));
282 } else { 300 } else {
283 stat_success = NO_RETRY_EXPECTED(lstat(pathname, &entry_info)); 301 stat_success = TEMP_FAILURE_RETRY(
302 fstatat(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW));
284 } 303 }
285 if (stat_success == -1) { 304 if (stat_success == -1) {
286 return File::kDoesNotExist; 305 return File::kDoesNotExist;
287 } 306 }
288 if (S_ISDIR(entry_info.st_mode)) { 307 if (S_ISDIR(entry_info.st_mode)) {
289 return File::kIsDirectory; 308 return File::kIsDirectory;
290 } 309 }
291 if (S_ISREG(entry_info.st_mode)) { 310 if (S_ISREG(entry_info.st_mode)) {
292 return File::kIsFile; 311 return File::kIsFile;
293 } 312 }
294 if (S_ISLNK(entry_info.st_mode)) { 313 if (S_ISLNK(entry_info.st_mode)) {
295 return File::kIsLink; 314 return File::kIsLink;
296 } 315 }
297 return File::kDoesNotExist; 316 return File::kDoesNotExist;
298 } 317 }
299 318
300 static bool CheckTypeAndSetErrno(const char* name, 319 static bool CheckTypeAndSetErrno(Namespace* namespc,
320 const char* name,
301 File::Type expected, 321 File::Type expected,
302 bool follow_links) { 322 bool follow_links) {
303 File::Type actual = File::GetType(name, follow_links); 323 File::Type actual = File::GetType(namespc, name, follow_links);
304 if (actual == expected) { 324 if (actual == expected) {
305 return true; 325 return true;
306 } 326 }
307 switch (actual) { 327 switch (actual) {
308 case File::kIsDirectory: 328 case File::kIsDirectory:
309 errno = EISDIR; 329 errno = EISDIR;
310 break; 330 break;
311 case File::kDoesNotExist: 331 case File::kDoesNotExist:
312 errno = ENOENT; 332 errno = ENOENT;
313 break; 333 break;
314 default: 334 default:
315 errno = EINVAL; 335 errno = EINVAL;
316 break; 336 break;
317 } 337 }
318 return false; 338 return false;
319 } 339 }
320 340
321 bool File::Delete(const char* name) { 341 bool File::Delete(Namespace* namespc, const char* name) {
322 return CheckTypeAndSetErrno(name, kIsFile, true) && 342 NamespaceScope ns(namespc, name);
323 (NO_RETRY_EXPECTED(unlink(name)) == 0); 343 return CheckTypeAndSetErrno(namespc, name, kIsFile, true) &&
344 (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
324 } 345 }
325 346
326 bool File::DeleteLink(const char* name) { 347 bool File::DeleteLink(Namespace* namespc, const char* name) {
327 return CheckTypeAndSetErrno(name, kIsLink, false) && 348 NamespaceScope ns(namespc, name);
328 (NO_RETRY_EXPECTED(unlink(name)) == 0); 349 return CheckTypeAndSetErrno(namespc, name, kIsLink, false) &&
350 (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
329 } 351 }
330 352
331 bool File::Rename(const char* old_path, const char* new_path) { 353 bool File::Rename(Namespace* namespc,
332 return CheckTypeAndSetErrno(old_path, kIsFile, true) && 354 const char* old_path,
333 (NO_RETRY_EXPECTED(rename(old_path, new_path)) == 0); 355 const char* new_path) {
356 NamespaceScope oldns(namespc, old_path);
357 NamespaceScope newns(namespc, new_path);
358 return CheckTypeAndSetErrno(namespc, old_path, kIsFile, true) &&
359 (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
360 newns.path())) == 0);
334 } 361 }
335 362
336 bool File::RenameLink(const char* old_path, const char* new_path) { 363 bool File::RenameLink(Namespace* namespc,
337 return CheckTypeAndSetErrno(old_path, kIsLink, false) && 364 const char* old_path,
338 (NO_RETRY_EXPECTED(rename(old_path, new_path)) == 0); 365 const char* new_path) {
366 NamespaceScope oldns(namespc, old_path);
367 NamespaceScope newns(namespc, new_path);
368 return CheckTypeAndSetErrno(namespc, old_path, kIsLink, false) &&
369 (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
370 newns.path())) == 0);
339 } 371 }
340 372
341 bool File::Copy(const char* old_path, const char* new_path) { 373 bool File::Copy(Namespace* namespc,
342 if (!CheckTypeAndSetErrno(old_path, kIsFile, true)) { 374 const char* old_path,
375 const char* new_path) {
376 if (!CheckTypeAndSetErrno(namespc, old_path, kIsFile, true)) {
343 return false; 377 return false;
344 } 378 }
379 NamespaceScope oldns(namespc, old_path);
345 struct stat st; 380 struct stat st;
346 if (NO_RETRY_EXPECTED(stat(old_path, &st)) != 0) { 381 if (TEMP_FAILURE_RETRY(fstatat(oldns.fd(), oldns.path(), &st, 0)) != 0) {
347 return false; 382 return false;
348 } 383 }
349 int old_fd = TEMP_FAILURE_RETRY(open(old_path, O_RDONLY | O_CLOEXEC)); 384 const int old_fd = TEMP_FAILURE_RETRY(
385 openat(oldns.fd(), oldns.path(), O_RDONLY | O_CLOEXEC));
350 if (old_fd < 0) { 386 if (old_fd < 0) {
351 return false; 387 return false;
352 } 388 }
353 int new_fd = TEMP_FAILURE_RETRY( 389 NamespaceScope newns(namespc, new_path);
354 open(new_path, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, st.st_mode)); 390 const int new_fd = TEMP_FAILURE_RETRY(
391 openat(newns.fd(), newns.path(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC,
392 st.st_mode));
355 if (new_fd < 0) { 393 if (new_fd < 0) {
356 VOID_TEMP_FAILURE_RETRY(close(old_fd)); 394 VOID_TEMP_FAILURE_RETRY(close(old_fd));
357 return false; 395 return false;
358 } 396 }
359 off_t offset = 0; 397 off_t offset = 0;
360 int result = 1; 398 intptr_t result = 1;
361 while (result > 0) { 399 while (result > 0) {
362 // Loop to ensure we copy everything, and not only up to 2GB. 400 // Loop to ensure we copy everything, and not only up to 2GB.
363 result = NO_RETRY_EXPECTED(sendfile(new_fd, old_fd, &offset, kMaxUint32)); 401 result = NO_RETRY_EXPECTED(sendfile(new_fd, old_fd, &offset, kMaxUint32));
364 } 402 }
365 // From sendfile man pages: 403 // From sendfile man pages:
366 // Applications may wish to fall back to read(2)/write(2) in the case 404 // Applications may wish to fall back to read(2)/write(2) in the case
367 // where sendfile() fails with EINVAL or ENOSYS. 405 // where sendfile() fails with EINVAL or ENOSYS.
368 if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) { 406 if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) {
369 const intptr_t kBufferSize = 8 * KB; 407 const intptr_t kBufferSize = 8 * KB;
370 uint8_t buffer[kBufferSize]; 408 uint8_t buffer[kBufferSize];
371 while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) > 409 while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) >
372 0) { 410 0) {
373 int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result)); 411 int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result));
374 if (wrote != result) { 412 if (wrote != result) {
375 result = -1; 413 result = -1;
376 break; 414 break;
377 } 415 }
378 } 416 }
379 } 417 }
380 int e = errno; 418 int e = errno;
381 VOID_TEMP_FAILURE_RETRY(close(old_fd)); 419 VOID_TEMP_FAILURE_RETRY(close(old_fd));
382 VOID_TEMP_FAILURE_RETRY(close(new_fd)); 420 VOID_TEMP_FAILURE_RETRY(close(new_fd));
383 if (result < 0) { 421 if (result < 0) {
384 VOID_NO_RETRY_EXPECTED(unlink(new_path)); 422 VOID_NO_RETRY_EXPECTED(unlinkat(newns.fd(), newns.path(), 0));
385 errno = e; 423 errno = e;
386 return false; 424 return false;
387 } 425 }
388 return true; 426 return true;
389 } 427 }
390 428
391 static bool StatHelper(const char* name, struct stat* st) { 429 static bool StatHelper(Namespace* namespc, const char* name, struct stat* st) {
392 if (NO_RETRY_EXPECTED(stat(name, st)) != 0) { 430 NamespaceScope ns(namespc, name);
431 if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), st, 0)) != 0) {
393 return false; 432 return false;
394 } 433 }
395 // Signal an error if it's a directory. 434 // Signal an error if it's a directory.
396 if (S_ISDIR(st->st_mode)) { 435 if (S_ISDIR(st->st_mode)) {
397 errno = EISDIR; 436 errno = EISDIR;
398 return false; 437 return false;
399 } 438 }
400 // Otherwise assume the caller knows what it's doing. 439 // Otherwise assume the caller knows what it's doing.
401 return true; 440 return true;
402 } 441 }
403 442
404 int64_t File::LengthFromPath(const char* name) { 443 int64_t File::LengthFromPath(Namespace* namespc, const char* name) {
405 struct stat st; 444 struct stat st;
406 if (!StatHelper(name, &st)) { 445 if (!StatHelper(namespc, name, &st)) {
407 return -1; 446 return -1;
408 } 447 }
409 return st.st_size; 448 return st.st_size;
410 } 449 }
411 450
412 void File::Stat(const char* name, int64_t* data) { 451 static void MillisecondsToTimespec(int64_t millis, struct timespec* t) {
452 ASSERT(t != NULL);
453 t->tv_sec = millis / kMillisecondsPerSecond;
454 t->tv_nsec = (millis - (t->tv_sec * kMillisecondsPerSecond)) * 1000L;
455 }
456
457 void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
458 NamespaceScope ns(namespc, name);
413 struct stat st; 459 struct stat st;
414 if (NO_RETRY_EXPECTED(stat(name, &st)) == 0) { 460 if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &st, 0)) == 0) {
415 if (S_ISREG(st.st_mode)) { 461 if (S_ISREG(st.st_mode)) {
416 data[kType] = kIsFile; 462 data[kType] = kIsFile;
417 } else if (S_ISDIR(st.st_mode)) { 463 } else if (S_ISDIR(st.st_mode)) {
418 data[kType] = kIsDirectory; 464 data[kType] = kIsDirectory;
419 } else if (S_ISLNK(st.st_mode)) { 465 } else if (S_ISLNK(st.st_mode)) {
420 data[kType] = kIsLink; 466 data[kType] = kIsLink;
421 } else { 467 } else {
422 data[kType] = kDoesNotExist; 468 data[kType] = kDoesNotExist;
423 } 469 }
424 data[kCreatedTime] = static_cast<int64_t>(st.st_ctime) * 1000; 470 data[kCreatedTime] = static_cast<int64_t>(st.st_ctime) * 1000;
425 data[kModifiedTime] = static_cast<int64_t>(st.st_mtime) * 1000; 471 data[kModifiedTime] = static_cast<int64_t>(st.st_mtime) * 1000;
426 data[kAccessedTime] = static_cast<int64_t>(st.st_atime) * 1000; 472 data[kAccessedTime] = static_cast<int64_t>(st.st_atime) * 1000;
427 data[kMode] = st.st_mode; 473 data[kMode] = st.st_mode;
428 data[kSize] = st.st_size; 474 data[kSize] = st.st_size;
429 } else { 475 } else {
430 data[kType] = kDoesNotExist; 476 data[kType] = kDoesNotExist;
431 } 477 }
432 } 478 }
433 479
434 time_t File::LastModified(const char* name) { 480 time_t File::LastModified(Namespace* namespc, const char* name) {
435 struct stat st; 481 struct stat st;
436 if (!StatHelper(name, &st)) { 482 if (!StatHelper(namespc, name, &st)) {
437 return -1; 483 return -1;
438 } 484 }
439 return st.st_mtime; 485 return st.st_mtime;
440 } 486 }
441 487
442 time_t File::LastAccessed(const char* name) { 488 time_t File::LastAccessed(Namespace* namespc, const char* name) {
443 struct stat st; 489 struct stat st;
444 if (!StatHelper(name, &st)) { 490 if (!StatHelper(namespc, name, &st)) {
445 return -1; 491 return -1;
446 } 492 }
447 return st.st_atime; 493 return st.st_atime;
448 } 494 }
449 495
450 bool File::SetLastAccessed(const char* name, int64_t millis) { 496 bool File::SetLastAccessed(Namespace* namespc,
497 const char* name,
498 int64_t millis) {
451 // First get the current times. 499 // First get the current times.
452 struct stat st; 500 struct stat64 st;
453 if (!StatHelper(name, &st)) { 501 if (!StatHelper(namespc, name, &st)) {
454 return false; 502 return false;
455 } 503 }
456 504
457 // Set the new time: 505 // Set the new time:
458 struct utimbuf times; 506 NamespaceScope ns(namespc, name);
459 times.actime = millis / kMillisecondsPerSecond; 507 struct timespec times[2];
460 times.modtime = st.st_mtime; 508 MillisecondsToTimespec(millis, &times[0]);
461 return utime(name, &times) == 0; 509 MillisecondsToTimespec(static_cast<int64_t>(st.st_mtime) * 1000, &times[1]);
510 return utimensat(ns.fd(), ns.path(), times, 0) == 0;
462 } 511 }
463 512
464 bool File::SetLastModified(const char* name, int64_t millis) { 513 bool File::SetLastModified(Namespace* namespc,
514 const char* name,
515 int64_t millis) {
465 // First get the current times. 516 // First get the current times.
466 struct stat st; 517 struct stat64 st;
467 if (!StatHelper(name, &st)) { 518 if (!StatHelper(namespc, name, &st)) {
468 return false; 519 return false;
469 } 520 }
470 521
471 // Set the new time: 522 // Set the new time:
472 struct utimbuf times; 523 NamespaceScope ns(namespc, name);
473 times.actime = st.st_atime; 524 struct timespec times[2];
474 times.modtime = millis / kMillisecondsPerSecond; 525 MillisecondsToTimespec(static_cast<int64_t>(st.st_atime) * 1000, &times[0]);
475 return utime(name, &times) == 0; 526 MillisecondsToTimespec(millis, &times[1]);
527 return utimensat(ns.fd(), ns.path(), times, 0) == 0;
476 } 528 }
477 529
478 const char* File::LinkTarget(const char* pathname) { 530 // readlinkat is added to Android libc in android-21.
531 static int ReadLinkAt(int dirfd,
532 const char* pathname,
533 char* buf,
534 size_t bufsize) {
535 return syscall(__NR_readlinkat, dirfd, pathname, buf, bufsize);
536 }
537
538 const char* File::LinkTarget(Namespace* namespc, const char* name) {
539 NamespaceScope ns(namespc, name);
479 struct stat link_stats; 540 struct stat link_stats;
480 if (lstat(pathname, &link_stats) != 0) { 541 const int status = TEMP_FAILURE_RETRY(
542 fstatat(ns.fd(), ns.path(), &link_stats, AT_SYMLINK_NOFOLLOW));
543 if (status != 0) {
481 return NULL; 544 return NULL;
482 } 545 }
483 if (!S_ISLNK(link_stats.st_mode)) { 546 if (!S_ISLNK(link_stats.st_mode)) {
484 errno = ENOENT; 547 errno = ENOENT;
485 return NULL; 548 return NULL;
486 } 549 }
487 size_t target_size = link_stats.st_size; 550 // Don't rely on the link_stats.st_size for the size of the link
551 // target. For some filesystems, e.g. procfs, this value is always
552 // 0. Also the link might have changed before the readlink call.
553 const int kBufferSize = PATH_MAX + 1;
554 char target[kBufferSize];
555 const int target_size =
556 TEMP_FAILURE_RETRY(ReadLinkAt(ns.fd(), ns.path(), target, kBufferSize));
557 if (target_size <= 0) {
558 return NULL;
559 }
488 char* target_name = DartUtils::ScopedCString(target_size + 1); 560 char* target_name = DartUtils::ScopedCString(target_size + 1);
489 ASSERT(target_name != NULL); 561 ASSERT(target_name != NULL);
490 size_t read_size = readlink(pathname, target_name, target_size + 1); 562 memmove(target_name, target, target_size);
491 if (read_size != target_size) {
492 return NULL;
493 }
494 target_name[target_size] = '\0'; 563 target_name[target_size] = '\0';
495 return target_name; 564 return target_name;
496 } 565 }
497 566
498 bool File::IsAbsolutePath(const char* pathname) { 567 bool File::IsAbsolutePath(const char* pathname) {
499 return ((pathname != NULL) && (pathname[0] == '/')); 568 return ((pathname != NULL) && (pathname[0] == '/'));
500 } 569 }
501 570
502 const char* File::GetCanonicalPath(const char* pathname) { 571 const char* File::ReadLink(const char* pathname) {
503 char* abs_path = NULL; 572 ASSERT(pathname != NULL);
504 if (pathname != NULL) { 573 ASSERT(IsAbsolutePath(pathname));
505 char* resolved_path = DartUtils::ScopedCString(PATH_MAX + 1); 574 struct stat link_stats;
506 ASSERT(resolved_path != NULL); 575 if (TEMP_FAILURE_RETRY(lstat(pathname, &link_stats)) != 0) {
507 do { 576 return NULL;
508 abs_path = realpath(pathname, resolved_path);
509 } while ((abs_path == NULL) && (errno == EINTR));
510 ASSERT((abs_path == NULL) || IsAbsolutePath(abs_path));
511 ASSERT((abs_path == NULL) || (abs_path == resolved_path));
512 } 577 }
578 if (!S_ISLNK(link_stats.st_mode)) {
579 errno = ENOENT;
580 return NULL;
581 }
582 // Don't rely on the link_stats.st_size for the size of the link
583 // target. For some filesystems, e.g. procfs, this value is always
584 // 0. Also the link might have changed before the readlink call.
585 const int kBufferSize = PATH_MAX + 1;
586 char target[kBufferSize];
587 size_t target_size =
588 TEMP_FAILURE_RETRY(readlink(pathname, target, kBufferSize));
589 if (target_size <= 0) {
590 return NULL;
591 }
592 char* target_name = DartUtils::ScopedCString(target_size + 1);
593 ASSERT(target_name != NULL);
594 memmove(target_name, target, target_size);
595 target_name[target_size] = '\0';
596 return target_name;
597 }
598
599 const char* File::GetCanonicalPath(Namespace* namespc, const char* name) {
600 if (name == NULL) {
601 return NULL;
602 }
603 if (!Namespace::IsDefault(namespc)) {
604 // TODO(zra): There is no realpathat(). Also chasing a symlink might result
605 // in a path to something outside of the namespace, so canonicalizing paths
606 // would have to be done carefully. For now, don't do anything.
607 return name;
608 }
609 char* abs_path;
610 char* resolved_path = DartUtils::ScopedCString(PATH_MAX + 1);
611 ASSERT(resolved_path != NULL);
612 do {
613 abs_path = realpath(name, resolved_path);
614 } while ((abs_path == NULL) && (errno == EINTR));
615 ASSERT(abs_path == NULL || IsAbsolutePath(abs_path));
616 ASSERT(abs_path == NULL || (abs_path == resolved_path));
513 return abs_path; 617 return abs_path;
514 } 618 }
515 619
516 const char* File::PathSeparator() { 620 const char* File::PathSeparator() {
517 return "/"; 621 return "/";
518 } 622 }
519 623
520 const char* File::StringEscapedPathSeparator() { 624 const char* File::StringEscapedPathSeparator() {
521 return "/"; 625 return "/";
522 } 626 }
(...skipping 13 matching lines...) Expand all
536 } 640 }
537 if (S_ISSOCK(buf.st_mode)) { 641 if (S_ISSOCK(buf.st_mode)) {
538 return kSocket; 642 return kSocket;
539 } 643 }
540 if (S_ISREG(buf.st_mode)) { 644 if (S_ISREG(buf.st_mode)) {
541 return kFile; 645 return kFile;
542 } 646 }
543 return kOther; 647 return kOther;
544 } 648 }
545 649
546 File::Identical File::AreIdentical(const char* file_1, const char* file_2) { 650 File::Identical File::AreIdentical(Namespace* namespc,
651 const char* file_1,
652 const char* file_2) {
653 NamespaceScope ns1(namespc, file_1);
654 NamespaceScope ns2(namespc, file_2);
547 struct stat file_1_info; 655 struct stat file_1_info;
548 struct stat file_2_info; 656 struct stat file_2_info;
549 if ((NO_RETRY_EXPECTED(lstat(file_1, &file_1_info)) == -1) || 657 int status = TEMP_FAILURE_RETRY(
550 (NO_RETRY_EXPECTED(lstat(file_2, &file_2_info)) == -1)) { 658 fstatat(ns1.fd(), ns1.path(), &file_1_info, AT_SYMLINK_NOFOLLOW));
659 if (status == -1) {
660 return File::kError;
661 }
662 status = TEMP_FAILURE_RETRY(
663 fstatat(ns2.fd(), ns2.path(), &file_2_info, AT_SYMLINK_NOFOLLOW));
664 if (status == -1) {
551 return File::kError; 665 return File::kError;
552 } 666 }
553 return ((file_1_info.st_ino == file_2_info.st_ino) && 667 return ((file_1_info.st_ino == file_2_info.st_ino) &&
554 (file_1_info.st_dev == file_2_info.st_dev)) 668 (file_1_info.st_dev == file_2_info.st_dev))
555 ? File::kIdentical 669 ? File::kIdentical
556 : File::kDifferent; 670 : File::kDifferent;
557 } 671 }
558 672
559 } // namespace bin 673 } // namespace bin
560 } // namespace dart 674 } // namespace dart
561 675
562 #endif // defined(HOST_OS_ANDROID) 676 #endif // defined(HOST_OS_ANDROID)
OLDNEW
« no previous file with comments | « runtime/bin/file.cc ('k') | runtime/bin/file_fuchsia.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698