Source file src/syscall/dir_plan9.go

     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  

View as plain text