forked from TrueCloudLab/restic
Add support for Windows EA in node
Refactor Extended Attribute related functions in node files as windows apis get and set EA in bulk
This commit is contained in:
parent
d6708505b9
commit
5cff6e084e
8 changed files with 246 additions and 96 deletions
|
@ -284,16 +284,6 @@ func (node Node) restoreMetadata(path string, warn func(msg string)) error {
|
||||||
return firsterr
|
return firsterr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node Node) restoreExtendedAttributes(path string) error {
|
|
||||||
for _, attr := range node.ExtendedAttributes {
|
|
||||||
err := Setxattr(path, attr.Name, attr.Value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node Node) RestoreTimestamps(path string) error {
|
func (node Node) RestoreTimestamps(path string) error {
|
||||||
var utimes = [...]syscall.Timespec{
|
var utimes = [...]syscall.Timespec{
|
||||||
syscall.NsecToTimespec(node.AccessTime.UnixNano()),
|
syscall.NsecToTimespec(node.AccessTime.UnixNano()),
|
||||||
|
@ -726,34 +716,6 @@ func (node *Node) fillExtra(path string, fi os.FileInfo, ignoreXattrListError bo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Node) fillExtendedAttributes(path string, ignoreListError bool) error {
|
|
||||||
xattrs, err := Listxattr(path)
|
|
||||||
debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err)
|
|
||||||
if err != nil {
|
|
||||||
if ignoreListError && IsListxattrPermissionError(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
node.ExtendedAttributes = make([]ExtendedAttribute, 0, len(xattrs))
|
|
||||||
for _, attr := range xattrs {
|
|
||||||
attrVal, err := Getxattr(path, attr)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "can not obtain extended attribute %v for %v:\n", attr, path)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
attr := ExtendedAttribute{
|
|
||||||
Name: attr,
|
|
||||||
Value: attrVal,
|
|
||||||
}
|
|
||||||
|
|
||||||
node.ExtendedAttributes = append(node.ExtendedAttributes, attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mkfifo(path string, mode uint32) (err error) {
|
func mkfifo(path string, mode uint32) (err error) {
|
||||||
return mknod(path, mode|syscall.S_IFIFO, 0)
|
return mknod(path, mode|syscall.S_IFIFO, 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,25 +23,21 @@ func (s statT) atim() syscall.Timespec { return toTimespec(s.Atim) }
|
||||||
func (s statT) mtim() syscall.Timespec { return toTimespec(s.Mtim) }
|
func (s statT) mtim() syscall.Timespec { return toTimespec(s.Mtim) }
|
||||||
func (s statT) ctim() syscall.Timespec { return toTimespec(s.Ctim) }
|
func (s statT) ctim() syscall.Timespec { return toTimespec(s.Ctim) }
|
||||||
|
|
||||||
// Getxattr is a no-op on AIX.
|
// restoreExtendedAttributes is a no-op on AIX.
|
||||||
func Getxattr(path, name string) ([]byte, error) {
|
func (node Node) restoreExtendedAttributes(_ string) error {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listxattr is a no-op on AIX.
|
// fillExtendedAttributes is a no-op on AIX.
|
||||||
func Listxattr(path string) ([]string, error) {
|
func (node *Node) fillExtendedAttributes(_ string, _ bool) error {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsListxattrPermissionError is a no-op on AIX.
|
||||||
func IsListxattrPermissionError(_ error) bool {
|
func IsListxattrPermissionError(_ error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setxattr is a no-op on AIX.
|
|
||||||
func Setxattr(path, name string, data []byte) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// restoreGenericAttributes is no-op on AIX.
|
// restoreGenericAttributes is no-op on AIX.
|
||||||
func (node *Node) restoreGenericAttributes(_ string, warn func(msg string)) error {
|
func (node *Node) restoreGenericAttributes(_ string, warn func(msg string)) error {
|
||||||
return node.handleAllUnknownGenericAttributesFound(warn)
|
return node.handleAllUnknownGenericAttributesFound(warn)
|
||||||
|
|
|
@ -13,25 +13,21 @@ func (s statT) atim() syscall.Timespec { return s.Atimespec }
|
||||||
func (s statT) mtim() syscall.Timespec { return s.Mtimespec }
|
func (s statT) mtim() syscall.Timespec { return s.Mtimespec }
|
||||||
func (s statT) ctim() syscall.Timespec { return s.Ctimespec }
|
func (s statT) ctim() syscall.Timespec { return s.Ctimespec }
|
||||||
|
|
||||||
// Getxattr is a no-op on netbsd.
|
// restoreExtendedAttributes is a no-op on netbsd.
|
||||||
func Getxattr(path, name string) ([]byte, error) {
|
func (node Node) restoreExtendedAttributes(_ string) error {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listxattr is a no-op on netbsd.
|
// fillExtendedAttributes is a no-op on netbsd.
|
||||||
func Listxattr(path string) ([]string, error) {
|
func (node *Node) fillExtendedAttributes(_ string, _ bool) error {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsListxattrPermissionError is a no-op on netbsd.
|
||||||
func IsListxattrPermissionError(_ error) bool {
|
func IsListxattrPermissionError(_ error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setxattr is a no-op on netbsd.
|
|
||||||
func Setxattr(path, name string, data []byte) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// restoreGenericAttributes is no-op on netbsd.
|
// restoreGenericAttributes is no-op on netbsd.
|
||||||
func (node *Node) restoreGenericAttributes(_ string, warn func(msg string)) error {
|
func (node *Node) restoreGenericAttributes(_ string, warn func(msg string)) error {
|
||||||
return node.handleAllUnknownGenericAttributesFound(warn)
|
return node.handleAllUnknownGenericAttributesFound(warn)
|
||||||
|
|
|
@ -13,25 +13,21 @@ func (s statT) atim() syscall.Timespec { return s.Atim }
|
||||||
func (s statT) mtim() syscall.Timespec { return s.Mtim }
|
func (s statT) mtim() syscall.Timespec { return s.Mtim }
|
||||||
func (s statT) ctim() syscall.Timespec { return s.Ctim }
|
func (s statT) ctim() syscall.Timespec { return s.Ctim }
|
||||||
|
|
||||||
// Getxattr is a no-op on openbsd.
|
// restoreExtendedAttributes is a no-op on openbsd.
|
||||||
func Getxattr(path, name string) ([]byte, error) {
|
func (node Node) restoreExtendedAttributes(_ string) error {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listxattr is a no-op on openbsd.
|
// fillExtendedAttributes is a no-op on openbsd.
|
||||||
func Listxattr(path string) ([]string, error) {
|
func (node *Node) fillExtendedAttributes(_ string, _ bool) error {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsListxattrPermissionError is a no-op on openbsd.
|
||||||
func IsListxattrPermissionError(_ error) bool {
|
func IsListxattrPermissionError(_ error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setxattr is a no-op on openbsd.
|
|
||||||
func Setxattr(path, name string, data []byte) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// restoreGenericAttributes is no-op on openbsd.
|
// restoreGenericAttributes is no-op on openbsd.
|
||||||
func (node *Node) restoreGenericAttributes(_ string, warn func(msg string)) error {
|
func (node *Node) restoreGenericAttributes(_ string, warn func(msg string)) error {
|
||||||
return node.handleAllUnknownGenericAttributesFound(warn)
|
return node.handleAllUnknownGenericAttributesFound(warn)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -205,8 +206,18 @@ func TestNodeRestoreAt(t *testing.T) {
|
||||||
var nodePath string
|
var nodePath string
|
||||||
if test.ExtendedAttributes != nil {
|
if test.ExtendedAttributes != nil {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
// restic does not support xattrs on windows
|
// In windows extended attributes are case insensitive and windows returns
|
||||||
return
|
// the extended attributes in UPPER case.
|
||||||
|
// Update the tests to use UPPER case xattr names for windows.
|
||||||
|
extAttrArr := test.ExtendedAttributes
|
||||||
|
// Iterate through the array using pointers
|
||||||
|
for i := 0; i < len(extAttrArr); i++ {
|
||||||
|
// Get the pointer to the current element
|
||||||
|
namePtr := &extAttrArr[i].Name
|
||||||
|
|
||||||
|
// Modify the value through the pointer
|
||||||
|
*namePtr = strings.ToUpper(*namePtr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tempdir might be backed by a filesystem that does not support
|
// tempdir might be backed by a filesystem that does not support
|
||||||
|
|
|
@ -70,26 +70,109 @@ func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespe
|
||||||
return syscall.SetFileTime(h, nil, &a, &w)
|
return syscall.SetFileTime(h, nil, &a, &w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getxattr retrieves extended attribute data associated with path.
|
// restore extended attributes for windows
|
||||||
func Getxattr(path, name string) ([]byte, error) {
|
func (node Node) restoreExtendedAttributes(path string) (err error) {
|
||||||
return nil, nil
|
eas := []fs.ExtendedAttribute{}
|
||||||
}
|
for _, attr := range node.ExtendedAttributes {
|
||||||
|
extr := new(fs.ExtendedAttribute)
|
||||||
// Listxattr retrieves a list of names of extended attributes associated with the
|
extr.Name = attr.Name
|
||||||
// given path in the file system.
|
extr.Value = attr.Value
|
||||||
func Listxattr(path string) ([]string, error) {
|
eas = append(eas, *extr)
|
||||||
return nil, nil
|
}
|
||||||
}
|
if len(eas) > 0 {
|
||||||
|
if errExt := restoreExtendedAttributes(node.Type, path, eas); errExt != nil {
|
||||||
func IsListxattrPermissionError(_ error) bool {
|
return errExt
|
||||||
return false
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setxattr associates name and data together as an attribute of path.
|
|
||||||
func Setxattr(path, name string, data []byte) error {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fill extended attributes in the node. This also includes the Generic attributes for windows.
|
||||||
|
func (node *Node) fillExtendedAttributes(path string, _ bool) (err error) {
|
||||||
|
var fileHandle windows.Handle
|
||||||
|
|
||||||
|
//Get file handle for file or dir
|
||||||
|
if node.Type == "file" {
|
||||||
|
if strings.HasSuffix(filepath.Clean(path), `\`) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
utf16Path := windows.StringToUTF16Ptr(path)
|
||||||
|
fileAccessRightReadWriteEA := (0x8 | 0x10)
|
||||||
|
fileHandle, err = windows.CreateFile(utf16Path, uint32(fileAccessRightReadWriteEA), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
|
||||||
|
} else if node.Type == "dir" {
|
||||||
|
utf16Path := windows.StringToUTF16Ptr(path)
|
||||||
|
fileAccessRightReadWriteEA := (0x8 | 0x10)
|
||||||
|
fileHandle, err = windows.CreateFile(utf16Path, uint32(fileAccessRightReadWriteEA), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Errorf("open file failed for path: %s, with: %v", path, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := windows.CloseHandle(fileHandle)
|
||||||
|
if err != nil {
|
||||||
|
debug.Log("Error closing file handle for %s: %v\n", path, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
//Get the windows Extended Attributes using the file handle
|
||||||
|
extAtts, err := fs.GetFileEA(fileHandle)
|
||||||
|
debug.Log("fillExtendedAttributes(%v) %v", path, extAtts)
|
||||||
|
if err != nil {
|
||||||
|
debug.Log("open file failed for path: %s : %v", path, err)
|
||||||
|
return err
|
||||||
|
} else if len(extAtts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fill the ExtendedAttributes in the node using the name/value pairs in the windows EA
|
||||||
|
for _, attr := range extAtts {
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Errorf("can not obtain extended attribute for path %v, attr: %v, err: %v\n,", path, attr, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
extendedAttr := ExtendedAttribute{
|
||||||
|
Name: attr.Name,
|
||||||
|
Value: attr.Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
node.ExtendedAttributes = append(node.ExtendedAttributes, extendedAttr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// restoreExtendedAttributes handles restore of the Windows Extended Attributes to the specified path.
|
||||||
|
// The Windows API requires setting of all the Extended Attributes in one call.
|
||||||
|
func restoreExtendedAttributes(nodeType, path string, eas []fs.ExtendedAttribute) (err error) {
|
||||||
|
var fileHandle windows.Handle
|
||||||
|
switch nodeType {
|
||||||
|
case "file":
|
||||||
|
utf16Path := windows.StringToUTF16Ptr(path)
|
||||||
|
fileAccessRightReadWriteEA := (0x8 | 0x10)
|
||||||
|
fileHandle, err = windows.CreateFile(utf16Path, uint32(fileAccessRightReadWriteEA), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
|
||||||
|
case "dir":
|
||||||
|
utf16Path := windows.StringToUTF16Ptr(path)
|
||||||
|
fileAccessRightReadWriteEA := (0x8 | 0x10)
|
||||||
|
fileHandle, err = windows.CreateFile(utf16Path, uint32(fileAccessRightReadWriteEA), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := windows.CloseHandle(fileHandle)
|
||||||
|
if err != nil {
|
||||||
|
debug.Log("Error closing file handle for %s: %v\n", path, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Errorf("open file failed for path %v, with: %v:\n", path, err)
|
||||||
|
} else if err = fs.SetFileEA(fileHandle, eas); err != nil {
|
||||||
|
err = errors.Errorf("set EA failed for path %v, with: %v:\n", path, err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
type statT syscall.Win32FileAttributeData
|
type statT syscall.Win32FileAttributeData
|
||||||
|
|
||||||
func toStatT(i interface{}) (*statT, bool) {
|
func toStatT(i interface{}) (*statT, bool) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -265,3 +266,68 @@ func TestNewGenericAttributeType(t *testing.T) {
|
||||||
test.Assert(t, len(ua) == 0, "Unkown attributes: %s found for path: %s", ua, testPath)
|
test.Assert(t, len(ua) == 0, "Unkown attributes: %s found for path: %s", ua, testPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRestoreExtendedAttributes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
expectedNodes := []Node{
|
||||||
|
{
|
||||||
|
Name: "testfile",
|
||||||
|
Type: "file",
|
||||||
|
Mode: 0644,
|
||||||
|
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||||
|
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||||
|
ChangeTime: parseTime("2005-05-14 21:07:05.333"),
|
||||||
|
ExtendedAttributes: []ExtendedAttribute{
|
||||||
|
{"user.foo", []byte("bar")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "testdirectory",
|
||||||
|
Type: "dir",
|
||||||
|
Mode: 0755,
|
||||||
|
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||||
|
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||||
|
ChangeTime: parseTime("2005-05-14 21:07:05.333"),
|
||||||
|
ExtendedAttributes: []ExtendedAttribute{
|
||||||
|
{"user.foo", []byte("bar")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testNode := range expectedNodes {
|
||||||
|
testPath, node := restoreAndGetNode(t, tempDir, testNode, false)
|
||||||
|
|
||||||
|
var handle windows.Handle
|
||||||
|
var err error
|
||||||
|
utf16Path := windows.StringToUTF16Ptr(testPath)
|
||||||
|
if node.Type == "file" {
|
||||||
|
handle, err = windows.CreateFile(utf16Path, windows.FILE_READ_EA, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
|
||||||
|
} else if node.Type == "dir" {
|
||||||
|
handle, err = windows.CreateFile(utf16Path, windows.FILE_READ_EA, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||||
|
}
|
||||||
|
test.OK(t, errors.Wrapf(err, "Error opening file/directory for: %s", testPath))
|
||||||
|
defer func() {
|
||||||
|
err := windows.Close(handle)
|
||||||
|
test.OK(t, errors.Wrapf(err, "Error closing file for: %s", testPath))
|
||||||
|
}()
|
||||||
|
|
||||||
|
if len(node.ExtendedAttributes) > 0 {
|
||||||
|
extAttr, err := fs.GetFileEA(handle)
|
||||||
|
test.OK(t, errors.Wrapf(err, "Error getting extended attributes for: %s", testPath))
|
||||||
|
test.Equals(t, len(node.ExtendedAttributes), len(extAttr))
|
||||||
|
|
||||||
|
for _, expectedExtAttr := range node.ExtendedAttributes {
|
||||||
|
var foundExtAttr *fs.ExtendedAttribute
|
||||||
|
for _, ea := range extAttr {
|
||||||
|
if strings.EqualFold(ea.Name, expectedExtAttr.Name) {
|
||||||
|
foundExtAttr = &ea
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test.Assert(t, foundExtAttr != nil, "Expected extended attribute not found")
|
||||||
|
test.Equals(t, expectedExtAttr.Value, foundExtAttr.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,23 +4,25 @@
|
||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/restic/restic/internal/debug"
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
|
|
||||||
"github.com/pkg/xattr"
|
"github.com/pkg/xattr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Getxattr retrieves extended attribute data associated with path.
|
// getxattr retrieves extended attribute data associated with path.
|
||||||
func Getxattr(path, name string) ([]byte, error) {
|
func getxattr(path, name string) ([]byte, error) {
|
||||||
b, err := xattr.LGet(path, name)
|
b, err := xattr.LGet(path, name)
|
||||||
return b, handleXattrErr(err)
|
return b, handleXattrErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listxattr retrieves a list of names of extended attributes associated with the
|
// listxattr retrieves a list of names of extended attributes associated with the
|
||||||
// given path in the file system.
|
// given path in the file system.
|
||||||
func Listxattr(path string) ([]string, error) {
|
func listxattr(path string) ([]string, error) {
|
||||||
l, err := xattr.LList(path)
|
l, err := xattr.LList(path)
|
||||||
return l, handleXattrErr(err)
|
return l, handleXattrErr(err)
|
||||||
}
|
}
|
||||||
|
@ -33,8 +35,8 @@ func IsListxattrPermissionError(err error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setxattr associates name and data together as an attribute of path.
|
// setxattr associates name and data together as an attribute of path.
|
||||||
func Setxattr(path, name string, data []byte) error {
|
func setxattr(path, name string, data []byte) error {
|
||||||
return handleXattrErr(xattr.LSet(path, name, data))
|
return handleXattrErr(xattr.LSet(path, name, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,3 +68,41 @@ func (node *Node) restoreGenericAttributes(_ string, warn func(msg string)) erro
|
||||||
func (node *Node) fillGenericAttributes(_ string, _ os.FileInfo, _ *statT) (allowExtended bool, err error) {
|
func (node *Node) fillGenericAttributes(_ string, _ os.FileInfo, _ *statT) (allowExtended bool, err error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (node Node) restoreExtendedAttributes(path string) error {
|
||||||
|
for _, attr := range node.ExtendedAttributes {
|
||||||
|
err := setxattr(path, attr.Name, attr.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *Node) fillExtendedAttributes(path string, ignoreListError bool) error {
|
||||||
|
xattrs, err := listxattr(path)
|
||||||
|
debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err)
|
||||||
|
if err != nil {
|
||||||
|
if ignoreListError && IsListxattrPermissionError(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
node.ExtendedAttributes = make([]ExtendedAttribute, 0, len(xattrs))
|
||||||
|
for _, attr := range xattrs {
|
||||||
|
attrVal, err := getxattr(path, attr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "can not obtain extended attribute %v for %v:\n", attr, path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
attr := ExtendedAttribute{
|
||||||
|
Name: attr,
|
||||||
|
Value: attrVal,
|
||||||
|
}
|
||||||
|
|
||||||
|
node.ExtendedAttributes = append(node.ExtendedAttributes, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue