GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/rmt/rmt.c Lines: 0 144 0.0 %
Date: 2017-11-07 Branches: 0 116 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: rmt.c,v 1.20 2016/08/14 18:34:48 guenther Exp $	*/
2
3
/*
4
 * Copyright (c) 1983 Regents of the University of California.
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 * 3. Neither the name of the University nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
32
/*
33
 * rmt
34
 */
35
#include <sys/types.h>
36
#include <sys/socket.h>
37
#include <sys/file.h>
38
#include <sys/stat.h>
39
#include <sys/ioctl.h>
40
#include <sys/mtio.h>
41
#include <unistd.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <err.h>
45
#include <errno.h>
46
#include <string.h>
47
#include <limits.h>
48
49
int	tape = -1;
50
51
char	*record;
52
int	maxrecsize = -1;
53
54
#define	STRSIZE	64
55
char	device[PATH_MAX];
56
char	lastdevice[PATH_MAX] = "";
57
char	count[STRSIZE], mode[STRSIZE], pos[STRSIZE], op[STRSIZE];
58
59
char	resp[BUFSIZ];
60
61
FILE	*debug;
62
#define	DEBUG(f)	if (debug) fprintf(debug, f)
63
#define	DEBUG1(f,a)	if (debug) fprintf(debug, f, a)
64
#define	DEBUG2(f,a1,a2)	if (debug) fprintf(debug, f, a1, a2)
65
66
char		*checkbuf(char *, int);
67
void		getstring(char *, int);
68
void		error(int);
69
__dead void	usage(void);
70
71
int
72
main(int argc, char *argv[])
73
{
74
	off_t orval;
75
	int rval;
76
	char c;
77
	int n, i, cc;
78
	int ch, rflag = 0, wflag = 0;
79
	int f, acc;
80
	mode_t m;
81
	char *dir = NULL;
82
	char *devp;
83
	size_t dirlen;
84
85
	if (pledge("stdio rpath wpath cpath inet flock", NULL) == -1)
86
		err(1, "pledge");
87
88
	while ((ch = getopt(argc, argv, "d:rw")) != -1) {
89
		switch (ch) {
90
		case 'd':
91
			dir = optarg;
92
			if (*dir != '/')
93
				errx(1, "directory must be absolute");
94
			break;
95
		case 'r':
96
			rflag = 1;
97
			break;
98
		case 'w':
99
			wflag = 1;
100
			break;
101
		default:
102
			usage();
103
			/* NOTREACHED */
104
		}
105
	}
106
	argc -= optind;
107
	argv += optind;
108
109
	if (rflag && wflag)
110
		usage();
111
112
	if (argc > 0) {
113
		debug = fopen(*argv, "w");
114
		if (debug == 0)
115
			err(1, "cannot open debug file");
116
		setvbuf(debug, NULL, _IONBF, 0);
117
	}
118
119
	if (dir) {
120
		if (chdir(dir) != 0)
121
			err(1, "chdir");
122
		dirlen = strlen(dir);
123
	}
124
125
top:
126
	errno = 0;
127
	rval = 0;
128
	if (read(STDIN_FILENO, &c, 1) != 1)
129
		exit(0);
130
	switch (c) {
131
132
	case 'O':
133
		if (tape >= 0)
134
			(void) close(tape);
135
		getstring(device, sizeof(device));
136
		getstring(mode, sizeof(mode));
137
		DEBUG2("rmtd: O %s %s\n", device, mode);
138
139
		devp = device;
140
		f = atoi(mode);
141
		m = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
142
		acc = f & O_ACCMODE;
143
		if (dir) {
144
			/* Strip away valid directory prefix. */
145
			if (strncmp(dir, devp, dirlen) == 0 &&
146
			    (devp[dirlen - 1] == '/' ||
147
			     devp[dirlen] == '/')) {
148
			     devp += dirlen;
149
			     while (*devp == '/')
150
				devp++;
151
			}
152
			/* Don't allow directory traversal. */
153
			if (strchr(devp, '/')) {
154
				errno = EACCES;
155
				goto ioerror;
156
			}
157
			f |= O_NOFOLLOW;
158
		}
159
		if (rflag) {
160
			/*
161
			 * Only allow readonly open and ignore file
162
			 * creation requests.
163
			 */
164
			if (acc != O_RDONLY) {
165
				errno = EPERM;
166
				goto ioerror;
167
			}
168
			f &= ~O_CREAT;
169
		} else if (wflag) {
170
			/*
171
			 * Require, and force creation of, a nonexistant file,
172
			 * unless we are reopening the last opened file again,
173
			 * in which case it is opened read-only.
174
			 */
175
			if (strcmp(devp, lastdevice) != 0) {
176
				/*
177
				 * Disallow read-only open since that would
178
				 * only result in an empty file.
179
				 */
180
				if (acc == O_RDONLY) {
181
					errno = EPERM;
182
					goto ioerror;
183
				}
184
				f |= O_CREAT | O_EXCL;
185
			} else {
186
				acc = O_RDONLY;
187
			}
188
			/* Create readonly file */
189
			m = S_IRUSR|S_IRGRP|S_IROTH;
190
		}
191
		/* Apply new access mode. */
192
		f = (f & ~O_ACCMODE) | acc;
193
194
		tape = open(devp, f, m);
195
		if (tape == -1)
196
			goto ioerror;
197
		(void)strlcpy(lastdevice, devp, sizeof(lastdevice));
198
		goto respond;
199
200
	case 'C':
201
		DEBUG("rmtd: C\n");
202
		getstring(device, sizeof(device));	/* discard */
203
		if (close(tape) == -1)
204
			goto ioerror;
205
		tape = -1;
206
		goto respond;
207
208
	case 'L':
209
		getstring(count, sizeof(count));
210
		getstring(pos, sizeof(pos));
211
		DEBUG2("rmtd: L %s %s\n", count, pos);
212
		orval = lseek(tape, strtoll(count, NULL, 0), atoi(pos));
213
		if (orval == -1)
214
			goto ioerror;
215
		goto respond;
216
217
	case 'W':
218
		getstring(count, sizeof(count));
219
		n = atoi(count);
220
		DEBUG1("rmtd: W %s\n", count);
221
		record = checkbuf(record, n);
222
		for (i = 0; i < n; i += cc) {
223
			cc = read(STDIN_FILENO, &record[i], n - i);
224
			if (cc <= 0) {
225
				DEBUG("rmtd: premature eof\n");
226
				exit(2);
227
			}
228
		}
229
		rval = write(tape, record, n);
230
		if (rval < 0)
231
			goto ioerror;
232
		goto respond;
233
234
	case 'R':
235
		getstring(count, sizeof(count));
236
		DEBUG1("rmtd: R %s\n", count);
237
		n = atoi(count);
238
		record = checkbuf(record, n);
239
		rval = read(tape, record, n);
240
		if (rval < 0)
241
			goto ioerror;
242
		(void) snprintf(resp, sizeof resp, "A%d\n", rval);
243
		(void) write(STDOUT_FILENO, resp, strlen(resp));
244
		(void) write(STDOUT_FILENO, record, rval);
245
		goto top;
246
247
	case 'I':
248
		getstring(op, sizeof(op));
249
		getstring(count, sizeof(count));
250
		DEBUG2("rmtd: I %s %s\n", op, count);
251
		{ struct mtop mtop;
252
		  mtop.mt_op = atoi(op);
253
		  mtop.mt_count = atoi(count);
254
		  if (ioctl(tape, MTIOCTOP, (char *)&mtop) == -1)
255
			goto ioerror;
256
		  rval = mtop.mt_count;
257
		}
258
		goto respond;
259
260
	case 'S':		/* status */
261
		DEBUG("rmtd: S\n");
262
		{ struct mtget mtget;
263
		  if (ioctl(tape, MTIOCGET, (char *)&mtget) == -1)
264
			goto ioerror;
265
		  rval = sizeof (mtget);
266
		  (void) snprintf(resp, sizeof resp, "A%d\n", rval);
267
		  (void) write(STDOUT_FILENO, resp, strlen(resp));
268
		  (void) write(STDOUT_FILENO, (char *)&mtget, sizeof (mtget));
269
		  goto top;
270
		}
271
272
	default:
273
		DEBUG1("rmtd: garbage command %c\n", c);
274
		exit(3);
275
	}
276
respond:
277
	DEBUG1("rmtd: A %d\n", rval);
278
	(void) snprintf(resp, sizeof resp, "A%d\n", rval);
279
	(void) write(STDOUT_FILENO, resp, strlen(resp));
280
	goto top;
281
ioerror:
282
	error(errno);
283
	goto top;
284
}
285
286
void
287
getstring(char *bp, int size)
288
{
289
	char *cp = bp;
290
	char *ep = bp + size - 1;
291
292
	do {
293
		if (read(STDIN_FILENO, cp, 1) != 1)
294
			exit(0);
295
	} while (*cp != '\n' && ++cp < ep);
296
	*cp = '\0';
297
}
298
299
char *
300
checkbuf(char *record, int size)
301
{
302
	if (size <= maxrecsize)
303
		return (record);
304
	if (record != 0)
305
		free(record);
306
	record = malloc(size);
307
	if (record == 0) {
308
		DEBUG("rmtd: cannot allocate buffer space\n");
309
		exit(4);
310
	}
311
	maxrecsize = size;
312
	while (size > 1024 &&
313
	    setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) == -1)
314
		size -= 1024;
315
	return (record);
316
}
317
318
void
319
error(int num)
320
{
321
322
	DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
323
	(void) snprintf(resp, sizeof (resp), "E%d\n%s\n", num, strerror(num));
324
	(void) write(STDOUT_FILENO, resp, strlen(resp));
325
}
326
327
__dead void
328
usage(void)
329
{
330
	extern char *__progname;
331
332
	(void)fprintf(stderr, "usage: %s [-r | -w] [-d directory]\n",
333
	    __progname);
334
	exit(1);
335
}