From d12d56e47ed7d6a8047fda66c17e6f54ef7f3b53 Mon Sep 17 00:00:00 2001 From: Mateusz Kwiatkowski Date: Sat, 16 Jan 2021 20:42:54 +0100 Subject: [PATCH] Fix FreeBSD suport Ref https://github.com/containers/skopeo/issues/1163 --- drivers/driver_freebsd.go | 4 +- pkg/archive/archive_freebsd.go | 126 +++++++++++++++++++++++++++++++ pkg/archive/archive_unix.go | 2 +- pkg/homedir/homedir_others.go | 2 +- pkg/mount/mounter_unsupported.go | 2 +- pkg/system/mknod.go | 2 +- pkg/system/mknod_freebsd.go | 22 ++++++ 7 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 pkg/archive/archive_freebsd.go create mode 100644 pkg/system/mknod_freebsd.go diff --git a/drivers/driver_freebsd.go b/drivers/driver_freebsd.go index 53394b738..5cba100c8 100644 --- a/drivers/driver_freebsd.go +++ b/drivers/driver_freebsd.go @@ -2,8 +2,6 @@ package graphdriver import ( "syscall" - - "golang.org/x/sys/unix" ) var ( @@ -15,7 +13,7 @@ var ( // Mounted checks if the given path is mounted as the fs type func Mounted(fsType FsMagic, mountPath string) (bool, error) { - var buf unix.Statfs_t + var buf syscall.Statfs_t if err := syscall.Statfs(mountPath, &buf); err != nil { return false, err } diff --git a/pkg/archive/archive_freebsd.go b/pkg/archive/archive_freebsd.go new file mode 100644 index 000000000..de1e2c450 --- /dev/null +++ b/pkg/archive/archive_freebsd.go @@ -0,0 +1,126 @@ +// +build freebsd + +package archive + +import ( + "archive/tar" + "errors" + "os" + "path/filepath" + "syscall" + + "github.com/containers/storage/pkg/idtools" + "github.com/containers/storage/pkg/system" + rsystem "github.com/opencontainers/runc/libcontainer/system" + "golang.org/x/sys/unix" +) + +// fixVolumePathPrefix does platform specific processing to ensure that if +// the path being passed in is not in a volume path format, convert it to one. +func fixVolumePathPrefix(srcPath string) string { + return srcPath +} + +// getWalkRoot calculates the root path when performing a TarWithOptions. +// We use a separate function as this is platform specific. On Linux, we +// can't use filepath.Join(srcPath,include) because this will clean away +// a trailing "." or "/" which may be important. +func getWalkRoot(srcPath string, include string) string { + return srcPath + string(filepath.Separator) + include +} + +// CanonicalTarNameForPath returns platform-specific filepath +// to canonical posix-style path for tar archival. p is relative +// path. +func CanonicalTarNameForPath(p string) (string, error) { + return p, nil // already unix-style +} + +// 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 +} + +func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) { + s, ok := stat.(*syscall.Stat_t) + + if ok { + // Currently go does not fill in the major/minors + if s.Mode&unix.S_IFBLK != 0 || + s.Mode&unix.S_IFCHR != 0 { + hdr.Devmajor = int64(major(uint64(s.Rdev))) // nolint: unconvert + hdr.Devminor = int64(minor(uint64(s.Rdev))) // nolint: unconvert + } + } + + return +} + +func getInodeFromStat(stat interface{}) (inode uint64, err error) { + s, ok := stat.(*syscall.Stat_t) + + if ok { + inode = s.Ino + } + + return +} + +func getFileUIDGID(stat interface{}) (idtools.IDPair, error) { + s, ok := stat.(*syscall.Stat_t) + + if !ok { + return idtools.IDPair{}, errors.New("cannot convert stat value to syscall.Stat_t") + } + return idtools.IDPair{UID: int(s.Uid), GID: int(s.Gid)}, nil +} + +func major(device uint64) uint64 { + return (device >> 8) & 0xfff +} + +func minor(device uint64) uint64 { + return (device & 0xff) | ((device >> 12) & 0xfff00) +} + +// handleTarTypeBlockCharFifo is an OS-specific helper function used by +// createTarFile to handle the following types of header: Block; Char; Fifo +func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { + if rsystem.RunningInUserNS() { + // cannot create a device if running in user namespace + return nil + } + + mode := uint32(hdr.Mode & 07777) + switch hdr.Typeflag { + case tar.TypeBlock: + mode |= unix.S_IFBLK + case tar.TypeChar: + mode |= unix.S_IFCHR + case tar.TypeFifo: + mode |= unix.S_IFIFO + } + + return system.Mknod(path, mode, uint64(system.Mkdev(hdr.Devmajor, hdr.Devminor))) +} + +func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo, forceMask *os.FileMode) error { + permissionsMask := hdrInfo.Mode() + if forceMask != nil { + permissionsMask = *forceMask + } + if hdr.Typeflag == tar.TypeLink { + if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { + if err := os.Chmod(path, permissionsMask); err != nil { + return err + } + } + } else if hdr.Typeflag != tar.TypeSymlink { + if err := os.Chmod(path, permissionsMask); err != nil { + return err + } + } + return nil +} diff --git a/pkg/archive/archive_unix.go b/pkg/archive/archive_unix.go index ecb704b64..5438700ab 100644 --- a/pkg/archive/archive_unix.go +++ b/pkg/archive/archive_unix.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!freebsd package archive diff --git a/pkg/homedir/homedir_others.go b/pkg/homedir/homedir_others.go index 4f778c858..607c5feb5 100644 --- a/pkg/homedir/homedir_others.go +++ b/pkg/homedir/homedir_others.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin +// +build !linux,!darwin,!freebsd package homedir diff --git a/pkg/mount/mounter_unsupported.go b/pkg/mount/mounter_unsupported.go index 42d1d422c..9d20cfbf8 100644 --- a/pkg/mount/mounter_unsupported.go +++ b/pkg/mount/mounter_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !linux,!freebsd package mount diff --git a/pkg/system/mknod.go b/pkg/system/mknod.go index af79a6538..c276ce8e8 100644 --- a/pkg/system/mknod.go +++ b/pkg/system/mknod.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!freebsd package system diff --git a/pkg/system/mknod_freebsd.go b/pkg/system/mknod_freebsd.go new file mode 100644 index 000000000..d09005589 --- /dev/null +++ b/pkg/system/mknod_freebsd.go @@ -0,0 +1,22 @@ +// +build freebsd + +package system + +import ( + "golang.org/x/sys/unix" +) + +// Mknod creates a filesystem node (file, device special file or named pipe) named path +// with attributes specified by mode and dev. +func Mknod(path string, mode uint32, dev uint64) error { + return unix.Mknod(path, mode, dev) +} + +// Mkdev is used to build the value of linux devices (in /dev/) which specifies major +// and minor number of the newly created device special file. +// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. +// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major, +// then the top 12 bits of the minor. +func Mkdev(major int64, minor int64) uint32 { + return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff)) +}