...making Linux just a little more fun!

Playing with Chroot

By Oscar Laycock

It took me a while to realise what chroot does.

Chroot runs a command with the root directory for file name translation changed to the specified directory. Usually only root can do this.

Here is a quick example:

First, I use ldd to print the shared libraries needed by my bash:

    libtermcap.so.2 => /lib/libtermcap.so.2
    libdl.so.2 => /lib/libdl.so.2
    libc.so.6 => /lib/libc.so.6
    /lib/ld-linux.so.2 => /lib/ld-linux.so.2

Then I create a directory and copy in the files:

    myroot/bin:
        ls bash
    myroot/lib:
        ld-linux.so.2 libc.so.6 libtermcap.so.2 libdl.so.2

then I just:

    chroot myroot /bin/bash
    cd /
    ls

The Bash prompt may say "I have no name!", as there is no /etc/passwd file!

In the kernel

The chroot program is part of the GNU shell utilities package. It is tiny, merely calling the C library function chroot() and then executing its second argument (or the default /bin/sh) with the C function execvp(). Here it use the shell PATH or "/bin:/usr/bin" if it is not set. The chroot library function has its definition in unistd.h:

/* Make PATH be the root directory (the starting point for absolute paths).
      This call is restricted to the super-user.  */
extern int chroot (__const char *__path)
Inside the kernel is the function "sys_chroot". It checks for the CAP_SYS_CHROOT capability. Then it simply changes the "current->fs" global structure's "rootmnt" and "root" fields to the filename's "dentry". Other code then uses these fields to determine the root directory. Have a look in the kernel sources in fs/open.c and fs/namespace.c (for the function set_fs_root).

Chroot in Linux from Scratch

Chroot is a key part of the Linux from Scratch (LFS) project which builds a handmade Linux system from a packaged one. See www.linuxfromscratch.org . The actual chroot command is a bit more complex:

chroot "$LFS" /tools/bin/env -i \ 
    HOME=/root TERM="$TERM" PS1='\u:\w\$ ' \ 
    PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin \ 
    /tools/bin/bash --login +h 

The -i option gives an empty environment. Bash hashing is switched off as we will be changing the location of the tools.

You can see how chroot fits in the whole LFS project. We take the following steps:

  1. Create a new partition and base directories (/lib, /bin, /usr etc)
  2. Build a new "toolchain" in the partition, comprising binutils (the assembler and linker), the gcc compiler and the large glibc (C library)
  3. Rebuild gcc, using configure options to use the new glibc and changing the gcc specs to use the new glibc's dynamic linker. (You usually "configure", "make" and "make install" when building a program from source code. Try running "gcc -dumpspecs" to see the mysterious compiler specs.)
  4. Rebuild binutils using its configure's --prefix option to use the new glibc
  5. build lots of tools such as bash, core/file utils, make, perl and so on
  6. CHROOT INTO THE NEW PARTITION'S DIRECTORIES!!!
  7. Rebuild glibc.
  8. Rebuild binutils and gcc, changing the directories to be relative to the chroot top directory. Build all the tools again.
  9. Build a kernel.
  10. Add the new partition and kernel to the bootloader.

As you can see, you end up building the basic tools three times! Luckily, there is another LFS project which automates this with scripts. Going further, the "Beyond Linux from Scratch" project shows you how to add lots more, such as web servers, and the Gnome and KDE window environments.

A quick compiler

I am currently building a linux system from scratch on an old laptop a friend gave me. I started with a kernel, and some small tools (fdisk, ls, cp etc) statically built, squeezed on a floppy. I then copied across Damn Small Linux (DSL) floppy by floppy, before setting up a ppp link with a serial cable. DSL does not have a compiler by default and I wanted to get one going quickly. The compiler seemed to conflict with the DSL system (a smaller old 2.4 kernel with no "thread local storage" for the C library to use), so I created a chroot directory with just enough to build a simple "hello world" program. I added the following files. (I believe "crt" stands for "C run-time" and "begin" files are code added at the start of the program!? A prefix or suffix of "s" usually means using shared libraries as normal.)

myroot/usr
|
+---include:
|       a.out.h ... xlocale.h
|
+---lib:
|       Mcrt1.o Scrt1.o crt1.o crti.o crtn.o gcrt1.o
|
+---local
    |
    +---bin:
    |       gcc
    |          
    +---i686-pc-linux-gnu
    |   |
    |   +---bin: 
    |   |       as ld
    |   |
    |   +---lib
    |       +---ldscripts:
    |               elf_i386.x ...
    |    
    +---lib:
    |   |   libgcc_s.so libgcc_s.so.1 libgmp.so.3 libmpfr.so.1
    |   |
    |   |---gcc
    |       +---i686-pc-linux-gnu
    |           +---4.3.2:
    |                   crtbegin.o crtbeginS.o ...
    |                   libgcc.a ... 
    |    
    +---libexec
        +---gcc
            +---i686-pc-linux-gnu
                +---4.3.2:
                        cc1 cc1plus collect2

Good luck!

Talkback: Discuss this article with The Answer Gang


[BIO]

I live by the River Thames in the suburbs of London, England. I play with Linux in my spare time on a ten year old PC. I was a C and Oracle programmer when I was younger.


Copyright © 2009, Oscar Laycock. Released under the Open Publication License unless otherwise noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its prior host, SSC, Inc.

Published in Issue 159 of Linux Gazette, February 2009

Tux