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