#!/usr/bin/env python 
# -*- coding: UTF-8 -*-

# afiolzofs : Support to mount afio archives with lzop compression.

# Copyright (c) 2010, Yoshiteru Ishimaru <y_ishimaru@users.sourceforge.jp>
# 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.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 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 stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISCHR, S_ISBLK # function
from stat import S_IMODE	# function (or 0007777)	get bitmask to be able to change with os.chmod()
from stat import S_IFMT		# function (or 0170000)	get bitmask for the file type bitfields
from stat import S_IFSOCK 	# 0140000 	socket
from stat import S_IFLNK 	# 0120000 	symbolic link
from stat import S_IFREG 	# 0100000 	regular file
from stat import S_IFBLK 	# 0060000 	block device
from stat import S_IFDIR 	# 0040000 	directory
from stat import S_IFCHR 	# 0020000 	character device
from stat import S_IFIFO 	# 0010000 	FIFO
from stat import S_ISUID 	# 0004000 	set UID bit
from stat import S_ISGID 	# 0002000	set-group-ID bit (see below)
from stat import S_ISVTX 	# 0001000	sticky bit (see below)
from stat import S_IRWXU 	# 00700		mask for file owner permissions
from stat import S_IRUSR 	# 00400 	owner has read permission
from stat import S_IWUSR 	# 00200 	owner has write permission
from stat import S_IXUSR 	# 00100 	owner has execute permission
from stat import S_IRWXG 	# 00070 	mask for group permissions
from stat import S_IRGRP 	# 00040 	group has read permission
from stat import S_IWGRP 	# 00020 	group has write permission
from stat import S_IXGRP 	# 00010 	group has execute permission
from stat import S_IRWXO 	# 00007 	mask for permissions for others (not in group)
from stat import S_IROTH 	# 00004 	others have read permission
from stat import S_IWOTH 	# 00002 	others have write permission
from stat import S_IXOTH 	# 00001 	others have execute permission

from sys import argv, exit 
from time import time,mktime ,strptime
import subprocess
import pwd
import grp
import os
from errno import * 
try:
  from fuse import FUSE, FuseOSError, Operations, LoggingMixIn ,fuse_get_context
except:
  print 'I cannot find fuse.py.'
  print 'Please download fuse.py from http://code.google.com/p/fusepy.'
  print 'And put it in the same dir.'
  exit()

"""
ENOSYS		(Function not implemented) 
EPERM		(Operation not permitted) 
ENOENT		(No such file or directory) 
EIO		(I/O error) 
ENXIO		(No such device or address) 
EBADF		(Bad file number) 
ENOMEM		(Out of memory) 
EACCES		(Permission denied) 
EFAULT		(Bad address) 
ENOTBLK		(Block device required) 
EBUSY		(Device or resource busy) 
EEXIST		(File exists) 
EXDEV		(Cross-device link) 
ENODEV		(No such device) 
ENOTDIR		(Not a directory) 
EISDIR		(Is a directory) 
ENOSPC		(No space left on device) 
ESPIPE		(Illegal seek) 
EROFS		(Read-only file system) 
EPIPE		(Broken pipe) 
ENAMETOOLONG	(File name too long) 
ENOTEMPTY	(Directory not empty) 
"""
#################################################################################################################
#														#
#					Inode Class								#
#														#
#################################################################################################################
class Inode():
	def __init__(self,time,context):
	  self.st_ctime=time
	  self.st_mtime=time
	  self.st_atime=time
	  self.st_uid=context[0]
	  self.st_gid=context[1]
	### Inode Dir access functions ###
	def set_dentry(self,name,inode):							# set or add dir entry
	  if hasattr(self,'i_dentry'):
	    if hasattr(self.i_dentry,name):	return True					# return True if name is exist -> error(file exist)
	    self.i_dentry[name]=inode								# if exist
	  else:				self.i_dentry={name:inode}			# if no i_dentry
	def get_dentry(self,name):		return getattr(self,'i_dentry',{})[name]	# get dir entry
	def pop_dentry(self,name):		return getattr(self,'i_dentry',{}).pop(name)	# delete or move dir entry
	def ls_dentry(self):			return getattr(self,'i_dentry',{}).keys()	# list dir entry
	def len_dentry(self):			return len(getattr(self,'i_dentry',{}))	# number of the dir entrys (for delete check)
	### Inode Xattr access functions ###
	def set_xattr(self,name,val):								# set or add xattr
	  if hasattr(self,'i_xattr'):		self.i_xattr[name]=val				#
	  else:				self.i_xattr={name:val}				#
	def get_xattr(self,name):		return getattr(self,'i_xattr',{})[name]	#
	def pop_xattr(self,name):		return getattr(self,'i_xattr',{}).pop(name)	#
	def ls_xattr(self):			return getattr(self,'i_xattr',{}).keys()	#
	### Inode Set Attr functions ###
	def set_nlink(self,n=1):
	  if n!=1 or hasattr(self,'st_nlink'):	self.st_nlink=n
	def inc_nlink(self):
	  if hasattr(self,'st_nlink'):		self.st_nlink+=1
	  else:				self.st_nlink=2
	def dec_nlink(self):
	  if hasattr(self,'st_nlink'):		self.st_nlink-=1
	  else:				self.st_nlink=0
	def update_atime(self,time):		self.st_atime=time
	def update_mtime(self,time):		self.st_mtime=time
	def update_ctime(self,time):		self.st_ctime=time
	def set_rdev(self,n=0):		self.st_rdev=n
	def set_mode(self,mode):		self.st_mode=mode
	def set_gid(self,gid):			self.st_gid=gid
	def set_uid(self,uid):			self.st_uid=uid
	### Inode Get Attr functions ###
	def get_items(self,keys=('st_mode','st_uid','st_gid')):			# called to get mode,uid,gid
	  return [val for key,val in self.items(keys)]
	def items(self,keys=('st_mode','st_uid','st_gid','st_rdev','st_atime','st_ctime','st_mtime','st_ino','st_size','st_nlink')):
	  inode=None									# called at 'getattr' from fuse.py.
	  for key in keys:
	    if key=='st_ino':		yield ('st_ino',id(self))
	    elif key=='st_nlink':	yield (key,getattr(self,key,1))
	    elif key=='st_size':
	      key='st_size';val=getattr(self,'i_data',None)
	      if val==None:
	        if inode==None:inode=self.get_default_inode()
	        val=inode.get(key,0)
	      else:val=len(val)
	      yield (key,val)
	    else:
	      val=getattr(self,key,None)
	      if val==None:
	        if inode==None:inode=self.get_default_inode()
	        val=inode.get(key,0)
	      yield (key,val)
	### Inode File IO functions ###
	def get_default_inode(self):		return {}		# if no attr is, use from this dict or 0 in the 'items'.
	def get_inode_data(self,offset=0,size=None):			# get data for read.
	  if size==None:return getattr(self,'i_data','')[offset:]
	  else:return getattr(self,'i_data','')[offset:(offset + size)] 
	def set_inode_data(self,data,offset=0):			# set data for write 
	  i_data=self.get_inode_data()
	  self.i_data = i_data[0:offset]+data+i_data[offset+len(data):]
	  return len(data)						# return write length
	def trunc_inode_data(self,offset):				# truncate data length
	  self.i_data=self.get_inode_data(0,offset)

