/* * Diff files from a tar archive. * * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu. * * @(#) diffarch.c 1.10 87/11/11 Public Domain - gnu */ #include #include #include #include #ifdef BSD42 #include #endif #ifdef USG #include #endif /* Some systems don't have these #define's -- we fake it here. */ #ifndef O_RDONLY #define O_RDONLY 0 #endif #ifndef O_NDELAY #define O_NDELAY 0 #endif extern int errno; /* From libc.a */ extern char *valloc(); /* From libc.a */ #include "tar.h" #include "port.h" extern union record *head; /* Points to current tape header */ extern struct stat hstat; /* Stat struct corresponding */ extern int head_standard; /* Tape header is in ANSI format */ extern void print_header(); extern void skip_file(); char *filedata; /* Pointer to area for reading file contents into */ /* * Initialize for a diff operation */ diff_init() { /*NOSTRICT*/ filedata = (char *) valloc((unsigned)blocksize); if (!filedata) { fprintf(stderr, "tar: could not allocate memory for diff buffer of %d bytes\n", blocking); exit(EX_ARGSBAD); } } /* * Diff a file against the archive. */ void diff_archive() { register char *data; int fd, check, namelen, written; int err, firsttime; long size; struct stat filestat; char linkbuf[NAMSIZ+3]; 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 (f_verbose) print_header(stdout); switch (head->header.linkflag) { default: annofile(stderr, tar); fprintf(stderr, "Unknown file type '%c' for %s, diffed as normal file\n", head->header.linkflag, head->header.name); /* 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(head->header.name)-1; if (head->header.name[namelen] == '/') goto really_dir; fd = open(head->header.name, O_NDELAY|O_RDONLY); if (fd < 0) { if (errno == ENOENT) { /* Expected error -- to stdout */ annofile(stdout, (char *)NULL); fprintf(stdout, "%s: does not exist\n", head->header.name); } else { annofile(stderr, (char *)NULL); perror(head->header.name); } skip_file((long)hstat.st_size); goto quit; } #ifdef AMIGA err = stat(head->header.name, &filestat); #else err = fstat(fd, &filestat); #endif if (err < 0) { annofile(stdout, (char *)NULL); fprintf(stdout, "Cannot fstat file "); perror(head->header.name); skip_file((long)hstat.st_size); goto qclose; } if ((filestat.st_mode & S_IFMT) != S_IFREG) { annofile(stdout, (char *)NULL); fprintf(stdout, "%s: not a regular file\n", head->header.name); skip_file((long)hstat.st_size); goto qclose; } filestat.st_mode &= ~S_IFMT; 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_size != hstat.st_size) { sigh("size"); skip_file((long)hstat.st_size); goto qclose; } if (filestat.st_mtime != hstat.st_mtime) sigh("mod time"); firsttime = 0; for (size = hstat.st_size; size > 0; size -= written) { /* * 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... */ annorec(stderr, tar); fprintf(stderr, "Unexpected EOF on archive file\n"); break; } written = endofrecs()->charptr - data; if (written > size) written = size; errno = 0; check = read (fd, filedata, written); /* * The following is in violation of strict * typing, since the arg to userec * should be a struct rec *. FIXME. */ userec(data + written - 1); if (check == written) { /* The read worked, now compare the data */ if (bcmp(data, filedata, check) == 0) continue; /* It compares */ if (firsttime++) { annofile(stdout, (char *)NULL); fprintf(stdout, "%s: data differs\n", head->header.name); } } /* * Error in reading from file. * Print it, skip to next file in archive. */ annofile(stderr, tar); fprintf(stderr, "Tried to read %d bytes from file, could only read %d:\n", written, check); perror(head->header.name); skip_file((long)(size - written)); break; /* Still do the close, mod time, chmod, etc */ } qclose: check = close(fd); if (check < 0) { annofile(stderr, tar); fprintf(stderr, "Error while closing "); perror(head->header.name); } quit: break; case LF_LINK: check = 1; /* FIXME deal with this */ /* check = link (head->header.linkname, head->header.name); */ /* FIXME, don't worry uid, gid, etc... */ if (check == 0) break; annofile(stderr, tar); fprintf(stderr, "Could not link %s to ", head->header.name); perror(head->header.linkname); break; #ifdef S_IFLNK case LF_SYMLINK: check = readlink(head->header.name, linkbuf, (sizeof linkbuf)-1); if (check < 0) { if (errno == ENOENT) { annofile(stdout, (char *)NULL); fprintf(stdout, "%s: no such file or directory\n", head->header.name); } else { annofile(stderr, tar); fprintf(stderr, "Could not read link"); perror(head->header.name); } break; } linkbuf[check] = '\0'; /* Null-terminate it */ if (strncmp(head->header.linkname, linkbuf, check) != 0) { annofile(stdout, (char *)NULL); fprintf(stdout, "%s: symlink differs\n", head->header.linkname); } break; #endif case LF_CHR: hstat.st_mode |= S_IFCHR; goto make_node; #ifdef S_IFBLK /* If local system doesn't support block devices, use default case */ case LF_BLK: hstat.st_mode |= S_IFBLK; goto make_node; #endif #ifdef S_IFIFO /* If local system doesn't support FIFOs, use default case */ case LF_FIFO: hstat.st_mode |= S_IFIFO; hstat.st_rdev = 0; /* FIXME, do we need this? */ goto make_node; #endif make_node: /* FIXME, deal with umask */ check = 1; /* FIXME, implement this */ /* check = mknod(head->header.name, (int) hstat.st_mode, (int) hstat.st_rdev); */ if (check != 0) { annofile(stderr, tar); fprintf(stderr, "Could not make "); perror(head->header.name); break; }; break; case LF_DIR: /* Check for trailing / */ namelen = strlen(head->header.name)-1; really_dir: while (namelen && head->header.name[namelen] == '/') head->header.name[namelen--] = '\0'; /* Zap / */ check = 1; /* FIXME, implement this */ /* check = mkdir(head->header.name, 0300 | (int)hstat.st_mode); */ #ifndef AMIGA if (check != 0) { annofile(stderr, tar); fprintf(stderr, "Could not make directory "); perror(head->header.name); break; } #endif break; } /* We don't need to save it any longer. */ saverec((union record **) 0); /* Unsave it */ } /* * Sigh about something that differs. */ sigh(what) char *what; { annofile(stdout, (char *)NULL); fprintf(stdout, "%s: %s differs\n", head->header.name, what); }