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.
5 #include "base/files/file.h"
9 #include "base/logging.h"
10 #include "base/metrics/sparse_histogram.h"
11 #include "base/threading/thread_restrictions.h"
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
);
20 bool File::IsValid() const {
21 return file_
.IsValid();
24 PlatformFile
File::GetPlatformFile() const {
28 PlatformFile
File::TakePlatformFile() {
36 ThreadRestrictions::AssertIOAllowed();
37 SCOPED_FILE_TRACE("Close");
41 int64
File::Seek(Whence whence
, int64 offset
) {
42 ThreadRestrictions::AssertIOAllowed();
45 SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset
);
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
))
55 int File::Read(int64 offset
, char* data
, int size
) {
56 ThreadRestrictions::AssertIOAllowed();
62 SCOPED_FILE_TRACE_WITH_SIZE("Read", size
);
64 LARGE_INTEGER offset_li
;
65 offset_li
.QuadPart
= offset
;
67 OVERLAPPED overlapped
= {0};
68 overlapped
.Offset
= offset_li
.LowPart
;
69 overlapped
.OffsetHigh
= offset_li
.HighPart
;
72 if (::ReadFile(file_
.Get(), data
, size
, &bytes_read
, &overlapped
))
74 if (ERROR_HANDLE_EOF
== GetLastError())
80 int File::ReadAtCurrentPos(char* data
, int size
) {
81 ThreadRestrictions::AssertIOAllowed();
87 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size
);
90 if (::ReadFile(file_
.Get(), data
, size
, &bytes_read
, NULL
))
92 if (ERROR_HANDLE_EOF
== GetLastError())
98 int File::ReadNoBestEffort(int64 offset
, char* data
, int size
) {
99 // TODO(dbeam): trace this separately?
100 return Read(offset
, data
, size
);
103 int File::ReadAtCurrentPosNoBestEffort(char* data
, int size
) {
104 // TODO(dbeam): trace this separately?
105 return ReadAtCurrentPos(data
, size
);
108 int File::Write(int64 offset
, const char* data
, int size
) {
109 ThreadRestrictions::AssertIOAllowed();
113 SCOPED_FILE_TRACE_WITH_SIZE("Write", size
);
115 LARGE_INTEGER offset_li
;
116 offset_li
.QuadPart
= offset
;
118 OVERLAPPED overlapped
= {0};
119 overlapped
.Offset
= offset_li
.LowPart
;
120 overlapped
.OffsetHigh
= offset_li
.HighPart
;
123 if (::WriteFile(file_
.Get(), data
, size
, &bytes_written
, &overlapped
))
124 return bytes_written
;
129 int File::WriteAtCurrentPos(const char* data
, int size
) {
130 ThreadRestrictions::AssertIOAllowed();
136 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size
);
139 if (::WriteFile(file_
.Get(), data
, size
, &bytes_written
, NULL
))
140 return bytes_written
;
145 int File::WriteAtCurrentPosNoBestEffort(const char* data
, int size
) {
146 return WriteAtCurrentPos(data
, size
);
149 int64
File::GetLength() {
150 ThreadRestrictions::AssertIOAllowed();
153 SCOPED_FILE_TRACE("GetLength");
156 if (!::GetFileSizeEx(file_
.Get(), &size
))
159 return static_cast<int64
>(size
.QuadPart
);
162 bool File::SetLength(int64 length
) {
163 ThreadRestrictions::AssertIOAllowed();
166 SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length
);
168 // Get the current file pointer.
169 LARGE_INTEGER file_pointer
;
172 if (!::SetFilePointerEx(file_
.Get(), zero
, &file_pointer
, FILE_CURRENT
))
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
))
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
) !=
193 bool File::SetTimes(Time last_access_time
, Time last_modified_time
) {
194 ThreadRestrictions::AssertIOAllowed();
197 SCOPED_FILE_TRACE("SetTimes");
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
);
205 bool File::GetInfo(Info
* info
) {
206 ThreadRestrictions::AssertIOAllowed();
209 SCOPED_FILE_TRACE("GetInfo");
211 BY_HANDLE_FILE_INFORMATION file_info
;
212 if (!GetFileInformationByHandle(file_
.Get(), &file_info
))
216 size
.HighPart
= file_info
.nFileSizeHigh
;
217 size
.LowPart
= file_info
.nFileSizeLow
;
218 info
->size
= size
.QuadPart
;
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
);
228 File::Error
File::Lock() {
231 SCOPED_FILE_TRACE("Lock");
233 BOOL result
= LockFile(file_
.Get(), 0, 0, MAXDWORD
, MAXDWORD
);
235 return OSErrorToFileError(GetLastError());
239 File::Error
File::Unlock() {
242 SCOPED_FILE_TRACE("Unlock");
244 BOOL result
= UnlockFile(file_
.Get(), 0, 0, MAXDWORD
, MAXDWORD
);
246 return OSErrorToFileError(GetLastError());
250 File
File::Duplicate() {
254 SCOPED_FILE_TRACE("Duplicate");
256 HANDLE other_handle
= nullptr;
258 if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle
260 GetCurrentProcess(), // hTargetProcessHandle
262 0, // dwDesiredAccess ignored due to SAME_ACCESS
263 FALSE
, // !bInheritHandle
264 DUPLICATE_SAME_ACCESS
)) {
265 return File(OSErrorToFileError(GetLastError()));
268 File
other(other_handle
);
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
;
305 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Windows",
307 return FILE_ERROR_FAILED
;
311 void File::DoInitialize(uint32 flags
) {
312 ThreadRestrictions::AssertIOAllowed();
315 DWORD disposition
= 0;
317 if (flags
& FLAG_OPEN
)
318 disposition
= OPEN_EXISTING
;
320 if (flags
& FLAG_CREATE
) {
321 DCHECK(!disposition
);
322 disposition
= CREATE_NEW
;
325 if (flags
& FLAG_OPEN_ALWAYS
) {
326 DCHECK(!disposition
);
327 disposition
= OPEN_ALWAYS
;
330 if (flags
& FLAG_CREATE_ALWAYS
) {
331 DCHECK(!disposition
);
332 DCHECK(flags
& FLAG_WRITE
);
333 disposition
= CREATE_ALWAYS
;
336 if (flags
& FLAG_OPEN_TRUNCATED
) {
337 DCHECK(!disposition
);
338 DCHECK(flags
& FLAG_WRITE
);
339 disposition
= TRUNCATE_EXISTING
;
348 if (flags
& FLAG_WRITE
)
349 access
= GENERIC_WRITE
;
350 if (flags
& FLAG_APPEND
) {
352 access
= FILE_APPEND_DATA
;
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
;
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
;
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
;
379 file_
.Set(CreateFile(path_
.value().c_str(), access
, sharing
, NULL
,
380 disposition
, create_flags
, NULL
));
382 if (file_
.IsValid()) {
383 error_details_
= FILE_OK
;
384 async_
= ((flags
& FLAG_ASYNC
) == FLAG_ASYNC
);
386 if (flags
& (FLAG_OPEN_ALWAYS
))
387 created_
= (ERROR_ALREADY_EXISTS
!= GetLastError());
388 else if (flags
& (FLAG_CREATE_ALWAYS
| FLAG_CREATE
))
391 error_details_
= OSErrorToFileError(GetLastError());
395 bool File::DoFlush() {
396 ThreadRestrictions::AssertIOAllowed();
398 return ::FlushFileBuffers(file_
.Get()) != FALSE
;
401 void File::SetPlatformFile(PlatformFile file
) {