Merge branch 'tianon-separate-dockerinit' into dm

Conflicts:
	runtime_test.go
diff --git a/Dockerfile b/Dockerfile
index 67963c8..9852609 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -24,46 +24,58 @@
 #
 
 docker-version 0.6.1
-from	ubuntu:12.04
-maintainer	Solomon Hykes <solomon@dotcloud.com>
+from    ubuntu:12.10
+maintainer      Solomon Hykes <solomon@dotcloud.com>
 
 # Build dependencies
-run	echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt/sources.list
-run	apt-get update
-run	apt-get install -y -q curl
-run	apt-get install -y -q git
-run	apt-get install -y -q mercurial
-run	apt-get install -y -q build-essential
+run     apt-get update
+run     apt-get install -y -q curl
+run     apt-get install -y -q git
+run     apt-get install -y -q mercurial
+run     apt-get install -y -q build-essential
+run     apt-get install -y -q libsqlite3-dev
 
 # Install Go from source (for eventual cross-compiling)
-env	CGO_ENABLED 0
-run	curl -s https://go.googlecode.com/files/go1.1.2.src.tar.gz | tar -v -C / -xz && mv /go /goroot
-run	cd /goroot/src && ./make.bash
-env GOROOT	/goroot
-env	PATH	$PATH:/goroot/bin
-env	GOPATH	/go:/go/src/github.com/dotcloud/docker/vendor
+run     curl -s https://go.googlecode.com/files/go1.2rc1.src.tar.gz | tar -v -C / -xz && mv /go /goroot
+run     cd /goroot/src && ./make.bash
+env     GOROOT  /goroot
+env     PATH    $PATH:/goroot/bin
+env     GOPATH  /go:/go/src/github.com/dotcloud/docker/vendor
+
+# Create Go cache with tag netgo (for static compilation of Go while preserving CGO support)
+run     go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
+
+# Get lvm2 source for compiling statically
+run     git clone git://git.fedorahosted.org/git/lvm2.git /lvm2
+run     cd /lvm2 && git checkout v2_02_102
+
+# can't use git clone -b because it's not supported by git versions before 1.7.10
+run	cd /lvm2 && ./configure --enable-static_link && make && make install_device-mapper
+# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
 
 # Ubuntu stuff
-run	apt-get install -y -q ruby1.9.3 rubygems libffi-dev
-run	gem install --no-rdoc --no-ri fpm
-run	apt-get install -y -q reprepro dpkg-sig
+run     apt-get install -y -q ruby1.9.3 rubygems libffi-dev
+run     gem install --no-rdoc --no-ri fpm
+run     apt-get install -y -q reprepro dpkg-sig
 
 # Install s3cmd 1.0.1 (earlier versions don't support env variables in the config)
-run	apt-get install -y -q python-pip
-run	pip install s3cmd
-run	pip install python-magic
-run	/bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg
+run     apt-get install -y -q python-pip
+run     pip install s3cmd
+run     pip install python-magic
+run     /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg
 
 # Runtime dependencies
-run	apt-get install -y -q iptables
-run	apt-get install -y -q lxc
-run	apt-get install -y -q aufs-tools
+run     apt-get install -y -q iptables
+run     dpkg-divert --local --rename --add /sbin/initctl && \
+        ln -s /bin/true /sbin/initctl && \
+        apt-get install -y -q lxc
 
-volume	/var/lib/docker
-workdir	/go/src/github.com/dotcloud/docker
+volume  /var/lib/docker
+workdir /go/src/github.com/dotcloud/docker
 
 # Wrap all commands in the "docker-in-docker" script to allow nested containers
 entrypoint ["hack/dind"]
 
 # Upload docker source
-add	.       /go/src/github.com/dotcloud/docker
+add     .		/go/src/github.com/dotcloud/docker
+
diff --git a/api.go b/api.go
index ad30833..ec0a6f8 100644
--- a/api.go
+++ b/api.go
@@ -6,6 +6,7 @@
 	"encoding/json"
 	"fmt"
 	"github.com/dotcloud/docker/auth"
+	"github.com/dotcloud/docker/gograph"
 	"github.com/dotcloud/docker/utils"
 	"github.com/gorilla/mux"
 	"io"
@@ -69,12 +70,12 @@
 		statusCode = http.StatusUnauthorized
 	} else if strings.Contains(err.Error(), "hasn't been activated") {
 		statusCode = http.StatusForbidden
-	}	
-	
+	}
+
 	if err != nil {
 		utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
-		http.Error(w, err.Error(), statusCode)		
-	}	
+		http.Error(w, err.Error(), statusCode)
+	}
 }
 
 func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
@@ -515,16 +516,19 @@
 		return err
 	}
 
-	if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
+	if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
 		out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
 		config.Dns = defaultDns
 	}
 
-	id, err := srv.ContainerCreate(config)
+	id, warnings, err := srv.ContainerCreate(config)
 	if err != nil {
 		return err
 	}
 	out.ID = id
+	for _, warning := range warnings {
+		out.Warnings = append(out.Warnings, warning)
+	}
 
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
@@ -570,12 +574,17 @@
 		return fmt.Errorf("Missing parameter")
 	}
 	name := vars["name"]
+
 	removeVolume, err := getBoolParam(r.Form.Get("v"))
 	if err != nil {
 		return err
 	}
+	removeLink, err := getBoolParam(r.Form.Get("link"))
+	if err != nil {
+		return err
+	}
 
-	if err := srv.ContainerDestroy(name, removeVolume); err != nil {
+	if err := srv.ContainerDestroy(name, removeVolume, removeLink); err != nil {
 		return err
 	}
 	w.WriteHeader(http.StatusNoContent)
@@ -623,6 +632,7 @@
 	}
 	name := vars["name"]
 	if err := srv.ContainerStart(name, hostConfig); err != nil {
+		utils.Debugf("error ContainerStart: %s", err)
 		return err
 	}
 	w.WriteHeader(http.StatusNoContent)
@@ -655,6 +665,7 @@
 		return fmt.Errorf("Missing parameter")
 	}
 	name := vars["name"]
+
 	status, err := srv.ContainerWait(name)
 	if err != nil {
 		return err
@@ -975,7 +986,7 @@
 		if err != nil {
 			version = APIVERSION
 		}
-		if srv.enableCors {
+		if srv.runtime.config.EnableCors {
 			writeCorsHeaders(w, r)
 		}
 
@@ -991,6 +1002,73 @@
 	}
 }
 
+func getContainersLinks(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if err := parseForm(r); err != nil {
+		return err
+	}
+
+	runtime := srv.runtime
+	all, err := getBoolParam(r.Form.Get("all"))
+	if err != nil {
+		return err
+	}
+
+	out := []APILink{}
+	err = runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
+		if container := runtime.Get(e.ID()); container != nil {
+			if !all && strings.Contains(p, container.ID) {
+				return nil
+			}
+			out = append(out, APILink{
+				Path:        p,
+				ContainerID: container.ID,
+				Image:       runtime.repositories.ImageName(container.Image),
+			})
+		}
+		return nil
+	}, -1)
+
+	if err != nil {
+		return err
+	}
+	return writeJSON(w, http.StatusOK, out)
+}
+
+func postContainerLink(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if vars == nil {
+		return fmt.Errorf("Missing parameter")
+	}
+	values := make(map[string]string)
+	if matchesContentType(r.Header.Get("Content-Type"), "application/json") && r.Body != nil {
+		defer r.Body.Close()
+
+		dec := json.NewDecoder(r.Body)
+		if err := dec.Decode(&values); err != nil {
+			return err
+		}
+	} else {
+		return fmt.Errorf("Invalid json body")
+	}
+	currentName := values["currentName"]
+	newName := values["newName"]
+
+	if currentName == "" {
+		return fmt.Errorf("currentName cannot be empty")
+	}
+	if newName == "" {
+		return fmt.Errorf("newName cannot be empty")
+	}
+
+	if err := srv.runtime.RenameLink(currentName, newName); err != nil {
+		if strings.HasSuffix(err.Error(), "name are not unique") {
+			return fmt.Errorf("Conflict, %s already exists", newName)
+		}
+		return err
+	}
+
+	return nil
+}
+
 func createRouter(srv *Server, logging bool) (*mux.Router, error) {
 	r := mux.NewRouter()
 
@@ -1011,6 +1089,7 @@
 			"/containers/{name:.*}/json":      getContainersByName,
 			"/containers/{name:.*}/top":       getContainersTop,
 			"/containers/{name:.*}/attach/ws": wsContainersAttach,
+			"/containers/links":               getContainersLinks,
 		},
 		"POST": {
 			"/auth":                         postAuth,
@@ -1029,6 +1108,7 @@
 			"/containers/{name:.*}/resize":  postContainersResize,
 			"/containers/{name:.*}/attach":  postContainersAttach,
 			"/containers/{name:.*}/copy":    postContainersCopy,
+			"/containers/link":              postContainerLink,
 		},
 		"DELETE": {
 			"/containers/{name:.*}": deleteContainers,
diff --git a/api_params.go b/api_params.go
index 6403bc6..acda3f7 100644
--- a/api_params.go
+++ b/api_params.go
@@ -1,7 +1,5 @@
 package docker
 
-import "encoding/json"
-
 type APIHistory struct {
 	ID        string   `json:"Id"`
 	Tags      []string `json:",omitempty"`
@@ -19,18 +17,23 @@
 }
 
 type APIInfo struct {
-	Debug              bool
-	Containers         int
-	Images             int
-	NFd                int    `json:",omitempty"`
-	NGoroutines        int    `json:",omitempty"`
-	MemoryLimit        bool   `json:",omitempty"`
-	SwapLimit          bool   `json:",omitempty"`
-	IPv4Forwarding     bool   `json:",omitempty"`
-	LXCVersion         string `json:",omitempty"`
-	NEventsListener    int    `json:",omitempty"`
-	KernelVersion      string `json:",omitempty"`
-	IndexServerAddress string `json:",omitempty"`
+	Debug                  bool
+	Containers             int
+	Images                 int
+	NFd                    int    `json:",omitempty"`
+	NGoroutines            int    `json:",omitempty"`
+	MemoryLimit            bool   `json:",omitempty"`
+	SwapLimit              bool   `json:",omitempty"`
+	IPv4Forwarding         bool   `json:",omitempty"`
+	LXCVersion             string `json:",omitempty"`
+	NEventsListener        int    `json:",omitempty"`
+	KernelVersion          string `json:",omitempty"`
+	IndexServerAddress     string `json:",omitempty"`
+	DevmapperPool          string `json:",omitempty"`
+	DevmapperDataUsed      uint64 `json:",omitempty"`
+	DevmapperDataTotal     uint64 `json:",omitempty"`
+	DevmapperMetadataUsed  uint64 `json:",omitempty"`
+	DevmapperMetadataTotal uint64 `json:",omitempty"`
 }
 
 type APITop struct {
@@ -52,17 +55,18 @@
 	Ports      []APIPort
 	SizeRw     int64
 	SizeRootFs int64
+	Names      []string
 }
 
 func (self *APIContainers) ToLegacy() APIContainersOld {
 	return APIContainersOld{
-		ID: self.ID,
-		Image: self.Image,
-		Command: self.Command,
-		Created: self.Created,
-		Status: self.Status,
-		Ports: displayablePorts(self.Ports),
-		SizeRw: self.SizeRw,
+		ID:         self.ID,
+		Image:      self.Image,
+		Command:    self.Command,
+		Created:    self.Created,
+		Status:     self.Status,
+		Ports:      displayablePorts(self.Ports),
+		SizeRw:     self.SizeRw,
 		SizeRootFs: self.SizeRootFs,
 	}
 }
@@ -96,14 +100,7 @@
 	PrivatePort int64
 	PublicPort  int64
 	Type        string
-}
-
-func (port *APIPort) MarshalJSON() ([]byte, error) {
-	return json.Marshal(map[string]interface{}{
-		"PrivatePort": port.PrivatePort,
-		"PublicPort":  port.PublicPort,
-		"Type":        port.Type,
-	})
+	IP          string
 }
 
 type APIVersion struct {
@@ -129,3 +126,9 @@
 	Resource string
 	HostPath string
 }
+
+type APILink struct {
+	Path        string
+	ContainerID string
+	Image       string
+}
diff --git a/api_test.go b/api_test.go
index 479b9e1..54cd0d9 100644
--- a/api_test.go
+++ b/api_test.go
@@ -347,7 +347,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"echo", "test"},
 	})
@@ -362,9 +362,11 @@
 	}
 
 	r := httptest.NewRecorder()
-	if err := getContainersJSON(srv, APIVERSION, r, req, nil); err != nil {
-		t.Fatal(err)
-	}
+	setTimeout(t, "getContainerJSON timed out", 5*time.Second, func() {
+		if err := getContainersJSON(srv, APIVERSION, r, req, nil); err != nil {
+			t.Fatal(err)
+		}
+	})
 	containers := []APIContainers{}
 	if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
 		t.Fatal(err)
@@ -384,7 +386,7 @@
 	srv := &Server{runtime: runtime}
 
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"touch", "/test"},
@@ -400,7 +402,7 @@
 	}
 
 	r := httptest.NewRecorder()
-	if err = getContainersExport(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
+	if err := getContainersExport(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 	}
 
@@ -434,7 +436,7 @@
 	srv := &Server{runtime: runtime}
 
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
@@ -477,7 +479,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/sh", "-c", "cat"},
@@ -559,7 +561,7 @@
 	srv := &Server{runtime: runtime}
 
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "test"},
@@ -590,7 +592,7 @@
 	srv := &Server{runtime: runtime}
 
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"touch", "/test"},
@@ -669,13 +671,21 @@
 		t.Fatal(err)
 	}
 
-	if _, err := os.Stat(path.Join(container.rwPath(), "test")); err != nil {
+	if err := container.EnsureMounted(); err != nil {
+		t.Fatalf("Unable to mount container: %s", err)
+	}
+
+	if _, err := os.Stat(path.Join(container.RootfsPath(), "test")); err != nil {
 		if os.IsNotExist(err) {
 			utils.Debugf("Err: %s", err)
 			t.Fatalf("The test file has not been created")
 		}
 		t.Fatal(err)
 	}
+
+	if err := container.Unmount(); err != nil {
+		t.Fatalf("Unable to unmount container: %s", err)
+	}
 }
 
 func TestPostContainersKill(t *testing.T) {
@@ -684,7 +694,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
@@ -726,7 +736,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
@@ -780,7 +790,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
@@ -832,7 +842,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
@@ -879,7 +889,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/sleep", "1"},
@@ -921,7 +931,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
@@ -1010,7 +1020,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/sh", "-c", "/bin/cat >&2"},
@@ -1102,7 +1112,7 @@
 
 	srv := &Server{runtime: runtime}
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"touch", "/test"},
 	})
@@ -1140,7 +1150,8 @@
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	srv := &Server{runtime: runtime, enableCors: true}
+	runtime.config.EnableCors = true
+	srv := &Server{runtime: runtime}
 
 	r := httptest.NewRecorder()
 	router, err := createRouter(srv, false)
@@ -1163,7 +1174,8 @@
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	srv := &Server{runtime: runtime, enableCors: true}
+	runtime.config.EnableCors = true
+	srv := &Server{runtime: runtime}
 
 	r := httptest.NewRecorder()
 
@@ -1290,7 +1302,7 @@
 	srv := &Server{runtime: runtime}
 
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"touch", "/test.txt"},
diff --git a/archive.go b/archive.go
index bb019fb..6ea436c 100644
--- a/archive.go
+++ b/archive.go
@@ -80,20 +80,74 @@
 // Tar creates an archive from the directory at `path`, and returns it as a
 // stream of bytes.
 func Tar(path string, compression Compression) (io.Reader, error) {
-	return TarFilter(path, compression, nil)
+	return TarFilter(path, compression, nil, true, nil)
+}
+
+func escapeName(name string) string {
+	escaped := make([]byte, 0)
+	for i, c := range []byte(name) {
+		if i == 0 && c == '/' {
+			continue
+		}
+		// all printable chars except "-" which is 0x2d
+		if (0x20 <= c && c <= 0x7E) && c != 0x2d {
+			escaped = append(escaped, c)
+		} else {
+			escaped = append(escaped, fmt.Sprintf("\\%03o", c)...)
+		}
+	}
+	return string(escaped)
 }
 
 // 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, compression Compression, filter []string) (io.Reader, error) {
-	args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path}
+func TarFilter(path string, compression Compression, filter []string, recursive bool, createFiles []string) (io.Reader, error) {
+	args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"}
 	if filter == nil {
 		filter = []string{"."}
 	}
-	for _, f := range filter {
-		args = append(args, "-c"+compression.Flag(), f)
+	args = append(args, "-c"+compression.Flag())
+
+	if !recursive {
+		args = append(args, "--no-recursion")
 	}
-	return CmdStream(exec.Command(args[0], args[1:]...))
+
+	files := ""
+	for _, f := range filter {
+		files = files + escapeName(f) + "\n"
+	}
+
+	tmpDir := ""
+
+	if createFiles != nil {
+		var err error // Can't use := here or we override the outer tmpDir
+		tmpDir, err = ioutil.TempDir("", "docker-tar")
+		if err != nil {
+			return nil, err
+		}
+
+		files = files + "-C" + tmpDir + "\n"
+		for _, f := range createFiles {
+			path := filepath.Join(tmpDir, f)
+			err := os.MkdirAll(filepath.Dir(path), 0600)
+			if err != nil {
+				return nil, err
+			}
+
+			if file, err := os.OpenFile(path, os.O_CREATE, 0600); err != nil {
+				return nil, err
+			} else {
+				file.Close()
+			}
+			files = files + escapeName(f) + "\n"
+		}
+	}
+
+	return CmdStream(exec.Command(args[0], args[1:]...), &files, func() {
+		if tmpDir != "" {
+			_ = os.RemoveAll(tmpDir)
+		}
+	})
 }
 
 // Untar reads a stream of bytes from `archive`, parses it as a tar archive,
@@ -140,7 +194,7 @@
 // TarUntar aborts and returns the error.
 func TarUntar(src string, filter []string, dst string) error {
 	utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
-	archive, err := TarFilter(src, Uncompressed, filter)
+	archive, err := TarFilter(src, Uncompressed, filter, true, nil)
 	if err != nil {
 		return err
 	}
@@ -227,13 +281,33 @@
 // CmdStream executes a command, and returns its stdout as a stream.
 // If the command fails to run or doesn't complete successfully, an error
 // will be returned, including anything written on stderr.
-func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
+func CmdStream(cmd *exec.Cmd, input *string, atEnd func()) (io.Reader, error) {
+	if input != nil {
+		stdin, err := cmd.StdinPipe()
+		if err != nil {
+			if atEnd != nil {
+				atEnd()
+			}
+			return nil, err
+		}
+		// Write stdin if any
+		go func() {
+			_, _ = stdin.Write([]byte(*input))
+			stdin.Close()
+		}()
+	}
 	stdout, err := cmd.StdoutPipe()
 	if err != nil {
+		if atEnd != nil {
+			atEnd()
+		}
 		return nil, err
 	}
 	stderr, err := cmd.StderrPipe()
 	if err != nil {
+		if atEnd != nil {
+			atEnd()
+		}
 		return nil, err
 	}
 	pipeR, pipeW := io.Pipe()
@@ -258,6 +332,9 @@
 		} else {
 			pipeW.Close()
 		}
+		if atEnd != nil {
+			atEnd()
+		}
 	}()
 	// Run the command and return the pipe
 	if err := cmd.Start(); err != nil {
diff --git a/archive_test.go b/archive_test.go
index 9a0a8e1..c86b451 100644
--- a/archive_test.go
+++ b/archive_test.go
@@ -14,7 +14,7 @@
 
 func TestCmdStreamLargeStderr(t *testing.T) {
 	cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
-	out, err := CmdStream(cmd)
+	out, err := CmdStream(cmd, nil, nil)
 	if err != nil {
 		t.Fatalf("Failed to start command: %s", err)
 	}
@@ -35,7 +35,7 @@
 
 func TestCmdStreamBad(t *testing.T) {
 	badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
-	out, err := CmdStream(badCmd)
+	out, err := CmdStream(badCmd, nil, nil)
 	if err != nil {
 		t.Fatalf("Failed to start command: %s", err)
 	}
@@ -50,7 +50,7 @@
 
 func TestCmdStreamGood(t *testing.T) {
 	cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
-	out, err := CmdStream(cmd)
+	out, err := CmdStream(cmd, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/buildfile.go b/buildfile.go
index 8231287..da94913 100644
--- a/buildfile.go
+++ b/buildfile.go
@@ -176,6 +176,8 @@
 func (b *buildFile) CmdCmd(args string) error {
 	var cmd []string
 	if err := json.Unmarshal([]byte(args), &cmd); err != nil {
+		// If the unmarshal fails, it is not an error, we just use the
+		// args as a string.
 		utils.Debugf("Error unmarshalling: %s, setting cmd to /bin/sh -c", err)
 		cmd = []string{"/bin/sh", "-c", args}
 	}
@@ -187,6 +189,9 @@
 }
 
 func (b *buildFile) CmdExpose(args string) error {
+	if strings.Contains(args, ":") {
+		return fmt.Errorf("EXPOSE cannot be used to bind to a host ip or port")
+	}
 	ports := strings.Split(args, " ")
 	b.config.PortSpecs = append(ports, b.config.PortSpecs...)
 	return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
@@ -296,7 +301,7 @@
 		}
 		// First try to unpack the source as an archive
 	} else if err := UntarPath(origPath, destPath); err != nil {
-		utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err)
+		utils.Debugf("[tar] Not a directory nor a tar archive. Copying as a file. Untar error from %s to %s: %s", origPath, destPath, err)
 		// If that fails, just copy it as a regular file
 		if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
 			return err
@@ -332,7 +337,7 @@
 
 	b.config.Image = b.image
 	// Create the container and start it
-	container, err := b.runtime.Create(b.config)
+	container, _, err := b.runtime.Create(b.config)
 	if err != nil {
 		return err
 	}
@@ -367,7 +372,7 @@
 	b.config.Image = b.image
 
 	// Create the container and start it
-	c, err := b.runtime.Create(b.config)
+	c, _, err := b.runtime.Create(b.config)
 	if err != nil {
 		return "", err
 	}
@@ -423,7 +428,7 @@
 			}
 		}
 
-		container, err := b.runtime.Create(b.config)
+		container, _, err := b.runtime.Create(b.config)
 		if err != nil {
 			return err
 		}
diff --git a/changes.go b/changes.go
index 43573cd..765dca7 100644
--- a/changes.go
+++ b/changes.go
@@ -5,6 +5,7 @@
 	"os"
 	"path/filepath"
 	"strings"
+	"syscall"
 )
 
 type ChangeType int
@@ -33,74 +34,283 @@
 	return fmt.Sprintf("%s %s", kind, change.Path)
 }
 
-func Changes(layers []string, rw string) ([]Change, error) {
+type FileInfo struct {
+	parent   *FileInfo
+	name     string
+	stat     syscall.Stat_t
+	children map[string]*FileInfo
+}
+
+func (root *FileInfo) LookUp(path string) *FileInfo {
+	parent := root
+	if path == "/" {
+		return root
+	}
+
+	pathElements := strings.Split(path, "/")
+	for _, elem := range pathElements {
+		if elem != "" {
+			child := parent.children[elem]
+			if child == nil {
+				return nil
+			}
+			parent = child
+		}
+	}
+	return parent
+}
+
+func (info *FileInfo) path() string {
+	if info.parent == nil {
+		return "/"
+	}
+	return filepath.Join(info.parent.path(), info.name)
+}
+
+func (info *FileInfo) unlink() {
+	if info.parent != nil {
+		delete(info.parent.children, info.name)
+	}
+}
+
+func (info *FileInfo) Remove(path string) bool {
+	child := info.LookUp(path)
+	if child != nil {
+		child.unlink()
+		return true
+	}
+	return false
+}
+
+func (info *FileInfo) isDir() bool {
+	return info.parent == nil || info.stat.Mode&syscall.S_IFDIR == syscall.S_IFDIR
+}
+
+func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
+	if oldInfo == nil {
+		// add
+		change := Change{
+			Path: info.path(),
+			Kind: ChangeAdd,
+		}
+		*changes = append(*changes, change)
+	}
+
+	// We make a copy so we can modify it to detect additions
+	// also, we only recurse on the old dir if the new info is a directory
+	// otherwise any previous delete/change is considered recursive
+	oldChildren := make(map[string]*FileInfo)
+	if oldInfo != nil && info.isDir() {
+		for k, v := range oldInfo.children {
+			oldChildren[k] = v
+		}
+	}
+
+	for name, newChild := range info.children {
+		oldChild, _ := oldChildren[name]
+		if oldChild != nil {
+			// change?
+			oldStat := &oldChild.stat
+			newStat := &newChild.stat
+			// Note: We can't compare inode or ctime or blocksize here, because these change
+			// when copying a file into a container. However, that is not generally a problem
+			// because any content change will change mtime, and any status change should
+			// be visible when actually comparing the stat fields. The only time this
+			// breaks down is if some code intentionally hides a change by setting
+			// back mtime
+			oldMtime := syscall.NsecToTimeval(oldStat.Mtim.Nano())
+			newMtime := syscall.NsecToTimeval(oldStat.Mtim.Nano())
+			if oldStat.Mode != newStat.Mode ||
+				oldStat.Uid != newStat.Uid ||
+				oldStat.Gid != newStat.Gid ||
+				oldStat.Rdev != newStat.Rdev ||
+				// Don't look at size for dirs, its not a good measure of change
+				(oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) ||
+				oldMtime.Sec != newMtime.Sec ||
+				oldMtime.Usec != newMtime.Usec {
+				change := Change{
+					Path: newChild.path(),
+					Kind: ChangeModify,
+				}
+				*changes = append(*changes, change)
+			}
+
+			// Remove from copy so we can detect deletions
+			delete(oldChildren, name)
+		}
+
+		newChild.addChanges(oldChild, changes)
+	}
+	for _, oldChild := range oldChildren {
+		// delete
+		change := Change{
+			Path: oldChild.path(),
+			Kind: ChangeDelete,
+		}
+		*changes = append(*changes, change)
+	}
+
+}
+
+func (info *FileInfo) Changes(oldInfo *FileInfo) []Change {
 	var changes []Change
-	err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error {
+
+	info.addChanges(oldInfo, &changes)
+
+	return changes
+}
+
+func newRootFileInfo() *FileInfo {
+	root := &FileInfo{
+		name:     "/",
+		children: make(map[string]*FileInfo),
+	}
+	return root
+}
+
+func applyLayer(root *FileInfo, layer string) error {
+	err := filepath.Walk(layer, func(layerPath string, f os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+
+		// Skip root
+		if layerPath == layer {
+			return nil
+		}
+
+		// rebase path
+		relPath, err := filepath.Rel(layer, layerPath)
+		if err != nil {
+			return err
+		}
+		relPath = filepath.Join("/", relPath)
+
+		// Skip AUFS metadata
+		if matched, err := filepath.Match("/.wh..wh.*", relPath); err != nil || matched {
+			if err != nil || !f.IsDir() {
+				return err
+			}
+			return filepath.SkipDir
+		}
+
+		var layerStat syscall.Stat_t
+		err = syscall.Lstat(layerPath, &layerStat)
+		if err != nil {
+			return err
+		}
+
+		file := filepath.Base(relPath)
+		// If there is a whiteout, then the file was removed
+		if strings.HasPrefix(file, ".wh.") {
+			originalFile := file[len(".wh."):]
+			deletePath := filepath.Join(filepath.Dir(relPath), originalFile)
+
+			root.Remove(deletePath)
+		} else {
+			// Added or changed file
+			existing := root.LookUp(relPath)
+			if existing != nil {
+				// Changed file
+				existing.stat = layerStat
+				if !existing.isDir() {
+					// Changed from dir to non-dir, delete all previous files
+					existing.children = make(map[string]*FileInfo)
+				}
+			} else {
+				// Added file
+				parent := root.LookUp(filepath.Dir(relPath))
+				if parent == nil {
+					return fmt.Errorf("collectFileInfo: Unexpectedly no parent for %s", relPath)
+				}
+
+				info := &FileInfo{
+					name:     filepath.Base(relPath),
+					children: make(map[string]*FileInfo),
+					parent:   parent,
+					stat:     layerStat,
+				}
+
+				parent.children[info.name] = info
+			}
+		}
+		return nil
+	})
+	return err
+}
+
+func collectFileInfo(sourceDir string) (*FileInfo, error) {
+	root := newRootFileInfo()
+
+	err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error {
 		if err != nil {
 			return err
 		}
 
 		// Rebase path
-		path, err = filepath.Rel(rw, path)
+		relPath, err := filepath.Rel(sourceDir, path)
 		if err != nil {
 			return err
 		}
-		path = filepath.Join("/", path)
+		relPath = filepath.Join("/", relPath)
 
-		// Skip root
-		if path == "/" {
+		if relPath == "/" {
 			return nil
 		}
 
-		// Skip AUFS metadata
-		if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
+		parent := root.LookUp(filepath.Dir(relPath))
+		if parent == nil {
+			return fmt.Errorf("collectFileInfo: Unexpectedly no parent for %s", relPath)
+		}
+
+		info := &FileInfo{
+			name:     filepath.Base(relPath),
+			children: make(map[string]*FileInfo),
+			parent:   parent,
+		}
+
+		if err := syscall.Lstat(path, &info.stat); err != nil {
 			return err
 		}
 
-		change := Change{
-			Path: path,
-		}
+		parent.children[info.name] = info
 
-		// Find out what kind of modification happened
-		file := filepath.Base(path)
-		// If there is a whiteout, then the file was removed
-		if strings.HasPrefix(file, ".wh.") {
-			originalFile := file[len(".wh."):]
-			change.Path = filepath.Join(filepath.Dir(path), originalFile)
-			change.Kind = ChangeDelete
-		} else {
-			// Otherwise, the file was added
-			change.Kind = ChangeAdd
-
-			// ...Unless it already existed in a top layer, in which case, it's a modification
-			for _, layer := range layers {
-				stat, err := os.Stat(filepath.Join(layer, path))
-				if err != nil && !os.IsNotExist(err) {
-					return err
-				}
-				if err == nil {
-					// The file existed in the top layer, so that's a modification
-
-					// However, if it's a directory, maybe it wasn't actually modified.
-					// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
-					if stat.IsDir() && f.IsDir() {
-						if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
-							// Both directories are the same, don't record the change
-							return nil
-						}
-					}
-					change.Kind = ChangeModify
-					break
-				}
-			}
-		}
-
-		// Record change
-		changes = append(changes, change)
 		return nil
 	})
-	if err != nil && !os.IsNotExist(err) {
+	if err != nil {
 		return nil, err
 	}
-	return changes, nil
+	return root, nil
+}
+
+// Compare a directory with an array of layer directories it was based on and
+// generate an array of Change objects describing the changes
+func ChangesLayers(newDir string, layers []string) ([]Change, error) {
+	newRoot, err := collectFileInfo(newDir)
+	if err != nil {
+		return nil, err
+	}
+	oldRoot := newRootFileInfo()
+	for i := len(layers) - 1; i >= 0; i-- {
+		layer := layers[i]
+		if err = applyLayer(oldRoot, layer); err != nil {
+			return nil, err
+		}
+	}
+
+	return newRoot.Changes(oldRoot), nil
+}
+
+// Compare two directories and generate an array of Change objects describing the changes
+func ChangesDirs(newDir, oldDir string) ([]Change, error) {
+	oldRoot, err := collectFileInfo(oldDir)
+	if err != nil {
+		return nil, err
+	}
+	newRoot, err := collectFileInfo(newDir)
+	if err != nil {
+		return nil, err
+	}
+
+	return newRoot.Changes(oldRoot), nil
 }
diff --git a/commands.go b/commands.go
index 7d33b81..241e8fc 100644
--- a/commands.go
+++ b/commands.go
@@ -23,6 +23,7 @@
 	"os/signal"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"runtime"
 	"sort"
 	"strconv"
@@ -465,6 +466,11 @@
 
 	fmt.Fprintf(cli.out, "Containers: %d\n", out.Containers)
 	fmt.Fprintf(cli.out, "Images: %d\n", out.Images)
+	if out.DevmapperDataTotal != 0 {
+		fmt.Fprintf(cli.out, "Devmapper disk use: Data: %.1f/%.1f Metadata: %.1f/%.1f\n",
+			float64(out.DevmapperDataUsed)/(1024*1024), float64(out.DevmapperDataTotal)/(1024*1024),
+			float64(out.DevmapperMetadataUsed)/(1024*1024), float64(out.DevmapperMetadataTotal)/(1024*1024))
+	}
 	if out.Debug || os.Getenv("DEBUG") != "" {
 		fmt.Fprintf(cli.out, "Debug mode (server): %v\n", out.Debug)
 		fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
@@ -473,6 +479,7 @@
 		fmt.Fprintf(cli.out, "LXC Version: %s\n", out.LXCVersion)
 		fmt.Fprintf(cli.out, "EventsListeners: %d\n", out.NEventsListener)
 		fmt.Fprintf(cli.out, "Kernel Version: %s\n", out.KernelVersion)
+		fmt.Fprintf(cli.out, "Devmapper pool: %s\n", out.DevmapperPool)
 	}
 
 	if len(out.IndexServerAddress) != 0 {
@@ -740,6 +747,8 @@
 func (cli *DockerCli) CmdRm(args ...string) error {
 	cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
 	v := cmd.Bool("v", false, "Remove the volumes associated to the container")
+	link := cmd.Bool("link", false, "Remove the specified link and not the underlying container")
+
 	if err := cmd.Parse(args); err != nil {
 		return nil
 	}
@@ -751,6 +760,9 @@
 	if *v {
 		val.Set("v", "1")
 	}
+	if *link {
+		val.Set("link", "1")
+	}
 	for _, name := range cmd.Args() {
 		_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
 		if err != nil {
@@ -985,25 +997,19 @@
 				out.Tag = "<none>"
 			}
 
+			if !*noTrunc {
+				out.ID = utils.TruncateID(out.ID)
+			}
+
 			if !*quiet {
-				fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag)
-				if *noTrunc {
-					fmt.Fprintf(w, "%s\t", out.ID)
-				} else {
-					fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID))
-				}
-				fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
+				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t", out.Repository, out.Tag, out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
 				if out.VirtualSize > 0 {
 					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize))
 				} else {
 					fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size))
 				}
 			} else {
-				if *noTrunc {
-					fmt.Fprintln(w, out.ID)
-				} else {
-					fmt.Fprintln(w, utils.TruncateID(out.ID))
-				}
+				fmt.Fprintln(w, out.ID)
 			}
 		}
 
@@ -1017,10 +1023,10 @@
 func displayablePorts(ports []APIPort) string {
 	result := []string{}
 	for _, port := range ports {
-		if port.Type == "tcp" {
-			result = append(result, fmt.Sprintf("%d->%d", port.PublicPort, port.PrivatePort))
+		if port.IP == "" {
+			result = append(result, fmt.Sprintf("%d/%s", port.PublicPort, port.Type))
 		} else {
-			result = append(result, fmt.Sprintf("%d->%d/%s", port.PublicPort, port.PrivatePort, port.Type))
+			result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
 		}
 	}
 	sort.Strings(result)
@@ -1073,7 +1079,7 @@
 	}
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	if !*quiet {
-		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS")
+		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
 		if *size {
 			fmt.Fprintln(w, "\tSIZE")
 		} else {
@@ -1082,12 +1088,17 @@
 	}
 
 	for _, out := range outs {
+		if !*noTrunc {
+			out.ID = utils.TruncateID(out.ID)
+		}
 		if !*quiet {
-			if *noTrunc {
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports))
-			} else {
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports))
+			if !*noTrunc {
+				out.Command = utils.Trunc(out.Command, 20)
+				for i := 0; i < len(out.Names); i++ {
+					out.Names[i] = utils.Trunc(out.Names[i], 10)
+				}
 			}
+			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports), strings.Join(out.Names, ","))
 			if *size {
 				if out.SizeRootFs > 0 {
 					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs))
@@ -1098,11 +1109,7 @@
 				fmt.Fprint(w, "\n")
 			}
 		} else {
-			if *noTrunc {
-				fmt.Fprintln(w, out.ID)
-			} else {
-				fmt.Fprintln(w, utils.TruncateID(out.ID))
-			}
+			fmt.Fprintln(w, out.ID)
 		}
 	}
 
@@ -1112,6 +1119,64 @@
 	return nil
 }
 
+func (cli *DockerCli) CmdLs(args ...string) error {
+	cmd := Subcmd("ls", "", "List links for containers")
+	flAll := cmd.Bool("a", false, "Show all links")
+
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	v := url.Values{}
+	if *flAll {
+		v.Set("all", "1")
+	}
+
+	body, _, err := cli.call("GET", "/containers/links?"+v.Encode(), nil)
+	if err != nil {
+		return err
+	}
+	var links []APILink
+	if err := json.Unmarshal(body, &links); err != nil {
+		return err
+	}
+
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
+	fmt.Fprintf(w, "NAME\tID\tIMAGE")
+	fmt.Fprintf(w, "\n")
+
+	sortLinks(links, func(i, j APILink) bool {
+		return len(i.Path) < len(j.Path)
+	})
+	for _, link := range links {
+		fmt.Fprintf(w, "%s\t%s\t%s", link.Path, utils.TruncateID(link.ContainerID), link.Image)
+		fmt.Fprintf(w, "\n")
+	}
+	w.Flush()
+
+	return nil
+}
+
+func (cli *DockerCli) CmdLink(args ...string) error {
+	cmd := Subcmd("link", "CURRENT_NAME NEW_NAME", "Link the container with a new name")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() != 2 {
+		cmd.Usage()
+		return nil
+	}
+	body := map[string]string{
+		"currentName": cmd.Arg(0),
+		"newName":     cmd.Arg(1),
+	}
+
+	_, _, err := cli.call("POST", "/containers/link", body)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
 func (cli *DockerCli) CmdCommit(args ...string) error {
 	cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
 	flComment := cmd.String("m", "", "Commit message")
@@ -1229,8 +1294,9 @@
 		cmd.Usage()
 		return nil
 	}
+	name := cmd.Arg(0)
 
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err); err != nil {
+	if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err); err != nil {
 		return err
 	}
 	return nil
@@ -1245,8 +1311,8 @@
 		cmd.Usage()
 		return nil
 	}
-
-	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
+	name := cmd.Arg(0)
+	body, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
 	if err != nil {
 		return err
 	}
@@ -1326,18 +1392,6 @@
 // Ports type - Used to parse multiple -p flags
 type ports []int
 
-// ListOpts type
-type ListOpts []string
-
-func (opts *ListOpts) String() string {
-	return fmt.Sprint(*opts)
-}
-
-func (opts *ListOpts) Set(value string) error {
-	*opts = append(*opts, value)
-	return nil
-}
-
 // AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to
 type AttachOpts map[string]bool
 
@@ -1533,7 +1587,11 @@
 	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
 		if config.Tty {
 			if err := cli.monitorTtySize(runResult.ID); err != nil {
-				utils.Errorf("Error monitoring TTY size: %s\n", err)
+				// When running the test suite, there is no terminal, just pipes.
+				// Discard the error then.
+				if os.Getenv("TEST") != "1" {
+					utils.Errorf("Error monitoring TTY size: %s\n", err)
+				}
 			}
 		}
 
@@ -1642,6 +1700,10 @@
 		params = bytes.NewBuffer(buf)
 	}
 
+	// fixme: refactor client to support redirect
+	re := regexp.MustCompile("/+")
+	path = re.ReplaceAllString(path, "/")
+
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
 	if err != nil {
 		return nil, -1, err
@@ -1687,6 +1749,11 @@
 	if (method == "POST" || method == "PUT") && in == nil {
 		in = bytes.NewReader([]byte{})
 	}
+
+	// fixme: refactor client to support redirect
+	re := regexp.MustCompile("/+")
+	path = re.ReplaceAllString(path, "/")
+
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
 	if err != nil {
 		return err
@@ -1743,6 +1810,9 @@
 }
 
 func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer) error {
+	// fixme: refactor client to support redirect
+	re := regexp.MustCompile("/+")
+	path = re.ReplaceAllString(path, "/")
 
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
 	if err != nil {
diff --git a/config.go b/config.go
new file mode 100644
index 0000000..8f2d22c
--- /dev/null
+++ b/config.go
@@ -0,0 +1,18 @@
+package docker
+
+import (
+	"net"
+)
+
+type DaemonConfig struct {
+	Pidfile                     string
+	GraphPath                   string
+	ProtoAddresses              []string
+	AutoRestart                 bool
+	EnableCors                  bool
+	Dns                         []string
+	EnableIptables              bool
+	BridgeIface                 string
+	DefaultIp                   net.IP
+	InterContainerCommunication bool
+}
diff --git a/container.go b/container.go
index 7b52290..c94cae3 100644
--- a/container.go
+++ b/container.go
@@ -16,7 +16,6 @@
 	"os/exec"
 	"path"
 	"path/filepath"
-	"strconv"
 	"strings"
 	"syscall"
 	"time"
@@ -43,6 +42,7 @@
 	ResolvConfPath string
 	HostnamePath   string
 	HostsPath      string
+	FilesystemType string
 
 	cmd       *exec.Cmd
 	stdout    *utils.WriteBroadcaster
@@ -58,6 +58,8 @@
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
 	// Easier than migrating older container configs :)
 	VolumesRW map[string]bool
+
+	activeLinks map[string]*Link
 }
 
 type Config struct {
@@ -70,7 +72,8 @@
 	AttachStdin     bool
 	AttachStdout    bool
 	AttachStderr    bool
-	PortSpecs       []string
+	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
+	ExposedPorts    map[Port]struct{}
 	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
 	OpenStdin       bool // Open stdin
 	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
@@ -90,6 +93,8 @@
 	Binds           []string
 	ContainerIDFile string
 	LxcConf         []KeyValuePair
+	PortBindings    map[Port][]PortBinding
+	Links           []string
 }
 
 type BindMap struct {
@@ -107,6 +112,34 @@
 	Value string
 }
 
+type PortBinding struct {
+	HostIp   string
+	HostPort string
+}
+
+// 80/tcp
+type Port string
+
+func (p Port) Proto() string {
+	return strings.Split(string(p), "/")[1]
+}
+
+func (p Port) Port() string {
+	return strings.Split(string(p), "/")[0]
+}
+
+func (p Port) Int() int {
+	i, err := parsePort(p.Port())
+	if err != nil {
+		panic(err)
+	}
+	return i
+}
+
+func NewPort(proto, port string) Port {
+	return Port(fmt.Sprintf("%s/%s", port, proto))
+}
+
 func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
 	if os.Getenv("TEST") != "" {
@@ -135,26 +168,32 @@
 
 	flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)")
 
-	var flPorts ListOpts
-	cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)")
+	var flPublish utils.ListOpts
+	cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)")
 
-	var flEnv ListOpts
+	var flExpose utils.ListOpts
+	cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host")
+
+	var flEnv utils.ListOpts
 	cmd.Var(&flEnv, "e", "Set environment variables")
 
-	var flDns ListOpts
+	var flDns utils.ListOpts
 	cmd.Var(&flDns, "dns", "Set custom dns servers")
 
 	flVolumes := NewPathOpts()
 	cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
 
-	var flVolumesFrom ListOpts
+	var flVolumesFrom utils.ListOpts
 	cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container")
 
 	flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
 
-	var flLxcOpts ListOpts
+	var flLxcOpts utils.ListOpts
 	cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
 
+	var flLinks utils.ListOpts
+	cmd.Var(&flLinks, "link", "Add link to another container (containerid:alias)")
+
 	if err := cmd.Parse(args); err != nil {
 		return nil, nil, cmd, err
 	}
@@ -220,10 +259,28 @@
 		hostname = parts[0]
 		domainname = parts[1]
 	}
+
+	ports, portBindings, err := parsePortSpecs(flPublish)
+	if err != nil {
+		return nil, nil, cmd, err
+	}
+
+	// Merge in exposed ports to the map of published ports
+	for _, e := range flExpose {
+		if strings.Contains(e, ":") {
+			return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e)
+		}
+		p := NewPort(splitProtoPort(e))
+		if _, exists := ports[p]; !exists {
+			ports[p] = struct{}{}
+		}
+	}
+
 	config := &Config{
-		Hostname:        hostname,
+		Hostname:        *flHostname,
 		Domainname:      domainname,
-		PortSpecs:       flPorts,
+		PortSpecs:       nil, // Deprecated
+		ExposedPorts:    ports,
 		User:            *flUser,
 		Tty:             *flTty,
 		NetworkDisabled: !*flNetwork,
@@ -243,10 +300,13 @@
 		Privileged:      *flPrivileged,
 		WorkingDir:      *flWorkingDir,
 	}
+
 	hostConfig := &HostConfig{
 		Binds:           binds,
 		ContainerIDFile: *flContainerIDFile,
 		LxcConf:         lxcConf,
+		PortBindings:    portBindings,
+		Links:           flLinks,
 	}
 
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
@@ -261,48 +321,55 @@
 	return config, hostConfig, cmd, nil
 }
 
-type PortMapping map[string]string
+type PortMapping map[string]string // Deprecated
 
 type NetworkSettings struct {
 	IPAddress   string
 	IPPrefixLen int
 	Gateway     string
 	Bridge      string
-	PortMapping map[string]PortMapping
+	PortMapping map[string]PortMapping // Deprecated
+	Ports       map[Port][]PortBinding
 }
 
-// returns a more easy to process description of the port mapping defined in the settings
 func (settings *NetworkSettings) PortMappingAPI() []APIPort {
 	var mapping []APIPort
-	for private, public := range settings.PortMapping["Tcp"] {
-		pubint, _ := strconv.ParseInt(public, 0, 0)
-		privint, _ := strconv.ParseInt(private, 0, 0)
-		mapping = append(mapping, APIPort{
-			PrivatePort: privint,
-			PublicPort:  pubint,
-			Type:        "tcp",
-		})
-	}
-	for private, public := range settings.PortMapping["Udp"] {
-		pubint, _ := strconv.ParseInt(public, 0, 0)
-		privint, _ := strconv.ParseInt(private, 0, 0)
-		mapping = append(mapping, APIPort{
-			PrivatePort: privint,
-			PublicPort:  pubint,
-			Type:        "udp",
-		})
+	for port, bindings := range settings.Ports {
+		p, _ := parsePort(port.Port())
+		if len(bindings) == 0 {
+			mapping = append(mapping, APIPort{
+				PublicPort: int64(p),
+				Type:       port.Proto(),
+			})
+			continue
+		}
+		for _, binding := range bindings {
+			p, _ := parsePort(port.Port())
+			h, _ := parsePort(binding.HostPort)
+			mapping = append(mapping, APIPort{
+				PrivatePort: int64(p),
+				PublicPort:  int64(h),
+				Type:        port.Proto(),
+				IP:          binding.HostIp,
+			})
+		}
 	}
 	return mapping
 }
 
 // Inject the io.Reader at the given path. Note: do not close the reader
 func (container *Container) Inject(file io.Reader, pth string) error {
-	// Make sure the directory exists
-	if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil {
+	if err := container.EnsureMounted(); err != nil {
 		return err
 	}
+
+	// Make sure the directory exists
+	if err := os.MkdirAll(path.Join(container.RootfsPath(), path.Dir(pth)), 0755); err != nil {
+		return err
+	}
+
 	// FIXME: Handle permissions/already existing dest
-	dest, err := os.Create(path.Join(container.rwPath(), pth))
+	dest, err := os.Create(path.Join(container.RootfsPath(), pth))
 	if err != nil {
 		return err
 	}
@@ -590,7 +657,7 @@
 	if container.runtime.networkManager.disabled {
 		container.Config.NetworkDisabled = true
 	} else {
-		if err := container.allocateNetwork(); err != nil {
+		if err := container.allocateNetwork(hostConfig); err != nil {
 			return err
 		}
 	}
@@ -753,6 +820,7 @@
 	}
 
 	params := []string{
+		"lxc-start",
 		"-n", container.ID,
 		"-f", container.lxcConfigPath(),
 		"--",
@@ -780,6 +848,46 @@
 		"-e", "container=lxc",
 		"-e", "HOSTNAME="+container.Config.Hostname,
 	)
+
+	// Init any links between the parent and children
+	runtime := container.runtime
+
+	children, err := runtime.Children(fmt.Sprintf("/%s", container.ID))
+	if err != nil {
+		return err
+	}
+
+	if len(children) > 0 {
+		container.activeLinks = make(map[string]*Link, len(children))
+
+		// If we encounter an error make sure that we rollback any network
+		// config and ip table changes
+		rollback := func() {
+			for _, link := range container.activeLinks {
+				link.Disable()
+			}
+			container.activeLinks = nil
+		}
+
+		for p, child := range children {
+			link, err := NewLink(container, child, p, runtime.networkManager.bridgeIface)
+			if err != nil {
+				rollback()
+				return err
+			}
+
+			container.activeLinks[link.Alias()] = link
+			if err := link.Enable(); err != nil {
+				rollback()
+				return err
+			}
+
+			for _, envVar := range link.ToEnv() {
+				params = append(params, "-e", envVar)
+			}
+		}
+	}
+
 	if container.Config.WorkingDir != "" {
 		workingDir := path.Clean(container.Config.WorkingDir)
 		utils.Debugf("[working dir] working dir is %s", workingDir)
@@ -801,7 +909,21 @@
 	params = append(params, "--", container.Path)
 	params = append(params, container.Args...)
 
-	container.cmd = exec.Command("lxc-start", params...)
+	if RootIsShared() {
+		// lxc-start really needs / to be private, or all kinds of stuff break
+		// What we really want is to clone into a new namespace and then
+		// mount / MS_REC|MS_PRIVATE, but since we can't really clone or fork
+		// without exec in go we have to do this horrible shell hack...
+		shellString :=
+			"mount --make-rprivate /; exec " +
+				utils.ShellQuoteArguments(params)
+
+		params = []string{
+			"unshare", "-m", "--", "/bin/sh", "-c", shellString,
+		}
+	}
+
+	container.cmd = exec.Command(params[0], params[1:]...)
 
 	// Setup logging of stdout and stderr to disk
 	if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
@@ -882,7 +1004,7 @@
 	return utils.NewBufReader(reader), nil
 }
 
-func (container *Container) allocateNetwork() error {
+func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
 	if container.Config.NetworkDisabled {
 		return nil
 	}
@@ -909,36 +1031,59 @@
 		}
 	}
 
-	var portSpecs []string
-	if !container.State.Ghost {
-		portSpecs = container.Config.PortSpecs
-	} else {
-		for backend, frontend := range container.NetworkSettings.PortMapping["Tcp"] {
-			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/tcp", frontend, backend))
+	if container.Config.PortSpecs != nil {
+		utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
+		if err := migratePortMappings(container.Config); err != nil {
+			return err
 		}
-		for backend, frontend := range container.NetworkSettings.PortMapping["Udp"] {
-			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/udp", frontend, backend))
+		container.Config.PortSpecs = nil
+	}
+
+	portSpecs := make(map[Port]struct{})
+	bindings := make(map[Port][]PortBinding)
+
+	if !container.State.Ghost {
+		if container.Config.ExposedPorts != nil {
+			portSpecs = container.Config.ExposedPorts
+		}
+		if hostConfig.PortBindings != nil {
+			bindings = hostConfig.PortBindings
+		}
+	} else {
+		if container.NetworkSettings.Ports != nil {
+			for port, binding := range container.NetworkSettings.Ports {
+				portSpecs[port] = struct{}{}
+				bindings[port] = binding
+			}
 		}
 	}
 
-	container.NetworkSettings.PortMapping = make(map[string]PortMapping)
-	container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping)
-	container.NetworkSettings.PortMapping["Udp"] = make(PortMapping)
-	for _, spec := range portSpecs {
-		nat, err := iface.AllocatePort(spec)
-		if err != nil {
-			iface.Release()
-			return err
+	container.NetworkSettings.PortMapping = nil
+
+	for port := range portSpecs {
+		binding := bindings[port]
+		for i := 0; i < len(binding); i++ {
+			b := binding[i]
+			nat, err := iface.AllocatePort(port, b)
+			if err != nil {
+				iface.Release()
+				return err
+			}
+			utils.Debugf("Allocate port: %s:%s->%s", nat.Binding.HostIp, port, nat.Binding.HostPort)
+			binding[i] = nat.Binding
 		}
-		proto := strings.Title(nat.Proto)
-		backend, frontend := strconv.Itoa(nat.Backend), strconv.Itoa(nat.Frontend)
-		container.NetworkSettings.PortMapping[proto][backend] = frontend
+		bindings[port] = binding
 	}
+	container.SaveHostConfig(hostConfig)
+
+	container.NetworkSettings.Ports = bindings
 	container.network = iface
+
 	container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
 	container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
 	container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
 	container.NetworkSettings.Gateway = iface.Gateway.String()
+
 	return nil
 }
 
@@ -973,7 +1118,8 @@
 	if container.cmd == nil {
 		utils.Debugf("monitor: waiting for container %s using waitLxc", container.ID)
 		if err := container.waitLxc(); err != nil {
-			utils.Errorf("monitor: while waiting for container %s, waitLxc had a problem: %s", container.ID, err)
+			// Discard the error as any signals or non 0 returns will generate an error
+			utils.Debugf("monitor: while waiting for container %s, waitLxc had a problem: %s", container.ShortID(), err)
 		}
 	} else {
 		utils.Debugf("monitor: waiting for container %s using cmd.Wait", container.ID)
@@ -1021,6 +1167,14 @@
 
 func (container *Container) cleanup() {
 	container.releaseNetwork()
+
+	// Disable all active links
+	if container.activeLinks != nil {
+		for _, link := range container.activeLinks {
+			link.Disable()
+		}
+	}
+
 	if container.Config.OpenStdin {
 		if err := container.stdin.Close(); err != nil {
 			utils.Errorf("%s: Error close stdin: %s", container.ID, err)
@@ -1132,7 +1286,15 @@
 }
 
 func (container *Container) ExportRw() (Archive, error) {
-	return Tar(container.rwPath(), Uncompressed)
+	if err := container.EnsureMounted(); err != nil {
+		return nil, err
+	}
+
+	image, err := container.GetImage()
+	if err != nil {
+		return nil, err
+	}
+	return image.ExportChanges(container.runtime, container.RootfsPath(), container.rwPath(), container.ID)
 }
 
 func (container *Container) RwChecksum() (string, error) {
@@ -1174,20 +1336,33 @@
 	return container.Mount()
 }
 
+func (container *Container) EnsureUnmounted() error {
+	if mounted, err := container.Mounted(); err != nil {
+		return err
+	} else if !mounted {
+		return nil
+	}
+	return container.Unmount()
+}
+
 func (container *Container) Mount() error {
 	image, err := container.GetImage()
 	if err != nil {
 		return err
 	}
-	return image.Mount(container.RootfsPath(), container.rwPath())
+	return image.Mount(container.runtime, container.RootfsPath(), container.rwPath(), container.ID)
 }
 
 func (container *Container) Changes() ([]Change, error) {
+	if err := container.EnsureMounted(); err != nil {
+		return nil, err
+	}
+
 	image, err := container.GetImage()
 	if err != nil {
 		return nil, err
 	}
-	return image.Changes(container.rwPath())
+	return image.Changes(container.runtime, container.RootfsPath(), container.rwPath(), container.ID)
 }
 
 func (container *Container) GetImage() (*Image, error) {
@@ -1198,11 +1373,20 @@
 }
 
 func (container *Container) Mounted() (bool, error) {
-	return Mounted(container.RootfsPath())
+	image, err := container.GetImage()
+	if err != nil {
+		return false, err
+	}
+	return image.Mounted(container.runtime, container.RootfsPath(), container.rwPath())
 }
 
 func (container *Container) Unmount() error {
-	return Unmount(container.RootfsPath())
+	image, err := container.GetImage()
+	if err != nil {
+		return err
+	}
+	err = image.Unmount(container.runtime, container.RootfsPath(), container.ID)
+	return err
 }
 
 // ShortID returns a shorthand version of the container's id for convenience.
@@ -1290,5 +1474,11 @@
 		filter = []string{path.Base(basePath)}
 		basePath = path.Dir(basePath)
 	}
-	return TarFilter(basePath, Uncompressed, filter)
+	return TarFilter(basePath, Uncompressed, filter, true, nil)
+}
+
+// Returns true if the container exposes a certain port
+func (container *Container) Exposes(p Port) bool {
+	_, exists := container.Config.ExposedPorts[p]
+	return exists
 }
diff --git a/container_test.go b/container_test.go
index 9fdddc9..786489d 100644
--- a/container_test.go
+++ b/container_test.go
@@ -18,7 +18,7 @@
 func TestIDFormat(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container1, err := runtime.Create(
+	container1, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
@@ -388,7 +388,7 @@
 func TestOutput(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foobar"},
@@ -407,11 +407,32 @@
 	}
 }
 
+func TestContainerNetwork(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+	container, _, err := runtime.Create(
+		&Config{
+			Image: GetTestImage(runtime).ID,
+			Cmd:   []string{"ping", "-c", "1", "127.0.0.1"},
+		},
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer runtime.Destroy(container)
+	if err := container.Run(); err != nil {
+		t.Fatal(err)
+	}
+	if container.State.ExitCode != 0 {
+		t.Errorf("Unexpected ping 127.0.0.1 exit code %d (expected 0)", container.State.ExitCode)
+	}
+}
+
 func TestKillDifferentUser(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:     GetTestImage(runtime).ID,
 		Cmd:       []string{"cat"},
 		OpenStdin: true,
@@ -471,7 +492,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	c, err := runtime.Create(config)
+	c, _, err := runtime.Create(config)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -486,7 +507,7 @@
 func TestKill(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 	},
@@ -530,7 +551,7 @@
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	trueContainer, err := runtime.Create(&Config{
+	trueContainer, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true", ""},
 	})
@@ -545,7 +566,7 @@
 		t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode)
 	}
 
-	falseContainer, err := runtime.Create(&Config{
+	falseContainer, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/false", ""},
 	})
@@ -564,7 +585,7 @@
 func TestRestart(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"echo", "-n", "foobar"},
 	},
@@ -594,7 +615,7 @@
 func TestRestartStdin(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 
@@ -672,7 +693,7 @@
 	defer nuke(runtime)
 
 	// Default user must be root
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 	},
@@ -690,7 +711,7 @@
 	}
 
 	// Set a username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
@@ -710,7 +731,7 @@
 	}
 
 	// Set a UID
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
@@ -730,7 +751,7 @@
 	}
 
 	// Set a different user by uid
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
@@ -752,7 +773,7 @@
 	}
 
 	// Set a different user by username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
@@ -772,7 +793,7 @@
 	}
 
 	// Test an wrong username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
@@ -793,7 +814,7 @@
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container1, err := runtime.Create(&Config{
+	container1, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 	},
@@ -803,7 +824,7 @@
 	}
 	defer runtime.Destroy(container1)
 
-	container2, err := runtime.Create(&Config{
+	container2, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 	},
@@ -847,7 +868,7 @@
 func TestStdin(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 
@@ -892,7 +913,7 @@
 func TestTty(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 
@@ -937,7 +958,7 @@
 func TestEnv(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"env"},
 	},
@@ -986,7 +1007,7 @@
 func TestEntrypoint(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:      GetTestImage(runtime).ID,
 			Entrypoint: []string{"/bin/echo"},
@@ -1009,7 +1030,7 @@
 func TestEntrypointNoCmd(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:      GetTestImage(runtime).ID,
 			Entrypoint: []string{"/bin/echo", "foobar"},
@@ -1060,7 +1081,7 @@
 	cpuMin := 100
 	cpuMax := 10000
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true"},
 
@@ -1072,7 +1093,6 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer runtime.Destroy(container)
 	container.generateLXCConfig(nil)
 	grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar")
 	grepFile(t, container.lxcConfigPath(),
@@ -1084,7 +1104,7 @@
 func TestCustomLxcConfig(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true"},
 
@@ -1115,7 +1135,7 @@
 	runtime := mkRuntime(b)
 	defer nuke(runtime)
 	for i := 0; i < b.N; i++ {
-		container, err := runtime.Create(&Config{
+		container, _, err := runtime.Create(&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foo"},
 		},
@@ -1147,7 +1167,7 @@
 		complete := make(chan error)
 		tasks = append(tasks, complete)
 		go func(i int, complete chan error) {
-			container, err := runtime.Create(&Config{
+			container, _, err := runtime.Create(&Config{
 				Image: GetTestImage(runtime).ID,
 				Cmd:   []string{"echo", "-n", "foo"},
 			},
@@ -1297,7 +1317,7 @@
 func TestVolumesFromReadonlyMount(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
@@ -1316,7 +1336,7 @@
 		t.Fail()
 	}
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
@@ -1352,7 +1372,7 @@
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"echo", "-n", "foobar"},
 		Volumes: map[string]struct{}{"/test": {}},
@@ -1395,7 +1415,7 @@
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Volumes: map[string]struct{}{"/test": {}},
@@ -1422,7 +1442,7 @@
 		t.Fail()
 	}
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"cat", "/test/foo"},
@@ -1463,7 +1483,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	c, err := runtime.Create(config)
+	c, _, err := runtime.Create(config)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1529,7 +1549,7 @@
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Volumes: map[string]struct{}{"/test": {}},
@@ -1556,7 +1576,7 @@
 		t.Fail()
 	}
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
@@ -1577,7 +1597,7 @@
 		t.Fatal(err)
 	}
 
-	container3, err := runtime.Create(
+	container3, _, err := runtime.Create(
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
diff --git a/devmapper/deviceset.go b/devmapper/deviceset.go
new file mode 100644
index 0000000..a80f2a9
--- /dev/null
+++ b/devmapper/deviceset.go
@@ -0,0 +1,850 @@
+package devmapper
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/dotcloud/docker/utils"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path"
+	"path/filepath"
+	"strconv"
+	"sync"
+	"syscall"
+	"time"
+)
+
+var (
+	DefaultDataLoopbackSize     int64  = 100 * 1024 * 1024 * 1024
+	DefaultMetaDataLoopbackSize int64  = 2 * 1024 * 1024 * 1024
+	DefaultBaseFsSize           uint64 = 10 * 1024 * 1024 * 1024
+)
+
+type DevInfo struct {
+	Hash          string     `json:"-"`
+	DeviceId      int        `json:"device_id"`
+	Size          uint64     `json:"size"`
+	TransactionId uint64     `json:"transaction_id"`
+	Initialized   bool       `json:"initialized"`
+	devices       *DeviceSet `json:"-"`
+}
+
+type MetaData struct {
+	Devices map[string]*DevInfo `json:devices`
+}
+
+type DeviceSet struct {
+	MetaData
+	sync.Mutex
+	initialized      bool
+	root             string
+	devicePrefix     string
+	TransactionId    uint64
+	NewTransactionId uint64
+	nextFreeDevice   int
+	activeMounts     map[string]int
+}
+
+type DiskUsage struct {
+	Used  uint64
+	Total uint64
+}
+
+type Status struct {
+	PoolName         string
+	DataLoopback     string
+	MetadataLoopback string
+	Data             DiskUsage
+	Metadata         DiskUsage
+}
+
+func getDevName(name string) string {
+	return "/dev/mapper/" + name
+}
+
+func (info *DevInfo) Name() string {
+	hash := info.Hash
+	if hash == "" {
+		hash = "base"
+	}
+	return fmt.Sprintf("%s-%s", info.devices.devicePrefix, hash)
+}
+
+func (info *DevInfo) DevName() string {
+	return getDevName(info.Name())
+}
+
+func (devices *DeviceSet) loopbackDir() string {
+	return path.Join(devices.root, "devicemapper")
+}
+
+func (devices *DeviceSet) jsonFile() string {
+	return path.Join(devices.loopbackDir(), "json")
+}
+
+func (devices *DeviceSet) getPoolName() string {
+	return devices.devicePrefix + "-pool"
+}
+
+func (devices *DeviceSet) getPoolDevName() string {
+	return getDevName(devices.getPoolName())
+}
+
+func (devices *DeviceSet) hasImage(name string) bool {
+	dirname := devices.loopbackDir()
+	filename := path.Join(dirname, name)
+
+	_, err := os.Stat(filename)
+	return err == nil
+}
+
+// ensureImage creates a sparse file of <size> bytes at the path
+// <root>/devicemapper/<name>.
+// If the file already exists, it does nothing.
+// Either way it returns the full path.
+func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
+	dirname := devices.loopbackDir()
+	filename := path.Join(dirname, name)
+
+	if err := os.MkdirAll(dirname, 0700); err != nil && !os.IsExist(err) {
+		return "", err
+	}
+
+	if _, err := os.Stat(filename); err != nil {
+		if !os.IsNotExist(err) {
+			return "", err
+		}
+		utils.Debugf("Creating loopback file %s for device-manage use", filename)
+		file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600)
+		if err != nil {
+			return "", err
+		}
+
+		if err = file.Truncate(size); err != nil {
+			return "", err
+		}
+	}
+	return filename, nil
+}
+
+func (devices *DeviceSet) allocateDeviceId() int {
+	// TODO: Add smarter reuse of deleted devices
+	id := devices.nextFreeDevice
+	devices.nextFreeDevice = devices.nextFreeDevice + 1
+	return id
+}
+
+func (devices *DeviceSet) allocateTransactionId() uint64 {
+	devices.NewTransactionId = devices.NewTransactionId + 1
+	return devices.NewTransactionId
+}
+
+func (devices *DeviceSet) saveMetadata() error {
+	jsonData, err := json.Marshal(devices.MetaData)
+	if err != nil {
+		return fmt.Errorf("Error encoding metaadata to json: %s", err)
+	}
+	tmpFile, err := ioutil.TempFile(filepath.Dir(devices.jsonFile()), ".json")
+	if err != nil {
+		return fmt.Errorf("Error creating metadata file: %s", err)
+	}
+
+	n, err := tmpFile.Write(jsonData)
+	if err != nil {
+		return fmt.Errorf("Error writing metadata to %s: %s", tmpFile.Name(), err)
+	}
+	if n < len(jsonData) {
+		return io.ErrShortWrite
+	}
+	if err := tmpFile.Sync(); err != nil {
+		return fmt.Errorf("Error syncing metadata file %s: %s", tmpFile.Name(), err)
+	}
+	if err := tmpFile.Close(); err != nil {
+		return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err)
+	}
+	if err := os.Rename(tmpFile.Name(), devices.jsonFile()); err != nil {
+		return fmt.Errorf("Error committing metadata file", err)
+	}
+
+	if devices.NewTransactionId != devices.TransactionId {
+		if err = setTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
+			return fmt.Errorf("Error setting devmapper transition ID: %s", err)
+		}
+		devices.TransactionId = devices.NewTransactionId
+	}
+	return nil
+}
+
+func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*DevInfo, error) {
+	utils.Debugf("registerDevice(%v, %v)", id, hash)
+	info := &DevInfo{
+		Hash:          hash,
+		DeviceId:      id,
+		Size:          size,
+		TransactionId: devices.allocateTransactionId(),
+		Initialized:   false,
+		devices:       devices,
+	}
+
+	devices.Devices[hash] = info
+	if err := devices.saveMetadata(); err != nil {
+		// Try to remove unused device
+		delete(devices.Devices, hash)
+		return nil, err
+	}
+
+	return info, nil
+}
+
+func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error {
+	utils.Debugf("activateDeviceIfNeeded(%v)", hash)
+	info := devices.Devices[hash]
+	if info == nil {
+		return fmt.Errorf("Unknown device %s", hash)
+	}
+
+	if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
+		return nil
+	}
+
+	return activateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size)
+}
+
+func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
+	devname := info.DevName()
+
+	err := exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname).Run()
+	if err != nil {
+		err = exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname).Run()
+	}
+	if err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+	return nil
+}
+
+func (devices *DeviceSet) loadMetaData() error {
+	utils.Debugf("loadMetadata()")
+	defer utils.Debugf("loadMetadata END")
+	_, _, _, params, err := getStatus(devices.getPoolName())
+	if err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+	devices.NewTransactionId = devices.TransactionId
+
+	jsonData, err := ioutil.ReadFile(devices.jsonFile())
+	if err != nil && !os.IsNotExist(err) {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	devices.MetaData.Devices = make(map[string]*DevInfo)
+	if jsonData != nil {
+		if err := json.Unmarshal(jsonData, &devices.MetaData); err != nil {
+			utils.Debugf("\n--->Err: %s\n", err)
+			return err
+		}
+	}
+
+	for hash, d := range devices.Devices {
+		d.Hash = hash
+		d.devices = devices
+
+		if d.DeviceId >= devices.nextFreeDevice {
+			devices.nextFreeDevice = d.DeviceId + 1
+		}
+
+		// If the transaction id is larger than the actual one we lost the device due to some crash
+		if d.TransactionId > devices.TransactionId {
+			utils.Debugf("Removing lost device %s with id %d", hash, d.TransactionId)
+			delete(devices.Devices, hash)
+		}
+	}
+	return nil
+}
+
+func (devices *DeviceSet) setupBaseImage() error {
+	oldInfo := devices.Devices[""]
+	if oldInfo != nil && oldInfo.Initialized {
+		return nil
+	}
+
+	if oldInfo != nil && !oldInfo.Initialized {
+		utils.Debugf("Removing uninitialized base image")
+		if err := devices.removeDevice(""); err != nil {
+			utils.Debugf("\n--->Err: %s\n", err)
+			return err
+		}
+	}
+
+	utils.Debugf("Initializing base device-manager snapshot")
+
+	id := devices.allocateDeviceId()
+
+	// Create initial device
+	if err := createDevice(devices.getPoolDevName(), id); err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
+	info, err := devices.registerDevice(id, "", DefaultBaseFsSize)
+	if err != nil {
+		_ = deleteDevice(devices.getPoolDevName(), id)
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	utils.Debugf("Creating filesystem on base device-manager snapshot")
+
+	if err = devices.activateDeviceIfNeeded(""); err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	if err := devices.createFilesystem(info); err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	info.Initialized = true
+	if err = devices.saveMetadata(); err != nil {
+		info.Initialized = false
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	return nil
+}
+
+func setCloseOnExec(name string) {
+	fileInfos, _ := ioutil.ReadDir("/proc/self/fd")
+	if fileInfos != nil {
+		for _, i := range fileInfos {
+			link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name()))
+			if link == name {
+				fd, err := strconv.Atoi(i.Name())
+				if err == nil {
+					syscall.CloseOnExec(fd)
+				}
+			}
+		}
+	}
+}
+
+func (devices *DeviceSet) log(level int, file string, line int, dmError int, message string) {
+	if level >= 7 {
+		return // Ignore _LOG_DEBUG
+	}
+
+	utils.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
+}
+
+func major(device uint64) uint64 {
+	return (device >> 8) & 0xfff
+}
+
+func minor(device uint64) uint64 {
+	return (device & 0xff) | ((device >> 12) & 0xfff00)
+}
+
+func (devices *DeviceSet) initDevmapper() error {
+	logInit(devices)
+
+	// Make sure the sparse images exist in <root>/devicemapper/data and
+	// <root>/devicemapper/metadata
+
+	createdLoopback := !devices.hasImage("data") || !devices.hasImage("metadata")
+	data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
+	if err != nil {
+		utils.Debugf("Error device ensureImage (data): %s\n", err)
+		return err
+	}
+	metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
+	if err != nil {
+		utils.Debugf("Error device ensureImage (metadata): %s\n", err)
+		return err
+	}
+
+	// Set the device prefix from the device id and inode of the docker root dir
+
+	st, err := os.Stat(devices.root)
+	if err != nil {
+		return fmt.Errorf("Error looking up dir %s: %s", devices.root, err)
+	}
+	sysSt := st.Sys().(*syscall.Stat_t)
+	// "reg-" stands for "regular file".
+	// In the future we might use "dev-" for "device file", etc.
+	// docker-maj,min[-inode] stands for:
+	//	- Managed by docker
+	//	- The target of this device is at major <maj> and minor <min>
+	//	- If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself.
+	devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(sysSt.Dev), minor(sysSt.Dev), sysSt.Ino)
+	utils.Debugf("Generated prefix: %s", devices.devicePrefix)
+
+	// Check for the existence of the device <prefix>-pool
+	utils.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
+	info, err := getInfo(devices.getPoolName())
+	if info == nil {
+		utils.Debugf("Error device getInfo: %s", err)
+		return err
+	}
+
+	// It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files
+	// that are not Close-on-exec, and lxc-start will die if it inherits any unexpected files,
+	// so we add this badhack to make sure it closes itself
+	setCloseOnExec("/dev/mapper/control")
+
+	// If the pool doesn't exist, create it
+	if info.Exists == 0 {
+		utils.Debugf("Pool doesn't exist. Creating it.")
+
+		dataFile, err := AttachLoopDevice(data)
+		if err != nil {
+			utils.Debugf("\n--->Err: %s\n", err)
+			return err
+		}
+		defer dataFile.Close()
+
+		metadataFile, err := AttachLoopDevice(metadata)
+		if err != nil {
+			utils.Debugf("\n--->Err: %s\n", err)
+			return err
+		}
+		defer metadataFile.Close()
+
+		if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
+			utils.Debugf("\n--->Err: %s\n", err)
+			return err
+		}
+	}
+
+	// If we didn't just create the data or metadata image, we need to
+	// load the metadata from the existing file.
+	if !createdLoopback {
+		if err = devices.loadMetaData(); err != nil {
+			utils.Debugf("\n--->Err: %s\n", err)
+			return err
+		}
+	}
+
+	// Setup the base image
+	if err := devices.setupBaseImage(); err != nil {
+		utils.Debugf("Error device setupBaseImage: %s\n", err)
+		return err
+	}
+
+	return nil
+}
+
+func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
+	devices.Lock()
+	defer devices.Unlock()
+
+	if err := devices.ensureInit(); err != nil {
+		utils.Debugf("Error init: %s\n", err)
+		return err
+	}
+
+	if devices.Devices[hash] != nil {
+		return fmt.Errorf("hash %s already exists", hash)
+	}
+
+	baseInfo := devices.Devices[baseHash]
+	if baseInfo == nil {
+		return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash)
+	}
+
+	deviceId := devices.allocateDeviceId()
+
+	if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
+		utils.Debugf("Error creating snap device: %s\n", err)
+		return err
+	}
+
+	if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
+		deleteDevice(devices.getPoolDevName(), deviceId)
+		utils.Debugf("Error registering device: %s\n", err)
+		return err
+	}
+	return nil
+}
+
+func (devices *DeviceSet) removeDevice(hash string) error {
+	info := devices.Devices[hash]
+	if info == nil {
+		return fmt.Errorf("hash %s doesn't exists", hash)
+	}
+
+	devinfo, _ := getInfo(info.Name())
+	if devinfo != nil && devinfo.Exists != 0 {
+		if err := removeDevice(info.Name()); err != nil {
+			utils.Debugf("Error removing device: %s\n", err)
+			return err
+		}
+	}
+
+	if info.Initialized {
+		info.Initialized = false
+		if err := devices.saveMetadata(); err != nil {
+			utils.Debugf("Error saving meta data: %s\n", err)
+			return err
+		}
+	}
+
+	if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
+		utils.Debugf("Error deleting device: %s\n", err)
+		return err
+	}
+
+	devices.allocateTransactionId()
+	delete(devices.Devices, info.Hash)
+
+	if err := devices.saveMetadata(); err != nil {
+		devices.Devices[info.Hash] = info
+		utils.Debugf("Error saving meta data: %s\n", err)
+		return err
+	}
+
+	return nil
+}
+
+func (devices *DeviceSet) RemoveDevice(hash string) error {
+	devices.Lock()
+	defer devices.Unlock()
+
+	if err := devices.ensureInit(); err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	return devices.removeDevice(hash)
+}
+
+func (devices *DeviceSet) deactivateDevice(hash string) error {
+	utils.Debugf("[devmapper] deactivateDevice(%s)", hash)
+	defer utils.Debugf("[devmapper] deactivateDevice END")
+	var devname string
+	// FIXME: shouldn't we just register the pool into devices?
+	devname, err := devices.byHash(hash)
+	if err != nil {
+		return err
+	}
+	devinfo, err := getInfo(devname)
+	if err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+	if devinfo.Exists != 0 {
+		if err := removeDevice(devname); err != nil {
+			utils.Debugf("\n--->Err: %s\n", err)
+			return err
+		}
+		if err := devices.waitRemove(hash); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// waitRemove blocks until either:
+// a) the device registered at <device_set_prefix>-<hash> is removed,
+// or b) the 1 second timeout expires.
+func (devices *DeviceSet) waitRemove(hash string) error {
+	utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, hash)
+	defer utils.Debugf("[deviceset %s] waitRemove END", devices.devicePrefix, hash)
+	devname, err := devices.byHash(hash)
+	if err != nil {
+		return err
+	}
+	i := 0
+	for ; i < 1000; i += 1 {
+		devinfo, err := getInfo(devname)
+		if err != nil {
+			// If there is an error we assume the device doesn't exist.
+			// The error might actually be something else, but we can't differentiate.
+			return nil
+		}
+		utils.Debugf("Waiting for removal of %s: exists=%d", devname, devinfo.Exists)
+		if devinfo.Exists == 0 {
+			break
+		}
+		time.Sleep(1 * time.Millisecond)
+	}
+	if i == 1000 {
+		return fmt.Errorf("Timeout while waiting for device %s to be removed", devname)
+	}
+	return nil
+}
+
+// waitClose blocks until either:
+// a) the device registered at <device_set_prefix>-<hash> is closed,
+// or b) the 1 second timeout expires.
+func (devices *DeviceSet) waitClose(hash string) error {
+	devname, err := devices.byHash(hash)
+	if err != nil {
+		return err
+	}
+	i := 0
+	for ; i < 1000; i += 1 {
+		devinfo, err := getInfo(devname)
+		if err != nil {
+			return err
+		}
+		utils.Debugf("Waiting for unmount of %s: opencount=%d", devname, devinfo.OpenCount)
+		if devinfo.OpenCount == 0 {
+			break
+		}
+		time.Sleep(1 * time.Millisecond)
+	}
+	if i == 1000 {
+		return fmt.Errorf("Timeout while waiting for device %s to close", devname)
+	}
+	return nil
+}
+
+// byHash is a hack to allow looking up the deviceset's pool by the hash "pool".
+// FIXME: it seems probably cleaner to register the pool in devices.Devices,
+// but I am afraid of arcane implications deep in the devicemapper code,
+// so this will do.
+func (devices *DeviceSet) byHash(hash string) (devname string, err error) {
+	if hash == "pool" {
+		return devices.getPoolDevName(), nil
+	}
+	info := devices.Devices[hash]
+	if info == nil {
+		return "", fmt.Errorf("hash %s doesn't exists", hash)
+	}
+	return info.Name(), nil
+}
+
+func (devices *DeviceSet) Shutdown() error {
+	utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
+	defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
+	devices.Lock()
+	utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
+	defer devices.Unlock()
+
+	if !devices.initialized {
+		return nil
+	}
+
+	for path, count := range devices.activeMounts {
+		for i := count; i > 0; i-- {
+			if err := syscall.Unmount(path, 0); err != nil {
+				utils.Debugf("Shutdown unmounting %s, error: %s\n", path, err)
+			}
+		}
+		delete(devices.activeMounts, path)
+	}
+
+	for _, d := range devices.Devices {
+		if err := devices.waitClose(d.Hash); err != nil {
+			utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
+		}
+		if err := devices.deactivateDevice(d.Hash); err != nil {
+			utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
+		}
+	}
+
+	pool := devices.getPoolDevName()
+	if devinfo, err := getInfo(pool); err == nil && devinfo.Exists != 0 {
+		if err := devices.deactivateDevice("pool"); err != nil {
+			utils.Debugf("Shutdown deactivate %s , error: %s\n", pool, err)
+		}
+	}
+
+	return nil
+}
+
+func (devices *DeviceSet) MountDevice(hash, path string, readOnly bool) error {
+	devices.Lock()
+	defer devices.Unlock()
+
+	if err := devices.ensureInit(); err != nil {
+		return fmt.Errorf("Error initializing devmapper: %s", err)
+	}
+
+	if err := devices.activateDeviceIfNeeded(hash); err != nil {
+		return fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
+	}
+
+	info := devices.Devices[hash]
+
+	var flags uintptr = syscall.MS_MGC_VAL
+
+	if readOnly {
+		flags = flags | syscall.MS_RDONLY
+	}
+
+	err := syscall.Mount(info.DevName(), path, "ext4", flags, "discard")
+	if err != nil && err == syscall.EINVAL {
+		err = syscall.Mount(info.DevName(), path, "ext4", flags, "")
+	}
+	if err != nil {
+		return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
+	}
+
+	count := devices.activeMounts[path]
+	devices.activeMounts[path] = count + 1
+
+	return nil
+}
+
+func (devices *DeviceSet) UnmountDevice(hash, path string, deactivate bool) error {
+	utils.Debugf("[devmapper] UnmountDevice(hash=%s path=%s)", hash, path)
+	defer utils.Debugf("[devmapper] UnmountDevice END")
+	devices.Lock()
+	defer devices.Unlock()
+
+	utils.Debugf("[devmapper] Unmount(%s)", path)
+	if err := syscall.Unmount(path, 0); err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+	utils.Debugf("[devmapper] Unmount done")
+	// Wait for the unmount to be effective,
+	// by watching the value of Info.OpenCount for the device
+	if err := devices.waitClose(hash); err != nil {
+		return err
+	}
+
+	if count := devices.activeMounts[path]; count > 1 {
+		devices.activeMounts[path] = count - 1
+	} else {
+		delete(devices.activeMounts, path)
+	}
+
+	if deactivate {
+		devices.deactivateDevice(hash)
+	}
+
+	return nil
+}
+
+func (devices *DeviceSet) HasDevice(hash string) bool {
+	devices.Lock()
+	defer devices.Unlock()
+
+	if err := devices.ensureInit(); err != nil {
+		return false
+	}
+	return devices.Devices[hash] != nil
+}
+
+func (devices *DeviceSet) HasInitializedDevice(hash string) bool {
+	devices.Lock()
+	defer devices.Unlock()
+
+	if err := devices.ensureInit(); err != nil {
+		return false
+	}
+
+	info := devices.Devices[hash]
+	return info != nil && info.Initialized
+}
+
+func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
+	devices.Lock()
+	defer devices.Unlock()
+
+	if err := devices.ensureInit(); err != nil {
+		return false
+	}
+
+	info := devices.Devices[hash]
+	if info == nil {
+		return false
+	}
+	devinfo, _ := getInfo(info.Name())
+	return devinfo != nil && devinfo.Exists != 0
+}
+
+func (devices *DeviceSet) SetInitialized(hash string) error {
+	devices.Lock()
+	defer devices.Unlock()
+
+	if err := devices.ensureInit(); err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	info := devices.Devices[hash]
+	if info == nil {
+		return fmt.Errorf("Unknown device %s", hash)
+	}
+
+	info.Initialized = true
+	if err := devices.saveMetadata(); err != nil {
+		info.Initialized = false
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	return nil
+}
+
+func (devices *DeviceSet) Status() *Status {
+	devices.Lock()
+	defer devices.Unlock()
+
+	status := &Status{}
+
+	if err := devices.ensureInit(); err != nil {
+		return status
+	}
+
+	status.PoolName = devices.getPoolName()
+	status.DataLoopback = path.Join(devices.loopbackDir(), "data")
+	status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata")
+
+	_, totalSizeInSectors, _, params, err := getStatus(devices.getPoolName())
+	if err == nil {
+		var transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64
+		if _, err := fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal); err == nil {
+			// Convert from blocks to bytes
+			blockSizeInSectors := totalSizeInSectors / dataTotal
+
+			status.Data.Used = dataUsed * blockSizeInSectors * 512
+			status.Data.Total = dataTotal * blockSizeInSectors * 512
+
+			// metadata blocks are always 4k
+			status.Metadata.Used = metadataUsed * 4096
+			status.Metadata.Total = metadataTotal * 4096
+		}
+	}
+
+	return status
+}
+
+func (devices *DeviceSet) ensureInit() error {
+	if !devices.initialized {
+		devices.initialized = true
+		if err := devices.initDevmapper(); err != nil {
+			utils.Debugf("\n--->Err: %s\n", err)
+			return err
+		}
+	}
+	return nil
+}
+
+func NewDeviceSet(root string) *DeviceSet {
+	SetDevDir("/dev")
+
+	return &DeviceSet{
+		initialized:  false,
+		root:         root,
+		MetaData:     MetaData{Devices: make(map[string]*DevInfo)},
+		activeMounts: make(map[string]int),
+	}
+}
diff --git a/devmapper/devmapper.go b/devmapper/devmapper.go
new file mode 100644
index 0000000..eae4224
--- /dev/null
+++ b/devmapper/devmapper.go
@@ -0,0 +1,709 @@
+package devmapper
+
+/*
+#cgo LDFLAGS: -L. -ldevmapper
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libdevmapper.h>
+#include <linux/loop.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <errno.h>
+
+#ifndef LOOP_CTL_GET_FREE
+#define LOOP_CTL_GET_FREE       0x4C82
+#endif
+
+// FIXME: this could easily be rewritten in go
+char*			attach_loop_device(const char *filename, int *loop_fd_out)
+{
+  struct loop_info64	loopinfo = {0};
+  struct stat		st;
+  char			buf[64];
+  int			i, loop_fd, fd, start_index;
+  char*			loopname;
+
+
+  *loop_fd_out = -1;
+
+  start_index = 0;
+  fd = open("/dev/loop-control", O_RDONLY);
+  if (fd >= 0) {
+    start_index = ioctl(fd, LOOP_CTL_GET_FREE);
+    close(fd);
+
+    if (start_index < 0)
+      start_index = 0;
+  }
+
+  fd = open(filename, O_RDWR);
+  if (fd < 0) {
+    perror("open");
+    return NULL;
+  }
+
+  loop_fd = -1;
+  for (i = start_index ; loop_fd < 0 ; i++ ) {
+    if (sprintf(buf, "/dev/loop%d", i) < 0) {
+	close(fd);
+	return NULL;
+    }
+
+    if (stat(buf, &st)) {
+      if (!S_ISBLK(st.st_mode)) {
+	 fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf);
+      } else if (errno == ENOENT) {
+	fprintf(stderr, "[error] There are no more loopback device available.\n");
+      } else {
+	fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno);
+      }
+      close(fd);
+      return NULL;
+    }
+
+    loop_fd = open(buf, O_RDWR);
+    if (loop_fd < 0 && errno == ENOENT) {
+      fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf);
+      close(fd);
+      return NULL;
+    } else if (loop_fd < 0) {
+	fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno);
+	continue;
+    }
+
+    if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) {
+      int errsv = errno;
+      close(loop_fd);
+      loop_fd = -1;
+      if (errsv != EBUSY) {
+        close(fd);
+        fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv));
+        return NULL;
+      }
+      continue;
+    }
+
+    close(fd);
+
+    strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE);
+    loopinfo.lo_offset = 0;
+    loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR;
+
+    if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) {
+      perror("ioctl LOOP_SET_STATUS64");
+      if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
+        perror("ioctl LOOP_CLR_FD");
+      }
+      close(loop_fd);
+      fprintf (stderr, "cannot set up loopback device info");
+      return (NULL);
+    }
+
+    loopname = strdup(buf);
+    if (loopname == NULL) {
+      close(loop_fd);
+      return (NULL);
+    }
+
+    *loop_fd_out = loop_fd;
+    return (loopname);
+  }
+
+  return (NULL);
+}
+
+static int64_t	get_block_size(int fd)
+{
+  uint64_t	size;
+
+  if (ioctl(fd, BLKGETSIZE64, &size) == -1)
+    return -1;
+  return ((int64_t)size);
+}
+
+extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str);
+
+static void
+log_cb(int level, const char *file, int line,
+       int dm_errno_or_class, const char *f, ...)
+{
+  char buffer[256];
+  va_list ap;
+
+  va_start(ap, f);
+  vsnprintf(buffer, 256, f, ap);
+  va_end(ap);
+
+  DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer);
+}
+
+static void
+log_with_errno_init ()
+{
+  dm_log_with_errno_init(log_cb);
+}
+
+*/
+import "C"
+
+import (
+	"errors"
+	"fmt"
+	"github.com/dotcloud/docker/utils"
+	"os"
+	"runtime"
+	"syscall"
+	"unsafe"
+)
+
+type DevmapperLogger interface {
+	log(level int, file string, line int, dmError int, message string)
+}
+
+const (
+	DeviceCreate TaskType = iota
+	DeviceReload
+	DeviceRemove
+	DeviceRemoveAll
+	DeviceSuspend
+	DeviceResume
+	DeviceInfo
+	DeviceDeps
+	DeviceRename
+	DeviceVersion
+	DeviceStatus
+	DeviceTable
+	DeviceWaitevent
+	DeviceList
+	DeviceClear
+	DeviceMknodes
+	DeviceListVersions
+	DeviceTargetMsg
+	DeviceSetGeometry
+)
+
+const (
+	AddNodeOnResume AddNodeType = iota
+	AddNodeOnCreate
+)
+
+var (
+	ErrTaskRun              = errors.New("dm_task_run failed")
+	ErrTaskSetName          = errors.New("dm_task_set_name failed")
+	ErrTaskSetMessage       = errors.New("dm_task_set_message failed")
+	ErrTaskSetAddNode       = errors.New("dm_task_set_add_node failed")
+	ErrTaskSetRO            = errors.New("dm_task_set_ro failed")
+	ErrTaskAddTarget        = errors.New("dm_task_add_target failed")
+	ErrGetDriverVersion     = errors.New("dm_task_get_driver_version failed")
+	ErrAttachLoopbackDevice = errors.New("loopback mounting failed")
+	ErrGetBlockSize         = errors.New("Can't get block size")
+	ErrUdevWait             = errors.New("wait on udev cookie failed")
+	ErrSetDevDir            = errors.New("dm_set_dev_dir failed")
+	ErrGetLibraryVersion    = errors.New("dm_get_library_version failed")
+	ErrCreateRemoveTask     = errors.New("Can't create task of type DeviceRemove")
+	ErrRunRemoveDevice      = errors.New("running removeDevice failed")
+)
+
+type (
+	Task struct {
+		unmanaged *C.struct_dm_task
+	}
+	Info struct {
+		Exists        int
+		Suspended     int
+		LiveTable     int
+		InactiveTable int
+		OpenCount     int32
+		EventNr       uint32
+		Major         uint32
+		Minor         uint32
+		ReadOnly      int
+		TargetCount   int32
+	}
+	TaskType    int
+	AddNodeType int
+)
+
+func (t *Task) destroy() {
+	if t != nil {
+		C.dm_task_destroy(t.unmanaged)
+		runtime.SetFinalizer(t, nil)
+	}
+}
+
+func TaskCreate(tasktype TaskType) *Task {
+	c_task := C.dm_task_create(C.int(tasktype))
+	if c_task == nil {
+		return nil
+	}
+	task := &Task{unmanaged: c_task}
+	runtime.SetFinalizer(task, (*Task).destroy)
+	return task
+}
+
+func (t *Task) Run() error {
+	if res := C.dm_task_run(t.unmanaged); res != 1 {
+		return ErrTaskRun
+	}
+	return nil
+}
+
+func (t *Task) SetName(name string) error {
+	c_name := C.CString(name)
+	defer free(c_name)
+
+	if res := C.dm_task_set_name(t.unmanaged, c_name); res != 1 {
+		return ErrTaskSetName
+	}
+	return nil
+}
+
+func (t *Task) SetMessage(message string) error {
+	c_message := C.CString(message)
+	defer free(c_message)
+
+	if res := C.dm_task_set_message(t.unmanaged, c_message); res != 1 {
+		return ErrTaskSetMessage
+	}
+	return nil
+}
+
+func (t *Task) SetSector(sector uint64) error {
+	if res := C.dm_task_set_sector(t.unmanaged, C.uint64_t(sector)); res != 1 {
+		return ErrTaskSetAddNode
+	}
+	return nil
+}
+
+func (t *Task) SetCookie(cookie *uint32, flags uint16) error {
+	c_cookie := C.uint32_t(*cookie)
+	if res := C.dm_task_set_cookie(t.unmanaged, &c_cookie, C.uint16_t(flags)); res != 1 {
+		return ErrTaskSetAddNode
+	}
+	*cookie = uint32(c_cookie)
+	return nil
+}
+
+func (t *Task) SetAddNode(add_node AddNodeType) error {
+	if res := C.dm_task_set_add_node(t.unmanaged, C.dm_add_node_t(add_node)); res != 1 {
+		return ErrTaskSetAddNode
+	}
+	return nil
+}
+
+func (t *Task) SetRo() error {
+	if res := C.dm_task_set_ro(t.unmanaged); res != 1 {
+		return ErrTaskSetRO
+	}
+	return nil
+}
+
+func (t *Task) AddTarget(start uint64, size uint64, ttype string, params string) error {
+	c_ttype := C.CString(ttype)
+	defer free(c_ttype)
+
+	c_params := C.CString(params)
+	defer free(c_params)
+
+	if res := C.dm_task_add_target(t.unmanaged, C.uint64_t(start), C.uint64_t(size), c_ttype, c_params); res != 1 {
+		return ErrTaskAddTarget
+	}
+	return nil
+}
+
+func (t *Task) GetDriverVersion() (string, error) {
+	buffer := C.CString(string(make([]byte, 128)))
+	defer free(buffer)
+
+	if res := C.dm_task_get_driver_version(t.unmanaged, buffer, 128); res != 1 {
+		return "", ErrGetDriverVersion
+	}
+	return C.GoString(buffer), nil
+}
+
+func (t *Task) GetInfo() (*Info, error) {
+	c_info := C.struct_dm_info{}
+	if res := C.dm_task_get_info(t.unmanaged, &c_info); res != 1 {
+		return nil, ErrGetDriverVersion
+	}
+	return &Info{
+		Exists:        int(c_info.exists),
+		Suspended:     int(c_info.suspended),
+		LiveTable:     int(c_info.live_table),
+		InactiveTable: int(c_info.inactive_table),
+		OpenCount:     int32(c_info.open_count),
+		EventNr:       uint32(c_info.event_nr),
+		Major:         uint32(c_info.major),
+		Minor:         uint32(c_info.minor),
+		ReadOnly:      int(c_info.read_only),
+		TargetCount:   int32(c_info.target_count),
+	}, nil
+}
+
+func (t *Task) GetNextTarget(next uintptr) (uintptr, uint64, uint64, string, string) {
+	var (
+		c_start, c_length       C.uint64_t
+		c_target_type, c_params *C.char
+	)
+
+	nextp := C.dm_get_next_target(t.unmanaged, unsafe.Pointer(next), &c_start, &c_length, &c_target_type, &c_params)
+	return uintptr(nextp), uint64(c_start), uint64(c_length), C.GoString(c_target_type), C.GoString(c_params)
+}
+
+func AttachLoopDevice(filename string) (*os.File, error) {
+	c_filename := C.CString(filename)
+	defer free(c_filename)
+
+	var fd C.int
+	res := C.attach_loop_device(c_filename, &fd)
+	if res == nil {
+		if os.Getenv("DEBUG") != "" {
+			C.perror(C.CString(fmt.Sprintf("[debug] Error attach_loop_device(%s, %d)", filename, int(fd))))
+		}
+		return nil, ErrAttachLoopbackDevice
+	}
+	defer free(res)
+
+	return os.NewFile(uintptr(fd), C.GoString(res)), nil
+}
+
+func getBlockSize(fd uintptr) int {
+	var size uint64
+
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))); err != 0 {
+		utils.Debugf("Error ioctl: %s", err)
+		return -1
+	}
+	return int(size)
+}
+
+func GetBlockDeviceSize(file *os.File) (uint64, error) {
+	if size := C.get_block_size(C.int(file.Fd())); size == -1 {
+		return 0, ErrGetBlockSize
+	} else {
+		return uint64(size), nil
+	}
+}
+
+func UdevWait(cookie uint32) error {
+	if res := C.dm_udev_wait(C.uint32_t(cookie)); res != 1 {
+		utils.Debugf("Failed to wait on udev cookie %d", cookie)
+		return ErrUdevWait
+	}
+	return nil
+}
+
+func LogInitVerbose(level int) {
+	C.dm_log_init_verbose(C.int(level))
+}
+
+var dmLogger DevmapperLogger = nil
+
+func logInit(logger DevmapperLogger) {
+	dmLogger = logger
+	C.log_with_errno_init()
+}
+
+func SetDevDir(dir string) error {
+	c_dir := C.CString(dir)
+	defer free(c_dir)
+
+	if res := C.dm_set_dev_dir(c_dir); res != 1 {
+		utils.Debugf("Error dm_set_dev_dir")
+		return ErrSetDevDir
+	}
+	return nil
+}
+
+func GetLibraryVersion() (string, error) {
+	buffer := C.CString(string(make([]byte, 128)))
+	defer free(buffer)
+
+	if res := C.dm_get_library_version(buffer, 128); res != 1 {
+		return "", ErrGetLibraryVersion
+	}
+	return C.GoString(buffer), nil
+}
+
+// Useful helper for cleanup
+func RemoveDevice(name string) error {
+	task := TaskCreate(DeviceRemove)
+	if task == nil {
+		return ErrCreateRemoveTask
+	}
+	if err := task.SetName(name); err != nil {
+		utils.Debugf("Can't set task name %s", name)
+		return err
+	}
+	if err := task.Run(); err != nil {
+		return ErrRunRemoveDevice
+	}
+	return nil
+}
+
+func free(p *C.char) {
+	C.free(unsafe.Pointer(p))
+}
+
+// This is the programmatic example of "dmsetup create"
+func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error {
+	task, err := createTask(DeviceCreate, poolName)
+	if task == nil {
+		return err
+	}
+
+	size, err := GetBlockDeviceSize(dataFile)
+	if err != nil {
+		return fmt.Errorf("Can't get data size")
+	}
+
+	params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768"
+	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
+		return fmt.Errorf("Can't add target")
+	}
+
+	var cookie uint32 = 0
+	if err := task.SetCookie(&cookie, 0); err != nil {
+		return fmt.Errorf("Can't set cookie")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running DeviceCreate")
+	}
+
+	UdevWait(cookie)
+
+	return nil
+}
+
+func createTask(t TaskType, name string) (*Task, error) {
+	task := TaskCreate(t)
+	if task == nil {
+		return nil, fmt.Errorf("Can't create task of type %d", int(t))
+	}
+	if err := task.SetName(name); err != nil {
+		return nil, fmt.Errorf("Can't set task name %s", name)
+	}
+	return task, nil
+}
+
+func getInfo(name string) (*Info, error) {
+	task, err := createTask(DeviceInfo, name)
+	if task == nil {
+		return nil, err
+	}
+	if err := task.Run(); err != nil {
+		return nil, err
+	}
+	return task.GetInfo()
+}
+
+func getStatus(name string) (uint64, uint64, string, string, error) {
+	task, err := createTask(DeviceStatus, name)
+	if task == nil {
+		utils.Debugf("getStatus: Error createTask: %s", err)
+		return 0, 0, "", "", err
+	}
+	if err := task.Run(); err != nil {
+		utils.Debugf("getStatus: Error Run: %s", err)
+		return 0, 0, "", "", err
+	}
+
+	devinfo, err := task.GetInfo()
+	if err != nil {
+		utils.Debugf("getStatus: Error GetInfo: %s", err)
+		return 0, 0, "", "", err
+	}
+	if devinfo.Exists == 0 {
+		utils.Debugf("getStatus: Non existing device %s", name)
+		return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
+	}
+
+	_, start, length, target_type, params := task.GetNextTarget(0)
+	return start, length, target_type, params, nil
+}
+
+func setTransactionId(poolName string, oldId uint64, newId uint64) error {
+	task, err := createTask(DeviceTargetMsg, poolName)
+	if task == nil {
+		return err
+	}
+
+	if err := task.SetSector(0); err != nil {
+		return fmt.Errorf("Can't set sector")
+	}
+
+	if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
+		return fmt.Errorf("Can't set message")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running setTransactionId")
+	}
+	return nil
+}
+
+func suspendDevice(name string) error {
+	task, err := createTask(DeviceSuspend, name)
+	if task == nil {
+		return err
+	}
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running DeviceSuspend")
+	}
+	return nil
+}
+
+func resumeDevice(name string) error {
+	task, err := createTask(DeviceResume, name)
+	if task == nil {
+		return err
+	}
+
+	var cookie uint32 = 0
+	if err := task.SetCookie(&cookie, 0); err != nil {
+		return fmt.Errorf("Can't set cookie")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running DeviceSuspend")
+	}
+
+	UdevWait(cookie)
+
+	return nil
+}
+
+func createDevice(poolName string, deviceId int) error {
+	utils.Debugf("[devmapper] createDevice(poolName=%v, deviceId=%v)", poolName, deviceId)
+	task, err := createTask(DeviceTargetMsg, poolName)
+	if task == nil {
+		return err
+	}
+
+	if err := task.SetSector(0); err != nil {
+		return fmt.Errorf("Can't set sector")
+	}
+
+	if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
+		return fmt.Errorf("Can't set message")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running createDevice")
+	}
+	return nil
+}
+
+func deleteDevice(poolName string, deviceId int) error {
+	task, err := createTask(DeviceTargetMsg, poolName)
+	if task == nil {
+		return err
+	}
+
+	if err := task.SetSector(0); err != nil {
+		return fmt.Errorf("Can't set sector")
+	}
+
+	if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
+		return fmt.Errorf("Can't set message")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running deleteDevice")
+	}
+	return nil
+}
+
+func removeDevice(name string) error {
+	utils.Debugf("[devmapper] removeDevice START")
+	defer utils.Debugf("[devmapper] removeDevice END")
+	task, err := createTask(DeviceRemove, name)
+	if task == nil {
+		return err
+	}
+	if err = task.Run(); err != nil {
+		return fmt.Errorf("Error running removeDevice")
+	}
+	return nil
+}
+
+func activateDevice(poolName string, name string, deviceId int, size uint64) error {
+	task, err := createTask(DeviceCreate, name)
+	if task == nil {
+		return err
+	}
+
+	params := fmt.Sprintf("%s %d", poolName, deviceId)
+	if err := task.AddTarget(0, size/512, "thin", params); err != nil {
+		return fmt.Errorf("Can't add target")
+	}
+	if err := task.SetAddNode(AddNodeOnCreate); err != nil {
+		return fmt.Errorf("Can't add node")
+	}
+
+	var cookie uint32 = 0
+	if err := task.SetCookie(&cookie, 0); err != nil {
+		return fmt.Errorf("Can't set cookie")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running DeviceCreate")
+	}
+
+	UdevWait(cookie)
+
+	return nil
+}
+
+func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error {
+	devinfo, _ := getInfo(baseName)
+	doSuspend := devinfo != nil && devinfo.Exists != 0
+
+	if doSuspend {
+		if err := suspendDevice(baseName); err != nil {
+			return err
+		}
+	}
+
+	task, err := createTask(DeviceTargetMsg, poolName)
+	if task == nil {
+		if doSuspend {
+			resumeDevice(baseName)
+		}
+		return err
+	}
+
+	if err := task.SetSector(0); err != nil {
+		if doSuspend {
+			resumeDevice(baseName)
+		}
+		return fmt.Errorf("Can't set sector")
+	}
+
+	if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
+		if doSuspend {
+			resumeDevice(baseName)
+		}
+		return fmt.Errorf("Can't set message")
+	}
+
+	if err := task.Run(); err != nil {
+		if doSuspend {
+			resumeDevice(baseName)
+		}
+		return fmt.Errorf("Error running DeviceCreate")
+	}
+
+	if doSuspend {
+		if err := resumeDevice(baseName); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/devmapper/devmapper_log.go b/devmapper/devmapper_log.go
new file mode 100644
index 0000000..1f95eb7
--- /dev/null
+++ b/devmapper/devmapper_log.go
@@ -0,0 +1,13 @@
+package devmapper
+
+import "C"
+
+// Due to the way cgo works this has to be in a separate file, as devmapper.go has
+// definitions in the cgo block, which is incompatible with using "//export"
+
+//export DevmapperLogCallback
+func DevmapperLogCallback(level C.int, file *C.char, line C.int, dm_errno_or_class C.int, message *C.char) {
+	if dmLogger != nil {
+		dmLogger.log(int(level), C.GoString(file), int(line), int(dm_errno_or_class), C.GoString(message))
+	}
+}
diff --git a/devmapper/docker-device-tool/device_tool.go b/devmapper/docker-device-tool/device_tool.go
new file mode 100644
index 0000000..f66762d
--- /dev/null
+++ b/devmapper/docker-device-tool/device_tool.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+	"fmt"
+	"github.com/dotcloud/docker/devmapper"
+	"os"
+)
+
+func usage() {
+	fmt.Printf("Usage: %s [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0])
+	os.Exit(1)
+}
+
+func main() {
+	devices := devmapper.NewDeviceSet("/var/lib/docker")
+
+	if len(os.Args) < 2 {
+		usage()
+	}
+
+	cmd := os.Args[1]
+	if cmd == "snap" {
+		if len(os.Args) < 4 {
+			usage()
+		}
+
+		err := devices.AddDevice(os.Args[2], os.Args[3])
+		if err != nil {
+			fmt.Println("Can't create snap device: ", err)
+			os.Exit(1)
+		}
+	} else if cmd == "remove" {
+		if len(os.Args) < 3 {
+			usage()
+		}
+
+		err := devices.RemoveDevice(os.Args[2])
+		if err != nil {
+			fmt.Println("Can't remove device: ", err)
+			os.Exit(1)
+		}
+	} else if cmd == "mount" {
+		if len(os.Args) < 4 {
+			usage()
+		}
+
+		err := devices.MountDevice(os.Args[2], os.Args[3])
+		if err != nil {
+			fmt.Println("Can't create snap device: ", err)
+			os.Exit(1)
+		}
+	} else {
+		fmt.Printf("Unknown command %s\n", cmd)
+		if len(os.Args) < 4 {
+			usage()
+		}
+
+		os.Exit(1)
+	}
+
+	return
+}
diff --git a/docker-init/docker-init.go b/docker-init/docker-init.go
new file mode 100644
index 0000000..0c363f4
--- /dev/null
+++ b/docker-init/docker-init.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+	"github.com/dotcloud/docker/sysinit"
+)
+
+var (
+	GITCOMMIT string
+	VERSION   string
+)
+
+func main() {
+	// Running in init mode
+	sysinit.SysInit()
+	return
+}
diff --git a/docker/docker.go b/docker/docker.go
index deae9b6..9248c46 100644
--- a/docker/docker.go
+++ b/docker/docker.go
@@ -8,6 +8,7 @@
 	"github.com/dotcloud/docker/utils"
 	"io/ioutil"
 	"log"
+	"net"
 	"os"
 	"os/signal"
 	"strconv"
@@ -36,9 +37,14 @@
 	flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.")
 	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
 	flDns := flag.String("dns", "", "Set custom dns servers")
-	flHosts := docker.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
+	flHosts := utils.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
 	flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
+	flEnableIptables := flag.Bool("iptables", true, "Disable iptables within docker")
+	flDefaultIp := flag.String("ip", "0.0.0.0", "Default ip address to use when binding a containers ports")
+	flInterContainerComm := flag.Bool("enable-container-comm", false, "Enable inter-container communication")
+
 	flag.Parse()
+
 	if *flVersion {
 		showVersion()
 		return
@@ -50,10 +56,9 @@
 		flHosts[i] = utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost)
 	}
 
+	bridge := docker.DefaultNetworkBridge
 	if *bridgeName != "" {
-		docker.NetworkBridgeIface = *bridgeName
-	} else {
-		docker.NetworkBridgeIface = docker.DefaultNetworkBridge
+		bridge = *bridgeName
 	}
 	if *flDebug {
 		os.Setenv("DEBUG", "1")
@@ -65,7 +70,26 @@
 			flag.Usage()
 			return
 		}
-		if err := daemon(*pidfile, *flGraphPath, flHosts, *flAutoRestart, *flEnableCors, *flDns); err != nil {
+		var dns []string
+		if *flDns != "" {
+			dns = []string{*flDns}
+		}
+
+		ip := net.ParseIP(*flDefaultIp)
+
+		config := &docker.DaemonConfig{
+			Pidfile:                     *pidfile,
+			GraphPath:                   *flGraphPath,
+			AutoRestart:                 *flAutoRestart,
+			EnableCors:                  *flEnableCors,
+			Dns:                         dns,
+			EnableIptables:              *flEnableIptables,
+			BridgeIface:                 bridge,
+			ProtoAddresses:              flHosts,
+			DefaultIp:                   ip,
+			InterContainerCommunication: *flInterContainerComm,
+		}
+		if err := daemon(config); err != nil {
 			log.Fatal(err)
 			os.Exit(-1)
 		}
@@ -116,30 +140,26 @@
 	}
 }
 
-func daemon(pidfile string, flGraphPath string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error {
-	if err := createPidFile(pidfile); err != nil {
+func daemon(config *docker.DaemonConfig) error {
+	if err := createPidFile(config.Pidfile); err != nil {
 		log.Fatal(err)
 	}
-	defer removePidFile(pidfile)
+	defer removePidFile(config.Pidfile)
 
 	c := make(chan os.Signal, 1)
-	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
+	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
 	go func() {
 		sig := <-c
 		log.Printf("Received signal '%v', exiting\n", sig)
-		removePidFile(pidfile)
+		removePidFile(config.Pidfile)
 		os.Exit(0)
 	}()
-	var dns []string
-	if flDns != "" {
-		dns = []string{flDns}
-	}
-	server, err := docker.NewServer(flGraphPath, autoRestart, enableCors, dns)
+	server, err := docker.NewServer(config)
 	if err != nil {
 		return err
 	}
-	chErrors := make(chan error, len(protoAddrs))
-	for _, protoAddr := range protoAddrs {
+	chErrors := make(chan error, len(config.ProtoAddresses))
+	for _, protoAddr := range config.ProtoAddresses {
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
 		if protoAddrParts[0] == "unix" {
 			syscall.Unlink(protoAddrParts[1])
@@ -155,7 +175,7 @@
 			chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
 		}()
 	}
-	for i := 0; i < len(protoAddrs); i += 1 {
+	for i := 0; i < len(config.ProtoAddresses); i += 1 {
 		err := <-chErrors
 		if err != nil {
 			return err
diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst
index 71a902d..db0a03d 100644
--- a/docs/sources/commandline/cli.rst
+++ b/docs/sources/commandline/cli.rst
@@ -93,8 +93,8 @@
 
 .. _cli_build_examples:
 
-Examples
-~~~~~~~~
+Examples:
+~~~~~~~~~
 
 .. code-block:: bash
 
@@ -400,6 +400,33 @@
 
     Kill a running container
 
+.. _cli_link:
+
+``link``
+--------
+
+::
+
+    Usage: docker link CURRENT_NAME NEW_NAME
+
+    Link a container to a new name.
+
+
+Examples:
+~~~~~~~~~
+
+.. code-block:: bash
+
+    $ docker link /59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc /redis
+    $ docker ls
+    NAME                                                                      ID                                                                 IMAGE
+    /redis                                                                    59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc   crosbymichael/redis:latest
+    /59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc         59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc   crosbymichael/redis:latest
+
+
+This will create a new link for the existing name ``/59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc`` 
+with the new name ``/redis`` so that we can new reference the same container under the new name ``/redis``.
+
 .. _cli_login:
 
 ``login``
@@ -427,7 +454,6 @@
 ``logs``
 --------
 
-
 ::
 
     Usage: docker logs [OPTIONS] CONTAINER
@@ -507,6 +533,29 @@
     Usage: docker rm [OPTIONS] CONTAINER
 
     Remove one or more containers
+        -link="": Remove the link instead of the actual container
+ 
+
+Examples:
+~~~~~~~~~
+
+.. code-block:: bash
+
+    $ docker rm /redis
+    /redis
+
+
+This will remove the container referenced under the link ``/redis``.
+
+
+.. code-block:: bash
+
+    $ docker rm -link /webapp/redis
+    /webapp/redis
+
+
+This will remove the underlying link between ``/webapp`` and the ``/redis`` containers removing all
+network communication.
 
 .. _cli_rmi:
 
@@ -530,7 +579,7 @@
 
     Run a command in a new container
 
-      -a=map[]: Attach to stdin, stdout or stderr.
+      -a=map[]: Attach to stdin, stdout or stderr
       -c=0: CPU shares (relative weight)
       -cidfile="": Write the container ID to the file
       -d=false: Detached mode: Run container in the background, print new container id
@@ -546,13 +595,15 @@
       -u="": Username or UID
       -dns=[]: Set custom dns servers for the container
       -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume.
-      -volumes-from="": Mount all volumes from the given container.
-      -entrypoint="": Overwrite the default entrypoint set by the image.
+      -volumes-from="": Mount all volumes from the given container
+      -entrypoint="": Overwrite the default entrypoint set by the image
       -w="": Working directory inside the container
       -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
+      -expose=[]: Expose a port from the container without publishing it to your host
+      -link="": Add link to another container (containerid:alias)
 
 Examples
-~~~~~~~~
+--------
 
 .. code-block:: bash
 
@@ -600,6 +651,38 @@
 returned by ``pwd``. So this combination executes the command
 using the container, but inside the current working directory.
 
+.. code-block:: bash
+
+    docker run -p 127.0.0.0::80 ubuntu bash
+
+This the ``-p`` flag now allows you to bind a port to a specific
+interface of the host machine.  In this example port ``80`` of the 
+container will have a dynamically allocated port bound to 127.0.0.1 
+of the host.
+
+.. code-block:: bash
+
+    docker run -p 127.0.0.1:80:80 ubuntu bash
+
+This will bind port ``80`` of the container to port ``80`` on 127.0.0.1 of your
+host machine.
+
+.. code-block:: bash
+
+    docker run -expose 80 ubuntu bash
+
+This will expose port ``80`` of the container for use within a link
+without publishing the port to the host system's interfaces.  
+
+.. code-block:: bash
+
+    docker run -link /redis:redis ubuntu bash
+
+The ``-link`` flag will link the container named ``/redis`` into the 
+newly created container with the alias ``redis``.  The new container
+can access the network and environment of the redis container via
+environment variables.
+
 .. _cli_search:
 
 ``search``
diff --git a/docs/sources/examples/index.rst b/docs/sources/examples/index.rst
index 0125168..74a7e7c 100644
--- a/docs/sources/examples/index.rst
+++ b/docs/sources/examples/index.rst
@@ -1,6 +1,6 @@
 :title: Docker Examples
 :description: Examples on how to use Docker
-:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql
+:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql, link
 
 
 .. _example_list:
@@ -24,3 +24,4 @@
    postgresql_service
    mongodb
    running_riak_service
+   linking_into_redis
diff --git a/docs/sources/examples/linking_into_redis.rst b/docs/sources/examples/linking_into_redis.rst
new file mode 100644
index 0000000..70a83b4
--- /dev/null
+++ b/docs/sources/examples/linking_into_redis.rst
@@ -0,0 +1,132 @@
+:title: Linking to an Redis container
+:description: Running redis linked into your web app
+:keywords: docker, example, networking, redis, link
+
+.. _linking_redis:
+
+Linking Redis
+=============
+
+.. include:: example_header.inc
+
+Building a redis container to link as a child of our web application.
+
+Building the redis container
+----------------------------
+
+We will use a pre-build version of redis from the index under 
+the name ``crosbymichael/redis``.  If you are interested in the 
+Dockerfile that was used to build this container here it is.
+
+.. code-block:: bash
+    
+    # Build redis from source
+    # Make sure you have the redis source code checked out in
+    # the same directory as this Dockerfile
+    FROM ubuntu
+
+    RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
+    RUN apt-get update
+    RUN apt-get upgrade -y
+
+    RUN apt-get install -y gcc make g++ build-essential libc6-dev tcl
+
+    ADD . /redis
+
+    RUN (cd /redis && make)
+    RUN (cd /redis && make test)
+
+    RUN mkdir -p /redis-data
+    VOLUME ["/redis-data"]
+    EXPOSE 6379
+
+    ENTRYPOINT ["/redis/src/redis-server"]
+    CMD ["--dir", "/redis-data"]
+
+
+We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports 
+to connect to our redis container on.  If you do not expose any ports for the
+image then docker will not be able to establish the link between containers.
+
+
+Run the redis container
+-----------------------
+
+.. code-block:: bash
+    
+    docker run -d -e PASSWORD=docker crosbymichael/redis --requirepass docker
+ 
+This will run our redis container using the default port of 6379 and using
+as password to secure our service. Next we will link the redis container to 
+a new name using ``docker link``.
+
+
+Linking an existing container
+-----------------------------
+
+Docker will automatically create an initial link with the container's id but
+because the is long and not very user friendly we can link the container with
+a new name.
+
+.. code-block:: bash
+
+    docker link /39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 /redis
+
+Now we can reference our running redis service using the friendly name ``/redis``.  
+We can issue all the commands that you would expect; start, stop, attach, using the new name.
+
+Linking redis as a child
+------------------------
+
+Next we can start a new web application that has a dependency on redis and apply a link 
+to connect both containers.  If you noticed when running our redis service we did not use
+the ``-p`` option to publish the redis port to the host system.  Redis exposed port 6379
+but we did not publish the port.  This allows docker to prevent all network traffic to
+the redis container except when explicitly specified within a link.  This is a big win
+for security.  
+
+
+Now lets start our web application with a link into redis.
+
+.. code-block:: bash
+   
+    docker run -t -i -link /redis:db ubuntu bash
+
+    root@4c01db0b339c:/# env
+
+    HOSTNAME=4c01db0b339c
+    DB_NAME=/4c01db0b339cf19958731255a796ee072040a652f51652a4ade190ab8c27006f/db
+    TERM=xterm
+    DB_PORT=tcp://172.17.0.8:6379
+    DB_PORT_6379_TCP=tcp://172.17.0.8:6379
+    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+    PWD=/
+    DB_ENV_PASSWORD=dockerpass
+    SHLVL=1
+    HOME=/
+    container=lxc
+    _=/usr/bin/env
+    root@4c01db0b339c:/#
+
+
+When we inspect the environment of the linked container we can see a few extra environment 
+variables have been added.  When you specified ``-link /redis:db`` you are telling docker
+to link the container named ``/redis`` into this new container with the alias ``db``.  
+Environment variables are prefixed with the alias so that the parent container can access
+network and environment information from the child.
+
+.. code-block:: bash
+
+    # The name of the child container
+    DB_NAME=/4c01db0b339cf19958731255a796ee072040a652f51652a4ade190ab8c27006f/db
+    # The default protocol, ip, and port of the service running in the container
+    DB_PORT=tcp://172.17.0.8:6379
+    # A specific protocol, ip, and port of various services
+    DB_PORT_6379_TCP=tcp://172.17.0.8:6379
+    # Get environment variables of the container 
+    DB_ENV_PASSWORD=dockerpass
+
+
+Accessing the network information along with the environment of the child container allows
+us to easily connect to the redis service on the specific ip and port and use the password
+specified in the environment.  
diff --git a/gograph/MAINTAINERS b/gograph/MAINTAINERS
new file mode 100644
index 0000000..1e998f8
--- /dev/null
+++ b/gograph/MAINTAINERS
@@ -0,0 +1 @@
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
diff --git a/gograph/gograph.go b/gograph/gograph.go
new file mode 100644
index 0000000..38de5f4
--- /dev/null
+++ b/gograph/gograph.go
@@ -0,0 +1,462 @@
+package gograph
+
+import (
+	_ "code.google.com/p/gosqlite/sqlite3"
+	"database/sql"
+	"fmt"
+	"os"
+	"path"
+)
+
+const (
+	createEntityTable = `
+    CREATE TABLE IF NOT EXISTS entity (
+        id text NOT NULL PRIMARY KEY
+    );`
+
+	createEdgeTable = `
+    CREATE TABLE IF NOT EXISTS edge (
+        "entity_id" text NOT NULL,
+        "parent_id" text NULL,
+        "name" text NOT NULL,
+        CONSTRAINT "parent_fk" FOREIGN KEY ("parent_id") REFERENCES "entity" ("id"),
+        CONSTRAINT "entity_fk" FOREIGN KEY ("entity_id") REFERENCES "entity" ("id")
+        );
+    `
+
+	createEdgeIndices = `
+    CREATE UNIQUE INDEX "name_parent_ix" ON "edge" (parent_id, name);
+    `
+)
+
+// Entity with a unique id
+type Entity struct {
+	id string
+}
+
+// An Edge connects two entities together
+type Edge struct {
+	EntityID string
+	Name     string
+	ParentID string
+}
+
+type Entities map[string]*Entity
+type Edges []*Edge
+
+type WalkFunc func(fullPath string, entity *Entity) error
+
+// Graph database for storing entities and their relationships
+type Database struct {
+	dbPath string
+}
+
+// Create a new graph database initialized with a root entity
+func NewDatabase(dbPath string) (*Database, error) {
+	db := &Database{dbPath}
+	if _, err := os.Stat(dbPath); err == nil {
+		return db, nil
+	}
+	conn, err := db.openConn()
+	if err != nil {
+		return nil, err
+	}
+	defer conn.Close()
+
+	if _, err := conn.Exec(createEntityTable); err != nil {
+		return nil, err
+	}
+	if _, err := conn.Exec(createEdgeTable); err != nil {
+		return nil, err
+	}
+	if _, err := conn.Exec(createEdgeIndices); err != nil {
+		return nil, err
+	}
+
+	rollback := func() {
+		conn.Exec("ROLLBACK")
+	}
+
+	// Create root entities
+	if _, err := conn.Exec("BEGIN"); err != nil {
+		return nil, err
+	}
+	if _, err := conn.Exec("INSERT INTO entity (id) VALUES (?);", "0"); err != nil {
+		rollback()
+		return nil, err
+	}
+
+	if _, err := conn.Exec("INSERT INTO edge (entity_id, name) VALUES(?,?);", "0", "/"); err != nil {
+		rollback()
+		return nil, err
+	}
+
+	if _, err := conn.Exec("COMMIT"); err != nil {
+		return nil, err
+	}
+	return db, nil
+}
+
+// Set the entity id for a given path
+func (db *Database) Set(fullPath, id string) (*Entity, error) {
+	conn, err := db.openConn()
+	if err != nil {
+		return nil, err
+	}
+	defer conn.Close()
+	// FIXME: is rollback implicit when closing the connection?
+	rollback := func() {
+		conn.Exec("ROLLBACK")
+	}
+	// FIXME: use exclusive transactions to avoid race conditions
+	if _, err := conn.Exec("BEGIN"); err != nil {
+		return nil, err
+	}
+	var entityId string
+	if err := conn.QueryRow("SELECT id FROM entity WHERE id = ?;", id).Scan(&entityId); err != nil {
+		if err == sql.ErrNoRows {
+			if _, err := conn.Exec("INSERT INTO entity (id) VALUES(?);", id); err != nil {
+				rollback()
+				return nil, err
+			}
+		} else {
+			rollback()
+			return nil, err
+		}
+	}
+	e := &Entity{id}
+
+	parentPath, name := splitPath(fullPath)
+	if err := db.setEdge(conn, parentPath, name, e); err != nil {
+		rollback()
+		return nil, err
+	}
+
+	if _, err := conn.Exec("COMMIT"); err != nil {
+		return nil, err
+	}
+	return e, nil
+}
+
+func (db *Database) setEdge(conn *sql.DB, parentPath, name string, e *Entity) error {
+	parent, err := db.get(conn, parentPath)
+	if err != nil {
+		return err
+	}
+	if parent.id == e.id {
+		return fmt.Errorf("Cannot set self as child")
+	}
+
+	if _, err := conn.Exec("INSERT INTO edge (parent_id, name, entity_id) VALUES (?,?,?);", parent.id, name, e.id); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Return the root "/" entity for the database
+func (db *Database) RootEntity() *Entity {
+	return &Entity{
+		id: "0",
+	}
+}
+
+// Return the entity for a given path
+func (db *Database) Get(name string) *Entity {
+	conn, err := db.openConn()
+	if err != nil {
+		return nil
+	}
+	e, err := db.get(conn, name)
+	if err != nil {
+		return nil
+	}
+	return e
+}
+
+func (db *Database) get(conn *sql.DB, name string) (*Entity, error) {
+	e := db.RootEntity()
+	// We always know the root name so return it if
+	// it is requested
+	if name == "/" {
+		return e, nil
+	}
+
+	parts := split(name)
+	for i := 1; i < len(parts); i++ {
+		p := parts[i]
+
+		next := db.child(conn, e, p)
+		if next == nil {
+			return nil, fmt.Errorf("Cannot find child")
+		}
+		e = next
+	}
+	return e, nil
+
+}
+
+// List all entities by from the name
+// The key will be the full path of the entity
+func (db *Database) List(name string, depth int) Entities {
+	out := Entities{}
+	conn, err := db.openConn()
+	if err != nil {
+		return out
+	}
+	defer conn.Close()
+
+	for c := range db.children(conn, name, depth) {
+		out[c.FullPath] = c.Entity
+	}
+	return out
+}
+
+func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
+	conn, err := db.openConn()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	for c := range db.children(conn, name, depth) {
+		if err := walkFunc(c.FullPath, c.Entity); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Return the refrence count for a specified id
+func (db *Database) Refs(id string) int {
+	conn, err := db.openConn()
+	if err != nil {
+		return -1
+	}
+	defer conn.Close()
+
+	var count int
+	if err := conn.QueryRow("SELECT COUNT(*) FROM edge WHERE entity_id = ?;", id).Scan(&count); err != nil {
+		return 0
+	}
+	return count
+}
+
+// Return all the id's path references
+func (db *Database) RefPaths(id string) Edges {
+	refs := Edges{}
+	conn, err := db.openConn()
+	if err != nil {
+		return refs
+	}
+	defer conn.Close()
+
+	rows, err := conn.Query("SELECT name, parent_id FROM edge WHERE entity_id = ?;", id)
+	if err != nil {
+		return refs
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		var name string
+		var parentId string
+		if err := rows.Scan(&name, &parentId); err != nil {
+			return refs
+		}
+		refs = append(refs, &Edge{
+			EntityID: id,
+			Name:     name,
+			ParentID: parentId,
+		})
+	}
+	return refs
+}
+
+// Delete the reference to an entity at a given path
+func (db *Database) Delete(name string) error {
+	if name == "/" {
+		return fmt.Errorf("Cannot delete root entity")
+	}
+	conn, err := db.openConn()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	parentPath, n := splitPath(name)
+	parent, err := db.get(conn, parentPath)
+	if err != nil {
+		return err
+	}
+
+	if _, err := conn.Exec("DELETE FROM edge WHERE parent_id = ? AND name LIKE ?;", parent.id, n+"%"); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Remove the entity with the specified id
+// Walk the graph to make sure all references to the entity
+// are removed and return the number of references removed
+func (db *Database) Purge(id string) (int, error) {
+	conn, err := db.openConn()
+	if err != nil {
+		return -1, err
+	}
+	defer conn.Close()
+
+	rollback := func() {
+		conn.Exec("ROLLBACK")
+	}
+
+	if _, err := conn.Exec("BEGIN"); err != nil {
+		return -1, err
+	}
+
+	// Delete all edges
+	rows, err := conn.Exec("DELETE FROM edge WHERE entity_id = ?;", id)
+	if err != nil {
+		rollback()
+		return -1, err
+	}
+
+	changes, err := rows.RowsAffected()
+	if err != nil {
+		return -1, err
+	}
+
+	// Delete entity
+	if _, err := conn.Exec("DELETE FROM entity where id = ?;", id); err != nil {
+		rollback()
+		return -1, err
+	}
+
+	if _, err := conn.Exec("COMMIT"); err != nil {
+		return -1, err
+	}
+	return int(changes), nil
+}
+
+// Rename an edge for a given path
+func (db *Database) Rename(currentName, newName string) error {
+	parentPath, name := splitPath(currentName)
+	newParentPath, newEdgeName := splitPath(newName)
+
+	if parentPath != newParentPath {
+		return fmt.Errorf("Cannot rename when root paths do not match %s != %s", parentPath, newParentPath)
+	}
+
+	conn, err := db.openConn()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	parent, err := db.get(conn, parentPath)
+	if err != nil {
+		return err
+	}
+
+	rows, err := conn.Exec("UPDATE edge SET name = ? WHERE parent_id = ? AND name LIKE ?;", newEdgeName, parent.id, name+"%")
+	if err != nil {
+		return err
+	}
+	i, err := rows.RowsAffected()
+	if err != nil {
+		return err
+	}
+	if i == 0 {
+		return fmt.Errorf("Cannot locate edge for %s %s", parent.id, name)
+	}
+	return nil
+}
+
+type WalkMeta struct {
+	Parent   *Entity
+	Entity   *Entity
+	FullPath string
+	Edge     *Edge
+}
+
+func (db *Database) children(conn *sql.DB, name string, depth int) <-chan WalkMeta {
+	out := make(chan WalkMeta)
+	e, err := db.get(conn, name)
+	if err != nil {
+		close(out)
+		return out
+	}
+
+	go func() {
+		rows, err := conn.Query("SELECT entity_id, name FROM edge where parent_id = ?;", e.id)
+		if err != nil {
+			close(out)
+		}
+		defer rows.Close()
+
+		for rows.Next() {
+			var entityId, entityName string
+			if err := rows.Scan(&entityId, &entityName); err != nil {
+				// Log error
+				continue
+			}
+			child := &Entity{entityId}
+			edge := &Edge{
+				ParentID: e.id,
+				Name:     entityName,
+				EntityID: child.id,
+			}
+
+			meta := WalkMeta{
+				Parent:   e,
+				Entity:   child,
+				FullPath: path.Join(name, edge.Name),
+				Edge:     edge,
+			}
+
+			out <- meta
+			if depth == 0 {
+				continue
+			}
+			nDepth := depth
+			if depth != -1 {
+				nDepth -= 1
+			}
+			sc := db.children(conn, meta.FullPath, nDepth)
+			for c := range sc {
+				out <- c
+			}
+		}
+		close(out)
+	}()
+	return out
+}
+
+// Return the entity based on the parent path and name
+func (db *Database) child(conn *sql.DB, parent *Entity, name string) *Entity {
+	var id string
+	if err := conn.QueryRow("SELECT entity_id FROM edge WHERE parent_id = ? AND name LIKE ?;", parent.id, name+"%").Scan(&id); err != nil {
+		return nil
+	}
+	return &Entity{id}
+}
+
+func (db *Database) openConn() (*sql.DB, error) {
+	return sql.Open("sqlite3", db.dbPath)
+}
+
+// Return the id used to reference this entity
+func (e *Entity) ID() string {
+	return e.id
+}
+
+// Return the paths sorted by depth
+func (e Entities) Paths() []string {
+	out := make([]string, len(e))
+	var i int
+	for k := range e {
+		out[i] = k
+		i++
+	}
+	sortByDepth(out)
+
+	return out
+}
diff --git a/gograph/gograph_test.go b/gograph/gograph_test.go
new file mode 100644
index 0000000..5c12128
--- /dev/null
+++ b/gograph/gograph_test.go
@@ -0,0 +1,464 @@
+package gograph
+
+import (
+	"os"
+	"path"
+	"strconv"
+	"testing"
+)
+
+func newTestDb(t *testing.T) *Database {
+	db, err := NewDatabase(path.Join(os.TempDir(), "sqlite.db"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	return db
+}
+
+func destroyTestDb(db *Database) {
+	os.Remove(db.dbPath)
+}
+
+func TestNewDatabase(t *testing.T) {
+	db := newTestDb(t)
+	if db == nil {
+		t.Fatal("Database should not be nil")
+	}
+	defer destroyTestDb(db)
+}
+
+func TestCreateRootEnity(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+	root := db.RootEntity()
+	if root == nil {
+		t.Fatal("Root entity should not be nil")
+	}
+}
+
+func TestGetRootEntity(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	e := db.Get("/")
+	if e == nil {
+		t.Fatal("Entity should not be nil")
+	}
+	if e.ID() != "0" {
+		t.Fatalf("Enity id should be 0, got %s", e.ID())
+	}
+}
+
+func TestSetEntityWithDifferentName(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	db.Set("/test", "1")
+	if _, err := db.Set("/other", "1"); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestSetDuplicateEntity(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	if _, err := db.Set("/foo", "42"); err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/foo", "43"); err == nil {
+		t.Fatalf("Creating an entry with a duplciate path did not cause an error")
+	}
+}
+
+func TestCreateChild(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	child, err := db.Set("/db", "1")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if child == nil {
+		t.Fatal("Child should not be nil")
+	}
+	if child.ID() != "1" {
+		t.Fail()
+	}
+}
+
+func TestListAllRootChildren(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	for i := 1; i < 6; i++ {
+		a := strconv.Itoa(i)
+		if _, err := db.Set("/"+a, a); err != nil {
+			t.Fatal(err)
+		}
+	}
+	entries := db.List("/", -1)
+	if len(entries) != 5 {
+		t.Fatalf("Expect 5 entries for / got %d", len(entries))
+	}
+}
+
+func TestListAllSubChildren(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	_, err := db.Set("/webapp", "1")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child2, err := db.Set("/db", "2")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child4, err := db.Set("/logs", "4")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child3, err := db.Set("/sentry", "3")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	entries := db.List("/webapp", 1)
+	if len(entries) != 3 {
+		t.Fatalf("Expect 3 entries for / got %d", len(entries))
+	}
+
+	entries = db.List("/webapp", 0)
+	if len(entries) != 2 {
+		t.Fatalf("Expect 2 entries for / got %d", len(entries))
+	}
+}
+
+func TestAddSelfAsChild(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	child, err := db.Set("/test", "1")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/test/other", child.ID()); err == nil {
+		t.Fatal("Error should not be nil")
+	}
+}
+
+func TestAddChildToNonExistantRoot(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	if _, err := db.Set("/myapp", "1"); err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := db.Set("/myapp/proxy/db", "2"); err == nil {
+		t.Fatal("Error should not be nil")
+	}
+}
+
+func TestWalkAll(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+	_, err := db.Set("/webapp", "1")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child2, err := db.Set("/db", "2")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child4, err := db.Set("/db/logs", "4")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/logs", child4.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child3, err := db.Set("/sentry", "3")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child5, err := db.Set("/gograph", "5")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := db.Walk("/", func(p string, e *Entity) error {
+		t.Logf("Path: %s Entity: %s", p, e.ID())
+		return nil
+	}, -1); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestGetEntityByPath(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+	_, err := db.Set("/webapp", "1")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child2, err := db.Set("/db", "2")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child4, err := db.Set("/logs", "4")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child3, err := db.Set("/sentry", "3")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child5, err := db.Set("/gograph", "5")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	entity := db.Get("/webapp/db/logs")
+	if entity == nil {
+		t.Fatal("Entity should not be nil")
+	}
+	if entity.ID() != "4" {
+		t.Fatalf("Expected to get entity with id 4, got %s", entity.ID())
+	}
+}
+
+func TestEnitiesPaths(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+	_, err := db.Set("/webapp", "1")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child2, err := db.Set("/db", "2")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child4, err := db.Set("/logs", "4")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child3, err := db.Set("/sentry", "3")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child5, err := db.Set("/gograph", "5")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	out := db.List("/", -1)
+	for _, p := range out.Paths() {
+		t.Log(p)
+	}
+}
+
+func TestDeleteRootEntity(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	if err := db.Delete("/"); err == nil {
+		t.Fatal("Error should not be nil")
+	}
+}
+
+func TestDeleteEntity(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+	_, err := db.Set("/webapp", "1")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child2, err := db.Set("/db", "2")
+	if err != nil {
+		t.Fatal(err)
+	}
+	child4, err := db.Set("/logs", "4")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child3, err := db.Set("/sentry", "3")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	child5, err := db.Set("/gograph", "5")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := db.Delete("/webapp/sentry"); err != nil {
+		t.Fatal(err)
+	}
+	entity := db.Get("/webapp/sentry")
+	if entity != nil {
+		t.Fatal("Entity /webapp/sentry should be nil")
+	}
+}
+
+func TestCountRefs(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	db.Set("/webapp", "1")
+
+	if db.Refs("1") != 1 {
+		t.Fatal("Expect reference count to be 1")
+	}
+
+	db.Set("/db", "2")
+	db.Set("/webapp/db", "2")
+	if db.Refs("2") != 2 {
+		t.Fatal("Expect reference count to be 2")
+	}
+}
+
+func TestPurgeId(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	db.Set("/webapp", "1")
+
+	if db.Refs("1") != 1 {
+		t.Fatal("Expect reference count to be 1")
+	}
+
+	db.Set("/db", "2")
+	db.Set("/webapp/db", "2")
+
+	count, err := db.Purge("2")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if count != 2 {
+		t.Fatal("Expected 2 references to be removed")
+	}
+}
+
+func TestRename(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	db.Set("/webapp", "1")
+
+	if db.Refs("1") != 1 {
+		t.Fatal("Expect reference count to be 1")
+	}
+
+	db.Set("/db", "2")
+	db.Set("/webapp/db", "2")
+
+	if db.Get("/webapp/db") == nil {
+		t.Fatal("Cannot find entity at path /webapp/db")
+	}
+
+	if err := db.Rename("/webapp/db", "/webapp/newdb"); err != nil {
+		t.Fatal(err)
+	}
+	if db.Get("/webapp/db") != nil {
+		t.Fatal("Entity should not exist at /webapp/db")
+	}
+	if db.Get("/webapp/newdb") == nil {
+		t.Fatal("Cannot find entity at path /webapp/newdb")
+	}
+
+}
+
+func TestCreateMultipleNames(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	db.Set("/db", "1")
+	if _, err := db.Set("/myapp", "1"); err != nil {
+		t.Fatal(err)
+	}
+
+	db.Walk("/", func(p string, e *Entity) error {
+		t.Logf("%s\n", p)
+		return nil
+	}, -1)
+}
+
+func TestRefPaths(t *testing.T) {
+	db := newTestDb(t)
+	defer destroyTestDb(db)
+
+	db.Set("/webapp", "1")
+
+	db.Set("/db", "2")
+	db.Set("/webapp/db", "2")
+
+	refs := db.RefPaths("2")
+	if len(refs) != 2 {
+		t.Fatalf("Expected reference count to be 2, got %d", len(refs))
+	}
+
+}
diff --git a/gograph/sort.go b/gograph/sort.go
new file mode 100644
index 0000000..cc936cb
--- /dev/null
+++ b/gograph/sort.go
@@ -0,0 +1,27 @@
+package gograph
+
+import "sort"
+
+type pathSorter struct {
+	paths []string
+	by    func(i, j string) bool
+}
+
+func sortByDepth(paths []string) {
+	s := &pathSorter{paths, func(i, j string) bool {
+		return pathDepth(i) > pathDepth(j)
+	}}
+	sort.Sort(s)
+}
+
+func (s *pathSorter) Len() int {
+	return len(s.paths)
+}
+
+func (s *pathSorter) Swap(i, j int) {
+	s.paths[i], s.paths[j] = s.paths[j], s.paths[i]
+}
+
+func (s *pathSorter) Less(i, j int) bool {
+	return s.by(s.paths[i], s.paths[j])
+}
diff --git a/gograph/sort_test.go b/gograph/sort_test.go
new file mode 100644
index 0000000..4043103
--- /dev/null
+++ b/gograph/sort_test.go
@@ -0,0 +1,29 @@
+package gograph
+
+import (
+	"testing"
+)
+
+func TestSort(t *testing.T) {
+	paths := []string{
+		"/",
+		"/myreallylongname",
+		"/app/db",
+	}
+
+	sortByDepth(paths)
+
+	if len(paths) != 3 {
+		t.Fatalf("Expected 3 parts got %d", len(paths))
+	}
+
+	if paths[0] != "/app/db" {
+		t.Fatalf("Expected /app/db got %s", paths[0])
+	}
+	if paths[1] != "/myreallylongname" {
+		t.Fatalf("Expected /myreallylongname got %s", paths[1])
+	}
+	if paths[2] != "/" {
+		t.Fatalf("Expected / got %s", paths[2])
+	}
+}
diff --git a/gograph/utils.go b/gograph/utils.go
new file mode 100644
index 0000000..c20dd12
--- /dev/null
+++ b/gograph/utils.go
@@ -0,0 +1,32 @@
+package gograph
+
+import (
+	"path"
+	"strings"
+)
+
+// Split p on /
+func split(p string) []string {
+	return strings.Split(p, "/")
+}
+
+// Returns the depth or number of / in a given path
+func pathDepth(p string) int {
+	parts := split(p)
+	if len(parts) == 2 && parts[1] == "" {
+		return 1
+	}
+	return len(parts)
+}
+
+func splitPath(p string) (parent, name string) {
+	if p[0] != '/' {
+		p = "/" + p
+	}
+	parent, name = path.Split(p)
+	l := len(parent)
+	if parent[l-1] == '/' {
+		parent = parent[:l-1]
+	}
+	return
+}
diff --git a/graph_test.go b/graph_test.go
index 4710169..ecb5ffb 100644
--- a/graph_test.go
+++ b/graph_test.go
@@ -121,6 +121,9 @@
 }
 
 func TestMount(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+
 	graph := tempGraph(t)
 	defer os.RemoveAll(graph.Root)
 	archive, err := fakeTar()
@@ -144,12 +147,12 @@
 	if err := os.MkdirAll(rw, 0700); err != nil {
 		t.Fatal(err)
 	}
-	if err := image.Mount(rootfs, rw); err != nil {
+	if err := image.Mount(runtime, rootfs, rw, "testing"); err != nil {
 		t.Fatal(err)
 	}
 	// FIXME: test for mount contents
 	defer func() {
-		if err := Unmount(rootfs); err != nil {
+		if err := image.Unmount(runtime, rootfs, "testing"); err != nil {
 			t.Error(err)
 		}
 	}()
diff --git a/hack/PACKAGERS.md b/hack/PACKAGERS.md
index ab8d74d..d237525 100644
--- a/hack/PACKAGERS.md
+++ b/hack/PACKAGERS.md
@@ -32,14 +32,14 @@
 
 ## System build dependencies
 
-To build docker, you will need the following system dependencies
+To build docker, you will need the following system dependencies:
 
 * An amd64 machine
 * A recent version of git and mercurial
-* Go version 1.1.2
+* Go version 1.2rc1
+* A copy of libdevmapper.a (statically compiled), and associated headers
 * A clean checkout of the source must be added to a valid Go [workspace](http://golang.org/doc/code.html#Workspaces)
-under the path *src/github.com/dotcloud/docker*. See 
-
+under the path *src/github.com/dotcloud/docker*.
 
 ## Go dependencies
 
@@ -55,15 +55,13 @@
 please get in touch so we can remediate! Who knows what discrepancies can be caused by even the
 slightest deviation. We promise to do our best to make everybody happy.
 
+## Disabling CGO for the net package
 
-## Disabling CGO
-
-Make sure to disable CGO on your system, and then recompile the standard library on the build
-machine:
+Make sure to disable CGO on your system for the net package using `-tags netgo`,
+and then recompile the standard library on the build machine:
 
 ```bash
-export CGO_ENABLED=0
-cd /tmp && echo 'package main' > t.go && go test -a -i -v
+go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
 ```
 
 ## Building Docker
@@ -71,7 +69,7 @@
 To build the docker binary, run the following command with the source checkout as the
 working directory:
 
-```
+```bash
 ./hack/make.sh binary
 ```
 
@@ -80,9 +78,9 @@
 
 You are encouraged to use ./hack/make.sh without modification. If you must absolutely write
 your own script (are you really, really sure you need to? make.sh is really not that complicated),
-then please take care the respect the following:
+then please take care to respect the following:
 
-* In *./hack/make.sh*: $LDFLAGS, $VERSION and $GITCOMMIT
+* In *./hack/make.sh*: $LDFLAGS, $BUILDFLAGS, $VERSION and $GITCOMMIT
 * In *./hack/make/binary*: the exact build command to run
 
 You may be tempted to tweak these settings. In particular, being a rigorous maintainer, you may want
@@ -106,7 +104,6 @@
 
 The test suite will also download a small test container, so you will need internet connectivity.
 
-
 ## Runtime dependencies
 
 To run properly, docker needs the following software to be installed at runtime:
diff --git a/hack/make.sh b/hack/make.sh
index 98b62ea..a5d66e3 100755
--- a/hack/make.sh
+++ b/hack/make.sh
@@ -44,7 +44,8 @@
 fi
 
 # Use these flags when compiling the tests and final binary
-LDFLAGS="-X main.GITCOMMIT $GITCOMMIT -X main.VERSION $VERSION -d -w"
+LDFLAGS='-X main.GITCOMMIT "'$GITCOMMIT'" -X main.VERSION "'$VERSION'" -w -linkmode external -extldflags "-lpthread -static -Wl,--unresolved-symbols=ignore-all"'
+BUILDFLAGS='-tags netgo'
 
 
 bundle() {
diff --git a/hack/make/binary b/hack/make/binary
index cff9f5c..3301c4a 100644
--- a/hack/make/binary
+++ b/hack/make/binary
@@ -2,6 +2,6 @@
 
 DEST=$1
 
-if go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" ./docker; then
+if go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" $BUILDFLAGS ./docker; then
 	echo "Created binary: $DEST/docker-$VERSION"
 fi
diff --git a/hack/make/test b/hack/make/test
index c097277..9941249 100644
--- a/hack/make/test
+++ b/hack/make/test
@@ -1,3 +1,5 @@
+#!/bin/sh
+
 DEST=$1
 
 set -e
@@ -14,7 +16,7 @@
 		for test_dir in $(find_test_dirs); do (
 			set -x
 			cd $test_dir
-			go test -v -ldflags "$LDFLAGS" $TESTFLAGS
+			go test -v -ldflags "$LDFLAGS" $BUILDFLAGS $TESTFLAGS
 		)  done
 	} 2>&1 | tee $DEST/test.log
 }
@@ -29,4 +31,26 @@
                sort -u
 }
 
+
+check_test_garbage() {
+	nGarbage=`find /tmp/ -name 'docker-*' | wc -l`
+	if [ $nGarbage -gt 0 ]; then
+		(
+		cat <<EOF
+
+Error: there are $nGarbage docker-related files in /tmp. Please clean them up and try again.
+--------------------------------------------------------------------------------------------
+EOF
+		find /tmp -name 'docker-*'
+		cat <<EOF
+--------------------------------------------------------------------------------------------
+If the error persists, your tests might leak temporary files. This is considered a test fail.
+EOF
+		exit 1
+		) 2>&1
+	fi
+}
+
+check_test_garbage
 bundle_test
+check_test_garbage
diff --git a/image.go b/image.go
index 9f34160..11816d7 100644
--- a/image.go
+++ b/image.go
@@ -5,16 +5,16 @@
 	"encoding/hex"
 	"encoding/json"
 	"fmt"
+	"github.com/dotcloud/docker/devmapper"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io/ioutil"
-	"log"
 	"os"
-	"os/exec"
 	"path"
 	"path/filepath"
 	"strconv"
 	"strings"
+	"syscall"
 	"time"
 )
 
@@ -61,6 +61,7 @@
 	}
 
 	// Check that the filesystem layer exists
+	// FIXME: once an image is added into device mapper, the layer is no longer needed
 	if stat, err := os.Stat(layerPath(root)); err != nil {
 		if os.IsNotExist(err) {
 			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
@@ -136,29 +137,8 @@
 	return path.Join(root, "json")
 }
 
-func MountAUFS(ro []string, rw string, target string) error {
-	// FIXME: Now mount the layers
-	rwBranch := fmt.Sprintf("%v=rw", rw)
-	roBranches := ""
-	for _, layer := range ro {
-		roBranches += fmt.Sprintf("%v=ro+wh:", layer)
-	}
-	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
-
-	branches += ",xino=/dev/shm/aufs.xino"
-
-	//if error, try to load aufs kernel module
-	if err := mount("none", target, "aufs", 0, branches); err != nil {
-		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
-		if err := exec.Command("modprobe", "aufs").Run(); err != nil {
-			return fmt.Errorf("Unable to load the AUFS module")
-		}
-		log.Printf("...module loaded.")
-		if err := mount("none", target, "aufs", 0, branches); err != nil {
-			return fmt.Errorf("Unable to mount using aufs")
-		}
-	}
-	return nil
+func mountPath(root string) string {
+	return path.Join(root, "mount")
 }
 
 // TarLayer returns a tar archive of the image's filesystem layer.
@@ -170,35 +150,383 @@
 	return Tar(layerPath, compression)
 }
 
-func (image *Image) Mount(root, rw string) error {
-	if mounted, err := Mounted(root); err != nil {
-		return err
-	} else if mounted {
-		return fmt.Errorf("%s is already mounted", root)
-	}
-	layers, err := image.layers()
+type TimeUpdate struct {
+	path string
+	time []syscall.Timeval
+	mode uint32
+}
+
+func (image *Image) applyLayer(layer, target string) error {
+	var updateTimes []TimeUpdate
+	oldmask := syscall.Umask(0)
+	defer syscall.Umask(oldmask)
+	err := filepath.Walk(layer, func(srcPath string, f os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+
+		// Skip root
+		if srcPath == layer {
+			return nil
+		}
+
+		var srcStat syscall.Stat_t
+		err = syscall.Lstat(srcPath, &srcStat)
+		if err != nil {
+			return err
+		}
+
+		relPath, err := filepath.Rel(layer, srcPath)
+		if err != nil {
+			return err
+		}
+
+		targetPath := filepath.Join(target, relPath)
+
+		// Skip AUFS metadata
+		if matched, err := filepath.Match(".wh..wh.*", relPath); err != nil || matched {
+			if err != nil || !f.IsDir() {
+				return err
+			}
+			return filepath.SkipDir
+		}
+
+		// Find out what kind of modification happened
+		file := filepath.Base(srcPath)
+
+		// If there is a whiteout, then the file was removed
+		if strings.HasPrefix(file, ".wh.") {
+			originalFile := file[len(".wh."):]
+			deletePath := filepath.Join(filepath.Dir(targetPath), originalFile)
+
+			err = os.RemoveAll(deletePath)
+			if err != nil {
+				return err
+			}
+		} else {
+			var targetStat = &syscall.Stat_t{}
+			err := syscall.Lstat(targetPath, targetStat)
+			if err != nil {
+				if !os.IsNotExist(err) {
+					return err
+				}
+				targetStat = nil
+			}
+
+			if targetStat != nil && !(targetStat.Mode&syscall.S_IFDIR == syscall.S_IFDIR && srcStat.Mode&syscall.S_IFDIR == syscall.S_IFDIR) {
+				// Unless both src and dest are directories we remove the target and recreate it
+				// This is a bit wasteful in the case of only a mode change, but that is unlikely
+				// to matter much
+				err = os.RemoveAll(targetPath)
+				if err != nil {
+					return err
+				}
+				targetStat = nil
+			}
+
+			if f.IsDir() {
+				// Source is a directory
+				if targetStat == nil {
+					err = syscall.Mkdir(targetPath, srcStat.Mode&07777)
+					if err != nil {
+						return err
+					}
+				}
+			} else if srcStat.Mode&syscall.S_IFLNK == syscall.S_IFLNK {
+				// Source is symlink
+				link, err := os.Readlink(srcPath)
+				if err != nil {
+					return err
+				}
+
+				err = os.Symlink(link, targetPath)
+				if err != nil {
+					return err
+				}
+			} else if srcStat.Mode&syscall.S_IFBLK == syscall.S_IFBLK ||
+				srcStat.Mode&syscall.S_IFCHR == syscall.S_IFCHR ||
+				srcStat.Mode&syscall.S_IFIFO == syscall.S_IFIFO ||
+				srcStat.Mode&syscall.S_IFSOCK == syscall.S_IFSOCK {
+				// Source is special file
+				err = syscall.Mknod(targetPath, srcStat.Mode, int(srcStat.Rdev))
+				if err != nil {
+					return err
+				}
+			} else if srcStat.Mode&syscall.S_IFREG == syscall.S_IFREG {
+				// Source is regular file
+				fd, err := syscall.Open(targetPath, syscall.O_CREAT|syscall.O_WRONLY, srcStat.Mode&07777)
+				if err != nil {
+					return err
+				}
+				dstFile := os.NewFile(uintptr(fd), targetPath)
+				srcFile, err := os.Open(srcPath)
+				if err != nil {
+					_ = dstFile.Close()
+					return err
+				}
+				err = CopyFile(dstFile, srcFile)
+				_ = dstFile.Close()
+				_ = srcFile.Close()
+				if err != nil {
+					return err
+				}
+			} else {
+				return fmt.Errorf("Unknown type for file %s", srcPath)
+			}
+
+			err = syscall.Lchown(targetPath, int(srcStat.Uid), int(srcStat.Gid))
+			if err != nil {
+				return err
+			}
+
+			if srcStat.Mode&syscall.S_IFLNK != syscall.S_IFLNK {
+				err = syscall.Chmod(targetPath, srcStat.Mode&07777)
+				if err != nil {
+					return err
+				}
+			}
+
+			ts := []syscall.Timeval{
+				syscall.NsecToTimeval(srcStat.Atim.Nano()),
+				syscall.NsecToTimeval(srcStat.Mtim.Nano()),
+			}
+
+			u := TimeUpdate{
+				path: targetPath,
+				time: ts,
+				mode: srcStat.Mode,
+			}
+
+			// Delay time updates until all other changes done, or it is
+			// overwritten for directories (by child changes)
+			updateTimes = append(updateTimes, u)
+		}
+		return nil
+	})
 	if err != nil {
 		return err
 	}
+
+	// We do this in reverse order so that children are updated before parents
+	for i := len(updateTimes) - 1; i >= 0; i-- {
+		update := updateTimes[i]
+
+		O_PATH := 010000000 // Not in syscall yet
+		var err error
+		if update.mode&syscall.S_IFLNK == syscall.S_IFLNK {
+			// Update time on the symlink via O_PATH + futimes(), if supported by the kernel
+
+			fd, err := syscall.Open(update.path, syscall.O_RDWR|O_PATH|syscall.O_NOFOLLOW, 0600)
+			if err == syscall.EISDIR || err == syscall.ELOOP {
+				// O_PATH not supported by kernel, nothing to do, ignore
+			} else if err != nil {
+				return err
+			} else {
+				syscall.Futimes(fd, update.time)
+				syscall.Close(fd)
+			}
+		} else {
+			err = syscall.Utimes(update.path, update.time)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+func (image *Image) ensureImageDevice(devices *devmapper.DeviceSet) error {
+	if devices.HasInitializedDevice(image.ID) {
+		return nil
+	}
+
+	if image.Parent != "" && !devices.HasInitializedDevice(image.Parent) {
+		parentImg, err := image.GetParent()
+		if err != nil {
+			return fmt.Errorf("Error while getting parent image: %v", err)
+		}
+		err = parentImg.ensureImageDevice(devices)
+		if err != nil {
+			return err
+		}
+	}
+
+	root, err := image.root()
+	if err != nil {
+		return err
+	}
+
+	mountDir := mountPath(root)
+	if err := os.Mkdir(mountDir, 0600); err != nil && !os.IsExist(err) {
+		return err
+	}
+
+	mounted, err := Mounted(mountDir)
+	if err == nil && mounted {
+		utils.Debugf("Image %s is unexpectedly mounted, unmounting...", image.ID)
+		err = syscall.Unmount(mountDir, 0)
+		if err != nil {
+			return err
+		}
+	}
+
+	if devices.HasDevice(image.ID) {
+		utils.Debugf("Found non-initialized demove-mapper device for image %s, removing", image.ID)
+		err = devices.RemoveDevice(image.ID)
+		if err != nil {
+			return err
+		}
+	}
+
+	utils.Debugf("Creating device-mapper device for image id %s", image.ID)
+	if err := devices.AddDevice(image.ID, image.Parent); err != nil {
+		utils.Debugf("Error add device: %s", err)
+		return err
+	}
+
+	if err := devices.MountDevice(image.ID, mountDir, false); err != nil {
+		utils.Debugf("Error mounting device: %s", err)
+		devices.RemoveDevice(image.ID)
+		return err
+	}
+
+	if err = image.applyLayer(layerPath(root), mountDir); err != nil {
+		utils.Debugf("Error applying layer: %s", err)
+		devices.UnmountDevice(image.ID, mountDir, true)
+		devices.RemoveDevice(image.ID)
+		return err
+	}
+
+	// The docker init layer is conceptually above all other layers, so we apply
+	// it for every image. This is safe because the layer directory is the
+	// definition of the image, and the device-mapper device is just a cache
+	// of it instantiated. Diffs/commit compare the container device with the
+	// image device, which will then *not* pick up the init layer changes as
+	// part of the container changes
+	dockerinitLayer, err := image.getDockerInitLayer()
+	if err != nil {
+		devices.UnmountDevice(image.ID, mountDir, true)
+		devices.RemoveDevice(image.ID)
+		return err
+	}
+
+	if err := image.applyLayer(dockerinitLayer, mountDir); err != nil {
+		devices.UnmountDevice(image.ID, mountDir, true)
+		devices.RemoveDevice(image.ID)
+		return err
+	}
+
+	if err := devices.UnmountDevice(image.ID, mountDir, true); err != nil {
+		devices.RemoveDevice(image.ID)
+		return err
+	}
+
+	devices.SetInitialized(image.ID)
+
+	return nil
+}
+
+func (image *Image) Mounted(runtime *Runtime, root, rw string) (bool, error) {
+	return Mounted(root)
+}
+
+func (image *Image) Mount(runtime *Runtime, root, rw string, id string) error {
+	if mounted, _ := image.Mounted(runtime, root, rw); mounted {
+		return fmt.Errorf("%s is already mounted", root)
+	}
+
 	// Create the target directories if they don't exist
 	if err := os.Mkdir(root, 0755); err != nil && !os.IsExist(err) {
 		return err
 	}
-	if err := os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) {
+
+	devices, err := runtime.GetDeviceSet()
+	if err != nil {
 		return err
 	}
-	if err := MountAUFS(layers, rw, root); err != nil {
+
+	if err := image.ensureImageDevice(devices); err != nil {
 		return err
 	}
+
+	if !devices.HasDevice(id) {
+		utils.Debugf("Creating device %s for container based on image %s", id, image.ID)
+		err = devices.AddDevice(id, image.ID)
+		if err != nil {
+			return err
+		}
+	}
+
+	utils.Debugf("Mounting container %s at %s for container", id, root)
+	if err := devices.MountDevice(id, root, false); err != nil {
+		return err
+	}
+
 	return nil
 }
 
-func (image *Image) Changes(rw string) ([]Change, error) {
-	layers, err := image.layers()
+func (image *Image) Unmount(runtime *Runtime, root string, id string) error {
+	// Try to deactivate the device as generally there is no use for it anymore
+	devices, err := runtime.GetDeviceSet()
+	if err != nil {
+		return err
+	}
+
+	if err = devices.UnmountDevice(id, root, true); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (image *Image) Changes(runtime *Runtime, root, rw, id string) ([]Change, error) {
+	devices, err := runtime.GetDeviceSet()
 	if err != nil {
 		return nil, err
 	}
-	return Changes(layers, rw)
+
+	if err := os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) {
+		return nil, err
+	}
+
+	wasActivated := devices.HasActivatedDevice(image.ID)
+
+	// We re-use rw for the temporary mount of the base image as its
+	// not used by device-mapper otherwise
+	err = devices.MountDevice(image.ID, rw, true)
+	if err != nil {
+		return nil, err
+	}
+
+	changes, err := ChangesDirs(root, rw)
+	devices.UnmountDevice(image.ID, rw, !wasActivated)
+	if err != nil {
+		return nil, err
+	}
+	return changes, nil
+}
+
+func (image *Image) ExportChanges(runtime *Runtime, root, rw, id string) (Archive, error) {
+	changes, err := image.Changes(runtime, root, rw, id)
+	if err != nil {
+		return nil, err
+	}
+
+	files := make([]string, 0)
+	deletions := make([]string, 0)
+	for _, change := range changes {
+		if change.Kind == ChangeModify || change.Kind == ChangeAdd {
+			files = append(files, change.Path)
+		}
+		if change.Kind == ChangeDelete {
+			base := filepath.Base(change.Path)
+			dir := filepath.Dir(change.Path)
+			deletions = append(deletions, filepath.Join(dir, ".wh."+base))
+		}
+	}
+
+	return TarFilter(root, Uncompressed, files, false, deletions)
 }
 
 func (image *Image) ShortID() string {
diff --git a/iptables/MAINTAINERS b/iptables/MAINTAINERS
new file mode 100644
index 0000000..1e998f8
--- /dev/null
+++ b/iptables/MAINTAINERS
@@ -0,0 +1 @@
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
diff --git a/iptables/iptables.go b/iptables/iptables.go
new file mode 100644
index 0000000..5974d4d
--- /dev/null
+++ b/iptables/iptables.go
@@ -0,0 +1,105 @@
+package iptables
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"os/exec"
+	"strconv"
+	"strings"
+)
+
+type Action string
+
+const (
+	Add    Action = "-A"
+	Delete Action = "-D"
+)
+
+var (
+	ErrIptablesNotFound = errors.New("Iptables not found")
+	nat                 = []string{"-t", "nat"}
+)
+
+type Chain struct {
+	Name   string
+	Bridge string
+}
+
+func NewChain(name, bridge string) (*Chain, error) {
+	if err := Raw("-t", "nat", "-N", name); err != nil {
+		return nil, err
+	}
+	chain := &Chain{
+		Name:   name,
+		Bridge: bridge,
+	}
+
+	if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
+		return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
+	}
+	if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
+		return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
+	}
+	return chain, nil
+}
+
+func RemoveExistingChain(name string) error {
+	chain := &Chain{
+		Name: name,
+	}
+	return chain.Remove()
+}
+
+func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error {
+	return Raw("-t", "nat", fmt.Sprint(action), c.Name,
+		"-p", proto,
+		"-d", ip.String(),
+		"--dport", strconv.Itoa(port),
+		"!", "-i", c.Bridge,
+		"-j", "DNAT",
+		"--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port)))
+}
+
+func (c *Chain) Prerouting(action Action, args ...string) error {
+	a := append(nat, fmt.Sprint(action), "PREROUTING")
+	if len(args) > 0 {
+		a = append(a, args...)
+	}
+	return Raw(append(a, "-j", c.Name)...)
+}
+
+func (c *Chain) Output(action Action, args ...string) error {
+	a := append(nat, fmt.Sprint(action), "OUTPUT")
+	if len(args) > 0 {
+		a = append(a, args...)
+	}
+	return Raw(append(a, "-j", c.Name)...)
+}
+
+func (c *Chain) Remove() error {
+	// Ignore errors - This could mean the chains were never set up
+	c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
+	c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
+	c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
+
+	c.Prerouting(Delete)
+	c.Output(Delete)
+
+	Raw("-t", "nat", "-F", c.Name)
+	Raw("-t", "nat", "-X", c.Name)
+
+	return nil
+}
+
+func Raw(args ...string) error {
+	path, err := exec.LookPath("iptables")
+	if err != nil {
+		return ErrIptablesNotFound
+	}
+	if err := exec.Command(path, args...).Run(); err != nil {
+		return fmt.Errorf("iptables failed: iptables %v", strings.Join(args, " "))
+	}
+	return nil
+
+}
diff --git a/iptables/iptables_test.go b/iptables/iptables_test.go
new file mode 100644
index 0000000..aad8acd
--- /dev/null
+++ b/iptables/iptables_test.go
@@ -0,0 +1,18 @@
+package iptables
+
+import (
+	"os"
+	"testing"
+)
+
+func TestIptables(t *testing.T) {
+	if err := Raw("-L"); err != nil {
+		t.Fatal(err)
+	}
+	path := os.Getenv("PATH")
+	os.Setenv("PATH", "")
+	defer os.Setenv("PATH", path)
+	if err := Raw("-L"); err == nil {
+		t.Fatal("Not finding iptables in the PATH should cause an error")
+	}
+}
diff --git a/links.go b/links.go
new file mode 100644
index 0000000..f1087ec
--- /dev/null
+++ b/links.go
@@ -0,0 +1,141 @@
+package docker
+
+import (
+	"fmt"
+	"github.com/dotcloud/docker/iptables"
+	"path"
+	"strings"
+)
+
+type Link struct {
+	ParentIP         string
+	ChildIP          string
+	Name             string
+	BridgeInterface  string
+	ChildEnvironment []string
+	Ports            []Port
+	IsEnabled        bool
+}
+
+func NewLink(parent, child *Container, name, bridgeInterface string) (*Link, error) {
+	if parent.ID == child.ID {
+		return nil, fmt.Errorf("Cannot link to self: %s == %s", parent.ID, child.ID)
+	}
+	if !child.State.Running {
+		return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.ID, name)
+	}
+
+	ports := make([]Port, len(child.Config.ExposedPorts))
+	var i int
+	for p := range child.Config.ExposedPorts {
+		ports[i] = p
+		i++
+	}
+
+	l := &Link{
+		BridgeInterface:  bridgeInterface,
+		Name:             name,
+		ChildIP:          child.NetworkSettings.IPAddress,
+		ParentIP:         parent.NetworkSettings.IPAddress,
+		ChildEnvironment: child.Config.Env,
+		Ports:            ports,
+	}
+	return l, nil
+
+}
+
+func (l *Link) Alias() string {
+	_, alias := path.Split(l.Name)
+	return alias
+}
+
+func (l *Link) ToEnv() []string {
+	env := []string{}
+	alias := strings.ToUpper(l.Alias())
+
+	if p := l.getDefaultPort(); p != nil {
+		env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
+	}
+
+	// Load exposed ports into the environment
+	for _, p := range l.Ports {
+		env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
+	}
+
+	// Load the linked container's name into the environment
+	env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name))
+
+	if l.ChildEnvironment != nil {
+		for _, v := range l.ChildEnvironment {
+			parts := strings.Split(v, "=")
+			if len(parts) != 2 {
+				continue
+			}
+			// Ignore a few variables that are added during docker build
+			if parts[0] == "HOME" || parts[0] == "PATH" {
+				continue
+			}
+			env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1]))
+		}
+	}
+	return env
+}
+
+// Default port rules
+func (l *Link) getDefaultPort() *Port {
+	var p Port
+	i := len(l.Ports)
+
+	if i == 0 {
+		return nil
+	} else if i > 1 {
+		sortPorts(l.Ports, func(ip, jp Port) bool {
+			// If the two ports have the same number, tcp takes priority
+			// Sort in desc order
+			return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
+		})
+	}
+	p = l.Ports[0]
+	return &p
+}
+
+func (l *Link) Enable() error {
+	if err := l.toggle("-I", false); err != nil {
+		return err
+	}
+	l.IsEnabled = true
+	return nil
+}
+
+func (l *Link) Disable() {
+	// We do not care about errors here because the link may not
+	// exist in iptables
+	l.toggle("-D", true)
+
+	l.IsEnabled = false
+}
+
+func (l *Link) toggle(action string, ignoreErrors bool) error {
+	for _, p := range l.Ports {
+		if err := iptables.Raw(action, "FORWARD",
+			"-i", l.BridgeInterface, "-o", l.BridgeInterface,
+			"-p", p.Proto(),
+			"-s", l.ParentIP,
+			"--dport", p.Port(),
+			"-d", l.ChildIP,
+			"-j", "ACCEPT"); !ignoreErrors && err != nil {
+			return err
+		}
+
+		if err := iptables.Raw(action, "FORWARD",
+			"-i", l.BridgeInterface, "-o", l.BridgeInterface,
+			"-p", p.Proto(),
+			"-s", l.ChildIP,
+			"--sport", p.Port(),
+			"-d", l.ParentIP,
+			"-j", "ACCEPT"); !ignoreErrors && err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/links_test.go b/links_test.go
new file mode 100644
index 0000000..64608e1
--- /dev/null
+++ b/links_test.go
@@ -0,0 +1,104 @@
+package docker
+
+import (
+	"strings"
+	"testing"
+)
+
+func newMockLinkContainer(id string, ip string) *Container {
+	return &Container{
+		Config: &Config{},
+		ID:     id,
+		NetworkSettings: &NetworkSettings{
+			IPAddress: ip,
+		},
+	}
+}
+
+func TestLinkNew(t *testing.T) {
+	toID := GenerateID()
+	fromID := GenerateID()
+
+	from := newMockLinkContainer(fromID, "172.0.17.2")
+	from.Config.Env = []string{}
+	from.State = State{Running: true}
+	ports := make(map[Port]struct{})
+
+	ports[Port("6379/tcp")] = struct{}{}
+
+	from.Config.ExposedPorts = ports
+
+	to := newMockLinkContainer(toID, "172.0.17.3")
+
+	link, err := NewLink(to, from, "/db/docker", "172.0.17.1")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if link == nil {
+		t.FailNow()
+	}
+	if link.Name != "/db/docker" {
+		t.Fail()
+	}
+	if link.Alias() != "docker" {
+		t.Fail()
+	}
+	if link.ParentIP != "172.0.17.3" {
+		t.Fail()
+	}
+	if link.ChildIP != "172.0.17.2" {
+		t.Fail()
+	}
+	if link.BridgeInterface != "172.0.17.1" {
+		t.Fail()
+	}
+	for _, p := range link.Ports {
+		if p != Port("6379/tcp") {
+			t.Fail()
+		}
+	}
+}
+
+func TestLinkEnv(t *testing.T) {
+	toID := GenerateID()
+	fromID := GenerateID()
+
+	from := newMockLinkContainer(fromID, "172.0.17.2")
+	from.Config.Env = []string{"PASSWORD=gordon"}
+	from.State = State{Running: true}
+	ports := make(map[Port]struct{})
+
+	ports[Port("6379/tcp")] = struct{}{}
+
+	from.Config.ExposedPorts = ports
+
+	to := newMockLinkContainer(toID, "172.0.17.3")
+
+	link, err := NewLink(to, from, "/db/docker", "172.0.17.1")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rawEnv := link.ToEnv()
+	env := make(map[string]string, len(rawEnv))
+	for _, e := range rawEnv {
+		parts := strings.Split(e, "=")
+		if len(parts) != 2 {
+			t.FailNow()
+		}
+		env[parts[0]] = parts[1]
+	}
+	if env["DOCKER_PORT"] != "tcp://172.0.17.2:6379" {
+		t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT"])
+	}
+	if env["DOCKER_PORT_6379_TCP"] != "tcp://172.0.17.2:6379" {
+		t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT_6379_TCP"])
+	}
+	if env["DOCKER_NAME"] != "/db/docker" {
+		t.Fatalf("Expected /db/docker, got %s", env["DOCKER_NAME"])
+	}
+	if env["DOCKER_ENV_PASSWORD"] != "gordon" {
+		t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
+	}
+}
diff --git a/mount.go b/mount.go
index f4a4dfb..3e2a21d 100644
--- a/mount.go
+++ b/mount.go
@@ -1,40 +1,11 @@
 package docker
 
 import (
-	"fmt"
-	"github.com/dotcloud/docker/utils"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"syscall"
-	"time"
 )
 
-func Unmount(target string) error {
-	if err := exec.Command("auplink", target, "flush").Run(); err != nil {
-		utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
-	}
-	if err := syscall.Unmount(target, 0); err != nil {
-		return err
-	}
-	// Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint
-	// for some time. We'll just keep retrying until it succeeds.
-	for retries := 0; retries < 1000; retries++ {
-		err := os.Remove(target)
-		if err == nil {
-			// rm mntpoint succeeded
-			return nil
-		}
-		if os.IsNotExist(err) {
-			// mntpoint doesn't exist anymore. Success.
-			return nil
-		}
-		// fmt.Printf("(%v) Remove %v returned: %v\n", retries, target, err)
-		time.Sleep(10 * time.Millisecond)
-	}
-	return fmt.Errorf("Umount: Failed to umount %v", target)
-}
-
 func Mounted(mountpoint string) (bool, error) {
 	mntpoint, err := os.Stat(mountpoint)
 	if err != nil {
diff --git a/netlink/netlink.go b/netlink/netlink.go
new file mode 100644
index 0000000..239614b
--- /dev/null
+++ b/netlink/netlink.go
@@ -0,0 +1,548 @@
+package netlink
+
+import (
+	"encoding/binary"
+	"fmt"
+	"net"
+	"syscall"
+	"unsafe"
+)
+
+var nextSeqNr int
+
+func nativeEndian() binary.ByteOrder {
+	var x uint32 = 0x01020304
+	if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
+		return binary.BigEndian
+	}
+	return binary.LittleEndian
+}
+
+func getSeq() int {
+	nextSeqNr = nextSeqNr + 1
+	return nextSeqNr
+}
+
+func getIpFamily(ip net.IP) int {
+	if len(ip) <= net.IPv4len {
+		return syscall.AF_INET
+	}
+	if ip.To4() != nil {
+		return syscall.AF_INET
+	}
+	return syscall.AF_INET6
+}
+
+type NetlinkRequestData interface {
+	ToWireFormat() []byte
+}
+
+type IfInfomsg struct {
+	syscall.IfInfomsg
+}
+
+func newIfInfomsg(family int) *IfInfomsg {
+	msg := &IfInfomsg{}
+	msg.Family = uint8(family)
+	msg.Type = uint16(0)
+	msg.Index = int32(0)
+	msg.Flags = uint32(0)
+	msg.Change = uint32(0)
+
+	return msg
+}
+
+func (msg *IfInfomsg) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	len := syscall.SizeofIfInfomsg
+	b := make([]byte, len)
+	b[0] = msg.Family
+	b[1] = 0
+	native.PutUint16(b[2:4], msg.Type)
+	native.PutUint32(b[4:8], uint32(msg.Index))
+	native.PutUint32(b[8:12], msg.Flags)
+	native.PutUint32(b[12:16], msg.Change)
+	return b
+}
+
+type IfAddrmsg struct {
+	syscall.IfAddrmsg
+}
+
+func newIfAddrmsg(family int) *IfAddrmsg {
+	msg := &IfAddrmsg{}
+	msg.Family = uint8(family)
+	msg.Prefixlen = uint8(0)
+	msg.Flags = uint8(0)
+	msg.Scope = uint8(0)
+	msg.Index = uint32(0)
+
+	return msg
+}
+
+func (msg *IfAddrmsg) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	len := syscall.SizeofIfAddrmsg
+	b := make([]byte, len)
+	b[0] = msg.Family
+	b[1] = msg.Prefixlen
+	b[2] = msg.Flags
+	b[3] = msg.Scope
+	native.PutUint32(b[4:8], msg.Index)
+	return b
+}
+
+type RtMsg struct {
+	syscall.RtMsg
+}
+
+func newRtMsg(family int) *RtMsg {
+	msg := &RtMsg{}
+	msg.Family = uint8(family)
+	msg.Table = syscall.RT_TABLE_MAIN
+	msg.Scope = syscall.RT_SCOPE_UNIVERSE
+	msg.Protocol = syscall.RTPROT_BOOT
+	msg.Type = syscall.RTN_UNICAST
+
+	return msg
+}
+
+func (msg *RtMsg) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	len := syscall.SizeofRtMsg
+	b := make([]byte, len)
+	b[0] = msg.Family
+	b[1] = msg.Dst_len
+	b[2] = msg.Src_len
+	b[3] = msg.Tos
+	b[4] = msg.Table
+	b[5] = msg.Protocol
+	b[6] = msg.Scope
+	b[7] = msg.Type
+	native.PutUint32(b[8:12], msg.Flags)
+	return b
+}
+
+func rtaAlignOf(attrlen int) int {
+	return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
+}
+
+type RtAttr struct {
+	syscall.RtAttr
+	Data []byte
+}
+
+func newRtAttr(attrType int, data []byte) *RtAttr {
+	attr := &RtAttr{}
+	attr.Type = uint16(attrType)
+	attr.Data = data
+
+	return attr
+}
+
+func (attr *RtAttr) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	len := syscall.SizeofRtAttr + len(attr.Data)
+	b := make([]byte, rtaAlignOf(len))
+	native.PutUint16(b[0:2], uint16(len))
+	native.PutUint16(b[2:4], attr.Type)
+	for i, d := range attr.Data {
+		b[4+i] = d
+	}
+
+	return b
+}
+
+type NetlinkRequest struct {
+	syscall.NlMsghdr
+	Data []NetlinkRequestData
+}
+
+func (rr *NetlinkRequest) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	length := rr.Len
+	dataBytes := make([][]byte, len(rr.Data))
+	for i, data := range rr.Data {
+		dataBytes[i] = data.ToWireFormat()
+		length = length + uint32(len(dataBytes[i]))
+	}
+	b := make([]byte, length)
+	native.PutUint32(b[0:4], length)
+	native.PutUint16(b[4:6], rr.Type)
+	native.PutUint16(b[6:8], rr.Flags)
+	native.PutUint32(b[8:12], rr.Seq)
+	native.PutUint32(b[12:16], rr.Pid)
+
+	i := 16
+	for _, data := range dataBytes {
+		for _, dataByte := range data {
+			b[i] = dataByte
+			i = i + 1
+		}
+	}
+	return b
+}
+
+func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
+	rr.Data = append(rr.Data, data)
+}
+
+func newNetlinkRequest(proto, flags int) *NetlinkRequest {
+	rr := &NetlinkRequest{}
+	rr.Len = uint32(syscall.NLMSG_HDRLEN)
+	rr.Type = uint16(proto)
+	rr.Flags = syscall.NLM_F_REQUEST | uint16(flags)
+	rr.Seq = uint32(getSeq())
+	return rr
+}
+
+type NetlinkSocket struct {
+	fd  int
+	lsa syscall.SockaddrNetlink
+}
+
+func getNetlinkSocket() (*NetlinkSocket, error) {
+	fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE)
+	if err != nil {
+		return nil, err
+	}
+	s := &NetlinkSocket{
+		fd: fd,
+	}
+	s.lsa.Family = syscall.AF_NETLINK
+	if err := syscall.Bind(fd, &s.lsa); err != nil {
+		syscall.Close(fd)
+		return nil, err
+	}
+
+	return s, nil
+}
+
+func (s *NetlinkSocket) Close() {
+	syscall.Close(s.fd)
+}
+
+func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
+	if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
+	rb := make([]byte, syscall.Getpagesize())
+	nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
+	if err != nil {
+		return nil, err
+	}
+	if nr < syscall.NLMSG_HDRLEN {
+		return nil, fmt.Errorf("Got short response from netlink")
+	}
+	rb = rb[:nr]
+	return syscall.ParseNetlinkMessage(rb)
+}
+
+func (s *NetlinkSocket) GetPid() (uint32, error) {
+	lsa, err := syscall.Getsockname(s.fd)
+	if err != nil {
+		return 0, err
+	}
+	switch v := lsa.(type) {
+	case *syscall.SockaddrNetlink:
+		return v.Pid, nil
+	}
+	return 0, fmt.Errorf("Wrong socket type")
+}
+
+func (s *NetlinkSocket) HandleAck(seq uint32) error {
+	native := nativeEndian()
+
+	pid, err := s.GetPid()
+	if err != nil {
+		return err
+	}
+
+done:
+	for {
+		msgs, err := s.Recieve()
+		if err != nil {
+			return err
+		}
+		for _, m := range msgs {
+			if m.Header.Seq != seq {
+				return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
+			}
+			if m.Header.Pid != pid {
+				return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
+			}
+			if m.Header.Type == syscall.NLMSG_DONE {
+				break done
+			}
+			if m.Header.Type == syscall.NLMSG_ERROR {
+				error := int32(native.Uint32(m.Data[0:4]))
+				if error == 0 {
+					break done
+				}
+				return syscall.Errno(-error)
+			}
+		}
+	}
+
+	return nil
+}
+
+// Add a new default gateway. Identical to:
+// ip route add default via $ip
+func AddDefaultGw(ip net.IP) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	family := getIpFamily(ip)
+
+	wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+	msg := newRtMsg(family)
+	wb.AddData(msg)
+
+	var ipData []byte
+	if family == syscall.AF_INET {
+		ipData = ip.To4()
+	} else {
+		ipData = ip.To16()
+	}
+
+	gateway := newRtAttr(syscall.RTA_GATEWAY, ipData)
+
+	wb.AddData(gateway)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+
+}
+
+// Bring up a particular network interface
+func NetworkLinkUp(iface *net.Interface) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	msg.Change = syscall.IFF_UP
+	msg.Flags = syscall.IFF_UP
+	msg.Index = int32(iface.Index)
+	wb.AddData(msg)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+// Add an Ip address to an interface. This is identical to:
+// ip addr add $ip/$ipNet dev $iface
+func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	family := getIpFamily(ip)
+
+	wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+	msg := newIfAddrmsg(family)
+	msg.Index = uint32(iface.Index)
+	prefixLen, _ := ipNet.Mask.Size()
+	msg.Prefixlen = uint8(prefixLen)
+	wb.AddData(msg)
+
+	var ipData []byte
+	if family == syscall.AF_INET {
+		ipData = ip.To4()
+	} else {
+		ipData = ip.To16()
+	}
+
+	localData := newRtAttr(syscall.IFA_LOCAL, ipData)
+	wb.AddData(localData)
+
+	addrData := newRtAttr(syscall.IFA_ADDRESS, ipData)
+	wb.AddData(addrData)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+func zeroTerminated(s string) []byte {
+	bytes := make([]byte, len(s)+1)
+	for i := 0; i < len(s); i++ {
+		bytes[i] = s[i]
+	}
+	bytes[len(s)] = 0
+	return bytes
+}
+
+func nonZeroTerminated(s string) []byte {
+	bytes := make([]byte, len(s))
+	for i := 0; i < len(s); i++ {
+		bytes[i] = s[i]
+	}
+	return bytes
+}
+
+// Add a new network link of a specified type. This is identical to
+// running: ip add link $name type $linkType
+func NetworkLinkAdd(name string, linkType string) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	wb.AddData(msg)
+
+	nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
+	wb.AddData(nameData)
+
+	IFLA_INFO_KIND := 1
+
+	kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
+
+	infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat())
+	wb.AddData(infoData)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+// Returns an array of IPNet for all the currently routed subnets on ipv4
+// This is similar to the first column of "ip route" output
+func NetworkGetRoutes() ([]*net.IPNet, error) {
+	native := nativeEndian()
+
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return nil, err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	wb.AddData(msg)
+
+	if err := s.Send(wb); err != nil {
+		return nil, err
+	}
+
+	pid, err := s.GetPid()
+	if err != nil {
+		return nil, err
+	}
+
+	res := make([]*net.IPNet, 0)
+
+done:
+	for {
+		msgs, err := s.Recieve()
+		if err != nil {
+			return nil, err
+		}
+		for _, m := range msgs {
+			if m.Header.Seq != wb.Seq {
+				return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
+			}
+			if m.Header.Pid != pid {
+				return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
+			}
+			if m.Header.Type == syscall.NLMSG_DONE {
+				break done
+			}
+			if m.Header.Type == syscall.NLMSG_ERROR {
+				error := int32(native.Uint32(m.Data[0:4]))
+				if error == 0 {
+					break done
+				}
+				return nil, syscall.Errno(-error)
+			}
+			if m.Header.Type != syscall.RTM_NEWROUTE {
+				continue
+			}
+
+			var iface *net.Interface = nil
+			var ipNet *net.IPNet = nil
+
+			msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0]))
+
+			if msg.Flags&syscall.RTM_F_CLONED != 0 {
+				// Ignore cloned routes
+				continue
+			}
+
+			if msg.Table != syscall.RT_TABLE_MAIN {
+				// Ignore non-main tables
+				continue
+			}
+
+			if msg.Family != syscall.AF_INET {
+				// Ignore non-ipv4 routes
+				continue
+			}
+
+			if msg.Dst_len == 0 {
+				// Ignore default routes
+				continue
+			}
+
+			attrs, err := syscall.ParseNetlinkRouteAttr(&m)
+			if err != nil {
+				return nil, err
+			}
+			for _, attr := range attrs {
+				switch attr.Attr.Type {
+				case syscall.RTA_DST:
+					ip := attr.Value
+					ipNet = &net.IPNet{
+						IP:   ip,
+						Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)),
+					}
+				case syscall.RTA_OIF:
+					index := int(native.Uint32(attr.Value[0:4]))
+					iface, _ = net.InterfaceByIndex(index)
+					_ = iface
+				}
+			}
+			if ipNet != nil {
+				res = append(res, ipNet)
+			}
+		}
+	}
+
+	return res, nil
+}
diff --git a/network.go b/network.go
index 59d8257..593ed6c 100644
--- a/network.go
+++ b/network.go
@@ -4,17 +4,16 @@
 	"encoding/binary"
 	"errors"
 	"fmt"
+	"github.com/dotcloud/docker/iptables"
+	"github.com/dotcloud/docker/netlink"
+	"github.com/dotcloud/docker/proxy"
 	"github.com/dotcloud/docker/utils"
 	"log"
 	"net"
-	"os/exec"
 	"strconv"
-	"strings"
 	"sync"
 )
 
-var NetworkBridgeIface string
-
 const (
 	DefaultNetworkBridge = "docker0"
 	DisableNetworkBridge = "none"
@@ -68,54 +67,10 @@
 	return int32(binary.BigEndian.Uint32(m)) + 1
 }
 
-//Wrapper around the ip command
-func ip(args ...string) (string, error) {
-	path, err := exec.LookPath("ip")
-	if err != nil {
-		return "", fmt.Errorf("command not found: ip")
-	}
-	output, err := exec.Command(path, args...).CombinedOutput()
-	if err != nil {
-		return "", fmt.Errorf("ip failed: ip %v", strings.Join(args, " "))
-	}
-	return string(output), nil
-}
-
-// Wrapper around the iptables command
-func iptables(args ...string) error {
-	path, err := exec.LookPath("iptables")
-	if err != nil {
-		return fmt.Errorf("command not found: iptables")
-	}
-	if err := exec.Command(path, args...).Run(); err != nil {
-		return fmt.Errorf("iptables failed: iptables %v", strings.Join(args, " "))
-	}
-	return nil
-}
-
-func checkRouteOverlaps(routes string, dockerNetwork *net.IPNet) error {
-	utils.Debugf("Routes:\n\n%s", routes)
-	for _, line := range strings.Split(routes, "\n") {
-		if strings.Trim(line, "\r\n\t ") == "" || strings.Contains(line, "default") {
-			continue
-		}
-		_, network, err := net.ParseCIDR(strings.Split(line, " ")[0])
-		if err != nil {
-			// is this a mask-less IP address?
-			if ip := net.ParseIP(strings.Split(line, " ")[0]); ip == nil {
-				// fail only if it's neither a network nor a mask-less IP address
-				return fmt.Errorf("Unexpected ip route output: %s (%s)", err, line)
-			} else {
-				_, network, err = net.ParseCIDR(ip.String() + "/32")
-				if err != nil {
-					return err
-				}
-			}
-		}
-		if err == nil && network != nil {
-			if networkOverlaps(dockerNetwork, network) {
-				return fmt.Errorf("Network %s is already routed: '%s'", dockerNetwork, line)
-			}
+func checkRouteOverlaps(networks []*net.IPNet, dockerNetwork *net.IPNet) error {
+	for _, network := range networks {
+		if networkOverlaps(dockerNetwork, network) {
+			return fmt.Errorf("Network %s is already routed: '%s'", dockerNetwork, network)
 		}
 	}
 	return nil
@@ -124,7 +79,7 @@
 // CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`,
 // and attempts to configure it with an address which doesn't conflict with any other interface on the host.
 // If it can't find an address which doesn't conflict, it will return an error.
-func CreateBridgeIface(ifaceName string) error {
+func CreateBridgeIface(config *DaemonConfig) error {
 	addrs := []string{
 		// Here we don't follow the convention of using the 1st IP of the range for the gateway.
 		// This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges.
@@ -151,7 +106,7 @@
 		if err != nil {
 			return err
 		}
-		routes, err := ip("route")
+		routes, err := netlink.NetworkGetRoutes()
 		if err != nil {
 			return err
 		}
@@ -163,23 +118,43 @@
 		}
 	}
 	if ifaceAddr == "" {
-		return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
+		return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", config.BridgeIface, config.BridgeIface)
 	}
-	utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
+	utils.Debugf("Creating bridge %s with network %s", config.BridgeIface, ifaceAddr)
 
-	if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
-		return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
+	if err := netlink.NetworkLinkAdd(config.BridgeIface, "bridge"); err != nil {
+		return fmt.Errorf("Error creating bridge: %s", err)
+	}
+	iface, err := net.InterfaceByName(config.BridgeIface)
+	if err != nil {
+		return err
+	}
+	ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr)
+	if err != nil {
+		return err
+	}
+	if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
+		return fmt.Errorf("Unable to add private network: %s", err)
+	}
+	if err := netlink.NetworkLinkUp(iface); err != nil {
+		return fmt.Errorf("Unable to start network bridge: %s", err)
 	}
 
-	if output, err := ip("addr", "add", ifaceAddr, "dev", ifaceName); err != nil {
-		return fmt.Errorf("Unable to add private network: %s (%s)", err, output)
-	}
-	if output, err := ip("link", "set", ifaceName, "up"); err != nil {
-		return fmt.Errorf("Unable to start network bridge: %s (%s)", err, output)
-	}
-	if err := iptables("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr,
-		"!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil {
-		return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
+	if config.EnableIptables {
+		if err := iptables.Raw("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr,
+			"!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil {
+			return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
+		}
+
+		if !config.InterContainerCommunication {
+			utils.Debugf("Disable inter-container communication")
+			if err := iptables.Raw("-A", "FORWARD", "-i", config.BridgeIface, "-o", config.BridgeIface, "-j", "DROP"); err != nil {
+				return fmt.Errorf("Unable to prevent intercontainer communication: %s", err)
+			}
+		} else {
+			utils.Debugf("Enable inter-container communication")
+			iptables.Raw("-D", "FORWARD", "-i", config.BridgeIface, "-o", config.BridgeIface, "-j", "DROP")
+		}
 	}
 	return nil
 }
@@ -216,58 +191,27 @@
 // It keeps track of all mappings and is able to unmap at will
 type PortMapper struct {
 	tcpMapping map[int]*net.TCPAddr
-	tcpProxies map[int]Proxy
+	tcpProxies map[int]proxy.Proxy
 	udpMapping map[int]*net.UDPAddr
-	udpProxies map[int]Proxy
+	udpProxies map[int]proxy.Proxy
+
+	iptables  *iptables.Chain
+	defaultIp net.IP
 }
 
-func (mapper *PortMapper) cleanup() error {
-	// Ignore errors - This could mean the chains were never set up
-	iptables("-t", "nat", "-D", "PREROUTING", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER")
-	iptables("-t", "nat", "-D", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", "DOCKER")
-	iptables("-t", "nat", "-D", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER") // Created in versions <= 0.1.6
-	// Also cleanup rules created by older versions, or -X might fail.
-	iptables("-t", "nat", "-D", "PREROUTING", "-j", "DOCKER")
-	iptables("-t", "nat", "-D", "OUTPUT", "-j", "DOCKER")
-	iptables("-t", "nat", "-F", "DOCKER")
-	iptables("-t", "nat", "-X", "DOCKER")
-	mapper.tcpMapping = make(map[int]*net.TCPAddr)
-	mapper.tcpProxies = make(map[int]Proxy)
-	mapper.udpMapping = make(map[int]*net.UDPAddr)
-	mapper.udpProxies = make(map[int]Proxy)
-	return nil
-}
-
-func (mapper *PortMapper) setup() error {
-	if err := iptables("-t", "nat", "-N", "DOCKER"); err != nil {
-		return fmt.Errorf("Failed to create DOCKER chain: %s", err)
-	}
-	if err := iptables("-t", "nat", "-A", "PREROUTING", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER"); err != nil {
-		return fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
-	}
-	if err := iptables("-t", "nat", "-A", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", "DOCKER"); err != nil {
-		return fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
-	}
-	return nil
-}
-
-func (mapper *PortMapper) iptablesForward(rule string, port int, proto string, dest_addr string, dest_port int) error {
-	return iptables("-t", "nat", rule, "DOCKER", "-p", proto, "--dport", strconv.Itoa(port),
-		"!", "-i", NetworkBridgeIface,
-		"-j", "DNAT", "--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port)))
-}
-
-func (mapper *PortMapper) Map(port int, backendAddr net.Addr) error {
+func (mapper *PortMapper) Map(ip net.IP, port int, backendAddr net.Addr) error {
 	if _, isTCP := backendAddr.(*net.TCPAddr); isTCP {
 		backendPort := backendAddr.(*net.TCPAddr).Port
 		backendIP := backendAddr.(*net.TCPAddr).IP
-		if err := mapper.iptablesForward("-A", port, "tcp", backendIP.String(), backendPort); err != nil {
-			return err
+		if mapper.iptables != nil {
+			if err := mapper.iptables.Forward(iptables.Add, ip, port, "tcp", backendIP.String(), backendPort); err != nil {
+				return err
+			}
 		}
 		mapper.tcpMapping[port] = backendAddr.(*net.TCPAddr)
-		proxy, err := NewProxy(&net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: port}, backendAddr)
+		proxy, err := proxy.NewProxy(&net.TCPAddr{IP: ip, Port: port}, backendAddr)
 		if err != nil {
-			mapper.Unmap(port, "tcp")
+			mapper.Unmap(ip, port, "tcp")
 			return err
 		}
 		mapper.tcpProxies[port] = proxy
@@ -275,13 +219,15 @@
 	} else {
 		backendPort := backendAddr.(*net.UDPAddr).Port
 		backendIP := backendAddr.(*net.UDPAddr).IP
-		if err := mapper.iptablesForward("-A", port, "udp", backendIP.String(), backendPort); err != nil {
-			return err
+		if mapper.iptables != nil {
+			if err := mapper.iptables.Forward(iptables.Add, ip, port, "udp", backendIP.String(), backendPort); err != nil {
+				return err
+			}
 		}
 		mapper.udpMapping[port] = backendAddr.(*net.UDPAddr)
-		proxy, err := NewProxy(&net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: port}, backendAddr)
+		proxy, err := proxy.NewProxy(&net.UDPAddr{IP: ip, Port: port}, backendAddr)
 		if err != nil {
-			mapper.Unmap(port, "udp")
+			mapper.Unmap(ip, port, "udp")
 			return err
 		}
 		mapper.udpProxies[port] = proxy
@@ -290,7 +236,7 @@
 	return nil
 }
 
-func (mapper *PortMapper) Unmap(port int, proto string) error {
+func (mapper *PortMapper) Unmap(ip net.IP, port int, proto string) error {
 	if proto == "tcp" {
 		backendAddr, ok := mapper.tcpMapping[port]
 		if !ok {
@@ -300,8 +246,10 @@
 			proxy.Close()
 			delete(mapper.tcpProxies, port)
 		}
-		if err := mapper.iptablesForward("-D", port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
-			return err
+		if mapper.iptables != nil {
+			if err := mapper.iptables.Forward(iptables.Delete, ip, port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
+				return err
+			}
 		}
 		delete(mapper.tcpMapping, port)
 	} else {
@@ -313,21 +261,37 @@
 			proxy.Close()
 			delete(mapper.udpProxies, port)
 		}
-		if err := mapper.iptablesForward("-D", port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
-			return err
+		if mapper.iptables != nil {
+			if err := mapper.iptables.Forward(iptables.Delete, ip, port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
+				return err
+			}
 		}
 		delete(mapper.udpMapping, port)
 	}
 	return nil
 }
 
-func newPortMapper() (*PortMapper, error) {
-	mapper := &PortMapper{}
-	if err := mapper.cleanup(); err != nil {
+func newPortMapper(config *DaemonConfig) (*PortMapper, error) {
+	// We can always try removing the iptables
+	if err := iptables.RemoveExistingChain("DOCKER"); err != nil {
 		return nil, err
 	}
-	if err := mapper.setup(); err != nil {
-		return nil, err
+	var chain *iptables.Chain
+	if config.EnableIptables {
+		var err error
+		chain, err = iptables.NewChain("DOCKER", config.BridgeIface)
+		if err != nil {
+			return nil, fmt.Errorf("Failed to create DOCKER chain: %s", err)
+		}
+	}
+
+	mapper := &PortMapper{
+		tcpMapping: make(map[int]*net.TCPAddr),
+		tcpProxies: make(map[int]proxy.Proxy),
+		udpMapping: make(map[int]*net.UDPAddr),
+		udpProxies: make(map[int]proxy.Proxy),
+		iptables:   chain,
+		defaultIp:  config.DefaultIp,
 	}
 	return mapper, nil
 }
@@ -519,40 +483,56 @@
 	disabled bool
 }
 
-// Allocate an external TCP port and map it to the interface
-func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
+// Allocate an external port and map it to the interface
+func (iface *NetworkInterface) AllocatePort(port Port, binding PortBinding) (*Nat, error) {
 
 	if iface.disabled {
 		return nil, fmt.Errorf("Trying to allocate port for interface %v, which is disabled", iface) // FIXME
 	}
 
-	nat, err := parseNat(spec)
+	ip := iface.manager.portMapper.defaultIp
+
+	if binding.HostIp != "" {
+		ip = net.ParseIP(binding.HostIp)
+	} else {
+		binding.HostIp = ip.String()
+	}
+
+	nat := &Nat{
+		Port:    port,
+		Binding: binding,
+	}
+
+	containerPort, err := parsePort(port.Port())
 	if err != nil {
 		return nil, err
 	}
 
-	if nat.Proto == "tcp" {
-		extPort, err := iface.manager.tcpPortAllocator.Acquire(nat.Frontend)
+	hostPort, _ := parsePort(nat.Binding.HostPort)
+
+	if nat.Port.Proto() == "tcp" {
+		extPort, err := iface.manager.tcpPortAllocator.Acquire(hostPort)
 		if err != nil {
 			return nil, err
 		}
-		backend := &net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}
-		if err := iface.manager.portMapper.Map(extPort, backend); err != nil {
+
+		backend := &net.TCPAddr{IP: iface.IPNet.IP, Port: containerPort}
+		if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
 			iface.manager.tcpPortAllocator.Release(extPort)
 			return nil, err
 		}
-		nat.Frontend = extPort
+		nat.Binding.HostPort = strconv.Itoa(extPort)
 	} else {
-		extPort, err := iface.manager.udpPortAllocator.Acquire(nat.Frontend)
+		extPort, err := iface.manager.udpPortAllocator.Acquire(hostPort)
 		if err != nil {
 			return nil, err
 		}
-		backend := &net.UDPAddr{IP: iface.IPNet.IP, Port: nat.Backend}
-		if err := iface.manager.portMapper.Map(extPort, backend); err != nil {
+		backend := &net.UDPAddr{IP: iface.IPNet.IP, Port: containerPort}
+		if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
 			iface.manager.udpPortAllocator.Release(extPort)
 			return nil, err
 		}
-		nat.Frontend = extPort
+		nat.Binding.HostPort = strconv.Itoa(extPort)
 	}
 	iface.extPorts = append(iface.extPorts, nat)
 
@@ -560,83 +540,37 @@
 }
 
 type Nat struct {
-	Proto    string
-	Frontend int
-	Backend  int
+	Port    Port
+	Binding PortBinding
 }
 
-func parseNat(spec string) (*Nat, error) {
-	var nat Nat
-
-	if strings.Contains(spec, "/") {
-		specParts := strings.Split(spec, "/")
-		if len(specParts) != 2 {
-			return nil, fmt.Errorf("Invalid port format.")
-		}
-		proto := specParts[1]
-		spec = specParts[0]
-		if proto != "tcp" && proto != "udp" {
-			return nil, fmt.Errorf("Invalid port format: unknown protocol %v.", proto)
-		}
-		nat.Proto = proto
-	} else {
-		nat.Proto = "tcp"
-	}
-
-	if strings.Contains(spec, ":") {
-		specParts := strings.Split(spec, ":")
-		if len(specParts) != 2 {
-			return nil, fmt.Errorf("Invalid port format.")
-		}
-		// If spec starts with ':', external and internal ports must be the same.
-		// This might fail if the requested external port is not available.
-		var sameFrontend bool
-		if len(specParts[0]) == 0 {
-			sameFrontend = true
-		} else {
-			front, err := strconv.ParseUint(specParts[0], 10, 16)
-			if err != nil {
-				return nil, err
-			}
-			nat.Frontend = int(front)
-		}
-		back, err := strconv.ParseUint(specParts[1], 10, 16)
-		if err != nil {
-			return nil, err
-		}
-		nat.Backend = int(back)
-		if sameFrontend {
-			nat.Frontend = nat.Backend
-		}
-	} else {
-		port, err := strconv.ParseUint(spec, 10, 16)
-		if err != nil {
-			return nil, err
-		}
-		nat.Backend = int(port)
-	}
-
-	return &nat, nil
+func (n *Nat) String() string {
+	return fmt.Sprintf("%s:%d:%d/%s", n.Binding.HostIp, n.Binding.HostPort, n.Port.Port(), n.Port.Proto())
 }
 
 // Release: Network cleanup - release all resources
 func (iface *NetworkInterface) Release() {
-
 	if iface.disabled {
 		return
 	}
 
 	for _, nat := range iface.extPorts {
-		utils.Debugf("Unmaping %v/%v", nat.Proto, nat.Frontend)
-		if err := iface.manager.portMapper.Unmap(nat.Frontend, nat.Proto); err != nil {
-			log.Printf("Unable to unmap port %v/%v: %v", nat.Proto, nat.Frontend, err)
+		hostPort, err := parsePort(nat.Binding.HostPort)
+		if err != nil {
+			log.Printf("Unable to get host port: %s", err)
+			continue
 		}
-		if nat.Proto == "tcp" {
-			if err := iface.manager.tcpPortAllocator.Release(nat.Frontend); err != nil {
-				log.Printf("Unable to release port tcp/%v: %v", nat.Frontend, err)
+		ip := net.ParseIP(nat.Binding.HostIp)
+		utils.Debugf("Unmaping %s/%s", nat.Port.Proto, nat.Binding.HostPort)
+		if err := iface.manager.portMapper.Unmap(ip, hostPort, nat.Port.Proto()); err != nil {
+			log.Printf("Unable to unmap port %s: %s", nat, err)
+		}
+		if nat.Port.Proto() == "tcp" {
+			if err := iface.manager.tcpPortAllocator.Release(hostPort); err != nil {
+				log.Printf("Unable to release port %s", nat)
 			}
-		} else if err := iface.manager.udpPortAllocator.Release(nat.Frontend); err != nil {
-			log.Printf("Unable to release port udp/%v: %v", nat.Frontend, err)
+		} else if err := iface.manager.udpPortAllocator.Release(hostPort); err != nil {
+			log.Printf("Unable to release port %s: %s", nat, err)
 		}
 	}
 
@@ -704,22 +638,21 @@
 	return err3
 }
 
-func newNetworkManager(bridgeIface string) (*NetworkManager, error) {
-
-	if bridgeIface == DisableNetworkBridge {
+func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) {
+	if config.BridgeIface == DisableNetworkBridge {
 		manager := &NetworkManager{
 			disabled: true,
 		}
 		return manager, nil
 	}
 
-	addr, err := getIfaceAddr(bridgeIface)
+	addr, err := getIfaceAddr(config.BridgeIface)
 	if err != nil {
 		// If the iface is not found, try to create it
-		if err := CreateBridgeIface(bridgeIface); err != nil {
+		if err := CreateBridgeIface(config); err != nil {
 			return nil, err
 		}
-		addr, err = getIfaceAddr(bridgeIface)
+		addr, err = getIfaceAddr(config.BridgeIface)
 		if err != nil {
 			return nil, err
 		}
@@ -737,13 +670,13 @@
 		return nil, err
 	}
 
-	portMapper, err := newPortMapper()
+	portMapper, err := newPortMapper(config)
 	if err != nil {
 		return nil, err
 	}
 
 	manager := &NetworkManager{
-		bridgeIface:      bridgeIface,
+		bridgeIface:      config.BridgeIface,
 		bridgeNetwork:    network,
 		ipAllocator:      ipAllocator,
 		tcpPortAllocator: tcpPortAllocator,
diff --git a/network_proxy.go b/network_proxy.go
deleted file mode 100644
index 8654580..0000000
--- a/network_proxy.go
+++ /dev/null
@@ -1,266 +0,0 @@
-package docker
-
-import (
-	"encoding/binary"
-	"fmt"
-	"github.com/dotcloud/docker/utils"
-	"io"
-	"log"
-	"net"
-	"sync"
-	"syscall"
-	"time"
-)
-
-const (
-	UDPConnTrackTimeout = 90 * time.Second
-	UDPBufSize          = 2048
-)
-
-type Proxy interface {
-	// Start forwarding traffic back and forth the front and back-end
-	// addresses.
-	Run()
-	// Stop forwarding traffic and close both ends of the Proxy.
-	Close()
-	// Return the address on which the proxy is listening.
-	FrontendAddr() net.Addr
-	// Return the proxied address.
-	BackendAddr() net.Addr
-}
-
-type TCPProxy struct {
-	listener     *net.TCPListener
-	frontendAddr *net.TCPAddr
-	backendAddr  *net.TCPAddr
-}
-
-func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
-	listener, err := net.ListenTCP("tcp", frontendAddr)
-	if err != nil {
-		return nil, err
-	}
-	// If the port in frontendAddr was 0 then ListenTCP will have a picked
-	// a port to listen on, hence the call to Addr to get that actual port:
-	return &TCPProxy{
-		listener:     listener,
-		frontendAddr: listener.Addr().(*net.TCPAddr),
-		backendAddr:  backendAddr,
-	}, nil
-}
-
-func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
-	backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
-	if err != nil {
-		log.Printf("Can't forward traffic to backend tcp/%v: %v\n", proxy.backendAddr, err.Error())
-		client.Close()
-		return
-	}
-
-	event := make(chan int64)
-	var broker = func(to, from *net.TCPConn) {
-		written, err := io.Copy(to, from)
-		if err != nil {
-			// If the socket we are writing to is shutdown with
-			// SHUT_WR, forward it to the other end of the pipe:
-			if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE {
-				from.CloseWrite()
-			}
-		}
-		to.CloseRead()
-		event <- written
-	}
-	utils.Debugf("Forwarding traffic between tcp/%v and tcp/%v", client.RemoteAddr(), backend.RemoteAddr())
-	go broker(client, backend)
-	go broker(backend, client)
-
-	var transferred int64 = 0
-	for i := 0; i < 2; i++ {
-		select {
-		case written := <-event:
-			transferred += written
-		case <-quit:
-			// Interrupt the two brokers and "join" them.
-			client.Close()
-			backend.Close()
-			for ; i < 2; i++ {
-				transferred += <-event
-			}
-			goto done
-		}
-	}
-	client.Close()
-	backend.Close()
-done:
-	utils.Debugf("%v bytes transferred between tcp/%v and tcp/%v", transferred, client.RemoteAddr(), backend.RemoteAddr())
-}
-
-func (proxy *TCPProxy) Run() {
-	quit := make(chan bool)
-	defer close(quit)
-
-	utils.Debugf("Starting proxy on tcp/%v for tcp/%v", proxy.frontendAddr, proxy.backendAddr)
-	for {
-		client, err := proxy.listener.Accept()
-		if err != nil {
-			if utils.IsClosedError(err) {
-				utils.Debugf("Stopping proxy on tcp/%v for tcp/%v (socket was closed)", proxy.frontendAddr, proxy.backendAddr)
-			} else {
-				utils.Errorf("Stopping proxy on tcp/%v for tcp/%v (%v)", proxy.frontendAddr, proxy.backendAddr, err.Error())
-			}
-			return
-		}
-		go proxy.clientLoop(client.(*net.TCPConn), quit)
-	}
-}
-
-func (proxy *TCPProxy) Close()                 { proxy.listener.Close() }
-func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
-func (proxy *TCPProxy) BackendAddr() net.Addr  { return proxy.backendAddr }
-
-// A net.Addr where the IP is split into two fields so you can use it as a key
-// in a map:
-type connTrackKey struct {
-	IPHigh uint64
-	IPLow  uint64
-	Port   int
-}
-
-func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
-	if len(addr.IP) == net.IPv4len {
-		return &connTrackKey{
-			IPHigh: 0,
-			IPLow:  uint64(binary.BigEndian.Uint32(addr.IP)),
-			Port:   addr.Port,
-		}
-	}
-	return &connTrackKey{
-		IPHigh: binary.BigEndian.Uint64(addr.IP[:8]),
-		IPLow:  binary.BigEndian.Uint64(addr.IP[8:]),
-		Port:   addr.Port,
-	}
-}
-
-type connTrackMap map[connTrackKey]*net.UDPConn
-
-type UDPProxy struct {
-	listener       *net.UDPConn
-	frontendAddr   *net.UDPAddr
-	backendAddr    *net.UDPAddr
-	connTrackTable connTrackMap
-	connTrackLock  sync.Mutex
-}
-
-func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
-	listener, err := net.ListenUDP("udp", frontendAddr)
-	if err != nil {
-		return nil, err
-	}
-	return &UDPProxy{
-		listener:       listener,
-		frontendAddr:   listener.LocalAddr().(*net.UDPAddr),
-		backendAddr:    backendAddr,
-		connTrackTable: make(connTrackMap),
-	}, nil
-}
-
-func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) {
-	defer func() {
-		proxy.connTrackLock.Lock()
-		delete(proxy.connTrackTable, *clientKey)
-		proxy.connTrackLock.Unlock()
-		utils.Debugf("Done proxying between udp/%v and udp/%v", clientAddr.String(), proxy.backendAddr.String())
-		proxyConn.Close()
-	}()
-
-	readBuf := make([]byte, UDPBufSize)
-	for {
-		proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout))
-	again:
-		read, err := proxyConn.Read(readBuf)
-		if err != nil {
-			if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED {
-				// This will happen if the last write failed
-				// (e.g: nothing is actually listening on the
-				// proxied port on the container), ignore it
-				// and continue until UDPConnTrackTimeout
-				// expires:
-				goto again
-			}
-			return
-		}
-		for i := 0; i != read; {
-			written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr)
-			if err != nil {
-				return
-			}
-			i += written
-			utils.Debugf("Forwarded %v/%v bytes to udp/%v", i, read, clientAddr.String())
-		}
-	}
-}
-
-func (proxy *UDPProxy) Run() {
-	readBuf := make([]byte, UDPBufSize)
-	utils.Debugf("Starting proxy on udp/%v for udp/%v", proxy.frontendAddr, proxy.backendAddr)
-	for {
-		read, from, err := proxy.listener.ReadFromUDP(readBuf)
-		if err != nil {
-			// NOTE: Apparently ReadFrom doesn't return
-			// ECONNREFUSED like Read do (see comment in
-			// UDPProxy.replyLoop)
-			if utils.IsClosedError(err) {
-				utils.Debugf("Stopping proxy on udp/%v for udp/%v (socket was closed)", proxy.frontendAddr, proxy.backendAddr)
-			} else {
-				utils.Errorf("Stopping proxy on udp/%v for udp/%v (%v)", proxy.frontendAddr, proxy.backendAddr, err.Error())
-			}
-			break
-		}
-
-		fromKey := newConnTrackKey(from)
-		proxy.connTrackLock.Lock()
-		proxyConn, hit := proxy.connTrackTable[*fromKey]
-		if !hit {
-			proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
-			if err != nil {
-				log.Printf("Can't proxy a datagram to udp/%s: %v\n", proxy.backendAddr.String(), err)
-				continue
-			}
-			proxy.connTrackTable[*fromKey] = proxyConn
-			go proxy.replyLoop(proxyConn, from, fromKey)
-		}
-		proxy.connTrackLock.Unlock()
-		for i := 0; i != read; {
-			written, err := proxyConn.Write(readBuf[i:read])
-			if err != nil {
-				log.Printf("Can't proxy a datagram to udp/%s: %v\n", proxy.backendAddr.String(), err)
-				break
-			}
-			i += written
-			utils.Debugf("Forwarded %v/%v bytes to udp/%v", i, read, proxy.backendAddr.String())
-		}
-	}
-}
-
-func (proxy *UDPProxy) Close() {
-	proxy.listener.Close()
-	proxy.connTrackLock.Lock()
-	defer proxy.connTrackLock.Unlock()
-	for _, conn := range proxy.connTrackTable {
-		conn.Close()
-	}
-}
-
-func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
-func (proxy *UDPProxy) BackendAddr() net.Addr  { return proxy.backendAddr }
-
-func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
-	switch frontendAddr.(type) {
-	case *net.UDPAddr:
-		return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
-	case *net.TCPAddr:
-		return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr))
-	default:
-		panic(fmt.Errorf("Unsupported protocol"))
-	}
-}
diff --git a/network_test.go b/network_test.go
index bd3a16a..7304e20 100644
--- a/network_test.go
+++ b/network_test.go
@@ -2,117 +2,9 @@
 
 import (
 	"net"
-	"os"
 	"testing"
 )
 
-func TestIptables(t *testing.T) {
-	if err := iptables("-L"); err != nil {
-		t.Fatal(err)
-	}
-	path := os.Getenv("PATH")
-	os.Setenv("PATH", "")
-	defer os.Setenv("PATH", path)
-	if err := iptables("-L"); err == nil {
-		t.Fatal("Not finding iptables in the PATH should cause an error")
-	}
-}
-
-func TestParseNat(t *testing.T) {
-	if nat, err := parseNat("4500"); err == nil {
-		if nat.Frontend != 0 || nat.Backend != 4500 || nat.Proto != "tcp" {
-			t.Errorf("-p 4500 should produce 0->4500/tcp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if nat, err := parseNat(":4501"); err == nil {
-		if nat.Frontend != 4501 || nat.Backend != 4501 || nat.Proto != "tcp" {
-			t.Errorf("-p :4501 should produce 4501->4501/tcp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if nat, err := parseNat("4502:4503"); err == nil {
-		if nat.Frontend != 4502 || nat.Backend != 4503 || nat.Proto != "tcp" {
-			t.Errorf("-p 4502:4503 should produce 4502->4503/tcp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if nat, err := parseNat("4502:4503/tcp"); err == nil {
-		if nat.Frontend != 4502 || nat.Backend != 4503 || nat.Proto != "tcp" {
-			t.Errorf("-p 4502:4503/tcp should produce 4502->4503/tcp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if nat, err := parseNat("4502:4503/udp"); err == nil {
-		if nat.Frontend != 4502 || nat.Backend != 4503 || nat.Proto != "udp" {
-			t.Errorf("-p 4502:4503/udp should produce 4502->4503/udp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if nat, err := parseNat(":4503/udp"); err == nil {
-		if nat.Frontend != 4503 || nat.Backend != 4503 || nat.Proto != "udp" {
-			t.Errorf("-p :4503/udp should produce 4503->4503/udp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if nat, err := parseNat(":4503/tcp"); err == nil {
-		if nat.Frontend != 4503 || nat.Backend != 4503 || nat.Proto != "tcp" {
-			t.Errorf("-p :4503/tcp should produce 4503->4503/tcp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if nat, err := parseNat("4503/tcp"); err == nil {
-		if nat.Frontend != 0 || nat.Backend != 4503 || nat.Proto != "tcp" {
-			t.Errorf("-p 4503/tcp should produce 0->4503/tcp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if nat, err := parseNat("4503/udp"); err == nil {
-		if nat.Frontend != 0 || nat.Backend != 4503 || nat.Proto != "udp" {
-			t.Errorf("-p 4503/udp should produce 0->4503/udp, got %d->%d/%s",
-				nat.Frontend, nat.Backend, nat.Proto)
-		}
-	} else {
-		t.Fatal(err)
-	}
-
-	if _, err := parseNat("4503/tcpgarbage"); err == nil {
-		t.Fatal(err)
-	}
-
-	if _, err := parseNat("4503/tcp/udp"); err == nil {
-		t.Fatal(err)
-	}
-
-	if _, err := parseNat("4503/"); err == nil {
-		t.Fatal(err)
-	}
-}
-
 func TestPortAllocation(t *testing.T) {
 	allocator, err := newPortAllocator()
 	if err != nil {
@@ -385,12 +277,13 @@
 }
 
 func TestCheckRouteOverlaps(t *testing.T) {
-	routes := `default via 10.0.2.2 dev eth0
-10.0.2.0 dev eth0  proto kernel  scope link  src 10.0.2.15
-10.0.3.0/24 dev lxcbr0  proto kernel  scope link  src 10.0.3.1
-10.0.42.0/24 dev testdockbr0  proto kernel  scope link  src 10.0.42.1
-172.16.42.0/24 dev docker0  proto kernel  scope link  src 172.16.42.1
-192.168.142.0/24 dev eth1  proto kernel  scope link  src 192.168.142.142`
+	routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"}
+
+	routes := []*net.IPNet{}
+	for _, addr := range routesData {
+		_, netX, _ := net.ParseCIDR(addr)
+		routes = append(routes, netX)
+	}
 
 	_, netX, _ := net.ParseCIDR("172.16.0.1/24")
 	if err := checkRouteOverlaps(routes, netX); err != nil {
diff --git a/proxy/MAINTAINERS b/proxy/MAINTAINERS
new file mode 100644
index 0000000..1e998f8
--- /dev/null
+++ b/proxy/MAINTAINERS
@@ -0,0 +1 @@
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
diff --git a/network_proxy_test.go b/proxy/network_proxy_test.go
similarity index 99%
rename from network_proxy_test.go
rename to proxy/network_proxy_test.go
index c27393e..b57c23c 100644
--- a/network_proxy_test.go
+++ b/proxy/network_proxy_test.go
@@ -1,4 +1,4 @@
-package docker
+package proxy
 
 import (
 	"bytes"
diff --git a/proxy/proxy.go b/proxy/proxy.go
new file mode 100644
index 0000000..7a711f6
--- /dev/null
+++ b/proxy/proxy.go
@@ -0,0 +1,29 @@
+package proxy
+
+import (
+	"fmt"
+	"net"
+)
+
+type Proxy interface {
+	// Start forwarding traffic back and forth the front and back-end
+	// addresses.
+	Run()
+	// Stop forwarding traffic and close both ends of the Proxy.
+	Close()
+	// Return the address on which the proxy is listening.
+	FrontendAddr() net.Addr
+	// Return the proxied address.
+	BackendAddr() net.Addr
+}
+
+func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
+	switch frontendAddr.(type) {
+	case *net.UDPAddr:
+		return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
+	case *net.TCPAddr:
+		return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr))
+	default:
+		panic(fmt.Errorf("Unsupported protocol"))
+	}
+}
diff --git a/proxy/tcp_proxy.go b/proxy/tcp_proxy.go
new file mode 100644
index 0000000..e7c460f
--- /dev/null
+++ b/proxy/tcp_proxy.go
@@ -0,0 +1,93 @@
+package proxy
+
+import (
+	"github.com/dotcloud/docker/utils"
+	"io"
+	"log"
+	"net"
+	"syscall"
+)
+
+type TCPProxy struct {
+	listener     *net.TCPListener
+	frontendAddr *net.TCPAddr
+	backendAddr  *net.TCPAddr
+}
+
+func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
+	listener, err := net.ListenTCP("tcp", frontendAddr)
+	if err != nil {
+		return nil, err
+	}
+	// If the port in frontendAddr was 0 then ListenTCP will have a picked
+	// a port to listen on, hence the call to Addr to get that actual port:
+	return &TCPProxy{
+		listener:     listener,
+		frontendAddr: listener.Addr().(*net.TCPAddr),
+		backendAddr:  backendAddr,
+	}, nil
+}
+
+func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
+	backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
+	if err != nil {
+		log.Printf("Can't forward traffic to backend tcp/%v: %v\n", proxy.backendAddr, err.Error())
+		client.Close()
+		return
+	}
+
+	event := make(chan int64)
+	var broker = func(to, from *net.TCPConn) {
+		written, err := io.Copy(to, from)
+		if err != nil {
+			// If the socket we are writing to is shutdown with
+			// SHUT_WR, forward it to the other end of the pipe:
+			if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE {
+				from.CloseWrite()
+			}
+		}
+		to.CloseRead()
+		event <- written
+	}
+	utils.Debugf("Forwarding traffic between tcp/%v and tcp/%v", client.RemoteAddr(), backend.RemoteAddr())
+	go broker(client, backend)
+	go broker(backend, client)
+
+	var transferred int64 = 0
+	for i := 0; i < 2; i++ {
+		select {
+		case written := <-event:
+			transferred += written
+		case <-quit:
+			// Interrupt the two brokers and "join" them.
+			client.Close()
+			backend.Close()
+			for ; i < 2; i++ {
+				transferred += <-event
+			}
+			goto done
+		}
+	}
+	client.Close()
+	backend.Close()
+done:
+	utils.Debugf("%v bytes transferred between tcp/%v and tcp/%v", transferred, client.RemoteAddr(), backend.RemoteAddr())
+}
+
+func (proxy *TCPProxy) Run() {
+	quit := make(chan bool)
+	defer close(quit)
+	utils.Debugf("Starting proxy on tcp/%v for tcp/%v", proxy.frontendAddr, proxy.backendAddr)
+	for {
+		client, err := proxy.listener.Accept()
+		if err != nil {
+			utils.Debugf("Stopping proxy on tcp/%v for tcp/%v (%v)", proxy.frontendAddr, proxy.backendAddr, err.Error())
+			return
+		}
+		go proxy.clientLoop(client.(*net.TCPConn), quit)
+	}
+}
+
+func (proxy *TCPProxy) Close()                 { proxy.listener.Close() }
+func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
+func (proxy *TCPProxy) BackendAddr() net.Addr  { return proxy.backendAddr }
diff --git a/proxy/udp_proxy.go b/proxy/udp_proxy.go
new file mode 100644
index 0000000..7d34988
--- /dev/null
+++ b/proxy/udp_proxy.go
@@ -0,0 +1,152 @@
+package proxy
+
+import (
+	"encoding/binary"
+	"github.com/dotcloud/docker/utils"
+	"log"
+	"net"
+	"sync"
+	"syscall"
+	"time"
+)
+
+const (
+	UDPConnTrackTimeout = 90 * time.Second
+	UDPBufSize          = 2048
+)
+
+// A net.Addr where the IP is split into two fields so you can use it as a key
+// in a map:
+type connTrackKey struct {
+	IPHigh uint64
+	IPLow  uint64
+	Port   int
+}
+
+func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
+	if len(addr.IP) == net.IPv4len {
+		return &connTrackKey{
+			IPHigh: 0,
+			IPLow:  uint64(binary.BigEndian.Uint32(addr.IP)),
+			Port:   addr.Port,
+		}
+	}
+	return &connTrackKey{
+		IPHigh: binary.BigEndian.Uint64(addr.IP[:8]),
+		IPLow:  binary.BigEndian.Uint64(addr.IP[8:]),
+		Port:   addr.Port,
+	}
+}
+
+type connTrackMap map[connTrackKey]*net.UDPConn
+
+type UDPProxy struct {
+	listener       *net.UDPConn
+	frontendAddr   *net.UDPAddr
+	backendAddr    *net.UDPAddr
+	connTrackTable connTrackMap
+	connTrackLock  sync.Mutex
+}
+
+func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
+	listener, err := net.ListenUDP("udp", frontendAddr)
+	if err != nil {
+		return nil, err
+	}
+	return &UDPProxy{
+		listener:       listener,
+		frontendAddr:   listener.LocalAddr().(*net.UDPAddr),
+		backendAddr:    backendAddr,
+		connTrackTable: make(connTrackMap),
+	}, nil
+}
+
+func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) {
+	defer func() {
+		proxy.connTrackLock.Lock()
+		delete(proxy.connTrackTable, *clientKey)
+		proxy.connTrackLock.Unlock()
+		utils.Debugf("Done proxying between udp/%v and udp/%v", clientAddr.String(), proxy.backendAddr.String())
+		proxyConn.Close()
+	}()
+
+	readBuf := make([]byte, UDPBufSize)
+	for {
+		proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout))
+	again:
+		read, err := proxyConn.Read(readBuf)
+		if err != nil {
+			if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED {
+				// This will happen if the last write failed
+				// (e.g: nothing is actually listening on the
+				// proxied port on the container), ignore it
+				// and continue until UDPConnTrackTimeout
+				// expires:
+				goto again
+			}
+			return
+		}
+		for i := 0; i != read; {
+			written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr)
+			if err != nil {
+				return
+			}
+			i += written
+			utils.Debugf("Forwarded %v/%v bytes to udp/%v", i, read, clientAddr.String())
+		}
+	}
+}
+
+func (proxy *UDPProxy) Run() {
+	readBuf := make([]byte, UDPBufSize)
+	utils.Debugf("Starting proxy on udp/%v for udp/%v", proxy.frontendAddr, proxy.backendAddr)
+	for {
+		read, from, err := proxy.listener.ReadFromUDP(readBuf)
+		if err != nil {
+			// NOTE: Apparently ReadFrom doesn't return
+			// ECONNREFUSED like Read do (see comment in
+			// UDPProxy.replyLoop)
+			if utils.IsClosedError(err) {
+				utils.Debugf("Stopping proxy on udp/%v for udp/%v (socket was closed)", proxy.frontendAddr, proxy.backendAddr)
+			} else {
+				utils.Errorf("Stopping proxy on udp/%v for udp/%v (%v)", proxy.frontendAddr, proxy.backendAddr, err.Error())
+			}
+			break
+		}
+
+		fromKey := newConnTrackKey(from)
+		proxy.connTrackLock.Lock()
+		proxyConn, hit := proxy.connTrackTable[*fromKey]
+		if !hit {
+			proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
+			if err != nil {
+				log.Printf("Can't proxy a datagram to udp/%s: %v\n", proxy.backendAddr.String(), err)
+				continue
+			}
+			proxy.connTrackTable[*fromKey] = proxyConn
+			go proxy.replyLoop(proxyConn, from, fromKey)
+		}
+		proxy.connTrackLock.Unlock()
+		for i := 0; i != read; {
+			written, err := proxyConn.Write(readBuf[i:read])
+			if err != nil {
+				log.Printf("Can't proxy a datagram to udp/%s: %v\n", proxy.backendAddr.String(), err)
+				break
+			}
+			i += written
+			utils.Debugf("Forwarded %v/%v bytes to udp/%v", i, read, proxy.backendAddr.String())
+		}
+	}
+}
+
+func (proxy *UDPProxy) Close() {
+	proxy.listener.Close()
+	proxy.connTrackLock.Lock()
+	defer proxy.connTrackLock.Unlock()
+	for _, conn := range proxy.connTrackTable {
+		conn.Close()
+	}
+}
+
+func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
+func (proxy *UDPProxy) BackendAddr() net.Addr  { return proxy.backendAddr }
diff --git a/runtime.go b/runtime.go
index f218521..24c1b1a 100644
--- a/runtime.go
+++ b/runtime.go
@@ -3,6 +3,8 @@
 import (
 	"container/list"
 	"fmt"
+	"github.com/dotcloud/docker/devmapper"
+	"github.com/dotcloud/docker/gograph"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io/ioutil"
@@ -10,11 +12,16 @@
 	"os"
 	"os/exec"
 	"path"
+	"path/filepath"
 	"sort"
 	"strings"
 	"time"
 )
 
+const (
+	DefaultFilesystemType = "devicemapper"
+)
+
 var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
 
 type Capabilities struct {
@@ -24,7 +31,6 @@
 }
 
 type Runtime struct {
-	root           string
 	repository     string
 	containers     *list.List
 	networkManager *NetworkManager
@@ -33,16 +39,33 @@
 	idIndex        *utils.TruncIndex
 	capabilities   *Capabilities
 	kernelVersion  *utils.KernelVersionInfo
-	autoRestart    bool
 	volumes        *Graph
 	srv            *Server
 	Dns            []string
+	deviceSet      *devmapper.DeviceSet
+	config         *DaemonConfig
+	containerGraph *gograph.Database
 }
 
 var sysInitPath string
 
 func init() {
-	sysInitPath = utils.SelfPath()
+	env := os.Getenv("_DOCKER_INIT_PATH")
+	if env != "" {
+		sysInitPath = env
+	} else {
+		selfPath := utils.SelfPath()
+
+		// If we have a separate dockerinit, use that, otherwise use the
+		// main docker binary
+		dir := filepath.Dir(selfPath)
+		dockerInitPath := filepath.Join(dir, "dockerinit")
+		if _, err := os.Stat(dockerInitPath); err != nil {
+			sysInitPath = selfPath
+		} else {
+			sysInitPath = dockerInitPath
+		}
+	}
 }
 
 // List returns an array of all containers registered in the runtime.
@@ -64,13 +87,28 @@
 	return nil
 }
 
+func (runtime *Runtime) GetDeviceSet() (*devmapper.DeviceSet, error) {
+	if runtime.deviceSet == nil {
+		return nil, fmt.Errorf("No device set available")
+	}
+	return runtime.deviceSet, nil
+}
+
 // Get looks for a container by the specified ID or name, and returns it.
 // If the container is not found, or if an error occurs, nil is returned.
 func (runtime *Runtime) Get(name string) *Container {
+	if name[0] != '/' {
+		name = "/" + name
+	}
+	if c, _ := runtime.GetByName(name); c != nil {
+		return c
+	}
+
 	id, err := runtime.idIndex.Get(name)
 	if err != nil {
 		return nil
 	}
+
 	e := runtime.getContainerElement(id)
 	if e == nil {
 		return nil
@@ -88,10 +126,9 @@
 	return path.Join(runtime.repository, id)
 }
 
-// Load reads the contents of a container from disk and registers
-// it with Register.
+// Load reads the contents of a container from disk
 // This is typically done at startup.
-func (runtime *Runtime) Load(id string) (*Container, error) {
+func (runtime *Runtime) load(id string) (*Container, error) {
 	container := &Container{root: runtime.containerRoot(id)}
 	if err := container.FromDisk(); err != nil {
 		return nil, err
@@ -102,9 +139,6 @@
 	if container.State.Running {
 		container.State.Ghost = true
 	}
-	if err := runtime.Register(container); err != nil {
-		return nil, err
-	}
 	return container, nil
 }
 
@@ -149,11 +183,11 @@
 		}
 		if !strings.Contains(string(output), "RUNNING") {
 			utils.Debugf("Container %s was supposed to be running be is not.", container.ID)
-			if runtime.autoRestart {
+			if runtime.config.AutoRestart {
 				utils.Debugf("Restarting")
 				container.State.Ghost = false
 				container.State.setStopped(0)
-				hostConfig := &HostConfig{}
+				hostConfig, _ := container.ReadHostConfig()
 				if err := container.Start(hostConfig); err != nil {
 					return err
 				}
@@ -173,9 +207,9 @@
 	if !container.State.Running {
 		close(container.waitLock)
 	} else if !nomonitor {
-		container.allocateNetwork()
-		// hostConfig isn't needed here and can be nil
-		go container.monitor(nil)
+		hostConfig, _ := container.ReadHostConfig()
+		container.allocateNetwork(hostConfig)
+		go container.monitor(hostConfig)
 	}
 	return nil
 }
@@ -203,6 +237,7 @@
 	if err := container.Stop(3); err != nil {
 		return err
 	}
+
 	if mounted, err := container.Mounted(); err != nil {
 		return err
 	} else if mounted {
@@ -210,12 +245,35 @@
 			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
 		}
 	}
+
+	if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
+		utils.Debugf("Unable to remove container from link graph: %s", err)
+	}
+
 	// Deregister the container before removing its directory, to avoid race conditions
 	runtime.idIndex.Delete(container.ID)
 	runtime.containers.Remove(element)
 	if err := os.RemoveAll(container.root); err != nil {
 		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
 	}
+	if runtime.deviceSet.HasDevice(container.ID) {
+		if err := runtime.deviceSet.RemoveDevice(container.ID); err != nil {
+			return fmt.Errorf("Unable to remove device for %v: %v", container.ID, err)
+		}
+	}
+	return nil
+}
+
+func (runtime *Runtime) DeleteImage(id string) error {
+	err := runtime.graph.Delete(id)
+	if err != nil {
+		return err
+	}
+	if runtime.deviceSet.HasDevice(id) {
+		if err := runtime.deviceSet.RemoveDevice(id); err != nil {
+			return fmt.Errorf("Unable to remove device for %v: %v", id, err)
+		}
+	}
 	return nil
 }
 
@@ -228,9 +286,15 @@
 	if err != nil {
 		return err
 	}
+
+	var (
+		containers          []*Container
+		containersToMigrate []*Container
+	)
+
 	for i, v := range dir {
 		id := v.Name()
-		container, err := runtime.Load(id)
+		container, err := runtime.load(id)
 		if i%21 == 0 && os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
 			fmt.Printf("\b%c", wheel[i%4])
 		}
@@ -239,10 +303,110 @@
 			continue
 		}
 		utils.Debugf("Loaded container %v", container.ID)
+		containers = append(containers, container)
+
+		if container.FilesystemType != DefaultFilesystemType {
+			containersToMigrate = append(containersToMigrate, container)
+		}
+	}
+
+	// Migrate containers to the default filesystem type
+	if len(containersToMigrate) > 0 {
+		if err := migrateToDeviceMapper(runtime, containersToMigrate); err != nil {
+			return err
+		}
+	}
+
+	sortContainers(containers, func(i, j *Container) bool {
+		ic, _ := i.ReadHostConfig()
+		jc, _ := j.ReadHostConfig()
+
+		if ic == nil || ic.Links == nil {
+			return true
+		}
+		if jc == nil || jc.Links == nil {
+			return false
+		}
+		return len(ic.Links) < len(jc.Links)
+	})
+	for _, container := range containers {
+		if err := runtime.Register(container); err != nil {
+			utils.Debugf("Failed to register container %s: %s", container.ID, err)
+			continue
+		}
 	}
 	if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
 		fmt.Printf("\bdone.\n")
 	}
+
+	return nil
+}
+
+func migrateToDeviceMapper(runtime *Runtime, containers []*Container) error {
+	var (
+		image    *Image
+		contents []os.FileInfo
+		err      error
+	)
+
+	fmt.Printf("Migrating %d containers to new storage backend\n", len(containers))
+	for _, container := range containers {
+		if container.State.Running {
+			fmt.Printf("WARNING - Cannot migrate %s because the container is running.  Please stop the container and relaunch the daemon!")
+			continue
+		}
+
+		fmt.Printf("Migrating %s\n", container.ID)
+
+		if contents, err = ioutil.ReadDir(container.rwPath()); err != nil {
+			if !os.IsNotExist(err) {
+				fmt.Printf("Error reading rw dir %s\n", err)
+			}
+			continue
+		}
+
+		if len(contents) == 0 {
+			fmt.Printf("Skipping migration of %s because rw layer contains no changes\n")
+			continue
+		}
+
+		if image, err = runtime.graph.Get(container.Image); err != nil {
+			fmt.Printf("Failed to fetch image %s\n", err)
+			continue
+		}
+
+		unmount := func() {
+			if err = image.Unmount(runtime, container.RootfsPath(), container.ID); err != nil {
+				fmt.Printf("Failed to unmount image %s\n", err)
+			}
+		}
+
+		if err = image.Mount(runtime, container.RootfsPath(), container.rwPath(), container.ID); err != nil {
+			fmt.Printf("Failed to mount image %s\n", err)
+			continue
+		}
+
+		if err = image.applyLayer(container.rwPath(), container.RootfsPath()); err != nil {
+			fmt.Printf("Failed to apply layer in storage backend %s\n", err)
+			unmount()
+			continue
+		}
+
+		unmount()
+
+		if err = os.RemoveAll(container.rwPath()); err != nil {
+			fmt.Printf("Failed to remove rw layer %s\n", err)
+		}
+
+		container.FilesystemType = DefaultFilesystemType
+		if err := container.ToDisk(); err != nil {
+			fmt.Printf("Failed to save filesystem type to disk %s\n", err)
+		}
+
+		fmt.Printf("Successful migration for %s\n", container.ID)
+	}
+	fmt.Printf("Migration complete\n")
+
 	return nil
 }
 
@@ -275,27 +439,44 @@
 }
 
 // Create creates a new container from the given configuration.
-func (runtime *Runtime) Create(config *Config) (*Container, error) {
+func (runtime *Runtime) Create(config *Config) (*Container, []string, error) {
 	// Lookup image
 	img, err := runtime.repositories.LookupImage(config.Image)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
+	warnings := []string{}
 	if img.Config != nil {
+		if img.Config.PortSpecs != nil && warnings != nil {
+			for _, p := range img.Config.PortSpecs {
+				if strings.Contains(p, ":") {
+					warnings = append(warnings, "This image expects private ports to be mapped to public ports on your host. "+
+						"This has been deprecated and the public mappings will not be honored."+
+						"Use -p to publish the ports.")
+					break
+				}
+			}
+		}
 		if err := MergeConfig(config, img.Config); err != nil {
-			return nil, err
+			return nil, nil, err
 		}
 	}
 
 	if len(config.Entrypoint) != 0 && config.Cmd == nil {
 		config.Cmd = []string{}
 	} else if config.Cmd == nil || len(config.Cmd) == 0 {
-		return nil, fmt.Errorf("No command specified")
+		return nil, nil, fmt.Errorf("No command specified")
 	}
 
 	// Generate id
 	id := GenerateID()
+
+	// Set the default enitity in the graph
+	if _, err := runtime.containerGraph.Set(fmt.Sprintf("/%s", id), id); err != nil {
+		return nil, nil, err
+	}
+
 	// Generate default hostname
 	// FIXME: the lxc template no longer needs to set a default hostname
 	if config.Hostname == "" {
@@ -323,42 +504,43 @@
 		Image:           img.ID, // Always use the resolved image id
 		NetworkSettings: &NetworkSettings{},
 		// FIXME: do we need to store this in the container?
-		SysInitPath: sysInitPath,
+		SysInitPath:    sysInitPath,
+		FilesystemType: DefaultFilesystemType,
 	}
 	container.root = runtime.containerRoot(container.ID)
 	// Step 1: create the container directory.
 	// This doubles as a barrier to avoid race conditions.
 	if err := os.Mkdir(container.root, 0700); err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
 	resolvConf, err := utils.GetResolvConf()
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
-	if len(config.Dns) == 0 && len(runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
+	if len(config.Dns) == 0 && len(runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
 		//"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns
-		runtime.Dns = defaultDns
+		runtime.config.Dns = defaultDns
 	}
 
 	// If custom dns exists, then create a resolv.conf for the container
-	if len(config.Dns) > 0 || len(runtime.Dns) > 0 {
+	if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 {
 		var dns []string
 		if len(config.Dns) > 0 {
 			dns = config.Dns
 		} else {
-			dns = runtime.Dns
+			dns = runtime.config.Dns
 		}
 		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
 		f, err := os.Create(container.ResolvConfPath)
 		if err != nil {
-			return nil, err
+			return nil, nil, err
 		}
 		defer f.Close()
 		for _, dns := range dns {
 			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
-				return nil, err
+				return nil, nil, err
 			}
 		}
 	} else {
@@ -367,7 +549,7 @@
 
 	// Step 2: save the container json
 	if err := container.ToDisk(); err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
 	// Step 3: if hostname, build hostname and hosts files
@@ -397,9 +579,9 @@
 
 	// Step 4: register the container
 	if err := runtime.Register(container); err != nil {
-		return nil, err
+		return nil, nil, err
 	}
-	return container, nil
+	return container, warnings, nil
 }
 
 // Commit creates a new filesystem image from the current state of a container.
@@ -429,13 +611,85 @@
 	return img, nil
 }
 
-// FIXME: harmonize with NewGraph()
-func NewRuntime(flGraphPath string, autoRestart bool, dns []string) (*Runtime, error) {
-	runtime, err := NewRuntimeFromDirectory(flGraphPath, autoRestart)
+func (runtime *Runtime) GetByName(name string) (*Container, error) {
+	if id, err := runtime.idIndex.Get(name); err == nil {
+		name = id
+	}
+
+	entity := runtime.containerGraph.Get(name)
+	if entity == nil {
+		return nil, fmt.Errorf("Could not find entity for %s", name)
+	}
+	e := runtime.getContainerElement(entity.ID())
+	if e == nil {
+		return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
+	}
+	return e.Value.(*Container), nil
+}
+
+func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
+	children := make(map[string]*Container)
+
+	err := runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
+		c := runtime.Get(e.ID())
+		if c == nil {
+			return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
+		}
+		children[p] = c
+		return nil
+	}, 0)
+
 	if err != nil {
 		return nil, err
 	}
-	runtime.Dns = dns
+	return children, nil
+}
+
+func (runtime *Runtime) RenameLink(oldName, newName string) error {
+	if id, err := runtime.idIndex.Get(oldName); err == nil {
+		oldName = id
+	}
+	entity := runtime.containerGraph.Get(oldName)
+	if entity == nil {
+		return fmt.Errorf("Could not find entity for %s", oldName)
+	}
+
+	// This is not rename but adding a new link for the default name
+	// Strip the leading '/'
+	if strings.HasPrefix(entity.ID(), oldName[1:]) {
+		_, err := runtime.containerGraph.Set(newName, entity.ID())
+		return err
+	}
+	return runtime.containerGraph.Rename(oldName, newName)
+}
+
+func (runtime *Runtime) Link(parentName, childName, alias string) error {
+	if id, err := runtime.idIndex.Get(parentName); err == nil {
+		parentName = id
+	}
+	parent := runtime.containerGraph.Get(parentName)
+	if parent == nil {
+		return fmt.Errorf("Could not get container for %s", parentName)
+	}
+	if id, err := runtime.idIndex.Get(childName); err == nil {
+		childName = id
+	}
+	child := runtime.containerGraph.Get(childName)
+	if child == nil {
+		return fmt.Errorf("Could not get container for %s", childName)
+	}
+	cc := runtime.Get(child.ID())
+
+	_, err := runtime.containerGraph.Set(path.Join(parentName, alias), cc.ID)
+	return err
+}
+
+// FIXME: harmonize with NewGraph()
+func NewRuntime(config *DaemonConfig) (*Runtime, error) {
+	runtime, err := NewRuntimeFromDirectory(config)
+	if err != nil {
+		return nil, err
+	}
 
 	if k, err := utils.GetKernelVersion(); err != nil {
 		log.Printf("WARNING: %s\n", err)
@@ -449,34 +703,42 @@
 	return runtime, nil
 }
 
-func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
-	runtimeRepo := path.Join(root, "containers")
+func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
+	runtimeRepo := path.Join(config.GraphPath, "containers")
 
 	if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
 		return nil, err
 	}
 
-	g, err := NewGraph(path.Join(root, "graph"))
+	g, err := NewGraph(path.Join(config.GraphPath, "graph"))
 	if err != nil {
 		return nil, err
 	}
-	volumes, err := NewGraph(path.Join(root, "volumes"))
+	volumes, err := NewGraph(path.Join(config.GraphPath, "volumes"))
 	if err != nil {
 		return nil, err
 	}
-	repositories, err := NewTagStore(path.Join(root, "repositories"), g)
+	repositories, err := NewTagStore(path.Join(config.GraphPath, "repositories"), g)
 	if err != nil {
 		return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
 	}
-	if NetworkBridgeIface == "" {
-		NetworkBridgeIface = DefaultNetworkBridge
+	if config.BridgeIface == "" {
+		config.BridgeIface = DefaultNetworkBridge
 	}
-	netManager, err := newNetworkManager(NetworkBridgeIface)
+	netManager, err := newNetworkManager(config)
 	if err != nil {
 		return nil, err
 	}
+
+	graph, err := gograph.NewDatabase(path.Join(config.GraphPath, "linkgraph.db"))
+	if err != nil {
+		return nil, err
+	}
+
+	// Initialize devicemapper deviceSet
+	deviceSet := devmapper.NewDeviceSet(config.GraphPath)
+
 	runtime := &Runtime{
-		root:           root,
 		repository:     runtimeRepo,
 		containers:     list.New(),
 		networkManager: netManager,
@@ -484,8 +746,10 @@
 		repositories:   repositories,
 		idIndex:        utils.NewTruncIndex(),
 		capabilities:   &Capabilities{},
-		autoRestart:    autoRestart,
 		volumes:        volumes,
+		config:         config,
+		containerGraph: graph,
+		deviceSet:      deviceSet,
 	}
 
 	if err := runtime.restore(); err != nil {
diff --git a/runtime_test.go b/runtime_test.go
index 2a3a093..423df6f 100644
--- a/runtime_test.go
+++ b/runtime_test.go
@@ -3,12 +3,16 @@
 import (
 	"bytes"
 	"fmt"
+	"github.com/dotcloud/docker/devmapper"
 	"github.com/dotcloud/docker/sysinit"
 	"github.com/dotcloud/docker/utils"
 	"io"
+	"io/ioutil"
 	"log"
 	"net"
 	"os"
+	"path"
+	"path/filepath"
 	"runtime"
 	"strconv"
 	"strings"
@@ -25,6 +29,10 @@
 	unitTestStoreBase     = "/var/lib/docker/unit-tests"
 	testDaemonAddr        = "127.0.0.1:4270"
 	testDaemonProto       = "tcp"
+
+	unitTestDMDataLoopbackSize     = 209715200 // 200MB
+	unitTestDMMetaDataLoopbackSize = 104857600 // 100MB
+	unitTestDMBaseFsSize           = 157286400 // 150MB
 )
 
 var (
@@ -44,7 +52,14 @@
 	}
 	wg.Wait()
 	runtime.networkManager.Close()
-	return os.RemoveAll(runtime.root)
+
+	for _, container := range runtime.List() {
+		container.EnsureUnmounted()
+	}
+	if err := runtime.deviceSet.Shutdown(); err != nil {
+		utils.Debugf("Error shutting down devicemapper for runtime %s", runtime.config.GraphPath)
+	}
+	return os.RemoveAll(runtime.config.GraphPath)
 }
 
 func cleanup(runtime *Runtime) error {
@@ -58,7 +73,7 @@
 	}
 	for _, image := range images {
 		if image.ID != unitTestImageID {
-			runtime.graph.Delete(image.ID)
+			runtime.DeleteImage(image.ID)
 		}
 	}
 	return nil
@@ -73,9 +88,83 @@
 	return f, nil
 }
 
+// Remove any leftover device mapper devices from earlier runs of the unit tests
+func removeDev(name string) error {
+	path := filepath.Join("/dev/mapper", name)
+
+	if f, err := os.OpenFile(path, os.O_RDONLY, 07777); err != nil {
+		if er, ok := err.(*os.PathError); ok && er.Err == syscall.ENXIO {
+			// No device for this node, just remove it
+			return os.Remove(path)
+		}
+	} else {
+		f.Close()
+	}
+	if err := devmapper.RemoveDevice(name); err != nil {
+		return fmt.Errorf("Unable to remove device %s needed to get a freash unit test environment", name)
+	}
+	return nil
+}
+
+func cleanupDevMapper() error {
+	utils.Debugf("[devmapper cleanup] starting")
+	defer utils.Debugf("[devmapper cleanup] done")
+	filter := "docker-" + path.Base(unitTestStoreBase)
+	utils.Debugf("Filtering out %s\n", filter)
+	// Unmount any leftover mounts from previous unit test runs
+	if data, err := ioutil.ReadFile("/proc/mounts"); err != nil {
+		return err
+	} else {
+		for _, line := range strings.Split(string(data), "\n") {
+			if cols := strings.Split(line, " "); len(cols) >= 2 && strings.HasPrefix(cols[0], path.Join("/dev/mapper", filter)) {
+				utils.Debugf("[devmapper cleanup] Unmounting device: %s", cols[1])
+				if err := syscall.Unmount(cols[1], 0); err != nil {
+					return fmt.Errorf("Unable to unmount %s needed to get a freash unit test environment: %s", cols[1], err)
+				}
+			}
+		}
+	}
+
+	utils.Debugf("[devmapper cleanup] looking for leftover devices")
+	// Remove any leftover devmapper devices from previous unit run tests
+	infos, err := ioutil.ReadDir("/dev/mapper")
+	if err != nil {
+		// If the mapper file does not exist there is nothing to clean up
+		if os.IsNotExist(err) {
+			return nil
+		}
+		return err
+	}
+	pools := []string{}
+	for _, info := range infos {
+		if name := info.Name(); strings.HasPrefix(name, filter+"-") {
+			if strings.HasSuffix(name, "-pool") {
+				pools = append(pools, name)
+			} else {
+				if err := removeDev(name); err != nil {
+					return err
+				}
+			}
+		}
+	}
+	// We need to remove the pool last as the other devices block it
+	for _, pool := range pools {
+		utils.Debugf("[devmapper cleanup] Removing pool: %s", pool)
+		if err := removeDev(pool); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 func init() {
 	os.Setenv("TEST", "1")
 
+	// Set unit-test specific values
+	devmapper.DefaultDataLoopbackSize = unitTestDMDataLoopbackSize
+	devmapper.DefaultMetaDataLoopbackSize = unitTestDMMetaDataLoopbackSize
+	devmapper.DefaultBaseFsSize = unitTestDMBaseFsSize
+
 	// Hack to run sys init during unit testing
 	if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" {
 		sysinit.SysInit()
@@ -86,7 +175,9 @@
 		log.Fatal("docker tests need to be run as root")
 	}
 
-	NetworkBridgeIface = unitTestNetworkBridge
+	if err := cleanupDevMapper(); err != nil {
+		log.Fatalf("Unable to cleanup devmapper: %s", err)
+	}
 
 	// Setup the base runtime, which will be duplicated for each test.
 	// (no tests are run directly in the base)
@@ -97,9 +188,13 @@
 	startFds, startGoroutines = utils.GetTotalUsedFds(), runtime.NumGoroutine()
 }
 
-
 func setupBaseImage() {
-	runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, false)
+	config := &DaemonConfig{
+		GraphPath:   unitTestStoreBase,
+		AutoRestart: false,
+		BridgeIface: unitTestNetworkBridge,
+	}
+	runtime, err := NewRuntimeFromDirectory(config)
 	if err != nil {
 		log.Fatalf("Unable to create a runtime for tests:", err)
 	}
@@ -107,7 +202,6 @@
 	// Create the "Server"
 	srv := &Server{
 		runtime:     runtime,
-		enableCors:  false,
 		pullingPool: make(map[string]struct{}),
 		pushingPool: make(map[string]struct{}),
 	}
@@ -121,7 +215,6 @@
 	}
 }
 
-
 func spawnGlobalDaemon() {
 	if globalRuntime != nil {
 		utils.Debugf("Global runtime already exists. Skipping.")
@@ -130,7 +223,6 @@
 	globalRuntime = mkRuntime(log.New(os.Stderr, "", 0))
 	srv := &Server{
 		runtime:     globalRuntime,
-		enableCors:  false,
 		pullingPool: make(map[string]struct{}),
 		pushingPool: make(map[string]struct{}),
 	}
@@ -172,7 +264,7 @@
 		t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
 	}
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 	},
@@ -181,12 +273,6 @@
 		t.Fatal(err)
 	}
 
-	defer func() {
-		if err := runtime.Destroy(container); err != nil {
-			t.Error(err)
-		}
-	}()
-
 	// Make sure we can find the newly created container with List()
 	if len(runtime.List()) != 1 {
 		t.Errorf("Expected 1 container, %v found", len(runtime.List()))
@@ -213,7 +299,7 @@
 	}
 
 	// Make sure crete with bad parameters returns an error
-	_, err = runtime.Create(
+	_, _, err = runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 		},
@@ -222,7 +308,7 @@
 		t.Fatal("Builder.Create should throw an error when Cmd is missing")
 	}
 
-	_, err = runtime.Create(
+	_, _, err = runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{},
@@ -237,29 +323,18 @@
 		Cmd:       []string{"/bin/ls"},
 		PortSpecs: []string{"80"},
 	}
-	container, err = runtime.Create(config)
+	container, _, err = runtime.Create(config)
 
-	image, err := runtime.Commit(container, "testrepo", "testtag", "", "", config)
+	_, err = runtime.Commit(container, "testrepo", "testtag", "", "", config)
 	if err != nil {
 		t.Error(err)
 	}
-
-	_, err = runtime.Create(
-		&Config{
-			Image:     image.ID,
-			PortSpecs: []string{"80000:80"},
-		},
-	)
-	if err == nil {
-		t.Fatal("Builder.Create should throw an error when PortSpecs is invalid")
-	}
-
 }
 
 func TestDestroy(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 	},
@@ -333,6 +408,7 @@
 	port := 5554
 	var container *Container
 	var strPort string
+	var p Port
 	for {
 		port += 1
 		strPort = strconv.Itoa(port)
@@ -345,22 +421,33 @@
 			t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
 		}
 		t.Log("Trying port", strPort)
-		container, err = runtime.Create(&Config{
-			Image:     GetTestImage(runtime).ID,
-			Cmd:       []string{"sh", "-c", cmd},
-			PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)},
+		ep := make(map[Port]struct{}, 1)
+		p = Port(fmt.Sprintf("%s/%s", strPort, proto))
+		ep[p] = struct{}{}
+
+		container, _, err = runtime.Create(&Config{
+			Image:        GetTestImage(runtime).ID,
+			Cmd:          []string{"sh", "-c", cmd},
+			PortSpecs:    []string{fmt.Sprintf("%s/%s", strPort, proto)},
+			ExposedPorts: ep,
 		})
-		if container != nil {
-			break
-		}
 		if err != nil {
 			nuke(runtime)
 			t.Fatal(err)
 		}
+
+		if container != nil {
+			break
+		}
 		t.Logf("Port %v already in use", strPort)
 	}
 
-	hostConfig := &HostConfig{}
+	hostConfig := &HostConfig{
+		PortBindings: make(map[Port][]PortBinding),
+	}
+	hostConfig.PortBindings[p] = []PortBinding{
+		{},
+	}
 	if err := container.Start(hostConfig); err != nil {
 		nuke(runtime)
 		t.Fatal(err)
@@ -375,7 +462,7 @@
 	// Even if the state is running, lets give some time to lxc to spawn the process
 	container.WaitTimeout(500 * time.Millisecond)
 
-	strPort = container.NetworkSettings.PortMapping[strings.Title(proto)][strPort]
+	strPort = container.NetworkSettings.Ports[p][0].HostPort
 	return runtime, container, strPort
 }
 
@@ -507,7 +594,8 @@
 
 	// Here are are simulating a docker restart - that is, reloading all containers
 	// from scratch
-	runtime2, err := NewRuntimeFromDirectory(runtime1.root, false)
+	runtime1.config.AutoRestart = false
+	runtime2, err := NewRuntimeFromDirectory(runtime1.config)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -534,3 +622,283 @@
 	}
 	container2.State.Running = false
 }
+
+func TestContainerCreatedWithDefaultFilesystemType(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+
+	container, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
+	defer runtime.Destroy(container)
+
+	if container.FilesystemType != DefaultFilesystemType {
+		t.Fatalf("Container filesystem type should be %s but got %s", DefaultFilesystemType, container.FilesystemType)
+	}
+}
+
+func TestReloadContainerLinks(t *testing.T) {
+	runtime1 := mkRuntime(t)
+	defer nuke(runtime1)
+	// Create a container with one instance of docker
+	container1, _, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t)
+	defer runtime1.Destroy(container1)
+
+	// Create a second container meant to be killed
+	container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
+	defer runtime1.Destroy(container2)
+
+	// Start the container non blocking
+	hostConfig := &HostConfig{}
+	if err := container2.Start(hostConfig); err != nil {
+		t.Fatal(err)
+	}
+	h1 := &HostConfig{}
+	// Add a link to container 2
+	h1.Links = []string{utils.TruncateID(container2.ID) + ":first"}
+	if err := container1.Start(h1); err != nil {
+		t.Fatal(err)
+	}
+
+	if !container2.State.Running {
+		t.Fatalf("Container %v should appear as running but isn't", container2.ID)
+	}
+
+	if !container1.State.Running {
+		t.Fatalf("Container %s should appear as running bu isn't", container1.ID)
+	}
+
+	if len(runtime1.List()) != 2 {
+		t.Errorf("Expected 2 container, %v found", len(runtime1.List()))
+	}
+
+	if !container2.State.Running {
+		t.Fatalf("Container %v should appear as running but isn't", container2.ID)
+	}
+
+	// Here are are simulating a docker restart - that is, reloading all containers
+	// from scratch
+	runtime1.config.AutoRestart = true
+	runtime2, err := NewRuntimeFromDirectory(runtime1.config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer nuke(runtime2)
+	if len(runtime2.List()) != 2 {
+		t.Errorf("Expected 2 container, %v found", len(runtime2.List()))
+	}
+	runningCount := 0
+	for _, c := range runtime2.List() {
+		if c.State.Running {
+			t.Logf("Running container found: %v (%v)", c.ID, c.Path)
+			runningCount++
+		}
+	}
+	if runningCount != 2 {
+		t.Fatalf("Expected 2 container alive, %d found", runningCount)
+	}
+
+	// Make sure container 2 ( the child of container 1 ) was registered and started first
+	// with the runtime
+	first := runtime2.containers.Front()
+	if first.Value.(*Container).ID != container2.ID {
+		t.Fatalf("Container 2 %s should be registered first in the runtime", container2.ID)
+	}
+
+	t.Logf("Number of links: %d", runtime2.containerGraph.Refs("engine"))
+	// Verify that the link is still registered in the runtime
+	entity := runtime2.containerGraph.Get(fmt.Sprintf("/%s", container1.ID))
+	if entity == nil {
+		t.Fatal("Entity should not be nil")
+	}
+}
+
+func TestDefaultContainerName(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+	srv := &Server{runtime: runtime}
+
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	shortId, _, err := srv.ContainerCreate(config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	container := runtime.Get(shortId)
+	containerID := container.ID
+
+	paths := runtime.containerGraph.RefPaths(containerID)
+	if paths == nil || len(paths) == 0 {
+		t.Fatalf("Could not find edges for %s", containerID)
+	}
+	edge := paths[0]
+	if edge.ParentID != "0" {
+		t.Fatalf("Expected engine got %s", edge.ParentID)
+	}
+	if edge.EntityID != containerID {
+		t.Fatalf("Expected %s got %s", containerID, edge.EntityID)
+	}
+	if edge.Name != containerID {
+		t.Fatalf("Expected %s got %s", containerID, edge.Name)
+	}
+}
+
+func TestDefaultContainerRename(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+	srv := &Server{runtime: runtime}
+
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	shortId, _, err := srv.ContainerCreate(config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	container := runtime.Get(shortId)
+	containerID := container.ID
+
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", containerID), "/webapp"); err != nil {
+		t.Fatal(err)
+	}
+
+	webapp, err := runtime.GetByName("/webapp")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if webapp.ID != container.ID {
+		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
+	}
+}
+
+func TestLinkChildContainer(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+	srv := &Server{runtime: runtime}
+
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	shortId, _, err := srv.ContainerCreate(config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	container := runtime.Get(shortId)
+
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", container.ID), "/webapp"); err != nil {
+		t.Fatal(err)
+	}
+
+	webapp, err := runtime.GetByName("/webapp")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if webapp.ID != container.ID {
+		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
+	}
+
+	config, _, _, err = ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	shortId, _, err = srv.ContainerCreate(config)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	childContainer := runtime.Get(shortId)
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", childContainer.ID), "/db"); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := runtime.Link("/webapp", "/db", "db"); err != nil {
+		t.Fatal(err)
+	}
+
+	// Get the child by it's new name
+	db, err := runtime.GetByName("/webapp/db")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if db.ID != childContainer.ID {
+		t.Fatalf("Expect db id to match container id: %s != %s", db.ID, childContainer.ID)
+	}
+}
+
+func TestGetAllChildren(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+	srv := &Server{runtime: runtime}
+
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	shortId, _, err := srv.ContainerCreate(config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	container := runtime.Get(shortId)
+
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", container.ID), "/webapp"); err != nil {
+		t.Fatal(err)
+	}
+
+	webapp, err := runtime.GetByName("/webapp")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if webapp.ID != container.ID {
+		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
+	}
+
+	config, _, _, err = ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	shortId, _, err = srv.ContainerCreate(config)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	childContainer := runtime.Get(shortId)
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", childContainer.ID), "/db"); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := runtime.Link("/webapp", "/db", "db"); err != nil {
+		t.Fatal(err)
+	}
+
+	children, err := runtime.Children("/webapp")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if children == nil {
+		t.Fatal("Children should not be nil")
+	}
+	if len(children) == 0 {
+		t.Fatal("Children should not be empty")
+	}
+
+	for key, value := range children {
+		if key != "/webapp/db" {
+			t.Fatalf("Expected /webapp/db got %s", key)
+		}
+		if value.ID != childContainer.ID {
+			t.Fatalf("Expected id %s got %s", childContainer.ID, value.ID)
+		}
+	}
+}
diff --git a/server.go b/server.go
index 27d1968..e1ec19a 100644
--- a/server.go
+++ b/server.go
@@ -6,6 +6,7 @@
 	"errors"
 	"fmt"
 	"github.com/dotcloud/docker/auth"
+	"github.com/dotcloud/docker/gograph"
 	"github.com/dotcloud/docker/registry"
 	"github.com/dotcloud/docker/utils"
 	"io"
@@ -103,7 +104,7 @@
 }
 
 func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
-	r, err := registry.NewRegistry(srv.runtime.root, nil, srv.HTTPRequestFactory(nil))
+	r, err := registry.NewRegistry(srv.runtime.config.GraphPath, nil, srv.HTTPRequestFactory(nil))
 	if err != nil {
 		return nil, err
 	}
@@ -140,7 +141,7 @@
 		return "", err
 	}
 
-	c, err := srv.runtime.Create(config)
+	c, _, err := srv.runtime.Create(config)
 	if err != nil {
 		return "", err
 	}
@@ -268,19 +269,26 @@
 		kernelVersion = kv.String()
 	}
 
+	devSetInfo := srv.runtime.deviceSet.Status()
+
 	return &APIInfo{
-		Containers:         len(srv.runtime.List()),
-		Images:             imgcount,
-		MemoryLimit:        srv.runtime.capabilities.MemoryLimit,
-		SwapLimit:          srv.runtime.capabilities.SwapLimit,
-		IPv4Forwarding:     !srv.runtime.capabilities.IPv4ForwardingDisabled,
-		Debug:              os.Getenv("DEBUG") != "",
-		NFd:                utils.GetTotalUsedFds(),
-		NGoroutines:        runtime.NumGoroutine(),
-		LXCVersion:         lxcVersion,
-		NEventsListener:    len(srv.events),
-		KernelVersion:      kernelVersion,
-		IndexServerAddress: auth.IndexServerAddress(),
+		Containers:             len(srv.runtime.List()),
+		Images:                 imgcount,
+		MemoryLimit:            srv.runtime.capabilities.MemoryLimit,
+		SwapLimit:              srv.runtime.capabilities.SwapLimit,
+		IPv4Forwarding:         !srv.runtime.capabilities.IPv4ForwardingDisabled,
+		Debug:                  os.Getenv("DEBUG") != "",
+		NFd:                    utils.GetTotalUsedFds(),
+		NGoroutines:            runtime.NumGoroutine(),
+		LXCVersion:             lxcVersion,
+		NEventsListener:        len(srv.events),
+		KernelVersion:          kernelVersion,
+		IndexServerAddress:     auth.IndexServerAddress(),
+		DevmapperPool:          devSetInfo.PoolName,
+		DevmapperDataUsed:      devSetInfo.Data.Used,
+		DevmapperDataTotal:     devSetInfo.Data.Total,
+		DevmapperMetadataUsed:  devSetInfo.Metadata.Used,
+		DevmapperMetadataTotal: devSetInfo.Metadata.Total,
 	}
 }
 
@@ -358,7 +366,7 @@
 func (srv *Server) Containers(all, size bool, n int, since, before string) []APIContainers {
 	var foundBefore bool
 	var displayed int
-	retContainers := []APIContainers{}
+	out := []APIContainers{}
 
 	for _, container := range srv.runtime.List() {
 		if !container.State.Running && !all && n == -1 && since == "" && before == "" {
@@ -380,23 +388,35 @@
 			break
 		}
 		displayed++
-
-		c := APIContainers{
-			ID: container.ID,
-		}
-		c.Image = srv.runtime.repositories.ImageName(container.Image)
-		c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
-		c.Created = container.Created.Unix()
-		c.Status = container.State.String()
-		c.Ports = container.NetworkSettings.PortMappingAPI()
-		if size {
-			c.SizeRw, c.SizeRootFs = container.GetSize()
-		}
-		retContainers = append(retContainers, c)
+		c := createAPIContainer(container, size, srv.runtime)
+		out = append(out, c)
 	}
-	return retContainers
+	return out
 }
 
+func createAPIContainer(container *Container, size bool, runtime *Runtime) APIContainers {
+	c := APIContainers{
+		ID: container.ID,
+	}
+	names := []string{}
+	runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
+		if e.ID() == container.ID {
+			names = append(names, p)
+		}
+		return nil
+	}, -1)
+	c.Names = names
+
+	c.Image = runtime.repositories.ImageName(container.Image)
+	c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
+	c.Created = container.Created.Unix()
+	c.Status = container.State.String()
+	c.Ports = container.NetworkSettings.PortMappingAPI()
+	if size {
+		c.SizeRw, c.SizeRootFs = container.GetSize()
+	}
+	return c
+}
 func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, config *Config) (string, error) {
 	container := srv.runtime.Get(name)
 	if container == nil {
@@ -635,7 +655,7 @@
 }
 
 func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string, parallel bool) error {
-	r, err := registry.NewRegistry(srv.runtime.root, authConfig, srv.HTTPRequestFactory(metaHeaders))
+	r, err := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders))
 	if err != nil {
 		return err
 	}
@@ -844,7 +864,7 @@
 
 	out = utils.NewWriteFlusher(out)
 	img, err := srv.runtime.graph.Get(localName)
-	r, err2 := registry.NewRegistry(srv.runtime.root, authConfig, srv.HTTPRequestFactory(metaHeaders))
+	r, err2 := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders))
 	if err2 != nil {
 		return err2
 	}
@@ -909,10 +929,9 @@
 	return nil
 }
 
-func (srv *Server) ContainerCreate(config *Config) (string, error) {
-
+func (srv *Server) ContainerCreate(config *Config) (string, []string, error) {
 	if config.Memory != 0 && config.Memory < 524288 {
-		return "", fmt.Errorf("Memory limit must be given in bytes (minimum 524288 bytes)")
+		return "", nil, fmt.Errorf("Memory limit must be given in bytes (minimum 524288 bytes)")
 	}
 
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
@@ -922,7 +941,7 @@
 	if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
 		config.MemorySwap = -1
 	}
-	container, err := srv.runtime.Create(config)
+	container, buildWarnings, err := srv.runtime.Create(config)
 	if err != nil {
 		if srv.runtime.graph.IsNotExist(err) {
 
@@ -931,12 +950,12 @@
 				tag = DEFAULTTAG
 			}
 
-			return "", fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag)
+			return "", nil, fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag)
 		}
-		return "", err
+		return "", nil, err
 	}
 	srv.LogEvent("create", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
-	return container.ShortID(), nil
+	return container.ShortID(), buildWarnings, nil
 }
 
 func (srv *Server) ContainerRestart(name string, t int) error {
@@ -951,7 +970,34 @@
 	return nil
 }
 
-func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
+func (srv *Server) ContainerDestroy(name string, removeVolume, removeLink bool) error {
+	if removeLink {
+		p := name
+		if p[0] != '/' {
+			p = "/" + p
+		}
+		parent, n := path.Split(p)
+		l := len(parent)
+		if parent[l-1] == '/' {
+			parent = parent[:l-1]
+		}
+
+		pe := srv.runtime.containerGraph.Get(parent)
+		parentContainer := srv.runtime.Get(pe.ID())
+
+		if parentContainer != nil && parentContainer.activeLinks != nil {
+			if link, exists := parentContainer.activeLinks[n]; exists {
+				link.Disable()
+			} else {
+				utils.Debugf("Could not find active link for %s", name)
+			}
+		}
+
+		if err := srv.runtime.containerGraph.Delete(name); err != nil {
+			return err
+		}
+		return nil
+	}
 	if container := srv.runtime.Get(name); container != nil {
 		if container.State.Running {
 			return fmt.Errorf("Impossible to remove a running container, please stop it first")
@@ -996,6 +1042,14 @@
 
 var ErrImageReferenced = errors.New("Image referenced by a repository")
 
+func (srv *Server) getChildImages(id string) ([]*Image, error) {
+	byParents, err := srv.runtime.graph.ByParent()
+	if err != nil {
+		return nil, err
+	}
+	return byParents[id], nil
+}
+
 func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error {
 	// If the image is referenced by a repo, do not delete
 	if len(srv.runtime.repositories.ByID()[id]) != 0 {
@@ -1028,7 +1082,7 @@
 		if err := srv.runtime.repositories.DeleteAll(id); err != nil {
 			return err
 		}
-		err := srv.runtime.graph.Delete(id)
+		err := srv.runtime.DeleteImage(id)
 		if err != nil {
 			return err
 		}
@@ -1101,8 +1155,35 @@
 	if err != nil {
 		return nil, fmt.Errorf("No such image: %s", name)
 	}
+	images := make(map[string]bool)
+	images[img.ID] = true
+
+	children, err := srv.getChildImages(img.ID)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, i := range children {
+		images[i.ID] = true
+	}
+
+	// Check for any containers referencing the image or children of the image
+	referencedContainers := []string{}
+
+	for e := srv.runtime.containers.Front(); e != nil; e = e.Next() {
+		c := e.Value.(*Container)
+		if images[c.Image] {
+			referencedContainers = append(referencedContainers, c.ID)
+		}
+	}
+
+	if len(referencedContainers) > 0 {
+		return nil, fmt.Errorf("Cannot delete image with existing containers.  Please remove %s before deleting image.",
+			strings.Join(referencedContainers, ", "))
+	}
+
 	if !autoPrune {
-		if err := srv.runtime.graph.Delete(img.ID); err != nil {
+		if err := srv.runtime.DeleteImage(img.ID); err != nil {
 			return nil, fmt.Errorf("Error deleting image %s: %s", name, err)
 		}
 		return nil, nil
@@ -1143,14 +1224,32 @@
 }
 
 func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
-	if container := srv.runtime.Get(name); container != nil {
-		if err := container.Start(hostConfig); err != nil {
-			return fmt.Errorf("Error starting container %s: %s", name, err)
-		}
-		srv.LogEvent("start", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
-	} else {
+	runtime := srv.runtime
+	container := runtime.Get(name)
+	if container == nil {
 		return fmt.Errorf("No such container: %s", name)
 	}
+
+	// Register links
+	if hostConfig != nil && hostConfig.Links != nil {
+		for _, l := range hostConfig.Links {
+			parts, err := parseLink(l)
+			if err != nil {
+				return err
+			}
+
+			childName := parts["name"]
+			if err := runtime.Link(fmt.Sprintf("/%s", container.ID), childName, parts["alias"]); err != nil {
+				return err
+			}
+		}
+	}
+
+	if err := container.Start(hostConfig); err != nil {
+		return fmt.Errorf("Error starting container %s: %s", name, err)
+	}
+	srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image))
+
 	return nil
 }
 
@@ -1302,35 +1401,30 @@
 
 }
 
-func NewServer(flGraphPath string, autoRestart, enableCors bool, dns ListOpts) (*Server, error) {
+func NewServer(config *DaemonConfig) (*Server, error) {
 	if runtime.GOARCH != "amd64" {
 		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
 	}
-	runtime, err := NewRuntime(flGraphPath, autoRestart, dns)
+	runtime, err := NewRuntime(config)
 	if err != nil {
 		return nil, err
 	}
-	srv := &Server{
+	runtime.srv = &Server{
 		runtime:     runtime,
-		enableCors:  enableCors,
 		pullingPool: make(map[string]struct{}),
 		pushingPool: make(map[string]struct{}),
 		events:      make([]utils.JSONMessage, 0, 64), //only keeps the 64 last events
 		listeners:   make(map[string]chan utils.JSONMessage),
 		reqFactory:  nil,
 	}
-	runtime.srv = srv
-	return srv, nil
+	return runtime.srv, nil
 }
 
 func (srv *Server) HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFactory {
 	if srv.reqFactory == nil {
-		ud := utils.NewHTTPUserAgentDecorator(srv.versionInfos()...)
-		md := &utils.HTTPMetaHeadersDecorator{
-			Headers: metaHeaders,
-		}
-		factory := utils.NewHTTPRequestFactory(ud, md)
-		srv.reqFactory = factory
+		srv.reqFactory = utils.NewHTTPRequestFactory(
+			utils.NewHTTPUserAgentDecorator(srv.versionInfos()...),
+			&utils.HTTPMetaHeadersDecorator{Headers: metaHeaders})
 	}
 	return srv.reqFactory
 }
@@ -1350,7 +1444,6 @@
 type Server struct {
 	sync.Mutex
 	runtime     *Runtime
-	enableCors  bool
 	pullingPool map[string]struct{}
 	pushingPool map[string]struct{}
 	events      []utils.JSONMessage
diff --git a/server_test.go b/server_test.go
index c13754b..4639177 100644
--- a/server_test.go
+++ b/server_test.go
@@ -89,7 +89,7 @@
 		t.Fatal(err)
 	}
 
-	id, err := srv.ContainerCreate(config)
+	id, _, err := srv.ContainerCreate(config)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -98,7 +98,7 @@
 		t.Errorf("Expected 1 container, %v found", len(runtime.List()))
 	}
 
-	if err = srv.ContainerDestroy(id, true); err != nil {
+	if err = srv.ContainerDestroy(id, true, false); err != nil {
 		t.Fatal(err)
 	}
 
@@ -119,7 +119,7 @@
 		t.Fatal(err)
 	}
 
-	id, err := srv.ContainerCreate(config)
+	id, _, err := srv.ContainerCreate(config)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -138,7 +138,7 @@
 		t.Fatal(err)
 	}
 
-	if err = srv.ContainerDestroy(id, true); err != nil {
+	if err = srv.ContainerDestroy(id, true, false); err != nil {
 		t.Fatal(err)
 	}
 
@@ -158,7 +158,7 @@
 		t.Fatal(err)
 	}
 
-	id, err := srv.ContainerCreate(config)
+	id, _, err := srv.ContainerCreate(config)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -179,7 +179,7 @@
 		t.Fatal(err)
 	}
 
-	id, err := srv.ContainerCreate(config)
+	id, _, err := srv.ContainerCreate(config)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -214,7 +214,7 @@
 	}
 
 	// FIXME: this failed once with a race condition ("Unable to remove filesystem for xxx: directory not empty")
-	if err = srv.ContainerDestroy(id, true); err != nil {
+	if err = srv.ContainerDestroy(id, true, false); err != nil {
 		t.Fatal(err)
 	}
 
@@ -230,7 +230,7 @@
 	srv := &Server{runtime: runtime}
 	defer nuke(runtime)
 	// Try to create a container with a memory limit of 1 byte less than the minimum allowed limit.
-	_, err = srv.ContainerCreate(
+	_, _, err = srv.ContainerCreate(
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Memory:    524287,
@@ -402,7 +402,7 @@
 		t.Fatal(err)
 	}
 
-	containerID, err := srv.ContainerCreate(config)
+	containerID, _, err := srv.ContainerCreate(config)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -423,7 +423,7 @@
 		t.Fatal(err)
 	}
 
-	containerID, err = srv.ContainerCreate(config)
+	containerID, _, err = srv.ContainerCreate(config)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/sorter.go b/sorter.go
index a818841..ad931eb 100644
--- a/sorter.go
+++ b/sorter.go
@@ -34,3 +34,72 @@
 
 	sort.Sort(sorter)
 }
+
+type portSorter struct {
+	ports []Port
+	by    func(i, j Port) bool
+}
+
+func (s *portSorter) Len() int {
+	return len(s.ports)
+}
+
+func (s *portSorter) Swap(i, j int) {
+	s.ports[i], s.ports[j] = s.ports[j], s.ports[i]
+}
+
+func (s *portSorter) Less(i, j int) bool {
+	ip := s.ports[i]
+	jp := s.ports[j]
+
+	return s.by(ip, jp)
+}
+
+func sortPorts(ports []Port, predicate func(i, j Port) bool) {
+	s := &portSorter{ports, predicate}
+	sort.Sort(s)
+}
+
+type containerSorter struct {
+	containers []*Container
+	by         func(i, j *Container) bool
+}
+
+func (s *containerSorter) Len() int {
+	return len(s.containers)
+}
+
+func (s *containerSorter) Swap(i, j int) {
+	s.containers[i], s.containers[j] = s.containers[j], s.containers[i]
+}
+
+func (s *containerSorter) Less(i, j int) bool {
+	return s.by(s.containers[i], s.containers[j])
+}
+
+func sortContainers(containers []*Container, predicate func(i, j *Container) bool) {
+	s := &containerSorter{containers, predicate}
+	sort.Sort(s)
+}
+
+type apiLinkSorter struct {
+	links []APILink
+	by    func(i, j APILink) bool
+}
+
+func (s *apiLinkSorter) Len() int {
+	return len(s.links)
+}
+
+func (s *apiLinkSorter) Swap(i, j int) {
+	s.links[i], s.links[j] = s.links[j], s.links[i]
+}
+
+func (s *apiLinkSorter) Less(i, j int) bool {
+	return s.by(s.links[i], s.links[j])
+}
+
+func sortLinks(links []APILink, predicate func(i, j APILink) bool) {
+	s := &apiLinkSorter{links, predicate}
+	sort.Sort(s)
+}
diff --git a/sorter_test.go b/sorter_test.go
index 5519708..d61b1a7 100644
--- a/sorter_test.go
+++ b/sorter_test.go
@@ -1,6 +1,7 @@
 package docker
 
 import (
+	"fmt"
 	"testing"
 )
 
@@ -55,3 +56,38 @@
 		t.Error("Expected []APIImges to be ordered by most recent creation date and tag name.")
 	}
 }
+
+func TestSortUniquePorts(t *testing.T) {
+	ports := []Port{
+		Port("6379/tcp"),
+		Port("22/tcp"),
+	}
+
+	sortPorts(ports, func(ip, jp Port) bool {
+		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
+	})
+
+	first := ports[0]
+	if fmt.Sprint(first) != "22/tcp" {
+		t.Log(fmt.Sprint(first))
+		t.Fail()
+	}
+}
+
+func TestSortSamePortWithDifferentProto(t *testing.T) {
+	ports := []Port{
+		Port("8888/tcp"),
+		Port("8888/udp"),
+		Port("6379/tcp"),
+		Port("6379/udp"),
+	}
+
+	sortPorts(ports, func(ip, jp Port) bool {
+		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
+	})
+
+	first := ports[0]
+	if fmt.Sprint(first) != "6379/tcp" {
+		t.Fail()
+	}
+}
diff --git a/state.go b/state.go
index 484032b..af7de9f 100644
--- a/state.go
+++ b/state.go
@@ -9,12 +9,12 @@
 
 type State struct {
 	sync.Mutex
-	Running   bool
-	Pid       int
-	ExitCode  int
-	StartedAt time.Time
+	Running    bool
+	Pid        int
+	ExitCode   int
+	StartedAt  time.Time
 	FinishedAt time.Time
-	Ghost     bool
+	Ghost      bool
 }
 
 // String returns a human-readable description of the state
diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go
index 2e80327..929a3f6 100644
--- a/sysinit/sysinit.go
+++ b/sysinit/sysinit.go
@@ -3,8 +3,10 @@
 import (
 	"flag"
 	"fmt"
+	"github.com/dotcloud/docker/netlink"
 	"github.com/dotcloud/docker/utils"
 	"log"
+	"net"
 	"os"
 	"os/exec"
 	"strconv"
@@ -17,7 +19,14 @@
 	if gw == "" {
 		return
 	}
-	if _, err := ip("route", "add", "default", "via", gw); err != nil {
+
+	ip := net.ParseIP(gw)
+	if ip == nil {
+		log.Fatalf("Unable to set up networking, %s is not a valid IP", gw)
+		return
+	}
+
+	if err := netlink.AddDefaultGw(ip); err != nil {
 		log.Fatalf("Unable to set up networking: %v", err)
 	}
 }
@@ -58,7 +67,7 @@
 }
 
 // Clear environment pollution introduced by lxc-start
-func cleanupEnv(env ListOpts) {
+func cleanupEnv(env utils.ListOpts) {
 	os.Clearenv()
 	for _, kv := range env {
 		parts := strings.SplitN(kv, "=", 2)
@@ -93,7 +102,7 @@
 	var gw = flag.String("g", "", "gateway address")
 	var workdir = flag.String("w", "", "workdir")
 
-	var flEnv ListOpts
+	var flEnv utils.ListOpts
 	flag.Var(&flEnv, "e", "Set environment variables")
 
 	flag.Parse()
diff --git a/utils.go b/utils.go
index 8d821e8..8c5ace2 100644
--- a/utils.go
+++ b/utils.go
@@ -1,8 +1,35 @@
 package docker
 
+/*
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <errno.h>
+
+// See linux.git/fs/btrfs/ioctl.h
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
+
+int
+btrfs_reflink(int fd_out, int fd_in)
+{
+  int res;
+  res = ioctl(fd_out, BTRFS_IOC_CLONE, fd_in);
+  if (res < 0)
+    return errno;
+  return 0;
+}
+
+*/
+import "C"
 import (
 	"fmt"
+	"github.com/dotcloud/docker/utils"
+	"io"
+	"io/ioutil"
+	"os"
+	"strconv"
 	"strings"
+	"syscall"
 )
 
 // Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
@@ -27,6 +54,7 @@
 		len(a.Dns) != len(b.Dns) ||
 		len(a.Env) != len(b.Env) ||
 		len(a.PortSpecs) != len(b.PortSpecs) ||
+		len(a.ExposedPorts) != len(b.ExposedPorts) ||
 		len(a.Entrypoint) != len(b.Entrypoint) ||
 		len(a.Volumes) != len(b.Volumes) {
 		return false
@@ -52,6 +80,11 @@
 			return false
 		}
 	}
+	for k := range a.ExposedPorts {
+		if _, exists := b.ExposedPorts[k]; !exists {
+			return false
+		}
+	}
 	for i := 0; i < len(a.Entrypoint); i++ {
 		if a.Entrypoint[i] != b.Entrypoint[i] {
 			return false
@@ -78,26 +111,38 @@
 	if userConf.CpuShares == 0 {
 		userConf.CpuShares = imageConf.CpuShares
 	}
-	if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 {
-		userConf.PortSpecs = imageConf.PortSpecs
-	} else {
-		for _, imagePortSpec := range imageConf.PortSpecs {
-			found := false
-			imageNat, err := parseNat(imagePortSpec)
-			if err != nil {
-				return err
+	if userConf.ExposedPorts == nil || len(userConf.ExposedPorts) == 0 {
+		userConf.ExposedPorts = imageConf.ExposedPorts
+	}
+
+	if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
+		if userConf.ExposedPorts == nil {
+			userConf.ExposedPorts = make(map[Port]struct{})
+		}
+		ports, _, err := parsePortSpecs(userConf.PortSpecs)
+		if err != nil {
+			return err
+		}
+		for port := range ports {
+			if _, exists := userConf.ExposedPorts[port]; !exists {
+				userConf.ExposedPorts[port] = struct{}{}
 			}
-			for _, userPortSpec := range userConf.PortSpecs {
-				userNat, err := parseNat(userPortSpec)
-				if err != nil {
-					return err
-				}
-				if imageNat.Proto == userNat.Proto && imageNat.Backend == userNat.Backend {
-					found = true
-				}
-			}
-			if !found {
-				userConf.PortSpecs = append(userConf.PortSpecs, imagePortSpec)
+		}
+		userConf.PortSpecs = nil
+	}
+	if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
+		utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
+		if userConf.ExposedPorts == nil {
+			userConf.ExposedPorts = make(map[Port]struct{})
+		}
+
+		ports, _, err := parsePortSpecs(imageConf.PortSpecs)
+		if err != nil {
+			return err
+		}
+		for port := range ports {
+			if _, exists := userConf.ExposedPorts[port]; !exists {
+				userConf.ExposedPorts[port] = struct{}{}
 			}
 		}
 	}
@@ -155,7 +200,7 @@
 	return nil
 }
 
-func parseLxcConfOpts(opts ListOpts) ([]KeyValuePair, error) {
+func parseLxcConfOpts(opts utils.ListOpts) ([]KeyValuePair, error) {
 	out := make([]KeyValuePair, len(opts))
 	for i, o := range opts {
 		k, v, err := parseLxcOpt(o)
@@ -174,3 +219,131 @@
 	}
 	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
 }
+
+func RootIsShared() bool {
+	if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
+		for _, line := range strings.Split(string(data), "\n") {
+			cols := strings.Split(line, " ")
+			if len(cols) >= 6 && cols[4] == "/" {
+				return strings.HasPrefix(cols[6], "shared")
+			}
+		}
+	}
+
+	// No idea, probably safe to assume so
+	return true
+}
+
+func BtrfsReflink(fd_out, fd_in uintptr) error {
+	res := C.btrfs_reflink(C.int(fd_out), C.int(fd_in))
+	if res != 0 {
+		return syscall.Errno(res)
+	}
+	return nil
+}
+
+func CopyFile(dstFile, srcFile *os.File) error {
+	err := BtrfsReflink(dstFile.Fd(), srcFile.Fd())
+	if err == nil {
+		return nil
+	}
+
+	// Fall back to normal copy
+	_, err = io.Copy(dstFile, srcFile)
+	return err
+}
+
+// We will receive port specs in the format of ip:public:private/proto and these need to be
+// parsed in the internal types
+func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
+	exposedPorts := make(map[Port]struct{}, len(ports))
+	bindings := make(map[Port][]PortBinding)
+
+	for _, rawPort := range ports {
+		proto := "tcp"
+		if i := strings.LastIndex(rawPort, "/"); i != -1 {
+			proto = rawPort[i+1:]
+			rawPort = rawPort[:i]
+		}
+		if !strings.Contains(rawPort, ":") {
+			rawPort = fmt.Sprintf("::%s", rawPort)
+		} else if len(strings.Split(rawPort, ":")) == 2 {
+			rawPort = fmt.Sprintf(":%s", rawPort)
+		}
+
+		parts, err := utils.PartParser("ip:hostPort:containerPort", rawPort)
+		if err != nil {
+			return nil, nil, err
+		}
+		containerPort := parts["containerPort"]
+		rawIp := parts["ip"]
+		hostPort := parts["hostPort"]
+
+		if containerPort == "" {
+			return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
+		}
+
+		port := NewPort(proto, containerPort)
+		if _, exists := exposedPorts[port]; !exists {
+			exposedPorts[port] = struct{}{}
+		}
+
+		binding := PortBinding{
+			HostIp:   rawIp,
+			HostPort: hostPort,
+		}
+		bslice, exists := bindings[port]
+		if !exists {
+			bslice = []PortBinding{}
+		}
+		bindings[port] = append(bslice, binding)
+	}
+	return exposedPorts, bindings, nil
+}
+
+// Splits a port in the format of port/proto
+func splitProtoPort(rawPort string) (string, string) {
+	parts := strings.Split(rawPort, "/")
+	l := len(parts)
+	if l == 0 {
+		return "", ""
+	}
+	if l == 1 {
+		return "tcp", rawPort
+	}
+	return parts[0], parts[1]
+}
+
+func parsePort(rawPort string) (int, error) {
+	port, err := strconv.ParseUint(rawPort, 10, 16)
+	if err != nil {
+		return 0, err
+	}
+	return int(port), nil
+}
+
+func migratePortMappings(config *Config) error {
+	if config.PortSpecs != nil {
+		// We don't have to worry about migrating the bindings to the host
+		// This is our breaking change
+		ports, _, err := parsePortSpecs(config.PortSpecs)
+		if err != nil {
+			return err
+		}
+		config.PortSpecs = nil
+
+		if config.ExposedPorts == nil {
+			config.ExposedPorts = make(map[Port]struct{}, len(ports))
+		}
+		for k, v := range ports {
+			config.ExposedPorts[k] = v
+		}
+	}
+	return nil
+}
+
+// Links come in the format of
+// name:alias
+func parseLink(rawLink string) (map[string]string, error) {
+	return utils.PartParser("name:alias", rawLink)
+}
diff --git a/utils/utils.go b/utils/utils.go
index b0327dd..6658b83 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -22,6 +22,18 @@
 	"time"
 )
 
+// ListOpts type
+type ListOpts []string
+
+func (opts *ListOpts) String() string {
+	return fmt.Sprint(*opts)
+}
+
+func (opts *ListOpts) Set(value string) error {
+	*opts = append(*opts, value)
+	return nil
+}
+
 // Go is a basic promise implementation: it wraps calls a function in a goroutine,
 // and returns a channel which will later return the function's return value.
 func Go(f func() error) chan error {
@@ -1029,6 +1041,41 @@
 	return fmt.Sprintf("Status: %d", e.Status)
 }
 
+func quote(word string, buf *bytes.Buffer) {
+	// Bail out early for "simple" strings
+	if word != "" && !strings.ContainsAny(word, "\\'\"`${[|&;<>()~*?! \t\n") {
+		buf.WriteString(word)
+		return
+	}
+
+	buf.WriteString("'")
+
+	for i := 0; i < len(word); i++ {
+		b := word[i]
+		if b == '\'' {
+			// Replace literal ' with a close ', a \', and a open '
+			buf.WriteString("'\\''")
+		} else {
+			buf.WriteByte(b)
+		}
+	}
+
+	buf.WriteString("'")
+}
+
+// Take a list of strings and escape them so they will be handled right
+// when passed as arguments to an program via a shell
+func ShellQuoteArguments(args []string) string {
+	var buf bytes.Buffer
+	for i, arg := range args {
+		if i != 0 {
+			buf.WriteByte(' ')
+		}
+		quote(arg, &buf)
+	}
+	return buf.String()
+}
+
 func IsClosedError(err error) bool {
 	/* This comparison is ugly, but unfortunately, net.go doesn't export errClosing.
 	 * See:
@@ -1038,3 +1085,22 @@
 	 */
 	return strings.HasSuffix(err.Error(), "use of closed network connection")
 }
+
+func PartParser(template, data string) (map[string]string, error) {
+	// ip:public:private
+	templateParts := strings.Split(template, ":")
+	parts := strings.Split(data, ":")
+	if len(parts) != len(templateParts) {
+		return nil, fmt.Errorf("Invalid format to parse.  %s should match template %s", data, template)
+	}
+	out := make(map[string]string, len(templateParts))
+
+	for i, t := range templateParts {
+		value := ""
+		if len(parts) > i {
+			value = parts[i]
+		}
+		out[t] = value
+	}
+	return out, nil
+}
diff --git a/utils/utils_test.go b/utils/utils_test.go
index 9a55e7f..a5fef4e 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -421,3 +421,23 @@
 		t.Fatalf("Expected [d], found %v instead", res[2])
 	}
 }
+
+func TestParsePortMapping(t *testing.T) {
+	data, err := PartParser("ip:public:private", "192.168.1.1:80:8080")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(data) != 3 {
+		t.FailNow()
+	}
+	if data["ip"] != "192.168.1.1" {
+		t.Fail()
+	}
+	if data["public"] != "80" {
+		t.Fail()
+	}
+	if data["private"] != "8080" {
+		t.Fail()
+	}
+}
diff --git a/utils_test.go b/utils_test.go
index 3f99de5..d90014b 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -1,15 +1,15 @@
 package docker
 
 import (
-	"github.com/dotcloud/docker/utils"
 	"fmt"
+	"github.com/dotcloud/docker/utils"
 	"io"
 	"io/ioutil"
 	"os"
 	"path"
+	"runtime"
 	"strings"
 	"testing"
-	"runtime"
 )
 
 // This file contains utility functions for docker's unit test suite.
@@ -26,7 +26,7 @@
 	pc, _, _, _ := runtime.Caller(1)
 	callerLongName := runtime.FuncForPC(pc).Name()
 	parts := strings.Split(callerLongName, ".")
-	callerShortName := parts[len(parts) - 1]
+	callerShortName := parts[len(parts)-1]
 	if globalTestID == "" {
 		globalTestID = GenerateID()[:4]
 	}
@@ -62,11 +62,17 @@
 	if err := os.Remove(root); err != nil {
 		return nil, err
 	}
+	utils.Debugf("Copying %s to %s", unitTestStoreBase, root)
 	if err := utils.CopyDirectory(unitTestStoreBase, root); err != nil {
+		utils.Debugf("ERROR: Copying %s to %s returned %s", unitTestStoreBase, root, err)
 		return nil, err
 	}
 
-	runtime, err = NewRuntimeFromDirectory(root, false)
+	config := &DaemonConfig{
+		GraphPath:   root,
+		AutoRestart: false,
+	}
+	runtime, err = NewRuntimeFromDirectory(config)
 	if err != nil {
 		return nil, err
 	}
@@ -125,7 +131,7 @@
 	if config.Image == "_" {
 		config.Image = GetTestImage(r).ID
 	}
-	c, err := r.Create(config)
+	c, _, err := r.Create(config)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -253,12 +259,12 @@
 		}
 	}
 
-	if len(configUser.PortSpecs) != 3 {
-		t.Fatalf("Expected 3 portSpecs, 1111:1111, 3333:2222 and 3333:3333, found %d", len(configUser.PortSpecs))
+	if len(configUser.ExposedPorts) != 3 {
+		t.Fatalf("Expected 3 portSpecs, 1111, 2222 and 3333, found %d", len(configUser.PortSpecs))
 	}
-	for _, portSpecs := range configUser.PortSpecs {
-		if portSpecs != "1111:1111" && portSpecs != "3333:2222" && portSpecs != "3333:3333" {
-			t.Fatalf("Expected 1111:1111 or 3333:2222 or 3333:3333, found %s", portSpecs)
+	for portSpecs := range configUser.ExposedPorts {
+		if portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
+			t.Fatalf("Expected 1111 or 2222 or 3333, found %s", portSpecs)
 		}
 	}
 	if len(configUser.Env) != 3 {
@@ -284,48 +290,6 @@
 	}
 }
 
-func TestMergeConfigPublicPortNotHonored(t *testing.T) {
-	volumesImage := make(map[string]struct{})
-	volumesImage["/test1"] = struct{}{}
-	volumesImage["/test2"] = struct{}{}
-	configImage := &Config{
-		Dns:       []string{"1.1.1.1", "2.2.2.2"},
-		PortSpecs: []string{"1111", "2222"},
-		Env:       []string{"VAR1=1", "VAR2=2"},
-		Volumes:   volumesImage,
-	}
-
-	volumesUser := make(map[string]struct{})
-	volumesUser["/test3"] = struct{}{}
-	configUser := &Config{
-		Dns:       []string{"3.3.3.3"},
-		PortSpecs: []string{"1111:3333"},
-		Env:       []string{"VAR2=3", "VAR3=3"},
-		Volumes:   volumesUser,
-	}
-
-	MergeConfig(configUser, configImage)
-
-	contains := func(a []string, expect string) bool {
-		for _, p := range a {
-			if p == expect {
-				return true
-			}
-		}
-		return false
-	}
-
-	if !contains(configUser.PortSpecs, "2222") {
-		t.Logf("Expected '2222' Ports: %v", configUser.PortSpecs)
-		t.Fail()
-	}
-
-	if !contains(configUser.PortSpecs, "1111:3333") {
-		t.Logf("Expected '1111:3333' Ports: %v", configUser.PortSpecs)
-		t.Fail()
-	}
-}
-
 func TestParseLxcConfOpt(t *testing.T) {
 	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
 
@@ -342,3 +306,129 @@
 		}
 	}
 }
+
+func TestParseNetworkOptsPrivateOnly(t *testing.T) {
+	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::80"})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(ports) != 1 {
+		t.Logf("Expected 1 got %d", len(ports))
+		t.FailNow()
+	}
+	if len(bindings) != 1 {
+		t.Logf("Expected 1 got %d", len(bindings))
+		t.FailNow()
+	}
+	for k := range ports {
+		if k.Proto() != "tcp" {
+			t.Logf("Expected tcp got %s", k.Proto())
+			t.Fail()
+		}
+		if k.Port() != "80" {
+			t.Logf("Expected 80 got %s", k.Port())
+			t.Fail()
+		}
+		b, exists := bindings[k]
+		if !exists {
+			t.Log("Binding does not exist")
+			t.FailNow()
+		}
+		if len(b) != 1 {
+			t.Logf("Expected 1 got %d", len(b))
+			t.FailNow()
+		}
+		s := b[0]
+		if s.HostPort != "" {
+			t.Logf("Expected \"\" got %s", s.HostPort)
+			t.Fail()
+		}
+		if s.HostIp != "192.168.1.100" {
+			t.Fail()
+		}
+	}
+}
+
+func TestParseNetworkOptsPublic(t *testing.T) {
+	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100:8080:80"})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(ports) != 1 {
+		t.Logf("Expected 1 got %d", len(ports))
+		t.FailNow()
+	}
+	if len(bindings) != 1 {
+		t.Logf("Expected 1 got %d", len(bindings))
+		t.FailNow()
+	}
+	for k := range ports {
+		if k.Proto() != "tcp" {
+			t.Logf("Expected tcp got %s", k.Proto())
+			t.Fail()
+		}
+		if k.Port() != "80" {
+			t.Logf("Expected 80 got %s", k.Port())
+			t.Fail()
+		}
+		b, exists := bindings[k]
+		if !exists {
+			t.Log("Binding does not exist")
+			t.FailNow()
+		}
+		if len(b) != 1 {
+			t.Logf("Expected 1 got %d", len(b))
+			t.FailNow()
+		}
+		s := b[0]
+		if s.HostPort != "8080" {
+			t.Logf("Expected 8080 got %s", s.HostPort)
+			t.Fail()
+		}
+		if s.HostIp != "192.168.1.100" {
+			t.Fail()
+		}
+	}
+}
+
+func TestParseNetworkOptsUdp(t *testing.T) {
+	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::6000/udp"})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(ports) != 1 {
+		t.Logf("Expected 1 got %d", len(ports))
+		t.FailNow()
+	}
+	if len(bindings) != 1 {
+		t.Logf("Expected 1 got %d", len(bindings))
+		t.FailNow()
+	}
+	for k := range ports {
+		if k.Proto() != "udp" {
+			t.Logf("Expected udp got %s", k.Proto())
+			t.Fail()
+		}
+		if k.Port() != "6000" {
+			t.Logf("Expected 6000 got %s", k.Port())
+			t.Fail()
+		}
+		b, exists := bindings[k]
+		if !exists {
+			t.Log("Binding does not exist")
+			t.FailNow()
+		}
+		if len(b) != 1 {
+			t.Logf("Expected 1 got %d", len(b))
+			t.FailNow()
+		}
+		s := b[0]
+		if s.HostPort != "" {
+			t.Logf("Expected \"\" got %s", s.HostPort)
+			t.Fail()
+		}
+		if s.HostIp != "192.168.1.100" {
+			t.Fail()
+		}
+	}
+}
diff --git a/vendor/src/code.google.com/p/gosqlite/sqlite/sqlite.go b/vendor/src/code.google.com/p/gosqlite/sqlite/sqlite.go
new file mode 100644
index 0000000..d2fbb62
--- /dev/null
+++ b/vendor/src/code.google.com/p/gosqlite/sqlite/sqlite.go
@@ -0,0 +1,404 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sqlite provides access to the SQLite library, version 3.
+package sqlite
+
+/*
+#cgo LDFLAGS: -lsqlite3
+
+#include <sqlite3.h>
+#include <stdlib.h>
+
+// These wrappers are necessary because SQLITE_TRANSIENT
+// is a pointer constant, and cgo doesn't translate them correctly.
+// The definition in sqlite3.h is:
+//
+// typedef void (*sqlite3_destructor_type)(void*);
+// #define SQLITE_STATIC      ((sqlite3_destructor_type)0)
+// #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)
+
+static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
+	return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
+}
+static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
+	return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
+}
+
+*/
+import "C"
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"strconv"
+	"time"
+	"unsafe"
+)
+
+type Errno int
+
+func (e Errno) Error() string {
+	s := errText[e]
+	if s == "" {
+		return fmt.Sprintf("errno %d", int(e))
+	}
+	return s
+}
+
+var (
+	ErrError      error = Errno(1)   //    /* SQL error or missing database */
+	ErrInternal   error = Errno(2)   //    /* Internal logic error in SQLite */
+	ErrPerm       error = Errno(3)   //    /* Access permission denied */
+	ErrAbort      error = Errno(4)   //    /* Callback routine requested an abort */
+	ErrBusy       error = Errno(5)   //    /* The database file is locked */
+	ErrLocked     error = Errno(6)   //    /* A table in the database is locked */
+	ErrNoMem      error = Errno(7)   //    /* A malloc() failed */
+	ErrReadOnly   error = Errno(8)   //    /* Attempt to write a readonly database */
+	ErrInterrupt  error = Errno(9)   //    /* Operation terminated by sqlite3_interrupt()*/
+	ErrIOErr      error = Errno(10)  //    /* Some kind of disk I/O error occurred */
+	ErrCorrupt    error = Errno(11)  //    /* The database disk image is malformed */
+	ErrFull       error = Errno(13)  //    /* Insertion failed because database is full */
+	ErrCantOpen   error = Errno(14)  //    /* Unable to open the database file */
+	ErrEmpty      error = Errno(16)  //    /* Database is empty */
+	ErrSchema     error = Errno(17)  //    /* The database schema changed */
+	ErrTooBig     error = Errno(18)  //    /* String or BLOB exceeds size limit */
+	ErrConstraint error = Errno(19)  //    /* Abort due to constraint violation */
+	ErrMismatch   error = Errno(20)  //    /* Data type mismatch */
+	ErrMisuse     error = Errno(21)  //    /* Library used incorrectly */
+	ErrNolfs      error = Errno(22)  //    /* Uses OS features not supported on host */
+	ErrAuth       error = Errno(23)  //    /* Authorization denied */
+	ErrFormat     error = Errno(24)  //    /* Auxiliary database format error */
+	ErrRange      error = Errno(25)  //    /* 2nd parameter to sqlite3_bind out of range */
+	ErrNotDB      error = Errno(26)  //    /* File opened that is not a database file */
+	Row                 = Errno(100) //   /* sqlite3_step() has another row ready */
+	Done                = Errno(101) //   /* sqlite3_step() has finished executing */
+)
+
+var errText = map[Errno]string{
+	1:   "SQL error or missing database",
+	2:   "Internal logic error in SQLite",
+	3:   "Access permission denied",
+	4:   "Callback routine requested an abort",
+	5:   "The database file is locked",
+	6:   "A table in the database is locked",
+	7:   "A malloc() failed",
+	8:   "Attempt to write a readonly database",
+	9:   "Operation terminated by sqlite3_interrupt()*/",
+	10:  "Some kind of disk I/O error occurred",
+	11:  "The database disk image is malformed",
+	12:  "NOT USED. Table or record not found",
+	13:  "Insertion failed because database is full",
+	14:  "Unable to open the database file",
+	15:  "NOT USED. Database lock protocol error",
+	16:  "Database is empty",
+	17:  "The database schema changed",
+	18:  "String or BLOB exceeds size limit",
+	19:  "Abort due to constraint violation",
+	20:  "Data type mismatch",
+	21:  "Library used incorrectly",
+	22:  "Uses OS features not supported on host",
+	23:  "Authorization denied",
+	24:  "Auxiliary database format error",
+	25:  "2nd parameter to sqlite3_bind out of range",
+	26:  "File opened that is not a database file",
+	100: "sqlite3_step() has another row ready",
+	101: "sqlite3_step() has finished executing",
+}
+
+func (c *Conn) error(rv C.int) error {
+	if c == nil || c.db == nil {
+		return errors.New("nil sqlite database")
+	}
+	if rv == 0 {
+		return nil
+	}
+	if rv == 21 { // misuse
+		return Errno(rv)
+	}
+	return errors.New(Errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
+}
+
+type Conn struct {
+	db *C.sqlite3
+}
+
+func Version() string {
+	p := C.sqlite3_libversion()
+	return C.GoString(p)
+}
+
+func Open(filename string) (*Conn, error) {
+	if C.sqlite3_threadsafe() == 0 {
+		return nil, errors.New("sqlite library was not compiled for thread-safe operation")
+	}
+
+	var db *C.sqlite3
+	name := C.CString(filename)
+	defer C.free(unsafe.Pointer(name))
+	rv := C.sqlite3_open_v2(name, &db,
+		C.SQLITE_OPEN_FULLMUTEX|
+			C.SQLITE_OPEN_READWRITE|
+			C.SQLITE_OPEN_CREATE,
+		nil)
+	if rv != 0 {
+		return nil, Errno(rv)
+	}
+	if db == nil {
+		return nil, errors.New("sqlite succeeded without returning a database")
+	}
+	return &Conn{db}, nil
+}
+
+func NewBackup(dst *Conn, dstTable string, src *Conn, srcTable string) (*Backup, error) {
+	dname := C.CString(dstTable)
+	sname := C.CString(srcTable)
+	defer C.free(unsafe.Pointer(dname))
+	defer C.free(unsafe.Pointer(sname))
+
+	sb := C.sqlite3_backup_init(dst.db, dname, src.db, sname)
+	if sb == nil {
+		return nil, dst.error(C.sqlite3_errcode(dst.db))
+	}
+	return &Backup{sb, dst, src}, nil
+}
+
+type Backup struct {
+	sb       *C.sqlite3_backup
+	dst, src *Conn
+}
+
+func (b *Backup) Step(npage int) error {
+	rv := C.sqlite3_backup_step(b.sb, C.int(npage))
+	if rv == 0 || Errno(rv) == ErrBusy || Errno(rv) == ErrLocked {
+		return nil
+	}
+	return Errno(rv)
+}
+
+type BackupStatus struct {
+	Remaining int
+	PageCount int
+}
+
+func (b *Backup) Status() BackupStatus {
+	return BackupStatus{int(C.sqlite3_backup_remaining(b.sb)), int(C.sqlite3_backup_pagecount(b.sb))}
+}
+
+func (b *Backup) Run(npage int, period time.Duration, c chan<- BackupStatus) error {
+	var err error
+	for {
+		err = b.Step(npage)
+		if err != nil {
+			break
+		}
+		if c != nil {
+			c <- b.Status()
+		}
+		time.Sleep(period)
+	}
+	return b.dst.error(C.sqlite3_errcode(b.dst.db))
+}
+
+func (b *Backup) Close() error {
+	if b.sb == nil {
+		return errors.New("backup already closed")
+	}
+	C.sqlite3_backup_finish(b.sb)
+	b.sb = nil
+	return nil
+}
+
+func (c *Conn) BusyTimeout(ms int) error {
+	rv := C.sqlite3_busy_timeout(c.db, C.int(ms))
+	if rv == 0 {
+		return nil
+	}
+	return Errno(rv)
+}
+
+func (c *Conn) Exec(cmd string, args ...interface{}) error {
+	s, err := c.Prepare(cmd)
+	if err != nil {
+		return err
+	}
+	defer s.Finalize()
+	err = s.Exec(args...)
+	if err != nil {
+		return err
+	}
+	rv := C.sqlite3_step(s.stmt)
+	if Errno(rv) != Done {
+		return c.error(rv)
+	}
+	return nil
+}
+
+type Stmt struct {
+	c    *Conn
+	stmt *C.sqlite3_stmt
+	err  error
+	t0   time.Time
+	sql  string
+	args string
+}
+
+func (c *Conn) Prepare(cmd string) (*Stmt, error) {
+	if c == nil || c.db == nil {
+		return nil, errors.New("nil sqlite database")
+	}
+	cmdstr := C.CString(cmd)
+	defer C.free(unsafe.Pointer(cmdstr))
+	var stmt *C.sqlite3_stmt
+	var tail *C.char
+	rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &stmt, &tail)
+	if rv != 0 {
+		return nil, c.error(rv)
+	}
+	return &Stmt{c: c, stmt: stmt, sql: cmd, t0: time.Now()}, nil
+}
+
+func (s *Stmt) Exec(args ...interface{}) error {
+	s.args = fmt.Sprintf(" %v", []interface{}(args))
+	rv := C.sqlite3_reset(s.stmt)
+	if rv != 0 {
+		return s.c.error(rv)
+	}
+
+	n := int(C.sqlite3_bind_parameter_count(s.stmt))
+	if n != len(args) {
+		return errors.New(fmt.Sprintf("incorrect argument count for Stmt.Exec: have %d want %d", len(args), n))
+	}
+
+	for i, v := range args {
+		var str string
+		switch v := v.(type) {
+		case []byte:
+			var p *byte
+			if len(v) > 0 {
+				p = &v[0]
+			}
+			if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
+				return s.c.error(rv)
+			}
+			continue
+
+		case bool:
+			if v {
+				str = "1"
+			} else {
+				str = "0"
+			}
+
+		default:
+			str = fmt.Sprint(v)
+		}
+
+		cstr := C.CString(str)
+		rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
+		C.free(unsafe.Pointer(cstr))
+		if rv != 0 {
+			return s.c.error(rv)
+		}
+	}
+	return nil
+}
+
+func (s *Stmt) Error() error {
+	return s.err
+}
+
+func (s *Stmt) Next() bool {
+	rv := C.sqlite3_step(s.stmt)
+	err := Errno(rv)
+	if err == Row {
+		return true
+	}
+	if err != Done {
+		s.err = s.c.error(rv)
+	}
+	return false
+}
+
+func (s *Stmt) Reset() error {
+	C.sqlite3_reset(s.stmt)
+	return nil
+}
+
+func (s *Stmt) Scan(args ...interface{}) error {
+	n := int(C.sqlite3_column_count(s.stmt))
+	if n != len(args) {
+		return errors.New(fmt.Sprintf("incorrect argument count for Stmt.Scan: have %d want %d", len(args), n))
+	}
+
+	for i, v := range args {
+		n := C.sqlite3_column_bytes(s.stmt, C.int(i))
+		p := C.sqlite3_column_blob(s.stmt, C.int(i))
+		if p == nil && n > 0 {
+			return errors.New("got nil blob")
+		}
+		var data []byte
+		if n > 0 {
+			data = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
+		}
+		switch v := v.(type) {
+		case *[]byte:
+			*v = data
+		case *string:
+			*v = string(data)
+		case *bool:
+			*v = string(data) == "1"
+		case *int:
+			x, err := strconv.Atoi(string(data))
+			if err != nil {
+				return errors.New("arg " + strconv.Itoa(i) + " as int: " + err.Error())
+			}
+			*v = x
+		case *int64:
+			x, err := strconv.ParseInt(string(data), 10, 64)
+			if err != nil {
+				return errors.New("arg " + strconv.Itoa(i) + " as int64: " + err.Error())
+			}
+			*v = x
+		case *float64:
+			x, err := strconv.ParseFloat(string(data), 64)
+			if err != nil {
+				return errors.New("arg " + strconv.Itoa(i) + " as float64: " + err.Error())
+			}
+			*v = x
+		default:
+			return errors.New("unsupported type in Scan: " + reflect.TypeOf(v).String())
+		}
+	}
+	return nil
+}
+
+func (s *Stmt) SQL() string {
+	return s.sql + s.args
+}
+
+func (s *Stmt) Nanoseconds() int64 {
+	return time.Now().Sub(s.t0).Nanoseconds()
+}
+
+func (s *Stmt) Finalize() error {
+	rv := C.sqlite3_finalize(s.stmt)
+	if rv != 0 {
+		return s.c.error(rv)
+	}
+	return nil
+}
+
+func (c *Conn) Close() error {
+	if c == nil || c.db == nil {
+		return errors.New("nil sqlite database")
+	}
+	rv := C.sqlite3_close(c.db)
+	if rv != 0 {
+		return c.error(rv)
+	}
+	c.db = nil
+	return nil
+}
diff --git a/vendor/src/code.google.com/p/gosqlite/sqlite3/driver.go b/vendor/src/code.google.com/p/gosqlite/sqlite3/driver.go
new file mode 100644
index 0000000..982e08e
--- /dev/null
+++ b/vendor/src/code.google.com/p/gosqlite/sqlite3/driver.go
@@ -0,0 +1,498 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sqlite3 provides access to the SQLite library, version 3.
+//
+// The package has no exported API.
+// It registers a driver for the standard Go database/sql package.
+//
+//	import _ "code.google.com/p/gosqlite/sqlite3"
+//
+// (For an alternate, earlier API, see the code.google.com/p/gosqlite/sqlite package.)
+package sqlite
+
+/*
+#cgo LDFLAGS: -lsqlite3
+
+#include <sqlite3.h>
+#include <stdlib.h>
+
+// These wrappers are necessary because SQLITE_TRANSIENT
+// is a pointer constant, and cgo doesn't translate them correctly.
+// The definition in sqlite3.h is:
+//
+// typedef void (*sqlite3_destructor_type)(void*);
+// #define SQLITE_STATIC      ((sqlite3_destructor_type)0)
+// #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)
+
+static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
+	return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
+}
+static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
+	return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
+}
+
+*/
+import "C"
+
+import (
+	"database/sql"
+	"database/sql/driver"
+	"errors"
+	"fmt"
+	"io"
+	"strings"
+	"time"
+	"unsafe"
+)
+
+func init() {
+	sql.Register("sqlite3", impl{})
+}
+
+type errno int
+
+func (e errno) Error() string {
+	s := errText[e]
+	if s == "" {
+		return fmt.Sprintf("errno %d", int(e))
+	}
+	return s
+}
+
+var (
+	errError      error = errno(1)   //    /* SQL error or missing database */
+	errInternal   error = errno(2)   //    /* Internal logic error in SQLite */
+	errPerm       error = errno(3)   //    /* Access permission denied */
+	errAbort      error = errno(4)   //    /* Callback routine requested an abort */
+	errBusy       error = errno(5)   //    /* The database file is locked */
+	errLocked     error = errno(6)   //    /* A table in the database is locked */
+	errNoMem      error = errno(7)   //    /* A malloc() failed */
+	errReadOnly   error = errno(8)   //    /* Attempt to write a readonly database */
+	errInterrupt  error = errno(9)   //    /* Operation terminated by sqlite3_interrupt()*/
+	errIOErr      error = errno(10)  //    /* Some kind of disk I/O error occurred */
+	errCorrupt    error = errno(11)  //    /* The database disk image is malformed */
+	errFull       error = errno(13)  //    /* Insertion failed because database is full */
+	errCantOpen   error = errno(14)  //    /* Unable to open the database file */
+	errEmpty      error = errno(16)  //    /* Database is empty */
+	errSchema     error = errno(17)  //    /* The database schema changed */
+	errTooBig     error = errno(18)  //    /* String or BLOB exceeds size limit */
+	errConstraint error = errno(19)  //    /* Abort due to constraint violation */
+	errMismatch   error = errno(20)  //    /* Data type mismatch */
+	errMisuse     error = errno(21)  //    /* Library used incorrectly */
+	errNolfs      error = errno(22)  //    /* Uses OS features not supported on host */
+	errAuth       error = errno(23)  //    /* Authorization denied */
+	errFormat     error = errno(24)  //    /* Auxiliary database format error */
+	errRange      error = errno(25)  //    /* 2nd parameter to sqlite3_bind out of range */
+	errNotDB      error = errno(26)  //    /* File opened that is not a database file */
+	stepRow             = errno(100) //   /* sqlite3_step() has another row ready */
+	stepDone            = errno(101) //   /* sqlite3_step() has finished executing */
+)
+
+var errText = map[errno]string{
+	1:   "SQL error or missing database",
+	2:   "Internal logic error in SQLite",
+	3:   "Access permission denied",
+	4:   "Callback routine requested an abort",
+	5:   "The database file is locked",
+	6:   "A table in the database is locked",
+	7:   "A malloc() failed",
+	8:   "Attempt to write a readonly database",
+	9:   "Operation terminated by sqlite3_interrupt()*/",
+	10:  "Some kind of disk I/O error occurred",
+	11:  "The database disk image is malformed",
+	12:  "NOT USED. Table or record not found",
+	13:  "Insertion failed because database is full",
+	14:  "Unable to open the database file",
+	15:  "NOT USED. Database lock protocol error",
+	16:  "Database is empty",
+	17:  "The database schema changed",
+	18:  "String or BLOB exceeds size limit",
+	19:  "Abort due to constraint violation",
+	20:  "Data type mismatch",
+	21:  "Library used incorrectly",
+	22:  "Uses OS features not supported on host",
+	23:  "Authorization denied",
+	24:  "Auxiliary database format error",
+	25:  "2nd parameter to sqlite3_bind out of range",
+	26:  "File opened that is not a database file",
+	100: "sqlite3_step() has another row ready",
+	101: "sqlite3_step() has finished executing",
+}
+
+type impl struct{}
+
+func (impl) Open(name string) (driver.Conn, error) {
+	if C.sqlite3_threadsafe() == 0 {
+		return nil, errors.New("sqlite library was not compiled for thread-safe operation")
+	}
+
+	var db *C.sqlite3
+	cname := C.CString(name)
+	defer C.free(unsafe.Pointer(cname))
+	rv := C.sqlite3_open_v2(cname, &db,
+		C.SQLITE_OPEN_FULLMUTEX|
+			C.SQLITE_OPEN_READWRITE|
+			C.SQLITE_OPEN_CREATE,
+		nil)
+	if rv != 0 {
+		return nil, errno(rv)
+	}
+	if db == nil {
+		return nil, errors.New("sqlite succeeded without returning a database")
+	}
+	return &conn{db: db}, nil
+}
+
+type conn struct {
+	db     *C.sqlite3
+	closed bool
+	tx     bool
+}
+
+func (c *conn) error(rv C.int) error {
+	if rv == 0 {
+		return nil
+	}
+	if rv == 21 || c.closed {
+		return errno(rv)
+	}
+	return errors.New(errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
+}
+
+func (c *conn) Prepare(cmd string) (driver.Stmt, error) {
+	if c.closed {
+		panic("database/sql/driver: misuse of sqlite driver: Prepare after Close")
+	}
+	cmdstr := C.CString(cmd)
+	defer C.free(unsafe.Pointer(cmdstr))
+	var s *C.sqlite3_stmt
+	var tail *C.char
+	rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &s, &tail)
+	if rv != 0 {
+		return nil, c.error(rv)
+	}
+	return &stmt{c: c, stmt: s, sql: cmd, t0: time.Now()}, nil
+}
+
+func (c *conn) Close() error {
+	if c.closed {
+		panic("database/sql/driver: misuse of sqlite driver: multiple Close")
+	}
+	c.closed = true
+	rv := C.sqlite3_close(c.db)
+	c.db = nil
+	return c.error(rv)
+}
+
+func (c *conn) exec(cmd string) error {
+	cstring := C.CString(cmd)
+	defer C.free(unsafe.Pointer(cstring))
+	rv := C.sqlite3_exec(c.db, cstring, nil, nil, nil)
+	return c.error(rv)
+}
+
+func (c *conn) Begin() (driver.Tx, error) {
+	if c.tx {
+		panic("database/sql/driver: misuse of sqlite driver: multiple Tx")
+	}
+	if err := c.exec("BEGIN TRANSACTION"); err != nil {
+		return nil, err
+	}
+	c.tx = true
+	return &tx{c}, nil
+}
+
+type tx struct {
+	c *conn
+}
+
+func (t *tx) Commit() error {
+	if t.c == nil || !t.c.tx {
+		panic("database/sql/driver: misuse of sqlite driver: extra Commit")
+	}
+	t.c.tx = false
+	err := t.c.exec("COMMIT TRANSACTION")
+	t.c = nil
+	return err
+}
+
+func (t *tx) Rollback() error {
+	if t.c == nil || !t.c.tx {
+		panic("database/sql/driver: misuse of sqlite driver: extra Rollback")
+	}
+	t.c.tx = false
+	err := t.c.exec("ROLLBACK")
+	t.c = nil
+	return err
+}
+
+type stmt struct {
+	c        *conn
+	stmt     *C.sqlite3_stmt
+	err      error
+	t0       time.Time
+	sql      string
+	args     string
+	closed   bool
+	rows     bool
+	colnames []string
+	coltypes []string
+}
+
+func (s *stmt) Close() error {
+	if s.rows {
+		panic("database/sql/driver: misuse of sqlite driver: Close with active Rows")
+	}
+	if s.closed {
+		panic("database/sql/driver: misuse of sqlite driver: double Close of Stmt")
+	}
+	s.closed = true
+	rv := C.sqlite3_finalize(s.stmt)
+	if rv != 0 {
+		return s.c.error(rv)
+	}
+	return nil
+}
+
+func (s *stmt) NumInput() int {
+	if s.closed {
+		panic("database/sql/driver: misuse of sqlite driver: NumInput after Close")
+	}
+	return int(C.sqlite3_bind_parameter_count(s.stmt))
+}
+
+func (s *stmt) reset() error {
+	return s.c.error(C.sqlite3_reset(s.stmt))
+}
+
+func (s *stmt) start(args []driver.Value) error {
+	if err := s.reset(); err != nil {
+		return err
+	}
+
+	n := int(C.sqlite3_bind_parameter_count(s.stmt))
+	if n != len(args) {
+		return fmt.Errorf("incorrect argument count for command: have %d want %d", len(args), n)
+	}
+
+	for i, v := range args {
+		var str string
+		switch v := v.(type) {
+		case nil:
+			if rv := C.sqlite3_bind_null(s.stmt, C.int(i+1)); rv != 0 {
+				return s.c.error(rv)
+			}
+			continue
+
+		case float64:
+			if rv := C.sqlite3_bind_double(s.stmt, C.int(i+1), C.double(v)); rv != 0 {
+				return s.c.error(rv)
+			}
+			continue
+
+		case int64:
+			if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(v)); rv != 0 {
+				return s.c.error(rv)
+			}
+			continue
+
+		case []byte:
+			var p *byte
+			if len(v) > 0 {
+				p = &v[0]
+			}
+			if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
+				return s.c.error(rv)
+			}
+			continue
+
+		case bool:
+			var vi int64
+			if v {
+				vi = 1
+			}
+			if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(vi)); rv != 0 {
+				return s.c.error(rv)
+			}
+			continue
+
+		case time.Time:
+			str = v.UTC().Format(timefmt[0])
+
+		case string:
+			str = v
+
+		default:
+			str = fmt.Sprint(v)
+		}
+
+		cstr := C.CString(str)
+		rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
+		C.free(unsafe.Pointer(cstr))
+		if rv != 0 {
+			return s.c.error(rv)
+		}
+	}
+
+	return nil
+}
+
+func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
+	if s.closed {
+		panic("database/sql/driver: misuse of sqlite driver: Exec after Close")
+	}
+	if s.rows {
+		panic("database/sql/driver: misuse of sqlite driver: Exec with active Rows")
+	}
+
+	err := s.start(args)
+	if err != nil {
+		return nil, err
+	}
+
+	rv := C.sqlite3_step(s.stmt)
+	if errno(rv) != stepDone {
+		if rv == 0 {
+			rv = 21 // errMisuse
+		}
+		return nil, s.c.error(rv)
+	}
+
+	id := int64(C.sqlite3_last_insert_rowid(s.c.db))
+	rows := int64(C.sqlite3_changes(s.c.db))
+	return &result{id, rows}, nil
+}
+
+func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
+	if s.closed {
+		panic("database/sql/driver: misuse of sqlite driver: Query after Close")
+	}
+	if s.rows {
+		panic("database/sql/driver: misuse of sqlite driver: Query with active Rows")
+	}
+
+	err := s.start(args)
+	if err != nil {
+		return nil, err
+	}
+
+	s.rows = true
+	if s.colnames == nil {
+		n := int64(C.sqlite3_column_count(s.stmt))
+		s.colnames = make([]string, n)
+		s.coltypes = make([]string, n)
+		for i := range s.colnames {
+			s.colnames[i] = C.GoString(C.sqlite3_column_name(s.stmt, C.int(i)))
+			s.coltypes[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(s.stmt, C.int(i))))
+		}
+	}
+	return &rows{s}, nil
+}
+
+type rows struct {
+	s *stmt
+}
+
+func (r *rows) Columns() []string {
+	if r.s == nil {
+		panic("database/sql/driver: misuse of sqlite driver: Columns of closed Rows")
+	}
+	return r.s.colnames
+}
+
+const maxslice = 1<<31 - 1
+
+var timefmt = []string{
+	"2006-01-02 15:04:05.999999999",
+	"2006-01-02T15:04:05.999999999",
+	"2006-01-02 15:04:05",
+	"2006-01-02T15:04:05",
+	"2006-01-02 15:04",
+	"2006-01-02T15:04",
+	"2006-01-02",
+}
+
+func (r *rows) Next(dst []driver.Value) error {
+	if r.s == nil {
+		panic("database/sql/driver: misuse of sqlite driver: Next of closed Rows")
+	}
+
+	rv := C.sqlite3_step(r.s.stmt)
+	if errno(rv) != stepRow {
+		if errno(rv) == stepDone {
+			return io.EOF
+		}
+		if rv == 0 {
+			rv = 21
+		}
+		return r.s.c.error(rv)
+	}
+
+	for i := range dst {
+		switch typ := C.sqlite3_column_type(r.s.stmt, C.int(i)); typ {
+		default:
+			return fmt.Errorf("unexpected sqlite3 column type %d", typ)
+		case C.SQLITE_INTEGER:
+			val := int64(C.sqlite3_column_int64(r.s.stmt, C.int(i)))
+			switch r.s.coltypes[i] {
+			case "timestamp", "datetime":
+				dst[i] = time.Unix(val, 0).UTC()
+			case "boolean":
+				dst[i] = val > 0
+			default:
+				dst[i] = val
+			}
+
+		case C.SQLITE_FLOAT:
+			dst[i] = float64(C.sqlite3_column_double(r.s.stmt, C.int(i)))
+
+		case C.SQLITE_BLOB, C.SQLITE_TEXT:
+			n := int(C.sqlite3_column_bytes(r.s.stmt, C.int(i)))
+			var b []byte
+			if n > 0 {
+				p := C.sqlite3_column_blob(r.s.stmt, C.int(i))
+				b = (*[maxslice]byte)(unsafe.Pointer(p))[:n]
+			}
+			dst[i] = b
+			switch r.s.coltypes[i] {
+			case "timestamp", "datetime":
+				dst[i] = time.Time{}
+				s := string(b)
+				for _, f := range timefmt {
+					if t, err := time.Parse(f, s); err == nil {
+						dst[i] = t
+						break
+					}
+				}
+			}
+
+		case C.SQLITE_NULL:
+			dst[i] = nil
+		}
+	}
+	return nil
+}
+
+func (r *rows) Close() error {
+	if r.s == nil {
+		panic("database/sql/driver: misuse of sqlite driver: Close of closed Rows")
+	}
+	r.s.rows = false
+	r.s = nil
+	return nil
+}
+
+type result struct {
+	id   int64
+	rows int64
+}
+
+func (r *result) LastInsertId() (int64, error) {
+	return r.id, nil
+}
+
+func (r *result) RowsAffected() (int64, error) {
+	return r.rows, nil
+}
diff --git a/z_final_test.go b/z_final_test.go
index 837b5d1..5bdfa4f 100644
--- a/z_final_test.go
+++ b/z_final_test.go
@@ -13,5 +13,6 @@
 func TestFinal(t *testing.T) {
 	nuke(globalRuntime)
 	t.Logf("Start Fds: %d, Start Goroutines: %d", startFds, startGoroutines)
+	cleanupDevMapper()
 	displayFdGoroutines(t)
 }