在Go中获取文件所有者信息确实需要依赖系统调用,因为标准库没有提供跨平台的统一接口。以下是针对不同操作系统的实现方法:
Linux/Unix系统实现
package main
import (
"fmt"
"os"
"syscall"
)
func getFileOwner(filename string) (uid, gid uint32, err error) {
fileInfo, err := os.Stat(filename)
if err != nil {
return 0, 0, err
}
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
if !ok {
return 0, 0, fmt.Errorf("failed to get stat_t")
}
return stat.Uid, stat.Gid, nil
}
func main() {
uid, gid, err := getFileOwner("/etc/passwd")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("UID: %d, GID: %d\n", uid, gid)
}
Windows系统实现
// +build windows
package main
import (
"fmt"
"syscall"
"unsafe"
)
var (
advapi32 = syscall.NewLazyDLL("advapi32.dll")
getNamedSecurityInfo = advapi32.NewProc("GetNamedSecurityInfoW")
)
func getFileOwner(filename string) (ownerSid string, err error) {
var sid *syscall.SID
var secDesc uintptr
ret, _, _ := getNamedSecurityInfo.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(filename))),
uintptr(1), // SE_FILE_OBJECT
uintptr(4), // OWNER_SECURITY_INFORMATION
uintptr(unsafe.Pointer(&sid)),
0, 0, 0,
uintptr(unsafe.Pointer(&secDesc)),
)
if ret != 0 {
return "", syscall.Errno(ret)
}
// 将SID转换为字符串
sidString, err := sid.String()
if err != nil {
return "", err
}
return sidString, nil
}
跨平台兼容方案
package main
import (
"fmt"
"os"
"runtime"
"syscall"
)
type FileOwner struct {
UID uint32
GID uint32
User string
Group string
}
func GetFileOwner(filename string) (*FileOwner, error) {
switch runtime.GOOS {
case "linux", "darwin", "freebsd", "openbsd":
return getUnixFileOwner(filename)
case "windows":
return getWindowsFileOwner(filename)
default:
return nil, fmt.Errorf("unsupported platform: %s", runtime.GOOS)
}
}
func getUnixFileOwner(filename string) (*FileOwner, error) {
fileInfo, err := os.Stat(filename)
if err != nil {
return nil, err
}
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
if !ok {
return nil, fmt.Errorf("failed to get stat_t")
}
owner := &FileOwner{
UID: stat.Uid,
GID: stat.Gid,
}
// 可选:将UID/GID转换为用户名/组名
// owner.User = getUserFromUID(stat.Uid)
// owner.Group = getGroupFromGID(stat.Gid)
return owner, nil
}
func main() {
owner, err := GetFileOwner("/etc/passwd")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("UID: %d, GID: %d\n", owner.UID, owner.GID)
}
使用第三方库
对于生产环境,建议使用成熟的第三方库:
import "github.com/pkg/xattr"
// 或者使用更完整的文件属性处理库
import "golang.org/x/sys/unix"
func getOwnerWithXattr(filename string) {
// xattr库提供了跨平台的文件扩展属性支持
// 但所有者信息获取仍需要系统特定调用
}
你提到的 fi.Sys().(*syscall.Stat_t).Uid 方法是正确的Unix/Linux系统解决方案。Windows系统需要使用不同的API,主要是GetNamedSecurityInfo函数来获取文件的安全描述符和所有者SID。