sylveos

Toy Operating System
Log | Files | Refs

fatvfs.zig (10270B)


      1 const std = @import("std");
      2 
      3 const vfs = @import("./vfs.zig");
      4 const fat = @import("./fat.zig");
      5 
      6 // TODO;
      7 // - writing
      8 // - use arenas in place of FileSystem's allocator?
      9 
     10 const Error = vfs.Error;
     11 
     12 pub const DirectoryEntry = struct {
     13     name: []const u8,
     14     entry: fat.DirectoryEntry,
     15 
     16     fs: *const Self,
     17 
     18     pub fn close(self: *DirectoryEntry) void {
     19         self.fs.alloc.free(self.name);
     20         self.fs.alloc.destroy(self);
     21     }
     22 
     23     pub fn stat(self: *const DirectoryEntry) vfs.Stat {
     24         const attr = self.entry.attributes;
     25 
     26         return .{
     27             .name = self.name,
     28             .read_only = attr.read_only,
     29             .hidden = attr.hidden,
     30             .directory = attr.subdirectory,
     31 
     32             .creation_time = self.entry.create_date.into_seconds() + self.entry.creation_time.into_seconds(),
     33             .modify_time = self.entry.modify_date.into_seconds() + self.entry.modify_time.into_seconds(),
     34             .access_time = self.entry.access_date.into_seconds(),
     35 
     36             .size = self.entry.number_bytes,
     37         };
     38     }
     39 };
     40 
     41 pub const File = struct {
     42     first_cluster: u32,
     43     size: u32,
     44 
     45     current_cluster: u32,
     46     remaining: u32,
     47 
     48     fs: *const Self,
     49 
     50     pub fn close(self: *File) void {
     51         self.fs.alloc.destroy(self);
     52     }
     53 
     54     // TODO; should we read by even smaller increments (by sector?)
     55     pub fn read(self: *File) Error!?[]u8 {
     56         if (self.remaining == 0) return null;
     57 
     58         const to_alloc = @min(self.remaining, @sizeOf(vfs.Sector) * @as(usize, self.fs.bpb.sectors_per_cluster));
     59         const file: []u8 = try self.fs.alloc.alloc(u8, to_alloc);
     60 
     61         const contents = try self.fs.disk.read_sectors(
     62             self.fs.lba + self.fs.bpb.sector_from_cluster(self.current_cluster),
     63             self.fs.bpb.sectors_per_cluster,
     64         );
     65         defer self.fs.disk.free_sectors(contents);
     66 
     67         const bytes: [*]const u8 = @ptrCast(contents);
     68         @memcpy(file, bytes);
     69 
     70         self.remaining -= to_alloc;
     71 
     72         return file;
     73     }
     74 
     75     // TODO;
     76     pub fn write(self: *File, bytes: []const u8) Error!u32 {
     77         _ = self;
     78         _ = bytes;
     79         return Error.WriteFailed;
     80     }
     81 
     82     pub fn reset(self: *File) Error!void {
     83         self.current_cluster = self.first_cluster;
     84         self.remaining = self.size;
     85     }
     86 
     87     // TODO; seek
     88 };
     89 
     90 pub const DirectoryIterator = struct {
     91     first_cluster: u32,
     92     current_cluster: u32,
     93     current_entry: u32,
     94     fs: *const Self,
     95 
     96     cluster: []vfs.Sector,
     97 
     98     pub fn load(self: *DirectoryIterator) Error!void {
     99         // Load cluster
    100         self.current_entry = 0;
    101         self.cluster = try self.fs.disk.read_sectors(
    102             self.fs.lba + self.fs.bpb.sector_from_cluster(self.current_cluster),
    103             self.fs.bpb.sectors_per_cluster,
    104         );
    105     }
    106 
    107     pub fn close(self: *DirectoryIterator) void {
    108         self.fs.disk.free_sectors(self.cluster);
    109         self.fs.alloc.destroy(self);
    110     }
    111 
    112     pub fn next(self: *DirectoryIterator) Error!?vfs.DirectoryEntry {
    113         // ensure we always increment
    114         defer self.current_entry += 1;
    115 
    116         const len = (@sizeOf(vfs.Sector) * @as(usize, self.fs.bpb.sectors_per_cluster)) / @sizeOf(fat.DirectoryEntry);
    117 
    118         // Max file name length is 512
    119         var long_name_buffer: [256]u16 = undefined;
    120         var long_name_list: std.ArrayList(u16) = .initBuffer(&long_name_buffer);
    121 
    122         // Find next actual entry (and collect together LFN)
    123         const entries: [*]fat.DirectoryEntry = @ptrCast(self.cluster);
    124         while (true) : (self.current_entry += 1) {
    125             if (self.current_entry == len) {
    126                 // Cycle into new cluster
    127                 self.current_cluster = try self.fs.next_cluster(self.current_cluster) orelse return null;
    128                 self.fs.disk.free_sectors(self.cluster);
    129 
    130                 try self.load();
    131             }
    132 
    133             const entry = entries[self.current_entry];
    134             const bytes: [*]const u8 = @ptrCast(&entry);
    135 
    136             if (bytes[0] == 0x00) return null;
    137             if (bytes[0] == 0xE5) continue;
    138             if (entry.attributes.volume_label and !entry.is_long_filename()) continue;
    139 
    140             if (entry.is_long_filename()) {
    141                 const lfn = entry.as_lfn();
    142                 const name = lfn.name_ucs_2();
    143 
    144                 for (0..name.len) |i| {
    145                     const rev = (name.len - 1 - i);
    146 
    147                     if (rev == 0xFFFF) continue;
    148 
    149                     if (long_name_list.items.len == long_name_buffer.len) return Error.InvalidFileName;
    150                     long_name_list.appendAssumeCapacity(name[rev]);
    151                 }
    152 
    153                 continue;
    154             }
    155 
    156             var vfs_entry = try self.fs.alloc.create(DirectoryEntry);
    157             vfs_entry.entry = entry;
    158             vfs_entry.fs = self.fs;
    159 
    160             if (long_name_list.items.len > 0) {
    161                 // End of LFN chain
    162                 std.mem.reverse(u16, long_name_list.items);
    163 
    164                 const name = std.unicode.utf16LeToUtf8Alloc(
    165                     self.fs.alloc,
    166                     long_name_list.items,
    167                 ) catch return Error.InvalidFileName;
    168 
    169                 vfs_entry.name = for (name, 0..) |b, idx| {
    170                     if (b == 0) {
    171                         const new_name = try self.fs.alloc.dupe(u8, name[0..idx]);
    172                         self.fs.alloc.free(name);
    173 
    174                         break new_name;
    175                     }
    176                 } else name;
    177 
    178                 long_name_list.items.len = 0;
    179 
    180                 return vfs.DirectoryEntry.impl_by(vfs_entry);
    181             }
    182 
    183             const short_name = entry.filename_str();
    184 
    185             if (short_name[0] == '.') {
    186                 // Exception to 8.3
    187 
    188                 vfs_entry.name = try self.fs.alloc.dupe(
    189                     u8,
    190                     if (short_name[1] == '.') ".." else ".",
    191                 );
    192 
    193                 return vfs.DirectoryEntry.impl_by(vfs_entry);
    194             }
    195 
    196             // 8.3 - max length is 12
    197             var short_name_buffer: [12]u8 = undefined;
    198             var short_name_list: std.ArrayList(u8) = .initBuffer(&short_name_buffer);
    199 
    200             for (short_name) |b| {
    201                 if (b == ' ') break;
    202                 short_name_list.appendAssumeCapacity(b);
    203             }
    204 
    205             short_name_list.appendAssumeCapacity('.');
    206             short_name_list.appendSliceAssumeCapacity(short_name[8..11]);
    207 
    208             vfs_entry.name = try self.fs.alloc.dupe(u8, short_name_list.items);
    209 
    210             return vfs.DirectoryEntry.impl_by(vfs_entry);
    211         }
    212     }
    213 
    214     pub fn reset(self: *DirectoryIterator) Error!void {
    215         self.current_cluster = self.first_cluster;
    216         try self.load();
    217     }
    218 };
    219 
    220 pub const Directory = struct {
    221     first_cluster: u32,
    222     fs: *const Self,
    223 
    224     // FAT means we just need to iterate
    225     pub fn open_entry(self: *Directory, name: []const u8) Error!?vfs.DirectoryEntry {
    226         var it = try self.iterate();
    227         defer it.close();
    228 
    229         while (try it.next()) |entry| {
    230             if (std.mem.eql(u8, entry.stat().name, name)) {
    231                 return entry;
    232             } else {
    233                 entry.close();
    234             }
    235         }
    236 
    237         return null;
    238     }
    239 
    240     pub fn close(self: *Directory) void {
    241         self.fs.alloc.destroy(self);
    242     }
    243 
    244     pub fn iterate(self: *Directory) Error!vfs.DirectoryIterator {
    245         var iter = try self.fs.alloc.create(DirectoryIterator);
    246 
    247         iter.* = .{
    248             .current_cluster = self.first_cluster,
    249             .first_cluster = self.first_cluster,
    250             .current_entry = 0,
    251             .cluster = undefined,
    252             .fs = self.fs,
    253         };
    254 
    255         try iter.load();
    256 
    257         return vfs.DirectoryIterator.impl_by(iter);
    258     }
    259 };
    260 
    261 alloc: std.mem.Allocator,
    262 bpb: fat.Fat32BootSector,
    263 info: fat.FatInfo,
    264 disk: *vfs.Disk,
    265 lba: u32,
    266 
    267 is_initialized: bool,
    268 
    269 const Self = @This();
    270 
    271 pub fn init() Self {
    272     return .{
    273         .is_initialized = false,
    274         .alloc = undefined,
    275         .bpb = undefined,
    276         .info = undefined,
    277         .disk = undefined,
    278         .lba = undefined,
    279     };
    280 }
    281 
    282 pub fn open(self: *Self, alloc: std.mem.Allocator, disk: *vfs.Disk, lba: u32) Error!void {
    283     const bpb: fat.Fat32BootSector = @bitCast(try disk.read_sector(lba));
    284     if (!bpb.is_valid()) return Error.InvalidFileSystem;
    285 
    286     const info: fat.FatInfo = @bitCast(try disk.read_sector(lba + bpb.fat_info));
    287     if (!info.is_valid()) return Error.InvalidFileSystem;
    288 
    289     self.info = info;
    290     self.disk = disk;
    291     self.alloc = alloc;
    292     self.bpb = bpb;
    293     self.lba = lba;
    294     self.is_initialized = true;
    295 }
    296 
    297 pub fn close(self: *Self) void {
    298     self.is_initialized = false;
    299 }
    300 
    301 fn read_cluster_directory(self: *const Self, first_cluster: u32) Error!vfs.Directory {
    302     const directory = try self.alloc.create(Directory);
    303 
    304     directory.* = .{
    305         .first_cluster = first_cluster,
    306         .fs = self,
    307     };
    308 
    309     return vfs.Directory.impl_by(directory);
    310 }
    311 
    312 fn read_cluster_file(self: *const Self, first_cluster: u32, size: u32) Error!vfs.File {
    313     const file = try self.alloc.create(File);
    314 
    315     file.* = .{
    316         .current_cluster = first_cluster,
    317         .first_cluster = first_cluster,
    318         .remaining = size,
    319         .size = size,
    320         .fs = self,
    321     };
    322 
    323     return vfs.File.impl_by(file);
    324 }
    325 
    326 fn next_cluster(self: *const Self, current_cluster: u32) Error!?u32 {
    327     const sector_n = self.bpb.fat_entry_sector(current_cluster) + self.lba;
    328     const sector = try self.disk.read_sector(sector_n);
    329 
    330     const next = self.bpb.fat_entry(current_cluster, sector);
    331 
    332     if (next >= 0x0FFFFFF8) return null;
    333 
    334     return next;
    335 }
    336 
    337 pub fn open_root(self: *const Self) Error!vfs.Directory {
    338     if (!self.is_initialized) return Error.NotInitialized;
    339 
    340     return try self.read_cluster_directory(self.bpb.root_cluster);
    341 }
    342 
    343 pub fn open_directory(self: *const Self, entry: *fat.DirectoryEntry) Error!vfs.Directory {
    344     if (!self.is_initialized) return Error.NotInitialized;
    345 
    346     return try self.read_cluster_directory(entry.first_cluster());
    347 }
    348 
    349 pub fn open_file(self: *const Self, entry: *fat.DirectoryEntry) Error!vfs.File {
    350     if (!self.is_initialized) return Error.NotInitialized;
    351 
    352     return try self.read_cluster_file(entry.first_cluster(), entry.number_bytes);
    353 }