14 # ToDo replace with ikarus
21 MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath", "LocalScanserverURL", "RemoteScanserverURL", "ReadOnly"]}
23 CONFIG_NOT_READABLE = "Configfile is not readable"
24 CONFIG_WRONG = "Something is wrong with the config"
25 CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
27 LOCAL_SCANSERVER_URL = ""
28 REMOTE_SCANSERVER_URL = ""
30 STATUS_CODE_INFECTED = 210
31 STATUS_CODE_NOT_FOUND = 404
33 SYSTEM_FILE_COMMAND = "file"
36 def checkMinimumOptions (config):
37 for section, options in MINOPTS.iteritems ():
38 for option in options:
39 if (config.has_option(section, option) == False):
40 print (CONFIG_MISSING % (section, option))
45 print ("%s configfile mountpath ro/rw" % (sys.argv[0]))
51 if (len (sys.argv) < 4):
54 configfile = sys.argv[1]
55 config = ConfigParser.SafeConfigParser ()
57 if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
58 print (CONFIG_NOT_READABLE)
62 config.read (sys.argv[1])
65 print ("Error: %s" % (e))
68 config.set("Main", "Mountpoint", sys.argv[2])
69 if (sys.argv[3] == "rw"):
70 config.set("Main", "ReadOnly", "false")
72 config.set("Main", "ReadOnly", "true")
74 checkMinimumOptions (config)
82 logfile = config.get("Main", "Logfile")
84 # ToDo move log level and maybe other things to config file
86 level = logging.DEBUG,
87 format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
88 datefmt = "%Y-%m-%d %H:%M:%S",
92 LOG = logging.getLogger("fuse_main")
98 def rootPath (rootpath, path):
99 return "%s%s" % (rootpath, path)
101 def flag2mode (flags):
102 md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
103 m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
105 if flags | os.O_APPEND:
106 m = m.replace('w', 'a', 1)
110 def scanFileIkarus (path, fileobject):
112 LOG.debug ("Scan File: %s" % (path))
114 files = {'up_file': (path, fileobject)}
117 #TODO: change to remote server
118 r = requests.post(LOCAL_SCANSERVER_URL, files=files)
119 except requests.exceptions.ConnectionError:
120 #LOG.info("Remote scan server unreachable, using local scan server.")
123 # Here the local scan server should be contacted.
124 # The requests package does not upload content in the second post request,
125 # so no fallback server can be used right now (bug?)
126 # I did not a find a solution yet, maybe another http package has to be used.
130 # r = requests.post(LOCAL_SCANSERVER_URL, files=files)
131 #except requests.exceptions.ConnectionError:
133 LOG.error ("Connection to scan server could not be established.")
136 LOG.error ("Something went wrong at scanning.")
137 LOG.error ("Exception: %s" %(sys.exc_info()[0],))
141 if r.status_code == STATUS_CODE_OK:
143 elif r.status_code == STATUS_CODE_INFECTED:
144 # Parse xml for info if desired
145 #contentXML = r.content
146 #root = ET.fromstring(contentXML)
147 #status = root[1][2].text
150 LOG.error ("Connection error to scan server.")
152 if (infected == True):
153 LOG.error ("Virus found, denying access.")
155 LOG.debug ("No virus found.")
159 def scanFileClamAV (path):
162 LOG.debug ("Scan File: %s" % (path))
164 # ToDo implement ikarus
165 result = pyclamav.scanfile (path)
166 LOG.debug ("Result of file \"%s\": %s" % (path, result))
170 if (infected == True):
171 LOG.error ("Virus found, deny Access %s" % (result,))
175 def whitelistFile (path):
178 LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
182 result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
183 # ToDo replace with real whitelist
185 except Exception as e:
186 LOG.error ("Call returns with an error!")
189 LOG.debug ("Type: %s" %(result))
198 def __init__(self, rootpath, *args, **kw):
199 self.__rootpath = rootpath
200 Fuse.__init__ (self, *args, **kw)
201 LOG.debug ("Init complete.")
203 # defines that our working directory will be the __rootpath
205 os.chdir (self.__rootpath)
207 def getattr(self, path):
208 LOG.debug ("*** getattr (%s)" % (fixPath (path)))
209 return os.lstat (fixPath (path));
211 def getdir(self, path):
212 LOG.debug ("*** getdir (%s)" % (path));
213 return os.listdir (fixPath (path))
215 def readdir(self, path, offset):
216 LOG.debug ("*** readdir (%s %s)" % (path, offset));
217 for e in os.listdir (fixPath (path)):
218 yield fuse.Direntry(e)
220 def chmod (self, path, mode):
221 LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
222 if (config.get("Main", "ReadOnly") == "true"):
224 os.chmod (fixPath (path), mode)
226 def chown (self, path, uid, gid):
227 LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
228 if (config.get("Main", "ReadOnly") == "true"):
230 os.chown (fixPath (path), uid, gid)
232 def link (self, targetPath, linkPath):
233 LOG.debug ("*** link %s %s" % (targetPath, linkPath))
234 if (config.get("Main", "ReadOnly") == "true"):
236 os.link (fixPath (targetPath), fixPath (linkPath))
238 def mkdir (self, path, mode):
239 LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
240 if (config.get("Main", "ReadOnly") == "true"):
242 os.mkdir (fixPath (path), mode)
244 def mknod (self, path, mode, dev):
245 LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
246 if (config.get("Main", "ReadOnly") == "true"):
248 os.mknod (fixPath (path), mode, dev)
250 # to implement virus scan
251 def open (self, path, flags):
252 LOG.debug ("*** open %s %s" % (path, oct (flags)))
253 self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
254 self.fd = self.file.fileno ()
256 infected = scanFileIkarus (rootPath(self.__rootpath, path), self.file)
257 #infected = scanFileClamAV (rootPath(self.__rootpath, path))
258 if (infected == True):
262 whitelisted = whitelistFile (rootPath(self.__rootpath, path))
263 if (whitelisted == False):
267 def read (self, path, length, offset):
268 LOG.debug ("*** read %s %s %s" % (path, length, offset))
269 self.file.seek (offset)
270 return self.file.read (length)
272 def readlink (self, path):
273 LOG.debug ("*** readlink %s" % (path))
274 return os.readlink (fixPath (path))
276 def release (self, path, flags):
277 LOG.debug ("*** release %s %s" % (path, oct (flags)))
280 def rename (self, oldPath, newPath):
281 LOG.debug ("*** rename %s %s %s" % (oldPath, newPath, config.get("Main", "ReadOnly")))
282 if (config.get("Main", "ReadOnly") == "true"):
284 os.rename (fixPath (oldPath), fixPath (newPath))
286 def rmdir (self, path):
287 LOG.debug ("*** rmdir %s %s" % (path, config.get("Main", "ReadOnly")))
288 if (config.get("Main", "ReadOnly") == "true"):
290 os.rmdir (fixPath (path))
293 LOG.debug ("*** statfs")
294 return os.statvfs(".")
296 def symlink (self, targetPath, linkPath):
297 LOG.debug ("*** symlink %s %s %s" % (targetPath, linkPath, config.get("Main", "ReadOnly")))
298 if (config.get("Main", "ReadOnly") == "true"):
300 os.symlink (fixPath (targetPath), fixPath (linkPath))
302 def truncate (self, path, length):
303 LOG.debug ("*** truncate %s %s %s" % (path, length, config.get("Main", "ReadOnly")))
304 if (config.get("Main", "ReadOnly") == "true"):
306 f = open (fixPath (path), "a")
310 def unlink (self, path):
311 LOG.debug ("*** unlink %s %s" % (path, config.get("Main", "ReadOnly")))
312 if (config.get("Main", "ReadOnly") == "true"):
314 os.unlink (fixPath (path))
316 def utime (self, path, times):
317 LOG.debug ("*** utime %s %s" % (path, times))
318 os.utime (fixPath (path), times)
320 def write (self, path, buf, offset):
321 LOG.debug ("*** write %s %s %s %s" % (path, buf, offset, config.get("Main", "ReadOnly")))
322 if (config.get("Main", "ReadOnly") == "true"):
325 self.file.seek (offset)
326 self.file.write (buf)
329 def access (self, path, mode):
330 LOG.debug ("*** access %s %s" % (path, oct (mode)))
331 if not os.access (fixPath (path), mode):
334 def create (self, path, flags, mode):
335 LOG.debug ("*** create %s %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags), config.get("Main", "ReadOnly")))
336 if (config.get("Main", "ReadOnly") == "true"):
338 self.file = os.fdopen (os.open (fixPath (path), flags, mode), flag2mode (flags))
339 self.fd = self.file.fileno ()
342 if __name__ == "__main__":
344 fuse.fuse_python_api = (0, 2)
345 fuse.feature_assert ('stateful_files', 'has_init')
347 config = loadConfig ()
350 LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
351 REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
353 osecfs = OsecFS (config.get ("Main", "Rootpath"))
355 osecfs.multithreaded = 0
357 # osecfs.parser.add_option (mountopt=config.get("Main", "Mountpoint"),
359 # default=config.get("Main", "Rootpath"),
360 # help="mirror filesystem from under PATH [default: %default]")
361 # osecfs.parse(values=osecfs, errex=1)
363 fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
364 osecfs.main (fuse_args)