#!/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.12 (06 Dec 2010) # fix some error # 0.0.11 (05 Dec 2010) # x permission # 0.0.10 (04 Dec 2010) # r,w permission # 0.0.9 (03 Dec 2010) # test implementation for permission to dirread # 0.0.8 (02 Dec 2010) # fix some error # 0.0.7 (01 Dec 2010) # fix some of the atime,mtime,ctime # fix some error code # 0.0.6 (30 Nov 2010) # fix some of the atime,mtime,ctime # use get_fuse_context # 0.0.5 (29 Nov 2010) # list many item shuld be fix. # fix some of them. # 0.0.4 (27 Nov 2010) # support extended ASCII format # fix "unlink error" # fix "other magic number" # not to use defaultdict # fix "rename error" # change inode structure # 0.0.3 (26 Nov 2010) # fix "cannot mount XXXXXX.afio.lzo" # 0.0.2 (26 Nov 2010) # BUG fix "REG FILE" # 0.0.1 (21 Nov 2010) # New release # This program is based on fusepy. And it is started by coping the 'fuse.py' and 'memory.py' # # Copyright of the fuse.py and memory.py # # Copyright (c) 2008 Giorgos Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #from collections import defaultdict from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISCHR, S_ISBLK # function from stat import S_IMODE # or 0007777 os.chmod() で設定することのできる一部のファイルモード from stat import S_IFMT # or 0170000 ファイル種別を示すビット領域を表すビットマスク # from stat import S_IFSOCK # 0140000 ソケットファイル from stat import S_IFLNK # 0120000 シンボリックリンク from stat import S_IFREG # 0100000 通常ファイル from stat import S_IFBLK # 0060000 ブロック型デバイスファイル from stat import S_IFDIR # 0040000 ディレクトリ from stat import S_IFCHR # 0020000 キャラクタ型デバイスファイル from stat import S_IFIFO # 0010000 パイプ from stat import S_ISUID # 0004000 セットUID from stat import S_ISGID # 0002000 セットGID from stat import S_ISVTX # 0001000 ステッキービット。ファイルの名前を変更したり削除したりできるのはそのファイルの所有者、ディレクトリの所有者、特権プロセスだけとなる from stat import S_IRWXU # 00700 ファイル所有者のアクセス許可用のビットマスク from stat import S_IRUSR # 00400 所有者の読み込み許可 from stat import S_IWUSR # 00200 所有者の書き込み許可 from stat import S_IXUSR # 00100 所有者の実行許可 from stat import S_IRWXG # 00070 グループのアクセス許可用のビットマスク from stat import S_IRGRP # 00040 グループの読み込み許可 from stat import S_IWGRP # 00020 グループの書き込み許可 from stat import S_IXGRP # 00010 グループの実行許可 from stat import S_IRWXO # 00007 他人 (others) のアクセス許可用のビットマスク from stat import S_IROTH # 00004 他人の読み込み許可 from stat import S_IWOTH # 00002 他人の書き込み許可 from stat import S_IXOTH # 00001 他人の実行許可 from errno import * from sys import argv, exit from time import time,mktime ,strptime from fuse import FUSE, FuseOSError, Operations, LoggingMixIn ,fuse_get_context import subprocess import pwd import grp import os """ 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) """ #### Get information #### def _lzop(data,compress=False): if compress: args=['lzop','-c9'] else: args=['lzop','-dc'] proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True, ) pid = os.fork() if pid==0: # child proc.stdout.close() proc.stdin.write(data) proc.stdin.flush() proc.stdin.close() os._exit(os.EX_OSERR) else: # parent proc.stdin.close() data=proc.stdout.read() proc.stdout.close() return data def _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]) #### FUSE Operation class #### class Afiolzofs(LoggingMixIn, Operations): # for debug #class Afiolzofs(Operations): # for normal use """Example memory filesystem. Supports only one level of files.""" def __init__(self,mountpoint): self.mountpoint=mountpoint self.ino = {} # for read afio file self.fd = 0 now = time() inode = dict( st_mode=(S_IFDIR | 0755), st_atime=now, # access time shuld be changed with read (not be changed with write, open or read without permision) st_mtime=now, # modify time shuld be changed with write or truncate st_ctime=now, # change time shuld be changed with write, rename, truncate, chmod, link or chown st_uid=os.getuid(), st_gid=os.getgid(), st_nlink=2, i_data={}, ) inode['st_ino']=id(inode) inode['i_data']['.']=inode self.inode = inode ######## Load Afio file function. without Changing atime,mtime,ctim and Permission checking ######## def _getinode(self,path): ###### Get inode without Permission check ###### if path=="/": dentry=["/","."] return self.inode else: dentry=os.path.split(path) try: inode_dir=self._getinode(dentry[0]) inode=inode_dir['i_data'][dentry[1]] return inode except: raise FuseOSError(ENOENT) # No such file or directory def _addfile_inode(self,path,inode): dentry=os.path.split(path) inode_dir=self._getinode(dentry[0]) inode_dir['i_data'][dentry[1]] = inode def _adddir_inode(self,path,inode): dentry=os.path.split(path) inode_dir=self._getinode(dentry[0]) inode_dir['i_data'][dentry[1]] = inode inode_dir['st_nlink'] += 1 def _setnlink(self,inode): ino=inode['i_ino'] inode2=self.ino.get(ino,None) if inode2==None: self.ino[ino]=inode return inode,True else: return inode2,False def read_afio_item(self,inode): f=open(inode['i_abspath'],"r") f.seek(inode['i_startaddr']) data=f.read(inode['i_readsize']) f.close() compfunc=inode['i_compfunc'] if compfunc != None:data=compfunc(data) return data def load_afio_file(self,f=None,seek=None,target="",abspath=""): f.seek(seek) hdr=f.read(6) if hdr=='070707': # old ASCII magic number hdr2=f.read(70) # length: 70 inode=dict( i_dev= int(hdr2[0:6],8), i_ino= int(hdr2[6:12],8), st_mode= int(hdr2[12:18],8), st_uid= int(hdr2[18:24],8), st_gid= int(hdr2[24:30],8), st_nlink= int(hdr2[30:36],8), rdev= int(hdr2[36:42],8), st_mtime= int(hdr2[42:53],8), i_namelength= int(hdr2[53:59],8), st_size= int(hdr2[59:70],8), ) inode['st_ctime']=inode['st_mtime'] inode['st_atime']=inode['st_mtime'] inode['i_readsize']=inode['st_size'] inode['i_filepath']=f.read(inode['i_namelength'])[:-1] # next field is path name inode['i_startaddr']=seek+6+70+inode['i_namelength'] # calc data address #print id(inode) print "|23456|23456|23456|23456|23456|23456|23456|23456|23456789ab|23456|23456789ab|..." print "| hdr| dev| ino| mode| uid| gid|nlink| rdev| mtime|nmlen| size|pathname" print "%s%s%s" % (hdr,hdr2,inode['i_filepath']) elif hdr=='070717': # extended ASCII magic number hdr2=f.read(75) # length 75 inode=dict( i_dev= int(hdr2[0:6],8), i_ino= int(hdr2[6:17],8), st_mode= int(hdr2[17:23],8), st_uid= int(hdr2[23:29],8), st_gid= int(hdr2[29:35],8), st_nlink= int(hdr2[35:41],8), # rdev= int(hdr2[41:47],8), st_mtime= int(hdr2[47:58],8), i_namelength= int(hdr2[58:64],8), st_size= int(hdr2[64:75],8), ) inode['st_ctime']=inode['st_mtime'] inode['st_atime']=inode['st_mtime'] inode['i_readsize']=inode['st_size'] inode['i_filepath']=f.read(inode['i_namelength'])[:-1] # next field is path name inode['i_startaddr']=seek+6+75+inode['i_namelength'] # calc data address print "|23456|23456|23456789ab|23456|23456|23456|23456|23456|23456789ab|23456|23456789ab|..." print "| hdr| dev| ino| mode| uid| gid|nlink| rdev| mtime|nmlen| size|pathname" print "%s%s%s" % (hdr,hdr2,inode['i_filepath']) elif hdr=='070727': # large ASCII magic number not implemented yet hdr2=f.read(110) # length 110 print "|23456|2345678|234567890123456m|23456|2345678|2345678|2345678|2345678|234567890123456n|234|234|234s|234567890123456:|..." print "| hdr| dev| inoM| mod| uid| gid| nlink| rdev| mtimeN|nml|flg|xszS| size:|pathname" print "%s%s" % (hdr,hdr2) inode={} return elif hdr=='070701': # cpio new ASCII magic number not implemented yet hdr2=f.read(110) # length: 110 inode={} return elif hdr=='070702': # cpio new ASCII magic number with CRC not implemented yet #hdr2=f.read(110) # length: ?? inode={} return elif hdr=='070703': # Tcpio magic number of TI/E not implemented yet #hdr2=f.read(110) # length: ?? return else: return inode['st_ino']=id(inode) # experimental inode['i_readfunc']=self.read_afio_item # use this, if read. inode['i_compfunc']=None # None means raw data, not compress inode['i_abspath']=abspath # abspath of the afio.lzo file <- shuld be list or instance mode=inode['st_mode'] if S_ISDIR(mode): # DIR if inode['st_nlink']>2:inode,new=self._setnlink(inode) path='%s/%s' % (target,inode['i_filepath']) inode['i_data']={} # for DIR ({}: real empty dir entry) self._adddir_inode(path,inode) elif S_ISLNK(mode): # SLINK if inode['st_nlink']>1:inode,new=self._setnlink(inode) else: new=True if new:inode['i_data'] = f.read(inode['i_readsize']) # for Symlink ("xxx": linked source name) path='%s/%s' % (target,inode['i_filepath']) self._addfile_inode(path,inode) elif S_ISREG(mode): # REG FILE # '.z' and 'rdev & 1==0' mean compressed file in afio header if ((inode['rdev'] & 1)== 0) and (inode['i_filepath'][-2:] =='.z'): # Chake Compressed or Not. inode['i_filepath']=inode['i_filepath'][:-2] # remove ".z" size=inode['i_readsize'] if size>50:size=50 # read first 50 bytes top50=f.read(size) filetype=_filetype(top50) if filetype=='lzop compressed data': inode['i_compfunc']=_lzop # read size from lzop header inode['st_size']=int("%02x%02x%02x%02x"%(ord(top50[38]),ord(top50[39]),ord(top50[40]),ord(top50[41])),16) inode['i_data']=None # for REG FILE (None: not read data yet) path='%s/%s' % (target,inode['i_filepath']) if inode['st_nlink']>1:inode,new=self._setnlink(inode) self._addfile_inode(path,inode) print "============================== normal file ",path elif (S_ISCHR(mode) or S_ISBLK(mode)) : # S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO self._addfile_inode(path,inode) else: path='%s/%s' % (target,inode['i_filepath']) print "============ not implimented ===================",path def load_afio_files(self,targetdir,sourceabspath): pipe=subprocess.Popen('afio -tvBZ "%s"' % sourceabspath, shell=True,stdout=subprocess.PIPE).stdout self.ino = {} # clear ino dict f=open(sourceabspath,"r") for line in pipe: i=int(line.split(None,2)[0]) self.load_afio_file(f,i,targetdir,sourceabspath) f.close() ######## Temporaly Randisk function (Permission check) ######## def is_inode_R_OK(self,inode,context,uname): mode=inode['st_mode'] if not (mode & S_IROTH) and not((mode & S_IRUSR) and (inode['st_uid'] == context[0])): if (mode & S_IRGRP): if not(uname in grp.getgrgid(inode['st_gid']).gr_mem): if context[1] != inode['st_gid']: raise FuseOSError(EACCES) # (Permission denied) else: raise FuseOSError(EACCES) # (Permission denied) def is_inode_W_OK(self,inode,context,uname): mode=inode['st_mode'] if not (mode & S_IWOTH) and not((mode & S_IWUSR) and (inode['st_uid'] == context[0])): if (mode & S_IWGRP): if not(uname in grp.getgrgid(inode['st_gid']).gr_mem): if context[1] != inode['st_gid']: raise FuseOSError(EACCES) # (Permission denied) else: raise FuseOSError(EACCES) # (Permission denied) def is_inode_X_OK(self,inode,context,uname): mode=inode['st_mode'] if not (mode & S_IXOTH) and not((mode & S_IXUSR) and (inode['st_uid'] == context[0])): if (mode & S_IXGRP): if not(uname in grp.getgrgid(inode['st_gid']).gr_mem): if context[1] != inode['st_gid']: raise FuseOSError(EACCES) # (Permission denied) else: raise FuseOSError(EACCES) # (Permission denied) ######## Temporaly Randisk function (Get inode with Permission check) ######## def _get_inode_for_access(self,path): ###### Get inode for Access ###### context=fuse_get_context() # get system call uid and gid uname=pwd.getpwuid(context[0]).pw_name # get system call uname if path=="/": dentry=["/","."] inode=self.inode else: dentry=os.path.split(path) inode_dir,context,uname=self._get_inode_for_access(dentry[0]) # fix me !!! evry time call context self.is_inode_X_OK(inode_dir,context,uname) # dir access permission check try:inode=inode_dir['i_data'][dentry[1]] except:raise FuseOSError(ENOENT) # (No such file or directory) return inode,context,uname def _get_inode_for_read(self,path): ###### Get inode for Read ###### inode,context,uname=self._get_inode_for_access(path) # access permission check self.is_inode_R_OK(inode,context,uname) # read permission check return inode def _get_inode_for_write(self,path): # inode,context,uname=self._get_inode_for_access(path) # access permission check self.is_inode_W_OK(inode,context,uname) # write permission check return inode def _get_dir_inode_for_create(self,path): # dentry=os.path.split(path) # split to dirpath and filename inode,context,uname=self._get_inode_for_access(dentry[0]) # dir access permission check self.is_inode_W_OK(inode,context,uname) # dir write permission check return inode,dentry[1] # return inode_dir and filename def _get_inode_for_delete(self,path): # dentry=os.path.split(path) # split to dirpath and filename inode,context,uname=self._get_inode_for_access(dentry[0]) # dir access permission check self.is_inode_W_OK(inode,context,uname) # dir write permission check now=time() inode_file=inode['i_data'][dentry[1]] return inode_file,inode,dentry[1],now # return inode_file, inode_dir, filename and now def _get_inode_for_create(self,path): # dentry=os.path.split(path) # split to dirpath and filename inode,context,uname=self._get_inode_for_access(dentry[0]) # dir access permission check self.is_inode_W_OK(inode,context,uname) # dir write permission check now=time() inode_file = dict( # set default common parameters st_nlink=0,st_size=0, # nlink and size st_ctime=now,st_mtime=now,st_atime=now, # ctime,mtime,atime st_uid=context[0],st_gid=context[1], # uid,gid ) inode_file['st_ino']=id(inode) # inode number <-- address of inode_file return inode_file,inode,dentry[1],now # return inode_file, inode_dir, filename and now ######## Temporaly Randisk function (inode Operation) ######## def _update_atime(self,inode): # read,readdir,readlink -> update atime now = time() inode['st_atime']=now # read dir list -> change atime def _write_filedata(self,inode,filedata): # now=time() inode['i_data'] = filedata inode['st_mtime'] = now # write file data -> change mtime inode['st_size'] = len(filedata) inode['st_ctime'] = now # change size -> change ctime def _add_inode_as_dir(self,inode_dir,inode,filename,now): # inode_dir['i_data'][filename] = inode inode_dir['st_mtime'] = now # write to dir list -> change mtime inode_dir['st_nlink'] += 1 inode_dir['st_ctime'] = now # change nlink -> change ctime inode['st_nlink'] += 1 inode['st_ctime']=now # change nlink -> change ctime def _add_inode_as_file(self,inode_dir,inode,filename,now): # inode_dir['i_data'][filename] = inode inode_dir['st_mtime'] = now # write to dir list -> change mtime inode_dir['st_ctime']=now # ??? inode['st_nlink'] += 1 inode['st_ctime']=now # change nlink -> change ctime def _removefile_inode(self,inode,inode_dir,filename,now): # inode_dir['i_data'].pop(filename) inode_dir['st_mtime']=now # change dir list -> change mtime inode_dir['st_nlink'] -= 1 inode_dir['st_ctime']=now # ??? inode['st_nlink'] -= 1 inode['st_ctime']=now # change nlink -> change ctime inode['st_mtime']=now # ??? def _removedir_inode(self,inode,inode_dir,filename,now): # if name == '/': # Shuld be check root? ?? raise FuseOSError(EPERM) # Operation not permitted elif len(inode['i_data'])>=2: # Attention!!! empty means only '.' in the Dir list raise FuseOSError(ENOTEMPTY) # Directory not empty else: inode_dir['i_data'].pop(filename) inode_dir['st_mtime']=now # change dir list -> change mtime inode_dir['st_nlink'] -= 1 inode_dir['st_ctime']=now # change nlink -> change ctime # not for rmdir, but for rename. inode['st_nlink'] -= 1 inode['st_ctime']=now # change nlink -> change ctime inode['st_mtime']=now # change dir list '..' -> change mtime ######## Root Symlink Check ######## def _check_symlink(self,target,source): targetpath=os.path.split(target) if targetpath[0]=="/": # if target is root of the mountpoint targetname=".%s" % targetpath[1] targetdir=os.path.join(targetpath[0],targetname) sourceabspath=os.path.normpath(os.path.join(self.mountpoint,source)) f=open(sourceabspath) top50=f.read(50);f.close() filetype=_filetype(top50) if "ASCII cpio archive": st=os.stat(sourceabspath) inode,inode_dir,filename,now=self._get_inode_for_create(targetdir) # write permission check inode['st_mode']= S_IMODE(0755) | S_IFDIR # set mode (dir) inode['i_data']={'.':inode} # add '.' link to self inode['st_nlink'] +=1 # add nlink 1 inode['st_ctime']=st.st_ctime inode['st_mtime']=st.st_mtime inode['st_atime']=st.st_atime inode['st_uid']=st.st_uid inode['st_gid']=st.st_gid inode['st_blksize']=st.st_blksize # fix me ! inode['st_blocks']=st.st_blocks # fix me ! inode['st_dev']=st.st_dev # fix me ! inode['st_ino']=st.st_ino # fix me ! inode['st_rdev']=st.st_rdev # fix me ! inode['st_size']=st.st_size # fix me ! self._add_inode_as_dir(inode_dir,inode,filename,st.st_ctime)# add inode as dir self.load_afio_files(targetdir,sourceabspath) return targetname # afio.lzo file else: return source # normal symlink return source def _check_symlinkX(self,target,source): targetpath=os.path.split(target) if targetpath[0]=="/": # if target is root of the mountpoint targetname=".%s" % targetpath[1] targetdir=os.path.join(targetpath[0],targetname) sourceabspath=os.path.normpath(os.path.join(self.mountpoint,source)) f=open(sourceabspath) top50=f.read(50);f.close() filetype=_filetype(top50) if "ASCII cpio archive": st=os.stat(sourceabspath) inode=dict( st_mode=(S_IFDIR | 0755), st_ctime=st.st_ctime, st_mtime=st.st_mtime, st_atime=st.st_atime, st_uid=st.st_uid, st_gid=st.st_gid, st_nlink=2, st_blksize=st.st_blksize, st_blocks=st.st_blocks, st_dev=st.st_dev, st_ino=st.st_ino, st_rdev=st.st_rdev, st_size=st.st_size, ) self._adddir(targetdir,inode) self.load_afio_files(targetdir,sourceabspath) return targetname else: return source return source ######## FUSE Operating function (Start File System) ######## def init(self, path): """Called on filesystem initialization. Path is always / Use it instead of __init__ if you start threads on initialization.""" pass def statfs(self, path): return dict(f_bsize=512, f_blocks=4096, f_bavail=2048) def destroy(self, path): """Called on filesystem destruction. Path is always /""" pass ######## FUSE Operating function (Attr) ######## # fgetattr ??? what is fgetattr? def access(self, path, amode): inode,context,uname=self._get_inode_for_access(path) # access permission check return 0 # ???? def getattr(self, path, fh=None): ###### getattr ###### inode,context,uname=self._get_inode_for_access(path) # access permission check return inode # return inode # fix me ! inode contain many other data. use xattr! def chmod(self, path, mode): ###### chmod ###### inode,context,uname=self._get_inode_for_access(path) # access permission check inode['st_mode'] = S_IFMT(inode['st_mode']) | S_IMODE(mode) # see S_IFMT,S_IMODE now=time() inode['st_ctime'] = now # change inode data -> change ctime return 0 # return 0 # ??? def chown(self, path, uid, gid): ###### chown ###### inode,context,uname=self._get_inode_for_access(path) # access permission check if uid != -1:inode['st_uid'] = uid # set uid with check -1 if gid != -1:inode['st_gid'] = gid # set gid with check -1 now=time() inode['st_ctime'] = now # change inode data -> change ctime def utimens(self, path, times=None): ###### utime ###### # atime mtime can be changed by touch (utimes),but not ctime # In BSD symlink's atime,mtime,ctime is can changed with lutimes, but not in Linux because no lutimes. inode,context,uname=self._get_inode_for_access(path) # access permission check now = time() atime, mtime = times if times else (now, now) inode['st_atime'] = atime inode['st_mtime'] = mtime # ctime <-- now ? ######## FUSE Operating function (XAttr) ######## def listxattr(self, path): inode,context,uname=self._get_inode_for_access(path) # access permission check attrs = inode.get('attrs', {}) return attrs.keys() def getxattr(self, path, name, position=0): inode,context,uname=self._get_inode_for_access(path) # access permission check attrs = inode.get('attrs', {}) try: return attrs[name] except KeyError: raise FuseOSError(ENOSYS) # Function not implemented def setxattr(self, path, name, value, options, position=0): # Ignore options inode,context,uname=self._get_inode_for_access(path) # access permission check attrs = inode.setdefault('attrs', {}) attrs[name] = value def removexattr(self, path, name): inode,context,uname=self._get_inode_for_access(path) # access permission check attrs = inode.get('attrs', {}) try: del attrs[name] except KeyError: raise FuseOSError(ENOSYS) # Function not implemented ######## FUSE Operating function (Read Dir) ######## def opendir(self, path): """Returns a numerical file handle.""" return 0 def readdir(self, path, fh): ###### readdir ###### inode=self._get_inode_for_read(path) # read permission check if not S_ISDIR(inode['st_mode']):raise FuseOSError(ENOSYS) # Function not implemented dirnames=inode['i_data'].keys() # read dir list dirnames.append('..') # append parent dir '..' self._update_atime(inode) # read dir list -> update 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['st_mode']= S_IMODE(mode) | S_IFDIR # set mode (dir) inode['i_data']={'.':inode} # add '.' link to self inode['st_nlink'] +=1 # add nlink 1 self._add_inode_as_dir(inode_dir,inode,filename,now) # add inode as dir def rmdir(self, path): ###### rmdir ###### inode,inode_dir,filename,now=self._get_inode_for_delete(path) # write permission check if not S_ISDIR(inode['st_mode']):raise FuseOSError(ENOSYS) # Function not implemented (not Dir shuld be delete unlink) self._removedir_inode(inode,inode_dir,name,now) # remove dir ######## FUSE Operating function (Create or Remove File) ######## def mknod(self, path, mode, dev): ###### mknod ###### mask=S_IFSOCK | S_IFBLK | S_IFCHR | S_IFIFO # ??? S_IFREG ? # fix me! if (mode & mask) == 0 :raise FuseOSError(EPERM) # (Operation not permitted) # inode,inode_dir,filename,now=self._get_inode_for_create(path) # write permission check inode['st_mode']= S_IMODE(mode) | (mode & mask) # set node mode # fix me! inode['st_dev']=dev # set dev # fix me! self._add_inode_as_file(inode_dir,inode,filename,now) # add inode as file def create(self, path, mode): ###### create ###### inode,inode_dir,filename,now=self._get_inode_for_create(path) # write permission check inode['st_mode']= S_IFREG | S_IMODE(mode) # set reguler file mode inode['i_data']='' # set no data self._add_inode_as_file(inode_dir,inode,filename,now) # add inode as file self.fd += 1 return self.fd # return fd def unlink(self, path): ###### unlink ###### inode,inode_dir,filename,now=self._get_inode_for_delete(path) # write permission check if S_ISDIR(inode['st_mode']):raise FuseOSError(ENOSYS) # Function not implemented (Dir shuld be delete rmdir) self._removefile_inode(inode,inode_dir,filename,now) # remove file def rename(self, old, new): # change time shuld be changed with write, rename, truncate, chmod, link or chown inode,old_dir,old_name,now=self._get_inode_for_delete(old) # write permission check inode_dir,filename=self._get_dir_inode_for_create(new) # write permission check # If oldpath and newpath are existing hard links referring to the same # file, then rename() does nothing, and returns a success status. */ # EINVAL The new pathname contained a path prefix of the old: # this should be checked by fuse */ # EISDIR newpath is an existing directory, but oldpath is not a directory. */ # ENOTEMPTY newpath is a non-empty directory */ # rt = do_check_empty_dir(e2fs, dest_ino); # ENOTDIR: oldpath is a directory, and newpath exists but is not a # directory */ # Shuld I remove existing "new" ? # d_dest_inode.i_mtime = d_dest_inode.i_ctime = src_inode->i_ctime if S_ISDIR(inode['st_mode']): self._add_inode_as_dir(inode_dir,inode,filename,now) # add dir link self._removedir_inode(inode,old_dir,old_name,now) # remove dir (Should be first ?) else: self._add_inode_as_file(inode_dir,inode,filename,now) # add file link self._removefile_inode(inode,old_dir,old_name,now) # remove file def link(self, target, source): ###### link ###### inode_dir,filename=self._get_dir_inode_for_create(target) # write permission check inode=self._getinode(source) now=time() # get time now if S_ISDIR(inode['st_mode']):raise FuseOSError(ENOSYS) # Function not implemented self._add_inode_as_file(inode_dir,inode,filename,now) # add inode as file ######## FUSE Operating function (Sym Link) ######## def readlink(self, path): ###### readlink ###### inode=self._get_inode_for_read(path) # read permission check if not S_ISLNK(inode['st_mode']):raise FuseOSError(EPERM) #(Operation not permitted) self._update_atime(inode) # read link path -> update atime return inode['i_data'] # return source path def symlink(self, target, source): ###### symlink ###### source=self._check_symlink(target,source) # check mount root -> mount afio.lzo # make normal symlink inode,inode_dir,filename,now=self._get_inode_for_create(target) # write permission check inode['st_mode']=(S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO ) # set symlink mode inode['st_size']=len(source) # set size inode['i_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): # any time shuld not be changed with only open self.fd += 1 return self.fd # shuld return file handle pointer. Can I use inode instead? Then Where shuld i save flags ? def read(self, path, size, offset, fh): ###### read ###### inode=self._get_inode_for_read(path) # read permission check if not S_ISREG(inode['st_mode']):raise FuseOSError(EPERM) #(Operation not permitted) filedata=inode.get('i_data',None) if filedata==None: filedata=inode['i_readfunc'](inode) inode['i_data']=filedata self._update_atime(inode) # read filedata -> update atime return filedata[offset:(offset + size)] # return read data def write(self, path, data, offset, fh): ###### write ###### inode=self._get_inode_for_write(path) # write permission check if not S_ISREG(inode['st_mode']):raise FuseOSError(EPERM) #(Operation not permitted) filedata=inode.get('i_data',None) if filedata==None: filedata=inode['i_readfunc'](inode) filedata=filedata[:offset] + data self._write_filedata(inode,filedata) # wite filedata return len(data) # return write length def truncate(self, path, length, fh=None): ###### truncate ###### inode=self._get_inode_for_write(path) # write permission check if not S_ISREG(inode['st_mode']):raise FuseOSError(EPERM) #(Operation not permitted) filedata=inode.get('i_data',None) if filedata==None: filedata=inode['i_readfunc'](inode) filedata=filedata[:length] self._write_filedata(inode,filedata) # wite filedata def release(self, path, fh): return 0 def flush(self, path, fh): return 0 def fsync(self, path, datasync, fh): return 0 if __name__ == "__main__": if len(argv) != 2: print 'usage: %s ' % argv[0] print ' make symlink "old ASCII cpio" or "afio" file in the mountpoint root to mount the archive' exit(1) mountpoint=os.path.abspath(argv[1]) afiolzofs= Afiolzofs(mountpoint) fuse = FUSE(afiolzofs, mountpoint,foreground=True)