1//===- FuzzerIO.cpp - IO utils. -------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// IO functions.
9//===----------------------------------------------------------------------===//
10
11#include "FuzzerIO.h"
12#include "FuzzerDefs.h"
13#include "FuzzerExtFunctions.h"
14#include "FuzzerUtil.h"
15#include <algorithm>
16#include <cerrno>
17#include <cstdarg>
18#include <cstring>
19#include <fstream>
20#include <iterator>
21#include <sys/stat.h>
22#include <sys/types.h>
23
24namespace fuzzer {
25
26static FILE *OutputFile = stderr;
27
28FILE *GetOutputFile() {
29 return OutputFile;
30}
31
32void SetOutputFile(FILE *NewOutputFile) {
33 OutputFile = NewOutputFile;
34}
35
36long GetEpoch(const std::string &Path) {
37 struct stat St;
38 if (stat(file: Path.c_str(), buf: &St))
39 return 0; // Can't stat, be conservative.
40 return St.st_mtime;
41}
42
43Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
44 std::ifstream T(Path, std::ios::binary);
45 if (ExitOnError && !T) {
46 Printf(Fmt: "No such file: %s (%s); exiting\n", Path.c_str(), strerror(errno));
47 exit(status: 1);
48 }
49
50 T.seekg(off: 0, dir: T.end);
51 auto EndPos = T.tellg();
52 if (EndPos < 0) return {};
53 size_t FileLen = EndPos;
54 if (MaxSize)
55 FileLen = std::min(a: FileLen, b: MaxSize);
56
57 T.seekg(off: 0, dir: T.beg);
58 Unit Res(FileLen);
59 T.read(s: reinterpret_cast<char *>(Res.data()), n: FileLen);
60 return Res;
61}
62
63std::string FileToString(const std::string &Path) {
64 std::ifstream T(Path, std::ios::binary);
65 return std::string((std::istreambuf_iterator<char>(T)),
66 std::istreambuf_iterator<char>());
67}
68
69void CopyFileToErr(const std::string &Path) {
70 Puts(Str: FileToString(Path).c_str());
71}
72
73void WriteToFile(const Unit &U, const std::string &Path) {
74 WriteToFile(Data: U.data(), Size: U.size(), Path);
75}
76
77void WriteToFile(const std::string &Data, const std::string &Path) {
78 WriteToFile(Data: reinterpret_cast<const uint8_t *>(Data.c_str()), Size: Data.size(),
79 Path);
80}
81
82void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
83 // Use raw C interface because this function may be called from a sig handler.
84 FILE *Out = fopen(filename: Path.c_str(), modes: "wb");
85 if (!Out) return;
86 fwrite(ptr: Data, size: sizeof(Data[0]), n: Size, s: Out);
87 fclose(stream: Out);
88}
89
90void AppendToFile(const std::string &Data, const std::string &Path) {
91 AppendToFile(Data: reinterpret_cast<const uint8_t *>(Data.data()), Size: Data.size(),
92 Path);
93}
94
95void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
96 FILE *Out = fopen(filename: Path.c_str(), modes: "a");
97 if (!Out)
98 return;
99 fwrite(ptr: Data, size: sizeof(Data[0]), n: Size, s: Out);
100 fclose(stream: Out);
101}
102
103void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V, long *Epoch,
104 size_t MaxSize, bool ExitOnError,
105 std::vector<std::string> *VPaths) {
106 long E = Epoch ? *Epoch : 0;
107 std::vector<std::string> Files;
108 ListFilesInDirRecursive(Dir: Path, Epoch, V: &Files, /*TopDir*/true);
109 size_t NumLoaded = 0;
110 for (size_t i = 0; i < Files.size(); i++) {
111 auto &X = Files[i];
112 if (Epoch && GetEpoch(Path: X) < E) continue;
113 NumLoaded++;
114 if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024)
115 Printf(Fmt: "Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path);
116 auto S = FileToVector(Path: X, MaxSize, ExitOnError);
117 if (!S.empty()) {
118 V->push_back(x: S);
119 if (VPaths)
120 VPaths->push_back(x: X);
121 }
122 }
123}
124
125void GetSizedFilesFromDir(const std::string &Dir, std::vector<SizedFile> *V) {
126 std::vector<std::string> Files;
127 ListFilesInDirRecursive(Dir, Epoch: 0, V: &Files, /*TopDir*/true);
128 for (auto &File : Files)
129 if (size_t Size = FileSize(Path: File))
130 V->push_back(x: {.File: File, .Size: Size});
131}
132
133std::string DirPlusFile(const std::string &DirPath,
134 const std::string &FileName) {
135 return DirPath + GetSeparator() + FileName;
136}
137
138void DupAndCloseStderr() {
139 int OutputFd = DuplicateFile(Fd: 2);
140 if (OutputFd >= 0) {
141 FILE *NewOutputFile = OpenFile(Fd: OutputFd, Mode: "w");
142 if (NewOutputFile) {
143 OutputFile = NewOutputFile;
144 if (EF->__sanitizer_set_report_fd)
145 EF->__sanitizer_set_report_fd(
146 reinterpret_cast<void *>(GetHandleFromFd(fd: OutputFd)));
147 DiscardOutput(Fd: 2);
148 }
149 }
150}
151
152void CloseStdout() {
153 DiscardOutput(Fd: 1);
154}
155
156void Puts(const char *Str) {
157 fputs(s: Str, stream: OutputFile);
158 fflush(stream: OutputFile);
159}
160
161void Printf(const char *Fmt, ...) {
162 va_list ap;
163 va_start(ap, Fmt);
164 vfprintf(s: OutputFile, format: Fmt, arg: ap);
165 va_end(ap);
166 fflush(stream: OutputFile);
167}
168
169void VPrintf(bool Verbose, const char *Fmt, ...) {
170 if (!Verbose) return;
171 va_list ap;
172 va_start(ap, Fmt);
173 vfprintf(s: OutputFile, format: Fmt, arg: ap);
174 va_end(ap);
175 fflush(stream: OutputFile);
176}
177
178static bool MkDirRecursiveInner(const std::string &Leaf) {
179 // Prevent chance of potential infinite recursion
180 if (Leaf == ".")
181 return true;
182
183 const std::string &Dir = DirName(FileName: Leaf);
184
185 if (IsDirectory(Path: Dir)) {
186 MkDir(Path: Leaf);
187 return IsDirectory(Path: Leaf);
188 }
189
190 bool ret = MkDirRecursiveInner(Leaf: Dir);
191 if (!ret) {
192 // Give up early if a previous MkDir failed
193 return ret;
194 }
195
196 MkDir(Path: Leaf);
197 return IsDirectory(Path: Leaf);
198}
199
200bool MkDirRecursive(const std::string &Dir) {
201 if (Dir.empty())
202 return false;
203
204 if (IsDirectory(Path: Dir))
205 return true;
206
207 return MkDirRecursiveInner(Leaf: Dir);
208}
209
210void RmDirRecursive(const std::string &Dir) {
211 IterateDirRecursive(
212 Dir, DirPreCallback: [](const std::string &Path) {},
213 DirPostCallback: [](const std::string &Path) { RmDir(Path); },
214 FileCallback: [](const std::string &Path) { RemoveFile(Path); });
215}
216
217std::string TempPath(const char *Prefix, const char *Extension) {
218 return DirPlusFile(DirPath: TmpDir(), FileName: std::string("libFuzzerTemp.") + Prefix +
219 std::to_string(val: GetPid()) + Extension);
220}
221
222} // namespace fuzzer
223