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