Merge pull request #912 from dotcloud/bump_0.4.1

Bumped version to 0.4.1
diff --git a/archive.go b/archive.go
index 0646662..44fdd56 100644
--- a/archive.go
+++ b/archive.go
@@ -51,6 +51,7 @@
 	return CmdStream(cmd)
 }
 
+// FIXME: specify behavior when target path exists vs. doesn't exist.
 func Untar(archive io.Reader, path string) error {
 	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
 	cmd.Stdin = archive
@@ -64,6 +65,30 @@
 	return nil
 }
 
+// UntarPath is a convenience function which looks for an archive
+// at filesystem path `src`, and unpacks it at `dst`.
+func UntarPath(src, dst string) error {
+	if archive, err := os.Open(src); err != nil {
+		return err
+	} else if err := Untar(archive, dst); err != nil {
+		return err
+	}
+	return nil
+}
+
+// CopyWithTar creates a tar archive of filesystem path `src`, and
+// unpacks it at filesystem path `dst`.
+// The archive is streamed directly with fixed buffering and no
+// intermediary disk IO.
+//
+func CopyWithTar(src, dst string) error {
+	archive, err := Tar(src, Uncompressed)
+	if err != nil {
+		return err
+	}
+	return Untar(archive, dst)
+}
+
 // 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.
diff --git a/buildfile.go b/buildfile.go
index aaa6f6b..de350a8 100644
--- a/buildfile.go
+++ b/buildfile.go
@@ -202,21 +202,17 @@
 		if err := os.MkdirAll(destPath, 0700); err != nil {
 			return err
 		}
-
-		files, err := ioutil.ReadDir(path.Join(b.context, orig))
-		if err != nil {
+		if err := CopyWithTar(origPath, destPath); err != nil {
 			return err
 		}
-		for _, fi := range files {
-			if err := utils.CopyDirectory(path.Join(origPath, fi.Name()), path.Join(destPath, fi.Name())); err != nil {
-				return err
-			}
-		}
-	} else {
+		// 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)
+		// If that fails, just copy it as a regular file
 		if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil {
 			return err
 		}
-		if err := utils.CopyDirectory(origPath, destPath); err != nil {
+		if err := CopyWithTar(origPath, destPath); err != nil {
 			return err
 		}
 	}
diff --git a/buildfile_test.go b/buildfile_test.go
index d9c60a7..33e6a31 100644
--- a/buildfile_test.go
+++ b/buildfile_test.go
@@ -23,6 +23,12 @@
 run    sh -c 'echo root:testpass > /tmp/passwd'
 run    mkdir -p /var/run/sshd`
 
+// FIXME: test building with a context
+
+// FIXME: test building with a local ADD as first command
+
+// FIXME: test building with 2 successive overlapping ADD commands
+
 func TestBuild(t *testing.T) {
 	dockerfiles := []string{Dockerfile, DockerfileNoNewLine}
 	for _, Dockerfile := range dockerfiles {
diff --git a/docs/sources/use/index.rst b/docs/sources/use/index.rst
index a1086c1..2f74f60 100644
--- a/docs/sources/use/index.rst
+++ b/docs/sources/use/index.rst
@@ -14,6 +14,7 @@
 
    basics
    workingwithrepository
+   port_redirection
    builder
    puppet
 
diff --git a/docs/sources/use/port_redirection.rst b/docs/sources/use/port_redirection.rst
new file mode 100644
index 0000000..5cf848f
--- /dev/null
+++ b/docs/sources/use/port_redirection.rst
@@ -0,0 +1,25 @@
+:title: Port redirection
+:description: usage about port redirection
+:keywords: Usage, basic port, docker, documentation, examples
+
+
+Port redirection
+================
+
+Docker can redirect public tcp ports to your container, so it can be reached over the network.
+Port redirection is done on ``docker run`` using the -p flag.
+
+A port redirect is specified as PUBLIC:PRIVATE, where tcp port PUBLIC will be redirected to
+tcp port PRIVATE. As a special case, the public port can be omitted, in which case a random
+public port will be allocated.
+
+.. code-block:: bash
+
+    # A random PUBLIC port is redirected to PRIVATE port 80 on the container
+    docker run -p 80 <image> <cmd>
+
+    # PUBLIC port 80 is redirected to PRIVATE port 80
+    docker run -p 80:80 <image> <cmd>
+
+
+Default port redirects can be built into a container with the EXPOSE build command.
diff --git a/utils/utils.go b/utils/utils.go
index 708c8bb..b77c1ea 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -548,6 +548,7 @@
 	}, nil
 }
 
+// FIXME: this is deprecated by CopyWithTar in archive.go
 func CopyDirectory(source, dest string) error {
 	if output, err := exec.Command("cp", "-ra", source, dest).CombinedOutput(); err != nil {
 		return fmt.Errorf("Error copy: %s (%s)", err, output)