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/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/metrics/sparse_histogram.h"
15 // TODO(rvargas): remove this (needed for kInvalidPlatformFileValue).
16 #include "base/platform_file.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/thread_restrictions.h"
21 #if defined(OS_ANDROID)
22 #include "base/os_compat_android.h"
27 // Make sure our Whence mappings match the system headers.
28 COMPILE_ASSERT(File::FROM_BEGIN
== SEEK_SET
&&
29 File::FROM_CURRENT
== SEEK_CUR
&&
30 File::FROM_END
== SEEK_END
, whence_matches_system
);
34 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
35 static int CallFstat(int fd
, stat_wrapper_t
*sb
) {
36 base::ThreadRestrictions::AssertIOAllowed();
40 static int CallFstat(int fd
, stat_wrapper_t
*sb
) {
41 base::ThreadRestrictions::AssertIOAllowed();
42 return fstat64(fd
, sb
);
46 // NaCl doesn't provide the following system calls, so either simulate them or
47 // wrap them in order to minimize the number of #ifdef's in this file.
49 static bool IsOpenAppend(PlatformFile file
) {
50 return (fcntl(file
, F_GETFL
) & O_APPEND
) != 0;
53 static int CallFtruncate(PlatformFile file
, int64 length
) {
54 return HANDLE_EINTR(ftruncate(file
, length
));
57 static int CallFsync(PlatformFile file
) {
58 return HANDLE_EINTR(fsync(file
));
61 static int CallFutimes(PlatformFile file
, const struct timeval times
[2]) {
63 // futimens should be available, but futimes might not be
64 // http://pubs.opengroup.org/onlinepubs/9699919799/
67 ts_times
[0].tv_sec
= times
[0].tv_sec
;
68 ts_times
[0].tv_nsec
= times
[0].tv_usec
* 1000;
69 ts_times
[1].tv_sec
= times
[1].tv_sec
;
70 ts_times
[1].tv_nsec
= times
[1].tv_usec
* 1000;
72 return futimens(file
, ts_times
);
74 return futimes(file
, times
);
78 static File::Error
CallFctnlFlock(PlatformFile file
, bool do_lock
) {
80 lock
.l_type
= F_WRLCK
;
81 lock
.l_whence
= SEEK_SET
;
83 lock
.l_len
= 0; // Lock entire file.
84 if (HANDLE_EINTR(fcntl(file
, do_lock
? F_SETLK
: F_UNLCK
, &lock
)) == -1)
85 return File::OSErrorToFileError(errno
);
88 #else // defined(OS_NACL)
90 static bool IsOpenAppend(PlatformFile file
) {
91 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
92 // standard and always appends if the file is opened with O_APPEND, just
97 static int CallFtruncate(PlatformFile file
, int64 length
) {
98 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
102 static int CallFsync(PlatformFile file
) {
103 NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
107 static int CallFutimes(PlatformFile file
, const struct timeval times
[2]) {
108 NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
112 static File::Error
CallFctnlFlock(PlatformFile file
, bool do_lock
) {
113 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
114 return File::FILE_ERROR_INVALID_OPERATION
;
116 #endif // defined(OS_NACL)
120 void File::Info::FromStat(const stat_wrapper_t
& stat_info
) {
121 is_directory
= S_ISDIR(stat_info
.st_mode
);
122 is_symbolic_link
= S_ISLNK(stat_info
.st_mode
);
123 size
= stat_info
.st_size
;
125 #if defined(OS_LINUX)
126 time_t last_modified_sec
= stat_info
.st_mtim
.tv_sec
;
127 int64 last_modified_nsec
= stat_info
.st_mtim
.tv_nsec
;
128 time_t last_accessed_sec
= stat_info
.st_atim
.tv_sec
;
129 int64 last_accessed_nsec
= stat_info
.st_atim
.tv_nsec
;
130 time_t creation_time_sec
= stat_info
.st_ctim
.tv_sec
;
131 int64 creation_time_nsec
= stat_info
.st_ctim
.tv_nsec
;
132 #elif defined(OS_ANDROID)
133 time_t last_modified_sec
= stat_info
.st_mtime
;
134 int64 last_modified_nsec
= stat_info
.st_mtime_nsec
;
135 time_t last_accessed_sec
= stat_info
.st_atime
;
136 int64 last_accessed_nsec
= stat_info
.st_atime_nsec
;
137 time_t creation_time_sec
= stat_info
.st_ctime
;
138 int64 creation_time_nsec
= stat_info
.st_ctime_nsec
;
139 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
140 time_t last_modified_sec
= stat_info
.st_mtimespec
.tv_sec
;
141 int64 last_modified_nsec
= stat_info
.st_mtimespec
.tv_nsec
;
142 time_t last_accessed_sec
= stat_info
.st_atimespec
.tv_sec
;
143 int64 last_accessed_nsec
= stat_info
.st_atimespec
.tv_nsec
;
144 time_t creation_time_sec
= stat_info
.st_ctimespec
.tv_sec
;
145 int64 creation_time_nsec
= stat_info
.st_ctimespec
.tv_nsec
;
147 time_t last_modified_sec
= stat_info
.st_mtime
;
148 int64 last_modified_nsec
= 0;
149 time_t last_accessed_sec
= stat_info
.st_atime
;
150 int64 last_accessed_nsec
= 0;
151 time_t creation_time_sec
= stat_info
.st_ctime
;
152 int64 creation_time_nsec
= 0;
156 Time::FromTimeT(last_modified_sec
) +
157 TimeDelta::FromMicroseconds(last_modified_nsec
/
158 Time::kNanosecondsPerMicrosecond
);
161 Time::FromTimeT(last_accessed_sec
) +
162 TimeDelta::FromMicroseconds(last_accessed_nsec
/
163 Time::kNanosecondsPerMicrosecond
);
166 Time::FromTimeT(creation_time_sec
) +
167 TimeDelta::FromMicroseconds(creation_time_nsec
/
168 Time::kNanosecondsPerMicrosecond
);
171 // NaCl doesn't implement system calls to open files directly.
172 #if !defined(OS_NACL)
173 // TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
174 void File::InitializeUnsafe(const FilePath
& name
, uint32 flags
) {
175 base::ThreadRestrictions::AssertIOAllowed();
179 if (flags
& FLAG_CREATE
)
180 open_flags
= O_CREAT
| O_EXCL
;
184 if (flags
& FLAG_CREATE_ALWAYS
) {
186 DCHECK(flags
& FLAG_WRITE
);
187 open_flags
= O_CREAT
| O_TRUNC
;
190 if (flags
& FLAG_OPEN_TRUNCATED
) {
192 DCHECK(flags
& FLAG_WRITE
);
193 open_flags
= O_TRUNC
;
196 if (!open_flags
&& !(flags
& FLAG_OPEN
) && !(flags
& FLAG_OPEN_ALWAYS
)) {
199 error_details_
= FILE_ERROR_FAILED
;
203 if (flags
& FLAG_WRITE
&& flags
& FLAG_READ
) {
204 open_flags
|= O_RDWR
;
205 } else if (flags
& FLAG_WRITE
) {
206 open_flags
|= O_WRONLY
;
207 } else if (!(flags
& FLAG_READ
) &&
208 !(flags
& FLAG_WRITE_ATTRIBUTES
) &&
209 !(flags
& FLAG_APPEND
) &&
210 !(flags
& FLAG_OPEN_ALWAYS
)) {
214 if (flags
& FLAG_TERMINAL_DEVICE
)
215 open_flags
|= O_NOCTTY
| O_NDELAY
;
217 if (flags
& FLAG_APPEND
&& flags
& FLAG_READ
)
218 open_flags
|= O_APPEND
| O_RDWR
;
219 else if (flags
& FLAG_APPEND
)
220 open_flags
|= O_APPEND
| O_WRONLY
;
222 COMPILE_ASSERT(O_RDONLY
== 0, O_RDONLY_must_equal_zero
);
224 int mode
= S_IRUSR
| S_IWUSR
;
225 #if defined(OS_CHROMEOS)
226 mode
|= S_IRGRP
| S_IROTH
;
229 int descriptor
= HANDLE_EINTR(open(name
.value().c_str(), open_flags
, mode
));
231 if (flags
& FLAG_OPEN_ALWAYS
) {
232 if (descriptor
< 0) {
233 open_flags
|= O_CREAT
;
234 if (flags
& FLAG_EXCLUSIVE_READ
|| flags
& FLAG_EXCLUSIVE_WRITE
)
235 open_flags
|= O_EXCL
; // together with O_CREAT implies O_NOFOLLOW
237 descriptor
= HANDLE_EINTR(open(name
.value().c_str(), open_flags
, mode
));
243 if (descriptor
< 0) {
244 error_details_
= File::OSErrorToFileError(errno
);
248 if (flags
& (FLAG_CREATE_ALWAYS
| FLAG_CREATE
))
251 if (flags
& FLAG_DELETE_ON_CLOSE
)
252 unlink(name
.value().c_str());
254 async_
= ((flags
& FLAG_ASYNC
) == FLAG_ASYNC
);
255 error_details_
= FILE_OK
;
256 file_
.reset(descriptor
);
258 #endif // !defined(OS_NACL)
260 bool File::IsValid() const {
261 return file_
.is_valid();
264 PlatformFile
File::GetPlatformFile() const {
268 PlatformFile
File::TakePlatformFile() {
269 return file_
.release();
276 base::ThreadRestrictions::AssertIOAllowed();
280 int64
File::Seek(Whence whence
, int64 offset
) {
281 base::ThreadRestrictions::AssertIOAllowed();
286 return lseek(file_
.get(), static_cast<off_t
>(offset
),
287 static_cast<int>(whence
));
290 int File::Read(int64 offset
, char* data
, int size
) {
291 base::ThreadRestrictions::AssertIOAllowed();
299 rv
= HANDLE_EINTR(pread(file_
.get(), data
+ bytes_read
,
300 size
- bytes_read
, offset
+ bytes_read
));
305 } while (bytes_read
< size
);
307 return bytes_read
? bytes_read
: rv
;
310 int File::ReadAtCurrentPos(char* data
, int size
) {
311 base::ThreadRestrictions::AssertIOAllowed();
319 rv
= HANDLE_EINTR(read(file_
.get(), data
+ bytes_read
, size
- bytes_read
));
324 } while (bytes_read
< size
);
326 return bytes_read
? bytes_read
: rv
;
329 int File::ReadNoBestEffort(int64 offset
, char* data
, int size
) {
330 base::ThreadRestrictions::AssertIOAllowed();
333 return HANDLE_EINTR(pread(file_
.get(), data
, size
, offset
));
336 int File::ReadAtCurrentPosNoBestEffort(char* data
, int size
) {
337 base::ThreadRestrictions::AssertIOAllowed();
342 return HANDLE_EINTR(read(file_
.get(), data
, size
));
345 int File::Write(int64 offset
, const char* data
, int size
) {
346 base::ThreadRestrictions::AssertIOAllowed();
348 if (IsOpenAppend(file_
.get()))
349 return WriteAtCurrentPos(data
, size
);
355 int bytes_written
= 0;
358 rv
= HANDLE_EINTR(pwrite(file_
.get(), data
+ bytes_written
,
359 size
- bytes_written
, offset
+ bytes_written
));
364 } while (bytes_written
< size
);
366 return bytes_written
? bytes_written
: rv
;
369 int File::WriteAtCurrentPos(const char* data
, int size
) {
370 base::ThreadRestrictions::AssertIOAllowed();
375 int bytes_written
= 0;
378 rv
= HANDLE_EINTR(write(file_
.get(), data
+ bytes_written
,
379 size
- bytes_written
));
384 } while (bytes_written
< size
);
386 return bytes_written
? bytes_written
: rv
;
389 int File::WriteAtCurrentPosNoBestEffort(const char* data
, int size
) {
390 base::ThreadRestrictions::AssertIOAllowed();
395 return HANDLE_EINTR(write(file_
.get(), data
, size
));
398 int64
File::GetLength() {
401 stat_wrapper_t file_info
;
402 if (CallFstat(file_
.get(), &file_info
))
405 return file_info
.st_size
;
408 bool File::SetLength(int64 length
) {
409 base::ThreadRestrictions::AssertIOAllowed();
411 return !CallFtruncate(file_
.get(), length
);
415 base::ThreadRestrictions::AssertIOAllowed();
417 return !CallFsync(file_
.get());
420 bool File::SetTimes(Time last_access_time
, Time last_modified_time
) {
421 base::ThreadRestrictions::AssertIOAllowed();
425 times
[0] = last_access_time
.ToTimeVal();
426 times
[1] = last_modified_time
.ToTimeVal();
428 return !CallFutimes(file_
.get(), times
);
431 bool File::GetInfo(Info
* info
) {
434 stat_wrapper_t file_info
;
435 if (CallFstat(file_
.get(), &file_info
))
438 info
->FromStat(file_info
);
442 File::Error
File::Lock() {
443 return CallFctnlFlock(file_
.get(), true);
446 File::Error
File::Unlock() {
447 return CallFctnlFlock(file_
.get(), false);
451 File::Error
File::OSErrorToFileError(int saved_errno
) {
452 switch (saved_errno
) {
457 return FILE_ERROR_ACCESS_DENIED
;
458 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
460 return FILE_ERROR_IN_USE
;
463 return FILE_ERROR_EXISTS
;
465 return FILE_ERROR_NOT_FOUND
;
467 return FILE_ERROR_TOO_MANY_OPENED
;
469 return FILE_ERROR_NO_MEMORY
;
471 return FILE_ERROR_NO_SPACE
;
473 return FILE_ERROR_NOT_A_DIRECTORY
;
475 #if !defined(OS_NACL) // NaCl build has no metrics code.
476 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
479 return FILE_ERROR_FAILED
;
483 void File::SetPlatformFile(PlatformFile file
) {
484 DCHECK(!file_
.is_valid());