Added unit tests. Cleaned up the code and added comments. Added README

This commit is contained in:
Andrei Medar 2020-10-01 00:15:33 +00:00
parent 7e6872ee26
commit efe48ccca6
16 changed files with 353 additions and 3735 deletions

View File

@ -22,33 +22,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED 17)
# Build SAPI library
set(SAPI_ROOT "/usr/local/google/home/amedar/internship/sandboxed-api" CACHE PATH "Path to the Sandboxed API source tree")
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/patches")
#add_custom_command(
# OUTPUT "${PROJECT_BINARY_DIR}/patches/archive.h"
# COMMENT "Applying patches."
# COMMAND cp "${PROJECT_SOURCE_DIR}/patches/header.patch" "${PROJECT_BINARY_DIR}/patches/"
# COMMAND cp "${PROJECT_SOURCE_DIR}/libarchive/libarchive/archive.h" "${PROJECT_BINARY_DIR}/patches/"
# COMMAND cd "${PROJECT_BINARY_DIR}/patches" && patch < header.patch
# COMMAND cp "${PROJECT_BINARY_DIR}/patches/archive.h" "${PROJECT_SOURCE_DIR}/libarchive/libarchive/"
#)
#
#set_property(
# SOURCE
# "${PROJECT_SOURCE_DIR}/libarchive/libarchive/archive.h"
# APPEND PROPERTY OBJECT_DEPENDS
# "${PROJECT_BINARY_DIR}/patches/archive.h"
#)
add_subdirectory(libarchive)
#set_property(SOURCE
# "${PROJECT_SOURCE_DIR}/libarchive/libarchive/archive.h"
# APPEND PROPERTY OBJECT_DEPENDS
# "${PROJECT_SOURCE_DIR}/patches/archive.h"
#)
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL
@ -74,6 +49,5 @@ target_include_directories(libarchive_sapi INTERFACE
"${PROJECT_BINARY_DIR}" # To find the generated SAPI header
)
add_subdirectory(examples)
add_subdirectory(test)

View File

