xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/tools/filewrapper/filewrapper.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Simple utility to wrap a binary file in a C++ source file.
16 
17 #include <algorithm>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <string>
22 #include <utility>
23 #include <vector>
24 
25 #include "absl/strings/ascii.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/str_format.h"
28 #include "absl/strings/str_replace.h"
29 #include "sandboxed_api/util/fileops.h"
30 #include "sandboxed_api/util/raw_logging.h"
31 #include "sandboxed_api/util/strerror.h"
32 
33 // C-escapes a character and writes it to a file stream.
FWriteCEscapedC(int c,FILE * out)34 void FWriteCEscapedC(int c, FILE* out) {
35   /* clang-format off */
36   constexpr char kCEscapedLen[256] = {
37       4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4,  // \t, \n, \r
38       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
39       1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // "
40       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,  // '0'..'9'
41       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 'A'..'O'
42       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,  // 'P'..'Z', '\'
43       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 'a'..'o'
44       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,  // 'p'..'z', DEL
45       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
46       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
47       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
48       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
49       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
50       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
51       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
52       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
53   };
54   /* clang-format on */
55 
56   int char_len = kCEscapedLen[c];
57   if (char_len == 1) {
58     fputc(c, out);
59   } else if (char_len == 2) {
60     fputc('\\', out);
61     switch (c) {
62       case '\0':
63         fputc('0', out);
64         break;
65       case '\n':
66         fputc('n', out);
67         break;
68       case '\r':
69         fputc('r', out);
70         break;
71       case '\t':
72         fputc('t', out);
73         break;
74       case '\"':
75       case '\'':
76       case '\\':
77       case '?':
78         fputc(c, out);
79         break;
80     }
81   } else {
82     fputc('\\', out);
83     fputc('0' + c / 64, out);
84     fputc('0' + (c % 64) / 8, out);
85     fputc('0' + c % 8, out);
86   }
87 }
88 
89 // Small RAII class that wraps C-style FILE streams and sets up buffering.
90 class File {
91  public:
File(const char * name,const char * mode)92   File(const char* name, const char* mode)
93       : name_{name}, stream_{fopen(name, mode)}, buf_(4096, '\0') {
94     SAPI_RAW_PCHECK(stream_ != nullptr, "Open %s", name_);
95     std::setvbuf(stream_, &buf_[0], _IOFBF, buf_.size());
96     Check();
97   }
~File()98   ~File() { fclose(stream_); }
99 
Check()100   void Check() {
101     if (ferror(stream_)) {
102       SAPI_RAW_PLOG(ERROR, "I/O on %s", name_);
103       _Exit(EXIT_FAILURE);
104     }
105   }
106 
get() const107   FILE* get() const { return stream_; }
108 
109  private:
110   const char* name_;
111   FILE* stream_;
112   std::string buf_;
113 };
114 
115 // Format literals for generating the .h file
116 constexpr const char kHFileHeaderFmt[] =
117     R"(// Automatically generated by sapi_cc_embed_data() Bazel rule
118 
119 #ifndef SANDBOXED_API_FILE_TOC_H_
120 #define SANDBOXED_API_FILE_TOC_H_
121 
122 #include <cstddef>
123 
124 struct FileToc {
125   const char* name;
126   const char* data;
127   size_t size;
128   // Not actually used/computed by sapi_cc_embed_data(), this is for
129   // compatibility with legacy code.
130   unsigned char md5digest[16];
131 };
132 
133 #endif  // SANDBOXED_API_FILE_TOC_H_
134 
135 #ifndef %1$s
136 #define %1$s
137 
138 )";
139 constexpr const char kHNamespaceBeginFmt[] =
140     R"(namespace %s {
141 )";
142 constexpr const char kHFileTocDefsFmt[] =
143     R"(
144 const FileToc* %1$s_create();
145 size_t %1$s_size();
146 )";
147 constexpr const char kHNamespaceEndFmt[] =
148     R"(
149 }  // namespace %s
150 )";
151 constexpr const char kHFileFooterFmt[] =
152     R"(
153 #endif  // %s
154 )";
155 
156 // Format literals for generating the .cc file out of the input files.
157 constexpr const char kCcFileHeaderFmt[] =
158     R"(// Automatically generated by sapi_cc_embed_data() build rule
159 
160 #include "%s.h"
161 #include "absl/base/macros.h"
162 #include "absl/strings/string_view.h"
163 
164 )";
165 constexpr const char kCcNamespaceBeginFmt[] =
166     R"(namespace %s {
167 
168 )";
169 constexpr const char kCcDataBeginFmt[] =
170     R"(constexpr absl::string_view %s = {")";
171 constexpr const char kCcDataEndFmt[] =
172     R"(", %d};
173 )";
174 constexpr const char kCcFileTocDefsBegin[] =
175     R"(
176 constexpr FileToc kToc[] = {
177 )";
178 constexpr const char kCcFileTocDefsEntryFmt[] =
179     R"(    {"%1$s", %2$s.data(), %2$s.size(), {}},
180 )";
181 constexpr const char kCcFileTocDefsEndFmt[] =
182     R"(
183     // Terminate array
184     {nullptr, nullptr, 0, {}},
185 };
186 
187 const FileToc* %1$s_create() {
188   return kToc;
189 }
190 
191 size_t %1$s_size() {
192   return ABSL_ARRAYSIZE(kToc) - 1;
193 }
194 )";
195 constexpr const char kCcNamespaceEndFmt[] =
196     R"(
197 }  // namespace %s
198 )";
199 
main(int argc,char * argv[])200 int main(int argc, char* argv[]) {
201   if (argc < 7) {
202     // We're not aiming for human usability here, as this tool is always run as
203     // part of the build.
204     absl::FPrintF(stderr,
205                   "%s PACKAGE NAME NAMESPACE OUTPUT_H OUTPUT_CC INPUT...\n",
206                   argv[0]);
207     return EXIT_FAILURE;
208   }
209   char** arg = &argv[1];
210 
211   const char* package = *arg++;
212   --argc;
213   const char* name = *arg++;
214   std::string toc_ident = absl::StrReplaceAll(name, {{"-", "_"}});
215   --argc;
216 
217   const char* ns = *arg++;
218   const bool have_ns = strlen(ns) > 0;
219   --argc;
220 
221   {  // Write header file first.
222     File out_h(*arg++, "wb");
223     --argc;
224     std::string header_guard = absl::StrFormat("%s_%s_H_", package, toc_ident);
225     std::replace_if(
226         header_guard.begin(), header_guard.end(),
227         [](char c) { return !absl::ascii_isalnum(c); }, '_');
228     absl::FPrintF(out_h.get(), kHFileHeaderFmt, header_guard);
229     if (have_ns) {
230       absl::FPrintF(out_h.get(), kHNamespaceBeginFmt, ns);
231     }
232     absl::FPrintF(out_h.get(), kHFileTocDefsFmt, toc_ident);
233     if (have_ns) {
234       absl::FPrintF(out_h.get(), kHNamespaceEndFmt, ns);
235     }
236     absl::FPrintF(out_h.get(), kHFileFooterFmt, header_guard);
237     out_h.Check();
238   }
239 
240   // Write actual translation unit with the data.
241   File out_cc(*arg++, "wb");
242   --argc;
243 
244   std::string package_name = package;
245   if (!package_name.empty()) {
246     absl::StrAppend(&package_name, "/");
247   }
248   absl::StrAppend(&package_name, name);
249   absl::FPrintF(out_cc.get(), kCcFileHeaderFmt, package_name);
250   if (have_ns) {
251     absl::FPrintF(out_cc.get(), kCcNamespaceBeginFmt, ns);
252   }
253 
254   std::vector<std::pair<std::string, std::string>> toc_entries;
255   while (argc > 1) {
256     const char* in_filename = *arg++;
257     --argc;
258     File in(in_filename, "rb");
259 
260     std::string basename = sapi::file_util::fileops::Basename(in_filename);
261     std::string ident = absl::StrCat("k", basename);
262     std::replace_if(
263         ident.begin(), ident.end(),
264         [](char c) { return !absl::ascii_isalnum(c); }, '_');
265     absl::FPrintF(out_cc.get(), kCcDataBeginFmt, ident);
266     // Remember identifiers, they are needed in the kToc array.
267     toc_entries.emplace_back(std::move(basename), std::move(ident));
268 
269     int c;
270     while ((c = fgetc(in.get())) != EOF) {
271       FWriteCEscapedC(c, out_cc.get());
272     }
273     in.Check();
274 
275     absl::FPrintF(out_cc.get(), kCcDataEndFmt, ftell(in.get()));
276   }
277   absl::FPrintF(out_cc.get(), kCcFileTocDefsBegin);
278   for (const auto& entry : toc_entries) {
279     absl::FPrintF(out_cc.get(), kCcFileTocDefsEntryFmt, entry.first,
280                   entry.second);
281   }
282   absl::FPrintF(out_cc.get(), kCcFileTocDefsEndFmt, toc_ident);
283 
284   if (have_ns) {
285     absl::FPrintF(out_cc.get(), kCcNamespaceEndFmt, ns);
286   }
287 
288   out_cc.Check();
289   return EXIT_SUCCESS;
290 }
291