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 <io.h> 8 9 #include "base/files/file_path.h" 10 #include "base/logging.h" 11 #include "base/metrics/sparse_histogram.h" 12 #include "base/threading/thread_restrictions.h" 13 14 namespace base { 15 PlatformFile CreatePlatformFileUnsafe(const FilePath& name, 16 int flags, 17 bool* created, 18 PlatformFileError* error) { 19 base::ThreadRestrictions::AssertIOAllowed(); 20 21 DWORD disposition = 0; 22 if (created) 23 *created = false; 24 25 if (flags & PLATFORM_FILE_OPEN) 26 disposition = OPEN_EXISTING; 27 28 if (flags & PLATFORM_FILE_CREATE) { 29 DCHECK(!disposition); 30 disposition = CREATE_NEW; 31 } 32 33 if (flags & PLATFORM_FILE_OPEN_ALWAYS) { 34 DCHECK(!disposition); 35 disposition = OPEN_ALWAYS; 36 } 37 38 if (flags & PLATFORM_FILE_CREATE_ALWAYS) { 39 DCHECK(!disposition); 40 disposition = CREATE_ALWAYS; 41 } 42 43 if (flags & PLATFORM_FILE_OPEN_TRUNCATED) { 44 DCHECK(!disposition); 45 DCHECK(flags & PLATFORM_FILE_WRITE); 46 disposition = TRUNCATE_EXISTING; 47 } 48 49 if (!disposition) { 50 NOTREACHED(); 51 return NULL; 52 } 53 54 DWORD access = 0; 55 if (flags & PLATFORM_FILE_WRITE) 56 access = GENERIC_WRITE; 57 if (flags & PLATFORM_FILE_APPEND) { 58 DCHECK(!access); 59 access = FILE_APPEND_DATA; 60 } 61 if (flags & PLATFORM_FILE_READ) 62 access |= GENERIC_READ; 63 if (flags & PLATFORM_FILE_WRITE_ATTRIBUTES) 64 access |= FILE_WRITE_ATTRIBUTES; 65 if (flags & PLATFORM_FILE_EXECUTE) 66 access |= GENERIC_EXECUTE; 67 68 DWORD sharing = (flags & PLATFORM_FILE_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; 69 if (!(flags & PLATFORM_FILE_EXCLUSIVE_WRITE)) 70 sharing |= FILE_SHARE_WRITE; 71 if (flags & PLATFORM_FILE_SHARE_DELETE) 72 sharing |= FILE_SHARE_DELETE; 73 74 DWORD create_flags = 0; 75 if (flags & PLATFORM_FILE_ASYNC) 76 create_flags |= FILE_FLAG_OVERLAPPED; 77 if (flags & PLATFORM_FILE_TEMPORARY) 78 create_flags |= FILE_ATTRIBUTE_TEMPORARY; 79 if (flags & PLATFORM_FILE_HIDDEN) 80 create_flags |= FILE_ATTRIBUTE_HIDDEN; 81 if (flags & PLATFORM_FILE_DELETE_ON_CLOSE) 82 create_flags |= FILE_FLAG_DELETE_ON_CLOSE; 83 if (flags & PLATFORM_FILE_BACKUP_SEMANTICS) 84 create_flags |= FILE_FLAG_BACKUP_SEMANTICS; 85 86 HANDLE file = CreateFile(name.value().c_str(), access, sharing, NULL, 87 disposition, create_flags, NULL); 88 89 if (created && (INVALID_HANDLE_VALUE != file)) { 90 if (flags & (PLATFORM_FILE_OPEN_ALWAYS)) 91 *created = (ERROR_ALREADY_EXISTS != GetLastError()); 92 else if (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE)) 93 *created = true; 94 } 95 96 if (error) { 97 if (file != kInvalidPlatformFileValue) 98 *error = PLATFORM_FILE_OK; 99 else 100 *error = LastErrorToPlatformFileError(GetLastError()); 101 } 102 103 return file; 104 } 105 106 FILE* FdopenPlatformFile(PlatformFile file, const char* mode) { 107 if (file == kInvalidPlatformFileValue) 108 return NULL; 109 int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0); 110 if (fd < 0) 111 return NULL; 112 return _fdopen(fd, mode); 113 } 114 115 bool ClosePlatformFile(PlatformFile file) { 116 base::ThreadRestrictions::AssertIOAllowed(); 117 return (CloseHandle(file) != 0); 118 } 119 120 int64 SeekPlatformFile(PlatformFile file, 121 PlatformFileWhence whence, 122 int64 offset) { 123 base::ThreadRestrictions::AssertIOAllowed(); 124 if (file == kInvalidPlatformFileValue || offset < 0) 125 return -1; 126 127 LARGE_INTEGER distance, res; 128 distance.QuadPart = offset; 129 DWORD move_method = static_cast<DWORD>(whence); 130 if (!SetFilePointerEx(file, distance, &res, move_method)) 131 return -1; 132 return res.QuadPart; 133 } 134 135 int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) { 136 base::ThreadRestrictions::AssertIOAllowed(); 137 if (file == kInvalidPlatformFileValue || size < 0) 138 return -1; 139 140 LARGE_INTEGER offset_li; 141 offset_li.QuadPart = offset; 142 143 OVERLAPPED overlapped = {0}; 144 overlapped.Offset = offset_li.LowPart; 145 overlapped.OffsetHigh = offset_li.HighPart; 146 147 DWORD bytes_read; 148 if (::ReadFile(file, data, size, &bytes_read, &overlapped) != 0) 149 return bytes_read; 150 if (ERROR_HANDLE_EOF == GetLastError()) 151 return 0; 152 153 return -1; 154 } 155 156 int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) { 157 base::ThreadRestrictions::AssertIOAllowed(); 158 if (file == kInvalidPlatformFileValue || size < 0) 159 return -1; 160 161 DWORD bytes_read; 162 if (::ReadFile(file, data, size, &bytes_read, NULL) != 0) 163 return bytes_read; 164 if (ERROR_HANDLE_EOF == GetLastError()) 165 return 0; 166 167 return -1; 168 } 169 170 int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, char* data, 171 int size) { 172 return ReadPlatformFile(file, offset, data, size); 173 } 174 175 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file, 176 char* data, int size) { 177 return ReadPlatformFileAtCurrentPos(file, data, size); 178 } 179 180 int WritePlatformFile(PlatformFile file, int64 offset, 181 const char* data, int size) { 182 base::ThreadRestrictions::AssertIOAllowed(); 183 if (file == kInvalidPlatformFileValue) 184 return -1; 185 186 LARGE_INTEGER offset_li; 187 offset_li.QuadPart = offset; 188 189 OVERLAPPED overlapped = {0}; 190 overlapped.Offset = offset_li.LowPart; 191 overlapped.OffsetHigh = offset_li.HighPart; 192 193 DWORD bytes_written; 194 if (::WriteFile(file, data, size, &bytes_written, &overlapped) != 0) 195 return bytes_written; 196 197 return -1; 198 } 199 200 int WritePlatformFileAtCurrentPos(PlatformFile file, const char* data, 201 int size) { 202 return WritePlatformFile(file, 0, data, size); 203 } 204 205 int WritePlatformFileCurPosNoBestEffort(PlatformFile file, 206 const char* data, int size) { 207 return WritePlatformFile(file, 0, data, size); 208 } 209 210 bool TruncatePlatformFile(PlatformFile file, int64 length) { 211 base::ThreadRestrictions::AssertIOAllowed(); 212 if (file == kInvalidPlatformFileValue) 213 return false; 214 215 // Get the current file pointer. 216 LARGE_INTEGER file_pointer; 217 LARGE_INTEGER zero; 218 zero.QuadPart = 0; 219 if (::SetFilePointerEx(file, zero, &file_pointer, FILE_CURRENT) == 0) 220 return false; 221 222 LARGE_INTEGER length_li; 223 length_li.QuadPart = length; 224 // If length > file size, SetFilePointerEx() should extend the file 225 // with zeroes on all Windows standard file systems (NTFS, FATxx). 226 if (!::SetFilePointerEx(file, length_li, NULL, FILE_BEGIN)) 227 return false; 228 229 // Set the new file length and move the file pointer to its old position. 230 // This is consistent with ftruncate()'s behavior, even when the file 231 // pointer points to a location beyond the end of the file. 232 return ((::SetEndOfFile(file) != 0) && 233 (::SetFilePointerEx(file, file_pointer, NULL, FILE_BEGIN) != 0)); 234 } 235 236 bool FlushPlatformFile(PlatformFile file) { 237 base::ThreadRestrictions::AssertIOAllowed(); 238 return ((file != kInvalidPlatformFileValue) && ::FlushFileBuffers(file)); 239 } 240 241 bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time, 242 const base::Time& last_modified_time) { 243 base::ThreadRestrictions::AssertIOAllowed(); 244 if (file == kInvalidPlatformFileValue) 245 return false; 246 247 FILETIME last_access_filetime = last_access_time.ToFileTime(); 248 FILETIME last_modified_filetime = last_modified_time.ToFileTime(); 249 return (::SetFileTime(file, NULL, &last_access_filetime, 250 &last_modified_filetime) != 0); 251 } 252 253 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { 254 base::ThreadRestrictions::AssertIOAllowed(); 255 if (!info) 256 return false; 257 258 BY_HANDLE_FILE_INFORMATION file_info; 259 if (GetFileInformationByHandle(file, &file_info) == 0) 260 return false; 261 262 LARGE_INTEGER size; 263 size.HighPart = file_info.nFileSizeHigh; 264 size.LowPart = file_info.nFileSizeLow; 265 info->size = size.QuadPart; 266 info->is_directory = 267 (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 268 info->is_symbolic_link = false; // Windows doesn't have symbolic links. 269 info->last_modified = base::Time::FromFileTime(file_info.ftLastWriteTime); 270 info->last_accessed = base::Time::FromFileTime(file_info.ftLastAccessTime); 271 info->creation_time = base::Time::FromFileTime(file_info.ftCreationTime); 272 return true; 273 } 274 275 PlatformFileError LockPlatformFile(PlatformFile file) { 276 BOOL result = LockFile(file, 0, 0, MAXDWORD, MAXDWORD); 277 if (!result) 278 return LastErrorToPlatformFileError(GetLastError()); 279 return PLATFORM_FILE_OK; 280 } 281 282 PlatformFileError UnlockPlatformFile(PlatformFile file) { 283 BOOL result = UnlockFile(file, 0, 0, MAXDWORD, MAXDWORD); 284 if (!result) 285 return LastErrorToPlatformFileError(GetLastError()); 286 return PLATFORM_FILE_OK; 287 } 288 289 PlatformFileError LastErrorToPlatformFileError(DWORD last_error) { 290 switch (last_error) { 291 case ERROR_SHARING_VIOLATION: 292 return PLATFORM_FILE_ERROR_IN_USE; 293 case ERROR_FILE_EXISTS: 294 return PLATFORM_FILE_ERROR_EXISTS; 295 case ERROR_FILE_NOT_FOUND: 296 case ERROR_PATH_NOT_FOUND: 297 return PLATFORM_FILE_ERROR_NOT_FOUND; 298 case ERROR_ACCESS_DENIED: 299 return PLATFORM_FILE_ERROR_ACCESS_DENIED; 300 case ERROR_TOO_MANY_OPEN_FILES: 301 return PLATFORM_FILE_ERROR_TOO_MANY_OPENED; 302 case ERROR_OUTOFMEMORY: 303 case ERROR_NOT_ENOUGH_MEMORY: 304 return PLATFORM_FILE_ERROR_NO_MEMORY; 305 case ERROR_HANDLE_DISK_FULL: 306 case ERROR_DISK_FULL: 307 case ERROR_DISK_RESOURCES_EXHAUSTED: 308 return PLATFORM_FILE_ERROR_NO_SPACE; 309 case ERROR_USER_MAPPED_FILE: 310 return PLATFORM_FILE_ERROR_INVALID_OPERATION; 311 case ERROR_NOT_READY: 312 case ERROR_SECTOR_NOT_FOUND: 313 case ERROR_DEV_NOT_EXIST: 314 case ERROR_IO_DEVICE: 315 case ERROR_FILE_CORRUPT: 316 case ERROR_DISK_CORRUPT: 317 return PLATFORM_FILE_ERROR_IO; 318 default: 319 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Windows", 320 last_error); 321 return PLATFORM_FILE_ERROR_FAILED; 322 } 323 } 324 325 } // namespace base 326