Golang中如何获取文件的所有者信息

Golang中如何获取文件的所有者信息 在Go语言中,获取文件所有者的标准方法是什么?我昨天花了一些时间研究这个问题,但没有找到简单的解决方案。有人建议使用 fi.Sys().(*syscall.Stat_t).Uid

一些帖子指出,由于没有独立于操作系统的统一方法来实现此功能,Go语言并未内置此功能。特别是在Windows和GNU/Linux系统之间,这方面存在差异。

我今天将继续研究这个问题,并会在找到最终使用的解决方案后(如果我能找到的话)回复我的帖子。

诚挚的问候。

4 回复

在那个例子中,我要找的文件叫做‘tcpscan’——请替换成你要找的文件名。

更多关于Golang中如何获取文件的所有者信息的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


更新: 我决定采用 fi.Sys().(*syscall.Stat_t).Uid 这个方法。在这个过程中,我重新认识到/学到了一些东西。user.Uid 是一个字符串。要从中获取 uint32 类型,必须使用 strconv.ParseUint()uint32()

我还刚刚了解了 go doc 命令。例如:

go doc strconv

干杯!

一种非常简单的方法是使用 GitHub - bitfield/script: 让用 Go 编写类似 shell 的脚本变得容易

package main

import (
	"fmt"
	"github.com/bitfield/script"
)

func main() {
	owner, _ := script.Exec("ls -alhrt /usr/local/bin").Match("tcpscan").Column(3).String()
	fmt.Println("Owner of the file is", owner)
}

输出:

go run main.go
Owner of the file is rmasci

如果你在 Windows 上,你只需要检查 GOOS 是 windows、mac 还是 linux,并相应地替换命令。

在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。

回到顶部