mirror of
https://github.com/samuelncui/acp.git
synced 2026-03-27 21:45:01 +00:00
140 lines
2.7 KiB
Go
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
|
|
}
|