summaryrefslogtreecommitdiff
path: root/lib/microtar/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/microtar/src')
-rw-r--r--lib/microtar/src/microtar-rockbox.c115
-rw-r--r--lib/microtar/src/microtar-rockbox.h29
-rw-r--r--lib/microtar/src/microtar-stdio.c113
-rw-r--r--lib/microtar/src/microtar-stdio.h39
-rw-r--r--lib/microtar/src/microtar.c886
-rw-r--r--lib/microtar/src/microtar.h193
6 files changed, 903 insertions, 472 deletions
diff --git a/lib/microtar/src/microtar-rockbox.c b/lib/microtar/src/microtar-rockbox.c
index 04ba93cc42..d0f579fefb 100644
--- a/lib/microtar/src/microtar-rockbox.c
+++ b/lib/microtar/src/microtar-rockbox.c
@@ -20,81 +20,72 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#include "file.h" 22#include "file.h"
23#include "microtar.h" 23#include "microtar-rockbox.h"
24#include <stdint.h> 24#include <stdint.h>
25 25
26static int fd_write(mtar_t *tar, const void *data, unsigned size) { 26static int fd_read(void* stream, void* data, unsigned size)
27 intptr_t fd = (intptr_t)tar->stream; 27{
28 ssize_t res = write(fd, data, size); 28 ssize_t res = read((intptr_t)stream, data, size);
29 if(res < 0 || ((unsigned)res != size)) 29 if(res < 0)
30 return MTAR_EWRITEFAIL; 30 return MTAR_EREADFAIL;
31 else
32 return MTAR_ESUCCESS;
33}
34 31
35static int fd_read(mtar_t *tar, void *data, unsigned size) { 32 return res;
36 intptr_t fd = (intptr_t)tar->stream;
37 ssize_t res = read(fd, data, size);
38 if(res < 0 || ((unsigned)res != size))
39 return MTAR_EREADFAIL;
40 else
41 return MTAR_ESUCCESS;
42} 33}
43 34
44static int fd_seek(mtar_t *tar, unsigned offset) { 35static int fd_write(void* stream, const void* data, unsigned size)
45 intptr_t fd = (intptr_t)tar->stream; 36{
46 off_t res = lseek(fd, offset, SEEK_SET); 37 ssize_t res = write((intptr_t)stream, data, size);
47 if(res < 0 || ((unsigned)res != offset)) 38 if(res < 0)
48 return MTAR_ESEEKFAIL; 39 return MTAR_EWRITEFAIL;
49 else
50 return MTAR_ESUCCESS;
51}
52 40
53static int fd_close(mtar_t *tar) { 41 return res;
54 intptr_t fd = (intptr_t)tar->stream;
55 close(fd);
56 return MTAR_ESUCCESS;
57} 42}
58 43
44static int fd_seek(void* stream, unsigned offset)
45{
46 off_t res = lseek((intptr_t)stream, offset, SEEK_SET);
47 if(res < 0 || ((unsigned)res != offset))
48 return MTAR_ESEEKFAIL;
49 else
50 return MTAR_ESUCCESS;
51}
59 52
60int mtar_open(mtar_t *tar, const char *filename, const char *mode) { 53static int fd_close(void* stream)
61 int err; 54{
62 int openmode; 55 close((intptr_t)stream);
63 int fd; 56 return MTAR_ESUCCESS;
64 57}
65 /* Init tar struct and functions */
66 memset(tar, 0, sizeof(*tar));
67 tar->write = fd_write;
68 tar->read = fd_read;
69 tar->seek = fd_seek;
70 tar->close = fd_close;
71 58
72 /* Get correct mode flags */ 59static const mtar_ops_t fd_ops = {
73 if ( strchr(mode, 'r') ) 60 .read = fd_read,
74 openmode = O_RDONLY; 61 .write = fd_write,
75 else if ( strchr(mode, 'w') ) 62 .seek = fd_seek,
76 openmode = O_CREAT|O_TRUNC|O_WRONLY; 63 .close = fd_close,
77 else if ( strchr(mode, 'a') ) 64};
78 openmode = O_WRONLY|O_APPEND;
79 else
80 return MTAR_EFAILURE;
81 65
82 /* Open file */ 66int mtar_open(mtar_t* tar, const char* filename, int flags)
83 fd = open(filename, openmode); 67{
84 if(fd < 0) 68 /* Determine access mode */
85 return MTAR_EOPENFAIL; 69 int access;
70 switch(flags & O_ACCMODE) {
71 case O_RDONLY:
72 access = MTAR_READ;
73 break;
86 74
87 tar->stream = (void*)(intptr_t)fd; 75 case O_WRONLY:
76 access = MTAR_WRITE;
77 break;
88 78
89 /* Read first header to check it is valid if mode is `r` */ 79 default:
90 if ( openmode & O_RDONLY ) { 80 /* Note - O_RDWR isn't very useful so we don't allow it */
91 err = mtar_read_header(tar, &tar->header); 81 return MTAR_EAPI;
92 if (err != MTAR_ESUCCESS) {
93 mtar_close(tar);
94 return err;
95 } 82 }
96 }
97 83
98 /* Return ok */ 84 int fd = open(filename, flags);
99 return MTAR_ESUCCESS; 85 if(fd < 0)
86 return MTAR_EOPENFAIL;
87
88 /* Init tar struct and functions */
89 mtar_init(tar, access, &fd_ops, (void*)(intptr_t)fd);
90 return MTAR_ESUCCESS;
100} 91}
diff --git a/lib/microtar/src/microtar-rockbox.h b/lib/microtar/src/microtar-rockbox.h
new file mode 100644
index 0000000000..36acbb2bfe
--- /dev/null
+++ b/lib/microtar/src/microtar-rockbox.h
@@ -0,0 +1,29 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef MICROTAR_ROCKBOX_H
23#define MICROTAR_ROCKBOX_H
24
25#include "microtar.h"
26
27int mtar_open(mtar_t* tar, const char* filename, int flags);
28
29#endif
diff --git a/lib/microtar/src/microtar-stdio.c b/lib/microtar/src/microtar-stdio.c
index 215000aa98..2697d005cd 100644
--- a/lib/microtar/src/microtar-stdio.c
+++ b/lib/microtar/src/microtar-stdio.c
@@ -1,65 +1,88 @@
1/** 1/*
2 * Copyright (c) 2017 rxi 2 * Copyright (c) 2017 rxi
3 * Copyright (c) 2021 Aidan MacDonald
3 * 4 *
4 * This library is free software; you can redistribute it and/or modify it 5 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * under the terms of the MIT license. See `microtar.c` for details. 6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
6 */ 22 */
7 23
24#include "microtar-stdio.h"
8#include <stdio.h> 25#include <stdio.h>
9#include <string.h> 26#include <string.h>
10 27
11#include "microtar.h" 28static int file_read(void* stream, void* data, unsigned size)
29{
30 unsigned res = fread(data, 1, size, (FILE*)stream);
31 if(res != size && ferror((FILE*)stream))
32 return MTAR_EREADFAIL;
12 33
13static int file_write(mtar_t *tar, const void *data, unsigned size) { 34 return res;
14 unsigned res = fwrite(data, 1, size, tar->stream);
15 return (res == size) ? MTAR_ESUCCESS : MTAR_EWRITEFAIL;
16} 35}
17 36
18static int file_read(mtar_t *tar, void *data, unsigned size) { 37static int file_write(void* stream, const void* data, unsigned size)
19 unsigned res = fread(data, 1, size, tar->stream); 38{
20 return (res == size) ? MTAR_ESUCCESS : MTAR_EREADFAIL; 39 unsigned res = fwrite(data, 1, size, (FILE*)stream);
21} 40 if(res != size && ferror((FILE*)stream))
41 return MTAR_EWRITEFAIL;
22 42
23static int file_seek(mtar_t *tar, unsigned offset) { 43 return res;
24 int res = fseek(tar->stream, offset, SEEK_SET);
25 return (res == 0) ? MTAR_ESUCCESS : MTAR_ESEEKFAIL;
26} 44}
27 45
28static int file_close(mtar_t *tar) { 46static int file_seek(void* stream, unsigned offset)
29 fclose(tar->stream); 47{
30 return MTAR_ESUCCESS; 48 int res = fseek((FILE*)stream, offset, SEEK_SET);
49 return (res == 0) ? MTAR_ESUCCESS : MTAR_ESEEKFAIL;
31} 50}
32 51
52static int file_close(void* stream)
53{
54 int err = fclose((FILE*)stream);
55 return (err == 0 ? MTAR_ESUCCESS : MTAR_EFAILURE);
56}
33 57
34int mtar_open(mtar_t *tar, const char *filename, const char *mode) { 58static const mtar_ops_t file_ops = {
35 int err; 59 .read = file_read,
36 mtar_header_t h; 60 .write = file_write,
37 61 .seek = file_seek,
38 /* Init tar struct and functions */ 62 .close = file_close,
39 memset(tar, 0, sizeof(*tar)); 63};
40 tar->write = file_write;
41 tar->read = file_read;
42 tar->seek = file_seek;
43 tar->close = file_close;
44 64
45 /* Assure mode is always binary */ 65int mtar_open(mtar_t* tar, const char* filename, const char* mode)
46 if ( strchr(mode, 'r') ) mode = "rb"; 66{
47 if ( strchr(mode, 'w') ) mode = "wb"; 67 /* Determine access mode */
48 if ( strchr(mode, 'a') ) mode = "ab"; 68 int access;
49 /* Open file */ 69 char* read = strchr(mode, 'r');
50 tar->stream = fopen(filename, mode); 70 char* write = strchr(mode, 'w');
51 if (!tar->stream) { 71 if(read) {
52 return MTAR_EOPENFAIL; 72 if(write)
53 } 73 return MTAR_EAPI;
54 /* Read first header to check it is valid if mode is `r` */ 74 access = MTAR_READ;
55 if (*mode == 'r') { 75 } else if(write) {
56 err = mtar_read_header(tar, &h); 76 access = MTAR_WRITE;
57 if (err != MTAR_ESUCCESS) { 77 } else {
58 mtar_close(tar); 78 return MTAR_EAPI;
59 return err;
60 } 79 }
61 }
62 80
63 /* Return ok */ 81 /* Open file */
64 return MTAR_ESUCCESS; 82 FILE* file = fopen(filename, mode);
83 if(!file)
84 return MTAR_EOPENFAIL;
85
86 mtar_init(tar, access, &file_ops, file);
87 return MTAR_ESUCCESS;
65} 88}
diff --git a/lib/microtar/src/microtar-stdio.h b/lib/microtar/src/microtar-stdio.h
new file mode 100644
index 0000000000..ad46af0390
--- /dev/null
+++ b/lib/microtar/src/microtar-stdio.h
@@ -0,0 +1,39 @@
1/*
2 * Copyright (c) 2017 rxi
3 * Copyright (c) 2021 Aidan MacDonald
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#ifndef MICROTAR_STDIO_H
25#define MICROTAR_STDIO_H
26
27#include "microtar.h"
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33int mtar_open(mtar_t* tar, const char* filename, const char* mode);
34
35#ifdef __cplusplus
36}
37#endif
38
39#endif
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 */
32static char* strncpy(char* dest, const char* src, size_t n) { 30#define strncpy my_strncpy
31static 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
44enum {
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
52enum {
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
66static 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
46static 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
91static 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
69static 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; 116static unsigned round_up_512(unsigned n)
117{
118 return (n + 511u) & ~511u;
91} 119}
92 120
121static 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
94static unsigned round_up(unsigned n, unsigned incr) { 127 return ret;
95 return n + (incr - n % incr) % incr;
96} 128}
97 129
130static 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
99static 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 139static int tseek(mtar_t* tar, unsigned pos)
113static 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
146static 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
120static 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
166static unsigned checksum(const char* raw)
167{
168 unsigned i;
169 unsigned char* p = (unsigned char*)raw;
170 unsigned res = 256;
126 171
127static 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
180static 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
140static int raw_to_header(mtar_header_t *h, const mtar_raw_header_t *rh) { 220static 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)); 267static 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; 272static unsigned data_end_pos(const mtar_t* tar)
273{
274 return tar->end_pos;
178} 275}
179 276
277static int ensure_header(mtar_t* tar)
278{
279 int ret, err;
180 280
181static 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
211const 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
308const 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
228int mtar_close(mtar_t *tar) { 330void 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
338int 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
233int mtar_seek(mtar_t *tar, unsigned pos) { 346int 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
351mtar_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
240int mtar_rewind(mtar_t *tar) { 359int 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
364int 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
247int 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
376int 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
260int 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 } 395int 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;
288int 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); 416static 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
423int 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
434int 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
307int 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; 454int 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; 495unsigned 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
505int 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
342int 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
514int 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
350int 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
551int 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
365int 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
587int 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
379int 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
613int 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
639int 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
659int 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
679int 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
713int 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
400int 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}
diff --git a/lib/microtar/src/microtar.h b/lib/microtar/src/microtar.h
index d30c829521..7a9be2ac92 100644
--- a/lib/microtar/src/microtar.h
+++ b/lib/microtar/src/microtar.h
@@ -1,105 +1,132 @@
1/** 1/*
2 * Copyright (c) 2017 rxi 2 * Copyright (c) 2017 rxi
3 * Copyright (c) 2021 Aidan MacDonald
3 * 4 *
4 * This library is free software; you can redistribute it and/or modify it 5 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * under the terms of the MIT license. See `microtar.c` for details. 6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
6 */ 22 */
7 23
8#ifndef MICROTAR_H 24#ifndef MICROTAR_H
9#define MICROTAR_H 25#define MICROTAR_H
10 26
11#ifdef __cplusplus 27#ifdef __cplusplus
12extern "C" 28extern "C" {
13{
14#endif 29#endif
15 30
16#include <stdio.h> 31#include <stdio.h> /* SEEK_SET et al. */
17#include <stdlib.h> 32
18 33enum mtar_error {
19#define MTAR_VERSION "0.1.0" 34 MTAR_ESUCCESS = 0,
20 35 MTAR_EFAILURE = -1,
21enum { 36 MTAR_EOPENFAIL = -2,
22 MTAR_ESUCCESS = 0, 37 MTAR_EREADFAIL = -3,
23 MTAR_EFAILURE = -1, 38 MTAR_EWRITEFAIL = -4,
24 MTAR_EOPENFAIL = -2, 39 MTAR_ESEEKFAIL = -5,
25 MTAR_EREADFAIL = -3, 40 MTAR_ESEEKRANGE = -6,
26 MTAR_EWRITEFAIL = -4, 41 MTAR_EBADCHKSUM = -7,
27 MTAR_ESEEKFAIL = -5, 42 MTAR_ENULLRECORD = -8,
28 MTAR_EBADCHKSUM = -6, 43 MTAR_ENOTFOUND = -9,
29 MTAR_ENULLRECORD = -7, 44 MTAR_EOVERFLOW = -10,
30 MTAR_ENOTFOUND = -8, 45 MTAR_EAPI = -11,
31 MTAR_EOVERFLOW = -9, 46 MTAR_ENAMETOOLONG = -12,
47 MTAR_EWRONGSIZE = -13,
48 MTAR_EACCESS = -14,
49};
50
51enum mtar_type {
52 MTAR_TREG = '0',
53 MTAR_TLNK = '1',
54 MTAR_TSYM = '2',
55 MTAR_TCHR = '3',
56 MTAR_TBLK = '4',
57 MTAR_TDIR = '5',
58 MTAR_TFIFO = '6',
32}; 59};
33 60
34enum { 61enum mtar_access {
35 MTAR_TREG = '0', 62 MTAR_READ,
36 MTAR_TLNK = '1', 63 MTAR_WRITE,
37 MTAR_TSYM = '2',
38 MTAR_TCHR = '3',
39 MTAR_TBLK = '4',
40 MTAR_TDIR = '5',
41 MTAR_TFIFO = '6'
42}; 64};
43 65
44typedef struct { 66typedef struct mtar_header mtar_header_t;
45 unsigned mode; 67typedef struct mtar mtar_t;
46 unsigned owner; 68typedef struct mtar_ops mtar_ops_t;
47 unsigned group; 69
48 unsigned size; 70typedef int(*mtar_foreach_cb)(mtar_t*, const mtar_header_t*, void*);
49 unsigned mtime; 71
50 unsigned type; 72struct mtar_header {
51 char name[101]; /* +1 byte in order to ensure null termination */ 73 unsigned mode;
52 char linkname[101]; 74 unsigned owner;
53} mtar_header_t; 75 unsigned group;
54 76 unsigned size;
55 77 unsigned mtime;
56typedef struct { 78 unsigned type;
57 char name[100]; 79 char name[101]; /* +1 byte in order to ensure null termination */
58 char mode[8]; 80 char linkname[101];
59 char owner[8];
60 char group[8];
61 char size[12];
62 char mtime[12];
63 char checksum[8];
64 char type;
65 char linkname[100];
66 char _padding[255];
67} mtar_raw_header_t;
68
69
70typedef struct mtar_t mtar_t;
71
72struct mtar_t {
73 int (*read)(mtar_t *tar, void *data, unsigned size);
74 int (*write)(mtar_t *tar, const void *data, unsigned size);
75 int (*seek)(mtar_t *tar, unsigned pos);
76 int (*close)(mtar_t *tar);
77 void *stream;
78 unsigned pos;
79 unsigned remaining_data;
80 unsigned last_header;
81 mtar_header_t header;
82 mtar_raw_header_t raw_header;
83}; 81};
84 82
83struct mtar_ops {
84 int(*read)(void* stream, void* data, unsigned size);
85 int(*write)(void* stream, const void* data, unsigned size);
86 int(*seek)(void* stream, unsigned pos);
87 int(*close)(void* stream);
88};
89
90struct mtar {
91 char buffer[512]; /* IO buffer, put first to allow library users to
92 * control its alignment */
93 int state; /* Used to simplify the API and verify API usage */
94 int access; /* Access mode */
95 unsigned pos; /* Current position in file */
96 unsigned end_pos; /* End position of the current file */
97 unsigned header_pos; /* Position of the current header */
98 mtar_header_t header; /* Most recently parsed header */
99 const mtar_ops_t* ops; /* Stream operations */
100 void* stream; /* Stream handle */
101};
85 102
86const char* mtar_strerror(int err); 103const char* mtar_strerror(int err);
87 104
88int mtar_open(mtar_t *tar, const char *filename, const char *mode); 105void mtar_init(mtar_t* tar, int access, const mtar_ops_t* ops, void* stream);
89int mtar_close(mtar_t *tar); 106int mtar_close(mtar_t* tar);
90 107int mtar_is_open(mtar_t* tar);
91int mtar_seek(mtar_t *tar, unsigned pos); 108
92int mtar_rewind(mtar_t *tar); 109mtar_header_t* mtar_get_header(mtar_t* tar);
93int mtar_next(mtar_t *tar); 110int mtar_access_mode(const mtar_t* tar);
94int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h); 111
95int mtar_read_header(mtar_t *tar, mtar_header_t *h); 112int mtar_rewind(mtar_t* tar);
96int mtar_read_data(mtar_t *tar, void *ptr, unsigned size); 113int mtar_next(mtar_t* tar);
97 114int mtar_foreach(mtar_t* tar, mtar_foreach_cb cb, void* arg);
98int mtar_write_header(mtar_t *tar, const mtar_header_t *h); 115int mtar_find(mtar_t* tar, const char* name);
99int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size); 116
100int mtar_write_dir_header(mtar_t *tar, const char *name); 117int mtar_read_data(mtar_t* tar, void* ptr, unsigned size);
101int mtar_write_data(mtar_t *tar, const void *data, unsigned size); 118int mtar_seek_data(mtar_t* tar, int offset, int whence);
102int mtar_finalize(mtar_t *tar); 119unsigned mtar_tell_data(mtar_t* tar);
120int mtar_eof_data(mtar_t* tar);
121
122int mtar_write_header(mtar_t* tar, const mtar_header_t* h);
123int mtar_update_header(mtar_t* tar, const mtar_header_t* h);
124int mtar_write_file_header(mtar_t* tar, const char* name, unsigned size);
125int mtar_write_dir_header(mtar_t* tar, const char* name);
126int mtar_write_data(mtar_t* tar, const void* ptr, unsigned size);
127int mtar_update_file_size(mtar_t* tar);
128int mtar_end_data(mtar_t* tar);
129int mtar_finalize(mtar_t* tar);
103 130
104#ifdef __cplusplus 131#ifdef __cplusplus
105} 132}