commit: 4d65987d63d5c2e573ae2be35dc5427950ff3677 Author: Fabian Groffen <grobian <AT> gentoo <DOT> org> AuthorDate: Sat Mar 3 21:42:43 2018 +0000 Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> CommitDate: Sat Mar 3 21:42:43 2018 +0000 URL: https://gitweb.gentoo.org/repo/proj/prefix.git/commit/?id=4d65987d
scripts/rsync-generation/hashgen: rewrite hashgen to be simpler a logic rewrite was necessary to be able to properly generate the right type of Manifest for the level at hand scripts/rsync-generation/hashgen.c | 424 ++++++++++++++++++++++--------------- 1 file changed, 256 insertions(+), 168 deletions(-) diff --git a/scripts/rsync-generation/hashgen.c b/scripts/rsync-generation/hashgen.c index 446299f92e..7bedc75ca4 100644 --- a/scripts/rsync-generation/hashgen.c +++ b/scripts/rsync-generation/hashgen.c @@ -342,13 +342,12 @@ write_hashes_dir( } static char -process_files(const char *dir, const char *off, FILE *m) +process_files(struct timeval *tv, const char *dir, const char *off, FILE *m) { char path[8192]; char **dentries; size_t dentrieslen; size_t i; - struct timeval tv[2]; /* dummy, won't use its result */ snprintf(path, sizeof(path), "%s/%s", dir, off); if (list_dir(&dentries, &dentrieslen, path) == 0) { @@ -356,7 +355,7 @@ process_files(const char *dir, const char *off, FILE *m) snprintf(path, sizeof(path), "%s%s%s", off, *off == '\0' ? "" : "/", dentries[i]); free(dentries[i]); - if (process_files(dir, path, m) == 0) + if (process_files(tv, dir, path, m) == 0) continue; /* regular file */ write_hashes(tv, dir, path, "AUX", m, NULL); @@ -452,22 +451,22 @@ parse_layout_conf(const char *path) static char *str_manifest = "Manifest"; static char *str_manifest_gz = "Manifest.gz"; static char *str_manifest_files_gz = "Manifest.files.gz"; +enum type_manifest { + GLOBAL_MANIFEST, /* Manifest.files.gz + Manifest */ + SUBTREE_MANIFEST, /* Manifest.gz for recursive list of files */ + EBUILD_MANIFEST, /* Manifest thick from thin */ + CATEGORY_MANIFEST /* Manifest.gz with Manifest entries */ +}; static char * -process_dir_gen(const char *dir) +generate_dir(const char *dir, enum type_manifest mtype) { - char manifest[8192]; FILE *f; char path[8192]; - const char *p; - int newhashes; - enum { - GLOBAL_MANIFEST, /* Manifest.files.gz + Manifest */ - SUBTREE_MANIFEST, /* Manifest.gz for recursive list of files */ - EBUILD_MANIFEST, /* Manifest thick from thin */ - CATEGORY_MANIFEST /* Manifest.gz with Manifest entries */ - } type_manifest; struct stat s; struct timeval tv[2]; + char **dentries; + size_t dentrieslen; + size_t i; /* our timestamp strategy is as follows: * - when a Manifest exists, use its timestamp @@ -482,155 +481,216 @@ process_dir_gen(const char *dir) tv[1].tv_sec = 0; tv[1].tv_usec = 0; - type_manifest = CATEGORY_MANIFEST; - snprintf(path, sizeof(path), "%s/metadata/layout.conf", dir); - if ((newhashes = parse_layout_conf(path)) != 0) { - type_manifest = GLOBAL_MANIFEST; - hashes = newhashes; - } else { - if ((p = strrchr(dir, '/')) != NULL) { - p++; - } else { - p = dir; + if (mtype == GLOBAL_MANIFEST) { + char *mfest; + size_t len; + gzFile mf; + time_t rtime; + + snprintf(path, sizeof(path), "%s/%s", dir, str_manifest_files_gz); + if ((mf = gzopen(path, "wb9")) == NULL) { + fprintf(stderr, "failed to open file '%s' for writing: %s\n", + path, strerror(errno)); + return NULL; } - if ( - strcmp(p, "eclass") == 0 || - strcmp(p, "licenses") == 0 || - strcmp(p, "metadata") == 0 || - strcmp(p, "profiles") == 0 || - strcmp(p, "scripts") == 0 - ) - { - type_manifest = SUBTREE_MANIFEST; + /* These "IGNORE" entries are taken from gx86, there is no + * standardisation on this, on purpose, apparently. */ + len = snprintf(path, sizeof(path), + "IGNORE distfiles\n" + "IGNORE local\n" + "IGNORE lost+found\n" + "IGNORE packages\n"); + gzwrite(mf, path, len); + + if (list_dir(&dentries, &dentrieslen, dir) != 0) + return NULL; + + for (i = 0; i < dentrieslen; i++) { + /* ignore existing Manifests */ + if (strcmp(dentries[i], str_manifest_files_gz) == 0 || + strcmp(dentries[i], str_manifest) == 0) + { + free(dentries[i]); + continue; + } + + snprintf(path, sizeof(path), "%s/%s", dir, dentries[i]); + + mfest = NULL; + if (!stat(path, &s)) { + if (s.st_mode & S_IFDIR) { + if ( + strcmp(dentries[i], "eclass") == 0 || + strcmp(dentries[i], "licenses") == 0 || + strcmp(dentries[i], "metadata") == 0 || + strcmp(dentries[i], "profiles") == 0 || + strcmp(dentries[i], "scripts") == 0 + ) + { + mfest = generate_dir(path, SUBTREE_MANIFEST); + } else { + mfest = generate_dir(path, CATEGORY_MANIFEST); + } + + if (mfest == NULL) { + fprintf(stderr, "generating Manifest for %s failed!\n", + path); + gzclose(mf); + return NULL; + } + + snprintf(path, sizeof(path), "%s/%s", + dentries[i], mfest); + write_hashes(tv, dir, path, "MANIFEST", NULL, mf); + } else if (s.st_mode & S_IFREG) { + write_hashes(tv, dir, dentries[i], "DATA", NULL, mf); + } /* ignore other "things" (like symlinks) as they + don't belong in a tree */ + } else { + fprintf(stderr, "stat(%s) failed: %s\n", + path, strerror(errno)); + } + free(dentries[i]); } - } + free(dentries); + gzclose(mf); - /* If a Manifest file exists, this is an ebuild dir, unless we - * already established this is the top level dir which also has a - * Manifest file. */ - snprintf(manifest, sizeof(manifest), "%s/%s", dir, str_manifest); - if (type_manifest == GLOBAL_MANIFEST || - (f = fopen(manifest, "r")) == NULL) - { - /* all of these types (GLOBAL, SUBTREE, CATEGORY) have a gzipped - * Manifest */ + if (tv[0].tv_sec != 0) { + snprintf(path, sizeof(path), "%s/%s", dir, str_manifest_files_gz); + utimes(path, tv); + } + + /* create global Manifest */ + snprintf(path, sizeof(path), "%s/%s", dir, str_manifest); + if ((f = fopen(path, "w")) == NULL) { + fprintf(stderr, "failed to open file '%s' for writing: %s\n", + path, strerror(errno)); + return NULL; + } + + write_hashes(tv, dir, str_manifest_files_gz, "MANIFEST", f, NULL); + time(&rtime); + len = strftime(path, sizeof(path), + "TIMESTAMP %Y-%m-%dT%H:%M:%SZ\n", gmtime(&rtime)); + fwrite(path, len, 1, f); + fflush(f); + fclose(f); + + /* because we write a timestamp in Manifest, we don't mess with + * its mtime, else it would obviously lie */ + return str_manifest_files_gz; + } else if (mtype == SUBTREE_MANIFEST) { + const char *ldir; gzFile mf; - char **dentries; - size_t dentrieslen; - size_t i; - if (list_dir(&dentries, &dentrieslen, dir) == 0) { - char *my_manifest = str_manifest_gz; + snprintf(path, sizeof(path), "%s/%s", dir, str_manifest_gz); + if ((mf = gzopen(path, "wb9")) == NULL) { + fprintf(stderr, "failed to open file '%s' for writing: %s\n", + path, strerror(errno)); + return NULL; + } - if (type_manifest == GLOBAL_MANIFEST) - my_manifest = str_manifest_files_gz; + ldir = strrchr(dir, '/'); + if (ldir == NULL) + ldir = dir; + if (strcmp(ldir, "metadata") == 0) { + size_t len; + len = snprintf(path, sizeof(path), + "IGNORE timestamp\n" + "IGNORE timestamp.chk\n" + "IGNORE timestamp.commit\n" + "IGNORE timestamp.x\n"); + gzwrite(mf, path, len); + } + + if (list_dir(&dentries, &dentrieslen, dir) != 0) + return NULL; - snprintf(manifest, sizeof(manifest), "%s/%s", dir, my_manifest); - if ((mf = gzopen(manifest, "wb9")) == NULL) { - fprintf(stderr, "failed to open file '%s' for writing: %s\n", - manifest, strerror(errno)); - return NULL; + for (i = 0; i < dentrieslen; i++) { + /* ignore existing Manifests */ + if (strcmp(dentries[i], str_manifest_gz) == 0) { + free(dentries[i]); + continue; } - for (i = 0; i < dentrieslen; i++) { - /* ignore existing Manifests */ - if (strcmp(dentries[i], my_manifest) == 0 || - strcmp(dentries[i], str_manifest) == 0) - { - free(dentries[i]); - continue; - } + if (write_hashes_dir(tv, dir, dentries[i], mf) != 0) + write_hashes(tv, dir, dentries[i], "DATA", NULL, mf); + free(dentries[i]); + } - snprintf(path, sizeof(path), "%s/%s", dir, dentries[i]); - if (!stat(path, &s)) { - if (s.st_mode & S_IFDIR) { - if (type_manifest == SUBTREE_MANIFEST) { - write_hashes_dir(tv, dir, dentries[i], mf); - if (strcmp(dentries[i], "metadata") == 0) { - char buf[2048]; - size_t len; - len = snprintf(buf, sizeof(buf), - "IGNORE timestamp\n" - "IGNORE timestamp.chk\n" - "IGNORE timestamp.commit\n" - "IGNORE timestamp.x\n"); - gzwrite(mf, buf, len); - } - free(dentries[i]); - } else { - char *mfest = process_dir_gen(path); - if (mfest == NULL) { - gzclose(mf); - free(dentries[i]); - return NULL; - } - snprintf(path, sizeof(path), "%s/%s", - dentries[i], mfest); - free(dentries[i]); - write_hashes(tv, dir, path, "MANIFEST", NULL, mf); - } - } else if (s.st_mode & S_IFREG) { - write_hashes(tv, dir, dentries[i], "DATA", NULL, mf); - free(dentries[i]); - } - } + free(dentries); + gzclose(mf); + + if (tv[0].tv_sec != 0) { + /* set Manifest and dir mtime to most recent file found */ + snprintf(path, sizeof(path), "%s/%s", dir, str_manifest_gz); + utimes(path, tv); + utimes(dir, tv); + } + + return str_manifest_gz; + } else if (mtype == CATEGORY_MANIFEST) { + char *mfest; + gzFile mf; + + snprintf(path, sizeof(path), "%s/%s", dir, str_manifest_gz); + if ((mf = gzopen(path, "wb9")) == NULL) { + fprintf(stderr, "failed to open file '%s' for writing: %s\n", + path, strerror(errno)); + return NULL; + } + + if (list_dir(&dentries, &dentrieslen, dir) != 0) + return NULL; + + for (i = 0; i < dentrieslen; i++) { + /* ignore existing Manifests */ + if (strcmp(dentries[i], str_manifest_gz) == 0) { + free(dentries[i]); + continue; } - free(dentries); - if (type_manifest == GLOBAL_MANIFEST) { - char globmanifest[8192]; - char buf[2048]; - size_t len; - FILE *m; - time_t rtime; - struct timeval ntv[2]; /* dummy, not used */ - - len = snprintf(buf, sizeof(buf), - "IGNORE distfiles\n" - "IGNORE local\n" - "IGNORE lost+found\n" - "IGNORE packages\n"); - gzwrite(mf, buf, len); - gzclose(mf); - - /* create global Manifest */ - snprintf(globmanifest, sizeof(globmanifest), - "%s/%s", dir, str_manifest); - if ((m = fopen(globmanifest, "w")) == NULL) { - fprintf(stderr, "failed to open file '%s' " - "for writing: %s\n", - globmanifest, strerror(errno)); - return NULL; - } + snprintf(path, sizeof(path), "%s/%s", dir, dentries[i]); + if (!stat(path, &s)) { + if (s.st_mode & S_IFDIR) { + mfest = generate_dir(path, EBUILD_MANIFEST); + + if (mfest == NULL) { + fprintf(stderr, "generating Manifest for %s failed!\n", + path); + gzclose(mf); + return NULL; + } - write_hashes(ntv, dir, my_manifest, "MANIFEST", m, NULL); - time(&rtime); - len = strftime(buf, sizeof(buf), - "TIMESTAMP %Y-%m-%dT%H:%M:%SZ\n", gmtime(&rtime)); - fwrite(buf, len, 1, m); - fflush(m); - fclose(m); + snprintf(path, sizeof(path), "%s/%s", + dentries[i], mfest); + write_hashes(tv, dir, path, "MANIFEST", NULL, mf); + } else if (s.st_mode & S_IFREG) { + write_hashes(tv, dir, dentries[i], "DATA", NULL, mf); + } /* ignore other "things" (like symlinks) as they + don't belong in a tree */ } else { - gzclose(mf); + fprintf(stderr, "stat(%s) failed: %s\n", + path, strerror(errno)); } + free(dentries[i]); + } - if (tv[0].tv_sec != 0) { - /* restore dir mtime, and set Manifest mtime to match it */ - utimes(manifest, tv); - utimes(dir, tv); - } + free(dentries); + gzclose(mf); + + if (tv[0].tv_sec != 0) { + /* set Manifest and dir mtime to most ebuild dir found */ + snprintf(path, sizeof(path), "%s/%s", dir, str_manifest_gz); + utimes(path, tv); + utimes(dir, tv); } return str_manifest_gz; - } else { - /* this looks like an ebuild dir, so update the Manifest */ - FILE *m; + } else if (mtype == EBUILD_MANIFEST) { char newmanifest[8192]; - char buf[8192]; - char **dentries; - size_t dentrieslen; - size_t i; + FILE *m; snprintf(newmanifest, sizeof(newmanifest), "%s/.Manifest.new", dir); if ((m = fopen(newmanifest, "w")) == NULL) { @@ -643,20 +703,30 @@ process_dir_gen(const char *dir) * prefixed with AUX, hence, if it exists, we need to do it * first */ snprintf(path, sizeof(path), "%s/files", dir); - process_files(path, "", m); - - /* copy the DIST entries, we could do it unconditional, but this - * way we can re-run without producing invalid Manifests */ - while (fgets(buf, sizeof(buf), f) != NULL) { - if (strncmp(buf, "DIST ", 5) == 0) - if (fwrite(buf, strlen(buf), 1, m) != 1) { - fprintf(stderr, "failed to write to %s/.Manifest.new: %s\n", - dir, strerror(errno)); - fclose(f); - return NULL; - } + process_files(tv, path, "", m); + + /* the Manifest file may be missing in case there are no DIST + * entries to be stored */ + snprintf(path, sizeof(path), "%s/%s", dir, str_manifest); + if (!stat(path, &s)) + update_times(tv, &s); + f = fopen(path, "r"); + if (f != NULL) { + /* copy the DIST entries, we could do it unconditional, but this + * way we can re-run without producing invalid Manifests */ + while (fgets(path, sizeof(path), f) != NULL) { + if (strncmp(path, "DIST ", 5) == 0) + if (fwrite(path, strlen(path), 1, m) != 1) { + fprintf(stderr, "failed to write to " + "%s/.Manifest.new: %s\n", + dir, strerror(errno)); + fclose(f); + fclose(m); + return NULL; + } + } + fclose(f); } - fclose(f); if (list_dir(&dentries, &dentrieslen, dir) == 0) { for (i = 0; i < dentrieslen; i++) { @@ -678,25 +748,43 @@ process_dir_gen(const char *dir) fflush(m); fclose(m); - if (stat(manifest, &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/%s", dir, str_manifest); + rename(newmanifest, path); - rename(newmanifest, manifest); if (tv[0].tv_sec != 0) { - /* restore dir mtime, and set Manifest mtime to match it */ - utimes(manifest, tv); + /* set Manifest and dir mtime to most recent file we found */ + utimes(path, tv); utimes(dir, tv); } return str_manifest; + } else { + return NULL; + } +} + +static char * +process_dir_gen(const char *dir) +{ + char path[8192]; + int newhashes; + + snprintf(path, sizeof(path), "%s/metadata/layout.conf", dir); + if ((newhashes = parse_layout_conf(path)) != 0) { + hashes = newhashes; + } else { + return "generation must be done on a full tree"; + } + + if (chdir(dir) != 0) { + fprintf(stderr, "cannot chdir() to %s: %s\n", dir, strerror(errno)); + return "not a directory"; } + + if (generate_dir(".\0", GLOBAL_MANIFEST) == NULL) + return "generation failed"; + + return NULL; } static char @@ -1396,14 +1484,14 @@ main(int argc, char *argv[]) if (argc > 1) { for (; arg < argc; arg++) { rsn = runfunc(argv[arg]); - if (runfunc == &process_dir_vrfy && rsn != NULL) { + if (rsn != NULL) { printf("%s\n", rsn); ret |= 1; } } } else { rsn = runfunc("."); - if (runfunc == &process_dir_vrfy && rsn != NULL) { + if (rsn != NULL) { printf("%s\n", rsn); ret |= 1; }