Merge pull request #35343 from thaJeztah/bump-api-version-1.35

Bump API version to 1.35
diff --git a/api/swagger.yaml b/api/swagger.yaml
index fb77e4f..2106c31 100644
--- a/api/swagger.yaml
+++ b/api/swagger.yaml
@@ -732,7 +732,15 @@
             description: "Gives the container full access to the host."
           PublishAllPorts:
             type: "boolean"
-            description: "Allocates a random host port for all of a container's exposed ports."
+            description: |
+              Allocates an ephemeral host port for all of a container's
+              exposed ports.
+
+              Ports are de-allocated when the container stops and allocated when the container starts.
+              The allocated port might be changed when restarting the container.
+
+              The port is selected from the ephemeral port range that depends on the kernel.
+              For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
           ReadonlyRootfs:
             type: "boolean"
             description: "Mount the container's root filesystem as read only."
diff --git a/builder/remotecontext/archive.go b/builder/remotecontext/archive.go
index fc18c5d..b62d9dd 100644
--- a/builder/remotecontext/archive.go
+++ b/builder/remotecontext/archive.go
@@ -122,8 +122,5 @@
 	if err != nil {
 		return "", "", errors.Wrapf(err, "forbidden path outside the build context: %s (%s)", path, cleanPath)
 	}
-	if _, err := root.Lstat(fullPath); err != nil {
-		return "", "", errors.WithStack(convertPathError(err, path))
-	}
 	return
 }
diff --git a/builder/remotecontext/lazycontext.go b/builder/remotecontext/lazycontext.go
index 66f36de..08b8058 100644
--- a/builder/remotecontext/lazycontext.go
+++ b/builder/remotecontext/lazycontext.go
@@ -40,16 +40,18 @@
 		return "", err
 	}
 
-	fi, err := c.root.Lstat(fullPath)
-	if err != nil {
-		return "", errors.WithStack(err)
-	}
-
 	relPath, err := Rel(c.root, fullPath)
 	if err != nil {
 		return "", errors.WithStack(convertPathError(err, cleanPath))
 	}
 
+	fi, err := os.Lstat(fullPath)
+	if err != nil {
+		// Backwards compatibility: a missing file returns a path as hash.
+		// This is reached in the case of a broken symlink.
+		return relPath, nil
+	}
+
 	sum, ok := c.sums[relPath]
 	if !ok {
 		sum, err = c.prepareHash(relPath, fi)
diff --git a/builder/remotecontext/tarsum_test.go b/builder/remotecontext/tarsum_test.go
index 6d2b41d..9395460 100644
--- a/builder/remotecontext/tarsum_test.go
+++ b/builder/remotecontext/tarsum_test.go
@@ -104,17 +104,6 @@
 	}
 }
 
-func TestStatNotExisting(t *testing.T) {
-	contextDir, cleanup := createTestTempDir(t, "", "builder-tarsum-test")
-	defer cleanup()
-
-	src := makeTestArchiveContext(t, contextDir)
-	_, err := src.Hash("not-existing")
-	if !os.IsNotExist(errors.Cause(err)) {
-		t.Fatalf("This file should not exist: %s", err)
-	}
-}
-
 func TestRemoveDirectory(t *testing.T) {
 	contextDir, cleanup := createTestTempDir(t, "", "builder-tarsum-test")
 	defer cleanup()
@@ -129,17 +118,20 @@
 
 	src := makeTestArchiveContext(t, contextDir)
 
-	tarSum := src.(modifiableContext)
+	_, err = src.Root().Stat(src.Root().Join(src.Root().Path(), relativePath))
+	if err != nil {
+		t.Fatalf("Statting %s shouldn't fail: %+v", relativePath, err)
+	}
 
+	tarSum := src.(modifiableContext)
 	err = tarSum.Remove(relativePath)
 	if err != nil {
 		t.Fatalf("Error when executing Remove: %s", err)
 	}
 
-	_, err = src.Hash(contextSubdir)
-
+	_, err = src.Root().Stat(src.Root().Join(src.Root().Path(), relativePath))
 	if !os.IsNotExist(errors.Cause(err)) {
-		t.Fatal("Directory should not exist at this point")
+		t.Fatalf("Directory should not exist at this point: %+v ", err)
 	}
 }
 
