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/platform_file.h" 6 7 #include <fcntl.h> 8 #include <errno.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 #include "base/posix/eintr_wrapper.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/threading/thread_restrictions.h" 18 19 #if defined(OS_ANDROID) 20 #include "base/os_compat_android.h" 21 #endif 22 23 namespace base { 24 25 // Make sure our Whence mappings match the system headers. 26 COMPILE_ASSERT(PLATFORM_FILE_FROM_BEGIN == SEEK_SET && 27 PLATFORM_FILE_FROM_CURRENT == SEEK_CUR && 28 PLATFORM_FILE_FROM_END == SEEK_END, whence_matches_system); 29 30 namespace { 31 32 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) 33 typedef struct stat stat_wrapper_t; 34 static int CallFstat(int fd, stat_wrapper_t *sb) { 35 base::ThreadRestrictions::AssertIOAllowed(); 36 return fstat(fd, sb); 37 } 38 #else 39 typedef struct stat64 stat_wrapper_t; 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 PlatformFileError 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 ErrnoToPlatformFileError(errno); 86 return PLATFORM_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 PlatformFileError CallFctnlFlock(PlatformFile file, bool do_lock) { 113 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct. 114 return PLATFORM_FILE_ERROR_INVALID_OPERATION; 115 } 116 #endif // defined(OS_NACL) 117 118 } // namespace 119 120 // NaCl doesn't implement system calls to open files directly. 121 #if !defined(OS_NACL) 122 // TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here? 123 PlatformFile CreatePlatformFileUnsafe(const FilePath& name, 124 int flags, 125 bool* created, 126 PlatformFileError* error) { 127 base::ThreadRestrictions::AssertIOAllowed(); 128 129 int open_flags = 0; 130 if (flags & PLATFORM_FILE_CREATE) 131 open_flags = O_CREAT | O_EXCL; 132 133 if (created) 134 *created = false; 135 136 if (flags & PLATFORM_FILE_CREATE_ALWAYS) { 137 DCHECK(!open_flags); 138 open_flags = O_CREAT | O_TRUNC; 139 } 140 141 if (flags & PLATFORM_FILE_OPEN_TRUNCATED) { 142 DCHECK(!open_flags); 143 DCHECK(flags & PLATFORM_FILE_WRITE); 144 open_flags = O_TRUNC; 145 } 146 147 if (!open_flags && !(flags & PLATFORM_FILE_OPEN) && 148 !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { 149 NOTREACHED(); 150 errno = EOPNOTSUPP; 151 if (error) 152 *error = PLATFORM_FILE_ERROR_FAILED; 153 return kInvalidPlatformFileValue; 154 } 155 156 if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) { 157 open_flags |= O_RDWR; 158 } else if (flags & PLATFORM_FILE_WRITE) { 159 open_flags |= O_WRONLY; 160 } else if (!(flags & PLATFORM_FILE_READ) && 161 !(flags & PLATFORM_FILE_WRITE_ATTRIBUTES) && 162 !(flags & PLATFORM_FILE_APPEND) && 163 !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { 164 NOTREACHED(); 165 } 166 167 if (flags & PLATFORM_FILE_TERMINAL_DEVICE) 168 open_flags |= O_NOCTTY | O_NDELAY; 169 170 if (flags & PLATFORM_FILE_APPEND && flags & PLATFORM_FILE_READ) 171 open_flags |= O_APPEND | O_RDWR; 172 else if (flags & PLATFORM_FILE_APPEND) 173 open_flags |= O_APPEND | O_WRONLY; 174 175 COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero); 176 177 int mode = S_IRUSR | S_IWUSR; 178 #if defined(OS_CHROMEOS) 179 mode |= S_IRGRP | S_IROTH; 180 #endif 181 182 int descriptor = 183 HANDLE_EINTR(open(name.value().c_str(), open_flags, mode)); 184 185 if (flags & PLATFORM_FILE_OPEN_ALWAYS) { 186 if (descriptor < 0) { 187 open_flags |= O_CREAT; 188 if (flags & PLATFORM_FILE_EXCLUSIVE_READ || 189 flags & PLATFORM_FILE_EXCLUSIVE_WRITE) { 190 open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW 191 } 192 descriptor = HANDLE_EINTR( 193 open(name.value().c_str(), open_flags, mode)); 194 if (created && descriptor >= 0) 195 *created = true; 196 } 197 } 198 199 if (created && (descriptor >= 0) && 200 (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE))) 201 *created = true; 202 203 if ((descriptor >= 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) { 204 unlink(name.value().c_str()); 205 } 206 207 if (error) { 208 if (descriptor >= 0) 209 *error = PLATFORM_FILE_OK; 210 else 211 *error = ErrnoToPlatformFileError(errno); 212 } 213 214 return descriptor; 215 } 216 217 FILE* FdopenPlatformFile(PlatformFile file, const char* mode) { 218 return fdopen(file, mode); 219 } 220 #endif // !defined(OS_NACL) 221 222 bool ClosePlatformFile(PlatformFile file) { 223 base::ThreadRestrictions::AssertIOAllowed(); 224 return !IGNORE_EINTR(close(file)); 225 } 226 227 int64 SeekPlatformFile(PlatformFile file, 228 PlatformFileWhence whence, 229 int64 offset) { 230 base::ThreadRestrictions::AssertIOAllowed(); 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 ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) { 238 base::ThreadRestrictions::AssertIOAllowed(); 239 if (file < 0 || size < 0) 240 return -1; 241 242 int bytes_read = 0; 243 int rv; 244 do { 245 rv = HANDLE_EINTR(pread(file, data + bytes_read, 246 size - bytes_read, offset + bytes_read)); 247 if (rv <= 0) 248 break; 249 250 bytes_read += rv; 251 } while (bytes_read < size); 252 253 return bytes_read ? bytes_read : rv; 254 } 255 256 int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) { 257 base::ThreadRestrictions::AssertIOAllowed(); 258 if (file < 0 || size < 0) 259 return -1; 260 261 int bytes_read = 0; 262 int rv; 263 do { 264 rv = HANDLE_EINTR(read(file, data, size)); 265 if (rv <= 0) 266 break; 267 268 bytes_read += rv; 269 } while (bytes_read < size); 270 271 return bytes_read ? bytes_read : rv; 272 } 273 274 int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, 275 char* data, int size) { 276 base::ThreadRestrictions::AssertIOAllowed(); 277 if (file < 0) 278 return -1; 279 280 return HANDLE_EINTR(pread(file, data, size, offset)); 281 } 282 283 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file, 284 char* data, int size) { 285 base::ThreadRestrictions::AssertIOAllowed(); 286 if (file < 0 || size < 0) 287 return -1; 288 289 return HANDLE_EINTR(read(file, data, size)); 290 } 291 292 int WritePlatformFile(PlatformFile file, int64 offset, 293 const char* data, int size) { 294 base::ThreadRestrictions::AssertIOAllowed(); 295 296 if (IsOpenAppend(file)) 297 return WritePlatformFileAtCurrentPos(file, data, size); 298 299 if (file < 0 || 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 WritePlatformFileAtCurrentPos(PlatformFile file, 317 const char* data, int size) { 318 base::ThreadRestrictions::AssertIOAllowed(); 319 if (file < 0 || 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 WritePlatformFileCurPosNoBestEffort(PlatformFile file, 336 const char* data, int size) { 337 base::ThreadRestrictions::AssertIOAllowed(); 338 if (file < 0 || size < 0) 339 return -1; 340 341 return HANDLE_EINTR(write(file, data, size)); 342 } 343 344 bool TruncatePlatformFile(PlatformFile file, int64 length) { 345 base::ThreadRestrictions::AssertIOAllowed(); 346 return ((file >= 0) && !CallFtruncate(file, length)); 347 } 348 349 bool FlushPlatformFile(PlatformFile file) { 350 base::ThreadRestrictions::AssertIOAllowed(); 351 return !CallFsync(file); 352 } 353 354 bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time, 355 const base::Time& last_modified_time) { 356 base::ThreadRestrictions::AssertIOAllowed(); 357 if (file < 0) 358 return false; 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 GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { 368 if (!info) 369 return false; 370 371 stat_wrapper_t file_info; 372 if (CallFstat(file, &file_info)) 373 return false; 374 375 info->is_directory = S_ISDIR(file_info.st_mode); 376 info->is_symbolic_link = S_ISLNK(file_info.st_mode); 377 info->size = file_info.st_size; 378 379 #if defined(OS_LINUX) 380 const time_t last_modified_sec = file_info.st_mtim.tv_sec; 381 const int64 last_modified_nsec = file_info.st_mtim.tv_nsec; 382 const time_t last_accessed_sec = file_info.st_atim.tv_sec; 383 const int64 last_accessed_nsec = file_info.st_atim.tv_nsec; 384 const time_t creation_time_sec = file_info.st_ctim.tv_sec; 385 const int64 creation_time_nsec = file_info.st_ctim.tv_nsec; 386 #elif defined(OS_ANDROID) 387 const time_t last_modified_sec = file_info.st_mtime; 388 const int64 last_modified_nsec = file_info.st_mtime_nsec; 389 const time_t last_accessed_sec = file_info.st_atime; 390 const int64 last_accessed_nsec = file_info.st_atime_nsec; 391 const time_t creation_time_sec = file_info.st_ctime; 392 const int64 creation_time_nsec = file_info.st_ctime_nsec; 393 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD) 394 const time_t last_modified_sec = file_info.st_mtimespec.tv_sec; 395 const int64 last_modified_nsec = file_info.st_mtimespec.tv_nsec; 396 const time_t last_accessed_sec = file_info.st_atimespec.tv_sec; 397 const int64 last_accessed_nsec = file_info.st_atimespec.tv_nsec; 398 const time_t creation_time_sec = file_info.st_ctimespec.tv_sec; 399 const int64 creation_time_nsec = file_info.st_ctimespec.tv_nsec; 400 #else 401 // TODO(gavinp): Investigate a good high resolution option for OS_NACL. 402 const time_t last_modified_sec = file_info.st_mtime; 403 const int64 last_modified_nsec = 0; 404 const time_t last_accessed_sec = file_info.st_atime; 405 const int64 last_accessed_nsec = 0; 406 const time_t creation_time_sec = file_info.st_ctime; 407 const int64 creation_time_nsec = 0; 408 #endif 409 410 info->last_modified = 411 base::Time::FromTimeT(last_modified_sec) + 412 base::TimeDelta::FromMicroseconds(last_modified_nsec / 413 base::Time::kNanosecondsPerMicrosecond); 414 info->last_accessed = 415 base::Time::FromTimeT(last_accessed_sec) + 416 base::TimeDelta::FromMicroseconds(last_accessed_nsec / 417 base::Time::kNanosecondsPerMicrosecond); 418 info->creation_time = 419 base::Time::FromTimeT(creation_time_sec) + 420 base::TimeDelta::FromMicroseconds(creation_time_nsec / 421 base::Time::kNanosecondsPerMicrosecond); 422 return true; 423 } 424 425 PlatformFileError LockPlatformFile(PlatformFile file) { 426 return CallFctnlFlock(file, true); 427 } 428 429 PlatformFileError UnlockPlatformFile(PlatformFile file) { 430 return CallFctnlFlock(file, false); 431 } 432 433 PlatformFileError ErrnoToPlatformFileError(int saved_errno) { 434 switch (saved_errno) { 435 case EACCES: 436 case EISDIR: 437 case EROFS: 438 case EPERM: 439 return PLATFORM_FILE_ERROR_ACCESS_DENIED; 440 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl. 441 case ETXTBSY: 442 return PLATFORM_FILE_ERROR_IN_USE; 443 #endif 444 case EEXIST: 445 return PLATFORM_FILE_ERROR_EXISTS; 446 case ENOENT: 447 return PLATFORM_FILE_ERROR_NOT_FOUND; 448 case EMFILE: 449 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED; 450 case ENOMEM: 451 return PLATFORM_FILE_ERROR_NO_MEMORY; 452 case ENOSPC: 453 return PLATFORM_FILE_ERROR_NO_SPACE; 454 case ENOTDIR: 455 return PLATFORM_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 PLATFORM_FILE_ERROR_FAILED; 462 } 463 } 464 465 } // namespace base 466