mirror of
https://github.com/restic/restic.git
synced 2026-07-19 13:44:53 +00:00
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:
+14
@@ -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
@@ -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
@@ -1,6 +1,7 @@
|
||||
[](http://godoc.org/github.com/pkg/xattr)
|
||||
[](https://goreportcard.com/report/github.com/pkg/xattr)
|
||||
[](https://travis-ci.org/pkg/xattr)
|
||||
[](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
@@ -1 +1,3 @@
|
||||
module "github.com/pkg/xattr"
|
||||
module github.com/pkg/xattr
|
||||
|
||||
require golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
-38
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user