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