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 "SkOSFile.h" 9#include "SkString.h" 10#include "SkTFitsIn.h" 11#include "SkTemplates.h" 12#include "SkTypes.h" 13 14#include <dirent.h> 15#include <stdio.h> 16#include <string.h> 17#include <sys/mman.h> 18#include <sys/stat.h> 19#include <sys/types.h> 20#include <unistd.h> 21 22bool sk_exists(const char *path, SkFILE_Flags flags) { 23 int mode = F_OK; 24 if (flags & kRead_SkFILE_Flag) { 25 mode |= R_OK; 26 } 27 if (flags & kWrite_SkFILE_Flag) { 28 mode |= W_OK; 29 } 30 return (0 == access(path, mode)); 31} 32 33typedef struct { 34 dev_t dev; 35 ino_t ino; 36} SkFILEID; 37 38static bool sk_ino(FILE* a, SkFILEID* id) { 39 int fd = fileno(a); 40 if (fd < 0) { 41 return 0; 42 } 43 struct stat status; 44 if (0 != fstat(fd, &status)) { 45 return 0; 46 } 47 id->dev = status.st_dev; 48 id->ino = status.st_ino; 49 return true; 50} 51 52bool sk_fidentical(FILE* a, FILE* b) { 53 SkFILEID aID, bID; 54 return sk_ino(a, &aID) && sk_ino(b, &bID) 55 && aID.ino == bID.ino 56 && aID.dev == bID.dev; 57} 58 59void sk_fmunmap(const void* addr, size_t length) { 60 munmap(const_cast<void*>(addr), length); 61} 62 63void* sk_fdmmap(int fd, size_t* size) { 64 struct stat status; 65 if (0 != fstat(fd, &status)) { 66 return nullptr; 67 } 68 if (!S_ISREG(status.st_mode)) { 69 return nullptr; 70 } 71 if (!SkTFitsIn<size_t>(status.st_size)) { 72 return nullptr; 73 } 74 size_t fileSize = static_cast<size_t>(status.st_size); 75 76 void* addr = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0); 77 if (MAP_FAILED == addr) { 78 return nullptr; 79 } 80 81 *size = fileSize; 82 return addr; 83} 84 85int sk_fileno(FILE* f) { 86 return fileno(f); 87} 88 89void* sk_fmmap(FILE* f, size_t* size) { 90 int fd = sk_fileno(f); 91 if (fd < 0) { 92 return nullptr; 93 } 94 95 return sk_fdmmap(fd, size); 96} 97 98size_t sk_qread(FILE* file, void* buffer, size_t count, size_t offset) { 99 int fd = sk_fileno(file); 100 if (fd < 0) { 101 return SIZE_MAX; 102 } 103 ssize_t bytesRead = pread(fd, buffer, count, offset); 104 if (bytesRead < 0) { 105 return SIZE_MAX; 106 } 107 return bytesRead; 108} 109 110//////////////////////////////////////////////////////////////////////////// 111 112struct SkOSFileIterData { 113 SkOSFileIterData() : fDIR(0) { } 114 DIR* fDIR; 115 SkString fPath, fSuffix; 116}; 117static_assert(sizeof(SkOSFileIterData) <= SkOSFile::Iter::kStorageSize, "not_enough_space"); 118 119SkOSFile::Iter::Iter() { new (fSelf.get()) SkOSFileIterData; } 120 121SkOSFile::Iter::Iter(const char path[], const char suffix[]) { 122 new (fSelf.get()) SkOSFileIterData; 123 this->reset(path, suffix); 124} 125 126SkOSFile::Iter::~Iter() { 127 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 128 if (self.fDIR) { 129 ::closedir(self.fDIR); 130 } 131 self.~SkOSFileIterData(); 132} 133 134void SkOSFile::Iter::reset(const char path[], const char suffix[]) { 135 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 136 if (self.fDIR) { 137 ::closedir(self.fDIR); 138 self.fDIR = 0; 139 } 140 141 self.fPath.set(path); 142 if (path) { 143 self.fDIR = ::opendir(path); 144 self.fSuffix.set(suffix); 145 } else { 146 self.fSuffix.reset(); 147 } 148} 149 150// returns true if suffix is empty, or if str ends with suffix 151static bool issuffixfor(const SkString& suffix, const char str[]) { 152 size_t suffixLen = suffix.size(); 153 size_t strLen = strlen(str); 154 155 return strLen >= suffixLen && 156 memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0; 157} 158 159bool SkOSFile::Iter::next(SkString* name, bool getDir) { 160 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 161 if (self.fDIR) { 162 dirent* entry; 163 164 while ((entry = ::readdir(self.fDIR)) != nullptr) { 165 struct stat s; 166 SkString str(self.fPath); 167 168 if (!str.endsWith("/") && !str.endsWith("\\")) { 169 str.append("/"); 170 } 171 str.append(entry->d_name); 172 173 if (0 == stat(str.c_str(), &s)) { 174 if (getDir) { 175 if (s.st_mode & S_IFDIR) { 176 break; 177 } 178 } else { 179 if (!(s.st_mode & S_IFDIR) && issuffixfor(self.fSuffix, entry->d_name)) { 180 break; 181 } 182 } 183 } 184 } 185 if (entry) { // we broke out with a file 186 if (name) { 187 name->set(entry->d_name); 188 } 189 return true; 190 } 191 } 192 return false; 193} 194