#################################################################################################################
#														#
#				FUSE Operation Ramfs Class							#
#														#
#################################################################################################################
class Ramfs(Operations):			# for normal use
    """Temporaly Read Write Memory filesystem. No Saving files. """ 
    def __init__(self,mountpoint): 
        self.mountpoint=mountpoint						# abspath of the mount point
        self.fd = 0 								# 
	self.uid=os.getuid()
	self.inode=Inode(time(),(self.uid,os.getgid()))				# root inode of the file system
	self.inode.set_mode(S_IFDIR | 0755)
	self.inode.set_nlink(2)
    ######## Ramfs function (Permission check) ########
    def _is_inode_USER(self,inode,context,uname,items,filesystem=False):	# filesystem is set 'True' at 'chown'
	if (context[0]==items[1])or(context[0]==0):return
	if filesystem and(context[0]==self.uid):return			# -> filesystem owner can change UID of any files.
	raise FuseOSError(EPERM)						# (Operation not permitted)
    def _is_inode_R_OK(self,inode,context,uname,items):
	if not (items[0] & S_IROTH) and not((items[0] & S_IRUSR) and (items[1] == context[0])):
	  if (items[0] & S_IRGRP):
	    if not(uname in grp.getgrgid(items[2]).gr_mem):
	      if context[1] != items[2]:	raise FuseOSError(EACCES)	# (Permission denied)
	  else:				raise FuseOSError(EACCES)	# (Permission denied)
    def _is_inode_W_OK(self,inode,context,uname,items):
	if not (items[0] & S_IWOTH) and not((items[0] & S_IWUSR) and (items[1] == context[0])):
	  if (items[0] & S_IWGRP):
	    if not(uname in grp.getgrgid(items[2]).gr_mem):
	      if context[1] != items[2]:	raise FuseOSError(EACCES)	# (Permission denied)
	  else:				raise FuseOSError(EACCES)	# (Permission denied)
    def _is_inode_X_OK(self,inode,context,uname,items):
	if not (items[0] & S_IXOTH) and not((items[0] & S_IXUSR) and (items[1] == context[0])):
	  if (items[0] & S_IXGRP):
	    if not(uname in grp.getgrgid(items[1]).gr_mem):
	      if context[1] != items[1]:	raise FuseOSError(EACCES)	# (Permission denied)
	  else:				raise FuseOSError(EACCES)	# (Permission denied)
    ######## Ramfs function (Get inode with Permission check) ########
    def _get_dir_inode_nopermisson(self,path):				###### Get inode without any permission checking ######
	  if path=="/":						# if path is root
	    return self.inode						#   return root inode of the file system
	  pname=os.path.split(path)
	  inode_dir=self._get_dir_inode_nopermisson(pname[0])
	  try:inode=inode_dir.get_dentry(pname[1])			# get inode
	  except:							# if no inode
	    inode=Inode(time(),(self.uid,os.getgid()))						# make one as dir inode
	    inode.set_mode( S_IMODE(0755) | S_IFDIR )			# set mode (dir)
	    inode.set_nlink(2)
	    inode_dir.set_dentry(pname[1],inode)
	    inode_dir.inc_nlink()
	  return inode
    def _get_inode(self,path,context,uname):				###### Get inode from path,context,uname #######
	if path=="/":							# if path is root of the file system
	  inode=self.inode
	else:								# not root of the file system
	  pname=os.path.split(path)
	  inode_dir=self._get_inode(pname[0],context,uname)
	  items=inode_dir.get_items()
	  self._is_inode_X_OK(inode_dir,context,uname,items)		# dir access permission check
	  try:inode=inode_dir.get_dentry(pname[1])			# if 'no name in dir list' or 'not dir' -> error
	  except:raise FuseOSError(ENOENT)				# (No such file or directory)
	return inode
    def _get_inode_for_access(self,path):				###### Get inode for Access ######
	context=fuse_get_context()					# get system call uid and gid
	uname=pwd.getpwuid(context[0]).pw_name				# get system call uname
	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)	# read permission check
	return inode,items
    def _get_inode_for_read(self,path):				###### Get inode for Read ######
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
	items=inode.get_items()
	self._is_inode_R_OK(inode,context,uname,items)			# read permission check
	return inode,items
    def _get_inode_for_write(self,path):				###### Get inode for Write ######
	inode,context,uname,items=self._get_inode_for_access(path)	# 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 ######
	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()
	inode_file=inode.get_dentry(pname[1])
	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()
        inode_file = Inode(now,context)
	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,dirinode=False):	# add inode without any permission checking   # fix me ! if no dir,create or error?
	pname=os.path.split(path)
	inode_dir=self._get_dir_inode_nopermisson(pname[0])
	if inode_dir.set_dentry(pname[1],inode):
	  raise FuseOSError(EEXIST)					# (File exists) 
	if dirinode:inode_dir.inc_nlink()
    def _add_inode_as_dir(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.inc_nlink()
	inode_dir.update_ctime(now)					# change nlink 		-> change ctime
	inode.update_ctime(now)						# change nlink 		-> change ctime
	inode.update_mtime(now) 					# change dir list	-> change mtime
    def _removedir_inode(self,inode,inode_dir,filename,now):		#
	inode_dir.pop_dentry(filename)
	inode_dir.update_mtime(now)					# change dir list 	-> change mtime
        inode_dir.dec_nlink()
	inode_dir.update_ctime(now) 					# change nlink 		-> change ctime
    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 _removefile_inode(self,inode,inode_dir,filename,now):		#
	inode_dir.pop_dentry(filename)					# fix me ! no name ?
	inode_dir.update_mtime(now)					# change dir list -> change mtime
	inode_dir.update_ctime(now)					# change mtime -> change ctime
	inode.update_ctime(now) 					# change nlink -> change ctime
    ######## 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=0x00040000, f_blocks=4096, f_bavail=2048) 
    def destroy(self, path):
        """Called on filesystem destruction. Path is always /"""
        pass
    ######## FUSE Operating function (Attr) ########
    def access(self, path, amode):
	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: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 ######
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        return inode							# return inode
    def chmod(self, path, mode):					###### chmod ######
	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 ######
	inode,items=self._get_inode_for_user(path,filesystem=True)	# user check
        if uid != -1:inode.set_uid(uid) 				# set uid with check -1 
        if gid != -1:inode.set_gid(gid) 				# set gid with check -1 
	now=time()
        inode.update_ctime(now) 					# change inode data -> change ctime
    def utimens(self, path, times=None):				###### utime ######
	inode,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									# fix me!
#    def bmap(self,path, blocksize, idx): pass									# fix me!

    ######## FUSE Operating function (XAttr) ########
    def listxattr(self, path): 
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        return inode.ls_xattr() 
    def getxattr(self, path, name, position=0): 
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        try:return inode.get_xattr(name)
        except KeyError:raise FuseOSError(ENOSYS)			# (Function not implemented) 			# fix me! what error should be.
    def setxattr(self, path, name, value, options, position=0):	# Ignore options 
	inode,items=self._get_inode_for_user(path)			# user check
	inode.set_xattr(value)
    def removexattr(self, path, name): 
	inode,items=self._get_inode_for_user(path)			# user check
        try:inode.pop_xattr(value)
        except KeyError:raise FuseOSError(ENOSYS)			# (Function not implemented) 			# fix me! what error should be.
    ######## FUSE Operating function (Read Dir) ########
    def opendir(self, path):
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS)		# Function not implemented
	self._is_inode_R_OK(inode,context,uname,items)						# fix me change by read ??? or not. change atime?
        """Returns a numerical file handle."""
	self.fd += 1 
        return self.fd 						# return fd
    def readdir(self, path, fh):					###### readdir ######
        inode,items=self._get_inode_for_read(path)			# read permission check
        if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS)		# Function not implemented
        dirnames=inode.ls_dentry()					# read dir list
	dirnames.append('..')						# append parent dir '..'
	dirnames.append('.')						# append parent dir '.'
        self._update_atime(inode,time())				# read data -> change atime
	return dirnames						# return dir list
    def releasedir(self, path, fh):
        return 0
    def fsyncdir(self, path, datasync, fh):
        return 0
    ######## FUSE Operating function (Create or Remove Dir) ########
    def mkdir(self, path, mode): 					###### mkdir ######
	inode,inode_dir,filename,now=self._get_inode_for_create(path)	# write permission check
        inode.set_mode( S_IMODE(mode) | S_IFDIR )			# set mode (dir)
        inode.set_nlink(2)
	self._add_inode_as_dir(inode_dir,inode,filename,now)		# add inode as dir
    def rmdir(self, path): 						###### rmdir ######
        inode,inode_dir,filename,now=self._get_inode_for_delete(path)	# write permission check
	items=inode.get_items()
        if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS)		# Function not implemented (not Dir shuld be delete unlink)
	if filename == '/':						# Shuld be check root? ??
	  raise FuseOSError(EPERM)					# Operation not permitted
	elif inode.len_dentry()>0:					# 
	  raise FuseOSError(ENOTEMPTY)					# Directory not empty 
	else:
	  self._removedir_inode(inode,inode_dir,filename,now)		# remove dir
    ######## FUSE Operating function (Create or Remove File) ########
    def mknod(self, path, mode, dev):					###### mknod ######
	mask=S_IFSOCK | S_IFBLK | S_IFCHR | S_IFIFO			# ??? S_IFREG ?			# fix me!
	if (mode & mask) == 0 :raise FuseOSError(EPERM)		# (Operation not permitted) 					# 
	inode,inode_dir,filename,now=self._get_inode_for_create(path)	# write permission check
	inode.set_mode( S_IMODE(mode) | (mode & mask) )			# set node mode 		# fix me!
	inode.set_rdev(dev)						# set rdev			# fix me!
	self._add_inode_as_file(inode_dir,inode,filename,now)		# add inode as file
    def create(self, path, mode): 					###### create ######
	inode,inode_dir,filename,now=self._get_inode_for_create(path)	# write permission check
        inode.set_mode( S_IFREG | S_IMODE(mode)	)			# set reguler file mode
        inode.set_inode_data('')					# set no data
	self._add_inode_as_file(inode_dir,inode,filename,now)		# add inode as file
	self.fd += 1 
        return self.fd 						# return fd
    def unlink(self, path): 						###### unlink ######
        inode,inode_dir,filename,now=self._get_inode_for_delete(path)	# write permission check
	items=inode.get_items()
        if S_ISDIR(items[0]):raise FuseOSError(ENOSYS)			# Function not implemented (Dir shuld be delete rmdir)
        inode.dec_nlink()
	self._removefile_inode(inode,inode_dir,filename,now)		# remove file
    def rename(self, old, new): 
        inode,old_dir,old_name,now=self._get_inode_for_delete(old)	# write permission check
	inode_dir,filename=self._get_dir_inode_for_create(new)		# write permission check
	items=inode.get_items()
        if S_ISDIR(items[0]):
	  self._add_inode_as_dir(inode_dir,inode,filename,now)		# add dir link
	  self._removedir_inode(inode,old_dir,old_name,now)		# remove dir (Should be first ?)
	else:
	  self._add_inode_as_file(inode_dir,inode,filename,now)		# add file link
	  self._removefile_inode(inode,old_dir,old_name,now)		# remove file
    def link(self, target, source):					###### link ######
	inode_dir,filename=self._get_dir_inode_for_create(target)	# write permission check
	inode,context,uname,items=self._get_inode_for_access(source)	# we can make link of other's file
	now=time()							# get time now
        if S_ISDIR(items[0]):raise FuseOSError(ENOSYS)			# Function not implemented	# fix me! error NO
	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,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): 
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        if S_ISDIR(items[0]):raise FuseOSError(ENOSYS)			# Function not implemented (Dir shuld be delete rmdir)
	if (flags & 3) == os.O_RDONLY:
	  self._is_inode_R_OK(inode,context,uname,items)
	elif (flags & 3) == os.O_RDWR:
	  self._is_inode_R_OK(inode,context,uname,items)
	  self._is_inode_W_OK(inode,context,uname,items)
	elif (flags & 3)== os.O_WRONLY:
	  self._is_inode_W_OK(inode,context,uname,items)
        self.fd += 1 							# any time shuld not be changed with only open
        return self.fd  # shuld return file handle pointer. Can I use inode instead? Then Where shuld i save flags ?
    def read(self, path, size, offset, fh):  				###### read ######
	print "=== read === %04x %04x" % (size,offset)
        inode,items=self._get_inode_for_read(path)			# 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)			# 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 ######
        inode,items=self._get_inode_for_write(path)			# 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):
        return 0
    def flush(self, path, fh):
        return 0
    def fsync(self, path, datasync, fh):
        return 0

