//go:build solaris // +build solaris package xattr import ( "os" "syscall" "golang.org/x/sys/unix" ) const ( // XATTR_SUPPORTED will be true if the current platform is supported XATTR_SUPPORTED = true XATTR_CREATE = 0x1 XATTR_REPLACE = 0x2 // ENOATTR is not exported by the syscall package on Linux, because it is // an alias for ENODATA. We export it here so it is available on all // our supported platforms. ENOATTR = syscall.ENODATA ) func getxattr(path string, name string, data []byte) (int, error) { f, err := openNonblock(path) if err != nil { return 0, err } defer func() { _ = f.Close() }() return fgetxattr(f, name, data) } func lgetxattr(path string, name string, data []byte) (int, error) { return 0, unix.ENOTSUP } func fgetxattr(f *os.File, name string, data []byte) (int, error) { fd, err := unix.Openat(int(f.Fd()), name, unix.O_RDONLY|unix.O_XATTR, 0) if err != nil { return 0, err } defer func() { _ = unix.Close(fd) }() return unix.Read(fd, data) } func setxattr(path string, name string, data []byte, flags int) error { f, err := openNonblock(path) if err != nil { return err } err = fsetxattr(f, name, data, flags) if err != nil { _ = f.Close() return err } return f.Close() } func lsetxattr(path string, name string, data []byte, flags int) error { return unix.ENOTSUP } func fsetxattr(f *os.File, name string, data []byte, flags int) error { mode := unix.O_WRONLY | unix.O_XATTR if flags&XATTR_REPLACE != 0 { mode |= unix.O_TRUNC } else if flags&XATTR_CREATE != 0 { mode |= unix.O_CREAT | unix.O_EXCL } else { mode |= unix.O_CREAT | unix.O_TRUNC } fd, err := unix.Openat(int(f.Fd()), name, mode, 0666) if err != nil { return err } if _, err = unix.Write(fd, data); err != nil { _ = unix.Close(fd) return err } return unix.Close(fd) } func removexattr(path string, name string) error { mode := unix.O_RDONLY | unix.O_XATTR | unix.O_NONBLOCK | unix.O_CLOEXEC fd, err := unix.Open(path, mode, 0) if err != nil { return err } f := os.NewFile(uintptr(fd), path) defer func() { _ = f.Close() }() return fremovexattr(f, name) } func lremovexattr(path string, name string) error { return unix.ENOTSUP } func fremovexattr(f *os.File, name string) error { fd, err := unix.Openat(int(f.Fd()), ".", unix.O_XATTR, 0) if err != nil { return err } defer func() { _ = unix.Close(fd) }() return unix.Unlinkat(fd, name, 0) } func listxattr(path string, data []byte) (int, error) { f, err := openNonblock(path) if err != nil { return 0, err } defer func() { _ = f.Close() }() return flistxattr(f, data) } func llistxattr(path string, data []byte) (int, error) { return 0, unix.ENOTSUP } func flistxattr(f *os.File, data []byte) (int, error) { fd, err := unix.Openat(int(f.Fd()), ".", unix.O_RDONLY|unix.O_XATTR, 0) if err != nil { return 0, unix.ENOTSUP } xf := os.NewFile(uintptr(fd), f.Name()) defer func() { _ = xf.Close() }() names, err := xf.Readdirnames(-1) if err != nil { return 0, err } var buf []byte for _, name := range names { buf = append(buf, append([]byte(name), '\000')...) } if data == nil { return len(buf), nil } return copy(data, buf), nil } // Like os.Open, but passes O_NONBLOCK to the open(2) syscall. func openNonblock(path string) (*os.File, error) { fd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC|unix.O_NONBLOCK, 0) if err != nil { return nil, err } return os.NewFile(uintptr(fd), path), err } // stringsFromByteSlice converts a sequence of attributes to a []string. // We simulate Linux/Darwin, where each entry is a NULL-terminated string. func stringsFromByteSlice(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 }