diff --git a/daemon/graphdriver/quota/projectquota.go b/daemon/graphdriver/quota/projectquota.go
index 0e70515..84e391a 100644
--- a/daemon/graphdriver/quota/projectquota.go
+++ b/daemon/graphdriver/quota/projectquota.go
@@ -47,6 +47,8 @@
 #ifndef Q_XGETPQUOTA
 #define Q_XGETPQUOTA QCMD(Q_XGETQUOTA, PRJQUOTA)
 #endif
+
+const int Q_XGETQSTAT_PRJQUOTA = QCMD(Q_XGETQSTAT, PRJQUOTA);
 */
 import "C"
 import (
@@ -56,10 +58,15 @@
 	"path/filepath"
 	"unsafe"
 
+	"errors"
+
 	"github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
 )
 
+// ErrQuotaNotSupported indicates if were found the FS does not have projects quotas available
+var ErrQuotaNotSupported = errors.New("Filesystem does not support or has not enabled quotas")
+
 // Quota limit params - currently we only control blocks hard limit
 type Quota struct {
 	Size uint64
@@ -97,6 +104,24 @@
 //
 func NewControl(basePath string) (*Control, error) {
 	//
+	// create backing filesystem device node
+	//
+	backingFsBlockDev, err := makeBackingFsDev(basePath)
+	if err != nil {
+		return nil, err
+	}
+
+	// check if we can call quotactl with project quotas
+	// as a mechanism to determine (early) if we have support
+	hasQuotaSupport, err := hasQuotaSupport(backingFsBlockDev)
+	if err != nil {
+		return nil, err
+	}
+	if !hasQuotaSupport {
+		return nil, ErrQuotaNotSupported
+	}
+
+	//
 	// Get project id of parent dir as minimal id to be used by driver
 	//
 	minProjectID, err := getProjectID(basePath)
@@ -106,14 +131,6 @@
 	minProjectID++
 
 	//
-	// create backing filesystem device node
-	//
-	backingFsBlockDev, err := makeBackingFsDev(basePath)
-	if err != nil {
-		return nil, err
-	}
-
-	//
 	// Test if filesystem supports project quotas by trying to set
 	// a quota on the first available project id
 	//
@@ -335,3 +352,23 @@
 
 	return backingFsBlockDev, nil
 }
+
+func hasQuotaSupport(backingFsBlockDev string) (bool, error) {
+	var cs = C.CString(backingFsBlockDev)
+	defer free(cs)
+	var qstat C.fs_quota_stat_t
+
+	_, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, uintptr(C.Q_XGETQSTAT_PRJQUOTA), uintptr(unsafe.Pointer(cs)), 0, uintptr(unsafe.Pointer(&qstat)), 0, 0)
+	if errno == 0 && qstat.qs_flags&C.FS_QUOTA_PDQ_ENFD > 0 && qstat.qs_flags&C.FS_QUOTA_PDQ_ACCT > 0 {
+		return true, nil
+	}
+
+	switch errno {
+	// These are the known fatal errors, consider all other errors (ENOTTY, etc.. not supporting quota)
+	case unix.EFAULT, unix.ENOENT, unix.ENOTBLK, unix.EPERM:
+	default:
+		return false, nil
+	}
+
+	return false, errno
+}
diff --git a/daemon/graphdriver/quota/projectquota_test.go b/daemon/graphdriver/quota/projectquota_test.go
new file mode 100644
index 0000000..2b47a58
--- /dev/null
+++ b/daemon/graphdriver/quota/projectquota_test.go
@@ -0,0 +1,161 @@
+// +build linux
+
+package quota
+
+import (
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"golang.org/x/sys/unix"
+)
+
+// 10MB
+const testQuotaSize = 10 * 1024 * 1024
+const imageSize = 64 * 1024 * 1024
+
+func TestBlockDev(t *testing.T) {
+	mkfs, err := exec.LookPath("mkfs.xfs")
+	if err != nil {
+		t.Fatal("mkfs.xfs not installed")
+	}
+
+	// create a sparse image
+	imageFile, err := ioutil.TempFile("", "xfs-image")
+	if err != nil {
+		t.Fatal(err)
+	}
+	imageFileName := imageFile.Name()
+	defer os.Remove(imageFileName)
+	if _, err = imageFile.Seek(imageSize-1, 0); err != nil {
+		t.Fatal(err)
+	}
+	if _, err = imageFile.Write([]byte{0}); err != nil {
+		t.Fatal(err)
+	}
+	if err = imageFile.Close(); err != nil {
+		t.Fatal(err)
+	}
+
+	// The reason for disabling these options is sometimes people run with a newer userspace
+	// than kernelspace
+	out, err := exec.Command(mkfs, "-m", "crc=0,finobt=0", imageFileName).CombinedOutput()
+	if len(out) > 0 {
+		t.Log(string(out))
+	}
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	runTest(t, "testBlockDevQuotaDisabled", wrapMountTest(imageFileName, false, testBlockDevQuotaDisabled))
+	runTest(t, "testBlockDevQuotaEnabled", wrapMountTest(imageFileName, true, testBlockDevQuotaEnabled))
+	runTest(t, "testSmallerThanQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testSmallerThanQuota)))
+	runTest(t, "testBiggerThanQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testBiggerThanQuota)))
+	runTest(t, "testRetrieveQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testRetrieveQuota)))
+}
+
+func runTest(t *testing.T, testName string, testFunc func(*testing.T)) {
+	if success := t.Run(testName, testFunc); !success {
+		out, _ := exec.Command("dmesg").CombinedOutput()
+		t.Log(string(out))
+	}
+}
+
+func wrapMountTest(imageFileName string, enableQuota bool, testFunc func(t *testing.T, mountPoint, backingFsDev string)) func(*testing.T) {
+	return func(t *testing.T) {
+		mountOptions := "loop"
+
+		if enableQuota {
+			mountOptions = mountOptions + ",prjquota"
+		}
+
+		// create a mountPoint
+		mountPoint, err := ioutil.TempDir("", "xfs-mountPoint")
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer os.RemoveAll(mountPoint)
+
+		out, err := exec.Command("mount", "-o", mountOptions, imageFileName, mountPoint).CombinedOutput()
+		if len(out) > 0 {
+			t.Log(string(out))
+		}
+		if err != nil {
+			t.Fatal("mount failed")
+		}
+
+		defer func() {
+			if err := unix.Unmount(mountPoint, 0); err != nil {
+				t.Fatal(err)
+			}
+		}()
+
+		backingFsDev, err := makeBackingFsDev(mountPoint)
+		require.NoError(t, err)
+
+		testFunc(t, mountPoint, backingFsDev)
+	}
+}
+
+func testBlockDevQuotaDisabled(t *testing.T, mountPoint, backingFsDev string) {
+	hasSupport, err := hasQuotaSupport(backingFsDev)
+	require.NoError(t, err)
+	assert.False(t, hasSupport)
+}
+
+func testBlockDevQuotaEnabled(t *testing.T, mountPoint, backingFsDev string) {
+	hasSupport, err := hasQuotaSupport(backingFsDev)
+	require.NoError(t, err)
+	assert.True(t, hasSupport)
+}
+
+func wrapQuotaTest(testFunc func(t *testing.T, ctrl *Control, mountPoint, testDir, testSubDir string)) func(t *testing.T, mountPoint, backingFsDev string) {
+	return func(t *testing.T, mountPoint, backingFsDev string) {
+		testDir, err := ioutil.TempDir(mountPoint, "per-test")
+		require.NoError(t, err)
+		defer os.RemoveAll(testDir)
+
+		ctrl, err := NewControl(testDir)
+		require.NoError(t, err)
+
+		testSubDir, err := ioutil.TempDir(testDir, "quota-test")
+		require.NoError(t, err)
+		testFunc(t, ctrl, mountPoint, testDir, testSubDir)
+	}
+
+}
+
+func testSmallerThanQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) {
+	require.NoError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize}))
+	smallerThanQuotaFile := filepath.Join(testSubDir, "smaller-than-quota")
+	require.NoError(t, ioutil.WriteFile(smallerThanQuotaFile, make([]byte, testQuotaSize/2), 0644))
+	require.NoError(t, os.Remove(smallerThanQuotaFile))
+}
+
+func testBiggerThanQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) {
+	// Make sure the quota is being enforced
+	// TODO: When we implement this under EXT4, we need to shed CAP_SYS_RESOURCE, otherwise
+	// we're able to violate quota without issue
+	require.NoError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize}))
+
+	biggerThanQuotaFile := filepath.Join(testSubDir, "bigger-than-quota")
+	err := ioutil.WriteFile(biggerThanQuotaFile, make([]byte, testQuotaSize+1), 0644)
+	require.Error(t, err)
+	if err == io.ErrShortWrite {
+		require.NoError(t, os.Remove(biggerThanQuotaFile))
+	}
+}
+
+func testRetrieveQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) {
+	// Validate that we can retrieve quota
+	require.NoError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize}))
+
+	var q Quota
+	require.NoError(t, ctrl.GetQuota(testSubDir, &q))
+	assert.EqualValues(t, testQuotaSize, q.Size)
+}
diff --git a/daemon/logger/awslogs/cloudwatchlogs.go b/daemon/logger/awslogs/cloudwatchlogs.go
index 9788253..4ea9420 100644
--- a/daemon/logger/awslogs/cloudwatchlogs.go
+++ b/daemon/logger/awslogs/cloudwatchlogs.go
@@ -275,6 +275,10 @@
 	return name
 }
 
