diff --git a/internal/safepath/join_linux.go b/internal/safepath/join_linux.go index ea87fcbe82..5ce393adf9 100644 --- a/internal/safepath/join_linux.go +++ b/internal/safepath/join_linux.go @@ -8,7 +8,7 @@ import ( "strconv" "github.com/containerd/log" - "github.com/moby/sys/mount" + "github.com/docker/docker/internal/unix_noeintr" "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -33,7 +33,7 @@ func Join(path, subpath string) (*SafePath, error) { return nil, err } - defer unix.Close(fd) + defer unix_noeintr.Close(fd) tmpMount, err := tempMountPoint(fd) if err != nil { @@ -47,7 +47,7 @@ func Join(path, subpath string) (*SafePath, error) { // TODO(vvoland): Investigate. mountSource := "/proc/" + pid + "/fd/" + strconv.Itoa(fd) - if err := unix.Mount(mountSource, tmpMount, "none", unix.MS_BIND, ""); err != nil { + if err := unix_noeintr.Mount(mountSource, tmpMount, "none", unix.MS_BIND, ""); err != nil { os.Remove(tmpMount) return nil, errors.Wrap(err, "failed to mount resolved path") } @@ -69,14 +69,14 @@ func Join(path, subpath string) (*SafePath, error) { // error was returned. func safeOpenFd(path, subpath string) (int, error) { // Open base volume path (_data directory). - prevFd, err := unix.Open(path, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0) + prevFd, err := unix_noeintr.Open(path, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0) if err != nil { return -1, &ErrNotAccessible{Path: path, Cause: err} } - defer unix.Close(prevFd) + defer unix_noeintr.Close(prevFd) // Try to use the Openat2 syscall first (available on Linux 5.6+). - fd, err := unix.Openat2(prevFd, subpath, &unix.OpenHow{ + fd, err := unix_noeintr.Openat2(prevFd, subpath, &unix.OpenHow{ Flags: unix.O_PATH | unix.O_CLOEXEC, Mode: 0, Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_MAGICLINKS | unix.RESOLVE_NO_SYMLINKS, @@ -106,7 +106,7 @@ func softOpenat2(baseFd int, subpath string) (int, error) { // point for the file descriptor. func tempMountPoint(sourceFd int) (string, error) { var stat unix.Stat_t - err := unix.Fstat(sourceFd, &stat) + err := unix_noeintr.Fstat(sourceFd, &stat) if err != nil { return "", errors.Wrap(err, "failed to Fstat mount source fd") } @@ -134,7 +134,7 @@ func cleanupSafePath(path string) func() error { return func() error { log.G(context.TODO()).WithField("path", path).Debug("removing safe temp mount") - if err := unix.Unmount(path, unix.MNT_DETACH); err != nil { + if err := unix_noeintr.Unmount(path, unix.MNT_DETACH); err != nil { if errors.Is(err, unix.EINVAL) { log.G(context.TODO()).WithField("path", path).Warn("safe temp mount no longer exists?") return nil diff --git a/internal/unix_noeintr/fs_unix.go b/internal/unix_noeintr/fs_unix.go new file mode 100644 index 0000000000..32c72d0041 --- /dev/null +++ b/internal/unix_noeintr/fs_unix.go @@ -0,0 +1,85 @@ +//go:build !windows + +// Wrappers for unix syscalls that retry on EINTR +// TODO: Consider moving (for example to moby/sys) and making the wrappers +// auto-generated. +package unix_noeintr + +import ( + "errors" + + "golang.org/x/sys/unix" +) + +func Retry(f func() error) { + for { + err := f() + if !errors.Is(err, unix.EINTR) { + return + } + } +} + +func Mount(source string, target string, fstype string, flags uintptr, data string) (err error) { + Retry(func() error { + err = unix.Mount(source, target, fstype, flags, data) + return err + }) + return +} + +func Unmount(target string, flags int) (err error) { + Retry(func() error { + err = unix.Unmount(target, flags) + return err + }) + return +} + +func Open(path string, mode int, perm uint32) (fd int, err error) { + Retry(func() error { + fd, err = unix.Open(path, mode, perm) + return err + }) + return +} + +func Close(fd int) (err error) { + Retry(func() error { + err = unix.Close(fd) + return err + }) + return +} + +func Openat(dirfd int, path string, mode int, perms uint32) (fd int, err error) { + Retry(func() error { + fd, err = unix.Openat(dirfd, path, mode, perms) + return err + }) + return +} + +func Openat2(dirfd int, path string, how *unix.OpenHow) (fd int, err error) { + Retry(func() error { + fd, err = unix.Openat2(dirfd, path, how) + return err + }) + return +} + +func Fstat(fd int, stat *unix.Stat_t) (err error) { + Retry(func() error { + err = unix.Fstat(fd, stat) + return err + }) + return +} + +func Fstatat(fd int, path string, stat *unix.Stat_t, flags int) (err error) { + Retry(func() error { + err = unix.Fstatat(fd, path, stat, flags) + return err + }) + return +}