#!/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.37 (18 Feb 2011)
#  fix write function
#  Popen error check
#  fix data read
#  fix lzop load function
#  fix data_buffer
#  fix fork&subprocess zombie
# 0.0.36 (11 Feb 2011)
#  lzop		get file size from file formt, "Multiple blocks compression, Random-access reading"
#  gzip		get file size from file formt
#  xz		get file size from file formt, "Multiple blocks compression" is not implement xz command line tool yet.
#  bzip2	get file size with decompress
#
#  setgid
#  sticky bit
# 0.0.35 (29 Jan 2011)
#  new buffer class
# 0.0.34 (21 Jan 2011)
#  file handle = id(inode)
#  store 'file handle' to buffer
#  short cut for dir accsess
# 0.0.33 (15 Jan 2011)
#  buffer issue
#  check lzop file signature
#  check gzip file signature
# 0.0.32 (14 Jan 2011)
#  Header Class 
# 0.0.31 (09 Jan 2011)
#  comment
# 0.0.30 (09 Jan 2011)
#  separate inode_buffer,data_buffer
#  filetype
# 0.0.29 (08 Jan 2011)
#  data,afio_inode buffer
# 0.0.28 (08 Jan 2011)
#  long lzop file >256K block decompress, but non buffered
# 0.0.27 (07 Jan 2011)
#  long lzop file >256K block decompress(experimental)
#  AFIO header class
# 0.0.26 (01 Jan 2011)
#  long lzop file >256K
# 0.0.25 (29 Dec 2010)
#  Xattr,Attr,Dir function
#  get_items,items,et,set_inode_data function
# 0.0.24 (24 Dec 2010)
#  _setnlink(calc_nlnk)
#  fix mount option
#  access
# 0.0.23 (23 Dec 2010)
#  atime at readonly
#  fix write,trunc
#  fix mount option
# 0.0.22 (22 Dec 2010)
#  error message of 'cannot find fuse.py'
# 0.0.21 (21 Dec 2010)
#  options
# 0.0.20 (19 Dec 2010)
#  Ramfs,AfioLzofs
# 0.0.19 (18 Dec 2010)
#  bug fix
# 0.0.18 (18 Dec 2010)
#  fix open,opendir,use_ino
#  inode dict -> class
# 0.0.17 (17 Dec 2010)
#  fix rm
#  fix open
# 0.0.16 (12 Dec 2010)
#  fix some error
# 0.0.15 (08 Dec 2010)
#  Ramfs Class and Afiolzofs Class
# 0.0.14 (08 Dec 2010)
#  start to separate Temp Ramfs Block and AFIO access Block
# 0.0.13 (07 Dec 2010)
#  _get_inode
#  read_afio_symlink		no read symlinkpath at first
#  read_afio_item_offsetsize	no read all and store for non compressed data at only read.
# 0.0.12 (06 Dec 2010)
#  fix some error
# 0.0.11 (05 Dec 2010)
#  x permission
# 0.0.10 (04 Dec 2010)
#  r,w permission
# 0.0.9 (03 Dec 2010)
#  test implementation for permission to dirread
# 0.0.8 (02 Dec 2010)
#  fix some error
# 0.0.7 (01 Dec 2010)
#  fix some of the atime,mtime,ctime
#  fix some error code
# 0.0.6 (30 Nov 2010)
#  fix some of the atime,mtime,ctime
#  use get_fuse_context
# 0.0.5 (29 Nov 2010)
#  list many item shuld be fix.
#  fix some of them.
# 0.0.4 (27 Nov 2010)
#  support extended ASCII format
#  fix "unlink error"
#  fix "other magic number"
#  not to use defaultdict
#  fix "rename error"
#  change inode structure
# 0.0.3 (26 Nov 2010)
#  fix "cannot mount 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. 


#################################################################################################################
#														#
#	Some comments are copied from (http://www.freedesktop.org/wiki/CommonExtendedAttributes) 		#
#	CS135 FUSE Documentation										#
#	© 2010, Geoff Kuenning											#
#														#
#################################################################################################################

try:
  from fuse import FUSE, FuseOSError, Operations, LoggingMixIn ,fuse_get_context
except:
  print 'I cannot find fuse.py.'
  print 'Please download fuse.py from http://code.google.com/p/fusepy.'
  print 'And put it in the same dir.'
  exit()

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

from types import MethodType
import copy
from sys import argv, exit 
from time import time
import subprocess
import pwd
import grp
import os
from errno import * 
"""
ENOSYS		(Function not implemented) 
EPERM		(Operation not permitted) 
ENOENT		(No such file or directory) 
EIO		(I/O error) 
ENXIO		(No such device or address) 
EBADF		(Bad file number) 
ENOMEM		(Out of memory) 
EACCES		(Permission denied) 
EFAULT		(Bad address) 
ENOTBLK		(Block device required) 
EBUSY		(Device or resource busy) 
EEXIST		(File exists) 
EXDEV		(Cross-device link) 
ENODEV		(No such device) 
ENOTDIR		(Not a directory) 
EISDIR		(Is a directory) 
ENOSPC		(No space left on device) 
ESPIPE		(Illegal seek) 
EROFS		(Read-only file system) 
EPIPE		(Broken pipe) 
ENAMETOOLONG	(File name too long) 
ENOTEMPTY	(Directory not empty) 
"""
#################################################################################################################
#														#
#					Inode Class								#
#														#
#################################################################################################################
class Inode():
	"""Inode Class of the Ramfs,Romfs"""
	def __init__(self,time=0,context=(0,0),address=None,header=None):
	  if header==None:		# normal inode
	    self.st_ctime=time
	    self.st_mtime=time
	    self.st_atime=time
	    self.st_uid=context[0]
	    self.st_gid=context[1]
	  else:			# inode linked to archive file
	    self.header=header
	    self.header_address=address
	### for buffers ###
	def get_inode(self):return self							# used in header_buffers
	def add_inode(self):									# add inode to header_buffers
	    inode_id=id(self)
	    if not hasattr(self,'header'):
	      header_buffers.add(inode_id,self)							# add header to header buffer
	    return inode_id
	### Inode Dir access functions ###
	def set_dentry(self,name,inode):							# add inode to the dir entry (create it if no i_dentry)
	  i_dentry=getattr(self,'i_dentry',None)
	  if i_dentry==None:			self.i_dentry={name:inode}			#   create dentry & add inode
	  elif hasattr(i_dentry,name):		return True					#   name is exist & return True -> should be error(file exist)
	  else:				i_dentry[name]=inode				#   add inode
	def get_dentry(self,name):		return getattr(self,'i_dentry',{})[name]	# get dir entry
	def pop_dentry(self,name):		return getattr(self,'i_dentry',{}).pop(name)	# pop dir entry to delete it or to move it
	def list_dentry(self):			return getattr(self,'i_dentry',{}).keys()	# list dir entry, it do not contain '.' and '..'
	def len_dentry(self):			return len(getattr(self,'i_dentry',{}))	# return number of the dir entrys (for dir delete check),it do not contain '.' and '..'
	### Inode Xattr access functions ###
	def set_xattr(self,name,val):								# add parameter to the xattr entry (create it if no i_xattr)
	  i_xattr=getattr(self,'i_xattr',None)
	  if i_xattr==None:			self.i_xattr={name:val}				#   create xattr & add parameter
	  else:				i_xattr[name]=val				#   add parameter
	def get_xattr(self,name):		return getattr(self,'i_xattr',{})[name]	# get xattr entry
	def pop_xattr(self,name):		return getattr(self,'i_xattr',{}).pop(name)	# delete xattr entry
	def ls_xattr(self):			return getattr(self,'i_xattr',{}).keys()	# list xattr entry
	### Inode Set Attr functions ###
	def set_nlink(self,n=1):
	  if n!=1 or hasattr(self,'st_nlink'):	self.st_nlink=n					# 'No st_nlink' means st_nlink=1
	def inc_nlink(self):
	  if hasattr(self,'st_nlink'):		self.st_nlink+=1
	  else:				self.st_nlink=2
	def dec_nlink(self):
	  if hasattr(self,'st_nlink'):		self.st_nlink-=1
	  else:				self.st_nlink=0
	def update_atime(self,time):		self.st_atime=time
	def update_mtime(self,time):		self.st_mtime=time
	def update_ctime(self,time):		self.st_ctime=time
	def set_rdev(self,n=0):		self.st_rdev=n
	def set_mode(self,mode):		self.st_mode=mode
	def set_gid(self,gid):			self.st_gid=gid
	def set_uid(self,uid):			self.st_uid=uid
	### Inode Get Attr functions ###
	def get_dir_type(self):
	  header=getattr(self,'header',None)
	  if header==None:return False
	  return header.get_dir_type(self)
	def get_items(self,keys=('st_mode','st_uid','st_gid')):		# get 'mode','uid','gid' for permission check
	  return [val for key,val in self.items(keys)]
	def items(self,keys=('st_mode','st_uid','st_gid','st_rdev','st_atime','st_ctime','st_mtime','st_ino','st_size','st_nlink')):
	  header=None								# called from fuse.py for stat file
	  for key in keys:
	    if key=='st_ino':		yield ('st_ino',id(self))
	    elif key=='st_nlink':	yield (key,getattr(self,key,1))
	    elif key=='st_size':
	      val=getattr(self,'i_data',None)					# calc size from i_data (i_data is the file data)
	      if val==None:							# no i_data in the inode
	        if header==None:header=self.get_header()			#  if no header -> get header from archive file
	        val=header.get(key,0)						#  read the data length from archive file
	      else:val=len(val)
	      yield (key,val)
	    else:
	      val=getattr(self,key,None)
	      if val==None:							# no data in the inode
	        if header==None:header=self.get_header()			#  if no header -> get header from archive file
	        val=header.get(key,0)						#  read the data from archive file
	      yield (key,val)
	### Inode File IO functions ###
	def get_header(self):
	  header=getattr(self,'header',None)
	  if header==None:return {}
	  return header.copy(inode=self)
	def get_inode_data(self,offset=0,size=None):				#
	  data=getattr(self,'i_data',None)  
          if data==None:							# no data in the inode -> read from archive file
	    header=self.get_header()						#  if no header -> get header from archive file
	    if header=={}:return ''
	    return header.get_st_data(offset,size)				#  read from archive file
	  if size==None:	return data[offset:]
	  else:		return data[offset:(offset + size)]
	def set_inode_data(self,data,offset=0):				# set data for write 
	  i_data=self.get_inode_data()
	  #if len(i_data)<offset :print "--------->",len(i_data),offset
	  self.i_data = "".join([i_data[0:offset],"\0"*(offset-len(i_data)),data,i_data[offset+len(data):]])
	  return len(data)
	def trunc_inode_data(self,offset):					# truncate data length
	  self.i_data=self.get_inode_data(0,offset)
