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  }