Пример #1
0
static int
is_tar(unsigned char *buf, size_t nbytes)
{
    register union record *header = (union record *) buf;
    register int i;
    register long sum, recsum;
    register char *p;

    if (nbytes < sizeof(union record))
       return 0;

    recsum = from_oct(8, header->header.chksum);
    sum = 0;
    p = header->charptr;
    for (i = sizeof(union record); --i >= 0;) {
        /*
         * We can't use unsigned char here because of old compilers, e.g. V7.
         */
        sum += 0xFF & *p++;
    }

    /* Adjust checksum to count the "chksum" field as blanks. */
    for (i = sizeof(header->header.chksum); --i >= 0;)
        sum -= 0xFF & header->header.chksum[i];
    sum += ' ' * sizeof header->header.chksum;

    if (sum != recsum)
        return 0;       /* Not a tar archive */

    if (0 == strcmp(header->header.magic, TMAGIC))
        return 2;       /* Unix Standard tar archive */

    return 1;           /* Old fashioned tar archive */
}
Пример #2
0
static void fill_stat_from_header (vfs *me, struct stat *st, union record *header) 
{
    st->st_mode = from_oct (8, header->header.mode);

    /* Adjust st->st_mode because there are tar-files with
     * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't 
     * know about the other modes but I think I cause no new
     * problem when I adjust them, too. -- Norbert.
     */
    if (header->header.linkflag == LF_DIR) {
	st->st_mode |= S_IFDIR;
    } else if (header->header.linkflag == LF_SYMLINK) {
	st->st_mode |= S_IFLNK;
    } else if (header->header.linkflag == LF_CHR) {
	st->st_mode |= S_IFCHR;
    } else if (header->header.linkflag == LF_BLK) {
	st->st_mode |= S_IFBLK;
    } else if (header->header.linkflag == LF_FIFO) {
	st->st_mode |= S_IFIFO;
    } else 
	st->st_mode |= S_IFREG;

    st->st_rdev = 0;
    if (!strcmp (header->header.magic, TMAGIC)) {
	st->st_uid = *header->header.uname ? finduid (header->header.uname) :
	    from_oct (8, header->header.uid);
	st->st_gid = *header->header.gname ? findgid (header->header.gname) :
	    from_oct (8, header->header.gid);
	switch (header->header.linkflag) {
	case LF_BLK:
	case LF_CHR:
	    st->st_rdev = (from_oct (8, header->header.devmajor) << 8) |
		from_oct (8, header->header.devminor);
	}
    } else { /* Old Unix tar */
	st->st_uid = from_oct (8, header->header.uid);
	st->st_gid = from_oct (8, header->header.gid);
    }
    st->st_size = hstat.st_size;
    st->st_mtime = from_oct (1 + 12, header->header.mtime);
    st->st_atime = from_oct (1 + 12, header->header.atime);
    st->st_ctime = from_oct (1 + 12, header->header.ctime);
}
Пример #3
0
/*
 * Extract a file from the archive.
 */