#################################################################################################################
#														#
#				FUSE Operation Ramfs Class							#
#														#
#################################################################################################################
class Ramfs(Operations):			# for normal use
    """Memory File System Class (Read Write but No Save)""" 
    def __init__(self,mountpoint): 
        self.mountpoint=mountpoint								# abspath of the mount point
	self.uid=os.getuid()									# file system owner id
	self.inode=Inode(time(),(self.uid,os.getgid()))						# root inode of the file system
	self.inode.set_mode(S_IFDIR | 0755)							#   set as dir
	self.inode.set_nlink(2)									#   empty dir nlink is 2 (in linux)
    ######## Ramfs function (Permission check) ########
    def _is_inode_USER(self,inode,context,uname,items,filesystem=False):			# Permission check(file owner,root or filesystem owner)
	if (context[0]==items[1])or(context[0]==0):return					# items='st_mode','st_uid','st_gid'
	if filesystem and(context[0]==self.uid):return					# filesystem owner can change UID of any files.
	raise FuseOSError(EPERM)								# (Operation not permitted)
    def _is_inode_R_OK(self,inode,context,uname,items):					# Permission check(Readable)
	if items[0] & S_IROTH:					return				# allow other
	if (items[0] & S_IRUSR) and (items[1] == context[0]):	return				# allow owner & I'm owner
	if not(items[0] & S_IRGRP):				raise FuseOSError(EACCES)	# don't allow group
	if uname in grp.getgrgid(items[2]).gr_mem:		return				# user is the group member
	if context[1] != items[2]:				raise FuseOSError(EACCES)	# group is not same
    def _is_inode_W_OK(self,inode,context,uname,items):					# Permission check(writable)
	if items[0] & S_IWOTH:					return				# allow other
	if (items[0] & S_IWUSR) and (items[1] == context[0]):	return				# allow owner & I'm owner
	if not (items[0] & S_IWGRP):				raise FuseOSError(EACCES)	# don't allow group
	if uname in grp.getgrgid(items[2]).gr_mem:		return				# user is the group member
	if context[1] != items[2]:				raise FuseOSError(EACCES)	# group is not same
    def _is_inode_X_OK(self,inode,context,uname,items):					# Permission check(Excutable)   # fix me!    X for other
	if items[0] & S_IXOTH:					return				# allow other
	if (items[0] & S_IXUSR) and (items[1] == context[0]):	return				# allow owner & I'm owner
	if not(items[0] & S_IXGRP):				raise FuseOSError(EACCES)	# don't allow group
	if uname in grp.getgrgid(items[1]).gr_mem:		return				# user is the group member
	if context[1] != items[1]:				raise FuseOSError(EACCES)	# group is not same
    ######## Ramfs function (Get inode with Permission check) ########
    def _get_dir_inode_nopermisson(self,path):				###### Get inode without any permission checking ######
	if path=="/":return self.inode				# if path is root, return root inode of the file system
	pname=os.path.split(path)
	inode_dir=self._get_dir_inode_nopermisson(pname[0])
	try:return inode_dir.get_dentry(pname[1])			# get inode
	except KeyError:						# if no inode
	    inode=Inode(time(),(self.uid,os.getgid()))			#   make one as dir inode
	    inode.set_mode( S_IMODE(0755) | S_IFDIR )			#   set mode (dir)
	    inode.set_nlink(2)
	    inode_dir.set_dentry(pname[1],inode)
	    inode_dir.inc_nlink()
	    return inode
    def _get_inode(self,path,context,uname):				###### Get inode from path,context,uname #######
	if path=="/":return self.inode				# if path is root, return root inode of the file system
	else:								# not root of the file system
	  pname=os.path.split(path)
	  inode_dir=self._get_inode(pname[0],context,uname)
	  if not inode_dir.get_dir_type():
	    items=inode_dir.get_items()
	    self._is_inode_X_OK(inode_dir,context,uname,items)		# dir access permission check
	  try:return inode_dir.get_dentry(pname[1])			# if 'no name in dir list' or 'not dir' -> error
	  except KeyError:raise FuseOSError(ENOENT)			# (No such file or directory)
    def _get_inode_for_access(self,path,fh=None):			###### Get inode for Access ######
	# (FUSE Context)
	# uid		The (numeric) user ID of the process invoking the operation. 
	# gid		The (numeric) group ID of the process invoking the operation. 
	# pid		The thread (process) ID of the process invoking the operation. 
	# private_data	A pointer (void*) to the private data returned by the init function. 
	# umask		The umask of the process invoking the operation. 				# not implemented
	context=fuse_get_context()					# get system call uid and gid
	uname=pwd.getpwuid(context[0]).pw_name				# get system call uname
	inode=None
	if fh != None:
	  header=header_buffers.get(fh)
	  if header !=None:
	    inode=header.get_inode()
	    # print "find inode in the buffer!!!",fh
	  else:
	    pass
	    # print "no such inode in the buffer",fh
	if inode==None:inode=self._get_inode(path,context,uname)
	items=inode.get_items()
	return inode,context,uname,items
    def _get_inode_for_user(self,path,filesystem=False):		###### Get inode for user (or root) ######
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
	self._is_inode_USER(inode,context,uname,items,filesystem)	# user check
	return inode,items
    def _get_inode_for_read(self,path,fh=None):			###### Get inode for Read ######
	inode,context,uname,items=self._get_inode_for_access(path,fh)	# access permission check
	self._is_inode_R_OK(inode,context,uname,items)			# read permission check
	return inode,items
    def _get_inode_for_write(self,path,fh=None):			###### Get inode for Write ######
	inode,context,uname,items=self._get_inode_for_access(path,fh)	# access permission check
	self._is_inode_W_OK(inode,context,uname,items)			# write permission check
	return inode,items
    def _get_dir_inode_for_create(self,path):				###### Get dir inode for Rename or Link ######
	pname=os.path.split(path)					# split to dirpath and filename 
	inode,context,uname,items=self._get_inode_for_access(pname[0])	# access permission check
	self._is_inode_W_OK(inode,context,uname,items)			# dir write permission check
	return inode,pname[1]						# return inode_dir and filename
    def _get_inode_for_delete(self,path):				###### Get inode for Delete ######
	# (sticky bit)	 [From Wikipedia]			
	# When the sticky bit is set, only the item's owner, the directory's owner, or the superuser can rename or delete files.  #### fix me! directory?????
	# Without the sticky bit set, any user with write and execute permissions for the directory can rename or 
	# delete contained files, regardless of owner.
	pname=os.path.split(path)					# split to dirpath and filename 
	inode,context,uname,items=self._get_inode_for_access(pname[0])	# access permission check items:'st_mode','st_uid','st_gid'
	self._is_inode_W_OK(inode,context,uname,items)			# dir write permission check
	now=time()
	inode_file=inode.get_dentry(pname[1])
	if items[0] & S_ISVTX :					# sticky bit is ON
	  if   context[0]==0 : pass					# you are root
	  elif context[0]==items[1] : pass				# you are directory's owner
	  else :
	    items_file=inode_file.get_intems()
	    if items_file[1]==items[1]: pass				# you are item's owner
	    else: raise FuseOSError(EACCES)				# Don't allow with sticky bit
	return inode_file,inode,pname[1],now				# return inode_file, inode_dir, filename and now
    def _get_inode_for_create(self,path):				###### Get default inode for Create File,Dir,Symlink or Node ######
	pname=os.path.split(path)					# split to dirpath and filename 
	inode,context,uname,items=self._get_inode_for_access(pname[0])	# access permission check
	self._is_inode_W_OK(inode,context,uname,items)			# dir write permission check
	now=time()
	# (setgid)       [From Wikipedia]
	# Setting the setgid permission on a directory (chmod g+s) causes new files and subdirectories created 
	# within it to inherit its groupID, rather than the primary groupID of the user who created the file
	# (the ownerID is never affected, only the groupID). 
	# Newly created subdirectories inherit the setgid bit. Note that setting the setgid permission on a directory only affects
	# the groupID of new files and subdirectories created after the setgid bit is set, and is not applied to existing entities.
	# (setuid)       [From Wikipedia]
	# The setuid permission set on a directory is ignored on UNIX and Linux systems. 
	# FreeBSD can be configured to interpret it analogously to setgid, namely, to force all files and sub-directories to be 
	# owned by the top directory owner.
        inode_file = Inode(now,context)
	if items[0] & S_ISGID :					# setgid is ON
	  inode_file.set_gid(items[2])					# set item's GID to directory's GID
	return inode_file,inode,pname[1],now				# return inode_file(new inode), inode_dir, filename and now
    ######## Ramfs function (inode Operation) ########
    def _update_atime(self,inode,now):
        inode.update_atime(now)						# write file data -> change mtime
    def _add_inode_nopermisson(self,path,inode,is_dir):		# add inode without any permission checking
	pname=os.path.split(path)
	inode_dir=self._get_dir_inode_nopermisson(pname[0])
	if inode_dir.set_dentry(pname[1],inode):raise FuseOSError(EEXIST)	# (File exists) 
	if is_dir:inode_dir.inc_nlink()
    def _add_inode_as_file(self,inode_dir,inode,filename,now):		#
	if inode_dir.set_dentry(filename,inode):raise FuseOSError(EEXIST)	# (File exists) 
	inode_dir.update_mtime(now)					# write to dir list	-> change mtime
	inode_dir.update_ctime(now) 					# ???
	inode.update_ctime(now)						# change nlink 		-> change ctime
    def _add_inode_as_dir(self,inode_dir,inode,filename,now):		#
	self._add_inode_as_file(inode_dir,inode,filename,now)
	inode_dir.update_mtime(now)					# write to dir list	-> change mtime
	inode_dir.inc_nlink()
	inode.update_mtime(now) 					# change dir list	-> change mtime
    def _removefile_inode(self,inode,inode_dir,filename,now):		#
	try:inode_dir.pop_dentry(filename)
	except KeyError:raise FuseOSError(ENOENT)			# (No such file or directory)
	inode_dir.update_mtime(now)					# change dir list -> change mtime
	inode_dir.update_ctime(now)					# change mtime -> change ctime
	inode.update_ctime(now) 					# change nlink -> change ctime
    def _removedir_inode(self,inode,inode_dir,filename,now):		#
	self._removefile_inode(inode,inode_dir,filename,now)
        inode_dir.dec_nlink()
    ######## FUSE Operating function (Start File System) ########
    # FUSE Documentation
    # http://www.cs.hmc.edu/~geoff/classes/hmc.cs135.201001/homework/fuse/fuse_doc.html
    def init(self, path):
        """Called on filesystem initialization. Path is always /
           Use it instead of __init__ if you start threads on initialization."""
	# The return value of this function is available to all file operations in the private_data field of fuse_context. 
	# It is also passed as a parameter to the destroy() method. 
        pass
    def statfs(self, path):						##### statfs (See statvfs(2) for a description of the structure contents.) #####
	# Usually, you can ignore the path.
	# programs (like df) determine the free space. 
        return dict(f_bsize=0x00040000, f_blocks=4096, f_bavail=2048) 
    def destroy(self, path):
        """Called on filesystem destruction. Path is always /"""
        pass
    ######## FUSE Operating function (Attr) ########
    def access(self, path, amode):
	# This is the same as the access(2) system call. 
	# It returns 	-ENOENT if the path doesn't exist,
	#		-EACCESS if the requested permission isn't available, or 
	#		0 for success. 
	# Note that it can be called on files, directories, or any other object that appears in the filesystem.
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check (ENOENT,EACCESS)
	if os.X_OK & amode:self._is_inode_X_OK(inode,context,uname,items)
	if os.W_OK & amode:self._is_inode_W_OK(inode,context,uname,items)
	if os.R_OK & amode:self._is_inode_R_OK(inode,context,uname,items)
    def getattr(self, path, fh=None): 					###### getattr ######
	# (getattr) The "stat" structure is described in detail in the stat(2) manual page.
	# (fgetattr) called when fgetattr(2) is invoked by the user program. 
	inode,context,uname,items=self._get_inode_for_access(path,fh)	# access permission check
        return inode							# return inode
    def chmod(self, path, mode):					###### chmod (See chmod(2) for details.) ######
	# Only the permissions bits of mode should be examined.
	inode,items=self._get_inode_for_user(path)			# user check
	inode.set_mode( S_IFMT(items[0]) | S_IMODE(mode) )		# see S_IFMT,S_IMODE
	now=time()
        inode.update_ctime(now) 					# change inode data -> change ctime
    def chown(self, path, uid, gid):					###### chown (See chown(2) for details.) ######
	# FUSE doesn't deal particularly well with file ownership,
	# since it usually runs as an unprivileged user and this call is restricted to the superuser.
	inode,items=self._get_inode_for_user(path,filesystem=True)	# user check
        if uid != -1:inode.set_uid(uid) 				# set uid with check -1 						# fix me! only root,mount user
        if gid != -1:inode.set_gid(gid) 				# set gid with check -1 						# fix me ! group which user is in
	now=time()
        inode.update_ctime(now) 					# change inode data -> change ctime
    def utimens(self, path, times=None):				###### utime (see utimensat(2) for full details.) ######
	inode,items=self._get_inode_for_user(path)			# user check
        now = time() 
        atime, mtime = times if times else (now, now) 			# atime mtime can be changed by touch (utimes),but not ctime
	inode.update_atime(atime)					# In BSD symlink's atime,mtime,ctime is can changed with lutimes,	cannot change read NG
	inode.update_mtime(mtime)					# but not in Linux because no lutimes.					can change if dir mode ok
	inode.update_ctime(now)						# change atime,mtime -> change ctime
    # def lock(self,path, fh, cmd, lock): pass				##### lock (Perform a POSIX file-locking operation.) #####
    lock = None
    # def bmap(self,path, blocksize, idx): pass				##### bmap (This function is similar to bmap(9).) #####
    bmap = None

    ######## FUSE Operating function (XAttr) ########			# fix me!!!
    # Extended attributes (xattrs) can be set in different namespaces. Linux uses "security", "system", "trusted", and "user". 
    def listxattr(self, path):						##### listxattr (See listxattr(2).) #####
	# This should be implemented only if HAVE_SETXATTR is true. 
	raise FuseOSError(ENOSYS)										# (Function not implemented)
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        return inode.ls_xattr() 
    def getxattr(self, path, name, position=0):			##### getxattr (See getxattr(2).) #####
	# This should be implemented only if HAVE_SETXATTR is true. 
	raise FuseOSError(ENOSYS)										# (Function not implemented)
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        try:return inode.get_xattr(name)
        except KeyError:raise FuseOSError(ENOSYS)			# (Function not implemented)
    def setxattr(self, path, name, value, options, position=0):	#####  (See setxattr(2).) #####
	# This should be implemented only if HAVE_SETXATTR is true. 
	raise FuseOSError(ENOSYS)										# (Function not implemented)
	inode,items=self._get_inode_for_user(path)			# user check
	inode.set_xattr(value)						# Ignore options 
    def removexattr(self, path, name):					##### removexattr () #####
	raise FuseOSError(ENOSYS)										# (Function not implemented)
	inode,items=self._get_inode_for_user(path)			# user check
        try:inode.pop_xattr(value)
        except KeyError:raise FuseOSError(ENOSYS)			# (Function not implemented)
    ######## FUSE Operating function (Read Dir) ########
    def opendir(self, path):
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS)		# Function not implemented
	self._is_inode_R_OK(inode,context,uname,items)			# read permission check
        return inode.add_inode()					# return fd
    def readdir(self, path, fh):					###### readdir ######
        inode,items=self._get_inode_for_read(path,fh)			# read permission check
        if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS)		# (Function not implemented)
        dirnames=inode.list_dentry()					# read dir list
	dirnames.append('..')						# append parent dir '..'
	dirnames.append('.')						# append parent dir '.'
        self._update_atime(inode,time())				# read data -> change atime
	return dirnames						# return dir list
    def releasedir(self, path, fh):
        return 0
    def fsyncdir(self, path, datasync, fh):
        return 0
    ######## FUSE Operating function (Create or Remove Dir) ########
    def mkdir(self, path, mode): 					###### mkdir ######
	inode,inode_dir,filename,now=self._get_inode_for_create(path)	# write permission check
        inode.set_mode( S_IMODE(mode) | S_IFDIR )			# set mode (dir)
        inode.set_nlink(2)
	self._add_inode_as_dir(inode_dir,inode,filename,now)		# add inode as dir
    def rmdir(self, path): 						###### rmdir (See rmdir(2) for details.) ######
	# Remove the given directory. This should succeed only if the directory is empty (except for "." and "..").
        inode,inode_dir,filename,now=self._get_inode_for_delete(path)	# write permission check
	items=inode.get_items()
        if not S_ISDIR(items[0]):raise FuseOSError(ENOSYS)		# Function not implemented (not Dir shuld be delete unlink)
	if filename == '/':						# Shuld be check root? ??
	  raise FuseOSError(EPERM)					# (Operation not permitted)
	elif inode.len_dentry()>0:					# 
	  raise FuseOSError(ENOTEMPTY)					# (Directory not empty) 
	else:
	  self._removedir_inode(inode,inode_dir,filename,now)		# remove dir
    ######## FUSE Operating function (Create or Remove File) ########
    def mknod(self, path, mode, dev):					###### mknod ######
	# Make a special (device) file, FIFO, or socket. See mknod(2) for details.
	mask=S_IFSOCK | S_IFBLK | S_IFCHR | S_IFIFO			#
	if (mode & mask) == 0 :raise FuseOSError(EPERM)		# (Operation not permitted) 
	inode,inode_dir,filename,now=self._get_inode_for_create(path)	# write permission check
	inode.set_mode( S_IMODE(mode) | (mode & mask) )			# set node mode 		# fix me! check multi mode or not?
	inode.set_rdev(dev)						# set rdev
	self._add_inode_as_file(inode_dir,inode,filename,now)		# add inode as file
    def create(self, path, mode): 					###### create ######
	inode,inode_dir,filename,now=self._get_inode_for_create(path)	# write permission check
        inode.set_mode( S_IFREG | S_IMODE(mode)	)			# set reguler file mode
        inode.set_inode_data('')					# set no data
	self._add_inode_as_file(inode_dir,inode,filename,now)		# add inode as file
        return inode.add_inode()					# return fd
    def unlink(self, path): 						###### unlink (See unlink(2) for details.) ######
        inode,inode_dir,filename,now=self._get_inode_for_delete(path)	# write permission check
	items=inode.get_items()
        if S_ISDIR(items[0]):raise FuseOSError(ENOSYS)			# Function not implemented (Dir shuld be delete rmdir)
        inode.dec_nlink()
	self._removefile_inode(inode,inode_dir,filename,now)		# remove file
    def rename(self, old, new):					###### rename (See rename(2) for full details.) ######
	# Note that the source and target don't have to be in the same directory, 
	# so it may be necessary to move the source to an entirely new directory.
        inode,old_dir,old_name,now=self._get_inode_for_delete(old)	# write permission check
	inode_dir,filename=self._get_dir_inode_for_create(new)		# write permission check
	items=inode.get_items()
        if S_ISDIR(items[0]):
	  self._add_inode_as_dir(inode_dir,inode,filename,now)		# add dir link
	  self._removedir_inode(inode,old_dir,old_name,now)		# remove dir (Should be first ?)
	else:
	  self._add_inode_as_file(inode_dir,inode,filename,now)		# add file link
	  self._removefile_inode(inode,old_dir,old_name,now)		# remove file
    def link(self, target, source):					###### link (See link(2) for details.) ######
	# be aware that they have an effect on how unlink works.
	inode_dir,filename=self._get_dir_inode_for_create(target)	# write permission check
	inode,context,uname,items=self._get_inode_for_access(source)	# we can make link of other's file
	now=time()							# get time now
        if S_ISDIR(items[0]):raise FuseOSError(ENOSYS)			# Function not implemented
	self._add_inode_as_file(inode_dir,inode,filename,now)		# add inode as file
    ######## FUSE Operating function (Sym Link) ########
    def readlink(self, path):						###### readlink ###### 
	# See readlink(2) for how to handle a too-small buffer and for error codes.
        inode,items=self._get_inode_for_read(path)			# read permission check
        if not S_ISLNK(items[0]):raise FuseOSError(EPERM)		# (Operation not permitted)
        self._update_atime(inode,time())				# read data -> change atime
	return inode.get_inode_data()
    def symlink(self, target, source): 				###### symlink ######
	inode,inode_dir,filename,now=self._get_inode_for_create(target)	# write permission check
        inode.set_mode(S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO )		# set symlink mode
        inode.set_inode_data(source)					# set source path as data
	self._add_inode_as_file(inode_dir,inode,filename,now)		# add inode as file

    ######## FUSE Operating function (Read or Write File) ########
    def open(self, path, flags):					###### open ######
	# Open a file. If you aren't using file handles, this function should just check for existence and 
	# permissions and return either success or an error code.
	# If you use file handles, you should also allocate any necessary structures and set fi->fh.
	# In addition, fi has some other fields that an advanced filesystem might find useful
	# see the structure definition in fuse_common.h for very brief commentary. 
	inode,context,uname,items=self._get_inode_for_access(path)	# access permission check
        if S_ISDIR(items[0]):raise FuseOSError(ENOSYS)			# Function not implemented (Dir shuld be delete rmdir)
	if (flags & 3) == os.O_RDONLY:
	  self._is_inode_R_OK(inode,context,uname,items)
	elif (flags & 3) == os.O_RDWR:
	  self._is_inode_R_OK(inode,context,uname,items)
	  self._is_inode_W_OK(inode,context,uname,items)
	elif (flags & 3)== os.O_WRONLY:
	  self._is_inode_W_OK(inode,context,uname,items)
        return inode.add_inode()
    def read(self, path, size, offset, fh):  				###### read (See read(2) for full details.) ######
        inode,items=self._get_inode_for_read(path,fh)			# read permission check
        if not S_ISREG(items[0]):raise FuseOSError(EPERM)		# (Operation not permitted)
        self._update_atime(inode,time())				# read data -> change atime
	return inode.get_inode_data(offset,size)			# return read data
    def write(self, path, data, offset, fh): 				###### write ######
        inode,items=self._get_inode_for_write(path,fh)			# write permission check
        if not S_ISREG(items[0]):raise FuseOSError(EPERM)		# (Operation not permitted)
	now=time()
        inode.update_mtime(now)						# write file data -> change mtime
        inode.update_ctime(now) 					# change size -> change ctime
	return inode.set_inode_data(data,offset)
    def truncate(self, path, length, fh=None): 			###### truncate (See truncate(2) for details.) ######
        inode,items=self._get_inode_for_write(path,fh)			# write permission check
        if not S_ISREG(items[0]):raise FuseOSError(EPERM)		# (Operation not permitted)
	now=time()
        inode.update_mtime(now)						# write file data -> change mtime
        inode.update_ctime(now) 					# change size -> change ctime
	inode.trunc_inode_data(length)
    def release(self, path, fh):
	# called when FUSE is completely done with a file
	# The IBM document claims that there is exactly one release per open
        return 0
    def flush(self, path, fh):
	# Called on each close so that the filesystem has a chance to report delayed errors.
	# there may be more than one flush call for each open.
        return 0
    def fsync(self, path, datasync, fh):
	# Flush any dirty information about the file to disk.
	# If isdatasync is nonzero, only data, not metadata, needs to be flushed.
        return 0

