mirror of
https://github.com/octoleo/restic.git
synced 2024-11-22 21:05:10 +00:00
Use github.com/pkg/xattr for extended attributes
This commit is contained in:
parent
0674f32d79
commit
eb9be4e884
12
vendor/manifest
vendored
12
vendor/manifest
vendored
@ -19,12 +19,6 @@
|
|||||||
"revision": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75",
|
"revision": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"importpath": "github.com/ivaxer/go-xattr",
|
|
||||||
"repository": "https://github.com/ivaxer/go-xattr",
|
|
||||||
"revision": "1a541654d8e447148cf23d472c948f9f0078ac50",
|
|
||||||
"branch": "master"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"importpath": "github.com/kr/fs",
|
"importpath": "github.com/kr/fs",
|
||||||
"repository": "https://github.com/kr/fs",
|
"repository": "https://github.com/kr/fs",
|
||||||
@ -55,6 +49,12 @@
|
|||||||
"revision": "8197a2e580736b78d704be0fc47b2324c0591a32",
|
"revision": "8197a2e580736b78d704be0fc47b2324c0591a32",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"importpath": "github.com/pkg/xattr",
|
||||||
|
"repository": "https://github.com/pkg/xattr",
|
||||||
|
"revision": "1d40b70a947cd8e8457e4715e1123f8e99f5f241",
|
||||||
|
"branch": "master"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"importpath": "github.com/restic/chunker",
|
"importpath": "github.com/restic/chunker",
|
||||||
"repository": "https://github.com/restic/chunker",
|
"repository": "https://github.com/restic/chunker",
|
||||||
|
10
vendor/src/github.com/ivaxer/go-xattr/README.md
vendored
10
vendor/src/github.com/ivaxer/go-xattr/README.md
vendored
@ -1,10 +0,0 @@
|
|||||||
xattr
|
|
||||||
=====
|
|
||||||
|
|
||||||
Package xattr provides a simple interface to user extended attributes on Linux and OSX.
|
|
||||||
|
|
||||||
Install it: `go get github.com/ivaxer/go-xattr`
|
|
||||||
|
|
||||||
Documentation is available on [godoc.org](http://godoc.org/github.com/ivaxer/go-xattr).
|
|
||||||
|
|
||||||
License: Simplified BSD License (see LICENSE).
|
|
@ -1,156 +0,0 @@
|
|||||||
package xattr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func get(path, attr string, buf []byte) (rs int, err error) {
|
|
||||||
return getxattr(path, attr, buf, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getxattr retrieves value of the extended attribute identified by attr
|
|
||||||
// associated with given path in filesystem into buffer buf.
|
|
||||||
//
|
|
||||||
// options specify options for retrieving extended attributes:
|
|
||||||
// - syscall.XATTR_NOFOLLOW
|
|
||||||
// - syscall.XATTR_SHOWCOMPRESSION
|
|
||||||
//
|
|
||||||
// position should be zero. For advanded usage see getxattr(2).
|
|
||||||
//
|
|
||||||
// On success, buf contains data associated with attr, retrieved value size sz
|
|
||||||
// and nil error returned.
|
|
||||||
//
|
|
||||||
// On error, non-nil error returned. It returns error if buf was to small.
|
|
||||||
//
|
|
||||||
// A nil slice can be passed as buf to get current size of attribute value,
|
|
||||||
// which can be used to estimate buf length for value associated with attr.
|
|
||||||
//
|
|
||||||
// See getxattr(2) for more details.
|
|
||||||
//
|
|
||||||
// ssize_t getxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options);
|
|
||||||
func getxattr(path, name string, buf []byte, position, options int) (sz int, err error) {
|
|
||||||
p, err := syscall.BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := syscall.BytePtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var b *byte
|
|
||||||
if len(buf) > 0 {
|
|
||||||
b = &buf[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR,
|
|
||||||
uintptr(unsafe.Pointer(p)),
|
|
||||||
uintptr(unsafe.Pointer(n)),
|
|
||||||
uintptr(unsafe.Pointer(b)),
|
|
||||||
uintptr(len(buf)),
|
|
||||||
uintptr(position),
|
|
||||||
uintptr(options))
|
|
||||||
|
|
||||||
sz = int(r0)
|
|
||||||
if e1 != 0 {
|
|
||||||
err = e1
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func list(path string, dest []byte) (sz int, err error) {
|
|
||||||
return listxattr(path, dest, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ssize_t listxattr(const char *path, char *namebuf, size_t size, int options);
|
|
||||||
func listxattr(path string, buf []byte, options int) (sz int, err error) {
|
|
||||||
p, err := syscall.BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var b *byte
|
|
||||||
if len(buf) > 0 {
|
|
||||||
b = &buf[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
r0, _, e1 := syscall.Syscall6(syscall.SYS_LISTXATTR,
|
|
||||||
uintptr(unsafe.Pointer(p)),
|
|
||||||
uintptr(unsafe.Pointer(b)),
|
|
||||||
uintptr(len(buf)),
|
|
||||||
uintptr(options), 0, 0)
|
|
||||||
|
|
||||||
sz = int(r0)
|
|
||||||
if e1 != 0 {
|
|
||||||
err = e1
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(path, attr string, data []byte, flags int) error {
|
|
||||||
return setxattr(path, attr, data, 0, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// int setxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options);
|
|
||||||
func setxattr(path string, name string, data []byte, position, options int) (err error) {
|
|
||||||
p, err := syscall.BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := syscall.BytePtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var b *byte
|
|
||||||
if len(data) > 0 {
|
|
||||||
b = &data[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR,
|
|
||||||
uintptr(unsafe.Pointer(p)),
|
|
||||||
uintptr(unsafe.Pointer(n)),
|
|
||||||
uintptr(unsafe.Pointer(b)),
|
|
||||||
uintptr(len(data)),
|
|
||||||
uintptr(position),
|
|
||||||
uintptr(options))
|
|
||||||
|
|
||||||
if e1 != 0 {
|
|
||||||
err = e1
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(path, attr string) error {
|
|
||||||
return removexattr(path, attr, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// int removexattr(const char *path, const char *name, int options);
|
|
||||||
func removexattr(path string, name string, options int) (err error) {
|
|
||||||
p, err := syscall.BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := syscall.BytePtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR,
|
|
||||||
uintptr(unsafe.Pointer(p)),
|
|
||||||
uintptr(unsafe.Pointer(n)),
|
|
||||||
uintptr(options))
|
|
||||||
|
|
||||||
if e1 != 0 {
|
|
||||||
err = e1
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package xattr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func get(path, attr string, dest []byte) (sz int, err error) {
|
|
||||||
return syscall.Getxattr(path, attr, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
func list(path string, dest []byte) (sz int, err error) {
|
|
||||||
return syscall.Listxattr(path, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(path, attr string, data []byte, flags int) error {
|
|
||||||
return syscall.Setxattr(path, attr, data, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(path, attr string) error {
|
|
||||||
return syscall.Removexattr(path, attr)
|
|
||||||
}
|
|
171
vendor/src/github.com/ivaxer/go-xattr/xattr.go
vendored
171
vendor/src/github.com/ivaxer/go-xattr/xattr.go
vendored
@ -1,171 +0,0 @@
|
|||||||
// Package xattr provides a simple interface to user extended attributes on
|
|
||||||
// Linux and OSX. Support for xattrs is filesystem dependant, so not a given
|
|
||||||
// even if you are running one of those operating systems.
|
|
||||||
//
|
|
||||||
// On Linux you have to edit /etc/fstab to include "user_xattr". Also, on Linux
|
|
||||||
// user's extended attributes have a manditory prefix of "user.".
|
|
||||||
package xattr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsNotExist returns a boolean indicating whether the error is known to report
|
|
||||||
// that an extended attribute does not exist.
|
|
||||||
func IsNotExist(err error) bool {
|
|
||||||
if e, ok := err.(*os.PathError); ok {
|
|
||||||
err = e.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
return isNotExist(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts an array of NUL 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getxattr retrieves value of the extended attribute identified by attr
|
|
||||||
// associated with given path in filesystem into buffer dest.
|
|
||||||
//
|
|
||||||
// On success, dest contains data associated with attr, retrieved value size sz
|
|
||||||
// and nil error are returned.
|
|
||||||
//
|
|
||||||
// On error, non-nil error is returned. Getxattr returns error if dest was too
|
|
||||||
// small for attribute value.
|
|
||||||
//
|
|
||||||
// A nil slice can be passed as dest to get current size of attribute value,
|
|
||||||
// which can be used to estimate dest length for value associated with attr.
|
|
||||||
//
|
|
||||||
// See getxattr(2) for more information.
|
|
||||||
//
|
|
||||||
// Get is high-level function on top of Getxattr. Getxattr more efficient,
|
|
||||||
// because it issues one syscall per call, doesn't allocate memory for
|
|
||||||
// attribute data (caller can reuse buffer).
|
|
||||||
func Getxattr(path, attr string, dest []byte) (sz int, err error) {
|
|
||||||
return get(path, attr, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves extended attribute data associated with path. If there is an
|
|
||||||
// error, it will be of type *os.PathError.
|
|
||||||
//
|
|
||||||
// See Getxattr for low-level usage.
|
|
||||||
func Get(path, attr string) ([]byte, error) {
|
|
||||||
// find size
|
|
||||||
size, err := Getxattr(path, attr, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &os.PathError{"getxattr", path, err}
|
|
||||||
}
|
|
||||||
if size == 0 {
|
|
||||||
return []byte{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// read into buffer of that size
|
|
||||||
buf := make([]byte, size)
|
|
||||||
size, err = Getxattr(path, attr, buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &os.PathError{"getxattr", path, err}
|
|
||||||
}
|
|
||||||
return buf[:size], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listxattr retrieves the list of extended attribute names associated with
|
|
||||||
// path. The list is set of NULL-terminated names.
|
|
||||||
//
|
|
||||||
// On success, dest containes list of NULL-terminated names, the length of the
|
|
||||||
// extended attribute list and nil error are returned.
|
|
||||||
//
|
|
||||||
// On error, non nil error is returned. Listxattr returns error if dest buffer
|
|
||||||
// was too small for extended attribute list.
|
|
||||||
//
|
|
||||||
// The list of names is returned as an unordered array of NULL-terminated
|
|
||||||
// character strings (attribute names are separated by NULL characters), like
|
|
||||||
// this:
|
|
||||||
// user.name1\0system.name1\0user.name2\0
|
|
||||||
//
|
|
||||||
// A nil slice can be passed as dest to get the current size of the list of
|
|
||||||
// extended attribute names, which can be used to estimate dest length for
|
|
||||||
// the list of names.
|
|
||||||
//
|
|
||||||
// See listxattr(2) for more information.
|
|
||||||
//
|
|
||||||
// List is high-level function on top of Listxattr.
|
|
||||||
func Listxattr(path string, dest []byte) (sz int, err error) {
|
|
||||||
return list(path, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
// List retrieves a list of names of extended attributes associated with path.
|
|
||||||
// If there is an error, it will be of type *os.PathError.
|
|
||||||
//
|
|
||||||
// See Listxattr for low-level usage.
|
|
||||||
func List(path string) ([]string, error) {
|
|
||||||
// find size
|
|
||||||
size, err := Listxattr(path, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &os.PathError{"listxattr", path, err}
|
|
||||||
}
|
|
||||||
if size == 0 {
|
|
||||||
return []string{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// read into buffer of that size
|
|
||||||
buf := make([]byte, size)
|
|
||||||
size, err = Listxattr(path, buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &os.PathError{"listxattr", path, err}
|
|
||||||
}
|
|
||||||
return nullTermToStrings(buf[:size]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setxattr sets value in data of extended attribute attr and accosiated with
|
|
||||||
// path.
|
|
||||||
//
|
|
||||||
// The flags refine the semantic of the operation. XATTR_CREATE specifies pure
|
|
||||||
// create, which fails if attr already exists. XATTR_REPLACE specifies a pure
|
|
||||||
// replace operation, which fails if the attr does not already exist. By
|
|
||||||
// default (no flags), the attr will be created if need be, or will simply
|
|
||||||
// replace the value if attr exists.
|
|
||||||
//
|
|
||||||
// On error, non nil error is returned.
|
|
||||||
//
|
|
||||||
// See setxattr(2) for more information.
|
|
||||||
func Setxattr(path, attr string, data []byte, flags int) error {
|
|
||||||
return set(path, attr, data, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set associates data as an extended attribute of path. If there is an error,
|
|
||||||
// it will be of type *os.PathError.
|
|
||||||
//
|
|
||||||
// See Setxattr for low-level usage.
|
|
||||||
func Set(path, attr string, data []byte) error {
|
|
||||||
if err := Setxattr(path, attr, data, 0); err != nil {
|
|
||||||
return &os.PathError{"setxattr", path, err}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removexattr removes the extended attribute attr accosiated with path.
|
|
||||||
//
|
|
||||||
// On error, non-nil error is returned.
|
|
||||||
//
|
|
||||||
// See removexattr(2) for more information.
|
|
||||||
func Removexattr(path, attr string) error {
|
|
||||||
return remove(path, attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the extended attribute. If there is an error, it will be of
|
|
||||||
// type *os.PathError.
|
|
||||||
func Remove(path, attr string) error {
|
|
||||||
if err := Removexattr(path, attr); err != nil {
|
|
||||||
return &os.PathError{"removexattr", path, err}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package xattr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func isNotExist(err error) bool {
|
|
||||||
return err == syscall.ENOATTR
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package xattr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func isNotExist(err error) bool {
|
|
||||||
return err == syscall.ENODATA
|
|
||||||
}
|
|
153
vendor/src/github.com/ivaxer/go-xattr/xattr_test.go
vendored
153
vendor/src/github.com/ivaxer/go-xattr/xattr_test.go
vendored
@ -1,153 +0,0 @@
|
|||||||
package xattr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tmpdir = os.Getenv("TEST_XATTR_PATH")
|
|
||||||
|
|
||||||
func mktemp(t *testing.T) *os.File {
|
|
||||||
file, err := ioutil.TempFile(tmpdir, "test_xattr_")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("TempFile() failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringsEqual(got, expected []string) bool {
|
|
||||||
if len(got) != len(expected) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range got {
|
|
||||||
if got[i] != expected[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// expected must be sorted slice of attribute names.
|
|
||||||
func checkList(t *testing.T, path string, expected []string) {
|
|
||||||
got, err := List(path)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("List(%q) failed: %v", path, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(got)
|
|
||||||
|
|
||||||
if !stringsEqual(got, expected) {
|
|
||||||
t.Errorf("List(%q): expected %v, got %v", path, got, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkListError(t *testing.T, path string, f func(error) bool) {
|
|
||||||
got, err := List(path)
|
|
||||||
if !f(err) {
|
|
||||||
t.Errorf("List(%q): unexpected error value: %v", path, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got != nil {
|
|
||||||
t.Error("List(): expected nil slice on error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSet(t *testing.T, path, attr string, data []byte) {
|
|
||||||
if err := Set(path, attr, data); err != nil {
|
|
||||||
t.Fatalf("Set(%q, %q, %v) failed: %v", path, attr, data, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSetError(t *testing.T, path, attr string, data []byte, f func(error) bool) {
|
|
||||||
if err := Set(path, attr, data); !f(err) {
|
|
||||||
t.Fatalf("Set(%q, %q, %v): unexpected error value: %v", path, attr, data, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkGet(t *testing.T, path, attr string, expected []byte) {
|
|
||||||
got, err := Get(path, attr)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Get(%q, %q) failed: %v", path, attr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(got, expected) {
|
|
||||||
t.Errorf("Get(%q, %q): got %v, expected %v", path, attr, got, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkGetError(t *testing.T, path, attr string, f func(error) bool) {
|
|
||||||
got, err := Get(path, attr)
|
|
||||||
if !f(err) {
|
|
||||||
t.Errorf("Get(%q, %q): unexpected error value: %v", path, attr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got != nil {
|
|
||||||
t.Error("Get(): expected nil slice on error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRemove(t *testing.T, path, attr string) {
|
|
||||||
if err := Remove(path, attr); err != nil {
|
|
||||||
t.Fatalf("Remove(%q, %q) failed: %v", path, attr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRemoveError(t *testing.T, path, attr string, f func(error) bool) {
|
|
||||||
if err := Remove(path, attr); !f(err) {
|
|
||||||
t.Errorf("Remove(%q, %q): unexpected error value: %v", path, attr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlow(t *testing.T) {
|
|
||||||
f := mktemp(t)
|
|
||||||
defer func() { f.Close(); os.Remove(f.Name()) }()
|
|
||||||
|
|
||||||
path := f.Name()
|
|
||||||
data := []byte("test xattr data")
|
|
||||||
attr := "user.test xattr"
|
|
||||||
attr2 := "user.text xattr 2"
|
|
||||||
|
|
||||||
checkList(t, path, []string{})
|
|
||||||
checkSet(t, path, attr, data)
|
|
||||||
checkList(t, path, []string{attr})
|
|
||||||
checkSet(t, path, attr2, data)
|
|
||||||
checkList(t, path, []string{attr, attr2})
|
|
||||||
checkGet(t, path, attr, data)
|
|
||||||
checkGetError(t, path, "user.unknown attr", IsNotExist)
|
|
||||||
checkRemove(t, path, attr)
|
|
||||||
checkList(t, path, []string{attr2})
|
|
||||||
checkRemove(t, path, attr2)
|
|
||||||
checkList(t, path, []string{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyAttr(t *testing.T) {
|
|
||||||
f := mktemp(t)
|
|
||||||
defer func() { f.Close(); os.Remove(f.Name()) }()
|
|
||||||
|
|
||||||
path := f.Name()
|
|
||||||
attr := "user.test xattr"
|
|
||||||
data := []byte{}
|
|
||||||
|
|
||||||
checkSet(t, path, attr, data)
|
|
||||||
checkList(t, path, []string{attr})
|
|
||||||
checkGet(t, path, attr, []byte{})
|
|
||||||
checkRemove(t, path, attr)
|
|
||||||
checkList(t, path, []string{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoFile(t *testing.T) {
|
|
||||||
path := "no-such-file"
|
|
||||||
attr := "user.test xattr"
|
|
||||||
data := []byte("test_xattr data")
|
|
||||||
|
|
||||||
checkListError(t, path, os.IsNotExist)
|
|
||||||
checkSetError(t, path, attr, data, os.IsNotExist)
|
|
||||||
checkGetError(t, path, attr, os.IsNotExist)
|
|
||||||
checkRemoveError(t, path, attr, os.IsNotExist)
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
Copyright (c) 2012 Dave Cheney. All rights reserved.
|
Copyright (c) 2012 Dave Cheney. All rights reserved.
|
||||||
Copyright (c) 2013 Alexey Palazhchenko. All rights reserved.
|
Copyright (c) 2014 Kuba Podgórski. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
21
vendor/src/github.com/pkg/xattr/README.md
vendored
Normal file
21
vendor/src/github.com/pkg/xattr/README.md
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
xattr
|
||||||
|
=====
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```
|
||||||
|
const path = "/tmp/myfile"
|
||||||
|
const prefix = "user."
|
||||||
|
|
||||||
|
if err = Setxattr(path, prefix+"test", []byte("test-attr-value")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
data, err = Getxattr(path, prefix+"test"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
```
|
39
vendor/src/github.com/pkg/xattr/syscall_darwin.go
vendored
Normal file
39
vendor/src/github.com/pkg/xattr/syscall_darwin.go
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// +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
vendor/src/github.com/pkg/xattr/syscall_freebsd.go
vendored
Normal file
91
vendor/src/github.com/pkg/xattr/syscall_freebsd.go
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// +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
|
||||||
|
}
|
26
vendor/src/github.com/pkg/xattr/xattr.go
vendored
Normal file
26
vendor/src/github.com/pkg/xattr/xattr.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package xattr
|
||||||
|
|
||||||
|
// XAttrError records an error and the operation, file path and attribute that caused it.
|
||||||
|
type XAttrError struct {
|
||||||
|
Op string
|
||||||
|
Path string
|
||||||
|
Name string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *XAttrError) Error() string {
|
||||||
|
return e.Op + " " + e.Path + " " + e.Name + ": " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
56
vendor/src/github.com/pkg/xattr/xattr_darwin.go
vendored
Normal file
56
vendor/src/github.com/pkg/xattr/xattr_darwin.go
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package xattr
|
||||||
|
|
||||||
|
// Retrieve extended attribute data associated with path.
|
||||||
|
func Getxattr(path, name string) ([]byte, error) {
|
||||||
|
// find size.
|
||||||
|
size, err := getxattr(path, name, nil, 0, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"getxattr", path, name, err}
|
||||||
|
}
|
||||||
|
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, &XAttrError{"getxattr", path, name, err}
|
||||||
|
}
|
||||||
|
return buf[:read], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves a list of names of extended attributes associated with the
|
||||||
|
// given path in the file system.
|
||||||
|
func Listxattr(path string) ([]string, error) {
|
||||||
|
// find size.
|
||||||
|
size, err := listxattr(path, nil, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"listxattr", path, "", err}
|
||||||
|
}
|
||||||
|
if size == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, size)
|
||||||
|
// Read into buffer of that size.
|
||||||
|
read, err := listxattr(path, &buf[0], size, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"listxattr", path, "", err}
|
||||||
|
}
|
||||||
|
return nullTermToStrings(buf[:read]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Associates name and data together as an attribute of path.
|
||||||
|
func Setxattr(path, name string, data []byte) error {
|
||||||
|
if err := setxattr(path, name, &data[0], len(data), 0, 0); err != nil {
|
||||||
|
return &XAttrError{"setxattr", path, name, err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the attribute.
|
||||||
|
func Removexattr(path, name string) error {
|
||||||
|
if err := removexattr(path, name, 0); err != nil {
|
||||||
|
return &XAttrError{"removexattr", path, name, err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
79
vendor/src/github.com/pkg/xattr/xattr_freebsd.go
vendored
Normal file
79
vendor/src/github.com/pkg/xattr/xattr_freebsd.go
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// +build freebsd
|
||||||
|
|
||||||
|
package xattr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EXTATTR_NAMESPACE_USER = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Retrieve extended attribute data associated with path.
|
||||||
|
func Getxattr(path, name string) ([]byte, error) {
|
||||||
|
// find size.
|
||||||
|
size, err := extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"extattr_get_file", path, name, err}
|
||||||
|
}
|
||||||
|
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, &XAttrError{"extattr_get_file", path, name, err}
|
||||||
|
}
|
||||||
|
return buf[:read], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves a list of names of extended attributes associated with the
|
||||||
|
// given path in the file system.
|
||||||
|
func Listxattr(path string) ([]string, error) {
|
||||||
|
// find size.
|
||||||
|
size, err := extattr_list_file(path, EXTATTR_NAMESPACE_USER, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"extattr_list_file", path, "", err}
|
||||||
|
}
|
||||||
|
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, &XAttrError{"extattr_list_file", path, "", err}
|
||||||
|
}
|
||||||
|
return attrListToStrings(buf[:read]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Associates name and data together as an attribute of path.
|
||||||
|
func Setxattr(path, name string, data []byte) error {
|
||||||
|
written, err := extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, &data[0], len(data))
|
||||||
|
if err != nil {
|
||||||
|
return &XAttrError{"extattr_set_file", path, name, err}
|
||||||
|
}
|
||||||
|
if written != len(data) {
|
||||||
|
return &XAttrError{"extattr_set_file", path, name, syscall.E2BIG}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the attribute.
|
||||||
|
func Removexattr(path, name string) error {
|
||||||
|
if err := extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name); err != nil {
|
||||||
|
return &XAttrError{"extattr_delete_file", path, name, err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a sequnce of attribute name entries to a []string.
|
||||||
|
// 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
|
||||||
|
index := 0
|
||||||
|
for index < len(buf) {
|
||||||
|
next := index + 1 + int(buf[index])
|
||||||
|
result = append(result, string(buf[index+1:next]))
|
||||||
|
index = next
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
54
vendor/src/github.com/pkg/xattr/xattr_linux.go
vendored
Normal file
54
vendor/src/github.com/pkg/xattr/xattr_linux.go
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package xattr
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// Retrieve extended attribute data associated with path.
|
||||||
|
func Getxattr(path, name string) ([]byte, error) {
|
||||||
|
// find size.
|
||||||
|
size, err := syscall.Getxattr(path, name, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"getxattr", path, name, err}
|
||||||
|
}
|
||||||
|
data := make([]byte, size)
|
||||||
|
// Read into buffer of that size.
|
||||||
|
read, err := syscall.Getxattr(path, name, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"getxattr", path, name, err}
|
||||||
|
}
|
||||||
|
return data[:read], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves a list of names of extended attributes associated with the
|
||||||
|
// given path in the file system.
|
||||||
|
func Listxattr(path string) ([]string, error) {
|
||||||
|
// find size.
|
||||||
|
size, err := syscall.Listxattr(path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"listxattr", path, "", err}
|
||||||
|
}
|
||||||
|
buf := make([]byte, size)
|
||||||
|
// Read into buffer of that size.
|
||||||
|
read, err := syscall.Listxattr(path, buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &XAttrError{"listxattr", path, "", err}
|
||||||
|
}
|
||||||
|
return nullTermToStrings(buf[:read]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Associates name and data together as an attribute of path.
|
||||||
|
func Setxattr(path, name string, data []byte) error {
|
||||||
|
if err := syscall.Setxattr(path, name, data, 0); err != nil {
|
||||||
|
return &XAttrError{"setxattr", path, name, err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the attribute.
|
||||||
|
func Removexattr(path, name string) error {
|
||||||
|
if err := syscall.Removexattr(path, name); err != nil {
|
||||||
|
return &XAttrError{"removexattr", path, name, err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
57
vendor/src/github.com/pkg/xattr/xattr_test.go
vendored
Normal file
57
vendor/src/github.com/pkg/xattr/xattr_test.go
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// +build linux darwin freebsd
|
||||||
|
|
||||||
|
package xattr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const UserPrefix = "user."
|
||||||
|
|
||||||
|
func Test_setxattr(t *testing.T) {
|
||||||
|
tmp, err := ioutil.TempFile("", "")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tmp.Name())
|
||||||
|
|
||||||
|
err = Setxattr(tmp.Name(), UserPrefix+"test", []byte("test-attr-value"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := Listxattr(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 = Getxattr(tmp.Name(), UserPrefix+"test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
value := string(data)
|
||||||
|
t.Log(value)
|
||||||
|
if "test-attr-value" != value {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Removexattr(tmp.Name(), UserPrefix+"test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user