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 typedef struct stat stat_wrapper_t; 36 static int CallFstat(int fd, stat_wrapper_t *sb) { 37 base::ThreadRestrictions::AssertIOAllowed(); 38 return fstat(fd, sb); 39 } 40 #else 41 typedef struct stat64 stat_wrapper_t; 42 static 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) 51 static bool IsOpenAppend(PlatformFile file) { 52 return (fcntl(file, F_GETFL) & O_APPEND) != 0; 53 } 54 55 static int CallFtruncate(PlatformFile file, int64 length) { 56 return HANDLE_EINTR(ftruncate(file, length)); 57 } 58 59 static int CallFsync(PlatformFile file) { 60 return HANDLE_EINTR(fsync(file)); 61 } 62 63 static 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 80 static 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 92 static 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 99 static int CallFtruncate(PlatformFile file, int64 length) { 100 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate. 101 return 0; 102 } 103 104 static int CallFsync(PlatformFile file) { 105 NOTIMPLEMENTED(); // NaCl doesn't implement fsync. 106 return 0; 107 } 108 109 static int CallFutimes(PlatformFile file, const struct timeval times[2]) { 110 NOTIMPLEMENTED(); // NaCl doesn't implement futimes. 111 return 0; 112 } 113 114 static 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? 125 void 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 209 bool File::IsValid() const { 210 return file_ >= 0; 211 } 212 213 PlatformFile File::TakePlatformFile() { 214 PlatformFile file = file_; 215 file_ = kInvalidPlatformFileValue; 216 return file; 217 } 218 219 void File::Close() { 220 base::ThreadRestrictions::AssertIOAllowed(); 221 if (!IsValid()) 222 return; 223 224 if (!IGNORE_EINTR(close(file_))) 225 file_ = kInvalidPlatformFileValue; 226 } 227 228 int64 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 237 int 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 257 int 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 276 int 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 283 int 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 292 int 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 316 int 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 335 int 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 344 bool File::Truncate(int64 length) { 345 base::ThreadRestrictions::AssertIOAllowed(); 346 DCHECK(IsValid()); 347 return !CallFtruncate(file_, length); 348 } 349 350 bool File::Flush() { 351 base::ThreadRestrictions::AssertIOAllowed(); 352 DCHECK(IsValid()); 353 return !CallFsync(file_); 354 } 355 356 bool 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 367 bool 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 424 File::Error File::Lock() { 425 return CallFctnlFlock(file_, true); 426 } 427 428 File::Error File::Unlock() { 429 return CallFctnlFlock(file_, false); 430 } 431 432 // Static. 433 File::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 465 void File::SetPlatformFile(PlatformFile file) { 466 DCHECK_EQ(file_, kInvalidPlatformFileValue); 467 file_ = file; 468 } 469 470 } // namespace base 471