package filesystem import ( "bufio" "bytes" "io" "os" ) const defaultBufferSize = 4096 // FileEqualsTarget will read in the file from path and compare its contents // to the target bytes. func FileEqualsTarget(path string, targetBytes []byte) (bool, error) { fileInfo, err := os.Stat(path) if err != nil { return false, err } if fileInfo.Size() != int64(len(targetBytes)) { return false, nil } file, closeFn, err := newReader(path) if err != nil { return false, err } defer closeFn() //nolint:errcheck bytesBuffer := bytes.NewReader(targetBytes) return Equal(file, bytesBuffer, defaultBufferSize) } // FilesEqual will read in two file from path1 and path2 and compare the contents // of each file and return true if they are equal. func FilesEqual(path1, path2 string) (bool, error) { file1Info, err := os.Stat(path1) if err != nil { return false, err } file2Info, err := os.Stat(path2) if err != nil { return false, err } if file1Info.Size() != file2Info.Size() { return false, nil } file1, closeFn1, err := newReader(path1) if err != nil { return false, err } defer closeFn1() //nolint:errcheck file2, closeFn2, err := newReader(path2) if err != nil { return false, err } defer closeFn2() //nolint:errcheck return Equal(file1, file2, defaultBufferSize) } // Equal iterates through each reader chunk by chunk, evaluating // if each chunk is equal between the readers. func Equal(r1, r2 io.Reader, bufferSize int) (bool, error) { if bufferSize <= 0 { bufferSize = defaultBufferSize } buf1 := make([]byte, bufferSize) buf2 := make([]byte, bufferSize) for { n1, err1 := readFull(r1, buf1) n2, err2 := readFull(r2, buf2) // Check for non-EOF errors first if err1 != nil && err1 != io.EOF { return false, err1 } if err2 != nil && err2 != io.EOF { return false, err2 } // Compare the read data if n1 != n2 || !bytes.Equal(buf1[:n1], buf2[:n2]) { return false, nil } // Handle EOF cases if err1 == io.EOF && err2 == io.EOF { return true, nil // Both readers reached EOF simultaneously } if err1 == io.EOF || err2 == io.EOF { return false, nil // One reader reached EOF before the other } } } // readFull reads from r into buf, handling short reads func readFull(r io.Reader, buf []byte) (int, error) { n, err := io.ReadFull(r, buf) if err == io.ErrUnexpectedEOF { return n, io.EOF } return n, err } func newReader(path string) (io.Reader, func() error, error) { base, err := os.Open(path) if err != nil { return nil, nil, err } baseBuffer := bufio.NewReader(base) return baseBuffer, base.Close, nil }