[comp.unix.wizards] Tar and absolute pathnames

gnu@hoptoad.uucp (John Gilmore) (02/02/88)

In article <2561@encore.UUCP> adamm@encore.UUCP (Adam S. Moskowitz) writes:
>Some time back, ted@braggvax posted a query about 4.3BSD tar & absolute path
>names.  Did this issue ever get resolved (other than by telling Ted he was a
>fool for using absolute pathnames)?

I never saw the query, but can maybe provide some info.  Absolute pathnames
worked OK on the 4.2 tar we used at Sun.  The bad part was that I would
occasionally receive tapes written by amateurs, which had absolute path
names.  This made it hard to extract them into, say, my home directory.

My PD tar (in the comp.sources.unix archives) automatically strips off
a leading "/", both when creating a tape and when extracting one.  In both
cases it warns you about it.  Recently I got a request from David Vezie
at r2d2, mentioning that to move things from machine to machine he often
does:

	tar cf - /path/path/dir | rsh machine tar xvpf -

but if "rsh" won't put him in the root directory, with pdtar he has to:

	tar cf - /path/path/dir | rsh machine 'cd / ; tar xvpf -'

This is the only half-reasonable use of absolute pathnames in tar that
I've ever seen.  I advised him to make a shell script that just does
the second form, since it works with both tars.
-- 
{pyramid,ptsfa,amdahl,sun,ihnp4}!hoptoad!gnu			  gnu@toad.com
		"Watch me change my world..." -- Liquid Theatre

phil@sequent.UUCP (Phil Hochstetler) (02/04/88)

I once ran into a tape written this way and devised a quick filter that
gets around this problem.  Basically, you can process the tar header
blocks moving the pathname up to get rid of the leading '/' but have to
be sure the header checksum is correct.  My quick solution is to move
the '/' to the end of the pathname array, after the terminating null
character.  This means you can then do:

	tarfix < absolute.archive.tar | tar -xpvBf -

to extract the archive correctly.  Code follows:

---- tarfix.c
main()
{
	char fixbuf[512];

	while (read(0, fixbuf, sizeof (fixbuf)) == sizeof (fixbuf)) {
		if (fixbuf[0] == '/' && fixbuf[99] == 0) {
			strcpy(fixbuf, &fixbuf[1]);
			fixbuf[99] = '/';
		}
		write(1, fixbuf, sizeof (fixbuf));
	}
}
----
Phil Hochstetler
Sequent Computer Systems
Beaverton, Oregon

bd@hpsemc.HP.COM (bob desinger) (02/12/88)

I received tar tapes with absolute pathnames enough times to write a
script to fix the problem, at least on System V.  The basic idea is to
do a chroot to the local directory (say, your $HOME) before reading the
tape.

Complications set in because you need to have the tar binary under the
new root; easy to fix by copying tar to the current directory.
Additionally, you need to make the node of the tape device from which
tar reads.  Only root can do the mknod on System V, so this script
wants you to be root.

The below script has System-V-isms in it (the pathname to tar is
hardwired, cringe; the /dev entry is specific to SysV; the chroot
command is in SysIII/V/Xenix but not BSD).  But at least it works.

Come to think of it, it runs `whoami', a BSD-ism that runs on several
System V implementations including HP-UX.  So it's not vanilla SysV.
Okay, for both of you who don't have `whoami' on your SysV box out
there, replace whoami with:

	id | sed -e 's/^[^(][^(]*(//' -e 's/).*$//'

bob desinger
bd%hpsemc@hplabs.HP.COM		{ucbvax, uunet}!hpda!hpsemc!bd

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unwrap it by saving it in a file and typing "sh file".
#
# Wrapped by bd at hpsemc on Thu Feb 11 17:17:36 1988
# Contents:
#	readtape 	

PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:$PATH; export PATH
echo 'At the end, you should see the message "End of shell archive."'


echo Extracting readtape
cat >readtape <<'@//E*O*F readtape//'
#! /bin/sh
: Reads a tar tape written with absolute pathnames into a relative path.
# @(#)readtape	1.2	10/08/86
# Usage:
#	[1] % cd /some/directory/somewhere
#	[2] % cp `which readtape` readtape
#	[3] % su root -c "cd /some/directory/somewhere && ./readtape"

me=`basename $0`
USAGE="Usage (a three-step process):
	% cd /some/directory/somewhere
	% cp /usr/public/bin/readtape readtape
	% su root -c \"cd \`pwd\` && ./readtape\""

# We assume we're in the place where we want the files to land.
# We also assume that readtape is here already and that you're super-user now,
# but make sure of that first.

if [ ! -f ./readtape ]
then	echo >&2 "$me:  Sorry, no readtape in `pwd`"
	echo >&2 "$USAGE"
	exit 1
