git: febca0e64361 - main - etcupdate: Add a -N flag to perform a NO_ROOT build

From: Jessica Clarke <jrtc27_at_FreeBSD.org>
Date: Tue, 09 Aug 2022 21:59:07 UTC
The branch main has been updated by jrtc27:

URL: https://cgit.FreeBSD.org/src/commit/?id=febca0e643612eb1ce14fbd892c2af7e17717bf5

commit febca0e643612eb1ce14fbd892c2af7e17717bf5
Author:     Jessica Clarke <jrtc27@FreeBSD.org>
AuthorDate: 2022-08-09 21:57:47 +0000
Commit:     Jessica Clarke <jrtc27@FreeBSD.org>
CommitDate: 2022-08-09 21:57:47 +0000

    etcupdate: Add a -N flag to perform a NO_ROOT build
    
    This is in preparation for including an etcupdate tree when performing a
    -DNO_ROOT release image build. Although -DNO_ROOT can be passed via -M,
    to be useful we need to mangle the resulting METALOG to mirror the
    various cleanups to the tree that are done after the build (removing
    generated files, empty files and empty directories), so etcupdate needs
    its own flag.
    
    Reviewed by:    jhb, pauamma
    Obtained from:  CheriBSD
    Differential Revision:  https://reviews.freebsd.org/D35857
---
 usr.sbin/etcupdate/etcupdate.8  | 15 ++++++++---
 usr.sbin/etcupdate/etcupdate.sh | 57 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 60 insertions(+), 12 deletions(-)

diff --git a/usr.sbin/etcupdate/etcupdate.8 b/usr.sbin/etcupdate/etcupdate.8
index be107f6a214d..773568eef9ec 100644
--- a/usr.sbin/etcupdate/etcupdate.8
+++ b/usr.sbin/etcupdate/etcupdate.8
@@ -33,7 +33,7 @@
 .Nd "manage updates to system files not updated by installworld"
 .Sh SYNOPSIS
 .Nm
-.Op Fl npBF
+.Op Fl npBFN
 .Op Fl d Ar workdir
 .Op Fl r | Fl s Ar source | Fl t Ar tarball
 .Op Fl A Ar patterns
@@ -44,7 +44,7 @@
 .Op Fl m Ar make
 .Nm
 .Cm build
-.Op Fl B
+.Op Fl BN
 .Op Fl d Ar workdir
 .Op Fl s Ar source
 .Op Fl L Ar logfile
@@ -59,7 +59,7 @@
 .Op Fl L Ar logfile
 .Nm
 .Cm extract
-.Op Fl B
+.Op Fl BN
 .Op Fl d Ar workdir
 .Op Fl s Ar source | Fl t Ar tarball
 .Op Fl D Ar destdir
@@ -518,6 +518,15 @@ option is not specified,
 then a temporary
 .Dq current
 tree will be extracted to perform the comparison.
+.It Fl N
+Perform a
+.Dv NO_ROOT
+build when building a
+.Dq current
+tree.
+The resulting tree will include a corresponding
+.Pa METALOG
+file at its root.
 .It Fl p
 Enable
 .Dq pre-world
