'''
fusefs.py - Fuse filesystem with antivirus control.

(c) 2005-2016 Jan ONDREJ (SAL) <ondrejj(at)salstar.sk>

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

'''

import os,stat,errno
from aglib import *

__all__=['fusefs']

try:
  import fuse
  class SgFuse(fuse.FUSE):
    multithreaded=1
    allow_other=True
    files={}
    def __init__(self, mountpoint, root_path):
        self.mountpoint=mountpoint
        self.base=root_path
        if debug.debug_level>8:
          self.debug=True
    def getattr(self, path):
        return os.lstat(self.base+path)
    def readlink(self, path):
        return os.readlink(self.base+path)
    def getdir(self, path):
        return [('.',0),('..',0)]+[(x,0) for x in os.listdir(self.base+path)]
    def unlink(self, path):
        return os.unlink(self.base+path)
    def rmdir(self, path):
        return os.rmdir(self.base+path)
    def symlink(self, path, path1):
        return os.symlink(self.base+path, path1)
    def rename(self, path, path1):
        return os.rename(self.base+path, self.base+path1)
    def link(self, path, path1):
        return os.link(self.base+path, self.base+path1)
    def chmod(self, path, mode):
        return os.chmod(self.base+path, mode)
    def chown(self, path, user, group):
        return os.chown(self.base+path, user, group)
    def truncate(self, path, size):
        f = open(self.base+path, "w+")
        return f.truncate(size)
    def mknod(self, path, mode, dev):
        if stat.S_ISREG(mode):
          open(self.base+path, "w")
        else:
          return -errno.EINVAL
    def mkdir(self, path, mode):
        return os.mkdir(self.base+path, mode)
    def utime(self, path, times):
        return os.utime(self.base+path, times)
    def open(self, path, flags):
        debug.echo(6,"open: %s, 0x%x" % (path,flags))
        if flags & os.O_APPEND:
          python_flags='a'
        else:
          python_flags=['r','w','r+'][flags & 3]
        self.files[path]=open(self.base+path,python_flags)
        if ('r' in python_flags) or ('+' in python_flags):
          # reinit scanners
          for scnr in self.SCANNERS:
            scnr.reinit()
          mail.data=self.files[path].read()
          mail.xheader=''
          globals.reset()
          for scnr in self.SCANNERS:
            level, detected, virlist, scan_reply, err \
              = do_scan(scnr,os.path.basename(path))
            if is_infected(level,detected):
              debug.echo(1,"fusefs(): %s %s [%s,%0.2f]" \
                         % (path,detected,globals.found_by.name,level))
              del self.files[path]
              return -errno.EPERM
        debug.echo(7,'open: %s, done' % path)
        return 0
    def read(self, path, len, offset):
        debug.echo(6,"read: %s" % path)
        f=self.files[path]
        f.seek(offset)
        return f.read(len)
    def write(self, path, buf, offset):
        debug.echo(6,"write: %s" % path,self.files)
        f=self.files[path]
        f.seek(offset)
        f.write(buf)
        return len(buf)
    def release(self, path, flags):
        debug.echo(6,"close: %s" % path)
        self.files[path].close()
        del self.files[path]
        return 0
    def fsync(self, path, isfsyncfile):
        debug.echo(6,"fsync: path=%s, isfsyncfile=%s" % (path, isfsyncfile))
        return 0
except ImportError:
  class SgFuse:
    errorstring='''Importing of fuse module failed!
    You have configured fusefs() service and this service can't
    import fuse python module. Install python-fuse package.'''
    def __init__(self,*args,**kw):
        debug.echo(1,self.errorstring)
        raise ImportError(self.errorstring)
    
class fusefs(service):
  '''
  Fuse filesystem with antivirus checking.
  
  This service can be used to check filesystem access for viruses.
  
  Usage: fusefs(SCANNERS, mountpoint, root_path='/')
  
  Where: mountpoint is a string, which defines an directory, where files
           will be accessed.
         root_path is a path, which files will real files.
  
  Example: fusefs(SCANNERS, '/home', '/realhome')
  
  New in version 0.8.0.
  '''
  name='fusefs()'
  def __init__(self,scanners,mountpoint,root_path='/'):
      self.server=SgFuse(mountpoint,root_path)
      self.server.SCANNERS=scanners
      self.MOUNTPOINT=mountpoint
      self.ROOT_PATH=root_path
      self.EXITING=False
  def start(self):
      if not os.path.isdir(self.MOUNTPOINT):
        raise IOError("No such directory: %s" % self.MOUNTPOINT)
      if not os.path.isdir(self.ROOT_PATH):
        raise IOError("No such directory: %s" % self.ROOT_PATH)
      self.test_scanners(self.server.SCANNERS)
      pid=self.fork()
      return [pid]
  def fork(self):
      if self.EXITING:
        return -1
      if self.childs!=[]:
        return -1
      p=os.fork()
      if p==0:
        signal.signal(signal.SIGHUP,self.sighup)
        signal.signal(signal.SIGTERM,self.sigterm)
        debug.echo(1,"%s: service started, waiting ... [%d]" \
                   % (self.name,os.getpid()))
        self.server.main()
        debug.echo(1,"%s: server stopped." % self.name)
      else:
        self.childs.append(p)
      return p
  def cleanup(self):
      debug.echo(5,'%s: umounting %s' \
                 % (self.name,self.MOUNTPOINT))
      ret=os.system('umount %s' % self.MOUNTPOINT)
      if ret:
        debug.echo(1,'%s: umount return status: %d' % (self.name,ret))
