You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
catcli/catcli/fuser.py

143 lines
4.1 KiB
Python

"""
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
from typing import List, Dict, Any
import anytree # type: ignore
import fuse # type: ignore
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:
"""fuse filesystem mounter"""
def __init__(self, mountpoint: str,
top: anytree.AnyNode,
noder: Noder,
debug: bool = False):
"""fuse filesystem"""
filesystem = CatcliFilesystem(top, noder)
fuse.FUSE(filesystem,
mountpoint,
foreground=debug,
allow_other=True,
nothreads=True,
debug=debug)
class CatcliFilesystem(fuse.LoggingMixIn, fuse.Operations): # type: ignore
"""in-memory filesystem for catcli catalog"""
def __init__(self, top: anytree.AnyNode,
noder: Noder):
"""init fuse filesystem"""
self.top = top
self.noder = noder
def _get_entry(self, path: str) -> anytree.AnyNode:
"""return the node pointed by 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 None
def _get_entries(self, path: str) -> List[anytree.AnyNode]:
"""return nodes pointed by 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: str) -> Dict[str, Any]:
entry = self._get_entry(path)
if not entry:
return {}
curt = time()
mode: Any = 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: str, _fh: Any = None) -> Dict[str, Any]:
"""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: str, _fh: Any) -> List[str]:
"""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