X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Frtapelib.c;h=a31a8930c912cffca617ac4e9f9e2c294983c70a;hb=e3bd2a63a62aede213fe3b451952c254b095090d;hp=937fd70c605c862feb06ed20f4d856c37a4cb4f3;hpb=d46735235be2b3bcaf2e580d86f321130cd78e94;p=chaz%2Ftar diff --git a/src/rtapelib.c b/src/rtapelib.c index 937fd70..a31a893 100644 --- a/src/rtapelib.c +++ b/src/rtapelib.c @@ -1,5 +1,7 @@ /* 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, 2004 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 @@ -31,6 +33,9 @@ code, courtesy of Dan Kegel. */ #include "system.h" +#include "common.h" +#include +#include /* 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. */ @@ -55,9 +60,6 @@ #include "rmt.h" -/* FIXME: Just to shut up -Wall. */ -int rexec (); - /* Exit status if exec errors. */ #define EXIT_ON_EXEC_ERROR 128 @@ -86,14 +88,13 @@ static int from_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; /* The pipes for sending data to remote tape drives. */ static int to_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; +#define RMT_COMMAND (rmt_command_option ? rmt_command_option : "/etc/rmt") + /* Temporary variable used by macros in rmt.h. */ -char *rmt_path__; +char *rmt_dev_name__; -/*----------------------------------------------------------------------. -| 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) { @@ -101,33 +102,27 @@ _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) { - size_t 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 (full_write (WRITE_SIDE (handle), buffer, 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; } @@ -170,8 +165,6 @@ get_status_string (int handle, char *command_buffer) if (*cursor == 'E' || *cursor == 'F') { - errno = atoi (cursor + 1); /* FIXME: errno should be read-only */ - /* Skip the error message line. */ /* FIXME: there is better to do than merely ignoring error messages @@ -185,6 +178,8 @@ get_status_string (int handle, char *command_buffer) break; } + errno = atoi (cursor + 1); + if (*cursor == 'F') _rmt_shutdown (handle, errno); @@ -204,17 +199,21 @@ get_status_string (int handle, char *command_buffer) return cursor + 1; } -/*----------------------------------------------------------------------. -| Read and return the status from remote tape connection HANDLE. If an | -| error occurred, return -1 and set errno. | -`----------------------------------------------------------------------*/ - -static long +/* Read and return the status from remote tape connection HANDLE. If + an error occurred, return -1 and set errno. */ +static long int get_status (int handle) { char command_buffer[COMMAND_BUFFER_SIZE]; const char *status = get_status_string (handle, command_buffer); - return status ? atol (status) : -1L; + if (status) + { + long int result = atol (status); + if (0 <= result) + return result; + errno = EIO; + } + return -1; } static off_t @@ -236,10 +235,10 @@ get_status_off (int handle) for (; *status == ' ' || *status == '\t'; status++) continue; - + negative = *status == '-'; status += negative || *status == '+'; - + for (;;) { int digit = *status++ - '0'; @@ -261,18 +260,15 @@ get_status_off (int handle) #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 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. | -`-------------------------------------------------------------------------*/ +/* 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) { @@ -287,16 +283,15 @@ _rmt_rexec (char *host, char *user) /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, RMT_COMMAND, 0); if (fclose (stdin) == EOF) error (0, errno, _("stdin")); fdopen (saved_stdin, "r"); @@ -309,18 +304,60 @@ _rmt_rexec (char *host, char *user) #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 + FILE_NAME, as the given user. FILE_NAME 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) +rmt_open__ (const char *file_name, int open_mode, int bias, + const char *remote_shell) { int remote_pipe_number; /* pseudo, biased file descriptor */ - char *path_copy ; /* copy of path string */ + char *file_name_copy; /* copy of file_name string */ char *remote_host; /* remote host name */ char *remote_file; /* remote file name (often a device) */ char *remote_user; /* remote user name */ @@ -336,7 +373,7 @@ 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; } @@ -345,17 +382,24 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) { char *cursor; - path_copy = xstrdup (path); - remote_host = path_copy; - remote_user = NULL; - remote_file = NULL; + file_name_copy = xstrdup (file_name); + remote_host = file_name_copy; + remote_user = 0; + remote_file = 0; - for (cursor = path_copy; *cursor; cursor++) + for (cursor = file_name_copy; *cursor; cursor++) switch (*cursor) { default: break; + case '\n': + /* Do not allow newlines in the file_name, since the protocol + uses newline delimiters. */ + free (file_name_copy); + errno = ENOENT; + return -1; + case '@': if (!remote_user) { @@ -378,7 +422,7 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) /* FIXME: Should somewhat validate the decoding, here. */ if (remote_user && *remote_user == '\0') - remote_user = NULL; + remote_user = 0; #if WITH_REXEC @@ -387,7 +431,9 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user); if (READ_SIDE (remote_pipe_number) < 0) { - free (path_copy); + int e = errno; + free (file_name_copy); + errno = e; return -1; } @@ -405,30 +451,30 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) #ifdef REMOTE_SHELL remote_shell = REMOTE_SHELL; #else - errno = EIO; /* FIXME: errno should be read-only */ - free (path_copy); + free (file_name_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) { - free (path_copy); + int e = errno; + free (file_name_copy); + errno = e; return -1; } status = fork (); if (status == -1) { - free (path_copy); + int e = errno; + free (file_name_copy); + errno = e; return -1; } @@ -446,17 +492,14 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) close (from_remote[remote_pipe_number][PREAD]); close (from_remote[remote_pipe_number][PWRITE]); -#if !MSDOS - setuid (getuid ()); - setgid (getgid ()); -#endif + sys_reset_uid_gid (); if (remote_user) execl (remote_shell, remote_shell_basename, remote_host, - "-l", remote_user, "/etc/rmt", (char *) 0); + "-l", remote_user, RMT_COMMAND, (char *) 0); else execl (remote_shell, remote_shell_basename, remote_host, - "/etc/rmt", (char *) 0); + RMT_COMMAND, (char *) 0); /* Bad problems if we get here. */ @@ -474,31 +517,33 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) /* 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); - free (path_copy); + int e = errno; + free (command_buffer); + free (file_name_copy); + _rmt_shutdown (remote_pipe_number, e); return -1; } + free (command_buffer); } - free (path_copy); + free (file_name_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) { - int status; + long int status; if (do_command (handle, "C\n") == -1) return -1; @@ -508,70 +553,68 @@ 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. | -`-------------------------------------------------------------------------*/ - -ssize_t +/* Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE. + Return the number of bytes read on success, SAFE_READ_ERROR on error. */ +size_t rmt_read__ (int handle, char *buffer, size_t length) { char command_buffer[COMMAND_BUFFER_SIZE]; - ssize_t status, rlen; + size_t status; + size_t rlen; size_t counter; sprintf (command_buffer, "R%lu\n", (unsigned long) length); if (do_command (handle, command_buffer) == -1 - || (status = get_status (handle)) == -1) - return -1; + || (status = get_status (handle)) == SAFE_READ_ERROR) + return SAFE_READ_ERROR; for (counter = 0; counter < status; counter += rlen, buffer += rlen) { rlen = safe_read (READ_SIDE (handle), buffer, status - counter); - if (rlen <= 0) + if (rlen == SAFE_READ_ERROR || rlen == 0) { _rmt_shutdown (handle, EIO); - return -1; + return SAFE_READ_ERROR; } } return status; } -/*-------------------------------------------------------------------------. -| Write LENGTH bytes from BUFFER to remote tape connection HANDLE. Return | -| the number of bytes written on success, -1 on error. | -`-------------------------------------------------------------------------*/ - -ssize_t +/* Write LENGTH bytes from BUFFER to remote tape connection HANDLE. + Return the number of bytes written. */ +size_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%lu\n", (unsigned long) length); if (do_command (handle, command_buffer) == -1) - return -1; + return 0; pipe_handler = signal (SIGPIPE, SIG_IGN); - if (full_write (WRITE_SIDE (handle), buffer, length) == length) + written = full_write (WRITE_SIDE (handle), buffer, length); + signal (SIGPIPE, pipe_handler); + if (written == length) { - signal (SIGPIPE, pipe_handler); - return get_status (handle); + long int r = get_status (handle); + if (r < 0) + return 0; + if (r == length) + return length; + written = r; } /* Write error. */ - signal (SIGPIPE, pipe_handler); _rmt_shutdown (handle, EIO); - return -1; + return written; } -/*------------------------------------------------------------------------. -| Perform an imitation lseek operation on remote tape connection HANDLE. | -| Return the new file offset if successful, -1 if on error. | -`------------------------------------------------------------------------*/ - +/* 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) { @@ -580,6 +623,7 @@ rmt_lseek__ (int handle, off_t offset, int whence) uintmax_t u = offset < 0 ? - (uintmax_t) offset : (uintmax_t) offset; char *p = operand_buffer + sizeof operand_buffer; + *--p = 0; do *--p = '0' + (int) (u % 10); while ((u /= 10) != 0); @@ -602,18 +646,15 @@ rmt_lseek__ (int handle, off_t offset, int whence) 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 @@ -625,14 +666,15 @@ rmt_ioctl__ (int handle, int operation, char *argument) ? - (uintmax_t) ((struct mtop *) argument)->mt_count : (uintmax_t) ((struct mtop *) argument)->mt_count); char *p = operand_buffer + sizeof operand_buffer; - + + *--p = 0; 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 transfered in binary. */ + /* MTIOCTOP is the easy one. Nothing is transferred in binary. */ sprintf (command_buffer, "I%d\n%s\n", ((struct mtop *) argument)->mt_op, p); @@ -647,7 +689,7 @@ rmt_ioctl__ (int handle, int operation, char *argument) case MTIOCGET: { ssize_t status; - ssize_t counter; + size_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 @@ -661,9 +703,8 @@ rmt_ioctl__ (int handle, int operation, char *argument) for (; status > 0; status -= counter, argument += counter) { - counter = safe_read (READ_SIDE (handle), - argument, (size_t) status); - if (counter <= 0) + counter = safe_read (READ_SIDE (handle), argument, status); + if (counter == SAFE_READ_ERROR || counter == 0) { _rmt_shutdown (handle, EIO); return -1;