david@elroy.jpl.nasa.gov (David Robinson) (12/02/87)
Here is the copytape version that I mentioned earlier that works with the rmtlib routines to allow you to copy tapes across a network. The syntax is the same as normal copytape but allows the input and output files to be given in the remote file format (ala rcp). Example: % copytape /dev/rmt0 foobar:/dev/rmt8 -David Robinson david@elroy.jpl.nasa.gov # -----------CUT HERE--------------------- # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by elroy!david on Sun Nov 29 06:40:35 PST 1987 # Contents: Makefile README.network copytape.1 copytape.5 copytape.c rmt.h # rmtlib.c echo x - Makefile sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//' CFLAGS= -O MAN1 = /usr/man/man1 MAN5 = /usr/man/man5 BIN = /usr/local copytape: copytape.o rmtlib.o cc -O -o copytape copytape.o rmtlib.o install: copytape install -s -m 0511 copytape ${BIN} clean: rm -f copytape copytape.o rmtlib.o man: man1 man5 man1: ${MAN1}/copytape.1 cp copytape.1 ${MAN1} man5: ${MAN5}/copytape.5 cp copytape.5 ${MAN5} @//E*O*F Makefile// chmod u=rw,g=r,o=r Makefile echo x - README.network sed 's/^@//' > "README.network" <<'@//E*O*F README.network//' This is a version of the public domain copytape program that works with a slightly modified version of the public domain rmtlib remote tape library. The version of copytape is not the most uptodate version but it works. It should not be difficult to upgrade it if you have the most recent version. The rmtlib was slightly modified to get some (not all) of the ioctl calls to work. In particular the MTIOCGET ioctl does not work correctly with a remote tape so it is fake by always returning zero (0). This assumes that the user will never try to copy a tape to a remote file instead of device. Bugs probably do exist if so forward them on to me. 11/29/87 David Robinson MS 168-522 Jet Propulsion Lab 4800 Oak Grove Pasadena CA 91109 david@elroy.jpl.nasa.gov @//E*O*F README.network// chmod u=rw,g=r,o=r README.network echo x - copytape.1 sed 's/^@//' > "copytape.1" <<'@//E*O*F copytape.1//' @.TH COPYTAPE 1 "25 June 1986" @.\"@(#)copytape.1 1.0 86/07/08 AICenter; by David S. Hayes @.SH NAME copytape \- duplicate magtapes @.SH SYNOPSIS @.B copytape \[\-f\] \[\-t\] \[\-s\fInnn\fP\] \[\-l\fInnn\fP\] \[\-v\] @.I \[input \[output\]\] @.SH DESCRIPTION @.LP @.I copytape duplicates magtapes. It is intended for duplication of bootable or other non-file-structured (non-tar-structured) magtapes on systems with only one tape drive. @.I copytape is blissfully ignorant of tape formats. It merely makes a bit-for-bit copy of its input. @.PP In normal use, @.I copytape would be run twice. First, a boot tape is copied to an intermediate disk file. The file is in a special format that preserves the record boundaries and tape marks. On the second run, @.I copytape reads this file and generates a new tape. The second step may be repeated if multiple copies are required. The typical process would look like this: @.sp @.RS +.5i tutorial% copytape /dev/rmt8 tape.tmp @.br tutorial% copytape tape.tmp /dev/rmt8 @.br tutorial% rm tape.tmp @.RE @.PP @.I copytape copies from the standard input to the standard output, unless input and output arguments are provided. It will automatically determine whether its input and output are physical tapes, or data files. Data files are encoded in a special (human-readable) format. @.PP Since @.I copytape will automatically determine what sort of thing its input and output are, a twin-drive system can duplicate a tape in one pass. The command would be @.RS +.5i tutorial% copytape /dev/rmt8 /dev/rmt9 @.RE @.PP @.I copytape will also take as input and/or output the name of a remote tape drive in the format hostname[.username]:drive. If for example you are inputing from local rmt8 and wish to copy to rmt9 on machine fred, the command would be @.RS +.5i tutorial% copytape /dev/rmt8 fred:/dev/rmt9 @.RE @.SH OPTIONS @.TP 3 @.RI \-s nnn Skip tape marks. The specified number of tape marks are skipped on the input tape, before the copy begins. By default, nothing is skipped, resulting in a copy of the complete input tape. Multiple tar(1) and dump(1) archives on a single tape are normally separated by a single tape mark. On ANSI or IBM labelled tapes, each file has three associated tape marks. Count carefully. @.TP 3 @.RI \-l nnn Limit. Only nnn files (data followed by a tape mark), at most, are copied. This can be used to terminate a copy early. If the skip option is also specified, the files skipped do not count against the limit. @.TP 3 \-f @From tape. The input is treated as though it were a physical tape, even if it is a data file. This option can be used to copy block-structured device files other than magtapes. @.TP 3 \-t To tape. The output is treated as though it were a physical tape, even if it is a data file. Normally, data files mark physical tape blocks with a (human\-readable) header describing the block. If the \-t option is used when the output is actually a disk file, these headers will not be written. This will extract all the information from the tape, but @.I copytape will not be able to duplicate the original tape based on the resulting data file. @.TP 3 \-v Verbose. @.I copytape does not normally produce any output on the control terminal. The verbose option will identify the input and output files, tell whether they are physical tapes or data files, and announce the size of each block copied. This can produce a lot of output on even relatively short tapes. It is intended mostly for diagnostic work. @.SH FILES /dev/rmt* @.SH "SEE ALSO" ansitape(1), dd(1), tar(1), mtio(4), copytape(5) @.SH AUTHOR David S. Hayes, Site Manager, US Army Artificial Intelligence Center. Originally developed September 1984 at Rensselaer Polytechnic Institute, Troy, New York. Revised July 1986. This software is in the public domain. @.SH BUGS @.LP @.I copytape treats two successive file marks as logical end-of-tape. @.LP The intermediate data file can consume huge amounts of disk space. A 2400-foot reel at 6250-bpi can burn 140 megabytes. This is not strictly speaking a bug, but users should be aware of the possibility. Check disk space with @.I df(1) before starting @.IR copytape . Caveat Emptor! @.LP A 256K buffer is used internally. This limits the maximum block size of the input tape. @//E*O*F copytape.1// chmod u=rw,g=r,o=r copytape.1 echo x - copytape.5 sed 's/^@//' > "copytape.5" <<'@//E*O*F copytape.5//' @.TH COPYTAPE 5 "8 August 1986" @.SH NAME copytape \- copytape intermediate data file format @.SH DESCRIPTION @.I copytape duplicates magtapes on single\-tape systems by making an intermediate copy of the tape in a disk file. This disk file has a special format that preserves the block boundaries and tape marks of the original physical tape. @.PP Each block is preceded by a header identifying what sort of block it is. In the case of data blocks, the length of the data is also given. Each header is on a separate text line, followed by a newline character. @.sp @.TP 3 CPTP:BLK \fInnnnnn\fP @.ti -3 \fIdata\fP\\n @.sp A data block is identified by the keyword @.IR BLK . The length of the block is given in a six\-character numeric field. The field is zero\-padded on the left if less than six characters are needed. The header is followed by a newline character. The original data follows. The data may have any characters in it, since @.I copytape uses a read(2) to extract it. The data is followed by a newline, to make the file easy to view with an editor. @.TP 3 CPTP:MRK A tape mark was encountered in the original tape. @.TP 3 CPTP:EOT When two consecutive tape marks are encountered, @.I copytape treats the second as a logical end\-of\-tape. On output, both MRK and EOT generate a physical tape mark. @.I copytape stops processing after copying an EOT. @.SH "SEE ALSO" mtio(4) @.SH BUGS Some weird tapes may not use two consecutive tape marks as logical end\-of\-tape. @//E*O*F copytape.5// chmod u=rw,g=r,o=r copytape.5 echo x - copytape.c sed 's/^@//' > "copytape.c" <<'@//E*O*F copytape.c//' /* * COPYTAPE.C * * This program duplicates magnetic tapes, preserving the * blocking structure and placement of tape marks. * * This program was updated at * * U.S. Army Artificial Intelligence Center * HQDA (Attn: DACS-DMA) * Pentagon * Washington, DC 20310-0200 * * Phone: (202) 694-6900 * ************************************************** * * THIS PROGRAM IS IN THE PUBLIC DOMAIN * ************************************************** * * July 1986 David S. Hayes * Made data file format human-readable. * * April 1985 David S. Hayes * Original Version. */ #include <stdio.h> #include <sys/types.h> #include "rmt.h" #include <sys/ioctl.h> #include <sys/mtio.h> #include <sys/file.h> extern int errno; #define BUFLEN 262144 /* max tape block size */ #define TAPE_MARK -100 /* return record length if we read a * tape mark */ #define END_OF_TAPE -101 /* 2 consecutive tape marks */ #define FORMAT_ERROR -102 /* data file munged */ int totape = 0, /* treat destination as a tape drive */ fromtape = 0; /* treat source as a tape drive */ int verbose = 0; /* tell what we're up to */ char *source = "stdin", *dest = "stdout"; char tapebuf[BUFLEN]; main(argc, argv) int argc; char *argv[]; { int from = 0, to = 1; int len; /* number of bytes in record */ int skip = 0; /* number of files to skip before * copying */ unsigned int limit = 0xffffffff; int i; struct mtget status; for (i = 1; i < argc && argv[i][0] == '-'; i++) { switch (argv[i][1]) { case 's': /* skip option */ skip = atoi(&argv[i][2]); break; case 'l': limit = atoi(&argv[i][2]); break; case 'f': /* from tape option */ fromtape = 1; break; case 't': /* to tape option */ totape = 1; break; case 'v': /* be wordy */ verbose = 1; break; default: fprintf(stderr, "usage: copytape [-f] [-t] [-lnn] [-snn] [-v] from to\n"); exit(-1); } } if (i < argc) { from = open(argv[i], O_RDONLY); source = argv[i]; if (from == -1) { perror("copytape: input open failed"); exit(-1); } i++;; } if (i < argc) { to = open(argv[i], O_WRONLY | O_CREAT | O_TRUNC, 0666); dest = argv[i]; if (to == -1) { perror("copytape: output open failed"); exit(-1); } i++; } if (i < argc) perror("copytape: extra arguments ignored"); /* * Determine if source and/or destination is a tape device. Try to * issue a magtape ioctl to it. If it doesn't error, then it was a * magtape. */ errno = 0; ioctl(from, MTIOCGET, &status); fromtape |= errno == 0; errno = 0; ioctl(to, MTIOCGET, &status); totape |= errno == 0; errno = 0; if (verbose) { fprintf(stderr, "copytape: from %s (%s)\n", source, fromtape ? "tape" : "data"); fprintf(stderr, " to %s (%s)\n", dest, totape ? "tape" : "data"); } /* * Skip number of files, specified by -snnn, given on the command * line. This is used to copy second and subsequent files on the * tape. */ if (verbose && skip) { fprintf(stderr, "copytape: skipping %d input files\n", skip); } for (i = 0; i < skip; i++) { do { len = input(from); } while (len > 0); if (len == FORMAT_ERROR) { perror(stderr, "copytape: format error on skip"); exit(-1); }; if (len == END_OF_TAPE) { fprintf(stderr, "copytape: only %d files in input\n", i); exit(-1); }; }; /* * Do the copy. */ len = 0; while (limit && !(len == END_OF_TAPE || len == FORMAT_ERROR)) { do { do { len = input(from); if (len == FORMAT_ERROR) perror("copytape: data format error - block ignored"); } while (len == FORMAT_ERROR); output(to, len); if (verbose) { switch (len) { case TAPE_MARK: fprintf(stderr, " copied MRK\n"); break; case END_OF_TAPE: fprintf(stderr, " copied EOT\n"); break; default: fprintf(stderr, " copied %d bytes\n", len); }; }; } while (len > 0); limit--; } exit(0); } /* * Input up to 256K from a file or tape. If input file is a tape, then * do markcount stuff. Input record length will be supplied by the * operating system. */ input(fd) int fd; { static markcount = 0; /* number of consecutive tape * marks */ int len, l2, c; char header[40]; if (fromtape) { len = read(fd, tapebuf, BUFLEN); switch (len) { case -1: perror("copytape: can't read input"); return END_OF_TAPE; case 0: if (++markcount == 2) return END_OF_TAPE; else return TAPE_MARK; default: markcount = 0; /* reset tape mark count */ return len; }; } /* Input is really a data file. */ l2 = read(fd, header, 5); if (l2 != 5 || strncmp(header, "CPTP:", 5) != 0) return FORMAT_ERROR; l2 = read(fd, header, 4); if (strncmp(header, "BLK ", 4) == 0) { l2 = read(fd, header, 7); if (l2 != 7) return FORMAT_ERROR; header[6] = '\0'; len = atoi(header); l2 = read(fd, tapebuf, len); if (l2 != len) return FORMAT_ERROR; read(fd, header, 1); /* skip trailing newline */ } else if (strncmp(header, "MRK\n", 4) == 0) return TAPE_MARK; else if (strncmp(header, "EOT\n", 4) == 0) return END_OF_TAPE; else return FORMAT_ERROR; return len; } /* * Copy a buffer out to a file or tape. * * If output is a tape, write the record. A length of zero indicates that * a tapemark should be written. * * If not a tape, write len to the output file, then the buffer. */ output(fd, len) int fd, len; { struct mtop op; char header[20]; if (totape && (len == TAPE_MARK || len == END_OF_TAPE)) { op.mt_op = MTWEOF; op.mt_count = 1; ioctl(fd, MTIOCTOP, &op); return; } if (!totape) { switch (len) { case TAPE_MARK: write(fd, "CPTP:MRK\n", 9); break; case END_OF_TAPE: write(fd, "CPTP:EOT\n", 9); break; case FORMAT_ERROR: break; default: sprintf(header, "CPTP:BLK %06d\n", len); write(fd, header, strlen(header)); write(fd, tapebuf, len); write(fd, "\n", 1); } } else write(fd, tapebuf, len); } @//E*O*F copytape.c// chmod u=rw,g=r,o=r copytape.c echo x - rmt.h sed 's/^@//' > "rmt.h" <<'@//E*O*F rmt.h//' /* * rmt.h * * Added routines to replace open(), close(), lseek(), ioctl(), etc. * The preprocessor can be used to remap these the rmtopen(), etc * thus minimizing source changes. * * This file must be included before <sys/stat.h>, since it redefines * stat to be rmtstat, so that struct stat xyzzy; declarations work * properly. * * -- Fred Fish (w/some changes by Arnold Robbins) */ #ifndef access /* avoid multiple redefinition */ #ifndef lint /* in this case what lint doesn't know won't hurt it */ #define access rmtaccess #define close rmtclose #define creat rmtcreat #define dup rmtdup #define fcntl rmtfcntl #define fstat rmtfstat #define ioctl rmtioctl #define isatty rmtisatty #define lseek rmtlseek #define lstat rmtlstat #define open rmtopen #define read rmtread #define stat rmtstat #define write rmtwrite extern long rmtlseek (); /* all the rest are int's */ #endif #endif @//E*O*F rmt.h// chmod u=rw,g=r,o=r rmt.h echo x - rmtlib.c sed 's/^@//' > "rmtlib.c" <<'@//E*O*F rmtlib.c//' /* * rmt --- remote tape emulator subroutines * * Originally written by Jeff Lee, modified some by Arnold Robbins * * WARNING: The man page rmt(8) for /etc/rmt documents the remote mag * tape protocol which rdump and rrestore use. Unfortunately, the man * page is *WRONG*. The author of the routines I'm including originally * wrote his code just based on the man page, and it didn't work, so he * went to the rdump source to figure out why. The only thing he had to * change was to check for the 'F' return code in addition to the 'E', * and to separate the various arguments with \n instead of a space. I * personally don't think that this is much of a problem, but I wanted to * point it out. * -- Arnold Robbins * * Redone as a library that can replace open, read, write, etc, by * Fred Fish, with some additional work by Arnold Robbins. */ /* * MAXUNIT --- Maximum number of remote tape file units * * READ --- Return the number of the read side file descriptor * WRITE --- Return the number of the write side file descriptor */ #define RMTIOCTL 1 #include <stdio.h> #include <signal.h> #include <sys/types.h> #ifdef RMTIOCTL #include <sys/ioctl.h> #include <sys/mtio.h> #endif #include <errno.h> #include <setjmp.h> #include <sys/stat.h> #define BUFMAGIC 64 /* a magic number for buffer sizes */ #define MAXUNIT 4 #define READ(fd) (Ctp[fd][0]) #define WRITE(fd) (Ptc[fd][1]) static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; static jmp_buf Jmpbuf; extern int errno; /* * abort --- close off a remote tape connection */ static void abort(fildes) int fildes; { close(READ(fildes)); close(WRITE(fildes)); READ(fildes) = -1; WRITE(fildes) = -1; } /* * command --- attempt to perform a remote tape command */ static int command(fildes, buf) int fildes; char *buf; { register int blen; int (*pstat)(); /* * save current pipe status and try to make the request */ blen = strlen(buf); pstat = signal(SIGPIPE, SIG_IGN); if (write(WRITE(fildes), buf, blen) == blen) { signal(SIGPIPE, pstat); return(0); } /* * something went wrong. close down and go home */ signal(SIGPIPE, pstat); abort(fildes); errno = EIO; return(-1); } /* * status --- retrieve the status from the pipe */ static int status(fildes) int fildes; { int i; char c, *cp; char buffer[BUFMAGIC]; int st; /* * read the reply command line */ for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++) { if ((st = read(READ(fildes), cp, 1)) != 1) { abort(fildes); errno = EIO; return(-1); } if (*cp == '\n') { *cp = 0; break; } } if (i == BUFMAGIC) { abort(fildes); errno = EIO; return(-1); } /* * check the return status */ for (cp = buffer; *cp; cp++) if (*cp != ' ') break; if (*cp == 'E' || *cp == 'F') { errno = atoi(cp + 1); while (read(READ(fildes), &c, 1) == 1) if (c == '\n') break; if (*cp == 'F') abort(fildes); return(-1); } /* * check for mis-synced pipes */ if (*cp != 'A') { abort(fildes); errno = EIO; return(-1); } return(atoi(cp + 1)); } /* * _rmt_open --- open a magtape device on system specified, as given user * * file name has the form system[.user]:/dev/???? */ #define MAXHOSTLEN 257 /* BSD allows very long host names... */ static int _rmt_open (path, oflag, mode) char *path; int oflag; int mode; { int i, rc; char buffer[BUFMAGIC]; char system[MAXHOSTLEN]; char device[BUFMAGIC]; char login[BUFMAGIC]; char *sys, *dev, *user; sys = system; dev = device; user = login; /* * first, find an open pair of file descriptors */ for (i = 0; i < MAXUNIT; i++) if (READ(i) == -1 && WRITE(i) == -1) break; if (i == MAXUNIT) { errno = EMFILE; return(-1); } /* * pull apart system and device, and optional user * don't munge original string */ while (*path != '.' && *path != ':') { *sys++ = *path++; } *sys = '\0'; path++; if (*(path - 1) == '.') { while (*path != ':') { *user++ = *path++; } *user = '\0'; path++; } else *user = '\0'; while (*path) { *dev++ = *path++; } *dev = '\0'; /* * setup the pipes for the 'rsh' command and fork */ if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1) return(-1); if ((rc = fork()) == -1) return(-1); if (rc == 0) { close(0); dup(Ptc[i][0]); close(Ptc[i][0]); close(Ptc[i][1]); close(1); dup(Ctp[i][1]); close(Ctp[i][0]); close(Ctp[i][1]); (void) setuid (getuid ()); (void) setgid (getgid ()); if (*user) { execl("/usr/ucb/rsh", "rsh", system, "-l", login, "/etc/rmt", (char *) 0); execl("/usr/bin/remsh", "remsh", system, "-l", login, "/etc/rmt", (char *) 0); } else { execl("/usr/ucb/rsh", "rsh", system, "/etc/rmt", (char *) 0); execl("/usr/bin/remsh", "remsh", system, "/etc/rmt", (char *) 0); } /* * bad problems if we get here */ perror("exec"); exit(1); } close(Ptc[i][0]); close(Ctp[i][1]); /* * now attempt to open the tape device */ sprintf(buffer, "O%s\n%d\n", device, oflag); if (command(i, buffer) == -1 || status(i) == -1) return(-1); return(i); } /* * _rmt_close --- close a remote magtape unit and shut down */ static int _rmt_close(fildes) int fildes; { int rc; if (command(fildes, "C\n") != -1) { rc = status(fildes); abort(fildes); return(rc); } return(-1); } /* * _rmt_read --- read a buffer from a remote tape */ static int _rmt_read(fildes, buf, nbyte) int fildes; char *buf; unsigned int nbyte; { int rc, i; char buffer[BUFMAGIC]; sprintf(buffer, "R%d\n", nbyte); if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1) return(-1); for (i = 0; i < rc; i += nbyte, buf += nbyte) { nbyte = read(READ(fildes), buf, rc); if (nbyte <= 0) { abort(fildes); errno = EIO; return(-1); } } return(rc); } /* * _rmt_write --- write a buffer to the remote tape */ static int _rmt_write(fildes, buf, nbyte) int fildes; char *buf; unsigned int nbyte; { int rc; char buffer[BUFMAGIC]; int (*pstat)(); sprintf(buffer, "W%d\n", nbyte); if (command(fildes, buffer) == -1) return(-1); pstat = signal(SIGPIPE, SIG_IGN); if (write(WRITE(fildes), buf, nbyte) == nbyte) { signal (SIGPIPE, pstat); return(status(fildes)); } signal (SIGPIPE, pstat); abort(fildes); errno = EIO; return(-1); } /* * _rmt_lseek --- perform an imitation lseek operation remotely */ static long _rmt_lseek(fildes, offset, whence) int fildes; long offset; int whence; { char buffer[BUFMAGIC]; sprintf(buffer, "L%d\n%d\n", offset, whence); if (command(fildes, buffer) == -1) return(-1); return(status(fildes)); } /* * _rmt_ioctl --- perform raw tape operations remotely */ #ifdef RMTIOCTL static _rmt_ioctl(fildes, op, arg) int fildes, op; char *arg; { char c; int rc, cnt; char buffer[BUFMAGIC]; /* * MTIOCOP is the easy one. nothing is transfered in binary */ if (op == MTIOCTOP) { sprintf(buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op, ((struct mtop *) arg)->mt_count); if (command(fildes, buffer) == -1) return(-1); return(status(fildes)); } /* * we can only handle 2 ops, if not the other one, punt */ if (op != MTIOCGET) { errno = EINVAL; return(-1); } /* * grab the status and read it directly into the structure * this assumes that the status buffer is (hopefully) not * padded and that 2 shorts fit in a long without any word * alignment problems, ie - the whole struct is contiguous * NOTE - this is probably NOT a good assumption. */ /* * This doesn't work right so punt! */ errno = 0; return(0); #ifdef notdef if (command(fildes, "S\n") == -1 || (rc = status(fildes)) == -1) return(-1); for (; rc > 0; rc -= cnt, arg += cnt) { cnt = read(READ(fildes), arg, rc); if (cnt <= 0) { abort(fildes); errno = EIO; return(-1); } } /* * now we check for byte position. mt_type is a small integer field * (normally) so we will check its magnitude. if it is larger than * 256, we will assume that the bytes are swapped and go through * and reverse all the bytes */ if (((struct mtget *) arg)->mt_type < 256) return(0); for (cnt = 0; cnt < rc; cnt += 2) { c = arg[cnt]; arg[cnt] = arg[cnt+1]; arg[cnt+1] = c; } return(0); #endif } #endif /* RMTIOCTL */ /* * Added routines to replace open(), close(), lseek(), ioctl(), etc. * The preprocessor can be used to remap these the rmtopen(), etc * thus minimizing source changes: * * #ifdef <something> * # define access rmtaccess * # define close rmtclose * # define creat rmtcreat * # define dup rmtdup * # define fcntl rmtfcntl * # define fstat rmtfstat * # define ioctl rmtioctl * # define isatty rmtisatty * # define lseek rmtlseek * # define lstat rmtlstat * # define open rmtopen * # define read rmtread * # define stat rmtstat * # define write rmtwrite * # define access rmtaccess * # define close rmtclose * # define creat rmtcreat * # define dup rmtdup * # define fcntl rmtfcntl * # define fstat rmtfstat * # define ioctl rmtioctl * # define lseek rmtlseek * # define open rmtopen * # define read rmtread * # define stat rmtstat * # define write rmtwrite * #endif * * -- Fred Fish * * ADR --- I set up a <rmt.h> include file for this * */ /* * Note that local vs remote file descriptors are distinquished * by adding a bias to the remote descriptors. This is a quick * and dirty trick that may not be portable to some systems. */ #define REM_BIAS 128 /* * Test pathname to see if it is local or remote. A remote device * is any string that contains ":/dev/". Returns 1 if remote, * 0 otherwise. */ static int remdev (path) register char *path; { #define strchr index extern char *strchr (); if ((path = strchr (path, ':')) != NULL) { if (strncmp (path + 1, "/dev/", 5) == 0) { return (1); } } return (0); } /* * Open a local or remote file. Looks just like open(2) to * caller. */ int rmtopen (path, oflag, mode) char *path; int oflag; int mode; { if (remdev (path)) { return (_rmt_open (path, oflag, mode) + REM_BIAS); } else { return (open (path, oflag, mode)); } } /* * Test pathname for specified access. Looks just like access(2) * to caller. */ int rmtaccess (path, amode) char *path; int amode; { if (remdev (path)) { return (0); /* Let /etc/rmt find out */ } else { return (access (path, amode)); } } /* * Read from stream. Looks just like read(2) to caller. */ int rmtread (fildes, buf, nbyte) int fildes; char *buf; unsigned int nbyte; { if (isrmt (fildes)) { return (_rmt_read (fildes - REM_BIAS, buf, nbyte)); } else { return (read (fildes, buf, nbyte)); } } /* * Write to stream. Looks just like write(2) to caller. */ int rmtwrite (fildes, buf, nbyte) int fildes; char *buf; unsigned int nbyte; { if (isrmt (fildes)) { return (_rmt_write (fildes - REM_BIAS, buf, nbyte)); } else { return (write (fildes, buf, nbyte)); } } /* * Perform lseek on file. Looks just like lseek(2) to caller. */ long rmtlseek (fildes, offset, whence) int fildes; long offset; int whence; { if (isrmt (fildes)) { return (_rmt_lseek (fildes - REM_BIAS, offset, whence)); } else { return (lseek (fildes, offset, whence)); } } /* * Close a file. Looks just like close(2) to caller. */ int rmtclose (fildes) int fildes; { if (isrmt (fildes)) { return (_rmt_close (fildes - REM_BIAS)); } else { return (close (fildes)); } } /* * Do ioctl on file. Looks just like ioctl(2) to caller. */ int rmtioctl (fildes, request, arg) int fildes, request, arg; { if (isrmt (fildes)) { #ifdef RMTIOCTL return (_rmt_ioctl (fildes - REM_BIAS, request, arg)); #else errno = EOPNOTSUPP; return (-1); /* For now (fnf) */ #endif } else { return (ioctl (fildes, request, arg)); } } /* * Duplicate an open file descriptor. Looks just like dup(2) * to caller. */ int rmtdup (fildes) int fildes; { if (isrmt (fildes)) { errno = EOPNOTSUPP; return (-1); /* For now (fnf) */ } else { return (dup (fildes)); } } /* * Get file status. Looks just like fstat(2) to caller. */ int rmtfstat (fildes, buf) int fildes; struct stat *buf; { if (isrmt (fildes)) { errno = EOPNOTSUPP; return (-1); /* For now (fnf) */ } else { return (fstat (fildes, buf)); } } /* * Get file status. Looks just like stat(2) to caller. */ int rmtstat (path, buf) char *path; struct stat *buf; { if (remdev (path)) { errno = EOPNOTSUPP; return (-1); /* For now (fnf) */ } else { return (stat (path, buf)); } } /* * Create a file from scratch. Looks just like creat(2) to the caller. */ #include <sys/file.h> /* BSD DEPENDANT!!! */ /* #include <fcntl.h> /* use this one for S5 with remote stuff */ int rmtcreat (path, mode) char *path; int mode; { if (remdev (path)) { return (rmtopen (path, 1 | O_CREAT, mode)); } else { return (creat (path, mode)); } } /* * Isrmt. Let a programmer know he has a remote device. */ int isrmt (fd) int fd; { return (fd >= REM_BIAS); } /* * Rmtfcntl. Do a remote fcntl operation. */ int rmtfcntl (fd, cmd, arg) int fd, cmd, arg; { if (isrmt (fd)) { errno = EOPNOTSUPP; return (-1); } else { return (fcntl (fd, cmd, arg)); } } /* * Rmtisatty. Do the isatty function. */ int rmtisatty (fd) int fd; { if (isrmt (fd)) return (0); else return (isatty (fd)); } /* * Get file status, even if symlink. Looks just like lstat(2) to caller. */ int rmtlstat (path, buf) char *path; struct stat *buf; { if (remdev (path)) { errno = EOPNOTSUPP; return (-1); /* For now (fnf) */ } else { return (lstat (path, buf)); } } @//E*O*F rmtlib.c// chmod u=rw,g=r,o=r rmtlib.c echo Inspecting for damage in transit... temp=/tmp/shar$$; dtemp=/tmp/.shar$$ trap "rm -f $temp $dtemp; exit" 0 1 2 3 15 cat > $temp <<\!!! 21 47 354 Makefile 25 136 774 README.network 132 711 4256 copytape.1 50 259 1472 copytape.5 302 951 6549 copytape.c 35 144 904 rmt.h 845 2292 13807 rmtlib.c 1410 4540 28116 total !!! wc Makefile README.network copytape.1 copytape.5 copytape.c rmt.h rmtlib.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp if [ -s $dtemp ] then echo "Ouch [diff of wc output]:" ; cat $dtemp else echo "No problems found." fi exit 0 -- David Robinson elroy!david@csvax.caltech.edu ARPA david@elroy.jpl.nasa.gov ARPA {cit-vax,ames}!elroy!david UUCP Disclaimer: No one listens to me anyway!