1 /*
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 
8 #include "include/core/SkTypes.h"
9 #if defined(SK_BUILD_FOR_WIN)
10 
11 #include "include/private/SkMalloc.h"
12 #include "include/private/SkNoncopyable.h"
13 #include "include/private/SkTFitsIn.h"
14 #include "src/core/SkLeanWindows.h"
15 #include "src/core/SkOSFile.h"
16 #include "src/core/SkStringUtils.h"
17 
18 #include <io.h>
19 #include <new>
20 #include <stdio.h>
21 #include <sys/stat.h>
22 
sk_exists(const char * path,SkFILE_Flags flags)23 bool sk_exists(const char *path, SkFILE_Flags flags) {
24     int mode = 0; // existence
25     if (flags & kRead_SkFILE_Flag) {
26         mode |= 4; // read
27     }
28     if (flags & kWrite_SkFILE_Flag) {
29         mode |= 2; // write
30     }
31     return (0 == _access(path, mode));
32 }
33 
34 typedef struct {
35     ULONGLONG fVolume;
36     ULONGLONG fLsbSize;
37     ULONGLONG fMsbSize;
38 } SkFILEID;
39 
sk_ino(FILE * f,SkFILEID * id)40 static bool sk_ino(FILE* f, SkFILEID* id) {
41     int fileno = _fileno((FILE*)f);
42     if (fileno < 0) {
43         return false;
44     }
45 
46     HANDLE file = (HANDLE)_get_osfhandle(fileno);
47     if (INVALID_HANDLE_VALUE == file) {
48         return false;
49     }
50 
51     //TODO: call GetFileInformationByHandleEx on Vista and later with FileIdInfo.
52     BY_HANDLE_FILE_INFORMATION info;
53     if (0 == GetFileInformationByHandle(file, &info)) {
54         return false;
55     }
56     id->fVolume = info.dwVolumeSerialNumber;
57     id->fLsbSize = info.nFileIndexLow + (((ULONGLONG)info.nFileIndexHigh) << 32);
58     id->fMsbSize = 0;
59 
60     return true;
61 }
62 
sk_fidentical(FILE * a,FILE * b)63 bool sk_fidentical(FILE* a, FILE* b) {
64     SkFILEID aID, bID;
65     return sk_ino(a, &aID) && sk_ino(b, &bID)
66            && aID.fLsbSize == bID.fLsbSize
67            && aID.fMsbSize == bID.fMsbSize
68            && aID.fVolume == bID.fVolume;
69 }
70 
71 class SkAutoNullKernelHandle : SkNoncopyable {
72 public:
SkAutoNullKernelHandle(const HANDLE handle)73     SkAutoNullKernelHandle(const HANDLE handle) : fHandle(handle) { }
~SkAutoNullKernelHandle()74     ~SkAutoNullKernelHandle() { CloseHandle(fHandle); }
operator HANDLE() const75     operator HANDLE() const { return fHandle; }
isValid() const76     bool isValid() const { return SkToBool(fHandle); }
77 private:
78     HANDLE fHandle;
79 };
80 typedef SkAutoNullKernelHandle SkAutoWinMMap;
81 
sk_fmunmap(const void * addr,size_t)82 void sk_fmunmap(const void* addr, size_t) {
83     UnmapViewOfFile(addr);
84 }
85 
sk_fdmmap(int fileno,size_t * length)86 void* sk_fdmmap(int fileno, size_t* length) {
87     HANDLE file = (HANDLE)_get_osfhandle(fileno);
88     if (INVALID_HANDLE_VALUE == file) {
89         return nullptr;
90     }
91 
92     LARGE_INTEGER fileSize;
93     if (0 == GetFileSizeEx(file, &fileSize)) {
94         //TODO: use SK_TRACEHR(GetLastError(), "Could not get file size.") to report.
95         return nullptr;
96     }
97     if (!SkTFitsIn<size_t>(fileSize.QuadPart)) {
98         return nullptr;
99     }
100 
101     SkAutoWinMMap mmap(CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr));
102     if (!mmap.isValid()) {
103         //TODO: use SK_TRACEHR(GetLastError(), "Could not create file mapping.") to report.
104         return nullptr;
105     }
106 
107     // Eventually call UnmapViewOfFile
108     void* addr = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
109     if (nullptr == addr) {
110         //TODO: use SK_TRACEHR(GetLastError(), "Could not map view of file.") to report.
111         return nullptr;
112     }
113 
114     *length = static_cast<size_t>(fileSize.QuadPart);
115     return addr;
116 }
117 
sk_fileno(FILE * f)118 int sk_fileno(FILE* f) {
119     return _fileno((FILE*)f);
120 }
121 
sk_fmmap(FILE * f,size_t * length)122 void* sk_fmmap(FILE* f, size_t* length) {
123     int fileno = sk_fileno(f);
124     if (fileno < 0) {
125         return nullptr;
126     }
127 
128     return sk_fdmmap(fileno, length);
129 }
130 
sk_qread(FILE * file,void * buffer,size_t count,size_t offset)131 size_t sk_qread(FILE* file, void* buffer, size_t count, size_t offset) {
132     int fileno = sk_fileno(file);
133     HANDLE fileHandle = (HANDLE)_get_osfhandle(fileno);
134     if (INVALID_HANDLE_VALUE == file) {
135         return SIZE_MAX;
136     }
137 
138     OVERLAPPED overlapped;
139     memset(&overlapped, 0, sizeof(overlapped));
140     ULARGE_INTEGER winOffset;
141     winOffset.QuadPart = offset;
142     overlapped.Offset = winOffset.LowPart;
143     overlapped.OffsetHigh = winOffset.HighPart;
144 
145     if (!SkTFitsIn<DWORD>(count)) {
146         count = std::numeric_limits<DWORD>::max();
147     }
148 
149     DWORD bytesRead;
150     if (ReadFile(fileHandle, buffer, static_cast<DWORD>(count), &bytesRead, &overlapped)) {
151         return bytesRead;
152     }
153     if (GetLastError() == ERROR_HANDLE_EOF) {
154         return 0;
155     }
156     return SIZE_MAX;
157 }
158 
159 ////////////////////////////////////////////////////////////////////////////
160 
161 struct SkOSFileIterData {
SkOSFileIterDataSkOSFileIterData162     SkOSFileIterData() : fHandle(0), fPath16(nullptr) { }
163     HANDLE fHandle;
164     uint16_t* fPath16;
165 };
166 static_assert(sizeof(SkOSFileIterData) <= SkOSFile::Iter::kStorageSize, "not_enough_space");
167 
concat_to_16(const char src[],const char suffix[])168 static uint16_t* concat_to_16(const char src[], const char suffix[]) {
169     size_t  i, len = strlen(src);
170     size_t  len2 = 3 + (suffix ? strlen(suffix) : 0);
171     uint16_t* dst = (uint16_t*)sk_malloc_throw((len + len2) * sizeof(uint16_t));
172 
173     for (i = 0; i < len; i++) {
174         dst[i] = src[i];
175     }
176 
177     if (i > 0 && dst[i-1] != '/') {
178         dst[i++] = '/';
179     }
180     dst[i++] = '*';
181 
182     if (suffix) {
183         while (*suffix) {
184             dst[i++] = *suffix++;
185         }
186     }
187     dst[i] = 0;
188     SkASSERT(i + 1 <= len + len2);
189 
190     return dst;
191 }
192 
Iter()193 SkOSFile::Iter::Iter() { new (fSelf.get()) SkOSFileIterData; }
194 
Iter(const char path[],const char suffix[])195 SkOSFile::Iter::Iter(const char path[], const char suffix[]) {
196     new (fSelf.get()) SkOSFileIterData;
197     this->reset(path, suffix);
198 }
199 
~Iter()200 SkOSFile::Iter::~Iter() {
201     SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
202     sk_free(self.fPath16);
203     if (self.fHandle) {
204         ::FindClose(self.fHandle);
205     }
206     self.~SkOSFileIterData();
207 }
208 
reset(const char path[],const char suffix[])209 void SkOSFile::Iter::reset(const char path[], const char suffix[]) {
210     SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
211     if (self.fHandle) {
212         ::FindClose(self.fHandle);
213         self.fHandle = 0;
214     }
215     if (nullptr == path) {
216         path = "";
217     }
218 
219     sk_free(self.fPath16);
220     self.fPath16 = concat_to_16(path, suffix);
221 }
222 
is_magic_dir(const uint16_t dir[])223 static bool is_magic_dir(const uint16_t dir[]) {
224     // return true for "." and ".."
225     return dir[0] == '.' && (dir[1] == 0 || (dir[1] == '.' && dir[2] == 0));
226 }
227 
get_the_file(HANDLE handle,SkString * name,WIN32_FIND_DATAW * dataPtr,bool getDir)228 static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir) {
229     WIN32_FIND_DATAW    data;
230 
231     if (nullptr == dataPtr) {
232         if (::FindNextFileW(handle, &data))
233             dataPtr = &data;
234         else
235             return false;
236     }
237 
238     for (;;) {
239         if (getDir) {
240             if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
241                 !is_magic_dir((uint16_t*)dataPtr->cFileName))
242             {
243                 break;
244             }
245         } else {
246             if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
247                 break;
248             }
249         }
250         if (!::FindNextFileW(handle, dataPtr)) {
251             return false;
252         }
253     }
254     // if we get here, we've found a file/dir
255     if (name) {
256         const uint16_t* utf16name = (const uint16_t*)dataPtr->cFileName;
257         const uint16_t* ptr = utf16name;
258         while (*ptr != 0) { ++ptr; }
259         *name = SkStringFromUTF16(utf16name, ptr - utf16name);
260     }
261     return true;
262 }
263 
next(SkString * name,bool getDir)264 bool SkOSFile::Iter::next(SkString* name, bool getDir) {
265     SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get());
266     WIN32_FIND_DATAW    data;
267     WIN32_FIND_DATAW*   dataPtr = nullptr;
268 
269     if (self.fHandle == 0) {  // our first time
270         if (self.fPath16 == nullptr || *self.fPath16 == 0) {  // check for no path
271             return false;
272         }
273 
274         self.fHandle = ::FindFirstFileW((LPCWSTR)self.fPath16, &data);
275         if (self.fHandle != 0 && self.fHandle != (HANDLE)~0) {
276             dataPtr = &data;
277         }
278     }
279     return self.fHandle != (HANDLE)~0 && get_the_file(self.fHandle, name, dataPtr, getDir);
280 }
281 
282 #endif//defined(SK_BUILD_FOR_WIN)
283