]> git.wh0rd.org - dump.git/blob - rmt/rmt.c
Make dump/restore use O_CREAT|O_TRUNC both locally and remotely.
[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. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41
42 #ifndef lint
43 static const char rcsid[] =
44 "$Id: rmt.c,v 1.20 2002/05/21 15:48:46 stelian Exp $";
45 #endif /* not linux */
46
47 /*
48 * rmt
49 */
50 #include <config.h>
51 #include <compatlfs.h>
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <sys/mtio.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #ifndef __linux__
58 #include <sgtty.h>
59 #endif
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64
65 static int tape = -1;
66
67 static char *record;
68 static int maxrecsize = -1;
69
70 #define SSIZE 64
71 static char device[SSIZE];
72 static char count[SSIZE], filemode[SSIZE], pos[SSIZE], op[SSIZE];
73
74 static char resp[BUFSIZ];
75
76 static FILE *debug;
77 #define DEBUG(f) if (debug) fprintf(debug, f)
78 #define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
79 #define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
80
81 /*
82 * Support for Sun's extended RMT protocol
83 * code originally written by Jörg Schilling <schilling@fokus.gmd.de>
84 * and relicensed by his permission from GPL to BSD for use in dump.
85 *
86 * rmt_version is 0 for regular clients (Linux included)
87 * rmt_version is 1 for extended clients (Sun especially). In this case
88 * we support some extended commands (see below) and we remap
89 * the ioctl commands to the UNIX "standard", as per:
90 * ftp://ftp.fokus.gmd.de/pub/unix/star/README.mtio
91 *
92 * In order to use rmt version 1, a client must send "I-1\n0\n"
93 * before issuing the other I commands.
94 */
95 static int rmt_version = 0;
96 #define RMTI_VERSION -1
97 #define RMT_VERSION 1
98
99 /* Extended 'i' commands */
100 #define RMTI_CACHE 0
101 #define RMTI_NOCACHE 1
102 #define RMTI_RETEN 2
103 #define RMTI_ERASE 3
104 #define RMTI_EOM 4
105 #define RMTI_NBSF 5
106
107 /* Extended 's' comands */
108 #define MTS_TYPE 'T'
109 #define MTS_DSREG 'D'
110 #define MTS_ERREG 'E'
111 #define MTS_RESID 'R'
112 #define MTS_FILENO 'F'
113 #define MTS_BLKNO 'B'
114 #define MTS_FLAGS 'f'
115 #define MTS_BF 'b'
116
117 char *checkbuf __P((char *, int));
118 void error __P((int));
119 void getstring __P((char *));
120 int getopenflags __P((char *));
121
122 int
123 main(int argc, char *argv[])
124 {
125 int rval = 0;
126 char c;
127 int n, i, cc, oflags;
128 unsigned long block = 0;
129
130 argc--, argv++;
131 if (argc > 0) {
132 debug = fopen(*argv, "w");
133 if (debug == 0)
134 exit(1);
135 (void)setbuf(debug, (char *)0);
136 }
137 top:
138 errno = 0;
139 rval = 0;
140 if (read(0, &c, 1) != 1)
141 exit(0);
142 switch (c) {
143
144 case 'O':
145 if (tape >= 0)
146 (void) close(tape);
147 getstring(device);
148 getstring(filemode);
149 DEBUG2("rmtd: O %s %s\n", device, filemode);
150 /*
151 * Translate extended GNU syntax into its numeric platform equivalent
152 */
153 oflags = getopenflags(filemode);
154 #ifdef O_TEXT
155 /*
156 * Default to O_BINARY the client may not know that we need it.
157 */
158 if ((oflags & O_TEXT) == 0)
159 oflags |= O_BINARY;
160 #endif
161 DEBUG2("rmtd: O %s %d\n", device, oflags);
162 /*
163 * XXX the rmt protocol does not provide a means to
164 * specify the permission bits; allow rw for everyone,
165 * as modified by the users umask
166 */
167 tape = OPEN(device, oflags, 0666);
168 if (tape < 0)
169 goto ioerror;
170 block = 0;
171 goto respond;
172
173 case 'C':
174 DEBUG1("rmtd: C (%lu blocks)\n", block);
175 getstring(device); /* discard */
176 if (close(tape) < 0)
177 goto ioerror;
178 tape = -1;
179 block = 0;
180 goto respond;
181
182 case 'L':
183 getstring(count);
184 getstring(pos);
185 DEBUG2("rmtd: L %s %s\n", count, pos);
186 rval = LSEEK(tape, (off_t)atol(count), atoi(pos));
187 if (rval < 0)
188 goto ioerror;
189 goto respond;
190
191 case 'W':
192 getstring(count);
193 n = atoi(count);
194 DEBUG2("rmtd: W %s (block = %lu)\n", count, block);
195 record = checkbuf(record, n);
196 for (i = 0; i < n; i += cc) {
197 cc = read(0, &record[i], n - i);
198 if (cc <= 0) {
199 DEBUG("rmtd: premature eof\n");
200 exit(2);
201 }
202 }
203 rval = write(tape, record, n);
204 if (rval < 0)
205 goto ioerror;
206 block += n >> 10;
207 goto respond;
208
209 case 'R':
210 getstring(count);
211 DEBUG2("rmtd: R %s (block %lu)\n", count, block);
212 n = atoi(count);
213 record = checkbuf(record, n);
214 rval = read(tape, record, n);
215 if (rval < 0)
216 goto ioerror;
217 (void)sprintf(resp, "A%d\n", rval);
218 (void)write(1, resp, strlen(resp));
219 (void)write(1, record, rval);
220 block += n >> 10;
221 goto top;
222
223 case 'I':
224 getstring(op);
225 getstring(count);
226 DEBUG2("rmtd: I %s %s\n", op, count);
227 if (atoi(op) == RMTI_VERSION) {
228 rval = RMT_VERSION;
229 rmt_version = 1;
230 }
231 else {
232 struct mtop mtop;
233 mtop.mt_op = -1;
234 if (rmt_version) {
235 /* rmt version 1, assume UNIX client */
236 switch (atoi(op)) {
237 #ifdef MTWEOF
238 case 0:
239 mtop.mt_op = MTWEOF;
240 break;
241 #endif
242 #ifdef MTFSF
243 case 1:
244 mtop.mt_op = MTFSF;
245 break;
246 #endif
247 #ifdef MTBSF
248 case 2:
249 mtop.mt_op = MTBSF;
250 break;
251 #endif
252 #ifdef MTFSR
253 case 3:
254 mtop.mt_op = MTFSR;
255 break;
256 #endif
257 #ifdef MTBSR
258 case 4:
259 mtop.mt_op = MTBSR;
260 break;
261 #endif
262 #ifdef MTREW
263 case 5:
264 mtop.mt_op = MTREW;
265 break;
266 #endif
267 #ifdef MTOFFL
268 case 6:
269 mtop.mt_op = MTOFFL;
270 break;
271 #endif
272 #ifdef MTNOP
273 case 7:
274 mtop.mt_op = MTNOP;
275 break;
276 #endif
277 }
278 if (mtop.mt_op == -1) {
279 errno = EINVAL;
280 goto ioerror;
281 }
282 }
283 else {
284 /* rmt version 0, assume linux client */
285 mtop.mt_op = atoi(op);
286 }
287 mtop.mt_count = atoi(count);
288 if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0)
289 goto ioerror;
290 rval = mtop.mt_count;
291 }
292 goto respond;
293
294 case 'i':
295 { struct mtop mtop;
296
297 getstring (op);
298 getstring (count);
299 DEBUG2 ("rmtd: i %s %s\n", op, count);
300 switch (atoi(op)) {
301 #ifdef MTCACHE
302 case RMTI_CACHE:
303 mtop.mt_op = MTCACHE;
304 break;
305 #endif
306 #ifdef MTNOCACHE
307 case RMTI_NOCACHE:
308 mtop.mt_op = MTNOCACHE;
309 break;
310 #endif
311 #ifdef MTRETEN
312 case RMTI_RETEN:
313 mtop.mt_op = MTRETEN;
314 break;
315 #endif
316 #ifdef MTERASE
317 case RMTI_ERASE:
318 mtop.mt_op = MTERASE;
319 break;
320 #endif
321 #ifdef MTEOM
322 case RMTI_EOM:
323 mtop.mt_op = MTEOM;
324 break;
325 #endif
326 #ifdef MTNBSF
327 case RMTI_NBSF:
328 mtop.mt_op = MTNBSF;
329 break;
330 #endif
331 default:
332 errno = EINVAL;
333 goto ioerror;
334 }
335 mtop.mt_count = atoi (count);
336 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
337 goto ioerror;
338
339 rval = mtop.mt_count;
340
341 goto respond;
342 }
343
344 case 'S': /* status */
345 DEBUG("rmtd: S\n");
346 { struct mtget mtget;
347 if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0)
348 goto ioerror;
349 rval = sizeof (mtget);
350 (void)sprintf(resp, "A%d\n", rval);
351 (void)write(1, resp, strlen(resp));
352 (void)write(1, (char *)&mtget, sizeof (mtget));
353 goto top;
354 }
355
356 case 's':
357 { char s;
358 struct mtget mtget;
359
360 if (read (0, &s, 1) != 1)
361 goto top;
362
363 if (ioctl (tape, MTIOCGET, (char *) &mtget) < 0)
364 goto ioerror;
365
366 switch (s) {
367 case MTS_TYPE:
368 rval = mtget.mt_type;
369 break;
370 case MTS_DSREG:
371 rval = mtget.mt_dsreg;
372 break;
373 case MTS_ERREG:
374 rval = mtget.mt_erreg;
375 break;
376 case MTS_RESID:
377 rval = mtget.mt_resid;
378 break;
379 case MTS_FILENO:
380 rval = mtget.mt_fileno;
381 break;
382 case MTS_BLKNO:
383 rval = mtget.mt_blkno;
384 break;
385 case MTS_FLAGS:
386 rval = mtget.mt_gstat;
387 break;
388 case MTS_BF:
389 rval = 0;
390 break;
391 default:
392 errno = EINVAL;
393 goto ioerror;
394 }
395
396 goto respond;
397 }
398
399 case 'V': /* version */
400 getstring(op);
401 DEBUG1("rmtd: V %s\n", op);
402 rval = 2;
403 goto respond;
404
405 default:
406 DEBUG1("rmtd: garbage command %c\n", c);
407 exit(3);
408 }
409 respond:
410 DEBUG1("rmtd: A %d\n", rval);
411 (void)sprintf(resp, "A%d\n", rval);
412 (void)write(1, resp, strlen(resp));
413 goto top;
414 ioerror:
415 error(errno);
416 goto top;
417 }
418
419 void getstring(char *bp)
420 {
421 int i;
422 char *cp = bp;
423
424 for (i = 0; i < SSIZE; i++) {
425 if (read(0, cp+i, 1) != 1)
426 exit(0);
427 if (cp[i] == '\n')
428 break;
429 }
430 cp[i] = '\0';
431 }
432
433 char *
434 checkbuf(char *record, int size)
435 {
436
437 if (size <= maxrecsize)
438 return (record);
439 if (record != 0)
440 free(record);
441 record = malloc(size);
442 if (record == 0) {
443 DEBUG("rmtd: cannot allocate buffer space\n");
444 exit(4);
445 }
446 maxrecsize = size;
447 while (size > 1024 &&
448 setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0)
449 size -= 1024;
450 return (record);
451 }
452
453 void
454 error(int num)
455 {
456
457 DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
458 (void)snprintf(resp, sizeof(resp), "E%d\n%s\n", num, strerror(num));
459 (void)write(1, resp, strlen(resp));
460 }
461
462 struct openflags {
463 char *name;
464 int value;
465 } openflags[] = {
466 { "O_RDONLY", O_RDONLY },
467 { "O_WRONLY", O_WRONLY },
468 { "O_RDWR", O_RDWR },
469 #ifdef O_CREAT
470 { "O_CREAT", O_CREAT },
471 #endif
472 #ifdef O_EXCL
473 { "O_EXCL", O_EXCL },
474 #endif
475 #ifdef O_NOCTTY
476 { "O_NOCTTY", O_NOCTTY },
477 #endif
478 #ifdef O_TRUNC
479 { "O_TRUNC", O_TRUNC },
480 #endif
481 #ifdef O_APPEND
482 { "O_APPEND", O_APPEND },
483 #endif
484 #ifdef O_NONBLOCK
485 { "O_NONBLOCK", O_NONBLOCK },
486 #endif
487 #ifdef O_NDELAY
488 { "O_NDELAY", O_NDELAY },
489 #endif
490 #ifdef O_SYNC
491 { "O_SYNC", O_SYNC },
492 #endif
493 #ifdef O_FSYNC
494 { "O_FSYNC", O_FSYNC },
495 #endif
496 #ifdef O_ASYNC
497 { "O_ASYNC", O_ASYNC },
498 #endif
499 #ifdef O_TEXT
500 { "O_TEXT", O_TEXT },
501 #endif
502 #ifdef O_DSYNC
503 { "O_DSYNC", O_DSYNC },
504 #endif
505 #ifdef O_RSYNC
506 { "O_RSYNC", O_RSYNC },
507 #endif
508 #ifdef O_PRIV
509 { "O_PRIV", O_PRIV },
510 #endif
511 #ifdef O_LARGEFILE
512 { "O_LARGEFILE",O_LARGEFILE },
513 #endif
514 { NULL, 0 }
515 };
516
517 /* Parts of this stolen again from Jörg Schilling's star package... */
518 int
519 getopenflags(char *filemode)
520 {
521 char *p = filemode;
522 struct openflags *op;
523 int result = 0;
524
525 do {
526 /* skip space */
527 while (*p != '\0' && *p == ' ')
528 p++;
529 /* get O_XXXX constant */
530 if (p[0] != 'O' || p[1] != '_') {
531 /* numeric syntax detected */
532 result = atoi(filemode);
533 result &= O_RDONLY | O_WRONLY | O_RDWR;
534 return result;
535 }
536
537 /* translate O_XXXX constant */
538 for (op = openflags; op->name; op++) {
539 int slen = strlen(op->name);
540 if ((strncmp(op->name, p, slen) == 0) &&
541 (p[slen] == '|' || p[slen] == ' ' ||
542 p[slen] == '\0')) {
543 result |= op->value;
544 break;
545 }
546 }
547
548 /* goto next constant */
549 p = strchr(p, '|');
550 } while (p && *p++ == '|');
551
552 return result;
553 }