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