osecfs
author ft
Tue, 04 Nov 2014 16:28:40 +0100
changeset 22 23028352807f
permissions -rwxr-xr-x
initial commit of osefcs package
ft@22
     1
#!/usr/bin/python
ft@22
     2
ft@22
     3
# ------------------------------------------------------------
ft@22
     4
# opensecurity package file
ft@22
     5
#
ft@22
     6
# Autor: X-Net Services GmbH <office@x-net.at>
ft@22
     7
#
ft@22
     8
# Copyright 2013-2014 X-Net and AIT Austrian Institute of Technology
ft@22
     9
#
ft@22
    10
#
ft@22
    11
#     X-Net Technologies GmbH
ft@22
    12
#     Elisabethstrasse 1
ft@22
    13
#     4020 Linz
ft@22
    14
#     AUSTRIA
ft@22
    15
#     https://www.x-net.at
ft@22
    16
#
ft@22
    17
#     AIT Austrian Institute of Technology
ft@22
    18
#     Donau City Strasse 1
ft@22
    19
#     1220 Wien
ft@22
    20
#     AUSTRIA
ft@22
    21
#     http://www.ait.ac.at
ft@22
    22
#
ft@22
    23
#
ft@22
    24
# Licensed under the Apache License, Version 2.0 (the "License");
ft@22
    25
# you may not use this file except in compliance with the License.
ft@22
    26
# You may obtain a copy of the License at
ft@22
    27
#
ft@22
    28
#    http://www.apache.org/licenses/LICENSE-2.0
ft@22
    29
#
ft@22
    30
# Unless required by applicable law or agreed to in writing, software
ft@22
    31
# distributed under the License is distributed on an "AS IS" BASIS,
ft@22
    32
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
ft@22
    33
# See the License for the specific language governing permissions and
ft@22
    34
# limitations under the License.
ft@22
    35
# ------------------------------------------------------------
ft@22
    36
ft@22
    37
ft@22
    38
from fuse import Fuse
ft@22
    39
import fuse
ft@22
    40
ft@22
    41
import ConfigParser
ft@22
    42
ft@22
    43
import sys
ft@22
    44
ft@22
    45
import logging
ft@22
    46
import os
ft@22
    47
import errno
ft@22
    48
import time
ft@22
    49
ft@22
    50
from importlib import import_module
ft@22
    51
ft@22
    52
ft@22
    53
import subprocess
ft@22
    54
ft@22
    55
import urllib3
ft@22
    56
import netifaces
ft@22
    57
import netaddr
ft@22
    58
import hashlib
ft@22
    59
ft@22
    60
ft@22
    61
sys.stderr = open('/var/log/osecfs_error.log', 'a+')
ft@22
    62
ft@22
    63
ft@22
    64
MINOPTS = { "Main" : ["Logfile", "LogLevel", "Mountpoint", "Rootpath", "ScannerPath", "ScannerModuleName", "ScannerClassName", "ScannerConfig", "ReadOnly"]}
ft@22
    65
ft@22
    66
CONFIG_NOT_READABLE = "Configfile is not readable"
ft@22
    67
CONFIG_WRONG = "Something is wrong with the config"
ft@22
    68
CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
ft@22
    69
SCAN_WRONG_RETURN_VALUE = "The return Value of the malware scanner is wrong. Has to be an dictionary"
ft@22
    70
SCAN_RETURN_VALUE_KEY_MISSING = "The dictionary has to include key \"infected\" (True, False) and \"virusname\" (String)"
ft@22
    71
VIRUS_FOUND = "Virus found. Access denied"
ft@22
    72
NOTIFICATION_CRITICAL = "critical"
ft@22
    73
NOTIFICATION_INFO = "info"
ft@22
    74
LOG = None
ft@22
    75
MalwareScanner = None
ft@22
    76
STATUS_CODE_OK = 200
ft@22
    77
ft@22
    78
SYSTEM_FILE_COMMAND = "file"
ft@22
    79
httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
ft@22
    80
ft@22
    81
