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