Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
1ce643b0c8 | |||
ae7bf076dc | |||
73c4dce573 | |||
7ef1c80fb6 | |||
6b1b8fef55 | |||
cd0c5df5f5 | |||
1644ab59eb | |||
f12f58bacf | |||
702348a365 | |||
f6507e5461 | |||
7e42b10078 | |||
cb432fd306 | |||
a6d38efc7d | |||
85a6d79151 | |||
320231c70b | |||
7571ef42c6 | |||
88d6fbffb7 | |||
01d3c27d41 | |||
bf52d9e321 | |||
7339aeeae5 | |||
40302ddd41 | |||
8cb5175630 | |||
5bfc7e45ac | |||
873f056cc3 | |||
9b39f43595 | |||
9c43461e02 | |||
53aa377bbb | |||
f09e6f2e85 | |||
845b357bef | |||
482ac6d949 |
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.8..3.22)
|
||||
|
||||
project(minitar LANGUAGES C VERSION 1.6.1)
|
||||
project(minitar LANGUAGES C VERSION 1.7.6)
|
||||
|
||||
option(MINITAR_IGNORE_UNSUPPORTED_TYPES "Skip past entries that have unsupported types instead of panicking (deprecated)" OFF)
|
||||
|
||||
@ -31,4 +31,4 @@ endif()
|
||||
install(TARGETS minitar DESTINATION lib)
|
||||
install(FILES minitar.h DESTINATION include)
|
||||
|
||||
add_subdirectory(examples)
|
||||
add_subdirectory(examples)
|
||||
|
@ -1,10 +1,10 @@
|
||||
# minitar
|
||||
|
||||
Tiny and easy-to-use C library to parse tar (specifically, the newer [USTAR](https://www.ibm.com/docs/en/zos/2.3.0?topic=formats-tar-format-tar-archives#taf) variant, which is the one pretty much everybody uses) archives.
|
||||
Tiny and easy-to-use C library to read/write tar (specifically, the [ustar](https://www.ibm.com/docs/en/zos/2.3.0?topic=formats-tar-format-tar-archives#taf) variant, which is a bit old but simple, and newer tar variants (pax, GNU tar) are mostly backwards-compatible with it) archives.
|
||||
|
||||
No third-party dependencies, only a minimally capable standard C library (pretty much only requires a basic subset of the C FILE API, apart from other simple functions).
|
||||
|
||||
Aims to be bloat-free (currently just above 500 LoC), fast and optimized, and as portable between systems as possible (has its own implementation of some non-standard functions, such as [strlcpy](https://linux.die.net/man/3/strlcpy) or [basename](https://linux.die.net/man/3/basename)).
|
||||
Aims to be bloat-free (currently a bit above 500 LoC), fast and optimized, and as portable between systems as possible (has its own implementation of some non-standard functions, such as [strlcpy](https://linux.die.net/man/3/strlcpy) or [basename](https://linux.die.net/man/3/basename)).
|
||||
|
||||
Does not include support for compressed archives. You'll have to pass those through another program or library to decompress them before minitar can handle them.
|
||||
|
||||
@ -43,7 +43,7 @@ See [examples](examples/) for more examples using minitar.
|
||||
|
||||
## Project structure
|
||||
|
||||
The user-facing API (functions defined in `minitar.h` and documented in this README) is implemented in `src/tar.c`. Utility and internally-used functions live in `src/util.c`.
|
||||
The user-facing API (functions defined in `minitar.h` and documented in [API.md](docs/API.md)) is implemented in `src/tar.c`. Utility and internally-used functions live in `src/util.c`.
|
||||
|
||||
## Documentation
|
||||
|
||||
@ -75,4 +75,4 @@ pub extern "C" fn minitar_handle_panic(message: *const u8) -> !
|
||||
|
||||
## License
|
||||
|
||||
`minitar` is free and open-source software under the [BSD-2-Clause](LICENSE) license.
|
||||
`minitar` is free and open-source software under the [BSD-2-Clause](LICENSE) license.
|
||||
|
63
docs/API.md
63
docs/API.md
@ -1,10 +1,17 @@
|
||||
# minitar API documentation
|
||||
|
||||
Functions/types suffixed with `_w` or that contain `write` in their names are part of the newer API for writing to archives. Other types/functions are part of the (older) API for reading archives.
|
||||
|
||||
## Functions
|
||||
### minitar_open
|
||||
`int minitar_open(const char* pathname, struct minitar* mp)`
|
||||
|
||||
Initializes the caller-provided `mp` structure by opening the archive pointed to by `pathname` for reading. Returns 0 on success, anything else is failure.
|
||||
Initializes the caller-provided `mp` [handle](API.md#minitar) by opening the archive pointed to by `pathname` for reading. Returns 0 on success, anything else is failure.
|
||||
|
||||
### minitar_open_w
|
||||
`int minitar_open_w(const char* pathname, struct minitar_w* mp, enum minitar_write_mode mode)`
|
||||
|
||||
Initializes the caller-provided `mp` [handle](API.md#minitar_w) by opening the archive pointed to by `pathname` for writing (in case `pathname` already exists, mode selects if the existing file is overwritten or if new entries are appended to it). Returns 0 on success, anything else is failure.
|
||||
|
||||
### minitar_read_entry
|
||||
`int minitar_read_entry(struct minitar* mp, struct minitar_entry* out)`
|
||||
@ -17,6 +24,30 @@ To read the contents of an entry, you should allocate a buffer large enough to h
|
||||
|
||||
This function returns 0 on success and -1 on end-of-file (when all entries have been read).
|
||||
|
||||
### minitar_write_file_entry
|
||||
`int minitar_write_file_entry(struct minitar_w* mp, const struct minitar_entry_metadata* metadata, char* buf)`
|
||||
|
||||
Writes a regular file entry into a `struct minitar_w` which should be initialized by a previous call to `minitar_open_w()`.
|
||||
|
||||
This function writes both a header (generated from the metadata) and the file contents in `buf`, which should be `metadata.size` bytes long.
|
||||
|
||||
This function will only write entries for regular files (metadata.type == `MTAR_REGULAR`). It will ignore the `type` field and write the "regular file" type into the tar archive.
|
||||
|
||||
To write any other kind of entry (directories, special files), use `minitar_write_special_entry`.
|
||||
|
||||
This function returns 0 on success.
|
||||
|
||||
### minitar_write_special_entry
|
||||
`int minitar_write_special_entry(struct minitar_w* mp, const struct minitar_entry_metadata* metadata)`
|
||||
|
||||
Writes a special file entry (anything that does not have contents, so directories or special files) into a `struct minitar_w` which should be initialized by a previous call to `minitar_open_w()`.
|
||||
|
||||
This function only writes a header (generated from the metadata). The `size` field is written as 0, no matter what its original value was.
|
||||
|
||||
This function does not write entries for regular files (metadata.type == `MTAR_REGULAR`). Trying to do so will result in minitar panicking (see [error handling](../README.md#error-handling)). To write regular files, use `minitar_write_file_entry`.
|
||||
|
||||
This function returns 0 on success.
|
||||
|
||||
### minitar_rewind
|
||||
`void minitar_rewind(struct minitar* mp)`
|
||||
|
||||
@ -53,7 +84,7 @@ This function can be called as many times as desired, and at any given point in
|
||||
|
||||
This function returns the number of bytes read, or 0 on error. 0 might also be a successful return value (if `max` is 0 or the entry's size is 0, for example), which means `errno` should be checked to see if 0 means error or simply 0 bytes read.
|
||||
|
||||
`minitar_read_contents()` only reads up to `metadata.size`, regardless of the value in `max`.
|
||||
`minitar_read_contents()` will never read more than `metadata.size`, regardless of the value in `max`. (so, if `max == SIZE_MAX`, `minitar_read_contents()` will always read `metadata.size` bytes).
|
||||
|
||||
The contents are not null-terminated. If you want null-termination (keep in mind the contents might not be ASCII and might contain null bytes before the end), just do `buf[nread] = 0;`. In that case, the value of `max` should be one less than the size of the buffer, to make sure the zero byte is not written past the end of `buf` if `max` bytes are read.
|
||||
|
||||
@ -64,6 +95,13 @@ Closes the tar archive file `mp` points to. The pointer passed to `minitar_close
|
||||
|
||||
Returns 0 on success, everything else is failure and you should check `errno`.
|
||||
|
||||
### minitar_close_w
|
||||
`int minitar_close_w(struct minitar_w* mp)`
|
||||
|
||||
Closes the tar archive file `mp` points to. The pointer passed to `minitar_close_w()` should be initialized by a previous call to `minitar_open_w()`.
|
||||
|
||||
Returns 0 on success, everything else is failure and you should check `errno`.
|
||||
|
||||
## Types
|
||||
|
||||
### minitar_file_type
|
||||
@ -85,7 +123,14 @@ This enum lists all supported file types:
|
||||
|
||||
`MTAR_CHRDEV`: Character devices
|
||||
|
||||
Other file types supported in tar archives, such as block/character devices or FIFOs, are not supported and minitar will throw an error when encountering one of them. This behavior can be controlled by passing `-DMINITAR_IGNORE_UNSUPPORTED_TYPES=ON` to CMake when configuring, which will make minitar silently ignore such entries instead of panicking.
|
||||
### minitar_write_mode
|
||||
`enum minitar_write_mode`
|
||||
|
||||
This enum tells `minitar_open_w` what to do if the chosen archive path already exists:
|
||||
|
||||
`MTAR_OVERWRITE`: Overwrite the archive
|
||||
|
||||
`MTAR_APPEND`: Add new entries to the end of it
|
||||
|
||||
### minitar_entry_metadata
|
||||
`struct minitar_entry_metadata`
|
||||
@ -125,4 +170,14 @@ An entry in a tar archive. Fields:
|
||||
|
||||
`metadata`: The entry's metadata. (`struct minitar_entry_metadata`)
|
||||
|
||||
`_internal`: Reserved for internal use. (`struct minitar_entry_internal`)
|
||||
`_internal`: Reserved for internal use. (`struct minitar_entry_internal`)
|
||||
|
||||
### minitar
|
||||
`struct minitar`
|
||||
|
||||
An archive handle for the "reading" API. To write to an archive, use `struct minitar_w` and `minitar_open_w()` instead.
|
||||
|
||||
### minitar_w
|
||||
`struct minitar_w`
|
||||
|
||||
An archive handle for the "writing" API. To read from an archive, use `struct minitar` and `minitar_open()` instead.
|
@ -4,4 +4,9 @@ target_link_libraries(list PRIVATE minitar)
|
||||
add_executable(untar EXCLUDE_FROM_ALL untar.c)
|
||||
target_link_libraries(untar PRIVATE minitar)
|
||||
|
||||
add_custom_target(examples DEPENDS list untar)
|
||||
add_executable(pack EXCLUDE_FROM_ALL pack.c)
|
||||
target_link_libraries(pack PRIVATE minitar)
|
||||
|
||||
add_custom_target(examples DEPENDS list untar pack)
|
||||
add_custom_target(examples-posix DEPENDS list untar pack)
|
||||
add_custom_target(examples-windows DEPENDS list)
|
@ -24,9 +24,9 @@ int main(int argc, char** argv)
|
||||
}
|
||||
struct minitar_entry entry;
|
||||
do {
|
||||
if (minitar_read_entry(&mp, &entry) == 0) { printf("%s\n", entry.metadata.path); }
|
||||
if (minitar_read_entry(&mp, &entry) == 0) { printf("%s (%s, %zu bytes, mode %o)\n", entry.metadata.path, entry.metadata.name, entry.metadata.size, entry.metadata.mode); }
|
||||
else
|
||||
break;
|
||||
} while (1);
|
||||
minitar_close(&mp);
|
||||
}
|
||||
}
|
||||
|
100
examples/pack.c
Normal file
100
examples/pack.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2023, apio.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* pack.c: Example utility which creates a tar archive (POSIX only).
|
||||
*/
|
||||
|
||||
#define _XOPEN_SOURCE 700
|
||||
#include <minitar.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [output] files\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
struct minitar_w mp;
|
||||
if (minitar_open_w(argv[1], &mp, MTAR_OVERWRITE) != 0)
|
||||
{
|
||||
perror(argv[1]);
|
||||
return 1;
|
||||
}
|
||||
int exit_status = 0;
|
||||
int arg = 2;
|
||||
while (arg < argc)
|
||||
{
|
||||
FILE* fp = fopen(argv[arg], "r");
|
||||
if (!fp)
|
||||
{
|
||||
perror("fopen");
|
||||
exit_status = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the file length.
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t length = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
char* buf = malloc(length);
|
||||
if (!buf)
|
||||
{
|
||||
perror("malloc");
|
||||
fclose(fp);
|
||||
exit_status = 1;
|
||||
break;
|
||||
}
|
||||
fread(buf, 1, length, fp);
|
||||
if (ferror(fp))
|
||||
{
|
||||
perror("fread");
|
||||
goto err;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
int rc = fstat(fileno(fp), &st);
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("fstat");
|
||||
goto err;
|
||||
}
|
||||
|
||||
struct minitar_entry_metadata metadata;
|
||||
strncpy(metadata.path, argv[arg], sizeof(metadata.path));
|
||||
metadata.uid = st.st_uid;
|
||||
metadata.gid = st.st_gid;
|
||||
metadata.mtime = st.st_mtime;
|
||||
metadata.size = length;
|
||||
metadata.type = MTAR_REGULAR;
|
||||
metadata.mode = st.st_mode & ~S_IFMT;
|
||||
|
||||
rc = minitar_write_file_entry(&mp, &metadata, buf);
|
||||
free(buf);
|
||||
fclose(fp);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
perror("write entry failed");
|
||||
exit_status = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
arg++;
|
||||
continue;
|
||||
|
||||
err:
|
||||
free(buf);
|
||||
fclose(fp);
|
||||
exit_status = 1;
|
||||
break;
|
||||
}
|
||||
minitar_close_w(&mp);
|
||||
return exit_status;
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
#define _XOPEN_SOURCE 700
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <minitar.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -17,8 +18,37 @@
|
||||
#include <sys/sysmacros.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int create_parent_recursively(const char* path)
|
||||
{
|
||||
char* path_copy = strdup(path);
|
||||
if (!path_copy) return -1;
|
||||
|
||||
char* parent = dirname(path_copy);
|
||||
|
||||
create:
|
||||
if (mkdir(parent, 0755) < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
create_parent_recursively(parent);
|
||||
goto create;
|
||||
}
|
||||
|
||||
if (errno == EEXIST) goto success;
|
||||
|
||||
free(path_copy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
success:
|
||||
free(path_copy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int untar_file(const struct minitar_entry* entry, const void* buf)
|
||||
{
|
||||
if (create_parent_recursively(entry->metadata.path) < 0) return 1;
|
||||
|
||||
int fd = open(entry->metadata.path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
|
||||
if (fd < 0) return 1;
|
||||
|
||||
@ -31,6 +61,8 @@ static int untar_file(const struct minitar_entry* entry, const void* buf)
|
||||
|
||||
static int untar_directory(const struct minitar_entry* entry)
|
||||
{
|
||||
if (create_parent_recursively(entry->metadata.path) < 0) return 1;
|
||||
|
||||
if (mkdir(entry->metadata.path, entry->metadata.mode) < 0) return 1;
|
||||
|
||||
return 0;
|
||||
@ -56,6 +88,7 @@ int main(int argc, char** argv)
|
||||
{
|
||||
if (entry.metadata.type == MTAR_DIRECTORY)
|
||||
{
|
||||
if (!strcmp(entry.metadata.name, ".") || !strcmp(entry.metadata.name, "..")) continue;
|
||||
int status = untar_directory(&entry);
|
||||
if (status != 0)
|
||||
{
|
||||
@ -93,10 +126,13 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (entry.metadata.type == MTAR_SYMLINK)
|
||||
{
|
||||
if (create_parent_recursively(entry.metadata.path) < 0) goto symlink_err;
|
||||
|
||||
int status = symlink(entry.metadata.link, entry.metadata.path);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
symlink_err:
|
||||
fprintf(stderr, "Failed to create symlink %s: %s\n", entry.metadata.path, strerror(errno));
|
||||
exit_status = 1;
|
||||
break;
|
||||
@ -106,10 +142,13 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (entry.metadata.type == MTAR_HARDLINK)
|
||||
{
|
||||
if (create_parent_recursively(entry.metadata.path) < 0) goto hardlink_err;
|
||||
|
||||
int status = link(entry.metadata.link, entry.metadata.path);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
hardlink_err:
|
||||
fprintf(stderr, "Failed to create hard link %s: %s\n", entry.metadata.path, strerror(errno));
|
||||
exit_status = 1;
|
||||
break;
|
||||
@ -119,42 +158,57 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (entry.metadata.type == MTAR_FIFO)
|
||||
{
|
||||
#ifndef __luna__
|
||||
if (create_parent_recursively(entry.metadata.path) < 0) goto fifo_err;
|
||||
|
||||
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFIFO, 0);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
fifo_err:
|
||||
fprintf(stderr, "Failed to create FIFO %s: %s\n", entry.metadata.path, strerror(errno));
|
||||
exit_status = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("fifo %s\n", entry.metadata.path);
|
||||
}
|
||||
else if (entry.metadata.type == MTAR_BLKDEV)
|
||||
{
|
||||
#ifndef __luna__
|
||||
if (create_parent_recursively(entry.metadata.path) < 0) goto blkdev_err;
|
||||
|
||||
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFBLK,
|
||||
makedev(entry.metadata.devmajor, entry.metadata.devminor));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
blkdev_err:
|
||||
fprintf(stderr, "Failed to create block device %s: %s\n", entry.metadata.path, strerror(errno));
|
||||
exit_status = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("blkdev %s (%u:%u)\n", entry.metadata.path, entry.metadata.devmajor, entry.metadata.devminor);
|
||||
}
|
||||
else if (entry.metadata.type == MTAR_CHRDEV)
|
||||
{
|
||||
#ifndef __luna__
|
||||
if (create_parent_recursively(entry.metadata.path) < 0) goto chrdev_err;
|
||||
|
||||
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFCHR,
|
||||
makedev(entry.metadata.devmajor, entry.metadata.devminor));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
chrdev_err:
|
||||
fprintf(stderr, "Failed to create character device %s: %s\n", entry.metadata.path, strerror(errno));
|
||||
exit_status = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("chrdev %s (%u:%u)\n", entry.metadata.path, entry.metadata.devmajor, entry.metadata.devminor);
|
||||
}
|
||||
@ -170,4 +224,4 @@ int main(int argc, char** argv)
|
||||
} while (1);
|
||||
minitar_close(&mp);
|
||||
return exit_status;
|
||||
}
|
||||
}
|
||||
|
25
minitar.h
25
minitar.h
@ -10,13 +10,32 @@
|
||||
#define MINITAR_H
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef unsigned int mode_t;
|
||||
typedef unsigned int gid_t;
|
||||
typedef unsigned int uid_t;
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
struct minitar
|
||||
{
|
||||
FILE* stream;
|
||||
};
|
||||
|
||||
struct minitar_w
|
||||
{
|
||||
FILE* stream;
|
||||
};
|
||||
|
||||
enum minitar_write_mode
|
||||
{
|
||||
MTAR_APPEND,
|
||||
MTAR_OVERWRITE
|
||||
};
|
||||
|
||||
enum minitar_file_type
|
||||
{
|
||||
MTAR_REGULAR,
|
||||
@ -36,7 +55,7 @@ struct minitar_entry_internal
|
||||
struct minitar_entry_metadata
|
||||
{
|
||||
char path[257];
|
||||
char name[128];
|
||||
char name[101];
|
||||
char link[101];
|
||||
mode_t mode;
|
||||
uid_t uid;
|
||||
@ -62,13 +81,17 @@ extern "C"
|
||||
#endif
|
||||
|
||||
int minitar_open(const char* pathname, struct minitar* out);
|
||||
int minitar_open_w(const char* pathname, struct minitar_w* out, enum minitar_write_mode mode);
|
||||
int minitar_read_entry(struct minitar* mp, struct minitar_entry* out);
|
||||
int minitar_write_file_entry(struct minitar_w* mp, const struct minitar_entry_metadata* metadata, char* buf);
|
||||
int minitar_write_special_entry(struct minitar_w* mp, const struct minitar_entry_metadata* metadata);
|
||||
void minitar_rewind(struct minitar* mp);
|
||||
int minitar_find_by_name(struct minitar* mp, const char* name, struct minitar_entry* out);
|
||||
int minitar_find_by_path(struct minitar* mp, const char* path, struct minitar_entry* out);
|
||||
int minitar_find_any_of(struct minitar* mp, enum minitar_file_type type, struct minitar_entry* out);
|
||||
size_t minitar_read_contents(struct minitar* mp, const struct minitar_entry* entry, char* buf, size_t max);
|
||||
int minitar_close(struct minitar* mp);
|
||||
int minitar_close_w(struct minitar_w* mp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
69
src/tar.c
69
src/tar.c
@ -11,10 +11,18 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef __TINYC__
|
||||
#include <stdnoreturn.h>
|
||||
#else
|
||||
#define noreturn _Noreturn
|
||||
#endif
|
||||
|
||||
// all of these are defined in util.c
|
||||
int minitar_read_header(struct minitar*, struct tar_header*);
|
||||
noreturn void minitar_handle_panic(const char*);
|
||||
int minitar_validate_header(const struct tar_header*);
|
||||
void minitar_parse_metadata_from_tar_header(const struct tar_header*, struct minitar_entry_metadata*);
|
||||
void minitar_construct_header_from_metadata(struct tar_header*, const struct minitar_entry_metadata*);
|
||||
size_t minitar_align_up_to_block_size(size_t);
|
||||
|
||||
int minitar_open(const char* pathname, struct minitar* out)
|
||||
@ -27,11 +35,31 @@ int minitar_open(const char* pathname, struct minitar* out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int minitar_open_w(const char* pathname, struct minitar_w* out, enum minitar_write_mode mode)
|
||||
{
|
||||
const char* mode_string;
|
||||
switch (mode)
|
||||
{
|
||||
case MTAR_APPEND: mode_string = "ab"; break;
|
||||
case MTAR_OVERWRITE: mode_string = "wb"; break;
|
||||
default: minitar_handle_panic("mode passed to minitar_open_w is not supported");
|
||||
}
|
||||
FILE* fp = fopen(pathname, mode_string);
|
||||
if (!fp) return -1;
|
||||
out->stream = fp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int minitar_close(struct minitar* mp)
|
||||
{
|
||||
return fclose(mp->stream);
|
||||
}
|
||||
|
||||
int minitar_close_w(struct minitar_w* mp)
|
||||
{
|
||||
return fclose(mp->stream);
|
||||
}
|
||||
|
||||
// Try to read a valid header, and construct an entry from it. If the 512-byte block at the current read offset is not a
|
||||
// valid header, valid is set to 0 so we can try again with the next block. In any other case, valid is set to 1. This
|
||||
// helps distinguish valid return values, EOF, and invalid headers that we should just skip.
|
||||
@ -74,6 +102,47 @@ int minitar_read_entry(struct minitar* mp, struct minitar_entry* out)
|
||||
return result;
|
||||
}
|
||||
|
||||
int minitar_write_file_entry(struct minitar_w* mp, const struct minitar_entry_metadata* metadata, char* buf)
|
||||
{
|
||||
struct minitar_entry_metadata meta = *metadata;
|
||||
meta.type = MTAR_REGULAR;
|
||||
|
||||
struct tar_header hdr;
|
||||
minitar_construct_header_from_metadata(&hdr, &meta);
|
||||
// Write the header.
|
||||
size_t nwrite = fwrite(&hdr, sizeof(hdr), 1, mp->stream);
|
||||
if (nwrite == 0 && ferror(mp->stream)) return -1;
|
||||
|
||||
// Write the file data.
|
||||
nwrite = fwrite(buf, 1, meta.size, mp->stream);
|
||||
if (nwrite == 0 && ferror(mp->stream)) return -1;
|
||||
|
||||
char zeroes[512];
|
||||
memset(zeroes, 0, sizeof(zeroes));
|
||||
|
||||
// Pad with zeroes to finish a block (512 bytes).
|
||||
size_t nzero = minitar_align_up_to_block_size(meta.size) - meta.size;
|
||||
nwrite = fwrite(zeroes, 1, nzero, mp->stream);
|
||||
if (nwrite == 0 && ferror(mp->stream)) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int minitar_write_special_entry(struct minitar_w* mp, const struct minitar_entry_metadata* metadata)
|
||||
{
|
||||
struct minitar_entry_metadata meta = *metadata;
|
||||
if (meta.type == MTAR_REGULAR)
|
||||
minitar_handle_panic("Trying to write a special entry, yet MTAR_REGULAR passed as the entry type");
|
||||
meta.size = 0;
|
||||
|
||||
struct tar_header hdr;
|
||||
minitar_construct_header_from_metadata(&hdr, &meta);
|
||||
size_t nwrite = fwrite(&hdr, sizeof(hdr), 1, mp->stream);
|
||||
if (nwrite == 0 && ferror(mp->stream)) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void minitar_rewind(struct minitar* mp)
|
||||
{
|
||||
rewind(mp->stream);
|
||||
|
53
src/util.c
53
src/util.c
@ -13,7 +13,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef __TINYC__
|
||||
#include <stdnoreturn.h>
|
||||
@ -21,7 +20,7 @@
|
||||
#define noreturn _Noreturn
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__TINYC__)
|
||||
#if !defined(_WIN32) && !defined(__TINYC__)
|
||||
#define WEAK __attribute__((weak))
|
||||
#else
|
||||
#define WEAK
|
||||
@ -234,6 +233,56 @@ uint32_t minitar_checksum_header(const struct tar_header* hdr)
|
||||
return sum;
|
||||
}
|
||||
|
||||
void minitar_construct_header_from_metadata(struct tar_header* hdr, const struct minitar_entry_metadata* metadata)
|
||||
{
|
||||
if (strlen(metadata->path) > 100)
|
||||
{
|
||||
minitar_handle_panic("FIXME: pathnames over 100 (using the prefix field) are unsupported for now");
|
||||
}
|
||||
|
||||
// We intentionally want strncpy to not write a null terminator here if the path field is 100 bytes long.
|
||||
strncpy(hdr->name, metadata->path, 100);
|
||||
|
||||
snprintf(hdr->mode, 8, "%.7o", metadata->mode);
|
||||
snprintf(hdr->uid, 8, "%.7o", metadata->uid);
|
||||
snprintf(hdr->gid, 8, "%.7o", metadata->gid);
|
||||
|
||||
// snprintf will write the null terminator past the size field. We don't care, as we will overwrite that zero later.
|
||||
snprintf(hdr->size, 13, "%.12zo", metadata->size);
|
||||
// Same here.
|
||||
snprintf(hdr->mtime, 13, "%.12llo", (long long)metadata->mtime);
|
||||
|
||||
switch (metadata->type)
|
||||
{
|
||||
case MTAR_REGULAR: hdr->typeflag = '0'; break;
|
||||
case MTAR_HARDLINK: hdr->typeflag = '1'; break;
|
||||
case MTAR_SYMLINK: hdr->typeflag = '2'; break;
|
||||
case MTAR_CHRDEV: hdr->typeflag = '3'; break;
|
||||
case MTAR_BLKDEV: hdr->typeflag = '4'; break;
|
||||
case MTAR_DIRECTORY: hdr->typeflag = '5'; break;
|
||||
case MTAR_FIFO: hdr->typeflag = '6'; break;
|
||||
}
|
||||
|
||||
strncpy(hdr->linkname, metadata->link, 100);
|
||||
|
||||
memcpy(hdr->magic, "ustar", 6);
|
||||
|
||||
hdr->version[0] = '0';
|
||||
hdr->version[1] = '0';
|
||||
|
||||
strncpy(hdr->uname, metadata->uname, 32);
|
||||
strncpy(hdr->gname, metadata->gname, 32);
|
||||
|
||||
snprintf(hdr->devmajor, 8, "%.7o", metadata->devmajor);
|
||||
snprintf(hdr->devminor, 8, "%.7o", metadata->devminor);
|
||||
|
||||
memset(hdr->prefix, 0, sizeof(hdr->prefix));
|
||||
memset(hdr->padding, 0, sizeof(hdr->padding));
|
||||
|
||||
uint32_t checksum = minitar_checksum_header(hdr);
|
||||
snprintf(hdr->chksum, 8, "%.7o", checksum);
|
||||
}
|
||||
|
||||
int minitar_validate_header(const struct tar_header* hdr)
|
||||
{
|
||||
if (hdr->typeflag != '\0' && hdr->typeflag != '0' && hdr->typeflag != '1' && hdr->typeflag != '2' &&
|
||||
|
Loading…
Reference in New Issue
Block a user