[sql] Integrate SQLite recover.c module.
[chromium-blink-merge.git] / base / platform_file_posix.cc
blobe1bab1e34c997ad03cb64f3cb82b38c47f47ef2a
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"
7 #include <fcntl.h>
8 #include <errno.h>
9 #include <sys/stat.h>
10 #include <unistd.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"
21 #endif
23 namespace base {
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);
30 namespace {
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();
36 return fstat(fd, sb);
38 #else
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);
44 #endif
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.
48 #if !defined(OS_NACL)
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,
54 int64 offset) {
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]) {
71 #ifdef __USE_XOPEN2K8
72 // futimens should be available, but futimes might not be
73 // http://pubs.opengroup.org/onlinepubs/9699919799/
75 timespec ts_times[2];
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);
82 #else
83 return futimes(file, times);
84 #endif
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,
94 int64 offset) {
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.
103 return false;
106 static int CallFtruncate(PlatformFile file, int64 length) {
107 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
108 return 0;
111 static int CallFsync(PlatformFile file) {
112 NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
113 return 0;
116 static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
117 NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
118 return 0;
120 #endif // defined(OS_NACL)
122 } // namespace
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,
128 int flags,
129 bool* created,
130 PlatformFileError* error) {
131 base::ThreadRestrictions::AssertIOAllowed();
133 int open_flags = 0;
134 if (flags & PLATFORM_FILE_CREATE)
135 open_flags = O_CREAT | O_EXCL;
137 if (created)
138 *created = false;
140 if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
141 DCHECK(!open_flags);
142 open_flags = O_CREAT | O_TRUNC;
145 if (flags & PLATFORM_FILE_OPEN_TRUNCATED) {
146 DCHECK(!open_flags);
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)) {
153 NOTREACHED();
154 errno = EOPNOTSUPP;
155 if (error)
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)) {
168 NOTREACHED();
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;
184 #endif
186 int descriptor =
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)
199 *created = true;
203 if (created && (descriptor >= 0) &&
204 (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE)))
205 *created = true;
207 if ((descriptor >= 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) {
208 unlink(name.value().c_str());
211 if (error) {
212 if (descriptor >= 0)
213 *error = PLATFORM_FILE_OK;
214 else
215 *error = ErrnoToPlatformFileError(errno);
218 return descriptor;
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,
233 int64 offset) {
234 base::ThreadRestrictions::AssertIOAllowed();
235 if (file < 0 || offset < 0)
236 return -1;
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)
244 return -1;
246 int bytes_read = 0;
247 int rv;
248 do {
249 rv = DoPread(file, data + bytes_read,
250 size - bytes_read, offset + bytes_read);
251 if (rv <= 0)
252 break;
254 bytes_read += rv;
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)
263 return -1;
265 int bytes_read = 0;
266 int rv;
267 do {
268 rv = HANDLE_EINTR(read(file, data, size));
269 if (rv <= 0)
270 break;
272 bytes_read += rv;
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();
281 if (file < 0)
282 return -1;
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)
291 return -1;
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)
304 return -1;
306 int bytes_written = 0;
307 int rv;
308 do {
309 rv = DoPwrite(file, data + bytes_written,
310 size - bytes_written, offset + bytes_written);
311 if (rv <= 0)
312 break;
314 bytes_written += rv;
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)
324 return -1;
326 int bytes_written = 0;
327 int rv;
328 do {
329 rv = HANDLE_EINTR(write(file, data, size));
330 if (rv <= 0)
331 break;
333 bytes_written += rv;
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)
343 return -1;
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();
361 if (file < 0)
362 return false;
364 timeval times[2];
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) {
372 if (!info)
373 return false;
375 stat_wrapper_t file_info;
376 if (CallFstat(file, &file_info))
377 return false;
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);
385 return true;
388 PlatformFileError ErrnoToPlatformFileError(int saved_errno) {
389 switch (saved_errno) {
390 case EACCES:
391 case EISDIR:
392 case EROFS:
393 case EPERM:
394 return PLATFORM_FILE_ERROR_ACCESS_DENIED;
395 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
396 case ETXTBSY:
397 return PLATFORM_FILE_ERROR_IN_USE;
398 #endif
399 case EEXIST:
400 return PLATFORM_FILE_ERROR_EXISTS;
401 case ENOENT:
402 return PLATFORM_FILE_ERROR_NOT_FOUND;
403 case EMFILE:
404 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
405 case ENOMEM:
406 return PLATFORM_FILE_ERROR_NO_MEMORY;
407 case ENOSPC:
408 return PLATFORM_FILE_ERROR_NO_SPACE;
409 case ENOTDIR:
410 return PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
411 default:
412 #if !defined(OS_NACL) // NaCl build has no metrics code.
413 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
414 saved_errno);
415 #endif
416 return PLATFORM_FILE_ERROR_FAILED;
420 } // namespace base