diff options
| author | Jakob Unterwurzacher | 2019-01-02 16:56:23 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2019-01-02 16:56:23 +0100 | 
| commit | 7995a8358e6a99a6b2387eb6f0e10b789706aa08 (patch) | |
| tree | 320cb10c179264106bc0bc007934ec4f8904874b | |
| parent | 55a27a47dfb61cf553cf499802d614926b3b8a72 (diff) | |
syscallcompat: add Fgetxattr / Fsetxattr wrappers
These take care of buffer sizing and parsing.
| -rw-r--r-- | internal/syscallcompat/sys_common.go | 67 | 
1 files changed, 67 insertions, 0 deletions
| diff --git a/internal/syscallcompat/sys_common.go b/internal/syscallcompat/sys_common.go index 33ed807..3a0d5a1 100644 --- a/internal/syscallcompat/sys_common.go +++ b/internal/syscallcompat/sys_common.go @@ -1,6 +1,7 @@  package syscallcompat  import ( +	"bytes"  	"syscall"  	"golang.org/x/sys/unix" @@ -45,3 +46,69 @@ func Faccessat(dirfd int, path string, mode uint32) error {  func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) {  	return unix.Linkat(olddirfd, oldpath, newdirfd, newpath, flags)  } + +const XATTR_SIZE_MAX = 65536 + +// Make the buffer 1kB bigger so we can detect overflows +const XATTR_BUFSZ = XATTR_SIZE_MAX + 1024 + +// Fgetxattr is a wrapper around unix.Fgetxattr that handles the buffer sizing. +func Fgetxattr(fd int, attr string) (val []byte, err error) { +	// If the buffer is too small to fit the value, Linux and MacOS react +	// differently: +	// Linux: returns an ERANGE error and "-1" bytes. +	// MacOS: truncates the value and returns "size" bytes. +	// +	// We choose the simple approach of buffer that is bigger than the limit on +	// Linux, and return an error for everything that is bigger (which can +	// only happen on MacOS). +	// +	// See https://github.com/pkg/xattr for a smarter solution. +	// TODO: be smarter? +	buf := make([]byte, XATTR_BUFSZ) +	sz, err := unix.Fgetxattr(fd, attr, buf) +	if err == syscall.ERANGE { +		// Do NOT return ERANGE - the user might retry ad inifinitum! +		return nil, syscall.EOVERFLOW +	} +	if err != nil { +		return nil, err +	} +	if sz >= XATTR_SIZE_MAX { +		return nil, syscall.EOVERFLOW +	} +	// Copy only the actually used bytes to a new (smaller) buffer +	// so "buf" never leaves the function and can be allocated on the stack. +	val = make([]byte, sz) +	copy(val, buf) +	return val, nil +} + +// Flistxattr is a wrapper unix.Flistxattr that handles buffer sizing and +// parsing the returned blob to a string slice. +func Flistxattr(fd int) (attrs []string, err error) { +	// See the buffer sizing comments in Fgetxattr. +	// TODO: be smarter? +	buf := make([]byte, XATTR_BUFSZ) +	sz, err := unix.Flistxattr(fd, buf) +	if err == syscall.ERANGE { +		// Do NOT return ERANGE - the user might retry ad inifinitum! +		return nil, syscall.EOVERFLOW +	} +	if err != nil { +		return nil, err +	} +	if sz >= XATTR_SIZE_MAX { +		return nil, syscall.EOVERFLOW +	} +	buf = buf[:sz] +	parts := bytes.Split(buf, []byte{0}) +	for _, part := range parts { +		if len(part) == 0 { +			// Last part is empty, ignore +			continue +		} +		attrs = append(attrs, string(part)) +	} +	return attrs, nil +} | 
