How I updated 4.11 to 6.2 in a single reboot

Yar Tikhiy yar at comp.chem.msu.su
Tue Feb 20 14:27:20 UTC 2007


Hi all,

I hope the following story will divert a few potential subscribers
from freebsd-eol and save some effort aimed to keep the 4.11 zombie
dancing.

DISCLAIMER: I take no responsibility for inconvenience or damage
that can happen if one uses my story as a how-to guide, especially
without full knowledge of what and why he is actually doing.  YMMV.

I had an old machine with its CD-ROM and floppy drives long-dead
and PXE missing from its boot options.  It was running 4.11-STABLE.
Facing the virtual EOL of the entire 4-STABLE branch, I wanted to
update the machine's OS to 6.2-RELEASE and forget about it for a
while, bar running freebsd-update occasionally.  You'll say I should
have connected a working CD-ROM drive to the machine and used the
opportunity to dust its inside.  Alas, I felt too lazy even to walk
to the console.  So I chose the path of least effort: to replace
the binaries in the live system through SSH.  This might sound like
installing a heart implant without narcosis, but the surgeon was
sufficiently heartless himself.

First, I downloaded the following 6.2 distribution parts from an
FTP server to the machine: base, dict, games, info, kernels, manpages.
Had I wanted a minimal set, it would have consisted of base, kernels,
manpages.  (I had been taught well in the past not to save on
manpages irrespective of the system's role.)

Second, I added the system users and groups that had been born
between 4.11 and 6.2.  The new users were proxy, _pflogd, _dhcp;
the new groups were proxy, authpf, _pflogd, _dhcp, audit.  I just
grabbed the appropriate revisions of src/etc/master.passwd and
src/etc/group from CVSWeb, extracted the new records from them, and
added those records to /etc/master.passwd (using vipw) and /etc/group.
The new master.passwd and group files could also have been extracted
from the base 6.2 distro.  Note that the next step, full extraction
of distros, could have resulted in wrong file ownership without
this step.

After that, I set DESTDIR to /usr/tmp:

# mkdir /usr/tmp
# export DESTDIR=/usr/tmp

and ran install.sh in each distro subdirectory:

# for d in base dict games info manpages
> do
> ( cd $d && sh install.sh )
> done
# cd kernels && sh install.sh GENERIC

So I got the new system's tree under /usr/tmp.

At that stage, the old 4.11 tar(1) slightly mishandled the new
archive format of the 6.2 distros by creating bogus PaxHeader
directories instead of setting the schg flag on some files from the
base distro.  Fortunately, each PaxHeader directory hinted at which
files should have had the schg flag set.  Due to that I was able
to save the list of files needing the flag.

# find /usr/tmp -path "*/PaxHeader/*" | sed -e s,/PaxHeader,, -e s,/usr/tmp,, > /root/schg.lst

I didn't set the flag on the files immediately because the files
to be transferred to / would lose it anyway.  (See below.)  Then
it was safe to remove the bogus directories:

# find /usr/tmp -name PaxHeader | xargs rm -rf

Having done with that, I created a scratch directory in / and
transferred the respective files to it to make sure they fit in /:

# mkdir /newroot
# cd /usr/tmp
# tar cf -  COPYRIGHT bin boot dev etc lib libexec rescue sbin | ( cd /newroot && tar xpf - )

Note my having used tar(1), not cp(1) there.  Alas, cp(1) would
mishandle hard links by copying each link as a separate file.  OTOH,
tar(1) would lose file flags -- that's why I had saved the schg
candidate list.

Then I fixed the default kernel:

# cd /newroot/boot
# cp -pR GENERIC/* kernel/

... updated the boot blocks:

# disklabel -B -b /newroot/boot/boot1 -s /newroot/boot/boot2 ad0s1

... created the new system directories in /var (the others could wait):

# cp -pR /usr/tmp/var/named /var/
# mtree -deU -f /newroot/etc/mtree/BSD.var.dist -p /var

The mtree command was the second to require that the new system
users and groups be created in advance.

Then I transferred the most essential settings from /etc to
/newroot/etc for the machine to be up and accessible over the network
after a reboot.  The following files could simply be copied: fstab,
resolv.conf, ssh/*key*.

A few files in /newroot/etc called for manual work:
- rc.conf (see below);
- master.passwd (added myself and the rest of local users);
- group (the most important thing was to assign myself to the wheel group);
- mail/aliases (set the root alias.)

I also made sure I didn't have a shell from /usr/local.  In addition,
I disabled all extra services in the new rc.conf.  The hostname,
network interface configuration, defaultrouter, and sshd_enable="YES"
were basically needed in my case; stock services using the default
configuration could also be enabled from the outset.

After the preliminary merging of /etc, I built spwd.db and pwd.db
in /newroot/etc:

# pwd_mkdb -p -d /newroot/etc /newroot/etc/master.passwd

To get the correct time zone after the reboot, I also copied
/newroot/etc/localtime from /usr/tmp/usr/share/zoneinfo/Europe/Moscow.
I should have created /newroot/etc/wall_cmos_clock if the machine
hadn't had its hardware clock set in UTC.

I also should have paid attention to /newroot/boot/loader.conf, had
the system needed any modules pre-loaded or tunables pre-set.

Then came the tricky part: replacing the binaries.

I started it by killing off most daemons, especially those running
external programs:

# killall cron inetd sendmail syslogd

At the same time, I killed neither sshd nor dhclient, or else I
would lose my connection to the host.  To prevent other users from
logging into the system under update, I opened a couple of spare
SSH sessions for myself just in case, then touched /var/run/nologin.
There were no other users logged in at that moment.

Actual replacing began from /usr:

# cd /usr/tmp/usr
# for d in bin games include lib libdata libexec sbin share src
> do
> mv /usr/$d /usr/$d.old && mv $d /usr/
> done

And now -- /!

# cd /newroot
# mv COPYRIGHT lib libexec rescue /
# for d in boot dev etc sbin
> do
> mv /$d /$d.old && mv $d /
> done
# mv /bin /bin.old
# /bin.old/mv bin /

At last, I crossed my fingers and typed:

# /sbin.old/reboot

It worked!  In a couple of minutes I could log into the 6.2 system.
It was time to finish the update.

First, I applied the rest of /etc/mtree files except BSD.x11*:

# cd /etc/mtree
# mtree -deU -f BSD.root.dist -p /
# mtree -deU -f BSD.usr.dist -p /usr
# mtree -deU -f BSD.sendmail.dist -p /
# mtree -deU -f BSD.include.dist -p /usr/include
# mtree -deU -f BSD.local.dist -p /usr/local

Then the schg candidate list in /root/schg.lst came handy:

# xargs chflags schg < /root/schg.lst

After that, I copied /etc/exports from /etc.old and manually merged
local bits in rc.conf, inetd.conf, syslog.conf, newsyslog.conf, and
shells.  It would have had more sense to unpack the system sources
and run mergemaster(8) against the /etc.old contents if I had had
more local changes there.  The commands I would have issued are as
follows:

# mv /etc /etc.new
# cp -pR /etc.old /etc
# mtree -deU -f /etc.new/BSD.root.dist -p /
# mergemaster -i
# cp /etc.new/localtime /etc

Mergemaster would also have taken care of the dot-files in / and
/root.  I just overwrote them with the new versions as I hadn't had
customized them.

Further polishing included pkg_add'ing compat[45]x, enabling in
rc.conf and starting important services from the base system and
/usr/local (it's easy due to rc.d!)  Now I'm going to update old
ports gradually.

Some day I'll remove the *.old stuff and say the last farewell to
the good old 4.11.  It did an excellent job for me, and I'll miss
it.

That's all, folks!


P.S. To refresh my memory, I reproduced my scenario later in a
virtual machine using 4.11-RELEASE.  I was also interested in
estimates for disc space usage during the process, as I was going
to update even older and weaker machines in this manner.  Here are
some df(1) readings for the curious:

Original 4.11 w/o catman, docs or src:

Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a    201518  36114  149284    19%    /
/dev/ad0s1f   1610806 109076 1372866     7%    /usr
/dev/ad0s1e     50350    318   46004     1%    /var
procfs              4      4       0   100%    /proc

After unpacking 6.2 to /usr/tmp from an external partition:

Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a    201518  36116  149282    19%    /
/dev/ad0s1f   1610806 270066 1211876    18%    /usr
/dev/ad0s1e     50350    318   46004     1%    /var
procfs              4      4       0   100%    /proc

After copying the respective files to /newroot:

Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a    201518  72884  112514    39%    /
/dev/ad0s1f   1610806 270066 1211876    18%    /usr
/dev/ad0s1e     50350    318   46004     1%    /var
procfs              4      4       0   100%    /proc

After copying GENERIC/* to kernel/:

Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a    201518  95896   89502    52%    /
/dev/ad0s1f   1610806 270066 1211876    18%    /usr
/dev/ad0s1e     50350    318   46004     1%    /var
procfs              4      4       0   100%    /proc

So, if you have a tiny / partition, you can mv instead of cp the
kernel files from GENERIC/ to kernel/ and save some / space.  See
below.

After the reboot:

Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a    201518  95926   89472    52%    /
devfs               1      1       0   100%    /dev
/dev/ad0s1f   1610806 270068 1211874    18%    /usr
/dev/ad0s1e     50350    316   46006     1%    /var
procfs              4      4       0   100%    /proc

After removing all old and tmp cruft:

Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a    201518  66916  118482    36%    /
devfs               1      1       0   100%    /dev
/dev/ad0s1f   1610806 124096 1357846     8%    /usr
/dev/ad0s1e     50350    318   46004     1%    /var
procfs              4      4       0   100%    /proc

After removing /boot/GENERIC, which was a duplicate of /boot/kernel:

Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a    201518  43924  141474    24%    /
devfs               1      1       0   100%    /dev
/dev/ad0s1f   1610806 124096 1357846     8%    /usr
/dev/ad0s1e     50350    320   46002     1%    /var
procfs              4      4       0   100%    /proc

It is clear from the first and last readings that disc space usage
by 6.2 hasn't grown much as against 4.11, although there are a lot
of important new features in 6.2.  FreeBSD isn't like Some Other
OS Types(tm), which double their disc space requirements on each
major release.  Great job, folks!

-- 
Yar


More information about the freebsd-stable mailing list