#!/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.37 (18 Feb 2011) # fix write function # Popen error check # fix data read # fix lzop load function # fix data_buffer # fix fork&subprocess zombie # 0.0.36 (11 Feb 2011) # lzop get file size from file formt, "Multiple blocks compression, Random-access reading" # gzip get file size from file formt # xz get file size from file formt, "Multiple blocks compression" is not implement xz command line tool yet. # bzip2 get file size with decompress # # setgid # sticky bit # 0.0.35 (29 Jan 2011) # new buffer class # 0.0.34 (21 Jan 2011) # file handle = id(inode) # store 'file handle' to buffer # short cut for dir accsess # 0.0.33 (15 Jan 2011) # buffer issue # check lzop file signature # check gzip file signature # 0.0.32 (14 Jan 2011) # Header Class # 0.0.31 (09 Jan 2011) # comment # 0.0.30 (09 Jan 2011) # separate inode_buffer,data_buffer # filetype # 0.0.29 (08 Jan 2011) # data,afio_inode buffer # 0.0.28 (08 Jan 2011) # long lzop file >256K block decompress, but non buffered # 0.0.27 (07 Jan 2011) # long lzop file >256K block decompress(experimental) # AFIO header class # 0.0.26 (01 Jan 2011) # long lzop file >256K # 0.0.25 (29 Dec 2010) # Xattr,Attr,Dir function # get_items,items,et,set_inode_data function # 0.0.24 (24 Dec 2010) # _setnlink(calc_nlnk) # fix mount option # access # 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. ################################################################################################################# # # # Some comments are copied from (http://www.freedesktop.org/wiki/CommonExtendedAttributes) # # CS135 FUSE Documentation # # © 2010, Geoff Kuenning # # # ################################################################################################################# 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() 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 types import MethodType import copy from sys import argv, exit from time import time import subprocess import pwd import grp import os from errno import * """ 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(): """Inode Class of the Ramfs,Romfs""" def __init__(self,time=0,context=(0,0),address=None,header=None): if header==None: # normal inode self.st_ctime=time self.st_mtime=time self.st_atime=time self.st_uid=context[0] self.st_gid=context[1] else: # inode linked to archive file self.header=header self.header_address=address ### for buffers ### def get_inode(self):return self # used in header_buffers def add_inode(self): # add inode to header_buffers inode_id=id(self) if not hasattr(self,'header'): header_buffers.add(inode_id,self) # add header to header buffer return inode_id ### Inode Dir access functions ### def set_dentry(self,name,inode): # add inode to the dir entry (create it if no i_dentry) i_dentry=getattr(self,'i_dentry',None) if i_dentry==None: self.i_dentry={name:inode} # create dentry & add inode elif hasattr(i_dentry,name): return True # name is exist & return True -> should be error(file exist) else: i_dentry[name]=inode # add inode def get_dentry(self,name): return getattr(self,'i_dentry',{})[name] # get dir entry def pop_dentry(self,name): return getattr(self,'i_dentry',{}).pop(name) # pop dir entry to delete it or to move it def list_dentry(self): return getattr(self,'i_dentry',{}).keys() # list dir entry, it do not contain '.' and '..' def len_dentry(self): return len(getattr(self,'i_dentry',{})) # return number of the dir entrys (for dir delete check),it do not contain '.' and '..' ### Inode Xattr access functions ### def set_xattr(self,name,val): # add parameter to the xattr entry (create it if no i_xattr) i_xattr=getattr(self,'i_xattr',None) if i_xattr==None: self.i_xattr={name:val} # create xattr & add parameter else: i_xattr[name]=val # add parameter def get_xattr(self,name): return getattr(self,'i_xattr',{})[name] # get xattr entry def pop_xattr(self,name): return getattr(self,'i_xattr',{}).pop(name) # delete xattr entry def ls_xattr(self): return getattr(self,'i_xattr',{}).keys() # list xattr entry ### Inode Set Attr functions ### def set_nlink(self,n=1): if n!=1 or hasattr(self,'st_nlink'): self.st_nlink=n # 'No st_nlink' means st_nlink=1 def inc_nlink(self): if hasattr(self,'st_nlink'): self.st_nlink+=1 else: self.st_nlink=2 def dec_nlink(self): if hasattr(self,'st_nlink'): self.st_nlink-=1 else: self.st_nlink=0 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_mode(self,mode): self.st_mode=mode def set_gid(self,gid): self.st_gid=gid def set_uid(self,uid): self.st_uid=uid ### Inode Get Attr functions ### def get_dir_type(self): header=getattr(self,'header',None) if header==None:return False return header.get_dir_type(self) def get_items(self,keys=('st_mode','st_uid','st_gid')): # get 'mode','uid','gid' for permission check return [val for key,val in self.items(keys)] def items(self,keys=('st_mode','st_uid','st_gid','st_rdev','st_atime','st_ctime','st_mtime','st_ino','st_size','st_nlink')): header=None # called from fuse.py for stat file for key in keys: if key=='st_ino': yield ('st_ino',id(self)) elif key=='st_nlink': yield (key,getattr(self,key,1)) elif key=='st_size': val=getattr(self,'i_data',None) # calc size from i_data (i_data is the file data) if val==None: # no i_data in the inode if header==None:header=self.get_header() # if no header -> get header from archive file val=header.get(key,0) # read the data length from archive file else:val=len(val) yield (key,val) else: val=getattr(self,key,None) if val==None: # no data in the inode if header==None:header=self.get_header() # if no header -> get header from archive file val=header.get(key,0) # read the data from archive file yield (key,val) ### Inode File IO functions ### def get_header(self): header=getattr(self,'header',None) if header==None:return {} return header.copy(inode=self) def get_inode_data(self,offset=0,size=None): # data=getattr(self,'i_data',None) if data==None: # no data in the inode -> read from archive file header=self.get_header() # if no header -> get header from archive file if header=={}:return '' return header.get_st_data(offset,size) # read from archive file if size==None: return data[offset:] else: return data[offset:(offset + size)] def set_inode_data(self,data,offset=0): # set data for write i_data=self.get_inode_data() #if len(i_data)",len(i_data),offset self.i_data = "".join([i_data[0:offset],"\0"*(offset-len(i_data)),data,i_data[offset+len(data):]]) return len(data) def trunc_inode_data(self,offset): # truncate data length self.i_data=self.get_inode_data(0,offset) ################################################################################################################# # # # FUSE Operation Ramfs Class # # # ################################################################################################################# class Ramfs(Operations): # for normal use """Memory File System Class (Read Write but No Save)""" def __init__(self,mountpoint): self.mountpoint=mountpoint # abspath of the mount point self.uid=os.getuid() # file system owner id self.inode=Inode(time(),(self.uid,os.getgid())) # root inode of the file system self.inode.set_mode(S_IFDIR | 0755) # set as dir self.inode.set_nlink(2) # empty dir nlink is 2 (in linux) ######## Ramfs function (Permission check) ######## def _is_inode_USER(self,inode,context,uname,items,filesystem=False): # Permission check(file owner,root or filesystem owner) if (context[0]==items[1])or(context[0]==0):return # items='st_mode','st_uid','st_gid' if filesystem and(context[0]==self.uid):return # filesystem owner can change UID of any files. raise FuseOSError(EPERM) # (Operation not permitted) def _is_inode_R_OK(self,inode,context,uname,items): # Permission check(Readable) if items[0] & S_IROTH: return # allow other if (items[0] & S_IRUSR) and (items[1] == context[0]): return # allow owner & I'm owner if not(items[0] & S_IRGRP): raise FuseOSError(EACCES) # don't allow group if uname in grp.getgrgid(items[2]).gr_mem: return # user is the group member if context[1] != items[2]: raise FuseOSError(EACCES) # group is not same def _is_inode_W_OK(self,inode,context,uname,items): # Permission check(writable) if items[0] & S_IWOTH: return # allow other if (items[0] & S_IWUSR) and (items[1] == context[0]): return # allow owner & I'm owner if not (items[0] & S_IWGRP): raise FuseOSError(EACCES) # don't allow group if uname in grp.getgrgid(items[2]).gr_mem: return # user is the group member if context[1] != items[2]: raise FuseOSError(EACCES) # group is not same def _is_inode_X_OK(self,inode,context,uname,items): # Permission check(Excutable) # fix me! X for other if items[0] & S_IXOTH: return # allow other if (items[0] & S_IXUSR) and (items[1] == context[0]): return # allow owner & I'm owner if not(items[0] & S_IXGRP): raise FuseOSError(EACCES) # don't allow group if uname in grp.getgrgid(items[1]).gr_mem: return # user is the group member if context[1] != items[1]: raise FuseOSError(EACCES) # group is not same ######## Ramfs function (Get inode with Permission check) ######## def _get_dir_inode_nopermisson(self,path): ###### Get inode without any permission checking ###### if path=="/":return self.inode # if path is root, return root inode of the file system pname=os.path.split(path) inode_dir=self._get_dir_inode_nopermisson(pname[0]) try:return inode_dir.get_dentry(pname[1]) # get inode except KeyError: # if no inode inode=Inode(time(),(self.uid,os.getgid())) # make one as dir inode inode.set_mode( S_IMODE(0755) | S_IFDIR ) # set mode (dir) inode.set_nlink(2) inode_dir.set_dentry(pname[1],inode) inode_dir.inc_nlink() return inode def _get_inode(self,path,context,uname): ###### Get inode from path,context,uname ####### if path=="/":return self.inode # if path is root, return root inode of the file system else: # not root of the file system pname=os.path.split(path) inode_dir=self._get_inode(pname[0],context,uname) if not inode_dir.get_dir_type(): items=inode_dir.get_items() self._is_inode_X_OK(inode_dir,context,uname,items) # dir access permission check try:return inode_dir.get_dentry(pname[1]) # if 'no name in dir list' or 'not dir' -> error except KeyError:raise FuseOSError(ENOENT) # (No such file or directory) def _get_inode_for_access(self,path,fh=None): ###### Get inode for Access ###### # (FUSE Context) # uid The (numeric) user ID of the process invoking the operation. # gid The (numeric) group ID of the process invoking the operation. # pid The thread (process) ID of the process invoking the operation. # private_data A pointer (void*) to the private data returned by the init function. # umask The umask of the process invoking the operation. # not implemented context=fuse_get_context() # get system call uid and gid uname=pwd.getpwuid(context[0]).pw_name # get system call uname inode=None if fh != None: header=header_buffers.get(fh) if header !=None: inode=header.get_inode() # print "find inode in the buffer!!!",fh else: pass # print "no such inode in the buffer",fh if inode==None:inode=self._get_inode(path,context,uname) items=inode.get_items() return inode,context,uname,items def _get_inode_for_user(self,path,filesystem=False): ###### Get inode for user (or root) ###### inode,context,uname,items=self._get_inode_for_access(path) # access permission check self._is_inode_USER(inode,context,uname,items,filesystem) # user check return inode,items def _get_inode_for_read(self,path,fh=None): ###### Get inode for Read ###### inode,context,uname,items=self._get_inode_for_access(path,fh) # access permission check self._is_inode_R_OK(inode,context,uname,items) # read permission check return inode,items def _get_inode_for_write(self,path,fh=None): ###### Get inode for Write ###### inode,context,uname,items=self._get_inode_for_access(path,fh) # access permission check self._is_inode_W_OK(inode,context,uname,items) # write permission check return inode,items def _get_dir_inode_for_create(self,path): ###### Get dir inode for Rename or Link ###### pname=os.path.split(path) # split to dirpath and filename inode,context,uname,items=self._get_inode_for_access(pname[0]) # access permission check self._is_inode_W_OK(inode,context,uname,items) # dir write permission check return inode,pname[1] # return inode_dir and filename def _get_inode_for_delete(self,path): ###### Get inode for Delete ###### # (sticky bit) [From Wikipedia] # When the sticky bit is set, only the item's owner, the directory's owner, or the superuser can rename or delete files. #### fix me! directory????? # Without the sticky bit set, any user with write and execute permissions for the directory can rename or # delete contained files, regardless of owner. pname=os.path.split(path) # split to dirpath and filename inode,context,uname,items=self._get_inode_for_access(pname[0]) # access permission check items:'st_mode','st_uid','st_gid' self._is_inode_W_OK(inode,context,uname,items) # dir write permission check now=time() inode_file=inode.get_dentry(pname[1]) if items[0] & S_ISVTX : # sticky bit is ON if context[0]==0 : pass # you are root elif context[0]==items[1] : pass # you are directory's owner else : items_file=inode_file.get_intems() if items_file[1]==items[1]: pass # you are item's owner else: raise FuseOSError(EACCES) # Don't allow with sticky bit return inode_file,inode,pname[1],now # return inode_file, inode_dir, filename and now def _get_inode_for_create(self,path): ###### Get default inode for Create File,Dir,Symlink or Node ###### pname=os.path.split(path) # split to dirpath and filename inode,context,uname,items=self._get_inode_for_access(pname[0]) # access permission check self._is_inode_W_OK(inode,context,uname,items) # dir write permission check now=time() # (setgid) [From Wikipedia] # Setting the setgid permission on a directory (chmod g+s) causes new files and subdirectories created # within it to inherit its groupID, rather than the primary groupID of the user who created the file # (the ownerID is never affected, only the groupID). # Newly created subdirectories inherit the setgid bit. Note that setting the setgid permission on a directory only affects # the groupID of new files and subdirectories created after the setgid bit is set, and is not applied to existing entities. # (setuid) [From Wikipedia] # The setuid permission set on a directory is ignored on UNIX and Linux systems. # FreeBSD can be configured to interpret it analogously to setgid, namely, to force all files and sub-directories to be # owned by the top directory owner. inode_file = Inode(now,context) if items[0] & S_ISGID : # setgid is ON inode_file.set_gid(items[2]) # set item's GID to directory's GID return inode_file,inode,pname[1],now # return inode_file(new inode), 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,is_dir): # add inode without any permission checking pname=os.path.split(path) inode_dir=self._get_dir_inode_nopermisson(pname[0]) if inode_dir.set_dentry(pname[1],inode):raise FuseOSError(EEXIST) # (File exists) if is_dir:inode_dir.inc_nlink() def _add_inode_as_file(self,inode_dir,inode,filename,now): # if inode_dir.set_dentry(filename,inode):raise FuseOSError(EEXIST) # (File exists) inode_dir.update_mtime(now) # write to dir list -> change mtime inode_dir.update_ctime(now) # ??? inode.update_ctime(now) # change nlink -> change ctime def _add_inode_as_dir(self,inode_dir,inode,filename,now): # self._add_inode_as_file(inode_dir,inode,filename,now) inode_dir.update_mtime(now) # write to dir list -> change mtime inode_dir.inc_nlink() inode.update_mtime(now) # change dir list -> change mtime def _removefile_inode(self,inode,inode_dir,filename,now): # try:inode_dir.pop_dentry(filename) except KeyError:raise FuseOSError(ENOENT) # (No such file or directory) inode_dir.update_mtime(now) # change dir list -> change mtime inode_dir.update_ctime(now) # change mtime -> change ctime inode.update_ctime(now) # change nlink -> change ctime def _removedir_inode(self,inode,inode_dir,filename,now): # self._removefile_inode(inode,inode_dir,filename,now) inode_dir.dec_nlink() ######## FUSE Operating function (Start File System) ######## # FUSE Documentation # http://www.cs.hmc.edu/~geoff/classes/hmc.cs135.201001/homework/fuse/fuse_doc.html def init(self, path): """Called on filesystem initialization. Path is always / Use it instead of __init__ if you start threads on initialization.""" # The return value of this function is available to all file operations in the private_data field of fuse_context. # It is also passed as a parameter to the destroy() method. pass def statfs(self, path): ##### statfs (See statvfs(2) for a description of the structure contents.) ##### # Usually, you can ignore the path. # programs (like df) determine the free space. return dict(f_bsize=0x00040000, 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): # This is the same as the access(2) system call. # It returns -ENOENT if the path doesn't exist, # -EACCESS if the requested permission isn't available, or # 0 for success. # Note that it can be called on files, directories, or any other object that appears in the filesystem. inode,context,uname,items=self._get_inode_for_access(path) # access permission check (ENOENT,EACCESS) if os.X_OK & amode:self._is_inode_X_OK(inode,context,uname,items) if os.W_OK & amode:self._is_inode_W_OK(inode,context,uname,items) if os.R_OK & amode:self._is_inode_R_OK(inode,context,uname,items) def getattr(self, path, fh=None): ###### getattr ###### # (getattr) The "stat" structure is described in detail in the stat(2) manual page. # (fgetattr) called when fgetattr(2) is invoked by the user program. inode,context,uname,items=self._get_inode_for_access(path,fh) # access permission check return inode # return inode def chmod(self, path, mode): ###### chmod (See chmod(2) for details.) ###### # Only the permissions bits of mode should be examined. inode,items=self._get_inode_for_user(path) # user check inode.set_mode( S_IFMT(items[0]) | S_IMODE(mode) ) # see S_IFMT,S_IMODE now=time() inode.update_ctime(now) # change inode data -> change ctime def chown(self, path, uid, gid): ###### chown (See chown(2) for details.) ###### # FUSE doesn't deal particularly well with file ownership, # since it usually runs as an unprivileged user and this call is restricted to the superuser. inode,items=self._get_inode_for_user(path,filesystem=True) # user check if uid != -1:inode.set_uid(uid) # set uid with check -1 # fix me! only root,mount user if gid != -1:inode.set_gid(gid) # set gid with check -1 # fix me ! group which user is in now=time() inode.update_ctime(now) # change inode data -> change ctime def utimens(self, path, times=None): ###### utime (see utimensat(2) for full details.) ###### inode,items=self._get_inode_for_user(path) # user check now = time() atime, mtime = times if times else (now, now) # atime mtime can be changed by touch (utimes),but not ctime 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 # def lock(self,path, fh, cmd, lock): pass ##### lock (Perform a POSIX file-locking operation.) ##### lock = None # def bmap(self,path, blocksize, idx): pass ##### bmap (This function is similar to bmap(9).) ##### bmap = None ######## FUSE Operating function (XAttr) ######## # fix me!!! # Extended attributes (xattrs) can be set in different namespaces. Linux uses "security", "system", "trusted", and "user". def listxattr(self, path): ##### listxattr (See listxattr(2).) ##### # This should be implemented only if HAVE_SETXATTR is true. raise FuseOSError(ENOSYS) # (Function not implemented) inode,context,uname,items=self._get_inode_for_access(path) # access permission check return inode.ls_xattr() def getxattr(self, path, name, position=0): ##### getxattr (See getxattr(2).) ##### # This should be implemented only if HAVE_SETXATTR is true. raise FuseOSError(ENOSYS) # (Function not implemented) inode,context,uname,items=self._get_inode_for_access(path) # access permission check try:return inode.get_xattr(name) except KeyError:raise FuseOSError(ENOSYS) # (Function not implemented) def setxattr(self, path, name, value, options, position=0): ##### (See setxattr(2).) ##### # This should be implemented only if HAVE_SETXATTR is true. raise FuseOSError(ENOSYS) # (Function not implemented) inode,items=self._get_inode_for_user(path) # user check inode.set_xattr(value) # Ignore options def removexattr(self, path, name): ##### removexattr () ##### raise FuseOSError(ENOSYS) # (Function not implemented) inode,items=self._get_inode_for_user(path) # user check try:inode.pop_xattr(value) except KeyError:raise FuseOSError(ENOSYS) # (Function not implemented) ######## FUSE Operating function (Read Dir) ######## def opendir(self, path): inode,context,uname,items=self._get_inode_for_access(path) # access permission check if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS) # Function not implemented self._is_inode_R_OK(inode,context,uname,items) # read permission check return inode.add_inode() # return fd def readdir(self, path, fh): ###### readdir ###### inode,items=self._get_inode_for_read(path,fh) # read permission check if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS) # (Function not implemented) dirnames=inode.list_dentry() # read dir list dirnames.append('..') # append parent dir '..' dirnames.append('.') # append parent dir '.' self._update_atime(inode,time()) # read data -> change atime 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_nlink(2) self._add_inode_as_dir(inode_dir,inode,filename,now) # add inode as dir def rmdir(self, path): ###### rmdir (See rmdir(2) for details.) ###### # Remove the given directory. This should succeed only if the directory is empty (except for "." and ".."). inode,inode_dir,filename,now=self._get_inode_for_delete(path) # write permission check items=inode.get_items() if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS) # Function not implemented (not Dir shuld be delete unlink) if filename == '/': # Shuld be check root? ?? raise FuseOSError(EPERM) # (Operation not permitted) elif inode.len_dentry()>0: # raise FuseOSError(ENOTEMPTY) # (Directory not empty) else: self._removedir_inode(inode,inode_dir,filename,now) # remove dir ######## FUSE Operating function (Create or Remove File) ######## def mknod(self, path, mode, dev): ###### mknod ###### # Make a special (device) file, FIFO, or socket. See mknod(2) for details. mask=S_IFSOCK | S_IFBLK | S_IFCHR | S_IFIFO # 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! check multi mode or not? inode.set_rdev(dev) # set rdev 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 return inode.add_inode() # return fd def unlink(self, path): ###### unlink (See unlink(2) for details.) ###### inode,inode_dir,filename,now=self._get_inode_for_delete(path) # write permission check items=inode.get_items() if S_ISDIR(items[0]):raise FuseOSError(ENOSYS) # Function not implemented (Dir shuld be delete rmdir) inode.dec_nlink() self._removefile_inode(inode,inode_dir,filename,now) # remove file def rename(self, old, new): ###### rename (See rename(2) for full details.) ###### # Note that the source and target don't have to be in the same directory, # so it may be necessary to move the source to an entirely new directory. 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 items=inode.get_items() if S_ISDIR(items[0]): 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 (See link(2) for details.) ###### # be aware that they have an effect on how unlink works. inode_dir,filename=self._get_dir_inode_for_create(target) # write permission check inode,context,uname,items=self._get_inode_for_access(source) # we can make link of other's file now=time() # get time now if S_ISDIR(items[0]):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 ###### # See readlink(2) for how to handle a too-small buffer and for error codes. inode,items=self._get_inode_for_read(path) # read permission check if not S_ISLNK(items[0]):raise FuseOSError(EPERM) # (Operation not permitted) self._update_atime(inode,time()) # read data -> change atime 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): ###### open ###### # Open a file. If you aren't using file handles, this function should just check for existence and # permissions and return either success or an error code. # If you use file handles, you should also allocate any necessary structures and set fi->fh. # In addition, fi has some other fields that an advanced filesystem might find useful # see the structure definition in fuse_common.h for very brief commentary. inode,context,uname,items=self._get_inode_for_access(path) # access permission check if S_ISDIR(items[0]):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,items) elif (flags & 3) == os.O_RDWR: self._is_inode_R_OK(inode,context,uname,items) self._is_inode_W_OK(inode,context,uname,items) elif (flags & 3)== os.O_WRONLY: self._is_inode_W_OK(inode,context,uname,items) return inode.add_inode() def read(self, path, size, offset, fh): ###### read (See read(2) for full details.) ###### inode,items=self._get_inode_for_read(path,fh) # read permission check if not S_ISREG(items[0]):raise FuseOSError(EPERM) # (Operation not permitted) self._update_atime(inode,time()) # read data -> change atime return inode.get_inode_data(offset,size) # return read data def write(self, path, data, offset, fh): ###### write ###### inode,items=self._get_inode_for_write(path,fh) # write permission check if not S_ISREG(items[0]):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(data,offset) def truncate(self, path, length, fh=None): ###### truncate (See truncate(2) for details.) ###### inode,items=self._get_inode_for_write(path,fh) # write permission check if not S_ISREG(items[0]):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(length) def release(self, path, fh): # called when FUSE is completely done with a file # The IBM document claims that there is exactly one release per open return 0 def flush(self, path, fh): # Called on each close so that the filesystem has a chance to report delayed errors. # there may be more than one flush call for each open. return 0 def fsync(self, path, datasync, fh): # Flush any dirty information about the file to disk. # If isdatasync is nonzero, only data, not metadata, needs to be flushed. return 0 ################################################################################################################# # # # FUSE Operation Romfs Class # # # ################################################################################################################# class Romfs(Ramfs): """Memory File System Class (Read only)""" def _update_atime(self,inode,now): pass # no update atime ######## FUSE Operating function (Attr) ######## def access(self, path, amode): inode,context,uname,items=self._get_inode_for_access(path) # access permission check if os.X_OK & amode:self._is_inode_X_OK(inode,context,uname,items) if os.W_OK & amode: raise FuseOSError(EROFS) # (Read-only file system) if os.R_OK & amode:self._is_inode_R_OK(inode,context,uname,items) def chmod(self, path, mode): raise FuseOSError(EROFS) # (Read-only file system) def chown(self, path, uid, gid): raise FuseOSError(EROFS) # (Read-only file system) def utimens(self, path, times=None): raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (XAttr) ######## def setxattr(self, path, name, value, options, position=0): 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): raise FuseOSError(EROFS) # (Read-only file system) def rmdir(self, path): raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (Create or Remove File) ######## def mknod(self, path, mode, dev): raise FuseOSError(EROFS) # (Read-only file system) def create(self, path, mode): raise FuseOSError(EROFS) # (Read-only file system) def unlink(self, path): raise FuseOSError(EROFS) # (Read-only file system) def rename(self, old, new): raise FuseOSError(EROFS) # (Read-only file system) def link(self, target, source): raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (Sym Link) ######## def symlink(self, target, source): raise FuseOSError(EROFS) # (Read-only file system) ######## FUSE Operating function (Read or Write File) ######## def open(self, path, flags): inode,context,uname,items=self._get_inode_for_access(path) # access permission check if S_ISDIR(items[0]): raise FuseOSError(ENOSYS) # (Function not implemented) if (flags & 3) == os.O_RDONLY:self._is_inode_R_OK(inode,context,uname,items) 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) return id(inode) def write(self, path, data, offset, fh): raise FuseOSError(EROFS) # (Read-only file system) def truncate(self, path, length, fh=None): 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 ################################################################################################################# # # # Header Afio Class # # # ################################################################################################################# class Header_Afio_Base(): ID=6 LEN=0 s1= "" s2= "" header_base_buffer={} def __init__(self,abspath): #### #### self.abspath=abspath # abspath of the archive file def print_header(self): #### print afio header #### print self.s1 print self.s2 print "%s%s" % (self._get_hdr2(),self.get_st_path()) def init(self,inode): #### initialize with inode data #### self.inode=inode # store to update inode.header header_address=inode.header_address+self.ID # address is without 6byte '070707' self.header_address=header_address # store the address self.hdr2=self._read_data(header_address,self.LEN) # pre-read hdr2, use the data soon def copy(self,inode): #### return copied header, #### inode_id=id(inode) copied_header=header_buffers.get(inode_id) # use one in the header buffer, if exist if copied_header==None: # when there is no one copied_header=copy.copy(self) # copy copied_header.init(inode) # and initialize header_buffers.add(inode_id,copied_header) # add header to header buffer return copied_header def get(self,name,default): #### get data like as dict #### x=getattr(self,name,None) if x != None: return x x=getattr(self,'get_'+name,None) if x != None: return x() return default #### get Header Data Function #### def get_inode(self): return getattr(self,'inode',None) def get_st_path(self): #### path is read once when archive is loaded #### if self.get_st_compress(): return self._read_data(self.header_address+self.LEN,self.get_st_nlen()-3) else: return self._read_data(self.header_address+self.LEN,self.get_st_nlen()-1) def get_st_compress(self): #### compressed or not #### st_compress=getattr(self,'st_compress',None) if st_compress != None:return st_compress st_nlen=self.get_st_nlen() # st_nlen= name length + 1(\x00) if st_nlen<=3:st_compress=False # compressed file name is with '.z' -> length > 3 else: path=self._read_data(self.header_address+self.LEN+st_nlen-3,2) # get last 2 char if (path=='.z') and ((self.get_st_rdev() & 1)==0):st_compress=True # '.z' and 'rdev &1 =0' -> compressed data else:st_compress=False self.st_compress=st_compress # store the data self.inode.header=self._copy_base() # change inode header fot next time return st_compress def get_st_comp_size(self): #### get decompressed data length of the compressed data #### st_comp_size=getattr(self,'st_comp_size',None) if st_comp_size != None:return st_comp_size[0][-1] # already know the size st_compress=self.st_compress if st_compress!=True:return self.st_compress[0](self) # already know decompress function st_size=self._get_common_size() if st_size != None:return st_size raise FuseOSError(EIO) def get_st_size(self): #### get size #### if self.get_st_compress(): return self.get_st_comp_size() else: return self.get_st_raw_size() def get_next_addr(self): return self._get_data_address()+self.get_st_raw_size() def get_st_raw_data(self,offset=0,size=None): #### get raw data #### data_offset=self._get_data_address()+offset data_size=self.get_st_raw_size()-offset if size==None: return self._read_data(data_offset,data_size) else: return self._read_data(data_offset,min(data_size,size)) def get_st_comp_data(self,offset=0,size=None): #### get decompressed data of the compressed data #### if self.st_compress==True:st_size=self.get_st_comp_size() # get size -> get compress method return self.st_compress[1](self,offset,size) def get_st_data(self,offset=0,size=None): #### get data #### if self.get_st_compress(): return self.get_st_comp_data(offset,size) else: return self.get_st_raw_data(offset,size) def get_dir_type(self,inode): #### get_dir_type ( call as 'self.get_dir_type()' ) #### dir_type=getattr(self,'dir_type',None) if dir_type!=None:return dir_type # this is a dir, and other's X permission OK header=self.copy(inode) mode=header.get_st_mode() if S_ISDIR(mode): # this is dir dir_type = mode & S_IXOTH # other's permission OK header.dir_type=dir_type # set dir_type inode.header=header._copy_base() # change inode's header for next time return dir_type else: False # this is not dir #### Local Function #### def _copy_base(self): #### make new header for next time #### keys=['abspath','st_compress','dir_type'] header_id=[id(self.__class__)] for key in keys:header_id.append(getattr(self,key,None)) copied_header=self.header_base_buffer.get(tuple(header_id)) # try to get from header base buffer if copied_header==None: copied_header=copy.copy(self.inode.header) for key in keys:setattr(copied_header,key,getattr(self,key,None)) return copied_header def _read_data(self,addr,size): #### read any data in the archive file #### f=open(self.abspath,"r") f.seek(addr) data=f.read(size) f.close() return data def _get_hdr2(self,name='',s=0,e=0): #### get hdr2 or hdr2 parameter #### if name != '': val=getattr(self,name,None) if val==None: if not hasattr(self,'hdr2'):self.hdr2=self._read_data(self.header_address,self.LEN) val=int(self.hdr2[s:e],8);setattr(self,name,val) return val if e != 0: if not hasattr(self,'hdr2'):self.hdr2=self._read_data(self.header_address,self.LEN) return int(self.hdr2[s:e],8) return self.hdr2 def _get_data_address(self): return self.header_address+self.LEN+self.get_st_nlen() #### get data address in the archive #### #### lzop functions #### def _get_lzop_header_size(self): #### get lzop header length & flags #### st_compress=self.st_compress if len(st_compress)>=7:return st_compress[5:7] # return lzop_header_size,lzop_flags f=open(self.abspath) f.seek(self._get_data_address()) s=f.read(512) # enough length is 42byte in afio, because no filename. f.close() num= 9 # 89 4c 5a 4f 00 0d 0a 1a 0a #lzop file signature lzop_ver=(ord(s[num])<<8)|ord(s[num+1]) # ver_2 if lzop_ver>0x0940: lzop_ver_x=1 # 'ext_2' 'level_1' 'mtimeH_4' are exist else: lzop_ver_x=0 # num=14+lzop_ver_x*3 # lib_2 (ext_2) method_1 (level_1) lzop_flags=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3]) # flags_4 F_H_FILTER=0x00000800L if lzop_flags & F_H_FILTER: lzop_filter_x=1 # 'filter_4' is exist else: lzop_filter_x=0 num=26+lzop_ver_x*7+lzop_filter_x*4 # mode_4 mtimeL_4 (mtimeH_4) lzop_nlen=ord(s[num]) # nlen_1 num=31+lzop_ver_x*7+lzop_filter_x+lzop_nlen # csum_4 F_H_EXTRA_FIELD = 0x00000040L if lzop_flags & F_H_EXTRA_FIELD: # No 'EXTRA_FIELD' in lzop yet lzop_extr=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3])+8 # extrlen_4 extr_n extr_csum_4 else: lzop_extr=0 lzop_header_size=31+lzop_ver_x*7+lzop_filter_x+lzop_nlen+lzop_extr if lzop_nlen==0: # if lzop_nlen is 0 self.st_compress=( st_compress[0],st_compress[1],st_compress[2],st_compress[3],st_compress[4], lzop_header_size,lzop_flags) self.inode.header=self._copy_base() # change inode's header for next time return lzop_header_size,lzop_flags def _get_lzop_data_size(self): #### get lzop data block length (compressed & decompressed) #### lzop_raw_len,lzop_flags=self._get_lzop_header_size() if lzop_raw_len==None:return None lzop_data_len=0 seek=self._get_data_address() size=self.get_st_raw_size() f=open(self.abspath) lzop_raw_data=[] lzop_data=[] F_ADLER32_D=0x00000001L F_ADLER32_C=0x00000002L F_CRC32_D=0x00000100L F_CRC32_C=0x00000200L lzop_raw_data.append(lzop_raw_len) while 1: # sum each block size if lzop_raw_len>size:raise FuseOSError(EIO) # (I/O error) f.seek(seek+lzop_raw_len) s=f.read(8) num=0;d_len=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3]) if d_len==0:break num=4;c_len=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3]) lzop_data_len+=d_len lzop_raw_len+=c_len+8 if lzop_flags & F_ADLER32_D :lzop_raw_len+=4 if lzop_flags & F_CRC32_D :lzop_raw_len+=4 if d_len>c_len: if lzop_flags & F_ADLER32_C :lzop_raw_len+=4 if lzop_flags & F_CRC32_C :lzop_raw_len+=4 lzop_data.append(lzop_data_len) lzop_raw_data.append(lzop_raw_len) # print "--------> %08x %08x %08x %08x %08x" % (size,lzop_raw_len,lzop_data_len,d_len,c_len) f.close() self.st_comp_size=(lzop_data,lzop_raw_data) # print self.st_comp_size return lzop_data[-1] def _get_lzop_data(self,offset=0,size=None): #### get lzop data #### lzop_data,lzop_raw_data=self.st_comp_size if size==None:size=lzop_data[-1] endoffset=offset+size for start_block in xrange(0,len(lzop_data)): if offset0:offset=offset-lzop_data[start_block-1] break else:return '' # read after the end -> return '' data=self._get_lzop_data_block_buffered(start_block) if endoffset>lzop_data[start_block]: for end_block in xrange(start_block+1,len(lzop_data)): data=data+self._get_lzop_data_block_buffered(end_block) if endoffset= len(s) or ord(s[i]) == 0x00 :return 0,i+i0+1 n |= (ord(s[i+i0]) & 0x7F) << (i * 7) return n,i0+i+1 def _get_xz_data_size(self): #### get xz data length #### s=self.get_st_raw_data(self.get_st_raw_size()-12) indexsize=((ord(s[7])<<24)|(ord(s[6])<<16)|(ord(s[5])<<8)|ord(s[4])+1)*4 s=self.get_st_raw_data(self.get_st_raw_size()-12-indexsize,indexsize) num=1 indexnum,num=self._get_xz_integer(s,num) lzop_data_len=0 lzop_raw_len=0 lzop_data=[] lzop_raw_data=[] for i in xrange(indexnum): x,num=self._get_xz_integer(s,num) lzop_raw_len += x x,num=self._get_xz_integer(s,num) lzop_data_len += x lzop_data.append(lzop_data_len) lzop_raw_data.append(lzop_raw_len) self.st_comp_size=(lzop_data,lzop_raw_data) # print self.st_comp_size return lzop_data_len #### gzip functions #### def _get_gzip_data_size(self): #### get gzip data length #### s=self.get_st_raw_data(self.get_st_raw_size()-4) st_comp_size=(ord(s[3])<<24)|(ord(s[2])<<16)|(ord(s[1])<<8)|ord(s[0]) self.st_comp_size=([st_comp_size],None) return st_comp_size #### common functions #### def _get_common_size(self): #### #### f=open(self.abspath) f.seek(self._get_data_address()) s=f.read(512) # f.close() if s[0:9] == '\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a': if self.st_compress==True: # self.st_compress=( Header_Afio_Base._get_lzop_data_size, Header_Afio_Base._get_lzop_data, 2,'lzop','-dc') # self.inode.header=self._copy_base() # return self.st_compress[0](self) elif s[0:2] == '\x1f\x8b': if self.st_compress==True: # self.st_compress=( Header_Afio_Base._get_gzip_data_size, Header_Afio_Base._get_common_data, 2,'gzip','-dc') # self.inode.header=self._copy_base() # return self.st_compress[0](self) elif s[0:6] == '\xfd7zXZ\x00': if self.st_compress==True: # self.st_compress=( Header_Afio_Base._get_xz_data_size, Header_Afio_Base._get_common_data, 2,'xz','-dc') # self.inode.header=self._copy_base() # return self.st_compress[0](self) elif s[0:3] == 'BZh': if self.st_compress==True: # self.st_compress=( Header_Afio_Base._get_common_data_size, Header_Afio_Base._get_common_data, 2,'bzip2','-dc') # self.inode.header=self._copy_base() # return self.st_compress[0](self) #### Common Decompress Function #### def _get_common_data_size(self): #### get data length #### data=self._get_common_data_block_buffered() st_comp_size=len(data) self.st_comp_size=([st_comp_size],None) return st_comp_size def _get_common_data(self,offset=0,size=None): #### get compressed data #### data=self._get_common_data_block_buffered() return data[offset:(offset+size)] def _get_common_data_block_buffered(self): #### decompress data block(buffered) #### key=(id(self.inode),0) # make key data_block=data_buffers.get(key) # try to get block from buffer if data_block==None: data_block=self._get_common_data_block() data_buffers.add(key,data_block,self.inode) return data_block def _get_common_data_block(self): #### get data block #### args=self.st_compress[3:self.st_compress[2]+3] pipe=subprocess.PIPE try: proc = subprocess.Popen(args,stdin=pipe,stdout=pipe,close_fds=True,) # make subprocess(ex. gzip -dc) for decompress except OSError: print "OSError : %s" % " ".join(args) raise FuseOSError(EIO) pid = os.fork() # fork if pid==0: # child (read compressed data and send then to program) proc.stdout.close() data=self.get_st_raw_data() # compressed data proc.stdin.write(data) proc.stdin.flush() proc.stdin.close() os._exit(0) proc.stdin.close() # parent (read decompressed data from program) data_block=proc.stdout.read() proc.stdout.close() exit_status =proc.wait() if exit_status !=0 : print "exit_status(%s) : %s" % (" ".join(args),exit_status) raise FuseOSError(EIO) pid,exit_status =os.waitpid(pid,0) if exit_status !=0 : print "exit_status(fork) : %s" % exit_status raise FuseOSError(EIO) return data_block """ 7z LZMA A. Local file header: local file header signature 4 bytes (0x04034b50) version needed to extract 2 bytes general purpose bit flag 2 bytes compression method 2 bytes last mod file time 2 bytes last mod file date 2 bytes crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes file name length 2 bytes extra field length 2 bytes file name (variable size) extra field (variable size) """ ### Binary(& swap byte) archive header fix me ! #/* binary format */ #define M_BINARY 070707 /* Binary magic number */ #define M_STRLEN 6 /* ASCII magic number length */ #/* # * Binary archive header (obsolete). # */ #typedef struct #{ # short b_dev; /* Device code */ # ushort b_ino; /* Inode number */ # ushort b_mode; /* Type and permissions */ # ushort b_uid; /* Owner */ # ushort b_gid; /* Group */ # short b_nlink; /* Number of links */ # short b_rdev; /* Real device */ # ushort b_mtime[2]; /* Modification time (hi/lo) */ # ushort b_name; /* Length of pathname (with null) */ # ushort b_size[2]; /* Length of data */ #} Binary; class Header_Afio_070707(Header_Afio_Base): LEN=70 s1= "|23456|23456|23456|23456|23456|23456|23456|23456789ab|23456|23456789ab|..." s2= "| dev| ino| mode| uid| gid|nlink| rdev| mtime|nmlen| size|pathname" def get_st_dev(self): return self._get_hdr2('' , 0, 6) def get_st_ino(self): return self._get_hdr2('' , 6,12) def get_st_mode(self): return self._get_hdr2('st_mode' ,12,18) def get_st_uid(self): return self._get_hdr2('st_uid' ,18,24) def get_st_gid(self): return self._get_hdr2('st_gid' ,24,30) def get_st_nlink(self): return self._get_hdr2('' ,30,36) def get_st_rdev(self): return self._get_hdr2('st_rdev' ,36,42) def get_st_mtime(self): return self._get_hdr2('mtime' ,42,53) def get_st_nlen(self): return self._get_hdr2('st_nlen' ,53,59) def get_st_raw_size(self): return self._get_hdr2('st_raw_size' ,59,70) get_st_atime=get_st_mtime get_st_ctime=get_st_mtime class Header_Afio_070717(Header_Afio_Base): 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_st_dev(self): return self._get_hdr2('' , 0, 6) def get_st_ino(self): return self._get_hdr2('' , 6,17) def get_st_mode(self): return self._get_hdr2('st_mode' ,17,23) def get_st_uid(self): return self._get_hdr2('st_uid' ,23,29) def get_st_gid(self): return self._get_hdr2('st_gid' ,29,35) def get_st_nlink(self): return self._get_hdr2('' ,35,41) def get_st_rdev(self): return self._get_hdr2('st_rdev' ,41,47) def get_st_mtime(self): return self._get_hdr2('mtime' ,47,58) def get_st_nlen(self): return self._get_hdr2('st_nlen' ,58,64) def get_st_raw_size(self): return self._get_hdr2('st_raw_size' ,64,75) get_st_atime=get_st_mtime get_st_ctime=get_st_mtime class Header_Afio_070727(Header_Afio_Base): 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_st_dev(self): return self._get_hdr2('' , 0, 8) def get_st_ino(self): return self._get_hdr2('' , 8,25) def get_st_mode(self): return self._get_hdr2('st_mode' ,25,31) def get_st_uid(self): return self._get_hdr2('st_uid' ,31,39) def get_st_gid(self): return self._get_hdr2('st_gid' ,39,47) def get_st_nlink(self): return self._get_hdr2('' ,47,55) def get_st_rdev(self): return self._get_hdr2('st_rdev' ,55,63) def get_st_mtime(self): return self._get_hdr2('mtime' ,63,80) def get_st_nlen(self): return self._get_hdr2('st_nlen' ,80,84) def get_st_flag(self): return self._get_hdr2('' ,84,88) # specialflags 0 def get_st_xszS(self): return self._get_hdr2('' ,88,93) # extraheaderlen 0 def get_st_raw_size(self): return self._get_hdr2('st_raw_size' ,93,110) get_st_atime=get_st_mtime get_st_ctime=get_st_mtime ################################################################################################################# # # # Load Archive Class # # # ################################################################################################################# class Afio_load1(): """Load afio file(abspath) to target path in the filesystem""" def __init__(self,target,abspath,filesystem): self.target=target # path to mount afio file in the file system self.fs=filesystem # file system self.header_070707=Header_Afio_070707(abspath) # Old ASCII magic number self.header_070717=Header_Afio_070717(abspath) # Extended ASCII magic number self.header_070727=Header_Afio_070727(abspath) # Large ASCII magic number self.ino={} # Dict to seach hard link proc=subprocess.Popen('afio -tvBZ "%s"' % abspath, shell=True,stdout=subprocess.PIPE) pipe=proc.stdout f=open(abspath,"r") for line in pipe: i=int(line.split(None,2)[0]) # get header address in the archive file f.seek(i) # print "seek ----> " , i hdr=f.read(6) self.load_header(i,hdr) f.close() pipe.close() if proc.wait() !=0 : raise FuseOSError(EIO) delattr(self,'ino') def load_header(self,seek=None,hdr=''): if hdr=='070707':inode=Inode(address=seek,header=self.header_070707) elif hdr=='070717':inode=Inode(address=seek,header=self.header_070717) elif hdr=='070727':inode=Inode(address=seek,header=self.header_070727) elif hdr=='070701':return # cpio new ASCII magic number length: 110 not implemented elif hdr=='070702':return # cpio new ASCII magic number with CRC not implemented elif hdr=='070703':return # Tcpio magic number of TI/E not implemented else:return header=inode.get_header() # get header header.print_header() is_dir=False mode=header.get_st_mode() if S_ISDIR(mode): # 'DIR' inode.set_nlink(2);is_dir=True # nlink = 2 elif header.get_st_nlink()>1: # 'not DIR' and 'Hard Linked(nlink>1)' ino_key=(header.get_st_dev(),header.get_st_ino()) # must combine 'dev' and 'ino' for key inode2=self.ino.get(ino_key,None) # search Hard linked file with the key if inode2==None: self.ino[ino_key]=inode # LinkedFile is first time, save inode. else: inode=inode2;inode.inc_nlink() # LinkedFile is second time, use saved inode. if mode !=0: path='%s/%s' % (self.target,header.get_st_path()) # make path of the inode in target dir. self.fs._add_inode_nopermisson(path,inode,is_dir) # add inode to the filesystem # print "seek XXX ----> " ,header.get_next_addr() return header.get_next_addr() class Afio_load(Afio_load1): """Load afio file(abspath) to target path in the filesystem""" def __init__(self,target,abspath,filesystem): self.target=target # path to mount afio file in the file system self.fs=filesystem # file system self.header_070707=Header_Afio_070707(abspath) # Old ASCII magic number self.header_070717=Header_Afio_070717(abspath) # Extended ASCII magic number self.header_070727=Header_Afio_070727(abspath) # Large ASCII magic number self.ino={} # Dict to seach hard link i=0 f=open(abspath,"r") # print "Load START" while True: f.seek(i) print "seek ----> " , i hdr=f.read(6) i=self.load_header(i,hdr) if i==None:break # "unknown header" or "file end" # print "Load END" f.close() delattr(self,'ino') ################################################################################################################# # # # FUSE Operation Afiolzofs Class # # # ################################################################################################################# class Afiolzofs(): """Base Class of the Afio Lzo Filesystem""" def _check_symlink(self,target,source): ####### Symlink Check (root of the file system or not) ####### print source print self.mountpoint targetpath=os.path.split(target) if targetpath[0]!="/":return source # if target is not in the root of the filesystem -> normal link sourceabspath=os.path.normpath(os.path.join(self.mountpoint,source)) # get abspath of the file if sourceabspath[0:len(self.mountpoint)]==self.mountpoint: # if source is in the self.mountpoint(same filesystem), print 'file(%s) is in the mount point(%s)' % (sourceabspath,self.mountpoint) return source # cannot to load archive file -> normal link try: # try to load afio archive file f=open(sourceabspath);top50=f.read(50);f.close() # read top 50 byte of the file except: print 'loading is failed' return source # failed -> return source path to symlink normally filetype=_filetype(top50) # get file type if filetype=="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 # return loaded dir path to symlink. instead to normal symlink def _symlink(self, target, source): ###### _symlink (internal symlink command) ###### source=self._check_symlink(target,source) # check root of the file system -> load afio.lzo Ramfs.symlink(self,target, source) # make normal symlink, even if Romfs class Afiolzofs_SymlinkLoading(Afiolzofs): """Base Class of the Afio Lzo Filesystem with SymlinkLoading option""" def symlink(self, target, source):self._symlink(target, source) ###### symlink ###### ### Real File System Class ### 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 ################################################################################################################# # # # Buffers # # # ################################################################################################################# class Buffers(): def __init__(self,num1=10,num2=10): self.num=num1 self.data={} self.start=[None,None,None,None] self.start[1]=self.start # before self.start[2]=self.start # next def get(self,key,default=None): data=self.data datalist=data.get(key,None) if datalist == None: # print "not extst in the Buffer" return None else: start=self.start before=datalist[1] next=datalist[2] before[2]=next next[1]=before last=start[1] last[2]=datalist datalist[1]=last datalist[2]=start start[1]=datalist return datalist[0] def add(self,key,val,hold=None): data=self.data if not key in data: start=self.start before=start[1] datalist=[val,before,start,key,hold] start[1]=datalist before[2]=datalist data[key]=datalist num=self.num while len(data)>num: # dell datalist=start[2] next=datalist[2] start[2]=next next[1]=start data.pop(datalist[3]) ################################################################################################################# # # # Main # # # ################################################################################################################# def main(args,command='afiolzofs.py'): if len(argv) < 2: # print usage print 'usage: %s [options] [archives ...] ' % command 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 ' --kernel_cache : allow kernel cache' # print ' --direct_io : allow directio (not implemented)' # print ' --big_writes : allow big writes (not implemented)' 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' % command print ' ln -s XXX.afio.lzo mountpoint/XXX # make symlink ' print ' (XXX.afio.gz also OK, but cannot block read)' 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) mountpoint=os.path.abspath(args.pop()) key=('--logging','--nosymlink','--ramfs','--debug','--foreground','--nothreads','--allow_other','--allow_root','--kernel_cache') if key[0] in args: # select file system 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) keyargs={} # args for FUSE if key[3] in args: keyargs[key[3][2:]]=True if (key[4] in args)or(key[0] 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)and(not key[6] in args): keyargs[key[7][2:]]=True if key[8] in args: keyargs[key[8][2:]]=True # if key[9] in args: keyargs[key[9][2:]]=True for i in key: # delete overlaped arguments from 'args' while i in args:args.remove(i) # global data_buffers data_buffers=Buffers(20,20) # data buffer global header_buffers header_buffers=Buffers(50,50) # header buffer for i in args: # mount afio files in the 'args' basename=os.path.basename(i) # make symlink name target=os.path.join(mountpoint,'.'+basename) # make 'target dir' name afiolzofs._symlink(target,i) # load afio file to the 'target dir' fuse = FUSE(afiolzofs, mountpoint,use_ino=True,**keyargs) # fuse operation start if __name__ == "__main__": main(argv[1:],argv[0])