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 
24 namespace base {
25 
26 // Make sure our Whence mappings match the system headers.
27 static_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 
31 namespace {
32 
33 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
CallFstat(int fd,stat_wrapper_t * sb)34 int CallFstat(int fd, stat_wrapper_t *sb) {
35   ThreadRestrictions::AssertIOAllowed();
36   return fstat(fd, sb);
37 }
38 #else
39 int 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)
IsOpenAppend(PlatformFile file)48 bool IsOpenAppend(PlatformFile file) {
49   return (fcntl(file, F_GETFL) & O_APPEND) != 0;
50 }
51 
CallFtruncate(PlatformFile file,int64_t length)52 int CallFtruncate(PlatformFile file, int64_t length) {
53   return HANDLE_EINTR(ftruncate(file, length));
54 }
55 
CallFutimes(PlatformFile file,const struct timeval times[2])56 int 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 
CallFcntlFlock(PlatformFile file,bool do_lock)73 File::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 
IsOpenAppend(PlatformFile file)85 bool 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 
CallFtruncate(PlatformFile file,int64_t length)92 int CallFtruncate(PlatformFile file, int64_t length) {
93   NOTIMPLEMENTED();  // NaCl doesn't implement ftruncate.
94   return 0;
95 }
96 
CallFutimes(PlatformFile file,const struct timeval times[2])97 int CallFutimes(PlatformFile file, const struct timeval times[2]) {
98   NOTIMPLEMENTED();  // NaCl doesn't implement futimes.
99   return 0;
100 }
101 
CallFcntlFlock(PlatformFile file,bool do_lock)102 File::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 
FromStat(const stat_wrapper_t & stat_info)110 void 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 
IsValid() const161 bool File::IsValid() const {
162   return file_.is_valid();
163 }
164 
GetPlatformFile() const165 PlatformFile File::GetPlatformFile() const {
166   return file_.get();
167 }
168 
TakePlatformFile()169 PlatformFile File::TakePlatformFile() {
170   return file_.release();
171 }
172 
Close()173 void File::Close() {
174   if (!IsValid())
175     return;
176 
177   SCOPED_FILE_TRACE("Close");
178   ThreadRestrictions::AssertIOAllowed();
179   file_.reset();
180 }
181 
Seek(Whence whence,int64_t offset)182 int64_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 
Read(int64_t offset,char * data,int size)201 int 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 
ReadAtCurrentPos(char * data,int size)223 int 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 
ReadNoBestEffort(int64_t offset,char * data,int size)244 int 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 
ReadAtCurrentPosNoBestEffort(char * data,int size)251 int 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 
Write(int64_t offset,const char * data,int size)261 int 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 
WriteAtCurrentPos(const char * data,int size)287 int 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 
WriteAtCurrentPosNoBestEffort(const char * data,int size)309 int 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 
GetLength()319 int64_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 
SetLength(int64_t length)331 bool 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 
SetTimes(Time last_access_time,Time last_modified_time)339 bool 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 
GetInfo(Info * info)352 bool 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 
Lock()365 File::Error File::Lock() {
366   SCOPED_FILE_TRACE("Lock");
367   return CallFcntlFlock(file_.get(), true);
368 }
369 
Unlock()370 File::Error File::Unlock() {
371   SCOPED_FILE_TRACE("Unlock");
372   return CallFcntlFlock(file_.get(), false);
373 }
374 
Duplicate()375 File 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.
OSErrorToFileError(int saved_errno)392 File::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?
DoInitialize(const FilePath & path,uint32_t flags)430 void 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 
DoFlush()516 bool 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 
SetPlatformFile(PlatformFile file)530 void File::SetPlatformFile(PlatformFile file) {
531   DCHECK(!file_.is_valid());
532   file_.reset(file);
533 }
534 
535 }  // namespace base
536