From 53d22c779c5d2df5ccda5a8e23db0501a0dadf44 Mon Sep 17 00:00:00 2001
From: Matthew Heon <matthew.heon@pm.me>
Date: Wed, 3 Mar 2021 15:30:52 -0500
Subject: [PATCH] Compat API: create volume source dirs on the host

It took a lot to figure out exactly how this should work, but I
think I finally have it. My initial versions of this created the
directory with the same owner as the user the container was run
with, which was rather complicated - but after review against
Docker, I have determined that is incorrect, and it's always made
as root:root 0755 (Ubuntu's Docker, which I was using to try and
test, is a snap - and as such it was sandboxed, and not actually
placing directories it made in a place I could find?). This makes
things much easier, since I just need to parse out source
directories for binds and ensure they exist.

Fixes #9510

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
---
 cmd/podman/common/create_opts.go | 17 +++++++++++++++++
 test/apiv2/44-mounts.at          |  2 +-
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index f945c9c54a..03cd8a7213 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -3,6 +3,7 @@ package common
 import (
 	"fmt"
 	"net"
+	"os"
 	"path/filepath"
 	"strconv"
 	"strings"
@@ -13,6 +14,7 @@ import (
 	"github.com/containers/podman/v3/pkg/domain/entities"
 	"github.com/containers/podman/v3/pkg/rootless"
 	"github.com/containers/podman/v3/pkg/specgen"
+	"github.com/pkg/errors"
 )
 
 type ContainerCLIOpts struct {
@@ -397,6 +399,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
 	}
 
 	// volumes
+	volSources := make(map[string]bool)
 	volDestinations := make(map[string]bool)
 	for _, vol := range cc.HostConfig.Binds {
 		cliOpts.Volume = append(cliOpts.Volume, vol)
@@ -407,6 +410,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
 		case 1:
 			volDestinations[vol] = true
 		default:
+			volSources[splitVol[0]] = true
 			volDestinations[splitVol[1]] = true
 		}
 	}
@@ -421,6 +425,19 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
 		}
 		cliOpts.Volume = append(cliOpts.Volume, vol)
 	}
+	// Make mount points for compat volumes
+	for vol := range volSources {
+		// This might be a named volume.
+		// Assume it is if it's not an absolute path.
+		if !filepath.IsAbs(vol) {
+			continue
+		}
+		if err := os.MkdirAll(vol, 0755); err != nil {
+			if !os.IsExist(err) {
+				return nil, nil, errors.Wrapf(err, "error making volume mountpoint for volume %s", vol)
+			}
+		}
+	}
 	if len(cc.HostConfig.BlkioWeightDevice) > 0 {
 		devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice))
 		for _, d := range cc.HostConfig.BlkioWeightDevice {
diff --git a/test/apiv2/44-mounts.at b/test/apiv2/44-mounts.at
index fe202576d4..5dc560852e 100644
--- a/test/apiv2/44-mounts.at
+++ b/test/apiv2/44-mounts.at
@@ -4,7 +4,7 @@ podman pull $IMAGE &>/dev/null
 
 # Test various HostConfig options
 tmpfs_name="/mytmpfs"
-t POST containers/create?name=hostconfig_test '"Image":"'$IMAGE'","Cmd":["df"],"HostConfig":{"TmpFs":{"'$tmpfs_name'":"rw"}}' 201 \
+t POST containers/create?name=hostconfig_test '"Image":"'$IMAGE'","Cmd":["df"],"HostConfig":{"Binds":["/tmp/doesnotexist:/test1"],"TmpFs":{"'$tmpfs_name'":"rw"}}' 201 \
   .Id~[0-9a-f]\\{64\\}
 cid=$(jq -r '.Id' <<<"$output")