From 6224908d4ea027322e6d3002ce9f1c0d9940c7b7 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 24 Sep 2014 02:02:54 -0700 Subject: [PATCH 1/2] Update system/xattrs_linux.go - Add Llistxattr() support - Additionally cleanup Lgetxattr() and implement it properly in accordance with getxattr() syscall behavior. Signed-off-by: Harshavardhana --- system/xattrs_linux.go | 66 +++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/system/xattrs_linux.go b/system/xattrs_linux.go index 00edb201..30f74dfb 100644 --- a/system/xattrs_linux.go +++ b/system/xattrs_linux.go @@ -5,8 +5,35 @@ import ( "unsafe" ) -// Returns a nil slice and nil error if the xattr is not set +var _zero uintptr + +// Returns the size of xattrs and nil error +// Requires path, takes allocated []byte or nil as last argument +func Llistxattr(path string, dest []byte) (size int, err error) { + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return -1, err + } + var newpathBytes unsafe.Pointer + if len(dest) > 0 { + newpathBytes = unsafe.Pointer(&dest[0]) + } else { + newpathBytes = unsafe.Pointer(&_zero) + } + + _size, _, errno := syscall.Syscall6(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(newpathBytes), uintptr(len(dest)), 0, 0, 0) + size = int(_size) + if errno != 0 { + return -1, errno + } + + return size, nil +} + +// Returns a []byte slice if the xattr is set and nil otherwise +// Requires path and its attribute as arguments func Lgetxattr(path string, attr string) ([]byte, error) { + var sz int pathBytes, err := syscall.BytePtrFromString(path) if err != nil { return nil, err @@ -16,26 +43,39 @@ func Lgetxattr(path string, attr string) ([]byte, error) { return nil, err } - dest := make([]byte, 128) + // Start with a 128 length byte array + sz = 128 + dest := make([]byte, sz) destBytes := unsafe.Pointer(&dest[0]) - sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) - if errno == syscall.ENODATA { - return nil, nil - } - if errno == syscall.ERANGE { + _sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) + + switch { + case errno == syscall.ENODATA: + return nil, errno + case errno == syscall.ENOTSUP: + return nil, errno + case errno == syscall.ERANGE: + // 128 byte array might just not be good enough, + // A dummy buffer is used ``uintptr(0)`` to get real size + // of the xattrs on disk + _sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(unsafe.Pointer(nil)), uintptr(0), 0, 0) + sz = int(_sz) + if sz < 0 { + return nil, errno + } dest = make([]byte, sz) destBytes := unsafe.Pointer(&dest[0]) - sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) - } - if errno != 0 { + _sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) + if errno != 0 { + return nil, errno + } + case errno != 0: return nil, errno } - + sz = int(_sz) return dest[:sz], nil } -var _zero uintptr - func Lsetxattr(path string, attr string, data []byte, flags int) error { pathBytes, err := syscall.BytePtrFromString(path) if err != nil { From 5dca16dcb8d342efa967530fab169e6f2fe1b994 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 29 Sep 2014 14:13:36 -0700 Subject: [PATCH 2/2] xattr/xattr.go: Add new higher level helpers for xattr Signed-off-by: Harshavardhana --- xattr/xattr.go | 53 +++++++++++++++++++++++++++++++ xattr/xattr_test.go | 77 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 xattr/xattr.go create mode 100644 xattr/xattr_test.go diff --git a/xattr/xattr.go b/xattr/xattr.go new file mode 100644 index 00000000..fc08d01f --- /dev/null +++ b/xattr/xattr.go @@ -0,0 +1,53 @@ +// +build linux + +package xattr + +import ( + "syscall" + + "github.com/docker/libcontainer/system" +) + +func XattrEnabled(path string) bool { + if Setxattr(path, "user.test", "") == syscall.ENOTSUP { + return false + } + return true +} + +func stringsfromByte(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 +} + +func Listxattr(path string) ([]string, error) { + size, err := system.Llistxattr(path, nil) + if err != nil { + return nil, err + } + buf := make([]byte, size) + read, err := system.Llistxattr(path, buf) + if err != nil { + return nil, err + } + names := stringsfromByte(buf[:read]) + return names, nil +} + +func Getxattr(path, attr string) (string, error) { + value, err := system.Lgetxattr(path, attr) + if err != nil { + return "", err + } + return string(value), nil +} + +func Setxattr(path, xattr, value string) error { + return system.Lsetxattr(path, xattr, []byte(value), 0) +} diff --git a/xattr/xattr_test.go b/xattr/xattr_test.go new file mode 100644 index 00000000..d818c691 --- /dev/null +++ b/xattr/xattr_test.go @@ -0,0 +1,77 @@ +// +build linux + +package xattr_test + +import ( + "os" + "testing" + + "github.com/docker/libcontainer/xattr" +) + +func testXattr(t *testing.T) { + tmp := "xattr_test" + out, err := os.OpenFile(tmp, os.O_WRONLY, 0) + if err != nil { + t.Fatal("failed") + } + attr := "user.test" + out.Close() + + if !xattr.XattrEnabled(tmp) { + t.Log("Disabled") + t.Fatal("failed") + } + t.Log("Success") + + err = xattr.Setxattr(tmp, attr, "test") + if err != nil { + t.Fatal("failed") + } + + var value string + value, err = xattr.Getxattr(tmp, attr) + if err != nil { + t.Fatal("failed") + } + if value != "test" { + t.Fatal("failed") + } + t.Log("Success") + + var names []string + names, err = xattr.Listxattr(tmp) + if err != nil { + t.Fatal("failed") + } + + var found int + for _, name := range names { + if name == attr { + found = 1 + } + } + // Listxattr doesn't return trusted.* and system.* namespace + // attrs when run in unprevileged mode. + if found != 1 { + t.Fatal("failed") + } + t.Log("Success") + + big := "0000000000000000000000000000000000000000000000000000000000000000000008c6419ad822dfe29283fb3ac98dcc5908810cb31f4cfe690040c42c144b7492eicompslf20dxmlpgz" + // Test for long xattrs larger than 128 bytes + err = xattr.Setxattr(tmp, attr, big) + if err != nil { + t.Fatal("failed to add long value") + } + value, err = xattr.Getxattr(tmp, attr) + if err != nil { + t.Fatal("failed to get long value") + } + t.Log("Success") + + if value != big { + t.Fatal("failed, value doesn't match") + } + t.Log("Success") +}