Saturday, March 7, 2009

FUSE test implementation

















I made a test script with FusePython, referencing a couple of existing scripts (this one and this one). You can make a file e.g. www.google.co.jp and get the contents of the page from it. Internally it uses urllib2 module. Once it downloads the HTML it caches it in the memory.

To mount it,


python hohefs.py mountpoint

To unmount it,

fusermount -u mountpoint


It is not at all a good example. I just made it to see what it would be like to make a file system with FUSE. I didn't implement permission, I omitted lots of error checkings, and unlink equals to deleting a file, etc.etc.etc....

import fuse
from fuse import Fuse
from time import time
import sys
import urllib2

import stat # for file properties
import os # for filesystem modes (O_RDONLY, etc)
import errno # for error number codes (ENOENT, etc)
# - note: these must be returned as negatives

fuse.fuse_python_api = (0, 2)


class Mystdout():

def __init__(self, filename):
self.f = open(filename, 'w')

def write(self, text):
self.f.write(text)
self.f.flush()


class HoheStat(fuse.Stat):

def __init__(self):
self.st_mode = 0
self.st_ino = 0
self.st_dev = 0
self.st_nlink = 0
self.st_uid = 0
self.st_gid = 0
self.st_size = 0
self.st_atime = 0
self.st_mtime = 0
self.st_ctime = 0

def setStat(self, path):
st =os.stat(path)
self.st_mode = st[0]
self.st_ino = st[1]
self.st_dev = st[2]
self.st_nlink = st[3]
self.st_uid = st[4]
self.st_gid = st[5]
self.st_size = st[6]
self.st_atime = st[7]
self.st_mtime = st[8]
self.st_ctime = st[9]


class NullFS(Fuse):

def __init__(self, logfilename, mountpoint, *args, **kw):
Fuse.__init__(self, *args, **kw)
sys.stdout = Mystdout(logfilename)
self.__filestats = {}
s = self.__filestats['/'] = HoheStat()
s.setStat(mountpoint)
self.__docs ={}
print 'Init complete.'

def getattr(self, path):
print '*** getattr', path
return self.__filestats.get(path, -errno.ENOENT)

def readdir(self, path, offset):
print '*** readdir', path, offset
for r in self.__getFileNames(path) + ['.', '..']:
yield fuse.Direntry(r)

def getdir(self, path):
print '*** getdir', path
return map(lambda x: (x, 0), self.__getFileNames(path))

def mknod(self, path, mode, dev):
print '*** mknod', path, oct(mode), dev
st = HoheStat()
st.st_mode = stat.S_IFREG | mode
st.st_dev = dev
st.st_nlink = 1
st.st_size = 0
st.st_uid = os.getuid()
st.st_gid = os.getgid()
st.st_utime = time()
self.__filestats[path] = st

def utime (self, path, times):
print '*** utime', path, times
st = self.__filestats.get(path)
if st:
st.st_atime = times[0]
st.st_utime = times[1]

def read(self, path, length, offset):
print '*** read', path, length, offset
st = self.__filestats[path]
st.st_atime = time()
return self.__docs[path][offset:offset +length]

def open(self, path, flags):
print '*** open', path, flags
st = self.__filestats.get(path)
if not st:
return -errno.ENOENT

accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
if not (flags & accmode) in [os.O_RDONLY, os.O_WRONLY, os.O_RDWR]:
return -errno.EACCES

docs = self.__docs
if path in docs:
doc = docs[path]
else:
f = urllib2.urlopen("http:/" + path)
doc = f.read()
st.st_size = len(doc)
docs[path] = doc
#Oops I need to close f

def mkdir(self, path, mode):
print '*** mkdir', path, oct(mode)

st =self.__filestats.get(path)
if st:
return -errno.EEXIST

parentpath = os.path.dirname(path)
parentst = self.__filestats.get(parentpath)
if not parentst:
return -errno.ENOENT
if not parentst.st_mode & stat.S_IFDIR:
return -errno.ENOTDIR

st = HoheStat()
st.st_mode = stat.S_IFDIR | mode
st.st_dev = 0
st.st_nlink = 1
st.st_size = 0
st.st_uid = os.getuid()
st.st_gid = os.getgid()
st.st_utime = time()
self.__filestats[path] = st

def unlink(self, path):
print '*** unlink', path
if path in self.__filestats:
del self.__filestats[path]
docs =self.__docs
if path in docs:
del docs[path]
else:
return -errno.ENOENT

def rmdir(self, path):
print '*** rmdir', path
if self.__getFileNames(path):
return -errno.EEXIST
if path in self.__filestats:
del self.__filestats[path]
else:
return -errno.ENOENT

def rename(self, oldPath, newPath):
print '*** rename', oldPath, newPath
return -errno.ENOENT

def mythread(self):
print '*** mythread'
return -errno.ENOSYS

def chmod(self, path, mode):
print '*** chmod', path, oct(mode)
return -errno.ENOSYS

def chown(self, path, uid, gid):
print '*** chown', path, uid, gid
return -errno.ENOSYS

def fsync(self, path, isFsyncFile):
print '*** fsync', path, isFsyncFile
return -errno.ENOSYS

def link(self, targetPath, linkPath):
print '*** link', targetPath, linkPath
return -errno.ENOSYS

def readlink(self, path):
print '*** readlink', path
return -errno.ENOSYS

def release(self, path, flags):
print '*** release', path, flags
return 0
return -errno.ENOSYS

def statfs(self):
print '*** statfs'
return -errno.ENOSYS

def symlink(self, targetPath, linkPath):
print '*** symlink', targetPath, linkPath
return -errno.ENOSYS

def truncate(self, path, size):
print '*** truncate', path, size
return -errno.ENOSYS

def write(self, path, buf, offset):
print '*** write', path, buf, offset
return -errno.ENOSYS

def __getFileNames(self, currentpath):
filt = lambda(x): os.path.dirname(x) == currentpath
filesincurrent = filter(filt, self.__filestats)
if currentpath in filesincurrent:
filesincurrent.remove(currentpath)
return map(os.path.basename, filesincurrent)

if __name__ == '__main__':

usage="""
Userspace hello example

""" + Fuse.fusage

fs = NullFS(
"hohefs.log",
sys.argv[1],
version="%prog " + fuse.__version__,
usage=usage,
dash_s_do='setsingle')

fs.parse(values=fs, errex=1)
fs.main()

No comments: