Upload folder using huggingface_hub
Browse files- v13/breaking_rsa_solution.zip +3 -0
- v13/my_solution/.dockerignore +5 -0
- v13/my_solution/Dockerfile +55 -0
- v13/my_solution/breaking_rsa.py +254 -0
- v13/my_solution/cado-nfs.tar.gz +3 -0
- v13/my_solution/enigma_challenges/__init__.py +128 -0
- v13/my_solution/enigma_challenges/breaking_rsa.py +86 -0
- v13/my_solution/enigma_challenges/solution_output.py +67 -0
- v13/my_solution/ramnfs/broker.c +196 -0
- v13/my_solution/ramnfs/shim.c +761 -0
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 |
+
}
|