logo

utils-std

Collection of commonly available Unix tools
commit: 49da680801952afba3de52055b0cbc49e0190763
parent 1141e7489795f77ee4cc22466a7c539ee9e405d2
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Thu, 15 Aug 2024 15:12:01 +0200

cmd/sha1sum: new

Diffstat:

MMakefile3+++
Acmd/sha1sum.139+++++++++++++++++++++++++++++++++++++++
Acmd/sha1sum.c239+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcoreutils.txt2+-
Mtest-cmd/Kyuafile1+
Atest-cmd/sha1sum.sh52++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 335 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile @@ -176,3 +176,6 @@ cmd/install: cmd/install.c lib/mode.c lib/user_group_parse.c lib/user_group_pars cmd/mv: cmd/mv.c lib/consent.c lib/consent.h lib/fs.c lib/fs.h Makefile config.mk $(RM) -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno} $(CC) -std=c99 $(CFLAGS) -o $@ cmd/mv.c lib/consent.c lib/fs.c $(LDFLAGS) $(LDSTATIC) + +cmd/sha1sum: cmd/sha1sum.c lib/sha1.c lib/sha1.h lib/bytes2hex.c lib/strconv.h Makefile + $(CC) -std=c99 $(CFLAGS) -o $@ cmd/sha1sum.c lib/sha1.c lib/bytes2hex.c $(LDFLAGS) $(LDSTATIC) diff --git a/cmd/sha1sum.1 b/cmd/sha1sum.1 @@ -0,0 +1,39 @@ +.\" utils-std: Collection of commonly available Unix tools +.\" Copyright 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2024-08-15 +.Dt SHA1SUM 1 +.Os +.Sh NAME +.Nm sha1sum +.Nd write and verify sha1 checksums +.Sh SYNOPSIS +.Nm +.Op Fl c +.Op Ar file... +.Sh DESCRIPTION +.Nm +reads each +.Ar file , +or standard input if none were specified, +and prints each of their SHA1 checksum. +.Sh OPTIONS +.Bl -tag -width _c +.It Fl c +Verify checksums contained in each +.Ar file . +Currently supported format being checksum, whitespace, an optional asterisk +.Ql * +and finally a filename. +.El +.Sh EXIT STATUS +.Ex -std +.Sh STANDARDS +SHA1 is standardized both in FIPS 180-4 and RFC 3174. +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact+utils@hacktivis.me +.Sh SECURITY CONSIDERATIONS +Due to it's known collissions SHA-1 is deprecated, the +.Nm +utility is only provided in the interest of compatibility and +verification of legacy checksums. diff --git a/cmd/sha1sum.c b/cmd/sha1sum.c @@ -0,0 +1,239 @@ +// utils-std: Collection of commonly available Unix tools +// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include "../lib/sha1.h" // sha1_* +#include "../lib/strconv.h" // bytes2hex + +#include <errno.h> +#include <fcntl.h> // open, O_* +#include <limits.h> // PATH_MAX +#include <stdbool.h> +#include <stdio.h> // fprintf +#include <stdlib.h> // free +#include <string.h> // strerror +#include <unistd.h> // read, write, close, getopt + +#define SHA1SUM_LEN SHA1_DIGEST_LENGTH * 2 + 1 + +static int +sha1sum(int fd, char *fdname, char sum[SHA1SUM_LEN]) +{ + struct sha1 ctx; + + sha1_init(&ctx); + + uint8_t buf[BUFSIZ]; + ssize_t nread = -1; + while((nread = read(fd, buf, BUFSIZ)) > 0) + { + sha1_update(&ctx, buf, nread); + } + if(nread < 0) + { + fprintf(stderr, + "sha1sum: I/O Error while reading file '%s': %s\n", + fdname ? fdname : "<stdin>", + strerror(errno)); + return -1; + } + + uint8_t res[SHA1_DIGEST_LENGTH] = ""; + sha1_sum(&ctx, res); + + bytes2hex(res, SHA1_DIGEST_LENGTH, sum, SHA1SUM_LEN); + + return 0; +} + +#define STR(s) #s +#define XSTR(s) STR(s) + +static int +check(FILE *file, char *filename) +{ + int err = 0; + + ssize_t nread = -1; + char *line = NULL; + size_t len = 0; + errno = 0; + while((nread = getline(&line, &len, file)) > 0) + { + char expect[SHA1SUM_LEN] = ""; + char target[PATH_MAX] = ""; + + static char *fmt = "%41[0-9A-Za-z]%*[* ]%" XSTR(PATH_MAX) "s%*[\n]"; + if(sscanf(line, fmt, &expect, &target) <= 0) + { + fprintf(stderr, "sha1sum: Error: Failed parsing line from file '%s': %s\n", filename, line); + + if(len > 0) free(line); + return -1; + } + + int fd = open(target, O_RDONLY | O_NOCTTY); + if(fd < 0) + { + fprintf(stderr, "sha1sum: Error: Failed opening file '%s': %s\n", target, strerror(errno)); + + if(len > 0) free(line); + return 1; + } + + int ret = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); + if(ret != 0) + fprintf(stderr, + "sha1sum: Warning: posix_fadvise failed on file '%s': %s\n", + target, + strerror(ret)); + + char got[SHA1SUM_LEN] = ""; + if(sha1sum(fd, target, got) < 0) err = 1; + if(memcmp(expect, got, SHA1SUM_LEN) == 0) + { + printf("%s: OK\n", target); + } + else + { + err = 1; + printf("%s: FAILED\n", target); + } + + if(close(fd) < 0) + { + fprintf(stderr, "sha1sum: Failed closing file '%s': %s\n", filename, strerror(errno)); + + if(len > 0) free(line); + return 1; + } + } + if(nread < 0 && errno != 0) + { + err = 1; + fprintf(stderr, "sha1sum: Failed reading line from file '%s': %s\n", filename, strerror(errno)); + } + if(len > 0) free(line); + + return err; +} + +int +main(int argc, char *argv[]) +{ + bool opt_c = false; + + int c = -1; + while((c = getopt(argc, argv, "c")) != -1) + { + switch(c) + { + case 'c': + opt_c = true; + break; + default: + fprintf(stderr, "sha1sum: Unhandled option '-%c'\n", c); + return 1; + } + } + + argc -= optind; + argv += optind; + + if(opt_c) + { + if(argc == 0) + { + if(check(stdin, "<stdin>") != 0) return 1; + + return 0; + } + + int err = 0; + + for(int i = 0; i < argc; i++) + { + FILE *file = NULL; + + if(argv[i][0] == '-' && argv[i][1] == '\0') + { + argv[i] = "<stdin>"; + file = stdin; + } + else + { + file = fopen(argv[i], "rb"); + if(file == NULL) + { + fprintf( + stderr, "sha1sum: Error: Failed opening file '%s': %s\n", argv[i], strerror(errno)); + return 1; + } + } + + if(check(file, argv[i]) != 0) err = 1; + + if(fclose(file) < 0) + { + fprintf(stderr, "sha1sum: Failed closing file '%s': %s\n", argv[i], strerror(errno)); + return 1; + } + } + + return err; + } + + if(argc == 0) + { + + char sum[SHA1SUM_LEN] = ""; + if(sha1sum(STDIN_FILENO, NULL, sum) < 0) return 1; + + puts(sum); + + return 0; + } + + for(int i = 0; i < argc; i++) + { + int fd = -1; + if(argv[i][0] == '-' && argv[i][1] == '\0') + { + argv[i] = "<stdin>"; + fd = STDIN_FILENO; + } + else + { + fd = open(argv[i], O_RDONLY | O_NOCTTY); + if(fd < 0) + { + fprintf(stderr, "sha1sum: Error: Failed opening file '%s': %s\n", argv[i], strerror(errno)); + return 1; + } + + int ret = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); + if(ret != 0) + fprintf(stderr, + "sha1sum: Warning: posix_fadvise failed on file '%s': %s\n", + argv[i], + strerror(ret)); + } + + int err = 0; + + char sum[SHA1SUM_LEN] = ""; + if(sha1sum(fd, argv[i], sum) < 0) err = 1; + printf("%s %s\n", sum, argv[i]); + + if(close(fd) < 0) + { + fprintf(stderr, "sha1sum: Failed closing file '%s': %s\n", argv[i], strerror(errno)); + return 1; + } + + if(err == 1) return 1; + } + + return 0; +} diff --git a/coreutils.txt b/coreutils.txt @@ -68,7 +68,7 @@ rm: Done rmdir: Done runcon: No. (SELinux-specific util, wtf) seq: Done -sha1sum: No +sha1sum: Done sha224sum: No sha256sum: No sha384sum: No diff --git a/test-cmd/Kyuafile b/test-cmd/Kyuafile @@ -30,6 +30,7 @@ tap_test_program{name="mktemp.sh", required_files=basedir.."/cmd/mktemp", timeou tap_test_program{name="pwd.sh", required_files=basedir.."/cmd/pwd", timeout=1} tap_test_program{name="realpath.sh", required_files=basedir.."/cmd/realpath", timeout=1} tap_test_program{name="seq.sh", required_files=basedir.."/cmd/seq", timeout=1} +tap_test_program{name="sha1sum.sh", required_files=basedir.."/cmd/sha1sum", timeout=1} tap_test_program{name="sleep.sh", required_files=basedir.."/cmd/sleep", timeout=1} tap_test_program{name="strings.sh", required_files=basedir.."/cmd/strings", timeout=1} tap_test_program{name="test.sh", required_files=basedir.."/cmd/test", timeout=2} diff --git a/test-cmd/sha1sum.sh b/test-cmd/sha1sum.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +WD="$(dirname "$0")" +target="${WD}/../cmd/sha1sum" +plans=16 +. "$(dirname "$0")/tap.sh" + +if test "$(uname -s)" = "FreeBSD" +then + skip devnull 'FreeBSD treats posix_fadvise on /dev/null as invalid' +else + t devnull '/dev/null' 'da39a3ee5e6b4b0d3255bfef95601890afd80709 /dev/null +' +fi +t empty "$WD/inputs/empty" "da39a3ee5e6b4b0d3255bfef95601890afd80709 $WD/inputs/empty +" +t --input='' 'empty_stdin' '' 'da39a3ee5e6b4b0d3255bfef95601890afd80709 +' + +t --input='abc' 'abc' '' 'a9993e364706816aba3e25717850c26c9cd0d89d +' + +t --exit=1 'enoent' '/var/empty/e/no/ent' "sha1sum: Error: Failed opening file '/var/empty/e/no/ent': No such file or directory +" + +t --input="da39a3ee5e6b4b0d3255bfef95601890afd80709 $WD/inputs/empty" 'check:empty' '-c' "$WD/inputs/empty: OK +" +t --input="da39a3ee5e6b4b0d3255bfef95601890afd80709 *$WD/inputs/empty" 'bin_check:empty' '-c' "$WD/inputs/empty: OK +" +t --input="da39a3ee5e6b4b0d3255bfef95601890afd80709 *$WD/inputs/empty" 'bin_check:empty:dash' '-c -' "$WD/inputs/empty: OK +" + +t --exit=1 --input="a9993e364706816aba3e25717850c26c9cd0d89d $WD/inputs/empty" 'xfail_check:empty' '-c' "$WD/inputs/empty: FAILED +" +t --exit=1 --input="a9993e364706816aba3e25717850c26c9cd0d89d *$WD/inputs/empty" 'xfail_bin_check:empty' '-c' "$WD/inputs/empty: FAILED +" + +t --exit=1 --input='a9993e364706816aba3e25717850c26c9cd0d89d /var/empty/e/no/ent' 'xfail_check:enoent' '-c' "sha1sum: Error: Failed opening file '/var/empty/e/no/ent': No such file or directory +" +t --exit=1 --input='a9993e364706816aba3e25717850c26c9cd0d89d */var/empty/e/no/ent' 'xfail_bin_check:enoent' '-c' "sha1sum: Error: Failed opening file '/var/empty/e/no/ent': No such file or directory +" + +t --exit=1 --input="# $WD/inputs/empty" 'invalid_chars:#' '-c' "sha1sum: Error: Failed parsing line from file '<stdin>': # $WD/inputs/empty +" +t --exit=1 --input="a9993e364706816aba3e25717850c26c9cd0d89d" 'missing_file' '-c' "sha1sum: Error: Failed opening file '': No such file or directory +" +t --exit=1 --input="a9993e364706816aba3e25717 $WD/inputs/empty" 'truncated_hash' '-c' "$WD/inputs/empty: FAILED +" +t --exit=1 --input="a9993e364706816aba3e25717850c26c9cd0d89da9993e364706816aba3e25717850c26c9cd0d89d $WD/inputs/empty" 'elongated_hash' '-c' "sha1sum: Error: Failed opening file '': No such file or directory +"