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