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