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 24namespace base { 25 26// Make sure our Whence mappings match the system headers. 27static_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 31namespace { 32 33#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) 34int CallFstat(int fd, stat_wrapper_t *sb) { 35 ThreadRestrictions::AssertIOAllowed(); 36 return fstat(fd, sb); 37} 38#else 39int 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) 48bool IsOpenAppend(PlatformFile file) { 49 return (fcntl(file, F_GETFL) & O_APPEND) != 0; 50} 51 52int CallFtruncate(PlatformFile file, int64_t length) { 53 return HANDLE_EINTR(ftruncate(file, length)); 54} 55 56int 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 73File::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 85bool 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 92int CallFtruncate(PlatformFile file, int64_t length) { 93 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate. 94 return 0; 95} 96 97int CallFutimes(PlatformFile file, const struct timeval times[2]) { 98 NOTIMPLEMENTED(); // NaCl doesn't implement futimes. 99 return 0; 100} 101 102File::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 110void 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 161bool File::IsValid() const { 162 return file_.is_valid(); 163} 164 165PlatformFile File::GetPlatformFile() const { 166 return file_.get(); 167} 168 169PlatformFile File::TakePlatformFile() { 170 return file_.release(); 171} 172 173void File::Close() { 174 if (!IsValid()) 175 return; 176 177 SCOPED_FILE_TRACE("Close"); 178 ThreadRestrictions::AssertIOAllowed(); 179 file_.reset(); 180} 181 182int64_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 201int 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 223int 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 244int 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 251int 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 261int 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 287int 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 309int 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 319int64_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 331bool 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 339bool 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 352bool 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 365File::Error File::Lock() { 366 SCOPED_FILE_TRACE("Lock"); 367 return CallFcntlFlock(file_.get(), true); 368} 369 370File::Error File::Unlock() { 371 SCOPED_FILE_TRACE("Unlock"); 372 return CallFcntlFlock(file_.get(), false); 373} 374 375File 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. 392File::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? 430void 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 516bool 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 530void File::SetPlatformFile(PlatformFile file) { 531 DCHECK(!file_.is_valid()); 532 file_.reset(file); 533} 534 535} // namespace base 536