#################################################################################################################
#														#
#				FUSE Operation Romfs Class							#
#														#
#################################################################################################################
class Romfs(Ramfs):
    """Read only Memory filesystem. """ 
    def _update_atime(self,inode,now):
        pass
    ######## 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):					###### chmod ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    def chown(self, path, uid, gid):					###### chown ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    def utimens(self, path, times=None):				###### utime ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    ######## FUSE Operating function (XAttr) ########
    def setxattr(self, path, name, value, options, position=0):	# Ignore options 
	raise FuseOSError(EROFS)					# (Read-only file system)
    def removexattr(self, path, name): 
	raise FuseOSError(EROFS)					# (Read-only file system)
    ######## FUSE Operating function (Create or Remove Dir) ########
    def mkdir(self, path, mode): 					###### mkdir ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    def rmdir(self, path): 						###### rmdir ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    ######## FUSE Operating function (Create or Remove File) ########
    def mknod(self, path, mode, dev):					###### mknod ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    def create(self, path, mode): 					###### create ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    def unlink(self, path): 						###### unlink ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    def rename(self, old, new): 
	raise FuseOSError(EROFS)					# (Read-only file system)
    def link(self, target, source):					###### link ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    ######## FUSE Operating function (Sym Link) ########
    def symlink(self, target, source): 				###### symlink ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    ######## FUSE Operating function (Read or Write File) ########
    def open(self, path, flags): 
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        if S_ISDIR(items[0]):raise FuseOSError(ENOSYS)			# Function not implemented (Dir shuld be delete rmdir)
	if (flags & 3) == os.O_RDONLY:
	  self._is_inode_R_OK(inode,context,uname,items)
	elif (flags & 3) == os.O_RDWR:
	  raise FuseOSError(EROFS)					# (Read-only file system)
	elif (flags & 3)== os.O_WRONLY:
	  raise FuseOSError(EROFS)					# (Read-only file system)
        self.fd += 1 							# any time shuld not be changed with only open
        return self.fd  # shuld return file handle pointer. Can I use inode instead? Then Where shuld i save flags ?
    def write(self, path, data, offset, fh): 				###### write ######
	raise FuseOSError(EROFS)					# (Read-only file system)
    def truncate(self, path, length, fh=None): 			###### truncate ######
	raise FuseOSError(EROFS)					# (Read-only file system)

