Fix scripted installs on EFI systems after default mounting of the ESP.

Because the ESP mount point (/boot/efi) is in mtree, tar will attempt to
extract a directory at that point post-mount when the system is installed.
Normally, this is fine, since tar can happily set whatever properties it
wants. For FAT32 file systems, however, like the ESP, tar will attempt to
set mtime on the root directory, which FAT does not support, and tar will
interpret this as a fatal error, breaking the install (see
https://github.com/libarchive/libarchive/issues/1516). This issue would
also break scripted installs on bare-metal POWER8, POWER9, and PS3
systems, as well as some ARM systems.

This patch solves the problem in two ways:
- If stdout is a TTY, use the distextract stage instead of tar, as in
  interactive installs. distextract solves this problem internally and
  provides a nicer UI to boot, but requires a TTY.
- If stdout is not a TTY, use tar but, as a stopgap for 13.0, exclude
  boot/efi from tarball extraction and then add it by hand. This is a
  hack, and better solutions (as in the libarchive ticket above) will
  obsolete it, but it solves the most common case, leaving only
  unattended TTY-less installs on a few tier-2 platforms broken.

In addition, fix a bug with fstab generation uncovered once the tar issue
is fixed that umount(8) can depend on the ordering of lines in fstab in a
way that mount(8) does not. The partition editor now writes out fstab in
mount order, making sure umount (run at the end of scripted, but not
interactive, installs) succeeds.

PR:		254395
Reviewed by:	gjb, imp
MFC after:	3 days
Differential Revision:	https://reviews.freebsd.org/D29380
This commit is contained in:
Nathan Whitehorn 2021-03-23 09:19:42 -04:00
parent 3c6b59567f
commit c2f16c595e
2 changed files with 61 additions and 8 deletions

View File

@ -323,6 +323,22 @@ validate_setup(void)
return (TRUE);
}
static int
mountpoint_sorter(const void *xa, const void *xb)
{
struct partition_metadata *a = *(struct partition_metadata **)xa;
struct partition_metadata *b = *(struct partition_metadata **)xb;
if (a->fstab == NULL && b->fstab == NULL)
return 0;
if (a->fstab == NULL)
return 1;
if (b->fstab == NULL)
return -1;
return strcmp(a->fstab->fs_file, b->fstab->fs_file);
}
static int
apply_changes(struct gmesh *mesh)
{
@ -386,6 +402,29 @@ apply_changes(struct gmesh *mesh)
free(__DECONST(char *, items[i*2]));
free(items);
/* Sort filesystems for fstab so that mountpoints are ordered */
{
struct partition_metadata **tobesorted;
struct partition_metadata *tmp;
int nparts = 0;
TAILQ_FOREACH(md, &part_metadata, metadata)
nparts++;
tobesorted = malloc(sizeof(struct partition_metadata *)*nparts);
nparts = 0;
TAILQ_FOREACH_SAFE(md, &part_metadata, metadata, tmp) {
tobesorted[nparts++] = md;
TAILQ_REMOVE(&part_metadata, md, metadata);
}
qsort(tobesorted, nparts, sizeof(tobesorted[0]),
mountpoint_sorter);
/* Now re-add everything */
while (nparts-- > 0)
TAILQ_INSERT_HEAD(&part_metadata,
tobesorted[nparts], metadata);
free(tobesorted);
}
if (getenv("PATH_FSTAB") != NULL)
fstab_path = getenv("PATH_FSTAB");
else

View File

@ -116,14 +116,28 @@ fi
# Unpack distributions
bsdinstall checksum
for set in $DISTRIBUTIONS; do
f_dprintf "Extracting $BSDINSTALL_DISTDIR/$set"
# XXX: this will fail if any mountpoints are FAT, due to inability to
# set ctime/mtime on the root of FAT partitions. tar has no option to
# ignore this. We probably need to switch back to distextract here
# to properly support EFI.
tar -xf "$BSDINSTALL_DISTDIR/$set" -C $BSDINSTALL_CHROOT
done
if [ -t 0 ]; then
# If install is a tty, use distextract as normal
bsdinstall distextract
else
# Otherwise, we need to use tar (see https://reviews.freebsd.org/D10736)
for set in $DISTRIBUTIONS; do
f_dprintf "Extracting $BSDINSTALL_DISTDIR/$set"
# XXX: The below fails if any mountpoints are FAT, due to
# inability to set ctime/mtime on the root of FAT partitions,
# which is needed to support e.g. EFI system partitions. tar has
# no option to ignore this (distextract ignores them internally
# through a hack), and returns 1 on any warning or error,
# effectively turning all warnings into fatal errors.
#
# Work around this in an extremely lame way for the specific
# case of EFI system partitions only. This *ONLY WORKS* if
# /boot/efi is empty and does not handle analagous problems on
# other systems (ARM, PPC64).
tar -xf "$BSDINSTALL_DISTDIR/$set" -C $BSDINSTALL_CHROOT --exclude boot/efi
mkdir -p $BSDINSTALL_CHROOT/boot/efi
done
fi
# Configure bootloader if needed
bsdinstall bootconfig