func mountTmpfs(dirname string) bool { var statfs syscall.Statfs_t if err := syscall.Statfs(dirname, &statfs); err != nil { fmt.Fprintf(os.Stderr, "Unable to create Statfs: %s: %s\n", dirname, err) return false } if statfs.Type != 0x01021994 { err := wsyscall.Mount("none", dirname, "tmpfs", 0, "size=65536,mode=0750") if err == nil { fmt.Printf("Mounted tmpfs on: %s\n", dirname) } else { fmt.Fprintf(os.Stderr, "Unable to mount tmpfs on: %s: %s\n", dirname, err) return false } } return true }
func unshareAndBind(workingRootDir string) bool { if *unshare { // Re-exec myself using the unshare syscall while on a locked thread. // This hack is required because syscall.Unshare() operates on only one // thread in the process, and Go switches execution between threads // randomly. Thus, the namespace can be suddenly switched for running // code. This is an aspect of Go that was not well thought out. runtime.LockOSThread() if err := wsyscall.UnshareMountNamespace(); err != nil { fmt.Fprintf(os.Stderr, "Unable to unshare mount namesace: %s\n", err) return false } // Ensure the process is slightly niced. Since the Linux implementation // of setpriority(2) only applies to a thread, not the whole process // (contrary to the POSIX specification), do this in the pinned OS // thread so that the whole process (after exec) will be niced. syscall.Setpriority(syscall.PRIO_PROCESS, 0, 1) args := append(os.Args, "-unshare=false") if err := syscall.Exec(args[0], args, os.Environ()); err != nil { fmt.Fprintf(os.Stderr, "Unable to Exec:%s: %s\n", args[0], err) return false } } syscall.Unmount(workingRootDir, 0) err := wsyscall.Mount(*rootDir, workingRootDir, "", wsyscall.MS_BIND, "") if err != nil { fmt.Fprintf(os.Stderr, "Unable to bind mount %s to %s: %s\n", *rootDir, workingRootDir, err) return false } // Clean up -unshare=false so that a subsequent re-exec starts from scratch. args := make([]string, 0, len(os.Args)-1) for _, arg := range os.Args { if arg != "-unshare=false" { args = append(args, arg) } } os.Args = args return true }