fingerprint.ml (3473B)
1 (* Unison file synchronizer: src/fingerprint.ml *) 2 (* Copyright 1999-2020, Benjamin C. Pierce 3 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation, either version 3 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>. 16 *) 17 18 (* NOTE: IF YOU CHANGE TYPE "FINGERPRINT", THE ARCHIVE FORMAT CHANGES; *) 19 (* INCREMENT "UPDATE.ARCHIVEFORMAT" *) 20 type t = string 21 22 let m = Umarshal.string 23 24 let pseudo_prefix = "LEN" 25 26 let pseudo path len = pseudo_prefix ^ (Uutil.Filesize.toString len) ^ "@" ^ 27 (Digest.string (Path.toString path)) 28 29 let ispseudo f = Util.startswith f pseudo_prefix 30 31 (* Assumes that (fspath, path) is a file and gives its ``digest '', that is *) 32 (* a short string of cryptographic quality representing it. *) 33 let file fspath path = 34 let f = Fspath.concat fspath path in 35 Util.convertUnixErrorsToTransient 36 ("digesting " ^ Fspath.toPrintString f) 37 (fun () -> 38 let ic = Fs.open_in_bin f in 39 try 40 let d = Digest.channel ic (-1) in 41 close_in ic; 42 d 43 with e -> 44 close_in_noerr ic; 45 raise e 46 ) 47 48 let maxLength = Uutil.Filesize.ofInt max_int 49 let subfile path offset len = 50 if len > maxLength then 51 raise (Util.Transient 52 (Format.sprintf "File '%s' too big for fingerprinting" 53 (Fspath.toPrintString path))); 54 Util.convertUnixErrorsToTransient 55 "digesting subfile" 56 (fun () -> 57 let inch = Fs.open_in_bin path in 58 begin try 59 LargeFile.seek_in inch offset; 60 let res = Digest.channel inch (Uutil.Filesize.toInt len) in 61 close_in inch; 62 res 63 with 64 End_of_file -> 65 close_in_noerr inch; 66 raise (Util.Transient 67 (Format.sprintf 68 "Error in digesting subfile '%s': truncated file" 69 (Fspath.toPrintString path))) 70 | e -> 71 close_in_noerr inch; 72 raise e 73 end) 74 75 let int2hexa quartet = 76 if quartet < 10 then 77 (char_of_int ((int_of_char '0') + quartet)) 78 else char_of_int ((int_of_char 'a') + quartet - 10) 79 80 let hexaCode theChar = 81 let intCode = int_of_char theChar in 82 let first = intCode / 16 in 83 let second = intCode mod 16 in 84 (int2hexa first, int2hexa second) 85 86 let toString md5 = 87 if ispseudo md5 then md5 else begin 88 let length = String.length md5 in 89 let string = Bytes.create (length * 2) in 90 for i=0 to (length - 1) do 91 let c1, c2 = hexaCode (md5.[i]) in 92 Bytes.set string (2*i) c1; 93 Bytes.set string (2*i + 1) c2; 94 done; 95 Bytes.to_string string 96 end 97 98 let dummy = "" 99 100 let hash d = 101 let l = String.length d in 102 if l = 0 then 103 1234577 104 else begin 105 assert (l >= 3); 106 Char.code (String.unsafe_get d 0) + 107 (Char.code (String.unsafe_get d 1) lsl 8) + 108 (Char.code (String.unsafe_get d 2) lsl 16) 109 end 110 111 let equal (d : string) d' = d = d'