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 // NaCl doesn't implement system calls to open files directly.
121 #if !defined(OS_NACL)
122 FILE* FdopenPlatformFile(PlatformFile file
, const char* mode
) {
123 return fdopen(file
, mode
);
125 #endif // !defined(OS_NACL)
127 bool ClosePlatformFile(PlatformFile file
) {
128 base::ThreadRestrictions::AssertIOAllowed();
129 return !IGNORE_EINTR(close(file
));
132 int64
SeekPlatformFile(PlatformFile file
,
133 PlatformFileWhence whence
,
135 base::ThreadRestrictions::AssertIOAllowed();
136 if (file
< 0 || offset
< 0)
139 return lseek(file
, static_cast<off_t
>(offset
), static_cast<int>(whence
));
142 int ReadPlatformFile(PlatformFile file
, int64 offset
, char* data
, int size
) {
143 base::ThreadRestrictions::AssertIOAllowed();
144 if (file
< 0 || size
< 0)
150 rv
= HANDLE_EINTR(pread(file
, data
+ bytes_read
,
151 size
- bytes_read
, offset
+ bytes_read
));
156 } while (bytes_read
< size
);
158 return bytes_read
? bytes_read
: rv
;
161 int ReadPlatformFileAtCurrentPos(PlatformFile file
, char* data
, int size
) {
162 base::ThreadRestrictions::AssertIOAllowed();
163 if (file
< 0 || size
< 0)
169 rv
= HANDLE_EINTR(read(file
, data
+ bytes_read
, size
- bytes_read
));
174 } while (bytes_read
< size
);
176 return bytes_read
? bytes_read
: rv
;
179 int ReadPlatformFileNoBestEffort(PlatformFile file
, int64 offset
,
180 char* data
, int size
) {
181 base::ThreadRestrictions::AssertIOAllowed();
185 return HANDLE_EINTR(pread(file
, data
, size
, offset
));
188 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file
,
189 char* data
, int size
) {
190 base::ThreadRestrictions::AssertIOAllowed();
191 if (file
< 0 || size
< 0)
194 return HANDLE_EINTR(read(file
, data
, size
));
197 int WritePlatformFile(PlatformFile file
, int64 offset
,
198 const char* data
, int size
) {
199 base::ThreadRestrictions::AssertIOAllowed();
201 if (IsOpenAppend(file
))
202 return WritePlatformFileAtCurrentPos(file
, data
, size
);
204 if (file
< 0 || size
< 0)
207 int bytes_written
= 0;
210 rv
= HANDLE_EINTR(pwrite(file
, data
+ bytes_written
,
211 size
- bytes_written
, offset
+ bytes_written
));
216 } while (bytes_written
< size
);
218 return bytes_written
? bytes_written
: rv
;
221 int WritePlatformFileAtCurrentPos(PlatformFile file
,
222 const char* data
, int size
) {
223 base::ThreadRestrictions::AssertIOAllowed();
224 if (file
< 0 || size
< 0)
227 int bytes_written
= 0;
230 rv
= HANDLE_EINTR(write(file
, data
+ bytes_written
, size
- bytes_written
));
235 } while (bytes_written
< size
);
237 return bytes_written
? bytes_written
: rv
;
240 int WritePlatformFileCurPosNoBestEffort(PlatformFile file
,
241 const char* data
, int size
) {
242 base::ThreadRestrictions::AssertIOAllowed();
243 if (file
< 0 || size
< 0)
246 return HANDLE_EINTR(write(file
, data
, size
));
249 bool TruncatePlatformFile(PlatformFile file
, int64 length
) {
250 base::ThreadRestrictions::AssertIOAllowed();
251 return ((file
>= 0) && !CallFtruncate(file
, length
));
254 bool FlushPlatformFile(PlatformFile file
) {
255 base::ThreadRestrictions::AssertIOAllowed();
256 return !CallFsync(file
);
259 bool TouchPlatformFile(PlatformFile file
, const base::Time
& last_access_time
,
260 const base::Time
& last_modified_time
) {
261 base::ThreadRestrictions::AssertIOAllowed();
266 times
[0] = last_access_time
.ToTimeVal();
267 times
[1] = last_modified_time
.ToTimeVal();
269 return !CallFutimes(file
, times
);
272 bool GetPlatformFileInfo(PlatformFile file
, PlatformFileInfo
* info
) {
276 stat_wrapper_t file_info
;
277 if (CallFstat(file
, &file_info
))
280 info
->is_directory
= S_ISDIR(file_info
.st_mode
);
281 info
->is_symbolic_link
= S_ISLNK(file_info
.st_mode
);
282 info
->size
= file_info
.st_size
;
284 #if defined(OS_LINUX)
285 const time_t last_modified_sec
= file_info
.st_mtim
.tv_sec
;
286 const int64 last_modified_nsec
= file_info
.st_mtim
.tv_nsec
;
287 const time_t last_accessed_sec
= file_info
.st_atim
.tv_sec
;
288 const int64 last_accessed_nsec
= file_info
.st_atim
.tv_nsec
;
289 const time_t creation_time_sec
= file_info
.st_ctim
.tv_sec
;
290 const int64 creation_time_nsec
= file_info
.st_ctim
.tv_nsec
;
291 #elif defined(OS_ANDROID)
292 const time_t last_modified_sec
= file_info
.st_mtime
;
293 const int64 last_modified_nsec
= file_info
.st_mtime_nsec
;
294 const time_t last_accessed_sec
= file_info
.st_atime
;
295 const int64 last_accessed_nsec
= file_info
.st_atime_nsec
;
296 const time_t creation_time_sec
= file_info
.st_ctime
;
297 const int64 creation_time_nsec
= file_info
.st_ctime_nsec
;
298 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
299 const time_t last_modified_sec
= file_info
.st_mtimespec
.tv_sec
;
300 const int64 last_modified_nsec
= file_info
.st_mtimespec
.tv_nsec
;
301 const time_t last_accessed_sec
= file_info
.st_atimespec
.tv_sec
;
302 const int64 last_accessed_nsec
= file_info
.st_atimespec
.tv_nsec
;
303 const time_t creation_time_sec
= file_info
.st_ctimespec
.tv_sec
;
304 const int64 creation_time_nsec
= file_info
.st_ctimespec
.tv_nsec
;
306 // TODO(gavinp): Investigate a good high resolution option for OS_NACL.
307 const time_t last_modified_sec
= file_info
.st_mtime
;
308 const int64 last_modified_nsec
= 0;
309 const time_t last_accessed_sec
= file_info
.st_atime
;
310 const int64 last_accessed_nsec
= 0;
311 const time_t creation_time_sec
= file_info
.st_ctime
;
312 const int64 creation_time_nsec
= 0;
315 info
->last_modified
=
316 base::Time::FromTimeT(last_modified_sec
) +
317 base::TimeDelta::FromMicroseconds(last_modified_nsec
/
318 base::Time::kNanosecondsPerMicrosecond
);
319 info
->last_accessed
=
320 base::Time::FromTimeT(last_accessed_sec
) +
321 base::TimeDelta::FromMicroseconds(last_accessed_nsec
/
322 base::Time::kNanosecondsPerMicrosecond
);
323 info
->creation_time
=
324 base::Time::FromTimeT(creation_time_sec
) +
325 base::TimeDelta::FromMicroseconds(creation_time_nsec
/
326 base::Time::kNanosecondsPerMicrosecond
);
330 PlatformFileError
LockPlatformFile(PlatformFile file
) {
331 return CallFctnlFlock(file
, true);
334 PlatformFileError
UnlockPlatformFile(PlatformFile file
) {
335 return CallFctnlFlock(file
, false);
338 PlatformFileError
ErrnoToPlatformFileError(int saved_errno
) {
339 switch (saved_errno
) {
344 return PLATFORM_FILE_ERROR_ACCESS_DENIED
;
345 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
347 return PLATFORM_FILE_ERROR_IN_USE
;
350 return PLATFORM_FILE_ERROR_EXISTS
;
352 return PLATFORM_FILE_ERROR_NOT_FOUND
;
354 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED
;
356 return PLATFORM_FILE_ERROR_NO_MEMORY
;
358 return PLATFORM_FILE_ERROR_NO_SPACE
;
360 return PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
;
362 #if !defined(OS_NACL) // NaCl build has no metrics code.
363 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
366 return PLATFORM_FILE_ERROR_FAILED
;