]> git.wh0rd.org Git - dump.git/blob - dump/tape.c
Andreas Dilger fixes (CPP, ext2 features, const char etc).
[dump.git] / dump / tape.c
1 /*
2  *      Ported to Linux's Second Extended File System as part of the
3  *      dump and restore backup suit
4  *      Remy Card <card@Linux.EU.Org>, 1994-1997
5  *      Stelian Pop <pop@noos.fr>, 1999-2000
6  *      Stelian Pop <pop@noos.fr> - AlcĂ´ve <www.alcove.fr>, 2000
7  */
8
9 /*-
10  * Copyright (c) 1980, 1991, 1993
11  *      The Regents of the University of California.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  */
41
42 #ifndef lint
43 static const char rcsid[] =
44         "$Id: tape.c,v 1.34 2001/03/19 13:22:48 stelian Exp $";
45 #endif /* not lint */
46
47 #include <config.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <setjmp.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <compaterr.h>
54 #ifdef __STDC__
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 #else
59 int    write(), read();
60 #endif
61
62 #ifdef __linux__
63 #include <sys/types.h>
64 #include <time.h>
65 #include <linux/types.h>
66 #endif
67 #include <sys/param.h>
68 #include <sys/socket.h>
69 #include <sys/time.h>
70 #include <sys/wait.h>
71 #ifdef __linux__
72 #include <linux/ext2_fs.h>
73 #include <ext2fs/ext2fs.h>
74 #include <bsdcompat.h>
75 #elif defined sunos
76 #include <sys/vnode.h>
77
78 #include <ufs/fs.h>
79 #include <ufs/inode.h>
80 #else
81 #include <ufs/ufs/dinode.h>
82 #include <ufs/ffs/fs.h>
83 #endif  /* __linux__ */
84
85 #include <protocols/dumprestore.h>
86
87 #ifdef HAVE_ZLIB
88 #include <zlib.h>
89 #endif /* HAVE_ZLIB */
90
91 #include "dump.h"
92
93 int     writesize;              /* size of malloc()ed buffer for tape */
94 long    lastspclrec = -1;       /* tape block number of last written header */
95 int     trecno = 0;             /* next record to write in current block */
96 extern  long blocksperfile;     /* number of blocks per output file */
97 long    blocksthisvol;          /* number of blocks on current output file */
98 extern  int ntrec;              /* blocking factor on tape */
99 extern  int cartridge;
100 extern  char *host;
101 char    *nexttape;
102 extern  pid_t rshpid;
103 int     eot_code = 1;
104 long long tapea_bytes = 0;      /* bytes_written at start of current volume */
105
106 static  ssize_t atomic_read __P((int, void *, size_t));
107 static  ssize_t atomic_write __P((int, const void *, size_t));
108 static  void doslave __P((int, int));
109 static  void enslave __P((void));
110 static  void flushtape __P((void));
111 static  void killall __P((void));
112 static  void rollforward __P((void));
113 static  int system_command __P((const char *, const char *, int));
114
115 /*
116  * Concurrent dump mods (Caltech) - disk block reading and tape writing
117  * are exported to several slave processes.  While one slave writes the
118  * tape, the others read disk blocks; they pass control of the tape in
119  * a ring via signals. The parent process traverses the filesystem and
120  * sends writeheader()'s and lists of daddr's to the slaves via pipes.
121  * The following structure defines the instruction packets sent to slaves.
122  */
123 struct req {
124         daddr_t dblk;
125         int count;
126 };
127 int reqsiz;
128
129 struct slave_results {
130         ssize_t unclen;         /* uncompressed length */
131         ssize_t clen;           /* compressed length */
132 };
133
134 #define SLAVES 3                /* 1 slave writing, 1 reading, 1 for slack */
135 struct slave {
136         int tapea;              /* header number at start of this chunk */
137         int count;              /* count to next header (used for TS_TAPE */
138                                 /* after EOT) */
139         int inode;              /* inode that we are currently dealing with */
140         int fd;                 /* FD for this slave */
141         int pid;                /* PID for this slave */
142         int sent;               /* 1 == we've sent this slave requests */
143         int firstrec;           /* record number of this block */
144         char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
145         struct req *req;        /* buffer for requests */
146 } slaves[SLAVES+1];
147 struct slave *slp;
148
149 char    (*nextblock)[TP_BSIZE];
150
151 static time_t tstart_volume;    /* time of volume start */ 
152 static int tapea_volume;        /* value of spcl.c_tapea at volume start */
153
154 int master;             /* pid of master, for sending error signals */
155 int tenths;             /* length of tape overhead per block written */
156 static int caught;      /* have we caught the signal to proceed? */
157 static int ready;       /* have we reached the lock point without having */
158                         /* received the SIGUSR2 signal from the prev slave? */
159 static sigjmp_buf jmpbuf;       /* where to jump to if we are ready when the */
160                         /* SIGUSR2 arrives from the previous slave */
161
162 int
163 alloctape(void)
164 {
165         int pgoff = getpagesize() - 1;
166         char *buf;
167         int i;
168
169         writesize = ntrec * TP_BSIZE;
170         reqsiz = (ntrec + 1) * sizeof(struct req);
171         /*
172          * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
173          * (see DEC TU80 User's Guide).  The shorter gaps of 6250-bpi require
174          * repositioning after stopping, i.e, streaming mode, where the gap is
175          * variable, 0.30" to 0.45".  The gap is maximal when the tape stops.
176          */
177         if (blocksperfile == 0 && !unlimited)
178                 tenths = (cartridge ? 16 : density == 625 ? 5 : 8);
179         else {
180                 tenths = 0;
181                 density = 1;
182         }
183         /*
184          * Allocate tape buffer contiguous with the array of instruction
185          * packets, so flushtape() can write them together with one write().
186          * Align tape buffer on page boundary to speed up tape write().
187          */
188         for (i = 0; i <= SLAVES; i++) {
189                 buf = (char *)
190                     malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE));
191                 if (buf == NULL)
192                         return(0);
193                 slaves[i].tblock = (char (*)[TP_BSIZE])
194 #ifdef  __linux__
195                     (((long)&buf[reqsiz] + pgoff) &~ pgoff);
196 #else
197                     (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
198 #endif
199                 slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
200         }
201         slp = &slaves[0];
202         slp->count = 1;
203         slp->tapea = 0;
204         slp->firstrec = 0;
205         nextblock = slp->tblock;
206         return(1);
207 }
208
209 void
210 writerec(const void *dp, int isspcl)
211 {
212
213         slp->req[trecno].dblk = (daddr_t)0;
214         slp->req[trecno].count = 1;
215         /* XXX post increment triggers an egcs-1.1.2-12 bug on alpha/sparc */
216         *(union u_spcl *)(*(nextblock)) = *(union u_spcl *)dp;
217         nextblock++;
218         if (isspcl)
219                 lastspclrec = spcl.c_tapea;
220         trecno++;
221         spcl.c_tapea++;
222         if (trecno >= ntrec)
223                 flushtape();
224 }
225
226 void
227 dumpblock(daddr_t blkno, int size)
228 {
229         int avail, tpblks, dblkno;
230
231         dblkno = fsbtodb(sblock, blkno);
232         tpblks = size >> tp_bshift;
233         while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
234                 slp->req[trecno].dblk = dblkno;
235                 slp->req[trecno].count = avail;
236                 trecno += avail;
237                 spcl.c_tapea += avail;
238                 if (trecno >= ntrec)
239                         flushtape();
240                 dblkno += avail << (tp_bshift - dev_bshift);
241                 tpblks -= avail;
242         }
243 }
244
245 int     nogripe = 0;
246
247 static void
248 tperror(int errnum)
249 {
250
251         if (pipeout) {
252                 msg("write error on %s: %s\n", tape, strerror(errnum));
253                 quit("Cannot recover\n");
254                 /* NOTREACHED */
255         }
256         msg("write error %d blocks into volume %d: %s\n", blocksthisvol, tapeno, strerror(errnum));
257         broadcast("DUMP WRITE ERROR!\n");
258         if (query("Do you want to rewrite this volume?")) {
259                 msg("Closing this volume.  Prepare to restart with new media;\n");
260                 msg("this dump volume will be rewritten.\n");
261                 killall();
262                 nogripe = 1;
263                 close_rewind();
264                 Exit(X_REWRITE);
265         }
266         if (query("Do you want to start the next tape?"))
267                 return;
268         dumpabort(0);
269 }
270
271 static void
272 sigpipe(int signo)
273 {
274
275         quit("Broken pipe\n");
276 }
277
278 /*
279  * do_stats --
280  *     Update xferrate stats
281  */
282 time_t
283 do_stats(void)
284 {
285         time_t tnow, ttaken;
286         int blocks;
287
288 #ifdef __linux__
289         (void)time4(&tnow);
290 #else
291         (void)time(&tnow);
292 #endif
293         ttaken = tnow - tstart_volume;
294         blocks = spcl.c_tapea - tapea_volume;
295         msg("Volume %d completed at: %s", tapeno, 
296 #ifdef __linux__
297                                           ctime4(&tnow));
298 #else
299                                           ctime(&tnow));
300 #endif
301         if (! compressed)
302                 msg("Volume %d %ld tape blocks (%.2fMB)\n", tapeno, 
303                         blocks, ((double)blocks * TP_BSIZE / 1048576));
304         if (ttaken > 0) {
305                 long volkb = (bytes_written - tapea_bytes) / 1024;
306                 long txfrate = volkb / ttaken;
307                 msg("Volume %d took %d:%02d:%02d\n", tapeno,
308                         ttaken / 3600, (ttaken % 3600) / 60, ttaken % 60);
309                 msg("Volume %d transfer rate: %ld KB/s\n", tapeno,
310                         txfrate);
311                 xferrate += txfrate;
312                 if (compressed) {
313                         double rate = .0005 + (double) blocks / (double) volkb;
314                         msg("Volume %d %ldKB uncompressed, %ldKB compressed,"
315                                 " compression ratio %1.3f\n",
316                                 tapeno, blocks, volkb, rate);
317                 }
318         }
319         return(tnow);
320 }
321
322 char *
323 mktimeest(time_t tnow)
324 {
325         static char msgbuf[128];
326         time_t deltat;
327
328         msgbuf[0] = '\0';
329
330         if (blockswritten < 500)
331                 return NULL;
332         if (blockswritten > tapesize)
333                 tapesize = blockswritten;
334         deltat = tstart_writing - tnow + (1.0 * (tnow - tstart_writing))
335                 / blockswritten * tapesize;
336         if (tnow > tstart_volume)
337                 (void)snprintf(msgbuf, sizeof(msgbuf),
338                         "%3.2f%% done at %ld KB/s, finished in %d:%02d\n",
339                         (blockswritten * 100.0) / tapesize,
340                         (spcl.c_tapea - tapea_volume) / (tnow - tstart_volume),
341                         (int)(deltat / 3600), (int)((deltat % 3600) / 60));
342         else
343                 (void)snprintf(msgbuf, sizeof(msgbuf),
344                         "%3.2f%% done, finished in %d:%02d\n",
345                         (blockswritten * 100.0) / tapesize,
346                         (int)(deltat / 3600), (int)((deltat % 3600) / 60));
347
348         return msgbuf;
349 }
350
351 #if defined(SIGINFO)
352 /*
353  * statussig --
354  *     information message upon receipt of SIGINFO
355  */
356 void
357 statussig(int notused)
358 {
359         time_t tnow;
360         int save_errno = errno;
361         char *buf;
362
363 #ifdef __linux__
364         (void) time4(&tnow);
365 #else
366         (void) time((time_t *) &tnow);
367 #endif
368         buf = mktimeest(tnow);
369         if (buf)
370                 write(STDERR_FILENO, buf, strlen(buf));
371         errno = save_errno;
372 }
373 #endif
374
375 static void
376 flushtape(void)
377 {
378         int i, blks, got;
379         long lastfirstrec;
380         struct slave_results returned;
381
382         int siz = (char *)nextblock - (char *)slp->req;
383
384         slp->req[trecno].count = 0;                     /* Sentinel */
385
386         if (atomic_write( slp->fd, (char *)slp->req, siz) != siz)
387                 quit("error writing command pipe: %s\n", strerror(errno));
388         slp->sent = 1; /* we sent a request, read the response later */
389
390         lastfirstrec = slp->firstrec;
391
392         if (++slp >= &slaves[SLAVES])
393                 slp = &slaves[0];
394
395         /* Read results back from next slave */
396         if (slp->sent) {
397                 if (atomic_read( slp->fd, (char *)&returned, sizeof returned)
398                     != sizeof returned) {
399                         perror("  DUMP: error reading command pipe in master");
400                         dumpabort(0);
401                 }
402                 got = returned.unclen;
403                 bytes_written += returned.clen;
404                 if (returned.unclen == returned.clen)
405                         uncomprblks++;
406                 slp->sent = 0;
407
408                 /* Check for errors */
409                 if (got < 0)
410                         tperror(-got);
411                 
412                 /* Check for end of tape */
413                 if (got == 0) {
414                         msg("End of tape detected\n");
415
416                         /*
417                          * Drain the results, don't care what the values were.
418                          * If we read them here then trewind won't...
419                          */
420                         for (i = 0; i < SLAVES; i++) {
421                                 if (slaves[i].sent) {
422                                         if (atomic_read( slaves[i].fd,
423                                             (char *)&returned, sizeof returned)
424                                             != sizeof returned) {
425                                                 perror("  DUMP: error reading command pipe in master");
426                                                 dumpabort(0);
427                                         }
428                                         slaves[i].sent = 0;
429                                 }
430                         }
431
432                         close_rewind();
433                         rollforward();
434                         return;
435                 }
436         }
437
438         blks = 0;
439         if (spcl.c_type != TS_END) {
440                 for (i = 0; i < spcl.c_count; i++)
441                         if (spcl.c_addr[i] != 0)
442                                 blks++;
443         }
444         slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
445         slp->tapea = spcl.c_tapea;
446         slp->firstrec = lastfirstrec + ntrec;
447         slp->inode = curino;
448         nextblock = slp->tblock;
449         trecno = 0;
450         asize += tenths + returned.clen / density;
451         blockswritten += ntrec;
452         blocksthisvol += ntrec;
453         if (!pipeout && !unlimited && (blocksperfile ?
454             (blocksthisvol >= blocksperfile) : (asize > tsize))) {
455                 close_rewind();
456                 startnewtape(0);
457         }
458         timeest();
459 }
460
461 /*
462  * Executes the command in a shell.
463  * Returns -1 if an error occured, the exit status of
464  * the command on success.
465  */
466 int system_command(const char *command, const char *device, int volnum) {
467         int pid, status;
468         char commandstr[4096];
469
470         pid = fork();
471         if (pid == -1) {
472                 perror("  DUMP: unable to fork");
473                 return -1;
474         }
475         if (pid == 0) {
476                 setuid(getuid());
477                 setgid(getgid());
478 #if OLD_STYLE_FSCRIPT
479                 snprintf(commandstr, sizeof(commandstr), "%s", command);
480 #else
481                 snprintf(commandstr, sizeof(commandstr), "%s %s %d", command, device, volnum);
482 #endif
483                 commandstr[sizeof(commandstr) - 1] = '\0';
484                 execl("/bin/sh", "sh", "-c", commandstr, NULL);
485                 perror("  DUMP: unable to execute shell");
486                 exit(-1);
487         }
488         do {
489                 if (waitpid(pid, &status, 0) == -1) {
490                         if (errno != EINTR) {
491                                 perror("  DUMP: waitpid error");
492                                 return -1;
493                         }
494                 } else {
495                         if (WIFEXITED(status))
496                                 return WEXITSTATUS(status);
497                         else
498                                 return -1;
499                 }
500         } while(1);
501 }
502
503 time_t
504 trewind(void)
505 {
506         int f;
507         int got;
508         struct slave_results returned;
509
510         for (f = 0; f < SLAVES; f++) {
511                 /*
512                  * Drain the results, but unlike EOT we DO (or should) care
513                  * what the return values were, since if we detect EOT after
514                  * we think we've written the last blocks to the tape anyway,
515                  * we have to replay those blocks with rollforward.
516                  *
517                  * fixme: punt for now.
518                  */
519                 if (slaves[f].sent) {
520                         if (atomic_read( slaves[f].fd, (char *)&returned, sizeof returned)
521                             != sizeof returned) {
522                                 perror("  DUMP: error reading command pipe in master");
523                                 dumpabort(0);
524                         }
525                         got = returned.unclen;
526                         bytes_written += returned.clen;
527                         if (returned.unclen == returned.clen)
528                                 uncomprblks++;
529                         slaves[f].sent = 0;
530
531                         if (got < 0)
532                                 tperror(-got);
533
534                         if (got == 0) {
535                                 msg("EOT detected in last 2 tape records!\n");
536                                 msg("Use a longer tape, decrease the size estimate\n");
537                                 quit("or use no size estimate at all.\n");
538                         }
539                 }
540                 (void) close(slaves[f].fd);
541         }
542         while (wait((int *)NULL) >= 0)  /* wait for any signals from slaves */
543                 /* void */;
544
545         if (!pipeout) {
546
547                 msg("Closing %s\n", tape);
548
549 #ifdef RDUMP
550                 if (host) {
551                         rmtclose();
552                         while (rmtopen(tape, 0) < 0)
553                                 sleep(10);
554                         rmtclose();
555                 }
556                 else 
557 #endif
558                 {
559                         (void) close(tapefd);
560                         while ((f = open(tape, 0)) < 0)
561                                 sleep (10);
562                         (void) close(f);
563                 }
564                 eot_code = 1;
565                 if (eot_script && spcl.c_type != TS_END) {
566                         msg("Launching %s\n", eot_script);
567                         eot_code = system_command(eot_script, tape, tapeno);
568                 }
569                 if (eot_code != 0 && eot_code != 1) {
570                         msg("Dump aborted by the end of tape script\n");
571                         dumpabort(0);
572                 }
573         }
574         return do_stats();
575 }
576
577                 
578 void
579 close_rewind(void)
580 {
581         (void)trewind();
582         if (nexttape || Mflag || (eot_code == 0) )
583                 return;
584         if (!nogripe) {
585                 msg("Change Volumes: Mount volume #%d\n", tapeno+1);
586                 broadcast("CHANGE DUMP VOLUMES!\7\7\n");
587         }
588         while (!query("Is the new volume mounted and ready to go?"))
589                 if (query("Do you want to abort?")) {
590                         dumpabort(0);
591                         /*NOTREACHED*/
592                 }
593 }
594
595 void
596 rollforward(void)
597 {
598         register struct req *p, *q, *prev;
599         register struct slave *tslp;
600         int i, size, savedtapea, got;
601         union u_spcl *ntb, *otb;
602         struct slave_results returned;
603 #ifdef __linux__
604         int blks;
605         long lastfirstrec;
606 #endif
607         tslp = &slaves[SLAVES];
608         ntb = (union u_spcl *)tslp->tblock[1];
609
610         /*
611          * Each of the N slaves should have requests that need to
612          * be replayed on the next tape.  Use the extra slave buffers
613          * (slaves[SLAVES]) to construct request lists to be sent to
614          * each slave in turn.
615          */
616         for (i = 0; i < SLAVES; i++) {
617                 q = &tslp->req[1];
618                 otb = (union u_spcl *)slp->tblock;
619
620                 /*
621                  * For each request in the current slave, copy it to tslp.
622                  */
623
624                 prev = NULL;
625                 for (p = slp->req; p->count > 0; p += p->count) {
626                         *q = *p;
627                         if (p->dblk == 0)
628                                 *ntb++ = *otb++; /* copy the datablock also */
629                         prev = q;
630                         q += q->count;
631                 }
632                 if (prev == NULL)
633                         quit("rollforward: protocol botch");
634                 if (prev->dblk != 0)
635                         prev->count -= 1;
636                 else
637                         ntb--;
638                 q -= 1;
639                 q->count = 0;
640                 q = &tslp->req[0];
641                 if (i == 0) {
642                         q->dblk = 0;
643                         q->count = 1;
644                         trecno = 0;
645                         nextblock = tslp->tblock;
646                         savedtapea = spcl.c_tapea;
647                         spcl.c_tapea = slp->tapea;
648                         startnewtape(0);
649                         spcl.c_tapea = savedtapea;
650                         lastspclrec = savedtapea - 1;
651                 }
652                 size = (char *)ntb - (char *)q;
653                 if (atomic_write( slp->fd, (char *)q, size) != size) {
654                         perror("  DUMP: error writing command pipe");
655                         dumpabort(0);
656                 }
657                 slp->sent = 1;
658 #ifdef __linux__
659                 lastfirstrec = slp->firstrec;
660 #endif
661                 if (++slp >= &slaves[SLAVES])
662                         slp = &slaves[0];
663
664                 q->count = 1;
665
666                 if (prev->dblk != 0) {
667                         /*
668                          * If the last one was a disk block, make the
669                          * first of this one be the last bit of that disk
670                          * block...
671                          */
672                         q->dblk = prev->dblk +
673                                 prev->count * (TP_BSIZE / DEV_BSIZE);
674                         ntb = (union u_spcl *)tslp->tblock;
675                 } else {
676                         /*
677                          * It wasn't a disk block.  Copy the data to its
678                          * new location in the buffer.
679                          */
680                         q->dblk = 0;
681                         *((union u_spcl *)tslp->tblock) = *ntb;
682                         ntb = (union u_spcl *)tslp->tblock[1];
683                 }
684         }
685         slp->req[0] = *q;
686         nextblock = slp->tblock;
687         if (q->dblk == 0) {
688 #ifdef __linux__
689         /* XXX post increment triggers an egcs-1.1.2-12 bug on alpha/sparc */
690                 *(union u_spcl *)(*nextblock) = *(union u_spcl *)tslp->tblock;
691 #endif
692                 nextblock++;
693         }
694         trecno = 1;
695
696         /*
697          * Clear the first slaves' response.  One hopes that it
698          * worked ok, otherwise the tape is much too short!
699          */
700         if (slp->sent) {
701                 if (atomic_read( slp->fd, (char *)&returned, sizeof returned)
702                     != sizeof returned) {
703                         perror("  DUMP: error reading command pipe in master");
704                         dumpabort(0);
705                 }
706                 got = returned.unclen;
707                 bytes_written += returned.clen;
708                 if (returned.clen == returned.unclen)
709                         uncomprblks++;
710                 slp->sent = 0;
711
712                 if (got < 0)
713                         tperror(-got);
714
715                 if (got == 0) {
716                         quit("EOT detected at start of the tape!\n");
717                 }
718         }
719
720 #ifdef __linux__
721         blks = 0;
722         if (spcl.c_type != TS_END) {
723                 for (i = 0; i < spcl.c_count; i++)
724                         if (spcl.c_addr[i] != 0)
725                                 blks++;
726         }
727
728         slp->firstrec = lastfirstrec + ntrec;
729         slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
730         slp->inode = curino;
731         asize += tenths + returned.clen / density;
732         blockswritten += ntrec;
733         blocksthisvol += ntrec;
734 #endif
735 }
736
737 /*
738  * We implement taking and restoring checkpoints on the tape level.
739  * When each tape is opened, a new process is created by forking; this
740  * saves all of the necessary context in the parent.  The child
741  * continues the dump; the parent waits around, saving the context.
742  * If the child returns X_REWRITE, then it had problems writing that tape;
743  * this causes the parent to fork again, duplicating the context, and
744  * everything continues as if nothing had happened.
745  */
746 void
747 startnewtape(int top)
748 {
749         int     parentpid;
750         int     childpid;
751         int     status;
752         int     waitpid;
753         char    *p;
754
755 #ifdef  __linux__
756         sigset_t sigs;
757         sigemptyset(&sigs);
758         sigaddset(&sigs, SIGINT);
759         sigprocmask(SIG_BLOCK, &sigs, NULL);
760 #else   /* __linux__ */
761 #ifdef sunos
762         void    (*interrupt_save)();
763 #else
764         sig_t   interrupt_save;
765 #endif
766         interrupt_save = signal(SIGINT, SIG_IGN);
767 #endif  /* __linux__ */
768
769         parentpid = getpid();
770         tapea_volume = spcl.c_tapea;
771         tapea_bytes = bytes_written;
772 #ifdef __linux__
773         (void)time4(&tstart_volume);
774 #else
775         (void)time((&tstart_volume);
776 #endif
777
778 restore_check_point:
779 #ifdef  __linux__
780         sigprocmask(SIG_UNBLOCK, &sigs, NULL);
781 #else
782         (void)signal(SIGINT, interrupt_save);
783 #endif
784         /*
785          *      All signals are inherited...
786          */
787         childpid = fork();
788         if (childpid < 0) {
789                 msg("Context save fork fails in parent %d\n", parentpid);
790                 Exit(X_ABORT);
791         }
792         if (childpid != 0) {
793                 /*
794                  *      PARENT:
795                  *      save the context by waiting
796                  *      until the child doing all of the work returns.
797                  *      don't catch the interrupt
798                  */
799 #ifdef  __linux__
800                 sigprocmask(SIG_BLOCK, &sigs, NULL);
801 #else
802                 signal(SIGINT, SIG_IGN);
803 #endif
804 #ifdef TDEBUG
805                 msg("Tape: %d; parent process: %d child process %d\n",
806                         tapeno+1, parentpid, childpid);
807 #endif /* TDEBUG */
808                 while ((waitpid = wait(&status)) != childpid)
809                         if (waitpid != rshpid)
810                                 msg("Parent %d waiting for child %d has another child %d return\n",
811                                 parentpid, childpid, waitpid);
812                 if (status & 0xFF) {
813                         msg("Child %d returns LOB status %o\n",
814                                 childpid, status&0xFF);
815                 }
816                 status = (status >> 8) & 0xFF;
817 #ifdef TDEBUG
818                 switch(status) {
819                         case X_FINOK:
820                                 msg("Child %d finishes X_FINOK\n", childpid);
821                                 break;
822                         case X_ABORT:
823                                 msg("Child %d finishes X_ABORT\n", childpid);
824                                 break;
825                         case X_REWRITE:
826                                 msg("Child %d finishes X_REWRITE\n", childpid);
827                                 break;
828                         default:
829                                 msg("Child %d finishes unknown %d\n",
830                                         childpid, status);
831                                 break;
832                 }
833 #endif /* TDEBUG */
834                 switch(status) {
835                         case X_FINOK:
836                                 Exit(X_FINOK);
837                         case X_ABORT:
838                                 Exit(X_ABORT);
839                         case X_REWRITE:
840                                 goto restore_check_point;
841                         default:
842                                 msg("Bad return code from dump: %d\n", status);
843                                 Exit(X_ABORT);
844                 }
845                 /*NOTREACHED*/
846         } else {        /* we are the child; just continue */
847 #ifdef TDEBUG
848                 sleep(4);       /* allow time for parent's message to get out */
849                 msg("Child on Tape %d has parent %d, my pid = %d\n",
850                         tapeno+1, parentpid, getpid());
851 #endif /* TDEBUG */
852                 /*
853                  * If we have a name like "/dev/rmt0,/dev/rmt1",
854                  * use the name before the comma first, and save
855                  * the remaining names for subsequent volumes.
856                  */
857                 tapeno++;               /* current tape sequence */
858                 if (Mflag) {
859                         snprintf(tape, MAXPATHLEN, "%s%03d", tapeprefix, tapeno);
860                         tape[MAXPATHLEN - 1] = '\0';
861                         msg("Dumping volume %d on %s\n", tapeno, tape);
862                 }
863                 else if (nexttape || strchr(tapeprefix, ',')) {
864                         if (nexttape && *nexttape)
865                                 tapeprefix = nexttape;
866                         if ((p = strchr(tapeprefix, ',')) != NULL) {
867                                 *p = '\0';
868                                 nexttape = p + 1;
869                         } else
870                                 nexttape = NULL;
871                         strncpy(tape, tapeprefix, MAXPATHLEN);
872                         tape[MAXPATHLEN - 1] = '\0';
873                         msg("Dumping volume %d on %s\n", tapeno, tape);
874                 }
875 #ifdef RDUMP
876                 while ((tapefd = (host ? rmtopen(tape, 2) : pipeout ? 
877                         fileno(stdout) : 
878                         open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
879 #else
880                 while ((tapefd = (pipeout ? fileno(stdout) :
881                                   open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
882 #endif
883                     {
884                         msg("Cannot open output \"%s\".\n", tape);
885                         if (!query("Do you want to retry the open?"))
886                                 dumpabort(0);
887                 }
888
889                 enslave();  /* Share open tape file descriptor with slaves */
890
891                 asize = 0;
892                 blocksthisvol = 0;
893                 if (top)
894                         newtape++;              /* new tape signal */
895                 spcl.c_count = slp->count;
896                 /*
897                  * measure firstrec in TP_BSIZE units since restore doesn't
898                  * know the correct ntrec value...
899                  */
900                 spcl.c_firstrec = slp->firstrec;
901                 spcl.c_volume++;
902                 spcl.c_type = TS_TAPE;
903                 spcl.c_flags |= DR_NEWHEADER;
904                 if (compressed)
905                         spcl.c_flags |= DR_COMPRESSED;
906                 writeheader((ino_t)slp->inode);
907                 spcl.c_flags &=~ DR_NEWHEADER;
908                 msg("Volume %d started at: %s", tapeno, 
909 #ifdef __linux__
910                                                 ctime4(&tstart_volume));
911 #else
912                                                 ctime(&tstart_volume));
913 #endif
914                 if (tapeno > 1)
915                         msg("Volume %d begins with blocks from inode %d\n",
916                                 tapeno, slp->inode);
917         }
918 }
919
920 void
921 dumpabort(int signo)
922 {
923
924         if (master != 0 && master != getpid())
925                 /* Signals master to call dumpabort */
926                 (void) kill(master, SIGTERM);
927         else {
928                 killall();
929                 msg("The ENTIRE dump is aborted.\n");
930         }
931 #ifdef RDUMP
932         rmtclose();
933 #endif
934         Exit(X_ABORT);
935 }
936
937 void
938 Exit(int status)
939 {
940
941 #ifdef TDEBUG
942         msg("pid = %d exits with status %d\n", getpid(), status);
943 #endif /* TDEBUG */
944         exit(status);
945 }
946
947 /*
948  * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
949  */
950 static void
951 proceed(int signo)
952 {
953         if (ready)
954                 siglongjmp(jmpbuf, 1);
955         caught++;
956 }
957
958 void
959 enslave(void)
960 {
961         int cmd[2];
962 #ifdef  LINUX_FORK_BUG
963         int i, j;
964 #else
965         register int i, j;
966 #endif
967
968         master = getpid();
969
970     {   struct sigaction sa;
971         memset(&sa, 0, sizeof sa);
972         sigemptyset(&sa.sa_mask);
973         sa.sa_handler = dumpabort;
974         sigaction(SIGTERM, &sa, NULL); /* Slave sends SIGTERM on dumpabort() */
975         sa.sa_handler = sigpipe;
976         sigaction(SIGPIPE, &sa, NULL);
977         sa.sa_handler = proceed;
978         sa.sa_flags = SA_RESTART;
979         sigaction(SIGUSR2, &sa, NULL); /* Slave sends SIGUSR2 to next slave */
980    }
981
982         for (i = 0; i < SLAVES; i++) {
983                 if (i == slp - &slaves[0]) {
984                         caught = 1;
985                 } else {
986                         caught = 0;
987                 }
988
989                 if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
990                     (slaves[i].pid = fork()) < 0)
991                         quit("too many slaves, %d (recompile smaller): %s\n",
992                             i, strerror(errno));
993
994                 slaves[i].fd = cmd[1];
995                 slaves[i].sent = 0;
996                 if (slaves[i].pid == 0) {           /* Slave starts up here */
997                         sigset_t sigs;
998                         for (j = 0; j <= i; j++)
999                                 (void) close(slaves[j].fd);
1000                         sigemptyset(&sigs);
1001                         sigaddset(&sigs, SIGINT);  /* Master handles this */
1002 #if defined(SIGINFO)
1003                         sigaddset(&sigs, SIGINFO);
1004 #endif
1005                         sigprocmask(SIG_BLOCK, &sigs, NULL);
1006
1007 #ifdef  LINUX_FORK_BUG
1008                         if (atomic_write( cmd[0], (char *) &i, sizeof i)
1009                             != sizeof i)
1010                                 quit("master/slave protocol botched 3\n");
1011 #endif
1012                         doslave(cmd[0], i);
1013                         Exit(X_FINOK);
1014                 }
1015         }
1016
1017 #ifdef  LINUX_FORK_BUG
1018         /*
1019          * Wait for all slaves to _actually_ start to circumvent a bug in
1020          * Linux kernels >= 2.1.3 where a signal sent to a child that hasn't
1021          * returned from fork() causes a SEGV in the child process
1022          */
1023         for (i = 0; i < SLAVES; i++)
1024                 if (atomic_read( slaves[i].fd, (char *) &j, sizeof j) != sizeof j)
1025                         quit("master/slave protocol botched 4\n");
1026 #endif
1027
1028         for (i = 0; i < SLAVES; i++)
1029                 (void) atomic_write( slaves[i].fd, 
1030                               (char *) &slaves[(i + 1) % SLAVES].pid, 
1031                               sizeof slaves[0].pid);
1032                 
1033         master = 0; 
1034 }
1035
1036 void
1037 killall(void)
1038 {
1039         register int i;
1040
1041         for (i = 0; i < SLAVES; i++)
1042                 if (slaves[i].pid > 0) {
1043                         (void) kill(slaves[i].pid, SIGKILL);
1044                         slaves[i].sent = 0;
1045                 }
1046 }
1047
1048 /*
1049  * Synchronization - each process waits for a SIGUSR2 from the
1050  * previous process before writing to the tape, and sends SIGUSR2
1051  * to the next process when the tape write completes. On tape errors
1052  * a SIGUSR1 is sent to the master which then terminates all of the
1053  * slaves.
1054  */
1055 static void
1056 doslave(int cmd, int slave_number)
1057 {
1058         register int nread;
1059         int nextslave, size, eot_count, bufsize;
1060         volatile int wrote = 0;
1061         char *buffer;
1062 #ifdef HAVE_ZLIB
1063         struct tapebuf *comp_buf = NULL;
1064         int compresult, complevel = 6, do_compress = 0;
1065         unsigned long worklen;
1066 #endif /* HAVE_ZLIB */
1067         struct slave_results returns;
1068 #ifdef  __linux__
1069         errcode_t retval;
1070 #endif
1071
1072         /*
1073          * Need our own seek pointer.
1074          */
1075         (void) close(diskfd);
1076         if ((diskfd = open(disk, O_RDONLY)) < 0)
1077                 quit("slave couldn't reopen disk: %s\n", strerror(errno));
1078 #ifdef  __linux__
1079         ext2fs_close(fs);
1080         retval = dump_fs_open(disk, &fs);
1081         if (retval)
1082                 quit("slave couldn't reopen disk: %s\n", error_message(retval));
1083 #endif  /* __linux__ */
1084
1085         /*
1086          * Need the pid of the next slave in the loop...
1087          */
1088         if ((nread = atomic_read( cmd, (char *)&nextslave, sizeof nextslave))
1089             != sizeof nextslave) {
1090                 quit("master/slave protocol botched - didn't get pid of next slave.\n");
1091         }
1092
1093 #ifdef HAVE_ZLIB
1094         /* if we're doing a compressed dump, allocate the compress buffer */
1095         if (compressed) {
1096                 comp_buf = malloc(sizeof(struct tapebuf) + TP_BSIZE + writesize);
1097                 if (comp_buf == NULL)
1098                         quit("couldn't allocate a compress buffer.\n");
1099                 comp_buf->flags = 0;
1100         }
1101 #endif /* HAVE_ZLIB */
1102
1103         /*
1104          * Get list of blocks to dump, read the blocks into tape buffer
1105          */
1106         while ((nread = atomic_read( cmd, (char *)slp->req, reqsiz)) == reqsiz) {
1107                 register struct req *p = slp->req;
1108
1109                 for (trecno = 0; trecno < ntrec;
1110                      trecno += p->count, p += p->count) {
1111                         if (p->dblk) {  /* read a disk block */
1112                                 bread(p->dblk, slp->tblock[trecno],
1113                                         p->count * TP_BSIZE);
1114                         } else {        /* read record from pipe */
1115                                 if (p->count != 1 || atomic_read( cmd,
1116                                     (char *)slp->tblock[trecno],
1117                                     TP_BSIZE) != TP_BSIZE)
1118                                        quit("master/slave protocol botched.\n");
1119                         }
1120                 }
1121                 if (sigsetjmp(jmpbuf, 1) == 0) {
1122                         ready = 1;
1123                         if (!caught)
1124                                 (void) pause();
1125                 }
1126                 ready = 0;
1127                 caught = 0;
1128
1129                 /* Try to write the data... */
1130                 wrote = 0;
1131                 eot_count = 0;
1132                 size = 0;
1133                 buffer = (char *) slp->tblock[0];       /* set write pointer */
1134                 bufsize = writesize;                    /* length to write */
1135                 returns.clen = returns.unclen = bufsize;
1136
1137 #ifdef HAVE_ZLIB
1138                 /* 
1139                  * The first NR_SLAVE blocks are not compressed.
1140                  * When writing a compressed dump, each block is
1141                  * written from struct tapebuf with an 4 byte prefix
1142                  * followed by the data. This can be less than
1143                  * writesize. Restore, on a short read, can compare the
1144                  * length read to the compressed length in the header
1145                  * to verify that the read was good. Blocks which don't
1146                  * compress well are written uncompressed.
1147                  */
1148
1149                 if (compressed) {
1150                         comp_buf->length = bufsize;
1151                         worklen = TP_BSIZE + writesize;
1152                         if (do_compress)
1153                                 compresult = compress2(comp_buf->buf, &worklen,
1154                                         (char *)slp->tblock[0], writesize, complevel);
1155                         if (compresult == Z_OK && worklen <= writesize-32) {
1156                                 /* write the compressed buffer */
1157                                 comp_buf->length = worklen;
1158                                 comp_buf->compressed = 1;
1159                                 buffer = (char *) comp_buf;
1160                                 returns.clen = bufsize = worklen + sizeof(struct tapebuf);
1161                         }
1162                         else {
1163                                 /* write the data uncompressed */
1164                                 comp_buf->length = writesize;
1165                                 comp_buf->compressed = 0;
1166                                 buffer = (char *) comp_buf;
1167                                 returns.clen = bufsize = writesize + sizeof(struct tapebuf);
1168                                 returns.unclen = returns.clen;
1169                                 memcpy(comp_buf->buf, (char *)slp->tblock[0], writesize);
1170                         }
1171                 }
1172                 /* compress the remaining blocks */
1173                 do_compress = compressed;
1174 #endif /* HAVE_ZLIB */
1175
1176                 while (eot_count < 10 && size < bufsize) {
1177 #ifdef RDUMP
1178                         if (host)
1179                                 wrote = rmtwrite(buffer + size, bufsize - size);
1180                         else
1181 #endif
1182                                 wrote = write(tapefd, buffer + size, bufsize - size);
1183 #ifdef WRITEDEBUG
1184                         printf("slave %d wrote %d\n", slave_number, wrote);
1185 #endif
1186                         if (wrote < 0)
1187                                 break;
1188                         if (wrote == 0)
1189                                 eot_count++;
1190                         size += wrote;
1191                 }
1192
1193 #ifdef WRITEDEBUG
1194                 if (size != bufsize)
1195                  printf("slave %d only wrote %d out of %d bytes and gave up.\n",
1196                      slave_number, size, bufsize);
1197 #endif
1198
1199                 /*
1200                  * Handle ENOSPC as an EOT condition.
1201                  */
1202                 if (wrote < 0 && errno == ENOSPC) {
1203                         wrote = 0;
1204                         eot_count++;
1205                 }
1206
1207                 if (eot_count > 0)
1208                         returns.clen = returns.unclen = 0;
1209
1210                 /*
1211                  * pass errno back to master for special handling
1212                  */
1213                 if (wrote < 0)
1214                         returns.unclen = -errno;
1215
1216                 /*
1217                  * pass size of data and size of write back to master
1218                  * (for EOT handling)
1219                  */
1220                 (void) atomic_write( cmd, (char *)&returns, sizeof returns);
1221
1222                 /*
1223                  * Signal the next slave to go.
1224                  */
1225                 (void) kill(nextslave, SIGUSR2);
1226         }
1227         if (nread != 0)
1228                 quit("error reading command pipe: %s\n", strerror(errno));
1229 }
1230
1231 /*
1232  * Since a read from a pipe may not return all we asked for,
1233  * or a write may not write all we ask if we get a signal,
1234  * loop until the count is satisfied (or error).
1235  */
1236 static ssize_t
1237 atomic_read(int fd, void *buf, size_t count)
1238 {
1239         int got, need = count;
1240
1241         do {
1242                 while ((got = read(fd, buf, need)) > 0 && (need -= got) > 0)
1243                         (char *)buf += got;
1244         } while (got == -1 && errno == EINTR);
1245         return (got < 0 ? got : count - need);
1246 }
1247
1248 /*
1249  * Since a read from a pipe may not return all we asked for,
1250  * or a write may not write all we ask if we get a signal,
1251  * loop until the count is satisfied (or error).
1252  */
1253 static ssize_t
1254 atomic_write(int fd, const void *buf, size_t count)
1255 {
1256         int got, need = count;
1257
1258         do {
1259                 while ((got = write(fd, buf, need)) > 0 && (need -= got) > 0)
1260                         (char *)buf += got;
1261         } while (got == -1 && errno == EINTR);
1262         return (got < 0 ? got : count - need);
1263 }