--- name: chroot signature: 'chroot FILENAME' since: 5.0 status: documented categories: ["Filehandles, files, directories"] --- ```{index} single: chroot; Perl built-in ``` *[Filehandles, files, directories](../perlfunc-by-category)* # chroot Change the root directory of the current process and every child it later spawns. After a successful `chroot`, any pathname that begins with `/` is resolved relative to the directory named by `FILENAME` instead of the real filesystem root. The process loses the ability to name files outside that subtree through absolute paths. The kernel enforces this for the current process and for every descendant — there is no per-thread scope and no way to undo it from inside the jail. `chroot` does **not** change the current working directory. If the working directory was outside the new root when the call succeeded, it stays outside, and relative paths can still escape. Always follow a successful `chroot` with [`chdir`](chdir) to `/` (which now means inside the jail) before doing anything else. The call is restricted to the superuser. An unprivileged process gets `EPERM` and [`$!`](../perlvar) is set accordingly. ## Synopsis ```perl chroot FILENAME chroot ``` ## What you get back `1` on success, `0` on failure. On failure [`$!`](../perlvar) holds the `errno` from the underlying `chroot(2)` system call — commonly `EPERM` (not root), `ENOENT` (path does not exist), or `ENOTDIR` (path is not a directory). Always check the return value. A silent failure leaves the process running outside the intended jail, which is usually worse than the code path that made the call in the first place. ```perl chroot "/var/empty" or die "chroot: $!"; chdir "/" or die "chdir after chroot: $!"; ``` ## Global state it touches - [`$_`](../perlvar) — when called with no argument, `chroot` uses the value of `$_` as the path. Inside a `while (<>)` loop this is rarely what you want; pass the path explicitly. - [`$!`](../perlvar) — set to the underlying `errno` on failure. ## Examples The canonical safe sequence — change root, then change working directory to the new root, then drop privileges: ```perl chroot "/srv/jail" or die "chroot: $!"; chdir "/" or die "chdir: $!"; $) = $(= 65534; # drop to 'nobody' gids $> = $< = 65534; # drop to 'nobody' uids ``` Open files and load shared libraries **before** `chroot`, not after. The new root typically has neither `/etc/resolv.conf` nor the dynamic linker's search paths: ```perl open my $log, ">>", "/var/log/app.log" or die $!; my $cfg = read_config("/etc/app.conf"); chroot "/srv/jail" or die "chroot: $!"; chdir "/" or die "chdir: $!"; # $log and $cfg are still usable inside the jail. ``` Checking failure modes separately — distinguishing "not root" from "bad path" matters for diagnostics: ```perl use Errno qw(EPERM ENOENT ENOTDIR); unless (chroot $path) { die "chroot: need root privileges\n" if $! == EPERM; die "chroot: $path does not exist\n" if $! == ENOENT; die "chroot: $path is not a directory\n" if $! == ENOTDIR; die "chroot: $!"; } ``` Default-argument form reads the path from [`$_`](../perlvar): ```perl $_ = "/srv/jail"; chroot or die "chroot: $!"; ``` ## Edge cases - **Not root**: fails with `EPERM`. There is no capability-based relaxation in stock Linux unless the process holds `CAP_SYS_CHROOT`. - **Relative path**: `chroot "jail"` resolves against the current working directory at the moment of the call. Prefer absolute paths so the behavior does not depend on the caller's `cwd`. - **Symlinks in `FILENAME`**: resolved before the chroot takes effect. Symlinks *inside* the new root that point to absolute paths will resolve relative to the new root, not the real root — this is the intended jailing behavior, not a bug. - **Open file descriptors survive**: any handle opened before the call remains usable afterward. This is how well-behaved daemons keep logs, config, and listening sockets across the transition. - **No escape from inside**: a process cannot `chroot` its way out. The only way to leave a chroot is for a privileged process *outside* it to reap and replace it. - **Working directory outside the new root**: the kernel allows the call, but relative paths and `..` traversal from there can still reach files outside the jail. The mandatory `chdir "/"` after `chroot` closes that gap. - **Threads**: `chroot` on Linux is per-process (strictly, per fs struct). Under `use threads` all interpreter threads share the same root after the call — there is no per-thread jail. - **Does not affect [`$0`](../perlvar), [`%ENV`](../perlvar), or open sockets**. Only path resolution changes. ## Differences from upstream Fully compatible with upstream Perl 5.42. ## See also - [`chdir`](chdir) — the mandatory follow-up after a successful `chroot`; without it the working directory may still point outside the new root - [`fork`](fork) — children inherit the chroot; combine with `chroot` to run untrusted code in a restricted subtree - [`exec`](exec) — runs the replacement program inside the current root, so the target binary and its dynamic libraries must exist inside the jail - [`open`](open) — opening files by absolute path after `chroot` looks up names inside the jail; pre-open anything you need from outside before the call - [`$_`](../perlvar) — the default path when `chroot` is called with no argument - [`$!`](../perlvar) — holds the `errno` from `chroot(2)` on failure