]> git.wh0rd.org - dump.git/blob - rmt/rmt.c
3bd6207406336874a960410a365583ecc597eed2
[dump.git] / rmt / rmt.c
1 /*
2 * Ported to Linux's Second Extended File System as part of the
3 * dump and restore backup suit
4 * Remy Card <card@Linux.EU.Org>, 1994-1997
5 * Stelian Pop <stelian@popies.net>, 1999-2000
6 * Stelian Pop <stelian@popies.net> - Alcôve <www.alcove.com>, 2000-2002
7 */
8
9 /*
10 * Copyright (c) 1983, 1993
11 * The Regents of the University of California. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #ifndef lint
39 static const char rcsid[] =
40 "$Id: rmt.c,v 1.25 2003/03/31 10:09:41 stelian Exp $";
41 #endif /* not linux */
42
43 /*
44 * rmt
45 */
46 #include <config.h>
47 #include <compatlfs.h>
48 #include <rmtflags.h>
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <sys/mtio.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #ifndef __linux__
55 #include <sgtty.h>
56 #endif
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 static int tape = -1;
63
64 static char *record;
65 static int maxrecsize = -1;
66
67 #define SSIZE 64
68 static char device[SSIZE];
69 static char count[SSIZE], filemode[SSIZE], pos[SSIZE], op[SSIZE];
70
71 static char resp[BUFSIZ];
72
73 static FILE *debug;
74 #define DEBUG(f) if (debug) fprintf(debug, f)
75 #define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
76 #define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
77
78 /*
79 * Support for Sun's extended RMT protocol
80 * code originally written by Jörg Schilling <schilling@fokus.gmd.de>
81 * and relicensed by his permission from GPL to BSD for use in dump.
82 *
83 * rmt_version is 0 for regular clients (Linux included)
84 * rmt_version is 1 for extended clients (Sun especially). In this case
85 * we support some extended commands (see below) and we remap
86 * the ioctl commands to the UNIX "standard", as per:
87 * ftp://ftp.fokus.gmd.de/pub/unix/star/README.mtio
88 *
89 * In order to use rmt version 1, a client must send "I-1\n0\n"
90 * before issuing the other I commands.
91 */
92 static int rmt_version = 0;
93 #define RMTI_VERSION -1
94 #define RMT_VERSION 1
95
96 /* Extended 'i' commands */
97 #define RMTI_CACHE 0
98 #define RMTI_NOCACHE 1
99 #define RMTI_RETEN 2
100 #define RMTI_ERASE 3
101 #define RMTI_EOM 4
102 #define RMTI_NBSF 5
103
104 /* Extended 's' comands */
105 #define MTS_TYPE 'T'
106 #define MTS_DSREG 'D'
107 #define MTS_ERREG 'E'
108 #define MTS_RESID 'R'
109 #define MTS_FILENO 'F'
110 #define MTS_BLKNO 'B'
111 #define MTS_FLAGS 'f'
112 #define MTS_BF 'b'
113
114 char *checkbuf __P((char *, int));
115 void error __P((int));
116 void getstring __P((char *));
117
118 int
119 main(int argc, char *argv[])
120 {
121 OFF_T rval = 0;
122 char c;
123 int n, i, cc, oflags;
124 unsigned long block = 0;
125
126 argc--, argv++;
127 if (argc > 0) {
128 debug = fopen(*argv, "w");
129 if (debug == 0)
130 exit(1);
131 (void)setbuf(debug, (char *)0);
132 }
133 top:
134 errno = 0;
135 rval = 0;
136 if (read(0, &c, 1) != 1)
137 exit(0);
138 switch (c) {
139
140 case 'O':
141 if (tape >= 0)
142 (void) close(tape);
143 getstring(device);
144 getstring(filemode);
145 DEBUG2("rmtd: O %s %s\n", device, filemode);
146 /*
147 * Translate extended GNU syntax into its numeric platform equivalent
148 */
149 oflags = rmtflags_toint(filemode);
150 #ifdef O_TEXT
151 /*
152 * Default to O_BINARY the client may not know that we need it.
153 */
154 if ((oflags & O_TEXT) == 0)
155 oflags |= O_BINARY;
156 #endif
157 DEBUG2("rmtd: O %s %d\n", device, oflags);
158 /*
159 * XXX the rmt protocol does not provide a means to
160 * specify the permission bits; allow rw for everyone,
161 * as modified by the users umask
162 */
163 tape = OPEN(device, oflags, 0666);
164 if (tape < 0)
165 goto ioerror;
166 block = 0;
167 goto respond;
168
169 case 'C':
170 DEBUG1("rmtd: C (%lu blocks)\n", block);
171 getstring(device); /* discard */
172 if (close(tape) < 0)
173 goto ioerror;
174 tape = -1;
175 block = 0;
176 goto respond;
177
178 case 'L':
179 getstring(count);
180 getstring(pos);
181 DEBUG2("rmtd: L %s %s\n", count, pos);
182 rval = LSEEK(tape, (OFF_T)atoll(count), atoi(pos));
183 if (rval < 0)
184 goto ioerror;
185 goto respond;
186
187 case 'W':
188 getstring(count);
189 n = atoi(count);
190 if (n < 1)
191 exit(2);
192 DEBUG2("rmtd: W %s (block = %lu)\n", count, block);
193 record = checkbuf(record, n);
194 for (i = 0; i < n; i += cc) {
195 cc = read(0, &record[i], n - i);
196 if (cc <= 0) {
197 DEBUG("rmtd: premature eof\n");
198 exit(2);
199 }
200 }
201 rval = write(tape, record, n);
202 if (rval < 0)
203 goto ioerror;
204 block += n >> 10;
205 goto respond;
206
207 case 'R':
208 getstring(count);
209 DEBUG2("rmtd: R %s (block %lu)\n", count, block);
210 n = atoi(count);
211 record = checkbuf(record, n);
212 rval = read(tape, record, n);
213 if (rval < 0)
214 goto ioerror;
215 (void)sprintf(resp, "A%lld\n", (long long)rval);
216 (void)write(1, resp, strlen(resp));
217 (void)write(1, record, rval);
218 block += n >> 10;
219 goto top;
220
221 case 'I':
222 getstring(op);
223 getstring(count);
224 DEBUG2("rmtd: I %s %s\n", op, count);
225 if (atoi(op) == RMTI_VERSION) {
226 rval = RMT_VERSION;
227 rmt_version = 1;
228 }
229 else {
230 struct mtop mtop;
231 mtop.mt_op = -1;
232 if (rmt_version) {
233 /* rmt version 1, assume UNIX client */
234 switch (atoi(op)) {
235 #ifdef MTWEOF
236 case 0:
237 mtop.mt_op = MTWEOF;
238 break;
239 #endif
240 #ifdef MTFSF
241 case 1:
242 mtop.mt_op = MTFSF;
243 break;
244 #endif
245 #ifdef MTBSF
246 case 2:
247 mtop.mt_op = MTBSF;
248 break;
249 #endif
250 #ifdef MTFSR
251 case 3:
252 mtop.mt_op = MTFSR;
253 break;
254 #endif
255 #ifdef MTBSR
256 case 4:
257 mtop.mt_op = MTBSR;
258 break;
259 #endif
260 #ifdef MTREW
261 case 5:
262 mtop.mt_op = MTREW;
263 break;
264 #endif
265 #ifdef MTOFFL
266 case 6:
267 mtop.mt_op = MTOFFL;
268 break;
269 #endif
270 #ifdef MTNOP
271 case 7:
272 mtop.mt_op = MTNOP;
273 break;
274 #endif
275 }
276 if (mtop.mt_op == -1) {
277 errno = EINVAL;
278 goto ioerror;
279 }
280 }
281 else {
282 /* rmt version 0, assume linux client */
283 mtop.mt_op = atoi(op);
284 }
285 mtop.mt_count = atoi(count);
286 if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0)
287 goto ioerror;
288 rval = mtop.mt_count;
289 }
290 goto respond;
291
292 case 'i':
293 { struct mtop mtop;
294
295 getstring (op);
296 getstring (count);
297 DEBUG2 ("rmtd: i %s %s\n", op, count);
298 switch (atoi(op)) {
299 #ifdef MTCACHE
300 case RMTI_CACHE:
301 mtop.mt_op = MTCACHE;
302 break;
303 #endif
304 #ifdef MTNOCACHE
305 case RMTI_NOCACHE:
306 mtop.mt_op = MTNOCACHE;
307 break;
308 #endif
309 #ifdef MTRETEN
310 case RMTI_RETEN:
311 mtop.mt_op = MTRETEN;
312 break;
313 #endif
314 #ifdef MTERASE
315 case RMTI_ERASE:
316 mtop.mt_op = MTERASE;
317 break;
318 #endif
319 #ifdef MTEOM
320 case RMTI_EOM:
321 mtop.mt_op = MTEOM;
322 break;
323 #endif
324 #ifdef MTNBSF
325 case RMTI_NBSF:
326 mtop.mt_op = MTNBSF;
327 break;
328 #endif
329 default:
330 errno = EINVAL;
331 goto ioerror;
332 }
333 mtop.mt_count = atoi (count);
334 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
335 goto ioerror;
336
337 rval = mtop.mt_count;
338
339 goto respond;
340 }
341
342 case 'S': /* status */
343 DEBUG("rmtd: S\n");
344 { struct mtget mtget;
345 if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0)
346 goto ioerror;
347 rval = sizeof (mtget);
348 (void)sprintf(resp, "A%lld\n", (long long)rval);
349 (void)write(1, resp, strlen(resp));
350 (void)write(1, (char *)&mtget, sizeof (mtget));
351 goto top;
352 }
353
354 case 's':
355 { char s;
356 struct mtget mtget;
357
358 if (read (0, &s, 1) != 1)
359 goto top;
360
361 if (ioctl (tape, MTIOCGET, (char *) &mtget) < 0)
362 goto ioerror;
363
364 switch (s) {
365 case MTS_TYPE:
366 rval = mtget.mt_type;
367 break;
368 case MTS_DSREG:
369 rval = mtget.mt_dsreg;
370 break;
371 case MTS_ERREG:
372 rval = mtget.mt_erreg;
373 break;
374 case MTS_RESID:
375 rval = mtget.mt_resid;
376 break;
377 case MTS_FILENO:
378 rval = mtget.mt_fileno;
379 break;
380 case MTS_BLKNO:
381 rval = mtget.mt_blkno;
382 break;
383 case MTS_FLAGS:
384 rval = mtget.mt_gstat;
385 break;
386 case MTS_BF:
387 rval = 0;
388 break;
389 default:
390 errno = EINVAL;
391 goto ioerror;
392 }
393
394 goto respond;
395 }
396
397 case 'V': /* version */
398 getstring(op);
399 DEBUG1("rmtd: V %s\n", op);
400 rval = 2;
401 goto respond;
402
403 default:
404 DEBUG1("rmtd: garbage command %c\n", c);
405 exit(3);
406 }
407 respond:
408 DEBUG1("rmtd: A %lld\n", (long long)rval);
409 (void)sprintf(resp, "A%lld\n", (long long)rval);
410 (void)write(1, resp, strlen(resp));
411 goto top;
412 ioerror:
413 error(errno);
414 goto top;
415 }
416
417 void getstring(char *bp)
418 {
419 int i;
420 char *cp = bp;
421
422 for (i = 0; i < SSIZE - 1; i++) {
423 if (read(0, cp+i, 1) != 1)
424 exit(0);
425 if (cp[i] == '\n')
426 break;
427 }
428 cp[i] = '\0';
429 }
430
431 char *
432 checkbuf(char *record, int size)
433 {
434
435 if (size <= maxrecsize)
436 return (record);
437 if (record != 0)
438 free(record);
439 record = malloc(size);
440 if (record == 0) {
441 DEBUG("rmtd: cannot allocate buffer space\n");
442 exit(4);
443 }
444 maxrecsize = size;
445 while (size > 1024 &&
446 setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0)
447 size -= 1024;
448 return (record);
449 }
450
451 void
452 error(int num)
453 {
454
455 DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
456 (void)snprintf(resp, sizeof(resp), "E%d\n%s\n", num, strerror(num));
457 (void)write(1, resp, strlen(resp));
458 }