diff --git a/usr.sbin/etcupdate/etcupdate.sh b/usr.sbin/etcupdate/etcupdate.sh
index d9993fb2e58a..0190e5868f05 100755
--- a/usr.sbin/etcupdate/etcupdate.sh
+++ b/usr.sbin/etcupdate/etcupdate.sh
@@ -62,13 +62,13 @@
 usage()
 {
 	cat <<EOF
-usage: etcupdate [-npBF] [-d workdir] [-r | -s source | -t tarball]
+usage: etcupdate [-npBFN] [-d workdir] [-r | -s source | -t tarball]
                  [-A patterns] [-D destdir] [-I patterns] [-L logfile]
                  [-M options] [-m make]
-       etcupdate build [-B] [-d workdir] [-s source] [-L logfile] [-M options]
+       etcupdate build [-BN] [-d workdir] [-s source] [-L logfile] [-M options]
                  [-m make] <tarball>
        etcupdate diff [-d workdir] [-D destdir] [-I patterns] [-L logfile]
-       etcupdate extract [-B] [-d workdir] [-s source | -t tarball]
+       etcupdate extract [-BN] [-d workdir] [-s source | -t tarball]
                  [-D destdir] [-L logfile] [-M options] [-m make]
        etcupdate resolve [-p] [-d workdir] [-D destdir] [-L logfile]
        etcupdate revert [-d workdir] [-D destdir] [-L logfile] file ...
@@ -184,14 +184,24 @@ always_install()
 # $1 - directory to store new tree in
 build_tree()
 (
-	local destdir dir file make
+	local destdir dir file make autogenfiles metatmp
 
 	make="$MAKE_CMD $MAKE_OPTIONS -DNO_FILEMON"
 
+	if [ -n "$noroot" ]; then
+		make="$make -DNO_ROOT"
+		metatmp=`mktemp $WORKDIR/etcupdate-XXXXXXX`
+		: > $metatmp
+		trap "rm -f $metatmp; trap '' EXIT; return 1" INT
+		trap "rm -f $metatmp" EXIT
+	else
+		metatmp="/dev/null"
+		trap "return 1" INT
+	fi
+
 	log "Building tree at $1 with $make"
 
 	exec >&3 2>&1
-	trap 'return 1' INT
 
 	mkdir -p $1/usr/obj
 	destdir=`realpath $1`
@@ -219,13 +229,38 @@ build_tree()
 
 	# Purge auto-generated files.  Only the source files need to
 	# be updated after which these files are regenerated.
-	rm -f $1/etc/*.db $1/etc/passwd $1/var/db/services.db || return 1
+	autogenfiles="./etc/*.db ./etc/passwd ./var/db/services.db"
+	(cd $1 && printf '%s\n' $autogenfiles >> $metatmp && \
+	    rm -f $autogenfiles) || return 1
 
 	# Remove empty files.  These just clutter the output of 'diff'.
-	find $1 -type f -size 0 -delete || return 1
+	(cd $1 && find . -type f -size 0 -delete -print >> $metatmp) || \
+	    return 1
 
 	# Trim empty directories.
-	find $1 -depth -type d -empty -delete || return 1
+	(cd $1 && find . -depth -type d -empty -delete -print >> $metatmp) || \
+	    return 1
+
+	if [ -n "$noroot" ]; then
+		# Rewrite the METALOG to exclude the files (and directories)
+		# removed above. $metatmp contains the list of files to delete,
+		# and we append #METALOG# as a delimiter followed by the
+		# original METALOG. This lets us scan through $metatmp in awk
+		# building up a table of names to delete until we reach the
+		# delimiter, then emit all the entries of the original METALOG
+		# after it that aren't in that table. We also exclude ./usr/obj
+		# and its children explicitly for simplicity rather than
+		# building up that list (and in practice only ./usr/obj itself
+		# will be in the METALOG since nothing is installed there).
+		echo '#METALOG#' >> $metatmp || return 1
+		cat $1/METALOG >> $metatmp || return 1
+		awk '/^#METALOG#$/ { metalog = 1; next }
+		    { f=$1; gsub(/\/\/+/, "/", f) }
+		    !metalog { rm[f] = 1; next }
+		    !rm[f] && f !~ /^\.\/usr\/obj(\/|$)/ { print }' \
+		    $metatmp > $1/METALOG || return 1
+	fi
+
 	return 0
 )
 
@@ -1738,7 +1773,8 @@ dryrun=
 ignore=
 nobuild=
 preworld=
-while getopts "d:m:nprs:t:A:BD:FI:L:M:" option; do
+noroot=
+while getopts "d:m:nprs:t:A:BD:FI:L:M:N" option; do
 	case "$option" in
 		d)
 			WORKDIR=$OPTARG
@@ -1798,6 +1834,9 @@ while getopts "d:m:nprs:t:A:BD:FI:L:M:" option; do
 		M)
 			MAKE_OPTIONS="$OPTARG"
 			;;
+		N)
+			noroot=YES
+			;;
 		*)
 			echo
 			usage