]> git.wh0rd.org - dump.git/blob - rmt/rmt.c
Relicensed dump/restore under the revised BSD license, as per ftp://ftp.cs.berkeley...
[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.24 2003/03/30 15:40:40 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 DEBUG2("rmtd: W %s (block = %lu)\n", count, block);
191 record = checkbuf(record, n);
192 for (i = 0; i < n; i += cc) {
193 cc = read(0, &record[i], n - i);
194 if (cc <= 0) {
195 DEBUG("rmtd: premature eof\n");
196 exit(2);
197 }
198 }
199 rval = write(tape, record, n);
200 if (rval < 0)
201 goto ioerror;
202 block += n >> 10;
203 goto respond;
204
205 case 'R':
206 getstring(count);
207 DEBUG2("rmtd: R %s (block %lu)\n", count, block);
208 n = atoi(count);
209 record = checkbuf(record, n);
210 rval = read(tape, record, n);
211 if (rval < 0)
212 goto ioerror;
213 (void)sprintf(resp, "A%lld\n", (long long)rval);
214 (void)write(1, resp, strlen(resp));
215 (void)write(1, record, rval);
216 block += n >> 10;
217 goto top;
218
219 case 'I':
220 getstring(op);
221 getstring(count);
222 DEBUG2("rmtd: I %s %s\n", op, count);
223 if (atoi(op) == RMTI_VERSION) {
224 rval = RMT_VERSION;
225 rmt_version = 1;
226 }
227 else {
228 struct mtop mtop;
229 mtop.mt_op = -1;
230 if (rmt_version) {
231 /* rmt version 1, assume UNIX client */
232 switch (atoi(op)) {
233 #ifdef MTWEOF
234 case 0:
235 mtop.mt_op = MTWEOF;
236 break;
237 #endif
238 #ifdef MTFSF
239 case 1:
240 mtop.mt_op = MTFSF;
241 break;
242 #endif
243 #ifdef MTBSF
244 case 2:
245 mtop.mt_op = MTBSF;
246 break;
247 #endif
248 #ifdef MTFSR
249 case 3:
250 mtop.mt_op = MTFSR;
251 break;
252 #endif
253 #ifdef MTBSR
254 case 4:
255 mtop.mt_op = MTBSR;
256 break;
257 #endif
258 #ifdef MTREW
259 case 5:
260 mtop.mt_op = MTREW;
261 break;
262 #endif
263 #ifdef MTOFFL
264 case 6:
265 mtop.mt_op = MTOFFL;
266 break;
267 #endif
268 #ifdef MTNOP
269 case 7:
270 mtop.mt_op = MTNOP;
271 break;
272 #endif
273 }
274 if (mtop.mt_op == -1) {
275 errno = EINVAL;
276 goto ioerror;
277 }
278 }
279 else {
280 /* rmt version 0, assume linux client */
281 mtop.mt_op = atoi(op);
282 }
283 mtop.mt_count = atoi(count);
284 if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0)
285 goto ioerror;
286 rval = mtop.mt_count;
287 }
288 goto respond;
289
290 case 'i':
291 { struct mtop mtop;
292
293 getstring (op);
294 getstring (count);
295 DEBUG2 ("rmtd: i %s %s\n", op, count);
296 switch (atoi(op)) {
297 #ifdef MTCACHE
298 case RMTI_CACHE:
299 mtop.mt_op = MTCACHE;
300 break;
301 #endif
302 #ifdef MTNOCACHE
303 case RMTI_NOCACHE:
304 mtop.mt_op = MTNOCACHE;
305 break;
306 #endif
307 #ifdef MTRETEN
308 case RMTI_RETEN:
309 mtop.mt_op = MTRETEN;
310 break;
311 #endif
312 #ifdef MTERASE
313 case RMTI_ERASE:
314 mtop.mt_op = MTERASE;
315 break;
316 #endif
317 #ifdef MTEOM
318 case RMTI_EOM:
319 mtop.mt_op = MTEOM;
320 break;
321 #endif
322 #ifdef MTNBSF
323 case RMTI_NBSF:
324 mtop.mt_op = MTNBSF;
325 break;
326 #endif
327 default:
328 errno = EINVAL;
329 goto ioerror;
330 }
331 mtop.mt_count = atoi (count);
332 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
333 goto ioerror;
334
335 rval = mtop.mt_count;
336
337 goto respond;
338 }
339
340 case 'S': /* status */
341 DEBUG("rmtd: S\n");
342 { struct mtget mtget;
343 if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0)
344 goto ioerror;
345 rval = sizeof (mtget);
346 (void)sprintf(resp, "A%lld\n", (long long)rval);
347 (void)write(1, resp, strlen(resp));
348 (void)write(1, (char *)&mtget, sizeof (mtget));
349 goto top;
350 }
351
352 case 's':
353 { char s;
354 struct mtget mtget;
355
356 if (read (0, &s, 1) != 1)
357 goto top;
358
359 if (ioctl (tape, MTIOCGET, (char *) &mtget) < 0)
360 goto ioerror;
361
362 switch (s) {
363 case MTS_TYPE:
364 rval = mtget.mt_type;
365 break;
366 case MTS_DSREG:
367 rval = mtget.mt_dsreg;
368 break;
369 case MTS_ERREG:
370 rval = mtget.mt_erreg;
371 break;
372 case MTS_RESID:
373 rval = mtget.mt_resid;
374 break;
375 case MTS_FILENO:
376 rval = mtget.mt_fileno;
377 break;
378 case MTS_BLKNO:
379 rval = mtget.mt_blkno;
380 break;
381 case MTS_FLAGS:
382 rval = mtget.mt_gstat;
383 break;
384 case MTS_BF:
385 rval = 0;
386 break;
387 default:
388 errno = EINVAL;
389 goto ioerror;
390 }
391
392 goto respond;
393 }
394
395 case 'V': /* version */
396 getstring(op);
397 DEBUG1("rmtd: V %s\n", op);
398 rval = 2;
399 goto respond;
400
401 default:
402 DEBUG1("rmtd: garbage command %c\n", c);
403 exit(3);
404 }
405 respond:
406 DEBUG1("rmtd: A %lld\n", (long long)rval);
407 (void)sprintf(resp, "A%lld\n", (long long)rval);
408 (void)write(1, resp, strlen(resp));
409 goto top;
410 ioerror:
411 error(errno);
412 goto top;
413 }
414
415 void getstring(char *bp)
416 {
417 int i;
418 char *cp = bp;
419
420 for (i = 0; i < SSIZE; i++) {
421 if (read(0, cp+i, 1) != 1)
422 exit(0);
423 if (cp[i] == '\n')
424 break;
425 }
426 cp[i] = '\0';
427 }
428
429 char *
430 checkbuf(char *record, int size)
431 {
432
433 if (size <= maxrecsize)
434 return (record);
435 if (record != 0)
436 free(record);
437 record = malloc(size);
438 if (record == 0) {
439 DEBUG("rmtd: cannot allocate buffer space\n");
440 exit(4);
441 }
442 maxrecsize = size;
443 while (size > 1024 &&
444 setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0)
445 size -= 1024;
446 return (record);
447 }
448
449 void
450 error(int num)
451 {
452
453 DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
454 (void)snprintf(resp, sizeof(resp), "E%d\n%s\n", num, strerror(num));
455 (void)write(1, resp, strlen(resp));
456 }