@ -0,0 +1,53 @@
# libarchive Sandboxed API
Sandboxed version of the [libarchive](https://www.libarchive.org/) minitar [example](https://github.com/libarchive/libarchive/blob/master/examples/minitar/minitar.c) using [Sandboxed API](https://github.com/google/sandboxed-api).
## Build
First, run `git submodule update --init --recursive` to update submodules.
After this, run the following commands:
`mkdir -p build && cd build`
`cmake .. -G Ninja`
`cmake --build .`
The example binary file can be found at `build/examples/sapi_minitar` and the unit tests at `build/test/sapi_minitar_test`.
## Patches
TODO
## Examples
In this project, the minitar example is sandboxed.
The code is found in the **examples** directory and is structured as follows:
- **sapi_minitar_main.cc** - ***main*** function of the minitar tool. This is mostly similar to the original example.
- **sapi_minitar.h** and **sapi_minitar.cc** - The two main functions (***create*** and ***extract***) and also other helper functions.
- **sandbox.h** - Custom security policies, depending on the whether the user creates or extracts an archive.
On top of that, unit tests can be found in the **test/minitar_test.cc** file.
## Usage
The unit tests can be executed with `./build/test/sapi_minitar_test`.
The **sapi_minitar** command line tool can be used in the same way as the original example. It is also similar to the [tar](https://man7.org/linux/man-pages/man1/tar.1.html) command, only with fewer options:
`./build/examples/sapi_minitar -[options] [-f file] [files]`
The available options are:
- *c* - Create archive.
- *x* - Extract archive.
- *t* - Extract archive but only print entries.
- *p* - Preserve.
- *v* - Verbose.
- *j* or *y* - Compress with BZIP2.
- *Z* - Default compression.
- *z* - Compress with GZIP.
If no compression method is chosen (in the case of archive creation) the files will only be archived.

View File

@ -38,16 +38,5 @@ add_executable(sapi_minitar
)
target_link_libraries(sapi_minitar PRIVATE
#libarchive_sapi
#sapi::sapi
sapi_minitar_lib
#glog::glog
)
add_executable(minitar
orig/minitar.cc
)
target_link_libraries(minitar PRIVATE
archive
)

View File

@ -1,478 +0,0 @@
/*-
* This file is in the public domain.
* Do with it as you will.
*/
/*-
* This is a compact "tar" program whose primary goal is small size.
* Statically linked, it can be very small indeed. This serves a number
* of goals:
* o a testbed for libarchive (to check for link pollution),
* o a useful tool for space-constrained systems (boot floppies, etc),
* o a place to experiment with new implementation ideas for bsdtar,
* o a small program to demonstrate libarchive usage.
*
* Use the following macros to suppress features:
* NO_BZIP2 - Implies NO_BZIP2_CREATE and NO_BZIP2_EXTRACT
* NO_BZIP2_CREATE - Suppress bzip2 compression support.
* NO_BZIP2_EXTRACT - Suppress bzip2 auto-detection and decompression.
* NO_COMPRESS - Implies NO_COMPRESS_CREATE and NO_COMPRESS_EXTRACT
* NO_COMPRESS_CREATE - Suppress compress(1) compression support
* NO_COMPRESS_EXTRACT - Suppress compress(1) auto-detect and decompression.
* NO_CREATE - Suppress all archive creation support.
* NO_CPIO_EXTRACT - Suppress auto-detect and dearchiving of cpio archives.
* NO_GZIP - Implies NO_GZIP_CREATE and NO_GZIP_EXTRACT
* NO_GZIP_CREATE - Suppress gzip compression support.
* NO_GZIP_EXTRACT - Suppress gzip auto-detection and decompression.
* NO_LOOKUP - Try to avoid getpw/getgr routines, which can be very large
* NO_TAR_EXTRACT - Suppress tar extraction
*
* With all of the above macros defined (except NO_TAR_EXTRACT), you
* get a very small program that can recognize and extract essentially
* any uncompressed tar archive. On FreeBSD 5.1, this minimal program
* is under 64k, statically linked, which compares rather favorably to
* main(){printf("hello, world");}
* which is over 60k statically linked on the same operating system.
* Without any of the above macros, you get a static executable of
* about 180k with a lot of very sophisticated modern features.
* Obviously, it's trivial to add support for ISO, Zip, mtree,
* lzma/xz, etc. Just fill in the appropriate setup calls.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
/*
* NO_CREATE implies NO_BZIP2_CREATE and NO_GZIP_CREATE and NO_COMPRESS_CREATE.
*/
#ifdef NO_CREATE
#undef NO_BZIP2_CREATE
#define NO_BZIP2_CREATE
#undef NO_COMPRESS_CREATE
#define NO_COMPRESS_CREATE
#undef NO_GZIP_CREATE
#define NO_GZIP_CREATE
#endif
/*
* The combination of NO_BZIP2_CREATE and NO_BZIP2_EXTRACT is
* equivalent to NO_BZIP2.
*/
#ifdef NO_BZIP2_CREATE
#ifdef NO_BZIP2_EXTRACT
#undef NO_BZIP2
#define NO_BZIP2
#endif
#endif
#ifdef NO_BZIP2
#undef NO_BZIP2_EXTRACT
#define NO_BZIP2_EXTRACT
#undef NO_BZIP2_CREATE
#define NO_BZIP2_CREATE
#endif
/*
* The combination of NO_COMPRESS_CREATE and NO_COMPRESS_EXTRACT is
* equivalent to NO_COMPRESS.
*/
#ifdef NO_COMPRESS_CREATE
#ifdef NO_COMPRESS_EXTRACT
#undef NO_COMPRESS
#define NO_COMPRESS
#endif
#endif
#ifdef NO_COMPRESS
#undef NO_COMPRESS_EXTRACT
#define NO_COMPRESS_EXTRACT
#undef NO_COMPRESS_CREATE
#define NO_COMPRESS_CREATE
#endif
/*
* The combination of NO_GZIP_CREATE and NO_GZIP_EXTRACT is
* equivalent to NO_GZIP.
*/
#ifdef NO_GZIP_CREATE
#ifdef NO_GZIP_EXTRACT
#undef NO_GZIP
#define NO_GZIP
#endif
#endif
#ifdef NO_GZIP
#undef NO_GZIP_EXTRACT
#define NO_GZIP_EXTRACT
#undef NO_GZIP_CREATE
#define NO_GZIP_CREATE
#endif
#ifndef NO_CREATE
static void create(const char *filename, int compress, const char **argv);
#endif
static void errmsg(const char *);
static void extract(const char *filename, int do_extract, int flags);
static int copy_data(struct archive *, struct archive *);
static void msg(const char *);
static void usage(void);
static int verbose = 0;
int
main(int argc, const char **argv)
{
std::cout << "BEGIN\n";
const char *filename = NULL;
int compress, flags, mode, opt;
(void)argc;
mode = 'x';
verbose = 0;
compress = '\0';
flags = ARCHIVE_EXTRACT_TIME;
/* Among other sins, getopt(3) pulls in printf(3). */
while (*++argv != NULL && **argv == '-') {
const char *p = *argv + 1;
while ((opt = *p++) != '\0') {
switch (opt) {
#ifndef NO_CREATE
case 'c':
mode = opt;
break;
#endif
case 'f':
if (*p != '\0')
filename = p;
else
filename = *++argv;
p += strlen(p);
break;
#ifndef NO_BZIP2_CREATE
case 'j':
compress = opt;
break;
#endif
case 'p':
flags |= ARCHIVE_EXTRACT_PERM;
flags |= ARCHIVE_EXTRACT_ACL;
flags |= ARCHIVE_EXTRACT_FFLAGS;
break;
case 't':
mode = opt;
break;
case 'v':
verbose++;
break;
case 'x':
mode = opt;
break;
#ifndef NO_BZIP2_CREATE
case 'y':
compress = opt;
break;
#endif
#ifndef NO_COMPRESS_CREATE
case 'Z':
compress = opt;
break;
#endif
#ifndef NO_GZIP_CREATE
case 'z':
compress = opt;
break;
#endif
default:
usage();
}
}
}
switch (mode) {
#ifndef NO_CREATE
case 'c':
create(filename, compress, argv);
break;
#endif
case 't':
extract(filename, 0, flags);
break;
case 'x':
extract(filename, 1, flags);
break;
}
return (0);
}
#ifndef NO_CREATE
static char buff[16384];
static void
create(const char *filename, int compress, const char **argv)
{
std::cout << "CREATE FILENAME=" << filename << std::endl;
struct archive *a;
struct archive_entry *entry;
ssize_t len;
int fd;
a = archive_write_new();
switch (compress) {
#ifndef NO_BZIP2_CREATE
case 'j': case 'y':
archive_write_add_filter_bzip2(a);
break;
#endif
#ifndef NO_COMPRESS_CREATE
case 'Z':
archive_write_add_filter_compress(a);
break;
#endif
#ifndef NO_GZIP_CREATE
case 'z':
archive_write_add_filter_gzip(a);
break;
#endif
default:
archive_write_add_filter_none(a);
break;
}
archive_write_set_format_ustar(a);
if (filename != NULL && strcmp(filename, "-") == 0)
filename = NULL;
archive_write_open_filename(a, filename);
while (*argv != NULL) {
std::cout << "handling file = " << *argv << std::endl;
struct archive *disk = archive_read_disk_new();
#ifndef NO_LOOKUP
archive_read_disk_set_standard_lookup(disk);
#endif
int r;
r = archive_read_disk_open(disk, *argv);
if (r != ARCHIVE_OK) {
errmsg(archive_error_string(disk));
errmsg("\n");
exit(1);
}
for (;;) {
int needcr = 0;
entry = archive_entry_new();
r = archive_read_next_header2(disk, entry);
if (r == ARCHIVE_EOF)
break;
if (r != ARCHIVE_OK) {
errmsg(archive_error_string(disk));
errmsg("\n");
exit(1);
}
archive_read_disk_descend(disk);
if (verbose) {
msg("a ");
msg(archive_entry_pathname(entry));
needcr = 1;
}
r = archive_write_header(a, entry);
if (r < ARCHIVE_OK) {
std::cout << " --- error here, code = " << r << " --- " << std::endl;
errmsg(": ");
errmsg(archive_error_string(a));
needcr = 1;
}
if (r == ARCHIVE_FATAL)
exit(1);
if (r > ARCHIVE_FAILED) {
#if 0
/* Ideally, we would be able to use
* the same code to copy a body from
* an archive_read_disk to an
* archive_write that we use for
* copying data from an archive_read
* to an archive_write_disk.
* Unfortunately, this doesn't quite
* work yet. */
copy_data(disk, a);
#else
/* For now, we use a simpler loop to copy data
* into the target archive. */
fd = open(archive_entry_sourcepath(entry), O_RDONLY);
len = read(fd, buff, sizeof(buff));
while (len > 0) {
archive_write_data(a, buff, len);
len = read(fd, buff, sizeof(buff));
}
close(fd);
#endif
}
archive_entry_free(entry);
if (needcr)
msg("\n");
}
archive_read_close(disk);
archive_read_free(disk);
argv++;
}
archive_write_close(a);
archive_write_free(a);
}
#endif
static void
extract(const char *filename, int do_extract, int flags)
{
struct archive *a;
struct archive *ext;
struct archive_entry *entry;
int r;
a = archive_read_new();
ext = archive_write_disk_new();
archive_write_disk_set_options(ext, flags);
#ifndef NO_BZIP2_EXTRACT
archive_read_support_filter_bzip2(a);
#endif
#ifndef NO_GZIP_EXTRACT
archive_read_support_filter_gzip(a);
#endif
#ifndef NO_COMPRESS_EXTRACT
archive_read_support_filter_compress(a);
#endif
#ifndef NO_TAR_EXTRACT
archive_read_support_format_tar(a);
#endif
#ifndef NO_CPIO_EXTRACT
archive_read_support_format_cpio(a);
#endif
#ifndef NO_LOOKUP
archive_write_disk_set_standard_lookup(ext);
#endif
if (filename != NULL && strcmp(filename, "-") == 0)
filename = NULL;
if ((r = archive_read_open_filename(a, filename, 10240))) {
errmsg(archive_error_string(a));
errmsg("\n");
exit(r);
}
for (;;) {
int needcr = 0;
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
break;
if (r != ARCHIVE_OK) {
errmsg(archive_error_string(a));
errmsg("\n");
exit(1);
}
if (verbose && do_extract)
msg("x ");
if (verbose || !do_extract) {
msg(archive_entry_pathname(entry));
msg(" ");
needcr = 1;
}
if (do_extract) {
r = archive_write_header(ext, entry);
if (r != ARCHIVE_OK) {
errmsg(archive_error_string(a));
needcr = 1;
}
else {
r = copy_data(a, ext);
if (r != ARCHIVE_OK)
needcr = 1;
}
}
if (needcr)
msg("\n");
}
archive_read_close(a);
archive_read_free(a);
archive_write_close(ext);
archive_write_free(ext);
exit(0);
}
static int
copy_data(struct archive *ar, struct archive *aw)
{
int r;
const void *buff;
size_t size;
int64_t offset;
for (;;) {
r = archive_read_data_block(ar, &buff, &size, &offset);
if (r == ARCHIVE_EOF)
return (ARCHIVE_OK);
if (r != ARCHIVE_OK) {
errmsg(archive_error_string(ar));
return (r);
}
r = archive_write_data_block(aw, buff, size, offset);
if (r != ARCHIVE_OK) {
errmsg(archive_error_string(ar));
return (r);
}
}
}
static void
msg(const char *m)
{
write(1, m, strlen(m));
}
static void
errmsg(const char *m)
{
if (m == NULL) {
m = "Error: No error description provided.\n";
}
write(2, m, strlen(m));
}
static void
usage(void)
{
/* Many program options depend on compile options. */
const char *m = "Usage: minitar [-"
#ifndef NO_CREATE
"c"
#endif
#ifndef NO_BZIP2
"j"
#endif
"tvx"
#ifndef NO_BZIP2
"y"
#endif
#ifndef NO_COMPRESS
"Z"
#endif
#ifndef NO_GZIP
"z"
#endif
"] [-f file] [file]\n";
errmsg(m);
exit(1);
}

View File

@ -1,113 +0,0 @@
/*
* This file is in the public domain.
*
* Feel free to use it as you wish.
*/
/*
* This example program reads an archive from stdin (which can be in
* any format recognized by libarchive) and writes certain entries to
* an uncompressed ustar archive on stdout. This is a template for
* many kinds of archive manipulation: converting formats, resetting
* ownership, inserting entries, removing entries, etc.
*
* To compile:
* gcc -Wall -o tarfilter tarfilter.c -larchive -lz -lbz2
*/
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
static void
die(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(1);
}
int
main(int argc, char **argv)
{
char buff[8192];
ssize_t len;
int r;
mode_t m;
struct archive *ina;
struct archive *outa;
struct archive_entry *entry;
/* Read an archive from stdin, with automatic format detection. */
ina = archive_read_new();
if (ina == NULL)
die("Couldn't create archive reader.");
if (archive_read_support_filter_all(ina) != ARCHIVE_OK)
die("Couldn't enable decompression");
if (archive_read_support_format_all(ina) != ARCHIVE_OK)
die("Couldn't enable read formats");
if (archive_read_open_fd(ina, 0, 10240) != ARCHIVE_OK)
die("Couldn't open input archive");
/* Write an uncompressed ustar archive to stdout. */
outa = archive_write_new();
if (outa == NULL)
die("Couldn't create archive writer.");
if (archive_write_set_compression_none(outa) != ARCHIVE_OK)
die("Couldn't enable compression");
if (archive_write_set_format_ustar(outa) != ARCHIVE_OK)
die("Couldn't set output format");
if (archive_write_open_fd(outa, 1) != ARCHIVE_OK)
die("Couldn't open output archive");
/* Examine each entry in the input archive. */
while ((r = archive_read_next_header(ina, &entry)) == ARCHIVE_OK) {
fprintf(stderr, "%s: ", archive_entry_pathname(entry));
/* Skip anything that isn't a regular file. */
if (!S_ISREG(archive_entry_mode(entry))) {
fprintf(stderr, "skipped\n");
continue;
}
/* Make everything owned by root/wheel. */
archive_entry_set_uid(entry, 0);
archive_entry_set_uname(entry, "root");
archive_entry_set_gid(entry, 0);
archive_entry_set_gname(entry, "wheel");
/* Make everything permission 0744, strip SUID, etc. */
m = archive_entry_mode(entry);
archive_entry_set_mode(entry, (m & ~07777) | 0744);
/* Copy input entries to output archive. */
if (archive_write_header(outa, entry) != ARCHIVE_OK)
die("Error writing output archive");
if (archive_entry_size(entry) > 0) {
len = archive_read_data(ina, buff, sizeof(buff));
while (len > 0) {
if (archive_write_data(outa, buff, len) != len)
die("Error writing output archive");
len = archive_read_data(ina, buff, sizeof(buff));
}
if (len < 0)
die("Error reading input archive");
}
fprintf(stderr, "copied\n");
}
if (r != ARCHIVE_EOF)
die("Error reading archive");
/* Close the archives. */
if (archive_read_free(ina) != ARCHIVE_OK)
die("Error closing input archive");
if (archive_write_free(outa) != ARCHIVE_OK)
die("Error closing output archive");
return (0);
}

View File

@ -1,277 +0,0 @@
/*
* This file is in the public domain.
* Use it as you wish.
*/
/*
* This is a compact tar extraction program using libarchive whose
* primary goal is small executable size. Statically linked, it can
* be very small, depending in large part on how cleanly factored your
* system libraries are. Note that this uses the standard libarchive,
* without any special recompilation. The only functional concession
* is that this program uses the uid/gid from the archive instead of
* doing uname/gname lookups. (Add a call to
* archive_write_disk_set_standard_lookup() to enable uname/gname
* lookups, but be aware that this can add 500k or more to a static
* executable, depending on the system libraries, since user/group
* lookups frequently pull in password, YP/LDAP, networking, and DNS
* resolver libraries.)
*
* To build:
* $ gcc -static -Wall -o untar untar.c -larchive
* $ strip untar
*
* NOTE: On some systems, you may need to add additional flags
* to ensure that untar.c is compiled the same way as libarchive
* was compiled. In particular, Linux users will probably
* have to add -D_FILE_OFFSET_BITS=64 to the command line above.
*
* For fun, statically compile the following simple hello.c program
* using the same flags as for untar and compare the size:
*
* #include <stdio.h>
* int main(int argc, char **argv) {
* printf("hello, world\n");
* return(0);
* }
*
* You may be even more surprised by the compiled size of true.c listed here:
*
* int main(int argc, char **argv) {
* return (0);
* }
*
* On a slightly customized FreeBSD 5 system that I used around
* 2005, hello above compiled to 89k compared to untar of 69k. So at
* that time, libarchive's tar reader and extract-to-disk routines
* compiled to less code than printf().
*
* On my FreeBSD development system today (August, 2009):
* hello: 195024 bytes
* true: 194912 bytes
* untar: 259924 bytes
*/
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void errmsg(const char *);
static void extract(const char *filename, int do_extract, int flags);
static void fail(const char *, const char *, int);
static int copy_data(struct archive *, struct archive *);
static void msg(const char *);
static void usage(void);
static void warn(const char *, const char *);
static int verbose = 0;
int
main(int argc, const char **argv)
{
const char *filename = NULL;
int compress, flags, mode, opt;
(void)argc;
mode = 'x';
verbose = 0;
compress = '\0';
flags = ARCHIVE_EXTRACT_TIME;
/* Among other sins, getopt(3) pulls in printf(3). */
while (*++argv != NULL && **argv == '-') {
const char *p = *argv + 1;
while ((opt = *p++) != '\0') {
switch (opt) {
case 'f':
if (*p != '\0')
filename = p;
else
filename = *++argv;
p += strlen(p);
break;
case 'p':
flags |= ARCHIVE_EXTRACT_PERM;
flags |= ARCHIVE_EXTRACT_ACL;
flags |= ARCHIVE_EXTRACT_FFLAGS;
break;
case 't':
mode = opt;
break;
case 'v':
verbose++;
break;
case 'x':
mode = opt;
break;
default:
usage();
}
}
}
std::cout << "begin\n";
switch (mode) {
case 't':
extract(filename, 0, flags);
break;
case 'x':
extract(filename, 1, flags);
break;
}
std::cout << "end";
return (0);
}
static void
extract(const char *filename, int do_extract, int flags)
{
struct archive *a;
struct archive *ext;
struct archive_entry *entry;
int r;
a = archive_read_new();
ext = archive_write_disk_new();
archive_write_disk_set_options(ext, flags);
/*
* Note: archive_write_disk_set_standard_lookup() is useful
* here, but it requires library routines that can add 500k or
* more to a static executable.
*/
archive_read_support_format_tar(a);
/*
* On my system, enabling other archive formats adds 20k-30k
* each. Enabling gzip decompression adds about 20k.
* Enabling bzip2 is more expensive because the libbz2 library
* isn't very well factored.
*/
std::cout <<"AAA\n";
if (filename != NULL && strcmp(filename, "-") == 0)
filename = NULL;
if ((r = archive_read_open_filename(a, filename, 10240)))
fail("archive_read_open_filename()",
archive_error_string(a), r);
std::cout << "BBB\n";
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
break;
if (r != ARCHIVE_OK)
fail("archive_read_next_header()",
archive_error_string(a), 1);
if (verbose && do_extract)
msg("x ");
if (verbose || !do_extract)
msg(archive_entry_pathname(entry));
if (do_extract) {
r = archive_write_header(ext, entry);
if (r != ARCHIVE_OK)
warn("archive_write_header()",
archive_error_string(ext));
else {
copy_data(a, ext);
r = archive_write_finish_entry(ext);
if (r != ARCHIVE_OK)
fail("archive_write_finish_entry()",
archive_error_string(ext), 1);
}
}
if (verbose || !do_extract)
msg("\n");
}
archive_read_close(a);
archive_read_free(a);
archive_write_close(ext);
archive_write_free(ext);
exit(0);
}
static int
copy_data(struct archive *ar, struct archive *aw)
{
int r;
const void *buff;
size_t size;
#if ARCHIVE_VERSION_NUMBER >= 3000000
int64_t offset;
#else
off_t offset;
#endif
for (;;) {
r = archive_read_data_block(ar, &buff, &size, &offset);
if (r == ARCHIVE_EOF)
return (ARCHIVE_OK);
if (r != ARCHIVE_OK)
return (r);
r = archive_write_data_block(aw, buff, size, offset);
if (r != ARCHIVE_OK) {
warn("archive_write_data_block()",
archive_error_string(aw));
return (r);
}
}
}
/*
* These reporting functions use low-level I/O; on some systems, this
* is a significant code reduction. Of course, on many server and
* desktop operating systems, malloc() and even crt rely on printf(),
* which in turn pulls in most of the rest of stdio, so this is not an
* optimization at all there. (If you're going to pay 100k or more
* for printf() anyway, you may as well use it!)
*/
static void
msg(const char *m)
{
write(1, m, strlen(m));
}
static void
errmsg(const char *m)
{
write(2, m, strlen(m));
}
static void
warn(const char *f, const char *m)
{
errmsg(f);
errmsg(" failed: ");
errmsg(m);
errmsg("\n");
}
static void
fail(const char *f, const char *m, int r)
{
warn(f, m);
exit(r);
}
static void
usage(void)
{
const char *m = "Usage: untar [-tvx] [-f file] [file]\n";
errmsg(m);
exit(1);
}

View File

@ -1,20 +1,28 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SAPI_LIBARCHIVE_SANDBOX_H
#define SAPI_LIBARCHIVE_SANDBOX_H
#include <asm/unistd_64.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syscall.h>
#include <unistd.h>
#include "libarchive_sapi.sapi.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sapi_minitar.h"
// #include "sandboxed_api/sandbox2/util/fileops.h"
// When creating an archive, we need read permissions on each of the
// file/directory added in the archive. Also, in order to create the archive, we
// map /output with the basename of the archive. This way, the program can
// map "/output" with the basename of the archive. This way, the program can
// create the file without having access to anything else.
class SapiLibarchiveSandboxCreate : public LibarchiveSandbox {
public:
@ -54,7 +62,7 @@ class SapiLibarchiveSandboxCreate : public LibarchiveSandbox {
__NR_getdents64,
});
// Here we only check whether the entry is a file or a directory.
// We check whether the entry is a file or a directory.
for (const auto& i : files_) {
struct stat s;
stat(i.c_str(), &s);

View File

@ -1,10 +1,23 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sapi_minitar.h"
void create(const char* initial_filename, int compress, const char** argv,
bool verbose /* = true */) {
// We split the filename path into dirname and filename. To the filename we
// prepend "/output/"" so that it will work with the security policy.
std::string abs_path = MakeAbsolutePathAtCWD(std::string(initial_filename));
auto [archive_path, filename_tmp] =
std::move(sandbox2::file::SplitPath(abs_path));
@ -24,16 +37,14 @@ void create(const char* initial_filename, int compress, const char** argv,
std::transform(relative_paths.begin(), relative_paths.end(),
relative_paths.begin(), sandbox2::file::CleanPath);
// At this point, we have the cleaned relative and absolute paths saved
// At this point, we have the relative and absolute paths (cleaned) saved
// in vectors.
// Initialize sandbox and api object
// Initialize sandbox and api objects.
SapiLibarchiveSandboxCreate sandbox(absolute_paths, archive_path);
CHECK(sandbox.Init().ok()) << "Error during sandbox initialization";
LibarchiveApi api(&sandbox);
std::cout << "AJUNGE AICI" << std::endl;
absl::StatusOr<archive*> ret = api.archive_write_new();
CHECK(ret.ok()) << "write_new call failed";
CHECK(ret.value() != NULL) << "Failed to create write archive";
@ -42,8 +53,6 @@ void create(const char* initial_filename, int compress, const char** argv,
// to the client process.
sapi::v::RemotePtr a(ret.value());
std::cout << "AJUNGE AICI" << std::endl;
absl::StatusOr<int> ret2;
switch (compress) {
@ -114,8 +123,6 @@ void create(const char* initial_filename, int compress, const char** argv,
<< CheckStatusAndGetString(api.archive_error_string(&disk), sandbox);
for (;;) {
int needcr = 0;
absl::StatusOr<archive_entry*> ret3;
ret3 = api.archive_entry_new();
@ -139,14 +146,15 @@ void create(const char* initial_filename, int compress, const char** argv,
// After using the absolute path before, we now need to add the pathname
// to the archive entry. This would help store the files by their relative
// paths. However, in the case where a directory is added to the archive,
// all of the files inside of it are addes as well so we replace the
// absolute path prefix with the relative one. Example: we add the folder
// test_files which becomes /absolute/path/test_files and the files inside
// of it will become /absolute/path/test_files/file1 and we change it to
// test_files/file1 so that it is relative. This only changes the pathname
// so that relative paths are preserved.
// paths(similar to the usual tar command).
// However, in the case where a directory is added to the archive,
// all of the files inside of it are added as well so we replace the
// absolute path prefix with the relative one.
// Example:
// we add the folder "test_files" which becomes
// "/absolute/path/test_files" and the files inside of it will become
// similar to "/absolute/path/test_files/file1"
// which we then change to "test_files/file1" so that it is relative.
std::string path_name =
CheckStatusAndGetString(api.archive_entry_pathname(&entry), sandbox);
@ -156,7 +164,6 @@ void create(const char* initial_filename, int compress, const char** argv,
// On top of those changes, we need to remove leading '/' characters
// and also remove everything up to the last occurrence of '../'.
size_t found = path_name.find_first_not_of("/");
if (found != std::string::npos) {
path_name.erase(path_name.begin(), path_name.begin() + found);
@ -174,8 +181,8 @@ void create(const char* initial_filename, int compress, const char** argv,
if (verbose) {
std::cout << CheckStatusAndGetString(api.archive_entry_pathname(&entry),
sandbox);
needcr = 1;
sandbox)
<< std::endl;
}
ret2 = api.archive_write_header(&a, &entry);
@ -183,8 +190,8 @@ void create(const char* initial_filename, int compress, const char** argv,
if (ret2.value() < ARCHIVE_OK) {
std::cout << CheckStatusAndGetString(api.archive_error_string(&a),
sandbox);
needcr = 1;
sandbox)
<< std::endl;
}
CHECK(ret2.value() != ARCHIVE_FATAL)
<< "Unexpected result from write_header call";
@ -204,12 +211,12 @@ void create(const char* initial_filename, int compress, const char** argv,
sapi::v::Array<char> buff(kBuffSize);
sapi::v::UInt ssize(kBuffSize);
// We allocate the buffer remotely and then we can simply use the remote
// pointer.
// We allocate the buffer remotely and then we can simply use the
// remote pointer(with PtrNone).
CHECK(sandbox.Allocate(&buff, true).ok())
<< "Could not allocate remote buffer";
// We can use sapi objects that help us with file descriptors.
// We can use sapi methods that help us with file descriptors.
CHECK(sandbox.TransferToSandboxee(&sapi_fd).ok())
<< "Could not transfer file descriptor";
@ -230,12 +237,7 @@ void create(const char* initial_filename, int compress, const char** argv,
// sapi_fd variable goes out of scope here so both the local and the
// remote file descriptors are closed.
}
CHECK(api.archive_entry_free(&entry).ok()) << "entry_free call failed";
if (needcr) {
std::cout << std::endl;
}
}
ret2 = api.archive_read_close(&disk);
@ -258,7 +260,6 @@ void create(const char* initial_filename, int compress, const char** argv,
void extract(const char* filename, int do_extract, int flags,
bool verbose /* = true */) {
std::cout << "flags = " << flags << std::endl;
std::string tmp_dir;
if (do_extract) {
tmp_dir = CreateTempDirAtCWD();
@ -275,17 +276,17 @@ void extract(const char* filename, int do_extract, int flags,
// We should only delete it if the do_extract flag is true which
// means that this struct is instantiated only in that case.
std::shared_ptr<ExtractTempDirectoryCleanup> cleanup_ptr;
std::unique_ptr<ExtractTempDirectoryCleanup> cleanup_ptr;
if (do_extract) {
cleanup_ptr = std::make_unique<ExtractTempDirectoryCleanup>();
cleanup_ptr = absl::make_unique<ExtractTempDirectoryCleanup>();
cleanup_ptr->dir = tmp_dir;
}
std::string filename_absolute = MakeAbsolutePathAtCWD(filename);
// Initialize sandbox and api objects.
SapiLibarchiveSandboxExtract sandbox(filename_absolute, do_extract, tmp_dir);
CHECK(sandbox.Init().ok()) << "Error during sandbox initialization";
LibarchiveApi api(&sandbox);
absl::StatusOr<archive*> ret = api.archive_read_new();
@ -350,7 +351,6 @@ void extract(const char* filename, int do_extract, int flags,
sandbox);
for (;;) {
int needcr = 0;
sapi::v::IntBase<struct archive_entry*> entry_ptr_tmp(0);
ret2 = api.archive_read_next_header(&a, entry_ptr_tmp.PtrAfter());
@ -372,8 +372,7 @@ void extract(const char* filename, int do_extract, int flags,
if (verbose || !do_extract) {
std::cout << CheckStatusAndGetString(api.archive_entry_pathname(&entry),
sandbox)
<< " ";
needcr = 1;
<< std::endl;
}
if (do_extract) {
@ -383,15 +382,10 @@ void extract(const char* filename, int do_extract, int flags,
if (ret2.value() != ARCHIVE_OK) {
std::cout << CheckStatusAndGetString(api.archive_error_string(&a),
sandbox);
needcr = 1;
} else if (copy_data(&a, &ext, api, sandbox) != ARCHIVE_OK) {
needcr = 1;
} else {
copy_data(&a, &ext, api, sandbox);
}
}
if (needcr) {
std::cout << std::endl;
}
}
ret2 = api.archive_read_close(&a);
@ -411,8 +405,6 @@ void extract(const char* filename, int do_extract, int flags,
CHECK(!ret2.value()) << "Unexpected result from write_free call";
}
// This function is only called from the "extract function". It is still
// isolated in order to not modify the code structure as much.
int copy_data(sapi::v::RemotePtr* ar, sapi::v::RemotePtr* aw,
LibarchiveApi& api, SapiLibarchiveSandboxExtract& sandbox) {
absl::StatusOr<int> ret;

View File

@ -1,43 +1,51 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SAPI_LIBARCHIVE_MINITAR_H
#define SAPI_LIBARCHIVE_MINITAR_H
#include <archive.h>
#include <archive_entry.h>
#include <fcntl.h>
#include <glog/logging.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdlib>
#include <iostream>
#include <memory>
#include "libarchive_sapi.sapi.h"
#include "sandbox.h"
#include "sandboxed_api/sandbox2/util.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
#include "sandboxed_api/sandbox2/util/temp_file.h"
#include "sandboxed_api/var_array.h"
// Creates an archive file at the given filename.
void create(const char* filename, int compress, const char** argv,
bool verbose = true);
// Extracts an archive file. If do_extract is true, the files will
// be created relative to the current working directory. If do_extract
// is false then the function will just print the entries of the archive.
void extract(const char* filename, int do_extract, int flags,
bool verbose = true);
// This function is only called from the "extract function". It is still
// isolated in order to not modify the code structure as much.
int copy_data(sapi::v::RemotePtr* ar, sapi::v::RemotePtr* aw,
LibarchiveApi& api, SapiLibarchiveSandboxExtract& sandbox);
inline constexpr size_t kBlockSize = 10240;
inline constexpr size_t kBuffSize = 16384;
// Converts only one string to an absolute path by prepending the current
// working directory to the relative path
// Converts one string to an absolute path by prepending the current
// working directory to the relative path.
// The path is also cleaned at the end.
std::string MakeAbsolutePathAtCWD(const std::string& path);
// This function takes a status as argument and after checking the status
@ -47,8 +55,8 @@ std::string CheckStatusAndGetString(const absl::StatusOr<char*>& status,
LibarchiveSandbox& sandbox);
// Creates a temporary directory in the current working directory and
// returns the path. This is used in the extract function where the sandbox
// changes the current working directory to this temporary directory.
// returns the path. This is used in the extract function where the sandboxed
// process changes the current working directory to this temporary directory.
std::string CreateTempDirAtCWD();
#endif // SAPI_LIBARCHIVE_MINITAR_H

View File

@ -1,4 +1,22 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file contains the main function from the original minitar example:
// https://github.com/libarchive/libarchive/blob/master/examples/minitar/minitar.c
// Most of the logic is the same, it was only simplified a bit since this is
// only used for the command line tool.
// No sandboxing takes place in this function.
#include <iostream>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,163 +0,0 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "archive_platform.h"
__FBSDID("$FreeBSD: head/lib/libarchive/archive_virtual.c 201098 2009-12-28 02:58:14Z kientzle $");
#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
int
archive_filter_code(struct archive *a, int n)
{
return ((a->vtable->archive_filter_code)(a, n));
}
int
archive_filter_count(struct archive *a)
{
return ((a->vtable->archive_filter_count)(a));
}
const char *
archive_filter_name(struct archive *a, int n)
{
return ((a->vtable->archive_filter_name)(a, n));
}
la_int64_t
archive_filter_bytes(struct archive *a, int n)
{
return ((a->vtable->archive_filter_bytes)(a, n));
}
int
archive_free(struct archive *a)
{
if (a == NULL)
return (ARCHIVE_OK);
return ((a->vtable->archive_free)(a));
}
int
archive_write_close(struct archive *a)
{
return ((a->vtable->archive_close)(a));
}
int
archive_read_close(struct archive *a)
{
return ((a->vtable->archive_close)(a));
}
int
archive_write_fail(struct archive *a)
{
a->state = ARCHIVE_STATE_FATAL;
return a->state;
}
int
archive_write_free(struct archive *a)
{
return archive_free(a);
}
#if ARCHIVE_VERSION_NUMBER < 4000000
/* For backwards compatibility; will be removed with libarchive 4.0. */
int
archive_write_finish(struct archive *a)
{
return archive_write_free(a);
}
#endif
int
archive_read_free(struct archive *a)
{
return archive_free(a);
}
#if ARCHIVE_VERSION_NUMBER < 4000000
/* For backwards compatibility; will be removed with libarchive 4.0. */
int
archive_read_finish(struct archive *a)
{
return archive_read_free(a);
}
#endif
int
archive_write_header(struct archive *a, struct archive_entry *entry)
{
++a->file_count;
return ((a->vtable->archive_write_header)(a, entry));
}
int
archive_write_finish_entry(struct archive *a)
{
return ((a->vtable->archive_write_finish_entry)(a));
}
la_ssize_t
archive_write_data(struct archive *a, const void *buff, size_t s)
{
return ((a->vtable->archive_write_data)(a, buff, s));
}
la_ssize_t
archive_write_data_block(struct archive *a, const void *buff, size_t s,
la_int64_t o)
{
if (a->vtable->archive_write_data_block == NULL) {
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"archive_write_data_block not supported");
a->state = ARCHIVE_STATE_FATAL;
return (ARCHIVE_FATAL);
}
return ((a->vtable->archive_write_data_block)(a, buff, s, o));
}
int
archive_read_next_header(struct archive *a, struct archive_entry **entry)
{
return ((a->vtable->archive_read_next_header)(a, entry));
}
int
archive_read_next_header2(struct archive *a, struct archive_entry *entry)
{
return ((a->vtable->archive_read_next_header2)(a, entry));
}
int
archive_read_data_block(struct archive *a,
const void **buff, size_t *s, la_int64_t *o)
{
return ((a->vtable->archive_read_data_block)(a, buff, s, o));
}

View File

@ -1,163 +0,0 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "archive_platform.h"
__FBSDID("$FreeBSD: head/lib/libarchive/archive_virtual.c 201098 2009-12-28 02:58:14Z kientzle $");
#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
int
archive_filter_code(struct archive *a, int n)
{
return ((a->vtable->archive_filter_code)(a, n));
}
int
archive_filter_count(struct archive *a)
{
return ((a->vtable->archive_filter_count)(a));
}
const char *
archive_filter_name(struct archive *a, int n)
{
return ((a->vtable->archive_filter_name)(a, n));
}
la_int64_t
archive_filter_bytes(struct archive *a, int n)
{
return ((a->vtable->archive_filter_bytes)(a, n));
}
int
archive_free(struct archive *a)
{
if (a == NULL)
return (ARCHIVE_OK);
return ((a->vtable->archive_free)(a));
}
int
archive_write_close(struct archive *a)
{
return ((a->vtable->archive_close)(a));
}
int
archive_read_close(struct archive *a)
{
return ((a->vtable->archive_close)(a));
}
int
archive_write_fail(struct archive *a)
{
a->state = ARCHIVE_STATE_FATAL;
return a->state;
}
int
archive_write_free(struct archive *a)
{
return archive_free(a);
}
#if ARCHIVE_VERSION_NUMBER < 4000000
/* For backwards compatibility; will be removed with libarchive 4.0. */
int
archive_write_finish(struct archive *a)
{
return archive_write_free(a);
}
#endif
int
archive_read_free(struct archive *a)
{
return archive_free(a);
}
#if ARCHIVE_VERSION_NUMBER < 4000000
/* For backwards compatibility; will be removed with libarchive 4.0. */
int
archive_read_finish(struct archive *a)
{
return archive_read_free(a);
}
#endif
int
archive_write_header(struct archive *a, struct archive_entry *entry)
{
++a->file_count;
return ((a->vtable->archive_write_header)(a, entry));
}
int
archive_write_finish_entry(struct archive *a)
{
return ((a->vtable->archive_write_finish_entry)(a));
}
int
archive_write_data(struct archive *a, const void *buff, size_t s)
{
return ((a->vtable->archive_write_data)(a, buff, s));
}
int
archive_write_data_block(struct archive *a, const void *buff, size_t s,
la_int64_t o)
{
if (a->vtable->archive_write_data_block == NULL) {
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"archive_write_data_block not supported");
a->state = ARCHIVE_STATE_FATAL;
return (ARCHIVE_FATAL);
}
return ((a->vtable->archive_write_data_block)(a, buff, s, o));
}
int
archive_read_next_header(struct archive *a, struct archive_entry **entry)
{
return ((a->vtable->archive_read_next_header)(a, entry));
}
int
archive_read_next_header2(struct archive *a, struct archive_entry *entry)
{
return ((a->vtable->archive_read_next_header2)(a, entry));
}
int
archive_read_data_block(struct archive *a,
const void **buff, size_t *s, la_int64_t *o)
{
return ((a->vtable->archive_read_data_block)(a, buff, s, o));
}

View File

@ -1,3 +1,16 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
include(GoogleTest)
enable_testing()
@ -9,7 +22,6 @@ add_executable(sapi_minitar_test
target_link_libraries(sapi_minitar_test PRIVATE
sapi_minitar_lib
gtest
gmock
sapi::test_main
)

View File

@ -1,39 +1,63 @@
// #include <gmock/gmock-more-matchers.h>
#include <gmock/gmock-more-matchers.h>
#include <unistd.h>
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fstream>
#include <string>
#include "absl/strings/string_view.h"
#include "gmock/gmock.h"
#include "build/googletest-src/googlemock/include/gmock/gmock-more-matchers.h"
#include "gtest/gtest.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
#include "sandboxed_api/util/status_matchers.h"
#include "sapi_minitar.h"
// #include "testing/base/public/gunit.h"
// #include "testing/base/public/gunit.h"
using ::sandbox2::file::JoinPath;
using ::testing::Eq;
using ::testing::IsTrue;
using ::testing::StrEq;
using ::sandbox2::file_util::fileops::Exists;
using ::sandbox2::util::VecStringToCharPtrArr;
namespace {
// We will use a fixture class for testing which allows us to override the
// SetUp and TearDown functions. Also, data that needs to be initialized
// or destroyed only once (the test files and directories) will be handled
// in the SetUpTestSuite and TearDownTestSuite functions which are executed
// only once.
// All of the testing data will be placed in a temporary directory and each
// test will have it's own temporary directory. At the end of each test
// and all of the tests, the temporary data is deleted.
class MiniTarTest : public ::testing::Test {
protected:
// Before running the tests, we create a temporary directory which will
// store generated files and directories used for testing.
// The directory will look as follows:
// -file1
// -dir1 - file2
// - dir2 - file3
static void SetUpTestSuite() {
data_dir_ = CreateTempDirAtCWD();
init_wd_ = sandbox2::file_util::fileops::GetCWD();
ASSERT_THAT(Exists(data_dir_, false), IsTrue())
<< "Test data directory was not created";
ASSERT_THAT(chdir(data_dir_.c_str()), Eq(0))
ASSERT_THAT(chdir(data_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
CreateAndWriteToFile("file1");
CreateAndWriteToFile(kFile1_);
ASSERT_THAT(mkdir(kDir1_.data(), 0755), Eq(0)) << "Could not create dir1";
CreateAndWriteToFile(kFile2_);
ASSERT_THAT(mkdir(kDir2_.data(), 0755), Eq(0)) << "Could not create dir2";
CreateAndWriteToFile(kFile3_);
test_count_ = 0;
}
@ -42,7 +66,7 @@ class MiniTarTest : public ::testing::Test {
// The tests have the data directory as their working directory at the end
// so we move to the initial working directory in order to not delete the
// directory that we are inside of.
ASSERT_THAT(chdir(init_wd_.c_str()), Eq(0))
ASSERT_THAT(chdir(init_wd_.data()), Eq(0))
<< "Could not chdir into initial working directory";
EXPECT_THAT(sandbox2::file_util::fileops::DeleteRecursively(data_dir_),
IsTrue)
@ -50,17 +74,19 @@ class MiniTarTest : public ::testing::Test {
}
void SetUp() override {
// We use a unique id based on test count to make sure that files created
// during tests do not overlap.
id_ = "test" + std::to_string(test_count_);
tmp_dir_ = CreateTempDirAtCWD();
ASSERT_THAT(Exists(tmp_dir_, false), IsTrue)
<< "Could not create test specific temporary directory";
ASSERT_THAT(chdir(data_dir_.c_str()), Eq(0))
ASSERT_THAT(chdir(data_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
}
void TearDown() override {
// Move to another directory before deleting the temporary folder
ASSERT_THAT(chdir(data_dir_.c_str()), Eq(0))
// Move to another directory before deleting the temporary folder.
ASSERT_THAT(chdir(data_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
EXPECT_THAT(sandbox2::file_util::fileops::DeleteRecursively(tmp_dir_),
@ -70,8 +96,8 @@ class MiniTarTest : public ::testing::Test {
}
// Creates the file specified and writes the same filename.
// This is done in order to not have completely empty files for the archiving
// step.
// This is done in order to not have completely empty files for the
// archiving step.
static void CreateAndWriteToFile(absl::string_view file) {
std::ofstream fin(file.data());
ASSERT_THAT(fin.is_open(), IsTrue()) << "Could not create" << file;
@ -79,30 +105,158 @@ class MiniTarTest : public ::testing::Test {
fin.close();
}
// Checks if the files exists and if the contents are correct.
// In these tests, each file contains the relative path from the test
// directory.
// Example: dir1/dir2/file3 will contain dir1/dir2/file3.
// What the files contain does not matter as much, the only important thing
// is that they are not empty so we can check if the contents are preserved.
static void CheckFile(const std::string& file) {
ASSERT_THAT(Exists(file, false), IsTrue()) << "Could not find " << file;
std::ifstream fin(file);
ASSERT_THAT(fin.is_open(), IsTrue()) << "Error when opening " << file;
std::string file_contents((std::istreambuf_iterator<char>(fin)),
std::istreambuf_iterator<char>());
EXPECT_THAT(file_contents, StrEq(file))
<< "Contents of " << file << " are different after extraction";
fin.close();
}
static int test_count_;
static std::string data_dir_;
static std::string init_wd_;
std::string tmp_dir_, id_;
static constexpr absl::string_view kFile1_ = "file1";
static constexpr absl::string_view kFile2_ = "dir1/file2";
static constexpr absl::string_view kFile3_ = "dir1/dir2/file3";
static constexpr absl::string_view kDir1_ = "dir1";
static constexpr absl::string_view kDir2_ = "dir1/dir2";
};
int MiniTarTest::test_count_ = 0;
int MiniTarTest::test_count_;
std::string MiniTarTest::data_dir_;
std::string MiniTarTest::init_wd_;
TEST_F(MiniTarTest, Test1) {
// ASSERT_THAT(true, IsTrue()) << "TEST";
const char* args[] = {"file1", nullptr};
create(id_.c_str(), 0, args, false);
// The tests have the following pattern:
// 1) From inside the test data directory, call the create function with
// different arguments.
// 2) Move to the test specific temporary directory created during the
// set up phase.
// 3) Extract the archive created at step 1.
// 4) Check that the files in the archive have been extracted correctly
// by first checking if they exist and then checking if the content is the
// same as in the original file.
TEST_F(MiniTarTest, TestFileSimple) {
std::vector<std::string> v = {kFile1_.data()};
ASSERT_THAT(chdir(tmp_dir_.c_str()), Eq(0))
create(id_.data(), 0, VecStringToCharPtrArr(v), false);
ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
extract(JoinPath(data_dir_, id_).c_str(), 1, 0, false);
EXPECT_THAT(Exists("file1", false), IsTrue()) << "Could not find file1";
extract(JoinPath(data_dir_, id_).data(), 1, 0, false);
CheckFile(std::string(kFile1_));
}
TEST_F(MiniTarTest, Test2) { ASSERT_THAT(true, IsTrue()) << "TEST"; }
TEST_F(MiniTarTest, TestMultipleFiles) {
std::vector<std::string> v = {kFile1_.data(), kFile2_.data(), kFile3_.data()};
create(id_.data(), 0, VecStringToCharPtrArr(v), false);
ASSERT_THAT(Exists(id_.data(), false), IsTrue())
<< "Archive file was not created";
TEST(TESTEX1, TESTEX2) { ASSERT_THAT(true, IsTrue()) << "TEST"; }
ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
extract(JoinPath(data_dir_, id_).data(), 1, 0, false);
CheckFile(std::string(kFile1_));
CheckFile(std::string(kFile2_));
CheckFile(std::string(kFile3_));
}
TEST_F(MiniTarTest, TestDirectorySimple) {
std::vector<std::string> v = {kDir2_.data()};
create(id_.data(), 0, VecStringToCharPtrArr(v), false);
ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
extract(JoinPath(data_dir_, id_).data(), 1, 0, false);
CheckFile(std::string(kFile3_));
}
TEST_F(MiniTarTest, TestDirectoryNested) {
std::vector<std::string> v = {kDir1_.data()};
create(id_.data(), 0, VecStringToCharPtrArr(v), false);
ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
extract(JoinPath(data_dir_, id_).data(), 1, 0, false);
CheckFile(std::string(kFile2_));
CheckFile(std::string(kFile3_));
}
TEST_F(MiniTarTest, TestComplex) {
std::vector<std::string> v = {kFile1_.data(), kDir1_.data()};
create(id_.data(), 0, VecStringToCharPtrArr(v), false);
ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
extract(JoinPath(data_dir_, id_).data(), 1, 0, false);
CheckFile(std::string(kFile1_));
CheckFile(std::string(kFile2_));
CheckFile(std::string(kFile3_));
}
TEST_F(MiniTarTest, TestCompress) {
std::vector<std::string> v = {kFile1_.data(), kDir1_.data()};
int compress = 'Z';
create(id_.data(), compress, VecStringToCharPtrArr(v), false);
ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
extract(JoinPath(data_dir_, id_).data(), 1, 0, false);
CheckFile(std::string(kFile1_));
CheckFile(std::string(kFile2_));
CheckFile(std::string(kFile3_));
}
TEST_F(MiniTarTest, TestGZIP) {
std::vector<std::string> v = {kFile1_.data(), kDir1_.data()};
int compress = 'z';
create(id_.data(), compress, VecStringToCharPtrArr(v), false);
ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
extract(JoinPath(data_dir_, id_).data(), 1, 0, false);
CheckFile(std::string(kFile1_));
CheckFile(std::string(kFile2_));
CheckFile(std::string(kFile3_));
}
TEST_F(MiniTarTest, TestBZIP2) {
std::vector<std::string> v = {kFile1_.data(), kDir1_.data()};
int compress = 'j';
create(id_.data(), compress, VecStringToCharPtrArr(v), false);
ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
<< "Could not chdir into test data directory";
extract(JoinPath(data_dir_, id_).data(), 1, 0, false);
CheckFile(std::string(kFile1_));
CheckFile(std::string(kFile2_));
CheckFile(std::string(kFile3_));
}
} // namespace