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