+func (l *logStream) BufSize() int {
+	return maximumBytesPerEvent
+}
+
 // Log submits messages for logging by an instance of the awslogs logging driver
 func (l *logStream) Log(msg *logger.Message) error {
 	l.lock.RLock()
diff --git a/daemon/logger/awslogs/cloudwatchlogs_test.go b/daemon/logger/awslogs/cloudwatchlogs_test.go
index 7d482d8..7ebc5de 100644
--- a/daemon/logger/awslogs/cloudwatchlogs_test.go
+++ b/daemon/logger/awslogs/cloudwatchlogs_test.go
@@ -1052,6 +1052,11 @@
 	}
 }
 
+func TestIsSizedLogger(t *testing.T) {
+	awslogs := &logStream{}
+	assert.Implements(t, (*logger.SizedLogger)(nil), awslogs, "awslogs should implement SizedLogger")
+}
+
 func BenchmarkUnwrapEvents(b *testing.B) {
 	events := make([]wrappedEvent, maximumLogEventsPerPut)
 	for i := 0; i < maximumLogEventsPerPut; i++ {
diff --git a/daemon/logger/copier.go b/daemon/logger/copier.go
index c773fc6..a1d4f06 100644
--- a/daemon/logger/copier.go
+++ b/daemon/logger/copier.go
@@ -10,8 +10,13 @@
 )
 
 const (
-	bufSize  = 16 * 1024
+	// readSize is the maximum bytes read during a single read
+	// operation.
 	readSize = 2 * 1024
+
+	// defaultBufSize provides a reasonable default for loggers that do
+	// not have an external limit to impose on log line size.
+	defaultBufSize = 16 * 1024
 )
 
 // Copier can copy logs from specified sources to Logger and attach Timestamp.
@@ -44,7 +49,13 @@
 
 func (c *Copier) copySrc(name string, src io.Reader) {
 	defer c.copyJobs.Done()
+
+	bufSize := defaultBufSize
+	if sizedLogger, ok := c.dst.(SizedLogger); ok {
+		bufSize = sizedLogger.BufSize()
+	}
 	buf := make([]byte, bufSize)
+
 	n := 0
 	eof := false
 
diff --git a/daemon/logger/copier_test.go b/daemon/logger/copier_test.go
index 4210022..a911a70 100644
--- a/daemon/logger/copier_test.go
+++ b/daemon/logger/copier_test.go
@@ -31,6 +31,25 @@
 
 func (l *TestLoggerJSON) Name() string { return "json" }
 
+type TestSizedLoggerJSON struct {
+	*json.Encoder
+	mu sync.Mutex
+}
+
+func (l *TestSizedLoggerJSON) Log(m *Message) error {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	return l.Encode(m)
+}
+
+func (*TestSizedLoggerJSON) Close() error { return nil }
+
+func (*TestSizedLoggerJSON) Name() string { return "sized-json" }
+
+func (*TestSizedLoggerJSON) BufSize() int {
+	return 32 * 1024
+}
+
 func TestCopier(t *testing.T) {
 	stdoutLine := "Line that thinks that it is log line from docker stdout"
 	stderrLine := "Line that thinks that it is log line from docker stderr"
@@ -104,10 +123,9 @@
 
 // TestCopierLongLines tests long lines without line breaks
 func TestCopierLongLines(t *testing.T) {
-	// Long lines (should be split at "bufSize")
-	const bufSize = 16 * 1024
-	stdoutLongLine := strings.Repeat("a", bufSize)
-	stderrLongLine := strings.Repeat("b", bufSize)
+	// Long lines (should be split at "defaultBufSize")
+	stdoutLongLine := strings.Repeat("a", defaultBufSize)
+	stderrLongLine := strings.Repeat("b", defaultBufSize)
 	stdoutTrailingLine := "stdout trailing line"
 	stderrTrailingLine := "stderr trailing line"
 
@@ -205,6 +223,41 @@
 	}
 }
 
+func TestCopierWithSized(t *testing.T) {
+	var jsonBuf bytes.Buffer
+	expectedMsgs := 2
+	sizedLogger := &TestSizedLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}
+	logbuf := bytes.NewBufferString(strings.Repeat(".", sizedLogger.BufSize()*expectedMsgs))
+	c := NewCopier(map[string]io.Reader{"stdout": logbuf}, sizedLogger)
+
+	c.Run()
+	// Wait for Copier to finish writing to the buffered logger.
+	c.Wait()
+	c.Close()
+
+	recvdMsgs := 0
+	dec := json.NewDecoder(&jsonBuf)
+	for {
+		var msg Message
+		if err := dec.Decode(&msg); err != nil {
+			if err == io.EOF {
+				break
+			}
+			t.Fatal(err)
+		}
+		if msg.Source != "stdout" {
+			t.Fatalf("Wrong Source: %q, should be %q", msg.Source, "stdout")
+		}
+		if len(msg.Line) != sizedLogger.BufSize() {
+			t.Fatalf("Line was not of expected max length %d, was %d", sizedLogger.BufSize(), len(msg.Line))
+		}
+		recvdMsgs++
+	}
+	if recvdMsgs != expectedMsgs {
+		t.Fatalf("expected to receive %d messages, actually received %d", expectedMsgs, recvdMsgs)
+	}
+}
+
 type BenchmarkLoggerDummy struct {
 }
 
diff --git a/daemon/logger/logger.go b/daemon/logger/logger.go
index ee91b79..1108597 100644
--- a/daemon/logger/logger.go
+++ b/daemon/logger/logger.go
@@ -78,6 +78,13 @@
 	Close() error
 }
 
