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 int DoPread(PlatformFile file
, char* data
, int size
, int64 offset
) {
50 return HANDLE_EINTR(pread(file
, data
, size
, offset
));
53 static int DoPwrite(PlatformFile file
, const char* data
, int size
,
55 return HANDLE_EINTR(pwrite(file
, data
, size
, offset
));
58 static bool IsOpenAppend(PlatformFile file
) {
59 return (fcntl(file
, F_GETFL
) & O_APPEND
) != 0;
62 static int CallFtruncate(PlatformFile file
, int64 length
) {
63 return HANDLE_EINTR(ftruncate(file
, length
));
66 static int CallFsync(PlatformFile file
) {
67 return HANDLE_EINTR(fsync(file
));
70 static int CallFutimes(PlatformFile file
, const struct timeval times
[2]) {
72 // futimens should be available, but futimes might not be
73 // http://pubs.opengroup.org/onlinepubs/9699919799/
76 ts_times
[0].tv_sec
= times
[0].tv_sec
;
77 ts_times
[0].tv_nsec
= times
[0].tv_usec
* 1000;
78 ts_times
[1].tv_sec
= times
[1].tv_sec
;
79 ts_times
[1].tv_nsec
= times
[1].tv_usec
* 1000;
81 return futimens(file
, ts_times
);
83 return futimes(file
, times
);
86 #else // defined(OS_NACL)
87 // TODO(bbudge) Remove DoPread, DoPwrite when NaCl implements pread, pwrite.
88 static int DoPread(PlatformFile file
, char* data
, int size
, int64 offset
) {
89 lseek(file
, static_cast<off_t
>(offset
), SEEK_SET
);
90 return HANDLE_EINTR(read(file
, data
, size
));
93 static int DoPwrite(PlatformFile file
, const char* data
, int size
,
95 lseek(file
, static_cast<off_t
>(offset
), SEEK_SET
);
96 return HANDLE_EINTR(write(file
, data
, size
));
99 static bool IsOpenAppend(PlatformFile file
) {
100 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
101 // standard and always appends if the file is opened with O_APPEND, just
102 // return false here.
106 static int CallFtruncate(PlatformFile file
, int64 length
) {
107 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
111 static int CallFsync(PlatformFile file
) {
112 NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
116 static int CallFutimes(PlatformFile file
, const struct timeval times
[2]) {
117 NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
120 #endif // defined(OS_NACL)
124 // NaCl doesn't implement system calls to open files directly.
125 #if !defined(OS_NACL)
126 // TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here?
127 PlatformFile
CreatePlatformFileUnsafe(const FilePath
& name
,
130 PlatformFileError
* error
) {
131 base::ThreadRestrictions::AssertIOAllowed();
134 if (flags
& PLATFORM_FILE_CREATE
)
135 open_flags
= O_CREAT
| O_EXCL
;
140 if (flags
& PLATFORM_FILE_CREATE_ALWAYS
) {
142 open_flags
= O_CREAT
| O_TRUNC
;
145 if (flags
& PLATFORM_FILE_OPEN_TRUNCATED
) {
147 DCHECK(flags
& PLATFORM_FILE_WRITE
);
148 open_flags
= O_TRUNC
;
151 if (!open_flags
&& !(flags
& PLATFORM_FILE_OPEN
) &&
152 !(flags
& PLATFORM_FILE_OPEN_ALWAYS
)) {
156 *error
= PLATFORM_FILE_ERROR_FAILED
;
157 return kInvalidPlatformFileValue
;
160 if (flags
& PLATFORM_FILE_WRITE
&& flags
& PLATFORM_FILE_READ
) {
161 open_flags
|= O_RDWR
;
162 } else if (flags
& PLATFORM_FILE_WRITE
) {
163 open_flags
|= O_WRONLY
;
164 } else if (!(flags
& PLATFORM_FILE_READ
) &&
165 !(flags
& PLATFORM_FILE_WRITE_ATTRIBUTES
) &&
166 !(flags
& PLATFORM_FILE_APPEND
) &&
167 !(flags
& PLATFORM_FILE_OPEN_ALWAYS
)) {
171 if (flags
& PLATFORM_FILE_TERMINAL_DEVICE
)
172 open_flags
|= O_NOCTTY
| O_NDELAY
;
174 if (flags
& PLATFORM_FILE_APPEND
&& flags
& PLATFORM_FILE_READ
)
175 open_flags
|= O_APPEND
| O_RDWR
;
176 else if (flags
& PLATFORM_FILE_APPEND
)
177 open_flags
|= O_APPEND
| O_WRONLY
;
179 COMPILE_ASSERT(O_RDONLY
== 0, O_RDONLY_must_equal_zero
);
181 int mode
= S_IRUSR
| S_IWUSR
;
182 #if defined(OS_CHROMEOS)
183 mode
|= S_IRGRP
| S_IROTH
;
187 HANDLE_EINTR(open(name
.value().c_str(), open_flags
, mode
));
189 if (flags
& PLATFORM_FILE_OPEN_ALWAYS
) {
190 if (descriptor
< 0) {
191 open_flags
|= O_CREAT
;
192 if (flags
& PLATFORM_FILE_EXCLUSIVE_READ
||
193 flags
& PLATFORM_FILE_EXCLUSIVE_WRITE
) {
194 open_flags
|= O_EXCL
; // together with O_CREAT implies O_NOFOLLOW
196 descriptor
= HANDLE_EINTR(
197 open(name
.value().c_str(), open_flags
, mode
));
198 if (created
&& descriptor
>= 0)
203 if (created
&& (descriptor
>= 0) &&
204 (flags
& (PLATFORM_FILE_CREATE_ALWAYS
| PLATFORM_FILE_CREATE
)))
207 if ((descriptor
>= 0) && (flags
& PLATFORM_FILE_DELETE_ON_CLOSE
)) {
208 unlink(name
.value().c_str());
213 *error
= PLATFORM_FILE_OK
;
215 *error
= ErrnoToPlatformFileError(errno
);
221 FILE* FdopenPlatformFile(PlatformFile file
, const char* mode
) {
222 return fdopen(file
, mode
);
224 #endif // !defined(OS_NACL)
226 bool ClosePlatformFile(PlatformFile file
) {
227 base::ThreadRestrictions::AssertIOAllowed();
228 return !HANDLE_EINTR(close(file
));
231 int64
SeekPlatformFile(PlatformFile file
,
232 PlatformFileWhence whence
,
234 base::ThreadRestrictions::AssertIOAllowed();
235 if (file
< 0 || offset
< 0)
238 return lseek(file
, static_cast<off_t
>(offset
), static_cast<int>(whence
));
241 int ReadPlatformFile(PlatformFile file
, int64 offset
, char* data
, int size
) {
242 base::ThreadRestrictions::AssertIOAllowed();
243 if (file
< 0 || size
< 0)
249 rv
= DoPread(file
, data
+ bytes_read
,
250 size
- bytes_read
, offset
+ bytes_read
);
255 } while (bytes_read
< size
);
257 return bytes_read
? bytes_read
: rv
;
260 int ReadPlatformFileAtCurrentPos(PlatformFile file
, char* data
, int size
) {
261 base::ThreadRestrictions::AssertIOAllowed();
262 if (file
< 0 || size
< 0)
268 rv
= HANDLE_EINTR(read(file
, data
, size
));
273 } while (bytes_read
< size
);
275 return bytes_read
? bytes_read
: rv
;
278 int ReadPlatformFileNoBestEffort(PlatformFile file
, int64 offset
,
279 char* data
, int size
) {
280 base::ThreadRestrictions::AssertIOAllowed();
284 return DoPread(file
, data
, size
, offset
);
287 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file
,
288 char* data
, int size
) {
289 base::ThreadRestrictions::AssertIOAllowed();
290 if (file
< 0 || size
< 0)
293 return HANDLE_EINTR(read(file
, data
, size
));
296 int WritePlatformFile(PlatformFile file
, int64 offset
,
297 const char* data
, int size
) {
298 base::ThreadRestrictions::AssertIOAllowed();
300 if (IsOpenAppend(file
))
301 return WritePlatformFileAtCurrentPos(file
, data
, size
);
303 if (file
< 0 || size
< 0)
306 int bytes_written
= 0;
309 rv
= DoPwrite(file
, data
+ bytes_written
,
310 size
- bytes_written
, offset
+ bytes_written
);
315 } while (bytes_written
< size
);
317 return bytes_written
? bytes_written
: rv
;
320 int WritePlatformFileAtCurrentPos(PlatformFile file
,
321 const char* data
, int size
) {
322 base::ThreadRestrictions::AssertIOAllowed();
323 if (file
< 0 || size
< 0)
326 int bytes_written
= 0;
329 rv
= HANDLE_EINTR(write(file
, data
, size
));
334 } while (bytes_written
< size
);
336 return bytes_written
? bytes_written
: rv
;
339 int WritePlatformFileCurPosNoBestEffort(PlatformFile file
,
340 const char* data
, int size
) {
341 base::ThreadRestrictions::AssertIOAllowed();
342 if (file
< 0 || size
< 0)
345 return HANDLE_EINTR(write(file
, data
, size
));
348 bool TruncatePlatformFile(PlatformFile file
, int64 length
) {
349 base::ThreadRestrictions::AssertIOAllowed();
350 return ((file
>= 0) && !CallFtruncate(file
, length
));
353 bool FlushPlatformFile(PlatformFile file
) {
354 base::ThreadRestrictions::AssertIOAllowed();
355 return !CallFsync(file
);
358 bool TouchPlatformFile(PlatformFile file
, const base::Time
& last_access_time
,
359 const base::Time
& last_modified_time
) {
360 base::ThreadRestrictions::AssertIOAllowed();
365 times
[0] = last_access_time
.ToTimeVal();
366 times
[1] = last_modified_time
.ToTimeVal();
368 return !CallFutimes(file
, times
);
371 bool GetPlatformFileInfo(PlatformFile file
, PlatformFileInfo
* info
) {
375 stat_wrapper_t file_info
;
376 if (CallFstat(file
, &file_info
))
379 info
->is_directory
= S_ISDIR(file_info
.st_mode
);
380 info
->is_symbolic_link
= S_ISLNK(file_info
.st_mode
);
381 info
->size
= file_info
.st_size
;
382 info
->last_modified
= base::Time::FromTimeT(file_info
.st_mtime
);
383 info
->last_accessed
= base::Time::FromTimeT(file_info
.st_atime
);
384 info
->creation_time
= base::Time::FromTimeT(file_info
.st_ctime
);
388 PlatformFileError
ErrnoToPlatformFileError(int saved_errno
) {
389 switch (saved_errno
) {
394 return PLATFORM_FILE_ERROR_ACCESS_DENIED
;
395 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
397 return PLATFORM_FILE_ERROR_IN_USE
;
400 return PLATFORM_FILE_ERROR_EXISTS
;
402 return PLATFORM_FILE_ERROR_NOT_FOUND
;
404 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED
;
406 return PLATFORM_FILE_ERROR_NO_MEMORY
;
408 return PLATFORM_FILE_ERROR_NO_SPACE
;
410 return PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
;
412 #if !defined(OS_NACL) // NaCl build has no metrics code.
413 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
416 return PLATFORM_FILE_ERROR_FAILED
;