#################################################################################################################
#														#
#					AfioLzoFs Function							#
#														#
#################################################################################################################
def _filetype(data):
	pipe=subprocess.Popen("file -" ,shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE)
	pipe.stdin.write(data);			pipe.stdin.close()
	s=pipe.stdout.read().split();		pipe.stdout.close()
	return " ".join(s[1:4])
#def _lzop(data,compress=False):
#        if compress:	args=['lzop','-c9']
#        else:		args=['lzop','-dc']
#	proc = subprocess.Popen(args,
#        	stdin=subprocess.PIPE,
#		stdout=subprocess.PIPE,
#		close_fds=True,
#		)
#	pid = os.fork()
#	if pid==0: # child
#		proc.stdout.close()
#		proc.stdin.write(data)
#		proc.stdin.flush()
#		proc.stdin.close()
#		os._exit(os.EX_OSERR)
#	else:      # parent
#		proc.stdin.close()
#		data=proc.stdout.read()
#               proc.stdout.close()
#                return data

#################################################################################################################
#														#
#					Header Afio Class							#
#														#
#################################################################################################################
class Header_Afio_Base():
	ID=6
	LEN=0
	s1= ""
	s2= ""
	def __init__(self,inode):
	  self.inode=inode
	  self.header_address=inode.header_address
	  self.abspath=inode.abspath
	  print afiolzofs
	def read_data(self,addr,size):
	  f=open(self.abspath,"r")
	  f.seek(addr)
	  data=f.read(size)
	  f.close()
	  return data
	def print_header(self):
	  print self.s1
	  print self.s2
          print "%s%s" % (self.get_hdr2(),self.get_st_raw_path())
        def get_hdr2(self):
	  if not hasattr(self,'hdr2'):self.hdr2=self.read_data(self.header_address+self.ID,self.LEN)
	  return self.hdr2
	def get_st_atime(self):	return self.get_st_mtime()
	def get_st_ctime(self):	return self.get_st_mtime()
	def get_st_raw_path(self):	return self.read_data(self.header_address+self.ID+self.LEN,self.get_st_nlen()-1)
	def get_st_data_addr(self):	return self.header_address+self.ID+self.LEN+self.get_st_nlen()
	def get_st_compress(self):
	  path=self.get_st_raw_path()
	  if len(path)<2:		return False
	  else:			return ((self.get_st_rdev() & 1)==0)and(path[-2:]=='.z')
	def get_st_path(self):
	  if self.get_st_compress():	return self.get_st_raw_path()[:-2]
	  else:			return self.get_st_raw_path()
	def get_st_size(self):
	  if self.get_st_compress():	return self.get_st_comp_size()
	  else:			return self.get_st_raw_size()
	def get(self,name,default):					# 
	  x=getattr(self,name,None)
	  if x != None: return x
	  x=getattr(self,'get_'+name,None)
	  if x != None: return x()
	  return default	  
	def __getitem__(self,name):					# 
	  x=getattr(self,name,None)
	  if x != None: return x
	  x=getattr(self,'get_'+name,None)
	  if x != None: return x()
	  print 'ERROR 123'
	#def __setitem__(self,name,data):					# 
	#  setattr(self,name,data)
	def get_st_raw_data(self,offset=0,size=None):
	  data_offset=self.header_address+self.ID+self.LEN+self.get_st_nlen()+offset
	  data_size=self.get_st_raw_size()-offset
	  if size==None:		return self.read_data(data_offset,data_size)
	  else:			return self.read_data(data_offset,min(data_size,size))
	def get_st_data(self,offset=0,size=None):
	  if self.get_st_compress():
	      if not hasattr(self,'lzop_data'):self.get_st_comp_size()
	      lzop_data=self.lzop_data
	      if size==None:size=lzop_data[-1]
	      endoffset=offset+size
	      for start_block in xrange(0,len(lzop_data)):
	        if offset<lzop_data[start_block]:
		  if start_block>0:offset=offset-lzop_data[start_block-1]
		  break 
	      else:
		return ''	
	      for end_block in xrange(start_block,len(lzop_data)):
	        if endoffset<lzop_data[end_block]:
		  break 
	      data=''
	      for i in xrange(start_block,end_block+1):
		#print "buffers",buffers.keys()
		data_block=buffers.get((id(self.inode),i),None)						# Buffers !!
		if data_block==None:
		  data_block=self.get_comp_data(i)
		  buffers[(id(self.inode),i)]=data_block
		else:
		  pass
		  #print "found!"
		data=data+data_block
	      return 	data[offset:(offset+size)]
	  else:return	self.get_st_raw_data(offset,size)
	def get_comp_data(self,i):									# read lzop data
	    lzop_raw_data=self.lzop_raw_data
            args=['lzop','-dc']
	    proc = subprocess.Popen(args,stdin=subprocess.PIPE,stdout=subprocess.PIPE,close_fds=True,)
	    #print "decompressing %s block" % i
	    pid = os.fork()
	    if pid==0: # child
		proc.stdout.close()
		data=self.get_st_raw_data(0,self.lzop_raw_data[0])
		proc.stdin.write(data)
		data=self.get_st_raw_data(lzop_raw_data[i],lzop_raw_data[i+1]-lzop_raw_data[i])
		proc.stdin.write(data)
		proc.stdin.write('\0\0\0\0')
		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 get_st_comp_size(self):									# read lzop length
	  if not hasattr(self,'lzop_data'):
	    seek=self.get_st_data_addr()
	    size=self.get_st_raw_size()
	    f=open(self.abspath)
	    f.seek(seek)
	    s=f.read(512)	 									# enough length is 42byte in afio, because no filename.
	    num= 9											# 89 4c 5a 4f 00 0d 0a 1a 0a #lzop file signature
	    lzop_ver=(ord(s[num])<<8)|ord(s[num+1]) 							# ver_2
	    if lzop_ver>0x0940:		lzop_ver_x=1						#	 'ext_2' 'level_1' 'mtimeH_4' are exist
	    else:				lzop_ver_x=0						#	
	    num=14+lzop_ver_x*3										# lib_2 (ext_2) method_1 (level_1) 
	    lzop_flags=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3]) 		# flags_4
	    F_H_FILTER=0x00000800L
	    if lzop_flags & F_H_FILTER:	lzop_filter_x=1						#	'filter_4' is exist
	    else:				lzop_filter_x=0
	    num=26+lzop_ver_x*7+lzop_filter_x*4								# mode_4 mtimeL_4 (mtimeH_4)
	    lzop_nlen=ord(s[num])									# nlen_1
	    num=31+lzop_ver_x*7+lzop_filter_x+lzop_nlen							# csum_4
	    F_H_EXTRA_FIELD = 0x00000040L
	    if lzop_flags & F_H_EXTRA_FIELD:								# No 'EXTRA_FIELD' in lzop yet
	      lzop_extr=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3])+8	# extrlen_4 extr_n extr_csum_4 
	    else:
	      lzop_extr=0
	    lzop_header=s[:31+lzop_ver_x*7+lzop_filter_x+lzop_nlen+lzop_extr]
	    lzop_data_len=0
	    lzop_raw_len=len(lzop_header)
	    lzop_raw_data=[]
	    lzop_data=[]
	    F_ADLER32_D=0x00000001L
	    F_ADLER32_C=0x00000002L
	    F_CRC32_D=0x00000100L
	    F_CRC32_C=0x00000200L
	    lzop_raw_data.append(lzop_raw_len)
	    while True:
	     if lzop_raw_len>size:raise FuseOSError(EIO)	# (I/O error) 
	     f.seek(seek+lzop_raw_len)
	     s=f.read(8)
	     num=0;d_len=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3])
	     if d_len==0:break
	     num=4;c_len=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3])
	     lzop_data_len+=d_len
	     lzop_raw_len+=c_len+8	# lzop
	     if lzop_flags & F_ADLER32_D	:lzop_raw_len+=4
	     if lzop_flags & F_CRC32_D	:lzop_raw_len+=4
	     if d_len>c_len:
	       if lzop_flags & F_ADLER32_C	:lzop_raw_len+=4
	       if lzop_flags & F_CRC32_C	:lzop_raw_len+=4
	     lzop_data.append(lzop_data_len)
	     lzop_raw_data.append(lzop_raw_len)
	     #print "--------> %08x %08x %08x %08x %08x" % (size,lzop_raw_len,lzop_data_len,d_len,c_len)
	    f.close()
	    self.lzop_data=lzop_data
	    self.lzop_raw_data=lzop_raw_data
	  return self.lzop_data[-1]

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):		self.get_hdr2();return int(self.hdr2[ 0: 6],8)
	def get_st_ino(self):		self.get_hdr2();return int(self.hdr2[ 6:12],8)
	def get_st_mode(self):		self.get_hdr2();return int(self.hdr2[12:18],8)
	def get_st_uid(self):		self.get_hdr2();return int(self.hdr2[18:24],8)
	def get_st_gid(self):		self.get_hdr2();return int(self.hdr2[24:30],8)
	def get_st_nlink(self):	self.get_hdr2();return int(self.hdr2[30:36],8)
	def get_st_rdev(self):		self.get_hdr2();return int(self.hdr2[36:42],8)
	def get_st_mtime(self):	self.get_hdr2();return int(self.hdr2[42:53],8)
	def get_st_nlen(self):		self.get_hdr2();return int(self.hdr2[53:59],8)
	def get_st_raw_size(self):	self.get_hdr2();return int(self.hdr2[59:70],8)
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):		self.get_hdr2();return int(self.hdr2[ 0: 6],8)
	def get_st_ino(self):		self.get_hdr2();return int(self.hdr2[ 6:17],8)
	def get_st_mode(self):		self.get_hdr2();return int(self.hdr2[17:23],8)
	def get_st_uid(self):		self.get_hdr2();return int(self.hdr2[23:29],8)
	def get_st_gid(self):		self.get_hdr2();return int(self.hdr2[29:35],8)
	def get_st_nlink(self):	self.get_hdr2();return int(self.hdr2[35:41],8)
	def get_st_rdev(self):		self.get_hdr2();return int(self.hdr2[41:47],8)
	def get_st_mtime(self):	self.get_hdr2();return int(self.hdr2[47:58],8)
	def get_st_nlen(self):		self.get_hdr2();return int(self.hdr2[58:64],8)
	def get_st_raw_size(self):	self.get_hdr2();return int(self.hdr2[64:75],8)
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):		self.get_hdr2();return int(self.hdr2[ 0: 8],8)
	def get_st_ino(self):		self.get_hdr2();return int(self.hdr2[ 8:25],8)
	def get_st_mode(self):		self.get_hdr2();return int(self.hdr2[25:31],8)
	def get_st_uid(self):		self.get_hdr2();return int(self.hdr2[31:39],8)
	def get_st_gid(self):		self.get_hdr2();return int(self.hdr2[39:47],8)
	def get_st_nlink(self):	self.get_hdr2();return int(self.hdr2[47:55],8)
	def get_st_rdev(self):		self.get_hdr2();return int(self.hdr2[55:63],8)
	def get_st_mtime(self):	self.get_hdr2();return int(self.hdr2[63:80],8)
	def get_st_nlen(self):		self.get_hdr2();return int(self.hdr2[80:84],8)
	def get_st_flag(self):		self.get_hdr2();return int(self.hdr2[84:88],8)	# ???
	def get_st_xszS(self):		self.get_hdr2();return int(self.hdr2[88:93],8)	# ???
	def get_st_raw_size(self):	self.get_hdr2();return int(self.hdr2[93:110],8)
