github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/executor/files.h (about) 1 // Copyright 2024 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 #include <memory> 5 #include <string> 6 #include <utility> 7 #include <vector> 8 9 #include <dirent.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <glob.h> 13 #include <stdarg.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 static std::vector<std::string> Glob(const std::string& pattern) 18 { 19 glob_t buf = {}; 20 buf.gl_opendir = reinterpret_cast<void* (*)(const char* name)>(opendir); 21 buf.gl_closedir = reinterpret_cast<void (*)(void* dirp)>(closedir); 22 // Use own readdir to ignore links. Links to files are not useful to us, 23 // we will discover the target file itself. Links to directories are harmful 24 // because they cause recursion, or lead outside of the target glob 25 // (e.g. /proc/self/{root,cwd}). 26 // However, we want to keep few links: /proc/self, /proc/thread-self, 27 // /sys/kernel/slab/kmalloc-64 (may be a link with slab merging), 28 // and cgroup links created in the test dir. 29 // This is a hacky way to do it b/c e.g. "self" will be matched in all paths, 30 // not just /proc. A proper fix would require writing completly custom version of glob 31 // that would support recursion and would allow using/not using links on demand. 32 33 buf.gl_readdir = [](void* dir) -> dirent* { 34 for (;;) { 35 struct dirent* ent = readdir(static_cast<DIR*>(dir)); 36 if (!ent || ent->d_type != DT_LNK || 37 !strcmp(ent->d_name, "self") || 38 !strcmp(ent->d_name, "thread-self") || 39 !strcmp(ent->d_name, "kmalloc-64") || 40 !strcmp(ent->d_name, "cgroup") || 41 !strcmp(ent->d_name, "cgroup.cpu") || 42 !strcmp(ent->d_name, "cgroup.net")) 43 return ent; 44 } 45 }; 46 buf.gl_stat = stat; 47 buf.gl_lstat = lstat; 48 int res = glob(pattern.c_str(), GLOB_MARK | GLOB_NOSORT | GLOB_ALTDIRFUNC, nullptr, &buf); 49 if (res != 0 && res != GLOB_NOMATCH) 50 failmsg("glob failed", "pattern='%s' res=%d", pattern.c_str(), res); 51 std::vector<std::string> files; 52 for (size_t i = 0; i < buf.gl_pathc; i++) { 53 const char* file = buf.gl_pathv[i]; 54 if (file[strlen(file) - 1] == '/') 55 continue; 56 files.push_back(file); 57 } 58 globfree(&buf); 59 debug("glob %s resolved to %zu files\n", pattern.c_str(), files.size()); 60 return files; 61 } 62 63 static std::unique_ptr<rpc::FileInfoRawT> ReadFile(const std::string& file) 64 { 65 auto info = std::make_unique<rpc::FileInfoRawT>(); 66 info->name = file; 67 int fd = open(file.c_str(), O_RDONLY); 68 if (fd == -1) { 69 info->exists = errno != EEXIST && errno != ENOENT; 70 info->error = strerror(errno); 71 } else { 72 info->exists = true; 73 for (;;) { 74 constexpr size_t kChunk = 4 << 10; 75 info->data.resize(info->data.size() + kChunk); 76 ssize_t n = read(fd, info->data.data() + info->data.size() - kChunk, kChunk); 77 if (n < 0) { 78 info->error = strerror(errno); 79 break; 80 } 81 info->data.resize(info->data.size() - kChunk + n); 82 if (n == 0) 83 break; 84 } 85 close(fd); 86 } 87 debug("reading file %s: size=%zu exists=%d error=%s\n", 88 info->name.c_str(), info->data.size(), info->exists, info->error.c_str()); 89 return info; 90 } 91 92 static std::string ReadTextFile(const char* file_fmt, ...) 93 { 94 char file[1024]; 95 va_list args; 96 va_start(args, file_fmt); 97 vsnprintf(file, sizeof(file), file_fmt, args); 98 va_end(args); 99 file[sizeof(file) - 1] = 0; 100 auto data = ReadFile(file)->data; 101 std::string str(data.begin(), data.end()); 102 while (!str.empty() && (str.back() == '\n' || str.back() == 0)) 103 str.resize(str.size() - 1); 104 return str; 105 } 106 107 static std::vector<std::unique_ptr<rpc::FileInfoRawT>> ReadFiles(const std::vector<std::string>& files) 108 { 109 std::vector<std::unique_ptr<rpc::FileInfoRawT>> results; 110 for (const auto& file : files) { 111 if (!strchr(file.c_str(), '*')) { 112 results.push_back(ReadFile(file)); 113 continue; 114 } 115 for (const auto& match : Glob(file)) 116 results.push_back(ReadFile(match)); 117 } 118 return results; 119 }