freebsd-update: Fix merging already-updated files

When performing an "upgrade" (moving between FreeBSD releases, as
opposed to "update" which merely applies security/errata updates
to the installed release) FreeBSD Update:

1. Generates a list of "files needing to be merged", namely those
files which don't match the version installed in the "old" release
and have paths matching the MergeChanges configuration directive
(by default, /boot/device.hints and everything under /etc/).

and later on,

2. Compares the currently-installed files to the versions in the
"new" release, removing index entries for files which "don't need
to be updated because they're not changing".

Unfortunately if a file falls into both of these categories -- that
is to say, if a file in /etc/ is the same as the version in the new
release and not the same as the version in the old release -- the
resulting "merge" step saw that the file was no longer listed as
being part of the new release, resulting in the file being deleted.

For the first 18 years of FreeBSD Update's existence, this never
happened, since $FreeBSD$ tags resulted in "new release" files
always being different from any files systems would already have
installed.

This commit fixes this behaviour by only placing a file into the
"files needing to be merged" list if it does not match the version
in the old release *or* the version in the new release.

Reported by:	des
Reviewed by:	delphij (earlier version), des, emaste
MFC after:	7 days
X-EN-Candidate:	yes
Differential Revision:	https://reviews.freebsd.org/D39973
This commit is contained in:
Colin Percival 2023-05-04 20:00:58 -07:00
parent f5f1b9a982
commit c55b7e5226

View File

@ -1677,11 +1677,12 @@ fetch_inspect_system () {
echo "done." echo "done."
} }
# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any # For any paths matching ${MERGECHANGES}, compare $2 against $1 and $3 and
# files which differ; generate $3 containing these paths and the old hashes. # find any files with values unique to $2; generate $4 containing these paths
# and their corresponding hashes from $1.
fetch_filter_mergechanges () { fetch_filter_mergechanges () {
# Pull out the paths and hashes of the files matching ${MERGECHANGES}. # Pull out the paths and hashes of the files matching ${MERGECHANGES}.
for F in $1 $2; do for F in $1 $2 $3; do
for X in ${MERGECHANGES}; do for X in ${MERGECHANGES}; do
grep -E "^${X}" ${F} grep -E "^${X}" ${F}
done | done |
@ -1689,9 +1690,10 @@ fetch_filter_mergechanges () {
sort > ${F}-values sort > ${F}-values
done done
# Any line in $2-values which doesn't appear in $1-values and is a # Any line in $2-values which doesn't appear in $1-values or $3-values
# file means that we should list the path in $3. # and is a file means that we should list the path in $3.
comm -13 $1-values $2-values | sort $1-values $3-values |
comm -13 - $2-values |
fgrep '|f|' | fgrep '|f|' |
cut -f 1 -d '|' > $2-paths cut -f 1 -d '|' > $2-paths
@ -1703,10 +1705,10 @@ fetch_filter_mergechanges () {
while read X; do while read X; do
look "${X}|" $1-values | look "${X}|" $1-values |
head -1 head -1
done < $2-paths > $3 done < $2-paths > $4
# Clean up # Clean up
rm $1-values $2-values $2-paths rm $1-values $2-values $3-values $2-paths
} }
# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123] # For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
@ -2711,7 +2713,7 @@ upgrade_run () {
# Based on ${MERGECHANGES}, generate a file tomerge-old with the # Based on ${MERGECHANGES}, generate a file tomerge-old with the
# paths and hashes of old versions of files to merge. # paths and hashes of old versions of files to merge.
fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
# correspond to lines in INDEX-PRESENT with hashes not appearing # correspond to lines in INDEX-PRESENT with hashes not appearing