Home | History | Annotate | Download | only in syscall
      1 // Copyright 2012 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Plan 9 directory marshaling. See intro(5).
      6 
      7 package syscall
      8 
      9 import "errors"
     10 
     11 var (
     12 	ErrShortStat = errors.New("stat buffer too short")
     13 	ErrBadStat   = errors.New("malformed stat buffer")
     14 	ErrBadName   = errors.New("bad character in file name")
     15 )
     16 
     17 // A Qid represents a 9P server's unique identification for a file.
     18 type Qid struct {
     19 	Path uint64 // the file server's unique identification for the file
     20 	Vers uint32 // version number for given Path
     21 	Type uint8  // the type of the file (syscall.QTDIR for example)
     22 }
     23 
     24 // A Dir contains the metadata for a file.
     25 type Dir struct {
     26 	// system-modified data
     27 	Type uint16 // server type
     28 	Dev  uint32 // server subtype
     29 
     30 	// file data
     31 	Qid    Qid    // unique id from server
     32 	Mode   uint32 // permissions
     33 	Atime  uint32 // last read time
     34 	Mtime  uint32 // last write time
     35 	Length int64  // file length
     36 	Name   string // last element of path
     37 	Uid    string // owner name
     38 	Gid    string // group name
     39 	Muid   string // last modifier name
     40 }
     41 
     42 var nullDir = Dir{
     43 	Type: ^uint16(0),
     44 	Dev:  ^uint32(0),
     45 	Qid: Qid{
     46 		Path: ^uint64(0),
     47 		Vers: ^uint32(0),
     48 		Type: ^uint8(0),
     49 	},
     50 	Mode:   ^uint32(0),
     51 	Atime:  ^uint32(0),
     52 	Mtime:  ^uint32(0),
     53 	Length: ^int64(0),
     54 }
     55 
     56 // Null assigns special "don't touch" values to members of d to
     57 // avoid modifying them during syscall.Wstat.
     58 func (d *Dir) Null() { *d = nullDir }
     59 
     60 // Marshal encodes a 9P stat message corresponding to d into b
     61 //
     62 // If there isn't enough space in b for a stat message, ErrShortStat is returned.
     63 func (d *Dir) Marshal(b []byte) (n int, err error) {
     64 	n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
     65 	if n > len(b) {
     66 		return n, ErrShortStat
     67 	}
     68 
     69 	for _, c := range d.Name {
     70 		if c == '/' {
     71 			return n, ErrBadName
     72 		}
     73 	}
     74 
     75 	b = pbit16(b, uint16(n)-2)
     76 	b = pbit16(b, d.Type)
     77 	b = pbit32(b, d.Dev)
     78 	b = pbit8(b, d.Qid.Type)
     79 	b = pbit32(b, d.Qid.Vers)
     80 	b = pbit64(b, d.Qid.Path)
     81 	b = pbit32(b, d.Mode)
     82 	b = pbit32(b, d.Atime)
     83 	b = pbit32(b, d.Mtime)
     84 	b = pbit64(b, uint64(d.Length))
     85 	b = pstring(b, d.Name)
     86 	b = pstring(b, d.Uid)
     87 	b = pstring(b, d.Gid)
     88 	b = pstring(b, d.Muid)
     89 
     90 	return n, nil
     91 }
     92 
     93 // UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
     94 //
     95 // If b is too small to hold a valid stat message, ErrShortStat is returned.
     96 //
     97 // If the stat message itself is invalid, ErrBadStat is returned.
     98 func UnmarshalDir(b []byte) (*Dir, error) {
     99 	if len(b) < STATFIXLEN {
    100 		return nil, ErrShortStat
    101 	}
    102 	size, buf := gbit16(b)
    103 	if len(b) != int(size)+2 {
    104 		return nil, ErrBadStat
    105 	}
    106 	b = buf
    107 
    108 	var d Dir
    109 	d.Type, b = gbit16(b)
    110 	d.Dev, b = gbit32(b)
    111 	d.Qid.Type, b = gbit8(b)
    112 	d.Qid.Vers, b = gbit32(b)
    113 	d.Qid.Path, b = gbit64(b)
    114 	d.Mode, b = gbit32(b)
    115 	d.Atime, b = gbit32(b)
    116 	d.Mtime, b = gbit32(b)
    117 
    118 	n, b := gbit64(b)
    119 	d.Length = int64(n)
    120 
    121 	var ok bool
    122 	if d.Name, b, ok = gstring(b); !ok {
    123 		return nil, ErrBadStat
    124 	}
    125 	if d.Uid, b, ok = gstring(b); !ok {
    126 		return nil, ErrBadStat
    127 	}
    128 	if d.Gid, b, ok = gstring(b); !ok {
    129 		return nil, ErrBadStat
    130 	}
    131 	if d.Muid, b, ok = gstring(b); !ok {
    132 		return nil, ErrBadStat
    133 	}
    134 
    135 	return &d, nil
    136 }
    137 
    138 // pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
    139 func pbit8(b []byte, v uint8) []byte {
    140 	b[0] = byte(v)
    141 	return b[1:]
    142 }
    143 
    144 // pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
    145 func pbit16(b []byte, v uint16) []byte {
    146 	b[0] = byte(v)
    147 	b[1] = byte(v >> 8)
    148 	return b[2:]
    149 }
    150 
    151 // pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
    152 func pbit32(b []byte, v uint32) []byte {
    153 	b[0] = byte(v)
    154 	b[1] = byte(v >> 8)
    155 	b[2] = byte(v >> 16)
    156 	b[3] = byte(v >> 24)
    157 	return b[4:]
    158 }
    159 
    160 // pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
    161 func pbit64(b []byte, v uint64) []byte {
    162 	b[0] = byte(v)
    163 	b[1] = byte(v >> 8)
    164 	b[2] = byte(v >> 16)
    165 	b[3] = byte(v >> 24)
    166 	b[4] = byte(v >> 32)
    167 	b[5] = byte(v >> 40)
    168 	b[6] = byte(v >> 48)
    169 	b[7] = byte(v >> 56)
    170 	return b[8:]
    171 }
    172 
    173 // pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
    174 // returning the remaining slice of b..
    175 func pstring(b []byte, s string) []byte {
    176 	b = pbit16(b, uint16(len(s)))
    177 	n := copy(b, s)
    178 	return b[n:]
    179 }
    180 
    181 // gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
    182 func gbit8(b []byte) (uint8, []byte) {
    183 	return uint8(b[0]), b[1:]
    184 }
    185 
    186 // gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
    187 //go:nosplit
    188 func gbit16(b []byte) (uint16, []byte) {
    189 	return uint16(b[0]) | uint16(b[1])<<8, b[2:]
    190 }
    191 
    192 // gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
    193 func gbit32(b []byte) (uint32, []byte) {
    194 	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
    195 }
    196 
    197 // gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
    198 func gbit64(b []byte) (uint64, []byte) {
    199 	lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
    200 	hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
    201 	return uint64(lo) | uint64(hi)<<32, b[8:]
    202 }
    203 
    204 // gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
    205 // It returns the string with the remaining slice of b and a boolean. If the length is
    206 // greater than the number of bytes in b, the boolean will be false.
    207 func gstring(b []byte) (string, []byte, bool) {
    208 	n, b := gbit16(b)
    209 	if int(n) > len(b) {
    210 		return "", b, false
    211 	}
    212 	return string(b[:n]), b[n:], true
    213 }
    214