commit: bb8b83447563ef869f083703e01ad9d6fdceb1cb
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Wed Nov 29 14:28:01 2017 +0000
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Wed Nov 29 14:29:00 2017 +0000
URL: https://gitweb.gentoo.org/repo/proj/prefix.git/commit/?id=bb8b8344
hashgen: first pass on generating cascading Manifest.gz files
scripts/rsync-generation/hashgen.c | 215 +++++++++++++++++++++++++++++--------
1 file changed, 173 insertions(+), 42 deletions(-)
diff --git a/scripts/rsync-generation/hashgen.c
b/scripts/rsync-generation/hashgen.c
index ddc52752b4..68d5575ad2 100644
--- a/scripts/rsync-generation/hashgen.c
+++ b/scripts/rsync-generation/hashgen.c
@@ -2,6 +2,7 @@
#include <stdio.h>
#include <string.h>
#include <strings.h>
+#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
@@ -10,14 +11,16 @@
#include <openssl/sha.h>
#include <openssl/whrlpool.h>
#include <blake2.h>
+#include <zlib.h>
/* Generate thick Manifests based on thin Manifests */
/* In order to build this program, the following packages are required:
* - app-crypt/libb2 (for BLAKE2, for as long as openssl doesn't include it)
* - dev-libs/openssl (for SHA, WHIRLPOOL)
+ * - sys-libs/zlib (for compressing Manifest files)
* compile like this
- * ${CC} -o hashgen -fopenmp ${CFLAGS} -lssl -lcrypto -lb2 hashgen.c
+ * ${CC} -o hashgen -fopenmp ${CFLAGS} -lssl -lcrypto -lb2 -lz hashgen.c
*/
enum hash_impls {
@@ -26,7 +29,8 @@ enum hash_impls {
HASH_WHIRLPOOL = 1<<2,
HASH_BLAKE2B = 1<<3
};
-/* default changed from sha256, sha512, whirlpool to blake2b, sha512 */
+/* default changed from sha256, sha512, whirlpool
+ * to blake2b, sha512 on 2017-11-21 */
static int hashes = HASH_BLAKE2B | HASH_SHA512;
static inline void
@@ -39,16 +43,21 @@ hex_hash(char *out, const unsigned char *buf, const int
length)
}
static void
-write_hashes(const char *root, const char *name, const char *type, FILE *m)
+write_hashes(
+ const char *root,
+ const char *name,
+ const char *type,
+ FILE *m,
+ gzFile gm)
{
FILE *f;
- char fname[8096];
+ char fname[8192];
size_t flen = 0;
char sha256[(SHA256_DIGEST_LENGTH * 2) + 1];
char sha512[(SHA512_DIGEST_LENGTH * 2) + 1];
char whrlpl[(WHIRLPOOL_DIGEST_LENGTH * 2) + 1];
char blak2b[(BLAKE2B_OUTBYTES * 2) + 1];
- char data[8096];
+ char data[8192];
size_t len;
SHA256_CTX s256;
SHA512_CTX s512;
@@ -121,28 +130,37 @@ write_hashes(const char *root, const char *name, const
char *type, FILE *m)
if (hashes & HASH_BLAKE2B) {
unsigned char blak2bbuf[BLAKE2B_OUTBYTES];
blake2b_final(&bl2b, blak2bbuf,
BLAKE2B_OUTBYTES);
- hex_hash(blak2b, blak2bbuf,
WHIRLPOOL_DIGEST_LENGTH);
+ hex_hash(blak2b, blak2bbuf, BLAKE2B_OUTBYTES);
}
}
}
fclose(f);
- fprintf(m, "%s %s %zd",type, name, flen);
+ len = snprintf(data, sizeof(data), "%s %s %zd", type, name, flen);
if (hashes & HASH_BLAKE2B)
- fprintf(m, " BLAKE2B %s", blak2b);
+ len += snprintf(data + len, sizeof(data) - len,
+ " BLAKE2B %s", blak2b);
if (hashes & HASH_SHA256)
- fprintf(m, " SHA256 %s", sha256);
+ len += snprintf(data + len, sizeof(data) - len,
+ " SHA256 %s", sha256);
if (hashes & HASH_SHA512)
- fprintf(m, " SHA512 %s", sha512);
+ len += snprintf(data + len, sizeof(data) - len,
+ " SHA512 %s", sha512);
if (hashes & HASH_WHIRLPOOL)
- fprintf(m, " WHIRLPOOL %s", whrlpl);
- fprintf(m, "\n");
+ len += snprintf(data + len, sizeof(data) - len,
+ " WHIRLPOOL %s", whrlpl);
+ len += snprintf(data + len, sizeof(data) - len, "\n");
+
+ if (m != NULL)
+ fwrite(data, 1, len, m);
+ if (gm != NULL)
+ gzwrite(gm, data, len);
}
static char
process_files(const char *dir, const char *off, FILE *m)
{
- char path[8096];
+ char path[8192];
DIR *d;
struct dirent *e;
@@ -157,7 +175,7 @@ process_files(const char *dir, const char *off, FILE *m)
if (process_files(dir, path, m))
continue;
/* regular file */
- write_hashes(dir, path, "AUX", m);
+ write_hashes(dir, path, "AUX", m, NULL);
}
closedir(d);
return 1;
@@ -166,17 +184,124 @@ process_files(const char *dir, const char *off, FILE *m)
}
}
-static void
+static int
+parse_layout_conf(const char *path)
+{
+ FILE *f;
+ char buf[8192];
+ size_t len = 0;
+ size_t sz;
+ char *p;
+ char *q;
+ char *tok;
+ char *last_nl;
+ int ret = 0;
+
+ if ((f = fopen(path, "r")) == NULL)
+ return 0;
+
+ /* read file, examine lines after encountering a newline, that is,
+ * if the file doesn't end with a newline, the final bit is ignored */
+ while (sz = fread(buf + len, 1, sizeof(buf) - len, f) > 0) {
+ len += sz;
+ last_nl = NULL;
+ for (p = buf; p - buf < len; p++) {
+ if (*p == '\n') {
+ last_nl = p;
+ sz = strlen("manifest-hashes");
+ if (strncmp(buf, "manifest-hashes", sz))
+ continue;
+ if ((q = strchr(buf + sz, '=')) == NULL)
+ continue;
+ q++;
+ while (isspace((int)*q))
+ q++;
+ /* parse the tokens, whitespace separated */
+ tok = q;
+ do {
+ while (!isspace((int)*q))
+ q++;
+ sz = q - tok;
+ if (strncmp(tok, "SHA256", sz) == 0) {
+ ret |= HASH_SHA256;
+ } else if (strncmp(tok, "SHA512", sz)
== 0) {
+ ret |= HASH_SHA512;
+ } else if (strncmp(tok, "WHIRLPOOL",
sz) == 0) {
+ ret |= HASH_WHIRLPOOL;
+ } else if (strncmp(tok, "BLAKE2B", sz)
== 0) {
+ ret |= HASH_BLAKE2B;
+ }
+ while (isspace((int)*q) && *q != '\n')
+ q++;
+ tok = q;
+ } while (*q != '\n');
+ /* got it, expect only once, so stop processing
*/
+ fclose(f);
+ return ret;
+ }
+ }
+ if (last_nl != NULL) {
+ last_nl++; /* skip \n */
+ len = last_nl - buf;
+ memmove(buf, last_nl, len);
+ } else {
+ /* too long line, just skip */
+ len = 0;
+ }
+ }
+
+ fclose(f);
+ return 0;
+}
+
+static char *str_manifest = "Manifest";
+static char *str_manifest_gz = "Manifest.gz";
+static char *
process_dir(const char *dir)
{
- char manifest[8096];
+ char manifest[8192];
FILE *f;
DIR *d;
struct dirent *e;
- char path[8096];
+ char path[8192];
+ int newhashes;
+ char global_manifest = 0;
+ struct stat s;
+ struct timeval tv[2];
+
+ /* set mtime of Manifest(.gz) to the one of the parent dir, this way
+ * we ensure the Manifest gets mtime bumped upon any change made
+ * to the directory, that is, a DIST change (Manifest itself) or
+ * any other change (ebuild, files, metadata) */
+ if (stat(dir, &s)) {
+ tv[0].tv_sec = 0;
+ tv[0].tv_usec = 0;
+ } else {
+ tv[0].tv_sec = s.st_atim.tv_sec;
+ tv[0].tv_usec = s.st_atim.tv_nsec / 1000;
+ tv[1].tv_sec = s.st_mtim.tv_sec;
+ tv[1].tv_usec = s.st_mtim.tv_nsec / 1000;
+ }
+
+ snprintf(path, sizeof(path), "%s/metadata/layout.conf", dir);
+ if ((newhashes = parse_layout_conf(path)) != 0) {
+ global_manifest = 1;
+ hashes = newhashes;
+ }
snprintf(manifest, sizeof(manifest), "%s/Manifest", dir);
if ((f = fopen(manifest, "r")) == NULL) {
+ gzFile mf;
+
+ /* open up a gzipped Manifest to keep the hashes of the
+ * Manifests in the subdirs */
+ snprintf(manifest, sizeof(manifest), "%s/Manifest.gz", dir);
+ if ((mf = gzopen(manifest, "wb9")) == NULL) {
+ fprintf(stderr, "failed to open file '%s' for writing:
%s\n",
+ manifest, strerror(errno));
+ return NULL;
+ }
+
/* recurse into subdirs */
if ((d = opendir(dir)) != NULL) {
struct stat s;
@@ -184,38 +309,42 @@ process_dir(const char *dir)
if (e->d_name[0] == '.')
continue;
snprintf(path, sizeof(path), "%s/%s", dir,
e->d_name);
- if (!stat(path, &s) && s.st_mode & S_IFDIR)
- process_dir(path);
+ if (!stat(path, &s)) {
+ if (s.st_mode & S_IFDIR) {
+ char *mfest = process_dir(path);
+ if (mfest == NULL) {
+ gzclose(mf);
+ return NULL;
+ }
+ snprintf(path, sizeof(path),
"%s/%s", e->d_name, mfest);
+ write_hashes(dir, path,
"MANIFEST", NULL, mf);
+ } else if (s.st_mode & S_IFREG) {
+ write_hashes(dir, e->d_name,
"DATA", NULL, mf);
+ }
+ }
}
closedir(d);
}
+
+ gzclose(mf);
+ if (tv[0].tv_sec != 0) {
+ /* restore dir mtime, and set Manifest mtime to match
it */
+ utimes(manifest, tv);
+ utimes(dir, tv);
+ }
+
+ return str_manifest_gz;
} else {
/* this looks like an ebuild dir, so update the Manifest */
FILE *m;
- char newmanifest[8096];
- char buf[8096];
- struct stat s;
- struct timeval tv[2];
-
- /* set mtime of Manifest to the one of the parent dir, this way
- * we ensure the Manifest gets mtime bumped upon any change made
- * to the directory, that is, a DIST change (Manifest itself) or
- * any other change (ebuild, files, metadata) */
- if (stat(dir, &s)) {
- tv[0].tv_sec = 0;
- tv[0].tv_usec = 0;
- } else {
- tv[0].tv_sec = s.st_atim.tv_sec;
- tv[0].tv_usec = s.st_atim.tv_nsec / 1000;
- tv[1].tv_sec = s.st_mtim.tv_sec;
- tv[1].tv_usec = s.st_mtim.tv_nsec / 1000;
- }
+ char newmanifest[8192];
+ char buf[8192];
snprintf(newmanifest, sizeof(newmanifest), "%s/.Manifest.new",
dir);
if ((m = fopen(newmanifest, "w")) == NULL) {
fprintf(stderr, "failed to open file '%s' for writing:
%s\n",
newmanifest, strerror(errno));
- return;
+ return NULL;
}
/* we know the Manifest is sorted, and stuff in files/ is
@@ -232,7 +361,7 @@ process_dir(const char *dir)
fprintf(stderr, "failed to write to
%s/.Manifest.new: %s\n",
dir, strerror(errno));
fclose(f);
- return;
+ return NULL;
}
}
fclose(f);
@@ -246,13 +375,13 @@ process_dir(const char *dir)
continue;
if (strcmp(e->d_name + strlen(e->d_name) - 7,
".ebuild") != 0)
continue;
- write_hashes(dir, e->d_name, "EBUILD", m);
+ write_hashes(dir, e->d_name, "EBUILD", m, NULL);
}
closedir(d);
}
- write_hashes(dir, "ChangeLog", "MISC", m);
- write_hashes(dir, "metadata.xml", "MISC", m);
+ write_hashes(dir, "ChangeLog", "MISC", m, NULL);
+ write_hashes(dir, "metadata.xml", "MISC", m, NULL);
fflush(m);
fclose(m);
@@ -263,6 +392,8 @@ process_dir(const char *dir)
utimes(manifest, tv);
utimes(dir, tv);
}
+
+ return str_manifest;
}
}