summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/pack/doc.go4
-rw-r--r--src/cmd/pack/pack.go50
-rw-r--r--src/cmd/pack/pack_test.go23
3 files changed, 57 insertions, 20 deletions
diff --git a/src/cmd/pack/doc.go b/src/cmd/pack/doc.go
index 1529e07e9..a702594e2 100644
--- a/src/cmd/pack/doc.go
+++ b/src/cmd/pack/doc.go
@@ -20,6 +20,10 @@ The operation op is given by one of these letters:
t list files from the archive
x extract files from the archive
+The archive argument to the c command must be non-existent or a
+valid archive file, which will be cleared before adding new entries. It
+is an error if the file exists but is not an archive.
+
For the p, t, and x commands, listing no names on the command line
causes the operation to apply to all files in the archive.
diff --git a/src/cmd/pack/pack.go b/src/cmd/pack/pack.go
index 594433712..ffb2d617a 100644
--- a/src/cmd/pack/pack.go
+++ b/src/cmd/pack/pack.go
@@ -142,16 +142,19 @@ type Archive struct {
matchAll bool // match all files in archive
}
-// archive opens (or if necessary creates) the named archive.
+// archive opens (and if necessary creates) the named archive.
func archive(name string, mode int, files []string) *Archive {
- fd, err := os.OpenFile(name, mode, 0)
- if err != nil && mode&^os.O_TRUNC == os.O_RDWR && os.IsNotExist(err) {
- fd, err = create(name)
+ // If the file exists, it must be an archive. If it doesn't exist, or if
+ // we're doing the c command, indicated by O_TRUNC, truncate the archive.
+ if !existingArchive(name) || mode&os.O_TRUNC != 0 {
+ create(name)
+ mode &^= os.O_TRUNC
}
+ fd, err := os.OpenFile(name, mode, 0)
if err != nil {
log.Fatal(err)
}
- mustBeArchive(fd)
+ checkHeader(fd)
return &Archive{
fd: fd,
files: files,
@@ -160,23 +163,40 @@ func archive(name string, mode int, files []string) *Archive {
}
// create creates and initializes an archive that does not exist.
-func create(name string) (*os.File, error) {
- fd, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
+func create(name string) {
+ fd, err := os.Create(name)
if err != nil {
- return nil, err
+ log.Fatal(err)
+ }
+ _, err = fmt.Fprint(fd, arHeader)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fd.Close()
+}
+
+// existingArchive reports whether the file exists and is a valid archive.
+// If it exists but is not an archive, existingArchive will exit.
+func existingArchive(name string) bool {
+ fd, err := os.Open(name)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return false
+ }
+ log.Fatal("cannot open file: %s", err)
}
- fmt.Fprint(fd, arHeader)
- fd.Seek(0, 0)
- return fd, nil
+ checkHeader(fd)
+ fd.Close()
+ return true
}
-// mustBeArchive verifies the header of the file. It assumes the file offset
-// is 0 coming in, and leaves it positioned immediately after the header.
-func mustBeArchive(fd *os.File) {
+// checkHeader verifies the header of the file. It assumes the file
+// is positioned at 0 and leaves it positioned at the end of the header.
+func checkHeader(fd *os.File) {
buf := make([]byte, len(arHeader))
_, err := io.ReadFull(fd, buf)
if err != nil || string(buf) != arHeader {
- log.Fatal("file is not an archive: bad header")
+ log.Fatal("%s is not an archive: bad header", fd.Name())
}
}
diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go
index e41cf3ce4..cf6121fcc 100644
--- a/src/cmd/pack/pack_test.go
+++ b/src/cmd/pack/pack_test.go
@@ -56,11 +56,8 @@ func tmpDir(t *testing.T) string {
return name
}
-// Test that we can create an archive, write to it, and get the same contents back.
-// Tests the rv and then the pv command on a new archive.
-func TestCreate(t *testing.T) {
- dir := tmpDir(t)
- defer os.RemoveAll(dir)
+// testCreate creates an archive in the specified directory.
+func testCreate(t *testing.T, dir string) {
name := filepath.Join(dir, "pack.a")
ar := archive(name, os.O_RDWR, nil)
// Add an entry by hand.
@@ -85,6 +82,22 @@ func TestCreate(t *testing.T) {
}
}
+// Test that we can create an archive, write to it, and get the same contents back.
+// Tests the rv and then the pv command on a new archive.
+func TestCreate(t *testing.T) {
+ dir := tmpDir(t)
+ defer os.RemoveAll(dir)
+ testCreate(t, dir)
+}
+
+// Test that we can create an archive twice with the same name (Issue 8369).
+func TestCreateTwice(t *testing.T) {
+ dir := tmpDir(t)
+ defer os.RemoveAll(dir)
+ testCreate(t, dir)
+ testCreate(t, dir)
+}
+
// Test that we can create an archive, put some files in it, and get back a correct listing.
// Tests the tv command.
func TestTableOfContents(t *testing.T) {