Merge pull request #3474 from creack/fix_add_cache_issue

Fix ADD caching issue with . prefixed path
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f6c8399
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,23 @@
+# Note: right now we don't use go-specific features of travis.
+# Later we might automate "go test" etc. (or do it inside a docker container...?)
+
+language: go
+
+go: 1.2
+
+# Disable the normal go build.
+install: true
+
+before_script:
+  - env | sort
+  - sudo apt-get update -qq
+  - sudo apt-get install -qq python-yaml
+  - git remote add upstream git://github.com/dotcloud/docker.git
+  - git fetch --append --no-tags upstream refs/heads/master:refs/remotes/upstream/master
+# sometimes we have upstream master already as origin/master (PRs), but other times we don't, so let's just make sure we have a completely unambiguous way to specify "upstream master" from here out
+
+script:
+  - hack/travis/dco.py
+  - hack/travis/gofmt.py
+
+# vim:set sw=2 ts=2:
diff --git a/AUTHORS b/AUTHORS
index f9e9ecd..1ff9b8d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -48,7 +48,6 @@
 Darren Coxall <darren@darrencoxall.com>
 David Calavera <david.calavera@gmail.com>
 David Sissitka <me@dsissitka.com>
-Dinesh Subhraveti <dineshs@altiscale.com>
 Deni Bertovic <deni@kset.org>
 Dominik Honnef <dominik@honnef.co>
 Don Spaulding <donspauldingii@gmail.com>
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f68270f..1caf9c8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -105,17 +105,39 @@
 regenerated occasionally from the git commit history, so a mismatch may result
 in your changes being overwritten.
 
-### Approval
+### Sign your work
 
-Docker maintainers use LGTM (looks good to me) in comments on the code review
-to indicate acceptance.
+The sign-off is a simple line at the end of the explanation for the
+patch, which certifies that you wrote it or otherwise have the right to
+pass it on as an open-source patch.  The rules are pretty simple: if you
+can certify the below:
 
