Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!csd4.csd.uwm.edu!uxc.cso.uiuc.edu!tank!shamash!com50!midgard!syntel!dal From: dal@syntel.UUCP (Dale Schumacher) Newsgroups: comp.os.minix Subject: tar Summary: This version of tar fixes many bugs in the distribution version Keywords: tar, Minix-ST Message-ID: <072889A3579@syntel.UUCP> Date: 28 Aug 89 13:38:54 GMT Reply-To: dal@syntel.UUCP (Dale Schumacher) Lines: 594 X-Member-Of: STdNET (ST Developer's Network) 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 #include #include 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