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