Reputation: 413
I am having trouble figuring out how to write a directory into tarball's headerinfo. For example, I have this directory structure:
test [dir]
-- test1.txt
-- subDirA [sub dir]
-- test2.txt
If I tar up test using my Go tar executable, it will untar and have the original structure. If I do a tar -tvf on the tarball, the listing will be:
test/test1.txt
test/subDirA/test2.txt
However if I manually do a tar -cvf of test and then do a tar -tvf, the listing will be:
test/
test/test1.txt
test/subDirA/
test/subDirA/test2.txt
This is what I want my Go tarring program to do as well. In my Go program, I tried to add a new method writeDirToTar
to deal with adding directories into the tarWriter. I call either writeFileToTar or writeDirToTar from a fileWalk function. I am getting a "test/subDirA is a directory" error when I call io.Copy(tarWriter, file)
in writeDirToTar
, which kind of makes sense. Is there a workaround for this?
func writeToTar(fileDir string,
sourceBase string,
tarWriter *tar.Writer,
f os.FileInfo) error {
file, err := os.Open(fileDir)
if err != nil {
log.Print("error opening file")
return err
}
defer file.Close()
// relative paths are used to preserve the directory paths in each file path
relativePath, err := filepath.Rel(sourceBase, fileDir)
tarheader := new(tar.Header)
tarheader.Name = relativePath
tarheader.Size = f.Size()
tarheader.Mode = int64(f.Mode())
tarheader.ModTime = f.ModTime()
err = tarWriter.WriteHeader(tarheader)
if err != nil {
log.Print("error writing tarHeader")
return err
}
_, err = io.Copy(tarWriter, file)
if err != nil {
log.Print("error writing to tarWriter")
return err
}
return nil
}
func writeDirToTar(fileDir string,
sourceBase string,
tarWriter *tar.Writer,
f os.FileInfo) error {
file, err := os.Open(fileDir)
if err != nil {
log.Print("error opening file")
return err
}
defer file.Close()
// relative paths are used to preserve the directory paths in each file path
relativePath, err := filepath.Rel(sourceBase, fileDir)
if tarHeader, err := tar.FileInfoHeader(f, relativePath); err != nil {
log.Print("error writing tarHeader")
return err
}
tarHeader.Name = relativePath
tarheader.Mode = int64(f.Mode())
tarheader.ModTime = f.ModTime()
_, err = io.Copy(tarWriter, file)
if err != nil {
log.Print("error writing to tarWriter")
return err
}
return nil
}
Upvotes: 3
Views: 3765
Reputation: 109401
You're missing the Typeflag
field in the tar.Header
.
For the directory you'll want at least:
tarheader := &tar.Header{
Name: relativePath,
Mode: int64(f.Mode()),
ModTime: f.ModTime(),
Typeflag: tar.TypeDir,
}
You don't want a size here, nor do you want to copy the directory data, since the internal directory data structures mean nothing to tar.
You may also want to add a /
to the filename to visually indicate it's a directory and match the POSIX tar behavior.
Alternatively you could just use the tar.FileInfoHeader
function just like you did with the files to create the proper header structure.
Upvotes: 2