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