fat.zig (10663B)
1 const std = @import("std"); 2 3 pub const vfs = @import("./vfs.zig"); 4 5 pub const Error = 6 std.mem.Allocator.Error || 7 std.unicode.Utf16LeToUtf8Error || 8 vfs.Error; 9 10 pub const Sector = vfs.Sector; 11 pub const Disk = vfs.Disk; 12 13 pub const Fat32BootSector = packed struct(Sector) { 14 // u8[3] 15 asm_code: u24, 16 // u8[8] 17 oem: u64, 18 bytes_per_sector: u16, 19 sectors_per_cluster: u8, 20 reserved_sectors: u16, 21 table_count: u8, 22 root_entry_count: u16, 23 total_sectors_16: u16, 24 media_type: u8, 25 table_size_16: u16, 26 sectors_per_track: u16, 27 head_side_count: u16, 28 hidden_sectors: u32, 29 total_sectors_32: u32, 30 31 // Fat32 Specific 32 table_size_32: u32, 33 extended_flags: u16, 34 fat_version: u16, 35 root_cluster: u32, 36 fat_info: u16, 37 backup_boot_location: u16, 38 // u8[12] 39 _reserved0: u96, 40 logical_drive_number: u8, 41 _reserved1: u8, 42 extended_signature: u8, 43 volume_id: u32, 44 // u8[11] 45 volume_label: u88, 46 // u8[8] 47 fs_type: u64, 48 // u8[420] 49 boot_code: u3360, 50 // 0xAA55 51 signature: u16, 52 53 const Self = @This(); 54 55 pub fn oem_str(self: *const Self) [8]u8 { 56 var res: [8]u8 = undefined; 57 std.mem.writeInt(u64, &res, self.oem, .little); 58 return res; 59 } 60 61 pub fn volume_label_str(self: *const Self) [11]u8 { 62 var res: [11]u8 = undefined; 63 std.mem.writeInt(u88, &res, self.volume_label, .little); 64 return res; 65 } 66 67 pub fn fs_type_str(self: *const Self) [8]u8 { 68 var res: [8]u8 = undefined; 69 std.mem.writeInt(u64, &res, self.fs_type, .little); 70 return res; 71 } 72 73 pub inline fn first_fat_sector(self: *const Self) u32 { 74 return self.reserved_sectors; 75 } 76 77 pub inline fn first_data_sector(self: *const Self) u32 { 78 return self.first_fat_sector() + (self.table_count * self.table_size_32); 79 } 80 81 pub inline fn sector_from_cluster(self: *const Self, cluster: u32) u32 { 82 return ((cluster - 2) * self.sectors_per_cluster) + self.first_data_sector(); 83 } 84 85 pub inline fn fat_entry_sector(self: *const Self, cluster: u32) u32 { 86 const fat_offset = cluster * 4; 87 return self.first_fat_sector() + (fat_offset / self.bytes_per_sector); 88 } 89 90 pub inline fn fat_entry(self: *const Self, cluster: u32, sector: Sector) u32 { 91 const fat_offset = cluster * 4; 92 const entry_offset = fat_offset % self.bytes_per_sector; 93 94 const sector_bytes: [*]const u32 = @ptrCast(§or); 95 96 return sector_bytes[entry_offset / 4] & @as(u32, 0x0FFFFFFF); 97 } 98 99 pub inline fn is_valid(self: *const Self) bool { 100 // TODO; check if FAT32 specifically 101 return self.signature == 0xAA55; 102 } 103 104 pub fn debug_print(self: *const Self, w: *std.io.Writer) !void { 105 try w.print( 106 \\ oem = {s} 107 \\ bytes_per_sector = {d} 108 \\ sectors_per_cluster = {d} 109 \\ reserved_sectors = {d} 110 \\ table_count = {d} 111 \\ root_entry_count = {d} 112 \\ total_sectors_16 = {d} 113 \\ media_type = 0x{X} 114 \\ table_size_16 = {d} 115 \\ sectors_per_track = {d} 116 \\ head_side_count = {d} 117 \\ hidden_sectors = {d} 118 \\ total_sectors_32 = {d} 119 \\ table_size_32 = {d} 120 \\ extended_flags = 0x{X} 121 \\ version = {d} 122 \\ root_cluster = 0x{X} 123 \\ fat_info = 0x{X} 124 \\ backup_boot_location = 0x{X} 125 \\ logical_drive_number = {d} 126 \\ extended_signature = 0x{X} 127 \\ volume_id = 0x{X} 128 \\ volume_label = {s} 129 \\ fs_type = {s} 130 \\ signature = 0x{X} 131 \\ 132 , .{ 133 self.oem_str(), 134 self.bytes_per_sector, 135 self.sectors_per_cluster, 136 self.reserved_sectors, 137 self.table_count, 138 self.root_entry_count, 139 self.total_sectors_16, 140 self.media_type, 141 self.table_size_16, 142 self.sectors_per_track, 143 self.head_side_count, 144 self.hidden_sectors, 145 self.total_sectors_32, 146 self.table_size_32, 147 self.extended_flags, 148 self.fat_version, 149 self.root_cluster, 150 self.fat_info, 151 self.backup_boot_location, 152 self.logical_drive_number, 153 self.extended_signature, 154 self.volume_id, 155 self.volume_label_str(), 156 self.fs_type_str(), 157 self.signature, 158 }); 159 } 160 }; 161 162 pub const FatInfo = packed struct(Sector) { 163 // 0x41615252 164 signature_1: u32, 165 // u8[480] 166 _reserved_0: u3840, 167 // 0x61417272 168 signature_2: u32, 169 free_cluster_count: u32, 170 next_free_cluster: u32, 171 // u8[12] 172 _reserved_1: u96, 173 // 0xAA550000 174 signature_3: u32, 175 176 const Self = @This(); 177 178 pub inline fn is_valid(self: *const Self) bool { 179 return self.signature_1 == 0x41615252 and self.signature_2 == 0x61417272 and self.signature_3 == 0xAA550000; 180 } 181 182 pub fn debug_print(self: *const Self, w: *std.io.Writer) !void { 183 try w.print( 184 \\ signature_1 = 0x{X} 185 \\ signature_2 = 0x{X} 186 \\ free_cluster_count = {d} 187 \\ next_free_cluster = 0x{X} 188 \\ signature_3 = 0x{X} 189 \\ 190 , .{ 191 self.signature_1, 192 self.signature_2, 193 self.free_cluster_count, 194 self.next_free_cluster, 195 self.signature_3, 196 }); 197 } 198 }; 199 200 pub const DirectoryEntry = packed struct(u256) { 201 pub const Attributes = packed struct(u8) { 202 read_only: bool, 203 hidden: bool, 204 system_file: bool, 205 volume_label: bool, 206 subdirectory: bool, 207 archive: bool, 208 _unused: u2, 209 }; 210 211 pub const Time = packed struct(u16) { 212 seconds: u5, 213 minutes: u6, 214 hour: u5, 215 216 pub fn into_seconds(self: Time) i32 { 217 return @as(i32, self.hour) * std.time.s_per_hour + 218 @as(i32, self.minutes) * std.time.s_per_min + 219 @as(i32, self.seconds); 220 } 221 }; 222 223 pub const Date = packed struct(u16) { 224 day: u5, 225 month: u4, 226 year: u7, 227 228 pub fn into_seconds(self: Date) i32 { 229 var days: i32 = 0; 230 231 // Add 1970 - 1980 232 for (0..10) |y| { 233 days += std.time.epoch.getDaysInYear(1970 + @as(u16, @truncate(y))); 234 } 235 236 for (0..self.year) |y| { 237 days += std.time.epoch.getDaysInYear(1980 + @as(u16, @truncate(y))); 238 } 239 240 days += std.time.epoch.getDaysInMonth(self.year, @enumFromInt(self.month)); 241 days += self.day; 242 243 return days * std.time.s_per_day; 244 } 245 }; 246 247 // u8[11] 248 // 8.3 file name 249 // first 8 are name, last 3 are ext 250 filename: u88, 251 attributes: Attributes, 252 _reserved: u8, 253 creation_time_tenths: u8, 254 creation_time: Time, 255 create_date: Date, 256 access_date: Date, 257 high_start: u16, 258 modify_time: Time, 259 modify_date: Date, 260 low_start: u16, 261 number_bytes: u32, 262 263 const Self = @This(); 264 265 pub fn filename_str(self: *const Self) [11]u8 { 266 var res: [11]u8 = undefined; 267 std.mem.writeInt(u88, &res, self.filename, .little); 268 return res; 269 } 270 271 pub fn is_long_filename(self: *const Self) bool { 272 return @as(u8, @bitCast(self.attributes)) == 0x0F; 273 } 274 275 pub fn as_lfn(self: *const Self) *const LFNDirectoryEntry { 276 return @ptrCast(self); 277 } 278 279 pub fn calculate_lfn_checksum(self: *const Self) u8 { 280 var buffer: [11]u8 = undefined; 281 std.mem.writeInt(u88, &buffer, self.filename, .little); 282 283 var checksum = 0; 284 for (0..11) |i| { 285 checksum = ((checksum & 1) << 7) + (checksum >> 1) + buffer[i]; 286 } 287 return checksum; 288 } 289 290 pub inline fn is_free(self: *const Self) bool { 291 if (self.is_long_filename()) { 292 const lfn = self.as_lfn() catch unreachable; 293 return lfn.is_deleted(); 294 } else { 295 return self.filename_str()[0] == 0 or self.filename_str()[0] == 0xE5; 296 } 297 } 298 299 pub fn first_cluster(self: *const Self) u32 { 300 return (@as(u32, self.high_start) << 16) | @as(u32, self.low_start); 301 } 302 303 pub fn debug_print(self: *const Self, w: *std.io.Writer) !void { 304 try w.print( 305 \\ filename = {s} 306 \\ attributes = 0b{b} 307 \\ creation_time_tenths = {} 308 \\ creation_time = {:0>2}:{:0>2}:{:0>2} 309 \\ create_date = {:0>4}/{:0>2}/{:0>2} 310 \\ access_date = {:0>4}/{:0>2}/{:0>2} 311 \\ modify_time = {:0>2}:{:0>2}:{:0>2} 312 \\ modify_date = {:0>4}/{:0>2}/{:0>2} 313 \\ first_cluster = {} 314 \\ number_bytes = {B} 315 \\ 316 , .{ 317 self.filename_str(), 318 @as(u8, @bitCast(self.attributes)), 319 self.creation_time_tenths, 320 self.creation_time.hour, 321 self.creation_time.minutes, 322 self.creation_time.seconds, 323 self.create_date.year, 324 self.create_date.month, 325 self.create_date.day, 326 self.access_date.year, 327 self.access_date.month, 328 self.access_date.day, 329 self.modify_time.hour, 330 self.modify_time.minutes, 331 self.modify_time.seconds, 332 self.modify_date.year, 333 self.modify_date.month, 334 self.modify_date.day, 335 self.first_cluster(), 336 self.number_bytes, 337 }); 338 } 339 }; 340 341 pub const LFNDirectoryEntry = packed struct(u256) { 342 sequence_number: u8, 343 // u8[10] 344 name1_5: u80, 345 attributes: DirectoryEntry.Attributes, 346 _reserved0: u8, 347 checksum: u8, 348 // u8[12] 349 name6_11: u96, 350 _reserved1: u16, 351 // u8[4] 352 name12_13: u32, 353 354 const Self = @This(); 355 356 pub fn is_deleted(self: *const Self) bool { 357 return (self.sequence_number & 0xE5) == 0xE5; 358 } 359 360 pub fn is_last(self: *const Self) bool { 361 return (self.sequence_number & (1 << 6)) != 0; 362 } 363 364 pub fn is_first(self: *const Self) bool { 365 return (self.sequence_number & ~(1 << 6)) == 1; 366 } 367 368 pub fn name_ucs_2(self: *const Self) [13]u16 { 369 var res: [26]u8 = .{0} ** 26; 370 371 std.mem.writeInt(u80, res[0..10], self.name1_5, .little); 372 std.mem.writeInt(u96, res[10..22], self.name6_11, .little); 373 std.mem.writeInt(u32, res[22..26], self.name12_13, .little); 374 375 return @bitCast(res); 376 } 377 };