Imported Upstream version 3.1.0
[debian/amanda] / server-src / chunker.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /* $Id: chunker.c,v 1.36 2006/08/24 11:23:32 martinea Exp $
27  *
28  * requests remote amandad processes to dump filesystems
29  */
30 #include "amanda.h"
31 #include "arglist.h"
32 #include "clock.h"
33 #include "conffile.h"
34 #include "event.h"
35 #include "logfile.h"
36 #include "packet.h"
37 #include "protocol.h"
38 #include "security.h"
39 #include "stream.h"
40 #include "fileheader.h"
41 #include "amfeatures.h"
42 #include "server_util.h"
43 #include "util.h"
44 #include "holding.h"
45 #include "timestamp.h"
46
47 #define chunker_debug(i, ...) do {      \
48         if ((i) <= debug_chunker) {     \
49             dbprintf(__VA_ARGS__);      \
50         }                               \
51 } while (0)
52
53 #ifndef SEEK_SET
54 #define SEEK_SET 0
55 #endif
56
57 #ifndef SEEK_CUR
58 #define SEEK_CUR 1
59 #endif
60
61 #define CONNECT_TIMEOUT 5*60
62
63 #define STARTUP_TIMEOUT 60
64
65 struct databuf {
66     int fd;                     /* file to flush to */
67     char *filename;             /* name of what fd points to */
68     int filename_seq;           /* for chunking */
69     off_t split_size;           /* when to chunk */
70     off_t chunk_size;           /* size of each chunk */
71     off_t use;                  /* size to use on this disk */
72     char buf[DISK_BLOCK_BYTES];
73     char *datain;               /* data buffer markers */
74     char *dataout;
75     char *datalimit;
76 };
77
78 static char *handle = NULL;
79
80 static char *errstr = NULL;
81 static int abort_pending;
82 static off_t dumpsize;
83 static unsigned long headersize;
84 static off_t dumpbytes;
85 static off_t filesize;
86
87 static char *hostname = NULL;
88 static char *diskname = NULL;
89 static char *qdiskname = NULL;
90 static char *options = NULL;
91 static char *progname = NULL;
92 static int level;
93 static char *dumpdate = NULL;
94 static struct cmdargs *command_in_transit = NULL;
95 static char *chunker_timestamp = NULL;
96
97 static dumpfile_t file;
98
99 /* local functions */
100 int main(int, char **);
101 static ssize_t write_tapeheader(int, dumpfile_t *);
102 static void databuf_init(struct databuf *, int, char *, off_t, off_t);
103 static int databuf_flush(struct databuf *);
104
105 static int startup_chunker(char *, off_t, off_t, struct databuf *, int *);
106 static int do_chunk(int, struct databuf *, int);
107
108
109 int
110 main(
111     int         argc,
112     char **     argv)
113 {
114     static struct databuf db;
115     struct cmdargs *cmdargs;
116     int header_fd;
117     char *q = NULL;
118     char *filename = NULL;
119     off_t chunksize, use;
120     times_t runtime;
121     am_feature_t *their_features = NULL;
122     int a;
123     config_overrides_t *cfg_ovr = NULL;
124     char *cfg_opt = NULL;
125     char *m;
126     int data_socket;
127
128     /*
129      * Configure program for internationalization:
130      *   1) Only set the message locale for now.
131      *   2) Set textdomain for all amanda related programs to "amanda"
132      *      We don't want to be forced to support dozens of message catalogs.
133      */  
134     setlocale(LC_MESSAGES, "C");
135     textdomain("amanda"); 
136
137     safe_fd(-1, 0);
138
139     set_pname("chunker");
140
141     dbopen(DBG_SUBDIR_SERVER);
142
143     /* Don't die when child closes pipe */
144     signal(SIGPIPE, SIG_IGN);
145
146     add_amanda_log_handler(amanda_log_stderr);
147     add_amanda_log_handler(amanda_log_trace_log);
148
149     cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
150
151     if (argc > 1) 
152         cfg_opt = argv[1];
153
154     set_config_overrides(cfg_ovr);
155     config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
156
157     if (config_errors(NULL) >= CFGERR_WARNINGS) {
158         config_print_errors();
159         if (config_errors(NULL) >= CFGERR_ERRORS) {
160             g_critical(_("errors processing config file"));
161         }
162     }
163
164     safe_cd(); /* do this *after* config_init() */
165
166     check_running_as(RUNNING_AS_DUMPUSER);
167
168     dbrename(get_config_name(), DBG_SUBDIR_SERVER);
169
170     log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
171     g_fprintf(stderr,
172             _("%s: pid %ld executable %s version %s\n"),
173             get_pname(), (long) getpid(),
174             argv[0], VERSION);
175     fflush(stderr);
176
177     /* now, make sure we are a valid user */
178
179     signal(SIGPIPE, SIG_IGN);
180     signal(SIGCHLD, SIG_IGN);
181
182     cmdargs = getcmd();
183     if(cmdargs->cmd == START) {
184         if(cmdargs->argc <= 1)
185             error(_("error [dumper START: not enough args: timestamp]"));
186         chunker_timestamp = newstralloc(chunker_timestamp, cmdargs->argv[1]);
187     }
188     else {
189         log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
190         error(_("Didn't get START command"));
191     }
192
193 /*    do {*/
194         cmdargs = getcmd();
195
196         switch(cmdargs->cmd) {
197         case QUIT:
198             break;
199
200         case PORT_WRITE:
201             /*
202              * PORT-WRITE
203              *   handle
204              *   filename
205              *   host
206              *   features
207              *   disk
208              *   level
209              *   dumpdate
210              *   chunksize
211              *   progname
212              *   use
213              *   options
214              */
215             a = 1;
216
217             if(a >= cmdargs->argc) {
218                 error(_("error [chunker PORT-WRITE: not enough args: handle]"));
219                 /*NOTREACHED*/
220             }
221             handle = newstralloc(handle, cmdargs->argv[a++]);
222
223             if(a >= cmdargs->argc) {
224                 error(_("error [chunker PORT-WRITE: not enough args: filename]"));
225                 /*NOTREACHED*/
226             }
227             filename = newstralloc(filename, cmdargs->argv[a++]);
228
229             if(a >= cmdargs->argc) {
230                 error(_("error [chunker PORT-WRITE: not enough args: hostname]"));
231                 /*NOTREACHED*/
232             }
233             hostname = newstralloc(hostname, cmdargs->argv[a++]);
234
235             if(a >= cmdargs->argc) {
236                 error(_("error [chunker PORT-WRITE: not enough args: features]"));
237                 /*NOTREACHED*/
238             }
239             am_release_feature_set(their_features);
240             their_features = am_string_to_feature(cmdargs->argv[a++]);
241             if (!their_features) {
242                 error(_("error [chunker PORT-WRITE: invalid feature string]"));
243                 /*NOTREACHED*/
244             }
245
246             if(a >= cmdargs->argc) {
247                 error(_("error [chunker PORT-WRITE: not enough args: diskname]"));
248                 /*NOTREACHED*/
249             }
250             diskname = newstralloc(diskname, cmdargs->argv[a++]);
251             if (qdiskname)
252                 amfree(qdiskname);
253             qdiskname = quote_string(diskname); /* qdiskname is a global */
254
255             if(a >= cmdargs->argc) {
256                 error(_("error [chunker PORT-WRITE: not enough args: level]"));
257                 /*NOTREACHED*/
258             }
259             level = atoi(cmdargs->argv[a++]);
260
261             if(a >= cmdargs->argc) {
262                 error(_("error [chunker PORT-WRITE: not enough args: dumpdate]"));
263                 /*NOTREACHED*/
264             }
265             dumpdate = newstralloc(dumpdate, cmdargs->argv[a++]);
266
267             if(a >= cmdargs->argc) {
268                 error(_("error [chunker PORT-WRITE: not enough args: chunksize]"));
269                 /*NOTREACHED*/
270             }
271             chunksize = OFF_T_ATOI(cmdargs->argv[a++]);
272             chunksize = am_floor(chunksize, (off_t)DISK_BLOCK_KB);
273
274             if(a >= cmdargs->argc) {
275                 error(_("error [chunker PORT-WRITE: not enough args: progname]"));
276                 /*NOTREACHED*/
277             }
278             progname = newstralloc(progname, cmdargs->argv[a++]);
279
280             if(a >= cmdargs->argc) {
281                 error(_("error [chunker PORT-WRITE: not enough args: use]"));
282                 /*NOTREACHED*/
283             }
284             use = am_floor(OFF_T_ATOI(cmdargs->argv[a++]), DISK_BLOCK_KB);
285
286             if(a >= cmdargs->argc) {
287                 error(_("error [chunker PORT-WRITE: not enough args: options]"));
288                 /*NOTREACHED*/
289             }
290             options = newstralloc(options, cmdargs->argv[a++]);
291
292             if(a != cmdargs->argc) {
293                 error(_("error [chunker PORT-WRITE: too many args: %d != %d]"),
294                       cmdargs->argc, a);
295                 /*NOTREACHED*/
296             }
297
298             if ((header_fd = startup_chunker(filename, use, chunksize, &db,
299                                              &data_socket)) < 0) {
300                 q = quote_string(vstrallocf(_("[chunker startup failed: %s]"), errstr));
301                 putresult(TRYAGAIN, "%s %s\n", handle, q);
302                 error("startup_chunker failed: %s", errstr);
303             }
304             command_in_transit = NULL;
305             if (header_fd >= 0 && do_chunk(header_fd, &db, data_socket)) {
306                 char kb_str[NUM_STR_SIZE];
307                 char kps_str[NUM_STR_SIZE];
308                 double rt;
309
310                 runtime = stopclock();
311                 rt = g_timeval_to_double(runtime);
312                 g_snprintf(kb_str, SIZEOF(kb_str), "%lld",
313                          (long long)(dumpsize - (off_t)headersize));
314                 g_snprintf(kps_str, SIZEOF(kps_str), "%3.1lf",
315                                 isnormal(rt) ? (double)dumpsize / rt : 0.0);
316                 errstr = newvstrallocf(errstr, "sec %s kb %s kps %s",
317                                 walltime_str(runtime), kb_str, kps_str);
318                 m = vstrallocf("[%s]", errstr);
319                 q = quote_string(m);
320                 amfree(m);
321                 if(command_in_transit != NULL) {
322                     cmdargs = command_in_transit;
323                     command_in_transit = NULL;
324                 } else {
325                     cmdargs = getcmd();
326                 }
327                 switch(cmdargs->cmd) {
328                 case DONE:
329                     putresult(DONE, "%s %lld %s\n", handle,
330                              (long long)(dumpsize - (off_t)headersize), q);
331                     log_add(L_SUCCESS, "%s %s %s %d [%s]",
332                             hostname, qdiskname, chunker_timestamp, level, errstr);
333                     break;
334                 case BOGUS:
335                 case TRYAGAIN:
336                 case FAILED:
337                 case ABORT_FINISHED:
338                     if(dumpsize > (off_t)DISK_BLOCK_KB) {
339                         putresult(PARTIAL, "%s %lld %s\n", handle,
340                                  (long long)(dumpsize - (off_t)headersize),
341                                  q);
342                         log_add(L_PARTIAL, "%s %s %s %d [%s]",
343                                 hostname, qdiskname, chunker_timestamp, level, errstr);
344                     }
345                     else {
346                         errstr = newvstrallocf(errstr,
347                                         _("dumper returned %s"), cmdstr[cmdargs->cmd]);
348                         amfree(q);
349                         m = vstrallocf("[%s]",errstr);
350                         q = quote_string(m);
351                         amfree(m);
352                         putresult(FAILED, "%s %s\n", handle, q);
353                         log_add(L_FAIL, "%s %s %s %d [%s]",
354                                 hostname, qdiskname, chunker_timestamp, level, errstr);
355                     }
356                 default: break;
357                 }
358                 amfree(q);
359             } else if (header_fd != -2) {
360                 if(q == NULL) {
361                     m = vstrallocf("[%s]", errstr);
362                     q = quote_string(m);
363                     amfree(m);
364                 }
365                 if(!abort_pending) {
366                     putresult(FAILED, "%s %s\n", handle, q);
367                 }
368                 log_add(L_FAIL, "%s %s %s %d [%s]",
369                         hostname, qdiskname, chunker_timestamp, level, errstr);
370                 amfree(q);
371             }
372             amfree(filename);
373             amfree(db.filename);
374             break;
375
376         default:
377             if(cmdargs->argc >= 1) {
378                 q = quote_string(cmdargs->argv[0]);
379             } else {
380                 q = stralloc(_("(no input?)"));
381             }
382             putresult(BAD_COMMAND, "%s\n", q);
383             amfree(q);
384             break;
385         }
386
387 /*    } while(cmdargs->cmd != QUIT); */
388
389     log_add(L_INFO, "pid-done %ld", (long)getpid());
390
391     amfree(errstr);
392     amfree(chunker_timestamp);
393     amfree(handle);
394     amfree(hostname);
395     amfree(diskname);
396     amfree(qdiskname);
397     amfree(dumpdate);
398     amfree(progname);
399     amfree(options);
400     free_cmdargs(cmdargs);
401     if (command_in_transit)
402         free_cmdargs(command_in_transit);
403     am_release_feature_set(their_features);
404     their_features = NULL;
405
406     dbclose();
407
408     return (0); /* exit */
409 }
410
411 /*
412  * Returns a file descriptor to the incoming port
413  * on success, or -1 on error.
414  */
415 static int
416 startup_chunker(
417     char *              filename,
418     off_t               use,
419     off_t               chunksize,
420     struct databuf *    db,
421     int                *datasocket)
422 {
423     int header_fd, outfd;
424     char *tmp_filename, *pc;
425     in_port_t header_port, data_port;
426     int header_socket, data_socket;
427     int result;
428     struct addrinfo *res;
429
430     header_port = 0;
431     data_port = 0;
432     if ((result = resolve_hostname("localhost", 0, &res, NULL) != 0)) {
433         errstr = newvstrallocf(errstr, _("could not resolve localhost: %s"),
434                                gai_strerror(result));
435         return -1;
436     }
437     header_socket = stream_server(res->ai_family, &header_port, 0,
438                                 STREAM_BUFSIZE, 0);
439     data_socket = stream_server(res->ai_family, &data_port, 0,
440                                 STREAM_BUFSIZE, 0);
441     if (res) freeaddrinfo(res);
442
443     if (header_socket < 0) {
444         errstr = vstrallocf(_("error creating header stream server: %s"), strerror(errno));
445         aclose(data_socket);
446         return -1;
447     }
448
449     if (data_socket < 0) {
450         errstr = vstrallocf(_("error creating data stream server: %s"), strerror(errno));
451         aclose(header_socket);
452         return -1;
453     }
454
455     putresult(PORT, "%d 127.0.0.1:%d\n", header_port, data_port);
456
457     header_fd = stream_accept(header_socket, CONNECT_TIMEOUT, 0,
458                               STREAM_BUFSIZE);
459     if (header_fd == -1) {
460         errstr = vstrallocf(_("error accepting header stream: %s"),
461                             strerror(errno));
462         aclose(header_socket);
463         aclose(data_socket);
464         return -1;
465     }
466     aclose(header_socket);
467
468     tmp_filename = vstralloc(filename, ".tmp", NULL);
469     pc = strrchr(tmp_filename, '/');
470     g_assert(pc != NULL);
471     *pc = '\0';
472     mkholdingdir(tmp_filename);
473     *pc = '/';
474     if ((outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) {
475         int save_errno = errno;
476         char *m = vstrallocf(_("holding file \"%s\": %s"),
477                          tmp_filename,
478                          strerror(errno));
479
480         errstr = quote_string(m);
481         amfree(m);
482         amfree(tmp_filename);
483         aclose(header_fd);
484         aclose(data_socket);
485         if(save_errno == ENOSPC) {
486             putresult(NO_ROOM, "%s %lld\n",
487                       handle, (long long)use);
488             return -2;
489         } else {
490             return -1;
491         }
492     }
493     amfree(tmp_filename);
494     databuf_init(db, outfd, filename, use, chunksize);
495     db->filename_seq++;
496     *datasocket = data_socket;
497     return header_fd;
498 }
499
500 static int
501 do_chunk(
502     int                 header_fd,
503     struct databuf *    db,
504     int                 data_socket)
505 {
506     size_t nread;
507     int    data_fd;
508     char header_buf[DISK_BLOCK_BYTES];
509
510     startclock();
511
512     dumpsize = dumpbytes = filesize = (off_t)0;
513     headersize = 0;
514     memset(header_buf, 0, sizeof(header_buf));
515
516     /*
517      * The first thing we should receive is the file header, which we
518      * need to save into "file", as well as write out.  Later, the
519      * chunk code will rewrite it.
520      */
521     nread = full_read(header_fd, header_buf, SIZEOF(header_buf));
522     if (nread != sizeof(header_buf)) {
523         if(errno != 0) {
524             errstr = vstrallocf(_("cannot read header: %s"), strerror(errno));
525         } else {
526             errstr = vstrallocf(_("cannot read header: got %zd bytes instead of %zd"),
527                                 nread, sizeof(header_buf));
528         }
529         aclose(data_socket);
530         return 0;
531     }
532     parse_file_header(header_buf, &file, (size_t)nread);
533     if(write_tapeheader(db->fd, &file)) {
534         int save_errno = errno;
535         char *m = vstrallocf(_("write_tapeheader file %s: %s"),
536                          db->filename, strerror(errno));
537         errstr = quote_string(m);
538         amfree(m);
539         if(save_errno == ENOSPC) {
540             putresult(NO_ROOM, "%s %lld\n", handle, 
541                       (long long)(db->use+db->split_size-dumpsize));
542         }
543         aclose(data_socket);
544         return 0;
545     }
546     dumpsize += (off_t)DISK_BLOCK_KB;
547     filesize = (off_t)DISK_BLOCK_KB;
548     headersize += DISK_BLOCK_KB;
549
550     /* open the data socket */
551     data_fd = stream_accept(data_socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE);
552
553     if (data_fd == -1) {
554         errstr = vstrallocf(_("error accepting data stream: %s"),
555                             strerror(errno));
556         aclose(data_socket);
557         return 0;
558     }
559     aclose(data_socket);
560
561     /*
562      * We've written the file header.  Now, just write data until the
563      * end.
564      */
565     while ((nread = full_read(data_fd, db->buf,
566                              (size_t)(db->datalimit - db->datain))) > 0) {
567         db->datain += nread;
568         while(db->dataout < db->datain) {
569             if(!databuf_flush(db)) {
570                 return 0;
571             }
572         }
573     }
574     while(db->dataout < db->datain) {
575         if(!databuf_flush(db)) {
576             return 0;
577         }
578     }
579     if(dumpbytes > (off_t)0) {
580         dumpsize += (off_t)1;                   /* count partial final KByte */
581         filesize += (off_t)1;
582     }
583     return 1;
584 }
585
586 /*
587  * Initialize a databuf.  Takes a writeable file descriptor.
588  */
589 static void
590 databuf_init(
591     struct databuf *    db,
592     int                 fd,
593     char *              filename,
594     off_t               use,
595     off_t               chunk_size)
596 {
597     db->fd = fd;
598     db->filename = stralloc(filename);
599     db->filename_seq = (off_t)0;
600     db->chunk_size = chunk_size;
601     db->split_size = (db->chunk_size > use) ? use : db->chunk_size;
602     db->use = (use > db->split_size) ? use - db->split_size : (off_t)0;
603     db->datain = db->dataout = db->buf;
604     db->datalimit = db->buf + SIZEOF(db->buf);
605 }
606
607
608 /*
609  * Write out the buffer to the backing file
610  */
611 static int
612 databuf_flush(
613     struct databuf *    db)
614 {
615     struct cmdargs *cmdargs = NULL;
616     int rc = 1;
617     size_t size_to_write;
618     size_t written;
619     off_t left_in_chunk;
620     char *arg_filename = NULL;
621     char *new_filename = NULL;
622     char *tmp_filename = NULL;
623     char sequence[NUM_STR_SIZE];
624     int newfd;
625     filetype_t save_type;
626     char *q;
627     int a;
628     char *pc;
629
630     /*
631      * If there's no data, do nothing.
632      */
633     if (db->dataout >= db->datain) {
634         goto common_exit;
635     }
636
637     /*
638      * See if we need to split this file.
639      */
640     while (db->split_size > (off_t)0 && dumpsize >= db->split_size) {
641         if( db->use == (off_t)0 ) {
642             /*
643              * Probably no more space on this disk.  Request some more.
644              */
645             putresult(RQ_MORE_DISK, "%s\n", handle);
646             cmdargs = getcmd();
647             if(command_in_transit == NULL &&
648                (cmdargs->cmd == DONE || cmdargs->cmd == TRYAGAIN || cmdargs->cmd == FAILED)) {
649                 command_in_transit = cmdargs;
650                 cmdargs = getcmd();
651             }
652             if(cmdargs->cmd == CONTINUE) {
653                 /*
654                  * CONTINUE
655                  *   serial
656                  *   filename
657                  *   chunksize
658                  *   use
659                  */
660                 a = 2; /* skip CONTINUE and serial */
661
662                 if(a >= cmdargs->argc) {
663                     error(_("error [chunker CONTINUE: not enough args: filename]"));
664                     /*NOTREACHED*/
665                 }
666                 arg_filename = newstralloc(arg_filename, cmdargs->argv[a++]);
667
668                 if(a >= cmdargs->argc) {
669                     error(_("error [chunker CONTINUE: not enough args: chunksize]"));
670                     /*NOTREACHED*/
671                 }
672                 db->chunk_size = OFF_T_ATOI(cmdargs->argv[a++]);
673                 db->chunk_size = am_floor(db->chunk_size, (off_t)DISK_BLOCK_KB);
674
675                 if(a >= cmdargs->argc) {
676                     error(_("error [chunker CONTINUE: not enough args: use]"));
677                     /*NOTREACHED*/
678                 }
679                 db->use = OFF_T_ATOI(cmdargs->argv[a++]);
680
681                 if(a != cmdargs->argc) {
682                     error(_("error [chunker CONTINUE: too many args: %d != %d]"),
683                           cmdargs->argc, a);
684                     /*NOTREACHED*/
685                 }
686
687                 if(strcmp(db->filename, arg_filename) == 0) {
688                     /*
689                      * Same disk, so use what room is left up to the
690                      * next chunk boundary or the amount we were given,
691                      * whichever is less.
692                      */
693                     left_in_chunk = db->chunk_size - filesize;
694                     if(left_in_chunk > db->use) {
695                         db->split_size += db->use;
696                         db->use = (off_t)0;
697                     } else {
698                         db->split_size += left_in_chunk;
699                         db->use -= left_in_chunk;
700                     }
701                     if(left_in_chunk > (off_t)0) {
702                         /*
703                          * We still have space in this chunk.
704                          */
705                         break;
706                     }
707                 } else {
708                     /*
709                      * Different disk, so use new file.
710                      */
711                     db->filename = newstralloc(db->filename, arg_filename);
712                 }
713             } else if(cmdargs->cmd == ABORT) {
714                 abort_pending = 1;
715                 errstr = newstralloc(errstr, cmdargs->argv[1]);
716                 putresult(ABORT_FINISHED, "%s\n", handle);
717                 rc = 0;
718                 goto common_exit;
719             } else {
720                 if(cmdargs->argc >= 1) {
721                     q = quote_string(cmdargs->argv[0]);
722                 } else {
723                     q = stralloc(_("(no input?)"));
724                 }
725                 error(_("error [bad command after RQ-MORE-DISK: \"%s\"]"), q);
726                 /*NOTREACHED*/
727             }
728         }
729
730         /*
731          * Time to use another file.
732          */
733
734         /*
735          * First, open the new chunk file, and give it a new header
736          * that has no cont_filename pointer.
737          */
738         g_snprintf(sequence, SIZEOF(sequence), "%d", db->filename_seq);
739         new_filename = newvstralloc(new_filename,
740                                     db->filename,
741                                     ".",
742                                     sequence,
743                                     NULL);
744         tmp_filename = newvstralloc(tmp_filename,
745                                     new_filename,
746                                     ".tmp",
747                                     NULL);
748         pc = strrchr(tmp_filename, '/');
749         g_assert(pc != NULL); /* Only a problem if db->filename has no /. */
750         *pc = '\0';
751         mkholdingdir(tmp_filename);
752         *pc = '/';
753         newfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
754         if (newfd == -1) {
755             int save_errno = errno;
756             char *m;
757
758             if(save_errno == ENOSPC) {
759                 putresult(NO_ROOM, "%s %lld\n", handle, 
760                           (long long)(db->use+db->split_size-dumpsize));
761                 db->use = (off_t)0;                     /* force RQ_MORE_DISK */
762                 db->split_size = dumpsize;
763                 continue;
764             }
765             m = vstrallocf(_("creating chunk holding file \"%s\": %s"),
766                              tmp_filename,
767                              strerror(errno));
768             errstr = quote_string(m);
769             amfree(m);
770             aclose(db->fd);
771             rc = 0;
772             goto common_exit;
773         }
774         save_type = file.type;
775         file.type = F_CONT_DUMPFILE;
776         file.cont_filename[0] = '\0';
777         if(write_tapeheader(newfd, &file)) {
778             int save_errno = errno;
779             char *m;
780
781             aclose(newfd);
782             if(save_errno == ENOSPC) {
783                 putresult(NO_ROOM, "%s %lld\n", handle, 
784                           (long long)(db->use+db->split_size-dumpsize));
785                 db->use = (off_t)0;                     /* force RQ_MORE DISK */
786                 db->split_size = dumpsize;
787                 continue;
788             }
789             m = vstrallocf(_("write_tapeheader file %s: %s"),
790                              tmp_filename,
791                              strerror(errno));
792             errstr = quote_string(m);
793             amfree(m);
794             rc = 0;
795             goto common_exit;
796         }
797
798         /*
799          * Now, update the header of the current file to point
800          * to the next chunk, and then close it.
801          */
802         if (lseek(db->fd, (off_t)0, SEEK_SET) < (off_t)0) {
803             char *m = vstrallocf(_("lseek holding file %s: %s"),
804                              db->filename,
805                              strerror(errno));
806             errstr = quote_string(m);
807             amfree(m);
808             aclose(newfd);
809             rc = 0;
810             goto common_exit;
811         }
812
813         file.type = save_type;
814         strncpy(file.cont_filename, new_filename, SIZEOF(file.cont_filename));
815         file.cont_filename[SIZEOF(file.cont_filename)-1] = '\0';
816         if(write_tapeheader(db->fd, &file)) {
817             char * m = vstrallocf(_("write_tapeheader file \"%s\": %s"),
818                              db->filename,
819                              strerror(errno));
820             errstr = quote_string(m);
821             amfree(m);
822             aclose(newfd);
823             unlink(tmp_filename);
824             rc = 0;
825             goto common_exit;
826         }
827         file.type = F_CONT_DUMPFILE;
828
829         /*
830          * Now shift the file descriptor.
831          */
832         aclose(db->fd);
833         db->fd = newfd;
834         newfd = -1;
835
836         /*
837          * Update when we need to chunk again
838          */
839         if(db->use <= (off_t)DISK_BLOCK_KB) {
840             /*
841              * Cheat and use one more block than allowed so we can make
842              * some progress.
843              */
844             db->split_size += (off_t)(2 * DISK_BLOCK_KB);
845             db->use = (off_t)0;
846         } else if(db->chunk_size > db->use) {
847             db->split_size += db->use;
848             db->use = (off_t)0;
849         } else {
850             db->split_size += db->chunk_size;
851             db->use -= db->chunk_size;
852         }
853
854
855         amfree(tmp_filename);
856         amfree(new_filename);
857         dumpsize += (off_t)DISK_BLOCK_KB;
858         filesize = (off_t)DISK_BLOCK_KB;
859         headersize += DISK_BLOCK_KB;
860         db->filename_seq++;
861     }
862
863     /*
864      * Write out the buffer
865      */
866     size_to_write = (size_t)(db->datain - db->dataout);
867     written = full_write(db->fd, db->dataout, size_to_write);
868     if (written > 0) {
869         db->dataout += written;
870         dumpbytes += (off_t)written;
871     }
872     dumpsize += (dumpbytes / (off_t)1024);
873     filesize += (dumpbytes / (off_t)1024);
874     dumpbytes %= 1024;
875     if (written < size_to_write) {
876         if (errno != ENOSPC) {
877             char *m = vstrallocf(_("data write: %s"), strerror(errno));
878             errstr = quote_string(m);
879             amfree(m);
880             rc = 0;
881             goto common_exit;
882         }
883
884         /*
885          * NO-ROOM is informational only.  Later, RQ_MORE_DISK will be
886          * issued to use another holding disk.
887          */
888         putresult(NO_ROOM, "%s %lld\n", handle,
889                   (long long)(db->use+db->split_size-dumpsize));
890         db->use = (off_t)0;                             /* force RQ_MORE_DISK */
891         db->split_size = dumpsize;
892         goto common_exit;
893     }
894     if (db->datain == db->dataout) {
895         /*
896          * We flushed the whole buffer so reset to use it all.
897          */
898         db->datain = db->dataout = db->buf;
899     }
900
901 common_exit:
902
903     if (cmdargs)
904         free_cmdargs(cmdargs);
905     amfree(new_filename);
906     /*@i@*/ amfree(tmp_filename);
907     amfree(arg_filename);
908     return rc;
909 }
910
911
912 /*
913  * Send an Amanda dump header to the output file and set file->blocksize
914  */
915 static ssize_t
916 write_tapeheader(
917     int         outfd,
918     dumpfile_t *file)
919 {
920     char *buffer;
921     size_t written;
922
923     file->blocksize = DISK_BLOCK_BYTES;
924     if (debug_chunker > 1)
925         dump_dumpfile_t(file);
926     buffer = build_header(file, NULL, DISK_BLOCK_BYTES);
927     if (!buffer) /* this shouldn't happen */
928         error(_("header does not fit in %zd bytes"), (size_t)DISK_BLOCK_BYTES);
929
930     written = full_write(outfd, buffer, DISK_BLOCK_BYTES);
931     amfree(buffer);
932     if(written == DISK_BLOCK_BYTES) return 0;
933
934     /* fake ENOSPC when we get a short write without errno set */
935     if(errno == 0)
936         errno = ENOSPC;
937
938     return (ssize_t)-1;
939 }