~rbn/neinp

ref: 55b6eee1e78f053b260d845551ee71f4777448c6 neinp/stat/stat.go -rw-r--r-- 5.1 KiB
55b6eee1Ruben Schuller move to sr.ht and go modulize 1 year, 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//Package stat provides functionality to handle 9p stat values.
package stat

import (
	"git.sr.ht/~rbn/neinp/basic"
	"git.sr.ht/~rbn/neinp/qid"
	"bytes"
	"io"
	"time"
)

/*Stat contains file attributes.

Typ and Dev are usually unused. Qid contains the Qid of this file.
Mode is a combination of UNIX-permission bits for owner, group and others.
Atime contains the time of last access, Mtime the time of last modification.
Length is the file length in bytes, directories have a conventional
length of 0. Name is the file name and must be "/" if the file is the root
directory of the server. Uid is the owners name (not UNIX uid), group is the
groups name. Muid is the name of the last user who modified the file.

http://man.cat-v.org/inferno/5/stat
*/
type Stat struct {
	//	Size   uint16
	Typ    uint16
	Dev    uint32
	Qid    qid.Qid
	Mode   Mode
	Atime  time.Time
	Mtime  time.Time
	Length uint64
	Name   string
	Uid    string
	Gid    string
	Muid   string
}

//IsDir returns true if Dir is set.
func (s *Stat) IsDir() bool {
	return s.Mode&Dir == Dir
}

//IsAppend returns true if Append is set.
func (s *Stat) IsAppend() bool {
	return s.Mode&Append == Append
}

//IsExcl returns true if Excl is set.
func (s *Stat) IsExcl() bool {
	return s.Mode&Excl == Excl
}

//IsMount returns true if Mount is set.
func (s *Stat) IsMount() bool {
	return s.Mode&Mount == Mount
}

//IsAuth returns true if Auth is set.
func (s *Stat) IsAuth() bool {
	return s.Mode&Auth == Auth
}

//IsTmp returns true if Tmp is set.
func (s *Stat) IsTmp() bool {
	return s.Mode&Tmp == Tmp
}

//IsSymlink returns true if Symlink is set.
func (s *Stat) IsSymlink() bool {
	return s.Mode&Symlink == Symlink
}

//IsDevice returns true if Device is set.
func (s *Stat) IsDevice() bool {
	return s.Mode&Device == Device
}

//IsNamedPipe returns true if NamedPipe is set.
func (s *Stat) IsNamedPipe() bool {
	return s.Mode&NamedPipe == NamedPipe
}

//IsSocket returns true if Socket is set.
func (s *Stat) IsSocket() bool {
	return s.Mode&Socket == Socket
}

//IsSetUid returns true if SetUid is set.
func (s *Stat) IsSetUid() bool {
	return s.Mode&SetUid == SetUid
}

//IsSetGid returns true if SetGid is set
func (s *Stat) IsSetGid() bool {
	return s.Mode&SetGid == SetGid
}

func (s *Stat) Encode(w io.Writer) (int64, error) {
	var buf bytes.Buffer

	_, err := basic.Uint16Encode(&buf, s.Typ)
	if err != nil {
		return 0, err
	}

	_, err = basic.Uint32Encode(&buf, s.Dev)
	if err != nil {
		return 0, err
	}

	_, err = s.Qid.Encode(&buf)
	if err != nil {
		return 0, err
	}

	_, err = basic.Uint32Encode(&buf, uint32(s.Mode))
	if err != nil {
		return 0, err
	}

	_, err = basic.Uint32Encode(&buf, uint32(s.Atime.Unix()))
	if err != nil {
		return 0, err
	}

	_, err = basic.Uint32Encode(&buf, uint32(s.Mtime.Unix()))
	if err != nil {
		return 0, err
	}

	_, err = basic.Uint64Encode(&buf, s.Length)
	if err != nil {
		return 0, err
	}

	_, err = basic.StringEncode(&buf, s.Name)
	if err != nil {
		return 0, err
	}

	_, err = basic.StringEncode(&buf, s.Uid)
	if err != nil {
		return 0, err
	}

	_, err = basic.StringEncode(&buf, s.Gid)
	if err != nil {
		return 0, err
	}

	_, err = basic.StringEncode(&buf, s.Muid)
	if err != nil {
		return 0, err
	}

	/* From plan9 manual:
	BUGS
	To make the contents of a directory, such as returned by
	read(9P), easy to parse, each directory entry begins with a
	size field.  For consistency, the entries in Twstat and
	Rstat messages also contain their size, which means the size
	appears twice.  For example, the Rstat message is formatted
	as ``(4+1+2+2+n)[4] Rstat tag[2] n[2] (n-2)[2] type[2]
	dev[4]...,'' where n is the value returned by convD2M.
	*/
	size := uint16(buf.Len()) // we don't need to subtract 2 because size isn't in buf

	n1, err := basic.Uint16Encode(w, size)
	if err != nil {
		return n1, err
	}

	n2, err := buf.WriteTo(w)
	if err != nil {
		return n1 + n2, err
	}

	return n1 + n2, nil
}

func (s *Stat) Decode(r io.Reader) (int64, error) {
	size, n1, err := basic.Uint16Decode(r)
	if err != nil {
		return n1, err
	}

	b := make([]byte, size)
	n2, err := r.Read(b)
	if err != nil {
		return n1 + int64(n2), err
	}

	n := n1 + int64(n2)

	buf := bytes.NewBuffer(b)

	typ, _, err := basic.Uint16Decode(buf)
	if err != nil {
		return n, err
	}

	dev, _, err := basic.Uint32Decode(buf)
	if err != nil {
		return n, err
	}

	_, err = s.Qid.Decode(buf)
	if err != nil {
		return n, err
	}

	mod, _, err := basic.Uint32Decode(buf)
	if err != nil {
		return n, err
	}

	atime, _, err := basic.Uint32Decode(buf)
	if err != nil {
		return n, err
	}

	mtime, _, err := basic.Uint32Decode(buf)
	if err != nil {
		return n, err
	}

	length, _, err := basic.Uint64Decode(buf)
	if err != nil {
		return n, err
	}

	name, _, err := basic.StringDecode(buf)
	if err != nil {
		return n, err
	}

	uid, _, err := basic.StringDecode(buf)
	if err != nil {
		return n, err
	}

	gid, _, err := basic.StringDecode(buf)
	if err != nil {
		return n, err
	}

	muid, _, err := basic.StringDecode(buf)
	if err != nil {
		return n, err
	}

	s.Typ = typ
	s.Dev = dev
	s.Mode = Mode(mod)
	s.Atime = time.Unix(int64(atime), 0)
	s.Mtime = time.Unix(int64(mtime), 0)
	s.Length = length
	s.Name = name
	s.Uid = uid
	s.Gid = gid
	s.Muid = muid

	return n, nil
}