From ba996acfcad1698adbf4a1834fe50e73b1c6cab9 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sat, 19 Sep 2026 23:28:22 -0300 Subject: Rename programs: 9player -> 9ns, introspect -> 9proc, app -> web (9web) Directories, binaries, build options (-D9ns, -D9proc), step names, module name (9proc), thread and fs names, env var NINEPLAYER_MOUNT -> NINE_MOUNT, docs and test scripts. Browser assets move to web/static. Co-Authored-By: Claude Fable 5.1 --- 9player/test/adv_bridge_hostile.py | 487 ------------------------------------- 1 file changed, 487 deletions(-) delete mode 100755 9player/test/adv_bridge_hostile.py (limited to '9player/test/adv_bridge_hostile.py') diff --git a/9player/test/adv_bridge_hostile.py b/9player/test/adv_bridge_hostile.py deleted file mode 100755 index b842a1a..0000000 --- a/9player/test/adv_bridge_hostile.py +++ /dev/null @@ -1,487 +0,0 @@ -#!/usr/bin/env python3 -"""A scriptable, hostile 9P2000 server on a Unix socket (stdlib only). - -Usage: adv_bridge_hostile.py SOCKET MODE - -Serves a tiny in-memory tree: - /f "hello world\\n" - /d/g "in d\\n" - /fids reading it returns the number of fids currently bound - /big 1 MiB of pseudo-random bytes -plus create/write/remove/wstat so the scratch battery can run in `ok` mode. - -MODE selects one misbehaviour (see MODES below). Everything not covered by -the mode behaves normally, so 9player gets through version/attach/stat(root). -""" -import os -import random -import socket -import struct -import sys -import time - -NOTAG = 0xFFFF -NOFID = 0xFFFFFFFF -QTDIR = 0x80 -DMDIR = 0x80000000 - -Tversion, Rversion = 100, 101 -Tauth, Rauth = 102, 103 -Tattach, Rattach = 104, 105 -Rerror = 107 -Tflush, Rflush = 108, 109 -Twalk, Rwalk = 110, 111 -Topen, Ropen = 112, 113 -Tcreate, Rcreate = 114, 115 -Tread, Rread = 116, 117 -Twrite, Rwrite = 118, 119 -Tclunk, Rclunk = 120, 121 -Tremove, Rremove = 122, 123 -Tstat, Rstat = 124, 125 -Twstat, Rwstat = 126, 127 - -MODES = """ -ok behave (qid paths are recycled LIFO after remove, like many servers) -trunc Rread on /f: send half the frame, then close -short_frame Rread on /f: frame whose size field is 3 -huge_frame Rread on /f: frame whose size field is msize+1 -wrong_tag Rread on /f: reply carries tag+1 -wrong_type Tstat on /f: answer with an Rwalk -rread_big Rread on /f: count = requested+1 -rwalk_many Twalk to f: nwqid = nwname+1 -rwalk_zero Twalk to nope: Rwalk nwqid=0 instead of Rerror -rstat_garbage Tstat on /f: random bytes as the stat -rstat_overlong Tstat on /f: inner stat size disagrees with outer -dir_split Tread on /: a stat record split across two Rreads -dir_forever Tread on /: ignore offset, always return the same records -qid_collide every file and dir shares qid.path 7 (root keeps its own) -qid_zero every qid.path is 0, including the root -name_slash / has an entry "a/b" -name_empty / has an entry "" -name_huge / has an entry with a 60000-byte name -name_dots / lists "." and ".." too -rerror_big Twalk to nope: Rerror with 65535 bytes of text -extra_reply Rread on /f: an unsolicited Rclunk (tag 9) precedes the real reply -never Tread on /f: never reply (hang) -close_mid Tread on /f: close the socket without replying -renegotiate Tread on /f: an unsolicited Rversion precedes the real reply -length_max Tstat on /f: length = 2**64-1 -iounit_one Ropen: iounit = 1 -rwrite_big Rwrite: count = requested+1 -msize_tiny Rversion msize = 64 -version_unknown Rversion "unknown" -rename_fail Twstat with a new name always fails "file already exists" -slow every reply delayed 20 ms (for interrupt tests) -""" - - -def s8(x): return struct.pack('= 4: - n = struct.unpack('= n: - msg, buf = buf[:n], buf[n:] - out = self.handle(msg) - if out is None: - return # hang up / hang - if self.mode == 'slow': - time.sleep(0.02) - conn.sendall(out) - continue - data = conn.recv(65536) - if not data: - return - buf += data - - def handle(self, msg): - typ = msg[4] - tag = struct.unpack(' len(node.content): - node.content.extend(b'\0' * (off - len(node.content))) - node.content[off:off + len(data)] = data - node.mtime = int(time.time()) - n = len(data) + 1 if self.mode == 'rwrite_big' else len(data) - return self.frame(Rwrite, tag, s32(n)) - if typ == Tclunk: - fid = r.u32() - if fid not in self.fids: - return self.err(tag, 'unknown fid') - del self.fids[fid] - return self.frame(Rclunk, tag, b'') - if typ == Tremove: - fid = r.u32() - if fid not in self.fids: - return self.err(tag, 'unknown fid') - node = self.fids[fid][0] - del self.fids[fid] - if node is self.root: - return self.err(tag, 'cannot remove root') - if node.isdir and node.children: - return self.err(tag, 'directory not empty') - parent = self.find_parent(self.root, node) - if parent is not None: - del parent.children[node.name] - self.free_paths.append(node.path) - node.removed = True - return self.frame(Rremove, tag, b'') - if typ == Tstat: - fid = r.u32() - if fid not in self.fids: - return self.err(tag, 'unknown fid') - node = self.fids[fid][0] - if node is self.root.children.get('f'): - m = self.mode - if m == 'wrong_type': - return self.frame(Rwalk, tag, s16(0)) - if m == 'rstat_garbage': - junk = bytes([0xAB] * 60) - return self.frame(Rstat, tag, s16(len(junk)) + junk) - if m == 'rstat_overlong': - st = node.stat_bytes(self) - inner = st[2:] - return self.frame(Rstat, tag, s16(len(inner) + 5) + inner) - if m == 'length_max': - st = node.stat_bytes(self, length=2 ** 64 - 1) - return self.frame(Rstat, tag, s16(len(st)) + st) - st = node.stat_bytes(self) - return self.frame(Rstat, tag, s16(len(st)) + st) - if typ == Twstat: - fid = r.u32() - r.u16() - st = r.bytes(r.u16()) - if fid not in self.fids: - return self.err(tag, 'unknown fid') - node = self.fids[fid][0] - sr = Reader(st) - sr.u16(); sr.u32(); sr.bytes(13) - mode = sr.u32(); sr.u32(); mtime = sr.u32(); length = sr.u64() - name = sr.str() - if name and name != node.name: - if self.mode == 'rename_fail': - return self.err(tag, 'file already exists') - parent = self.find_parent(self.root, node) - if name in parent.children: - return self.err(tag, 'file already exists') - del parent.children[node.name] - node.name = name - parent.children[name] = node - if mode != 0xFFFFFFFF: - node.mode = mode & 0o777 - if mtime != 0xFFFFFFFF: - node.mtime = mtime - if length != 0xFFFFFFFFFFFFFFFF and not node.isdir: - if length < len(node.content): - del node.content[length:] - else: - node.content.extend(b'\0' * (length - len(node.content))) - return self.frame(Rwstat, tag, b'') - return self.err(tag, 'unsupported message') - - def find_parent(self, cur, node): - for c in cur.children.values(): - if c is node: - return cur - if c.isdir: - p = self.find_parent(c, node) - if p is not None: - return p - return None - - def readdir(self, tag, node, off, count): - recs = [] - if node is self.root: - m = self.mode - if m == 'name_slash': - recs.append(node.stat_bytes(self, name='a/b')) - if m == 'name_empty': - recs.append(node.stat_bytes(self, name='')) - if m == 'name_huge': - recs.append(node.stat_bytes(self, name='h' * 60000)) - if m == 'name_dots': - recs.append(node.stat_bytes(self, name='.')) - recs.append(node.stat_bytes(self, name='..')) - for c in node.children.values(): - recs.append(c.stat_bytes(self)) - blob = b''.join(recs) - if node is self.root and self.mode == 'dir_forever': - return self.frame(Rread, tag, s32(len(blob)) + blob) - if node is self.root and self.mode == 'dir_split': - # first read: up to the middle of the second record; second read: the rest - cut = len(recs[0]) + len(recs[1]) // 2 - if off == 0: - data = blob[:cut] - elif off == cut: - data = blob[cut:] - else: - data = b'' - return self.frame(Rread, tag, s32(len(data)) + data) - # 9P rule: offset 0 or previous offset+count; never split a record. - out = b'' - pos = 0 - for rec in recs: - if pos >= off and len(out) + len(rec) <= count: - out += rec - elif pos >= off: - break - pos += len(rec) - return self.frame(Rread, tag, s32(len(out)) + out) - - -class Reader: - def __init__(self, b): - self.b = b - self.i = 0 - - def bytes(self, n): - v = self.b[self.i:self.i + n] - self.i += n - return v - - def u8(self): return struct.unpack('