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
+12
View File
@@ -0,0 +1,12 @@
# Common definitions
include ../common.mak
all:
$(MAKE) -C src
install: all
$(MAKE) -C src install
$(MAKE) -C man install
clean:
$(MAKE) -C src clean
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.
*/
#ifndef BUFFER_H
#define BUFFER_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
/* Buffers for building tar file entries */
struct buffer {
size_t total; /* Total number of bytes in buffer */
size_t off; /* Current offset to next free byte in memory buffer */
size_t size; /* Memory buffer size */
char *addr; /* Memory buffer address */
bool fd_open; /* Has fd been openend yet? */
FILE *file; /* FILE * of file containing previous buffer data */
int fd; /* Handle of file containing previous buffer data */
};
void buffer_init(struct buffer *buffer, size_t size);
struct buffer *buffer_alloc(size_t size);
void buffer_reset(struct buffer *buffer);
void buffer_close(struct buffer *buffer);
void buffer_free(struct buffer *buffer, bool dyn);
int buffer_open(struct buffer *buffer);
int buffer_flush(struct buffer *buffer);
ssize_t buffer_make_room(struct buffer *buffer, size_t size, bool usefile,
size_t max_buffer_size);
int buffer_truncate(struct buffer *buffer, size_t len);
ssize_t buffer_read_fd(struct buffer *buffer, int fd, size_t chunk,
bool usefile, size_t max_buffer_size);
int buffer_add_data(struct buffer *buffer, char *addr, size_t len,
bool usefile, size_t max_buffer_size);
typedef int (*buffer_cb_t)(void *data, void *addr, size_t len);
int buffer_iterate(struct buffer *buffer, buffer_cb_t cb, void *data);
void buffer_print(struct buffer *buffer);
#endif /* BUFFER_H */
+29
View File
@@ -0,0 +1,29 @@
/*
* 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.
*/
#ifndef DREF_H
#define DREF_H
#include <dirent.h>
#include <stdbool.h>
/* Multiple jobs may refer to an open DIR * - need reference counting */
struct dref {
DIR *dd;
int dirfd;
unsigned int count;
};
struct dref *dref_create(const char *dirname);
struct dref *dref_get(struct dref *dref);
void dref_put(struct dref *dref);
#endif /* DREF_H */
+63
View File
@@ -0,0 +1,63 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Main dump logic
*
* 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.
*/
#ifndef DUMP_H
#define DUMP_H
#include <stdbool.h>
#include <stddef.h>
#include <sys/stat.h>
#include "strarray.h"
#define NUM_EXCLUDE_TYPES 7
struct dump_spec {
char *inname;
char *outname;
bool is_cmd;
};
struct dump_opts {
bool add_cmd_status;
bool append;
bool dereference;
bool exclude_type[NUM_EXCLUDE_TYPES];
bool gzip;
bool ignore_failed_read;
bool no_eof;
bool quiet;
bool recursive;
bool threaded;
bool verbose;
const char *output_file;
int file_timeout;
int timeout;
long jobs;
long jobs_per_cpu;
size_t file_max_size;
size_t max_buffer_size;
size_t max_size;
size_t read_chunk_size;
struct strarray exclude;
struct dump_spec *specs;
unsigned int num_specs;
};
struct dump_opts *dump_opts_new(void);
int dump_opts_set_type_excluded(struct dump_opts *opts, char c);
void dump_opts_add_spec(struct dump_opts *opts, char *inname, char *outname,
bool is_cmd);
void dump_opts_free(struct dump_opts *opts);
int dump_to_tar(struct dump_opts *opts);
#endif /* DUMP_H */
+23
View File
@@ -0,0 +1,23 @@
/*
* 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.
*/
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdbool.h>
extern bool global_threaded;
extern bool global_debug;
extern bool global_verbose;
extern bool global_quiet;
extern bool global_timestamps;
#endif /* GLOBAL_H */
+26
View File
@@ -0,0 +1,26 @@
/*
* 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.
*/
#ifndef IDCACHE_H
#define IDCACHE_H
#include <stdlib.h>
#include <sys/types.h>
/* Buffer sizes for getpwuid_r and getgid_r calls (bytes) */
#define PWD_BUFFER_SIZE 4096
#define GRP_BUFFER_SIZE 4096
void uid_to_name(uid_t uid, char *name, size_t len);
void gid_to_name(gid_t gid, char *name, size_t len);
void idcache_cleanup(void);
#endif /* IDCACHE_H */
+101
View File
@@ -0,0 +1,101 @@
/*
* 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.
*/
#ifndef MISC_H
#define MISC_H
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include "lib/util_libc.h"
#include "global.h"
#define MSG_LEN 256
#define DBG(...) \
do { \
if (global_debug) \
debug(__FILE__, __LINE__, ##__VA_ARGS__); \
} while (0)
#define mwarn(fmt, ...) _mwarn(true, (fmt), ##__VA_ARGS__)
#define mwarnx(fmt, ...) _mwarn(false, (fmt), ##__VA_ARGS__)
/* Helper macro for constructing messages in variables */
#define HANDLE_RC(rc, max, off, label) \
do { \
if ((rc) > 0) \
(off) += (rc); \
if ((off) > (max)) \
goto label; \
} while (0)
/* Program exit codes */
#define EXIT_OK 0
#define EXIT_RUNTIME 1
#define EXIT_USAGE 2
/* Number of nanoseconds in a second */
#define NSEC_PER_SEC 1000000000L
#define NSEC_PER_MSEC 1000000L
#define NSEC_PER_USEC 1000L
extern struct timespec main_start_ts;
struct dref;
int misc_write_data(int fd, char *addr, size_t len);
ssize_t misc_read_data(int fd, char *addr, size_t len);
void inc_timespec(struct timespec *ts, time_t sec, long nsec);
void set_timespec(struct timespec *ts, time_t sec, long nsec);
bool ts_before(struct timespec *a, struct timespec *b);
int snprintf_duration(char *buff, size_t len, struct timespec *start,
struct timespec *end);
char *get_threadname(void);
void debug(const char *file, unsigned long line, const char *format, ...);
void _mwarn(bool print_errno, const char *format, ...);
void verb(const char *format, ...);
void info(const char *format, ...);
#define mmalloc(len) util_zalloc(len)
#define mcalloc(n, len) util_zalloc((n) * (len))
#define mrealloc(ptr, len) util_realloc((ptr), (len))
#define mstrdup(str) util_strdup(str)
#define masprintf(fmt, ...) __masprintf(__func__, __FILE__, __LINE__, \
(fmt), ##__VA_ARGS__)
char *__masprintf(const char *func, const char *file, int line,
const char *fmt, ...);
#define set_threadname(fmt, ...) __set_threadname(__func__, __FILE__, \
__LINE__, (fmt), \
##__VA_ARGS__)
void __set_threadname(const char *func, const char *file, int line,
const char *fmt, ...);
void clear_threadname(void);
void chomp(char *str, char *c);
void lchomp(char *str, char *c);
void remove_double_slashes(char *str);
int stat_file(bool dereference, const char *abs, const char *rel,
struct dref *dref, struct stat *st);
void set_dummy_stat(struct stat *st);
bool starts_with(const char *str, const char *prefix);
bool ends_with(const char *str, const char *suffix);
int cmd_child(int fd, char *cmd);
int cmd_open(char *cmd, pid_t *pid_ptr);
int cmd_close(int fd, pid_t pid, int *status_ptr);
void misc_init(void);
void misc_cleanup(void);
void set_stdout_data(void);
#endif /* MISC_H */
+26
View File
@@ -0,0 +1,26 @@
/*
* 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.
*/
#ifndef STRARRAY_H
#define STRARRAY_H
/* A string array that can grow in size */
struct strarray {
unsigned int num;
char **str;
};
void free_strarray(struct strarray *array);
void add_str_to_strarray(struct strarray *array, const char *str);
void add_vstr_to_strarray(struct strarray *array, const char *fmt, ...);
int add_file_to_strarray(struct strarray *array, const char *filename);
#endif /* STRARRAY_H */
+44
View File
@@ -0,0 +1,44 @@
/*
* 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.
*/
#ifndef TAR_H
#define TAR_H
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#define TYPE_REGULAR '0'
#define TYPE_LINK '2'
#define TYPE_DIR '5'
#define TAR_BLOCKSIZE 512
struct buffer;
/* emit_cb_t - Callback used for emitting chunks of a byte stream
* @data: Arbitrary pointer passed via the @data parameter of the
* tar_emit_file_* functions
* @addr: Pointer to data
* @len: Size of data
* Return %0 on success. Returning non-zero will indicate failure and abort
* further data emission. */
typedef int (*emit_cb_t)(void *data, void *addr, size_t len);
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 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);
#endif /* TAR_H */
+12
View File
@@ -0,0 +1,12 @@
# Common definitions
include ../../common.mak
all:
install:
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1
$(INSTALL) -m 644 -c dump2tar.1 $(DESTDIR)$(MANDIR)/man1
clean:
.PHONY: all clean
+458
View File
@@ -0,0 +1,458 @@
.\" Copyright 2017 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.\" Macro for inserting an option description prologue.
.\" .OD <long> [<short>] [args]
.de OD
. ds args "
. if !'\\$3'' .as args \fI\\$3\fP
. if !'\\$4'' .as args \\$4
. if !'\\$5'' .as args \fI\\$5\fP
. if !'\\$6'' .as args \\$6
. if !'\\$7'' .as args \fI\\$7\fP
. PD 0
. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4
. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
. PD
..
.\" Macro for inserting code line.
.\" .CL <text>
.de CL
. ds pfont \\n[.f]
. nh
. na
. ft CW
\\$*
. ft \\*[pfont]
. ad
. hy
. br
..
.\" Macro for inserting a man page reference.
.\" .MP man-page section [suffix]
.de MP
. nh
. na
. BR \\$1 (\\$2)\\$3
. ad
. hy
..
.
.TH "dump2tar" "1" "2016\-09\-02" "" ""
.
.SH "NAME"
dump2tar - Gather file contents and command output into a tar archive
.
.
.SH "SYNOPSIS"
.B "dump2tar "
.RI "[" "OPTIONS" "] " "SPECS"
.
.
.SH "DESCRIPTION"
.B dump2tar
creates a tar archive from the contents of any files, including files of
unknown size.
Examples for files of unknown size are:
.IP \(bu 3
Named pipes (FIFOs)
.PP
.IP \(bu 3
Particular Linux kernel debugfs or sysfs files
.PP
.IP \(bu 3
Character or block devices
.PP
When adding such a file,
.B dump2tar
first reads all available data until an end-of-file indication is found. From
this data, it then creates a regular file entry in the resulting tar archive.
By default, symbolic links and directories are preserved in the archive in
their original form.
.B dump2tar
can also:
.IP \(bu 3
Add files under a different name
.PP
.IP \(bu 3
Run arbitrary commands and add the resulting command output as a
regular file
.PP
.
.
.SH "FILE SPECIFICATIONS"
.
This section describes the format of the
.I SPECS
argument mentioned in the command synopsis.
Use the following command line syntax to identify data sources and
to specify file names within the archive:
.PP
.TP
.I "PATH"
Adds the contents of the file system subtree at file system location
.I PATH
(with possible exceptions described by options) in the archive under the same
file name as on the file system.
.PP
.
.
.TP
.IR "FILENAME" ":=" "PATH"
Adds the contents of the file at file system location
.I PATH
in the archive under the name specified by
.IR FILENAME .
.PP
.
.
.TP
.IR "FILENAME" "|=" "CMDLINE"
Runs the command
.IR CMDLINE
and captures both the resulting standard output and standard error streams.
Adds the collected output as a regular file named
.I FILENAME
in the resulting archive. You can also include the resulting program exit code
by using option \-\-add\-cmd\-status.
.PP
.
You can also specify "\-\-". All specifications that follow are interpreted as
simple file names. This is useful for archiving files that contain ":=" or "|=".
.PP
.
.
.SH "OUTPUT OPTIONS"
.
.OD "output\-file" "o" "TARFILE"
Writes the resulting tar archive to
.IR TARFILE .
An existing file at the specified file system location is overwritten.
If this option is omitted or if "\-" is specified for
.IR TARFILE ,
the archive is written to the standard output stream.
.PP
.
.
.OD "gzip" "z" ""
Compresses the resulting tar archive using gzip.
.PP
.
.
.OD "max\-size" "m" "VALUE"
Sets an upper size limit, in bytes, for the resulting archive. If this limit
is exceeded after adding a file, no further files are added.
.PP
.
.
.OD "timeout" "t" "VALUE"
Sets an upper time limit, in seconds, for the archiving process. If this limit
is exceeded while adding a file, that file is truncated and no
further files are added.
.PP
.
.
.OD "no-eof" "" ""
Does not write an end-of-file marker.
Use this option if you want to create an archive that can be extended by
appending additional tar archive data.
Note: Do not use this option for the final data to be added.
A valid tar archive requires a trailing end-of-file marker.
.PP
.
.
.OD "append" "" ""
Appends data to the end of the archive.
Use this option to incrementally build a tar file by repeatedly calling
.BR dump2tar .
You must specify the \-\-no\-eof option for each but the final call of
.BR dump2tar .
.PP
.
.
.OD "add-cmd-status" "" ""
Adds a separate file named
.RI \(dq FILENAME .cmdstatus\(dq
for each command output added through the
.RI \(dq FILENAME |= CMDLINE \(dq
notation (see FILE SPECIFICATIONS).
This file contains information about the exit status of the
process that executed the command:
.
.RS 8
.TP
.RI EXITSTATUS= VALUE
Unless
.I VALUE
is -1, the process ended normally with the specified exit value.
.PP
.
.TP
.RI TERMSIG= VALUE
Unless
.I VALUE
is -1, the process was stopped by a signal of the specified number.
.PP
.
.TP
.RI WAITPID_ERRNO= VALUE
Unless
.I VALUE
is -1, an attempt to obtain the status of the process failed with the
specified error.
.PP
.RE
.
.
.
.SH "INPUT OPTIONS"
.
.OD "files\-from" "F" "FILENAME"
Reads input data specifications (see FILE SPECIFICATIONS) from
.IR FILENAME ,
one specification per line. Each line contains either a file name or a
.IR FILENAME := PATH
or
.IR FILENAME |= CMDLINE
specification. Empty lines are ignored.
A line can also consist of only "\-\-". All lines following this specification
are interpreted as simple file names. This is useful for archiving files that
contain ":=" or "|=".
.PP
.
.
.OD "ignore\-failed\-read" "i" ""
Continues after read errors.
By default,
.B dump2tar
stops processing after encountering errors while reading an input file.
With this option,
.B dump2tar
prints a warning message and adds an empty entry for the erroneous file in
the archive.
.PP
.
.
.OD "buffer\-size" "b" "VALUE"
Reads data from input files in chunks of
.I VALUE
bytes. Large values can accelerate the archiving process for large files
at the cost of increased memory usage. The default value is 1048576.
.PP
.
.
.OD "file\-timeout" "T" "VALUE"
Sets an upper time limit, in seconds, for reading an input file.
.B dump2tar
stops processing a file when the time limit is exceeded. Archive entries for
such files are truncated to the amount of data that is collected by the time
the limit is reached.
.PP
.
.
.OD "file\-max\-size" "M" "N"
Sets an upper size limit, in bytes, for an input file.
.B dump2tar
stops processing a file when the size limit is exceeded. Archive entries for
such files are truncated to the specified size.
.PP
.
.
.OD "jobs" "j" "N"
By default,
.B dump2tar
processes one file at a time. With this option,
.B dump2tar
processes
.I N
files in parallel.
Parallel processing can accelerate the archiving process,
especially if input files are located on slow devices, or when output from
multiple commands is added to the archive.
Note: Use
.B tar
option \-\-delay\-directory\-restore when extracting files from an archive
created with \-\-jobs to prevent conflicts with directory permissions and
modification times.
.PP
.
.
.OD "jobs\-per\-cpu" "J" "N"
Processes
.I N
files for each online CPU in parallel.
Parallel processing can accelerate the
archiving process, especially if input files are located on slow devices, or
when output from multiple commands is added to the archive.
Note: Use
.B tar
option \-\-delay\-directory\-restore when extracting files from an archive
created with \-\-jobs\-per\-cpu to prevent conflicts with directory permissions
and modification times.
.PP
.
.
.OD "exclude" "x" "PATTERN"
Does not add files to the archive if their file names match
.IR PATTERN .
.I PATTERN
is an expression that uses the shell wildcards.
.PP
.
.
.OD "exclude\-from" "X" "FILENAME"
Does not add files to the archive if their names match at least one of the
patterns listed in the pattern file with name
.IR FILENAME .
In the pattern file, each line specifies an expression that uses the
shell wildcards.
.PP
.
.
.OD "exclude\-type" "" "TYPE"
Does not add files to the archive if they match at least one of the file types
specified with
.IR TYPE .
.I TYPE
uses one or more of the characters "fdcbpls", where:
.RS 8
.IP f 3
regular files
.PP
.IP d 3
directories
.PP
.IP c 3
character devices
.PP
.IP b 3
block devices
.PP
.IP p 3
named pipes (FIFOs)
.PP
.IP l 3
symbolic links
.PP
.IP s 3
sockets
.PP
.RE
.
.PP
.
.
.OD "dereference" "" ""
Adds the content of link targets instead of symbolic links.
.PP
.
.
.OD "no\-recursion" "" ""
Does not add files from sub\-directories.
By default,
.B dump2tar
adds archive entries for specified directories, and for the files within these
directories. With this option, a specified directory results in a single entry
for the directory. Any contained files to be included must be specified
explicitly.
.PP
.
.
.SH "MISC OPTIONS"
.
.OD "help" "h" ""
Prints an overview of available options, then exits.
.PP
.
.
.OD "verbose" "V" ""
Prints additional informational output.
.PP
.
.
.OD "quiet" "q" ""
Suppresses printing of informational output.
.PP
.
.
.
.SH "EXAMPLES"
.
.\fB
.CL # dump2tar a b \-o archive.tar
.\fR
.RS 4
Creates a tar archive named archive.tar containing files a and b.
.RE
.PP
.
.\fB
.CL # dump2tar /proc \-o procdump.tar.gz \-z \-i \-T 1 \-M 1048576
.\fR
.RS 4
Creates a gzip compressed tar archive named procdump.tar.gz that contains
all procfs files. Unreadable files are ignored. Files are truncated when the
first of the two limiting conditions is reached, either 1048576 bytes of
content or the reading time of 1 second.
.RE
.PP
.
.\fB
.CL # dump2tar '|=dmesg' '|=lspci' \-o data.tar
.\fR
.RS 4
Creates a tar archive named data.tar containing the output of the 'dmesg'
and 'lspci' commands.
.RE
.PP
.
.\fB
.CL # dump2tar /sys/kernel/debug/ -x '*/tracing/*' -o debug.tar -i
.\fR
.RS 4
Creates a tar archive named debug.tar containing the contents of directory
/sys/kernel/debug/ while excluding any file that is located in a sub-directory
named 'tracing'.
.RE
.PP
.
.
.SH "EXIT CODES"
.TP
.B 0
The program finished successfully
.TP
.B 1
A run-time error occurred
.TP
.B 2
The specified command was not valid
.PP
.
.
.SH "SEE ALSO"
.MP dump2tar 1 ,
.MP tar 1
+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;
}