def checkMinimumOptions (config):
ft@22
    82
    for section, options in MINOPTS.iteritems ():
ft@22
    83
        for option in options:
ft@22
    84
            if (config.has_option(section, option) == False):
ft@22
    85
                print (CONFIG_MISSING % (section, option))
ft@22
    86
                exit (129)
ft@22
    87
ft@22
    88
def printUsage ():
ft@22
    89
    print ("Usage:")
ft@22
    90
    print ("%s configfile mountpath ro/rw" % (sys.argv[0]))
ft@22
    91
    exit (128)
ft@22
    92
ft@22
    93
def loadConfig ():
ft@22
    94
    print ("load config")
ft@22
    95
ft@22
    96
    if (len (sys.argv) < 4):
ft@22
    97
        printUsage ()
ft@22
    98
ft@22
    99
    configfile = sys.argv[1]
ft@22
   100
    config = ConfigParser.SafeConfigParser ()
ft@22
   101
ft@22
   102
    if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
ft@22
   103
        print (CONFIG_NOT_READABLE)
ft@22
   104
        printUsage ()
ft@22
   105
ft@22
   106
    try:
ft@22
   107
        config.read (sys.argv[1])
ft@22
   108
    except Exception, e:
ft@22
   109
        print (CONFIG_WRONG)
ft@22
   110
        print ("Error: %s" % (e))
ft@22
   111
ft@22
   112
ft@22
   113
    config.set("Main", "Mountpoint", sys.argv[2])
ft@22
   114
    if (sys.argv[3] == "rw"):
ft@22
   115
        config.set("Main", "ReadOnly", "false")
ft@22
   116
    else:
ft@22
   117
        config.set("Main", "ReadOnly", "true")
ft@22
   118
ft@22
   119
    checkMinimumOptions (config)
ft@22
   120
ft@22
   121
    return config
ft@22
   122
ft@22
   123
def initLog (config):
ft@22
   124
    print ("init log")
ft@22
   125
ft@22
   126
    global LOG
ft@22
   127
    logfile = config.get("Main", "Logfile")
ft@22
   128
    
ft@22
   129
    numeric_level = getattr(logging, config.get("Main", "LogLevel").upper(), None)
ft@22
   130
    if not isinstance(numeric_level, int):
ft@22
   131
        raise ValueError('Invalid log level: %s' % loglevel)
ft@22
   132
ft@22
   133
    # ToDo move log level and maybe other things to config file
ft@22
   134
    logging.basicConfig(
ft@22
   135
                        level = numeric_level,
ft@22
   136
                        format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
ft@22
   137
                        datefmt = "%Y-%m-%d %H:%M:%S",
ft@22
   138
                        filename = logfile,
ft@22
   139
                        filemode = "a+",
ft@22
   140
    )
ft@22
   141
    LOG = logging.getLogger("fuse_main")
ft@22
   142
ft@22
   143
ft@22
   144
def fixPath (path):
ft@22
   145
    return ".%s" % (path)
ft@22
   146
ft@22
   147
def rootPath (rootpath, path):
ft@22
   148
    return "%s%s" % (rootpath, path)
ft@22
   149
ft@22
   150
def flag2mode (flags):
ft@22
   151
    md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
ft@22
   152
    m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
ft@22
   153
ft@22
   154
    # windows sets append even if it would overwrite the whole file (seek 0)
ft@22
   155
    # so ignore append option
ft@22
   156
    #if flags | os.O_APPEND:
ft@22
   157
    #    m = m.replace('w', 'a', 1)
ft@22
   158
ft@22
   159
    return m
ft@22
   160
ft@22
   161
def scanFile (path, fileobject):
ft@22
   162
    LOG.debug ("Scan File \"%s\" with malware Scanner" %(path,) )
ft@22
   163
    return MalwareScanner.scanFile (path, fileobject)
ft@22
   164
ft@22
   165
ft@22
   166
def scanFileClamAV (path):
ft@22
   167
    infected = False
ft@22
   168
