[comp.os.minix] Tar

PRMJ@cornella (Network Wiz) (03/31/88)

I haven't had a chance to use it yet since I haven't gotten my hard disk up yet
but it definitely sounds like something I'm going to want to.  I'd say post the
final vers & src.  Thankx.

dal@syntel.UUCP (Dale Schumacher) (08/28/89)

The 'tar' program that was distributed with Minix-ST had a number of bugs
in it which I ran into while trying to move ~11Mb of data from my old hard
disk (which I sold) to my new hard disk.  I'm posting my fixed version in
hopes that I may save someone else the pain I went through.

Here are some of the bugs I found.  The file type flags were being tested
in such a way as to make certain special files look like directories, etc.
Filenames containing 14 characters were not handled properly, which is a
common bug since such filename are not NUL terminated in the archive header.
The filename '-' is now understood to mean read from stdin or write to
stdout, as appropriate.  All error messages are now written to stderr
rather than stdout.  Other improvements were made particularly to the output
routines.

In addition to fixing the bugs I could find, I've added a couple of
extremely useful options.  The first is the '-v' flag for verbose output,
which was essentially default before.  The second is '-o' for adopting
the owner/group of the extractor rather than that on tape, which again
was default before.  The old default behaviour was, however, quite annoying
when trying to backup and restore an entire file-system where I definately
wanted the owner/group restored to what I was in the tar-file.  The final
option is '-F', a non-standard option which forces 'tar' to ignore certain
error conditions which otherwise caused it to quit processing a tar-file.
This is intended to give a chance of properly restoring files which come
after a corrupted segment of the tar-file.

Enjoy.

------------------------------------------------------------------------
/* tar - tape archiver			Author: Michiel Huisjes */

/* Usage: tar [cxt][vo][F] tapefile [files]
 *
 * Bugs:
 *	This tape archiver should only be used as a program to read or make
 *	simple tape archives. Its basic goal is to read (or build) UNIX V7 tape
 *	archives and extract the named files. It is not wise to use it on
 *	raw magnetic tapes. It doesn't know anything about linked files,
 *	except when the involved fields are filled in.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>

typedef char BOOL;
#define TRUE	1
#define FALSE	0

#define HEADER_SIZE	512
#define NAME_SIZE	100
#define BLOCK_BOUNDARY	 20

typedef union {
  char hdr_block[HEADER_SIZE];
  struct m {
	char m_name[NAME_SIZE];
	char m_mode[8];
	char m_uid[8];
	char m_gid[8];
	char m_size[12];
	char m_time[12];
	char m_checksum[8];
	char m_linked;
	char m_link[NAME_SIZE];
  } member;
} HEADER;

HEADER header;

#define INT_TYPE	(sizeof(header.member.m_uid))
#define LONG_TYPE	(sizeof(header.member.m_size))

#define MKDIR		"/bin/mkdir"

#define NIL_HEADER	((HEADER *) 0)
#define NIL_PTR		((char *) 0)
#define BLOCK_SIZE	512

#define flush()		print(NIL_PTR)

BOOL show_fl, creat_fl, ext_fl;

int tar_fd;
/* char usage[] = "Usage: tar [cxt] tarfile [files]."; */
char usage[] = "Usage: tar [cxt][vo][F] tarfile [files].";
char io_buffer[BLOCK_SIZE];
char path[NAME_SIZE];
char pathname[NAME_SIZE];
int force_flag = 0;
#ifdef ORIGINAL_DEFAULTS
int chown_flag = 1;
int verbose_flag = 1;
#else
int chown_flag = 0;
int verbose_flag = 0;
#endif

int total_blocks;
long convert();

#define block_size()	(int) ((convert(header.member.m_size, LONG_TYPE) \
			+ (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE)

error(s1, s2)
char *s1, *s2;
{
  string_print(NIL_PTR, "%s %s\n", s1, s2 ? s2 : "");
  flush();
  exit(1);
}

