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

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

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

Powered by Google App Engine
This is Rietveld 408576698