diff options
-rw-r--r-- | .cvsignore | 12 | ||||
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | Makefile.am | 23 | ||||
-rw-r--r-- | NEWS | 32 | ||||
-rw-r--r-- | README-alpha | 86 | ||||
-rw-r--r-- | THANKS | 10 | ||||
-rw-r--r-- | build-aux/.cvsignore | 7 | ||||
-rwxr-xr-x | build-aux/bootstrap | 270 | ||||
-rw-r--r-- | build-aux/gnulib.modules | 17 | ||||
-rw-r--r-- | configure.ac | 268 | ||||
-rw-r--r-- | lib/.cvsignore | 75 | ||||
-rw-r--r-- | lib/Makefile.tmpl | 32 | ||||
-rw-r--r-- | m4/.cvsignore | 70 | ||||
-rw-r--r-- | paxlib/.cvsignore | 4 | ||||
-rw-r--r-- | paxlib/Makefile.am | 43 | ||||
-rw-r--r-- | paxlib/pax.h | 69 | ||||
-rw-r--r-- | paxlib/paxbuf.c | 332 | ||||
-rw-r--r-- | paxlib/paxbuf.h | 65 | ||||
-rw-r--r-- | paxlib/rtape.c | 761 | ||||
-rw-r--r-- | paxlib/tar.h | 279 | ||||
-rw-r--r-- | paxlib/tarbuf.c | 218 | ||||
-rw-r--r-- | paxlib/tardef.h | 54 | ||||
-rw-r--r-- | paxtest/.cvsignore | 4 | ||||
-rw-r--r-- | paxtest/Makefile.am | 28 | ||||
-rw-r--r-- | paxtest/paxtest.c | 85 | ||||
-rw-r--r-- | paxtest/paxtest.h | 25 |
26 files changed, 2872 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..51cfcd3 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,12 @@ +ABOUT-NLS +INSTALL +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +config.h +config.h.in +config.log +config.status +configure +stamp-h1 @@ -0,0 +1,3 @@ +Authors of GNU paxutils + +Sergey Poznyakoff gray@gnu.org.ua diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..f8cae47 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,23 @@ +# 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. + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = lib paxlib paxtest po @@ -0,0 +1,32 @@ +GNU paxutils NEWS -- history of user-visible changes. 2005-05-14 +Copyright (C) 2005 Free Software Foundation, Inc. +See the end of file for copying conditions. + +Please send paxutils bug reports to <bug-tar@gnu.org>. + + +Version 0.0.1: + +* Implemented general-purpose buffer support. +* Implemented the framework of paxtest utility + + +---------------------------------------------------------------------- +* Copyright information: + +Copyright (C) 2005 Free Software Foundation, Inc. + + Permission is granted to anyone to make or distribute verbatim copies + of this document as received, in any medium, provided that the + copyright notice and this permission notice are preserved, + thus giving the recipient permission to redistribute in turn. + + Permission is granted to distribute modified versions + of this document, or of portions of it, + under the above conditions, provided also that they + carry prominent notices stating who last changed them. + +Local variables: +mode: outline +paragraph-separate: "[ ]*$" +end: diff --git a/README-alpha b/README-alpha new file mode 100644 index 0000000..9c26f9b --- /dev/null +++ b/README-alpha @@ -0,0 +1,86 @@ +This is GNU paxutils. +This document describes the actions needed to build the pre-release +or CVS version of the package. See end of file for copying conditions. + +* Introduction + + This is a *pre-release* version, and not ready for production use +yet. For information about GNU paxutils and its aims, please see file +README. + + If you are taking source from CVS, you will need to have automake, +and autoconf installed to bootstrap the package. See the chapter +`Building' for the detailed instructions. After bootstrapping, there +should be a file 'INSTALL' with generic installation +instructions. Package-specific installation instructions are set forth +in the file README. + + Please, note that the accompanying documentation may be inaccurate +or incomplete (well, to say the truth it is absent. See TODO for more +info). The ChangeLog file is currently the authoritative documentation +of all recent changes. + +Report bugs to <bug-tar@gnu.org> + +* Checking Out the Sources + + The following instructions apply if you wish to obtain sources from +the CVS repository: + +To checkout the source tree from CVS issue the following command: + +CVS_RSH=ssh \ + cvs -d :ext:anoncvs@savannah.gnu.org:/cvsroot/paxutils checkout paxutils + +Make sure SSHv2 is used. + +This will give you read-only access. If you think you need write access, +contact the mailing list. + +The CVS repository is also available via HTTP from + + http://savannah.gnu.org/cgi-bin/viewcvs/paxutils/paxutils + +* Building + + In order to build this you will first need to have right versions +of autotools. At the time of this writing these are: + + Package Version (>=) + ======== ============ + automake 1.8.5 + autoconf 2.59 + gettext 0.14.1 + + To prepare the package for building run build-aux/bootstrap. For +example: + + $ cd paxutils + $ build-aux/bootstrap + + If you have already checked out gnulib sources, use --gnulib-srcdir +to specify their location, this will spare you time and bandwidth: + + $ cd paxutils + $ build-aux/bootstrap --gnulib-srcdir=$HOME/gnu/gnulib + +* Copyright information: + +Copyright (C) 2005 Free Software Foundation, Inc. + + Permission is granted to anyone to make or distribute verbatim copies + of this document as received, in any medium, provided that the + copyright notice and this permission notice are preserved, + thus giving the recipient permission to redistribute in turn. + + Permission is granted to distribute modified versions + of this document, or of portions of it, + under the above conditions, provided also that they + carry prominent notices stating who last changed them. + + +Local Variables: +mode: outline +paragraph-separate: "[ ]*$" +version-control: never +End: @@ -0,0 +1,10 @@ +GNU paxutils THANKS file + +Paul Eggert eggert@twinsun.com +François Pinard pinard@iro.umontreal.ca + + +;;;; Local variables: +;;;; mode: Fundamental +;;;; buffer-file-coding-system: utf-8 +;;;; End: diff --git a/build-aux/.cvsignore b/build-aux/.cvsignore new file mode 100644 index 0000000..eb303c8 --- /dev/null +++ b/build-aux/.cvsignore @@ -0,0 +1,7 @@ +config.guess +config.rpath +config.sub +depcomp +install-sh +missing +mkinstalldirs diff --git a/build-aux/bootstrap b/build-aux/bootstrap new file mode 100755 index 0000000..25d5242 --- /dev/null +++ b/build-aux/bootstrap @@ -0,0 +1,270 @@ +#! /bin/sh + +# Bootstrap 'paxutils' from CVS. + +# Copyright (C) 2003, 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. + +# Written by Paul Eggert and Sergey Poznyakoff. + +# *Eventual* URL of our text domain page in Translation Project +TP_URL="http://www.iro.umontreal.ca/translation/maint/paxutils/" + +# Ensure file names are sorted consistently across platforms; +# e.g., m4/ulonglong_gl.m4 should follow m4/ulonglong.m4. +LC_ALL=C +export LC_ALL + +usage() { + cat <<EOF + usage: $0 [--gnulib-srcdir=DIR][--cvs-auth=AUTH-METHOD][--cvs-user=USERNAME][--no-po] + Options are: + --gnulib-srcdir=DIRNAME Specify the local directory where gnulib + sources reside. Use this if you already + have gnulib sources on your machine, and + do not want to waste your bandwidth dowloading + them again. + --cvs-auth=METHOD Set the CVS access method used for downloading + gnulib files. METHOD is one of the keywords + accepted by cvs -d option (see info cvs + repository). + --cvs-user=USERNAME Set the CVS username to be used when accessing + the gnulib repository. + --no-po Do not download po files. + --update-po[=LANG] Update po file(s) and exit. + +Running without arguments will suffice in most cases. It is equivalent +to + + ./bootstrap --cvs-auth=ext --cvs-user=anoncvs + +EOF +} + +bailout() { + echo "$0: $*" >&2 + exit 1 +} + +update_po() { + if [ $# = 1 ]; then + case $1 in + *.po) POFILE=$1;; + *) POFILE=${1}.po;; + esac + echo "$0: getting translation for $1..." + wget -r -C off $TP_URL/$POFILE + else + echo "$0: getting translations into po..." + (cd po && + rm -f dummy `ls | sed -n '/\.gmo$/p; /\.po/p'` && + wget -nv -nd -r -l 1 -A .po -C off $TP_URL && + rm -f index.html index.html.[0-9]* + ls *.po | sed 's/\.po$//' >LINGUAS + ) || exit + fi +} + +# Parse options. + +DOWNLOAD_PO=no +for option +do + case $option in + --help) + usage + exit;; + --gnulib-srcdir=*) + GNULIB_SRCDIR=`expr "$option" : '--gnulib-srcdir=\(.*\)'`;; + --cvs-auth=*) + CVS_AUTH=`expr "$option" : '--cvs-auth=\(.*\)'`;; + --cvs-user=*) + CVS_USER=`expr "$option" : '--cvs-user=\(.*\)'`;; + --no-po) + DOWNLOAD_PO=no;; + --update-po=*) + bailout "Option is not yet active" + DOWNLOAD_PO=`expr "$option" : '--update-po=\(.*\)'`;; + --update-po) + bailout "Option is not yet active" + DOWNLOAD_PO=only;; + *) + bailout "$option: unknown option" + exit 1;; + esac +done + +case $DOWNLOAD_PO in +only) update_po + exit 0 + ;; +no|yes) ;; +*) update_po $DOWNLOAD_PO + exit 0 +esac + +echo "$0: Bootstrapping CVS paxutils..." + +build_cvs_prefix() { + CVS_PREFIX=:${1}: + if [ "${2}" != - ]; then + CVS_PREFIX=${CVS_PREFIX}${2}@ + fi + if [ "$1" = "ext" ]; then + if [ -z "${CVS_RSH}" ]; then + CVS_RSH=ssh + export CVS_RSH + fi + fi +} + +# checkout package +checkout() { + if [ ! -d $1 ]; then + echo "$0: getting $1 files..." + + trap exit 1 2 13 15 + trap 'rm -fr $1; exit 1' 0 + + case "${CVS_AUTH--}" in + -) build_cvs_prefix ext anoncvs + ;; + pserver) build_cvs_prefix $CVS_AUTH ${CVS_USER:-anoncvs} + ;; + gserver|server) + build_cvs_prefix $CVS_AUTH ${CVS_USER--} + ;; + ext) build_cvs_prefix $CVS_AUTH ${CVS_USER--} + ;; + *) echo "$0: Unknown CVS access method" >&2 + exit 1;; + esac + if [ "${CVS_AUTH--}" = "pserver" ]; then + cvs -d ${CVS_PREFIX}subversions.gnu.org:/cvsroot/$1 login || exit + fi + cvs -q -d ${CVS_PREFIX}subversions.gnu.org:/cvsroot/$1 co $1 || exit + + trap 0 + fi +} + +# Prepare temporary module list +cat /dev/null > modlist.tmp +trap 'rm -f modlist.tmp' 0 1 2 13 15 + +get_modules() { + sed '/^[ ]*#/d;/^[ ]*$/d' $* >> modlist.tmp +} + +# copy_files srcdir dstdir +copy_files() { + for file in `cat $1/DISTFILES` + do + case $file in + "#*") continue;; + esac + echo "$0: Copying file $1/$file" + cp -p $1/$file $2/`expr $file : '.*/\(.*\)'` + done +} + +# Get gnulib files. + +case ${GNULIB_SRCDIR--} in +-) checkout gnulib + GNULIB_SRCDIR=gnulib +esac + +<$GNULIB_SRCDIR/gnulib-tool || exit + +get_modules build-aux/gnulib.modules + +gnulib_modules=`sort -u modlist.tmp` +previous_gnulib_modules= +while [ "$gnulib_modules" != "$previous_gnulib_modules" ]; do + previous_gnulib_modules=$gnulib_modules + gnulib_modules=` + (echo "$gnulib_modules" + for gnulib_module in $gnulib_modules; do + $GNULIB_SRCDIR/gnulib-tool --extract-dependencies $gnulib_module + done) | sort -u + ` +done + +gnulib_files=` + (for gnulib_module in $gnulib_modules; do + $GNULIB_SRCDIR/gnulib-tool --extract-filelist $gnulib_module + done) | sort -u +` + +gnulib_dirs=`echo "$gnulib_files" | sed 's,/[^/]*$,,' | sort -u` +mkdir -p $gnulib_dirs || exit + +for gnulib_file in $gnulib_files; do + dest=$gnulib_file + + case $gnulib_file in + m4/codeset.m4) continue;; + m4/intdiv0.m4) continue;; + m4/inttypes-pri.m4) continue;; + m4/isc-posix.m4) continue;; + m4/lcmessage.m4) continue;; + m4/onceonly_2_57.m4) dest=m4/onceonly.m4;; + # These will be overwritten by autopoint, which still uses + # old jm_.* macro names, so we have to keep both copies. + m4/gettext.m4 | m4/glibc21.m4 | m4/inttypes_h.m4 | m4/lib-ld.m4 | \ + m4/lib-prefix.m4 | m4/po.m4 | m4/stdint_h.m4 | m4/uintmax_t.m4 | \ + m4/ulonglong.m4) + dest=`expr $gnulib_file : '\(.*\).m4'`_gl.m4;; + esac + + rm -f $dest && + echo "$0: Copying file $GNULIB_SRCDIR/$gnulib_file" && + cp -p $GNULIB_SRCDIR/$gnulib_file $dest || exit +done + +echo "$0: Creating m4/gnulib.m4" +(echo "# This file is generated automatically. Please, do not edit." + echo "#" + echo "AC_DEFUN([paxutils_GNULIB],[" + for gnulib_module in $gnulib_modules; do + echo "# $gnulib_module" + $GNULIB_SRCDIR/gnulib-tool --extract-autoconf-snippet $gnulib_module + done | sed '/AM_GNU_GETTEXT/d' + echo "])") > ./m4/gnulib.m4 + +echo "$0: Creating lib/Makefile.am" +(echo "# This file is generated automatically from lib/Makefile.am. Do not edit!" + cat lib/Makefile.tmpl + + for gnulib_module in $gnulib_modules; do + echo "# $gnulib_module" + $GNULIB_SRCDIR/gnulib-tool --extract-automake-snippet $gnulib_module + done | sed 's/lib_SOURCES/libgnu_a_SOURCES/g' ) > lib/Makefile.am + +# Get translations. +if test "$DOWNLOAD_PO" = "yes"; then + update_po +fi + +# Reconfigure, getting other files. + +echo "$0: autoreconf --verbose --install --force ..." +autoreconf --verbose --install --force || exit 1 + + +echo "$0: done. Now you can run './configure'." diff --git a/build-aux/gnulib.modules b/build-aux/gnulib.modules new file mode 100644 index 0000000..6b0b649 --- /dev/null +++ b/build-aux/gnulib.modules @@ -0,0 +1,17 @@ +# List of gnulib modules needed for GNU paxutils. +# A module name per line. Empty lines and comments are ignored. + +argp +savedir +unlocked-io +fileblocks +error +gettext +inttostr +dirname +full-write +getopt +safe-read +stdbool +strtol +xalloc diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..6923d0c --- /dev/null +++ b/configure.ac @@ -0,0 +1,268 @@ +# This file is part of GNU paxutils +# +# Copyright (C) 2005 Free Software Foundation, Inc. +# +# 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. + +AC_INIT([GNU paxutils], [0.0.1], [bug-paxutils@gnu.org]) +AC_CONFIG_SRCDIR([paxtest/paxtest.c]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_HEADERS([config.h]) +AC_PREREQ([2.59]) +AM_INIT_AUTOMAKE([1.8 gnits dist-bzip2 dist-shar std-options]) + +gl_USE_SYSTEM_EXTENSIONS +AC_PROG_CC +AC_EXEEXT +AC_PROG_RANLIB +AC_SYS_LARGEFILE +AC_ISC_POSIX +AC_C_INLINE + +AC_CHECK_HEADERS(fcntl.h linux/fd.h memory.h net/errno.h \ + sgtty.h string.h \ + sys/param.h sys/device.h sys/gentape.h \ + sys/inet.h sys/io/trioctl.h \ + sys/mtio.h sys/time.h sys/tprintf.h sys/tape.h \ + unistd.h locale.h) + +AC_CHECK_HEADERS([sys/buf.h], [], [], +[#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif]) + +AC_HEADER_SYS_WAIT +AM_STDBOOL_H + +AC_HEADER_DIRENT +AC_HEADER_MAJOR +AC_HEADER_STAT +AC_HEADER_STDC +AC_STRUCT_ST_BLKSIZE +AC_STRUCT_ST_BLOCKS +AC_MSG_CHECKING([for st_fstype string in struct stat]) +AC_CACHE_VAL(diff_cv_st_fstype_string, + [AC_TRY_COMPILE([#include <sys/types.h> +#include <sys/stat.h>], [struct stat s; s.st_fstype[0] = 'x';], + diff_cv_st_fstype_string=yes, + diff_cv_st_fstype_string=no)]) +AC_MSG_RESULT($diff_cv_st_fstype_string) +if test $diff_cv_st_fstype_string = yes; then + AC_DEFINE(HAVE_ST_FSTYPE_STRING, 1, + [Define if struct stat has a char st_fstype[] member.]) +fi + +AC_TYPE_SIGNAL +AC_TYPE_MODE_T +AC_TYPE_PID_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_TYPE_UID_T +AC_CHECK_TYPE(major_t, , AC_DEFINE(major_t, int, + [Type of major device numbers.])) +AC_CHECK_TYPE(minor_t, , AC_DEFINE(minor_t, int, + [Type of minor device numbers.])) +AC_CHECK_TYPE(dev_t, unsigned) +AC_CHECK_TYPE(ino_t, unsigned) + +gt_TYPE_SSIZE_T +gl_AC_TYPE_INTMAX_T +jm_AC_TYPE_UINTMAX_T + +MU_DEBUG_MODE + +# gnulib modules +paxutils_GNULIB + +PU_RMT +PU_RTAPELIB +PU_SYSTEM + +AC_CHECK_MEMBERS([struct stat.st_spare1, struct stat.st_atim.tv_nsec, struct stat.st_atimespec.tv_nsec, struct stat.st_atimensec], , , + [ +#include <sys/types.h> +#include <sys/stat.h>]) + +# Save and restore LIBS so e.g., -lrt, isn't added to it. Otherwise, *all* +# programs in the package would end up linked with that potentially-shared +# library, inducing unnecessary run-time overhead. + +# Solaris 2.5.1 needs -lposix4 to get the clock_gettime function. +# Solaris 7 prefers the library name -lrt to the obsolescent name -lposix4. +tar_save_LIBS=$LIBS + LIB_CLOCK_GETTIME= + AC_SEARCH_LIBS(clock_gettime, [rt posix4]) + case "$ac_cv_search_clock_gettime" in + -l*) LIB_CLOCK_GETTIME=$ac_cv_search_clock_gettime;; + esac + AC_SUBST(LIB_CLOCK_GETTIME) + AC_CHECK_FUNCS(clock_gettime) +LIBS=$tar_save_LIBS + +AC_CHECK_FUNCS(fsync lstat mkfifo readlink strerror symlink setlocale utimes) +AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>]) +AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>]) +AC_CHECK_DECLS([time],,, [#include <time.h>]) + +# Set LIB_SETSOCKOPT to -lnsl -lsocket if necessary. +tar_save_LIBS=$LIBS + LIB_SETSOCKOPT= + AC_SEARCH_LIBS(setsockopt, [socket], , + [AC_SEARCH_LIBS(setsockopt, [socket], , , [-lnsl])]) + AC_SEARCH_LIBS(setsockopt, [nsl]) + + case "$ac_cv_search_setsockopt" in + -l*) LIB_SETSOCKOPT=$ac_cv_search_setsockopt + esac + AC_SUBST(LIB_SETSOCKOPT) +LIBS=$tar_save_LIBS + +AC_REPLACE_FUNCS(waitpid) + +AC_CACHE_CHECK(for remote shell, tar_cv_path_RSH, + [if test -n "$RSH"; then + tar_cv_path_RSH=$RSH + else + tar_cv_path_RSH=no + for ac_file in /usr/ucb/rsh /usr/bin/remsh /usr/bin/rsh /usr/bsd/rsh \ + /usr/bin/nsh /usr/bin/rcmd + do + # Prefer a non-symlink rsh to a symlink one, so that binaries built + # on AIX 4.1.4, where /usr/ucb/rsh is a symlink to /usr/bin/rsh + # will run on AIX 4.3.0, which has only /usr/bin/rsh. + if test -f $ac_file; then + if (test -h $ac_file) 2>/dev/null; then + test $tar_cv_path_RSH = no && tar_cv_path_RSH=$ac_file + else + tar_cv_path_RSH=$ac_file + break + fi + fi + done + fi]) +if test $tar_cv_path_RSH = no; then + AC_CHECK_HEADERS(netdb.h) +else + AC_DEFINE_UNQUOTED(REMOTE_SHELL, "$tar_cv_path_RSH", + [Define to the full path of your rsh, if any.]) +fi + +AC_MSG_CHECKING(for default archive format) + +AC_ARG_VAR([DEFAULT_ARCHIVE_FORMAT], + [Set the default archive format. Allowed values are: V7, OLDGNU, USTAR, POSIX, GNU. Default is GNU]) + +if test -z "$DEFAULT_ARCHIVE_FORMAT"; then + DEFAULT_ARCHIVE_FORMAT="GNU" +fi +case $DEFAULT_ARCHIVE_FORMAT in + V7|OLDGNU|USTAR|POSIX|GNU) ;; + *) AC_MSG_ERROR(Invalid format name);; +esac +AC_DEFINE_UNQUOTED(DEFAULT_ARCHIVE_FORMAT, ${DEFAULT_ARCHIVE_FORMAT}_FORMAT, + [By default produce archives of this format]) +AC_MSG_RESULT($DEFAULT_ARCHIVE_FORMAT) + +AC_MSG_CHECKING(for default archive) + +AC_ARG_VAR([DEFAULT_ARCHIVE], + [Set the name of the default archive (default: -)]) +if test -z "$DEFAULT_ARCHIVE"; then + DEFAULT_ARCHIVE=- +else + if test -z "`ls $DEFAULT_ARCHIVE 2>/dev/null`"; then + AC_MSG_WARN(DEFAULT_ARCHIVE \`$DEFAULT_ARCHIVE' not found on this system) + fi + # FIXME: Look for DEFTAPE in <sys/mtio.h>. + # FIXME: Let DEVICE_PREFIX be configured from the environment. + # FIXME: Rearrange, here. + case $DEFAULT_ARCHIVE in + *[[0-7][lmh]]) + AC_DEFINE(DENSITY_LETTER, 1, + [[Define to 1 if density may be indicated by [lmh] at end of device.]]) + device_prefix=`echo $DEFAULT_ARCHIVE | sed 's/[0-7][lmh]$//'` + ;; + *[[0-7]]) + device_prefix=`echo $DEFAULT_ARCHIVE | sed 's/[0-7]$//'` + ;; + *) + device_prefix= + ;; + esac + case "$device_prefix" in + ?*) + AC_DEFINE_UNQUOTED(DEVICE_PREFIX, "$device_prefix", + [Define to a string giving the prefix of the default device, without the part specifying the unit and density.]) + ;; + esac +fi +AC_DEFINE_UNQUOTED(DEFAULT_ARCHIVE, "$DEFAULT_ARCHIVE", + [Define to a string giving the full name of the default archive file.]) +AC_MSG_RESULT($DEFAULT_ARCHIVE) + +AC_ARG_VAR([DEFAULT_BLOCKING], + [Define default blocking factor (default: 20)]) +AC_MSG_CHECKING(for default blocking) +DEFAULT_BLOCKING=${DEFAULT_BLOCKING-20} +AC_DEFINE_UNQUOTED(DEFAULT_BLOCKING, $DEFAULT_BLOCKING, + [Define to a number giving the default blocking size for archives.]) +AC_MSG_RESULT($DEFAULT_BLOCKING) + +# Iconv +AM_ICONV +AC_CHECK_HEADERS(iconv.h) +AC_CHECK_TYPE(iconv_t,:, + AC_DEFINE(iconv_t, int, + [Conversion descriptor type]), + [ +#ifdef HAVE_ICONV_H +# include <iconv.h> +#endif +]) + +# Gettext. +AM_GNU_GETTEXT([external], [need-ngettext]) +AM_GNU_GETTEXT_VERSION(0.12.1) + +# Initialize the test suite. +# AC_CONFIG_TESTDIR(tests) +# AC_CONFIG_FILES([tests/Makefile tests/atlocal]) +AM_MISSING_PROG([AUTOM4TE], [autom4te]) + +AC_SUBST(BACKUP_LIBEXEC_SCRIPTS) +AC_SUBST(BACKUP_SBIN_SCRIPTS) +AC_ARG_ENABLE(backup-scripts, + AC_HELP_STRING([--enable-backup-scripts], + [Create and install backup and restore scripts]), + [case $enableval in + yes) BACKUP_LIBEXEC_SCRIPTS='$(BACKUP_LIBEXEC_SCRIPTS_LIST)' + BACKUP_SBIN_SCRIPTS='$(BACKUP_SBIN_SCRIPTS_LIST)' + ;; + esac]) + +AC_SUBST(BACKUP_SED_COND) +if date +%Y-%m-%d 2>/dev/null >&2; then + BACKUP_SED_COND='/^\#ELSE_DATE_FORMAT_OK/,/^\#ENDIF_DATE_FORMAT_OK/d;/^\#IF_DATE_FORMAT_OK/d' +else + BACKUP_SED_COND='/^\#IF_DATE_FORMAT_OK/,/^\#ELSE_DATE_FORMAT_OK/d;/^\#ENDIF_DATE_FORMAT_OK/d' +fi + + +AC_OUTPUT([Makefile\ + lib/Makefile\ + paxlib/Makefile\ + po/Makefile.in\ + paxtest/Makefile]) + diff --git a/lib/.cvsignore b/lib/.cvsignore new file mode 100644 index 0000000..bbc9fcb --- /dev/null +++ b/lib/.cvsignore @@ -0,0 +1,75 @@ +.deps +Makefile +Makefile.am +Makefile.in +alloca.c +alloca.h +alloca_.h +argp-ba.c +argp-eexst.c +argp-fmtstream.c +argp-fmtstream.h +argp-fs-xinl.c +argp-help.c +argp-namefrob.h +argp-parse.c +argp-pv.c +argp-pvh.c +argp-xinl.c +argp.h +asnprintf.c +basename.c +dirname.c +dirname.h +error.c +error.h +fileblocks.c +full-write.c +full-write.h +getopt.c +getopt.h +getopt1.c +getopt_.h +getopt_int.h +gettext.h +imaxtostr.c +intprops.h +inttostr.c +inttostr.h +mempcpy.c +mempcpy.h +minmax.h +offtostr.c +paxutils.h +printf-args.c +printf-args.h +printf-parse.c +printf-parse.h +safe-read.c +safe-read.h +safe-write.c +safe-write.h +savedir.c +savedir.h +stdbool_.h +strcase.h +strcasecmp.c +strchrnul.c +strchrnul.h +stripslash.c +strncasecmp.c +strndup.c +strndup.h +strnlen.c +strtol.c +sysexit_.h +umaxtostr.c +unlocked-io.h +vasnprintf.c +vasnprintf.h +vsnprintf.c +vsnprintf.h +waitpid.c +xalloc.h +xmalloc.c +xsize.h diff --git a/lib/Makefile.tmpl b/lib/Makefile.tmpl new file mode 100644 index 0000000..7ad5ced --- /dev/null +++ b/lib/Makefile.tmpl @@ -0,0 +1,32 @@ +# Makefile for GNU cflow library. -*- Makefile -*- + +# Copyright (C) 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. + +noinst_LIBRARIES = libgnu.a + +libgnu_a_SOURCES = +libgnu_a_LIBADD = $(LIBOBJS) $(ALLOCA) +libgnu_a_DEPENDENCIES = $(libgnu_a_LIBADD) + +BUILT_SOURCES = +EXTRA_DIST = Makefile.tmpl +MAINTAINERCLEANFILES = +MOSTLYCLEANFILES = +lib_OBJECTS = $(libgnu_a_OBJECTS) + +# gnulib modules diff --git a/m4/.cvsignore b/m4/.cvsignore new file mode 100644 index 0000000..cb68a60 --- /dev/null +++ b/m4/.cvsignore @@ -0,0 +1,70 @@ +alloca.m4 +argp.m4 +codeset.m4 +debug.m4 +dirname.m4 +dos.m4 +eoverflow.m4 +error.m4 +extensions.m4 +fileblocks.m4 +getopt.m4 +gettext.m4 +gettext_gl.m4 +glibc2.m4 +glibc21.m4 +glibc21_gl.m4 +gnulib.m4 +iconv.m4 +intdiv0.m4 +intmax.m4 +intmax_t.m4 +inttostr.m4 +inttypes-pri.m4 +inttypes.m4 +inttypes_h.m4 +inttypes_h_gl.m4 +isc-posix.m4 +lcmessage.m4 +lib-ld.m4 +lib-ld_gl.m4 +lib-link.m4 +lib-prefix.m4 +lib-prefix_gl.m4 +longdouble.m4 +longlong.m4 +mempcpy.m4 +nls.m4 +onceonly.m4 +po.m4 +po_gl.m4 +printf-posix.m4 +progtest.m4 +restrict.m4 +safe-read.m4 +safe-write.m4 +savedir.m4 +signed.m4 +size_max.m4 +ssize_t.m4 +stdbool.m4 +stdint_h.m4 +stdint_h_gl.m4 +strcase.m4 +strchrnul.m4 +strerror_r.m4 +strndup.m4 +strnlen.m4 +strtol.m4 +sysexits.m4 +uintmax_t.m4 +uintmax_t_gl.m4 +ulonglong.m4 +ulonglong_gl.m4 +unlocked-io.m4 +vasnprintf.m4 +vsnprintf.m4 +wchar_t.m4 +wint_t.m4 +xalloc.m4 +xsize.m4 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); diff --git a/paxtest/.cvsignore b/paxtest/.cvsignore new file mode 100644 index 0000000..4fa7de9 --- /dev/null +++ b/paxtest/.cvsignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +paxtest +.deps
\ No newline at end of file diff --git a/paxtest/Makefile.am b/paxtest/Makefile.am new file mode 100644 index 0000000..58bea1a --- /dev/null +++ b/paxtest/Makefile.am @@ -0,0 +1,28 @@ +# 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. + +noinst_PROGRAMS = paxtest +paxtest_SOURCES = paxtest.c +noinst_HEADERS = paxtest.h + +INCLUDES = -I$(top_srcdir)/lib -I../ -I../lib -I../paxlib + +LDADD = ../paxlib/libpax.a ../lib/libgnu.a $(LIBINTL) $(LIBICONV) + diff --git a/paxtest/paxtest.c b/paxtest/paxtest.c new file mode 100644 index 0000000..47eb365 --- /dev/null +++ b/paxtest/paxtest.c @@ -0,0 +1,85 @@ +/* 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. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <paxtest.h> + +#ifndef DEFAULT_BLOCKING_FACTOR +# define DEFAULT_BLOCKING_FACTOR 20 +#endif + +void +xalloc_die (void) +{ + error(1, ENOMEM, "Exiting"); +} + +void +dump (unsigned char *buf, size_t size) +{ + while (size) + { + int i; + for (i = 0; i < 16 && size; i++, size--, buf++) + printf ("%02X ", *buf); + printf ("\n"); + } +} + + +void +read_and_dump (paxbuf_t pbuf) +{ + union block block; + size_t size; + pax_io_status_t rc; + + while ((rc = paxbuf_read (pbuf, block.buffer, sizeof block, &size)) + == pax_io_success) + { + dump (block.buffer, size); + } + if (rc == pax_io_failure) + error (1, 0, "Read error"); +} + +int +main (int argc, char **argv) +{ + paxbuf_t pbuf; + int rc; + + if (argc == 1) + error (1, 0, "Not enough arguments"); + + tar_archive_create (&pbuf, argv[1], 0, PAXBUF_READ, DEFAULT_BLOCKING_FACTOR); + + rc = paxbuf_open (pbuf); + printf ("Open: %d\n", rc); + if (rc) + abort (); + read_and_dump (pbuf); + paxbuf_close (pbuf); + paxbuf_destroy (&pbuf); + return 0; +} diff --git a/paxtest/paxtest.h b/paxtest/paxtest.h new file mode 100644 index 0000000..5aee693 --- /dev/null +++ b/paxtest/paxtest.h @@ -0,0 +1,25 @@ +/* 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 <stdlib.h> +#include <system.h> +#include <paxbuf.h> +#include <tar.h> +#include <pax.h> |