main(argc, argv)
int argc;
register char *argv[];
{
  register char *ptr;
  int i;

  if (argc < 3)
	error(usage, NIL_PTR);
  
  for (ptr = argv[1]; *ptr; ptr++) {
	switch (*ptr) {
		case 'c' :
			creat_fl = TRUE;
			break;
		case 'x' :
			ext_fl = TRUE;
			break;
		case 't' :
			show_fl = TRUE;
			break;
		case 'v' :			/* verbose output  -Dal */
			verbose_flag = !verbose_flag;
			break;
		case 'o' :			/* chown/chgrp files  -Dal */
			chown_flag = TRUE;
			break;
		case 'F' :			/* IGNORE ERRORS  -Dal */
			force_flag = TRUE;
			break;
		default :
			error(usage, NIL_PTR);
	}
  }

  if (creat_fl + ext_fl + show_fl != 1) 
	error(usage, NIL_PTR);
  
  if (argv[2][0] == '-')
	tar_fd = creat_fl ? 1 : 0;	/* '-' means used stdin/stdout  -Dal */
  else
	tar_fd = creat_fl ? creat(argv[2], 0644) : open(argv[2], 0);

  if (tar_fd < 0)
	error("Cannot open ", argv[2]);

  if (creat_fl) {
	for (i = 3; i < argc; i++) {
		add_file(argv[i]);
		path[0] = '\0';
	}
	adjust_boundary();
  }
  else
	tarfile();

  flush();
  exit(0);
}

BOOL get_header()
{
  register int check;

  mread(tar_fd, &header, sizeof(header));
  if (header.member.m_name[0] == '\0')
	return FALSE;

  if (force_flag)		/* skip checksum verification  -Dal */
	return TRUE;

  check = (int) convert(header.member.m_checksum, INT_TYPE);
  
  if (check != checksum())
	error("Tar: header checksum error.", NIL_PTR);

  return TRUE;
}

tarfile()
{
  register char *ptr;
  register char *mem_name;

  while (get_header()) {
	mem_name = header.member.m_name;
	if (ext_fl) {
		if (is_dir(mem_name)) {
			for (ptr = mem_name; *ptr; ptr++)
				;
			*(ptr - 1) = '\0';
			mkdir(mem_name);
		}
		else
			extract(mem_name);
	}
	else  {
		string_print(NIL_PTR, "%s%s", mem_name,
				(verbose_flag ? " " : "\n"));
		if (header.member.m_linked == '1') {
			if (verbose_flag)
				string_print(NIL_PTR, "linked to %s\n",
							  header.member.m_link);
		}
		else {
			if (verbose_flag)
				string_print(NIL_PTR, "%d tape blocks\n",
							block_size());
			skip_entry();
		}
	}
	flush();
  }
}

skip_entry()
{
  register int blocks = block_size();

  while (blocks--)
	(void) read(tar_fd, io_buffer, BLOCK_SIZE);
}

extract(file)
register char *file;
{
  register int fd;

  if (header.member.m_linked == '1') {
	if (link(header.member.m_link, file) < 0)
		string_print(NIL_PTR, "Cannot link %s to %s\n",
						    header.member.m_link, file);
	else if (verbose_flag)
		string_print(NIL_PTR, "Linked %s to %s\n",
						    header.member.m_link, file);
	return;
  }

  if ((fd = creat(file, 0644)) < 0) {
	string_print(NIL_PTR, "Cannot create %s\n", file);
	return;
  }

  copy(file, tar_fd, fd, convert(header.member.m_size, LONG_TYPE));
  (void) close(fd);

  chmod(file, (int)convert(header.member.m_mode, INT_TYPE));

  if(!chown_flag) {		/* set correct owner and group  -Dal */
	chown(file, (int)convert(header.member.m_uid, INT_TYPE),
		    (int)convert(header.member.m_gid, INT_TYPE));
  }

  /* should the timestamp be updated here??  -Dal */

  flush();
}

