xi-keyring

simple and extensible alternative for gnome-keyring
git clone https://git.ce9e.org/xi-keyring.git

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    -1 
   37    -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