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

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