54e2aa0c078520533357936641053686b4b1c5e2
[debian/amanda] / tape-src / tapeio.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 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  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27
28 /*
29  * $Id: tapeio.c,v 1.53 2006/01/14 04:37:20 paddy_s Exp $
30  *
31  * implements generic tape I/O functions
32  */
33
34 #include "amanda.h"
35 #include <stdarg.h>
36 #include <errno.h>
37
38 #include "tapeio.h"
39 #include "fileheader.h"
40 #ifndef R_OK
41 #define R_OK 4
42 #define W_OK 2
43 #endif
44
45 #include "output-tape.h"
46 #include "output-null.h"
47 #include "output-rait.h"
48 #include "output-file.h"
49
50 static struct virtualtape {
51     char *prefix;
52     int (*xxx_tape_access) P((char *, int));
53     int (*xxx_tape_open) (char *, int, int);
54     int (*xxx_tape_stat) P((char *, struct stat *));
55     int (*xxx_tapefd_close) P((int));
56     int (*xxx_tapefd_fsf) P((int, int));
57     ssize_t (*xxx_tapefd_read) P((int, void *, size_t));
58     int (*xxx_tapefd_rewind) P((int));
59     void (*xxx_tapefd_resetofs) P((int));
60     int (*xxx_tapefd_unload) P((int));
61     int (*xxx_tapefd_status) P((int, struct am_mt_status *));
62     int (*xxx_tapefd_weof) P((int, int));
63     ssize_t (*xxx_tapefd_write) P((int, const void *, size_t));
64     int (*xxx_tapefd_can_fork) P((int));
65 } vtable[] = {
66   /* note: "tape" has to be the first entry because it is the
67   **        default if no prefix match is found.
68   */
69   {"tape", tape_tape_access, tape_tape_open, tape_tape_stat,
70         tape_tapefd_close, tape_tapefd_fsf,
71         tape_tapefd_read, tape_tapefd_rewind, tape_tapefd_resetofs,
72         tape_tapefd_unload, tape_tapefd_status, tape_tapefd_weof,
73         tape_tapefd_write, tape_tapefd_can_fork },
74   {"null", null_tape_access, null_tape_open, null_tape_stat,
75         null_tapefd_close, null_tapefd_fsf,
76         null_tapefd_read, null_tapefd_rewind, null_tapefd_resetofs,
77         null_tapefd_unload, null_tapefd_status, null_tapefd_weof,
78         null_tapefd_write, null_tapefd_can_fork },
79   {"rait", rait_access, rait_tape_open, rait_stat,
80         rait_close, rait_tapefd_fsf,
81         rait_read, rait_tapefd_rewind, rait_tapefd_resetofs,
82         rait_tapefd_unload, rait_tapefd_status, rait_tapefd_weof,
83         rait_write, rait_tapefd_can_fork },
84   {"file", file_tape_access, file_tape_open, file_tape_stat,
85         file_tapefd_close, file_tapefd_fsf,
86         file_tapefd_read, file_tapefd_rewind, file_tapefd_resetofs,
87         file_tapefd_unload, file_tapefd_status, file_tapefd_weof,
88         file_tapefd_write, file_tapefd_can_fork },
89   {NULL,},
90 };
91
92 static struct tape_info {
93     int vtape_index;
94     char *host;
95     char *disk;
96     int level;
97     char *datestamp;
98     long length;
99     char *tapetype;
100     int fake_label;
101     int ioctl_fork;
102     int master_fd;
103 } *tape_info = NULL;
104 static int tape_info_count = 0;
105
106 static char *errstr = NULL;
107
108 /*
109  * Additional initialization function for tape_info table.
110  */
111
112 static void
113 tape_info_init(ptr)
114     void *ptr;
115 {
116     struct tape_info *t = ptr;
117
118     t->level = -1;
119     t->vtape_index = -1;
120     t->ioctl_fork = 1;
121     t->master_fd = -1;
122 }
123
124 /*
125  * Convert the "name" part of a device to a vtape slot.
126  */
127
128 static int
129 name2slot(name, ntrans)
130     char *name;
131     char **ntrans;
132 {
133     char *pc;
134     int len, i;
135
136     if(0 != (pc = strchr(name, ':'))) {
137         len = pc - name;
138         for( i = 0 ; vtable[i].prefix && vtable[i].prefix[0]; i++ ) {
139             if(0 == strncmp(vtable[i].prefix, name , len)
140                 && '\0' == vtable[i].prefix[len]) {
141                 *ntrans = pc + 1;
142                 return i;
143             }
144         }
145     }
146     *ntrans = name;
147     return 0;
148 }
149
150 /*
151  * Routines for parsing a device name.
152  */
153
154 /*
155  * Initialize parsing.  The text in the "dev" parameter will be altered,
156  * so a copy should be passed to us.
157  */
158
159 int
160 tapeio_init_devname(char * dev,
161                     char **dev_left,
162                     char **dev_right,
163                     char **dev_next) {
164     int ch;
165     char *p;
166     int depth;
167
168     *dev_left = *dev_right = *dev_next = NULL;  /* defensive coding */
169
170     /*
171      * See if there is a '{' and find the matching '}'.
172      */
173     if((*dev_next = p = strchr(dev, '{')) != NULL) {
174         depth = 1;
175         p++;
176         while(depth > 0) {
177             while((ch = *p++) != '\0' && ch != '{' && ch != '}') {}
178             if(ch == '\0') {
179                 /*
180                  * Did not find a matching '}'.
181                  */
182                 amfree(dev);
183                 errno = EINVAL;
184                 return -1;
185             } else if(ch == '{') {
186                 depth++;
187             } else if(ch == '}') {
188                 depth--;
189             }
190         }
191         if(strchr(p, '{') != NULL || strchr(p, '}') != NULL) {
192             amfree(dev);
193             errno = EINVAL;
194             return -1;                          /* only one list allowed */
195         }
196         *dev_left = dev;                        /* text before the '{' */
197         **dev_next = '\0';                      /* zap the '{' */
198         (*dev_next)++;                          /* point to the first name */
199         p[-1] = '\0';                           /* zap the '}' */
200         *dev_right = p;                         /* text after the '}' */
201     } else {
202         /*
203          * Arrange to return just one name.
204          */
205         *dev_next = dev;
206         *dev_left = *dev_right = "";
207     }
208     return 0;
209 }
210
211 /*
212  * Return the next device name.  A dynamic area is returned that the
213  * caller is responsible for freeing.
214  */
215
216 char *
217 tapeio_next_devname(char * dev_left,
218                     char * dev_right,
219                     char **dev_next) {
220     int ch;
221     char *next;
222     char *p;
223     int depth;
224
225     p = next = *dev_next;                       /* remember the start point */
226     depth = 0;
227     do {
228         while((ch = *p++) != '\0' && ch != '{' && ch != '}' && ch != ',') {}
229         if(ch == '\0') {
230             /*
231              * Found the end of a name.
232              */
233             assert(depth == 0);
234             if(*next == '\0') {
235                 return NULL;                    /* end of the list */
236             }
237             p--;                                /* point to the null byte */
238             break;
239         } else if(ch == '{') {
240             depth++;
241         } else if(ch == '}') {
242             assert(depth > 0);
243             depth--;
244         }
245     } while(depth != 0 || ch != ',');
246     if(ch == ',') {
247         p[-1] = '\0';                           /* zap the ',' */
248     }
249     *dev_next = p;                              /* set up for the next call */
250     return vstralloc(dev_left, next, dev_right, NULL);
251 }
252
253 /*
254  * The following functions get/set fields in the tape_info structure.
255  * To allow them to be called (e.g. set) from lower level open functions
256  * started by tape_open, we check and allocate the tape_info structure
257  * here as well as in tape_open.
258  */
259
260 char *
261 tapefd_getinfo_host(fd)
262     int fd;
263 {
264     amtable_alloc((void **)&tape_info,
265                   &tape_info_count,
266                   sizeof(*tape_info),
267                   fd + 1,
268                   10,
269                   tape_info_init);
270     if(tape_info[fd].master_fd != -1)
271         return tapefd_getinfo_host(tape_info[fd].master_fd);
272     return tape_info[fd].host;
273 }
274
275 void
276 tapefd_setinfo_host(fd, v)
277     int fd;
278     char *v;
279 {
280     amtable_alloc((void **)&tape_info,
281                   &tape_info_count,
282                   sizeof(*tape_info),
283                   fd + 1,
284                   10,
285                   tape_info_init);
286     amfree(tape_info[fd].host);
287     if(v) {
288         tape_info[fd].host = stralloc(v);
289     }
290 }
291
292 char *
293 tapefd_getinfo_disk(fd)
294     int fd;
295 {
296     amtable_alloc((void **)&tape_info,
297                   &tape_info_count,
298                   sizeof(*tape_info),
299                   fd + 1,
300                   10,
301                   tape_info_init);
302     if(tape_info[fd].master_fd != -1)
303         return tapefd_getinfo_disk(tape_info[fd].master_fd);
304     return tape_info[fd].disk;
305 }
306
307 void
308 tapefd_setinfo_disk(fd, v)
309     int fd;
310     char *v;
311 {
312     amtable_alloc((void **)&tape_info,
313                   &tape_info_count,
314                   sizeof(*tape_info),
315                   fd + 1,
316                   10,
317                   tape_info_init);
318     amfree(tape_info[fd].disk);
319     if(v) {
320         tape_info[fd].disk = stralloc(v);
321     }
322 }
323
324 int
325 tapefd_getinfo_level(fd)
326     int fd;
327 {
328     amtable_alloc((void **)&tape_info,
329                   &tape_info_count,
330                   sizeof(*tape_info),
331                   fd + 1,
332                   10,
333                   tape_info_init);
334     if(tape_info[fd].master_fd != -1)
335         return tapefd_getinfo_level(tape_info[fd].master_fd);
336     return tape_info[fd].level;
337 }
338
339 void
340 tapefd_setinfo_level(fd, v)
341     int fd;
342     int v;
343 {
344     amtable_alloc((void **)&tape_info,
345                   &tape_info_count,
346                   sizeof(*tape_info),
347                   fd + 1,
348                   10,
349                   tape_info_init);
350     tape_info[fd].level = v;
351 }
352
353 char *
354 tapefd_getinfo_datestamp(fd)
355     int fd;
356 {
357     amtable_alloc((void **)&tape_info,
358                   &tape_info_count,
359                   sizeof(*tape_info),
360                   fd + 1,
361                   10,
362                   tape_info_init);
363     return tape_info[fd].datestamp;
364 }
365
366 void
367 tapefd_setinfo_datestamp(fd, v)
368     int fd;
369     char *v;
370 {
371     amtable_alloc((void **)&tape_info,
372                   &tape_info_count,
373                   sizeof(*tape_info),
374                   fd + 1,
375                   10,
376                   tape_info_init);
377     tape_info[fd].datestamp = newstralloc(tape_info[fd].datestamp, v);
378 }
379
380 long
381 tapefd_getinfo_length(fd)
382     int fd;
383 {
384     amtable_alloc((void **)&tape_info,
385                   &tape_info_count,
386                   sizeof(*tape_info),
387                   fd + 1,
388                   10,
389                   tape_info_init);
390     return tape_info[fd].length;
391 }
392
393 void
394 tapefd_setinfo_length(fd, v)
395     int fd;
396     long v;
397 {
398     amtable_alloc((void **)&tape_info,
399                   &tape_info_count,
400                   sizeof(*tape_info),
401                   fd + 1,
402                   10,
403                   tape_info_init);
404     tape_info[fd].length = v;
405 }
406
407 char *
408 tapefd_getinfo_tapetype(fd)
409     int fd;
410 {
411     amtable_alloc((void **)&tape_info,
412                   &tape_info_count,
413                   sizeof(*tape_info),
414                   fd + 1,
415                   10,
416                   tape_info_init);
417     return tape_info[fd].tapetype;
418 }
419
420 void
421 tapefd_setinfo_tapetype(fd, v)
422     int fd;
423     char *v;
424 {
425     amtable_alloc((void **)&tape_info,
426                   &tape_info_count,
427                   sizeof(*tape_info),
428                   fd + 1,
429                   10,
430                   tape_info_init);
431     tape_info[fd].tapetype = newstralloc(tape_info[fd].tapetype, v);
432 }
433
434 int
435 tapefd_getinfo_fake_label(fd)
436     int fd;
437 {
438     amtable_alloc((void **)&tape_info,
439                   &tape_info_count,
440                   sizeof(*tape_info),
441                   fd + 1,
442                   10,
443                   tape_info_init);
444     return tape_info[fd].fake_label;
445 }
446
447 void
448 tapefd_setinfo_fake_label(fd, v)
449     int fd;
450     int v;
451 {
452     amtable_alloc((void **)&tape_info,
453                   &tape_info_count,
454                   sizeof(*tape_info),
455                   fd + 1,
456                   10,
457                   tape_info_init);
458     tape_info[fd].fake_label = v;
459 }
460
461 int
462 tapefd_getinfo_ioctl_fork(fd)
463     int fd;
464 {
465     amtable_alloc((void **)&tape_info,
466                   &tape_info_count,
467                   sizeof(*tape_info),
468                   fd + 1,
469                   10,
470                   tape_info_init);
471     return tape_info[fd].ioctl_fork;
472 }
473
474 void
475 tapefd_setinfo_ioctl_fork(fd, v)
476     int fd;
477     int v;
478 {
479     amtable_alloc((void **)&tape_info,
480                   &tape_info_count,
481                   sizeof(*tape_info),
482                   fd + 1,
483                   10,
484                   tape_info_init);
485     tape_info[fd].ioctl_fork = v;
486 }
487
488 void
489 tapefd_set_master_fd(fd, master_fd)
490     int fd;
491     int master_fd;
492 {
493     amtable_alloc((void **)&tape_info,
494                   &tape_info_count,
495                   sizeof(*tape_info),
496                   fd + 1,
497                   10,
498                   tape_info_init);
499     tape_info[fd].master_fd = master_fd;
500 }
501
502
503 /*
504  * The normal tape operation functions.
505  */
506
507 int
508 tape_access(filename, mode)
509     char *filename;
510     int mode;
511 {
512     char *tname;
513     int vslot;
514
515     vslot = name2slot(filename, &tname);
516     return vtable[vslot].xxx_tape_access(tname, mode);
517 }
518
519 int
520 tape_stat(filename, buf)
521     char *filename;
522     struct stat *buf;
523 {
524     char *tname;
525     int vslot;
526
527     vslot = name2slot(filename, &tname);
528     return vtable[vslot].xxx_tape_stat(tname, buf);
529 }
530
531 int
532 tape_open(char *filename, int mode, ...)
533 {
534     char *tname;
535     int vslot;
536     int fd;
537     int mask;
538     va_list ap;
539
540     va_start(ap, mode);
541     mask = va_arg(ap, int);
542     va_end(ap);
543
544     vslot = name2slot(filename, &tname);
545     if((fd = vtable[vslot].xxx_tape_open(tname, mode, mask)) >= 0) {
546         amtable_alloc((void **)&tape_info,
547                       &tape_info_count,
548                       sizeof(*tape_info),
549                       fd + 1,
550                       10,
551                       tape_info_init);
552         /*
553          * It is possible to recurse in the above open call and come
554          * back here twice for the same file descriptor.  Set the vtape
555          * index only if it is not already set, i.e. the first call wins.
556          */
557         if(tape_info[fd].vtape_index < 0) {
558             tape_info[fd].vtape_index = vslot;
559         }
560     }
561     return fd;
562 }
563
564 int
565 tapefd_close(fd)
566     int fd;
567 {
568     int vslot, res = -1;
569
570     if(fd < 0
571        || fd >= tape_info_count
572        || (vslot = tape_info[fd].vtape_index) < 0) {
573         errno = EBADF;
574         return -1;
575     }
576     if((res = vtable[vslot].xxx_tapefd_close(fd)) == 0) {
577         amfree(tape_info[fd].host);
578         amfree(tape_info[fd].disk);
579         amfree(tape_info[fd].datestamp);
580         amfree(tape_info[fd].tapetype);
581         memset(tape_info + fd, 0, sizeof(*tape_info));
582         tape_info_init((void *)(tape_info + fd));
583     }
584     return res;
585 }
586
587 int
588 tapefd_can_fork(fd)
589     int fd;
590 {
591     int vslot, res = -1;
592
593     if(fd < 0
594        || fd >= tape_info_count
595        || (vslot = tape_info[fd].vtape_index) < 0) {
596         errno = EBADF;
597         return -1;
598     }
599     res = vtable[vslot].xxx_tapefd_can_fork(fd);
600
601     return res;
602 }
603
604 int
605 tapefd_fsf(fd, count)
606     int fd;
607     int count;
608 {
609     int vslot;
610
611     if(fd < 0
612        || fd >= tape_info_count
613        || (vslot = tape_info[fd].vtape_index) < 0) {
614         errno = EBADF;
615         return -1;
616     }
617     return vtable[vslot].xxx_tapefd_fsf(fd, count);
618 }
619
620 int
621 tapefd_rewind(fd)
622     int fd;
623 {
624     int vslot;
625
626     if(fd < 0
627        || fd >= tape_info_count
628        || (vslot = tape_info[fd].vtape_index) < 0) {
629         errno = EBADF;
630         return -1;
631     }
632     return vtable[vslot].xxx_tapefd_rewind(fd);
633 }
634
635 void
636 tapefd_resetofs(fd)
637     int fd;
638 {
639     int vslot;
640
641     if(fd < 0
642        || fd >= tape_info_count
643        || (vslot = tape_info[fd].vtape_index) < 0) {
644         errno = EBADF;                          /* not that it matters */
645         return;
646     }
647     vtable[vslot].xxx_tapefd_resetofs(fd);
648 }
649
650 int
651 tapefd_unload(fd)
652     int fd;
653 {
654     int vslot;
655
656     if(fd < 0
657        || fd >= tape_info_count
658        || (vslot = tape_info[fd].vtape_index) < 0) {
659         errno = EBADF;
660         return -1;
661     }
662     return vtable[vslot].xxx_tapefd_unload(fd);
663 }
664
665 int
666 tapefd_status(fd, stat)
667     int fd;
668     struct am_mt_status *stat;
669 {
670     int vslot;
671
672     if(fd < 0
673        || fd >= tape_info_count
674        || (vslot = tape_info[fd].vtape_index) < 0) {
675         errno = EBADF;
676         return -1;
677     }
678     return vtable[vslot].xxx_tapefd_status(fd, stat);
679 }
680
681 int
682 tapefd_weof(fd, count)
683     int fd;
684     int count;
685 {
686     int vslot;
687
688     if(fd < 0
689        || fd >= tape_info_count
690        || (vslot = tape_info[fd].vtape_index) < 0) {
691         errno = EBADF;
692         return -1;
693     }
694     return vtable[vslot].xxx_tapefd_weof(fd, count);
695 }
696
697 ssize_t
698 tapefd_read(fd, buffer, count)
699     int fd;
700     void *buffer;
701     size_t count;
702 {
703     int vslot;
704
705     if(fd < 0
706        || fd >= tape_info_count
707        || (vslot = tape_info[fd].vtape_index) < 0) {
708         errno = EBADF;
709         return -1;
710     }
711     return vtable[vslot].xxx_tapefd_read(fd, buffer, count);
712 }
713
714 ssize_t
715 tapefd_write(fd, buffer, count)
716     int fd;
717     const void *buffer;
718     size_t count;
719 {
720     int vslot;
721
722     if(fd < 0
723        || fd >= tape_info_count
724        || (vslot = tape_info[fd].vtape_index) < 0) {
725         errno = EBADF;
726         return -1;
727     }
728     return vtable[vslot].xxx_tapefd_write(fd, buffer, count);
729 }
730
731 char *
732 tape_rewind(devname)
733     char *devname;
734 {
735     int fd;
736     char *r = NULL;
737
738     if((fd = tape_open(devname, O_RDONLY)) < 0) {
739         r = errstr = newvstralloc(errstr,
740                                   "tape_rewind: tape open: ",
741                                   devname,
742                                   ": ",
743                                   strerror(errno),
744                                   NULL);
745     } else if(tapefd_rewind(fd) == -1) {
746         r = errstr = newvstralloc(errstr,
747                                   "tape_rewind: rewinding tape: ",
748                                   devname,
749                                   ": ",
750                                   strerror(errno),
751                                   NULL);
752     }
753     if(fd >= 0) {
754         tapefd_close(fd);
755     }
756     return r;
757 }
758
759 char *
760 tape_unload(devname)
761     char *devname;
762 {
763     int fd;
764     char *r = NULL;
765
766     if((fd = tape_open(devname, O_RDONLY)) < 0) {
767         r = errstr = newvstralloc(errstr,
768                                   "tape_unload: tape open: ",
769                                   devname,
770                                   ": ",
771                                   strerror(errno),
772                                   NULL);
773     } else if(tapefd_unload(fd) == -1) {
774         r = errstr = newvstralloc(errstr,
775                                   "tape_unload: unloading tape: ",
776                                   devname,
777                                   ": ",
778                                   strerror(errno),
779                                   NULL);
780     }
781     if(fd >= 0) {
782         tapefd_close(fd);
783     }
784     return r;
785 }
786
787 char *
788 tape_fsf(devname, count)
789     char *devname;
790     int count;
791 {
792     int fd;
793     char count_str[NUM_STR_SIZE];
794     char *r = NULL;
795
796     if((fd = tape_open(devname, O_RDONLY)) < 0) {
797         r = errstr = newvstralloc(errstr,
798                                   "tape_fsf: tape open: ",
799                                   devname,
800                                   ": ",
801                                   strerror(errno),
802                                   NULL);
803     } else if(tapefd_fsf(fd, count) == -1) {
804         snprintf(count_str, sizeof(count_str), "%d", count);
805         r = errstr = newvstralloc(errstr,
806                                   "tape_fsf: fsf ",
807                                   count_str,
808                                   "file", (count == 1) ? "" : "s",
809                                   ": ",
810                                   strerror(errno),
811                                   NULL);
812     }
813     if(fd >= 0) {
814         tapefd_close(fd);
815     }
816     return r;
817 }
818
819 /* Reads the tape label, like you expect. If failure, returns an error
820    string. If the tape might not be an Amanda tape, the returned
821    string will start with NOT_AMANDA_TAPE_MSG. */
822
823 char *
824 tapefd_rdlabel(fd, datestamp, label)
825     int fd;
826     char **datestamp;
827     char **label;
828 {
829     int rc;
830     size_t buflen;
831     char *buffer = NULL;
832     dumpfile_t file;
833     char *r = NULL;
834
835     amfree(*datestamp);
836     amfree(*label);
837     buflen = MAX_TAPE_BLOCK_BYTES;
838     buffer = alloc(buflen + 1);
839
840     if(tapefd_getinfo_fake_label(fd)) {
841         *datestamp = stralloc("X");
842         *label = stralloc(FAKE_LABEL);
843     } else if(tapefd_rewind(fd) == -1) {
844         r = stralloc2("rewinding tape: ", strerror(errno));
845     } else if((rc = tapefd_read(fd, buffer, buflen)) == -1) {
846         r = vstralloc(NOT_AMANDA_TAPE_MSG, " (",
847                       strerror(errno), ")", NULL);
848     } else if (rc == 0) {
849         r = stralloc2(NOT_AMANDA_TAPE_MSG, " (Read 0 bytes)");
850     } else {
851         /* make sure buffer is null-terminated */
852         buffer[rc] = '\0';
853
854         parse_file_header(buffer, &file, rc);
855         if(file.type != F_TAPESTART) {
856             r = stralloc(NOT_AMANDA_TAPE_MSG);
857         } else {
858             *datestamp = stralloc(file.datestamp);
859             *label = stralloc(file.name);
860         }
861     }
862     amfree(buffer);
863     errstr = newvstralloc(errstr, r, NULL);
864     return r;
865 }
866
867 char *
868 tape_rdlabel(devname, datestamp, label)
869     char *devname;
870     char **datestamp;
871     char **label;
872 {
873     int fd;
874     char *r = NULL;
875
876     if((fd = tape_open(devname, O_RDONLY)) < 0) {
877         r = vstralloc("tape_rdlabel: tape open: ",
878                       devname,
879                       ": ",
880                       strerror(errno),
881                       NULL);
882     } else
883         r = tapefd_rdlabel(fd, datestamp, label);
884
885     if(fd >= 0) {
886         tapefd_close(fd);
887     }
888     errstr = newvstralloc(errstr, r, NULL);
889     return r;
890 }
891
892 char *
893 tapefd_wrlabel(fd, datestamp, label, size)
894     int fd;
895     char *datestamp;
896     char *label;
897     unsigned int size;
898 {
899     int rc;
900     char *buffer = NULL;
901     dumpfile_t file;
902     char *r = NULL;
903
904     if(tapefd_rewind(fd) == -1) {
905         r = errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno));
906     } else {
907         fh_init(&file);
908         file.type = F_TAPESTART;
909         strncpy(file.datestamp, datestamp, sizeof(file.datestamp) - 1);
910         file.datestamp[sizeof(file.datestamp) - 1] = '\0';
911         strncpy(file.name, label, sizeof(file.name) - 1);
912         file.name[sizeof(file.name) - 1] = '\0';
913         buffer = alloc(size);
914         file.blocksize = size;
915         build_header(buffer, &file, size);
916         tapefd_setinfo_host(fd, NULL);
917         tapefd_setinfo_disk(fd, label);
918         tapefd_setinfo_level(fd, -1);
919         if((rc = tapefd_write(fd, buffer, size)) != size) {
920             r = errstr = newstralloc2(errstr,
921                                       "writing label: ",
922                                       (rc != -1) ? "short write"
923                                                  : strerror(errno));
924         }
925         amfree(buffer);
926     }
927     return r;
928 }
929
930 char *
931 tape_wrlabel(devname, datestamp, label, size)
932     char *devname;
933     char *datestamp;
934     char *label;
935     unsigned int size;
936 {
937     int fd;
938     char *r = NULL;
939
940     if((fd = tape_open(devname, O_WRONLY)) < 0) {
941         r = errstr = newstralloc2(errstr,
942                                   "writing label: ",
943                                   (errno == EACCES) ? "tape is write-protected"
944                                                     : strerror(errno));
945     } else if(tapefd_wrlabel(fd, datestamp, label, size) != NULL) {
946         r = errstr;
947     }
948     if(fd >= 0) {
949         tapefd_close(fd);
950     }
951     return r;
952 }
953
954 char *
955 tapefd_wrendmark(fd, datestamp, size)
956     int fd;
957     char *datestamp;
958     unsigned int size;
959 {
960     int rc;
961     char *buffer = NULL;
962     dumpfile_t file;
963     char *r = NULL;
964
965     fh_init(&file);
966     file.type = F_TAPEEND;
967     strncpy(file.datestamp, datestamp, sizeof(file.datestamp) - 1);
968     file.datestamp[sizeof(file.datestamp) - 1] = '\0';
969     buffer = alloc(size);
970     file.blocksize = size;
971     build_header(buffer, &file, size);
972     tapefd_setinfo_host(fd, NULL);
973     tapefd_setinfo_disk(fd, "TAPEEND");
974     tapefd_setinfo_level(fd, -1);
975
976     if((rc = tapefd_write(fd, buffer, size)) != size) {
977         r = errstr = newstralloc2(errstr, "writing endmark: ",
978                                   (rc != -1) ? "short write" : strerror(errno));
979     }
980     amfree(buffer);
981
982     return r;
983 }
984
985 char *
986 tape_wrendmark(devname, datestamp, size)
987     char *devname;
988     char *datestamp;
989     unsigned int size;
990 {
991     int fd;
992     char *r = NULL;
993
994     if((fd = tape_open(devname, O_WRONLY)) < 0) {
995         r = errstr = newstralloc2(errstr,
996                                   "writing endmark: ",
997                                   (errno == EACCES) ? "tape is write-protected"
998                                                     : strerror(errno));
999     } else if(tapefd_wrendmark(fd, datestamp, size) != NULL) {
1000         r = errstr;
1001     }
1002     if(fd >= 0) {
1003         tapefd_close(fd);
1004     }
1005     return r;
1006 }
1007
1008 char *
1009 tape_writable(devname)
1010     char *devname;
1011 {
1012     int fd = -1;
1013     char *r = NULL;
1014
1015     /* first, make sure the file exists and the permissions are right */
1016
1017     if(tape_access(devname, R_OK|W_OK) == -1) {
1018         r = errstr = newstralloc(errstr, strerror(errno));
1019     } else if((fd = tape_open(devname, O_WRONLY)) < 0) {
1020         r = errstr = newstralloc(errstr,
1021                                  (errno == EACCES) ? "tape write-protected"
1022                                                    : strerror(errno));
1023     }
1024     if(fd >= 0) {
1025         tapefd_close(fd);
1026     }
1027     return r;
1028 }
1029
1030 #ifdef TEST
1031
1032 /*
1033  * The following test program may be used to exercise I/O patterns through
1034  * the tapeio interface.  Commands may either be on the command line or
1035  * read from stdin (e.g. for a test suite).
1036  */
1037
1038 #include "token.h"
1039
1040 #if USE_RAND
1041 /* If the C library does not define random(), try to use rand() by
1042    defining USE_RAND, but then make sure you are not using hardware
1043    compression, because the low-order bits of rand() may not be that
1044    random... :-( */
1045 #define random() rand()
1046 #define srandom(seed) srand(seed)
1047 #endif
1048
1049 static char *pgm;
1050
1051 static void
1052 do_help()
1053 {
1054     fprintf(stderr, "  ?|help\n");
1055     fprintf(stderr, "  open [\"file\"|$TAPE [\"mode\":O_RDONLY]]\n");
1056     fprintf(stderr, "  read [\"records\":\"all\"]\n");
1057     fprintf(stderr, "  write [\"records\":1] [\"file#\":\"+\"] [\"record#\":\"+\"] [\"host\"] [\"disk\"] [\"level\"]\n");
1058     fprintf(stderr, "  eof|weof [\"count\":1]\n");
1059     fprintf(stderr, "  fsf [\"count\":1]\n");
1060     fprintf(stderr, "  rewind\n");
1061     fprintf(stderr, "  unload\n");
1062 }
1063
1064 static void
1065 usage()
1066 {
1067     fprintf(stderr, "usage: %s [-c cmd [args] [%% cmd [args] ...]]\n", pgm);
1068     do_help();
1069 }
1070
1071 #define TEST_BLOCKSIZE  (32 * 1024)
1072
1073 #define MAX_TOKENS      10
1074
1075 extern int optind;
1076
1077 static char *token_area[MAX_TOKENS + 1];
1078 static char **token;
1079 static int token_count;
1080
1081 static int fd = -1;
1082 static int current_file = 0;
1083 static int current_record = 0;
1084
1085 static int have_length = 0;
1086 static int length = 0;
1087
1088 static int show_timestamp = 0;
1089
1090 char write_buf[TEST_BLOCKSIZE];
1091
1092 static void
1093 do_open()
1094 {
1095     int mode;
1096     char *file;
1097
1098     if(token_count < 2
1099        || (token_count >= 2 && strcmp(token[1], "$TAPE") == 0)) {
1100         if((file = getenv("TAPE")) == NULL) {
1101             fprintf(stderr, "tape_open: no file name and $TAPE not set\n");
1102             return;
1103         }
1104     } else {
1105         file = token[1];
1106     }
1107     if(token_count > 2) {
1108         mode = atoi(token[2]);
1109     } else {
1110         mode = O_RDONLY;
1111     }
1112
1113     fprintf(stderr, "tapefd_open(\"%s\", %d): ", file, mode);
1114     if((fd = tape_open(file, mode, 0644)) < 0) {
1115         perror("");
1116     } else {
1117         fprintf(stderr, "%d (OK)\n", fd);
1118         if(have_length) {
1119             tapefd_setinfo_length(fd, length);
1120         }
1121     }
1122 }
1123
1124 static void
1125 do_close()
1126 {
1127     int result;
1128
1129     fprintf(stderr, "tapefd_close(): ");
1130     if((result = tapefd_close(fd)) < 0) {
1131         perror("");
1132     } else {
1133         fprintf(stderr, "%d (OK)\n", result);
1134     }
1135 }
1136
1137 static void
1138 do_read()
1139 {
1140     int result;
1141     int count = 0;
1142     int have_count = 0;
1143     char buf[sizeof(write_buf)];
1144     int *p;
1145     int i;
1146     char *s;
1147     time_t then;
1148     struct tm *tm;
1149
1150     if(token_count > 1 && strcmp(token[1], "all") != 0) {
1151         count = atoi(token[1]);
1152         have_count = 1;
1153     }
1154
1155     p = (int *)buf;
1156     for(i = 0; (! have_count) || (i < count); i++) {
1157         fprintf(stderr, "tapefd_read(%d): ", i);
1158         if((result = tapefd_read(fd, buf, sizeof(buf))) < 0) {
1159             perror("");
1160             break;
1161         } else if(result == 0) {
1162             fprintf(stderr, "%d (EOF)\n", result);
1163             /*
1164              * If we were not given a count, EOF breaks the loop, otherwise
1165              * we keep trying (to test read after EOF handling).
1166              */
1167             if(! have_count) {
1168                 break;
1169             }
1170         } else {
1171             if(result == sizeof(buf)) {
1172                 s = "OK";
1173             } else {
1174                 s = "short read";
1175             }
1176
1177             /*
1178              * If the amount read is really short, we may refer to junk
1179              * when displaying the record data, but things are pretty
1180              * well screwed up at this point anyway so it is not worth
1181              * the effort to deal with.
1182              */
1183             fprintf(stderr,
1184                     "%d (%s): file %d: record %d",
1185                     result,
1186                     s,
1187                     p[0],
1188                     p[1]);
1189             if(show_timestamp) {
1190                 then = p[2];
1191                 tm = localtime(&then);
1192                 fprintf(stderr,
1193                         ": %04d/%02d/%02d %02d:%02d:%02d\n",
1194                         tm->tm_year + 1900,
1195                         tm->tm_mon + 1,
1196                         tm->tm_mday,
1197                         tm->tm_hour,
1198                         tm->tm_min,
1199                         tm->tm_sec);
1200             }
1201             fputc('\n', stderr);
1202         }
1203     }
1204 }
1205
1206 static void
1207 do_write()
1208 {
1209     int result;
1210     int count;
1211     int *p;
1212     int i;
1213     char *s;
1214     time_t now;
1215     struct tm *tm;
1216
1217     if(token_count > 1) {
1218         count = atoi(token[1]);
1219     } else {
1220         count = 1;
1221     }
1222
1223     if(token_count > 2 && strcmp(token[2], "+") != 0) {
1224         current_file = atoi(token[2]);
1225     }
1226
1227     if(token_count > 3 && strcmp(token[3], "+") != 0) {
1228         current_record = atoi(token[3]);
1229     }
1230
1231     if(token_count > 4 && token[4][0] != '\0') {
1232         tapefd_setinfo_host(fd, token[4]);
1233     }
1234
1235     if(token_count > 5 && token[5][0] != '\0') {
1236         tapefd_setinfo_disk(fd, token[5]);
1237     }
1238
1239     if(token_count > 6 && token[6][0] != '\0') {
1240         tapefd_setinfo_level(fd, atoi(token[6]));
1241     }
1242
1243     p = (int *)write_buf;
1244     time(&now);
1245     p[2] = now;
1246     tm = localtime(&now);
1247     for(i = 0; i < count; i++, current_record++) {
1248         p[0] = current_file;
1249         p[1] = current_record;
1250         fprintf(stderr, "tapefd_write(%d): ", i);
1251         if((result = tapefd_write(fd, write_buf, sizeof(write_buf))) < 0) {
1252             perror("");
1253             break;
1254         } else {
1255             if(result == sizeof(write_buf)) {
1256                 s = "OK";
1257             } else {
1258                 s = "short write";
1259             }
1260             fprintf(stderr,
1261                     "%d (%s): file %d: record %d",
1262                     result,
1263                     s,
1264                     p[0],
1265                     p[1]);
1266             if(show_timestamp) {
1267                 fprintf(stderr,
1268                         ": %04d/%02d/%02d %02d:%02d:%02d\n",
1269                         tm->tm_year + 1900,
1270                         tm->tm_mon + 1,
1271                         tm->tm_mday,
1272                         tm->tm_hour,
1273                         tm->tm_min,
1274                         tm->tm_sec);
1275             }
1276             fputc('\n', stderr);
1277         }
1278     }
1279 }
1280
1281 static void
1282 do_fsf()
1283 {
1284     int result;
1285     int count;
1286
1287     if(token_count > 1) {
1288         count = atoi(token[1]);
1289     } else {
1290         count = 1;
1291     }
1292
1293     fprintf(stderr, "tapefd_fsf(%d): ", count);
1294     if((result = tapefd_fsf(fd, count)) < 0) {
1295         perror("");
1296     } else {
1297         fprintf(stderr, "%d (OK)\n", result);
1298         current_file += count;
1299         current_record = 0;
1300     }
1301 }
1302
1303 static void
1304 do_weof()
1305 {
1306     int result;
1307     int count;
1308
1309     if(token_count > 1) {
1310         count = atoi(token[1]);
1311     } else {
1312         count = 1;
1313     }
1314
1315     fprintf(stderr, "tapefd_weof(%d): ", count);
1316     if((result = tapefd_weof(fd, count)) < 0) {
1317         perror("");
1318     } else {
1319         fprintf(stderr, "%d (OK)\n", result);
1320         current_file += count;
1321         current_record = 0;
1322     }
1323 }
1324
1325 static void
1326 do_rewind()
1327 {
1328     int result;
1329
1330     fprintf(stderr, "tapefd_rewind(): ");
1331     if((result = tapefd_rewind(fd)) < 0) {
1332         perror("");
1333     } else {
1334         fprintf(stderr, "%d (OK)\n", result);
1335         current_file = 0;
1336         current_record = 0;
1337     }
1338 }
1339
1340 static void
1341 do_unload()
1342 {
1343     int result;
1344
1345     fprintf(stderr, "tapefd_unload(): ");
1346     if((result = tapefd_unload(fd)) < 0) {
1347         perror("");
1348     } else {
1349         fprintf(stderr, "%d (OK)\n", result);
1350         current_file = -1;
1351         current_record = -1;
1352     }
1353 }
1354
1355 struct cmd {
1356     char *name;
1357     int min_chars;
1358     void (*func)();
1359 } cmd[] = {
1360     { "?",              0,      do_help },
1361     { "help",           0,      do_help },
1362     { "eof",            0,      do_weof },
1363     { "weof",           0,      do_weof },
1364     { "fsf",            0,      do_fsf },
1365     { "rewind",         0,      do_rewind },
1366     { "offline",        0,      do_unload },
1367     { "open",           0,      do_open },
1368     { "close",          0,      do_close },
1369     { "read",           0,      do_read },
1370     { "write",          0,      do_write },
1371     { NULL,             0,      NULL }
1372 };
1373
1374 int
1375 main(argc, argv)
1376     int argc;
1377     char **argv;
1378 {
1379     int ch;
1380     int cmdline = 0;
1381     char *line = NULL;
1382     char *s;
1383     int i;
1384     int j;
1385     time_t now;
1386
1387     /* Don't die when child closes pipe */
1388     signal(SIGPIPE, SIG_IGN);
1389
1390     if((pgm = strrchr(argv[0], '/')) != NULL) {
1391         pgm++;
1392     } else {
1393         pgm = argv[0];
1394     }
1395
1396     /*
1397      * Compute the minimum abbreviation for each command.
1398      */
1399     for(i = 0; cmd[i].name; i++) {
1400         cmd[i].min_chars = 1;
1401         while(1) {
1402             for(j = 0; cmd[j].name; j++) {
1403                 if(i == j) {
1404                     continue;
1405                 }
1406                 if(0 == strncmp(cmd[i].name, cmd[j].name, cmd[i].min_chars)) {
1407                     break;
1408                 }
1409             }
1410             if(0 == cmd[j].name) {
1411                 break;
1412             }
1413             cmd[i].min_chars++;
1414         }
1415     }
1416
1417     /*
1418      * Process the command line flags.
1419      */
1420     while((ch = getopt(argc, argv, "hcl:t")) != EOF) {
1421         switch (ch) {
1422         case 'c':
1423             cmdline = 1;
1424             break;
1425         case 'l':
1426             have_length = 1;
1427             length = atoi(optarg);
1428             j = strlen(optarg);
1429             if(j > 0) {
1430                 switch(optarg[j-1] ) {
1431                 case 'k':                               break;
1432                 case 'b': length /= 2;                  break;
1433                 case 'M': length *= 1024;               break;
1434                 default:  length /= 1024;               break;
1435                 }
1436             } else {
1437                 length /= 1024;
1438             }
1439             break;
1440         case 't':
1441             show_timestamp = 1;
1442             break;
1443         case 'h':
1444         default:
1445             usage();
1446             return 1;
1447         }
1448     }
1449
1450     /*
1451      * Initialize the write buffer.
1452      */
1453     time(&now);
1454     srandom(now);
1455     for(j = 0; j < sizeof(write_buf); j++) {
1456         write_buf[j] = (char)random();
1457     }
1458
1459     /*
1460      * Do the tests.
1461      */
1462     token = token_area + 1;
1463     token_area[0] = "";                         /* if cmdline */
1464     while(1) {
1465         if(cmdline) {
1466             for(token_count = 1;
1467                 token_count < (sizeof(token_area) / sizeof(token_area[0]))
1468                 && optind < argc;
1469                 token_count++, optind++) {
1470                 if(strcmp(argv[optind], "%") == 0) {
1471                     optind++;
1472                     break;
1473                 }
1474                 token_area[token_count] = argv[optind];
1475             }
1476             token_count--;
1477             if(token_count == 0 && optind >= argc) {
1478                 break;
1479             }
1480         } else {
1481             if((line = areads(0)) == NULL) {
1482                 break;
1483             }
1484             if((s = strchr(line, '#')) != NULL) {
1485                 *s = '\0';
1486             }
1487             s = line + strlen(line) - 1;
1488             while(s >= line && isspace(*s)) {
1489                 *s-- = '\0';
1490             }
1491             token_count = split(line,
1492                                 token_area,
1493                                 sizeof(token_area) / sizeof(token_area[0]),
1494                                 " ");
1495         }
1496         amfree(line);
1497
1498         /*
1499          * Truncate tokens at first comment indicator, then test for
1500          * empty command.
1501          */
1502         for(i = 0; i < token_count; i++) {
1503             if(token[i][0] == '#') {
1504                 token_count = i;
1505                 break;
1506             }
1507         }
1508         if(token_count <= 0) {
1509             continue;                           /* blank/comment input line */
1510         }
1511
1512         /*
1513          * Find the command to run, the do it.
1514          */
1515         j = strlen(token[0]);
1516         for(i = 0; cmd[i].name; i++) {
1517             if(strncmp(cmd[i].name, token[0], j) == 0
1518                && j >= cmd[i].min_chars) {
1519                 break;
1520             }
1521         }
1522         if(cmd[i].name == NULL) {
1523             fprintf(stderr, "%s: unknown command: %s\n", pgm, token[0]);
1524             exit(1);
1525         }
1526         (*cmd[i].func)();
1527     }
1528
1529     return 0;
1530 }
1531
1532 #endif /* TEST */