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