有没有办法用Go复制tar命令的-h选项?

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

Is there a way to replicate the tar -h option with Go?

问题

在Unix中,tar实用程序有一个-h选项,可以归档链接的目标。我想知道在Go中是否有一种方法可以做到这一点。目前,当我尝试将链接的内容复制到tar文件中,并将头文件名设置为符号链接文件时,会出现tar: 损坏的tar归档错误。下面的代码是我目前正在使用的。该函数的目标是递归地将当前目录中的所有内容打包到tar文件中。

我想也许我只需要将符号链接的大小设置为目标文件的大小,但仍然出现错误。

func createTarFile() {
    tarfile, _ := os.Create(buildCtxTar)
    defer tarfile.Close()

    tarball := tar.NewWriter(tarfile)
    defer tarball.Close()

    //获取当前工作目录
    rootDir, _ := os.Getwd()

    //将当前目录中的所有文件添加到tarball中
    walkFn := func(path string, info os.FileInfo, err error) error {
        //不要将tar文件本身添加到其中,也不要添加工作目录
        if info.Name() == buildCtxTar || info.Name() == filepath.Base(rootDir) {
            return nil
        }

        //tar文件中不包含绝对路径,"+1"去除前导文件分隔符
        relativePath := path[len(rootDir)+1:]

        isSymlink := info.Mode()&os.ModeSymlink == os.ModeSymlink
        var size int64 = -1
        //处理符号链接
        if isSymlink {
            path, size, err = handleSymlink(path)
            if err != nil {
                return err
            }
        }

        header, err := tar.FileInfoHeader(info, path)
        if err != nil {
            return err
        }

        //如果操作系统是Windows,我们需要将"\\"转换为"/"
        relativePath = filepath.ToSlash(relativePath)

        //将文件名重命名为其路径,以便创建结构化的tarball
        header.Name = relativePath
        if isSymlink {
            header.Size = size
        }
        if err := tarball.WriteHeader(header); err != nil {
            return err
        }

        //不要尝试复制特殊文件(如目录)的数据
        if !isSymlink && !info.Mode().IsRegular() {
            return nil
        }
        f, err := os.Open(path)
        if err != nil {
            return err
        }
        defer f.Close()

        //将文件数据复制到tarball中
        if _, err := io.Copy(tarball, f); err != nil {
            return err
        }
        return nil
    }

    //遍历当前目录的所有文件和子目录
    err := filepath.Walk(rootDir, walkFn)
}

//获取符号链接的文件路径
func handleSymlink(path string) (string, int64, error) {
    //读取链接
    link, err := os.Readlink(path)
    if err != nil {
        return "", -1, err
    }
    //如果文件路径不是绝对路径,我们需要重建它
    if !filepath.IsAbs(link) {
        link = filepath.Join(filepath.Dir(path), link)
        if err != nil {
            return "", -1, err
        }
    }
    fi, err := os.Stat(link)
    if err != nil {
        return "", -1, err
    }
    size := fi.Size()
    return link, size, nil
}
英文:

In Unix the tar utility has a -h option that archives the target of the link. I'm wondering if there's a way to do this with Go. Right now when I try to copy the contents of the link to the tar with the header name set to the symlink file I get a tar: Damaged tar archive error. The code below is what I'm currently using. The goal of the function is to recursively tar everything in the current directory.

I thought maybe all I'd have to do is set the size of the symlink to the size of the target file but still getting an error.

<!-- language: go -->

func createTarFile() {
tarfile, _ := os.Create(buildCtxTar)
defer tarfile.Close()
tarball := tar.NewWriter(tarfile)
defer tarball.Close()
//get the working directory
rootDir, _ := os.Getwd()
//add all the files in the current directory to the tarball
walkFn := func(path string, info os.FileInfo, err error) error {
//don&#39;t add the tar file to itself, don&#39;t add the working directory either
if info.Name() == buildCtxTar || info.Name() == filepath.Base(rootDir) {
return nil
}
//no absolute paths in the tar file, the &quot;+1&quot; strips the leading file separator
relativePath := path[len(rootDir)+1:]
isSymlink := info.Mode()&amp;os.ModeSymlink == os.ModeSymlink
var size int64 = -1
//evaluate symbolic links
if isSymlink {
path, size, err = handleSymlink(path)
if err != nil {
return err
}
}
header, err := tar.FileInfoHeader(info, path)
if err != nil {
return err
}
//if the OS is windows we need to convert the &#39;\&#39; to &#39;/&#39;
relativePath = filepath.ToSlash(relativePath)
//rename the name of the file to its path for a structured tarball
header.Name = relativePath
if isSymlink {
header.Size = size
}
if err := tarball.WriteHeader(header); err != nil {
return err
}
//don&#39;t attempt copy data from special files (like directories)
if !isSymlink &amp;&amp; !info.Mode().IsRegular() {
return nil
}
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
// copy the file data to the tarball
if _, err := io.Copy(tarball, f); err != nil {
return err
}
return nil
}
//walks through all files and subdirectories of the current directory
err := filepath.Walk(rootDir, walkFn)
}
//get the filepath for the symbolic link
func handleSymlink(path string) (string, int64, error) {
//read the link
link, err := os.Readlink(path)
if err != nil {
return &quot;&quot;, -1, err
}
//if the filepath isn&#39;t an absolute path we need to
//reconstruct it so that it is
if !filepath.IsAbs(link) {
link = filepath.Join(filepath.Dir(path), link)
if err != nil {
return &quot;&quot;, -1, err
}
}
fi, err := os.Stat(link)
if err != nil {
return &quot;&quot;, -1, err
}
size := fi.Size()
return link, size, nil
}

答案1

得分: 0

你正在使用符号链接的FileInfo创建tar.Header,该符号链接仍然具有符号链接的TypeflagMode

从目标文件创建一个新的头部,并通过将名称字段更改为符号链接的名称,将其“移动”到存档中。

英文:

You are creating the tar.Header from the symlink FileInfo, which is still going to have the Typeflag and Mode of a symlink.

Create a new header from the target file, and "move" it into the archive by changing the Name field to that of the symlink.

huangapple
  • 本文由 发表于 2017年3月24日 02:25:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/42984121.html
匿名

发表评论

匿名网友

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

确定