lib9p

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

stat.go (4626B)


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