copy(file, from, to, bytes)
char *file;
int from, to;
register long bytes;
{
  register int rest;
  int blocks = (int) ((bytes + (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE);

  if (verbose_flag)
	string_print(NIL_PTR, "%s, %d tape blocks\n", file, blocks);

  while (blocks--) {
	(void) read(from, io_buffer, BLOCK_SIZE);
	rest = (bytes > (long) BLOCK_SIZE) ? BLOCK_SIZE : (int) bytes;
	mwrite(to, io_buffer, (to == tar_fd) ? BLOCK_SIZE : rest);
	bytes -= (long) rest;
  }
}

long convert(str, type)
char str[];
int type;
{
  register long ac = 0L;
  register int i;

  for (i = 0; i < type; i++) {
	if (str[i] >= '0' && str[i] <= '7') {
		ac <<= 3;
		ac += (long) (str[i] - '0');
	}
  }

  return ac;
}

mkdir(dir_name)
char *dir_name;
{
  register int pid, w;

  if ((dir_name[0] == '.') && ((dir_name[1] == '\0') || (dir_name[1] == '.')))
	return;

  if ((pid = fork()) < 0)
	error("Cannot fork().", NIL_PTR);
  
  if (pid == 0) {
	execl(MKDIR, "mkdir", dir_name, (char *) 0);
	error("Cannot find mkdir.", NIL_PTR);
  }

  do {
	w = wait((int *) 0);
  } while (w != -1 && w != pid);
}

checksum()
{
  register char *ptr = header.member.m_checksum;
  register int ac = 0;

  while (ptr < &header.member.m_checksum[INT_TYPE])
	*ptr++ = ' ';

  ptr = header.hdr_block;
  while (ptr < &header.hdr_block[BLOCK_SIZE])
	ac += *ptr++;

  return ac;
}

is_dir(file)
register char *file;
{
  while (*file++ != '\0')
	;

  return (*(file - 2) == '/');
}


char *path_name(file)
register char *file;
{

  string_print(pathname, "%s%s", path, file);
  return pathname;
}

add_path(name)
register char *name;
{
  register char *path_ptr = path;

  while (*path_ptr)
	path_ptr++;
  
  if (name == NIL_PTR) {
	while (*path_ptr-- != '/')
		;
	while (*path_ptr != '/' && path_ptr != path)
		path_ptr--;
	if (*path_ptr == '/')
		path_ptr++;
	*path_ptr = '\0';
  }
  else {
	while (*name) {
		if (path_ptr == &path[NAME_SIZE])
			error("Pathname too long", NIL_PTR);
		*path_ptr++ = *name++;
	}
	*path_ptr++ = '/';
	*path_ptr = '\0';
  }
}

add_file(file)
register char *file;
{
  struct stat st;
  struct direct dir;
  register int fd;
  char namebuf[16];	/* -Dal */

  if (stat(file, &st) < 0) {
	string_print(NIL_PTR, "Cannot find %s\n", file);
	return;
  }
  if ((fd = open(file, 0)) < 0) {
	string_print(NIL_PTR, "Cannot open %s\n", file);
	return;
  }

  make_header(path_name(file), &st);
  if ((st.st_mode & S_IFMT) == S_IFREG)	{	/* fixed test  -Dal */
	mwrite(tar_fd, &header, sizeof(header));
	copy(path_name(file), fd, tar_fd, st.st_size);
  }
  else if ((st.st_mode & S_IFMT) == S_IFDIR) {	/* fixed test  -Dal */
	mwrite(tar_fd, &header, sizeof(header));
	if (chdir(file) < 0)
		string_print(NIL_PTR, "Cannot chdir to %s\n", file);
	else {
		add_path(file);
		mread(fd, &dir, sizeof(dir));		/* "." */
		mread(fd, &dir, sizeof(dir));		/* ".." */
		while (read(fd, &dir, sizeof(dir)) == sizeof(dir))
			if (dir.d_ino) {
				strncpy(namebuf, dir.d_name, 14);
				namebuf[14] = '\0';
				add_file(namebuf);
			}
		chdir("..");
		add_path(NIL_PTR);
	}
  }
  else
	print(" Tar: unknown file type. Not added.\n");

  (void) close(fd);
}

make_header(file, st)
char *file;
register struct stat *st;
{
  register char *ptr = header.member.m_name;

  clear_header();

  while (*ptr++ = *file++)
	;

  if ((st->st_mode & S_IFMT) == S_IFDIR) {	/* fixed test  -Dal */
	*(ptr - 1) = '/';
	st->st_size = 0L;
  }
  
  string_print(header.member.m_mode, "%I ", st->st_mode & 07777);
  string_print(header.member.m_uid, "%I ", st->st_uid);
  string_print(header.member.m_gid, "%I ", st->st_gid);
  string_print(header.member.m_size, "%L ", st->st_size);
  string_print(header.member.m_time, "%L ", st->st_mtime);
  header.member.m_linked = ' ';
  string_print(header.member.m_checksum, "%I ", checksum());
  header.member.m_linked = ' ';
}

clear_header()
{
  register char *ptr = header.hdr_block;

  while (ptr < &header.hdr_block[BLOCK_SIZE])
	*ptr++ = '\0';
}

adjust_boundary()
{
  clear_header();
  mwrite(tar_fd, &header, sizeof(header));

  while (total_blocks++ < BLOCK_BOUNDARY)
	mwrite(tar_fd, &header, sizeof(header));
  (void) close(tar_fd);
}

mread(fd, address, bytes)
int fd, bytes;
char *address;
{
  if (read(fd, address, bytes) != bytes)
	error("Tar: read error.", NIL_PTR);
}

mwrite(fd, address, bytes)
int fd, bytes;
char *address;
{
  if (write(fd, address, bytes) != bytes)
	error("Tar: write error.", NIL_PTR);

  total_blocks++;
}

char output[BLOCK_SIZE];
print(str)		/* changed to use stderr rather than stdout  -Dal */
register char *str;
{
  static int index = 0;

  if (str == NIL_PTR) {
	write(2, output, index);
	index = 0;
	return;
  }

  while (*str) {
	output[index++] = *str++;
	if (index == BLOCK_SIZE) {
		write(2, output, BLOCK_SIZE);
		index = 0;
	}
  }
}

char *num_out(number)
register long number;
{
  static char num_buf[12];
  register int i;

  for (i = 11; i--; ) {
	num_buf[i] = (number & 07) + '0';
	number >>= 3;
  }

  return num_buf;
}

/* VARARGS */
string_print(buffer, fmt, args)
char *buffer;
register char *fmt;
int args;
{
  register char *buf_ptr;
  char *scan_ptr;
  char buf[NAME_SIZE];
  char *argptr = (char *)&args;
  BOOL pr_fl, i;

  if (pr_fl = (buffer == NIL_PTR))
	buffer = buf;

  buf_ptr = buffer;
  while (*fmt) {
	if (*fmt == '%') {
		fmt++;
		switch (*fmt++) {
			case 's': 
				scan_ptr = *((char **)argptr);
				argptr += sizeof(char *);
				break;
			case 'I': 
				scan_ptr = num_out((long) *((int *)argptr));
				argptr += sizeof(int);
				for (i = 0; i < 5; i++)
					scan_ptr++;
				break;
			case 'L': 
				scan_ptr = num_out(*((long *) argptr));
				argptr += sizeof(long);
				break;
			case 'd' :
				scan_ptr = num_out((long) *((int *)argptr));
				argptr += sizeof(int);
				while (*scan_ptr == '0')
					scan_ptr++;
				scan_ptr--;
				break;
			default: 
				scan_ptr = "";
		}
		while (*buf_ptr++ = *scan_ptr++)
			;
		buf_ptr--;
	}
	else
		*buf_ptr++ = *fmt++;
  }
  *buf_ptr = '\0';

  if (pr_fl)
	print(buffer);
}
------------------------------------------------------------------------

\\   /  Dale Schumacher                         399 Beacon Ave.
 \\ /   (alias: Dalnefre')                      St. Paul, MN  55104-3527
  ><    ...umn-cs!midgard.mn.org!syntel!dal     United States of America
 / \\   "What is wanted is not the will to believe, but the will to find out,
/   \\  which is the exact opposite." -Bertrand Russell

kjh@pollux.usc.edu (Kenneth J. Hendrickson) (02/17/91)

A while ago, there was much comment about tar not working.  I just ran
into the problem.

I did		tar c - . | compress | vol 1440 /dev/ps1
and		vol -u 1440 /dev/ps1 | uncompress | tar x - .

and I found that tar couldn't create directories!  :-(

THIS MAKES TAR USELESS!!!!!

You can't use tar for backup purposes, or for moving files between
machines, if any subdirectories are involved.

Has anybody fixed this?  Or ported some other version of tar?

I am trying to set up a new Minix machine, and I need a working tar.
Thanks very much for responding either by email, or posting.

-- 
favourite oxymorons:   student athlete, military justice, mercy killing
Ken Hendrickson N8DGN/6       kjh@usc.edu      ...!uunet!usc!pollux!kjh

kjh@pollux.usc.edu (Kenneth J. Hendrickson) (02/17/91)

In reference to getting a working tar for Minix, please don't suggest
pdtar unless it is newer than Nov 87.  I got that version from
uunet.uu.net, and tried to compile it.  The standard Minix C compiler
(ACK) barfed on it.  It appears that /lib/opt was running out of memory,
and it had already been chmem'd to the maximum.

If there is a working/compilable pdtar for Minix, please tell me where
to get it.  Thanks very much.

-- 
favourite oxymorons:   student athlete, military justice, mercy killing
Ken Hendrickson N8DGN/6       kjh@usc.edu      ...!uunet!usc!pollux!kjh

EIGALY%yubgef51@pucc.princeton.edu (Cedomir Igaly, +38-41-420-639) (02/18/91)

>A while ago, there was much comment about tar not working.  I just ran
>into the problem.
>
>I did           tar c - . | compress | vol 1440 /dev/ps1
>and             vol -u 1440 /dev/ps1 | uncompress | tar x - .
>
>and I found that tar couldn't create directories!  :-(
>
>THIS MAKES TAR USELESS!!!!!
>
>You can't use tar for backup purposes, or for moving files between
>machines, if any subdirectories are involved.
>
>Has anybody fixed this?  Or ported some other version of tar?

Yes. Tar trying to start mkdir as /bin/mkdir, and you probably have
mkdir in /usr/bin. Solution is simple - just put mkdir in /bin, or
change definition in tar.c. This last works fine for me.

Cedomir Igaly

kjh@pollux.usc.edu (Kenneth J. Hendrickson) (02/18/91)

**********	tar solution	**********

Make sure you have mkdir in /bin/mkdir.  tar requires this, if it has to
create any directories.  RTFsource code.  :-)		/* I did. */

-- 
favourite oxymorons:   student athlete, military justice, mercy killing
Ken Hendrickson N8DGN/6       kjh@usc.edu      ...!uunet!usc!pollux!kjh

root@demug.de.mugnet.org (02/19/91)

In article <30463@usc>, kjh@pollux.usc.edu (Kenneth J. Hendrickson) wrote:
> A while ago, there was much comment about tar not working.  I just ran
> into the problem.
> 
> I did		tar c - . | compress | vol 1440 /dev/ps1
> and		vol -u 1440 /dev/ps1 | uncompress | tar x - .
I always use:

	(cd /; tar cfv - *) | to TAPEIT

with TAPEIT being a command line like:

	from TAPEIT | (cd /shadow; tar xfv -)

on my other ("uwalt") system. This effectively creates a pipe over the
Ethernet, and the entire file system of "minixug" will be copied to
"uwalt" in directory /shadow.

This is done each week. After this backup, "uwalt" starts to yell at its
tape drive, which completes the process...

Each night, my system does:

	(cd /; tar cf - etc usr/lib/news usr/lib/uucp usr/spool/mail) |
		vol 1200 /dev/at0

which backups some important directories to disk each time. It works OK.

> and I found that tar couldn't create directories!  :-(
I must admit: I use John Gilmore's PDTAR program, since that understands
about much more types of special files than the MINIX tar. However, the
standard MINIX tar does its work OK; it makes directories...

> THIS MAKES TAR USELESS!!!!!
Yes.  What TAR are you using?

> You can't use tar for backup purposes, or for moving files between
> machines, if any subdirectories are involved.
> 
> Has anybody fixed this?  Or ported some other version of tar?
See above: use PDTAR, available from me :-).  I can post it if you
are not the only one interested in it.....

> I am trying to set up a new Minix machine, and I need a working tar.
> Thanks very much for responding either by email, or posting.

Hurray, another MINIX box.  Look at this header; this is also a newly
set up MINIX machine... :-)

