Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs.CopyDir: support sockets and pipes #197

Merged
merged 1 commit into from Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 9 additions & 7 deletions fs/copy.go
Expand Up @@ -22,6 +22,8 @@ import (
"os"
"path/filepath"
"sync"

"github.com/sirupsen/logrus"
)

var bufferPool = &sync.Pool{
Expand Down Expand Up @@ -152,15 +154,15 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
if err := os.Symlink(link, target); err != nil {
return fmt.Errorf("failed to create symlink: %s: %w", target, err)
}
case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
if err := copyDevice(target, fi); err != nil {
return fmt.Errorf("failed to create device: %w", err)
case (fi.Mode() & os.ModeDevice) == os.ModeDevice,
(fi.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe,
(fi.Mode() & os.ModeSocket) == os.ModeSocket:
if err := copyIrregular(target, fi); err != nil {
return fmt.Errorf("failed to create irregular file: %w", err)
}
default:
// TODO: Support pipes and sockets
if err != nil {
return fmt.Errorf("unsupported mode %s: %w", fi.Mode(), err)
}
logrus.Warnf("unsupported mode: %s: %s", source, fi.Mode())
continue
}

if err := copyFileInfo(fi, source, target); err != nil {
Expand Down
36 changes: 0 additions & 36 deletions fs/copy_freebsd.go

This file was deleted.

20 changes: 10 additions & 10 deletions fs/copy_darwin.go → fs/copy_irregular_freebsd.go
@@ -1,6 +1,3 @@
//go:build darwin
// +build darwin

/*
Copyright The containerd Authors.
Expand All @@ -20,17 +17,20 @@
package fs

import (
"errors"
"fmt"
"os"
"syscall"

"golang.org/x/sys/unix"
)

func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
// copyIrregular covers devices, pipes, and sockets
func copyIrregular(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t) // not *unix.Stat_t
if !ok {
return errors.New("unsupported stat type")
return fmt.Errorf("unsupported stat type: %s: %v", dst, fi.Mode())
}
var rDev uint64 // uint64 on FreeBSD, int on other unixen
if fi.Mode()&os.ModeDevice == os.ModeDevice {
rDev = st.Rdev
}
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
return syscall.Mknod(dst, uint32(st.Mode), rDev)
}
22 changes: 13 additions & 9 deletions fs/copy_device_unix.go → fs/copy_irregular_unix.go
@@ -1,5 +1,5 @@
//go:build openbsd || solaris || netbsd
// +build openbsd solaris netbsd
//go:build !windows && !freebsd
// +build !windows,!freebsd

/*
Copyright The containerd Authors.
Expand All @@ -20,17 +20,21 @@
package fs

import (
"errors"
"fmt"
"os"
"syscall"

"golang.org/x/sys/unix"
)

func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
// copyIrregular covers devices, pipes, and sockets
func copyIrregular(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t) // not *unix.Stat_t
if !ok {
return errors.New("unsupported stat type")
return fmt.Errorf("unsupported stat type: %s: %v", dst, fi.Mode())
}
var rDev int
if fi.Mode()&os.ModeDevice == os.ModeDevice {
rDev = int(st.Rdev)
}
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
//nolint:unconvert
return syscall.Mknod(dst, uint32(st.Mode), rDev)
}
9 changes: 0 additions & 9 deletions fs/copy_linux.go
Expand Up @@ -17,7 +17,6 @@
package fs

import (
"errors"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -144,11 +143,3 @@ func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAtt

return nil
}

func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
}
88 changes: 88 additions & 0 deletions fs/copy_unix_test.go
Expand Up @@ -22,10 +22,13 @@ package fs
import (
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"

"github.com/containerd/continuity/fs/fstest"
"github.com/containerd/continuity/sysx"
"golang.org/x/sys/unix"
)

func assertXAttr(t *testing.T, dir, xattr, xval string, xerr error) {
Expand Down Expand Up @@ -86,3 +89,88 @@ func TestCopyDirWithXAttrExcludes(t *testing.T) {
assertXAttr(t, dst, "user.test-x", "", sysx.ENODATA)
})
}

func TestCopyIrregular(t *testing.T) {
var prepared int
prepareSrc := func(src string) {
f0Pipe := filepath.Join(src, "f0.pipe")
if err := unix.Mkfifo(f0Pipe, 0600); err != nil {
t.Fatal(err)
}
prepared++
f1Normal := filepath.Join(src, "f1.normal")
if err := ioutil.WriteFile(f1Normal, []byte("content of f1.normal"), 0600); err != nil {
t.Fatal(err)
}
prepared++
f2Socket := filepath.Join(src, "f2.sock")
if err := unix.Mknod(f2Socket, 0600|unix.S_IFSOCK, 0); err != nil {
t.Fatal(err)
}
prepared++
f3Dev := filepath.Join(src, "f3.dev")
if err := unix.Mknod(f3Dev, 0600|unix.S_IFCHR, 42); err != nil {
t.Logf("skipping testing S_IFCHR: %v", err)
} else {
prepared++
}
}

verifyDst := func(dst string) {
entries, err := ioutil.ReadDir(dst)
if err != nil {
t.Fatal(err)
}
var verified int
for _, f := range entries {
name := f.Name()
full := filepath.Join(dst, name)
fi, err := os.Stat(full)
if err != nil {
t.Fatal(err)
}
mode := fi.Mode()
switch name {
case "f0.pipe":
if mode&os.ModeNamedPipe != os.ModeNamedPipe {
t.Fatalf("unexpected mode of %s: %v", name, mode)
}
case "f1.normal":
b, err := ioutil.ReadFile(full)
if err != nil {
t.Fatal(err)
}
if string(b) != "content of f1.normal" {
t.Fatalf("unexpected content of %s: %q", name, string(b))
}
case "f2.sock":
if mode&os.ModeSocket != os.ModeSocket {
t.Fatalf("unexpected mode of %s: %v", name, mode)
}
case "f3.dev":
if mode&os.ModeDevice != os.ModeDevice {
t.Fatalf("unexpected mode of %s: %v", name, mode)
}
sys, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
t.Fatalf("unexpected type: %v", fi.Sys())
}
if sys.Rdev != 42 {
t.Fatalf("unexpected rdev of %s: %d", name, sys.Rdev)
}
}
verified++
}
if verified != prepared {
t.Fatalf("prepared %d files, verified %d files", prepared, verified)
}
}

src := t.TempDir()
dst := t.TempDir()
prepareSrc(src)
if err := CopyDir(dst, src); err != nil {
t.Fatal(err)
}
verifyDst(dst)
}
4 changes: 2 additions & 2 deletions fs/copy_windows.go
Expand Up @@ -85,6 +85,6 @@ func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAtt
return nil
}

func copyDevice(dst string, fi os.FileInfo) error {
return errors.New("device copy not supported")
func copyIrregular(dst string, fi os.FileInfo) error {
return errors.New("irregular copy not supported")
}