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
151
152
153
154
155
|
# 9P2000.txt - Definitions of 9P2000 messages
#
# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-Licence-Identifier: AGPL-3.0-or-later
# The format of each message (excluding the "size[4] msg_type[1]
# tag[2]" header) is written here as a sequence of
# "member_name[member_type]" struct members.
#
# The primitive member types types are the following single-character
# mnemonics:
#
# - 1 = u8
# - 2 = u16le
# - 4 = u32le
# - 8 = u16le
# "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.
version "9P2000"
# data (u32le `n`, then `n` bytes of data)
d = "len[4] len*(dat[1])"
# string (u16le `n`, then `n` bytes of UTF-8)
s = "len[2] len*(utf8[1])"
# "d"? mode - (file permissions and attributes)
bitfield dm 32
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=0777 # {OWNER,GROUP,OTHER}_{R,W,X}
# QID Type (see qid below)
bitfield qt 8
7/DIR
6/APPEND
5/EXCL
4/_PLAN9_MOUNT # see DMMOUNT 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 qid_type bitfield.
qid = "type[qt] vers[4] path[8]"
# stat (TODO)
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 8
0/rwx_0 # low bit of the 2-bit READ/WRITE/RDWR/EXEC enum
1/rwx_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 # unlock read()
WRITE = 1 # unlock write()
RDWR = 2 # unlock read() and write()
EXEC = 3 # unlock read() for files, walk() for directories
# In the 9P protocol, each message has a type, and message types come
# in pairs (except "Rerror"); "T" and "R"; T-messages are
# client->server requests, and R-messages are server->client responses
# (the client "Transmits" T-messages and "Receives" R-messages). The
# type of a message is represented by a u8 ID; T-messages are even and
# R-messages are odd.
100/Tversion = "max_msg_size[4] version[s]"
101/Rversion = "max_msg_size[4] version[s]"
102/Tauth = "afid[4] uname[s] aname[s]"
103/Rauth = "aqid[qid]"
104/Tattach = "fid[4] afid[4] uname[s] aname[s]"
105/Rattach = "qid[qid]"
#106/Terror = "illegal"
107/Rerror = "ename[s]"
108/Tflush = "oldtag[2]"
109/Rflush = ""
110/Twalk = "fid[4] newfid[4] nwname[2,max=16] nwname*(wname[s])"
111/Rwalk = "nwqid[2,max=16] nwqid*(wqid[qid])"
112/Topen = "fid[4] mode[o]"
113/Ropen = "qid[qid] iounit[4]"
114/Tcreate = "fid[4] name[s] perm[dm] mode[o]"
115/Rcreate = "qid[qid] iounit[4]"
116/Tread = "fid[4] offset[8] count[4]"
117/Rread = "data[d]" # for directories, `data` is the sequence "cnt*(entries[stat])"
118/Twrite = "fid[4] offset[8] data[d]"
119/Rwrite = "count[4]"
120/Tclunk = "fid[4]"
121/Rclunk = ""
122/Tremove = "fid[4]"
123/Rremove = ""
124/Tstat = "fid[4]"
125/Rstat = "nstat[2,val=end-&stat] stat[stat]" # See the "BUG" note in the RFC for the nstat field
126/Twstat = "fid[4] nstat[2,val=end-&stat] stat[stat]" # See the "BUG" note in the RFC for the nstat field
127/Rwstat = ""
|