Status readFile(const fs::path& path, std::string& content, size_t size, bool dry_run) { auto handle = OpenReadableFile(path); if (handle.fd < 0) { return Status(1, "Cannot open file for reading: " + path.string()); } struct stat file; if (fstat(handle.fd, &file) < 0) { return Status(1, "Cannot access path: " + path.string()); } off_t file_size = file.st_size; if (file_size == 0 && size > 0) { file_size = static_cast<off_t>(size); } // Erase/clear provided string buffer. content.erase(); // Apply the max byte-read based on file/link target ownership. off_t read_max = (file.st_uid == 0) ? FLAGS_read_max : std::min(FLAGS_read_max, FLAGS_read_user_max); if (file_size > read_max) { VLOG(1) << "Cannot read " << path << " size exceeds limit: " << file_size << " > " << read_max; return Status(1, "File exceeds read limits"); } if (dry_run) { // The caller is only interested in performing file read checks. boost::system::error_code ec; return Status(0, fs::canonical(path, ec).string()); } if (file_size == 0) { off_t total_bytes = 0; ssize_t part_bytes = 0; do { auto part = std::string(4096, '\0'); part_bytes = read(handle.fd, &part[0], 4096); if (part_bytes > 0) { total_bytes += part_bytes; if (total_bytes >= read_max) { return Status(1, "File exceeds read limits"); } content += part.substr(0, part_bytes); } } while (part_bytes > 0); } else { content = std::string(file_size, '\0'); read(handle.fd, &content[0], file_size); } return Status(0, "OK"); }
Status readFile( const fs::path& path, size_t size, size_t block_size, bool dry_run, bool preserve_time, std::function<void(std::string& buffer, size_t size)> predicate) { auto handle = OpenReadableFile(path); if (handle.fd < 0) { return Status(1, "Cannot open file for reading: " + path.string()); } struct stat file; if (fstat(handle.fd, &file) < 0) { return Status(1, "Cannot access path: " + path.string()); } off_t file_size = file.st_size; if (file_size == 0 && size > 0) { file_size = static_cast<off_t>(size); } // Apply the max byte-read based on file/link target ownership. off_t read_max = (file.st_uid == 0) ? FLAGS_read_max : std::min(FLAGS_read_max, FLAGS_read_user_max); if (file_size > read_max) { VLOG(1) << "Cannot read " << path << " size exceeds limit: " << file_size << " > " << read_max; return Status(1, "File exceeds read limits"); } if (dry_run) { // The caller is only interested in performing file read checks. boost::system::error_code ec; return Status(0, fs::canonical(path, ec).string()); } struct timeval times[2]; #if defined(__linux__) TIMESPEC_TO_TIMEVAL(×[0], &file.st_atim); TIMESPEC_TO_TIMEVAL(×[1], &file.st_mtim); #else TIMESPEC_TO_TIMEVAL(×[0], &file.st_atimespec); TIMESPEC_TO_TIMEVAL(×[1], &file.st_mtimespec); #endif if (file_size == 0) { off_t total_bytes = 0; ssize_t part_bytes = 0; do { auto part = std::string(4096, '\0'); part_bytes = read(handle.fd, &part[0], block_size); if (part_bytes > 0) { total_bytes += part_bytes; if (total_bytes >= read_max) { return Status(1, "File exceeds read limits"); } // content += part.substr(0, part_bytes); predicate(part, part_bytes); } } while (part_bytes > 0); } else { auto content = std::string(file_size, '\0'); read(handle.fd, &content[0], file_size); predicate(content, file_size); } // Attempt to restore the atime and mtime before the file read. if (preserve_time && !FLAGS_disable_forensic) { futimes(handle.fd, times); } return Status(0, "OK"); }