david22guy commited on
Commit
8d1e50f
·
verified ·
1 Parent(s): 6dab4e6

Upload folder using huggingface_hub

Browse files
v13/breaking_rsa_solution.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4fd817bf5e5ea2d24c3470eb10338979da075472b026c8ec4492d6d9e5a7fb3c
3
+ size 74512293
v13/my_solution/.dockerignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ .cache
2
+ .git
3
+ .gitattributes
4
+ __pycache__
5
+ *.log
v13/my_solution/Dockerfile ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:24.04
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ RUN apt-get update && apt-get install -y --no-install-recommends \
6
+ python3 python3-pip python3-dev \
7
+ build-essential gcc g++ make \
8
+ autoconf automake libtool m4 perl \
9
+ libgmp-dev libhwloc15 libhwloc-dev libgomp1 \
10
+ zlib1g-dev git \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ RUN pip install --no-cache-dir --break-system-packages gmpy2 flask requests
14
+
15
+ RUN git clone --depth 1 https://gitlab.inria.fr/zimmerma/ecm.git /tmp/ecm-src \
16
+ && cd /tmp/ecm-src \
17
+ && autoreconf -i && ./configure CFLAGS="-O2" \
18
+ && make -j"$(nproc)" && make install && ldconfig \
19
+ && cp ecm /usr/local/bin/ecm \
20
+ && rm -rf /tmp/ecm-src
21
+
22
+ COPY cado-nfs.tar.gz /tmp/
23
+ RUN tar xzf /tmp/cado-nfs.tar.gz -C / \
24
+ && rm /tmp/cado-nfs.tar.gz \
25
+ && chmod +x /opt/cado-nfs/build/release/sieve/las \
26
+ /opt/cado-nfs/build/release/polyselect/polyselect \
27
+ && python3 /opt/cado-nfs/build/release/cado-nfs.py --help > /dev/null 2>&1
28
+
29
+ COPY ramnfs/broker.c ramnfs/shim.c /opt/ramnfs/
30
+ RUN cd /opt/ramnfs \
31
+ && gcc -O2 -pthread -o broker broker.c -lpthread \
32
+ && gcc -O2 -fPIC -shared -o shim.so shim.c -ldl -lpthread \
33
+ && chmod +x broker
34
+
35
+ RUN userdel -r ubuntu 2>/dev/null; useradd -m -u 1000 -s /usr/sbin/nologin miner
36
+
37
+ WORKDIR /app
38
+ COPY enigma_challenges /app/enigma_challenges/
39
+ COPY breaking_rsa.py /app/
40
+
41
+ ENV ECM_BIN=/usr/local/bin/ecm \
42
+ CADO_NFS=/opt/cado-nfs/build/release/cado-nfs.py \
43
+ RAMNFS_BROKER=/opt/ramnfs/broker \
44
+ RAMNFS_SHIM=/opt/ramnfs/shim.so \
45
+ RAMNFS_SOCK=/tmp/ramnfs.sock \
46
+ RAMNFS_WORKDIR=/ramwork/factor.work \
47
+ HOME=/tmp \
48
+ TMPDIR=/tmp \
49
+ WALL_TIME=14400 \
50
+ DEADLINE_MARGIN=120 \
51
+ ECM_PRETEST_CAP=60 \
52
+ PYTHONUNBUFFERED=1
53
+
54
+ USER miner
55
+ ENTRYPOINT ["python3", "/app/breaking_rsa.py"]
v13/my_solution/breaking_rsa.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import glob
3
+ import json
4
+ import os
5
+ import re
6
+ import signal
7
+ import subprocess
8
+ import sys
9
+ import time
10
+ from datetime import datetime, timezone
11
+ from pathlib import Path
12
+ from typing import *
13
+
14
+ import gmpy2
15
+ from gmpy2 import mpz
16
+
17
+ from enigma_challenges.breaking_rsa import Problem, Solution
18
+ from enigma_challenges.solution_output import build_solution_zip, write_solution_output
19
+
20
+ CHALLENGE_INPUT_FILE = "/challenge_input/challenge_input.json"
21
+
22
+
23
+ def _env_int(name: str, default: int) -> int:
24
+ try:
25
+ return int(os.environ.get(name, default))
26
+ except (TypeError, ValueError):
27
+ return default
28
+
29
+
30
+ def _cpu_count() -> int:
31
+ n = os.cpu_count() or 8
32
+ try:
33
+ parts = Path("/sys/fs/cgroup/cpu.max").read_text().split()
34
+ if parts and parts[0] != "max":
35
+ return max(1, min(n, int(parts[0]) // int(parts[1])))
36
+ except (OSError, ValueError, IndexError):
37
+ pass
38
+ try:
39
+ quota = int(Path("/sys/fs/cgroup/cpu/cpu.cfs_quota_us").read_text())
40
+ period = int(Path("/sys/fs/cgroup/cpu/cpu.cfs_period_us").read_text())
41
+ if quota > 0 and period > 0:
42
+ return max(1, min(n, quota // period))
43
+ except (OSError, ValueError):
44
+ pass
45
+ return n
46
+
47
+
48
+ def _find_bin(env_key: str, candidates: List[str]) -> Optional[str]:
49
+ env = os.environ.get(env_key, "").strip()
50
+ if env and os.path.isfile(env):
51
+ return env
52
+ for c in candidates:
53
+ if os.path.isfile(c):
54
+ return c
55
+ return None
56
+
57
+
58
+ def _find_cado_script() -> Optional[str]:
59
+ env = os.environ.get("CADO_NFS", "").strip()
60
+ if env and os.path.isfile(env):
61
+ return env
62
+ for c in ["/opt/cado-nfs/build/release/cado-nfs.py",
63
+ "/usr/local/bin/cado-nfs.py", "/usr/bin/cado-nfs.py"]:
64
+ if os.path.isfile(c):
65
+ return c
66
+ for pat in ["/opt/cado-nfs/build/*/cado-nfs.py", "/usr/local/lib/cado-nfs-*/cado-nfs.py"]:
67
+ m = sorted(glob.glob(pat))
68
+ if m:
69
+ return m[-1]
70
+ return None
71
+
72
+
73
+ def _start_broker(sock_path: str) -> Optional[subprocess.Popen]:
74
+ broker = _find_bin("RAMNFS_BROKER", ["/opt/ramnfs/broker", "/app/ramnfs/broker"])
75
+ if not broker:
76
+ return None
77
+ try:
78
+ os.unlink(sock_path)
79
+ except OSError:
80
+ pass
81
+ try:
82
+ proc = subprocess.Popen([broker, sock_path],
83
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
84
+ except Exception:
85
+ return None
86
+ for _ in range(50):
87
+ if os.path.exists(sock_path):
88
+ return proc
89
+ time.sleep(0.1)
90
+ proc.kill()
91
+ return None
92
+
93
+
94
+ def _factors_from_line(line: str, n: mpz) -> Optional[Tuple[int, int]]:
95
+ parts = line.split()
96
+ if len(parts) < 2 or not all(re.fullmatch(r"\d+", p) for p in parts):
97
+ return None
98
+ prod = 1
99
+ for p in parts:
100
+ prod *= int(p)
101
+ if prod == n and all(gmpy2.is_prime(mpz(int(p))) for p in parts):
102
+ a, b = int(parts[0]), int(parts[1])
103
+ return (a, b) if a <= b else (b, a)
104
+ return None
105
+
106
+
107
+ def _kill(proc: subprocess.Popen) -> None:
108
+ if proc is None or proc.poll() is not None:
109
+ return
110
+ for sig in (signal.SIGTERM, signal.SIGKILL):
111
+ try:
112
+ os.killpg(os.getpgid(proc.pid), sig)
113
+ except Exception:
114
+ try:
115
+ proc.kill()
116
+ except Exception:
117
+ pass
118
+ try:
119
+ proc.wait(timeout=8)
120
+ return
121
+ except Exception:
122
+ continue
123
+
124
+
125
+ def _run_cado(n: mpz) -> Optional[Tuple[int, int]]:
126
+ cado = _find_cado_script()
127
+ if not cado:
128
+ return None
129
+ shim = _find_bin("RAMNFS_SHIM", ["/opt/ramnfs/shim.so", "/app/ramnfs/shim.so"])
130
+ sock = os.environ.get("RAMNFS_SOCK", "/tmp/ramnfs.sock")
131
+ workdir = os.environ.get("RAMNFS_WORKDIR", "/ramwork/factor.work")
132
+ threads = _env_int("CADO_THREADS", 0) or _cpu_count()
133
+
134
+ broker = None
135
+ if shim:
136
+ broker = _start_broker(sock)
137
+ if not broker:
138
+ shim = None
139
+ if not shim:
140
+ workdir = os.path.join(os.environ.get("TMPDIR", "/tmp"), "cado_run")
141
+ os.makedirs(workdir, exist_ok=True)
142
+
143
+ env = dict(os.environ)
144
+ env["HOME"] = env["TMPDIR"] = "/tmp"
145
+ if shim:
146
+ env["LD_PRELOAD"] = shim
147
+ env["RAMNFS_SOCK"] = sock
148
+ env["RAMNFS_PREFIX"] = "/ramwork"
149
+
150
+ cado_build = str(Path(cado).parent)
151
+ cmd = [
152
+ sys.executable, cado, str(int(n)),
153
+ f"tasks.workdir={workdir}",
154
+ f"tasks.threads={threads}",
155
+ "server.address=localhost", "server.port=0", "server.threaded=1",
156
+ f"slaves.nrclients={threads}",
157
+ f"slaves.cado_nfs_client.bindir={cado_build}",
158
+ f"tasks.linalg.bwc.threads={threads}",
159
+ "tasks.sieve.las.threads=1",
160
+ ]
161
+ if os.environ.get("CADO_ADMAX"):
162
+ cmd.append(f"tasks.polyselect.admax={os.environ['CADO_ADMAX']}")
163
+ if os.environ.get("CADO_DEGREE"):
164
+ cmd.append(f"tasks.polyselect.degree={os.environ['CADO_DEGREE']}")
165
+
166
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
167
+ text=True, env=env, start_new_session=True)
168
+ factors: Optional[Tuple[int, int]] = None
169
+ try:
170
+ assert proc.stdout
171
+ for raw in proc.stdout:
172
+ for line in raw.replace("\r", "\n").splitlines():
173
+ s = line.strip()
174
+ if not s:
175
+ continue
176
+ factors = _factors_from_line(s, n)
177
+ if factors:
178
+ break
179
+ if factors:
180
+ break
181
+ except Exception:
182
+ pass
183
+ finally:
184
+ _kill(proc)
185
+ if broker:
186
+ _kill(broker)
187
+ return factors
188
+
189
+
190
+ def factor_semiprime(n_int: int) -> Tuple[Optional[int], Optional[int], str]:
191
+ res = _run_cado(mpz(n_int))
192
+ if res:
193
+ return res[0], res[1], "cado_gnfs"
194
+ return None, None, "failed"
195
+
196
+
197
+ def _load_problem() -> Tuple[str, "Problem"]:
198
+ if os.path.isfile(CHALLENGE_INPUT_FILE):
199
+ try:
200
+ data = json.loads(Path(CHALLENGE_INPUT_FILE).read_text())
201
+ prob = Problem(int(data["difficulty"]), int(data["num"]), int(data["num_bits"]))
202
+ cid = (sys.argv[1].strip() if len(sys.argv) > 1 else "") or "challenge"
203
+ return cid, prob
204
+ except Exception:
205
+ pass
206
+ if len(sys.argv) == 3:
207
+ prob = Problem.from_json(sys.argv[2].strip())
208
+ return sys.argv[1].strip(), prob
209
+ raise SystemExit("No problem input")
210
+
211
+
212
+ def main() -> None:
213
+ timestamp_start = datetime.now(timezone.utc).isoformat()
214
+ start = time.time()
215
+ challenge_id, problem = _load_problem()
216
+ if problem.num < 6:
217
+ sys.exit(1)
218
+
219
+ p, q, method = factor_semiprime(problem.num)
220
+ solve_time = time.time() - start
221
+
222
+ ok = (p is not None and q is not None
223
+ and mpz(p) * mpz(q) == problem.num
224
+ and gmpy2.is_prime(mpz(p)) and gmpy2.is_prime(mpz(q)))
225
+ solution = Solution("success", int(p), int(q)) if ok else Solution("failed", None, None)
226
+
227
+ result_json = json.dumps(solution.to_dict(), indent=2)
228
+ solve_info_json = json.dumps({
229
+ "solution_status": solution.status,
230
+ "challenge_id": challenge_id,
231
+ "timestamp_utc": timestamp_start,
232
+ "solve_time_seconds": solve_time,
233
+ "method": method,
234
+ "num_bits": problem.num_bits,
235
+ })
236
+
237
+ output_dir = os.environ.get("OUTPUT_DIR")
238
+ if output_dir:
239
+ try:
240
+ Path(output_dir).mkdir(exist_ok=True)
241
+ Path(output_dir, "result.json").write_text(result_json)
242
+ Path(output_dir, "solve_info.json").write_text(solve_info_json)
243
+ except OSError:
244
+ pass
245
+
246
+ write_solution_output(build_solution_zip({
247
+ "result.json": result_json,
248
+ "solve_info.json": solve_info_json,
249
+ }))
250
+ os._exit(0 if solution.status == "success" else 1)
251
+
252
+
253
+ if __name__ == "__main__":
254
+ main()
v13/my_solution/cado-nfs.tar.gz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:64b4ad20da54b389fbe7c0b521dc95339aeb8fb61aaebf6db8bee1309602b2c8
3
+ size 74769637
v13/my_solution/enigma_challenges/__init__.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from abc import ABC, abstractmethod
3
+ import collections.abc
4
+ import json
5
+ from pathlib import Path
6
+ import typing
7
+ from typing import *
8
+
9
+ class Challenge[P, S, V](ABC):
10
+
11
+ @abstractmethod
12
+ def generate(self, seed: int) -> tuple[P, V]:
13
+ raise NotImplementedError()
14
+
15
+ @abstractmethod
16
+ def verify(self, problem: P, solution: S, secrets: V) -> bool:
17
+ raise NotImplementedError()
18
+
19
+ class Solver[P, S](ABC):
20
+
21
+ @abstractmethod
22
+ def solve(self, problem: P) -> S:
23
+ raise NotImplementedError()
24
+
25
+ def _is_callable_hint(ty: Any) -> bool:
26
+ return typing.get_origin(ty) is collections.abc.Callable or ty is collections.abc.Callable
27
+
28
+ def _is_serde(ty: type) -> bool:
29
+ return isinstance(ty, type) and issubclass(ty, Serde) and (ty is not Serde)
30
+
31
+ def _convert_from(val: Any, ty: Any) -> Any:
32
+ if ty is Any:
33
+ return val
34
+ origin = typing.get_origin(ty)
35
+ args = typing.get_args(ty)
36
+ if origin is Union:
37
+ if val is None and type(None) in args:
38
+ return None
39
+ for variant in args:
40
+ if variant is type(None):
41
+ continue
42
+ try:
43
+ return _convert_from(val, variant)
44
+ except (TypeError, KeyError):
45
+ continue
46
+ raise TypeError(f'value {val!r} does not match any variant of {ty}')
47
+ if origin is list:
48
+ if not isinstance(val, list):
49
+ raise TypeError(f'expected list, got {type(val)}')
50
+ if args:
51
+ return [_convert_from(item, args[0]) for item in val]
52
+ return val
53
+ if origin is dict:
54
+ if not isinstance(val, dict):
55
+ raise TypeError(f'expected dict, got {type(val)}')
56
+ if len(args) == 2:
57
+ return {_convert_from(k, args[0]): _convert_from(v, args[1]) for k, v in val.items()}
58
+ return val
59
+ if origin is tuple:
60
+ if not isinstance(val, (list, tuple)):
61
+ raise TypeError(f'expected tuple, got {type(val)}')
62
+ if args:
63
+ return tuple((_convert_from(item, a) for item, a in zip(val, args)))
64
+ return tuple(val)
65
+ if _is_serde(ty):
66
+ if isinstance(val, ty):
67
+ return val
68
+ if isinstance(val, dict):
69
+ return ty.from_dict(val)
70
+ raise TypeError(f'expected dict or {ty.__name__}, got {type(val)}')
71
+ if isinstance(ty, type) and (not isinstance(val, ty)):
72
+ raise TypeError(f'expected type `{ty.__name__}` but got `{type(val).__name__}`')
73
+ return val
74
+
75
+ def _convert_to(val: Any) -> Any:
76
+ if isinstance(val, Serde):
77
+ return val.to_dict()
78
+ if isinstance(val, list):
79
+ return [_convert_to(item) for item in val]
80
+ if isinstance(val, dict):
81
+ return {k: _convert_to(v) for k, v in val.items()}
82
+ if isinstance(val, tuple):
83
+ return [_convert_to(item) for item in val]
84
+ return val
85
+
86
+ class Serde:
87
+
88
+ @classmethod
89
+ def from_dict(cls, data: dict[str, Any]) -> Self:
90
+ hints = typing.get_type_hints(cls)
91
+ defaults = {key: val for key in dir(cls) if not key.startswith('__') and (not isinstance((val := getattr(cls, key)), Callable))}
92
+ args = list()
93
+ for key, ty in hints.items():
94
+ if _is_callable_hint(ty):
95
+ continue
96
+ if key in data:
97
+ val = data[key]
98
+ elif key in defaults:
99
+ val = defaults[key]
100
+ else:
101
+ raise KeyError(f"missing expected key '{key}'")
102
+ args.append(_convert_from(val, ty))
103
+ return cls(*args)
104
+
105
+ def to_dict(self) -> dict[str, Any]:
106
+ hints = typing.get_type_hints(type(self))
107
+ result = {}
108
+ for key, ty in hints.items():
109
+ if _is_callable_hint(ty):
110
+ continue
111
+ result[key] = _convert_to(getattr(self, key))
112
+ return result
113
+
114
+ @classmethod
115
+ def from_json(cls, json_str: str) -> Self:
116
+ return cls.from_dict(json.loads(json_str))
117
+
118
+ def to_json(self) -> str:
119
+ return json.dumps(self.to_dict())
120
+
121
+ @classmethod
122
+ def from_json_file(cls, json_file: Path) -> Self:
123
+ with json_file.open('r') as infile:
124
+ return cls.from_dict(json.load(infile))
125
+
126
+ def to_json_file(self, out: Path) -> None:
127
+ with out.open('w') as outfile:
128
+ json.dump(self.to_dict(), outfile)
v13/my_solution/enigma_challenges/breaking_rsa.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ import logging
4
+ from typing import *
5
+ import gmpy2
6
+ from . import Challenge, Serde
7
+ _logger = logging.getLogger(__name__)
8
+
9
+ @dataclass
10
+ class Problem(Serde):
11
+ difficulty: int
12
+ num: int
13
+ num_bits: int
14
+
15
+ @dataclass
16
+ class Solution(Serde):
17
+ status: str
18
+ p: Optional[int]
19
+ q: Optional[int]
20
+
21
+ @dataclass
22
+ class Verif(Serde):
23
+ n: int
24
+ p: int
25
+ q: int
26
+
27
+ def validate_breaking_rsa_solution(solution: Solution, verif: Verif, prob: Problem | None=None, require_success_status: bool=True) -> tuple[bool, str | None]:
28
+ if require_success_status and getattr(solution, 'status', None) != 'success':
29
+ return (False, f"Solution status is '{getattr(solution, 'status', None)}', not 'success'")
30
+ if solution.p is None or solution.q is None:
31
+ return (False, 'Solution has missing factors (p or q is null)')
32
+ try:
33
+ sol_p = int(solution.p)
34
+ sol_q = int(solution.q)
35
+ except (TypeError, ValueError):
36
+ return (False, f'Solution p/q are not valid integers: p={solution.p}, q={solution.q}')
37
+ sol_p, sol_q = (min(sol_p, sol_q), max(sol_p, sol_q))
38
+ p_check, q_check = (min(verif.p, verif.q), max(verif.p, verif.q))
39
+ n = verif.n
40
+ if sol_p * sol_q != n:
41
+ return (False, f'p * q != n: {sol_p} * {sol_q} != {n}')
42
+ if sol_p != p_check or sol_q != q_check:
43
+ return (False, f"Factors don't match expected: got ({sol_p}, {sol_q}), expected ({p_check}, {q_check})")
44
+ if prob is not None:
45
+ if prob.num != n or sol_p * sol_q != prob.num:
46
+ return (False, f'n mismatch between problem ({prob.num}) and verif ({n})')
47
+ return (True, None)
48
+
49
+ def _gen_prime(num_bits: int, rng: gmpy2.random_state) -> gmpy2.mpz:
50
+ n = gmpy2.mpz_urandomb(rng, num_bits)
51
+ n |= 1 << num_bits - 1 | 1
52
+ return gmpy2.next_prime(n)
53
+
54
+ @dataclass
55
+ class BreakingRSA(Challenge[Problem, Solution, Verif]):
56
+ difficulty: int
57
+ num_bits: int
58
+
59
+ def generate(self, seed: int) -> tuple[Problem, Verif]:
60
+ rng = gmpy2.random_state(seed)
61
+ bits_p = self.num_bits // 2
62
+ bits_q = self.num_bits - bits_p
63
+ fermat_thresh = 2 ** max(bits_p - 100, 1)
64
+ while True:
65
+ p = _gen_prime(bits_p, rng)
66
+ q = _gen_prime(bits_q, rng)
67
+ n = p * q
68
+ if n.bit_length() == self.num_bits and abs(p - q) > fermat_thresh:
69
+ break
70
+ n = int(n)
71
+ p = int(p)
72
+ q = int(q)
73
+ _logger.info(f'Generated {self.num_bits}-bit semiprime ({len(str(n))} digits)')
74
+ problem = Problem(self.difficulty, n, self.num_bits)
75
+ verif = Verif(n, p, q)
76
+ return (problem, verif)
77
+
78
+ def verify(self, prob: Problem, sol: Solution, verif: Verif) -> bool:
79
+ success, reason = validate_breaking_rsa_solution(sol, verif, prob, require_success_status=False)
80
+ if success:
81
+ _logger.info('Verification SUCCESS')
82
+ elif sol.p is None or sol.q is None:
83
+ _logger.info(f'Verification FAILURE: missing factors, got p={sol.p} and' + f' q={sol.q}, with solution status {sol.status}')
84
+ else:
85
+ _logger.info(f'Verification FAILURE: {reason or 'factors do not match'}')
86
+ return success
v13/my_solution/enigma_challenges/solution_output.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import base64
3
+ import binascii
4
+ import io
5
+ import os
6
+ import sys
7
+ import zipfile
8
+ SOLUTION_OUTPUT_SEPARATOR: bytes = b'\n----- ENIGMA-SOLUTION-OUTPUT-BEGIN-a8c7f3e2-9d4b-4c5a-8f1e-2b6d3a4e5f7c -----\n'
9
+
10
+ def build_solution_zip(files: dict[str, str | bytes]) -> bytes:
11
+ buffer = io.BytesIO()
12
+ with zipfile.ZipFile(buffer, 'w', compression=zipfile.ZIP_DEFLATED) as zf:
13
+ for name, content in files.items():
14
+ zf.writestr(name, content)
15
+ return buffer.getvalue()
16
+
17
+ def write_solution_output(zip_bytes: bytes) -> None:
18
+ sys.stdout.flush()
19
+ sys.stderr.flush()
20
+ encoded = base64.b64encode(zip_bytes)
21
+ buf = sys.stdout.buffer
22
+ buf.write(SOLUTION_OUTPUT_SEPARATOR)
23
+ buf.write(encoded)
24
+ buf.write(b'\n')
25
+ buf.flush()
26
+
27
+ def split_on_separator(raw_stdout: bytes) -> tuple[bytes, bytes, bool]:
28
+ idx = raw_stdout.find(SOLUTION_OUTPUT_SEPARATOR)
29
+ if idx == -1:
30
+ return (raw_stdout, b'', False)
31
+ return (raw_stdout[:idx], raw_stdout[idx + len(SOLUTION_OUTPUT_SEPARATOR):], True)
32
+
33
+ def extract_artifacts(raw_stdout: bytes, output_dir: str) -> tuple[bool, str | None]:
34
+ os.makedirs(output_dir, exist_ok=True)
35
+ logs_bytes, payload_b64, found = split_on_separator(raw_stdout)
36
+ log_path = os.path.join(output_dir, 'stdout.log')
37
+ with open(log_path, 'wb') as f:
38
+ f.write(logs_bytes)
39
+ if not found:
40
+ return (False, 'No solution output separator found in container stdout. The solver must print logs, then the separator line, then a base64-encoded zip of result.json and other artifacts.')
41
+ payload_b64 = payload_b64.strip()
42
+ if not payload_b64:
43
+ return (False, 'Separator found but base64 payload is empty.')
44
+ try:
45
+ payload_bytes = base64.b64decode(payload_b64, validate=True)
46
+ except (binascii.Error, ValueError) as e:
47
+ return (False, f'Base64 decode of solution payload failed: {e}')
48
+ if not payload_bytes:
49
+ return (False, 'Decoded payload is empty.')
50
+ zip_path = os.path.join(output_dir, 'solution_artifacts.zip')
51
+ with open(zip_path, 'wb') as f:
52
+ f.write(payload_bytes)
53
+ if not zipfile.is_zipfile(zip_path):
54
+ return (False, 'Decoded payload is not a valid zip file.')
55
+ try:
56
+ with zipfile.ZipFile(zip_path, 'r') as zf:
57
+ dest = os.path.abspath(output_dir)
58
+ for member in zf.infolist():
59
+ member_path = os.path.normpath(os.path.join(dest, member.filename))
60
+ if not (member_path == dest or member_path.startswith(dest + os.sep)):
61
+ return (False, f"Zip member '{member.filename}' escapes output directory.")
62
+ zf.extractall(dest)
63
+ except zipfile.BadZipFile as e:
64
+ return (False, f'Bad zip file: {e}')
65
+ except Exception as e:
66
+ return (False, f'Zip extraction failed: {e}')
67
+ return (True, None)
v13/my_solution/ramnfs/broker.c ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #define _GNU_SOURCE
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+ #include <unistd.h>
6
+ #include <sys/socket.h>
7
+ #include <sys/un.h>
8
+ #include <sys/stat.h>
9
+ #include <fcntl.h>
10
+ #include <sys/mman.h>
11
+ #include <pthread.h>
12
+ #include <errno.h>
13
+ #include <signal.h>
14
+
15
+ #define MAXF 16384
16
+ #define PATHMAX 1024
17
+
18
+ static char g_paths[MAXF][PATHMAX];
19
+ static int g_fds[MAXF];
20
+ static int g_nf = 0;
21
+ static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
22
+ static int g_dbg = 0;
23
+ static int g_keep = 0;
24
+
25
+ static int idx_of(const char *p) {
26
+ for (int i = 0; i < g_nf; i++)
27
+ if (g_fds[i] >= 0 && strcmp(g_paths[i], p) == 0) return i;
28
+ return -1;
29
+ }
30
+
31
+ static int find_or_create(const char *path) {
32
+ int i = idx_of(path);
33
+ if (i >= 0) return g_fds[i];
34
+ if (g_nf >= MAXF) return -1;
35
+ int fd = memfd_create("ramnfs", 0);
36
+ if (fd < 0) return -1;
37
+ strncpy(g_paths[g_nf], path, PATHMAX - 1);
38
+ g_fds[g_nf] = fd;
39
+ g_nf++;
40
+ return fd;
41
+ }
42
+
43
+ static int send_fd(int sock, int fd, char status) {
44
+ struct msghdr msg = {0};
45
+ char b = status;
46
+ struct iovec io = {.iov_base = &b, .iov_len = 1};
47
+ msg.msg_iov = &io; msg.msg_iovlen = 1;
48
+ char cbuf[CMSG_SPACE(sizeof(int))];
49
+ memset(cbuf, 0, sizeof cbuf);
50
+ if (fd >= 0) {
51
+ msg.msg_control = cbuf; msg.msg_controllen = sizeof cbuf;
52
+ struct cmsghdr *c = CMSG_FIRSTHDR(&msg);
53
+ c->cmsg_level = SOL_SOCKET; c->cmsg_type = SCM_RIGHTS;
54
+ c->cmsg_len = CMSG_LEN(sizeof(int));
55
+ memcpy(CMSG_DATA(c), &fd, sizeof(int));
56
+ }
57
+ return sendmsg(sock, &msg, 0);
58
+ }
59
+
60
+ static int readn(int fd, void *buf, int n) {
61
+ int g = 0;
62
+ while (g < n) {
63
+ int r = read(fd, (char *)buf + g, n - g);
64
+ if (r <= 0) return g;
65
+ g += r;
66
+ }
67
+ return g;
68
+ }
69
+
70
+ static void *handle_conn(void *arg) {
71
+ int c = (int)(intptr_t)arg;
72
+ char op = 0;
73
+ if (readn(c, &op, 1) != 1) goto done;
74
+ unsigned short len = 0;
75
+ if (readn(c, &len, 2) != 2) goto done;
76
+ if (len >= PATHMAX) len = PATHMAX - 1;
77
+ char path[PATHMAX] = {0};
78
+ if (readn(c, path, len) != (int)len) goto done;
79
+ path[len] = 0;
80
+
81
+ if (op == 'O') {
82
+ pthread_mutex_lock(&g_mu);
83
+ int existed = idx_of(path) >= 0;
84
+ int memfd = find_or_create(path);
85
+ pthread_mutex_unlock(&g_mu);
86
+ if (g_dbg) fprintf(stderr, "broker: OPEN %s %s\n", existed ? "existing" : "CREATE", path);
87
+ if (memfd < 0) { send_fd(c, -1, 'E'); goto done; }
88
+ char proc[64];
89
+ snprintf(proc, sizeof proc, "/proc/self/fd/%d", memfd);
90
+ int sendable = open(proc, O_RDWR);
91
+ send_fd(c, sendable, sendable >= 0 ? 'K' : 'E');
92
+ if (sendable >= 0) close(sendable);
93
+
94
+ } else if (op == 'S') {
95
+ pthread_mutex_lock(&g_mu);
96
+ int i = idx_of(path);
97
+ long long sz = 0; char st = 'E';
98
+ if (i >= 0) {
99
+ struct stat stt;
100
+ if (fstat(g_fds[i], &stt) == 0) { sz = stt.st_size; st = 'K'; }
101
+ }
102
+ pthread_mutex_unlock(&g_mu);
103
+ if (g_dbg && st == 'E') fprintf(stderr, "broker: STAT MISS %s\n", path);
104
+ write(c, &st, 1); write(c, &sz, 8);
105
+
106
+ } else if (op == 'U') {
107
+
108
+ size_t pl = strlen(path);
109
+ int is_rel = (pl >= 3 && strcmp(path + pl - 3, ".gz") == 0);
110
+ int retain = g_keep || is_rel;
111
+ pthread_mutex_lock(&g_mu);
112
+ int i = idx_of(path);
113
+ if (g_dbg) fprintf(stderr, "broker: UNLINK %s %s%s\n",
114
+ i >= 0 ? "hit" : "miss", path, (i >= 0 && retain) ? " (RETAINED)" : "");
115
+ if (i >= 0 && !retain) { close(g_fds[i]); g_fds[i] = -1; g_paths[i][0] = 0; }
116
+ pthread_mutex_unlock(&g_mu);
117
+ char st = 'K'; write(c, &st, 1);
118
+
119
+ } else if (op == 'L') {
120
+
121
+ char pfx[PATHMAX]; strncpy(pfx, path, PATHMAX - 1); pfx[PATHMAX-1] = 0;
122
+ size_t pl = strlen(pfx);
123
+ if (pl > 0 && pfx[pl-1] == '/') pfx[--pl] = 0;
124
+
125
+ char **names = NULL; int cnt = 0;
126
+ pthread_mutex_lock(&g_mu);
127
+ for (int i = 0; i < g_nf; i++) {
128
+ if (g_fds[i] < 0) continue;
129
+ const char *p = g_paths[i];
130
+ if (strncmp(p, pfx, pl) != 0 || p[pl] != '/') continue;
131
+ const char *rest = p + pl + 1;
132
+ if (strchr(rest, '/') != NULL) continue;
133
+ names = realloc(names, (cnt + 1) * sizeof(char *));
134
+ names[cnt++] = strdup(rest);
135
+ }
136
+ pthread_mutex_unlock(&g_mu);
137
+
138
+ write(c, &cnt, 4);
139
+ for (int i = 0; i < cnt; i++) {
140
+ unsigned short nl = (unsigned short)strlen(names[i]);
141
+ write(c, &nl, 2); write(c, names[i], nl);
142
+ free(names[i]);
143
+ }
144
+ free(names);
145
+
146
+ } else if (op == 'P') {
147
+ fprintf(stderr, "shimlog: %s\n", path);
148
+ }
149
+ done:
150
+ close(c);
151
+ return NULL;
152
+ }
153
+
154
+ static void dump_table(int sig) {
155
+ (void)sig;
156
+ fprintf(stderr, "=== BROKER DUMP: %d slots ===\n", g_nf);
157
+ for (int i = 0; i < g_nf; i++) {
158
+ if (g_fds[i] < 0) continue;
159
+ if (strstr(g_paths[i], "filelist") || strstr(g_paths[i], "stderr") || strstr(g_paths[i], "stdout")) {
160
+ fprintf(stderr, "DUMP %s CONTENT:\n", g_paths[i]);
161
+ off_t cur = lseek(g_fds[i], 0, SEEK_CUR);
162
+ lseek(g_fds[i], 0, SEEK_SET);
163
+ char buf[16384]; int r;
164
+ while ((r = read(g_fds[i], buf, sizeof buf)) > 0) fwrite(buf, 1, r, stderr);
165
+ lseek(g_fds[i], cur, SEEK_SET);
166
+ fprintf(stderr, "--- end filelist ---\n");
167
+ } else {
168
+ fprintf(stderr, "FILE %s\n", g_paths[i]);
169
+ }
170
+ }
171
+ fprintf(stderr, "=== END DUMP ===\n");
172
+ }
173
+
174
+ int main(int argc, char **argv) {
175
+ const char *sp = argc > 1 ? argv[1] : "/tmp/ramnfs.sock";
176
+ signal(SIGUSR1, dump_table);
177
+ g_dbg = getenv("RAMNFS_DEBUG") != NULL;
178
+ g_keep = getenv("RAMNFS_KEEP") != NULL;
179
+ unlink(sp);
180
+ int s = socket(AF_UNIX, SOCK_STREAM, 0);
181
+ struct sockaddr_un a = {0}; a.sun_family = AF_UNIX;
182
+ strncpy(a.sun_path, sp, sizeof a.sun_path - 1);
183
+ if (bind(s, (struct sockaddr *)&a, sizeof a) < 0) { perror("bind"); return 1; }
184
+ listen(s, 4096);
185
+ fprintf(stderr, "broker: listening on %s (pid %d)\n", sp, getpid());
186
+ for (;;) {
187
+ int c = accept(s, 0, 0);
188
+ if (c < 0) continue;
189
+ pthread_t t; pthread_attr_t attr;
190
+ pthread_attr_init(&attr);
191
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
192
+ if (pthread_create(&t, &attr, handle_conn, (void *)(intptr_t)c) != 0)
193
+ handle_conn((void *)(intptr_t)c);
194
+ pthread_attr_destroy(&attr);
195
+ }
196
+ }
v13/my_solution/ramnfs/shim.c ADDED
@@ -0,0 +1,761 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #define _GNU_SOURCE
2
+ #include <dlfcn.h>
3
+ #include <stdarg.h>
4
+ #include <string.h>
5
+ #include <stdlib.h>
6
+ #include <stdio.h>
7
+ #include <fcntl.h>
8
+ #include <unistd.h>
9
+ #include <sys/types.h>
10
+ #include <sys/stat.h>
11
+ #include <linux/stat.h>
12
+ #include <sys/socket.h>
13
+ #include <sys/un.h>
14
+ #include <dirent.h>
15
+ #include <errno.h>
16
+ #include <pthread.h>
17
+
18
+ static const char *g_sock = NULL;
19
+ static const char *g_prefix = NULL;
20
+ static size_t g_plen = 0;
21
+ static int g_debug = 0;
22
+
23
+ static void dbg(const char *fmt, ...) {
24
+ if (!g_debug) return;
25
+ va_list ap; va_start(ap, fmt);
26
+ vfprintf(stderr, fmt, ap);
27
+ va_end(ap);
28
+ fflush(stderr);
29
+ }
30
+
31
+ #define SQLITE_REAL_DIR "/tmp/cado-sqlite"
32
+
33
+ #define CWD_REAL_PREFIX "/tmp/ramnfs_cwd"
34
+ #define PATHMAX 1024
35
+
36
+ #define MAXDIRS 2048
37
+ static char g_dirs[MAXDIRS][1024];
38
+ static int g_ndirs = 0;
39
+ static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
40
+
41
+ __attribute__((constructor))
42
+ static void shim_init(void) {
43
+ const char *s = getenv("RAMNFS_SOCK");
44
+ const char *p = getenv("RAMNFS_PREFIX");
45
+ g_sock = s ? s : "/tmp/ramnfs.sock";
46
+ g_prefix = p ? p : "/ramwork";
47
+ g_plen = strlen(g_prefix);
48
+ g_debug = getenv("RAMNFS_DEBUG") != NULL;
49
+
50
+ mkdir(SQLITE_REAL_DIR, 0777);
51
+ }
52
+
53
+ static void ensure_real_dir(const char *path) {
54
+ static int (*real_mkdir)(const char *, mode_t) = NULL;
55
+ if (!real_mkdir) real_mkdir = dlsym(RTLD_NEXT, "mkdir");
56
+ char tmp[PATHMAX]; strncpy(tmp, path, PATHMAX-1); tmp[PATHMAX-1]=0;
57
+ for (char *p = tmp+1; *p; p++) {
58
+ if (*p == '/') { *p=0; real_mkdir(tmp, 0755); *p='/'; }
59
+ }
60
+ real_mkdir(tmp, 0755);
61
+ }
62
+
63
+ static const char *real_to_virt_cwd(const char *real_cwd) {
64
+ static const char *pfx = CWD_REAL_PREFIX;
65
+ size_t plen = strlen(pfx);
66
+ if (strncmp(real_cwd, pfx, plen) == 0
67
+ && (real_cwd[plen] == '/' || real_cwd[plen] == '\0')) {
68
+ static __thread char buf[PATHMAX];
69
+ snprintf(buf, PATHMAX, "%s%s", g_prefix, real_cwd + plen);
70
+ return buf;
71
+ }
72
+ return NULL;
73
+ }
74
+
75
+ static const char *resolve_relpath(const char *path) {
76
+ static __thread char buf[PATHMAX];
77
+ if (!path) return path;
78
+ if (path[0] == '/') {
79
+
80
+ if (!strstr(path, "//")) return path;
81
+ size_t j = 0;
82
+ for (size_t i = 0; path[i] && j + 1 < PATHMAX; i++) {
83
+ if (path[i] == '/' && j > 0 && buf[j-1] == '/') continue;
84
+ buf[j++] = path[i];
85
+ }
86
+ buf[j] = 0;
87
+ return buf;
88
+ }
89
+ char cwd[PATHMAX];
90
+ if (!getcwd(cwd, sizeof cwd)) return path;
91
+ const char *virt = real_to_virt_cwd(cwd);
92
+ if (!virt) return path;
93
+ if (path[0] == '.' && path[1] == '\0') {
94
+ snprintf(buf, PATHMAX, "%s", virt);
95
+ } else {
96
+ snprintf(buf, PATHMAX, "%s/%s", virt, path);
97
+ }
98
+
99
+ size_t n = strlen(buf);
100
+ while (n > 1 && buf[n-1] == '.' && buf[n-2] == '/') { n -= 2; buf[n] = '\0'; }
101
+ while (n > 1 && buf[n-1] == '/') buf[--n] = '\0';
102
+ return buf;
103
+ }
104
+
105
+ static int is_ramnfs(const char *path) {
106
+ if (!path || !g_prefix || g_plen == 0) return 0;
107
+ return strncmp(path, g_prefix, g_plen) == 0
108
+ && (path[g_plen] == '/' || path[g_plen] == '\0');
109
+ }
110
+
111
+ static int looks_ramnfs(const char *path) {
112
+ if (!path) return 0;
113
+ if (path[0] == '/') path = resolve_relpath(path);
114
+ return is_ramnfs(path);
115
+ }
116
+
117
+ static int is_sqlite_file(const char *path) {
118
+ if (!path) return 0;
119
+ size_t n = strlen(path);
120
+
121
+ if (n >= 3 && strcmp(path + n - 3, ".db") == 0) return 1;
122
+ if (n >= 11 && strcmp(path + n - 11, ".db-journal") == 0) return 1;
123
+ if (n >= 7 && strcmp(path + n - 7, ".db-wal") == 0) return 1;
124
+ if (n >= 7 && strcmp(path + n - 7, ".db-shm") == 0) return 1;
125
+ return 0;
126
+ }
127
+
128
+ static const char *sqlite_real_path(const char *ramnfs_path) {
129
+ static char buf[PATHMAX];
130
+
131
+ const char *rest = ramnfs_path + g_plen;
132
+ snprintf(buf, sizeof buf, SQLITE_REAL_DIR "%s", rest);
133
+
134
+ char parent[PATHMAX];
135
+ strncpy(parent, buf, sizeof parent - 1);
136
+ char *slash = strrchr(parent, '/');
137
+ if (slash && slash != parent) {
138
+ *slash = 0;
139
+
140
+ static int (*real_mkdir)(const char*, mode_t) = NULL;
141
+ if (!real_mkdir) real_mkdir = dlsym(RTLD_NEXT, "mkdir");
142
+ if (real_mkdir) {
143
+
144
+ char tmp[PATHMAX];
145
+ strncpy(tmp, SQLITE_REAL_DIR, sizeof tmp - 1);
146
+ const char *p = rest + 1;
147
+ while (p && *p) {
148
+ const char *next = strchr(p, '/');
149
+ if (!next) break;
150
+ int len = next - rest;
151
+ snprintf(tmp, sizeof tmp, SQLITE_REAL_DIR "%.*s", len, rest);
152
+ real_mkdir(tmp, 0777);
153
+ p = next + 1;
154
+ }
155
+ real_mkdir(parent, 0777);
156
+ }
157
+ }
158
+ return buf;
159
+ }
160
+
161
+ static int readn(int fd, void *buf, int n) {
162
+ int g = 0;
163
+ while (g < n) { int r = read(fd,(char*)buf+g,n-g); if(r<=0) return g; g+=r; }
164
+ return g;
165
+ }
166
+
167
+ static int broker_connect(void) {
168
+
169
+ for (int attempt = 0; attempt < 200; attempt++) {
170
+ int s = socket(AF_UNIX, SOCK_STREAM, 0);
171
+ if (s >= 0) {
172
+ struct sockaddr_un a = {0}; a.sun_family = AF_UNIX;
173
+ strncpy(a.sun_path, g_sock, sizeof a.sun_path - 1);
174
+ if (connect(s, (struct sockaddr *)&a, sizeof a) == 0) return s;
175
+ close(s);
176
+ }
177
+ usleep(attempt < 20 ? 500 : 3000);
178
+ }
179
+ return -1;
180
+ }
181
+
182
+ static void broker_send_req(int s, char op, const char *path) {
183
+ unsigned short len = (unsigned short)strlen(path);
184
+ write(s, &op, 1); write(s, &len, 2); write(s, path, len);
185
+ }
186
+
187
+ static void blog(const char *fmt, ...) {
188
+ if (!g_debug) return;
189
+ char buf[1200];
190
+ va_list ap; va_start(ap, fmt);
191
+ vsnprintf(buf, sizeof buf, fmt, ap);
192
+ va_end(ap);
193
+ int s = broker_connect();
194
+ if (s < 0) return;
195
+ broker_send_req(s, 'P', buf);
196
+ close(s);
197
+ }
198
+
199
+ static int broker_open_fd(const char *path) {
200
+ int s = broker_connect(); if (s < 0) return -1;
201
+ broker_send_req(s, 'O', path);
202
+ char status = 0;
203
+ struct iovec iov = {.iov_base=&status,.iov_len=1};
204
+ char cbuf[CMSG_SPACE(sizeof(int))]; memset(cbuf,0,sizeof cbuf);
205
+ struct msghdr msg = {.msg_iov=&iov,.msg_iovlen=1,
206
+ .msg_control=cbuf,.msg_controllen=sizeof cbuf};
207
+ int fd = -1;
208
+ if (recvmsg(s, &msg, 0) >= 1) {
209
+ struct cmsghdr *c = CMSG_FIRSTHDR(&msg);
210
+ if (c && c->cmsg_type == SCM_RIGHTS) memcpy(&fd, CMSG_DATA(c), sizeof(int));
211
+ }
212
+ close(s);
213
+ return (status == 'K') ? fd : (fd >= 0 ? (close(fd),-1) : -1);
214
+ }
215
+
216
+ static int broker_stat(const char *path, long long *sz_out) {
217
+ int s = broker_connect(); if (s < 0) return 0;
218
+ broker_send_req(s, 'S', path);
219
+ char st = 0; long long sz = 0;
220
+ readn(s, &st, 1); readn(s, &sz, 8); close(s);
221
+ if (sz_out) *sz_out = sz;
222
+ return st == 'K';
223
+ }
224
+
225
+ static void broker_unlink_path(const char *path) {
226
+ int s = broker_connect(); if (s < 0) return;
227
+ broker_send_req(s, 'U', path);
228
+ char st; readn(s, &st, 1); close(s);
229
+ }
230
+
231
+ static char **broker_listdir(const char *dir, int *count_out) {
232
+ *count_out = 0;
233
+ int s = broker_connect(); if (s < 0) return NULL;
234
+ broker_send_req(s, 'L', dir);
235
+ int cnt = 0; readn(s, &cnt, 4);
236
+ if (cnt <= 0) { close(s); return NULL; }
237
+ char **names = calloc(cnt, sizeof(char *));
238
+ for (int i = 0; i < cnt; i++) {
239
+ unsigned short nl = 0; readn(s, &nl, 2);
240
+ names[i] = malloc(nl + 1);
241
+ readn(s, names[i], nl); names[i][nl] = 0;
242
+ }
243
+ close(s);
244
+ *count_out = cnt;
245
+ return names;
246
+ }
247
+
248
+ static void record_dir(const char *path) {
249
+ pthread_mutex_lock(&g_mu);
250
+ for (int i = 0; i < g_ndirs; i++)
251
+ if (strcmp(g_dirs[i], path) == 0) { pthread_mutex_unlock(&g_mu); return; }
252
+ if (g_ndirs < MAXDIRS) strncpy(g_dirs[g_ndirs++], path, 1023);
253
+ pthread_mutex_unlock(&g_mu);
254
+ }
255
+
256
+ static int is_known_dir(const char *path) {
257
+ if (strcmp(path, g_prefix) == 0) return 1;
258
+ pthread_mutex_lock(&g_mu);
259
+ for (int i = 0; i < g_ndirs; i++)
260
+ if (strcmp(g_dirs[i], path) == 0) { pthread_mutex_unlock(&g_mu); return 1; }
261
+ pthread_mutex_unlock(&g_mu);
262
+ return 0;
263
+ }
264
+
265
+ int open(const char *path, int flags, ...) {
266
+ static int (*real)(const char*,int,...) = NULL;
267
+ if (!real) real = dlsym(RTLD_NEXT, "open");
268
+ path = resolve_relpath(path);
269
+ if (is_ramnfs(path)) {
270
+ if (is_sqlite_file(path)) {
271
+
272
+ const char *rpath = sqlite_real_path(path);
273
+ mode_t m = 0644;
274
+ if (flags & O_CREAT) { va_list v; va_start(v,flags); m=va_arg(v,mode_t); va_end(v); }
275
+ return real(rpath, flags, m);
276
+ }
277
+
278
+ size_t plen = strlen(path);
279
+ if (plen > g_plen && path[plen-1] == '/') { errno = EISDIR; return -1; }
280
+ if (is_known_dir(path)) { errno = EISDIR; return -1; }
281
+ if ((flags & O_CREAT) == 0) {
282
+ long long sz;
283
+ if (!broker_stat(path, &sz)) {
284
+ dbg("[shim] open FAIL path=%s flags=%d (ENOENT, not in broker)\n", path, flags);
285
+ errno = ENOENT; return -1;
286
+ }
287
+ }
288
+ int fd = broker_open_fd(path);
289
+ if (fd < 0) { dbg("[shim] open FAIL path=%s (EIO broker)\n", path); errno = EIO; return -1; }
290
+ if (flags & O_TRUNC) ftruncate(fd, 0);
291
+ if (flags & O_APPEND) lseek(fd, 0, SEEK_END);
292
+ else lseek(fd, 0, SEEK_SET);
293
+ return fd;
294
+ }
295
+ mode_t m = 0;
296
+ if (flags & O_CREAT) { va_list v; va_start(v,flags); m=va_arg(v,mode_t); va_end(v); }
297
+ return real(path, flags, m);
298
+ }
299
+
300
+ int open64(const char *path, int flags, ...) {
301
+ mode_t m = 0;
302
+ if (flags & O_CREAT) { va_list v; va_start(v,flags); m=va_arg(v,mode_t); va_end(v); }
303
+ return open(path, flags, m);
304
+ }
305
+
306
+ int openat(int dirfd, const char *path, int flags, ...) {
307
+ static int (*real)(int,const char*,int,...) = NULL;
308
+ if (!real) real = dlsym(RTLD_NEXT, "openat");
309
+
310
+ if (path && (path[0] == '/' || dirfd == AT_FDCWD))
311
+ path = resolve_relpath(path);
312
+ if (path && path[0]=='/' && is_ramnfs(path)) {
313
+ mode_t m = 0;
314
+ if (flags & O_CREAT) { va_list v; va_start(v,flags); m=va_arg(v,mode_t); va_end(v); }
315
+ return open(path, flags, m);
316
+ }
317
+ mode_t m = 0;
318
+ if (flags & O_CREAT) { va_list v; va_start(v,flags); m=va_arg(v,mode_t); va_end(v); }
319
+ return real ? real(dirfd, path, flags, m) : (errno=ENOSYS,-1);
320
+ }
321
+
322
+ int openat64(int dirfd, const char *path, int flags, ...) {
323
+ mode_t m = 0;
324
+ if (flags & O_CREAT) { va_list v; va_start(v,flags); m=va_arg(v,mode_t); va_end(v); }
325
+ return openat(dirfd, path, flags, m);
326
+ }
327
+
328
+ FILE *fopen(const char *path, const char *mode) {
329
+ static FILE *(*real)(const char*,const char*) = NULL;
330
+ if (!real) real = dlsym(RTLD_NEXT, "fopen");
331
+ path = resolve_relpath(path);
332
+ if (is_ramnfs(path)) {
333
+ if (is_sqlite_file(path)) {
334
+ return real(sqlite_real_path(path), mode);
335
+ }
336
+ int flags;
337
+ if (mode[0]=='r' && !strchr(mode,'+')) flags = O_RDONLY;
338
+ else if (mode[0]=='w' && !strchr(mode,'+')) flags = O_WRONLY|O_CREAT|O_TRUNC;
339
+ else if (mode[0]=='a') flags = O_WRONLY|O_CREAT|O_APPEND;
340
+ else flags = O_RDWR|O_CREAT;
341
+ int fd = open(path, flags, 0666);
342
+ if (fd < 0) return NULL;
343
+ char m2[8]={0}; strncpy(m2, mode, 7);
344
+ FILE *f = fdopen(fd, m2);
345
+ if (!f) close(fd);
346
+ return f;
347
+ }
348
+ return real(path, mode);
349
+ }
350
+
351
+ FILE *fopen64(const char *path, const char *mode) { return fopen(path, mode); }
352
+
353
+ int creat(const char *path, mode_t mode) {
354
+ return open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
355
+ }
356
+
357
+ static void fill_stat_dir(struct stat *buf) {
358
+ memset(buf, 0, sizeof *buf);
359
+ buf->st_mode = S_IFDIR | 0755; buf->st_nlink = 2;
360
+ }
361
+ static void fill_stat_file(struct stat *buf, long long sz) {
362
+ memset(buf, 0, sizeof *buf);
363
+ buf->st_mode = S_IFREG | 0755; buf->st_nlink = 1; buf->st_size = sz;
364
+ }
365
+
366
+ int stat(const char *path, struct stat *buf) {
367
+ static int (*real)(const char*,struct stat*) = NULL;
368
+ if (!real) real = dlsym(RTLD_NEXT, "stat");
369
+ path = resolve_relpath(path);
370
+ if (is_ramnfs(path)) {
371
+ if (is_sqlite_file(path)) return real(sqlite_real_path(path), buf);
372
+ if (is_known_dir(path)) { fill_stat_dir(buf); return 0; }
373
+ long long sz = 0;
374
+ if (broker_stat(path, &sz)) { fill_stat_file(buf, sz); return 0; }
375
+ errno = ENOENT; return -1;
376
+ }
377
+ return real(path, buf);
378
+ }
379
+
380
+ int lstat(const char *path, struct stat *buf) { return stat(resolve_relpath(path), buf); }
381
+
382
+ int stat64(const char *path, struct stat64 *buf) {
383
+ static int (*real)(const char*,struct stat64*) = NULL;
384
+ if (!real) real = dlsym(RTLD_NEXT, "stat64");
385
+ path = resolve_relpath(path);
386
+ if (is_ramnfs(path)) {
387
+ if (is_sqlite_file(path)) return real(sqlite_real_path(path), buf);
388
+ if (is_known_dir(path)) {
389
+ memset(buf, 0, sizeof *buf);
390
+ buf->st_mode = S_IFDIR | 0755; buf->st_nlink = 2; return 0;
391
+ }
392
+ long long sz = 0;
393
+ if (broker_stat(path, &sz)) {
394
+ memset(buf, 0, sizeof *buf);
395
+ buf->st_mode = S_IFREG | 0644; buf->st_nlink = 1;
396
+ buf->st_size = (off64_t)sz; return 0;
397
+ }
398
+ errno = ENOENT; return -1;
399
+ }
400
+ return real(path, buf);
401
+ }
402
+ int lstat64(const char *path, struct stat64 *buf) { return stat64(resolve_relpath(path), buf); }
403
+
404
+ int fstatat(int dirfd, const char *path, struct stat *buf, int flags) {
405
+ static int (*real)(int,const char*,struct stat*,int) = NULL;
406
+ if (!real) real = dlsym(RTLD_NEXT, "fstatat");
407
+ if (path && (path[0] == '/' || dirfd == AT_FDCWD)) path = resolve_relpath(path);
408
+ if (path && path[0]=='/' && is_ramnfs(path)) {
409
+ if (is_sqlite_file(path)) return real(AT_FDCWD, sqlite_real_path(path), buf, flags);
410
+ return stat(path, buf);
411
+ }
412
+ return real ? real(dirfd, path, buf, flags) : (errno=ENOSYS,-1);
413
+ }
414
+
415
+ int fstatat64(int dirfd, const char *path, struct stat64 *buf, int flags) {
416
+ static int (*real)(int,const char*,struct stat64*,int) = NULL;
417
+ if (!real) real = dlsym(RTLD_NEXT, "fstatat64");
418
+ if (path && (path[0] == '/' || dirfd == AT_FDCWD)) path = resolve_relpath(path);
419
+ if (path && path[0]=='/' && is_ramnfs(path)) {
420
+ if (is_sqlite_file(path)) return real(AT_FDCWD, sqlite_real_path(path), buf, flags);
421
+ if (is_known_dir(path)) {
422
+ memset(buf, 0, sizeof *buf);
423
+ buf->st_mode = S_IFDIR | 0755; buf->st_nlink = 2;
424
+ return 0;
425
+ }
426
+ long long sz = 0;
427
+ if (broker_stat(path, &sz)) {
428
+ memset(buf, 0, sizeof *buf);
429
+ buf->st_mode = S_IFREG | 0644; buf->st_nlink = 1;
430
+ buf->st_size = (off64_t)sz;
431
+ return 0;
432
+ }
433
+ errno = ENOENT; return -1;
434
+ }
435
+ return real ? real(dirfd, path, buf, flags) : (errno=ENOSYS,-1);
436
+ }
437
+
438
+ int statx(int dirfd, const char *path, int flags, unsigned int mask,
439
+ struct statx *stxbuf) {
440
+ static int (*real)(int,const char*,int,unsigned,struct statx*) = NULL;
441
+ if (!real) real = dlsym(RTLD_NEXT, "statx");
442
+ if (path && (path[0] == '/' || dirfd == AT_FDCWD)) path = resolve_relpath(path);
443
+ if (path && path[0]=='/' && is_ramnfs(path)) {
444
+ if (is_sqlite_file(path)) return real(AT_FDCWD, sqlite_real_path(path), flags, mask, stxbuf);
445
+ if (is_known_dir(path)) {
446
+ memset(stxbuf, 0, sizeof *stxbuf);
447
+ stxbuf->stx_mode = S_IFDIR | 0755;
448
+ stxbuf->stx_nlink = 2;
449
+ stxbuf->stx_mask = STATX_BASIC_STATS;
450
+ return 0;
451
+ }
452
+ long long sz = 0;
453
+ if (broker_stat(path, &sz)) {
454
+ memset(stxbuf, 0, sizeof *stxbuf);
455
+ stxbuf->stx_mode = S_IFREG | 0644;
456
+ stxbuf->stx_nlink = 1;
457
+ stxbuf->stx_size = (unsigned long long)sz;
458
+ stxbuf->stx_mask = STATX_BASIC_STATS;
459
+ return 0;
460
+ }
461
+ errno = ENOENT; return -1;
462
+ }
463
+ return real ? real(dirfd, path, flags, mask, stxbuf) : (errno=ENOSYS,-1);
464
+ }
465
+
466
+ int __xstat(int ver, const char *path, struct stat *buf) {
467
+ static int (*real)(int,const char*,struct stat*) = NULL;
468
+ if (!real) real = dlsym(RTLD_NEXT, "__xstat");
469
+ if (looks_ramnfs(path)) return stat(path, buf);
470
+ return real ? real(ver, path, buf) : (errno=ENOSYS,-1);
471
+ }
472
+ int __lxstat(int ver, const char *path, struct stat *buf) { return __xstat(ver,path,buf); }
473
+ int __xstat64(int ver, const char *path, struct stat *buf) { return __xstat(ver,path,buf); }
474
+ int __lxstat64(int ver, const char *path, struct stat *buf) { return __xstat(ver,path,buf); }
475
+
476
+ int access(const char *path, int mode) {
477
+ static int (*real)(const char*,int) = NULL;
478
+ if (!real) real = dlsym(RTLD_NEXT, "access");
479
+ path = resolve_relpath(path);
480
+ if (is_ramnfs(path)) {
481
+ if (is_sqlite_file(path)) return real(sqlite_real_path(path), mode);
482
+ if (is_known_dir(path)) return 0;
483
+ if (broker_stat(path, NULL)) return 0;
484
+ blog("ACCESS ramnfs MISS '%s' mode=%d", path, mode);
485
+ errno = ENOENT; return -1;
486
+ }
487
+ if (strstr(path, ".gz")) blog("ACCESS non-ramnfs gz '%s'", path);
488
+ int r = real(path, mode);
489
+ if (r != 0 && strstr(path, ".gz")) blog("ACCESS real FAIL '%s' errno=%d", path, errno);
490
+ return r;
491
+ }
492
+
493
+ int faccessat(int dirfd, const char *path, int mode, int flags) {
494
+ static int (*real)(int,const char*,int,int) = NULL;
495
+ if (!real) real = dlsym(RTLD_NEXT, "faccessat");
496
+ if (looks_ramnfs(path)) return access(path, mode);
497
+ return real ? real(dirfd, path, mode, flags) : (errno=ENOSYS,-1);
498
+ }
499
+
500
+ int unlink(const char *path) {
501
+ static int (*real)(const char*) = NULL;
502
+ if (!real) real = dlsym(RTLD_NEXT, "unlink");
503
+ path = resolve_relpath(path);
504
+ if (is_ramnfs(path)) {
505
+ if (is_sqlite_file(path)) return real(sqlite_real_path(path));
506
+ broker_unlink_path(path); return 0;
507
+ }
508
+ return real(path);
509
+ }
510
+
511
+ int unlinkat(int dirfd, const char *path, int flags) {
512
+ static int (*real)(int,const char*,int) = NULL;
513
+ if (!real) real = dlsym(RTLD_NEXT, "unlinkat");
514
+ if (looks_ramnfs(path)) return unlink(path);
515
+ return real ? real(dirfd, path, flags) : (errno=ENOSYS,-1);
516
+ }
517
+
518
+ int rename(const char *oldp, const char *newp) {
519
+ static int (*real)(const char*,const char*) = NULL;
520
+ if (!real) real = dlsym(RTLD_NEXT, "rename");
521
+
522
+ char old_buf[PATHMAX], new_buf[PATHMAX];
523
+ strncpy(old_buf, resolve_relpath(oldp), PATHMAX-1); old_buf[PATHMAX-1]=0; oldp=old_buf;
524
+ strncpy(new_buf, resolve_relpath(newp), PATHMAX-1); new_buf[PATHMAX-1]=0; newp=new_buf;
525
+ if (is_ramnfs(oldp) || is_ramnfs(newp)) {
526
+
527
+ if (is_sqlite_file(oldp) || is_sqlite_file(newp)) {
528
+ const char *ro = is_sqlite_file(oldp) ? sqlite_real_path(oldp) : oldp;
529
+ const char *rn = is_sqlite_file(newp) ? sqlite_real_path(newp) : newp;
530
+ return real(ro, rn);
531
+ }
532
+ int src = broker_open_fd(oldp);
533
+ int dst = broker_open_fd(newp);
534
+ if (src < 0 || dst < 0) {
535
+ if (src>=0) close(src); if (dst>=0) close(dst);
536
+ errno = EIO; return -1;
537
+ }
538
+ ftruncate(dst, 0); lseek(src, 0, SEEK_SET);
539
+ char buf[131072]; ssize_t r;
540
+ while ((r = read(src, buf, sizeof buf)) > 0) write(dst, buf, r);
541
+ close(src); close(dst);
542
+ broker_unlink_path(oldp);
543
+ return 0;
544
+ }
545
+ return real(oldp, newp);
546
+ }
547
+
548
+ int renameat(int od, const char *op, int nd, const char *np) {
549
+ if (looks_ramnfs(op)) return rename(op, np);
550
+ static int (*real)(int,const char*,int,const char*) = NULL;
551
+ if (!real) real = dlsym(RTLD_NEXT, "renameat");
552
+ return real ? real(od, op, nd, np) : (errno=ENOSYS,-1);
553
+ }
554
+
555
+ int symlink(const char *target, const char *linkpath) {
556
+ static int (*real)(const char*,const char*) = NULL;
557
+ if (!real) real = dlsym(RTLD_NEXT, "symlink");
558
+
559
+ char lp_buf[PATHMAX];
560
+ strncpy(lp_buf, resolve_relpath(linkpath), PATHMAX-1); lp_buf[PATHMAX-1]=0;
561
+ if (is_ramnfs(lp_buf)) {
562
+
563
+ char src_path[PATHMAX];
564
+ if (target[0] == '/') {
565
+ strncpy(src_path, target, PATHMAX-1); src_path[PATHMAX-1]=0;
566
+ } else {
567
+
568
+ strncpy(src_path, lp_buf, PATHMAX-1);
569
+ char *slash = strrchr(src_path, '/');
570
+ if (slash) { slash[1] = '\0'; strncat(src_path, target, PATHMAX-1-strlen(src_path)); }
571
+ else strncpy(src_path, target, PATHMAX-1);
572
+ }
573
+ if (!is_ramnfs(src_path)) { errno = EXDEV; return -1; }
574
+
575
+ int src = broker_open_fd(src_path);
576
+ int dst = broker_open_fd(lp_buf);
577
+ if (src < 0 || dst < 0) {
578
+ if (src>=0) close(src); if (dst>=0) close(dst);
579
+ errno = ENOENT; return -1;
580
+ }
581
+ ftruncate(dst, 0); lseek(src, 0, SEEK_SET);
582
+ char buf[131072]; ssize_t r;
583
+ while ((r = read(src, buf, sizeof buf)) > 0) write(dst, buf, r);
584
+ close(src); close(dst);
585
+ return 0;
586
+ }
587
+ return real ? real(target, linkpath) : (errno=ENOSYS,-1);
588
+ }
589
+
590
+ int symlinkat(const char *target, int newdirfd, const char *linkpath) {
591
+ if (looks_ramnfs(linkpath))
592
+ return symlink(target, linkpath);
593
+ static int (*real)(const char*,int,const char*) = NULL;
594
+ if (!real) real = dlsym(RTLD_NEXT, "symlinkat");
595
+ return real ? real(target, newdirfd, linkpath) : (errno=ENOSYS,-1);
596
+ }
597
+
598
+ int mkdir(const char *path, mode_t mode) {
599
+ static int (*real)(const char*,mode_t) = NULL;
600
+ if (!real) real = dlsym(RTLD_NEXT, "mkdir");
601
+ path = resolve_relpath(path);
602
+ if (is_ramnfs(path)) { record_dir(path); return 0; }
603
+ return real(path, mode);
604
+ }
605
+
606
+ int mkdirat(int dirfd, const char *path, mode_t mode) {
607
+ static int (*real)(int,const char*,mode_t) = NULL;
608
+ if (!real) real = dlsym(RTLD_NEXT, "mkdirat");
609
+ if (looks_ramnfs(path)) return mkdir(path, mode);
610
+ return real ? real(dirfd, path, mode) : (errno=ENOSYS,-1);
611
+ }
612
+
613
+ int rmdir(const char *path) {
614
+ static int (*real)(const char*) = NULL;
615
+ if (!real) real = dlsym(RTLD_NEXT, "rmdir");
616
+ if (looks_ramnfs(path)) return 0;
617
+ return real(path);
618
+ }
619
+
620
+ int chdir(const char *path) {
621
+ static int (*real)(const char*) = NULL;
622
+ if (!real) real = dlsym(RTLD_NEXT, "chdir");
623
+ if (path && path[0]=='/') path = resolve_relpath(path);
624
+ if (is_ramnfs(path)) {
625
+ char real_dir[PATHMAX];
626
+ const char *rest = path + g_plen;
627
+ snprintf(real_dir, PATHMAX, CWD_REAL_PREFIX "%s", rest);
628
+ ensure_real_dir(real_dir);
629
+ return real(real_dir);
630
+ }
631
+ return real(path);
632
+ }
633
+
634
+ int chmod(const char *path, mode_t mode) {
635
+ static int (*real)(const char*,mode_t) = NULL;
636
+ if (!real) real = dlsym(RTLD_NEXT, "chmod");
637
+ path = resolve_relpath(path);
638
+ if (is_ramnfs(path)) {
639
+ if (is_sqlite_file(path)) return real(sqlite_real_path(path), mode);
640
+ return 0;
641
+ }
642
+ return real(path, mode);
643
+ }
644
+
645
+ int lchmod(const char *path, mode_t mode) { return chmod(path, mode); }
646
+
647
+ int fchmodat(int dirfd, const char *path, mode_t mode, int flags) {
648
+ static int (*real)(int,const char*,mode_t,int) = NULL;
649
+ if (!real) real = dlsym(RTLD_NEXT, "fchmodat");
650
+ if (looks_ramnfs(path)) return chmod(path, mode);
651
+ return real ? real(dirfd, path, mode, flags) : (errno=ENOSYS,-1);
652
+ }
653
+
654
+ int chown(const char *path, uid_t uid, gid_t gid) {
655
+ static int (*real)(const char*,uid_t,gid_t) = NULL;
656
+ if (!real) real = dlsym(RTLD_NEXT, "chown");
657
+ if (looks_ramnfs(path)) return 0;
658
+ return real(path, uid, gid);
659
+ }
660
+
661
+ int lchown(const char *path, uid_t uid, gid_t gid) { return chown(path, uid, gid); }
662
+
663
+ int truncate(const char *path, off_t len) {
664
+ static int (*real)(const char*,off_t) = NULL;
665
+ if (!real) real = dlsym(RTLD_NEXT, "truncate");
666
+ path = resolve_relpath(path);
667
+ if (is_ramnfs(path)) {
668
+ if (is_sqlite_file(path)) return real(sqlite_real_path(path), len);
669
+ int fd = broker_open_fd(path);
670
+ if (fd < 0) { errno = ENOENT; return -1; }
671
+ int r = ftruncate(fd, len); close(fd); return r;
672
+ }
673
+ return real(path, len);
674
+ }
675
+
676
+ #define FAKEDIR_MAGIC 0x52414d46U
677
+
678
+ typedef struct {
679
+ unsigned int magic;
680
+ char **names;
681
+ int count;
682
+ int idx;
683
+ struct dirent64 de64;
684
+ struct dirent de;
685
+ } FakeDir;
686
+
687
+ DIR *opendir(const char *name) {
688
+ static DIR *(*real)(const char*) = NULL;
689
+ if (!real) real = dlsym(RTLD_NEXT, "opendir");
690
+ name = resolve_relpath(name);
691
+ if (is_ramnfs(name)) {
692
+ long long sz;
693
+ if (broker_stat(name, &sz)) { errno = ENOTDIR; return NULL; }
694
+ record_dir(name);
695
+ int cnt = 0;
696
+ char **names = broker_listdir(name, &cnt);
697
+ FakeDir *fd = calloc(1, sizeof(FakeDir));
698
+ fd->magic = FAKEDIR_MAGIC;
699
+ fd->names = names; fd->count = cnt; fd->idx = 0;
700
+ return (DIR *)fd;
701
+ }
702
+ return real(name);
703
+ }
704
+
705
+ struct dirent *readdir(DIR *dirp) {
706
+ static struct dirent *(*real)(DIR*) = NULL;
707
+ if (!real) real = dlsym(RTLD_NEXT, "readdir");
708
+ if (dirp && ((FakeDir *)dirp)->magic == FAKEDIR_MAGIC) {
709
+ FakeDir *fd = (FakeDir *)dirp;
710
+ if (fd->idx >= fd->count) return NULL;
711
+ memset(&fd->de, 0, sizeof fd->de);
712
+ strncpy(fd->de.d_name, fd->names[fd->idx++], sizeof fd->de.d_name - 1);
713
+ fd->de.d_type = DT_REG;
714
+ return &fd->de;
715
+ }
716
+ return real(dirp);
717
+ }
718
+
719
+ struct dirent64 *readdir64(DIR *dirp) {
720
+ static struct dirent64 *(*real)(DIR*) = NULL;
721
+ if (!real) real = dlsym(RTLD_NEXT, "readdir64");
722
+ if (dirp && ((FakeDir *)dirp)->magic == FAKEDIR_MAGIC) {
723
+ FakeDir *fd = (FakeDir *)dirp;
724
+ if (fd->idx >= fd->count) return NULL;
725
+ memset(&fd->de64, 0, sizeof fd->de64);
726
+ strncpy(fd->de64.d_name, fd->names[fd->idx++], sizeof fd->de64.d_name - 1);
727
+ fd->de64.d_type = DT_REG;
728
+ return &fd->de64;
729
+ }
730
+ return real(dirp);
731
+ }
732
+
733
+ int closedir(DIR *dirp) {
734
+ static int (*real)(DIR*) = NULL;
735
+ if (!real) real = dlsym(RTLD_NEXT, "closedir");
736
+ if (dirp && ((FakeDir *)dirp)->magic == FAKEDIR_MAGIC) {
737
+ FakeDir *fd = (FakeDir *)dirp;
738
+ for (int i = 0; i < fd->count; i++) free(fd->names[i]);
739
+ free(fd->names); free(fd);
740
+ return 0;
741
+ }
742
+ return real(dirp);
743
+ }
744
+
745
+ int fstat(int fd, struct stat *buf) {
746
+ static int (*real)(int, struct stat*) = NULL;
747
+ if (!real) real = dlsym(RTLD_NEXT, "fstat");
748
+ int rc = real(fd, buf);
749
+ if (rc == 0 && S_ISREG(buf->st_mode) && buf->st_nlink == 0)
750
+ buf->st_nlink = 1;
751
+ return rc;
752
+ }
753
+
754
+ int fstat64(int fd, struct stat64 *buf) {
755
+ static int (*real)(int, struct stat64*) = NULL;
756
+ if (!real) real = dlsym(RTLD_NEXT, "fstat64");
757
+ int rc = real(fd, buf);
758
+ if (rc == 0 && S_ISREG(buf->st_mode) && buf->st_nlink == 0)
759
+ buf->st_nlink = 1;
760
+ return rc;
761
+ }