Merge pull request #33236 from cpuguy83/17.03.2_cherry_picks

17.03.2 cherry picks
diff --git a/cli/command/system/info.go b/cli/command/system/info.go
index 94a7aad..ea4af76 100644
--- a/cli/command/system/info.go
+++ b/cli/command/system/info.go
@@ -6,8 +6,6 @@
 	"strings"
 	"time"
 
-	"golang.org/x/net/context"
-
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/cli"
@@ -17,6 +15,7 @@
 	"github.com/docker/docker/utils/templates"
 	"github.com/docker/go-units"
 	"github.com/spf13/cobra"
+	"golang.org/x/net/context"
 )
 
 type infoOptions struct {
@@ -66,11 +65,6 @@
 	if info.DriverStatus != nil {
 		for _, pair := range info.DriverStatus {
 			fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1])
-
-			// print a warning if devicemapper is using a loopback file
-			if pair[0] == "Data loop file" {
-				fmt.Fprintln(dockerCli.Err(), " WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.")
-			}
 		}
 
 	}
@@ -228,43 +222,6 @@
 		fmt.Fprintf(dockerCli.Out(), "Registry: %v\n", info.IndexServerAddress)
 	}
 
-	// Only output these warnings if the server does not support these features
-	if info.OSType != "windows" {
-		if !info.MemoryLimit {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: No memory limit support")
-		}
-		if !info.SwapLimit {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: No swap limit support")
-		}
-		if !info.KernelMemory {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: No kernel memory limit support")
-		}
-		if !info.OomKillDisable {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: No oom kill disable support")
-		}
-		if !info.CPUCfsQuota {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs quota support")
-		}
-		if !info.CPUCfsPeriod {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs period support")
-		}
-		if !info.CPUShares {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu shares support")
-		}
-		if !info.CPUSet {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: No cpuset support")
-		}
-		if !info.IPv4Forwarding {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: IPv4 forwarding is disabled")
-		}
-		if !info.BridgeNfIptables {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-iptables is disabled")
-		}
-		if !info.BridgeNfIP6tables {
-			fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-ip6tables is disabled")
-		}
-	}
-
 	if info.Labels != nil {
 		fmt.Fprintln(dockerCli.Out(), "Labels:")
 		for _, attribute := range info.Labels {
@@ -317,11 +274,85 @@
 		}
 	}
 
-	fmt.Fprintf(dockerCli.Out(), "Live Restore Enabled: %v\n", info.LiveRestoreEnabled)
+	fmt.Fprintf(dockerCli.Out(), "Live Restore Enabled: %v\n\n", info.LiveRestoreEnabled)
+
+	// Only output these warnings if the server does not support these features
+	if info.OSType != "windows" {
+		printStorageDriverWarnings(dockerCli, info)
+
+		if !info.MemoryLimit {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: No memory limit support")
+		}
+		if !info.SwapLimit {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: No swap limit support")
+		}
+		if !info.KernelMemory {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: No kernel memory limit support")
+		}
+		if !info.OomKillDisable {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: No oom kill disable support")
+		}
+		if !info.CPUCfsQuota {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs quota support")
+		}
+		if !info.CPUCfsPeriod {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs period support")
+		}
+		if !info.CPUShares {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu shares support")
+		}
+		if !info.CPUSet {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: No cpuset support")
+		}
+		if !info.IPv4Forwarding {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: IPv4 forwarding is disabled")
+		}
+		if !info.BridgeNfIptables {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-iptables is disabled")
+		}
+		if !info.BridgeNfIP6tables {
+			fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-ip6tables is disabled")
+		}
+	}
 
 	return nil
 }
 
+func printStorageDriverWarnings(dockerCli *command.DockerCli, info types.Info) {
+	if info.DriverStatus == nil {
+		return
+	}
+
+	for _, pair := range info.DriverStatus {
+		if pair[0] == "Data loop file" {
+			fmt.Fprintf(dockerCli.Err(), "WARNING: %s: usage of loopback devices is strongly discouraged for production use.\n         Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.\n", info.Driver)
+		}
+		if pair[0] == "Supports d_type" && pair[1] == "false" {
+			backingFs := getBackingFs(info)
+
+			msg := fmt.Sprintf("WARNING: %s: the backing %s filesystem is formatted without d_type support, which leads to incorrect behavior.\n", info.Driver, backingFs)
+			if backingFs == "xfs" {
+				msg += "         Reformat the filesystem with ftype=1 to enable d_type support.\n"
+			}
+			msg += "         Running without d_type support will not be supported in future releases."
+			fmt.Fprintln(dockerCli.Err(), msg)
+		}
+	}
+}
+
+func getBackingFs(info types.Info) string {
+	if info.DriverStatus == nil {
+		return ""
+	}
+
+	for _, pair := range info.DriverStatus {
+		if pair[0] == "Backing Filesystem" {
+			return pair[1]
+		}
+	}
+	return ""
+}
+
 func formatInfo(dockerCli *command.DockerCli, info types.Info, format string) error {
 	tmpl, err := templates.Parse(format)
 	if err != nil {
diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go
index 2296045..68cb072 100644
--- a/daemon/container_operations_unix.go
+++ b/daemon/container_operations_unix.go
@@ -213,6 +213,8 @@
 		}
 	}
 
+	label.Relabel(localMountPath, c.MountLabel, false)
+
 	// remount secrets ro
 	if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil {
 		return errors.Wrap(err, "unable to remount secret dir as readonly")
diff --git a/daemon/volumes.go b/daemon/volumes.go
index f5ae284..cb7591e 100644
--- a/daemon/volumes.go
+++ b/daemon/volumes.go
@@ -16,7 +16,6 @@
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/volume"
 	"github.com/docker/docker/volume/drivers"
-	"github.com/opencontainers/runc/libcontainer/label"
 )
 
 var (
@@ -195,9 +194,6 @@
 				return err
 			}
 
