
聰明的讀者可能已經意識到了我接下來要做的事情:我決定用python寫一個加密檔案系統層!它與encfs會非常相似,但也有一些重要的差別:
它預設以反向模式運作,接收正常的檔案并且暴露一個被加密的目錄。任何備份程式會發現(并且備份)這些加密的目錄,不需要任何其它的存儲。
它也能接受由一個目錄清單組成的配置檔案,并且在挂載點将這些目錄暴露出來。這樣的話,所有的備份腳本就需要将挂載點備份,各種不同的目錄會立刻得以備份。
它會更偏重于備份,而不是加密存儲。寫起來應該會挺有意思的。
<a target="_blank"></a>
寫這個腳本的第一步是寫出一個純粹的傳遞式的檔案系統。它僅僅是接受一個目錄,并在挂載點将其暴露出來,確定任何在挂載點的修改都會鏡像到源資料中。
fusepy 要求你寫一個類,裡面定義了各種作業系統級别的方法。你可以選擇定義那些你的檔案系統想要支援的方法,其他的可以暫時不予定義,但是我需要定義全部的方法,因為我的檔案系統是一個傳遞式的檔案系統,它應該表現的與原有的檔案系統盡可能一緻。
寫這段代碼會非常簡單有趣,因為大部分的方法隻是對os子產品的一些簡單封裝(确實,你可以直接給它們指派,比如 open=os.open 等等,但是我的子產品需要一些路徑擴充)。不幸的是,fuse-python有一個bug(據我所知)是當打開和讀檔案的時候,它無法将檔案句柄回傳給檔案系統。因而我的腳本不知道某個應用執行讀寫操作時對應的是哪個檔案句柄,進而導緻了失敗。隻需要對fusepy做極少的改動,它就可以很好地運作。它隻有一個檔案,是以你可以把它直接放到你的工程裡。
代碼
在這裡,我很樂意給出這段代碼,當你打算自己實作檔案系統的時候可以拿來參考。這段代碼提供了一個很好的起點,你可以直接把這個類複制到你的工程中并且根據需要重寫裡面的一些方法。
接下來是真正的代碼了:
<code>#!/usr/bin/env python</code>
<code></code>
<code>from __future__ import with_statement</code>
<code>import os</code>
<code>import sys</code>
<code>import errno</code>
<code>from fuse import fuse, fuseoserror, operations</code>
<code>class passthrough(operations):</code>
<code>def __init__(self, root):</code>
<code>self.root = root</code>
<code># helpers</code>
<code># =======</code>
<code>def _full_path(self, partial):</code>
<code>if partial.startswith("/"):</code>
<code>partial = partial[1:]</code>
<code>path = os.path.join(self.root, partial)</code>
<code>return path</code>
<code># filesystem methods</code>
<code># ==================</code>
<code>def access(self, path, mode):</code>
<code>full_path = self._full_path(path)</code>
<code>if not os.access(full_path, mode):</code>
<code>raise fuseoserror(errno.eacces)</code>
<code>def chmod(self, path, mode):</code>
<code>return os.chmod(full_path, mode)</code>
<code>def chown(self, path, uid, gid):</code>
<code>return os.chown(full_path, uid, gid)</code>
<code>def getattr(self, path, fh=none):</code>
<code>st = os.lstat(full_path)</code>
<code>return dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime',</code>
<code>'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid'))</code>
<code>def readdir(self, path, fh):</code>
<code>dirents = ['.', '..']</code>
<code>if os.path.isdir(full_path):</code>
<code>dirents.extend(os.listdir(full_path))</code>
<code>for r in dirents:</code>
<code>yield r</code>
<code>def readlink(self, path):</code>
<code>pathname = os.readlink(self._full_path(path))</code>
<code>if pathname.startswith("/"):</code>
<code># path name is absolute, sanitize it.</code>
<code>return os.path.relpath(pathname, self.root)</code>
<code>else:</code>
<code>return pathname</code>
<code>def mknod(self, path, mode, dev):</code>
<code>return os.mknod(self._full_path(path), mode, dev)</code>
<code>def rmdir(self, path):</code>
<code>return os.rmdir(full_path)</code>
<code>def mkdir(self, path, mode):</code>
<code>return os.mkdir(self._full_path(path), mode)</code>
<code>def statfs(self, path):</code>
<code>stv = os.statvfs(full_path)</code>
<code>return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree',</code>
<code>'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag',</code>
<code>'f_frsize', 'f_namemax'))</code>
<code>def unlink(self, path):</code>
<code>return os.unlink(self._full_path(path))</code>
<code>def symlink(self, target, name):</code>
<code>return os.symlink(self._full_path(target), self._full_path(name))</code>
<code>def rename(self, old, new):</code>
<code>return os.rename(self._full_path(old), self._full_path(new))</code>
<code>def link(self, target, name):</code>
<code>return os.link(self._full_path(target), self._full_path(name))</code>
<code>def utimens(self, path, times=none):</code>
<code>return os.utime(self._full_path(path), times)</code>
<code># file methods</code>
<code># ============</code>
<code>def open(self, path, flags):</code>
<code>return os.open(full_path, flags)</code>
<code>def create(self, path, mode, fi=none):</code>
<code>return os.open(full_path, os.o_wronly | os.o_creat, mode)</code>
<code>def read(self, path, length, offset, fh):</code>
<code>os.lseek(fh, offset, os.seek_set)</code>
<code>return os.read(fh, length)</code>
<code>def write(self, path, buf, offset, fh):</code>
<code>return os.write(fh, buf)</code>
<code>def truncate(self, path, length, fh=none):</code>
<code>with open(full_path, 'r+') as f:</code>
<code>f.truncate(length)</code>
<code>def flush(self, path, fh):</code>
<code>return os.fsync(fh)</code>
<code>def release(self, path, fh):</code>
<code>return os.close(fh)</code>
<code>def fsync(self, path, fdatasync, fh):</code>
<code>return self.flush(path, fh)</code>
<code>def main(mountpoint, root):</code>
<code>fuse(passthrough(root), mountpoint, foreground=true)</code>
<code>if __name__ == '__main__':</code>
<code>main(sys.argv[2], sys.argv[1])</code>
如果你想要運作它,隻需要安裝fusepy,把這段代碼放進一個檔案(比如myfuse.py)然後運作 python myfuse.py /你的目錄 /挂載點目錄 。你會發現 “/你的目錄” 路徑下的所有檔案都跑到”/挂載點目錄”,還能像用原生檔案系統一樣操作它們。
總的來說,我并不認為寫一個檔案系統就這麼簡單。接下來要做的是在腳本裡添加加密/解密的功能,以及一些幫助類的方法。我的目标是能讓它除了有更好的擴充性(因為是用python寫的),以及包含一些針對備份檔案的額外特性外,可以成為一個encfs的完全替代品。
原文釋出時間為:2013-12-03
本文來自雲栖社群合作夥伴“linux中國”