1// Copyright 2018 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 darwin freebsd linux netbsd
6
7package unix_test
8
9import (
10	"io/ioutil"
11	"os"
12	"runtime"
13	"strings"
14	"testing"
15
16	"golang.org/x/sys/unix"
17)
18
19func TestXattr(t *testing.T) {
20	defer chtmpdir(t)()
21
22	f := "xattr1"
23	touch(t, f)
24
25	xattrName := "user.test"
26	xattrDataSet := "gopher"
27
28	err := unix.Setxattr(f, xattrName, []byte{}, 0)
29	if err == unix.ENOTSUP || err == unix.EOPNOTSUPP {
30		t.Skip("filesystem does not support extended attributes, skipping test")
31	} else if err != nil {
32		t.Fatalf("Setxattr: %v", err)
33	}
34
35	err = unix.Setxattr(f, xattrName, []byte(xattrDataSet), 0)
36	if err != nil {
37		t.Fatalf("Setxattr: %v", err)
38	}
39
40	// find size
41	size, err := unix.Listxattr(f, nil)
42	if err != nil {
43		t.Fatalf("Listxattr: %v", err)
44	}
45
46	if size <= 0 {
47		t.Fatalf("Listxattr returned an empty list of attributes")
48	}
49
50	buf := make([]byte, size)
51	read, err := unix.Listxattr(f, buf)
52	if err != nil {
53		t.Fatalf("Listxattr: %v", err)
54	}
55
56	xattrs := stringsFromByteSlice(buf[:read])
57
58	xattrWant := xattrName
59	if runtime.GOOS == "freebsd" {
60		// On FreeBSD, the namespace is stored separately from the xattr
61		// name and Listxattr doesn't return the namespace prefix.
62		xattrWant = strings.TrimPrefix(xattrWant, "user.")
63	}
64	found := false
65	for _, name := range xattrs {
66		if name == xattrWant {
67			found = true
68		}
69	}
70
71	if !found {
72		t.Errorf("Listxattr did not return previously set attribute '%s'", xattrName)
73	}
74
75	// find size
76	size, err = unix.Getxattr(f, xattrName, nil)
77	if err != nil {
78		t.Fatalf("Getxattr: %v", err)
79	}
80
81	if size <= 0 {
82		t.Fatalf("Getxattr returned an empty attribute")
83	}
84
85	xattrDataGet := make([]byte, size)
86	_, err = unix.Getxattr(f, xattrName, xattrDataGet)
87	if err != nil {
88		t.Fatalf("Getxattr: %v", err)
89	}
90
91	got := string(xattrDataGet)
92	if got != xattrDataSet {
93		t.Errorf("Getxattr: expected attribute value %s, got %s", xattrDataSet, got)
94	}
95
96	err = unix.Removexattr(f, xattrName)
97	if err != nil {
98		t.Fatalf("Removexattr: %v", err)
99	}
100
101	n := "nonexistent"
102	err = unix.Lsetxattr(n, xattrName, []byte(xattrDataSet), 0)
103	if err != unix.ENOENT {
104		t.Errorf("Lsetxattr: expected %v on non-existent file, got %v", unix.ENOENT, err)
105	}
106
107	_, err = unix.Lgetxattr(n, xattrName, nil)
108	if err != unix.ENOENT {
109		t.Errorf("Lgetxattr: %v", err)
110	}
111
112	s := "symlink1"
113	err = os.Symlink(n, s)
114	if err != nil {
115		t.Fatal(err)
116	}
117
118	err = unix.Lsetxattr(s, xattrName, []byte(xattrDataSet), 0)
119	if err != nil {
120		// Linux and Android doen't support xattrs on symlinks according
121		// to xattr(7), so just test that we get the proper error.
122		if (runtime.GOOS != "linux" && runtime.GOOS != "android") || err != unix.EPERM {
123			t.Fatalf("Lsetxattr: %v", err)
124		}
125	}
126}
127
128func TestFdXattr(t *testing.T) {
129	file, err := ioutil.TempFile("", "TestFdXattr")
130	if err != nil {
131		t.Fatal(err)
132	}
133	defer os.Remove(file.Name())
134	defer file.Close()
135
136	fd := int(file.Fd())
137	xattrName := "user.test"
138	xattrDataSet := "gopher"
139
140	err = unix.Fsetxattr(fd, xattrName, []byte(xattrDataSet), 0)
141	if err == unix.ENOTSUP || err == unix.EOPNOTSUPP {
142		t.Skip("filesystem does not support extended attributes, skipping test")
143	} else if err != nil {
144		t.Fatalf("Fsetxattr: %v", err)
145	}
146
147	// find size
148	size, err := unix.Flistxattr(fd, nil)
149	if err != nil {
150		t.Fatalf("Flistxattr: %v", err)
151	}
152
153	if size <= 0 {
154		t.Fatalf("Flistxattr returned an empty list of attributes")
155	}
156
157	buf := make([]byte, size)
158	read, err := unix.Flistxattr(fd, buf)
159	if err != nil {
160		t.Fatalf("Flistxattr: %v", err)
161	}
162
163	xattrs := stringsFromByteSlice(buf[:read])
164
165	xattrWant := xattrName
166	if runtime.GOOS == "freebsd" {
167		// On FreeBSD, the namespace is stored separately from the xattr
168		// name and Listxattr doesn't return the namespace prefix.
169		xattrWant = strings.TrimPrefix(xattrWant, "user.")
170	}
171	found := false
172	for _, name := range xattrs {
173		if name == xattrWant {
174			found = true
175		}
176	}
177
178	if !found {
179		t.Errorf("Flistxattr did not return previously set attribute '%s'", xattrName)
180	}
181
182	// find size
183	size, err = unix.Fgetxattr(fd, xattrName, nil)
184	if err != nil {
185		t.Fatalf("Fgetxattr: %v", err)
186	}
187
188	if size <= 0 {
189		t.Fatalf("Fgetxattr returned an empty attribute")
190	}
191
192	xattrDataGet := make([]byte, size)
193	_, err = unix.Fgetxattr(fd, xattrName, xattrDataGet)
194	if err != nil {
195		t.Fatalf("Fgetxattr: %v", err)
196	}
197
198	got := string(xattrDataGet)
199	if got != xattrDataSet {
200		t.Errorf("Fgetxattr: expected attribute value %s, got %s", xattrDataSet, got)
201	}
202
203	err = unix.Fremovexattr(fd, xattrName)
204	if err != nil {
205		t.Fatalf("Fremovexattr: %v", err)
206	}
207}
208