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"
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"
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
);
31 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
32 static int CallFstat(int fd
, stat_wrapper_t
*sb
) {
33 ThreadRestrictions::AssertIOAllowed();
37 static int CallFstat(int fd
, stat_wrapper_t
*sb
) {
38 ThreadRestrictions::AssertIOAllowed();
39 return fstat64(fd
, sb
);
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.
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]) {
56 // futimens should be available, but futimes might not be
57 // http://pubs.opengroup.org/onlinepubs/9699919799/
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
);
67 return futimes(file
, times
);
71 static File::Error
CallFctnlFlock(PlatformFile file
, bool do_lock
) {
73 lock
.l_type
= F_WRLCK
;
74 lock
.l_whence
= SEEK_SET
;
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
);
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
90 static int CallFtruncate(PlatformFile file
, int64 length
) {
91 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
95 static int CallFutimes(PlatformFile file
, const struct timeval times
[2]) {
96 NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
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)
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
;
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;
144 Time::FromTimeT(last_modified_sec
) +
145 TimeDelta::FromMicroseconds(last_modified_nsec
/
146 Time::kNanosecondsPerMicrosecond
);
149 Time::FromTimeT(last_accessed_sec
) +
150 TimeDelta::FromMicroseconds(last_accessed_nsec
/
151 Time::kNanosecondsPerMicrosecond
);
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 {
167 PlatformFile
File::TakePlatformFile() {
168 return file_
.release();
175 SCOPED_FILE_TRACE("Close");
176 ThreadRestrictions::AssertIOAllowed();
180 int64
File::Seek(Whence whence
, int64 offset
) {
181 ThreadRestrictions::AssertIOAllowed();
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
));
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
));
197 int File::Read(int64 offset
, char* data
, int size
) {
198 ThreadRestrictions::AssertIOAllowed();
203 SCOPED_FILE_TRACE_WITH_SIZE("Read", size
);
208 rv
= HANDLE_EINTR(pread(file_
.get(), data
+ bytes_read
,
209 size
- bytes_read
, offset
+ bytes_read
));
214 } while (bytes_read
< size
);
216 return bytes_read
? bytes_read
: rv
;
219 int File::ReadAtCurrentPos(char* data
, int size
) {
220 ThreadRestrictions::AssertIOAllowed();
225 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size
);
230 rv
= HANDLE_EINTR(read(file_
.get(), data
+ bytes_read
, size
- bytes_read
));
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();
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();
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
);
267 SCOPED_FILE_TRACE_WITH_SIZE("Write", size
);
269 int bytes_written
= 0;
272 rv
= HANDLE_EINTR(pwrite(file_
.get(), data
+ bytes_written
,
273 size
- bytes_written
, offset
+ bytes_written
));
278 } while (bytes_written
< size
);
280 return bytes_written
? bytes_written
: rv
;
283 int File::WriteAtCurrentPos(const char* data
, int size
) {
284 ThreadRestrictions::AssertIOAllowed();
289 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size
);
291 int bytes_written
= 0;
294 rv
= HANDLE_EINTR(write(file_
.get(), data
+ bytes_written
,
295 size
- bytes_written
));
300 } while (bytes_written
< size
);
302 return bytes_written
? bytes_written
: rv
;
305 int File::WriteAtCurrentPosNoBestEffort(const char* data
, int size
) {
306 ThreadRestrictions::AssertIOAllowed();
311 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size
);
312 return HANDLE_EINTR(write(file_
.get(), data
, size
));
315 int64
File::GetLength() {
318 SCOPED_FILE_TRACE("GetLength");
320 stat_wrapper_t file_info
;
321 if (CallFstat(file_
.get(), &file_info
))
324 return file_info
.st_size
;
327 bool File::SetLength(int64 length
) {
328 ThreadRestrictions::AssertIOAllowed();
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();
339 SCOPED_FILE_TRACE("SetTimes");
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
) {
351 SCOPED_FILE_TRACE("GetInfo");
353 stat_wrapper_t file_info
;
354 if (CallFstat(file_
.get(), &file_info
))
357 info
->FromStat(file_info
);
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() {
375 SCOPED_FILE_TRACE("Duplicate");
377 PlatformFile other_fd
= dup(GetPlatformFile());
379 return File(OSErrorToFileError(errno
));
381 File
other(other_fd
);
388 File::Error
File::OSErrorToFileError(int saved_errno
) {
389 switch (saved_errno
) {
394 return FILE_ERROR_ACCESS_DENIED
;
396 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
399 return FILE_ERROR_IN_USE
;
401 return FILE_ERROR_EXISTS
;
403 return FILE_ERROR_IO
;
405 return FILE_ERROR_NOT_FOUND
;
407 return FILE_ERROR_TOO_MANY_OPENED
;
409 return FILE_ERROR_NO_MEMORY
;
411 return FILE_ERROR_NO_SPACE
;
413 return FILE_ERROR_NOT_A_DIRECTORY
;
415 #if !defined(OS_NACL) // NaCl build has no metrics code.
416 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
419 return FILE_ERROR_FAILED
;
423 File::MemoryCheckingScopedFD::MemoryCheckingScopedFD() {
427 File::MemoryCheckingScopedFD::MemoryCheckingScopedFD(int fd
) : file_(fd
) {
431 File::MemoryCheckingScopedFD::~MemoryCheckingScopedFD() {}
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
);
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(const FilePath
& path
, uint32 flags
) {
470 ThreadRestrictions::AssertIOAllowed();
474 if (flags
& FLAG_CREATE
)
475 open_flags
= O_CREAT
| O_EXCL
;
479 if (flags
& FLAG_CREATE_ALWAYS
) {
481 DCHECK(flags
& FLAG_WRITE
);
482 open_flags
= O_CREAT
| O_TRUNC
;
485 if (flags
& FLAG_OPEN_TRUNCATED
) {
487 DCHECK(flags
& FLAG_WRITE
);
488 open_flags
= O_TRUNC
;
491 if (!open_flags
&& !(flags
& FLAG_OPEN
) && !(flags
& FLAG_OPEN_ALWAYS
)) {
494 error_details_
= FILE_ERROR_FAILED
;
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
)) {
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
;
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
));
538 if (descriptor
< 0) {
539 error_details_
= File::OSErrorToFileError(errno
);
543 if (flags
& (FLAG_CREATE_ALWAYS
| FLAG_CREATE
))
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();
560 NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
562 #elif defined(OS_LINUX) || defined(OS_ANDROID)
563 return !HANDLE_EINTR(fdatasync(file_
.get()));
565 return !HANDLE_EINTR(fsync(file_
.get()));
569 void File::SetPlatformFile(PlatformFile file
) {
570 DCHECK(!file_
.is_valid());