【问题标题】:Check whether a file is a hard link检查文件是否为硬链接
【发布时间】:2020-11-16 19:42:50
【问题描述】:

在 Go 中如何检查文件是否为链接os.FileMode 只有符号链接模式,没有硬链接。

我有一个想法,可惜行不通:

package main

func main() {
    filename := os.Args[1]
    var hardlink bool
    link, _ := os.Readlink(filename)
    fi, _ := os.Stat(filename)
    mode := string(fi.Mode().String()[0])
    if link != "" && mode != "L" {
        hardlink = true
    }
    fmt.Printf("%v is hard link? %v\n", filename, hardlink)
}

这个^ 不起作用,因为os.Readlink 只读取符号链接,而不是硬链接。

我找到了一个有点相关的答案:
Counting hard links to a file in Go
但是这个答案显示了如何找到文件的硬链接数量,而不是是否文件本身是硬链接。

我猜测该答案中使用的 syscall 包,或者更好的是,sys 包可以测试文件是否为硬链接。有谁知道这样做吗? (我很难理解这些包,因为它们太低级了。)

编辑

我应该添加我想检查这个的原因。我正在尝试创建一个函数来创建目录的 tar 存档 [使用filepath.Walk()]。在此函数中,当我为文件创建 *tar.Header 时,我将值设置为 *tar.Header.Typeflag
例如,如果fi 是文件的*os.FileInfo 变量,而hdr 是该文件在新tar 归档中的位置的*tar.Header 变量,则如下所示:

if fi.Mode().IsDir() {
    hdr.Typeflag = tar.TypeDir
}

tar 包中,硬链接和常规文件的模式是不同的,TypeLinkTypeReg,但在os 包中不是这种情况。所以运行它不会设置正确的Typeflag

hdr.Mode = int64(fi.Mode())

【问题讨论】:

  • 每个常规文件都是硬链接。指向同一个 inode 的两个硬链接之间没有区别,只是它们的名称不同。
  • 删除文件的系统调用称为“取消链接”是有原因的。 (另外,golang.org/x/sys 包只是 syscall 包加上更新,因为 stdlib 系统调用被冻结了)
  • 老实说,我以前从来没有这样想过。这清楚了很多。我正在更新我的答案以显示我想检查这个的原因。

标签: file go attributes hardlink


【解决方案1】:

从 Docker 源代码中的一个示例中弄清楚:
https://github.com/docker/docker/blob/master/pkg/archive/archive.go
https://github.com/docker/docker/blob/master/pkg/archive/archive_unix.go

package main

import (
    "errors"
    "fmt"
    "log"
    "os"
    "syscall"
)

func main() {
    filename := os.Args[1]

    // 'os.Lstat()' reads the link itself.
    // 'os.Stat()' would read the link's target.
    fi, err := os.Lstat(filename)
    if err != nil {
        log.Fatal(err)
    }

    // https://github.com/docker/docker/blob/master/pkg/archive/archive_unix.go
    // in 'func setHeaderForSpecialDevice()'
    s, ok := fi.Sys().(*syscall.Stat_t)
    if !ok {
        err = errors.New("cannot convert stat value to syscall.Stat_t")
        log.Fatal(err)
    }

    // The index number of this file's inode:
    inode := uint64(s.Ino)
    // Total number of files/hardlinks connected to this file's inode:
    nlink := uint32(s.Nlink)

    // True if the file is a symlink.
    if fi.Mode()&os.ModeSymlink != 0 {
        link, err := os.Readlink(fi.Name())
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%v is a symlink to %v on inode %v.\n", filename, link, inode)
        os.Exit(0)
    }

    // Otherwise, for hardlinks:
    fmt.Printf("The inode for %v, %v, has %v hardlinks.\n", filename, inode, nlink)
    if nlink > 1 {
        fmt.Printf("Inode %v has %v other hardlinks besides %v.\n", inode, nlink, filename)
    } else {
        fmt.Printf("%v is the only hardlink to inode %v.\n", filename, inode)
    }
}

【讨论】:

  • 顺便说一句,您通常不应该只保存/使用/引用文件的 inode 编号;如果不提及设备(例如保存/使用/参考s.Devs.Ino 代替),它是没有意义的。特别是对于像归档工具这样遍历文件树的东西,它可能会跨越设备/文件系统边界,并且需要将不同的 inode 与具有相同 inode 编号的不同设备区分开来(例如,通过使用 s.Dev, s.Ino 的结构作为映射/设置键)。
  • 这是一个非常好的观点;忘记这样做可能会很麻烦。我会确保将其添加到我的代码中。 Docker 代码的链接也有一个例子,谢天谢地
【解决方案2】:

术语“硬链接”有点用词不当。文件在大多数文件系统中的工作方式是给定文件(由路径标识的东西,如/foo)实际上只是一个指向称为“inode”的结构的指针。索引节点是磁盘上实际表示文件内容和元数据的结构。当一个文件是另一个文件的“硬链接”时,它只是意味着它们都指向同一个 inode。系统将跟踪指向给定 inode 的文件的数量,并确保在指向它的所有文件都被删除之前不要删除该 inode。

因此,“此文件是否为硬链接”这个问题没有任何意义。 做什么 有意义的是“这两个文件是否相互硬链接”,或者更准确地说,“这两个文件是否指向同一个 inode”。如果你想在 Go 中回答这个问题,你只需要os.SameFile

【讨论】:

  • 感谢您的解释!是的,问题的标题应该更像“检查文件的 inode 是否有任何其他硬链接”。除非有人认为我应该更改标题,否则我将保持原样,否则答案将没有多大意义。
  • 啊,明白了。我不知道。我不确定它究竟是如何在幕后实现的,但我猜在 inode 上有某种“指向我”的计数器,在这种情况下可能会有某种方式来访问它。但我不确定这是否正确,即使正确,你将如何去做。
  • 是的,你是对的!它位于syscall.Stat_t.Nlinksyscall.Stat_t.Ino 之下。我在 Docker 的源代码中找到了一个示例,展示了如何获取它。我在我发布的答案中做了一个快速、简洁的例子。
  • 我们可以说所有文件都是硬链接吗?
  • 通常,当人们说“硬链接”时,他们的意思是暗示两个或多个文件共享相同的 inode。但是您当然可以想象定义术语“硬链接”,以便所有文件都是硬链接。但是,“硬链接”这个词不会很有帮助,因为它基本上与“文件”的含义相同:)
【解决方案3】:

文件是指向 inode 的硬链接,您可以有多个指向 inode 的硬链接,文件系统不会区分它们。如果您有两个文件指向磁盘上的同一个 inode 并删除了一个文件,则该文件仍然存在。只有在删除所有指向 inode 的链接后,文件系统才会回收空间。

您可以使用ls -i 确定索引,并使用stats <path to file> 计算文件系统中有多少指向inode 的链接。

【讨论】:

    猜你喜欢
    • 2013-04-28
    • 2010-10-16
    • 1970-01-01
    • 2013-10-24
    • 1970-01-01
    • 1970-01-01
    • 2014-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多