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