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