Re: etcupdate created empty files = system unbootable

From: Miroslav Lachman <000.fbsd_at_quip.cz>
Date: Sat, 06 May 2023 07:33:58 UTC
On 04/05/2023 22:59, Miroslav Lachman wrote:

[..]
> I still don't know how to reproduce it / what is the root cause, but it 
> has happend again. This is the resulting list of empty files after a 
> "successful" run of etcupdate after upgrading from 12.3 to 13.2.
> 
> # find /etc/ -type f -empty ! -mtime +15m -ls
> 65994   0 -rw-r--r--   1 root  wheel   0 May  4 22:19 /etc/rc.firewall
> 66060   0 -rw-r--r--   1 root  wheel   0 May  4 22:19 /etc/network.subr
> 65814   0 -rw-r--r--   1 root  wheel   0 May  4 22:20 /etc/ssh/ssh_config

OK, now I have all the details of why it failed, and why etcupdate 
continued even when the file copy failed.

I found dozens of errors like this in /var/db/etcupdate/log after 
running "etcupdate".

 >>> cp -Rp /var/db/etcupdate/etcupdate-0PvaUI8/etc/rc.d/zfskeys 
/etc/rc.d/zfskeys
cp: /var/db/etcupdate/etcupdate-0PvaUI8/etc/rc.d/zfskeys: Function not 
implemented
 >>> cp -Rp /var/db/etcupdate/etcupdate-0PvaUI8/etc/rc.d/zpool 
/etc/rc.d/zpool
cp: /var/db/etcupdate/etcupdate-0PvaUI8/etc/rc.d/zpool: Function not 
implemented
 >>> cp -Rp /var/db/etcupdate/etcupdate-0PvaUI8/etc/rc.d/zpoolreguid 
/etc/rc.d/zpoolreguid
cp: /var/db/etcupdate/etcupdate-0PvaUI8/etc/rc.d/zpoolreguid: Function 
not implemented

As you can see, cp failed, but etcupdate continued without any sign of 
error.
cp failed because I did installkernel & installworld without rebooting. 
I have never run into a problem like this in the past, but I understand 
the reason - running the new 13.2 binary "cp" on an older 12.3 kernel is 
not supported.


However, there is a bug in etcupdate which causes it to continue 
silently instead of stopping when the first copy fails.

This function needs to check the return status of cp and not blindly 
return 0 even if the copy failed. In other words, the function 
install_new() does not do what the comment says. This needs to be fixed.

# Install the "new" version of a file.  Returns true if it succeeds
# and false otherwise.
#
# $1 - pathname of the file to install (relative to DESTDIR)
install_new()
{

	if ! install_dirs $NEWTREE "$DESTDIR" $1; then
		return 1
	fi
	log "cp -Rp ${NEWTREE}$1 ${DESTDIR}$1"
	if [ -z "$dryrun" ]; then
		cp -Rp ${NEWTREE}$1 ${DESTDIR}$1 >&3 2>&1
	fi
	post_install_file $1
	return 0
}

I will open PR with all the details today.

Kind regards
Miroslav Lachman