Imported Upstream version 2.5.1
[debian/amanda] / server-src / taper.c
index 612e822012a86a21be9925ed26e985b85bbb6d4e..37d17ee51054e0b5017ca32c155a7dcfeebf3d74 100644 (file)
  * Authors: the Amanda Development Team.  Its members are listed in a
  * file named AUTHORS, in the root directory of this distribution.
  */
-/* $Id: taper.c,v 1.47.2.14.4.8.2.17.2.4 2005/10/11 11:10:20 martinea Exp $
+/* $Id: taper.c,v 1.144 2006/08/24 11:23:32 martinea Exp $
  *
  * moves files from holding disk to tape, or from a socket to tape
  */
 
 #include "amanda.h"
+#include "util.h"
 #include "conffile.h"
 #include "tapefile.h"
 #include "clock.h"
 #include "stream.h"
+#include "holding.h"
 #include "logfile.h"
 #include "tapeio.h"
 #include "changer.h"
 #include "amfeatures.h"
 #include "fileheader.h"
 #include "server_util.h"
+#include "taperscan.c"
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
 #ifdef HAVE_LIBVTBLC
 #include <vtblc.h>
 #include <strings.h>
+#include <math.h>
+
 
 static int vtbl_no   = -1;
 static int len       =  0;
 static int offset    =  0;
 static char *datestr = NULL;
 static char start_datestr[20];
