190 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package fstest
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 
 | |
| 	"github.com/containerd/continuity"
 | |
| )
 | |
| 
 | |
| type resourceUpdate struct {
 | |
| 	Original continuity.Resource
 | |
| 	Updated  continuity.Resource
 | |
| }
 | |
| 
 | |
| func (u resourceUpdate) String() string {
 | |
| 	return fmt.Sprintf("%s(mode: %o, uid: %d, gid: %d) -> %s(mode: %o, uid: %d, gid: %d)",
 | |
| 		u.Original.Path(), u.Original.Mode(), u.Original.UID(), u.Original.GID(),
 | |
| 		u.Updated.Path(), u.Updated.Mode(), u.Updated.UID(), u.Updated.GID(),
 | |
| 	)
 | |
| }
 | |
| 
 | |
| type resourceListDifference struct {
 | |
| 	Additions []continuity.Resource
 | |
| 	Deletions []continuity.Resource
 | |
| 	Updates   []resourceUpdate
 | |
| }
 | |
| 
 | |
| func (l resourceListDifference) HasDiff() bool {
 | |
| 	return len(l.Additions) > 0 || len(l.Deletions) > 0 || len(l.Updates) > 0
 | |
| }
 | |
| 
 | |
| func (l resourceListDifference) String() string {
 | |
| 	buf := bytes.NewBuffer(nil)
 | |
| 	for _, add := range l.Additions {
 | |
| 		fmt.Fprintf(buf, "+ %s\n", add.Path())
 | |
| 	}
 | |
| 	for _, del := range l.Deletions {
 | |
| 		fmt.Fprintf(buf, "- %s\n", del.Path())
 | |
| 	}
 | |
| 	for _, upt := range l.Updates {
 | |
| 		fmt.Fprintf(buf, "~ %s\n", upt.String())
 | |
| 	}
 | |
| 	return string(buf.Bytes())
 | |
| }
 | |
| 
 | |
| // diffManifest compares two resource lists and returns the list
 | |
| // of adds updates and deletes, resource lists are not reordered
 | |
| // before doing difference.
 | |
| func diffResourceList(r1, r2 []continuity.Resource) resourceListDifference {
 | |
| 	i1 := 0
 | |
| 	i2 := 0
 | |
| 	var d resourceListDifference
 | |
| 
 | |
| 	for i1 < len(r1) && i2 < len(r2) {
 | |
| 		p1 := r1[i1].Path()
 | |
| 		p2 := r2[i2].Path()
 | |
| 		switch {
 | |
| 		case p1 < p2:
 | |
| 			d.Deletions = append(d.Deletions, r1[i1])
 | |
| 			i1++
 | |
| 		case p1 == p2:
 | |
| 			if !compareResource(r1[i1], r2[i2]) {
 | |
| 				d.Updates = append(d.Updates, resourceUpdate{
 | |
| 					Original: r1[i1],
 | |
| 					Updated:  r2[i2],
 | |
| 				})
 | |
| 			}
 | |
| 			i1++
 | |
| 			i2++
 | |
| 		case p1 > p2:
 | |
| 			d.Additions = append(d.Additions, r2[i2])
 | |
| 			i2++
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for i1 < len(r1) {
 | |
| 		d.Deletions = append(d.Deletions, r1[i1])
 | |
| 		i1++
 | |
| 
 | |
| 	}
 | |
| 	for i2 < len(r2) {
 | |
| 		d.Additions = append(d.Additions, r2[i2])
 | |
| 		i2++
 | |
| 	}
 | |
| 
 | |
| 	return d
 | |
| }
 | |
| 
 | |
| func compareResource(r1, r2 continuity.Resource) bool {
 | |
| 	if r1.Path() != r2.Path() {
 | |
| 		return false
 | |
| 	}
 | |
| 	if r1.Mode() != r2.Mode() {
 | |
| 		return false
 | |
| 	}
 | |
| 	if r1.UID() != r2.UID() {
 | |
| 		return false
 | |
| 	}
 | |
| 	if r1.GID() != r2.GID() {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// TODO(dmcgowan): Check if is XAttrer
 | |
| 
 | |
| 	return compareResourceTypes(r1, r2)
 | |
| 
 | |
| }
 | |
| 
 | |
| func compareResourceTypes(r1, r2 continuity.Resource) bool {
 | |
| 	switch t1 := r1.(type) {
 | |
| 	case continuity.RegularFile:
 | |
| 		t2, ok := r2.(continuity.RegularFile)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		return compareRegularFile(t1, t2)
 | |
| 	case continuity.Directory:
 | |
| 		t2, ok := r2.(continuity.Directory)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		return compareDirectory(t1, t2)
 | |
| 	case continuity.SymLink:
 | |
| 		t2, ok := r2.(continuity.SymLink)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		return compareSymLink(t1, t2)
 | |
| 	case continuity.NamedPipe:
 | |
| 		t2, ok := r2.(continuity.NamedPipe)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		return compareNamedPipe(t1, t2)
 | |
| 	case continuity.Device:
 | |
| 		t2, ok := r2.(continuity.Device)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		return compareDevice(t1, t2)
 | |
| 	default:
 | |
| 		// TODO(dmcgowan): Should this panic?
 | |
| 		return r1 == r2
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func compareRegularFile(r1, r2 continuity.RegularFile) bool {
 | |
| 	if r1.Size() != r2.Size() {
 | |
| 		return false
 | |
| 	}
 | |
| 	p1 := r1.Paths()
 | |
| 	p2 := r2.Paths()
 | |
| 	if len(p1) != len(p2) {
 | |
| 		return false
 | |
| 	}
 | |
| 	for i := range p1 {
 | |
| 		if p1[i] != p2[i] {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	d1 := r1.Digests()
 | |
| 	d2 := r2.Digests()
 | |
| 	if len(d1) != len(d2) {
 | |
| 		return false
 | |
| 	}
 | |
| 	for i := range d1 {
 | |
| 		if d1[i] != d2[i] {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func compareSymLink(r1, r2 continuity.SymLink) bool {
 | |
| 	return r1.Target() == r2.Target()
 | |
| }
 | |
| 
 | |
| func compareDirectory(r1, r2 continuity.Directory) bool {
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func compareNamedPipe(r1, r2 continuity.NamedPipe) bool {
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func compareDevice(r1, r2 continuity.Device) bool {
 | |
| 	return r1.Major() == r2.Major() && r1.Minor() == r2.Minor()
 | |
| }
 | 
