#!/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.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 XXX<space>XXX.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 <verigak@gmail.com> 
#  
# 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 errno import * 
from stat import S_IFDIR, S_IFLNK, S_IFREG
from stat import S_IFMT ,S_ISDIR, S_ISLNK, S_ISREG
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.inode = defaultdict(dict) 
        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
    #### local function ####
    def _getinode(self,path):
	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 _getinodedir(self,path):
	if path=="/":
	  dentry=["/","."]			# what should be return here?
	else:
	  dentry=os.path.split(path)
	try:
	  inode_dir=self._getinode(dentry[0])
	  inode=inode_dir['i_data'][dentry[1]]
	  return inode,inode_dir,dentry[1]
	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
	now = time() 
	inode_dir['st_ctime']=now # Shuld be updated parent dir's ctime,mtime.	# But, when loading afio file, What I shuld do? 
	inode_dir['st_mtime']=now
    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
	now = time() 
	inode_dir['st_ctime']=now # Shuld be updated parent dir's ctime,mtime.	# But, when loading afio file, What I shuld do? 
	inode_dir['st_mtime']=now
    def _removefile_inode(self,inode,inode_dir,name):
	now = time() 
        inode['st_nlink'] -= 1
	inode['st_ctime']=now 		# Shuld be updated parent dir's ctime.
	inode['st_mtime']=now 		# ??????
	inode_dir['i_data'].pop(name)
	inode_dir['st_ctime']=now 	# Shuld be updated parent dir's ctime,mtime.
	inode_dir['st_mtime']=now
    def _removedir_inode(self,inode,inode_dir,name):
	if path == '/':			# Shuld be check root? ??
	  raise FuseOSError(EPERM)		# Operation not permitted
	elif inode['i_data'].len()>2:
	  raise FuseOSError(ENOTEMPTY)		# Directory not empty 
	else:
	  # change time is changed with directory's inode change(owner, permissions, etc.).
	  now = time() 
          inode['st_nlink'] -= 1	# ??? fix me. dir has no link
	  inode['st_ctime']=now 	# ??? 
	  inode['st_mtime']=now 	# ???
	  inode_dir['i_data'].pop(name)
          inode_dir['st_nlink'] -= 1
	  inode_dir['st_ctime']=now 	# Shuld be updated parent dir's ctime,mtime.
	  inode_dir['st_mtime']=now

    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
	  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()  

    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=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,
			i_data={}
		)
	    inode['st_ino']=id(inode)
	    inode['i_data']['.']=inode
	    self._adddir_inode(targetdir,inode)
            self.load_afio_files(targetdir,sourceabspath)
            return targetname
          else:
            return source
	return source
    ######## FUSE Operations ########
    #### Start File System ####     
    def statfs(self, path): 
        return dict(f_bsize=512, f_blocks=4096, f_bavail=2048) 
    #### Attr ####     
