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 | |
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')
-rw-r--r-- | lib/microtar/.gitignore | 3 | ||||
-rw-r--r-- | lib/microtar/LICENSE | 1 | ||||
-rw-r--r-- | lib/microtar/Makefile | 25 | ||||
-rw-r--r-- | lib/microtar/README.md | 312 | ||||
-rw-r--r-- | lib/microtar/README.rockbox | 14 | ||||
-rw-r--r-- | lib/microtar/mtar.c | 243 | ||||
-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 |
12 files changed, 1415 insertions, 558 deletions
diff --git a/lib/microtar/.gitignore b/lib/microtar/.gitignore new file mode 100644 index 0000000000..cccd994a85 --- /dev/null +++ b/lib/microtar/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | *.o | ||
2 | *.a | ||
3 | /mtar | ||
diff --git a/lib/microtar/LICENSE b/lib/microtar/LICENSE index 7e3bf17ccb..72cf80d63b 100644 --- a/lib/microtar/LICENSE +++ b/lib/microtar/LICENSE | |||
@@ -1,4 +1,5 @@ | |||
1 | Copyright (c) 2017 rxi | 1 | Copyright (c) 2017 rxi |
2 | Copyright (c) 2021 Aidan MacDonald | ||
2 | 3 | ||
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of |
4 | this software and associated documentation files (the "Software"), to deal in | 5 | this software and associated documentation files (the "Software"), to deal in |
diff --git a/lib/microtar/Makefile b/lib/microtar/Makefile new file mode 100644 index 0000000000..ae2c4f6d6e --- /dev/null +++ b/lib/microtar/Makefile | |||
@@ -0,0 +1,25 @@ | |||
1 | CPPFLAGS = -Isrc | ||
2 | CFLAGS = -std=c99 -Wall -Wextra -O2 | ||
3 | |||
4 | MTAR_OBJ = mtar.o | ||
5 | MTAR_BIN = mtar | ||
6 | |||
7 | MICROTAR_OBJ = src/microtar.o src/microtar-stdio.o | ||
8 | MICROTAR_LIB = libmicrotar.a | ||
9 | |||
10 | $(MTAR_BIN): $(MTAR_OBJ) $(MICROTAR_LIB) | ||
11 | $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ | ||
12 | |||
13 | $(MICROTAR_LIB): $(MICROTAR_OBJ) | ||
14 | $(AR) cr $@ $^ | ||
15 | |||
16 | %.o: %.c | ||
17 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ | ||
18 | |||
19 | src/microtar.o: src/microtar.h | ||
20 | src/microtar-stdio.o: src/microtar.h src/microtar-stdio.h | ||
21 | mtar.o: src/microtar.h src/microtar-stdio.h | ||
22 | |||
23 | clean: | ||
24 | rm -f $(MICROTAR_LIB) $(MICROTAR_OBJ) | ||
25 | rm -f $(MTAR_BIN) $(MTAR_OBJ) | ||
diff --git a/lib/microtar/README.md b/lib/microtar/README.md index 18153caa21..109626cdd4 100644 --- a/lib/microtar/README.md +++ b/lib/microtar/README.md | |||
@@ -1,128 +1,268 @@ | |||
1 | # microtar | 1 | # microtar |
2 | A lightweight tar library written in ANSI C | ||
3 | 2 | ||
3 | A lightweight tar library written in ANSI C. | ||
4 | 4 | ||
5 | ## Modifications from upstream | 5 | This version is a fork of [rxi's microtar](https://github.com/rxi/microtar) |
6 | with bugfixes and API changes aimed at improving usability, but still keeping | ||
7 | with the minimal design of the original library. | ||
6 | 8 | ||
7 | [Upstream](https://github.com/rxi/microtar) has numerous bugs and gotchas, | 9 | ## License |
8 | which I fixed in order to improve the overall robustness of the library. | 10 | |
11 | This library is free software; you can redistribute it and/or modify it under | ||
12 | the terms of the MIT license. See [LICENSE](LICENSE) for details. | ||
9 | 13 | ||
10 | A summary of my changes, in no particular order: | ||
11 | 14 | ||
12 | - Fix possible sscanf beyond the bounds of the input buffer | 15 | ## Supported format variants |
13 | - Fix possible buffer overruns due to strcpy on untrusted input | ||
14 | - Fix incorrect octal formatting by sprintf and possible output overrruns | ||
15 | - Catch read/writes which are too big and handle them gracefully | ||
16 | - Handle over-long names in `mtar_write_file_header` / `mtar_write_dir_header` | ||
17 | - Ensure strings in `mtar_header_t` are always null-terminated | ||
18 | - Save and load group information so we don't lose information | ||
19 | - Move `mtar_open()` to `microtar-stdio.c` so `microtar.c` can be used in | ||
20 | a freestanding environment | ||
21 | - Allow control of stack usage by moving temporary variables into `mtar_t`, | ||
22 | so the caller can decide whether to use the stack or heap | ||
23 | 16 | ||
24 | An up-to-date copy of this modified version can be found | 17 | No effort has been put into handling every tar format variant. Basically |
25 | [here](https://github.com/amachronic/microtar). | 18 | what is accepted is the "old-style" format, which appears to work well |
19 | enough to access basic archives created by GNU `tar`. | ||
26 | 20 | ||
27 | 21 | ||
28 | ## Modifications for Rockbox | 22 | ## Basic usage |
29 | 23 | ||
30 | Added file `microtar-rockbox.c` implementing `mtar_open()` with native | 24 | The library consists of two files, `microtar.c` and `microtar.h`, which only |
31 | Rockbox filesystem API. | 25 | depend on a tiny part of the standard C library & can be easily incorporated |
26 | into a host project's build system. | ||
32 | 27 | ||
28 | The core library does not include any I/O hooks as these are supposed to be | ||
29 | provided by the host application. If the C library's `fopen` and friends is | ||
30 | good enough, you can use `microtar-stdio.c`. | ||
33 | 31 | ||
34 | ## Basic Usage | ||
35 | The library consists of `microtar.c` and `microtar.h`. These two files can be | ||
36 | dropped into an existing project and compiled along with it. | ||
37 | 32 | ||
33 | ### Initialization | ||
34 | |||
35 | Initialization is very simple. Everything the library needs is contained in | ||
36 | the `mtar_t` struct; there is no memory allocation and no global state. It is | ||
37 | enough to zero-initialize an `mtar_t` object to put it into a "closed" state. | ||
38 | You can use `mtar_is_open()` to query whether the archive is open or not. | ||
39 | |||
40 | An archive can be opened for reading _or_ writing, but not both. You have to | ||
41 | specify which access mode you're using when you create the archive. | ||
38 | 42 | ||
39 | #### Reading | ||
40 | ```c | 43 | ```c |
41 | mtar_t tar; | 44 | mtar_t tar; |
42 | mtar_header_t h; | 45 | mtar_init(&tar, MTAR_READ, my_io_ops, my_stream); |
43 | char *p; | 46 | ``` |
44 | 47 | ||
45 | /* Open archive for reading */ | 48 | Or if using `microtar-stdio.c`: |
46 | mtar_open(&tar, "test.tar", "r"); | ||
47 | 49 | ||
48 | /* Print all file names and sizes */ | 50 | ```c |
49 | while ( (mtar_read_header(&tar, &h)) != MTAR_ENULLRECORD ) { | 51 | int error = mtar_open(&tar, "file.tar", "rb"); |
50 | printf("%s (%d bytes)\n", h.name, h.size); | 52 | if(error) { |
51 | mtar_next(&tar); | 53 | /* do something about it */ |
52 | } | 54 | } |
55 | ``` | ||
53 | 56 | ||
54 | /* Load and print contents of file "test.txt" */ | 57 | Note that `mtar_init()` is called for you in this case and the access mode is |
55 | mtar_find(&tar, "test.txt", &h); | 58 | deduced from the mode flags. |
56 | p = calloc(1, h.size + 1); | ||
57 | mtar_read_data(&tar, p, h.size); | ||
58 | printf("%s", p); | ||
59 | free(p); | ||
60 | 59 | ||
61 | /* Close archive */ | ||
62 | mtar_close(&tar); | ||
63 | ``` | ||
64 | 60 | ||
65 | #### Writing | 61 | ### Iterating and locating files |
62 | |||
63 | If you opened an archive for reading, you'll likely want to iterate over | ||
64 | all the files. Here's the long way of doing it: | ||
65 | |||
66 | ```c | 66 | ```c |
67 | mtar_t tar; | 67 | mtar_t tar; |
68 | const char *str1 = "Hello world"; | 68 | int err; |
69 | const char *str2 = "Goodbye world"; | 69 | |
70 | /* Go to the start of the archive... Not necessary if you've | ||
71 | * just opened the archive and are already at the beginning. | ||
72 | * (And of course you normally want to check the return value.) */ | ||
73 | mtar_rewind(&tar); | ||
70 | 74 | ||
71 | /* Open archive for writing */ | 75 | /* Iterate over the archive members */ |
72 | mtar_open(&tar, "test.tar", "w"); | 76 | while((err = mtar_next(&tar)) == MTAR_ESUCCESS) { |
77 | /* Get a pointer to the current file header. It will | ||
78 | * remain valid until you move to another record with | ||
79 | * mtar_next() or call mtar_rewind() */ | ||
80 | const mtar_header_t* header = mtar_get_header(&tar); | ||
73 | 81 | ||
74 | /* Write strings to files `test1.txt` and `test2.txt` */ | 82 | printf("%s (%d bytes)\n", header->name, header->size); |
75 | mtar_write_file_header(&tar, "test1.txt", strlen(str1)); | 83 | } |
76 | mtar_write_data(&tar, str1, strlen(str1)); | 84 | |
77 | mtar_write_file_header(&tar, "test2.txt", strlen(str2)); | 85 | if(err != MTAR_ENULLRECORD) { |
78 | mtar_write_data(&tar, str2, strlen(str2)); | 86 | /* ENULLRECORD means we hit end of file; any |
87 | * other return value is an actual error. */ | ||
88 | } | ||
89 | ``` | ||
79 | 90 | ||
80 | /* Finalize -- this needs to be the last thing done before closing */ | 91 | There's a useful shortcut for this type of iteration which removes the |
81 | mtar_finalize(&tar); | 92 | loop boilerplate, replacing it with another kind of boilerplate that may |
93 | be more palatable in some cases. | ||
94 | |||
95 | ```c | ||
96 | /* Will be called for each archive member visited by mtar_foreach(). | ||
97 | * The member's header is passed in as an argument so you don't need | ||
98 | * to fetch it manually with mtar_get_header(). You can freely read | ||
99 | * data (if present) and seek around. There is no special cleanup | ||
100 | * required and it is not necessary to read to the end of the stream. | ||
101 | * | ||
102 | * The callback should return zero (= MTAR_SUCCESS) to continue the | ||
103 | * iteration or return nonzero to abort. On abort, the value returned | ||
104 | * by the callback will be returned from mtar_foreach(). Since it may | ||
105 | * also return normal microtar error codes, it is suggested to use a | ||
106 | * positive value or pass the result via 'arg'. | ||
107 | */ | ||
108 | int foreach_cb(mtar_t* tar, const mtar_header_t* header, void* arg) | ||
109 | { | ||
110 | // ... | ||
111 | return 0; | ||
112 | } | ||
82 | 113 | ||
83 | /* Close archive */ | 114 | void main() |
84 | mtar_close(&tar); | 115 | { |
116 | mtar_t tar; | ||
117 | |||
118 | // ... | ||
119 | |||
120 | int ret = mtar_foreach(&tar, foreach_cb, NULL); | ||
121 | if(ret < 0) { | ||
122 | /* Microtar error codes are negative and may be returned if | ||
123 | * there is a problem with the iteration. */ | ||
124 | } else if(ret == MTAR_ESUCCESS) { | ||
125 | /* If the iteration reaches the end of the archive without | ||
126 | * errors, the return code is MTAR_ESUCCESS. */ | ||
127 | } else if(ret > 0) { | ||
128 | /* Positive values might be returned by the callback to | ||
129 | * signal some condition was met; they'll never be returned | ||
130 | * by microtar */ | ||
131 | } | ||
132 | } | ||
133 | ``` | ||
134 | |||
135 | The other thing you're likely to do is look for a specific file: | ||
136 | |||
137 | ```c | ||
138 | /* Seek to a specific member in the archive */ | ||
139 | int err = mtar_find(&tar, "foo.txt"); | ||
140 | if(err == MTAR_ESUCCESS) { | ||
141 | /* File was found -- read the header with mtar_get_header() */ | ||
142 | } else if(err == MTAR_ENOTFOUND) { | ||
143 | /* File wasn't in the archive */ | ||
144 | } else { | ||
145 | /* Some error occurred */ | ||
146 | } | ||
85 | ``` | 147 | ``` |
86 | 148 | ||
149 | Note this isn't terribly efficient since it scans the entire archive | ||
150 | looking for the file. | ||
151 | |||
152 | |||
153 | ### Reading file data | ||
154 | |||
155 | Once pointed at a file via `mtar_next()` or `mtar_find()` you can read the | ||
156 | data with a simple POSIX-like API. | ||
157 | |||
158 | - `mtar_read_data(tar, buf, count)` reads up to `count` bytes into `buf`, | ||
159 | returning the actual number of bytes read, or a negative error value. | ||
160 | If at EOF, this returns zero. | ||
161 | |||
162 | - `mtar_seek_data(tar, offset, whence)` works exactly like `fseek()` with | ||
163 | `whence` being one of `SEEK_SET`, `SEEK_CUR`, or `SEEK_END` and `offset` | ||
164 | indicating a point relative to the beginning, current position, or end | ||
165 | of the file. Returns zero on success, or a negative error code. | ||
166 | |||
167 | - `mtar_eof_data(tar)` returns nonzero if the end of the file has been | ||
168 | reached. It is possible to seek backward to clear this condition. | ||
169 | |||
170 | |||
171 | ### Writing archives | ||
172 | |||
173 | Microtar has limited support for creating archives. When an archive is opened | ||
174 | for writing, you can add new members using `mtar_write_header()`. | ||
175 | |||
176 | - `mtar_write_header(tar, header)` writes out the header for a new member. | ||
177 | The amount of data that follows is dictated by `header->size`, though if | ||
178 | the underlying stream supports seeking and re-writing data, this size can | ||
179 | be updated later with `mtar_update_header()` or `mtar_update_file_size()`. | ||
180 | |||
181 | - `mtar_update_header(tar, header)` will re-write the previously written | ||
182 | header. This may be used to change any header field. The underlying stream | ||
183 | must support seeking. On a successful return the stream will be returned | ||
184 | to the position it was at before the call. | ||
185 | |||
186 | File data can be written with `mtar_write_data()`, and if the underlying stream | ||
187 | supports seeking, you can seek with `mtar_seek_data()` and read back previously | ||
188 | written data with `mtar_read_data()`. Note that it is not possible to truncate | ||
189 | the file stream by any means. | ||
190 | |||
191 | - `mtar_write_data(tar, buf, count)` will write up to `count` bytes from | ||
192 | `buf` to the current member's data. Returns the number of bytes actually | ||
193 | written or a negative error code. | ||
194 | |||
195 | - `mtar_update_file_size(tar)` will update the header size to reflect the | ||
196 | actual amount of written data. This is intended to be called right before | ||
197 | `mtar_end_data()` if you are not declaring file sizes in advance. | ||
198 | |||
199 | - `mtar_end_data(tar)` will end the current member. It will complain if you | ||
200 | did not write the correct amount data provided in the header. This must be | ||
201 | called before writing the next header. | ||
202 | |||
203 | - `mtar_finalize(tar)` is called after you have written all members to the | ||
204 | archive. It writes out some null records which mark the end of the archive, | ||
205 | so you cannot write any more archive members after this. | ||
206 | |||
207 | Note that `mtar_close()` can fail if there was a problem flushing buffered | ||
208 | data to disk, so its return value should always be checked. | ||
209 | |||
87 | 210 | ||
88 | ## Error handling | 211 | ## Error handling |
89 | All functions which return an `int` will return `MTAR_ESUCCESS` if the operation | ||
90 | is successful. If an error occurs an error value less-than-zero will be | ||
91 | returned; this value can be passed to the function `mtar_strerror()` to get its | ||
92 | corresponding error string. | ||
93 | 212 | ||
213 | Most functions that return `int` return an error code from `enum mtar_error`. | ||
214 | Zero is success and all other error codes are negative. `mtar_strerror()` can | ||
215 | return a string describing the error code. | ||
94 | 216 | ||
95 | ## Wrapping a stream | 217 | A couple of functions use a different return value convention: |
96 | If you want to read or write from something other than a file, the `mtar_t` | ||
97 | struct can be manually initialized with your own callback functions and a | ||
98 | `stream` pointer. | ||
99 | 218 | ||
100 | All callback functions are passed a pointer to the `mtar_t` struct as their | 219 | - `mtar_foreach()` may error codes or an arbitrary nonzero value provided |
101 | first argument. They should return `MTAR_ESUCCESS` if the operation succeeds | 220 | by the callback. |
102 | without an error, or an integer below zero if an error occurs. | 221 | - `mtar_read_data()` and `mtar_write_data()` returns the number of bytes read |
222 | or written, or a negative error code. In particular zero means that no bytes | ||
223 | were read or written. | ||
224 | - `mtar_get_header()` may return `NULL` if there is no valid header. | ||
225 | It is only possible to see a null pointer if misusing the API or after | ||
226 | a previous error so checking for this is usually not necessary. | ||
103 | 227 | ||
104 | After the `stream` field has been set, all required callbacks have been set and | 228 | There is essentially no support for error recovery. After an error you can |
105 | all unused fields have been zeroset the `mtar_t` struct can be safely used with | 229 | only do two things reliably: close the archive with `mtar_close()` or try |
106 | the microtar functions. `mtar_open` *should not* be called if the `mtar_t` | 230 | rewinding to the beginning with `mtar_rewind()`. |
107 | struct was initialized manually. | ||
108 | 231 | ||
109 | #### Reading | ||
110 | The following callbacks should be set for reading an archive from a stream: | ||
111 | 232 | ||
112 | Name | Arguments | Description | 233 | ## I/O hooks |
113 | --------|------------------------------------------|--------------------------- | ||
114 | `read` | `mtar_t *tar, void *data, unsigned size` | Read data from the stream | ||
115 | `seek` | `mtar_t *tar, unsigned pos` | Set the position indicator | ||
116 | `close` | `mtar_t *tar` | Close the stream | ||
117 | 234 | ||
118 | #### Writing | 235 | You can provide your own I/O hooks in a `mtar_ops_t` struct. The same ops |
119 | The following callbacks should be set for writing an archive to a stream: | 236 | struct can be shared among multiple `mtar_t` objects but each object gets |
237 | its own `void* stream` pointer. | ||
120 | 238 | ||
121 | Name | Arguments | Description | 239 | Name | Arguments | Required |
122 | --------|------------------------------------------------|--------------------- | 240 | --------|-------------------------------------------|------------ |
123 | `write` | `mtar_t *tar, const void *data, unsigned size` | Write data to the stream | 241 | `read` | `void* stream, void* data, unsigned size` | If reading |
242 | `write` | `void* stream, void* data, unsigned size` | If writing | ||
243 | `seek` | `void* stream, unsigned pos` | If reading | ||
244 | `close` | `void* stream` | Always | ||
124 | 245 | ||
246 | `read` and `write` should transfer the number of bytes indicated | ||
247 | and return the number of bytes actually read or written, or a negative | ||
248 | `enum mtar_error` code on error. | ||
125 | 249 | ||
126 | ## License | 250 | `seek` must have semantics like `lseek(..., pos, SEEK_SET)`; that is, |
127 | This library is free software; you can redistribute it and/or modify it under | 251 | the position is an absolute byte offset in the stream. Seeking is not |
128 | the terms of the MIT license. See [LICENSE](LICENSE) for details. | 252 | optional for read support, but the library only performs backward |
253 | seeks under two circumstances: | ||
254 | |||
255 | - `mtar_rewind()` seeks to position 0. | ||
256 | - `mtar_seek_data()` may seek backward if the user requests it. | ||
257 | |||
258 | Therefore, you will be able to get away with a limited forward-only | ||
259 | seek function if you're able to read everything in a single pass use | ||
260 | the API carefully. Note `mtar_find()` and `mtar_foreach()` will call | ||
261 | `mtar_rewind()`. | ||
262 | |||
263 | `close` is called by `mtar_close()` to clean up the stream. Note the | ||
264 | library assumes that the stream handle is cleaned up by `close` even | ||
265 | if an error occurs. | ||
266 | |||
267 | `seek` and `close` should return an `enum mtar_error` code, either | ||
268 | `MTAR_SUCCESS`, or a negative value on error. | ||
diff --git a/lib/microtar/README.rockbox b/lib/microtar/README.rockbox new file mode 100644 index 0000000000..944f792996 --- /dev/null +++ b/lib/microtar/README.rockbox | |||
@@ -0,0 +1,14 @@ | |||
1 | Upstream commit/repo | ||
2 | 1e921369b2c92bb219fcef84a37d4d2347794c0f | ||
3 | https://github.com/amachronic/microtar | ||
4 | |||
5 | Summary of Rockbox-specific changes: | ||
6 | |||
7 | - microtar.make, SOURCES | ||
8 | Target build system integration. | ||
9 | |||
10 | - microtar-rockbox.c, microtar-rockbox.h | ||
11 | Implements mtar ops with Rockbox filesystem API. | ||
12 | |||
13 | - microtar.c | ||
14 | Define strncpy since Rockbox's libc doesn't define it. | ||
diff --git a/lib/microtar/mtar.c b/lib/microtar/mtar.c new file mode 100644 index 0000000000..4a9a27b782 --- /dev/null +++ b/lib/microtar/mtar.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2021 Aidan MacDonald | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
5 | * of this software and associated documentation files (the "Software"), to | ||
6 | * deal in the Software without restriction, including without limitation the | ||
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
8 | * sell copies of the Software, and to permit persons to whom the Software is | ||
9 | * furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
20 | * IN THE SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #include "microtar-stdio.h" | ||
24 | #include <stdio.h> | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <stdarg.h> | ||
28 | #include <sys/stat.h> | ||
29 | #include <fcntl.h> | ||
30 | #include <errno.h> | ||
31 | #include <unistd.h> | ||
32 | |||
33 | /* exit codes */ | ||
34 | #define E_TAR 1 | ||
35 | #define E_FS 2 | ||
36 | #define E_OTHER 4 | ||
37 | #define E_ARGS 8 | ||
38 | |||
39 | enum { | ||
40 | OP_LIST, | ||
41 | OP_CREATE, | ||
42 | OP_EXTRACT, | ||
43 | }; | ||
44 | |||
45 | void die(int err, const char* msg, ...) | ||
46 | { | ||
47 | fprintf(stderr, "mtar: "); | ||
48 | |||
49 | va_list ap; | ||
50 | va_start(ap, msg); | ||
51 | vfprintf(stderr, msg, ap); | ||
52 | va_end(ap); | ||
53 | |||
54 | fprintf(stderr, "\n"); | ||
55 | exit(err); | ||
56 | } | ||
57 | |||
58 | int list_foreach_cb(mtar_t* tar, const mtar_header_t* h, void* arg) | ||
59 | { | ||
60 | (void)tar; | ||
61 | (void)arg; | ||
62 | printf("%s\n", h->name); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | void list_files(mtar_t* tar) | ||
67 | { | ||
68 | int err = mtar_foreach(tar, list_foreach_cb, NULL); | ||
69 | if(err) | ||
70 | die(E_TAR, "listing failed: %s", mtar_strerror(err)); | ||
71 | } | ||
72 | |||
73 | struct extract_args { | ||
74 | char** names; | ||
75 | int count; | ||
76 | }; | ||
77 | |||
78 | int extract_foreach_cb(mtar_t* tar, const mtar_header_t* h, void* arg) | ||
79 | { | ||
80 | struct extract_args* args = arg; | ||
81 | (void)args; /* TODO */ | ||
82 | |||
83 | if(h->type == MTAR_TDIR) { | ||
84 | if(mkdir(h->name, h->mode) != 0) | ||
85 | die(E_FS, "cannot create directory \"%s\"", h->name); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | if(h->type != MTAR_TREG) { | ||
90 | fprintf(stderr, "warning: not extracting unsupported type \"%s\"", h->name); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | int fd = open(h->name, O_CREAT|O_WRONLY|O_TRUNC, h->mode); | ||
95 | if(fd < 0) | ||
96 | die(E_FS, "extracting \"%s\" failed: %s", h->name, strerror(errno)); | ||
97 | |||
98 | char iobuf[1024]; | ||
99 | while(!mtar_eof_data(tar)) { | ||
100 | int rcount = mtar_read_data(tar, iobuf, sizeof(iobuf)); | ||
101 | if(rcount < 0) | ||
102 | die(E_TAR, "extracting \"%s\" failed: %s", h->name, mtar_strerror(rcount)); | ||
103 | |||
104 | int wcount = write(fd, iobuf, rcount); | ||
105 | if(wcount != rcount) | ||
106 | die(E_FS, "extracting \"%s\" failed: %s", h->name, strerror(errno)); | ||
107 | } | ||
108 | |||
109 | close(fd); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | void extract_files(mtar_t* tar, char** files, int num_files) | ||
114 | { | ||
115 | struct extract_args args; | ||
116 | args.names = files; | ||
117 | args.count = num_files; | ||
118 | |||
119 | int err = mtar_foreach(tar, extract_foreach_cb, &args); | ||
120 | if(err) | ||
121 | die(E_TAR, "extraction failed: %s", mtar_strerror(err)); | ||
122 | } | ||
123 | |||
124 | void add_files(mtar_t* tar, char** files, int num_files) | ||
125 | { | ||
126 | for(int i = 0; i < num_files; ++i) { | ||
127 | int fd = open(files[i], O_RDONLY); | ||
128 | if(fd < 0) | ||
129 | die(E_FS, "adding \"%s\" failed: %s", files[i], strerror(errno)); | ||
130 | |||
131 | off_t off = lseek(fd, 0, SEEK_END); | ||
132 | if(off < 0) | ||
133 | die(E_FS, "adding \"%s\" failed: %s", files[i], strerror(errno)); | ||
134 | |||
135 | unsigned filesize = off; | ||
136 | lseek(fd, 0, SEEK_SET); | ||
137 | |||
138 | int err = mtar_write_file_header(tar, files[i], filesize); | ||
139 | if(err) | ||
140 | die(E_TAR, "adding \"%s\" failed: %s", files[i], mtar_strerror(err)); | ||
141 | |||
142 | char iobuf[1024]; | ||
143 | while(1) { | ||
144 | int rcount = read(fd, iobuf, sizeof(iobuf)); | ||
145 | if(rcount < 0) | ||
146 | die(E_FS, "adding \"%s\" failed: %s", files[i], strerror(errno)); | ||
147 | if(rcount == 0) | ||
148 | break; | ||
149 | |||
150 | int wcount = mtar_write_data(tar, iobuf, rcount); | ||
151 | if(wcount < 0) | ||
152 | die(E_TAR, "adding \"%s\" failed: %s", files[i], mtar_strerror(wcount)); | ||
153 | if(wcount != rcount) | ||
154 | die(E_TAR, "adding \"%s\" failed: write too short %d/%d", files[i], wcount, rcount); | ||
155 | } | ||
156 | |||
157 | close(fd); | ||
158 | |||
159 | err = mtar_end_data(tar); | ||
160 | if(err) | ||
161 | die(E_TAR, "adding \"%s\" failed: %s", files[i], mtar_strerror(err)); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | int main(int argc, char* argv[]) | ||
166 | { | ||
167 | ++argv, --argc; | ||
168 | if(argc == 0) | ||
169 | die(E_ARGS, "no input files"); | ||
170 | |||
171 | if(!strcmp(*argv, "--help")) { | ||
172 | printf( | ||
173 | "usage:\n" | ||
174 | " mtar list tar-file\n" | ||
175 | " List the members of the given tar archive, one filename per line.\n" | ||
176 | "\n" | ||
177 | " mtar create tar-file members...\n" | ||
178 | " mtar add tar-file members...\n" | ||
179 | " Create a new tar archive from the files listed on the command line.\n" | ||
180 | " WARNING: Any existing file at tar-file will be overwritten!\n" | ||
181 | "\n" | ||
182 | " mtar extract tar-file [members...]\n" | ||
183 | " Extract the contents of the tar archive to the current directory.\n" | ||
184 | " If filenames are given, only the named members will be extracted.\n" | ||
185 | "\n"); | ||
186 | exit(E_ARGS); | ||
187 | } | ||
188 | |||
189 | int op; | ||
190 | if(!strcmp(*argv, "list")) | ||
191 | op = OP_LIST; | ||
192 | else if(!strcmp(*argv, "create")) | ||
193 | op = OP_CREATE; | ||
194 | else if(!strcmp(*argv, "extract")) | ||
195 | op = OP_EXTRACT; | ||
196 | else | ||
197 | die(E_ARGS, "invalid operation \"%s\"", *argv); | ||
198 | ++argv, --argc; | ||
199 | |||
200 | if(argc == 0) | ||
201 | die(E_ARGS, "missing archive name"); | ||
202 | const char* archive_name = *argv; | ||
203 | ++argv, --argc; | ||
204 | |||
205 | if(op == OP_LIST && argc != 0) | ||
206 | die(E_ARGS, "excess arguments on command line"); | ||
207 | |||
208 | const char* mode = "rb"; | ||
209 | if(op == OP_CREATE) | ||
210 | mode = "wb"; | ||
211 | |||
212 | mtar_t tar; | ||
213 | int err = mtar_open(&tar, archive_name, mode); | ||
214 | if(err) | ||
215 | die(E_TAR, "can't open archive: %s", mtar_strerror(err)); | ||
216 | |||
217 | switch(op) { | ||
218 | case OP_LIST: | ||
219 | list_files(&tar); | ||
220 | break; | ||
221 | |||
222 | case OP_EXTRACT: | ||
223 | extract_files(&tar, argv, argc); | ||
224 | break; | ||
225 | |||
226 | case OP_CREATE: | ||
227 | add_files(&tar, argv, argc); | ||
228 | err = mtar_finalize(&tar); | ||
229 | if(err) | ||
230 | die(E_TAR, "failed to finalize archive: %s", mtar_strerror(err)); | ||
231 | break; | ||
232 | |||
233 | default: | ||
234 | die(E_OTHER, "not implemented"); | ||
235 | break; | ||
236 | } | ||
237 | |||
238 | err = mtar_close(&tar); | ||
239 | if(err) | ||
240 | die(E_TAR, "failed to close archive: %s", mtar_strerror(err)); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
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 | } |