]> git.wh0rd.org - dump.git/blob - rmt/rmt.c
Fixed largefile seeks in rmt
[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.23 2003/02/12 11:02:31 stelian Exp $";
45 #endif /* not linux */
46
47 /*
48 * rmt
49 */
50 #include <config.h>
51 #include <compatlfs.h>
52 #include <rmtflags.h>
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <sys/mtio.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #ifndef __linux__
59 #include <sgtty.h>
60 #endif
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65
66 static int tape = -1;
67
68 static char *record;
69 static int maxrecsize = -1;
70
71 #define SSIZE 64
72 static char device[SSIZE];
73 static char count[SSIZE], filemode[SSIZE], pos[SSIZE], op[SSIZE];
74
75 static char resp[BUFSIZ];
76
77 static FILE *debug;
78 #define DEBUG(f) if (debug) fprintf(debug, f)
79 #define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
80 #define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
81
82 /*
83 * Support for Sun's extended RMT protocol
84 * code originally written by Jörg Schilling <schilling@fokus.gmd.de>
85 * and relicensed by his permission from GPL to BSD for use in dump.
86 *
87 * rmt_version is 0 for regular clients (Linux included)
88 * rmt_version is 1 for extended clients (Sun especially). In this case
89 * we support some extended commands (see below) and we remap
90 * the ioctl commands to the UNIX "standard", as per:
91 * ftp://ftp.fokus.gmd.de/pub/unix/star/README.mtio
92 *
93 * In order to use rmt version 1, a client must send "I-1\n0\n"
94 * before issuing the other I commands.
95 */
96 static int rmt_version = 0;
97 #define RMTI_VERSION -1
98 #define RMT_VERSION 1
99
100 /* Extended 'i' commands */
101 #define RMTI_CACHE 0
102 #define RMTI_NOCACHE 1
103 #define RMTI_RETEN 2
104 #define RMTI_ERASE 3
105 #define RMTI_EOM 4
106 #define RMTI_NBSF 5
107
108 /* Extended 's' comands */
109 #define MTS_TYPE 'T'
110 #define MTS_DSREG 'D'
111 #define MTS_ERREG 'E'
112 #define MTS_RESID 'R'
113 #define MTS_FILENO 'F'
114 #define MTS_BLKNO 'B'
115 #define MTS_FLAGS 'f'
116 #define MTS_BF 'b'
117
118 char *checkbuf __P((char *, int));
119 void error __P((int));
120 void getstring __P((char *));
121
122 int
123 main(int argc, char *argv[])
124 {
125 OFF_T 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 = rmtflags_toint(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)atoll(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%lld\n", (long long)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%lld\n", (long long)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 %lld\n", (long long)rval);
411 (void)sprintf(resp, "A%lld\n", (long long)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 }