# access
# destroy  # at unmount the file system
# fgetattr
# init
# mknod
    def getattr(self, path, fh=None): 
        inode=self._getinode(path)
        return inode
    def chmod(self, path, mode): 
        inode=self._getinode(path)
	# mask = S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX; # Shuld I check these mask ? 
	# inode->i_mode = (inode->i_mode & ~mask) | (mode & mask);
        inode['st_mode'] &= 0770000 
        inode['st_mode'] |= mode 
	# change time shuld be changed with write, rename, truncate, chmod, link or chown
	now=time()
        inode['st_ctime'] = now 
        return 0 
    def chown(self, path, uid, gid): 
        inode=self._getinode(path)
        if uid != -1:inode['st_uid'] = uid # Check -1 
        if gid != -1:inode['st_gid'] = gid # Check -1 
	# change time shuld be changed with write, rename, truncate, chmod, link or chown
	now=time()
        inode['st_ctime'] = now 
    def utimens(self, path, times=None): 
        inode=self._getinode(path)
	# 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.
        now = time() 
        atime, mtime = times if times else (now, now) 
	inode['st_atime'] = atime 
	inode['st_mtime'] = mtime 
    #### XAttr ####     
    def listxattr(self, path): 
        inode=self._getinode(path)
        attrs = inode.get('attrs', {}) 
        return attrs.keys() 
    def getxattr(self, path, name, position=0): 
        inode=self._getinode(path)
        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=self._getinode(path)
        attrs = inode.setdefault('attrs', {}) 
        attrs[name] = value 
    def removexattr(self, path, name): 
        inode=self._getinode(path)
        attrs = inode.get('attrs', {}) 
        try: 
            del attrs[name] 
        except KeyError: 
	  raise FuseOSError(ENOSYS)		# Function not implemented
    #### Create or Remove Dir ####
    def readdir(self, path, fh): 
	inode_dir=self._getinode(path)
        dirnames=[name for name in inode_dir['i_data']]
	dirnames.append('..')
	# access time shuld be changed with read also dir
	now = time() 
	inode_dir['st_atime']=now
	return dirnames
    def mkdir(self, path, mode): 
	ctx.uid, ctx.gid, ctx.pid=fuse_get_context()
	now=time()
        inode = dict(
		st_mode=(S_IFDIR | mode), 
		st_nlink=2, 
                st_size=0, 
		st_ctime=now, 
		st_mtime=now, 
		st_atime=now,
		st_uid=ctx.uid,
		st_gid=ctx.gid,
		i_data={}		# {}: real empty dir entries
		) 
        inode['st_ino']=id(inode)
        inode['i_data']['.']=inode
	self._adddir_inode(path,inode)
    def rmdir(self, path): 
	inode,inode_dir,filename=self._getinodedir(path)
	self._removedir_inode(inode,inode_dir,name)
    #### Create or Remove File ####     
    def create(self, path, mode): 
	ctx.uid, ctx.gid, ctx.pid=fuse_get_context()
	now=time()
        inode=dict(
		st_mode=(S_IFREG | mode), 
		st_nlink=1, 
		st_size=0, 
		st_ctime=now, 
		st_mtime=now, 
		st_atime=now,
		st_uid=ctx.uid,
		st_gid=ctx.gid,
		i_data=''
		)
        inode['st_ino']=id(inode)
	self._addfile_inode(path,inode)
        self.fd += 1 
        return self.fd 
    def unlink(self, path): 
	# Shuld I check REG file(not DIR)
        inode,inode_dir,filename=self._getinodedir(path)
	# change time shuld be changed with write, rename, truncate, chmod, link or chown.  unlink means change st_nlink.
	now=time()
        inode['st_ctime'] = now
	inode['st_nlink'] -= 1
        inode_dir['i_data'].pop(filename)
    def rename(self, old, new): 
	# change time shuld be changed with write, rename, truncate, chmod, link or chown
        inode,old_inode_dir,old_filename=self._getinodedir(old)
	dentry_new=os.path.split(new)
	new_inode_dir=self._getinode(dentry_new[0])
	new_filename=dentry_new[1]
        mode=inode['st_mode']
	# 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(mode):
	  new_inode_dir['i_data'][new_filename]=inode
	  new_inode_dir['st_nlink'] += 1 
	  old_inode_dir['i_data'].pop(old_filename)		# fix me !! if new==old , new is include old path
	  old_inode_dir['st_nlink'] -= 1 
	else:
	  new_inode_dir['i_data'][new_filename]=inode
	  old_inode_dir['i_data'].pop(old_filename)		# fix me !! if new==old
	now=time()
        inode['st_ctime'] = now 
	# Shuld I change old and new parent dir? if file or dir 
    def link(self, target, source):
        inode,old_inode_dir,old_filename=self._getinodedir(source)
	dentry_new=os.path.split(target)
	new_inode_dir=self._getinode(dentry_new[0])
	new_filename=dentry_new[1]
        if S_ISDIR(mode):					# fix me !!! no link if DIR
	  new_inode_dir['i_data'][new_filename]=inode
	  new_inode_dir['st_nlink'] += 1 
	  inode['st_nlink'] += 1 
	else:
	  new_inode_dir['i_data'][new_filename]=inode	# use addfile_inode
	  inode['st_nlink'] += 1 
	# change time shuld be changed with write, rename, truncate, chmod, link or chown
	now=time()
        inode['st_ctime'] = now 
    #### Sym Link ####
    def readlink(self, path): 
	if S_ISLNK(inode['st_mode']):	# shuld I check this?
          inode=self._getinode(path)
	  # access time shuld be changed with read also symlink
	  now = time() 
	  inode_dir['st_atime']=now
          return inode['i_data'] 
	else:
	  raise FuseOSError(EPERM)		# Operation not permitted
    def symlink(self, target, source): 
        source=self._check_symlink(target,source)	# if root of the mount point, then mount afio.lzo file
	# In also Linux symlink has the own atime,mtime,ctime but cannot be changed because no lutimes
	now=time()
	inode = dict(					# make normal symlink
		st_mode=(S_IFLNK | 0777), 
		st_nlink=1,
		st_size=len(source),
		st_uid=os.getuid(),
		st_gid=os.getgid(),
		st_atime=now,
		st_mtime=now,
		st_ctime=now,
		i_data= source,
		) 
        inode['st_ino']=id(inode)
	self._addfile_inode(target,inode)
    #### 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): 
        inode=self._getinode(path)
        data=inode.get('i_data',None)  
        if data==None:
          data=inode['i_readfunc'](inode)
	  inode['i_data']=data
	# access time shuld be changed with read (not be changed with write, open or read without permision)
	now=time()
        inode['st_atime'] = now 
        return data[offset:(offset + size)] 
    def write(self, path, data, offset, fh): 
        inode=self._getinode(path)
        data_old=inode.get('i_data',None)  
        if data_old==None:
          data_old=inode['i_readfunc'](inode)
	  inode['i_data']=data_old
	data_new=data_old[:offset] + data    ##### fix me !
        inode['i_data'] = data_new
        inode['st_size'] = len(data_new) 
	# modify time shuld be changed with write or truncate
	# change time shuld be changed with write, rename, truncate, chmod, link or chown
	now=time()
        inode['st_mtime'] = now 
        inode['st_ctime'] = now 
        return len(data) 
    def truncate(self, path, length, fh=None): 
        inode=self._getinode(path)
        inode['i_data'] = inode['i_data'][:length] 
        inode['st_size'] = length 
	# modify time shuld be changed with write or truncate
	# change time shuld be changed with write, rename, truncate, chmod, link or chown
	now=time()
        inode['st_mtime'] = now 
        inode['st_ctime'] = now 

    #### Release Flush Fsync ####
    def release(self, path, fh):
	#print fh
        return 0
    def flush(self, path, fh):
	#print fh
        return 0
    def fsync(self, path, datasync, fh):
	#print fh
        return 0
    def fsyncdir(self, path, datasync, fh):
	#print fh
        return 0
 
if __name__ == "__main__": 
    if len(argv) != 2: 
        print 'usage: %s <mountpoint>' % 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) 

