Skip to content

Commit 56060f7

Browse files
committed
Optimize cowfs: skip path read for read-only ops before first write
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent 6d843de commit 56060f7

File tree

2 files changed

+27
-0
lines changed

2 files changed

+27
-0
lines changed

src/sandlock/_notif.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,10 @@ def _dispatch(self, notif: SeccompNotif) -> None:
652652
nr_getdents = _SYSCALL_NR.get("getdents")
653653

654654
if nr in (nr_getdents64, nr_getdents) and self._cow_handler is not None:
655+
# Fast path: no changes yet → kernel handles readdir correctly
656+
if not self._cow_handler._branch.has_changes:
657+
self._respond_continue(notif.id)
658+
return
655659
child_fd_num = notif.data.args[0] & 0xFFFFFFFF
656660
try:
657661
target = os.readlink(f"/proc/{pid}/fd/{child_fd_num}")
@@ -701,6 +705,15 @@ def _dispatch(self, notif: SeccompNotif) -> None:
701705
# Special arg layouts
702706
cow_special_nrs = {nr_symlinkat, nr_symlink,
703707
nr_linkat, nr_link} - {None}
708+
# Read-only COW syscalls — can skip when no changes yet
709+
cow_readonly_nrs = {nr_newfstatat, nr_statx, nr_faccessat,
710+
nr_stat, nr_lstat, nr_access,
711+
nr_readlinkat, nr_readlink} - {None}
712+
713+
# Fast path: read-only COW syscalls with no changes → let kernel handle
714+
if nr in cow_readonly_nrs and not self._cow_handler._branch.has_changes:
715+
self._respond_continue(notif.id)
716+
return
704717

705718
# symlink/link have special arg layouts — handle separately
706719
if nr in cow_special_nrs:
@@ -900,6 +913,12 @@ def _dispatch(self, notif: SeccompNotif) -> None:
900913

901914
# --- COW: redirect opens under workdir to upper dir ---
902915
if self._cow_handler is not None and self._cow_handler.matches(path):
916+
# Fast path: read-only open with no changes → kernel handles it
917+
from .cowfs._handler import _WRITE_FLAGS, O_DIRECTORY
918+
is_read_only = not (flags & (_WRITE_FLAGS | O_DIRECTORY))
919+
if is_read_only and not self._cow_handler._branch.has_changes:
920+
self._respond_continue(notif.id)
921+
return
903922
self._handle_cow_open(notif, path, flags)
904923
return
905924

src/sandlock/cowfs/_branch.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def __init__(self, workdir: Path, storage: Path | None = None):
2424
self._branch_id: str | None = None
2525
self._finished = False
2626
self._deleted: set[str] = set() # relative paths deleted by sandbox
27+
self._has_changes = False # True after first write or delete
2728

2829
@property
2930
def workdir(self) -> Path:
@@ -60,9 +61,15 @@ def is_deleted(self, rel_path: str) -> bool:
6061
"""Check if a path has been deleted in this branch."""
6162
return rel_path in self._deleted
6263

64+
@property
65+
def has_changes(self) -> bool:
66+
"""True after any write, delete, or metadata change."""
67+
return self._has_changes
68+
6369
def mark_deleted(self, rel_path: str) -> None:
6470
"""Mark a path as deleted."""
6571
self._deleted.add(rel_path)
72+
self._has_changes = True
6673

6774
def ensure_cow_copy(self, rel_path: str) -> Path:
6875
"""Ensure a COW copy exists in upper. Returns the upper path.
@@ -72,6 +79,7 @@ def ensure_cow_copy(self, rel_path: str) -> Path:
7279
Clears any deletion mark for this path.
7380
"""
7481
self._deleted.discard(rel_path)
82+
self._has_changes = True
7583

7684
upper_file = self.upper_dir / rel_path
7785
lower_file = self._workdir / rel_path

0 commit comments

Comments
 (0)