github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/cmds/tcz/tcz.go (about) 1 // Copyright 2012 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "flag" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "log" 13 "net/http" 14 "os" 15 "path/filepath" 16 "strings" 17 "syscall" 18 ) 19 20 const ( 21 cmd = "tcz [options] package-names" 22 /* 23 * IOCTL commands --- we will commandeer 0x4C ('L') 24 */ 25 LOOP_SET_CAPACITY = 0x4C07 26 LOOP_CHANGE_FD = 0x4C06 27 LOOP_GET_STATUS64 = 0x4C05 28 LOOP_SET_STATUS64 = 0x4C04 29 LOOP_GET_STATUS = 0x4C03 30 LOOP_SET_STATUS = 0x4C02 31 LOOP_CLR_FD = 0x4C01 32 LOOP_SET_FD = 0x4C00 33 LO_NAME_SIZE = 64 34 LO_KEY_SIZE = 32 35 /* /dev/loop-control interface */ 36 LOOP_CTL_ADD = 0x4C80 37 LOOP_CTL_REMOVE = 0x4C81 38 LOOP_CTL_GET_FREE = 0x4C82 39 SYS_ioctl = 16 40 dirMode = 0755 41 tinyCoreRoot = "/TinyCorePackages/tcloop" 42 ) 43 44 //http://distro.ibiblio.org/tinycorelinux/5.x/x86_64/tcz/ 45 //The .dep is the name + .dep 46 47 var ( 48 l = log.New(os.Stdout, "tcz: ", 0) 49 host = flag.String("h", "tinycorelinux.net", "Host name for packages") 50 version = flag.String("v", "8.x", "tinycore version") 51 arch = flag.String("a", "x86_64", "tinycore architecture") 52 port = flag.String("p", "80", "Host port") 53 install = flag.Bool("i", true, "Install the packages, i.e. mount and create symlinks") 54 tczRoot = flag.String("r", "/tcz", "tcz root directory") 55 debugPrint = flag.Bool("d", false, "Enable debug prints") 56 skip = flag.String("skip", "", "Packages to skip") 57 debug = func(f string, s ...interface{}) {} 58 tczServerDir string 59 tczLocalPackageDir string 60 ignorePackage = make(map[string]struct{}) 61 ) 62 63 // consider making this a goroutine which pushes the string down the channel. 64 func findloop() (name string, err error) { 65 cfd, err := syscall.Open("/dev/loop-control", syscall.O_RDWR, 0) 66 if err != nil { 67 log.Fatalf("/dev/loop-control: %v", err) 68 } 69 defer syscall.Close(cfd) 70 a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(cfd), LOOP_CTL_GET_FREE, 0) 71 if errno != 0 { 72 log.Fatalf("ioctl: %v\n", err) 73 } 74 debug("a %v b %v err %v\n", a, b, err) 75 name = fmt.Sprintf("/dev/loop%d", a) 76 return name, nil 77 } 78 79 func clonetree(tree string) error { 80 debug("Clone tree %v", tree) 81 lt := len(tree) 82 err := filepath.Walk(tree, func(path string, fi os.FileInfo, err error) error { 83 84 debug("Clone tree with path %s fi %v", path, fi) 85 if fi.IsDir() { 86 debug("Walking, dir %v\n", path) 87 if path[lt:] == "" { 88 return nil 89 } 90 if err := os.MkdirAll(path[lt:], dirMode); err != nil { 91 debug("Mkdir of %s failed: %v", path[lt:], err) 92 // TODO: EEXIST should not be an error. Ignore 93 // err for now. FIXME. 94 //return err 95 } 96 return nil 97 } 98 // all else gets a symlink. 99 100 // If the link exists 101 if target, err := os.Readlink(path[lt:]); err == nil { 102 // Confirm that it points to the same path to be symlinked 103 if target == path { 104 return nil 105 } 106 107 // If it does not, return error because tcz packages are inconsistent 108 return fmt.Errorf("symlink: need %q -> %q, but %q -> %q is already there", path, path[lt:], path, target) 109 } 110 111 debug("Need to symlink %v to %v\n", path, path[lt:]) 112 113 if err := os.Symlink(path, path[lt:]); err != nil { 114 return fmt.Errorf("Symlink: %v", err) 115 } 116 117 return nil 118 }) 119 if err != nil { 120 l.Fatalf("Clone tree: %v", err) 121 } 122 return nil 123 } 124 125 func fetch(p string) error { 126 fullpath := filepath.Join(tczLocalPackageDir, p) 127 packageName := filepath.Join(tczServerDir, p) 128 129 if _, err := os.Stat(fullpath); !os.IsNotExist(err) { 130 debug("package %s is downloaded\n", fullpath) 131 return nil 132 } 133 134 if _, err := os.Stat(fullpath); err != nil { 135 cmd := fmt.Sprintf("http://%s:%s/%s", *host, *port, packageName) 136 debug("Fetch %v\n", cmd) 137 138 resp, err := http.Get(cmd) 139 if err != nil { 140 l.Fatalf("Get of %v failed: %v\n", cmd, err) 141 } 142 defer resp.Body.Close() 143 144 if resp.Status != "200 OK" { 145 debug("%v Not OK! %v\n", cmd, resp.Status) 146 return syscall.ENOENT 147 } 148 149 debug("resp %v err %v\n", resp, err) 150 // we have the whole tcz in resp.Body. 151 // First, save it to /tczRoot/name 152 f, err := os.Create(fullpath) 153 if err != nil { 154 l.Fatalf("Create of :%v: failed: %v\n", fullpath, err) 155 } else { 156 debug("created %v f %v\n", fullpath, f) 157 } 158 159 if c, err := io.Copy(f, resp.Body); err != nil { 160 l.Fatal(err) 161 } else { 162 /* OK, these are compressed tars ... */ 163 debug("c %v err %v\n", c, err) 164 } 165 f.Close() 166 } 167 return nil 168 } 169 170 // deps is ALL the packages we need fetched or not 171 // this may even let us work with parallel tcz, ALMOST 172 func installPackage(tczName string, deps map[string]bool) error { 173 debug("installPackage: %v %v\n", tczName, deps) 174 depName := tczName + ".dep" 175 if err := fetch(tczName); err != nil { 176 l.Fatal(err) 177 } 178 deps[tczName] = true 179 debug("Fetched %v\n", tczName) 180 // now fetch dependencies if any. 181 if err := fetch(depName); err == nil { 182 debug("Fetched dep ok!\n") 183 } else { 184 debug("No dep file found\n") 185 if err := ioutil.WriteFile(filepath.Join(tczLocalPackageDir, depName), []byte{}, os.FileMode(0444)); err != nil { 186 debug("Tried to write Blank file %v, failed %v\n", depName, err) 187 } 188 return nil 189 } 190 // read deps file 191 depFullPath := filepath.Join(tczLocalPackageDir, depName) 192 deplist, err := ioutil.ReadFile(depFullPath) 193 if err != nil { 194 l.Fatalf("Fetched dep file %v but can't read it? %v", depName, err) 195 } 196 debug("deplist for %v is :%v:\n", depName, deplist) 197 realDepList := "" 198 for _, v := range strings.Split(string(deplist), "\n") { 199 // split("name\n") gets you a 2-element array with second 200 // element the empty string 201 if len(v) == 0 { 202 break 203 } 204 if _, ok := ignorePackage[v]; ok { 205 debug("%v is ignored", v) 206 continue 207 } 208 realDepList = realDepList + v + "\n" 209 debug("FOR %v get package %v\n", tczName, v) 210 if deps[v] { 211 continue 212 } 213 if err := installPackage(v, deps); err != nil { 214 return err 215 } 216 } 217 if string(deplist) == realDepList { 218 return nil 219 } 220 if err := ioutil.WriteFile(depFullPath, []byte(realDepList), os.FileMode(0444)); err != nil { 221 debug("Tried to write deplist file %v, failed %v\n", depName, err) 222 return err 223 } 224 return nil 225 226 } 227 228 func setupPackages(tczName string, deps map[string]bool) error { 229 debug("setupPackages: @ %v deps %v\n", tczName, deps) 230 for v := range deps { 231 cmdName := strings.Split(v, filepath.Ext(v))[0] 232 packagePath := filepath.Join(tinyCoreRoot, cmdName) 233 234 if _, err := os.Stat(packagePath); err == nil { 235 debug("PackagePath %s exists, skipping mount", packagePath) 236 continue 237 } 238 239 if err := os.MkdirAll(packagePath, dirMode); err != nil { 240 l.Fatalf("Package directory %s at %s, can not be created: %v", tczName, packagePath, err) 241 } 242 243 loopname, err := findloop() 244 if err != nil { 245 l.Fatal(err) 246 } 247 debug("findloop gets %v err %v\n", loopname, err) 248 pkgpath := filepath.Join(tczLocalPackageDir, v) 249 ffd, err := syscall.Open(pkgpath, syscall.O_RDONLY, 0) 250 if err != nil { 251 l.Fatalf("%v: %v\n", pkgpath, err) 252 } 253 lfd, err := syscall.Open(loopname, syscall.O_RDONLY, 0) 254 if err != nil { 255 l.Fatalf("%v: %v\n", loopname, err) 256 } 257 debug("ffd %v lfd %v\n", ffd, lfd) 258 259 a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(lfd), LOOP_SET_FD, uintptr(ffd)) 260 if errno != 0 { 261 l.Fatalf("loop set fd ioctl: pkgpath :%v:, loop :%v:, %v, %v, %v\n", pkgpath, loopname, a, b, errno) 262 } 263 264 /* now mount it. The convention is the mount is in /tinyCoreRoot/packagename */ 265 if err := syscall.Mount(loopname, packagePath, "squashfs", syscall.MS_MGC_VAL|syscall.MS_RDONLY, ""); err != nil { 266 l.Fatalf("Mount :%s: on :%s: %v\n", loopname, packagePath, err) 267 } 268 err = clonetree(packagePath) 269 if err != nil { 270 l.Fatalf("clonetree: %v\n", err) 271 } 272 } 273 return nil 274 275 } 276 277 func usage() string { 278 return "tcz [-v version] [-a architecture] [-h hostname] [-p host port] [-d debug prints] PROGRAM..." 279 } 280 281 func init() { 282 defUsage := flag.Usage 283 flag.Usage = func() { 284 os.Args[0] = cmd 285 defUsage() 286 } 287 } 288 289 func main() { 290 flag.Parse() 291 if *debugPrint { 292 debug = l.Printf 293 } 294 295 ip := strings.Fields(*skip) 296 debug("ignored packages: %v", ip) 297 for _, p := range ip { 298 ignorePackage[p+".tcz"] = struct{}{} 299 } 300 needPackages := make(map[string]bool) 301 tczServerDir = filepath.Join("/", *version, *arch, "/tcz") 302 tczLocalPackageDir = filepath.Join(*tczRoot, tczServerDir) 303 304 packages := flag.Args() 305 306 if len(packages) == 0 { 307 flag.Usage() 308 os.Exit(1) 309 } 310 311 if err := os.MkdirAll(tczLocalPackageDir, dirMode); err != nil { 312 l.Fatal(err) 313 } 314 315 if *install { 316 if err := os.MkdirAll(tinyCoreRoot, dirMode); err != nil { 317 l.Fatal(err) 318 } 319 } 320 321 for _, cmdName := range packages { 322 323 tczName := cmdName + ".tcz" 324 325 if err := installPackage(tczName, needPackages); err != nil { 326 l.Fatal(err) 327 } 328 329 debug("After installpackages: needPackages %v\n", needPackages) 330 331 if *install { 332 if err := setupPackages(tczName, needPackages); err != nil { 333 l.Fatal(err) 334 } 335 } 336 } 337 }