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 "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