#################################################################################################################
#														#
#				FUSE Operation Romfs Class							#
#														#
#################################################################################################################
class Romfs(Ramfs):
    """Memory File System Class (Read only)""" 
    def _update_atime(self,inode,now):	pass								# no update atime
    ######## FUSE Operating function (Attr) ########
    def access(self, path, amode):
	inode,context,uname,items=self._get_inode_for_access(path)					# access permission check
	if os.X_OK & amode:self._is_inode_X_OK(inode,context,uname,items)
	if os.W_OK & amode:						raise FuseOSError(EROFS)	# (Read-only file system)
	if os.R_OK & amode:self._is_inode_R_OK(inode,context,uname,items)
    def chmod(self, path, mode):					raise FuseOSError(EROFS)	# (Read-only file system)
    def chown(self, path, uid, gid):					raise FuseOSError(EROFS)	# (Read-only file system)
    def utimens(self, path, times=None):				raise FuseOSError(EROFS)	# (Read-only file system)
    ######## FUSE Operating function (XAttr) ########
    def setxattr(self, path, name, value, options, position=0):	raise FuseOSError(EROFS)	# (Read-only file system)
    def removexattr(self, path, name):					raise FuseOSError(EROFS)	# (Read-only file system)
    ######## FUSE Operating function (Create or Remove Dir) ########
    def mkdir(self, path, mode): 					raise FuseOSError(EROFS)	# (Read-only file system)
    def rmdir(self, path): 						raise FuseOSError(EROFS)	# (Read-only file system)
    ######## FUSE Operating function (Create or Remove File) ########
    def mknod(self, path, mode, dev):					raise FuseOSError(EROFS)	# (Read-only file system)
    def create(self, path, mode): 					raise FuseOSError(EROFS)	# (Read-only file system)
    def unlink(self, path): 						raise FuseOSError(EROFS)	# (Read-only file system)
    def rename(self, old, new):					raise FuseOSError(EROFS)	# (Read-only file system)
    def link(self, target, source):					raise FuseOSError(EROFS)	# (Read-only file system)
    ######## FUSE Operating function (Sym Link) ########
    def symlink(self, target, source):					raise FuseOSError(EROFS)	# (Read-only file system)
    ######## FUSE Operating function (Read or Write File) ########
    def open(self, path, flags): 
	inode,context,uname,items=self._get_inode_for_access(path)					# access permission check
        if S_ISDIR(items[0]):						raise FuseOSError(ENOSYS)	# (Function not implemented)
	if (flags & 3) == os.O_RDONLY:self._is_inode_R_OK(inode,context,uname,items)
	elif (flags & 3) == os.O_RDWR:					raise FuseOSError(EROFS)	# (Read-only file system)
	elif (flags & 3)== os.O_WRONLY:				raise FuseOSError(EROFS)	# (Read-only file system)
        return id(inode)
    def write(self, path, data, offset, fh):				raise FuseOSError(EROFS)	# (Read-only file system)
    def truncate(self, path, length, fh=None):				raise FuseOSError(EROFS)	# (Read-only file system)

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

