Compare commits
23 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 |
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.8..3.22)
|
cmake_minimum_required(VERSION 3.8..3.22)
|
||||||
|
|
||||||
project(minitar LANGUAGES C VERSION 1.7.0)
|
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)
|
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(TARGETS minitar DESTINATION lib)
|
||||||
install(FILES minitar.h DESTINATION include)
|
install(FILES minitar.h DESTINATION include)
|
||||||
|
|
||||||
add_subdirectory(examples)
|
add_subdirectory(examples)
|
||||||
|
@ -43,7 +43,7 @@ See [examples](examples/) for more examples using minitar.
|
|||||||
|
|
||||||
## Project structure
|
## 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
|
## Documentation
|
||||||
|
|
||||||
@ -75,4 +75,4 @@ pub extern "C" fn minitar_handle_panic(message: *const u8) -> !
|
|||||||
|
|
||||||
## License
|
## 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.
|
||||||
|
@ -84,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.
|
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.
|
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.
|
||||||
|
|
||||||
|
@ -7,4 +7,6 @@ target_link_libraries(untar PRIVATE minitar)
|
|||||||
add_executable(pack EXCLUDE_FROM_ALL pack.c)
|
add_executable(pack EXCLUDE_FROM_ALL pack.c)
|
||||||
target_link_libraries(pack PRIVATE minitar)
|
target_link_libraries(pack PRIVATE minitar)
|
||||||
|
|
||||||
add_custom_target(examples DEPENDS list untar pack)
|
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;
|
struct minitar_entry entry;
|
||||||
do {
|
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
|
else
|
||||||
break;
|
break;
|
||||||
} while (1);
|
} while (1);
|
||||||
minitar_close(&mp);
|
minitar_close(&mp);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ int main(int argc, char** argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int exit_status = 0;
|
int exit_status = 0;
|
||||||
struct minitar_entry_metadata metadata;
|
|
||||||
int arg = 2;
|
int arg = 2;
|
||||||
while (arg < argc)
|
while (arg < argc)
|
||||||
{
|
{
|
||||||
@ -45,12 +44,18 @@ int main(int argc, char** argv)
|
|||||||
fseek(fp, 0, SEEK_SET);
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
char* buf = malloc(length);
|
char* buf = malloc(length);
|
||||||
|
if (!buf)
|
||||||
|
{
|
||||||
|
perror("malloc");
|
||||||
|
fclose(fp);
|
||||||
|
exit_status = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
fread(buf, 1, length, fp);
|
fread(buf, 1, length, fp);
|
||||||
if (ferror(fp))
|
if (ferror(fp))
|
||||||
{
|
{
|
||||||
perror("fread");
|
perror("fread");
|
||||||
exit_status = 1;
|
goto err;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@ -58,21 +63,21 @@ int main(int argc, char** argv)
|
|||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
{
|
{
|
||||||
perror("fstat");
|
perror("fstat");
|
||||||
exit_status = 1;
|
goto err;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct minitar_entry_metadata metadata;
|
struct minitar_entry_metadata metadata;
|
||||||
strncpy(metadata.path, argv[arg], sizeof(metadata.path));
|
strncpy(metadata.path, argv[arg], sizeof(metadata.path));
|
||||||
metadata.uid = st.st_uid;
|
metadata.uid = st.st_uid;
|
||||||
metadata.gid = st.st_gid;
|
metadata.gid = st.st_gid;
|
||||||
metadata.mtime = st.st_mtim.tv_sec;
|
metadata.mtime = st.st_mtime;
|
||||||
metadata.size = length;
|
metadata.size = length;
|
||||||
metadata.type = MTAR_REGULAR;
|
metadata.type = MTAR_REGULAR;
|
||||||
metadata.mode = st.st_mode & ~S_IFMT;
|
metadata.mode = st.st_mode & ~S_IFMT;
|
||||||
|
|
||||||
rc = minitar_write_file_entry(&mp, &metadata, buf);
|
rc = minitar_write_file_entry(&mp, &metadata, buf);
|
||||||
free(buf);
|
free(buf);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
{
|
{
|
||||||
@ -82,6 +87,13 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
arg++;
|
arg++;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err:
|
||||||
|
free(buf);
|
||||||
|
fclose(fp);
|
||||||
|
exit_status = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
minitar_close_w(&mp);
|
minitar_close_w(&mp);
|
||||||
return exit_status;
|
return exit_status;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#define _XOPEN_SOURCE 700
|
#define _XOPEN_SOURCE 700
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <libgen.h>
|
||||||
#include <minitar.h>
|
#include <minitar.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -17,8 +18,37 @@
|
|||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
#include <unistd.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)
|
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);
|
int fd = open(entry->metadata.path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
|
||||||
if (fd < 0) return 1;
|
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)
|
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;
|
if (mkdir(entry->metadata.path, entry->metadata.mode) < 0) return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -56,6 +88,7 @@ int main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
if (entry.metadata.type == MTAR_DIRECTORY)
|
if (entry.metadata.type == MTAR_DIRECTORY)
|
||||||
{
|
{
|
||||||
|
if (!strcmp(entry.metadata.name, ".") || !strcmp(entry.metadata.name, "..")) continue;
|
||||||
int status = untar_directory(&entry);
|
int status = untar_directory(&entry);
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
@ -93,10 +126,13 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else if (entry.metadata.type == MTAR_SYMLINK)
|
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);
|
int status = symlink(entry.metadata.link, entry.metadata.path);
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
|
symlink_err:
|
||||||
fprintf(stderr, "Failed to create symlink %s: %s\n", entry.metadata.path, strerror(errno));
|
fprintf(stderr, "Failed to create symlink %s: %s\n", entry.metadata.path, strerror(errno));
|
||||||
exit_status = 1;
|
exit_status = 1;
|
||||||
break;
|
break;
|
||||||
@ -106,10 +142,13 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else if (entry.metadata.type == MTAR_HARDLINK)
|
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);
|
int status = link(entry.metadata.link, entry.metadata.path);
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
|
hardlink_err:
|
||||||
fprintf(stderr, "Failed to create hard link %s: %s\n", entry.metadata.path, strerror(errno));
|
fprintf(stderr, "Failed to create hard link %s: %s\n", entry.metadata.path, strerror(errno));
|
||||||
exit_status = 1;
|
exit_status = 1;
|
||||||
break;
|
break;
|
||||||
@ -119,42 +158,57 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else if (entry.metadata.type == MTAR_FIFO)
|
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);
|
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFIFO, 0);
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
|
fifo_err:
|
||||||
fprintf(stderr, "Failed to create FIFO %s: %s\n", entry.metadata.path, strerror(errno));
|
fprintf(stderr, "Failed to create FIFO %s: %s\n", entry.metadata.path, strerror(errno));
|
||||||
exit_status = 1;
|
exit_status = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
printf("fifo %s\n", entry.metadata.path);
|
printf("fifo %s\n", entry.metadata.path);
|
||||||
}
|
}
|
||||||
else if (entry.metadata.type == MTAR_BLKDEV)
|
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,
|
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFBLK,
|
||||||
makedev(entry.metadata.devmajor, entry.metadata.devminor));
|
makedev(entry.metadata.devmajor, entry.metadata.devminor));
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
|
blkdev_err:
|
||||||
fprintf(stderr, "Failed to create block device %s: %s\n", entry.metadata.path, strerror(errno));
|
fprintf(stderr, "Failed to create block device %s: %s\n", entry.metadata.path, strerror(errno));
|
||||||
exit_status = 1;
|
exit_status = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
printf("blkdev %s (%u:%u)\n", entry.metadata.path, entry.metadata.devmajor, entry.metadata.devminor);
|
printf("blkdev %s (%u:%u)\n", entry.metadata.path, entry.metadata.devmajor, entry.metadata.devminor);
|
||||||
}
|
}
|
||||||
else if (entry.metadata.type == MTAR_CHRDEV)
|
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,
|
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFCHR,
|
||||||
makedev(entry.metadata.devmajor, entry.metadata.devminor));
|
makedev(entry.metadata.devmajor, entry.metadata.devminor));
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
|
chrdev_err:
|
||||||
fprintf(stderr, "Failed to create character device %s: %s\n", entry.metadata.path, strerror(errno));
|
fprintf(stderr, "Failed to create character device %s: %s\n", entry.metadata.path, strerror(errno));
|
||||||
exit_status = 1;
|
exit_status = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
printf("chrdev %s (%u:%u)\n", entry.metadata.path, entry.metadata.devmajor, entry.metadata.devminor);
|
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);
|
} while (1);
|
||||||
minitar_close(&mp);
|
minitar_close(&mp);
|
||||||
return exit_status;
|
return exit_status;
|
||||||
}
|
}
|
||||||
|
10
minitar.h
10
minitar.h
@ -10,7 +10,15 @@
|
|||||||
#define MINITAR_H
|
#define MINITAR_H
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.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>
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct minitar
|
struct minitar
|
||||||
{
|
{
|
||||||
@ -47,7 +55,7 @@ struct minitar_entry_internal
|
|||||||
struct minitar_entry_metadata
|
struct minitar_entry_metadata
|
||||||
{
|
{
|
||||||
char path[257];
|
char path[257];
|
||||||
char name[128];
|
char name[101];
|
||||||
char link[101];
|
char link[101];
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
|
@ -120,7 +120,7 @@ int minitar_write_file_entry(struct minitar_w* mp, const struct minitar_entry_me
|
|||||||
char zeroes[512];
|
char zeroes[512];
|
||||||
memset(zeroes, 0, sizeof(zeroes));
|
memset(zeroes, 0, sizeof(zeroes));
|
||||||
|
|
||||||
// Write as many zeroes as necessary to finish a block.
|
// Pad with zeroes to finish a block (512 bytes).
|
||||||
size_t nzero = minitar_align_up_to_block_size(meta.size) - meta.size;
|
size_t nzero = minitar_align_up_to_block_size(meta.size) - meta.size;
|
||||||
nwrite = fwrite(zeroes, 1, nzero, mp->stream);
|
nwrite = fwrite(zeroes, 1, nzero, mp->stream);
|
||||||
if (nwrite == 0 && ferror(mp->stream)) return -1;
|
if (nwrite == 0 && ferror(mp->stream)) return -1;
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#ifndef __TINYC__
|
#ifndef __TINYC__
|
||||||
#include <stdnoreturn.h>
|
#include <stdnoreturn.h>
|
||||||
@ -21,7 +20,7 @@
|
|||||||
#define noreturn _Noreturn
|
#define noreturn _Noreturn
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_MSC_VER) && !defined(__TINYC__)
|
#if !defined(_WIN32) && !defined(__TINYC__)
|
||||||
#define WEAK __attribute__((weak))
|
#define WEAK __attribute__((weak))
|
||||||
#else
|
#else
|
||||||
#define WEAK
|
#define WEAK
|
||||||
@ -251,7 +250,7 @@ void minitar_construct_header_from_metadata(struct tar_header* hdr, const struct
|
|||||||
// snprintf will write the null terminator past the size field. We don't care, as we will overwrite that zero later.
|
// 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);
|
snprintf(hdr->size, 13, "%.12zo", metadata->size);
|
||||||
// Same here.
|
// Same here.
|
||||||
snprintf(hdr->mtime, 13, "%.12lo", metadata->mtime);
|
snprintf(hdr->mtime, 13, "%.12llo", (long long)metadata->mtime);
|
||||||
|
|
||||||
switch (metadata->type)
|
switch (metadata->type)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user