...making Linux just a little more fun!

Playing with Chroot

By Oscar Laycock

It took me a while to realise what chroot does. As I found out, it runs a command with the root directory for file name translation changed to the specified directory. Usually, only root can do this. [1]

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:

        ls bash
        ld-linux.so.2 libc.so.6 libtermcap.so.2 libdl.so.2

then I just:

    chroot myroot /bin/bash
    cd /

Note: the bash prompt will very likely say "I have no name!", as there is no /etc/passwd file in the chrooted structure.

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 uses 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 (the function name is 'set_fs_root') for more info.

Chroot in Linux from Scratch

Chroot is a key part of the Linux from Scratch (LFS) project, which allows you to build a handmade Linux system. The actual chroot command there 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. Once we have the above set up, 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 the '--prefix' option of "configure" to use the new glibc.
  5. Build lots of tools such as bash, core/file utils, make, perl, and so on.
  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 that automates this process, with scripts. Even more, the "Beyond Linux from Scratch" project shows you how to add much more, such as Web servers and the GNOME and KDE desktop environments.

A Quick Compiler

I am currently building an LFS system on an old laptop a friend gave me. I started with a kernel, and some small tools (fdisk, ls, cp, etc.), statically built and squeezed onto 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.)

|       a.out.h ... xlocale.h
|       Mcrt1.o Scrt1.o crt1.o crti.o crtn.o gcrt1.o
    |       gcc
    |   |
    |   +---bin: 
    |   |       as ld
    |   |
    |   +---lib
    |       +---ldscripts:
    |               elf_i386.x ...
    |   |   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 ... 
                        cc1 cc1plus collect2

A common application of the chroot call would be to run a Web or FTP server chrooted in a directory like /home/www or /home/ftp; this provides an excellent layer of security, since even a malicious non-root user who manages to crack that server is stuck in a "filesystem" that contains few or no tools, no useful files other than the ones already available for viewing or downloading, and no way to get up "above" the top of that filesystem. This is referred to as a "chroot jail".
Do note, however, that allowing a user to log in as root into your chroot account is not safe: root can break out of a chroot jail with trivial ease. Please see the following links for more information:
-- Ben Okopnik

Talkback: Discuss this article with The Answer Gang


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 161 of Linux Gazette, April 2009