Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
tag.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
(* tags are the only thing in littlefs stored in big-endian. *) (* be careful when editing to remember this :) *) [%%cenum type abstract_type = | LFS_TYPE_NAME [@id 0x0] (* associates IDs with file names and file types OR initializes them as files, directories, or superblocks *) | LFS_TYPE_STRUCT [@id 0x2] (* gives an id a structure (inline or CTZ) *) | LFS_TYPE_USERATTR [@id 0x3] (* 'user-defined', gross. "currently no standard user attributes" so we can just ignore them *) | LFS_TYPE_SPLICE [@id 0x4] (* create or delete file with a given ID depending on chunk info *) | LFS_TYPE_CRC [@id 0x5] (* CRC-32 for commits to the metadata block; polynomial of 0x04c11db7 initialized with 0xffffffff *) | LFS_TYPE_TAIL [@id 0x6] (* tail pointer for the metadata pair; hard or soft *) | LFS_TYPE_GSTATE [@id 0x7] (* global state entries; currently, only movestate *) (* data checksummed includes all metadata since previous CRC tag, including the CRC tag itself *) [@@uint8_t]] module Magic = struct let struct_dir = 0x00 let struct_inline = 0x01 let struct_ctz = 0x02 let tail_soft = 0x00 let tail_hard = 0x01 let not_associated = 0x3ff (* special value for "id" field *) let deleted_tag = 0x3ff (* special value for "length" field *) let = [ Cstruct.of_string "\x00\x00\x00\x00"; Cstruct.of_string "\xff\xff\xff\xff"; ] end type t = { valid : bool; type3 : (abstract_type * Cstruct.uint8); id : int; length : int; } let size = 4 (* tags are always 32 bits, with internal numerical representations big-endian *) let pp fmt tag = Format.fprintf fmt "id %d (%x), length %d (%x), valid %b, type is %x with chunk %x" tag.id tag.id tag.length tag.length (not tag.valid) (abstract_type_to_int (fst tag.type3)) (snd tag.type3) let xor ~into arg = (* it doesn't really need to be tags, it could be any 4-byte cstruct *) for i = 0 to 3 do let new_byte = (Cstruct.get_uint8 into i) lxor (Cstruct.get_uint8 arg i) in Cstruct.set_uint8 into i new_byte done let is_file_struct tag = (fst tag.type3) = LFS_TYPE_STRUCT && ((snd tag.type3) = Magic.struct_dir || snd tag.type3 = Magic.struct_ctz) let is_hardtail {type3; _} = (fst type3) = LFS_TYPE_TAIL && (snd type3) = Magic.tail_hard let has_links tag = is_file_struct tag || is_hardtail tag let delete id = { valid = true; type3 = ( LFS_TYPE_SPLICE, 0xff ); id; length = 0; } let of_cstruct ~xor_tag_with cs = let tag_region = Cstruct.sub cs 0 size in if List.exists (Cstruct.equal tag_region) Magic.invalid_tags then Error (`Msg "invalid tag") else begin xor ~into:cs xor_tag_with; if List.exists (Cstruct.equal (Cstruct.sub cs 0 size)) Magic.invalid_tags then Error (`Msg "invalid tag") else begin let r32 = Cstruct.BE.get_uint32 cs 0 in let r = Int32.to_int r32 in let valid = Int.compare (Cstruct.get_uint8 cs 0) 128 < 0 and abstract_type = (r lsr 28) land 0x7 |> int_to_abstract_type and chunk = (r lsr 20) land 0xff and id = (r lsr 10) land 0x3ff and length = r land 0x3ff in match abstract_type with | None -> Error (`Msg "invalid abstract type in metadata tag") | Some abstract_type -> let type3 = abstract_type, chunk in Ok {valid; type3; id; length} end end let into_cstruct_raw cs t = let abstract_type, chunk = t.type3 in let id = t.id land 0x3ff and length = t.length land 0x3ff in (* most significant bit (31): valid or no? *) (* this is inverted from what we'd expect the value to be -- * the spec isn't as explicit about this as I would be if I were writing something * where 1 was no and 0 was yes :/ *) let byte0 = if t.valid then 0x00 else 0x80 in (* bits 30, 29, and 28: abstract type *) let shifted_type = (0x7 land (abstract_type_to_int abstract_type)) lsl 4 in let byte0 = byte0 lor shifted_type in (* bits 27, 26, 25, 24 : first nibble of chunk *) let chunk_msb = (0xf0 land chunk) lsr 4 in let byte0 = byte0 lor chunk_msb in Cstruct.set_uint8 cs 0 byte0; (* bits 23, 22, 21, 20 : second nibble of chunk *) let byte1 = (0x0f land chunk) lsl 4 in (* bits 19, 18, 17, 16 : most significant 4 bits of id *) let id_4_msb = (0x3c0 land id) lsr 6 in let byte1 = byte1 lor id_4_msb in Cstruct.set_uint8 cs 1 byte1; (* bits 15, 14, 13, 12, 11, 10 : least significant 6 bits of id *) let byte2 = (0x03f land id) lsl 2 in (* bits 9, 8 : most significant 2 bits of length *) let length_2_msb = (0x300 land length) lsr 8 in let byte2 = byte2 lor length_2_msb in Cstruct.set_uint8 cs 2 byte2; (* bits 7, 6, 5, 4, 3, 2, 1, 0 : least significant 8 bits of length *) let byte3 = length land 0xff in Cstruct.set_uint8 cs 3 byte3 let to_cstruct_raw t = let cs = Cstruct.create 4 in into_cstruct_raw cs t; cs let into_cstruct ~xor_tag_with cs t = into_cstruct_raw cs t; xor ~into:cs xor_tag_with let to_cstruct ~xor_tag_with t = let cs = Cstruct.create 4 in into_cstruct ~xor_tag_with cs t; cs