summaryrefslogtreecommitdiff
path: root/paxlib
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2005-05-14 10:55:01 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2005-05-14 10:55:01 +0000
commit4154f020dc9aaba332cad1028bca4f3b6024aa08 (patch)
treedeb1b334b9ba48d4ed6970a5768683203afdfaa6 /paxlib
parentdfe767c78bb5f85709574dae8f07caf1052d1030 (diff)
downloadpaxutils-4154f020dc9aaba332cad1028bca4f3b6024aa08.tar.gz
New file
Diffstat (limited to 'paxlib')
-rw-r--r--paxlib/.cvsignore4
-rw-r--r--paxlib/Makefile.am43
-rw-r--r--paxlib/pax.h69
-rw-r--r--paxlib/paxbuf.c332
-rw-r--r--paxlib/paxbuf.h65
-rw-r--r--paxlib/rtape.c761
-rw-r--r--paxlib/tar.h279
-rw-r--r--paxlib/tarbuf.c218
-rw-r--r--paxlib/tardef.h54
9 files changed, 1825 insertions, 0 deletions
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 <config.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#include <gettext.h>
+#include <system.h>
+#include <paxbuf.h>
+
+/* 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 <system.h>
+#include <safe-read.h>
+#include <full-write.h>
+#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 <net/errno.h>
+# endif
+# if HAVE_SYS_INET_H
+# include <sys/inet.h>
+# endif
+# ifndef EOPNOTSUPP
+# define EOPNOTSUPP ENOSYS
+# endif
+#endif
+
+#include <signal.h>
+
+#if HAVE_NETDB_H
+# include <netdb.h>
+#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 <system.h>
+#include <safe-read.h>
+#include <safe-write.h>
+#include <paxbuf.h>
+#include <pax.h>
+#include <tar.h>
+
+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);