Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / butm / file_tm.c
CommitLineData
805e021f
CE
1/*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10#include <afsconfig.h>
11#include <afs/param.h>
12
13#include <roken.h>
14
15#ifdef HAVE_SYS_WAIT_H
16#include <sys/wait.h>
17#endif
18
19#include <limits.h>
20#include <ctype.h>
21
22#include <lwp.h>
23#include <afs/com_err.h>
24#include <afs/butm.h>
25#include <afs/usd.h>
26
27#include "error_macros.h"
28#include "butm_prototypes.h"
29
30#ifdef O_LARGEFILE
31typedef off64_t osi_lloff_t;
32#else /* O_LARGEFILE */
33#ifdef AFS_HAVE_LLSEEK
34typedef offset_t osi_lloff_t;
35#else /* AFS_HAVE_LLSEEK */
36typedef off_t osi_lloff_t;
37#endif /* AFS_HAVE_LLSEEK */
38#endif /* O_LARGEFILE */
39
40extern int isafile;
41
42#define FILE_MAGIC 1000000007 /* s/w file mark */
43#define FILE_BEGIN 0 /* byte field in file mark */
44#define FILE_FMEND 1 /* byte field in file mark */
45#define FILE_EOD -1 /* byte field in file mark */
46#define TAPE_MAGIC 1100000009 /* tape label block */
47#define BLOCK_MAGIC 1100000005 /* file data block */
48#ifdef AFS_PTHREAD_ENV
49#define POLL()
50#define SLEEP(s) sleep(s)
51#else
52#define POLL() IOMGR_Poll()
53#define SLEEP(s) IOMGR_Sleep(s)
54#endif
55
56/* Notes: (PA)
57 *
58 * 1) filemarks and block marks have the magic,bytes fields reversed. This
59 * is undoubtedly a bug. Also note that the two structures have
60 * inconsistent types, overlaying int and afs_int32.
61 * 2) When a volume is dumped, if the volume is locked, the dump will produce
62 * an anomalous tape format of the form:
63 * s/w file begin mark
64 * volume header
65 * s/w file end mark
66 * The design of the original butm code means that this cannot be
67 * handled correctly. The code is modified so that ReadFileData
68 * returns BUTM_ENDVOLUME if it encounters the s/w filemark.
69 */
70
71/* data organization on tape:
72 * all writes are in blocks of BUTM_BLOCKSIZE (= sizeof(blockMark) + BUTM_BLKSIZE)
73 * blockMark contains a magic number and counts of real data bytes
74 * written out in the block.
75 *
76 * each file is preceeded by a fileMark, which acts as the file
77 * delimiter. A file consists of contigous data blocks. TM does
78 * understand or interrpet the data in data blocks.
79 *
80 * The tape begins with a tape label and ends with EOF file markers
81 * in succession (2 or 4 of them ).
82 */
83
84
85struct fileMark { /* in network byte order */
86 afs_int32 magic;
87 afs_uint32 nBytes;
88};
89
90struct tapeLabel {
91 afs_int32 magic;
92 struct butm_tapeLabel label;
93};
94
95struct progress {
96 usd_handle_t fid; /* file id of simulated tape */
97 afs_int32 mountId; /* current mountId */
98 afs_int32 reading; /* read file operation in progress */
99 afs_int32 writing; /* write file operation in progress */
100};
101
102static struct configuration {
103 char tapedir[64]; /* directory to create "tapes" */
104 afs_int32 mountId; /* for detecting simultaneous mounts */
105 afs_uint32 tapeSize; /* size of simulated tapes */
106 afs_uint32 fileMarkSize; /* size of file mark, bytes */
107 afs_int32 portOffset; /* port + portOffset is used by TC to listen */
108} config;
109
110static char *whoami = "file_tm";
111char tapeBlock[BUTM_BLOCKSIZE]; /* Tape buffer for reads and writes */
112
113#define BLOCK_LABEL 0 /* read/write a tape label */
114#define BLOCK_FMBEGIN 1 /* read/write a begin FileMark */
115#define BLOCK_DATA 2 /* read/write a data block */
116#define BLOCK_FMEND 3 /* read/write an end FileMark */
117#define BLOCK_EOD 4 /* read/write an EOD FileMark */
118#define BLOCK_EOF 5 /* tape block is a HW EOF mark (usually error) */
119#define BLOCK_UNKNOWN 6 /* tape block is unknwon (error) */
120
121#define WRITE_OP 1
122#define READ_OP 0
123
124#define READS (((struct progress *)(info->tmRock))->reading)
125#define WRITES (((struct progress *)(info->tmRock))->writing)
126
127/* ----------------------------------------------------------------------
128 * These routines use the usd library to perform tape operations.
129 * ForkIoctl, ForkOpen, ForkClose, ForwardSpace, BackwardSpace, WriteEOF,
130 * PrepareAccess(nt), ShutdownAccess(nt)
131 *
132 * Return Values: USD functions return 0 if successful or errno if failed.
133 */
134/* ------------------ USD Interface Functions Begin ------------------------ */
135
136/*
137 * On Unix, fork a child process to perform an IOCTL call. This avoids
138 * blocking the entire process during a tape operation
139 */
140
141#ifndef AFS_NT40_ENV
142/* Unix version of function */
143
144static int
145ForkIoctl(usd_handle_t fd, int op, int count)
146{
147 int rc; /* return code from system calls */
148 int i; /* loop index */
149 int pid; /* process ID of child process */
150 int status; /* exit status of child process */
151 int ioctl_rc; /* return code from ioctl call */
152 int pipefd[2]; /* pipe for child return status */
153 int forkflag; /* flag set when ready to fork */
154 usd_tapeop_t tapeop; /* tape operation specification */
155 int unixfd;
156
157#ifdef AFS_PTHREAD_ENV
158 forkflag = 0; /* No need to fork if using pthreads */
159#else
160 forkflag = 1;
161#endif
162
163 /* initialize tape command structure */
164 tapeop.tp_op = op;
165 tapeop.tp_count = count;
166
167 /* create pipe for getting return code from child */
168 if (forkflag) {
169 rc = pipe(pipefd);
170 if (rc < 0) {
171 printf("butm: Can't open pipe for IOCTL process. Error %d\n",
172 errno);
173 forkflag = 0;
174 }
175 }
176
177 if (forkflag) {
178 pid = fork();
179 if (pid < 0) {
180 close(pipefd[0]);
181 close(pipefd[1]);
182 printf("butm: Can't fork IOCTL process. Error %d\n", errno);
183 forkflag = 0;
184
185 }
186 }
187
188 if (!forkflag) { /* problem starting child process */
189 /* do the ioctl anyway, it will probably work */
190 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
191 } else if (pid == 0) { /* child process */
192 /* close all unneccessary file descriptors */
193 /* note: as painful as it is, we have to reach under the covers of
194 * the usd package to implement this functionality.
195 */
196 unixfd = (intptr_t)(fd->handle);
197
198 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
199 if (i != unixfd && i != pipefd[1]) {
200 close(i);
201 }
202 }
203
204 /* do the ioctl call */
205 ioctl_rc = USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop);
206
207 /*
208 * Send the return code back to the parent.
209 * If this fails, there's nothing we can do, but we must test
210 * it in order to avoid complier warnings on some platforms.
211 */
212 if (write(pipefd[1], &ioctl_rc, sizeof(int)) < 0) {
213 /* don't care */
214 }
215
216 exit(0);
217 } else { /* parent process */
218
219 close(pipefd[1]);
220 POLL();
221 /* read the result from the child process */
222 rc = read(pipefd[0], &ioctl_rc, sizeof(int));
223 if (rc != sizeof(int)) {
224 /* tape is now in unknown state */
225 printf("butm: Can't determine IOCTL child status. Error %d\n",
226 errno);
227 ioctl_rc = EFAULT;
228 }
229
230 close(pipefd[0]);
231
232 /* get the completion status from the child process */
233 rc = waitpid(pid, &status, 0);
234 while (rc < 0 && errno == EINTR) {
235 rc = waitpid(pid, &status, 0);
236 }
237 if (rc < 0) {
238 printf("butm: Can't determine IOCTL child status. Error %d\n",
239 errno);
240 } else if (status != 0) {
241 printf
242 ("butm: Unexpected IOCTL process status 0x%04x . Error %d\n",
243 status, errno);
244 }
245 SLEEP(1);
246 }
247
248 return (ioctl_rc);
249}
250#else
251/* NT version of function */
252
253static int
254ForkIoctl(usd_handle_t fd, int op, int count)
255{
256 usd_tapeop_t tapeop;
257
258 /* Issue requested tape control */
259 tapeop.tp_op = op;
260 tapeop.tp_count = count;
261
262 return (USD_IOCTL(fd, USD_IOCTL_TAPEOPERATION, (void *)&tapeop));
263}
264#endif /* !AFS_NT40_ENV */
265
266
267/*
268 * On Unix, fork a child process to attempt to open the drive. We want to make
269 * certain there is tape in the drive before trying to open the device
270 * in the main process
271 */
272
273#ifndef AFS_NT40_ENV
274/* Unix version of function. */
275
276static int
277ForkOpen(char *device)
278{
279 int rc; /* return code from system calls */
280 int i; /* loop index */
281 int pid; /* process ID of child process */
282 int status; /* exit status of child process */
283 int open_rc; /* return code from open */
284 int pipefd[2]; /* pipe for child return status */
285 int forkflag; /* flag set when ready to fork */
286 usd_handle_t fd; /* handle returned from open */
287
288#ifdef AFS_PTHREAD_ENV
289 forkflag = 0; /* No need to fork if using pthreads */
290#else
291 forkflag = 1;
292#endif
293
294 /* create pipe for getting return code from child */
295 if (forkflag) {
296 rc = pipe(pipefd);
297 if (rc < 0) {
298 printf("butm: Cannot create pipe for OPEN process. Error %d\n",
299 errno);
300 forkflag = 0;
301 }
302 }
303
304 if (forkflag) {
305 pid = fork();
306 if (pid < 0) {
307 close(pipefd[0]);
308 close(pipefd[1]);
309 printf("butm: Cannot create child process for OPEN. Error %d\n",
310 errno);
311 forkflag = 0;
312 }
313 }
314
315 if (!forkflag) { /* problem starting child process */
316 /*
317 *return success, the caller will discover any problems
318 * when it opens the device.
319 */
320 open_rc = 0;
321 } else if (pid == 0) { /* child process */
322 /* close all unneccessary file descriptors */
323 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
324 if (i != pipefd[1]) {
325 close(i);
326 }
327 }
328
329 /* try the open */
330 open_rc = usd_Open(device, USD_OPEN_RDONLY, 0, &fd);
331
332 if (open_rc == 0) {
333 USD_CLOSE(fd);
334 }
335
336 /*
337 * Send the return code back to the parent.
338 * If this fails, there's nothing we can do, but we must test
339 * it in order to avoid complier warnings on some platforms.
340 */
341 if (write(pipefd[1], &open_rc, sizeof(open_rc)) < 0) {
342 /* don't care */
343 }
344
345 exit(0);
346 } else { /* parent process */
347
348 close(pipefd[1]);
349
350 POLL();
351 /* read the result from the child process */
352 rc = read(pipefd[0], &open_rc, sizeof(open_rc));
353 if (rc != sizeof(open_rc)) {
354 /* this is not a problem since we will reopen the device anyway */
355 printf("butm: No response from OPEN process. Error %d\n", errno);
356 open_rc = 0;
357 }
358
359 close(pipefd[0]);
360
361 /* get the completion status from the child process */
362 rc = waitpid(pid, &status, 0);
363 while (rc < 0 && errno == EINTR) {
364 rc = waitpid(pid, &status, 0);
365 }
366 if (rc < 0) {
367 printf("butm: Cannot get status of OPEN process. Error %d\n",
368 errno);
369 } else if (status != 0) {
370 printf
371 ("butm: Unexpected OPEN process exit status 0x%04x. Error %d \n",
372 status, errno);
373 }
374 SLEEP(1);
375 }
376
377 return (open_rc);
378}
379#else
380/* NT version of function. */
381
382static int
383ForkOpen(char *device)
384{
385 return (0);
386}
387#endif /* AFS_NT40_ENV */
388
389/*
390 * On Unix, fork a child process to close the drive. If the drive rewinds
391 * on close it could cause the process to block.
392 */
393
394#ifndef AFS_NT40_ENV
395/* Unix version of function */
396
397static int
398ForkClose(usd_handle_t fd)
399{
400 int rc; /* return code from system calls */
401 int i; /* loop index */
402 int pid; /* process ID of child process */
403 int status; /* exit status of child process */
404 int close_rc, parent_close_rc; /* return codes from close */
405 int pipefd[2]; /* pipe for child return status */
406 int ctlpipe[2]; /* pipe for message to child */
407 int forkflag; /* flag set when ready to fork */
408 int unixfd;
409
410#ifdef AFS_PTHREAD_ENV
411 forkflag = 0; /* No need to fork if using pthreads */
412#else
413 forkflag = 1;
414#endif
415
416 /* create pipe for getting return code from child */
417 if (forkflag) {
418 rc = pipe(pipefd);
419 if (rc < 0) {
420 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
421 errno);
422 forkflag = 0;
423 }
424 }
425
426 /* create pipe for notifying child when to close */
427 if (forkflag) {
428 rc = pipe(ctlpipe);
429 if (rc < 0) {
430 close(pipefd[0]);
431 close(pipefd[1]);
432 printf("butm: Cannot create pipe for CLOSE process. Error %d\n",
433 errno);
434 forkflag = 0;
435 }
436 }
437
438 if (forkflag) {
439 pid = fork();
440 if (pid < 0) {
441 close(pipefd[0]);
442 close(pipefd[1]);
443 close(ctlpipe[0]);
444 close(ctlpipe[1]);
445 printf("butm: Cannot create CLOSE child process. Error %d\n",
446 errno);
447 forkflag = 0;
448 }
449 }
450
451 if (!forkflag) { /* problem starting child process */
452 close_rc = USD_CLOSE(fd);
453 parent_close_rc = close_rc;
454 } else if (pid == 0) { /* child process */
455 /* close all unneccessary file descriptors */
456 /* note: as painful as it is, we have to reach under the covers of
457 * the usd package to implement this functionality.
458 */
459 unixfd = (intptr_t)(fd->handle);
460
461 for (i = 3; i < _POSIX_OPEN_MAX; i++) {
462 if (i != unixfd && i != ctlpipe[0] && i != pipefd[1]) {
463 close(i);
464 }
465 }
466
467 /*
468 * The parent writes the control pipe after it closes the device.
469 * We don't actually care about the read; we're just using it to
470 * block until the parent is ready. But we must do something
471 * with the result, to avoid complier warnings on some platforms.
472 */
473 if (read(ctlpipe[0], &close_rc, sizeof(int)) < 0) {
474 /* don't care */
475 }
476 close(ctlpipe[0]);
477
478 /* do the close */
479 close_rc = USD_CLOSE(fd);
480
481 /*
482 * Send the return code back to the parent.
483 * If this fails, there's nothing we can do, but we must test
484 * it in order to avoid complier warnings on some platforms.
485 */
486 if (write(pipefd[1], &close_rc, sizeof(int)) < 0) {
487 /* don't care */
488 }
489
490 exit(0);
491 } else { /* parent process */
492
493 close(pipefd[1]);
494 close(ctlpipe[0]);
495
496 POLL();
497 /*
498 * close the device, this should have no effect as long as the
499 * child has not closed
500 */
501
502 parent_close_rc = USD_CLOSE(fd);
503
504 /* notify the child to do its close */
505 rc = write(ctlpipe[1], &close_rc, sizeof(int)); /* just send garbage */
506 if (rc != sizeof(int)) {
507 printf("butm: Error communicating with CLOSE process. Error %d\n",
508 errno);
509 }
510 close(ctlpipe[1]);
511
512 /* read the result from the child process */
513 rc = read(pipefd[0], &close_rc, sizeof(int));
514 if (rc != sizeof(int)) {
515 /* logging is enough, since we wrote a file mark the */
516 /* return code from the close doesn't really matter */
517 printf("butm: No response from CLOSE process. Error %d\n",
518 errno);
519 close_rc = 0;
520 }
521
522 close(pipefd[0]);
523
524 /* get the completion status from the child process */
525 rc = waitpid(pid, &status, 0);
526 while (rc < 0 && errno == EINTR) {
527 rc = waitpid(pid, &status, 0);
528 }
529 if (rc < 0) {
530 printf("butm: Cannot get status of CLOSE process. Error %d\n",
531 errno);
532 } else if (status != 0) {
533 printf
534 ("butm: Unexpected exit status 0x%04x from CLOSE process. Error %d\n",
535 status, errno);
536 }
537
538 /* if either process received an error, then return an error */
539 if (parent_close_rc < 0) {
540 close_rc = parent_close_rc;
541 }
542 SLEEP(1);
543 }
544
545 return (close_rc);
546}
547#else
548/* NT version of function */
549
550static int
551ForkClose(usd_handle_t fd)
552{
553 return (USD_CLOSE(fd));
554}
555#endif /* AFS_NT40_ENV */
556
557/* Forward space file */
558static int
559ForwardSpace(usd_handle_t fid, int count)
560{
561 POLL();
562
563 if (isafile) {
564 return (0);
565 } else {
566 return (ForkIoctl(fid, USDTAPE_FSF, count));
567 }
568}
569
570/* Backward space file */
571static int
572BackwardSpace(usd_handle_t fid, int count)
573{
574 POLL();
575
576 if (isafile) {
577 return (0);
578 } else {
579 return (ForkIoctl(fid, USDTAPE_BSF, count));
580 }
581}
582
583/* write end of file mark */
584static int
585WriteEOF(usd_handle_t fid, int count)
586{
587 POLL();
588
589 if (isafile) {
590 return (0);
591 } else {
592 return (ForkIoctl(fid, USDTAPE_WEOF, count));
593 }
594}
595
596/* rewind tape */
597static int
598Rewind(usd_handle_t fid)
599{
600 if (isafile) {
601 afs_int64 stopOff;
602
603 return (USD_SEEK(fid, 0, SEEK_SET, &stopOff));
604 } else {
605 return (ForkIoctl(fid, USDTAPE_REW, 0));
606 }
607}
608
609/* prepare tape drive for access */
610static int
611PrepareAccess(usd_handle_t fid)
612{
613 int code = 0;
614#ifdef AFS_NT40_ENV
615 if (!isafile) {
616 (void)ForkIoctl(fid, USDTAPE_PREPARE, 0);
617 }
618 /* NT won't rewind tape when it is opened */
619 code = Rewind(fid);
620#endif /* AFS_NT40_ENV */
621 return code;
622}
623
624/* decommission tape drive after all accesses complete */
625static int
626ShutdownAccess(usd_handle_t fid)
627{
628#ifdef AFS_NT40_ENV
629 if (!isafile) {
630 (void)ForkIoctl(fid, USDTAPE_SHUTDOWN, 0);
631 }
632#endif /* AFS_NT40_ENV */
633 return 0;
634}
635
636/* -------------------- USD Interface Functions End ----------------------- */
637
638/* =====================================================================
639 * Support routines
640 * ===================================================================== */
641
642/* incSize
643 * add the supplied no. of bytes to the byte count of information placed
644 * on the tape.
645 * entry:
646 * dataSize - bytes used on the tape
647 */
648
649void
650incSize(struct butm_tapeInfo *info, afs_uint32 dataSize)
651{
652 info->nBytes += dataSize;
653 info->kBytes += (info->nBytes / 1024);
654 info->nBytes %= 1024;
655}
656
657/* incPosition
658 * IF YOU ADD/CHANGE THE ifdefs, BE SURE
659 * TO ALSO MAKE THE SAME CHANGES IN bu_utils/fms.c.
660 *
661 * add the supplied no. of bytes to the byte count of data placed
662 * on the tape. Also check for reaching 2GB limit and reset the
663 * pointer if necessary. This allows us to use >2GB tapes.
664 * entry:
665 * fid - file id for the tape.
666 * dataSize - bytes used on the tape
667 */
668
669void
670incPosition(struct butm_tapeInfo *info, usd_handle_t fid, afs_uint32 dataSize)
671{
672 /* Add this to the amount of data written to the tape */
673 incSize(info, dataSize);
674
675 info->posCount += dataSize;
676
677 if (info->posCount >= 2147467264) { /* 2GB - 16K */
678 info->posCount = 0;
679#if (defined(AFS_SUN_ENV) || defined(AFS_LINUX24_ENV))
680 if (!isafile) {
681 afs_int64 off;
682
683 off = 0;
684 USD_IOCTL(fid, USD_IOCTL_SETSIZE, &off);
685 }
686#endif
687 }
688}
689
690/*
691 * This accounts for tape drives with a block size different from variable or 16K
692 * blocks and only reads that block size.
693 */
694afs_int32 TapeBlockSize;
695afs_int32
696readData(usd_handle_t fid, char *data, afs_uint32 totalSize, afs_int32 *errorP)
697{
698 afs_int32 toread; /* Number of bytes to read */
699 afs_uint32 rSize; /* Total bytes read so far */
700 afs_uint32 tSize; /* Temporary size */
701 afs_int32 rc; /* return code */
702
703 toread = totalSize; /* First, try to read all the data */
704
705 rc = USD_READ(fid, &data[0], toread, &rSize);
706 if (rc != 0) {
707 *errorP = rc;
708 return -1;
709 }
710 if (rSize == 0) /* reached EOF */
711 return rSize;
712
713 if (rSize != TapeBlockSize) { /* Tape block size has changed */
714 TapeBlockSize = rSize;
715 printf("Tape blocks read in %d Byte chunks.\n", TapeBlockSize);
716 }
717
718 /* Read the rest of the data in */
719 while (rSize < totalSize) {
720 toread =
721 ((totalSize - rSize) <
722 TapeBlockSize ? (totalSize - rSize) : TapeBlockSize);
723 rc = USD_READ(fid, &data[rSize], toread, &tSize);
724 if (rc)
725 *errorP = rc;
726 else
727 rSize += tSize;
728 if (tSize != toread)
729 break;
730 }
731
732 if (rSize > totalSize)
733 printf("readData - Read > 16K data block - continuing.\n");
734
735 return (rSize);
736}
737
738afs_int32
739SeekFile(struct butm_tapeInfo *info, int count)
740{
741 afs_int32 code = 0;
742 struct progress *p;
743 afs_int32 error = 0;
744
745 if (count == 0)
746 ERROR_EXIT(0);
747 p = (struct progress *)info->tmRock;
748
749 if (isafile) { /* no reason for seeking through a file */
750 p->reading = p->writing = 0;
751 return (0);
752 }
753
754 POLL();
755
756 if (count > 0)
757 error = ForwardSpace(p->fid, count);
758 else
759 error = BackwardSpace(p->fid, -count);
760
761 POLL();
762
763 if (error) {
764 info->status |= BUTM_STATUS_SEEKERROR;
765 ERROR_EXIT(BUTM_IOCTL);
766 }
767
768 info->position += count;
769 incSize(info, (count * config.fileMarkSize));
770 p = (struct progress *)info->tmRock;
771 p->reading = p->writing = 0;
772
773 error_exit:
774 if (error)
775 info->error = error;
776 return (code);
777}
778
779/* Step to the next filemark if we are not at one already */
780afs_int32
781NextFile(struct butm_tapeInfo *info)
782{
783 afs_int32 code;
784
785 if (!READS && !WRITES)
786 return 0;
787
788 code = SeekFile(info, 1);
789 return (code);
790}
791
792static afs_int32
793WriteTapeBlock(struct butm_tapeInfo *info,
794 char *buffer, /* assumed to be 16384 bytes with data in it */
795 afs_int32 length, /* amount data in buffer */
796 afs_int32 blockType)
797{
798 afs_int32 code = 0, rc = 0;
799 afs_uint32 wsize;
800 struct tapeLabel *label;
801 struct fileMark *fmark;
802 struct blockMark *bmark;
803 struct progress *p;
804 afs_int32 error = 0;
805
806 p = (struct progress *)info->tmRock;
807
808 if (blockType == BLOCK_DATA) { /* Data Block */
809 if (length == 0)
810 ERROR_EXIT(0);
811 bmark = (struct blockMark *)buffer;
812 memset(bmark, 0, sizeof(struct blockMark));
813 bmark->magic = htonl(BLOCK_MAGIC);
814 bmark->count = htonl(length);
815 } else if (blockType == BLOCK_FMBEGIN) { /* Filemark - begin */
816 fmark = (struct fileMark *)buffer;
817 fmark->magic = htonl(FILE_MAGIC);
818 fmark->nBytes = htonl(FILE_BEGIN);
819 } else if (blockType == BLOCK_FMEND) { /* Filemark - end */
820 fmark = (struct fileMark *)buffer;
821 fmark->magic = htonl(FILE_MAGIC);
822 fmark->nBytes = htonl(FILE_FMEND);
823 } else if (blockType == BLOCK_LABEL) { /* Label */
824 label = (struct tapeLabel *)buffer;
825 label->magic = htonl(TAPE_MAGIC);
826 } else if (blockType == BLOCK_EOD) { /* Filemark - EOD mark */
827 fmark = (struct fileMark *)buffer;
828 fmark->magic = htonl(FILE_MAGIC);
829 fmark->nBytes = htonl(FILE_EOD);
830 }
831
832 /* Write the tape block */
833 /* -------------------- */
834 rc = USD_WRITE(p->fid, buffer, BUTM_BLOCKSIZE, &wsize);
835 if ((rc == 0) && (wsize > 0)) {
836 incPosition(info, p->fid, wsize); /* record whats written */
837 p->writing++;
838 }
839
840 if (wsize != BUTM_BLOCKSIZE) {
841 info->status |= BUTM_STATUS_WRITEERROR;
842 if (rc != 0) {
843 error = rc;
844 ERROR_EXIT(BUTM_IO);
845 } else
846 ERROR_EXIT(BUTM_EOT);
847 }
848 if (isafile)
849 info->position++;
850
851 /* Write trailing EOF marker for some block types */
852 /* ---------------------------------------------- */
853 if ((blockType == BLOCK_FMEND) || (blockType == BLOCK_LABEL)
854 || (blockType == BLOCK_EOD)) {
855
856 POLL();
857 error = WriteEOF(p->fid, 1);
858 POLL();
859 if (error) {
860 info->status |= BUTM_STATUS_WRITEERROR;
861 ERROR_EXIT(BUTM_IOCTL);
862 }
863
864 incSize(info, config.fileMarkSize);
865 if (!isafile)
866 info->position++;
867 p->writing = 0;
868 }
869
870 error_exit:
871 if (error)
872 info->error = error;
873 return (code);
874}
875
876static afs_int32
877ReadTapeBlock(struct butm_tapeInfo *info,
878 char *buffer, /* assumed to be 16384 bytes */
879 afs_int32 *blockType)
880{
881 afs_int32 code = 0;
882 afs_int32 rsize, fmtype;
883 struct tapeLabel *label;
884 struct fileMark *fmark;
885 struct blockMark *bmark;
886 struct progress *p;
887
888 *blockType = BLOCK_UNKNOWN;
889
890 p = (struct progress *)info->tmRock;
891
892 memset(buffer, 0, BUTM_BLOCKSIZE);
893 label = (struct tapeLabel *)buffer;
894 fmark = (struct fileMark *)buffer;
895 bmark = (struct blockMark *)buffer;
896
897 rsize = readData(p->fid, buffer, BUTM_BLOCKSIZE, &info->error);
898 if (rsize > 0) {
899 incPosition(info, p->fid, rsize);
900 p->reading++;
901 }
902
903 if (rsize == 0) { /* Read a HW EOF Marker? OK */
904 *blockType = BLOCK_EOF;
905 incSize(info, config.fileMarkSize); /* Size of filemark */
906 if (!isafile)
907 info->position++; /* bump position */
908 p->reading = 0; /* No reads since EOF */
909 }
910
911 else if (rsize != BUTM_BLOCKSIZE) { /* Didn't Read a full block */
912 info->status |= BUTM_STATUS_READERROR;
913 ERROR_EXIT((rsize < 0) ? BUTM_IO : BUTM_EOT);
914 }
915
916 else if (ntohl(bmark->magic) == BLOCK_MAGIC) { /* Data block? */
917 *blockType = BLOCK_DATA;
918 }
919
920 else if (ntohl(fmark->magic) == FILE_MAGIC) { /* Read a filemark? */
921 fmtype = ntohl(fmark->nBytes);
922
923 if (fmtype == FILE_BEGIN) { /* filemark begin */
924 *blockType = BLOCK_FMBEGIN;
925 } else if (fmtype == FILE_FMEND) { /* filemark end */
926 *blockType = BLOCK_FMEND;
927 code = SeekFile(info, 1);
928 } else if (fmtype == FILE_EOD) { /* EOD mark */
929 *blockType = BLOCK_EOD;
930 info->status |= BUTM_STATUS_EOD;
931 code = SeekFile(info, 1);
932 }
933 }
934
935 else if (ntohl(label->magic) == TAPE_MAGIC) { /* Read a tape label? */
936 *blockType = BLOCK_LABEL;
937 code = SeekFile(info, 1);
938 }
939
940 if (isafile)
941 info->position++;
942
943 error_exit:
944 return (code);
945}
946
947/* check
948 * check version numbers and permissions in the info structure
949 */
950
951static afs_int32
952check(struct butm_tapeInfo *info,
953 int write) /* write operation requested */
954{
955 struct progress *p;
956
957 if (!info)
958 return (BUTM_BADARGUMENT);
959
960 /* Check version number in info structure */
961 if (info->structVersion != BUTM_MAJORVERSION)
962 return BUTM_OLDINTERFACE;
963
964 /* Check if a tape is mounted */
965 if (((p = (struct progress *)info->tmRock) == 0) || (p->fid == 0))
966 return BUTM_NOMOUNT;
967
968 /* If writing check if there is write access */
969 if (write && (info->flags & BUTM_FLAGS_READONLY))
970 return BUTM_READONLY;
971
972 return 0;
973}
974
975static afs_int32
976rewindFile(struct butm_tapeInfo *info)
977{
978 struct progress *p;
979 afs_int32 code = 0;
980 afs_int32 error;
981
982 p = (struct progress *)info->tmRock;
983
984 POLL();
985
986 error = Rewind(p->fid);
987
988 POLL();
989
990 if (error) {
991 info->status |= BUTM_STATUS_SEEKERROR;
992 ERROR_EXIT(BUTM_IOCTL);
993 }
994
995 info->position = (isafile ? 0 : 1);
996 info->kBytes = info->nBytes = 0;
997 info->nFiles = info->nRecords = 0;
998 p->reading = p->writing = 0;
999
1000 error_exit:
1001 if (error)
1002 info->error = error;
1003 return (code);
1004}
1005
1006/* =====================================================================
1007 * butm routines
1008 * ===================================================================== */
1009
1010static afs_int32
1011file_Mount(struct butm_tapeInfo *info, char *tape)
1012{
1013 struct progress *p;
1014 char filename[64];
1015 usd_handle_t fid;
1016 int xflags;
1017 afs_int32 code = 0, error = 0, rc = 0;
1018
1019 if (!info || !tape)
1020 ERROR_EXIT(BUTM_BADARGUMENT);
1021
1022 if (info->debug)
1023 printf("butm: Mount tape drive\n");
1024
1025 POLL();
1026 info->error = 0;
1027
1028 if (info->structVersion != BUTM_MAJORVERSION)
1029 ERROR_EXIT(BUTM_OLDINTERFACE);
1030 if (info->tmRock)
1031 ERROR_EXIT(BUTM_PARALLELMOUNTS);
1032 if (strlen(tape) >= sizeof(info->name))
1033 ERROR_EXIT(BUTM_BADARGUMENT);
1034
1035 strcpy(info->name, tape);
1036
1037 strcpy(filename, config.tapedir); /* the name of the tape device */
1038 info->position = (isafile ? 0 : 1);
1039 info->kBytes = info->nBytes = 0;
1040 info->nRecords = info->nFiles = 0;
1041 info->recordSize = 0;
1042 info->tapeSize = config.tapeSize;
1043 info->coefBytes = 1;
1044 info->coefRecords = 0;
1045 info->coefFiles = sizeof(struct fileMark);
1046 info->simultaneousTapes = 1;
1047 info->status = 0;
1048 info->error = 0;
1049 info->flags = BUTM_FLAGS_SEQUENTIAL;
1050
1051 xflags = 0;
1052 if (isafile) {
1053 xflags |= USD_OPEN_CREATE;
1054 } else {
1055 /*
1056 * try to open in a child process first so nothing will
1057 * time out should the process block because the device
1058 * isn't ready.
1059 */
1060
1061 if (ForkOpen(filename)) {
1062 ERROR_EXIT(BUTM_MOUNTFAIL);
1063 }
1064 }
1065
1066 /* Now go ahead and open the tape drive for real */
1067 rc = usd_Open(filename, (USD_OPEN_RDWR | USD_OPEN_WLOCK | xflags), 0777,
1068 &fid);
1069 if (rc != 0) { /* try for lesser access */
1070 rc = usd_Open(filename, (USD_OPEN_RDONLY | USD_OPEN_RLOCK), 0, &fid);
1071
1072 if (rc) {
1073 error = rc;
1074 ERROR_EXIT(BUTM_MOUNTFAIL);
1075 }
1076 info->flags |= BUTM_FLAGS_READONLY;
1077 }
1078
1079 (void)PrepareAccess(fid); /* for NT */
1080
1081 p = malloc(sizeof(*p));
1082 info->tmRock = (char *)p;
1083 p->fid = fid;
1084 p->mountId = config.mountId = time(0);
1085 p->reading = p->writing = 0;
1086
1087 TapeBlockSize = BUTM_BLOCKSIZE; /* Initialize */
1088
1089 error_exit:
1090 if (error)
1091 info->error = error;
1092 return (code);
1093}
1094
1095static afs_int32
1096file_Dismount(struct butm_tapeInfo *info)
1097{
1098 struct progress *p;
1099 afs_int32 code = 0, error = 0;
1100
1101 if (info->debug)
1102 printf("butm: Unmount tape drive\n");
1103
1104 POLL();
1105 info->error = 0;
1106
1107 code = check(info, READ_OP);
1108 if (code)
1109 ERROR_EXIT(code);
1110
1111 p = (struct progress *)info->tmRock;
1112
1113 (void)ShutdownAccess(p->fid); /* for NT */
1114
1115 /* close the device */
1116 if ((error = ForkClose(p->fid))) {
1117 printf("butm: Tape close failed. Error %d\n", errno);
1118 }
1119
1120 POLL();
1121
1122 if (error) {
1123 code = BUTM_DISMOUNTFAIL;
1124 info->status |= BUTM_STATUS_TAPEERROR;
1125 }
1126
1127 config.mountId = 0;
1128 info->tmRock = 0; /* mark it as closed - even if error on close */
1129 if (p)
1130 free(p);
1131
1132 error_exit:
1133 if (error)
1134 info->error = error;
1135 return (code);
1136}
1137
1138/* file_WriteLabel
1139 * write the header on a tape
1140 * entry:
1141 * info - handle on tape unit
1142 * label - label information. This label is not copied onto the tape.
1143 * If supplied, various fields are copied from this label to
1144 * the actual tape label written on the tape.
1145 */
1146
1147static afs_int32
1148file_WriteLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1149 afs_int32 rewind)
1150{
1151 afs_int32 code = 0;
1152 afs_int32 fcode;
1153 struct tapeLabel *tlabel;
1154 struct progress *p;
1155 afs_int64 off; /* offset */
1156
1157 if (info->debug)
1158 printf("butm: Write tape label\n");
1159
1160 POLL();
1161 info->error = 0;
1162
1163 code = check(info, WRITE_OP);
1164 if (code)
1165 ERROR_EXIT(code);
1166 if (!label)
1167 ERROR_EXIT(BUTM_BADARGUMENT);
1168 if (label->structVersion != CUR_TAPE_VERSION)
1169 ERROR_EXIT(BUTM_OLDINTERFACE);
1170
1171 if (rewind) { /* Not appending, so rewind */
1172 code = rewindFile(info);
1173 if (code)
1174 ERROR_EXIT(code);
1175
1176 if (isafile) {
1177 p = (struct progress *)info->tmRock;
1178 off = 0;
1179 code = USD_IOCTL(p->fid, USD_IOCTL_SETSIZE, &off);
1180 if (code)
1181 ERROR_EXIT(BUTM_POSITION);
1182 }
1183 } else {
1184 if (READS || WRITES)
1185 ERROR_EXIT(BUTM_BADOP);
1186 }
1187
1188 /* Copy the label into the tape block
1189 * ---------------------------------- */
1190 memset(tapeBlock, 0, BUTM_BLOCKSIZE);
1191
1192 if (!label->creationTime)
1193 label->creationTime = time(0);
1194
1195 tlabel = (struct tapeLabel *)tapeBlock;
1196 memcpy(&tlabel->label, label, sizeof(struct butm_tapeLabel));
1197 tlabel->label.structVersion = htonl(CUR_TAPE_VERSION);
1198 tlabel->label.creationTime = htonl(tlabel->label.creationTime);
1199 tlabel->label.expirationDate = htonl(tlabel->label.expirationDate);
1200 tlabel->label.size = htonl(tlabel->label.size);
1201 tlabel->label.useCount = htonl(tlabel->label.useCount);
1202 tlabel->label.dumpid = htonl(tlabel->label.dumpid);
1203
1204 /*
1205 * write the tape label - For appends, the write may need to skip
1206 * over 1 or 2 EOF marks that were written when tape was closed after
1207 * the last dump. Plus, some AIX tape drives require we try forwarding
1208 * over the last EOF and take an error before we can write the new label.
1209 * ---------------------------------------------------------------------- */
1210 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1211 if (!isafile && !rewind && (code == BUTM_IO))
1212 do { /* do if write failed */
1213 fcode = SeekFile(info, 1); /* skip over the EOF */
1214 if (fcode)
1215 break; /* leave if error */
1216
1217 code =
1218 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1219 if (code != BUTM_IO)
1220 break; /* continue if write failed */
1221
1222 fcode = SeekFile(info, 1); /* skip over the EOF */
1223 if (fcode) { /* retry 1 write if couldn't skip */
1224 code =
1225 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1226 BLOCK_LABEL);
1227 break;
1228 }
1229
1230 code =
1231 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_LABEL);
1232 if (code != BUTM_IO)
1233 break; /* continue if write failed */
1234
1235 fcode = SeekFile(info, 1); /* skip over the EOF */
1236 if (fcode) { /* retry 1 write if couldn't skip */
1237 code =
1238 WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE,
1239 BLOCK_LABEL);
1240 break;
1241 }
1242 break;
1243 } while (0);
1244
1245 /* clear the write error status a failed WriteTapeBlock may have produced */
1246 if (!code)
1247 info->status &= ~BUTM_STATUS_WRITEERROR;
1248
1249 error_exit:
1250 return code;
1251}
1252
1253static afs_int32
1254file_ReadLabel(struct butm_tapeInfo *info, struct butm_tapeLabel *label,
1255 afs_int32 rewind)
1256{
1257 struct tapeLabel *tlabel;
1258 afs_int32 code = 0;
1259 afs_int32 blockType;
1260
1261 if (info->debug)
1262 printf("butm: Read tape label\n");
1263
1264 POLL();
1265 info->error = 0;
1266
1267 code = check(info, READ_OP);
1268 if (code)
1269 ERROR_EXIT(code);
1270 if (READS || WRITES)
1271 ERROR_EXIT(BUTM_BADOP);
1272
1273 if (rewind) {
1274 code = rewindFile(info);
1275 if (code)
1276 ERROR_EXIT(code); /* status is set so return */
1277 }
1278
1279 /*
1280 * When appended labels were written, either 1 or 2 EOF marks may
1281 * have had to be skipped. When reading a label, these EOF marks
1282 * must also be skipped. When an EOF is read, 0 bytes are returned
1283 * (refer to the write calls in file_WriteLabel routine).
1284 * ---------------------------------------------------------------- */
1285 code = ReadTapeBlock(info, tapeBlock, &blockType);
1286 if (!isafile && !rewind && (blockType == BLOCK_EOF))
1287 do {
1288 code = ReadTapeBlock(info, tapeBlock, &blockType);
1289 if (blockType != BLOCK_EOF)
1290 break; /* didn't read an EOF */
1291
1292 code = ReadTapeBlock(info, tapeBlock, &blockType);
1293 } while (0);
1294
1295 if (blockType == BLOCK_UNKNOWN)
1296 ERROR_EXIT(BUTM_NOLABEL);
1297 if (code)
1298 ERROR_EXIT(code);
1299 if (blockType != BLOCK_LABEL)
1300 ERROR_EXIT(BUTM_BADBLOCK);
1301
1302 /* Copy label out
1303 * -------------- */
1304 if (label) {
1305 tlabel = (struct tapeLabel *)tapeBlock;
1306 memcpy(label, &tlabel->label, sizeof(struct butm_tapeLabel));
1307 label->structVersion = ntohl(label->structVersion);
1308 label->creationTime = ntohl(label->creationTime);
1309 label->expirationDate = ntohl(label->expirationDate);
1310 label->size = ntohl(label->size);
1311 label->dumpid = ntohl(label->dumpid);
1312 label->useCount = ntohl(label->useCount);
1313
1314 info->tapeSize = label->size; /* use size from label */
1315 }
1316
1317 error_exit:
1318 return (code);
1319}
1320
1321static afs_int32
1322file_WriteFileBegin(struct butm_tapeInfo *info)
1323{
1324 afs_int32 code = 0;
1325
1326 if (info->debug)
1327 printf("butm: Write filemark begin\n");
1328
1329 POLL();
1330
1331 code = check(info, WRITE_OP);
1332 if (code)
1333 ERROR_EXIT(code);
1334
1335 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMBEGIN);
1336 if (code)
1337 ERROR_EXIT(code);
1338
1339 info->nFiles++;
1340
1341 error_exit:
1342 return (code);
1343}
1344
1345static afs_int32
1346file_ReadFileBegin(struct butm_tapeInfo *info)
1347{
1348 afs_int32 code = 0;
1349 afs_int32 blockType;
1350
1351 if (info->debug)
1352 printf("butm: Read filemark begin\n");
1353
1354 POLL();
1355 info->error = 0;
1356
1357 code = check(info, READ_OP);
1358 if (code)
1359 ERROR_EXIT(code);
1360 if (READS || WRITES)
1361 ERROR_EXIT(BUTM_BADOP);
1362
1363 code = ReadTapeBlock(info, tapeBlock, &blockType);
1364 if (code)
1365 ERROR_EXIT(code);
1366
1367 if (blockType != BLOCK_FMBEGIN) {
1368 if (blockType == BLOCK_EOD)
1369 ERROR_EXIT(BUTM_EOD); /* EODump label */
1370 if (blockType == BLOCK_LABEL)
1371 ERROR_EXIT(BUTM_LABEL); /* Tape label */
1372 ERROR_EXIT(BUTM_BADBLOCK); /* Other */
1373 }
1374
1375 error_exit:
1376 return (code);
1377}
1378
1379/* Writes data out in block sizes of 16KB. Does destroy the data.
1380 * Assumes the data buffer has a space reserved at beginning for a blockMark.
1381 */
1382static afs_int32
1383file_WriteFileData(struct butm_tapeInfo *info, char *data, afs_int32 blocks, afs_int32 len)
1384{
1385 afs_int32 code = 0;
1386 int length;
1387 afs_int32 b;
1388 char *bstart; /* Where block starts for a 16K block */
1389 char *dstart; /* Where data starts for a 16K block */
1390
1391 if (info->debug)
1392 printf("butm: Write tape data - %u bytes\n", len);
1393
1394 POLL();
1395 info->error = 0;
1396
1397 code = check(info, WRITE_OP);
1398 if (code)
1399 ERROR_EXIT(code);
1400 if (!data || (len < 0))
1401 ERROR_EXIT(BUTM_BADARGUMENT);
1402 if (READS || !WRITES)
1403 ERROR_EXIT(BUTM_BADOP);
1404
1405 b = 0; /* start at block 0 */
1406 while (len > 0) {
1407 dstart = &data[b * BUTM_BLKSIZE];
1408 bstart = dstart - sizeof(struct blockMark);
1409
1410 if (len < BUTM_BLKSIZE) {
1411 memset(&dstart[len], 0, BUTM_BLKSIZE - len);
1412 length = len;
1413 } else {
1414 length = BUTM_BLKSIZE;
1415 }
1416
1417 code = WriteTapeBlock(info, bstart, length, BLOCK_DATA);
1418
1419 len -= length;
1420
1421 /* If there are more blocks, step to next block */
1422 /* Otherwise, copy the data to beginning of last block */
1423
1424 if (b < (blocks - 1))
1425 b++;
1426 else if (len)
1427 memcpy(&dstart[0], &dstart[BUTM_BLKSIZE], len);
1428 }
1429
1430 error_exit:
1431 return (code);
1432}
1433
1434/* file_ReadFileData
1435 * Read a data block from tape.
1436 * entry:
1437 * info - tape info structure, c.f. fid
1438 * data - ptr to buffer for data
1439 * len - size of data buffer
1440 * exit:
1441 * nBytes - no. of data bytes read.
1442 */
1443
1444static afs_int32
1445file_ReadFileData(struct butm_tapeInfo *info, char *data, int len, int *nBytes)
1446{
1447 struct blockMark *bmark;
1448 afs_int32 code = 0;
1449 afs_int32 blockType;
1450
1451 if (info->debug)
1452 printf("butm: Read tape data - %u bytes\n", len);
1453
1454 POLL();
1455 info->error = 0;
1456
1457 if (!nBytes)
1458 ERROR_EXIT(BUTM_BADARGUMENT);
1459 *nBytes = 0;
1460
1461 code = check(info, READ_OP);
1462 if (code)
1463 ERROR_EXIT(code);
1464 if (!data || (len < 0) || (len > BUTM_BLKSIZE))
1465 ERROR_EXIT(BUTM_BADARGUMENT);
1466 if (!READS || WRITES)
1467 ERROR_EXIT(BUTM_BADOP);
1468
1469 data -= sizeof(struct blockMark);
1470 code = ReadTapeBlock(info, data, &blockType);
1471 if (code)
1472 ERROR_EXIT(code);
1473
1474 if (blockType != BLOCK_DATA) {
1475 if (blockType == BLOCK_EOF)
1476 ERROR_EXIT(BUTM_EOF);
1477 if (blockType == BLOCK_FMEND)
1478 ERROR_EXIT(BUTM_ENDVOLUME);
1479 ERROR_EXIT(BUTM_BADBLOCK);
1480 }
1481
1482 bmark = (struct blockMark *)data;
1483 *nBytes = ntohl(bmark->count); /* Size of data in buf */
1484
1485 error_exit:
1486 return (code);
1487}
1488
1489static afs_int32
1490file_WriteFileEnd(struct butm_tapeInfo *info)
1491{
1492 afs_int32 code = 0;
1493
1494 if (info->debug)
1495 printf("butm: Write filemark end\n");
1496
1497 POLL();
1498 info->error = 0;
1499
1500 code = check(info, WRITE_OP);
1501 if (code)
1502 ERROR_EXIT(code);
1503 if (READS || !WRITES)
1504 ERROR_EXIT(BUTM_BADOP);
1505
1506 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_FMEND);
1507
1508 error_exit:
1509 return (code);
1510}
1511
1512/* Read a SW filemark, verify that it is a SW filemark, then skip to the next
1513 * HW filemark. If the read of the SW filemark shows it's an EOF, then
1514 * ignore that the SW filemark is not there and return 0 (found the SW filemark
1515 * missing with some 3.1 dumps).
1516 */
1517static afs_int32
1518file_ReadFileEnd(struct butm_tapeInfo *info)
1519{
1520 afs_int32 code = 0;
1521 afs_int32 blockType;
1522
1523 if (info->debug)
1524 printf("butm: Read filemark end\n");
1525
1526 POLL();
1527 info->error = 0;
1528
1529 code = check(info, READ_OP);
1530 if (code)
1531 ERROR_EXIT(code);
1532 if (!READS || WRITES)
1533 ERROR_EXIT(BUTM_BADOP);
1534
1535 info->status &= ~BUTM_STATUS_EOF;
1536
1537 code = ReadTapeBlock(info, tapeBlock, &blockType);
1538 if (code)
1539 ERROR_EXIT(code);
1540
1541 if ((blockType != BLOCK_FMEND) && (blockType != BLOCK_EOF))
1542 ERROR_EXIT(BUTM_BADBLOCK);
1543
1544 error_exit:
1545 return code;
1546}
1547
1548/*
1549 * Write the end-of-dump marker.
1550 */
1551static afs_int32
1552file_WriteEODump(struct butm_tapeInfo *info)
1553{
1554 afs_int32 code = 0;
1555
1556 if (info->debug)
1557 printf("butm: Write filemark EOD\n");
1558
1559 POLL();
1560 info->error = 0;
1561
1562 code = check(info, WRITE_OP);
1563 if (code)
1564 ERROR_EXIT(code);
1565 if (READS || WRITES)
1566 ERROR_EXIT(BUTM_BADOP);
1567
1568 code = WriteTapeBlock(info, tapeBlock, BUTM_BLOCKSIZE, BLOCK_EOD);
1569 if (code)
1570 ERROR_EXIT(code);
1571
1572 info->status |= BUTM_STATUS_EOD;
1573
1574 error_exit:
1575 return (code);
1576}
1577
1578static afs_int32
1579file_Seek(struct butm_tapeInfo *info, afs_int32 position)
1580{
1581 afs_int32 code = 0;
1582 afs_int32 w;
1583 osi_lloff_t posit;
1584 struct progress *p;
1585 afs_int64 stopOff; /* for normal file(non-tape) seeks */
1586
1587 if (info->debug)
1588 printf("butm: Seek to the tape position %d\n", position);
1589
1590 info->error = 0;
1591
1592 code = check(info, READ_OP);
1593 if (code)
1594 ERROR_EXIT(code);
1595
1596 if (isafile) {
1597 p = (struct progress *)info->tmRock;
1598 posit = (osi_lloff_t) position *(osi_lloff_t) BUTM_BLOCKSIZE;
1599
1600 w = USD_SEEK(p->fid, posit, SEEK_SET, &stopOff);
1601 if (w)
1602 info->error = w;
1603 if (posit != stopOff)
1604 ERROR_EXIT(BUTM_POSITION);
1605
1606 p->reading = p->writing = 0;
1607 info->position = position;
1608 } else {
1609 /* Don't position backwards if we are in-between FMs */
1610 if ((READS || WRITES) && ((position - info->position) <= 0))
1611 ERROR_EXIT(BUTM_BADOP);
1612
1613 code = SeekFile(info, (position - info->position));
1614 if (code)
1615 ERROR_EXIT(code);
1616 }
1617
1618 error_exit:
1619 return (code);
1620}
1621
1622/*
1623 * Seek to the EODump (end-of-dump) after the given position. This is
1624 * the position after the EOF filemark immediately after the EODump mark.
1625 * This is for tapes of version 4 or greater.
1626 */
1627static afs_int32
1628file_SeekEODump(struct butm_tapeInfo *info, afs_int32 position)
1629{
1630 afs_int32 code = 0;
1631 afs_int32 blockType;
1632 afs_int32 w;
1633 struct progress *p;
1634 afs_int64 stopOff; /* file seek offsets */
1635
1636 if (info->debug)
1637 printf("butm: Seek to end-of-dump\n");
1638 info->error = 0;
1639
1640 code = check(info, READ_OP);
1641 if (code)
1642 ERROR_EXIT(code);
1643 if (READS || WRITES)
1644 ERROR_EXIT(BUTM_BADOP);
1645
1646 if (isafile) {
1647 p = (struct progress *)info->tmRock;
1648 w = USD_SEEK(p->fid, 0, SEEK_END, &stopOff);
1649 if (w) {
1650 info->error = w;
1651 ERROR_EXIT(BUTM_POSITION);
1652 }
1653
1654 if (stopOff % BUTM_BLOCKSIZE)
1655 ERROR_EXIT(BUTM_POSITION);
1656 info->position = (stopOff / BUTM_BLOCKSIZE);
1657 } else {
1658 /* Seek to the desired position */
1659 code = SeekFile(info, (position - info->position) + 1);
1660 if (code)
1661 ERROR_EXIT(code);
1662
1663 /*
1664 * Search until the filemark is an EODump filemark.
1665 * Skip over volumes only.
1666 */
1667 while (1) {
1668 code = ReadTapeBlock(info, tapeBlock, &blockType);
1669 if (code)
1670 ERROR_EXIT(code);
1671
1672 if (blockType == BLOCK_EOD)
1673 break;
1674 if (blockType != BLOCK_FMBEGIN)
1675 ERROR_EXIT(BUTM_BADBLOCK);
1676
1677 code = SeekFile(info, 1); /* Step forward to next volume */
1678 if (code)
1679 ERROR_EXIT(code);
1680 }
1681 code = 0;
1682 }
1683
1684 error_exit:
1685 return (code);
1686}
1687
1688static afs_int32
1689file_SetSize(struct butm_tapeInfo *info, afs_uint32 size)
1690{
1691 if (info->debug)
1692 printf("butm: Set size of tape\n");
1693 info->error = 0;
1694
1695 if (size <= 0)
1696 info->tapeSize = config.tapeSize;
1697 else
1698 info->tapeSize = size;
1699 return 0;
1700}
1701
1702static afs_int32
1703file_GetSize(struct butm_tapeInfo *info, afs_uint32 *size)
1704{
1705 if (info->debug)
1706 printf("butm: Get size of tape\n");
1707 info->error = 0;
1708
1709 *size = info->tapeSize;
1710 return 0;
1711}
1712
1713/* =====================================================================
1714 * Startup/configuration routines.
1715 * ===================================================================== */
1716
1717static afs_int32
1718file_Configure(struct tapeConfig *file)
1719{
1720 if (!file) {
1721 afs_com_err(whoami, BUTM_BADCONFIG, "device not specified");
1722 return BUTM_BADCONFIG;
1723 }
1724
1725 config.tapeSize = file->capacity;
1726 config.fileMarkSize = file->fileMarkSize;
1727 config.portOffset = file->portOffset;
1728 strcpy(config.tapedir, file->device);
1729
1730 /* Tape must be large enough to at least fit a label */
1731 if (config.tapeSize <= 0) {
1732 afs_com_err(whoami, BUTM_BADCONFIG, "Tape size bogus: %d Kbytes",
1733 config.tapeSize);
1734 return BUTM_BADCONFIG;
1735 }
1736
1737 if (strlen(config.tapedir) == 0) {
1738 afs_com_err(whoami, BUTM_BADCONFIG, "no tape device specified");
1739 return BUTM_BADCONFIG;
1740 }
1741
1742 config.mountId = 0;
1743 return 0;
1744}
1745
1746/* This procedure instantiates a tape module of type file_tm. */
1747afs_int32
1748butm_file_Instantiate(struct butm_tapeInfo *info, struct tapeConfig *file)
1749{
1750 extern int debugLevel;
1751 afs_int32 code = 0;
1752
1753 if (debugLevel > 98)
1754 printf("butm: Instantiate butc\n");
1755
1756 if (!info)
1757 ERROR_EXIT(BUTM_BADARGUMENT);
1758 if (info->structVersion != BUTM_MAJORVERSION)
1759 ERROR_EXIT(BUTM_OLDINTERFACE);
1760
1761 memset(info, 0, sizeof(struct butm_tapeInfo));
1762 info->structVersion = BUTM_MAJORVERSION;
1763 info->ops.mount = file_Mount;
1764 info->ops.dismount = file_Dismount;
1765 info->ops.create = file_WriteLabel;
1766 info->ops.readLabel = file_ReadLabel;
1767 info->ops.seek = file_Seek;
1768 info->ops.seekEODump = file_SeekEODump;
1769 info->ops.readFileBegin = file_ReadFileBegin;
1770 info->ops.readFileData = file_ReadFileData;
1771 info->ops.readFileEnd = file_ReadFileEnd;
1772 info->ops.writeFileBegin = file_WriteFileBegin;
1773 info->ops.writeFileData = file_WriteFileData;
1774 info->ops.writeFileEnd = file_WriteFileEnd;
1775 info->ops.writeEOT = file_WriteEODump;
1776 info->ops.setSize = file_SetSize;
1777 info->ops.getSize = file_GetSize;
1778 info->debug = ((debugLevel > 98) ? 1 : 0);
1779
1780 code = file_Configure(file);
1781
1782 error_exit:
1783 return code;
1784}