|
diff --git a/vendor/microtar-0.1.0/README.md b/vendor/microtar-0.1.0/README.md
|
|
|
1 |
# microtar |
|
|
2 |
A lightweight tar library written in ANSI C |
|
|
3 |
|
|
|
4 |
|
|
|
5 |
## Basic Usage |
|
|
6 |
The library consists of `microtar.c` and `microtar.h`. These two files can be |
|
|
7 |
dropped into an existing project and compiled along with it. |
|
|
8 |
|
|
|
9 |
|
|
|
10 |
#### Reading |
|
|
11 |
```c |
|
|
12 |
mtar_t tar; |
|
|
13 |
mtar_header_t h; |
|
|
14 |
char *p; |
|
|
15 |
|
|
|
16 |
/* Open archive for reading */ |
|
|
17 |
mtar_open(&tar, "test.tar", "r"); |
|
|
18 |
|
|
|
19 |
/* Print all file names and sizes */ |
|
|
20 |
while ( (mtar_read_header(&tar, &h)) != MTAR_ENULLRECORD ) { |
|
|
21 |
printf("%s (%d bytes)\n", h.name, h.size); |
|
|
22 |
mtar_next(&tar); |
|
|
23 |
} |
|
|
24 |
|
|
|
25 |
/* Load and print contents of file "test.txt" */ |
|
|
26 |
mtar_find(&tar, "test.txt", &h); |
|
|
27 |
p = calloc(1, h.size + 1); |
|
|
28 |
mtar_read_data(&tar, p, h.size); |
|
|
29 |
printf("%s", p); |
|
|
30 |
free(p); |
|
|
31 |
|
|
|
32 |
/* Close archive */ |
|
|
33 |
mtar_close(&tar); |
|
|
34 |
``` |
|
|
35 |
|
|
|
36 |
#### Writing |
|
|
37 |
```c |
|
|
38 |
mtar_t tar; |
|
|
39 |
const char *str1 = "Hello world"; |
|
|
40 |
const char *str2 = "Goodbye world"; |
|
|
41 |
|
|
|
42 |
/* Open archive for writing */ |
|
|
43 |
mtar_open(&tar, "test.tar", "w"); |
|
|
44 |
|
|
|
45 |
/* Write strings to files `test1.txt` and `test2.txt` */ |
|
|
46 |
mtar_write_file_header(&tar, "test1.txt", strlen(str1)); |
|
|
47 |
mtar_write_data(&tar, str1, strlen(str1)); |
|
|
48 |
mtar_write_file_header(&tar, "test2.txt", strlen(str2)); |
|
|
49 |
mtar_write_data(&tar, str2, strlen(str2)); |
|
|
50 |
|
|
|
51 |
/* Finalize -- this needs to be the last thing done before closing */ |
|
|
52 |
mtar_finalize(&tar); |
|
|
53 |
|
|
|
54 |
/* Close archive */ |
|
|
55 |
mtar_close(&tar); |
|
|
56 |
``` |
|
|
57 |
|
|
|
58 |
|
|
|
59 |
## Error handling |
|
|
60 |
All functions which return an `int` will return `MTAR_ESUCCESS` if the operation |
|
|
61 |
is successful. If an error occurs an error value less-than-zero will be |
|
|
62 |
returned; this value can be passed to the function `mtar_strerror()` to get its |
|
|
63 |
corresponding error string. |
|
|
64 |
|
|
|
65 |
|
|
|
66 |
## Wrapping a stream |
|
|
67 |
If you want to read or write from something other than a file, the `mtar_t` |
|
|
68 |
struct can be manually initialized with your own callback functions and a |
|
|
69 |
`stream` pointer. |
|
|
70 |
|
|
|
71 |
All callback functions are passed a pointer to the `mtar_t` struct as their |
|
|
72 |
first argument. They should return `MTAR_ESUCCESS` if the operation succeeds |
|
|
73 |
without an error, or an integer below zero if an error occurs. |
|
|
74 |
|
|
|
75 |
After the `stream` field has been set, all required callbacks have been set and |
|
|
76 |
all unused fields have been zeroset the `mtar_t` struct can be safely used with |
|
|
77 |
the microtar functions. `mtar_open` *should not* be called if the `mtar_t` |
|
|
78 |
struct was initialized manually. |
|
|
79 |
|
|
|
80 |
#### Reading |
|
|
81 |
The following callbacks should be set for reading an archive from a stream: |
|
|
82 |
|
|
|
83 |
Name | Arguments | Description |
|
|
84 |
--------|------------------------------------------|--------------------------- |
|
|
85 |
`read` | `mtar_t *tar, void *data, unsigned size` | Read data from the stream |
|
|
86 |
`seek` | `mtar_t *tar, unsigned pos` | Set the position indicator |
|
|
87 |
`close` | `mtar_t *tar` | Close the stream |
|
|
88 |
|
|
|
89 |
#### Writing |
|
|
90 |
The following callbacks should be set for writing an archive to a stream: |
|
|
91 |
|
|
|
92 |
Name | Arguments | Description |
|
|
93 |
--------|------------------------------------------------|--------------------- |
|
|
94 |
`write` | `mtar_t *tar, const void *data, unsigned size` | Write data to the stream |
|
|
95 |
|
|
|
96 |
|
|
|
97 |
## License |
|
|
98 |
This library is free software; you can redistribute it and/or modify it under |
|
|
99 |
the terms of the MIT license. See [LICENSE](LICENSE) for details. |
|
diff --git a/vendor/microtar-0.1.0/src/microtar.c b/vendor/microtar-0.1.0/src/microtar.c
|
|
|
1 |
/* |
|
|
2 |
* Copyright (c) 2017 rxi |
|
|
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 <stdio.h> |
|
|
24 |
#include <stdlib.h> |
|
|
25 |
#include <stddef.h> |
|
|
26 |
#include <string.h> |
|
|
27 |
|
|
|
28 |
#include "microtar.h" |
|
|
29 |
|
|
|
30 |
typedef struct { |
|
|
31 |
char name[100]; |
|
|
32 |
char mode[8]; |
|
|
33 |
char owner[8]; |
|
|
34 |
char group[8]; |
|
|
35 |
char size[12]; |
|
|
36 |
char mtime[12]; |
|
|
37 |
char checksum[8]; |
|
|
38 |
char type; |
|
|
39 |
char linkname[100]; |
|
|
40 |
char _padding[255]; |
|
|
41 |
} mtar_raw_header_t; |
|
|
42 |
|
|
|
43 |
|
|
|
44 |
static unsigned round_up(unsigned n, unsigned incr) { |
|
|
45 |
return n + (incr - n % incr) % incr; |
|
|
46 |
} |
|
|
47 |
|
|
|
48 |
|
|
|
49 |
static unsigned checksum(const mtar_raw_header_t* rh) { |
|
|
50 |
unsigned i; |
|
|
51 |
unsigned char *p = (unsigned char*) rh; |
|
|
52 |
unsigned res = 256; |
|
|
53 |
for (i = 0; i < offsetof(mtar_raw_header_t, checksum); i++) { |
|
|
54 |
res += p[i]; |
|
|
55 |
} |
|
|
56 |
for (i = offsetof(mtar_raw_header_t, type); i < sizeof(*rh); i++) { |
|
|
57 |
res += p[i]; |
|
|
58 |
} |
|
|
59 |
return res; |
|
|
60 |
} |
|
|
61 |
|
|
|
62 |
|
|
|
63 |
static int tread(mtar_t *tar, void *data, unsigned size) { |
|
|
64 |
int err = tar->read(tar, data, size); |
|
|
65 |
tar->pos += size; |
|
|
66 |
return err; |
|
|
67 |
} |
|
|
68 |
|
|
|
69 |
|
|
|
70 |
static int twrite(mtar_t *tar, const void *data, unsigned size) { |
|
|
71 |
int err = tar->write(tar, data, size); |
|
|
72 |
tar->pos += size; |
|
|
73 |
return err; |
|
|
74 |
} |
|
|
75 |
|
|
|
76 |
|
|
|
77 |
static int write_null_bytes(mtar_t *tar, int n) { |
|
|
78 |
int i, err; |
|
|
79 |
char nul = '\0'; |
|
|
80 |
for (i = 0; i < n; i++) { |
|
|
81 |
err = twrite(tar, &nul, 1); |
|
|
82 |
if (err) { |
|
|
83 |
return err; |
|
|
84 |
} |
|
|
85 |
} |
|
|
86 |
return MTAR_ESUCCESS; |
|
|
87 |
} |
|
|
88 |
|
|
|
89 |
|
|
|
90 |
static int raw_to_header(mtar_header_t *h, const mtar_raw_header_t *rh) { |
|
|
91 |
unsigned chksum1, chksum2; |
|
|
92 |
|
|
|
93 |
/* If the checksum starts with a null byte we assume the record is NULL */ |
|
|
94 |
if (*rh->checksum == '\0') { |
|
|
95 |
return MTAR_ENULLRECORD; |
|
|
96 |
} |
|
|
97 |
|
|
|
98 |
/* Build and compare checksum */ |
|
|
99 |
chksum1 = checksum(rh); |
|
|
100 |
sscanf(rh->checksum, "%o", &chksum2); |
|
|
101 |
if (chksum1 != chksum2) { |
|
|
102 |
return MTAR_EBADCHKSUM; |
|
|
103 |
} |
|
|
104 |
|
|
|
105 |
/* Load raw header into header */ |
|
|
106 |
sscanf(rh->mode, "%o", &h->mode); |
|
|
107 |
sscanf(rh->owner, "%o", &h->owner); |
|
|
108 |
sscanf(rh->size, "%o", &h->size); |
|
|
109 |
sscanf(rh->mtime, "%o", &h->mtime); |
|
|
110 |
h->type = rh->type; |
|
|
111 |
strcpy(h->name, rh->name); |
|
|
112 |
strcpy(h->linkname, rh->linkname); |
|
|
113 |
|
|
|
114 |
return MTAR_ESUCCESS; |
|
|
115 |
} |
|
|
116 |
|
|
|
117 |
|
|
|
118 |
static int header_to_raw(mtar_raw_header_t *rh, const mtar_header_t *h) { |
|
|
119 |
unsigned chksum; |
|
|
120 |
|
|
|
121 |
/* Load header into raw header */ |
|
|
122 |
memset(rh, 0, sizeof(*rh)); |
|
|
123 |
sprintf(rh->mode, "%o", h->mode); |
|
|
124 |
sprintf(rh->owner, "%o", h->owner); |
|
|
125 |
sprintf(rh->size, "%o", h->size); |
|
|
126 |
sprintf(rh->mtime, "%o", h->mtime); |
|
|
127 |
rh->type = h->type ? h->type : MTAR_TREG; |
|
|
128 |
strcpy(rh->name, h->name); |
|
|
129 |
strcpy(rh->linkname, h->linkname); |
|
|
130 |
|
|
|
131 |
/* Calculate and write checksum */ |
|
|
132 |
chksum = checksum(rh); |
|
|
133 |
sprintf(rh->checksum, "%06o", chksum); |
|
|
134 |
rh->checksum[7] = ' '; |
|
|
135 |
|
|
|
136 |
return MTAR_ESUCCESS; |
|
|
137 |
} |
|
|
138 |
|
|
|
139 |
|
|
|
140 |
const char* mtar_strerror(int err) { |
|
|
141 |
switch (err) { |
|
|
142 |
case MTAR_ESUCCESS : return "success"; |
|
|
143 |
case MTAR_EFAILURE : return "failure"; |
|
|
144 |
case MTAR_EOPENFAIL : return "could not open"; |
|
|
145 |
case MTAR_EREADFAIL : return "could not read"; |
|
|
146 |
case MTAR_EWRITEFAIL : return "could not write"; |
|
|
147 |
case MTAR_ESEEKFAIL : return "could not seek"; |
|
|
148 |
case MTAR_EBADCHKSUM : return "bad checksum"; |
|
|
149 |
case MTAR_ENULLRECORD : return "null record"; |
|
|
150 |
case MTAR_ENOTFOUND : return "file not found"; |
|
|
151 |
} |
|
|
152 |
return "unknown error"; |
|
|
153 |
} |
|
|
154 |
|
|
|
155 |
|
|
|
156 |
static int file_write(mtar_t *tar, const void *data, unsigned size) { |
|
|
157 |
unsigned res = fwrite(data, 1, size, tar->stream); |
|
|
158 |
return (res == size) ? MTAR_ESUCCESS : MTAR_EWRITEFAIL; |
|
|
159 |
} |
|
|
160 |
|
|
|
161 |
static int file_read(mtar_t *tar, void *data, unsigned size) { |
|
|
162 |
unsigned res = fread(data, 1, size, tar->stream); |
|
|
163 |
return (res == size) ? MTAR_ESUCCESS : MTAR_EREADFAIL; |
|
|
164 |
} |
|
|
165 |
|
|
|
166 |
static int file_seek(mtar_t *tar, unsigned offset) { |
|
|
167 |
int res = fseek(tar->stream, offset, SEEK_SET); |
|
|
168 |
return (res == 0) ? MTAR_ESUCCESS : MTAR_ESEEKFAIL; |
|
|
169 |
} |
|
|
170 |
|
|
|
171 |
static int file_close(mtar_t *tar) { |
|
|
172 |
fclose(tar->stream); |
|
|
173 |
return MTAR_ESUCCESS; |
|
|
174 |
} |
|
|
175 |
|
|
|
176 |
|
|
|
177 |
int mtar_open(mtar_t *tar, const char *filename, const char *mode) { |
|
|
178 |
int err; |
|
|
179 |
mtar_header_t h; |
|
|
180 |
|
|
|
181 |
/* Init tar struct and functions */ |
|
|
182 |
memset(tar, 0, sizeof(*tar)); |
|
|
183 |
tar->write = file_write; |
|
|
184 |
tar->read = file_read; |
|
|
185 |
tar->seek = file_seek; |
|
|
186 |
tar->close = file_close; |
|
|
187 |
|
|
|
188 |
/* Assure mode is always binary */ |
|
|
189 |
if ( strchr(mode, 'r') ) mode = "rb"; |
|
|
190 |
if ( strchr(mode, 'w') ) mode = "wb"; |
|
|
191 |
if ( strchr(mode, 'a') ) mode = "ab"; |
|
|
192 |
/* Open file */ |
|
|
193 |
tar->stream = fopen(filename, mode); |
|
|
194 |
if (!tar->stream) { |
|
|
195 |
return MTAR_EOPENFAIL; |
|
|
196 |
} |
|
|
197 |
/* Read first header to check it is valid if mode is `r` */ |
|
|
198 |
if (*mode == 'r') { |
|
|
199 |
err = mtar_read_header(tar, &h); |
|
|
200 |
if (err != MTAR_ESUCCESS) { |
|
|
201 |
mtar_close(tar); |
|
|
202 |
return err; |
|
|
203 |
} |
|
|
204 |
} |
|
|
205 |
|
|
|
206 |
/* Return ok */ |
|
|
207 |
return MTAR_ESUCCESS; |
|
|
208 |
} |
|
|
209 |
|
|
|
210 |
|
|
|
211 |
int mtar_close(mtar_t *tar) { |
|
|
212 |
return tar->close(tar); |
|
|
213 |
} |
|
|
214 |
|
|
|
215 |
|
|
|
216 |
int mtar_seek(mtar_t *tar, unsigned pos) { |
|
|
217 |
int err = tar->seek(tar, pos); |
|
|
218 |
tar->pos = pos; |
|
|
219 |
return err; |
|
|
220 |
} |
|
|
221 |
|
|
|
222 |
|
|
|
223 |
int mtar_rewind(mtar_t *tar) { |
|
|
224 |
tar->remaining_data = 0; |
|
|
225 |
tar->last_header = 0; |
|
|
226 |
return mtar_seek(tar, 0); |
|
|
227 |
} |
|
|
228 |
|
|
|
229 |
|
|
|
230 |
int mtar_next(mtar_t *tar) { |
|
|
231 |
int err, n; |
|
|
232 |
mtar_header_t h; |
|
|
233 |
/* Load header */ |
|
|
234 |
err = mtar_read_header(tar, &h); |
|
|
235 |
if (err) { |
|
|
236 |
return err; |
|
|
237 |
} |
|
|
238 |
/* Seek to next record */ |
|
|
239 |
n = round_up(h.size, 512) + sizeof(mtar_raw_header_t); |
|
|
240 |
return mtar_seek(tar, tar->pos + n); |
|
|
241 |
} |
|
|
242 |
|
|
|
243 |
|
|
|
244 |
int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h) { |
|
|
245 |
int err; |
|
|
246 |
mtar_header_t header; |
|
|
247 |
/* Start at beginning */ |
|
|
248 |
err = mtar_rewind(tar); |
|
|
249 |
if (err) { |
|
|
250 |
return err; |
|
|
251 |
} |
|
|
252 |
/* Iterate all files until we hit an error or find the file */ |
|
|
253 |
while ( (err = mtar_read_header(tar, &header)) == MTAR_ESUCCESS ) { |
|
|
254 |
if ( !strcmp(header.name, name) ) { |
|
|
255 |
if (h) { |
|
|
256 |
*h = header; |
|
|
257 |
} |
|
|
258 |
return MTAR_ESUCCESS; |
|
|
259 |
} |
|
|
260 |
mtar_next(tar); |
|
|
261 |
} |
|
|
262 |
/* Return error */ |
|
|
263 |
if (err == MTAR_ENULLRECORD) { |
|
|
264 |
err = MTAR_ENOTFOUND; |
|
|
265 |
} |
|
|
266 |
return err; |
|
|
267 |
} |
|
|
268 |
|
|
|
269 |
|
|
|
270 |
int mtar_read_header(mtar_t *tar, mtar_header_t *h) { |
|
|
271 |
int err; |
|
|
272 |
mtar_raw_header_t rh; |
|
|
273 |
/* Save header position */ |
|
|
274 |
tar->last_header = tar->pos; |
|
|
275 |
/* Read raw header */ |
|
|
276 |
err = tread(tar, &rh, sizeof(rh)); |
|
|
277 |
if (err) { |
|
|
278 |
return err; |
|
|
279 |
} |
|
|
280 |
/* Seek back to start of header */ |
|
|
281 |
err = mtar_seek(tar, tar->last_header); |
|
|
282 |
if (err) { |
|
|
283 |
return err; |
|
|
284 |
} |
|
|
285 |
/* Load raw header into header struct and return */ |
|
|
286 |
return raw_to_header(h, &rh); |
|
|
287 |
} |
|
|
288 |
|
|
|
289 |
|
|
|
290 |
int mtar_read_data(mtar_t *tar, void *ptr, unsigned size) { |
|
|
291 |
int err; |
|
|
292 |
/* If we have no remaining data then this is the first read, we get the size, |
|
|
293 |
* set the remaining data and seek to the beginning of the data */ |
|
|
294 |
if (tar->remaining_data == 0) { |
|
|
295 |
mtar_header_t h; |
|
|
296 |
/* Read header */ |
|
|
297 |
err = mtar_read_header(tar, &h); |
|
|
298 |
if (err) { |
|
|
299 |
return err; |
|
|
300 |
} |
|
|
301 |
/* Seek past header and init remaining data */ |
|
|
302 |
err = mtar_seek(tar, tar->pos + sizeof(mtar_raw_header_t)); |
|
|
303 |
if (err) { |
|
|
304 |
return err; |
|
|
305 |
} |
|
|
306 |
tar->remaining_data = h.size; |
|
|
307 |
} |
|
|
308 |
/* Read data */ |
|
|
309 |
err = tread(tar, ptr, size); |
|
|
310 |
if (err) { |
|
|
311 |
return err; |
|
|
312 |
} |
|
|
313 |
tar->remaining_data -= size; |
|
|
314 |
/* If there is no remaining data we've finished reading and seek back to the |
|
|
315 |
* header */ |
|
|
316 |
if (tar->remaining_data == 0) { |
|
|
317 |
return mtar_seek(tar, tar->last_header); |
|
|
318 |
} |
|
|
319 |
return MTAR_ESUCCESS; |
|
|
320 |
} |
|
|
321 |
|
|
|
322 |
|
|
|
323 |
int mtar_write_header(mtar_t *tar, const mtar_header_t *h) { |
|
|
324 |
mtar_raw_header_t rh; |
|
|
325 |
/* Build raw header and write */ |
|
|
326 |
header_to_raw(&rh, h); |
|
|
327 |
tar->remaining_data = h->size; |
|
|
328 |
return twrite(tar, &rh, sizeof(rh)); |
|
|
329 |
} |
|
|
330 |
|
|
|
331 |
|
|
|
332 |
int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size) { |
|
|
333 |
mtar_header_t h; |
|
|
334 |
/* Build header */ |
|
|
335 |
memset(&h, 0, sizeof(h)); |
|
|
336 |
strcpy(h.name, name); |
|
|
337 |
h.size = size; |
|
|
338 |
h.type = MTAR_TREG; |
|
|
339 |
h.mode = 0664; |
|
|
340 |
/* Write header */ |
|
|
341 |
return mtar_write_header(tar, &h); |
|
|
342 |
} |
|
|
343 |
|
|
|
344 |
|
|
|
345 |
int mtar_write_dir_header(mtar_t *tar, const char *name) { |
|
|
346 |
mtar_header_t h; |
|
|
347 |
/* Build header */ |
|
|
348 |
memset(&h, 0, sizeof(h)); |
|
|
349 |
strcpy(h.name, name); |
|
|
350 |
h.type = MTAR_TDIR; |
|
|
351 |
h.mode = 0775; |
|
|
352 |
/* Write header */ |
|
|
353 |
return mtar_write_header(tar, &h); |
|
|
354 |
} |
|
|
355 |
|
|
|
356 |
|
|
|
357 |
int mtar_write_data(mtar_t *tar, const void *data, unsigned size) { |
|
|
358 |
int err; |
|
|
359 |
/* Write data */ |
|
|
360 |
err = twrite(tar, data, size); |
|
|
361 |
if (err) { |
|
|
362 |
return err; |
|
|
363 |
} |
|
|
364 |
tar->remaining_data -= size; |
|
|
365 |
/* Write padding if we've written all the data for this file */ |
|
|
366 |
if (tar->remaining_data == 0) { |
|
|
367 |
return write_null_bytes(tar, round_up(tar->pos, 512) - tar->pos); |
|
|
368 |
} |
|
|
369 |
return MTAR_ESUCCESS; |
|
|
370 |
} |
|
|
371 |
|
|
|
372 |
|
|
|
373 |
int mtar_finalize(mtar_t *tar) { |
|
|
374 |
/* Write two NULL records */ |
|
|
375 |
return write_null_bytes(tar, sizeof(mtar_raw_header_t) * 2); |
|
|
376 |
} |