diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2004-09-06 13:49:42 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2004-09-06 13:49:42 +0000 |
commit | 754e65fc14e65028bc2aa035c8fff283aa708b85 (patch) | |
tree | a0cd837b43e479094146fe78fab2d8c059da068b | |
download | paxutils-754e65fc14e65028bc2aa035c8fff283aa708b85.tar.gz |
Initial revision
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | HISTORY | 21 | ||||
-rw-r--r-- | README | 10 | ||||
-rw-r--r-- | TODO | 13 | ||||
-rw-r--r-- | lib/DISTFILES | 3 | ||||
-rw-r--r-- | lib/rmt.h | 99 | ||||
-rw-r--r-- | lib/rtapelib.c | 740 | ||||
-rw-r--r-- | lib/system.h | 524 | ||||
-rw-r--r-- | m4/DISTFILES | 1 | ||||
-rw-r--r-- | m4/rmt.m4 | 60 | ||||
-rw-r--r-- | rmt/DISTFILES | 2 | ||||
-rw-r--r-- | rmt/Makefile.am | 13 | ||||
-rw-r--r-- | rmt/rmt.c | 577 |
13 files changed, 2072 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..8241e6f --- /dev/null +++ b/ChangeLog @@ -0,0 +1,9 @@ +2004-09-06 Sergey Poznyakoff <gray@Mirddin.farlep.net> + + Initial import. + + +Local Variables: +mode: change-log +version-control: never +End: @@ -0,0 +1,21 @@ +The paxutils project was started somewhere near 1994-06-30 (at least +this is the date of the earliest entry in the ChangeLog for version +2.4h). Unfortunately it has never reached mature status and was +abandoned after 1999-06-11 (again, as per its ChangeLog). Both +GNU tar and cpio, which were included in this project, have since +then developed independently, which brought all projects out +of synch with each other. + +On 2003-06-21 I have taken the maintenance of GNU tar, on 2003-11-01 +that of cpio, and finally on 2003-12-19 that of paxutils. By that time +tar needed lots of work, so I decided first to take care of it, in the +mean time preparing it and cpio for the future merging. The version 1.14 +of tar (the first stable version since 1999) was released on 2004-05-11, +the next maintenance release (1.14.90) followed it on 2004-09-02. +On 2004-09-06, after more than half a year's delay, Savannah finally +approved the project, and I started merging the three projects. + +More information about backgrounds of GNU paxutils and its history +is available at http://paxutils.progiciels-bpi.ca. + + @@ -0,0 +1,10 @@ +Currently the contents of this repository is used by GNU tar +and cpio build systems. See files HISTORY and TODO for +the information about background of GNU paxutils and its future +plans. + +Each subdirectory in the repository contains file DISTFILES that +lists the files to be exported from this subdirectory to the +above projects. The file consists of a list of filenames, each +on a separate line. Empty lines and comments (beginning with #) +are allowed. Comments must occupy a separate line. @@ -0,0 +1,13 @@ +This is the roadmap for GNU paxutils. The entries are in 'historical' +order, i.e. they must be executed from top to bottom. More entries +will be added soon. + +* Move all project-independent parts of tar and cpio there and +update both projects to use these. + +* Add general-purpose buffering functions for use by tar and cpio. + +* Create libpaxutils. + + + diff --git a/lib/DISTFILES b/lib/DISTFILES new file mode 100644 index 0000000..65bab61 --- /dev/null +++ b/lib/DISTFILES @@ -0,0 +1,3 @@ +rmt.h +rtapelib.c +system.h diff --git a/lib/rmt.h b/lib/rmt.h new file mode 100644 index 0000000..1fd2f29 --- /dev/null +++ b/lib/rmt.h @@ -0,0 +1,99 @@ +/* Definitions for communicating with a remote tape drive. + + Copyright (C) 1988, 1992, 1996, 1997, 2001, 2003, 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +extern char *rmt_command; +extern char *rmt_dev_name__; + +int rmt_open__ (const char *, int, int, const char *); +int rmt_close__ (int); +size_t rmt_read__ (int, char *, size_t); +size_t rmt_write__ (int, char *, size_t); +off_t rmt_lseek__ (int, off_t, int); +int rmt_ioctl__ (int, int, char *); + +extern bool force_local_option; + +/* A filename is remote if it contains a colon not preceded by a slash, + to take care of `/:/' which is a shorthand for `/.../<CELL-NAME>/fs' + on machines running OSF's Distributing Computing Environment (DCE) and + Distributed File System (DFS). However, when --force-local, a + filename is never remote. */ + +#define _remdev(dev_name) \ + (!force_local_option && (rmt_dev_name__ = strchr (dev_name, ':')) \ + && rmt_dev_name__ > (dev_name) \ + && ! memchr (dev_name, '/', rmt_dev_name__ - (dev_name))) + +#define _isrmt(fd) \ + ((fd) >= __REM_BIAS) + +#define __REM_BIAS (1 << 30) + +#ifndef O_CREAT +# define O_CREAT 01000 +#endif + +#define rmtopen(dev_name, oflag, mode, command) \ + (_remdev (dev_name) ? rmt_open__ (dev_name, oflag, __REM_BIAS, command) \ + : open (dev_name, oflag, mode)) + +#define rmtaccess(dev_name, amode) \ + (_remdev (dev_name) ? 0 : access (dev_name, amode)) + +#define rmtstat(dev_name, buffer) \ + (_remdev (dev_name) ? (errno = EOPNOTSUPP), -1 : stat (dev_name, buffer)) + +#define rmtcreat(dev_name, mode, command) \ + (_remdev (dev_name) \ + ? rmt_open__ (dev_name, 1 | O_CREAT, __REM_BIAS, command) \ + : creat (dev_name, mode)) + +#define rmtlstat(dev_name, muffer) \ + (_remdev (dev_name) ? (errno = EOPNOTSUPP), -1 : lstat (dev_name, buffer)) + +#define rmtread(fd, buffer, length) \ + (_isrmt (fd) ? rmt_read__ (fd - __REM_BIAS, buffer, length) \ + : safe_read (fd, buffer, length)) + +#define rmtwrite(fd, buffer, length) \ + (_isrmt (fd) ? rmt_write__ (fd - __REM_BIAS, buffer, length) \ + : full_write (fd, buffer, length)) + +#define rmtlseek(fd, offset, where) \ + (_isrmt (fd) ? rmt_lseek__ (fd - __REM_BIAS, offset, where) \ + : lseek (fd, offset, where)) + +#define rmtclose(fd) \ + (_isrmt (fd) ? rmt_close__ (fd - __REM_BIAS) : close (fd)) + +#define rmtioctl(fd, request, argument) \ + (_isrmt (fd) ? rmt_ioctl__ (fd - __REM_BIAS, request, argument) \ + : ioctl (fd, request, argument)) + +#define rmtdup(fd) \ + (_isrmt (fd) ? (errno = EOPNOTSUPP), -1 : dup (fd)) + +#define rmtfstat(fd, buffer) \ + (_isrmt (fd) ? (errno = EOPNOTSUPP), -1 : fstat (fd, buffer)) + +#define rmtfcntl(cd, command, argument) \ + (_isrmt (fd) ? (errno = EOPNOTSUPP), -1 : fcntl (fd, command, argument)) + +#define rmtisatty(fd) \ + (_isrmt (fd) ? 0 : isatty (fd)) diff --git a/lib/rtapelib.c b/lib/rtapelib.c new file mode 100644 index 0000000..0e499b6 --- /dev/null +++ b/lib/rtapelib.c @@ -0,0 +1,740 @@ +/* Functions for communicating with a remote tape drive. + + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* The man page rmt(8) for /etc/rmt documents the remote mag tape protocol + which rdump and rrestore use. Unfortunately, the man page is *WRONG*. + The author of the routines I'm including originally wrote his code just + based on the man page, and it didn't work, so he went to the rdump source + to figure out why. The only thing he had to change was to check for the + 'F' return code in addition to the 'E', and to separate the various + arguments with \n instead of a space. I personally don't think that this + is much of a problem, but I wanted to point it out. -- Arnold Robbins + + 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 -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. */ + +#ifndef EOPNOTSUPP +# if HAVE_NET_ERRNO_H +# include <net/errno.h> +# endif +# if HAVE_SYS_INET_H +# include <sys/inet.h> +# endif +# ifndef EOPNOTSUPP +# define EOPNOTSUPP EINVAL +# endif +#endif + +#include <signal.h> + +#if HAVE_NETDB_H +# include <netdb.h> +#endif + +#include <rmt.h> +#include <localedir.h> + +/* Exit status if exec errors. */ +#define EXIT_ON_EXEC_ERROR 128 + +/* FIXME: Size of buffers for reading and writing commands to rmt. */ +#define COMMAND_BUFFER_SIZE 64 + +#ifndef RETSIGTYPE +# define RETSIGTYPE void +#endif + +/* FIXME: Maximum number of simultaneous remote tape connections. */ +#define MAXUNIT 4 + +#define PREAD 0 /* read file descriptor from pipe() */ +#define PWRITE 1 /* write file descriptor from pipe() */ + +/* Return the parent's read side of remote tape connection Fd. */ +#define READ_SIDE(Fd) (from_remote[Fd][PREAD]) + +/* Return the parent's write side of remote tape connection Fd. */ +#define WRITE_SIDE(Fd) (to_remote[Fd][PWRITE]) + +/* The pipes for receiving data from remote tape drives. */ +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}}; + +char *rmt_command = DEFAULT_RMT_COMMAND; + +/* Temporary variable used by macros in rmt.h. */ +char *rmt_dev_name__; + +/* If true, always consider file names to be local, even if they contain + colons */ +bool force_local_option; + + + +/* Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE. */ +static void +_rmt_shutdown (int handle, int errno_value) +{ + close (READ_SIDE (handle)); + close (WRITE_SIDE (handle)); + READ_SIDE (handle) = -1; + WRITE_SIDE (handle) = -1; + 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. */ +static int +do_command (int handle, const char *buffer) +{ + /* Save the current pipe handler and try to make the request. */ + + 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. */ + + _rmt_shutdown (handle, EIO); + return -1; +} + +static char * +get_status_string (int handle, char *command_buffer) +{ + char *cursor; + int counter; + + /* Read the reply command line. */ + + for (counter = 0, cursor = command_buffer; + counter < COMMAND_BUFFER_SIZE; + counter++, cursor++) + { + if (safe_read (READ_SIDE (handle), cursor, 1) != 1) + { + _rmt_shutdown (handle, EIO); + return 0; + } + if (*cursor == '\n') + { + *cursor = '\0'; + break; + } + } + + if (counter == COMMAND_BUFFER_SIZE) + { + _rmt_shutdown (handle, EIO); + return 0; + } + + /* Check the return status. */ + + for (cursor = command_buffer; *cursor; cursor++) + if (*cursor != ' ') + break; + + if (*cursor == 'E' || *cursor == 'F') + { + /* Skip the error message line. */ + + /* FIXME: there is better to do than merely ignoring error messages + coming from the remote end. Translate them, too... */ + + { + char character; + + while (safe_read (READ_SIDE (handle), &character, 1) == 1) + if (character == '\n') + break; + } + + errno = atoi (cursor + 1); + + if (*cursor == 'F') + _rmt_shutdown (handle, errno); + + return 0; + } + + /* Check for mis-synced pipes. */ + + if (*cursor != 'A') + { + _rmt_shutdown (handle, EIO); + return 0; + } + + /* Got an `A' (success) response. */ + + return cursor + 1; +} + +/* 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); + if (status) + { + long int result = atol (status); + if (0 <= result) + return result; + errno = EIO; + } + return -1; +} + +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 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 (STDIN_FILENO); + int saved_stdout = dup (STDOUT_FILENO); + struct servent *rexecserv; + int result; + + /* When using cpio -o < filename, stdin is no longer the tty. But the + rexec subroutine reads the login and the passwd on stdin, to allow + remote execution of the command. So, reopen stdin and stdout on + /dev/tty before the rexec and give them back their original value + after. */ + + if (! freopen ("/dev/tty", "r", stdin)) + freopen ("/dev/null", "r", stdin); + 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, 0, rmt_command, 0); + if (fclose (stdin) == EOF) + error (0, errno, _("stdin")); + fdopen (saved_stdin, "r"); + if (fclose (stdout) == EOF) + error (0, errno, _("stdout")); + fdopen (saved_stdout, "w"); + + return result; +} + +#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 (); + } + +#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 *file_name, int open_mode, int bias, + const char *remote_shell) +{ + int remote_pipe_number; /* pseudo, biased file descriptor */ + 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 */ + + /* Find an unused pair of file descriptors. */ + + for (remote_pipe_number = 0; + remote_pipe_number < MAXUNIT; + remote_pipe_number++) + if (READ_SIDE (remote_pipe_number) == -1 + && WRITE_SIDE (remote_pipe_number) == -1) + break; + + if (remote_pipe_number == MAXUNIT) + { + errno = EMFILE; + return -1; + } + + /* Pull apart the system and device, and optional user. */ + + { + char *cursor; + + file_name_copy = xstrdup (file_name); + remote_host = file_name_copy; + remote_user = 0; + remote_file = 0; + + 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) + { + remote_user = remote_host; + *cursor = '\0'; + remote_host = cursor + 1; + } + break; + + case ':': + if (!remote_file) + { + *cursor = '\0'; + remote_file = cursor + 1; + } + break; + } + } + + /* FIXME: Should somewhat validate the decoding, here. */ + + if (remote_user && *remote_user == '\0') + remote_user = 0; + +#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 (file_name_copy); + errno = e; + return -1; + } + + WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number); + +#else /* not WITH_REXEC */ + { + const char *remote_shell_basename; + pid_t status; + + /* Identify the remote command to be executed. */ + + if (!remote_shell) + { +#ifdef REMOTE_SHELL + remote_shell = REMOTE_SHELL; +#else + free (file_name_copy); + errno = EIO; + return -1; +#endif + } + 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 (file_name_copy); + errno = e; + return -1; + } + + status = fork (); + if (status == -1) + { + int e = errno; + free (file_name_copy); + errno = e; + return -1; + } + + if (status == 0) + { + /* Child. */ + + 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 (STDOUT_FILENO); + dup (from_remote[remote_pipe_number][PWRITE]); + close (from_remote[remote_pipe_number][PREAD]); + close (from_remote[remote_pipe_number][PWRITE]); + + sys_reset_uid_gid (); + + if (remote_user) + execl (remote_shell, remote_shell_basename, remote_host, + "-l", remote_user, rmt_command, (char *) 0); + else + execl (remote_shell, remote_shell_basename, remote_host, + rmt_command, (char *) 0); + + /* Bad problems if we get here. */ + + /* In a previous version, _exit was used here instead of exit. */ + error (EXIT_ON_EXEC_ERROR, errno, _("Cannot execute remote shell")); + } + + /* Parent. */ + + close (from_remote[remote_pipe_number][PWRITE]); + close (to_remote[remote_pipe_number][PREAD]); + } +#endif /* not WITH_REXEC */ + + /* Attempt to open the tape device. */ + + { + 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) + { + int e = errno; + free (command_buffer); + free (file_name_copy); + _rmt_shutdown (remote_pipe_number, e); + return -1; + } + free (command_buffer); + } + + free (file_name_copy); + return remote_pipe_number + bias; +} + +/* Close remote tape connection HANDLE and shut down. Return 0 if + successful, -1 on error. */ +int +rmt_close__ (int handle) +{ + long int status; + + if (do_command (handle, "C\n") == -1) + return -1; + + status = get_status (handle); + _rmt_shutdown (handle, errno); + return status; +} + +/* 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]; + 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)) == 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 == SAFE_READ_ERROR || rlen == 0) + { + _rmt_shutdown (handle, EIO); + return SAFE_READ_ERROR; + } + } + + return status; +} + +/* 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 0; + + pipe_handler = signal (SIGPIPE, SIG_IGN); + written = full_write (WRITE_SIDE (handle), buffer, length); + signal (SIGPIPE, pipe_handler); + if (written == length) + { + long int r = get_status (handle); + if (r < 0) + return 0; + if (r == length) + return length; + written = r; + } + + /* Write error. */ + + _rmt_shutdown (handle, EIO); + return written; +} + +/* 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; + + *--p = 0; + 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); + + if (do_command (handle, command_buffer) == -1) + return -1; + + 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. */ +int +rmt_ioctl__ (int handle, int operation, char *argument) +{ + switch (operation) + { + default: + errno = EOPNOTSUPP; + return -1; + +#ifdef MTIOCTOP + case MTIOCTOP: + { + char command_buffer[COMMAND_BUFFER_SIZE]; + 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; + + *--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 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 get_status (handle); + } +#endif /* MTIOCTOP */ + +#ifdef MTIOCGET + case MTIOCGET: + { + ssize_t status; + 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 + fit in a long without any word alignment problems; i.e., the + whole struct is contiguous. NOTE - this is probably NOT a good + assumption. */ + + if (do_command (handle, "S") == -1 + || (status = get_status (handle), status == -1)) + return -1; + + for (; status > 0; status -= counter, argument += counter) + { + counter = safe_read (READ_SIDE (handle), argument, status); + if (counter == SAFE_READ_ERROR || counter == 0) + { + _rmt_shutdown (handle, EIO); + return -1; + } + } + + /* Check for byte position. mt_type (or mt_model) is a small integer + field (normally) so we will check its magnitude. If it is larger + than 256, we will assume that the bytes are swapped and go through + and reverse all the bytes. */ + + if (((struct mtget *) argument)->MTIO_CHECK_FIELD < 256) + return 0; + + for (counter = 0; counter < status; counter += 2) + { + char copy = argument[counter]; + + argument[counter] = argument[counter + 1]; + argument[counter + 1] = copy; + } + + return 0; + } +#endif /* MTIOCGET */ + + } +} diff --git a/lib/system.h b/lib/system.h new file mode 100644 index 0000000..0914195 --- /dev/null +++ b/lib/system.h @@ -0,0 +1,524 @@ +/* System dependent definitions for GNU tar. + + Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003, + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <alloca.h> + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(spec) /* empty */ +# endif +#endif + +#include <sys/types.h> +#include <ctype.h> + +/* IN_CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given + as an argument to <ctype.h> macros like `isspace'. */ +#if STDC_HEADERS +# define IN_CTYPE_DOMAIN(c) 1 +#else +# define IN_CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) +#endif + +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) +#define ISODIGIT(c) ((unsigned) (c) - '0' <= 7) +#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c)) +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) + +/* Declare string and memory handling routines. Take care that an ANSI + string.h and pre-ANSI memory.h might conflict, and that memory.h and + strings.h conflict on some systems. */ + +#if STDC_HEADERS || HAVE_STRING_H +# include <string.h> +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +#else +# include <strings.h> +# ifndef strchr +# define strchr index +# endif +# ifndef strrchr +# define strrchr rindex +# endif +# ifndef memcpy +# define memcpy(d, s, n) bcopy ((char const *) (s), (char *) (d), n) +# endif +# ifndef memcmp +# define memcmp(a, b, n) bcmp ((char const *) (a), (char const *) (b), n) +# endif +#endif + +/* Declare errno. */ + +#include <errno.h> +#ifndef errno +extern int errno; +#endif + +/* Declare open parameters. */ + +#if HAVE_FCNTL_H +# include <fcntl.h> +#else +# include <sys/file.h> +#endif + /* Pick only one of the next three: */ +#ifndef O_RDONLY +# define O_RDONLY 0 /* only allow read */ +#endif +#ifndef O_WRONLY +# define O_WRONLY 1 /* only allow write */ +#endif +#ifndef O_RDWR +# define O_RDWR 2 /* both are allowed */ +#endif +#ifndef O_ACCMODE +# define O_ACCMODE (O_RDONLY | O_RDWR | O_WRONLY) +#endif + /* The rest can be OR-ed in to the above: */ +#ifndef O_CREAT +# define O_CREAT 8 /* create file if needed */ +#endif +#ifndef O_EXCL +# define O_EXCL 16 /* file cannot already exist */ +#endif +#ifndef O_TRUNC +# define O_TRUNC 32 /* truncate file on open */ +#endif + /* MS-DOG forever, with my love! */ +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/* Declare file status routines and bits. */ + +#include <sys/stat.h> + +#if !HAVE_LSTAT && !defined lstat +# define lstat stat +#endif + +#if STX_HIDDEN && !_LARGE_FILES /* AIX */ +# ifdef stat +# undef stat +# endif +# define stat(file_name, buf) statx (file_name, buf, STATSIZE, STX_HIDDEN) +# ifdef lstat +# undef lstat +# endif +# define lstat(file_name, buf) statx (file_name, buf, STATSIZE, STX_HIDDEN | STX_LINK) +#endif + +#if STAT_MACROS_BROKEN +# undef S_ISBLK +# undef S_ISCHR +# undef S_ISCTG +# undef S_ISDIR +# undef S_ISFIFO +# undef S_ISLNK +# undef S_ISREG +# undef S_ISSOCK +#endif + +/* On MSDOS, there are missing things from <sys/stat.h>. */ +#if MSDOS +# define S_ISUID 0 +# define S_ISGID 0 +# define S_ISVTX 0 +#endif + +#ifndef S_ISDIR +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#ifndef S_ISREG +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISBLK +# ifdef S_IFBLK +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +# else +# define S_ISBLK(mode) 0 +# endif +#endif +#ifndef S_ISCHR +# ifdef S_IFCHR +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +# else +# define S_ISCHR(mode) 0 +# endif +#endif +#ifndef S_ISCTG +# ifdef S_IFCTG +# define S_ISCTG(mode) (((mode) & S_IFMT) == S_IFCTG) +# else +# define S_ISCTG(mode) 0 +# endif +#endif +#ifndef S_ISDOOR +# define S_ISDOOR(mode) 0 +#endif +#ifndef S_ISFIFO +# ifdef S_IFIFO +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +# else +# define S_ISFIFO(mode) 0 +# endif +#endif +#ifndef S_ISLNK +# ifdef S_IFLNK +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +# else +# define S_ISLNK(mode) 0 +# endif +#endif +#ifndef S_ISSOCK +# ifdef S_IFSOCK +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +# else +# define S_ISSOCK(mode) 0 +# endif +#endif + +#if !HAVE_MKFIFO && !defined mkfifo && defined S_IFIFO +# define mkfifo(file_name, mode) (mknod (file_name, (mode) | S_IFIFO, 0)) +#endif + +#ifndef S_ISUID +# define S_ISUID 0004000 +#endif +#ifndef S_ISGID +# define S_ISGID 0002000 +#endif +#ifndef S_ISVTX +# define S_ISVTX 0001000 +#endif +#ifndef S_IRUSR +# define S_IRUSR 0000400 +#endif +#ifndef S_IWUSR +# define S_IWUSR 0000200 +#endif +#ifndef S_IXUSR +# define S_IXUSR 0000100 +#endif +#ifndef S_IRGRP +# define S_IRGRP 0000040 +#endif +#ifndef S_IWGRP +# define S_IWGRP 0000020 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0000010 +#endif +#ifndef S_IROTH +# define S_IROTH 0000004 +#endif +#ifndef S_IWOTH +# define S_IWOTH 0000002 +#endif +#ifndef S_IXOTH +# define S_IXOTH 0000001 +#endif + +#define MODE_WXUSR (S_IWUSR | S_IXUSR) +#define MODE_R (S_IRUSR | S_IRGRP | S_IROTH) +#define MODE_RW (S_IWUSR | S_IWGRP | S_IWOTH | MODE_R) +#define MODE_RWX (S_IXUSR | S_IXGRP | S_IXOTH | MODE_RW) +#define MODE_ALL (S_ISUID | S_ISGID | S_ISVTX | MODE_RWX) + +/* Include <unistd.h> before any preprocessor test of _POSIX_VERSION. */ +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +# define SEEK_CUR 1 +#endif +#ifndef SEEK_END +# define SEEK_END 2 +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +/* Declare make device, major and minor. Since major is a function on + SVR4, we have to resort to GOT_MAJOR instead of just testing if + major is #define'd. */ + +#if MAJOR_IN_MKDEV +# include <sys/mkdev.h> +# define GOT_MAJOR +#endif + +#if MAJOR_IN_SYSMACROS +# include <sys/sysmacros.h> +# define GOT_MAJOR +#endif + +/* Some <sys/types.h> defines the macros. */ +#ifdef major +# define GOT_MAJOR +#endif + +#ifndef GOT_MAJOR +# if MSDOS +# define major(device) (device) +# define minor(device) (device) +# define makedev(major, minor) (((major) << 8) | (minor)) +# define GOT_MAJOR +# endif +#endif + +/* For HP-UX before HP-UX 8, major/minor are not in <sys/sysmacros.h>. */ +#ifndef GOT_MAJOR +# if defined(hpux) || defined(__hpux__) || defined(__hpux) +# include <sys/mknod.h> +# define GOT_MAJOR +# endif +#endif + +#ifndef GOT_MAJOR +# define major(device) (((device) >> 8) & 0xff) +# define minor(device) ((device) & 0xff) +# define makedev(major, minor) (((major) << 8) | (minor)) +#endif + +#undef GOT_MAJOR + +/* Declare wait status. */ + +#if HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(s) (((s) >> 8) & 0xff) +#endif +#ifndef WIFSIGNALED +# define WIFSIGNALED(s) (((s) & 0xffff) - 1 < (unsigned) 0xff) +#endif +#ifndef WTERMSIG +# define WTERMSIG(s) ((s) & 0x7f) +#endif + +/* FIXME: It is wrong to use BLOCKSIZE for buffers when the logical block + size is greater than 512 bytes; so ST_BLKSIZE code below, in preparation + for some cleanup in this area, later. */ + +/* Extract or fake data from a `struct stat'. ST_BLKSIZE gives the + optimal I/O blocksize for the file, in bytes. Some systems, like + Sequents, return st_blksize of 0 on pipes. */ + +#define DEFAULT_ST_BLKSIZE 512 + +#if !HAVE_ST_BLKSIZE +# define ST_BLKSIZE(statbuf) DEFAULT_ST_BLKSIZE +#else +# define ST_BLKSIZE(statbuf) \ + ((statbuf).st_blksize > 0 ? (statbuf).st_blksize : DEFAULT_ST_BLKSIZE) +#endif + +/* Extract or fake data from a `struct stat'. ST_NBLOCKS gives the + number of ST_NBLOCKSIZE-byte blocks in the file (including indirect blocks). + HP-UX counts st_blocks in 1024-byte units, + this loses when mixing HP-UX and BSD filesystems with NFS. AIX PS/2 + counts st_blocks in 4K units. */ + +#if !HAVE_ST_BLOCKS +# if defined(_POSIX_SOURCE) || !defined(BSIZE) +# define ST_NBLOCKS(statbuf) ((statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0)) +# else + off_t st_blocks (); +# define ST_NBLOCKS(statbuf) (st_blocks ((statbuf).st_size)) +# endif +#else +# define ST_NBLOCKS(statbuf) ((statbuf).st_blocks) +# if defined(hpux) || defined(__hpux__) || defined(__hpux) +# define ST_NBLOCKSIZE 1024 +# else +# if defined(_AIX) && defined(_I386) +# define ST_NBLOCKSIZE (4 * 1024) +# endif +# endif +#endif + +#ifndef ST_NBLOCKSIZE +#define ST_NBLOCKSIZE 512 +#endif + +/* This is a real challenge to properly get MTIO* symbols :-(. ISC uses + <sys/gentape.h>. SCO and BSDi uses <sys/tape.h>; BSDi also requires + <sys/tprintf.h> and <sys/device.h> for defining tp_dev and tpr_t. It + seems that the rest use <sys/mtio.h>, which itself requires other files, + depending on systems. Pyramid defines _IOW in <sgtty.h>, for example. */ + +#if HAVE_SYS_GENTAPE_H +# include <sys/gentape.h> +#else +# if HAVE_SYS_TAPE_H +# if HAVE_SYS_DEVICE_H +# include <sys/device.h> +# endif +# if HAVE_SYS_PARAM_H +# include <sys/param.h> +# endif +# if HAVE_SYS_BUF_H +# include <sys/buf.h> +# endif +# if HAVE_SYS_TPRINTF_H +# include <sys/tprintf.h> +# endif +# include <sys/tape.h> +# else +# if HAVE_SYS_MTIO_H +# include <sys/ioctl.h> +# if HAVE_SGTTY_H +# include <sgtty.h> +# endif +# if HAVE_SYS_IO_TRIOCTL_H +# include <sys/io/trioctl.h> +# endif +# include <sys/mtio.h> +# endif +# endif +#endif + +/* Declare standard functions. */ + +#if STDC_HEADERS +# include <stdlib.h> +#else +void *malloc (); +char *getenv (); +#endif + +#include <stdbool.h> +#include <stddef.h> + +#include <stdio.h> +#if !defined _POSIX_VERSION && MSDOS +# include <io.h> +#endif + +#if WITH_DMALLOC +# undef HAVE_DECL_VALLOC +# define DMALLOC_FUNC_CHECK +# include <dmalloc.h> +#endif + +#include <limits.h> + +#ifndef MB_LEN_MAX +# define MB_LEN_MAX 1 +#endif + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +/* These macros work even on ones'-complement hosts (!). + The extra casts work around common compiler bugs. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) +#define TYPE_MINIMUM(t) (TYPE_SIGNED (t) \ + ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) \ + : (t) 0) +#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t))) + +/* Bound on length of the string representing an integer value of type t. + Subtract one for the sign bit if t is signed; + 302 / 1000 is log10 (2) rounded up; + add one for integer division truncation; + add one more for a minus sign if t is signed. */ +#define INT_STRLEN_BOUND(t) \ + ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 \ + + 1 + TYPE_SIGNED (t)) + +#define UINTMAX_STRSIZE_BOUND (INT_STRLEN_BOUND (uintmax_t) + 1) + +/* Prototypes for external functions. */ + +#if HAVE_LOCALE_H +# include <locale.h> +#endif +#if !HAVE_SETLOCALE +# define setlocale(category, locale) /* empty */ +#endif + +#include <time.h> +#if defined(HAVE_SYS_TIME_H) && defined(TIME_WITH_SYS_TIME) +# include <sys/time.h> +#endif +#if ! HAVE_DECL_TIME +time_t time (); +#endif + +#ifdef HAVE_UTIME_H +# include <utime.h> +#endif + +/* Library modules. */ + +#include <dirname.h> +#include <error.h> +#include <savedir.h> +#include <unlocked-io.h> +#include <xalloc.h> + +#include <gettext.h> +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +#if MSDOS +# include <process.h> +# define SET_BINARY_MODE(arc) setmode(arc, O_BINARY) +# define ERRNO_IS_EACCES errno == EACCES +# define mkdir(file, mode) (mkdir) (file) +# define TTY_NAME "con" +# define sys_reset_uid_gid() +#else +# include <pwd.h> +# include <grp.h> +# define SET_BINARY_MODE(arc) +# define ERRNO_IS_EACCES 0 +# define TTY_NAME "/dev/tty" +# define sys_reset_uid_gid() \ + do { setuid (getuid ()); setgid (getgid ()); } while (0) +#endif + +#if XENIX +# include <sys/inode.h> +#endif diff --git a/m4/DISTFILES b/m4/DISTFILES new file mode 100644 index 0000000..4092bd2 --- /dev/null +++ b/m4/DISTFILES @@ -0,0 +1 @@ +rmt.m4 diff --git a/m4/rmt.m4 b/m4/rmt.m4 new file mode 100644 index 0000000..3b34cf9 --- /dev/null +++ b/m4/rmt.m4 @@ -0,0 +1,60 @@ + +AC_DEFUN([PU_RMT],[ + enable_rmt() { + if test $ac_cv_header_sys_mtio_h = yes; then + AC_CACHE_CHECK(for remote tape header files, pu_cv_header_rmt, + [AC_TRY_CPP([ +#if HAVE_SGTTY_H +# include <sgtty.h> +#endif +#include <sys/socket.h>], + pu_cv_header_rmt=yes, + pu_cv_header_rmt=no)]) + test $pu_cv_header_rmt = yes && PU_RMT_PROG='rmt' + AC_SUBST(PU_RMT_PROG) + fi + } + + AC_CHECK_HEADERS([sys/mtio.h]) + AC_CACHE_CHECK(which ioctl field to test for reversed bytes, + pu_cv_header_mtio_check_field, + [AC_EGREP_HEADER(mt_model, sys/mtio.h, + pu_cv_header_mtio_check_field=mt_model, + pu_cv_header_mtio_check_field=mt_type)]) + AC_DEFINE_UNQUOTED(MTIO_CHECK_FIELD, + $pu_cv_header_mtio_check_field, + [Define to mt_model (v.g., for DG/UX), else to mt_type.]) + + + AC_ARG_VAR([DEFAULT_RMT_DIR], + [Define full file name of the directory where to install `rmt'. (default: $(libexecdir))]) + if test "x$DEFAULT_RMT_DIR" != x; then + DEFAULT_RMT_COMMAND=$DEFAULT_RMT_DIR/rmt + else + DEFAULT_RMT_DIR='$(libexecdir)' + fi + + AC_MSG_CHECKING([whether to build rmt]) + AC_ARG_WITH([rmt], + AC_HELP_STRING([--with-rmt=FILE], + [Use FILE as the default `rmt' program. Do not build included copy of `rmt'.]), + [case $withval in + yes|no) AC_MSG_ERROR([Invalid argument to --with-rmt]);; + /*) DEFAULT_RMT_COMMAND=$withval + AC_MSG_RESULT([no, use $withval instead]);; + *) AC_MSG_ERROR([Argument to --with-rmt must be an absolute file name]);; + esac], + [AC_MSG_RESULT([yes]) + enable_rmt + if test "$PU_RMT_PROG" = ""; then + AC_MSG_WARN([not building rmt, required header files are missing]) + fi]) + + AC_SUBST(DEFAULT_RMT_COMMAND) + if test "x$DEFAULT_RMT_COMMAND" != x; then + AC_DEFINE_UNQUOTED(DEFAULT_RMT_COMMAND, "$DEFAULT_RMT_COMMAND", + [Define full file name of rmt program.]) + fi +]) + + diff --git a/rmt/DISTFILES b/rmt/DISTFILES new file mode 100644 index 0000000..f0e0c34 --- /dev/null +++ b/rmt/DISTFILES @@ -0,0 +1,2 @@ +Makefile.am +rmt.c diff --git a/rmt/Makefile.am b/rmt/Makefile.am new file mode 100644 index 0000000..c6ba2e3 --- /dev/null +++ b/rmt/Makefile.am @@ -0,0 +1,13 @@ +rmtdir=$(DEFAULT_RMT_DIR) +rmt_PROGRAMS = @PU_RMT_PROG@ +EXTRA_PROGRAMS = rmt + +rmt_SOURCES = rmt.c + +INCLUDES = -I$(top_srcdir)/lib -I../ -I../lib + +LDADD = ../lib/lib$(PACKAGE).a $(LIBINTL) + +rmt_LDADD = $(LDADD) $(LIB_SETSOCKOPT) + +rmt.o: ../lib/localedir.h diff --git a/rmt/rmt.c b/rmt/rmt.c new file mode 100644 index 0000000..9ac327f --- /dev/null +++ b/rmt/rmt.c @@ -0,0 +1,577 @@ +/* Remote connection server. + + Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 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 the + Free Software Foundation; either version 2, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Copyright (C) 1983 Regents of the University of California. + All rights reserved. + + Redistribution and use in source and binary forms are permitted provided + that the above copyright notice and this paragraph are duplicated in all + such forms and that any documentation, advertising materials, and other + materials related to such distribution and use acknowledge that the + software was developed by the University of California, Berkeley. The + name of the University may not be used to endorse or promote products + derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ + +#include "system.h" +#include <localedir.h> +#include <safe-read.h> +#include <full-write.h> + +#include <getopt.h> +#include <sys/socket.h> + +#ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif + +/* Maximum size of a string from the requesting program. */ +#define STRING_SIZE 64 + +/* Name of executing program. */ +const char *program_name; + +/* File descriptor of the tape device, or negative if none open. */ +static int tape = -1; + +/* Buffer containing transferred data, and its allocated size. */ +static char *record_buffer; +static size_t allocated_size; + +/* Buffer for constructing the reply. */ +static char reply_buffer[BUFSIZ]; + +/* Debugging tools. */ + +static FILE *debug_file; + +#define DEBUG(File) \ + if (debug_file) fprintf(debug_file, File) + +#define DEBUG1(File, Arg) \ + if (debug_file) fprintf(debug_file, File, Arg) + +#define DEBUG2(File, Arg1, Arg2) \ + if (debug_file) fprintf(debug_file, File, Arg1, Arg2) + +/* Return an error string, given an error number. */ +#if HAVE_STRERROR +# ifndef strerror +char *strerror (); +# endif +#else +static char * +private_strerror (int errnum) +{ + extern char *sys_errlist[]; + extern int sys_nerr; + + if (errnum > 0 && errnum <= sys_nerr) + return _(sys_errlist[errnum]); + return _("Unknown system error"); +} +# define strerror private_strerror +#endif + +static void +report_error_message (const char *string) +{ + DEBUG1 ("rmtd: E 0 (%s)\n", string); + + sprintf (reply_buffer, "E0\n%s\n", string); + full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); +} + +static void +report_numbered_error (int num) +{ + DEBUG2 ("rmtd: E %d (%s)\n", num, strerror (num)); + + sprintf (reply_buffer, "E%d\n%s\n", num, strerror (num)); + full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); +} + +static void +get_string (char *string) +{ + int counter; + + for (counter = 0; ; counter++) + { + if (safe_read (STDIN_FILENO, string + counter, 1) != 1) + exit (EXIT_SUCCESS); + + if (string[counter] == '\n' || counter == STRING_SIZE - 1) + break; + } + string[counter] = '\0'; +} + +static void +prepare_input_buffer (int fd, size_t size) +{ + if (size <= allocated_size) + return; + + if (record_buffer) + free (record_buffer); + + record_buffer = malloc (size); + + if (! record_buffer) + { + DEBUG (_("rmtd: Cannot allocate buffer space\n")); + + report_error_message (N_("Cannot allocate buffer space")); + exit (EXIT_FAILURE); /* exit status used to be 4 */ + } + + allocated_size = size; + +#ifdef SO_RCVBUF + if (0 <= fd) + { + int isize = size < INT_MAX ? size : INT_MAX; + while (setsockopt (fd, SOL_SOCKET, SO_RCVBUF, + (char *) &isize, sizeof isize) + && 1024 < isize) + isize >>= 1; + } +#endif +} + +/* Decode OFLAG_STRING, which represents the 2nd argument to `open'. + OFLAG_STRING should contain an optional integer, followed by an optional + symbolic representation of an open flag using only '|' to separate its + components (e.g. "O_WRONLY|O_CREAT|O_TRUNC"). Prefer the symbolic + representation if available, falling back on the numeric + representation, or to zero if both formats are absent. + + This function should be the inverse of encode_oflag. The numeric + representation is not portable from one host to another, but it is + for backward compatibility with old-fashioned clients that do not + emit symbolic open flags. */ + +static int +decode_oflag (char const *oflag_string) +{ + char *oflag_num_end; + int numeric_oflag = strtol (oflag_string, &oflag_num_end, 10); + int symbolic_oflag = 0; + + oflag_string = oflag_num_end; + while (ISSPACE ((unsigned char) *oflag_string)) + oflag_string++; + + do + { + struct name_value_pair { char const *name; int value; }; + static struct name_value_pair const table[] = + { +#ifdef O_APPEND + {"APPEND", O_APPEND}, +#endif + {"CREAT", O_CREAT}, +#ifdef O_DSYNC + {"DSYNC", O_DSYNC}, +#endif + {"EXCL", O_EXCL}, +#ifdef O_LARGEFILE + {"LARGEFILE", O_LARGEFILE}, /* LFS extension for opening large files */ +#endif +#ifdef O_NOCTTY + {"NOCTTY", O_NOCTTY}, +#endif +#ifdef O_NONBLOCK + {"NONBLOCK", O_NONBLOCK}, +#endif + {"RDONLY", O_RDONLY}, + {"RDWR", O_RDWR}, +#ifdef O_RSYNC + {"RSYNC", O_RSYNC}, +#endif +#ifdef O_SYNC + {"SYNC", O_SYNC}, +#endif + {"TRUNC", O_TRUNC}, + {"WRONLY", O_WRONLY} + }; + struct name_value_pair const *t; + size_t s; + + if (*oflag_string++ != 'O' || *oflag_string++ != '_') + return numeric_oflag; + + for (t = table; + (strncmp (oflag_string, t->name, s = strlen (t->name)) != 0 + || (oflag_string[s] + && strchr ("ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", + oflag_string[s]))); + t++) + if (t == table + sizeof table / sizeof *table - 1) + return numeric_oflag; + + symbolic_oflag |= t->value; + oflag_string += s; + } + while (*oflag_string++ == '|'); + + return symbolic_oflag; +} + +static struct option const long_opts[] = +{ + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} +}; + +static void usage (int) __attribute__ ((noreturn)); + +static void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("\ +Usage: %s [OPTION]\n\ +Manipulate a tape drive, accepting commands from a remote process.\n\ +\n\ + --version Output version info.\n\ + --help Output this help.\n"), + program_name); + printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + } + + exit (status); +} + +int +main (int argc, char *const *argv) +{ + char command; + size_t status; + + /* FIXME: Localization is meaningless, unless --help and --version are + locally used. Localization would be best accomplished by the calling + tar, on messages found within error packets. */ + + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + switch (getopt_long (argc, argv, "", long_opts, NULL)) + { + default: + usage (EXIT_FAILURE); + + case 'h': + usage (EXIT_SUCCESS); + + case 'v': + { + printf ("rmt (%s) %s\n%s\n", PACKAGE_NAME, PACKAGE_VERSION, + "Copyright (C) 2004 Free Software Foundation, Inc."); + puts (_("\ +This program comes with NO WARRANTY, to the extent permitted by law.\n\ +You may redistribute it under the terms of the GNU General Public License;\n\ +see the file named COPYING for details.")); + } + return EXIT_SUCCESS; + + case -1: + break; + } + + if (optind < argc) + { + if (optind != argc - 1) + usage (EXIT_FAILURE); + debug_file = fopen (argv[optind], "w"); + if (debug_file == 0) + { + report_numbered_error (errno); + return EXIT_FAILURE; + } + setbuf (debug_file, 0); + } + +top: + errno = 0; + status = 0; + if (safe_read (STDIN_FILENO, &command, 1) != 1) + return EXIT_SUCCESS; + + switch (command) + { + /* FIXME: Maybe 'H' and 'V' for --help and --version output? */ + + case 'O': + { + char device_string[STRING_SIZE]; + char oflag_string[STRING_SIZE]; + + get_string (device_string); + get_string (oflag_string); + DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string); + + if (tape >= 0) + close (tape); + + tape = open (device_string, decode_oflag (oflag_string), MODE_RW); + if (tape < 0) + goto ioerror; + goto respond; + } + + case 'C': + { + char device_string[STRING_SIZE]; + + get_string (device_string); /* discard */ + DEBUG ("rmtd: C\n"); + + if (close (tape) < 0) + goto ioerror; + tape = -1; + goto respond; + } + + case 'L': + { + char count_string[STRING_SIZE]; + char position_string[STRING_SIZE]; + off_t count = 0; + int negative; + int whence; + char *p; + + get_string (count_string); + get_string (position_string); + DEBUG2 ("rmtd: L %s %s\n", count_string, position_string); + + /* Parse count_string, taking care to check for overflow. + We can't use standard functions, + since off_t might be longer than long. */ + + for (p = count_string; *p == ' ' || *p == '\t'; p++) + continue; + + negative = *p == '-'; + p += negative || *p == '+'; + + for (;;) + { + int digit = *p++ - '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)) + { + report_error_message (N_("Seek offset out of range")); + return EXIT_FAILURE; + } + count = nc; + } + } + + switch (atoi (position_string)) + { + case 0: whence = SEEK_SET; break; + case 1: whence = SEEK_CUR; break; + case 2: whence = SEEK_END; break; + default: + report_error_message (N_("Seek direction out of range")); + return EXIT_FAILURE; + } + count = lseek (tape, count, whence); + if (count < 0) + goto ioerror; + + /* Convert count back to string for reply. + We can't use sprintf, since off_t might be longer than long. */ + p = count_string + sizeof count_string; + *--p = '\0'; + do + *--p = '0' + (int) (count % 10); + while ((count /= 10) != 0); + + DEBUG1 ("rmtd: A %s\n", p); + + sprintf (reply_buffer, "A%s\n", p); + full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + goto top; + } + + case 'W': + { + char count_string[STRING_SIZE]; + size_t size; + size_t counter; + + get_string (count_string); + size = atol (count_string); + DEBUG1 ("rmtd: W %s\n", count_string); + + prepare_input_buffer (STDIN_FILENO, size); + for (counter = 0; counter < size; counter += status) + { + status = safe_read (STDIN_FILENO, &record_buffer[counter], + size - counter); + if (status == SAFE_READ_ERROR || status == 0) + { + DEBUG (_("rmtd: Premature eof\n")); + + report_error_message (N_("Premature end of file")); + return EXIT_FAILURE; /* exit status used to be 2 */ + } + } + status = full_write (tape, record_buffer, size); + if (status != size) + goto ioerror; + goto respond; + } + + case 'R': + { + char count_string[STRING_SIZE]; + size_t size; + + get_string (count_string); + DEBUG1 ("rmtd: R %s\n", count_string); + + size = atol (count_string); + prepare_input_buffer (-1, size); + status = safe_read (tape, record_buffer, size); + if (status == SAFE_READ_ERROR) + goto ioerror; + sprintf (reply_buffer, "A%lu\n", (unsigned long int) status); + full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + full_write (STDOUT_FILENO, record_buffer, status); + goto top; + } + + case 'I': + { + char operation_string[STRING_SIZE]; + char count_string[STRING_SIZE]; + + get_string (operation_string); + get_string (count_string); + DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string); + +#ifdef MTIOCTOP + { + struct mtop mtop; + const char *p; + off_t count = 0; + int negative; + + /* Parse count_string, taking care to check for overflow. + We can't use standard functions, + since off_t might be longer than long. */ + + for (p = count_string; *p == ' ' || *p == '\t'; p++) + continue; + + negative = *p == '-'; + p += negative || *p == '+'; + + for (;;) + { + int digit = *p++ - '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)) + { + report_error_message (N_("Seek offset out of range")); + return EXIT_FAILURE; + } + count = nc; + } + } + + mtop.mt_count = count; + if (mtop.mt_count != count) + { + report_error_message (N_("Seek offset out of range")); + return EXIT_FAILURE; + } + mtop.mt_op = atoi (operation_string); + + if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0) + goto ioerror; + } +#endif + goto respond; + } + + case 'S': /* status */ + { + DEBUG ("rmtd: S\n"); + +#ifdef MTIOCGET + { + struct mtget operation; + + if (ioctl (tape, MTIOCGET, (char *) &operation) < 0) + goto ioerror; + status = sizeof operation; + sprintf (reply_buffer, "A%ld\n", (long) status); + full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + full_write (STDOUT_FILENO, (char *) &operation, sizeof operation); + } +#endif + goto top; + } + + default: + DEBUG1 (_("rmtd: Garbage command %c\n"), command); + + report_error_message (N_("Garbage command")); + return EXIT_FAILURE; /* exit status used to be 3 */ + } + +respond: + DEBUG1 ("rmtd: A %ld\n", (long) status); + + sprintf (reply_buffer, "A%ld\n", (long) status); + full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + goto top; + +ioerror: + report_numbered_error (errno); + goto top; +} |