From c405b58f3fa7988d42b5e5e46910344f342c5b45 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 12 Mar 2012 21:49:43 -0700 Subject: [PATCH] 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 --- misc/dist/bindist.go | 86 ++++++++++++++++++++++++++++++++-------- misc/dist/stat_darwin.go | 32 +++++++++++++++ misc/dist/stat_linux.go | 32 +++++++++++++++ 3 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 misc/dist/stat_darwin.go create mode 100644 misc/dist/stat_linux.go 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 + } +} -- 2.48.1