github.com/rminnich/u-root@v7.0.0+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  }