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
7package syscall
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 (syscall.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 syscall.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.
187//go:nosplit
188func gbit16(b []byte) (uint16, []byte) {
189	return uint16(b[0]) | uint16(b[1])<<8, b[2:]
190}
191
192// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
193func gbit32(b []byte) (uint32, []byte) {
194	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
195}
196
197// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
198func gbit64(b []byte) (uint64, []byte) {
199	lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
200	hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
201	return uint64(lo) | uint64(hi)<<32, b[8:]
202}
203
204// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
205// It returns the string with the remaining slice of b and a boolean. If the length is
206// greater than the number of bytes in b, the boolean will be false.
207func gstring(b []byte) (string, []byte, bool) {
208	n, b := gbit16(b)
209	if int(n) > len(b) {
210		return "", b, false
211	}
212	return string(b[:n]), b[n:], true
213}
214