Use github.com/pkg/xattr for extended attributes

This commit is contained in:
Alexander Neumann
2017-02-16 14:25:47 +01:00
parent 0674f32d79
commit eb9be4e884
17 changed files with 430 additions and 536 deletions

25
vendor/src/github.com/pkg/xattr/LICENSE vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2012 Dave Cheney. 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
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.

View 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)
}
```

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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)
}
}