Changeset View
Changeset View
Standalone View
Standalone View
swh/fuse/fuse.py
Show All 10 Lines | |||||
import time | import time | ||||
from typing import Any, Dict, List | from typing import Any, Dict, List | ||||
import pyfuse3 | import pyfuse3 | ||||
import pyfuse3_asyncio | import pyfuse3_asyncio | ||||
import requests | import requests | ||||
from swh.fuse.cache import FuseCache | from swh.fuse.cache import FuseCache | ||||
from swh.fuse.fs.entry import FuseEntry | from swh.fuse.fs.entry import FuseDirEntry, FuseEntry, FuseFileEntry, FuseSymlinkEntry | ||||
from swh.fuse.fs.mountpoint import Root | from swh.fuse.fs.mountpoint import Root | ||||
from swh.model.identifiers import CONTENT, SWHID | from swh.model.identifiers import CONTENT, SWHID | ||||
from swh.web.client.client import WebAPIClient | from swh.web.client.client import WebAPIClient | ||||
class Fuse(pyfuse3.Operations): | class Fuse(pyfuse3.Operations): | ||||
""" Software Heritage Filesystem in Userspace (FUSE). Locally mount parts of | """ Software Heritage Filesystem in Userspace (FUSE). Locally mount parts of | ||||
the archive and navigate it as a virtual file system. """ | the archive and navigate it as a virtual file system. """ | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | class Fuse(pyfuse3.Operations): | ||||
async def readdir(self, fh: int, offset: int, token: pyfuse3.ReaddirToken) -> None: | async def readdir(self, fh: int, offset: int, token: pyfuse3.ReaddirToken) -> None: | ||||
""" Read entries in an open directory """ | """ Read entries in an open directory """ | ||||
# opendir() uses inode as directory handle | # opendir() uses inode as directory handle | ||||
inode = fh | inode = fh | ||||
# TODO: add cache on direntry list? | # TODO: add cache on direntry list? | ||||
direntry = self.inode2entry(inode) | direntry = self.inode2entry(inode) | ||||
assert isinstance(direntry, FuseDirEntry) | |||||
next_id = offset + 1 | next_id = offset + 1 | ||||
i = 0 | i = 0 | ||||
async for entry in direntry: | async for entry in direntry: | ||||
if i < offset: | if i < offset: | ||||
i += 1 | i += 1 | ||||
continue | continue | ||||
name = os.fsencode(entry.name) | name = os.fsencode(entry.name) | ||||
Show All 14 Lines | class Fuse(pyfuse3.Operations): | ||||
async def read(self, fh: int, offset: int, length: int) -> bytes: | async def read(self, fh: int, offset: int, length: int) -> bytes: | ||||
""" Read `length` bytes from file handle `fh` at position `offset` """ | """ Read `length` bytes from file handle `fh` at position `offset` """ | ||||
# open() uses inode as file handle | # open() uses inode as file handle | ||||
inode = fh | inode = fh | ||||
entry = self.inode2entry(inode) | entry = self.inode2entry(inode) | ||||
assert isinstance(entry, FuseFileEntry) | |||||
data = await entry.get_content() | data = await entry.get_content() | ||||
return data[offset : offset + length] | return data[offset : offset + length] | ||||
async def lookup( | async def lookup( | ||||
self, parent_inode: int, name: str, _ctx: pyfuse3.RequestContext | self, parent_inode: int, name: str, _ctx: pyfuse3.RequestContext | ||||
) -> pyfuse3.EntryAttributes: | ) -> pyfuse3.EntryAttributes: | ||||
""" Look up a directory entry by name and get its attributes """ | """ Look up a directory entry by name and get its attributes """ | ||||
name = os.fsdecode(name) | name = os.fsdecode(name) | ||||
parent_entry = self.inode2entry(parent_inode) | parent_entry = self.inode2entry(parent_inode) | ||||
assert isinstance(parent_entry, FuseDirEntry) | |||||
lookup_entry = await parent_entry.lookup(name) | lookup_entry = await parent_entry.lookup(name) | ||||
if lookup_entry: | if lookup_entry: | ||||
return await self.get_attrs(lookup_entry) | return await self.get_attrs(lookup_entry) | ||||
else: | else: | ||||
logging.error(f"Unknown name during lookup: '{name}'") | logging.error(f"Unknown name during lookup: '{name}'") | ||||
raise pyfuse3.FUSEError(errno.ENOENT) | raise pyfuse3.FUSEError(errno.ENOENT) | ||||
async def readlink(self, inode: int, _ctx: pyfuse3.RequestContext) -> bytes: | async def readlink(self, inode: int, _ctx: pyfuse3.RequestContext) -> bytes: | ||||
entry = self.inode2entry(inode) | entry = self.inode2entry(inode) | ||||
assert isinstance(entry, FuseSymlinkEntry) | |||||
return os.fsencode(entry.get_target()) | return os.fsencode(entry.get_target()) | ||||
async def main(swhids: List[SWHID], root_path: Path, conf: Dict[str, Any]) -> None: | async def main(swhids: List[SWHID], root_path: Path, conf: Dict[str, Any]) -> None: | ||||
""" swh-fuse CLI entry-point """ | """ swh-fuse CLI entry-point """ | ||||
# Use pyfuse3 asyncio layer to match the rest of Software Heritage codebase | # Use pyfuse3 asyncio layer to match the rest of Software Heritage codebase | ||||
pyfuse3_asyncio.enable() | pyfuse3_asyncio.enable() | ||||
Show All 18 Lines |