Compare commits

...

17 Commits
1.7.1 ... main

Author SHA1 Message Date
1ce643b0c8
chore: bump patch version 2023-12-12 23:29:23 +01:00
ae7bf076dc
fix: Avoid leaking all kinds of stuff in examples/pack
Apart from potentionally leaking malloced memory on error,
which isn't too bad since we immediately exit afterwards,
we were leaving all opened files dangling, as fclose() was never called.
2023-12-12 23:27:05 +01:00
73c4dce573
fix: Functions implemented in tar.c are no longer implemented in the README 2023-06-18 21:09:35 +02:00
7ef1c80fb6
chore: bump patch version 2023-06-18 20:53:56 +02:00
6b1b8fef55
fix: Don't create . or .. in the untar example 2023-06-17 12:05:13 +02:00
cd0c5df5f5
chore: bump patch version 2023-05-27 20:02:46 +02:00
1644ab59eb
fix: Add support for my own OS to examples
Luna doesn't support FIFOs, and special device files are
automatically created by the kernel, so there is no mknod().
2023-05-27 19:58:23 +02:00
f12f58bacf
fix: Make the untar example create parent directories for all entry types 2023-03-08 17:05:10 +01:00
702348a365
chore: bump patch version 2023-02-25 20:31:45 +01:00
f6507e5461
chore: add note to self
untar creates parent dirs only for regular files now, not symlinks/hard links/FIFOs/etc
2023-02-25 20:30:31 +01:00
7e42b10078
fix: Make the untar example create parent directories if they don't exist (like mkdir -p)
This lets it deal with tar archives that are packaged like this:
usr/include/hello.h
usr/include/bye.h
usr/lib/libexample.a
Instead of requiring directory entries:
usr/
usr/include/
usr/include/hello.h (etc)

This helps since some tar archives (our own example tool, pack, for example, does this) are packaged without directory entries.
2023-02-25 20:28:23 +01:00
cb432fd306
docs: Fix potentially confusing statement
First we're saying "reads up to max" and then "always reads up to metadata.size, regardless of max".
We actually mean that we never read more than metadata.size, so that's what this patched text says.
2023-02-18 22:34:17 +01:00
a6d38efc7d
chore: bump patch version 2023-02-15 19:24:54 +01:00
85a6d79151
fix: Shorten the 'name' field in minitar_entry_metadata
Sure, 256 characters might fit in 'path', but because of the way
paths are stored in a tar archive, basenames cannot exceed 100
characters. So, adding space for a null-terminator, this reduces
our 'name' field from 128 bytes to 101.

This change is backwards-compatible since any reasonable application
should not depend on the name field being 128 bytes (this was never
mentioned in any documentation, API.md describes it as 'char[]' without
a fixed length). sizeof(metadata.name) should always be used instead.
2023-02-09 16:41:26 +01:00
320231c70b
fix: Make the 'list' example show more metadata 2023-02-09 16:40:57 +01:00
7571ef42c6
fix: Cast time_t to a known type for snprintf() 2023-02-06 23:18:05 +01:00
88d6fbffb7
fix: Check for _WIN32 instead of _MSC_VER 2023-02-06 23:17:28 +01:00
8 changed files with 77 additions and 16 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.8..3.22)
project(minitar LANGUAGES C VERSION 1.7.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)

View File

@ -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.

View File

@ -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.
`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.

View File

@ -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);
}
}

View File

@ -47,6 +47,7 @@ int main(int argc, char** argv)
if (!buf)
{
perror("malloc");
fclose(fp);
exit_status = 1;
break;
}
@ -54,8 +55,7 @@ int main(int argc, char** argv)
if (ferror(fp))
{
perror("fread");
exit_status = 1;
break;
goto err;
}
struct stat st;
@ -63,21 +63,21 @@ int main(int argc, char** argv)
if (rc < 0)
{
perror("fstat");
exit_status = 1;
break;
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_mtim.tv_sec;
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)
{
@ -87,6 +87,13 @@ int main(int argc, char** argv)
}
arg++;
continue;
err:
free(buf);
fclose(fp);
exit_status = 1;
break;
}
minitar_close_w(&mp);
return exit_status;

View File

@ -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;
}
}

View File

@ -12,7 +12,7 @@
#include <stdio.h>
#include <time.h>
#ifdef _MSC_VER
#ifdef _WIN32
typedef unsigned int mode_t;
typedef unsigned int gid_t;
typedef unsigned int uid_t;
@ -55,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;

View File

@ -20,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
@ -250,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(hdr->size, 13, "%.12zo", metadata->size);
// Same here.
snprintf(hdr->mtime, 13, "%.12lo", metadata->mtime);
snprintf(hdr->mtime, 13, "%.12llo", (long long)metadata->mtime);
switch (metadata->type)
{