void
extract_archive ()
{
  register char *data;
  int fd, check, namelen, written, openflag;
  long size;
  struct utimbuf acc_upd_times;
  register int skipcrud;
  register int i;
  /*	int sparse_ind = 0;*/
  union record *exhdr;
  struct saved_dir_info *tmp;
  /*	int end_nulls; */

  saverec (&head);		/* Make sure it sticks around */
  userec (head);		/* And go past it in the archive */
  decode_header (head, &hstat, &head_standard, 1);	/* Snarf fields */

  if ((f_confirm && !confirm ("extract", current_file_name)) ||
      (f_exstdout && head->header.linkflag != LF_OLDNORMAL &&
       head->header.linkflag != LF_NORMAL &&
       head->header.linkflag != LF_CONTIG))
    {
      if (head->header.isextended)
	skip_extended_headers ();
      skip_file ((long) hstat.st_size);
      saverec ((union record **) 0);
      return;
    }

  /* Print the record from 'head' and 'hstat' */
  if (f_verbose)
    print_header ();

  /*
	 * Check for fully specified pathnames and other atrocities.
	 *
	 * Note, we can't just make a pointer to the new file name,
	 * since saverec() might move the header and adjust "head".
	 * We have to start from "head" every time we want to touch
	 * the header record.
	 */
  skipcrud = 0;
  while (!f_absolute_paths
	 && '/' == current_file_name[skipcrud])
    {
      static int warned_once = 0;

      skipcrud++;		/* Force relative path */
      if (!warned_once++)
	{
	  msg ("Removing leading / from absolute path names in the archive.");
	}
    }

  switch (head->header.linkflag)
    {

    default:
      msg ("Unknown file type '%c' for %s, extracted as normal file",
	   head->header.linkflag, skipcrud + current_file_name);
      /* FALL THRU */

      /*
	  * JK - What we want to do if the file is sparse is loop through
	  * the array of sparse structures in the header and read in
	  * and translate the character strings representing  1) the offset
	  * at which to write and 2) how many bytes to write into numbers,
	  * which we store into the scratch array, "sparsearray".  This
	  * array makes our life easier the same way it did in creating
	  * the tar file that had to deal with a sparse file.
	  *
	  * After we read in the first five (at most) sparse structures,
	  * we check to see if the file has an extended header, i.e.,
	  * if more sparse structures are needed to describe the contents
	  * of the new file.  If so, we read in the extended headers
	  * and continue to store their contents into the sparsearray.
	  */
    case LF_SPARSE:
      sp_array_size = 10;
      sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
      for (i = 0; i < SPARSE_IN_HDR; i++)
	{
	  sparsearray[i].offset =
	    from_oct (1 + 12, head->header.sp[i].offset);
	  sparsearray[i].numbytes =
	    from_oct (1 + 12, head->header.sp[i].numbytes);
	  if (!sparsearray[i].numbytes)
	    break;
	}

      /*		end_nulls = from_oct(1+12, head->header.ending_blanks);*/

      if (head->header.isextended)
	{
	  /* read in the list of extended headers
			    and translate them into the sparsearray
			    as before */

	  /* static */ int ind = SPARSE_IN_HDR;

	  for (;;)
	    {

	      exhdr = findrec ();
	      for (i = 0; i < SPARSE_EXT_HDR; i++)
		{

		  if (i + ind > sp_array_size - 1)
		    {
		      /*
					  * realloc the scratch area
					  * since we've run out of room --
					  */
		      sparsearray = (struct sp_array *)
			ck_realloc (sparsearray,
			    2 * sp_array_size * (sizeof (struct sp_array)));
		      sp_array_size *= 2;
		    }
		  if (!exhdr->ext_hdr.sp[i].numbytes)
		    break;
		  sparsearray[i + ind].offset =
		    from_oct (1 + 12, exhdr->ext_hdr.sp[i].offset);
		  sparsearray[i + ind].numbytes =
		    from_oct (1 + 12, exhdr->ext_hdr.sp[i].numbytes);
		}
	      if (!exhdr->ext_hdr.isextended)
		break;
	      else
		{
		  ind += SPARSE_EXT_HDR;
		  userec (exhdr);
		}
	    }
	  userec (exhdr);
	}

      /* FALL THRU */
    case LF_OLDNORMAL:
    case LF_NORMAL:
    case LF_CONTIG:
      /*
		  * Appears to be a file.
		  * See if it's really a directory.
		  */
      namelen = strlen (skipcrud + current_file_name) - 1;
      if (current_file_name[skipcrud + namelen] == '/')
	goto really_dir;

      /* FIXME, deal with protection issues */
    again_file:
      openflag = (f_keep ?
		  O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
		  O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
	| ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND);
      /*
			  * JK - The last | is a kludge to solve the problem
			  * the O_APPEND flag  causes with files we are
			  * trying to make sparse:  when a file is opened
			  * with O_APPEND, it writes  to the last place
			  * that something was written, thereby ignoring
			  * any lseeks that we have done.  We add this
			  * extra condition to make it able to lseek when
			  * a file is sparse, i.e., we don't open the new
			  * file with this flag.  (Grump -- this bug caused
			  * me to waste a good deal of time, I might add)
			  */

      if (f_exstdout)
	{
	  fd = 1;
	  goto extract_file;
	}

      if (f_unlink && !f_keep) {
	if (unlink(skipcrud + current_file_name) == -1)
		if (errno != ENOENT)
		   msg_perror ("Could not unlink %s",
                      skipcrud + current_file_name);
      }

#ifdef O_CTG
      /*
		  * Contiguous files (on the Masscomp) have to specify
		  * the size in the open call that creates them.
		  */
      if (head->header.linkflag == LF_CONTIG)
	fd = open ((longname ? longname : head->header.name)
		   + skipcrud,
		   openflag | O_CTG,
		   hstat.st_mode, hstat.st_size);
      else
#endif
	{
#ifdef NO_OPEN3
	  /*
			  * On raw V7 we won't let them specify -k (f_keep), but
			  * we just bull ahead and create the files.
			  */
	  fd = creat ((longname
		       ? longname
		       : head->header.name) + skipcrud,
		      hstat.st_mode);
#else
	  /*
			  * With 3-arg open(), we can do this up right.
			  */
	  fd = open (skipcrud + current_file_name,
		     openflag, hstat.st_mode);
#endif
	}

      if (fd < 0)
	{
	  if (make_dirs (skipcrud + current_file_name))
	    goto again_file;
	  msg_perror ("Could not create file %s",
		      skipcrud + current_file_name);
	  if (head->header.isextended)
	    skip_extended_headers ();
	  skip_file ((long) hstat.st_size);
	  goto quit;
	}

    extract_file:
      if (head->header.linkflag == LF_SPARSE)
	{
	  char *name;
	  int namelen;

	  /*
			  * Kludge alert.  NAME is assigned to header.name
			  * because during the extraction, the space that
			  * contains the header will get scribbled on, and
			  * the name will get munged, so any error messages
			  * that happen to contain the filename will look
			  * REAL interesting unless we do this.
			  */
	  namelen = strlen (skipcrud + current_file_name) + 1;
	  name = (char *) ck_malloc ((sizeof (char)) * namelen);
	  bcopy (skipcrud + current_file_name, name, namelen);
	  size = hstat.st_size;
	  extract_sparse_file (fd, &size, hstat.st_size, name);
	}
      else
	for (size = hstat.st_size;
	     size > 0;
	     size -= written)
	  {

	    /*			long	offset,
				 numbytes;*/

	    if (f_multivol)
	      {
		save_name = current_file_name;
		save_totsize = hstat.st_size;
		save_sizeleft = size;
	      }

	    /*
			  * Locate data, determine max length
			  * writeable, write it, record that
			  * we have used the data, then check
			  * if the write worked.
			  */
	    data = findrec ()->charptr;
	    if (data == NULL)
	      {			/* Check it... */
		msg ("Unexpected EOF on archive file");
		break;
	      }
	    /*
			  * JK - If the file is sparse, use the sparsearray
			  * that we created before to lseek into the new
			  * file the proper amount, and to see how many
			  * bytes we want to write at that position.
			  */
	    /*			if (head->header.linkflag == LF_SPARSE) {
				 off_t pos;

				 pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0);
				 printf("%d at %d\n", (int) pos, sparse_ind);
				 written = sparsearray[sparse_ind++].numbytes;
			 } else*/
	    written = endofrecs ()->charptr - data;
	    if (written > size)
	      written = size;
	    errno = 0;
	    check = write (fd, data, written);
	    /*
			  * The following is in violation of strict
			  * typing, since the arg to userec
			  * should be a struct rec *.  FIXME.
			  */
	    userec ((union record *) (data + written - 1));
	    if (check == written)
	      continue;
	    /*
			  * Error in writing to file.
			  * Print it, skip to next file in archive.
			  */
	    if (check < 0)
	      msg_perror ("couldn't write to file %s",
			  skipcrud + current_file_name);
	    else
	      msg ("could only write %d of %d bytes to file %s",
		   check, written, skipcrud + current_file_name);
	    skip_file ((long) (size - written));
	    break;		/* Still do the close, mod time, chmod, etc */
	  }

      if (f_multivol)
	save_name = 0;

      /* If writing to stdout, don't try to do anything
			    to the filename; it doesn't exist, or we don't
			    want to touch it anyway */
      if (f_exstdout)
	break;

      /*		if (head->header.isextended) {
			 register union record *exhdr;
			 register int i;

			 for (i = 0; i < 21; i++) {
				 long offset;

				 if (!exhdr->ext_hdr.sp[i].numbytes)
					 break;
				 offset = from_oct(1+12,
						 exhdr->ext_hdr.sp[i].offset);
				 written = from_oct(1+12,
						 exhdr->ext_hdr.sp[i].numbytes);
				 lseek(fd, offset, 0);
				 check = write(fd, data, written);
				 if (check == written) continue;

			 }


		 }*/
      check = close (fd);
      if (check < 0)
	{
	  msg_perror ("Error while closing %s",
		      skipcrud + current_file_name);
	}


    set_filestat:

      /*
		  * If we are root, set the owner and group of the extracted
		  * file.  This does what is wanted both on real Unix and on
		  * System V.  If we are running as a user, we extract as that
		  * user; if running as root, we extract as the original owner.
		  */
      if (we_are_root || f_do_chown)
	{
	  if (chown (skipcrud + current_file_name,
		     hstat.st_uid, hstat.st_gid) < 0)
	    {
	      msg_perror ("cannot chown file %s to uid %d gid %d",
			  skipcrud + current_file_name,
			  hstat.st_uid, hstat.st_gid);
	    }
	}

      /*
       * Set the modified time of the file.
       *
       * Note that we set the accessed time to "now", which
       * is really "the time we started extracting files".
       * unless f_gnudump is used, in which case .st_atime is used
       */
      if (!f_modified)
	{
	  /* fixme if f_gnudump should set ctime too, but how? */
	  if (f_gnudump)
	    acc_upd_times.actime = hstat.st_atime;
	  else
	    acc_upd_times.actime = now;	/* Accessed now */
	  acc_upd_times.modtime = hstat.st_mtime;	/* Mod'd */
	  if (utime (skipcrud + current_file_name,
		     &acc_upd_times) < 0)
	    {
	      msg_perror ("couldn't change access and modification times of %s", skipcrud + current_file_name);
	    }
	}
      /* We do the utime before the chmod because some versions of
		   utime are broken and trash the modes of the file.  Since
		   we then change the mode anyway, we don't care. . . */

      /*
		 * If '-k' is not set, open() or creat() could have saved
		 * the permission bits from a previously created file,
		 * ignoring the ones we specified.
		 * Even if -k is set, if the file has abnormal
		 * mode bits, we must chmod since writing or chown() has
		 * probably reset them.
		 *
		 * If -k is set, we know *we* created this file, so the mode
		 * bits were set by our open().   If the file is "normal", we
		 * skip the chmod.  This works because we did umask(0) if -p
		 * is set, so umask will have left the specified mode alone.
		 */
      if ((!f_keep)
	  || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
	{
	  if (chmod (skipcrud + current_file_name,
		     notumask & (int) hstat.st_mode) < 0)
	    {
	      msg_perror ("cannot change mode of file %s to 0%o",
			  skipcrud + current_file_name,
			  notumask & (int) hstat.st_mode);
	    }
	}

    quit:
      break;

    case LF_LINK:
    again_link:
      {
	struct stat st1, st2;

        if (f_unlink && !f_keep) {
	  if (unlink(skipcrud + current_file_name) == -1)
		if (errno != ENOENT)
		   msg_perror ("Could not unlink %s",
                      skipcrud + current_file_name);
        }

	check = link (current_link_name, skipcrud + current_file_name);

	if (check == 0)
	  break;
	if (make_dirs (skipcrud + current_file_name))
	  goto again_link;
	if (f_gnudump && errno == EEXIST)
	  break;
	if (stat (current_link_name, &st1) == 0
	    && stat (current_file_name + skipcrud, &st2) == 0
	    && st1.st_dev == st2.st_dev
	    && st1.st_ino == st2.st_ino)
	  break;
	msg_perror ("Could not link %s to %s",
		    skipcrud + current_file_name,
		    current_link_name);
      }
      break;

#ifdef S_ISLNK
    case LF_SYMLINK:
    again_symlink:
      if (f_unlink && !f_keep) {
	  if (unlink(skipcrud + current_file_name) == -1)
		if (errno != ENOENT)
		   msg_perror ("Could not unlink %s",
                      skipcrud + current_file_name);
      }

      check = symlink (current_link_name,
		       skipcrud + current_file_name);
      /* FIXME, don't worry uid, gid, etc... */
      if (check == 0)
	break;
      if (make_dirs (current_file_name + skipcrud))
	goto again_symlink;
      msg_perror ("Could not create symlink to %s",
		  current_link_name);
      break;
#endif

#ifdef S_IFCHR
    case LF_CHR:
      hstat.st_mode |= S_IFCHR;
      goto make_node;
#endif

#ifdef S_IFBLK
    case LF_BLK:
      hstat.st_mode |= S_IFBLK;
#endif
#if defined(S_IFCHR) || defined(S_IFBLK)
    make_node:
      if (f_unlink && !f_keep) {
	  if (unlink(skipcrud + current_file_name) == -1)
		if (errno != ENOENT)
		   msg_perror ("Could not unlink %s",
                      skipcrud + current_file_name);
      }

      check = mknod (current_file_name + skipcrud,
		     (int) hstat.st_mode, (int) hstat.st_rdev);
      if (check != 0)
	{
	  if (make_dirs (skipcrud + current_file_name))
	    goto make_node;
	  msg_perror ("Could not make %s",
		      current_file_name + skipcrud);
	  break;
	};
      goto set_filestat;
#endif

#ifdef S_ISFIFO
      /* If local system doesn't support FIFOs, use default case */
    case LF_FIFO:
    make_fifo:
      if (f_unlink && !f_keep) {
	  if (unlink(skipcrud + current_file_name) == -1)
		if (errno != ENOENT)
		   msg_perror ("Could not unlink %s",
                      skipcrud + current_file_name);
      }

      check = mkfifo (current_file_name + skipcrud,
		      (int) hstat.st_mode);
      if (check != 0)
	{
	  if (make_dirs (current_file_name + skipcrud))
	    goto make_fifo;
	  msg_perror ("Could not make %s",
		      skipcrud + current_file_name);
	  break;
	};
      goto set_filestat;
#endif

    case LF_DIR:
    case LF_DUMPDIR:
      namelen = strlen (current_file_name + skipcrud) - 1;
    really_dir:
      /* Check for trailing /, and zap as many as we find. */
      while (namelen
	     && current_file_name[skipcrud + namelen] == '/')
	current_file_name[skipcrud + namelen--] = '\0';
      if (f_gnudump)
	{			/* Read the entry and delete files
					   that aren't listed in the archive */
	  gnu_restore (skipcrud);

	}
      else if (head->header.linkflag == LF_DUMPDIR)
	skip_file ((long) (hstat.st_size));


    again_dir:
      check = mkdir (skipcrud + current_file_name,
		     (we_are_root ? 0 : 0300) | (int) hstat.st_mode);
      if (check != 0)
	{
	  struct stat st1;

	  if (make_dirs (skipcrud + current_file_name))
	    goto again_dir;
	  /* If we're trying to create '.', let it be. */
	  if (current_file_name[skipcrud + namelen] == '.' &&
	      (namelen == 0 ||
	       current_file_name[skipcrud + namelen - 1] == '/'))
	    goto check_perms;
	  if (errno == EEXIST
	      && stat (skipcrud + current_file_name, &st1) == 0
	      && (S_ISDIR (st1.st_mode)))
	    break;
	  msg_perror ("Could not create directory %s", skipcrud + current_file_name);
	  break;
	}

    check_perms:
      if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode))
	{
	  hstat.st_mode |= 0300;
	  msg ("Added write and execute permission to directory %s",
	       skipcrud + current_file_name);
	}

      /*
       * If we are root, set the owner and group of the extracted
       * file.  This does what is wanted both on real Unix and on
       * System V.  If we are running as a user, we extract as that
       * user; if running as root, we extract as the original owner.
       */
      if (we_are_root || f_do_chown)
	{
	  if (chown (skipcrud + current_file_name,
		     hstat.st_uid, hstat.st_gid) < 0)
	    {
	      msg_perror ("cannot chown file %s to uid %d gid %d",
			  skipcrud + current_file_name,
			  hstat.st_uid, hstat.st_gid);
	    }
	}

      if (!f_modified)
	{
	  tmp = ((struct saved_dir_info *)
		 ck_malloc (sizeof (struct saved_dir_info)));
	  tmp->path = (char *) ck_malloc (strlen (skipcrud
						  + current_file_name) + 1);
	  strcpy (tmp->path, skipcrud + current_file_name);
	  tmp->mode = hstat.st_mode;
	  tmp->atime = hstat.st_atime;
	  tmp->mtime = hstat.st_mtime;
	  tmp->next = saved_dir_info_head;
	  saved_dir_info_head = tmp;
	}
      else
	{
	  /* This functions exactly as the code for set_filestat above. */
	  if ((!f_keep)
	      || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
	    {
	      if (chmod (skipcrud + current_file_name,
			 notumask & (int) hstat.st_mode) < 0)
		{
		  msg_perror ("cannot change mode of file %s to 0%o",
			      skipcrud + current_file_name,
			      notumask & (int) hstat.st_mode);
		}
	    }
	}
      break;

    case LF_VOLHDR:
      if (f_verbose)
	{
	  printf ("Reading %s\n", current_file_name);
	}
      break;

    case LF_NAMES:
      extract_mangle (head);
      break;

    case LF_MULTIVOL:
      msg ("Can't extract '%s'--file is continued from another volume\n", current_file_name);
      skip_file ((long) hstat.st_size);
      break;

    case LF_LONGNAME:
    case LF_LONGLINK:
      msg ("Visible long name error\n");
      skip_file ((long) hstat.st_size);
      break;
    }

  /* We don't need to save it any longer. */
  saverec ((union record **) 0);/* Unsave it */
}
Пример #4
0
void
print_header (void)
{
  char modes[11];
  char *timestamp;
  char uform[11], gform[11];	/* these hold formatted ints */
  char *user, *group;
  char size[24];		/* holds a formatted long or maj, min */
  time_t longie;		/* to make ctime() call portable */
  int pad;
  char *name;

  if (block_number_option)
    fprintf (stdlis, _("block %10ld: "), current_block_ordinal ());

  if (verbose_option <= 1)
    {
      /* Just the fax, mam.  */

      char *quoted_name = quote_copy_string (current_file_name);

      if (quoted_name)
	{
	  fprintf (stdlis, "%s\n", quoted_name);
	  free (quoted_name);
	}
      else
	fprintf (stdlis, "%s\n", current_file_name);
    }
  else
    {
      /* File type and modes.  */

      modes[0] = '?';
      switch (current_header->header.typeflag)
	{
	case GNUTYPE_VOLHDR:
	  modes[0] = 'V';
	  break;

	case GNUTYPE_MULTIVOL:
	  modes[0] = 'M';
	  break;

	case GNUTYPE_NAMES:
	  modes[0] = 'N';
	  break;

	case GNUTYPE_LONGNAME:
	case GNUTYPE_LONGLINK:
	  ERROR ((0, 0, _("Visible longname error")));
	  break;

	case GNUTYPE_SPARSE:
	case REGTYPE:
	case AREGTYPE:
	case LNKTYPE:
	  modes[0] = '-';
	  if (current_file_name[strlen (current_file_name) - 1] == '/')
	    modes[0] = 'd';
	  break;
	case GNUTYPE_DUMPDIR:
	  modes[0] = 'd';
	  break;
	case DIRTYPE:
	  modes[0] = 'd';
	  break;
	case SYMTYPE:
	  modes[0] = 'l';
	  break;
	case BLKTYPE:
	  modes[0] = 'b';
	  break;
	case CHRTYPE:
	  modes[0] = 'c';
	  break;
	case FIFOTYPE:
	  modes[0] = 'p';
	  break;
	case CONTTYPE:
	  modes[0] = 'C';
	  break;
	}

      decode_mode ((unsigned) current_stat.st_mode, modes + 1);

      /* Timestamp.  */

      longie = current_stat.st_mtime;
#if USE_OLD_CTIME
      timestamp = ctime (&longie);
      timestamp[16] = '\0';
      timestamp[24] = '\0';
#else
      timestamp = isotime (&longie);
      timestamp[16] = '\0';
#endif

      /* User and group names.  */

      if (*current_header->header.uname && current_format != V7_FORMAT)
	user = current_header->header.uname;
      else
	{
	  user = uform;
	  sprintf (uform, "%ld", from_oct (8, current_header->header.uid));
	}

      if (*current_header->header.gname && current_format != V7_FORMAT)
	group = current_header->header.gname;
      else
	{
	  group = gform;
	  sprintf (gform, "%ld", from_oct (8, current_header->header.gid));
	}

      /* Format the file size or major/minor device numbers.  */

      switch (current_header->header.typeflag)
	{
#if defined(S_IFBLK) || defined(S_IFCHR)
	case CHRTYPE:
	case BLKTYPE:
	  sprintf (size, "%d,%d",
		   major (current_stat.st_rdev), minor (current_stat.st_rdev));
	  break;
#endif
	case GNUTYPE_SPARSE:
	  sprintf (size, "%lld",
		   llfrom_oct (sizeof current_header->oldgnu_header.realsize,
				current_header->oldgnu_header.realsize));
	  break;
	default:
	  sprintf (size, "%lld", (long long) current_stat.st_size);
	}

      /* Figure out padding and print the whole line.  */

      pad = strlen (user) + strlen (group) + strlen (size) + 1;
      if (pad > ugswidth)
	ugswidth = pad;

#if USE_OLD_CTIME
      fprintf (stdlis, "%s %s/%s %*s%s %s %s",
	       modes, user, group, ugswidth - pad, "",
	       size, timestamp + 4, timestamp + 20);
#else
      fprintf (stdlis, "%s %s/%s %*s%s %s",
	       modes, user, group, ugswidth - pad, "", size, timestamp);
#endif

      name = quote_copy_string (current_file_name);
      if (name)
	{
	  fprintf (stdlis, " %s", name);
	  free (name);
	}
      else
	fprintf (stdlis, " %s", current_file_name);

      switch (current_header->header.typeflag)
	{
	case SYMTYPE:
	  name = quote_copy_string (current_link_name);
	  if (name)
	    {
	      fprintf (stdlis, " -> %s\n", name);
	      free (name);
	    }
	  else
	    fprintf (stdlis, " -> %s\n", current_link_name);
	  break;

	case LNKTYPE:
	  name = quote_copy_string (current_link_name);
	  if (name)
	    {
	      fprintf (stdlis, _(" link to %s\n"), name);
	      free (name);
	    }
	  else
	    fprintf (stdlis, _(" link to %s\n"), current_link_name);
	  break;

	default:
	  fprintf (stdlis, _(" unknown file type `%c'\n"),
		   current_header->header.typeflag);
	  break;

	case AREGTYPE:
	case REGTYPE:
	case GNUTYPE_SPARSE:
	case CHRTYPE:
	case BLKTYPE:
	case DIRTYPE:
	case FIFOTYPE:
	case CONTTYPE:
	case GNUTYPE_DUMPDIR:
	  putc ('\n', stdlis);
	  break;

	case GNUTYPE_VOLHDR:
	  fprintf (stdlis, _("--Volume Header--\n"));
	  break;

	case GNUTYPE_MULTIVOL:
	  fprintf (stdlis, _("--Continued at byte %ld--\n"),
		   from_oct (1 + 12, current_header->oldgnu_header.offset));
	  break;

	case GNUTYPE_NAMES:
	  fprintf (stdlis, _("--Mangled file names--\n"));
	  break;
	}
    }
  fflush (stdlis);
}
Пример #5
0
void
read_and (void (*do_something) ())
{
  enum read_header status = HEADER_STILL_UNREAD;
  enum read_header prev_status;
  char save_typeflag;

  name_gather ();
  open_archive (ACCESS_READ);

  while (1)
    {
      prev_status = status;
      status = read_header ();
      switch (status)
	{
	case HEADER_STILL_UNREAD:
	  abort ();

	case HEADER_SUCCESS:

	  /* Valid header.  We should decode next field (mode) first.
	     Ensure incoming names are null terminated.  */

	  /* FIXME: This is a quick kludge before 1.12 goes out.  */
	  current_stat.st_mtime
	    = from_oct (1 + 12, current_header->header.mtime);

	  if (!name_match (current_file_name)
	      || current_stat.st_mtime < newer_mtime_option
	      || (exclude_option && check_exclude (current_file_name)))
	    {
	      int isextended = 0;

	      if (current_header->header.typeflag == GNUTYPE_VOLHDR
		  || current_header->header.typeflag == GNUTYPE_MULTIVOL
		  || current_header->header.typeflag == GNUTYPE_NAMES)
		{
		  (*do_something) ();
		  continue;
		}
	      if (show_omitted_dirs_option
		  && current_header->header.typeflag == DIRTYPE)
		WARN ((0, 0, _("Omitting %s"), current_file_name));

	      /* Skip past it in the archive.  */

	      if (current_header->oldgnu_header.isextended)
		isextended = 1;
	      save_typeflag = current_header->header.typeflag;
	      set_next_block_after (current_header);
	      if (isextended)
		{
#if 0
		  union block *exhdr;

		  while (1)
		    {
		      exhdr = find_next_block ();
		      if (!exhdr->sparse_header.isextended)
			{
			  set_next_block_after (exhdr);
			  break;
			}
		    }
		  set_next_block_after (exhdr);
#endif
		  skip_extended_headers ();
		}

	      /* Skip to the next header on the archive.  */

	      if (save_typeflag != DIRTYPE)
		skip_file ((long long) current_stat.st_size);
	      continue;
	    }

	  (*do_something) ();
	  continue;

	case HEADER_ZERO_BLOCK:
	  if (block_number_option)
	    fprintf (stdlis, _("block %10ld: ** Block of NULs **\n"),
		     current_block_ordinal ());

	  set_next_block_after (current_header);
	  status = prev_status;
	  if (ignore_zeros_option)
	    continue;
	  break;

	case HEADER_END_OF_FILE:
	  if (block_number_option)
	    fprintf (stdlis, _("block %10ld: ** End of File **\n"),
		     current_block_ordinal ());
	  break;

	case HEADER_FAILURE:
	  /* If the previous header was good, tell them that we are
	     skipping bad ones.  */
	  set_next_block_after (current_header);
	  switch (prev_status)
	    {
	    case HEADER_STILL_UNREAD:
	      WARN ((0, 0, _("Could not extract file(s); file might not be a tar archive")));
	      /* Fall through.  */

	    case HEADER_ZERO_BLOCK:
	    case HEADER_SUCCESS:
	      WARN ((0, 0, _("Skipping to next file header")));
	      break;

	    case HEADER_END_OF_FILE:
	    case HEADER_FAILURE:
	      /* We are in the middle of a cascade of errors.  */
	      break;
	    }
	  continue;
	}
      break;
    }

  apply_delayed_set_stat ();
  close_archive ();
  names_notfound ();		/* print names not found */
}
Пример #6
0
void
decode_header (union block *header, struct L_STAT *stat_info,
	       enum archive_format *format_pointer, int do_user_group)
{
  enum archive_format format;

