4. Building a root filesystem
Creating the root filesystem involves selecting files necessary for the system to run. In this section we describe how to build a compressed root filesystem. A less common option is to build an uncompressed filesystem on a diskette that is directly mounted as root; this alternative is described in Section 9.1.
4.1. Overview
A root filesystem must contain everything needed to support a full Linux system. To be able to do this, the disk must include the minimum requirements for a Linux system:
The basic file system structure,
Minimum set of directories: /dev, /proc, /bin, /etc, /lib, /usr, /tmp,
Basic set of utilities: sh, ls, cp, mv, etc.,
Minimum set of config files: rc, inittab, fstab, etc.,
Devices: /dev/hd*, /dev/tty*, /dev/fd0, etc.,
Runtime library to provide basic functions used by utilities.
Of course, any system only becomes useful when you can run something on it, and a root diskette usually only becomes useful when you can do something like:
Check a file system on another drive, for example to check your root file system on your hard drive, you need to be able to boot Linux from another drive, as you can with a root diskette system. Then you can run fsck on your original root drive while it is not mounted.
Restore all or part of your original root drive from backup using archive and compression utilities such as cpio, tar, gzip and ftape.
We describe how to build a compressed filesystem, so called because it is compressed on disk and, when booted, is uncompressed onto a ramdisk. With a compressed filesystem you can fit many files (approximately six megabytes) onto a standard 1440K diskette. Because the filesystem is much larger than a diskette, it cannot be built on the diskette. We have to build it elsewhere, compress it, then copy it to the diskette.
4.2. Creating the filesystem
In order to build such a root filesystem, you need a spare device that is large enough to hold all the files before compression. You will need a device capable of holding about four megabytes. There are several choices:
Use a ramdisk (DEVICE = /dev/ram0). In this case, memory is used to simulate a disk drive. The ramdisk must be large enough to hold a filesystem of the appropriate size. If you use LILO, check your configuration file (/etc/lilo.conf) for a line like RAMDISK = nnn which determines the maximum RAM that can be allocated to a ramdisk. The default is 4096K, which should be sufficient. You should probably not try to use such a ramdisk on a machine with less than 8MB of RAM. Check to make sure you have a device like /dev/ram0, /dev/ram or /dev/ramdisk. If not, create /dev/ram0 with mknod (major number 1, minor 0).
If you have an unused hard disk partition that is large enough (several megabytes), this is acceptable.
Use a loopback device, which allows a disk file to be treated as a device. Using a loopback device you can create a three megabyte file on your hard disk and build the filesystem on it.
Type man losetup for instructions on using loopback devices. If you don't have losetup, you can get it along with compatible versions of mount and unmount from the util-linux package in the directory ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/.
If you do not have a loop device (/dev/loop0, /dev/loop1, etc.) on your system, you will have to create one with ``mknod /dev/loop0 b 7 0''. Once you've installed these special mount and umount binaries, create a temporary file on a hard disk with enough capacity (eg, /tmp/fsfile). You can use a command like:
to create an nnn-block file.dd if=/dev/zero of=/tmp/fsfile bs=1k count=nnn
Use the file name in place of DEVICE below. When you issue a mount command you must include the option -o loop to tell mount to use a loopback device.
After you've chosen one of these options, prepare the DEVICE with:
dd if=/dev/zero of=DEVICE bs=1k count=4096 |
This command zeroes out the device.
Zeroing the device is critical because the filesystem will be compressed later, so all unused portions should be filled with zeroes to achieve maximum compression. Keep this in mind whenever you move or delete files on the filesystem. The filesystem will correctly de-allocate the blocks, but it will not zero them out again. If you do a lot of deletions and copying, your compressed filesystem may end up much larger than necessary. |
Next, create the filesystem. The Linux kernel recognizes two file system types for root disks to be automatically copied to ramdisk. These are minix and ext2, of which ext2 is preferred. If using ext2, you may find it useful to use the -N option to specify more inodes than the default; -N 2000 is suggested so that you don't run out of inodes. Alternatively, you can save on inodes by removing lots of unnecessary /dev files. mke2fs will by default create 360 inodes on a 1.44Mb diskette. I find that 120 inodes is ample on my current rescue root diskette, but if you include all the devices in /dev you will easily exceed 360. Using a compressed root filesystem allows a larger filesystem, and hence more inodes by default, but you may still need to either reduce the number of files or increase the number of inodes.
So the command you use will look like:
mke2fs -m 0 -N 2000 DEVICE |
(If you're using a loopback device, the disk file you're using should be supplied in place of this DEVICE.)
The mke2fs command will automatically detect the space available and configure itself accordingly. The ``-m 0'' parameter prevents it from reserving space for root, and hence provides more usable space on the disk.
Next, mount the device:
mount -t ext2 DEVICE /mnt |
4.3. Populating the filesystem
Here is a reasonable minimum set of directories for your root filesystem [1]:
/dev -- Device files, required to perform I/O
/proc -- Directory stub required by the proc filesystem
/etc -- System configuration files
/sbin -- Critical system binaries
/bin -- Essential binaries considered part of the system
/lib -- Shared libraries to provide run-time support
/mnt -- A mount point for maintenance on other disks
/usr -- Additional utilities and applications
Three of these directories will be empty on the root filesystem, so they only need to be created with mkdir. The /proc directory is basically a stub under which the proc filesystem is placed. The directories /mnt and /usr are only mount points for use after the boot/root system is running. Hence again, these directories only need to be created.
The remaining four directories are described in the following sections.
4.3.1. /dev
A /dev directory containing a special file for all devices to be used by the system is mandatory for any Linux system. The directory itself is a normal directory, and can be created with mkdir in the normal way. The device special files, however, must be created in a special way, using the mknod command.
There is a shortcut, though — copy devices files from your existing hard disk /dev directory. The only requirement is that you copy the device special files using -R option. This will copy the directory without attempting to copy the contents of the files. Be sure to use an upper case R. For example:
cp -dpR /dev/fd[01]* /mnt/dev cp -dpR /dev/tty[0-6] /mnt/dev |
If you want to do it the hard way, use ls -l to display the major and minor device numbers for the devices you want, and create them on the diskette using mknod.
However the devices files are created, check that any special devices you need have been placed on the rescue diskette. For example, ftape uses tape devices, so you will need to copy all of these if you intend to access your floppy tape drive from the bootdisk.
Note that one inode is required for each device special file, and inodes can at times be a scarce resource, especially on diskette filesystems. You'll need to be selective about the device files you include. For example, if you do not have SCSI disks you can safely ignore /dev/sd*; if you don't intend to use serial ports you can ignore /dev/ttyS*.
If, in building your root filesystem, you get the error No space left on device but a df command shows space still available, you have probably run out of inodes. A df -i will display inode usage.
Be sure to include the following files from this directory: console, kmem, mem, null, ram0 and tty1. |
4.3.2. /etc
The /etc directory contains configuration files. What it should contain depends on what programs you intend to run. On most systems, these can be divided into three groups:
Required at all times, e.g. rc, fstab, passwd.
May be required, but no one is too sure.
Junk that crept in.
ls -ltru |
On my root diskettes, I have the number of config files down to 15. This reduces my work to dealing with three sets of files:
The ones I must configure for a boot/root system:
rc.d/* -- system startup and run level change scripts
fstab -- list of file systems to be mounted
inittab -- parameters for the init process, the first process started at boot time.
gettydefs -- parameters for the init process, the first process started at boot time.
The ones I should tidy up for a boot/root system:
passwd -- Critical list of users, home directories, etc.
group -- user groups.
shadow -- passwords of users. You may not have this.
termcap -- the terminal capability database.
If security is important, passwd and shadow should be pruned to avoid copying user passwords off the system, and so that unwanted logins are rejected when you boot from diskette.
Be sure that passwd contains at least root. If you intend other users to login, be sure their home directories and shells exist.
termcap, the terminal database, is typically several hundred kilobytes. The version on your boot/root diskette should be pruned down to contain only the terminal(s) you use, which is usually just the linux or linux-console entry.
The rest. They work at the moment, so I leave them alone.
Out of this, I only really have to configure two files, and what they should contain is surprisingly small.
rc should contain:
Be sure it is executable, be sure it has a "#!" line at the top, and be sure any absolute filenames are correct. You don't really need to run hostname — it just looks nicer if you do.#!/bin/sh /bin/mount -av /bin/hostname Kangaroo
fstab should contain at least:
You can copy entries from your existing fstab, but you should not automatically mount any of your hard disk partitions; use the noauto keyword with them. Your hard disk may be damaged or dead when the bootdisk is used./dev/ram0 / ext2 defaults /dev/fd0 / ext2 defaults /proc /proc proc defaults
Your inittab should be changed so that its sysinit line runs rc or whatever basic boot script will be used. Also, if you want to ensure that users on serial ports cannot login, comment out all the entries for getty which include a ttys or ttyS device at the end of the line. Leave in the tty ports so that you can login at the console.
A minimal inittab file looks like this:
id:2:initdefault: si::sysinit:/etc/rc 1:2345:respawn:/sbin/getty 9600 tty1 2:23:respawn:/sbin/getty 9600 tty2 |
Note that some programs cannot be moved elsewhere because other programs have hardcoded their locations. For example, on my system, /etc/shutdown has hardcoded in it /etc/reboot. If I move reboot to /bin/reboot, and then issue a shutdown command, it will fail because it cannot find the reboot file.
For the rest, just copy all the text files in your /etc directory, plus all the executables in your /etc directory that you cannot be sure you do not need. As a guide, consult the sample listing in Appendix C. Probably it will suffice to copy only those files, but systems differ a great deal, so you cannot be sure that the same set of files on your system is equivalent to the files in the list. The only sure method is to start with inittab and work out what is required.
Most systems now use an /etc/rc.d/ directory containing shell scripts for different run levels. The minimum is a single rc script, but it may be simpler just to copy inittab and the /etc/rc.d directory from your existing system, and prune the shell scripts in the rc.d directory to remove processing not relevent to a diskette system environment.
4.3.3. /bin and /sbin
The /bin directory is a convenient place for extra utilities you need to perform basic operations, utilities such as ls, mv, cat and dd. See Appendix C for an example list of files that go in a /bin and /sbin directories. It does not include any of the utilities required to restore from backup, such as cpio, tar and gzip. That is because I place these on a separate utility diskette, to save space on the boot/root diskette. Once the boot/root diskette is booted, it is copied to the ramdisk leaving the diskette drive free to mount another diskette, the utility diskette. I usually mount this as /usr.
Creation of a utility diskette is described below in Section 9.2. It is probably desirable to maintain a copy of the same version of backup utilities used to write the backups so you don't waste time trying to install versions that cannot read your backup tapes.
Be sure to include the following programs: init, getty or equivalent, login, mount, some shell capable of running your rc scripts, a link from sh to the shell. |
4.3.4. /lib
In /lib you place necessary shared libraries and loaders. If the necessary libraries are not found in your /lib directory then the system will be unable to boot. If you're lucky you may see an error message telling you why.
Nearly every program requires at least the libc library, libc.so.N, where N is the current version number. Check your /lib directory. The file libc.so.N is usually a symlink to a filename with a complete version number:
% ls -l /lib/libc* -rwxr-xr-x 1 root root 4016683 Apr 16 18:48 libc-2.1.1.so* lrwxrwxrwx 1 root root 13 Apr 10 12:25 libc.so.6 -> libc-2.1.1.so* |
In this case, you want libc-2.1.1.so. To find other libraries you should go through all the binaries you plan to include and check their dependencies with ldd. For example:
% ldd /sbin/mke2fs libext2fs.so.2 => /lib/libext2fs.so.2 (0x40014000) libcom_err.so.2 => /lib/libcom_err.so.2 (0x40026000) libuuid.so.1 => /lib/libuuid.so.1 (0x40028000) libc.so.6 => /lib/libc.so.6 (0x4002c000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) |
Note that some libraries are quite large and will not fit easily on your root filesystem. For example, the libc.so listed above is about 4 meg. You will probably need to strip libraries when copying them to your root filesystem. See Section 8 for instructions.
In /lib you must also include a loader for the libraries. The loader will be either ld.so (for A.OUT libraries, which are no longer common) or ld-linux.so (for ELF libraries). Newer versions of ldd tell you exactly which loader is needed, as in the example above, but older versions may not. If you're unsure which you need, run the file command on the library. For example:
% file /lib/libc.so.4.7.2 /lib/libc.so.5.4.33 /lib/libc-2.1.1.so /lib/libc.so.4.7.2: Linux/i386 demand-paged executable (QMAGIC), stripped /lib/libc.so.5.4.33: ELF 32-bit LSB shared object, Intel 80386, version 1, stripped /lib/libc-2.1.1.so: ELF 32-bit LSB shared object, Intel 80386, version 1, not stripped |
Copy the specific loader(s) you need to the root filesystem you're building. Libraries and loaders should be checked carefully against the included binaries. If the kernel cannot load a necessary library, the kernel may hang with no error message.
4.4. Providing for PAM and NSS
Your system may require dynamically loaded libraries that are not visible to ldd. If you don't provide for these, you may have trouble logging in or using your bootdisk.
4.4.1. PAM (Pluggable Authentication Modules)
If your system uses PAM (Pluggable Authentication Modules), you must make some provision for it on your bootdisk. Briefly, PAM is a sophisticated modular method for authenticating users and controlling their access to services. An easy way to determine if your system uses PAM is run ldd on your login executable; if the output includes libpam.so, you need PAM.
Fortunately, security is usually of no concern with bootdisks since anyone who has physical access to a machine can usually do anything they want anyway. Therefore, you can effectively disable PAM by creating a simple /etc/pam.conf file in your root filesystem that looks like this:
OTHER auth optional /lib/security/pam_permit.so OTHER account optional /lib/security/pam_permit.so OTHER password optional /lib/security/pam_permit.so OTHER session optional /lib/security/pam_permit.so |
This configuration allows anyone complete access to the files and services on your machine. If you care about security on your bootdisk for some reason, you'll have to copy some or all of your hard disk's PAM setup to your root filesystem. Be sure to read the PAM documentation carefully, and copy any libraries needed in /lib/security onto your root filesystem.
You must also include /lib/libpam.so on your bootdisk. But you already know this since you ran ldd on /bin/login, which showed this dependency.
4.4.2. NSS (Name Service Switch)
If you are using glibc (aka libc6), you will have to make provisions for name services or you will not be able to login. The file /etc/nsswitch.conf controls database lookups for various servies. If you don't plan to access services from the network (eg, DNS or NIS lookups), you need only prepare a simple nsswitch.conf file that looks like this:
passwd: files shadow: files group: files hosts: files services: files networks: files protocols: files rpc: files ethers: files netmasks: files bootparams: files automount: files aliases: files netgroup: files publickey: files |
If you plan to access the network from your bootdisk, you may want to create a more elaborate nsswitch.conf file. See the nsswitch man page for details. You must include a file /lib/libnss_service.so.1 for each service you specify.
4.5. Modules
If you have a modular kernel, you must consider which modules you may want to load from your bootdisk after booting. You might want to include ftape and zftape modules if your backup tapes are on floppy tape, modules for SCSI devices if you have them, and possibly modules for PPP or SLIP support if you want to access the net in an emergency.
These modules may be placed in /lib/modules. You should also include insmod, rmmod and lsmod. Depending on whether you want to load modules automatically, you might also include modprobe, depmod and swapout. If you use kerneld, include it along with /etc/conf.modules.
However, the main advantage to using modules is that you can move non-critical modules to a utility disk and load them when needed, thus using less space on your root disk. If you may have to deal with many different devices, this approach is preferable to building one huge kernel with many drivers built in.
In order to boot a compressed ext2 filesystem, you must have ramdisk and ext2 support built-in. They cannot be supplied as modules. |
4.6. Some final details
Some system programs, such as login, complain if the file /var/run/utmp and the directory /var/log do not exist. So:
mkdir -p /mnt/var/{log,run} touch /mnt/var/run/utmp |
Finally, after you have set up all the libraries you need, run ldconfig to remake /etc/ld.so.cache on the root filesystem. The cache tells the loader where to find the libraries. You can do this with:
ldconfig -r /mnt |
4.7. Wrapping it up
When you have finished constructing the root filesystem, unmount it, copy it to a file and compress it:
umount /mnt
dd if=DEVICE bs=1k | gzip -v9 > rootfs.gz |
Notes
[1] | The directory structure presented here is for root diskette use only. Real Linux systems have a more complex and disciplined set of policies, called the Filesystem Hierarchy Standard, for determining where files should go.) |