File: merge.go

package info (click to toggle)
singularity-container 4.1.5%2Bds4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 43,876 kB
  • sloc: asm: 14,840; sh: 3,190; ansic: 1,751; awk: 414; makefile: 413; python: 99
file content (109 lines) | stat: -rw-r--r-- 2,058 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package staticfs

import (
	"context"
	"io"
	"io/fs"
	"os"

	"github.com/tonistiigi/fsutil"
	"golang.org/x/sync/errgroup"
)

type MergeFS struct {
	Lower fsutil.FS
	Upper fsutil.FS
}

var _ fsutil.FS = &MergeFS{}

func NewMergeFS(lower, upper fsutil.FS) *MergeFS {
	return &MergeFS{
		Lower: lower,
		Upper: upper,
	}
}

type record struct {
	path  string
	entry fs.DirEntry
	err   error
}

func (r *record) key() string {
	if r == nil {
		return ""
	}
	return convertPathToKey(r.path)
}

func (mfs *MergeFS) Walk(ctx context.Context, target string, fn fs.WalkDirFunc) error {
	ch1 := make(chan *record, 10)
	ch2 := make(chan *record, 10)

	eg, ctx := errgroup.WithContext(ctx)
	eg.Go(func() error {
		defer close(ch1)
		return mfs.Lower.Walk(ctx, target, func(path string, entry fs.DirEntry, err error) error {
			select {
			case ch1 <- &record{path: path, entry: entry, err: err}:
			case <-ctx.Done():
			}
			return context.Cause(ctx)
		})
	})
	eg.Go(func() error {
		defer close(ch2)
		return mfs.Upper.Walk(ctx, target, func(path string, entry fs.DirEntry, err error) error {
			select {
			case ch2 <- &record{path: path, entry: entry, err: err}:
			case <-ctx.Done():
			}
			return context.Cause(ctx)
		})
	})

	eg.Go(func() error {
		next1, ok1 := <-ch1
		key1 := next1.key()
		next2, ok2 := <-ch2
		key2 := next2.key()

		for {
			if !ok1 && !ok2 {
				break
			}
			if !ok2 || ok1 && key1 < key2 {
				if err := fn(next1.path, next1.entry, next1.err); err != nil {
					return err
				}
				next1, ok1 = <-ch1
				key1 = next1.key()
			} else if !ok1 || ok2 && key1 >= key2 {
				if err := fn(next2.path, next2.entry, next2.err); err != nil {
					return err
				}
				if ok1 && key1 == key2 {
					next1, ok1 = <-ch1
					key1 = next1.key()
				}
				next2, ok2 = <-ch2
				key2 = next2.key()
			}
		}
		return nil
	})

	return eg.Wait()
}

func (mfs *MergeFS) Open(p string) (io.ReadCloser, error) {
	r, err := mfs.Upper.Open(p)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
		return mfs.Lower.Open(p)
	}
	return r, nil
}