| //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 := os.OpenFile(path, os.O_RDONLY, 0) |
| 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 := os.OpenFile(path, os.O_RDONLY, 0) |
| 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 { |
| fd, err := unix.Open(path, unix.O_RDONLY|unix.O_XATTR, 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 := os.OpenFile(path, os.O_RDONLY, 0) |
| 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 |
| } |
| |
| // stringsFromByteSlice converts a sequence of attributes to a []string. |
| // On Darwin and Linux, 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 |
| } |