- commit
- 48688bbed73d28c7cf89fd428e456c08ba872371
- parent
- 1d2fba14b56e0b1484c7976ea99e7a895b000906
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2026-05-24 16:54
fix PID.path() have to implement the resolve logic myself after all
Diffstat
| M | xikeyring/pidfd.py | 40 | +++++++++++++++++++++++++++++++--------- |
1 files changed, 31 insertions, 9 deletions
diff --git a/xikeyring/pidfd.py b/xikeyring/pidfd.py
@@ -30,15 +30,37 @@ class PID: 30 30 raise ValueError('Calling process has quit') 31 31 32 32 def path(self, path: str | Path) -> Path:33 -1 root = (Path('/proc') / str(self.pid) / 'root').resolve()34 -1 rel_path = Path(path).absolute().relative_to('/')35 -1 result = (root / rel_path).resolve()36 -137 -1 # FIXME: symlinks are resoled relative to the host.-1 33 # openat2() is not available in python. -1 34 # -1 35 # Also, /proc/pid/root/ is a pseudo-symlink to /, so calling -1 36 # resolve() will just return the host path. 38 37 #39 -1 # A proper fix would involve openat2()40 -1 # (see https://github.com/python/cpython/issues/141878).41 -1 if root not in result.parents:42 -1 raise ValueError('path escapes mount namespace')-1 38 # Instead, we have to carefully resolve the path ourselves. -1 39 -1 40 root = (Path('/proc') / str(self.pid) / 'root') -1 41 result = root -1 42 parts = list(Path(path).absolute().relative_to('/').parts) -1 43 visited_symlinks = set() -1 44 -1 45 while parts: -1 46 part = parts.pop(0) -1 47 result = result / part -1 48 -1 49 if part == '..': -1 50 result = result.parent.parent -1 51 if root not in result.parents: -1 52 raise ValueError('mount namespace escape') -1 53 elif result.is_symlink(): -1 54 if result in visited_symlinks: -1 55 raise ValueError('circular symlinks') -1 56 visited_symlinks.add(result) -1 57 -1 58 link = result.readlink() -1 59 if link.is_absolute(): -1 60 result = root -1 61 parts = [*link.relative_to('/').parts, *parts] -1 62 else: -1 63 result = root.parent -1 64 parts = [*link.parts, *parts] 43 65 44 66 return result