Golang 实现 Linux 文件复制,含 ACL 权限和扩展属性
以下是使用 Golang 实现 Linux 下文件复制,包括 ACL 权限及文件扩展属性的示例代码:
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"syscall"
)
func copyFile(src, dst string) error {
srcStat, err := os.Stat(src)
if err != nil {
return err
}
dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcStat.Mode())
if err != nil {
return err
}
defer dstFile.Close()
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
err = os.Chmod(dst, srcStat.Mode())
if err != nil {
return err
}
err = copyAcl(src, dst)
if err != nil {
return err
}
err = copyXattr(src, dst)
if err != nil {
return err
}
return nil
}
func copyAcl(src, dst string) error {
acl, err := getAcl(src)
if err != nil {
return err
}
if acl != nil {
err = setAcl(dst, acl)
if err != nil {
return err
}
}
return nil
}
func getAcl(path string) (*syscall.ACL, error) {
fileInfo, err := os.Stat(path)
if err != nil {
return nil, err
}
acl, err := syscall.Getfacl(path)
if err != nil {
return nil, err
}
userMap, err := getUserMap()
if err != nil {
return nil, err
}
for i := range acl {
if acl[i].Tag == 'user' {
uid, ok := userMap[acl[i].Qualifier]
if ok {
acl[i].Qualifier = fmt.Sprintf('%d', uid)
}
}
}
return acl, nil
}
func setAcl(path string, acl *syscall.ACL) error {
userMap, err := getUserMap()
if err != nil {
return err
}
for i := range acl {
if acl[i].Tag == 'user' {
uid, ok := userMap[acl[i].Qualifier]
if ok {
acl[i].Qualifier = fmt.Sprintf('%d', uid)
}
}
}
return syscall.Setfacl(path, acl)
}
func getUserMap() (map[string]int, error) {
userMap := make(map[string]int)
passwdFile, err := os.Open('/etc/passwd')
if err != nil {
return nil, err
}
defer passwdFile.Close()
scanner := bufio.NewScanner(passwdFile)
for scanner.Scan() {
fields := strings.Split(scanner.Text(), ':')
if len(fields) >= 3 {
uid, err := strconv.Atoi(fields[2])
if err == nil {
userMap[fields[0]] = uid
}
}
}
return userMap, nil
}
func copyXattr(src, dst string) error {
xattrs, err := getXattrs(src)
if err != nil {
return err
}
for name, value := range xattrs {
err := setXattr(dst, name, value)
if err != nil {
return err
}
}
return nil
}
func getXattrs(path string) (map[string][]byte, error) {
xattrs := make(map[string][]byte)
xattrList := make([]byte, 1024)
for {
n, err := syscall.Listxattr(path, xattrList)
if err != nil {
return nil, err
}
if n == len(xattrList) {
xattrList = make([]byte, n*2)
} else {
break
}
}
for _, name := range bytes.Split(xattrList[:], []byte{0}) {
if len(name) > 0 {
value := make([]byte, 1024)
for {
n, err := syscall.Getxattr(path, string(name), value)
if err != nil {
return nil, err
}
if n == len(value) {
value = make([]byte, n*2)
} else {
xattrs[string(name)] = value[:n]
break
}
}
}
}
return xattrs, nil
}
func setXattr(path, name string, value []byte) error {
return syscall.Setxattr(path, name, value, 0)
}
func main() {
src := '/path/to/source/file'
dst := '/path/to/destination/file'
err := copyFile(src, dst)
if err != nil {
fmt.Println(err)
return
}
fmt.Println('File copied successfully.')
}
注意事项:
- 以上代码仅在 Linux 环境下使用,并且需要 root 权限才能访问 ACL 和 xattr。
- 由于 ACL 和 xattr 可能包含敏感信息,因此需要谨慎使用。在实际使用中,应该先检查源文件是否包含这些属性,然后再复制到目标文件中。
原文地址: https://www.cveoy.top/t/topic/mtZu 著作权归作者所有。请勿转载和采集!