69fdb5cac683d9602ed30515e363a171ceeaca30
[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 = newvstrallocf(errstr,
769                                   _("tape_rewind: tape open: %s: %s"),
770                                   devname, strerror(errno));
771     } else if(tapefd_rewind(fd) == -1) {
772         r = errstr = newvstrallocf(errstr,
773                                   _("tape_rewind: rewinding tape: %s: %s"),
774                                   devname,
775                                   strerror(errno));
776     }
777     if(fd >= 0) {
778         tapefd_close(fd);
779     }
780     return r;
781 }
782
783 char *
784 tape_unload(
785     char *devname)
786 {
787     int fd;
788     char *r = NULL;
789
790     if((fd = tape_open(devname, O_RDONLY)) < 0) {
791         r = errstr = newvstrallocf(errstr,
792                                   _("tape_unload: tape open: %s: %s"),
793                                   devname,
794                                   strerror(errno));
795     } else if(tapefd_unload(fd) == -1) {
796         r = errstr = newvstrallocf(errstr,
797                                   _("tape_unload: unloading tape: %s: %s"),
798                                   devname,
799                                   strerror(errno));
800     }
801     if(fd >= 0) {
802         tapefd_close(fd);
803     }
804     return r;
805 }
806
807 char *
808 tape_fsf(
809     char *devname,
810     off_t count)
811 {
812     int fd;
813     char *r = NULL;
814
815     if((fd = tape_open(devname, O_RDONLY)) < 0) {
816         r = errstr = newvstrallocf(errstr,
817                                   _("tape_fsf: tape open: %s: %s"),
818                                   devname,
819                                   strerror(errno));
820     } else if(tapefd_fsf(fd, count) == -1) {
821         r = errstr = newvstrallocf(errstr,
822                                   plural(_("tape_fsf: fsf %lld file: %s"),
823                                          _("tape_fsf: fsf %lld files: %s"),
824                                          count),
825                                   (long long)count, strerror(errno));
826     }
827     if(fd >= 0) {
828         tapefd_close(fd);
829     }
830     return r;
831 }
832
833 /* Reads the tape label, like you expect. If failure, returns an error
834    string. If the tape might not be an Amanda tape, the returned
835    string will start with NOT_AMANDA_TAPE_MSG. */
836
837 char *
838 tapefd_rdlabel(
839     int fd,
840     char **datestamp,
841     char **label)
842 {
843     ssize_t rc;
844     size_t buflen;
845     char *buffer = NULL;
846     dumpfile_t file;
847     char *r = NULL;
848
849     amfree(*datestamp);
850     amfree(*label);
851     buflen = getconf_readblocksize() * 1024;
852     buffer = alloc(buflen + 1);
853
854     if(tapefd_getinfo_fake_label(fd)) {
855         *datestamp = stralloc("X");
856         *label = stralloc(FAKE_LABEL);
857     } else if(tapefd_rewind(fd) == -1) {
858         r = vstrallocf(_("rewinding tape: %s"), strerror(errno));
859     } else if((rc = tapefd_read(fd, buffer, buflen)) == -1) {
860         r = vstrallocf(_(NOT_AMANDA_TAPE_MSG "(%s)"), strerror(errno));
861     } else if (rc == 0) {
862         r = vstrallocf(_(NOT_AMANDA_TAPE_MSG " (Read 0 bytes)"));
863     } else {
864         /* make sure buffer is null-terminated */
865         buffer[rc] = '\0';
866
867         parse_file_header(buffer, &file, (size_t)rc);
868         if(file.type != F_TAPESTART) {
869             r = vstrallocf(NOT_AMANDA_TAPE_MSG);
870         } else {
871             *datestamp = stralloc(file.datestamp);
872             *label = stralloc(file.name);
873         }
874     }
875     amfree(buffer);
876     if (r)
877         errstr = newvstrallocf(errstr, "%s", r);
878     return r;
879 }
880
881 char *
882 tape_rdlabel(
883     char *devname,
884     char **datestamp,
885     char **label)
886 {
887     int fd;
888     char *r = NULL;
889
890     if((fd = tape_open(devname, O_RDONLY)) < 0) {
891         r = vstrallocf(_("tape_rdlabel: tape open: %s: %s"),
892                       devname,
893                       strerror(errno));
894     } else
895         r = tapefd_rdlabel(fd, datestamp, label);
896
897     if(fd >= 0) {
898         tapefd_close(fd);
899     }
900     if (r)
901         errstr = newvstrallocf(errstr, "%s", r);
902     return r;
903 }
904
905 char *
906 tapefd_wrlabel(
907     int fd,
908     char *datestamp,
909     char *label,
910     size_t size)
911 {
912     ssize_t rc;
913     char *buffer = NULL;
914     dumpfile_t file;
915     char *r = NULL;
916
917     if(tapefd_rewind(fd) == -1) {
918         r = errstr = newvstrallocf(errstr, _("rewinding tape: %s"),
919                         strerror(errno));
920     } else {
921         fh_init(&file);
922         file.type = F_TAPESTART;
923         strncpy(file.datestamp, datestamp, SIZEOF(file.datestamp) - 1);
924         file.datestamp[SIZEOF(file.datestamp) - 1] = '\0';
925         strncpy(file.name, label, SIZEOF(file.name) - 1);
926         file.name[SIZEOF(file.name) - 1] = '\0';
927         file.blocksize = size;
928         buffer = build_header(&file, size);
929         tapefd_setinfo_host(fd, NULL);
930         tapefd_setinfo_disk(fd, label);
931         tapefd_setinfo_level(fd, -1);
932         if((rc = tapefd_write(fd, buffer, size)) != (ssize_t)size) {
933             if (rc != 1) {
934                 r = errstr = newvstrallocf(errstr,
935                                 _("writing label: short write"));
936             } else {
937                 r = errstr = newvstrallocf(errstr,
938                                 _("writing label: %s"), strerror(errno));
939             }
940         }
941         amfree(buffer);
942     }
943     return r;
944 }
945
946 char *
947 tape_wrlabel(
948     char *devname,
949     char *datestamp,
950     char *label,
951     size_t size)
952 {
953     int fd;
954     char *r = NULL;
955
956     if((fd = tape_open(devname, O_WRONLY)) < 0) {
957         if (errno == EACCES) {
958             r = errstr = newvstrallocf(errstr,
959                                   _("writing label: tape is write-protected"));
960         } else {
961             r = errstr = newvstrallocf(errstr,
962                                   _("writing label: %s"), strerror(errno));
963         }
964     } else if(tapefd_wrlabel(fd, datestamp, label, size) != NULL) {
965         r = errstr;
966     }
967     if(fd >= 0) {
968         tapefd_close(fd);
969     }
970     return r;
971 }
972
973 char *
974 tapefd_wrendmark(
975     int fd,
976     char *datestamp,
977     size_t size)
978 {
979     ssize_t rc;
980     char *buffer = NULL;
981     dumpfile_t file;
982     char *r = NULL;
983
984     fh_init(&file);
985     file.type = F_TAPEEND;
986     strncpy(file.datestamp, datestamp, SIZEOF(file.datestamp) - 1);
987     file.datestamp[SIZEOF(file.datestamp) - 1] = '\0';
988     file.blocksize = size;
989     buffer = build_header(&file, size);
990     tapefd_setinfo_host(fd, NULL);
991     tapefd_setinfo_disk(fd, "TAPEEND");
992     tapefd_setinfo_level(fd, -1);
993
994     if((rc = tapefd_write(fd, buffer, size)) != (ssize_t)size) {
995         if (rc != 1) {
996             r = errstr = newvstrallocf(errstr,
997                                 _("writing endmark: short write"));
998         } else {
999             r = errstr = newvstrallocf(errstr,
1000                                 _("writing endmark: %s"), strerror(errno));
1001         }
1002     }
1003     amfree(buffer);
1004
1005     return r;
1006 }
1007
1008 char *
1009 tape_wrendmark(
1010     char *devname,
1011     char *datestamp,
1012     size_t size)
1013 {
1014     int fd;
1015     char *r = NULL;
1016
1017     if((fd = tape_open(devname, O_WRONLY)) < 0) {
1018         if (errno == EACCES) {
1019             r = errstr = newvstrallocf(errstr,
1020                                   _("writing endmark: tape is write-protected"));
1021         } else {
1022             r = errstr = newvstrallocf(errstr,
1023                                   _("writing endmark: %s"), strerror(errno));
1024         }
1025     } else if(tapefd_wrendmark(fd, datestamp, size) != NULL) {
1026         r = errstr;
1027     }
1028     if(fd >= 0) {
1029         tapefd_close(fd);
1030     }
1031     return r;
1032 }
1033
1034 char *
1035 tape_writable(
1036     char *devname)
1037 {
1038     int fd = -1;
1039     char *r = NULL;
1040
1041     /* first, make sure the file exists and the permissions are right */
1042
1043     if(tape_access(devname, R_OK|W_OK) == -1) {
1044         r = errstr = newvstrallocf(errstr, "%s", strerror(errno));
1045     } else if((fd = tape_open(devname, O_WRONLY)) < 0) {
1046         if (errno == EACCES) {
1047             r = errstr = newvstrallocf(errstr, _("tape is write-protected"));
1048         } else {
1049             r = errstr = newvstrallocf(errstr, "%s", strerror(errno));
1050         }
1051     }
1052     if(fd >= 0) {
1053         tapefd_close(fd);
1054     }
1055     return r;
1056 }
1057
1058 ssize_t
1059 getconf_readblocksize(void)
1060 {
1061     tapetype_t *tape;
1062     char       *conf_tapetype;
1063
1064     conf_tapetype = getconf_str(CNF_TAPETYPE);
1065
1066     if (!conf_tapetype || strlen(conf_tapetype) == 0)
1067         return MAX_TAPE_BLOCK_KB;
1068
1069     tape = lookup_tapetype(conf_tapetype);
1070     if (!tape)
1071         return MAX_TAPE_BLOCK_KB;
1072
1073     return tapetype_get_readblocksize(tape);
1074 }
1075
1076 #ifdef TEST
1077
1078 /*
1079  * The following test program may be used to exercise I/O patterns through
1080  * the tapeio interface.  Commands may either be on the command line or
1081  * read from stdin (e.g. for a test suite).
1082  */
1083
1084 #include "token.h"
1085
1086 #if USE_RAND
1087 /* If the C library does not define random(), try to use rand() by
1088    defining USE_RAND, but then make sure you are not using hardware
1089    compression, because the low-order bits of rand() may not be that
1090    random... :-( */
1091 #define random() rand()
1092 #define srandom(seed) srand(seed)
1093 #endif
1094
1095 static char *pgm;
1096
1097 static void
1098 do_help(void)
1099 {
1100     g_fprintf(stderr, _("  ?|help\n"));
1101     g_fprintf(stderr, _("  open [\"file\"|$TAPE [\"mode\":O_RDONLY]]\n"));
1102     g_fprintf(stderr, _("  read [\"records\":\"all\"]\n"));
1103     g_fprintf(stderr, _("  write [\"records\":1] [\"file#\":\"+\"] [\"record#\":\"+\"] [\"host\"] [\"disk\"] [\"level\"]\n"));
1104     g_fprintf(stderr, _("  eof|weof [\"count\":1]\n"));
1105     g_fprintf(stderr, _("  fsf [\"count\":1]\n"));
1106     g_fprintf(stderr, _("  rewind\n"));
1107     g_fprintf(stderr, _("  unload\n"));
1108 }
1109
1110 static void
1111 usage(void)
1112 {
1113     g_fprintf(stderr, _("usage: %s [-c cmd [args] [%% cmd [args] ...]]\n"), pgm);
1114     do_help();
1115 }
1116
1117 #define TEST_BLOCKSIZE  (32 * 1024)
1118
1119 #define MAX_TOKENS      10
1120
1121 extern int optind;
1122
1123 static char *token_area[MAX_TOKENS + 1];
1124 static char **token;
1125 static int token_count;
1126
1127 static int fd = -1;
1128 static off_t current_file = (off_t)0;
1129 static off_t current_record = (off_t)0;
1130
1131 static int have_length = 0;
1132 static int length = (off_t)0;
1133
1134 static int show_timestamp = 0;
1135
1136 char write_buf[TEST_BLOCKSIZE];
1137
1138 static void
1139 do_open(void)
1140 {
1141     mode_t mode;
1142     char *file;
1143
1144     if(token_count < 2
1145        || (token_count >= 2 && strcmp(token[1], "$TAPE") == 0)) {
1146         if((file = getenv("TAPE")) == NULL) {
1147             g_fprintf(stderr, _("tape_open: no file name and $TAPE not set\n"));
1148             return;
1149         }
1150     } else {
1151         file = token[1];
1152     }
1153     if(token_count > 2) {
1154         mode = atoi(token[2]);
1155     } else {
1156         mode = O_RDONLY;
1157     }
1158
1159     g_fprintf(stderr, _("tapefd_open(\"%s\", %d): "), file, mode);
1160     if((fd = tape_open(file, mode, 0644)) < 0) {
1161         perror("");
1162     } else {
1163         g_fprintf(stderr, _("%d (OK)\n"), fd);
1164         if(have_length) {
1165             tapefd_setinfo_length(fd, length);
1166         }
1167     }
1168 }
1169
1170 static void
1171 do_close(void)
1172 {
1173     int result;
1174
1175     g_fprintf(stderr, _("tapefd_close(): "));
1176     if((result = tapefd_close(fd)) < 0) {
1177         perror("");
1178     } else {
1179         g_fprintf(stderr, _("%d (OK)\n"), result);
1180     }
1181 }
1182
1183 static void
1184 do_read(void)
1185 {
1186     ssize_t     result;
1187     off_t count = (off_t)0;
1188     int have_count = 0;
1189     char buf[SIZEOF(write_buf)];
1190     int *p;
1191     off_t i;
1192     char *s;
1193     time_t then;
1194     struct tm *tm;
1195
1196     if(token_count > 1 && strcmp(token[1], "all") != 0) {
1197         count = OFF_T_ATOI(token[1]);
1198         have_count = 1;
1199     }
1200
1201     p = (int *)buf;
1202     for(i = 0; (! have_count) || (i < count); i++) {
1203         g_fprintf(stderr, _("tapefd_read(%lld): "), (long long)i);
1204         if((result = tapefd_read(fd, buf, SIZEOF(buf))) < 0) {
1205             perror("");
1206             break;
1207         } else if(result == 0) {
1208             g_fprintf(stderr,  _("%zd (EOF)\n"), result);
1209             /*
1210              * If we were not given a count, EOF breaks the loop, otherwise
1211              * we keep trying (to test read after EOF handling).
1212              */
1213             if(! have_count) {
1214                 break;
1215             }
1216         } else {
1217             if(result == (ssize_t)sizeof(buf)) {
1218                 s = _("OK");
1219             } else {
1220                 s = _("short read");
1221             }
1222
1223             /*
1224              * If the amount read is really short, we may refer to junk
1225              * when displaying the record data, but things are pretty
1226              * well screwed up at this point anyway so it is not worth
1227              * the effort to deal with.
1228              */
1229             g_fprintf(stderr,
1230                     _("%zd (%s): file %d: record %d"),
1231                     result,
1232                     s,
1233                     p[0],
1234                     p[1]);
1235             if(show_timestamp) {
1236                 then = p[2];
1237                 tm = localtime(&then);
1238                 g_fprintf(stderr,
1239                         ": %04d/%02d/%02d %02d:%02d:%02d\n",
1240                         tm->tm_year + 1900,
1241                         tm->tm_mon + 1,
1242                         tm->tm_mday,
1243                         tm->tm_hour,
1244                         tm->tm_min,
1245                         tm->tm_sec);
1246             }
1247             fputc('\n', stderr);
1248         }
1249     }
1250 }
1251
1252 static void
1253 do_write(void)
1254 {
1255     int result;
1256     off_t count;
1257     off_t *p;
1258     off_t i;
1259     char *s;
1260     time_t now;
1261     struct tm *tm;
1262
1263     if(token_count > 1) {
1264         count = OFF_T_ATOI(token[1]);
1265     } else {
1266         count = (off_t)1;
1267     }
1268
1269     if(token_count > 2 && strcmp(token[2], "+") != 0) {
1270         current_file = OFF_T_ATOI(token[2]);
1271     }
1272
1273     if(token_count > 3 && strcmp(token[3], "+") != 0) {
1274         current_record = OFF_T_ATOI(token[3]);
1275     }
1276
1277     if(token_count > 4 && token[4][0] != '\0') {
1278         tapefd_setinfo_host(fd, token[4]);
1279     }
1280
1281     if(token_count > 5 && token[5][0] != '\0') {
1282         tapefd_setinfo_disk(fd, token[5]);
1283     }
1284
1285     if(token_count > 6 && token[6][0] != '\0') {
1286         tapefd_setinfo_level(fd, atoi(token[6]));
1287     }
1288
1289     p = (off_t *)write_buf;
1290     time(&now);
1291     p[2] = now;
1292     tm = localtime(&now);
1293     for(i = 0; i < count; i++, (current_record += (off_t)1)) {
1294         p[0] = current_file;
1295         p[1] = current_record;
1296         g_fprintf(stderr, _("tapefd_write(%lld): "), i);
1297         if((result = tapefd_write(fd, write_buf, SIZEOF(write_buf))) < 0) {
1298             perror("");
1299             break;
1300         } else {
1301             if(result == (ssize_t)sizeof(write_buf)) {
1302                 s = _("OK");
1303             } else {
1304                 s = _("short write");
1305             }
1306             g_fprintf(stderr,
1307                     _("%d (%s): file %lld: record %lld"),
1308                     result,
1309                     s,
1310                     p[0],
1311                     p[1]);
1312             if(show_timestamp) {
1313                 g_fprintf(stderr,
1314                         ": %04d/%02d/%02d %02d:%02d:%02d\n",
1315                         tm->tm_year + 1900,
1316                         tm->tm_mon + 1,
1317                         tm->tm_mday,
1318                         tm->tm_hour,
1319                         tm->tm_min,
1320                         tm->tm_sec);
1321             }
1322             fputc('\n', stderr);
1323         }
1324     }
1325 }
1326
1327 static void
1328 do_fsf(void)
1329 {
1330     int result;
1331     off_t count;
1332
1333     if(token_count > 1) {
1334         count = OFF_T_ATOI(token[1]);
1335     } else {
1336         count = (off_t)1;
1337     }
1338
1339     g_fprintf(stderr, _("tapefd_fsf(%lld): "), (long long)count);
1340     if((result = tapefd_fsf(fd, count)) < 0) {
1341         perror("");
1342     } else {
1343         g_fprintf(stderr, _("%d (OK)\n"), result);
1344         current_file += count;
1345         current_record = (off_t)0;
1346     }
1347 }
1348
1349 static void
1350 do_weof(void)
1351 {
1352     int result;
1353     off_t count;
1354
1355     if(token_count > 1) {
1356         count = OFF_T_ATOI(token[1]);
1357     } else {
1358         count = (off_t)1;
1359     }
1360
1361     g_fprintf(stderr, _("tapefd_weof(%lld): "), count);
1362     if((result = tapefd_weof(fd, count)) < 0) {
1363         perror("");
1364     } else {
1365         g_fprintf(stderr, _("%d (OK)\n"), result);
1366         current_file += count;
1367         current_record = (off_t)0;
1368     }
1369 }
1370
1371 static void
1372 do_rewind(void)
1373 {
1374     int result;
1375
1376     g_fprintf(stderr, _("tapefd_rewind(): "));
1377     if((result = tapefd_rewind(fd)) < 0) {
1378         perror("");
1379     } else {
1380         g_fprintf(stderr, _("%d (OK)\n"), result);
1381         current_file = (off_t)0;
1382         current_record = (off_t)0;
1383     }
1384 }
1385
1386 static void
1387 do_unload(void)
1388 {
1389     int result;
1390
1391     g_fprintf(stderr, _("tapefd_unload(): "));
1392     if((result = tapefd_unload(fd)) < 0) {
1393         perror("");
1394     } else {
1395         g_fprintf(stderr, _("%d (OK)\n"), result);
1396         current_file = (off_t)-1;
1397         current_record = (off_t)-1;
1398     }
1399 }
1400
1401 struct cmd {
1402     char *name;
1403     int min_chars;
1404     void (*func)(void);
1405 } cmd[] = {
1406     { "?",              0,      do_help },
1407     { "help",           0,      do_help },
1408     { "eof",            0,      do_weof },
1409     { "weof",           0,      do_weof },
1410     { "fsf",            0,      do_fsf },
1411     { "rewind",         0,      do_rewind },
1412     { "offline",        0,      do_unload },
1413     { "open",           0,      do_open },
1414     { "close",          0,      do_close },
1415     { "read",           0,      do_read },
1416     { "write",          0,      do_write },
1417     { NULL,             0,      NULL }
1418 };
1419
1420 int
1421 main(
1422     int         argc,
1423     char **     argv)
1424 {
1425     int ch;
1426     int cmdline = 0;
1427     char *line = NULL;
1428     char *s;
1429     int i;
1430     int j;
1431     time_t now;
1432
1433     /*
1434      * Configure program for internationalization:
1435      *   1) Only set the message locale for now.
1436      *   2) Set textdomain for all amanda related programs to "amanda"
1437      *      We don't want to be forced to support dozens of message catalogs.
1438      */  
1439     setlocale(LC_MESSAGES, "C");
1440     textdomain("amanda"); 
1441
1442     /* Don't die when child closes pipe */
1443     signal(SIGPIPE, SIG_IGN);
1444
1445     if((pgm = strrchr(argv[0], '/')) != NULL) {
1446         pgm++;
1447     } else {
1448         pgm = argv[0];
1449     }
1450
1451     /*
1452      * Compute the minimum abbreviation for each command.
1453      */
1454     for(i = 0; cmd[i].name; i++) {
1455         cmd[i].min_chars = 1;
1456         while(1) {
1457             for(j = 0; cmd[j].name; j++) {
1458                 if(i == j) {
1459                     continue;
1460                 }
1461                 if(0 == strncmp(cmd[i].name, cmd[j].name, cmd[i].min_chars)) {
1462                     break;
1463                 }
1464             }
1465             if(0 == cmd[j].name) {
1466                 break;
1467             }
1468             cmd[i].min_chars++;
1469         }
1470     }
1471
1472     /*
1473      * Process the command line flags.
1474      */
1475     while((ch = getopt(argc, argv, "hcl:t")) != EOF) {
1476         switch (ch) {
1477         case 'c':
1478             cmdline = 1;
1479             break;
1480         case 'l':
1481             have_length = 1;
1482             length = OFF_T_ATOI(optarg);
1483             j = strlen(optarg);
1484             if(j > 0) {
1485                 switch(optarg[j-1] ) {
1486                 case 'k':                               break;
1487                 case 'b': length /= (off_t)2;           break;
1488                 case 'M': length *= (off_t)1024;        break;
1489                 default:  length /= (off_t)1024;        break;
1490                 }
1491             } else {
1492                 length /= (off_t)1024;
1493             }
1494             break;
1495         case 't':
1496             show_timestamp = 1;
1497             break;
1498         case 'h':
1499         default:
1500             usage();
1501             return 1;
1502         }
1503     }
1504
1505     /*
1506      * Initialize the write buffer.
1507      */
1508     time(&now);
1509     srandom(now);
1510     for(j = 0; j < (int)SIZEOF(write_buf); j++) {
1511         write_buf[j] = (char)random();
1512     }
1513
1514     /*
1515      * Do the tests.
1516      */
1517     token = token_area + 1;
1518     token_area[0] = "";                         /* if cmdline */
1519     while(1) {
1520         if(cmdline) {
1521             for(token_count = 1;
1522                 token_count < (int)(SIZEOF(token_area) / SIZEOF(token_area[0]))
1523                 && optind < argc;
1524                 token_count++, optind++) {
1525                 if(strcmp(argv[optind], "%") == 0) {
1526                     optind++;
1527                     break;
1528                 }
1529                 token_area[token_count] = argv[optind];
1530             }
1531             token_count--;
1532             if(token_count == 0 && optind >= argc) {
1533                 break;
1534             }
1535         } else {
1536             if((line = areads(0)) == NULL) {
1537                 break;
1538             }
1539             if((s = strchr(line, '#')) != NULL) {
1540                 *s = '\0';
1541             }
1542             s = line + strlen(line) - 1;
1543             while(s >= line && isspace(*s)) {
1544                 *s-- = '\0';
1545             }
1546             token_count = split(line,
1547                                 token_area,
1548                                 SIZEOF(token_area) / SIZEOF(token_area[0]),
1549                                 " ");
1550         }
1551         amfree(line);
1552
1553         /*
1554          * Truncate tokens at first comment indicator, then test for
1555          * empty command.
1556          */
1557         for(i = 0; i < token_count; i++) {
1558             if(token[i][0] == '#') {
1559                 token_count = i;
1560                 break;
1561             }
1562         }
1563         if(token_count <= 0) {
1564             continue;                           /* blank/comment input line */
1565         }
1566
1567         /*
1568          * Find the command to run, the do it.
1569          */
1570         j = strlen(token[0]);
1571         for(i = 0; cmd[i].name; i++) {
1572             if(strncmp(cmd[i].name, token[0], j) == 0
1573                && j >= cmd[i].min_chars) {
1574                 break;
1575             }
1576         }
1577         if(cmd[i].name == NULL) {
1578             g_fprintf(stderr, _("%s: unknown command: %s\n"), pgm, token[0]);
1579             exit(1);
1580         }
1581         (*cmd[i].func)();
1582     }
1583
1584     return 0;
1585 }
1586
1587 #endif /* TEST */