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/platform_file.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/posix/eintr_wrapper.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/threading/thread_restrictions.h"
19 #if defined(OS_ANDROID)
20 #include "base/os_compat_android.h"
25 // Make sure our Whence mappings match the system headers.
26 COMPILE_ASSERT(PLATFORM_FILE_FROM_BEGIN
== SEEK_SET
&&
27 PLATFORM_FILE_FROM_CURRENT
== SEEK_CUR
&&
28 PLATFORM_FILE_FROM_END
== SEEK_END
, whence_matches_system
);
32 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
33 typedef struct stat stat_wrapper_t
;
34 static int CallFstat(int fd
, stat_wrapper_t
*sb
) {
35 base::ThreadRestrictions::AssertIOAllowed();
39 typedef struct stat64 stat_wrapper_t
;
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 PlatformFileError
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 ErrnoToPlatformFileError(errno
);
86 return PLATFORM_FILE_OK
;
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 PlatformFileError
CallFctnlFlock(PlatformFile file
, bool do_lock
) {
113 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
114 return PLATFORM_FILE_ERROR_INVALID_OPERATION
;
116 #endif // defined(OS_NACL)
120 bool ClosePlatformFile(PlatformFile file
) {
121 base::ThreadRestrictions::AssertIOAllowed();
122 return !IGNORE_EINTR(close(file
));
125 int64
SeekPlatformFile(PlatformFile file
,
126 PlatformFileWhence whence
,
128 base::ThreadRestrictions::AssertIOAllowed();
129 if (file
< 0 || offset
< 0)
132 return lseek(file
, static_cast<off_t
>(offset
), static_cast<int>(whence
));
135 int ReadPlatformFile(PlatformFile file
, int64 offset
, char* data
, int size
) {
136 base::ThreadRestrictions::AssertIOAllowed();
137 if (file
< 0 || size
< 0)
143 rv
= HANDLE_EINTR(pread(file
, data
+ bytes_read
,
144 size
- bytes_read
, offset
+ bytes_read
));
149 } while (bytes_read
< size
);
151 return bytes_read
? bytes_read
: rv
;
154 int ReadPlatformFileAtCurrentPos(PlatformFile file
, char* data
, int size
) {
155 base::ThreadRestrictions::AssertIOAllowed();
156 if (file
< 0 || size
< 0)
162 rv
= HANDLE_EINTR(read(file
, data
+ bytes_read
, size
- bytes_read
));
167 } while (bytes_read
< size
);
169 return bytes_read
? bytes_read
: rv
;
172 int ReadPlatformFileNoBestEffort(PlatformFile file
, int64 offset
,
173 char* data
, int size
) {
174 base::ThreadRestrictions::AssertIOAllowed();
178 return HANDLE_EINTR(pread(file
, data
, size
, offset
));
181 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file
,
182 char* data
, int size
) {
183 base::ThreadRestrictions::AssertIOAllowed();
184 if (file
< 0 || size
< 0)
187 return HANDLE_EINTR(read(file
, data
, size
));
190 int WritePlatformFile(PlatformFile file
, int64 offset
,
191 const char* data
, int size
) {
192 base::ThreadRestrictions::AssertIOAllowed();
194 if (IsOpenAppend(file
))
195 return WritePlatformFileAtCurrentPos(file
, data
, size
);
197 if (file
< 0 || size
< 0)
200 int bytes_written
= 0;
203 rv
= HANDLE_EINTR(pwrite(file
, data
+ bytes_written
,
204 size
- bytes_written
, offset
+ bytes_written
));
209 } while (bytes_written
< size
);
211 return bytes_written
? bytes_written
: rv
;
214 int WritePlatformFileAtCurrentPos(PlatformFile file
,
215 const char* data
, int size
) {
216 base::ThreadRestrictions::AssertIOAllowed();
217 if (file
< 0 || size
< 0)
220 int bytes_written
= 0;
223 rv
= HANDLE_EINTR(write(file
, data
+ bytes_written
, size
- bytes_written
));
228 } while (bytes_written
< size
);
230 return bytes_written
? bytes_written
: rv
;
233 int WritePlatformFileCurPosNoBestEffort(PlatformFile file
,
234 const char* data
, int size
) {
235 base::ThreadRestrictions::AssertIOAllowed();
236 if (file
< 0 || size
< 0)
239 return HANDLE_EINTR(write(file
, data
, size
));
242 bool TruncatePlatformFile(PlatformFile file
, int64 length
) {
243 base::ThreadRestrictions::AssertIOAllowed();
244 return ((file
>= 0) && !CallFtruncate(file
, length
));
247 bool FlushPlatformFile(PlatformFile file
) {
248 base::ThreadRestrictions::AssertIOAllowed();
249 return !CallFsync(file
);
252 bool TouchPlatformFile(PlatformFile file
, const base::Time
& last_access_time
,
253 const base::Time
& last_modified_time
) {
254 base::ThreadRestrictions::AssertIOAllowed();
259 times
[0] = last_access_time
.ToTimeVal();
260 times
[1] = last_modified_time
.ToTimeVal();
262 return !CallFutimes(file
, times
);
265 bool GetPlatformFileInfo(PlatformFile file
, PlatformFileInfo
* info
) {
269 stat_wrapper_t file_info
;
270 if (CallFstat(file
, &file_info
))
273 info
->is_directory
= S_ISDIR(file_info
.st_mode
);
274 info
->is_symbolic_link
= S_ISLNK(file_info
.st_mode
);
275 info
->size
= file_info
.st_size
;
277 #if defined(OS_LINUX)
278 const time_t last_modified_sec
= file_info
.st_mtim
.tv_sec
;
279 const int64 last_modified_nsec
= file_info
.st_mtim
.tv_nsec
;
280 const time_t last_accessed_sec
= file_info
.st_atim
.tv_sec
;
281 const int64 last_accessed_nsec
= file_info
.st_atim
.tv_nsec
;
282 const time_t creation_time_sec
= file_info
.st_ctim
.tv_sec
;
283 const int64 creation_time_nsec
= file_info
.st_ctim
.tv_nsec
;
284 #elif defined(OS_ANDROID)
285 const time_t last_modified_sec
= file_info
.st_mtime
;
286 const int64 last_modified_nsec
= file_info
.st_mtime_nsec
;
287 const time_t last_accessed_sec
= file_info
.st_atime
;
288 const int64 last_accessed_nsec
= file_info
.st_atime_nsec
;
289 const time_t creation_time_sec
= file_info
.st_ctime
;
290 const int64 creation_time_nsec
= file_info
.st_ctime_nsec
;
291 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
292 const time_t last_modified_sec
= file_info
.st_mtimespec
.tv_sec
;
293 const int64 last_modified_nsec
= file_info
.st_mtimespec
.tv_nsec
;
294 const time_t last_accessed_sec
= file_info
.st_atimespec
.tv_sec
;
295 const int64 last_accessed_nsec
= file_info
.st_atimespec
.tv_nsec
;
296 const time_t creation_time_sec
= file_info
.st_ctimespec
.tv_sec
;
297 const int64 creation_time_nsec
= file_info
.st_ctimespec
.tv_nsec
;
299 // TODO(gavinp): Investigate a good high resolution option for OS_NACL.
300 const time_t last_modified_sec
= file_info
.st_mtime
;
301 const int64 last_modified_nsec
= 0;
302 const time_t last_accessed_sec
= file_info
.st_atime
;
303 const int64 last_accessed_nsec
= 0;
304 const time_t creation_time_sec
= file_info
.st_ctime
;
305 const int64 creation_time_nsec
= 0;
308 info
->last_modified
=
309 base::Time::FromTimeT(last_modified_sec
) +
310 base::TimeDelta::FromMicroseconds(last_modified_nsec
/
311 base::Time::kNanosecondsPerMicrosecond
);
312 info
->last_accessed
=
313 base::Time::FromTimeT(last_accessed_sec
) +
314 base::TimeDelta::FromMicroseconds(last_accessed_nsec
/
315 base::Time::kNanosecondsPerMicrosecond
);
316 info
->creation_time
=
317 base::Time::FromTimeT(creation_time_sec
) +
318 base::TimeDelta::FromMicroseconds(creation_time_nsec
/
319 base::Time::kNanosecondsPerMicrosecond
);
323 PlatformFileError
LockPlatformFile(PlatformFile file
) {
324 return CallFctnlFlock(file
, true);
327 PlatformFileError
UnlockPlatformFile(PlatformFile file
) {
328 return CallFctnlFlock(file
, false);
331 PlatformFileError
ErrnoToPlatformFileError(int saved_errno
) {
332 switch (saved_errno
) {
337 return PLATFORM_FILE_ERROR_ACCESS_DENIED
;
338 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
340 return PLATFORM_FILE_ERROR_IN_USE
;
343 return PLATFORM_FILE_ERROR_EXISTS
;
345 return PLATFORM_FILE_ERROR_NOT_FOUND
;
347 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED
;
349 return PLATFORM_FILE_ERROR_NO_MEMORY
;
351 return PLATFORM_FILE_ERROR_NO_SPACE
;
353 return PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
;
355 #if !defined(OS_NACL) // NaCl build has no metrics code.
356 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
359 return PLATFORM_FILE_ERROR_FAILED
;