ここいらでfuseを一区切り

どうすればfuseを使えるのかを知りたかったので、サンプルコードを参考にあれこれしていましたが、ここらで一区切り。

"ローカルファイル"以外の何かを使って読み書きが出来ればFUSEのさわり位は体験出来るだろうという事で、お題を『memcachedへの入出力をFUSEを使って書いてみる』としてみました。

書いたコードでは、読み・書き・削除くらいしか出来ませんが、カーネルもファイルシステムもろくに知らない自分でも、ls,cat,vi,rmなどのコマンドが使える似非ファイルシステムが書けました。

FUSEを利用したファイルシステムを実装する作業は、システムコールAPIを実装する作業と言えると思います。今回利用したPythonバインディングでは、fuse.Fuseクラスを継承してopen,read,writeなどのシステムコールをメソッドとして実装しました。この時、当然ながらシステムコール関数が利用する構造体(statやfstat)の遷移(?)などを扱う必要があるので、各システムコールがどんな役割を持っていてどんな動作をするのかは知っている必要があります。この辺りは適当な知識しか無いので困りました。

今回は目標を"感覚を掴む事が出来ればよし"としていたので、この辺りでやめにしておきますが、やはりI/Oの基礎(になるのかな?)は勉強し直さないと駄目ですね。ある程度"なにを知りたいのか"がはっきりしているので、やり易い気がしています。

そんなこんななFUSE体験記でした。

memcachefs.py

以下、習作として書いたコードです。記録がてら残しますが、だいぶ駄目なコードなのでご注意ください。

./memcachefs.py <memcache_server>:<memcache_port> </to/mount/point>
#!/usr/bin/env python
 
import sys
import os
import stat
import time
import errno
import memcache
import fuse
 
class MemcacheStat(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
 
class MemcacheFS(fuse.Fuse):
 
    def __init__(self, memcache, *args, **kw):
        fuse.Fuse.__init__(self, *args, **kw)
        self.__memcache = memcache
        self.__init_root()
        self.__keys = {}
 
    def __init_root(self):
        root_stat = os.stat(sys.argv[-1])
        self.__root_stat = MemcacheStat()
        self.__root_stat.st_mode  = root_stat[0]
        self.__root_stat.st_ino   = root_stat[1]
        self.__root_stat.st_dev   = root_stat[2]
        self.__root_stat.st_nlink = root_stat[3]
        self.__root_stat.st_uid   = root_stat[4]
        self.__root_stat.st_gid   = root_stat[5]
        self.__root_stat.st_size  = root_stat[6]
        self.__root_stat.st_atime = root_stat[7]
        self.__root_stat.st_utime = root_stat[8]
        self.__root_stat.st_ctime = root_stat[9]
 
    def __get_key(self, path):
        return '/' + os.path.basename(path)
 
    def getattr(self, path):
        if path == '/':
            return self.__root_stat
        else:
            key = self.__get_key(path)
            if self.__keys.get(key, None):
                return self.__keys[key]
            else:
                return -errno.ENOENT
 
    def readdir(self, path, offset):
        for r in  ['.', '..'] + [os.path.basename(k) for k in self.__keys]:
            yield fuse.Direntry(r)
 
    def open(self, path, flags):
        key = self.__get_key(path)
        if self.__memcache.get(key) == None:
            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
 
    def read(self, path, size, offset):
        key = self.__get_key(path)
        d = self.__memcache.get(key)
        if d == None:
            return -errno.ENOENT
        else:
            self.__keys[key].st_atime = time.time()
            return d
 
    def write(self, path, buf, offset):
        key = self.__get_key(path)
        size = len(buf)
        if self.__memcache.set(key, buf):
            self.__keys[key].st_utime = time.time()
            self.__keys[key].st_size  = size
            #self.__keys[key].st_ctime = time.time()
            return size
        else:
            return -error.EIO
 
    def getdir(self, path):
        return map(lambda x: (x, 0), path_list)
 
    def mythread ( self ):
        print '*** mythread'
        -errno.ENOSYS
 
    def chmod ( self, path, mode ):
        key = self.__get_key(path)
        if self.__keys.get(key, None):
            self.__keys[key].st_mode = stat.S_IFREG | mode
            #self.__keys[key].st_ctime = time.time()
        else:
            return -errno.ENOENT
 
    def chown ( self, path, uid, gid ):
        key = self.__get_key(path)
        if self.__keys.get(key, None):
            self.__keys[key].st_uid = uid
            self.__keys[key].st_gid = gid
            #self.__keys[key].st_ctime = time.time()
        else:
            return -errno.ENOENT
 
    def fsync ( self, path, isFsyncFile ):
        print '*** fsync', path, isFsyncFile
        -errno.ENOSYS
 
    def link ( self, targetPath, linkPath ):
        print '*** link', targetPath, linkPath
        -errno.ENOSYS
 
    def mkdir ( self, path, mode ):
        print '*** mkdir', path, oct(mode)
        -errno.ENOSYS
 
    def mknod ( self, path, mode, dev ):
        key = self.__get_key(path)
        self.__memcache.set(key, '')
        now = time.time()
        st = MemcacheStat()
        st.st_mode  = stat.S_IFREG | mode
        st.st_dev   = dev
        st.st_nlink = 1
        st.st_size  = len(self.__memcache.get(key))
        st.st_uid   = os.getuid()
        st.st_gid   = os.getgid()
        st.st_utime = now
        #st.st_ctime = now
        self.__keys[key] = st
 
    def readlink ( self, path ):
        print '*** readlink', path
        -errno.ENOSYS
 
    def release ( self, path, flags ):
        print '*** release', path, flags
        -errno.ENOSYS
 
    def rename ( self, oldPath, newPath ):
        old_key = self.__get_key(oldPath)
        new_key = self.__get_key(newPath)
        d = self.__memcache.get(old_key)
        self.__memcache.set(new_key, d)
        self.__memcache.delete(old_key)
        self.__keys[new_key] = self.__keys[old_key]
        del(self.__keys[old_key])
 
    def rmdir ( self, path ):
        print '*** rmdir', path
        -errno.ENOSYS
 
    def statfs ( self ):
        print '*** statfs'
        -errno.ENOSYS
 
    def symlink ( self, targetPath, linkPath ):
        print '*** symlink', targetPath, linkPath
        -errno.ENOSYS
 
    def truncate ( self, path, size ):
        print '*** truncate', path, size
        -errno.ENOSYS
 
    def unlink ( self, path ):
        key = self.__get_key(path)
        self.__memcache.delete(key)
        del(self.__keys[key])
 
    def utime ( self, path, times ):
        key = self.__get_key(path)
        if self.__keys.get(key, None):
            self.__keys[key].st_atime = times[0]
            self.__keys[key].st_utime = times[1]
 
def main():
    usage="""
memcached client use Filesystem
<prog> <host:port> <mount point>
""" + fuse.Fuse.fusage
    if len(sys.argv) < 3:
        raise usage
    mc = memcache.Client([sys.argv[1]], debug=0)
    if not mc.get_stats():
        raise 'memcached error: could not connect to ' + sys.argv[1]
    server = MemcacheFS(version="%prog " + fuse.__version__,
                        usage=usage,
                        dash_s_do='setsingle',
                        memcache=mc)
    server.parse(errex=1)
    server.main()
 
if __name__ == '__main__':
    main()
 
プロフィール

このブログ記事について

このページは、koshigoeが2007年4月 8日 23:00に書いたブログ記事です。

ひとつ前のブログ記事は「井筒監督はやっぱり熱い!」です。

次のブログ記事は「PythonでPlaggerしてますか?」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。