ft@22
   169
    LOG.debug ("Scan File: %s" % (path))
ft@22
   170
ft@22
   171
    result = pyclamav.scanfile (path)
ft@22
   172
    LOG.debug ("Result of file \"%s\": %s" % (path, result))
ft@22
   173
    if (result[0] != 0):
ft@22
   174
        infected = True
ft@22
   175
ft@22
   176
    if (infected == True):
ft@22
   177
        LOG.error ("Virus found, deny Access %s" % (result,))
ft@22
   178
ft@22
   179
    return infected
ft@22
   180
ft@22
   181
def whitelistFile (path):
ft@22
   182
    whitelisted = False;
ft@22
   183
ft@22
   184
    LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
ft@22
   185
    
ft@22
   186
    result = None
ft@22
   187
    try:
ft@22
   188
        result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
ft@22
   189
        # ToDo replace with real whitelist
ft@22
   190
        whitelisted = True
ft@22
   191
    except Exception as e:
ft@22
   192
        LOG.error ("Call returns with an error!")
ft@22
   193
        LOG.error (e)
ft@22
   194
ft@22
   195
    LOG.debug ("Type: %s" %(result))
ft@22
   196
ft@22
   197
    return whitelisted
ft@22
   198
ft@22
   199
def sendDataToRest (urlpath, data):
ft@22
   200
    netifaces.ifaddresses("eth0")[2][0]["addr"]
ft@22
   201
    
ft@22
   202
    # Get first address in network (0 = network ip -> 192.168.0.0)
ft@22
   203
    remote_ip = netaddr.IPNetwork("%s/%s" %(netifaces.ifaddresses("eth0")[2][0]["addr"], netifaces.ifaddresses("eth0")[2][0]["netmask"]))[1]
ft@22
   204
    
ft@22
   205
    url = ("http://%s:8090//%s" %(remote_ip, urlpath))
ft@22
   206
ft@22
   207
    LOG.debug ("Send data to \"%s\"" %(url, ))
ft@22
   208
    LOG.debug ("Data: %s" %(data, ))
ft@22
   209
    
ft@22
   210
    try:
ft@22
   211
        response = httpPool.request_encode_body("POST", url, fields=data, retries=0)
ft@22
   212
    except Exception, e:
ft@22
   213
        LOG.error("Remote host not reachable")
ft@22
   214
        LOG.error ("Exception: %s" %(e,))
ft@22
   215
        return
ft@22
   216
    
ft@22
   217
    if response.status == STATUS_CODE_OK:
ft@22
   218
        LOG.info("Data sent successfully to rest server")
ft@22
   219
        return True
ft@22
   220
    else:
ft@22
   221
        LOG.error("Server returned errorcode: %s" %(response.status,))
ft@22
   222
        return False
ft@22
   223
    
ft@22
   224
ft@22
   225
def sendNotification (type, message):
ft@22
   226
    data = {"msgtype" : type, "text" : message}
ft@22
   227
    
ft@22
   228
    if (type == "information"):
ft@22
   229
        sendDataToRest ("message", data)
ft@22
   230
    else:
ft@22
   231
        sendDataToRest ("notification", data)
ft@22
   232
ft@22
   233
def sendReadOnlyNotification():
ft@22
   234
    sendNotification("critical", "Filesystem is in read only mode. If you want to export files please initialize an encrypted filesystem.")
ft@22
   235
    
ft@22
   236
def sendLogNotPossibleNotification():
ft@22
   237
    sendNotification("critical", "Send log entry to opensecurity rest server failed.")
ft@22
   238
    
ft@22
   239
def sendFileLog(filename, filesize, filehash, hashtype):
ft@22
   240
    data = {"filename" : filename, "filesize" : "%s" %(filesize,), "filehash" : filehash, "hashtype" : hashtype}
ft@22
   241
    retval = sendDataToRest ("log", data)
ft@22
   242
    if (retval == False):
ft@22
   243
        sendLogNotPossibleNotification()
ft@22
   244
ft@22
   245
