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; } } }
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 */ }