Read-only rootfs (Was Re: Multiple partition)

Archaic archaic at indy.rr.com
Sun Sep 14 09:33:53 PDT 2003


On Sun, Sep 14, 2003 at 11:22:31AM -0500, Archaic wrote:

Maybe I should attach the file. :)

-- 
Archaic

"One of the ordinary modes, by which tyrants accomplish their purposes
without resistance, is, by disarming the people, and making it an
offense to keep arms."

- Constitutional scholar and Supreme Court Justice Joseph Story, 1840

-------------- next part --------------
AUTHOR: Archaic <archaic at remove-this.indy.rr.com>
AUTHOR: Oliver Brakmann <obrakmann at gmx.net> 

DATE: 2003-09-13

LICENSE: GNU Free Documentation License http://www.gnu.org/licenses/fdl.txt

SYNOPSIS: How to create a read-only root file system

DESCRIPTION: A read-only root file system has many advantages over read-write
when the computer unexpectedly powers off. However, some parts of the file
system still need read-write access. This hint will show how to split the file
system into multiple partitions to achieve data stability.

PREREQUISITES: The kernel must be compiled with tmpfs (virtual memory file
system) support.

HINT: Warning! Serious ruin of your system may occur if you mistype something.
I offer no warranty or guarantee that this will work and your data will be
safe. It is recommended that you backup (and test the backup) all data before
beginning. If you use tar, don't forget the -p switch. I prefer to boot with a
boot CD and then manipulate the hard drive ensuring all needed data is in a
static state. Also, you cannot read just the tabbed commands and succeed. You
must read the paragraphs as well.

   This hint assumes reiser file systems. I chose reiser for its stability and
journaling capabilities. Ext2/3 file systems will work as well. For you uptime
junkies, you will not like the many reboots in this hint, but that's the only
true way to test the boot process. Now let's begin...

   First, we need to partition according to our needs. The bare minimum to run
a read-only rootfs is / and /var. You may want /tmp as well, but this hint
assumes /tmp is on tmpfs. Optionally, /usr can be separated, but unless you are
sharing /usr or want it to be read-write, then it can stay on /. Additionally,
most will want a partition for /home and possibly /root. Partition size is very
subjective so I will not cover it here. This hint assumes you have the ability
to add at least 1 extra partition for /var. Here is my partitioning scheme:

	hda1 /     2GB   reiserfs
	hda2 /var  900Mb reiserfs
	hda3 /home 3.1GB reiserfs

   Now it's time to start splitting the file systems. You have two choices
currently; static dev or devfs (when the 2.6 series kernel stabilizes, devfs
will be deprecated and a new virtual /dev will be available). While devfs has
many potentially good qualities, I do not like the workarounds needed, the
lessened stability, and the soon to be extinct nature of it. If you want devfs
then read the devfs and/or devfsd hints and get it working on your system
first. Then continue with this hint, skipping the "Dev" section.

### Dev ###

   Here, we make the transition from a hard drive-based /dev tree to a virtual
one. Start by making sure /dev/pts and /dev/shm are _not_ mounted. Also switch
to single user mode and make sure all daemons are shut off. If your boot
scripts and symlinks are in order you can do this by simply typing 'telinit 1'.
We also want to copy the device files and directories to a new location.

	#umount /dev/shm
	#umount /dev/pts
	#telinit 1
	#cd /
	#cp -a dev .dev

   Next, remove all device files and directories from /dev except console, then
mount a tmpfs on /dev and copy the original device files to the new /dev.

	#mount dev /dev -t tmpfs
	#cp -a .dev/* dev/

   Verify that everything is in order by catting /proc/mounts or using df to
see if a tmpfs is mounted on /dev, and ls /dev to make sure the files are
there.

   Now it's time to make /dev mount at boot time. Since some of the device
