用Python编写一个简单的FUSE文件系统的教程_python

如果你是我的长期读者,那么你应该知道我在寻找一个完美备份程序,最后我写了一个基于bup的我自己的加密层。

在写encbup的时候,我对仅仅恢复一个文件就必须要下载整个巨大的档案文件的做法不甚满意,但仍然希望能将EncFS和 rdiff-backup一起使用来实现可远程挂载、加密、去重、版本化备份的功能。

再次试用obnam 后(啰嗦一句:它还是慢的出奇),我注意到了它有一个mount命令。深入研究后,我发现了fuse-python和fusepy,感觉用Python写一个FUSE文件系统应该挺简单的。

聪明的读者可能已经意识到了我接下来要做的事情:我决定用Python写一个加密文件系统层!它与EncFS会非常相似,但也有一些重要的区别:

  •     它默认以反向模式运行,接收正常的文件并且暴露一个被加密的目录。任何备份程序会发现(并且备份)这些加密的目录,不需要任何其它的存储。
  •     它也能接受由一个目录列表组成的配置文件,并且在挂载点将这些目录暴露出来。这样的话,所有的备份脚本就需要将挂载点备份,各种不同的目录会立刻得以备份。
  •     它会更偏重于备份,而不是加密存储。写起来应该会挺有意思的。

一个FUSE文件系统示例

写这个脚本的第一步是写出一个纯粹的传递式的文件系统。它仅仅是接受一个目录,并在挂载点将其暴露出来,确保任何在挂载点的修改都会镜像到源数据中。

fusepy 要求你写一个类,里面定义了各种操作系统级别的方法。你可以选择定义那些你的文件系统想要支持的方法,其他的可以暂时不予定义,但是我需要定义全部的方法,因为我的文件系统是一个传递式的文件系统,它应该表现的与原有的文件系统尽可能一致。

写这段代码会非常简单有趣,因为大部分的方法只是对os模块的一些简单封装(确实,你可以直接给它们赋值,比如 open=os.open 等等,但是我的模块需要一些路径扩展)。不幸的是,fuse-python有一个bug(据我所知)是当打开和读文件的时候,它无法将文件句柄回传给文件系统。因而我的脚本不知道某个应用执行读写操作时对应的是哪个文件句柄,从而导致了失败。只需要对fusepy做极少的改动,它就可以很好地运行。它只有一个文件,所以你可以把它直接放到你的工程里。
代码

在这里,我很乐意给出这段代码,当你打算自己实现文件系统的时候可以拿来参考。这段代码提供了一个很好的起点,你可以直接把这个类复制到你的工程中并且根据需要重写里面的一些方法。

接下来是真正的代码了:

#!/usr/bin/env python

from __future__ import with_statement

import os
import sys
import errno

from fuse import FUSE, FuseOSError, Operations

