diff --git a/usr.sbin/etcupdate/etcupdate.8 b/usr.sbin/etcupdate/etcupdate.8 --- 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 @@ 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 --- a/usr.sbin/etcupdate/etcupdate.sh +++ b/usr.sbin/etcupdate/etcupdate.sh @@ -62,13 +62,13 @@ usage() { cat < 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 @@ # $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 @@ # 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 @@ 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 @@ M) MAKE_OPTIONS="$OPTARG" ;; + N) + noroot=YES + ;; *) echo usage