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.
4
5#include "base/files/file.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <stdint.h>
10#include <sys/stat.h>
11#include <unistd.h>
12
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"
18#include "build/build_config.h"
19
20#if defined(OS_ANDROID)
21#include "base/os_compat_android.h"
22#endif
23
24namespace base {
25
26// Make sure our Whence mappings match the system headers.
27static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
28                  File::FROM_END == SEEK_END,
29              "whence mapping must match the system headers");
30
31namespace {
32
33#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
34int CallFstat(int fd, stat_wrapper_t *sb) {
35  ThreadRestrictions::AssertIOAllowed();
36  return fstat(fd, sb);
37}
38#else
39int CallFstat(int fd, stat_wrapper_t *sb) {
40  ThreadRestrictions::AssertIOAllowed();
41  return fstat64(fd, sb);
42}
43#endif
44
45// NaCl doesn't provide the following system calls, so either simulate them or
46// wrap them in order to minimize the number of #ifdef's in this file.
47#if !defined(OS_NACL)
48bool IsOpenAppend(PlatformFile file) {
49  return (fcntl(file, F_GETFL) & O_APPEND) != 0;
50}
51
52int CallFtruncate(PlatformFile file, int64_t length) {
53  return HANDLE_EINTR(ftruncate(file, length));
54}
55
56int CallFutimes(PlatformFile file, const struct timeval times[2]) {
57#ifdef __USE_XOPEN2K8
58  // futimens should be available, but futimes might not be
59  // http://pubs.opengroup.org/onlinepubs/9699919799/
60
61  timespec ts_times[2];
62  ts_times[0].tv_sec  = times[0].tv_sec;
63  ts_times[0].tv_nsec = times[0].tv_usec * 1000;
64  ts_times[1].tv_sec  = times[1].tv_sec;
65  ts_times[1].tv_nsec = times[1].tv_usec * 1000;
66
67  return futimens(file, ts_times);
68#else
69  return futimes(file, times);
70#endif
71}
72
73File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
74  struct flock lock;
75  lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
76  lock.l_whence = SEEK_SET;
77  lock.l_start = 0;
78  lock.l_len = 0;  // Lock entire file.
79  if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
80    return File::OSErrorToFileError(errno);
81  return File::FILE_OK;
82}
83#else  // defined(OS_NACL)
84
85bool IsOpenAppend(PlatformFile file) {
86  // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
87  // standard and always appends if the file is opened with O_APPEND, just
88  // return false here.
89  return false;
90}
91
92int CallFtruncate(PlatformFile file, int64_t length) {
93  NOTIMPLEMENTED();  // NaCl doesn't implement ftruncate.
94  return 0;
95}
96
97int CallFutimes(PlatformFile file, const struct timeval times[2]) {
98  NOTIMPLEMENTED();  // NaCl doesn't implement futimes.
99  return 0;
100}
101
102File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
103  NOTIMPLEMENTED();  // NaCl doesn't implement flock struct.
104  return File::FILE_ERROR_INVALID_OPERATION;
105}
106#endif  // defined(OS_NACL)
107
108}  // namespace
109
110void File::Info::FromStat(const stat_wrapper_t& stat_info) {
111  is_directory = S_ISDIR(stat_info.st_mode);
112  is_symbolic_link = S_ISLNK(stat_info.st_mode);
113  size = stat_info.st_size;
114
115#if defined(OS_LINUX)
116  time_t last_modified_sec = stat_info.st_mtim.tv_sec;
117  int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
118  time_t last_accessed_sec = stat_info.st_atim.tv_sec;
119  int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
120  time_t creation_time_sec = stat_info.st_ctim.tv_sec;
121  int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
122#elif defined(OS_ANDROID)
123  time_t last_modified_sec = stat_info.st_mtime;
124  int64_t last_modified_nsec = stat_info.st_mtime_nsec;
125  time_t last_accessed_sec = stat_info.st_atime;
126  int64_t last_accessed_nsec = stat_info.st_atime_nsec;
127  time_t creation_time_sec = stat_info.st_ctime;
128  int64_t creation_time_nsec = stat_info.st_ctime_nsec;
129#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
130  time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
131  int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
132  time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
133  int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
134  time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
135  int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
136#else
137  time_t last_modified_sec = stat_info.st_mtime;
138  int64_t last_modified_nsec = 0;
139  time_t last_accessed_sec = stat_info.st_atime;
140  int64_t last_accessed_nsec = 0;
141  time_t creation_time_sec = stat_info.st_ctime;
142  int64_t creation_time_nsec = 0;
143#endif
144
145  last_modified =
146      Time::FromTimeT(last_modified_sec) +
147      TimeDelta::FromMicroseconds(last_modified_nsec /
148                                  Time::kNanosecondsPerMicrosecond);
149
150  last_accessed =
151      Time::FromTimeT(last_accessed_sec) +
152      TimeDelta::FromMicroseconds(last_accessed_nsec /
153                                  Time::kNanosecondsPerMicrosecond);
154
155  creation_time =
156      Time::FromTimeT(creation_time_sec) +
157      TimeDelta::FromMicroseconds(creation_time_nsec /
158                                  Time::kNanosecondsPerMicrosecond);
159}
160
161bool File::IsValid() const {
162  return file_.is_valid();
163}
164
165PlatformFile File::GetPlatformFile() const {
166  return file_.get();
167}
168
169PlatformFile File::TakePlatformFile() {
170  return file_.release();
171}
172
173void File::Close() {
174  if (!IsValid())
175    return;
176
177  SCOPED_FILE_TRACE("Close");
178  ThreadRestrictions::AssertIOAllowed();
179  file_.reset();
180}
181
182int64_t File::Seek(Whence whence, int64_t offset) {
183  ThreadRestrictions::AssertIOAllowed();
184  DCHECK(IsValid());
185
186  SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset);
187
188// Additionally check __BIONIC__ since older versions of Android don't define
189// _FILE_OFFSET_BITS.
190#if _FILE_OFFSET_BITS != 64 || defined(__BIONIC__)
191  static_assert(sizeof(int64_t) == sizeof(off64_t), "off64_t must be 64 bits");
192  return lseek64(file_.get(), static_cast<off64_t>(offset),
193                 static_cast<int>(whence));
194#else
195  static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
196  return lseek(file_.get(), static_cast<off_t>(offset),
197               static_cast<int>(whence));
198#endif
199}
200
201int File::Read(int64_t offset, char* data, int size) {
202  ThreadRestrictions::AssertIOAllowed();
203  DCHECK(IsValid());
204  if (size < 0)
205    return -1;
206
207  SCOPED_FILE_TRACE_WITH_SIZE("Read", size);
208
209  int bytes_read = 0;
210  int rv;
211  do {
212    rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read,
213                            size - bytes_read, offset + bytes_read));
214    if (rv <= 0)
215      break;
216
217    bytes_read += rv;
218  } while (bytes_read < size);
219
220  return bytes_read ? bytes_read : rv;
221}
222
223int File::ReadAtCurrentPos(char* data, int size) {
224  ThreadRestrictions::AssertIOAllowed();
225  DCHECK(IsValid());
226  if (size < 0)
227    return -1;
228
229  SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size);
230
231  int bytes_read = 0;
232  int rv;
233  do {
234    rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
235    if (rv <= 0)
236      break;
237
238    bytes_read += rv;
239  } while (bytes_read < size);
240
241  return bytes_read ? bytes_read : rv;
242}
243
244int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
245  ThreadRestrictions::AssertIOAllowed();
246  DCHECK(IsValid());
247  SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size);
248  return HANDLE_EINTR(pread(file_.get(), data, size, offset));
249}
250
251int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
252  ThreadRestrictions::AssertIOAllowed();
253  DCHECK(IsValid());
254  if (size < 0)
255    return -1;
256
257  SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size);
258  return HANDLE_EINTR(read(file_.get(), data, size));
259}
260
261int File::Write(int64_t offset, const char* data, int size) {
262  ThreadRestrictions::AssertIOAllowed();
263
264  if (IsOpenAppend(file_.get()))
265    return WriteAtCurrentPos(data, size);
266
267  DCHECK(IsValid());
268  if (size < 0)
269    return -1;
270
271  SCOPED_FILE_TRACE_WITH_SIZE("Write", size);
272
273  int bytes_written = 0;
274  int rv;
275  do {
276    rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
277                             size - bytes_written, offset + bytes_written));
278    if (rv <= 0)
279      break;
280
281    bytes_written += rv;
282  } while (bytes_written < size);
283
284  return bytes_written ? bytes_written : rv;
285}
286
287int File::WriteAtCurrentPos(const char* data, int size) {
288  ThreadRestrictions::AssertIOAllowed();
289  DCHECK(IsValid());
290  if (size < 0)
291    return -1;
292
293  SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size);
294
295  int bytes_written = 0;
296  int rv;
297  do {
298    rv = HANDLE_EINTR(write(file_.get(), data + bytes_written,
299                            size - bytes_written));
300    if (rv <= 0)
301      break;
302
303    bytes_written += rv;
304  } while (bytes_written < size);
305
306  return bytes_written ? bytes_written : rv;
307}
308
309int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
310  ThreadRestrictions::AssertIOAllowed();
311  DCHECK(IsValid());
312  if (size < 0)
313    return -1;
314
315  SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size);
316  return HANDLE_EINTR(write(file_.get(), data, size));
317}
318
319int64_t File::GetLength() {
320  DCHECK(IsValid());
321
322  SCOPED_FILE_TRACE("GetLength");
323
324  stat_wrapper_t file_info;
325  if (CallFstat(file_.get(), &file_info))
326    return false;
327
328  return file_info.st_size;
329}
330
331bool File::SetLength(int64_t length) {
332  ThreadRestrictions::AssertIOAllowed();
333  DCHECK(IsValid());
334
335  SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length);
336  return !CallFtruncate(file_.get(), length);
337}
338
339bool File::SetTimes(Time last_access_time, Time last_modified_time) {
340  ThreadRestrictions::AssertIOAllowed();
341  DCHECK(IsValid());
342
343  SCOPED_FILE_TRACE("SetTimes");
344
345  timeval times[2];
346  times[0] = last_access_time.ToTimeVal();
347  times[1] = last_modified_time.ToTimeVal();
348
349  return !CallFutimes(file_.get(), times);
350}
351
352bool File::GetInfo(Info* info) {
353  DCHECK(IsValid());
354
355  SCOPED_FILE_TRACE("GetInfo");
356
357  stat_wrapper_t file_info;
358  if (CallFstat(file_.get(), &file_info))
359    return false;
360
361  info->FromStat(file_info);
362  return true;
363}
364
365File::Error File::Lock() {
366  SCOPED_FILE_TRACE("Lock");
367  return CallFcntlFlock(file_.get(), true);
368}
369
370File::Error File::Unlock() {
371  SCOPED_FILE_TRACE("Unlock");
372  return CallFcntlFlock(file_.get(), false);
373}
374
375File File::Duplicate() {
376  if (!IsValid())
377    return File();
378
379  SCOPED_FILE_TRACE("Duplicate");
380
381  PlatformFile other_fd = dup(GetPlatformFile());
382  if (other_fd == -1)
383    return File(OSErrorToFileError(errno));
384
385  File other(other_fd);
386  if (async())
387    other.async_ = true;
388  return other;
389}
390
391// Static.
392File::Error File::OSErrorToFileError(int saved_errno) {
393  switch (saved_errno) {
394    case EACCES:
395    case EISDIR:
396    case EROFS:
397    case EPERM:
398      return FILE_ERROR_ACCESS_DENIED;
399    case EBUSY:
400#if !defined(OS_NACL)  // ETXTBSY not defined by NaCl.
401    case ETXTBSY:
402#endif
403      return FILE_ERROR_IN_USE;
404    case EEXIST:
405      return FILE_ERROR_EXISTS;
406    case EIO:
407      return FILE_ERROR_IO;
408    case ENOENT:
409      return FILE_ERROR_NOT_FOUND;
410    case EMFILE:
411      return FILE_ERROR_TOO_MANY_OPENED;
412    case ENOMEM:
413      return FILE_ERROR_NO_MEMORY;
414    case ENOSPC:
415      return FILE_ERROR_NO_SPACE;
416    case ENOTDIR:
417      return FILE_ERROR_NOT_A_DIRECTORY;
418    default:
419#if !defined(OS_NACL)  // NaCl build has no metrics code.
420      UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
421                                  saved_errno);
422#endif
423      return FILE_ERROR_FAILED;
424  }
425}
426
427// NaCl doesn't implement system calls to open files directly.
428#if !defined(OS_NACL)
429// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
430void File::DoInitialize(const FilePath& path, uint32_t flags) {
431  ThreadRestrictions::AssertIOAllowed();
432  DCHECK(!IsValid());
433
434  int open_flags = 0;
435  if (flags & FLAG_CREATE)
436    open_flags = O_CREAT | O_EXCL;
437
438  created_ = false;
439
440  if (flags & FLAG_CREATE_ALWAYS) {
441    DCHECK(!open_flags);
442    DCHECK(flags & FLAG_WRITE);
443    open_flags = O_CREAT | O_TRUNC;
444  }
445
446  if (flags & FLAG_OPEN_TRUNCATED) {
447    DCHECK(!open_flags);
448    DCHECK(flags & FLAG_WRITE);
449    open_flags = O_TRUNC;
450  }
451
452  if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
453    NOTREACHED();
454    errno = EOPNOTSUPP;
455    error_details_ = FILE_ERROR_FAILED;
456    return;
457  }
458
459  if (flags & FLAG_WRITE && flags & FLAG_READ) {
460    open_flags |= O_RDWR;
461  } else if (flags & FLAG_WRITE) {
462    open_flags |= O_WRONLY;
463  } else if (!(flags & FLAG_READ) &&
464             !(flags & FLAG_WRITE_ATTRIBUTES) &&
465             !(flags & FLAG_APPEND) &&
466             !(flags & FLAG_OPEN_ALWAYS)) {
467    NOTREACHED();
468  }
469
470  if (flags & FLAG_TERMINAL_DEVICE)
471    open_flags |= O_NOCTTY | O_NDELAY;
472
473  if (flags & FLAG_APPEND && flags & FLAG_READ)
474    open_flags |= O_APPEND | O_RDWR;
475  else if (flags & FLAG_APPEND)
476    open_flags |= O_APPEND | O_WRONLY;
477
478  static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
479
480  int mode = S_IRUSR | S_IWUSR;
481#if defined(OS_CHROMEOS)
482  mode |= S_IRGRP | S_IROTH;
483#endif
484
485  int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
486
487  if (flags & FLAG_OPEN_ALWAYS) {
488    if (descriptor < 0) {
489      open_flags |= O_CREAT;
490      if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE)
491        open_flags |= O_EXCL;   // together with O_CREAT implies O_NOFOLLOW
492
493      descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
494      if (descriptor >= 0)
495        created_ = true;
496    }
497  }
498
499  if (descriptor < 0) {
500    error_details_ = File::OSErrorToFileError(errno);
501    return;
502  }
503
504  if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
505    created_ = true;
506
507  if (flags & FLAG_DELETE_ON_CLOSE)
508    unlink(path.value().c_str());
509
510  async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
511  error_details_ = FILE_OK;
512  file_.reset(descriptor);
513}
514#endif  // !defined(OS_NACL)
515
516bool File::DoFlush() {
517  ThreadRestrictions::AssertIOAllowed();
518  DCHECK(IsValid());
519
520#if defined(OS_NACL)
521  NOTIMPLEMENTED();  // NaCl doesn't implement fsync.
522  return true;
523#elif defined(OS_LINUX) || defined(OS_ANDROID)
524  return !HANDLE_EINTR(fdatasync(file_.get()));
525#else
526  return !HANDLE_EINTR(fsync(file_.get()));
527#endif
528}
529
530void File::SetPlatformFile(PlatformFile file) {
531  DCHECK(!file_.is_valid());
532  file_.reset(file);
533}
534
535}  // namespace base
536