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 */ }
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); }
/* * 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 */ }
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); }
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 */ }
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; } } }
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 (¤t_file_name, (next_long_name ? next_long_name : current_header->header.name)); assign_string (¤t_link_name, (next_long_link ? next_long_link : current_header->header.linkname)); next_long_link = next_long_name = 0; return HEADER_SUCCESS; } } }
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, ¤t_stat, ¤t_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; } } }
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); } }
/* * 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; } }
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; }
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 */ }
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); } }