#################################################################################################################
#														#
#					Inode Afio Base Class							#
#														#
#################################################################################################################
class Inode_Afio_Base(Inode):
	def __init__(self,seek=None,abspath=None):
	  self.abspath=abspath								# save abspath
	  self.header_address=seek							# save first address in the archive
	def get_inode_data(self,offset=0,size=None):					# call from read, if i_data=None
	  data=getattr(self,'i_data',None)  
          if data==None:
	    afio_inode=self.get_default_inode()
	    return afio_inode.get_st_data(offset,size)
            #if afio_inode.get_st_compress():
	    #  data=afio_inode.get_st_data()
	    #  self.i_data=data													# fix me! release data
	    #else:		return afio_inode.get_st_raw_data(offset,size)
	  if size==None:	return data[offset:]		 			# return read data
	  else:		return data[offset:(offset + size)] 			# return read data
#################################################################################################################
#														#
#					Inode Afio Class							#
#														#
#################################################################################################################
class Inode_Afio_070707(Inode_Afio_Base):					# old ASCII magic number
	def get_default_inode(self):
	  afio_inode=buffers.get(id(self),None)
	  if afio_inode==None:
	    afio_inode=Header_Afio_070707(self)
	    buffers[id(self)]=afio_inode
	  return afio_inode
