Fix implicit DeviceMapper selection

DeviceMapper must be explicitly selected because the Docker binary might not be linked to the right devmapper library.

With this change, Docker fails fast if the driver detection finds the devicemapper directory but the driver is not the default option.
The option `override_udev_sync_check` doesn't make sense anymore, since the user must be explicit to select devicemapper, so it's being removed.
Docker fails to use devicemapper only if Docker has been built statically unless the option was explicit.

Signed-off-by: David Calavera <david.calavera@gmail.com>
diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go
index 2480517..c6cf109 100644
--- a/daemon/graphdriver/devmapper/deviceset.go
+++ b/daemon/graphdriver/devmapper/deviceset.go
@@ -30,8 +30,7 @@
 	DefaultDataLoopbackSize     int64  = 100 * 1024 * 1024 * 1024
 	DefaultMetaDataLoopbackSize int64  = 2 * 1024 * 1024 * 1024
 	DefaultBaseFsSize           uint64 = 10 * 1024 * 1024 * 1024
-	DefaultThinpBlockSize       uint32 = 128 // 64K = 128 512b sectors
-	DefaultUdevSyncOverride     bool   = false
+	DefaultThinpBlockSize       uint32 = 128      // 64K = 128 512b sectors
 	MaxDeviceId                 int    = 0xffffff // 24 bit, pool limit
 	DeviceIdMapSz               int    = (MaxDeviceId + 1) / 8
 	// We retry device removal so many a times that even error messages
@@ -90,22 +89,21 @@
 	deviceIdMap   []byte
 
 	// Options
