forked from u-root/u-root
-
Notifications
You must be signed in to change notification settings - Fork 2
/
testramfs.go
139 lines (121 loc) · 2.96 KB
/
testramfs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2012-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// testramfs tests things, badly
package main
import (
"io"
"io/ioutil"
"log"
"os"
"syscall"
flag "github.com/spf13/pflag"
"golang.org/x/sys/unix"
"github.com/u-root/u-root/pkg/cpio"
"github.com/u-root/u-root/pkg/pty"
)
const (
unshareFlags = syscall.CLONE_NEWNS
cloneFlags = syscall.CLONE_NEWIPC |
syscall.CLONE_NEWNET |
// making newpid work will be more tricky,
// since none of my CLs to fix go runtime for
// it ever got in.
//syscall.CLONE_NEWPID |
syscall.CLONE_NEWUTS |
0
)
var (
noremove = flag.BoolP("noremove", "n", false, "remove tempdir when done")
interactive = flag.BoolP("interactive", "i", false, "interactive mode")
)
func main() {
flag.Parse()
if flag.NArg() != 1 {
log.Fatalf("usage: %s <cpio-path>", os.Args[0])
}
c := flag.Args()[0]
f, err := os.Open(c)
if err != nil {
log.Fatal(err)
}
// So, what's the plan here?
//
// - new mount namespace
// - root mount is a tmpfs mount filled with the archive.
//
// - new PID namespace
// - archive/init actually runs as PID 1.
// Note this is basically a chroot and umask is inherited.
// The umask has to be zero else some creation will end
// up with incorrect permissions, a particular problem
// in device creation.
u := unix.Umask(0)
defer unix.Umask(u)
tempDir, err := ioutil.TempDir("", "u-root")
if err != nil {
log.Fatal(err)
}
// Don't do a RemoveAll. This should be empty and
// an error can tell us we got something wrong.
if !*noremove {
defer func(n string) {
log.Printf("Removing %v", n)
if err := os.Remove(n); err != nil {
log.Fatal(err)
}
}(tempDir)
}
if err := syscall.Mount("", tempDir, "tmpfs", 0, ""); err != nil {
log.Fatal(err)
}
if !*noremove {
defer func(n string) {
log.Printf("Unmounting %v", n)
if err := syscall.Unmount(n, syscall.MNT_DETACH); err != nil {
log.Fatal(err)
}
}(tempDir)
}
archiver, err := cpio.Format("newc")
if err != nil {
log.Fatal(err)
}
r := archiver.Reader(f)
for {
rec, err := r.ReadRecord()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
cpio.CreateFileInRoot(rec, tempDir)
}
cmd, err := pty.New()
if err != nil {
log.Fatal(err)
}
cmd.Command("/init")
cmd.C.SysProcAttr.Chroot = tempDir
cmd.C.SysProcAttr.Cloneflags = cloneFlags
cmd.C.SysProcAttr.Unshareflags = cloneFlags
if *interactive {
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
return
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
go io.Copy(cmd.TTY, cmd.Ptm)
// At this point you could use an array of commands/output templates to
// drive the test, and end with the exit command shown nere.
if n, err := cmd.Ptm.Write([]byte("exit\n")); err != nil {
log.Printf("Writing exit: want (5, nil); got (%d, %v)\n", n, err)
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
}