forked from TrueCloudLab/restic
commit
23c2717ab2
20 changed files with 465 additions and 577 deletions
|
@ -577,9 +577,8 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
|
|||
case "symlink":
|
||||
node.LinkTarget, err = fs.Readlink(path)
|
||||
node.Links = uint64(stat.nlink())
|
||||
err = errors.Wrap(err, "Readlink")
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "Readlink")
|
||||
}
|
||||
case "dev":
|
||||
node.Device = uint64(stat.rdev())
|
||||
|
@ -597,26 +596,36 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (node *Node) fillExtendedAttributes(path string) error {
|
||||
if node.Type == "symlink" {
|
||||
return nil
|
||||
}
|
||||
|
||||
xattrs, err := Listxattr(path)
|
||||
if err == nil {
|
||||
node.ExtendedAttributes = make([]ExtendedAttribute, len(xattrs))
|
||||
for i, attr := range xattrs {
|
||||
debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
node.ExtendedAttributes = make([]ExtendedAttribute, 0, len(xattrs))
|
||||
for _, attr := range xattrs {
|
||||
attrVal, err := Getxattr(path, attr)
|
||||
if err != nil {
|
||||
return errors.Errorf("can not obtain extended attribute %v for %v:\n", attr, path)
|
||||
fmt.Fprintf(os.Stderr, "can not obtain extended attribute %v for %v:\n", attr, path)
|
||||
continue
|
||||
}
|
||||
node.ExtendedAttributes[i].Name = attr
|
||||
node.ExtendedAttributes[i].Value = attrVal
|
||||
attr := ExtendedAttribute{
|
||||
Name: attr,
|
||||
Value: attrVal,
|
||||
}
|
||||
|
||||
node.ExtendedAttributes = append(node.ExtendedAttributes, attr)
|
||||
}
|
||||
return err
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type statT interface {
|
||||
|
|
|
@ -9,19 +9,3 @@ func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespe
|
|||
func (s statUnix) atim() syscall.Timespec { return s.Atimespec }
|
||||
func (s statUnix) mtim() syscall.Timespec { return s.Mtimespec }
|
||||
func (s statUnix) ctim() syscall.Timespec { return s.Ctimespec }
|
||||
|
||||
// Getxattr retrieves extended attribute data associated with path.
|
||||
func Getxattr(path, name string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Listxattr retrieves a list of names of extended attributes associated with the
|
||||
// given path in the file system.
|
||||
func Listxattr(path string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Setxattr associates name and data together as an attribute of path.
|
||||
func Setxattr(path, name string, data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,38 +1,39 @@
|
|||
// +build !openbsd
|
||||
// +build !windows
|
||||
// +build !freebsd
|
||||
|
||||
package restic
|
||||
|
||||
import (
|
||||
"github.com/ivaxer/go-xattr"
|
||||
"restic/errors"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/xattr"
|
||||
)
|
||||
|
||||
// Getxattr retrieves extended attribute data associated with path.
|
||||
func Getxattr(path, name string) ([]byte, error) {
|
||||
b, e := xattr.Get(path, name)
|
||||
if e == syscall.ENOTSUP {
|
||||
b, e := xattr.Getxattr(path, name)
|
||||
if err, ok := e.(*xattr.XAttrError); ok && err.Err == syscall.ENOTSUP {
|
||||
return nil, nil
|
||||
}
|
||||
return b, e
|
||||
return b, errors.Wrap(e, "Getxattr")
|
||||
}
|
||||
|
||||
// Listxattr retrieves a list of names of extended attributes associated with the
|
||||
// given path in the file system.
|
||||
func Listxattr(path string) ([]string, error) {
|
||||
s, e := xattr.List(path)
|
||||
if e == syscall.ENOTSUP {
|
||||
s, e := xattr.Listxattr(path)
|
||||
if err, ok := e.(*xattr.XAttrError); ok && err.Err == syscall.ENOTSUP {
|
||||
return nil, nil
|
||||
}
|
||||
return s, e
|
||||
return s, errors.Wrap(e, "Listxattr")
|
||||
}
|
||||
|
||||
// Setxattr associates name and data together as an attribute of path.
|
||||
func Setxattr(path, name string, data []byte) error {
|
||||
e := xattr.Set(path, name, data)
|
||||
if e == syscall.ENOTSUP {
|
||||
e := xattr.Setxattr(path, name, data)
|
||||
if err, ok := e.(*xattr.XAttrError); ok && err.Err == syscall.ENOTSUP {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
return errors.Wrap(e, "Setxattr")
|
||||
}
|
||||
|
|
12
vendor/manifest
vendored
12
vendor/manifest
vendored
|
@ -19,12 +19,6 @@
|
|||
"revision": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/ivaxer/go-xattr",
|
||||
"repository": "https://github.com/ivaxer/go-xattr",
|
||||
"revision": "1a541654d8e447148cf23d472c948f9f0078ac50",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/kr/fs",
|
||||
"repository": "https://github.com/kr/fs",
|
||||
|
@ -55,6 +49,12 @@
|
|||
"revision": "8197a2e580736b78d704be0fc47b2324c0591a32",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/pkg/xattr",
|
||||
"repository": "https://github.com/pkg/xattr",
|
||||
"revision": "1d40b70a947cd8e8457e4715e1123f8e99f5f241",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "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) 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
|
||||
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 a new issue