github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/cmp.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package osutil 21 22 import ( 23 "bytes" 24 "io" 25 "os" 26 ) 27 28 const defaultChunkSize = 16 * 1024 29 30 func filesAreEqualChunked(a, b string, chunkSize int) bool { 31 fa, err := os.Open(a) 32 if err != nil { 33 return false 34 } 35 defer fa.Close() 36 37 fb, err := os.Open(b) 38 if err != nil { 39 return false 40 } 41 defer fb.Close() 42 43 fia, err := fa.Stat() 44 if err != nil { 45 return false 46 } 47 48 fib, err := fb.Stat() 49 if err != nil { 50 return false 51 } 52 53 if fia.Size() != fib.Size() { 54 return false 55 } 56 57 return streamsEqualChunked(fa, fb, chunkSize) 58 } 59 60 // FilesAreEqual compares the two files' contents and returns whether 61 // they are the same. 62 func FilesAreEqual(a, b string) bool { 63 return filesAreEqualChunked(a, b, 0) 64 } 65 66 func streamsEqualChunked(a, b io.Reader, chunkSize int) bool { 67 if a == b { 68 return true 69 } 70 if chunkSize <= 0 { 71 chunkSize = defaultChunkSize 72 } 73 bufa := make([]byte, chunkSize) 74 bufb := make([]byte, chunkSize) 75 for { 76 ra, erra := io.ReadAtLeast(a, bufa, chunkSize) 77 rb, errb := io.ReadAtLeast(b, bufb, chunkSize) 78 if erra == io.EOF && errb == io.EOF { 79 return true 80 } 81 if erra != nil || errb != nil { 82 // if both files finished in the middle of a 83 // ReadAtLeast, (returning io.ErrUnexpectedEOF), then we 84 // still need to check what was read to know whether 85 // they're equal. Otherwise, we know they're not equal 86 // (because we count any read error as a being non-equal 87 // also). 88 tailMightBeEqual := erra == io.ErrUnexpectedEOF && errb == io.ErrUnexpectedEOF 89 if !tailMightBeEqual { 90 return false 91 } 92 } 93 if !bytes.Equal(bufa[:ra], bufb[:rb]) { 94 return false 95 } 96 } 97 } 98 99 // StreamsEqual compares two streams and returns true if both 100 // have the same content. 101 func StreamsEqual(a, b io.Reader) bool { 102 return streamsEqualChunked(a, b, 0) 103 }