elif [ ! -x ./readtape ]
then	echo >&2 "$me:  Sorry, ./readtape isn't executable"
	echo >&2 "$USAGE"
	exit 1
elif [ `whoami` != root ]
then	echo >&2 "$me:  Sorry, you must be root to run readtape"
	echo >&2 "$USAGE"
	exit 1
elif [ $# -ne 0 ]
then	echo >&2 "$USAGE"
	exit 1
fi

# Here we know an executable readtape is here and that we're super-user.
# Get the tar program into a known place.

cp /usr/bin/tar tar

# Create the mag tape device file from which tar will read.

set - `ls -l /dev/rmt/0m`
# crw-rw-rw-   1 root     other      5 0x020000 Aug 22 14:30 /dev/rmt/0m
mkdir dev dev/rmt
mknod dev/rmt/0m c $5 $6
chmod 666 dev/rmt/0m

# Now read in the tape.

/etc/chroot `pwd` ./tar xp
tarstatus=$?

# Clean up after yourself.

dir=`pwd`
test $dir != /		&& rm -rf dev
test $dir != /usr/bin	&& rm -f  tar

# Exit, returning the status returned by tar.
exit $tarstatus
@//E*O*F readtape//

set `wc -lwc <readtape`
if test $1 -ne 61 -o $2 -ne 281 -o $3 -ne 1578
then	echo ! readtape should have 61 lines, 281 words, and 1578 characters
	echo ! but has $1 lines, $2 words, and $3 characters
fi
chmod 555 readtape

echo "End of shell archive."
exit 0

edward@csvaxa.UUCP (Edward Wilkinson) (03/08/88)

I, too, have had this problem.  So I wrote a small  fix to the  PD tar
floating around. Not very fancy, but it seems to work:

----------cut-here-------------cut-here-------------cut-here----------

*** tar.c      Thu Mar  3 15:50:30 1988
--- tar.c~     Mon Oct 26 18:33:19 1987
***************
*** 102,108
               ar_file = DEF_AR_FILE;  /* From Makefile */

       /* Parse options */
!      while ((c = getoldopt(argc, argv, "b:BcdDf:ahikmopstT:vxzZ")
               ) != EOF) {
               switch (c) {


--- 102,108 -----
               ar_file = DEF_AR_FILE;  /* From Makefile */

       /* Parse options */
!      while ((c = getoldopt(argc, argv, "b:BcdDf:hikmopstT:vxzZ")
               ) != EOF) {
               switch (c) {

***************
*** 179,188
                       f_namefile++;
                       break;

-              case 'a':
-                      f_crunch_slash++;
-                      break;
-
               case 'v':
                       f_verbose++;
                       break;

--- 179,184 -----
                       f_namefile++;
                       break;

               case 'v':
                       f_verbose++;
                       break;
***************
*** 215,221
  {

       fputs("tar: valid options:\n\
- -a   remove any leading / from absolute pathnames when extracting\n\
  -b N blocking factor N (block size = Nx512 bytes)\n\
  -B   reblock as we read (for reading 4.2BSD pipes)\n\
  -c   create an archive\n\

--- 211,216 -----
  {

       fputs("tar: valid options:\n\
  -b N blocking factor N (block size = Nx512 bytes)\n\
  -B   reblock as we read (for reading 4.2BSD pipes)\n\
  -c   create an archive\n\
*** tar.h      Thu Mar  3 14:56:04 1988
--- tar.h~     Mon Oct 26 18:33:22 1987
***************
*** 109,115
  TAR_EXTERN char      f_verbose;              /* -v */
  TAR_EXTERN char      f_extract;              /* -x */
  TAR_EXTERN char      f_compress;             /* -z */
- TAR_EXTERN char      f_crunch_slash;         /* -a */

  /*
   * We now default to Unix Standard format rather than 4.2BSD tar format.

--- 109,114 -----
  TAR_EXTERN char      f_verbose;              /* -v */
  TAR_EXTERN char      f_extract;              /* -x */
  TAR_EXTERN char      f_compress;             /* -z */

  /*
   * We now default to Unix Standard format rather than 4.2BSD tar format.
*** extract.c  Thu Mar  3 15:38:37 1988
--- extract.c~ Mon Oct 26 18:33:19 1987
***************
*** 106,120
       if (f_verbose)
               print_header(stdout);

-      /* if -a then crunch initial / on pathname */
-      if (f_crunch_slash)
-        if (head->header.name[0] == '/') {
-          char tmp[4096];
-
-          strcpy(tmp, head->header.name + 1);
-          strcpy(head->header.name, tmp);
-        }
-
       switch (head->header.linkflag) {

       default:

--- 106,111 -----
       if (f_verbose)
               print_header(stdout);

       switch (head->header.linkflag) {

       d