files are needed by init, we need to mount /dev before init runs. Create the
following script:

	#cat >/sbin/tmpdev <<EOF
	#!/bin/sh
	mount -n dev /dev -t tmpfs
	cp -a .dev/* /dev
	exec /sbin/init
	EOF
	#chmod 0700 /sbin/tmpdev

   To make the kernel run this script, you have to change your boot loader's
config file by adding init=/sbin/tmpdev.

   Next, we modify the mountfs boot script so that /dev gets written properly
to mtab. We do this by adding mount -f /dev to the script. I will show the
relevant part of the boot script with the change being underlined like this.
                                                                       ^^^^
	echo "Recording existing mounts in /etc/mtab..."
	> /etc/mtab
	mount -f / && mount -f /proc && mount -f /dev
                                     ^^^^^^^^^^^^^^^^

   Lastly, we modify fstab to include the mount. Though not strictly necessary
(as /dev is mounted before fstab is read), it's included for a sense of
completeness. Note the 'noauto' option. That is to keep the mountfs script from
trying to mount it again.

	#cat >>/etc/fstab <<EOF
	dev     /dev     tmpfs     defaults,noauto     0 0
	EOF

   Now it's time to test it out by rebooting your computer. Use /proc/mounts to
verify /dev is mounted. If you use /dev/pts, make sure it is mounted as well.
If all is well, it's time to separate var.

### Var ###

   Here we will separate /var from /. Again, we need to make sure that no
daemons are running. This process is pretty much the same as the Dev section.
We will start with mounting the soon-to-be var partition on a temporary mount
point and copying all the files in /var to this mount point, then unmounting
and remounting the new partition over /var. Don't forget to substitute
partition numbers for your setup.

	#telinit 1
	#cd /
	#mount /dev/hda2 /mnt
	#cp -a var/* /mnt
	#umount /mnt
	#mount /dev/hda2 /var

   Now check to make sure var is properly mounted and contains the directory
structure it's supposed to have. Cat /proc/mounts instead of using mtab for
maximum reliability. If it's all good, edit fstab to mount /var upon reboot.

	#cat >>/etc/fstab <<EOF
	/dev/hda2     /var     reiserfs     defaults     0 0
	EOF

   Now it's time to test it out by rebooting your computer. Use /proc/mounts to
verify /var is mounted. If all is well, we can delete the stuff in the original
/var. You might want to md5sum the stuff first, to be extra safe.

	#telinit 1
	#umount -n /var
	#rm -rf /var/*
	#mount -n /var

### Home ###

   Follow the same steps as in the "Var" section. Telinit'ing to runlevel 1 is
not necessary unless by some weird chance you have a daemon writing log files
there.

### Tmp ###

   Follow the same basic idea as in the "Dev" section copying the files in /tmp
to another location, then mounting tmpfs over /tmp and copying the files back.
Make sure to telinit to runlevel 1 in case something is using /tmp. Note: Some
people think /tmp should be wiped at reboot and /var/tmp should be used for
tmpfiles that need to last across reboots. Others vehemently object. If you are
the latter, get used to using /var/tmp. ;) Tmpfs is virtual.

        #telinit 1
        #cd /
        #cp -a tmp orig_tmp
        #mount tmp /tmp -t tmpfs
	#cp -a orig_tmp/* /tmp

   Verify that everything is in order by catting /proc/mounts or using df to
see if a tmpfs is mounted on /tmp, and ls /tmp to make sure any files that
should be there are there.  If it's all good, edit fstab to mount /tmp upon
reboot.

        #cat >>/etc/fstab <<EOF
        tmp     /tmp     tmpfs     defaults     0 0
        EOF

   Now it's time to test it out by rebooting your computer. Use /proc/mounts to
verify /tmp is mounted. If all is well, we can delete the orig_tmp directory
and the stuff still in /tmp on the root partition.

	#rm -rf /orig_tmp
	#telinit 1
	#umount /tmp
	#rm -rf /tmp/*
	#mount /tmp
	#telinit 3   # Or your runlevel of choice.

   Note, there may be some hidden dirs or files that still need to be deleted.
If someone has a command to catch them, please email me.

### Mtab ###

   Now it is time to make a decision. If you prefer having a writable mtab,
skip the "Symlink Mtab" section and read the "Writable Mtab" section. If you
think /proc/mounts is good enough, do the opposite. If you can't decide, search
the lfs-dev mailing list archives for arguments for and against each.

### Symlink Mtab ###

   Here we delete mtab and make the link to /proc/mounts.

	#rm /etc/mtab
	#ln -s /proc/mounts /etc/mtab

   Next we modify the mountfs boot script to mount / read-only. Comment out the
following lines:

	echo "Remounting root file system in read-write mode..."
	mount -n -o remount,rw /
	evaluate_retval

   Finally, we edit fstab to include the ro option for our root fs.

   Since the kernel command line option for reiserfs is generally set to mount
read-write, you also must change your boot loader config file to mount / ro
instead of rw.

   Now it's time to test it out by rebooting your computer. Cat /etc/mtab
(which is now /proc/mounts) to verify everything is mounted properly. As an
alternative, you can just type 'mount' to get a listing. You should see
something like this:

	rootfs / rootfs rw 0 0
	/dev/root / reiserfs ro 0 0 #<- The "ro" is what you are looking for
	dev /dev tmpfs rw 0 0
	proc /proc proc rw 0 0
	devpts /dev/pts devpts rw 0 0
	/dev/hda2 /var reiserfs rw 0 0

   You can disregard rootfs. Some kernels show this, but it's supposed to be a
hidden, virtual fs that only the kernel uses.

   If everything looks good then congratulations! You are finished. Read the
section "Other Concerns" to prepare for any (minor) gotcha's your new read-only
system may throw at you.

### Writable Mtab ###

   Note: With the exception of a few edits, the following text covering a
writable mtab was taken directly from the now-obsolete mtab hint written by
Oliver Brakmann.

   OK, so we want to have a read-only root partition, but /etc/mtab needs to be
writable. The simple solution is to put /etc/mtab somewhere else! I will use
/var/lib/misc/mtab in this hint, as that is the most logical place as I read
the FHS. The downside to this approach is that you have to recompile glibc,
util-linux and possibly other software packages. To be compatible with scripts
and other (broken) pieces of software, we re-create /etc/mtab as a link to our
new mtab file.

   Enough introduction, now we'll get our hands dirty. Start by switching to
runlevel 1 (single-user mode).

	#telinit 1

   We need to unpack glibc and linuxthreads, as well as apply any patches
necessary per the lfs book instructions. The following instructions assume you
are in glibc's top directory. The sed makes newly compiled programs look for
the mtab file in /var/lib/misc/mtab.

	#cp sysdeps/unix/sysv/linux/paths.h{,.orig}
	#sed 's%/etc/mtab%/var/lib/misc/mtab%' \
	  sysdeps/unix/sysv/linux/paths.h.orig >sysdeps/unix/sysv/linux/paths.h

   Next we will rebuild util-linux so that mount, umount and friends are aware
of mtab's new location.  There are no patches required, just look up the
instructions in the LFS Book.

   Note: Other packages you might want to rebuild are the GNU fileutils (df)
and other programs that might access /etc/mtab.  As long as these programs do
not write to that file, it is not absolutely required, though. That is why we
will set up that symlink.

   The next step is to move /etc/mtab to /var/lib/misc/mtab then re-create
/etc/mtab as a link to /var/lib/misc/mtab (for compatibility reasons).

	#mv /etc/mtab /var/lib/misc/
	#chmod 644 /var/lib/misc/mtab   # Just to make sure
	#ln -s ../var/lib/misc/mtab /etc/mtab

   Now we need to create /var/lib/misc/mtab _on your root partition_. We need
this so that mount and umount always know what is mounted regardless of whether
or not /var (where the real mtab is) is mounted, otherwise file systems may not
get unmounted properly and disk corruption may occur. We will make this a
symlink to /proc/mounts so we can have dynamic information on a static
(read-only) root file system. We must do this by first unmounting /var.

	#umount -n /var
	#mkdir -p /var/lib/misc
	#ln -s /proc/mounts /var/lib/misc/mtab
	#mount -n /var

   Now we have to modify the mountfs boot script. We need to comment out the
lines that remount / in read-write mode. Also, we need to explicitly mount /var
so we can clear the writable mtab and start recording the mounts. Here is what
it should look like:

#               echo "Remounting root file system in read-write mode..."
#               mount -n -o remount,rw /
#               evaluate_retval

                echo "Recording existing mounts in /var/lib/misc/mtab..."
                mount -n /var &&
                > /var/lib/misc/mtab &&
                mount -f / && mount -f /proc && mount -f /dev && mount -f /var
                evaluate_retval

   While we are editing mountfs we need to modify the umount section by adding
the -n switch. What happens is umount sees a writable mtab (not a symlink),
unmounts /var, and then tries to write a lock file to /var/lib/misc. This won't
work due to being a read-only file system.  (If it wasn't, I wonder if it
would/could write to /proc/mounts by way of the symlink.) Anyway, here is the
relevant part of the script:

        echo "Unmounting all other currently mounted file systems..."
        umount -n -a -r
               ^^ <- Note the addition of -n

   Next, modify your /etc/fstab so that /, /proc, and /var do not get mounted
automatically (as they will already be mounted). The key word in the following
example is 'noauto'. Note that with the lfs-boot scripts, my testing shows that
this is not really necessary, but it makes things nice and tidy. We will also
add the 'ro' option for your root file system. It's not absolutely necessary as
the checkfs script explicitly mounts root as ro and we commented out the
remounting rw in the mountfs script. But if you leave it out, mount will
mistakenly say it is rw (/proc/mounts will say ro, though).

	/dev/hda1     /        reiserfs     defaults,noauto,ro     0 0
	/dev/hda2     /var     reiserfs     defaults,noauto        0 0
	proc          /proc    proc         defaults,noauto        0 0

   Finally, if you are on an ext2/3 root file system (or any other that
initially boots up in read-only), you are ready to go. If you use reiser,
however, by default it initially boots in read-write. You will have to change
the config file of your boot loader to mount ro.

   You can now remount root read-only since we are not changing any more files.
If you have not unmounted any partitions other than /var during the process,
all those listed in /etc/mtab should still be mounted.  Check out /proc/mounts
and be sure that it reports the same mounted partitions as /var/lib/misc/mtab.
If it checks out ok, you can switch to runlevel 3 prior to rebooting. The
reason for this is to fire up the daemons so they will be using /var. This will
put the system into real-life use and may cause a problem to surface that might
not be detected until you reboot the next time.

	#mount -n -o remount,ro /
	#telinit 3

   Now it's time to test it out by rebooting your computer. Cat /proc/mounts to
verify everything is mounted properly. You should see something like this:

	rootfs / rootfs rw 0 0
	/dev/root / reiserfs ro 0 0 #<- The "ro" is what you are looking for
	dev /dev tmpfs rw 0 0
	proc /proc proc rw 0 0
	devpts /dev/pts devpts rw 0 0
	/dev/hda2 /var reiserfs rw 0 0

   You can disregard rootfs. Some kernels show this, but it's supposed to be a
hidden, virtual fs that only the kernel uses. Also, make sure you compare
/proc/mounts with /etc/mtab (by catting mtab or typing 'mount') to make sure
they agree. /proc/mounts will lack some detail, but what it does have needs to
be the same on mtab. Make sure all mounting options are correct.

   If everything looks good then congratulations! You are finished. Read the
section "Other Concerns" to prepare for any (minor) gotcha's your new read-only
system may throw at you.

### Other Concerns ###

   Here I list a few of caveats you will find with your new system. The main
problems I see are installing/uninstalling software, changing passwords, and
creating/removing devices (if you use the static /dev method), but there are
surely others.

Installing New Software
   To install something, build as usual (in /home or /var or somewhere else of
your choosing as long as it is writable). Keep in mind the space requirements
of the package while compiling. Glibc-2.5.x needed around 400MB during
compilation but needed much less installed. I keep /home extra large for just
that reason. Then when you switch to root to install, just remount root
read-write and install (making sure to run ldconfig if necessary). Then remount
back to read-only. That's a small price to pay for data security. Also, for the
people who think it's redundant to use reiserfs when you're just going to mount
read-only, I ask, "What if you lose power while installing software?" I want my
data safe. Period.

Changing Passwords
   Initially, the easy way to change passwords is also to remount root
read-write, but this can be tedious if you have more than a couple of users, as
you will have to be present whenever they change them. I haven't yet explored
other options, but the simplest would seem to be symlinking passwd, shadow,
group, etc. to /var. This may not be so cut-and-dry however. Shadow may need
patching. If anyone tries the latter two, I would be interested in knowing how
it comes out. Also anyone using pam with shadow (if that makes any difference).

Creating/Removing Devices
   This is just a reminder that you must create/remove devices in the /.dev
directory for the change to last across a reboot. If you don't want to reboot
for the changes to apply (who does?) then simply repeat the creation/removal of
the device in the virtual /dev as well.

### Added Value ###

   Now that you are running a multi-partition system, you can tweak the
mounting options used for each file system. Here is a sample fstab to give you
an idea of some of the things that are possible:

	/dev/hda1 /           reiserfs  ro,noauto,nodev                     0 0
	/dev/hda2 /var        reiserfs  noauto,nodev,noexec,nosuid          0 0
	/dev/hda3 /home       reiserfs  nodev,nosuid                        0 0
	/dev/hda4 /tarballs   reiserfs  nodev,noexec,nosuid,users           0 0
	dev       /dev        tmpfs     noauto,noexec,nosuid,size=50k       0 0
	proc      /proc       proc      noauto                              0 0
	tmp       /tmp        tmpfs     nodev,nosuid,size=80m,nr_inodes=20k 0 0
	devpts    /dev/pts    devpts    gid=4,mode=620                      0 0
	devshm    /dev/shm    tmpfs     defaults                            0 0

   Read 'man 8 mount' to understand the options. The options for tmpfs can be
found in the kernel source under Documentation/filesystems/tmpfs.txt.

ACKNOWLEDGEMENTS: I would like to thank Richard Lightman for the initial idea
of mounting /dev before exec'ing init (I really hate devfs ;)) and Oliver
Brakmann for the mtab hint (I also hate symlinking mtab to /proc/mounts ;)). I
would also like to thank Oliver for allowing me to absorb his hint into mine.
It would not have been possible to have a complete hint without his hint.

Oliver Brakmann would like to give credit to Seth W. Klein for teaching him
through the process in the first place and further corrections and suggestions.
Also thanks to DJ Lucas, Jesse Tie-Ten-Quee, Richard Lightman and Wouter
Vanwalleghem for additional feedback.

CHANGELOG:
[2003-09-13]
 * Initial version


More information about the blfs-support mailing list