Loading...
dyld3/shared-cache/FileUtils.cpp dyld-551.3 dyld-851.27
--- dyld/dyld-551.3/dyld3/shared-cache/FileUtils.cpp
+++ dyld/dyld-851.27/dyld3/shared-cache/FileUtils.cpp
@@ -45,14 +45,12 @@
 #include <sstream>
 
 #include "FileUtils.h"
+#include "StringUtils.h"
 #include "Diagnostics.h"
-
-#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
-extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import));
-#endif
-
-
-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)
+#include "JSONReader.h"
+
+
+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, bool recurse)
 {
     std::string fullDirPath = pathPrefix + path;
     DIR* dir = ::opendir(fullDirPath.c_str());
@@ -62,7 +60,7 @@
     }
     while (dirent* entry = readdir(dir)) {
         struct stat statBuf;
-        std::string dirAndFile = path + "/" + entry->d_name;
+        std::string dirAndFile = path + (path.back() != '/' ? "/" : "") + entry->d_name;
         std::string fullDirAndFile = pathPrefix + dirAndFile;
          switch ( entry->d_type ) {
             case DT_REG:
@@ -81,7 +79,8 @@
                     break;
                 if ( dirFilter(dirAndFile) )
                     break;
-                iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback);
+                if (recurse)
+                    iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback, processFiles, true);
                 break;
             case DT_LNK:
                 // don't follow symlinks, dylib will be found through absolute path