-	dataLoopbackSize      int64
-	metaDataLoopbackSize  int64
-	baseFsSize            uint64
-	filesystem            string
-	mountOptions          string
-	mkfsArgs              []string
-	dataDevice            string // block or loop dev
-	dataLoopFile          string // loopback file, if used
-	metadataDevice        string // block or loop dev
-	metadataLoopFile      string // loopback file, if used
-	doBlkDiscard          bool
-	thinpBlockSize        uint32
-	thinPoolDevice        string
-	Transaction           `json:"-"`
-	overrideUdevSyncCheck bool
-	deferredRemove        bool // use deferred removal
+	dataLoopbackSize     int64
+	metaDataLoopbackSize int64
+	baseFsSize           uint64
+	filesystem           string
+	mountOptions         string
+	mkfsArgs             []string
+	dataDevice           string // block or loop dev
+	dataLoopFile         string // loopback file, if used
+	metadataDevice       string // block or loop dev
+	metadataLoopFile     string // loopback file, if used
+	doBlkDiscard         bool
+	thinpBlockSize       uint32
+	thinPoolDevice       string
+	Transaction          `json:"-"`
+	deferredRemove       bool // use deferred removal
 }
 
 type DiskUsage struct {
@@ -1033,10 +1031,7 @@
 
 	// https://github.com/docker/docker/issues/4036
 	if supported := devicemapper.UdevSetSyncSupport(true); !supported {
-		logrus.Errorf("Udev sync is not supported. This will lead to unexpected behavior, data loss and errors. For more information, see https://docs.docker.com/reference/commandline/cli/#daemon-storage-driver-option")
-		if !devices.overrideUdevSyncCheck {
-			return graphdriver.ErrNotSupported
-		}
+		logrus.Warn("Udev sync is not supported. This will lead to unexpected behavior, data loss and errors. For more information, see https://docs.docker.com/reference/commandline/cli/#daemon-storage-driver-option")
 	}
 
 	if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil && !os.IsExist(err) {
@@ -1704,16 +1699,15 @@
 	devicemapper.SetDevDir("/dev")
 
 	devices := &DeviceSet{
-		root:                  root,
-		MetaData:              MetaData{Devices: make(map[string]*DevInfo)},
-		dataLoopbackSize:      DefaultDataLoopbackSize,
-		metaDataLoopbackSize:  DefaultMetaDataLoopbackSize,
-		baseFsSize:            DefaultBaseFsSize,
-		overrideUdevSyncCheck: DefaultUdevSyncOverride,
-		filesystem:            "ext4",
-		doBlkDiscard:          true,
-		thinpBlockSize:        DefaultThinpBlockSize,
-		deviceIdMap:           make([]byte, DeviceIdMapSz),
+		root:                 root,
+		MetaData:             MetaData{Devices: make(map[string]*DevInfo)},
+		dataLoopbackSize:     DefaultDataLoopbackSize,
+		metaDataLoopbackSize: DefaultMetaDataLoopbackSize,
+		baseFsSize:           DefaultBaseFsSize,
+		filesystem:           "ext4",
+		doBlkDiscard:         true,
+		thinpBlockSize:       DefaultThinpBlockSize,
+		deviceIdMap:          make([]byte, DeviceIdMapSz),
 	}
 
 	foundBlkDiscard := false
@@ -1770,12 +1764,6 @@
 			}
 			// convert to 512b sectors
 			devices.thinpBlockSize = uint32(size) >> 9
-		case "dm.override_udev_sync_check":
-			devices.overrideUdevSyncCheck, err = strconv.ParseBool(val)
-			if err != nil {
-				return nil, err
-			}
-
 		case "dm.use_deferred_removal":
 			EnableDeferredRemoval, err = strconv.ParseBool(val)
 			if err != nil {
diff --git a/daemon/graphdriver/devmapper/devmapper_test.go b/daemon/graphdriver/devmapper/devmapper_test.go
index 60006af..6cb7572 100644
--- a/daemon/graphdriver/devmapper/devmapper_test.go
+++ b/daemon/graphdriver/devmapper/devmapper_test.go
@@ -13,7 +13,6 @@
 	DefaultDataLoopbackSize = 300 * 1024 * 1024
 	DefaultMetaDataLoopbackSize = 200 * 1024 * 1024
 	DefaultBaseFsSize = 300 * 1024 * 1024
-	DefaultUdevSyncOverride = true
 	if err := graphtest.InitLoopbacks(); err != nil {
 		panic(err)
 	}
diff --git a/daemon/graphdriver/driver.go b/daemon/graphdriver/driver.go
index 963acdf..c482973 100644
--- a/daemon/graphdriver/driver.go
+++ b/daemon/graphdriver/driver.go
@@ -8,6 +8,7 @@
 	"strings"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/autogen/dockerversion"
 	"github.com/docker/docker/pkg/archive"
 )
 
@@ -22,9 +23,10 @@
 	// All registred drivers
 	drivers map[string]InitFunc
 
-	ErrNotSupported   = errors.New("driver not supported")
-	ErrPrerequisites  = errors.New("prerequisites for driver not satisfied (wrong filesystem?)")
-	ErrIncompatibleFS = fmt.Errorf("backing file system is unsupported for this graph driver")
+	ErrNotSupported                 = errors.New("driver not supported")
+	ErrPrerequisites                = errors.New("prerequisites for driver not satisfied (wrong filesystem?)")
+	ErrIncompatibleFS               = fmt.Errorf("backing file system is unsupported for this graph driver")
+	ErrDeviceMapperWithStaticDocker = fmt.Errorf("devicemapper storage driver cannot reliably be used with a statically linked docker binary: please either pick a different storage driver, install a dynamically linked docker binary, or force this unreliable setup anyway by specifying --storage-driver=devicemapper")
 )
 
 type InitFunc func(root string, options []string) (Driver, error)
@@ -110,36 +112,35 @@
 	}
 
 	// Guess for prior driver
