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

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

Powered by Google App Engine
This is Rietveld 408576698