-			if err := label.Relabel(mp.Source, container.MountLabel, false); err != nil {
-				return err
-			}
 			mp.Volume = v
 			mp.Name = v.Name()
 			mp.Driver = v.DriverName()
diff --git a/plugin/backend_linux.go b/plugin/backend_linux.go
index 33200d8..a5a3f9b 100644
--- a/plugin/backend_linux.go
+++ b/plugin/backend_linux.go
@@ -575,7 +575,7 @@
 func getMounts(root string) ([]string, error) {
 	infos, err := mount.GetMounts()
 	if err != nil {
-		return nil, errors.Wrap(err, "failed to read mount table while performing recursive unmount")
+		return nil, errors.Wrap(err, "failed to read mount table")
 	}
 
 	var mounts []string
diff --git a/plugin/manager_linux.go b/plugin/manager_linux.go
index ad66616..7e734b7 100644
--- a/plugin/manager_linux.go
+++ b/plugin/manager_linux.go
@@ -171,9 +171,17 @@
 
 	pdir := filepath.Join(pm.config.Root, p.PluginObj.ID)
 	orig := filepath.Join(pdir, "rootfs")
+
+	// Make sure nothing is mounted
+	// This could happen if the plugin was disabled with `-f` with active mounts.
+	// If there is anything in `orig` is still mounted, this should error out.
+	if err := recursiveUnmount(orig); err != nil {
+		return err
+	}
+
 	backup := orig + "-old"
 	if err := os.Rename(orig, backup); err != nil {
-		return err
+		return errors.Wrap(err, "error backing up plugin data before upgrade")
 	}
 
 	defer func() {
diff --git a/volume/store/store.go b/volume/store/store.go
index 38afd86..02c858a 100644
--- a/volume/store/store.go
+++ b/volume/store/store.go
@@ -519,7 +519,11 @@
 	if err != nil {
 		err = errors.Cause(err)
 		if _, ok := err.(net.Error); ok {
-			return nil, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", v.Name(), v.DriverName())
+			if v != nil {
+				volumeName = v.Name()
+				driverName = v.DriverName()
+			}
+			return nil, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", volumeName, driverName)
 		}
 
 		// At this point, the error could be anything from the driver, such as "no such volume"
@@ -542,7 +546,7 @@
 
 	vd, err := volumedrivers.GetDriver(v.DriverName())
 	if err != nil {
-		return &OpErr{Err: err, Name: vd.Name(), Op: "remove"}
+		return &OpErr{Err: err, Name: v.DriverName(), Op: "remove"}
 	}
 
 	logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name)
diff --git a/volume/volume.go b/volume/volume.go
index f3227fe..a941d8f 100644
--- a/volume/volume.go
+++ b/volume/volume.go
@@ -124,7 +124,20 @@
 
 // Setup sets up a mount point by either mounting the volume if it is
 // configured, or creating the source directory if supplied.
-func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (string, error) {
+func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (path string, err error) {
+	defer func() {
+		if err == nil {
+			if label.RelabelNeeded(m.Mode) {
+				if err = label.Relabel(m.Source, mountLabel, label.IsShared(m.Mode)); err != nil {
+					path = ""
+					err = errors.Wrapf(err, "error setting label on mount source '%s'", m.Source)
+					return
+				}
+			}
+		}
+		return
+	}()
+
 	if m.Volume != nil {
 		id := m.ID
 		if id == "" {
@@ -152,11 +165,6 @@
 			}
 		}
 	}
-	if label.RelabelNeeded(m.Mode) {
-		if err := label.Relabel(m.Source, mountLabel, label.IsShared(m.Mode)); err != nil {
-			return "", errors.Wrapf(err, "error setting label on mount source '%s'", m.Source)
-		}
-	}
 	return m.Source, nil
 }
 
@@ -303,10 +311,12 @@
 		}
 	case mounttypes.TypeBind:
 		mp.Source = clean(convertSlash(cfg.Source))
-		if cfg.BindOptions != nil {
-			if len(cfg.BindOptions.Propagation) > 0 {
-				mp.Propagation = cfg.BindOptions.Propagation
-			}
+		if cfg.BindOptions != nil && len(cfg.BindOptions.Propagation) > 0 {
+			mp.Propagation = cfg.BindOptions.Propagation
+		} else {
+			// If user did not specify a propagation mode, get
+			// default propagation mode.
+			mp.Propagation = DefaultPropagationMode
 		}
 	case mounttypes.TypeTmpfs:
 		// NOP
diff --git a/volume/volume_test.go b/volume/volume_test.go
index 54df380..18018f4 100644
--- a/volume/volume_test.go
+++ b/volume/volume_test.go
@@ -229,10 +229,10 @@
 	defer os.RemoveAll(testDir)
 
 	cases := []c{
-		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath}},
-		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, RW: true}},
-		{mount.Mount{Type: mount.TypeBind, Source: testDir + string(os.PathSeparator), Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath}},
-		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath + string(os.PathSeparator), ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath}},
+		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: DefaultPropagationMode}},
+		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, RW: true, Propagation: DefaultPropagationMode}},
+		{mount.Mount{Type: mount.TypeBind, Source: testDir + string(os.PathSeparator), Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: DefaultPropagationMode}},
+		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath + string(os.PathSeparator), ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: DefaultPropagationMode}},
 		{mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath}, MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: DefaultCopyMode}},
 		{mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath + string(os.PathSeparator)}, MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: DefaultCopyMode}},
 	}