Last active
September 18, 2020 05:13
-
-
Save rcrowley/9cc826addd549e515720e145c741d744 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// It seems that Go's archive/zip does not preserve the | |
// executable bit on input files on Mac OS. It does, so | |
// something else is fishy, but I'll preserve this for | |
// posterity and further thinking about what's wrong. | |
package zipadeedoodah | |
import ( | |
"archive/zip" | |
"io/ioutil" | |
"os" | |
"os/exec" | |
"path/filepath" | |
"testing" | |
) | |
func TestZipExecutableBit(t *testing.T) { | |
// Run this test in a temporary directory and clean it up afterwards. | |
dirname, err := ioutil.TempDir("", "TestZipExecutableBit-") | |
if err != nil { | |
t.Fatal(err) | |
} | |
defer func() { | |
if err := os.RemoveAll(dirname); err != nil { | |
t.Fatal(err) | |
} | |
}() | |
// Create an empty executable file which we'll add to the archive to | |
// demonstrate the bug. | |
/* | |
f, err := os.OpenFile(filepath.Join(dirname, "bin.bin"), os.O_CREATE, 0777) | |
//f, err := os.Create(filepath.Join(dirname, "bin.bin")) // no x, test fails | |
if err != nil { | |
t.Fatal(err) | |
} | |
if err := f.Close(); err != nil { | |
t.Fatal(err) | |
} | |
*/ | |
// Create an executable ELF binary that does nothing which we'll add to | |
// the archive to demonstrate the bug. | |
if err := ioutil.WriteFile( | |
filepath.Join(dirname, "bin.go"), | |
[]byte(`package main; func main() {}`), | |
0666, | |
); err != nil { | |
t.Fatal(err) | |
} | |
cmd := exec.Command("go", "mod", "init", "zipadeedoodah") | |
cmd.Dir = dirname | |
cmd.Stdout = os.Stdout | |
cmd.Stderr = os.Stderr | |
if err := cmd.Run(); err != nil { | |
t.Fatal(err) | |
} | |
cmd = exec.Command("go", "build", "-o", "bin.bin") | |
cmd.Dir = dirname | |
cmd.Env = []string{"GOARCH=amd64", "GOOS=linux", "HOME=" + os.Getenv("HOME")} | |
cmd.Stdout = os.Stdout | |
cmd.Stderr = os.Stderr | |
if err := cmd.Run(); err != nil { | |
t.Fatal(err) | |
} | |
/* | |
cmd = exec.Command(filepath.Join(dirname, "bin.bin")) | |
cmd.Stdout = os.Stdout | |
cmd.Stderr = os.Stderr | |
if err := cmd.Run(); err != nil { // confirm it executes; only works on Linux | |
t.Fatal(err) | |
} | |
*/ | |
// Create a zip file and add our empty executable to it. | |
f, err := os.Create(filepath.Join(dirname, "zip.zip")) | |
if err != nil { | |
t.Fatal(err) | |
} | |
w := zip.NewWriter(f) | |
fi, err := os.Stat(filepath.Join(dirname, "bin.bin")) | |
if err != nil { | |
t.Fatal(err) | |
} | |
h, err := zip.FileInfoHeader(fi) | |
if err != nil { | |
t.Fatal(err) | |
} | |
_, err = w.CreateHeader(h) | |
if err != nil { | |
t.Fatal(err) | |
} | |
// Close the zip file as pedantically as possible. | |
if err := w.Close(); err != nil { | |
t.Fatal(err) | |
} | |
if err := f.Close(); err != nil { | |
t.Fatal(err) | |
} | |
// Examine the zip file using the external toolchain. | |
/* | |
cmd = exec.Command("zipinfo", filepath.Join(dirname, "zip.zip")) | |
cmd.Stdout = os.Stdout | |
cmd.Stderr = os.Stderr | |
if err := cmd.Run(); err != nil { | |
t.Fatal(err) | |
} | |
*/ | |
// Open the zip file again, this time for reading. | |
r, err := zip.OpenReader(filepath.Join(dirname, "zip.zip")) | |
if err != nil { | |
t.Fatal(err) | |
} | |
defer func() { | |
if err := r.Close(); err != nil { | |
t.Fatal(err) | |
} | |
}() | |
// Assert that the empty executable in the zip file is executable and, | |
// indeed, has the same permissions as the original. | |
for _, f := range r.File { | |
if f.Name != "bin.bin" { | |
t.Fatal(f.Name) | |
} | |
if f.Mode().Perm()&0111 == 0 { | |
t.Fatalf("%o", f.Mode().Perm()) | |
} | |
if f.Mode().Perm() != fi.Mode().Perm() { | |
t.Fatalf("%o", f.Mode().Perm()) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment