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