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