#################################################################################################################
#														#
#					Header Afio Class							#
#														#
#################################################################################################################
class Header_Afio_Base():
	ID=6
	LEN=0
	s1= ""
	s2= ""
	header_base_buffer={}
	def __init__(self,abspath):								####  ####
	  self.abspath=abspath									# abspath of the archive file
	def print_header(self):								#### print afio header ####
	  print self.s1
	  print self.s2
          print "%s%s" % (self._get_hdr2(),self.get_st_path())
	def init(self,inode):									#### initialize with inode data ####
	  self.inode=inode									# store to update inode.header
	  header_address=inode.header_address+self.ID						# address is without 6byte '070707'
	  self.header_address=header_address							# store the address
	  self.hdr2=self._read_data(header_address,self.LEN)					# pre-read hdr2, use the data soon
	def copy(self,inode):									#### return copied header, ####
	  inode_id=id(inode)
	  copied_header=header_buffers.get(inode_id)						# use one in the header buffer, if exist
	  if copied_header==None:								# when there is no one
	    copied_header=copy.copy(self)							# copy 
	    copied_header.init(inode)								# and initialize
	    header_buffers.add(inode_id,copied_header)						# add header to header buffer
	  return copied_header
	def get(self,name,default):								#### get data like as dict ####
	  x=getattr(self,name,None)
	  if x != None: return x
	  x=getattr(self,'get_'+name,None)
	  if x != None: return x()
	  return default	  
	#### get Header Data Function ####
	def get_inode(self):
	  return getattr(self,'inode',None)
	def get_st_path(self):									#### path is read once when archive is loaded ####
	  if self.get_st_compress():	return self._read_data(self.header_address+self.LEN,self.get_st_nlen()-3)
	  else:			return self._read_data(self.header_address+self.LEN,self.get_st_nlen()-1)
	def get_st_compress(self):								#### compressed or not ####
	  st_compress=getattr(self,'st_compress',None)
	  if st_compress != None:return st_compress
	  st_nlen=self.get_st_nlen()								# st_nlen= name length + 1(\x00)
	  if st_nlen<=3:st_compress=False							# compressed file name is with '.z' -> length > 3
	  else:
	    path=self._read_data(self.header_address+self.LEN+st_nlen-3,2)			# get last 2 char
	    if (path=='.z') and ((self.get_st_rdev() & 1)==0):st_compress=True			# '.z' and 'rdev &1 =0' -> compressed data
	    else:st_compress=False
	  self.st_compress=st_compress								# store the data
	  self.inode.header=self._copy_base()							# change inode header fot next time
	  return st_compress
	def get_st_comp_size(self):								#### get decompressed data length of the compressed data ####
	  st_comp_size=getattr(self,'st_comp_size',None)
	  if st_comp_size != None:return st_comp_size[0][-1]					# already know the size
	  st_compress=self.st_compress
	  if st_compress!=True:return self.st_compress[0](self)				# already know decompress function
	  st_size=self._get_common_size()
	  if st_size != None:return st_size
	  raise FuseOSError(EIO)
	def get_st_size(self):									#### get size ####
	  if self.get_st_compress():			return self.get_st_comp_size()
	  else:					return self.get_st_raw_size()
	def get_next_addr(self):
	  return self._get_data_address()+self.get_st_raw_size()
	def get_st_raw_data(self,offset=0,size=None):						#### get raw data ####
	  data_offset=self._get_data_address()+offset
	  data_size=self.get_st_raw_size()-offset
	  if size==None:				return self._read_data(data_offset,data_size)
	  else:					return self._read_data(data_offset,min(data_size,size))
	def get_st_comp_data(self,offset=0,size=None):						#### get decompressed data of the compressed data ####
	  if self.st_compress==True:st_size=self.get_st_comp_size()				# get size -> get compress method
	  return self.st_compress[1](self,offset,size)
	def get_st_data(self,offset=0,size=None):						#### get data ####
	  if self.get_st_compress():			return self.get_st_comp_data(offset,size)
	  else:					return	self.get_st_raw_data(offset,size)
	def get_dir_type(self,inode):								#### get_dir_type ( call as 'self.get_dir_type()' ) ####
	  dir_type=getattr(self,'dir_type',None)
	  if dir_type!=None:return dir_type		# this is a dir, and other's X permission OK
	  header=self.copy(inode)
	  mode=header.get_st_mode()
	  if S_ISDIR(mode):				# this is dir
	    dir_type = mode & S_IXOTH			# other's permission OK
	    header.dir_type=dir_type			# set dir_type
	    inode.header=header._copy_base()		# change inode's header for next time
	    return dir_type
	  else: False					# this is not dir
	#### Local Function ####
	def _copy_base(self):									#### make new header for next time ####
	  keys=['abspath','st_compress','dir_type']
	  header_id=[id(self.__class__)]
	  for key in keys:header_id.append(getattr(self,key,None))
	  copied_header=self.header_base_buffer.get(tuple(header_id))				# try to get from header base buffer
	  if copied_header==None:
	    copied_header=copy.copy(self.inode.header)
	    for key in keys:setattr(copied_header,key,getattr(self,key,None))
	  return copied_header
	def _read_data(self,addr,size):							#### read any data in the archive file ####
	  f=open(self.abspath,"r")
	  f.seek(addr)
	  data=f.read(size)
	  f.close()
	  return data
        def _get_hdr2(self,name='',s=0,e=0):							#### get hdr2 or hdr2 parameter ####
	  if name != '':
	    val=getattr(self,name,None)
	    if val==None:
	      if not hasattr(self,'hdr2'):self.hdr2=self._read_data(self.header_address,self.LEN)
	      val=int(self.hdr2[s:e],8);setattr(self,name,val)
	    return val
	  if e != 0:
	    if not hasattr(self,'hdr2'):self.hdr2=self._read_data(self.header_address,self.LEN)
	    return int(self.hdr2[s:e],8)
	  return self.hdr2
	def _get_data_address(self):	return self.header_address+self.LEN+self.get_st_nlen()	#### get data address in the archive ####
	#### lzop functions ####
	def _get_lzop_header_size(self):							#### get lzop header length & flags ####
	  st_compress=self.st_compress
	  if len(st_compress)>=7:return st_compress[5:7]					# return lzop_header_size,lzop_flags
	  f=open(self.abspath)
	  f.seek(self._get_data_address())
	  s=f.read(512)	 									# enough length is 42byte in afio, because no filename.
	  f.close()
	  num= 9										# 89 4c 5a 4f 00 0d 0a 1a 0a #lzop file signature
	  lzop_ver=(ord(s[num])<<8)|ord(s[num+1]) 						# ver_2
	  if lzop_ver>0x0940:			lzop_ver_x=1					#	 'ext_2' 'level_1' 'mtimeH_4' are exist
	  else:				lzop_ver_x=0					#	
	  num=14+lzop_ver_x*3									# lib_2 (ext_2) method_1 (level_1) 
	  lzop_flags=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3]) 	# flags_4
	  F_H_FILTER=0x00000800L
	  if lzop_flags & F_H_FILTER:		lzop_filter_x=1					#	'filter_4' is exist
	  else:				lzop_filter_x=0
	  num=26+lzop_ver_x*7+lzop_filter_x*4							# mode_4 mtimeL_4 (mtimeH_4)
	  lzop_nlen=ord(s[num])									# nlen_1
	  num=31+lzop_ver_x*7+lzop_filter_x+lzop_nlen						# csum_4
	  F_H_EXTRA_FIELD = 0x00000040L
	  if lzop_flags & F_H_EXTRA_FIELD:							# No 'EXTRA_FIELD' in lzop yet
	    lzop_extr=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3])+8	# extrlen_4 extr_n extr_csum_4 
	  else:				lzop_extr=0
	  lzop_header_size=31+lzop_ver_x*7+lzop_filter_x+lzop_nlen+lzop_extr
	  if lzop_nlen==0:									# if lzop_nlen is 0
	    self.st_compress=(
		st_compress[0],st_compress[1],st_compress[2],st_compress[3],st_compress[4],
	        lzop_header_size,lzop_flags)	
	    self.inode.header=self._copy_base()							#   change inode's header for next time
	  return lzop_header_size,lzop_flags
	def _get_lzop_data_size(self):								#### get lzop data block length (compressed & decompressed) ####
	  lzop_raw_len,lzop_flags=self._get_lzop_header_size()
	  if lzop_raw_len==None:return None
	  lzop_data_len=0
	  seek=self._get_data_address()
	  size=self.get_st_raw_size()
	  f=open(self.abspath)
	  lzop_raw_data=[]
	  lzop_data=[]
	  F_ADLER32_D=0x00000001L
	  F_ADLER32_C=0x00000002L
	  F_CRC32_D=0x00000100L
	  F_CRC32_C=0x00000200L
	  lzop_raw_data.append(lzop_raw_len)
	  while 1:										# sum each block size
	   if lzop_raw_len>size:raise FuseOSError(EIO)						# (I/O error) 
	   f.seek(seek+lzop_raw_len)
	   s=f.read(8)
	   num=0;d_len=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3])
	   if d_len==0:break
	   num=4;c_len=(ord(s[num])<<24)|(ord(s[num+1])<<16)|(ord(s[num+2])<<8)|ord(s[num+3])
	   lzop_data_len+=d_len
	   lzop_raw_len+=c_len+8
	   if lzop_flags & F_ADLER32_D		:lzop_raw_len+=4
	   if lzop_flags & F_CRC32_D		:lzop_raw_len+=4
	   if d_len>c_len:
	     if lzop_flags & F_ADLER32_C	:lzop_raw_len+=4
	     if lzop_flags & F_CRC32_C		:lzop_raw_len+=4
	   lzop_data.append(lzop_data_len)
	   lzop_raw_data.append(lzop_raw_len)
	   # print "--------> %08x %08x %08x %08x %08x" % (size,lzop_raw_len,lzop_data_len,d_len,c_len)
	  f.close()
	  self.st_comp_size=(lzop_data,lzop_raw_data)
	  # print self.st_comp_size
	  return lzop_data[-1]
	def _get_lzop_data(self,offset=0,size=None):						#### get lzop data ####
	  lzop_data,lzop_raw_data=self.st_comp_size
	  if size==None:size=lzop_data[-1]
	  endoffset=offset+size
	  for start_block in xrange(0,len(lzop_data)):
	    if offset<lzop_data[start_block]:
	      if start_block>0:offset=offset-lzop_data[start_block-1]
	      break 
	  else:return ''									# read after the end -> return ''	
	  data=self._get_lzop_data_block_buffered(start_block)
	  if endoffset>lzop_data[start_block]:
	    for end_block in xrange(start_block+1,len(lzop_data)):
	      data=data+self._get_lzop_data_block_buffered(end_block)
	      if endoffset<lzop_data[end_block]:break
	  return data[offset:(offset+size)]
	def _get_lzop_data_block_buffered(self,i):						#### decompress lzop data block (buffered) #### 
	  key=(id(self.inode),i)								# make key
	  data_block=data_buffers.get(key)							# try to get block from buffer
	  if data_block==None:
	    data_block=self._get_lzop_data_block(i)
	    data_buffers.add(key,data_block,self.inode)
	  return data_block
	def _get_lzop_data_block(self,i):							#### decompress lzop data block ####
	  lzop_data,lzop_raw_data=self.st_comp_size
          args=self.st_compress[3:self.st_compress[2]+3]					# args=('lzop','-dc')
	  pipe=subprocess.PIPE
	  try:
	    proc = subprocess.Popen(args,stdin=pipe,stdout=pipe,close_fds=True,)			# make subprocess(ex. gzip -dc) for decompress 
	  except OSError:
	    print "OSError : %s" % " ".join(args)
	    raise FuseOSError(EIO)
	  pid = os.fork()									# fork 
	  if pid==0: 			# child (read compressed data and send then to lzop)
		proc.stdout.close()
		data=self.get_st_raw_data(0,lzop_raw_data[0])					# lzop header
		proc.stdin.write(data)
		data=self.get_st_raw_data(lzop_raw_data[i],lzop_raw_data[i+1]-lzop_raw_data[i])	# one of the lzop data block
		proc.stdin.write(data)
		proc.stdin.write('\0\0\0\0')							# lzop data end(next decompress data block size = 0)
		proc.stdin.flush()
		proc.stdin.close()
		os._exit(0)
	  proc.stdin.close()		# parent (read decompressed data from lzop)
	  data_block=proc.stdout.read()
          proc.stdout.close()
	  exit_status =proc.wait()
	  if exit_status !=0 :
	    print "exit_status(%s) : %s" % (" ".join(args),exit_status)
	    raise FuseOSError(EIO)
	  pid,exit_status =os.waitpid(pid,0)
	  if exit_status !=0 :
	    print "exit_status(fork) : %s" % exit_status
	    raise FuseOSError(EIO)
          return data_block
	#### xz functions ####
	def _get_xz_integer(self,s,i0):
	  i=0
	  n=ord(s[i0]) & 0x7F
	  while ord(s[i+i0]) & 0x80:
	    i +=1
	    if i >= len(s) or ord(s[i]) == 0x00 :return 0,i+i0+1
	    n |= (ord(s[i+i0]) & 0x7F) << (i * 7)
	  return n,i0+i+1
	def _get_xz_data_size(self):								#### get xz data length  ####
	  s=self.get_st_raw_data(self.get_st_raw_size()-12)
	  indexsize=((ord(s[7])<<24)|(ord(s[6])<<16)|(ord(s[5])<<8)|ord(s[4])+1)*4
	  s=self.get_st_raw_data(self.get_st_raw_size()-12-indexsize,indexsize)
	  num=1
	  indexnum,num=self._get_xz_integer(s,num)
	  lzop_data_len=0
	  lzop_raw_len=0
	  lzop_data=[]
	  lzop_raw_data=[]
	  for i in xrange(indexnum):
	    x,num=self._get_xz_integer(s,num)
	    lzop_raw_len += x
	    x,num=self._get_xz_integer(s,num)
	    lzop_data_len += x
	    lzop_data.append(lzop_data_len)
	    lzop_raw_data.append(lzop_raw_len)
	  self.st_comp_size=(lzop_data,lzop_raw_data)
	  # print self.st_comp_size
	  return lzop_data_len
	#### gzip functions ####
	def _get_gzip_data_size(self):								#### get gzip data length ####
	  s=self.get_st_raw_data(self.get_st_raw_size()-4)
	  st_comp_size=(ord(s[3])<<24)|(ord(s[2])<<16)|(ord(s[1])<<8)|ord(s[0])
	  self.st_comp_size=([st_comp_size],None)
	  return st_comp_size
	#### common functions ####
	def _get_common_size(self):								####  ####
	  f=open(self.abspath)
	  f.seek(self._get_data_address())
	  s=f.read(512)	 									#
	  f.close()

	  if s[0:9] == '\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a':
	    if self.st_compress==True:								#
	      self.st_compress=(
		Header_Afio_Base._get_lzop_data_size,
		Header_Afio_Base._get_lzop_data,
		2,'lzop','-dc')									#
	      self.inode.header=self._copy_base()						#
	    return self.st_compress[0](self)
	  elif s[0:2] == '\x1f\x8b':
	    if self.st_compress==True:								#
	      self.st_compress=(
		Header_Afio_Base._get_gzip_data_size,
		Header_Afio_Base._get_common_data,
		2,'gzip','-dc')									#
	      self.inode.header=self._copy_base()						#
	    return self.st_compress[0](self)
	  elif s[0:6] == '\xfd7zXZ\x00':
	    if self.st_compress==True:								#
	      self.st_compress=(
		Header_Afio_Base._get_xz_data_size,
		Header_Afio_Base._get_common_data,
		2,'xz','-dc')									# 
	      self.inode.header=self._copy_base()						#
	    return self.st_compress[0](self)
	  elif s[0:3] == 'BZh':
	    if self.st_compress==True:								# 
	      self.st_compress=(
		Header_Afio_Base._get_common_data_size,
		Header_Afio_Base._get_common_data,
		2,'bzip2','-dc')								#
	      self.inode.header=self._copy_base()						#
	    return self.st_compress[0](self)
	#### Common Decompress Function ####
	def _get_common_data_size(self):							#### get data length  ####
	  data=self._get_common_data_block_buffered()
	  st_comp_size=len(data)
	  self.st_comp_size=([st_comp_size],None)
	  return st_comp_size
	def _get_common_data(self,offset=0,size=None):						#### get compressed data ####
	  data=self._get_common_data_block_buffered()
	  return data[offset:(offset+size)]
	def _get_common_data_block_buffered(self):						#### decompress data block(buffered)  ####
	  key=(id(self.inode),0)								# make key
	  data_block=data_buffers.get(key)							# try to get block from buffer
	  if data_block==None:
	    data_block=self._get_common_data_block()
	    data_buffers.add(key,data_block,self.inode)
	  return data_block
	def _get_common_data_block(self):							#### get data block ####
          args=self.st_compress[3:self.st_compress[2]+3]
	  pipe=subprocess.PIPE
	  try:
	    proc = subprocess.Popen(args,stdin=pipe,stdout=pipe,close_fds=True,)			# make subprocess(ex. gzip -dc) for decompress 
	  except OSError:
	    print "OSError : %s" % " ".join(args)
	    raise FuseOSError(EIO)
	  pid = os.fork()									# fork 
	  if pid==0: 			# child (read compressed data and send then to program)
		proc.stdout.close()
		data=self.get_st_raw_data()							# compressed data
		proc.stdin.write(data)
		proc.stdin.flush()
		proc.stdin.close()
		os._exit(0)
	  proc.stdin.close()		# parent (read decompressed data from program)
	  data_block=proc.stdout.read()
          proc.stdout.close()
	  exit_status =proc.wait()
	  if exit_status !=0 :
	    print "exit_status(%s) : %s" % (" ".join(args),exit_status)
	    raise FuseOSError(EIO)
	  pid,exit_status =os.waitpid(pid,0)
	  if exit_status !=0 :
	    print "exit_status(fork) : %s" % exit_status
	    raise FuseOSError(EIO)
	  return data_block