Cheers,
	Fred.

HBO043%DJUKFA11.BITNET@cunyvm.cuny.edu (Christoph van Wuellen) (02/20/91)

I compiled GNU-tar 1.09 just recently, but I have MINIX-68K so I cannot
say if it works on a PC.
A lot of MINIX stuff has to be REMOVED (the 'MINIX' port handles deficiencies
of an early MINIX version, like the open3 stuff etc).

C.v.W.
P.S.
The MINIX tar is unusuable since you cannot specify a blocking factor.
Try to write the tar archive to a disk with 1K-sectors and you know what
I mean. I got rid of setting up a pipe with a (modified) dd all the time.

C.v.W.

kjh@pollux.usc.edu (Kenneth J. Hendrickson) (02/22/91)

In article <910219395@demug.de.mugnet.org> root@demug.de.mugnet.org writes:
>I must admit: I use John Gilmore's PDTAR program
>See above: use PDTAR, available from me :-).  I can post it if you
>are not the only one interested in it.....

I am very interested.  I got the most recent version of pdtar from
uunet.uunet, and I found that it wouldn't compile.  I didn't even look
at it any further.  Please post your fixes to pktar.  Thank you.

-- 
favourite oxymorons:   student athlete, military justice, mercy killing
Ken Hendrickson N8DGN/6       kjh@usc.edu      ...!uunet!usc!pollux!kjh

