1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
# 2002-9P2000.9p - Definitions of 9P2000 messages
#
# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-Licence-Identifier: AGPL-3.0-or-later
# "9P2000" base protocol
# https://ericvh.github.io/9p-rfc/rfc9p2000.html
# https://github.com/ericvh/9p-rfc/blob/master/9p2000.xml
#
# But due to incompleteness of the draft RFC, the Plan 9 manual
# section-5 and the Plan 9 headers (particularly fcall.h) are often
# better references.
#
# https://github.com/plan9foundation/plan9/tree/main/sys/man/5
# https://man.cat-v.org/plan_9/5/
# https://github.com/plan9foundation/plan9/blob/main/sys/include/fcall.h
version "9P2000"
# tag - identify a request/response pair
num tag = 2
# file identifier - like a UNIX file-descriptor
num fid = 4
# data - u32le `n`, then `n` bytes of data
struct d = "len[4] len*(dat[1])"
# string - u16le `n`, then `n` bytes of UTF-8, without any nul-bytes
struct s = "len[2] len*(utf8[1])"
# "d"? mode - file permissions and attributes
bitfield dm = 4
"31=DIR"
"30=APPEND"
"29=EXCL"
# DMMOUNT has been around in Plan 9 forever, but is
# undocumented, and is explicitly excluded from the 9P2000
# draft RFC. As I understand it, DMMOUNT indicates that the
# file is mounted by the kernel as a 9P transport; that the
# kernel has a lock on doing I/O on it, so userspace can't do
# I/O on it.
"28=_PLAN9_MOUNT"
"27=AUTH"
"26=TMP"
#...
"8=OWNER_R"
"7=OWNER_W"
"6=OWNER_X"
"5=GROUP_R"
"4=GROUP_W"
"3=GROUP_X"
"2=OTHER_R"
"1=OTHER_W"
"0=OTHER_X"
"PERM_MASK=0777" # {OWNER,GROUP,OTHER}_{R,W,X}
# QID Type - see `struct qid` below
bitfield qt = 1
"7=DIR"
"6=APPEND"
"5=EXCL"
"4=_PLAN9_MOUNT" # see "MOUNT" in "dm" above
"3=AUTH"
# Fun historical fact: QTTMP was a relatively late addition to
# Plan 9, in 2003-12.
"2=TMP"
#"1=unused"
# "The name QTFILE, defined to be zero, identifies the value
# of the type for a plain file."
"FILE=0"
# uni"Q"ue "ID"entification - "two files on the same server hierarchy
# are the same if and only if their qids are the same"
#
# - "path" is a unique uint64_t that does most of the work in the
# above statement about files being the same if their QIDs are the
# same; " If a file is deleted and recreated with the same name in
# the same directory, the old and new path components of the qids
# should be different"
#
# - "vers" "is a version number for a file; typically, it is
# incremented every time the file is modified.
#
# - "type" indicates "whether the file is a directory, append-only
# file, etc."; is an instance of the qt bitfield.
struct qid = "type[qt] vers[4] path[8]"
# stat - file status
struct stat = "stat_size[2,val=end-&kern_type]"
"kern_type[2]"
"kern_dev[4]"
"file_qid[qid]"
"file_mode[dm]"
"file_atime[4]"
"file_mtime[4]"
"file_size[8]"
"file_name[s]"
"file_owner_uid[s]"
"file_owner_gid[s]"
"file_last_modified_uid[s]"
# "O"pen flags (flags to pass to Topen and Tcreate)
bitfield o = 1
"0=mode_0" # low bit of the 2-bit READ/WRITE/RDWR/EXEC enum
"1=mode_1" # high bit of the 2-bit READ/WRITE/RDWR/EXEC enum"
#"2=unused"
#"3=unused"
"4=TRUNC"
#"5=unused"
"6=RCLOSE" # remove-on-close
#"7=unused"
"READ = 0" # make available for this FID: Tread()
"WRITE = 1" # make available for this FID: Twrite()
"RDWR = 2" # make available for this FID: Tread() and Twrite()
"EXEC = 3" # make available for this FID: Tread()
"MODE_MASK = 0b00000011"
"FLAG_MASK = 0b11111100"
msg Tversion = "size[4,val=end-&size] typ[1,val=100] tag[tag] max_msg_size[4] version[s]"
msg Rversion = "size[4,val=end-&size] typ[1,val=101] tag[tag] max_msg_size[4] version[s]"
msg Tauth = "size[4,val=end-&size] typ[1,val=102] tag[tag] afid[fid] uname[s] aname[s]"
msg Rauth = "size[4,val=end-&size] typ[1,val=103] tag[tag] aqid[qid]"
msg Tattach = "size[4,val=end-&size] typ[1,val=104] tag[tag] fid[fid] afid[fid] uname[s] aname[s]"
msg Rattach = "size[4,val=end-&size] typ[1,val=105] tag[tag] qid[qid]"
#msg Terror = "size[4,val=end-&size] typ[1,val=106] tag[tag] illegal"
msg Rerror = "size[4,val=end-&size] typ[1,val=107] tag[tag] ename[s]"
msg Tflush = "size[4,val=end-&size] typ[1,val=108] tag[tag] oldtag[2]"
msg Rflush = "size[4,val=end-&size] typ[1,val=109] tag[tag]"
msg Twalk = "size[4,val=end-&size] typ[1,val=110] tag[tag] fid[fid] newfid[fid] nwname[2,max=16] nwname*(wname[s])"
msg Rwalk = "size[4,val=end-&size] typ[1,val=111] tag[tag] nwqid[2,max=16] nwqid*(wqid[qid])"
msg Topen = "size[4,val=end-&size] typ[1,val=112] tag[tag] fid[fid] mode[o]"
msg Ropen = "size[4,val=end-&size] typ[1,val=113] tag[tag] qid[qid] iounit[4]"
msg Tcreate = "size[4,val=end-&size] typ[1,val=114] tag[tag] fid[fid] name[s] perm[dm] mode[o]"
msg Rcreate = "size[4,val=end-&size] typ[1,val=115] tag[tag] qid[qid] iounit[4]"
msg Tread = "size[4,val=end-&size] typ[1,val=116] tag[tag] fid[fid] offset[8] count[4]"
msg Rread = "size[4,val=end-&size] typ[1,val=117] tag[tag] data[d]" # for directories, `data` is the sequence "cnt*(entries[stat])"
msg Twrite = "size[4,val=end-&size] typ[1,val=118] tag[tag] fid[fid] offset[8] data[d]"
msg Rwrite = "size[4,val=end-&size] typ[1,val=119] tag[tag] count[4]"
msg Tclunk = "size[4,val=end-&size] typ[1,val=120] tag[tag] fid[fid]"
msg Rclunk = "size[4,val=end-&size] typ[1,val=121] tag[tag]"
msg Tremove = "size[4,val=end-&size] typ[1,val=122] tag[tag] fid[fid]"
msg Rremove = "size[4,val=end-&size] typ[1,val=123] tag[tag]"
msg Tstat = "size[4,val=end-&size] typ[1,val=124] tag[tag] fid[fid]"
msg Rstat = "size[4,val=end-&size] typ[1,val=125] tag[tag] nstat[2,val=end-&stat] stat[stat]" # See the "BUG" note in the RFC for the nstat field
msg Twstat = "size[4,val=end-&size] typ[1,val=126] tag[tag] fid[fid] nstat[2,val=end-&stat] stat[stat]" # See the "BUG" note in the RFC for the nstat field
msg Rwstat = "size[4,val=end-&size] typ[1,val=127] tag[tag]"
|