#!/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.39 (27 Feb 2011) # fix loader (with fuse context before start file system) # change many header function to common # change 'if' to 'try' # 0.0.38 (20 Feb 2011) # fix load archive error at command line # change data type "string --> array" # 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 array import pwd import grp import os from ctypes import * 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(object): """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 header information self.header=header # template header self.header_address=address # archive header address(first 6 byte ID's address) ### File Handle function ### def get_inode(self): #### get inode function #### return self # common function 'Inode' and 'Header_Afio_XXXX' def get_file_handle(self): #### get file handle #### inode_id=id(self) # Inode instance ID is used as 'file handle' if not hasattr(self,'header'): # If normal inode header_buffers.add(inode_id,self) # inode(instead of header) is bufferd in header_buffers return inode_id # return ID as file handle ### Inode Dir access functions ### def set_dentry(self,name,inode): #### set inode to the dir entry #### i_dentry=getattr(self,'i_dentry',None) # if i_dentry==None: self.i_dentry={name:inode} # empty i_dentry, create it, and 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 inode from dir entry (error if not it) #### def pop_dentry(self,name): return getattr(self,'i_dentry',{}).pop(name) #### pop(get & delete) inode from dir entry (no error if not it) #### def list_dentry(self): return getattr(self,'i_dentry',{}).keys() #### list dir entry (do not contain '.' and '..') #### def len_dentry(self): return len(getattr(self,'i_dentry',{})) #### get entry number for dir delete check (do not contain '.' and '..') #### ### Inode Xattr access functions ### def set_xattr(self,name,val): #### set parameter to the xattr entry #### try:self.i_xatt[name]=val except AttributeError:self.i_xattr={name:val} 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) #### pop(get&delete) xattr entry #### def ls_xattr(self): return getattr(self,'i_xattr',{}).keys() #### list xattr entry #### ### Inode Attr access 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): try: self.st_nlink+=1 except AttributeError: self.st_nlink=2 def dec_nlink(self): try: self.st_nlink-=1 except AttributeError: 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): #### get dir type (for fast access parent dir) #### try:return self.header.get_dir_type(self) # header template know 'other X' or not. except AttributeError:return False # 'False' mean 'I don't know.' def get_items(self,keys=('st_mode','st_uid','st_gid')): #### get items (for permittion check) #### return [val for key,val in self.items(keys)] # get 'mode','uid','gid' for permission check def items(self,keys=('st_mode','st_uid','st_gid','st_rdev','st_atime', #### items #### 'st_ctime','st_mtime','st_ino','st_size','st_nlink')): # called from fuse.py for stat file header=None for key in keys: if key=='st_ino': yield ('st_ino',id(self)) # ino is ID of the Inode instance elif key=='st_nlink': yield (key,getattr(self,key,1)) # 'no nlink' means nlink=1 elif key=='st_size': # fix me ! How do i treat long file? 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 fix me ! please use 'try' val=header.get(key,0) # read the data length from archive file else:val=len(val) yield (key,val) else: # other parateters are in 'Inode instance' or 'Header instance' try:yield (key,getattr(self,key)) # Inode instance already knows 'key' except AttributeError: # Inode instance doesn't know 'key' yet try:yield (key,header.get(key,0)) # Header instance has 'key' instead except AttributeError: # 'header' is 'None' yet header=self.get_header() # get 'header' from archive file yield (key,header.get(key,0)) # Header instance has 'key'. ### Inode File IO functions ### def get_header(self): try: return self.header.get_real_header(inode=self) # real header is made by copying of template header(use buffer data, if exist) except AttributeError: return {} # No 'self.header'. {} is dummy header for 'header.get(key,0)' def get_inode_data(self,offset=0,size=None): # try:data=self.i_data except AttributeError: # no data has Inode instance header=self.get_header() # if no header -> get header from archive file if header=={}:return array.array('c') 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) 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 try: context=self.fuse_get_context() # get system call uid and gid (fuse context) except AttributeError: context=self.context # befor start file system, use mount owner instead of 'fuse context'. uname=pwd.getpwuid(context[0]).pw_name # get system call uname inode=None if fh != None: header=header_buffers.get(fh) # get header (if not archive inode, get inode) if header !=None:inode=header.get_inode() # not only 'header', but also inode has 'get_inode()' 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) # fix me! 'dir/file' 1st, 'dir' 2nd in archive 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. self.fuse_get_context=fuse_get_context # befor start file system, self.fuse_get_context() makes error. 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 /""" # not store any written data in the file system 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!!! # These should be implemented only if HAVE_SETXATTR is true. # Extended attributes (xattrs) can be set in different namespaces. Linux uses "security", "system", "trusted", and "user". def listxattr(self, path): ##### listxattr (See listxattr(2).) ##### 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).) ##### inode,context,uname,items=self._get_inode_for_access(path) # access permission check try:return inode.get_xattr(name) except KeyError: raise FuseOSError(ENODATA) # (Should return ENOATTR, but may return ENODATA) def setxattr(self, path, name, value, options, position=0): ##### (See setxattr(2).) ##### inode,items=self._get_inode_for_user(path) # user check inode.set_xattr(name,value) # Ignore options def removexattr(self, path, name): ##### removexattr () ##### inode,items=self._get_inode_for_user(path) # user check try:inode.pop_xattr(value) except KeyError: raise FuseOSError(ENODATA) # (Should return ENOATTR, but may return ENODATA) ######## FUSE Operating function (Read Dir) ######## def opendir(self, path): ###### open dir ###### inode,context,uname,items=self._get_inode_for_access(path) # access permission check if not S_ISDIR(items[0]): raise FuseOSError(ENOTDIR) # (Not a directory) self._is_inode_R_OK(inode,context,uname,items) # read permission check return inode.get_file_handle() # return fd def readdir(self, path, fh): ###### read dir ###### inode,items=self._get_inode_for_read(path,fh) # read permission check if not S_ISDIR(items[0]): raise FuseOSError(ENOTDIR) # (Not a directory) dirnames=inode.list_dentry() # read dir list dirnames.extend(['..','.']) # append '..' and '.' 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): ###### make dir ###### 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): ###### remove dir (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(ENOTDIR) # (Not a directory) if filename == '/': 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): ###### make node ###### # 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! Shuld I 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.get_file_handle() # 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 of the dir's inode items=inode.get_items() if S_ISDIR(items[0]): raise FuseOSError(EISDIR) # (Is a directory) 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 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 for target's dir inode,context,uname,items=self._get_inode_for_access(source) # we can make(or delete) link of other's file if S_ISDIR(items[0]): raise FuseOSError(EISDIR) # (Is a directory) now=time() # get time now 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(EISDIR) # (Is a directory) 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.get_file_handle() 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(EISDIR) # (Is a directory) 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) ################################################################################################################# # # # Local 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]) ################################################################################################################# # # # Header Afio Base Class # # # ################################################################################################################# class Header_Afio_Base(object): ID=6 # afio ID size LEN=0 # afio header size s1= "" # afio header name list s2= "" # afio header name length list header_base_buffer={} # header base buffer (header temlpate) def __init__(self,abspath): #### initialize #### self.abspath=abspath # abspath of the archive file ######################################################################################################### # # # Template Header Function # # # ######################################################################################################### def get_real_header(self,inode): #### get real header (template header function) #### inode_id=id(inode) real_header=header_buffers.get(inode_id) # use one in the header buffer, if exist if real_header==None: # when there is no one real_header=copy.copy(self) # copy instance real_header._init_real_header(inode) # and initialize header_buffers.add(inode_id,real_header) # add header to header buffer return real_header def _get_template_header(self): #### get template header (template header function) #### keys=['abspath','st_compress','dir_type'] header_id=[getattr(self,key,None) for key in keys] header_id.append(id(self.__class__)) # information of header(070707,070717,070727) header_id=tuple(header_id) try:return self.header_base_buffer[header_id] # return header in header base buffer except KeyError: copied_header=copy.copy(self.inode.header) for key in keys: try:setattr(copied_header,key,getattr(self,key)) # copy header parameters if exist except AttributeError:pass self.header_base_buffer[header_id]=copied_header return copied_header # return new template header def get_dir_type(self,inode): #### get_dir_type (template header function) #### try:return self.dir_type except AttributeError: header=self.get_real_header(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._get_template_header() # change inode's header for next time return dir_type # return other X OK(True) or not(False) else: False # this is not dir(False) ######################################################################################################### # # # Real Header Function # # # ######################################################################################################### def _init_real_header(self,inode): #### initialize real header (real header function) #### 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 _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_data_address(self): return self.header_address+self.LEN+self.get_st_nlen() #### get data address in the archive #### def get_next_addr(self): return self._get_data_address()+self.get_st_raw_size() #### get next ID address (call from loader) #### def _get_hdr2(self,name='',s=0,e=0): #### get hdr2 or hdr2 parameter #### if name != '': try:return getattr(self,name) except AttributeError: try:hdr2=self.hdr2 except AttributeError: hdr2=self._read_data(self.header_address,self.LEN) self.hdr2=hdr2 val=int(hdr2[s:e],8);setattr(self,name,val) return val try:hdr2=self.hdr2 except AttributeError: hdr2=self._read_data(self.header_address,self.LEN) self.hdr2=hdr2 if e != 0: return int(hdr2[s:e],8) else: return self.hdr2 def print_header(self): #### print afio header (real header function) #### print self.s1 print self.s2 print "%s%s" % (self._get_hdr2(),self.get_st_path()) def get_inode(self): return getattr(self,'inode') #### get inode (copied header must know 'inode') #### ######################################################################################################### # # # Get Parameter Function # # # ######################################################################################################### def get(self,name,default): #### get data like as dict #### try:return getattr(self,name) # Inode instance already knows 'key' except AttributeError: # Inode instance doesn't know 'key' yet try:return getattr(self,'get_'+name)() # Inode instance has function to get 'key' except AttributeError:return default # Inode instance doesn't have the function def get_st_compress(self): #### compressed or not #### try:return self.st_compress except AttributeError: # Inode instance doesn't know 'key' yet 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._get_template_header() # change inode header fot next time return st_compress 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_size(self): #### get size #### if self.get_st_compress(): return self.get_st_comp_size() else: return self.get_st_raw_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) ######################################################################################################### # # # Raw/Comp Function # # # ######################################################################################################### def get_st_comp_size(self): #### get 'decompressed data size' of the compressed data #### try:return self.st_comp_size[0][-1] # already know the size except AttributeError: st_compress=self.st_compress try:return self.st_compress[0](self) # already know get size function except TypeError: # ('bool' object is unsubscriptable) s=self.get_st_raw_data(size=16).tostring() if s[0:9] == '\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a': # lzop self.st_compress=(Header_Afio_Base._get_lzop_size, Header_Afio_Base._get_lzop_block, 2,'lzop','-dc') elif (s[0:2] == '\x1f\x8b') or (s[0:2] == '\x1f\x9d') or (s[0:4] == '\x50\x41\x43\x4b'): # gzip,compress,pack self.st_compress=(Header_Afio_Base._get_gzip_size, Header_Afio_Base._get_block, 2,'gzip','-dc') elif s[0:6] == '\xfd7zXZ\x00': # xz self.st_compress=(Header_Afio_Base._get_xz_size, Header_Afio_Base._get_block, 2,'xz','-dc') elif s[0:3] == 'BZh': # bzip2 self.st_compress=(Header_Afio_Base._get_size, Header_Afio_Base._get_block, 2,'bzip2','-dc') else:raise FuseOSError(EIO) # Other I/O Error self.inode.header=self._get_template_header() # return self.st_compress[0](self) # return decompressed file size def get_st_raw_data(self,offset=0,size=None): #### get raw data #### data_offset=self._get_data_address()+offset max_size=self.get_st_raw_size()-offset if size==None: size=max_size else: size=min(max_size,size) data=array.array('c') f=open(self.abspath,"r") f.seek(data_offset) try:data.fromfile(f,size) except EOFError:pass f.close() return data def get_st_comp_data(self,offset=0,size=None): #### get decompressed data of the compressed data #### if self.st_compress==True:self.get_st_comp_size() # get_st_comp_size() makes 'self.st_compress' to list lzop_data=self.st_comp_size[0] # (1st Bloak END,2nd Bloak END,..) if size==None:size=lzop_data[-1]-offset # Last Bloak END endoffset=offset+size # if offset return '' data=self._get_block_buffered(self._get_block,start_block)[offset:] if endoffset>lzop_data[start_block]: # need next block for end_block in xrange(start_block+1,len(lzop_data)): data.extend(self._get_block_buffered(self._get_block,end_block)) # fix me ! for long data if endoffset=7:return st_compress[5:7] # return lzop_header_size,lzop_flags s=self.get_st_raw_data(size=512) 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: 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 # No 'EXTRA_FIELD' in lzop yet 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._get_template_header() # change inode's header for next time return lzop_header_size,lzop_flags def _get_lzop_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 # IO error lzop_data_len=0 offset=self._get_data_address() size=self.get_st_raw_size() f=open(self.abspath); seek=f.seek;read=f.read lzop_raw_data=[]; raw_append=lzop_raw_data.append # (1st Bloak END,2nd Bloak END,..) lzop_data=[]; data_append=lzop_data.append # (Header END,1st Bloak END,.....) No tailer END F_ADLER32_D=0x00000001L F_ADLER32_C=0x00000002L F_CRC32_D=0x00000100L F_CRC32_C=0x00000200L raw_append(lzop_raw_len) while 1: # sum each block size if lzop_raw_len>size:raise FuseOSError(EIO) # (I/O error) seek(offset+lzop_raw_len) s=read(8) d_len=(ord(s[0])<<24)|(ord(s[1])<<16)|(ord(s[2])<<8)|ord(s[3]) if d_len==0:break c_len=(ord(s[4])<<24)|(ord(s[5])<<16)|(ord(s[6])<<8)|ord(s[7]) 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 # elif ? 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 # elif ? data_append(lzop_data_len) raw_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_block(self,i): #### decompress lzop data block #### lzop_data,lzop_raw_data=self.st_comp_size args=self.st_compress[3:self.st_compress[2]+3] # args=('lzop','-dc') 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 lzop) proc.stdout.close() data=self.get_st_raw_data(0,lzop_raw_data[0]) # lzop header data.tofile(proc.stdin) # proc.stdin.write(data) data=self.get_st_raw_data(lzop_raw_data[i],lzop_raw_data[i+1]-lzop_raw_data[i]) # one of the lzop data block data.tofile(proc.stdin) # proc.stdin.write(data) proc.stdin.write('\0\0\0\0') # lzop data end(next decompress data block size = 0) proc.stdin.flush() proc.stdin.close() os._exit(0) proc.stdin.close() # parent (read decompressed data from lzop) data_block=array.array('c') if i == 0 : size=lzop_data[0] else: size=lzop_data[i]-lzop_data[i-1] try:data_block.fromfile(proc.stdout,size) # data_block=proc.stdout.read() except EOFError:pass 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 ######################################################################################################### # # # Xz Function # # # ######################################################################################################### def _get_xz_integer(self,s,i0): i=0 n=ord(s[i0]) & 0x7F while ord(s[i+i0]) & 0x80: i +=1 if i >= 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_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 Function # # # ######################################################################################################### def _get_gzip_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 """ 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) """ ################################################################################################################# # # # Header Afio Class # # # ################################################################################################################# #class Header_Afio_Binary70707(Header_Afio_Base): ### 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_load(object): """Load afio file(abspath) to target path in the filesystem""" def __init__(self,target,abspath,filesystem): self.init_header(target,abspath,filesystem) 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() def init_header(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 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 inode_id=id(inode) header_buffers.add_rev(inode_id,header) # add header to header buffer # print "seek XXX ----> " ,header.get_next_addr() return header.get_next_addr() class Afio_load_B(Afio_load): """Load afio file(abspath) to target path in the filesystem""" command='afio -tvBZ "%s"' def __init__(self,target,abspath,filesystem): self.init_header(target,abspath,filesystem) proc=subprocess.Popen(command % 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) class Afio_load_Bk(Afio_load_B): """Load afio file(abspath) to target path in the filesystem""" command='afio -tvBkZ "%s"' ################################################################################################################# # # # FUSE Operation Afiolzofs Class # # # ################################################################################################################# class Afiolzofs(object): """Base Class of the Afio Lzo Filesystem""" def _check_symlink(self,target,source): ####### Symlink Check (root of the file system or not) ####### print "target,source=",target,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. this is made automaticaly 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 print "target,source",target,source 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(object): def __init__(self,num1=10): self.num=num1 # max items num self.data={} # buffer dictionary self.start=[None,None,None,None] # root of the itemlists [value,before item,next item,hold key] self.start[1]=self.start # before item self.start[2]=self.start # next item def get(self,key,default=None): #### get value in the buffer #### data=self.data itemlist=data.get(key,None) if itemlist == None:return None # not extst in the buffer else: # exist in the buffer start=self.start # set the itemlist to the end of the itemlist chain before=itemlist[1] next=itemlist[2] before[2]=next next[1]=before last=start[1] last[2]=itemlist itemlist[1]=last itemlist[2]=start start[1]=itemlist return itemlist[0] # return value def add(self,key,val,hold=None): #### add value to the buffer end #### data=self.data if not key in data: start=self.start before=start[1] itemlist=[val,before,start,key,hold] start[1]=itemlist before[2]=itemlist data[key]=itemlist num=self.num while len(data)>num: # delete 1st item, if too many items itemlist=start[2] next=itemlist[2] start[2]=next next[1]=start data.pop(itemlist[3]) def add_rev(self,key,val,hold=None): #### add value to the buffer top #### data=self.data if (not key in data) and (len(data)' % 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.' def main(args,command='afiolzofs.py'): if len(argv) < 2:print_usage(command='afiolzofs.py');exit(1) # print usage mountpoint=os.path.abspath(args.pop()) key=('--logging','--nosymlink','--ramfs','--debug','--foreground','--nothreads','--allow_other','--allow_root','--kernel_cache') if key[0] in args: # with logging if key[1] in args: # without symlink loading if key[2] in args: afiolzofs= LogAfiolzoRamfs(mountpoint) # act as ram fs else: afiolzofs= LogAfiolzoRomfs(mountpoint) # act as rom fs else: # with symlink loading if key[2] in args: afiolzofs= LogAfiolzoSLRamfs(mountpoint) # act as ram fs else: afiolzofs= LogAfiolzoSLRomfs(mountpoint) # act as rom fs else: # without logging if key[1] in args: # without symlink loading if key[2] in args: afiolzofs= AfiolzoRamfs(mountpoint) # act as ram fs else: afiolzofs= AfiolzoRomfs(mountpoint) # act as rom fs else: # with symlink loading if key[2] in args: afiolzofs= AfiolzoSLRamfs(mountpoint) # act as ram fs else: afiolzofs= AfiolzoSLRomfs(mountpoint) # act as rom fs keyargs={} # args for FUSE if key[3] in args: keyargs[key[3][2:]]=True # debug if (key[4] in args)or(key[0] in args): keyargs[key[4][2:]]=True # foreground if key[5] in args: keyargs[key[5][2:]]=True # nothreads if key[6] in args: keyargs[key[6][2:]]=True # allow other if (key[7] in args)and(not key[6] in args): keyargs[key[7][2:]]=True # allow root if key[8] in args: keyargs[key[8][2:]]=True # kernel cache # 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 buffer data_buffers=Buffers(20) # max buffer num 20 global header_buffers # header buffer header_buffers=Buffers(200) # max buffer num 200 for i in args: # mount afio files in the 'args' sourcename=os.path.abspath(i) basename=os.path.basename(sourcename) # make symlink name # Afio_load('/'+basename,sourcename,afiolzofs) afiolzofs._symlink('/'+basename,sourcename) fuse = FUSE_array(afiolzofs, mountpoint,use_ino=True,**keyargs) # fuse operation start if __name__ == "__main__": main(argv[1:],argv[0])