diff options
| author | Mattias Andrée <m@maandree.se> | 2026-05-28 18:46:27 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-05-28 18:46:27 +0200 |
| commit | 55e9f58fa7c551aebfd6084b778cd942fa8b20ce (patch) | |
| tree | 7177e49a9c3a3c2e9cd4f88e8507cb19c9f6c1b0 | |
| parent | m fix (diff) | |
| download | release-scripts-55e9f58fa7c551aebfd6084b778cd942fa8b20ce.tar.gz release-scripts-55e9f58fa7c551aebfd6084b778cd942fa8b20ce.tar.bz2 release-scripts-55e9f58fa7c551aebfd6084b778cd942fa8b20ce.tar.xz | |
Update (fixes support for a few edge cases, and leaves nothing laying around)
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
| -rw-r--r-- | LICENSE | 24 | ||||
| -rw-r--r-- | README | 2 | ||||
| -rwxr-xr-x | gen-checksums | 106 | ||||
| -rw-r--r-- | util/.gitignore | 18 | ||||
| -rw-r--r-- | util/.makeenv | 2 | ||||
| -rw-r--r-- | util/Makefile | 36 | ||||
| -rw-r--r-- | util/README | 80 | ||||
| -rw-r--r-- | util/config.mk | 10 | ||||
| -rwxr-xr-x | util/gen-checksums | 23 | ||||
| -rwxr-xr-x | util/get-and-check | 17 | ||||
| -rwxr-xr-x | util/get-checksums | 42 | ||||
| -rwxr-xr-x | util/order-checksums | 16 | ||||
| -rw-r--r-- | util/repodiff.c | 316 | ||||
| -rw-r--r-- | util/tmpmount.c | 44 | ||||
| -rwxr-xr-x | util/validate-tarball | 27 |
15 files changed, 663 insertions, 100 deletions
@@ -1,6 +1,20 @@ -Copyright © 2024, 2025, 2026 Mattias Andrée (m@maandree.se) +ISC License -Copying and distribution of these scripts, with or without modification, -are permitted in any medium without royalty provided the copyright -notice and this notice are preserved. These scripts is offered as-is, -without any warranty. +© 2024, 2025, 2026 Mattias Andrée <m@maandree.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------- + +./maandree-dl and ./validate-checksum are under more lax license, +their license text is available near the top of those files. @@ -8,7 +8,7 @@ a git tag, I know that my local git repositry is clean. This is used as the reference for truth. I also create a tarball for a static release. I then use ./gen-checksums which checks the tarball, along with my non-static releases (created by pushing the git tags), against my local -git repositry. Once all releases have been validated, ./gen-checksums +git repositry. Once a release have been validated, ./gen-checksums outputs the checksums for each tarball, using a number of hash functions. The checksums are not specifically tied to the tarballs, but rather listed as known good checksums. diff --git a/gen-checksums b/gen-checksums index 785aff6..f894a38 100755 --- a/gen-checksums +++ b/gen-checksums @@ -1,104 +1,22 @@ #!/bin/sh -# Copyright © 2024, 2025 Mattias Andrée (m@maandree.se) -# -# Copying and distribution of this script, with or without modification, -# are permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. This script is offered as-is, -# without any warranty. - - set -e -if ! test $# = 2 || test -t 0 || test -t 1; then - printf 'usage: %s tarball git-dir < version-info-file > checksum-listing\n' "$0" >&2 +if test ! $# = 3; then + printf 'usage: %s git-dir project version\n' "$0" >&2 exit 1 fi -tarball="$1" -gitdir="$2" - -urls="$(grep '^Tarball: ' | cut -d ' ' -f 2 | grep -v '^https://maandree.se/static/')" - -workdir="$$.tmpdir" -rm -rf -- "${workdir}" - -refdir="${workdir}/reference" -mkdir -p -- "${refdir}" -gunzip < "${tarball}" | (cd "${refdir}" && tar -x) -if test "$(printf '%s\n' "${refdir}"/* | wc -l)" = 1; then - refdir="$(printf '%s\n' "${refdir}"/*)" -fi - -ln -s -- "$(cd -- "${gitdir}" && pwd)" "${workdir}/gitdir" - -add_dirs () { - while read f; do - printf '%s\n' "$f" - while :; do - d="$(printf '%s\n' "$f" | sed 's|/[^/]*$||')" - if test "$d" = "$f"; then - break - fi - printf '%s\n' "$d" - f="$d" - done - done -} - -gitfiles="$( (cd -- "${gitdir}" && git ls-files) | add_dirs | sort -u | tee -- "${workdir}/gitfiles")" -test -n "${gitfiles}" -tarfiles="$( (cd -- "${refdir}" && find) | sed 's/^\.\///' | sed '/^\.$/d' | add_dirs | sort -u | tee -- "${workdir}/tarfiles")" -test -n "${tarfiles}" -diff -u -- "${workdir}/gitfiles" "${workdir}/tarfiles" >&2 -# the following line makes the safe assumption that $workdir (and $refdir) does not contain {} -xargs -I {} diff -u "${refdir}"/{} "${workdir}/gitdir"/{} < "${workdir}/gitfiles" >&2 - -getandcheck () { - url="$1" - number="$2" - curl -sL -- "${url}" > "${workdir}/downloaded-${number}.tar.gz" - checkdir="${workdir}/downloaded" - rm -rf -- "${checkdir}" - mkdir -p -- "${checkdir}" - gunzip < "${workdir}/downloaded-${number}.tar.gz" | (cd "${checkdir}" && tar -x) - if test "$(printf '%s\n' "${checkdir}"/* | wc -l)" = 1; then - checkdir="$(printf '%s\n' "${checkdir}"/*)" - fi - diff -ur -- "${refdir}" "${checkdir}" >&2 - rm -rf -- "${checkdir}" -} - -nr=1 -for url in $urls; do - getandcheck "${url}" $(( nr++ )) -done - -genfor () { - tool="$1" - name="$2" - sum="$(${tool} < "${tarball}" | cut -d ' ' -f 1)" - sums="$(printf '%s checksum: %s\n' "${name}" "${sum}")" - number=1 - for url in $urls; do - sum="$($tool < "${workdir}/downloaded-${number}.tar.gz" | cut -d ' ' -f 1)" - test -n "$sum" - sums="$(printf '%s\n%s checksum: %s\n' "${sums}" "${name}" "${sum}")" - : $(( number++ )) - done - printf '%s\n' "${sums}" | sort -u +die () { + printf '%s\n' "$@" >&2 + exit 1 } -genfor sha224sum SHA224 -genfor sha256sum SHA256 -genfor sha384sum SHA384 -genfor sha512sum SHA512 -genfor sha512-224sum SHA512/224 -genfor sha512-256sum SHA512/256 -genfor sha3-224sum SHA3-224 -genfor sha3-256sum SHA3-256 -genfor sha3-384sum SHA3-384 -genfor sha3-512sum SHA3-512 -genfor b2sum BLAKE2b +gitdir="$1" +tarball=".static/$2-$3.tar.gz" +infofile="rel/$2/$3.info" +sumfile="rel/$2/$3.checksums" +test -e "${tarball}" || die "${tarball} does not exist" +test -e "${infofile}" || die "${infofile} does not exist" -rm -rf -- "${workdir}" +"$(dirname -- "$0")"/util/gen-checksums "${tarball}" "${gitdir}" < "${infofile}" > "${sumfile}" diff --git a/util/.gitignore b/util/.gitignore new file mode 100644 index 0000000..88f9aca --- /dev/null +++ b/util/.gitignore @@ -0,0 +1,18 @@ +*\#* +*~ +*.o +*.a +*.t +*.lo +*.to +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda +/tmpmount +/repodiff diff --git a/util/.makeenv b/util/.makeenv new file mode 100644 index 0000000..f345ae5 --- /dev/null +++ b/util/.makeenv @@ -0,0 +1,2 @@ +MAKEENV_OPTS_OPT_ATTACHED_ARG = j +CC = gcc-extreme -std=c99 diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..d5e3a6c --- /dev/null +++ b/util/Makefile @@ -0,0 +1,36 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +BIN =\ + tmpmount\ + repodiff + +OBJ =\ + tmpmount.o\ + repodiff.o + +HDR = + +all: $(BIN) +$(OBJ): $(HDR) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +tmpmount: tmpmount.o + $(CC) -o $@ tmpmount.o $(LDFLAGS) + $(ASROOT) sh -c "chown -- '0:0' $@ && chmod -- 4755 $@" + +repodiff: repodiff.o + $(CC) -o $@ repodiff.o $(LDFLAGS) + +clean: + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.gch *.gcov *.gcno *.gcda + -rm -f -- $(BIN) + +.SUFFIXES: +.SUFFIXES: .o .c + +.PHONY: all clean diff --git a/util/README b/util/README new file mode 100644 index 0000000..b4ce9b4 --- /dev/null +++ b/util/README @@ -0,0 +1,80 @@ +tmpmount mountpoint command [argument] ... + + Mounts a tmpfs at `mountpoint` and execs into + `command [argument] ...`. The tmpfs is private + to the process and it's children. + + +repodiff directory-1 file-list-1 directory-2 file-list-2 + + Checks for differences betweens the files listed in + file-list-1, which are relative to directory-1, and + files listed in file-list-2, which are relative to + directory-2. Will only check for things supported + by git: file listing, file names, file content, + symlink target, and executable by owner. (Files + that are not regular files, symbolic links or + directories will cause failure.) + + file-list-1 and file-list-2 use <nul> termination + rather then <newline> termination, meaning that + if the file listed is created with file(1), + `-print0` should have been used, or if with + git-ls-files(1), `-z` should have been used. + + If a difference is found 1 is returned, 0 otherwise. + Exit value 2 is used to signal runtime error. + + +validate-tarball tarball git-dir work-dir + + Validates the contents a tarball againts a known + good directory (git-dir). An empty directory + (work-dir) shall be provided as a space for + temporary files. + + +order-checksums + + Sorts standard input, removed duplicates checksums + and output the checksums lines primarily ordered + by the hash algorithms in a particular order and + secondarily by the checksums alphabetically sorted. + + +get-checksums tarball + + Calculates the checksums of a tarball. + + A special argument can be provided for printing + the order the hash algorithms are output in + + +get-and-check tarball-url reference-dir work-dir + + Downloads tarball-url and uses ./validate-tarball + to validate its contents against a known good + directory (reference-dir). An empty directory + (work-dir) shall be provided as a space for + temporary files. If the content of the tarball + matches git-tracked files in reference-dir, + ./get-checksums is used to output the tarball's + checksums. + + +gen-checksums tarball git-dir < version-info-file > checksum-listing + + This brings all of the above together. The tarball, + which is assumed to be the tarball for the static release + is validates against a known good directory (git-dir), + for which the tarball is supposted to be generated from, + but this check validates that git itself is not manipulating + the tarballs. Then it reads stdin for URLs in lines that + begin with "Tarball: " (however, the URL for the static + release is discarded as it is assumed to not exist yet + but be the same file as input as the argument). These + URLs are used to download release tarballs from all + mirrors; they are also validates against the known good + directory. The checksums for all tarballs, both the + one provided in the command line and those downloaded, + are output to stdout. diff --git a/util/config.mk b/util/config.mk new file mode 100644 index 0000000..d78941c --- /dev/null +++ b/util/config.mk @@ -0,0 +1,10 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = c99 + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = +LDFLAGS = -lsimple + +ASROOT = asroot diff --git a/util/gen-checksums b/util/gen-checksums new file mode 100755 index 0000000..bb1f981 --- /dev/null +++ b/util/gen-checksums @@ -0,0 +1,23 @@ +#!/bin/sh +# See LICENSE file for copyright and license details. + +set -e + +if test ! $# = 2 || test -t 0 || test -t 1; then + printf 'usage: %s tarball git-dir < version-info-file > checksum-listing\n' "$0" >&2 +fi + +tarball="$1" +gitdir="$2" + +utildir="$(dirname -- "$0")" +(cd -- "${utildir}" && (make >/dev/null 2>/dev/null || make)) + +"${utildir}"/tmpmount /var/empty "${utildir}"/validate-tarball "${tarball}" "${gitdir}" /var/empty + +grep '^Tarball: ' | cut -d ' ' -f 2 | grep -v '^https://maandree.se/static/' | ( + "${utildir}"/get-checksums "${tarball}" + while read -r url; do + "${utildir}"/tmpmount /var/empty "${utildir}"/get-and-check "${url}" "${gitdir}" /var/empty + done +) | "${utildir}"/order-checksums diff --git a/util/get-and-check b/util/get-and-check new file mode 100755 index 0000000..da9f1aa --- /dev/null +++ b/util/get-and-check @@ -0,0 +1,17 @@ +#!/bin/sh +# See LICENSE file for copyright and license details. + +set -e + +if ! test $# = 3; then + printf 'usage: %s tarball-url reference-dir work-dir\n' "$0" >&2 + exit 1 +fi + +utildir="$(dirname -- "$0")" +tarball="$3/download-$$.tar.gz" + +curl -sL -- "$1" > "${tarball}" +"${utildir}"/validate-tarball "${tarball}" "$2" "$3" +"${utildir}"/get-checksums "${tarball}" +rm -f -- "${tarball}" diff --git a/util/get-checksums b/util/get-checksums new file mode 100755 index 0000000..9d1b86a --- /dev/null +++ b/util/get-checksums @@ -0,0 +1,42 @@ +#!/bin/sh +# See LICENSE file for copyright and license details. + +set -e + +gensums () { + gensum sha224sum SHA224 + gensum sha256sum SHA256 + gensum sha384sum SHA384 + gensum sha512sum SHA512 + gensum sha512-224sum SHA512/224 + gensum sha512-256sum SHA512/256 + gensum sha3-224sum SHA3-224 + gensum sha3-256sum SHA3-256 + gensum sha3-384sum SHA3-384 + gensum sha3-512sum SHA3-512 + gensum b2sum BLAKE2b +} + +if test $# = 1 && test "$1" = '-- output checksum order --'; then + gensum () { printf '%s\n' "$2"; } + gensums + exit 0 +fi + +if ! test $# = 1; then + printf 'usage: %s tarball\n' "$0" >&2 + exit 1 +fi + +tarball="$1" + +gensum () { + tool="$1" + name="$2" + + sum="$(${tool} < "${tarball}" | cut -d ' ' -f 1)" + test -n "${sum}" + printf '%s checksum: %s\n' "${name}" "${sum}" +} + +gensums diff --git a/util/order-checksums b/util/order-checksums new file mode 100755 index 0000000..00abca9 --- /dev/null +++ b/util/order-checksums @@ -0,0 +1,16 @@ +#!/bin/sh +# See LICENSE file for copyright and license details. + +set -e + +if ! test $# = 0; then + printf 'usage: %s < unordered-checksum-table > ordered-checksum-table \n' "$0" >&2 + exit 1 +fi + +utildir="$(dirname -- "$0")" + +text="$(sort -u)" +"${utildir}"/get-checksums '-- output checksum order --' | while read sum; do + printf '%s\n' "${text}" | grep "^${sum} checksum:" +done diff --git a/util/repodiff.c b/util/repodiff.c new file mode 100644 index 0000000..b27aef6 --- /dev/null +++ b/util/repodiff.c @@ -0,0 +1,316 @@ +/* See LICENSE file for copyright and license details. */ +#include <libsimple.h> +#include <libsimple-arg.h> + +NUSAGE(2, "directory-1 file-list-1 directory-2 file-list-2"); + + +LIBSIMPLE_PURE +static int +parse_fd(const char *s, int dash_fd) +{ + int fd, d; + + if (streq(s, "/dev/stdin")) + return STDIN_FILENO; + if (streq(s, "/dev/stdout")) + return STDOUT_FILENO; + if (streq(s, "/dev/stderr")) + return STDERR_FILENO; + if (streq(s, "-")) + return dash_fd; + + if (strstarts(s, "/proc/self/fd/")) + s = &s[sizeof("/proc/self/fd/") - 1u]; + else if (strstarts(s, "/dev/fd/")) + s = &s[sizeof("/dev/fd/") - 1u]; + else + return -1; + + if ('0' > *s || *s > '9') + return -1; + fd = (int)(*s++ - '0'); + while ('0' <= *s && *s <= '9') { + d = (int)(*s - '0'); + if (fd > (INT_MAX - d) / 10) + return -1; + fd = fd * 10 + d; + } + + return *s ? -1 : fd; +} + + +static char ** +get_file_list(const char *path, size_t *n_out) +{ + char *buf = NULL; + size_t bufsize = 0u; + size_t len = 0u; + size_t off, i, j, d; + int fd, do_close; + ssize_t r; + char **list = NULL; + size_t listsize = 0u; + + *n_out = 0u; + + do_close = 0; + fd = parse_fd(path, STDIN_FILENO); + if (fd < 0) { + do_close = 1; + fd = open(path, O_RDONLY); + if (fd < 0) + eprintf("open %s O_RDONLY:", path); + } + + for (;;) { + if (len == bufsize) + buf = erealloc(buf, bufsize += (size_t)8 << 10); + r = read(fd, &buf[len], bufsize - len); + if (r <= 0) { + if (!r) + break; + if (errno == EINTR) + continue; + eprintf("read %s:", path); + } + len += (size_t)r; + } + + if (do_close) + close(fd); + + buf = erealloc(buf, len + 1u); + buf[len++] = '\0'; + + off = 0u; + for (i = 0u; i < len; i++) { + if (buf[i]) + continue; + if (i == off) + goto next; + while (buf[off] == '/') + off += 1u; + if (buf[off + 0u] == '.' && buf[off + 1u] == '/') + off += 2u; + if (!buf[off]) + goto next; + if (buf[off + 0u] == '.' && !buf[off + 1u]) + goto next; + + d = buf[i - 1u] == '/' ? 1u : 0u; + buf[i - d] = '\0'; + if (!buf[off]) + goto next; + + if (*n_out == listsize) + list = ereallocarray(list, listsize += 128u, sizeof(*list)); + list[(*n_out)++] = estrdup(&buf[off]); + for (j = i - 1u - d; j > off; j--) { + if (buf[j] != '/') + continue; + buf[j] = '\0'; + if (*n_out == listsize) + list = ereallocarray(list, listsize += 128u, sizeof(*list)); + list[(*n_out)++] = estrdup(&buf[off]); + } + + next: + off = i + 1u; + } + + free(buf); + return list; +} + + +static size_t +uniq(char **list, size_t n) +{ + size_t r, w = 1u; + if (!n) + return 0u; + for (r = 1u; r < n; r++) { + if (strcmp(list[r], list[w - 1u])) + list[w++] = list[r]; + else + free(list[r]); + } + return w; +} + + +static int +files_equal(int fd1, const char *dir1, int fd2, const char *dir2, const char *path) +{ + static char buf1[8192]; + static char buf2[sizeof(buf1)]; + size_t len1, len2; + ssize_t r; + + do { + len1 = 0u; + while (len1 < sizeof(buf1)) { + r = read(fd1, &buf1[len1], sizeof(buf1) - len1); + if (r <= 0) { + if (!r) + break; + if (errno == EINTR) + continue; + eprintf("read %s/%s:", dir1, path); + } + len1 += (size_t)r; + } + + len2 = 0u; + while (len2 < sizeof(buf2)) { + r = read(fd2, &buf2[len2], sizeof(buf2)); + if (r <= 0) { + if (!r) + break; + if (errno == EINTR) + continue; + eprintf("read %s/%s:", dir2, path); + } + len2 += (size_t)r; + } + + if (len1 != len2 || memcmp(buf1, buf2, len1)) + return 0; + } while (len1 && len2); + + return !len1 && !len2; +} + + +int +main(int argc, char *argv[]) +{ + struct stat st1, st2; + const char *dir1, *dir2; + char **files1, **files2; + size_t nfiles1, nfiles2, i, j; + char *target1, *target2; + int dirfd1, dirfd2, cmp; + int fd1, fd2; + int ret = 0; + + libsimple_default_failure_exit = 2; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc != 4) + usage(); + + dir1 = argv[0]; + dir2 = argv[2]; + dirfd1 = open(dir1, O_PATH); + if (dirfd1 < 0) + eprintf("open %s O_PATH", dir1); + dirfd2 = open(dir2, O_PATH); + if (dirfd2 < 0) + eprintf("open %s O_PATH", dir2); + + files1 = get_file_list(argv[1], &nfiles1); + files2 = get_file_list(argv[3], &nfiles2); + libsimple_qsort_str((void *)files1, nfiles1); + libsimple_qsort_str((void *)files2, nfiles2); + nfiles1 = uniq(files1, nfiles1); + nfiles2 = uniq(files2, nfiles2); + + i = j = 0u; + while (i < nfiles1 || j < nfiles2) { + if (i == nfiles1) { + exclusive_to_dir2: + weprintf("%s exists only in %s\n", files2[j], dir2); + j++; + ret = 1; + continue; + } + if (j == nfiles2) { + exclusive_to_dir1: + weprintf("%s exists only in %s\n", files1[i], dir1); + i++; + ret = 1; + continue; + } + + cmp = strcmp(files1[i], files2[j]); + if (cmp < 0) + goto exclusive_to_dir1; + if (cmp > 1) + goto exclusive_to_dir2; + + if (fstatat(dirfd1, files1[i], &st1, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) + eprintf("fstatat %s %s AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT:", dir1, files1[i]); + if (fstatat(dirfd2, files2[j], &st2, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) + eprintf("fstatat %s %s AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT:", dir2, files2[j]); + + if ((st1.st_mode & S_IFMT) != (st2.st_mode & S_IFMT)) { + weprintf("%s has different file types in %s and %s\n", files1[i], dir1, dir2); + ret = 1; + goto next; + } + + switch (st1.st_mode & S_IFMT) { + case S_IFREG: + if ((st1.st_mode ^ st2.st_mode) & S_IXUSR) { + weprintf("%s is executable in one of but in not both of %s and %s\n", files1[i], dir1, dir2); + ret = 1; + } + if (st1.st_size != st2.st_size) { + weprintf("%s has different sizes in %s and %s\n", files1[i], dir1, dir2); + ret = 1; + goto next; + } + fd1 = openat(dirfd1, files1[i], O_RDONLY | O_NOFOLLOW); + if (fd1 < 0) + eprintf("openat %s %s O_RDONLY|O_NOFOLLOW:", dir1, files1[i]); + fd2 = openat(dirfd2, files2[j], O_RDONLY | O_NOFOLLOW); + if (fd2 < 0) + eprintf("openat %s %s O_RDONLY|O_NOFOLLOW:", dir2, files2[j]); + if (!files_equal(fd1, dir1, fd2, dir2, files1[i])) { + weprintf("%s has different contents in %s and %s\n", files1[i], dir1, dir2); + ret = 1; + } + close(fd1); + close(fd2); + break; + + case S_IFLNK: + target1 = libsimple_ereadlinkat(dirfd1, files1[i]); + target2 = libsimple_ereadlinkat(dirfd2, files2[j]); + if (strcmp(target1, target2)) { + weprintf("%s has different targets in %s and %s\n", files1[i], dir1, dir2); + ret = 1; + } + free(target1); + free(target2); + break; + + case S_IFDIR: + break; + + default: + eprintf("%s has unsupported file type\n", files1[i]); + } + + next: + i++; + j++; + } + + close(dirfd1); + close(dirfd2); + while (nfiles1--) + free(files1[nfiles1]); + while (nfiles2--) + free(files2[nfiles2]); + free(files1); + free(files2); + return ret; +} diff --git a/util/tmpmount.c b/util/tmpmount.c new file mode 100644 index 0000000..75e225d --- /dev/null +++ b/util/tmpmount.c @@ -0,0 +1,44 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/mount.h> +#include <sched.h> +#include <libsimple.h> +#include <libsimple-arg.h> + +NUSAGE(125, "mountpoint utility [argument] ..."); + + +int +main(int argc, char *argv[]) +{ + const char *mountpoint; + + libsimple_default_failure_exit = 125; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc < 2) + usage(); + + mountpoint = *argv++; + argc--; + + if (unshare(CLONE_NEWNS)) + eprintf("unshare CLONE_NEWNS:"); + if (mount("none", "/", NULL, MS_REC | MS_SLAVE, NULL)) + eprintf("mount none / NULL MS_REC|MS_SLAVE NULL:"); + + if (mount("tmpfs", mountpoint, "tmpfs", 0, NULL)) + eprintf("mount tmpfs %s tmpfs 0 NULL:", mountpoint); + + if (setegid(getgid())) + eprintf("setegid <real group>:"); + if (seteuid(getuid())) + eprintf("seteuid <real user>:"); + + execvp(argv[0], argv); + enprintf(errno == ENOENT ? 127 : 126, "execvp %s:", argv[0]); + return 0; +} diff --git a/util/validate-tarball b/util/validate-tarball new file mode 100755 index 0000000..14aff6d --- /dev/null +++ b/util/validate-tarball @@ -0,0 +1,27 @@ +#!/bin/sh +# See LICENSE file for copyright and license details. + +set -e + +if ! test $# = 3; then + printf 'usage: %s tarball git-dir work-dir\n' "$0" >&2 + exit 1 +fi + +utildir="$(dirname -- "$0")" + +dir="$3/$$.tmpdir" +rm -rf -- "${dir}" +mkdir -- "${dir}" + +gunzip < "$1" | (cd -- "${dir}" && tar -x) + +(cd -- "$2" && git ls-files -z) > "$3/listing-1" +(cd -- "${dir}"/*/ && find -print0) > "$3/listing-2" + +set +e +"${utildir}"/repodiff "$2" "$3/listing-1" "${dir}"/* "$3/listing-2" +ret=$? + +rm -rf -- "${dir}" +exit $ret |
