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) 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) 49 bool IsOpenAppend(PlatformFile file) { 50 return (fcntl(file, F_GETFL) & O_APPEND) != 0; 51 } 52 53 int CallFtruncate(PlatformFile file, int64_t length) { 54 return HANDLE_EINTR(ftruncate(file, length)); 55 } 56 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) 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 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 96 int CallFtruncate(PlatformFile file, int64_t length) { 97 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate. 98 return 0; 99 } 100 101 int CallFutimes(PlatformFile file, const struct timeval times[2]) { 102 NOTIMPLEMENTED(); // NaCl doesn't implement futimes. 103 return 0; 104 } 105 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 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 165 bool File::IsValid() const { 166 return file_.is_valid(); 167 } 168 169 PlatformFile File::GetPlatformFile() const { 170 return file_.get(); 171 } 172 173 PlatformFile File::TakePlatformFile() { 174 return file_.release(); 175 } 176 177 void File::Close() { 178 if (!IsValid()) 179 return; 180 181 SCOPED_FILE_TRACE("Close"); 182 AssertBlockingAllowed(); 183 file_.reset(); 184 } 185 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 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 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 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 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 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 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 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 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 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 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 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) 379 File::Error File::Lock() { 380 SCOPED_FILE_TRACE("Lock"); 381 return CallFcntlFlock(file_.get(), true); 382 } 383 384 File::Error File::Unlock() { 385 SCOPED_FILE_TRACE("Unlock"); 386 return CallFcntlFlock(file_.get(), false); 387 } 388 #endif 389 390 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. 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? 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 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 545 void File::SetPlatformFile(PlatformFile file) { 546 DCHECK(!file_.is_valid()); 547 file_.reset(file); 548 } 549 550 // static 551 File::Error File::GetLastFileError() { 552 return base::File::OSErrorToFileError(errno); 553 } 554 555 } // namespace base 556