Last active
December 4, 2015 00:42
-
-
Save micoli/27e4d724b6dc9b594421 to your computer and use it in GitHub Desktop.
fuse memory fs stored in xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
#sudo pip install lxml | |
from collections import defaultdict | |
from errno import ENOENT | |
from stat import S_IFDIR, S_IFLNK, S_IFREG | |
from sys import argv, exit,_getframe | |
from time import time | |
from datetime import datetime, timedelta | |
from fuse import FUSE, FuseOSError, Operations, LoggingMixIn | |
from lxml import etree | |
from copy import deepcopy | |
import logging | |
import json | |
import os | |
import base64 | |
import binascii | |
import collections | |
import re | |
if not hasattr(__builtins__, 'bytes'): | |
bytes = str | |
class inode(): | |
def __init__(self,node): | |
self.node = node | |
def set_text(self,value): | |
self.node.text = etree.CDATA(base64.encodestring(value)) | |
def getparent(self): | |
return inode(self.node.getparent()) | |
def remove(self,n): | |
return self.node.remove(n) | |
def get_text(self): | |
return base64.decodestring(self.node.text) | |
def create_sub(self,type,childName): | |
node = etree.SubElement(self.node,childName) | |
node.attrib['type']=type | |
return inode(node) | |
def get_attrs(self): | |
attrs = deepcopy(self.node.attrib) | |
for k in attrs: | |
attrs[k]=self.get_attr(k) | |
return attrs | |
def set_attr(self,attr,value): | |
if(attr=='attrs'): | |
self.node.attrib[attr]=json.dumps(value) | |
else: | |
self.node.attrib[attr]=repr(value) | |
def get_attr(self,attr): | |
if(attr=='st_ctime') or (attr=='st_mtime' ) or (attr=='st_atime'): | |
return float(self.node.attrib[attr]) | |
elif(attr=='type'): | |
return self.node.attrib[attr] | |
else: | |
if(attr=='attrs'): | |
if (attr not in self.node.attrib) or (self.node.attrib[attr] is None) or (self.node.attrib[attr]==""): | |
return {} | |
else: | |
try: | |
return json.loads(self.node.attrib['attrs']) | |
except Exception as e: | |
print("error ["+self.node.attrib['attrs']+']'+repr(e)) | |
return {} | |
else: | |
return int(self.node.attrib[attr]) | |
class XmlFS(LoggingMixIn, Operations): | |
'Memory xml filesystem.' | |
def __init__(self,xmlFile): | |
self.root = etree.Element("root") | |
self.xmlFile = xmlFile | |
self.fd = 0 | |
now = time() | |
if(os.path.exists(xmlFile)): | |
parser = etree.XMLParser() | |
for line in open(xmlFile): | |
parser.feed(line) | |
self.root = parser.close() | |
self.fs = self.root[0] | |
self.fd = int(self.root.xpath('count(//*)'))+1 | |
else: | |
self.fs = etree.SubElement(self.root,'fs') | |
_inode=inode(self.fs) | |
_inode.set_attr('st_mode' ,S_IFDIR | 0755) | |
_inode.set_attr('st_ctime',now) | |
_inode.set_attr('st_mtime',now) | |
_inode.set_attr('st_atime',now) | |
_inode.set_attr('st_nlink',2) | |
_inode.set_attr('root',1) | |
def get_node(self,path): | |
if(path=="/"): | |
return inode(self.fs) | |
node = self.root.find('fs'+path) | |
if node is None: | |
raise FuseOSError(ENOENT) | |
return inode(node) | |
def get_node_name(self,path): | |
a= ('fs'+path).split('/') | |
logger.debug(a) | |
return a.pop(-1) | |
def get_node_parent(self,path): | |
a = ('fs'+path).split('/') | |
a.pop(-1) | |
return inode(self.root.find('/'.join(a))) | |
def dumptree(self): | |
logger.debug(etree.tostring(self.root, pretty_print=True)) | |
def destroy(self,path): | |
self.save() | |
logger.info('clean exit, saved') | |
def save(self): | |
f = open(self.xmlFile, 'w') | |
f.write(etree.tostring(self.root, pretty_print=True)) | |
f.close() | |
####################### fs | |
def statfs(self, path): | |
return dict(f_bsize=512, f_blocks=4096, f_bavail=2048) | |
####################### attribs | |
def chmod(self, path, mode): | |
_inode = self.get_node(path) | |
val = _inode.get_attr('st_mode') | |
val &= 0770000 | |
val |= mode | |
_inode.set_attr('st_mode',val) | |
return 0 | |
def chown(self, path, uid, gid): | |
_inode = self.get_node(path) | |
_inode.set_attr('st_uid',uid) | |
_inode.set_attr('st_gid',gid) | |
def getattr(self, path, fh=None): | |
return self.get_node(path).get_attrs() | |
def utimens(self, path, times=None): | |
now = time() | |
atime, mtime = times if times else (now, now) | |
_node = self.get_node(path) | |
_node.set_attr('st_atime', atime) | |
_node.set_attr('st_mtime', mtime) | |
####################### xattrs | |
def getxattr(self, path, name, position=0): | |
logger.debug("get xattr "+path+" "+name) | |
_node = self.get_node(path) | |
try: | |
return _node.get_attr('attrs')[name] | |
except KeyError: | |
return '' # Should return ENOATTR | |
def listxattr(self, path): | |
return self.get_node(path).get_attr('attrs').keys() | |
def removexattr(self, path, name): | |
_node = self.get_node(path) | |
attrs = _node.get_attr('attrs') | |
try: | |
del attrs[name] | |
_node.set_attr('attrs',attrs) | |
except KeyError: | |
pass # Should return ENOATTR | |
def setxattr(self, path, name, value, options, position=0): | |
# Ignore options | |
_node = self.get_node(path) | |
attrs = _node.get_attr('attrs') | |
attrs[name] = value | |
_node.set_attr('attrs',attrs) | |
####################### files | |
def create(self, path, mode): | |
if(path=="/--save"): | |
self.save() | |
return False | |
_parent = self.get_node_parent(path) | |
_node = _parent.create_sub('file',self.get_node_name(path)) | |
_node.set_attr('st_mode' ,S_IFREG | mode) | |
_node.set_attr('st_size' ,0) | |
_node.set_attr('st_ctime',time()) | |
_node.set_attr('st_mtime',time()) | |
_node.set_attr('st_atime',time()) | |
_node.set_attr('st_nlink',1) | |
_node.set_text("") | |
self.fd += 1 | |
return self.fd | |
def open(self, path, flags): | |
self.fd += 1 | |
return self.fd | |
def read(self, path, size, offset, fh): | |
return self.get_node(path).get_text()[offset:offset + size] | |
def write(self, path, data, offset, fh): | |
_node = self.get_node(path) | |
_node.set_text(_node.get_text()[:offset] + data) | |
_node.set_attr('st_size',len(_node.get_text())) | |
return len(data) | |
def truncate(self, path, length, fh=None): | |
_node = self.get_node(path) | |
_node.set_text(_node.get_text()[:length]) | |
_node.set_attr('st_size',length) | |
####################### paths | |
def readdir(self, path, fh): | |
_node = self.get_node(path) | |
return ['.', '..'] + [child.tag for child in _node.node if child.tag != '/'] | |
def rmdir(self, path): | |
_inode = self.get_node(path) | |
_parent = _inode.getparent() | |
_parent.remove(_inode.node) | |
_parent.set_attr('st_nlink',_parent.get_attr('st_nlink')-1) | |
def mkdir(self, path, mode): | |
_parent = self.get_node_parent(path) | |
_parent.set_attr('st_nlink',_parent.get_attr('st_nlink')+1) | |
_node = _parent.create_sub('path',self.get_node_name(path)) | |
_node.set_attr('st_ctime',time()) | |
_node.set_attr('st_mtime',time()) | |
_node.set_attr('st_atime',time()) | |
_node.set_attr('st_mode' ,S_IFDIR | mode) | |
_node.set_attr('st_size' ,0) | |
_node.set_attr('st_nlink',2) | |
####################### paths | |
def rename(self, old, new): | |
print "rename to do" | |
#self.files[new] = self.files.pop(old) | |
def symlink(self, target, source): | |
_parent = self.get_node_parent(path) | |
_node = _parent.create_sub('symlink',self.get_node_name(path)) | |
_node.set_attr('st_mode', (S_IFLNK | 0777)) | |
_node.set_attr('st_nlink', 1) | |
_node.set_attr('st_size', len(source)) | |
_node.set_text(source) | |
def readlink(self, path): | |
return self.get_node(path).get_text() | |
def unlink(self, path): | |
_inode = self.get_node(path) | |
_parent = _inode.getparent() | |
_parent.remove(_inode.node) | |
_parent.set_attr('st_nlink',_parent.get_attr('st_nlink')-1) | |
if __name__ == '__main__': | |
if len(argv) != 3: | |
print('usage: %s <mountpoint> <xmlfile>' % argv[0]) | |
exit(1) | |
logging.getLogger().setLevel(logging.DEBUG) | |
logging.basicConfig() | |
logger = logging.getLogger() | |
xmlFS = FUSE(XmlFS(argv[2]), argv[1], foreground=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment