// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build freebsd || netbsd // +build freebsd netbsd package unix import ( "strings" "unsafe" ) // Derive extattr namespace and attribute name func xattrnamespace(fullattr string) (ns int, attr string, err error) { s := strings.IndexByte(fullattr, '.') if s == -1 { return -1, "", ENOATTR } namespace := fullattr[0:s] attr = fullattr[s+1:] switch namespace { case "user": return EXTATTR_NAMESPACE_USER, attr, nil case "system": return EXTATTR_NAMESPACE_SYSTEM, attr, nil default: return -1, "", ENOATTR } } func initxattrdest(dest []byte, idx int) (d unsafe.Pointer) { if len(dest) > idx { return unsafe.Pointer(&dest[idx]) } else { return unsafe.Pointer(_zero) } } // FreeBSD and NetBSD implement their own syscalls to handle extended attributes func Getxattr(file string, attr string, dest []byte) (sz int, err error) { d := initxattrdest(dest, 0) destsize := len(dest) nsid, a, err := xattrnamespace(attr) if err != nil { return -1, err } return ExtattrGetFile(file, nsid, a, uintptr(d), destsize) } func Fgetxattr(fd int, attr string, dest []byte) (sz int, err error) { d := initxattrdest(dest, 0) destsize := len(dest) nsid, a, err := xattrnamespace(attr) if err != nil { return -1, err } return ExtattrGetFd(fd, nsid, a, uintptr(d), destsize) } func Lgetxattr(link string, attr string, dest []byte) (sz int, err error) { d := initxattrdest(dest, 0) destsize := len(dest) nsid, a, err := xattrnamespace(attr) if err != nil { return -1, err } return ExtattrGetLink(link, nsid, a, uintptr(d), destsize) } // flags are unused on FreeBSD func Fsetxattr(fd int, attr string, data []byte, flags int) (err error) { var d unsafe.Pointer if len(data) > 0 { d = unsafe.Pointer(&data[0]) } datasiz := len(data) nsid, a, err := xattrnamespace(attr) if err != nil { return } _, err = ExtattrSetFd(fd, nsid, a, uintptr(d), datasiz) return } func Setxattr(file string, attr string, data []byte, flags int) (err error) { var d unsafe.Pointer if len(data) > 0 { d = unsafe.Pointer(&data[0]) } datasiz := len(data) nsid, a, err := xattrnamespace(attr) if err != nil { return } _, err = ExtattrSetFile(file, nsid, a, uintptr(d), datasiz) return } func Lsetxattr(link string, attr string, data []byte, flags int) (err error) { var d unsafe.Pointer if len(data) > 0 { d = unsafe.Pointer(&data[0]) } datasiz := len(data) nsid, a, err := xattrnamespace(attr) if err != nil { return } _, err = ExtattrSetLink(link, nsid, a, uintptr(d), datasiz) return } func Removexattr(file string, attr string) (err error) { nsid, a, err := xattrnamespace(attr) if err != nil { return } err = ExtattrDeleteFile(file, nsid, a) return } func Fremovexattr(fd int, attr string) (err error) { nsid, a, err := xattrnamespace(attr) if err != nil { return } err = ExtattrDeleteFd(fd, nsid, a) return } func Lremovexattr(link string, attr string) (err error) { nsid, a, err := xattrnamespace(attr) if err != nil { return } err = ExtattrDeleteLink(link, nsid, a) return } func Listxattr(file string, dest []byte) (sz int, err error) { destsiz := len(dest) // FreeBSD won't allow you to list xattrs from multiple namespaces s, pos := 0, 0 for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} { stmp, e := ListxattrNS(file, nsid, dest[pos:]) /* Errors accessing system attrs are ignored so that * we can implement the Linux-like behavior of omitting errors that * we don't have read permissions on * * Linux will still error if we ask for user attributes on a file that * we don't have read permissions on, so don't ignore those errors */ if e != nil { if e == EPERM && nsid != EXTATTR_NAMESPACE_USER { continue } return s, e } s += stmp pos = s if pos > destsiz { pos = destsiz } } return s, nil } func ListxattrNS(file string, nsid int, dest []byte) (sz int, err error) { d := initxattrdest(dest, 0) destsiz := len(dest) s, e := ExtattrListFile(file, nsid, uintptr(d), destsiz) if e != nil { return 0, err } return s, nil } func Flistxattr(fd int, dest []byte) (sz int, err error) { destsiz := len(dest) s, pos := 0, 0 for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} { stmp, e := FlistxattrNS(fd, nsid, dest[pos:]) if e != nil { if e == EPERM && nsid != EXTATTR_NAMESPACE_USER { continue } return s, e } s += stmp pos = s if pos > destsiz { pos = destsiz } } return s, nil } func FlistxattrNS(fd int, nsid int, dest []byte) (sz int, err error) { d := initxattrdest(dest, 0) destsiz := len(dest) s, e := ExtattrListFd(fd, nsid, uintptr(d), destsiz) if e != nil { return 0, err } return s, nil } func Llistxattr(link string, dest []byte) (sz int, err error) { destsiz := len(dest) s, pos := 0, 0 for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} { stmp, e := LlistxattrNS(link, nsid, dest[pos:]) if e != nil { if e == EPERM && nsid != EXTATTR_NAMESPACE_USER { continue } return s, e } s += stmp pos = s if pos > destsiz { pos = destsiz } } return s, nil } func LlistxattrNS(link string, nsid int, dest []byte) (sz int, err error) { d := initxattrdest(dest, 0) destsiz := len(dest) s, e := ExtattrListLink(link, nsid, uintptr(d), destsiz) if e != nil { return 0, err } return s, nil }