emacs/lib-src/make-fingerprint.c

180 lines
4.2 KiB
C

/* Hash inputs and generate C file with the digest.
Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2016, 2018-2024 Free
Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
/* The argument given to this program is the initial version of the
temacs executable file used when building GNU Emacs. This program computes
a digest fingerprint for the executable, and modifies the binary in
place, replacing all instances of the existing fingerprint (normally
the default fingerprint from libgnu's lib/fingerprint.c) with the
new value. With option -r, it just prints the digest. */
#include <config.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sysstdio.h>
#include <fingerprint.h>
#include <getopt.h>
#include <intprops.h>
#include <min-max.h>
#include <sha256.h>
#ifndef SSIZE_MAX
# define SSIZE_MAX TYPE_MAXIMUM (ssize_t)
#endif
#ifdef WINDOWSNT
/* Defined to be sys_fopen in ms-w32.h, but only #ifdef emacs, so this
is really just insurance. */
#undef fopen
#include <direct.h>
#ifndef MINGW_W64
# undef fseeko
# define fseeko fseeko64
#endif
#endif /* WINDOWSNT */
/* Static (instead of being local to 'main') to pacify LeakSanitizer. */
static char *buf;
int
main (int argc, char **argv)
{
int c;
bool raw = false;
while (0 <= (c = getopt (argc, argv, "rh")))
{
switch (c)
{
case 'r':
raw = true;
break;
case 'h':
printf ("make-fingerprint [-r] FILE: replace or compute a hash\n");
return EXIT_SUCCESS;
default:
return EXIT_FAILURE;
}
}
struct sha256_ctx ctx;
sha256_init_ctx (&ctx);
char *prog = argv[0];
char *file = argv[optind];
if (argc - optind != 1)
{
fprintf (stderr, "%s: missing or extra file operand\n", prog);
return EXIT_FAILURE;
}
FILE *f = fopen (file, raw ? "r" FOPEN_BINARY : "r+" FOPEN_BINARY);
struct stat st;
if (!f || fstat (fileno (f), &st) != 0)
{
perror (file);
return EXIT_FAILURE;
}
if (!S_ISREG (st.st_mode))
{
fprintf (stderr, "%s: Error: %s is not a regular file\n",
prog, file);
return EXIT_FAILURE;
}
ptrdiff_t maxlen = min (min (TYPE_MAXIMUM (off_t), PTRDIFF_MAX),
min (SIZE_MAX, SSIZE_MAX));
if (maxlen <= st.st_size)
{
fprintf (stderr, "%s: %s: file too big\n", prog, file);
return EXIT_FAILURE;
}
buf = malloc (st.st_size + 1);
if (!buf)
{
perror ("malloc");
return EXIT_FAILURE;
}
size_t chunksz = fread (buf, 1, st.st_size + 1, f);
if (ferror (f) || chunksz != st.st_size)
{
fprintf (stderr, "%s: Error: could not read %s\n", prog, file);
return EXIT_FAILURE;
}
sha256_process_bytes (buf, chunksz, &ctx);
unsigned char digest[32];
sha256_finish_ctx (&ctx, digest);
if (raw)
{
for (int i = 0; i < 32; ++i)
printf ("%02X", digest[i]);
}
else
{
bool fingered = false;
for (char *finger = buf;
(finger = memmem (finger, buf + chunksz - finger,
(unsigned char *) fingerprint,
sizeof fingerprint));
finger++)
{
if (! (fseeko (f, finger - buf, SEEK_SET) == 0
&& fwrite (digest, 1, sizeof digest, f) == sizeof digest))
{
perror (file);
return EXIT_FAILURE;
}
fingered = true;
}
if (!fingered)
{
fprintf (stderr, "%s: %s: missing fingerprint\n", prog, file);
return EXIT_FAILURE;
}
}
if (fclose (f) != 0)
{
perror (file);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/* make-fingerprint.c ends here */