lib9p

Go 9P library.
Log | Files | Refs | LICENSE

stat.go (4667B)


      1 package lib9p
      2 
      3 import (
      4 	"fmt"
      5 	"io/fs"
      6 	"time"
      7 )
      8 
      9 type FileMode = fs.FileMode
     10 
     11 // PermString returns the string representation of perm's
     12 // lower 9 bits and DMDIR bit.
     13 func permString(perm FileMode) string {
     14 	s := ""
     15 	if perm&fs.ModeDir != 0 {
     16 		s += "d"
     17 	}
     18 	for i := 6; i >= 0; i -= 3 {
     19 		p := perm >> i
     20 		if p&4 != 0 {
     21 			s += "r"
     22 		} else {
     23 			s += "-"
     24 		}
     25 		if p&2 != 0 {
     26 			s += "w"
     27 		} else {
     28 			s += "-"
     29 		}
     30 		if p&1 != 0 {
     31 			s += "x"
     32 		} else {
     33 			s += "-"
     34 		}
     35 	}
     36 	return s
     37 }
     38 
     39 // These consts are used to record the access mode to a file.
     40 // This is derived from plan9's lib9p, but I think this is not needed.
     41 const (
     42 	AEXEC fs.FileMode = 1 << iota
     43 	AWRITE
     44 	AREAD
     45 )
     46 
     47 // Stat represents the stat defined by 9P.
     48 type Stat struct {
     49 	Type  uint16
     50 	Dev   uint32
     51 	Qid   Qid
     52 	Mode  FileMode
     53 	Atime uint32
     54 	Mtime uint32
     55 	//TODO: In 9P protocol Length is unsigned integer.
     56 	Length int64
     57 	Name   string
     58 	Uid    string
     59 	Gid    string
     60 	Muid   string
     61 }
     62 
     63 // NewStat converts a byte array of stat from a 9P message into Stat struct.
     64 func NewStat(b []byte) *Stat {
     65 	stat := new(Stat)
     66 	size := gbit16(b[:2]) + 2
     67 	cur := 2 // ignore size field
     68 	stat.Type = gbit16(b[cur : cur+2])
     69 	cur += 2
     70 	stat.Dev = gbit32(b[cur : cur+4])
     71 	cur += 4
     72 	stat.Qid = unmarshalQid(b[cur : cur+13])
     73 	cur += 13
     74 	stat.Mode = FileMode(gbit32(b[cur : cur+4]))
     75 	cur += 4
     76 	stat.Atime = gbit32(b[cur : cur+4])
     77 	cur += 4
     78 	stat.Mtime = gbit32(b[cur : cur+4])
     79 	cur += 4
     80 	stat.Length = int64(gbit64(b[cur : cur+8]))
     81 	cur += 8
     82 	nameSize := int(gbit16(b[cur : cur+2]))
     83 	cur += 2
     84 	stat.Name = string(b[cur : cur+nameSize])
     85 	cur += nameSize
     86 	uidSize := int(gbit16(b[cur : cur+2]))
     87 	cur += 2
     88 	stat.Uid = string(b[cur : cur+uidSize])
     89 	cur += uidSize
     90 	gidSize := int(gbit16(b[cur : cur+2]))
     91 	cur += 2
     92 	stat.Gid = string(b[cur : cur+gidSize])
     93 	cur += gidSize
     94 	muidSize := int(gbit16(b[cur : cur+2]))
     95 	cur += 2
     96 	stat.Muid = string(b[cur : cur+muidSize])
     97 	cur += muidSize
     98 	if cur != int(size) {
     99 		panic(fmt.Errorf("size %d != cursor position %d", size, cur))
    100 	}
    101 	return stat
    102 }
    103 
    104 // Size returns the size of marshaled stat in bytes excluding the
    105 // size field itself.
    106 func (s *Stat) Size() uint16 {
    107 	// type + dev + qid + mode + atime + mtime + length
    108 	return uint16(2 + 4 + 13 + 4 + 4 + 4 + 8 +
    109 		2 + len(s.Name) +
    110 		2 + len(s.Uid) +
    111 		2 + len(s.Gid) +
    112 		2 + len(s.Muid))
    113 }
    114 
    115 // Marshal converts a Stat struct into a byte array of stat to be transmitted
    116 // in 9P conversatino.
    117 func (s *Stat) marshal() []byte {
    118 	cur := 0
    119 	size := s.Size()
    120 	msg := make([]byte, 2+size)
    121 	pbit16(msg[cur:cur+2], size)
    122 	cur += 2
    123 	pbit16(msg[cur:cur+2], s.Type)
    124 	cur += 2
    125 	pbit32(msg[cur:cur+4], s.Dev)
    126 	cur += 4
    127 	msg[cur] = uint8(s.Qid.Type)
    128 	cur += 1
    129 	pbit32(msg[cur:cur+4], s.Qid.Vers)
    130 	cur += 4
    131 	pbit64(msg[cur:cur+8], s.Qid.Path)
    132 	cur += 8
    133 	pbit32(msg[cur:cur+4], uint32(s.Mode))
    134 	cur += 4
    135 	pbit32(msg[cur:cur+4], s.Atime)
    136 	cur += 4
    137 	pbit32(msg[cur:cur+4], s.Mtime)
    138 	cur += 4
    139 	pbit64(msg[cur:cur+8], uint64(s.Length))
    140 	cur += 8
    141 	pbit16(msg[cur:cur+2], uint16(len(s.Name)))
    142 	cur += 2
    143 	for i := 0; i < len(s.Name); i++ {
    144 		msg[cur+i] = s.Name[i]
    145 	}
    146 	cur += len(s.Name)
    147 	pbit16(msg[cur:cur+2], uint16(len(s.Uid)))
    148 	cur += 2
    149 	for i := 0; i < len(s.Uid); i++ {
    150 		msg[cur+i] = s.Uid[i]
    151 	}
    152 	cur += len(s.Uid)
    153 	pbit16(msg[cur:cur+2], uint16(len(s.Gid)))
    154 	cur += 2
    155 	for i := 0; i < len(s.Gid); i++ {
    156 		msg[cur+i] = s.Gid[i]
    157 	}
    158 	cur += len(s.Gid)
    159 	pbit16(msg[cur:cur+2], uint16(len(s.Muid)))
    160 	cur += 2
    161 	for i := 0; i < len(s.Muid); i++ {
    162 		msg[cur+i] = s.Muid[i]
    163 	}
    164 	cur += len(s.Muid)
    165 
    166 	if len(msg) != cur {
    167 		panic(fmt.Errorf("cursor position %d and msg length %d don't match",
    168 			cur, len(msg)))
    169 	}
    170 	return msg
    171 }
    172 
    173 func (s *Stat) String() string {
    174 	return fmt.Sprintf("'%s' '%s' '%s' '%s' q %v m %#o at %d mt %d l %d t %d d %d",
    175 		s.Name, s.Uid, s.Gid, s.Muid, s.Qid, s.Mode,
    176 		s.Atime, s.Mtime, s.Length, s.Type, s.Dev)
    177 }
    178 
    179 // A FileInfo is a struct returned by File.Stat().
    180 // This struct is needed because of the name collision:
    181 // Stat.Name and FileInfo.Name.
    182 type FileInfo struct {
    183 	Stat Stat
    184 }
    185 
    186 func (fi *FileInfo) Name() string       { return fi.Stat.Name }
    187 func (fi *FileInfo) Size() int64        { return fi.Stat.Length }
    188 func (fi *FileInfo) Mode() fs.FileMode  { return fi.Stat.Mode }
    189 func (fi *FileInfo) ModTime() time.Time { return time.Unix(int64(fi.Stat.Mtime), 0) }
    190 func (fi *FileInfo) IsDir() bool        { return fi.Stat.Mode&fs.ModeDir != 0 }
    191 
    192 // Sys returns *Stat
    193 func (fi *FileInfo) Sys() any { return &fi.Stat }
    194 
    195 // A DirEntry is returned by File.ReadDir.
    196 type DirEntry = FileInfo
    197 
    198 func (de *DirEntry) Type() fs.FileMode          { return de.Mode().Type() }
    199 func (de *DirEntry) Info() (fs.FileInfo, error) { return de, nil }