| OLD | NEW |
| (Empty) |
| 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 bool ClosePlatformFile(PlatformFile file) { | |
| 121 base::ThreadRestrictions::AssertIOAllowed(); | |
| 122 return !IGNORE_EINTR(close(file)); | |
| 123 } | |
| 124 | |
| 125 int64 SeekPlatformFile(PlatformFile file, | |
| 126 PlatformFileWhence whence, | |
| 127 int64 offset) { | |
| 128 base::ThreadRestrictions::AssertIOAllowed(); | |
| 129 if (file < 0 || offset < 0) | |
| 130 return -1; | |
| 131 | |
| 132 return lseek(file, static_cast<off_t>(offset), static_cast<int>(whence)); | |
| 133 } | |
| 134 | |
| 135 int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) { | |
| 136 base::ThreadRestrictions::AssertIOAllowed(); | |
| 137 if (file < 0 || size < 0) | |
| 138 return -1; | |
| 139 | |
| 140 int bytes_read = 0; | |
| 141 int rv; | |
| 142 do { | |
| 143 rv = HANDLE_EINTR(pread(file, data + bytes_read, | |
| 144 size - bytes_read, offset + bytes_read)); | |
| 145 if (rv <= 0) | |
| 146 break; | |
| 147 | |
| 148 bytes_read += rv; | |
| 149 } while (bytes_read < size); | |
| 150 | |
| 151 return bytes_read ? bytes_read : rv; | |
| 152 } | |
| 153 | |
| 154 int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) { | |
| 155 base::ThreadRestrictions::AssertIOAllowed(); | |
| 156 if (file < 0 || size < 0) | |
| 157 return -1; | |
| 158 | |
| 159 int bytes_read = 0; | |
| 160 int rv; | |
| 161 do { | |
| 162 rv = HANDLE_EINTR(read(file, data + bytes_read, size - bytes_read)); | |
| 163 if (rv <= 0) | |
| 164 break; | |
| 165 | |
| 166 bytes_read += rv; | |
| 167 } while (bytes_read < size); | |
| 168 | |
| 169 return bytes_read ? bytes_read : rv; | |
| 170 } | |
| 171 | |
| 172 int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, | |
| 173 char* data, int size) { | |
| 174 base::ThreadRestrictions::AssertIOAllowed(); | |
| 175 if (file < 0) | |
| 176 return -1; | |
| 177 | |
| 178 return HANDLE_EINTR(pread(file, data, size, offset)); | |
| 179 } | |
| 180 | |
| 181 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file, | |
| 182 char* data, int size) { | |
| 183 base::ThreadRestrictions::AssertIOAllowed(); | |
| 184 if (file < 0 || size < 0) | |
| 185 return -1; | |
| 186 | |
| 187 return HANDLE_EINTR(read(file, data, size)); | |
| 188 } | |
| 189 | |
| 190 int WritePlatformFile(PlatformFile file, int64 offset, | |
| 191 const char* data, int size) { | |
| 192 base::ThreadRestrictions::AssertIOAllowed(); | |
| 193 | |
| 194 if (IsOpenAppend(file)) | |
| 195 return WritePlatformFileAtCurrentPos(file, data, size); | |
| 196 | |
| 197 if (file < 0 || size < 0) | |
| 198 return -1; | |
| 199 | |
| 200 int bytes_written = 0; | |
| 201 int rv; | |
| 202 do { | |
| 203 rv = HANDLE_EINTR(pwrite(file, data + bytes_written, | |
| 204 size - bytes_written, offset + bytes_written)); | |
| 205 if (rv <= 0) | |
| 206 break; | |
| 207 | |
| 208 bytes_written += rv; | |
| 209 } while (bytes_written < size); | |
| 210 | |
| 211 return bytes_written ? bytes_written : rv; | |
| 212 } | |
| 213 | |
| 214 int WritePlatformFileAtCurrentPos(PlatformFile file, | |
| 215 const char* data, int size) { | |
| 216 base::ThreadRestrictions::AssertIOAllowed(); | |
| 217 if (file < 0 || size < 0) | |
| 218 return -1; | |
| 219 | |
| 220 int bytes_written = 0; | |
| 221 int rv; | |
| 222 do { | |
| 223 rv = HANDLE_EINTR(write(file, data + bytes_written, size - bytes_written)); | |
| 224 if (rv <= 0) | |
| 225 break; | |
| 226 | |
| 227 bytes_written += rv; | |
| 228 } while (bytes_written < size); | |
| 229 | |
| 230 return bytes_written ? bytes_written : rv; | |
| 231 } | |
| 232 | |
| 233 int WritePlatformFileCurPosNoBestEffort(PlatformFile file, | |
| 234 const char* data, int size) { | |
| 235 base::ThreadRestrictions::AssertIOAllowed(); | |
| 236 if (file < 0 || size < 0) | |
| 237 return -1; | |
| 238 | |
| 239 return HANDLE_EINTR(write(file, data, size)); | |
| 240 } | |
| 241 | |
| 242 bool TruncatePlatformFile(PlatformFile file, int64 length) { | |
| 243 base::ThreadRestrictions::AssertIOAllowed(); | |
| 244 return ((file >= 0) && !CallFtruncate(file, length)); | |
| 245 } | |
| 246 | |
| 247 bool FlushPlatformFile(PlatformFile file) { | |
| 248 base::ThreadRestrictions::AssertIOAllowed(); | |
| 249 return !CallFsync(file); | |
| 250 } | |
| 251 | |
| 252 bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time, | |
| 253 const base::Time& last_modified_time) { | |
| 254 base::ThreadRestrictions::AssertIOAllowed(); | |
| 255 if (file < 0) | |
| 256 return false; | |
| 257 | |
| 258 timeval times[2]; | |
| 259 times[0] = last_access_time.ToTimeVal(); | |
| 260 times[1] = last_modified_time.ToTimeVal(); | |
| 261 | |
| 262 return !CallFutimes(file, times); | |
| 263 } | |
| 264 | |
| 265 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { | |
| 266 if (!info) | |
| 267 return false; | |
| 268 | |
| 269 stat_wrapper_t file_info; | |
| 270 if (CallFstat(file, &file_info)) | |
| 271 return false; | |
| 272 | |
| 273 info->is_directory = S_ISDIR(file_info.st_mode); | |
| 274 info->is_symbolic_link = S_ISLNK(file_info.st_mode); | |
| 275 info->size = file_info.st_size; | |
| 276 | |
| 277 #if defined(OS_LINUX) | |
| 278 const time_t last_modified_sec = file_info.st_mtim.tv_sec; | |
| 279 const int64 last_modified_nsec = file_info.st_mtim.tv_nsec; | |
| 280 const time_t last_accessed_sec = file_info.st_atim.tv_sec; | |
| 281 const int64 last_accessed_nsec = file_info.st_atim.tv_nsec; | |
| 282 const time_t creation_time_sec = file_info.st_ctim.tv_sec; | |
| 283 const int64 creation_time_nsec = file_info.st_ctim.tv_nsec; | |
| 284 #elif defined(OS_ANDROID) | |
| 285 const time_t last_modified_sec = file_info.st_mtime; | |
| 286 const int64 last_modified_nsec = file_info.st_mtime_nsec; | |
| 287 const time_t last_accessed_sec = file_info.st_atime; | |
| 288 const int64 last_accessed_nsec = file_info.st_atime_nsec; | |
| 289 const time_t creation_time_sec = file_info.st_ctime; | |
| 290 const int64 creation_time_nsec = file_info.st_ctime_nsec; | |
| 291 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD) | |
| 292 const time_t last_modified_sec = file_info.st_mtimespec.tv_sec; | |
| 293 const int64 last_modified_nsec = file_info.st_mtimespec.tv_nsec; | |
| 294 const time_t last_accessed_sec = file_info.st_atimespec.tv_sec; | |
| 295 const int64 last_accessed_nsec = file_info.st_atimespec.tv_nsec; | |
| 296 const time_t creation_time_sec = file_info.st_ctimespec.tv_sec; | |
| 297 const int64 creation_time_nsec = file_info.st_ctimespec.tv_nsec; | |
| 298 #else | |
| 299 // TODO(gavinp): Investigate a good high resolution option for OS_NACL. | |
| 300 const time_t last_modified_sec = file_info.st_mtime; | |
| 301 const int64 last_modified_nsec = 0; | |
| 302 const time_t last_accessed_sec = file_info.st_atime; | |
| 303 const int64 last_accessed_nsec = 0; | |
| 304 const time_t creation_time_sec = file_info.st_ctime; | |
| 305 const int64 creation_time_nsec = 0; | |
| 306 #endif | |
| 307 | |
| 308 info->last_modified = | |
| 309 base::Time::FromTimeT(last_modified_sec) + | |
| 310 base::TimeDelta::FromMicroseconds(last_modified_nsec / | |
| 311 base::Time::kNanosecondsPerMicrosecond); | |
| 312 info->last_accessed = | |
| 313 base::Time::FromTimeT(last_accessed_sec) + | |
| 314 base::TimeDelta::FromMicroseconds(last_accessed_nsec / | |
| 315 base::Time::kNanosecondsPerMicrosecond); | |
| 316 info->creation_time = | |
| 317 base::Time::FromTimeT(creation_time_sec) + | |
| 318 base::TimeDelta::FromMicroseconds(creation_time_nsec / | |
| 319 base::Time::kNanosecondsPerMicrosecond); | |
| 320 return true; | |
| 321 } | |
| 322 | |
| 323 PlatformFileError LockPlatformFile(PlatformFile file) { | |
| 324 return CallFctnlFlock(file, true); | |
| 325 } | |
| 326 | |
| 327 PlatformFileError UnlockPlatformFile(PlatformFile file) { | |
| 328 return CallFctnlFlock(file, false); | |
| 329 } | |
| 330 | |
| 331 PlatformFileError ErrnoToPlatformFileError(int saved_errno) { | |
| 332 switch (saved_errno) { | |
| 333 case EACCES: | |
| 334 case EISDIR: | |
| 335 case EROFS: | |
| 336 case EPERM: | |
| 337 return PLATFORM_FILE_ERROR_ACCESS_DENIED; | |
| 338 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl. | |
| 339 case ETXTBSY: | |
| 340 return PLATFORM_FILE_ERROR_IN_USE; | |
| 341 #endif | |
| 342 case EEXIST: | |
| 343 return PLATFORM_FILE_ERROR_EXISTS; | |
| 344 case ENOENT: | |
| 345 return PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 346 case EMFILE: | |
| 347 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED; | |
| 348 case ENOMEM: | |
| 349 return PLATFORM_FILE_ERROR_NO_MEMORY; | |
| 350 case ENOSPC: | |
| 351 return PLATFORM_FILE_ERROR_NO_SPACE; | |
| 352 case ENOTDIR: | |
| 353 return PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; | |
| 354 default: | |
| 355 #if !defined(OS_NACL) // NaCl build has no metrics code. | |
| 356 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix", | |
| 357 saved_errno); | |
| 358 #endif | |
| 359 return PLATFORM_FILE_ERROR_FAILED; | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 } // namespace base | |
| OLD | NEW |