Skip to content

Commit

Permalink
fs.CopyDir: support sockets and pipes
Browse files Browse the repository at this point in the history
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
  • Loading branch information
AkihiroSuda committed Mar 30, 2022
1 parent cd17c99 commit ca52b93
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 73 deletions.
16 changes: 9 additions & 7 deletions fs/copy.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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")
}

0 comments on commit ca52b93

Please sign in to comment.