小编典典

io.Copy()导致文件稀疏

go

我想将文件从一个地方复制到另一个地方,问题是我处理了很多稀疏文件。

有什么(简单的)复制稀疏文件而不会在目标位置变得庞大的方法?

我的基本代码:

out, err := os.Create(bricks[0] + "/" + fileName)
in, err := os.Open(event.Name)
io.Copy(out, in)

阅读 294

收藏
2020-07-02

共1个答案

小编典典

一些背景理论

请注意,io.Copy()通过管道传输原始字节–一旦您认为将原始数据通过管道从io.Readerio.Writer提供Read([]byte)和的Write([]byte),就可以理解。这样,io.Copy()绝对可以处理提供字节的任何源以及使用它们的任何接收器。

另一方面,文件中漏洞的位置是“侧通道”信息,这些“侧通道”信息是“经典”系统调用,例如read(2)对用户隐藏。
io.Copy()不能以任何方式传达这种辅助信道信息。

IOW,最初,文件稀疏是一种仅在用户背后有效存储数据的想法。

因此,不,io.Copy()它本身无法处理稀疏文件。

该怎么办

您需要更深入一点,并使用该syscall软件包和一些手动修改来实现所有这些功能。

要使用孔,应为syscall
使用SEEK_HOLESEEK_DATA特殊值lseek(2),尽管这在形式上是非标准的,但所有
主要
平台均支持。

不幸的是,库存syscall包(从Go
1.8.1开始)和目录golang.org/x/sys树中都不存在对那些“因此”位置的支持。

但是不要担心,有两个简单的步骤:

  1. 首先,将库存syscall.Seek()实际映射到lseek(2) 相关平台上。

  2. 接下来,你需要找出正确的值SEEK_HOLE,并 SEEK_DATA为需要支持的平台。

请注意,它们可以 自由地 在不同平台之间有所不同!

说,在我的Linux系统上,我可以做简单

    $ grep -E 'SEEK_(HOLE|DATA)' </usr/include/unistd.h 
#  define SEEK_DATA     3       /* Seek to next data.  */
#  define SEEK_HOLE     4       /* Seek to next hole.  */

…找出这些符号的值。

现在,假设您在包中创建了一个特定于Linux的文件,其中包含以下内容

// +build linux

const (
    SEEK_DATA = 3
    SEEK_HOLE = 4
)

然后将这些值与一起使用syscall.Seek()

syscall.Seek()可以使用值Fd()方法从打开的文件中获取传递给朋友的文件描述符os.File

读取时使用的模式是检测包含数据的区域,并从中读取数据-
请参阅示例。

请注意,这涉及读取稀疏文件。但是如果您实际上想稀疏地 转移 它们-也就是说,保留它们的这种特性-
情况就更加复杂了:它似乎更不便于携带,因此需要进行一些研究和实验。

在Linux上,您似乎可以尝试使用fallocate(2)with
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE尝试在要写入的文件末尾打个孔;如果该操作合法失败(带有syscall.EOPNOTSUPP),则只需将要清零的洞覆盖的零块塞入目标文件中即可-
希望操作系统能够做正确的事情并将其自身转换为一个洞。

请注意,作为一个概念,某些文件系统根本不支持漏洞。一个示例是FAT系列中的文件系统。我要带您的是,根据您的情况,无法创建稀疏文件实际上可能是目标文件系统的属性。

您可能会发现Go感兴趣的问题#13548“ archive /
tar:添加对编写包含稀疏文件的tar的支持”


另一个注意事项:您可能还考虑检查要复制源文件的目标目录是否与源文件位于同一文件系统中,如果成立,请使用syscall.Rename()(在POSIX系统上)或os.Rename()仅在不同目录中移动文件没有实际复制其数据。

2020-07-02