mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
7a7357dae1
This enables docker cp and ADD/COPY docker build support for LCOW. Originally, the graphdriver.Get() interface returned a local path to the container root filesystem. This does not work for LCOW, so the Get() method now returns an interface that LCOW implements to support copying to and from the container. Signed-off-by: Akash Gupta <akagup@microsoft.com>
171 lines
3.7 KiB
Go
171 lines
3.7 KiB
Go
package remotecontext
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
|
|
iradix "github.com/hashicorp/go-immutable-radix"
|
|
|
|
"github.com/docker/docker/pkg/containerfs"
|
|
"github.com/pkg/errors"
|
|
"github.com/tonistiigi/fsutil"
|
|
)
|
|
|
|
type hashed interface {
|
|
Hash() string
|
|
}
|
|
|
|
// CachableSource is a source that contains cache records for its contents
|
|
type CachableSource struct {
|
|
mu sync.Mutex
|
|
root containerfs.ContainerFS
|
|
tree *iradix.Tree
|
|
txn *iradix.Txn
|
|
}
|
|
|
|
// NewCachableSource creates new CachableSource
|
|
func NewCachableSource(root string) *CachableSource {
|
|
ts := &CachableSource{
|
|
tree: iradix.New(),
|
|
root: containerfs.NewLocalContainerFS(root),
|
|
}
|
|
return ts
|
|
}
|
|
|
|
// MarshalBinary marshals current cache information to a byte array
|
|
func (cs *CachableSource) MarshalBinary() ([]byte, error) {
|
|
b := TarsumBackup{Hashes: make(map[string]string)}
|
|
root := cs.getRoot()
|
|
root.Walk(func(k []byte, v interface{}) bool {
|
|
b.Hashes[string(k)] = v.(*fileInfo).sum
|
|
return false
|
|
})
|
|
return b.Marshal()
|
|
}
|
|
|
|
// UnmarshalBinary decodes cache information for presented byte array
|
|
func (cs *CachableSource) UnmarshalBinary(data []byte) error {
|
|
var b TarsumBackup
|
|
if err := b.Unmarshal(data); err != nil {
|
|
return err
|
|
}
|
|
txn := iradix.New().Txn()
|
|
for p, v := range b.Hashes {
|
|
txn.Insert([]byte(p), &fileInfo{sum: v})
|
|
}
|
|
cs.mu.Lock()
|
|
defer cs.mu.Unlock()
|
|
cs.tree = txn.Commit()
|
|
return nil
|
|
}
|
|
|
|
// Scan rescans the cache information from the file system
|
|
func (cs *CachableSource) Scan() error {
|
|
lc, err := NewLazySource(cs.root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
txn := iradix.New().Txn()
|
|
err = cs.root.Walk(cs.root.Path(), func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to walk %s", path)
|
|
}
|
|
rel, err := Rel(cs.root, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h, err := lc.Hash(rel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
txn.Insert([]byte(rel), &fileInfo{sum: h})
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cs.mu.Lock()
|
|
defer cs.mu.Unlock()
|
|
cs.tree = txn.Commit()
|
|
return nil
|
|
}
|
|
|
|
// HandleChange notifies the source about a modification operation
|
|
func (cs *CachableSource) HandleChange(kind fsutil.ChangeKind, p string, fi os.FileInfo, err error) (retErr error) {
|
|
cs.mu.Lock()
|
|
if cs.txn == nil {
|
|
cs.txn = cs.tree.Txn()
|
|
}
|
|
if kind == fsutil.ChangeKindDelete {
|
|
cs.txn.Delete([]byte(p))
|
|
cs.mu.Unlock()
|
|
return
|
|
}
|
|
|
|
h, ok := fi.(hashed)
|
|
if !ok {
|
|
cs.mu.Unlock()
|
|
return errors.Errorf("invalid fileinfo: %s", p)
|
|
}
|
|
|
|
hfi := &fileInfo{
|
|
sum: h.Hash(),
|
|
}
|
|
cs.txn.Insert([]byte(p), hfi)
|
|
cs.mu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (cs *CachableSource) getRoot() *iradix.Node {
|
|
cs.mu.Lock()
|
|
if cs.txn != nil {
|
|
cs.tree = cs.txn.Commit()
|
|
cs.txn = nil
|
|
}
|
|
t := cs.tree
|
|
cs.mu.Unlock()
|
|
return t.Root()
|
|
}
|
|
|
|
// Close closes the source
|
|
func (cs *CachableSource) Close() error {
|
|
return nil
|
|
}
|
|
|
|
func (cs *CachableSource) normalize(path string) (cleanpath, fullpath string, err error) {
|
|
cleanpath = cs.root.Clean(string(cs.root.Separator()) + path)[1:]
|
|
fullpath, err = cs.root.ResolveScopedPath(path, true)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("Forbidden path outside the context: %s (%s)", path, fullpath)
|
|
}
|
|
_, err = cs.root.Lstat(fullpath)
|
|
if err != nil {
|
|
return "", "", convertPathError(err, path)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Hash returns a hash for a single file in the source
|
|
func (cs *CachableSource) Hash(path string) (string, error) {
|
|
n := cs.getRoot()
|
|
// TODO: check this for symlinks
|
|
v, ok := n.Get([]byte(path))
|
|
if !ok {
|
|
return path, nil
|
|
}
|
|
return v.(*fileInfo).sum, nil
|
|
}
|
|
|
|
// Root returns a root directory for the source
|
|
func (cs *CachableSource) Root() containerfs.ContainerFS {
|
|
return cs.root
|
|
}
|
|
|
|
type fileInfo struct {
|
|
sum string
|
|
}
|
|
|
|
func (fi *fileInfo) Hash() string {
|
|
return fi.sum
|
|
}
|