šŸ’¾ Archived View for gemini.omarpolo.com ā€ŗ post ā€ŗ taking-about-9p-intro.gmi captured on 2023-09-08 at 16:20:06. Gemini links have been rewritten to link to archived content

View Raw

More Information

ā¬…ļø Previous capture (2023-01-29)

-=-=-=-=-=-=-

Talking ā€™bout 9P: intro

Getting to know 9P

Written while listening to ā€œThe Pharaoh Sails to Orionā€ by Nightwish.

Published: 2021-07-31

Tagged with:

#nine-p

These days Iā€™m hacking on 9P. In case you donā€™t know, 9P is a protocol for a network file system that was developed as part of the plan9 operating system.

Now, to make it clear, Iā€™ve never ā€” sigh! ā€” used 9P nor plan9 before. Iā€™m just starting to explore the 9P protocol, hacking something together (for a secret project), and writing some notes here on my blog.

If you find some errors please be kind and notify me. The contacts are at the end of every entry in the Gemini version of this blog.

The 9P protocol is pretty simple: the client sends requests (called T-messages) and the server replies (with R-messages). Replies can be delayed or received out of order. A transaction of some type is completed when the server replies with the matching R-message (or with an error).

Iā€™m going to use the same syntax used in the plan9 manpages to describe the packets. Fields are written as name[n] where ā€˜nameā€™ represents the name of the field and ā€˜nā€™ (which is either 1, 2, 4, 8 or 13) represents the number of byte. An exception to this rule are strings and other variable-width fields: they are represented by a two-byte integer counter followed by the actual data. Strings in particular are denoted as name[s] (where ā€˜sā€™ is a literal s character.)

Integers are transmitted in little-endian format, and strings are encoded in UTF-8 without the NUL-terminator. The NUL byte is illegal in strings transmitted over 9P and thus excluded by paths, user login names etc.

Both the requests and the replies share a common structure, the header, which looks like this

size[4] type[1] tag[2]

Size, the first field, is a 32 bit field that indicates the length of the message (including ā€˜sizeā€™ itself!). Type is a one-byte integer that specifies the type of the requests and tag is an arbitrary client-chosen integer that uniquely represents this transaction. The client cannot issue two or more ongoing transaction with the same tag.

Following the header there is an optional body whose structure depends on the type of message.

Clients can only send T-messages, and the server can only reply with a R-message.

The available messages are:

There are some extension (or ā€œdialectsā€ should I say) of 9P which adds (and slightly change) these messages, but at least for now Iā€™m trying to stick to 9P2000 ā€œvanillaā€.

An important role in 9P is played by ā€œqidā€s and ā€œfidā€s. A fid is a 32bit integer chosen by the client that identifies a ā€œcurrent fileā€ on the server. They are similar, albeit different, to UNIX file descriptors. Qids are the server idea of a file: they are a jumbo object of a whopping 13 bytes: the first one identifies the type (whether is a file, directory, and so on), a four byte integer unique among all files in the hierarchy called ā€œpathā€ and a four byte ā€œversionā€ field that should get incremented every time the file is modified.

Fids are often present in T-messages and qids in R-messages.

The first message that a client should issue after it has established a connection to a file server is Tversion to negotiate the version used and the maximum size of the packets. The Tversion signature is

size[4] Tversion tag[2] msize[4] version[s]

(from now on Iā€™ll omit the three header fields when describing the structure of a packet)

The msize is the maximum size of a packet that the client is willing to accept, and the version is a string the identifies the protocol version used. It MUST start with the ā€œ9Pā€ characters. The client canā€™t issue further requests until the server replies with a Rversion, which has the same structure. The msize replied by the server has to be smaller or equal to the one proposed by the client.

Then there is an optional authentication using Tauth. Iā€™m not interested in how ā€œnormalā€ authentication works in 9P in my project (for now at least), but the idea is that the server provides to the client a special ā€œauthentication fileā€ that an unauthenticated client can read and write to. This is used to implement a custom auth protocol which is external to 9P.

At this point the client can ā€œattachā€ a file tree using Tattach:

fid[4] afid[4] uname[s] aname[s]

If successful, fid will represent the file tree accessed by aname. ā€˜afidā€™ is the authentication fid, which can be -1 (i.e. 0xFFFFFFFF) for the no-authentication case. ā€˜unameā€™ is the user name.

If successful, the client has access to a file tree and can start moving around (by means of Twalk) and messing with files (Topen, Tremove, ā€¦). This is also all for the introduction: future entries will focus in particular about the other kinds of messages.

-- text: CC0 1.0; code: public domain (unless specified otherwise). No copyright here.