Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/platform_file.h" | 5 #include "base/platform_file.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <sys/stat.h> | 9 #include <sys/stat.h> |
| 10 #include <unistd.h> | 10 #include <unistd.h> |
| 11 | 11 |
| 12 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/metrics/sparse_histogram.h" | 14 #include "base/metrics/sparse_histogram.h" |
| 15 #include "base/posix/eintr_wrapper.h" | 15 #include "base/posix/eintr_wrapper.h" |
| 16 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 17 #include "base/threading/thread_restrictions.h" | 17 #include "base/threading/thread_restrictions.h" |
| 18 | 18 |
| 19 #if defined(OS_ANDROID) | 19 #if defined(OS_ANDROID) |
| 20 #include "base/os_compat_android.h" | 20 #include "base/os_compat_android.h" |
| 21 #endif | 21 #endif |
| 22 | 22 |
| 23 namespace base { | 23 namespace base { |
| 24 | 24 |
| 25 // Make sure our Whence mappings match the system headers. | 25 // Make sure our Whence mappings match the system headers. |
| 26 COMPILE_ASSERT(PLATFORM_FILE_FROM_BEGIN == SEEK_SET && | 26 COMPILE_ASSERT(PLATFORM_FILE_FROM_BEGIN == SEEK_SET && |
| 27 PLATFORM_FILE_FROM_CURRENT == SEEK_CUR && | 27 PLATFORM_FILE_FROM_CURRENT == SEEK_CUR && |
| 28 PLATFORM_FILE_FROM_END == SEEK_END, whence_matches_system); | 28 PLATFORM_FILE_FROM_END == SEEK_END, whence_matches_system); |
| 29 | 29 |
| 30 #if defined(OS_BSD) || defined(OS_MACOSX) | 30 namespace { |
| 31 | |
| 32 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) | |
| 31 typedef struct stat stat_wrapper_t; | 33 typedef struct stat stat_wrapper_t; |
| 32 static int CallFstat(int fd, stat_wrapper_t *sb) { | 34 static int CallFstat(int fd, stat_wrapper_t *sb) { |
| 33 base::ThreadRestrictions::AssertIOAllowed(); | 35 base::ThreadRestrictions::AssertIOAllowed(); |
| 34 return fstat(fd, sb); | 36 return fstat(fd, sb); |
| 35 } | 37 } |
| 36 #else | 38 #else |
| 37 typedef struct stat64 stat_wrapper_t; | 39 typedef struct stat64 stat_wrapper_t; |
| 38 static int CallFstat(int fd, stat_wrapper_t *sb) { | 40 static int CallFstat(int fd, stat_wrapper_t *sb) { |
| 39 base::ThreadRestrictions::AssertIOAllowed(); | 41 base::ThreadRestrictions::AssertIOAllowed(); |
| 40 return fstat64(fd, sb); | 42 return fstat64(fd, sb); |
| 41 } | 43 } |
| 42 #endif | 44 #endif |
| 43 | 45 |
| 46 // TODO(bbudge) Remove NaCl workarounds when it implements pread and pwrite. | |
|
brettw
2013/07/11 18:02:43
Probably this comment should be more generic (sinc
bbudge
2013/07/11 20:08:23
I moved the comment to just above DoPread. It's li
bbudge
2013/07/11 20:13:18
Added comments about why these are necessary.
| |
| 47 #if !defined(OS_NACL) | |
| 48 static int DoPread(PlatformFile file, char* data, int size, int64 offset) { | |
| 49 return HANDLE_EINTR(pread(file, data, size, offset)); | |
| 50 } | |
| 51 | |
| 52 static int DoPwrite(PlatformFile file, const char* data, int size, | |
| 53 int64 offset) { | |
| 54 return HANDLE_EINTR(pwrite(file, data, size, offset)); | |
| 55 } | |
| 56 | |
| 57 static bool IsOpenAppend(PlatformFile file) { | |
| 58 return (fcntl(file, F_GETFL) & O_APPEND) != 0; | |
| 59 } | |
| 60 | |
| 61 static int CallFtruncate(PlatformFile file, int64 length) { | |
| 62 return HANDLE_EINTR(ftruncate(file, length)); | |
| 63 } | |
| 64 | |
| 65 static int CallFsync(PlatformFile file) { | |
| 66 return HANDLE_EINTR(fsync(file)); | |
| 67 } | |
| 68 | |
| 69 static int CallFutimes(PlatformFile file, const struct timeval times[2]) { | |
| 70 #ifdef __USE_XOPEN2K8 | |
| 71 // futimens should be available, but futimes might not be | |
| 72 // http://pubs.opengroup.org/onlinepubs/9699919799/ | |
| 73 | |
| 74 timespec ts_times[2]; | |
| 75 ts_times[0].tv_sec = times[0].tv_sec; | |
| 76 ts_times[0].tv_nsec = times[0].tv_usec * 1000; | |
| 77 ts_times[1].tv_sec = times[1].tv_sec; | |
| 78 ts_times[1].tv_nsec = times[1].tv_usec * 1000; | |
| 79 | |
| 80 return futimens(file, ts_times); | |
| 81 #else | |
| 82 return futimes(file, times); | |
| 83 #endif | |
| 84 } | |
| 85 #else | |
| 86 static int DoPread(PlatformFile file, char* data, int size, int64 offset) { | |
| 87 lseek(file, static_cast<off_t>(offset), SEEK_SET); | |
| 88 return HANDLE_EINTR(read(file, data, size)); | |
| 89 } | |
| 90 | |
| 91 static int DoPwrite(PlatformFile file, const char* data, int size, | |
| 92 int64 offset) { | |
| 93 lseek(file, static_cast<off_t>(offset), SEEK_SET); | |
| 94 return HANDLE_EINTR(write(file, data, size)); | |
| 95 } | |
| 96 | |
| 97 static bool IsOpenAppend(PlatformFile file) { | |
| 98 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX | |
| 99 // standard and always appends if the file is opened with O_APPEND, just | |
| 100 // return false here. | |
| 101 return false; | |
| 102 } | |
| 103 | |
| 104 static int CallFtruncate(PlatformFile file, int64 length) { | |
| 105 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate. | |
| 106 return 0; | |
| 107 } | |
| 108 | |
| 109 static int CallFsync(PlatformFile file) { | |
| 110 NOTIMPLEMENTED(); // NaCl doesn't implement fsync. | |
| 111 return 0; | |
| 112 } | |
| 113 | |
| 114 static int CallFutimes(PlatformFile file, const struct timeval times[2]) { | |
| 115 NOTIMPLEMENTED(); // NaCl doesn't implement futimes. | |
| 116 return 0; | |
| 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) | |
| 44 // TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here? | 124 // TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here? |
| 45 PlatformFile CreatePlatformFileUnsafe(const FilePath& name, | 125 PlatformFile CreatePlatformFileUnsafe(const FilePath& name, |
| 46 int flags, | 126 int flags, |
| 47 bool* created, | 127 bool* created, |
| 48 PlatformFileError* error) { | 128 PlatformFileError* error) { |
| 49 base::ThreadRestrictions::AssertIOAllowed(); | 129 base::ThreadRestrictions::AssertIOAllowed(); |
| 50 | 130 |
| 51 int open_flags = 0; | 131 int open_flags = 0; |
| 52 if (flags & PLATFORM_FILE_CREATE) | 132 if (flags & PLATFORM_FILE_CREATE) |
| 53 open_flags = O_CREAT | O_EXCL; | 133 open_flags = O_CREAT | O_EXCL; |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 132 else | 212 else |
| 133 *error = ErrnoToPlatformFileError(errno); | 213 *error = ErrnoToPlatformFileError(errno); |
| 134 } | 214 } |
| 135 | 215 |
| 136 return descriptor; | 216 return descriptor; |
| 137 } | 217 } |
| 138 | 218 |
| 139 FILE* FdopenPlatformFile(PlatformFile file, const char* mode) { | 219 FILE* FdopenPlatformFile(PlatformFile file, const char* mode) { |
| 140 return fdopen(file, mode); | 220 return fdopen(file, mode); |
| 141 } | 221 } |
| 222 #endif // !defined(OS_NACL) | |
| 142 | 223 |
| 143 bool ClosePlatformFile(PlatformFile file) { | 224 bool ClosePlatformFile(PlatformFile file) { |
| 144 base::ThreadRestrictions::AssertIOAllowed(); | 225 base::ThreadRestrictions::AssertIOAllowed(); |
| 145 return !HANDLE_EINTR(close(file)); | 226 return !HANDLE_EINTR(close(file)); |
| 146 } | 227 } |
| 147 | 228 |
| 148 int64 SeekPlatformFile(PlatformFile file, | 229 int64 SeekPlatformFile(PlatformFile file, |
| 149 PlatformFileWhence whence, | 230 PlatformFileWhence whence, |
| 150 int64 offset) { | 231 int64 offset) { |
| 151 base::ThreadRestrictions::AssertIOAllowed(); | 232 base::ThreadRestrictions::AssertIOAllowed(); |
| 152 if (file < 0 || offset < 0) | 233 if (file < 0 || offset < 0) |
| 153 return -1; | 234 return -1; |
| 154 | 235 |
| 155 return lseek(file, static_cast<off_t>(offset), static_cast<int>(whence)); | 236 return lseek(file, static_cast<off_t>(offset), static_cast<int>(whence)); |
| 156 } | 237 } |
| 157 | 238 |
| 158 int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) { | 239 int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) { |
| 159 base::ThreadRestrictions::AssertIOAllowed(); | 240 base::ThreadRestrictions::AssertIOAllowed(); |
| 160 if (file < 0 || size < 0) | 241 if (file < 0 || size < 0) |
| 161 return -1; | 242 return -1; |
| 162 | 243 |
| 163 int bytes_read = 0; | 244 int bytes_read = 0; |
| 164 int rv; | 245 int rv; |
| 165 do { | 246 do { |
| 166 rv = HANDLE_EINTR(pread(file, data + bytes_read, | 247 rv = DoPread(file, data + bytes_read, |
| 167 size - bytes_read, offset + bytes_read)); | 248 size - bytes_read, offset + bytes_read); |
| 168 if (rv <= 0) | 249 if (rv <= 0) |
| 169 break; | 250 break; |
| 170 | 251 |
| 171 bytes_read += rv; | 252 bytes_read += rv; |
| 172 } while (bytes_read < size); | 253 } while (bytes_read < size); |
| 173 | 254 |
| 174 return bytes_read ? bytes_read : rv; | 255 return bytes_read ? bytes_read : rv; |
| 175 } | 256 } |
| 176 | 257 |
| 177 int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) { | 258 int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 191 | 272 |
| 192 return bytes_read ? bytes_read : rv; | 273 return bytes_read ? bytes_read : rv; |
| 193 } | 274 } |
| 194 | 275 |
| 195 int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, | 276 int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, |
| 196 char* data, int size) { | 277 char* data, int size) { |
| 197 base::ThreadRestrictions::AssertIOAllowed(); | 278 base::ThreadRestrictions::AssertIOAllowed(); |
| 198 if (file < 0) | 279 if (file < 0) |
| 199 return -1; | 280 return -1; |
| 200 | 281 |
| 201 return HANDLE_EINTR(pread(file, data, size, offset)); | 282 return DoPread(file, data, size, offset); |
| 202 } | 283 } |
| 203 | 284 |
| 204 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file, | 285 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file, |
| 205 char* data, int size) { | 286 char* data, int size) { |
| 206 base::ThreadRestrictions::AssertIOAllowed(); | 287 base::ThreadRestrictions::AssertIOAllowed(); |
| 207 if (file < 0 || size < 0) | 288 if (file < 0 || size < 0) |
| 208 return -1; | 289 return -1; |
| 209 | 290 |
| 210 return HANDLE_EINTR(read(file, data, size)); | 291 return HANDLE_EINTR(read(file, data, size)); |
| 211 } | 292 } |
| 212 | 293 |
| 213 int WritePlatformFile(PlatformFile file, int64 offset, | 294 int WritePlatformFile(PlatformFile file, int64 offset, |
| 214 const char* data, int size) { | 295 const char* data, int size) { |
| 215 base::ThreadRestrictions::AssertIOAllowed(); | 296 base::ThreadRestrictions::AssertIOAllowed(); |
| 216 | 297 |
| 217 if (fcntl(file, F_GETFL) & O_APPEND) | 298 if (IsOpenAppend(file)) |
| 218 return WritePlatformFileAtCurrentPos(file, data, size); | 299 return WritePlatformFileAtCurrentPos(file, data, size); |
| 219 | 300 |
| 220 if (file < 0 || size < 0) | 301 if (file < 0 || size < 0) |
| 221 return -1; | 302 return -1; |
| 222 | 303 |
| 223 int bytes_written = 0; | 304 int bytes_written = 0; |
| 224 int rv; | 305 int rv; |
| 225 do { | 306 do { |
| 226 rv = HANDLE_EINTR(pwrite(file, data + bytes_written, | 307 rv = DoPwrite(file, data + bytes_written, |
| 227 size - bytes_written, offset + bytes_written)); | 308 size - bytes_written, offset + bytes_written); |
| 228 if (rv <= 0) | 309 if (rv <= 0) |
| 229 break; | 310 break; |
| 230 | 311 |
| 231 bytes_written += rv; | 312 bytes_written += rv; |
| 232 } while (bytes_written < size); | 313 } while (bytes_written < size); |
| 233 | 314 |
| 234 return bytes_written ? bytes_written : rv; | 315 return bytes_written ? bytes_written : rv; |
| 235 } | 316 } |
| 236 | 317 |
| 237 int WritePlatformFileAtCurrentPos(PlatformFile file, | 318 int WritePlatformFileAtCurrentPos(PlatformFile file, |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 257 const char* data, int size) { | 338 const char* data, int size) { |
| 258 base::ThreadRestrictions::AssertIOAllowed(); | 339 base::ThreadRestrictions::AssertIOAllowed(); |
| 259 if (file < 0 || size < 0) | 340 if (file < 0 || size < 0) |
| 260 return -1; | 341 return -1; |
| 261 | 342 |
| 262 return HANDLE_EINTR(write(file, data, size)); | 343 return HANDLE_EINTR(write(file, data, size)); |
| 263 } | 344 } |
| 264 | 345 |
| 265 bool TruncatePlatformFile(PlatformFile file, int64 length) { | 346 bool TruncatePlatformFile(PlatformFile file, int64 length) { |
| 266 base::ThreadRestrictions::AssertIOAllowed(); | 347 base::ThreadRestrictions::AssertIOAllowed(); |
| 267 return ((file >= 0) && !HANDLE_EINTR(ftruncate(file, length))); | 348 return ((file >= 0) && !CallFtruncate(file, length)); |
| 268 } | 349 } |
| 269 | 350 |
| 270 bool FlushPlatformFile(PlatformFile file) { | 351 bool FlushPlatformFile(PlatformFile file) { |
| 271 base::ThreadRestrictions::AssertIOAllowed(); | 352 base::ThreadRestrictions::AssertIOAllowed(); |
| 272 return !HANDLE_EINTR(fsync(file)); | 353 return !CallFsync(file); |
| 273 } | 354 } |
| 274 | 355 |
| 275 bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time, | 356 bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time, |
| 276 const base::Time& last_modified_time) { | 357 const base::Time& last_modified_time) { |
| 277 base::ThreadRestrictions::AssertIOAllowed(); | 358 base::ThreadRestrictions::AssertIOAllowed(); |
| 278 if (file < 0) | 359 if (file < 0) |
| 279 return false; | 360 return false; |
| 280 | 361 |
| 281 timeval times[2]; | 362 timeval times[2]; |
| 282 times[0] = last_access_time.ToTimeVal(); | 363 times[0] = last_access_time.ToTimeVal(); |
| 283 times[1] = last_modified_time.ToTimeVal(); | 364 times[1] = last_modified_time.ToTimeVal(); |
| 284 | 365 |
| 285 #ifdef __USE_XOPEN2K8 | 366 return !CallFutimes(file, times); |
| 286 // futimens should be available, but futimes might not be | |
| 287 // http://pubs.opengroup.org/onlinepubs/9699919799/ | |
| 288 | |
| 289 timespec ts_times[2]; | |
| 290 ts_times[0].tv_sec = times[0].tv_sec; | |
| 291 ts_times[0].tv_nsec = times[0].tv_usec * 1000; | |
| 292 ts_times[1].tv_sec = times[1].tv_sec; | |
| 293 ts_times[1].tv_nsec = times[1].tv_usec * 1000; | |
| 294 | |
| 295 return !futimens(file, ts_times); | |
| 296 #else | |
| 297 return !futimes(file, times); | |
| 298 #endif | |
| 299 } | 367 } |
| 300 | 368 |
| 301 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { | 369 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { |
| 302 if (!info) | 370 if (!info) |
| 303 return false; | 371 return false; |
| 304 | 372 |
| 305 stat_wrapper_t file_info; | 373 stat_wrapper_t file_info; |
| 306 if (CallFstat(file, &file_info)) | 374 if (CallFstat(file, &file_info)) |
| 307 return false; | 375 return false; |
| 308 | 376 |
| 309 info->is_directory = S_ISDIR(file_info.st_mode); | 377 info->is_directory = S_ISDIR(file_info.st_mode); |
| 310 info->is_symbolic_link = S_ISLNK(file_info.st_mode); | 378 info->is_symbolic_link = S_ISLNK(file_info.st_mode); |
| 311 info->size = file_info.st_size; | 379 info->size = file_info.st_size; |
| 312 info->last_modified = base::Time::FromTimeT(file_info.st_mtime); | 380 info->last_modified = base::Time::FromTimeT(file_info.st_mtime); |
| 313 info->last_accessed = base::Time::FromTimeT(file_info.st_atime); | 381 info->last_accessed = base::Time::FromTimeT(file_info.st_atime); |
| 314 info->creation_time = base::Time::FromTimeT(file_info.st_ctime); | 382 info->creation_time = base::Time::FromTimeT(file_info.st_ctime); |
| 315 return true; | 383 return true; |
| 316 } | 384 } |
| 317 | 385 |
| 318 PlatformFileError ErrnoToPlatformFileError(int saved_errno) { | 386 PlatformFileError ErrnoToPlatformFileError(int saved_errno) { |
| 319 switch (saved_errno) { | 387 switch (saved_errno) { |
| 320 case EACCES: | 388 case EACCES: |
| 321 case EISDIR: | 389 case EISDIR: |
| 322 case EROFS: | 390 case EROFS: |
| 323 case EPERM: | 391 case EPERM: |
| 324 return PLATFORM_FILE_ERROR_ACCESS_DENIED; | 392 return PLATFORM_FILE_ERROR_ACCESS_DENIED; |
| 393 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl. | |
| 325 case ETXTBSY: | 394 case ETXTBSY: |
| 326 return PLATFORM_FILE_ERROR_IN_USE; | 395 return PLATFORM_FILE_ERROR_IN_USE; |
| 396 #endif | |
| 327 case EEXIST: | 397 case EEXIST: |
| 328 return PLATFORM_FILE_ERROR_EXISTS; | 398 return PLATFORM_FILE_ERROR_EXISTS; |
| 329 case ENOENT: | 399 case ENOENT: |
| 330 return PLATFORM_FILE_ERROR_NOT_FOUND; | 400 return PLATFORM_FILE_ERROR_NOT_FOUND; |
| 331 case EMFILE: | 401 case EMFILE: |
| 332 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED; | 402 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED; |
| 333 case ENOMEM: | 403 case ENOMEM: |
| 334 return PLATFORM_FILE_ERROR_NO_MEMORY; | 404 return PLATFORM_FILE_ERROR_NO_MEMORY; |
| 335 case ENOSPC: | 405 case ENOSPC: |
| 336 return PLATFORM_FILE_ERROR_NO_SPACE; | 406 return PLATFORM_FILE_ERROR_NO_SPACE; |
| 337 case ENOTDIR: | 407 case ENOTDIR: |
| 338 return PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; | 408 return PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; |
| 339 default: | 409 default: |
| 340 #if !defined(OS_NACL) // NaCl build has no metrics code. | 410 #if !defined(OS_NACL) // NaCl build has no metrics code. |
| 341 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix", | 411 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix", |
| 342 saved_errno); | 412 saved_errno); |
| 343 #endif | 413 #endif |
| 344 return PLATFORM_FILE_ERROR_FAILED; | 414 return PLATFORM_FILE_ERROR_FAILED; |
| 345 } | 415 } |
| 346 } | 416 } |
| 347 | 417 |
| 348 } // namespace base | 418 } // namespace base |
| OLD | NEW |