]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/FileUtils.cpp
dyld-551.4.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / FileUtils.cpp
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include <string.h>
26 #include <stdint.h>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <limits.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/mman.h>
38 #include <dispatch/dispatch.h>
39 #include <mach-o/dyld.h>
40 #include <System/sys/csr.h>
41 #include <rootless.h>
42
43 #include <string>
44 #include <fstream>
45 #include <sstream>
46
47 #include "FileUtils.h"
48 #include "Diagnostics.h"
49
50 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
51 extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import));
52 #endif
53
54
55 void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path, bool (^dirFilter)(const std::string& path), void (^fileCallback)(const std::string& path, const struct stat&), bool processFiles)
56 {
57 std::string fullDirPath = pathPrefix + path;
58 DIR* dir = ::opendir(fullDirPath.c_str());
59 if ( dir == nullptr ) {
60 //fprintf(stderr, "can't read 'dir '%s', errno=%d\n", inputPath.c_str(), errno);
61 return;
62 }
63 while (dirent* entry = readdir(dir)) {
64 struct stat statBuf;
65 std::string dirAndFile = path + "/" + entry->d_name;
66 std::string fullDirAndFile = pathPrefix + dirAndFile;
67 switch ( entry->d_type ) {
68 case DT_REG:
69 if ( processFiles ) {
70 if ( ::lstat(fullDirAndFile.c_str(), &statBuf) == -1 )
71 break;
72 if ( ! S_ISREG(statBuf.st_mode) )
73 break;
74 fileCallback(dirAndFile, statBuf);
75 }
76 break;
77 case DT_DIR:
78 if ( strcmp(entry->d_name, ".") == 0 )
79 break;
80 if ( strcmp(entry->d_name, "..") == 0 )
81 break;
82 if ( dirFilter(dirAndFile) )
83 break;
84 iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback);
85 break;
86 case DT_LNK:
87 // don't follow symlinks, dylib will be found through absolute path
88 break;
89 }
90 }
91 ::closedir(dir);
92 }
93
94
95 bool safeSave(const void* buffer, size_t bufferLen, const std::string& path)
96 {
97 std::string pathTemplate = path + "-XXXXXX";
98 size_t templateLen = strlen(pathTemplate.c_str())+2;
99 char pathTemplateSpace[templateLen];
100 strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
101 int fd = mkstemp(pathTemplateSpace);
102 if ( fd != -1 ) {
103 ssize_t writtenSize = pwrite(fd, buffer, bufferLen, 0);
104 if ( writtenSize == bufferLen ) {
105 ::fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "rw-r--r--"
106 if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
107 ::close(fd);
108 return true; // success
109 }
110 }
111 ::close(fd);
112 ::unlink(pathTemplateSpace);
113 }
114 return false; // failure
115 }
116
117 const void* mapFileReadOnly(const std::string& path, size_t& mappedSize)
118 {
119 struct stat statBuf;
120 if ( ::stat(path.c_str(), &statBuf) != 0 )
121 return nullptr;
122
123 int fd = ::open(path.c_str(), O_RDONLY);
124 if ( fd < 0 )
125 return nullptr;
126
127 const void *p = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
128 ::close(fd);
129 if ( p != MAP_FAILED ) {
130 mappedSize = (size_t)statBuf.st_size;
131 return p;
132 }
133
134 return nullptr;
135 }
136
137 static bool sipIsEnabled()
138 {
139 static bool rootlessEnabled;
140 static dispatch_once_t onceToken;
141 // Check to make sure file system protections are on at all
142 dispatch_once(&onceToken, ^{
143 rootlessEnabled = (csr_check(CSR_ALLOW_UNRESTRICTED_FS) != 0);
144 });
145 return rootlessEnabled;
146 }
147
148 bool isProtectedBySIP(const std::string& path)
149 {
150 if ( !sipIsEnabled() )
151 return false;
152
153 return (rootless_check_trusted(path.c_str()) == 0);
154 }
155
156 bool isProtectedBySIP(int fd)
157 {
158 if ( !sipIsEnabled() )
159 return false;
160
161 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
162 return (rootless_check_trusted_fd(fd) == 0);
163 #else
164 // fallback to using rootless_check_trusted
165 char realPath[MAXPATHLEN];
166 if ( fcntl(fd, F_GETPATH, realPath) == 0 )
167 return (rootless_check_trusted(realPath) == 0);
168 return false;
169 #endif
170 }
171
172 bool fileExists(const std::string& path)
173 {
174 struct stat statBuf;
175 return ( ::stat(path.c_str(), &statBuf) == 0 );
176 }
177
178 // There is an order file specifying the order in which dylibs are laid out in
179 // general, as well as an order file specifying the order in which __DATA_DIRTY
180 // segments are laid out in particular.
181 //
182 // The syntax is one dylib (install name) per line. Blank lines are ignored.
183 // Comments start with the # character.
184 std::unordered_map<std::string, uint32_t> loadOrderFile(const std::string& orderFile) {
185 std::unordered_map<std::string, uint32_t> order;
186
187 std::ifstream myfile(orderFile);
188 if ( myfile.is_open() ) {
189 uint32_t count = 0;
190 std::string line;
191 while ( std::getline(myfile, line) ) {
192 size_t pos = line.find('#');
193 if ( pos != std::string::npos )
194 line.resize(pos);
195 while ( !line.empty() && isspace(line.back()) ) {
196 line.pop_back();
197 }
198 if ( !line.empty() )
199 order[line] = count++;
200 }
201 myfile.close();
202 }
203
204 return order;
205 }
206
207
208 std::string toolDir()
209 {
210 char buffer[PATH_MAX];
211 uint32_t bufsize = PATH_MAX;
212 int result = _NSGetExecutablePath(buffer, &bufsize);
213 if ( result == 0 ) {
214 std::string path = buffer;
215 size_t pos = path.rfind('/');
216 if ( pos != std::string::npos )
217 return path.substr(0,pos+1);
218 }
219 //warning("tool directory not found");
220 return "/tmp/";
221 }
222
223 std::string basePath(const std::string& path)
224 {
225 std::string::size_type slash_pos = path.rfind("/");
226 if (slash_pos != std::string::npos) {
227 slash_pos++;
228 return path.substr(slash_pos);
229 } else {
230 return path;
231 }
232 }
233
234 std::string dirPath(const std::string& path)
235 {
236 std::string::size_type slash_pos = path.rfind("/");
237 if (slash_pos != std::string::npos) {
238 slash_pos++;
239 return path.substr(0, slash_pos);
240 } else {
241 char cwd[MAXPATHLEN];
242 (void)getcwd(cwd, MAXPATHLEN);
243 return cwd;
244 }
245 }
246
247 std::string realPath(const std::string& path)
248 {
249 char resolvedPath[PATH_MAX];
250 if (realpath(dirPath(path).c_str(), &resolvedPath[0]) != nullptr) {
251 return std::string(resolvedPath) + "/" + basePath(path);
252 } else {
253 return "";
254 }
255 }
256
257 std::string realFilePath(const std::string& path)
258 {
259 char resolvedPath[PATH_MAX];
260 if ( realpath(path.c_str(), resolvedPath) != nullptr )
261 return std::string(resolvedPath);
262 else
263 return "";
264 }
265
266
267 std::string normalize_absolute_file_path(std::string path) {
268 std::vector<std::string> components;
269 std::vector<std::string> processed_components;
270 std::stringstream ss(path);
271 std::string retval;
272 std::string item;
273
274 while (std::getline(ss, item, '/')) {
275 components.push_back(item);
276 }
277
278 if (components[0] == ".") {
279 retval = ".";
280 }
281
282 for (auto& component : components) {
283 if (component.empty() || component == ".")
284 continue;
285 else if (component == ".." && processed_components.size())
286 processed_components.pop_back();
287 else
288 processed_components.push_back(component);
289 }
290
291 for (auto & component : processed_components) {
292 retval = retval + "/" + component;
293 }
294
295 return retval;
296 }
297
298
299 #if BUILDING_CACHE_BUILDER
300
301 FileCache fileCache;
302
303 FileCache::FileCache(void)
304 {
305 cache_queue = dispatch_queue_create("com.apple.dyld.cache.cache", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
306 }
307
308 void FileCache::preflightCache(Diagnostics& diags, const std::unordered_set<std::string>& paths)
309 {
310 for (auto& path : paths) {
311 preflightCache(diags, path);
312 }
313 }
314
315 void FileCache::preflightCache(Diagnostics& diags, const std::string& path)
316 {
317 dispatch_async(cache_queue, ^{
318 std::string normalizedPath = normalize_absolute_file_path(path);
319 if (entries.count(normalizedPath) == 0) {
320 entries[normalizedPath] = fill(diags, normalizedPath);
321 }
322 });
323 }
324
325 std::pair<uint8_t*, struct stat> FileCache::cacheLoad(Diagnostics& diags, const std::string path)
326 {
327 __block bool found = false;
328 __block std::pair<uint8_t*, struct stat> retval;
329 std::string normalizedPath = normalize_absolute_file_path(path);
330 dispatch_sync(cache_queue, ^{
331 auto entry = entries.find(normalizedPath);
332 if (entry != entries.end()) {
333 retval = entry->second;
334 found = true;
335 }
336 });
337
338 if (!found) {
339 auto info = fill(diags, normalizedPath);
340 dispatch_sync(cache_queue, ^{
341 auto entry = entries.find(normalizedPath);
342 if (entry != entries.end()) {
343 retval = entry->second;
344 } else {
345 retval = entries[normalizedPath] = info;
346 retval = info;
347 }
348 });
349 }
350
351 return retval;
352 }
353
354 //FIXME error handling
355 std::pair<uint8_t*, struct stat> FileCache::fill(Diagnostics& diags, const std::string& path)
356 {
357 void* buffer_ptr = nullptr;
358 struct stat stat_buf;
359 struct statfs statfs_buf;
360 bool localcopy = true;
361
362 int fd = ::open(path.c_str(), O_RDONLY, 0);
363 if (fd == -1) {
364 diags.verbose("can't open file '%s', errno=%d\n", path.c_str(), errno);
365 return std::make_pair((uint8_t*)(-1), stat_buf);
366 }
367
368 if (fstat(fd, &stat_buf) == -1) {
369 diags.verbose("can't stat open file '%s', errno=%d\n", path.c_str(), errno);
370 ::close(fd);
371 return std::make_pair((uint8_t*)(-1), stat_buf);
372 }
373
374 if (stat_buf.st_size < 4096) {
375 diags.verbose("file too small '%s'\n", path.c_str());
376 ::close(fd);
377 return std::make_pair((uint8_t*)(-1), stat_buf);
378 }
379
380 if(fstatfs(fd, &statfs_buf) == 0) {
381 std::string fsName = statfs_buf.f_fstypename;
382 if (fsName == "hfs" || fsName == "apfs") {
383 localcopy = false;
384 }
385 }
386
387 if (!localcopy) {
388 buffer_ptr = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
389 if (buffer_ptr == MAP_FAILED) {
390 diags.verbose("mmap() for file at %s failed, errno=%d\n", path.c_str(), errno);
391 ::close(fd);
392 return std::make_pair((uint8_t*)(-1), stat_buf);
393 }
394 } else {
395 buffer_ptr = malloc((size_t)stat_buf.st_size);
396 ssize_t readBytes = pread(fd, buffer_ptr, (size_t)stat_buf.st_size, 0);
397 if (readBytes == -1) {
398 diags.verbose("Network read for file at %s failed, errno=%d\n", path.c_str(), errno);
399 ::close(fd);
400 return std::make_pair((uint8_t*)(-1), stat_buf);
401 } else if (readBytes != stat_buf.st_size) {
402 diags.verbose("Network read udnerrun for file at %s, expected %lld bytes, got %zd bytes\n", path.c_str(), stat_buf.st_size, readBytes);
403 ::close(fd);
404 return std::make_pair((uint8_t*)(-1), stat_buf);
405 }
406 }
407
408 ::close(fd);
409
410 return std::make_pair((uint8_t*)buffer_ptr, stat_buf);
411 }
412
413 #endif // BUILDING_CACHE_BUILDER
414