unison

Fork of Unison, a bi-directional file synchronization tool
git clone git://git.laack.co/unison.git
Log | Files | Refs | README | LICENSE

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'