783fa5270c
patch to the well-known digest package. The output is the same for files, but if rdigest is given a directory as an argument, it will recursively checksum the directory in such a manner as to: (1) give a repeatable checksum (2) with no regard for file ownership (3) or timestamps (4) or modes (5) while taking into account empty files and directories such that the checksum of an extracted tar file will match the checksum of the same tree as retrieved via cvs, or two allegedly matching trees from different cvs servers, etc.
204 lines
4.9 KiB
Text
204 lines
4.9 KiB
Text
$NetBSD: patch-ac,v 1.1.1.1 2003/07/24 05:21:07 atatat Exp $
|
|
|
|
--- digest.c.orig 2003-07-23 20:27:09.000000000 -0400
|
|
+++ digest.c
|
|
@@ -43,9 +43,14 @@ __RCSID("$NetBSD: digest.c,v 1.8 2003/07
|
|
#endif
|
|
|
|
|
|
+#include <sys/queue.h>
|
|
+#include <sys/stat.h>
|
|
+
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
+#include <fcntl.h>
|
|
+#include <fts.h>
|
|
#ifdef HAVE_LOCALE_H
|
|
#include <locale.h>
|
|
#endif
|
|
@@ -147,21 +152,172 @@ digest_file(char *fn, alg_t *alg)
|
|
return (rc);
|
|
}
|
|
|
|
+struct excl {
|
|
+ LIST_ENTRY(excl) n;
|
|
+ const char *p;
|
|
+};
|
|
+
|
|
+LIST_HEAD(, excl) excl = LIST_HEAD_INITIALIZER(excl);
|
|
+
|
|
+static void
|
|
+exclude(const char *p)
|
|
+{
|
|
+ struct excl *e;
|
|
+
|
|
+ e = malloc(sizeof(struct excl));
|
|
+ e->p = p;
|
|
+ LIST_INSERT_HEAD(&excl, e, n);
|
|
+}
|
|
+
|
|
+static int
|
|
+skip(const char *p)
|
|
+{
|
|
+ struct excl *e;
|
|
+
|
|
+ LIST_FOREACH(e, &excl, n)
|
|
+ if (strcmp(e->p, p) == 0)
|
|
+ return (1);
|
|
+
|
|
+ return (0);
|
|
+}
|
|
+
|
|
+static int
|
|
+compar(const FTSENT **fa, const FTSENT **fb)
|
|
+{
|
|
+ return (strcmp((*fa)->fts_name, (*fb)->fts_name));
|
|
+}
|
|
+
|
|
+static int
|
|
+digest_directory(char *dn, alg_t *alg)
|
|
+{
|
|
+ char in[BUFSIZ * 20], dot[2];
|
|
+ char *digest;
|
|
+ int cc, rc, l, fd, cwd;
|
|
+ char *pathlist[2];
|
|
+ FTS *ftsp;
|
|
+ FTSENT *f;
|
|
+
|
|
+ rc = 1;
|
|
+ l = alg->hash_len * 2;
|
|
+ digest = malloc(l + 1);
|
|
+ sprintf(dot, ".");
|
|
+ pathlist[0] = dot;
|
|
+ pathlist[1] = NULL;
|
|
+
|
|
+ if ((cwd = open(".", O_RDONLY)) == -1 ||
|
|
+ chdir(dn) == -1 ||
|
|
+ (ftsp = fts_open(pathlist,
|
|
+ FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL,
|
|
+ compar)) == NULL) {
|
|
+ (void) fprintf(stderr, "%s\n", dn);
|
|
+ free(digest);
|
|
+ return (0);
|
|
+ }
|
|
+
|
|
+ (*alg->hash_init)(&alg->hash_ctx);
|
|
+
|
|
+ while ((f = fts_read(ftsp)) != NULL) {
|
|
+ /* skip the second pass on a directory */
|
|
+ if (f->fts_info == FTS_DP)
|
|
+ continue;
|
|
+
|
|
+ /* skip directories named CVS, RCS, or SCCS */
|
|
+ if ((f->fts_info == FTS_NS ||
|
|
+ S_ISDIR(f->fts_statp->st_mode)) &&
|
|
+ skip(f->fts_name)) {
|
|
+ fts_set(ftsp, f, FTS_SKIP);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* try to handle things based on stat info */
|
|
+ if (f->fts_info != FTS_NS) {
|
|
+ /* only mention directories */
|
|
+ if (S_ISDIR(f->fts_statp->st_mode)) {
|
|
+ (*alg->hash_init)(&alg->hash_ctx2);
|
|
+ (*alg->hash_update)(&alg->hash_ctx2, "d ", 2);
|
|
+ (*alg->hash_update)(&alg->hash_ctx2, f->fts_path, f->fts_pathlen);
|
|
+ (*alg->hash_end)(&alg->hash_ctx2, digest);
|
|
+ digest[l] = '\n';
|
|
+ (*alg->hash_update)(&alg->hash_ctx, digest, l + 1);
|
|
+
|
|
+ /* hash the filename and then the contents separately */
|
|
+ } else if (S_ISREG(f->fts_statp->st_mode)) {
|
|
+ if ((fd = open(f->fts_path, O_RDONLY)) != -1) {
|
|
+ (*alg->hash_init)(&alg->hash_ctx2);
|
|
+ (*alg->hash_update)(&alg->hash_ctx2, "f ", 2);
|
|
+ (*alg->hash_update)(&alg->hash_ctx2, f->fts_path, f->fts_pathlen);
|
|
+ (*alg->hash_end)(&alg->hash_ctx2, &digest[0]);
|
|
+ digest[l] = '\n';
|
|
+ (*alg->hash_update)(&alg->hash_ctx, digest, 33);
|
|
+
|
|
+ (*alg->hash_init)(&alg->hash_ctx2);
|
|
+ while ((cc = read(fd, in, sizeof(in))) > 0) {
|
|
+ (*alg->hash_update)(&alg->hash_ctx2, in, cc);
|
|
+ }
|
|
+ close(fd);
|
|
+ (*alg->hash_end)(&alg->hash_ctx2, digest);
|
|
+ digest[l] = '\n';
|
|
+ (*alg->hash_update)(&alg->hash_ctx, digest, l + 1);
|
|
+ } else {
|
|
+ (void) fprintf(stderr, "%s\n", f->fts_path);
|
|
+ rc = 0;
|
|
+ }
|
|
+
|
|
+ /* hash in symlinks as well, along with the link contents */
|
|
+ } else if (S_ISLNK(f->fts_statp->st_mode)) {
|
|
+ if ((cc = readlink(f->fts_path, in, sizeof(in))) > 0) {
|
|
+ (*alg->hash_init)(&alg->hash_ctx2);
|
|
+ (*alg->hash_update)(&alg->hash_ctx2, "l ", 2);
|
|
+ (*alg->hash_update)(&alg->hash_ctx2, f->fts_path, f->fts_pathlen);
|
|
+ (*alg->hash_end)(&alg->hash_ctx2, digest);
|
|
+ digest[l] = '\n';
|
|
+ (*alg->hash_update)(&alg->hash_ctx, digest, l + 1);
|
|
+
|
|
+ (*alg->hash_init)(&alg->hash_ctx2);
|
|
+ (*alg->hash_update)(&alg->hash_ctx2, in, cc);
|
|
+ (*alg->hash_end)(&alg->hash_ctx2, digest);
|
|
+ digest[l] = '\n';
|
|
+ (*alg->hash_update)(&alg->hash_ctx, digest, l + 1);
|
|
+ } else {
|
|
+ (void) fprintf(stderr, "%s\n", f->fts_path);
|
|
+ rc = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fts_close(ftsp);
|
|
+ fchdir(cwd);
|
|
+ close(cwd);
|
|
+
|
|
+ if (rc == 1) {
|
|
+ (*alg->hash_end)(&alg->hash_ctx, digest);
|
|
+ (void) printf("%s (%s) = %s\n", alg->name, dn, digest);
|
|
+ }
|
|
+
|
|
+ free(digest);
|
|
+ return (rc);
|
|
+}
|
|
+
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
alg_t *alg;
|
|
int rval;
|
|
int i;
|
|
+ struct stat st;
|
|
|
|
#ifdef HAVE_SETLOCALE
|
|
(void) setlocale(LC_ALL, "");
|
|
#endif
|
|
- while ((i = getopt(argc, argv, "V")) != -1) {
|
|
+ while ((i = getopt(argc, argv, "Vx:")) != -1) {
|
|
switch(i) {
|
|
case 'V':
|
|
printf("%s\n", VERSION);
|
|
return EXIT_SUCCESS;
|
|
+ case 'x':
|
|
+ exclude(optarg);
|
|
+ break;
|
|
}
|
|
}
|
|
argc -= optind;
|
|
@@ -186,7 +342,9 @@ main(int argc, char **argv)
|
|
}
|
|
} else {
|
|
for (i = 0 ; i < argc ; i++) {
|
|
- if (!digest_file(argv[i], alg)) {
|
|
+ if (stat(argv[i], &st) == -1 ||
|
|
+ (S_ISREG(st.st_mode) && !digest_file(argv[i], alg)) ||
|
|
+ (S_ISDIR(st.st_mode) && !digest_directory(argv[i], alg))) {
|
|
(void) fprintf(stderr, "%s\n", argv[i]);
|
|
rval = EXIT_FAILURE;
|
|
}
|