initial commit of osefcs package
authorft
Tue, 04 Nov 2014 16:28:40 +0100
changeset 2223028352807f
parent 2 d27473cf6a01
child 23 5b9fdcafd0df
initial commit of osefcs package
Apache License, Version 2.0.txt
ClamAVScanner.cfg
IkarusScanner.cfg
clamavscanner/ClamAVScanner.py
config/OsecFS.cfg
ikarusscanner/IkarusScanner.py
osecfs
osecfs-package
osecfs-package.conf
osecfs_0.0.10_all.deb
osecfs_0.0.11_all.deb
osecfs_0.0.12_all.deb
osecfs_0.0.13_all.deb
osecfs_0.0.14_all.deb
osecfs_0.0.15_all.deb
osecfs_0.0.16_all.deb
osecfs_0.0.17_all.deb
osecfs_0.0.18_all.deb
osecfs_0.0.19_all.deb
osecfs_0.0.1_all.deb
osecfs_0.0.20_all.deb
osecfs_0.0.21_all.deb
osecfs_0.0.22_all.deb
osecfs_0.0.23_all.deb
osecfs_0.0.24_all.deb
osecfs_0.0.25_all.deb
osecfs_0.0.26_all.deb
osecfs_0.0.2_all.deb
osecfs_0.0.3_all.deb
osecfs_0.0.4_all.deb
osecfs_0.0.5_all.deb
osecfs_0.0.6_all.deb
osecfs_0.0.7_all.deb
osecfs_0.0.8_all.deb
osecfs_0.0.9_all.deb
osecfs_downloads.cfg
osecfs_usb.cfg
src/OsecFS.py
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Apache License, Version 2.0.txt	Tue Nov 04 16:28:40 2014 +0100
     1.3 @@ -0,0 +1,202 @@
     1.4 +
     1.5 +                                 Apache License
     1.6 +                           Version 2.0, January 2004
     1.7 +                        http://www.apache.org/licenses/
     1.8 +
     1.9 +   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    1.10 +
    1.11 +   1. Definitions.
    1.12 +
    1.13 +      "License" shall mean the terms and conditions for use, reproduction,
    1.14 +      and distribution as defined by Sections 1 through 9 of this document.
    1.15 +
    1.16 +      "Licensor" shall mean the copyright owner or entity authorized by
    1.17 +      the copyright owner that is granting the License.
    1.18 +
    1.19 +      "Legal Entity" shall mean the union of the acting entity and all
    1.20 +      other entities that control, are controlled by, or are under common
    1.21 +      control with that entity. For the purposes of this definition,
    1.22 +      "control" means (i) the power, direct or indirect, to cause the
    1.23 +      direction or management of such entity, whether by contract or
    1.24 +      otherwise, or (ii) ownership of fifty percent (50%) or more of the
    1.25 +      outstanding shares, or (iii) beneficial ownership of such entity.
    1.26 +
    1.27 +      "You" (or "Your") shall mean an individual or Legal Entity
    1.28 +      exercising permissions granted by this License.
    1.29 +
    1.30 +      "Source" form shall mean the preferred form for making modifications,
    1.31 +      including but not limited to software source code, documentation
    1.32 +      source, and configuration files.
    1.33 +
    1.34 +      "Object" form shall mean any form resulting from mechanical
    1.35 +      transformation or translation of a Source form, including but
    1.36 +      not limited to compiled object code, generated documentation,
    1.37 +      and conversions to other media types.
    1.38 +
    1.39 +      "Work" shall mean the work of authorship, whether in Source or
    1.40 +      Object form, made available under the License, as indicated by a
    1.41 +      copyright notice that is included in or attached to the work
    1.42 +      (an example is provided in the Appendix below).
    1.43 +
    1.44 +      "Derivative Works" shall mean any work, whether in Source or Object
    1.45 +      form, that is based on (or derived from) the Work and for which the
    1.46 +      editorial revisions, annotations, elaborations, or other modifications
    1.47 +      represent, as a whole, an original work of authorship. For the purposes
    1.48 +      of this License, Derivative Works shall not include works that remain
    1.49 +      separable from, or merely link (or bind by name) to the interfaces of,
    1.50 +      the Work and Derivative Works thereof.
    1.51 +
    1.52 +      "Contribution" shall mean any work of authorship, including
    1.53 +      the original version of the Work and any modifications or additions
    1.54 +      to that Work or Derivative Works thereof, that is intentionally
    1.55 +      submitted to Licensor for inclusion in the Work by the copyright owner
    1.56 +      or by an individual or Legal Entity authorized to submit on behalf of
    1.57 +      the copyright owner. For the purposes of this definition, "submitted"
    1.58 +      means any form of electronic, verbal, or written communication sent
    1.59 +      to the Licensor or its representatives, including but not limited to
    1.60 +      communication on electronic mailing lists, source code control systems,
    1.61 +      and issue tracking systems that are managed by, or on behalf of, the
    1.62 +      Licensor for the purpose of discussing and improving the Work, but
    1.63 +      excluding communication that is conspicuously marked or otherwise
    1.64 +      designated in writing by the copyright owner as "Not a Contribution."
    1.65 +
    1.66 +      "Contributor" shall mean Licensor and any individual or Legal Entity
    1.67 +      on behalf of whom a Contribution has been received by Licensor and
    1.68 +      subsequently incorporated within the Work.
    1.69 +
    1.70 +   2. Grant of Copyright License. Subject to the terms and conditions of
    1.71 +      this License, each Contributor hereby grants to You a perpetual,
    1.72 +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    1.73 +      copyright license to reproduce, prepare Derivative Works of,
    1.74 +      publicly display, publicly perform, sublicense, and distribute the
    1.75 +      Work and such Derivative Works in Source or Object form.
    1.76 +
    1.77 +   3. Grant of Patent License. Subject to the terms and conditions of
    1.78 +      this License, each Contributor hereby grants to You a perpetual,
    1.79 +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    1.80 +      (except as stated in this section) patent license to make, have made,
    1.81 +      use, offer to sell, sell, import, and otherwise transfer the Work,
    1.82 +      where such license applies only to those patent claims licensable
    1.83 +      by such Contributor that are necessarily infringed by their
    1.84 +      Contribution(s) alone or by combination of their Contribution(s)
    1.85 +      with the Work to which such Contribution(s) was submitted. If You
    1.86 +      institute patent litigation against any entity (including a
    1.87 +      cross-claim or counterclaim in a lawsuit) alleging that the Work
    1.88 +      or a Contribution incorporated within the Work constitutes direct
    1.89 +      or contributory patent infringement, then any patent licenses
    1.90 +      granted to You under this License for that Work shall terminate
    1.91 +      as of the date such litigation is filed.
    1.92 +
    1.93 +   4. Redistribution. You may reproduce and distribute copies of the
    1.94 +      Work or Derivative Works thereof in any medium, with or without
    1.95 +      modifications, and in Source or Object form, provided that You
    1.96 +      meet the following conditions:
    1.97 +
    1.98 +      (a) You must give any other recipients of the Work or
    1.99 +          Derivative Works a copy of this License; and
   1.100 +
   1.101 +      (b) You must cause any modified files to carry prominent notices
   1.102 +          stating that You changed the files; and
   1.103 +
   1.104 +      (c) You must retain, in the Source form of any Derivative Works
   1.105 +          that You distribute, all copyright, patent, trademark, and
   1.106 +          attribution notices from the Source form of the Work,
   1.107 +          excluding those notices that do not pertain to any part of
   1.108 +          the Derivative Works; and
   1.109 +
   1.110 +      (d) If the Work includes a "NOTICE" text file as part of its
   1.111 +          distribution, then any Derivative Works that You distribute must
   1.112 +          include a readable copy of the attribution notices contained
   1.113 +          within such NOTICE file, excluding those notices that do not
   1.114 +          pertain to any part of the Derivative Works, in at least one
   1.115 +          of the following places: within a NOTICE text file distributed
   1.116 +          as part of the Derivative Works; within the Source form or
   1.117 +          documentation, if provided along with the Derivative Works; or,
   1.118 +          within a display generated by the Derivative Works, if and
   1.119 +          wherever such third-party notices normally appear. The contents
   1.120 +          of the NOTICE file are for informational purposes only and
   1.121 +          do not modify the License. You may add Your own attribution
   1.122 +          notices within Derivative Works that You distribute, alongside
   1.123 +          or as an addendum to the NOTICE text from the Work, provided
   1.124 +          that such additional attribution notices cannot be construed
   1.125 +          as modifying the License.
   1.126 +
   1.127 +      You may add Your own copyright statement to Your modifications and
   1.128 +      may provide additional or different license terms and conditions
   1.129 +      for use, reproduction, or distribution of Your modifications, or
   1.130 +      for any such Derivative Works as a whole, provided Your use,
   1.131 +      reproduction, and distribution of the Work otherwise complies with
   1.132 +      the conditions stated in this License.
   1.133 +
   1.134 +   5. Submission of Contributions. Unless You explicitly state otherwise,
   1.135 +      any Contribution intentionally submitted for inclusion in the Work
   1.136 +      by You to the Licensor shall be under the terms and conditions of
   1.137 +      this License, without any additional terms or conditions.
   1.138 +      Notwithstanding the above, nothing herein shall supersede or modify
   1.139 +      the terms of any separate license agreement you may have executed
   1.140 +      with Licensor regarding such Contributions.
   1.141 +
   1.142 +   6. Trademarks. This License does not grant permission to use the trade
   1.143 +      names, trademarks, service marks, or product names of the Licensor,
   1.144 +      except as required for reasonable and customary use in describing the
   1.145 +      origin of the Work and reproducing the content of the NOTICE file.
   1.146 +
   1.147 +   7. Disclaimer of Warranty. Unless required by applicable law or
   1.148 +      agreed to in writing, Licensor provides the Work (and each
   1.149 +      Contributor provides its Contributions) on an "AS IS" BASIS,
   1.150 +      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   1.151 +      implied, including, without limitation, any warranties or conditions
   1.152 +      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   1.153 +      PARTICULAR PURPOSE. You are solely responsible for determining the
   1.154 +      appropriateness of using or redistributing the Work and assume any
   1.155 +      risks associated with Your exercise of permissions under this License.
   1.156 +
   1.157 +   8. Limitation of Liability. In no event and under no legal theory,
   1.158 +      whether in tort (including negligence), contract, or otherwise,
   1.159 +      unless required by applicable law (such as deliberate and grossly
   1.160 +      negligent acts) or agreed to in writing, shall any Contributor be
   1.161 +      liable to You for damages, including any direct, indirect, special,
   1.162 +      incidental, or consequential damages of any character arising as a
   1.163 +      result of this License or out of the use or inability to use the
   1.164 +      Work (including but not limited to damages for loss of goodwill,
   1.165 +      work stoppage, computer failure or malfunction, or any and all
   1.166 +      other commercial damages or losses), even if such Contributor
   1.167 +      has been advised of the possibility of such damages.
   1.168 +
   1.169 +   9. Accepting Warranty or Additional Liability. While redistributing
   1.170 +      the Work or Derivative Works thereof, You may choose to offer,
   1.171 +      and charge a fee for, acceptance of support, warranty, indemnity,
   1.172 +      or other liability obligations and/or rights consistent with this
   1.173 +      License. However, in accepting such obligations, You may act only
   1.174 +      on Your own behalf and on Your sole responsibility, not on behalf
   1.175 +      of any other Contributor, and only if You agree to indemnify,
   1.176 +      defend, and hold each Contributor harmless for any liability
   1.177 +      incurred by, or claims asserted against, such Contributor by reason
   1.178 +      of your accepting any such warranty or additional liability.
   1.179 +
   1.180 +   END OF TERMS AND CONDITIONS
   1.181 +
   1.182 +   APPENDIX: How to apply the Apache License to your work.
   1.183 +
   1.184 +      To apply the Apache License to your work, attach the following
   1.185 +      boilerplate notice, with the fields enclosed by brackets "[]"
   1.186 +      replaced with your own identifying information. (Don't include
   1.187 +      the brackets!)  The text should be enclosed in the appropriate
   1.188 +      comment syntax for the file format. We also recommend that a
   1.189 +      file or class name and description of purpose be included on the
   1.190 +      same "printed page" as the copyright notice for easier
   1.191 +      identification within third-party archives.
   1.192 +
   1.193 +   Copyright [yyyy] [name of copyright owner]
   1.194 +
   1.195 +   Licensed under the Apache License, Version 2.0 (the "License");
   1.196 +   you may not use this file except in compliance with the License.
   1.197 +   You may obtain a copy of the License at
   1.198 +
   1.199 +       http://www.apache.org/licenses/LICENSE-2.0
   1.200 +
   1.201 +   Unless required by applicable law or agreed to in writing, software
   1.202 +   distributed under the License is distributed on an "AS IS" BASIS,
   1.203 +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   1.204 +   See the License for the specific language governing permissions and
   1.205 +   limitations under the License.
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/ClamAVScanner.cfg	Tue Nov 04 16:28:40 2014 +0100
     2.3 @@ -0,0 +1,2 @@
     2.4 +[Main]
     2.5 +Nothing: placeholder
     2.6 \ No newline at end of file
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/IkarusScanner.cfg	Tue Nov 04 16:28:40 2014 +0100
     3.3 @@ -0,0 +1,12 @@
     3.4 +[Main]
     3.5 +# the maximum file size in MB that is scanned
     3.6 +MaxFileSize: 50
     3.7 +
     3.8 +# the URL of the local scan server
     3.9 +LocalScanserverURL: http://localhost/virusscan
    3.10 +
    3.11 +# the URL of the remote scan server
    3.12 +RemoteScanserverURL: http://10.215.5.166/virusscan
    3.13 +
    3.14 +# wait time in seconds until a new connection attempt to remote server is made
    3.15 +RetryTimeout: 600
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/clamavscanner/ClamAVScanner.py	Tue Nov 04 16:28:40 2014 +0100
     4.3 @@ -0,0 +1,111 @@
     4.4 +#!/usr/bin/python
     4.5 +
     4.6 +# ------------------------------------------------------------
     4.7 +# opensecurity package file
     4.8 +#
     4.9 +# Autor: X-Net Services GmbH <office@x-net.at>
    4.10 +#
    4.11 +# Copyright 2013-2014 X-Net and AIT Austrian Institute of Technology
    4.12 +#
    4.13 +#
    4.14 +#     X-Net Technologies GmbH
    4.15 +#     Elisabethstrasse 1
    4.16 +#     4020 Linz
    4.17 +#     AUSTRIA
    4.18 +#     https://www.x-net.at
    4.19 +#
    4.20 +#     AIT Austrian Institute of Technology
    4.21 +#     Donau City Strasse 1
    4.22 +#     1220 Wien
    4.23 +#     AUSTRIA
    4.24 +#     http://www.ait.ac.at
    4.25 +#
    4.26 +#
    4.27 +# Licensed under the Apache License, Version 2.0 (the "License");
    4.28 +# you may not use this file except in compliance with the License.
    4.29 +# You may obtain a copy of the License at
    4.30 +#
    4.31 +#    http://www.apache.org/licenses/LICENSE-2.0
    4.32 +#
    4.33 +# Unless required by applicable law or agreed to in writing, software
    4.34 +# distributed under the License is distributed on an "AS IS" BASIS,
    4.35 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    4.36 +# See the License for the specific language governing permissions and
    4.37 +# limitations under the License.
    4.38 +# ------------------------------------------------------------
    4.39 +
    4.40 +import ConfigParser
    4.41 +
    4.42 +import sys
    4.43 +
    4.44 +import logging
    4.45 +import os
    4.46 +import errno
    4.47 +import time
    4.48 +
    4.49 +import pyclamav
    4.50 +
    4.51 +
    4.52 +class ClamAVScanner:
    4.53 +    
    4.54 +    # User the existing logger  instance
    4.55 +    __LOG = logging.getLogger("IkarusScanner")
    4.56 +    
    4.57 +    __MINOPTS = { "Main" : ["Nothing"]}
    4.58 +    __CONFIG_NOT_READABLE = "Configfile is not readable"
    4.59 +    __CONFIG_WRONG = "Something is wrong with the config"
    4.60 +    __CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
    4.61 +    
    4.62 +
    4.63 +    
    4.64 +    def __init__ (self, scanner_config_path):
    4.65 +        config = self.loadConfig (scanner_config_path)
    4.66 +
    4.67 +    
    4.68 +
    4.69 +    def checkMinimumOptions (self, config):
    4.70 +        for section, options in self.__MINOPTS.iteritems ():
    4.71 +            for option in options:
    4.72 +                if (config.has_option(section, option) == False):
    4.73 +                    self.__LOG.error (self.__CONFIG_MISSING % (section, option))
    4.74 +                    exit (129)
    4.75 +
    4.76 +    def loadConfig (self, scanner_config_path):
    4.77 +
    4.78 +        configfile = scanner_config_path
    4.79 +        config = ConfigParser.SafeConfigParser ()
    4.80 +    
    4.81 +        if ((os.path.exists (scanner_config_path) == False) or (os.path.isfile (scanner_config_path) == False) or (os.access (scanner_config_path, os.R_OK) == False)):
    4.82 +            self.__LOG.error(self.__CONFIG_NOT_READABLE);
    4.83 +            raise SystemError(self.__CONFIG_NOT_READABLE)
    4.84 +    
    4.85 +        try:
    4.86 +            config.read (scanner_config_path)
    4.87 +        except Exception, e:
    4.88 +            self.__LOG.error("Error: %s" % (e));
    4.89 +            raise SystemError("Error: %s" % (e))
    4.90 +
    4.91 +        self.checkMinimumOptions (config)
    4.92 +    
    4.93 +        return config
    4.94 +
    4.95 +    
    4.96 +    def scanFile (self, path, fileobject):
    4.97 +        return self.scanFileClamAV (path)
    4.98 +    
    4.99 +    def scanFileClamAV (self, path):        
   4.100 +        retval = { "infected" : False, "virusname" : "Unknown" }
   4.101 +    
   4.102 +        self.__LOG.debug ("Scan File: %s" % (path))
   4.103 +    
   4.104 +        result = pyclamav.scanfile (path)
   4.105 +        self.__LOG.debug ("Result of file \"%s\": %s" % (path, result))
   4.106 +        if (result[0] != 0):
   4.107 +            retval["infected"] = True
   4.108 +            retval["virusname"] = result[1]
   4.109 +    
   4.110 +        if (retval["infected"] == True):
   4.111 +            self.__LOG.error ("Virus found, deny Access %s" % (result,))
   4.112 +    
   4.113 +        return retval
   4.114 +
     5.1 --- a/config/OsecFS.cfg	Wed Nov 27 15:37:26 2013 +0100
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,15 +0,0 @@
     5.4 -[Main]
     5.5 -# make sure this file is writeable
     5.6 -Logfile: /var/log/fuse_test.log
     5.7 -
     5.8 -# the place that the user will see (you can't access virusfiles here)
     5.9 -Mountpoint: /tmp/virtual_fuse
    5.10 -
    5.11 -# where the files really are on the filesystem 
    5.12 -Rootpath: /tmp/root_fuse
    5.13 -
    5.14 -# the URL of the local scan server
    5.15 -LocalScanserverURL: http://192.168.63.128/virusscan
    5.16 -
    5.17 -# the URL of the remote scan server
    5.18 -RemoteScanserverURL: http://192.168.63.128/virusscan
    5.19 \ No newline at end of file
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/ikarusscanner/IkarusScanner.py	Tue Nov 04 16:28:40 2014 +0100
     6.3 @@ -0,0 +1,185 @@
     6.4 +#!/usr/bin/python
     6.5 +
     6.6 +# ------------------------------------------------------------
     6.7 +# opensecurity package file
     6.8 +#
     6.9 +# Autor:  Karlberger Christoph <Karlberger.C@ikarus.at>
    6.10 +#         X-Net Services GmbH <office@x-net.at>
    6.11 +#
    6.12 +# Copyright 2013-2014 X-Net and AIT Austrian Institute of Technology
    6.13 +#
    6.14 +#     IKARUS Security Software GmbH
    6.15 +#     Blechturmgasse 11
    6.16 +#     1050 Wien
    6.17 +#     AUSTRIA
    6.18 +#     http://www.ikarussecurity.com
    6.19 +#
    6.20 +#     X-Net Technologies GmbH
    6.21 +#     Elisabethstrasse 1
    6.22 +#     4020 Linz
    6.23 +#     AUSTRIA
    6.24 +#     https://www.x-net.at
    6.25 +#
    6.26 +#     AIT Austrian Institute of Technology
    6.27 +#     Donau City Strasse 1
    6.28 +#     1220 Wien
    6.29 +#     AUSTRIA
    6.30 +#     http://www.ait.ac.at
    6.31 +#
    6.32 +#
    6.33 +# Licensed under the Apache License, Version 2.0 (the "License");
    6.34 +# you may not use this file except in compliance with the License.
    6.35 +# You may obtain a copy of the License at
    6.36 +#
    6.37 +#    http://www.apache.org/licenses/LICENSE-2.0
    6.38 +#
    6.39 +# Unless required by applicable law or agreed to in writing, software
    6.40 +# distributed under the License is distributed on an "AS IS" BASIS,
    6.41 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    6.42 +# See the License for the specific language governing permissions and
    6.43 +# limitations under the License.
    6.44 +# ------------------------------------------------------------
    6.45 +
    6.46 +import ConfigParser
    6.47 +
    6.48 +import sys
    6.49 +
    6.50 +import logging
    6.51 +import os
    6.52 +import errno
    6.53 +import time
    6.54 +
    6.55 +import urllib3
    6.56 +import xml.etree.ElementTree as ET
    6.57 +
    6.58 +class IkarusScanner:
    6.59 +    
    6.60 +    # User the existing logger  instance
    6.61 +    __LOG = logging.getLogger("IkarusScanner")
    6.62 +    
    6.63 +    __MINOPTS = { "Main" : ["LocalScanserverURL", "RemoteScanserverURL", "MaxFileSize", "RetryTimeout"]}
    6.64 +    __CONFIG_NOT_READABLE = "Configfile is not readable"
    6.65 +    __CONFIG_WRONG = "Something is wrong with the config"
    6.66 +    __CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
    6.67 +    __LOCAL_SCANSERVER_URL = ""
    6.68 +    __REMOTE_SCANSERVER_URL = ""
    6.69 +    __STATUS_CODE_OK = 200
    6.70 +    __STATUS_CODE_INFECTED = 210
    6.71 +    __STATUS_CODE_NOT_FOUND = 404
    6.72 +    __MAX_SCAN_FILE_SIZE = 50 * 0x100000
    6.73 +    __SCANSERVER_RETRY_TIMEOUT = 60
    6.74 +    
    6.75 +    # Global http pool manager used to connect to the scan server
    6.76 +    __remoteScanserverReachable = True
    6.77 +    __scanserverTimestamp = 0
    6.78 +    __httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
    6.79 +    
    6.80 +    def __init__ (self, scanner_config_path):
    6.81 +        config = self.loadConfig (scanner_config_path)
    6.82 +    
    6.83 +        self.__scanserverTimestamp = time.time()
    6.84 +    
    6.85 +        self.__LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
    6.86 +        self.__REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
    6.87 +        self.__SCANSERVER_RETRY_TIMEOUT = int(config.get("Main", "RetryTimeout"))
    6.88 +    
    6.89 +        # Convert file size from MB to byte
    6.90 +        self.__MAX_SCAN_FILE_SIZE = int(config.get("Main", "MaxFileSize")) * 0x100000
    6.91 +    
    6.92 +
    6.93 +    def checkMinimumOptions (self, config):
    6.94 +        for section, options in self.__MINOPTS.iteritems ():
    6.95 +            for option in options:
    6.96 +                if (config.has_option(section, option) == False):
    6.97 +                    self.__LOG.error (self.__CONFIG_MISSING % (section, option))
    6.98 +                    exit (129)
    6.99 +
   6.100 +    def loadConfig (self, scanner_config_path):
   6.101 +
   6.102 +        configfile = scanner_config_path
   6.103 +        config = ConfigParser.SafeConfigParser ()
   6.104 +    
   6.105 +        if ((os.path.exists (scanner_config_path) == False) or (os.path.isfile (scanner_config_path) == False) or (os.access (scanner_config_path, os.R_OK) == False)):
   6.106 +            self.__LOG.error(self.__CONFIG_NOT_READABLE);
   6.107 +            raise SystemError(self.__CONFIG_NOT_READABLE)
   6.108 +    
   6.109 +        try:
   6.110 +            config.read (scanner_config_path)
   6.111 +        except Exception, e:
   6.112 +            self.__LOG.error("Error: %s" % (e));
   6.113 +            raise SystemError("Error: %s" % (e))
   6.114 +
   6.115 +        self.checkMinimumOptions (config)
   6.116 +    
   6.117 +        return config
   6.118 +
   6.119 +    def contactScanserver(self, url, fields):
   6.120 +        self.__LOG.debug("Contacting server %s" % url)
   6.121 +        return self.__httpPool.request_encode_body('POST', url, fields = fields, retries = 0)
   6.122 +    
   6.123 +    def scanFile (self, path, fileobject):
   6.124 +        return self.scanFileIkarus (path, fileobject)
   6.125 +
   6.126 +    def scanFileIkarus (self, path, fileobject):
   6.127 +        retval = { "infected" : False, "virusname" : "Unknown" }
   6.128 +        self.__LOG.debug ("Scan File: %s" % (path))
   6.129 +    
   6.130 +        if (os.fstat(fileobject.fileno()).st_size > self.__MAX_SCAN_FILE_SIZE):
   6.131 +            self.__LOG.info("File max size exceeded. The file is not scanned.")
   6.132 +            retval["infected"] = False
   6.133 +            retval["virusname"] = "File is to big to be scanned."
   6.134 +            return retval
   6.135 +    
   6.136 +        fields = { 'up_file' : fileobject.read() }
   6.137 +    
   6.138 +        if (self.__remoteScanserverReachable == False) and ((self.__scanserverTimestamp + self.__SCANSERVER_RETRY_TIMEOUT) < time.time()):
   6.139 +            self.__remoteScanserverReachable = True
   6.140 +    
   6.141 +        if self.__remoteScanserverReachable:
   6.142 +            try:
   6.143 +                response = self.contactScanserver(self.__REMOTE_SCANSERVER_URL, fields)
   6.144 +                # We should catch socket.error here, but this does not work. Needs checking.
   6.145 +            except:
   6.146 +                self.__LOG.info("Remote scan server unreachable, using local scan server.")
   6.147 +                self.__LOG.debug("Exception: %s: %s" % (sys.exc_info()[0], sys.exc_info()[1]))
   6.148 +                self.__LOG.info("Next check for remote server in %s seconds." % (self.__SCANSERVER_RETRY_TIMEOUT))
   6.149 +                
   6.150 +                self.__remoteScanserverReachable = False
   6.151 +                self.__scanserverTimestamp = time.time()
   6.152 +    
   6.153 +                try:
   6.154 +                    response = self.contactScanserver(self.__LOCAL_SCANSERVER_URL, fields)
   6.155 +                except:
   6.156 +                    self.__LOG.error ("Connection to local scan server could not be established.")
   6.157 +                    self.__LOG.debug ("Exception: %s" % (sys.exc_info()[0]))
   6.158 +                    return retval
   6.159 +        else:
   6.160 +            try:
   6.161 +                response = self.contactScanserver(self.__LOCAL_SCANSERVER_URL, fields)
   6.162 +            except:
   6.163 +                self.__LOG.error ("Connection to local scan server could not be established.")
   6.164 +                self.__LOG.error ("Exception: %s" %(sys.exc_info()[0]))
   6.165 +                return retval
   6.166 +        
   6.167 +    
   6.168 +        if response.status == self.__STATUS_CODE_OK:
   6.169 +            retval["infected"] = False
   6.170 +        elif response.status == self.__STATUS_CODE_INFECTED:
   6.171 +            # Parse xml for info
   6.172 +            root = ET.fromstring(response.data)
   6.173 +            
   6.174 +            # this should be done in a more generic way
   6.175 +            retval["virusname"] = root[1][3][0].text
   6.176 +            retval["infected"] = True
   6.177 +        else:
   6.178 +            self.__LOG.error ("Connection error to scan server.")
   6.179 +    
   6.180 +        if (retval["infected"] == True):
   6.181 +            self.__LOG.error ("Virus found, denying access.")
   6.182 +        else:
   6.183 +            self.__LOG.debug ("No virus found.")
   6.184 +        
   6.185 +        return retval
   6.186 +
   6.187 +    
   6.188 +
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/osecfs	Tue Nov 04 16:28:40 2014 +0100
     7.3 @@ -0,0 +1,471 @@
     7.4 +#!/usr/bin/python
     7.5 +
     7.6 +# ------------------------------------------------------------
     7.7 +# opensecurity package file
     7.8 +#
     7.9 +# Autor: X-Net Services GmbH <office@x-net.at>
    7.10 +#
    7.11 +# Copyright 2013-2014 X-Net and AIT Austrian Institute of Technology
    7.12 +#
    7.13 +#
    7.14 +#     X-Net Technologies GmbH
    7.15 +#     Elisabethstrasse 1
    7.16 +#     4020 Linz
    7.17 +#     AUSTRIA
    7.18 +#     https://www.x-net.at
    7.19 +#
    7.20 +#     AIT Austrian Institute of Technology
    7.21 +#     Donau City Strasse 1
    7.22 +#     1220 Wien
    7.23 +#     AUSTRIA
    7.24 +#     http://www.ait.ac.at
    7.25 +#
    7.26 +#
    7.27 +# Licensed under the Apache License, Version 2.0 (the "License");
    7.28 +# you may not use this file except in compliance with the License.
    7.29 +# You may obtain a copy of the License at
    7.30 +#
    7.31 +#    http://www.apache.org/licenses/LICENSE-2.0
    7.32 +#
    7.33 +# Unless required by applicable law or agreed to in writing, software
    7.34 +# distributed under the License is distributed on an "AS IS" BASIS,
    7.35 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    7.36 +# See the License for the specific language governing permissions and
    7.37 +# limitations under the License.
    7.38 +# ------------------------------------------------------------
    7.39 +
    7.40 +
    7.41 +from fuse import Fuse
    7.42 +import fuse
    7.43 +
    7.44 +import ConfigParser
    7.45 +
    7.46 +import sys
    7.47 +
    7.48 +import logging
    7.49 +import os
    7.50 +import errno
    7.51 +import time
    7.52 +
    7.53 +from importlib import import_module
    7.54 +
    7.55 +
    7.56 +import subprocess
    7.57 +
    7.58 +import urllib3
    7.59 +import netifaces
    7.60 +import netaddr
    7.61 +import hashlib
    7.62 +
    7.63 +
    7.64 +sys.stderr = open('/var/log/osecfs_error.log', 'a+')
    7.65 +
    7.66 +
    7.67 +MINOPTS = { "Main" : ["Logfile", "LogLevel", "Mountpoint", "Rootpath", "ScannerPath", "ScannerModuleName", "ScannerClassName", "ScannerConfig", "ReadOnly"]}
    7.68 +
    7.69 +CONFIG_NOT_READABLE = "Configfile is not readable"
    7.70 +CONFIG_WRONG = "Something is wrong with the config"
    7.71 +CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
    7.72 +SCAN_WRONG_RETURN_VALUE = "The return Value of the malware scanner is wrong. Has to be an dictionary"
    7.73 +SCAN_RETURN_VALUE_KEY_MISSING = "The dictionary has to include key \"infected\" (True, False) and \"virusname\" (String)"
    7.74 +VIRUS_FOUND = "Virus found. Access denied"
    7.75 +NOTIFICATION_CRITICAL = "critical"
    7.76 +NOTIFICATION_INFO = "info"
    7.77 +LOG = None
    7.78 +MalwareScanner = None
    7.79 +STATUS_CODE_OK = 200
    7.80 +
    7.81 +SYSTEM_FILE_COMMAND = "file"
    7.82 +httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
    7.83 +
    7.84 +def checkMinimumOptions (config):
    7.85 +    for section, options in MINOPTS.iteritems ():
    7.86 +        for option in options:
    7.87 +            if (config.has_option(section, option) == False):
    7.88 +                print (CONFIG_MISSING % (section, option))
    7.89 +                exit (129)
    7.90 +
    7.91 +def printUsage ():
    7.92 +    print ("Usage:")
    7.93 +    print ("%s configfile mountpath ro/rw" % (sys.argv[0]))
    7.94 +    exit (128)
    7.95 +
    7.96 +def loadConfig ():
    7.97 +    print ("load config")
    7.98 +
    7.99 +    if (len (sys.argv) < 4):
   7.100 +        printUsage ()
   7.101 +
   7.102 +    configfile = sys.argv[1]
   7.103 +    config = ConfigParser.SafeConfigParser ()
   7.104 +
   7.105 +    if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
   7.106 +        print (CONFIG_NOT_READABLE)
   7.107 +        printUsage ()
   7.108 +
   7.109 +    try:
   7.110 +        config.read (sys.argv[1])
   7.111 +    except Exception, e:
   7.112 +        print (CONFIG_WRONG)
   7.113 +        print ("Error: %s" % (e))
   7.114 +
   7.115 +
   7.116 +    config.set("Main", "Mountpoint", sys.argv[2])
   7.117 +    if (sys.argv[3] == "rw"):
   7.118 +        config.set("Main", "ReadOnly", "false")
   7.119 +    else:
   7.120 +        config.set("Main", "ReadOnly", "true")
   7.121 +
   7.122 +    checkMinimumOptions (config)
   7.123 +
   7.124 +    return config
   7.125 +
   7.126 +def initLog (config):
   7.127 +    print ("init log")
   7.128 +
   7.129 +    global LOG
   7.130 +    logfile = config.get("Main", "Logfile")
   7.131 +    
   7.132 +    numeric_level = getattr(logging, config.get("Main", "LogLevel").upper(), None)
   7.133 +    if not isinstance(numeric_level, int):
   7.134 +        raise ValueError('Invalid log level: %s' % loglevel)
   7.135 +
   7.136 +    # ToDo move log level and maybe other things to config file
   7.137 +    logging.basicConfig(
   7.138 +                        level = numeric_level,
   7.139 +                        format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
   7.140 +                        datefmt = "%Y-%m-%d %H:%M:%S",
   7.141 +                        filename = logfile,
   7.142 +                        filemode = "a+",
   7.143 +    )
   7.144 +    LOG = logging.getLogger("fuse_main")
   7.145 +
   7.146 +
   7.147 +def fixPath (path):
   7.148 +    return ".%s" % (path)
   7.149 +
   7.150 +def rootPath (rootpath, path):
   7.151 +    return "%s%s" % (rootpath, path)
   7.152 +
   7.153 +def flag2mode (flags):
   7.154 +    md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
   7.155 +    m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
   7.156 +
   7.157 +    # windows sets append even if it would overwrite the whole file (seek 0)
   7.158 +    # so ignore append option
   7.159 +    #if flags | os.O_APPEND:
   7.160 +    #    m = m.replace('w', 'a', 1)
   7.161 +
   7.162 +    return m
   7.163 +
   7.164 +def scanFile (path, fileobject):
   7.165 +    LOG.debug ("Scan File \"%s\" with malware Scanner" %(path,) )
   7.166 +    return MalwareScanner.scanFile (path, fileobject)
   7.167 +
   7.168 +
   7.169 +def scanFileClamAV (path):
   7.170 +    infected = False
   7.171 +
   7.172 +    LOG.debug ("Scan File: %s" % (path))
   7.173 +
   7.174 +    result = pyclamav.scanfile (path)
   7.175 +    LOG.debug ("Result of file \"%s\": %s" % (path, result))
   7.176 +    if (result[0] != 0):
   7.177 +        infected = True
   7.178 +
   7.179 +    if (infected == True):
   7.180 +        LOG.error ("Virus found, deny Access %s" % (result,))
   7.181 +
   7.182 +    return infected
   7.183 +
   7.184 +def whitelistFile (path):
   7.185 +    whitelisted = False;
   7.186 +
   7.187 +    LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
   7.188 +    
   7.189 +    result = None
   7.190 +    try:
   7.191 +        result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
   7.192 +        # ToDo replace with real whitelist
   7.193 +        whitelisted = True
   7.194 +    except Exception as e:
   7.195 +        LOG.error ("Call returns with an error!")
   7.196 +        LOG.error (e)
   7.197 +
   7.198 +    LOG.debug ("Type: %s" %(result))
   7.199 +
   7.200 +    return whitelisted
   7.201 +
   7.202 +def sendDataToRest (urlpath, data):
   7.203 +    netifaces.ifaddresses("eth0")[2][0]["addr"]
   7.204 +    
   7.205 +    # Get first address in network (0 = network ip -> 192.168.0.0)
   7.206 +    remote_ip = netaddr.IPNetwork("%s/%s" %(netifaces.ifaddresses("eth0")[2][0]["addr"], netifaces.ifaddresses("eth0")[2][0]["netmask"]))[1]
   7.207 +    
   7.208 +    url = ("http://%s:8090//%s" %(remote_ip, urlpath))
   7.209 +
   7.210 +    LOG.debug ("Send data to \"%s\"" %(url, ))
   7.211 +    LOG.debug ("Data: %s" %(data, ))
   7.212 +    
   7.213 +    try:
   7.214 +        response = httpPool.request_encode_body("POST", url, fields=data, retries=0)
   7.215 +    except Exception, e:
   7.216 +        LOG.error("Remote host not reachable")
   7.217 +        LOG.error ("Exception: %s" %(e,))
   7.218 +        return
   7.219 +    
   7.220 +    if response.status == STATUS_CODE_OK:
   7.221 +        LOG.info("Data sent successfully to rest server")
   7.222 +        return True
   7.223 +    else:
   7.224 +        LOG.error("Server returned errorcode: %s" %(response.status,))
   7.225 +        return False
   7.226 +    
   7.227 +
   7.228 +def sendNotification (type, message):
   7.229 +    data = {"msgtype" : type, "text" : message}
   7.230 +    
   7.231 +    if (type == "information"):
   7.232 +        sendDataToRest ("message", data)
   7.233 +    else:
   7.234 +        sendDataToRest ("notification", data)
   7.235 +
   7.236 +def sendReadOnlyNotification():
   7.237 +    sendNotification("critical", "Filesystem is in read only mode. If you want to export files please initialize an encrypted filesystem.")
   7.238 +    
   7.239 +def sendLogNotPossibleNotification():
   7.240 +    sendNotification("critical", "Send log entry to opensecurity rest server failed.")
   7.241 +    
   7.242 +def sendFileLog(filename, filesize, filehash, hashtype):
   7.243 +    data = {"filename" : filename, "filesize" : "%s" %(filesize,), "filehash" : filehash, "hashtype" : hashtype}
   7.244 +    retval = sendDataToRest ("log", data)
   7.245 +    if (retval == False):
   7.246 +        sendLogNotPossibleNotification()
   7.247 +
   7.248 +def calcMD5 (path, block_size=256*128, hr=True):
   7.249 +    md5 = hashlib.md5()
   7.250 +    with open(path,'rb') as f: 
   7.251 +        for chunk in iter(lambda: f.read(block_size), b''): 
   7.252 +             md5.update(chunk)
   7.253 +    if hr:
   7.254 +        return md5.hexdigest()
   7.255 +    return md5.digest()
   7.256 +
   7.257 +class OsecFS (Fuse):
   7.258 +
   7.259 +    __rootpath = None
   7.260 +
   7.261 +    # default fuse init
   7.262 +    def __init__(self, rootpath, *args, **kw):
   7.263 +        self.__rootpath = rootpath
   7.264 +        Fuse.__init__ (self, *args, **kw)
   7.265 +        LOG.debug ("Init complete.")
   7.266 +        sendNotification("information", "Filesystem successfully mounted.")
   7.267 +
   7.268 +    # defines that our working directory will be the __rootpath
   7.269 +    def fsinit(self):
   7.270 +        os.chdir (self.__rootpath)
   7.271 +
   7.272 +    def getattr(self, path):
   7.273 +        LOG.debug ("*** getattr (%s)" % (fixPath (path)))
   7.274 +        return os.lstat (fixPath (path));
   7.275 +
   7.276 +    def getdir(self, path):
   7.277 +        LOG.debug ("*** getdir (%s)" % (path));
   7.278 +        return os.listdir (fixPath (path))
   7.279 +
   7.280 +    def readdir(self, path, offset):
   7.281 +        LOG.debug ("*** readdir (%s %s)" % (path, offset));
   7.282 +        for e in os.listdir (fixPath (path)):
   7.283 +            yield fuse.Direntry(e)
   7.284 +
   7.285 +    def chmod (self, path, mode):
   7.286 +        LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
   7.287 +        if (config.get("Main", "ReadOnly") == "true"):
   7.288 +            sendReadOnlyNotification()
   7.289 +            return -errno.EACCES
   7.290 +        os.chmod (fixPath (path), mode)
   7.291 +
   7.292 +    def chown (self, path, uid, gid):
   7.293 +        LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
   7.294 +        if (config.get("Main", "ReadOnly") == "true"):
   7.295 +            sendReadOnlyNotification()
   7.296 +            return -errno.EACCES
   7.297 +        os.chown (fixPath (path), uid, gid)
   7.298 +
   7.299 +    def link (self, targetPath, linkPath):
   7.300 +        LOG.debug ("*** link %s %s" % (targetPath, linkPath))
   7.301 +        if (config.get("Main", "ReadOnly") == "true"):
   7.302 +            sendReadOnlyNotification()
   7.303 +            return -errno.EACCES
   7.304 +        os.link (fixPath (targetPath), fixPath (linkPath))
   7.305 +
   7.306 +    def mkdir (self, path, mode):
   7.307 +        LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
   7.308 +        if (config.get("Main", "ReadOnly") == "true"):
   7.309 +            sendReadOnlyNotification()
   7.310 +            return -errno.EACCES
   7.311 +        os.mkdir (fixPath (path), mode)
   7.312 +
   7.313 +    def mknod (self, path, mode, dev):
   7.314 +        LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
   7.315 +        if (config.get("Main", "ReadOnly") == "true"):
   7.316 +            sendReadOnlyNotification()
   7.317 +            return -errno.EACCES
   7.318 +        os.mknod (fixPath (path), mode, dev)
   7.319 +
   7.320 +    # to implement virus scan
   7.321 +    def open (self, path, flags):
   7.322 +        LOG.debug ("*** open %s %s" % (path, oct (flags)))
   7.323 +        self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
   7.324 +        self.written = False
   7.325 +        self.fd = self.file.fileno ()
   7.326 +        
   7.327 +        LOG.debug(self.__rootpath)
   7.328 +        LOG.debug(path)
   7.329 +        
   7.330 +        retval = scanFile (rootPath(self.__rootpath, path), self.file)
   7.331 +        
   7.332 +        #if type(retval) is not dict:
   7.333 +        if (isinstance(retval, dict) == False):
   7.334 +            LOG.error(SCAN_WRONG_RETURN_VALUE)
   7.335 +            self.file.close ()
   7.336 +            return -errno.EACCES
   7.337 +        
   7.338 +        if ((retval.has_key("infected") == False) or (retval.has_key("virusname") == False)):
   7.339 +            LOG.error(SCAN_RETURN_VALUE_KEY_MISSING)
   7.340 +            self.file.close ()
   7.341 +            return -errno.EACCES
   7.342 +            
   7.343 +        
   7.344 +        if (retval.get("infected") == True):
   7.345 +            self.file.close ()
   7.346 +            sendNotification(NOTIFICATION_CRITICAL, "%s\nFile: %s\nVirus: %s" %(VIRUS_FOUND, path, retval.get("virusname")))
   7.347 +            LOG.error("%s" %(VIRUS_FOUND,))
   7.348 +            LOG.error("Virus: %s" %(retval.get("virusname"),))
   7.349 +            return -errno.EACCES
   7.350 +        
   7.351 +        whitelisted = whitelistFile (rootPath(self.__rootpath, path))
   7.352 +        if (whitelisted == False):
   7.353 +            self.file.close ()
   7.354 +            sendNotification(NOTIFICATION_CRITICAL, "File not in whitelist. Access denied.")
   7.355 +            return -errno.EACCES
   7.356 +
   7.357 +    def read (self, path, length, offset):
   7.358 +        LOG.debug ("*** read %s %s %s" % (path, length, offset))
   7.359 +        self.file.seek (offset)
   7.360 +        return self.file.read (length)
   7.361 +
   7.362 +    def readlink (self, path):
   7.363 +        LOG.debug ("*** readlink %s" % (path))
   7.364 +        return os.readlink (fixPath (path))
   7.365 +
   7.366 +    def release (self, path, flags):
   7.367 +        LOG.debug ("*** release %s %s" % (path, oct (flags)))
   7.368 +        self.file.flush()
   7.369 +        os.fsync(self.file.fileno())
   7.370 +        self.file.close ()
   7.371 +        
   7.372 +        if (self.written == True):
   7.373 +            hashsum = calcMD5(fixPath(path))
   7.374 +            filesize = os.path.getsize(fixPath(path))
   7.375 +            sendFileLog(path, filesize, hashsum, "md5")
   7.376 +        
   7.377 +
   7.378 +    def rename (self, oldPath, newPath):
   7.379 +        LOG.debug ("*** rename %s %s %s" % (oldPath, newPath, config.get("Main", "ReadOnly")))
   7.380 +        if (config.get("Main", "ReadOnly") == "true"):
   7.381 +            sendReadOnlyNotification()
   7.382 +            return -errno.EACCES
   7.383 +        os.rename (fixPath (oldPath), fixPath (newPath))
   7.384 +
   7.385 +    def rmdir (self, path):
   7.386 +        LOG.debug ("*** rmdir %s %s" % (path, config.get("Main", "ReadOnly")))
   7.387 +        if (config.get("Main", "ReadOnly") == "true"):
   7.388 +            sendReadOnlyNotification()
   7.389 +            return -errno.EACCES
   7.390 +        os.rmdir (fixPath (path))
   7.391 +
   7.392 +    def statfs (self):
   7.393 +        LOG.debug ("*** statfs")
   7.394 +        return os.statvfs(".")
   7.395 +
   7.396 +    def symlink (self, targetPath, linkPath):
   7.397 +        LOG.debug ("*** symlink %s %s %s" % (targetPath, linkPath, config.get("Main", "ReadOnly")))
   7.398 +        if (config.get("Main", "ReadOnly") == "true"):
   7.399 +            sendReadOnlyNotification()
   7.400 +            return -errno.EACCES
   7.401 +        os.symlink (fixPath (targetPath), fixPath (linkPath))
   7.402 +
   7.403 +    def truncate (self, path, length):
   7.404 +        LOG.debug ("*** truncate %s %s %s" % (path, length, config.get("Main", "ReadOnly")))
   7.405 +        if (config.get("Main", "ReadOnly") == "true"):
   7.406 +            sendReadOnlyNotification()
   7.407 +            return -errno.EACCES
   7.408 +        f = open (fixPath (path), "w+")
   7.409 +        f.truncate (length)
   7.410 +        f.close ()
   7.411 +
   7.412 +    def unlink (self, path):
   7.413 +        LOG.debug ("*** unlink %s %s" % (path, config.get("Main", "ReadOnly")))
   7.414 +        if (config.get("Main", "ReadOnly") == "true"):
   7.415 +            sendReadOnlyNotification()
   7.416 +            return -errno.EACCES
   7.417 +        os.unlink (fixPath (path))
   7.418 +
   7.419 +    def utime (self, path, times):
   7.420 +        LOG.debug ("*** utime %s %s" % (path, times))
   7.421 +        os.utime (fixPath (path), times)
   7.422 +
   7.423 +    def write (self, path, buf, offset):
   7.424 +        #LOG.debug ("*** write %s %s %s %s" % (path, buf, offset, config.get("Main", "ReadOnly")))
   7.425 +        LOG.debug ("*** write %s %s %s %s" % (path, "filecontent", offset, config.get("Main", "ReadOnly")))
   7.426 +        if (config.get("Main", "ReadOnly") == "true"):
   7.427 +            self.file.close()
   7.428 +            sendReadOnlyNotification()
   7.429 +            return -errno.EACCES
   7.430 +        self.file.seek (offset)
   7.431 +        self.file.write (buf)
   7.432 +        self.written = True
   7.433 +        return len (buf)
   7.434 +
   7.435 +    def access (self, path, mode):
   7.436 +        LOG.debug ("*** access %s %s" % (path, oct (mode)))
   7.437 +        if not os.access (fixPath (path), mode):
   7.438 +            return -errno.EACCES
   7.439 +
   7.440 +    def create (self, path, flags, mode):
   7.441 +        LOG.debug ("*** create %s %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags), config.get("Main", "ReadOnly")))
   7.442 +        if (config.get("Main", "ReadOnly") == "true"):
   7.443 +            sendReadOnlyNotification()
   7.444 +            return -errno.EACCES
   7.445 +
   7.446 +        self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode(flags))
   7.447 +        self.written = True
   7.448 +        self.fd = self.file.fileno ()
   7.449 +
   7.450 +
   7.451 +if __name__ == "__main__":
   7.452 +    # Set api version
   7.453 +    fuse.fuse_python_api = (0, 2)
   7.454 +    fuse.feature_assert ('stateful_files', 'has_init')
   7.455 +
   7.456 +    config = loadConfig ()
   7.457 +    initLog (config)
   7.458 +    
   7.459 +    #sendNotification("Info", "OsecFS started")
   7.460 +    
   7.461 +    # Import the Malware Scanner
   7.462 +    sys.path.append(config.get("Main", "ScannerPath"))
   7.463 +    
   7.464 +    MalwareModule = import_module(config.get("Main", "ScannerModuleName"))
   7.465 +    MalwareClass = getattr(MalwareModule, config.get("Main", "ScannerClassName"))
   7.466 +    
   7.467 +    MalwareScanner = MalwareClass (config.get("Main", "ScannerConfig"));
   7.468 +    
   7.469 +    osecfs = OsecFS (config.get ("Main", "Rootpath"))
   7.470 +    osecfs.flags = 0
   7.471 +    osecfs.multithreaded = 0
   7.472 +
   7.473 +    fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
   7.474 +    osecfs.main (fuse_args)
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/osecfs-package	Tue Nov 04 16:28:40 2014 +0100
     8.3 @@ -0,0 +1,31 @@
     8.4 +### Commented entries have reasonable defaults.
     8.5 +### Uncomment to edit them.
     8.6 +# Source: <source package name; defaults to package name>
     8.7 +Section: misc
     8.8 +Priority: optional
     8.9 +# Homepage: <enter URL here; no default>
    8.10 +Standards-Version: 3.9.2
    8.11 +
    8.12 +Package: osecfs
    8.13 +Version: 0.0.26
    8.14 +Maintainer: ft <ft@x-net.at>
    8.15 +# Pre-Depends: <comma-separated list of packages>
    8.16 +Depends: python,python-requests,python-fuse,python-clamav,python-pyclamav,clamav,python-urllib3,python-netifaces,python-netaddr
    8.17 +# Recommends: <comma-separated list of packages>
    8.18 +# Suggests: <comma-separated list of packages>
    8.19 +# Provides: <comma-separated list of packages>
    8.20 +# Replaces: <comma-separated list of packages>
    8.21 +Architecture: all
    8.22 +# Copyright: <copyright file; defaults to GPL2>
    8.23 +# Changelog: <changelog file; defaults to a generic changelog>
    8.24 +# Readme: <README.Debian file; defaults to a generic one>
    8.25 +# Extra-Files: <comma-separated list of additional files for the doc directory>
    8.26 +Files: osecfs				/usr/bin/
    8.27 + ikarusscanner/IkarusScanner.py		/usr/bin/
    8.28 + clamavscanner/ClamAVScanner.py		/usr/bin/
    8.29 + osecfs_usb.cfg				/etc/osecfs/
    8.30 + osecfs_downloads.cfg			/etc/osecfs/
    8.31 + ClamAVScanner.cfg			/etc/osecfs/
    8.32 + IkarusScanner.cfg			/etc/osecfs/
    8.33 +Description: Mirror Filesystem for Open Security 
    8.34 + An Mirror FS which makes virus scanning whitelistening and other things
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/osecfs-package.conf	Tue Nov 04 16:28:40 2014 +0100
     9.3 @@ -0,0 +1,2 @@
     9.4 +# Original main-package.conf file. 
     9.5 +# Do not touch it!! It belongs to dpkg.
    10.1 Binary file osecfs_0.0.10_all.deb has changed
    11.1 Binary file osecfs_0.0.11_all.deb has changed
    12.1 Binary file osecfs_0.0.12_all.deb has changed
    13.1 Binary file osecfs_0.0.13_all.deb has changed
    14.1 Binary file osecfs_0.0.14_all.deb has changed
    15.1 Binary file osecfs_0.0.15_all.deb has changed
    16.1 Binary file osecfs_0.0.16_all.deb has changed
    17.1 Binary file osecfs_0.0.17_all.deb has changed
    18.1 Binary file osecfs_0.0.18_all.deb has changed
    19.1 Binary file osecfs_0.0.19_all.deb has changed
    20.1 Binary file osecfs_0.0.1_all.deb has changed
    21.1 Binary file osecfs_0.0.20_all.deb has changed
    22.1 Binary file osecfs_0.0.21_all.deb has changed
    23.1 Binary file osecfs_0.0.22_all.deb has changed
    24.1 Binary file osecfs_0.0.23_all.deb has changed
    25.1 Binary file osecfs_0.0.24_all.deb has changed
    26.1 Binary file osecfs_0.0.25_all.deb has changed
    27.1 Binary file osecfs_0.0.26_all.deb has changed
    28.1 Binary file osecfs_0.0.2_all.deb has changed
    29.1 Binary file osecfs_0.0.3_all.deb has changed
    30.1 Binary file osecfs_0.0.4_all.deb has changed
    31.1 Binary file osecfs_0.0.5_all.deb has changed
    32.1 Binary file osecfs_0.0.6_all.deb has changed
    33.1 Binary file osecfs_0.0.7_all.deb has changed
    34.1 Binary file osecfs_0.0.8_all.deb has changed
    35.1 Binary file osecfs_0.0.9_all.deb has changed
    36.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.2 +++ b/osecfs_downloads.cfg	Tue Nov 04 16:28:40 2014 +0100
    36.3 @@ -0,0 +1,32 @@
    36.4 +[Main]
    36.5 +# make sure this file is writeable
    36.6 +Logfile: /var/log/osecfs_downloads.log
    36.7 +
    36.8 +# DEBUG, INFO, WARNING, ERROR, CRITICAL
    36.9 +LogLevel: debug
   36.10 +
   36.11 +# where the files really are on the filesystem 
   36.12 +Rootpath: /home/osecuser/Downloads
   36.13 +
   36.14 +
   36.15 +
   36.16 +
   36.17 +# path to scanner class
   36.18 +#ScannerPath: /usr/bin/ikarusscanner/
   36.19 +
   36.20 +# scanner module name
   36.21 +#ScannerModuleName: IkarusScanner
   36.22 +#ScannerClassName: IkarusScanner
   36.23 +
   36.24 +# config file for scanner (path will be in the constructor)
   36.25 +#ScannerConfig: /etc/osecfs/IkarusScanner.cfg
   36.26 +
   36.27 +# path to scanner class
   36.28 +ScannerPath: /usr/bin/clamavscanner/
   36.29 +
   36.30 +# scanner module name
   36.31 +ScannerModuleName: ClamAVScanner
   36.32 +ScannerClassName: ClamAVScanner
   36.33 +
   36.34 +# config file for scanner (path will be in the constructor)
   36.35 +ScannerConfig: /etc/osecfs/ClamAVScanner.cfg
    37.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    37.2 +++ b/osecfs_usb.cfg	Tue Nov 04 16:28:40 2014 +0100
    37.3 @@ -0,0 +1,32 @@
    37.4 +[Main]
    37.5 +# make sure this file is writeable
    37.6 +Logfile: /var/log/osecfs_usb.log
    37.7 +
    37.8 +# DEBUG, INFO, WARNING, ERROR, CRITICAL
    37.9 +LogLevel: debug
   37.10 +
   37.11 +# where the files really are on the filesystem 
   37.12 +Rootpath: /media/usb0
   37.13 +
   37.14 +
   37.15 +
   37.16 +
   37.17 +# path to scanner class
   37.18 +#ScannerPath: /usr/bin/ikarusscanner/
   37.19 +
   37.20 +# scanner module name
   37.21 +#ScannerModuleName: IkarusScanner
   37.22 +#ScannerClassName: IkarusScanner
   37.23 +
   37.24 +# config file for scanner (path will be in the constructor)
   37.25 +#ScannerConfig: /etc/osecfs/IkarusScanner.cfg
   37.26 +
   37.27 +# path to scanner class
   37.28 +ScannerPath: /usr/bin/clamavscanner/
   37.29 +
   37.30 +# scanner module name
   37.31 +ScannerModuleName: ClamAVScanner
   37.32 +ScannerClassName: ClamAVScanner
   37.33 +
   37.34 +# config file for scanner (path will be in the constructor)
   37.35 +ScannerConfig: /etc/osecfs/ClamAVScanner.cfg
    38.1 --- a/src/OsecFS.py	Wed Nov 27 15:37:26 2013 +0100
    38.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.3 @@ -1,327 +0,0 @@
    38.4 -#!/usr/bin/python
    38.5 -
    38.6 -from fuse import Fuse
    38.7 -import fuse
    38.8 -
    38.9 -import ConfigParser
   38.10 -
   38.11 -import sys
   38.12 -
   38.13 -import logging
   38.14 -import os
   38.15 -import errno
   38.16 -
   38.17 -# ToDo replace with ikarus
   38.18 -#import pyclamav
   38.19 -import subprocess
   38.20 -
   38.21 -import requests
   38.22 -
   38.23 -
   38.24 -MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath", "LocalScanserverURL", "RemoteScanserverURL"]}
   38.25 -
   38.26 -CONFIG_NOT_READABLE = "Configfile is not readable"
   38.27 -CONFIG_WRONG = "Something is wrong with the config"
   38.28 -CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
   38.29 -LOG = None
   38.30 -LOCAL_SCANSERVER_URL = ""
   38.31 -REMOTE_SCANSERVER_URL = ""
   38.32 -STATUS_CODE_OK = 200
   38.33 -STATUS_CODE_INFECTED = 210
   38.34 -STATUS_CODE_NOT_FOUND = 404
   38.35 -
   38.36 -SYSTEM_FILE_COMMAND = "file"
   38.37 -
   38.38 -
   38.39 -def checkMinimumOptions (config):
   38.40 -    for section, options in MINOPTS.iteritems ():
   38.41 -        for option in options:
   38.42 -            if (config.has_option(section, option) == False):
   38.43 -                print (CONFIG_MISSING % (section, option))
   38.44 -                exit (129)
   38.45 -
   38.46 -def printUsage ():
   38.47 -    print ("Usage:")
   38.48 -    print ("%s configfile" % (sys.argv[0]))
   38.49 -    exit (128)
   38.50 -
   38.51 -def loadConfig ():
   38.52 -    print ("load config")
   38.53 -
   38.54 -    if (len (sys.argv) < 2):
   38.55 -        printUsage ()
   38.56 -
   38.57 -    configfile = sys.argv[1]
   38.58 -    config = ConfigParser.SafeConfigParser ()
   38.59 -
   38.60 -    if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
   38.61 -        print (CONFIG_NOT_READABLE)
   38.62 -        printUsage ()
   38.63 -
   38.64 -    try:
   38.65 -        config.read (sys.argv[1])
   38.66 -    except Exception, e:
   38.67 -        print (CONFIG_WRONG)
   38.68 -        print ("Error: %s" % (e))
   38.69 -
   38.70 -    checkMinimumOptions (config)
   38.71 -
   38.72 -    return config
   38.73 -
   38.74 -def initLog (config):
   38.75 -    print ("init log")
   38.76 -
   38.77 -    global LOG
   38.78 -    logfile = config.get("Main", "Logfile")
   38.79 -
   38.80 -    # ToDo move log level and maybe other things to config file
   38.81 -    logging.basicConfig(
   38.82 -                        level = logging.DEBUG,
   38.83 -                        format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
   38.84 -                        datefmt = "%Y-%m-%d %H:%M:%S",
   38.85 -                        filename = logfile,
   38.86 -                        filemode = "a+",
   38.87 -    )
   38.88 -    LOG = logging.getLogger("fuse_main")
   38.89 -
   38.90 -
   38.91 -def fixPath (path):
   38.92 -    return ".%s" % (path)
   38.93 -
   38.94 -def rootPath (rootpath, path):
   38.95 -    return "%s%s" % (rootpath, path)
   38.96 -
   38.97 -def flag2mode (flags):
   38.98 -    md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
   38.99 -    m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
  38.100 -
  38.101 -    if flags | os.O_APPEND:
  38.102 -        m = m.replace('w', 'a', 1)
  38.103 -
  38.104 -    return m
  38.105 -
  38.106 -def scanFileIkarus (path, fileobject):
  38.107 -    infected = False
  38.108 -    LOG.debug ("Scan File: %s" % (path))
  38.109 -    
  38.110 -    files = {'up_file': (path, fileobject)}
  38.111 -    
  38.112 -    try:
  38.113 -        #TODO: change to remote server
  38.114 -        r = requests.post(LOCAL_SCANSERVER_URL, files=files)
  38.115 -    except requests.exceptions.ConnectionError:
  38.116 -        #LOG.info("Remote scan server unreachable, using local scan server.")
  38.117 -
  38.118 -        # TODO:
  38.119 -        # Here the local scan server should be contacted.
  38.120 -        # The requests package does not upload content in the second post request,
  38.121 -        # so no fallback server can be used right now (bug?)
  38.122 -        # I did not a find a solution yet, maybe another http package has to be used.
  38.123 -        # Disabled for now.
  38.124 -
  38.125 -        #try:
  38.126 -        #    r = requests.post(LOCAL_SCANSERVER_URL, files=files)
  38.127 -        #except requests.exceptions.ConnectionError:
  38.128 -        #    return 2
  38.129 -        LOG.error ("Connection to scan server could not be established.")
  38.130 -        return False
  38.131 -
  38.132 -    if r.status_code == STATUS_CODE_OK:
  38.133 -        infected = False
  38.134 -    elif r.status_code == STATUS_CODE_INFECTED:
  38.135 -        # Parse xml for info if desired
  38.136 -        #contentXML = r.content
  38.137 -        #root = ET.fromstring(contentXML)
  38.138 -        #status = root[1][2].text
  38.139 -        infected = True
  38.140 -    else:
  38.141 -        LOG.error ("Connection error to scan server.")
  38.142 -
  38.143 -    if (infected == True):
  38.144 -        LOG.error ("Virus found, denying access.")
  38.145 -    else:
  38.146 -        LOG.debug ("No virus found.")
  38.147 -
  38.148 -    return infected
  38.149 -
  38.150 -def scanFileClamAV (path):
  38.151 -    infected = False
  38.152 -
  38.153 -    LOG.debug ("Scan File: %s" % (path))
  38.154 -
  38.155 -    # ToDo implement ikarus
  38.156 -    result = pyclamav.scanfile (path)
  38.157 -    LOG.debug ("Result of file \"%s\": %s" % (path, result))
  38.158 -    if (result[0] != 0):
  38.159 -        infected = True
  38.160 -
  38.161 -    if (infected == True):
  38.162 -        LOG.error ("Virus found, deny Access %s" % (result,))
  38.163 -
  38.164 -    return infected
  38.165 -
  38.166 -def whitelistFile (path):
  38.167 -    whitelisted = False;
  38.168 -
  38.169 -    LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
  38.170 -    
  38.171 -    result = None
  38.172 -    try:
  38.173 -        result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
  38.174 -        # ToDo replace with real whitelist
  38.175 -        whitelisted = True
  38.176 -    except Exception as e:
  38.177 -        LOG.error ("Call returns with an error!")
  38.178 -        LOG.error (e)
  38.179 -
  38.180 -    LOG.debug ("Type: %s" %(result))
  38.181 -
  38.182 -    return whitelisted
  38.183 -
  38.184 -class OsecFS (Fuse):
  38.185 -
  38.186 -    __rootpath = None
  38.187 -
  38.188 -    # default fuse init
  38.189 -    def __init__(self, rootpath, *args, **kw):
  38.190 -        self.__rootpath = rootpath
  38.191 -        Fuse.__init__ (self, *args, **kw)
  38.192 -        LOG.debug ("Init complete.")
  38.193 -
  38.194 -    # defines that our working directory will be the __rootpath
  38.195 -    def fsinit(self):
  38.196 -        os.chdir (self.__rootpath)
  38.197 -
  38.198 -    def getattr(self, path):
  38.199 -        LOG.debug ("*** getattr (%s)" % (fixPath (path)))
  38.200 -        return os.lstat (fixPath (path));
  38.201 -
  38.202 -    def getdir(self, path):
  38.203 -        LOG.debug ("*** getdir (%s)" % (path));
  38.204 -        return os.listdir (fixPath (path))
  38.205 -
  38.206 -    def readdir(self, path, offset):
  38.207 -        LOG.debug ("*** readdir (%s %s)" % (path, offset));
  38.208 -        for e in os.listdir (fixPath (path)):
  38.209 -            yield fuse.Direntry(e)
  38.210 -
  38.211 -    def chmod (self, path, mode):
  38.212 -        LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
  38.213 -        os.chmod (fixPath (path), mode)
  38.214 -
  38.215 -    def chown (self, path, uid, gid):
  38.216 -        LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
  38.217 -        os.chown (fixPath (path), uid, gid)
  38.218 -
  38.219 -    def link (self, targetPath, linkPath):
  38.220 -        LOG.debug ("*** link %s %s" % (targetPath, linkPath))
  38.221 -        os.link (fixPath (targetPath), fixPath (linkPath))
  38.222 -
  38.223 -    def mkdir (self, path, mode):
  38.224 -        LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
  38.225 -        os.mkdir (fixPath (path), mode)
  38.226 -
  38.227 -    def mknod (self, path, mode, dev):
  38.228 -        LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
  38.229 -        os.mknod (fixPath (path), mode, dev)
  38.230 -
  38.231 -    # to implement virus scan
  38.232 -    def open (self, path, flags):
  38.233 -        LOG.debug ("*** open %s %s" % (path, oct (flags)))
  38.234 -        self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
  38.235 -        self.fd = self.file.fileno ()
  38.236 -
  38.237 -        infected = scanFileIkarus (rootPath(self.__rootpath, path), self.file)
  38.238 -        #infected = scanFileClamAV (rootPath(self.__rootpath, path))
  38.239 -        if (infected == True):
  38.240 -            self.file.close ()
  38.241 -            return -errno.EACCES
  38.242 -        
  38.243 -        whitelisted = whitelistFile (rootPath(self.__rootpath, path))
  38.244 -        if (whitelisted == False):
  38.245 -            self.file.close ()
  38.246 -            return -errno.EACCES
  38.247 -
  38.248 -    def read (self, path, length, offset):
  38.249 -        LOG.debug ("*** read %s %s %s" % (path, length, offset))
  38.250 -        self.file.seek (offset)
  38.251 -        return self.file.read (length)
  38.252 -
  38.253 -    def readlink (self, path):
  38.254 -        LOG.debug ("*** readlink %s" % (path))
  38.255 -        return os.readlink (fixPath (path))
  38.256 -
  38.257 -    def release (self, path, flags):
  38.258 -        LOG.debug ("*** release %s %s" % (path, oct (flags)))
  38.259 -        self.file.close ()
  38.260 -
  38.261 -    def rename (self, oldPath, newPath):
  38.262 -        LOG.debug ("*** rename %s %s" % (oldPath, newPath))
  38.263 -        os.rename (fixPath (oldPath), fixPath (newPath))
  38.264 -
  38.265 -    def rmdir (self, path):
  38.266 -        LOG.debug ("*** rmdir %s" % (path))
  38.267 -        os.rmdir (fixPath (path))
  38.268 -
  38.269 -    def statfs (self):
  38.270 -        LOG.debug ("*** statfs")
  38.271 -        return os.statvfs(".")
  38.272 -
  38.273 -    def symlink (self, targetPath, linkPath):
  38.274 -        LOG.debug ("*** symlink %s %s" % (targetPath, linkPath))
  38.275 -        os.symlink (fixPath (targetPath), fixPath (linkPath))
  38.276 -
  38.277 -    def truncate (self, path, length):
  38.278 -        LOG.debug ("*** truncate %s %s" % (path, length))
  38.279 -        f = open (fixPath (path), "a")
  38.280 -        f.truncate (length)
  38.281 -        f.close ()
  38.282 -
  38.283 -    def unlink (self, path):
  38.284 -        LOG.debug ("*** unlink %s" % (path))
  38.285 -        os.unlink (fixPath (path))
  38.286 -
  38.287 -    def utime (self, path, times):
  38.288 -        LOG.debug ("*** utime %s %s" % (path, times))
  38.289 -        os.utime (fixPath (path), times)
  38.290 -
  38.291 -    def write (self, path, buf, offset):
  38.292 -        LOG.debug ("*** write %s %s %s" % (path, buf, offset))
  38.293 -        self.file.seek (offset)
  38.294 -        self.file.write (buf)
  38.295 -        return len (buf)
  38.296 -
  38.297 -    def access (self, path, mode):
  38.298 -        LOG.debug ("*** access %s %s" % (path, oct (mode)))
  38.299 -        if not os.access (fixPath (path), mode):
  38.300 -            return -errno.EACCES
  38.301 -
  38.302 -    def create (self, path, flags, mode):
  38.303 -        LOG.debug ("*** create %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags)))
  38.304 -        self.file = os.fdopen (os.open (fixPath (path), flags, mode), flag2mode (flags))
  38.305 -        self.fd = self.file.fileno ()
  38.306 -
  38.307 -
  38.308 -if __name__ == "__main__":
  38.309 -    # Set api version
  38.310 -    fuse.fuse_python_api = (0, 2)
  38.311 -    fuse.feature_assert ('stateful_files', 'has_init')
  38.312 -
  38.313 -    config = loadConfig ()
  38.314 -    initLog (config)
  38.315 -
  38.316 -    LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
  38.317 -    REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
  38.318 -
  38.319 -    osecfs = OsecFS (config.get ("Main", "Rootpath"))
  38.320 -    osecfs.flags = 0
  38.321 -    osecfs.multithreaded = 0
  38.322 -
  38.323 -    # osecfs.parser.add_option (mountopt=config.get("Main", "Mountpoint"),
  38.324 -    #                      metavar="PATH",
  38.325 -    #                      default=config.get("Main", "Rootpath"),
  38.326 -    #                      help="mirror filesystem from under PATH [default: %default]")
  38.327 -    # osecfs.parse(values=osecfs, errex=1)
  38.328 -
  38.329 -    fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
  38.330 -    osecfs.main (fuse_args)