class Passthrough(Operations):
  def __init__(self, root):
    self.root = root

  # Helpers
  # =======

  def _full_path(self, partial):
    if partial.startswith("/"):
      partial = partial[1:]
    path = os.path.join(self.root, partial)
    return path

  # Filesystem methods
  # ==================

  def access(self, path, mode):
    full_path = self._full_path(path)
    if not os.access(full_path, mode):
      raise FuseOSError(errno.EACCES)

  def chmod(self, path, mode):
    full_path = self._full_path(path)
    return os.chmod(full_path, mode)

  def chown(self, path, uid, gid):
    full_path = self._full_path(path)
    return os.chown(full_path, uid, gid)

  def getattr(self, path, fh=None):
    full_path = self._full_path(path)
    st = os.lstat(full_path)
    return dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime',
           'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid'))

  def readdir(self, path, fh):
    full_path = self._full_path(path)

    dirents = ['.', '..']
    if os.path.isdir(full_path):
      dirents.extend(os.listdir(full_path))
    for r in dirents:
      yield r

  def readlink(self, path):
    pathname = os.readlink(self._full_path(path))
    if pathname.startswith("/"):
      # Path name is absolute, sanitize it.
      return os.path.relpath(pathname, self.root)
    else:
      return pathname

  def mknod(self, path, mode, dev):
    return os.mknod(self._full_path(path), mode, dev)

  def rmdir(self, path):
    full_path = self._full_path(path)
    return os.rmdir(full_path)

  def mkdir(self, path, mode):
    return os.mkdir(self._full_path(path), mode)

  def statfs(self, path):
    full_path = self._full_path(path)
    stv = os.statvfs(full_path)
    return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree',
      'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag',
      'f_frsize', 'f_namemax'))

  def unlink(self, path):
    return os.unlink(self._full_path(path))

  def symlink(self, target, name):
    return os.symlink(self._full_path(target), self._full_path(name))

  def rename(self, old, new):
    return os.rename(self._full_path(old), self._full_path(new))

  def link(self, target, name):
    return os.link(self._full_path(target), self._full_path(name))

  def utimens(self, path, times=None):
    return os.utime(self._full_path(path), times)

  # File methods
  # ============

  def open(self, path, flags):
    full_path = self._full_path(path)
    return os.open(full_path, flags)

  def create(self, path, mode, fi=None):
    full_path = self._full_path(path)
    return os.open(full_path, os.O_WRONLY | os.O_CREAT, mode)

  def read(self, path, length, offset, fh):
    os.lseek(fh, offset, os.SEEK_SET)
    return os.read(fh, length)

  def write(self, path, buf, offset, fh):
    os.lseek(fh, offset, os.SEEK_SET)
    return os.write(fh, buf)

  def truncate(self, path, length, fh=None):
    full_path = self._full_path(path)
    with open(full_path, 'r+') as f:
      f.truncate(length)

  def flush(self, path, fh):
    return os.fsync(fh)

  def release(self, path, fh):
    return os.close(fh)

  def fsync(self, path, fdatasync, fh):
    return self.flush(path, fh)

def main(mountpoint, root):
  FUSE(Passthrough(root), mountpoint, foreground=True)

if __name__ == '__main__':
  main(sys.argv[2], sys.argv[1])

如果你想要运行它,只需要安装fusepy,把这段代码放进一个文件(比如myfuse.py)然后运行 python myfuse.py /你的目录 /挂载点目录 。你会发现 “/你的目录” 路径下的所有文件都跑到”/挂载点目录”,还能像用原生文件系统一样操作它们。
结语

总的来说,我并不认为写一个文件系统就这么简单。接下来要做的是在脚本里添加加密/解密的功能,以及一些帮助类的方法。我的目标是能让它除了有更好的扩展性(因为是用Python写的),以及包含一些针对备份文件的额外特性外,可以成为一个EncFS的完全替代品。

如果你想跟进这个脚本的开发过程,请在下面订阅我的邮件列表,或者在Twitter上关注我。一如既往的欢迎反馈(在下面评论就很好)。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索python
文件系统
fuse文件系统、fuse文件系统原理、android fuse文件系统、fuse用户空间文件系统、fuse 文件系统开发,以便于您获取更多的相关知识。

时间: 2024-08-17 13:15:30

用Python编写一个简单的FUSE文件系统的教程_python的相关文章

用Python编写一个简单的Lisp解释器的教程_python

