Initial s390-tools-2.0.0 import

This commit is based on the s390-tools-1.39.0 version.

Changes on top of s390-tools-1.39.0:

 - Add MIT license to all source files
 - Add LICENSE file
 - Transform REAMDE to README.md (markdown)
 - Add AUTHORS.md file
 - Add CONTRIBUTING.md file
 - Move changelog from README to CHANGELOG.md file

Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
Michael Holzheu
2017-08-07 16:13:17 +02:00
commit b627b8d8e1
647 changed files with 168974 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
# Common definitions
include ../../common.mak
ALL_CPPFLAGS += -I../include -std=gnu99 -Wno-unused-parameter
LDLIBS += -lpthread -lrt
ifneq ($(HAVE_ZLIB),0)
ALL_CPPFLAGS += -DHAVE_ZLIB
LDLIBS += -lz
endif
core_objects = buffer.o dref.o global.o dump.o idcache.o misc.o strarray.o tar.o
libs = $(rootdir)/libutil/libutil.a
check_dep_zlib:
$(call check_dep, \
"dump2tar", \
"zlib.h", \
"zlib-devel or libz-dev", \
"HAVE_ZLIB=0")
all: check_dep_zlib dump2tar
dump2tar: $(core_objects) dump2tar.o $(libs)
install: dump2tar
$(INSTALL) -c dump2tar $(DESTDIR)$(USRBINDIR)
clean:
@rm -f dump2tar *.o
.PHONY: all install clean
+273
View File
@@ -0,0 +1,273 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Data buffering functions
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "buffer.h"
#include "misc.h"
void buffer_print(struct buffer *buffer)
{
fprintf(stderr, "DEBUG: buffer at %p\n", (void *) buffer);
if (!buffer)
return;
fprintf(stderr, "DEBUG: total=%zu\n", buffer->total);
fprintf(stderr, "DEBUG: off=%zu\n", buffer->off);
fprintf(stderr, "DEBUG: size=%zu\n", buffer->size);
fprintf(stderr, "DEBUG: addr=%p\n", (void *) buffer->addr);
fprintf(stderr, "DEBUG: fd_open=%d\n", buffer->fd_open);
fprintf(stderr, "DEBUG: fd=%d\n", buffer->fd);
if (buffer->fd_open) {
fprintf(stderr, "DEBUG: fd->pos=%zu\n",
lseek(buffer->fd, 0, SEEK_CUR));
}
}
/* Initialize @buffer to hold @size bytes in memory */
void buffer_init(struct buffer *buffer, size_t size)
{
memset(buffer, 0, sizeof(struct buffer));
buffer->addr = mmalloc(size);
buffer->size = size;
}
/* Allocate a new buffer for holding @size bytes in memory */
struct buffer *buffer_alloc(size_t size)
{
struct buffer *buffer;
buffer = mmalloc(sizeof(struct buffer));
buffer_init(buffer, size);
return buffer;
}
/* Forget about any data stored in @buffer */
void buffer_reset(struct buffer *buffer)
{
buffer->total = 0;
buffer->off = 0;
if (buffer->fd_open) {
if (ftruncate(buffer->fd, 0))
mwarn("Cannot truncate temporary file");
if (lseek(buffer->fd, 0, SEEK_SET) == (off_t) -1)
mwarn("Cannot seek in temporary file");
}
}
/* Close buffer file associated with @buffer */
void buffer_close(struct buffer *buffer)
{
if (!buffer->fd_open)
return;
fclose(buffer->file);
buffer->fd = 0;
buffer->fd_open = false;
}
/* Release all resources associated with @buffer. If @dyn is %true, also free
* @buffer itself. */
void buffer_free(struct buffer *buffer, bool dyn)
{
if (!buffer)
return;
buffer_reset(buffer);
buffer_close(buffer);
free(buffer->addr);
if (dyn)
free(buffer);
}
/* Open a buffer file for @buffer. Return %EXIT_OK on success, %EXIT_RUNTIME
* otherwise. */
int buffer_open(struct buffer *buffer)
{
if (buffer->fd_open)
return EXIT_OK;
buffer->file = tmpfile();
if (!buffer->file) {
mwarn("Could not create temporary file");
return EXIT_RUNTIME;
}
buffer->fd = fileno(buffer->file);
buffer->fd_open = true;
return EXIT_OK;
}
/* Write data in memory of @buffer to buffer file. Return %EXIT_OK on success,
* %EXIT_RUNTIME otherwise. */
int buffer_flush(struct buffer *buffer)
{
if (buffer->off == 0)
return EXIT_OK;
if (buffer_open(buffer))
return EXIT_RUNTIME;
if (misc_write_data(buffer->fd, buffer->addr, buffer->off)) {
mwarn("Could not write to temporary file");
return EXIT_RUNTIME;
}
buffer->off = 0;
return EXIT_OK;
}
/* Try to ensure that at least @size bytes are available at
* @buffer->addr[buffer->off]. Return the actual number of bytes available or
* @-1 on error. If @usefile is %true, make use of a buffer file if
* the total buffer size exceeds @max_buffer_size. */
ssize_t buffer_make_room(struct buffer *buffer, size_t size, bool usefile,
size_t max_buffer_size)
{
size_t needsize;
if (size > max_buffer_size && usefile)
size = max_buffer_size;
needsize = buffer->off + size;
if (needsize <= buffer->size) {
/* Room available */
return size;
}
if (needsize > max_buffer_size && usefile) {
/* Need to write out memory buffer to buffer file */
if (buffer_flush(buffer))
return -1;
if (size <= buffer->size)
return size;
needsize = size;
}
/* Need to increase memory buffer size */
buffer->size = needsize;
buffer->addr = mrealloc(buffer->addr, buffer->size);
return size;
}
/* Try to read @chunk bytes from @fd to @buffer. Return the number of bytes
* read on success, %0 on EOF or %-1 on error. */
ssize_t buffer_read_fd(struct buffer *buffer, int fd, size_t chunk,
bool usefile, size_t max_buffer_size)
{
ssize_t c = buffer_make_room(buffer, chunk, usefile, max_buffer_size);
DBG("buffer_read_fd wanted %zd got %zd", chunk, c);
if (c < 0)
return c;
c = read(fd, buffer->addr + buffer->off, c);
if (c > 0) {
buffer->total += c;
buffer->off += c;
}
return c;
}
/* Add @len bytes at @addr to @buffer. If @addr is %NULL, add zeroes. Return
* %EXIT_OK on success, %EXIT_RUNTIME otherwise. */
int buffer_add_data(struct buffer *buffer, char *addr, size_t len, bool usefile,
size_t max_buffer_size)
{
ssize_t c;
while (len > 0) {
c = buffer_make_room(buffer, len, usefile, max_buffer_size);
if (c < 0)
return EXIT_RUNTIME;
if (addr) {
memcpy(buffer->addr + buffer->off, addr, c);
addr += c;
} else {
memset(buffer->addr + buffer->off, 0, c);
}
buffer->total += c;
buffer->off += c;
len -= c;
}
return EXIT_OK;
}
/* Call @cb for all chunks of data in @buffer. @data is passed to @cb. */
int buffer_iterate(struct buffer *buffer, buffer_cb_t cb, void *data)
{
int rc;
ssize_t r;
if (buffer->total == 0)
return EXIT_OK;
if (!buffer->fd_open)
return cb(data, buffer->addr, buffer->off);
/* Free memory buffer to be used as copy buffer */
if (buffer_flush(buffer))
return EXIT_RUNTIME;
if (lseek(buffer->fd, 0, SEEK_SET) == (off_t) -1) {
mwarn("Cannot seek in temporary file");
return EXIT_RUNTIME;
}
/* Copy data from temporary file to target file */
while ((r = misc_read_data(buffer->fd, buffer->addr,
buffer->size)) != 0) {
if (r < 0) {
mwarn("Cannot read from temporary file");
return EXIT_RUNTIME;
}
rc = cb(data, buffer->addr, r);
if (rc)
return rc;
}
return EXIT_OK;
}
/* Truncate @buffer to at most @len bytes */
int buffer_truncate(struct buffer *buffer, size_t len)
{
size_t delta;
if (buffer->total <= len)
return EXIT_OK;
delta = buffer->total - len;
buffer->total = len;
if (buffer->fd_open && delta > buffer->off) {
/* All of memory and some of file buffer is truncated */
buffer->off = 0;
if (ftruncate(buffer->fd, len)) {
mwarn("Cannot truncate temporary file");
return EXIT_RUNTIME;
}
if (lseek(buffer->fd, len, SEEK_SET) == (off_t) -1) {
mwarn("Cannot seek in temporary file");
return EXIT_RUNTIME;
}
} else {
/* Only memory buffer is truncated */
buffer->off -= delta;
}
return EXIT_OK;
}
+94
View File
@@ -0,0 +1,94 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Reference counting for directory handles
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <dirent.h>
#include <pthread.h>
#include <sys/types.h>
#include "dref.h"
#include "global.h"
#include "misc.h"
/* dref_mutex serializes access to drefs */
static pthread_mutex_t dref_mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned long num_open_dirs;
static unsigned long num_open_dirs_max;
/* Lock dref mutex */
static void dref_lock(void)
{
if (!global_threaded)
return;
pthread_mutex_lock(&dref_mutex);
}
/* Unlock dref mutex */
static void dref_unlock(void)
{
if (!global_threaded)
return;
pthread_mutex_unlock(&dref_mutex);
}
/* Create a reference count managed directory handle for @dirname */
struct dref *dref_create(const char *dirname)
{
struct dref *dref;
DIR *dd;
dd = opendir(dirname);
DBG("opendir(%s)=%p (total=%lu)", dirname, dd, ++num_open_dirs);
if (!dd) {
num_open_dirs--;
return NULL;
}
if (num_open_dirs > num_open_dirs_max)
num_open_dirs_max = num_open_dirs;
dref = mmalloc(sizeof(struct dref));
dref->dd = dd;
dref->dirfd = dirfd(dd);
dref->count = 1;
return dref;
}
/* Obtain a reference to @dref */
struct dref *dref_get(struct dref *dref)
{
if (dref) {
dref_lock();
dref->count++;
dref_unlock();
}
return dref;
}
/* Release a reference to @dref. If this was the last reference, lose the
* associated directory handle and free @dref. */
void dref_put(struct dref *dref)
{
if (dref) {
dref_lock();
dref->count--;
if (dref->count == 0) {
num_open_dirs--;
DBG("closedir(%p) (total=%lu, max=%lu)", dref->dd,
num_open_dirs, num_open_dirs_max);
closedir(dref->dd);
free(dref);
}
dref_unlock();
}
}
+1859
View File
File diff suppressed because it is too large Load Diff
+478
View File
@@ -0,0 +1,478 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Command line interface
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "lib/util_opt.h"
#include "lib/util_prg.h"
#include "dump.h"
#include "global.h"
#include "idcache.h"
#include "misc.h"
#include "strarray.h"
#define MIN_BUFFER_SIZE 4096
#define OPT_NOSHORT_BASE 256
#define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0)
#define OPT_NORECURSION (OPT_NOSHORT_BASE + 1)
#define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2)
/* Program description */
static const struct util_prg dump2tar_prg = {
.desc = "Use dump2tar to create a tar archive from the contents "
"of arbitrary files.\nIt works even when the size of actual "
"file content is not known beforehand,\nsuch as with FIFOs, "
"character devices or certain Linux debugfs or sysfs files.\n"
"\nYou can also add files under different names and add "
"command output using the\nformat described in section SPECS "
"below. When no additional options are\nspecified, the "
"resulting archive is written to the standard output stream\n"
"in uncompressed tar format.",
.args = "SPECS",
.copyright_vec = {
{ "IBM Corp.", 2016, 2016 },
UTIL_PRG_COPYRIGHT_END
},
};
/* Definition of command line options */
static struct util_opt dump2tar_opts[] = {
UTIL_OPT_SECTION("OUTPUT OPTIONS"),
{
.option = { "output-file", required_argument, NULL, 'o' },
.argument = "FILE",
.desc = "Write archive to FILE (default: standard output)",
},
#ifdef HAVE_ZLIB
{
.option = { "gzip", no_argument, NULL, 'z' },
.desc = "Write a gzip compressed archive",
},
#endif /* HAVE_ZLIB */
{
.option = { "max-size", required_argument, NULL, 'm' },
.argument = "N",
.desc = "Stop adding files when archive size exceeds N bytes",
},
{
.option = { "timeout", required_argument, NULL, 't' },
.argument = "SEC",
.desc = "Stop adding files after SEC seconds",
},
{
.option = { "no-eof", no_argument, NULL, 131 },
.desc = "Do not write an end-of-file marker",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "add-cmd-status", no_argument, NULL, 132 },
.desc = "Add status of commands as separate file",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "append", no_argument, NULL, 133 },
.desc = "Append output to end of file",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
UTIL_OPT_SECTION("INPUT OPTIONS"),
{
.option = { "files-from", required_argument, NULL, 'F' },
.argument = "FILE",
.desc = "Read filenames from FILE (- for standard input)",
},
{
.option = { "ignore-failed-read", no_argument, NULL, 'i' },
.desc = "Continue after read errors",
},
{
.option = { "buffer-size", required_argument, NULL, 'b' },
.argument = "N",
.desc = "Read data in chunks of N byte (default: 16384)",
},
{
.option = { "file-timeout", required_argument, NULL, 'T' },
.desc = "Stop reading file after SEC seconds",
},
{
.option = { "file-max-size", required_argument, NULL, 'M' },
.argument = "N",
.desc = "Stop reading file after N bytes",
},
{
.option = { "jobs", required_argument, NULL, 'j' },
.argument = "N",
.desc = "Read N files in parallel (default: 1)",
},
{
.option = { "jobs-per-cpu", required_argument, NULL, 'J' },
.argument = "N",
.desc = "Read N files per CPU in parallel",
},
{
.option = { "exclude", required_argument, NULL, 'x' },
.argument = "PATTERN",
.desc = "Don't add files matching PATTERN",
},
{
.option = { "exclude-from", required_argument, NULL, 'X' },
.argument = "FILE",
.desc = "Don't add files matching patterns in FILE",
},
{
.option = { "exclude-type", required_argument, NULL,
OPT_EXCLUDETYPE },
.argument = "TYPE",
.desc = "Don't add files of specified TYPE (one of: fdcbpls)",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "dereference", no_argument, NULL, OPT_DEREFERENCE },
.desc = "Add link targets instead of links",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "no-recursion", no_argument, NULL,
OPT_NORECURSION },
.desc = "Don't add files from sub-directories",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
UTIL_OPT_SECTION("MISC OPTIONS"),
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
{
.option = { "verbose", no_argument, NULL, 'V' },
.desc = "Print additional informational output",
},
{
.option = { "quiet", no_argument, NULL, 'q' },
.desc = "Suppress printing of informational output",
},
UTIL_OPT_END,
};
/* Split buffer size specification in @arg into two numbers to be stored in
* @from_ptr and @to_ptr. Return %EXIT_OK on success. */
static int parse_buffer_size(char *arg, size_t *from_ptr, size_t *to_ptr)
{
char *err;
unsigned long from, to;
if (!*arg) {
mwarnx("Empty buffer size specified");
return EXIT_USAGE;
}
from = strtoul(arg, &err, 10);
if (*err == '-')
to = strtoul(err + 1, &err, 10);
else
to = *to_ptr;
if (*err) {
mwarnx("Invalid buffer size: %s", arg);
return EXIT_USAGE;
}
if (from < MIN_BUFFER_SIZE || to < MIN_BUFFER_SIZE) {
mwarnx("Buffer size too low (minimum %u)", MIN_BUFFER_SIZE);
return EXIT_USAGE;
}
if (to < from)
to = from;
*from_ptr = from;
*to_ptr = to;
return EXIT_OK;
}
static void parse_and_add_spec(struct dump_opts *opts, const char *spec)
{
char *op, *s, *inname, *outname = NULL;
bool is_cmd = false;
s = mstrdup(spec);
op = strstr(s, "|=");
if (op)
is_cmd = true;
else
op = strstr(s, ":=");
if (op) {
*op = 0;
inname = op + 2;
outname = s;
} else {
inname = s;
}
dump_opts_add_spec(opts, inname, outname, is_cmd);
free(s);
}
static int add_specs_from_file(struct dump_opts *opts, const char *filename)
{
FILE *fd;
char *line = NULL;
size_t line_size;
int rc = EXIT_RUNTIME;
bool need_close = false, parse_spec = true;
if (strcmp(filename, "-") == 0)
fd = stdin;
else {
fd = fopen(filename, "r");
if (!fd) {
mwarn("%s: Cannot open file", filename);
goto out;
}
need_close = true;
}
while ((getline(&line, &line_size, fd) != -1)) {
chomp(line, "\n");
if (line[0] == 0)
continue;
if (parse_spec && strcmp(line, "--") == 0) {
/* After a line containing --, no more := or |= specs
* are expected */
parse_spec = false;
continue;
}
if (parse_spec)
parse_and_add_spec(opts, line);
else
dump_opts_add_spec(opts, line, NULL, false);
}
if (ferror(fd))
mwarn("%s: Cannot read file", filename);
else
rc = EXIT_OK;
out:
if (need_close)
fclose(fd);
free(line);
return rc;
}
static void print_help(void)
{
static const struct {
const char *name;
const char *desc;
} specs[] = {
{ "PATH", "Add file or directory at PATH" },
{ "NEWPATH:=PATH", "Add file or directory at PATH as NEWPATH" },
{ "NEWPATH|=CMDLINE", "Add output of command line CMDLINE as "
"NEWPATH" },
{ NULL, NULL },
};
int i;
util_prg_print_help();
printf("SPECS\n");
for (i = 0; specs[i].name; i++)
util_opt_print_indented(specs[i].name, specs[i].desc);
printf("\n");
util_opt_print_help();
}
int main(int argc, char *argv[])
{
int rc = EXIT_USAGE, opt;
long i;
struct dump_opts *opts;
if (getenv("DUMP2TAR_DEBUG"))
global_debug = true;
util_prg_init(&dump2tar_prg);
util_opt_init(dump2tar_opts, "-");
misc_init();
opts = dump_opts_new();
opterr = 0;
while ((opt = util_opt_getopt_long(argc, argv)) != -1) {
switch (opt) {
case 'h': /* --help */
print_help();
rc = EXIT_OK;
goto out;
case 'v': /* --version */
util_prg_print_version();
rc = EXIT_OK;
goto out;
case 'V': /* --verbose */
global_verbose = true;
global_quiet = false;
opts->verbose = true;
opts->quiet = false;
break;
case 'q': /* --quiet */
global_quiet = true;
global_verbose = false;
opts->quiet = true;
opts->verbose = false;
break;
case 'i': /* --ignore-failed-read */
opts->ignore_failed_read = true;
break;
case 'j': /* --jobs N */
opts->jobs = atoi(optarg);
if (opts->jobs < 1) {
mwarnx("Invalid number of jobs: %s", optarg);
goto out;
}
break;
case 'J': /* --jobs-per-cpu N */
opts->jobs_per_cpu = atoi(optarg);
if (opts->jobs_per_cpu < 1) {
mwarnx("Invalid number of jobs: %s", optarg);
goto out;
}
break;
case 'b': /* --buffer-size N */
if (parse_buffer_size(optarg, &opts->read_chunk_size,
&opts->max_buffer_size))
goto out;
break;
case 'x': /* --exclude PATTERN */
add_str_to_strarray(&opts->exclude, optarg);
break;
case 'X': /* --exclude-from FILE */
if (add_file_to_strarray(&opts->exclude, optarg))
goto out;
break;
case 'F': /* --files-from FILE */
if (add_specs_from_file(opts, optarg))
goto out;
break;
case 'o': /* --output-file FILE */
if (opts->output_file) {
mwarnx("Output file specified multiple times");
goto out;
}
opts->output_file = optarg;
break;
case OPT_DEREFERENCE: /* --dereference */
opts->dereference = true;
break;
case OPT_NORECURSION: /* --no-recursion */
opts->recursive = false;
break;
case OPT_EXCLUDETYPE: /* --exclude-type TYPE */
for (i = 0; optarg[i]; i++) {
if (dump_opts_set_type_excluded(opts,
optarg[i]))
break;
}
if (optarg[i]) {
mwarnx("Unrecognized file type: %c", optarg[i]);
goto out;
}
break;
case 131: /* --no-eof */
opts->no_eof = true;
break;
case 132: /* --add-cmd-status */
opts->add_cmd_status = true;
break;
case 133: /* --append */
opts->append = true;
break;
case 't': /* --timeout VALUE */
opts->timeout = atoi(optarg);
if (opts->timeout < 1) {
mwarnx("Invalid timeout value: %s", optarg);
goto out;
}
break;
case 'T': /* --file-timeout VALUE */
opts->file_timeout = atoi(optarg);
if (opts->file_timeout < 1) {
mwarnx("Invalid timeout value: %s", optarg);
goto out;
}
break;
case 'm': /* --max-size N */
opts->max_size = atol(optarg);
if (opts->max_size < 2) {
mwarnx("Invalid maximum size: %s", optarg);
goto out;
}
break;
case 'M': /* --file-max-size N */
opts->file_max_size = atol(optarg);
if (opts->file_max_size < 2) {
mwarnx("Invalid maximum size: %s", optarg);
goto out;
}
break;
case 'z': /* --gzip */
opts->gzip = true;
break;
case 1: /* Filename specification or unrecognized option */
if (optarg[0] == '-') {
mwarnx("Invalid option '%s'", optarg);
goto out;
}
parse_and_add_spec(opts, optarg);
break;
case '?': /* Unrecognized option */
if (optopt)
mwarnx("Invalid option '-%c'", optopt);
else
mwarnx("Invalid option '%s'", argv[optind - 1]);
goto out;
case ':': /* Missing argument */
mwarnx("Option '%s' requires an argument",
argv[optind - 1]);
goto out;
default:
break;
}
}
if (optind >= argc && opts->num_specs == 0) {
mwarnx("Please specify files to dump");
goto out;
}
for (i = optind; i < argc; i++)
dump_opts_add_spec(opts, argv[i], NULL, false);
rc = dump_to_tar(opts);
out:
idcache_cleanup();
misc_cleanup();
dump_opts_free(opts);
if (rc == EXIT_USAGE)
util_prg_print_parse_error();
return rc;
}
+20
View File
@@ -0,0 +1,20 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Global variables
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
*/
#include "global.h"
/* Global settings */
bool global_threaded;
bool global_debug;
bool global_verbose;
bool global_quiet;
bool global_timestamps;
+155
View File
@@ -0,0 +1,155 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Caches for user and group ID lookups
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <grp.h>
#include <pthread.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "idcache.h"
#include "misc.h"
/* Maximum user and group name lengths as defined in tar header */
#define ID_NAME_MAXLEN 32
/* Types for user and group ID caches */
typedef uid_t generic_id_t; /* Assumes that uid_t == gid_t */
struct id_cache_entry {
generic_id_t id;
char name[ID_NAME_MAXLEN];
};
struct id_cache {
unsigned int num;
struct id_cache_entry entries[];
};
/* cache_mutex serializes access to cached uid and gid data */
static pthread_mutex_t id_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct id_cache *id_cache_uid;
static struct id_cache *id_cache_gid;
/* Lock cache mutex */
static void cache_lock(void)
{
if (!global_threaded)
return;
pthread_mutex_lock(&id_cache_mutex);
}
/* Unlock cache mutex */
static void cache_unlock(void)
{
if (!global_threaded)
return;
pthread_mutex_unlock(&id_cache_mutex);
}
/* Copy the name associated with @id in @cache to at most @len bytes at @dest.
* Return %true if name was found in cache, %false otherwise. */
static bool strncpy_id_cache_entry(char *dest, struct id_cache *cache,
generic_id_t id, size_t len)
{
unsigned int i;
bool hit = false;
cache_lock();
if (cache) {
for (i = 0; i < cache->num; i++) {
if (cache->entries[i].id == id) {
strncpy(dest, cache->entries[i].name, len);
hit = true;
break;
}
}
}
cache_unlock();
return hit;
}
/* Add a new entry consisting of @id and @name to ID cache in @*cache_ptr.
* Update @cache_ptr if necessary. */
static void add_id_cache_entry(struct id_cache **cache_ptr, generic_id_t id,
char *name)
{
struct id_cache *cache;
unsigned int cache_num;
size_t new_size;
struct id_cache *new_cache;
cache_lock();
cache = *cache_ptr;
cache_num = cache ? cache->num : 0;
new_size = sizeof(struct id_cache) +
sizeof(struct id_cache_entry) * (cache_num + 1);
new_cache = mrealloc(cache, new_size);
if (cache_num == 0)
new_cache->num = 0;
new_cache->entries[cache_num].id = id;
strncpy(new_cache->entries[cache_num].name, name, ID_NAME_MAXLEN);
new_cache->num++;
*cache_ptr = new_cache;
cache_unlock();
}
/* Copy the user name corresponding to user ID @uid to at most @len bytes
* at @name */
void uid_to_name(uid_t uid, char *name, size_t len)
{
struct passwd pwd, *pwd_ptr;
char buffer[PWD_BUFFER_SIZE], *result;
if (strncpy_id_cache_entry(name, id_cache_uid, uid, len))
return;
/* getpwuid() can be slow so cache results */
getpwuid_r(uid, &pwd, buffer, PWD_BUFFER_SIZE, &pwd_ptr);
if (!pwd_ptr || !pwd_ptr->pw_name)
return;
result = pwd_ptr->pw_name;
add_id_cache_entry(&id_cache_uid, uid, result);
strncpy(name, result, len);
}
/* Copy the group name corresponding to group ID @gid to at most @len bytes
* at @name */
void gid_to_name(gid_t gid, char *name, size_t len)
{
struct group grp, *grp_ptr;
char buffer[GRP_BUFFER_SIZE], *result;
if (strncpy_id_cache_entry(name, id_cache_gid, gid, len))
return;
/* getgrgid() can be slow so cache results */
getgrgid_r(gid, &grp, buffer, GRP_BUFFER_SIZE, &grp_ptr);
if (!grp_ptr || !grp_ptr->gr_name)
return;
result = grp_ptr->gr_name;
add_id_cache_entry(&id_cache_gid, gid, result);
strncpy(name, result, len);
}
void idcache_cleanup(void)
{
free(id_cache_uid);
free(id_cache_gid);
}
+492
View File
@@ -0,0 +1,492 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Helper functions
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "dref.h"
#include "global.h"
#include "misc.h"
struct timespec main_start_ts;
static pthread_key_t thread_name_key;
static bool stdout_data;
/* Write @len bytes at @addr to @fd. Return %EXIT_OK on success, %EXIT_RUNTIME
* otherwise. */
int misc_write_data(int fd, char *addr, size_t len)
{
ssize_t w;
while (len > 0) {
w = write(fd, addr, len);
if (w < 0)
return EXIT_RUNTIME;
len -= w;
addr += w;
}
return EXIT_OK;
}
/* Read at most @len bytes from @fd to @addr. Return the number of bytes read
* or %-1 on error. */
ssize_t misc_read_data(int fd, char *addr, size_t len)
{
size_t done = 0;
ssize_t r;
while (len > 0) {
r = read(fd, addr, len);
if (r < 0)
return -1;
if (r == 0)
break;
len -= r;
addr += r;
done += r;
}
return done;
}
/* Advance timespec @ts by @sec seconds and @nsec nanoseconds */
void inc_timespec(struct timespec *ts, time_t sec, long nsec)
{
ts->tv_nsec += nsec;
ts->tv_sec += sec;
if (ts->tv_nsec > NSEC_PER_SEC) {
ts->tv_nsec -= NSEC_PER_SEC;
ts->tv_sec++;
}
}
/* Set timespec @ts to point to @sec seconds and @nsec nanoseconds in the
* future */
void set_timespec(struct timespec *ts, time_t sec, long nsec)
{
clock_gettime(CLOCK_MONOTONIC, ts);
inc_timespec(ts, sec, nsec);
}
/* Return true if timespec @a refers to a point in time before @b */
bool ts_before(struct timespec *a, struct timespec *b)
{
if (a->tv_sec < b->tv_sec ||
(a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec))
return true;
return false;
}
/* Store a string representing the time duration between @start and @end in
* at most @len bytes of @buff. */
int snprintf_duration(char *buff, size_t len, struct timespec *start,
struct timespec *end)
{
time_t sec;
long nsec, msec, s, m, h;
sec = end->tv_sec - start->tv_sec;
nsec = end->tv_nsec - start->tv_nsec;
if (nsec < 0) {
nsec += NSEC_PER_SEC;
sec--;
}
msec = nsec / NSEC_PER_MSEC;
s = sec % 60;
sec /= 60;
m = sec % 60;
sec /= 60;
h = sec;
if (h > 0)
return snprintf(buff, len, "%luh%lum%lu.%03lus", h, m, s, msec);
else if (m > 0)
return snprintf(buff, len, "%lum%lu.%03lus", m, s, msec);
else
return snprintf(buff, len, "%lu.%03lus", s, msec);
}
/* Return the name of the current thread */
char *get_threadname(void)
{
return pthread_getspecific(thread_name_key);
}
static int snprintf_timestamp(char *str, size_t size)
{
struct timespec now_ts;
set_timespec(&now_ts, 0, 0);
now_ts.tv_sec -= main_start_ts.tv_sec;
now_ts.tv_nsec -= main_start_ts.tv_nsec;
if (now_ts.tv_nsec < 0) {
now_ts.tv_nsec += NSEC_PER_SEC;
now_ts.tv_sec--;
}
return snprintf(str, size, "[%3lu.%06lu] ", now_ts.tv_sec,
now_ts.tv_nsec / NSEC_PER_USEC);
}
/* When DUMP2TAR_DEBUG is set to non-zero, print debugging information */
void debug(const char *file, unsigned long line, const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
/* Debug marker */
rc = snprintf(&msg[off], MSG_LEN - off, "DEBUG: ");
HANDLE_RC(rc, MSG_LEN, off, out);
/* Timestamp */
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
/* Thread name */
rc = snprintf(&msg[off], MSG_LEN - off, "%s: ", get_threadname());
HANDLE_RC(rc, MSG_LEN, off, out);
/* Message */
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
HANDLE_RC(rc, MSG_LEN, off, out);
/* Call site */
rc = snprintf(&msg[off], MSG_LEN - off, " (%s:%lu)", file, line);
out:
fprintf(stderr, "%s\n", msg);
}
/* Print a warning message consisting of @format and variable arguments.
* If @print_errno is true, also print the text corresponding to errno.
* We're not using err.h's warn since we want timestamps and synchronized
* output. */
void _mwarn(bool print_errno, const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
rc = snprintf(&msg[off], MSG_LEN - off, "%s: ",
program_invocation_short_name);
HANDLE_RC(rc, MSG_LEN, off, out);
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
HANDLE_RC(rc, MSG_LEN, off, out);
if (print_errno)
snprintf(&msg[off], MSG_LEN - off, ": %s", strerror(errno));
out:
fprintf(stderr, "%s\n", msg);
}
/* Provide informational output if --verbose was specified */
void verb(const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
FILE *fd;
if (!global_verbose)
return;
if (stdout_data)
fd = stderr;
else
fd = stdout;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
out:
fprintf(fd, "%s", msg);
}
/* Provide informational output. */
void info(const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
FILE *fd;
if (global_quiet)
return;
if (stdout_data)
fd = stderr;
else
fd = stdout;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
out:
fprintf(fd, "%s", msg);
}
/* Return a newly allocated buffer containing the result of the specified
* string format arguments */
char *__masprintf(const char *func, const char *file, int line, const char *fmt,
...)
{
char *str;
va_list args;
va_start(args, fmt);
__util_vasprintf(func, file, line, &str, fmt, args);
va_end(args);
return str;
}
/* Set the internal name of the calling thread */
void __set_threadname(const char *func, const char *file, int line,
const char *fmt, ...)
{
char *str;
va_list args;
va_start(args, fmt);
__util_vasprintf(func, file, line, &str, fmt, args);
va_end(args);
pthread_setspecific(thread_name_key, str);
}
/* Clear any previously set thread name */
void clear_threadname(void)
{
void *addr = pthread_getspecific(thread_name_key);
if (addr) {
pthread_setspecific(thread_name_key, NULL);
free(addr);
}
}
/* Remove any number of trailing characters @c in @str */
void chomp(char *str, char *c)
{
ssize_t i;
for (i = strlen(str) - 1; i >= 0 && strchr(c, str[i]); i--)
str[i] = 0;
}
/* Remove any number of leading characters @c in @str */
void lchomp(char *str, char *c)
{
char *from;
for (from = str; *from && strchr(c, *from); from++)
;
if (str != from)
memmove(str, from, strlen(from) + 1);
}
/* Perform a stat on file referenced by either @abs or @rel and @dref. Store
* results in @stat and return stat()'s return code. */
int stat_file(bool dereference, const char *abs, const char *rel,
struct dref *dref, struct stat *st)
{
int rc;
if (dref) {
if (dereference)
rc = fstatat(dref->dirfd, rel, st, 0);
else
rc = fstatat(dref->dirfd, rel, st, AT_SYMLINK_NOFOLLOW);
} else {
if (dereference)
rc = stat(abs, st);
else
rc = lstat(abs, st);
}
return rc;
}
/* Fill stat buffer @st with dummy values. */
void set_dummy_stat(struct stat *st)
{
/* Fake stat */
memset(st, 0, sizeof(struct stat));
st->st_mode = S_IRUSR | S_IWUSR | S_IFREG;
st->st_uid = geteuid();
st->st_gid = getegid();
st->st_mtime = time(NULL);
}
/* Redirect all output streams to @fd and execute command @CMD */
int cmd_child(int fd, char *cmd)
{
char *argv[] = { "/bin/sh", "-c", NULL, NULL };
char *env[] = { NULL };
argv[2] = cmd;
if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) {
mwarn("Could not redirect command output");
return EXIT_RUNTIME;
}
execve("/bin/sh", argv, env);
return EXIT_RUNTIME;
}
#define PIPE_READ 0
#define PIPE_WRITE 1
/* Run command @cmd as a child process and store its PID in @pid_ptr. On
* success, return a file descriptor that is an output pipe to the standard
* output and standard error streams of the child process. Return %-1 on
* error. */
int cmd_open(char *cmd, pid_t *pid_ptr)
{
int pfd[2];
pid_t pid;
if (pipe(pfd) < 0)
return -1;
pid = fork();
if (pid < 0) {
/* Fork error */
close(pfd[PIPE_READ]);
close(pfd[PIPE_WRITE]);
return -1;
} else if (pid == 0) {
/* Child process */
close(pfd[PIPE_READ]);
exit(cmd_child(pfd[PIPE_WRITE], cmd));
}
/* Parent process */
close(pfd[PIPE_WRITE]);
*pid_ptr = pid;
return pfd[PIPE_READ];
}
/* Close the file descriptor @fd and end the process with PID @pid. When
* not %NULL, use @status_ptr to store the resulting process status. */
int cmd_close(int fd, pid_t pid, int *status_ptr)
{
int status, rc = EXIT_OK;
close(fd);
kill(pid, SIGQUIT);
if (waitpid(pid, &status, 0) == -1) {
status = -errno;
rc = EXIT_RUNTIME;
}
if (status_ptr)
*status_ptr = status;
return rc;
}
void misc_init(void)
{
set_timespec(&main_start_ts, 0, 0);
pthread_key_create(&thread_name_key, free);
set_threadname("main");
}
void misc_cleanup(void)
{
clear_threadname();
pthread_key_delete(thread_name_key);
}
void set_stdout_data(void)
{
stdout_data = true;
}
bool starts_with(const char *str, const char *prefix)
{
size_t len;
len = strlen(prefix);
if (strncmp(str, prefix, len) == 0)
return true;
return false;
}
bool ends_with(const char *str, const char *suffix)
{
size_t str_len, s_len;
str_len = strlen(str);
s_len = strlen(suffix);
if (str_len < s_len)
return false;
if (strcmp(str + str_len - s_len, suffix) != 0)
return false;
return true;
}
/* Remove subsequent slashes in @str */
void remove_double_slashes(char *str)
{
size_t i, to;
char last;
last = 0;
for (i = 0, to = 0; str[i]; i++) {
if (last != '/' || str[i] != '/')
last = str[to++] = str[i];
}
str[to] = 0;
}
+83
View File
@@ -0,0 +1,83 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Dynamically growing string arrays
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "misc.h"
#include "strarray.h"
/* Release resources associated with string array @array */
void free_strarray(struct strarray *array)
{
unsigned int i;
for (i = 0; i < array->num; i++)
free(array->str[i]);
free(array->str);
array->str = NULL;
array->num = 0;
}
/* Add string @str to string array @array */
void add_str_to_strarray(struct strarray *array, const char *str)
{
array->str = mrealloc(array->str, sizeof(char *) * (array->num + 2));
array->str[array->num + 1] = NULL;
array->str[array->num] = mstrdup(str);
array->num++;
}
/* Add string resulting from @fmt and additional arguments to @array */
void add_vstr_to_strarray(struct strarray *array, const char *fmt, ...)
{
va_list args;
char *str;
va_start(args, fmt);
util_vasprintf(&str, fmt, args);
va_end(args);
array->str = mrealloc(array->str, sizeof(char *) * (array->num + 2));
array->str[array->num + 1] = NULL;
array->str[array->num] = str;
array->num++;
}
/* Add all lines in file at @filename to @array */
int add_file_to_strarray(struct strarray *array, const char *filename)
{
FILE *fd;
char *line = NULL;
size_t line_size;
int rc = EXIT_OK;
fd = fopen(filename, "r");
if (!fd) {
mwarn("%s: Cannot open file", filename);
return EXIT_RUNTIME;
}
while (!feof(fd) && !ferror(fd)) {
if (getline(&line, &line_size, fd) == -1)
continue;
chomp(line, "\n");
add_str_to_strarray(array, line);
}
if (ferror(fd))
rc = EXIT_RUNTIME;
free(line);
fclose(fd);
return rc;
}
+272
View File
@@ -0,0 +1,272 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* TAR file generation
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <string.h>
#include "buffer.h"
#include "idcache.h"
#include "misc.h"
#include "tar.h"
#define LONGLINK "././@LongLink"
#define TYPE_LONGLINK 'K'
#define TYPE_LONGNAME 'L'
#define BLOCKSIZE 512
/* Basic TAR header */
struct tar_header {
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
};
/* Store the octal value of @value to at most @len bytes at @dest */
static void set_octal(char *dest, size_t len, unsigned long value)
{
int i;
dest[len - 1] = 0;
for (i = len - 2; i >= 0; i--) {
dest[i] = '0' + (value & 7);
value >>= 3;
}
}
/* Store time @value to at most @len bytes at @dest */
static void set_time(char *dest, size_t len, time_t value)
{
time_t max = (1ULL << (3 * (len - 1))) - 1;
if (value >= 0 && value <= max) {
set_octal(dest, len, value);
return;
}
for (; len > 0; len--) {
dest[len - 1] = value & 0xff;
value >>= 8;
}
dest[0] |= 0x80;
}
#define SET_FIELD(obj, name, value) \
set_octal((obj)->name, sizeof((obj)->name), (unsigned long) (value))
#define SET_TIME_FIELD(obj, name, value) \
set_time((obj)->name, sizeof((obj)->name), (time_t) (value))
#define SET_STR_FIELD(obj, name, value) \
strncpy((obj)->name, (value), sizeof((obj)->name))
/* Initialize the tar file @header with the provided data */
static void init_header(struct tar_header *header, const char *filename,
const char *link, size_t len, struct stat *stat,
char type)
{
unsigned int i, checksum;
unsigned char *c;
memset(header, 0, sizeof(*header));
/* Fill in header fields */
SET_STR_FIELD(header, name, filename);
if (link)
SET_STR_FIELD(header, linkname, link);
SET_FIELD(header, size, len);
if (stat) {
SET_FIELD(header, mode, stat->st_mode & 07777);
SET_FIELD(header, uid, stat->st_uid);
SET_FIELD(header, gid, stat->st_gid);
SET_TIME_FIELD(header, mtime, stat->st_mtime);
uid_to_name(stat->st_uid, header->uname, sizeof(header->uname));
gid_to_name(stat->st_gid, header->gname, sizeof(header->gname));
} else {
SET_FIELD(header, mode, 0644);
SET_FIELD(header, uid, 0);
SET_FIELD(header, gid, 0);
SET_TIME_FIELD(header, mtime, 0);
uid_to_name(0, header->uname, sizeof(header->uname));
gid_to_name(0, header->gname, sizeof(header->gname));
}
header->typeflag = type;
memcpy(header->magic, "ustar ", sizeof(header->magic));
memcpy(header->version, " ", sizeof(header->version));
/* Calculate checksum */
memset(header->chksum, ' ', sizeof(header->chksum));
checksum = 0;
c = (unsigned char *) header;
for (i = 0; i < sizeof(*header); i++)
checksum += c[i];
snprintf(header->chksum, 7, "%06o", checksum);
}
/* Emit zero bytes via @emit_cb to pad @len to a multiple of BLOCKSIZE */
static int emit_padding(emit_cb_t emit_cb, void *data, size_t len)
{
size_t pad = BLOCKSIZE - len % BLOCKSIZE;
char zeroes[BLOCKSIZE];
if (len % BLOCKSIZE > 0) {
memset(zeroes, 0, BLOCKSIZE);
return emit_cb(data, zeroes, pad);
}
return 0;
}
/* Emit @len bytes at @addr via @emit_cb and pad data to BLOCKSIZE with zero
* bytes */
static int emit_data(emit_cb_t emit_cb, void *data, void *addr, size_t len)
{
int rc;
if (len == 0)
return 0;
rc = emit_cb(data, addr, len);
if (rc)
return rc;
return emit_padding(emit_cb, data, len);
}
/* Emit a tar header via @emit_cb */
static int emit_header(emit_cb_t emit_cb, void *data, char *filename,
char *link, size_t len, struct stat *stat, char type)
{
struct tar_header header;
size_t namelen = strlen(filename);
size_t linklen;
int rc;
/* /proc can contain unreadable links which causes tar to complain
* during extract - use a dummy value to handle this more gracefully */
if (link && !*link)
link = " ";
linklen = link ? strlen(link) : 0;
if (linklen > sizeof(header.linkname)) {
rc = emit_header(emit_cb, data, LONGLINK, NULL, linklen + 1,
NULL, TYPE_LONGLINK);
if (rc)
return rc;
rc = emit_data(emit_cb, data, link, linklen + 1);
if (rc)
return rc;
}
if (namelen > sizeof(header.name)) {
rc = emit_header(emit_cb, data, LONGLINK, NULL, namelen + 1,
NULL, TYPE_LONGNAME);
if (rc)
return rc;
rc = emit_data(emit_cb, data, filename, namelen + 1);
if (rc)
return rc;
}
init_header(&header, filename, link, len, stat, type);
return emit_data(emit_cb, data, &header, sizeof(header));
}
struct emit_content_cb_data {
emit_cb_t emit_cb;
void *data;
size_t len;
int rc;
};
/* Callback for emitting a single chunk of data of a buffer */
static int emit_content_cb(void *data, void *addr, size_t len)
{
struct emit_content_cb_data *cb_data = data;
if (len > cb_data->len)
len = cb_data->len;
cb_data->len -= len;
cb_data->rc = cb_data->emit_cb(cb_data->data, addr, len);
if (cb_data->rc || cb_data->len == 0)
return 1;
return 0;
}
/* Emit at most @len bytes of contents of @buffer via @emit_cb and pad output
* to BLOCKSIZE with zero bytes */
static int emit_content(emit_cb_t emit_cb, void *data, struct buffer *buffer,
size_t len)
{
struct emit_content_cb_data cb_data;
cb_data.emit_cb = emit_cb;
cb_data.data = data;
cb_data.len = len;
cb_data.rc = 0;
buffer_iterate(buffer, emit_content_cb, &cb_data);
if (cb_data.rc)
return cb_data.rc;
return emit_padding(emit_cb, data, buffer->total);
}
/* Convert file meta data and content specified as @content into a
* stream of bytes that is reported via the @emit_cb callback. @data is
* passed through to the callback for arbitrary use. */
int tar_emit_file_from_buffer(char *filename, char *link, size_t len,
struct stat *stat, char type,
struct buffer *content, emit_cb_t emit_cb,
void *data)
{
int rc;
DBG("emit tar file=%s type=%d len=%zu", filename, type, len);
rc = emit_header(emit_cb, data, filename, link, len, stat, type);
if (rc)
return rc;
if (content)
rc = emit_content(emit_cb, data, content, len);
return rc;
}
/* Convert file meta data and content specified as @addr and @len into a
* stream of bytes that is reported via the @emit_cb callback. @data is
* passed through to the callback for arbitrary use. */
int tar_emit_file_from_data(char *filename, char *link, size_t len,
struct stat *stat, char type, void *addr,
emit_cb_t emit_cb, void *data)
{
int rc;
DBG("emit tar file=%s type=%d len=%zu", filename, type, len);
rc = emit_header(emit_cb, data, filename, link, len, stat, type);
if (rc)
return rc;
if (addr)
rc = emit_data(emit_cb, data, addr, len);
return rc;
}