/*
* Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Carbonite Inc., 756 N Pastoria Ave
* Sunnyvale, CA 94085, or: http://www.zmanda.com
*/
#include "amanda.h"
#include "event.h"
#include "getopt.h"
#include "amar.h"
#include "amutil.h"
static struct option long_options[] = {
{"create" , 0, NULL, 1},
{"extract" , 0, NULL, 2},
{"list" , 0, NULL, 3},
{"verbose" , 0, NULL, 4},
{"file" , 1, NULL, 5},
{"version" , 0, NULL, 6},
{NULL, 0, NULL, 0}
};
static void
usage(void)
{
printf("Usage: amarchiver [--version|--create|--list|--extract] [--verbose]* [--file file]\n");
printf(" [filename]*\n");
exit(1);
}
static void
error_exit(const char *action, GError *gerror)
{
const char *msg = gerror->message? gerror->message : "(unknown)";
g_fprintf(stderr, "%s: %s\n", action, msg);
exit(1);
}
static void
do_create(char *opt_file, int opt_verbose, int argc, char **argv)
{
FILE *output = stdout;
amar_t *archive;
amar_file_t *file;
amar_attr_t *attribute;
GError *gerror = NULL;
int i, fd_out, fd_in;
off_t filesize = 0;
if (opt_file != NULL && !g_str_equal(opt_file, "-")) {
fd_out = open(opt_file, O_CREAT|O_WRONLY|O_TRUNC, 0660);
if (fd_out <= 0) {
error("open of '%s' failed: %s\n", opt_file, strerror(errno));
}
} else {
fd_out = fileno(stdout);
output = stderr;
}
archive = amar_new(fd_out, O_WRONLY, &gerror);
if (!archive)
error_exit("amar_new", gerror);
i = 0;
while (i<argc) {
fd_in = open(argv[i], O_RDONLY);
if (fd_in < 0) {
g_fprintf(stderr, "open of '%s' failed: %s\n", argv[i], strerror(errno));
i++;
continue;
}
filesize = 0;
file = amar_new_file(archive, argv[i], strlen(argv[i]), NULL, &gerror);
if (!file)
error_exit("amar_new_file", gerror);
attribute = amar_new_attr(file, AMAR_ATTR_GENERIC_DATA, &gerror);
if (!attribute)
error_exit("amar_new_attr", gerror);
filesize += amar_attr_add_data_fd(attribute, fd_in, 1, &gerror);
if (gerror)
error_exit("amar_attr_add_data_fd", gerror);
if (!amar_attr_close(attribute, &gerror))
error_exit("amar_attr_close", gerror);
if (!amar_file_close(file, &gerror))
error_exit("amar_file_close", gerror);
if (opt_verbose == 1) {
g_fprintf(output,"%s\n", argv[i]);
} else if (opt_verbose > 1) {
g_fprintf(output,"%llu %s\n", (unsigned long long)filesize, argv[i]);
}
close(fd_in);
i++;
}
if (!amar_close(archive, &gerror))
error_exit("amar_close", gerror);
close(fd_out);
}
struct read_user_data {
gboolean verbose;
char **argv;
int argc;
};
static gboolean
extract_file_start_cb(
gpointer user_data,
uint16_t filenum G_GNUC_UNUSED,
gpointer filename_buf,
gsize filename_len,
gboolean *ignore G_GNUC_UNUSED,
gpointer *file_data)
{
struct read_user_data *ud = user_data;
int i;
/* keep the filename for later */
*file_data = g_strndup(filename_buf, filename_len);
if (ud->argc) {
*ignore = TRUE;
for (i = 0; i < ud->argc; i++) {
if (strlen(ud->argv[i]) == filename_len
&& g_str_equal(ud->argv[i], *file_data))
*ignore = FALSE;
}
}
return TRUE;
}
static gboolean
extract_file_finish_cb(
gpointer user_data G_GNUC_UNUSED,
uint16_t filenum G_GNUC_UNUSED,
gpointer *file_data,
gboolean truncated)
{
if (truncated)
g_fprintf(stderr, _("Data for '%s' may have been truncated\n"),
(char *)*file_data);
g_free(*file_data);
return TRUE;
}
static int
mkpath(
const char *s,
mode_t mode)
{
char *path = NULL;
char *r = NULL;
int rv = -1;
if (strcmp(s, ".") == 0 || strcmp(s, "/") == 0)
return 0;
path = g_strdup(s);
r = dirname(path);
if ((mkpath(r, mode) == -1) && (errno != EEXIST))
goto out;
if ((mkdir(s, mode) == -1) && (errno != EEXIST))
rv = -1;
else
rv = 0;
out:
g_free(path);
return rv;
}
static gboolean
extract_frag_cb(
gpointer user_data G_GNUC_UNUSED,
uint16_t filenum G_GNUC_UNUSED,
gpointer file_data,
uint16_t attrid,
gpointer attrid_data G_GNUC_UNUSED,
gpointer *attr_data,
gpointer data,
gsize datasize,
gboolean eoa,
gboolean truncated)
{
struct read_user_data *ud = user_data;
int fd = GPOINTER_TO_INT(*attr_data);
if (!fd) {
char *filename;
char *dir;
if (attrid == AMAR_ATTR_GENERIC_DATA) {
filename = g_strdup((char *)file_data);
} else {
filename = g_strdup_printf("%s.%d", (char *)file_data, attrid);
}
dir = g_strdup(filename);
mkpath(dirname(dir), 0770);
g_free(dir);
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0660);
if (fd < 0) {
g_fprintf(stderr, _("Could not open '%s' for writing: %s"),
filename, strerror(errno));
return FALSE;
}
if (ud->verbose)
g_fprintf(stderr, "%s\n", filename);
g_free(filename);
*attr_data = GINT_TO_POINTER(fd);
}
if (full_write(fd, data, datasize) != datasize) {
g_fprintf(stderr, _("while writing '%s.%d': %s"),
(char *)file_data, attrid, strerror(errno));
return FALSE;
}
if (eoa) {
if (truncated) {
g_fprintf(stderr, _("'%s.%d' may be truncated\n"),
(char *)file_data, attrid);
}
close(fd);
}
return TRUE;
}
static void
do_extract(
char *opt_file,
int opt_verbose,
int argc,
char **argv)
{
amar_t *archive;
GError *gerror = NULL;
int fd_in;
amar_attr_handling_t handling[] = {
{ 0, 0, extract_frag_cb, NULL },
};
struct read_user_data ud;
ud.argv = argv;
ud.argc = argc;
ud.verbose = opt_verbose;
if (opt_file && !g_str_equal(opt_file, "-")) {
fd_in = open(opt_file, O_RDONLY);
if (fd_in <= 0) {
error("open of '%s' failed: %s\n", opt_file, strerror(errno));
}
} else {
fd_in = fileno(stdin);
}
archive = amar_new(fd_in, O_RDONLY, &gerror);
if (!archive)
error_exit("amar_new", gerror);
// if (!amar_read(archive, &ud, handling, extract_file_start_cb,
// extract_file_finish_cb, NULL, &gerror)) {
// if (gerror)
// error_exit("amar_read", gerror);
// else
// /* one of the callbacks already printed an error message */
// exit(1);
// }
set_amar_read_cb(archive, &ud, handling, extract_file_start_cb,
extract_file_finish_cb, NULL, &gerror);
event_loop(0);
if (gerror) {
error_exit("amar_read", gerror);
}
amar_close(archive, NULL);
}
static gboolean
list_file_start_cb(
gpointer user_data G_GNUC_UNUSED,
uint16_t filenum G_GNUC_UNUSED,
gpointer filename_buf,
gsize filename_len,
gboolean *ignore,
gpointer *file_data G_GNUC_UNUSED)
{
g_printf("%.*s\n", (int)filename_len, (char *)filename_buf);
*ignore = TRUE;
return TRUE;
}
static void
do_list(
char *opt_file,
int opt_verbose G_GNUC_UNUSED)
{
amar_t *archive;
GError *gerror = NULL;
int fd_in;
amar_attr_handling_t handling[] = {
{ 0, 0, NULL, NULL },
};
if (opt_file && !g_str_equal(opt_file, "-")) {
fd_in = open(opt_file, O_RDONLY);
if (fd_in <= 0) {
error("open of '%s' failed: %s\n", opt_file, strerror(errno));
}
} else {
fd_in = fileno(stdin);
}
archive = amar_new(fd_in, O_RDONLY, &gerror);
if (!archive)
error_exit("amar_new", gerror);
if (!amar_read(archive, NULL, handling, list_file_start_cb,
NULL, NULL, &gerror)) {
if (gerror)
error_exit("amar_read", gerror);
else
/* one of the callbacks already printed an error message */
exit(1);
}
printf("size: %lld\n", (long long)amar_size(archive));
amar_close(archive, NULL);
}
int main(
int argc,
char **argv)
{
int opt_create = 0;
int opt_extract = 0;
int opt_list = 0;
int opt_verbose = 0;
char *opt_file = NULL;
glib_init();
while(1) {
int option_index = 0;
int c = getopt_long (argc, argv, "", long_options, &option_index);
if (c == -1) {
break;
}
switch (c) {
case 1: opt_create = 1;
break;
case 2: opt_extract = 1;
break;
case 3: opt_list = 1;
break;
case 4: opt_verbose += 1;
break;
case 5: amfree(opt_file);
opt_file = g_strdup(optarg);
break;
case 6: printf("amarchiver %s\n", VERSION);
exit(0);
break;
}
}
argc -= optind;
argv += optind;
/* check those arguments */
if (opt_create + opt_extract + opt_list == 0) {
g_fprintf(stderr,"--create, --list or --extract must be provided\n");
usage();
}
if (opt_create + opt_extract + opt_list > 1) {
g_fprintf(stderr,"Only one of --create, --list or --extract must be provided\n");
usage();
}
if (opt_create > 0)
do_create(opt_file, opt_verbose, argc, argv);
else if (opt_extract > 0)
do_extract(opt_file, opt_verbose, argc, argv);
else if (opt_list > 0)
do_list(opt_file, opt_verbose);
amfree(opt_file);
return 0;
}