  if (strcmp (header->header.magic, TMAGIC) == 0)
    format = POSIX_FORMAT;
  else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
    format = OLDGNU_FORMAT;
  else
    format = V7_FORMAT;
  *format_pointer = format;

  stat_info->st_mode = from_oct (8, header->header.mode);
  stat_info->st_mode &= 07777;
  stat_info->st_mtime = from_oct (1 + 12, header->header.mtime);

  if (format == OLDGNU_FORMAT && incremental_option)
    {
      stat_info->st_atime = from_oct (1 + 12, header->oldgnu_header.atime);
      stat_info->st_ctime = from_oct (1 + 12, header->oldgnu_header.ctime);
    }

  if (format == V7_FORMAT)
    {
      stat_info->st_uid = from_oct (8, header->header.uid);
      stat_info->st_gid = from_oct (8, header->header.gid);
      stat_info->st_rdev = 0;
    }
  else
    {
      if (do_user_group)
	{
	  /* FIXME: Decide if this should somewhat depend on -p.  */

	  if (numeric_owner_option
	      || !*header->header.uname
	      || !uname_to_uid (header->header.uname, &stat_info->st_uid))
	    stat_info->st_uid = from_oct (8, header->header.uid);

	  if (numeric_owner_option
	      || !*header->header.gname
	      || !gname_to_gid (header->header.gname, &stat_info->st_gid))
	    stat_info->st_gid = from_oct (8, header->header.gid);
	}
      switch (header->header.typeflag)
	{
#ifdef S_IFBLK
	case BLKTYPE:
	  stat_info->st_rdev = makedev (from_oct (8, header->header.devmajor),
					from_oct (8, header->header.devminor));
	  break;
#endif

#ifdef S_IFCHR
	case CHRTYPE:
	  stat_info->st_rdev = makedev (from_oct (8, header->header.devmajor),
					from_oct (8, header->header.devminor));
	  break;
#endif

	default:
	  stat_info->st_rdev = 0;
	}
    }
}
Пример #7
0
enum read_header
read_header (void)
{
  int i;
  long unsigned_sum;		/* the POSIX one :-) */
  long signed_sum;		/* the Sun one :-( */
  long recorded_sum;
  char *p;
  union block *header;
  char **longp;
  char *bp;
  union block *data_block;
  long long size, written;
  static char *next_long_name, *next_long_link;

  while (1)
    {
      header = find_next_block ();
      current_header = header;
      if (!header)
	return HEADER_END_OF_FILE;

      recorded_sum
	= from_oct (sizeof header->header.chksum, header->header.chksum);

      unsigned_sum = 0;
      signed_sum = 0;
      p = header->buffer;
      for (i = sizeof (*header); --i >= 0;)
	{
	  /* We can't use unsigned char here because of old compilers,
	     e.g. V7.  */

	  unsigned_sum += 0xFF & *p;
	  signed_sum += *p++;
	}

      /* Adjust checksum to count the "chksum" field as blanks.  */

      for (i = sizeof (header->header.chksum); --i >= 0;)
	{
	  unsigned_sum -= 0xFF & header->header.chksum[i];
	  signed_sum -= header->header.chksum[i];
	}
      unsigned_sum += ' ' * sizeof header->header.chksum;
      signed_sum += ' ' * sizeof header->header.chksum;

      if (unsigned_sum == sizeof header->header.chksum * ' ')
	{
	  /* This is a zeroed block...whole block is 0's except for the
	     blanks we faked for the checksum field.  */

	  return HEADER_ZERO_BLOCK;
	}

      if (unsigned_sum != recorded_sum && signed_sum != recorded_sum)
	return HEADER_FAILURE;

      /* Good block.  Decode file size and return.  */

      if (header->header.typeflag == LNKTYPE)
	current_stat.st_size = 0;	/* links 0 size on tape */
      else
	current_stat.st_size = llfrom_str (sizeof header->header.size, header->header.size);

      header->header.name[NAME_FIELD_SIZE - 1] = '\0';
      if (header->header.typeflag == GNUTYPE_LONGNAME
	  || header->header.typeflag == GNUTYPE_LONGLINK)
	{
	  longp = ((header->header.typeflag == GNUTYPE_LONGNAME)
		   ? &next_long_name
		   : &next_long_link);

	  set_next_block_after (header);
	  if (*longp)
	    free (*longp);
	  bp = *longp = (char *) xmalloc ((size_t) current_stat.st_size);

	  for (size = current_stat.st_size; size > 0; size -= written)
	    {
	      data_block = find_next_block ();
	      if (data_block == NULL)
		{
		  ERROR ((0, 0, _("Unexpected EOF on archive file")));
		  break;
		}
	      written = available_space_after (data_block);
	      if (written > size)
		written = size;

	      memcpy (bp, data_block->buffer, (size_t) written);
	      bp += written;
	      set_next_block_after ((union block *)
				    (data_block->buffer + written - 1));
	    }

	  /* Loop!  */

	}
      else
	{
	  assign_string (&current_file_name,
			 (next_long_name ? next_long_name
			  : current_header->header.name));
	  assign_string (&current_link_name,
			 (next_long_link ? next_long_link
			  : current_header->header.linkname));
	  next_long_link = next_long_name = 0;
	  return HEADER_SUCCESS;
	}
    }
}
Пример #8
0
void
diff_archive (void)
{
  struct stat stat_data;
  int name_length;
  int status;

  errno = EPIPE;		/* FIXME: errno should be read-only */
				/* FIXME: remove perrors */

  set_next_block_after (current_header);
  decode_header (current_header, &current_stat, &current_format, 1);

  /* Print the block from `current_header' and `current_stat'.  */

  if (verbose_option)
    {
      if (now_verifying)
	fprintf (stdlis, _("Verify "));
      print_header ();
    }

  switch (current_header->header.typeflag)
    {
    default:
      WARN ((0, 0, _("Unknown file type '%c' for %s, diffed as normal file"),
		 current_header->header.typeflag, current_file_name));
      /* Fall through.  */

    case AREGTYPE:
    case REGTYPE:
    case GNUTYPE_SPARSE:
    case CONTTYPE:

      /* Appears to be a file.  See if it's really a directory.  */

      name_length = strlen (current_file_name) - 1;
      if (current_file_name[name_length] == PATHSEP)
	goto really_dir;

      if (!get_stat_data (&stat_data))
	{
	  if (current_header->oldgnu_header.isextended)
	    skip_extended_headers ();
	  skip_file ((long) current_stat.st_size);
	  goto quit;
	}

      if (!S_ISREG (stat_data.st_mode))
	{
	  report_difference (_("Not a regular file"));
	  skip_file ((long) current_stat.st_size);
	  goto quit;
	}

      stat_data.st_mode &= 07777;
      if (stat_data.st_mode != current_stat.st_mode)
	report_difference (_("Mode differs"));

#if !MSDOS && !WIN32
      /* stat() in djgpp's C library gives a constant number of 42 as the
	 uid and gid of a file.  So, comparing an FTP'ed archive just after
	 unpack would fail on MSDOS.  */
      if (stat_data.st_uid != current_stat.st_uid)
	report_difference (_("Uid differs"));
      if (stat_data.st_gid != current_stat.st_gid)
	report_difference (_("Gid differs"));
#endif

      if (stat_data.st_mtime != current_stat.st_mtime)
	report_difference (_("Mod time differs"));
      if (current_header->header.typeflag != GNUTYPE_SPARSE &&
	  stat_data.st_size != current_stat.st_size)
	{
	  report_difference (_("Size differs"));
	  skip_file ((long) current_stat.st_size);
	  goto quit;
	}

      diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);

      if (diff_handle < 0 && !absolute_names_option)
	{
	  char *tmpbuf = xmalloc (strlen (current_file_name) + 2);

	  *tmpbuf = PATHSEP;
	  strcpy (tmpbuf + 1, current_file_name);
	  diff_handle = open (tmpbuf, O_NDELAY | O_RDONLY);
	  free (tmpbuf);
	}
      if (diff_handle < 0)
	{
	  ERROR ((0, errno, _("Cannot open %s"), current_file_name));
	  if (current_header->oldgnu_header.isextended)
	    skip_extended_headers ();
	  skip_file ((long) current_stat.st_size);
	  report_difference (NULL);
	  goto quit;
	}

      /* Need to treat sparse files completely differently here.  */

      if (current_header->header.typeflag == GNUTYPE_SPARSE)
	diff_sparse_files (current_stat.st_size);
      else
	{
	  if (multi_volume_option)
	    {
	      assign_string (&save_name, current_file_name);
	      save_totsize = current_stat.st_size;
	      /* save_sizeleft is set in read_and_process.  */
	    }

	  read_and_process ((long) (current_stat.st_size), process_rawdata);

	  if (multi_volume_option)
	    assign_string (&save_name, NULL);
	}

      status = close (diff_handle);
      if (status < 0)
	ERROR ((0, errno, _("Error while closing %s"), current_file_name));

    quit:
      break;

#if !MSDOS
    case LNKTYPE:
      {
	dev_t dev;
	ino_t ino;

	if (!get_stat_data (&stat_data))
	  break;

	dev = stat_data.st_dev;
	ino = stat_data.st_ino;
	status = stat (current_link_name, &stat_data);
	if (status < 0)
	  {
	    if (errno == ENOENT)
	      report_difference (_("Does not exist"));
	    else
	      {
		WARN ((0, errno, _("Cannot stat file %s"), current_file_name));
		report_difference (NULL);
	      }
	    break;
	  }

	if (stat_data.st_dev != dev || stat_data.st_ino != ino)
	  {
	    char *message = (char *)
	      xmalloc (MESSAGE_BUFFER_SIZE + strlen (current_link_name));

	    sprintf (message, _("Not linked to %s"), current_link_name);
	    report_difference (message);
	    free (message);
	    break;
	  }

	break;
      }
#endif /* not MSDOS */

#ifdef S_ISLNK
    case SYMTYPE:
      {
	char linkbuf[NAME_FIELD_SIZE + 3]; /* FIXME: may be too short.  */

	status = readlink (current_file_name, linkbuf, (sizeof linkbuf) - 1);

	if (status < 0)
	  {
	    if (errno == ENOENT)
	      report_difference (_("No such file or directory"));
	    else
	      {
		WARN ((0, errno, _("Cannot read link %s"), current_file_name));
		report_difference (NULL);
	      }
	    break;
	  }

	linkbuf[status] = '\0';	/* null-terminate it */
	if (strncmp (current_link_name, linkbuf, (size_t) status) != 0)
	  report_difference (_("Symlink differs"));

	break;
      }
#endif /* not S_ISLNK */

#ifdef S_IFCHR
    case CHRTYPE:
      current_stat.st_mode |= S_IFCHR;
      goto check_node;
#endif /* not S_IFCHR */

#ifdef S_IFBLK
      /* If local system doesn't support block devices, use default case.  */

    case BLKTYPE:
      current_stat.st_mode |= S_IFBLK;
      goto check_node;
#endif /* not S_IFBLK */

#ifdef S_ISFIFO
      /* If local system doesn't support FIFOs, use default case.  */

    case FIFOTYPE:
# ifdef S_IFIFO
      current_stat.st_mode |= S_IFIFO;
# endif
      current_stat.st_rdev = 0;	/* FIXME: do we need this? */
      goto check_node;
#endif /* S_ISFIFO */

    check_node:
      /* FIXME: deal with umask.  */

      if (!get_stat_data (&stat_data))
	break;

      if (current_stat.st_rdev != stat_data.st_rdev)
	{
	  report_difference (_("Device numbers changed"));
	  break;
	}

      if (
#ifdef S_IFMT
	  current_stat.st_mode != stat_data.st_mode
#else
	  /* POSIX lossage.  */
	  (current_stat.st_mode & 07777) != (stat_data.st_mode & 07777)
#endif
	  )
	{
	  report_difference (_("Mode or device-type changed"));
	  break;
	}

      break;

    case GNUTYPE_DUMPDIR:
      {
	char *dumpdir_buffer = get_directory_contents (current_file_name, 0);

	if (multi_volume_option)
	  {
	    assign_string (&save_name, current_file_name);
	    save_totsize = current_stat.st_size;
	    /* save_sizeleft is set in read_and_process.  */
	  }

	if (dumpdir_buffer)
	  {
	    dumpdir_cursor = dumpdir_buffer;
	    read_and_process ((long) (current_stat.st_size), process_dumpdir);
	    free (dumpdir_buffer);
	  }
	else
	  read_and_process ((long) (current_stat.st_size), process_noop);

	if (multi_volume_option)
	  assign_string (&save_name, NULL);
	/* Fall through.  */
      }

    case DIRTYPE:
      /* Check for trailing /.  */

      name_length = strlen (current_file_name) - 1;

    really_dir:
      while (name_length && current_file_name[name_length] == PATHSEP)
	current_file_name[name_length--] = '\0';	/* zap / */

      if (!get_stat_data (&stat_data))
	break;

      if (!S_ISDIR (stat_data.st_mode))
	{
	  report_difference (_("No longer a directory"));
	  break;
	}

      if ((stat_data.st_mode & 07777) != (current_stat.st_mode & 07777))
	report_difference (_("Mode differs"));
      break;

    case GNUTYPE_VOLHDR:
      break;

    case GNUTYPE_MULTIVOL:
      {
	off_t offset;

	name_length = strlen (current_file_name) - 1;
	if (current_file_name[name_length] == PATHSEP)
	  goto really_dir;

	if (!get_stat_data (&stat_data))
	  break;

	if (!S_ISREG (stat_data.st_mode))
	  {
	    report_difference (_("Not a regular file"));
	    skip_file ((long) current_stat.st_size);
	    break;
	  }

	stat_data.st_mode &= 07777;
	offset = from_oct (1 + 12, current_header->oldgnu_header.offset);
	if (stat_data.st_size != current_stat.st_size + offset)
	  {
	    report_difference (_("Size differs"));
	    skip_file ((long) current_stat.st_size);
	    break;
	  }

	diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);

	if (diff_handle < 0)
	  {
	    WARN ((0, errno, _("Cannot open file %s"), current_file_name));
	    report_difference (NULL);
	    skip_file ((long) current_stat.st_size);
	    break;
	  }

	status = lseek (diff_handle, offset, 0);
	if (status != offset)
	  {
	    WARN ((0, errno, _("Cannot seek to %ld in file %s"),
		   offset, current_file_name));
	    report_difference (NULL);
	    break;
	  }

	if (multi_volume_option)
	  {
	    assign_string (&save_name, current_file_name);
	    save_totsize = stat_data.st_size;
	    /* save_sizeleft is set in read_and_process.  */
	  }

	read_and_process ((long) (current_stat.st_size), process_rawdata);

	if (multi_volume_option)
	  assign_string (&save_name, NULL);

	status = close (diff_handle);
	if (status < 0)
	  ERROR ((0, errno, _("Error while closing %s"), current_file_name));

	break;
      }
    }
}
Пример #9
0
static void
fill_in_sparse_array (void)
{
  int counter;

  /* Allocate space for our scratch space; it's initially 10 elements
     long, but can change in this routine if necessary.  */

  sp_array_size = 10;
  sparsearray = (struct sp_array *) xmalloc (sp_array_size * sizeof (struct sp_array));

  /* There are at most five of these structures in the header itself;
     read these in first.  */

  for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
    {
      /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler.  */
      if (current_header->oldgnu_header.sp[counter].numbytes == 0)
	break;

      sparsearray[counter].offset =
	from_oct (1 + 12, current_header->oldgnu_header.sp[counter].offset);
      sparsearray[counter].numbytes =
	from_oct (1 + 12, current_header->oldgnu_header.sp[counter].numbytes);
    }

  /* If the header's extended, we gotta read in exhdr's till we're done.  */

  if (current_header->oldgnu_header.isextended)
    {
      /* How far into the sparsearray we are `so far'.  */
      static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
      union block *exhdr;

      while (1)
	{
	  exhdr = find_next_block ();
	  for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
	    {
	      if (counter + so_far_ind > sp_array_size - 1)
		{
		  /* We just ran out of room in our scratch area -
		     realloc it.  */

		  sp_array_size *= 2;
		  sparsearray = (struct sp_array *)
		    xrealloc (sparsearray,
			      sp_array_size * sizeof (struct sp_array));
		}

	      /* Convert the character strings into longs.  */

	      sparsearray[counter + so_far_ind].offset =
		from_oct (1 + 12, exhdr->sparse_header.sp[counter].offset);
	      sparsearray[counter + so_far_ind].numbytes =
		from_oct (1 + 12, exhdr->sparse_header.sp[counter].numbytes);
	    }

	  /* If this is the last extended header for this file, we can
	     stop.  */

	  if (!exhdr->sparse_header.isextended)
	    break;

	  so_far_ind += SPARSES_IN_SPARSE_HEADER;
	  set_next_block_after (exhdr);
	}

      /* Be sure to skip past the last one.  */

      set_next_block_after (exhdr);
    }
}
Пример #10
0
/*
 * Return 1 for success, 0 if the checksum is bad, EOF on eof,
 * 2 for a record full of zeros (EOF marker).
 *
 */