本文有两个目的: 一是讲述实现计算机语言解释器的通用方法,另外一点,着重展示如何使用Python来实现Lisp方言Scheme的一个子集.我将我的解释器称之为Lispy (lis.py).几年前,我介绍过如何使用Java编写一个Scheme解释器,同时我还使用Common Lisp语言编写过一个版本.这一次,我的目的是尽可能简单明了地演示一下Alan Kay所说的"软件的麦克斯韦方程组" (Maxwell's Equations of Software). Lispy支持的Scheme

用Python编写一个简单的俄罗斯方块游戏的教程_python

俄罗斯方块游戏,使用Python实现,总共有350+行代码,实现了俄罗斯方块游戏的基本功能,同时会记录所花费时间,消去的总行数,所得的总分,还包括一个排行榜,可以查看最高记录. 排行榜中包含一系列的统计功能,如单位时间消去的行数,单位时间得分等.   附源码:     from Tkinter import * from tkMessageBox import * import random import time #俄罗斯方块界面的高度 HEIGHT = 18 #俄罗斯方块界面的宽度 WIDT

使用Python编写一个简单的tic-tac-toe游戏的教程_python

 这个教程,我们将展示如何用python创建一个井字游戏. 其中我们将使用函数.数组.if条件语句.while循环语句和错误捕获等. 首先我们需要创建两个函数,第一个函数用来显示游戏板:   def print_board(): for i in range(0,3): for j in range(0,3): print map[2-i][j], if j != 2: print "|", print "" 这我们使用两个for循环来遍历map,该map是一个包含

用Python进行一些简单的自然语言处理的教程_python

本月的每月挑战会主题是NLP,我们会在本文帮你开启一种可能:使用pandas和python的自然语言工具包分析你Gmail邮箱中的内容. NLP-风格的项目充满无限可能:     情感分析是对诸如在线评论.社交媒体等情感内容的测度.举例来说,关于某个话题的tweets趋向于正面还是负面的意见?一个新闻网站涵盖的主题,是使用了更正面/负面的词语,还是经常与某些情绪相关的词语?这个"正面"的Yelp点评不是很讽刺么?(祝最后去的那位好运!)     分析语言在文学中的使用,进而衡量词汇或者

《Python语言程序设计》——2.2 编写一个简单的程序

2.2 编写一个简单的程序 关键点:编写一个涉及设计解决问题的策略的程序,然后使用程序设计语言实现这些策略.首先,让我们来看一个计算圆面积的简单问题.我们该如何编写程序来解决这个问题呢?编写程序涉及如何设计算法以及如何将算法翻译成程序设计指令或代码.当你编写代码时--即你在编写程序时--你就将一个算法翻译成一段程序.算法描述的是如何通过列出要进行的动作和这些动作的执行顺序来解决一个问题.算法可以帮助程序员在使用程序设计语言编程之前做一个规划.算法可以用自然语言或伪代码(即自然语言与某些程序设计代

用Python编写一个基于终端的实现翻译的脚本

  用Python编写一个基于终端的实现翻译的脚本        为什么写这个程序,为什么不给这个程序配备gui?原因很简单,因为我是一个命令行控,Linux习惯了不习惯了鼠标,总觉得点着不如敲命令快,各位在看这篇文章就说明和本人有相同的爱好.这个用python写的翻译工具是通过google来实现的,由于google返回的数据不是很规范(或者说我没有找到规律),现在前三项能正常显示(源词,翻译结果,和汉语拼音).下面的词性和其他释义可能不同,见谅,望大神可以指点下小弟和帮小弟完善,这里赶紧不尽.

使用Python编写一个最基础的代码解释器的要点解析_python

一直以来都对编译器和解析器有着很大的兴趣,也很清楚一个编译器的概念和整体的框架,但是对于细节部分却不是很了解.我们编写的程序源代码实际上就是一串字符序列,编译器或者解释器可以直接理解并执行这个字符序列,这看起来实在是太奇妙了.本文会用Python实现一个简单的解析器,用于解释一种小的列表操作语言(类似于python的list).其实编译器.解释器并不神秘,只要对基本的理论理解之后,实现起来也比较简单(当然,一个产品级的编译器或解释器还是很复杂的). 这种列表语言支持的操作: veca = [1,

中文字符-如何用C语言编写一个简单的输入法程序,要求可以输入汉字。

问题描述 如何用C语言编写一个简单的输入法程序,要求可以输入汉字. 不太清楚汉字在计算机中是如何存储的,想知道例如微软的智能ABC以及搜狗输入法是怎样实现拼音拼写下的汉字输入. 解决方案 首先要有一个汉字的编码库,比如GB2312编写的是拼音输入法的话,还要建立一个拼音与汉字对应的数据库然后根据用户输入的拼音,提示出对应的汉字(汉字的优先顺序由数据库决定,同时还可以学习该用户的使用习惯)如果输入法还支持智能联想输入的话,还要加入词库(也有优先级),这样可以根据前一个字来推断出下一个可能的字 解决

用Python实现一个简单的多线程TCP服务器的教程

  这篇文章主要介绍了用Python实现一个简单的多线程TCP服务器的教程,示例的运行环境为Windows操作系统,需要的朋友可以参考下 最近看<python核心编程>,书中实现了一个简单的1对1的TCPserver,但是在实际使用中1对1的形势明显是不行的,所以研究了一下如何在server端通过启动不同的线程(进程)来实现每个链接一个线程. 其实python在类的设计上已经考虑到了这一方面的需求,我们只要在自己的server上继承一下SocketServer.BaseRequestHandl