Refuse to label media that is too large to handle a 32 bit disklabel
[dragonfly/vkernel-mp.git] / contrib / cpio / rtapelib.c
blob6550a6cd5bd417c20cdb245d6d21e25d8625dbb7
1 /* Functions for communicating with a remote tape drive.
2 Copyright (C) 1988, 1992 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* The man page rmt(8) for /etc/rmt documents the remote mag tape
19 protocol which rdump and rrestore use. Unfortunately, the man
20 page is *WRONG*. The author of the routines I'm including originally
21 wrote his code just based on the man page, and it didn't work, so he
22 went to the rdump source to figure out why. The only thing he had to
23 change was to check for the 'F' return code in addition to the 'E',
24 and to separate the various arguments with \n instead of a space. I
25 personally don't think that this is much of a problem, but I wanted to
26 point it out. -- Arnold Robbins
28 Originally written by Jeff Lee, modified some by Arnold Robbins.
29 Redone as a library that can replace open, read, write, etc., by
30 Fred Fish, with some additional work by Arnold Robbins.
31 Modified to make all rmtXXX calls into macros for speed by Jay Fenlason.
32 Use -DHAVE_NETDB_H for rexec code, courtesy of Dan Kegel, srs!dan. */
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <signal.h>
38 #ifdef HAVE_SYS_MTIO_H
39 #include <sys/ioctl.h>
40 #include <sys/mtio.h>
41 #endif
43 #ifdef HAVE_NETDB_H
44 #include <netdb.h>
45 #endif
47 #include <errno.h>
48 #include <setjmp.h>
49 #include <sys/stat.h>
51 #ifndef errno
52 extern int errno;
53 #endif
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
58 #ifdef STDC_HEADERS
59 #include <string.h>
60 #include <stdlib.h>
61 #endif
63 /* Maximum size of a fully qualified host name. */
64 #define MAXHOSTLEN 257
66 /* Size of buffers for reading and writing commands to rmt.
67 (An arbitrary limit.) */
68 #define CMDBUFSIZE 64
70 #ifndef RETSIGTYPE
71 #define RETSIGTYPE void
72 #endif
74 /* Maximum number of simultaneous remote tape connections.
75 (Another arbitrary limit.) */
76 #define MAXUNIT 4
78 /* Return the parent's read side of remote tape connection FILDES. */
79 #define READ(fildes) (from_rmt[fildes][0])
81 /* Return the parent's write side of remote tape connection FILDES. */
82 #define WRITE(fildes) (to_rmt[fildes][1])
84 /* The pipes for receiving data from remote tape drives. */
85 static int from_rmt[MAXUNIT][2] =
86 {-1, -1, -1, -1, -1, -1, -1, -1};
88 /* The pipes for sending data to remote tape drives. */
89 static int to_rmt[MAXUNIT][2] =
90 {-1, -1, -1, -1, -1, -1, -1, -1};
92 /* Temporary variable used by macros in rmt.h. */
93 char *__rmt_path;
95 /* Close remote tape connection FILDES. */
97 static void
98 _rmt_shutdown (fildes)
99 int fildes;
101 close (READ (fildes));
102 close (WRITE (fildes));
103 READ (fildes) = -1;
104 WRITE (fildes) = -1;
107 /* Attempt to perform the remote tape command specified in BUF
108 on remote tape connection FILDES.
109 Return 0 if successful, -1 on error. */
111 static int
112 command (fildes, buf)
113 int fildes;
114 char *buf;
116 register int buflen;
117 RETSIGTYPE (*pipe_handler) ();
119 /* Save the current pipe handler and try to make the request. */
121 pipe_handler = signal (SIGPIPE, SIG_IGN);
122 buflen = strlen (buf);
123 if (write (WRITE (fildes), buf, buflen) == buflen)
125 signal (SIGPIPE, pipe_handler);
126 return 0;
129 /* Something went wrong. Close down and go home. */
131 signal (SIGPIPE, pipe_handler);
132 _rmt_shutdown (fildes);
133 errno = EIO;
134 return -1;
137 /* Read and return the status from remote tape connection FILDES.
138 If an error occurred, return -1 and set errno. */
140 static int
141 status (fildes)
142 int fildes;
144 int i;
145 char c, *cp;
146 char buffer[CMDBUFSIZE];
148 /* Read the reply command line. */
150 for (i = 0, cp = buffer; i < CMDBUFSIZE; i++, cp++)
152 if (read (READ (fildes), cp, 1) != 1)
154 _rmt_shutdown (fildes);
155 errno = EIO;
156 return -1;
158 if (*cp == '\n')
160 *cp = '\0';
161 break;
165 if (i == CMDBUFSIZE)
167 _rmt_shutdown (fildes);
168 errno = EIO;
169 return -1;
172 /* Check the return status. */
174 for (cp = buffer; *cp; cp++)
175 if (*cp != ' ')
176 break;
178 if (*cp == 'E' || *cp == 'F')
180 errno = atoi (cp + 1);
181 /* Skip the error message line. */
182 while (read (READ (fildes), &c, 1) == 1)
183 if (c == '\n')
184 break;
186 if (*cp == 'F')
187 _rmt_shutdown (fildes);
189 return -1;
192 /* Check for mis-synced pipes. */
194 if (*cp != 'A')
196 _rmt_shutdown (fildes);
197 errno = EIO;
198 return -1;
201 /* Got an `A' (success) response. */
202 return atoi (cp + 1);
205 #ifdef HAVE_NETDB_H
206 /* Execute /etc/rmt as user USER on remote system HOST using rexec.
207 Return a file descriptor of a bidirectional socket for stdin and stdout.
208 If USER is NULL, or an empty string, use the current username.
210 By default, this code is not used, since it requires that
211 the user have a .netrc file in his/her home directory, or that the
212 application designer be willing to have rexec prompt for login and
213 password info. This may be unacceptable, and .rhosts files for use
214 with rsh are much more common on BSD systems. */
216 static int
217 _rmt_rexec (host, user)
218 char *host;
219 char *user;
221 struct servent *rexecserv;
222 int save_stdin = dup (fileno (stdin));
223 int save_stdout = dup (fileno (stdout));
224 int tape_fd; /* Return value. */
226 /* When using cpio -o < filename, stdin is no longer the tty.
227 But the rexec subroutine reads the login and the passwd on stdin,
228 to allow remote execution of the command.
229 So, reopen stdin and stdout on /dev/tty before the rexec and
230 give them back their original value after. */
231 if (freopen ("/dev/tty", "r", stdin) == NULL)
232 freopen ("/dev/null", "r", stdin);
233 if (freopen ("/dev/tty", "w", stdout) == NULL)
234 freopen ("/dev/null", "w", stdout);
236 rexecserv = getservbyname ("exec", "tcp");
237 if (NULL == rexecserv)
239 fprintf (stderr, "exec/tcp: service not available");
240 exit (1);
242 if (user != NULL && *user == '\0')
243 user = NULL;
244 tape_fd = rexec (&host, rexecserv->s_port, user, NULL,
245 "/etc/rmt", (int *) NULL);
246 fclose (stdin);
247 fdopen (save_stdin, "r");
248 fclose (stdout);
249 fdopen (save_stdout, "w");
251 return tape_fd;
254 #endif /* HAVE_NETDB_H */
256 /* Open a magtape device on the system specified in PATH, as the given user.
257 PATH has the form `[user@]system:/dev/????'.
258 If COMPAT is defined, it can also have the form `system[.user]:/dev/????'.
260 OFLAG is O_RDONLY, O_WRONLY, etc.
261 MODE is ignored; 0666 is always used.
263 If successful, return the remote tape pipe number plus BIAS.
264 On error, return -1. */
267 __rmt_open (path, oflag, mode, bias)
268 char *path;
269 int oflag;
270 int mode;
271 int bias;
273 int i, rc;
274 char buffer[CMDBUFSIZE]; /* Command buffer. */
275 char system[MAXHOSTLEN]; /* The remote host name. */
276 char device[CMDBUFSIZE]; /* The remote device name. */
277 char login[CMDBUFSIZE]; /* The remote user name. */
278 char *sys, *dev, *user; /* For copying into the above buffers. */
280 sys = system;
281 dev = device;
282 user = login;
284 /* Find an unused pair of file descriptors. */
286 for (i = 0; i < MAXUNIT; i++)
287 if (READ (i) == -1 && WRITE (i) == -1)
288 break;
290 if (i == MAXUNIT)
292 errno = EMFILE;
293 return -1;
296 /* Pull apart the system and device, and optional user.
297 Don't munge the original string. */
299 while (*path != '@'
300 #ifdef COMPAT
301 && *path != '.'
302 #endif
303 && *path != ':')
305 *sys++ = *path++;
307 *sys = '\0';
308 path++;
310 if (*(path - 1) == '@')
312 /* Saw user part of user@host. Start over. */
313 strcpy (user, system);
314 sys = system;
315 while (*path != ':')
317 *sys++ = *path++;
319 *sys = '\0';
320 path++;
322 #ifdef COMPAT
323 else if (*(path - 1) == '.')
325 while (*path != ':')
327 *user++ = *path++;
329 *user = '\0';
330 path++;
332 #endif
333 else
334 *user = '\0';
336 while (*path)
338 *dev++ = *path++;
340 *dev = '\0';
342 #ifdef HAVE_NETDB_H
343 /* Execute the remote command using rexec. */
344 READ (i) = WRITE (i) = _rmt_rexec (system, login);
345 if (READ (i) < 0)
346 return -1;
347 #else /* !HAVE_NETDB_H */
348 /* Set up the pipes for the `rsh' command, and fork. */
350 if (pipe (to_rmt[i]) == -1 || pipe (from_rmt[i]) == -1)
351 return -1;
353 rc = fork ();
354 if (rc == -1)
355 return -1;
357 if (rc == 0)
359 /* Child. */
360 close (0);
361 dup (to_rmt[i][0]);
362 close (to_rmt[i][0]);
363 close (to_rmt[i][1]);
365 close (1);
366 dup (from_rmt[i][1]);
367 close (from_rmt[i][0]);
368 close (from_rmt[i][1]);
370 setuid (getuid ());
371 setgid (getgid ());
373 if (*login)
375 execl ("/usr/bin/rsh", "rsh", "-l", login, system,
376 "/etc/rmt", (char *) 0);
378 else
380 execl ("/usr/bin/rsh", "rsh", system,
381 "/etc/rmt", (char *) 0);
384 /* Bad problems if we get here. */
386 perror ("cannot execute remote shell");
387 _exit (1);
390 /* Parent. */
391 close (to_rmt[i][0]);
392 close (from_rmt[i][1]);
393 #endif /* !HAVE_NETDB_H */
395 /* Attempt to open the tape device. */
397 sprintf (buffer, "O%s\n%d\n", device, oflag);
398 if (command (i, buffer) == -1 || status (i) == -1)
399 return -1;
401 return i + bias;
404 /* Close remote tape connection FILDES and shut down.
405 Return 0 if successful, -1 on error. */
408 __rmt_close (fildes)
409 int fildes;
411 int rc;
413 if (command (fildes, "C\n") == -1)
414 return -1;
416 rc = status (fildes);
417 _rmt_shutdown (fildes);
418 return rc;
421 /* Read up to NBYTE bytes into BUF from remote tape connection FILDES.
422 Return the number of bytes read on success, -1 on error. */
425 __rmt_read (fildes, buf, nbyte)
426 int fildes;
427 char *buf;
428 unsigned int nbyte;
430 int rc, i;
431 char buffer[CMDBUFSIZE];
433 sprintf (buffer, "R%d\n", nbyte);
434 if (command (fildes, buffer) == -1 || (rc = status (fildes)) == -1)
435 return -1;
437 for (i = 0; i < rc; i += nbyte, buf += nbyte)
439 nbyte = read (READ (fildes), buf, rc - i);
440 if (nbyte <= 0)
442 _rmt_shutdown (fildes);
443 errno = EIO;
444 return -1;
448 return rc;
451 /* Write NBYTE bytes from BUF to remote tape connection FILDES.
452 Return the number of bytes written on success, -1 on error. */
455 __rmt_write (fildes, buf, nbyte)
456 int fildes;
457 char *buf;
458 unsigned int nbyte;
460 char buffer[CMDBUFSIZE];
461 RETSIGTYPE (*pipe_handler) ();
463 sprintf (buffer, "W%d\n", nbyte);
464 if (command (fildes, buffer) == -1)
465 return -1;
467 pipe_handler = signal (SIGPIPE, SIG_IGN);
468 if (write (WRITE (fildes), buf, nbyte) == nbyte)
470 signal (SIGPIPE, pipe_handler);
471 return status (fildes);
474 /* Write error. */
475 signal (SIGPIPE, pipe_handler);
476 _rmt_shutdown (fildes);
477 errno = EIO;
478 return -1;
481 /* Perform an imitation lseek operation on remote tape connection FILDES.
482 Return the new file offset if successful, -1 if on error. */
484 long
485 __rmt_lseek (fildes, offset, whence)
486 int fildes;
487 long offset;
488 int whence;
490 char buffer[CMDBUFSIZE];
492 sprintf (buffer, "L%ld\n%d\n", offset, whence);
493 if (command (fildes, buffer) == -1)
494 return -1;
496 return status (fildes);
499 /* Perform a raw tape operation on remote tape connection FILDES.
500 Return the results of the ioctl, or -1 on error. */
502 #ifdef MTIOCTOP
504 __rmt_ioctl (fildes, op, arg)
505 int fildes, op;
506 char *arg;
508 char c;
509 int rc, cnt;
510 char buffer[CMDBUFSIZE];
512 switch (op)
514 default:
515 errno = EINVAL;
516 return -1;
518 case MTIOCTOP:
519 /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
520 sprintf (buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
521 ((struct mtop *) arg)->mt_count);
522 if (command (fildes, buffer) == -1)
523 return -1;
524 return status (fildes); /* Return the count. */
526 case MTIOCGET:
527 /* Grab the status and read it directly into the structure.
528 This assumes that the status buffer is not padded
529 and that 2 shorts fit in a long without any word
530 alignment problems; i.e., the whole struct is contiguous.
531 NOTE - this is probably NOT a good assumption. */
533 if (command (fildes, "S") == -1 || (rc = status (fildes)) == -1)
534 return -1;
536 for (; rc > 0; rc -= cnt, arg += cnt)
538 cnt = read (READ (fildes), arg, rc);
539 if (cnt <= 0)
541 _rmt_shutdown (fildes);
542 errno = EIO;
543 return -1;
547 /* Check for byte position. mt_type is a small integer field
548 (normally) so we will check its magnitude. If it is larger than
549 256, we will assume that the bytes are swapped and go through
550 and reverse all the bytes. */
552 if (((struct mtget *) arg)->mt_type < 256)
553 return 0;
555 for (cnt = 0; cnt < rc; cnt += 2)
557 c = arg[cnt];
558 arg[cnt] = arg[cnt + 1];
559 arg[cnt + 1] = c;
562 return 0;
566 #endif