def calcMD5 (path, block_size=256*128, hr=True):
ft@22
   246
    md5 = hashlib.md5()
ft@22
   247
    with open(path,'rb') as f: 
ft@22
   248
        for chunk in iter(lambda: f.read(block_size), b''): 
ft@22
   249
             md5.update(chunk)
ft@22
   250
    if hr:
ft@22
   251
        return md5.hexdigest()
ft@22
   252
    return md5.digest()
ft@22
   253
ft@22
   254
class OsecFS (Fuse):
ft@22
   255
ft@22
   256
    __rootpath = None
ft@22
   257
ft@22
   258
    # default fuse init
ft@22
   259
    def __init__(self, rootpath, *args, **kw):
ft@22
   260
        self.__rootpath = rootpath
ft@22
   261
        Fuse.__init__ (self, *args, **kw)
ft@22
   262
        LOG.debug ("Init complete.")
ft@22
   263
        sendNotification("information", "Filesystem successfully mounted.")
ft@22
   264
ft@22
   265
    # defines that our working directory will be the __rootpath
ft@22
   266
    def fsinit(self):
ft@22
   267
        os.chdir (self.__rootpath)
ft@22
   268
ft@22
   269
    def getattr(self, path):
ft@22
   270
        LOG.debug ("*** getattr (%s)" % (fixPath (path)))
ft@22
   271
        return os.lstat (fixPath (path));
ft@22
   272
ft@22
   273
    def getdir(self, path):
ft@22
   274
        LOG.debug ("*** getdir (%s)" % (path));
ft@22
   275
        return os.listdir (fixPath (path))
ft@22
   276
ft@22
   277
    def readdir(self, path, offset):
ft@22
   278
        LOG.debug ("*** readdir (%s %s)" % (path, offset));
ft@22
   279
        for e in os.listdir (fixPath (path)):
ft@22
   280
            yield fuse.Direntry(e)
ft@22
   281
ft@22
   282
    def chmod (self, path, mode):
ft@22
   283
        LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
ft@22
   284
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   285
            sendReadOnlyNotification()
ft@22
   286
            return -errno.EACCES
ft@22
   287
        os.chmod (fixPath (path), mode)
ft@22
   288
ft@22
   289
    def chown (self, path, uid, gid):
ft@22
   290
        LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
ft@22
   291
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   292
            sendReadOnlyNotification()
ft@22
   293
            return -errno.EACCES
ft@22
   294
        os.chown (fixPath (path), uid, gid)
ft@22
   295
ft@22
   296
    def link (self, targetPath, linkPath):
ft@22
   297
        LOG.debug ("*** link %s %s" % (targetPath, linkPath))
ft@22
   298
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   299
            sendReadOnlyNotification()
ft@22
   300
            return -errno.EACCES
ft@22
   301
        os.link (fixPath (targetPath), fixPath (linkPath))
ft@22
   302
ft@22
   303
    def mkdir (self, path, mode):
ft@22
   304
        LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
ft@22
   305
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   306
            sendReadOnlyNotification()
ft@22
   307
            return -errno.EACCES
ft@22
   308
        os.mkdir (fixPath (path), mode)
ft@22
   309
ft@22
   310
    def mknod (self, path, mode, dev):
ft@22
   311
        LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
ft@22
   312
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   313
            sendReadOnlyNotification()
ft@22
   314
            return -errno.EACCES
ft@22
   315
        os.mknod (fixPath (path), mode, dev)
ft@22
   316
ft@22
   317
    # to implement virus scan
ft@22
   318
    def open (self, path, flags):
ft@22
   319
        LOG.debug ("*** open %s %s" % (path, oct (flags)))
ft@22
   320
        self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
ft@22
   321
        self.written = False
ft@22
   322
        self.fd = self.file.fileno ()
ft@22
   323
        
ft@22
   324
        LOG.debug(self.__rootpath)
ft@22
   325
        LOG.debug(path)
ft@22
   326
        
ft@22
   327
        retval = scanFile (rootPath(self.__rootpath, path), self.file)
