blob: f76b0bced4d4d7f0c6977227f58f4e7fa30627fd [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"
Mike Klein8f11d4d2018-01-24 12:42:55 -05009#if defined(SK_BUILD_FOR_WIN)
mtklein1ee76512015-11-02 10:20:27 -080010
halcanary4dbbd042016-06-07 17:21:10 -070011#include "SkLeanWindows.h"
Herb Derbyb549cc32017-03-27 13:35:15 -040012#include "SkMalloc.h"
bungeman@google.com6cab1a42013-05-29 13:43:31 +000013#include "SkOSFile.h"
Hal Canaryee08b4a2018-03-01 15:56:37 -050014#include "SkStringUtils.h"
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000015#include "SkTFitsIn.h"
bungeman@google.com11c9a552013-06-03 17:10:35 +000016
bungeman@google.com6cab1a42013-05-29 13:43:31 +000017#include <io.h>
Mike Klein79aea6a2018-06-11 10:45:26 -040018#include <new>
bungeman@google.com6cab1a42013-05-29 13:43:31 +000019#include <stdio.h>
20#include <sys/stat.h>
21
bungemanbf0b9ce2014-07-10 15:18:23 -070022bool sk_exists(const char *path, SkFILE_Flags flags) {
23 int mode = 0; // existence
24 if (flags & kRead_SkFILE_Flag) {
25 mode |= 4; // read
26 }
27 if (flags & kWrite_SkFILE_Flag) {
28 mode |= 2; // write
29 }
30 return (0 == _access(path, mode));
31}
32
bungeman@google.com6cab1a42013-05-29 13:43:31 +000033typedef struct {
34 ULONGLONG fVolume;
35 ULONGLONG fLsbSize;
36 ULONGLONG fMsbSize;
37} SkFILEID;
38
halcanaryd76be9c2015-11-20 13:47:49 -080039static bool sk_ino(FILE* f, SkFILEID* id) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000040 int fileno = _fileno((FILE*)f);
41 if (fileno < 0) {
42 return false;
43 }
44
45 HANDLE file = (HANDLE)_get_osfhandle(fileno);
46 if (INVALID_HANDLE_VALUE == file) {
47 return false;
48 }
49
50 //TODO: call GetFileInformationByHandleEx on Vista and later with FileIdInfo.
51 BY_HANDLE_FILE_INFORMATION info;
52 if (0 == GetFileInformationByHandle(file, &info)) {
53 return false;
54 }
55 id->fVolume = info.dwVolumeSerialNumber;
56 id->fLsbSize = info.nFileIndexLow + (((ULONGLONG)info.nFileIndexHigh) << 32);
57 id->fMsbSize = 0;
58
59 return true;
60}
61
halcanaryd76be9c2015-11-20 13:47:49 -080062bool sk_fidentical(FILE* a, FILE* b) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000063 SkFILEID aID, bID;
64 return sk_ino(a, &aID) && sk_ino(b, &bID)
65 && aID.fLsbSize == bID.fLsbSize
66 && aID.fMsbSize == bID.fMsbSize
67 && aID.fVolume == bID.fVolume;
68}
69
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000070class SkAutoNullKernelHandle : SkNoncopyable {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000071public:
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000072 SkAutoNullKernelHandle(const HANDLE handle) : fHandle(handle) { }
73 ~SkAutoNullKernelHandle() { CloseHandle(fHandle); }
74 operator HANDLE() const { return fHandle; }
bsalomon49f085d2014-09-05 13:34:00 -070075 bool isValid() const { return SkToBool(fHandle); }
bungeman@google.com6cab1a42013-05-29 13:43:31 +000076private:
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000077 HANDLE fHandle;
bungeman@google.com6cab1a42013-05-29 13:43:31 +000078};
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000079typedef SkAutoNullKernelHandle SkAutoWinMMap;
bungeman@google.com6cab1a42013-05-29 13:43:31 +000080
81void sk_fmunmap(const void* addr, size_t) {
82 UnmapViewOfFile(addr);
83}
84
bungeman@google.com11c9a552013-06-03 17:10:35 +000085void* sk_fdmmap(int fileno, size_t* length) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000086 HANDLE file = (HANDLE)_get_osfhandle(fileno);
87 if (INVALID_HANDLE_VALUE == file) {
halcanary96fcdcc2015-08-27 07:41:13 -070088 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +000089 }
90
bungeman@google.com11c9a552013-06-03 17:10:35 +000091 LARGE_INTEGER fileSize;
92 if (0 == GetFileSizeEx(file, &fileSize)) {
93 //TODO: use SK_TRACEHR(GetLastError(), "Could not get file size.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -070094 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +000095 }
96 if (!SkTFitsIn<size_t>(fileSize.QuadPart)) {
halcanary96fcdcc2015-08-27 07:41:13 -070097 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +000098 }
99
halcanary96fcdcc2015-08-27 07:41:13 -0700100 SkAutoWinMMap mmap(CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr));
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000101 if (!mmap.isValid()) {
102 //TODO: use SK_TRACEHR(GetLastError(), "Could not create file mapping.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -0700103 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000104 }
105
106 // Eventually call UnmapViewOfFile
107 void* addr = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700108 if (nullptr == addr) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000109 //TODO: use SK_TRACEHR(GetLastError(), "Could not map view of file.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -0700110 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000111 }
112
bungeman@google.com11c9a552013-06-03 17:10:35 +0000113 *length = static_cast<size_t>(fileSize.QuadPart);
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000114 return addr;
115}
bungeman@google.com11c9a552013-06-03 17:10:35 +0000116
halcanaryd76be9c2015-11-20 13:47:49 -0800117int sk_fileno(FILE* f) {
bungeman@google.com11c9a552013-06-03 17:10:35 +0000118 return _fileno((FILE*)f);
119}
120
halcanaryd76be9c2015-11-20 13:47:49 -0800121void* sk_fmmap(FILE* f, size_t* length) {
bungeman@google.com11c9a552013-06-03 17:10:35 +0000122 int fileno = sk_fileno(f);
123 if (fileno < 0) {
halcanary96fcdcc2015-08-27 07:41:13 -0700124 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +0000125 }
126
127 return sk_fdmmap(fileno, length);
128}
bungemane998b7f2015-02-12 07:18:27 -0800129
Ben Wagner4d1955c2017-03-10 13:08:15 -0500130size_t sk_qread(FILE* file, void* buffer, size_t count, size_t offset) {
131 int fileno = sk_fileno(file);
132 HANDLE fileHandle = (HANDLE)_get_osfhandle(fileno);
133 if (INVALID_HANDLE_VALUE == file) {
134 return SIZE_MAX;
135 }
136
Chris Dalton1ef80942017-12-04 12:01:30 -0700137 OVERLAPPED overlapped;
138 memset(&overlapped, 0, sizeof(overlapped));
Ben Wagner4d1955c2017-03-10 13:08:15 -0500139 ULARGE_INTEGER winOffset;
140 winOffset.QuadPart = offset;
141 overlapped.Offset = winOffset.LowPart;
142 overlapped.OffsetHigh = winOffset.HighPart;
143
144 if (!SkTFitsIn<DWORD>(count)) {
145 count = std::numeric_limits<DWORD>::max();
146 }
147
148 DWORD bytesRead;
149 if (ReadFile(fileHandle, buffer, static_cast<DWORD>(count), &bytesRead, &overlapped)) {
150 return bytesRead;
151 }
152 if (GetLastError() == ERROR_HANDLE_EOF) {
153 return 0;
154 }
155 return SIZE_MAX;
156}
157
bungemane998b7f2015-02-12 07:18:27 -0800158////////////////////////////////////////////////////////////////////////////
159
160struct SkOSFileIterData {
halcanary96fcdcc2015-08-27 07:41:13 -0700161 SkOSFileIterData() : fHandle(0), fPath16(nullptr) { }
bungemane998b7f2015-02-12 07:18:27 -0800162 HANDLE fHandle;
163 uint16_t* fPath16;
164};
bungeman99fe8222015-08-20 07:57:51 -0700165static_assert(sizeof(SkOSFileIterData) <= SkOSFile::Iter::kStorageSize, "not_enough_space");
bungemane998b7f2015-02-12 07:18:27 -0800166
167static uint16_t* concat_to_16(const char src[], const char suffix[]) {
168 size_t i, len = strlen(src);
169 size_t len2 = 3 + (suffix ? strlen(suffix) : 0);
170 uint16_t* dst = (uint16_t*)sk_malloc_throw((len + len2) * sizeof(uint16_t));
171
172 for (i = 0; i < len; i++) {
173 dst[i] = src[i];
174 }
175
176 if (i > 0 && dst[i-1] != '/') {
177 dst[i++] = '/';
178 }
179 dst[i++] = '*';
180
181 if (suffix) {
182 while (*suffix) {
183 dst[i++] = *suffix++;
184 }
185 }
186 dst[i] = 0;
187 SkASSERT(i + 1 <= len + len2);
188
189 return dst;
190}
191
halcanary385fe4d2015-08-26 13:07:48 -0700192SkOSFile::Iter::Iter() { new (fSelf.get()) SkOSFileIterData; }
bungemane998b7f2015-02-12 07:18:27 -0800193
194SkOSFile::Iter::Iter(const char path[], const char suffix[]) {
halcanary385fe4d2015-08-26 13:07:48 -0700195 new (fSelf.get()) SkOSFileIterData;
bungemane998b7f2015-02-12 07:18:27 -0800196 this->reset(path, suffix);
197}
198
199SkOSFile::Iter::~Iter() {
200 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
201 sk_free(self.fPath16);
202 if (self.fHandle) {
203 ::FindClose(self.fHandle);
204 }
205 self.~SkOSFileIterData();
206}
207
208void SkOSFile::Iter::reset(const char path[], const char suffix[]) {
209 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
210 if (self.fHandle) {
211 ::FindClose(self.fHandle);
212 self.fHandle = 0;
213 }
halcanary96fcdcc2015-08-27 07:41:13 -0700214 if (nullptr == path) {
bungemane998b7f2015-02-12 07:18:27 -0800215 path = "";
216 }
217
218 sk_free(self.fPath16);
219 self.fPath16 = concat_to_16(path, suffix);
220}
221
222static bool is_magic_dir(const uint16_t dir[]) {
223 // return true for "." and ".."
224 return dir[0] == '.' && (dir[1] == 0 || (dir[1] == '.' && dir[2] == 0));
225}
226
227static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir) {
228 WIN32_FIND_DATAW data;
229
halcanary96fcdcc2015-08-27 07:41:13 -0700230 if (nullptr == dataPtr) {
bungemane998b7f2015-02-12 07:18:27 -0800231 if (::FindNextFileW(handle, &data))
232 dataPtr = &data;
233 else
234 return false;
235 }
236
237 for (;;) {
238 if (getDir) {
239 if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
240 !is_magic_dir((uint16_t*)dataPtr->cFileName))
241 {
242 break;
243 }
244 } else {
245 if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
246 break;
247 }
248 }
249 if (!::FindNextFileW(handle, dataPtr)) {
250 return false;
251 }
252 }
253 // if we get here, we've found a file/dir
254 if (name) {
Hal Canaryee08b4a2018-03-01 15:56:37 -0500255 const uint16_t* utf16name = (const uint16_t*)dataPtr->cFileName;
256 const uint16_t* ptr = utf16name;
257 while (*ptr != 0) { ++ptr; }
258 *name = SkStringFromUTF16(utf16name, ptr - utf16name);
bungemane998b7f2015-02-12 07:18:27 -0800259 }
260 return true;
261}
262
263bool SkOSFile::Iter::next(SkString* name, bool getDir) {
264 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
265 WIN32_FIND_DATAW data;
halcanary96fcdcc2015-08-27 07:41:13 -0700266 WIN32_FIND_DATAW* dataPtr = nullptr;
bungemane998b7f2015-02-12 07:18:27 -0800267
268 if (self.fHandle == 0) { // our first time
halcanary96fcdcc2015-08-27 07:41:13 -0700269 if (self.fPath16 == nullptr || *self.fPath16 == 0) { // check for no path
bungemane998b7f2015-02-12 07:18:27 -0800270 return false;
271 }
272
273 self.fHandle = ::FindFirstFileW((LPCWSTR)self.fPath16, &data);
274 if (self.fHandle != 0 && self.fHandle != (HANDLE)~0) {
275 dataPtr = &data;
276 }
277 }
278 return self.fHandle != (HANDLE)~0 && get_the_file(self.fHandle, name, dataPtr, getDir);
279}
mtklein1ee76512015-11-02 10:20:27 -0800280
Mike Klein8f11d4d2018-01-24 12:42:55 -0500281#endif//defined(SK_BUILD_FOR_WIN)