grub/util/grub-mkstandalone.c
Michael Chang f18a899ab1 util/grub-mkstandalone: Ensure deterministic tar file creation by sorting contents
The add_tar_files() function currently iterates through a directory's
content using readdir(), which doesn't guarantee a specific order. This
lack of deterministic behavior impacts reproducibility in the build process.

This commit resolves the issue by introducing sorting functionality.
The list retrieved by readdir() is now sorted alphabetically before
incorporation into the tar archive, ensuring consistent and predictable
file ordering within the archive.

On the occasion fix tfp memory leak.

Signed-off-by: Michael Chang <mchang@suse.com>
Signed-off-by: Bernhard Wiedemann <bwiedemann@suse.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2023-12-12 15:47:08 +01:00

385 lines
8.9 KiB
C

/*
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013 Free Software Foundation, Inc.
*
* GRUB 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.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <grub/util/install.h>
#include <grub/util/misc.h>
#include <grub/emu/config.h>
#include <string.h>
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#include <argp.h>
#pragma GCC diagnostic error "-Wmissing-prototypes"
#pragma GCC diagnostic error "-Wmissing-declarations"
/* use 2015-01-01T00:00:00+0000 as a stock timestamp */
#define STABLE_EMBEDDING_TIMESTAMP 1420070400
static char *output_image;
static char **files;
static int nfiles;
const struct grub_install_image_target_desc *format;
static FILE *memdisk;
enum
{
OPTION_OUTPUT = 'o',
OPTION_FORMAT = 'O'
};
static struct argp_option options[] = {
GRUB_INSTALL_OPTIONS,
{"output", 'o', N_("FILE"),
0, N_("save output in FILE [required]"), 2},
{"format", 'O', N_("FILE"), 0, 0, 2},
{"compression", 'C', "xz|none|auto", OPTION_HIDDEN, 0, 2},
{0, 0, 0, 0, 0, 0}
};
static char *
help_filter (int key, const char *text, void *input __attribute__ ((unused)))
{
switch (key)
{
case 'O':
{
char *formats = grub_install_get_image_targets_string (), *ret;
ret = xasprintf ("%s\n%s %s", _("generate an image in FORMAT"),
_("available formats:"), formats);
free (formats);
return ret;
}
default:
return grub_install_help_filter (key, text, input);
}
}
static error_t
argp_parser (int key, char *arg, struct argp_state *state)
{
if (key == 'C')
key = GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS;
if (grub_install_parse (key, arg))
return 0;
switch (key)
{
case 'o':
if (output_image)
free (output_image);
output_image = xstrdup (arg);
break;
case 'O':
{
format = grub_install_get_image_target (arg);
if (!format)
{
printf (_("unknown target format %s\n"), arg);
argp_usage (state);
exit (1);
}
break;
}
case ARGP_KEY_ARG:
files[nfiles++] = xstrdup (arg);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
struct argp argp = {
options, argp_parser, N_("[OPTION] SOURCE..."),
N_("Generate a standalone image (containing all modules) in the selected format")"\v"N_("Graft point syntax (E.g. /boot/grub/grub.cfg=./grub.cfg) is accepted"),
NULL, help_filter, NULL
};
/* tar support */
#define MAGIC "ustar"
struct head
{
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
char pad[12];
} GRUB_PACKED;
static void
write_zeros (unsigned rsz)
{
char buf[512];
memset (buf, 0, 512);
fwrite (buf, 1, rsz, memdisk);
}
static void
write_pad (unsigned sz)
{
write_zeros ((~sz + 1) & 511);
}
static void
set_tar_value (char *field, grub_uint32_t val,
unsigned len)
{
unsigned i;
for (i = 0; i < len - 1; i++)
field[len - 2 - i] = '0' + ((val >> (3 * i)) & 7);
}
static void
compute_checksum (struct head *hd)
{
unsigned int chk = 0;
unsigned char *ptr;
memset (hd->chksum, ' ', 8);
for (ptr = (unsigned char *) hd; ptr < (unsigned char *) (hd + 1); ptr++)
chk += *ptr;
set_tar_value (hd->chksum, chk, 8);
}
static void
add_tar_file (const char *from,
const char *to)
{
char *tcn;
const char *iptr;
char *optr;
struct head hd;
grub_util_fd_t in;
ssize_t r;
grub_uint32_t size;
COMPILE_TIME_ASSERT (sizeof (hd) == 512);
if (grub_util_is_special_file (from))
return;
optr = tcn = xmalloc (strlen (to) + 1);
for (iptr = to; *iptr == '/'; iptr++);
for (; *iptr; iptr++)
if (!(iptr[0] == '/' && iptr[1] == '/'))
*optr++ = *iptr;
*optr = '\0';
if (grub_util_is_directory (from))
{
grub_util_fd_dir_t d;
grub_util_fd_dirent_t de;
char **from_files;
grub_size_t alloc = 8, used = 0;
grub_size_t i;
d = grub_util_fd_opendir (from);
from_files = xmalloc (alloc * sizeof (*from_files));
while ((de = grub_util_fd_readdir (d)))
{
if (strcmp (de->d_name, ".") == 0)
continue;
if (strcmp (de->d_name, "..") == 0)
continue;
if (alloc <= used)
{
alloc <<= 1;
from_files = xrealloc (from_files, alloc * sizeof (*from_files));
}
from_files[used++] = xstrdup (de->d_name);
}
qsort (from_files, used, sizeof (*from_files), grub_qsort_strcmp);
for (i = 0; i < used; i++)
{
char *fp, *tfp;
fp = grub_util_path_concat (2, from, from_files[i]);
tfp = xasprintf ("%s/%s", to, from_files[i]);
add_tar_file (fp, tfp);
free (tfp);
free (fp);
free (from_files[i]);
}
grub_util_fd_closedir (d);
free (from_files);
free (tcn);
return;
}
if (optr - tcn > 99)
{
memset (&hd, 0, sizeof (hd));
memcpy (hd.name, tcn, 99);
memcpy (hd.mode, "0000600", 7);
memcpy (hd.uid, "0001750", 7);
memcpy (hd.gid, "0001750", 7);
set_tar_value (hd.size, optr - tcn, 12);
set_tar_value (hd.mtime, STABLE_EMBEDDING_TIMESTAMP, 12);
hd.typeflag = 'L';
memcpy (hd.magic, MAGIC, sizeof (hd.magic));
memcpy (hd.uname, "grub", 4);
memcpy (hd.gname, "grub", 4);
compute_checksum (&hd);
fwrite (&hd, 1, sizeof (hd), memdisk);
fwrite (tcn, 1, optr - tcn, memdisk);
write_pad (optr - tcn);
}
in = grub_util_fd_open (from, GRUB_UTIL_FD_O_RDONLY);
if (!GRUB_UTIL_FD_IS_VALID (in))
grub_util_error (_("cannot open `%s': %s"), from, grub_util_fd_strerror ());
if (!grub_install_copy_buffer)
grub_install_copy_buffer = xmalloc (GRUB_INSTALL_COPY_BUFFER_SIZE);
size = grub_util_get_fd_size (in, from, NULL);
memset (&hd, 0, sizeof (hd));
memcpy (hd.name, tcn, optr - tcn < 99 ? optr - tcn : 99);
memcpy (hd.mode, "0000600", 7);
memcpy (hd.uid, "0001750", 7);
memcpy (hd.gid, "0001750", 7);
set_tar_value (hd.size, size, 12);
set_tar_value (hd.mtime, STABLE_EMBEDDING_TIMESTAMP, 12);
hd.typeflag = '0';
memcpy (hd.magic, MAGIC, sizeof (hd.magic));
memcpy (hd.uname, "grub", 4);
memcpy (hd.gname, "grub", 4);
compute_checksum (&hd);
fwrite (&hd, 1, sizeof (hd), memdisk);
while (1)
{
r = grub_util_fd_read (in, grub_install_copy_buffer, GRUB_INSTALL_COPY_BUFFER_SIZE);
if (r <= 0)
break;
fwrite (grub_install_copy_buffer, 1, r, memdisk);
}
grub_util_fd_close (in);
write_pad (size);
free (tcn);
}
int
main (int argc, char *argv[])
{
const char *pkglibdir;
int i;
grub_util_host_init (&argc, &argv);
grub_util_disable_fd_syncs ();
files = xcalloc (argc + 1, sizeof (files[0]));
argp_parse (&argp, argc, argv, 0, 0, 0);
pkglibdir = grub_util_get_pkglibdir ();
if (!output_image)
grub_util_error ("%s", _("output file must be specified"));
if (!format)
grub_util_error ("%s", _("Target format not specified (use the -O option)."));
if (!grub_install_source_directory)
grub_install_source_directory = grub_util_path_concat (2, pkglibdir, grub_util_get_target_dirname (format));
enum grub_install_plat plat = grub_install_get_target (grub_install_source_directory);
char *memdisk_dir = grub_util_make_temporary_dir ();
char *boot_grub = grub_util_path_concat (3, memdisk_dir, "boot", "grub");
grub_install_copy_files (grub_install_source_directory,
boot_grub, plat);
grub_set_install_backup_ponr ();
char *memdisk_img = grub_util_make_temporary_file ();
memdisk = grub_util_fopen (memdisk_img, "wb");
add_tar_file (memdisk_dir, "");
for (i = 0; i < nfiles; i++)
{
char *eq = grub_strchr (files[i], '=');
char *from, *to;
if (!eq)
{
from = files[i];
to = files[i];
}
else
{
*eq = '\0';
to = files[i];
from = eq + 1;
}
while (*to == '/')
to++;
add_tar_file (from, to);
}
write_zeros (512);
fclose (memdisk);
grub_util_unlink_recursive (memdisk_dir);
grub_install_push_module ("memdisk");
grub_install_push_module ("tar");
grub_install_make_image_wrap (grub_install_source_directory,
"(memdisk)/boot/grub", output_image,
memdisk_img, NULL,
grub_util_get_target_name (format), 0);
grub_util_unlink (memdisk_img);
return 0;
}