β

fuse实现webfs

linux菜园子 7 阅读

fuse是一个用户空间级别的文件系统接口,大家都知道文件系统通常在内核中实现,但是因为一些原因,有一些文件系统不方便在在内核中实现,比如版权的原因,或者开发者不想写C,于是fuse诞生了,总之fuse就是一个让你在用户空间实现文件系统的接口,虽然是在用户空间实现的,但是文件系统该有的接口,fuse也基本不拉,不过,看用户的需求,如果你只要实现一个很简单的功能,那么,也许一个只需要实现4-5个就可以满足你的需求,接下来我们来实现一个基于web的文件系统。
在linux下,我们跨机器传输文件经常会用到如下这个命令:

python -m SimpleHTTPServer

这个命令会在本地起一个端口,然后从另一个机器的访问这个http端口,就可以访问文件并且下载,相信很多同学对这个不陌生。
那么,有没有办法把这种类似的接口挂载到本地,像访问文件夹一样cp呢?
那我们用fuse就可以实现这个功能,基本的我们实现如下几个接口就可以:
getattr:获得文件属性,是普通文件还是目录,文件权限,在我们常见下文件都为444,文件夹都是555,因为要保证进入文件夹,需要x权限
read:读取文件内容的接口,需要支持offset
readdir:读取文件夹下的子文件,返回是一个列表
具体的代码如下:

    def getattr(self, path, fh=None):
        wh = self.getwebheader(path)
        if "Content-Length" in wh:
            st_size = int(wh["Content-Length"])
        else:
            st_size = 0
        tplist = path.split("/")
        ppath = "/".join([i for i in tplist if i != ""][0:-1]) + "/"
        w = self.getweb(ppath)
        if re.search('a href="' + path.lstrip("/") + '/"', w) or path == "/":
            st_mode = 0o40555
        else:
            st_mode = 0o100444
        attr_dict = {
            "st_mode": st_mode,
            "st_size": st_size,
                 }
        return attr_dict

    getxattr = None
    listxattr = None

    def open(self, path, flags):
        self.fd += 1
        return self.fd

    def read(self, path, size, offset, fh):
        w = self.getweb(path, size, offset)
        return w

    def readdir(self, path, fh):
        nulldir = ['.', '..']
        w = self.getweb(path)
        allfiles = re.findall('a href="http://lisux.me/lishuai/(.*)"', w)
        dirs = [fs[0:-1] for fs in allfiles if fs.endswith("/")]
        files = [fs for fs in allfiles if not fs.endswith("/")]
        rsdir = files + dirs + nulldir
        return rsdir

    def getweb(self, path, size=None, offset=None):
        if size != None:
            headers = {"Range": "bytes=%d-%d" % (offset, offset + size - 1)}
        else:
            headers = {}
        r = requests.get(self.url + path, headers=headers)
        return r.content

    def getwebheader(self, path):
        r = requests.head(self.url + path)
        return r.headers

这个文件完整版本在这里:https://github.com/lishuai860113/fusepy/blob/master/examples/webfs.py
这样我们在远端起一个SimpleHTTPServer接口,然后本地这样挂载一下:

python webfs.py http://10.20.40.170:8000 /mnt/tmp

就可以在/mnt/tmp下读取到你web上的内容了。
那这样有一点美中不足的地方,在我们cp大文件的时候,会报错,这里是因为SimpleHTTPServer不支持下载的时候指定range,我们read的时候,一般都是按照一个block 4096去读取的,所以一定要web服务器支持range才可以,那我们也准备了一个支持range的server :https://github.com/lishuai860113/fusepy/blob/master/examples/RangeHTTPServer.py
用这个就没有问题了,另外go也写了一个简单的接口:
https://github.com/lishuai860113/UploadFileServer
go这个库原生是支持range的,所以没有问题的。
这样,就可以做出自己想要的文件系统了,祝玩的开心!

作者:linux菜园子
李帅的linux折腾总结记录
原文地址:fuse实现webfs, 感谢原作者分享。

发表评论