diff options
Diffstat (limited to 'lib/microtar/src/microtar.c')
-rw-r--r-- | lib/microtar/src/microtar.c | 886 |
1 files changed, 604 insertions, 282 deletions
diff --git a/lib/microtar/src/microtar.c b/lib/microtar/src/microtar.c index 04cd4ea132..8ab9a8c5f4 100644 --- a/lib/microtar/src/microtar.c +++ b/lib/microtar/src/microtar.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2017 rxi | 2 | * Copyright (c) 2017 rxi |
3 | * Copyright (c) 2021 Aidan MacDonald | ||
3 | * | 4 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | 5 | * 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 | * of this software and associated documentation files (the "Software"), to |
@@ -20,16 +21,14 @@ | |||
20 | * IN THE SOFTWARE. | 21 | * IN THE SOFTWARE. |
21 | */ | 22 | */ |
22 | 23 | ||
23 | #include <stdlib.h> | 24 | #include "microtar.h" |
24 | #include <stddef.h> | ||
25 | #include <limits.h> | 25 | #include <limits.h> |
26 | #include <string.h> | 26 | #include <string.h> |
27 | 27 | ||
28 | #include "microtar.h" | ||
29 | |||
30 | #ifdef ROCKBOX | 28 | #ifdef ROCKBOX |
31 | /* Rockbox lacks strncpy in its libc */ | 29 | /* Rockbox lacks strncpy in its libc */ |
32 | static char* strncpy(char* dest, const char* src, size_t n) { | 30 | #define strncpy my_strncpy |
31 | static char* my_strncpy(char* dest, const char* src, size_t n) { | ||
33 | size_t i; | 32 | size_t i; |
34 | 33 | ||
35 | for(i = 0; i < n && *src; ++i) | 34 | for(i = 0; i < n && *src; ++i) |
@@ -42,362 +41,685 @@ static char* strncpy(char* dest, const char* src, size_t n) { | |||
42 | } | 41 | } |
43 | #endif | 42 | #endif |
44 | 43 | ||
44 | enum { | ||
45 | S_HEADER_VALID = 1 << 0, | ||
46 | S_WROTE_HEADER = 1 << 1, | ||
47 | S_WROTE_DATA = 1 << 2, | ||
48 | S_WROTE_DATA_EOF = 1 << 3, | ||
49 | S_WROTE_FINALIZE = 1 << 4, | ||
50 | }; | ||
51 | |||
52 | enum { | ||
53 | NAME_OFF = 0, NAME_LEN = 100, | ||
54 | MODE_OFF = NAME_OFF+NAME_LEN, MODE_LEN = 8, | ||
55 | OWNER_OFF = MODE_OFF+MODE_LEN, OWNER_LEN = 8, | ||
56 | GROUP_OFF = OWNER_OFF+OWNER_LEN, GROUP_LEN = 8, | ||
57 | SIZE_OFF = GROUP_OFF+GROUP_LEN, SIZE_LEN = 12, | ||
58 | MTIME_OFF = SIZE_OFF+SIZE_LEN, MTIME_LEN = 12, | ||
59 | CHKSUM_OFF = MTIME_OFF+MTIME_LEN, CHKSUM_LEN = 8, | ||
60 | TYPE_OFF = CHKSUM_OFF+CHKSUM_LEN, | ||
61 | LINKNAME_OFF = TYPE_OFF+1, LINKNAME_LEN = 100, | ||
62 | |||
63 | HEADER_LEN = 512, | ||
64 | }; | ||
65 | |||
66 | static int parse_octal(const char* str, size_t len, unsigned* ret) | ||
67 | { | ||
68 | unsigned n = 0; | ||
69 | |||
70 | while(len-- > 0 && *str != 0) { | ||
71 | if(*str < '0' || *str > '9') | ||
72 | return MTAR_EOVERFLOW; | ||
73 | |||
74 | if(n > UINT_MAX/8) | ||
75 | return MTAR_EOVERFLOW; | ||
76 | else | ||
77 | n *= 8; | ||
78 | |||
79 | char r = *str++ - '0'; | ||
80 | |||
81 | if(n > UINT_MAX - r) | ||
82 | return MTAR_EOVERFLOW; | ||
83 | else | ||
84 | n += r; | ||
85 | } | ||
45 | 86 | ||
46 | static int parse_octal(const char* str, size_t len, unsigned* ret) { | 87 | *ret = n; |
47 | unsigned n = 0; | 88 | return MTAR_ESUCCESS; |
48 | while(len-- > 0 && *str != 0) { | ||
49 | if(*str < '0' || *str > '9') | ||
50 | return MTAR_EOVERFLOW; | ||
51 | |||
52 | if(n > UINT_MAX/8) | ||
53 | return MTAR_EOVERFLOW; | ||
54 | else | ||
55 | n *= 8; | ||
56 | |||
57 | char r = *str++ - '0'; | ||
58 | if(n > UINT_MAX - r) | ||
59 | return MTAR_EOVERFLOW; | ||
60 | else | ||
61 | n += r; | ||
62 | } | ||
63 | |||
64 | *ret = n; | ||
65 | return MTAR_ESUCCESS; | ||
66 | } | 89 | } |
67 | 90 | ||
91 | static int print_octal(char* str, size_t len, unsigned value) | ||
92 | { | ||
93 | /* move backwards over the output string */ | ||
94 | char* ptr = str + len - 1; | ||
95 | *ptr = 0; | ||
68 | 96 | ||
69 | static int print_octal(char* str, size_t len, unsigned value) { | 97 | /* output the significant digits */ |
70 | /* move backwards over the output string */ | 98 | while(value > 0) { |
71 | char* ptr = str + len - 1; | 99 | if(ptr == str) |
72 | *ptr = 0; | 100 | return MTAR_EOVERFLOW; |
73 | 101 | ||
74 | /* output the significant digits */ | 102 | --ptr; |
75 | while(value > 0) { | 103 | *ptr = '0' + (value % 8); |
76 | if(ptr == str) | 104 | value /= 8; |
77 | return MTAR_EOVERFLOW; | 105 | } |
78 | 106 | ||
79 | --ptr; | 107 | /* pad the remainder of the field with zeros */ |
80 | *ptr = '0' + (value % 8); | 108 | while(ptr != str) { |
81 | value /= 8; | 109 | --ptr; |
82 | } | 110 | *ptr = '0'; |
111 | } | ||
83 | 112 | ||
84 | /* pad the remainder of the field with zeros */ | 113 | return MTAR_ESUCCESS; |
85 | while(ptr != str) { | 114 | } |
86 | --ptr; | ||
87 | *ptr = '0'; | ||
88 | } | ||
89 | 115 | ||
90 | return MTAR_ESUCCESS; | 116 | static unsigned round_up_512(unsigned n) |
117 | { | ||
118 | return (n + 511u) & ~511u; | ||
91 | } | 119 | } |
92 | 120 | ||
121 | static int tread(mtar_t* tar, void* data, unsigned size) | ||
122 | { | ||
123 | int ret = tar->ops->read(tar->stream, data, size); | ||
124 | if(ret >= 0) | ||
125 | tar->pos += ret; | ||
93 | 126 | ||
94 | static unsigned round_up(unsigned n, unsigned incr) { | 127 | return ret; |
95 | return n + (incr - n % incr) % incr; | ||
96 | } | 128 | } |
97 | 129 | ||
130 | static int twrite(mtar_t* tar, const void* data, unsigned size) | ||
131 | { | ||
132 | int ret = tar->ops->write(tar->stream, data, size); | ||
133 | if(ret >= 0) | ||
134 | tar->pos += ret; | ||
98 | 135 | ||
99 | static unsigned checksum(const mtar_raw_header_t* rh) { | 136 | return ret; |
100 | unsigned i; | ||
101 | unsigned char *p = (unsigned char*) rh; | ||
102 | unsigned res = 256; | ||
103 | for (i = 0; i < offsetof(mtar_raw_header_t, checksum); i++) { | ||
104 | res += p[i]; | ||
105 | } | ||
106 | for (i = offsetof(mtar_raw_header_t, type); i < sizeof(*rh); i++) { | ||
107 | res += p[i]; | ||
108 | } | ||
109 | return res; | ||
110 | } | 137 | } |
111 | 138 | ||
112 | 139 | static int tseek(mtar_t* tar, unsigned pos) | |
113 | static int tread(mtar_t *tar, void *data, unsigned size) { | 140 | { |
114 | int err = tar->read(tar, data, size); | 141 | int err = tar->ops->seek(tar->stream, pos); |
115 | tar->pos += size; | 142 | tar->pos = pos; |
116 | return err; | 143 | return err; |
117 | } | 144 | } |
118 | 145 | ||
146 | static int write_null_bytes(mtar_t* tar, size_t count) | ||
147 | { | ||
148 | int ret; | ||
149 | size_t n; | ||
150 | |||
151 | memset(tar->buffer, 0, sizeof(tar->buffer)); | ||
152 | while(count > 0) { | ||
153 | n = count < sizeof(tar->buffer) ? count : sizeof(tar->buffer); | ||
154 | ret = twrite(tar, tar->buffer, n); | ||
155 | if(ret < 0) | ||
156 | return ret; | ||
157 | if(ret != (int)n) | ||
158 | return MTAR_EWRITEFAIL; | ||
159 | |||
160 | count -= n; | ||
161 | } | ||
119 | 162 | ||
120 | static int twrite(mtar_t *tar, const void *data, unsigned size) { | 163 | return MTAR_ESUCCESS; |
121 | int err = tar->write(tar, data, size); | ||
122 | tar->pos += size; | ||
123 | return err; | ||
124 | } | 164 | } |
125 | 165 | ||
166 | static unsigned checksum(const char* raw) | ||
167 | { | ||
168 | unsigned i; | ||
169 | unsigned char* p = (unsigned char*)raw; | ||
170 | unsigned res = 256; | ||
126 | 171 | ||
127 | static int write_null_bytes(mtar_t *tar, int n) { | 172 | for(i = 0; i < CHKSUM_OFF; i++) |
128 | int i, err; | 173 | res += p[i]; |
129 | char nul = '\0'; | 174 | for(i = TYPE_OFF; i < HEADER_LEN; i++) |
130 | for (i = 0; i < n; i++) { | 175 | res += p[i]; |
131 | err = twrite(tar, &nul, 1); | 176 | |
132 | if (err) { | 177 | return res; |
133 | return err; | ||
134 | } | ||
135 | } | ||
136 | return MTAR_ESUCCESS; | ||
137 | } | 178 | } |
138 | 179 | ||
180 | static int raw_to_header(mtar_header_t* h, const char* raw) | ||
181 | { | ||
182 | unsigned chksum; | ||
183 | int rc; | ||
184 | |||
185 | /* If the checksum starts with a null byte we assume the record is NULL */ | ||
186 | if(raw[CHKSUM_OFF] == '\0') | ||
187 | return MTAR_ENULLRECORD; | ||
188 | |||
189 | /* Compare the checksum */ | ||
190 | if((rc = parse_octal(&raw[CHKSUM_OFF], CHKSUM_LEN, &chksum))) | ||
191 | return rc; | ||
192 | if(chksum != checksum(raw)) | ||
193 | return MTAR_EBADCHKSUM; | ||
194 | |||
195 | /* Load raw header into header */ | ||
196 | if((rc = parse_octal(&raw[MODE_OFF], MODE_LEN, &h->mode))) | ||
197 | return rc; | ||
198 | if((rc = parse_octal(&raw[OWNER_OFF], OWNER_LEN, &h->owner))) | ||
199 | return rc; | ||
200 | if((rc = parse_octal(&raw[GROUP_OFF], GROUP_LEN, &h->group))) | ||
201 | return rc; | ||
202 | if((rc = parse_octal(&raw[SIZE_OFF], SIZE_LEN, &h->size))) | ||
203 | return rc; | ||
204 | if((rc = parse_octal(&raw[MTIME_OFF], MTIME_LEN, &h->mtime))) | ||
205 | return rc; | ||
206 | |||
207 | h->type = raw[TYPE_OFF]; | ||
208 | if(!h->type) | ||
209 | h->type = MTAR_TREG; | ||
210 | |||
211 | memcpy(h->name, &raw[NAME_OFF], NAME_LEN); | ||
212 | h->name[sizeof(h->name) - 1] = 0; | ||
213 | |||
214 | memcpy(h->linkname, &raw[LINKNAME_OFF], LINKNAME_LEN); | ||
215 | h->linkname[sizeof(h->linkname) - 1] = 0; | ||
216 | |||
217 | return MTAR_ESUCCESS; | ||
218 | } | ||
139 | 219 | ||
140 | static int raw_to_header(mtar_header_t *h, const mtar_raw_header_t *rh) { | 220 | static int header_to_raw(char* raw, const mtar_header_t* h) |
141 | unsigned chksum1, chksum2; | 221 | { |
142 | int rc; | 222 | unsigned chksum; |
223 | int rc; | ||
224 | |||
225 | memset(raw, 0, HEADER_LEN); | ||
226 | |||
227 | /* Load header into raw header */ | ||
228 | if((rc = print_octal(&raw[MODE_OFF], MODE_LEN, h->mode))) | ||
229 | return rc; | ||
230 | if((rc = print_octal(&raw[OWNER_OFF], OWNER_LEN, h->owner))) | ||
231 | return rc; | ||
232 | if((rc = print_octal(&raw[GROUP_OFF], GROUP_LEN, h->group))) | ||
233 | return rc; | ||
234 | if((rc = print_octal(&raw[SIZE_OFF], SIZE_LEN, h->size))) | ||
235 | return rc; | ||
236 | if((rc = print_octal(&raw[MTIME_OFF], MTIME_LEN, h->mtime))) | ||
237 | return rc; | ||
238 | |||
239 | raw[TYPE_OFF] = h->type ? h->type : MTAR_TREG; | ||
240 | |||
241 | #if defined(__GNUC__) && (__GNUC__ >= 8) | ||
242 | /* Sigh. GCC wrongly assumes the output of strncpy() is supposed to be | ||
243 | * a null-terminated string -- which it is not, and we are relying on | ||
244 | * that fact here -- and tries to warn about 'string truncation' because | ||
245 | * the null terminator might not be copied. Just suppress the warning. */ | ||
246 | # pragma GCC diagnostic push | ||
247 | # pragma GCC diagnostic ignored "-Wstringop-truncation" | ||
248 | #endif | ||
143 | 249 | ||
144 | /* If the checksum starts with a null byte we assume the record is NULL */ | 250 | strncpy(&raw[NAME_OFF], h->name, NAME_LEN); |
145 | if (*rh->checksum == '\0') { | 251 | strncpy(&raw[LINKNAME_OFF], h->linkname, LINKNAME_LEN); |
146 | return MTAR_ENULLRECORD; | ||
147 | } | ||
148 | 252 | ||
149 | /* Build and compare checksum */ | 253 | #if defined(__GNUC__) && (__GNUC__ >= 8) |
150 | chksum1 = checksum(rh); | 254 | # pragma GCC diagnostic pop |
151 | if((rc = parse_octal(rh->checksum, sizeof(rh->checksum), &chksum2))) | 255 | #endif |
152 | return rc; | ||
153 | if (chksum1 != chksum2) { | ||
154 | return MTAR_EBADCHKSUM; | ||
155 | } | ||
156 | 256 | ||
157 | /* Load raw header into header */ | 257 | /* Calculate and write checksum */ |
158 | if((rc = parse_octal(rh->mode, sizeof(rh->mode), &h->mode))) | 258 | chksum = checksum(raw); |
159 | return rc; | 259 | if((rc = print_octal(&raw[CHKSUM_OFF], CHKSUM_LEN-1, chksum))) |
160 | if((rc = parse_octal(rh->owner, sizeof(rh->owner), &h->owner))) | 260 | return rc; |
161 | return rc; | ||
162 | if((rc = parse_octal(rh->group, sizeof(rh->group), &h->group))) | ||
163 | return rc; | ||
164 | if((rc = parse_octal(rh->size, sizeof(rh->size), &h->size))) | ||
165 | return rc; | ||
166 | if((rc = parse_octal(rh->mtime, sizeof(rh->mtime), &h->mtime))) | ||
167 | return rc; | ||
168 | 261 | ||
169 | h->type = rh->type; | 262 | raw[CHKSUM_OFF + CHKSUM_LEN - 1] = ' '; |
170 | 263 | ||
171 | memcpy(h->name, rh->name, sizeof(rh->name)); | 264 | return MTAR_ESUCCESS; |
172 | h->name[sizeof(h->name) - 1] = 0; | 265 | } |
173 | 266 | ||
174 | memcpy(h->linkname, rh->linkname, sizeof(rh->linkname)); | 267 | static unsigned data_beg_pos(const mtar_t* tar) |
175 | h->linkname[sizeof(h->linkname) - 1] = 0; | 268 | { |
269 | return tar->header_pos + HEADER_LEN; | ||
270 | } | ||
176 | 271 | ||
177 | return MTAR_ESUCCESS; | 272 | static unsigned data_end_pos(const mtar_t* tar) |
273 | { | ||
274 | return tar->end_pos; | ||
178 | } | 275 | } |
179 | 276 | ||
277 | static int ensure_header(mtar_t* tar) | ||
278 | { | ||
279 | int ret, err; | ||
180 | 280 | ||
181 | static int header_to_raw(mtar_raw_header_t *rh, const mtar_header_t *h) { | 281 | if(tar->state & S_HEADER_VALID) |
182 | unsigned chksum; | 282 | return MTAR_ESUCCESS; |
183 | int rc; | ||
184 | 283 | ||
185 | /* Load header into raw header */ | 284 | if(tar->pos > UINT_MAX - HEADER_LEN) |
186 | memset(rh, 0, sizeof(*rh)); | 285 | return MTAR_EOVERFLOW; |
187 | if((rc = print_octal(rh->mode, sizeof(rh->mode), h->mode))) | ||
188 | return rc; | ||
189 | if((rc = print_octal(rh->owner, sizeof(rh->owner), h->owner))) | ||
190 | return rc; | ||
191 | if((rc = print_octal(rh->group, sizeof(rh->group), h->group))) | ||
192 | return rc; | ||
193 | if((rc = print_octal(rh->size, sizeof(rh->size), h->size))) | ||
194 | return rc; | ||
195 | if((rc = print_octal(rh->mtime, sizeof(rh->mtime), h->mtime))) | ||
196 | return rc; | ||
197 | rh->type = h->type ? h->type : MTAR_TREG; | ||
198 | strncpy(rh->name, h->name, sizeof(rh->name)); | ||
199 | strncpy(rh->linkname, h->linkname, sizeof(rh->linkname)); | ||
200 | 286 | ||
201 | /* Calculate and write checksum */ | 287 | tar->header_pos = tar->pos; |
202 | chksum = checksum(rh); | 288 | tar->end_pos = data_beg_pos(tar); |
203 | if((rc = print_octal(rh->checksum, 7, chksum))) | ||
204 | return rc; | ||
205 | rh->checksum[7] = ' '; | ||
206 | 289 | ||
207 | return MTAR_ESUCCESS; | 290 | ret = tread(tar, tar->buffer, HEADER_LEN); |
208 | } | 291 | if(ret < 0) |
292 | return ret; | ||
293 | if(ret != HEADER_LEN) | ||
294 | return MTAR_EREADFAIL; | ||
209 | 295 | ||
296 | err = raw_to_header(&tar->header, tar->buffer); | ||
297 | if(err) | ||
298 | return err; | ||
210 | 299 | ||
211 | const char* mtar_strerror(int err) { | 300 | if(tar->end_pos > UINT_MAX - tar->header.size) |
212 | switch (err) { | 301 | return MTAR_EOVERFLOW; |
213 | case MTAR_ESUCCESS : return "success"; | 302 | tar->end_pos += tar->header.size; |
214 | case MTAR_EFAILURE : return "failure"; | 303 | |
215 | case MTAR_EOPENFAIL : return "could not open"; | 304 | tar->state |= S_HEADER_VALID; |
216 | case MTAR_EREADFAIL : return "could not read"; | 305 | return MTAR_ESUCCESS; |
217 | case MTAR_EWRITEFAIL : return "could not write"; | ||
218 | case MTAR_ESEEKFAIL : return "could not seek"; | ||
219 | case MTAR_EBADCHKSUM : return "bad checksum"; | ||
220 | case MTAR_ENULLRECORD : return "null record"; | ||
221 | case MTAR_ENOTFOUND : return "file not found"; | ||
222 | case MTAR_EOVERFLOW : return "overflow"; | ||
223 | } | ||
224 | return "unknown error"; | ||
225 | } | 306 | } |
226 | 307 | ||
308 | const char* mtar_strerror(int err) | ||
309 | { | ||
310 | switch(err) { | ||
311 | case MTAR_ESUCCESS: return "success"; | ||
312 | case MTAR_EFAILURE: return "failure"; | ||
313 | case MTAR_EOPENFAIL: return "could not open"; | ||
314 | case MTAR_EREADFAIL: return "could not read"; | ||
315 | case MTAR_EWRITEFAIL: return "could not write"; | ||
316 | case MTAR_ESEEKFAIL: return "could not seek"; | ||
317 | case MTAR_ESEEKRANGE: return "seek out of bounds"; | ||
318 | case MTAR_EBADCHKSUM: return "bad checksum"; | ||
319 | case MTAR_ENULLRECORD: return "null record"; | ||
320 | case MTAR_ENOTFOUND: return "file not found"; | ||
321 | case MTAR_EOVERFLOW: return "overflow"; | ||
322 | case MTAR_EAPI: return "API usage error"; | ||
323 | case MTAR_ENAMETOOLONG: return "name too long"; | ||
324 | case MTAR_EWRONGSIZE: return "wrong amount of data written"; | ||
325 | case MTAR_EACCESS: return "wrong access mode"; | ||
326 | default: return "unknown error"; | ||
327 | } | ||
328 | } | ||
227 | 329 | ||
228 | int mtar_close(mtar_t *tar) { | 330 | void mtar_init(mtar_t* tar, int access, const mtar_ops_t* ops, void* stream) |
229 | return tar->close(tar); | 331 | { |
332 | memset(tar, 0, sizeof(mtar_t)); | ||
333 | tar->access = access; | ||
334 | tar->ops = ops; | ||
335 | tar->stream = stream; | ||
230 | } | 336 | } |
231 | 337 | ||
338 | int mtar_close(mtar_t* tar) | ||
339 | { | ||
340 | int err = tar->ops->close(tar->stream); | ||
341 | tar->ops = NULL; | ||
342 | tar->stream = NULL; | ||
343 | return err; | ||
344 | } | ||
232 | 345 | ||
233 | int mtar_seek(mtar_t *tar, unsigned pos) { | 346 | int mtar_is_open(mtar_t* tar) |
234 | int err = tar->seek(tar, pos); | 347 | { |
235 | tar->pos = pos; | 348 | return (tar->ops != NULL) ? 1 : 0; |
236 | return err; | ||
237 | } | 349 | } |
238 | 350 | ||
351 | mtar_header_t* mtar_get_header(mtar_t* tar) | ||
352 | { | ||
353 | if(tar->state & S_HEADER_VALID) | ||
354 | return &tar->header; | ||
355 | else | ||
356 | return NULL; | ||
357 | } | ||
239 | 358 | ||
240 | int mtar_rewind(mtar_t *tar) { | 359 | int mtar_access_mode(const mtar_t* tar) |
241 | tar->remaining_data = 0; | 360 | { |
242 | tar->last_header = 0; | 361 | return tar->access; |
243 | return mtar_seek(tar, 0); | ||
244 | } | 362 | } |
245 | 363 | ||
364 | int mtar_rewind(mtar_t* tar) | ||
365 | { | ||
366 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
367 | if(tar->access != MTAR_READ) | ||
368 | return MTAR_EAPI; | ||
369 | #endif | ||
246 | 370 | ||
247 | int mtar_next(mtar_t *tar) { | 371 | int err = tseek(tar, 0); |
248 | int err, n; | 372 | tar->state = 0; |
249 | /* Load header */ | ||
250 | err = mtar_read_header(tar, &tar->header); | ||
251 | if (err) { | ||
252 | return err; | 373 | return err; |
253 | } | ||
254 | /* Seek to next record */ | ||
255 | n = round_up(tar->header.size, 512) + sizeof(mtar_raw_header_t); | ||
256 | return mtar_seek(tar, tar->pos + n); | ||
257 | } | 374 | } |
258 | 375 | ||
376 | int mtar_next(mtar_t* tar) | ||
377 | { | ||
378 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
379 | if(tar->access != MTAR_READ) | ||
380 | return MTAR_EACCESS; | ||
381 | #endif | ||
259 | 382 | ||
260 | int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h) { | 383 | if(tar->state & S_HEADER_VALID) { |
261 | int err; | 384 | tar->state &= ~S_HEADER_VALID; |
262 | /* Start at beginning */ | 385 | |
263 | err = mtar_rewind(tar); | 386 | /* seek to the next header */ |
264 | if (err) { | 387 | int err = tseek(tar, round_up_512(data_end_pos(tar))); |
265 | return err; | 388 | if(err) |
266 | } | 389 | return err; |
267 | /* Iterate all files until we hit an error or find the file */ | ||
268 | while ( (err = mtar_read_header(tar, &tar->header)) == MTAR_ESUCCESS ) { | ||
269 | if ( !strcmp(tar->header.name, name) ) { | ||
270 | if (h) { | ||
271 | *h = tar->header; | ||
272 | } | ||
273 | return MTAR_ESUCCESS; | ||
274 | } | ||
275 | err = mtar_next(tar); | ||
276 | if (err) { | ||
277 | return err; | ||
278 | } | 390 | } |
279 | } | 391 | |
280 | /* Return error */ | 392 | return ensure_header(tar); |
281 | if (err == MTAR_ENULLRECORD) { | 393 | } |
282 | err = MTAR_ENOTFOUND; | 394 | |
283 | } | 395 | int mtar_foreach(mtar_t* tar, mtar_foreach_cb cb, void* arg) |
284 | return err; | 396 | { |
285 | } | 397 | #ifndef MICROTAR_DISABLE_API_CHECKS |
286 | 398 | if(tar->access != MTAR_READ) | |
287 | 399 | return MTAR_EACCESS; | |
288 | int mtar_read_header(mtar_t *tar, mtar_header_t *h) { | 400 | #endif |
289 | int err; | 401 | |
290 | /* Save header position */ | 402 | int err = mtar_rewind(tar); |
291 | tar->last_header = tar->pos; | 403 | if(err) |
292 | /* Read raw header */ | 404 | return err; |
293 | err = tread(tar, &tar->raw_header, sizeof(tar->raw_header)); | 405 | |
294 | if (err) { | 406 | while((err = mtar_next(tar)) == MTAR_ESUCCESS) |
407 | if((err = cb(tar, &tar->header, arg)) != 0) | ||
408 | return err; | ||
409 | |||
410 | if(err == MTAR_ENULLRECORD) | ||
411 | err = MTAR_ESUCCESS; | ||
412 | |||
295 | return err; | 413 | return err; |
296 | } | 414 | } |
297 | /* Seek back to start of header */ | 415 | |
298 | err = mtar_seek(tar, tar->last_header); | 416 | static int find_foreach_cb(mtar_t* tar, const mtar_header_t* h, void* arg) |
299 | if (err) { | 417 | { |
418 | (void)tar; | ||
419 | const char* name = (const char*)arg; | ||
420 | return strcmp(name, h->name) ? 0 : 1; | ||
421 | } | ||
422 | |||
423 | int mtar_find(mtar_t* tar, const char* name) | ||
424 | { | ||
425 | int err = mtar_foreach(tar, find_foreach_cb, (void*)name); | ||
426 | if(err == 1) | ||
427 | err = MTAR_ESUCCESS; | ||
428 | else if(err == MTAR_ESUCCESS) | ||
429 | err = MTAR_ENOTFOUND; | ||
430 | |||
300 | return err; | 431 | return err; |
301 | } | ||
302 | /* Load raw header into header struct and return */ | ||
303 | return raw_to_header(h, &tar->raw_header); | ||
304 | } | 432 | } |
305 | 433 | ||
434 | int mtar_read_data(mtar_t* tar, void* ptr, unsigned size) | ||
435 | { | ||
436 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
437 | if(!(tar->state & S_HEADER_VALID)) | ||
438 | return MTAR_EAPI; | ||
439 | #endif | ||
306 | 440 | ||
307 | int mtar_read_data(mtar_t *tar, void *ptr, unsigned size) { | 441 | /* have we reached end of file? */ |
308 | int err; | 442 | unsigned data_end = data_end_pos(tar); |
309 | /* If we have no remaining data then this is the first read, we get the size, | 443 | if(tar->pos >= data_end) |
310 | * set the remaining data and seek to the beginning of the data */ | 444 | return 0; |
311 | if (tar->remaining_data == 0) { | 445 | |
312 | /* Read header */ | 446 | /* truncate the read if it would go beyond EOF */ |
313 | err = mtar_read_header(tar, &tar->header); | 447 | unsigned data_left = data_end - tar->pos; |
314 | if (err) { | 448 | if(data_left < size) |
315 | return err; | 449 | size = data_left; |
316 | } | 450 | |
317 | /* Seek past header and init remaining data */ | 451 | return tread(tar, ptr, size); |
318 | err = mtar_seek(tar, tar->pos + sizeof(mtar_raw_header_t)); | 452 | } |
319 | if (err) { | 453 | |
320 | return err; | 454 | int mtar_seek_data(mtar_t* tar, int offset, int whence) |
455 | { | ||
456 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
457 | if(!(tar->state & S_HEADER_VALID)) | ||
458 | return MTAR_EAPI; | ||
459 | #endif | ||
460 | |||
461 | unsigned data_beg = data_beg_pos(tar); | ||
462 | unsigned data_end = data_end_pos(tar); | ||
463 | unsigned newpos; | ||
464 | |||
465 | switch(whence) { | ||
466 | case SEEK_SET: | ||
467 | if(offset < 0) | ||
468 | return MTAR_ESEEKRANGE; | ||
469 | |||
470 | newpos = data_beg + offset; | ||
471 | break; | ||
472 | |||
473 | case SEEK_CUR: | ||
474 | if((offset > 0 && (unsigned) offset > data_end - tar->pos) || | ||
475 | (offset < 0 && (unsigned)-offset > tar->pos - data_beg)) | ||
476 | return MTAR_ESEEKRANGE; | ||
477 | |||
478 | newpos = tar->pos + offset; | ||
479 | break; | ||
480 | |||
481 | case SEEK_END: | ||
482 | if(offset > 0) | ||
483 | return MTAR_ESEEKRANGE; | ||
484 | |||
485 | newpos = data_end + offset; | ||
486 | break; | ||
487 | |||
488 | default: | ||
489 | return MTAR_EAPI; | ||
321 | } | 490 | } |
322 | tar->remaining_data = tar->header.size; | 491 | |
323 | } | 492 | return tseek(tar, newpos); |
324 | /* Ensure caller does not read too much */ | 493 | } |
325 | if(size > tar->remaining_data) | 494 | |
326 | return MTAR_EOVERFLOW; | 495 | unsigned mtar_tell_data(mtar_t* tar) |
327 | /* Read data */ | 496 | { |
328 | err = tread(tar, ptr, size); | 497 | #ifndef MICROTAR_DISABLE_API_CHECKS |
329 | if (err) { | 498 | if(!(tar->state & S_HEADER_VALID)) |
330 | return err; | 499 | return MTAR_EAPI; |
331 | } | 500 | #endif |
332 | tar->remaining_data -= size; | 501 | |
333 | /* If there is no remaining data we've finished reading and seek back to the | 502 | return tar->pos - data_beg_pos(tar); |
334 | * header */ | ||
335 | if (tar->remaining_data == 0) { | ||
336 | return mtar_seek(tar, tar->last_header); | ||
337 | } | ||
338 | return MTAR_ESUCCESS; | ||
339 | } | 503 | } |
340 | 504 | ||
505 | int mtar_eof_data(mtar_t* tar) | ||
506 | { | ||
507 | /* API usage error, but just claim EOF. */ | ||
508 | if(!(tar->state & S_HEADER_VALID)) | ||
509 | return 1; | ||
341 | 510 | ||
342 | int mtar_write_header(mtar_t *tar, const mtar_header_t *h) { | 511 | return tar->pos >= data_end_pos(tar) ? 1 : 0; |
343 | /* Build raw header and write */ | ||
344 | header_to_raw(&tar->raw_header, h); | ||
345 | tar->remaining_data = h->size; | ||
346 | return twrite(tar, &tar->raw_header, sizeof(tar->raw_header)); | ||
347 | } | 512 | } |
348 | 513 | ||
514 | int mtar_write_header(mtar_t* tar, const mtar_header_t* h) | ||
515 | { | ||
516 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
517 | if(tar->access != MTAR_WRITE) | ||
518 | return MTAR_EACCESS; | ||
519 | if(((tar->state & S_WROTE_DATA) && !(tar->state & S_WROTE_DATA_EOF)) || | ||
520 | (tar->state & S_WROTE_FINALIZE)) | ||
521 | return MTAR_EAPI; | ||
522 | #endif | ||
523 | |||
524 | tar->state &= ~(S_HEADER_VALID | S_WROTE_HEADER | | ||
525 | S_WROTE_DATA | S_WROTE_DATA_EOF); | ||
526 | |||
527 | /* ensure we have enough space to write the declared amount of data */ | ||
528 | if(tar->pos > UINT_MAX - HEADER_LEN - round_up_512(h->size)) | ||
529 | return MTAR_EOVERFLOW; | ||
530 | |||
531 | tar->header_pos = tar->pos; | ||
532 | tar->end_pos = data_beg_pos(tar); | ||
533 | |||
534 | if(h != &tar->header) | ||
535 | tar->header = *h; | ||
536 | |||
537 | int err = header_to_raw(tar->buffer, &tar->header); | ||
538 | if(err) | ||
539 | return err; | ||
540 | |||
541 | int ret = twrite(tar, tar->buffer, HEADER_LEN); | ||
542 | if(ret < 0) | ||
543 | return ret; | ||
544 | if(ret != HEADER_LEN) | ||
545 | return MTAR_EWRITEFAIL; | ||
349 | 546 | ||
350 | int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size) { | 547 | tar->state |= (S_HEADER_VALID | S_WROTE_HEADER); |
351 | /* Build header */ | 548 | return MTAR_ESUCCESS; |
352 | memset(&tar->header, 0, sizeof(tar->header)); | ||
353 | /* Ensure name fits within header */ | ||
354 | if(strlen(name) > sizeof(tar->header.name)) | ||
355 | return MTAR_EOVERFLOW; | ||
356 | strncpy(tar->header.name, name, sizeof(tar->header.name)); | ||
357 | tar->header.size = size; | ||
358 | tar->header.type = MTAR_TREG; | ||
359 | tar->header.mode = 0664; | ||
360 | /* Write header */ | ||
361 | return mtar_write_header(tar, &tar->header); | ||
362 | } | 549 | } |
363 | 550 | ||
551 | int mtar_update_header(mtar_t* tar, const mtar_header_t* h) | ||
552 | { | ||
553 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
554 | if(tar->access != MTAR_WRITE) | ||
555 | return MTAR_EACCESS; | ||
556 | if(!(tar->state & S_WROTE_HEADER) || | ||
557 | (tar->state & S_WROTE_DATA_EOF) || | ||
558 | (tar->state & S_WROTE_FINALIZE)) | ||
559 | return MTAR_EAPI; | ||
560 | #endif | ||
561 | |||
562 | unsigned beg_pos = data_beg_pos(tar); | ||
563 | if(beg_pos > UINT_MAX - h->size) | ||
564 | return MTAR_EOVERFLOW; | ||
565 | |||
566 | unsigned old_pos = tar->pos; | ||
567 | int err = tseek(tar, tar->header_pos); | ||
568 | if(err) | ||
569 | return err; | ||
570 | |||
571 | if(h != &tar->header) | ||
572 | tar->header = *h; | ||
364 | 573 | ||
365 | int mtar_write_dir_header(mtar_t *tar, const char *name) { | 574 | err = header_to_raw(tar->buffer, &tar->header); |
366 | /* Build header */ | 575 | if(err) |
367 | memset(&tar->header, 0, sizeof(tar->header)); | 576 | return err; |
368 | /* Ensure name fits within header */ | 577 | |
369 | if(strlen(name) > sizeof(tar->header.name)) | 578 | int len = twrite(tar, tar->buffer, HEADER_LEN); |
370 | return MTAR_EOVERFLOW; | 579 | if(len < 0) |
371 | strncpy(tar->header.name, name, sizeof(tar->header.name)); | 580 | return len; |
372 | tar->header.type = MTAR_TDIR; | 581 | if(len != HEADER_LEN) |
373 | tar->header.mode = 0775; | 582 | return MTAR_EWRITEFAIL; |
374 | /* Write header */ | 583 | |
375 | return mtar_write_header(tar, &tar->header); | 584 | return tseek(tar, old_pos); |
376 | } | 585 | } |
377 | 586 | ||
587 | int mtar_write_file_header(mtar_t* tar, const char* name, unsigned size) | ||
588 | { | ||
589 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
590 | if(tar->access != MTAR_WRITE) | ||
591 | return MTAR_EACCESS; | ||
592 | if(((tar->state & S_WROTE_DATA) && !(tar->state & S_WROTE_DATA_EOF)) || | ||
593 | (tar->state & S_WROTE_FINALIZE)) | ||
594 | return MTAR_EAPI; | ||
595 | #endif | ||
378 | 596 | ||
379 | int mtar_write_data(mtar_t *tar, const void *data, unsigned size) { | 597 | size_t namelen = strlen(name); |
380 | int err; | 598 | if(namelen > NAME_LEN) |
599 | return MTAR_ENAMETOOLONG; | ||
381 | 600 | ||
382 | /* Ensure we are writing the correct amount of data */ | 601 | tar->header.mode = 0644; |
383 | if(size > tar->remaining_data) | 602 | tar->header.owner = 0; |
384 | return MTAR_EOVERFLOW; | 603 | tar->header.group = 0; |
604 | tar->header.size = size; | ||
605 | tar->header.mtime = 0; | ||
606 | tar->header.type = MTAR_TREG; | ||
607 | memcpy(tar->header.name, name, namelen + 1); | ||
608 | tar->header.linkname[0] = '\0'; | ||
609 | |||
610 | return mtar_write_header(tar, &tar->header); | ||
611 | } | ||
612 | |||
613 | int mtar_write_dir_header(mtar_t* tar, const char* name) | ||
614 | { | ||
615 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
616 | if(tar->access != MTAR_WRITE) | ||
617 | return MTAR_EACCESS; | ||
618 | if(((tar->state & S_WROTE_DATA) && !(tar->state & S_WROTE_DATA_EOF)) || | ||
619 | (tar->state & S_WROTE_FINALIZE)) | ||
620 | return MTAR_EAPI; | ||
621 | #endif | ||
622 | |||
623 | size_t namelen = strlen(name); | ||
624 | if(namelen > NAME_LEN) | ||
625 | return MTAR_ENAMETOOLONG; | ||
626 | |||
627 | tar->header.mode = 0755; | ||
628 | tar->header.owner = 0; | ||
629 | tar->header.group = 0; | ||
630 | tar->header.size = 0; | ||
631 | tar->header.mtime = 0; | ||
632 | tar->header.type = MTAR_TDIR; | ||
633 | memcpy(tar->header.name, name, namelen + 1); | ||
634 | tar->header.linkname[0] = '\0'; | ||
635 | |||
636 | return mtar_write_header(tar, &tar->header); | ||
637 | } | ||
638 | |||
639 | int mtar_write_data(mtar_t* tar, const void* ptr, unsigned size) | ||
640 | { | ||
641 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
642 | if(tar->access != MTAR_WRITE) | ||
643 | return MTAR_EACCESS; | ||
644 | if(!(tar->state & S_WROTE_HEADER) || | ||
645 | (tar->state & S_WROTE_DATA_EOF) || | ||
646 | (tar->state & S_WROTE_FINALIZE)) | ||
647 | return MTAR_EAPI; | ||
648 | #endif | ||
649 | |||
650 | tar->state |= S_WROTE_DATA; | ||
651 | |||
652 | int err = twrite(tar, ptr, size); | ||
653 | if(tar->pos > tar->end_pos) | ||
654 | tar->end_pos = tar->pos; | ||
385 | 655 | ||
386 | /* Write data */ | ||
387 | err = twrite(tar, data, size); | ||
388 | if (err) { | ||
389 | return err; | 656 | return err; |
390 | } | ||
391 | tar->remaining_data -= size; | ||
392 | /* Write padding if we've written all the data for this file */ | ||
393 | if (tar->remaining_data == 0) { | ||
394 | return write_null_bytes(tar, round_up(tar->pos, 512) - tar->pos); | ||
395 | } | ||
396 | return MTAR_ESUCCESS; | ||
397 | } | 657 | } |
398 | 658 | ||
659 | int mtar_update_file_size(mtar_t* tar) | ||
660 | { | ||
661 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
662 | if(tar->access != MTAR_WRITE) | ||
663 | return MTAR_EACCESS; | ||
664 | if(!(tar->state & S_WROTE_HEADER) || | ||
665 | (tar->state & S_WROTE_DATA_EOF) || | ||
666 | (tar->state & S_WROTE_FINALIZE)) | ||
667 | return MTAR_EAPI; | ||
668 | #endif | ||
669 | |||
670 | unsigned new_size = data_end_pos(tar) - data_beg_pos(tar); | ||
671 | if(new_size == tar->header.size) | ||
672 | return MTAR_ESUCCESS; | ||
673 | else { | ||
674 | tar->header.size = new_size; | ||
675 | return mtar_update_header(tar, &tar->header); | ||
676 | } | ||
677 | } | ||
678 | |||
679 | int mtar_end_data(mtar_t* tar) | ||
680 | { | ||
681 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
682 | if(tar->access != MTAR_WRITE) | ||
683 | return MTAR_EACCESS; | ||
684 | if(!(tar->state & S_WROTE_HEADER) || | ||
685 | (tar->state & S_WROTE_DATA_EOF) || | ||
686 | (tar->state & S_WROTE_FINALIZE)) | ||
687 | return MTAR_EAPI; | ||
688 | #endif | ||
689 | |||
690 | int err; | ||
691 | |||
692 | /* ensure the caller wrote the correct amount of data */ | ||
693 | unsigned expected_end = data_beg_pos(tar) + tar->header.size; | ||
694 | if(tar->end_pos != expected_end) | ||
695 | return MTAR_EWRONGSIZE; | ||
696 | |||
697 | /* ensure we're positioned at the end of the stream */ | ||
698 | if(tar->pos != tar->end_pos) { | ||
699 | err = tseek(tar, tar->end_pos); | ||
700 | if(err) | ||
701 | return err; | ||
702 | } | ||
703 | |||
704 | /* write remainder of the 512-byte record */ | ||
705 | err = write_null_bytes(tar, round_up_512(tar->pos) - tar->pos); | ||
706 | if(err) | ||
707 | return err; | ||
708 | |||
709 | tar->state |= S_WROTE_DATA_EOF; | ||
710 | return MTAR_ESUCCESS; | ||
711 | } | ||
712 | |||
713 | int mtar_finalize(mtar_t* tar) | ||
714 | { | ||
715 | #ifndef MICROTAR_DISABLE_API_CHECKS | ||
716 | if(tar->access != MTAR_WRITE) | ||
717 | return MTAR_EACCESS; | ||
718 | if(((tar->state & S_WROTE_DATA) && !(tar->state & S_WROTE_DATA_EOF)) || | ||
719 | (tar->state & S_WROTE_FINALIZE)) | ||
720 | return MTAR_EAPI; | ||
721 | #endif | ||
399 | 722 | ||
400 | int mtar_finalize(mtar_t *tar) { | 723 | tar->state |= S_WROTE_FINALIZE; |
401 | /* Write two NULL records */ | 724 | return write_null_bytes(tar, 1024); |
402 | return write_null_bytes(tar, sizeof(mtar_raw_header_t) * 2); | ||
403 | } | 725 | } |