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