blob: 9c5ada607d5575b62e6a3e0c0d80c787ae2760d8 [file] [log] [blame]
bungeman@google.com6cab1a42013-05-29 13:43:31 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
mtklein1ee76512015-11-02 10:20:27 -08008#include "SkTypes.h"
9#if defined(SK_BUILD_FOR_WIN32)
10
halcanary4dbbd042016-06-07 17:21:10 -070011#include "SkLeanWindows.h"
bungeman@google.com6cab1a42013-05-29 13:43:31 +000012#include "SkOSFile.h"
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000013#include "SkTFitsIn.h"
bungeman@google.com11c9a552013-06-03 17:10:35 +000014
bungeman@google.com6cab1a42013-05-29 13:43:31 +000015#include <io.h>
16#include <stdio.h>
17#include <sys/stat.h>
18
Ben Wagnereefa2892017-03-17 13:49:31 -040019size_t sk_fgetsize(FILE* f) {
20 int fileno = sk_fileno(f);
21 if (fileno < 0) {
22 return 0;
23 }
24
25 HANDLE file = (HANDLE)_get_osfhandle(fileno);
26 if (INVALID_HANDLE_VALUE == file) {
27 return 0;
28 }
29
30 LARGE_INTEGER fileSize;
31 if (0 == GetFileSizeEx(file, &fileSize)) {
32 return 0;
33 }
34 if (!SkTFitsIn<size_t>(fileSize.QuadPart)) {
35 return 0;
36 }
37 return static_cast<size_t>(fileSize.QuadPart);
38}
39
bungemanbf0b9ce2014-07-10 15:18:23 -070040bool sk_exists(const char *path, SkFILE_Flags flags) {
41 int mode = 0; // existence
42 if (flags & kRead_SkFILE_Flag) {
43 mode |= 4; // read
44 }
45 if (flags & kWrite_SkFILE_Flag) {
46 mode |= 2; // write
47 }
48 return (0 == _access(path, mode));
49}
50
bungeman@google.com6cab1a42013-05-29 13:43:31 +000051typedef struct {
52 ULONGLONG fVolume;
53 ULONGLONG fLsbSize;
54 ULONGLONG fMsbSize;
55} SkFILEID;
56
halcanaryd76be9c2015-11-20 13:47:49 -080057static bool sk_ino(FILE* f, SkFILEID* id) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000058 int fileno = _fileno((FILE*)f);
59 if (fileno < 0) {
60 return false;
61 }
62
63 HANDLE file = (HANDLE)_get_osfhandle(fileno);
64 if (INVALID_HANDLE_VALUE == file) {
65 return false;
66 }
67
68 //TODO: call GetFileInformationByHandleEx on Vista and later with FileIdInfo.
69 BY_HANDLE_FILE_INFORMATION info;
70 if (0 == GetFileInformationByHandle(file, &info)) {
71 return false;
72 }
73 id->fVolume = info.dwVolumeSerialNumber;
74 id->fLsbSize = info.nFileIndexLow + (((ULONGLONG)info.nFileIndexHigh) << 32);
75 id->fMsbSize = 0;
76
77 return true;
78}
79
halcanaryd76be9c2015-11-20 13:47:49 -080080bool sk_fidentical(FILE* a, FILE* b) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000081 SkFILEID aID, bID;
82 return sk_ino(a, &aID) && sk_ino(b, &bID)
83 && aID.fLsbSize == bID.fLsbSize
84 && aID.fMsbSize == bID.fMsbSize
85 && aID.fVolume == bID.fVolume;
86}
87
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000088class SkAutoNullKernelHandle : SkNoncopyable {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000089public:
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000090 SkAutoNullKernelHandle(const HANDLE handle) : fHandle(handle) { }
91 ~SkAutoNullKernelHandle() { CloseHandle(fHandle); }
92 operator HANDLE() const { return fHandle; }
bsalomon49f085d2014-09-05 13:34:00 -070093 bool isValid() const { return SkToBool(fHandle); }
bungeman@google.com6cab1a42013-05-29 13:43:31 +000094private:
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000095 HANDLE fHandle;
bungeman@google.com6cab1a42013-05-29 13:43:31 +000096};
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000097typedef SkAutoNullKernelHandle SkAutoWinMMap;
bungeman@google.com6cab1a42013-05-29 13:43:31 +000098
99void sk_fmunmap(const void* addr, size_t) {
100 UnmapViewOfFile(addr);
101}
102
bungeman@google.com11c9a552013-06-03 17:10:35 +0000103void* sk_fdmmap(int fileno, size_t* length) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000104 HANDLE file = (HANDLE)_get_osfhandle(fileno);
105 if (INVALID_HANDLE_VALUE == file) {
halcanary96fcdcc2015-08-27 07:41:13 -0700106 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000107 }
108
bungeman@google.com11c9a552013-06-03 17:10:35 +0000109 LARGE_INTEGER fileSize;
110 if (0 == GetFileSizeEx(file, &fileSize)) {
111 //TODO: use SK_TRACEHR(GetLastError(), "Could not get file size.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -0700112 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +0000113 }
114 if (!SkTFitsIn<size_t>(fileSize.QuadPart)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700115 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +0000116 }
117
halcanary96fcdcc2015-08-27 07:41:13 -0700118 SkAutoWinMMap mmap(CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr));
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000119 if (!mmap.isValid()) {
120 //TODO: use SK_TRACEHR(GetLastError(), "Could not create file mapping.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -0700121 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000122 }
123
124 // Eventually call UnmapViewOfFile
125 void* addr = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700126 if (nullptr == addr) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000127 //TODO: use SK_TRACEHR(GetLastError(), "Could not map view of file.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -0700128 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000129 }
130
bungeman@google.com11c9a552013-06-03 17:10:35 +0000131 *length = static_cast<size_t>(fileSize.QuadPart);
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000132 return addr;
133}
bungeman@google.com11c9a552013-06-03 17:10:35 +0000134
halcanaryd76be9c2015-11-20 13:47:49 -0800135int sk_fileno(FILE* f) {
bungeman@google.com11c9a552013-06-03 17:10:35 +0000136 return _fileno((FILE*)f);
137}
138
halcanaryd76be9c2015-11-20 13:47:49 -0800139void* sk_fmmap(FILE* f, size_t* length) {
bungeman@google.com11c9a552013-06-03 17:10:35 +0000140 int fileno = sk_fileno(f);
141 if (fileno < 0) {
halcanary96fcdcc2015-08-27 07:41:13 -0700142 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +0000143 }
144
145 return sk_fdmmap(fileno, length);
146}
bungemane998b7f2015-02-12 07:18:27 -0800147
Ben Wagner4d1955c2017-03-10 13:08:15 -0500148size_t sk_qread(FILE* file, void* buffer, size_t count, size_t offset) {
149 int fileno = sk_fileno(file);
150 HANDLE fileHandle = (HANDLE)_get_osfhandle(fileno);
151 if (INVALID_HANDLE_VALUE == file) {
152 return SIZE_MAX;
153 }
154
155 OVERLAPPED overlapped = {0};
156 ULARGE_INTEGER winOffset;
157 winOffset.QuadPart = offset;
158 overlapped.Offset = winOffset.LowPart;
159 overlapped.OffsetHigh = winOffset.HighPart;
160
161 if (!SkTFitsIn<DWORD>(count)) {
162 count = std::numeric_limits<DWORD>::max();
163 }
164
165 DWORD bytesRead;
166 if (ReadFile(fileHandle, buffer, static_cast<DWORD>(count), &bytesRead, &overlapped)) {
167 return bytesRead;
168 }
169 if (GetLastError() == ERROR_HANDLE_EOF) {
170 return 0;
171 }
172 return SIZE_MAX;
173}
174
bungemane998b7f2015-02-12 07:18:27 -0800175////////////////////////////////////////////////////////////////////////////
176
177struct SkOSFileIterData {
halcanary96fcdcc2015-08-27 07:41:13 -0700178 SkOSFileIterData() : fHandle(0), fPath16(nullptr) { }
bungemane998b7f2015-02-12 07:18:27 -0800179 HANDLE fHandle;
180 uint16_t* fPath16;
181};
bungeman99fe8222015-08-20 07:57:51 -0700182static_assert(sizeof(SkOSFileIterData) <= SkOSFile::Iter::kStorageSize, "not_enough_space");
bungemane998b7f2015-02-12 07:18:27 -0800183
184static uint16_t* concat_to_16(const char src[], const char suffix[]) {
185 size_t i, len = strlen(src);
186 size_t len2 = 3 + (suffix ? strlen(suffix) : 0);
187 uint16_t* dst = (uint16_t*)sk_malloc_throw((len + len2) * sizeof(uint16_t));
188
189 for (i = 0; i < len; i++) {
190 dst[i] = src[i];
191 }
192
193 if (i > 0 && dst[i-1] != '/') {
194 dst[i++] = '/';
195 }
196 dst[i++] = '*';
197
198 if (suffix) {
199 while (*suffix) {
200 dst[i++] = *suffix++;
201 }
202 }
203 dst[i] = 0;
204 SkASSERT(i + 1 <= len + len2);
205
206 return dst;
207}
208
halcanary385fe4d2015-08-26 13:07:48 -0700209SkOSFile::Iter::Iter() { new (fSelf.get()) SkOSFileIterData; }
bungemane998b7f2015-02-12 07:18:27 -0800210
211SkOSFile::Iter::Iter(const char path[], const char suffix[]) {
halcanary385fe4d2015-08-26 13:07:48 -0700212 new (fSelf.get()) SkOSFileIterData;
bungemane998b7f2015-02-12 07:18:27 -0800213 this->reset(path, suffix);
214}
215
216SkOSFile::Iter::~Iter() {
217 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
218 sk_free(self.fPath16);
219 if (self.fHandle) {
220 ::FindClose(self.fHandle);
221 }
222 self.~SkOSFileIterData();
223}
224
225void SkOSFile::Iter::reset(const char path[], const char suffix[]) {
226 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
227 if (self.fHandle) {
228 ::FindClose(self.fHandle);
229 self.fHandle = 0;
230 }
halcanary96fcdcc2015-08-27 07:41:13 -0700231 if (nullptr == path) {
bungemane998b7f2015-02-12 07:18:27 -0800232 path = "";
233 }
234
235 sk_free(self.fPath16);
236 self.fPath16 = concat_to_16(path, suffix);
237}
238
239static bool is_magic_dir(const uint16_t dir[]) {
240 // return true for "." and ".."
241 return dir[0] == '.' && (dir[1] == 0 || (dir[1] == '.' && dir[2] == 0));
242}
243
244static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir) {
245 WIN32_FIND_DATAW data;
246
halcanary96fcdcc2015-08-27 07:41:13 -0700247 if (nullptr == dataPtr) {
bungemane998b7f2015-02-12 07:18:27 -0800248 if (::FindNextFileW(handle, &data))
249 dataPtr = &data;
250 else
251 return false;
252 }
253
254 for (;;) {
255 if (getDir) {
256 if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
257 !is_magic_dir((uint16_t*)dataPtr->cFileName))
258 {
259 break;
260 }
261 } else {
262 if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
263 break;
264 }
265 }
266 if (!::FindNextFileW(handle, dataPtr)) {
267 return false;
268 }
269 }
270 // if we get here, we've found a file/dir
271 if (name) {
272 name->setUTF16((uint16_t*)dataPtr->cFileName);
273 }
274 return true;
275}
276
277bool SkOSFile::Iter::next(SkString* name, bool getDir) {
278 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
279 WIN32_FIND_DATAW data;
halcanary96fcdcc2015-08-27 07:41:13 -0700280 WIN32_FIND_DATAW* dataPtr = nullptr;
bungemane998b7f2015-02-12 07:18:27 -0800281
282 if (self.fHandle == 0) { // our first time
halcanary96fcdcc2015-08-27 07:41:13 -0700283 if (self.fPath16 == nullptr || *self.fPath16 == 0) { // check for no path
bungemane998b7f2015-02-12 07:18:27 -0800284 return false;
285 }
286
287 self.fHandle = ::FindFirstFileW((LPCWSTR)self.fPath16, &data);
288 if (self.fHandle != 0 && self.fHandle != (HANDLE)~0) {
289 dataPtr = &data;
290 }
291 }
292 return self.fHandle != (HANDLE)~0 && get_the_file(self.fHandle, name, dataPtr, getDir);
293}
mtklein1ee76512015-11-02 10:20:27 -0800294
295#endif//defined(SK_BUILD_FOR_WIN32)