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 #include <stdint.h>
9
10 #include "base/logging.h"
11 #include "base/win/win_util.h"
12
13 #include <windows.h>
14
15 namespace base {
16
17 // Make sure our Whence mappings match the system headers.
18 static_assert(File::FROM_BEGIN == FILE_BEGIN &&
19 File::FROM_CURRENT == FILE_CURRENT &&
20 File::FROM_END == FILE_END,
21 "whence mapping must match the system headers");
22
IsValid() const23 bool File::IsValid() const {
24 return file_.IsValid();
25 }
26
GetPlatformFile() const27 PlatformFile File::GetPlatformFile() const {
28 return file_.Get();
29 }
30
TakePlatformFile()31 PlatformFile File::TakePlatformFile() {
32 return file_.Take();
33 }
34
Close()35 void File::Close() {
36 if (!file_.IsValid())
37 return;
38
39 file_.Close();
40 }
41
Seek(Whence whence,int64_t offset)42 int64_t File::Seek(Whence whence, int64_t offset) {
43 DCHECK(IsValid());
44
45 LARGE_INTEGER distance, res;
46 distance.QuadPart = offset;
47 DWORD move_method = static_cast<DWORD>(whence);
48 if (!SetFilePointerEx(file_.Get(), distance, &res, move_method))
49 return -1;
50 return res.QuadPart;
51 }
52
Read(int64_t offset,char * data,int size)53 int File::Read(int64_t offset, char* data, int size) {
54 DCHECK(IsValid());
55 if (size < 0)
56 return -1;
57
58 LARGE_INTEGER offset_li;
59 offset_li.QuadPart = offset;
60
61 OVERLAPPED overlapped = {};
62 overlapped.Offset = offset_li.LowPart;
63 overlapped.OffsetHigh = offset_li.HighPart;
64
65 DWORD bytes_read;
66 if (::ReadFile(file_.Get(), data, size, &bytes_read, &overlapped))
67 return bytes_read;
68 if (ERROR_HANDLE_EOF == GetLastError())
69 return 0;
70
71 return -1;
72 }
73
ReadAtCurrentPos(char * data,int size)74 int File::ReadAtCurrentPos(char* data, int size) {
75 DCHECK(IsValid());
76 if (size < 0)
77 return -1;
78
79 DWORD bytes_read;
80 if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL))
81 return bytes_read;
82 if (ERROR_HANDLE_EOF == GetLastError())
83 return 0;
84
85 return -1;
86 }
87
ReadNoBestEffort(int64_t offset,char * data,int size)88 int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
89 // TODO(dbeam): trace this separately?
90 return Read(offset, data, size);
91 }
92
ReadAtCurrentPosNoBestEffort(char * data,int size)93 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
94 // TODO(dbeam): trace this separately?
95 return ReadAtCurrentPos(data, size);
96 }
97
Write(int64_t offset,const char * data,int size)98 int File::Write(int64_t offset, const char* data, int size) {
99 DCHECK(IsValid());
100
101 LARGE_INTEGER offset_li;
102 offset_li.QuadPart = offset;
103
104 OVERLAPPED overlapped = {};
105 overlapped.Offset = offset_li.LowPart;
106 overlapped.OffsetHigh = offset_li.HighPart;
107
108 DWORD bytes_written;
109 if (::WriteFile(file_.Get(), data, size, &bytes_written, &overlapped))
110 return bytes_written;
111
112 return -1;
113 }
114
WriteAtCurrentPos(const char * data,int size)115 int File::WriteAtCurrentPos(const char* data, int size) {
116 DCHECK(IsValid());
117 if (size < 0)
118 return -1;
119
120 DWORD bytes_written;
121 if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL))
122 return bytes_written;
123
124 return -1;
125 }
126
WriteAtCurrentPosNoBestEffort(const char * data,int size)127 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
128 return WriteAtCurrentPos(data, size);
129 }
130
GetLength()131 int64_t File::GetLength() {
132 DCHECK(IsValid());
133
134 LARGE_INTEGER size;
135 if (!::GetFileSizeEx(file_.Get(), &size))
136 return -1;
137
138 return static_cast<int64_t>(size.QuadPart);
139 }
140
SetLength(int64_t length)141 bool File::SetLength(int64_t length) {
142 DCHECK(IsValid());
143
144 // Get the current file pointer.
145 LARGE_INTEGER file_pointer;
146 LARGE_INTEGER zero;
147 zero.QuadPart = 0;
148 if (!::SetFilePointerEx(file_.Get(), zero, &file_pointer, FILE_CURRENT))
149 return false;
150
151 LARGE_INTEGER length_li;
152 length_li.QuadPart = length;
153 // If length > file size, SetFilePointerEx() should extend the file
154 // with zeroes on all Windows standard file systems (NTFS, FATxx).
155 if (!::SetFilePointerEx(file_.Get(), length_li, NULL, FILE_BEGIN))
156 return false;
157
158 // Set the new file length and move the file pointer to its old position.
159 // This is consistent with ftruncate()'s behavior, even when the file
160 // pointer points to a location beyond the end of the file.
161 // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not
162 // promised by the interface (nor was promised by PlatformFile). See if this
163 // implementation detail can be removed.
164 return ((::SetEndOfFile(file_.Get()) != FALSE) &&
165 (::SetFilePointerEx(file_.Get(), file_pointer, NULL, FILE_BEGIN) !=
166 FALSE));
167 }
168
GetInfo(Info * info)169 bool File::GetInfo(Info* info) {
170 DCHECK(IsValid());
171
172 BY_HANDLE_FILE_INFORMATION file_info;
173 if (!GetFileInformationByHandle(file_.Get(), &file_info))
174 return false;
175
176 LARGE_INTEGER size;
177 size.HighPart = file_info.nFileSizeHigh;
178 size.LowPart = file_info.nFileSizeLow;
179 info->size = size.QuadPart;
180 info->is_directory =
181 (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
182 info->is_symbolic_link = false; // Windows doesn't have symbolic links.
183 info->last_modified =
184 *reinterpret_cast<uint64_t*>(&file_info.ftLastWriteTime);
185 info->last_accessed =
186 *reinterpret_cast<uint64_t*>(&file_info.ftLastAccessTime);
187 info->creation_time = *reinterpret_cast<uint64_t*>(&file_info.ftCreationTime);
188 return true;
189 }
190
Lock()191 File::Error File::Lock() {
192 DCHECK(IsValid());
193
194 BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
195 if (!result)
196 return GetLastFileError();
197 return FILE_OK;
198 }
199
Unlock()200 File::Error File::Unlock() {
201 DCHECK(IsValid());
202
203 BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
204 if (!result)
205 return GetLastFileError();
206 return FILE_OK;
207 }
208
Duplicate() const209 File File::Duplicate() const {
210 if (!IsValid())
211 return File();
212
213 HANDLE other_handle = nullptr;
214
215 if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle
216 GetPlatformFile(),
217 GetCurrentProcess(), // hTargetProcessHandle
218 &other_handle,
219 0, // dwDesiredAccess ignored due to SAME_ACCESS
220 FALSE, // !bInheritHandle
221 DUPLICATE_SAME_ACCESS)) {
222 return File(GetLastFileError());
223 }
224
225 File other(other_handle);
226 return other;
227 }
228
229 // Static.
OSErrorToFileError(DWORD last_error)230 File::Error File::OSErrorToFileError(DWORD last_error) {
231 switch (last_error) {
232 case ERROR_SHARING_VIOLATION:
233 return FILE_ERROR_IN_USE;
234 case ERROR_ALREADY_EXISTS:
235 case ERROR_FILE_EXISTS:
236 return FILE_ERROR_EXISTS;
237 case ERROR_FILE_NOT_FOUND:
238 case ERROR_PATH_NOT_FOUND:
239 return FILE_ERROR_NOT_FOUND;
240 case ERROR_ACCESS_DENIED:
241 return FILE_ERROR_ACCESS_DENIED;
242 case ERROR_TOO_MANY_OPEN_FILES:
243 return FILE_ERROR_TOO_MANY_OPENED;
244 case ERROR_OUTOFMEMORY:
245 case ERROR_NOT_ENOUGH_MEMORY:
246 return FILE_ERROR_NO_MEMORY;
247 case ERROR_HANDLE_DISK_FULL:
248 case ERROR_DISK_FULL:
249 #ifndef __MINGW32__
250 case ERROR_DISK_RESOURCES_EXHAUSTED:
251 #endif
252 return FILE_ERROR_NO_SPACE;
253 case ERROR_USER_MAPPED_FILE:
254 return FILE_ERROR_INVALID_OPERATION;
255 case ERROR_NOT_READY:
256 case ERROR_SECTOR_NOT_FOUND:
257 case ERROR_DEV_NOT_EXIST:
258 case ERROR_IO_DEVICE:
259 case ERROR_FILE_CORRUPT:
260 case ERROR_DISK_CORRUPT:
261 return FILE_ERROR_IO;
262 default:
263 // This function should only be called for errors.
264 DCHECK_NE(static_cast<DWORD>(ERROR_SUCCESS), last_error);
265 return FILE_ERROR_FAILED;
266 }
267 }
268
DoInitialize(const FilePath & path,uint32_t flags)269 void File::DoInitialize(const FilePath& path, uint32_t flags) {
270 DCHECK(!IsValid());
271
272 DWORD disposition = 0;
273
274 if (flags & FLAG_OPEN)
275 disposition = OPEN_EXISTING;
276
277 if (flags & FLAG_CREATE_ALWAYS) {
278 DCHECK(!disposition);
279 DCHECK(flags & FLAG_WRITE);
280 disposition = CREATE_ALWAYS;
281 }
282
283 if (!disposition) {
284 ::SetLastError(ERROR_INVALID_PARAMETER);
285 error_details_ = FILE_ERROR_FAILED;
286 NOTREACHED();
287 return;
288 }
289
290 DWORD access = 0;
291 if (flags & FLAG_WRITE)
292 access = GENERIC_WRITE;
293 if (flags & FLAG_READ)
294 access |= GENERIC_READ;
295
296 DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
297 DWORD create_flags = 0;
298 file_.Set(CreateFile(ToWCharT(&path.value()), access, sharing, NULL,
299 disposition, create_flags, NULL));
300
301 if (file_.IsValid()) {
302 error_details_ = FILE_OK;
303 } else {
304 error_details_ = GetLastFileError();
305 }
306 }
307
Flush()308 bool File::Flush() {
309 DCHECK(IsValid());
310 return ::FlushFileBuffers(file_.Get()) != FALSE;
311 }
312
SetPlatformFile(PlatformFile file)313 void File::SetPlatformFile(PlatformFile file) {
314 file_.Set(file);
315 }
316
317 // static
GetLastFileError()318 File::Error File::GetLastFileError() {
319 return File::OSErrorToFileError(GetLastError());
320 }
321
322 } // namespace base
323