/* Scan a real filesystem and see it as fixture file descriptions. Usually used as a prelude to a pair of `Describe` calls followed by an equality assertion. Note that this loads all file bodies into memory at once, so it is not wise to use on large filesystems. Result will be sorted by filename, as per usual. */ func Scan(basePath string) Fixture { ffs := Fixture{fmt.Sprintf("Scan of %q", basePath), nil} preVisit := func(filenode *fs.FilewalkNode) error { if filenode.Err != nil { return filenode.Err } hdr, file := fs.ScanFile(basePath, filenode.Path) var body []byte if file != nil { defer file.Close() var err error body, err = ioutil.ReadAll(file) if err != nil { return err } } ffs.Files = append(ffs.Files, FixtureFile{hdr, body}) return nil } if err := fs.Walk(basePath, preVisit, nil); err != nil { panic(err) } sort.Sort(filesByPath(ffs.Files)) return ffs }
func saveWalk(srcBasePath string, tw *tar.Writer, filterset filter.FilterSet, bucket fshash.Bucket, hasherFactory func() hash.Hash) error { preVisit := func(filenode *fs.FilewalkNode) error { if filenode.Err != nil { return filenode.Err } hdr, file := fs.ScanFile(srcBasePath, filenode.Path, filenode.Info) // apply filters. on scans, this is pretty easy, all of em just apply to the stream in memory. hdr = filterset.Apply(hdr) // flaten time to seconds. this tar writer impl doesn't do subsecond precision. // the writer will flatten it internally of course, but we need to do it here as well // so that the hash and the serial form are describing the same thing. hdr.ModTime = hdr.ModTime.Truncate(time.Second) wat := tar.Header(hdr) // this line is... we're not gonna talk about this. tw.WriteHeader(&wat) if file == nil { bucket.Record(hdr, nil) } else { defer file.Close() hasher := hasherFactory() tee := io.MultiWriter(tw, hasher) _, err := io.Copy(tee, file) if err != nil { return err } bucket.Record(hdr, hasher.Sum(nil)) } return nil } return fs.Walk(srcBasePath, preVisit, nil) }
func CopyingPlacer(srcBasePath, destBasePath string, _ bool) integrity.Emplacement { srcBaseStat, err := os.Stat(srcBasePath) if err != nil || !srcBaseStat.IsDir() { panic(Error.New("copyingplacer: srcPath %q must be dir: %s", srcBasePath, err)) } destBaseStat, err := os.Stat(destBasePath) if err != nil || !destBaseStat.IsDir() { panic(Error.New("copyingplacer: destPath %q must be dir: %s", destBasePath, err)) } // remove any files already here (to emulate behavior like an overlapping mount) // (can't take the easy route and just `os.RemoveAll(destBasePath)` because that propagates times changes onto the parent.) d, err := os.Open(destBasePath) if err != nil { panic(Error.New("copyingplacer: io error: %s", err)) } names, err := d.Readdirnames(-1) if err != nil { panic(Error.New("copyingplacer: io error: %s", err)) } for _, name := range names { err := os.RemoveAll(filepath.Join(destBasePath, name)) if err != nil { panic(Error.New("copyingplacer: io error: %s", err)) } } // walk and copy preVisit := func(filenode *fs.FilewalkNode) error { if filenode.Err != nil { return filenode.Err } hdr, file := fs.ScanFile(srcBasePath, filenode.Path, filenode.Info) if file != nil { defer file.Close() } fs.PlaceFile(destBasePath, hdr, file) return nil } postVisit := func(filenode *fs.FilewalkNode) error { if filenode.Info.IsDir() { if err := fspatch.UtimesNano(filepath.Join(destBasePath, filenode.Path), def.Epochwhen, filenode.Info.ModTime()); err != nil { return err } } return nil } try.Do(func() { if err := fs.Walk(srcBasePath, preVisit, postVisit); err != nil { panic(err) } }).CatchAll(func(err error) { panic(Error.New("copyingplacer: io failed: %s", err)) }).Done() return copyEmplacement{path: destBasePath} }
func NewAufsPlacer(workPath string) integrity.Placer { err := os.MkdirAll(workPath, 0755) if err != nil { panic(errors.IOError.Wrap(err)) } workPath, err = filepath.Abs(workPath) if err != nil { panic(errors.IOError.Wrap(err)) } return func(srcBasePath, destBasePath string, writable bool) integrity.Emplacement { srcBaseStat, err := os.Stat(srcBasePath) if err != nil || !srcBaseStat.IsDir() { panic(Error.New("aufsplacer: srcPath %q must be dir: %s", srcBasePath, err)) } destBaseStat, err := os.Stat(destBasePath) if err != nil || !destBaseStat.IsDir() { panic(Error.New("aufsplacer: destPath %q must be dir: %s", destBasePath, err)) } // if a RO mount is requested, no need to set up COW; just hand off to bind. if !writable { return BindPlacer(srcBasePath, destBasePath, writable) } // make work dir for the overlay layer layerPath, err := ioutil.TempDir(workPath, "layer-") if err != nil { panic(errors.IOError.Wrap(err)) } // set up COW // if you were doing this in a shell, it'd be roughly `mount -t aufs -o br="$layer":"$base" none "$composite"`. // yes, this may behave oddly in the event of paths containing ":" or "=". syscall.Mount("none", destBasePath, "aufs", 0, fmt.Sprintf("br:%s=rw:%s=ro", layerPath, srcBasePath)) // fix props on layerPath; otherwise they instantly leak through hdr, _ := fs.ScanFile(srcBasePath, "./", srcBaseStat) fs.PlaceFile(layerPath, hdr, nil) // that's it; setting up COW also mounted it into destination. return aufsEmplacement{ layerPath: layerPath, landingPath: destBasePath, } } }