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 <fcntl.h>
16 #include <unistd.h>
17
18 #include <cstdlib>
19 #include <fstream>
20 #include <iostream>
21 #include <string>
22 #include <vector>
23
24 #include "absl/flags/flag.h"
25 #include "absl/flags/parse.h"
26 #include "absl/log/globals.h"
27 #include "absl/log/initialize.h"
28 #include "contrib/libzip/sandboxed.h"
29 #include "contrib/libzip/utils/utils_zip.h"
30
31 ABSL_FLAG(bool, list, false, "list files");
32 ABSL_FLAG(std::string, unzip, "", "unzip");
33 ABSL_FLAG(std::string, add_file, "", "add file");
34 ABSL_FLAG(std::string, delete, "", "delete");
35
ListFiles(LibZip & zip)36 absl::Status ListFiles(LibZip& zip) {
37 SAPI_ASSIGN_OR_RETURN(int64_t num, zip.GetNumberEntries());
38 for (uint64_t i = 0; i < num; i++) {
39 SAPI_ASSIGN_OR_RETURN(std::string name, zip.GetName(i));
40 std::cout << name << "\n";
41 }
42
43 return absl::OkStatus();
44 }
45
UnzipToStdout(LibZip & zip,const std::string & filename)46 absl::Status UnzipToStdout(LibZip& zip, const std::string& filename) {
47 SAPI_ASSIGN_OR_RETURN(std::vector<uint8_t> buf, zip.ReadFile(filename));
48
49 std::cout << buf.data();
50
51 return absl::OkStatus();
52 }
53
AddFile(LibZip & zip,const std::string & filename)54 absl::Status AddFile(LibZip& zip, const std::string& filename) {
55 int fd = open(filename.c_str(), O_RDONLY);
56 if (fd < 0) {
57 return absl::UnavailableError(
58 absl::StrCat("Unable to open file ", filename));
59 }
60
61 // The fd will be consumed
62 SAPI_ASSIGN_OR_RETURN(uint64_t index, zip.AddFile(filename, fd));
63
64 return absl::OkStatus();
65 }
66
DeleteFile(LibZip & zip,const std::string & filename)67 absl::Status DeleteFile(LibZip& zip, const std::string& filename) {
68 int64_t index = -1;
69
70 SAPI_ASSIGN_OR_RETURN(int64_t num, zip.GetNumberEntries());
71 for (uint64_t i = 0; i < num; i++) {
72 SAPI_ASSIGN_OR_RETURN(std::string name, zip.GetName(i));
73 if (name == filename) {
74 index = i;
75 break;
76 }
77 }
78 if (index == -1) {
79 return absl::UnavailableError(
80 absl::StrCat("Unable to remove file ", filename));
81 }
82
83 return zip.DeleteFile(index);
84 }
85
main(int argc,char * argv[])86 int main(int argc, char* argv[]) {
87 std::string prog_name(argv[0]);
88 absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
89 std::vector<char*> args = absl::ParseCommandLine(argc, argv);
90 absl::InitializeLog();
91
92 if (args.size() < 2 || args.size() > 3) {
93 std::cerr << "Usage:\n " << prog_name << " ZIPFILE [OUTFILE]\n";
94 return EXIT_FAILURE;
95 }
96
97 std::string filename(args[1]);
98 ZipSapiSandbox sandbox;
99 if (!sandbox.Init().ok()) {
100 std::cerr << "Unable to start sandbox\n";
101 return EXIT_FAILURE;
102 }
103
104 LibZip zip(&sandbox, filename, 0);
105
106 if (!zip.IsOpen()) {
107 std::cerr << "Unable to open file " << filename << "\n";
108 return EXIT_FAILURE;
109 }
110
111 int outfd = -1;
112 if (args.size() == 3) {
113 outfd = open(args[2], O_WRONLY | O_CREAT);
114 if (outfd < 0) {
115 std::cerr << "Unable to open file " << args[2] << "\n";
116 return EXIT_FAILURE;
117 }
118 }
119
120 bool needs_saving = false;
121 absl::Status status;
122 if (absl::GetFlag(FLAGS_list)) {
123 status = ListFiles(zip);
124 }
125 if (!absl::GetFlag(FLAGS_unzip).empty()) {
126 status = UnzipToStdout(zip, absl::GetFlag(FLAGS_unzip));
127 }
128 if (!absl::GetFlag(FLAGS_add_file).empty()) {
129 status = AddFile(zip, absl::GetFlag(FLAGS_add_file));
130 needs_saving = true;
131 }
132 if (!absl::GetFlag(FLAGS_delete).empty()) {
133 status = DeleteFile(zip, absl::GetFlag(FLAGS_delete));
134 needs_saving = true;
135 }
136
137 if (!status.ok()) {
138 std::cerr << status << "\n";
139 return EXIT_FAILURE;
140 }
141
142 status = zip.Finish();
143 if (!status.ok()) {
144 std::cerr << status << "\n";
145 return EXIT_FAILURE;
146 }
147
148 if (needs_saving) {
149 if (outfd == -1) {
150 status = zip.Save();
151 } else {
152 status = zip.Save(outfd);
153 }
154 if (!status.ok()) {
155 std::cerr << status << "\n";
156 return EXIT_FAILURE;
157 }
158 }
159
160 return EXIT_SUCCESS;
161 }
162