]> git.gag.com Git - debian/tar/blob - src/rtapelib.c
version 1.12.64003
[debian/tar] / src / rtapelib.c
1 /* Functions for communicating with a remote tape drive.
2    Copyright (C) 1988, 1992, 1994, 1996 Free Software Foundation, Inc.
3
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.
8
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.
13
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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* The man page rmt(8) for /etc/rmt documents the remote mag tape protocol
19    which rdump and rrestore use.  Unfortunately, the man page is *WRONG*.
20    The author of the routines I'm including originally wrote his code just
21    based on the man page, and it didn't work, so he went to the rdump source
22    to figure out why.  The only thing he had to change was to check for the
23    'F' return code in addition to the 'E', and to separate the various
24    arguments with \n instead of a space.  I personally don't think that this
25    is much of a problem, but I wanted to point it out. -- Arnold Robbins
26
27    Originally written by Jeff Lee, modified some by Arnold Robbins.  Redone
28    as a library that can replace open, read, write, etc., by Fred Fish, with
29    some additional work by Arnold Robbins.  Modified to make all rmt* calls
30    into macros for speed by Jay Fenlason.  Use -DWITH_REXEC for rexec
31    code, courtesy of Dan Kegel.  */
32
33 #include "system.h"
34
35 /* Try hard to get EOPNOTSUPP defined.  486/ISC has it in net/errno.h,
36    3B2/SVR3 has it in sys/inet.h.  Otherwise, like on MSDOS, use EINVAL.  */
37
38 #ifndef EOPNOTSUPP
39 # if HAVE_NET_ERRNO_H
40 #  include <net/errno.h>
41 # endif
42 # if HAVE_SYS_INET_H
43 #  include <sys/inet.h>
44 # endif
45 # ifndef EOPNOTSUPP
46 #  define EOPNOTSUPP EINVAL
47 # endif
48 #endif
49
50 #include <signal.h>
51
52 #if HAVE_NETDB_H
53 # include <netdb.h>
54 #endif
55
56 #include "rmt.h"
57
58 /* FIXME: Just to shut up -Wall.  */
59 int rexec ();
60
61 /* Exit status if exec errors.  */
62 #define EXIT_ON_EXEC_ERROR 128
63
64 /* FIXME: Size of buffers for reading and writing commands to rmt.  */
65 #define COMMAND_BUFFER_SIZE 64
66
67 #ifndef RETSIGTYPE
68 # define RETSIGTYPE void
69 #endif
70
71 /* FIXME: Maximum number of simultaneous remote tape connections.  */
72 #define MAXUNIT 4
73
74 #define PREAD 0                 /* read  file descriptor from pipe() */
75 #define PWRITE 1                /* write file descriptor from pipe() */
76
77 /* Return the parent's read side of remote tape connection Fd.  */
78 #define READ_SIDE(Fd) (from_remote[Fd][PREAD])
79
80 /* Return the parent's write side of remote tape connection Fd.  */
81 #define WRITE_SIDE(Fd) (to_remote[Fd][PWRITE])
82
83 /* The pipes for receiving data from remote tape drives.  */
84 static int from_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
85
86 /* The pipes for sending data to remote tape drives.  */
87 static int to_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
88
89 /* Temporary variable used by macros in rmt.h.  */
90 char *rmt_path__;
91 \f
92
93 /*----------------------------------------------------------------------.
94 | Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE.  |
95 `----------------------------------------------------------------------*/
96
97 static void
98 _rmt_shutdown (int handle, int errno_value)
99 {
100   close (READ_SIDE (handle));
101   close (WRITE_SIDE (handle));
102   READ_SIDE (handle) = -1;
103   WRITE_SIDE (handle) = -1;
104   errno = errno_value;          /* FIXME: errno should be read-only */
105 }
106
107 /*-------------------------------------------------------------------------.
108 | Attempt to perform the remote tape command specified in BUFFER on remote |
109 | tape connection HANDLE.  Return 0 if successful, -1 on error.            |
110 `-------------------------------------------------------------------------*/
111
112 static int
113 do_command (int handle, const char *buffer)
114 {
115   size_t length;
116   RETSIGTYPE (*pipe_handler) ();
117
118   /* Save the current pipe handler and try to make the request.  */
119
120   pipe_handler = signal (SIGPIPE, SIG_IGN);
121   length = strlen (buffer);
122   if (write (WRITE_SIDE (handle), buffer, length) == length)
123     {
124       signal (SIGPIPE, pipe_handler);
125       return 0;
126     }
127
128   /* Something went wrong.  Close down and go home.  */
129
130   signal (SIGPIPE, pipe_handler);
131   _rmt_shutdown (handle, EIO);
132   return -1;
133 }
134
135 static char *
136 get_status_string (int handle, char *command_buffer)
137 {
138   char *cursor;
139   int counter;
140
141   /* Read the reply command line.  */
142
143   for (counter = 0, cursor = command_buffer;
144        counter < COMMAND_BUFFER_SIZE;
145        counter++, cursor++)
146     {
147       if (read (READ_SIDE (handle), cursor, 1) != 1)
148         {
149           _rmt_shutdown (handle, EIO);
150           return 0;
151         }
152       if (*cursor == '\n')
153         {
154           *cursor = '\0';
155           break;
156         }
157     }
158
159   if (counter == COMMAND_BUFFER_SIZE)
160     {
161       _rmt_shutdown (handle, EIO);
162       return 0;
163     }
164
165   /* Check the return status.  */
166
167   for (cursor = command_buffer; *cursor; cursor++)
168     if (*cursor != ' ')
169       break;
170
171   if (*cursor == 'E' || *cursor == 'F')
172     {
173       errno = atoi (cursor + 1); /* FIXME: errno should be read-only */
174
175       /* Skip the error message line.  */
176
177       /* FIXME: there is better to do than merely ignoring error messages
178          coming from the remote end.  Translate them, too...  */
179
180       {
181         char character;
182
183         while (read (READ_SIDE (handle), &character, 1) == 1)
184           if (character == '\n')
185             break;
186       }
187
188       if (*cursor == 'F')
189         _rmt_shutdown (handle, errno);
190
191       return 0;
192     }
193
194   /* Check for mis-synced pipes.  */
195
196   if (*cursor != 'A')
197     {
198       _rmt_shutdown (handle, EIO);
199       return 0;
200     }
201
202   /* Got an `A' (success) response.  */
203
204   return cursor + 1;
205 }
206
207 /*----------------------------------------------------------------------.
208 | Read and return the status from remote tape connection HANDLE.  If an |
209 | error occurred, return -1 and set errno.                              |
210 `----------------------------------------------------------------------*/
211
212 static long
213 get_status (int handle)
214 {
215   char command_buffer[COMMAND_BUFFER_SIZE];
216   const char *status = get_status_string (handle, command_buffer);
217   return status ? atol (status) : -1L;
218 }
219
220 static off_t
221 get_status_off (int handle)
222 {
223   char command_buffer[COMMAND_BUFFER_SIZE];
224   const char *status = get_status_string (handle, command_buffer);
225
226   if (! status)
227     return -1;
228   else
229     {
230       /* Parse status, taking care to check for overflow.
231          We can't use standard functions,
232          since off_t might be longer than long.  */
233
234       off_t count = 0;
235       int negative;
236
237       for (;  *status == ' ' || *status == '\t';  status++)
238         continue;
239       
240       negative = *status == '-';
241       status += negative || *status == '+';
242       
243       for (;;)
244         {
245           int digit = *status++ - '0';
246           if (9 < (unsigned) digit)
247             break;
248           else
249             {
250               off_t c10 = 10 * count;
251               off_t nc = negative ? c10 - digit : c10 + digit;
252               if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
253                 return -1;
254               count = nc;
255             }
256         }
257
258       return count;
259     }
260 }
261
262 #if WITH_REXEC
263
264 /*-------------------------------------------------------------------------.
265 | Execute /etc/rmt as user USER on remote system HOST using rexec.  Return |
266 | a file descriptor of a bidirectional socket for stdin and stdout.  If    |
267 | USER is NULL, use the current username.                                  |
268 |                                                                          |
269 | By default, this code is not used, since it requires that the user have  |
270 | a .netrc file in his/her home directory, or that the application         |
271 | designer be willing to have rexec prompt for login and password info.    |
272 | This may be unacceptable, and .rhosts files for use with rsh are much    |
273 | more common on BSD systems.                                              |
274 `-------------------------------------------------------------------------*/
275
276 static int
277 _rmt_rexec (char *host, char *user)
278 {
279   int saved_stdin = dup (fileno (stdin));
280   int saved_stdout = dup (fileno (stdout));
281   struct servent *rexecserv;
282   int result;
283
284   /* When using cpio -o < filename, stdin is no longer the tty.  But the
285      rexec subroutine reads the login and the passwd on stdin, to allow
286      remote execution of the command.  So, reopen stdin and stdout on
287      /dev/tty before the rexec and give them back their original value
288      after.  */
289
290   if (freopen ("/dev/tty", "r", stdin) == NULL)
291     freopen ("/dev/null", "r", stdin);
292   if (freopen ("/dev/tty", "w", stdout) == NULL)
293     freopen ("/dev/null", "w", stdout);
294
295   if (rexecserv = getservbyname ("exec", "tcp"), !rexecserv)
296     error (EXIT_ON_EXEC_ERROR, 0, _("exec/tcp: Service not available"));
297
298   result = rexec (&host, rexecserv->s_port, user, NULL,
299                    "/etc/rmt", (int *) NULL);
300   if (fclose (stdin) == EOF)
301     error (0, errno, _("stdin"));
302   fdopen (saved_stdin, "r");
303   if (fclose (stdout) == EOF)
304     error (0, errno, _("stdout"));
305   fdopen (saved_stdout, "w");
306
307   return result;
308 }
309
310 #endif /* WITH_REXEC */
311
312 /*------------------------------------------------------------------------.
313 | Open a file (a magnetic tape device?) on the system specified in PATH,  |
314 | as the given user.  PATH has the form `[USER@]HOST:FILE'.  OPEN_MODE is |
315 | O_RDONLY, O_WRONLY, etc.  If successful, return the remote pipe number  |
316 | plus BIAS.  REMOTE_SHELL may be overriden.  On error, return -1.        |
317 `------------------------------------------------------------------------*/
318
319 int
320 rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell)
321 {
322   int remote_pipe_number;       /* pseudo, biased file descriptor */
323   char *path_copy ;             /* copy of path string */
324   char *remote_host;            /* remote host name */
325   char *remote_file;            /* remote file name (often a device) */
326   char *remote_user;            /* remote user name */
327
328   /* Find an unused pair of file descriptors.  */
329
330   for (remote_pipe_number = 0;
331        remote_pipe_number < MAXUNIT;
332        remote_pipe_number++)
333     if (READ_SIDE (remote_pipe_number) == -1
334         && WRITE_SIDE (remote_pipe_number) == -1)
335       break;
336
337   if (remote_pipe_number == MAXUNIT)
338     {
339       errno = EMFILE;           /* FIXME: errno should be read-only */
340       return -1;
341     }
342
343   /* Pull apart the system and device, and optional user.  */
344
345   {
346     char *cursor;
347
348     path_copy = xstrdup (path);
349     remote_host = path_copy;
350     remote_user = NULL;
351     remote_file = NULL;
352
353     for (cursor = path_copy; *cursor; cursor++)
354       switch (*cursor)
355         {
356         default:
357           break;
358
359         case '@':
360           if (!remote_user)
361             {
362               remote_user = remote_host;
363               *cursor = '\0';
364               remote_host = cursor + 1;
365             }
366           break;
367
368         case ':':
369           if (!remote_file)
370             {
371               *cursor = '\0';
372               remote_file = cursor + 1;
373             }
374           break;
375         }
376   }
377
378   /* FIXME: Should somewhat validate the decoding, here.  */
379
380   if (remote_user && *remote_user == '\0')
381     remote_user = NULL;
382
383 #if WITH_REXEC
384
385   /* Execute the remote command using rexec.  */
386
387   READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user);
388   if (READ_SIDE (remote_pipe_number) < 0)
389     {
390       free (path_copy);
391       return -1;
392     }
393
394   WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number);
395
396 #else /* not WITH_REXEC */
397   {
398     const char *remote_shell_basename;
399     pid_t status;
400
401     /* Identify the remote command to be executed.  */
402
403     if (!remote_shell)
404       {
405 #ifdef REMOTE_SHELL
406         remote_shell = REMOTE_SHELL;
407 #else
408         errno = EIO;            /* FIXME: errno should be read-only */
409         free (path_copy);
410         return -1;
411 #endif
412       }
413     remote_shell_basename = strrchr (remote_shell, '/');
414     if (remote_shell_basename)
415       remote_shell_basename++;
416     else
417       remote_shell_basename = remote_shell;
418
419     /* Set up the pipes for the `rsh' command, and fork.  */
420
421     if (pipe (to_remote[remote_pipe_number]) == -1
422         || pipe (from_remote[remote_pipe_number]) == -1)
423       {
424         free (path_copy);
425         return -1;
426       }
427
428     status = fork ();
429     if (status == -1)
430       {
431         free (path_copy);
432         return -1;
433       }
434
435     if (status == 0)
436       {
437         /* Child.  */
438
439         close (0);
440         dup (to_remote[remote_pipe_number][PREAD]);
441         close (to_remote[remote_pipe_number][PREAD]);
442         close (to_remote[remote_pipe_number][PWRITE]);
443
444         close (1);
445         dup (from_remote[remote_pipe_number][PWRITE]);
446         close (from_remote[remote_pipe_number][PREAD]);
447         close (from_remote[remote_pipe_number][PWRITE]);
448
449 #if !MSDOS
450         setuid (getuid ());
451         setgid (getgid ());
452 #endif
453
454         if (remote_user)
455           execl (remote_shell, remote_shell_basename, remote_host,
456                  "-l", remote_user, "/etc/rmt", (char *) 0);
457         else
458           execl (remote_shell, remote_shell_basename, remote_host,
459                  "/etc/rmt", (char *) 0);
460
461         /* Bad problems if we get here.  */
462
463         /* In a previous version, _exit was used here instead of exit.  */
464         error (EXIT_ON_EXEC_ERROR, errno, _("Cannot execute remote shell"));
465       }
466
467     /* Parent.  */
468
469     close (from_remote[remote_pipe_number][PWRITE]);
470     close (to_remote[remote_pipe_number][PREAD]);
471   }
472 #endif /* not WITH_REXEC */
473
474   /* Attempt to open the tape device.  */
475
476   {
477     char command_buffer[COMMAND_BUFFER_SIZE];
478
479     sprintf (command_buffer, "O%s\n%d\n", remote_file, open_mode);
480     if (do_command (remote_pipe_number, command_buffer) == -1
481         || get_status (remote_pipe_number) == -1)
482       {
483         _rmt_shutdown (remote_pipe_number, errno);
484         free (path_copy);
485         return -1;
486       }
487   }
488
489   free (path_copy);
490   return remote_pipe_number + bias;
491 }
492
493 /*----------------------------------------------------------------.
494 | Close remote tape connection HANDLE and shut down.  Return 0 if |
495 | successful, -1 on error.                                        |
496 `----------------------------------------------------------------*/
497
498 int
499 rmt_close__ (int handle)
500 {
501   int status;
502
503   if (do_command (handle, "C\n") == -1)
504     return -1;
505
506   status = get_status (handle);
507   _rmt_shutdown (handle, errno);
508   return status;
509 }
510
511 /*-------------------------------------------------------------------------.
512 | Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE.  |
513 | Return the number of bytes read on success, -1 on error.                 |
514 `-------------------------------------------------------------------------*/
515
516 ssize_t
517 rmt_read__ (int handle, char *buffer, size_t length)
518 {
519   char command_buffer[COMMAND_BUFFER_SIZE];
520   ssize_t status, rlen;
521   size_t counter;
522
523   sprintf (command_buffer, "R%lu\n", (unsigned long) length);
524   if (do_command (handle, command_buffer) == -1
525       || (status = get_status (handle)) == -1)
526     return -1;
527
528   for (counter = 0; counter < status; counter += rlen, buffer += rlen)
529     {
530       rlen = read (READ_SIDE (handle), buffer, status - counter);
531       if (rlen <= 0)
532         {
533           _rmt_shutdown (handle, EIO);
534           return -1;
535         }
536     }
537
538   return status;
539 }
540
541 /*-------------------------------------------------------------------------.
542 | Write LENGTH bytes from BUFFER to remote tape connection HANDLE.  Return |
543 | the number of bytes written on success, -1 on error.                     |
544 `-------------------------------------------------------------------------*/
545
546 ssize_t
547 rmt_write__ (int handle, char *buffer, size_t length)
548 {
549   char command_buffer[COMMAND_BUFFER_SIZE];
550   RETSIGTYPE (*pipe_handler) ();
551
552   sprintf (command_buffer, "W%lu\n", (unsigned long) length);
553   if (do_command (handle, command_buffer) == -1)
554     return -1;
555
556   pipe_handler = signal (SIGPIPE, SIG_IGN);
557   if (write (WRITE_SIDE (handle), buffer, length) == length)
558     {
559       signal (SIGPIPE, pipe_handler);
560       return get_status (handle);
561     }
562
563   /* Write error.  */
564
565   signal (SIGPIPE, pipe_handler);
566   _rmt_shutdown (handle, EIO);
567   return -1;
568 }
569
570 /*------------------------------------------------------------------------.
571 | Perform an imitation lseek operation on remote tape connection HANDLE.  |
572 | Return the new file offset if successful, -1 if on error.               |
573 `------------------------------------------------------------------------*/
574
575 off_t
576 rmt_lseek__ (int handle, off_t offset, int whence)
577 {
578   char command_buffer[COMMAND_BUFFER_SIZE];
579   char operand_buffer[UINTMAX_STRSIZE_BOUND];
580   uintmax_t u = offset < 0 ? - (uintmax_t) offset : (uintmax_t) offset;
581   char *p = operand_buffer + sizeof operand_buffer;
582
583   do
584     *--p = '0' + (int) (u % 10);
585   while ((u /= 10) != 0);
586   if (offset < 0)
587     *--p = '-';
588
589   sprintf (command_buffer, "L%s\n%d\n", p, whence);
590
591   if (do_command (handle, command_buffer) == -1)
592     return -1;
593
594   return get_status_off (handle);
595 }
596
597 /*-----------------------------------------------------------------------.
598 | Perform a raw tape operation on remote tape connection HANDLE.  Return |
599 | the results of the ioctl, or -1 on error.                              |
600 `-----------------------------------------------------------------------*/
601
602 int
603 rmt_ioctl__ (int handle, int operation, char *argument)
604 {
605   switch (operation)
606     {
607     default:
608       errno = EOPNOTSUPP;       /* FIXME: errno should be read-only */
609       return -1;
610
611 #ifdef MTIOCTOP
612     case MTIOCTOP:
613       {
614         char command_buffer[COMMAND_BUFFER_SIZE];
615         char operand_buffer[UINTMAX_STRSIZE_BOUND];
616         uintmax_t u = (((struct mtop *) argument)->mt_count < 0
617                        ? - (uintmax_t) ((struct mtop *) argument)->mt_count
618                        : (uintmax_t) ((struct mtop *) argument)->mt_count);
619         char *p = operand_buffer + sizeof operand_buffer;
620         
621         do
622           *--p = '0' + (int) (u % 10);
623         while ((u /= 10) != 0);
624         if (((struct mtop *) argument)->mt_count < 0)
625           *--p = '-';
626
627         /* MTIOCTOP is the easy one.  Nothing is transfered in binary.  */
628
629         sprintf (command_buffer, "I%d\n%s\n",
630                  ((struct mtop *) argument)->mt_op, p);
631         if (do_command (handle, command_buffer) == -1)
632           return -1;
633
634         return get_status (handle);
635       }
636 #endif /* MTIOCTOP */
637
638 #ifdef MTIOCGET
639     case MTIOCGET:
640       {
641         ssize_t status;
642         ssize_t counter;
643
644         /* Grab the status and read it directly into the structure.  This
645            assumes that the status buffer is not padded and that 2 shorts
646            fit in a long without any word alignment problems; i.e., the
647            whole struct is contiguous.  NOTE - this is probably NOT a good
648            assumption.  */
649
650         if (do_command (handle, "S") == -1
651             || (status = get_status (handle), status == -1))
652           return -1;
653
654         for (; status > 0; status -= counter, argument += counter)
655           {
656             counter = read (READ_SIDE (handle), argument, (size_t) status);
657             if (counter <= 0)
658               {
659                 _rmt_shutdown (handle, EIO);
660                 return -1;
661               }
662           }
663
664         /* Check for byte position.  mt_type (or mt_model) is a small integer
665            field (normally) so we will check its magnitude.  If it is larger
666            than 256, we will assume that the bytes are swapped and go through
667            and reverse all the bytes.  */
668
669         if (((struct mtget *) argument)->MTIO_CHECK_FIELD < 256)
670           return 0;
671
672         for (counter = 0; counter < status; counter += 2)
673           {
674             char copy = argument[counter];
675
676             argument[counter] = argument[counter + 1];
677             argument[counter + 1] = copy;
678           }
679
680         return 0;
681       }
682 #endif /* MTIOCGET */
683
684     }
685 }