1*df69c215Sderaadt /* $OpenBSD: rmt.c,v 1.23 2019/06/28 13:32:50 deraadt Exp $ */
215a14482Sjason
3df930be7Sderaadt /*
4df930be7Sderaadt * Copyright (c) 1983 Regents of the University of California.
5df930be7Sderaadt * All rights reserved.
6df930be7Sderaadt *
7df930be7Sderaadt * Redistribution and use in source and binary forms, with or without
8df930be7Sderaadt * modification, are permitted provided that the following conditions
9df930be7Sderaadt * are met:
10df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright
11df930be7Sderaadt * notice, this list of conditions and the following disclaimer.
12df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
13df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the
14df930be7Sderaadt * documentation and/or other materials provided with the distribution.
1529295d1cSmillert * 3. Neither the name of the University nor the names of its contributors
16df930be7Sderaadt * may be used to endorse or promote products derived from this software
17df930be7Sderaadt * without specific prior written permission.
18df930be7Sderaadt *
19df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29df930be7Sderaadt * SUCH DAMAGE.
30df930be7Sderaadt */
31df930be7Sderaadt
32df930be7Sderaadt /*
33df930be7Sderaadt * rmt
34df930be7Sderaadt */
35df930be7Sderaadt #include <sys/types.h>
36df930be7Sderaadt #include <sys/socket.h>
37df930be7Sderaadt #include <sys/stat.h>
38975bf715Sderaadt #include <sys/ioctl.h>
39df930be7Sderaadt #include <sys/mtio.h>
40f4147939Sguenther
41f996714dSderaadt #include <unistd.h>
42975bf715Sderaadt #include <stdio.h>
43f996714dSderaadt #include <stdlib.h>
4471c04358Shalex #include <err.h>
45df930be7Sderaadt #include <errno.h>
46f4147939Sguenther #include <fcntl.h>
47df930be7Sderaadt #include <string.h>
48b9fc9a72Sderaadt #include <limits.h>
49df930be7Sderaadt
50df930be7Sderaadt int tape = -1;
51df930be7Sderaadt
52df930be7Sderaadt char *record;
53df930be7Sderaadt int maxrecsize = -1;
54df930be7Sderaadt
5515a14482Sjason #define STRSIZE 64
56b9fc9a72Sderaadt char device[PATH_MAX];
5771c04358Shalex char lastdevice[PATH_MAX] = "";
5815a14482Sjason char count[STRSIZE], mode[STRSIZE], pos[STRSIZE], op[STRSIZE];
59df930be7Sderaadt
60df930be7Sderaadt char resp[BUFSIZ];
61df930be7Sderaadt
62df930be7Sderaadt FILE *debug;
63df930be7Sderaadt #define DEBUG(f) if (debug) fprintf(debug, f)
64df930be7Sderaadt #define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
65df930be7Sderaadt #define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
66df930be7Sderaadt
67c72b5b24Smillert char *checkbuf(char *, int);
686d921fc3Smillert void getstring(char *, int);
69c72b5b24Smillert void error(int);
7071c04358Shalex __dead void usage(void);
71f996714dSderaadt
72f996714dSderaadt int
main(int argc,char * argv[])737e3b3a6dSmillert main(int argc, char *argv[])
74df930be7Sderaadt {
75ee83a072Sderaadt off_t orval;
76df930be7Sderaadt int rval;
77df930be7Sderaadt char c;
78df930be7Sderaadt int n, i, cc;
7971c04358Shalex int ch, rflag = 0, wflag = 0;
8071c04358Shalex int f, acc;
8171c04358Shalex mode_t m;
8271c04358Shalex char *dir = NULL;
8371c04358Shalex char *devp;
8471c04358Shalex size_t dirlen;
85df930be7Sderaadt
86c169fbf6Sderaadt if (pledge("stdio rpath wpath cpath inet", NULL) == -1)
87c169fbf6Sderaadt err(1, "pledge");
88c169fbf6Sderaadt
8971c04358Shalex while ((ch = getopt(argc, argv, "d:rw")) != -1) {
9071c04358Shalex switch (ch) {
9171c04358Shalex case 'd':
9271c04358Shalex dir = optarg;
9371c04358Shalex if (*dir != '/')
9471c04358Shalex errx(1, "directory must be absolute");
9571c04358Shalex break;
9671c04358Shalex case 'r':
9771c04358Shalex rflag = 1;
9871c04358Shalex break;
9971c04358Shalex case 'w':
10071c04358Shalex wflag = 1;
10171c04358Shalex break;
10271c04358Shalex default:
10371c04358Shalex usage();
10471c04358Shalex /* NOTREACHED */
10571c04358Shalex }
10671c04358Shalex }
10771c04358Shalex argc -= optind;
10871c04358Shalex argv += optind;
10971c04358Shalex
11071c04358Shalex if (rflag && wflag)
11171c04358Shalex usage();
11271c04358Shalex
113df930be7Sderaadt if (argc > 0) {
114df930be7Sderaadt debug = fopen(*argv, "w");
115df930be7Sderaadt if (debug == 0)
11671c04358Shalex err(1, "cannot open debug file");
1179881b83cStedu setvbuf(debug, NULL, _IONBF, 0);
118df930be7Sderaadt }
11971c04358Shalex
12071c04358Shalex if (dir) {
12171c04358Shalex if (chdir(dir) != 0)
12271c04358Shalex err(1, "chdir");
12371c04358Shalex dirlen = strlen(dir);
12471c04358Shalex }
12571c04358Shalex
126df930be7Sderaadt top:
127df930be7Sderaadt errno = 0;
128df930be7Sderaadt rval = 0;
1297e3b3a6dSmillert if (read(STDIN_FILENO, &c, 1) != 1)
130df930be7Sderaadt exit(0);
131df930be7Sderaadt switch (c) {
132df930be7Sderaadt
133df930be7Sderaadt case 'O':
134df930be7Sderaadt if (tape >= 0)
135df930be7Sderaadt (void) close(tape);
1366d921fc3Smillert getstring(device, sizeof(device));
1376d921fc3Smillert getstring(mode, sizeof(mode));
138df930be7Sderaadt DEBUG2("rmtd: O %s %s\n", device, mode);
13971c04358Shalex
14071c04358Shalex devp = device;
14171c04358Shalex f = atoi(mode);
14271c04358Shalex m = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
14371c04358Shalex acc = f & O_ACCMODE;
14471c04358Shalex if (dir) {
14571c04358Shalex /* Strip away valid directory prefix. */
14671c04358Shalex if (strncmp(dir, devp, dirlen) == 0 &&
14771c04358Shalex (devp[dirlen - 1] == '/' ||
14871c04358Shalex devp[dirlen] == '/')) {
14971c04358Shalex devp += dirlen;
15071c04358Shalex while (*devp == '/')
15171c04358Shalex devp++;
15271c04358Shalex }
15371c04358Shalex /* Don't allow directory traversal. */
15471c04358Shalex if (strchr(devp, '/')) {
15571c04358Shalex errno = EACCES;
15671c04358Shalex goto ioerror;
15771c04358Shalex }
15871c04358Shalex f |= O_NOFOLLOW;
15971c04358Shalex }
16071c04358Shalex if (rflag) {
16171c04358Shalex /*
16271c04358Shalex * Only allow readonly open and ignore file
16371c04358Shalex * creation requests.
16471c04358Shalex */
16571c04358Shalex if (acc != O_RDONLY) {
16671c04358Shalex errno = EPERM;
16771c04358Shalex goto ioerror;
16871c04358Shalex }
16971c04358Shalex f &= ~O_CREAT;
17071c04358Shalex } else if (wflag) {
17171c04358Shalex /*
1726ba149ccSphessler * Require, and force creation of, a nonexistent file,
17371c04358Shalex * unless we are reopening the last opened file again,
17471c04358Shalex * in which case it is opened read-only.
17571c04358Shalex */
17671c04358Shalex if (strcmp(devp, lastdevice) != 0) {
17771c04358Shalex /*
17871c04358Shalex * Disallow read-only open since that would
17971c04358Shalex * only result in an empty file.
18071c04358Shalex */
18171c04358Shalex if (acc == O_RDONLY) {
18271c04358Shalex errno = EPERM;
18371c04358Shalex goto ioerror;
18471c04358Shalex }
18571c04358Shalex f |= O_CREAT | O_EXCL;
18671c04358Shalex } else {
18771c04358Shalex acc = O_RDONLY;
18871c04358Shalex }
18971c04358Shalex /* Create readonly file */
19071c04358Shalex m = S_IRUSR|S_IRGRP|S_IROTH;
19171c04358Shalex }
19271c04358Shalex /* Apply new access mode. */
19371c04358Shalex f = (f & ~O_ACCMODE) | acc;
19471c04358Shalex
19571c04358Shalex tape = open(devp, f, m);
196ee83a072Sderaadt if (tape == -1)
197df930be7Sderaadt goto ioerror;
19871c04358Shalex (void)strlcpy(lastdevice, devp, sizeof(lastdevice));
199df930be7Sderaadt goto respond;
200df930be7Sderaadt
201df930be7Sderaadt case 'C':
202df930be7Sderaadt DEBUG("rmtd: C\n");
2036d921fc3Smillert getstring(device, sizeof(device)); /* discard */
204ee83a072Sderaadt if (close(tape) == -1)
205df930be7Sderaadt goto ioerror;
206df930be7Sderaadt tape = -1;
207df930be7Sderaadt goto respond;
208df930be7Sderaadt
209df930be7Sderaadt case 'L':
2106d921fc3Smillert getstring(count, sizeof(count));
2116d921fc3Smillert getstring(pos, sizeof(pos));
212df930be7Sderaadt DEBUG2("rmtd: L %s %s\n", count, pos);
213f5cc6d1cSguenther orval = lseek(tape, strtoll(count, NULL, 0), atoi(pos));
214ee83a072Sderaadt if (orval == -1)
215df930be7Sderaadt goto ioerror;
216df930be7Sderaadt goto respond;
217df930be7Sderaadt
218df930be7Sderaadt case 'W':
2196d921fc3Smillert getstring(count, sizeof(count));
220df930be7Sderaadt n = atoi(count);
221df930be7Sderaadt DEBUG1("rmtd: W %s\n", count);
222df930be7Sderaadt record = checkbuf(record, n);
223df930be7Sderaadt for (i = 0; i < n; i += cc) {
2247e3b3a6dSmillert cc = read(STDIN_FILENO, &record[i], n - i);
225df930be7Sderaadt if (cc <= 0) {
226df930be7Sderaadt DEBUG("rmtd: premature eof\n");
227df930be7Sderaadt exit(2);
228df930be7Sderaadt }
229df930be7Sderaadt }
230df930be7Sderaadt rval = write(tape, record, n);
231*df69c215Sderaadt if (rval == -1)
232df930be7Sderaadt goto ioerror;
233df930be7Sderaadt goto respond;
234df930be7Sderaadt
235df930be7Sderaadt case 'R':
2366d921fc3Smillert getstring(count, sizeof(count));
237df930be7Sderaadt DEBUG1("rmtd: R %s\n", count);
238df930be7Sderaadt n = atoi(count);
239df930be7Sderaadt record = checkbuf(record, n);
240df930be7Sderaadt rval = read(tape, record, n);
241*df69c215Sderaadt if (rval == -1)
242df930be7Sderaadt goto ioerror;
243ead3e1caSderaadt (void) snprintf(resp, sizeof resp, "A%d\n", rval);
2447e3b3a6dSmillert (void) write(STDOUT_FILENO, resp, strlen(resp));
2457e3b3a6dSmillert (void) write(STDOUT_FILENO, record, rval);
246df930be7Sderaadt goto top;
247df930be7Sderaadt
248df930be7Sderaadt case 'I':
2496d921fc3Smillert getstring(op, sizeof(op));
2506d921fc3Smillert getstring(count, sizeof(count));
251df930be7Sderaadt DEBUG2("rmtd: I %s %s\n", op, count);
252df930be7Sderaadt { struct mtop mtop;
253df930be7Sderaadt mtop.mt_op = atoi(op);
254df930be7Sderaadt mtop.mt_count = atoi(count);
255ee83a072Sderaadt if (ioctl(tape, MTIOCTOP, (char *)&mtop) == -1)
256df930be7Sderaadt goto ioerror;
257df930be7Sderaadt rval = mtop.mt_count;
258df930be7Sderaadt }
259df930be7Sderaadt goto respond;
260df930be7Sderaadt
261df930be7Sderaadt case 'S': /* status */
262df930be7Sderaadt DEBUG("rmtd: S\n");
263df930be7Sderaadt { struct mtget mtget;
264ee83a072Sderaadt if (ioctl(tape, MTIOCGET, (char *)&mtget) == -1)
265df930be7Sderaadt goto ioerror;
266df930be7Sderaadt rval = sizeof (mtget);
267ead3e1caSderaadt (void) snprintf(resp, sizeof resp, "A%d\n", rval);
2687e3b3a6dSmillert (void) write(STDOUT_FILENO, resp, strlen(resp));
2697e3b3a6dSmillert (void) write(STDOUT_FILENO, (char *)&mtget, sizeof (mtget));
270df930be7Sderaadt goto top;
271df930be7Sderaadt }
272df930be7Sderaadt
273df930be7Sderaadt default:
274df930be7Sderaadt DEBUG1("rmtd: garbage command %c\n", c);
275df930be7Sderaadt exit(3);
276df930be7Sderaadt }
277df930be7Sderaadt respond:
278df930be7Sderaadt DEBUG1("rmtd: A %d\n", rval);
279ead3e1caSderaadt (void) snprintf(resp, sizeof resp, "A%d\n", rval);
2807e3b3a6dSmillert (void) write(STDOUT_FILENO, resp, strlen(resp));
281df930be7Sderaadt goto top;
282df930be7Sderaadt ioerror:
283df930be7Sderaadt error(errno);
284df930be7Sderaadt goto top;
285df930be7Sderaadt }
286df930be7Sderaadt
287f996714dSderaadt void
getstring(char * bp,int size)2887e3b3a6dSmillert getstring(char *bp, int size)
289df930be7Sderaadt {
290df930be7Sderaadt char *cp = bp;
2916d921fc3Smillert char *ep = bp + size - 1;
292df930be7Sderaadt
2936d921fc3Smillert do {
2947e3b3a6dSmillert if (read(STDIN_FILENO, cp, 1) != 1)
295df930be7Sderaadt exit(0);
2966d921fc3Smillert } while (*cp != '\n' && ++cp < ep);
2976d921fc3Smillert *cp = '\0';
298df930be7Sderaadt }
299df930be7Sderaadt
300df930be7Sderaadt char *
checkbuf(char * record,int size)3017e3b3a6dSmillert checkbuf(char *record, int size)
302df930be7Sderaadt {
303df930be7Sderaadt if (size <= maxrecsize)
304df930be7Sderaadt return (record);
305df930be7Sderaadt if (record != 0)
306df930be7Sderaadt free(record);
307df930be7Sderaadt record = malloc(size);
308df930be7Sderaadt if (record == 0) {
309df930be7Sderaadt DEBUG("rmtd: cannot allocate buffer space\n");
310df930be7Sderaadt exit(4);
311df930be7Sderaadt }
312df930be7Sderaadt maxrecsize = size;
313df930be7Sderaadt while (size > 1024 &&
314ee83a072Sderaadt setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) == -1)
315df930be7Sderaadt size -= 1024;
316df930be7Sderaadt return (record);
317df930be7Sderaadt }
318df930be7Sderaadt
319f996714dSderaadt void
error(int num)3207e3b3a6dSmillert error(int num)
321df930be7Sderaadt {
322df930be7Sderaadt
323df930be7Sderaadt DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
324550179eeSmillert (void) snprintf(resp, sizeof (resp), "E%d\n%s\n", num, strerror(num));
3257e3b3a6dSmillert (void) write(STDOUT_FILENO, resp, strlen(resp));
326df930be7Sderaadt }
32771c04358Shalex
32871c04358Shalex __dead void
usage(void)32971c04358Shalex usage(void)
33071c04358Shalex {
33171c04358Shalex extern char *__progname;
33271c04358Shalex
33371c04358Shalex (void)fprintf(stderr, "usage: %s [-r | -w] [-d directory]\n",
33471c04358Shalex __progname);
33571c04358Shalex exit(1);
33671c04358Shalex }
337