github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/cmds/chroot/chroot.go (about) 1 // +build !windows !plan9 2 3 package main 4 5 import ( 6 "bytes" 7 "flag" 8 "fmt" 9 "log" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strconv" 14 "strings" 15 "syscall" 16 ) 17 18 type userSpec struct { 19 uid uint32 20 gid uint32 21 } 22 23 func (u *userSpec) Set(s string) error { 24 var err error 25 userspecSplit := strings.Split(s, ":") 26 if len(userspecSplit) != 2 || userspecSplit[1] == "" { 27 return fmt.Errorf("Expected user spec flag to be \":\" separated values received %s", s) 28 } 29 30 u.uid, err = stringToUint32(userspecSplit[0]) 31 if err != nil { 32 return err 33 } 34 35 u.gid, err = stringToUint32(userspecSplit[1]) 36 if err != nil { 37 return err 38 } 39 40 return nil 41 } 42 43 func (u *userSpec) Get() interface{} { 44 return *u 45 } 46 47 func (u *userSpec) String() string { 48 return fmt.Sprintf("%d:%d", u.uid, u.gid) 49 } 50 51 func defaultUser() userSpec { 52 return userSpec{ 53 uid: uint32(os.Getuid()), 54 gid: uint32(os.Getgid()), 55 } 56 } 57 58 type groupsSpec struct { 59 groups []uint32 60 } 61 62 func (g *groupsSpec) Set(s string) error { 63 groupStrs := strings.Split(s, ",") 64 g.groups = make([]uint32, len(groupStrs)) 65 66 for index, group := range groupStrs { 67 68 gid, err := stringToUint32(group) 69 if err != nil { 70 return err 71 } 72 73 g.groups[index] = gid 74 } 75 76 return nil 77 } 78 79 func (g *groupsSpec) Get() interface{} { 80 return *g 81 } 82 83 func (g *groupsSpec) String() string { 84 var buffer bytes.Buffer 85 86 for index, gid := range g.groups { 87 buffer.WriteString(fmt.Sprint(gid)) 88 if index < len(g.groups)-1 { 89 buffer.WriteString(",") 90 } 91 } 92 93 return buffer.String() 94 } 95 96 var ( 97 skipchdirFlag bool 98 user userSpec = defaultUser() 99 groups groupsSpec = groupsSpec{} 100 ) 101 102 func init() { 103 flag.Var(&user, "u", "specify user and group (ID only) as USER:GROUP") 104 flag.Var(&groups, "g", "specify supplementary group ids as g1,g2,..,gN") 105 flag.BoolVar(&skipchdirFlag, "s", false, fmt.Sprint("Use this option to not change", 106 "the working directory to / after changing the root directory to newroot, i.e., ", 107 "inside the chroot. This option is only permitted when newroot is the old / directory.")) 108 } 109 110 func stringToUint32(str string) (uint32, error) { 111 ret, err := strconv.ParseUint(str, 10, 32) 112 if err != nil { 113 return 0, err 114 } 115 return uint32(ret), nil 116 } 117 118 func parseCommand(args []string) []string { 119 if len(args) > 1 { 120 return args[1:] 121 } 122 return []string{"/bin/sh", "-i"} 123 } 124 125 func parseRoot(args []string) (root string, err error) { 126 if len(args) < 1 { 127 return "", fmt.Errorf("Missing operand") 128 } 129 130 return filepath.Abs(args[0]) 131 } 132 133 func isRoot(dir string) (bool, error) { 134 realPath, err := filepath.EvalSymlinks(dir) 135 if err != nil { 136 return false, err 137 } 138 absolutePath, err := filepath.Abs(realPath) 139 if err != nil { 140 return false, err 141 } 142 if absolutePath == "/" { 143 return true, nil 144 } 145 return false, nil 146 } 147 148 func main() { 149 var ( 150 newRoot string 151 isOldroot bool 152 err error 153 ) 154 155 flag.Parse() 156 157 if flag.NFlag() == 0 && flag.NArg() == 0 { 158 flag.PrintDefaults() 159 os.Exit(1) 160 } 161 162 newRoot, err = parseRoot(flag.Args()) 163 if err != nil { 164 log.Fatal(err) 165 } 166 isOldroot, err = isRoot(newRoot) 167 if err != nil { 168 log.Fatal(err) 169 } 170 171 if !skipchdirFlag { 172 err = os.Chdir(newRoot) 173 if err != nil { 174 log.Fatal(err) 175 } 176 } else if !isOldroot { 177 log.Fatal("The -s option is only permitted when newroot is the old / directory") 178 } 179 180 argv := parseCommand(flag.Args()) 181 182 cmd := exec.Command(argv[0], argv[1:]...) 183 184 cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr 185 cmd.SysProcAttr = &syscall.SysProcAttr{ 186 Credential: &syscall.Credential{ 187 Uid: user.uid, 188 Gid: user.gid, 189 Groups: groups.groups, 190 }, 191 Chroot: newRoot, 192 } 193 194 if err = cmd.Run(); err != nil { 195 log.Fatal(err) 196 } 197 }