Home | History | Annotate | Download | only in ports
      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