title: Calling setns from Go returns EINVAL for mnt namespace
body: <p>The C code works fine and correctly enters the namespace, but the Go code always seems to return EINVAL from the <code>setns</code> call to enter the <code>mnt</code> namespace. I've tried a number of permutations (including embedded C code with cgo and external <code>.so</code>) on Go <code>1.2</code>, <code>1.3</code> and the current tip.</p>
  
  <p>Stepping through the code in <code>gdb</code> shows that both sequences are calling <code>setns</code> in <code>libc</code> the exact same way (or so it appears to me).</p>
  
  <p>I have boiled what seems to be the issue down to the code below. What am I doing wrong?</p>
  
  <h2>Setup</h2>
  
  <p>I have a shell alias for starting quick busybox containers:</p>
  
  <pre><code>alias startbb='docker inspect --format "{{ .State.Pid }}" $(docker run -d busybox sleep 1000000)'
  </code></pre>
  
  <p>After running this, <code>startbb</code> will start a container and output it's PID.</p>
  
  <p><code>lxc-checkconfig</code> outputs:</p>
  
  <pre><code>Found kernel config file /boot/config-3.8.0-44-generic
  --- Namespaces ---
  Namespaces: enabled
  Utsname namespace: enabled
  Ipc namespace: enabled
  Pid namespace: enabled
  User namespace: missing
  Network namespace: enabled
  Multiple /dev/pts instances: enabled
  
  --- Control groups ---
  Cgroup: enabled
  Cgroup clone_children flag: enabled
  Cgroup device: enabled
  Cgroup sched: enabled
  Cgroup cpu account: enabled
  Cgroup memory controller: missing
  Cgroup cpuset: enabled
  
  --- Misc ---
  Veth pair device: enabled
  Macvlan: enabled
  Vlan: enabled
  File capabilities: enabled
  </code></pre>
  
  <p><code>uname -a</code> produces:</p>
  
  <pre><code>Linux gecko 3.8.0-44-generic #66~precise1-Ubuntu SMP Tue Jul 15 04:01:04 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
  </code></pre>
  
  <h2>Working C code</h2>
  
  <p>The following C code works fine:</p>
  
  <pre><code>#include &lt;errno.h&gt;
  #include &lt;sched.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  #include &lt;fcntl.h&gt;
  
  main(int argc, char* argv[]) {
      int i;
      char nspath[1024];
      char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" };
  
      if (geteuid()) { fprintf(stderr, "%s\n", "abort: you want to run this as root"); exit(1); }
  
      if (argc != 2) { fprintf(stderr, "%s\n", "abort: you must provide a PID as the sole argument"); exit(2); }
  
      for (i=0; i&lt;5; i++) {
          sprintf(nspath, "/proc/%s/ns/%s", argv[1], namespaces[i]);
          int fd = open(nspath, O_RDONLY);
  
          if (setns(fd, 0) == -1) { 
              fprintf(stderr, "setns on %s namespace failed: %s\n", namespaces[i], strerror(errno));
          } else {
              fprintf(stdout, "setns on %s namespace succeeded\n", namespaces[i]);
          }
  
          close(fd);
      }
  }
  </code></pre>
  
  <p>After compiling with <code>gcc -o checkns checkns.c</code>, the output of <code>sudo ./checkns &lt;PID&gt;</code> is:</p>
  
  <pre><code>setns on ipc namespace succeeded
  setns on uts namespace succeeded
  setns on net namespace succeeded
  setns on pid namespace succeeded
  setns on mnt namespace succeeded
  </code></pre>
  
  <h2>Failing Go code</h2>
  
  <p>Conversely, the following Go code (which should be identical) doesn't work quite as well:</p>
  
  <pre><code>package main
  
  import (
      "fmt"
      "os"
      "path/filepath"
      "syscall"
  )
  
  func main() {
      if syscall.Geteuid() != 0 {
          fmt.Println("abort: you want to run this as root")
          os.Exit(1)
      }
  
      if len(os.Args) != 2 {
          fmt.Println("abort: you must provide a PID as the sole argument")
          os.Exit(2)
      }
  
      namespaces := []string{"ipc", "uts", "net", "pid", "mnt"}
  
      for i := range namespaces {
          fd, _ := syscall.Open(filepath.Join("/proc", os.Args[1], "ns", namespaces[i]), syscall.O_RDONLY, 0644)
          err, _, msg := syscall.RawSyscall(308, uintptr(fd), 0, 0) // 308 == setns
  
          if err != 0 {
              fmt.Println("setns on", namespaces[i], "namespace failed:", msg)
          } else {
              fmt.Println("setns on", namespaces[i], "namespace succeeded")
          }
  
      }
  }
  </code></pre>
  
  <p>Instead, running <code>sudo go run main.go &lt;PID&gt;</code> produces:</p>
  
  <pre><code>setns on ipc namespace succeeded
  setns on uts namespace succeeded
  setns on net namespace succeeded
  setns on pid namespace succeeded
  setns on mnt namespace failed: invalid argument
  </code></pre>