/* Functions for communicating with a remote tape drive.
- Copyright (C) 1988, 1992, 1994, 1996 Free Software Foundation, Inc.
+
+ Copyright 1988, 1992, 1994, 1996, 1997, 1999, 2000, 2001 Free Software
+ Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Originally written by Jeff Lee, modified some by Arnold Robbins. Redone
as a library that can replace open, read, write, etc., by Fred Fish, with
some additional work by Arnold Robbins. Modified to make all rmt* calls
- into macros for speed by Jay Fenlason. Use -DHAVE_NETDB_H for rexec
+ into macros for speed by Jay Fenlason. Use -DWITH_REXEC for rexec
code, courtesy of Dan Kegel. */
#include "system.h"
+#include <safe-read.h>
+#include <full-write.h>
+
/* Try hard to get EOPNOTSUPP defined. 486/ISC has it in net/errno.h,
3B2/SVR3 has it in sys/inet.h. Otherwise, like on MSDOS, use EINVAL. */
#include "rmt.h"
-/* FIXME: Just to shut up -Wall. */
-int rexec ();
-
/* Exit status if exec errors. */
#define EXIT_ON_EXEC_ERROR 128
char *rmt_path__;
\f
-/*----------------------------------------------------------------------.
-| Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE. |
-`----------------------------------------------------------------------*/
-
+/* Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE. */
static void
_rmt_shutdown (int handle, int errno_value)
{
close (WRITE_SIDE (handle));
READ_SIDE (handle) = -1;
WRITE_SIDE (handle) = -1;
- errno = errno_value; /* FIXME: errno should be read-only */
+ errno = errno_value;
}
-/*-------------------------------------------------------------------------.
-| Attempt to perform the remote tape command specified in BUFFER on remote |
-| tape connection HANDLE. Return 0 if successful, -1 on error. |
-`-------------------------------------------------------------------------*/
-
+/* Attempt to perform the remote tape command specified in BUFFER on
+ remote tape connection HANDLE. Return 0 if successful, -1 on
+ error. */
static int
do_command (int handle, const char *buffer)
{
- int length;
- RETSIGTYPE (*pipe_handler) ();
-
/* Save the current pipe handler and try to make the request. */
- pipe_handler = signal (SIGPIPE, SIG_IGN);
- length = strlen (buffer);
- if (write (WRITE_SIDE (handle), buffer, (size_t) length) == length)
- {
- signal (SIGPIPE, pipe_handler);
- return 0;
- }
+ size_t length = strlen (buffer);
+ RETSIGTYPE (*pipe_handler) () = signal (SIGPIPE, SIG_IGN);
+ ssize_t written = full_write (WRITE_SIDE (handle), buffer, length);
+ signal (SIGPIPE, pipe_handler);
+
+ if (written == length)
+ return 0;
/* Something went wrong. Close down and go home. */
- signal (SIGPIPE, pipe_handler);
_rmt_shutdown (handle, EIO);
return -1;
}
-/*----------------------------------------------------------------------.
-| Read and return the status from remote tape connection HANDLE. If an |
-| error occurred, return -1 and set errno. |
-`----------------------------------------------------------------------*/
-
-static int
-get_status (int handle)
+static char *
+get_status_string (int handle, char *command_buffer)
{
- char command_buffer[COMMAND_BUFFER_SIZE];
char *cursor;
int counter;
counter < COMMAND_BUFFER_SIZE;
counter++, cursor++)
{
- if (read (READ_SIDE (handle), cursor, 1) != 1)
+ if (safe_read (READ_SIDE (handle), cursor, 1) != 1)
{
_rmt_shutdown (handle, EIO);
- return -1;
+ return 0;
}
if (*cursor == '\n')
{
if (counter == COMMAND_BUFFER_SIZE)
{
_rmt_shutdown (handle, EIO);
- return -1;
+ return 0;
}
/* Check the return status. */
if (*cursor == 'E' || *cursor == 'F')
{
- errno = atoi (cursor + 1); /* FIXME: errno should be read-only */
+ errno = atoi (cursor + 1);
/* Skip the error message line. */
{
char character;
- while (read (READ_SIDE (handle), &character, 1) == 1)
+ while (safe_read (READ_SIDE (handle), &character, 1) == 1)
if (character == '\n')
break;
}
if (*cursor == 'F')
_rmt_shutdown (handle, errno);
- return -1;
+ return 0;
}
/* Check for mis-synced pipes. */
if (*cursor != 'A')
{
_rmt_shutdown (handle, EIO);
- return -1;
+ return 0;
}
/* Got an `A' (success) response. */
- return atoi (cursor + 1);
+ return cursor + 1;
}
-#if HAVE_NETDB_H
+/* Read and return the status from remote tape connection HANDLE. If
+ an error occurred, return -1 and set errno. */
+static long
+get_status (int handle)
+{
+ char command_buffer[COMMAND_BUFFER_SIZE];
+ const char *status = get_status_string (handle, command_buffer);
+ return status ? atol (status) : -1L;
+}
+
+static off_t
+get_status_off (int handle)
+{
+ char command_buffer[COMMAND_BUFFER_SIZE];
+ const char *status = get_status_string (handle, command_buffer);
+
+ if (! status)
+ return -1;
+ else
+ {
+ /* Parse status, taking care to check for overflow.
+ We can't use standard functions,
+ since off_t might be longer than long. */
+
+ off_t count = 0;
+ int negative;
+
+ for (; *status == ' ' || *status == '\t'; status++)
+ continue;
+
+ negative = *status == '-';
+ status += negative || *status == '+';
+
+ for (;;)
+ {
+ int digit = *status++ - '0';
+ if (9 < (unsigned) digit)
+ break;
+ else
+ {
+ off_t c10 = 10 * count;
+ off_t nc = negative ? c10 - digit : c10 + digit;
+ if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
+ return -1;
+ count = nc;
+ }
+ }
-/*-------------------------------------------------------------------------.
-| Execute /etc/rmt as user USER on remote system HOST using rexec. Return |
-| a file descriptor of a bidirectional socket for stdin and stdout. If |
-| USER is NULL, use the current username. |
-| |
-| By default, this code is not used, since it requires that the user have |
-| a .netrc file in his/her home directory, or that the application |
-| designer be willing to have rexec prompt for login and password info. |
-| This may be unacceptable, and .rhosts files for use with rsh are much |
-| more common on BSD systems. |
-`-------------------------------------------------------------------------*/
+ return count;
+ }
+}
+
+#if WITH_REXEC
+/* Execute /etc/rmt as user USER on remote system HOST using rexec.
+ Return a file descriptor of a bidirectional socket for stdin and
+ stdout. If USER is zero, use the current username.
+
+ By default, this code is not used, since it requires that the user
+ have a .netrc file in his/her home directory, or that the
+ application designer be willing to have rexec prompt for login and
+ password info. This may be unacceptable, and .rhosts files for use
+ with rsh are much more common on BSD systems. */
static int
_rmt_rexec (char *host, char *user)
{
- int saved_stdin = dup (fileno (stdin));
- int saved_stdout = dup (fileno (stdout));
+ int saved_stdin = dup (STDIN_FILENO);
+ int saved_stdout = dup (STDOUT_FILENO);
struct servent *rexecserv;
int result;
/dev/tty before the rexec and give them back their original value
after. */
- if (freopen ("/dev/tty", "r", stdin) == NULL)
+ if (! freopen ("/dev/tty", "r", stdin))
freopen ("/dev/null", "r", stdin);
- if (freopen ("/dev/tty", "w", stdout) == NULL)
+ if (! freopen ("/dev/tty", "w", stdout))
freopen ("/dev/null", "w", stdout);
if (rexecserv = getservbyname ("exec", "tcp"), !rexecserv)
error (EXIT_ON_EXEC_ERROR, 0, _("exec/tcp: Service not available"));
- result = rexec (&host, rexecserv->s_port, user, NULL,
- "/etc/rmt", (int *) NULL);
+ result = rexec (&host, rexecserv->s_port, user, 0, "/etc/rmt", 0);
if (fclose (stdin) == EOF)
error (0, errno, _("stdin"));
fdopen (saved_stdin, "r");
return result;
}
-#endif /* HAVE_NETDB_H */
+#endif /* WITH_REXEC */
-/*------------------------------------------------------------------------.
-| Open a file (a magnetic tape device?) on the system specified in PATH, |
-| as the given user. PATH has the form `[USER@]HOST:FILE'. OPEN_MODE is |
-| O_RDONLY, O_WRONLY, etc. If successful, return the remote pipe number |
-| plus BIAS. REMOTE_SHELL may be overriden. On error, return -1. |
-`------------------------------------------------------------------------*/
+/* Place into BUF a string representing OFLAG, which must be suitable
+ as argument 2 of `open'. BUF must be large enough to hold the
+ result. This function should generate a string that decode_oflag
+ can parse. */
+static void
+encode_oflag (char *buf, int oflag)
+{
+ sprintf (buf, "%d ", oflag);
+
+ switch (oflag & O_ACCMODE)
+ {
+ case O_RDONLY: strcat (buf, "O_RDONLY"); break;
+ case O_RDWR: strcat (buf, "O_RDWR"); break;
+ case O_WRONLY: strcat (buf, "O_WRONLY"); break;
+ default: abort ();
+ }
+
+#ifdef O_APPEND
+ if (oflag & O_APPEND) strcat (buf, "|O_APPEND");
+#endif
+ if (oflag & O_CREAT) strcat (buf, "|O_CREAT");
+#ifdef O_DSYNC
+ if (oflag & O_DSYNC) strcat (buf, "|O_DSYNC");
+#endif
+ if (oflag & O_EXCL) strcat (buf, "|O_EXCL");
+#ifdef O_LARGEFILE
+ if (oflag & O_LARGEFILE) strcat (buf, "|O_LARGEFILE");
+#endif
+#ifdef O_NOCTTY
+ if (oflag & O_NOCTTY) strcat (buf, "|O_NOCTTY");
+#endif
+#ifdef O_NONBLOCK
+ if (oflag & O_NONBLOCK) strcat (buf, "|O_NONBLOCK");
+#endif
+#ifdef O_RSYNC
+ if (oflag & O_RSYNC) strcat (buf, "|O_RSYNC");
+#endif
+#ifdef O_SYNC
+ if (oflag & O_SYNC) strcat (buf, "|O_SYNC");
+#endif
+ if (oflag & O_TRUNC) strcat (buf, "|O_TRUNC");
+}
+/* Open a file (a magnetic tape device?) on the system specified in
+ PATH, as the given user. PATH has the form `[USER@]HOST:FILE'.
+ OPEN_MODE is O_RDONLY, O_WRONLY, etc. If successful, return the
+ remote pipe number plus BIAS. REMOTE_SHELL may be overridden. On
+ error, return -1. */
int
rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell)
{
if (remote_pipe_number == MAXUNIT)
{
- errno = EMFILE; /* FIXME: errno should be read-only */
+ errno = EMFILE;
return -1;
}
path_copy = xstrdup (path);
remote_host = path_copy;
- remote_user = NULL;
- remote_file = NULL;
+ remote_user = 0;
+ remote_file = 0;
for (cursor = path_copy; *cursor; cursor++)
switch (*cursor)
default:
break;
+ case '\n':
+ /* Do not allow newlines in the path, since the protocol
+ uses newline delimiters. */
+ free (path_copy);
+ errno = ENOENT;
+ return -1;
+
case '@':
if (!remote_user)
{
/* FIXME: Should somewhat validate the decoding, here. */
if (remote_user && *remote_user == '\0')
- remote_user = NULL;
+ remote_user = 0;
-#if HAVE_NETDB_H
+#if WITH_REXEC
/* Execute the remote command using rexec. */
READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user);
if (READ_SIDE (remote_pipe_number) < 0)
{
+ int e = errno;
free (path_copy);
+ errno = e;
return -1;
}
WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number);
-#else /* not HAVE_NETDB_H */
+#else /* not WITH_REXEC */
{
const char *remote_shell_basename;
- int status;
+ pid_t status;
/* Identify the remote command to be executed. */
#ifdef REMOTE_SHELL
remote_shell = REMOTE_SHELL;
#else
- errno = EIO; /* FIXME: errno should be read-only */
free (path_copy);
+ errno = EIO;
return -1;
#endif
}
- remote_shell_basename = strrchr (remote_shell, '/');
- if (remote_shell_basename)
- remote_shell_basename++;
- else
- remote_shell_basename = remote_shell;
+ remote_shell_basename = base_name (remote_shell);
/* Set up the pipes for the `rsh' command, and fork. */
if (pipe (to_remote[remote_pipe_number]) == -1
|| pipe (from_remote[remote_pipe_number]) == -1)
{
+ int e = errno;
free (path_copy);
+ errno = e;
return -1;
}
status = fork ();
if (status == -1)
{
+ int e = errno;
free (path_copy);
+ errno = e;
return -1;
}
{
/* Child. */
- close (0);
+ close (STDIN_FILENO);
dup (to_remote[remote_pipe_number][PREAD]);
close (to_remote[remote_pipe_number][PREAD]);
close (to_remote[remote_pipe_number][PWRITE]);
- close (1);
+ close (STDOUT_FILENO);
dup (from_remote[remote_pipe_number][PWRITE]);
close (from_remote[remote_pipe_number][PREAD]);
close (from_remote[remote_pipe_number][PWRITE]);
close (from_remote[remote_pipe_number][PWRITE]);
close (to_remote[remote_pipe_number][PREAD]);
}
-#endif /* not HAVE_NETDB_H */
+#endif /* not WITH_REXEC */
/* Attempt to open the tape device. */
{
- char command_buffer[COMMAND_BUFFER_SIZE];
-
- sprintf (command_buffer, "O%s\n%d\n", remote_file, open_mode);
+ size_t remote_file_len = strlen (remote_file);
+ char *command_buffer = xmalloc (remote_file_len + 1000);
+ sprintf (command_buffer, "O%s\n", remote_file);
+ encode_oflag (command_buffer + remote_file_len + 2, open_mode);
+ strcat (command_buffer, "\n");
if (do_command (remote_pipe_number, command_buffer) == -1
|| get_status (remote_pipe_number) == -1)
{
- _rmt_shutdown (remote_pipe_number, errno);
+ int e = errno;
+ free (command_buffer);
free (path_copy);
+ _rmt_shutdown (remote_pipe_number, e);
return -1;
}
+ free (command_buffer);
}
free (path_copy);
return remote_pipe_number + bias;
}
-/*----------------------------------------------------------------.
-| Close remote tape connection HANDLE and shut down. Return 0 if |
-| successful, -1 on error. |
-`----------------------------------------------------------------*/
-
+/* Close remote tape connection HANDLE and shut down. Return 0 if
+ successful, -1 on error. */
int
rmt_close__ (int handle)
{
return status;
}
-/*-------------------------------------------------------------------------.
-| Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE. |
-| Return the number of bytes read on success, -1 on error. |
-`-------------------------------------------------------------------------*/
-
-int
-rmt_read__ (int handle, char *buffer, unsigned int length)
+/* Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE.
+ Return the number of bytes read on success, -1 on error. */
+ssize_t
+rmt_read__ (int handle, char *buffer, size_t length)
{
char command_buffer[COMMAND_BUFFER_SIZE];
- int status;
- int counter;
+ ssize_t status, rlen;
+ size_t counter;
- sprintf (command_buffer, "R%d\n", length);
+ sprintf (command_buffer, "R%lu\n", (unsigned long) length);
if (do_command (handle, command_buffer) == -1
|| (status = get_status (handle)) == -1)
return -1;
- for (counter = 0; counter < status; counter += length, buffer += length)
+ for (counter = 0; counter < status; counter += rlen, buffer += rlen)
{
- length = read (READ_SIDE (handle), buffer, (size_t) (status - counter));
- if (length <= 0)
+ rlen = safe_read (READ_SIDE (handle), buffer, status - counter);
+ if (rlen <= 0)
{
_rmt_shutdown (handle, EIO);
return -1;
return status;
}
-/*-------------------------------------------------------------------------.
-| Write LENGTH bytes from BUFFER to remote tape connection HANDLE. Return |
-| the number of bytes written on success, -1 on error. |
-`-------------------------------------------------------------------------*/
-
-int
-rmt_write__ (int handle, char *buffer, unsigned int length)
+/* Write LENGTH bytes from BUFFER to remote tape connection HANDLE.
+ Return the number of bytes written on success, -1 on error. */
+ssize_t
+rmt_write__ (int handle, char *buffer, size_t length)
{
char command_buffer[COMMAND_BUFFER_SIZE];
RETSIGTYPE (*pipe_handler) ();
+ size_t written;
- sprintf (command_buffer, "W%d\n", length);
+ sprintf (command_buffer, "W%lu\n", (unsigned long) length);
if (do_command (handle, command_buffer) == -1)
return -1;
pipe_handler = signal (SIGPIPE, SIG_IGN);
- if (write (WRITE_SIDE (handle), buffer, length) == length)
- {
- signal (SIGPIPE, pipe_handler);
- return get_status (handle);
- }
+ written = full_write (WRITE_SIDE (handle), buffer, length);
+ signal (SIGPIPE, pipe_handler);
+ if (written == length)
+ return get_status (handle);
/* Write error. */
- signal (SIGPIPE, pipe_handler);
_rmt_shutdown (handle, EIO);
return -1;
}
-/*------------------------------------------------------------------------.
-| Perform an imitation lseek operation on remote tape connection HANDLE. |
-| Return the new file offset if successful, -1 if on error. |
-`------------------------------------------------------------------------*/
-
-long
+/* Perform an imitation lseek operation on remote tape connection
+ HANDLE. Return the new file offset if successful, -1 if on error. */
+off_t
rmt_lseek__ (int handle, off_t offset, int whence)
{
char command_buffer[COMMAND_BUFFER_SIZE];
+ char operand_buffer[UINTMAX_STRSIZE_BOUND];
+ uintmax_t u = offset < 0 ? - (uintmax_t) offset : (uintmax_t) offset;
+ char *p = operand_buffer + sizeof operand_buffer;
+
+ do
+ *--p = '0' + (int) (u % 10);
+ while ((u /= 10) != 0);
+ if (offset < 0)
+ *--p = '-';
+
+ switch (whence)
+ {
+ case SEEK_SET: whence = 0; break;
+ case SEEK_CUR: whence = 1; break;
+ case SEEK_END: whence = 2; break;
+ default: abort ();
+ }
+
+ sprintf (command_buffer, "L%s\n%d\n", p, whence);
- sprintf (command_buffer, "L%ld\n%d\n", offset, whence);
if (do_command (handle, command_buffer) == -1)
return -1;
- return get_status (handle);
+ return get_status_off (handle);
}
-/*-----------------------------------------------------------------------.
-| Perform a raw tape operation on remote tape connection HANDLE. Return |
-| the results of the ioctl, or -1 on error. |
-`-----------------------------------------------------------------------*/
-
+/* Perform a raw tape operation on remote tape connection HANDLE.
+ Return the results of the ioctl, or -1 on error. */
int
rmt_ioctl__ (int handle, int operation, char *argument)
{
switch (operation)
{
default:
- errno = EOPNOTSUPP; /* FIXME: errno should be read-only */
+ errno = EOPNOTSUPP;
return -1;
#ifdef MTIOCTOP
case MTIOCTOP:
{
char command_buffer[COMMAND_BUFFER_SIZE];
-
- /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
-
- sprintf (command_buffer, "I%d\n%d\n", ((struct mtop *) argument)->mt_op,
- ((struct mtop *) argument)->mt_count);
+ char operand_buffer[UINTMAX_STRSIZE_BOUND];
+ uintmax_t u = (((struct mtop *) argument)->mt_count < 0
+ ? - (uintmax_t) ((struct mtop *) argument)->mt_count
+ : (uintmax_t) ((struct mtop *) argument)->mt_count);
+ char *p = operand_buffer + sizeof operand_buffer;
+
+ do
+ *--p = '0' + (int) (u % 10);
+ while ((u /= 10) != 0);
+ if (((struct mtop *) argument)->mt_count < 0)
+ *--p = '-';
+
+ /* MTIOCTOP is the easy one. Nothing is transferred in binary. */
+
+ sprintf (command_buffer, "I%d\n%s\n",
+ ((struct mtop *) argument)->mt_op, p);
if (do_command (handle, command_buffer) == -1)
return -1;
- /* Return the count. */
-
return get_status (handle);
}
#endif /* MTIOCTOP */
#ifdef MTIOCGET
case MTIOCGET:
{
- int status;
- int counter;
+ ssize_t status;
+ ssize_t counter;
/* Grab the status and read it directly into the structure. This
assumes that the status buffer is not padded and that 2 shorts
for (; status > 0; status -= counter, argument += counter)
{
- counter = read (READ_SIDE (handle), argument, (size_t) status);
+ counter = safe_read (READ_SIDE (handle), argument, status);
if (counter <= 0)
{
_rmt_shutdown (handle, EIO);