-time_t raw_time;
-struct tm tape_time;
-struct tm backup_time;
-struct tm *tape_timep = &tape_time;
+static time_t raw_time;
+static struct tm tape_time;
+static struct tm backup_time;
+static struct tm *tape_timep = &tape_time;
 typedef struct vtbl_lbls {
     u_int8_t  label[45];
     u_int8_t  date[20];
@@ -66,25 +76,46 @@ static vtbl_lbls vtbl_entry[MAX_VOLUMES];
  * XXX advance to next tape first in next_tape
  * XXX label is being read twice?
  */
+static off_t splitsize = (off_t)0; /* max size of dumpfile before split (Kb) */
+static off_t mmap_splitsize = (off_t)0;
+static char *mmap_filename = NULL;
+static char *mmap_splitbuf = NULL;
+static char *mem_splitbuf = NULL;
+static char *splitbuf = NULL;
+static off_t mem_splitsize = (off_t)0;
+static char *splitbuf_wr_ptr = NULL; /* the number of Kb we've written into splitbuf */
+int orig_holdfile = -1;
 
 /* NBUFS replaced by conf_tapebufs */
 /* #define NBUFS               20 */
-int conf_tapebufs;
+static int conf_tapebufs;
+
+static off_t maxseek = (off_t)1 << ((SIZEOF(off_t) * 8) - 11);
+
+static char *holdfile_path = NULL;
+static char *holdfile_path_thischunk = NULL;
+static int num_holdfile_chunks = 0;
+static off_t holdfile_offset_thischunk = (off_t)0;
+static int mmap_splitbuffer_fd = -1;
+
+#define MODE_NONE 0
+#define MODE_FILE_WRITE 1
+#define MODE_PORT_WRITE 2
+
+static mode_t mode = MODE_NONE;
 
 /* This is now the number of empties, not full bufs */
 #define THRESHOLD      1
 
 #define CONNECT_TIMEOUT 2*60
 
-
-
 #define EMPTY 1
 #define FILLING 2
 #define FULL 3
 
 typedef struct buffer_s {
     long status;
-    unsigned int size;
+    ssize_t size;
     char *buffer;
 } buffer_t;
 
@@ -92,29 +123,47 @@ typedef struct buffer_s {
 #define prevbuf(p)    ((p) == buftable? buftable+conf_tapebufs-1 : (p)-1)
 
 /* major modules */
-int main P((int main_argc, char **main_argv));
-void file_reader_side P((int rdpipe, int wrpipe));
-void tape_writer_side P((int rdpipe, int wrpipe));
+int main(int main_argc, char **main_argv);
+void file_reader_side(int rdpipe, int wrpipe);
+void tape_writer_side(int rdpipe, int wrpipe);
+void put_syncpipe_fault_result(char *handle);
 
 /* shared-memory routines */
-char *attach_buffers P((unsigned int size));
-void detach_buffers P((char *bufp));
-void destroy_buffers P((void));
+char *attach_buffers(size_t size);
+void detach_buffers(char *bufp);
+void destroy_buffers(void);
+#define REMOVE_SHARED_MEMORY() \
+    detach_buffers(buffers); \
+    if (strcmp(procname, "reader") == 0) { \
+       destroy_buffers(); \
+    }
 
 /* synchronization pipe routines */
-void syncpipe_init P((int rd, int wr));
-char syncpipe_get P((void));
-int  syncpipe_getint P((void));
-char *syncpipe_getstr P((void));
-void syncpipe_put P((int ch));
-void syncpipe_putint P((int i));
-void syncpipe_putstr P((char *str));
+void syncpipe_init(int rd, int wr);
+void syncpipe_read_error(ssize_t rc, ssize_t expected);
+void syncpipe_write_error(ssize_t rc, ssize_t expected);
+int syncpipe_get(int *intp);
+int syncpipe_getint(void);
+char *syncpipe_getstr(void);
+int syncpipe_put(int ch, int intval);
+int syncpipe_putint(int i);
+int syncpipe_putstr(const char *str);
 
 /* tape manipulation subsystem */
-int first_tape P((char *new_datestamp));
-int next_tape P((int writerr));
-int end_tape P((int writerr));
-int write_filemark P((void));
+int first_tape(char *new_datestamp);
+int next_tape(int writerr);
+int end_tape(int writerr);
+int write_filemark(void);
+
+/* support crap */
+int seek_holdfile(int fd, buffer_t *bp, off_t kbytes);
+
+/* signal handling */
+static void install_signal_handlers(void);
+static void signal_handler(int);
+
+/* exit routine */
+static void cleanup(void);
 
 /*
  * ========================================================================
@@ -122,7 +171,7 @@ int write_filemark P((void));
  *
  */
 int interactive;
-int writerpid;
+pid_t writerpid;
 times_t total_wait;
 #ifdef TAPER_DEBUG
 int bufdebug = 1;
@@ -132,10 +181,11 @@ int bufdebug = 0;
 
 char *buffers = NULL;
 buffer_t *buftable = NULL;
+int err;
 
 char *procname = "parent";
 
-char *taper_datestamp = NULL;
+char *taper_timestamp = NULL;
 char *label = NULL;
 int filenum;
 char *errstr = NULL;
@@ -143,12 +193,20 @@ int tape_fd = -1;
 char *tapedev = NULL;
 char *tapetype = NULL;
 tapetype_t *tt = NULL;
-long tt_blocksize;
-long tt_blocksize_kb;
-long buffer_size;
+size_t tt_blocksize;
+size_t tt_blocksize_kb;
+size_t buffer_size;
 int tt_file_pad;
 static unsigned long malloc_hist_1, malloc_size_1;
 static unsigned long malloc_hist_2, malloc_size_2;
+dumpfile_t file;
+dumpfile_t *save_holdfile = NULL;
+off_t cur_span_chunkstart = (off_t)0; /* start of current split dump chunk (Kb) */
+char *holdfile_name;
+int num_splits = 0;
+int expected_splits = 0;
+int num_holdfiles = 0;
+times_t curdump_rt;
 
 am_feature_t *their_features = NULL;
 
@@ -164,38 +222,53 @@ int first_seg, last_seg;
  * MAIN PROGRAM
  *
  */
-int main(main_argc, main_argv)
-int main_argc;
-char **main_argv;
+int
+main(
+    int main_argc,
+    char **main_argv)
 {
     int p2c[2], c2p[2];                /* parent-to-child, child-to-parent pipes */
     char *conffile;
-    unsigned int size;
+    size_t size;
     int i;
-    int j;
-    int page_size;
+    size_t j;
+    size_t page_size;
     char *first_buffer;
+    int    new_argc,   my_argc;
+    char **new_argv, **my_argv;
 
     safe_fd(-1, 0);
 
     set_pname("taper");
 
+    dbopen("server");
+
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
+
     malloc_size_1 = malloc_inuse(&malloc_hist_1);
 
+    parse_server_conf(main_argc, main_argv, &new_argc, &new_argv);
+    my_argc = new_argc;
+    my_argv = new_argv;
+
     fprintf(stderr, "%s: pid %ld executable %s version %s\n",
-           get_pname(), (long) getpid(), main_argv[0], version());
+           get_pname(), (long) getpid(), my_argv[0], version());
+    dbprintf(("%s: pid %ld executable %s version %s\n",
+           get_pname(), (long) getpid(), my_argv[0], version()));
     fflush(stderr);
 
-    if (main_argc > 1 && main_argv[1][0] != '-') {
-       config_name = stralloc(main_argv[1]);
-       config_dir = vstralloc(CONFIG_DIR, "/", main_argv[1], "/", NULL);
-       main_argc--;
-       main_argv++;
+    if (my_argc > 1 && my_argv[1][0] != '-') {
+       config_name = stralloc(my_argv[1]);
+       config_dir = vstralloc(CONFIG_DIR, "/", my_argv[1], "/", NULL);
+       my_argc--;
+       my_argv++;
     } else {
        char my_cwd[STR_SIZE];
 
-       if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
+       if (getcwd(my_cwd, SIZEOF(my_cwd)) == NULL) {
            error("cannot determine current working directory");
+           /*NOTREACHED*/
        }
        config_dir = stralloc2(my_cwd, "/");
        if ((config_name = strrchr(my_cwd, '/')) != NULL) {
@@ -205,37 +278,48 @@ char **main_argv;
 
     safe_cd();
 
+    install_signal_handlers();
+    atexit(cleanup);
+
     /* print prompts and debug messages if running interactive */
 
-    interactive = (main_argc > 1 && strcmp(main_argv[1],"-t") == 0);
-    if(interactive) {
+    interactive = (my_argc > 1 && strcmp(my_argv[1],"-t") == 0);
+    if (interactive) {
        erroutput_type = ERR_INTERACTIVE;
     } else {
        erroutput_type = ERR_AMANDALOG;
        set_logerror(logerror);
     }
 
+    free_new_argv(new_argc, new_argv);
+
     conffile = stralloc2(config_dir, CONFFILE_NAME);
-    if(read_conffile(conffile)) {
+    if (read_conffile(conffile)) {
        error("errors processing config file \"%s\"", conffile);
+       /*NOTREACHED*/
     }
     amfree(conffile);
 
+    dbrename(config_name, DBG_SUBDIR_SERVER);
+
+    report_bad_conf_arg();
+
     conf_tapelist = getconf_str(CNF_TAPELIST);
     if (*conf_tapelist == '/') {
        conf_tapelist = stralloc(conf_tapelist);
     } else {
        conf_tapelist = stralloc2(config_dir, conf_tapelist);
     }
-    if(read_tapelist(conf_tapelist)) {
+    if (read_tapelist(conf_tapelist)) {
        error("could not load tapelist \"%s\"", conf_tapelist);
+       /*NOTREACHED*/
     }
 
-    tapedev    = getconf_str(CNF_TAPEDEV);
+    tapedev    = stralloc(getconf_str(CNF_TAPEDEV));
     tapetype    = getconf_str(CNF_TAPETYPE);
     tt         = lookup_tapetype(tapetype);
 #ifdef HAVE_LIBVTBLC
-    rawtapedev = getconf_str(CNF_RAWTAPEDEV);
+    rawtapedev = stralloc(getconf_str(CNF_RAWTAPEDEV));
 #endif /* HAVE_LIBVTBLC */
     tapedays   = getconf_int(CNF_TAPECYCLE);
     labelstr   = getconf_str(CNF_LABELSTR);
@@ -245,38 +329,52 @@ char **main_argv;
 
     conf_tapebufs = getconf_int(CNF_TAPEBUFS);
 
-    tt_blocksize_kb = tt->blocksize;
+    tt_blocksize_kb = (size_t)tapetype_get_blocksize(tt);
     tt_blocksize = tt_blocksize_kb * 1024;
-    tt_file_pad = tt->file_pad;
+    tt_file_pad = tapetype_get_file_pad(tt);
 
-    if(interactive) {
+    if (interactive) {
        fprintf(stderr,"taper: running in interactive test mode\n");
+       dbprintf(("taper: running in interactive test mode\n"));
        fflush(stderr);
     }
 
     /* create read/write syncronization pipes */
 
-    if(pipe(p2c) || pipe(c2p))
+    if (pipe(p2c)) {
        error("creating sync pipes: %s", strerror(errno));
+       /*NOTREACHED*/
+    }
+    if (pipe(c2p)) {
+       error("creating sync pipes: %s", strerror(errno));
+       /*NOTREACHED*/
+    }
 
     /* create shared memory segment */
 
 #if defined(HAVE_GETPAGESIZE)
-    page_size = getpagesize();
-    fprintf(stderr, "%s: page size is %d\n", get_pname(), page_size);
+    page_size = (size_t)getpagesize();
+    fprintf(stderr, "%s: page size = " SIZE_T_FMT "\n",
+               get_pname(), (SIZE_T_FMT_TYPE)page_size);
+    dbprintf(("%s: page size = " SIZE_T_FMT "\n", get_pname(),
+               (SIZE_T_FMT_TYPE)page_size));
 #else
     page_size = 1024;
-    fprintf(stderr, "%s: getpagesize() not available, using %d\n",
-           get_pname(),
-           page_size);
+    fprintf(stderr, "%s: getpagesize() not available, using " SIZE_T_FMT "\n",
+           get_pname(), page_size);
+    dbprintf((stderr, "%s: getpagesize() not available, using " SIZE_T_FMT "\n",
+           get_pname(), page_size));
 #endif
     buffer_size = am_round(tt_blocksize, page_size);
-    fprintf(stderr, "%s: buffer size is %ld\n", get_pname(), buffer_size);
-    while(conf_tapebufs > 0) {
+    fprintf(stderr, "%s: buffer size is " SIZE_T_FMT "\n",
+           get_pname(), (SIZE_T_FMT_TYPE)buffer_size);
+    dbprintf(("%s: buffer size is " SIZE_T_FMT "\n",
+           get_pname(), (SIZE_T_FMT_TYPE)buffer_size));
+    while (conf_tapebufs > 0) {
        size  = page_size;
        size += conf_tapebufs * buffer_size;
-       size += conf_tapebufs * sizeof(buffer_t);
-       if((buffers = attach_buffers(size)) != NULL) {
+       size += conf_tapebufs * SIZEOF(buffer_t);
+       if ((buffers = attach_buffers(size)) != NULL) {
            break;
        }
        log_add(L_INFO, "attach_buffers: (%d tapebuf%s: %d bytes) %s",
@@ -286,45 +384,50 @@ char **main_argv;
                        strerror(errno));
        conf_tapebufs--;
     }
-    if(buffers == NULL) {
+    if (buffers == NULL) {
        error("cannot allocate shared memory");
+       /*NOTREACHED*/
     }
-    i = (buffers - (char *)0) & (page_size - 1);  /* page boundary offset */
-    if(i != 0) {
+
+    /* page boundary offset */
+    i = (int)((buffers - (char *)0) & (page_size - 1));
+    if (i != 0) {
        first_buffer = buffers + page_size - i;
-       fprintf(stderr, "%s: shared memory at %p, first buffer at %p\n",
+       dbprintf(("%s: shared memory at %p, first buffer at %p\n",
                get_pname(),
-               buffers,
-               first_buffer);
+               (void *)buffers,
+               (void *)first_buffer));
     } else {
        first_buffer = buffers;
     }
-    buftable = (buffer_t *)(first_buffer + conf_tapebufs * buffer_size);
-    memset(buftable, 0, conf_tapebufs * sizeof(buffer_t));
-    if(conf_tapebufs < 10) {
+
+    /*LINTED  first_buffer, conf_tapebufs and buffer size are all * pagesize */
+    buftable = (buffer_t *)(first_buffer + (conf_tapebufs * buffer_size));
+    memset(buftable, 0, conf_tapebufs * SIZEOF(buffer_t));
+    if (conf_tapebufs < 10) {
        j = 1;
-    } else if(conf_tapebufs < 100) {
+    } else if (conf_tapebufs < 100) {
        j = 2;
     } else {
        j = 3;
     }
-    for(i = 0; i < conf_tapebufs; i++) {
+    for (i = 0; i < conf_tapebufs; i++) {
        buftable[i].buffer = first_buffer + i * buffer_size;
-       fprintf(stderr, "%s: buffer[%0*d] at %p\n",
+       dbprintf(("%s: buffer[%0*d] at %p\n",
                get_pname(),
-               j, i,
-               buftable[i].buffer);
+               (int)j, i,
+               (void *)buftable[i].buffer));
     }
-    fprintf(stderr, "%s: buffer structures at %p for %d bytes\n",
+    dbprintf(("%s: buffer structures at %p for %d bytes\n",
            get_pname(),
-           buftable,
-           (int)(conf_tapebufs * sizeof(buffer_t)));
+           (void *)buftable,
+           (int)(conf_tapebufs * SIZEOF(buffer_t))));
 
     /* fork off child writer process, parent becomes reader process */
-
     switch(writerpid = fork()) {
     case -1:
        error("fork: %s", strerror(errno));
+       /*NOTREACHED*/
 
     case 0:    /* child */
        aclose(p2c[1]);
@@ -332,6 +435,7 @@ char **main_argv;
 
        tape_writer_side(p2c[0], c2p[1]);
        error("tape writer terminated unexpectedly");
+       /*NOTREACHED*/
 
     default:   /* parent */
        aclose(p2c[0]);
@@ -339,9 +443,10 @@ char **main_argv;
 
        file_reader_side(c2p[0], p2c[1]);
        error("file reader terminated unexpectedly");
+       /*NOTREACHED*/
     }
 
-    /* NOTREACHED */
+    /*NOTREACHED*/
     return 0;
 }
 
@@ -351,30 +456,237 @@ char **main_argv;
  * FILE READER SIDE
  *
  */
-void read_file P((int fd, char *handle,
+int read_file(int fd, char *handle,
                  char *host, char *disk, char *datestamp, 
-                 int level, int port_flag));
-int taper_fill_buffer P((int fd, buffer_t *bp, int buflen));
-void dumpbufs P((char *str1));
-void dumpstatus P((buffer_t *bp));
+                 int level);
+ssize_t taper_fill_buffer(int fd, buffer_t *bp, size_t buflen);
+void dumpbufs(char *str1);
+void dumpstatus(buffer_t *bp);
+ssize_t get_next_holding_file(int fd, buffer_t *bp, char **strclosing, size_t rc);
+int predict_splits(char *filename);
+void create_split_buffer(char *split_diskbuffer, size_t fallback_splitsize, char *id_string);
+void free_split_buffer(void);
 
-void file_reader_side(rdpipe, wrpipe)
-int rdpipe, wrpipe;
+
+/*
+ * Create a buffer, either in an mmapped file or in memory, where PORT-WRITE
+ * dumps can buffer the current split chunk in case of retry.
+ */
+void
+create_split_buffer(
+    char *split_diskbuffer,
+    size_t fallback_splitsize,
+    char *id_string)
+{
+    char *buff_err = NULL;
+    off_t offset;
+    char *splitbuffer_path = NULL;
+    
+    /* don't bother if we're not actually splitting */
+    if (splitsize <= (off_t)0) {
+       splitbuf = NULL;
+       splitbuf_wr_ptr = NULL;
+       return;
+    }
+
+#ifdef HAVE_MMAP
+#ifdef HAVE_SYS_MMAN_H
+    if (strcmp(split_diskbuffer, "NULL")) {
+       void *nulls = NULL;
+       char *quoted;
+       off_t c;
+
+       splitbuffer_path = vstralloc(split_diskbuffer,
+                                    "/splitdump_buffer",
+                                    NULL);
+       /* different file, munmap the previous */
+       if (mmap_filename && strcmp(mmap_filename, splitbuffer_path) != 0) {
+           dbprintf(("create_split_buffer: new file %s\n", splitbuffer_path));
+           munmap(splitbuf, (size_t)mmap_splitsize);
+           aclose(mmap_splitbuffer_fd);
+           mmap_splitbuf = NULL;
+           amfree(mmap_filename);
+           mmap_splitsize = 0;
+       }
+       if (!mmap_filename) {
+           dbprintf(("create_split_buffer: open file %s\n",
+                     splitbuffer_path));
+           mmap_splitbuffer_fd = open(splitbuffer_path, O_RDWR|O_CREAT, 0600);
+           if (mmap_splitbuffer_fd == -1) {
+               buff_err = newvstralloc(buff_err, "open of ", 
+                                       splitbuffer_path, "failed (",
+                                       strerror(errno), ")", NULL);
+               goto fallback;
+           }
+       }
+       offset = lseek(mmap_splitbuffer_fd, (off_t)0, SEEK_END) / 1024;
+       if (offset < splitsize) { /* Increase file size */
+           dbprintf(("create_split_buffer: increase file size of %s to "
+                     OFF_T_FMT "kb\n",
+                     splitbuffer_path, (OFF_T_FMT_TYPE)splitsize));
+           if (mmap_filename) {
+               dbprintf(("create_split_buffer: munmap old file %s\n",
+                         mmap_filename));
+               munmap(splitbuf, (size_t)mmap_splitsize);
+               mmap_splitsize = 0;
+               mmap_splitbuf = NULL;
+           }
+           nulls = alloc(1024); /* lame */
+           memset(nulls, 0, 1024);
+           for (c = offset; c < splitsize ; c += (off_t)1) {
+               if (fullwrite(mmap_splitbuffer_fd, nulls, 1024) < 1024) {
+                   buff_err = newvstralloc(buff_err, "write to ",
+                                           splitbuffer_path,
+                                           "failed (", strerror(errno),
+                                           ")", NULL);
+                   c -= 1;
+                   if (c <= (off_t)fallback_splitsize) {
+                       goto fallback;
+                   }
+                   splitsize = c;
+                   break;
+               }
+           }
+       }
+       amfree(nulls);
+
+       if (mmap_splitsize < splitsize*1024) {
+           mmap_splitsize = splitsize*1024;
+           mmap_filename = stralloc(splitbuffer_path);
+           dbprintf(("create_split_buffer: mmap file %s for " OFF_T_FMT "kb\n",
+                         mmap_filename,(OFF_T_FMT_TYPE)splitsize));
+            mmap_splitbuf = mmap(NULL, (size_t)mmap_splitsize,
+                                PROT_READ|PROT_WRITE,
+                                MAP_SHARED, mmap_splitbuffer_fd, (off_t)0);
+           if (mmap_splitbuf == (char*)-1) {
+               buff_err = newvstralloc(buff_err, "mmap failed (",
+                                       strerror(errno), ")", NULL);
+               aclose(mmap_splitbuffer_fd);
+               amfree(mmap_filename);
+               mmap_splitsize = 0;
+               mmap_splitbuf = NULL;
+               goto fallback;
+           }
+       }
+       quoted = quote_string(splitbuffer_path);
+       fprintf(stderr,
+               "taper: r: buffering " OFF_T_FMT
+               "kb split chunks in mmapped file %s\n",
+               (OFF_T_FMT_TYPE)splitsize, quoted);
+       dbprintf(("taper: r: buffering " OFF_T_FMT
+               "kb split chunks in mmapped file %s\n",
+               (OFF_T_FMT_TYPE)splitsize, quoted));
+       amfree(splitbuffer_path);
+       amfree(quoted);
+       amfree(buff_err);
+       splitbuf = mmap_splitbuf;
+       splitbuf_wr_ptr = splitbuf;
+       return;
+    } else {
+       buff_err = stralloc("no split_diskbuffer specified");
+    }
+#else
+    (void)split_diskbuffer;    /* Quite unused parameter warning */
+    buff_err = stralloc("mman.h not available");
+    goto fallback;
+#endif
+#else
+    (void)split_diskbuffer;    /* Quite unused parameter warning */
+    buff_err = stralloc("mmap not available");
+    goto fallback;
+#endif
+
+    /*
+      Buffer split dumps in memory, if we can't use a file.
+    */
+    fallback:
+       amfree(splitbuffer_path);
+        splitsize = (off_t)fallback_splitsize;
+       dbprintf(("create_split_buffer: fallback size " OFF_T_FMT "\n",
+                 (OFF_T_FMT_TYPE)splitsize));
+       log_add(L_INFO,
+               "%s: using fallback split size of %dkb to buffer %s in-memory",
+               buff_err, splitsize, id_string);
+       amfree(buff_err);
+       if (splitsize > mem_splitsize) {
+           amfree(mem_splitbuf);
+           mem_splitbuf = alloc(fallback_splitsize * 1024);
+           mem_splitsize = fallback_splitsize;
+           dbprintf(("create_split_buffer: alloc buffer size " OFF_T_FMT "\n",
+                         (OFF_T_FMT_TYPE)splitsize *1024));
+       }
+       splitbuf = mem_splitbuf;
+       splitbuf_wr_ptr = splitbuf;
+}
+
+/*
+ * Free up resources that create_split_buffer eats.
+ */
+void
+free_split_buffer(void)
+{
+    if (mmap_splitbuffer_fd != -1) {
+#ifdef HAVE_MMAP
+#ifdef HAVE_SYS_MMAN_H
+       if (splitbuf != NULL)
+           munmap(splitbuf, (size_t)mmap_splitsize);
+#endif
+#endif
+       aclose(mmap_splitbuffer_fd);
+       amfree(mmap_filename);
+       mmap_splitsize = 0;
+    }
+    if (mem_splitbuf) {
+       amfree(splitbuf);
+       mem_splitsize = 0;
+    }
+}
+
+void
+put_syncpipe_fault_result(
+    char *     handle)
+{
+    char *q;
+
+    if (handle == NULL)
+       handle = "<nohandle>";
+
+    q = squotef("[Taper syncpipe fault]");
+    putresult(TAPE_ERROR, "%s %s\n", handle, q);
+    log_add(L_ERROR, "tape-error %s %s", handle, q);
+    amfree(q);
+}
+
+void
+file_reader_side(
+    int rdpipe,
+    int wrpipe)
 {
     cmd_t cmd;
     struct cmdargs cmdargs;
     char *handle = NULL;
     char *filename = NULL;
+    char *qfilename = NULL;
     char *hostname = NULL;
     char *diskname = NULL;
+    char *qdiskname = NULL;
     char *result = NULL;
     char *datestamp = NULL;
-    char tok;
+    char *split_diskbuffer = NULL;
+    char *id_string = NULL;
+    int tok;
     char *q = NULL;
-    int level, fd, data_port, data_socket, wpid;
+    int level, fd;
+    in_port_t data_port;
+    int data_socket;
+    pid_t wpid;
+    char level_str[64];
     struct stat stat_file;
     int tape_started;
     int a;
+    size_t fallback_splitsize = 0;
+    int tmpint;
+    char *c, *c1;
 
     procname = "reader";
     syncpipe_init(rdpipe, wrpipe);
@@ -385,48 +697,92 @@ int rdpipe, wrpipe;
     cmd = getcmd(&cmdargs);
     total_wait = stopclock();
 
-    if(cmd != START_TAPER || cmdargs.argc != 2) {
+    if (cmd != START_TAPER || cmdargs.argc != 2) {
        error("error [file_reader_side cmd %d argc %d]", cmd, cmdargs.argc);
+       /*NOTREACHED*/
     }
 
     /* pass start command on to tape writer */
 
-    taper_datestamp = newstralloc(taper_datestamp, cmdargs.argv[2]);
+    taper_timestamp = newstralloc(taper_timestamp, cmdargs.argv[2]);
 
     tape_started = 0;
-    syncpipe_put('S');
-    syncpipe_putstr(taper_datestamp);
+    if (syncpipe_put('S', 0) == -1) {
+       put_syncpipe_fault_result(NULL);
+    }
+
+    if (syncpipe_putstr(taper_timestamp) == -1) {
+       put_syncpipe_fault_result(NULL);
+    }
 
     /* get result of start command */
 
-    tok = syncpipe_get();
+    tok = syncpipe_get(&tmpint);
     switch(tok) {
+    case -1:
+       put_syncpipe_fault_result(NULL);
+       break;
+
     case 'S':
        putresult(TAPER_OK, "\n");
        tape_started = 1;
        /* start is logged in writer */
        break;
+
     case 'E':
        /* no tape, bail out */
-       result = syncpipe_getstr();
-       q = squotef("[%s]", result ? result : "(null)");
-       putresult(TAPE_ERROR, "%s\n", q);
-       amfree(q);
-       log_add(L_ERROR,"no-tape [%s]", result);
-       amfree(result);
-       syncpipe_put('e');                      /* ACK error */
+       if ((result = syncpipe_getstr()) == NULL) {
+           put_syncpipe_fault_result(NULL);
+       } else {
+           q = squotef("[%s]", result);
+           putresult(TAPE_ERROR, "<nohandle> %s\n", q);
+           amfree(q);
+           log_add(L_ERROR,"no-tape [%s]", "No writable valid tape found");
+           c = c1 = result;
+           while (*c != '\0') {
+               if (*c == '\n') {
+                   *c = '\0';
+                   log_add(L_WARNING,"%s", c1);
+                   c1 = c+1;
+               }
+               c++;
+           }
+           if (strlen(c1) > 1 )
+               log_add(L_WARNING,"%s", c1);
+           amfree(result);
+           (void)syncpipe_put('e', 0);                 /* ACK error */
+       }
        break;
+
+    case 'H': /* Syncpipe I/O error */
+       /* No ACK syncpipe is down just exit */
+        put_syncpipe_fault_result(handle);
+       break;
+
+    case 'X':
+       /*
+        * Pipe read error: Communications is severed at least
+        * back to us.  We send a blind 'Q' (quit) and we don't
+        * wait for a response...
+        */
+       syncpipe_put('Q', 0);                   /* ACK error */
+       error("error [communications pipe from writer severed]");
+       /*NOTREACHED*/
+
     default:
-       error("expected 'S' or 'E' for START-TAPER, got '%c'", tok);
+       q = squotef("[syncpipe sequence fault: Expected 'S' or 'E']");
+       putresult(TAPE_ERROR, "<nohandle> %s\n", q);
+       log_add(L_ERROR, "no-tape %s]", q);
+       amfree(q);
     }
 
-    /* process further commands */
-
-    while(1) {
+    /* process further driver commands */
+    while (1) {
        startclock();
        cmd = getcmd(&cmdargs);
-       if(cmd != QUIT && !tape_started) {
+       if (cmd != QUIT && !tape_started) {
            error("error [file_reader_side cmd %d without tape ready]", cmd);
+           /*NOTREACHED*/
        }
        total_wait = timesadd(total_wait, stopclock());
 
@@ -440,51 +796,93 @@ int rdpipe, wrpipe;
             *   diskname
             *   level
             *   datestamp
+            *   splitsize
+            *   split_diskbuffer
             */
+           mode = MODE_PORT_WRITE;
            cmdargs.argc++;                     /* true count of args */
            a = 2;
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper PORT-WRITE: not enough args: handle]");
+               /*NOTREACHED*/
            }
            handle = newstralloc(handle, cmdargs.argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper PORT-WRITE: not enough args: hostname]");
+               /*NOTREACHED*/
            }
            hostname = newstralloc(hostname, cmdargs.argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper 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) {
+           if (a >= cmdargs.argc) {
                error("error [taper PORT-WRITE: not enough args: diskname]");
+               /*NOTREACHED*/
            }
-           diskname = newstralloc(diskname, cmdargs.argv[a++]);
+           qdiskname = newstralloc(qdiskname, cmdargs.argv[a++]);
+           if (diskname != NULL)
+               amfree(diskname);
+           diskname = unquote_string(qdiskname);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper PORT-WRITE: not enough args: level]");
+               /*NOTREACHED*/
            }
            level = atoi(cmdargs.argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper PORT-WRITE: not enough args: datestamp]");
+               /*NOTREACHED*/
            }
            datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
 
-           if(a != cmdargs.argc) {
+           if (a >= cmdargs.argc) {
+               error("error [taper PORT-WRITE: not enough args: splitsize]");
+               /*NOTREACHED*/
+           }
+           splitsize = OFF_T_ATOI(cmdargs.argv[a++]);
+
+           if (a >= cmdargs.argc) {
+               error("error [taper PORT-WRITE: not enough args: split_diskbuffer]");
+               /*NOTREACHED*/
+           }
+           split_diskbuffer = newstralloc(split_diskbuffer, cmdargs.argv[a++]);
+
+           if (a >= cmdargs.argc) {
+               error("error [taper PORT-WRITE: not enough args: fallback_splitsize]");
+               /*NOTREACHED*/
+           }
+           /* Must fit in memory... */
+           fallback_splitsize = (size_t)atoi(cmdargs.argv[a++]);
+
+           if (a != cmdargs.argc) {
                error("error [taper file_reader_side PORT-WRITE: too many args: %d != %d]",
                      cmdargs.argc, a);
+               /*NOTREACHED*/
+           }
+
+           if (fallback_splitsize < 128 ||
+               fallback_splitsize > 64 * 1024 * 1024) {
+               error("error [bad value for fallback_splitsize]");
+               /*NOTREACHED*/
            }
+           snprintf(level_str, SIZEOF(level_str), "%d", level);
+           id_string = newvstralloc(id_string, hostname, ":", qdiskname, ".",
+                                    level_str, NULL);
+
+           create_split_buffer(split_diskbuffer, fallback_splitsize, id_string);
+           amfree(id_string);
 
            data_port = 0;
-           data_socket = stream_server(&data_port,
-                                       -1,
-                                       STREAM_BUFSIZE);        
-           if(data_socket < 0) {
+           data_socket = stream_server(&data_port, 0, STREAM_BUFSIZE, 0);      
+           if (data_socket < 0) {
                char *m;
 
                m = vstralloc("[port create failure: ",
@@ -494,18 +892,24 @@ int rdpipe, wrpipe;
                q = squote(m);
                putresult(TAPE_ERROR, "%s %s\n", handle, q);
                amfree(m);
+               amfree(q);
                break;
            }
            putresult(PORT, "%d\n", data_port);
 
-           if((fd = stream_accept(data_socket, CONNECT_TIMEOUT,
-                                  -1, NETWORK_BLOCK_BYTES)) == -1) {
+           if ((fd = stream_accept(data_socket, CONNECT_TIMEOUT,
+                                  0, STREAM_BUFSIZE)) == -1) {
                q = squote("[port connect timeout]");
                putresult(TAPE_ERROR, "%s %s\n", handle, q);
                aclose(data_socket);
+               amfree(q);
                break;
            }
-           read_file(fd, handle, hostname, diskname, datestamp, level, 1);
+           expected_splits = -1;
+
+           while(read_file(fd, handle, hostname, qdiskname, datestamp, level))
+               (void)fd;  /* Quiet lint */
+
            aclose(data_socket);
            break;
 
@@ -519,62 +923,105 @@ int rdpipe, wrpipe;
             *   diskname
             *   level
             *   datestamp
+            *   splitsize
             */
+           mode = MODE_FILE_WRITE;
            cmdargs.argc++;                     /* true count of args */
            a = 2;
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper FILE-WRITE: not enough args: handle]");
+               /*NOTREACHED*/
            }
            handle = newstralloc(handle, cmdargs.argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper FILE-WRITE: not enough args: filename]");
+               /*NOTREACHED*/
            }
-           filename = newstralloc(filename, cmdargs.argv[a++]);
+           qfilename = newstralloc(qfilename, cmdargs.argv[a++]);
+           if (filename != NULL)
+               amfree(filename);
+           filename = unquote_string(qfilename);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper FILE-WRITE: not enough args: hostname]");
+               /*NOTREACHED*/
            }
            hostname = newstralloc(hostname, cmdargs.argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper FILE-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) {
+           if (a >= cmdargs.argc) {
                error("error [taper FILE-WRITE: not enough args: diskname]");
+               /*NOTREACHED*/
            }
-           diskname = newstralloc(diskname, cmdargs.argv[a++]);
+           qdiskname = newstralloc(qdiskname, cmdargs.argv[a++]);
+           if (diskname != NULL)
+               amfree(diskname);
+           diskname = unquote_string(qdiskname);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper FILE-WRITE: not enough args: level]");
+               /*NOTREACHED*/
            }
            level = atoi(cmdargs.argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if (a >= cmdargs.argc) {
                error("error [taper FILE-WRITE: not enough args: datestamp]");
+               /*NOTREACHED*/
            }
            datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
 
-           if(a != cmdargs.argc) {
+           if (a >= cmdargs.argc) {
+               error("error [taper FILE-WRITE: not enough args: splitsize]");
+               /*NOTREACHED*/
+           }
+           splitsize = OFF_T_ATOI(cmdargs.argv[a++]);
+
+           if (a != cmdargs.argc) {
                error("error [taper file_reader_side FILE-WRITE: too many args: %d != %d]",
                      cmdargs.argc, a);
+               /*NOTREACHED*/
+           }
+           if (holdfile_name != NULL) {
+               filename = newstralloc(filename, holdfile_name);
            }
 
-           if(stat(filename,&stat_file)!=0) {
+           if ((expected_splits = predict_splits(filename)) < 0) {
+               break;
+           }
+           if (stat(filename, &stat_file)!=0) {
                q = squotef("[%s]", strerror(errno));
                putresult(TAPE_ERROR, "%s %s\n", handle, q);
+               amfree(q);
                break;
            }
-           if((fd = open(filename, O_RDONLY)) == -1) {
+           if ((fd = open(filename, O_RDONLY)) == -1) {
                q = squotef("[%s]", strerror(errno));
                putresult(TAPE_ERROR, "%s %s\n", handle, q);
+               amfree(q);
                break;
            }
-           read_file(fd, handle, hostname, diskname, datestamp, level, 0);
+           holdfile_path = stralloc(filename);
+           holdfile_path_thischunk = stralloc(filename);
+           holdfile_offset_thischunk = (off_t)0;
+
+           while (read_file(fd,handle,hostname,qdiskname,datestamp,level)) {
+               if (splitsize > (off_t)0 && holdfile_path_thischunk)
+                   filename = newstralloc(filename, holdfile_path_thischunk);
+               if ((fd = open(filename, O_RDONLY)) == -1) {
+                   q = squotef("[%s]", strerror(errno));
+                   putresult(TAPE_ERROR, "%s %s\n", handle, q);
+                   amfree(q);
+                   break;
+               }
+           }
            break;
 
        case QUIT:
@@ -582,82 +1029,100 @@ int rdpipe, wrpipe;
            fprintf(stderr,"taper: DONE [idle wait: %s secs]\n",
                    walltime_str(total_wait));
            fflush(stderr);
-           syncpipe_put('Q');  /* tell writer we're exiting gracefully */
+           (void)syncpipe_put('Q', 0); /* tell writer we're exiting gracefully */
            aclose(wrpipe);
 
-           if((wpid = wait(NULL)) != writerpid) {
+           if ((wpid = wait(NULL)) != writerpid) {
+               dbprintf(("taper: writer wait returned %u instead of %u: %s\n",
+                       (unsigned)wpid, (unsigned)writerpid, strerror(errno)));
                fprintf(stderr,
-                       "taper: writer wait returned %d instead of %d: %s\n",
-                       wpid, writerpid, strerror(errno));
+                       "taper: writer wait returned %u instead of %u: %s\n",
+                       (unsigned)wpid, (unsigned)writerpid, strerror(errno));
                fflush(stderr);
            }
 
-           detach_buffers(buffers);
-           destroy_buffers();
+           free_split_buffer();
            amfree(datestamp);
+           clear_tapelist();
+           free_server_config();
+           amfree(taper_timestamp);
            amfree(label);
            amfree(errstr);
            amfree(changer_resultstr);
            amfree(tapedev);
+           amfree(filename);
+           amfree(conf_tapelist);
            amfree(config_dir);
            amfree(config_name);
+           amfree(holdfile_name);
 
            malloc_size_2 = malloc_inuse(&malloc_hist_2);
 
-           if(malloc_size_1 != malloc_size_2) {
+           if (malloc_size_1 != malloc_size_2) {
                malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
            }
-
            exit(0);
+           /*NOTREACHED*/
 
        default:
-           if(cmdargs.argc >= 1) {
+           if (cmdargs.argc >= 1) {
                q = squote(cmdargs.argv[1]);
-           } else if(cmdargs.argc >= 0) {
+           } else if (cmdargs.argc >= 0) {
                q = squote(cmdargs.argv[0]);
            } else {
                q = stralloc("(no input?)");
            }
            putresult(BAD_COMMAND, "%s\n", q);
+           amfree(q);
            break;
        }
     }
-    amfree(q);
-    amfree(handle);
-    amfree(filename);
-    amfree(hostname);
-    amfree(diskname);
+    /* NOTREACHED */
 }
 
-void dumpbufs(str1)
-char *str1;
+void
+dumpbufs(
+    char *str1)
 {
     int i,j;
     long v;
 
     fprintf(stderr, "%s: state", str1);
-    for(i = j = 0; i < conf_tapebufs; i = j+1) {
+    for (i = j = 0; i < conf_tapebufs; i = j+1) {
        v = buftable[i].status;
-       for(j = i; j < conf_tapebufs && buftable[j].status == v; j++);
+       for(j = i; j < conf_tapebufs && buftable[j].status == v; j++)
+           (void)j; /* Quiet lint */
        j--;
-       if(i == j) fprintf(stderr, " %d:", i);
-       else fprintf(stderr, " %d-%d:", i, j);
+       if (i == j) {
+           fprintf(stderr, " %d:", i);
+       } else {
+           fprintf(stderr, " %d-%d:", i, j);
+       }
        switch(v) {
-       case FULL:      fputc('F', stderr); break;
-       case FILLING:   fputc('f', stderr); break;
-       case EMPTY:     fputc('E', stderr); break;
+       case FULL:
+           fputc('F', stderr);
+           break;
+
+       case FILLING:
+           fputc('f', stderr);
+           break;
+
+       case EMPTY:
+           fputc('E', stderr);
+           break;
+
        default:
            fprintf(stderr, "%ld", v);
            break;
        }
-
     }
     fputc('\n', stderr);
     fflush(stderr);
 }
 
-void dumpstatus(bp)
-buffer_t *bp;
+void
+dumpstatus(
+    buffer_t *bp)
 {
     char pn[2];
     char bt[NUM_STR_SIZE];
@@ -666,15 +1131,24 @@ buffer_t *bp;
 
     pn[0] = procname[0];
     pn[1] = '\0';
-    ap_snprintf(bt, sizeof(bt), "%d", (int)(bp-buftable));
+    snprintf(bt, SIZEOF(bt), "%d", (int)(bp-buftable));
 
     switch(bp->status) {
-    case FULL:         ap_snprintf(status, sizeof(status), "F%d", bp->size);
-                       break;
-    case FILLING:      status[0] = 'f'; status[1] = '\0'; break;
-    case EMPTY:                status[0] = 'E'; status[1] = '\0'; break;
+    case FULL:
+       snprintf(status, SIZEOF(status), "F" SIZE_T_FMT,
+               (SIZE_T_FMT_TYPE)bp->size);
+       break;
+
+    case FILLING:
+       snprintf(status, SIZEOF(status), "f");
+       break;
+
+    case EMPTY:
+       snprintf(status, SIZEOF(status), "E");
+       break;
+
     default:
-       ap_snprintf(status, sizeof(status), "%ld", bp->status);
+       snprintf(status, SIZEOF(status), "%ld", bp->status);
        break;
     }
 
@@ -683,22 +1157,133 @@ buffer_t *bp;
     amfree(str);
 }
 
+/*
+  Handle moving to the next chunk of holding file, if any.  Returns -1 for
+  errors, 0 if there's no more file, or a positive integer for the amount of
+  stuff read that'll go into 'rc' (XXX That's fugly, maybe that should just
+  be another global.  What is rc anyway, 'read count?' I keep thinking it
+  should be 'return code')
+*/
+ssize_t
+get_next_holding_file(
+    int fd,
+    buffer_t *bp,
+    char **strclosing,
+    size_t rc)
+{
+    int save_fd;
+    ssize_t rc1;
+    struct stat stat_file;
+    ssize_t ret = -1;
+    
+    save_fd = fd;
+    close(fd);
+    
+    /* see if we're fresh out of file */
+    if (file.cont_filename[0] == '\0') {
+       err = 0;
+       ret = 0;
+    } else if (stat(file.cont_filename, &stat_file) != 0) {
+       err = errno;
+       ret = -1;
+       *strclosing = newvstralloc(*strclosing, "can't stat: ",
+                                  file.cont_filename, NULL);
+    } else if ((fd = open(file.cont_filename,O_RDONLY)) == -1) {
+       err = errno;
+       ret = -1;
+       *strclosing = newvstralloc(*strclosing, "can't open: ",
+                                  file.cont_filename, NULL);
+    } else if ((fd != save_fd) && dup2(fd, save_fd) == -1) {
+       err = errno;
+       ret = -1;
+       *strclosing = newvstralloc(*strclosing, "can't dup2: ",
+                                  file.cont_filename, NULL);
+    } else {
+       buffer_t bp1;
+       char *quoted;
+
+       holdfile_path = stralloc(file.cont_filename);
+       quoted = quote_string(holdfile_path);
+       fprintf(stderr, "taper: r: switching to next holding chunk '%s'\n",
+               quoted); 
+       amfree(quoted);
+       num_holdfile_chunks++;
+       
+       bp1.status = EMPTY;
+       bp1.size = DISK_BLOCK_BYTES;
+       bp1.buffer = alloc(DISK_BLOCK_BYTES);
+       
+       if (fd != save_fd) {
+           close(fd);
+           fd = save_fd;
+       }
+       
+       rc1 = taper_fill_buffer(fd, &bp1, DISK_BLOCK_BYTES);
+       if (rc1 <= 0) {
+           amfree(bp1.buffer);
+           err = (rc1 < 0) ? errno : 0;
+           ret = -1;
+           *strclosing = newvstralloc(*strclosing,
+                                      "Can't read header: ",
+                                      file.cont_filename,
+                                      NULL);
+       } else {
+           parse_file_header(bp1.buffer, &file, (size_t)rc1);
+           
+           amfree(bp1.buffer);
+           bp1.buffer = bp->buffer + rc;
+           
+           rc1 = taper_fill_buffer(fd, &bp1, (size_t)tt_blocksize - rc);
+           if (rc1 <= 0) {
+               err = (rc1 < 0) ? errno : 0;
+               ret = -1;
+               if (rc1 < 0) {
+                   *strclosing = newvstralloc(*strclosing,
+                                              "Can't read data: ",
+                                              file.cont_filename,
+                                              NULL);
+               }
+           } else {
+               ret = rc1;
+               num_holdfiles++;
+           }
+       }
+    }
 
-void read_file(fd, handle, hostname, diskname, datestamp, level, port_flag)
-    int fd, level, port_flag;
-    char *handle, *hostname, *diskname, *datestamp;
+    return(ret);
+}
+
+
+int
+read_file(
+    int                fd,
+    char *     handle,
+    char *     hostname,
+    char *     qdiskname,
+    char *     datestamp,
+    int                level)
 {
     buffer_t *bp;
-    char tok;
-    int rc, err, opening, closing, bufnum, need_closing;
-    long filesize;
+    int tok;
+    ssize_t rc;
+#ifdef ASSERTIONS
+    int opening;
+#endif
+    int closing, bufnum, need_closing, nexting;
+    off_t filesize;
     times_t runtime;
     char *strclosing = NULL;
+    char seekerrstr[STR_SIZE];
     char *str;
+    int header_written = 0;
+    size_t buflen;
+    dumpfile_t first_file;
+    dumpfile_t cur_holdfile;
+    off_t kbytesread = (off_t)0;
     int header_read = 0;
-    int buflen;
-    dumpfile_t file;
-
+    char *cur_filename = NULL;
+    int retry_from_splitbuf = 0;
+    char *splitbuf_rd_ptr = NULL;
     char *q = NULL;
 
 #ifdef HAVE_LIBVTBLC
@@ -709,75 +1294,168 @@ void read_file(fd, handle, hostname, diskname, datestamp, level, port_flag)
 
 
     /* initialize */
+    memset(&first_file, 0, SIZEOF(first_file));
+    memset(&cur_holdfile, 0, SIZEOF(cur_holdfile));
 
-    filesize = 0;
+    filesize = (off_t)0;
     closing = 0;
     need_closing = 0;
+    nexting = 0;
     err = 0;
-    fh_init(&file);
 
-    if(bufdebug) {
+    /* don't break this if we're still on the same file as a previous init */
+    if (cur_span_chunkstart <= (off_t)0) {
+       fh_init(&file);
+       header_read = 0;
+    } else if(mode == MODE_FILE_WRITE){
+       memcpy(&file, save_holdfile, SIZEOF(dumpfile_t));
+       memcpy(&cur_holdfile, save_holdfile, SIZEOF(dumpfile_t));
+    }
+
+    if (bufdebug) {
        fprintf(stderr, "taper: r: start file\n");
        fflush(stderr);
     }
 
-    for(bp = buftable; bp < buftable + conf_tapebufs; bp++) {
+    for (bp = buftable; bp < buftable + conf_tapebufs; bp++) {
        bp->status = EMPTY;
     }
 
     bp = buftable;
-    if(interactive || bufdebug) dumpstatus(bp);
+    if (interactive || bufdebug)
+       dumpstatus(bp);
+
+    if ((cur_span_chunkstart >= (off_t)0) && (splitsize > (off_t)0)) {
+        /* We're supposed to start at some later part of the file, not read the
+          whole thing. "Seek" forward to where we want to be. */
+       if (label)
+           putresult(SPLIT_CONTINUE, "%s %s\n", handle, label);
+        if ((mode == MODE_FILE_WRITE) && (cur_span_chunkstart > (off_t)0)) {
+           char *quoted = quote_string(holdfile_path_thischunk);
+           fprintf(stderr, "taper: r: seeking %s to " OFF_T_FMT " kb\n",
+                           quoted,
+                           (OFF_T_FMT_TYPE)holdfile_offset_thischunk);
+           fflush(stderr);
+
+           if (holdfile_offset_thischunk > maxseek) {
+               snprintf(seekerrstr, SIZEOF(seekerrstr), "Can't seek by "
+                       OFF_T_FMT " kb (compiled for %d-bit file offsets), "
+                       "recompile with large file support or "
+                       "set holdingdisk chunksize to <" OFF_T_FMT " Mb",
+                       (OFF_T_FMT_TYPE)holdfile_offset_thischunk,
+                       (int)(sizeof(off_t) * 8),
+                       (OFF_T_FMT_TYPE)(maxseek/(off_t)1024));
+               log_add(L_ERROR, "%s", seekerrstr);
+               fprintf(stderr, "taper: r: FATAL: %s\n", seekerrstr);
+               fflush(stderr);
+               if (syncpipe_put('X', 0) == -1) {
+                       put_syncpipe_fault_result(handle);
+               }
+               amfree(quoted);
+               return -1;
+           }
+           if (lseek(fd, holdfile_offset_thischunk*(off_t)1024, SEEK_SET) == (off_t)-1) {
+               fprintf(stderr, "taper: r: FATAL: seek_holdfile lseek error "
+                       "while seeking into %s by "
+                       OFF_T_FMT "kb: %s\n", quoted,
+                       (OFF_T_FMT_TYPE)holdfile_offset_thischunk,
+                       strerror(errno));
+               fflush(stderr);
+               if (syncpipe_put('X', 0) == -1) {
+                       put_syncpipe_fault_result(handle);
+               }
+               amfree(quoted);
+               return -1;
+           }
+           amfree(quoted);
+        } else if (mode == MODE_PORT_WRITE) {
+           fprintf(stderr, "taper: r: re-reading split dump piece from buffer\n");
+           fflush(stderr);
+           retry_from_splitbuf = 1;
+           splitbuf_rd_ptr = splitbuf;
+           if (splitbuf_rd_ptr >= splitbuf_wr_ptr)
+               retry_from_splitbuf = 0;
+        }
+        if (cur_span_chunkstart > (off_t)0)
+           header_read = 1; /* really initialized in prior run */
+    }
 
     /* tell writer to open tape */
 
+#ifdef ASSERTIONS
     opening = 1;
-    syncpipe_put('O');
-    syncpipe_putstr(datestamp);
-    syncpipe_putstr(hostname);
-    syncpipe_putstr(diskname);
-    syncpipe_putint(level);
+#endif
 
-    startclock();
+    if (syncpipe_put('O', 0) == -1) {
+       put_syncpipe_fault_result(handle);
+       return -1;
+    }
+    if (syncpipe_putstr(datestamp) == -1) {
+       put_syncpipe_fault_result(handle);
+       return -1;
+    }
+    if (syncpipe_putstr(hostname) == -1) {
+       put_syncpipe_fault_result(handle);
+       return -1;
+    }
+    if (syncpipe_putstr(qdiskname) == -1) {
+       put_syncpipe_fault_result(handle);
+       return -1;
+    }
+    if (syncpipe_putint(level) == -1) {
+       put_syncpipe_fault_result(handle);
+       return -1;
+    }
 
+    startclock();
+    
     /* read file in loop */
+    
+    while (1) {
+       if ((tok = syncpipe_get(&bufnum)) == -1) {
+           put_syncpipe_fault_result(handle);
+           return -1;
+       }
 
-    while(1) {
-       tok = syncpipe_get();
        switch(tok) {
-
        case 'O':
+#ifdef ASSERTIONS
            assert(opening);
            opening = 0;
+#endif
            err = 0;
            break;
-
+           
        case 'R':
-           bufnum = syncpipe_getint();
-
-           if(bufdebug) {
+           if (bufdebug) {
                fprintf(stderr, "taper: r: got R%d\n", bufnum);
                fflush(stderr);
            }
-
-           if(need_closing) {
-               syncpipe_put('C');
+           
+           if (need_closing) {
+               if (syncpipe_put('C', 0) == -1) {
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
                closing = 1;
                need_closing = 0;
                break;
            }
-
-           if(closing) break;  /* ignore extra read tokens */
-
+           
+           if (closing)
+               break;  /* ignore extra read tokens */
+           
+#ifdef ASSERTIONS
            assert(!opening);
-           if(bp->status != EMPTY || bufnum != bp-buftable) {
+#endif
+           if(bp->status != EMPTY || bufnum != (int)(bp - buftable)) {
                /* XXX this SHOULD NOT HAPPEN.  Famous last words. */
-               fprintf(stderr,"taper: panic: buffer mismatch at ofs %ld:\n",
-                       filesize);
-               if(bufnum != bp-buftable) {
+               fprintf(stderr,"taper: panic: buffer mismatch at ofs "
+                       OFF_T_FMT ":\n", (OFF_T_FMT_TYPE)filesize);
+               if(bufnum != (int)(bp - buftable)) {
                    fprintf(stderr, "    my buf %d but writer buf %d\n",
                            (int)(bp-buftable), bufnum);
-               }
-               else {
+               } else {
                    fprintf(stderr,"buf %d state %s (%ld) instead of EMPTY\n",
                            (int)(bp-buftable),
                            bp->status == FILLING? "FILLING" :
@@ -787,224 +1465,393 @@ void read_file(fd, handle, hostname, diskname, datestamp, level, port_flag)
                dumpbufs("taper");
                sleep(1);
                dumpbufs("taper: after 1 sec");
-               if(bp->status == EMPTY)
+               if (bp->status == EMPTY)
                    fprintf(stderr, "taper: result now correct!\n");
                fflush(stderr);
-
+               
                errstr = newstralloc(errstr,
                                     "[fatal buffer mismanagement bug]");
                q = squote(errstr);
                putresult(TRYAGAIN, "%s %s\n", handle, q);
+               cur_span_chunkstart = (off_t)0;
                amfree(q);
                log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
-                       hostname, diskname, level, errstr);
+                       hostname, qdiskname, level, errstr);
                closing = 1;
-               syncpipe_put('X');      /* X == buffer snafu, bail */
+               if (syncpipe_put('X', 0) == -1) {/* X == buffer snafu, bail */
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
                do {
-                   tok = syncpipe_get();
-                   if(tok == 'R')
-                       bufnum = syncpipe_getint();
-               } while(tok != 'x');
+                   if ((tok = syncpipe_get(&bufnum)) == -1) {
+                       put_syncpipe_fault_result(handle);
+                       return (-1);
+                   }
+               } while (tok != 'x');
                aclose(fd);
-               return;
-           }
+               return -1;
+           } /* end 'if (bf->status != EMPTY || bufnum != (int)(bp-buftable))' */
 
            bp->status = FILLING;
-           buflen = header_read ? tt_blocksize : DISK_BLOCK_BYTES;
-           if(interactive || bufdebug) dumpstatus(bp);
-           if((rc = taper_fill_buffer(fd, bp, buflen)) < 0) {
-               err = errno;
-               closing = 1;
-               strclosing = newvstralloc(strclosing,"Can't read data: ",NULL);
-               syncpipe_put('C');
-           } else {
-               if(rc < buflen) { /* switch to next file */
-                   int save_fd;
-                   struct stat stat_file;
-
-                   save_fd = fd;
-                   close(fd);
-                   if(file.cont_filename[0] == '\0') { /* no more file */
-                       err = 0;
-                       need_closing = 1;
-                   } else if(stat(file.cont_filename, &stat_file) != 0) {
-                       err = errno;
-                       need_closing = 1;
-                       strclosing = newvstralloc(strclosing,"can't stat: ",file.cont_filename,NULL);
-                   } else if((fd = open(file.cont_filename,O_RDONLY)) == -1) {
-                       err = errno;
-                       need_closing = 1;
-                       strclosing = newvstralloc(strclosing,"can't open: ",file.cont_filename,NULL);
-                   } else if((fd != save_fd) && dup2(fd, save_fd) == -1) {
-                       err = errno;
-                       need_closing = 1;
-                       strclosing = newvstralloc(strclosing,"can't dup2: ",file.cont_filename,NULL);
+           buflen = header_read ? (size_t)tt_blocksize : DISK_BLOCK_BYTES;
+           if (interactive || bufdebug)
+               dumpstatus(bp);
+           if (header_written == 0 &&
+                       (header_read == 1 || cur_span_chunkstart > (off_t)0)) {
+               /* for split dumpfiles, modify headers for the second - nth
+                  pieces that signify that they're continuations of the last
+                  normal one */
+               char *cont_filename;
+               file.type = F_SPLIT_DUMPFILE;
+               file.partnum = num_splits + 1;
+               file.totalparts = expected_splits;
+                cont_filename = stralloc(file.cont_filename);
+               file.cont_filename[0] = '\0';
+               build_header(bp->buffer, &file, tt_blocksize);
+  
+               if (cont_filename[0] != '\0') {
+                 file.type = F_CONT_DUMPFILE;
+                   strncpy(file.cont_filename, cont_filename,
+                           SIZEOF(file.cont_filename));
+                       }
+               memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
+  
+               if (interactive || bufdebug)
+                   dumpstatus(bp);
+               bp->size = (ssize_t)tt_blocksize;
+               rc = (ssize_t)tt_blocksize;
+               header_written = 1;
+               amfree(cont_filename);
+           } else if (retry_from_splitbuf) {
+               /* quietly pull dump data from our in-memory cache, and the
+                  writer side need never know the wiser */
+               memcpy(bp->buffer, splitbuf_rd_ptr, tt_blocksize);
+               bp->size = (ssize_t)tt_blocksize;
+               rc = (ssize_t)tt_blocksize;
+               splitbuf_rd_ptr += tt_blocksize;
+               if (splitbuf_rd_ptr >= splitbuf_wr_ptr)
+                   retry_from_splitbuf = 0;
+           } else if ((rc = taper_fill_buffer(fd, bp, buflen)) < 0) {
+               err = errno;
+               closing = 1;
+               strclosing = newvstralloc(strclosing,"Can't read data: ",
+                                         NULL);
+               if (syncpipe_put('C', 0) == -1) {
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
+           }
+  
+           if (!closing) {
+               if (rc < (ssize_t)buflen) { /* switch to next holding file */
+                   ssize_t ret;
+
+                   if (file.cont_filename[0] != '\0') {
+                       cur_filename = newvstralloc(cur_filename, file.cont_filename, NULL);
+                   }
+                   ret = get_next_holding_file(fd, bp, &strclosing, (size_t)rc);
+                   if (ret <= 0) {
+                       need_closing = 1;
                    } else {
-                       buffer_t bp1;
-                       int rc1;
-
-                       bp1.status = EMPTY;
-                       bp1.size = DISK_BLOCK_BYTES;
-                       bp1.buffer = malloc(DISK_BLOCK_BYTES);
-
-                       if(fd != save_fd) {
-                           close(fd);
-                           fd = save_fd;
-                       }
-
-                       rc1 = taper_fill_buffer(fd, &bp1, DISK_BLOCK_BYTES);
-                       if(rc1 <= 0) {
-                           amfree(bp1.buffer);
-                           err = (rc1 < 0) ? errno : 0;
-                           need_closing = 1;
-                           strclosing = newvstralloc(strclosing,
-                                                     "Can't read header: ",
-                                                     file.cont_filename,
-                                                     NULL);
-                       } else {
-                           parse_file_header(bp1.buffer, &file, rc1);
-
-                           amfree(bp1.buffer);
-                           bp1.buffer = bp->buffer + rc;
-
-                           rc1 = taper_fill_buffer(fd, &bp1, tt_blocksize - rc);
-                           if(rc1 <= 0) {
-                               err = (rc1 < 0) ? errno : 0;
-                               need_closing = 1;
-                               if(rc1 < 0) {
-                                   strclosing = newvstralloc(strclosing,
-                                                             "Can't read data: ",
-                                                             file.cont_filename,
-                                                             NULL);
-                               }
-                           }
-                           else {
-                               rc += rc1;
-                               bp->size = rc;
-                           }
-                       }
-                   }
+                       memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
+                       rc += ret;
+                       bp->size = rc;
+                   }
                }
-               if(rc > 0) {
-                   bp->status = FULL;
-                   if(header_read == 0) {
-                       char *cont_filename;
-
-                       parse_file_header(bp->buffer, &file, rc);
-                       cont_filename = stralloc(file.cont_filename);
-                       file.cont_filename[0] = '\0';
-                       file.blocksize = tt_blocksize;
-                       build_header(bp->buffer, &file, tt_blocksize);
-
-                       /* add CONT_FILENAME back to in-memory header */
-                       strncpy(file.cont_filename, cont_filename, 
-                               sizeof(file.cont_filename));
-                       if(interactive || bufdebug) dumpstatus(bp);
-                       bp->size = tt_blocksize; /* output a full tape block */
-                       header_read = 1;
-                       amfree(cont_filename);
-                   }
-                   else {
-                       filesize += am_round(rc, 1024) / 1024;
-                   }
-                   if(interactive || bufdebug) dumpstatus(bp);
-                   if(bufdebug) {
+               if (rc > 0) {
+                   bp->status = FULL;
+                   /* rebuild the header block, which might have CONT junk */
+                   if (header_read == 0) {
+                       char *cont_filename;
+                       /* write the "real" filename if the holding-file
+                          is a partial one */
+                       parse_file_header(bp->buffer, &file, (size_t)rc);
+                       parse_file_header(bp->buffer, &first_file, (size_t)rc);
+                       cont_filename = stralloc(file.cont_filename);
+                       file.cont_filename[0] = '\0';
+                       if (splitsize > (off_t)0) {
+                           file.type = F_SPLIT_DUMPFILE;
+                           file.partnum = 1;
+                           file.totalparts = expected_splits;
+                       }
+                       file.blocksize = tt_blocksize;
+                       build_header(bp->buffer, &file, tt_blocksize);
+                       kbytesread += (off_t)(tt_blocksize/1024); /* XXX shady */
+                       file.type = F_CONT_DUMPFILE;
+                       /* add CONT_FILENAME back to in-memory header */
+                       strncpy(file.cont_filename, cont_filename, 
+                               SIZEOF(file.cont_filename));
+                       if (interactive || bufdebug)
+                           dumpstatus(bp);
+                       bp->size = (ssize_t)tt_blocksize; /* output a full tape block */
+                       /* save the header, we'll need it if we jump tapes */
+                       memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
+                       header_read = 1;
+                       header_written = 1;
+                       amfree(cont_filename);
+                   } else {
+                       filesize = kbytesread;
+                   }
+
+                   if (bufdebug) {
                        fprintf(stderr,"taper: r: put W%d\n",(int)(bp-buftable));
                        fflush(stderr);
                    }
-                   syncpipe_put('W');
-                   syncpipe_putint(bp-buftable);
+                   if (syncpipe_put('W', (int)(bp-buftable)) == -1) {
+                       put_syncpipe_fault_result(handle);
+                       return (-1);
+                   }
                    bp = nextbuf(bp);
                }
-               if(need_closing && rc <= 0) {
-                   syncpipe_put('C');
+
+               if (((kbytesread + (off_t)(DISK_BLOCK_BYTES/1024)) >= splitsize)
+                       && (splitsize > (off_t)0) && !need_closing) {
+
+                   if (mode == MODE_PORT_WRITE) {
+                       splitbuf_wr_ptr = splitbuf;
+                       splitbuf_rd_ptr = splitbuf;
+                       memset(splitbuf, 0, SIZEOF(splitbuf));
+                       retry_from_splitbuf = 0;
+                   }
+
+                   fprintf(stderr,"taper: r: end %s.%s.%s.%d part %d, "
+                               "splitting chunk that started at "
+                               OFF_T_FMT "kb after " OFF_T_FMT
+                               "kb (next chunk will start at "
+                               OFF_T_FMT "kb)\n",
+                               hostname, qdiskname, datestamp, level,
+                               num_splits+1,
+                               (OFF_T_FMT_TYPE)cur_span_chunkstart,
+                               (OFF_T_FMT_TYPE)kbytesread,
+                               (OFF_T_FMT_TYPE)(cur_span_chunkstart+kbytesread));
+                   fflush(stderr);
+
+                   nexting = 1;
+                   need_closing = 1;
+               } /* end '(kbytesread >= splitsize && splitsize > 0)' */
+               if (need_closing && rc <= 0) {
+                   if (syncpipe_put('C', 0) == -1) {
+                       put_syncpipe_fault_result(handle);
+                       return (-1);
+                   }
                    need_closing = 0;
                    closing = 1;
                }
-           }
+                kbytesread += (off_t)(rc / 1024);
+           } /* end the 'if (!closing)' (successful buffer fill) */
            break;
 
        case 'T':
        case 'E':
-           syncpipe_put('e');  /* ACK error */
+       case 'H':
+           if (syncpipe_put('e', 0) == -1) {   /* ACK error */
+               put_syncpipe_fault_result(handle);
+               return (-1);
+           }
 
-           aclose(fd);
-           str = syncpipe_getstr();
-           errstr = newvstralloc(errstr, "[", str ? str : "(null)", "]", NULL);
+           if ((str = syncpipe_getstr()) == NULL) {
+               put_syncpipe_fault_result(handle);
+               return (-1);
+           }
+           
+           errstr = newvstralloc(errstr, "[", str, "]", NULL);
            amfree(str);
 
            q = squote(errstr);
-           if(tok == 'T') {
-               putresult(TRYAGAIN, "%s %s\n", handle, q);
-               log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
-                       hostname, diskname, level, errstr);
+           if (tok == 'T') {
+               if (splitsize > (off_t)0) {
+                   /* we'll be restarting this chunk on the next tape */
+                   if (mode == MODE_FILE_WRITE) {
+                     aclose(fd);
+                   }
+
+                   putresult(SPLIT_NEEDNEXT, "%s " OFF_T_FMT "\n", handle,
+                               (OFF_T_FMT_TYPE)cur_span_chunkstart);
+                   log_add(L_INFO, "continuing %s:%s.%d on new tape from "
+                               OFF_T_FMT "kb mark: %s",
+                               hostname, qdiskname, level,
+                               (OFF_T_FMT_TYPE)cur_span_chunkstart, errstr);
+                   return 1;
+               } else {
+                   /* restart the entire dump (failure propagates to driver) */
+                   aclose(fd);
+                   putresult(TRYAGAIN, "%s %s\n", handle, q);
+                   cur_span_chunkstart = (off_t)0;
+                   log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
+                           hostname, qdiskname, level, errstr);
+               }
            } else {
+               aclose(fd);
                putresult(TAPE_ERROR, "%s %s\n", handle, q);
                log_add(L_FAIL, "%s %s %s %d [out of tape]",
-                       hostname, diskname, datestamp, level);
-               log_add(L_ERROR,"no-tape [%s]", errstr);
+                       hostname, qdiskname, datestamp, level);
+               log_add(L_ERROR,"no-tape [%s]", "No more writable valid tape found");
            }
            amfree(q);
-           return;
+           return 0;
 
        case 'C':
+#ifdef ASSERTIONS
            assert(!opening);
+#endif
            assert(closing);
 
-           str = syncpipe_getstr();
+           if (nexting) {
+             cur_span_chunkstart += kbytesread; /* XXX possibly wrong */
+             if (cur_filename)
+               holdfile_name = newvstralloc(holdfile_name, cur_filename,
+                                            NULL);
+             else
+               amfree(holdfile_name);
+
+             kbytesread = (off_t)0;
+             amfree(cur_filename);
+           }
+
+           if ((str = syncpipe_getstr()) == NULL) {
+               put_syncpipe_fault_result(handle);
+               return (-1);
+           }
+
            label = newstralloc(label, str ? str : "(null)");
            amfree(str);
-           str = syncpipe_getstr();
+           if ((str = syncpipe_getstr()) == NULL) {
+               put_syncpipe_fault_result(handle);
+               return (-1);
+           }
+
            filenum = atoi(str ? str : "-9876");        /* ??? */
            amfree(str);
            fprintf(stderr, "taper: reader-side: got label %s filenum %d\n",
                    label, filenum);
            fflush(stderr);
 
-           aclose(fd);
+           /* we'll need that file descriptor if we're gonna write more */
+           if (!nexting) {
+               aclose(fd);
+           }
+
            runtime = stopclock();
-           if(err) {
-               if(strclosing) {
+           if (nexting)
+               startclock();
+           if (err) {
+               if (strclosing) {
                    errstr = newvstralloc(errstr,
                                          "[input: ", strclosing, ": ",
                                          strerror(err), "]", NULL);
                    amfree(strclosing);
-               }
-               else
+               } else
                    errstr = newvstralloc(errstr,
                                          "[input: ", strerror(err), "]",
                                          NULL);
                q = squote(errstr);
                putresult(TAPE_ERROR, "%s %s\n", handle, q);
+
                amfree(q);
-               log_add(L_FAIL, "%s %s %s %d %s",
-                       hostname, diskname, datestamp, level, errstr);
-               str = syncpipe_getstr();        /* reap stats */
+               if (splitsize != (off_t)0) {
+                   log_add(L_FAIL, "%s %s %s.%d %d %s", hostname, qdiskname,
+                               datestamp, num_splits, level, errstr);
+               } else {
+                   log_add(L_FAIL, "%s %s %s %d %s",
+                               hostname, qdiskname, datestamp, level, errstr);
+               }
+               if ((str = syncpipe_getstr()) == NULL) {        /* reap stats */
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
                amfree(str);
+                amfree(errstr);
            } else {
                char kb_str[NUM_STR_SIZE];
                char kps_str[NUM_STR_SIZE];
                double rt;
 
-               rt = runtime.r.tv_sec+runtime.r.tv_usec/1000000.0;
-               ap_snprintf(kb_str, sizeof(kb_str), "%ld", filesize);
-               ap_snprintf(kps_str, sizeof(kps_str), "%3.1f",
-                                    rt ? filesize / rt : 0.0);
-               str = syncpipe_getstr();
+               rt = (double)(runtime.r.tv_sec) +
+                    ((double)(runtime.r.tv_usec) / 1000000.0);
+               curdump_rt = timesadd(runtime, curdump_rt);
+               snprintf(kb_str, SIZEOF(kb_str), OFF_T_FMT,
+                       (OFF_T_FMT_TYPE)filesize);
+               snprintf(kps_str, SIZEOF(kps_str), "%3.1lf",
+                                 (isnormal(rt) ? (double)filesize / rt : 0.0));
+               if ((str = syncpipe_getstr()) == NULL) {
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
                errstr = newvstralloc(errstr,
                                      "[sec ", walltime_str(runtime),
                                      " kb ", kb_str,
                                      " kps ", kps_str,
-                                     " ", str ? str : "(null)",
+                                     " ", str,
                                      "]",
                                      NULL);
+               if (splitsize == (off_t)0) { /* Ordinary dump */
+                   q = squote(errstr);
+/*@i@*/                    if (first_file.is_partial) {
+                       putresult(PARTIAL, "%s %s %d %s\n",
+                                 handle, label, filenum, q);
+                       log_add(L_PARTIAL, "%s %s %s %d %s",
+                               hostname, qdiskname, datestamp, level, errstr);
+                   } else {
+                       putresult(DONE, "%s %s %d %s\n",
+                                 handle, label, filenum, q);
+                       log_add(L_SUCCESS, "%s %s %s %d %s",
+                               hostname, qdiskname, datestamp, level, errstr);
+                   }
+                   amfree(q);
+               } else { /* Chunked dump */
+                   num_splits++;
+                   if (mode == MODE_FILE_WRITE) {
+                       holdfile_path_thischunk = stralloc(holdfile_path);
+                       holdfile_offset_thischunk = (lseek(fd, (off_t)0, SEEK_CUR))/(off_t)1024;
+                       if(!save_holdfile){
+                           save_holdfile = alloc(SIZEOF(dumpfile_t));
+                       }
+                       memcpy(save_holdfile, &cur_holdfile,SIZEOF(dumpfile_t));
+                   }
+                   log_add(L_CHUNK, "%s %s %s %d %d %s", hostname, qdiskname,
+                           datestamp, num_splits, level, errstr);
+                   if (!nexting) { /* split dump complete */
+                       rt = (double)(curdump_rt.r.tv_sec) +
+                            ((double)(curdump_rt.r.tv_usec) / 1000000.0);
+                       snprintf(kb_str, SIZEOF(kb_str), OFF_T_FMT,
+                               (OFF_T_FMT_TYPE)(filesize + cur_span_chunkstart));
+                       snprintf(kps_str, SIZEOF(kps_str), "%3.1lf",
+                           isnormal(rt) ?
+                           ((double)(filesize+cur_span_chunkstart)) / rt :
+                           0.0);
+                        amfree(errstr);
+                       errstr = newvstralloc(errstr,
+                                             "[sec ", walltime_str(curdump_rt),
+                                             " kb ", kb_str,
+                                             " kps ", kps_str,
+                                             " ", str,
+                                             "]",
+                                             NULL);
+                        q = squote(errstr);
+                       putresult(DONE, "%s %s %d %s\n", handle, label,
+                                 filenum, q);
+                       log_add(L_CHUNKSUCCESS, "%s %s %s %d %s",
+                               hostname, qdiskname, datestamp, level, errstr);
+                       amfree(save_holdfile);
+                       amfree(holdfile_path_thischunk);
+                        amfree(q);
+                   }
+               }
                amfree(str);
-               q = squote(errstr);
-               putresult(DONE, "%s %s %d %s\n",
-                         handle, label, filenum, q);
-               amfree(q);
-               log_add(L_SUCCESS, "%s %s %s %d %s",
-                       hostname, diskname, datestamp, level, errstr);
+
+               if (!nexting) {
+                   num_splits = 0;
+                   expected_splits = 0;
+                   amfree(holdfile_name);
+                   num_holdfiles = 0;
+                   cur_span_chunkstart = (off_t)0;
+                   curdump_rt = times_zero;
+               }
+               amfree(errstr);
+               
 #ifdef HAVE_LIBVTBLC
                /* 
                 *  We have 44 characters available for the label string:
@@ -1020,19 +1867,17 @@ void read_file(fd, handle, hostname, diskname, datestamp, level, port_flag)
                if ((len = strlen(hostname)) <= 20) {
                    memset(desc + len, ' ', 1);
                    offset = len + 1;
-               }
-               else{
+               } else {
                    memset(desc + 20, ' ', 1);
                    offset = 21;
                }
 
-               strncpy(desc + offset, diskname, 20);
+               strncpy(desc + offset, qdiskname, 20);
 
-               if ((len = strlen(diskname)) <= 20) {
+               if ((len = strlen(qdiskname)) <= 20) {
                    memset(desc + offset + len, ' ', 1);
                    offset = offset + len + 1;
-               }
-               else{
+               } else {
                    memset(desc + offset + 20, ' ', 1);
                    offset = offset + 21;
                }
@@ -1045,9 +1890,14 @@ void read_file(fd, handle, hostname, diskname, datestamp, level, port_flag)
                fflush(stderr);
 
                /* pass label string on to tape writer */
-               syncpipe_put('L');
-               syncpipe_putint(filenum);
-               syncpipe_putstr(vol_label);             
+               if (syncpipe_put('L', filenum) == -1) {
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
+               if (syncpipe_putstr(vol_label) == -1) {
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
 
                /* 
                 * reformat datestamp for later use with set_date from vtblc 
@@ -1060,78 +1910,194 @@ void read_file(fd, handle, hostname, diskname, datestamp, level, port_flag)
                        vol_date);
 
                /* pass date string on to tape writer */                
-               syncpipe_put('D');
-               syncpipe_putint(filenum);
-               syncpipe_putstr(vol_date);
+               if (syncpipe_put('D', filenum) == -1) {
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
+               if (syncpipe_putstr(vol_date) == -1) {
+                   put_syncpipe_fault_result(handle);
+                   return (-1);
+               }
 
 #endif /* HAVE_LIBVTBLC */
            }
-           return;
+           /* reset stuff that assumes we're on a new file */
+
+           if (!nexting)
+               return 0;
+
+#ifdef ASSERTIONS
+           opening = 1;
+#endif
+           nexting = 0;
+           closing = 0;
+           filesize = (off_t)0;
+           if (syncpipe_put('O', 0) == -1) {
+               put_syncpipe_fault_result(handle);
+               return -1;
+           }
+           if (syncpipe_putstr(datestamp) == -1) {
+               put_syncpipe_fault_result(handle);
+               return -1;
+           }
+           if (syncpipe_putstr(hostname) == -1) {
+               put_syncpipe_fault_result(handle);
+               return -1;
+           }
+           if (syncpipe_putstr(qdiskname) == -1) {
+               put_syncpipe_fault_result(handle);
+               return -1;
+           }
+           if (syncpipe_putint(level) == -1) {
+               put_syncpipe_fault_result(handle);
+               return -1;
+           }
+           for (bp = buftable; bp < buftable + conf_tapebufs; bp++) {
+               bp->status = EMPTY;
+           }
+           bp = buftable;
+           header_written = 0;
+           break;
+
+       case 'X':
+           /*
+            * Pipe read error: Communications is severed at least
+            * back to us.  We send a blind 'Q' (quit) and we don't
+            * wait for a response...
+            */
+           syncpipe_put('Q', 0);                       /* ACK error */
+           fprintf(stderr, "taper: communications pipe from reader severed\n");
+           return -1;
 
        default:
-           assert(0);
+           q = squotef("[Taper syncpipe protocol error]");
+           putresult(TAPE_ERROR, "%s %s\n", handle, q);
+           log_add(L_ERROR, "tape-error %s %s", handle, q);
+           amfree(q);
+           return -1;
        }
     }
+    return 0;
 }
 
-int taper_fill_buffer(fd, bp, buflen)
-int fd;
-buffer_t *bp;
-int buflen;
+ssize_t
+taper_fill_buffer(
+    int fd,
+    buffer_t *bp,
+    size_t buflen)
 {
     char *curptr;
-    int spaceleft, cnt;
+    ssize_t cnt;
 
     curptr = bp->buffer;
-    bp->size = 0;
-    spaceleft = buflen;
 
-    do {
-       cnt = read(fd, curptr, spaceleft);
-       switch(cnt) {
-       case 0: /* eof */
-           if(interactive) fputs("r0", stderr);
-           return bp->size;
-       case -1:        /* error on read, punt */
-           if(interactive) fputs("rE", stderr);
-           return -1;
-       default:
-           spaceleft -= cnt;
-           curptr += cnt;
-           bp->size += cnt;
-       }
+    cnt = fullread(fd, curptr, buflen);
+    switch(cnt) {
+    case 0:    /* eof */
+       if (interactive)
+           fputs("r0", stderr);
+       bp->size = 0;
+       return (ssize_t)0;
+       /*NOTREACHED*/
+
+    case -1:   /* error on read, punt */
+       if (interactive)
+           fputs("rE", stderr);
+       bp->size = 0;
+       return -1;
+       /*NOTREACHED*/
 
-    } while(spaceleft > 0);
+    default:
+       if ((mode == MODE_PORT_WRITE) && (splitsize > (off_t)0)) {
+           memcpy(splitbuf_wr_ptr, curptr, (size_t)cnt);
+           splitbuf_wr_ptr += cnt;
+       }
+       bp->size = cnt;
+       break;
+    }
 
-    if(interactive) fputs("R", stderr);
-    return bp->size;
+    if (interactive)
+       fputs("R", stderr);
+    return ((ssize_t)bp->size);
 }
 
+/* Given a dumpfile in holding, determine its size and figure out how many
+ * times we'd have to split it.
+ */
+int
+predict_splits(
+    char *filename)
+{
+    int splits = 0;
+    off_t total_kb = (off_t)0;
+    off_t adj_splitsize = splitsize - (off_t)(DISK_BLOCK_BYTES / 1024);
+
+    if (splitsize <= (off_t)0)
+       return(0);
+
+    if (adj_splitsize <= (off_t)0) {
+      error("Split size must be > " OFF_T_FMT "k",
+       (OFF_T_FMT_TYPE)(DISK_BLOCK_BYTES/1024));
+      /*NOTREACHED*/
+    }
+
+    /* should only calculuate this once, not on retries etc */
+    if (expected_splits != 0)
+       return(expected_splits);
+
+    total_kb = size_holding_files(filename, 1);
+    
+    if (total_kb <= (off_t)0) {
+      fprintf(stderr, "taper: r: " OFF_T_FMT
+               " kb holding file makes no sense, not precalculating splits\n",
+               (OFF_T_FMT_TYPE)total_kb);
+      fflush(stderr);
+      return(0);
+    }
+
+    fprintf(stderr, "taper: r: Total dump size should be " OFF_T_FMT
+               "kb, chunk size is " OFF_T_FMT "kb\n",
+               (OFF_T_FMT_TYPE)total_kb,
+               (OFF_T_FMT_TYPE)splitsize);
+    fflush(stderr);
+
+    splits = (int)(total_kb / adj_splitsize);
+    if ((splits == 0) || (total_kb % adj_splitsize))
+       splits++;
 
 
+    fprintf(stderr, "taper: r: Expecting to split into %d parts \n", splits);
+    fflush(stderr);
+
+    return(splits);
+}
+
 /*
  * ========================================================================
  * TAPE WRITER SIDE
  *
  */
 times_t idlewait, rdwait, wrwait, fmwait;
-long total_writes;
-double total_tape_used;
+unsigned long total_writes;
+off_t total_tape_used;
 int total_tape_fm;
 
-void write_file P((void));
-int write_buffer P((buffer_t *bp));
+void write_file(void);
+int write_buffer(buffer_t *bp);
 
-void tape_writer_side(getp, putp)
-int getp, putp;
+void
+tape_writer_side(
+    int getp,
+    int putp)
 {
-    char tok;
+    int tok;
     int tape_started;
     char *str;
     char *hostname;
     char *diskname;
     char *datestamp;
     int level;
+    int tmpint;
 
 #ifdef HAVE_LIBVTBLC
     char *vol_label;
@@ -1140,73 +2106,118 @@ int getp, putp;
 
     procname = "writer";
     syncpipe_init(getp, putp);
-
     tape_started = 0;
     idlewait = times_zero;
 
-    while(1) {
+    while (1) {
        startclock();
-       tok = syncpipe_get();
+       if ((tok = syncpipe_get(&tmpint)) == -1) {
+           error("writer: Syncpipe failure before start");
+           /*NOTREACHED*/
+       }
+
        idlewait = timesadd(idlewait, stopclock());
-       if(tok != 'S' && tok != 'Q' && !tape_started) {
+       if (tok != 'S' && tok != 'Q' && !tape_started) {
            error("writer: token '%c' before start", tok);
+           /*NOTREACHED*/
        }
 
        switch(tok) {
+       case 'H':               /* Reader read pipe side is down */
+           dbprintf(("writer: Communications with reader is down"));
+           error("writer: Communications with reader is down");
+           /*NOTREACHED*/
+           
        case 'S':               /* start-tape */
-           if(tape_started) {
+           if (tape_started) {
                error("writer: multiple start requests");
+               /*NOTREACHED*/
+           }
+           if ((str = syncpipe_getstr()) == NULL) {
+               error("writer: Syncpipe failure");
+               /*NOTREACHED*/
            }
-           str = syncpipe_getstr();
-           if(!first_tape(str ? str : "bad-datestamp")) {
-               if(tape_fd >= 0) {
+           if (!first_tape(str ? str : "bad-datestamp")) {
+               if (tape_fd >= 0) {
                    tapefd_close(tape_fd);
                    tape_fd = -1;
                }
-               syncpipe_put('E');
-               syncpipe_putstr(errstr);
+               if (syncpipe_put('E', 0) == -1) {
+                   error("writer: Syncpipe failure passing exit code");
+                   /*NOTREACHED*/
+               }
+               if (syncpipe_putstr(errstr) == -1) {
+                   error("writer: Syncpipe failure passing exit string");
+                   /*NOTREACHED*/
+               }
                /* wait for reader to acknowledge error */
                do {
-                   tok = syncpipe_get();
-                   if(tok != 'e') {
+                   if ((tok = syncpipe_get(&tmpint)) == -1) {
+                       error("writer: Syncpipe failure waiting for error ack");
+                       /*NOTREACHED*/
+                   }
+                   if (tok != 'e') {
                        error("writer: got '%c' unexpectedly after error", tok);
+                       /*NOTREACHED*/
                    }
-               } while(tok != 'e');
+               } while (tok != 'e');
            } else {
-               syncpipe_put('S');
+               if (syncpipe_put('S', 0) == -1) {
+                   error("writer: syncpipe failure while starting tape");
+                   /*NOTREACHED*/
+               }
                tape_started = 1;
            }
            amfree(str);
-
            break;
 
        case 'O':               /* open-output */
-           datestamp = syncpipe_getstr();
+           if ((datestamp = syncpipe_getstr()) == NULL) {
+               error("writer: Syncpipe failure during open");
+               /*NOTREACHED*/
+           }
            tapefd_setinfo_datestamp(tape_fd, datestamp);
            amfree(datestamp);
-           hostname = syncpipe_getstr();
+
+           if ((hostname = syncpipe_getstr()) == NULL) {
+               error("writer: Syncpipe failure fetching hostname");
+               /*NOTREACHED*/
+           }
            tapefd_setinfo_host(tape_fd, hostname);
            amfree(hostname);
-           diskname = syncpipe_getstr();
+
+           if ((diskname = syncpipe_getstr()) == NULL) {
+               error("writer: Syncpipe failure fetching diskname");
+               /*NOTREACHED*/
+           }
            tapefd_setinfo_disk(tape_fd, diskname);
            amfree(diskname);
-           level = syncpipe_getint();
+           if ((level = syncpipe_getint()) == -1) {
+               error("writer: Syncpipe failure fetching level");
+               /*NOTREACHED*/
+           }
            tapefd_setinfo_level(tape_fd, level);
            write_file();
            break;
 
 #ifdef HAVE_LIBVTBLC
        case 'L':               /* read vtbl label */
-           vtbl_no = syncpipe_getint();
-           vol_label = syncpipe_getstr();
+           vtbl_no = tmpint;
+           if ((vol_label = syncpipe_getstr()) == NULL) {
+               error("writer: Syncpipe failure fetching vrbl label");
+               /*NOTREACHED*/
+           }
            fprintf(stderr, "taper: read label string \"%s\" from pipe\n", 
                    vol_label);
            strncpy(vtbl_entry[vtbl_no].label, vol_label, 45);
            break;
 
        case 'D':               /* read vtbl date */
-           vtbl_no = syncpipe_getint();
-           vol_date = syncpipe_getstr();
+           vtbl_no = tmpint;
+           if ((vol_date = syncpipe_getstr()) == NULL) {
+               error("writer: Syncpipe failure fetching vrbl date");
+               /*NOTREACHED*/
+           }
            fprintf(stderr, "taper: read date string \"%s\" from pipe\n", 
                    vol_date);
            strncpy(vtbl_entry[vtbl_no].date, vol_date, 20);
@@ -1216,22 +2227,23 @@ int getp, putp;
        case 'Q':
            end_tape(0);        /* XXX check results of end tape ?? */
            clear_tapelist();
-           detach_buffers(buffers);
-           amfree(taper_datestamp);
+           free_server_config();
+           amfree(taper_timestamp);
            amfree(label);
            amfree(errstr);
            amfree(changer_resultstr);
            amfree(tapedev);
+           amfree(conf_tapelist);
            amfree(config_dir);
            amfree(config_name);
 
            malloc_size_2 = malloc_inuse(&malloc_hist_2);
 
-           if(malloc_size_1 != malloc_size_2) {
+           if (malloc_size_1 != malloc_size_2) {
                malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
            }
-
            exit(0);
+           /*NOTREACHED*/
 
        default:
            assert(0);
@@ -1239,13 +2251,15 @@ int getp, putp;
     }
 }
 
-void write_file()
+void
+write_file(void)
 {
     buffer_t *bp;
     int full_buffers, i, bufnum;
-    char tok;
+    int tok;
     char number[NUM_STR_SIZE];
     char *rdwait_str, *wrwait_str, *fmwait_str;
+    int tmpint;
 
     rdwait = wrwait = times_zero;
     total_writes = 0;
@@ -1254,7 +2268,7 @@ void write_file()
     full_buffers = 0;
     tok = '?';
 
-    if(bufdebug) {
+    if (bufdebug) {
        fprintf(stderr, "taper: w: start file\n");
        fflush(stderr);
     }
@@ -1262,13 +2276,19 @@ void write_file()
     /*
      * Tell the reader that the tape is open, and give it all the buffers.
      */
-    syncpipe_put('O');
-    for(i = 0; i < conf_tapebufs; i++) {
-       if(bufdebug) {
+    if (syncpipe_put('O', 0) == -1) {
+       error("writer: Syncpipe failure starting write sequence");
+       /*NOTREACHED*/
+    }
+    for (i = 0; i < conf_tapebufs; i++) {
+       if (bufdebug) {
            fprintf(stderr, "taper: w: put R%d\n", i);
            fflush(stderr);
        }
-       syncpipe_put('R'); syncpipe_putint(i);
+       if (syncpipe_put('R', i) == -1) {
+           error("writer: Syncpipe failure readying write buffers");
+           /*NOTREACHED*/
+       }
     }
 
     /*
@@ -1278,7 +2298,7 @@ void write_file()
      */
 
     startclock();
-    if(!write_filemark())
+    if (!write_filemark())
        goto tape_error;
     fmwait = stopclock();
 
@@ -1296,13 +2316,17 @@ void write_file()
         * of starts/stops, which in turn saves tape and time.
         */
 
-       if(interactive) fputs("[WS]", stderr);
+       if (interactive)
+           fputs("[WS]", stderr);
        startclock();
-       while(full_buffers < conf_tapebufs - THRESHOLD) {
-           tok = syncpipe_get();
-           if(tok != 'W') break;
-           bufnum = syncpipe_getint();
-           if(bufdebug) {
+       while (full_buffers < conf_tapebufs - THRESHOLD) {
+           if ((tok = syncpipe_get(&bufnum)) == -1) {
+               error("writer: Syncpipe failure during buffer advance");
+               /*NOTREACHED*/
+           }
+           if (tok != 'W')
+               break;
+           if (bufdebug) {
                fprintf(stderr,"taper: w: got W%d\n",bufnum);
                fflush(stderr);
            }
@@ -1319,12 +2343,13 @@ void write_file()
         * in, then we will be STREAMING.
         */
 
-       while(full_buffers) {
-           if(tt_file_pad && bp->size < tt_blocksize) {
+       while (full_buffers) {
+           if (tt_file_pad && bp->size < (ssize_t)tt_blocksize) {
                memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size);
-               bp->size = tt_blocksize;
+               bp->size = (ssize_t)tt_blocksize;
            }
-           if(!write_buffer(bp)) goto tape_error;
+           if (!write_buffer(bp))
+               goto tape_error;
            full_buffers--;
            bp = nextbuf(bp);
        }
@@ -1342,54 +2367,74 @@ void write_file()
         * to wait for buffers to fill, we are then STOPPED again.
         */
 
-       while(tok == 'W' && bp->status == FULL) {
-           tok = syncpipe_get();
-           if(tok == 'W') {
-               bufnum = syncpipe_getint();
-               if(bufdebug) {
+       while (tok == 'W' && bp->status == FULL) {
+           if ((tok = syncpipe_get(&bufnum)) == -1) {
+               error("writer: Syncpipe failure advancing buffer");
+               /*NOTREACHED*/
+           }
+
+           if (tok == 'W') {
+               if (bufdebug) {
                    fprintf(stderr,"taper: w: got W%d\n",bufnum);
                    fflush(stderr);
                }
-               if(bufnum != bp-buftable) {
+               if(bufnum != (int)(bp - buftable)) {
                    fprintf(stderr,
                            "taper: tape-writer: my buf %d reader buf %d\n",
                            (int)(bp-buftable), bufnum);
                    fflush(stderr);
-                   syncpipe_put('E');
-                   syncpipe_putstr("writer-side buffer mismatch");
+                   if (syncpipe_put('E', 0) == -1) { 
+                       error("writer: Syncpipe failure putting error token");
+                       /*NOTREACHED*/
+                   }
+                   if (syncpipe_putstr("writer-side buffer mismatch") == -1) {
+                       error("writer: Syncpipe failure putting error messgae");
+                       /*NOTREACHED*/
+                   }
                    goto error_ack;
                }
-               if(tt_file_pad && bp->size < tt_blocksize) {
+               if (tt_file_pad && bp->size < (ssize_t)tt_blocksize) {
                    memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size);
-                   bp->size = tt_blocksize;
+                   bp->size = (ssize_t)tt_blocksize;
                }
-               if(!write_buffer(bp)) goto tape_error;
+               if (!write_buffer(bp))
+                   goto tape_error;
                bp = nextbuf(bp);
-           }
-           else if(tok == 'Q')
+           } else if (tok == 'Q') {
                return;
-           else if(tok == 'X')
+           } else if (tok == 'X') {
                goto reader_buffer_snafu;
-           else
+           } else {
                error("writer-side not expecting token: %c", tok);
+               /*NOTREACHED*/
+           }
        }
-    } while(tok == 'W');
+    } while (tok == 'W');
 
     /* got close signal from reader, acknowledge it */
 
-    if(tok == 'X')
+    if (tok == 'X')
        goto reader_buffer_snafu;
 
     assert(tok == 'C');
-    syncpipe_put('C');
+    if (syncpipe_put('C', 0) == -1) {
+       error("writer: Syncpipe failure putting close");
+       /*NOTREACHED*/
+    }
 
     /* tell reader the tape and file number */
 
-    syncpipe_putstr(label);
-    ap_snprintf(number, sizeof(number), "%d", filenum);
-    syncpipe_putstr(number);
+    if (syncpipe_putstr(label) == -1) {
+       error("writer: Syncpipe failure putting label");
+       /*NOTREACHED*/
+    }
+    snprintf(number, SIZEOF(number), "%d", filenum);
+    if (syncpipe_putstr(number) == -1) {
+       error("writer: Syncpipe failure putting filenum");
+       /*NOTREACHED*/
+    }
 
-    ap_snprintf(number, sizeof(number), "%ld", total_writes);
+    snprintf(number, SIZEOF(number), "%lu", total_writes);
     rdwait_str = stralloc(walltime_str(rdwait));
     wrwait_str = stralloc(walltime_str(wrwait));
     fmwait_str = stralloc(walltime_str(fmwait));
@@ -1404,7 +2449,10 @@ void write_file()
     amfree(rdwait_str);
     amfree(wrwait_str);
     amfree(fmwait_str);
-    syncpipe_putstr(errstr);
+    if (syncpipe_putstr(errstr) == -1) {
+       error("writer: Syncpipe failure putting '%s'", errstr);
+       /*NOTREACHED*/
+    }
 
     /* XXX go to next tape if past tape size? */
 
@@ -1412,39 +2460,56 @@ void write_file()
 
  tape_error:
     /* got tape error */
-    if(next_tape(1)) syncpipe_put('T');        /* next tape in place, try again */
-    else syncpipe_put('E');            /* no more tapes, fail */
-    syncpipe_putstr(errstr);
+    if (next_tape(1)) {
+       if (syncpipe_put('T', 0) == -1) {   /* next tape in place, try again */
+           error("writer: Syncpipe failure during tape advance");
+           /*NOTREACHED*/
+       }
+    } else {
+       if (syncpipe_put('E', 0) == -1) {   /* no more tapes, fail */
+           error("writer: Syncpipe failure during tape error");
+           /*NOTREACHED*/
+       }
+    }
+    if (syncpipe_putstr(errstr) == -1) {
+       error("writer: Syncpipe failure putting '%s'", errstr);
+       /*NOTREACHED*/
+    }
 
  error_ack:
     /* wait for reader to acknowledge error */
     do {
-       tok = syncpipe_get();
-       if(tok != 'W' && tok != 'C' && tok != 'e')
+       if ((tok = syncpipe_get(&tmpint)) == -1) {
+           error("writer: syncpipe failure waiting for error ack");
+           /*NOTREACHED*/
+       }
+
+       if (tok != 'W' && tok != 'C' && tok != 'e') {
            error("writer: got '%c' unexpectedly after error", tok);
-       if(tok == 'W')
-           syncpipe_getint();  /* eat buffer number */
-    } while(tok != 'e');
+           /*NOTREACHED*/
+       }
+    } while (tok != 'e');
     return;
 
  reader_buffer_snafu:
-    syncpipe_put('x');
+    if (syncpipe_put('x', 0) == -1) {
+       error("writer: syncpipe failure putting buffer snafu");
+       /*NOTREACHED*/
+    }
     return;
 }
 
-int write_buffer(bp)
-buffer_t *bp;
+int
+write_buffer(
+    buffer_t *bp)
 {
-    int rc;
+    ssize_t rc;
 
-    if(bp->status != FULL) {
-       /* XXX buffer management snafu */
-       assert(0);
-    }
+    assert(bp->status == FULL);
 
     startclock();
-    rc = tapefd_write(tape_fd, bp->buffer, bp->size);
-    if(rc == bp->size) {
+    rc = tapefd_write(tape_fd, bp->buffer, (size_t)bp->size);
+    if (rc == (ssize_t)bp->size) {
 #if defined(NEED_RESETOFS)
        static double tape_used_modulus_2gb = 0;
 
@@ -1454,23 +2519,28 @@ buffer_t *bp;
         * go silly on us.
         */
        tape_used_modulus_2gb += (double)rc;
-       if(tape_used_modulus_2gb + (double)rc > (double)0x7fffffff) {
+       if (tape_used_modulus_2gb + (double)rc > (double)0x7fffffff) {
            tape_used_modulus_2gb = 0;
            tapefd_resetofs(tape_fd);
        }
 #endif
        wrwait = timesadd(wrwait, stopclock());
        total_writes += 1;
-       total_tape_used += (double)rc;
+       total_tape_used += (off_t)rc;
        bp->status = EMPTY;
-       if(interactive || bufdebug) dumpstatus(bp);
-       if(interactive) fputs("W", stderr);
+       if (interactive || bufdebug)
+           dumpstatus(bp);
+       if (interactive)
+           fputs("W", stderr);
 
-       if(bufdebug) {
+       if (bufdebug) {
            fprintf(stderr, "taper: w: put R%d\n", (int)(bp-buftable));
            fflush(stderr);
        }
-       syncpipe_put('R'); syncpipe_putint(bp-buftable);
+       if (syncpipe_put('R', (int)(bp-buftable)) == -1) {
+           error("writer: Syncpipe failure during advancing write bufffer");
+           /*NOTREACHED*/
+       }
        return 1;
     } else {
        errstr = newvstralloc(errstr,
@@ -1478,12 +2548,81 @@ buffer_t *bp;
                              (rc != -1) ? "short write" : strerror(errno),
                              NULL);
        wrwait = timesadd(wrwait, stopclock());
-       if(interactive) fputs("[WE]", stderr);
+       if (interactive)
+           fputs("[WE]", stderr);
        return 0;
     }
 }
 
 
+static void 
+cleanup(void)
+{
+    REMOVE_SHARED_MEMORY(); 
+}
+
+
+/*
+ * Cleanup shared memory segments 
+ */
+static void 
+signal_handler(
+    int signum)
+{
+    log_add(L_INFO, "Received signal %d", signum);
+
+    exit(1);
+}
+
+
+/*
+ * Installing signal handlers for signal whose default action is 
+ * process termination so that we can clean up shared memory
+ * segments
+ */
+static void
+install_signal_handlers(void)
+{
+    struct sigaction act;
+
+    act.sa_handler = signal_handler;
+    act.sa_flags = 0;
+    sigemptyset(&act.sa_mask);
+
+    signal(SIGPIPE, SIG_IGN);
+
+    if (sigaction(SIGINT, &act, NULL) != 0) {
+       error("taper: couldn't install SIGINT handler [%s]", strerror(errno));
+       /*NOTREACHED*/
+    }
+
+    if (sigaction(SIGHUP, &act, NULL) != 0) {
+       error("taper: couldn't install SIGHUP handler [%s]", strerror(errno));
+       /*NOTREACHED*/
+    }
+   
+    if (sigaction(SIGTERM, &act, NULL) != 0) {
+       error("taper: couldn't install SIGTERM handler [%s]", strerror(errno));
+       /*NOTREACHED*/
+    }
+
+    if (sigaction(SIGUSR1, &act, NULL) != 0) {
+       error("taper: couldn't install SIGUSR1 handler [%s]", strerror(errno));
+       /*NOTREACHED*/
+    }
+
+    if (sigaction(SIGUSR2, &act, NULL) != 0) {
+       error("taper: couldn't install SIGUSR2 handler [%s]", strerror(errno));
+       /*NOTREACHED*/
+    }
+
+    if (sigaction(SIGALRM, &act, NULL) != 0) {
+       error("taper: couldn't install SIGALRM handler [%s]", strerror(errno));
+       /*NOTREACHED*/
+    }
+}
+
+
 /*
  * ========================================================================
  * SHARED-MEMORY BUFFER SUBSYSTEM
@@ -1494,43 +2633,51 @@ buffer_t *bp;
 
 int shmid = -1;
 
-char *attach_buffers(size)
-    unsigned int size;
+char *
+attach_buffers(
+    size_t size)
 {
     char *result;
 
     shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0700);
-    if(shmid == -1) {
+    if (shmid == -1) {
        return NULL;
     }
 
     result = (char *)shmat(shmid, (SHM_ARG_TYPE *)NULL, 0);
 
-    if(result == (char *)-1) {
+    if (result == (char *)-1) {
        int save_errno = errno;
 
        destroy_buffers();
        errno = save_errno;
        error("shmat: %s", strerror(errno));
+       /*NOTREACHED*/
     }
 
     return result;
 }
 
 
-void detach_buffers(bufp)
-    char *bufp;
+void
+detach_buffers(
+    char *bufp)
 {
-    if(shmdt((SHM_ARG_TYPE *)bufp) == -1) {
+    if ((bufp != NULL) &&
+        (shmdt((SHM_ARG_TYPE *)bufp) == -1)) {
        error("shmdt: %s", strerror(errno));
+       /*NOTREACHED*/
     }
 }
 
-void destroy_buffers()
+void
+destroy_buffers(void)
 {
-    if(shmid == -1) return;    /* nothing to destroy */
-    if(shmctl(shmid, IPC_RMID, NULL) == -1) {
+    if (shmid == -1)
+       return; /* nothing to destroy */
+    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        error("shmctl: %s", strerror(errno));
+       /*NOTREACHED*/
     }
 }
 
@@ -1551,19 +2698,21 @@ void destroy_buffers()
 #endif
 
 int shmfd = -1;
-unsigned int saved_size;
+size_t saved_size;
 
-char *attach_buffers(size)
-    unsigned int size;
+char *
+attach_buffers(
+    size_t size)
 {
     char *shmbuf;
 
 #ifdef ZERO_FILE
     shmfd = open(ZERO_FILE, O_RDWR);
-    if(shmfd == -1) {
+    if (shmfd == -1) {
        error("attach_buffers: could not open %s: %s",
              ZERO_FILE,
              strerror(errno));
+        /*NOTREACHED*/
     }
 #endif
 
@@ -1577,22 +2726,27 @@ char *attach_buffers(size)
     return shmbuf;
 }
 
-void detach_buffers(bufp)
-char *bufp;
+void
+detach_buffers(
+    char *bufp)
 {
-    if(munmap((void *)bufp, saved_size) == -1) {
+    if ((bufp != NULL) && 
+       (munmap((void *)bufp, saved_size) == -1)) {
        error("detach_buffers: munmap: %s", strerror(errno));
+       /*NOTREACHED*/
     }
 
-    aclose(shmfd);
+    if (shmfd != -1)
+       aclose(shmfd);
 }
 
-void destroy_buffers()
+void
+destroy_buffers(void)
 {
 }
 
 #else
-error: must define either HAVE_SYSVSHM or HAVE_MMAP!
+#error: must define either HAVE_SYSVSHM or HAVE_MMAP!
 #endif
 #endif
 
@@ -1606,208 +2760,244 @@ error: must define either HAVE_SYSVSHM or HAVE_MMAP!
 
 int getpipe, putpipe;
 
-void syncpipe_init(rd, wr)
-int rd, wr;
+void
+syncpipe_init(
+    int rd,
+    int wr)
 {
     getpipe = rd;
     putpipe = wr;
 }
 
-char syncpipe_get()
+void
+syncpipe_read_error(
+    ssize_t    rc,
+    ssize_t    expected)
 {
-    int rc;
-    char buf[1];
+    char buf[sizeof(char) + sizeof(int)];
+
+    if (rc == 0) {
+       dbprintf(("syncpipe_get %s halting: Unexpected read EOF\n", procname));
+       fprintf(stderr, "syncpipe_get %s halting: Unexpected read EOF\n", procname);
+    } else if (rc < 0) {
+       dbprintf(("syncpipe_get %s halting: Read error - %s\n",
+                       procname, strerror(errno)));
+       fprintf(stderr, "syncpipe_get %s halting: Read error - %s\n",
+                       procname, strerror(errno));
+    } else {
+       dbprintf(("syncpipe_get %s halting: Read "
+               SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
+               procname, (SSIZE_T_FMT_TYPE)(rc - expected),
+               (SSIZE_T_FMT_TYPE)expected));
+       fprintf(stderr, "syncpipe_get %s halting: Read "
+               SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
+               procname, (SSIZE_T_FMT_TYPE)(rc - expected),
+               (SSIZE_T_FMT_TYPE)expected);
+    }
+    /* Halt the other side if it's still alive */
+    buf[0] = 'H';
+    memset(&buf[1], 0, SIZEOF(int));
+    if (write(putpipe, buf, SIZEOF(buf)))
+       return;
+}
 
-    rc = read(getpipe, buf, sizeof(buf));
-    if(rc == 0)                /* EOF */
-       error("syncpipe_get: %c: unexpected EOF", *procname);
-    else if(rc < 0)
-       error("syncpipe_get: %c: %s", *procname, strerror(errno));
+void
+syncpipe_write_error(
+    ssize_t    rc,
+    ssize_t    expected)
+{
+    char buf[sizeof(char) + sizeof(int)];
+
+    if (rc == 0) {             /* EOF */
+       dbprintf(("syncpipe %s halting: Write EOF\n", procname));
+       fprintf(stderr, "syncpipe %s halting: Write EOF\n", procname);
+    } else if (rc < 0) {
+       dbprintf(("syncpipe %s halting: Write error - %s\n",
+                       procname, strerror(errno)));
+       fprintf(stderr, "syncpipe %s halting: Write error - %s\n",
+                       procname, strerror(errno));
+    } else {
+       dbprintf(("syncpipe %s halting: Write "
+                       SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
+                       procname, (SSIZE_T_FMT_TYPE)(rc - expected),
+                       (SSIZE_T_FMT_TYPE)expected));
+       fprintf(stderr, "syncpipe %s halting: Write "
+                       SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
+                       procname, (SSIZE_T_FMT_TYPE)(rc - expected),
+                       (SSIZE_T_FMT_TYPE)expected);
+    }
+    /* Halt the other side if it's still alive */
+    buf[0] = 'H';
+    memset(&buf[1], 0, SIZEOF(int));
+    if (write(putpipe, buf, SIZEOF(buf)))
+       return;
+}
+
+int
+syncpipe_get(
+    int *intp)
+{
+    ssize_t rc;
+    char buf[SIZEOF(char) + SIZEOF(int)];
+
+    memset(buf, 0, sizeof(buf));
+    rc = fullread(getpipe, buf, SIZEOF(buf));
+    if (rc != (ssize_t)sizeof(buf)) {
+       syncpipe_read_error(rc, (ssize_t)sizeof(buf));
+       return (-1);
+    }
 
-    if(bufdebug && *buf != 'R' && *buf != 'W') {
-       fprintf(stderr,"taper: %c: getc %c\n",*procname,*buf);
+    if (bufdebug && *buf != 'R' && *buf != 'W') {
+       fprintf(stderr,"taper: %c: getc %c\n", *procname, *buf);
        fflush(stderr);
     }
 
-    return buf[0];
+    memcpy(intp, &buf[1], SIZEOF(int));
+    return (int)buf[0];
 }
 
-int syncpipe_getint()
+int
+syncpipe_getint(void)
 {
-    int rc;
-    int i;
-    int len = sizeof(i);
-    char *p;
+    ssize_t rc;
+    int i = 0;
 
-    for(p = (char *)&i; len > 0; len -= rc, p += rc) {
-       if ((rc = read(getpipe, p, len)) <= 0) {
-           error("syncpipe_getint: %s",
-                 rc < 0 ? strerror(errno) : "short read");
-       }
+    rc = fullread(getpipe, &i, SIZEOF(i));
+    if (rc != (ssize_t)sizeof(i)) {
+       syncpipe_read_error(rc, (ssize_t)sizeof(i));
+       return (-1);
     }
 
-    return i;
+    return (i);
 }
 
 
-char *syncpipe_getstr()
+char *
+syncpipe_getstr(void)
 {
-    int rc;
+    ssize_t rc;
     int len;
-    char *p;
     char *str;
 
-    if((len = syncpipe_getint()) <= 0) {
-       return NULL;
+    if ((len = syncpipe_getint()) <= 0) {
+       fprintf(stderr, "syncpipe %s halting: Protocol error - "
+                       "Invalid string length (%d)\n", procname, len);
+       syncpipe_put('H', 0); /* Halt the other side */
+       exit(1);
+       /*NOTREACHED*/
     }
 
-    str = alloc(len);
+    str = alloc((size_t)len);
 
-    for(p = str; len > 0; len -= rc, p += rc) {
-       if ((rc = read(getpipe, p, len)) <= 0) {
-           error("syncpipe_getstr: %s",
-                 rc < 0 ? strerror(errno) : "short read");
-       }
+    rc = fullread(getpipe, str, (size_t)len);
+    if (rc != (ssize_t)len) {
+       syncpipe_read_error(rc, (ssize_t)len);
+       return (NULL);
     }
-
-    return str;
+    return (str);
 }
 
 
-void syncpipe_put(chi)
-int chi;
+int
+syncpipe_put(
+    int chi,
+    int intval)
 {
-    int l, n, s;
-    char ch = chi;
-    char *item = &ch;
+    char buf[sizeof(char) + sizeof(int)];
+    ssize_t    rc;
 
-    if(bufdebug && chi != 'R' && chi != 'W') {
-       fprintf(stderr,"taper: %c: putc %c\n",*procname,chi);
+    buf[0] = (char)chi;
+    memcpy(&buf[1], &intval, SIZEOF(int));
+    if (bufdebug && buf[0] != 'R' && buf[0] != 'W') {
+       fprintf(stderr,"taper: %c: putc %c\n",*procname,buf[0]);
        fflush(stderr);
     }
 
-    for(l = 0, n = sizeof(ch); l < n; l += s) {
-       if((s = write(putpipe, item + l, n - l)) < 0) {
-           error("syncpipe_put: %s", strerror(errno));
-       }
+    rc = fullwrite(putpipe, buf, SIZEOF(buf));
+    if (rc != (ssize_t)sizeof(buf)) {
+       syncpipe_write_error(rc, (ssize_t)sizeof(buf));
+       return (-1);
     }
+    return (0);
 }
 
-void syncpipe_putint(i)
-int i;
+int
+syncpipe_putint(
+    int i)
 {
-    int l, n, s;
-    char *item = (char *)&i;
+    ssize_t    rc;
 
-    for(l = 0, n = sizeof(i); l < n; l += s) {
-       if((s = write(putpipe, item + l, n - l)) < 0) {
-           error("syncpipe_putint: %s", strerror(errno));
-       }
+    rc = fullwrite(putpipe, &i, SIZEOF(i));
+    if (rc != (ssize_t)sizeof(i)) {
+       syncpipe_write_error(rc, (ssize_t)sizeof(i));
+       return (-1);
+       /* NOTREACHED */
     }
+    return (0);
 }
 
-void syncpipe_putstr(item)
-char *item;
+int
+syncpipe_putstr(
+    const char *str)
 {
-    int l, n, s;
+    ssize_t n, rc;
 
-    n = strlen(item)+1;                                /* send '\0' as well */
-    syncpipe_putint(n);
-    for(l = 0, n = strlen(item)+1; l < n; l += s) {
-       if((s = write(putpipe, item + l, n - l)) < 0) {
-           error("syncpipe_putstr: %s", strerror(errno));
-       }
+    if(!str)
+       str = "UNKNOWN syncpipe_putstr STRING";
+
+    n = (ssize_t)strlen(str) + 1;                      /* send '\0' as well */
+    syncpipe_putint((int)n);
+
+    rc = fullwrite(putpipe, str, (size_t)n);
+    if (rc != n) {
+       syncpipe_write_error(rc, n);
+       return (-1);
     }
+    return (0);
 }
-
 \f
 /*
  * ========================================================================
  * TAPE MANIPULATION SUBSYSTEM
  *
  */
+int label_tape(void);
 
 /* local functions */
-int scan_init P((int rc, int ns, int bk));
-int taperscan_slot P((int rc, char *slotstr, char *device));
-char *taper_scan P((void));
-int label_tape P((void));
 
-int label_tape()
-{
+/* return 0 on success              */
+/* return 1 on error and set errstr */
+int
+label_tape(void)
+{  
     char *conf_tapelist_old = NULL;
-    char *olddatestamp = NULL;
     char *result;
-    tape_t *tp;
     static int first_call = 1;
+    char *timestamp;
+    char *error_msg = NULL;
+    char *s, *r;
+    int slot = -1;
 
-    if(have_changer) {
-       amfree(tapedev);
-       if ((tapedev = taper_scan()) == NULL) {
-           errstr = newstralloc(errstr, changer_resultstr);
-           return 0;
-       }
-    }
-
-#ifdef HAVE_LINUX_ZFTAPE_H
-    if (is_zftape(tapedev) == 1){
-       if((tape_fd = tape_open(tapedev, O_RDONLY)) == -1) {
-           errstr = newstralloc2(errstr, "taper: ",
-                                 (errno == EACCES) ? "tape is write-protected"
-                                 : strerror(errno));
-           return 0;
-       }
-       if((result = tapefd_rdlabel(tape_fd, &olddatestamp, &label)) != NULL) {
-           amfree(olddatestamp);
-           errstr = newstralloc(errstr, result);
-           return 0;
-       }
-       if(tapefd_rewind(tape_fd) == -1) { 
-           return 0;
-       } 
-       tapefd_close(tape_fd);
-       tape_fd = -1;
-    }
-    else
-#endif /* !HAVE_LINUX_ZFTAPE_H */
-    if((result = tape_rdlabel(tapedev, &olddatestamp, &label)) != NULL) {
-       amfree(olddatestamp);
-       errstr = newstralloc(errstr, result);
+    amfree(label);
+    amfree(tapedev);
+    if (taper_scan(NULL, &label, &timestamp, &tapedev, CHAR_taperscan_output_callback, &error_msg) < 0) {
+       fprintf(stderr, "%s\n", error_msg);
+       errstr = error_msg;
+       error_msg = NULL;
+       amfree(timestamp);
        return 0;
     }
-
-    fprintf(stderr, "taper: read label `%s' date `%s'\n", label, olddatestamp);
-    fflush(stderr);
-    amfree(olddatestamp);
-
-    /* check against tape list */
-    if (strcmp(label, FAKE_LABEL) != 0) {
-       tp = lookup_tapelabel(label);
-       if(tp == NULL) {
-           errstr = newvstralloc(errstr,
-                                 "label ", label,
-               " match labelstr but it not listed in the tapelist file",
-                                 NULL);
-           return 0;
-       }
-       else if(tp != NULL && !reusable_tape(tp)) {
-           errstr = newvstralloc(errstr,
-                                 "cannot overwrite active tape ", label,
-                                 NULL);
-           return 0;
-       }
-
-       if(!match(labelstr, label)) {
-           errstr = newvstralloc(errstr,
-                                 "label ", label,
-                                 " doesn\'t match labelstr \"", labelstr, "\"",
-                                 NULL);
-           return 0;
+    amfree(timestamp);
+    if(error_msg) {
+       s = error_msg; r = NULL;
+       while((s=strstr(s,"slot "))) { s += 5; r=s; };
+       if(r) {
+           slot = atoi(r);
        }
+       amfree(error_msg);
     }
-
-    if((tape_fd = tape_open(tapedev, O_WRONLY)) == -1) {
-       if(errno == EACCES) {
+    if ((tape_fd = tape_open(tapedev, O_WRONLY)) == -1) {
+       if (errno == EACCES) {
            errstr = newstralloc(errstr,
                                 "writing label: tape is write protected");
        } else {
@@ -1817,21 +3007,28 @@ int label_tape()
        return 0;
     }
 
-    tapefd_setinfo_length(tape_fd, tt->length);
+    tapefd_setinfo_length(tape_fd, tapetype_get_length(tt));
 
-    tapefd_setinfo_datestamp(tape_fd, taper_datestamp);
+    tapefd_setinfo_datestamp(tape_fd, taper_timestamp);
     tapefd_setinfo_disk(tape_fd, label);
-    result = tapefd_wrlabel(tape_fd, taper_datestamp, label, tt_blocksize);
-    if(result != NULL) {
+    result = tapefd_wrlabel(tape_fd, taper_timestamp, label, tt_blocksize);
+    if (result != NULL) {
        errstr = newstralloc(errstr, result);
        return 0;
     }
 
-    fprintf(stderr, "taper: wrote label `%s' date `%s'\n", label, taper_datestamp);
+    if(slot > -1) {
+       fprintf(stderr, "taper: slot: %d wrote label `%s' date `%s'\n", slot,
+               label, taper_timestamp);
+    }
+    else {
+       fprintf(stderr, "taper: wrote label `%s' date `%s'\n", label,
+               taper_timestamp);
+    }
     fflush(stderr);
 
 #ifdef HAVE_LIBVTBLC
-    /* store time for the first volume entry */ 
+    /* store time for the first volume entry */
     time(&raw_time);
     tape_timep = localtime(&raw_time);
     strftime(start_datestr, 20, "%T %D", tape_timep);
@@ -1839,71 +3036,75 @@ int label_tape()
     fflush(stderr);
 #endif /* HAVE_LIBVTBLC */
 
-    /* write tape list */
-
-    /* XXX add cur_tape number to tape list structure */
     if (strcmp(label, FAKE_LABEL) != 0) {
-
-       if(cur_tape == 0) {
+       if (cur_tape == 0) {
            conf_tapelist_old = stralloc2(conf_tapelist, ".yesterday");
        } else {
            char cur_str[NUM_STR_SIZE];
 
-           ap_snprintf(cur_str, sizeof(cur_str), "%d", cur_tape - 1);
+           snprintf(cur_str, SIZEOF(cur_str), "%d", cur_tape - 1);
            conf_tapelist_old = vstralloc(conf_tapelist,
-                                       ".today.", cur_str, NULL);
+                                         ".today.", cur_str, NULL);
        }
-       if(write_tapelist(conf_tapelist_old)) {
+
+       if (write_tapelist(conf_tapelist_old)) {
            error("could not write tapelist: %s", strerror(errno));
+           /*NOTREACHED*/
        }
        amfree(conf_tapelist_old);
 
        remove_tapelabel(label);
-       add_tapelabel(atoi(taper_datestamp), label);
-       if(write_tapelist(conf_tapelist)) {
+       add_tapelabel(taper_timestamp, label);
+       if (write_tapelist(conf_tapelist)) {
            error("could not write tapelist: %s", strerror(errno));
+           /*NOTREACHED*/
        }
     }
 
     log_add(L_START, "datestamp %s label %s tape %d",
-           taper_datestamp, label, cur_tape);
+           taper_timestamp, label, cur_tape);
     if (first_call && strcmp(label, FAKE_LABEL) == 0) {
        first_call = 0;
        log_add(L_WARNING, "tapedev is %s, dumps will be thrown away", tapedev);
     }
 
-    total_tape_used=0.0;
+    total_tape_used=(off_t)0;
     total_tape_fm = 0;
 
     return 1;
 }
 
-int first_tape(new_datestamp)
-char *new_datestamp;
+/* return 0 on error and set errstr */
+/* return 1 on success              */
+int
+first_tape(
+    char *new_datestamp)
 {
-    if((have_changer = changer_init()) < 0) {
+    if ((have_changer = changer_init()) < 0) {
        error("changer initialization failed: %s", strerror(errno));
+       /*NOTREACHED*/
     }
     changer_debug = 1;
 
-    taper_datestamp = newstralloc(taper_datestamp, new_datestamp);
+    taper_timestamp = newstralloc(taper_timestamp, new_datestamp);
 
-    if(!label_tape())
+    if (!label_tape())
        return 0;
 
     filenum = 0;
     return 1;
 }
 
-int next_tape(writerror)
-int writerror;
+int
+next_tape(
+    int writerror)
 {
     end_tape(writerror);
 
-    if(++cur_tape >= runtapes)
+    if (++cur_tape >= runtapes)
        return 0;
 
-    if(!label_tape()) {
+    if (!label_tape()) {
        return 0;
     }
 
@@ -1912,33 +3113,34 @@ int writerror;
 }
 
 
-int end_tape(writerror)
-int writerror;
+int
+end_tape(
+    int writerror)
 {
     char *result;
     int rc = 0;
 
-    if(tape_fd >= 0) {
-       log_add(L_INFO, "tape %s kb %ld fm %d %s", 
+    if (tape_fd >= 0) {
+       log_add(L_INFO, "tape %s kb " OFF_T_FMT " fm %d %s", 
                label,
-               (long) ((total_tape_used+1023.0) / 1024.0),
+               (OFF_T_FMT_TYPE)((total_tape_used+(off_t)1023) / (off_t)1024),
                total_tape_fm,
                writerror? errstr : "[OK]");
 
-       fprintf(stderr, "taper: writing end marker. [%s %s kb %ld fm %d]\n",
-               label,
+       fprintf(stderr, "taper: writing end marker. [%s %s kb "
+               OFF_T_FMT " fm %d]\n", label,
                writerror? "ERR" : "OK",
-               (long) ((total_tape_used+1023.0) / 1024.0),
+               (OFF_T_FMT_TYPE)((total_tape_used+(off_t)1023) / (off_t)1024),
                total_tape_fm);
        fflush(stderr);
-       if(! writerror) {
-           if(! write_filemark()) {
+       if (! writerror) {
+           if (! write_filemark()) {
                rc = 1;
                goto common_exit;
            }
 
-           result = tapefd_wrendmark(tape_fd, taper_datestamp, tt_blocksize);
-           if(result != NULL) {
+           result = tapefd_wrendmark(tape_fd, taper_timestamp, tt_blocksize);
+           if (result != NULL) {
                errstr = newstralloc(errstr, result);
                rc = 1;
                goto common_exit;
@@ -1950,14 +3152,14 @@ int writerror;
     if (tape_fd >= 0 && is_zftape(tapedev) == 1) {
        /* rewind the tape */
 
-       if(tapefd_rewind(tape_fd) == -1 ) {
+       if (tapefd_rewind(tape_fd) == -1 ) {
            errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno));
            rc = 1;
            goto common_exit;
        }
        /* close the tape */
 
-       if(tapefd_close(tape_fd) == -1) {
+       if (tapefd_close(tape_fd) == -1) {
            errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
            rc = 1;
            goto common_exit;
@@ -1970,7 +3172,7 @@ int writerror;
        fflush(stderr);
     
        if ((tape_fd = raw_tape_open(rawtapedev, O_RDWR)) == -1) {
-           if(errno == EACCES) {
+           if (errno == EACCES) {
                errstr = newstralloc(errstr,
                                     "updating volume table: tape is write protected");
            } else {
@@ -1992,7 +3194,7 @@ int writerror;
        }
        /* set volume label and date for first entry */
        vtbl_no = 0;
-       if(set_label(label, volumes, num_volumes, vtbl_no)){
+       if (set_label(label, volumes, num_volumes, vtbl_no)) {
            errstr = newstralloc2(errstr,
                                  "setting label for entry 1: ",
                                  strerror(errno));
@@ -2000,7 +3202,7 @@ int writerror;
            goto common_exit;
        }
        /* date of start writing this tape */
-       if (set_date(start_datestr, volumes, num_volumes, vtbl_no)){
+       if (set_date(start_datestr, volumes, num_volumes, vtbl_no)) {
            errstr = newstralloc2(errstr,
                                  "setting date for entry 1: ", 
                                  strerror(errno));
@@ -2008,22 +3210,22 @@ int writerror;
            goto common_exit;
        }
        /* set volume labels and dates for backup files */
-       for (vtbl_no = 1; vtbl_no <= num_volumes - 2; vtbl_no++){ 
+       for (vtbl_no = 1; vtbl_no <= num_volumes - 2; vtbl_no++) 
            fprintf(stderr,"taper: label %i: %s, date %s\n", 
                    vtbl_no,
                    vtbl_entry[vtbl_no].label,
                    vtbl_entry[vtbl_no].date);
            fflush(stderr);
-           if(set_label(vtbl_entry[vtbl_no].label, 
-                        volumes, num_volumes, vtbl_no)){
+           if (set_label(vtbl_entry[vtbl_no].label, 
+                        volumes, num_volumes, vtbl_no)) {
                errstr = newstralloc2(errstr,
                                      "setting label for entry i: ", 
                                      strerror(errno));
                rc = 1;
                goto common_exit;
            }
-           if(set_date(vtbl_entry[vtbl_no].date, 
-                       volumes, num_volumes, vtbl_no)){
+           if (set_date(vtbl_entry[vtbl_no].date, 
+                       volumes, num_volumes, vtbl_no)) {
                errstr = newstralloc2(errstr,
                                      "setting date for entry i: ",
                                      strerror(errno));
@@ -2033,7 +3235,7 @@ int writerror;
        }
        /* set volume label and date for last entry */
        vtbl_no = num_volumes - 1;
-       if(set_label("AMANDA Tape End", volumes, num_volumes, vtbl_no)){
+       if (set_label("AMANDA Tape End", volumes, num_volumes, vtbl_no)) {
            errstr = newstralloc2(errstr,
                                  "setting label for last entry: ", 
                                  strerror(errno));
@@ -2041,7 +3243,7 @@ int writerror;
            goto common_exit;
        }
        datestr = NULL; /* take current time */ 
-       if (set_date(datestr, volumes, num_volumes, vtbl_no)){
+       if (set_date(datestr, volumes, num_volumes, vtbl_no)) {
            errstr = newstralloc2(errstr,
                                  "setting date for last entry 1: ", 
                                  strerror(errno));
@@ -2068,7 +3270,7 @@ int writerror;
 
 common_exit:
 
-    if(tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) {
+    if (tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) {
        errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
        rc = 1;
     }
@@ -2079,178 +3281,13 @@ common_exit:
 }
 
 
-int write_filemark()
+int
+write_filemark(void)
 {
-    if(tapefd_weof(tape_fd, 1) == -1) {
+    if (tapefd_weof(tape_fd, (off_t)1) == -1) {
        errstr = newstralloc2(errstr, "writing filemark: ", strerror(errno));
        return 0;
     }
     total_tape_fm++;
     return 1;
 }
-
-
-/*
- * ========================================================================
- * TAPE CHANGER SCAN
- *
- */
-int nslots, backwards, found, got_match, tapedays;
-char *first_match_label = NULL, *first_match = NULL, *found_device = NULL;
-char *searchlabel, *labelstr;
-tape_t *tp;
-
-int scan_init(rc, ns, bk)
-int rc, ns, bk;
-{
-    if(rc) {
-       fprintf(stderr, "%s: could not get changer info: %s\n",
-               get_pname(), changer_resultstr);
-       return rc;
-    }
-
-    nslots = ns;
-    backwards = bk;
-
-    return 0;
-}
-
-int taperscan_slot(rc, slotstr, device)
-     int rc;
-     char *slotstr;
-     char *device;
-{
-    char *t_errstr;
-    char *scan_datestamp = NULL;
-
-    if(rc == 2) {
-       fprintf(stderr, "%s: fatal slot %s: %s\n",
-               get_pname(), slotstr, changer_resultstr);
-       fflush(stderr);
-       return 1;
-    }
-    else if(rc == 1) {
-       fprintf(stderr, "%s: slot %s: %s\n", get_pname(),
-               slotstr, changer_resultstr);
-       fflush(stderr);
-       return 0;
-    }
-    else {
-       if((t_errstr = tape_rdlabel(device, &scan_datestamp, &label)) != NULL) {
-           amfree(scan_datestamp);
-           fprintf(stderr, "%s: slot %s: %s\n",
-                   get_pname(), slotstr, t_errstr);
-           fflush(stderr);
-       }
-       else {
-           /* got an amanda tape */
-           fprintf(stderr, "%s: slot %s: date %-8s label %s",
-                   get_pname(), slotstr, scan_datestamp, label);
-           fflush(stderr);
-           amfree(scan_datestamp);
-           if(searchlabel != NULL
-              && (strcmp(label, FAKE_LABEL) == 0
-                  || strcmp(label, searchlabel) == 0)) {
-               /* it's the one we are looking for, stop here */
-               fprintf(stderr, " (exact label match)\n");
-               fflush(stderr);
-               found_device = newstralloc(found_device, device);
-               found = 1;
-               return 1;
-           }
-           else if(!match(labelstr, label)) {
-               fprintf(stderr, " (no match)\n");
-               fflush(stderr);
-           }
-           else {
-               /* not an exact label match, but a labelstr match */
-               /* check against tape list */
-               tp = lookup_tapelabel(label);
-               if(tp == NULL) {
-                   fprintf(stderr, "(not in tapelist)\n");
-                   fflush(stderr);
-               }
-               else if(!reusable_tape(tp)) {
-                   fprintf(stderr, " (active tape)\n");
-                   fflush(stderr);
-               }
-               else if(got_match == 0 && tp->datestamp == 0) {
-                   got_match = 1;
-                   first_match = newstralloc(first_match, slotstr);
-                   first_match_label = newstralloc(first_match_label, label);
-                   fprintf(stderr, " (new tape)\n");
-                   fflush(stderr);
-                   found = 3;
-                   found_device = newstralloc(found_device, device);
-                   return 1;
-               }
-               else if(got_match) {
-                   fprintf(stderr, " (labelstr match)\n");
-                   fflush(stderr);
-               }
-               else {
-                   got_match = 1;
-                   first_match = newstralloc(first_match, slotstr);
-                   first_match_label = newstralloc(first_match_label, label);
-                   fprintf(stderr, " (first labelstr match)\n");
-                   fflush(stderr);
-                   if(!backwards || !searchlabel) {
-                       found = 2;
-                       found_device = newstralloc(found_device, device);
-                       return 1;
-                   }
-               }
-           }
-       }
-    }
-    return 0;
-}
-
-char *taper_scan()
-{
-    char *outslot = NULL;
-
-    if((tp = lookup_last_reusable_tape(0)) == NULL)
-       searchlabel = NULL;
-    else
-       searchlabel = tp->label;
-
-    found = 0;
-    got_match = 0;
-
-    if (searchlabel != NULL)
-      changer_find(scan_init, taperscan_slot, searchlabel);
-    else
-      changer_scan(scan_init, taperscan_slot);
-
-    if(found == 2 || found == 3)
-       searchlabel = first_match_label;
-    else if(!found && got_match) {
-       searchlabel = first_match_label;
-       amfree(found_device);
-       if(changer_loadslot(first_match, &outslot, &found_device) == 0) {
-           found = 1;
-       }
-       amfree(outslot);
-    }
-    else if(!found) {
-       if(searchlabel) {
-           changer_resultstr = newvstralloc(changer_resultstr,
-                                            "label ", searchlabel,
-                                            " or new tape not found in rack",
-                                            NULL);
-       } else {
-           changer_resultstr = newstralloc(changer_resultstr,
-                                           "new tape not found in rack");
-       }
-    }
-
-    if(found) {
-       outslot = found_device;
-       found_device = NULL;            /* forget about our copy */
-    } else {
-       outslot = NULL;
-       amfree(found_device);
-    }
-    return outslot;
-}