213 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2012 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // Plan 9 directory marshalling. See intro(5).
 | |
| 
 | |
| package plan9
 | |
| 
 | |
| import "errors"
 | |
| 
 | |
| var (
 | |
| 	ErrShortStat = errors.New("stat buffer too short")
 | |
| 	ErrBadStat   = errors.New("malformed stat buffer")
 | |
| 	ErrBadName   = errors.New("bad character in file name")
 | |
| )
 | |
| 
 | |
| // A Qid represents a 9P server's unique identification for a file.
 | |
| type Qid struct {
 | |
| 	Path uint64 // the file server's unique identification for the file
 | |
| 	Vers uint32 // version number for given Path
 | |
| 	Type uint8  // the type of the file (plan9.QTDIR for example)
 | |
| }
 | |
| 
 | |
| // A Dir contains the metadata for a file.
 | |
| type Dir struct {
 | |
| 	// system-modified data
 | |
| 	Type uint16 // server type
 | |
| 	Dev  uint32 // server subtype
 | |
| 
 | |
| 	// file data
 | |
| 	Qid    Qid    // unique id from server
 | |
| 	Mode   uint32 // permissions
 | |
| 	Atime  uint32 // last read time
 | |
| 	Mtime  uint32 // last write time
 | |
| 	Length int64  // file length
 | |
| 	Name   string // last element of path
 | |
| 	Uid    string // owner name
 | |
| 	Gid    string // group name
 | |
| 	Muid   string // last modifier name
 | |
| }
 | |
| 
 | |
| var nullDir = Dir{
 | |
| 	Type: ^uint16(0),
 | |
| 	Dev:  ^uint32(0),
 | |
| 	Qid: Qid{
 | |
| 		Path: ^uint64(0),
 | |
| 		Vers: ^uint32(0),
 | |
| 		Type: ^uint8(0),
 | |
| 	},
 | |
| 	Mode:   ^uint32(0),
 | |
| 	Atime:  ^uint32(0),
 | |
| 	Mtime:  ^uint32(0),
 | |
| 	Length: ^int64(0),
 | |
| }
 | |
| 
 | |
| // Null assigns special "don't touch" values to members of d to
 | |
| // avoid modifying them during plan9.Wstat.
 | |
| func (d *Dir) Null() { *d = nullDir }
 | |
| 
 | |
| // Marshal encodes a 9P stat message corresponding to d into b
 | |
| //
 | |
| // If there isn't enough space in b for a stat message, ErrShortStat is returned.
 | |
