检查文件是否为硬链接。

huangapple go评论74阅读模式
英文:

Check whether a file is a hard link

问题

你好!以下是翻译好的内容:

如何在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只读取符号链接,而不是硬链接。

我找到了一个有点相关的答案
https://stackoverflow.com/questions/26854961/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())

希望对你有帮助!如果还有其他问题,请随时提问。

英文:

How do you check whether a file is a hard link in Go? os.FileMode only has a mode for symlinks, not hard links.

I had an idea that unfortunately doesn't work:

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)
}

This^ doesn't work because os.Readlink reads only symlinks, not hard links.

I found a somewhat related answer:
https://stackoverflow.com/questions/26854961/counting-hard-links-to-a-file-in-go
But this answer shows how to find the number of hard links to a file, not whether a file itself is a hard link.

I'm guessing that the syscall package used in that answer or, better yet, the sys package has a way to test whether a file's a hard link. Does anyone know to do this? (I have trouble understanding those packages because they're so low-level.)

EDIT

I should add the reason why I'd like to check this. I'm trying to make a function to create a tar archive of a directory [using filepath.Walk()]. In this function, when I create the *tar.Header for a file, I set a value to *tar.Header.Typeflag.
For example, if fi is a file's *os.FileInfo variable and hdr is the *tar.Header variable for that file's place in a new tar archive, it looks like this:

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

In the tar package, the modes for hard links and regular files are distinct, TypeLink and TypeReg, but this isn't the case in the os package. So running this won't set the correct Typeflag:

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

答案1

得分: 5

从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()'读取链接本身。
	// 'os.Stat()'将读取链接的目标。
	fi, err := os.Lstat(filename)
	if err != nil {
		log.Fatal(err)
	}

	// https://github.com/docker/docker/blob/master/pkg/archive/archive_unix.go
	// 在'func setHeaderForSpecialDevice()'中
	s, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		err = errors.New("无法将stat值转换为syscall.Stat_t")
		log.Fatal(err)
	}

	// 此文件inode的索引号:
	inode := uint64(s.Ino)
	// 连接到此文件inode的文件/硬链接的总数:
	nlink := uint32(s.Nlink)

	// 如果文件是符号链接,则为真。
	if fi.Mode()&os.ModeSymlink != 0 {
		link, err := os.Readlink(fi.Name())
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%v是指向inode %v的符号链接:%v。\n", filename, inode, link)
		os.Exit(0)
	}

	// 否则,对于硬链接:
	fmt.Printf("%v的inode为%v,有%v个硬链接。\n", filename, inode, nlink)
	if nlink > 1 {
		fmt.Printf("除%v之外,inode %v还有%v个其他硬链接。\n", filename, inode, nlink)
	} else {
		fmt.Printf("%v是inode %v的唯一硬链接。\n", filename, inode)
	}
}
英文:

Figured it out from an example in Docker's source code:
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)
    }
}

答案2

得分: 4

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

因此,“这个文件是一个硬链接吗”的问题实际上没有意义。有意义的问题是,“这两个文件是彼此的硬链接吗”,或者更准确地说,“这两个文件是指向同一个inode的指针吗”。如果你想在Go中回答这个问题,你只需要使用os.SameFile

英文:

The term "hard link" is a bit of a misnomer. The way files work in most filesystems is that a given file (the thing identified by a path, like /foo) is actually just a pointer to a structure called an "inode." Inodes are the structures on disk that actually represent the contents and metadata of the file. When a file is a "hard link" to another file, it just means that they both point to the same inode. The system will keep track of the number of files pointing to a given inode, and make sure not to delete the inode until all files pointing to it have been deleted.

Thus, the question "is this file a hard link" doesn't really make sense. What does make sense is the question, "are these two files hard links to each other," or, more accurately, "are these two files pointers to the same inode." If you want to answer that question in Go, all you need is os.SameFile.

答案3

得分: 1

文件是指向索引节点(inode)的硬链接,你可以有多个指向同一个索引节点的硬链接,文件系统不会区分它们。如果你有两个文件指向磁盘上的同一个索引节点,并删除其中一个文件,那么文件仍然存在。只有当所有指向该索引节点的链接都被删除后,文件系统才会回收空间。

你可以使用 ls -i 命令确定索引节点的索引号,使用 stats <文件路径> 命令来查看文件系统中指向该索引节点的链接数量。

英文:

Files are hard links to inodes, you can have multiple hard links to inodes and the file system will not distinguish between them. If you have two files that point to the same inode on disk and remove one the file will still exist. Space is only reclaimed by the file system once all links to the inode have been deleted.

You can use ls -i to determine the index, and stats &lt;path to file&gt; to work out how many links to the inode there are in your file system.

huangapple
  • 本文由 发表于 2015年8月8日 10:14:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/31888955.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定