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