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