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 }