github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/exp/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 if err != nil { 84 return err 85 } 86 87 debug("Clone tree with path %s fi %v", path, fi) 88 if fi.IsDir() { 89 debug("Walking, dir %v\n", path) 90 if path[lt:] == "" { 91 return nil 92 } 93 if err := os.MkdirAll(path[lt:], dirMode); err != nil { 94 debug("Mkdir of %s failed: %v", path[lt:], err) 95 // TODO: EEXIST should not be an error. Ignore 96 // err for now. FIXME. 97 //return err 98 } 99 return nil 100 } 101 // all else gets a symlink. 102 103 // If the link exists 104 if target, err := os.Readlink(path[lt:]); err == nil { 105 // Confirm that it points to the same path to be symlinked 106 if target == path { 107 return nil 108 } 109 110 // If it does not, return error because tcz packages are inconsistent 111 return fmt.Errorf("symlink: need %q -> %q, but %q -> %q is already there", path, path[lt:], path, target) 112 } 113 114 debug("Need to symlink %v to %v\n", path, path[lt:]) 115 116 if err := os.Symlink(path, path[lt:]); err != nil { 117 return fmt.Errorf("Symlink: %v", err) 118 } 119 120 return nil 121 }) 122 if err != nil { 123 l.Fatalf("Clone tree: %v", err) 124 } 125 return nil 126 } 127 128 func fetch(p string) error { 129 fullpath := filepath.Join(tczLocalPackageDir, p) 130 packageName := filepath.Join(tczServerDir, p) 131 132 if _, err := os.Stat(fullpath); !os.IsNotExist(err) { 133 debug("package %s is downloaded\n", fullpath) 134 return nil 135 } 136 137 if _, err := os.Stat(fullpath); err != nil { 138 cmd := fmt.Sprintf("http://%s:%s/%s", *host, *port, packageName) 139 debug("Fetch %v\n", cmd) 140 141 resp, err := http.Get(cmd) 142 if err != nil { 143 l.Fatalf("Get of %v failed: %v\n", cmd, err) 144 } 145 defer resp.Body.Close() 146 147 if resp.Status != "200 OK" { 148 debug("%v Not OK! %v\n", cmd, resp.Status) 149 return syscall.ENOENT 150 } 151 152 debug("resp %v err %v\n", resp, err) 153 // we have the whole tcz in resp.Body. 154 // First, save it to /tczRoot/name 155 f, err := os.Create(fullpath) 156 if err != nil { 157 l.Fatalf("Create of :%v: failed: %v\n", fullpath, err) 158 } else { 159 debug("created %v f %v\n", fullpath, f) 160 } 161 162 if c, err := io.Copy(f, resp.Body); err != nil { 163 l.Fatal(err) 164 } else { 165 /* OK, these are compressed tars ... */ 166 debug("c %v err %v\n", c, err) 167 } 168 f.Close() 169 } 170 return nil 171 } 172 173 // deps is ALL the packages we need fetched or not 174 // this may even let us work with parallel tcz, ALMOST 175 func installPackage(tczName string, deps map[string]bool) error { 176 debug("installPackage: %v %v\n", tczName, deps) 177 depName := tczName + ".dep" 178 if err := fetch(tczName); err != nil { 179 l.Fatal(err) 180 } 181 deps[tczName] = true 182 debug("Fetched %v\n", tczName) 183 // now fetch dependencies if any. 184 if err := fetch(depName); err == nil { 185 debug("Fetched dep ok!\n") 186 } else { 187 debug("No dep file found\n") 188 if err := ioutil.WriteFile(filepath.Join(tczLocalPackageDir, depName), []byte{}, os.FileMode(0444)); err != nil { 189 debug("Tried to write Blank file %v, failed %v\n", depName, err) 190 } 191 return nil 192 } 193 // read deps file 194 depFullPath := filepath.Join(tczLocalPackageDir, depName) 195 deplist, err := ioutil.ReadFile(depFullPath) 196 if err != nil { 197 l.Fatalf("Fetched dep file %v but can't read it? %v", depName, err) 198 } 199 debug("deplist for %v is :%v:\n", depName, deplist) 200 realDepList := "" 201 for _, v := range strings.Split(string(deplist), "\n") { 202 // split("name\n") gets you a 2-element array with second 203 // element the empty string 204 if len(v) == 0 { 205 break 206 } 207 if _, ok := ignorePackage[v]; ok { 208 debug("%v is ignored", v) 209 continue 210 } 211 realDepList = realDepList + v + "\n" 212 debug("FOR %v get package %v\n", tczName, v) 213 if deps[v] { 214 continue 215 } 216 if err := installPackage(v, deps); err != nil { 217 return err 218 } 219 } 220 if string(deplist) == realDepList { 221 return nil 222 } 223 if err := ioutil.WriteFile(depFullPath, []byte(realDepList), os.FileMode(0444)); err != nil { 224 debug("Tried to write deplist file %v, failed %v\n", depName, err) 225 return err 226 } 227 return nil 228 229 } 230 231 func setupPackages(tczName string, deps map[string]bool) error { 232 debug("setupPackages: @ %v deps %v\n", tczName, deps) 233 for v := range deps { 234 cmdName := strings.Split(v, filepath.Ext(v))[0] 235 packagePath := filepath.Join(tinyCoreRoot, cmdName) 236 237 if _, err := os.Stat(packagePath); err == nil { 238 debug("PackagePath %s exists, skipping mount", packagePath) 239 continue 240 } 241 242 if err := os.MkdirAll(packagePath, dirMode); err != nil { 243 l.Fatalf("Package directory %s at %s, can not be created: %v", tczName, packagePath, err) 244 } 245 246 loopname, err := findloop() 247 if err != nil { 248 l.Fatal(err) 249 } 250 debug("findloop gets %v err %v\n", loopname, err) 251 pkgpath := filepath.Join(tczLocalPackageDir, v) 252 ffd, err := syscall.Open(pkgpath, syscall.O_RDONLY, 0) 253 if err != nil { 254 l.Fatalf("%v: %v\n", pkgpath, err) 255 } 256 lfd, err := syscall.Open(loopname, syscall.O_RDONLY, 0) 257 if err != nil { 258 l.Fatalf("%v: %v\n", loopname, err) 259 } 260 debug("ffd %v lfd %v\n", ffd, lfd) 261 262 a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(lfd), LOOP_SET_FD, uintptr(ffd)) 263 if errno != 0 { 264 l.Fatalf("loop set fd ioctl: pkgpath :%v:, loop :%v:, %v, %v, %v\n", pkgpath, loopname, a, b, errno) 265 } 266 267 /* now mount it. The convention is the mount is in /tinyCoreRoot/packagename */ 268 if err := syscall.Mount(loopname, packagePath, "squashfs", syscall.MS_MGC_VAL|syscall.MS_RDONLY, ""); err != nil { 269 // how I hate Linux. 270 // Since all you ever get back is a USELESS ERRNO 271 // note: the open succeeded for both things, the mkdir worked, 272 // the loop device open worked. And we can get ENODEV. So, 273 // just what went wrong? "We're not telling. Here's an errno." 274 li, lerr := os.Stat(loopname) 275 pi, perr := os.Stat(pkgpath) 276 di, derr := os.Stat(packagePath) 277 e := fmt.Sprintf("%v: loop is (%v, %v); package is (%v, %v); dir is (%v, %v). Is squashfs built into your kernel?", err, li, lerr, pi, perr, di, derr) 278 l.Fatalf("Mount :%s: on :%s: %v\n", loopname, packagePath, e) 279 } 280 err = clonetree(packagePath) 281 if err != nil { 282 l.Fatalf("clonetree: %v\n", err) 283 } 284 } 285 return nil 286 287 } 288 289 func usage() string { 290 return "tcz [-v version] [-a architecture] [-h hostname] [-p host port] [-d debug prints] PROGRAM..." 291 } 292 293 func init() { 294 defUsage := flag.Usage 295 flag.Usage = func() { 296 os.Args[0] = cmd 297 defUsage() 298 } 299 } 300 301 func main() { 302 flag.Parse() 303 if *debugPrint { 304 debug = l.Printf 305 } 306 307 ip := strings.Fields(*skip) 308 debug("ignored packages: %v", ip) 309 for _, p := range ip { 310 ignorePackage[p+".tcz"] = struct{}{} 311 } 312 needPackages := make(map[string]bool) 313 tczServerDir = filepath.Join("/", *version, *arch, "/tcz") 314 tczLocalPackageDir = filepath.Join(*tczRoot, tczServerDir) 315 316 packages := flag.Args() 317 debug("tcz: packages %v", packages) 318 319 if len(packages) == 0 { 320 flag.Usage() 321 os.Exit(1) 322 } 323 324 if err := os.MkdirAll(tczLocalPackageDir, dirMode); err != nil { 325 l.Fatal(err) 326 } 327 328 if *install { 329 if err := os.MkdirAll(tinyCoreRoot, dirMode); err != nil { 330 l.Fatal(err) 331 } 332 } 333 334 for _, cmdName := range packages { 335 336 tczName := cmdName + ".tcz" 337 338 if err := installPackage(tczName, needPackages); err != nil { 339 l.Fatal(err) 340 } 341 342 debug("After installpackages: needPackages %v\n", needPackages) 343 344 if *install { 345 if err := setupPackages(tczName, needPackages); err != nil { 346 l.Fatal(err) 347 } 348 } 349 } 350 }