1 // Copyright 2012 The Chromium Authors
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 <windows.h>
8
9 #include <io.h>
10 #include <stdint.h>
11
12 #include <tuple>
13
14 #include "base/check.h"
15 #include "base/check_op.h"
16 #include "base/files/file_util.h"
17 #include "base/immediate_crash.h"
18 #include "base/metrics/histogram_functions.h"
19 #include "base/notreached.h"
20 #include "base/strings/string_util.h"
21 #include "base/threading/scoped_blocking_call.h"
22
23 namespace base {
24
25 // Make sure our Whence mappings match the system headers.
26 static_assert(File::FROM_BEGIN == FILE_BEGIN &&
27 File::FROM_CURRENT == FILE_CURRENT &&
28 File::FROM_END == FILE_END,
29 "whence mapping must match the system headers");
30
IsValid() const31 bool File::IsValid() const {
32 return file_.is_valid();
33 }
34
GetPlatformFile() const35 PlatformFile File::GetPlatformFile() const {
36 return file_.get();
37 }
38
TakePlatformFile()39 PlatformFile File::TakePlatformFile() {
40 return file_.release();
41 }
42
Close()43 void File::Close() {
44 if (!file_.is_valid())
45 return;
46
47 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
48 SCOPED_FILE_TRACE("Close");
49 file_.Close();
50 }
51
Seek(Whence whence,int64_t offset)52 int64_t File::Seek(Whence whence, int64_t offset) {
53 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
54 DCHECK(IsValid());
55
56 SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset);
57
58 LARGE_INTEGER distance, res;
59 distance.QuadPart = offset;
60 DWORD move_method = static_cast<DWORD>(whence);
61 if (!SetFilePointerEx(file_.get(), distance, &res, move_method))
62 return -1;
63 return res.QuadPart;
64 }
65
Read(int64_t offset,char * data,int size)66 int File::Read(int64_t offset, char* data, int size) {
67 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
68 DCHECK(IsValid());
69 DCHECK(!async_);
70 if (size < 0 || offset < 0)
71 return -1;
72
73 SCOPED_FILE_TRACE_WITH_SIZE("Read", size);
74
75 ULARGE_INTEGER offset_li;
76 offset_li.QuadPart = static_cast<uint64_t>(offset);
77
78 OVERLAPPED overlapped = {};
79 overlapped.Offset = offset_li.LowPart;
80 overlapped.OffsetHigh = offset_li.HighPart;
81
82 DWORD bytes_read;
83 if (::ReadFile(file_.get(), data, static_cast<DWORD>(size), &bytes_read,
84 &overlapped)) {
85 // TODO(crbug.com/40227936): Change to return some type with a uint64_t size
86 // and eliminate this cast.
87 return checked_cast<int>(bytes_read);
88 }
89 if (ERROR_HANDLE_EOF == GetLastError())
90 return 0;
91
92 return -1;
93 }
94
ReadAtCurrentPos(char * data,int size)95 int File::ReadAtCurrentPos(char* data, int size) {
96 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
97 DCHECK(IsValid());
98 DCHECK(!async_);
99 if (size < 0)
100 return -1;
101
102 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size);
103
104 DWORD bytes_read;
105 if (::ReadFile(file_.get(), data, static_cast<DWORD>(size), &bytes_read,
106 NULL)) {
107 // TODO(crbug.com/40227936): Change to return some type with a uint64_t size
108 // and eliminate this cast.
109 return checked_cast<int>(bytes_read);
110 }
111 if (ERROR_HANDLE_EOF == GetLastError())
112 return 0;
113
114 return -1;
115 }
116
ReadNoBestEffort(int64_t offset,char * data,int size)117 int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
118 // TODO(dbeam): trace this separately?
119 // SAFETY: required from caller, enforced by UNSAFE_BUFFER_USAGE in header.
120 return UNSAFE_BUFFERS(Read(offset, data, size));
121 }
122
ReadAtCurrentPosNoBestEffort(char * data,int size)123 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
124 // TODO(dbeam): trace this separately?
125 // SAFETY: required from caller, enforced by UNSAFE_BUFFER_USAGE in header.
126 return UNSAFE_BUFFERS(ReadAtCurrentPos(data, size));
127 }
128
Write(int64_t offset,const char * data,int size)129 int File::Write(int64_t offset, const char* data, int size) {
130 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
131 DCHECK(IsValid());
132 DCHECK(!async_);
133 if (size < 0 || offset < 0)
134 return -1;
135
136 SCOPED_FILE_TRACE_WITH_SIZE("Write", size);
137
138 ULARGE_INTEGER offset_li;
139 offset_li.QuadPart = static_cast<uint64_t>(offset);
140
141 OVERLAPPED overlapped = {};
142 overlapped.Offset = offset_li.LowPart;
143 overlapped.OffsetHigh = offset_li.HighPart;
144
145 DWORD bytes_written;
146 if (::WriteFile(file_.get(), data, static_cast<DWORD>(size), &bytes_written,
147 &overlapped))
148 return static_cast<int>(bytes_written);
149
150 return -1;
151 }
152
WriteAtCurrentPos(const char * data,int size)153 int File::WriteAtCurrentPos(const char* data, int size) {
154 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
155 DCHECK(IsValid());
156 DCHECK(!async_);
157 if (size < 0)
158 return -1;
159
160 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size);
161
162 DWORD bytes_written;
163 if (::WriteFile(file_.get(), data, static_cast<DWORD>(size), &bytes_written,
164 NULL))
165 return static_cast<int>(bytes_written);
166
167 return -1;
168 }
169
WriteAtCurrentPosNoBestEffort(const char * data,int size)170 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
171 // SAFETY: required from caller, enforced by UNSAFE_BUFFER_USAGE in header.
172 return UNSAFE_BUFFERS(WriteAtCurrentPos(data, size));
173 }
174
GetLength() const175 int64_t File::GetLength() const {
176 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
177 DCHECK(IsValid());
178
179 SCOPED_FILE_TRACE("GetLength");
180
181 LARGE_INTEGER size;
182 if (!::GetFileSizeEx(file_.get(), &size))
183 return -1;
184
185 return static_cast<int64_t>(size.QuadPart);
186 }
187
SetLength(int64_t length)188 bool File::SetLength(int64_t length) {
189 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
190 DCHECK(IsValid());
191
192 SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length);
193
194 // Get the current file pointer.
195 LARGE_INTEGER file_pointer;
196 LARGE_INTEGER zero;
197 zero.QuadPart = 0;
198 if (!::SetFilePointerEx(file_.get(), zero, &file_pointer, FILE_CURRENT))
199 return false;
200
201 LARGE_INTEGER length_li;
202 length_li.QuadPart = length;
203 // If length > file size, SetFilePointerEx() should extend the file
204 // with zeroes on all Windows standard file systems (NTFS, FATxx).
205 if (!::SetFilePointerEx(file_.get(), length_li, NULL, FILE_BEGIN))
206 return false;
207
208 // Set the new file length and move the file pointer to its old position.
209 // This is consistent with ftruncate()'s behavior, even when the file
210 // pointer points to a location beyond the end of the file.
211 // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not
212 // promised by the interface (nor was promised by PlatformFile). See if this
213 // implementation detail can be removed.
214 return ((::SetEndOfFile(file_.get()) != FALSE) &&
215 (::SetFilePointerEx(file_.get(), file_pointer, NULL, FILE_BEGIN) !=
216 FALSE));
217 }
218
SetTimes(Time last_access_time,Time last_modified_time)219 bool File::SetTimes(Time last_access_time, Time last_modified_time) {
220 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
221 DCHECK(IsValid());
222
223 SCOPED_FILE_TRACE("SetTimes");
224
225 FILETIME last_access_filetime = last_access_time.ToFileTime();
226 FILETIME last_modified_filetime = last_modified_time.ToFileTime();
227 return (::SetFileTime(file_.get(), NULL, &last_access_filetime,
228 &last_modified_filetime) != FALSE);
229 }
230
GetInfo(Info * info) const231 bool File::GetInfo(Info* info) const {
232 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
233 DCHECK(IsValid());
234
235 SCOPED_FILE_TRACE("GetInfo");
236
237 BY_HANDLE_FILE_INFORMATION file_info;
238 if (!GetFileInformationByHandle(file_.get(), &file_info))
239 return false;
240
241 ULARGE_INTEGER size;
242 size.HighPart = file_info.nFileSizeHigh;
243 size.LowPart = file_info.nFileSizeLow;
244 // TODO(crbug.com/40227936): Change Info::size to uint64_t and eliminate this
245 // cast.
246 info->size = checked_cast<int64_t>(size.QuadPart);
247 info->is_directory =
248 (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
249 info->is_symbolic_link = false; // Windows doesn't have symbolic links.
250 info->last_modified = Time::FromFileTime(file_info.ftLastWriteTime);
251 info->last_accessed = Time::FromFileTime(file_info.ftLastAccessTime);
252 info->creation_time = Time::FromFileTime(file_info.ftCreationTime);
253 return true;
254 }
255
256 namespace {
257
LockFileFlagsForMode(File::LockMode mode)258 DWORD LockFileFlagsForMode(File::LockMode mode) {
259 DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
260 switch (mode) {
261 case File::LockMode::kShared:
262 return flags;
263 case File::LockMode::kExclusive:
264 return flags | LOCKFILE_EXCLUSIVE_LOCK;
265 }
266 NOTREACHED();
267 }
268
269 } // namespace
270
Lock(File::LockMode mode)271 File::Error File::Lock(File::LockMode mode) {
272 DCHECK(IsValid());
273
274 SCOPED_FILE_TRACE("Lock");
275
276 OVERLAPPED overlapped = {};
277 BOOL result =
278 LockFileEx(file_.get(), LockFileFlagsForMode(mode), /*dwReserved=*/0,
279 /*nNumberOfBytesToLockLow=*/MAXDWORD,
280 /*nNumberOfBytesToLockHigh=*/MAXDWORD, &overlapped);
281 if (!result)
282 return GetLastFileError();
283 return FILE_OK;
284 }
285
Unlock()286 File::Error File::Unlock() {
287 DCHECK(IsValid());
288
289 SCOPED_FILE_TRACE("Unlock");
290
291 OVERLAPPED overlapped = {};
292 BOOL result =
293 UnlockFileEx(file_.get(), /*dwReserved=*/0,
294 /*nNumberOfBytesToLockLow=*/MAXDWORD,
295 /*nNumberOfBytesToLockHigh=*/MAXDWORD, &overlapped);
296 if (!result)
297 return GetLastFileError();
298 return FILE_OK;
299 }
300
Duplicate() const301 File File::Duplicate() const {
302 if (!IsValid())
303 return File();
304
305 SCOPED_FILE_TRACE("Duplicate");
306
307 HANDLE other_handle = nullptr;
308
309 if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle
310 GetPlatformFile(),
311 GetCurrentProcess(), // hTargetProcessHandle
312 &other_handle,
313 0, // dwDesiredAccess ignored due to SAME_ACCESS
314 FALSE, // !bInheritHandle
315 DUPLICATE_SAME_ACCESS)) {
316 return File(GetLastFileError());
317 }
318
319 return File(ScopedPlatformFile(other_handle), async());
320 }
321
DeleteOnClose(bool delete_on_close)322 bool File::DeleteOnClose(bool delete_on_close) {
323 FILE_DISPOSITION_INFO disposition = {delete_on_close};
324 return ::SetFileInformationByHandle(GetPlatformFile(), FileDispositionInfo,
325 &disposition, sizeof(disposition)) != 0;
326 }
327
328 // Static.
OSErrorToFileError(DWORD last_error)329 File::Error File::OSErrorToFileError(DWORD last_error) {
330 switch (last_error) {
331 case ERROR_SHARING_VIOLATION:
332 case ERROR_UNABLE_TO_REMOVE_REPLACED: // ReplaceFile failure cases.
333 case ERROR_UNABLE_TO_MOVE_REPLACEMENT:
334 case ERROR_UNABLE_TO_MOVE_REPLACEMENT_2:
335 return FILE_ERROR_IN_USE;
336 case ERROR_ALREADY_EXISTS:
337 case ERROR_FILE_EXISTS:
338 return FILE_ERROR_EXISTS;
339 case ERROR_FILE_NOT_FOUND:
340 case ERROR_PATH_NOT_FOUND:
341 return FILE_ERROR_NOT_FOUND;
342 case ERROR_ACCESS_DENIED:
343 case ERROR_LOCK_VIOLATION:
344 return FILE_ERROR_ACCESS_DENIED;
345 case ERROR_TOO_MANY_OPEN_FILES:
346 return FILE_ERROR_TOO_MANY_OPENED;
347 case ERROR_OUTOFMEMORY:
348 case ERROR_NOT_ENOUGH_MEMORY:
349 return FILE_ERROR_NO_MEMORY;
350 case ERROR_HANDLE_DISK_FULL:
351 case ERROR_DISK_FULL:
352 case ERROR_DISK_RESOURCES_EXHAUSTED:
353 return FILE_ERROR_NO_SPACE;
354 case ERROR_USER_MAPPED_FILE:
355 return FILE_ERROR_INVALID_OPERATION;
356 case ERROR_NOT_READY: // The device is not ready.
357 case ERROR_SECTOR_NOT_FOUND: // The drive cannot find the sector requested.
358 case ERROR_GEN_FAILURE: // A device ... is not functioning.
359 case ERROR_DEV_NOT_EXIST: // Net resource or device is no longer available.
360 case ERROR_IO_DEVICE:
361 case ERROR_DISK_OPERATION_FAILED:
362 case ERROR_FILE_CORRUPT: // File or directory is corrupted and unreadable.
363 case ERROR_DISK_CORRUPT: // The disk structure is corrupted and unreadable.
364 return FILE_ERROR_IO;
365 default:
366 // This function should only be called for errors.
367 DCHECK_NE(static_cast<DWORD>(ERROR_SUCCESS), last_error);
368 return FILE_ERROR_FAILED;
369 }
370 }
371
DoInitialize(const FilePath & path,uint32_t flags)372 void File::DoInitialize(const FilePath& path, uint32_t flags) {
373 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
374 DCHECK(!IsValid());
375
376 DWORD disposition = 0;
377
378 if (flags & FLAG_OPEN)
379 disposition = OPEN_EXISTING;
380
381 if (flags & FLAG_CREATE) {
382 DCHECK(!disposition);
383 disposition = CREATE_NEW;
384 }
385
386 if (flags & FLAG_OPEN_ALWAYS) {
387 DCHECK(!disposition);
388 disposition = OPEN_ALWAYS;
389 }
390
391 if (flags & FLAG_CREATE_ALWAYS) {
392 DCHECK(!disposition);
393 DCHECK(flags & FLAG_WRITE);
394 disposition = CREATE_ALWAYS;
395 }
396
397 if (flags & FLAG_OPEN_TRUNCATED) {
398 DCHECK(!disposition);
399 DCHECK(flags & FLAG_WRITE);
400 disposition = TRUNCATE_EXISTING;
401 }
402
403 CHECK(disposition);
404
405 DWORD access = 0;
406 if (flags & FLAG_WRITE)
407 access = GENERIC_WRITE;
408 if (flags & FLAG_APPEND) {
409 DCHECK(!access);
410 access = FILE_APPEND_DATA;
411 }
412 if (flags & FLAG_READ)
413 access |= GENERIC_READ;
414 if (flags & FLAG_WRITE_ATTRIBUTES)
415 access |= FILE_WRITE_ATTRIBUTES;
416 if (flags & FLAG_WIN_EXECUTE) {
417 // Specifying both FLAG_WIN_EXECUTE and FLAG_WIN_NO_EXECUTE would
418 // constitute a security risk, so deny the access here.
419 CHECK_EQ(flags & FLAG_WIN_NO_EXECUTE, 0U);
420 access |= GENERIC_EXECUTE;
421 }
422 if (flags & FLAG_CAN_DELETE_ON_CLOSE)
423 access |= DELETE;
424
425 DWORD sharing = (flags & FLAG_WIN_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
426 if (!(flags & FLAG_WIN_EXCLUSIVE_WRITE))
427 sharing |= FILE_SHARE_WRITE;
428 if (flags & FLAG_WIN_SHARE_DELETE)
429 sharing |= FILE_SHARE_DELETE;
430
431 DWORD create_flags = 0;
432 if (flags & FLAG_ASYNC)
433 create_flags |= FILE_FLAG_OVERLAPPED;
434 if (flags & FLAG_WIN_TEMPORARY)
435 create_flags |= FILE_ATTRIBUTE_TEMPORARY;
436 if (flags & FLAG_WIN_HIDDEN)
437 create_flags |= FILE_ATTRIBUTE_HIDDEN;
438 if (flags & FLAG_DELETE_ON_CLOSE)
439 create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
440 if (flags & FLAG_WIN_BACKUP_SEMANTICS)
441 create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
442 if (flags & FLAG_WIN_SEQUENTIAL_SCAN)
443 create_flags |= FILE_FLAG_SEQUENTIAL_SCAN;
444
445 file_.Set(CreateFile(path.value().c_str(), access, sharing, NULL, disposition,
446 create_flags, NULL));
447
448 if (file_.is_valid()) {
449 error_details_ = FILE_OK;
450 async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
451
452 if (flags & (FLAG_OPEN_ALWAYS))
453 created_ = (ERROR_ALREADY_EXISTS != GetLastError());
454 else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
455 created_ = true;
456 if (flags & FLAG_WIN_NO_EXECUTE) {
457 // These two DCHECKs make sure that no callers are trying to remove
458 // execute permission from a file that might need to be mapped executable
459 // later. If they hit in code then the file should not have
460 // FLAG_WIN_NO_EXECUTE flag, but this will mean that the file cannot be
461 // passed to renderers.
462 DCHECK(!base::FilePath::CompareEqualIgnoreCase(FILE_PATH_LITERAL(".exe"),
463 path.Extension()));
464 DCHECK(!base::FilePath::CompareEqualIgnoreCase(FILE_PATH_LITERAL(".dll"),
465 path.Extension()));
466
467 // It is possible that the ACE could not be added if the file was created
468 // in a path for which the caller does not have WRITE_DAC access. In this
469 // case, ignore the error since if this is occurring then it's likely the
470 // file cannot be opened for write and more serious I/O failures are
471 // occurring or about to occur.
472 std::ignore = PreventExecuteMapping(path);
473 }
474 } else {
475 error_details_ = GetLastFileError();
476 }
477 }
478
Flush()479 bool File::Flush() {
480 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
481 DCHECK(IsValid());
482 SCOPED_FILE_TRACE("Flush");
483
484 // On Windows 8 and above, FlushFileBuffers is guaranteed to flush the storage
485 // device's internal buffers (if they exist) before returning.
486 // https://blogs.msdn.microsoft.com/oldnewthing/20170510-00/?p=95505
487 return ::FlushFileBuffers(file_.get()) != FALSE;
488 }
489
SetPlatformFile(PlatformFile file)490 void File::SetPlatformFile(PlatformFile file) {
491 file_.Set(file);
492 }
493
494 // static
GetLastFileError()495 File::Error File::GetLastFileError() {
496 return File::OSErrorToFileError(GetLastError());
497 }
498
499 } // namespace base
500