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/posix/eintr_wrapper.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "util/build_config.h"
17 
18 namespace base {
19 
20 // Make sure our Whence mappings match the system headers.
21 static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
22                   File::FROM_END == SEEK_END,
23               "whence mapping must match the system headers");
24 
25 namespace {
26 
27 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
28     defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ANDROID) && __ANDROID_API__ < 21
CallFstat(int fd,stat_wrapper_t * sb)29 int CallFstat(int fd, stat_wrapper_t* sb) {
30   return fstat(fd, sb);
31 }
32 #else
33 int CallFstat(int fd, stat_wrapper_t* sb) {
34   return fstat64(fd, sb);
35 }
36 #endif
37 
38 // Some systems don't provide the following system calls, so either simulate
39 // them or wrap them in order to minimize the number of #ifdef's in this file.
40 #if !defined(OS_AIX)
IsOpenAppend(PlatformFile file)41 bool IsOpenAppend(PlatformFile file) {
42   return (fcntl(file, F_GETFL) & O_APPEND) != 0;
43 }
44 
CallFtruncate(PlatformFile file,int64_t length)45 int CallFtruncate(PlatformFile file, int64_t length) {
46   return HANDLE_EINTR(ftruncate(file, length));
47 }
48 
49 #if !defined(OS_FUCHSIA)
CallFcntlFlock(PlatformFile file,bool do_lock)50 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
51   struct flock lock;
52   lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
53   lock.l_whence = SEEK_SET;
54   lock.l_start = 0;
55   lock.l_len = 0;  // Lock entire file.
56   if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
57     return File::GetLastFileError();
58   return File::FILE_OK;
59 }
60 #endif
61 
62 #else   // !defined(OS_AIX)
63 
IsOpenAppend(PlatformFile file)64 bool IsOpenAppend(PlatformFile file) {
65   // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
66   // standard and always appends if the file is opened with O_APPEND, just
67   // return false here.
68   return false;
69 }
70 
CallFtruncate(PlatformFile file,int64_t length)71 int CallFtruncate(PlatformFile file, int64_t length) {
72   NOTIMPLEMENTED();  // NaCl doesn't implement ftruncate.
73   return 0;
74 }
75 
CallFcntlFlock(PlatformFile file,bool do_lock)76 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
77   NOTIMPLEMENTED();  // NaCl doesn't implement flock struct.
78   return File::FILE_ERROR_INVALID_OPERATION;
79 }
80 #endif  // defined(OS_AIX)
81 
82 }  // namespace
83 
FromStat(const stat_wrapper_t & stat_info)84 void File::Info::FromStat(const stat_wrapper_t& stat_info) {
85   is_directory = S_ISDIR(stat_info.st_mode);
86   is_symbolic_link = S_ISLNK(stat_info.st_mode);
87   size = stat_info.st_size;
88 
89 #if defined(OS_MACOSX)
90   time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
91   int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
92   time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
93   int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
94   time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
95   int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
96 #elif defined(OS_AIX)
97   time_t last_modified_sec = stat_info.st_mtime;
98   int64_t last_modified_nsec = 0;
99   time_t last_accessed_sec = stat_info.st_atime;
100   int64_t last_accessed_nsec = 0;
101   time_t creation_time_sec = stat_info.st_ctime;
102   int64_t creation_time_nsec = 0;
103 #elif defined(OS_POSIX)
104   time_t last_modified_sec = stat_info.st_mtim.tv_sec;
105   int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
106   time_t last_accessed_sec = stat_info.st_atim.tv_sec;
107   int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
108   time_t creation_time_sec = stat_info.st_ctim.tv_sec;
109   int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
110 #else
111 #error
112 #endif
113 
114   constexpr uint64_t kNano = 1'000'000'000;
115   last_modified = last_modified_sec * kNano + last_modified_nsec;
116   last_accessed = last_accessed_sec * kNano + last_accessed_nsec;
117   creation_time = creation_time_sec * kNano + creation_time_nsec;
118 }
119 
IsValid() const120 bool File::IsValid() const {
121   return file_.is_valid();
122 }
123 
GetPlatformFile() const124 PlatformFile File::GetPlatformFile() const {
125   return file_.get();
126 }
127 
TakePlatformFile()128 PlatformFile File::TakePlatformFile() {
129   return file_.release();
130 }
131 
Close()132 void File::Close() {
133   if (!IsValid())
134     return;
135 
136   file_.reset();
137 }
138 
Seek(Whence whence,int64_t offset)139 int64_t File::Seek(Whence whence, int64_t offset) {
140   DCHECK(IsValid());
141 
142   static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
143   return lseek(file_.get(), static_cast<off_t>(offset),
144                static_cast<int>(whence));
145 }
146 
Read(int64_t offset,char * data,int size)147 int File::Read(int64_t offset, char* data, int size) {
148   DCHECK(IsValid());
149   if (size < 0)
150     return -1;
151 
152   int bytes_read = 0;
153   int rv;
154   do {
155     rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, size - bytes_read,
156                             offset + bytes_read));
157     if (rv <= 0)
158       break;
159 
160     bytes_read += rv;
161   } while (bytes_read < size);
162 
163   return bytes_read ? bytes_read : rv;
164 }
165 
ReadAtCurrentPos(char * data,int size)166 int File::ReadAtCurrentPos(char* data, int size) {
167   DCHECK(IsValid());
168   if (size < 0)
169     return -1;
170 
171   int bytes_read = 0;
172   int rv;
173   do {
174     rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
175     if (rv <= 0)
176       break;
177 
178     bytes_read += rv;
179   } while (bytes_read < size);
180 
181   return bytes_read ? bytes_read : rv;
182 }
183 
ReadNoBestEffort(int64_t offset,char * data,int size)184 int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
185   DCHECK(IsValid());
186   return HANDLE_EINTR(pread(file_.get(), data, size, offset));
187 }
188 
ReadAtCurrentPosNoBestEffort(char * data,int size)189 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
190   DCHECK(IsValid());
191   if (size < 0)
192     return -1;
193 
194   return HANDLE_EINTR(read(file_.get(), data, size));
195 }
196 
Write(int64_t offset,const char * data,int size)197 int File::Write(int64_t offset, const char* data, int size) {
198   if (IsOpenAppend(file_.get()))
199     return WriteAtCurrentPos(data, size);
200 
201   DCHECK(IsValid());
202   if (size < 0)
203     return -1;
204 
205   int bytes_written = 0;
206   int rv;
207   do {
208     rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
209                              size - bytes_written, offset + bytes_written));
210     if (rv <= 0)
211       break;
212 
213     bytes_written += rv;
214   } while (bytes_written < size);
215 
216   return bytes_written ? bytes_written : rv;
217 }
218 
WriteAtCurrentPos(const char * data,int size)219 int File::WriteAtCurrentPos(const char* data, int size) {
220   DCHECK(IsValid());
221   if (size < 0)
222     return -1;
223 
224   int bytes_written = 0;
225   int rv;
226   do {
227     rv = HANDLE_EINTR(
228         write(file_.get(), data + bytes_written, size - bytes_written));
229     if (rv <= 0)
230       break;
231 
232     bytes_written += rv;
233   } while (bytes_written < size);
234 
235   return bytes_written ? bytes_written : rv;
236 }
237 
WriteAtCurrentPosNoBestEffort(const char * data,int size)238 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
239   DCHECK(IsValid());
240   if (size < 0)
241     return -1;
242 
243   return HANDLE_EINTR(write(file_.get(), data, size));
244 }
245 
GetLength()246 int64_t File::GetLength() {
247   DCHECK(IsValid());
248 
249   stat_wrapper_t file_info;
250   if (CallFstat(file_.get(), &file_info))
251     return -1;
252 
253   return file_info.st_size;
254 }
255 
SetLength(int64_t length)256 bool File::SetLength(int64_t length) {
257   DCHECK(IsValid());
258 
259   return !CallFtruncate(file_.get(), length);
260 }
261 
GetInfo(Info * info)262 bool File::GetInfo(Info* info) {
263   DCHECK(IsValid());
264 
265   stat_wrapper_t file_info;
266   if (CallFstat(file_.get(), &file_info))
267     return false;
268 
269   info->FromStat(file_info);
270   return true;
271 }
272 
273 #if !defined(OS_FUCHSIA)
Lock()274 File::Error File::Lock() {
275   return CallFcntlFlock(file_.get(), true);
276 }
277 
Unlock()278 File::Error File::Unlock() {
279   return CallFcntlFlock(file_.get(), false);
280 }
281 #endif
282 
Duplicate() const283 File File::Duplicate() const {
284   if (!IsValid())
285     return File();
286 
287   PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile()));
288   if (other_fd == -1)
289     return File(File::GetLastFileError());
290 
291   File other(other_fd);
292   return other;
293 }
294 
295 // Static.
OSErrorToFileError(int saved_errno)296 File::Error File::OSErrorToFileError(int saved_errno) {
297   switch (saved_errno) {
298     case EACCES:
299     case EISDIR:
300     case EROFS:
301     case EPERM:
302       return FILE_ERROR_ACCESS_DENIED;
303     case EBUSY:
304     case ETXTBSY:
305       return FILE_ERROR_IN_USE;
306     case EEXIST:
307       return FILE_ERROR_EXISTS;
308     case EIO:
309       return FILE_ERROR_IO;
310     case ENOENT:
311       return FILE_ERROR_NOT_FOUND;
312     case ENFILE:  // fallthrough
313     case EMFILE:
314       return FILE_ERROR_TOO_MANY_OPENED;
315     case ENOMEM:
316       return FILE_ERROR_NO_MEMORY;
317     case ENOSPC:
318       return FILE_ERROR_NO_SPACE;
319     case ENOTDIR:
320       return FILE_ERROR_NOT_A_DIRECTORY;
321     default:
322       // This function should only be called for errors.
323       DCHECK_NE(0, saved_errno);
324       return FILE_ERROR_FAILED;
325   }
326 }
327 
DoInitialize(const FilePath & path,uint32_t flags)328 void File::DoInitialize(const FilePath& path, uint32_t flags) {
329   DCHECK(!IsValid());
330 
331   int open_flags = 0;
332   created_ = false;
333 
334   if (flags & FLAG_CREATE_ALWAYS) {
335     DCHECK(!open_flags);
336     DCHECK(flags & FLAG_WRITE);
337     open_flags = O_CREAT | O_TRUNC;
338   }
339 
340   if (!open_flags && !(flags & FLAG_OPEN)) {
341     NOTREACHED();
342     errno = EOPNOTSUPP;
343     error_details_ = FILE_ERROR_FAILED;
344     return;
345   }
346 
347   if (flags & FLAG_WRITE && flags & FLAG_READ) {
348     open_flags |= O_RDWR;
349   } else if (flags & FLAG_WRITE) {
350     open_flags |= O_WRONLY;
351   } else if (!(flags & FLAG_READ)) {
352     NOTREACHED();
353   }
354 
355   static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
356 
357   int mode = S_IRUSR | S_IWUSR;
358   int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
359 
360   if (descriptor < 0) {
361     error_details_ = File::GetLastFileError();
362     return;
363   }
364 
365   if (flags & FLAG_CREATE_ALWAYS)
366     created_ = true;
367 
368   error_details_ = FILE_OK;
369   file_.reset(descriptor);
370 }
371 
Flush()372 bool File::Flush() {
373   DCHECK(IsValid());
374 
375 #if defined(OS_LINUX) || defined(OS_BSD) && !defined(OS_DRAGONFLY)
376   return !HANDLE_EINTR(fdatasync(file_.get()));
377 #else
378   return !HANDLE_EINTR(fsync(file_.get()));
379 #endif
380 }
381 
SetPlatformFile(PlatformFile file)382 void File::SetPlatformFile(PlatformFile file) {
383   DCHECK(!file_.is_valid());
384   file_.reset(file);
385 }
386 
387 // static
GetLastFileError()388 File::Error File::GetLastFileError() {
389   return base::File::OSErrorToFileError(errno);
390 }
391 
392 }  // namespace base
393