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 }