英文:
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't add the tar file to itself, don'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 "+1" strips the leading file separator
relativePath := path[len(rootDir)+1:]
isSymlink := info.Mode()&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 '\' to '/'
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't attempt copy data from special files (like directories)
if !isSymlink && !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 "", -1, err
}
//if the filepath isn'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 "", -1, err
}
}
fi, err := os.Stat(link)
if err != nil {
return "", -1, err
}
size := fi.Size()
return link, size, nil
}
答案1
得分: 0
你正在使用符号链接的FileInfo
创建tar.Header
,该符号链接仍然具有符号链接的Typeflag
和Mode
。
从目标文件创建一个新的头部,并通过将名称字段更改为符号链接的名称,将其“移动”到存档中。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论