1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 // Plan 9 directory marshaling. See intro(5).
6 7 package syscall
8 9 import (
10 "errors"
11 "internal/byteorder"
12 )
13 14 var (
15 ErrShortStat = errors.New("stat buffer too short")
16 ErrBadStat = errors.New("malformed stat buffer")
17 ErrBadName = errors.New("bad character in file name")
18 )
19 20 // A Qid represents a 9P server's unique identification for a file.
21 type Qid struct {
22 Path uint64 // the file server's unique identification for the file
23 Vers uint32 // version number for given Path
24 Type uint8 // the type of the file (syscall.QTDIR for example)
25 }
26 27 // A Dir contains the metadata for a file.
28 type Dir struct {
29 // system-modified data
30 Type uint16 // server type
31 Dev uint32 // server subtype
32 33 // file data
34 Qid Qid // unique id from server
35 Mode uint32 // permissions
36 Atime uint32 // last read time
37 Mtime uint32 // last write time
38 Length int64 // file length
39 Name string // last element of path
40 Uid string // owner name
41 Gid string // group name
42 Muid string // last modifier name
43 }
44 45 var nullDir = Dir{
46 Type: ^uint16(0),
47 Dev: ^uint32(0),
48 Qid: Qid{
49 Path: ^uint64(0),
50 Vers: ^uint32(0),
51 Type: ^uint8(0),
52 },
53 Mode: ^uint32(0),
54 Atime: ^uint32(0),
55 Mtime: ^uint32(0),
56 Length: ^int64(0),
57 }
58 59 // Null assigns special "don't touch" values to members of d to
60 // avoid modifying them during [Wstat].
61 func (d *Dir) Null() { *d = nullDir }
62 63 // Marshal encodes a 9P stat message corresponding to d into b
64 //
65 // If there isn't enough space in b for a stat message, [ErrShortStat] is returned.
66 func (d *Dir) Marshal(b []byte) (n int, err error) {
67 n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
68 if n > len(b) {
69 return n, ErrShortStat
70 }
71 72 for _, c := range d.Name {
73 if c == '/' {
74 return n, ErrBadName
75 }
76 }
77 78 b = pbit16(b, uint16(n)-2)
79 b = pbit16(b, d.Type)
80 b = pbit32(b, d.Dev)
81 b = pbit8(b, d.Qid.Type)
82 b = pbit32(b, d.Qid.Vers)
83 b = pbit64(b, d.Qid.Path)
84 b = pbit32(b, d.Mode)
85 b = pbit32(b, d.Atime)
86 b = pbit32(b, d.Mtime)
87 b = pbit64(b, uint64(d.Length))
88 b = pstring(b, d.Name)
89 b = pstring(b, d.Uid)
90 b = pstring(b, d.Gid)
91 b = pstring(b, d.Muid)
92 93 return n, nil
94 }
95 96 // UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
97 //
98 // If b is too small to hold a valid stat message, [ErrShortStat] is returned.
99 //
100 // If the stat message itself is invalid, [ErrBadStat] is returned.
101 func UnmarshalDir(b []byte) (*Dir, error) {
102 if len(b) < STATFIXLEN {
103 return nil, ErrShortStat
104 }
105 size, buf := gbit16(b)
106 if len(b) != int(size)+2 {
107 return nil, ErrBadStat
108 }
109 b = buf
110 111 var d Dir
112 d.Type, b = gbit16(b)
113 d.Dev, b = gbit32(b)
114 d.Qid.Type, b = gbit8(b)
115 d.Qid.Vers, b = gbit32(b)
116 d.Qid.Path, b = gbit64(b)
117 d.Mode, b = gbit32(b)
118 d.Atime, b = gbit32(b)
119 d.Mtime, b = gbit32(b)
120 121 n, b := gbit64(b)
122 d.Length = int64(n)
123 124 var ok bool
125 if d.Name, b, ok = gstring(b); !ok {
126 return nil, ErrBadStat
127 }
128 if d.Uid, b, ok = gstring(b); !ok {
129 return nil, ErrBadStat
130 }
131 if d.Gid, b, ok = gstring(b); !ok {
132 return nil, ErrBadStat
133 }
134 if d.Muid, b, ok = gstring(b); !ok {
135 return nil, ErrBadStat
136 }
137 138 return &d, nil
139 }
140 141 // pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
142 func pbit8(b []byte, v uint8) []byte {
143 b[0] = byte(v)
144 return b[1:]
145 }
146 147 // pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
148 func pbit16(b []byte, v uint16) []byte {
149 byteorder.LEPutUint16(b, v)
150 return b[2:]
151 }
152 153 // pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
154 func pbit32(b []byte, v uint32) []byte {
155 byteorder.LEPutUint32(b, v)
156 return b[4:]
157 }
158 159 // pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
160 func pbit64(b []byte, v uint64) []byte {
161 byteorder.LEPutUint64(b, v)
162 return b[8:]
163 }
164 165 // pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
166 // returning the remaining slice of b..
167 func pstring(b []byte, s string) []byte {
168 b = pbit16(b, uint16(len(s)))
169 n := copy(b, s)
170 return b[n:]
171 }
172 173 // gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
174 func gbit8(b []byte) (uint8, []byte) {
175 return uint8(b[0]), b[1:]
176 }
177 178 // gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
179 //
180 //go:nosplit
181 func gbit16(b []byte) (uint16, []byte) {
182 return byteorder.LEUint16(b), b[2:]
183 }
184 185 // gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
186 func gbit32(b []byte) (uint32, []byte) {
187 return byteorder.LEUint32(b), b[4:]
188 }
189 190 // gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
191 func gbit64(b []byte) (uint64, []byte) {
192 return byteorder.LEUint64(b), b[8:]
193 }
194 195 // gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
196 // It returns the string with the remaining slice of b and a boolean. If the length is
197 // greater than the number of bytes in b, the boolean will be false.
198 func gstring(b []byte) (string, []byte, bool) {
199 n, b := gbit16(b)
200 if int(n) > len(b) {
201 return "", b, false
202 }
203 return string(b[:n]), b[n:], true
204 }
205