From dd84b9d64fb98746a230cd24233ff50a562c39c9 Mon Sep 17 00:00:00 2001 From: 简律纯 Date: Fri, 28 Apr 2023 01:36:44 +0800 Subject: --- cli/internal/tarpatch/tar.go | 92 ++++++++++++++++++++++++++++++++++++ cli/internal/tarpatch/tar_unix.go | 42 ++++++++++++++++ cli/internal/tarpatch/tar_windows.go | 27 +++++++++++ 3 files changed, 161 insertions(+) create mode 100644 cli/internal/tarpatch/tar.go create mode 100644 cli/internal/tarpatch/tar_unix.go create mode 100644 cli/internal/tarpatch/tar_windows.go (limited to 'cli/internal/tarpatch') diff --git a/cli/internal/tarpatch/tar.go b/cli/internal/tarpatch/tar.go new file mode 100644 index 0000000..a4dab23 --- /dev/null +++ b/cli/internal/tarpatch/tar.go @@ -0,0 +1,92 @@ +// Adapted from https://github.com/moby/moby/blob/924edb948c2731df3b77697a8fcc85da3f6eef57/pkg/archive/archive.go +// Copyright Docker, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Package tarpatch addresses an issue with stdlib throwing an error in some environments. +package tarpatch + +import ( + "archive/tar" + "io/fs" + "os" + "strings" + "time" + + "github.com/vercel/turbo/cli/internal/turbopath" +) + +// nosysFileInfo hides the system-dependent info of the wrapped FileInfo to +// prevent tar.FileInfoHeader from introspecting it and potentially calling into +// glibc. +type nosysFileInfo struct { + os.FileInfo +} + +func (fi nosysFileInfo) Sys() interface{} { + // A Sys value of type *tar.Header is safe as it is system-independent. + // The tar.FileInfoHeader function copies the fields into the returned + // header without performing any OS lookups. + if sys, ok := fi.FileInfo.Sys().(*tar.Header); ok { + return sys + } + return nil +} + +// FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi. +// +// Compared to the archive/tar.FileInfoHeader function, this function is safe to +// call from a chrooted process as it does not populate fields which would +// require operating system lookups. It behaves identically to +// tar.FileInfoHeader when fi is a FileInfo value returned from +// tar.Header.FileInfo(). +// +// When fi is a FileInfo for a native file, such as returned from os.Stat() and +// os.Lstat(), the returned Header value differs from one returned from +// tar.FileInfoHeader in the following ways. The Uname and Gname fields are not +// set as OS lookups would be required to populate them. The AccessTime and +// ChangeTime fields are not currently set (not yet implemented) although that +// is subject to change. Callers which require the AccessTime or ChangeTime +// fields to be zeroed should explicitly zero them out in the returned Header +// value to avoid any compatibility issues in the future. +func FileInfoHeaderNoLookups(fi fs.FileInfo, link string) (*tar.Header, error) { + hdr, err := tar.FileInfoHeader(nosysFileInfo{fi}, link) + if err != nil { + return nil, err + } + return hdr, sysStat(fi, hdr) +} + +// FileInfoHeader creates a populated Header from fi. +// +// Compared to the archive/tar package, this function fills in less information +// but is safe to call from a chrooted process. The AccessTime and ChangeTime +// fields are not set in the returned header, ModTime is truncated to one-second +// precision, and the Uname and Gname fields are only set when fi is a FileInfo +// value returned from tar.Header.FileInfo(). +func FileInfoHeader(fullPath turbopath.AnchoredUnixPath, fileInfo fs.FileInfo, link string) (*tar.Header, error) { + hdr, err := FileInfoHeaderNoLookups(fileInfo, link) + if err != nil { + return nil, err + } + hdr.Format = tar.FormatPAX + hdr.ModTime = hdr.ModTime.Truncate(time.Second) + hdr.AccessTime = time.Time{} + hdr.ChangeTime = time.Time{} + hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) + hdr.Name = canonicalTarName(fullPath, fileInfo.IsDir()) + return hdr, nil +} + +// canonicalTarName provides a platform-independent and consistent posix-style +// path for files and directories to be archived regardless of the platform. +func canonicalTarName(fullPath turbopath.AnchoredUnixPath, isDir bool) string { + nameString := fullPath.ToString() + if isDir { + // Append '/' if not already present. + if !strings.HasSuffix(nameString, "/") { + nameString += "/" + } + } + + return nameString +} diff --git a/cli/internal/tarpatch/tar_unix.go b/cli/internal/tarpatch/tar_unix.go new file mode 100644 index 0000000..3020c0e --- /dev/null +++ b/cli/internal/tarpatch/tar_unix.go @@ -0,0 +1,42 @@ +//go:build !windows +// +build !windows + +// Adapted from https://github.com/moby/moby/blob/924edb948c2731df3b77697a8fcc85da3f6eef57/pkg/archive/archive_unix.go +// Copyright Docker, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package tarpatch + +import ( + "archive/tar" + "os" + "syscall" + + "golang.org/x/sys/unix" +) + +// chmodTarEntry is used to adjust the file permissions used in tar header based +// on the platform the archival is done. +func chmodTarEntry(perm os.FileMode) os.FileMode { + return perm // noop for unix as golang APIs provide perm bits correctly +} + +// sysStat populates hdr from system-dependent fields of fi without performing +// any OS lookups. +func sysStat(fi os.FileInfo, hdr *tar.Header) error { + s, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return nil + } + + hdr.Uid = int(s.Uid) + hdr.Gid = int(s.Gid) + + if s.Mode&unix.S_IFBLK != 0 || + s.Mode&unix.S_IFCHR != 0 { + hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) //nolint: unconvert + hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) //nolint: unconvert + } + + return nil +} diff --git a/cli/internal/tarpatch/tar_windows.go b/cli/internal/tarpatch/tar_windows.go new file mode 100644 index 0000000..486e6fd --- /dev/null +++ b/cli/internal/tarpatch/tar_windows.go @@ -0,0 +1,27 @@ +//go:build windows +// +build windows + +// Adapted from https://github.com/moby/moby/blob/924edb948c2731df3b77697a8fcc85da3f6eef57/pkg/archive/archive_windows.go +// Copyright Docker, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package tarpatch + +import ( + "archive/tar" + "os" +) + +// chmodTarEntry is used to adjust the file permissions used in tar header based +// on the platform the archival is done. +func chmodTarEntry(perm os.FileMode) os.FileMode { + // Remove group- and world-writable bits. + perm &= 0o755 + + // Add the x bit: make everything +x on Windows + return perm | 0o111 +} + +func sysStat(fi os.FileInfo, hdr *tar.Header) error { + return nil +} -- cgit v1.2.3-70-g09d2