# 9P2000.txt - Definitions of 9P2000 messages # # Copyright (C) 2024 Luke T. Shumaker # 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 a 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]" "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 = "stat[stat]" 126/Twstat = "fid[4] stat[stat]" 127/Rwstat = ""