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