minikube/third_party/go9p/p9.go

531 lines
14 KiB
Go

// Copyright 2009 The Go9p Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The p9 package go9provides the definitions and functions used to implement
// the 9P2000 protocol.
// TODO.
// All the packet conversion code in this file is crap and needs a rewrite.
package go9p
import (
"fmt"
)
// 9P2000 message types
const (
Tversion = 100 + iota
Rversion
Tauth
Rauth
Tattach
Rattach
Terror
Rerror
Tflush
Rflush
Twalk
Rwalk
Topen
Ropen
Tcreate
Rcreate
Tread
Rread
Twrite
Rwrite
Tclunk
Rclunk
Tremove
Rremove
Tstat
Rstat
Twstat
Rwstat
Tlast
)
const (
MSIZE = 1048576 + IOHDRSZ // default message size (1048576+IOHdrSz)
IOHDRSZ = 24 // the non-data size of the Twrite messages
PORT = 564 // default port for 9P file servers
)
// Qid types
const (
QTDIR = 0x80 // directories
QTAPPEND = 0x40 // append only files
QTEXCL = 0x20 // exclusive use files
QTMOUNT = 0x10 // mounted channel
QTAUTH = 0x08 // authentication file
QTTMP = 0x04 // non-backed-up file
QTSYMLINK = 0x02 // symbolic link (Unix, 9P2000.u)
QTLINK = 0x01 // hard link (Unix, 9P2000.u)
QTFILE = 0x00
)
// Flags for the mode field in Topen and Tcreate messages
const (
OREAD = 0 // open read-only
OWRITE = 1 // open write-only
ORDWR = 2 // open read-write
OEXEC = 3 // execute (== read but check execute permission)
OTRUNC = 16 // or'ed in (except for exec), truncate file first
OCEXEC = 32 // or'ed in, close on exec
ORCLOSE = 64 // or'ed in, remove on close
)
// File modes
const (
DMDIR = 0x80000000 // mode bit for directories
DMAPPEND = 0x40000000 // mode bit for append only files
DMEXCL = 0x20000000 // mode bit for exclusive use files
DMMOUNT = 0x10000000 // mode bit for mounted channel
DMAUTH = 0x08000000 // mode bit for authentication file
DMTMP = 0x04000000 // mode bit for non-backed-up file
DMSYMLINK = 0x02000000 // mode bit for symbolic link (Unix, 9P2000.u)
DMLINK = 0x01000000 // mode bit for hard link (Unix, 9P2000.u)
DMDEVICE = 0x00800000 // mode bit for device file (Unix, 9P2000.u)
DMNAMEDPIPE = 0x00200000 // mode bit for named pipe (Unix, 9P2000.u)
DMSOCKET = 0x00100000 // mode bit for socket (Unix, 9P2000.u)
DMSETUID = 0x00080000 // mode bit for setuid (Unix, 9P2000.u)
DMSETGID = 0x00040000 // mode bit for setgid (Unix, 9P2000.u)
DMREAD = 0x4 // mode bit for read permission
DMWRITE = 0x2 // mode bit for write permission
DMEXEC = 0x1 // mode bit for execute permission
)
const (
NOTAG uint16 = 0xFFFF // no tag specified
NOFID uint32 = 0xFFFFFFFF // no fid specified
NOUID uint32 = 0xFFFFFFFF // no uid specified
)
// Error values
const (
EPERM = 1
ENOENT = 2
EIO = 5
EEXIST = 17
ENOTDIR = 20
EINVAL = 22
)
// Error represents a 9P2000 (and 9P2000.u) error
type Error struct {
Err string // textual representation of the error
Errornum uint32 // numeric representation of the error (9P2000.u)
}
// File identifier
type Qid struct {
Type uint8 // type of the file (high 8 bits of the mode)
Version uint32 // version number for the path
Path uint64 // server's unique identification of the file
}
// Dir describes a file
type Dir struct {
Size uint16 // size-2 of the Dir on the wire
Type uint16
Dev uint32
Qid // file's Qid
Mode uint32 // permissions and flags
Atime uint32 // last access time in seconds
Mtime uint32 // last modified time in seconds
Length uint64 // file length in bytes
Name string // file name
Uid string // owner name
Gid string // group name
Muid string // name of the last user that modified the file
/* 9P2000.u extension */
Ext string // special file's descriptor
Uidnum uint32 // owner ID
Gidnum uint32 // group ID
Muidnum uint32 // ID of the last user that modified the file
}
// Fcall represents a 9P2000 message
type Fcall struct {
Size uint32 // size of the message
Type uint8 // message type
Fid uint32 // file identifier
Tag uint16 // message tag
Msize uint32 // maximum message size (used by Tversion, Rversion)
Version string // protocol version (used by Tversion, Rversion)
Oldtag uint16 // tag of the message to flush (used by Tflush)
Error string // error (used by Rerror)
Qid // file Qid (used by Rauth, Rattach, Ropen, Rcreate)
Iounit uint32 // maximum bytes read without breaking in multiple messages (used by Ropen, Rcreate)
Afid uint32 // authentication fid (used by Tauth, Tattach)
Uname string // user name (used by Tauth, Tattach)
Aname string // attach name (used by Tauth, Tattach)
Perm uint32 // file permission (mode) (used by Tcreate)
Name string // file name (used by Tcreate)
Mode uint8 // open mode (used by Topen, Tcreate)
Newfid uint32 // the fid that represents the file walked to (used by Twalk)
Wname []string // list of names to walk (used by Twalk)
Wqid []Qid // list of Qids for the walked files (used by Rwalk)
Offset uint64 // offset in the file to read/write from/to (used by Tread, Twrite)
Count uint32 // number of bytes read/written (used by Tread, Rread, Twrite, Rwrite)
Data []uint8 // data read/to-write (used by Rread, Twrite)
Dir // file description (used by Rstat, Twstat)
/* 9P2000.u extensions */
Errornum uint32 // error code, 9P2000.u only (used by Rerror)
Ext string // special file description, 9P2000.u only (used by Tcreate)
Unamenum uint32 // user ID, 9P2000.u only (used by Tauth, Tattach)
Pkt []uint8 // raw packet data
Buf []uint8 // buffer to put the raw data in
}
// Interface for accessing users and groups
type Users interface {
Uid2User(uid int) User
Uname2User(uname string) User
Gid2Group(gid int) Group
Gname2Group(gname string) Group
}
// Represents a user
type User interface {
Name() string // user name
Id() int // user id
Groups() []Group // groups the user belongs to (can return nil)
IsMember(g Group) bool // returns true if the user is member of the specified group
}
// Represents a group of users
type Group interface {
Name() string // group name
Id() int // group id
Members() []User // list of members that belong to the group (can return nil)
}
// minimum size of a 9P2000 message for a type
var minFcsize = [...]uint32{
6, /* Tversion msize[4] version[s] */
6, /* Rversion msize[4] version[s] */
8, /* Tauth fid[4] uname[s] aname[s] */
13, /* Rauth aqid[13] */
12, /* Tattach fid[4] afid[4] uname[s] aname[s] */
13, /* Rattach qid[13] */
0, /* Terror */
2, /* Rerror ename[s] (ecode[4]) */
2, /* Tflush oldtag[2] */
0, /* Rflush */
10, /* Twalk fid[4] newfid[4] nwname[2] */
2, /* Rwalk nwqid[2] */
5, /* Topen fid[4] mode[1] */
17, /* Ropen qid[13] iounit[4] */
11, /* Tcreate fid[4] name[s] perm[4] mode[1] */
17, /* Rcreate qid[13] iounit[4] */
16, /* Tread fid[4] offset[8] count[4] */
4, /* Rread count[4] */
16, /* Twrite fid[4] offset[8] count[4] */
4, /* Rwrite count[4] */
4, /* Tclunk fid[4] */
0, /* Rclunk */
4, /* Tremove fid[4] */
0, /* Rremove */
4, /* Tstat fid[4] */
4, /* Rstat stat[n] */
8, /* Twstat fid[4] stat[n] */
0, /* Rwstat */
20, /* Tbread fileid[8] offset[8] count[4] */
4, /* Rbread count[4] */
20, /* Tbwrite fileid[8] offset[8] count[4] */
4, /* Rbwrite count[4] */
16, /* Tbtrunc fileid[8] offset[8] */
0, /* Rbtrunc */
}
// minimum size of a 9P2000.u message for a type
var minFcusize = [...]uint32{
6, /* Tversion msize[4] version[s] */
6, /* Rversion msize[4] version[s] */
12, /* Tauth fid[4] uname[s] aname[s] */
13, /* Rauth aqid[13] */
16, /* Tattach fid[4] afid[4] uname[s] aname[s] */
13, /* Rattach qid[13] */
0, /* Terror */
6, /* Rerror ename[s] (ecode[4]) */
2, /* Tflush oldtag[2] */
0, /* Rflush */
10, /* Twalk fid[4] newfid[4] nwname[2] */
2, /* Rwalk nwqid[2] */
5, /* Topen fid[4] mode[1] */
17, /* Ropen qid[13] iounit[4] */
13, /* Tcreate fid[4] name[s] perm[4] mode[1] */
17, /* Rcreate qid[13] iounit[4] */
16, /* Tread fid[4] offset[8] count[4] */
4, /* Rread count[4] */
16, /* Twrite fid[4] offset[8] count[4] */
4, /* Rwrite count[4] */
4, /* Tclunk fid[4] */
0, /* Rclunk */
4, /* Tremove fid[4] */
0, /* Rremove */
4, /* Tstat fid[4] */
4, /* Rstat stat[n] */
8, /* Twstat fid[4] stat[n] */
20, /* Tbread fileid[8] offset[8] count[4] */
4, /* Rbread count[4] */
20, /* Tbwrite fileid[8] offset[8] count[4] */
4, /* Rbwrite count[4] */
16, /* Tbtrunc fileid[8] offset[8] */
0, /* Rbtrunc */
}
func gint8(buf []byte) (uint8, []byte) { return buf[0], buf[1:] }
func gint16(buf []byte) (uint16, []byte) {
return uint16(buf[0]) | (uint16(buf[1]) << 8), buf[2:]
}
func gint32(buf []byte) (uint32, []byte) {
return uint32(buf[0]) | (uint32(buf[1]) << 8) | (uint32(buf[2]) << 16) |
(uint32(buf[3]) << 24),
buf[4:]
}
func Gint32(buf []byte) (uint32, []byte) { return gint32(buf) }
func gint64(buf []byte) (uint64, []byte) {
return uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) |
(uint64(buf[3]) << 24) | (uint64(buf[4]) << 32) | (uint64(buf[5]) << 40) |
(uint64(buf[6]) << 48) | (uint64(buf[7]) << 56),
buf[8:]
}
func gstr(buf []byte) (string, []byte) {
var n uint16
if buf == nil || len(buf) < 2 {
return "", nil
}
n, buf = gint16(buf)
if int(n) > len(buf) {
return "", nil
}
return string(buf[0:n]), buf[n:]
}
func gqid(buf []byte, qid *Qid) []byte {
qid.Type, buf = gint8(buf)
qid.Version, buf = gint32(buf)
qid.Path, buf = gint64(buf)
return buf
}
func gstat(buf []byte, d *Dir, dotu bool) ([]byte, error) {
sz := len(buf)
d.Size, buf = gint16(buf)
d.Type, buf = gint16(buf)
d.Dev, buf = gint32(buf)
buf = gqid(buf, &d.Qid)
d.Mode, buf = gint32(buf)
d.Atime, buf = gint32(buf)
d.Mtime, buf = gint32(buf)
d.Length, buf = gint64(buf)
d.Name, buf = gstr(buf)
if buf == nil {
s := fmt.Sprintf("Buffer too short for basic 9p: need %d, have %d",
49, sz)
return nil, &Error{s, EINVAL}
}
d.Uid, buf = gstr(buf)
if buf == nil {
return nil, &Error{"d.Uid failed", EINVAL}
}
d.Gid, buf = gstr(buf)
if buf == nil {
return nil, &Error{"d.Gid failed", EINVAL}
}
d.Muid, buf = gstr(buf)
if buf == nil {
return nil, &Error{"d.Muid failed", EINVAL}
}
if dotu {
d.Ext, buf = gstr(buf)
if buf == nil {
return nil, &Error{"d.Ext failed", EINVAL}
}
d.Uidnum, buf = gint32(buf)
d.Gidnum, buf = gint32(buf)
d.Muidnum, buf = gint32(buf)
} else {
d.Uidnum = NOUID
d.Gidnum = NOUID
d.Muidnum = NOUID
}
return buf, nil
}
func pint8(val uint8, buf []byte) []byte {
buf[0] = val
return buf[1:]
}
func pint16(val uint16, buf []byte) []byte {
buf[0] = uint8(val)
buf[1] = uint8(val >> 8)
return buf[2:]
}
func pint32(val uint32, buf []byte) []byte {
buf[0] = uint8(val)
buf[1] = uint8(val >> 8)
buf[2] = uint8(val >> 16)
buf[3] = uint8(val >> 24)
return buf[4:]
}
func pint64(val uint64, buf []byte) []byte {
buf[0] = uint8(val)
buf[1] = uint8(val >> 8)
buf[2] = uint8(val >> 16)
buf[3] = uint8(val >> 24)
buf[4] = uint8(val >> 32)
buf[5] = uint8(val >> 40)
buf[6] = uint8(val >> 48)
buf[7] = uint8(val >> 56)
return buf[8:]
}
func pstr(val string, buf []byte) []byte {
n := uint16(len(val))
buf = pint16(n, buf)
b := []byte(val)
copy(buf, b)
return buf[n:]
}
func pqid(val *Qid, buf []byte) []byte {
buf = pint8(val.Type, buf)
buf = pint32(val.Version, buf)
buf = pint64(val.Path, buf)
return buf
}
func statsz(d *Dir, dotu bool) int {
sz := 2 + 2 + 4 + 13 + 4 + 4 + 4 + 8 + 2 + 2 + 2 + 2 + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
if dotu {
sz += 2 + 4 + 4 + 4 + len(d.Ext)
}
return sz
}
func pstat(d *Dir, buf []byte, dotu bool) []byte {
sz := statsz(d, dotu)
buf = pint16(uint16(sz-2), buf)
buf = pint16(d.Type, buf)
buf = pint32(d.Dev, buf)
buf = pqid(&d.Qid, buf)
buf = pint32(d.Mode, buf)
buf = pint32(d.Atime, buf)
buf = pint32(d.Mtime, buf)
buf = pint64(d.Length, buf)
buf = pstr(d.Name, buf)
buf = pstr(d.Uid, buf)
buf = pstr(d.Gid, buf)
buf = pstr(d.Muid, buf)
if dotu {
buf = pstr(d.Ext, buf)
buf = pint32(d.Uidnum, buf)
buf = pint32(d.Gidnum, buf)
buf = pint32(d.Muidnum, buf)
}
return buf
}
// Converts a Dir value to its on-the-wire representation and writes it to
// the buf. Returns the number of bytes written, 0 if there is not enough space.
func PackDir(d *Dir, dotu bool) []byte {
sz := statsz(d, dotu)
buf := make([]byte, sz)
pstat(d, buf, dotu)
return buf
}
// Converts the on-the-wire representation of a stat to Stat value.
// Returns an error if the conversion is impossible, otherwise
// a pointer to a Stat value.
func UnpackDir(buf []byte, dotu bool) (d *Dir, b []byte, amt int, err error) {
sz := 2 + 2 + 4 + 13 + 4 + /* size[2] type[2] dev[4] qid[13] mode[4] */
4 + 4 + 8 + /* atime[4] mtime[4] length[8] */
2 + 2 + 2 + 2 /* name[s] uid[s] gid[s] muid[s] */
if dotu {
sz += 2 + 4 + 4 + 4 /* extension[s] n_uid[4] n_gid[4] n_muid[4] */
}
if len(buf) < sz {
s := fmt.Sprintf("short buffer: Need %d and have %v", sz, len(buf))
return nil, nil, 0, &Error{s, EINVAL}
}
d = new(Dir)
b, err = gstat(buf, d, dotu)
if err != nil {
return nil, nil, 0, err
}
return d, b, len(buf) - len(b), nil
}
// Allocates a new Fcall.
func NewFcall(sz uint32) *Fcall {
fc := new(Fcall)
fc.Buf = make([]byte, sz)
return fc
}
// Sets the tag of a Fcall.
func SetTag(fc *Fcall, tag uint16) {
fc.Tag = tag
pint16(tag, fc.Pkt[5:])
}
func packCommon(fc *Fcall, size int, id uint8) ([]byte, error) {
size += 4 + 1 + 2 /* size[4] id[1] tag[2] */
if len(fc.Buf) < int(size) {
return nil, &Error{"buffer too small", EINVAL}
}
fc.Size = uint32(size)
fc.Type = id
fc.Tag = NOTAG
p := fc.Buf
p = pint32(uint32(size), p)
p = pint8(id, p)
p = pint16(NOTAG, p)
fc.Pkt = fc.Buf[0:size]
return p, nil
}
func (err *Error) Error() string {
if err != nil {
return err.Err
}
return ""
}