| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| // Package fs implements an OS independent abstraction of a file system | ||
| // suitable for backup purposes. | ||
| package fs |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package restic | ||
|
|
||
| import ( | ||
| "sync" | ||
| ) | ||
|
|
||
| // HardlinkKey is a composed key for finding inodes on a specific device. | ||
| type HardlinkKey struct { | ||
| Inode, Device uint64 | ||
| } | ||
|
|
||
| // HardlinkIndex contains a list of inodes, devices these inodes are one, and associated file names. | ||
| type HardlinkIndex struct { | ||
| m sync.Mutex | ||
| Index map[HardlinkKey]string | ||
| } | ||
|
|
||
| // NewHardlinkIndex create a new index for hard links | ||
| func NewHardlinkIndex() *HardlinkIndex { | ||
| return &HardlinkIndex{ | ||
| Index: make(map[HardlinkKey]string), | ||
| } | ||
| } | ||
|
|
||
| // Has checks wether the link already exist in the index. | ||
| func (idx *HardlinkIndex) Has(inode uint64, device uint64) bool { | ||
| idx.m.Lock() | ||
| defer idx.m.Unlock() | ||
| _, ok := idx.Index[HardlinkKey{inode, device}] | ||
|
|
||
| return ok | ||
| } | ||
|
|
||
| // Add adds a link to the index. | ||
| func (idx *HardlinkIndex) Add(inode uint64, device uint64, name string) { | ||
| idx.m.Lock() | ||
| defer idx.m.Unlock() | ||
| _, ok := idx.Index[HardlinkKey{inode, device}] | ||
|
|
||
| if !ok { | ||
| idx.Index[HardlinkKey{inode, device}] = name | ||
| } | ||
| } | ||
|
|
||
| // GetFilename obtains the filename from the index. | ||
| func (idx *HardlinkIndex) GetFilename(inode uint64, device uint64) string { | ||
| idx.m.Lock() | ||
| defer idx.m.Unlock() | ||
| return idx.Index[HardlinkKey{inode, device}] | ||
| } | ||
|
|
||
| // Remove removes a link from the index. | ||
| func (idx *HardlinkIndex) Remove(inode uint64, device uint64) { | ||
| idx.m.Lock() | ||
| defer idx.m.Unlock() | ||
| delete(idx.Index, HardlinkKey{inode, device}) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package restic_test | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "restic" | ||
| . "restic/test" | ||
| ) | ||
|
|
||
| // TestHardLinks contains various tests for HardlinkIndex. | ||
| func TestHardLinks(t *testing.T) { | ||
|
|
||
| idx := restic.NewHardlinkIndex() | ||
|
|
||
| idx.Add(1, 2, "inode1-file1-on-device2") | ||
| idx.Add(2, 3, "inode2-file2-on-device3") | ||
|
|
||
| var sresult string | ||
| sresult = idx.GetFilename(1, 2) | ||
| Equals(t, sresult, "inode1-file1-on-device2") | ||
|
|
||
| sresult = idx.GetFilename(2, 3) | ||
| Equals(t, sresult, "inode2-file2-on-device3") | ||
|
|
||
| var bresult bool | ||
| bresult = idx.Has(1, 2) | ||
| Equals(t, bresult, true) | ||
|
|
||
| bresult = idx.Has(1, 3) | ||
| Equals(t, bresult, false) | ||
|
|
||
| idx.Remove(1, 2) | ||
| bresult = idx.Has(1, 2) | ||
| Equals(t, bresult, false) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // +build !openbsd | ||
| // +build !windows | ||
|
|
||
| package restic | ||
|
|
||
| import ( | ||
| "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.Getxattr(path, name) | ||
| if err, ok := e.(*xattr.XAttrError); ok && err.Err == syscall.ENOTSUP { | ||
| return nil, nil | ||
| } | ||
| 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.Listxattr(path) | ||
| if err, ok := e.(*xattr.XAttrError); ok && err.Err == syscall.ENOTSUP { | ||
| return nil, nil | ||
| } | ||
| 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.Setxattr(path, name, data) | ||
| if err, ok := e.(*xattr.XAttrError); ok && err.Err == syscall.ENOTSUP { | ||
| return nil | ||
| } | ||
| return errors.Wrap(e, "Setxattr") | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| [](http://godoc.org/github.com/pkg/xattr) | ||
| [](https://goreportcard.com/report/github.com/pkg/xattr) | ||
| [](https://travis-ci.org/pkg/xattr) | ||
|
|
||
| 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 := xattr.Setxattr(path, prefix+"test", []byte("test-attr-value")); err != nil { | ||
| log.Fatal(err) | ||
| } | ||
| var data []byte | ||
| data, err = xattr.Getxattr(path, prefix+"test"); err != nil { | ||
| log.Fatal(err) | ||
| } | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /* | ||
| Package xattr provides support for extended attributes on linux, darwin and 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. | ||
| More details you can find here: https://en.wikipedia.org/wiki/Extended_file_attributes | ||
| */ | ||
| 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() | ||
| } | ||
|
|
||
| // nullTermToStrings converts an array of NULL terminated UTF-8 strings to a []string. | ||
| func nullTermToStrings(buf []byte) (result []string) { | ||
| offset := 0 | ||
| for index, b := range buf { | ||
| if b == 0 { | ||
| result = append(result, string(buf[offset:index])) | ||
| offset = index + 1 | ||
| } | ||
| } | ||
| return | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // +build darwin | ||
|
|
||
| package xattr | ||
|
|
||
| // Getxattr retrieves 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} | ||
| } | ||
| if size > 0 { | ||
| buf := make([]byte, size) | ||
| // Read into buffer of that size. | ||
| read, err := getxattr(path, name, &buf[0], size, 0, 0) | ||
| if err != nil { | ||
| return nil, &XAttrError{"getxattr", path, name, err} | ||
| } | ||
| return buf[:read], nil | ||
| } | ||
| return []byte{}, 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) { | ||
| // find size. | ||
| size, err := listxattr(path, nil, 0, 0) | ||
| if err != nil { | ||
| return nil, &XAttrError{"listxattr", path, "", err} | ||
| } | ||
| if size > 0 { | ||
|
|
||
| buf := make([]byte, size) | ||
| // Read into buffer of that size. | ||
| read, err := listxattr(path, &buf[0], size, 0) | ||
| if err != nil { | ||
| return nil, &XAttrError{"listxattr", path, "", err} | ||
| } | ||
| return nullTermToStrings(buf[:read]), nil | ||
| } | ||
| return []string{}, nil | ||
| } | ||
|
|
||
| // Setxattr 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 | ||
| } | ||
|
|
||
| // Removexattr removes the attribute associated with the given path. | ||
| func Removexattr(path, name string) error { | ||
| if err := removexattr(path, name, 0); err != nil { | ||
| return &XAttrError{"removexattr", path, name, err} | ||
| } | ||
| return nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| // +build freebsd | ||
|
|
||
| package xattr | ||
|
|
||
| import ( | ||
| "syscall" | ||
| ) | ||
|
|
||
| const ( | ||
| EXTATTR_NAMESPACE_USER = 1 | ||
| ) | ||
|
|
||
| // Getxattr retrieves 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} | ||
| } | ||
| if size > 0 { | ||
| buf := make([]byte, size) | ||
| // Read into buffer of that size. | ||
| read, err := extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, &buf[0], size) | ||
| if err != nil { | ||
| return nil, &XAttrError{"extattr_get_file", path, name, err} | ||
| } | ||
| return buf[:read], nil | ||
| } | ||
| return []byte{}, 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) { | ||
| // find size. | ||
| size, err := extattr_list_file(path, EXTATTR_NAMESPACE_USER, nil, 0) | ||
| if err != nil { | ||
| return nil, &XAttrError{"extattr_list_file", path, "", err} | ||
| } | ||
| if size > 0 { | ||
| buf := make([]byte, size) | ||
| // Read into buffer of that size. | ||
| read, err := extattr_list_file(path, EXTATTR_NAMESPACE_USER, &buf[0], size) | ||
| if err != nil { | ||
| return nil, &XAttrError{"extattr_list_file", path, "", err} | ||
| } | ||
| return attrListToStrings(buf[:read]), nil | ||
| } | ||
| return []string{}, nil | ||
| } | ||
|
|
||
| // Setxattr 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 | ||
| } | ||
|
|
||
| // Removexattr removes the attribute associated with the given path. | ||
| 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 | ||
| } | ||
|
|
||
| // attrListToStrings converts 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 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // +build linux | ||
|
|
||
| package xattr | ||
|
|
||
| import "syscall" | ||
|
|
||
| // Getxattr retrieves 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} | ||
| } | ||
| if size > 0 { | ||
| data := make([]byte, size) | ||
| // Read into buffer of that size. | ||
| read, err := syscall.Getxattr(path, name, data) | ||
| if err != nil { | ||
| return nil, &XAttrError{"getxattr", path, name, err} | ||
| } | ||
| return data[:read], nil | ||
| } | ||
| return []byte{}, 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) { | ||
| // find size. | ||
| size, err := syscall.Listxattr(path, nil) | ||
| if err != nil { | ||
| return nil, &XAttrError{"listxattr", path, "", err} | ||
| } | ||
| if size > 0 { | ||
| 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 | ||
| } | ||
| return []string{}, nil | ||
| } | ||
|
|
||
| // Setxattr 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 | ||
| } | ||
|
|
||
| // Removexattr removes the attribute associated | ||
| // with the given path. | ||
| func Removexattr(path, name string) error { | ||
| if err := syscall.Removexattr(path, name); err != nil { | ||
| return &XAttrError{"removexattr", path, name, err} | ||
| } | ||
| return nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
| } | ||
| } |