class Inode_Afio_070717(Inode_Afio_Base):					# extended ASCII magic number
	def get_default_inode(self):
	  afio_inode=buffers.get(id(self),None)
	  if afio_inode==None:
	    afio_inode=Header_Afio_070717(self)
	    buffers[id(self)]=afio_inode
	  return afio_inode
class Inode_Afio_070727(Inode_Afio_Base):					# large ASCII magic number	
	def get_default_inode(self):
	  afio_inode=buffers.get(id(self),None)
	  if afio_inode==None:
	    afio_inode=Header_Afio_070727(self)
	    buffers[id(self)]=afio_inode
	  return afio_inode
#################################################################################################################
#														#
#					Load Archive Class							#
#														#
#################################################################################################################
class Afio_load():
	def __init__(self,target,abspath,fs):
	  self.target=target							# path to mount afio file in the file system
	  self.abspath=abspath							# abspath of the afio file
	  self.fs=fs								# file system
	  self.ino={}								# init st_ino(in afio file) Dict
	  pipe=subprocess.Popen('afio -tvBZ "%s"' % self.abspath, shell=True,stdout=subprocess.PIPE).stdout
	  f=open(self.abspath,"r")
          for line in pipe:
            i=int(line.split(None,2)[0])
	    f.seek(i)
	    hdr=f.read(6)
            self.load_header(i,hdr)
          f.close()  
	  pipe.close()
	  delattr(self,'ino')
	def load_header(self,seek=None,hdr=''):
	  if	hdr=='070707':inode=Inode_Afio_070707(seek=seek,abspath=self.abspath)	# old ASCII magic number
	  elif	hdr=='070717':inode=Inode_Afio_070717(seek=seek,abspath=self.abspath)	# extended ASCII magic number
	  elif	hdr=='070727':inode=Inode_Afio_070727(seek=seek,abspath=self.abspath)	# large ASCII magic number		not implemented yet
	  elif hdr=='070701':return 						# cpio new ASCII magic number	length: 110	 not implemented yet
	  elif hdr=='070702':return 						# cpio new ASCII magic number with CRC	 not implemented yet
	  elif hdr=='070703':return 						# Tcpio magic number of TI/E		 not implemented yet
          else:return
	  afio_inode=inode.get_default_inode()					# get afio_inode
	  afio_inode.print_header()
	  is_dir=False
          if S_ISDIR(afio_inode.get_st_mode()):				# DIR
	    inode.set_nlink(2);is_dir=True					# nlink = 2
	  elif afio_inode.get_st_nlink()>1:					# count up st_nlink, if inode is exist
	    afio_ino_key=(afio_inode.get_st_dev(),afio_inode.get_st_ino())
	    inode2=self.ino.get(afio_ino_key,None)
	    if inode2==None:	self.ino[afio_ino_key]=inode			# LinkedFile is first time, save inode.
	    else:		inode=inode2;inode.inc_nlink()			# LinkedFile is second time, use saved inode.
	  path='%s/%s' % (self.target,afio_inode.get_st_path())
	  self.fs._add_inode_nopermisson(path,inode,dirinode=is_dir)