"""
7z  LZMA
A. Local file header: 
local file header signature 4 bytes (0x04034b50) 
version needed to extract 2 bytes 
general purpose bit flag 2 bytes 
compression method 2 bytes 
last mod file time 2 bytes 
last mod file date 2 bytes 
crc-32 4 bytes 
compressed size 4 bytes 
uncompressed size 4 bytes 
file name length 2 bytes 
extra field length 2 bytes 
file name (variable size) 
extra field (variable size) 
"""

### Binary(& swap byte) archive header   fix me !

#/* binary format */
#define	M_BINARY 070707		/* Binary magic number */
#define	M_STRLEN 6		/* ASCII magic number length */

#/*
# * Binary archive header (obsolete).
# */
#typedef struct
#{
#  short b_dev;			/* Device code */
#  ushort b_ino;			/* Inode number */
#  ushort b_mode;		/* Type and permissions */
#  ushort b_uid;			/* Owner */
#  ushort b_gid;			/* Group */
#  short b_nlink;		/* Number of links */
#  short b_rdev;			/* Real device */
#  ushort b_mtime[2];		/* Modification time (hi/lo) */
#  ushort b_name;		/* Length of pathname (with null) */
#  ushort b_size[2];		/* Length of data */
#} Binary;

class Header_Afio_070707(Header_Afio_Base):
	LEN=70
	s1= "|23456|23456|23456|23456|23456|23456|23456|23456789ab|23456|23456789ab|..."
	s2= "|  dev|  ino| mode|  uid|  gid|nlink| rdev|     mtime|nmlen|      size|pathname"
	def get_st_dev(self):		return self._get_hdr2(''		, 0, 6)
	def get_st_ino(self):		return self._get_hdr2(''		, 6,12)
	def get_st_mode(self):		return self._get_hdr2('st_mode'	,12,18)
	def get_st_uid(self):		return self._get_hdr2('st_uid'		,18,24)
	def get_st_gid(self):		return self._get_hdr2('st_gid'		,24,30)
	def get_st_nlink(self):	return self._get_hdr2(''		,30,36)
	def get_st_rdev(self):		return self._get_hdr2('st_rdev'	,36,42)
	def get_st_mtime(self):	return self._get_hdr2('mtime'		,42,53)
	def get_st_nlen(self):		return self._get_hdr2('st_nlen'	,53,59)
	def get_st_raw_size(self):	return self._get_hdr2('st_raw_size'	,59,70)
	get_st_atime=get_st_mtime
	get_st_ctime=get_st_mtime
