github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/cp/cmp/cmp.go (about) 1 // Copyright 2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package cmp compares trees of files. 6 // 7 // cmp is an internal package for pkg/cp's and cmds/core/cp's tests. 8 package cmp 9 10 import ( 11 "fmt" 12 "io/ioutil" 13 "os" 14 "path/filepath" 15 "reflect" 16 17 "github.com/u-root/u-root/pkg/cp" 18 "github.com/u-root/u-root/pkg/uio" 19 ) 20 21 // isEqualFile compare two files by checksum 22 func isEqualFile(fpath1, fpath2 string) error { 23 file1, err := os.Open(fpath1) 24 if err != nil { 25 return err 26 } 27 defer file1.Close() 28 file2, err := os.Open(fpath2) 29 if err != nil { 30 return err 31 } 32 defer file2.Close() 33 34 if !uio.ReaderAtEqual(file1, file2) { 35 return fmt.Errorf("%q and %q do not have equal content", fpath1, fpath2) 36 } 37 return nil 38 } 39 40 func readDirNames(path string) ([]string, error) { 41 entries, err := ioutil.ReadDir(path) 42 if err != nil { 43 return nil, err 44 } 45 var basenames []string 46 for _, entry := range entries { 47 basenames = append(basenames, entry.Name()) 48 } 49 return basenames, nil 50 } 51 52 func stat(o cp.Options, path string) (os.FileInfo, error) { 53 if o.NoFollowSymlinks { 54 return os.Lstat(path) 55 } 56 return os.Stat(path) 57 } 58 59 // IsEqualTree compare the content in the file trees in src and dst paths 60 func IsEqualTree(o cp.Options, src, dst string) error { 61 srcInfo, err := stat(o, src) 62 if err != nil { 63 return err 64 } 65 dstInfo, err := stat(o, dst) 66 if err != nil { 67 return err 68 } 69 if sm, dm := srcInfo.Mode()&os.ModeType, dstInfo.Mode()&os.ModeType; sm != dm { 70 return fmt.Errorf("mismatched mode: %q has mode %s while %q has mode %s", src, sm, dst, dm) 71 } 72 73 switch { 74 case srcInfo.Mode().IsDir(): 75 srcEntries, err := readDirNames(src) 76 if err != nil { 77 return err 78 } 79 dstEntries, err := readDirNames(dst) 80 if err != nil { 81 return err 82 } 83 // ioutil.ReadDir guarantees these are sorted. 84 if !reflect.DeepEqual(srcEntries, dstEntries) { 85 return fmt.Errorf("directory contents did not match:\n%q had %v\n%q had %v", src, srcEntries, dst, dstEntries) 86 } 87 for _, basename := range srcEntries { 88 if err := IsEqualTree(o, filepath.Join(src, basename), filepath.Join(dst, basename)); err != nil { 89 return err 90 } 91 } 92 return nil 93 94 case srcInfo.Mode().IsRegular(): 95 return isEqualFile(src, dst) 96 97 case srcInfo.Mode()&os.ModeSymlink == os.ModeSymlink: 98 srcTarget, err := os.Readlink(src) 99 if err != nil { 100 return err 101 } 102 dstTarget, err := os.Readlink(dst) 103 if err != nil { 104 return err 105 } 106 if srcTarget != dstTarget { 107 return fmt.Errorf("target mismatch: symlink %q had target %q, while %q had target %q", src, srcTarget, dst, dstTarget) 108 } 109 return nil 110 111 default: 112 return fmt.Errorf("unsupported mode: %s", srcInfo.Mode()) 113 } 114 }