// smartFetch the things that blobs point to, not just blobs. func smartFetch(cl *client.Client, targ string, br *blobref.BlobRef) error { if *flagVerbose { log.Printf("Fetching %v into %q", br, targ) } rc, err := fetch(cl, br) if err != nil { return err } defer rc.Close() sniffer := new(index.BlobSniffer) _, err = io.CopyN(sniffer, rc, sniffSize) if err != nil && err != io.EOF { return err } sniffer.Parse() sc, ok := sniffer.Superset() if !ok { // opaque data - put it in a file f, err := os.Create(targ) if err != nil { return fmt.Errorf("opaque: %v", err) } defer f.Close() body, _ := sniffer.Body() r := io.MultiReader(bytes.NewBuffer(body), rc) _, err = io.Copy(f, r) return err } sc.BlobRef = br switch sc.Type { case "directory": dir := filepath.Join(targ, sc.FileName) if err := os.MkdirAll(dir, sc.FileMode()); err != nil { return err } if err := setFileMeta(dir, sc); err != nil { log.Print(err) } entries := blobref.Parse(sc.Entries) if entries == nil { return fmt.Errorf("bad entries blobref: %v", sc.Entries) } return smartFetch(cl, dir, entries) case "static-set": // directory entries for _, m := range sc.Members { dref := blobref.Parse(m) if dref == nil { return fmt.Errorf("bad member blobref: %v", m) } if err := smartFetch(cl, targ, dref); err != nil { return err } } return nil case "file": name := filepath.Join(targ, sc.FileName) f, err := os.Create(name) if err != nil { return fmt.Errorf("file type: %v", err) } defer f.Close() seekFetcher := blobref.SeekerFromStreamingFetcher(cl) fr, err := schema.NewFileReader(seekFetcher, br) if err != nil { return fmt.Errorf("NewFileReader: %v", err) } defer fr.Close() if err := setFileMeta(name, sc); err != nil { log.Print(err) } return nil default: return errors.New("unknown blob type: " + sc.Type) } panic("unreachable") }
// smartFetch the things that blobs point to, not just blobs. func smartFetch(src blobref.StreamingFetcher, targ string, br *blobref.BlobRef) error { rc, err := fetch(src, br) if err != nil { return err } defer rc.Close() sniffer := new(index.BlobSniffer) _, err = io.CopyN(sniffer, rc, sniffSize) if err != nil && err != io.EOF { return err } sniffer.Parse() sc, ok := sniffer.Superset() if !ok { if *flagVerbose { log.Printf("Fetching opaque data %v into %q", br, targ) } // opaque data - put it in a file f, err := os.Create(targ) if err != nil { return fmt.Errorf("opaque: %v", err) } defer f.Close() body, _ := sniffer.Body() r := io.MultiReader(bytes.NewReader(body), rc) _, err = io.Copy(f, r) return err } sc.BlobRef = br switch sc.Type { case "directory": dir := filepath.Join(targ, sc.FileName) if *flagVerbose { log.Printf("Fetching directory %v into %s", br, dir) } if err := os.MkdirAll(dir, sc.FileMode()); err != nil { return err } if err := setFileMeta(dir, sc); err != nil { log.Print(err) } entries := blobref.Parse(sc.Entries) if entries == nil { return fmt.Errorf("bad entries blobref: %v", sc.Entries) } return smartFetch(src, dir, entries) case "static-set": if *flagVerbose { log.Printf("Fetching directory entries %v into %s", br, targ) } // directory entries const numWorkers = 10 type work struct { br *blobref.BlobRef errc chan<- error } workc := make(chan work, len(sc.Members)) defer close(workc) for i := 0; i < numWorkers; i++ { go func() { for wi := range workc { wi.errc <- smartFetch(src, targ, wi.br) } }() } var errcs []<-chan error for _, m := range sc.Members { dref := blobref.Parse(m) if dref == nil { return fmt.Errorf("bad member blobref: %v", m) } errc := make(chan error, 1) errcs = append(errcs, errc) workc <- work{dref, errc} } for _, errc := range errcs { if err := <-errc; err != nil { return err } } return nil case "file": seekFetcher := blobref.SeekerFromStreamingFetcher(src) fr, err := schema.NewFileReader(seekFetcher, br) if err != nil { return fmt.Errorf("NewFileReader: %v", err) } fr.LoadAllChunks() defer fr.Close() name := filepath.Join(targ, sc.FileName) if fi, err := os.Stat(name); err == nil && fi.Size() == fi.Size() { if *flagVerbose { log.Printf("Skipping %s; already exists.", name) return nil } } if *flagVerbose { log.Printf("Writing %s to %s ...", br, name) } f, err := os.Create(name) if err != nil { return fmt.Errorf("file type: %v", err) } defer f.Close() if _, err := io.Copy(f, fr); err != nil { return fmt.Errorf("Copying %s to %s: %v", br, name, err) } if err := setFileMeta(name, sc); err != nil { log.Print(err) } return nil default: return errors.New("unknown blob type: " + sc.Type) } panic("unreachable") }