class Header_Afio_070717(Header_Afio_Base):
	LEN=75
	s1= "|23456|23456|23456789ab|23456|23456|23456|23456|23456|23456789ab|23456|23456789ab|..."
	s2= "|  hdr|  dev|       ino| mode|  uid|  gid|nlink| rdev|     mtime|nmlen|      size|pathname"
	def get_st_dev(self):		return self._get_hdr2(''		, 0, 6)
	def get_st_ino(self):		return self._get_hdr2(''		, 6,17)
	def get_st_mode(self):		return self._get_hdr2('st_mode'	,17,23)
	def get_st_uid(self):		return self._get_hdr2('st_uid'		,23,29)
	def get_st_gid(self):		return self._get_hdr2('st_gid'		,29,35)
	def get_st_nlink(self):	return self._get_hdr2(''		,35,41)
	def get_st_rdev(self):		return self._get_hdr2('st_rdev'	,41,47)
	def get_st_mtime(self):	return self._get_hdr2('mtime'		,47,58)
	def get_st_nlen(self):		return self._get_hdr2('st_nlen'	,58,64)
	def get_st_raw_size(self):	return self._get_hdr2('st_raw_size'	,64,75)
	get_st_atime=get_st_mtime
	get_st_ctime=get_st_mtime
class Header_Afio_070727(Header_Afio_Base):
	LEN=110
	s1= "|23456|2345678|234567890123456m|23456|2345678|2345678|2345678|2345678|234567890123456n|234|234|234s|234567890123456:|..."
	s2= "|  hdr|    dev|            inoM|  mod|    uid|    gid|  nlink|   rdev|          mtimeN|nml|flg|xszS|           size:|pathname"
	def get_st_dev(self):		return self._get_hdr2(''		, 0, 8)
	def get_st_ino(self):		return self._get_hdr2(''		, 8,25)
	def get_st_mode(self):		return self._get_hdr2('st_mode'	,25,31)
	def get_st_uid(self):		return self._get_hdr2('st_uid'		,31,39)
	def get_st_gid(self):		return self._get_hdr2('st_gid'		,39,47)
	def get_st_nlink(self):	return self._get_hdr2(''		,47,55)
	def get_st_rdev(self):		return self._get_hdr2('st_rdev'	,55,63)
	def get_st_mtime(self):	return self._get_hdr2('mtime'		,63,80)
	def get_st_nlen(self):		return self._get_hdr2('st_nlen'	,80,84)
	def get_st_flag(self):		return self._get_hdr2(''		,84,88)	# specialflags    0
	def get_st_xszS(self):		return self._get_hdr2(''		,88,93)	# extraheaderlen  0
	def get_st_raw_size(self):	return self._get_hdr2('st_raw_size'	,93,110)
	get_st_atime=get_st_mtime
	get_st_ctime=get_st_mtime
