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

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

Issue 3001963002: [dart:io] Namespaces for file IO (Closed)
Patch Set: Fuchsia fix 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(openat(
257 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 static int SymlinkAt(const char *oldpath, int newdirfd, const char *newpath) {
273 int status = NO_RETRY_EXPECTED(symlink(target, name)); 280 return syscall(__NR_symlinkat, oldpath, newdirfd, newpath);
274 return (status == 0);
275 } 281 }
276 282
277 File::Type File::GetType(const char* pathname, bool follow_links) { 283 bool File::CreateLink(Namespace* namespc, const char* name, const char* target) {
284 NamespaceScope ns(namespc, name);
285 return NO_RETRY_EXPECTED(SymlinkAt(target, ns.fd(), ns.path())) == 0;
286 }
287
288 File::Type File::GetType(
289 Namespace* namespc, const char* name, bool follow_links) {
290 NamespaceScope ns(namespc, name);
278 struct stat entry_info; 291 struct stat entry_info;
279 int stat_success; 292 int stat_success;
280 if (follow_links) { 293 if (follow_links) {
281 stat_success = NO_RETRY_EXPECTED(stat(pathname, &entry_info)); 294 stat_success =
295 TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &entry_info, 0));
282 } else { 296 } else {
283 stat_success = NO_RETRY_EXPECTED(lstat(pathname, &entry_info)); 297 stat_success = TEMP_FAILURE_RETRY(fstatat(
298 ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW));
284 } 299 }
285 if (stat_success == -1) { 300 if (stat_success == -1) {
286 return File::kDoesNotExist; 301 return File::kDoesNotExist;
287 } 302 }
288 if (S_ISDIR(entry_info.st_mode)) { 303 if (S_ISDIR(entry_info.st_mode)) {
289 return File::kIsDirectory; 304 return File::kIsDirectory;
290 } 305 }
291 if (S_ISREG(entry_info.st_mode)) { 306 if (S_ISREG(entry_info.st_mode)) {
292 return File::kIsFile; 307 return File::kIsFile;
293 } 308 }
294 if (S_ISLNK(entry_info.st_mode)) { 309 if (S_ISLNK(entry_info.st_mode)) {
295 return File::kIsLink; 310 return File::kIsLink;
296 } 311 }
297 return File::kDoesNotExist; 312 return File::kDoesNotExist;
298 } 313 }
299 314
300 static bool CheckTypeAndSetErrno(const char* name, 315 static bool CheckTypeAndSetErrno(Namespace* namespc,
316 const char* name,
301 File::Type expected, 317 File::Type expected,
302 bool follow_links) { 318 bool follow_links) {
303 File::Type actual = File::GetType(name, follow_links); 319 File::Type actual = File::GetType(namespc, name, follow_links);
304 if (actual == expected) { 320 if (actual == expected) {
305 return true; 321 return true;
306 } 322 }
307 switch (actual) { 323 switch (actual) {
308 case File::kIsDirectory: 324 case File::kIsDirectory:
309 errno = EISDIR; 325 errno = EISDIR;
310 break; 326 break;
311 case File::kDoesNotExist: 327 case File::kDoesNotExist:
312 errno = ENOENT; 328 errno = ENOENT;
313 break; 329 break;
314 default: 330 default:
315 errno = EINVAL; 331 errno = EINVAL;
316 break; 332 break;
317 } 333 }
318 return false; 334 return false;
319 } 335 }
320 336
321 bool File::Delete(const char* name) { 337 bool File::Delete(Namespace* namespc, const char* name) {
322 return CheckTypeAndSetErrno(name, kIsFile, true) && 338 NamespaceScope ns(namespc, name);
323 (NO_RETRY_EXPECTED(unlink(name)) == 0); 339 return CheckTypeAndSetErrno(namespc, name, kIsFile, true) &&
340 (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
324 } 341 }
325 342
326 bool File::DeleteLink(const char* name) { 343 bool File::DeleteLink(Namespace* namespc, const char* name) {
327 return CheckTypeAndSetErrno(name, kIsLink, false) && 344 NamespaceScope ns(namespc, name);
328 (NO_RETRY_EXPECTED(unlink(name)) == 0); 345 return CheckTypeAndSetErrno(namespc, name, kIsLink, false) &&
346 (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
329 } 347 }
330 348
331 bool File::Rename(const char* old_path, const char* new_path) { 349 bool File::Rename(
332 return CheckTypeAndSetErrno(old_path, kIsFile, true) && 350 Namespace* namespc, const char* old_path, const char* new_path) {
333 (NO_RETRY_EXPECTED(rename(old_path, new_path)) == 0); 351 NamespaceScope oldns(namespc, old_path);
352 NamespaceScope newns(namespc, new_path);
353 return CheckTypeAndSetErrno(namespc, old_path, kIsFile, true) &&
354 (NO_RETRY_EXPECTED(renameat(
355 oldns.fd(), oldns.path(), newns.fd(), newns.path())) == 0);
334 } 356 }
335 357
336 bool File::RenameLink(const char* old_path, const char* new_path) { 358 bool File::RenameLink(
337 return CheckTypeAndSetErrno(old_path, kIsLink, false) && 359 Namespace* namespc, const char* old_path, const char* new_path) {
338 (NO_RETRY_EXPECTED(rename(old_path, new_path)) == 0); 360 NamespaceScope oldns(namespc, old_path);
361 NamespaceScope newns(namespc, new_path);
362 return CheckTypeAndSetErrno(namespc, old_path, kIsLink, false) &&
363 (NO_RETRY_EXPECTED(renameat(
364 oldns.fd(), oldns.path(), newns.fd(), newns.path())) == 0);
339 } 365 }
340 366
341 bool File::Copy(const char* old_path, const char* new_path) { 367 bool File::Copy(
342 if (!CheckTypeAndSetErrno(old_path, kIsFile, true)) { 368 Namespace* namespc, const char* old_path, const char* new_path) {
369 if (!CheckTypeAndSetErrno(namespc, old_path, kIsFile, true)) {
343 return false; 370 return false;
344 } 371 }
372 NamespaceScope oldns(namespc, old_path);
345 struct stat st; 373 struct stat st;
346 if (NO_RETRY_EXPECTED(stat(old_path, &st)) != 0) { 374 if (TEMP_FAILURE_RETRY(fstatat(oldns.fd(), oldns.path(), &st, 0)) != 0) {
347 return false; 375 return false;
348 } 376 }
349 int old_fd = TEMP_FAILURE_RETRY(open(old_path, O_RDONLY | O_CLOEXEC)); 377 const int old_fd = TEMP_FAILURE_RETRY(openat(
378 oldns.fd(), oldns.path(), O_RDONLY | O_CLOEXEC));
350 if (old_fd < 0) { 379 if (old_fd < 0) {
351 return false; 380 return false;
352 } 381 }
353 int new_fd = TEMP_FAILURE_RETRY( 382 NamespaceScope newns(namespc, new_path);
354 open(new_path, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, st.st_mode)); 383 const int new_fd = TEMP_FAILURE_RETRY(openat(
384 newns.fd(), newns.path(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, st.st_ mode));
355 if (new_fd < 0) { 385 if (new_fd < 0) {
356 VOID_TEMP_FAILURE_RETRY(close(old_fd)); 386 VOID_TEMP_FAILURE_RETRY(close(old_fd));
357 return false; 387 return false;
358 } 388 }
359 off_t offset = 0; 389 off_t offset = 0;
360 int result = 1; 390 intptr_t result = 1;
361 while (result > 0) { 391 while (result > 0) {
362 // Loop to ensure we copy everything, and not only up to 2GB. 392 // Loop to ensure we copy everything, and not only up to 2GB.
363 result = NO_RETRY_EXPECTED(sendfile(new_fd, old_fd, &offset, kMaxUint32)); 393 result = NO_RETRY_EXPECTED(sendfile(new_fd, old_fd, &offset, kMaxUint32));
364 } 394 }
365 // From sendfile man pages: 395 // From sendfile man pages:
366 // Applications may wish to fall back to read(2)/write(2) in the case 396 // Applications may wish to fall back to read(2)/write(2) in the case
367 // where sendfile() fails with EINVAL or ENOSYS. 397 // where sendfile() fails with EINVAL or ENOSYS.
368 if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) { 398 if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) {
369 const intptr_t kBufferSize = 8 * KB; 399 const intptr_t kBufferSize = 8 * KB;
370 uint8_t buffer[kBufferSize]; 400 uint8_t buffer[kBufferSize];
371 while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) > 401 while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) >
372 0) { 402 0) {
373 int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result)); 403 int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result));
374 if (wrote != result) { 404 if (wrote != result) {
375 result = -1; 405 result = -1;
376 break; 406 break;
377 } 407 }
378 } 408 }
379 } 409 }
380 int e = errno; 410 int e = errno;
381 VOID_TEMP_FAILURE_RETRY(close(old_fd)); 411 VOID_TEMP_FAILURE_RETRY(close(old_fd));
382 VOID_TEMP_FAILURE_RETRY(close(new_fd)); 412 VOID_TEMP_FAILURE_RETRY(close(new_fd));
383 if (result < 0) { 413 if (result < 0) {
384 VOID_NO_RETRY_EXPECTED(unlink(new_path)); 414 VOID_NO_RETRY_EXPECTED(unlinkat(newns.fd(), newns.path(), 0));
385 errno = e; 415 errno = e;
386 return false; 416 return false;
387 } 417 }
388 return true; 418 return true;
389 } 419 }
390 420
391 static bool StatHelper(const char* name, struct stat* st) { 421 static bool StatHelper(Namespace* namespc, const char* name, struct stat* st) {
392 if (NO_RETRY_EXPECTED(stat(name, st)) != 0) { 422 NamespaceScope ns(namespc, name);
423 if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), st, 0)) != 0) {
393 return false; 424 return false;
394 } 425 }
395 // Signal an error if it's a directory. 426 // Signal an error if it's a directory.
396 if (S_ISDIR(st->st_mode)) { 427 if (S_ISDIR(st->st_mode)) {
397 errno = EISDIR; 428 errno = EISDIR;
398 return false; 429 return false;
399 } 430 }
400 // Otherwise assume the caller knows what it's doing. 431 // Otherwise assume the caller knows what it's doing.
401 return true; 432 return true;
402 } 433 }
403 434
404 int64_t File::LengthFromPath(const char* name) { 435 int64_t File::LengthFromPath(Namespace* namespc, const char* name) {
405 struct stat st; 436 struct stat st;
406 if (!StatHelper(name, &st)) { 437 if (!StatHelper(namespc, name, &st)) {
407 return -1; 438 return -1;
408 } 439 }
409 return st.st_size; 440 return st.st_size;
410 } 441 }
411 442
412 void File::Stat(const char* name, int64_t* data) { 443 static void MillisecondsToTimespec(int64_t millis, struct timespec* t) {
444 ASSERT(t != NULL);
445 t->tv_sec = millis / kMillisecondsPerSecond;
446 t->tv_nsec = (millis - (t->tv_nsec * kMillisecondsPerSecond)) * 1000L;
447 }
448
449 void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
450 NamespaceScope ns(namespc, name);
413 struct stat st; 451 struct stat st;
414 if (NO_RETRY_EXPECTED(stat(name, &st)) == 0) { 452 if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &st, 0)) == 0) {
415 if (S_ISREG(st.st_mode)) { 453 if (S_ISREG(st.st_mode)) {
416 data[kType] = kIsFile; 454 data[kType] = kIsFile;
417 } else if (S_ISDIR(st.st_mode)) { 455 } else if (S_ISDIR(st.st_mode)) {
418 data[kType] = kIsDirectory; 456 data[kType] = kIsDirectory;
419 } else if (S_ISLNK(st.st_mode)) { 457 } else if (S_ISLNK(st.st_mode)) {
420 data[kType] = kIsLink; 458 data[kType] = kIsLink;
421 } else { 459 } else {
422 data[kType] = kDoesNotExist; 460 data[kType] = kDoesNotExist;
423 } 461 }
424 data[kCreatedTime] = static_cast<int64_t>(st.st_ctime) * 1000; 462 data[kCreatedTime] = static_cast<int64_t>(st.st_ctime) * 1000;
425 data[kModifiedTime] = static_cast<int64_t>(st.st_mtime) * 1000; 463 data[kModifiedTime] = static_cast<int64_t>(st.st_mtime) * 1000;
426 data[kAccessedTime] = static_cast<int64_t>(st.st_atime) * 1000; 464 data[kAccessedTime] = static_cast<int64_t>(st.st_atime) * 1000;
427 data[kMode] = st.st_mode; 465 data[kMode] = st.st_mode;
428 data[kSize] = st.st_size; 466 data[kSize] = st.st_size;
429 } else { 467 } else {
430 data[kType] = kDoesNotExist; 468 data[kType] = kDoesNotExist;
431 } 469 }
432 } 470 }
433 471
434 time_t File::LastModified(const char* name) { 472 time_t File::LastModified(Namespace* namespc, const char* name) {
435 struct stat st; 473 struct stat st;
436 if (!StatHelper(name, &st)) { 474 if (!StatHelper(namespc, name, &st)) {
437 return -1; 475 return -1;
438 } 476 }
439 return st.st_mtime; 477 return st.st_mtime;
440 } 478 }
441 479
442 time_t File::LastAccessed(const char* name) { 480 time_t File::LastAccessed(Namespace* namespc, const char* name) {
443 struct stat st; 481 struct stat st;
444 if (!StatHelper(name, &st)) { 482 if (!StatHelper(namespc, name, &st)) {
445 return -1; 483 return -1;
446 } 484 }
447 return st.st_atime; 485 return st.st_atime;
448 } 486 }
449 487
450 bool File::SetLastAccessed(const char* name, int64_t millis) { 488 bool File::SetLastAccessed(
489 Namespace* namespc, const char* name, int64_t millis) {
451 // First get the current times. 490 // First get the current times.
452 struct stat st; 491 struct stat64 st;
453 if (!StatHelper(name, &st)) { 492 if (!StatHelper(namespc, name, &st)) {
454 return false; 493 return false;
455 } 494 }
456 495
457 // Set the new time: 496 // Set the new time:
458 struct utimbuf times; 497 NamespaceScope ns(namespc, name);
459 times.actime = millis / kMillisecondsPerSecond; 498 struct timespec times[2];
460 times.modtime = st.st_mtime; 499 MillisecondsToTimespec(millis, &times[0]);
461 return utime(name, &times) == 0; 500 MillisecondsToTimespec(static_cast<int64_t>(st.st_mtime) * 1000, &times[1]);
501 return utimensat(ns.fd(), ns.path(), times, 0) == 0;
462 } 502 }
463 503
464 bool File::SetLastModified(const char* name, int64_t millis) { 504 bool File::SetLastModified(
505 Namespace* namespc, const char* name, int64_t millis) {
465 // First get the current times. 506 // First get the current times.
466 struct stat st; 507 struct stat64 st;
467 if (!StatHelper(name, &st)) { 508 if (!StatHelper(namespc, name, &st)) {
468 return false; 509 return false;
469 } 510 }
470 511
471 // Set the new time: 512 // Set the new time:
472 struct utimbuf times; 513 NamespaceScope ns(namespc, name);
473 times.actime = st.st_atime; 514 struct timespec times[2];
474 times.modtime = millis / kMillisecondsPerSecond; 515 MillisecondsToTimespec(static_cast<int64_t>(st.st_atime) * 1000, &times[0]);
475 return utime(name, &times) == 0; 516 MillisecondsToTimespec(millis, &times[1]);
517 return utimensat(ns.fd(), ns.path(), times, 0) == 0;
476 } 518 }
477 519
478 const char* File::LinkTarget(const char* pathname) { 520 static int ReadLinkAt(int dirfd, const char* pathname, char* buf, size_t bufsize ) {
521 return syscall(__NR_readlinkat, dirfd, pathname, buf, bufsize);
522 }
523
524 const char* File::LinkTarget(Namespace* namespc, const char* name) {
525 NamespaceScope ns(namespc, name);
479 struct stat link_stats; 526 struct stat link_stats;
480 if (lstat(pathname, &link_stats) != 0) { 527 const int status = TEMP_FAILURE_RETRY(fstatat(
528 ns.fd(), ns.path(), &link_stats, AT_SYMLINK_NOFOLLOW));
529 if (status != 0) {
481 return NULL; 530 return NULL;
482 } 531 }
483 if (!S_ISLNK(link_stats.st_mode)) { 532 if (!S_ISLNK(link_stats.st_mode)) {
484 errno = ENOENT; 533 errno = ENOENT;
485 return NULL; 534 return NULL;
486 } 535 }
487 size_t target_size = link_stats.st_size; 536 // Don't rely on the link_stats.st_size for the size of the link
537 // target. For some filesystems, e.g. procfs, this value is always
538 // 0. Also the link might have changed before the readlink call.
539 const int kBufferSize = PATH_MAX + 1;
540 char target[kBufferSize];
541 const int target_size =
542 TEMP_FAILURE_RETRY(ReadLinkAt(ns.fd(), ns.path(), target, kBufferSize));
543 if (target_size <= 0) {
544 return NULL;
545 }
488 char* target_name = DartUtils::ScopedCString(target_size + 1); 546 char* target_name = DartUtils::ScopedCString(target_size + 1);
489 ASSERT(target_name != NULL); 547 ASSERT(target_name != NULL);
490 size_t read_size = readlink(pathname, target_name, target_size + 1); 548 memmove(target_name, target, target_size);
491 if (read_size != target_size) {
492 return NULL;
493 }
494 target_name[target_size] = '\0'; 549 target_name[target_size] = '\0';
495 return target_name; 550 return target_name;
496 } 551 }
497 552
498 bool File::IsAbsolutePath(const char* pathname) { 553 bool File::IsAbsolutePath(const char* pathname) {
499 return ((pathname != NULL) && (pathname[0] == '/')); 554 return ((pathname != NULL) && (pathname[0] == '/'));
500 } 555 }
501 556
502 const char* File::GetCanonicalPath(const char* pathname) { 557 const char* File::ReadLink(const char* pathname) {
503 char* abs_path = NULL; 558 ASSERT(pathname != NULL);
504 if (pathname != NULL) { 559 ASSERT(IsAbsolutePath(pathname));
505 char* resolved_path = DartUtils::ScopedCString(PATH_MAX + 1); 560 struct stat link_stats;
506 ASSERT(resolved_path != NULL); 561 if (TEMP_FAILURE_RETRY(lstat(pathname, &link_stats)) != 0) {
507 do { 562 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 } 563 }
564 if (!S_ISLNK(link_stats.st_mode)) {
565 errno = ENOENT;
566 return NULL;
567 }
568 // Don't rely on the link_stats.st_size for the size of the link
569 // target. For some filesystems, e.g. procfs, this value is always
570 // 0. Also the link might have changed before the readlink call.
571 const int kBufferSize = PATH_MAX + 1;
572 char target[kBufferSize];
573 size_t target_size =
574 TEMP_FAILURE_RETRY(readlink(pathname, target, kBufferSize));
575 if (target_size <= 0) {
576 return NULL;
577 }
578 char* target_name = DartUtils::ScopedCString(target_size + 1);
579 ASSERT(target_name != NULL);
580 memmove(target_name, target, target_size);
581 target_name[target_size] = '\0';
582 return target_name;
583 }
584
585 const char* File::GetCanonicalPath(Namespace* namespc, const char* name) {
586 if (name == NULL) {
587 return NULL;
588 }
589 if (!Namespace::IsDefault(namespc)) {
590 // TODO(zra): There is no realpathat(). Also chasing a symlink might result
591 // in a path to something outside of the namespace, so canonicalizing paths
592 // would have to be done carefully. For now, don't do anything.
593 return name;
594 }
595 char* abs_path;
596 char* resolved_path = DartUtils::ScopedCString(PATH_MAX + 1);
597 ASSERT(resolved_path != NULL);
598 do {
599 abs_path = realpath(name, resolved_path);
600 } while ((abs_path == NULL) && (errno == EINTR));
601 ASSERT(abs_path == NULL || IsAbsolutePath(abs_path));
602 ASSERT(abs_path == NULL || (abs_path == resolved_path));
513 return abs_path; 603 return abs_path;
514 } 604 }
515 605
516 const char* File::PathSeparator() { 606 const char* File::PathSeparator() {
517 return "/"; 607 return "/";
518 } 608 }
519 609
520 const char* File::StringEscapedPathSeparator() { 610 const char* File::StringEscapedPathSeparator() {
521 return "/"; 611 return "/";
522 } 612 }
(...skipping 13 matching lines...) Expand all
536 } 626 }
537 if (S_ISSOCK(buf.st_mode)) { 627 if (S_ISSOCK(buf.st_mode)) {
538 return kSocket; 628 return kSocket;
539 } 629 }
540 if (S_ISREG(buf.st_mode)) { 630 if (S_ISREG(buf.st_mode)) {
541 return kFile; 631 return kFile;
542 } 632 }
543 return kOther; 633 return kOther;
544 } 634 }
545 635
546 File::Identical File::AreIdentical(const char* file_1, const char* file_2) { 636 File::Identical File::AreIdentical(
637 Namespace* namespc, const char* file_1, const char* file_2) {
638 NamespaceScope ns1(namespc, file_1);
639 NamespaceScope ns2(namespc, file_2);
547 struct stat file_1_info; 640 struct stat file_1_info;
548 struct stat file_2_info; 641 struct stat file_2_info;
549 if ((NO_RETRY_EXPECTED(lstat(file_1, &file_1_info)) == -1) || 642 int status = TEMP_FAILURE_RETRY(fstatat(
550 (NO_RETRY_EXPECTED(lstat(file_2, &file_2_info)) == -1)) { 643 ns1.fd(), ns1.path(), &file_1_info, AT_SYMLINK_NOFOLLOW));
644 if (status == -1) {
645 return File::kError;
646 }
647 status = TEMP_FAILURE_RETRY(fstatat(
648 ns2.fd(), ns2.path(), &file_2_info, AT_SYMLINK_NOFOLLOW));
649 if (status == -1) {
551 return File::kError; 650 return File::kError;
552 } 651 }
553 return ((file_1_info.st_ino == file_2_info.st_ino) && 652 return ((file_1_info.st_ino == file_2_info.st_ino) &&
554 (file_1_info.st_dev == file_2_info.st_dev)) 653 (file_1_info.st_dev == file_2_info.st_dev))
555 ? File::kIdentical 654 ? File::kIdentical
556 : File::kDifferent; 655 : File::kDifferent;
557 } 656 }
558 657
559 } // namespace bin 658 } // namespace bin
560 } // namespace dart 659 } // namespace dart
561 660
562 #endif // defined(HOST_OS_ANDROID) 661 #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