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