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