4

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

是否有任何(简单)方法可以复制稀疏文件而不会在目的地变得庞大?

我的基本代码:

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

2 回答 2

7

一些背景理论

请注意,io.Copy()管道原始字节 - 一旦您认为它将数据从 an 管道传输io.Reader到 an io.Writerwhich 提供Read([]byte)Write([]byte)相应地,这是可以理解的。因此,io.Copy()绝对能够处理任何提供字节的源和绝对任何消耗它们的接收器。

另一方面,文件中漏洞的位置是“旁通道”信息,“经典”系统调用(例如read(2)对用户隐藏)。 io.Copy()无法以任何方式传达此类旁道信息。

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

所以,不,没有办法io.Copy()处理稀疏文件本身。

该怎么办

您需要更深一层并使用syscall包和一些手动修补来实现所有这些。

要处理漏洞,您应该使用系统调用的SEEK_HOLESEEK_DATA特殊值lseek(2),虽然它们在形式上是非标准的,但受到所有 主要 平台的支持。

syscall不幸的是,stock包(从 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()只是将文件移动到不同的目录没有实际复制其数据。

于 2017-03-27T07:39:35.223 回答
0

您不需要求助于系统调用。

package main

import "os"

func main() {
    f, _ := os.Create("/tmp/sparse.dat")
    f.Write([]byte("start"))
    f.Seek(1024*1024*10, 0)
    f.Write([]byte("end"))
}

然后你会看到:

$ ls -l /tmp/sparse.dat
-rw-rw-r-- 1 soren soren 10485763 Jun 25 14:29 /tmp/sparse.dat
$ du /tmp/sparse.dat
8   /tmp/sparse.dat

确实,您不能io.Copy按原样使用。相反,您需要实现一个替代方案,io.Copy从. 如果是,只需跳过. 该特定实现留给读者作为练习:)src'\0'dst.Seek(len(chunk), os.SEEK_CUR)dst

于 2020-06-25T12:38:57.377 回答