xattr_bsd.go

  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 freebsd || netbsd
  6
  7package unix
  8
  9import (
 10	"strings"
 11	"unsafe"
 12)
 13
 14// Derive extattr namespace and attribute name
 15
 16func xattrnamespace(fullattr string) (ns int, attr string, err error) {
 17	s := strings.IndexByte(fullattr, '.')
 18	if s == -1 {
 19		return -1, "", ENOATTR
 20	}
 21
 22	namespace := fullattr[0:s]
 23	attr = fullattr[s+1:]
 24
 25	switch namespace {
 26	case "user":
 27		return EXTATTR_NAMESPACE_USER, attr, nil
 28	case "system":
 29		return EXTATTR_NAMESPACE_SYSTEM, attr, nil
 30	default:
 31		return -1, "", ENOATTR
 32	}
 33}
 34
 35func initxattrdest(dest []byte, idx int) (d unsafe.Pointer) {
 36	if len(dest) > idx {
 37		return unsafe.Pointer(&dest[idx])
 38	}
 39	if dest != nil {
 40		// extattr_get_file and extattr_list_file treat NULL differently from
 41		// a non-NULL pointer of length zero. Preserve the property of nilness,
 42		// even if we can't use dest directly.
 43		return unsafe.Pointer(&_zero)
 44	}
 45	return nil
 46}
 47
 48// FreeBSD and NetBSD implement their own syscalls to handle extended attributes
 49
 50func Getxattr(file string, attr string, dest []byte) (sz int, err error) {
 51	d := initxattrdest(dest, 0)
 52	destsize := len(dest)
 53
 54	nsid, a, err := xattrnamespace(attr)
 55	if err != nil {
 56		return -1, err
 57	}
 58
 59	return ExtattrGetFile(file, nsid, a, uintptr(d), destsize)
 60}
 61
 62func Fgetxattr(fd int, attr string, dest []byte) (sz int, err error) {
 63	d := initxattrdest(dest, 0)
 64	destsize := len(dest)
 65
 66	nsid, a, err := xattrnamespace(attr)
 67	if err != nil {
 68		return -1, err
 69	}
 70
 71	return ExtattrGetFd(fd, nsid, a, uintptr(d), destsize)
 72}
 73
 74func Lgetxattr(link string, attr string, dest []byte) (sz int, err error) {
 75	d := initxattrdest(dest, 0)
 76	destsize := len(dest)
 77
 78	nsid, a, err := xattrnamespace(attr)
 79	if err != nil {
 80		return -1, err
 81	}
 82
 83	return ExtattrGetLink(link, nsid, a, uintptr(d), destsize)
 84}
 85
 86// flags are unused on FreeBSD
 87
 88func Fsetxattr(fd int, attr string, data []byte, flags int) (err error) {
 89	var d unsafe.Pointer
 90	if len(data) > 0 {
 91		d = unsafe.Pointer(&data[0])
 92	}
 93	datasiz := len(data)
 94
 95	nsid, a, err := xattrnamespace(attr)
 96	if err != nil {
 97		return
 98	}
 99
100	_, err = ExtattrSetFd(fd, nsid, a, uintptr(d), datasiz)
101	return
102}
103
104func Setxattr(file string, attr string, data []byte, flags int) (err error) {
105	var d unsafe.Pointer
106	if len(data) > 0 {
107		d = unsafe.Pointer(&data[0])
108	}
109	datasiz := len(data)
110
111	nsid, a, err := xattrnamespace(attr)
112	if err != nil {
113		return
114	}
115
116	_, err = ExtattrSetFile(file, nsid, a, uintptr(d), datasiz)
117	return
118}
119
120func Lsetxattr(link string, attr string, data []byte, flags int) (err error) {
121	var d unsafe.Pointer
122	if len(data) > 0 {
123		d = unsafe.Pointer(&data[0])
124	}
125	datasiz := len(data)
126
127	nsid, a, err := xattrnamespace(attr)
128	if err != nil {
129		return
130	}
131
132	_, err = ExtattrSetLink(link, nsid, a, uintptr(d), datasiz)
133	return
134}
135
136func Removexattr(file string, attr string) (err error) {
137	nsid, a, err := xattrnamespace(attr)
138	if err != nil {
139		return
140	}
141
142	err = ExtattrDeleteFile(file, nsid, a)
143	return
144}
145
146func Fremovexattr(fd int, attr string) (err error) {
147	nsid, a, err := xattrnamespace(attr)
148	if err != nil {
149		return
150	}
151
152	err = ExtattrDeleteFd(fd, nsid, a)
153	return
154}
155
156func Lremovexattr(link string, attr string) (err error) {
157	nsid, a, err := xattrnamespace(attr)
158	if err != nil {
159		return
160	}
161
162	err = ExtattrDeleteLink(link, nsid, a)
163	return
164}
165
166func Listxattr(file string, dest []byte) (sz int, err error) {
167	destsiz := len(dest)
168
169	// FreeBSD won't allow you to list xattrs from multiple namespaces
170	s, pos := 0, 0
171	for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
172		stmp, e := ListxattrNS(file, nsid, dest[pos:])
173
174		/* Errors accessing system attrs are ignored so that
175		 * we can implement the Linux-like behavior of omitting errors that
176		 * we don't have read permissions on
177		 *
178		 * Linux will still error if we ask for user attributes on a file that
179		 * we don't have read permissions on, so don't ignore those errors
180		 */
181		if e != nil {
182			if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
183				continue
184			}
185			return s, e
186		}
187
188		s += stmp
189		pos = s
190		if pos > destsiz {
191			pos = destsiz
192		}
193	}
194
195	return s, nil
196}
197
198func ListxattrNS(file string, nsid int, dest []byte) (sz int, err error) {
199	d := initxattrdest(dest, 0)
200	destsiz := len(dest)
201
202	s, e := ExtattrListFile(file, nsid, uintptr(d), destsiz)
203	if e != nil {
204		return 0, err
205	}
206
207	return s, nil
208}
209
210func Flistxattr(fd int, dest []byte) (sz int, err error) {
211	destsiz := len(dest)
212
213	s, pos := 0, 0
214	for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
215		stmp, e := FlistxattrNS(fd, nsid, dest[pos:])
216
217		if e != nil {
218			if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
219				continue
220			}
221			return s, e
222		}
223
224		s += stmp
225		pos = s
226		if pos > destsiz {
227			pos = destsiz
228		}
229	}
230
231	return s, nil
232}
233
234func FlistxattrNS(fd int, nsid int, dest []byte) (sz int, err error) {
235	d := initxattrdest(dest, 0)
236	destsiz := len(dest)
237
238	s, e := ExtattrListFd(fd, nsid, uintptr(d), destsiz)
239	if e != nil {
240		return 0, err
241	}
242
243	return s, nil
244}
245
246func Llistxattr(link string, dest []byte) (sz int, err error) {
247	destsiz := len(dest)
248
249	s, pos := 0, 0
250	for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
251		stmp, e := LlistxattrNS(link, nsid, dest[pos:])
252
253		if e != nil {
254			if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
255				continue
256			}
257			return s, e
258		}
259
260		s += stmp
261		pos = s
262		if pos > destsiz {
263			pos = destsiz
264		}
265	}
266
267	return s, nil
268}
269
270func LlistxattrNS(link string, nsid int, dest []byte) (sz int, err error) {
271	d := initxattrdest(dest, 0)
272	destsiz := len(dest)
273
274	s, e := ExtattrListLink(link, nsid, uintptr(d), destsiz)
275	if e != nil {
276		return 0, err
277	}
278
279	return s, nil
280}