* Authors: the Amanda Development Team. Its members are listed in a
* file named AUTHORS, in the root directory of this distribution.
*/
-/* $Id: chunker.c,v 1.25 2006/03/21 13:23:35 martinea Exp $
+/* $Id: chunker.c,v 1.36 2006/08/24 11:23:32 martinea Exp $
*
* requests remote amandad processes to dump filesystems
*/
#include "protocol.h"
#include "security.h"
#include "stream.h"
-#include "token.h"
-#include "version.h"
#include "fileheader.h"
#include "amfeatures.h"
#include "server_util.h"
#include "util.h"
#include "holding.h"
+#include "timestamp.h"
+#include "sockaddr-util.h"
#ifndef SEEK_SET
#define SEEK_SET 0
int fd; /* file to flush to */
char *filename; /* name of what fd points to */
int filename_seq; /* for chunking */
- long split_size; /* when to chunk */
- long chunk_size; /* size of each chunk */
- long use; /* size to use on this disk */
+ off_t split_size; /* when to chunk */
+ off_t chunk_size; /* size of each chunk */
+ off_t use; /* size to use on this disk */
char buf[DISK_BLOCK_BYTES];
char *datain; /* data buffer markers */
char *dataout;
static char *errstr = NULL;
static int abort_pending;
-static long dumpsize, headersize;
-static long dumpbytes;
-static long filesize;
+static off_t dumpsize;
+static unsigned long headersize;
+static off_t dumpbytes;
+static off_t filesize;
static char *hostname = NULL;
static char *diskname = NULL;
+static char *qdiskname = NULL;
static char *options = NULL;
static char *progname = NULL;
static int level;
static char *dumpdate = NULL;
-static char *datestamp;
-static int command_in_transit;
+static struct cmdargs *command_in_transit = NULL;
+static char *chunker_timestamp = NULL;
static dumpfile_t file;
/* local functions */
-int main P((int, char **));
-static int write_tapeheader P((int, dumpfile_t *));
-static void databuf_init P((struct databuf *, int, char *, long, long));
-static int databuf_flush P((struct databuf *));
+int main(int, char **);
+static ssize_t write_tapeheader(int, dumpfile_t *);
+static void databuf_init(struct databuf *, int, char *, off_t, off_t);
+static int databuf_flush(struct databuf *);
-static int startup_chunker P((char *, long, long, struct databuf *));
-static int do_chunk P((int, struct databuf *));
+static int startup_chunker(char *, off_t, off_t, struct databuf *, int *, int *);
+static int do_chunk(int, struct databuf *, int, int);
+/* we use a function pointer for full_write, so that we can "shim" in
+ * full_write_with_fake_enospc for testing */
+static size_t (*db_full_write)(int fd, const void *buf, size_t count);
+static size_t full_write_with_fake_enospc(int fd, const void *buf, size_t count);
+static off_t fake_enospc_at_byte = -1;
int
-main(main_argc, main_argv)
- int main_argc;
- char **main_argv;
+main(
+ int argc,
+ char ** argv)
{
static struct databuf db;
- struct cmdargs cmdargs;
- cmd_t cmd;
- int infd;
- unsigned long malloc_hist_1, malloc_size_1;
- unsigned long malloc_hist_2, malloc_size_2;
- char *conffile;
+ struct cmdargs *cmdargs;
+ int header_fd;
char *q = NULL;
- char *filename;
- long chunksize, use;
+ char *filename = NULL;
+ off_t chunksize, use;
times_t runtime;
am_feature_t *their_features = NULL;
int a;
+ config_overrides_t *cfg_ovr = NULL;
+ char *cfg_opt = NULL;
+ char *m;
+ int header_socket;
+ int data_socket;
+
+ /*
+ * Configure program for internationalization:
+ * 1) Only set the message locale for now.
+ * 2) Set textdomain for all amanda related programs to "amanda"
+ * We don't want to be forced to support dozens of message catalogs.
+ */
+ setlocale(LC_MESSAGES, "C");
+ textdomain("amanda");
safe_fd(-1, 0);
set_pname("chunker");
+ dbopen(DBG_SUBDIR_SERVER);
+
/* Don't die when child closes pipe */
signal(SIGPIPE, SIG_IGN);
- malloc_size_1 = malloc_inuse(&malloc_hist_1);
+ add_amanda_log_handler(amanda_log_stderr);
+ add_amanda_log_handler(amanda_log_trace_log);
- erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
- set_logerror(logerror);
+ cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
- if (main_argc > 1) {
- config_name = stralloc(main_argv[1]);
- config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
- } else {
- char my_cwd[STR_SIZE];
+ if (argc > 1)
+ cfg_opt = argv[1];
- if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
- error("cannot determine current working directory");
- }
- config_dir = stralloc2(my_cwd, "/");
- if ((config_name = strrchr(my_cwd, '/')) != NULL) {
- config_name = stralloc(config_name + 1);
+ set_config_overrides(cfg_ovr);
+ config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
+
+ if (config_errors(NULL) >= CFGERR_WARNINGS) {
+ config_print_errors();
+ if (config_errors(NULL) >= CFGERR_ERRORS) {
+ g_critical(_("errors processing config file"));
}
}
- safe_cd();
+ safe_cd(); /* do this *after* config_init() */
- conffile = stralloc2(config_dir, CONFFILE_NAME);
- if(read_conffile(conffile)) {
- error("errors processing config file \"%s\"", conffile);
- }
- amfree(conffile);
+ check_running_as(RUNNING_AS_DUMPUSER);
+
+ dbrename(get_config_name(), DBG_SUBDIR_SERVER);
- fprintf(stderr,
- "%s: pid %ld executable %s version %s\n",
+ log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
+ g_fprintf(stderr,
+ _("%s: pid %ld executable %s version %s\n"),
get_pname(), (long) getpid(),
- main_argv[0], version());
+ argv[0], VERSION);
fflush(stderr);
/* now, make sure we are a valid user */
- if (getpwuid(getuid()) == NULL)
- error("can't get login name for my uid %ld", (long)getuid());
-
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
- datestamp = construct_datestamp(NULL);
+ cmdargs = getcmd();
+ if(cmdargs->cmd == START) {
+ if(cmdargs->argc <= 1)
+ error(_("error [dumper START: not enough args: timestamp]"));
+ chunker_timestamp = newstralloc(chunker_timestamp, cmdargs->argv[1]);
+ }
+ else {
+ log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
+ error(_("Didn't get START command"));
+ }
+ free_cmdargs(cmdargs);
+
+ /* set up a fake ENOSPC for testing purposes. Note that this counts
+ * headers as well as data written to disk. */
+ if (getenv("CHUNKER_FAKE_ENOSPC_AT")) {
+ char *env = getenv("CHUNKER_FAKE_ENOSPC_AT");
+ fake_enospc_at_byte = (off_t)atoi(env); /* these values are never > MAXINT */
+ db_full_write = full_write_with_fake_enospc;
+ g_debug("will trigger fake ENOSPC at byte %d", (int)fake_enospc_at_byte);
+ } else {
+ db_full_write = full_write;
+ }
/* do {*/
- cmd = getcmd(&cmdargs);
+ cmdargs = getcmd();
- switch(cmd) {
+ switch(cmdargs->cmd) {
case QUIT:
break;
* use
* options
*/
- cmdargs.argc++; /* true count of args */
- a = 2;
+ a = 1;
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: handle]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: handle]"));
+ /*NOTREACHED*/
}
- handle = newstralloc(handle, cmdargs.argv[a++]);
+ handle = newstralloc(handle, cmdargs->argv[a++]);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: filename]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: filename]"));
+ /*NOTREACHED*/
}
- filename = cmdargs.argv[a++];
+ filename = newstralloc(filename, cmdargs->argv[a++]);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: hostname]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: hostname]"));
+ /*NOTREACHED*/
}
- hostname = newstralloc(hostname, cmdargs.argv[a++]);
+ hostname = newstralloc(hostname, cmdargs->argv[a++]);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: features]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: features]"));
+ /*NOTREACHED*/
}
am_release_feature_set(their_features);
- their_features = am_string_to_feature(cmdargs.argv[a++]);
-
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: diskname]");
+ their_features = am_string_to_feature(cmdargs->argv[a++]);
+ if (!their_features) {
+ error(_("error [chunker PORT-WRITE: invalid feature string]"));
+ /*NOTREACHED*/
}
- diskname = newstralloc(diskname, cmdargs.argv[a++]);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: level]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: diskname]"));
+ /*NOTREACHED*/
}
- level = atoi(cmdargs.argv[a++]);
+ diskname = newstralloc(diskname, cmdargs->argv[a++]);
+ if (qdiskname)
+ amfree(qdiskname);
+ qdiskname = quote_string(diskname); /* qdiskname is a global */
+
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: level]"));
+ /*NOTREACHED*/
+ }
+ level = atoi(cmdargs->argv[a++]);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: dumpdate]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: dumpdate]"));
+ /*NOTREACHED*/
}
- dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
+ dumpdate = newstralloc(dumpdate, cmdargs->argv[a++]);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: chunksize]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: chunksize]"));
+ /*NOTREACHED*/
}
- chunksize = atoi(cmdargs.argv[a++]);
- chunksize = am_floor(chunksize, DISK_BLOCK_KB);
+ chunksize = OFF_T_ATOI(cmdargs->argv[a++]);
+ chunksize = am_floor(chunksize, (off_t)DISK_BLOCK_KB);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: progname]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: progname]"));
+ /*NOTREACHED*/
}
- progname = newstralloc(progname, cmdargs.argv[a++]);
+ progname = newstralloc(progname, cmdargs->argv[a++]);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: use]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: use]"));
+ /*NOTREACHED*/
}
- use = am_floor(atoi(cmdargs.argv[a++]), DISK_BLOCK_KB);
+ use = am_floor(OFF_T_ATOI(cmdargs->argv[a++]), DISK_BLOCK_KB);
- if(a >= cmdargs.argc) {
- error("error [chunker PORT-WRITE: not enough args: options]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: not enough args: options]"));
+ /*NOTREACHED*/
}
- options = newstralloc(options, cmdargs.argv[a++]);
+ options = newstralloc(options, cmdargs->argv[a++]);
- if(a != cmdargs.argc) {
- error("error [chunker PORT-WRITE: too many args: %d != %d]",
- cmdargs.argc, a);
+ if(a != cmdargs->argc) {
+ error(_("error [chunker PORT-WRITE: too many args: %d != %d]"),
+ cmdargs->argc, a);
+ /*NOTREACHED*/
}
- if((infd = startup_chunker(filename, use, chunksize, &db)) < 0) {
- q = squotef("[chunker startup failed: %s]", errstr);
+ if ((header_fd = startup_chunker(filename, use, chunksize, &db,
+ &header_socket, &data_socket)) < 0) {
+ q = quote_string(vstrallocf(_("[chunker startup failed: %s]"), errstr));
putresult(TRYAGAIN, "%s %s\n", handle, q);
- error("startup_chunker failed");
+ error("startup_chunker failed: %s", errstr);
}
- command_in_transit = -1;
- if(infd >= 0 && do_chunk(infd, &db)) {
+ command_in_transit = NULL;
+ if (header_fd >= 0 && do_chunk(header_fd, &db, header_socket, data_socket)) {
char kb_str[NUM_STR_SIZE];
char kps_str[NUM_STR_SIZE];
double rt;
runtime = stopclock();
- rt = runtime.r.tv_sec+runtime.r.tv_usec/1000000.0;
- snprintf(kb_str, sizeof(kb_str), "%ld", dumpsize - headersize);
- snprintf(kps_str, sizeof(kps_str), "%3.1f",
- rt ? dumpsize / rt : 0.0);
- errstr = newvstralloc(errstr,
- "sec ", walltime_str(runtime),
- " kb ", kb_str,
- " kps ", kps_str,
- NULL);
- q = squotef("[%s]", errstr);
- if(command_in_transit != -1)
- cmd = command_in_transit;
- else
- cmd = getcmd(&cmdargs);
- switch(cmd) {
+ rt = g_timeval_to_double(runtime);
+ g_snprintf(kb_str, SIZEOF(kb_str), "%lld",
+ (long long)(dumpsize - (off_t)headersize));
+ g_snprintf(kps_str, SIZEOF(kps_str), "%3.1lf",
+ isnormal(rt) ? (double)dumpsize / rt : 0.0);
+ errstr = newvstrallocf(errstr, "sec %s kb %s kps %s",
+ walltime_str(runtime), kb_str, kps_str);
+ m = vstrallocf("[%s]", errstr);
+ q = quote_string(m);
+ amfree(m);
+ free_cmdargs(cmdargs);
+ if(command_in_transit != NULL) {
+ cmdargs = command_in_transit;
+ command_in_transit = NULL;
+ } else {
+ cmdargs = getcmd();
+ }
+ switch(cmdargs->cmd) {
case DONE:
- putresult(DONE, "%s %ld %s\n",
- handle, dumpsize - headersize, q);
+ putresult(DONE, "%s %lld %s\n", handle,
+ (long long)(dumpsize - (off_t)headersize), q);
log_add(L_SUCCESS, "%s %s %s %d [%s]",
- hostname, diskname, datestamp, level, errstr);
+ hostname, qdiskname, chunker_timestamp, level, errstr);
break;
case BOGUS:
case TRYAGAIN:
case FAILED:
case ABORT_FINISHED:
- if(dumpsize > DISK_BLOCK_KB) {
- putresult(PARTIAL, "%s %ld %s\n",
- handle, dumpsize - headersize, q);
+ if(dumpsize > (off_t)DISK_BLOCK_KB) {
+ putresult(PARTIAL, "%s %lld %s\n", handle,
+ (long long)(dumpsize - (off_t)headersize),
+ q);
log_add(L_PARTIAL, "%s %s %s %d [%s]",
- hostname, diskname, datestamp, level, errstr);
+ hostname, qdiskname, chunker_timestamp, level, errstr);
}
else {
- errstr = newvstralloc(errstr,
- "dumper returned ",
- cmdstr[cmd],
- NULL);
+ errstr = newvstrallocf(errstr,
+ _("dumper returned %s"), cmdstr[cmdargs->cmd]);
amfree(q);
- q = squotef("[%s]",errstr);
+ m = vstrallocf("[%s]",errstr);
+ q = quote_string(m);
+ amfree(m);
putresult(FAILED, "%s %s\n", handle, q);
log_add(L_FAIL, "%s %s %s %d [%s]",
- hostname, diskname, datestamp, level, errstr);
+ hostname, qdiskname, chunker_timestamp, level, errstr);
}
default: break;
}
amfree(q);
- } else if(infd != -2) {
+ } else if (header_fd != -2) {
+ if(q == NULL) {
+ m = vstrallocf("[%s]", errstr);
+ q = quote_string(m);
+ amfree(m);
+ }
if(!abort_pending) {
- if(q == NULL) {
- q = squotef("[%s]", errstr);
- }
putresult(FAILED, "%s %s\n", handle, q);
- log_add(L_FAIL, "%s %s %s %d [%s]",
- hostname, diskname, datestamp, level, errstr);
- amfree(q);
}
+ log_add(L_FAIL, "%s %s %s %d [%s]",
+ hostname, qdiskname, chunker_timestamp, level, errstr);
+ amfree(q);
}
+ amfree(filename);
+ amfree(db.filename);
break;
default:
- if(cmdargs.argc >= 1) {
- q = squote(cmdargs.argv[1]);
- } else if(cmdargs.argc >= 0) {
- q = squote(cmdargs.argv[0]);
+ if(cmdargs->argc >= 1) {
+ q = quote_string(cmdargs->argv[0]);
} else {
- q = stralloc("(no input?)");
+ q = stralloc(_("(no input?)"));
}
putresult(BAD_COMMAND, "%s\n", q);
amfree(q);
break;
}
-/* } while(cmd != QUIT); */
+/* } while(cmdargs->cmd != QUIT); */
+
+ log_add(L_INFO, "pid-done %ld", (long)getpid());
amfree(errstr);
- amfree(datestamp);
+ amfree(chunker_timestamp);
amfree(handle);
amfree(hostname);
amfree(diskname);
+ amfree(qdiskname);
amfree(dumpdate);
amfree(progname);
amfree(options);
- amfree(config_dir);
- amfree(config_name);
+ free_cmdargs(cmdargs);
+ if (command_in_transit)
+ free_cmdargs(command_in_transit);
am_release_feature_set(their_features);
their_features = NULL;
- malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
- if (malloc_size_1 != malloc_size_2)
- malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
+ dbclose();
- exit(0);
+ return (0); /* exit */
}
/*
* on success, or -1 on error.
*/
static int
-startup_chunker(filename, use, chunksize, db)
- char *filename;
- long use;
- long chunksize;
- struct databuf *db;
+startup_chunker(
+ char * filename,
+ off_t use,
+ off_t chunksize,
+ struct databuf * db,
+ int *headersocket,
+ int *datasocket)
{
- int infd, outfd;
+ int header_fd, outfd;
char *tmp_filename, *pc;
- int data_port, data_socket;
-
+ in_port_t header_port, data_port;
+ int header_socket, data_socket;
+ int result;
+ struct addrinfo *res;
+ struct addrinfo *res_addr;
+ sockaddr_union *addr = NULL;
+ sockaddr_union data_addr;
+
+ header_port = 0;
data_port = 0;
- data_socket = stream_server(&data_port, -1, STREAM_BUFSIZE);
+ if ((result = resolve_hostname("localhost", 0, &res, NULL) != 0)) {
+ errstr = newvstrallocf(errstr, _("could not resolve localhost: %s"),
+ gai_strerror(result));
+ return -1;
+ }
+ for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
+ g_debug("ra: %s\n", str_sockaddr((sockaddr_union*)res_addr->ai_addr));
+ if (res_addr->ai_family == AF_INET) {
+ addr = (sockaddr_union *)res_addr->ai_addr;
+ break;
+ }
+ }
+ if (!addr) {
+ addr = (sockaddr_union *)res->ai_addr;
+ g_debug("addr: %s\n", str_sockaddr(addr));
+ }
+
+ header_socket = stream_server(SU_GET_FAMILY(addr), &header_port, 0,
+ STREAM_BUFSIZE, 0);
+ data_socket = stream_server(SU_GET_FAMILY(addr), &data_port, 0,
+ STREAM_BUFSIZE, 0);
+ copy_sockaddr(&data_addr, addr);
+
+ SU_SET_PORT(&data_addr, data_port);
- if(data_socket < 0) {
- errstr = stralloc2("error creating stream server: ", strerror(errno));
+ if (res) freeaddrinfo(res);
+
+ if (header_socket < 0) {
+ errstr = vstrallocf(_("error creating header stream server: %s"), strerror(errno));
+ aclose(data_socket);
+ return -1;
+ }
+
+ if (data_socket < 0) {
+ errstr = vstrallocf(_("error creating data stream server: %s"), strerror(errno));
+ aclose(header_socket);
return -1;
}
- putresult(PORT, "%d\n", data_port);
+ putresult(PORT, "%d %s\n", header_port, str_sockaddr(&data_addr));
- infd = stream_accept(data_socket, CONNECT_TIMEOUT, -1, NETWORK_BLOCK_BYTES);
- if(infd == -1) {
- errstr = stralloc2("error accepting stream: ", strerror(errno));
+ header_fd = stream_accept(header_socket, CONNECT_TIMEOUT, 0,
+ STREAM_BUFSIZE);
+ if (header_fd == -1) {
+ errstr = vstrallocf(_("error accepting header stream: %s"),
+ strerror(errno));
+ aclose(header_socket);
+ aclose(data_socket);
return -1;
}
tmp_filename = vstralloc(filename, ".tmp", NULL);
pc = strrchr(tmp_filename, '/');
+ g_assert(pc != NULL);
*pc = '\0';
mkholdingdir(tmp_filename);
*pc = '/';
if ((outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) {
int save_errno = errno;
-
- errstr = squotef("holding file \"%s\": %s",
+ char *m = vstrallocf(_("holding file \"%s\": %s"),
tmp_filename,
strerror(errno));
+
+ errstr = quote_string(m);
+ amfree(m);
amfree(tmp_filename);
- aclose(infd);
+ aclose(header_fd);
+ aclose(data_socket);
if(save_errno == ENOSPC) {
- putresult(NO_ROOM, "%s %lu", handle, use);
+ putresult(NO_ROOM, "%s %lld\n",
+ handle, (long long)use);
return -2;
} else {
return -1;
amfree(tmp_filename);
databuf_init(db, outfd, filename, use, chunksize);
db->filename_seq++;
- return infd;
+ *headersocket = header_socket;
+ *datasocket = data_socket;
+ return header_fd;
}
static int
-do_chunk(infd, db)
- int infd;
- struct databuf *db;
+do_chunk(
+ int header_fd,
+ struct databuf * db,
+ int header_socket,
+ int data_socket)
{
- int nread;
+ size_t nread;
+ int data_fd;
char header_buf[DISK_BLOCK_BYTES];
startclock();
- dumpsize = headersize = dumpbytes = filesize = 0;
+ dumpsize = dumpbytes = filesize = (off_t)0;
+ headersize = 0;
+ memset(header_buf, 0, sizeof(header_buf));
/*
* The first thing we should receive is the file header, which we
* need to save into "file", as well as write out. Later, the
* chunk code will rewrite it.
*/
- nread = fullread(infd, header_buf, sizeof(header_buf));
- if (nread != DISK_BLOCK_BYTES) {
- char number1[NUM_STR_SIZE];
- char number2[NUM_STR_SIZE];
-
- if(nread < 0) {
- errstr = stralloc2("cannot read header: ", strerror(errno));
+ nread = full_read(header_fd, header_buf, SIZEOF(header_buf));
+ aclose(header_fd);
+ aclose(header_socket);
+ if (nread != sizeof(header_buf)) {
+ if(errno != 0) {
+ errstr = vstrallocf(_("cannot read header: %s"), strerror(errno));
} else {
- snprintf(number1, sizeof(number1), "%d", nread);
- snprintf(number2, sizeof(number2), "%d", DISK_BLOCK_BYTES);
- errstr = vstralloc("cannot read header: got ",
- number1,
- " instead of ",
- number2,
- NULL);
+ errstr = vstrallocf(_("cannot read header: got %zd bytes instead of %zd"),
+ nread, sizeof(header_buf));
}
+ aclose(data_socket);
return 0;
}
- parse_file_header(header_buf, &file, nread);
+ parse_file_header(header_buf, &file, (size_t)nread);
if(write_tapeheader(db->fd, &file)) {
int save_errno = errno;
-
- errstr = squotef("write_tapeheader file \"%s\": %s",
+ char *m = vstrallocf(_("write_tapeheader file %s: %s"),
db->filename, strerror(errno));
+ errstr = quote_string(m);
+ amfree(m);
if(save_errno == ENOSPC) {
- putresult(NO_ROOM, "%s %lu\n", handle,
- db->use+db->split_size-dumpsize);
+ putresult(NO_ROOM, "%s %lld\n", handle,
+ (long long)(db->use+db->split_size-dumpsize));
}
+ aclose(data_socket);
return 0;
}
- dumpsize += DISK_BLOCK_KB;
- filesize = DISK_BLOCK_KB;
+ dumpsize += (off_t)DISK_BLOCK_KB;
+ filesize = (off_t)DISK_BLOCK_KB;
headersize += DISK_BLOCK_KB;
+ /* open the data socket */
+ data_fd = stream_accept(data_socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE);
+
+ if (data_fd == -1) {
+ errstr = vstrallocf(_("error accepting data stream: %s"),
+ strerror(errno));
+ aclose(data_socket);
+ return 0;
+ }
+
/*
* We've written the file header. Now, just write data until the
* end.
*/
- while ((nread = fullread(infd, db->buf, db->datalimit - db->datain)) > 0) {
+ while ((nread = full_read(data_fd, db->buf,
+ (size_t)(db->datalimit - db->datain))) > 0) {
db->datain += nread;
while(db->dataout < db->datain) {
if(!databuf_flush(db)) {
+ aclose(data_fd);
+ aclose(data_socket);
return 0;
}
}
}
while(db->dataout < db->datain) {
if(!databuf_flush(db)) {
+ aclose(data_fd);
+ aclose(data_socket);
return 0;
}
}
- if(dumpbytes > 0) {
- dumpsize++; /* count partial final KByte */
- filesize++;
+ if(dumpbytes > (off_t)0) {
+ dumpsize += (off_t)1; /* count partial final KByte */
+ filesize += (off_t)1;
}
+ aclose(data_fd);
+ aclose(data_socket);
return 1;
}
* Initialize a databuf. Takes a writeable file descriptor.
*/
static void
-databuf_init(db, fd, filename, use, chunk_size)
- struct databuf *db;
- int fd;
- char *filename;
- long use;
- long chunk_size;
+databuf_init(
+ struct databuf * db,
+ int fd,
+ char * filename,
+ off_t use,
+ off_t chunk_size)
{
db->fd = fd;
db->filename = stralloc(filename);
- db->filename_seq = 0;
+ db->filename_seq = (off_t)0;
db->chunk_size = chunk_size;
db->split_size = (db->chunk_size > use) ? use : db->chunk_size;
- db->use = (use>db->split_size) ? use - db->split_size : 0;
+ db->use = (use > db->split_size) ? use - db->split_size : (off_t)0;
db->datain = db->dataout = db->buf;
- db->datalimit = db->buf + sizeof(db->buf);
+ db->datalimit = db->buf + SIZEOF(db->buf);
}
* Write out the buffer to the backing file
*/
static int
-databuf_flush(db)
- struct databuf *db;
+databuf_flush(
+ struct databuf * db)
{
- struct cmdargs cmdargs;
+ struct cmdargs *cmdargs = NULL;
int rc = 1;
- int written;
- long left_in_chunk;
+ size_t size_to_write;
+ size_t written;
+ off_t left_in_chunk;
char *arg_filename = NULL;
char *new_filename = NULL;
char *tmp_filename = NULL;
/*
* See if we need to split this file.
*/
- while (db->split_size > 0 && dumpsize >= db->split_size) {
- if( db->use == 0 ) {
+ while (db->split_size > (off_t)0 && dumpsize >= db->split_size) {
+ if( db->use == (off_t)0 ) {
/*
* Probably no more space on this disk. Request some more.
*/
- cmd_t cmd;
-
putresult(RQ_MORE_DISK, "%s\n", handle);
- cmd = getcmd(&cmdargs);
- if(command_in_transit == -1 &&
- (cmd == DONE || cmd == TRYAGAIN || cmd == FAILED)) {
- command_in_transit = cmd;
- cmd = getcmd(&cmdargs);
+ cmdargs = getcmd();
+ if(command_in_transit == NULL &&
+ (cmdargs->cmd == DONE || cmdargs->cmd == TRYAGAIN || cmdargs->cmd == FAILED)) {
+ command_in_transit = cmdargs;
+ cmdargs = getcmd();
}
- if(cmd == CONTINUE) {
+ if(cmdargs->cmd == CONTINUE) {
/*
* CONTINUE
* serial
* chunksize
* use
*/
- cmdargs.argc++; /* true count of args */
- a = 3;
+ a = 2; /* skip CONTINUE and serial */
- if(a >= cmdargs.argc) {
- error("error [chunker CONTINUE: not enough args: filename]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker CONTINUE: not enough args: filename]"));
+ /*NOTREACHED*/
}
- arg_filename = newstralloc(arg_filename, cmdargs.argv[a++]);
+ arg_filename = newstralloc(arg_filename, cmdargs->argv[a++]);
- if(a >= cmdargs.argc) {
- error("error [chunker CONTINUE: not enough args: chunksize]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker CONTINUE: not enough args: chunksize]"));
+ /*NOTREACHED*/
}
- db->chunk_size = atoi(cmdargs.argv[a++]);
- db->chunk_size = am_floor(db->chunk_size, DISK_BLOCK_KB);
+ db->chunk_size = OFF_T_ATOI(cmdargs->argv[a++]);
+ db->chunk_size = am_floor(db->chunk_size, (off_t)DISK_BLOCK_KB);
- if(a >= cmdargs.argc) {
- error("error [chunker CONTINUE: not enough args: use]");
+ if(a >= cmdargs->argc) {
+ error(_("error [chunker CONTINUE: not enough args: use]"));
+ /*NOTREACHED*/
}
- db->use = atoi(cmdargs.argv[a++]);
+ db->use = OFF_T_ATOI(cmdargs->argv[a++]);
- if(a != cmdargs.argc) {
- error("error [chunker CONTINUE: too many args: %d != %d]",
- cmdargs.argc, a);
+ if(a != cmdargs->argc) {
+ error(_("error [chunker CONTINUE: too many args: %d != %d]"),
+ cmdargs->argc, a);
+ /*NOTREACHED*/
}
if(strcmp(db->filename, arg_filename) == 0) {
left_in_chunk = db->chunk_size - filesize;
if(left_in_chunk > db->use) {
db->split_size += db->use;
- db->use = 0;
+ db->use = (off_t)0;
} else {
db->split_size += left_in_chunk;
db->use -= left_in_chunk;
}
- if(left_in_chunk > 0) {
+ if(left_in_chunk > (off_t)0) {
/*
* We still have space in this chunk.
*/
*/
db->filename = newstralloc(db->filename, arg_filename);
}
- } else if(cmd == ABORT) {
+ } else if(cmdargs->cmd == ABORT) {
abort_pending = 1;
- errstr = newstralloc(errstr, "ERROR");
+ errstr = newstralloc(errstr, cmdargs->argv[1]);
putresult(ABORT_FINISHED, "%s\n", handle);
rc = 0;
goto common_exit;
} else {
- if(cmdargs.argc >= 1) {
- q = squote(cmdargs.argv[1]);
- } else if(cmdargs.argc >= 0) {
- q = squote(cmdargs.argv[0]);
+ if(cmdargs->argc >= 1) {
+ q = quote_string(cmdargs->argv[0]);
} else {
- q = stralloc("(no input?)");
+ q = stralloc(_("(no input?)"));
}
- error("error [bad command after RQ-MORE-DISK: \"%s\"]", q);
+ error(_("error [bad command after RQ-MORE-DISK: \"%s\"]"), q);
+ /*NOTREACHED*/
}
}
* First, open the new chunk file, and give it a new header
* that has no cont_filename pointer.
*/
- snprintf(sequence, sizeof(sequence), "%d", db->filename_seq);
+ g_snprintf(sequence, SIZEOF(sequence), "%d", db->filename_seq);
new_filename = newvstralloc(new_filename,
db->filename,
".",
".tmp",
NULL);
pc = strrchr(tmp_filename, '/');
+ g_assert(pc != NULL); /* Only a problem if db->filename has no /. */
*pc = '\0';
mkholdingdir(tmp_filename);
*pc = '/';
newfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
if (newfd == -1) {
int save_errno = errno;
+ char *m;
if(save_errno == ENOSPC) {
- putresult(NO_ROOM, "%s %lu\n",
- handle,
- db->use+db->split_size-dumpsize);
- db->use = 0; /* force RQ_MORE_DISK */
+ putresult(NO_ROOM, "%s %lld\n", handle,
+ (long long)(db->use+db->split_size-dumpsize));
+ db->use = (off_t)0; /* force RQ_MORE_DISK */
db->split_size = dumpsize;
continue;
}
- errstr = squotef("creating chunk holding file \"%s\": %s",
+ m = vstrallocf(_("creating chunk holding file \"%s\": %s"),
tmp_filename,
strerror(errno));
+ errstr = quote_string(m);
+ amfree(m);
aclose(db->fd);
rc = 0;
goto common_exit;
file.cont_filename[0] = '\0';
if(write_tapeheader(newfd, &file)) {
int save_errno = errno;
+ char *m;
aclose(newfd);
if(save_errno == ENOSPC) {
- putresult(NO_ROOM, "%s %lu\n",
- handle,
- db->use+db->split_size-dumpsize);
- db->use = 0; /* force RQ_MORE DISK */
+ if (unlink(tmp_filename) < 0) {
+ g_debug("could not delete '%s'; ignoring", tmp_filename);
+ }
+ putresult(NO_ROOM, "%s %lld\n", handle,
+ (long long)(db->use+db->split_size-dumpsize));
+ db->use = (off_t)0; /* force RQ_MORE DISK */
db->split_size = dumpsize;
+ file.type = save_type;
continue;
}
- errstr = squotef("write_tapeheader file \"%s\": %s",
+ m = vstrallocf(_("write_tapeheader file %s: %s"),
tmp_filename,
strerror(errno));
+ errstr = quote_string(m);
+ amfree(m);
rc = 0;
goto common_exit;
}
* Now, update the header of the current file to point
* to the next chunk, and then close it.
*/
- if (lseek(db->fd, (off_t)0, SEEK_SET) < 0) {
- errstr = squotef("lseek holding file \"%s\": %s",
+ if (lseek(db->fd, (off_t)0, SEEK_SET) < (off_t)0) {
+ char *m = vstrallocf(_("lseek holding file %s: %s"),
db->filename,
strerror(errno));
+ errstr = quote_string(m);
+ amfree(m);
aclose(newfd);
rc = 0;
goto common_exit;
}
file.type = save_type;
- strncpy(file.cont_filename, new_filename, sizeof(file.cont_filename));
- file.cont_filename[sizeof(file.cont_filename)] = '\0';
+ strncpy(file.cont_filename, new_filename, SIZEOF(file.cont_filename));
+ file.cont_filename[SIZEOF(file.cont_filename)-1] = '\0';
if(write_tapeheader(db->fd, &file)) {
- errstr = squotef("write_tapeheader file \"%s\": %s",
+ char * m = vstrallocf(_("write_tapeheader file \"%s\": %s"),
db->filename,
strerror(errno));
+ errstr = quote_string(m);
+ amfree(m);
aclose(newfd);
unlink(tmp_filename);
rc = 0;
/*
* Update when we need to chunk again
*/
- if(db->use <= DISK_BLOCK_KB) {
+ if(db->use <= (off_t)DISK_BLOCK_KB) {
/*
* Cheat and use one more block than allowed so we can make
* some progress.
*/
- db->split_size += 2 * DISK_BLOCK_KB;
- db->use = 0;
+ db->split_size += (off_t)(2 * DISK_BLOCK_KB);
+ db->use = (off_t)0;
} else if(db->chunk_size > db->use) {
db->split_size += db->use;
- db->use = 0;
+ db->use = (off_t)0;
} else {
db->split_size += db->chunk_size;
db->use -= db->chunk_size;
amfree(tmp_filename);
amfree(new_filename);
- dumpsize += DISK_BLOCK_KB;
- filesize = DISK_BLOCK_KB;
+ dumpsize += (off_t)DISK_BLOCK_KB;
+ filesize = (off_t)DISK_BLOCK_KB;
headersize += DISK_BLOCK_KB;
db->filename_seq++;
}
/*
* Write out the buffer
*/
- written = fullwrite(db->fd, db->dataout, db->datain - db->dataout);
+ size_to_write = (size_t)(db->datain - db->dataout);
+ written = db_full_write(db->fd, db->dataout, size_to_write);
if (written > 0) {
db->dataout += written;
- dumpbytes += written;
+ dumpbytes += (off_t)written;
}
- dumpsize += (dumpbytes / 1024);
- filesize += (dumpbytes / 1024);
+ dumpsize += (dumpbytes / (off_t)1024);
+ filesize += (dumpbytes / (off_t)1024);
dumpbytes %= 1024;
- if (written < 0) {
+ if (written < size_to_write) {
if (errno != ENOSPC) {
- errstr = squotef("data write: %s", strerror(errno));
+ char *m = vstrallocf(_("data write: %s"), strerror(errno));
+ errstr = quote_string(m);
+ amfree(m);
rc = 0;
goto common_exit;
}
* NO-ROOM is informational only. Later, RQ_MORE_DISK will be
* issued to use another holding disk.
*/
- putresult(NO_ROOM, "%s %lu\n", handle, db->use+db->split_size-dumpsize);
- db->use = 0; /* force RQ_MORE_DISK */
+ putresult(NO_ROOM, "%s %lld\n", handle,
+ (long long)(db->use+db->split_size-dumpsize));
+ db->use = (off_t)0; /* force RQ_MORE_DISK */
db->split_size = dumpsize;
goto common_exit;
}
common_exit:
+ if (cmdargs)
+ free_cmdargs(cmdargs);
amfree(new_filename);
- amfree(tmp_filename);
+ /*@i@*/ amfree(tmp_filename);
amfree(arg_filename);
return rc;
}
/*
- * Send an Amanda dump header to the output file.
+ * Send an Amanda dump header to the output file and set file->blocksize
*/
-static int
-write_tapeheader(outfd, file)
- int outfd;
- dumpfile_t *file;
+static ssize_t
+write_tapeheader(
+ int outfd,
+ dumpfile_t *file)
{
- char buffer[DISK_BLOCK_BYTES];
- int written;
+ char *buffer;
+ size_t written;
file->blocksize = DISK_BLOCK_BYTES;
- build_header(buffer, file, sizeof(buffer));
+ if (debug_chunker > 1)
+ dump_dumpfile_t(file);
+ buffer = build_header(file, NULL, DISK_BLOCK_BYTES);
+ if (!buffer) /* this shouldn't happen */
+ error(_("header does not fit in %zd bytes"), (size_t)DISK_BLOCK_BYTES);
- written = fullwrite(outfd, buffer, sizeof(buffer));
- if(written == sizeof(buffer)) return 0;
- if(written < 0) return written;
- errno = ENOSPC;
- return -1;
+ written = db_full_write(outfd, buffer, DISK_BLOCK_BYTES);
+ amfree(buffer);
+ if(written == DISK_BLOCK_BYTES) return 0;
+
+ /* fake ENOSPC when we get a short write without errno set */
+ if(errno == 0)
+ errno = ENOSPC;
+
+ return (ssize_t)-1;
+}
+
+static size_t
+full_write_with_fake_enospc(
+ int fd,
+ const void *buf,
+ size_t count)
+{
+ size_t rc;
+
+ //g_debug("HERE %zd %zd", count, (size_t)fake_enospc_at_byte);
+
+ if (count <= (size_t)fake_enospc_at_byte) {
+ fake_enospc_at_byte -= count;
+ return full_write(fd, buf, count);
+ }
+
+ /* if we get here, the caller has requested a size that is less
+ * than fake_enospc_at_byte. */
+ count = fake_enospc_at_byte;
+ g_debug("returning fake ENOSPC");
+
+ if (fake_enospc_at_byte) {
+ rc = full_write(fd, buf, fake_enospc_at_byte);
+ if (rc == (size_t)fake_enospc_at_byte) {
+ /* full_write succeeded, so fake a failure */
+ errno = ENOSPC;
+ }
+ } else {
+ /* no bytes to write; just fake an error */
+ errno = ENOSPC;
+ rc = 0;
+ }
+
+ /* switch back to calling full_write directly */
+ fake_enospc_at_byte = -1;
+ db_full_write = full_write;
+ return rc;
}