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