#################################################################################################################
#														#
#				FUSE Operation Afiolzofs Class							#
#														#
#################################################################################################################
class Afiolzofs():
    """Afio Lzo filesystem. """ 
    def _check_symlink(self,target,source):					####### Symlink Check (root of the file system or not) #######
        targetpath=os.path.split(target)
        if targetpath[0]=="/":							# if target is root of the mountpoint
          sourceabspath=os.path.normpath(os.path.join(self.mountpoint,source))	# get abspath of the file
          f=open(sourceabspath);top50=f.read(50);f.close()			# read top 50 byte of the file 			# if source is dir? 
          filetype=_filetype(top50)						# get file type 
	  if "ASCII cpio archive":						# if 'ASCII cpio archive'
	    targetname=".%s" % targetpath[1]					# 'file name in the file system' for loading afio archive 
	    targetdir=os.path.join(targetpath[0],targetname)			# 'path name in the file system' for loading afio archive
	    Ramfs.mkdir(self,targetdir, S_IMODE(0755))				# make dir for load the archive
            Afio_load(targetdir,sourceabspath,self)				# load the archive
            return targetname							# afio.lzo file
	return source								# return 'file name in the file system'
    def _symlink(self, target, source): 					###### symlink ######
	  source=self._check_symlink(target,source)				# check root of the file system -> mount afio.lzo
	  Ramfs.symlink(self,target, source)					# make normal symlink
