FlatFS

A shallow file structure mirorring an existing directory tree


File under: flatfusepython.

Do you like methaphors? FlatFS is like watching a tree through glasses that were flattened by a steam roller.

FlatFS is a fuse filesystem that displays all files found in a certain directory (the source) and subdirectories thereof in a single directory (the target). It works by simply substituting / with something else. By default it uses ___ (triple underscore) but this is configurable. If you make a new file in the target-dir containing the triple underscore pattern new directories will be made accordingly in the source-directory.

$ tree source
source
├── bar
├── baz
│   ├── one
│   └── two
└── zzz

$ tree target

target
├── bar
├── baz___one
├── baz___two
└── zzz

$ cd target
$ touch baz___three
$ ls ../source/baz
one two three

$ touch flatfs___rocks___yeah
$ ls ../source
bar baz flatfs zzz
$ ls -R ../source/flatfs
flatfs/:
rocks

rocks/:
yeah

Well… you get it.

A good way to mess things up is by using a special pattern that is already present in the original file tree. Depending on what you do with your files FlatFS could move or copy the file to a new subdirectory.

But is it useful? I guess not; but who knows? For me it was a way to learn more about Fuse.

Anyhow, here’s the full source code:

#!/usr/bin/python

# FlatFS provides a different view on a directory by redirecting file-system calls.
# Copyright (C) 2006  Tijn Schuurmans
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import fuse
from fuse import Fuse
fuse.fuse_python_api = (0, 2)

import os
import sys
from errno import *
from stat import *
import thread

sourcedir = ""
DEBUG = True


def escape(path):
    return path.replace("/", "____")

def unescape(path):
    return path.replace("____", "/")

def realpath(path):
    return sourcedir + unescape(path);


class Flatfs(Fuse):

    def __init__(self, *args, **kw):
        Fuse.__init__(self, *args, **kw)

    def getattr(self, path):
        return os.lstat(realpath(path))

    def readlink(self, path):
        return os.readlink(realpath(path))

    def getdir(self, path):
        rp = realpath(path);
        files = []
        for root, dirs, realfiles in os.walk(rp):
            for name in realfiles:
                f = os.path.join(root, name)
                f = f.replace(sourcedir+"/","") # FIXME: make more robust by matching the beginning of the line
                files.append(escape(f))
        return map(lambda x: (x,0), files)

    def open(self, path, flags):
        rp = realpath(path)
        os.close(os.open(rp, flags))
        return 0

    def read(self, path, lenght, offset):
        rp = realpath(path)
        f = open(rp, "r")
        f.seek(offset)
        return f.read(lenght)

    def unlink(self, path):
        rp = realpath(path)
        return os.unlink(rp)

    def rename(self, path, path1):
        rp = realpath(path)
        realpath1 = realpath(path1)
        return os.rename(rp, realpath1)

    def chmod(self, path, mode):
        rp = realpath(path)
        return os.chmod(rp, mode)

    def chown(self, path, user, group):
        rp = realpath(path)
        return os.chown(rp, user, group)

    def truncate(self, path, size):
        rp = realpath(path)
        f = open(rp, "w+")
        return f.truncate(size)

    def utime(self, path, times):
        rp = realpath(path)
        return os.utime(rp, times)

    def write(self, path, buf, off):
        rp = realpath(path)
        f = open(rp, "r+")
        f.seek(off)
        f.write(buf)
        return len(buf)

    def release(self, path, flags):
        return 0

    def statfs(self):
        blocks_size = 0
        blocks = 0
        blocks_free = 0
        files = 0
        files_free = 0
        namelen = 0
        return (blocks_size, blocks, blocks_free, files, files_free, namelen)

    def fsync(self, path, isfsyncfile):
        return 0


if __name__ == '__main__':
    if len(sys.argv)<2:
        print "Usage:", sys.argv[0], "[mountpoint] [sourcedir]"
        sys.exit()
    mountdir = sys.argv[1]
    sourcedir = sys.argv[2]
    print "Flat filesytem off directory", sourcedir
    server = Flatfs(sys.argv)
    server.main()