-A change requires LGTMs from an absolute majority of the maintainers of each
-component affected. For example, if a change affects docs/ and registry/, it
-needs an absolute majority from the maintainers of docs/ AND, separately, an
-absolute majority of the maintainers of registry
+```
+Docker Developer Grant and Certificate of Origin 1.0
 
-For more details see [MAINTAINERS.md](hack/MAINTAINERS.md)
+By making a contribution to the Docker Project ("Project"), I represent and warrant that:
+
+a. The contribution was created in whole or in part by me and I have the right to submit the contribution on my own behalf or on behalf of a third party who has authorized me to submit this contribution to the Project; or
+
+
+b. The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right and authorization to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license) that I have identified in the contribution; or
+
+c. The contribution was provided directly to me by some other person who represented and warranted (a) or (b) and I have not modified it.
+
+d. I understand and agree that this Project and the contribution are publicly known and that a record of the contribution (including all personal information I submit with it, including my sign-off record) is maintained indefinitely and may be redistributed consistent with this Project or the open source license(s) involved.
+
+e. I hereby grant to the Project, Docker, Inc and its successors;  and recipients of software distributed by the Project a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, modify, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute this contribution and such modifications and derivative works consistent with this Project, the open source license indicated in the previous work or other appropriate open source license specified by the Project and approved by the Open Source Initiative(OSI) at http://www.opensource.org.
+```
+
+then you just add a line saying
+
+    Docker-DCO-1.0-Signed-off-by: Joe Smith <joe.smith@email.com> (github: github_handle)
+
+using your real name (sorry, no pseudonyms or anonymous contributions.)
+
+If you have any questions, please refer to the FAQ in the [docs](http://docs.docker.io)
+
+
 
 ### How can I become a maintainer?
 
diff --git a/MAINTAINERS b/MAINTAINERS
index ef3aeda..4a6c0ec 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2,6 +2,7 @@
 Guillaume Charmes <guillaume@dotcloud.com> (@creack)
 Victor Vieux <victor@dotcloud.com> (@vieux)
 Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
+.travis.yml: Tianon Gravi <admwiggin@gmail.com> (@tianon)
 api.go: Victor Vieux <victor@dotcloud.com> (@vieux)
 Dockerfile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
 Makefile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
diff --git a/REMOTE_TODO.md b/REMOTE_TODO.md
index cd9460a..8caddc6 100644
--- a/REMOTE_TODO.md
+++ b/REMOTE_TODO.md
@@ -7,7 +7,7 @@
 ...     "/images/json":                         getImagesJSON,          N
 TODO    "/images/viz":                          getImagesViz,           0                       yes
 TODO    "/images/search":                       getImagesSearch,        N
-TODO    "/images/{name:.*}/get":                getImagesGet,           0
+#3490   "/images/{name:.*}/get":                getImagesGet,           0
 TODO    "/images/{name:.*}/history":            getImagesHistory,       1
 TODO    "/images/{name:.*}/json":               getImagesByName,        1
 TODO    "/containers/ps":                       getContainersJSON,      N
@@ -29,7 +29,7 @@
 ok      "/images/{name:.*}/tag":                postImagesTag,          0
 ok      "/containers/create":                   postContainersCreate,   0
 ok      "/containers/{name:.*}/kill":           postContainersKill,     0
-TODO    "/containers/{name:.*}/restart":        postContainersRestart,  0
+#3476   "/containers/{name:.*}/restart":        postContainersRestart,  0
 ok      "/containers/{name:.*}/start":          postContainersStart,    0
 ok      "/containers/{name:.*}/stop":           postContainersStop,     0
 ok      "/containers/{name:.*}/wait":           postContainersWait,     0
@@ -43,4 +43,4 @@
 
 **OPTIONS**
 ok      "":                                     optionsHandler,         0
-```
\ No newline at end of file
+```
diff --git a/archive/archive.go b/archive/archive.go
index f8fcf0b..4dd5f00 100644
--- a/archive/archive.go
+++ b/archive/archive.go
@@ -3,8 +3,8 @@
 import (
 	"archive/tar"
 	"bytes"
-	"compress/gzip"
 	"compress/bzip2"
+	"compress/gzip"
 	"fmt"
 	"github.com/dotcloud/docker/utils"
 	"io"
@@ -149,7 +149,7 @@
 // Tar creates an archive from the directory at `path`, only including files whose relative
 // paths are included in `filter`. If `filter` is nil, then all files are included.
 func TarFilter(path string, options *TarOptions) (io.Reader, error) {
-	args := []string{"tar", "-S", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"}
+	args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"}
 	if options.Includes == nil {
 		options.Includes = []string{"."}
 	}
@@ -228,7 +228,7 @@
 	compression := DetectCompression(buf)
 
 	utils.Debugf("Archive compression detected: %s", compression.Extension())
-	args := []string{"-S", "--numeric-owner", "-f", "-", "-C", path, "-x" + compression.Flag()}
+	args := []string{"--numeric-owner", "-f", "-", "-C", path, "-x" + compression.Flag()}
 
 	if options != nil {
 		for _, exclude := range options.Excludes {
@@ -299,7 +299,7 @@
 //
 // If `dst` ends with a trailing slash '/', the final destination path
 // will be `dst/base(src)`.
-func CopyFileWithTar(src, dst string) error {
+func CopyFileWithTar(src, dst string) (err error) {
 	utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
 	srcSt, err := os.Stat(src)
 	if err != nil {
@@ -316,25 +316,38 @@
 	if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
 		return err
 	}
-	buf := new(bytes.Buffer)
-	tw := tar.NewWriter(buf)
-	hdr, err := tar.FileInfoHeader(srcSt, "")
-	if err != nil {
-		return err
-	}
-	hdr.Name = filepath.Base(dst)
-	if err := tw.WriteHeader(hdr); err != nil {
-		return err
-	}
-	srcF, err := os.Open(src)
-	if err != nil {
-		return err
-	}
-	if _, err := io.Copy(tw, srcF); err != nil {
-		return err
-	}
-	tw.Close()
-	return Untar(buf, filepath.Dir(dst), nil)
+
+	r, w := io.Pipe()
+	errC := utils.Go(func() error {
+		defer w.Close()
+
+		srcF, err := os.Open(src)
+		if err != nil {
+			return err
+		}
+		defer srcF.Close()
+
+		tw := tar.NewWriter(w)
+		hdr, err := tar.FileInfoHeader(srcSt, "")
+		if err != nil {
+			return err
+		}
+		hdr.Name = filepath.Base(dst)
+		if err := tw.WriteHeader(hdr); err != nil {
+			return err
+		}
+		if _, err := io.Copy(tw, srcF); err != nil {
+			return err
+		}
+		tw.Close()
+		return nil
+	})
+	defer func() {
+		if er := <-errC; err != nil {
+			err = er
+		}
+	}()
+	return Untar(r, filepath.Dir(dst), nil)
 }
 
 // CmdStream executes a command, and returns its stdout as a stream.
diff --git a/docs/sources/installation/amazon.rst b/docs/sources/installation/amazon.rst
index 273eceb..e8fdc2c 100644
--- a/docs/sources/installation/amazon.rst
+++ b/docs/sources/installation/amazon.rst
@@ -26,18 +26,13 @@
      <https://console.aws.amazon.com/ec2/v2/home?#LaunchInstanceWizard:>`_ menu
      on your AWS Console.
 
-   * When picking the source AMI for your instance type, select "Community
-     AMIs".
+   * Click the ``Select`` button for a 64Bit Ubuntu image. For example: Ubuntu Server 12.04.3 LTS
 
-   * Search for ``amd64 precise``. Pick one of the amd64 Ubuntu images.
-
-   * If you choose a EBS enabled AMI, you'll also be able to launch a
+   * For testing you can use the default (possibly free)
      ``t1.micro`` instance (more info on `pricing
-     <http://aws.amazon.com/en/ec2/pricing/>`_).  ``t1.micro`` instances are
-     eligible for Amazon's Free Usage Tier.
+     <http://aws.amazon.com/en/ec2/pricing/>`_).
 
-   * When you click select you'll be taken to the instance setup, and you're one
-     click away from having your Ubuntu VM up and running.
+   * Click the ``Next: Configure Instance Details`` button at the bottom right.
 
 2. **Tell CloudInit to install Docker:**
 
diff --git a/docs/sources/installation/fedora.rst b/docs/sources/installation/fedora.rst
index de296b4..6dd2bf9 100644
--- a/docs/sources/installation/fedora.rst
+++ b/docs/sources/installation/fedora.rst
@@ -67,7 +67,7 @@
 
 .. code-block:: bash
 
-   sudo docker run -i -t mattdm/fedora /bin/bash
+   sudo docker run -i -t fedora /bin/bash
 
 **Done!**, now continue with the :ref:`hello_world` example.
 
diff --git a/docs/sources/installation/google.rst b/docs/sources/installation/google.rst
index ff38e1e..38aab01 100644
--- a/docs/sources/installation/google.rst
+++ b/docs/sources/installation/google.rst
@@ -57,12 +57,13 @@
     docker-playground:~$ curl get.docker.io | bash
     docker-playground:~$ sudo update-rc.d docker defaults
 
-7. If running in zones: us-central1-a, europe-west1-1, and europe-west1-b, the docker daemon must be started with the `-mtu` flag. Without the flag, you may experience intermittent network pauses. 
+7. If running in zones: ``us-central1-a``, ``europe-west1-1``, and ``europe-west1-b``, the docker daemon must be started with the ``-mtu`` flag. Without the flag, you may experience intermittent network pauses. 
 `See this issue <https://code.google.com/p/google-compute-engine/issues/detail?id=57>`_ for more details.
 
 .. code-block:: bash
 
-    docker -d -mtu 1460
+    docker-playground:~$ echo "DOCKER_OPTS="$DOCKER_OPTS -mtu 1460" | sudo tee -a /etc/defaults/docker
+    docker-playground:~$ sudo service docker restart
 
 8. Start a new container:
 
diff --git a/docs/sources/installation/rhel.rst b/docs/sources/installation/rhel.rst
index b928b33..9036fb7 100644
--- a/docs/sources/installation/rhel.rst
+++ b/docs/sources/installation/rhel.rst
@@ -65,7 +65,7 @@
 
 .. code-block:: bash
 
-   sudo docker run -i -t mattdm/fedora /bin/bash
+   sudo docker run -i -t fedora /bin/bash
 
 **Done!**, now continue with the :ref:`hello_world` example.
 
diff --git a/docs/sources/installation/ubuntulinux.rst b/docs/sources/installation/ubuntulinux.rst
index 00400a9..e4432c6 100644
--- a/docs/sources/installation/ubuntulinux.rst
+++ b/docs/sources/installation/ubuntulinux.rst
@@ -17,7 +17,7 @@
 Docker is supported on the following versions of Ubuntu:
 
 - :ref:`ubuntu_precise`
-- :ref:`ubuntu_raring`
+- :ref:`ubuntu_raring_saucy`
 
 Please read :ref:`ufw`, if you plan to use `UFW (Uncomplicated
 Firewall) <https://help.ubuntu.com/community/UFW>`_
@@ -108,10 +108,12 @@
 
 **Done!**, now continue with the :ref:`hello_world` example.
 
-.. _ubuntu_raring:
+.. _ubuntu_raring_saucy:
 
-Ubuntu Raring 13.04 (64 bit)
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Ubuntu Raring 13.04 and Saucy 13.10 (64 bit)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+These instructions cover both Ubuntu Raring 13.04 and Saucy 13.10.
 
 Dependencies
 ------------
@@ -169,7 +171,6 @@
 
 **Done!**, now continue with the :ref:`hello_world` example.
 
-
 .. _ufw:
 
 Docker and UFW
diff --git a/docs/sources/use/networking.rst b/docs/sources/use/networking.rst
index 0e81440..4e75fbc 100644
--- a/docs/sources/use/networking.rst
+++ b/docs/sources/use/networking.rst
@@ -82,7 +82,7 @@
     $ sudo ifconfig bridge0 192.168.227.1 netmask 255.255.255.0
 
     # Edit your Docker startup file
-    $ echo "DOCKER_OPTS=\"-b=bridge0\"" /etc/default/docker 
+    $ echo "DOCKER_OPTS=\"-b=bridge0\"" >> /etc/default/docker 
     
     # Start Docker 
     $ sudo service docker start
diff --git a/docs/theme/docker/static/js/docs.js b/docs/theme/docker/static/js/docs.js
index 4151ea5..0340190 100755
--- a/docs/theme/docker/static/js/docs.js
+++ b/docs/theme/docker/static/js/docs.js
@@ -92,7 +92,7 @@
         $('.version-flyer ul').html('<li class="alternative active-slug"><a href="" title="Switch to local">Local</a></li>');
     }
 
-    if (doc_version == "master") {
+    if (doc_version == "latest") {
         $('.version-flyer .version-note').hide();
     }
 
diff --git a/engine/http.go b/engine/http.go
index 6391b3f..b115912 100644
--- a/engine/http.go
+++ b/engine/http.go
@@ -1,8 +1,8 @@
 package engine
 
 import (
-	"path"
 	"net/http"
+	"path"
 )
 
 // ServeHTTP executes a job as specified by the http request `r`, and sends the
@@ -22,7 +22,7 @@
 		jobArgs = []string{}
 	}
 	w.Header().Set("Job-Name", jobName)
-	for _, arg := range(jobArgs) {
+	for _, arg := range jobArgs {
 		w.Header().Add("Job-Args", arg)
 	}
 	job := eng.Job(jobName, jobArgs...)
diff --git a/graph.go b/graph.go
index 058ef7e..176626d 100644
--- a/graph.go
+++ b/graph.go
@@ -87,17 +87,17 @@
 	if err != nil {
 		return nil, err
 	}
-	// Check that the filesystem layer exists
-	rootfs, err := graph.driver.Get(img.ID)
-	if err != nil {
-		return nil, fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
-	}
 	if img.ID != id {
 		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
 	}
 	img.graph = graph
 
 	if img.Size < 0 {
+		rootfs, err := graph.driver.Get(img.ID)
+		if err != nil {
+			return nil, fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
+		}
+
 		var size int64
 		if img.Parent == "" {
 			if size, err = utils.TreeSize(rootfs); err != nil {
diff --git a/hack/travis/dco.py b/hack/travis/dco.py
new file mode 100755
index 0000000..cab26fe
--- /dev/null
+++ b/hack/travis/dco.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+import re
+import subprocess
+import yaml
+
+from env import commit_range
+
+commit_format = '-%n hash: "%h"%n author: %aN <%aE>%n message: |%n%w(0,2,2)%B'
+
+gitlog = subprocess.check_output([
+	'git', 'log', '--reverse',
+	'--format=format:'+commit_format,
+	'..'.join(commit_range), '--',
+])
+
+commits = yaml.load(gitlog)
+if not commits:
+	exit(0) # what?  how can we have no commits?
+
+DCO = 'Docker-DCO-1.0-Signed-off-by:'
+
+p = re.compile(r'^{0} ([^<]+) <([^<>@]+@[^<>]+)> \(github: (\S+)\)$'.format(re.escape(DCO)), re.MULTILINE|re.UNICODE)
+
+failed_commits = 0
+
+for commit in commits:
+	commit['stat'] = subprocess.check_output([
+		'git', 'log', '--format=format:', '--max-count=1',
+		'--name-status', commit['hash'], '--',
+	])
+	if commit['stat'] == '':
+		print 'Commit {0} has no actual changed content, skipping.'.format(commit['hash'])
+		continue
+	
+	m = p.search(commit['message'])
+	if not m:
+		print 'Commit {1} does not have a properly formatted "{0}" marker.'.format(DCO, commit['hash'])
+		failed_commits += 1
+		continue # print ALL the commits that don't have a proper DCO
+	
+	(name, email, github) = m.groups()
+	
+	# TODO verify that "github" is the person who actually made this commit via the GitHub API
+
+if failed_commits > 0:
+	exit(failed_commits)
+
+print 'All commits have a valid "{0}" marker.'.format(DCO)
+exit(0)
diff --git a/hack/travis/env.py b/hack/travis/env.py
new file mode 100644
index 0000000..86d90f1
--- /dev/null
+++ b/hack/travis/env.py
@@ -0,0 +1,21 @@
+import os
+import subprocess
+
+if 'TRAVIS' not in os.environ:
+	print 'TRAVIS is not defined; this should run in TRAVIS. Sorry.'
+	exit(127)
+
+if os.environ['TRAVIS_PULL_REQUEST'] != 'false':
+	commit_range = [os.environ['TRAVIS_BRANCH'], 'FETCH_HEAD']
+else:
+	try:
+		subprocess.check_call([
+			'git', 'log', '-1', '--format=format:',
+			os.environ['TRAVIS_COMMIT_RANGE'], '--',
+		])
+		commit_range = os.environ['TRAVIS_COMMIT_RANGE'].split('...')
+		if len(commit_range) == 1: # if it didn't split, it must have been separated by '..' instead
+			commit_range = commit_range[0].split('..')
+	except subprocess.CalledProcessError:
+		print 'TRAVIS_COMMIT_RANGE is invalid. This seems to be a force push. We will just assume it must be against upstream master and compare all commits in between.'
+		commit_range = ['upstream/master', 'HEAD']
diff --git a/hack/travis/gofmt.py b/hack/travis/gofmt.py
new file mode 100755
index 0000000..1bb062e
--- /dev/null
+++ b/hack/travis/gofmt.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+import subprocess
+
+from env import commit_range
+
+files = subprocess.check_output([
+	'git', 'diff', '--diff-filter=ACMR',
+	'--name-only', '...'.join(commit_range), '--',
+])
+
+exit_status = 0
+
+for filename in files.split('\n'):
+	if filename.endswith('.go'):
+		try:
+			out = subprocess.check_output(['gofmt', '-s', '-l', filename])
+			if out != '':
+				print out,
+				exit_status = 1
+		except subprocess.CalledProcessError:
+			exit_status = 1
+
+if exit_status != 0:
+	print 'Reformat the files listed above with "gofmt -s -w" and try again.'
+	exit(exit_status)
+
+print 'All files pass gofmt.'
+exit(0)
diff --git a/integration/buildfile_test.go b/integration/buildfile_test.go
index efbdc54..ef51777 100644
--- a/integration/buildfile_test.go
+++ b/integration/buildfile_test.go
@@ -132,6 +132,23 @@
 		[][2]string{{"/x", "hello"}, {"/", "blah"}},
 	},
 
+	// Comments, shebangs, and executability, oh my!
+	{
+		`
+FROM {IMAGE}
+# This is an ordinary comment.
+RUN { echo '#!/bin/sh'; echo 'echo hello world'; } > /hello.sh
+RUN [ ! -x /hello.sh ]
+RUN chmod +x /hello.sh
+RUN [ -x /hello.sh ]
+RUN [ "$(cat /hello.sh)" = $'#!/bin/sh\necho hello world' ]
+RUN [ "$(/hello.sh)" = "hello world" ]
+`,
+		nil,
+		nil,
+	},
+
+	// Environment variable
 	{
 		`
 from   {IMAGE}
@@ -142,6 +159,19 @@
 		nil,
 	},
 
+	// Environment overwriting
+	{
+		`
+from   {IMAGE}
+env    FOO BAR
+run    [ "$FOO" = "BAR" ]
+env    FOO BAZ
+run    [ "$FOO" = "BAZ" ]
+`,
+		nil,
+		nil,
+	},
+
 	{
 		`
 from {IMAGE}
diff --git a/integration/iptables_test.go b/integration/iptables_test.go
index 060d0fe..1dd4194 100644
--- a/integration/iptables_test.go
+++ b/integration/iptables_test.go
@@ -1,7 +1,7 @@
 package docker
 
 import (
-	"github.com/dotcloud/docker/iptables"
+	"github.com/dotcloud/docker/pkg/iptables"
 	"os"
 	"testing"
 )
diff --git a/links.go b/links.go
index 2fe255b..55834b9 100644
--- a/links.go
+++ b/links.go
@@ -2,7 +2,7 @@
 
 import (
 	"fmt"
-	"github.com/dotcloud/docker/iptables"
+	"github.com/dotcloud/docker/pkg/iptables"
 	"path"
 	"strings"
 )
diff --git a/network.go b/network.go
index 5ee5c4b..22ea8ba 100644
--- a/network.go
+++ b/network.go
@@ -4,7 +4,7 @@
 	"encoding/binary"
 	"errors"
 	"fmt"
-	"github.com/dotcloud/docker/iptables"
+	"github.com/dotcloud/docker/pkg/iptables"
 	"github.com/dotcloud/docker/pkg/netlink"
 	"github.com/dotcloud/docker/proxy"
 	"github.com/dotcloud/docker/utils"
@@ -248,12 +248,12 @@
 }
 
 func (mapper *PortMapper) Map(ip net.IP, port int, backendAddr net.Addr) error {
-	mapKey := (&net.TCPAddr{Port: port, IP: ip}).String()
-	if _, exists := mapper.tcpProxies[mapKey]; exists {
-		return fmt.Errorf("Port %s is already in use", mapKey)
-	}
 
 	if _, isTCP := backendAddr.(*net.TCPAddr); isTCP {
+		mapKey := (&net.TCPAddr{Port: port, IP: ip}).String()
+		if _, exists := mapper.tcpProxies[mapKey]; exists {
+			return fmt.Errorf("TCP Port %s is already in use", mapKey)
+		}
 		backendPort := backendAddr.(*net.TCPAddr).Port
 		backendIP := backendAddr.(*net.TCPAddr).IP
 		if mapper.iptables != nil {
@@ -270,6 +270,10 @@
 		mapper.tcpProxies[mapKey] = proxy
 		go proxy.Run()
 	} else {
+		mapKey := (&net.UDPAddr{Port: port, IP: ip}).String()
+		if _, exists := mapper.udpProxies[mapKey]; exists {
+			return fmt.Errorf("UDP: Port %s is already in use", mapKey)
+		}
 		backendPort := backendAddr.(*net.UDPAddr).Port
 		backendIP := backendAddr.(*net.UDPAddr).IP
 		if mapper.iptables != nil {
@@ -290,8 +294,8 @@
 }
 
 func (mapper *PortMapper) Unmap(ip net.IP, port int, proto string) error {
-	mapKey := (&net.TCPAddr{Port: port, IP: ip}).String()
 	if proto == "tcp" {
+		mapKey := (&net.TCPAddr{Port: port, IP: ip}).String()
 		backendAddr, ok := mapper.tcpMapping[mapKey]
 		if !ok {
 			return fmt.Errorf("Port tcp/%s is not mapped", mapKey)
@@ -307,6 +311,7 @@
 		}
 		delete(mapper.tcpMapping, mapKey)
 	} else {
+		mapKey := (&net.UDPAddr{Port: port, IP: ip}).String()
 		backendAddr, ok := mapper.udpMapping[mapKey]
 		if !ok {
 			return fmt.Errorf("Port udp/%s is not mapped", mapKey)
diff --git a/network_test.go b/network_test.go
index 184b497..69fcba0 100644
--- a/network_test.go
+++ b/network_test.go
@@ -1,7 +1,7 @@
 package docker
 
 import (
-	"github.com/dotcloud/docker/iptables"
+	"github.com/dotcloud/docker/pkg/iptables"
 	"github.com/dotcloud/docker/proxy"
 	"net"
 	"testing"
@@ -340,6 +340,7 @@
 }
 
 func TestPortMapper(t *testing.T) {
+	// FIXME: is this iptables chain still used anywhere?
 	var chain *iptables.Chain
 	mapper := &PortMapper{
 		tcpMapping:       make(map[string]*net.TCPAddr),
diff --git a/iptables/MAINTAINERS b/pkg/iptables/MAINTAINERS
similarity index 100%
rename from iptables/MAINTAINERS
rename to pkg/iptables/MAINTAINERS
diff --git a/iptables/iptables.go b/pkg/iptables/iptables.go
similarity index 100%
rename from iptables/iptables.go
rename to pkg/iptables/iptables.go
diff --git a/reflink_copy_darwin.go b/reflink_copy_darwin.go
index 3f3147d..4f0ea8c 100644
--- a/reflink_copy_darwin.go
+++ b/reflink_copy_darwin.go
@@ -1,8 +1,8 @@
 package docker
 
 import (
-	"os"
 	"io"
+	"os"
 )
 
 func CopyFile(dstFile, srcFile *os.File) error {
diff --git a/reflink_copy_linux.go b/reflink_copy_linux.go
index 8aae3ab..83c7f75 100644
--- a/reflink_copy_linux.go
+++ b/reflink_copy_linux.go
@@ -25,8 +25,8 @@
 import "C"
 
 import (
-	"os"
 	"io"
+	"os"
 	"syscall"
 )
 
diff --git a/registry/registry.go b/registry/registry.go
index 3c793cf..a038fdf 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -25,11 +25,11 @@
 	ErrLoginRequired         = errors.New("Authentication is required.")
 )
 
-func pingRegistryEndpoint(endpoint string) error {
+func pingRegistryEndpoint(endpoint string) (bool, error) {
 	if endpoint == auth.IndexServerAddress() {
 		// Skip the check, we now this one is valid
 		// (and we never want to fallback to http in case of error)
-		return nil
+		return false, nil
 	}
 	httpDial := func(proto string, addr string) (net.Conn, error) {
 		// Set the connect timeout to 5 seconds
@@ -45,14 +45,26 @@
 	client := &http.Client{Transport: httpTransport}
 	resp, err := client.Get(endpoint + "_ping")
 	if err != nil {
-		return err
+		return false, err
 	}
 	defer resp.Body.Close()
 
 	if resp.Header.Get("X-Docker-Registry-Version") == "" {
-		return errors.New("This does not look like a Registry server (\"X-Docker-Registry-Version\" header not found in the response)")
+		return false, errors.New("This does not look like a Registry server (\"X-Docker-Registry-Version\" header not found in the response)")
 	}
-	return nil
+
+	standalone := resp.Header.Get("X-Docker-Registry-Standalone")
+	utils.Debugf("Registry standalone header: '%s'", standalone)
+	// If the header is absent, we assume true for compatibility with earlier
+	// versions of the registry
+	if standalone == "" {
+		return true, nil
+	// Accepted values are "true" (case-insensitive) and "1".
+	} else if strings.EqualFold(standalone, "true") || standalone == "1" {
+		return true, nil
+	}
+	// Otherwise, not standalone
+	return false, nil
 }
 
 func validateRepositoryName(repositoryName string) error {
@@ -122,16 +134,16 @@
 			// there is no path given. Expand with default path
 			hostname = hostname + "/v1/"
 		}
-		if err := pingRegistryEndpoint(hostname); err != nil {
+		if _, err := pingRegistryEndpoint(hostname); err != nil {
 			return "", errors.New("Invalid Registry endpoint: " + err.Error())
 		}
 		return hostname, nil
 	}
 	endpoint := fmt.Sprintf("https://%s/v1/", hostname)
-	if err := pingRegistryEndpoint(endpoint); err != nil {
+	if _, err := pingRegistryEndpoint(endpoint); err != nil {
 		utils.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)
 		endpoint = fmt.Sprintf("http://%s/v1/", hostname)
-		if err = pingRegistryEndpoint(endpoint); err != nil {
+		if _, err = pingRegistryEndpoint(endpoint); err != nil {
 			//TODO: triggering highland build can be done there without "failing"
 			return "", errors.New("Invalid Registry endpoint: " + err.Error())
 		}
@@ -682,12 +694,18 @@
 		return nil, err
 	}
 
-	// If we're working with a private registry over HTTPS, send Basic Auth headers
+	// If we're working with a standalone private registry over HTTPS, send Basic Auth headers
 	// alongside our requests.
 	if indexEndpoint != auth.IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
-		utils.Debugf("Endpoint %s is eligible for private registry auth. Enabling decorator.", indexEndpoint)
-		dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password)
-		factory.AddDecorator(dec)
+		standalone, err := pingRegistryEndpoint(indexEndpoint)
+		if err != nil {
+			return nil, err
+		}
+		if standalone {
+			utils.Debugf("Endpoint %s is eligible for private registry auth. Enabling decorator.", indexEndpoint)
+			dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password)
+			factory.AddDecorator(dec)
+		}
 	}
 
 	r.reqFactory = factory
diff --git a/registry/registry_test.go b/registry/registry_test.go
index 69eb25b..16bc431 100644
--- a/registry/registry_test.go
+++ b/registry/registry_test.go
@@ -23,10 +23,11 @@
 }
 
 func TestPingRegistryEndpoint(t *testing.T) {
-	err := pingRegistryEndpoint(makeURL("/v1/"))
+	standalone, err := pingRegistryEndpoint(makeURL("/v1/"))
 	if err != nil {
 		t.Fatal(err)
 	}
+	assertEqual(t, standalone, true, "Expected standalone to be true (default)")
 }
 
 func TestGetRemoteHistory(t *testing.T) {
diff --git a/runtime.go b/runtime.go
index b1d8988..731e3a8 100644
--- a/runtime.go
+++ b/runtime.go
@@ -583,11 +583,6 @@
 	return img, nil
 }
 
-// FIXME: this is deprecated by the getFullName *function*
-func (runtime *Runtime) getFullName(name string) (string, error) {
-	return getFullName(name)
-}
-
 func getFullName(name string) (string, error) {
 	if name == "" {
 		return "", fmt.Errorf("Container name cannot be empty")
@@ -599,7 +594,7 @@
 }
 
 func (runtime *Runtime) GetByName(name string) (*Container, error) {
-	fullName, err := runtime.getFullName(name)
+	fullName, err := getFullName(name)
 	if err != nil {
 		return nil, err
 	}
@@ -615,7 +610,7 @@
 }
 
 func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
-	name, err := runtime.getFullName(name)
+	name, err := getFullName(name)
 	if err != nil {
 		return nil, err
 	}
diff --git a/server.go b/server.go
index c9c10fe..2405380 100644
--- a/server.go
+++ b/server.go
@@ -1438,7 +1438,7 @@
 		if container == nil {
 			return fmt.Errorf("No such link: %s", name)
 		}
-		name, err := srv.runtime.getFullName(name)
+		name, err := getFullName(name)
 		if err != nil {
 			return err
 		}
diff --git a/utils.go b/utils.go
index 5bcc678..3eb1eac 100644
--- a/utils.go
+++ b/utils.go
@@ -322,7 +322,6 @@
 	return nil
 }
 
-
 // Links come in the format of
 // name:alias
 func parseLink(rawLink string) (map[string]string, error) {
diff --git a/utils/utils.go b/utils/utils.go
index 25c8012..d5dbf35 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -781,7 +781,7 @@
 		host  string
 		port  int
 	)
-  addr = strings.TrimSpace(addr)
+	addr = strings.TrimSpace(addr)
 	switch {
 	case strings.HasPrefix(addr, "unix://"):
 		proto = "unix"
diff --git a/utils/utils_test.go b/utils/utils_test.go
index bd3cf22..1f23755 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -316,9 +316,9 @@
 	if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "tcp://:7777"); err != nil || addr != "tcp://127.0.0.1:7777" {
 		t.Errorf("tcp://:7777 -> expected tcp://127.0.0.1:7777, got %s", addr)
 	}
-  if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, ""); err != nil || addr != "unix:///var/run/docker.sock" {
-    t.Errorf("empty argument -> expected unix:///var/run/docker.sock, got %s", addr)
-  }
+	if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, ""); err != nil || addr != "unix:///var/run/docker.sock" {
+		t.Errorf("empty argument -> expected unix:///var/run/docker.sock, got %s", addr)
+	}
 	if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "unix:///var/run/docker.sock"); err != nil || addr != "unix:///var/run/docker.sock" {
 		t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr)
 	}