aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2012-03-12 21:49:43 -0700
committerBrad Fitzpatrick <bradfitz@golang.org>2012-03-12 21:49:43 -0700
commitc405b58f3fa7988d42b5e5e46910344f342c5b45 (patch)
tree3c4dfae9b4fbbd4ecb6c7022ac96c056e5b0ffaa
parentdaacba518425fe2dcbdfd89ff43f8ab11cdabea8 (diff)
downloadgo-c405b58f3fa7988d42b5e5e46910344f342c5b45.tar.gz
go-c405b58f3fa7988d42b5e5e46910344f342c5b45.zip
misc/dist: better archive/tar Headers
This should live in archive/tar later (CL 5796073) but we can always do that after Go 1 and stick it here for now. R=golang-dev, adg CC=golang-dev https://golang.org/cl/5754096
-rw-r--r--misc/dist/bindist.go86
-rw-r--r--misc/dist/stat_darwin.go32
-rw-r--r--misc/dist/stat_linux.go32
3 files changed, 134 insertions, 16 deletions
diff --git a/misc/dist/bindist.go b/misc/dist/bindist.go
index 0d2fa21ea6..9ffa028fae 100644
--- a/misc/dist/bindist.go
+++ b/misc/dist/bindist.go
@@ -26,7 +26,6 @@ import (
"path/filepath"
"runtime"
"strings"
- "syscall"
)
var (
@@ -527,22 +526,16 @@ func makeTar(targ, workdir string) error {
if fi.IsDir() {
return nil
}
- var typeFlag byte
- switch {
- case fi.Mode()&os.ModeType == 0:
- typeFlag = tar.TypeReg
- default:
- log.Fatalf("makeTar: unknown file for file %q", name)
- }
- hdr := &tar.Header{
- Name: name,
- Mode: int64(fi.Sys().(*syscall.Stat_t).Mode),
- Size: fi.Size(),
- ModTime: fi.ModTime(),
- Typeflag: typeFlag,
- Uname: "root",
- Gname: "root",
+ hdr, err := tarFileInfoHeader(fi, path)
+ if err != nil {
+ return err
}
+ hdr.Name = name
+ hdr.Uname = "root"
+ hdr.Gname = "root"
+ hdr.Uid = 0
+ hdr.Gid = 0
+
err = tw.WriteHeader(hdr)
if err != nil {
return fmt.Errorf("Error writing file %q: %v", name, err)
@@ -686,3 +679,64 @@ func lookPath(prog string) (absPath string, err error) {
}
return
}
+
+// sysStat, if non-nil, populates h from system-dependent fields of fi.
+var sysStat func(fi os.FileInfo, h *tar.Header) error
+
+// Mode constants from the tar spec.
+const (
+ c_ISDIR = 040000
+ c_ISFIFO = 010000
+ c_ISREG = 0100000
+ c_ISLNK = 0120000
+ c_ISBLK = 060000
+ c_ISCHR = 020000
+ c_ISSOCK = 0140000
+)
+
+// tarFileInfoHeader creates a partially-populated Header from an os.FileInfo.
+// The filename parameter is used only in the case of symlinks, to call os.Readlink.
+// If fi is a symlink but filename is empty, an error is returned.
+func tarFileInfoHeader(fi os.FileInfo, filename string) (*tar.Header, error) {
+ h := &tar.Header{
+ Name: fi.Name(),
+ ModTime: fi.ModTime(),
+ Mode: int64(fi.Mode().Perm()), // or'd with c_IS* constants later
+ }
+ switch {
+ case fi.Mode()&os.ModeType == 0:
+ h.Mode |= c_ISREG
+ h.Typeflag = tar.TypeReg
+ h.Size = fi.Size()
+ case fi.IsDir():
+ h.Typeflag = tar.TypeDir
+ h.Mode |= c_ISDIR
+ case fi.Mode()&os.ModeSymlink != 0:
+ h.Typeflag = tar.TypeSymlink
+ h.Mode |= c_ISLNK
+ if filename == "" {
+ return h, fmt.Errorf("archive/tar: unable to populate Header.Linkname of symlinks")
+ }
+ targ, err := os.Readlink(filename)
+ if err != nil {
+ return h, err
+ }
+ h.Linkname = targ
+ case fi.Mode()&os.ModeDevice != 0:
+ if fi.Mode()&os.ModeCharDevice != 0 {
+ h.Mode |= c_ISCHR
+ h.Typeflag = tar.TypeChar
+ } else {
+ h.Mode |= c_ISBLK
+ h.Typeflag = tar.TypeBlock
+ }
+ case fi.Mode()&os.ModeSocket != 0:
+ h.Mode |= c_ISSOCK
+ default:
+ return nil, fmt.Errorf("archive/tar: unknown file mode %v", fi.Mode())
+ }
+ if sysStat != nil {
+ return h, sysStat(fi, h)
+ }
+ return h, nil
+}
diff --git a/misc/dist/stat_darwin.go b/misc/dist/stat_darwin.go
new file mode 100644
index 0000000000..eb3f76a27c
--- /dev/null
+++ b/misc/dist/stat_darwin.go
@@ -0,0 +1,32 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin
+
+package main
+
+import (
+ "archive/tar"
+ "os"
+ "syscall"
+ "time"
+)
+
+func init() {
+ sysStat = func(fi os.FileInfo, h *tar.Header) error {
+ sys, ok := fi.Sys().(*syscall.Stat_t)
+ if !ok {
+ return nil
+ }
+ h.Uid = int(sys.Uid)
+ h.Gid = int(sys.Gid)
+ // TODO(bradfitz): populate username & group. os/user
+ // doesn't cache LookupId lookups, and lacks group
+ // lookup functions.
+ h.AccessTime = time.Unix(sys.Atimespec.Unix())
+ h.ChangeTime = time.Unix(sys.Ctimespec.Unix())
+ // TODO(bradfitz): major/minor device numbers?
+ return nil
+ }
+}
diff --git a/misc/dist/stat_linux.go b/misc/dist/stat_linux.go
new file mode 100644
index 0000000000..0ddb8a3bfc
--- /dev/null
+++ b/misc/dist/stat_linux.go
@@ -0,0 +1,32 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+
+package main
+
+import (
+ "archive/tar"
+ "os"
+ "syscall"
+ "time"
+)
+
+func init() {
+ sysStat = func(fi os.FileInfo, h *tar.Header) error {
+ sys, ok := fi.Sys().(*syscall.Stat_t)
+ if !ok {
+ return nil
+ }
+ h.Uid = int(sys.Uid)
+ h.Gid = int(sys.Gid)
+ // TODO(bradfitz): populate username & group. os/user
+ // doesn't cache LookupId lookups, and lacks group
+ // lookup functions.
+ h.AccessTime = time.Unix(sys.Atim.Unix())
+ h.ChangeTime = time.Unix(sys.Ctim.Unix())
+ // TODO(bradfitz): major/minor device numbers?
+ return nil
+ }
+}