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 "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; 136 memset(&overlapped, 0, sizeof(overlapped)); 137 ULARGE_INTEGER winOffset; 138 winOffset.QuadPart = offset; 139 overlapped.Offset = winOffset.LowPart; 140 overlapped.OffsetHigh = winOffset.HighPart; 141 142 if (!SkTFitsIn<DWORD>(count)) { 143 count = std::numeric_limits<DWORD>::max(); 144 } 145 146 DWORD bytesRead; 147 if (ReadFile(fileHandle, buffer, static_cast<DWORD>(count), &bytesRead, &overlapped)) { 148 return bytesRead; 149 } 150 if (GetLastError() == ERROR_HANDLE_EOF) { 151 return 0; 152 } 153 return SIZE_MAX; 154 } 155 156 //////////////////////////////////////////////////////////////////////////// 157 158 struct SkOSFileIterData { 159 SkOSFileIterData() : fHandle(0), fPath16(nullptr) { } 160 HANDLE fHandle; 161 uint16_t* fPath16; 162 }; 163 static_assert(sizeof(SkOSFileIterData) <= SkOSFile::Iter::kStorageSize, "not_enough_space"); 164 165 static uint16_t* concat_to_16(const char src[], const char suffix[]) { 166 size_t i, len = strlen(src); 167 size_t len2 = 3 + (suffix ? strlen(suffix) : 0); 168 uint16_t* dst = (uint16_t*)sk_malloc_throw((len + len2) * sizeof(uint16_t)); 169 170 for (i = 0; i < len; i++) { 171 dst[i] = src[i]; 172 } 173 174 if (i > 0 && dst[i-1] != '/') { 175 dst[i++] = '/'; 176 } 177 dst[i++] = '*'; 178 179 if (suffix) { 180 while (*suffix) { 181 dst[i++] = *suffix++; 182 } 183 } 184 dst[i] = 0; 185 SkASSERT(i + 1 <= len + len2); 186 187 return dst; 188 } 189 190 SkOSFile::Iter::Iter() { new (fSelf.get()) SkOSFileIterData; } 191 192 SkOSFile::Iter::Iter(const char path[], const char suffix[]) { 193 new (fSelf.get()) SkOSFileIterData; 194 this->reset(path, suffix); 195 } 196 197 SkOSFile::Iter::~Iter() { 198 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 199 sk_free(self.fPath16); 200 if (self.fHandle) { 201 ::FindClose(self.fHandle); 202 } 203 self.~SkOSFileIterData(); 204 } 205 206 void SkOSFile::Iter::reset(const char path[], const char suffix[]) { 207 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 208 if (self.fHandle) { 209 ::FindClose(self.fHandle); 210 self.fHandle = 0; 211 } 212 if (nullptr == path) { 213 path = ""; 214 } 215 216 sk_free(self.fPath16); 217 self.fPath16 = concat_to_16(path, suffix); 218 } 219 220 static bool is_magic_dir(const uint16_t dir[]) { 221 // return true for "." and ".." 222 return dir[0] == '.' && (dir[1] == 0 || (dir[1] == '.' && dir[2] == 0)); 223 } 224 225 static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir) { 226 WIN32_FIND_DATAW data; 227 228 if (nullptr == dataPtr) { 229 if (::FindNextFileW(handle, &data)) 230 dataPtr = &data; 231 else 232 return false; 233 } 234 235 for (;;) { 236 if (getDir) { 237 if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && 238 !is_magic_dir((uint16_t*)dataPtr->cFileName)) 239 { 240 break; 241 } 242 } else { 243 if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { 244 break; 245 } 246 } 247 if (!::FindNextFileW(handle, dataPtr)) { 248 return false; 249 } 250 } 251 // if we get here, we've found a file/dir 252 if (name) { 253 name->setUTF16((uint16_t*)dataPtr->cFileName); 254 } 255 return true; 256 } 257 258 bool SkOSFile::Iter::next(SkString* name, bool getDir) { 259 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 260 WIN32_FIND_DATAW data; 261 WIN32_FIND_DATAW* dataPtr = nullptr; 262 263 if (self.fHandle == 0) { // our first time 264 if (self.fPath16 == nullptr || *self.fPath16 == 0) { // check for no path 265 return false; 266 } 267 268 self.fHandle = ::FindFirstFileW((LPCWSTR)self.fPath16, &data); 269 if (self.fHandle != 0 && self.fHandle != (HANDLE)~0) { 270 dataPtr = &data; 271 } 272 } 273 return self.fHandle != (HANDLE)~0 && get_the_file(self.fHandle, name, dataPtr, getDir); 274 } 275 276 #endif//defined(SK_BUILD_FOR_WIN) 277