diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-26 14:37:36 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-27 13:13:35 +0000 |
commit | c1709e3194855b93ffd6f68eaa750c6704b3e52f (patch) | |
tree | 485af751c5a6ee3cd85baf6be117099fbbf1f3ec /lib/microtar/src | |
parent | 4052a9ddcfedcb6bc55f94e4ec5bc8e803e2dc7e (diff) | |
download | rockbox-c1709e3194855b93ffd6f68eaa750c6704b3e52f.tar.gz rockbox-c1709e3194855b93ffd6f68eaa750c6704b3e52f.zip |
microtar: Update to latest upstream commit
This is a major overhaul of the library with some API
changes and ease of use improvements, particularly for
creating new archives.
The updated functionality is intended to support a new
X1000 installer framework.
Change-Id: Icf192bb546831231d49303fbf529ef1c1ce8905c
Diffstat (limited to 'lib/microtar/src')
-rw-r--r-- | lib/microtar/src/microtar-rockbox.c | 115 | ||||
-rw-r--r-- | lib/microtar/src/microtar-rockbox.h | 29 | ||||
-rw-r--r-- | lib/microtar/src/microtar-stdio.c | 113 | ||||
-rw-r--r-- | lib/microtar/src/microtar-stdio.h | 39 | ||||
-rw-r--r-- | lib/microtar/src/microtar.c | 886 | ||||
-rw-r--r-- | lib/microtar/src/microtar.h | 193 |
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 | ||
26 | static int fd_write(mtar_t *tar, const void *data, unsigned size) { | 26 | static 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 | ||
35 | static 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 | ||
44 | static int fd_seek(mtar_t *tar, unsigned offset) { | 35 | static 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 | ||
53 | static 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 | ||
44 | static 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 | ||
60 | int mtar_open(mtar_t *tar, const char *filename, const char *mode) { | 53 | static 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 */ | 59 | static 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 */ | 66 | int 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 | |||
27 | int 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" | 28 | static 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 | ||
13 | static 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 | ||
18 | static int file_read(mtar_t *tar, void *data, unsigned size) { | 37 | static 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 | ||
23 | static 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 | ||
28 | static int file_close(mtar_t *tar) { | 46 | static 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 | ||
52 | static int file_close(void* stream) | ||
53 | { | ||
54 | int err = fclose((FILE*)stream); | ||
55 | return (err == 0 ? MTAR_ESUCCESS : MTAR_EFAILURE); | ||
56 | } | ||
33 | 57 | ||
34 | int mtar_open(mtar_t *tar, const char *filename, const char *mode) { | 58 | static 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 */ | 65 | int 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 | ||
30 | extern "C" { | ||
31 | #endif | ||
32 | |||
33 | int 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 */ |
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 | } |
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 |
12 | extern "C" | 28 | extern "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 | 33 | enum mtar_error { | |
19 | #define MTAR_VERSION "0.1.0" | 34 | MTAR_ESUCCESS = 0, |
20 | 35 | MTAR_EFAILURE = -1, | |
21 | enum { | 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 | |||
51 | enum 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 | ||
34 | enum { | 61 | enum 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 | ||
44 | typedef struct { | 66 | typedef struct mtar_header mtar_header_t; |
45 | unsigned mode; | 67 | typedef struct mtar mtar_t; |
46 | unsigned owner; | 68 | typedef struct mtar_ops mtar_ops_t; |
47 | unsigned group; | 69 | |
48 | unsigned size; | 70 | typedef int(*mtar_foreach_cb)(mtar_t*, const mtar_header_t*, void*); |
49 | unsigned mtime; | 71 | |
50 | unsigned type; | 72 | struct 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; | |
56 | typedef 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 | |||
70 | typedef struct mtar_t mtar_t; | ||
71 | |||
72 | struct 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 | ||
83 | struct 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 | |||
90 | struct 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 | ||
86 | const char* mtar_strerror(int err); | 103 | const char* mtar_strerror(int err); |
87 | 104 | ||
88 | int mtar_open(mtar_t *tar, const char *filename, const char *mode); | 105 | void mtar_init(mtar_t* tar, int access, const mtar_ops_t* ops, void* stream); |
89 | int mtar_close(mtar_t *tar); | 106 | int mtar_close(mtar_t* tar); |
90 | 107 | int mtar_is_open(mtar_t* tar); | |
91 | int mtar_seek(mtar_t *tar, unsigned pos); | 108 | |
92 | int mtar_rewind(mtar_t *tar); | 109 | mtar_header_t* mtar_get_header(mtar_t* tar); |
93 | int mtar_next(mtar_t *tar); | 110 | int mtar_access_mode(const mtar_t* tar); |
94 | int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h); | 111 | |
95 | int mtar_read_header(mtar_t *tar, mtar_header_t *h); | 112 | int mtar_rewind(mtar_t* tar); |
96 | int mtar_read_data(mtar_t *tar, void *ptr, unsigned size); | 113 | int mtar_next(mtar_t* tar); |
97 | 114 | int mtar_foreach(mtar_t* tar, mtar_foreach_cb cb, void* arg); | |
98 | int mtar_write_header(mtar_t *tar, const mtar_header_t *h); | 115 | int mtar_find(mtar_t* tar, const char* name); |
99 | int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size); | 116 | |
100 | int mtar_write_dir_header(mtar_t *tar, const char *name); | 117 | int mtar_read_data(mtar_t* tar, void* ptr, unsigned size); |
101 | int mtar_write_data(mtar_t *tar, const void *data, unsigned size); | 118 | int mtar_seek_data(mtar_t* tar, int offset, int whence); |
102 | int mtar_finalize(mtar_t *tar); | 119 | unsigned mtar_tell_data(mtar_t* tar); |
120 | int mtar_eof_data(mtar_t* tar); | ||
121 | |||
122 | int mtar_write_header(mtar_t* tar, const mtar_header_t* h); | ||
123 | int mtar_update_header(mtar_t* tar, const mtar_header_t* h); | ||
124 | int mtar_write_file_header(mtar_t* tar, const char* name, unsigned size); | ||
125 | int mtar_write_dir_header(mtar_t* tar, const char* name); | ||
126 | int mtar_write_data(mtar_t* tar, const void* ptr, unsigned size); | ||
127 | int mtar_update_file_size(mtar_t* tar); | ||
128 | int mtar_end_data(mtar_t* tar); | ||
129 | int mtar_finalize(mtar_t* tar); | ||
103 | 130 | ||
104 | #ifdef __cplusplus | 131 | #ifdef __cplusplus |
105 | } | 132 | } |