diff --git a/catcli/catcli.py b/catcli/catcli.py index 0655c62..526d701 100755 --- a/catcli/catcli.py +++ b/catcli/catcli.py @@ -21,6 +21,7 @@ from .catalog import Catalog from .walker import Walker from .noder import Noder from .utils import ask, edit +from .fuser import Fuser from .exceptions import BadFormatException, CatcliException NAME = 'catcli' @@ -43,6 +44,7 @@ Usage: {NAME} find [--catalog=] [--format=] [-aBCbdVsP] [--path=] [] {NAME} index [--catalog=] [--meta=...] [-aBCcfnV] {NAME} update [--catalog=] [-aBCcfnV] [--lpath=] + {NAME} mount [--catalog=] [-V] {NAME} rm [--catalog=] [-BCfV] {NAME} rename [--catalog=] [-BCfV] {NAME} edit [--catalog=] [-BCfV] @@ -76,6 +78,12 @@ Options: """ # nopep8 +def cmd_mount(args, top, noder): + """mount action""" + mountpoint = args[''] + Fuser(mountpoint, top, noder) + + def cmd_index(args, noder, catalog, top): """index action""" path = args[''] @@ -320,6 +328,11 @@ def main(): Logger.err(f'no such catalog: {catalog_path}') return False cmd_ls(args, noder, top) + elif args['mount']: + if not catalog.exists(): + Logger.err(f'no such catalog: {catalog_path}') + return False + cmd_mount(args, top, noder) elif args['rm']: if not catalog.exists(): Logger.err(f'no such catalog: {catalog_path}') diff --git a/catcli/fuser.py b/catcli/fuser.py new file mode 100644 index 0000000..1c62760 --- /dev/null +++ b/catcli/fuser.py @@ -0,0 +1,134 @@ +""" +author: deadc0de6 (https://github.com/deadc0de6) +Copyright (c) 2023, deadc0de6 + +fuse for catcli +""" + +import os +import logging +from time import time +from stat import S_IFDIR, S_IFREG +import fuse +from .noder import Noder + + +# build custom logger to log in /tmp +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +fh = logging.FileHandler('/tmp/fuse-catcli.log') +fh.setLevel(logging.DEBUG) +logger.addHandler(fh) + +# globals +WILD = '*' +SEPARATOR = '/' + + +class Fuser: + """fuser filesystem""" + + def __init__(self, mountpoint, top, noder): + """fuse filesystem""" + filesystem = CatcliFilesystem(top, noder) + fuse.FUSE(filesystem, + mountpoint, + foreground=True, + allow_other=True, + nothreads=True, + debug=True) + + +class CatcliFilesystem(fuse.LoggingMixIn, fuse.Operations): + """in-memory filesystem for catcli catalog""" + + def __init__(self, top, noder): + """init fuse filesystem""" + self.top = top + self.noder = noder + + def _get_entry(self, path): + pre = f'{SEPARATOR}{self.noder.NAME_TOP}' + if not path.startswith(pre): + path = pre + path + found = self.noder.list(self.top, path, + rec=False, + fmt='native', + raw=True) + if found: + return found[0] + return [] + + def _get_entries(self, path): + pre = f'{SEPARATOR}{self.noder.NAME_TOP}' + if not path.startswith(pre): + path = pre + path + if not path.endswith(SEPARATOR): + path += SEPARATOR + if not path.endswith(WILD): + path += WILD + found = self.noder.list(self.top, path, + rec=False, + fmt='native', + raw=True) + return found + + def _getattr(self, path): + entry = self._get_entry(path) + if not entry: + return None + + curt = time() + mode = S_IFREG + if entry.type == Noder.TYPE_ARC: + mode = S_IFREG + elif entry.type == Noder.TYPE_DIR: + mode = S_IFDIR + elif entry.type == Noder.TYPE_FILE: + mode = S_IFREG + elif entry.type == Noder.TYPE_STORAGE: + mode = S_IFDIR + elif entry.type == Noder.TYPE_META: + mode = S_IFREG + elif entry.type == Noder.TYPE_TOP: + mode = S_IFREG + return { + 'st_mode': (mode), + 'st_nlink': 1, + 'st_size': 0, + 'st_ctime': curt, + 'st_mtime': curt, + 'st_atime': curt, + 'st_uid': os.getuid(), + 'st_gid': os.getgid(), + } + + def getattr(self, path, _fh=None): + """return attr of file pointed by path""" + logger.info('getattr path: %s', path) + + if path == '/': + # mountpoint + curt = time() + meta = { + 'st_mode': (S_IFDIR), + 'st_nlink': 1, + 'st_size': 0, + 'st_ctime': curt, + 'st_mtime': curt, + 'st_atime': curt, + 'st_uid': os.getuid(), + 'st_gid': os.getgid(), + } + return meta + meta = self._getattr(path) + return meta + + def readdir(self, path, _fh): + """read directory content""" + logger.info('readdir path: %s', path) + content = ['.', '..'] + entries = self._get_entries(path) + for entry in entries: + content.append(entry.name) + return content diff --git a/catcli/utils.py b/catcli/utils.py index 6bc197d..1180455 100644 --- a/catcli/utils.py +++ b/catcli/utils.py @@ -15,6 +15,9 @@ import datetime from catcli.exceptions import CatcliException +SEPARATOR = '/' + + def md5sum(path): """ calculate md5 sum of a file diff --git a/requirements.txt b/requirements.txt index 936d33b..1143736 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ docopt; python_version >= '3.0' anytree; python_version >= '3.0' pyfzf; python_version >= '3.0' +fusepy; python_version >= '3.0' diff --git a/tests.sh b/tests.sh index 040de1a..ee403db 100755 --- a/tests.sh +++ b/tests.sh @@ -21,6 +21,7 @@ pyflakes tests/ # R0915: Too many statements # R0911: Too many return statements # R0903: Too few public methods +# R0801: Similar lines in 2 files pylint --version pylint \ --disable=R0914 \ @@ -29,6 +30,7 @@ pylint \ --disable=R0915 \ --disable=R0911 \ --disable=R0903 \ + --disable=R0801 \ catcli/ pylint \ --disable=W0212 \