podman
1package afero2
3import (4"io"5"os"6"path/filepath"7"syscall"8)
9
10// The UnionFile implements the afero.File interface and will be returned
11// when reading a directory present at least in the overlay or opening a file
12// for writing.
13//
14// The calls to
15// Readdir() and Readdirnames() merge the file os.FileInfo / names from the
16// base and the overlay - for files present in both layers, only those
17// from the overlay will be used.
18//
19// When opening files for writing (Create() / OpenFile() with the right flags)
20// the operations will be done in both layers, starting with the overlay. A
21// successful read in the overlay will move the cursor position in the base layer
22// by the number of bytes read.
23type UnionFile struct {24Base File
25Layer File
26Merger DirsMerger
27off int28files []os.FileInfo29}
30
31func (f *UnionFile) Close() error {32// first close base, so we have a newer timestamp in the overlay. If we'd close33// the overlay first, we'd get a cacheStale the next time we access this file34// -> cache would be useless ;-)35if f.Base != nil {36f.Base.Close()37}38if f.Layer != nil {39return f.Layer.Close()40}41return BADFD42}
43
44func (f *UnionFile) Read(s []byte) (int, error) {45if f.Layer != nil {46n, err := f.Layer.Read(s)47if (err == nil || err == io.EOF) && f.Base != nil {48// advance the file position also in the base file, the next49// call may be a write at this position (or a seek with SEEK_CUR)50if _, seekErr := f.Base.Seek(int64(n), io.SeekCurrent); seekErr != nil {51// only overwrite err in case the seek fails: we need to52// report an eventual io.EOF to the caller53err = seekErr54}55}56return n, err57}58if f.Base != nil {59return f.Base.Read(s)60}61return 0, BADFD62}
63
64func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {65if f.Layer != nil {66n, err := f.Layer.ReadAt(s, o)67if (err == nil || err == io.EOF) && f.Base != nil {68_, err = f.Base.Seek(o+int64(n), io.SeekStart)69}70return n, err71}72if f.Base != nil {73return f.Base.ReadAt(s, o)74}75return 0, BADFD76}
77
78func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {79if f.Layer != nil {80pos, err = f.Layer.Seek(o, w)81if (err == nil || err == io.EOF) && f.Base != nil {82_, err = f.Base.Seek(o, w)83}84return pos, err85}86if f.Base != nil {87return f.Base.Seek(o, w)88}89return 0, BADFD90}
91
92func (f *UnionFile) Write(s []byte) (n int, err error) {93if f.Layer != nil {94n, err = f.Layer.Write(s)95if err == nil && f.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}
105
106func (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}
119
120func (f *UnionFile) Name() string {121if f.Layer != nil {122return f.Layer.Name()123}124return f.Base.Name()125}
126
127// DirsMerger is how UnionFile weaves two directories together.
128// It takes the FileInfo slices from the layer and the base and returns a
129// single view.
130type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)131
132var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {133files := make(map[string]os.FileInfo)134
135for _, fi := range lofi {136files[fi.Name()] = fi137}138
139for _, fi := range bofi {140if _, exists := files[fi.Name()]; !exists {141files[fi.Name()] = fi142}143}144
145rfi := make([]os.FileInfo, len(files))146
147i := 0148for _, fi := range files {149rfi[i] = fi150i++151}152
153return rfi, nil154}
155
156// Readdir will weave the two directories together and
157// 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) {160var merge DirsMerger = f.Merger161if merge == nil {162merge = defaultUnionMergeDirsFn163}164
165if f.off == 0 {166var lfi []os.FileInfo167if f.Layer != nil {168lfi, err = f.Layer.Readdir(-1)169if err != nil {170return nil, err171}172}173
174var bfi []os.FileInfo175if f.Base != nil {176bfi, err = f.Base.Readdir(-1)177if err != nil {178return nil, err179}180
181}182merged, err := merge(lfi, bfi)183if err != nil {184return nil, err185}186f.files = append(f.files, merged...)187}188files := f.files[f.off:]189
190if c <= 0 {191return files, nil192}193
194if len(files) == 0 {195return nil, io.EOF196}197
198if c > len(files) {199c = len(files)200}201
202defer func() { f.off += c }()203return files[:c], nil204}
205
206func (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}
217
218func (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}
227
228func (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}
241
242func (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}
255
256func (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}
269
270func 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}282
283// 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}295
296bfi, err := bfh.Stat()297if err != nil || bfi.Size() != n {298layer.Remove(name)299lfh.Close()300return syscall.EIO301}302
303err = lfh.Close()304if err != nil {305layer.Remove(name)306lfh.Close()307return err308}309return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())310}
311
312func copyToLayer(base Fs, layer Fs, name string) error {313bfh, err := base.Open(name)314if err != nil {315return err316}317defer bfh.Close()318
319return copyFile(base, layer, name, bfh)320}
321
322func 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()328
329return copyFile(base, layer, name, bfh)330}
331