klamer@mi.eltn.utwente.nl (Klamer Schutte) (04/08/91)

In <3910@bruce.cs.monash.OZ.AU> cechew@bruce.cs.monash.OZ.AU (Earl Chew) writes:

>I wonder how many people have given up on tar and are using gnu-tar or pax?

What does the minix society want? You can use minix-tar, but it is only
tested on small file systems (I made a lot of changes when i didn't have
a hard disk. Also, on an atari (on any 68k machine, or a 386) the memory
problems are not a problem -- it aren't really memory leaks, just
inefficient ways (when a memory leak is a recursive recurrent 
alloc-but-no-free problem).

For POSIX, we need pax. Do we want a tar as well? If we want, i will
incorporate all fixes (approx. 4-5 now) into tar.
Please mail me. Or post if general interest.

Klamer
-- 
Klamer Schutte
Faculty of electrical engineering -- University of Twente, The Netherlands
klamer@mi.eltn.utwente.nl	{backbone}!mcsun!mi.eltn.utwente.nl!klamer

mitchell@MDI.COM (Bill Mitchell) (04/09/91)

In article <klamer.671112990@mi.eltn.utwente.nl> klamer@mi.eltn.utwente.nl (Klamer Schutte) writes:
>
>For POSIX, we need pax. Do we want a tar as well? 

As long as the outside world still uses tar, and there is a possibility
that we'll want to be able to un-tar tarfiles generated in the outside
world or send tarfiles to someone in the outside world who lacks pax
we'll still need tar.  Right?

-- 
mitchell@mdi.com (Bill Mitchell)

klamer@mi.eltn.utwente.nl (Klamer Schutte) (04/10/91)

In <1991Apr8.213707.7070@MDI.COM> mitchell@MDI.COM (Bill Mitchell) writes:

>In article <klamer.671112990@mi.eltn.utwente.nl> klamer@mi.eltn.utwente.nl (Klamer Schutte) writes:
>>
>>For POSIX, we need pax. Do we want a tar as well? 

>As long as the outside world still uses tar, and there is a possibility
>that we'll want to be able to un-tar tarfiles generated in the outside
>world or send tarfiles to someone in the outside world who lacks pax
>we'll still need tar.  Right?

The current minix tar can read both pax (POSIX tar) and old UNIX tar
archives. I will be suprised if pax can't -- but will try it.

Klamer
-- 
Klamer Schutte
Faculty of electrical engineering -- University of Twente, The Netherlands
klamer@mi.eltn.utwente.nl	{backbone}!mcsun!mi.eltn.utwente.nl!klamer

X913%DMAFHT1.BITNET@cunyvm.cuny.edu (Martin Sckopke) (04/10/91)

I'm not sure about still needing tar, cause the pax version I know of
supports pax, tar and afio. But I also remember it was quite big, so I am
not sure if it'll compile and run on PC-Minix. It does run on ST-Minix though.

                                       ! This space intentionally
       Martin Sckopke (X913@DMAFHT1)   !
                                       !    left blank.