Repository: moby/moby
File: daemon/archive_windows.go (Go)

Code before:

	// This will evaluate the last path element if it is a symlink.
	resolvedPath, err := container.GetResourcePath(absPath)
	if err != nil {
		return err
	}

	stat, err := os.Lstat(resolvedPath)
	if err != nil {
		return err
	}

	if !stat.IsDir() {
		return errdefs.InvalidParameter(errors.New("extraction point is not a directory"))
	}

	// Need to check if the path is in a volume. If it is, it cannot be in a
	// read-only volume. If it is not in a volume, the container cannot be
	// configured with a read-only rootfs.

	// Use the resolved path relative to the container rootfs as the new
	// absPath. This way we fully follow any symlinks in a volume that may
	// lead back outside the volume.
	//
	// The Windows implementation of filepath.Rel in golang 1.4 does not
	// support volume style file path semantics. On Windows when using the
	// filter driver, we are guaranteed that the path will always be
	// a volume file path.
	var baseRel string
	if strings.HasPrefix(resolvedPath, `\\?\Volume{`) {
		if strings.HasPrefix(resolvedPath, container.BaseFS) {
			baseRel = resolvedPath[len(container.BaseFS):]
			if baseRel[:1] == `\` {
				baseRel = baseRel[1:]
			}
		}
	} else {
		baseRel, err = filepath.Rel(container.BaseFS, resolvedPath)
		if err != nil {
			return err
		}
	}

	// TODO(thaJeztah): add check for read-only path once supported on windows
	//
	// - e5261d6e4a1e96d4c0fa4b4480042046b695eda1 added "FIXME Post-TP4 / TP5"; check whether this would be possible to implement on Windows.
	// - e5261d6e4a1e96d4c0fa4b4480042046b695eda1 added "or extracting to a mount point inside a volume"; check whether this is is still true, and adjust this check accordingly
	//
	// This comment is left in-place as a reminder :)
	//
	// absPath = filepath.Join(string(filepath.Separator), baseRel)

Review comment (suggestion):
IMO, we should also persist the `if container.HostConfig.ReadonlyRootfs` and the `container rootfs is marked read-only` error in the TODO comment.

Otherwise we lose the knowledge about part of the expected behavior described in the godoc of the removed `checkIfPathIsInAVolume` function:
> // checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
> // cannot be in a read-only volume. If it  is not in a volume, the container
> // cannot be configured with a read-only rootfs.

Code after:
	// This will evaluate the last path element if it is a symlink.
	resolvedPath, err := container.GetResourcePath(absPath)
	if err != nil {
		return err
	}

	stat, err := os.Lstat(resolvedPath)
	if err != nil {
		return err
	}

	if !stat.IsDir() {
		return errdefs.InvalidParameter(errors.New("extraction point is not a directory"))
	}

	// TODO(thaJeztah): add check for writable path once windows supports read-only rootFS and (read-only) volumes during copy
	//
	// - e5261d6e4a1e96d4c0fa4b4480042046b695eda1 added "FIXME Post-TP4 / TP5"; check whether this would be possible to implement on Windows.
	// - e5261d6e4a1e96d4c0fa4b4480042046b695eda1 added "or extracting to a mount point inside a volume"; check whether this is is still true, and adjust this check accordingly
	//
	// This comment is left in-place as a reminder :)
	//
	// absPath = filepath.Join(string(filepath.Separator), baseRel)
	// if err := checkWritablePath(container, absPath); err != nil {
	// 	return err
	// }

	options := daemon.defaultTarCopyOptions(noOverwriteDirNonDir)
	if err := chrootarchive.UntarWithRoot(content, resolvedPath, options, container.BaseFS); err != nil {
		return err
	}

	daemon.LogContainerEvent(container, events.ActionExtractToDir)

	return nil
}

func (daemon *Daemon) containerCopy(container *container.Container, resource string) (rc io.ReadCloser, err error) {
	if resource[0] == '/' || resource[0] == '\\' {
		resource = resource[1:]
	}
	container.Lock()

	defer func() {
		if err != nil {
			// Wait to unlock the container until the archive is fully read
			// (see the ReadCloseWrapper func below) or if there is an error
			// before that occurs.
			container.Unlock()
		}
	}()