Path: blob/main/vendor/github.com/spf13/afero/unionFile.go
2875 views
package afero12import (3"io"4"os"5"path/filepath"6"syscall"7)89// The UnionFile implements the afero.File interface and will be returned10// when reading a directory present at least in the overlay or opening a file11// for writing.12//13// The calls to14// Readdir() and Readdirnames() merge the file os.FileInfo / names from the15// base and the overlay - for files present in both layers, only those16// from the overlay will be used.17//18// When opening files for writing (Create() / OpenFile() with the right flags)19// the operations will be done in both layers, starting with the overlay. A20// successful read in the overlay will move the cursor position in the base layer21// by the number of bytes read.22type UnionFile struct {23Base File24Layer File25Merger DirsMerger26off int27files []os.FileInfo28}2930func (f *UnionFile) Close() error {31// first close base, so we have a newer timestamp in the overlay. If we'd close32// the overlay first, we'd get a cacheStale the next time we access this file33// -> cache would be useless ;-)34if f.Base != nil {35f.Base.Close()36}37if f.Layer != nil {38return f.Layer.Close()39}40return BADFD41}4243func (f *UnionFile) Read(s []byte) (int, error) {44if f.Layer != nil {45n, err := f.Layer.Read(s)46if (err == nil || err == io.EOF) && f.Base != nil {47// advance the file position also in the base file, the next48// call may be a write at this position (or a seek with SEEK_CUR)49if _, seekErr := f.Base.Seek(int64(n), io.SeekCurrent); seekErr != nil {50// only overwrite err in case the seek fails: we need to51// report an eventual io.EOF to the caller52err = seekErr53}54}55return n, err56}57if f.Base != nil {58return f.Base.Read(s)59}60return 0, BADFD61}6263func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {64if f.Layer != nil {65n, err := f.Layer.ReadAt(s, o)66if (err == nil || err == io.EOF) && f.Base != nil {67_, err = f.Base.Seek(o+int64(n), io.SeekStart)68}69return n, err70}71if f.Base != nil {72return f.Base.ReadAt(s, o)73}74return 0, BADFD75}7677func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {78if f.Layer != nil {79pos, err = f.Layer.Seek(o, w)80if (err == nil || err == io.EOF) && f.Base != nil {81_, err = f.Base.Seek(o, w)82}83return pos, err84}85if f.Base != nil {86return f.Base.Seek(o, w)87}88return 0, BADFD89}9091func (f *UnionFile) Write(s []byte) (n int, err error) {92if f.Layer != nil {93n, err = f.Layer.Write(s)94if err == nil &&95f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?96_, err = f.Base.Write(s)97}98return n, err99}100if f.Base != nil {101return f.Base.Write(s)102}103return 0, BADFD104}105106func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {107if f.Layer != nil {108n, err = f.Layer.WriteAt(s, o)109if err == nil && f.Base != nil {110_, err = f.Base.WriteAt(s, o)111}112return n, err113}114if f.Base != nil {115return f.Base.WriteAt(s, o)116}117return 0, BADFD118}119120func (f *UnionFile) Name() string {121if f.Layer != nil {122return f.Layer.Name()123}124return f.Base.Name()125}126127// DirsMerger is how UnionFile weaves two directories together.128// It takes the FileInfo slices from the layer and the base and returns a129// single view.130type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)131132var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {133files := make(map[string]os.FileInfo)134135for _, fi := range lofi {136files[fi.Name()] = fi137}138139for _, fi := range bofi {140if _, exists := files[fi.Name()]; !exists {141files[fi.Name()] = fi142}143}144145rfi := make([]os.FileInfo, len(files))146147i := 0148for _, fi := range files {149rfi[i] = fi150i++151}152153return rfi, nil154}155156// Readdir will weave the two directories together and157// return a single view of the overlayed directories.158// At the end of the directory view, the error is io.EOF if c > 0.159func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {160merge := f.Merger161if merge == nil {162merge = defaultUnionMergeDirsFn163}164165if f.off == 0 {166var lfi []os.FileInfo167if f.Layer != nil {168lfi, err = f.Layer.Readdir(-1)169if err != nil {170return nil, err171}172}173174var bfi []os.FileInfo175if f.Base != nil {176bfi, err = f.Base.Readdir(-1)177if err != nil {178return nil, err179}180181}182merged, err := merge(lfi, bfi)183if err != nil {184return nil, err185}186f.files = append(f.files, merged...)187}188files := f.files[f.off:]189190if c <= 0 {191return files, nil192}193194if len(files) == 0 {195return nil, io.EOF196}197198if c > len(files) {199c = len(files)200}201202defer func() { f.off += c }()203return files[:c], nil204}205206func (f *UnionFile) Readdirnames(c int) ([]string, error) {207rfi, err := f.Readdir(c)208if err != nil {209return nil, err210}211var names []string212for _, fi := range rfi {213names = append(names, fi.Name())214}215return names, nil216}217218func (f *UnionFile) Stat() (os.FileInfo, error) {219if f.Layer != nil {220return f.Layer.Stat()221}222if f.Base != nil {223return f.Base.Stat()224}225return nil, BADFD226}227228func (f *UnionFile) Sync() (err error) {229if f.Layer != nil {230err = f.Layer.Sync()231if err == nil && f.Base != nil {232err = f.Base.Sync()233}234return err235}236if f.Base != nil {237return f.Base.Sync()238}239return BADFD240}241242func (f *UnionFile) Truncate(s int64) (err error) {243if f.Layer != nil {244err = f.Layer.Truncate(s)245if err == nil && f.Base != nil {246err = f.Base.Truncate(s)247}248return err249}250if f.Base != nil {251return f.Base.Truncate(s)252}253return BADFD254}255256func (f *UnionFile) WriteString(s string) (n int, err error) {257if f.Layer != nil {258n, err = f.Layer.WriteString(s)259if err == nil && f.Base != nil {260_, err = f.Base.WriteString(s)261}262return n, err263}264if f.Base != nil {265return f.Base.WriteString(s)266}267return 0, BADFD268}269270func copyFile(base Fs, layer Fs, name string, bfh File) error {271// First make sure the directory exists272exists, err := Exists(layer, filepath.Dir(name))273if err != nil {274return err275}276if !exists {277err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME?278if err != nil {279return err280}281}282283// Create the file on the overlay284lfh, err := layer.Create(name)285if err != nil {286return err287}288n, err := io.Copy(lfh, bfh)289if err != nil {290// If anything fails, clean up the file291layer.Remove(name)292lfh.Close()293return err294}295296bfi, err := bfh.Stat()297if err != nil || bfi.Size() != n {298layer.Remove(name)299lfh.Close()300return syscall.EIO301}302303err = lfh.Close()304if err != nil {305layer.Remove(name)306lfh.Close()307return err308}309return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())310}311312func copyToLayer(base Fs, layer Fs, name string) error {313bfh, err := base.Open(name)314if err != nil {315return err316}317defer bfh.Close()318319return copyFile(base, layer, name, bfh)320}321322func copyFileToLayer(base Fs, layer Fs, name string, flag int, perm os.FileMode) error {323bfh, err := base.OpenFile(name, flag, perm)324if err != nil {325return err326}327defer bfh.Close()328329return copyFile(base, layer, name, bfh)330}331332333