1// Copyright 2014 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// +build freebsd
6
7package unix_test
8
9import (
10	"flag"
11	"fmt"
12	"io/ioutil"
13	"os"
14	"os/exec"
15	"path"
16	"path/filepath"
17	"runtime"
18	"testing"
19
20	"golang.org/x/sys/unix"
21)
22
23func TestSysctlUint64(t *testing.T) {
24	_, err := unix.SysctlUint64("vm.swap_total")
25	if err != nil {
26		t.Fatal(err)
27	}
28}
29
30// FIXME: Infrastructure for launching tests in subprocesses stolen from openbsd_test.go - refactor?
31// testCmd generates a proper command that, when executed, runs the test
32// corresponding to the given key.
33
34type testProc struct {
35	fn      func()                    // should always exit instead of returning
36	arg     func(t *testing.T) string // generate argument for test
37	cleanup func(arg string) error    // for instance, delete coredumps from testing pledge
38	success bool                      // whether zero-exit means success or failure
39}
40
41var (
42	testProcs = map[string]testProc{}
43	procName  = ""
44	procArg   = ""
45)
46
47const (
48	optName = "sys-unix-internal-procname"
49	optArg  = "sys-unix-internal-arg"
50)
51
52func init() {
53	flag.StringVar(&procName, optName, "", "internal use only")
54	flag.StringVar(&procArg, optArg, "", "internal use only")
55
56}
57
58func testCmd(procName string, procArg string) (*exec.Cmd, error) {
59	exe, err := filepath.Abs(os.Args[0])
60	if err != nil {
61		return nil, err
62	}
63	cmd := exec.Command(exe, "-"+optName+"="+procName, "-"+optArg+"="+procArg)
64	cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
65	return cmd, nil
66}
67
68// ExitsCorrectly is a comprehensive, one-line-of-use wrapper for testing
69// a testProc with a key.
70func ExitsCorrectly(t *testing.T, procName string) {
71	s := testProcs[procName]
72	arg := "-"
73	if s.arg != nil {
74		arg = s.arg(t)
75	}
76	c, err := testCmd(procName, arg)
77	defer func(arg string) {
78		if err := s.cleanup(arg); err != nil {
79			t.Fatalf("Failed to run cleanup for %s %s %#v", procName, err, err)
80		}
81	}(arg)
82	if err != nil {
83		t.Fatalf("Failed to construct command for %s", procName)
84	}
85	if (c.Run() == nil) != s.success {
86		result := "succeed"
87		if !s.success {
88			result = "fail"
89		}
90		t.Fatalf("Process did not %s when it was supposed to", result)
91	}
92}
93
94func TestMain(m *testing.M) {
95	flag.Parse()
96	if procName != "" {
97		t := testProcs[procName]
98		t.fn()
99		os.Stderr.WriteString("test function did not exit\n")
100		if t.success {
101			os.Exit(1)
102		} else {
103			os.Exit(0)
104		}
105	}
106	os.Exit(m.Run())
107}
108
109// end of infrastructure
110
111const testfile = "gocapmodetest"
112const testfile2 = testfile + "2"
113
114func CapEnterTest() {
115	_, err := os.OpenFile(path.Join(procArg, testfile), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
116	if err != nil {
117		panic(fmt.Sprintf("OpenFile: %s", err))
118	}
119
120	err = unix.CapEnter()
121	if err != nil {
122		panic(fmt.Sprintf("CapEnter: %s", err))
123	}
124
125	_, err = os.OpenFile(path.Join(procArg, testfile2), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
126	if err == nil {
127		panic("OpenFile works!")
128	}
129	if err.(*os.PathError).Err != unix.ECAPMODE {
130		panic(fmt.Sprintf("OpenFile failed wrong: %s %#v", err, err))
131	}
132	os.Exit(0)
133}
134
135func makeTempDir(t *testing.T) string {
136	d, err := ioutil.TempDir("", "go_openat_test")
137	if err != nil {
138		t.Fatalf("TempDir failed: %s", err)
139	}
140	return d
141}
142
143func removeTempDir(arg string) error {
144	err := os.RemoveAll(arg)
145	if err != nil && err.(*os.PathError).Err == unix.ENOENT {
146		return nil
147	}
148	return err
149}
150
151func init() {
152	testProcs["cap_enter"] = testProc{
153		CapEnterTest,
154		makeTempDir,
155		removeTempDir,
156		true,
157	}
158}
159
160func TestCapEnter(t *testing.T) {
161	if runtime.GOARCH != "amd64" {
162		t.Skipf("skipping test on %s", runtime.GOARCH)
163	}
164	ExitsCorrectly(t, "cap_enter")
165}
166
167func OpenatTest() {
168	f, err := os.Open(procArg)
169	if err != nil {
170		panic(err)
171	}
172
173	err = unix.CapEnter()
174	if err != nil {
175		panic(fmt.Sprintf("CapEnter: %s", err))
176	}
177
178	fxx, err := unix.Openat(int(f.Fd()), "xx", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
179	if err != nil {
180		panic(err)
181	}
182	unix.Close(fxx)
183
184	// The right to open BASE/xx is not ambient
185	_, err = os.OpenFile(procArg+"/xx", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
186	if err == nil {
187		panic("OpenFile succeeded")
188	}
189	if err.(*os.PathError).Err != unix.ECAPMODE {
190		panic(fmt.Sprintf("OpenFile failed wrong: %s %#v", err, err))
191	}
192
193	// Can't make a new directory either
194	err = os.Mkdir(procArg+"2", 0777)
195	if err == nil {
196		panic("MKdir succeeded")
197	}
198	if err.(*os.PathError).Err != unix.ECAPMODE {
199		panic(fmt.Sprintf("Mkdir failed wrong: %s %#v", err, err))
200	}
201
202	// Remove all caps except read and lookup.
203	r, err := unix.CapRightsInit([]uint64{unix.CAP_READ, unix.CAP_LOOKUP})
204	if err != nil {
205		panic(fmt.Sprintf("CapRightsInit failed: %s %#v", err, err))
206	}
207	err = unix.CapRightsLimit(f.Fd(), r)
208	if err != nil {
209		panic(fmt.Sprintf("CapRightsLimit failed: %s %#v", err, err))
210	}
211
212	// Check we can get the rights back again
213	r, err = unix.CapRightsGet(f.Fd())
214	if err != nil {
215		panic(fmt.Sprintf("CapRightsGet failed: %s %#v", err, err))
216	}
217	b, err := unix.CapRightsIsSet(r, []uint64{unix.CAP_READ, unix.CAP_LOOKUP})
218	if err != nil {
219		panic(fmt.Sprintf("CapRightsIsSet failed: %s %#v", err, err))
220	}
221	if !b {
222		panic(fmt.Sprintf("Unexpected rights"))
223	}
224	b, err = unix.CapRightsIsSet(r, []uint64{unix.CAP_READ, unix.CAP_LOOKUP, unix.CAP_WRITE})
225	if err != nil {
226		panic(fmt.Sprintf("CapRightsIsSet failed: %s %#v", err, err))
227	}
228	if b {
229		panic(fmt.Sprintf("Unexpected rights (2)"))
230	}
231
232	// Can no longer create a file
233	_, err = unix.Openat(int(f.Fd()), "xx2", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
234	if err == nil {
235		panic("Openat succeeded")
236	}
237	if err != unix.ENOTCAPABLE {
238		panic(fmt.Sprintf("OpenFileAt failed wrong: %s %#v", err, err))
239	}
240
241	// But can read an existing one
242	_, err = unix.Openat(int(f.Fd()), "xx", os.O_RDONLY, 0666)
243	if err != nil {
244		panic(fmt.Sprintf("Openat failed: %s %#v", err, err))
245	}
246
247	os.Exit(0)
248}
249
250func init() {
251	testProcs["openat"] = testProc{
252		OpenatTest,
253		makeTempDir,
254		removeTempDir,
255		true,
256	}
257}
258
259func TestOpenat(t *testing.T) {
260	if runtime.GOARCH != "amd64" {
261		t.Skipf("skipping test on %s", runtime.GOARCH)
262	}
263	ExitsCorrectly(t, "openat")
264}
265
266func TestCapRightsSetAndClear(t *testing.T) {
267	r, err := unix.CapRightsInit([]uint64{unix.CAP_READ, unix.CAP_WRITE, unix.CAP_PDWAIT})
268	if err != nil {
269		t.Fatalf("CapRightsInit failed: %s", err)
270	}
271
272	err = unix.CapRightsSet(r, []uint64{unix.CAP_EVENT, unix.CAP_LISTEN})
273	if err != nil {
274		t.Fatalf("CapRightsSet failed: %s", err)
275	}
276
277	b, err := unix.CapRightsIsSet(r, []uint64{unix.CAP_READ, unix.CAP_WRITE, unix.CAP_PDWAIT, unix.CAP_EVENT, unix.CAP_LISTEN})
278	if err != nil {
279		t.Fatalf("CapRightsIsSet failed: %s", err)
280	}
281	if !b {
282		t.Fatalf("Wrong rights set")
283	}
284
285	err = unix.CapRightsClear(r, []uint64{unix.CAP_READ, unix.CAP_PDWAIT})
286	if err != nil {
287		t.Fatalf("CapRightsClear failed: %s", err)
288	}
289
290	b, err = unix.CapRightsIsSet(r, []uint64{unix.CAP_WRITE, unix.CAP_EVENT, unix.CAP_LISTEN})
291	if err != nil {
292		t.Fatalf("CapRightsIsSet failed: %s", err)
293	}
294	if !b {
295		t.Fatalf("Wrong rights set")
296	}
297}
298
299// stringsFromByteSlice converts a sequence of attributes to a []string.
300// On FreeBSD, each entry consists of a single byte containing the length
301// of the attribute name, followed by the attribute name.
302// The name is _not_ NULL-terminated.
303func stringsFromByteSlice(buf []byte) []string {
304	var result []string
305	i := 0
306	for i < len(buf) {
307		next := i + 1 + int(buf[i])
308		result = append(result, string(buf[i+1:next]))
309		i = next
310	}
311	return result
312}
313