mirror of
https://github.com/restic/restic.git
synced 2025-08-23 08:47:37 +00:00
Add support for extended attributes (e.g. ACL)
This commit is contained in:

committed by
Alexander Neumann

parent
40685a0e61
commit
49cae0904f
6
vendor/manifest
vendored
6
vendor/manifest
vendored
@@ -19,6 +19,12 @@
|
||||
"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",
|
||||
|
25
vendor/src/github.com/ivaxer/go-xattr/LICENCE
vendored
Normal file
25
vendor/src/github.com/ivaxer/go-xattr/LICENCE
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2012 Dave Cheney. All rights reserved.
|
||||
Copyright (c) 2013 Alexey Palazhchenko. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
10
vendor/src/github.com/ivaxer/go-xattr/README.md
vendored
Normal file
10
vendor/src/github.com/ivaxer/go-xattr/README.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
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).
|
156
vendor/src/github.com/ivaxer/go-xattr/syscall_darwin.go
vendored
Normal file
156
vendor/src/github.com/ivaxer/go-xattr/syscall_darwin.go
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
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
|
||||
}
|
21
vendor/src/github.com/ivaxer/go-xattr/syscall_linux.go
vendored
Normal file
21
vendor/src/github.com/ivaxer/go-xattr/syscall_linux.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
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
Normal file
171
vendor/src/github.com/ivaxer/go-xattr/xattr.go
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
// 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
|
||||
}
|
9
vendor/src/github.com/ivaxer/go-xattr/xattr_darwin.go
vendored
Normal file
9
vendor/src/github.com/ivaxer/go-xattr/xattr_darwin.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func isNotExist(err error) bool {
|
||||
return err == syscall.ENOATTR
|
||||
}
|
9
vendor/src/github.com/ivaxer/go-xattr/xattr_linux.go
vendored
Normal file
9
vendor/src/github.com/ivaxer/go-xattr/xattr_linux.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
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
Normal file
153
vendor/src/github.com/ivaxer/go-xattr/xattr_test.go
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
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)
|
||||
}
|
Reference in New Issue
Block a user