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 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} 241