| func (d *Dir) Marshal(b []byte) (n int, err error) {
 | |
| 	n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
 | |
| 	if n > len(b) {
 | |
| 		return n, ErrShortStat
 | |
| 	}
 | |
| 
 | |
| 	for _, c := range d.Name {
 | |
| 		if c == '/' {
 | |
| 			return n, ErrBadName
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	b = pbit16(b, uint16(n)-2)
 | |
| 	b = pbit16(b, d.Type)
 | |
| 	b = pbit32(b, d.Dev)
 | |
| 	b = pbit8(b, d.Qid.Type)
 | |
| 	b = pbit32(b, d.Qid.Vers)
 | |
| 	b = pbit64(b, d.Qid.Path)
 | |
| 	b = pbit32(b, d.Mode)
 | |
| 	b = pbit32(b, d.Atime)
 | |
| 	b = pbit32(b, d.Mtime)
 | |
| 	b = pbit64(b, uint64(d.Length))
 | |
| 	b = pstring(b, d.Name)
 | |
| 	b = pstring(b, d.Uid)
 | |
| 	b = pstring(b, d.Gid)
 | |
| 	b = pstring(b, d.Muid)
 | |
| 
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| // UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
 | |
| //
 | |
| // If b is too small to hold a valid stat message, ErrShortStat is returned.
 | |
| //
 | |
| // If the stat message itself is invalid, ErrBadStat is returned.
 | |
| func UnmarshalDir(b []byte) (*Dir, error) {
 | |
| 	if len(b) < STATFIXLEN {
 | |
| 		return nil, ErrShortStat
 | |
| 	}
 | |
| 	size, buf := gbit16(b)
 | |
| 	if len(b) != int(size)+2 {
 | |
| 		return nil, ErrBadStat
 | |
| 	}
 | |
| 	b = buf
 | |
| 
 | |
| 	var d Dir
 | |
| 	d.Type, b = gbit16(b)
 | |
| 	d.Dev, b = gbit32(b)
 | |
| 	d.Qid.Type, b = gbit8(b)
 | |
| 	d.Qid.Vers, b = gbit32(b)
 | |
| 	d.Qid.Path, b = gbit64(b)
 | |
| 	d.Mode, b = gbit32(b)
 | |
| 	d.Atime, b = gbit32(b)
 | |
| 	d.Mtime, b = gbit32(b)
 | |
| 
 | |
| 	n, b := gbit64(b)
 | |
| 	d.Length = int64(n)
 | |
| 
 | |
| 	var ok bool
 | |
| 	if d.Name, b, ok = gstring(b); !ok {
 | |
| 		return nil, ErrBadStat
 | |
| 	}
 | |
| 	if d.Uid, b, ok = gstring(b); !ok {
 | |
| 		return nil, ErrBadStat
 | |
| 	}
 | |
| 	if d.Gid, b, ok = gstring(b); !ok {
 | |
| 		return nil, ErrBadStat
 | |
| 	}
 | |
| 	if d.Muid, b, ok = gstring(b); !ok {
 | |
| 		return nil, ErrBadStat
 | |
| 	}
 | |
| 
 | |
| 	return &d, nil
 | |
| }
 | |
| 
 | |
| // pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
 | |
| func pbit8(b []byte, v uint8) []byte {
 | |
| 	b[0] = byte(v)
 | |
| 	return b[1:]
 | |
| }
 | |
| 
 | |
| // pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
 | |
| func pbit16(b []byte, v uint16) []byte {
 | |
| 	b[0] = byte(v)
 | |
| 	b[1] = byte(v >> 8)
 | |
| 	return b[2:]
 | |
| }
 | |
| 
 | |
| // pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
 | |
| func pbit32(b []byte, v uint32) []byte {
 | |
| 	b[0] = byte(v)
 | |
| 	b[1] = byte(v >> 8)
 | |
| 	b[2] = byte(v >> 16)
 | |
| 	b[3] = byte(v >> 24)
 | |
| 	return b[4:]
 | |
| }
 | |
| 
 | |
| // pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
 | |
| func pbit64(b []byte, v uint64) []byte {
 | |
| 	b[0] = byte(v)
 | |
| 	b[1] = byte(v >> 8)
 | |
| 	b[2] = byte(v >> 16)
 | |
| 	b[3] = byte(v >> 24)
 | |
| 	b[4] = byte(v >> 32)
 | |
| 	b[5] = byte(v >> 40)
 | |
| 	b[6] = byte(v >> 48)
 | |
| 	b[7] = byte(v >> 56)
 | |
| 	return b[8:]
 | |
| }
 | |
| 
 | |
| // pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
 | |
| // returning the remaining slice of b..
 | |
| func pstring(b []byte, s string) []byte {
 | |
| 	b = pbit16(b, uint16(len(s)))
 | |
| 	n := copy(b, s)
 | |
| 	return b[n:]
 | |
| }
 | |
| 
 | |
| // gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
 | |
| func gbit8(b []byte) (uint8, []byte) {
 | |
| 	return uint8(b[0]), b[1:]
 | |
| }
 | |
| 
 | |
| // gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
 | |
| func gbit16(b []byte) (uint16, []byte) {
 | |
| 	return uint16(b[0]) | uint16(b[1])<<8, b[2:]
 | |
| }
 | |
| 
 | |
| // gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
 | |
| func gbit32(b []byte) (uint32, []byte) {
 | |
| 	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
 | |
| }
 | |
| 
 | |
| // gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
 | |
| func gbit64(b []byte) (uint64, []byte) {
 | |
| 	lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
 | |
| 	hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
 | |
| 	return uint64(lo) | uint64(hi)<<32, b[8:]
 | |
| }
 | |
| 
 | |
| // gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
 | |
| // It returns the string with the remaining slice of b and a boolean. If the length is
 | |
| // greater than the number of bytes in b, the boolean will be false.
 | |
| func gstring(b []byte) (string, []byte, bool) {
 | |
| 	n, b := gbit16(b)
 | |
| 	if int(n) > len(b) {
 | |
| 		return "", b, false
 | |
| 	}
 | |
| 	return string(b[:n]), b[n:], true
 | |
| }
 | 