#################################################################################################################
#														#
#					Load Archive Class							#
#														#
#################################################################################################################
class Afio_load1():
	"""Load afio file(abspath) to target path in the filesystem"""
	def __init__(self,target,abspath,filesystem):
	  self.target=target							# path to mount afio file in the file system
	  self.fs=filesystem							# file system
	  self.header_070707=Header_Afio_070707(abspath)			# Old ASCII magic number
	  self.header_070717=Header_Afio_070717(abspath)			# Extended ASCII magic number
	  self.header_070727=Header_Afio_070727(abspath)			# Large ASCII magic number
	  self.ino={}								# Dict to seach hard link
	  proc=subprocess.Popen('afio -tvBZ "%s"' % abspath, shell=True,stdout=subprocess.PIPE)
	  pipe=proc.stdout
	  f=open(abspath,"r")
          for line in pipe:
            i=int(line.split(None,2)[0])					# get header address in the archive file
	    f.seek(i)
	    # print "seek ----> " , i
	    hdr=f.read(6)
            self.load_header(i,hdr)
          f.close()  
	  pipe.close()
	  if proc.wait() !=0 : raise FuseOSError(EIO)
	  delattr(self,'ino')
	def load_header(self,seek=None,hdr=''):
	  if	hdr=='070707':inode=Inode(address=seek,header=self.header_070707)
	  elif	hdr=='070717':inode=Inode(address=seek,header=self.header_070717)
	  elif	hdr=='070727':inode=Inode(address=seek,header=self.header_070727)
	  elif	hdr=='070701':return 						# cpio new ASCII magic number	length: 110	not implemented
	  elif	hdr=='070702':return 						# cpio new ASCII magic number with CRC		not implemented
	  elif	hdr=='070703':return 						# Tcpio magic number of TI/E			not implemented
          else:return
	  header=inode.get_header()						# get header
	  header.print_header()
	  is_dir=False
	  mode=header.get_st_mode()
          if S_ISDIR(mode):							# 'DIR'
	    inode.set_nlink(2);is_dir=True					# nlink = 2
	  elif header.get_st_nlink()>1:					# 'not DIR' and 'Hard Linked(nlink>1)'
	    ino_key=(header.get_st_dev(),header.get_st_ino())			# must combine 'dev' and 'ino' for key
	    inode2=self.ino.get(ino_key,None)					# search Hard linked file with the key
	    if inode2==None:	self.ino[ino_key]=inode				# LinkedFile is first time, save inode.
	    else:		inode=inode2;inode.inc_nlink()			# LinkedFile is second time, use saved inode.
	  if mode !=0:
	    path='%s/%s' % (self.target,header.get_st_path())			# make path of the inode in target dir.
	    self.fs._add_inode_nopermisson(path,inode,is_dir)			# add inode to the filesystem
	  # print "seek XXX ----> " ,header.get_next_addr()
	  return header.get_next_addr()