ft@22
   328
        
ft@22
   329
        #if type(retval) is not dict:
ft@22
   330
        if (isinstance(retval, dict) == False):
ft@22
   331
            LOG.error(SCAN_WRONG_RETURN_VALUE)
ft@22
   332
            self.file.close ()
ft@22
   333
            return -errno.EACCES
ft@22
   334
        
ft@22
   335
        if ((retval.has_key("infected") == False) or (retval.has_key("virusname") == False)):
ft@22
   336
            LOG.error(SCAN_RETURN_VALUE_KEY_MISSING)
ft@22
   337
            self.file.close ()
ft@22
   338
            return -errno.EACCES
ft@22
   339
            
ft@22
   340
        
ft@22
   341
        if (retval.get("infected") == True):
ft@22
   342
            self.file.close ()
ft@22
   343
            sendNotification(NOTIFICATION_CRITICAL, "%s\nFile: %s\nVirus: %s" %(VIRUS_FOUND, path, retval.get("virusname")))
ft@22
   344
            LOG.error("%s" %(VIRUS_FOUND,))
ft@22
   345
            LOG.error("Virus: %s" %(retval.get("virusname"),))
ft@22
   346
            return -errno.EACCES
ft@22
   347
        
ft@22
   348
        whitelisted = whitelistFile (rootPath(self.__rootpath, path))
ft@22
   349
        if (whitelisted == False):
ft@22
   350
            self.file.close ()
ft@22
   351
            sendNotification(NOTIFICATION_CRITICAL, "File not in whitelist. Access denied.")
ft@22
   352
            return -errno.EACCES
ft@22
   353
ft@22
   354
    def read (self, path, length, offset):
ft@22
   355
        LOG.debug ("*** read %s %s %s" % (path, length, offset))
ft@22
   356
        self.file.seek (offset)
ft@22
   357
        return self.file.read (length)
ft@22
   358
ft@22
   359
    def readlink (self, path):
ft@22
   360
        LOG.debug ("*** readlink %s" % (path))
ft@22
   361
        return os.readlink (fixPath (path))
ft@22
   362
ft@22
   363
    def release (self, path, flags):
ft@22
   364
        LOG.debug ("*** release %s %s" % (path, oct (flags)))
ft@22
   365
        self.file.flush()
ft@22
   366
        os.fsync(self.file.fileno())
ft@22
   367
        self.file.close ()
ft@22
   368
        
ft@22
   369
        if (self.written == True):
ft@22
   370
            hashsum = calcMD5(fixPath(path))
ft@22
   371
            filesize = os.path.getsize(fixPath(path))
ft@22
   372
            sendFileLog(path, filesize, hashsum, "md5")
ft@22
   373
        
ft@22
   374
ft@22
   375
    def rename (self, oldPath, newPath):
ft@22
   376
        LOG.debug ("*** rename %s %s %s" % (oldPath, newPath, config.get("Main", "ReadOnly")))
ft@22
   377
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   378
            sendReadOnlyNotification()
ft@22
   379
            return -errno.EACCES
ft@22
   380
        os.rename (fixPath (oldPath), fixPath (newPath))
ft@22
   381
ft@22
   382
    def rmdir (self, path):
ft@22
   383
        LOG.debug ("*** rmdir %s %s" % (path, config.get("Main", "ReadOnly")))
ft@22
   384
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   385
            sendReadOnlyNotification()
ft@22
   386
            return -errno.EACCES
ft@22
   387
        os.rmdir (fixPath (path))
ft@22
   388
ft@22
   389
    def statfs (self):
ft@22
   390
        LOG.debug ("*** statfs")
ft@22
   391
        return os.statvfs(".")
ft@22
   392
ft@22
   393
    def symlink (self, targetPath, linkPath):
ft@22
   394
        LOG.debug ("*** symlink %s %s %s" % (targetPath, linkPath, config.get("Main", "ReadOnly")))
ft@22
   395
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   396
            sendReadOnlyNotification()
ft@22
   397
            return -errno.EACCES
