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