| 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/files/file.h" | |
| 6 | |
| 7 #include <io.h> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/metrics/sparse_histogram.h" | |
| 11 #include "base/threading/thread_restrictions.h" | |
| 12 | |
| 13 namespace base { | |
| 14 | |
| 15 // Make sure our Whence mappings match the system headers. | |
| 16 COMPILE_ASSERT(File::FROM_BEGIN == FILE_BEGIN && | |
| 17 File::FROM_CURRENT == FILE_CURRENT && | |
| 18 File::FROM_END == FILE_END, whence_matches_system); | |
| 19 | |
| 20 bool File::IsValid() const { | |
| 21 return file_.IsValid(); | |
| 22 } | |
| 23 | |
| 24 PlatformFile File::GetPlatformFile() const { | |
| 25 return file_.Get(); | |
| 26 } | |
| 27 | |
| 28 PlatformFile File::TakePlatformFile() { | |
| 29 return file_.Take(); | |
| 30 } | |
| 31 | |
| 32 void File::Close() { | |
| 33 if (!file_.IsValid()) | |
| 34 return; | |
| 35 | |
| 36 ThreadRestrictions::AssertIOAllowed(); | |
| 37 SCOPED_FILE_TRACE("Close"); | |
| 38 file_.Close(); | |
| 39 } | |
| 40 | |
| 41 int64 File::Seek(Whence whence, int64 offset) { | |
| 42 ThreadRestrictions::AssertIOAllowed(); | |
| 43 DCHECK(IsValid()); | |
| 44 | |
| 45 SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset); | |
| 46 | |
| 47 LARGE_INTEGER distance, res; | |
| 48 distance.QuadPart = offset; | |
| 49 DWORD move_method = static_cast<DWORD>(whence); | |
| 50 if (!SetFilePointerEx(file_.Get(), distance, &res, move_method)) | |
| 51 return -1; | |
| 52 return res.QuadPart; | |
| 53 } | |
| 54 | |
| 55 int File::Read(int64 offset, char* data, int size) { | |
| 56 ThreadRestrictions::AssertIOAllowed(); | |
| 57 DCHECK(IsValid()); | |
| 58 DCHECK(!async_); | |
| 59 if (size < 0) | |
| 60 return -1; | |
| 61 | |
| 62 SCOPED_FILE_TRACE_WITH_SIZE("Read", size); | |
| 63 | |
| 64 LARGE_INTEGER offset_li; | |
| 65 offset_li.QuadPart = offset; | |
| 66 | |
| 67 OVERLAPPED overlapped = {0}; | |
| 68 overlapped.Offset = offset_li.LowPart; | |
| 69 overlapped.OffsetHigh = offset_li.HighPart; | |
| 70 | |
| 71 DWORD bytes_read; | |
| 72 if (::ReadFile(file_.Get(), data, size, &bytes_read, &overlapped)) | |
| 73 return bytes_read; | |
| 74 if (ERROR_HANDLE_EOF == GetLastError()) | |
| 75 return 0; | |
| 76 | |
| 77 return -1; | |
| 78 } | |
| 79 | |
| 80 int File::ReadAtCurrentPos(char* data, int size) { | |
| 81 ThreadRestrictions::AssertIOAllowed(); | |
| 82 DCHECK(IsValid()); | |
| 83 DCHECK(!async_); | |
| 84 if (size < 0) | |
| 85 return -1; | |
| 86 | |
| 87 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size); | |
| 88 | |
| 89 DWORD bytes_read; | |
| 90 if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL)) | |
| 91 return bytes_read; | |
| 92 if (ERROR_HANDLE_EOF == GetLastError()) | |
| 93 return 0; | |
| 94 | |
| 95 return -1; | |
| 96 } | |
| 97 | |
| 98 int File::ReadNoBestEffort(int64 offset, char* data, int size) { | |
| 99 // TODO(dbeam): trace this separately? | |
| 100 return Read(offset, data, size); | |
| 101 } | |
| 102 | |
| 103 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { | |
| 104 // TODO(dbeam): trace this separately? | |
| 105 return ReadAtCurrentPos(data, size); | |
| 106 } | |
| 107 | |
| 108 int File::Write(int64 offset, const char* data, int size) { | |
| 109 ThreadRestrictions::AssertIOAllowed(); | |
| 110 DCHECK(IsValid()); | |
| 111 DCHECK(!async_); | |
| 112 | |
| 113 SCOPED_FILE_TRACE_WITH_SIZE("Write", size); | |
| 114 | |
| 115 LARGE_INTEGER offset_li; | |
| 116 offset_li.QuadPart = offset; | |
| 117 | |
| 118 OVERLAPPED overlapped = {0}; | |
| 119 overlapped.Offset = offset_li.LowPart; | |
| 120 overlapped.OffsetHigh = offset_li.HighPart; | |
| 121 | |
| 122 DWORD bytes_written; | |
| 123 if (::WriteFile(file_.Get(), data, size, &bytes_written, &overlapped)) | |
| 124 return bytes_written; | |
| 125 | |
| 126 return -1; | |
| 127 } | |
| 128 | |
| 129 int File::WriteAtCurrentPos(const char* data, int size) { | |
| 130 ThreadRestrictions::AssertIOAllowed(); | |
| 131 DCHECK(IsValid()); | |
| 132 DCHECK(!async_); | |
| 133 if (size < 0) | |
| 134 return -1; | |
| 135 | |
| 136 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size); | |
| 137 | |
| 138 DWORD bytes_written; | |
| 139 if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL)) | |
| 140 return bytes_written; | |
| 141 | |
| 142 return -1; | |
| 143 } | |
| 144 | |
| 145 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { | |
| 146 return WriteAtCurrentPos(data, size); | |
| 147 } | |
| 148 | |
| 149 int64 File::GetLength() { | |
| 150 ThreadRestrictions::AssertIOAllowed(); | |
| 151 DCHECK(IsValid()); | |
| 152 | |
| 153 SCOPED_FILE_TRACE("GetLength"); | |
| 154 | |
| 155 LARGE_INTEGER size; | |
| 156 if (!::GetFileSizeEx(file_.Get(), &size)) | |
| 157 return -1; | |
| 158 | |
| 159 return static_cast<int64>(size.QuadPart); | |
| 160 } | |
| 161 | |
| 162 bool File::SetLength(int64 length) { | |
| 163 ThreadRestrictions::AssertIOAllowed(); | |
| 164 DCHECK(IsValid()); | |
| 165 | |
| 166 SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length); | |
| 167 | |
| 168 // Get the current file pointer. | |
| 169 LARGE_INTEGER file_pointer; | |
| 170 LARGE_INTEGER zero; | |
| 171 zero.QuadPart = 0; | |
| 172 if (!::SetFilePointerEx(file_.Get(), zero, &file_pointer, FILE_CURRENT)) | |
| 173 return false; | |
| 174 | |
| 175 LARGE_INTEGER length_li; | |
| 176 length_li.QuadPart = length; | |
| 177 // If length > file size, SetFilePointerEx() should extend the file | |
| 178 // with zeroes on all Windows standard file systems (NTFS, FATxx). | |
| 179 if (!::SetFilePointerEx(file_.Get(), length_li, NULL, FILE_BEGIN)) | |
| 180 return false; | |
| 181 | |
| 182 // Set the new file length and move the file pointer to its old position. | |
| 183 // This is consistent with ftruncate()'s behavior, even when the file | |
| 184 // pointer points to a location beyond the end of the file. | |
| 185 // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not | |
| 186 // promised by the interface (nor was promised by PlatformFile). See if this | |
| 187 // implementation detail can be removed. | |
| 188 return ((::SetEndOfFile(file_.Get()) != FALSE) && | |
| 189 (::SetFilePointerEx(file_.Get(), file_pointer, NULL, FILE_BEGIN) != | |
| 190 FALSE)); | |
| 191 } | |
| 192 | |
| 193 bool File::SetTimes(Time last_access_time, Time last_modified_time) { | |
| 194 ThreadRestrictions::AssertIOAllowed(); | |
| 195 DCHECK(IsValid()); | |
| 196 | |
| 197 SCOPED_FILE_TRACE("SetTimes"); | |
| 198 | |
| 199 FILETIME last_access_filetime = last_access_time.ToFileTime(); | |
| 200 FILETIME last_modified_filetime = last_modified_time.ToFileTime(); | |
| 201 return (::SetFileTime(file_.Get(), NULL, &last_access_filetime, | |
| 202 &last_modified_filetime) != FALSE); | |
| 203 } | |
| 204 | |
| 205 bool File::GetInfo(Info* info) { | |
| 206 ThreadRestrictions::AssertIOAllowed(); | |
| 207 DCHECK(IsValid()); | |
| 208 | |
| 209 SCOPED_FILE_TRACE("GetInfo"); | |
| 210 | |
| 211 BY_HANDLE_FILE_INFORMATION file_info; | |
| 212 if (!GetFileInformationByHandle(file_.Get(), &file_info)) | |
| 213 return false; | |
| 214 | |
| 215 LARGE_INTEGER size; | |
| 216 size.HighPart = file_info.nFileSizeHigh; | |
| 217 size.LowPart = file_info.nFileSizeLow; | |
| 218 info->size = size.QuadPart; | |
| 219 info->is_directory = | |
| 220 (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; | |
| 221 info->is_symbolic_link = false; // Windows doesn't have symbolic links. | |
| 222 info->last_modified = Time::FromFileTime(file_info.ftLastWriteTime); | |
| 223 info->last_accessed = Time::FromFileTime(file_info.ftLastAccessTime); | |
| 224 info->creation_time = Time::FromFileTime(file_info.ftCreationTime); | |
| 225 return true; | |
| 226 } | |
| 227 | |
| 228 File::Error File::Lock() { | |
| 229 DCHECK(IsValid()); | |
| 230 | |
| 231 SCOPED_FILE_TRACE("Lock"); | |
| 232 | |
| 233 BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD); | |
| 234 if (!result) | |
| 235 return OSErrorToFileError(GetLastError()); | |
| 236 return FILE_OK; | |
| 237 } | |
| 238 | |
| 239 File::Error File::Unlock() { | |
| 240 DCHECK(IsValid()); | |
| 241 | |
| 242 SCOPED_FILE_TRACE("Unlock"); | |
| 243 | |
| 244 BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD); | |
| 245 if (!result) | |
| 246 return OSErrorToFileError(GetLastError()); | |
| 247 return FILE_OK; | |
| 248 } | |
| 249 | |
| 250 File File::Duplicate() { | |
| 251 if (!IsValid()) | |
| 252 return File(); | |
| 253 | |
| 254 SCOPED_FILE_TRACE("Duplicate"); | |
| 255 | |
| 256 HANDLE other_handle = nullptr; | |
| 257 | |
| 258 if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle | |
| 259 GetPlatformFile(), | |
| 260 GetCurrentProcess(), // hTargetProcessHandle | |
| 261 &other_handle, | |
| 262 0, // dwDesiredAccess ignored due to SAME_ACCESS | |
| 263 FALSE, // !bInheritHandle | |
| 264 DUPLICATE_SAME_ACCESS)) { | |
| 265 return File(OSErrorToFileError(GetLastError())); | |
| 266 } | |
| 267 | |
| 268 File other(other_handle); | |
| 269 if (async()) | |
| 270 other.async_ = true; | |
| 271 return other.Pass(); | |
| 272 } | |
| 273 | |
| 274 // Static. | |
| 275 File::Error File::OSErrorToFileError(DWORD last_error) { | |
| 276 switch (last_error) { | |
| 277 case ERROR_SHARING_VIOLATION: | |
| 278 return FILE_ERROR_IN_USE; | |
| 279 case ERROR_FILE_EXISTS: | |
| 280 return FILE_ERROR_EXISTS; | |
| 281 case ERROR_FILE_NOT_FOUND: | |
| 282 case ERROR_PATH_NOT_FOUND: | |
| 283 return FILE_ERROR_NOT_FOUND; | |
| 284 case ERROR_ACCESS_DENIED: | |
| 285 return FILE_ERROR_ACCESS_DENIED; | |
| 286 case ERROR_TOO_MANY_OPEN_FILES: | |
| 287 return FILE_ERROR_TOO_MANY_OPENED; | |
| 288 case ERROR_OUTOFMEMORY: | |
| 289 case ERROR_NOT_ENOUGH_MEMORY: | |
| 290 return FILE_ERROR_NO_MEMORY; | |
| 291 case ERROR_HANDLE_DISK_FULL: | |
| 292 case ERROR_DISK_FULL: | |
| 293 case ERROR_DISK_RESOURCES_EXHAUSTED: | |
| 294 return FILE_ERROR_NO_SPACE; | |
| 295 case ERROR_USER_MAPPED_FILE: | |
| 296 return FILE_ERROR_INVALID_OPERATION; | |
| 297 case ERROR_NOT_READY: | |
| 298 case ERROR_SECTOR_NOT_FOUND: | |
| 299 case ERROR_DEV_NOT_EXIST: | |
| 300 case ERROR_IO_DEVICE: | |
| 301 case ERROR_FILE_CORRUPT: | |
| 302 case ERROR_DISK_CORRUPT: | |
| 303 return FILE_ERROR_IO; | |
| 304 default: | |
| 305 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Windows", | |
| 306 last_error); | |
| 307 return FILE_ERROR_FAILED; | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 void File::DoInitialize(uint32 flags) { | |
| 312 ThreadRestrictions::AssertIOAllowed(); | |
| 313 DCHECK(!IsValid()); | |
| 314 | |
| 315 DWORD disposition = 0; | |
| 316 | |
| 317 if (flags & FLAG_OPEN) | |
| 318 disposition = OPEN_EXISTING; | |
| 319 | |
| 320 if (flags & FLAG_CREATE) { | |
| 321 DCHECK(!disposition); | |
| 322 disposition = CREATE_NEW; | |
| 323 } | |
| 324 | |
| 325 if (flags & FLAG_OPEN_ALWAYS) { | |
| 326 DCHECK(!disposition); | |
| 327 disposition = OPEN_ALWAYS; | |
| 328 } | |
| 329 | |
| 330 if (flags & FLAG_CREATE_ALWAYS) { | |
| 331 DCHECK(!disposition); | |
| 332 DCHECK(flags & FLAG_WRITE); | |
| 333 disposition = CREATE_ALWAYS; | |
| 334 } | |
| 335 | |
| 336 if (flags & FLAG_OPEN_TRUNCATED) { | |
| 337 DCHECK(!disposition); | |
| 338 DCHECK(flags & FLAG_WRITE); | |
| 339 disposition = TRUNCATE_EXISTING; | |
| 340 } | |
| 341 | |
| 342 if (!disposition) { | |
| 343 NOTREACHED(); | |
| 344 return; | |
| 345 } | |
| 346 | |
| 347 DWORD access = 0; | |
| 348 if (flags & FLAG_WRITE) | |
| 349 access = GENERIC_WRITE; | |
| 350 if (flags & FLAG_APPEND) { | |
| 351 DCHECK(!access); | |
| 352 access = FILE_APPEND_DATA; | |
| 353 } | |
| 354 if (flags & FLAG_READ) | |
| 355 access |= GENERIC_READ; | |
| 356 if (flags & FLAG_WRITE_ATTRIBUTES) | |
| 357 access |= FILE_WRITE_ATTRIBUTES; | |
| 358 if (flags & FLAG_EXECUTE) | |
| 359 access |= GENERIC_EXECUTE; | |
| 360 | |
| 361 DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; | |
| 362 if (!(flags & FLAG_EXCLUSIVE_WRITE)) | |
| 363 sharing |= FILE_SHARE_WRITE; | |
| 364 if (flags & FLAG_SHARE_DELETE) | |
| 365 sharing |= FILE_SHARE_DELETE; | |
| 366 | |
| 367 DWORD create_flags = 0; | |
| 368 if (flags & FLAG_ASYNC) | |
| 369 create_flags |= FILE_FLAG_OVERLAPPED; | |
| 370 if (flags & FLAG_TEMPORARY) | |
| 371 create_flags |= FILE_ATTRIBUTE_TEMPORARY; | |
| 372 if (flags & FLAG_HIDDEN) | |
| 373 create_flags |= FILE_ATTRIBUTE_HIDDEN; | |
| 374 if (flags & FLAG_DELETE_ON_CLOSE) | |
| 375 create_flags |= FILE_FLAG_DELETE_ON_CLOSE; | |
| 376 if (flags & FLAG_BACKUP_SEMANTICS) | |
| 377 create_flags |= FILE_FLAG_BACKUP_SEMANTICS; | |
| 378 | |
| 379 file_.Set(CreateFile(path_.value().c_str(), access, sharing, NULL, | |
| 380 disposition, create_flags, NULL)); | |
| 381 | |
| 382 if (file_.IsValid()) { | |
| 383 error_details_ = FILE_OK; | |
| 384 async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); | |
| 385 | |
| 386 if (flags & (FLAG_OPEN_ALWAYS)) | |
| 387 created_ = (ERROR_ALREADY_EXISTS != GetLastError()); | |
| 388 else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) | |
| 389 created_ = true; | |
| 390 } else { | |
| 391 error_details_ = OSErrorToFileError(GetLastError()); | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 bool File::DoFlush() { | |
| 396 ThreadRestrictions::AssertIOAllowed(); | |
| 397 DCHECK(IsValid()); | |
| 398 return ::FlushFileBuffers(file_.Get()) != FALSE; | |
| 399 } | |
| 400 | |
| 401 void File::SetPlatformFile(PlatformFile file) { | |
| 402 file_.Set(file); | |
| 403 } | |
| 404 | |
| 405 } // namespace base | |
| OLD | NEW |