ft@22
   398
        os.symlink (fixPath (targetPath), fixPath (linkPath))
ft@22
   399
ft@22
   400
    def truncate (self, path, length):
ft@22
   401
        LOG.debug ("*** truncate %s %s %s" % (path, length, config.get("Main", "ReadOnly")))
ft@22
   402
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   403
            sendReadOnlyNotification()
ft@22
   404
            return -errno.EACCES
ft@22
   405
        f = open (fixPath (path), "w+")
ft@22
   406
        f.truncate (length)
ft@22
   407
        f.close ()
ft@22
   408
ft@22
   409
    def unlink (self, path):
ft@22
   410
        LOG.debug ("*** unlink %s %s" % (path, config.get("Main", "ReadOnly")))
ft@22
   411
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   412
            sendReadOnlyNotification()
ft@22
   413
            return -errno.EACCES
ft@22
   414
        os.unlink (fixPath (path))
ft@22
   415
ft@22
   416
    def utime (self, path, times):
ft@22
   417
        LOG.debug ("*** utime %s %s" % (path, times))
ft@22
   418
        os.utime (fixPath (path), times)
ft@22
   419
ft@22
   420
    def write (self, path, buf, offset):
ft@22
   421
        #LOG.debug ("*** write %s %s %s %s" % (path, buf, offset, config.get("Main", "ReadOnly")))
ft@22
   422
        LOG.debug ("*** write %s %s %s %s" % (path, "filecontent", offset, config.get("Main", "ReadOnly")))
ft@22
   423
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   424
            self.file.close()
ft@22
   425
            sendReadOnlyNotification()
ft@22
   426
            return -errno.EACCES
ft@22
   427
        self.file.seek (offset)
ft@22
   428
        self.file.write (buf)
ft@22
   429
        self.written = True
ft@22
   430
        return len (buf)
ft@22
   431
ft@22
   432
    def access (self, path, mode):
ft@22
   433
        LOG.debug ("*** access %s %s" % (path, oct (mode)))
ft@22
   434
        if not os.access (fixPath (path), mode):
ft@22
   435
            return -errno.EACCES
ft@22
   436
ft@22
   437
    def create (self, path, flags, mode):
ft@22
   438
        LOG.debug ("*** create %s %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags), config.get("Main", "ReadOnly")))
ft@22
   439
        if (config.get("Main", "ReadOnly") == "true"):
ft@22
   440
            sendReadOnlyNotification()
ft@22
   441
            return -errno.EACCES
ft@22
   442
ft@22
   443
        self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode(flags))
ft@22
   444
        self.written = True
ft@22
   445
        self.fd = self.file.fileno ()
ft@22
   446
ft@22
   447
ft@22
   448
if __name__ == "__main__":
ft@22
   449
    # Set api version
ft@22
   450
    fuse.fuse_python_api = (0, 2)
ft@22
   451
    fuse.feature_assert ('stateful_files', 'has_init')
ft@22
   452
ft@22
   453
    config = loadConfig ()
ft@22
   454
    initLog (config)
ft@22
   455
    
ft@22
   456
    #sendNotification("Info", "OsecFS started")
ft@22
   457
    
ft@22
   458
    # Import the Malware Scanner
ft@22
   459
    sys.path.append(config.get("Main", "ScannerPath"))
ft@22
   460
    
ft@22
   461
    MalwareModule = import_module(config.get("Main", "ScannerModuleName"))
ft@22
   462
    MalwareClass = getattr(MalwareModule, config.get("Main", "ScannerClassName"))
ft@22
   463
    
ft@22
   464
    MalwareScanner = MalwareClass (config.get("Main", "ScannerConfig"));
ft@22
   465
    
ft@22
   466
    osecfs = OsecFS (config.get ("Main", "Rootpath"))
ft@22
   467
    osecfs.flags = 0
ft@22
   468
    osecfs.multithreaded = 0
ft@22
   469
ft@22
   470
    fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
ft@22
   471
    osecfs.main (fuse_args)