static int read_header (vfs *me, vfs_s_super *archive, int tard)
{
    register int i;
    register long sum, signed_sum, recsum;
    register char *p;
    register union record *header;
    char **longp;
    char *bp, *data;
    int size, written;
    static char *next_long_name = NULL, *next_long_link = NULL;
    char *current_file_name, *current_link_name;

  recurse:

    header = get_next_record (archive, tard);
    if (NULL == header)
	return EOF;

    recsum = from_oct (8, header->header.chksum);

    sum = 0; signed_sum = 0;
    p = header->charptr;
    for (i = sizeof (*header); --i >= 0;) {
	        /*
		 * We can't use unsigned char here because of old compilers,
		 * e.g. V7.
		 */
	signed_sum += *p;
	sum += 0xFF & *p++;
    }

    /* Adjust checksum to count the "chksum" field as blanks. */
    for (i = sizeof (header->header.chksum); --i >= 0;) {
	sum -= 0xFF & header->header.chksum[i];
	signed_sum -= (char) header->header.chksum[i];
    }
    sum += ' ' * sizeof header->header.chksum;
    signed_sum += ' ' * sizeof header->header.chksum;

    if (sum == 8 * ' ') {
	/*
	 * This is a zeroed record...whole record is 0's except
	 * for the 8 blanks we faked for the checksum field.
	 */
	return 2;
    }
    if (sum != recsum && signed_sum != recsum)
	return 0;
    
    /*
     * linkflag on BSDI tar (pax) always '\000'
     */
    if(header->header.linkflag == '\000' &&
       strlen(header->header.arch_name) &&
       header->header.arch_name[strlen(header->header.arch_name) - 1] == '/')
	header->header.linkflag = LF_DIR;
    
     /*
     * Good record.  Decode file size and return.
     */
    if (header->header.linkflag == LF_LINK || header->header.linkflag == LF_DIR)
	hstat.st_size = 0;	/* Links 0 size on tape */
    else
	hstat.st_size = from_oct (1 + 12, header->header.size);

    header->header.arch_name[NAMSIZ - 1] = '\0';
    if (header->header.linkflag == LF_LONGNAME
	|| header->header.linkflag == LF_LONGLINK) {
	longp = ((header->header.linkflag == LF_LONGNAME)
		 ? &next_long_name
		 : &next_long_link);

	if (*longp)
	    free (*longp);
	bp = *longp = (char *) xmalloc (hstat.st_size, "Tar: Long name");

	for (size = hstat.st_size;
	     size > 0;
	     size -= written) {
	    data = get_next_record (archive, tard)->charptr;
	    if (data == NULL) {
		message_1s (1, MSG_ERROR, _("Unexpected EOF on archive file"));
		return 0;
	    }
	    written = RECORDSIZE;
	    if (written > size)
		written = size;

	    bcopy (data, bp, written);
	    bp += written;
	}
#if 0
	if (hstat.st_size > 1)
	    bp [hstat.st_size - 1] = 0;	/* just to make sure */
#endif
	goto recurse;
    } else {
	struct stat st;
	struct vfs_s_entry *entry;
	struct vfs_s_inode *inode, *parent;
	long data_position;
	char *p, *q;
	int len;
	int isdir = 0;

	current_file_name = (next_long_name
			     ? next_long_name
			     : strdup (header->header.arch_name));
	len = strlen (current_file_name);
	if (current_file_name[len - 1] == '/') {
	    current_file_name[len - 1] = 0;
	    isdir = 1;
	}

	current_link_name = (next_long_link
			     ? next_long_link
			     : strdup (header->header.arch_linkname));
	len = strlen (current_link_name);
	if (len && current_link_name [len - 1] == '/')
	    current_link_name[len - 1] = 0;

	next_long_link = next_long_name = NULL;

	data_position = current_tar_position;
	
	p = strrchr (current_file_name, '/');
	if (p == NULL) {
	    p = current_file_name;
	    q = current_file_name + strlen (current_file_name); /* "" */
	} else {
	    *(p++) = 0;
	    q = current_file_name;
	}

	parent = vfs_s_find_inode (me, archive->root, q, LINK_NO_FOLLOW, FL_MKDIR);
	if (parent == NULL) {
	    message_1s (1, MSG_ERROR, _("Inconsistent tar archive"));
	    return 0;
	}

	if (header->header.linkflag == LF_LINK) {
	    parent = vfs_s_find_inode (me, archive->root, current_link_name, LINK_NO_FOLLOW, 0);
	    if (parent == NULL) {
	        message_1s (1, MSG_ERROR, _("Inconsistent tar archive"));
	    } else {
	        inode = parent;
		entry = vfs_s_new_entry(me, p, inode);
		vfs_s_insert_entry(me, parent, entry);
		free (current_link_name);
		goto done;
	    }
	}

	fill_stat_from_header (me, &st, header);
	inode = vfs_s_new_inode (me, archive, &st);

	inode->u.tar.data_offset = data_position;
	if (*current_link_name)
	    inode->linkname = current_link_name;
	entry = vfs_s_new_entry (me, p, inode);

	vfs_s_insert_entry (me, parent, entry);
	free (current_file_name);

    done:
	if (header->header.isextended) {
	    while (get_next_record (archive, tard)->ext_hdr.isextended);
	    inode->u.tar.data_offset = current_tar_position;
	}
	return 1;
    }
}
Пример #11
0
int
tdh_digest (char *data, unsigned data_len, struct tar_digest_hdr *tdh)
{
	char *			scan = data;
	char *			scan_end = data + data_len;
	int			lf;
	unsigned long		size, size_nrec;
	unsigned		sum;
	struct header *		hdr;

	tdh->n_header_records = 0;
	tdh->n_content_records = 0;
	tdh->name = 0;
	tdh->linkname = 0;
	tdh->is_end_marker = 0;

  top:
	if (scan + RECORDSIZE > scan_end) {
		/* need at least one more record */
		return tdh->n_header_records + 1;
	}

	hdr = (struct header *) scan;
	tdh->n_header_records++;
	sum = raw_sum ((unsigned char*)hdr, RECORDSIZE);

	if (sum == 0) {
		tdh->is_end_marker = 1;
		return 0;
	}

	sum -= raw_sum ((unsigned char *)hdr->chksum, 8);
	sum += ' ' * 8;

	if (sum != from_oct (8, hdr->chksum)) {
		return -1;
	}

	scan += RECORDSIZE;

	size = from_oct (12, hdr->size);
	size_nrec = (size + RECORDSIZE - 1) / RECORDSIZE;
	lf = hdr->linkflag;

	if (lf == LF_LONGNAME || lf == LF_LONGLINK) {
		if (scan + (size_nrec*RECORDSIZE) > scan_end) {
			return tdh->n_header_records + size_nrec;
		}
	}

	if (lf == LF_LONGNAME) {
		if (tdh->name) {
			/* duplicate LF_LONGNAME. Ignore first. */
		}
		tdh->name = scan;
		tdh->name_len = size;

		scan += size_nrec * RECORDSIZE;
		tdh->n_header_records += size_nrec;
		goto top;
	}

	if (hdr->linkflag == LF_LONGLINK) {
		if (tdh->linkname) {
			/* duplicate LF_LONGLINK. Ignore first. */
		}
		tdh->linkname = scan;
		tdh->linkname_len = size;

		scan += size_nrec * RECORDSIZE;
		tdh->n_header_records += size_nrec;
		goto top;
	}

	tdh->primary_header = hdr;

	/* slurp everything up */
	tdh->linkflag = lf;
	tdh->mode  = from_oct (8, hdr->mode);
	tdh->size  = size;
	tdh->uid   = from_oct (8, hdr->uid);
	tdh->gid   = from_oct (8, hdr->gid);
	tdh->mtime = from_oct (12, hdr->mtime);
	tdh->atime = 0;	/* TODO: sometimes available */
	tdh->ctime = 0;	/* TODO: sometimes available */
	if (lf == LF_OLDNORMAL || lf == LF_NORMAL) {
		tdh->n_content_records = size_nrec;
	} else {
		tdh->n_content_records = 0;
	}

	if (!tdh->name) {
		tdh->name = hdr->arch_name;
		tdh->name_len = strnlen (tdh->name, NAMSIZ);
	}
	if (lf == LF_LINK || lf == LF_SYMLINK) {
		if (!tdh->linkname) {
			tdh->linkname = hdr->arch_linkname;
			tdh->linkname_len = strnlen (tdh->linkname, NAMSIZ);
		}
	} else if (tdh->linkname) {
		/* warning: LONGLINK w/o (SYM)LINK */
		tdh->linkname = 0;
	}

	/* ignore trailing slashes in name by reducing name length */
	while (tdh->name_len > 0 && tdh->name[tdh->name_len-1] == '/')
		tdh->name_len--;

	return 0;
}
Пример #12
0
void
diff_archive (void)
{
  register char *data;
  int check, namelen;
  int err;
  off_t offset;
  struct stat filestat;

#ifndef __MSDOS__
  dev_t dev;
  ino_t ino;
#endif

  errno = EPIPE;		/* FIXME, remove perrors */

  saverec (&head);		/* make sure it sticks around */
  userec (head);		/* and go past it in the archive */
  decode_header (head, &hstat, &head_standard, 1);	/* snarf fields */

  /* Print the record from `head' and `hstat'.  */

  if (flag_verbose)
    {
      if (now_verifying)
	fprintf (stdlis, _("Verify "));
      print_header ();
    }

  switch (head->header.linkflag)
    {

    default:
      WARN ((0, 0, _("Unknown file type '%c' for %s, diffed as normal file"),
		 head->header.linkflag, current_file_name));
      /* Fall through.  */

    case LF_OLDNORMAL:
    case LF_NORMAL:
    case LF_SPARSE:
    case LF_CONTIG:

      /* Appears to be a file.  See if it's really a directory.  */

      namelen = strlen (current_file_name) - 1;
      if (current_file_name[namelen] == '/')
	goto really_dir;

      if (do_stat (&filestat))
	{
	  if (head->header.isextended)
	    skip_extended_headers ();
	  skip_file ((long) hstat.st_size);
	  different++;
	  goto quit;
	}

      if (!S_ISREG (filestat.st_mode))
	{
	  fprintf (stdlis, _("%s: Not a regular file\n"), current_file_name);
	  skip_file ((long) hstat.st_size);
	  different++;
	  goto quit;
	}

      filestat.st_mode &= 07777;
      if (filestat.st_mode != hstat.st_mode)
	sigh (_("Mode"));
      if (filestat.st_uid != hstat.st_uid)
	sigh (_("Uid"));
      if (filestat.st_gid != hstat.st_gid)
	sigh (_("Gid"));
      if (filestat.st_mtime != hstat.st_mtime)
	sigh (_("Mod time"));
      if (head->header.linkflag != LF_SPARSE &&
	  filestat.st_size != hstat.st_size)
	{
	  sigh (_("Size"));
	  skip_file ((long) hstat.st_size);
	  goto quit;
	}

      diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);

      if (diff_fd < 0 && !flag_absolute_names)
	{
	  char *tmpbuf = tar_xmalloc (strlen (current_file_name) + 2);

	  *tmpbuf = '/';
	  strcpy (tmpbuf + 1, current_file_name);
	  diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
	  free (tmpbuf);
	}
      if (diff_fd < 0)
	{
	  ERROR ((0, errno, _("Cannot open %s"), current_file_name));
	  if (head->header.isextended)
	    skip_extended_headers ();
	  skip_file ((long) hstat.st_size);
	  different++;
	  goto quit;
	}

      /* Need to treat sparse files completely differently here.  */

      if (head->header.linkflag == LF_SPARSE)
	diff_sparse_files (hstat.st_size);
      else
	{
	  if (flag_multivol)
	    {
	      assign_string (&save_name, current_file_name);
	      save_totsize = hstat.st_size;
	      /* save_size is set in wantbytes ().  */
	    }
	  wantbytes ((long) (hstat.st_size), compare_chunk);
	  if (flag_multivol)
	    assign_string (&save_name, NULL);
	}

      check = close (diff_fd);
      if (check < 0)
	ERROR ((0, errno, _("Error while closing %s"), current_file_name));

    quit:
      break;

#ifndef __MSDOS__
    case LF_LINK:
      if (do_stat (&filestat))
	break;
      dev = filestat.st_dev;
      ino = filestat.st_ino;
      err = stat (current_link_name, &filestat);
      if (err < 0)
	{
	  if (errno == ENOENT)
	    fprintf (stdlis, _("%s: Does not exist\n"), current_file_name);
	  else
	    WARN ((0, errno, _("Cannot stat file %s"), current_file_name));
	  different++;
	  break;
	}
      if (filestat.st_dev != dev || filestat.st_ino != ino)
	{
	  fprintf (stdlis, _("%s: Not linked to %s\n"),
		   current_file_name, current_link_name);
	  break;
	}
      break;
#endif

#ifdef S_ISLNK
    case LF_SYMLINK:
      {
	char linkbuf[NAMSIZ + 3]; /* FIXME: may be too short.  */

	check = readlink (current_file_name, linkbuf, (sizeof linkbuf) - 1);

	if (check < 0)
	  {
	    if (errno == ENOENT)
	      fprintf (stdlis, _("%s: No such file or directory\n"),
		       current_file_name);
	    else
	      WARN ((0, errno, _("Cannot read link %s"), current_file_name));
	    different++;
	    break;
	  }

	linkbuf[check] = '\0';	/* null-terminate it */
	if (strncmp (current_link_name, linkbuf, (size_t) check) != 0)
	  {
	    fprintf (stdlis, _("%s: Symlink differs\n"), current_link_name);
	    different++;
	  }
      }
      break;
#endif

#ifdef S_IFCHR
    case LF_CHR:
      hstat.st_mode |= S_IFCHR;
      goto check_node;
#endif

#ifdef S_IFBLK
      /* If local system doesn't support block devices, use default case.  */

    case LF_BLK:
      hstat.st_mode |= S_IFBLK;
      goto check_node;
#endif

#ifdef S_ISFIFO
      /* If local system doesn't support FIFOs, use default case.  */

    case LF_FIFO:
#ifdef S_IFIFO
      hstat.st_mode |= S_IFIFO;
#endif
      hstat.st_rdev = 0;	/* FIXME, do we need this? */
      goto check_node;
#endif

    check_node:
      /* FIXME, deal with umask.  */

      if (do_stat (&filestat))
	break;
      if (hstat.st_rdev != filestat.st_rdev)
	{
	  fprintf (stdlis, _("%s: Device numbers changed\n"),
		   current_file_name);
	  different++;
	  break;
	}
      if (
#ifdef S_IFMT
	  hstat.st_mode != filestat.st_mode
#else
	  /* POSIX lossage */
	  (hstat.st_mode & 07777) != (filestat.st_mode & 07777)
#endif
	  )
	{
	  fprintf (stdlis, _("%s: Mode or device-type changed\n"),
		   current_file_name);
	  different++;
	  break;
	}
      break;

    case LF_DUMPDIR:
      data = diff_dir = get_dir_contents (current_file_name, 0);
      if (flag_multivol)
	{
	  assign_string (&save_name, current_file_name);
	  save_totsize = hstat.st_size;
	  /* save_size is set in wantbytes ().  */
	}
      if (data)
	{
	  wantbytes ((long) (hstat.st_size), compare_dir);
	  free (data);
	}
      else
	wantbytes ((long) (hstat.st_size), no_op);
      if (flag_multivol)
	assign_string (&save_name, NULL);
      /* Fall through.  */

    case LF_DIR:
      /* Check for trailing /.  */

      namelen = strlen (current_file_name) - 1;

    really_dir:
      while (namelen && current_file_name[namelen] == '/')
	current_file_name[namelen--] = '\0';	/* zap / */

      if (do_stat (&filestat))
	break;
      if (!S_ISDIR (filestat.st_mode))
	{
	  fprintf (stdlis, _("%s: No longer a directory\n"),
		   current_file_name);
	  different++;
	  break;
	}
      if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
	sigh (_("Mode"));
      break;

    case LF_VOLHDR:
      break;

    case LF_MULTIVOL:
      namelen = strlen (current_file_name) - 1;
      if (current_file_name[namelen] == '/')
	goto really_dir;

      if (do_stat (&filestat))
	break;

      if (!S_ISREG (filestat.st_mode))
	{
	  fprintf (stdlis, _("%s: Not a regular file\n"), current_file_name);
	  skip_file ((long) hstat.st_size);
	  different++;
	  break;
	}

      filestat.st_mode &= 07777;
      offset = from_oct (1 + 12, head->header.offset);
      if (filestat.st_size != hstat.st_size + offset)
	{
	  sigh (_("Size"));
	  skip_file ((long) hstat.st_size);
	  different++;
	  break;
	}

      diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);

      if (diff_fd < 0)
	{
	  WARN ((0, errno, _("Cannot open file %s"), current_file_name));
	  skip_file ((long) hstat.st_size);
	  different++;
	  break;
	}
      err = lseek (diff_fd, offset, 0);
      if (err != offset)
	{
	  WARN ((0, errno, _("Cannot seek to %ld in file %s"),
		     offset, current_file_name));
	  different++;
	  break;
	}

      if (flag_multivol)
	{
	  assign_string (&save_name, current_file_name);
	  save_totsize = filestat.st_size;
	  /* save_size is set in wantbytes ().  */
	}
      wantbytes ((long) (hstat.st_size), compare_chunk);
      if (flag_multivol)
	assign_string (&save_name, NULL);

      check = close (diff_fd);
      if (check < 0)
	ERROR ((0, errno, _("Error while closing %s"), current_file_name));
      break;

    }

  /* We don't need to save it any longer. */

  saverec ((union record **) 0);	/* unsave it */
}
Пример #13
0
static void
fill_in_sparse_array (void)
{
  int ind;

  /* Allocate space for our scratch space; it's initially 10 elements
     long, but can change in this routine if necessary.  */

  sp_array_size = 10;
  sparsearray = (struct sp_array *) tar_xmalloc (sp_array_size * sizeof (struct sp_array));

  /* There are at most five of these structures in the header itself;
     read these in first.  */

  for (ind = 0; ind < SPARSE_IN_HDR; ind++)
    {
      /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler.  */
      if (head->header.sp[ind].numbytes == 0)
	break;
      sparsearray[ind].offset =
	from_oct (1 + 12, head->header.sp[ind].offset);
      sparsearray[ind].numbytes =
	from_oct (1 + 12, head->header.sp[ind].numbytes);
    }

  /* If the header's extended, we gotta read in exhdr's till we're done.  */

  if (head->header.isextended)
    {

      /* How far into the sparsearray we are `so far'.  */
      static int so_far_ind = SPARSE_IN_HDR;
      union record *exhdr;

      while (1)
	{
	  exhdr = findrec ();
	  for (ind = 0; ind < SPARSE_EXT_HDR; ind++)
	    {
	      if (ind + so_far_ind > sp_array_size - 1)
		{

		  /* We just ran out of room in our scratch area -
		     realloc it.  */

		  sp_array_size *= 2;
		  sparsearray = (struct sp_array *)
		    tar_realloc (sparsearray,
			      sp_array_size * sizeof (struct sp_array));
		}

	      /* Convert the character strings into longs.  */

	      sparsearray[ind + so_far_ind].offset =
		from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset);
	      sparsearray[ind + so_far_ind].numbytes =
		from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes);
	    }

	  /* If this is the last extended header for this file, we can
	     stop.  */

	  if (!exhdr->ext_hdr.isextended)
	    break;
	  else
	    {
	      so_far_ind += SPARSE_EXT_HDR;
	      userec (exhdr);
	    }
	}

      /* Be sure to skip past the last one.  */

      userec (exhdr);
    }
}