Roll src/third_party/WebKit b150dde:b4d2fd5 (svn 196008:196073)
[chromium-blink-merge.git] / base / files / file_posix.cc
blobbb49d2dd730eb4ee587e968a601942ab8afc6045
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/files/file.h"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
12 #include "base/logging.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread_restrictions.h"
18 #if defined(OS_ANDROID)
19 #include "base/os_compat_android.h"
20 #endif
22 namespace base {
24 // Make sure our Whence mappings match the system headers.
25 COMPILE_ASSERT(File::FROM_BEGIN == SEEK_SET &&
26 File::FROM_CURRENT == SEEK_CUR &&
27 File::FROM_END == SEEK_END, whence_matches_system);
29 namespace {
31 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
32 static int CallFstat(int fd, stat_wrapper_t *sb) {
33 ThreadRestrictions::AssertIOAllowed();
34 return fstat(fd, sb);
36 #else
37 static int CallFstat(int fd, stat_wrapper_t *sb) {
38 ThreadRestrictions::AssertIOAllowed();
39 return fstat64(fd, sb);
41 #endif
43 // NaCl doesn't provide the following system calls, so either simulate them or
44 // wrap them in order to minimize the number of #ifdef's in this file.
45 #if !defined(OS_NACL)
46 static bool IsOpenAppend(PlatformFile file) {
47 return (fcntl(file, F_GETFL) & O_APPEND) != 0;
50 static int CallFtruncate(PlatformFile file, int64 length) {
51 return HANDLE_EINTR(ftruncate(file, length));
54 static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
55 #ifdef __USE_XOPEN2K8
56 // futimens should be available, but futimes might not be
57 // http://pubs.opengroup.org/onlinepubs/9699919799/
59 timespec ts_times[2];
60 ts_times[0].tv_sec = times[0].tv_sec;
61 ts_times[0].tv_nsec = times[0].tv_usec * 1000;
62 ts_times[1].tv_sec = times[1].tv_sec;
63 ts_times[1].tv_nsec = times[1].tv_usec * 1000;
65 return futimens(file, ts_times);
66 #else
67 return futimes(file, times);
68 #endif
71 static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) {
72 struct flock lock;
73 lock.l_type = F_WRLCK;
74 lock.l_whence = SEEK_SET;
75 lock.l_start = 0;
76 lock.l_len = 0; // Lock entire file.
77 if (HANDLE_EINTR(fcntl(file, do_lock ? F_SETLK : F_UNLCK, &lock)) == -1)
78 return File::OSErrorToFileError(errno);
79 return File::FILE_OK;
81 #else // defined(OS_NACL)
83 static bool IsOpenAppend(PlatformFile file) {
84 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
85 // standard and always appends if the file is opened with O_APPEND, just
86 // return false here.
87 return false;
90 static int CallFtruncate(PlatformFile file, int64 length) {
91 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
92 return 0;
95 static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
96 NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
97 return 0;
100 static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) {
101 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
102 return File::FILE_ERROR_INVALID_OPERATION;
104 #endif // defined(OS_NACL)
106 } // namespace
108 void File::Info::FromStat(const stat_wrapper_t& stat_info) {
109 is_directory = S_ISDIR(stat_info.st_mode);
110 is_symbolic_link = S_ISLNK(stat_info.st_mode);
111 size = stat_info.st_size;
113 #if defined(OS_LINUX)
114 time_t last_modified_sec = stat_info.st_mtim.tv_sec;
115 int64 last_modified_nsec = stat_info.st_mtim.tv_nsec;
116 time_t last_accessed_sec = stat_info.st_atim.tv_sec;
117 int64 last_accessed_nsec = stat_info.st_atim.tv_nsec;
118 time_t creation_time_sec = stat_info.st_ctim.tv_sec;
119 int64 creation_time_nsec = stat_info.st_ctim.tv_nsec;
120 #elif defined(OS_ANDROID)
121 time_t last_modified_sec = stat_info.st_mtime;
122 int64 last_modified_nsec = stat_info.st_mtime_nsec;
123 time_t last_accessed_sec = stat_info.st_atime;
124 int64 last_accessed_nsec = stat_info.st_atime_nsec;
125 time_t creation_time_sec = stat_info.st_ctime;
126 int64 creation_time_nsec = stat_info.st_ctime_nsec;
127 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
128 time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
129 int64 last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
130 time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
131 int64 last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
132 time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
133 int64 creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
134 #else
135 time_t last_modified_sec = stat_info.st_mtime;
136 int64 last_modified_nsec = 0;
137 time_t last_accessed_sec = stat_info.st_atime;
138 int64 last_accessed_nsec = 0;
139 time_t creation_time_sec = stat_info.st_ctime;
140 int64 creation_time_nsec = 0;
141 #endif
143 last_modified =
144 Time::FromTimeT(last_modified_sec) +
145 TimeDelta::FromMicroseconds(last_modified_nsec /
146 Time::kNanosecondsPerMicrosecond);
148 last_accessed =
149 Time::FromTimeT(last_accessed_sec) +
150 TimeDelta::FromMicroseconds(last_accessed_nsec /
151 Time::kNanosecondsPerMicrosecond);
153 creation_time =
154 Time::FromTimeT(creation_time_sec) +
155 TimeDelta::FromMicroseconds(creation_time_nsec /
156 Time::kNanosecondsPerMicrosecond);
159 bool File::IsValid() const {
160 return file_.is_valid();
163 PlatformFile File::GetPlatformFile() const {
164 return file_.get();
167 PlatformFile File::TakePlatformFile() {
168 return file_.release();
171 void File::Close() {
172 if (!IsValid())
173 return;
175 SCOPED_FILE_TRACE("Close");
176 ThreadRestrictions::AssertIOAllowed();
177 file_.reset();
180 int64 File::Seek(Whence whence, int64 offset) {
181 ThreadRestrictions::AssertIOAllowed();
182 DCHECK(IsValid());
184 SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset);
186 #if defined(OS_ANDROID)
187 COMPILE_ASSERT(sizeof(int64) == sizeof(off64_t), off64_t_64_bit);
188 return lseek64(file_.get(), static_cast<off64_t>(offset),
189 static_cast<int>(whence));
190 #else
191 COMPILE_ASSERT(sizeof(int64) == sizeof(off_t), off_t_64_bit);
192 return lseek(file_.get(), static_cast<off_t>(offset),
193 static_cast<int>(whence));
194 #endif
197 int File::Read(int64 offset, char* data, int size) {
198 ThreadRestrictions::AssertIOAllowed();
199 DCHECK(IsValid());
200 if (size < 0)
201 return -1;
203 SCOPED_FILE_TRACE_WITH_SIZE("Read", size);
205 int bytes_read = 0;
206 int rv;
207 do {
208 rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read,
209 size - bytes_read, offset + bytes_read));
210 if (rv <= 0)
211 break;
213 bytes_read += rv;
214 } while (bytes_read < size);
216 return bytes_read ? bytes_read : rv;
219 int File::ReadAtCurrentPos(char* data, int size) {
220 ThreadRestrictions::AssertIOAllowed();
221 DCHECK(IsValid());
222 if (size < 0)
223 return -1;
225 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size);
227 int bytes_read = 0;
228 int rv;
229 do {
230 rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
231 if (rv <= 0)
232 break;
234 bytes_read += rv;
235 } while (bytes_read < size);
237 return bytes_read ? bytes_read : rv;
240 int File::ReadNoBestEffort(int64 offset, char* data, int size) {
241 ThreadRestrictions::AssertIOAllowed();
242 DCHECK(IsValid());
243 SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size);
244 return HANDLE_EINTR(pread(file_.get(), data, size, offset));
247 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
248 ThreadRestrictions::AssertIOAllowed();
249 DCHECK(IsValid());
250 if (size < 0)
251 return -1;
253 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size);
254 return HANDLE_EINTR(read(file_.get(), data, size));
257 int File::Write(int64 offset, const char* data, int size) {
258 ThreadRestrictions::AssertIOAllowed();
260 if (IsOpenAppend(file_.get()))
261 return WriteAtCurrentPos(data, size);
263 DCHECK(IsValid());
264 if (size < 0)
265 return -1;
267 SCOPED_FILE_TRACE_WITH_SIZE("Write", size);
269 int bytes_written = 0;
270 int rv;
271 do {
272 rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
273 size - bytes_written, offset + bytes_written));
274 if (rv <= 0)
275 break;
277 bytes_written += rv;
278 } while (bytes_written < size);
280 return bytes_written ? bytes_written : rv;
283 int File::WriteAtCurrentPos(const char* data, int size) {
284 ThreadRestrictions::AssertIOAllowed();
285 DCHECK(IsValid());
286 if (size < 0)
287 return -1;
289 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size);
291 int bytes_written = 0;
292 int rv;
293 do {
294 rv = HANDLE_EINTR(write(file_.get(), data + bytes_written,
295 size - bytes_written));
296 if (rv <= 0)
297 break;
299 bytes_written += rv;
300 } while (bytes_written < size);
302 return bytes_written ? bytes_written : rv;
305 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
306 ThreadRestrictions::AssertIOAllowed();
307 DCHECK(IsValid());
308 if (size < 0)
309 return -1;
311 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size);
312 return HANDLE_EINTR(write(file_.get(), data, size));
315 int64 File::GetLength() {
316 DCHECK(IsValid());
318 SCOPED_FILE_TRACE("GetLength");
320 stat_wrapper_t file_info;
321 if (CallFstat(file_.get(), &file_info))
322 return false;
324 return file_info.st_size;
327 bool File::SetLength(int64 length) {
328 ThreadRestrictions::AssertIOAllowed();
329 DCHECK(IsValid());
331 SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length);
332 return !CallFtruncate(file_.get(), length);
335 bool File::SetTimes(Time last_access_time, Time last_modified_time) {
336 ThreadRestrictions::AssertIOAllowed();
337 DCHECK(IsValid());
339 SCOPED_FILE_TRACE("SetTimes");
341 timeval times[2];
342 times[0] = last_access_time.ToTimeVal();
343 times[1] = last_modified_time.ToTimeVal();
345 return !CallFutimes(file_.get(), times);
348 bool File::GetInfo(Info* info) {
349 DCHECK(IsValid());
351 SCOPED_FILE_TRACE("GetInfo");
353 stat_wrapper_t file_info;
354 if (CallFstat(file_.get(), &file_info))
355 return false;
357 info->FromStat(file_info);
358 return true;
361 File::Error File::Lock() {
362 SCOPED_FILE_TRACE("Lock");
363 return CallFctnlFlock(file_.get(), true);
366 File::Error File::Unlock() {
367 SCOPED_FILE_TRACE("Unlock");
368 return CallFctnlFlock(file_.get(), false);
371 File File::Duplicate() {
372 if (!IsValid())
373 return File();
375 SCOPED_FILE_TRACE("Duplicate");
377 PlatformFile other_fd = dup(GetPlatformFile());
378 if (other_fd == -1)
379 return File(OSErrorToFileError(errno));
381 File other(other_fd);
382 if (async())
383 other.async_ = true;
384 return other.Pass();
387 // Static.
388 File::Error File::OSErrorToFileError(int saved_errno) {
389 switch (saved_errno) {
390 case EACCES:
391 case EISDIR:
392 case EROFS:
393 case EPERM:
394 return FILE_ERROR_ACCESS_DENIED;
395 case EBUSY:
396 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
397 case ETXTBSY:
398 #endif
399 return FILE_ERROR_IN_USE;
400 case EEXIST:
401 return FILE_ERROR_EXISTS;
402 case EIO:
403 return FILE_ERROR_IO;
404 case ENOENT:
405 return FILE_ERROR_NOT_FOUND;
406 case EMFILE:
407 return FILE_ERROR_TOO_MANY_OPENED;
408 case ENOMEM:
409 return FILE_ERROR_NO_MEMORY;
410 case ENOSPC:
411 return FILE_ERROR_NO_SPACE;
412 case ENOTDIR:
413 return FILE_ERROR_NOT_A_DIRECTORY;
414 default:
415 #if !defined(OS_NACL) // NaCl build has no metrics code.
416 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
417 saved_errno);
418 #endif
419 return FILE_ERROR_FAILED;
423 File::MemoryCheckingScopedFD::MemoryCheckingScopedFD() {
424 UpdateChecksum();
427 File::MemoryCheckingScopedFD::MemoryCheckingScopedFD(int fd) : file_(fd) {
428 UpdateChecksum();
431 File::MemoryCheckingScopedFD::~MemoryCheckingScopedFD() {}
433 // static
434 void File::MemoryCheckingScopedFD::ComputeMemoryChecksum(
435 unsigned int* out_checksum) const {
436 // Use a single iteration of a linear congruentional generator (lcg) to
437 // provide a cheap checksum unlikely to be accidentally matched by a random
438 // memory corruption.
440 // By choosing constants that satisfy the Hull-Duebell Theorem on lcg cycle
441 // length, we insure that each distinct fd value maps to a distinct checksum,
442 // which maximises the utility of our checksum.
444 // This code uses "unsigned int" throughout for its defined modular semantics,
445 // which implicitly gives us a divisor that is a power of two.
447 const unsigned int kMultiplier = 13035 * 4 + 1;
448 COMPILE_ASSERT(((kMultiplier - 1) & 3) == 0, pred_must_be_multiple_of_four);
449 const unsigned int kIncrement = 1595649551;
450 COMPILE_ASSERT(kIncrement & 1, must_be_coprime_to_powers_of_two);
452 *out_checksum =
453 static_cast<unsigned int>(file_.get()) * kMultiplier + kIncrement;
456 void File::MemoryCheckingScopedFD::Check() const {
457 unsigned int computed_checksum;
458 ComputeMemoryChecksum(&computed_checksum);
459 CHECK_EQ(file_memory_checksum_, computed_checksum) << "corrupted fd memory";
462 void File::MemoryCheckingScopedFD::UpdateChecksum() {
463 ComputeMemoryChecksum(&file_memory_checksum_);
466 // NaCl doesn't implement system calls to open files directly.
467 #if !defined(OS_NACL)
468 // TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
469 void File::DoInitialize(uint32 flags) {
470 ThreadRestrictions::AssertIOAllowed();
471 DCHECK(!IsValid());
473 int open_flags = 0;
474 if (flags & FLAG_CREATE)
475 open_flags = O_CREAT | O_EXCL;
477 created_ = false;
479 if (flags & FLAG_CREATE_ALWAYS) {
480 DCHECK(!open_flags);
481 DCHECK(flags & FLAG_WRITE);
482 open_flags = O_CREAT | O_TRUNC;
485 if (flags & FLAG_OPEN_TRUNCATED) {
486 DCHECK(!open_flags);
487 DCHECK(flags & FLAG_WRITE);
488 open_flags = O_TRUNC;
491 if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
492 NOTREACHED();
493 errno = EOPNOTSUPP;
494 error_details_ = FILE_ERROR_FAILED;
495 return;
498 if (flags & FLAG_WRITE && flags & FLAG_READ) {
499 open_flags |= O_RDWR;
500 } else if (flags & FLAG_WRITE) {
501 open_flags |= O_WRONLY;
502 } else if (!(flags & FLAG_READ) &&
503 !(flags & FLAG_WRITE_ATTRIBUTES) &&
504 !(flags & FLAG_APPEND) &&
505 !(flags & FLAG_OPEN_ALWAYS)) {
506 NOTREACHED();
509 if (flags & FLAG_TERMINAL_DEVICE)
510 open_flags |= O_NOCTTY | O_NDELAY;
512 if (flags & FLAG_APPEND && flags & FLAG_READ)
513 open_flags |= O_APPEND | O_RDWR;
514 else if (flags & FLAG_APPEND)
515 open_flags |= O_APPEND | O_WRONLY;
517 COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero);
519 int mode = S_IRUSR | S_IWUSR;
520 #if defined(OS_CHROMEOS)
521 mode |= S_IRGRP | S_IROTH;
522 #endif
524 int descriptor = HANDLE_EINTR(open(path_.value().c_str(), open_flags, mode));
526 if (flags & FLAG_OPEN_ALWAYS) {
527 if (descriptor < 0) {
528 open_flags |= O_CREAT;
529 if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE)
530 open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW
532 descriptor = HANDLE_EINTR(open(path_.value().c_str(), open_flags, mode));
533 if (descriptor >= 0)
534 created_ = true;
538 if (descriptor < 0) {
539 error_details_ = File::OSErrorToFileError(errno);
540 return;
543 if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
544 created_ = true;
546 if (flags & FLAG_DELETE_ON_CLOSE)
547 unlink(path_.value().c_str());
549 async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
550 error_details_ = FILE_OK;
551 file_.reset(descriptor);
553 #endif // !defined(OS_NACL)
555 bool File::DoFlush() {
556 ThreadRestrictions::AssertIOAllowed();
557 DCHECK(IsValid());
559 #if defined(OS_NACL)
560 NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
561 return true;
562 #elif defined(OS_LINUX) || defined(OS_ANDROID)
563 return !HANDLE_EINTR(fdatasync(file_.get()));
564 #else
565 return !HANDLE_EINTR(fsync(file_.get()));
566 #endif
569 void File::SetPlatformFile(PlatformFile file) {
570 DCHECK(!file_.is_valid());
571 file_.reset(file);
574 } // namespace base