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 // TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here?
123 PlatformFile
CreatePlatformFileUnsafe(const FilePath
& name
,
126 PlatformFileError
* error
) {
127 base::ThreadRestrictions::AssertIOAllowed();
130 if (flags
& PLATFORM_FILE_CREATE
)
131 open_flags
= O_CREAT
| O_EXCL
;
136 if (flags
& PLATFORM_FILE_CREATE_ALWAYS
) {
138 open_flags
= O_CREAT
| O_TRUNC
;
141 if (flags
& PLATFORM_FILE_OPEN_TRUNCATED
) {
143 DCHECK(flags
& PLATFORM_FILE_WRITE
);
144 open_flags
= O_TRUNC
;
147 if (!open_flags
&& !(flags
& PLATFORM_FILE_OPEN
) &&
148 !(flags
& PLATFORM_FILE_OPEN_ALWAYS
)) {
152 *error
= PLATFORM_FILE_ERROR_FAILED
;
153 return kInvalidPlatformFileValue
;
156 if (flags
& PLATFORM_FILE_WRITE
&& flags
& PLATFORM_FILE_READ
) {
157 open_flags
|= O_RDWR
;
158 } else if (flags
& PLATFORM_FILE_WRITE
) {
159 open_flags
|= O_WRONLY
;
160 } else if (!(flags
& PLATFORM_FILE_READ
) &&
161 !(flags
& PLATFORM_FILE_WRITE_ATTRIBUTES
) &&
162 !(flags
& PLATFORM_FILE_APPEND
) &&
163 !(flags
& PLATFORM_FILE_OPEN_ALWAYS
)) {
167 if (flags
& PLATFORM_FILE_TERMINAL_DEVICE
)
168 open_flags
|= O_NOCTTY
| O_NDELAY
;
170 if (flags
& PLATFORM_FILE_APPEND
&& flags
& PLATFORM_FILE_READ
)
171 open_flags
|= O_APPEND
| O_RDWR
;
172 else if (flags
& PLATFORM_FILE_APPEND
)
173 open_flags
|= O_APPEND
| O_WRONLY
;
175 COMPILE_ASSERT(O_RDONLY
== 0, O_RDONLY_must_equal_zero
);
177 int mode
= S_IRUSR
| S_IWUSR
;
178 #if defined(OS_CHROMEOS)
179 mode
|= S_IRGRP
| S_IROTH
;
183 HANDLE_EINTR(open(name
.value().c_str(), open_flags
, mode
));
185 if (flags
& PLATFORM_FILE_OPEN_ALWAYS
) {
186 if (descriptor
< 0) {
187 open_flags
|= O_CREAT
;
188 if (flags
& PLATFORM_FILE_EXCLUSIVE_READ
||
189 flags
& PLATFORM_FILE_EXCLUSIVE_WRITE
) {
190 open_flags
|= O_EXCL
; // together with O_CREAT implies O_NOFOLLOW
192 descriptor
= HANDLE_EINTR(
193 open(name
.value().c_str(), open_flags
, mode
));
194 if (created
&& descriptor
>= 0)
199 if (created
&& (descriptor
>= 0) &&
200 (flags
& (PLATFORM_FILE_CREATE_ALWAYS
| PLATFORM_FILE_CREATE
)))
203 if ((descriptor
>= 0) && (flags
& PLATFORM_FILE_DELETE_ON_CLOSE
)) {
204 unlink(name
.value().c_str());
209 *error
= PLATFORM_FILE_OK
;
211 *error
= ErrnoToPlatformFileError(errno
);
217 FILE* FdopenPlatformFile(PlatformFile file
, const char* mode
) {
218 return fdopen(file
, mode
);
220 #endif // !defined(OS_NACL)
222 bool ClosePlatformFile(PlatformFile file
) {
223 base::ThreadRestrictions::AssertIOAllowed();
224 return !IGNORE_EINTR(close(file
));
227 int64
SeekPlatformFile(PlatformFile file
,
228 PlatformFileWhence whence
,
230 base::ThreadRestrictions::AssertIOAllowed();
231 if (file
< 0 || offset
< 0)
234 return lseek(file
, static_cast<off_t
>(offset
), static_cast<int>(whence
));
237 int ReadPlatformFile(PlatformFile file
, int64 offset
, char* data
, int size
) {
238 base::ThreadRestrictions::AssertIOAllowed();
239 if (file
< 0 || size
< 0)
245 rv
= HANDLE_EINTR(pread(file
, data
+ bytes_read
,
246 size
- bytes_read
, offset
+ bytes_read
));
251 } while (bytes_read
< size
);
253 return bytes_read
? bytes_read
: rv
;
256 int ReadPlatformFileAtCurrentPos(PlatformFile file
, char* data
, int size
) {
257 base::ThreadRestrictions::AssertIOAllowed();
258 if (file
< 0 || size
< 0)
264 rv
= HANDLE_EINTR(read(file
, data
, size
));
269 } while (bytes_read
< size
);
271 return bytes_read
? bytes_read
: rv
;
274 int ReadPlatformFileNoBestEffort(PlatformFile file
, int64 offset
,
275 char* data
, int size
) {
276 base::ThreadRestrictions::AssertIOAllowed();
280 return HANDLE_EINTR(pread(file
, data
, size
, offset
));
283 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file
,
284 char* data
, int size
) {
285 base::ThreadRestrictions::AssertIOAllowed();
286 if (file
< 0 || size
< 0)
289 return HANDLE_EINTR(read(file
, data
, size
));
292 int WritePlatformFile(PlatformFile file
, int64 offset
,
293 const char* data
, int size
) {
294 base::ThreadRestrictions::AssertIOAllowed();
296 if (IsOpenAppend(file
))
297 return WritePlatformFileAtCurrentPos(file
, data
, size
);
299 if (file
< 0 || size
< 0)
302 int bytes_written
= 0;
305 rv
= HANDLE_EINTR(pwrite(file
, data
+ bytes_written
,
306 size
- bytes_written
, offset
+ bytes_written
));
311 } while (bytes_written
< size
);
313 return bytes_written
? bytes_written
: rv
;
316 int WritePlatformFileAtCurrentPos(PlatformFile file
,
317 const char* data
, int size
) {
318 base::ThreadRestrictions::AssertIOAllowed();
319 if (file
< 0 || size
< 0)
322 int bytes_written
= 0;
325 rv
= HANDLE_EINTR(write(file
, data
, size
));
330 } while (bytes_written
< size
);
332 return bytes_written
? bytes_written
: rv
;
335 int WritePlatformFileCurPosNoBestEffort(PlatformFile file
,
336 const char* data
, int size
) {
337 base::ThreadRestrictions::AssertIOAllowed();
338 if (file
< 0 || size
< 0)
341 return HANDLE_EINTR(write(file
, data
, size
));
344 bool TruncatePlatformFile(PlatformFile file
, int64 length
) {
345 base::ThreadRestrictions::AssertIOAllowed();
346 return ((file
>= 0) && !CallFtruncate(file
, length
));
349 bool FlushPlatformFile(PlatformFile file
) {
350 base::ThreadRestrictions::AssertIOAllowed();
351 return !CallFsync(file
);
354 bool TouchPlatformFile(PlatformFile file
, const base::Time
& last_access_time
,
355 const base::Time
& last_modified_time
) {
356 base::ThreadRestrictions::AssertIOAllowed();
361 times
[0] = last_access_time
.ToTimeVal();
362 times
[1] = last_modified_time
.ToTimeVal();
364 return !CallFutimes(file
, times
);
367 bool GetPlatformFileInfo(PlatformFile file
, PlatformFileInfo
* info
) {
371 stat_wrapper_t file_info
;
372 if (CallFstat(file
, &file_info
))
375 info
->is_directory
= S_ISDIR(file_info
.st_mode
);
376 info
->is_symbolic_link
= S_ISLNK(file_info
.st_mode
);
377 info
->size
= file_info
.st_size
;
379 #if defined(OS_LINUX)
380 const time_t last_modified_sec
= file_info
.st_mtim
.tv_sec
;
381 const int64 last_modified_nsec
= file_info
.st_mtim
.tv_nsec
;
382 const time_t last_accessed_sec
= file_info
.st_atim
.tv_sec
;
383 const int64 last_accessed_nsec
= file_info
.st_atim
.tv_nsec
;
384 const time_t creation_time_sec
= file_info
.st_ctim
.tv_sec
;
385 const int64 creation_time_nsec
= file_info
.st_ctim
.tv_nsec
;
386 #elif defined(OS_ANDROID)
387 const time_t last_modified_sec
= file_info
.st_mtime
;
388 const int64 last_modified_nsec
= file_info
.st_mtime_nsec
;
389 const time_t last_accessed_sec
= file_info
.st_atime
;
390 const int64 last_accessed_nsec
= file_info
.st_atime_nsec
;
391 const time_t creation_time_sec
= file_info
.st_ctime
;
392 const int64 creation_time_nsec
= file_info
.st_ctime_nsec
;
393 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
394 const time_t last_modified_sec
= file_info
.st_mtimespec
.tv_sec
;
395 const int64 last_modified_nsec
= file_info
.st_mtimespec
.tv_nsec
;
396 const time_t last_accessed_sec
= file_info
.st_atimespec
.tv_sec
;
397 const int64 last_accessed_nsec
= file_info
.st_atimespec
.tv_nsec
;
398 const time_t creation_time_sec
= file_info
.st_ctimespec
.tv_sec
;
399 const int64 creation_time_nsec
= file_info
.st_ctimespec
.tv_nsec
;
401 // TODO(gavinp): Investigate a good high resolution option for OS_NACL.
402 const time_t last_modified_sec
= file_info
.st_mtime
;
403 const int64 last_modified_nsec
= 0;
404 const time_t last_accessed_sec
= file_info
.st_atime
;
405 const int64 last_accessed_nsec
= 0;
406 const time_t creation_time_sec
= file_info
.st_ctime
;
407 const int64 creation_time_nsec
= 0;
410 info
->last_modified
=
411 base::Time::FromTimeT(last_modified_sec
) +
412 base::TimeDelta::FromMicroseconds(last_modified_nsec
/
413 base::Time::kNanosecondsPerMicrosecond
);
414 info
->last_accessed
=
415 base::Time::FromTimeT(last_accessed_sec
) +
416 base::TimeDelta::FromMicroseconds(last_accessed_nsec
/
417 base::Time::kNanosecondsPerMicrosecond
);
418 info
->creation_time
=
419 base::Time::FromTimeT(creation_time_sec
) +
420 base::TimeDelta::FromMicroseconds(creation_time_nsec
/
421 base::Time::kNanosecondsPerMicrosecond
);
425 PlatformFileError
LockPlatformFile(PlatformFile file
) {
426 return CallFctnlFlock(file
, true);
429 PlatformFileError
UnlockPlatformFile(PlatformFile file
) {
430 return CallFctnlFlock(file
, false);
433 PlatformFileError
ErrnoToPlatformFileError(int saved_errno
) {
434 switch (saved_errno
) {
439 return PLATFORM_FILE_ERROR_ACCESS_DENIED
;
440 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
442 return PLATFORM_FILE_ERROR_IN_USE
;
445 return PLATFORM_FILE_ERROR_EXISTS
;
447 return PLATFORM_FILE_ERROR_NOT_FOUND
;
449 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED
;
451 return PLATFORM_FILE_ERROR_NO_MEMORY
;
453 return PLATFORM_FILE_ERROR_NO_SPACE
;
455 return PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
;
457 #if !defined(OS_NACL) // NaCl build has no metrics code.
458 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
461 return PLATFORM_FILE_ERROR_FAILED
;