Merging Filesystems Virtually Under Apache


This is rather old code, but saved my bacon more than once. Runs under Apache with Mod_Perl, and “merges” any number of filesystems. You’ll see the @docroots array that takes a list of “document roots”. These all need correct permissions in Apache, or else the end request will fail. This module doesn’t trump Apache’s security model. In short, it tries the request against each of the entries in @docroots successively, setting the URI to the first found file, or declining altogether if none is found.


Most people who understand their web systems, know where their requests go. For performance, you really want to make sure the file system most likely to serve the request is listed first, the next most likely second, et cetera.

The exception to this is if you’re using this as a migration strategy. I have, twice, listed a mostly empty file system first, making the second file system “read-only”. The webby people then had a way to migrate their content to the new file system systematically and deliberately, with immediate results. This module is very efficient, so failed lookups are invisible to the user experience.


=head1 NAME

M::Apache::fsmerge - Want to merge multiple filesystems into one Apache filesystem?


PerlModule M::Apache::fsmerge


PerlTransHandler M::Apache::fsmerge



package M::Apache::fsmerge;

use Apache2::Const qw(DECLINED);
use Apache2::RequestUtil ();


=over 4

=item @docroots

An array of filesystem paths to merge. They are processed in array order, and the first match wins.


my @docroots = (
sub handler {
        my $r = shift;
        my $sofar=0;
        foreach my $dr (@docroots) {
                my $file=$dr . $r->uri();
                my $newuri=$dr; # $newuri is the uri we're building
                unless(-e $file) {
                        # File doesn't exist, let's try to find it!
                        my @uribits=split(/\//,$r->uri());
                        shift @uribits; # shift off the beginning ''
                        foreach my $bit (@uribits) {
                                $bit =~ s/\(|\)|\`//g; # stupid url tricks
                                $sofar=0; # reset sofar, so we know if we're still on track
                                opendir(FD,$newuri) or last;
                                foreach my $dthing (readdir(FD)) {
                                        if($dthing =~ /^\./) { next; } # safety first;
                                        if($dthing =~ /^$bit$/i) { # case-insenstive pattern match
                                                # We have a match!
                                                $newuri .= "/" . $dthing;
                                unless($sofar) { last; } # we missed this bit, don't bother recursing further
                } else {
                        # Woo hoo!

                if($sofar) {
                        # We made it!
                        $newuri =~ s/^$dr//; # strip off the document_root from the new uri
                        $r->document_root($dr); # Set the new docroot
                        $r->uri($newuri); # Set the uri to the new uri
                } # else we can't do anything...

        return DECLINED; # Pass it on, regardless of the outcome


This entry was posted in Architecture, Coding, Linuxy and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *