Update dependencies, enable pruning for vendor/

So, `dep` got an nice new feature to remove tests and non-go files from
`vendor/`, and this brings the size of the vendor directory from ~300MiB
down to ~20MiB. We don that now.
This commit is contained in:
Alexander Neumann
2018-08-01 21:32:15 +02:00
parent 3422c1ca83
commit bff635bc5f
6741 changed files with 26940 additions and 4902031 deletions
Generated Vendored Executable
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh -e
echo "Building for Linux..."
GOOS=linux go build
echo "Building for Darwin..."
GOOS=darwin go build
echo "Building for FreeBSD..."
GOOS=freebsd go build
echo "Running tests..."
go vet
go test -v -race -covermode=atomic
+2 -2
View File
@@ -14,10 +14,10 @@ install:
- go version
- export GOBIN="$GOPATH/bin"
- export PATH="$PATH:$GOBIN"
- go get golang.org/x/sys/unix
- go get golang.org/x/tools/cmd/goimports
- go build
script:
- go vet
- go test -v -race
- ./.travis.sh
- diff <(GOPATH="$PWD:$PWD/vendor" goimports -d .) <(printf "")
+11
View File
@@ -1,6 +1,7 @@
[![GoDoc](https://godoc.org/github.com/pkg/xattr?status.svg)](http://godoc.org/github.com/pkg/xattr)
[![Go Report Card](https://goreportcard.com/badge/github.com/pkg/xattr)](https://goreportcard.com/report/github.com/pkg/xattr)
[![Build Status](https://travis-ci.org/pkg/xattr.svg?branch=master)](https://travis-ci.org/pkg/xattr)
[![Version](https://badge.fury.io/gh/pkg%2Fxattr.svg)](https://github.com/pkg/xattr/releases)
xattr
=====
@@ -8,6 +9,11 @@ Extended attribute support for Go (linux + darwin + freebsd).
"Extended attributes are name:value pairs associated permanently with files and directories, similar to the environment strings associated with a process. An attribute may be defined or undefined. If it is defined, its value may be empty or non-empty." [See more...](https://en.wikipedia.org/wiki/Extended_file_attributes)
`SetWithFlags` allows to additionally pass system flags to be forwarded to the underlying calls, FreeBSD does not support this and the parameter will be ignored.
The `L` variants of all functions (`LGet/LSet/...`) are identical to `Get/Set/...` except that they
do not reference a symlink that appears at the end of a path. See
[GoDoc](http://godoc.org/github.com/pkg/xattr) for details.
### Example
```go
@@ -31,4 +37,9 @@ Extended attribute support for Go (linux + darwin + freebsd).
if err = xattr.Remove(path, prefix+"test"); err != nil {
log.Fatal(err)
}
// One can also specify the flags parameter to be passed to the OS.
if err := xattr.SetWithFlags(path, prefix+"test", []byte("test-attr-value"), xattr.XATTR_CREATE); err != nil {
log.Fatal(err)
}
```
+3 -1
View File
@@ -1 +1,3 @@
module "github.com/pkg/xattr"
module github.com/pkg/xattr
require golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43
+1
View File
@@ -0,0 +1 @@
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-38
View File
@@ -1,38 +0,0 @@
// +build darwin
package xattr
import (
"syscall"
"unsafe"
)
func getxattr(path string, name string, value *byte, size int, pos int, options int) (int, error) {
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))), uintptr(unsafe.Pointer(syscall.StringBytePtr(name))), uintptr(unsafe.Pointer(value)), uintptr(size), uintptr(pos), uintptr(options))
if e1 != syscall.Errno(0) {
return int(r0), e1
}
return int(r0), nil
}
func listxattr(path string, namebuf *byte, size int, options int) (int, error) {
r0, _, e1 := syscall.Syscall6(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))), uintptr(unsafe.Pointer(namebuf)), uintptr(size), uintptr(options), 0, 0)
if e1 != syscall.Errno(0) {
return int(r0), e1
}
return int(r0), nil
}
func setxattr(path string, name string, value *byte, size int, pos int, options int) error {
if _, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))), uintptr(unsafe.Pointer(syscall.StringBytePtr(name))), uintptr(unsafe.Pointer(value)), uintptr(size), uintptr(pos), uintptr(options)); e1 != syscall.Errno(0) {
return e1
}
return nil
}
func removexattr(path string, name string, options int) error {
if _, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))), uintptr(unsafe.Pointer(syscall.StringBytePtr(name))), uintptr(options)); e1 != syscall.Errno(0) {
return e1
}
return nil
}
-91
View File
@@ -1,91 +0,0 @@
// +build freebsd
package xattr
import (
"syscall"
"unsafe"
)
/*
ssize_t
extattr_get_file(const char *path, int attrnamespace,
const char *attrname, void *data, size_t nbytes);
ssize_t
extattr_set_file(const char *path, int attrnamespace,
const char *attrname, const void *data, size_t nbytes);
int
extattr_delete_file(const char *path, int attrnamespace,
const char *attrname);
ssize_t
extattr_list_file(const char *path, int attrnamespace, void *data,
size_t nbytes);
*/
func extattr_get_file(path string, attrnamespace int, attrname string, data *byte, nbytes int) (int, error) {
r, _, e := syscall.Syscall6(
syscall.SYS_EXTATTR_GET_FILE,
uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
uintptr(attrnamespace),
uintptr(unsafe.Pointer(syscall.StringBytePtr(attrname))),
uintptr(unsafe.Pointer(data)),
uintptr(nbytes),
0,
)
var err error
if e != 0 {
err = e
}
return int(r), err
}
func extattr_set_file(path string, attrnamespace int, attrname string, data *byte, nbytes int) (int, error) {
r, _, e := syscall.Syscall6(
syscall.SYS_EXTATTR_SET_FILE,
uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
uintptr(attrnamespace),
uintptr(unsafe.Pointer(syscall.StringBytePtr(attrname))),
uintptr(unsafe.Pointer(data)),
uintptr(nbytes),
0,
)
var err error
if e != 0 {
err = e
}
return int(r), err
}
func extattr_delete_file(path string, attrnamespace int, attrname string) error {
_, _, e := syscall.Syscall(
syscall.SYS_EXTATTR_DELETE_FILE,
uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
uintptr(attrnamespace),
uintptr(unsafe.Pointer(syscall.StringBytePtr(attrname))),
)
var err error
if e != 0 {
err = e
}
return err
}
func extattr_list_file(path string, attrnamespace int, data *byte, nbytes int) (int, error) {
r, _, e := syscall.Syscall6(
syscall.SYS_EXTATTR_LIST_FILE,
uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
uintptr(attrnamespace),
uintptr(unsafe.Pointer(data)),
uintptr(nbytes),
0,
0,
)
var err error
if e != 0 {
err = e
}
return int(r), err
}
+162 -8
View File
@@ -3,10 +3,22 @@ Package xattr provides support for extended attributes on linux, darwin and free
Extended attributes are name:value pairs associated permanently with files and directories,
similar to the environment strings associated with a process.
An attribute may be defined or undefined. If it is defined, its value may be empty or non-empty.
More details you can find here: https://en.wikipedia.org/wiki/Extended_file_attributes
More details you can find here: https://en.wikipedia.org/wiki/Extended_file_attributes .
All functions are provided in pairs: Get/LGet, Set/LSet etc. The "L"
variant will not follow a symlink at the end of the path.
Example assuming path is "/symlink1/symlink2", where both components are
symlinks:
Get will follow "symlink1" and "symlink2" and operate on the target of
"symlink2". LGet will follow "symlink1" but operate directly on "symlink2".
*/
package xattr
import (
"syscall"
)
// Error records an error and the operation, file path and attribute that caused it.
type Error struct {
Op string
@@ -19,14 +31,156 @@ func (e *Error) Error() string {
return e.Op + " " + e.Path + " " + e.Name + ": " + e.Err.Error()
}
// nullTermToStrings converts an array of NULL terminated UTF-8 strings to a []string.
func nullTermToStrings(buf []byte) (result []string) {
offset := 0
for index, b := range buf {
if b == 0 {
result = append(result, string(buf[offset:index]))
offset = index + 1
// Get retrieves extended attribute data associated with path. It will follow
// all symlinks along the path.
func Get(path, name string) ([]byte, error) {
return get(path, name, getxattr)
}
// LGet is like Get but does not follow a symlink at the end of the path.
func LGet(path, name string) ([]byte, error) {
return get(path, name, lgetxattr)
}
type getxattrFunc func(path string, name string, data []byte) (int, error)
// get contains the buffer allocation logic used by both Get and LGet.
func get(path string, name string, getxattrFunc getxattrFunc) ([]byte, error) {
const (
// Start with a 1 KB buffer for the xattr value
initialBufSize = 1024
// The theoretical maximum xattr value size on MacOS is 64 MB. On Linux it's
// much smaller at 64 KB. Unless the kernel is evil or buggy, we should never
// hit the limit.
maxBufSize = 64 * 1024 * 1024
// Function name as reported in error messages
myname = "xattr.get"
)
size := initialBufSize
for {
data := make([]byte, size)
read, err := getxattrFunc(path, name, data)
// If the buffer was too small to fit the value, Linux and MacOS react
// differently:
// Linux: returns an ERANGE error and "-1" bytes.
// MacOS: truncates the value and returns "size" bytes. If the value
// happens to be exactly as big as the buffer, we cannot know if it was
// truncated, and we retry with a bigger buffer. Contrary to documentation,
// MacOS never seems to return ERANGE!
// To keep the code simple, we always check both conditions, and sometimes
// double the buffer size without it being strictly neccessary.
if err == syscall.ERANGE || read == size {
// The buffer was too small. Try again.
size <<= 1
if size >= maxBufSize {
return nil, &Error{myname, path, name, syscall.EOVERFLOW}
}
continue
}
if err != nil {
return nil, &Error{myname, path, name, err}
}
return data[:read], nil
}
}
// Set associates name and data together as an attribute of path.
func Set(path, name string, data []byte) error {
if err := setxattr(path, name, data, 0); err != nil {
return &Error{"xattr.Set", path, name, err}
}
return nil
}
// LSet is like Set but does not follow a symlink at
// the end of the path.
func LSet(path, name string, data []byte) error {
if err := lsetxattr(path, name, data, 0); err != nil {
return &Error{"xattr.LSet", path, name, err}
}
return nil
}
// SetWithFlags associates name and data together as an attribute of path.
// Forwards the flags parameter to the syscall layer.
func SetWithFlags(path, name string, data []byte, flags int) error {
if err := setxattr(path, name, data, flags); err != nil {
return &Error{"xattr.SetWithFlags", path, name, err}
}
return nil
}
// LSetWithFlags is like SetWithFlags but does not follow a symlink at
// the end of the path.
func LSetWithFlags(path, name string, data []byte, flags int) error {
if err := lsetxattr(path, name, data, flags); err != nil {
return &Error{"xattr.LSetWithFlags", path, name, err}
}
return nil
}
// Remove removes the attribute associated with the given path.
func Remove(path, name string) error {
if err := removexattr(path, name); err != nil {
return &Error{"xattr.Remove", path, name, err}
}
return nil
}
// LRemove is like Remove but does not follow a symlink at the end of the
// path.
func LRemove(path, name string) error {
if err := lremovexattr(path, name); err != nil {
return &Error{"xattr.LRemove", path, name, err}
}
return nil
}
// List retrieves a list of names of extended attributes associated
// with the given path in the file system.
func List(path string) ([]string, error) {
return list(path, listxattr)
}
// LList is like List but does not follow a symlink at the end of the
// path.
func LList(path string) ([]string, error) {
return list(path, llistxattr)
}
type listxattrFunc func(path string, data []byte) (int, error)
// list contains the buffer allocation logic used by both List and LList.
func list(path string, listxattrFunc listxattrFunc) ([]string, error) {
myname := "xattr.list"
// find size.
size, err := listxattrFunc(path, nil)
if err != nil {
return nil, &Error{myname, path, "", err}
}
if size > 0 {
// `size + 1` because of ERANGE error when reading
// from a SMB1 mount point (https://github.com/pkg/xattr/issues/16).
buf := make([]byte, size+1)
// Read into buffer of that size.
read, err := listxattrFunc(path, buf)
if err != nil {
return nil, &Error{myname, path, "", err}
}
return stringsFromByteSlice(buf[:read]), nil
}
return []string{}, nil
}
// bytePtrFromSlice returns a pointer to array of bytes and a size.
func bytePtrFromSlice(data []byte) (ptr *byte, size int) {
size = len(data)
if size > 0 {
ptr = &data[0]
}
return
}
+94 -55
View File
@@ -2,72 +2,111 @@
package xattr
import "syscall"
import (
"syscall"
"unsafe"
// Get retrieves extended attribute data associated with path.
func Get(path, name string) ([]byte, error) {
// find size.
size, err := getxattr(path, name, nil, 0, 0, 0)
if err != nil {
return nil, &Error{"xattr.Get", path, name, err}
}
if size > 0 {
buf := make([]byte, size)
// Read into buffer of that size.
read, err := getxattr(path, name, &buf[0], size, 0, 0)
if err != nil {
return nil, &Error{"xattr.Get", path, name, err}
}
return buf[:read], nil
}
return []byte{}, nil
"golang.org/x/sys/unix"
)
// See https://opensource.apple.com/source/xnu/xnu-1504.15.3/bsd/sys/xattr.h.auto.html
const (
XATTR_NOFOLLOW = 0x0001
XATTR_CREATE = 0x0002
XATTR_REPLACE = 0x0004
XATTR_NOSECURITY = 0x0008
XATTR_NODEFAULT = 0x0010
XATTR_SHOWCOMPRESSION = 0x0020
// ENOATTR is not exported by the syscall package on Linux, because it is
// an alias for ENODATA. We export it here so it is available on all
// our supported platforms.
ENOATTR = syscall.ENOATTR
)
func getxattr(path string, name string, data []byte) (int, error) {
return unix.Getxattr(path, name, data)
}
// List retrieves a list of names of extended attributes associated
// with the given path in the file system.
func List(path string) ([]string, error) {
// find size.
size, err := listxattr(path, nil, 0, 0)
if err != nil {
return nil, &Error{"xattr.List", path, "", err}
func lgetxattr(path string, name string, data []byte) (int, error) {
value, size := bytePtrFromSlice(data)
/*
ssize_t getxattr(
const char *path,
const char *name,
void *value,
size_t size,
u_int32_t position,
int options
)
*/
r0, _, err := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
uintptr(unsafe.Pointer(syscall.StringBytePtr(name))), uintptr(unsafe.Pointer(value)), uintptr(size), 0, XATTR_NOFOLLOW)
if err != syscall.Errno(0) {
return int(r0), err
}
if size > 0 {
buf := make([]byte, size)
// Read into buffer of that size.
read, err := listxattr(path, &buf[0], size, 0)
if err != nil {
return nil, &Error{"xattr.List", path, "", err}
}
return nullTermToStrings(buf[:read]), nil
}
return []string{}, nil
return int(r0), nil
}
// Set associates name and data together as an attribute of path.
func Set(path, name string, data []byte) error {
var dataval *byte
datalen := len(data)
if datalen > 0 {
dataval = &data[0]
}
if err := setxattr(path, name, dataval, datalen, 0, 0); err != nil {
return &Error{"xattr.Set", path, name, err}
func setxattr(path string, name string, data []byte, flags int) error {
return unix.Setxattr(path, name, data, flags)
}
func lsetxattr(path string, name string, data []byte, flags int) error {
return unix.Setxattr(path, name, data, flags|XATTR_NOFOLLOW)
}
func removexattr(path string, name string) error {
return unix.Removexattr(path, name)
}
func lremovexattr(path string, name string) error {
/*
int removexattr(
const char *path,
const char *name,
int options
);
*/
_, _, err := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
uintptr(unsafe.Pointer(syscall.StringBytePtr(name))), XATTR_NOFOLLOW)
if err != syscall.Errno(0) {
return err
}
return nil
}
// Remove removes the attribute associated with the given path.
func Remove(path, name string) error {
if err := removexattr(path, name, 0); err != nil {
return &Error{"xattr.Remove", path, name, err}
}
return nil
func listxattr(path string, data []byte) (int, error) {
return unix.Listxattr(path, data)
}
// Supported checks if filesystem supports extended attributes
func Supported(path string) bool {
if _, err := listxattr(path, nil, 0, 0); err != nil {
return err != syscall.ENOTSUP
func llistxattr(path string, data []byte) (int, error) {
name, size := bytePtrFromSlice(data)
/*
ssize_t listxattr(
const char *path,
char *name,
size_t size,
int options
);
*/
r0, _, err := syscall.Syscall6(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
uintptr(unsafe.Pointer(name)), uintptr(size), XATTR_NOFOLLOW, 0, 0)
if err != syscall.Errno(0) {
return int(r0), err
}
return true
return int(r0), nil
}
// stringsFromByteSlice converts a sequence of attributes to a []string.
// On Darwin and Linux, each entry is a NULL-terminated string.
func stringsFromByteSlice(buf []byte) (result []string) {
offset := 0
for index, b := range buf {
if b == 0 {
result = append(result, string(buf[offset:index]))
offset = index + 1
}
}
return
}
+144 -62
View File
@@ -4,95 +4,177 @@ package xattr
import (
"syscall"
"unsafe"
)
const (
EXTATTR_NAMESPACE_USER = 1
// ENOATTR is not exported by the syscall package on Linux, because it is
// an alias for ENODATA. We export it here so it is available on all
// our supported platforms.
ENOATTR = syscall.ENOATTR
)
// Get retrieves extended attribute data associated with path.
func Get(path, name string) ([]byte, error) {
// find size.
size, err := extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, nil, 0)
if err != nil {
return nil, &Error{"xattr.Get", path, name, err}
}
if size > 0 {
buf := make([]byte, size)
// Read into buffer of that size.
read, err := extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, &buf[0], size)
if err != nil {
return nil, &Error{"xattr.Get", path, name, err}
}
return buf[:read], nil
}
return []byte{}, nil
func getxattr(path string, name string, data []byte) (int, error) {
return sysGet(syscall.SYS_EXTATTR_GET_FILE, path, name, data)
}
// List retrieves a list of names of extended attributes associated
// with the given path in the file system.
func List(path string) ([]string, error) {
// find size.
size, err := extattr_list_file(path, EXTATTR_NAMESPACE_USER, nil, 0)
if err != nil {
return nil, &Error{"xattr.List", path, "", err}
}
if size > 0 {
buf := make([]byte, size)
// Read into buffer of that size.
read, err := extattr_list_file(path, EXTATTR_NAMESPACE_USER, &buf[0], size)
if err != nil {
return nil, &Error{"xattr.List", path, "", err}
}
return attrListToStrings(buf[:read]), nil
}
return []string{}, nil
func lgetxattr(path string, name string, data []byte) (int, error) {
return sysGet(syscall.SYS_EXTATTR_GET_LINK, path, name, data)
}
// Set associates name and data together as an attribute of path.
func Set(path, name string, data []byte) error {
var dataval *byte
datalen := len(data)
if datalen > 0 {
dataval = &data[0]
// sysGet is called by getxattr and lgetxattr with the appropriate syscall
// number. This works because syscalls have the same signature and return
// values.
func sysGet(syscallNum uintptr, path string, name string, data []byte) (int, error) {
ptr, nbytes := bytePtrFromSlice(data)
/*
ssize_t extattr_get_file(
const char *path,
int attrnamespace,
const char *attrname,
void *data,
size_t nbytes);
ssize_t extattr_get_link(
const char *path,
int attrnamespace,
const char *attrname,
void *data,
size_t nbytes);
*/
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0)
if err != syscall.Errno(0) {
return int(r0), err
}
written, err := extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, dataval, datalen)
if err != nil {
return &Error{"xattr.Set", path, name, err}
return int(r0), nil
}
func setxattr(path string, name string, data []byte, flags int) error {
return sysSet(syscall.SYS_EXTATTR_SET_FILE, path, name, data)
}
func lsetxattr(path string, name string, data []byte, flags int) error {
return sysSet(syscall.SYS_EXTATTR_SET_LINK, path, name, data)
}
// sysSet is called by setxattr and lsetxattr with the appropriate syscall
// number. This works because syscalls have the same signature and return
// values.
func sysSet(syscallNum uintptr, path string, name string, data []byte) error {
ptr, nbytes := bytePtrFromSlice(data)
/*
ssize_t extattr_set_file(
const char *path,
int attrnamespace,
const char *attrname,
const void *data,
size_t nbytes
);
ssize_t extattr_set_link(
const char *path,
int attrnamespace,
const char *attrname,
const void *data,
size_t nbytes
);
*/
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0)
if err != syscall.Errno(0) {
return err
}
if written != datalen {
return &Error{"xattr.Set", path, name, syscall.E2BIG}
if int(r0) != nbytes {
return syscall.E2BIG
}
return nil
}
// Remove removes the attribute associated with the given path.
func Remove(path, name string) error {
if err := extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name); err != nil {
return &Error{"xattr.Remove", path, name, err}
func removexattr(path string, name string) error {
return sysRemove(syscall.SYS_EXTATTR_DELETE_FILE, path, name)
}
func lremovexattr(path string, name string) error {
return sysRemove(syscall.SYS_EXTATTR_DELETE_LINK, path, name)
}
// sysSet is called by removexattr and lremovexattr with the appropriate syscall
// number. This works because syscalls have the same signature and return
// values.
func sysRemove(syscallNum uintptr, path string, name string) error {
/*
int extattr_delete_file(
const char *path,
int attrnamespace,
const char *attrname
);
int extattr_delete_link(
const char *path,
int attrnamespace,
const char *attrname
);
*/
_, _, err := syscall.Syscall(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
)
if err != syscall.Errno(0) {
return err
}
return nil
}
// Supported checks if filesystem supports extended attributes
func Supported(path string) bool {
if _, err := extattr_list_file(path, EXTATTR_NAMESPACE_USER, nil, 0); err != nil {
return err != syscall.ENOTSUP
}
return true
func listxattr(path string, data []byte) (int, error) {
return sysList(syscall.SYS_EXTATTR_LIST_FILE, path, data)
}
// attrListToStrings converts a sequnce of attribute name entries to a []string.
// Each entry consists of a single byte containing the length
func llistxattr(path string, data []byte) (int, error) {
return sysList(syscall.SYS_EXTATTR_LIST_LINK, path, data)
}
// sysSet is called by listxattr and llistxattr with the appropriate syscall
// number. This works because syscalls have the same signature and return
// values.
func sysList(syscallNum uintptr, path string, data []byte) (int, error) {
ptr, nbytes := bytePtrFromSlice(data)
/*
ssize_t extattr_list_file(
const char *path,
int attrnamespace,
void *data,
size_t nbytes
);
ssize_t extattr_list_link(
const char *path,
int attrnamespace,
void *data,
size_t nbytes
);
*/
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0, 0)
if err != syscall.Errno(0) {
return int(r0), err
}
return int(r0), nil
}
// stringsFromByteSlice converts a sequence of attributes to a []string.
// On FreeBSD, each entry consists of a single byte containing the length
// of the attribute name, followed by the attribute name.
// The name is _not_ terminated by NUL.
func attrListToStrings(buf []byte) []string {
var result []string
// The name is _not_ terminated by NULL.
func stringsFromByteSlice(buf []byte) (result []string) {
index := 0
for index < len(buf) {
next := index + 1 + int(buf[index])
result = append(result, string(buf[index+1:next]))
index = next
}
return result
return
}
+56 -63
View File
@@ -2,70 +2,63 @@
package xattr
import "syscall"
import (
"syscall"
// Get retrieves extended attribute data associated with path.
func Get(path, name string) ([]byte, error) {
// find size.
size, err := syscall.Getxattr(path, name, nil)
if err != nil {
return nil, &Error{"xattr.Get", path, name, err}
}
if size > 0 {
data := make([]byte, size)
// Read into buffer of that size.
read, err := syscall.Getxattr(path, name, data)
if err != nil {
return nil, &Error{"xattr.Get", path, name, err}
"golang.org/x/sys/unix"
)
const (
XATTR_CREATE = unix.XATTR_CREATE
XATTR_REPLACE = unix.XATTR_REPLACE
// ENOATTR is not exported by the syscall package on Linux, because it is
// an alias for ENODATA. We export it here so it is available on all
// our supported platforms.
ENOATTR = syscall.ENODATA
)
func getxattr(path string, name string, data []byte) (int, error) {
return unix.Getxattr(path, name, data)
}
func lgetxattr(path string, name string, data []byte) (int, error) {
return unix.Lgetxattr(path, name, data)
}
func setxattr(path string, name string, data []byte, flags int) error {
return unix.Setxattr(path, name, data, flags)
}
func lsetxattr(path string, name string, data []byte, flags int) error {
return unix.Lsetxattr(path, name, data, flags)
}
func removexattr(path string, name string) error {
return unix.Removexattr(path, name)
}
func lremovexattr(path string, name string) error {
return unix.Lremovexattr(path, name)
}
func listxattr(path string, data []byte) (int, error) {
return unix.Listxattr(path, data)
}
func llistxattr(path string, data []byte) (int, error) {
return unix.Llistxattr(path, data)
}
// stringsFromByteSlice converts a sequence of attributes to a []string.
// On Darwin and Linux, each entry is a NULL-terminated string.
func stringsFromByteSlice(buf []byte) (result []string) {
offset := 0
for index, b := range buf {
if b == 0 {
result = append(result, string(buf[offset:index]))
offset = index + 1
}
return data[:read], nil
}
return []byte{}, nil
}
// List retrieves a list of names of extended attributes associated
// with the given path in the file system.
func List(path string) ([]string, error) {
// find size.
size, err := syscall.Listxattr(path, nil)
if err != nil {
return nil, &Error{"xattr.List", path, "", err}
}
if size > 0 {
// `size + 1` because of ERANGE error when reading
// from a SMB1 mount point (https://github.com/pkg/xattr/issues/16).
buf := make([]byte, size+1)
// Read into buffer of that size.
read, err := syscall.Listxattr(path, buf)
if err != nil {
return nil, &Error{"xattr.List", path, "", err}
}
return nullTermToStrings(buf[:read]), nil
}
return []string{}, nil
}
// Set associates name and data together as an attribute of path.
func Set(path, name string, data []byte) error {
if err := syscall.Setxattr(path, name, data, 0); err != nil {
return &Error{"xattr.Set", path, name, err}
}
return nil
}
// Remove removes the attribute associated
// with the given path.
func Remove(path, name string) error {
if err := syscall.Removexattr(path, name); err != nil {
return &Error{"xattr.Remove", path, name, err}
}
return nil
}
// Supported checks if filesystem supports extended attributes
func Supported(path string) bool {
if _, err := syscall.Listxattr(path, nil); err != nil {
return err != syscall.ENOTSUP
}
return true
return
}
-95
View File
@@ -1,95 +0,0 @@
// +build linux darwin freebsd
package xattr
import (
"io/ioutil"
"os"
"testing"
)
const UserPrefix = "user."
func Test(t *testing.T) {
tmp, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmp.Name())
// Check if filesystem supports extended attributes
if !Supported(tmp.Name()) {
t.Skip("Skipping test - filesystem does not support extended attributes")
}
err = Set(tmp.Name(), UserPrefix+"test", []byte("test-attr-value"))
if err != nil {
t.Fatal(err)
}
list, err := List(tmp.Name())
if err != nil {
t.Fatal(err)
}
found := false
for _, name := range list {
if name == UserPrefix+"test" {
found = true
}
}
if !found {
t.Fatal("Listxattr did not return test attribute")
}
var data []byte
data, err = Get(tmp.Name(), UserPrefix+"test")
if err != nil {
t.Fatal(err)
}
value := string(data)
t.Log(value)
if "test-attr-value" != value {
t.Fail()
}
err = Remove(tmp.Name(), UserPrefix+"test")
if err != nil {
t.Fatal(err)
}
}
func TestNoData(t *testing.T) {
tmp, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmp.Name())
// Check if filesystem supports extended attributes
if !Supported(tmp.Name()) {
t.Skip("Skipping test - filesystem does not support extended attributes")
}
err = Set(tmp.Name(), UserPrefix+"test", []byte{})
if err != nil {
t.Fatal(err)
}
list, err := List(tmp.Name())
if err != nil {
t.Fatal(err)
}
found := false
for _, name := range list {
if name == UserPrefix+"test" {
found = true
}
}
if !found {
t.Fatal("Listxattr did not return test attribute")
}
}