class Afio_load(Afio_load1):
	"""Load afio file(abspath) to target path in the filesystem"""
	def __init__(self,target,abspath,filesystem):
	  self.target=target							# path to mount afio file in the file system
	  self.fs=filesystem							# file system
	  self.header_070707=Header_Afio_070707(abspath)			# Old ASCII magic number
	  self.header_070717=Header_Afio_070717(abspath)			# Extended ASCII magic number
	  self.header_070727=Header_Afio_070727(abspath)			# Large ASCII magic number
	  self.ino={}								# Dict to seach hard link
	  i=0
	  f=open(abspath,"r")
	  # print "Load START"
	  while True:
	    f.seek(i)
	    print "seek ----> " , i
	    hdr=f.read(6)
            i=self.load_header(i,hdr)
	    if i==None:break							# "unknown header" or "file end"
	  # print "Load END"
          f.close()  
	  delattr(self,'ino')
#################################################################################################################
#														#
#				FUSE Operation Afiolzofs Class							#
#														#
#################################################################################################################
class Afiolzofs():
    """Base Class of the Afio Lzo Filesystem""" 
    def _check_symlink(self,target,source):					####### Symlink Check (root of the file system or not) #######
	print source
	print self.mountpoint
        targetpath=os.path.split(target)
        if targetpath[0]!="/":return source					# if target is not in the root of the filesystem -> normal link
        sourceabspath=os.path.normpath(os.path.join(self.mountpoint,source))	# get abspath of the file
	if sourceabspath[0:len(self.mountpoint)]==self.mountpoint:		# if source is in the self.mountpoint(same filesystem),
	  print 'file(%s) is in the mount point(%s)' % (sourceabspath,self.mountpoint)
	  return source							#   cannot to load archive file -> normal link
	try:									# try to load afio archive file
	    f=open(sourceabspath);top50=f.read(50);f.close()			# read top 50 byte of the file
	except:
	  print 'loading is failed' 
	  return source							# failed -> return source path to symlink normally  
	filetype=_filetype(top50)						# get file type 
	if filetype=="ASCII cpio archive":					# if 'ASCII cpio archive'
	      targetname=".%s" % targetpath[1]					# 'file name in the file system' for loading afio archive 
	      targetdir=os.path.join(targetpath[0],targetname)			# 'path name in the file system' for loading afio archive
	      Ramfs.mkdir(self,targetdir, S_IMODE(0755))			# make dir for load the archive
              Afio_load(targetdir,sourceabspath,self)				# load the archive
              return targetname						# return loaded dir path to symlink. instead to normal symlink
    def _symlink(self, target, source): 					###### _symlink (internal symlink command) ######
	  source=self._check_symlink(target,source)				# check root of the file system -> load afio.lzo
	  Ramfs.symlink(self,target, source)					# make normal symlink, even if Romfs
class Afiolzofs_SymlinkLoading(Afiolzofs):
    """Base Class of the Afio Lzo Filesystem with SymlinkLoading option""" 
    def symlink(self, target, source):self._symlink(target, source)		###### symlink ######
### Real File System Class ###
class AfiolzoRamfs(Afiolzofs,Ramfs):				pass	
class AfiolzoRomfs(Afiolzofs,Romfs):				pass 	
class AfiolzoSLRamfs(Afiolzofs_SymlinkLoading,Ramfs):			pass 	
class AfiolzoSLRomfs(Afiolzofs_SymlinkLoading,Romfs):			pass 	
class LogAfiolzoRamfs(LoggingMixIn,Afiolzofs,Ramfs):			pass 	
class LogAfiolzoRomfs(LoggingMixIn,Afiolzofs,Romfs):			pass 	
class LogAfiolzoSLRamfs(LoggingMixIn,Afiolzofs_SymlinkLoading,Ramfs):	pass 	
class LogAfiolzoSLRomfs(LoggingMixIn,Afiolzofs_SymlinkLoading,Romfs):	pass 	
#################################################################################################################
#														#
#						Buffers								#
#														#
#################################################################################################################
class Buffers():	
    def __init__(self,num1=10,num2=10):
	self.num=num1
	self.data={}
	self.start=[None,None,None,None]
	self.start[1]=self.start # before
	self.start[2]=self.start # next
    def get(self,key,default=None):
	data=self.data
	datalist=data.get(key,None)
	if datalist == None:
	  #  print "not extst in the Buffer"
	  return None
	else:
	  start=self.start
	  before=datalist[1]
	  next=datalist[2]
	  before[2]=next
	  next[1]=before
	  last=start[1]
	  last[2]=datalist
	  datalist[1]=last
	  datalist[2]=start
	  start[1]=datalist
	  return datalist[0]
    def add(self,key,val,hold=None):
	data=self.data
	if not key in data:
	  start=self.start
	  before=start[1]
	  datalist=[val,before,start,key,hold]
	  start[1]=datalist
	  before[2]=datalist
	  data[key]=datalist
	  num=self.num
	  while len(data)>num: # dell
	    datalist=start[2]
	    next=datalist[2]
	    start[2]=next
	    next[1]=start
	    data.pop(datalist[3])
#################################################################################################################
#														#
#						Main								#
#														#
#################################################################################################################
def main(args,command='afiolzofs.py'):
    if len(argv) < 2: 						# print usage
        print 'usage: %s [options] [archives ...] <mountpoint>' % command
        print '  "old ASCII cpio archive" or "afio archive" files will be loaded.'
	print '  (Options) '
	print '    --ramfs		: allow temporaly write'
	print '    --nosymlink		: do not allow archive loading with symlink'
	print '    --logging		: with logging'
	print '    --debug		: enable debug output (implies -f)'
	print '    --foreground	: foreground operation'
	print '    --nothreads		: disable multi-threaded operation'
	print '    --allow_other	: allow access to other users'
	print '    --allow_root	: allow access to root'
	print '    --kernel_cache	: allow kernel cache'
#	print '    --direct_io		: allow directio (not implemented)'
#	print '    --big_writes		: allow big writes (not implemented)'
	print '  (Archive loading with symlink)'
        print '    Please make symlink of the archive file under the <mountpoint> to load it.'  
        print '  (Example)'
	print '    %s mountpoint		# mount afiolzofs to mountpoint' % command
        print '    ln -s XXX.afio.lzo mountpoint/XXX	# make symlink '
        print '         (XXX.afio.gz also OK, but cannot block read)'
        print '    ls mountpoint/XXX			# list the archive'
        print '   Afiolzofs makes symlink not to the archive, but to mountpoint/.XXX instead,'
        print '   and loads the archive inode data to mountpoint/.XXX'
        print '   If you read file contents, Afiolzofs will read it from the archive.'
        exit(1)
    mountpoint=os.path.abspath(args.pop())
    key=('--logging','--nosymlink','--ramfs','--debug','--foreground','--nothreads','--allow_other','--allow_root','--kernel_cache')
    if key[0] in args:						# select file system
      if key[1] in args:
        if key[2] in args:	afiolzofs= LogAfiolzoRamfs(mountpoint)
        else:			afiolzofs= LogAfiolzoRomfs(mountpoint)
      else:
        if key[2] in args:	afiolzofs= LogAfiolzoSLRamfs(mountpoint)
        else:			afiolzofs= LogAfiolzoSLRomfs(mountpoint)
    else:
      if key[1] in args:
        if key[2] in args:	afiolzofs= AfiolzoRamfs(mountpoint)
        else:			afiolzofs= AfiolzoRomfs(mountpoint)
      else:
        if key[2] in args:	afiolzofs= AfiolzoSLRamfs(mountpoint)
        else:			afiolzofs= AfiolzoSLRomfs(mountpoint)
    keyargs={}							# args for FUSE
    if key[3] in args:					keyargs[key[3][2:]]=True
    if (key[4] in args)or(key[0] in args):		keyargs[key[4][2:]]=True
    if key[5] in args:					keyargs[key[5][2:]]=True
    if key[6] in args:					keyargs[key[6][2:]]=True
    if (key[7] in args)and(not key[6] in args):	keyargs[key[7][2:]]=True
    if key[8] in args:					keyargs[key[8][2:]]=True
#    if key[9] in args:					keyargs[key[9][2:]]=True
    for i in key:						# delete overlaped arguments from 'args'
      while i in args:args.remove(i)				# 
    global data_buffers
    data_buffers=Buffers(20,20)					# data buffer
    global header_buffers
    header_buffers=Buffers(50,50)				# header buffer
    for i in args:						# mount afio files in the 'args'
      basename=os.path.basename(i)				#   make symlink name
      target=os.path.join(mountpoint,'.'+basename)		#   make 'target dir' name
      afiolzofs._symlink(target,i)				#   load afio file to the 'target dir'
    fuse = FUSE(afiolzofs, mountpoint,use_ino=True,**keyargs)	# fuse operation start 

if __name__ == "__main__": 
  main(argv[1:],argv[0])

