fc783ba23acb5390b6f96da809c41e37037cabd5
[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.25 2006/03/21 13:23:35 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 "token.h"
41 #include "version.h"
42 #include "fileheader.h"
43 #include "amfeatures.h"
44 #include "server_util.h"
45 #include "util.h"
46 #include "holding.h"
47
48 #ifndef SEEK_SET
49 #define SEEK_SET 0
50 #endif
51
52 #ifndef SEEK_CUR
53 #define SEEK_CUR 1
54 #endif
55
56 #define CONNECT_TIMEOUT 5*60
57
58 #define STARTUP_TIMEOUT 60
59
60 struct databuf {
61     int fd;                     /* file to flush to */
62     char *filename;             /* name of what fd points to */
63     int filename_seq;           /* for chunking */
64     long split_size;            /* when to chunk */
65     long chunk_size;            /* size of each chunk */
66     long use;                   /* size to use on this disk */
67     char buf[DISK_BLOCK_BYTES];
68     char *datain;               /* data buffer markers */
69     char *dataout;
70     char *datalimit;
71 };
72
73 static char *handle = NULL;
74
75 static char *errstr = NULL;
76 static int abort_pending;
77 static long dumpsize, headersize;
78 static long dumpbytes;
79 static long filesize;
80
81 static char *hostname = NULL;
82 static char *diskname = NULL;
83 static char *options = NULL;
84 static char *progname = NULL;
85 static int level;
86 static char *dumpdate = NULL;
87 static char *datestamp;
88 static int command_in_transit;
89
90 static dumpfile_t file;
91
92 /* local functions */
93 int main P((int, char **));
94 static int write_tapeheader P((int, dumpfile_t *));
95 static void databuf_init P((struct databuf *, int, char *, long, long));
96 static int databuf_flush P((struct databuf *));
97
98 static int startup_chunker P((char *, long, long, struct databuf *));
99 static int do_chunk P((int, struct databuf *));
100
101
102 int
103 main(main_argc, main_argv)
104     int main_argc;
105     char **main_argv;
106 {
107     static struct databuf db;
108     struct cmdargs cmdargs;
109     cmd_t cmd;
110     int infd;
111     unsigned long malloc_hist_1, malloc_size_1;
112     unsigned long malloc_hist_2, malloc_size_2;
113     char *conffile;
114     char *q = NULL;
115     char *filename;
116     long chunksize, use;
117     times_t runtime;
118     am_feature_t *their_features = NULL;
119     int a;
120
121     safe_fd(-1, 0);
122
123     set_pname("chunker");
124
125     /* Don't die when child closes pipe */
126     signal(SIGPIPE, SIG_IGN);
127
128     malloc_size_1 = malloc_inuse(&malloc_hist_1);
129
130     erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
131     set_logerror(logerror);
132
133     if (main_argc > 1) {
134         config_name = stralloc(main_argv[1]);
135         config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
136     } else {
137         char my_cwd[STR_SIZE];
138
139         if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
140             error("cannot determine current working directory");
141         }
142         config_dir = stralloc2(my_cwd, "/");
143         if ((config_name = strrchr(my_cwd, '/')) != NULL) {
144             config_name = stralloc(config_name + 1);
145         }
146     }
147
148     safe_cd();
149
150     conffile = stralloc2(config_dir, CONFFILE_NAME);
151     if(read_conffile(conffile)) {
152         error("errors processing config file \"%s\"", conffile);
153     }
154     amfree(conffile);
155
156     fprintf(stderr,
157             "%s: pid %ld executable %s version %s\n",
158             get_pname(), (long) getpid(),
159             main_argv[0], version());
160     fflush(stderr);
161
162     /* now, make sure we are a valid user */
163
164     if (getpwuid(getuid()) == NULL)
165         error("can't get login name for my uid %ld", (long)getuid());
166
167     signal(SIGPIPE, SIG_IGN);
168     signal(SIGCHLD, SIG_IGN);
169
170     datestamp = construct_datestamp(NULL);
171
172 /*    do {*/
173         cmd = getcmd(&cmdargs);
174
175         switch(cmd) {
176         case QUIT:
177             break;
178
179         case PORT_WRITE:
180             /*
181              * PORT-WRITE
182              *   handle
183              *   filename
184              *   host
185              *   features
186              *   disk
187              *   level
188              *   dumpdate
189              *   chunksize
190              *   progname
191              *   use
192              *   options
193              */
194             cmdargs.argc++;                     /* true count of args */
195             a = 2;
196
197             if(a >= cmdargs.argc) {
198                 error("error [chunker PORT-WRITE: not enough args: handle]");
199             }
200             handle = newstralloc(handle, cmdargs.argv[a++]);
201
202             if(a >= cmdargs.argc) {
203                 error("error [chunker PORT-WRITE: not enough args: filename]");
204             }
205             filename = cmdargs.argv[a++];
206
207             if(a >= cmdargs.argc) {
208                 error("error [chunker PORT-WRITE: not enough args: hostname]");
209             }
210             hostname = newstralloc(hostname, cmdargs.argv[a++]);
211
212             if(a >= cmdargs.argc) {
213                 error("error [chunker PORT-WRITE: not enough args: features]");
214             }
215             am_release_feature_set(their_features);
216             their_features = am_string_to_feature(cmdargs.argv[a++]);
217
218             if(a >= cmdargs.argc) {
219                 error("error [chunker PORT-WRITE: not enough args: diskname]");
220             }
221             diskname = newstralloc(diskname, cmdargs.argv[a++]);
222
223             if(a >= cmdargs.argc) {
224                 error("error [chunker PORT-WRITE: not enough args: level]");
225             }
226             level = atoi(cmdargs.argv[a++]);
227
228             if(a >= cmdargs.argc) {
229                 error("error [chunker PORT-WRITE: not enough args: dumpdate]");
230             }
231             dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
232
233             if(a >= cmdargs.argc) {
234                 error("error [chunker PORT-WRITE: not enough args: chunksize]");
235             }
236             chunksize = atoi(cmdargs.argv[a++]);
237             chunksize = am_floor(chunksize, DISK_BLOCK_KB);
238
239             if(a >= cmdargs.argc) {
240                 error("error [chunker PORT-WRITE: not enough args: progname]");
241             }
242             progname = newstralloc(progname, cmdargs.argv[a++]);
243
244             if(a >= cmdargs.argc) {
245                 error("error [chunker PORT-WRITE: not enough args: use]");
246             }
247             use = am_floor(atoi(cmdargs.argv[a++]), DISK_BLOCK_KB);
248
249             if(a >= cmdargs.argc) {
250                 error("error [chunker PORT-WRITE: not enough args: options]");
251             }
252             options = newstralloc(options, cmdargs.argv[a++]);
253
254             if(a != cmdargs.argc) {
255                 error("error [chunker PORT-WRITE: too many args: %d != %d]",
256                       cmdargs.argc, a);
257             }
258
259             if((infd = startup_chunker(filename, use, chunksize, &db)) < 0) {
260                 q = squotef("[chunker startup failed: %s]", errstr);
261                 putresult(TRYAGAIN, "%s %s\n", handle, q);
262                 error("startup_chunker failed");
263             }
264             command_in_transit = -1;
265             if(infd >= 0 && do_chunk(infd, &db)) {
266                 char kb_str[NUM_STR_SIZE];
267                 char kps_str[NUM_STR_SIZE];
268                 double rt;
269
270                 runtime = stopclock();
271                 rt = runtime.r.tv_sec+runtime.r.tv_usec/1000000.0;
272                 snprintf(kb_str, sizeof(kb_str), "%ld", dumpsize - headersize);
273                 snprintf(kps_str, sizeof(kps_str), "%3.1f",
274                                 rt ? dumpsize / rt : 0.0);
275                 errstr = newvstralloc(errstr,
276                                       "sec ", walltime_str(runtime),
277                                       " kb ", kb_str,
278                                       " kps ", kps_str,
279                                       NULL);
280                 q = squotef("[%s]", errstr);
281                 if(command_in_transit != -1)
282                     cmd = command_in_transit;
283                 else
284                     cmd = getcmd(&cmdargs);
285                 switch(cmd) {
286                 case DONE:
287                     putresult(DONE, "%s %ld %s\n",
288                               handle, dumpsize - headersize, q);
289                     log_add(L_SUCCESS, "%s %s %s %d [%s]",
290                             hostname, diskname, datestamp, level, errstr);
291                     break;
292                 case BOGUS:
293                 case TRYAGAIN:
294                 case FAILED:
295                 case ABORT_FINISHED:
296                     if(dumpsize > DISK_BLOCK_KB) {
297                         putresult(PARTIAL, "%s %ld %s\n",
298                                   handle, dumpsize - headersize, q);
299                         log_add(L_PARTIAL, "%s %s %s %d [%s]",
300                                 hostname, diskname, datestamp, level, errstr);
301                     }
302                     else {
303                         errstr = newvstralloc(errstr,
304                                               "dumper returned ",
305                                               cmdstr[cmd],
306                                               NULL);
307                         amfree(q);
308                         q = squotef("[%s]",errstr);
309                         putresult(FAILED, "%s %s\n", handle, q);
310                         log_add(L_FAIL, "%s %s %s %d [%s]",
311                                 hostname, diskname, datestamp, level, errstr);
312                     }
313                 default: break;
314                 }
315                 amfree(q);
316             } else if(infd != -2) {
317                 if(!abort_pending) {
318                     if(q == NULL) {
319                         q = squotef("[%s]", errstr);
320                     }
321                     putresult(FAILED, "%s %s\n", handle, q);
322                     log_add(L_FAIL, "%s %s %s %d [%s]",
323                             hostname, diskname, datestamp, level, errstr);
324                     amfree(q);
325                 }
326             }
327             break;
328
329         default:
330             if(cmdargs.argc >= 1) {
331                 q = squote(cmdargs.argv[1]);
332             } else if(cmdargs.argc >= 0) {
333                 q = squote(cmdargs.argv[0]);
334             } else {
335                 q = stralloc("(no input?)");
336             }
337             putresult(BAD_COMMAND, "%s\n", q);
338             amfree(q);
339             break;
340         }
341
342 /*    } while(cmd != QUIT); */
343
344     amfree(errstr);
345     amfree(datestamp);
346     amfree(handle);
347     amfree(hostname);
348     amfree(diskname);
349     amfree(dumpdate);
350     amfree(progname);
351     amfree(options);
352     amfree(config_dir);
353     amfree(config_name);
354     am_release_feature_set(their_features);
355     their_features = NULL;
356
357     malloc_size_2 = malloc_inuse(&malloc_hist_2);
358
359     if (malloc_size_1 != malloc_size_2)
360         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
361
362     exit(0);
363 }
364
365 /*
366  * Returns a file descriptor to the incoming port
367  * on success, or -1 on error.
368  */
369 static int
370 startup_chunker(filename, use, chunksize, db)
371     char *filename;
372     long use;
373     long chunksize;
374     struct databuf *db;
375 {
376     int infd, outfd;
377     char *tmp_filename, *pc;
378     int data_port, data_socket;
379
380     data_port = 0;
381     data_socket = stream_server(&data_port, -1, STREAM_BUFSIZE);
382
383     if(data_socket < 0) {
384         errstr = stralloc2("error creating stream server: ", strerror(errno));
385         return -1;
386     }
387
388     putresult(PORT, "%d\n", data_port);
389
390     infd = stream_accept(data_socket, CONNECT_TIMEOUT, -1, NETWORK_BLOCK_BYTES);
391     if(infd == -1) {
392         errstr = stralloc2("error accepting stream: ", strerror(errno));
393         return -1;
394     }
395
396     tmp_filename = vstralloc(filename, ".tmp", NULL);
397     pc = strrchr(tmp_filename, '/');
398     *pc = '\0';
399     mkholdingdir(tmp_filename);
400     *pc = '/';
401     if ((outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) {
402         int save_errno = errno;
403
404         errstr = squotef("holding file \"%s\": %s",
405                          tmp_filename,
406                          strerror(errno));
407         amfree(tmp_filename);
408         aclose(infd);
409         if(save_errno == ENOSPC) {
410             putresult(NO_ROOM, "%s %lu", handle, use);
411             return -2;
412         } else {
413             return -1;
414         }
415     }
416     amfree(tmp_filename);
417     databuf_init(db, outfd, filename, use, chunksize);
418     db->filename_seq++;
419     return infd;
420 }
421
422 static int
423 do_chunk(infd, db)
424     int infd;
425     struct databuf *db;
426 {
427     int nread;
428     char header_buf[DISK_BLOCK_BYTES];
429
430     startclock();
431
432     dumpsize = headersize = dumpbytes = filesize = 0;
433
434     /*
435      * The first thing we should receive is the file header, which we
436      * need to save into "file", as well as write out.  Later, the
437      * chunk code will rewrite it.
438      */
439     nread = fullread(infd, header_buf, sizeof(header_buf));
440     if (nread != DISK_BLOCK_BYTES) {
441         char number1[NUM_STR_SIZE];
442         char number2[NUM_STR_SIZE];
443
444         if(nread < 0) {
445             errstr = stralloc2("cannot read header: ", strerror(errno));
446         } else {
447             snprintf(number1, sizeof(number1), "%d", nread);
448             snprintf(number2, sizeof(number2), "%d", DISK_BLOCK_BYTES);
449             errstr = vstralloc("cannot read header: got ",
450                                number1,
451                                " instead of ",
452                                number2,
453                                NULL);
454         }
455         return 0;
456     }
457     parse_file_header(header_buf, &file, nread);
458     if(write_tapeheader(db->fd, &file)) {
459         int save_errno = errno;
460
461         errstr = squotef("write_tapeheader file \"%s\": %s",
462                          db->filename, strerror(errno));
463         if(save_errno == ENOSPC) {
464             putresult(NO_ROOM, "%s %lu\n", handle, 
465                       db->use+db->split_size-dumpsize);
466         }
467         return 0;
468     }
469     dumpsize += DISK_BLOCK_KB;
470     filesize = DISK_BLOCK_KB;
471     headersize += DISK_BLOCK_KB;
472
473     /*
474      * We've written the file header.  Now, just write data until the
475      * end.
476      */
477     while ((nread = fullread(infd, db->buf, db->datalimit - db->datain)) > 0) {
478         db->datain += nread;
479         while(db->dataout < db->datain) {
480             if(!databuf_flush(db)) {
481                 return 0;
482             }
483         }
484     }
485     while(db->dataout < db->datain) {
486         if(!databuf_flush(db)) {
487             return 0;
488         }
489     }
490     if(dumpbytes > 0) {
491         dumpsize++;                     /* count partial final KByte */
492         filesize++;
493     }
494     return 1;
495 }
496
497 /*
498  * Initialize a databuf.  Takes a writeable file descriptor.
499  */
500 static void
501 databuf_init(db, fd, filename, use, chunk_size)
502     struct databuf *db;
503     int fd;
504     char *filename;
505     long use;
506     long chunk_size;
507 {
508     db->fd = fd;
509     db->filename = stralloc(filename);
510     db->filename_seq = 0;
511     db->chunk_size = chunk_size;
512     db->split_size = (db->chunk_size > use) ? use : db->chunk_size;
513     db->use = (use>db->split_size) ? use - db->split_size : 0;
514     db->datain = db->dataout = db->buf;
515     db->datalimit = db->buf + sizeof(db->buf);
516 }
517
518
519 /*
520  * Write out the buffer to the backing file
521  */
522 static int
523 databuf_flush(db)
524     struct databuf *db;
525 {
526     struct cmdargs cmdargs;
527     int rc = 1;
528     int written;
529     long left_in_chunk;
530     char *arg_filename = NULL;
531     char *new_filename = NULL;
532     char *tmp_filename = NULL;
533     char sequence[NUM_STR_SIZE];
534     int newfd;
535     filetype_t save_type;
536     char *q;
537     int a;
538     char *pc;
539
540     /*
541      * If there's no data, do nothing.
542      */
543     if (db->dataout >= db->datain) {
544         goto common_exit;
545     }
546
547     /*
548      * See if we need to split this file.
549      */
550     while (db->split_size > 0 && dumpsize >= db->split_size) {
551         if( db->use == 0 ) {
552             /*
553              * Probably no more space on this disk.  Request some more.
554              */
555             cmd_t cmd;
556
557             putresult(RQ_MORE_DISK, "%s\n", handle);
558             cmd = getcmd(&cmdargs);
559             if(command_in_transit == -1 &&
560                (cmd == DONE || cmd == TRYAGAIN || cmd == FAILED)) {
561                 command_in_transit = cmd;
562                 cmd = getcmd(&cmdargs);
563             }
564             if(cmd == CONTINUE) {
565                 /*
566                  * CONTINUE
567                  *   serial
568                  *   filename
569                  *   chunksize
570                  *   use
571                  */
572                 cmdargs.argc++;                 /* true count of args */
573                 a = 3;
574
575                 if(a >= cmdargs.argc) {
576                     error("error [chunker CONTINUE: not enough args: filename]");
577                 }
578                 arg_filename = newstralloc(arg_filename, cmdargs.argv[a++]);
579
580                 if(a >= cmdargs.argc) {
581                     error("error [chunker CONTINUE: not enough args: chunksize]");
582                 }
583                 db->chunk_size = atoi(cmdargs.argv[a++]);
584                 db->chunk_size = am_floor(db->chunk_size, DISK_BLOCK_KB);
585
586                 if(a >= cmdargs.argc) {
587                     error("error [chunker CONTINUE: not enough args: use]");
588                 }
589                 db->use = atoi(cmdargs.argv[a++]);
590
591                 if(a != cmdargs.argc) {
592                     error("error [chunker CONTINUE: too many args: %d != %d]",
593                           cmdargs.argc, a);
594                 }
595
596                 if(strcmp(db->filename, arg_filename) == 0) {
597                     /*
598                      * Same disk, so use what room is left up to the
599                      * next chunk boundary or the amount we were given,
600                      * whichever is less.
601                      */
602                     left_in_chunk = db->chunk_size - filesize;
603                     if(left_in_chunk > db->use) {
604                         db->split_size += db->use;
605                         db->use = 0;
606                     } else {
607                         db->split_size += left_in_chunk;
608                         db->use -= left_in_chunk;
609                     }
610                     if(left_in_chunk > 0) {
611                         /*
612                          * We still have space in this chunk.
613                          */
614                         break;
615                     }
616                 } else {
617                     /*
618                      * Different disk, so use new file.
619                      */
620                     db->filename = newstralloc(db->filename, arg_filename);
621                 }
622             } else if(cmd == ABORT) {
623                 abort_pending = 1;
624                 errstr = newstralloc(errstr, "ERROR");
625                 putresult(ABORT_FINISHED, "%s\n", handle);
626                 rc = 0;
627                 goto common_exit;
628             } else {
629                 if(cmdargs.argc >= 1) {
630                     q = squote(cmdargs.argv[1]);
631                 } else if(cmdargs.argc >= 0) {
632                     q = squote(cmdargs.argv[0]);
633                 } else {
634                     q = stralloc("(no input?)");
635                 }
636                 error("error [bad command after RQ-MORE-DISK: \"%s\"]", q);
637             }
638         }
639
640         /*
641          * Time to use another file.
642          */
643
644         /*
645          * First, open the new chunk file, and give it a new header
646          * that has no cont_filename pointer.
647          */
648         snprintf(sequence, sizeof(sequence), "%d", db->filename_seq);
649         new_filename = newvstralloc(new_filename,
650                                     db->filename,
651                                     ".",
652                                     sequence,
653                                     NULL);
654         tmp_filename = newvstralloc(tmp_filename,
655                                     new_filename,
656                                     ".tmp",
657                                     NULL);
658         pc = strrchr(tmp_filename, '/');
659         *pc = '\0';
660         mkholdingdir(tmp_filename);
661         *pc = '/';
662         newfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
663         if (newfd == -1) {
664             int save_errno = errno;
665
666             if(save_errno == ENOSPC) {
667                 putresult(NO_ROOM, "%s %lu\n",
668                           handle, 
669                           db->use+db->split_size-dumpsize);
670                 db->use = 0;                    /* force RQ_MORE_DISK */
671                 db->split_size = dumpsize;
672                 continue;
673             }
674             errstr = squotef("creating chunk holding file \"%s\": %s",
675                              tmp_filename,
676                              strerror(errno));
677             aclose(db->fd);
678             rc = 0;
679             goto common_exit;
680         }
681         save_type = file.type;
682         file.type = F_CONT_DUMPFILE;
683         file.cont_filename[0] = '\0';
684         if(write_tapeheader(newfd, &file)) {
685             int save_errno = errno;
686
687             aclose(newfd);
688             if(save_errno == ENOSPC) {
689                 putresult(NO_ROOM, "%s %lu\n",
690                           handle, 
691                           db->use+db->split_size-dumpsize);
692                 db->use = 0;                    /* force RQ_MORE DISK */
693                 db->split_size = dumpsize;
694                 continue;
695             }
696             errstr = squotef("write_tapeheader file \"%s\": %s",
697                              tmp_filename,
698                              strerror(errno));
699             rc = 0;
700             goto common_exit;
701         }
702
703         /*
704          * Now, update the header of the current file to point
705          * to the next chunk, and then close it.
706          */
707         if (lseek(db->fd, (off_t)0, SEEK_SET) < 0) {
708             errstr = squotef("lseek holding file \"%s\": %s",
709                              db->filename,
710                              strerror(errno));
711             aclose(newfd);
712             rc = 0;
713             goto common_exit;
714         }
715
716         file.type = save_type;
717         strncpy(file.cont_filename, new_filename, sizeof(file.cont_filename));
718         file.cont_filename[sizeof(file.cont_filename)] = '\0';
719         if(write_tapeheader(db->fd, &file)) {
720             errstr = squotef("write_tapeheader file \"%s\": %s",
721                              db->filename,
722                              strerror(errno));
723             aclose(newfd);
724             unlink(tmp_filename);
725             rc = 0;
726             goto common_exit;
727         }
728         file.type = F_CONT_DUMPFILE;
729
730         /*
731          * Now shift the file descriptor.
732          */
733         aclose(db->fd);
734         db->fd = newfd;
735         newfd = -1;
736
737         /*
738          * Update when we need to chunk again
739          */
740         if(db->use <= DISK_BLOCK_KB) {
741             /*
742              * Cheat and use one more block than allowed so we can make
743              * some progress.
744              */
745             db->split_size += 2 * DISK_BLOCK_KB;
746             db->use = 0;
747         } else if(db->chunk_size > db->use) {
748             db->split_size += db->use;
749             db->use = 0;
750         } else {
751             db->split_size += db->chunk_size;
752             db->use -= db->chunk_size;
753         }
754
755
756         amfree(tmp_filename);
757         amfree(new_filename);
758         dumpsize += DISK_BLOCK_KB;
759         filesize = DISK_BLOCK_KB;
760         headersize += DISK_BLOCK_KB;
761         db->filename_seq++;
762     }
763
764     /*
765      * Write out the buffer
766      */
767     written = fullwrite(db->fd, db->dataout, db->datain - db->dataout);
768     if (written > 0) {
769         db->dataout += written;
770         dumpbytes += written;
771     }
772     dumpsize += (dumpbytes / 1024);
773     filesize += (dumpbytes / 1024);
774     dumpbytes %= 1024;
775     if (written < 0) {
776         if (errno != ENOSPC) {
777             errstr = squotef("data write: %s", strerror(errno));
778             rc = 0;
779             goto common_exit;
780         }
781
782         /*
783          * NO-ROOM is informational only.  Later, RQ_MORE_DISK will be
784          * issued to use another holding disk.
785          */
786         putresult(NO_ROOM, "%s %lu\n", handle, db->use+db->split_size-dumpsize);
787         db->use = 0;                            /* force RQ_MORE_DISK */
788         db->split_size = dumpsize;
789         goto common_exit;
790     }
791     if (db->datain == db->dataout) {
792         /*
793          * We flushed the whole buffer so reset to use it all.
794          */
795         db->datain = db->dataout = db->buf;
796     }
797
798 common_exit:
799
800     amfree(new_filename);
801     amfree(tmp_filename);
802     amfree(arg_filename);
803     return rc;
804 }
805
806
807 /*
808  * Send an Amanda dump header to the output file.
809  */
810 static int
811 write_tapeheader(outfd, file)
812     int outfd;
813     dumpfile_t *file;
814 {
815     char buffer[DISK_BLOCK_BYTES];
816     int written;
817
818     file->blocksize = DISK_BLOCK_BYTES;
819     build_header(buffer, file, sizeof(buffer));
820
821     written = fullwrite(outfd, buffer, sizeof(buffer));
822     if(written == sizeof(buffer)) return 0;
823     if(written < 0) return written;
824     errno = ENOSPC;
825     return -1;
826 }