slimtoolkit/slim · error

source is symlink

Error message

source is symlink

What it means

CopyToVolume copies a host directory/file into a Docker volume via a helper container. Before archiving, it checks whether the resolved source path is a symlink with fsutil.IsSymlink and refuses to proceed. This guard exists because copying a symlinked source could pull in unintended target content or escape the expected copy root, so the library treats symlinks as unsafe inputs.

Source

Thrown at pkg/docker/dockerutil/dockerutil.go:574

		}

		err = dclient.RemoveContainer(removeOptions)
		if err != nil {
			fmt.Printf("dockerutil.CopyToVolume: dclient.RemoveContainer() error = %v\n", err)
		}
	}

	cleanSource, err := filepath.EvalSymlinks(source)
	if err != nil {
		log.Errorf("dockerutil.CopyToVolume: filepath.EvalSymlinks(%s) error = %v", source, err)
		rmContainer()
		return err
	}

	if fsutil.IsSymlink(cleanSource) {
		log.Errorf("dockerutil.CopyToVolume: source is a symlink = %s", cleanSource)
		rmContainer()
		return fmt.Errorf("source is symlink")
	}

	tarData, err := archive.Tar(cleanSource, archive.Uncompressed)
	if err != nil {
		log.Errorf("dockerutil.CopyToVolume: archive.Tar() error = %v", err)
		rmContainer()
		return err
	}

	targetPath := volumeBasePath
	if dstRootDir != "" {
		dirData, err := GenStateDirsTar(dstRootDir, dstTargetDir)
		if err != nil {
			log.Errorf("dockerutil.CopyToVolume: GenStateDirsTar() error = %v", err)
			rmContainer()
			return err
		}

View on GitHub (pinned to 81940d17fa)

Solutions

  1. Resolve the symlink first and pass the real path: use filepath.EvalSymlinks(cleanSource) and pass the resolved result to CopyToVolume.
  2. If symlink following is intended, copy the target content to a real directory (e.g. cp -rL) and use that directory as the source.
  3. If the symlink itself is the artifact to store, archive it manually and load it into the volume by other means instead of CopyToVolume.

Example fix

// before
err := dockerutil.CopyToVolume(ctx, "/tmp/mydata", volumeName)
// after
realPath, err := filepath.EvalSymlinks("/tmp/mydata")
if err != nil { return err }
err = dockerutil.CopyToVolume(ctx, realPath, volumeName)
Defensive patterns

Strategy: validation

Validate before calling

import "path/filepath"

func isSymlink(p string) bool {
	fi, err := os.Lstat(p)
	return err == nil && fi.Mode()&os.ModeSymlink != 0
}

func resolveSource(p string) (string, error) {
	if isSymlink(p) {
		return filepath.EvalSymlinks(p)
	}
	return p, nil
}

real, err := resolveSource(source)
if err != nil { return err }
err = dockerutil.CopyToVolume(ctx, real, volume)

Type guard

func isSymlinkPath(p string) bool {
	fi, err := os.Lstat(p)
	return err == nil && fi.Mode()&os.ModeSymlink != 0
}

Try / catch

if _, err := dockerutil.CopyToVolume(ctx, src, vol); err != nil {
	if strings.Contains(err.Error(), "source is symlink") {
		real, rerr := filepath.EvalSymlinks(src)
		if rerr != nil { return rerr }
		return dockerutil.CopyToVolume(ctx, real, vol)
	}
	return err
}

Prevention

When it happens

Trigger: Calling dockerutil.CopyToVolume (directly or via DoArchiveState / CreateVolumeWithData) with a source path that resolves to a symlink, e.g. passing /var/run/docker.sock, a /tmp symlinked directory, or a user-supplied path that is a soft link.

Common situations: macOS/OSX temp dirs (/tmp -> /private/tmp) or user home paths that are symlinks; passing paths like /var/data/current that admins implemented as symlink switches; passing socket or device files that are symlinks into /proc or /run.

Related errors


AI-assisted analysis of slimtoolkit/slim@81940d17fa (2026-08-31). Data as JSON: /api/errors/a5658be2a2e33888. Report an issue: GitHub.