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