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