#!/usr/bin/env python # -*- coding: UTF-8 -*- # afiolzofs : Support to mount afio archives with lzop compression. # Copyright (c) 2010, Yoshiteru Ishimaru # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the Yoshiteru Ishimaru nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # 0.0.23 (23 Dec 2010) # atime at readonly # fix write,trunc # fix mount option # 0.0.22 (22 Dec 2010) # error message of 'cannot find fuse.py' # 0.0.21 (21 Dec 2010) # options # 0.0.20 (19 Dec 2010) # Ramfs,AfioLzofs # 0.0.19 (18 Dec 2010) # bug fix # 0.0.18 (18 Dec 2010) # fix open,opendir,use_ino # inode dict -> class # 0.0.17 (17 Dec 2010) # fix rm # fix open # 0.0.16 (12 Dec 2010) # fix some error # 0.0.15 (08 Dec 2010) # Ramfs Class and Afiolzofs Class # 0.0.14 (08 Dec 2010) # start to separate Temp Ramfs Block and AFIO access Block # 0.0.13 (07 Dec 2010) # _get_inode # read_afio_symlink no read symlinkpath at first # read_afio_item_offsetsize no read all and store for non compressed data at only read. # 0.0.12 (06 Dec 2010) # fix some error # 0.0.11 (05 Dec 2010) # x permission # 0.0.10 (04 Dec 2010) # r,w permission # 0.0.9 (03 Dec 2010) # test implementation for permission to dirread # 0.0.8 (02 Dec 2010) # fix some error # 0.0.7 (01 Dec 2010) # fix some of the atime,mtime,ctime # fix some error code # 0.0.6 (30 Nov 2010) # fix some of the atime,mtime,ctime # use get_fuse_context # 0.0.5 (29 Nov 2010) # list many item shuld be fix. # fix some of them. # 0.0.4 (27 Nov 2010) # support extended ASCII format # fix "unlink error" # fix "other magic number" # not to use defaultdict # fix "rename error" # change inode structure # 0.0.3 (26 Nov 2010) # fix "cannot mount XXXXXX.afio.lzo" # 0.0.2 (26 Nov 2010) # BUG fix "REG FILE" # 0.0.1 (21 Nov 2010) # New release # This program is based on fusepy. And it is started by coping the 'fuse.py' and 'memory.py' # # Copyright of the fuse.py and memory.py # # Copyright (c) 2008 Giorgos Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISCHR, S_ISBLK # function from stat import S_IMODE # function (or 0007777) get bitmask to be able to change with os.chmod() from stat import S_IFMT # function (or 0170000) get bitmask for the file type bitfields from stat import S_IFSOCK # 0140000 socket from stat import S_IFLNK # 0120000 symbolic link from stat import S_IFREG # 0100000 regular file from stat import S_IFBLK # 0060000 block device from stat import S_IFDIR # 0040000 directory from stat import S_IFCHR # 0020000 character device from stat import S_IFIFO # 0010000 FIFO from stat import S_ISUID # 0004000 set UID bit from stat import S_ISGID # 0002000 set-group-ID bit (see below) from stat import S_ISVTX # 0001000 sticky bit (see below) from stat import S_IRWXU # 00700 mask for file owner permissions from stat import S_IRUSR # 00400 owner has read permission from stat import S_IWUSR # 00200 owner has write permission from stat import S_IXUSR # 00100 owner has execute permission from stat import S_IRWXG # 00070 mask for group permissions from stat import S_IRGRP # 00040 group has read permission from stat import S_IWGRP # 00020 group has write permission from stat import S_IXGRP # 00010 group has execute permission from stat import S_IRWXO # 00007 mask for permissions for others (not in group) from stat import S_IROTH # 00004 others have read permission from stat import S_IWOTH # 00002 others have write permission from stat import S_IXOTH # 00001 others have execute permission from sys import argv, exit from time import time,mktime ,strptime import subprocess import pwd import grp import os from errno import * try: from fuse import FUSE, FuseOSError, Operations, LoggingMixIn ,fuse_get_context except: print 'I cannot find fuse.py.' print 'Please download fuse.py from http://code.google.com/p/fusepy.' print 'And put it in the same dir.' exit() """ ENOSYS (Function not implemented) EPERM (Operation not permitted) ENOENT (No such file or directory) EIO (I/O error) ENXIO (No such device or address) EBADF (Bad file number) ENOMEM (Out of memory) EACCES (Permission denied) EFAULT (Bad address) ENOTBLK (Block device required) EBUSY (Device or resource busy) EEXIST (File exists) EXDEV (Cross-device link) ENODEV (No such device) ENOTDIR (Not a directory) EISDIR (Is a directory) ENOSPC (No space left on device) ESPIPE (Illegal seek) EROFS (Read-only file system) EPIPE (Broken pipe) ENAMETOOLONG (File name too long) ENOTEMPTY (Directory not empty) """ ################################################################################################################# # # # Inode Class # # # ################################################################################################################# class Inode(): def __init__(self,time,context): self.st_nlink=0 self.st_size=0 # nlink and size self.st_ctime=time self.st_mtime=time self.st_atime=time self.st_uid=context[0] self.st_gid=context[1] self.st_mode=(S_IFDIR | 0755) ### Basic inode function called from Operations ### def set_mode(self,mode): self.st_mode=mode def get_mode(self): return self.st_mode def set_nlink(self,n=1): self.st_nlink=n def inc_nlink(self): self.st_nlink+=1 def dec_nlink(self): self.st_nlink-=1 def set_dentry(self,dirname,inode): self.i_data[dirname]=inode # set or replace dir entry def get_dentry(self,dirname): return self.i_data[dirname] # get dir entry def pop_dentry(self,dirname): return self.i_data.pop(dirname) # delete or move dir entry def ls_dentry(self): return self.i_data.keys() # list dir entry def len_dentry(self): return len(self.i_data) # number of the dir entry (for delete) def update_atime(self,time): self.st_atime=time def update_mtime(self,time): self.st_mtime=time def update_ctime(self,time): self.st_ctime=time def set_rdev(self,n=0): self.st_rdev=n def set_gid(self,gid): self.st_gid=gid def get_gid(self): return self.st_gid def set_uid(self,uid): self.st_uid=uid def get_uid(self): return self.st_uid def items(self): # call as 'getattr' from fuse.py. inode=None yield ('st_ino',id(self)) key='st_size';val=getattr(self,'i_data',None) if val==None: if inode==None:inode=self.get_default_inode() val=inode.get(key,0) else:val=len(val) yield (key,val) for key in ('st_nlink','st_mode','st_uid','st_gid','st_rdev','st_atime','st_ctime','st_mtime',): val=getattr(self,key,None) if val==None: if inode==None:inode=self.get_default_inode() val=inode.get(key,0) yield (key,val) ### for xattr ### def __getitem__(self,name): # x=getattr(self,name,None) if x != None: return x def __setitem__(self,name,value): if name in self.keys():setattr(self,name,value) def keys(self): # return ['st_dev','__pad1','__st_ino','st_mode','st_nlink', 'st_uid','st_gid','st_rdev','__pad2','st_size','st_blksize', 'st_blocks','st_atime','st_mtime','st_ctime','st_ino', 'i_data','xattr'] def get(self,name,default=None): return getattr(self,name,default) ### Simple FileSystem IO function ### def get_default_inode(self): return {} # if no attr is, use from this dict or 0. def get_inode_data(self): return self.i_data # return source path. call from read symlink,write,truncate, if i_data=None def set_inode_data(self,data=''): # call from read symlink,write,truncate, if i_data=None self.i_data=data; return len(data) def get_inode_data_offsetsize(self,offset,size): # return read data. call from read, if i_data=None return self.i_data[offset:(offset + size)] def set_inode_data_offsetsize(self,data,offset): i_data=self.get_inode_data() self.i_data = i_data[0:offset]+data+i_data[offset+len(data):] return len(data) # return write length def trunc_inode_data_offsetsize(self,offset): self.i_data=self.get_inode_data_offsetsize(0,offset) ################################################################################################################# # # # FUSE Operation Ramfs Class # # # ################################################################################################################# class Ramfs(Operations): # for normal use """Temporaly Read Write Memory filesystem. No Saving files. """ def __init__(self,mountpoint): self.mountpoint=mountpoint # abspath of the mount point self.fd = 0 # self.inode=Inode(time(),(os.getuid(),os.getgid())) # root inode of the file system self.inode.set_mode(S_IFDIR | 0755) self.inode.set_nlink(2) self.inode.set_inode_data({}) ######## Ramfs function (Permission check) ######## def _is_inode_R_OK(self,inode,context,uname): mode=inode.get_mode();uid=inode.get_uid() if not (mode & S_IROTH) and not((mode & S_IRUSR) and (uid == context[0])): if (mode & S_IRGRP): gid=inode_get_gid(); if not(uname in grp.getgrgid(gid).gr_mem): if context[1] != gid: raise FuseOSError(EACCES) # (Permission denied) else: raise FuseOSError(EACCES) # (Permission denied) def _is_inode_W_OK(self,inode,context,uname): mode=inode.get_mode();uid=inode.get_uid() if not (mode & S_IWOTH) and not((mode & S_IWUSR) and (uid == context[0])): if (mode & S_IWGRP): gid=inode_get_gid(); if not(uname in grp.getgrgid(gid).gr_mem): if context[1] != gid: raise FuseOSError(EACCES) # (Permission denied) else: raise FuseOSError(EACCES) # (Permission denied) def _is_inode_X_OK(self,inode,context,uname): mode=inode.get_mode();uid=inode.get_uid() if not (mode & S_IXOTH) and not((mode & S_IXUSR) and (uid == context[0])): if (mode & S_IXGRP): gid=inode_get_gid(); if not(uname in grp.getgrgid(gid).gr_mem): if context[1] != gid: raise FuseOSError(EACCES) # (Permission denied) else: raise FuseOSError(EACCES) # (Permission denied) ######## Ramfs function (Get inode with Permission check) ######## def _get_inode_nopermisson(self,path): ###### Get inode without any permission checking ###### if path=="/": # if path is root return self.inode # return root inode of the file system dentry=os.path.split(path) try: inode_dir=self._get_inode_nopermisson(dentry[0]) inode=inode_dir.get_dentry(dentry[1]) # if 'no name in dir list' or 'not dir' -> error return inode except: raise FuseOSError(ENOENT) # (No such file or directory) def _get_inode(self,path,context,uname): ###### Get inode from path,context,uname ####### if path=="/": # if path is root of the file system inode=self.inode else: # not root of the file system dentry=os.path.split(path) inode_dir=self._get_inode(dentry[0],context,uname) self._is_inode_X_OK(inode_dir,context,uname) # dir access permission check try:inode=inode_dir.get_dentry(dentry[1]) # if 'no name in dir list' or 'not dir' -> error except:raise FuseOSError(ENOENT) # (No such file or directory) return inode def _get_inode_for_access(self,path): ###### Get inode for Access ###### context=fuse_get_context() # get system call uid and gid uname=pwd.getpwuid(context[0]).pw_name # get system call uname return self._get_inode(path,context,uname),context,uname def _get_inode_for_read(self,path): ###### Get inode for Read ###### inode,context,uname=self._get_inode_for_access(path) # access permission check self._is_inode_R_OK(inode,context,uname) # read permission check return inode def _get_inode_for_write(self,path): ###### Get inode for Write ###### inode,context,uname=self._get_inode_for_access(path) # access permission check self._is_inode_W_OK(inode,context,uname) # write permission check return inode def _get_dir_inode_for_create(self,path): ###### Get dir inode for Create ###### dentry=os.path.split(path) # split to dirpath and filename inode,context,uname=self._get_inode_for_access(dentry[0]) # dir access permission check self._is_inode_W_OK(inode,context,uname) # dir write permission check return inode,dentry[1] # return inode_dir and filename def _get_inode_for_delete(self,path): ###### Get inode for Delete ###### dentry=os.path.split(path) # split to dirpath and filename inode,context,uname=self._get_inode_for_access(dentry[0]) # dir access permission check self._is_inode_W_OK(inode,context,uname) # dir write permission check now=time() inode_file=inode.get_dentry(dentry[1]) return inode_file,inode,dentry[1],now # return inode_file, inode_dir, filename and now def _get_inode_for_create(self,path): ###### Get default inode for Create ###### dentry=os.path.split(path) # split to dirpath and filename inode,context,uname=self._get_inode_for_access(dentry[0]) # dir access permission check self._is_inode_W_OK(inode,context,uname) # dir write permission check now=time() inode_file = Inode(now,context) return inode_file,inode,dentry[1],now # return inode_file, inode_dir, filename and now ######## Ramfs function (inode Operation) ######## def _update_atime(self,inode,now): inode.update_atime(now) # write file data -> change mtime def _add_inode_nopermisson(self,path,inode,dirinode=False): # add inode without any permission checking dentry=os.path.split(path) inode_dir=self._get_inode_nopermisson(dentry[0]) inode_dir.set_dentry(dentry[1],inode) if dirinode:inode_dir.inc_nlink() def _add_inode_as_dir(self,inode_dir,inode,filename,now): # inode_dir.set_dentry(filename,inode) inode_dir.update_mtime(now) # write to dir list -> change mtime inode_dir.inc_nlink() inode_dir.update_ctime(now) # change nlink -> change ctime inode.inc_nlink() inode.update_ctime(now) # change nlink -> change ctime def _add_inode_as_file(self,inode_dir,inode,filename,now): # inode_dir.set_dentry(filename,inode) inode_dir.update_mtime(now) # write to dir list -> change mtime inode_dir.update_ctime(now) # ??? inode.inc_nlink() inode.update_ctime(now) # change nlink -> change ctime def _removefile_inode(self,inode,inode_dir,filename,now): # inode_dir.pop_dentry(filename) # fix me ! no name ? inode_dir.update_mtime(now) # change dir list -> change mtime inode_dir.update_ctime(now) # change mtime -> change ctime inode.dec_nlink() inode.update_ctime(now) # change nlink -> change ctime def _removedir_inode(self,inode,inode_dir,filename,now): # if filename == '/': # Shuld be check root? ?? raise FuseOSError(EPERM) # Operation not permitted elif inode.len_dentry()>=1: # raise FuseOSError(ENOTEMPTY) # Directory not empty else: inode_dir.pop_dentry(filename) inode_dir.update_mtime(now) # change dir list -> change mtime inode_dir.dec_nlink() inode_dir.update_ctime(now) # change nlink -> change ctime # not for rmdir, but for rename. inode.dec_nlink() inode.update_ctime(now) # change nlink -> change ctime inode.update_mtime(now) # change dir list '..' -> change mtime ######## FUSE Operating function (Start File System) ######## def init(self, path): """Called on filesystem initialization. Path is always / Use it instead of __init__ if you start threads on initialization.""" pass def statfs(self, path): return dict(f_bsize=512, f_blocks=4096, f_bavail=2048) def destroy(self, path): """Called on filesystem destruction. Path is always /""" pass ######## FUSE Operating function (Attr) ######## def access(self, path, amode): inode,context,uname=self._get_inode_for_access(path) # access permission check return 0 # ???? def getattr(self, path, fh=None): ###### getattr ###### inode,context,uname=self._get_inode_for_access(path) # access permission check return inode # return inode # fix me ! inode contain many other data. use xattr! def chmod(self, path, mode): ###### chmod ###### inode,context,uname=self._get_inode_for_access(path) # access permission check inode.set_mode( S_IFMT(inode.get_mode()) | S_IMODE(mode) ) # see S_IFMT,S_IMODE now=time() inode.update_ctime(now) # change inode data -> change ctime return 0 # return 0 # ??? def chown(self, path, uid, gid): ###### chown ###### inode,context,uname=self._get_inode_for_access(path) # access permission check if uid != -1:inode.set_uid(uid) # set uid with check -1 if gid != -1:inode.set_gid(gid) # set gid with check -1 now=time() inode.update_ctime(now) # change inode data -> change ctime def utimens(self, path, times=None): ###### utime ###### inode,context,uname=self._get_inode_for_access(path) # access permission check now = time() atime, mtime = times if times else (now, now) # atime mtime can be changed by touch (utimes),but not ctime mode=inode.get_mode() inode.update_atime(atime) # In BSD symlink's atime,mtime,ctime is can changed with lutimes, cannot change read NG inode.update_mtime(mtime) # but not in Linux because no lutimes. can change if dir mode ok inode.update_ctime(now) # change atime,mtime -> change ctime ######## FUSE Operating function (XAttr) ######## def listxattr(self, path): inode,context,uname=self._get_inode_for_access(path) # access permission check attrs = inode.get('attrs', {}) return attrs.keys() def getxattr(self, path, name, position=0): inode,context,uname=self._get_inode_for_access(path) # access permission check attrs = inode.get('attrs', {}) try: return attrs[name] except KeyError: raise FuseOSError(ENOSYS) # Function not implemented def setxattr(self, path, name, value, options, position=0): # Ignore options inode,context,uname=self._get_inode_for_access(path) # access permission check attrs = inode.setdefault('attrs', {}) attrs[name] = value def removexattr(self, path, name): inode,context,uname=self._get_inode_for_access(path) # access permission check attrs = inode.get('attrs', {}) try:del attrs[name] except KeyError:raise FuseOSError(ENOSYS) # (Function not implemented) ######## FUSE Operating function (Read Dir) ######## def opendir(self, path): inode,context,uname=self._get_inode_for_access(path) # access permission check if not S_ISDIR(inode.get_mode()):raise FuseOSError(ENOSYS) # Function not implemented (Dir shuld be delete rmdir) self._is_inode_R_OK(inode,context,uname) """Returns a numerical file handle.""" return 0 def readdir(self, path, fh): ###### readdir ###### inode=self._get_inode_for_read(path) # read permission check if not S_ISDIR(inode.get_mode()):raise FuseOSError(ENOSYS) # Function not implemented dirnames=inode.ls_dentry() # read dir list dirnames.append('..') # append parent dir '..' dirnames.append('.') # append parent dir '.' now=time() inode.update_atime(now) # write file data -> change mtime return dirnames # return dir list def releasedir(self, path, fh): return 0 def fsyncdir(self, path, datasync, fh): return 0 ######## FUSE Operating function (Create or Remove Dir) ######## def mkdir(self, path, mode): ###### mkdir ###### inode,inode_dir,filename,now=self._get_inode_for_create(path) # write permission check inode.set_mode( S_IMODE(mode) | S_IFDIR ) # set mode (dir) inode.set_inode_data({}) inode.inc_nlink() # add nlink 1 self._add_inode_as_dir(inode_dir,inode,filename,now) # add inode as dir def rmdir(self, path): ###### rmdir ###### inode,inode_dir,filename,now=self._get_inode_for_delete(path) # write permission check if not S_ISDIR(inode.get_mode()):raise FuseOSError(ENOSYS) # Function not implemented (not Dir shuld be delete unlink) self._removedir_inode(inode,inode_dir,filename,now) # remove dir ######## FUSE Operating function (Create or Remove File) ######## def mknod(self, path, mode, dev): ###### mknod ###### mask=S_IFSOCK | S_IFBLK | S_IFCHR | S_IFIFO # ??? S_IFREG ? # fix me! if (mode & mask) == 0 :raise FuseOSError(EPERM) # (Operation not permitted) # inode,inode_dir,filename,now=self._get_inode_for_create(path) # write permission check inode.set_mode( S_IMODE(mode) | (mode & mask) ) # set node mode # fix me! inode_set_rdev=dev # set rdev # fix me! self._add_inode_as_file(inode_dir,inode,filename,now) # add inode as file def create(self, path, mode): ###### create ###### inode,inode_dir,filename,now=self._get_inode_for_create(path) # write permission check inode.set_mode( S_IFREG | S_IMODE(mode) ) # set reguler file mode inode.set_inode_data('') # set no data self._add_inode_as_file(inode_dir,inode,filename,now) # add inode as file self.fd += 1 return self.fd # return fd def unlink(self, path): ###### unlink ###### inode,inode_dir,filename,now=self._get_inode_for_delete(path) # write permission check if S_ISDIR(inode.get_mode()):raise FuseOSError(ENOSYS) # Function not implemented (Dir shuld be delete rmdir) self._removefile_inode(inode,inode_dir,filename,now) # remove file def rename(self, old, new): # change time shuld be changed with write, rename, truncate, chmod, link or chown inode,old_dir,old_name,now=self._get_inode_for_delete(old) # write permission check inode_dir,filename=self._get_dir_inode_for_create(new) # write permission check # If oldpath and newpath are existing hard links referring to the same # fix me! # file, then rename() does nothing, and returns a success status. */ # EINVAL The new pathname contained a path prefix of the old: # this should be checked by fuse */ # EISDIR newpath is an existing directory, but oldpath is not a directory. */ # ENOTEMPTY newpath is a non-empty directory */ # rt = do_check_empty_dir(e2fs, dest_ino); # ENOTDIR: oldpath is a directory, and newpath exists but is not a # directory */ # Shuld I remove existing "new" ? # d_dest_inode.i_mtime = d_dest_inode.i_ctime = src_inode->i_ctime if S_ISDIR(inode.get_mode()): self._add_inode_as_dir(inode_dir,inode,filename,now) # add dir link self._removedir_inode(inode,old_dir,old_name,now) # remove dir (Should be first ?) else: self._add_inode_as_file(inode_dir,inode,filename,now) # add file link self._removefile_inode(inode,old_dir,old_name,now) # remove file def link(self, target, source): ###### link ###### inode_dir,filename=self._get_dir_inode_for_create(target) # write permission check inode=self._get_inode_nopermisson(source) # fix me ! what permission should be use? now=time() # get time now if S_ISDIR(inode.get_mode()):raise FuseOSError(ENOSYS) # Function not implemented self._add_inode_as_file(inode_dir,inode,filename,now) # add inode as file ######## FUSE Operating function (Sym Link) ######## def readlink(self, path): ###### readlink ###### inode=self._get_inode_for_read(path) # read permission check if not S_ISLNK(inode.get_mode()):raise FuseOSError(EPERM) #(Operation not permitted) self._update_atime(inode,time()) # write file data -> change mtime return inode.get_inode_data() def symlink(self, target, source): ###### symlink ###### inode,inode_dir,filename,now=self._get_inode_for_create(target) # write permission check inode.set_mode(S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO ) # set symlink mode inode.set_inode_data(source) # set source path as data self._add_inode_as_file(inode_dir,inode,filename,now) # add inode as file ######## FUSE Operating function (Read or Write File) ######## def open(self, path, flags): inode,context,uname=self._get_inode_for_access(path) # access permission check if S_ISDIR(inode.get_mode()):raise FuseOSError(ENOSYS) # Function not implemented (Dir shuld be delete rmdir) if (flags & 3) == os.O_RDONLY: self._is_inode_R_OK(inode,context,uname) elif (flags & 3) == os.O_RDWR: self._is_inode_R_OK(inode,context,uname) self._is_inode_W_OK(inode,context,uname) elif (flags & 3)== os.O_WRONLY: self._is_inode_W_OK(inode,context,uname) #print " CREAT:%04x WRONLY:%04x RDONLY:%04x O_RDWR:%04x O_APPEND:%04x FLAGS:%04x" % (os.O_CREAT,os.O_WRONLY,os.O_RDONLY,os.O_RDWR,os.O_APPEND,flags) self.fd += 1 # any time shuld not be changed with only open return self.fd # shuld return file handle pointer. Can I use inode instead? Then Where shuld i save flags ? def read(self, path, size, offset, fh): ###### read ###### inode=self._get_inode_for_read(path) # read permission check if not S_ISREG(inode.get_mode()):raise FuseOSError(EPERM) #(Operation not permitted) self._update_atime(inode,time()) # write file data -> change mtime return inode.get_inode_data_offsetsize(offset,size) # return read data def write(self, path, data, offset, fh): ###### write ###### inode=self._get_inode_for_write(path) # write permission check if not S_ISREG(inode.get_mode()):raise FuseOSError(EPERM) #(Operation not permitted) now=time() inode.update_mtime(now) # write file data -> change mtime inode.update_ctime(now) # change size -> change ctime return inode.set_inode_data_offsetsize(data,offset) def truncate(self, path, length, fh=None): ###### truncate ###### inode=self._get_inode_for_write(path) # write permission check if not S_ISREG(inode.get_mode()):raise FuseOSError(EPERM) #(Operation not permitted) now=time() inode.update_mtime(now) # write file data -> change mtime inode.update_ctime(now) # change size -> change ctime inode.trunc_inode_data_offsetsize(length) def release(self, path, fh): return 0 def flush(self, path, fh): return 0 def fsync(self, path, datasync, fh): return 0 ################################################################################################################# # # # FUSE Operation Romfs Class # # # ################################################################################################################# class Romfs(Ramfs): """Read only Memory filesystem. """ def __init__(self,mountpoint): self.mountpoint=mountpoint # abspath of the mount point self.fd = 0 # self.inode=Inode(time(),(os.getuid(),os.getgid())) # root inode of the file system self.inode.set_mode(S_IFDIR | 0755) self.inode.set_nlink(2) self.inode.set_inode_data({}) def _update_atime(self,inode,now): pass ######## FUSE Operating function (Attr) ######## def access(self, path, amode): inode,context,uname=self._get_inode_for_access(path) # access permission check # fix me readonly filesystem ! return 0 # ???? def chmod(self, path, mode): ###### chmod ###### raise FuseOSError(EROFS) # (Read-only file system) def chown(self, path, uid, gid): ###### chown ###### raise FuseOSError(EROFS) # (Read-only file system) def utimens(self, path, times=None): ###### utime ###### raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (XAttr) ######## def setxattr(self, path, name, value, options, position=0): # Ignore options raise FuseOSError(EROFS) # (Read-only file system) def removexattr(self, path, name): raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (Create or Remove Dir) ######## def mkdir(self, path, mode): ###### mkdir ###### raise FuseOSError(EROFS) # (Read-only file system) def rmdir(self, path): ###### rmdir ###### raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (Create or Remove File) ######## def mknod(self, path, mode, dev): ###### mknod ###### raise FuseOSError(EROFS) # (Read-only file system) def create(self, path, mode): ###### create ###### raise FuseOSError(EROFS) # (Read-only file system) def unlink(self, path): ###### unlink ###### raise FuseOSError(EROFS) # (Read-only file system) def rename(self, old, new): raise FuseOSError(EROFS) # (Read-only file system) def link(self, target, source): ###### link ###### raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (Sym Link) ######## def symlink(self, target, source): ###### symlink ###### raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (Read or Write File) ######## def open(self, path, flags): inode,context,uname=self._get_inode_for_access(path) # access permission check if S_ISDIR(inode.get_mode()):raise FuseOSError(ENOSYS) # Function not implemented (Dir shuld be delete rmdir) if (flags & 3) == os.O_RDONLY: self._is_inode_R_OK(inode,context,uname) elif (flags & 3) == os.O_RDWR: raise FuseOSError(EROFS) # (Read-only file system) elif (flags & 3)== os.O_WRONLY: raise FuseOSError(EROFS) # (Read-only file system) #print " CREAT:%04x WRONLY:%04x RDONLY:%04x O_RDWR:%04x O_APPEND:%04x FLAGS:%04x" % (os.O_CREAT,os.O_WRONLY,os.O_RDONLY,os.O_RDWR,os.O_APPEND,flags) self.fd += 1 # any time shuld not be changed with only open return self.fd # shuld return file handle pointer. Can I use inode instead? Then Where shuld i save flags ? def write(self, path, data, offset, fh): ###### write ###### raise FuseOSError(EROFS) # (Read-only file system) def truncate(self, path, length, fh=None): ###### truncate ###### raise FuseOSError(EROFS) # (Read-only file system) ################################################################################################################# # # # AfioLzoFs Function # # # ################################################################################################################# def _filetype(data): pipe=subprocess.Popen("file -" ,shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE) pipe.stdin.write(data); pipe.stdin.close() s=pipe.stdout.read().split(); pipe.stdout.close() return " ".join(s[1:4]) def _lzop(data,compress=False): if compress: args=['lzop','-c9'] else: args=['lzop','-dc'] proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True, ) pid = os.fork() if pid==0: # child proc.stdout.close() proc.stdin.write(data) proc.stdin.flush() proc.stdin.close() os._exit(os.EX_OSERR) else: # parent proc.stdin.close() data=proc.stdout.read() proc.stdout.close() return data def _lzop_size(path,seek,size): if size>50:size=50 # read first 50 bytes f=open(path) f.seek(seek) top50=f.read(size) size=int("%02x%02x%02x%02x"%(ord(top50[38]),ord(top50[39]),ord(top50[40]),ord(top50[41])),16) f.close() return size ################################################################################################################# # # # Inode Afio Base Class # # # ################################################################################################################# class Inode_Afio_Base(Inode): ID=6 LEN=0 s1= "" s2= "" def __init__(self,seek=None,loader=None): self.loader=loader # save loader self.header_address=seek # save first address in the archive self.init_inode() def init_inode(self) : # call from __init__ afio_inode=self.get_afio_inode() leng=self.ID+self.LEN self.st_mode= afio_inode['st_mode'] self.st_nlink=afio_inode['st_nlink'] self.st_uid= afio_inode['st_uid'] self.st_gid= afio_inode['st_gid'] self.data_size=afio_inode['st_size'] hdr2=self.loader.read_data(self.header_address,leng) st_path= self.loader.read_data(self.header_address+leng,afio_inode['st_nlen']-1) self.data_addr= self.header_address+leng+afio_inode['st_nlen'] print self.s1 print self.s2 print "%s%s" % (hdr2,st_path) if S_ISDIR(self.st_mode): # DIR self.i_data={} # empty dir self.st_nlink=2 # nlink = 2 path='%s/%s' % (self.loader.target,st_path) self.loader.fs._add_inode_nopermisson(path,self,dirinode=True) # count up parent dir st_nlink elif S_ISLNK(self.st_mode): # SLINK if self.st_nlink>1:inode=self.loader._setnlink(self,afio_inode['st_ino']) # count up st_nlink, if inode is exist path='%s/%s' % (self.loader.target,st_path) self.loader.fs._add_inode_nopermisson(path,self) elif S_ISREG(self.st_mode): # REG FILE if ((afio_inode['st_rdev'] & 1)== 0) and (st_path[-2:] =='.z'): # '.z' and 'rdev & 1==0' mean compressed file in afio header st_path=st_path[:-2] # remove ".z" self.compress=self.loader.read_compress_format(self.data_addr,self.data_size) # if not lzop? path='%s/%s' % (self.loader.target,st_path) if self.st_nlink>1:inode=self.loader.fs._setnlink(self,afio_inode['st_ino']) self.loader.fs._add_inode_nopermisson(path,self) else: # Special File if self.st_nlink>1:inode=self.loader.fs._setnlink(self,afio_inode['st_ino']) self.loader.fs._add_inode_nopermisson(path,self) ### Simple FileSystem IO function ### def get_default_inode(self): # call from items(getatter) afio_inode=self.get_afio_inode() afio_inode['st_atime']=afio_inode['st_mtime'] afio_inode['st_ctime']=afio_inode['st_mtime'] if getattr(self,'compress',None):afio_inode['st_size']=self.compress[1](self.loader.abspath,self.data_addr,self.data_size) return afio_inode def get_inode_data(self): # call from read symlink,write,truncate, if i_data=None data=getattr(self,'i_data',None) if data==None: # return self.loader.read_data(self.data_addr,self.data_size) # get afio raw data compress=getattr(self,'compress',None) if compress!=None: data=self.loader.read_data(self.data_addr,self.data_size) # get afio raw data data=compress[0](data) self.i_data=data return data else: return self.loader.read_data(self.data_addr,self.data_size) else: return data # return source path def get_inode_data_offsetsize(self,offset,size): # call from read, if i_data=None filedata=getattr(self,'i_data',None) if filedata==None: compress=getattr(self,'compress',None) if compress!=None: data=self.loader.read_data(self.data_addr,self.data_size) # get afio raw data data=compress[0](data) self.i_data=data return data[offset:(offset + size)] else: return self.loader.read_data(self.data_addr+offset,min(self.data_size-offset,size)) else: return filedata[offset:(offset + size)] # return read data ################################################################################################################# # # # Inode Afio Class # # # ################################################################################################################# class Inode_Afio_070707(Inode_Afio_Base): # old ASCII magic number LEN=70 s1= "|23456|23456|23456|23456|23456|23456|23456|23456|23456789ab|23456|23456789ab|..." s2= "| hdr| dev| ino| mode| uid| gid|nlink| rdev| mtime|nmlen| size|pathname" def get_afio_inode(self): hdr2=self.loader.read_data(self.header_address+self.ID,self.LEN) inode=dict( st_dev= int(hdr2[0:6],8), # 6 st_ino= int(hdr2[6:12],8), # 6 st_mode= int(hdr2[12:18],8), # 6 st_uid= int(hdr2[18:24],8), # 6 st_gid= int(hdr2[24:30],8), # 6 st_nlink= int(hdr2[30:36],8), # 6 st_rdev= int(hdr2[36:42],8), # 6 st_mtime= int(hdr2[42:53],8), # 11 st_nlen= int(hdr2[53:59],8), # 6 st_size= int(hdr2[59:70],8), # 11 ) return inode class Inode_Afio_070717(Inode_Afio_Base): # extended ASCII magic number LEN=75 s1= "|23456|23456|23456789ab|23456|23456|23456|23456|23456|23456789ab|23456|23456789ab|..." s2= "| hdr| dev| ino| mode| uid| gid|nlink| rdev| mtime|nmlen| size|pathname" def get_afio_inode(self): hdr2=self.loader.read_data(self.header_address+self.ID,self.LEN) inode=dict( st_dev= int(hdr2[0:6],8), # 6 st_ino= int(hdr2[6:17],8), # 11 st_mode= int(hdr2[17:23],8), # 6 st_uid= int(hdr2[23:29],8), # 6 st_gid= int(hdr2[29:35],8), # 6 st_nlink= int(hdr2[35:41],8), # 6 st_rdev= int(hdr2[41:47],8), # 6 st_mtime= int(hdr2[47:58],8), # 11 st_nlen= int(hdr2[58:64],8), # 6 st_size= int(hdr2[64:75],8), # 11 ) return inode class Inode_Afio_070727(Inode_Afio_Base): # large ASCII magic number LEN=110 s1= "|23456|2345678|234567890123456m|23456|2345678|2345678|2345678|2345678|234567890123456n|234|234|234s|234567890123456:|..." s2= "| hdr| dev| inoM| mod| uid| gid| nlink| rdev| mtimeN|nml|flg|xszS| size:|pathname" def get_afio_inode(self): hdr2=self.loader.read_data(self.header_address+self.ID,self.LEN) inode=dict( st_dev= int(hdr2[0:8],8), # 8 st_ino= int(hdr2[8:25],8), # 17 st_mode= int(hdr2[25:31],8), # 6 st_uid= int(hdr2[31:39],8), # 8 st_gid= int(hdr2[39:47],8), # 8 st_nlink= int(hdr2[47:55],8), # 8 st_rdev= int(hdr2[55:63],8), # 8 st_mtime= int(hdr2[63:80],8), # 17 st_nlen= int(hdr2[80:84],8), # 4 st_flag= int(hdr2[84:88],8), # 4 # ??? st_xszS= int(hdr2[88:93],8), # 5 # ??? st_size= int(hdr2[93:110],8), # 17 ) return inode ################################################################################################################# # # # Load Archive Class # # # ################################################################################################################# class Afio_load(): def __init__(self,target,abspath,fs): self.target=target # path to mount afio file in the file system self.abspath=abspath # abspath of the afio file self.fs=fs # file system self.compress=[None,None] self.load() def load(self): # load each inode in the afio file self.ino={} # init st_ino(in afio file) Dict pipe=subprocess.Popen('afio -tvBZ "%s"' % self.abspath, shell=True,stdout=subprocess.PIPE).stdout f=open(self.abspath,"r") for line in pipe: i=int(line.split(None,2)[0]) f.seek(i) hdr=f.read(6) self.load_header(i,hdr) f.close() pipe.close() self.ino=None # fix me ! del ino def load_header(self,seek=None,hdr=''): if hdr=='070707':Inode_Afio_070707(seek=seek,loader=self);return # old ASCII magic number elif hdr=='070717':Inode_Afio_070717(seek=seek,loader=self);return # extended ASCII magic number elif hdr=='070727':Inode_Afio_070727(seek=seek,loader=self);return # large ASCII magic number not implemented yet elif hdr=='070701':return # cpio new ASCII magic number length: 110 not implemented yet elif hdr=='070702':return # cpio new ASCII magic number with CRC not implemented yet elif hdr=='070703':return # Tcpio magic number of TI/E not implemented yet else:return def _setnlink(self,inode,st_ino): inode2=self.ino.get(st_ino,None) if inode2==None: self.ino[st_ino]=inode inode.set_nlink(1) return inode else: inode2.inc_nlink() return inode2 def read_data(self,addr,size): f=open(self.abspath,"r") f.seek(addr) data=f.read(size) f.close() return data def read_compress_format(self,addr,size): if self.compress[0] == None: f=open(self.abspath,"r") f.seek(addr) if size>50:size=50 # read first 50 bytes top50=self.read_data(addr,size) filetype=_filetype(top50) if filetype=='lzop compressed data': self.compress=[_lzop,_lzop_size] return self.compress ################################################################################################################# # # # FUSE Operation Afiolzofs Class # # # ################################################################################################################# class Afiolzofs(): """Afio Lzo filesystem. """ def _check_symlink(self,target,source): ####### Symlink Check (root of the file system or not) ####### targetpath=os.path.split(target) if targetpath[0]=="/": # if target is root of the mountpoint sourceabspath=os.path.normpath(os.path.join(self.mountpoint,source)) # get abspath of the file f=open(sourceabspath);top50=f.read(50);f.close() # read top 50 byte of the file # if source is dir? filetype=_filetype(top50) # get file type if "ASCII cpio archive": # if 'ASCII cpio archive' targetname=".%s" % targetpath[1] # 'file name in the file system' for loading afio archive targetdir=os.path.join(targetpath[0],targetname) # 'path name in the file system' for loading afio archive Ramfs.mkdir(self,targetdir, S_IMODE(0755)) # make dir for load the archive Afio_load(targetdir,sourceabspath,self) # load the archive return targetname # afio.lzo file return source # return 'file name in the file system' def _symlink(self, target, source): ###### symlink ###### source=self._check_symlink(target,source) # check root of the file system -> mount afio.lzo Ramfs.symlink(self,target, source) # make normal symlink class Afiolzofs_SymlinkLoading(Afiolzofs): """Afio Lzo filesystem. """ def symlink(self, target, source): ###### symlink ###### self._symlink(target, source) class AfiolzoRamfs(Afiolzofs,Ramfs): pass class AfiolzoRomfs(Afiolzofs,Romfs): pass class AfiolzoSLRamfs(Afiolzofs_SymlinkLoading,Ramfs): pass class AfiolzoSLRomfs(Afiolzofs_SymlinkLoading,Romfs): pass class LogAfiolzoRamfs(LoggingMixIn,Afiolzofs,Ramfs): pass class LogAfiolzoRomfs(LoggingMixIn,Afiolzofs,Romfs): pass class LogAfiolzoSLRamfs(LoggingMixIn,Afiolzofs_SymlinkLoading,Ramfs): pass class LogAfiolzoSLRomfs(LoggingMixIn,Afiolzofs_SymlinkLoading,Romfs): pass ################################################################################################################# # # # Main # # # ################################################################################################################# if __name__ == "__main__": if len(argv) < 2: print 'usage: %s [options] [archives ...] ' % argv[0] print ' "old ASCII cpio archive" or "afio archive" files will be loaded.' print ' (Options) ' print ' --ramfs : allow temporaly write' print ' --nosymlink : do not allow archive loading with symlink' print ' --logging : with logging' print ' --debug : enable debug output (implies -f)' print ' --foreground : foreground operation' print ' --nothreads : disable multi-threaded operation' print ' --allow_other : allow access to other users' print ' --allow_root : allow access to root' print ' (Archive loading with symlink)' print ' Please make symlink of the archive file under the to load it.' print ' (Example)' print ' %s mountpoint # mount afiolzofs to mountpoint' % argv[0] print ' ln -s XXX.afio.lzo mountpoint/XXX # make symlink' print ' ls mountpoint/XXX # list the archive' print ' Afiolzofs makes symlink not to the archive, but to mountpoint/.XXX instead,' print ' and loads the archive inode data to mountpoint/.XXX' print ' If you read file contents, Afiolzofs will read it from the archive.' exit(1) args=argv[1:] keyargs={} mountpoint=os.path.abspath(args.pop()) key=('--logging','--nosymlink','--ramfs','--debug','--foreground','--nothreads','--allow_other','--allow_root') if key[0] in args: if key[1] in args: if key[2] in args: afiolzofs= LogAfiolzoRamfs(mountpoint) else: afiolzofs= LogAfiolzoRomfs(mountpoint) else: if key[2] in args: afiolzofs= LogAfiolzoSLRamfs(mountpoint) else: afiolzofs= LogAfiolzoSLRomfs(mountpoint) else: if key[1] in args: if key[2] in args: afiolzofs= AfiolzoRamfs(mountpoint) else: afiolzofs= AfiolzoRomfs(mountpoint) else: if key[2] in args: afiolzofs= AfiolzoSLRamfs(mountpoint) else: afiolzofs= AfiolzoSLRomfs(mountpoint) if (key[3] in args)or(key[1] in args):keyargs[key[3][2:]]=True if key[4] in args:keyargs[key[4][2:]]=True if key[5] in args:keyargs[key[5][2:]]=True if key[6] in args:keyargs[key[6][2:]]=True if key[7] in args:keyargs[key[7][2:]]=True for i in key: while i in args:args.remove(i) for i in args: basename=os.path.basename(i) target=os.path.join(mountpoint,'.'+basename) afiolzofs._symlink(target,i) fuse = FUSE(afiolzofs, mountpoint,use_ino=True,**keyargs)