-	priorDrivers := scanPriorDrivers(root)
-	for _, name := range priority {
-		if name == "vfs" {
-			// don't use vfs even if there is state present.
-			continue
+	priorDriver, err := scanPriorDrivers(root)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(priorDriver) != 0 {
+		// Do not allow devicemapper when it's not explicit and the Docker binary was built statically.
+		if staticWithDeviceMapper(priorDriver) {
+			return nil, ErrDeviceMapperWithStaticDocker
 		}
-		for _, prior := range priorDrivers {
-			// of the state found from prior drivers, check in order of our priority
-			// which we would prefer
-			if prior == name {
-				driver, err = GetDriver(name, root, options)
-				if err != nil {
-					// unlike below, we will return error here, because there is prior
-					// state, and now it is no longer supported/prereq/compatible, so
-					// something changed and needs attention. Otherwise the daemon's
-					// images would just "disappear".
-					logrus.Errorf("[graphdriver] prior storage driver %q failed: %s", name, err)
-					return nil, err
-				}
-				if err := checkPriorDriver(name, root); err != nil {
-					return nil, err
-				}
-				logrus.Infof("[graphdriver] using prior storage driver %q", name)
-				return driver, nil
-			}
+
+		driver, err = GetDriver(priorDriver, root, options)
+		if err != nil {
+			// unlike below, we will return error here, because there is prior
+			// state, and now it is no longer supported/prereq/compatible, so
+			// something changed and needs attention. Otherwise the daemon's
+			// images would just "disappear".
+			logrus.Errorf("[graphdriver] prior storage driver %q failed: %s", priorDriver, err)
+			return nil, err
 		}
+		logrus.Infof("[graphdriver] using prior storage driver %q", priorDriver)
+		return driver, nil
 	}
 
 	// Check for priority drivers first
 	for _, name := range priority {
+		if staticWithDeviceMapper(name) {
+			continue
+		}
 		driver, err = GetDriver(name, root, options)
 		if err != nil {
 			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
@@ -151,7 +152,10 @@
 	}
 
 	// Check all registered drivers if no priority driver is found
-	for _, initFunc := range drivers {
+	for name, initFunc := range drivers {
+		if staticWithDeviceMapper(name) {
+			continue
+		}
 		if driver, err = initFunc(root, options); err != nil {
 			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
 				continue
@@ -163,31 +167,31 @@
 	return nil, fmt.Errorf("No supported storage backend found")
 }
 
-// scanPriorDrivers returns an un-ordered scan of directories of prior storage drivers
-func scanPriorDrivers(root string) []string {
-	priorDrivers := []string{}
+// scanPriorDrivers returns a previosly used driver.
+// it returns an error when there are several drivers scanned.
+func scanPriorDrivers(root string) (string, error) {
+	var priorDrivers []string
 	for driver := range drivers {
 		p := filepath.Join(root, driver)
-		if _, err := os.Stat(p); err == nil {
+		if _, err := os.Stat(p); err == nil && driver != "vfs" {
 			priorDrivers = append(priorDrivers, driver)
 		}
 	}
-	return priorDrivers
+
+	if len(priorDrivers) > 1 {
+		return "", multipleDriversError(root, priorDrivers)
+	}
+
+	if len(priorDrivers) == 0 {
+		return "", nil
+	}
+	return priorDrivers[0], nil
 }
 
-func checkPriorDriver(name, root string) error {
-	priorDrivers := []string{}
-	for _, prior := range scanPriorDrivers(root) {
-		if prior != name && prior != "vfs" {
-			if _, err := os.Stat(filepath.Join(root, prior)); err == nil {
-				priorDrivers = append(priorDrivers, prior)
-			}
-		}
-	}
+func multipleDriversError(root string, drivers []string) error {
+	return fmt.Errorf("%q contains several graphdrivers: %s; Please cleanup or explicitly choose storage driver (--storage-driver <DRIVER>)", root, strings.Join(drivers, ", "))
+}
 
-	if len(priorDrivers) > 0 {
-
-		return errors.New(fmt.Sprintf("%q contains other graphdrivers: %s; Please cleanup or explicitly choose storage driver (-s <DRIVER>)", root, strings.Join(priorDrivers, ",")))
-	}
-	return nil
+func staticWithDeviceMapper(name string) bool {
+	return name == "devicemapper" && dockerversion.IAMSTATIC == "true"
 }