dir_plan9.mx raw

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