class Afiolzofs_SymlinkLoading(Afiolzofs):
    def symlink(self, target, source): 					###### symlink ######
	self._symlink(target, source)
class AfiolzoRamfs(Afiolzofs,Ramfs):				pass 
class AfiolzoRomfs(Afiolzofs,Romfs):				pass 
class AfiolzoSLRamfs(Afiolzofs_SymlinkLoading,Ramfs):			pass 
class AfiolzoSLRomfs(Afiolzofs_SymlinkLoading,Romfs):			pass 
class LogAfiolzoRamfs(LoggingMixIn,Afiolzofs,Ramfs):			pass 
class LogAfiolzoRomfs(LoggingMixIn,Afiolzofs,Romfs):			pass 
class LogAfiolzoSLRamfs(LoggingMixIn,Afiolzofs_SymlinkLoading,Ramfs):	pass 
class LogAfiolzoSLRomfs(LoggingMixIn,Afiolzofs_SymlinkLoading,Romfs):	pass 
#################################################################################################################
#														#
#						Buffers								#
#														#
#################################################################################################################
class Buffers():	
    def __init__(self):
	self.data={}
	self.key=[]
    def get(self,key,default):
	val=self.data.get(key,None)
	if val != None:print "exist in the Buffer"
	else:print "not extst in the Buffer"
	return val
    def __setitem__(self,key,val):
	if not key in self.data:
	  self.key.append(key)
	self.data[key]=val
	print "keys:",len(self.key)
	if len(self.key)>20:
	  i=self.key.pop(0)
	  del self.data[i]
#################################################################################################################
#														#
#						Main								#
#														#
#################################################################################################################
if __name__ == "__main__": 
    if len(argv) < 2: 
        print 'usage: %s [options] [archives ...] <mountpoint>' % argv[0] 
        print '  "old ASCII cpio archive" or "afio archive" files will be loaded.'
	print '  (Options) '
	print '    --ramfs		: allow temporaly write'
	print '    --nosymlink		: do not allow archive loading with symlink'
	print '    --logging		: with logging'
	print '    --debug		: enable debug output (implies -f)'
	print '    --foreground	: foreground operation'
	print '    --nothreads		: disable multi-threaded operation'
	print '    --allow_other	: allow access to other users'
	print '    --allow_root	: allow access to root'
	print '    --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 <mountpoint> to load it.'  
        print '  (Example)'
	print '    %s mountpoint		# mount afiolzofs to mountpoint' % argv[0] 
        print '    ln -s XXX.afio.lzo mountpoint/XXX	# make symlink'
        print '    ls mountpoint/XXX			# list the archive'
        print '   Afiolzofs makes symlink not to the archive, but to mountpoint/.XXX instead,'
        print '   and loads the archive inode data to mountpoint/.XXX'
        print '   If you read file contents, Afiolzofs will read it from the archive.'
        exit(1)
    args=argv[1:]
    keyargs={}
    mountpoint=os.path.abspath(args.pop())
    key=('--logging','--nosymlink','--ramfs','--debug','--foreground','--nothreads','--allow_other','--allow_root','--kernel_cache')
    if key[0] in args:
      if key[1] in args:
        if key[2] in args:	afiolzofs= LogAfiolzoRamfs(mountpoint)
        else:			afiolzofs= LogAfiolzoRomfs(mountpoint)
      else:
        if key[2] in args:	afiolzofs= LogAfiolzoSLRamfs(mountpoint)
        else:			afiolzofs= LogAfiolzoSLRomfs(mountpoint)
    else:
      if key[1] in args:
        if key[2] in args:	afiolzofs= AfiolzoRamfs(mountpoint)
        else:			afiolzofs= AfiolzoRomfs(mountpoint)
      else:
        if key[2] in args:	afiolzofs= AfiolzoSLRamfs(mountpoint)
        else:			afiolzofs= AfiolzoSLRomfs(mountpoint)
    if key[3] in args:keyargs[key[3][2:]]=True
    if (key[4] in args)or(key[0] in args):keyargs[key[4][2:]]=True
    if key[5] in args:keyargs[key[5][2:]]=True
    if key[6] in args:keyargs[key[6][2:]]=True
    if (key[7] in args)and(not key[6] in args):keyargs[key[7][2:]]=True
    if key[8] in args:keyargs[key[8][2:]]=True
#    if key[9] in args:keyargs[key[9][2:]]=True
    for i in key:
      while i in args:args.remove(i)
    buffers=Buffers()
    for i in args:
      basename=os.path.basename(i)
      target=os.path.join(mountpoint,'.'+basename)
      afiolzofs._symlink(target,i)
    fuse = FUSE(afiolzofs, mountpoint,use_ino=True,**keyargs) 

