sylveos

Toy Operating System
Log | Files | Refs

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(&sector);
     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 };