Page MenuHomeSoftware Heritage

D4795.diff
No OneTemporary

D4795.diff

diff --git a/swh/fuse/cache.py b/swh/fuse/cache.py
--- a/swh/fuse/cache.py
+++ b/swh/fuse/cache.py
@@ -187,6 +187,12 @@
)
await self.conn.commit()
+ async def remove(self, swhid: SWHID) -> None:
+ await self.conn.execute(
+ "delete from metadata_cache where swhid=?", (str(swhid),),
+ )
+ await self.conn.commit()
+
class BlobCache(AbstractCache):
""" The blob cache map SWHIDs of type `cnt` to the bytes of their archived
@@ -227,6 +233,12 @@
)
await self.conn.commit()
+ async def remove(self, swhid: SWHID) -> None:
+ await self.conn.execute(
+ "delete from blob_cache where swhid=?", (str(swhid),),
+ )
+ await self.conn.commit()
+
class HistoryCache(AbstractCache):
""" The history cache map SWHIDs of type `rev` to a list of `rev` SWHIDs
@@ -374,7 +386,7 @@
return self.lru_cache.get(direntry.inode, None)
def set(self, direntry: FuseDirEntry, entries: List[FuseEntry]) -> None:
- if isinstance(direntry, (CacheDir, OriginDir)):
+ if isinstance(direntry, (CacheDir, CacheDir.ArtifactShardBySwhid, OriginDir)):
# The `cache/` and `origin/` directories are populated on the fly
pass
elif (
diff --git a/swh/fuse/fs/entry.py b/swh/fuse/fs/entry.py
--- a/swh/fuse/fs/entry.py
+++ b/swh/fuse/fs/entry.py
@@ -26,7 +26,11 @@
RDONLY_FILE = S_IFREG | 0o444
RDONLY_DIR = S_IFDIR | 0o555
- SYMLINK = S_IFLNK | 0o444
+ RDONLY_LNK = S_IFLNK | 0o444
+
+ # `cache/` sub-directories need the write permission in order to invalidate
+ # cached artifacts using `rm {SWHID}`
+ RDWR_DIR = S_IFDIR | 0o755
@dataclass
@@ -57,6 +61,9 @@
raise NotImplementedError
+ async def unlink(self, name: str) -> None:
+ raise NotImplementedError
+
def get_relative_root_path(self) -> str:
return "../" * (self.depth - 1)
@@ -132,7 +139,7 @@
target: path to symlink target
"""
- mode: int = field(init=False, default=int(EntryMode.SYMLINK))
+ mode: int = field(init=False, default=int(EntryMode.RDONLY_LNK))
target: Union[str, bytes, Path]
async def size(self) -> int:
diff --git a/swh/fuse/fs/mountpoint.py b/swh/fuse/fs/mountpoint.py
--- a/swh/fuse/fs/mountpoint.py
+++ b/swh/fuse/fs/mountpoint.py
@@ -176,6 +176,16 @@
target=Path(root_path, f"archive/{swhid}{JSON_SUFFIX}"),
)
+ async def unlink(self, name: str) -> None:
+ try:
+ if name.endswith(JSON_SUFFIX):
+ name = name[: -len(JSON_SUFFIX)]
+ swhid = parse_swhid(name)
+ await self.fuse.cache.metadata.remove(swhid)
+ await self.fuse.cache.blob.remove(swhid)
+ except ValidationError:
+ raise
+
async def compute_entries(self) -> AsyncIterator[FuseEntry]:
prefixes = set()
async for swhid in self.fuse.cache.get_cached_swhids():
@@ -185,7 +195,7 @@
yield self.create_child(
CacheDir.ArtifactShardBySwhid,
name=prefix,
- mode=int(EntryMode.RDONLY_DIR),
+ mode=int(EntryMode.RDWR_DIR),
prefix=prefix,
)
diff --git a/swh/fuse/fuse.py b/swh/fuse/fuse.py
--- a/swh/fuse/fuse.py
+++ b/swh/fuse/fuse.py
@@ -309,6 +309,26 @@
assert isinstance(entry, FuseSymlinkEntry)
return os.fsencode(entry.get_target())
+ async def unlink(
+ self, parent_inode: int, name: str, _ctx: pyfuse3.RequestContext
+ ) -> None:
+ """ Remove a file """
+
+ name = os.fsdecode(name)
+ parent_entry = self.inode2entry(parent_inode)
+ self.logger.debug(
+ "unlink(parent_name=%s, parent_inode=%d, name=%s)",
+ parent_entry.name,
+ parent_inode,
+ name,
+ )
+
+ try:
+ await parent_entry.unlink(name)
+ except Exception as err:
+ self.logger.exception("Cannot unlink: %s", err)
+ raise pyfuse3.FUSEError(errno.ENOENT)
+
async def main(swhids: List[SWHID], root_path: Path, conf: Dict[str, Any]) -> None:
""" swh-fuse CLI entry-point """
diff --git a/swh/fuse/tests/test_cache.py b/swh/fuse/tests/test_cache.py
new file mode 100644
--- /dev/null
+++ b/swh/fuse/tests/test_cache.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2020 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+import os
+
+from swh.fuse.tests.data.config import REGULAR_FILE
+from swh.model.identifiers import parse_swhid
+
+
+def test_cache_artifact(fuse_mntdir):
+ assert os.listdir(fuse_mntdir / "cache") == ["origin"]
+
+ (fuse_mntdir / "archive" / REGULAR_FILE).is_file()
+
+ swhid = parse_swhid(REGULAR_FILE)
+ assert os.listdir(fuse_mntdir / "cache") == [swhid.object_id[:2], "origin"]
+
+
+def test_purge_artifact(fuse_mntdir):
+ DEFAULT_CACHE_CONTENT = ["origin"]
+
+ assert os.listdir(fuse_mntdir / "cache") == DEFAULT_CACHE_CONTENT
+
+ # Access a content artifact...
+ (fuse_mntdir / "archive" / REGULAR_FILE).is_file()
+ assert os.listdir(fuse_mntdir / "cache") != DEFAULT_CACHE_CONTENT
+ # ... and remove it from cache
+ swhid = parse_swhid(REGULAR_FILE)
+ os.unlink(fuse_mntdir / "cache" / swhid.object_id[:2] / str(swhid))
+
+ assert os.listdir(fuse_mntdir / "cache") == DEFAULT_CACHE_CONTENT

File Metadata

Mime Type
text/plain
Expires
Tue, Jun 3, 7:26 PM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3216144

Event Timeline