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