+// SizedLogger is the interface for logging drivers that can control
+// the size of buffer used for their messages.
+type SizedLogger interface {
+	Logger
+	BufSize() int
+}
+
 // ReadConfig is the configuration passed into ReadLogs.
 type ReadConfig struct {
 	Since  time.Time
diff --git a/docs/api/v1.18.md b/docs/api/v1.18.md
index 7ae32cc..3277014 100644
--- a/docs/api/v1.18.md
+++ b/docs/api/v1.18.md
@@ -264,8 +264,14 @@
           should map to. A JSON object in the form
           `{ <port>/<protocol>: [{ "HostPort": "<port>" }] }`
           Take note that `port` is specified as a string and not an integer value.
-    -   **PublishAllPorts** - Allocates a random host port for all of a container's
+    -   **PublishAllPorts** - Allocates an ephemeral host port for all of a container's
           exposed ports. Specified as a boolean value.
+
+          Ports are de-allocated when the container stops and allocated when the container starts.
+          The allocated port might be changed when restarting the container.
+
+          The port is selected from the ephemeral port range that depends on the kernel.
+          For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
     -   **Privileged** - Gives the container full access to the host. Specified as
           a boolean value.
     -   **ReadonlyRootfs** - Mount the container's root filesystem as read only.
diff --git a/docs/api/v1.19.md b/docs/api/v1.19.md
index 3ecd312..448fe83 100644
--- a/docs/api/v1.19.md
+++ b/docs/api/v1.19.md
@@ -274,8 +274,14 @@
           should map to. A JSON object in the form
           `{ <port>/<protocol>: [{ "HostPort": "<port>" }] }`
           Take note that `port` is specified as a string and not an integer value.
-    -   **PublishAllPorts** - Allocates a random host port for all of a container's
+    -   **PublishAllPorts** - Allocates an ephemeral host port for all of a container's
           exposed ports. Specified as a boolean value.
+
+          Ports are de-allocated when the container stops and allocated when the container starts.
+          The allocated port might be changed when restarting the container.
+
+          The port is selected from the ephemeral port range that depends on the kernel.
+          For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
     -   **Privileged** - Gives the container full access to the host. Specified as
           a boolean value.
     -   **ReadonlyRootfs** - Mount the container's root filesystem as read only.
diff --git a/docs/api/v1.20.md b/docs/api/v1.20.md
index 1f94bda..b971bab 100644
--- a/docs/api/v1.20.md
+++ b/docs/api/v1.20.md
@@ -277,8 +277,14 @@
           should map to. A JSON object in the form
           `{ <port>/<protocol>: [{ "HostPort": "<port>" }] }`
           Take note that `port` is specified as a string and not an integer value.
-    -   **PublishAllPorts** - Allocates a random host port for all of a container's
+    -   **PublishAllPorts** - Allocates an ephemeral host port for all of a container's
           exposed ports. Specified as a boolean value.
+
+          Ports are de-allocated when the container stops and allocated when the container starts.
+          The allocated port might be changed when restarting the container.
+
+          The port is selected from the ephemeral port range that depends on the kernel.
+          For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
     -   **Privileged** - Gives the container full access to the host. Specified as
           a boolean value.
     -   **ReadonlyRootfs** - Mount the container's root filesystem as read only.
diff --git a/docs/api/v1.21.md b/docs/api/v1.21.md
index a3b79ed..2971d25 100644
--- a/docs/api/v1.21.md
+++ b/docs/api/v1.21.md
@@ -294,8 +294,14 @@
           should map to. A JSON object in the form
           `{ <port>/<protocol>: [{ "HostPort": "<port>" }] }`
           Take note that `port` is specified as a string and not an integer value.
-    -   **PublishAllPorts** - Allocates a random host port for all of a container's
+    -   **PublishAllPorts** - Allocates an ephemeral host port for all of a container's
           exposed ports. Specified as a boolean value.
+
+          Ports are de-allocated when the container stops and allocated when the container starts.
+          The allocated port might be changed when restarting the container.
+
+          The port is selected from the ephemeral port range that depends on the kernel.
+          For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
     -   **Privileged** - Gives the container full access to the host. Specified as
           a boolean value.
     -   **ReadonlyRootfs** - Mount the container's root filesystem as read only.
diff --git a/docs/api/v1.22.md b/docs/api/v1.22.md
index a6027bb..a51fbcb 100644
--- a/docs/api/v1.22.md
+++ b/docs/api/v1.22.md
@@ -408,8 +408,14 @@
           should map to. A JSON object in the form
           `{ <port>/<protocol>: [{ "HostPort": "<port>" }] }`
           Take note that `port` is specified as a string and not an integer value.
-    -   **PublishAllPorts** - Allocates a random host port for all of a container's
+    -   **PublishAllPorts** - Allocates an ephemeral host port for all of a container's
           exposed ports. Specified as a boolean value.
+
+          Ports are de-allocated when the container stops and allocated when the container starts.
+          The allocated port might be changed when restarting the container.
+
+          The port is selected from the ephemeral port range that depends on the kernel.
+          For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
     -   **Privileged** - Gives the container full access to the host. Specified as
           a boolean value.
     -   **ReadonlyRootfs** - Mount the container's root filesystem as read only.
diff --git a/docs/api/v1.23.md b/docs/api/v1.23.md
index 19f9e5a..677dcab 100644
--- a/docs/api/v1.23.md
+++ b/docs/api/v1.23.md
@@ -432,8 +432,14 @@
           should map to. A JSON object in the form
           `{ <port>/<protocol>: [{ "HostPort": "<port>" }] }`
           Take note that `port` is specified as a string and not an integer value.
-    -   **PublishAllPorts** - Allocates a random host port for all of a container's
+    -   **PublishAllPorts** - Allocates an ephemeral host port for all of a container's
           exposed ports. Specified as a boolean value.
+
+          Ports are de-allocated when the container stops and allocated when the container starts.
+          The allocated port might be changed when restarting the container.
+
+          The port is selected from the ephemeral port range that depends on the kernel.
+          For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
     -   **Privileged** - Gives the container full access to the host. Specified as
           a boolean value.
     -   **ReadonlyRootfs** - Mount the container's root filesystem as read only.
diff --git a/docs/api/v1.24.md b/docs/api/v1.24.md
index 6ab5dc2..a32325e 100644
--- a/docs/api/v1.24.md
+++ b/docs/api/v1.24.md
@@ -469,8 +469,14 @@
           should map to. A JSON object in the form
           `{ <port>/<protocol>: [{ "HostPort": "<port>" }] }`
           Take note that `port` is specified as a string and not an integer value.
-    -   **PublishAllPorts** - Allocates a random host port for all of a container's
+    -   **PublishAllPorts** - Allocates an ephemeral host port for all of a container's
           exposed ports. Specified as a boolean value.
+
+          Ports are de-allocated when the container stops and allocated when the container starts.
+          The allocated port might be changed when restarting the container.
+
+          The port is selected from the ephemeral port range that depends on the kernel.
+          For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
     -   **Privileged** - Gives the container full access to the host. Specified as
           a boolean value.
     -   **ReadonlyRootfs** - Mount the container's root filesystem as read only.
diff --git a/integration-cli/docker_cli_cp_from_container_test.go b/integration-cli/docker_cli_cp_from_container_test.go
index 687aec8..0a282f5 100644
--- a/integration-cli/docker_cli_cp_from_container_test.go
+++ b/integration-cli/docker_cli_cp_from_container_test.go
@@ -50,33 +50,6 @@
 	c.Assert(isCpNotDir(err), checker.True, check.Commentf("expected IsNotDir error, but got %T: %s", err, err))
 }
 
