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 }