blob: 05afcc6350a1fbd31bf126d7386d0d130cdd362f [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"
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000014#include "SkTFitsIn.h"
bungeman@google.com11c9a552013-06-03 17:10:35 +000015
bungeman@google.com6cab1a42013-05-29 13:43:31 +000016#include <io.h>
17#include <stdio.h>
18#include <sys/stat.h>
19
bungemanbf0b9ce2014-07-10 15:18:23 -070020bool sk_exists(const char *path, SkFILE_Flags flags) {
21 int mode = 0; // existence
22 if (flags & kRead_SkFILE_Flag) {
23 mode |= 4; // read
24 }
25 if (flags & kWrite_SkFILE_Flag) {
26 mode |= 2; // write
27 }
28 return (0 == _access(path, mode));
29}
30
bungeman@google.com6cab1a42013-05-29 13:43:31 +000031typedef struct {
32 ULONGLONG fVolume;
33 ULONGLONG fLsbSize;
34 ULONGLONG fMsbSize;
35} SkFILEID;
36
halcanaryd76be9c2015-11-20 13:47:49 -080037static bool sk_ino(FILE* f, SkFILEID* id) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000038 int fileno = _fileno((FILE*)f);
39 if (fileno < 0) {
40 return false;
41 }
42
43 HANDLE file = (HANDLE)_get_osfhandle(fileno);
44 if (INVALID_HANDLE_VALUE == file) {
45 return false;
46 }
47
48 //TODO: call GetFileInformationByHandleEx on Vista and later with FileIdInfo.
49 BY_HANDLE_FILE_INFORMATION info;
50 if (0 == GetFileInformationByHandle(file, &info)) {
51 return false;
52 }
53 id->fVolume = info.dwVolumeSerialNumber;
54 id->fLsbSize = info.nFileIndexLow + (((ULONGLONG)info.nFileIndexHigh) << 32);
55 id->fMsbSize = 0;
56
57 return true;
58}
59
halcanaryd76be9c2015-11-20 13:47:49 -080060bool sk_fidentical(FILE* a, FILE* b) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000061 SkFILEID aID, bID;
62 return sk_ino(a, &aID) && sk_ino(b, &bID)
63 && aID.fLsbSize == bID.fLsbSize
64 && aID.fMsbSize == bID.fMsbSize
65 && aID.fVolume == bID.fVolume;
66}
67
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000068class SkAutoNullKernelHandle : SkNoncopyable {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000069public:
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000070 SkAutoNullKernelHandle(const HANDLE handle) : fHandle(handle) { }
71 ~SkAutoNullKernelHandle() { CloseHandle(fHandle); }
72 operator HANDLE() const { return fHandle; }
bsalomon49f085d2014-09-05 13:34:00 -070073 bool isValid() const { return SkToBool(fHandle); }
bungeman@google.com6cab1a42013-05-29 13:43:31 +000074private:
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000075 HANDLE fHandle;
bungeman@google.com6cab1a42013-05-29 13:43:31 +000076};
commit-bot@chromium.org8e13a152014-03-19 19:28:00 +000077typedef SkAutoNullKernelHandle SkAutoWinMMap;
bungeman@google.com6cab1a42013-05-29 13:43:31 +000078
79void sk_fmunmap(const void* addr, size_t) {
80 UnmapViewOfFile(addr);
81}
82
bungeman@google.com11c9a552013-06-03 17:10:35 +000083void* sk_fdmmap(int fileno, size_t* length) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000084 HANDLE file = (HANDLE)_get_osfhandle(fileno);
85 if (INVALID_HANDLE_VALUE == file) {
halcanary96fcdcc2015-08-27 07:41:13 -070086 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +000087 }
88
bungeman@google.com11c9a552013-06-03 17:10:35 +000089 LARGE_INTEGER fileSize;
90 if (0 == GetFileSizeEx(file, &fileSize)) {
91 //TODO: use SK_TRACEHR(GetLastError(), "Could not get file size.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -070092 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +000093 }
94 if (!SkTFitsIn<size_t>(fileSize.QuadPart)) {
halcanary96fcdcc2015-08-27 07:41:13 -070095 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +000096 }
97
halcanary96fcdcc2015-08-27 07:41:13 -070098 SkAutoWinMMap mmap(CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr));
bungeman@google.com6cab1a42013-05-29 13:43:31 +000099 if (!mmap.isValid()) {
100 //TODO: use SK_TRACEHR(GetLastError(), "Could not create file mapping.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -0700101 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000102 }
103
104 // Eventually call UnmapViewOfFile
105 void* addr = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700106 if (nullptr == addr) {
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000107 //TODO: use SK_TRACEHR(GetLastError(), "Could not map view of file.") to report.
halcanary96fcdcc2015-08-27 07:41:13 -0700108 return nullptr;
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000109 }
110
bungeman@google.com11c9a552013-06-03 17:10:35 +0000111 *length = static_cast<size_t>(fileSize.QuadPart);
bungeman@google.com6cab1a42013-05-29 13:43:31 +0000112 return addr;
113}
bungeman@google.com11c9a552013-06-03 17:10:35 +0000114
halcanaryd76be9c2015-11-20 13:47:49 -0800115int sk_fileno(FILE* f) {
bungeman@google.com11c9a552013-06-03 17:10:35 +0000116 return _fileno((FILE*)f);
117}
118
halcanaryd76be9c2015-11-20 13:47:49 -0800119void* sk_fmmap(FILE* f, size_t* length) {
bungeman@google.com11c9a552013-06-03 17:10:35 +0000120 int fileno = sk_fileno(f);
121 if (fileno < 0) {
halcanary96fcdcc2015-08-27 07:41:13 -0700122 return nullptr;
bungeman@google.com11c9a552013-06-03 17:10:35 +0000123 }
124
125 return sk_fdmmap(fileno, length);
126}
bungemane998b7f2015-02-12 07:18:27 -0800127
Ben Wagner4d1955c2017-03-10 13:08:15 -0500128size_t sk_qread(FILE* file, void* buffer, size_t count, size_t offset) {
129 int fileno = sk_fileno(file);
130 HANDLE fileHandle = (HANDLE)_get_osfhandle(fileno);
131 if (INVALID_HANDLE_VALUE == file) {
132 return SIZE_MAX;
133 }
134
Chris Dalton1ef80942017-12-04 12:01:30 -0700135 OVERLAPPED overlapped;
136 memset(&overlapped, 0, sizeof(overlapped));
Ben Wagner4d1955c2017-03-10 13:08:15 -0500137 ULARGE_INTEGER winOffset;
138 winOffset.QuadPart = offset;
139 overlapped.Offset = winOffset.LowPart;
140 overlapped.OffsetHigh = winOffset.HighPart;
141
142 if (!SkTFitsIn<DWORD>(count)) {
143 count = std::numeric_limits<DWORD>::max();
144 }
145
146 DWORD bytesRead;
147 if (ReadFile(fileHandle, buffer, static_cast<DWORD>(count), &bytesRead, &overlapped)) {
148 return bytesRead;
149 }
150 if (GetLastError() == ERROR_HANDLE_EOF) {
151 return 0;
152 }
153 return SIZE_MAX;
154}
155
bungemane998b7f2015-02-12 07:18:27 -0800156////////////////////////////////////////////////////////////////////////////
157
158struct SkOSFileIterData {
halcanary96fcdcc2015-08-27 07:41:13 -0700159 SkOSFileIterData() : fHandle(0), fPath16(nullptr) { }
bungemane998b7f2015-02-12 07:18:27 -0800160 HANDLE fHandle;
161 uint16_t* fPath16;
162};
bungeman99fe8222015-08-20 07:57:51 -0700163static_assert(sizeof(SkOSFileIterData) <= SkOSFile::Iter::kStorageSize, "not_enough_space");
bungemane998b7f2015-02-12 07:18:27 -0800164
165static uint16_t* concat_to_16(const char src[], const char suffix[]) {
166 size_t i, len = strlen(src);
167 size_t len2 = 3 + (suffix ? strlen(suffix) : 0);
168 uint16_t* dst = (uint16_t*)sk_malloc_throw((len + len2) * sizeof(uint16_t));
169
170 for (i = 0; i < len; i++) {
171 dst[i] = src[i];
172 }
173
174 if (i > 0 && dst[i-1] != '/') {
175 dst[i++] = '/';
176 }
177 dst[i++] = '*';
178
179 if (suffix) {
180 while (*suffix) {
181 dst[i++] = *suffix++;
182 }
183 }
184 dst[i] = 0;
185 SkASSERT(i + 1 <= len + len2);
186
187 return dst;
188}
189
halcanary385fe4d2015-08-26 13:07:48 -0700190SkOSFile::Iter::Iter() { new (fSelf.get()) SkOSFileIterData; }
bungemane998b7f2015-02-12 07:18:27 -0800191
192SkOSFile::Iter::Iter(const char path[], const char suffix[]) {
halcanary385fe4d2015-08-26 13:07:48 -0700193 new (fSelf.get()) SkOSFileIterData;
bungemane998b7f2015-02-12 07:18:27 -0800194 this->reset(path, suffix);
195}
196
197SkOSFile::Iter::~Iter() {
198 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
199 sk_free(self.fPath16);
200 if (self.fHandle) {
201 ::FindClose(self.fHandle);
202 }
203 self.~SkOSFileIterData();
204}
205
206void SkOSFile::Iter::reset(const char path[], const char suffix[]) {
207 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
208 if (self.fHandle) {
209 ::FindClose(self.fHandle);
210 self.fHandle = 0;
211 }
halcanary96fcdcc2015-08-27 07:41:13 -0700212 if (nullptr == path) {
bungemane998b7f2015-02-12 07:18:27 -0800213 path = "";
214 }
215
216 sk_free(self.fPath16);
217 self.fPath16 = concat_to_16(path, suffix);
218}
219
220static bool is_magic_dir(const uint16_t dir[]) {
221 // return true for "." and ".."
222 return dir[0] == '.' && (dir[1] == 0 || (dir[1] == '.' && dir[2] == 0));
223}
224
225static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir) {
226 WIN32_FIND_DATAW data;
227
halcanary96fcdcc2015-08-27 07:41:13 -0700228 if (nullptr == dataPtr) {
bungemane998b7f2015-02-12 07:18:27 -0800229 if (::FindNextFileW(handle, &data))
230 dataPtr = &data;
231 else
232 return false;
233 }
234
235 for (;;) {
236 if (getDir) {
237 if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
238 !is_magic_dir((uint16_t*)dataPtr->cFileName))
239 {
240 break;
241 }
242 } else {
243 if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
244 break;
245 }
246 }
247 if (!::FindNextFileW(handle, dataPtr)) {
248 return false;
249 }
250 }
251 // if we get here, we've found a file/dir
252 if (name) {
253 name->setUTF16((uint16_t*)dataPtr->cFileName);
254 }
255 return true;
256}
257
258bool SkOSFile::Iter::next(SkString* name, bool getDir) {
259 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
260 WIN32_FIND_DATAW data;
halcanary96fcdcc2015-08-27 07:41:13 -0700261 WIN32_FIND_DATAW* dataPtr = nullptr;
bungemane998b7f2015-02-12 07:18:27 -0800262
263 if (self.fHandle == 0) { // our first time
halcanary96fcdcc2015-08-27 07:41:13 -0700264 if (self.fPath16 == nullptr || *self.fPath16 == 0) { // check for no path
bungemane998b7f2015-02-12 07:18:27 -0800265 return false;
266 }
267
268 self.fHandle = ::FindFirstFileW((LPCWSTR)self.fPath16, &data);
269 if (self.fHandle != 0 && self.fHandle != (HANDLE)~0) {
270 dataPtr = &data;
271 }
272 }
273 return self.fHandle != (HANDLE)~0 && get_the_file(self.fHandle, name, dataPtr, getDir);
274}
mtklein1ee76512015-11-02 10:20:27 -0800275
Mike Klein8f11d4d2018-01-24 12:42:55 -0500276#endif//defined(SK_BUILD_FOR_WIN)