@@ -101,7 +100,7 @@
     int fd = mkstemp(pathTemplateSpace);
     if ( fd != -1 ) {
         ssize_t writtenSize = pwrite(fd, buffer, bufferLen, 0);
-        if ( writtenSize == bufferLen ) {
+        if ( (size_t)writtenSize == bufferLen ) {
             ::fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "rw-r--r--"
             if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
                 ::close(fd);
@@ -114,13 +113,13 @@
     return false; // failure
 }
 
-const void* mapFileReadOnly(const std::string& path, size_t& mappedSize)
+const void* mapFileReadOnly(const char* path, size_t& mappedSize)
 {
     struct stat statBuf;
-    if ( ::stat(path.c_str(), &statBuf) != 0 )
+    if ( ::stat(path, &statBuf) != 0 )
         return nullptr;
 
-    int fd = ::open(path.c_str(), O_RDONLY);
+    int fd = ::open(path, O_RDONLY);
     if ( fd < 0 )
         return nullptr;
 
@@ -132,41 +131,6 @@
     }
 
     return nullptr;
-}
-
-static bool sipIsEnabled()
-{
-    static bool             rootlessEnabled;
-    static dispatch_once_t  onceToken;
-    // Check to make sure file system protections are on at all
-    dispatch_once(&onceToken, ^{
-        rootlessEnabled = (csr_check(CSR_ALLOW_UNRESTRICTED_FS) != 0);
-    });
-    return rootlessEnabled;
-}
-
-bool isProtectedBySIP(const std::string& path)
-{
-    if ( !sipIsEnabled() )
-        return false;
-
-    return (rootless_check_trusted(path.c_str()) == 0);
-}
-
-bool isProtectedBySIP(int fd)
-{
-    if ( !sipIsEnabled() )
-        return false;
-
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
-    return (rootless_check_trusted_fd(fd) == 0);
-#else
-    // fallback to using rootless_check_trusted
-    char realPath[MAXPATHLEN];
-    if ( fcntl(fd, F_GETPATH, realPath) == 0 )
-        return (rootless_check_trusted(realPath) == 0);
-    return false;
-#endif
 }
 
 bool fileExists(const std::string& path)
@@ -181,24 +145,37 @@
 //
 // The syntax is one dylib (install name) per line.  Blank lines are ignored.
 // Comments start with the # character.
-std::unordered_map<std::string, uint32_t> loadOrderFile(const std::string& orderFile) {
+std::unordered_map<std::string, uint32_t> parseOrderFile(const std::string& orderFileData) {
     std::unordered_map<std::string, uint32_t> order;
 
-    std::ifstream myfile(orderFile);
-    if ( myfile.is_open() ) {
-        uint32_t count = 0;
-        std::string line;
-        while ( std::getline(myfile, line) ) {
-            size_t pos = line.find('#');
-            if ( pos != std::string::npos )
-                line.resize(pos);
-            while ( !line.empty() && isspace(line.back()) ) {
-                line.pop_back();
-            }
-            if ( !line.empty() )
-                order[line] = count++;
-        }
-        myfile.close();
+    if (orderFileData.empty())
+        return order;
+
+    std::stringstream myData(orderFileData);
+
+    uint32_t count = 0;
+    std::string line;
+    while ( std::getline(myData, line) ) {
+        size_t pos = line.find('#');
+        if ( pos != std::string::npos )
+            line.resize(pos);
+        while ( !line.empty() && isspace(line.back()) ) {
+            line.pop_back();
+        }
+        if ( !line.empty() )
+            order[line] = count++;
+    }
+    return order;
+}
+
+std::string loadOrderFile(const std::string& orderFilePath) {
+    std::string order;
+
+    size_t size = 0;
+    char* data = (char*)mapFileReadOnly(orderFilePath.c_str(), size);
+    if (data) {
+        order = std::string(data, size);
+        ::munmap((void*)data, size);
     }
 
     return order;
@@ -303,23 +280,6 @@
 FileCache::FileCache(void)
 {
     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));
-}
-
-void FileCache::preflightCache(Diagnostics& diags, const std::unordered_set<std::string>& paths)
-{
-    for (auto& path : paths) {
-        preflightCache(diags, path);
-    }
-}
-
-void FileCache::preflightCache(Diagnostics& diags, const std::string& path)
-{
-    dispatch_async(cache_queue, ^{
-        std::string normalizedPath = normalize_absolute_file_path(path);
-        if (entries.count(normalizedPath) == 0) {
-            entries[normalizedPath] = fill(diags, normalizedPath);
-        }
-    });
 }
 
 std::pair<uint8_t*, struct stat> FileCache::cacheLoad(Diagnostics& diags, const std::string path)
@@ -410,5 +370,173 @@
     return std::make_pair((uint8_t*)buffer_ptr, stat_buf);
 }
 
+static void normalizePath(std::string& path) {
+    // Remove a bunch of stuff we don't need, like trailing slashes.
+    while ( !path.empty() && (path.back() == '/'))
+        path.pop_back();
+}
+
+void SymlinkResolver::addFile(Diagnostics& diags, std::string path) {
+    if (path.front() != '/') {
+        diags.error("Path must start with '/'");
+        return;
+    }
+    if (symlinks.find(path) != symlinks.end()) {
+        diags.error("Cannot add regular file as it is already a symlink");
+        return;
+    }
+    filePaths.insert(path);
+}
+
+void SymlinkResolver::addSymlink(Diagnostics& diags, std::string fromPath, std::string toPath) {
+    normalizePath(fromPath);
+    normalizePath(toPath);
+    if (fromPath.front() != '/') {
+        diags.error("Path must start with '/'");
+        return;
+    }
+    if (filePaths.find(fromPath) != filePaths.end()) {
+        diags.error("Cannot add symlink from '%s' as it is already a regular path", fromPath.c_str());
+        return;
+    }
+    auto itAndInserted = symlinks.insert({ fromPath, toPath });
+    if (!itAndInserted.second) {
+        // The path is already a symlink.  Make sure its a dupe.
+        if (toPath != itAndInserted.first->second) {
+            diags.error("Duplicate symlink for path '%s'", fromPath.c_str());
+            return;
+        }
+    }
+}
+
+std::string SymlinkResolver::realPath(Diagnostics& diags, const std::string& originalPath) const {
+    // First make sure the path doesn't have any magic in it.
+    std::string path = originalPath;
+    normalizePath(path);
+
+    std::set<std::string> seenSymlinks;
+
+    // Now see if any prefix is a symlink
+    if (path.front() != '/')
+        return path;
+
+    std::string::size_type prev_pos = 0;
+    while (prev_pos != std::string::npos) {
+        std::string::size_type pos = path.find("/", prev_pos + 1);
+
+        // First look to see if this path component is special, eg, ., .., etc.
+        std::string component = path.substr(prev_pos, pos - prev_pos);
+        if (component == "/..") {
+            // Fold with the previous path component.
+            if (prev_pos == 0) {
+                // This is the root path, and .. applied to / is just /
+                path = path.substr(3);
+                prev_pos = 0;
+            } else {
+                std::string::size_type lastSlashPos = path.rfind("/", prev_pos - 1);
+                path = path.substr(0, lastSlashPos) + path.substr(pos);
+                prev_pos = lastSlashPos;
+            }
+            continue;
+        } else if (component == "/.") {
+            if (prev_pos == 0) {
+                // Path starts with /./ so just remove the first one.
+                path = path.substr(2);
+            } else {
+                if (pos == std::string::npos) {
+                    // Trailing . on the path
+                    path = path.substr(0, prev_pos );
+                } else {
+                    path = path.substr(0, prev_pos) + path.substr(pos);
+                }
+            }
+            continue;
+        } else if (component == "/") {
+            // Path must contain // somewhere so strip out the duplicates.
+            if (prev_pos == 0) {
+                // Path starts with // so just remove the first one.
+                path = path.substr(1);
+            } else {
+                if (pos == std::string::npos) {
+                    // Trailing / on the path
+                    path = path.substr(0, prev_pos);
+                    prev_pos = pos;
+                } else {
+                    path = path.substr(0, pos) + path.substr(pos + 1);
+                }
+            }
+            continue;
+        }
+
+        // Path is not special, so see if it is a symlink to something.
+        std::string prefix = path.substr(0, pos);
+        //printf("%s\n", prefix.c_str());
+        auto it = symlinks.find(prefix);
+        if (it == symlinks.end()) {
+            // This is not a symlink so move to the next prefix.
+            prev_pos = pos;
+            continue;
+        }
+
+        // If we've already done this prefix then error out.
+        if (seenSymlinks.count(prefix)) {
+            diags.error("Loop in symlink processing for '%s'", originalPath.c_str());
+            return std::string();
+        }
+
+        seenSymlinks.insert(prefix);
+
+        // This is a symlink, so resolve the new path.
+        std::string toPath = it->second;
+        if (toPath.front() == '/') {
+            // Symlink points to an absolute address so substitute the whole prefix for the new path
+            // If we didn't substitute the last component of the path then there is also a path suffix.
+            std::string pathSuffix = "";
+            if (pos != std::string::npos) {
+                std::string::size_type nextSlashPos = path.find("/", pos + 1);
+                if (nextSlashPos != std::string::npos)
+                    pathSuffix = path.substr(nextSlashPos);
+            }
+            path = toPath + pathSuffix;
+            prev_pos = 0;
+            continue;
+        }
+
+        // Symlink points to a relative path so we need to do more processing to get the real path.
+
+        // First calculate which part of the previous prefix we'll keep.  Eg, in /a/b/c where "b -> blah", we want to keep /a here.
+        std::string prevPrefix = path.substr(0, prev_pos);
+        //printf("prevPrefix %s\n", prevPrefix.c_str());
+
+        // If we didn't substitute the last component of the path then there is also a path suffix.
+        std::string pathSuffix = "";
+        if (prefix.size() != path.size())
+            pathSuffix = path.substr(pos);
+
+        // The new path is the remaining prefix, plus the symlink target, plus any remaining suffix from the original path.
+        path = prevPrefix + "/" + toPath + pathSuffix;
+        prev_pos = 0;
+    }
+    return path;
+}
+
+std::vector<DyldSharedCache::FileAlias> SymlinkResolver::getResolvedSymlinks(Diagnostics& diags) {
+    diags.assertNoError();
+    std::vector<DyldSharedCache::FileAlias> aliases;
+    for (auto& fromPathAndToPath : symlinks) {
+        std::string newPath = realPath(diags, fromPathAndToPath.first);
+        if (diags.hasError()) {
+            aliases.clear();
+            return aliases;
+        }
+
+        if (filePaths.count(newPath)) {
+            aliases.push_back({ newPath, fromPathAndToPath.first });
+            // printf("symlink ('%s' -> '%s') resolved to '%s'\n", fromPathAndToPath.first.c_str(), fromPathAndToPath.second.c_str(), newPath.c_str());
+        }
+    }
+    return aliases;
+}
+
 #endif // BUILDING_CACHE_BUILDER