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