summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/CREDITS7
-rw-r--r--lib/microtar/LICENSE19
-rw-r--r--lib/microtar/README.md128
-rw-r--r--lib/microtar/SOURCES6
-rw-r--r--lib/microtar/microtar.make21
-rw-r--r--lib/microtar/src/Makefile91
-rw-r--r--lib/microtar/src/microtar-rockbox.c100
-rw-r--r--lib/microtar/src/microtar-stdio.c65
-rw-r--r--lib/microtar/src/microtar.c403
-rw-r--r--lib/microtar/src/microtar.h108
10 files changed, 945 insertions, 3 deletions
diff --git a/docs/CREDITS b/docs/CREDITS
index 720c3a99db..7dee2ce527 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -703,6 +703,9 @@ Georg Gadinger
703Wolfram Sang 703Wolfram Sang
704Aidan MacDonald 704Aidan MacDonald
705Caleb Connolly 705Caleb Connolly
706Spencer Brennessel
707Dana Conrad
708Albert Song
706 709
707The libmad team 710The libmad team
708The wavpack team 711The wavpack team
@@ -730,7 +733,6 @@ The libpng team
730The Pure Data team (Miller Puckette and others) 733The Pure Data team (Miller Puckette and others)
731The MikMod team 734The MikMod team
732Michael McTernan (The ARM unwinder author) 735Michael McTernan (The ARM unwinder author)
733Albert Song
734The New RAW team (Piotr Padkowski and others) 736The New RAW team (Piotr Padkowski and others)
735The Fabother World team (Fabien Sanglard and others) 737The Fabother World team (Fabien Sanglard and others)
736The sgt-puzzles team (Simon Tatham and others) 738The sgt-puzzles team (Simon Tatham and others)
@@ -743,5 +745,4 @@ The Pocket Quake team (Dan East and others)
743The bzip2 team 745The bzip2 team
744The bsdiff team 746The bsdiff team
745The libtomcrypt team 747The libtomcrypt team
746Spencer Brennessel 748The microtar team (rxi and others)
747Dana Conrad
diff --git a/lib/microtar/LICENSE b/lib/microtar/LICENSE
new file mode 100644
index 0000000000..7e3bf17ccb
--- /dev/null
+++ b/lib/microtar/LICENSE
@@ -0,0 +1,19 @@
1Copyright (c) 2017 rxi
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of
4this software and associated documentation files (the "Software"), to deal in
5the Software without restriction, including without limitation the rights to
6use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7of the Software, and to permit persons to whom the Software is furnished to do
8so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19SOFTWARE.
diff --git a/lib/microtar/README.md b/lib/microtar/README.md
new file mode 100644
index 0000000000..18153caa21
--- /dev/null
+++ b/lib/microtar/README.md
@@ -0,0 +1,128 @@
1# microtar
2A lightweight tar library written in ANSI C
3
4
5## Modifications from upstream
6
7[Upstream](https://github.com/rxi/microtar) has numerous bugs and gotchas,
8which I fixed in order to improve the overall robustness of the library.
9
10A summary of my changes, in no particular order:
11
12- Fix possible sscanf beyond the bounds of the input buffer
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
24An up-to-date copy of this modified version can be found
25[here](https://github.com/amachronic/microtar).
26
27
28## Modifications for Rockbox
29
30Added file `microtar-rockbox.c` implementing `mtar_open()` with native
31Rockbox filesystem API.
32
33
34## Basic Usage
35The library consists of `microtar.c` and `microtar.h`. These two files can be
36dropped into an existing project and compiled along with it.
37
38
39#### Reading
40```c
41mtar_t tar;
42mtar_header_t h;
43char *p;
44
45/* Open archive for reading */
46mtar_open(&tar, "test.tar", "r");
47
48/* Print all file names and sizes */
49while ( (mtar_read_header(&tar, &h)) != MTAR_ENULLRECORD ) {
50 printf("%s (%d bytes)\n", h.name, h.size);
51 mtar_next(&tar);
52}
53
54/* Load and print contents of file "test.txt" */
55mtar_find(&tar, "test.txt", &h);
56p = calloc(1, h.size + 1);
57mtar_read_data(&tar, p, h.size);
58printf("%s", p);
59free(p);
60
61/* Close archive */
62mtar_close(&tar);
63```
64
65#### Writing
66```c
67mtar_t tar;
68const char *str1 = "Hello world";
69const char *str2 = "Goodbye world";
70
71/* Open archive for writing */
72mtar_open(&tar, "test.tar", "w");
73
74/* Write strings to files `test1.txt` and `test2.txt` */
75mtar_write_file_header(&tar, "test1.txt", strlen(str1));
76mtar_write_data(&tar, str1, strlen(str1));
77mtar_write_file_header(&tar, "test2.txt", strlen(str2));
78mtar_write_data(&tar, str2, strlen(str2));
79
80/* Finalize -- this needs to be the last thing done before closing */
81mtar_finalize(&tar);
82
83/* Close archive */
84mtar_close(&tar);
85```
86
87
88## Error handling
89All functions which return an `int` will return `MTAR_ESUCCESS` if the operation
90is successful. If an error occurs an error value less-than-zero will be
91returned; this value can be passed to the function `mtar_strerror()` to get its
92corresponding error string.
93
94
95## Wrapping a stream
96If you want to read or write from something other than a file, the `mtar_t`
97struct can be manually initialized with your own callback functions and a
98`stream` pointer.
99
100All callback functions are passed a pointer to the `mtar_t` struct as their
101first argument. They should return `MTAR_ESUCCESS` if the operation succeeds
102without an error, or an integer below zero if an error occurs.
103
104After the `stream` field has been set, all required callbacks have been set and
105all unused fields have been zeroset the `mtar_t` struct can be safely used with
106the microtar functions. `mtar_open` *should not* be called if the `mtar_t`
107struct was initialized manually.
108
109#### Reading
110The following callbacks should be set for reading an archive from a stream:
111
112Name | Arguments | Description
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
118#### Writing
119The following callbacks should be set for writing an archive to a stream:
120
121Name | Arguments | Description
122--------|------------------------------------------------|---------------------
123`write` | `mtar_t *tar, const void *data, unsigned size` | Write data to the stream
124
125
126## License
127This library is free software; you can redistribute it and/or modify it under
128the terms of the MIT license. See [LICENSE](LICENSE) for details.
diff --git a/lib/microtar/SOURCES b/lib/microtar/SOURCES
new file mode 100644
index 0000000000..d9e9b112d8
--- /dev/null
+++ b/lib/microtar/SOURCES
@@ -0,0 +1,6 @@
1src/microtar.c
2#ifdef ROCKBOX
3src/microtar-rockbox.c
4#else
5src/microtar-stdio.c
6#endif
diff --git a/lib/microtar/microtar.make b/lib/microtar/microtar.make
new file mode 100644
index 0000000000..616561eea3
--- /dev/null
+++ b/lib/microtar/microtar.make
@@ -0,0 +1,21 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7#
8
9MICROTARLIB_DIR = $(ROOTDIR)/lib/microtar
10MICROTARLIB_SRC = $(call preprocess, $(MICROTARLIB_DIR)/SOURCES)
11MICROTARLIB_OBJ := $(call c2obj, $(MICROTARLIB_SRC))
12
13MICROTARLIB = $(BUILDDIR)/lib/libmicrotar.a
14
15INCLUDES += -I$(MICROTARLIB_DIR)/src
16OTHER_SRC += $(MICROTARLIB_SRC)
17CORE_LIBS += $(MICROTARLIB)
18
19$(MICROTARLIB): $(MICROTARLIB_OBJ)
20 $(SILENT)$(shell rm -f $@)
21 $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
diff --git a/lib/microtar/src/Makefile b/lib/microtar/src/Makefile
new file mode 100644
index 0000000000..27277f7c73
--- /dev/null
+++ b/lib/microtar/src/Makefile
@@ -0,0 +1,91 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10# Shameless copy+paste from tools/ucl/src/Makefile
11# This is only used for rbutil builds, Rockbox uses microtar.make
12
13ifndef V
14SILENT = @
15endif
16
17ifeq ($(OS),Windows_NT)
18SHELL = cmd.exe
19mkdir = if not exist $(subst /,\,$(1)) mkdir $(subst /,\,$(1))
20else
21mkdir = mkdir -p $(1)
22endif
23
24ifdef RBARCH
25CFLAGS += -arch $(RBARCH)
26endif
27
28CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -)
29#build standalone win32 executables on cygwin
30ifeq ($(findstring CYGWIN,$(CPPDEFINES)),CYGWIN)
31COMPILETARGET = cygwin
32else
33ifeq ($(findstring MINGW,$(CPPDEFINES)),MINGW)
34COMPILETARGET = mingw
35else
36# OS X specifics. Needs to consider cross compiling for Windows.
37ifeq ($(findstring APPLE,$(CPPDEFINES)),APPLE)
38# When building for 10.4+ we need to use gcc. Otherwise clang is used, so use
39# that to determine if we need to set arch and isysroot.
40ifeq ($(findstring __clang__,$(CPPDEFINES)),__clang__)
41CFLAGS += -mmacosx-version-min=10.5
42ifneq ($(ISYSROOT),)
43CFLAGS += -isysroot $(ISYSROOT)
44endif
45else
46# when building libs for OS X 10.4+ build for both i386 and ppc at the same time.
47# This creates fat objects, and ar can only create the archive but not operate
48# on it. As a result the ar call must NOT use the u (update) flag.
49ARCHFLAGS += -arch ppc -arch i386
50# building against SDK 10.4 is not compatible with gcc-4.2 (default on newer Xcode)
51# might need adjustment for older Xcode.
52CC = gcc-4.0
53CFLAGS += -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4
54NATIVECC ?= gcc-4.0
55endif
56COMPILETARGET = darwin
57else
58COMPILETARGET = posix
59endif
60endif
61endif
62$(info Compiler creates $(COMPILETARGET) binaries)
63
64TARGET_DIR ?= $(shell pwd)/
65OBJDIR = $(TARGET_DIR)build$(RBARCH)
66
67SOURCES = microtar.c microtar-stdio.c
68
69OBJS = $(addprefix $(OBJDIR)/,$(SOURCES:%.c=%.o))
70
71libmicrotar$(RBARCH).a: $(TARGET_DIR)libmicrotar$(RBARCH).a
72
73dll: microtar.dll
74microtar.dll: $(TARGET_DIR)microtar.dll
75$(TARGET_DIR)microtar.dll: $(OBJS)
76 @echo DLL $(notdir $@)
77 $(SILENT)$(CROSS)$(CC) $(CFLAGS) -shared -o $@ $^ \
78 -Wl,--output-def,$(TARGET_DIR)microtar.def
79
80$(TARGET_DIR)libmicrotar$(RBARCH).a: $(OBJS)
81 @echo AR $(notdir $@)
82 $(SILENT)$(CROSS)$(AR) rcs $@ $(OBJS)
83
84$(OBJDIR)/%.o: %.c
85 @echo CC $<
86 $(SILENT)$(call mkdir, $(dir $@))
87 $(SILENT)$(CROSS)$(CC) $(CFLAGS) $(ARCHFLAGS) -c $< -o $@
88
89clean:
90 rm -f $(TARGET_DIR)libmicrotar*.a
91 rm -rf build*
diff --git a/lib/microtar/src/microtar-rockbox.c b/lib/microtar/src/microtar-rockbox.c
new file mode 100644
index 0000000000..04ba93cc42
--- /dev/null
+++ b/lib/microtar/src/microtar-rockbox.c
@@ -0,0 +1,100 @@
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#include "file.h"
23#include "microtar.h"
24#include <stdint.h>
25
26static int fd_write(mtar_t *tar, const void *data, unsigned size) {
27 intptr_t fd = (intptr_t)tar->stream;
28 ssize_t res = write(fd, data, size);
29 if(res < 0 || ((unsigned)res != size))
30 return MTAR_EWRITEFAIL;
31 else
32 return MTAR_ESUCCESS;
33}
34
35static int fd_read(mtar_t *tar, void *data, unsigned size) {
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}
43
44static int fd_seek(mtar_t *tar, unsigned offset) {
45 intptr_t fd = (intptr_t)tar->stream;
46 off_t res = lseek(fd, offset, SEEK_SET);
47 if(res < 0 || ((unsigned)res != offset))
48 return MTAR_ESEEKFAIL;
49 else
50 return MTAR_ESUCCESS;
51}
52
53static int fd_close(mtar_t *tar) {
54 intptr_t fd = (intptr_t)tar->stream;
55 close(fd);
56 return MTAR_ESUCCESS;
57}
58
59
60int mtar_open(mtar_t *tar, const char *filename, const char *mode) {
61 int err;
62 int openmode;
63 int fd;
64
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
72 /* Get correct mode flags */
73 if ( strchr(mode, 'r') )
74 openmode = O_RDONLY;
75 else if ( strchr(mode, 'w') )
76 openmode = O_CREAT|O_TRUNC|O_WRONLY;
77 else if ( strchr(mode, 'a') )
78 openmode = O_WRONLY|O_APPEND;
79 else
80 return MTAR_EFAILURE;
81
82 /* Open file */
83 fd = open(filename, openmode);
84 if(fd < 0)
85 return MTAR_EOPENFAIL;
86
87 tar->stream = (void*)(intptr_t)fd;
88
89 /* Read first header to check it is valid if mode is `r` */
90 if ( openmode & O_RDONLY ) {
91 err = mtar_read_header(tar, &tar->header);
92 if (err != MTAR_ESUCCESS) {
93 mtar_close(tar);
94 return err;
95 }
96 }
97
98 /* Return ok */
99 return MTAR_ESUCCESS;
100}
diff --git a/lib/microtar/src/microtar-stdio.c b/lib/microtar/src/microtar-stdio.c
new file mode 100644
index 0000000000..215000aa98
--- /dev/null
+++ b/lib/microtar/src/microtar-stdio.c
@@ -0,0 +1,65 @@
1/**
2 * Copyright (c) 2017 rxi
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the MIT license. See `microtar.c` for details.
6 */
7
8#include <stdio.h>
9#include <string.h>
10
11#include "microtar.h"
12
13static int file_write(mtar_t *tar, const void *data, unsigned size) {
14 unsigned res = fwrite(data, 1, size, tar->stream);
15 return (res == size) ? MTAR_ESUCCESS : MTAR_EWRITEFAIL;
16}
17
18static int file_read(mtar_t *tar, void *data, unsigned size) {
19 unsigned res = fread(data, 1, size, tar->stream);
20 return (res == size) ? MTAR_ESUCCESS : MTAR_EREADFAIL;
21}
22
23static int file_seek(mtar_t *tar, unsigned offset) {
24 int res = fseek(tar->stream, offset, SEEK_SET);
25 return (res == 0) ? MTAR_ESUCCESS : MTAR_ESEEKFAIL;
26}
27
28static int file_close(mtar_t *tar) {
29 fclose(tar->stream);
30 return MTAR_ESUCCESS;
31}
32
33
34int mtar_open(mtar_t *tar, const char *filename, const char *mode) {
35 int err;
36 mtar_header_t h;
37
38 /* Init tar struct and functions */
39 memset(tar, 0, sizeof(*tar));
40 tar->write = file_write;
41 tar->read = file_read;
42 tar->seek = file_seek;
43 tar->close = file_close;
44
45 /* Assure mode is always binary */
46 if ( strchr(mode, 'r') ) mode = "rb";
47 if ( strchr(mode, 'w') ) mode = "wb";
48 if ( strchr(mode, 'a') ) mode = "ab";
49 /* Open file */
50 tar->stream = fopen(filename, mode);
51 if (!tar->stream) {
52 return MTAR_EOPENFAIL;
53 }
54 /* Read first header to check it is valid if mode is `r` */
55 if (*mode == 'r') {
56 err = mtar_read_header(tar, &h);
57 if (err != MTAR_ESUCCESS) {
58 mtar_close(tar);
59 return err;
60 }
61 }
62
63 /* Return ok */
64 return MTAR_ESUCCESS;
65}
diff --git a/lib/microtar/src/microtar.c b/lib/microtar/src/microtar.c
new file mode 100644
index 0000000000..04cd4ea132
--- /dev/null
+++ b/lib/microtar/src/microtar.c
@@ -0,0 +1,403 @@
1/*
2 * Copyright (c) 2017 rxi
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 * IN THE SOFTWARE.
21 */
22
23#include <stdlib.h>
24#include <stddef.h>
25#include <limits.h>
26#include <string.h>
27
28#include "microtar.h"
29
30#ifdef ROCKBOX
31/* Rockbox lacks strncpy in its libc */
32static char* strncpy(char* dest, const char* src, size_t n) {
33 size_t i;
34
35 for(i = 0; i < n && *src; ++i)
36 dest[i] = src[i];
37
38 for(; i < n; ++i)
39 dest[i] = 0;
40
41 return dest;
42}
43#endif
44
45
46static int parse_octal(const char* str, size_t len, unsigned* ret) {
47 unsigned n = 0;
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}
67
68
69static int print_octal(char* str, size_t len, unsigned value) {
70 /* move backwards over the output string */
71 char* ptr = str + len - 1;
72 *ptr = 0;
73
74 /* output the significant digits */
75 while(value > 0) {
76 if(ptr == str)
77 return MTAR_EOVERFLOW;
78
79 --ptr;
80 *ptr = '0' + (value % 8);
81 value /= 8;
82 }
83
84 /* pad the remainder of the field with zeros */
85 while(ptr != str) {
86 --ptr;
87 *ptr = '0';
88 }
89
90 return MTAR_ESUCCESS;
91}
92
93
94static unsigned round_up(unsigned n, unsigned incr) {
95 return n + (incr - n % incr) % incr;
96}
97
98
99static unsigned checksum(const mtar_raw_header_t* rh) {
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}
111
112
113static int tread(mtar_t *tar, void *data, unsigned size) {
114 int err = tar->read(tar, data, size);
115 tar->pos += size;
116 return err;
117}
118
119
120static int twrite(mtar_t *tar, const void *data, unsigned size) {
121 int err = tar->write(tar, data, size);
122 tar->pos += size;
123 return err;
124}
125
126
127static int write_null_bytes(mtar_t *tar, int n) {
128 int i, err;
129 char nul = '\0';
130 for (i = 0; i < n; i++) {
131 err = twrite(tar, &nul, 1);
132 if (err) {
133 return err;
134 }
135 }
136 return MTAR_ESUCCESS;
137}
138
139
140static int raw_to_header(mtar_header_t *h, const mtar_raw_header_t *rh) {
141 unsigned chksum1, chksum2;
142 int rc;
143
144 /* If the checksum starts with a null byte we assume the record is NULL */
145 if (*rh->checksum == '\0') {
146 return MTAR_ENULLRECORD;
147 }
148
149 /* Build and compare checksum */
150 chksum1 = checksum(rh);
151 if((rc = parse_octal(rh->checksum, sizeof(rh->checksum), &chksum2)))
152 return rc;
153 if (chksum1 != chksum2) {
154 return MTAR_EBADCHKSUM;
155 }
156
157 /* Load raw header into header */
158 if((rc = parse_octal(rh->mode, sizeof(rh->mode), &h->mode)))
159 return rc;
160 if((rc = parse_octal(rh->owner, sizeof(rh->owner), &h->owner)))
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
169 h->type = rh->type;
170
171 memcpy(h->name, rh->name, sizeof(rh->name));
172 h->name[sizeof(h->name) - 1] = 0;
173
174 memcpy(h->linkname, rh->linkname, sizeof(rh->linkname));
175 h->linkname[sizeof(h->linkname) - 1] = 0;
176
177 return MTAR_ESUCCESS;
178}
179
180
181static int header_to_raw(mtar_raw_header_t *rh, const mtar_header_t *h) {
182 unsigned chksum;
183 int rc;
184
185 /* Load header into raw header */
186 memset(rh, 0, sizeof(*rh));
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
201 /* Calculate and write checksum */
202 chksum = checksum(rh);
203 if((rc = print_octal(rh->checksum, 7, chksum)))
204 return rc;
205 rh->checksum[7] = ' ';
206
207 return MTAR_ESUCCESS;
208}
209
210
211const char* mtar_strerror(int err) {
212 switch (err) {
213 case MTAR_ESUCCESS : return "success";
214 case MTAR_EFAILURE : return "failure";
215 case MTAR_EOPENFAIL : return "could not open";
216 case MTAR_EREADFAIL : return "could not read";
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}
226
227
228int mtar_close(mtar_t *tar) {
229 return tar->close(tar);
230}
231
232
233int mtar_seek(mtar_t *tar, unsigned pos) {
234 int err = tar->seek(tar, pos);
235 tar->pos = pos;
236 return err;
237}
238
239
240int mtar_rewind(mtar_t *tar) {
241 tar->remaining_data = 0;
242 tar->last_header = 0;
243 return mtar_seek(tar, 0);
244}
245
246
247int mtar_next(mtar_t *tar) {
248 int err, n;
249 /* Load header */
250 err = mtar_read_header(tar, &tar->header);
251 if (err) {
252 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}
258
259
260int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h) {
261 int err;
262 /* Start at beginning */
263 err = mtar_rewind(tar);
264 if (err) {
265 return err;
266 }
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 }
279 }
280 /* Return error */
281 if (err == MTAR_ENULLRECORD) {
282 err = MTAR_ENOTFOUND;
283 }
284 return err;
285}
286
287
288int mtar_read_header(mtar_t *tar, mtar_header_t *h) {
289 int err;
290 /* Save header position */
291 tar->last_header = tar->pos;
292 /* Read raw header */
293 err = tread(tar, &tar->raw_header, sizeof(tar->raw_header));
294 if (err) {
295 return err;
296 }
297 /* Seek back to start of header */
298 err = mtar_seek(tar, tar->last_header);
299 if (err) {
300 return err;
301 }
302 /* Load raw header into header struct and return */
303 return raw_to_header(h, &tar->raw_header);
304}
305
306
307int mtar_read_data(mtar_t *tar, void *ptr, unsigned size) {
308 int err;
309 /* If we have no remaining data then this is the first read, we get the size,
310 * set the remaining data and seek to the beginning of the data */
311 if (tar->remaining_data == 0) {
312 /* Read header */
313 err = mtar_read_header(tar, &tar->header);
314 if (err) {
315 return err;
316 }
317 /* Seek past header and init remaining data */
318 err = mtar_seek(tar, tar->pos + sizeof(mtar_raw_header_t));
319 if (err) {
320 return err;
321 }
322 tar->remaining_data = tar->header.size;
323 }
324 /* Ensure caller does not read too much */
325 if(size > tar->remaining_data)
326 return MTAR_EOVERFLOW;
327 /* Read data */
328 err = tread(tar, ptr, size);
329 if (err) {
330 return err;
331 }
332 tar->remaining_data -= size;
333 /* If there is no remaining data we've finished reading and seek back to the
334 * header */
335 if (tar->remaining_data == 0) {
336 return mtar_seek(tar, tar->last_header);
337 }
338 return MTAR_ESUCCESS;
339}
340
341
342int mtar_write_header(mtar_t *tar, const mtar_header_t *h) {
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}
348
349
350int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size) {
351 /* Build header */
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}
363
364
365int mtar_write_dir_header(mtar_t *tar, const char *name) {
366 /* Build header */
367 memset(&tar->header, 0, sizeof(tar->header));
368 /* Ensure name fits within header */
369 if(strlen(name) > sizeof(tar->header.name))
370 return MTAR_EOVERFLOW;
371 strncpy(tar->header.name, name, sizeof(tar->header.name));
372 tar->header.type = MTAR_TDIR;
373 tar->header.mode = 0775;
374 /* Write header */
375 return mtar_write_header(tar, &tar->header);
376}
377
378
379int mtar_write_data(mtar_t *tar, const void *data, unsigned size) {
380 int err;
381
382 /* Ensure we are writing the correct amount of data */
383 if(size > tar->remaining_data)
384 return MTAR_EOVERFLOW;
385
386 /* Write data */
387 err = twrite(tar, data, size);
388 if (err) {
389 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}
398
399
400int mtar_finalize(mtar_t *tar) {
401 /* Write two NULL records */
402 return write_null_bytes(tar, sizeof(mtar_raw_header_t) * 2);
403}
diff --git a/lib/microtar/src/microtar.h b/lib/microtar/src/microtar.h
new file mode 100644
index 0000000000..d30c829521
--- /dev/null
+++ b/lib/microtar/src/microtar.h
@@ -0,0 +1,108 @@
1/**
2 * Copyright (c) 2017 rxi
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the MIT license. See `microtar.c` for details.
6 */
7
8#ifndef MICROTAR_H
9#define MICROTAR_H
10
11#ifdef __cplusplus
12extern "C"
13{
14#endif
15
16#include <stdio.h>
17#include <stdlib.h>
18
19#define MTAR_VERSION "0.1.0"
20
21enum {
22 MTAR_ESUCCESS = 0,
23 MTAR_EFAILURE = -1,
24 MTAR_EOPENFAIL = -2,
25 MTAR_EREADFAIL = -3,
26 MTAR_EWRITEFAIL = -4,
27 MTAR_ESEEKFAIL = -5,
28 MTAR_EBADCHKSUM = -6,
29 MTAR_ENULLRECORD = -7,
30 MTAR_ENOTFOUND = -8,
31 MTAR_EOVERFLOW = -9,
32};
33
34enum {
35 MTAR_TREG = '0',
36 MTAR_TLNK = '1',
37 MTAR_TSYM = '2',
38 MTAR_TCHR = '3',
39 MTAR_TBLK = '4',
40 MTAR_TDIR = '5',
41 MTAR_TFIFO = '6'
42};
43
44typedef struct {
45 unsigned mode;
46 unsigned owner;
47 unsigned group;
48 unsigned size;
49 unsigned mtime;
50 unsigned type;
51 char name[101]; /* +1 byte in order to ensure null termination */
52 char linkname[101];
53} mtar_header_t;
54
55
56typedef struct {
57 char name[100];
58 char mode[8];
59 char owner[8];
60 char group[8];
61 char size[12];
62 char mtime[12];
63 char checksum[8];
64 char type;
65 char linkname[100];
66 char _padding[255];
67} mtar_raw_header_t;
68
69
70typedef struct mtar_t mtar_t;
71
72struct mtar_t {
73 int (*read)(mtar_t *tar, void *data, unsigned size);
74 int (*write)(mtar_t *tar, const void *data, unsigned size);
75 int (*seek)(mtar_t *tar, unsigned pos);
76 int (*close)(mtar_t *tar);
77 void *stream;
78 unsigned pos;
79 unsigned remaining_data;
80 unsigned last_header;
81 mtar_header_t header;
82 mtar_raw_header_t raw_header;
83};
84
85
86const char* mtar_strerror(int err);
87
88int mtar_open(mtar_t *tar, const char *filename, const char *mode);
89int mtar_close(mtar_t *tar);
90
91int mtar_seek(mtar_t *tar, unsigned pos);
92int mtar_rewind(mtar_t *tar);
93int mtar_next(mtar_t *tar);
94int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h);
95int mtar_read_header(mtar_t *tar, mtar_header_t *h);
96int mtar_read_data(mtar_t *tar, void *ptr, unsigned size);
97
98int mtar_write_header(mtar_t *tar, const mtar_header_t *h);
99int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size);
100int mtar_write_dir_header(mtar_t *tar, const char *name);
101int mtar_write_data(mtar_t *tar, const void *data, unsigned size);
102int mtar_finalize(mtar_t *tar);
103
104#ifdef __cplusplus
105}
106#endif
107
108#endif