1*ec63e07aSXin Li // Copyright 2020 Google LLC
2*ec63e07aSXin Li //
3*ec63e07aSXin Li // Licensed under the Apache License, Version 2.0 (the "License");
4*ec63e07aSXin Li // you may not use this file except in compliance with the License.
5*ec63e07aSXin Li // You may obtain a copy of the License at
6*ec63e07aSXin Li //
7*ec63e07aSXin Li // https://www.apache.org/licenses/LICENSE-2.0
8*ec63e07aSXin Li //
9*ec63e07aSXin Li // Unless required by applicable law or agreed to in writing, software
10*ec63e07aSXin Li // distributed under the License is distributed on an "AS IS" BASIS,
11*ec63e07aSXin Li // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*ec63e07aSXin Li // See the License for the specific language governing permissions and
13*ec63e07aSXin Li // limitations under the License.
14*ec63e07aSXin Li
15*ec63e07aSXin Li #include "guetzli_transaction.h" // NOLINT(build/include)
16*ec63e07aSXin Li
17*ec63e07aSXin Li #include <fcntl.h>
18*ec63e07aSXin Li #include <sys/stat.h>
19*ec63e07aSXin Li #include <sys/types.h>
20*ec63e07aSXin Li #include <unistd.h>
21*ec63e07aSXin Li
22*ec63e07aSXin Li #include <iostream>
23*ec63e07aSXin Li #include <memory>
24*ec63e07aSXin Li
25*ec63e07aSXin Li #include "absl/status/statusor.h"
26*ec63e07aSXin Li
27*ec63e07aSXin Li namespace guetzli::sandbox {
28*ec63e07aSXin Li
Main()29*ec63e07aSXin Li absl::Status GuetzliTransaction::Main() {
30*ec63e07aSXin Li sapi::v::Fd in_fd(open(params_.in_file, O_RDONLY));
31*ec63e07aSXin Li
32*ec63e07aSXin Li if (in_fd.GetValue() < 0) {
33*ec63e07aSXin Li return absl::FailedPreconditionError("Error opening input file");
34*ec63e07aSXin Li }
35*ec63e07aSXin Li
36*ec63e07aSXin Li SAPI_ASSIGN_OR_RETURN(image_type_, GetImageTypeFromFd(in_fd.GetValue()));
37*ec63e07aSXin Li SAPI_RETURN_IF_ERROR(sandbox()->TransferToSandboxee(&in_fd));
38*ec63e07aSXin Li
39*ec63e07aSXin Li if (in_fd.GetRemoteFd() < 0) {
40*ec63e07aSXin Li return absl::FailedPreconditionError(
41*ec63e07aSXin Li "Error receiving remote FD: remote input fd is set to -1");
42*ec63e07aSXin Li }
43*ec63e07aSXin Li
44*ec63e07aSXin Li GuetzliApi api(sandbox());
45*ec63e07aSXin Li sapi::v::LenVal output(0);
46*ec63e07aSXin Li
47*ec63e07aSXin Li sapi::v::Struct<ProcessingParams> processing_params;
48*ec63e07aSXin Li *processing_params.mutable_data() = {in_fd.GetRemoteFd(), params_.verbose,
49*ec63e07aSXin Li params_.quality, params_.memlimit_mb};
50*ec63e07aSXin Li
51*ec63e07aSXin Li auto result =
52*ec63e07aSXin Li image_type_ == ImageType::kJpeg
53*ec63e07aSXin Li ? api.ProcessJpeg(processing_params.PtrBefore(), output.PtrBefore())
54*ec63e07aSXin Li : api.ProcessRgb(processing_params.PtrBefore(), output.PtrBefore());
55*ec63e07aSXin Li
56*ec63e07aSXin Li if (!result.value_or(false)) {
57*ec63e07aSXin Li return absl::FailedPreconditionError(absl::StrCat(
58*ec63e07aSXin Li "Error processing ", (image_type_ == ImageType::kJpeg ? "jpeg" : "rgb"),
59*ec63e07aSXin Li " data"));
60*ec63e07aSXin Li }
61*ec63e07aSXin Li
62*ec63e07aSXin Li sapi::v::Fd out_fd(open(".", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR));
63*ec63e07aSXin Li if (out_fd.GetValue() < 0) {
64*ec63e07aSXin Li return absl::FailedPreconditionError("Error creating temp output file");
65*ec63e07aSXin Li }
66*ec63e07aSXin Li
67*ec63e07aSXin Li SAPI_RETURN_IF_ERROR(sandbox()->TransferToSandboxee(&out_fd));
68*ec63e07aSXin Li
69*ec63e07aSXin Li if (out_fd.GetRemoteFd() < 0) {
70*ec63e07aSXin Li return absl::FailedPreconditionError(
71*ec63e07aSXin Li "Error receiving remote FD: remote output fd is set to -1");
72*ec63e07aSXin Li }
73*ec63e07aSXin Li
74*ec63e07aSXin Li auto write_result = api.WriteDataToFd(out_fd.GetRemoteFd(), output.PtrNone());
75*ec63e07aSXin Li
76*ec63e07aSXin Li if (!write_result.value_or(false)) {
77*ec63e07aSXin Li return absl::FailedPreconditionError("Error writing file inside sandbox");
78*ec63e07aSXin Li }
79*ec63e07aSXin Li
80*ec63e07aSXin Li SAPI_RETURN_IF_ERROR(LinkOutFile(out_fd.GetValue()));
81*ec63e07aSXin Li
82*ec63e07aSXin Li return absl::OkStatus();
83*ec63e07aSXin Li }
84*ec63e07aSXin Li
LinkOutFile(int out_fd) const85*ec63e07aSXin Li absl::Status GuetzliTransaction::LinkOutFile(int out_fd) const {
86*ec63e07aSXin Li if (access(params_.out_file, F_OK) != -1) {
87*ec63e07aSXin Li if (remove(params_.out_file) < 0) {
88*ec63e07aSXin Li return absl::FailedPreconditionError(absl::StrCat(
89*ec63e07aSXin Li "Error deleting existing output file: ", params_.out_file));
90*ec63e07aSXin Li }
91*ec63e07aSXin Li }
92*ec63e07aSXin Li
93*ec63e07aSXin Li std::string path = absl::StrCat("/proc/self/fd/", out_fd);
94*ec63e07aSXin Li
95*ec63e07aSXin Li if (linkat(AT_FDCWD, path.c_str(), AT_FDCWD, params_.out_file,
96*ec63e07aSXin Li AT_SYMLINK_FOLLOW) < 0) {
97*ec63e07aSXin Li return absl::FailedPreconditionError(
98*ec63e07aSXin Li absl::StrCat("Error linking: ", params_.out_file));
99*ec63e07aSXin Li }
100*ec63e07aSXin Li
101*ec63e07aSXin Li return absl::OkStatus();
102*ec63e07aSXin Li }
103*ec63e07aSXin Li
GetImageTypeFromFd(int fd) const104*ec63e07aSXin Li absl::StatusOr<ImageType> GuetzliTransaction::GetImageTypeFromFd(int fd) const {
105*ec63e07aSXin Li static const unsigned char kPNGMagicBytes[] = {
106*ec63e07aSXin Li 0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n',
107*ec63e07aSXin Li };
108*ec63e07aSXin Li char read_buf[sizeof(kPNGMagicBytes)];
109*ec63e07aSXin Li
110*ec63e07aSXin Li if (read(fd, read_buf, sizeof(kPNGMagicBytes)) != sizeof(kPNGMagicBytes)) {
111*ec63e07aSXin Li return absl::FailedPreconditionError(
112*ec63e07aSXin Li "Error determining type of the input file");
113*ec63e07aSXin Li }
114*ec63e07aSXin Li
115*ec63e07aSXin Li if (lseek(fd, 0, SEEK_SET) != 0) {
116*ec63e07aSXin Li return absl::FailedPreconditionError(
117*ec63e07aSXin Li "Error returnig cursor to the beginning");
118*ec63e07aSXin Li }
119*ec63e07aSXin Li
120*ec63e07aSXin Li return memcmp(read_buf, kPNGMagicBytes, sizeof(kPNGMagicBytes)) == 0
121*ec63e07aSXin Li ? ImageType::kPng
122*ec63e07aSXin Li : ImageType::kJpeg;
123*ec63e07aSXin Li }
124*ec63e07aSXin Li
125*ec63e07aSXin Li } // namespace guetzli::sandbox
126