From 4154f020dc9aaba332cad1028bca4f3b6024aa08 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sat, 14 May 2005 10:55:01 +0000 Subject: New file --- paxlib/.cvsignore | 4 + paxlib/Makefile.am | 43 +++ paxlib/pax.h | 69 +++++ paxlib/paxbuf.c | 332 +++++++++++++++++++++++ paxlib/paxbuf.h | 65 +++++ paxlib/rtape.c | 761 +++++++++++++++++++++++++++++++++++++++++++++++++++++ paxlib/tar.h | 279 ++++++++++++++++++++ paxlib/tarbuf.c | 218 +++++++++++++++ paxlib/tardef.h | 54 ++++ 9 files changed, 1825 insertions(+) create mode 100644 paxlib/.cvsignore create mode 100644 paxlib/Makefile.am create mode 100644 paxlib/pax.h create mode 100644 paxlib/paxbuf.c create mode 100644 paxlib/paxbuf.h create mode 100644 paxlib/rtape.c create mode 100644 paxlib/tar.h create mode 100644 paxlib/tarbuf.c create mode 100644 paxlib/tardef.h (limited to 'paxlib') diff --git a/paxlib/.cvsignore b/paxlib/.cvsignore new file mode 100644 index 0000000..9c613e1 --- /dev/null +++ b/paxlib/.cvsignore @@ -0,0 +1,4 @@ +.deps +Makefile +Makefile.in +localedir.h diff --git a/paxlib/Makefile.am b/paxlib/Makefile.am new file mode 100644 index 0000000..fdf9538 --- /dev/null +++ b/paxlib/Makefile.am @@ -0,0 +1,43 @@ +# This file is part of GNU paxutils +# +# Copyright (C) 2005 Free Software Foundation, Inc. +# +# Written by Sergey Poznyakoff +# +# GNU paxutils 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. +# +# GNU paxutils 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 GNU paxutils; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +INCLUDES = -I$(top_srcdir)/lib -I../ -I../lib -I../pax + +noinst_LIBRARIES = libpax.a +noinst_HEADERS = tar.h paxbuf.h pax.h + +libpax_a_SOURCES = \ + paxbuf.c\ + tarbuf.c\ + rtape.c + +localedir = $(datadir)/locale + +DISTCLEANFILES = localedir.h +localedir.h : Makefile + echo '#define LOCALEDIR "$(localedir)"' >$@ + echo "#ifndef DEFAULT_RMT_COMMAND" >> $@ + echo "# define DEFAULT_RMT_COMMAND \"$(DEFAULT_RMT_DIR)/`echo rmt | sed '$(transform)'`$(EXEEXT)\"" >> $@ + echo "#endif" >> $@ + +rtapelib.o: localedir.h + + + diff --git a/paxlib/pax.h b/paxlib/pax.h new file mode 100644 index 0000000..ce98636 --- /dev/null +++ b/paxlib/pax.h @@ -0,0 +1,69 @@ +/* This file is part of GNU paxutils + + Copyright (C) 2005 Free Software Foundation, Inc. + + Written by Sergey Poznyakoff + + GNU paxutils 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. + + GNU paxutils 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 GNU paxutils; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +struct tar_stat_info +{ + char *orig_file_name; /* name of file read from the archive header */ + char *file_name; /* name of file for the current archive entry + after being normalized. */ + int had_trailing_slash; /* nonzero if the current archive entry had a + trailing slash before it was normalized. */ + char *link_name; /* name of link for the current archive entry. */ + + unsigned int devminor; /* device minor number */ + unsigned int devmajor; /* device major number */ + char *uname; /* user name of owner */ + char *gname; /* group name of owner */ + struct stat stat; /* regular filesystem stat */ + + /* Nanosecond parts of file timestamps (if available) */ + unsigned long atime_nsec; + unsigned long mtime_nsec; + unsigned long ctime_nsec; + + off_t archive_file_size; /* Size of file as stored in the archive. + Equals stat.st_size for non-sparse files */ + + bool is_sparse; /* Is the file sparse */ + + size_t sparse_map_avail; /* Index to the first unused element in + sparse_map array. Zero if the file is + not sparse */ + size_t sparse_map_size; /* Size of the sparse map */ + struct sp_array *sparse_map; +}; + + +/* Remote device manipulations */ +int rmt_open (const char *file_name, int open_mode, int bias, + const char *remote_shell, const char *rmt_command); +int rmt_close (int handle); +size_t rmt_read (int handle, char *buffer, size_t length); +size_t rmt_write (int handle, char *buffer, size_t length); +off_t rmt_lseek (int handle, off_t offset, int whence); +int rmt_ioctl (int handle, int operation, char *argument); + + +/* Tar-specific functions */ +void tar_archive_create (paxbuf_t *pbuf, const char *filename, + int remote, int mode, size_t bfactor); +void tar_set_rmt (paxbuf_t pbuf, const char *rmt); +void tar_set_rsh (paxbuf_t pbuf, const char *rsh); + diff --git a/paxlib/paxbuf.c b/paxlib/paxbuf.c new file mode 100644 index 0000000..b39fed7 --- /dev/null +++ b/paxlib/paxbuf.c @@ -0,0 +1,332 @@ +/* This file is part of GNU paxutils + + Copyright (C) 2005 Free Software Foundation, Inc. + + Written by Sergey Poznyakoff + + GNU paxutils 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. + + GNU paxutils 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 GNU paxutils; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include + +/* PAX buffer structure */ +struct pax_buffer +{ + size_t record_size; /* Size of a record, bytes */ + size_t record_level; /* Number of bytes stored in the record */ + size_t pos; /* Current position in buffer */ + char *record; /* Record buffer, record_size bytes long */ + + int status; /* Return code from the latest I/O */ + + /* I/O functions */ + paxbuf_io_fp writer; /* Writes data */ + paxbuf_io_fp reader; /* Reads data */ + paxbuf_seek_fp seek; /* Seeks the underlying transport layer */ + /* Terminal functions */ + paxbuf_term_fp open; /* Open a new volume */ + paxbuf_term_fp close; /* Close the existing volume */ + paxbuf_destroy_fp destroy; /* Destroy the closure data */ + /* Other callbacks */ + paxbuf_wrapper_fp wrapper; /* Called when writer or reader returns EOF */ + + void *closure; /* Implementation-specific data */ + int mode; /* Working mode */ +}; + + +/* Default callbacks. Do nothing useful, except bailing out */ + +static void +noinit (const char *name) +{ + fprintf (stderr, + _("INTERNAL ERROR: %s is not initialized. Please, report.\n"), + name); + exit (1); +} + +static pax_io_status_t +default_reader (void *closure, void *data, size_t size, size_t *ret_size) +{ + noinit ("pax_buffer.reader"); + return pax_io_failure; +} + +static pax_io_status_t +default_writer (void *closure, void *data, size_t size, size_t *ret_size) +{ + noinit ("pax_buffer.writer"); + return pax_io_failure; +} + +static int +default_seek (void *closure, off_t offset) +{ + noinit ("pax_buffer.seek"); + return -1; +} + +static int +default_open (void *closure, int mode) +{ + noinit ("pax_buffer.open"); + return -1; +} + +static int +default_close (void *closure, int mode) +{ + noinit ("pax_buffer.close"); + return -1; +} + +static int +default_destroy (void *closure) +{ + noinit ("pax_buffer.destroy"); + return -1; +} + +static int +default_wrapper (void *closure) +{ + noinit ("pax_buffer.wrapper"); + return -1; +} + +static const char * +default_error (void *closure) +{ + return strerror (errno); +} + + +/* Interface funtions */ + +/* 1. Initialize/destroy */ + +int +paxbuf_create (paxbuf_t *pbuf, int mode, void *closure, size_t record_size) +{ + paxbuf_t buf; + + buf = malloc (sizeof *buf); + if (!buf) + return ENOMEM; + buf->record = malloc (record_size); + if (!buf->record) + { + free (buf); + return ENOMEM; + } + + buf->record_size = record_size; + buf->record_level = 0; + buf->closure = closure; + buf->mode = mode; + + paxbuf_set_io (buf, default_reader, default_writer, default_seek); + paxbuf_set_term (buf, default_open, default_close, default_destroy); + paxbuf_set_wrapper (buf, default_wrapper); + + *pbuf = buf; + return 0; +} + +void +paxbuf_destroy (paxbuf_t *pbuf) +{ + paxbuf_t buf = *pbuf; + free (buf->record); + if (buf->destroy) + buf->destroy (buf->closure); + free (buf); + *pbuf = NULL; +} + +void +paxbuf_set_io (paxbuf_t buf, + paxbuf_io_fp rd, paxbuf_io_fp wr, paxbuf_seek_fp seek) +{ + buf->writer = wr; + buf->reader = rd; + buf->seek = seek; +} + +void +paxbuf_set_term (paxbuf_t buf, + paxbuf_term_fp open, paxbuf_term_fp close, + paxbuf_destroy_fp destroy) +{ + buf->open = open; + buf->close = close; + buf->destroy = destroy; +} + +void +paxbuf_set_wrapper (paxbuf_t buf, paxbuf_wrapper_fp wrap) +{ + buf->wrapper = wrap; +} + + +/* 2. I/O operations and seek */ + +static pax_io_status_t +fill_buffer (paxbuf_t buf) +{ + pax_io_status_t status = pax_io_success; + + buf->record_level = 0; + do + { + size_t s = 0; + + status = buf->reader (buf->closure, buf->record + buf->record_level, + buf->record_size - buf->record_level, &s); + buf->record_level += s; + } + while ((status == pax_io_success && buf->record_level < buf->record_size) + || (status == pax_io_eof + && buf->wrapper + && buf->wrapper (buf->closure) == 0)); + + buf->pos = 0; + return status; +} + +static pax_io_status_t +flush_buffer (paxbuf_t buf) +{ + pax_io_status_t status = pax_io_success; + + buf->record_level = 0; + do + { + size_t s = 0; + status = buf->writer (buf->closure, buf->record + buf->record_level, + buf->record_size - buf->record_level, &s); + buf->record_level += s; + } + while ((status == pax_io_success && buf->record_level < buf->record_size) + || (status == pax_io_eof + && buf->wrapper + && buf->wrapper (buf->closure) == 0)); + buf->pos = 0; + return status; +} + +pax_io_status_t +paxbuf_read (paxbuf_t buf, char *data, size_t size, size_t *rsize) +{ + pax_io_status_t status = pax_io_success; + + *rsize = 0; + while (size && status == pax_io_success) + { + size_t s; + + if (buf->pos == buf->record_level) + { + status = fill_buffer (buf); + if (status == pax_io_failure) + break; + } + s = buf->record_level - buf->pos; + if (s > size) + s = size; + memcpy (data, buf->record + buf->pos, s); + data += s; + buf->pos += s; + size -= s; + *rsize += s; + } + return status; +} + +pax_io_status_t +paxbuf_write (paxbuf_t buf, char *data, size_t size, size_t *wsize) +{ + pax_io_status_t status = pax_io_success; + + *wsize = 0; + while (size && status == pax_io_success) + { + size_t s; + + if (buf->pos == buf->record_size) + { + status = flush_buffer (buf); + if (status == pax_io_failure) + break; + } + s = buf->record_size - buf->pos; + if (s > size) + s = size; + memcpy (buf->record + buf->pos, data, s); + data += s; + buf->pos += s; + size -= s; + *wsize += s; + } + return status; +} + +int +paxbuf_seek (paxbuf_t buf, off_t offset) +{ + /* FIXME */ + return buf->seek (buf->closure, offset); +} + + +/* 3. Open/close */ +int +paxbuf_open (paxbuf_t buf) +{ + return buf->open (buf->closure, buf->mode); +} + +int +paxbuf_close (paxbuf_t buf) +{ + pax_io_status_t status; + if ((buf->mode & PAXBUF_WRITE) && buf->pos != 0) + status = flush_buffer (buf); + return buf->close (buf->closure, buf->mode) || status != pax_io_success; +} + + +/* Accessors */ + +void * +paxbuf_get_data (paxbuf_t buf) +{ + return buf->closure; +} + +int +paxbuf_get_mode (paxbuf_t buf) +{ + return buf->mode; +} + diff --git a/paxlib/paxbuf.h b/paxlib/paxbuf.h new file mode 100644 index 0000000..9d057c9 --- /dev/null +++ b/paxlib/paxbuf.h @@ -0,0 +1,65 @@ +/* This file is part of GNU paxutils + + Copyright (C) 2005 Free Software Foundation, Inc. + + Written by Sergey Poznyakoff + + GNU paxutils 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. + + GNU paxutils 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 GNU paxutils; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +typedef struct pax_buffer *paxbuf_t; + +typedef enum pax_io_status + { + pax_io_success, + pax_io_failure, + pax_io_eof + } +pax_io_status_t; + +#define PAXBUF_READ 0x1 +#define PAXBUF_WRITE 0x2 +#define PAXBUF_CREAT 0x4 + +typedef pax_io_status_t (*paxbuf_io_fp) (void *closure, + void *data, size_t size, + size_t *ret_size); +typedef int (*paxbuf_seek_fp) (void *closure, off_t offset); +typedef int (*paxbuf_term_fp) (void *closure, int mode); +typedef int (*paxbuf_destroy_fp) (void *closure); +typedef int (*paxbuf_wrapper_fp) (void *closure); +typedef const char * (*paxbuf_error_fp) (void *closure); + +int paxbuf_create (paxbuf_t *buf, int mode, void *closure, size_t record_size); +int paxbuf_open (paxbuf_t buf); +int paxbuf_close (paxbuf_t buf); +void paxbuf_set_io (paxbuf_t buf, paxbuf_io_fp rd, paxbuf_io_fp wr, + paxbuf_seek_fp seek); +void paxbuf_set_term (paxbuf_t buf, + paxbuf_term_fp open, paxbuf_term_fp close, + paxbuf_destroy_fp destroy); +void paxbuf_set_wrapper (paxbuf_t buf, paxbuf_wrapper_fp wrap); +void paxbuf_set_error (paxbuf_t buf, paxbuf_error_fp err); + +pax_io_status_t paxbuf_read (paxbuf_t pbuf, char *buf, size_t size, + size_t *rsize); +pax_io_status_t paxbuf_write (paxbuf_t pbuf, char *buf, size_t size, + size_t *rsize); +int paxbuf_seek (paxbuf_t buf, off_t offset); + +void paxbuf_destroy (paxbuf_t *buf); + +void *paxbuf_get_data (paxbuf_t buf); +int paxbuf_get_mode (paxbuf_t buf); + diff --git a/paxlib/rtape.c b/paxlib/rtape.c new file mode 100644 index 0000000..a947b59 --- /dev/null +++ b/paxlib/rtape.c @@ -0,0 +1,761 @@ +/* Functions for communicating with a remote tape drive. + + Copyright 1988, 1992, 1994, 1996, 1997, 1999, 2000, 2001, 2004, 2005 + 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 +#include +#include +#include "localedir.h" + +/* Try hard to get EOPNOTSUPP defined. 486/ISC has it in net/errno.h, + 3B2/SVR3 has it in sys/inet.h. Otherwise, use ENOSYS. */ + +#ifndef EOPNOTSUPP +# if HAVE_NET_ERRNO_H +# include +# endif +# if HAVE_SYS_INET_H +# include +# endif +# ifndef EOPNOTSUPP +# define EOPNOTSUPP ENOSYS +# endif +#endif + +#include + +#if HAVE_NETDB_H +# include +#endif + +/* 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}}; + + + +/* 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 i; + + /* Read the reply command line. */ + + for (i = 0, cursor = command_buffer; i < COMMAND_BUFFER_SIZE; i++, cursor++) + { + if (safe_read (READ_SIDE (handle), cursor, 1) != 1) + { + _rmt_shutdown (handle, EIO); + return 0; + } + if (*cursor == '\n') + { + *cursor = '\0'; + break; + } + } + + if (i == 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, char *rmt_command) +{ + 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 remote file 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, const char *rmt_command) +{ + 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 = EINVAL; + 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 (!rmt_command) + rmt_command = DEFAULT_RMT_COMMAND; + + 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/paxlib/tar.h b/paxlib/tar.h new file mode 100644 index 0000000..059287d --- /dev/null +++ b/paxlib/tar.h @@ -0,0 +1,279 @@ +/* GNU tar Archive Format description. + + Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, + 2000, 2001, 2003 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. */ + +/* tar Header Block, from POSIX 1003.1-1990. */ + +/* POSIX header. */ + +struct posix_header +{ /* byte offset */ + char name[100]; /* 0 */ + char mode[8]; /* 100 */ + char uid[8]; /* 108 */ + char gid[8]; /* 116 */ + char size[12]; /* 124 */ + char mtime[12]; /* 136 */ + char chksum[8]; /* 148 */ + char typeflag; /* 156 */ + char linkname[100]; /* 157 */ + char magic[6]; /* 257 */ + char version[2]; /* 263 */ + char uname[32]; /* 265 */ + char gname[32]; /* 297 */ + char devmajor[8]; /* 329 */ + char devminor[8]; /* 337 */ + char prefix[155]; /* 345 */ + /* 500 */ +}; + +#define TMAGIC "ustar" /* ustar and a null */ +#define TMAGLEN 6 +#define TVERSION "00" /* 00 and no null */ +#define TVERSLEN 2 + +/* Values used in typeflag field. */ +#define REGTYPE '0' /* regular file */ +#define AREGTYPE '\0' /* regular file */ +#define LNKTYPE '1' /* link */ +#define SYMTYPE '2' /* reserved */ +#define CHRTYPE '3' /* character special */ +#define BLKTYPE '4' /* block special */ +#define DIRTYPE '5' /* directory */ +#define FIFOTYPE '6' /* FIFO special */ +#define CONTTYPE '7' /* reserved */ + +#define XHDTYPE 'x' /* Extended header referring to the + next file in the archive */ +#define XGLTYPE 'g' /* Global extended header */ + +/* Bits used in the mode field, values in octal. */ +#define TSUID 04000 /* set UID on execution */ +#define TSGID 02000 /* set GID on execution */ +#define TSVTX 01000 /* reserved */ + /* file permissions */ +#define TUREAD 00400 /* read by owner */ +#define TUWRITE 00200 /* write by owner */ +#define TUEXEC 00100 /* execute/search by owner */ +#define TGREAD 00040 /* read by group */ +#define TGWRITE 00020 /* write by group */ +#define TGEXEC 00010 /* execute/search by group */ +#define TOREAD 00004 /* read by other */ +#define TOWRITE 00002 /* write by other */ +#define TOEXEC 00001 /* execute/search by other */ + +/* tar Header Block, GNU extensions. */ + +/* In GNU tar, SYMTYPE is for to symbolic links, and CONTTYPE is for + contiguous files, so maybe disobeying the `reserved' comment in POSIX + header description. I suspect these were meant to be used this way, and + should not have really been `reserved' in the published standards. */ + +/* *BEWARE* *BEWARE* *BEWARE* that the following information is still + boiling, and may change. Even if the OLDGNU format description should be + accurate, the so-called GNU format is not yet fully decided. It is + surely meant to use only extensions allowed by POSIX, but the sketch + below repeats some ugliness from the OLDGNU format, which should rather + go away. Sparse files should be saved in such a way that they do *not* + require two passes at archive creation time. Huge files get some POSIX + fields to overflow, alternate solutions have to be sought for this. */ + +/* Descriptor for a single file hole. */ + +struct sparse +{ /* byte offset */ + char offset[12]; /* 0 */ + char numbytes[12]; /* 12 */ + /* 24 */ +}; + +/* Sparse files are not supported in POSIX ustar format. For sparse files + with a POSIX header, a GNU extra header is provided which holds overall + sparse information and a few sparse descriptors. When an old GNU header + replaces both the POSIX header and the GNU extra header, it holds some + sparse descriptors too. Whether POSIX or not, if more sparse descriptors + are still needed, they are put into as many successive sparse headers as + necessary. The following constants tell how many sparse descriptors fit + in each kind of header able to hold them. */ + +#define SPARSES_IN_EXTRA_HEADER 16 +#define SPARSES_IN_OLDGNU_HEADER 4 +#define SPARSES_IN_SPARSE_HEADER 21 + +/* Extension header for sparse files, used immediately after the GNU extra + header, and used only if all sparse information cannot fit into that + extra header. There might even be many such extension headers, one after + the other, until all sparse information has been recorded. */ + +struct sparse_header +{ /* byte offset */ + struct sparse sp[SPARSES_IN_SPARSE_HEADER]; + /* 0 */ + char isextended; /* 504 */ + /* 505 */ +}; + +/* The old GNU format header conflicts with POSIX format in such a way that + POSIX archives may fool old GNU tar's, and POSIX tar's might well be + fooled by old GNU tar archives. An old GNU format header uses the space + used by the prefix field in a POSIX header, and cumulates information + normally found in a GNU extra header. With an old GNU tar header, we + never see any POSIX header nor GNU extra header. Supplementary sparse + headers are allowed, however. */ + +struct oldgnu_header +{ /* byte offset */ + char unused_pad1[345]; /* 0 */ + char atime[12]; /* 345 Incr. archive: atime of the file */ + char ctime[12]; /* 357 Incr. archive: ctime of the file */ + char offset[12]; /* 369 Multivolume archive: the offset of + the start of this volume */ + char longnames[4]; /* 381 Not used */ + char unused_pad2; /* 385 */ + struct sparse sp[SPARSES_IN_OLDGNU_HEADER]; + /* 386 */ + char isextended; /* 482 Sparse file: Extension sparse header + follows */ + char realsize[12]; /* 483 Sparse file: Real size*/ + /* 495 */ +}; + +/* OLDGNU_MAGIC uses both magic and version fields, which are contiguous. + Found in an archive, it indicates an old GNU header format, which will be + hopefully become obsolescent. With OLDGNU_MAGIC, uname and gname are + valid, though the header is not truly POSIX conforming. */ +#define OLDGNU_MAGIC "ustar " /* 7 chars and a null */ + +/* The standards committee allows only capital A through capital Z for + user-defined expansion. Other letters in use include: + + 'A' Solaris Access Control List + 'E' Solaris Extended Attribute File + 'I' Inode only, as in 'star' + 'X' POSIX 1003.1-2001 eXtended (VU version) */ + +/* This is a dir entry that contains the names of files that were in the + dir at the time the dump was made. */ +#define GNUTYPE_DUMPDIR 'D' + +/* Identifies the *next* file on the tape as having a long linkname. */ +#define GNUTYPE_LONGLINK 'K' + +/* Identifies the *next* file on the tape as having a long name. */ +#define GNUTYPE_LONGNAME 'L' + +/* This is the continuation of a file that began on another volume. */ +#define GNUTYPE_MULTIVOL 'M' + +/* For storing filenames that do not fit into the main header. */ +#define GNUTYPE_NAMES 'N' + +/* This is for sparse files. */ +#define GNUTYPE_SPARSE 'S' + +/* This file is a tape/volume header. Ignore it on extraction. */ +#define GNUTYPE_VOLHDR 'V' + + +/* Jörg Schilling star header */ + +struct star_header +{ /* byte offset */ + char name[100]; /* 0 */ + char mode[8]; /* 100 */ + char uid[8]; /* 108 */ + char gid[8]; /* 116 */ + char size[12]; /* 124 */ + char mtime[12]; /* 136 */ + char chksum[8]; /* 148 */ + char typeflag; /* 156 */ + char linkname[100]; /* 157 */ + char magic[6]; /* 257 */ + char version[2]; /* 263 */ + char uname[32]; /* 265 */ + char gname[32]; /* 297 */ + char devmajor[8]; /* 329 */ + char devminor[8]; /* 337 */ + char prefix[131]; /* 345 */ + char atime[12]; /* 476 */ + char ctime[12]; /* 488 */ + /* 500 */ +}; + +#define SPARSES_IN_STAR_HEADER 4 +#define SPARSES_IN_STAR_EXT_HEADER 21 + +struct star_in_header { + char fill[345]; /* 0 Everything that is before t_prefix */ + char prefix[1]; /* 345 t_name prefix */ + char fill2; /* 346 */ + char fill3[8]; /* 347 */ + char isextended; /* 355 */ + struct sparse sp[SPARSES_IN_STAR_HEADER]; /* 356 */ + char realsize[12]; /* 452 Actual size of the file */ + char offset[12]; /* 464 Offset of multivolume contents */ + char atime[12]; /* 476 */ + char ctime[12]; /* 488 */ + char mfill[8]; /* 500 */ + char xmagic[4]; /* 508 "tar" */ +}; + +struct star_ext_header { + struct sparse sp[SPARSES_IN_STAR_EXT_HEADER]; + char isextended; +}; + + + +/* tar Header Block, overall structure. */ + +/* tar files are made in basic blocks of this size. */ +#define BLOCKSIZE 512 + +enum archive_format +{ + DEFAULT_FORMAT, /* format to be decided later */ + V7_FORMAT, /* old V7 tar format */ + OLDGNU_FORMAT, /* GNU format as per before tar 1.12 */ + USTAR_FORMAT, /* POSIX.1-1988 (ustar) format */ + POSIX_FORMAT, /* POSIX.1-2001 format */ + STAR_FORMAT, /* Star format defined in 1994 */ + GNU_FORMAT /* Same as OLDGNU_FORMAT with one exception: + see FIXME note for to_chars() function + (create.c:189) */ +}; + +/* Information about a sparse file. */ +struct sp_array + { + off_t offset; + size_t numbytes; + }; + +union block +{ + char buffer[BLOCKSIZE]; + struct posix_header header; + struct star_header star_header; + struct oldgnu_header oldgnu_header; + struct sparse_header sparse_header; + struct star_in_header star_in_header; + struct star_ext_header star_ext_header; +}; + +/* End of Format description. */ diff --git a/paxlib/tarbuf.c b/paxlib/tarbuf.c new file mode 100644 index 0000000..9fa911a --- /dev/null +++ b/paxlib/tarbuf.c @@ -0,0 +1,218 @@ +/* This file is part of GNU paxutils + + Copyright (C) 2005 Free Software Foundation, Inc. + + Written by Sergey Poznyakoff + + GNU paxutils 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. + + GNU paxutils 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 GNU paxutils; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include + +typedef struct tar_archive +{ + char *filename; /* Name of the archive file */ + int fd; /* Archive file descriptor */ + int bfactor; /* Number of blocks in a record */ + const char *rsh; /* Full pathname of rsh */ + const char *rmt; /* Full pathname of the remote command */ +} +tar_archive_t; + + +/* Operations on local files */ + +static pax_io_status_t +local_reader (void *closure, void *data, size_t size, size_t *ret_size) +{ + tar_archive_t *tar = closure; + ssize_t s; + + s = read (tar->fd, data, size); + if (s == -1) + return pax_io_failure; + if (s == 0) + return pax_io_eof; + *ret_size = s; + return pax_io_success; +} + +static pax_io_status_t +local_writer (void *closure, void *data, size_t size, size_t *ret_size) +{ + tar_archive_t *tar = closure; + ssize_t s; + + s = write (tar->fd, data, size); + if (s == -1) + return pax_io_failure; + *ret_size = s; + return pax_io_success; +} + +static int +local_seek (void *closure, off_t offset) +{ + tar_archive_t *tar = closure; + off_t off; + + off = lseek (tar->fd, offset, SEEK_SET); + if (off == -1) + return pax_io_failure; + return pax_io_success; +} + +static int +local_open (void *closure, int pax_mode) +{ + tar_archive_t *tar = closure; + int mode = (pax_mode & PAXBUF_READ) ? O_RDONLY : + O_RDWR | ((pax_mode & PAXBUF_CREAT) ? O_CREAT : 0); + tar->fd = open (tar->filename, mode, MODE_RW); + if (tar->fd == -1) + return pax_io_failure; + return pax_io_success; +} + +static int +local_close (void *closure, int mode) +{ + tar_archive_t *tar = closure; + close (tar->fd); + tar->fd = -1; + return 0; +} + + +/* Operations on remote files */ +static pax_io_status_t +remote_reader (void *closure, void *data, size_t size, size_t *ret_size) +{ + tar_archive_t *tar = closure; + size_t s; + + s = rmt_read (tar->fd, data, size); + if (s == SAFE_READ_ERROR) + return pax_io_failure; + if (s == 0) + return pax_io_eof; + *ret_size = s; + return pax_io_success; +} + +static pax_io_status_t +remote_writer (void *closure, void *data, size_t size, size_t *ret_size) +{ + tar_archive_t *tar = closure; + size_t s; + + s = rmt_write (tar->fd, data, size); + if (s == SAFE_WRITE_ERROR) + return pax_io_failure; + *ret_size = s; + return pax_io_success; +} + +static int +remote_seek (void *closure, off_t offset) +{ + tar_archive_t *tar = closure; + off_t off = rmt_lseek (tar->fd, offset, SEEK_SET); + if (off == -1) + return pax_io_failure; + return pax_io_success; +} + +static int +remote_open (void *closure, int pax_mode) +{ + tar_archive_t *tar = closure; + int mode = (pax_mode & PAXBUF_READ) ? O_RDONLY : + O_RDWR | ((pax_mode & PAXBUF_CREAT) ? O_CREAT : 0); + tar->fd = rmt_open (tar->filename, mode, 0, tar->rsh, tar->rmt); + if (tar->fd == -1) + return pax_io_failure; + return pax_io_success; +} + +static int +remote_close (void *closure, int mode) +{ + tar_archive_t *tar = closure; + int rc = rmt_close (tar->fd); + tar->fd = -1; + return rc; +} + + +static int +tar_destroy (void *closure) +{ + tar_archive_t *tar = closure; + free (tar->filename); + free (tar); + return 0; +} + +static int +tar_wrapper (void *closure) +{ + return 1; +} + +void +tar_archive_create (paxbuf_t *pbuf, const char *filename, + int remote, int mode, size_t bfactor) +{ + tar_archive_t *tar; + + tar = xmalloc (sizeof (*tar)); + tar->filename = xstrdup (filename); + tar->fd = -1; + tar->bfactor = bfactor; + tar->rsh = NULL; + tar->rmt = NULL; + paxbuf_create (pbuf, mode, tar, bfactor * BLOCKSIZE); + if (remote) + { + paxbuf_set_io (*pbuf, remote_reader, remote_writer, remote_seek); + paxbuf_set_term (*pbuf, remote_open, remote_close, tar_destroy); + } + else + { + paxbuf_set_io (*pbuf, local_reader, local_writer, local_seek); + paxbuf_set_term (*pbuf, local_open, local_close, tar_destroy); + } + + paxbuf_set_wrapper (*pbuf, tar_wrapper); +} + +void +tar_set_rmt (paxbuf_t pbuf, const char *rmt) +{ + tar_archive_t *tar = paxbuf_get_data (pbuf); + tar->rmt = rmt; +} + +void +tar_set_rsh (paxbuf_t pbuf, const char *rsh) +{ + tar_archive_t *tar = paxbuf_get_data (pbuf); + tar->rsh = rsh; +} diff --git a/paxlib/tardef.h b/paxlib/tardef.h new file mode 100644 index 0000000..d7068fd --- /dev/null +++ b/paxlib/tardef.h @@ -0,0 +1,54 @@ +/* This file is part of GNU paxutils + + Copyright (C) 2005 Free Software Foundation, Inc. + + Written by Sergey Poznyakoff + + GNU paxutils 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. + + GNU paxutils 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 GNU paxutils; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +struct tar_stat_info +{ + char *orig_file_name; /* name of file read from the archive header */ + char *file_name; /* name of file for the current archive entry + after being normalized. */ + int had_trailing_slash; /* nonzero if the current archive entry had a + trailing slash before it was normalized. */ + char *link_name; /* name of link for the current archive entry. */ + + unsigned int devminor; /* device minor number */ + unsigned int devmajor; /* device major number */ + char *uname; /* user name of owner */ + char *gname; /* group name of owner */ + struct stat stat; /* regular filesystem stat */ + + /* Nanosecond parts of file timestamps (if available) */ + unsigned long atime_nsec; + unsigned long mtime_nsec; + unsigned long ctime_nsec; + + off_t archive_file_size; /* Size of file as stored in the archive. + Equals stat.st_size for non-sparse files */ + + bool is_sparse; /* Is the file sparse */ + + size_t sparse_map_avail; /* Index to the first unused element in + sparse_map array. Zero if the file is + not sparse */ + size_t sparse_map_size; /* Size of the sparse map */ + struct sp_array *sparse_map; +}; + +void tar_archive_create (paxbuf_t *pbuf, const char *filename, + int mode, size_t bfactor); -- cgit v1.2.1