+ eprintf "unknown resident attribute %lx\n%!" attr_type
+
+and parse_nonresident_attr attr_type highest_vcn
+ allocated_size data_size initialized_size
+ mapping_pairs =
+ match attr_type with
+ | 0x80_l -> (* AT_DATA, ie. the $Data stream *)
+ if !debug then (
+ eprintf "AT_DATA: size = %Ld bytes, highest_vcn = 0x%Lx\n"
+ data_size highest_vcn;
+ Bitmatch.hexdump_bitstring Pervasives.stderr mapping_pairs
+ );
+
+ let lowest_vcn = ~^0 (* see assumption above *) in
+ let runlist = parse_runlist lowest_vcn ~^0 mapping_pairs in
+ if !debug then (
+ eprintf "AT_DATA: runlist is:\n";
+ List.iter (
+ function
+ | ((vcn, deltavcn), Some lcn) ->
+ eprintf "\tVCNs %s..%s -> LCN %s\n"
+ (Int63.to_string vcn) (Int63.to_string (vcn +^ deltavcn -^ ~^1))
+ (Int63.to_string lcn)
+ | ((vcn, deltavcn), None) ->
+ eprintf "\tVCNs %s..%s -> sparse hole\n"
+ (Int63.to_string vcn) (Int63.to_string (vcn +^ deltavcn -^ ~^1))
+ ) runlist
+ );
+
+ | _ ->
+ if !debug then
+ eprintf "unknown non-resident attribute %lx\n%!" attr_type
+
+(* mapping_pairs is not straightforward and not documented well. See
+ * ntfsprogs libntfs/runlist.c:ntfs_mapping_pairs_decompress
+ *)
+and parse_runlist vcn lcn bits =
+ bitmatch bits with
+ | { 0 : 8 } -> (* end of table *)
+ []
+
+ | { 0 : 4;
+ vcnlen : 4;
+ deltavcn : vcnlen * 8 : littleendian;
+ rest : -1 : bitstring
+ } when vcnlen >= 1 && vcnlen <= 4 ->
+
+ let deltavcn = Int63.of_int64 deltavcn in
+
+ (* This is a sparse file hole. *)
+ ((vcn, deltavcn), None) ::
+ parse_runlist (vcn +^ deltavcn) lcn rest
+
+ | { (* Really these fields are signed, but we'll just limit it to
+ * sensible values in the when clause instead.
+ *)
+ lcnlen : 4;
+ vcnlen : 4;
+ deltavcn : vcnlen * 8 : littleendian;
+ deltalcn : lcnlen * 8 : littleendian;
+ rest : -1 : bitstring
+ } when (vcnlen >= 1 && vcnlen <= 4) && (lcnlen >= 1 || lcnlen <= 4) ->
+
+ let deltavcn = Int63.of_int64 deltavcn in
+ let deltalcn = Int63.of_int64 deltalcn in (* XXX signed *)
+
+ let lcn = lcn +^ deltalcn in
+
+ eprintf "lcnlen = %d, vcnlen = %d\n" lcnlen vcnlen;
+
+ ((vcn, deltavcn), Some lcn) ::
+ parse_runlist (vcn +^ deltavcn) lcn rest
+
+ | { _ } ->
+ if !debug then (
+ eprintf "unknown field in the runlist\n%!";
+ Bitmatch.hexdump_bitstring Pervasives.stderr bits
+ );
+ []