1 // Copyright 2020 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 #include <fcntl.h>
16 #include <unistd.h>
17
18 #include "../sandboxed.h" // NOLINT(build/include)
19 #include "../tests/libpng.h" // NOLINT(build/include)
20
21 struct Data {
22 int width;
23 int height;
24 uint8_t color_type;
25 uint8_t bit_depth;
26 int number_of_passes;
27 size_t rowbytes;
28 std::unique_ptr<sapi::v::Array<uint8_t>> row_pointers;
29 };
30
ReadPng(LibPNGApi & api,absl::string_view infile)31 absl::StatusOr<Data> ReadPng(LibPNGApi& api, absl::string_view infile) {
32 sapi::v::Fd fd(open(infile.data(), O_RDONLY));
33
34 if (fd.GetValue() < 0) {
35 return absl::InternalError("Error opening input file");
36 }
37
38 SAPI_RETURN_IF_ERROR((&api)->sandbox()->TransferToSandboxee(&fd));
39
40 if (fd.GetRemoteFd() < 0) {
41 return absl::InternalError("Error receiving remote FD");
42 }
43
44 absl::StatusOr<void*> status_or_file;
45 sapi::v::ConstCStr rb_var("rb");
46 SAPI_ASSIGN_OR_RETURN(status_or_file,
47 api.png_fdopen(fd.GetRemoteFd(), rb_var.PtrBefore()));
48
49 sapi::v::RemotePtr file(status_or_file.value());
50 if (!file.GetValue()) {
51 return absl::InternalError(absl::StrCat("Could not open ", infile));
52 }
53
54 sapi::v::Array<char> header(8);
55 SAPI_RETURN_IF_ERROR(
56 api.png_fread(header.PtrBoth(), 1, header.GetSize(), &file));
57
58 SAPI_ASSIGN_OR_RETURN(int return_value,
59 api.png_sig_cmp(header.PtrBoth(), 0, header.GetSize()));
60 if (return_value != 0) {
61 return absl::InternalError(absl::StrCat(infile, " is not a PNG file"));
62 }
63
64 absl::StatusOr<png_structp> status_or_png_structp;
65 sapi::v::ConstCStr ver_string_var(PNG_LIBPNG_VER_STRING);
66 sapi::v::NullPtr null = sapi::v::NullPtr();
67 SAPI_ASSIGN_OR_RETURN(
68 status_or_png_structp,
69 api.png_create_read_struct_wrapper(ver_string_var.PtrBefore(), &null));
70
71 sapi::v::RemotePtr struct_ptr(status_or_png_structp.value());
72 if (!struct_ptr.GetValue()) {
73 return absl::InternalError("png_create_read_struct_wrapper failed");
74 }
75
76 absl::StatusOr<png_infop> status_or_png_infop;
77 SAPI_ASSIGN_OR_RETURN(status_or_png_infop,
78 api.png_create_info_struct(&struct_ptr));
79
80 sapi::v::RemotePtr info_ptr(status_or_png_infop.value());
81 if (!info_ptr.GetValue()) {
82 return absl::InternalError("png_create_info_struct failed");
83 }
84
85 SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
86 SAPI_RETURN_IF_ERROR(api.png_init_io_wrapper(&struct_ptr, &file));
87 SAPI_RETURN_IF_ERROR(api.png_set_sig_bytes(&struct_ptr, header.GetSize()));
88 SAPI_RETURN_IF_ERROR(api.png_read_info(&struct_ptr, &info_ptr));
89
90 Data data;
91 SAPI_ASSIGN_OR_RETURN(data.width,
92 api.png_get_image_width(&struct_ptr, &info_ptr));
93
94 SAPI_ASSIGN_OR_RETURN(data.height,
95 api.png_get_image_height(&struct_ptr, &info_ptr));
96
97 SAPI_ASSIGN_OR_RETURN(data.color_type,
98 api.png_get_color_type(&struct_ptr, &info_ptr));
99
100 SAPI_ASSIGN_OR_RETURN(data.bit_depth,
101 api.png_get_bit_depth(&struct_ptr, &info_ptr));
102
103 SAPI_ASSIGN_OR_RETURN(data.number_of_passes,
104 api.png_set_interlace_handling(&struct_ptr));
105
106 SAPI_RETURN_IF_ERROR(api.png_read_update_info(&struct_ptr, &info_ptr));
107 SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
108
109 SAPI_ASSIGN_OR_RETURN(data.rowbytes,
110 api.png_get_rowbytes(&struct_ptr, &info_ptr));
111 data.row_pointers =
112 std::make_unique<sapi::v::Array<uint8_t>>(data.height * data.rowbytes);
113
114 SAPI_RETURN_IF_ERROR(api.png_read_image_wrapper(
115 &struct_ptr, data.row_pointers->PtrAfter(), data.height, data.rowbytes));
116
117 SAPI_RETURN_IF_ERROR(api.png_fclose(&file));
118 return data;
119 }
120
WritePng(LibPNGApi & api,absl::string_view outfile,Data & data)121 absl::Status WritePng(LibPNGApi& api, absl::string_view outfile, Data& data) {
122 sapi::v::Fd fd(open(outfile.data(), O_WRONLY));
123 if (fd.GetValue() < 0) {
124 return absl::InternalError("Error opening output file");
125 }
126
127 SAPI_RETURN_IF_ERROR((&api)->sandbox()->TransferToSandboxee(&fd));
128 if (fd.GetRemoteFd() < 0) {
129 return absl::InternalError("Error receiving remote FD");
130 }
131
132 absl::StatusOr<void*> status_or_file;
133 sapi::v::ConstCStr wb_var("wb");
134 SAPI_ASSIGN_OR_RETURN(status_or_file,
135 api.png_fdopen(fd.GetRemoteFd(), wb_var.PtrBefore()));
136
137 sapi::v::RemotePtr file(status_or_file.value());
138 if (!file.GetValue()) {
139 return absl::InternalError(absl::StrCat("Could not open ", outfile));
140 }
141
142 absl::StatusOr<png_structp> status_or_png_structp;
143 sapi::v::ConstCStr ver_string_var(PNG_LIBPNG_VER_STRING);
144 sapi::v::NullPtr null = sapi::v::NullPtr();
145 SAPI_ASSIGN_OR_RETURN(
146 status_or_png_structp,
147 api.png_create_write_struct_wrapper(ver_string_var.PtrBefore(), &null));
148
149 sapi::v::RemotePtr struct_ptr(status_or_png_structp.value());
150 if (!struct_ptr.GetValue()) {
151 return absl::InternalError("png_create_write_struct_wrapper failed");
152 }
153
154 absl::StatusOr<png_infop> status_or_png_infop;
155 SAPI_ASSIGN_OR_RETURN(status_or_png_infop,
156 api.png_create_info_struct(&struct_ptr));
157
158 sapi::v::RemotePtr info_ptr(status_or_png_infop.value());
159 if (!info_ptr.GetValue()) {
160 return absl::InternalError("png_create_info_struct failed");
161 }
162
163 SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
164 SAPI_RETURN_IF_ERROR(api.png_init_io_wrapper(&struct_ptr, &file));
165
166 SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
167 SAPI_RETURN_IF_ERROR(
168 api.png_set_IHDR(&struct_ptr, &info_ptr, data.width, data.height,
169 data.bit_depth, data.color_type, PNG_INTERLACE_NONE,
170 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE));
171
172 SAPI_RETURN_IF_ERROR(api.png_write_info(&struct_ptr, &info_ptr));
173
174 SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
175 SAPI_RETURN_IF_ERROR(api.png_write_image_wrapper(
176 &struct_ptr, data.row_pointers->PtrBefore(), data.height, data.rowbytes));
177
178 SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
179 SAPI_RETURN_IF_ERROR(api.png_write_end(&struct_ptr, &null));
180
181 SAPI_RETURN_IF_ERROR(api.png_fclose(&file));
182 return absl::OkStatus();
183 }
184
LibPNGMain(const std::string & infile,const std::string & outfile)185 absl::Status LibPNGMain(const std::string& infile, const std::string& outfile) {
186 LibPNGSapiSandbox sandbox;
187 sandbox.AddFile(infile);
188 sandbox.AddFile(outfile);
189
190 SAPI_RETURN_IF_ERROR(sandbox.Init());
191 LibPNGApi api(&sandbox);
192
193 SAPI_ASSIGN_OR_RETURN(Data data, ReadPng(api, infile));
194
195 if (data.color_type != PNG_COLOR_TYPE_RGBA &&
196 data.color_type != PNG_COLOR_TYPE_RGB) {
197 return absl::InternalError(absl::StrCat(
198 infile, " has unexpected color type. Expected RGB or RGBA"));
199 }
200
201 size_t channel_count = 3;
202 if (data.color_type == PNG_COLOR_TYPE_RGBA) {
203 channel_count = 4;
204 }
205
206 // RGB to BGR
207 for (size_t i = 0; i != data.height; ++i) {
208 for (size_t j = 0; j != data.width; ++j) {
209 uint8_t r = (*data.row_pointers)[i * data.rowbytes + j * channel_count];
210 uint8_t g =
211 (*data.row_pointers)[i * data.rowbytes + j * channel_count + 1];
212 uint8_t b =
213 (*data.row_pointers)[i * data.rowbytes + j * channel_count + 2];
214 (*data.row_pointers)[i * data.rowbytes + j * channel_count] = b;
215 (*data.row_pointers)[i * data.rowbytes + j * channel_count + 2] = r;
216 }
217 }
218
219 SAPI_RETURN_IF_ERROR(WritePng(api, outfile, data));
220 return absl::OkStatus();
221 }
222
main(int argc,char * argv[])223 int main(int argc, char* argv[]) {
224 if (argc != 3) {
225 LOG(ERROR) << "Usage: example5 infile outfile";
226 return EXIT_FAILURE;
227 }
228
229 auto status = LibPNGMain(argv[1], argv[2]);
230 if (!status.ok()) {
231 LOG(ERROR) << "LibPNGMain failed with error:\n"
232 << status.ToString() << '\n';
233 return EXIT_FAILURE;
234 }
235
236 return EXIT_SUCCESS;
237 }
238