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