X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Frtapelib.c;h=2116cb655295df789716c4249fdd9a4609d56f07;hb=7be178a4d4c884e0ddd27ce93e04a00411f4332a;hp=7361bb245cb5217c9f5bae8260d961142835e2ef;hpb=3c08ec43171c8538a2ef0f75b3455cf83f1f75f6;p=chaz%2Ftar diff --git a/src/rtapelib.c b/src/rtapelib.c index 7361bb2..2116cb6 100644 --- a/src/rtapelib.c +++ b/src/rtapelib.c @@ -1,5 +1,5 @@ /* 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 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 @@ -27,11 +27,14 @@ 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 "basename.h" +#include "safe-read.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. */ @@ -112,14 +115,14 @@ _rmt_shutdown (int handle, int errno_value) static int do_command (int handle, const char *buffer) { - int length; + 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 (write (WRITE_SIDE (handle), buffer, (size_t) length) == length) + if (full_write (WRITE_SIDE (handle), buffer, length) == length) { signal (SIGPIPE, pipe_handler); return 0; @@ -132,15 +135,9 @@ do_command (int handle, const char *buffer) 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; @@ -150,10 +147,10 @@ get_status (int handle) 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') { @@ -165,7 +162,7 @@ get_status (int handle) if (counter == COMMAND_BUFFER_SIZE) { _rmt_shutdown (handle, EIO); - return -1; + return 0; } /* Check the return status. */ @@ -186,7 +183,7 @@ get_status (int handle) { char character; - while (read (READ_SIDE (handle), &character, 1) == 1) + while (safe_read (READ_SIDE (handle), &character, 1) == 1) if (character == '\n') break; } @@ -194,7 +191,7 @@ get_status (int handle) if (*cursor == 'F') _rmt_shutdown (handle, errno); - return -1; + return 0; } /* Check for mis-synced pipes. */ @@ -202,20 +199,75 @@ get_status (int handle) 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; + } + } + + 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 NULL, use the current username. | +| 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 | @@ -227,8 +279,8 @@ get_status (int handle) 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; @@ -238,16 +290,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, "/etc/rmt", 0); if (fclose (stdin) == EOF) error (0, errno, _("stdin")); fdopen (saved_stdin, "r"); @@ -258,13 +309,54 @@ _rmt_rexec (char *host, char *user) return result; } -#endif /* HAVE_NETDB_H */ +#endif /* WITH_REXEC */ + +/* 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 (); + } + + if (oflag & O_APPEND) strcat (buf, "|O_APPEND"); + 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 overriden. On error, return -1. | +| plus BIAS. REMOTE_SHELL may be overridden. On error, return -1. | `------------------------------------------------------------------------*/ int @@ -298,8 +390,8 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) 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) @@ -307,6 +399,13 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) 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) { @@ -329,25 +428,27 @@ 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 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. */ @@ -356,30 +457,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); + 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; } @@ -387,12 +488,12 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) { /* 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]); @@ -420,21 +521,26 @@ rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell) 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); @@ -464,22 +570,22 @@ rmt_close__ (int handle) | Return the number of bytes read on success, -1 on error. | `-------------------------------------------------------------------------*/ -int -rmt_read__ (int handle, char *buffer, unsigned int length) +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; @@ -494,18 +600,18 @@ rmt_read__ (int handle, char *buffer, unsigned int length) | the number of bytes written on success, -1 on error. | `-------------------------------------------------------------------------*/ -int -rmt_write__ (int handle, char *buffer, unsigned int length) +ssize_t +rmt_write__ (int handle, char *buffer, size_t length) { char command_buffer[COMMAND_BUFFER_SIZE]; RETSIGTYPE (*pipe_handler) (); - 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) + if (full_write (WRITE_SIDE (handle), buffer, length) == length) { signal (SIGPIPE, pipe_handler); return get_status (handle); @@ -523,16 +629,34 @@ rmt_write__ (int handle, char *buffer, unsigned int length) | Return the new file offset if successful, -1 if on error. | `------------------------------------------------------------------------*/ -long +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); } /*-----------------------------------------------------------------------. @@ -553,16 +677,25 @@ rmt_ioctl__ (int handle, int operation, char *argument) 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 */ @@ -570,8 +703,8 @@ rmt_ioctl__ (int handle, int operation, char *argument) #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 @@ -585,7 +718,7 @@ rmt_ioctl__ (int handle, int operation, char *argument) 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);