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 <sys/stat.h> 10#include <unistd.h> 11 12#include "base/files/file_path.h" 13#include "base/logging.h" 14#include "base/metrics/sparse_histogram.h" 15// TODO(rvargas): remove this (needed for kInvalidPlatformFileValue). 16#include "base/platform_file.h" 17#include "base/posix/eintr_wrapper.h" 18#include "base/strings/utf_string_conversions.h" 19#include "base/threading/thread_restrictions.h" 20 21#if defined(OS_ANDROID) 22#include "base/os_compat_android.h" 23#endif 24 25namespace base { 26 27// Make sure our Whence mappings match the system headers. 28COMPILE_ASSERT(File::FROM_BEGIN == SEEK_SET && 29 File::FROM_CURRENT == SEEK_CUR && 30 File::FROM_END == SEEK_END, whence_matches_system); 31 32namespace { 33 34#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) 35typedef struct stat stat_wrapper_t; 36static int CallFstat(int fd, stat_wrapper_t *sb) { 37 base::ThreadRestrictions::AssertIOAllowed(); 38 return fstat(fd, sb); 39} 40#else 41typedef struct stat64 stat_wrapper_t; 42static int CallFstat(int fd, stat_wrapper_t *sb) { 43 base::ThreadRestrictions::AssertIOAllowed(); 44 return fstat64(fd, sb); 45} 46#endif 47 48// NaCl doesn't provide the following system calls, so either simulate them or 49// wrap them in order to minimize the number of #ifdef's in this file. 50#if !defined(OS_NACL) 51static bool IsOpenAppend(PlatformFile file) { 52 return (fcntl(file, F_GETFL) & O_APPEND) != 0; 53} 54 55static int CallFtruncate(PlatformFile file, int64 length) { 56 return HANDLE_EINTR(ftruncate(file, length)); 57} 58 59static int CallFsync(PlatformFile file) { 60 return HANDLE_EINTR(fsync(file)); 61} 62 63static int CallFutimes(PlatformFile file, const struct timeval times[2]) { 64#ifdef __USE_XOPEN2K8 65 // futimens should be available, but futimes might not be 66 // http://pubs.opengroup.org/onlinepubs/9699919799/ 67 68 timespec ts_times[2]; 69 ts_times[0].tv_sec = times[0].tv_sec; 70 ts_times[0].tv_nsec = times[0].tv_usec * 1000; 71 ts_times[1].tv_sec = times[1].tv_sec; 72 ts_times[1].tv_nsec = times[1].tv_usec * 1000; 73 74 return futimens(file, ts_times); 75#else 76 return futimes(file, times); 77#endif 78} 79 80static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) { 81 struct flock lock; 82 lock.l_type = F_WRLCK; 83 lock.l_whence = SEEK_SET; 84 lock.l_start = 0; 85 lock.l_len = 0; // Lock entire file. 86 if (HANDLE_EINTR(fcntl(file, do_lock ? F_SETLK : F_UNLCK, &lock)) == -1) 87 return File::OSErrorToFileError(errno); 88 return File::FILE_OK; 89} 90#else // defined(OS_NACL) 91 92static bool IsOpenAppend(PlatformFile file) { 93 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX 94 // standard and always appends if the file is opened with O_APPEND, just 95 // return false here. 96 return false; 97} 98 99static int CallFtruncate(PlatformFile file, int64 length) { 100 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate. 101 return 0; 102} 103 104static int CallFsync(PlatformFile file) { 105 NOTIMPLEMENTED(); // NaCl doesn't implement fsync. 106 return 0; 107} 108 109static int CallFutimes(PlatformFile file, const struct timeval times[2]) { 110 NOTIMPLEMENTED(); // NaCl doesn't implement futimes. 111 return 0; 112} 113 114static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) { 115 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct. 116 return File::FILE_ERROR_INVALID_OPERATION; 117} 118#endif // defined(OS_NACL) 119 120} // namespace 121 122// NaCl doesn't implement system calls to open files directly. 123#if !defined(OS_NACL) 124// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here? 125void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { 126 base::ThreadRestrictions::AssertIOAllowed(); 127 DCHECK(!IsValid()); 128 DCHECK(!(flags & FLAG_ASYNC)); 129 130 int open_flags = 0; 131 if (flags & FLAG_CREATE) 132 open_flags = O_CREAT | O_EXCL; 133 134 created_ = false; 135 136 if (flags & FLAG_CREATE_ALWAYS) { 137 DCHECK(!open_flags); 138 open_flags = O_CREAT | O_TRUNC; 139 } 140 141 if (flags & FLAG_OPEN_TRUNCATED) { 142 DCHECK(!open_flags); 143 DCHECK(flags & FLAG_WRITE); 144 open_flags = O_TRUNC; 145 } 146 147 if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) { 148 NOTREACHED(); 149 errno = EOPNOTSUPP; 150 error_ = FILE_ERROR_FAILED; 151 return; 152 } 153 154 if (flags & FLAG_WRITE && flags & FLAG_READ) { 155 open_flags |= O_RDWR; 156 } else if (flags & FLAG_WRITE) { 157 open_flags |= O_WRONLY; 158 } else if (!(flags & FLAG_READ) && 159 !(flags & FLAG_WRITE_ATTRIBUTES) && 160 !(flags & FLAG_APPEND) && 161 !(flags & FLAG_OPEN_ALWAYS)) { 162 NOTREACHED(); 163 } 164 165 if (flags & FLAG_TERMINAL_DEVICE) 166 open_flags |= O_NOCTTY | O_NDELAY; 167 168 if (flags & FLAG_APPEND && flags & FLAG_READ) 169 open_flags |= O_APPEND | O_RDWR; 170 else if (flags & FLAG_APPEND) 171 open_flags |= O_APPEND | O_WRONLY; 172 173 COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero); 174 175 int mode = S_IRUSR | S_IWUSR; 176#if defined(OS_CHROMEOS) 177 mode |= S_IRGRP | S_IROTH; 178#endif 179 180 int descriptor = HANDLE_EINTR(open(name.value().c_str(), open_flags, mode)); 181 182 if (flags & FLAG_OPEN_ALWAYS) { 183 if (descriptor < 0) { 184 open_flags |= O_CREAT; 185 if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE) 186 open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW 187 188 descriptor = HANDLE_EINTR(open(name.value().c_str(), open_flags, mode)); 189 if (descriptor >= 0) 190 created_ = true; 191 } 192 } 193 194 if (descriptor >= 0 && (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))) 195 created_ = true; 196 197 if ((descriptor >= 0) && (flags & FLAG_DELETE_ON_CLOSE)) 198 unlink(name.value().c_str()); 199 200 if (descriptor >= 0) 201 error_ = FILE_OK; 202 else 203 error_ = File::OSErrorToFileError(errno); 204 205 file_ = descriptor; 206} 207#endif // !defined(OS_NACL) 208 209bool File::IsValid() const { 210 return file_ >= 0; 211} 212 213PlatformFile File::TakePlatformFile() { 214 PlatformFile file = file_; 215 file_ = kInvalidPlatformFileValue; 216 return file; 217} 218 219void File::Close() { 220 base::ThreadRestrictions::AssertIOAllowed(); 221 if (!IsValid()) 222 return; 223 224 if (!IGNORE_EINTR(close(file_))) 225 file_ = kInvalidPlatformFileValue; 226} 227 228int64 File::Seek(Whence whence, int64 offset) { 229 base::ThreadRestrictions::AssertIOAllowed(); 230 DCHECK(IsValid()); 231 if (file_ < 0 || offset < 0) 232 return -1; 233 234 return lseek(file_, static_cast<off_t>(offset), static_cast<int>(whence)); 235} 236 237int File::Read(int64 offset, char* data, int size) { 238 base::ThreadRestrictions::AssertIOAllowed(); 239 DCHECK(IsValid()); 240 if (size < 0) 241 return -1; 242 243 int bytes_read = 0; 244 int rv; 245 do { 246 rv = HANDLE_EINTR(pread(file_, data + bytes_read, 247 size - bytes_read, offset + bytes_read)); 248 if (rv <= 0) 249 break; 250 251 bytes_read += rv; 252 } while (bytes_read < size); 253 254 return bytes_read ? bytes_read : rv; 255} 256 257int File::ReadAtCurrentPos(char* data, int size) { 258 base::ThreadRestrictions::AssertIOAllowed(); 259 DCHECK(IsValid()); 260 if (size < 0) 261 return -1; 262 263 int bytes_read = 0; 264 int rv; 265 do { 266 rv = HANDLE_EINTR(read(file_, data, size)); 267 if (rv <= 0) 268 break; 269 270 bytes_read += rv; 271 } while (bytes_read < size); 272 273 return bytes_read ? bytes_read : rv; 274} 275 276int File::ReadNoBestEffort(int64 offset, char* data, int size) { 277 base::ThreadRestrictions::AssertIOAllowed(); 278 DCHECK(IsValid()); 279 280 return HANDLE_EINTR(pread(file_, data, size, offset)); 281} 282 283int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { 284 base::ThreadRestrictions::AssertIOAllowed(); 285 DCHECK(IsValid()); 286 if (size < 0) 287 return -1; 288 289 return HANDLE_EINTR(read(file_, data, size)); 290} 291 292int File::Write(int64 offset, const char* data, int size) { 293 base::ThreadRestrictions::AssertIOAllowed(); 294 295 if (IsOpenAppend(file_)) 296 return WriteAtCurrentPos(data, size); 297 298 DCHECK(IsValid()); 299 if (size < 0) 300 return -1; 301 302 int bytes_written = 0; 303 int rv; 304 do { 305 rv = HANDLE_EINTR(pwrite(file_, data + bytes_written, 306 size - bytes_written, offset + bytes_written)); 307 if (rv <= 0) 308 break; 309 310 bytes_written += rv; 311 } while (bytes_written < size); 312 313 return bytes_written ? bytes_written : rv; 314} 315 316int File::WriteAtCurrentPos(const char* data, int size) { 317 base::ThreadRestrictions::AssertIOAllowed(); 318 DCHECK(IsValid()); 319 if (size < 0) 320 return -1; 321 322 int bytes_written = 0; 323 int rv; 324 do { 325 rv = HANDLE_EINTR(write(file_, data, size)); 326 if (rv <= 0) 327 break; 328 329 bytes_written += rv; 330 } while (bytes_written < size); 331 332 return bytes_written ? bytes_written : rv; 333} 334 335int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { 336 base::ThreadRestrictions::AssertIOAllowed(); 337 DCHECK(IsValid()); 338 if (size < 0) 339 return -1; 340 341 return HANDLE_EINTR(write(file_, data, size)); 342} 343 344bool File::Truncate(int64 length) { 345 base::ThreadRestrictions::AssertIOAllowed(); 346 DCHECK(IsValid()); 347 return !CallFtruncate(file_, length); 348} 349 350bool File::Flush() { 351 base::ThreadRestrictions::AssertIOAllowed(); 352 DCHECK(IsValid()); 353 return !CallFsync(file_); 354} 355 356bool File::SetTimes(Time last_access_time, Time last_modified_time) { 357 base::ThreadRestrictions::AssertIOAllowed(); 358 DCHECK(IsValid()); 359 360 timeval times[2]; 361 times[0] = last_access_time.ToTimeVal(); 362 times[1] = last_modified_time.ToTimeVal(); 363 364 return !CallFutimes(file_, times); 365} 366 367bool File::GetInfo(Info* info) { 368 DCHECK(IsValid()); 369 370 stat_wrapper_t file_info; 371 if (CallFstat(file_, &file_info)) 372 return false; 373 374 info->is_directory = S_ISDIR(file_info.st_mode); 375 info->is_symbolic_link = S_ISLNK(file_info.st_mode); 376 info->size = file_info.st_size; 377 378#if defined(OS_LINUX) 379 const time_t last_modified_sec = file_info.st_mtim.tv_sec; 380 const int64 last_modified_nsec = file_info.st_mtim.tv_nsec; 381 const time_t last_accessed_sec = file_info.st_atim.tv_sec; 382 const int64 last_accessed_nsec = file_info.st_atim.tv_nsec; 383 const time_t creation_time_sec = file_info.st_ctim.tv_sec; 384 const int64 creation_time_nsec = file_info.st_ctim.tv_nsec; 385#elif defined(OS_ANDROID) 386 const time_t last_modified_sec = file_info.st_mtime; 387 const int64 last_modified_nsec = file_info.st_mtime_nsec; 388 const time_t last_accessed_sec = file_info.st_atime; 389 const int64 last_accessed_nsec = file_info.st_atime_nsec; 390 const time_t creation_time_sec = file_info.st_ctime; 391 const int64 creation_time_nsec = file_info.st_ctime_nsec; 392#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD) 393 const time_t last_modified_sec = file_info.st_mtimespec.tv_sec; 394 const int64 last_modified_nsec = file_info.st_mtimespec.tv_nsec; 395 const time_t last_accessed_sec = file_info.st_atimespec.tv_sec; 396 const int64 last_accessed_nsec = file_info.st_atimespec.tv_nsec; 397 const time_t creation_time_sec = file_info.st_ctimespec.tv_sec; 398 const int64 creation_time_nsec = file_info.st_ctimespec.tv_nsec; 399#else 400 // TODO(gavinp): Investigate a good high resolution option for OS_NACL. 401 const time_t last_modified_sec = file_info.st_mtime; 402 const int64 last_modified_nsec = 0; 403 const time_t last_accessed_sec = file_info.st_atime; 404 const int64 last_accessed_nsec = 0; 405 const time_t creation_time_sec = file_info.st_ctime; 406 const int64 creation_time_nsec = 0; 407#endif 408 409 info->last_modified = 410 base::Time::FromTimeT(last_modified_sec) + 411 base::TimeDelta::FromMicroseconds(last_modified_nsec / 412 base::Time::kNanosecondsPerMicrosecond); 413 info->last_accessed = 414 base::Time::FromTimeT(last_accessed_sec) + 415 base::TimeDelta::FromMicroseconds(last_accessed_nsec / 416 base::Time::kNanosecondsPerMicrosecond); 417 info->creation_time = 418 base::Time::FromTimeT(creation_time_sec) + 419 base::TimeDelta::FromMicroseconds(creation_time_nsec / 420 base::Time::kNanosecondsPerMicrosecond); 421 return true; 422} 423 424File::Error File::Lock() { 425 return CallFctnlFlock(file_, true); 426} 427 428File::Error File::Unlock() { 429 return CallFctnlFlock(file_, false); 430} 431 432// Static. 433File::Error File::OSErrorToFileError(int saved_errno) { 434 switch (saved_errno) { 435 case EACCES: 436 case EISDIR: 437 case EROFS: 438 case EPERM: 439 return FILE_ERROR_ACCESS_DENIED; 440#if !defined(OS_NACL) // ETXTBSY not defined by NaCl. 441 case ETXTBSY: 442 return FILE_ERROR_IN_USE; 443#endif 444 case EEXIST: 445 return FILE_ERROR_EXISTS; 446 case ENOENT: 447 return FILE_ERROR_NOT_FOUND; 448 case EMFILE: 449 return FILE_ERROR_TOO_MANY_OPENED; 450 case ENOMEM: 451 return FILE_ERROR_NO_MEMORY; 452 case ENOSPC: 453 return FILE_ERROR_NO_SPACE; 454 case ENOTDIR: 455 return FILE_ERROR_NOT_A_DIRECTORY; 456 default: 457#if !defined(OS_NACL) // NaCl build has no metrics code. 458 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix", 459 saved_errno); 460#endif 461 return FILE_ERROR_FAILED; 462 } 463} 464 465void File::SetPlatformFile(PlatformFile file) { 466 DCHECK_EQ(file_, kInvalidPlatformFileValue); 467 file_ = file; 468} 469 470} // namespace base 471