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