I just got Apache and perl running inside a chroot jail; this required that I make some libraries and also all of /usr/lib/perl5 available inside the chroot jail. So I wrote one program to hardlink libraries into the jail, and another to hardlink all of /usr/lib/perl5. (Some people prefer to copy rather than hardlink, due to an unfounded security concern: if someone gets root inside the chroot jail, they can scribble all over your system libraries if you hardlink them into the jail. However, if someone gets root inside the chroot jail, they can also escape from the chroot jail.)
Here's the program for hardlinking libraries: #!/usr/bin/perl -w use strict; # hardlink the .so's a particular program needs to start # into a chroot jail you intend to run that program in # resolve a path relative to a symlink. probably buggy. sub relpath { my ($base, $relative) = @_; return $relative if $relative =~ m|^/|; $base =~ s|/[^/]*$||; my $result = "$base/$relative"; 1 while ($result =~ s:/\./:/:g or $result =~ s:/[^/]+/\.\.\/:/:); return $result; } sub test_relpath { my ($expected, @args) = @_; my $real = relpath @args; die "get $real, expected $expected, for (@args)" if $real ne $expected; } test_relpath "/foo/baz", "/foo/bar", "baz"; test_relpath "/baz", "/foo/bar", "../baz"; test_relpath "/blork", "/foo/bar", "/blork"; # find the real pathname corresponding to a file which might be a # symlink sub pierce_veils { my ($file) = @_; $file = relpath $file, readlink $file while -l $file; return $file; } # emit the command to symlink a library into the chroot sub link_in { my ($chrootdir, $dependency) = @_; my $reality = pierce_veils $dependency; print "sudo ln $reality $chrootdir$dependency\n"; } # find the libraries a program depends on. sub dependencies { my ($program) = @_; open LDD, "ldd $program|"; my @results; while (<LDD>) { push @results, $1 if / => (.*) \(/; } close LDD or die "Couldn't popen ldd: $!"; return @results; } my ($chrootdir, $program) = @ARGV; die "Usage: $0 chrootdir program" unless defined $program; foreach my $dependency (dependencies $program) { link_in $chrootdir, $dependency unless -e "$chrootdir/$dependency"; } __END__ And here's the program for hardlinking entire directory trees: #!/usr/bin/perl -w use strict; # This program mirrors a directory tree in another location so as to # make the files in it available (read-only) inside a chroot. It's a # bit excessively paranoid --- it refuses to link in files that are # owned by other than root --- but it worked for my purposes. (I just # wanted to get perl running in a chroot, so I used this program to # mirror /usr/bin/perl5 in the chroot.) sub makedirs { my ($dir) = @_; my @chunks = split /\//, $dir; foreach my $i (1..$#chunks) { my $name = join '/', @chunks[0..$i]; if (not -d $name) { mkdir $name, 0755 or die "Can't mkdir $name: $!"; } } } sub mirrordir { my ($src, $dest) = @_; lstat $src; my $mode = (stat _)[2]; my $owner = (stat _)[4]; if ((not -l _ and $mode & 06022) or $owner) { # group or world writable or non-root-owned die "file $src doesn't look safe to hardlink into a chroot: " . sprintf "mode %o, owner %d", $mode, $owner; } elsif (-l _) { symlink readlink($src), $dest unless lstat $dest; } elsif (-d _) { makedirs $dest; opendir FOO, $src or die "Can't opendir $src: $!"; my @filenames = readdir FOO; closedir FOO; foreach my $file (@filenames) { next if $file eq '.' or $file eq '..'; mirrordir("$src/$file", "$dest/$file"); } } elsif (-f _) { link $src, $dest unless lstat $dest; } else { die "Can't deal with $src"; } } my ($src, $dest) = @ARGV; die "Usage: $0 sourcedir destdir" if not defined $dest; mirrordir($src, $dest); __END__