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