]> Cypherpunks repositories - gostls13.git/commitdiff
misc/dist: better archive/tar Headers
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 13 Mar 2012 04:49:43 +0000 (21:49 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 13 Mar 2012 04:49:43 +0000 (21:49 -0700)
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

misc/dist/bindist.go
misc/dist/stat_darwin.go [new file with mode: 0644]
misc/dist/stat_linux.go [new file with mode: 0644]

index 0d2fa21ea6ee24b444d3082bf60e7c30cb25b932..9ffa028fae75833d5f47d7801560a4a7799ba4a5 100644 (file)
@@ -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 (file)
index 0000000..eb3f76a
--- /dev/null
@@ -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 (file)
index 0000000..0ddb8a3
--- /dev/null
@@ -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
+       }
+}