diff options
Diffstat (limited to 'lib/microtar/mtar.c')
-rw-r--r-- | lib/microtar/mtar.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/lib/microtar/mtar.c b/lib/microtar/mtar.c new file mode 100644 index 0000000000..4a9a27b782 --- /dev/null +++ b/lib/microtar/mtar.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2021 Aidan MacDonald | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
5 | * of this software and associated documentation files (the "Software"), to | ||
6 | * deal in the Software without restriction, including without limitation the | ||
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
8 | * sell copies of the Software, and to permit persons to whom the Software is | ||
9 | * furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
20 | * IN THE SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #include "microtar-stdio.h" | ||
24 | #include <stdio.h> | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <stdarg.h> | ||
28 | #include <sys/stat.h> | ||
29 | #include <fcntl.h> | ||
30 | #include <errno.h> | ||
31 | #include <unistd.h> | ||
32 | |||
33 | /* exit codes */ | ||
34 | #define E_TAR 1 | ||
35 | #define E_FS 2 | ||
36 | #define E_OTHER 4 | ||
37 | #define E_ARGS 8 | ||
38 | |||
39 | enum { | ||
40 | OP_LIST, | ||
41 | OP_CREATE, | ||
42 | OP_EXTRACT, | ||
43 | }; | ||
44 | |||
45 | void die(int err, const char* msg, ...) | ||
46 | { | ||
47 | fprintf(stderr, "mtar: "); | ||
48 | |||
49 | va_list ap; | ||
50 | va_start(ap, msg); | ||
51 | vfprintf(stderr, msg, ap); | ||
52 | va_end(ap); | ||
53 | |||
54 | fprintf(stderr, "\n"); | ||
55 | exit(err); | ||
56 | } | ||
57 | |||
58 | int list_foreach_cb(mtar_t* tar, const mtar_header_t* h, void* arg) | ||
59 | { | ||
60 | (void)tar; | ||
61 | (void)arg; | ||
62 | printf("%s\n", h->name); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | void list_files(mtar_t* tar) | ||
67 | { | ||
68 | int err = mtar_foreach(tar, list_foreach_cb, NULL); | ||
69 | if(err) | ||
70 | die(E_TAR, "listing failed: %s", mtar_strerror(err)); | ||
71 | } | ||
72 | |||
73 | struct extract_args { | ||
74 | char** names; | ||
75 | int count; | ||
76 | }; | ||
77 | |||
78 | int extract_foreach_cb(mtar_t* tar, const mtar_header_t* h, void* arg) | ||
79 | { | ||
80 | struct extract_args* args = arg; | ||
81 | (void)args; /* TODO */ | ||
82 | |||
83 | if(h->type == MTAR_TDIR) { | ||
84 | if(mkdir(h->name, h->mode) != 0) | ||
85 | die(E_FS, "cannot create directory \"%s\"", h->name); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | if(h->type != MTAR_TREG) { | ||
90 | fprintf(stderr, "warning: not extracting unsupported type \"%s\"", h->name); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | int fd = open(h->name, O_CREAT|O_WRONLY|O_TRUNC, h->mode); | ||
95 | if(fd < 0) | ||
96 | die(E_FS, "extracting \"%s\" failed: %s", h->name, strerror(errno)); | ||
97 | |||
98 | char iobuf[1024]; | ||
99 | while(!mtar_eof_data(tar)) { | ||
100 | int rcount = mtar_read_data(tar, iobuf, sizeof(iobuf)); | ||
101 | if(rcount < 0) | ||
102 | die(E_TAR, "extracting \"%s\" failed: %s", h->name, mtar_strerror(rcount)); | ||
103 | |||
104 | int wcount = write(fd, iobuf, rcount); | ||
105 | if(wcount != rcount) | ||
106 | die(E_FS, "extracting \"%s\" failed: %s", h->name, strerror(errno)); | ||
107 | } | ||
108 | |||
109 | close(fd); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | void extract_files(mtar_t* tar, char** files, int num_files) | ||
114 | { | ||
115 | struct extract_args args; | ||
116 | args.names = files; | ||
117 | args.count = num_files; | ||
118 | |||
119 | int err = mtar_foreach(tar, extract_foreach_cb, &args); | ||
120 | if(err) | ||
121 | die(E_TAR, "extraction failed: %s", mtar_strerror(err)); | ||
122 | } | ||
123 | |||
124 | void add_files(mtar_t* tar, char** files, int num_files) | ||
125 | { | ||
126 | for(int i = 0; i < num_files; ++i) { | ||
127 | int fd = open(files[i], O_RDONLY); | ||
128 | if(fd < 0) | ||
129 | die(E_FS, "adding \"%s\" failed: %s", files[i], strerror(errno)); | ||
130 | |||
131 | off_t off = lseek(fd, 0, SEEK_END); | ||
132 | if(off < 0) | ||
133 | die(E_FS, "adding \"%s\" failed: %s", files[i], strerror(errno)); | ||
134 | |||
135 | unsigned filesize = off; | ||
136 | lseek(fd, 0, SEEK_SET); | ||
137 | |||
138 | int err = mtar_write_file_header(tar, files[i], filesize); | ||
139 | if(err) | ||
140 | die(E_TAR, "adding \"%s\" failed: %s", files[i], mtar_strerror(err)); | ||
141 | |||
142 | char iobuf[1024]; | ||
143 | while(1) { | ||
144 | int rcount = read(fd, iobuf, sizeof(iobuf)); | ||
145 | if(rcount < 0) | ||
146 | die(E_FS, "adding \"%s\" failed: %s", files[i], strerror(errno)); | ||
147 | if(rcount == 0) | ||
148 | break; | ||
149 | |||
150 | int wcount = mtar_write_data(tar, iobuf, rcount); | ||
151 | if(wcount < 0) | ||
152 | die(E_TAR, "adding \"%s\" failed: %s", files[i], mtar_strerror(wcount)); | ||
153 | if(wcount != rcount) | ||
154 | die(E_TAR, "adding \"%s\" failed: write too short %d/%d", files[i], wcount, rcount); | ||
155 | } | ||
156 | |||
157 | close(fd); | ||
158 | |||
159 | err = mtar_end_data(tar); | ||
160 | if(err) | ||
161 | die(E_TAR, "adding \"%s\" failed: %s", files[i], mtar_strerror(err)); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | int main(int argc, char* argv[]) | ||
166 | { | ||
167 | ++argv, --argc; | ||
168 | if(argc == 0) | ||
169 | die(E_ARGS, "no input files"); | ||
170 | |||
171 | if(!strcmp(*argv, "--help")) { | ||
172 | printf( | ||
173 | "usage:\n" | ||
174 | " mtar list tar-file\n" | ||
175 | " List the members of the given tar archive, one filename per line.\n" | ||
176 | "\n" | ||
177 | " mtar create tar-file members...\n" | ||
178 | " mtar add tar-file members...\n" | ||
179 | " Create a new tar archive from the files listed on the command line.\n" | ||
180 | " WARNING: Any existing file at tar-file will be overwritten!\n" | ||
181 | "\n" | ||
182 | " mtar extract tar-file [members...]\n" | ||
183 | " Extract the contents of the tar archive to the current directory.\n" | ||
184 | " If filenames are given, only the named members will be extracted.\n" | ||
185 | "\n"); | ||
186 | exit(E_ARGS); | ||
187 | } | ||
188 | |||
189 | int op; | ||
190 | if(!strcmp(*argv, "list")) | ||
191 | op = OP_LIST; | ||
192 | else if(!strcmp(*argv, "create")) | ||
193 | op = OP_CREATE; | ||
194 | else if(!strcmp(*argv, "extract")) | ||
195 | op = OP_EXTRACT; | ||
196 | else | ||
197 | die(E_ARGS, "invalid operation \"%s\"", *argv); | ||
198 | ++argv, --argc; | ||
199 | |||
200 | if(argc == 0) | ||
201 | die(E_ARGS, "missing archive name"); | ||
202 | const char* archive_name = *argv; | ||
203 | ++argv, --argc; | ||
204 | |||
205 | if(op == OP_LIST && argc != 0) | ||
206 | die(E_ARGS, "excess arguments on command line"); | ||
207 | |||
208 | const char* mode = "rb"; | ||
209 | if(op == OP_CREATE) | ||
210 | mode = "wb"; | ||
211 | |||
212 | mtar_t tar; | ||
213 | int err = mtar_open(&tar, archive_name, mode); | ||
214 | if(err) | ||
215 | die(E_TAR, "can't open archive: %s", mtar_strerror(err)); | ||
216 | |||
217 | switch(op) { | ||
218 | case OP_LIST: | ||
219 | list_files(&tar); | ||
220 | break; | ||
221 | |||
222 | case OP_EXTRACT: | ||
223 | extract_files(&tar, argv, argc); | ||
224 | break; | ||
225 | |||
226 | case OP_CREATE: | ||
227 | add_files(&tar, argv, argc); | ||
228 | err = mtar_finalize(&tar); | ||
229 | if(err) | ||
230 | die(E_TAR, "failed to finalize archive: %s", mtar_strerror(err)); | ||
231 | break; | ||
232 | |||
233 | default: | ||
234 | die(E_OTHER, "not implemented"); | ||
235 | break; | ||
236 | } | ||
237 | |||
238 | err = mtar_close(&tar); | ||
239 | if(err) | ||
240 | die(E_TAR, "failed to close archive: %s", mtar_strerror(err)); | ||
241 | |||
242 | return 0; | ||
243 | } | ||