1 // Copyright 2022 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 "contrib/libzip/utils/utils_zip.h"
16
17 #include <fstream>
18 #include <iostream>
19 #include <string>
20
21 #include "absl/cleanup/cleanup.h"
22 #include "contrib/libzip/sandboxed.h"
23
24 constexpr uint64_t kFileMaxSize = 1024 * 1024 * 1024; // 1GB
25
26 #define ZIP_FL_ENC_GUESS 0
27 #define ZIP_FL_OVERWRITE 8192u
28
~LibZip()29 LibZip::~LibZip() {
30 if (IsOpen()) {
31 api_.zip_close(zip_.get()).IgnoreError();
32 }
33 api_.zip_source_free(&(*zipsource_)).IgnoreError();
34 }
35
IsOpen()36 bool LibZip::IsOpen() { return zip_ != nullptr; }
37
IsOpenLocal()38 bool LibZip::IsOpenLocal() { return rfd_.GetValue() >= 0; }
39
CheckOpen()40 absl::Status LibZip::CheckOpen() {
41 if (!IsOpen()) {
42 return absl::UnavailableError("Modification stage finished");
43 }
44
45 return absl::OkStatus();
46 }
47
CheckFinished()48 absl::Status LibZip::CheckFinished() {
49 if (IsOpen()) {
50 return absl::UnavailableError("Still in modification stage");
51 }
52
53 return absl::OkStatus();
54 }
55
OpenRemote()56 absl::Status LibZip::OpenRemote() {
57 if (!IsOpenLocal()) {
58 return absl::UnavailableError("Zip file is not open");
59 }
60
61 SAPI_RETURN_IF_ERROR(sandbox_->TransferToSandboxee(&rfd_));
62
63 SAPI_ASSIGN_OR_RETURN(void* zipsource, CreateSourceFromFd(rfd_));
64 zipsource_ = std::make_unique<sapi::v::RemotePtr>(zipsource);
65
66 sapi::v::NullPtr null_ptr;
67 absl::StatusOr<zip_t*> status_or_zip =
68 api_.zip_open_from_source(&(*zipsource_), flags_, &null_ptr);
69 if (!status_or_zip.ok() || *status_or_zip == nullptr) {
70 api_.zip_source_free(&(*zipsource_)).IgnoreError();
71 zipsource_ = nullptr;
72 return absl::UnavailableError("Unable to open remote");
73 }
74
75 SAPI_RETURN_IF_ERROR(api_.zip_source_keep(&(*zipsource_)));
76
77 zip_ = std::make_unique<sapi::v::RemotePtr>(*status_or_zip);
78
79 return absl::OkStatus();
80 }
81
Finish()82 absl::Status LibZip::Finish() {
83 SAPI_ASSIGN_OR_RETURN(int ret, api_.zip_close(zip_.get()));
84 if (ret < 0) {
85 return absl::UnavailableError("Unable to close remote");
86 }
87 zip_ = nullptr;
88
89 return absl::OkStatus();
90 }
91
Save(int fd)92 absl::Status LibZip::Save(int fd) {
93 SAPI_RETURN_IF_ERROR(CheckFinished());
94 sapi::v::Fd rfd(fd);
95 SAPI_RETURN_IF_ERROR(sandbox_->TransferToSandboxee(&rfd));
96 return Save(rfd);
97 }
98
Save()99 absl::Status LibZip::Save() {
100 SAPI_RETURN_IF_ERROR(CheckFinished());
101 return Save(rfd_);
102 }
103
Save(sapi::v::Fd & rfd)104 absl::Status LibZip::Save(sapi::v::Fd& rfd) {
105 SAPI_RETURN_IF_ERROR(CheckFinished());
106 SAPI_ASSIGN_OR_RETURN(
107 bool ret, api_.zip_source_to_fd(&(*zipsource_), rfd.GetRemoteFd()));
108
109 if (!ret) {
110 return absl::UnavailableError("Unable to store data");
111 }
112
113 return absl::OkStatus();
114 }
115
GetName(uint64_t index)116 absl::StatusOr<std::string> LibZip::GetName(uint64_t index) {
117 SAPI_RETURN_IF_ERROR(CheckOpen());
118 SAPI_ASSIGN_OR_RETURN(const char* name,
119 api_.zip_get_name(zip_.get(), index, ZIP_FL_ENC_GUESS));
120 if (name == nullptr) {
121 return absl::UnavailableError("Unable to find name under index");
122 }
123
124 return sandbox_->GetCString(sapi::v::RemotePtr(const_cast<char*>(name)));
125 }
126
GetNumberEntries()127 absl::StatusOr<uint64_t> LibZip::GetNumberEntries() {
128 SAPI_RETURN_IF_ERROR(CheckOpen());
129 SAPI_ASSIGN_OR_RETURN(int64_t num, api_.zip_get_num_entries(zip_.get(), 0));
130 if (num < 0) {
131 /* Imposible as zip != nullptr */
132 return absl::UnavailableError("Internal error");
133 }
134 return num;
135 }
136
ReadFile(sapi::v::RemotePtr & rzipfile,uint32_t size)137 absl::StatusOr<std::vector<uint8_t>> LibZip::ReadFile(
138 sapi::v::RemotePtr& rzipfile, uint32_t size) {
139 SAPI_RETURN_IF_ERROR(CheckOpen());
140 if (size > kFileMaxSize) {
141 return absl::UnavailableError("File is to large");
142 }
143
144 std::vector<uint8_t> buf(size);
145 sapi::v::Array<uint8_t> arr(buf.data(), size);
146
147 SAPI_ASSIGN_OR_RETURN(uint64_t ret,
148 api_.zip_fread(&rzipfile, arr.PtrAfter(), size));
149 if (ret != size) {
150 return absl::UnavailableError("Unable to read file");
151 }
152
153 return buf;
154 }
155
ReadFile(const std::string & filename)156 absl::StatusOr<std::vector<uint8_t>> LibZip::ReadFile(
157 const std::string& filename) {
158 SAPI_RETURN_IF_ERROR(CheckOpen());
159 sapi::v::Struct<zip_stat_t> zipstat;
160 sapi::v::ConstCStr cfilename(filename.c_str());
161
162 SAPI_ASSIGN_OR_RETURN(
163 int err,
164 api_.zip_stat(zip_.get(), cfilename.PtrBefore(), 0, zipstat.PtrAfter()));
165 if (err < 0) {
166 return absl::UnavailableError("Unable to get file stat");
167 }
168
169 SAPI_ASSIGN_OR_RETURN(zip_file_t * zipfile,
170 api_.zip_fopen(zip_.get(), cfilename.PtrBefore(), 0));
171 if (zipfile == nullptr) {
172 return absl::UnavailableError("Unable to open file in archaive");
173 }
174
175 sapi::v::RemotePtr rzipfile(zipfile);
176 absl::Cleanup rzipfile_cleanup = [this, &rzipfile] {
177 api_.zip_fclose(&rzipfile).IgnoreError();
178 };
179 return ReadFile(rzipfile, zipstat.mutable_data()->size);
180 }
181
ReadFile(uint64_t index)182 absl::StatusOr<std::vector<uint8_t>> LibZip::ReadFile(uint64_t index) {
183 SAPI_RETURN_IF_ERROR(CheckOpen());
184 sapi::v::Struct<zip_stat_t> zipstat;
185
186 SAPI_ASSIGN_OR_RETURN(
187 int err, api_.zip_stat_index(zip_.get(), index, 0, zipstat.PtrAfter()));
188 if (err < 0) {
189 return absl::UnavailableError("Unable to get file stat");
190 }
191
192 SAPI_ASSIGN_OR_RETURN(zip_file_t * zipfile,
193 api_.zip_fopen_index(zip_.get(), index, 0));
194 if (zipfile == nullptr) {
195 return absl::UnavailableError("Unable to open file in archaive");
196 }
197 sapi::v::RemotePtr rzipfile(zipfile);
198 absl::Cleanup rzipfile_cleanup = [this, &rzipfile] {
199 api_.zip_fclose(&rzipfile).IgnoreError();
200 };
201
202 return ReadFile(rzipfile, zipstat.mutable_data()->size);
203 }
204
AddFile(const std::string & filename,sapi::v::RemotePtr & rzipsource)205 absl::StatusOr<uint64_t> LibZip::AddFile(const std::string& filename,
206 sapi::v::RemotePtr& rzipsource) {
207 SAPI_RETURN_IF_ERROR(CheckOpen());
208 sapi::v::ConstCStr cfilename(filename.c_str());
209
210 SAPI_ASSIGN_OR_RETURN(int64_t index,
211 api_.zip_file_add(zip_.get(), cfilename.PtrBefore(),
212 &rzipsource, ZIP_FL_OVERWRITE));
213 if (index < 0) {
214 SAPI_ASSIGN_OR_RETURN(std::string errs, GetError());
215 api_.zip_source_free(&rzipsource).IgnoreError();
216 return absl::UnavailableError("Unable to add file");
217 }
218
219 return index;
220 }
221
CreateSourceFromFd(sapi::v::Fd & rfd)222 absl::StatusOr<void*> LibZip::CreateSourceFromFd(sapi::v::Fd& rfd) {
223 sapi::v::NullPtr null_ptr;
224
225 SAPI_ASSIGN_OR_RETURN(void* zipsource, api_.zip_read_fd_to_source(
226 rfd.GetRemoteFd(), &null_ptr));
227 if (zipsource == nullptr) {
228 return absl::UnavailableError("Unable to create buffer");
229 }
230
231 return zipsource;
232 }
233
GetSource(std::vector<uint8_t> & buf)234 absl::StatusOr<void*> LibZip::GetSource(std::vector<uint8_t>& buf) {
235 sapi::v::Array<uint8_t> arr(buf.data(), buf.size());
236
237 SAPI_ASSIGN_OR_RETURN(
238 zip_source_t * zipsource,
239 api_.zip_source_buffer(zip_.get(), arr.PtrBefore(), arr.GetSize(),
240 1 /* autofree */));
241 if (zipsource == nullptr) {
242 return absl::UnavailableError("Unable to create buffer");
243 }
244 arr.SetRemote(nullptr); // We don't want to free automaticlt buffer
245 // leave memory menagment to the zip process
246 return zipsource;
247 }
248
GetSource(int fd,const std::string & mode)249 absl::StatusOr<void*> LibZip::GetSource(int fd, const std::string& mode) {
250 sapi::v::Fd rfd(fd);
251 SAPI_RETURN_IF_ERROR(sandbox_->TransferToSandboxee(&rfd));
252
253 sapi::v::ConstCStr cmode(mode.c_str());
254 SAPI_ASSIGN_OR_RETURN(void* zipsource,
255 api_.zip_source_filefd(zip_.get(), rfd.GetRemoteFd(),
256 cmode.PtrBefore(), 0, 0));
257 if (zipsource == nullptr) {
258 return absl::UnavailableError("Unable to create buffer");
259 }
260 rfd.OwnRemoteFd(false);
261
262 return zipsource;
263 }
264
AddFile(const std::string & filename,std::vector<uint8_t> & buf)265 absl::StatusOr<uint64_t> LibZip::AddFile(const std::string& filename,
266 std::vector<uint8_t>& buf) {
267 SAPI_RETURN_IF_ERROR(CheckOpen());
268 SAPI_ASSIGN_OR_RETURN(void* zipsource, GetSource(buf));
269 sapi::v::RemotePtr rzipsource(zipsource);
270 return AddFile(filename, rzipsource);
271 }
272
AddFile(const std::string & filename,int fd)273 absl::StatusOr<uint64_t> LibZip::AddFile(const std::string& filename, int fd) {
274 SAPI_RETURN_IF_ERROR(CheckOpen());
275 SAPI_ASSIGN_OR_RETURN(void* zipsource, GetSource(fd, "rb"));
276 sapi::v::RemotePtr rzipsource(zipsource);
277 return AddFile(filename, rzipsource);
278 }
279
ReplaceFile(uint64_t index,sapi::v::RemotePtr & rzipsource)280 absl::Status LibZip::ReplaceFile(uint64_t index,
281 sapi::v::RemotePtr& rzipsource) {
282 SAPI_RETURN_IF_ERROR(CheckOpen());
283 SAPI_ASSIGN_OR_RETURN(
284 int64_t ret, api_.zip_file_replace(zip_.get(), index, &rzipsource, 0));
285 if (ret < 0) {
286 api_.zip_source_free(&rzipsource).IgnoreError();
287 return absl::UnavailableError("Unable to replace file");
288 }
289
290 return absl::OkStatus();
291 }
292
ReplaceFile(uint64_t index,int fd)293 absl::Status LibZip::ReplaceFile(uint64_t index, int fd) {
294 SAPI_RETURN_IF_ERROR(CheckOpen());
295 SAPI_ASSIGN_OR_RETURN(void* zipsource, GetSource(fd, "rb"));
296 sapi::v::RemotePtr rzipsource(zipsource);
297 return ReplaceFile(index, rzipsource);
298 }
299
ReplaceFile(uint64_t index,std::vector<uint8_t> & buf)300 absl::Status LibZip::ReplaceFile(uint64_t index, std::vector<uint8_t>& buf) {
301 SAPI_RETURN_IF_ERROR(CheckOpen());
302 SAPI_ASSIGN_OR_RETURN(void* zipsource, GetSource(buf));
303 sapi::v::RemotePtr rzipsource(zipsource);
304 return ReplaceFile(index, rzipsource);
305 }
306
DeleteFile(uint64_t index)307 absl::Status LibZip::DeleteFile(uint64_t index) {
308 SAPI_RETURN_IF_ERROR(CheckOpen());
309 SAPI_ASSIGN_OR_RETURN(int ret, api_.zip_delete(zip_.get(), index));
310 if (ret < 0) {
311 return absl::UnavailableError("Unable to delete file");
312 }
313
314 return absl::OkStatus();
315 }
316
GetError()317 absl::StatusOr<std::string> LibZip::GetError() {
318 SAPI_RETURN_IF_ERROR(CheckOpen());
319 SAPI_ASSIGN_OR_RETURN(const char* err, api_.zip_strerror(zip_.get()));
320 if (err == nullptr) {
321 return absl::UnavailableError("No error");
322 }
323 return sandbox_->GetCString(sapi::v::RemotePtr(const_cast<char*>(err)));
324 }
325