Bug Summary

File:butm/file_tm.c
Location:line 1148, column 6
Description:Assigned value is always the same as the existing value

Annotated Source Code

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