-// Test for error when SRC is a valid file or directory,
-// bu the DST parent directory does not exist.
-func (s *DockerSuite) TestCpFromErrDstParentNotExists(c *check.C) {
-	testRequires(c, DaemonIsLinux)
-	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
-
-	tmpDir := getTestDir(c, "test-cp-from-err-dst-parent-not-exists")
-	defer os.RemoveAll(tmpDir)
-
-	makeTestContentInDir(c, tmpDir)
-
-	// Try with a file source.
-	srcPath := containerCpPath(containerID, "/file1")
-	dstPath := cpPath(tmpDir, "notExists", "file1")
-	_, dstStatErr := os.Lstat(filepath.Dir(dstPath))
-	c.Assert(os.IsNotExist(dstStatErr), checker.True)
-
-	err := runDockerCp(c, srcPath, dstPath, nil)
-	c.Assert(err.Error(), checker.Contains, dstStatErr.Error())
-
-	// Try with a directory source.
-	srcPath = containerCpPath(containerID, "/dir1")
-
-	err = runDockerCp(c, srcPath, dstPath, nil)
-	c.Assert(err.Error(), checker.Contains, dstStatErr.Error())
-}
-
 // Test for error when DST ends in a trailing
 // path separator but exists as a file.
 func (s *DockerSuite) TestCpFromErrDstNotDir(c *check.C) {