Files
acp/syscall_unix.go
2026-03-11 00:55:46 +08:00

140 lines
2.7 KiB
Go

//go:build darwin || linux
// +build darwin linux
package acp
import (
"errors"
"fmt"
"io/fs"
"os"
"syscall"
"golang.org/x/sys/unix"
)
type xattr struct {
key string
value []byte
}
type sysStat struct {
*syscall.Stat_t
xattrs []xattr
}
func readSysStat(path string, stat fs.FileInfo) (*sysStat, error) {
sysstat, ok := stat.Sys().(*syscall.Stat_t)
if !ok {
return nil, fmt.Errorf("stat sys failed, %T", stat.Sys())
}
xattrs, err := readXattrs(path)
if err != nil {
return nil, fmt.Errorf("read xattrs failed, %w", err)
}
return &sysStat{Stat_t: sysstat, xattrs: xattrs}, nil
}
func writeSysStat(name string, j *stat) error {
if err := writeXattrs(name, j.sys.xattrs); err != nil {
return fmt.Errorf("write xattr fail, %w", err)
}
if err := os.Chmod(name, j.mode); err != nil {
return fmt.Errorf("chmod fail, %w", err)
}
if os.Geteuid() == 0 {
if err := os.Chown(name, int(j.sys.Uid), int(j.sys.Gid)); err != nil {
return fmt.Errorf("chown fail, %w", err)
}
}
if err := os.Chtimes(name, j.modTime, j.modTime); err != nil {
return fmt.Errorf("chtimes fail, %w", err)
}
return nil
}
func readXattrs(path string) ([]xattr, error) {
size, err := unix.Listxattr(path, nil)
if err != nil {
if errors.Is(err, unix.ENOTSUP) || isNoAttrErr(err) {
return nil, nil
}
return nil, fmt.Errorf("count xattrs failed, %w", err)
}
if size == 0 {
return nil, nil
}
keyBuf := make([]byte, size)
n, err := unix.Listxattr(path, keyBuf)
if err != nil {
if errors.Is(err, unix.ENOTSUP) || isNoAttrErr(err) {
return nil, nil
}
return nil, fmt.Errorf("list xattrs failed, %w", err)
}
keyBuf = keyBuf[:n]
start := 0
xattrs := make([]xattr, 0)
for i, b := range keyBuf {
if b != 0 {
continue
}
if i <= start {
start = i + 1
continue
}
name := string(keyBuf[start:i])
start = i + 1
if name == "" {
continue
}
if !checkXattrKey(name) {
continue
}
valSize, err := unix.Getxattr(path, name, nil)
if err != nil {
if isNoAttrErr(err) {
continue
}
return nil, err
}
if valSize == 0 {
xattrs = append(xattrs, xattr{key: name, value: []byte{}})
continue
}
val := make([]byte, valSize)
n, err := unix.Getxattr(path, name, val)
if err != nil {
if isNoAttrErr(err) {
continue
}
return nil, err
}
xattrs = append(xattrs, xattr{key: name, value: val[:n]})
}
return xattrs, nil
}
func writeXattrs(path string, xattrs []xattr) error {
for _, xattr := range xattrs {
if err := unix.Setxattr(path, xattr.key, xattr.value, 0); err != nil {
if errors.Is(err, unix.ENOTSUP) ||
errors.Is(err, unix.EPERM) ||
errors.Is(err, unix.EROFS) {
continue
}
return err
}
}
return nil
}