From 4afecd650848804b4c24a0d39ae4a37a527a4078 Mon Sep 17 00:00:00 2001 From: DrDeano Date: Mon, 4 Jan 2021 08:50:20 +0000 Subject: [PATCH] See description Added finding the next free cluster Added name to long name Added long name to short name Added tests for above Added createLongNameEntry + tests Moved tests to bottom Added createShortNameEntry + test Used the RTC for the date and time for the created short entry Tidied createEntries A bit of refactor No symlinks for FAT32 findNextFreeCluster updates FAT Reordered tests to better follow the FAT32FS code FAT32 has no support for symlinks, so removed code around this. Removed open_args from createNode as it doesn't need it Added writeEntries + tests Write the short and long entries to disk findNextFreeCluster update cluster chain with parent cluster Added FAT32 write + tests This Added the ability to create files and directories and write to files. Added location of the short dir entry for the file so can update the size of the file on disk Added folders to the test FAT32 directory. Also fixed minor bug in mkfat32 Added check for destroying the filesystem Fixed error message for cluster size Simpler if condition 0x0FFFFFFF => 0xFFFFFFFF Spelling Fixed test --- .gitignore | 1 + mkfat32.zig | 4 +- src/kernel/arch/x86/arch.zig | 25 + src/kernel/arch/x86/rtc.zig | 140 +- src/kernel/filesystem/fat32.zig | 3897 +++++++++++++---- src/kernel/filesystem/initrd.zig | 24 +- src/kernel/filesystem/vfs.zig | 16 +- src/kernel/kmain.zig | 4 +- test/fat32/test_files/file~a.txt | 1 + test/fat32/test_files/folder1/file1.txt | 1 + .../test_files/folder1/folder2/file2.txt | 1 + .../folder1/folder2/folder3/file3.txt | 1 + .../folder1/folder2/folder3/folder4/file4.txt | 1 + .../folder2/folder3/folder4/folder5/file5.txt | 1 + .../folder3/folder4/folder5/folder6/file6.txt | 1 + .../folder4/folder5/folder6/folder7/file7.txt | 1 + .../folder5/folder6/folder7/folder8/file8.txt | 1 + .../folder6/folder7/folder8/folder9/file9.txt | 1 + test/mock/kernel/arch_mock.zig | 30 +- 19 files changed, 3192 insertions(+), 959 deletions(-) create mode 100644 test/fat32/test_files/file~a.txt create mode 100644 test/fat32/test_files/folder1/file1.txt create mode 100644 test/fat32/test_files/folder1/folder2/file2.txt create mode 100644 test/fat32/test_files/folder1/folder2/folder3/file3.txt create mode 100644 test/fat32/test_files/folder1/folder2/folder3/folder4/file4.txt create mode 100644 test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/file5.txt create mode 100644 test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/file6.txt create mode 100644 test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/file7.txt create mode 100644 test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8/file8.txt create mode 100644 test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8/folder9/file9.txt diff --git a/.gitignore b/.gitignore index e0a6c17..bee6ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ # Custom ignore **/mock_framework.zig **/*.ramdisk +**/*.img diff --git a/mkfat32.zig b/mkfat32.zig index e5c3267..b8ea3a3 100644 --- a/mkfat32.zig +++ b/mkfat32.zig @@ -523,8 +523,8 @@ pub const Fat32 = struct { } // Valid clusters are 1, 2, 4, 8, 16, 32, 64 and 128 - if (options.cluster_size < 0 or options.cluster_size > 128 or !std.math.isPowerOfTwo(options.cluster_size)) { - log.err("Sectors per cluster is invalid. Must be less then 32 and a power of 2. Found: {}", .{options.cluster_size}); + if (options.cluster_size < 1 or options.cluster_size > 128 or !std.math.isPowerOfTwo(options.cluster_size)) { + log.err("Sectors per cluster is invalid. Must be less then or equal to 128 and a power of 2. Found: {}", .{options.cluster_size}); return Error.InvalidOptionValue; } diff --git a/src/kernel/arch/x86/arch.zig b/src/kernel/arch/x86/arch.zig index aad9cbf..6864f5a 100644 --- a/src/kernel/arch/x86/arch.zig +++ b/src/kernel/arch/x86/arch.zig @@ -30,6 +30,9 @@ const MemProfile = mem.MemProfile; /// The type of a device. pub const Device = pci.PciDeviceInfo; +/// The type of the date and time structure. +pub const DateTime = rtc.DateTime; + /// The virtual end of the kernel code. extern var KERNEL_VADDR_END: *u32; @@ -565,10 +568,32 @@ pub fn initTask(task: *Task, entry_point: usize, allocator: *Allocator) Allocato task.stack_pointer = @ptrToInt(&stack.*[kernel_stack_bottom]); } +/// +/// Get a list of hardware devices attached to the system. +/// +/// Arguments: +/// IN allocator: *Allocator - An allocator for getting the devices +/// +/// Return: []Device +/// A list of hardware devices. +/// +/// Error: Allocator.Error +/// OutOfMemory - Unable to allocate space the operation. +/// pub fn getDevices(allocator: *Allocator) Allocator.Error![]Device { return pci.getDevices(allocator); } +/// +/// Get the system date and time. +/// +/// Return: DateTime +/// The current date and time. +/// +pub fn getDateTime() DateTime { + return rtc.getDateTime(); +} + /// /// Initialise the architecture /// diff --git a/src/kernel/arch/x86/rtc.zig b/src/kernel/arch/x86/rtc.zig index 4c6793e..6241cfb 100644 --- a/src/kernel/arch/x86/rtc.zig +++ b/src/kernel/arch/x86/rtc.zig @@ -24,8 +24,14 @@ const CURRENT_CENTURY: u32 = 2000; /// could report that the CMOS chip is faulty or the battery is dyeing. const CENTURY_REGISTER: bool = false; +/// The error set that can be returned from some RTC functions. +const RtcError = error{ + /// If setting the rate for interrupts is less than 3 or greater than 15. + RateError, +}; + /// A structure to hold all the date and time information in the RTC. -const DateTime = struct { +pub const DateTime = struct { second: u32, minute: u32, hour: u32, @@ -36,12 +42,6 @@ const DateTime = struct { day_of_week: u32, }; -/// The error set that can be returned from some RTC functions. -const RtcError = error{ - /// If setting the rate for interrupts is less than 3 or greater than 15. - RateError, -}; - /// The number of ticks that has passed when RTC was initially set up. var ticks: u32 = 0; @@ -145,66 +145,6 @@ fn readRtcRegisters() DateTime { return date_time; } -/// -/// Read a stable time from the real time clock registers on the CMOS chip and return a BCD and -/// 12 hour converted date and time. -/// -/// Return: DateTime -/// The data from the CMOS RTC registers with correct BCD conversions, 12 hour conversions and -/// the century added to the year. -/// -fn readRtc() DateTime { - var date_time1 = readRtcRegisters(); - var date_time2 = readRtcRegisters(); - - // Use the method: Read the registers twice and check if they are the same so to avoid - // inconsistent values due to RTC updates - - var compare = false; - - inline for (@typeInfo(DateTime).Struct.fields) |field| { - compare = compare or @field(date_time1, field.name) != @field(date_time2, field.name); - } - - while (compare) { - date_time1 = readRtcRegisters(); - date_time2 = readRtcRegisters(); - - compare = false; - inline for (@typeInfo(DateTime).Struct.fields) |field| { - compare = compare or @field(date_time1, field.name) != @field(date_time2, field.name); - } - } - - // Convert BCD to binary if necessary - if (isBcd()) { - date_time1.second = bcdToBinary(date_time1.second); - date_time1.minute = bcdToBinary(date_time1.minute); - // Needs a special calculation because the upper bit is set - date_time1.hour = ((date_time1.hour & 0x0F) + (((date_time1.hour & 0x70) / 16) * 10)) | (date_time1.hour & 0x80); - date_time1.day = bcdToBinary(date_time1.day); - date_time1.month = bcdToBinary(date_time1.month); - date_time1.year = bcdToBinary(date_time1.year); - if (CENTURY_REGISTER) { - date_time1.century = bcdToBinary(date_time1.century); - } - } - - // Need to add on the century to the year - if (CENTURY_REGISTER) { - date_time1.year += date_time1.century * 100; - } else { - date_time1.year += CURRENT_CENTURY; - } - - // Convert to 24hr time - if (is12Hr(date_time1)) { - date_time1.hour = ((date_time1.hour & 0x7F) + 12) % 24; - } - - return date_time1; -} - /// /// The interrupt handler for the RTC. /// @@ -262,6 +202,66 @@ fn enableInterrupts() void { cmos.writeStatusRegister(cmos.StatusRegister.B, status_b | 0x40, true); } +/// +/// Read a stable time from the real time clock registers on the CMOS chip and return a BCD and +/// 12 hour converted date and time. +/// +/// Return: DateTime +/// The data from the CMOS RTC registers with correct BCD conversions, 12 hour conversions and +/// the century added to the year. +/// +pub fn getDateTime() DateTime { + var date_time1 = readRtcRegisters(); + var date_time2 = readRtcRegisters(); + + // Use the method: Read the registers twice and check if they are the same so to avoid + // inconsistent values due to RTC updates + + var compare = false; + + inline for (@typeInfo(DateTime).Struct.fields) |field| { + compare = compare or @field(date_time1, field.name) != @field(date_time2, field.name); + } + + while (compare) { + date_time1 = readRtcRegisters(); + date_time2 = readRtcRegisters(); + + compare = false; + inline for (@typeInfo(DateTime).Struct.fields) |field| { + compare = compare or @field(date_time1, field.name) != @field(date_time2, field.name); + } + } + + // Convert BCD to binary if necessary + if (isBcd()) { + date_time1.second = bcdToBinary(date_time1.second); + date_time1.minute = bcdToBinary(date_time1.minute); + // Needs a special calculation because the upper bit is set + date_time1.hour = ((date_time1.hour & 0x0F) + (((date_time1.hour & 0x70) / 16) * 10)) | (date_time1.hour & 0x80); + date_time1.day = bcdToBinary(date_time1.day); + date_time1.month = bcdToBinary(date_time1.month); + date_time1.year = bcdToBinary(date_time1.year); + if (CENTURY_REGISTER) { + date_time1.century = bcdToBinary(date_time1.century); + } + } + + // Need to add on the century to the year + if (CENTURY_REGISTER) { + date_time1.year += date_time1.century * 100; + } else { + date_time1.year += CURRENT_CENTURY; + } + + // Convert to 24hr time + if (is12Hr(date_time1)) { + date_time1.hour = ((date_time1.hour & 0x7F) + 12) % 24; + } + + return date_time1; +} + /// /// Initialise the RTC. /// @@ -558,7 +558,7 @@ test "readRtc unstable read" { .century = 2000, .day_of_week = 5, }; - const actual = readRtc(); + const actual = getDateTime(); expectEqual(expected, actual); } @@ -611,7 +611,7 @@ test "readRtc is BCD" { .century = 2000, .day_of_week = 5, }; - const actual = readRtc(); + const actual = getDateTime(); expectEqual(expected, actual); } @@ -664,7 +664,7 @@ test "readRtc is 12 hours" { .century = 2000, .day_of_week = 5, }; - const actual = readRtc(); + const actual = getDateTime(); expectEqual(expected, actual); } diff --git a/src/kernel/filesystem/fat32.zig b/src/kernel/filesystem/fat32.zig index 623db3e..b28a0d5 100644 --- a/src/kernel/filesystem/fat32.zig +++ b/src/kernel/filesystem/fat32.zig @@ -8,6 +8,7 @@ const log = std.log.scoped(.fat32); const AutoHashMap = std.AutoHashMap; const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; +const arch = @import("../arch.zig").internals; const vfs = @import("vfs.zig"); const mem = @import("../mem.zig"); const CodePage = @import("../code_page/code_page.zig").CodePage; @@ -376,7 +377,7 @@ const ShortName = struct { } /// - /// Calculate the check sum for the long name entry. + /// Calculate the check sum for the short name entry. /// /// Arguments: /// IN self: *const ShortName - The short entry to calculate the check sum for. @@ -514,6 +515,48 @@ fn initStruct(comptime Type: type, bytes: []const u8) Type { return ret; } +/// +/// Initialise a slice with the values from a struct. +/// TODO: Once packed structs are good to go, then use std.mem.bytesAsValue. +/// +/// Arguments: +/// IN comptime Type: type - The type of the struct. +/// IN copy_struct: Type - The struct to copy from. +/// IN bytes: []u8 - The bytes to copy to. +/// +/// +fn initBytes(comptime Type: type, copy_struct: Type, bytes: []u8) void { + comptime var index = 0; + inline for (std.meta.fields(Type)) |item| { + switch (item.field_type) { + u8 => bytes[index] = @field(copy_struct, item.name), + u16 => std.mem.bytesAsSlice(u16, bytes[index .. index + 2])[0] = @field(copy_struct, item.name), + u32 => std.mem.bytesAsSlice(u32, bytes[index .. index + 4])[0] = @field(copy_struct, item.name), + else => { + switch (@typeInfo(item.field_type)) { + .Array => |info| switch (info.child) { + u8 => { + comptime var i = 0; + inline while (i < info.len) : (i += 1) { + bytes[index + i] = @field(copy_struct, item.name)[i]; + } + }, + u16 => { + comptime var i = 0; + inline while (i < info.len) : (i += 1) { + std.mem.bytesAsSlice(u16, bytes[index + (i * 2) .. index + 2 + (i * 2)])[0] = @field(copy_struct, item.name)[i]; + } + }, + else => @compileError("Unexpected field type: " ++ @typeName(info.child)), + }, + else => @compileError("Unexpected field type: " ++ @typeName(item.field_type)), + } + }, + } + index += @sizeOf(item.field_type); + } +} + /// /// A convenient function for returning the error types for reading, writing and seeking a stream. /// @@ -576,6 +619,10 @@ pub fn Fat32FS(comptime StreamType: type) type { /// See vfs.FileSystem.instance instance: usize, + // TODO: Have a FAT cache to not touching disk so much + // If then need to read a new part of the FAT, then flush the old one. + // Have a pub fn so the user can flush everything. + /// The root node struct for storing the root of the filesystem. const RootNode = struct { /// The VFS node of the root directory. @@ -592,6 +639,12 @@ pub fn Fat32FS(comptime StreamType: type) type { /// The real size of the file. This will be zero for directories. size: u32, + + /// The cluster at which the FAT dir short entry for this node is located. + entry_cluster: u32, + + /// The offset within the entry_cluster the short entry is located. + entry_offset: u32, }; /// The error set for the FAT32 filesystem. @@ -628,6 +681,12 @@ pub fn Fat32FS(comptime StreamType: type) type { /// When creating a new FAT32 entry, if the name doesn't match the specification. InvalidName, + + /// When there is is no more space on the stream for a new entry. + DiskFull, + + /// When destroying the filesystem, this is returned if there are filles left open. + FilesStillOpen, }; /// The internal self struct @@ -659,7 +718,7 @@ pub fn Fat32FS(comptime StreamType: type) type { else => StreamType.GetPosError, }; - /// An iterator for looping over the cluster chain in the FAT. + /// An iterator for looping over the cluster chain in the FAT and reading the cluster data. const ClusterChainIterator = struct { /// The allocator used for allocating the initial FAT array, then to free in deinit. allocator: *Allocator, @@ -744,7 +803,7 @@ pub fn Fat32FS(comptime StreamType: type) type { /// pub fn read(self: *ClusterChainIteratorSelf, buff: []u8) (Fat32Self.Error || ReadError || SeekError)!?usize { // FAT32 is really FAT28, so the top 4 bits are not used, so mask them out - if (buff.len != 0 and self.cluster != 0 and (self.cluster & 0x0FFFFFFF) < self.fat_config.cluster_end_marker and buff.len > 0) { + if (buff.len != 0 and self.cluster != 0 and (self.cluster & 0x0FFFFFFF) < self.fat_config.cluster_end_marker) { // Seek to the sector where the cluster is const sector = self.fat_config.clusterToSector(self.cluster); try self.stream.seekableStream().seekTo(sector * self.fat_config.bytes_per_sector + self.cluster_offset); @@ -773,8 +832,7 @@ pub fn Fat32FS(comptime StreamType: type) type { /// Deinitialise the cluster chain iterator. /// /// Arguments: - /// IN self: *ClusterChainIteratorSelf - Iterator self. - /// + /// IN self: *const ClusterChainIteratorSelf - Iterator self. /// pub fn deinit(self: *const ClusterChainIteratorSelf) void { self.allocator.free(self.fat); @@ -803,8 +861,7 @@ pub fn Fat32FS(comptime StreamType: type) type { var fat = try allocator.alloc(u32, fat_config.bytes_per_sector / @sizeOf(u32)); errdefer allocator.free(fat); - // FAT32 has u32 entries so need to divide - const table_offset = cluster / (fat_config.bytes_per_sector / @sizeOf(u32)); + const table_offset = cluster / fat.len; // Seek to the FAT // The FAT is just after the reserved sectors + the index @@ -949,8 +1006,8 @@ pub fn Fat32FS(comptime StreamType: type) type { var long_entries: ?[]LongName = null; defer if (long_entries) |entries| self.allocator.free(entries); - // If attribute is 0x0F, then is a long file name and - if (self.cluster_block[self.index] & 0x40 == 0x40 and self.cluster_block[self.index + 11] == 0x0F) { + // If attribute is 0x0F, then is a long file name + if (self.cluster_block[self.index + 11] == 0x0F and self.cluster_block[self.index] & 0x40 == 0x40) { // How many entries do we have, the first byte of the order. This starts at 1 not 0 var long_entry_count = self.cluster_block[self.index] & ~@as(u32, 0x40); // Allocate a buffer for the long name. 13 for the 13 characters in the long entry @@ -989,7 +1046,7 @@ pub fn Fat32FS(comptime StreamType: type) type { // Easy check for the attributes as 0x0F is invalid and a long name part // So if we have one, then this is a long entry not short entry as expected // Also make are we are not at the end - if (self.cluster_block[self.index] != 0x00 and self.cluster_block[self.index + 11] == 0x0F) { + if (self.cluster_block[self.index + 11] == 0x0F and self.cluster_block[self.index] != 0x00) { // This will be an invalid short name self.index += 32; return EntryItError.Orphan; @@ -1147,43 +1204,79 @@ pub fn Fat32FS(comptime StreamType: type) type { const self = @fieldParentPtr(Fat32Self, "instance", fs.instance); const cast_node = @ptrCast(*const vfs.Node, node); const opened_node = self.opened_files.get(cast_node) orelse return vfs.Error.NotOpened; - // TODO: Future PR - return 0; + // Short cut if length is less than cluster size, can just write the content directly without modifying the FAT + if (bytes.len <= self.fat_config.sectors_per_cluster * self.fat_config.bytes_per_sector) { + const sector = self.fat_config.clusterToSector(opened_node.cluster); + self.stream.seekableStream().seekTo(sector * self.fat_config.bytes_per_sector) catch return vfs.Error.Unexpected; + _ = self.stream.writer().writeAll(bytes) catch return vfs.Error.Unexpected; + } else { + var to_write: u32 = self.fat_config.sectors_per_cluster * self.fat_config.bytes_per_sector; + var write_index: u32 = 0; + var next_free_cluster: u32 = opened_node.cluster; + if (self.fat_config.has_fs_info) { + if (self.fat_config.number_free_clusters < bytes.len / (self.fat_config.sectors_per_cluster * self.fat_config.bytes_per_sector)) { + // Not enough free clusters + return vfs.Error.Unexpected; + } + } + while (write_index < bytes.len) : ({ + write_index = to_write; + to_write = std.math.min(bytes.len, write_index + self.fat_config.sectors_per_cluster * self.fat_config.bytes_per_sector); + if (write_index < bytes.len) { + next_free_cluster = self.findNextFreeCluster(next_free_cluster, next_free_cluster) catch return vfs.Error.Unexpected; + } + }) { + const sector = self.fat_config.clusterToSector(next_free_cluster); + self.stream.seekableStream().seekTo(sector * self.fat_config.bytes_per_sector) catch return vfs.Error.Unexpected; + _ = self.stream.writer().writeAll(bytes[write_index..to_write]) catch return vfs.Error.Unexpected; + } + } + // Update the entry the size for the file. + const entry_sector = self.fat_config.clusterToSector(opened_node.entry_cluster); + self.stream.seekableStream().seekTo(entry_sector * self.fat_config.bytes_per_sector + opened_node.entry_offset) catch return vfs.Error.Unexpected; + self.stream.writer().writeIntLittle(u32, bytes.len) catch return vfs.Error.Unexpected; + opened_node.size = bytes.len; + return bytes.len; } /// See vfs.FileSystem.open fn open(fs: *const vfs.FileSystem, dir: *const vfs.DirNode, name: []const u8, flags: vfs.OpenFlags, open_args: vfs.OpenArgs) (Allocator.Error || vfs.Error)!*vfs.Node { return switch (flags) { .NO_CREATION => openImpl(fs, dir, name), - .CREATE_DIR, .CREATE_FILE, .CREATE_SYMLINK => createFileOrDirOrSymlink(fs, dir, name, flags, open_args), + .CREATE_FILE => createFileOrDir(fs, dir, name, false), + .CREATE_DIR => createFileOrDir(fs, dir, name, true), + // FAT32 doesn't support symlinks + .CREATE_SYMLINK => vfs.Error.InvalidFlags, }; } /// - /// Helper function for creating the correct *Node + /// Helper function for creating the correct *Node. This can only create a FileNode or + /// DirNode, Symlinks are not supported for FAT32. The arguments are assumed correct: the + /// cluster points to a free block and the size is correct: zero for directories. /// /// Arguments: - /// IN self: *Fat32Self - Self, needed for the allocator and underlying filesystem - /// IN cluster: u32 - When creating a file, the cluster the file is at. - /// IN size: u32 - When creating a file, the size of the file is at. - /// IN flags: vfs.OpenFlags - The open flags for deciding on the Node type. - /// IN open_args: vfs.OpenArgs - The open arguments when creating a symlink. + /// IN self: *Fat32Self - Self, needed for the allocator and underlying filesystem + /// IN cluster: u32 - The cluster there the file/directory will be. + /// IN size: u32 - The size of the file or 0 for a directory. + /// IN entry_cluster: u32 - The cluster where the FAT dir entry is located. + /// IN entry_offset: u32 - The offset in the entry_cluster there the entry is located. + /// IN flags: vfs.OpenFlags - The open flags for deciding on the Node type. /// /// Return: *vfs.Node /// The VFS Node /// /// Error: Allocator.Error || vfs.Error - /// Allocator.Error - Not enough memory for allocating the Node - /// vfs.Error.NoSymlinkTarget - See vfs.Error.NoSymlinkTarget. + /// Allocator.Error - Not enough memory for allocating the Node + /// vfs.Error.InvalidFlags - Symlinks are not support in FAT32. /// - fn createNode(self: *Fat32Self, cluster: u32, size: u32, flags: vfs.OpenFlags, open_args: vfs.OpenArgs) (Allocator.Error || vfs.Error)!*vfs.Node { + fn createNode(self: *Fat32Self, cluster: u32, size: u32, entry_cluster: u32, entry_offset: u32, flags: vfs.OpenFlags) (Allocator.Error || vfs.Error)!*vfs.Node { var node = try self.allocator.create(vfs.Node); errdefer self.allocator.destroy(node); node.* = switch (flags) { .CREATE_DIR => .{ .Dir = .{ .fs = self.fs, .mount = null } }, .CREATE_FILE => .{ .File = .{ .fs = self.fs } }, - .CREATE_SYMLINK => return vfs.Error.InvalidFlags, - .NO_CREATION => unreachable, + .CREATE_SYMLINK, .NO_CREATION => return vfs.Error.InvalidFlags, }; // Create the opened info struct @@ -1192,6 +1285,8 @@ pub fn Fat32FS(comptime StreamType: type) type { opened_info.* = .{ .cluster = cluster, .size = size, + .entry_cluster = entry_cluster, + .entry_offset = entry_offset, }; try self.opened_files.put(node, opened_info); @@ -1211,7 +1306,7 @@ pub fn Fat32FS(comptime StreamType: type) type { /// Error: vfs.Error /// error.NotOpened - If directory node isn't opened. /// - fn getDirCluster(self: *Fat32Self, dir: *const vfs.DirNode) vfs.Error!u32 { + fn getDirCluster(self: *const Fat32Self, dir: *const vfs.DirNode) vfs.Error!u32 { return if (std.meta.eql(dir, self.fs.getRootNode(self.fs))) self.root_node.cluster else brk: { const parent = self.opened_files.get(@ptrCast(*const vfs.Node, dir)) orelse return vfs.Error.NotOpened; // Cluster 0 means is the root directory cluster @@ -1244,6 +1339,7 @@ pub fn Fat32FS(comptime StreamType: type) type { // Iterate over the directory and find the file/folder const cluster = try self.getDirCluster(dir); + var previous_cluster = cluster; var it = EntryIterator.init(self.allocator, self.fat_config, cluster, self.stream) catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, else => { @@ -1255,11 +1351,15 @@ pub fn Fat32FS(comptime StreamType: type) type { while (it.next() catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, else => { - log.err("Error initialising the entry iterator. Error: {}\n", .{e}); + log.err("Error in next() iterating the entry iterator. Error: {}\n", .{e}); return vfs.Error.Unexpected; }, }) |entry| { defer entry.deinit(); + if ((it.cluster_chain.cluster & 0x0FFFFFFF) < self.fat_config.cluster_end_marker) { + previous_cluster = it.cluster_chain.cluster; + } + // File name compare is case insensitive var match: bool = brk: { if (entry.long_name) |long_name| { @@ -1277,12 +1377,110 @@ pub fn Fat32FS(comptime StreamType: type) type { if (match) { const open_type = if (entry.short_name.isDir()) vfs.OpenFlags.CREATE_DIR else vfs.OpenFlags.CREATE_FILE; - return self.createNode(entry.short_name.getCluster(), entry.short_name.size, open_type, .{}); + return self.createNode(entry.short_name.getCluster(), entry.short_name.size, previous_cluster, it.index - 4, open_type); } } return vfs.Error.NoSuchFileOrDir; } + /// + /// Helper function for finding a free cluster from a given hint. The hint is where to + /// start looking. This will update the FAT and FSInfo accordingly. If a parent cluster + /// is provided, then the cluster chan will be updated so the parent cluster will point to + /// the new cluster. + /// + /// Arguments: + /// IN self: *Fat32Self - Self for allocating a free cluster with the current configuration. + /// IN cluster_hint: u32 - The cluster hint to start looking. + /// IN parent_cluster: ?u32 - The parent cluster to update the cluster chain from. Can + /// be null if creating a new cluster for a file/folder. + /// + /// Return: u32 + /// The next free cluster to use. + /// + /// Error: Allocator.Error || ReadError || SeekError || Fat32Self.Error + /// Allocator.Error - Not enough memory for allocating memory. + /// WriteError - Error while updating the FAT with the new cluster. + /// ReadError - Error while reading the stream. + /// SeekError - Error while seeking the stream. + /// Fat32Self.Error.BadRead - Error reading the FAT, not aligned to the sector. + /// Fat32Self.Error.DiskFull - No free clusters. + /// + fn findNextFreeCluster(self: *Fat32Self, cluster_hint: u32, parent_cluster: ?u32) (Allocator.Error || WriteError || ReadError || SeekError || Fat32Self.Error)!u32 { + var fat_buff = try self.allocator.alloc(u32, self.fat_config.bytes_per_sector / @sizeOf(u32)); + defer self.allocator.free(fat_buff); + var sector_offset = cluster_hint / fat_buff.len; + + const reader = self.stream.reader(); + const writer = self.stream.writer(); + const seeker = self.stream.seekableStream(); + + try seeker.seekTo((self.fat_config.reserved_sectors + sector_offset) * self.fat_config.bytes_per_sector); + var fat_read = try reader.readAll(std.mem.sliceAsBytes(fat_buff)); + if (fat_read != self.fat_config.bytes_per_sector) { + return Fat32Self.Error.BadRead; + } + + // Check for a free cluster by checking the FAT for a 0x00000000 entry (free) + var cluster = cluster_hint; + while (fat_buff[cluster - (sector_offset * fat_buff.len)] != 0x00000000) { + cluster += 1; + + // Check we are still in the FAT buffer, if not, read the next FAT part + const check_offset = cluster / fat_buff.len; + if (check_offset > sector_offset) { + // Check if the cluster will go outside the FAT + if (check_offset >= self.fat_config.sectors_per_fat) { + // TODO: Will need to wrap as there maybe free clusters before the hint + if (self.fat_config.has_fs_info) { + std.debug.assert(self.fat_config.number_free_clusters == 0); + } + return Fat32Self.Error.DiskFull; + } + sector_offset = check_offset; + try seeker.seekTo((self.fat_config.reserved_sectors + sector_offset) * self.fat_config.bytes_per_sector); + fat_read = try reader.readAll(std.mem.sliceAsBytes(fat_buff)); + if (fat_read != fat_buff.len * @sizeOf(u32)) { + return Fat32Self.Error.BadRead; + } + } + } + + // Update the FAT for the allocated cluster + if (parent_cluster) |p_cluster| { + try seeker.seekTo((self.fat_config.reserved_sectors * self.fat_config.bytes_per_sector) + (p_cluster * @sizeOf(u32))); + try writer.writeIntLittle(u32, cluster); + } + try seeker.seekTo((self.fat_config.reserved_sectors * self.fat_config.bytes_per_sector) + (cluster * @sizeOf(u32))); + try writer.writeIntLittle(u32, self.fat_config.cluster_end_marker); + // And the backup FAT + if (parent_cluster) |p_cluster| { + try seeker.seekTo(((self.fat_config.sectors_per_fat + self.fat_config.reserved_sectors) * self.fat_config.bytes_per_sector) + (p_cluster * @sizeOf(u32))); + try writer.writeIntLittle(u32, cluster); + } + try seeker.seekTo(((self.fat_config.sectors_per_fat + self.fat_config.reserved_sectors) * self.fat_config.bytes_per_sector) + (cluster * @sizeOf(u32))); + try writer.writeIntLittle(u32, self.fat_config.cluster_end_marker); + + // Update the FSInfo if we have one + if (self.fat_config.has_fs_info) { + self.fat_config.next_free_cluster = cluster + 1; + self.fat_config.number_free_clusters -= 1; + // write it to disk + // TODO: Have this cached and flush later to save writes + // Have a flush function so the user can flush and flush on deinit + try seeker.seekTo(self.fat_config.fsinfo_sector * self.fat_config.bytes_per_sector + 488); + try writer.writeIntLittle(u32, self.fat_config.number_free_clusters); + try writer.writeIntLittle(u32, self.fat_config.next_free_cluster); + // And the backup FSInfo + try seeker.seekTo((self.fat_config.backup_boot_sector + self.fat_config.fsinfo_sector) * self.fat_config.bytes_per_sector + 488); + try writer.writeIntLittle(u32, self.fat_config.number_free_clusters); + try writer.writeIntLittle(u32, self.fat_config.next_free_cluster); + } + + // Have found a free cluster + return cluster; + } + /// /// Helper function for creating a file, folder or symlink. /// @@ -1290,21 +1488,561 @@ pub fn Fat32FS(comptime StreamType: type) type { /// IN fs: *const vfs.FileSystem - The underlying filesystem. /// IN dir: *const vfs.DirNode - The parent directory. /// IN name: []const u8 - The name of the file/folder to open. - /// IN flags: vfs.OpenFlags - The open flags for if creating a file/folder/symlink. - /// IN open_args: vfs.OpenArgs - The open arguments if creating a symlink. + /// IN is_dir: bool - If creating a file/folder. /// /// Return: *vfs.Node /// The VFS Node for the opened/created file/folder. /// - /// Error: Allocator.Error || ReadError || SeekError || vfs.Error - /// Allocator.Error - Not enough memory for allocating memory + /// Error: Allocator.Error || vfs.Error + /// Allocator.Error - Not enough memory for allocating memory. /// vfs.Error.NoSuchFileOrDir - Error if creating a symlink and no target is provided. + /// vfs.Error.Unexpected - An error occurred whilst reading the file system, this + /// can be caused by a parsing error or errors on reading + /// or seeking the underlying stream. If this occurs, then + /// the real error is printed using `log.err`. /// - fn createFileOrDirOrSymlink(fs: *const vfs.FileSystem, dir: *const vfs.DirNode, name: []const u8, flags: vfs.OpenFlags, open_args: vfs.OpenArgs) (Allocator.Error || vfs.Error)!*vfs.Node { + fn createFileOrDir(fs: *const vfs.FileSystem, dir: *const vfs.DirNode, name: []const u8, is_dir: bool) (Allocator.Error || vfs.Error)!*vfs.Node { const self = @fieldParentPtr(Fat32Self, "instance", fs.instance); - // TODO: Future PR - return vfs.Error.NoSuchFileOrDir; + var files_in_dir = ArrayList([11]u8).init(self.allocator); + defer files_in_dir.deinit(); + + const dir_cluster = try self.getDirCluster(dir); + var previous_cluster = dir_cluster; + var previous_index: u32 = 0; + var it = EntryIterator.init(self.allocator, self.fat_config, dir_cluster, self.stream) catch |e| switch (e) { + error.OutOfMemory => return error.OutOfMemory, + else => { + log.err("Error initialising the entry iterator. Error: {}\n", .{e}); + return vfs.Error.Unexpected; + }, + }; + defer it.deinit(); + while (it.next() catch |e| switch (e) { + error.OutOfMemory => return error.OutOfMemory, + else => { + log.err("Error in next() iterating the entry iterator. Error: {}\n", .{e}); + return vfs.Error.Unexpected; + }, + }) |entry| { + defer entry.deinit(); + // Keep track of the last cluster before the end + previous_index = it.index; + if ((it.cluster_chain.cluster & 0x0FFFFFFF) < self.fat_config.cluster_end_marker) { + previous_cluster = it.cluster_chain.cluster; + } + try files_in_dir.append(entry.short_name.getSFNName()); + } + + const existing_files = files_in_dir.toOwnedSlice(); + defer self.allocator.free(existing_files); + + // Find a free cluster + // The default cluster to start looking for a free cluster + var cluster_hint: u32 = 2; + if (self.fat_config.has_fs_info and self.fat_config.next_free_cluster != 0x0FFFFFFF) { + // Have a next free cluster in the FSInfo, so can use this to start looking + cluster_hint = self.fat_config.next_free_cluster; + } + const free_cluster = self.findNextFreeCluster(cluster_hint, null) catch |e| switch (e) { + error.OutOfMemory => return error.OutOfMemory, + else => { + log.err("Error finding next cluster. Error: {}\n", .{e}); + return vfs.Error.Unexpected; + }, + }; + + const dir_attr: ShortName.Attributes = if (is_dir) .Directory else .None; + const entries = createEntries(self.allocator, name, free_cluster, dir_attr, existing_files) catch |e| switch (e) { + error.OutOfMemory => return error.OutOfMemory, + else => { + log.err("Error creating short and long entries. Error: {}\n", .{e}); + return vfs.Error.Unexpected; + }, + }; + defer self.allocator.free(entries.long_entry); + + // Write the entries to the directory + const short_offset = self.writeEntries(entries, previous_cluster, free_cluster, previous_index) catch |e| switch (e) { + error.OutOfMemory => return error.OutOfMemory, + else => { + log.err("Error writing entries to disk. Error: {}\n", .{e}); + return vfs.Error.Unexpected; + }, + }; + + // Calculate the offset where the file size is stored on disk + const file_size_offset = previous_index + (entries.long_entry.len * 32) + 28; + + return self.createNode(free_cluster, 0, short_offset.cluster, short_offset.offset + 28, if (is_dir) .CREATE_DIR else .CREATE_FILE); + } + + /// + /// Helper function for creating both long and short entries. This will convert the raw + /// name to a valid long and short FAT32 name and create the corresponding FAT32 entries. + /// error.InvalidName will be returned if the raw name cannot be converted to a valid FAT32 + /// name. The caller needs to free the long name entries. + /// + /// Arguments: + /// IN allocator: *Allocator - The allocator for allocating the long entries array. + /// IN name: []const u8 - The raw name to be used for creating the file/directory. + /// IN cluster: u32 - The cluster where the entry will point to. + /// IN attributes: ShortName.Attributes - The attributes of the the entry. + /// IN existing_short_names: []const [11]u8 - Existing short names so to resolve short name clashes. + /// + /// Return: FatDirEntry + /// The full FAT entry ready to be copies to disk, byte by byte. + /// + /// Error: Allocator.Error || Fat32Self.Error + /// Allocator.Error - Error allocating memory. + /// Fat32Self.Error - The name provided cannot be converted to a valid FAT32 name. + /// + fn createEntries(allocator: *Allocator, name: []const u8, cluster: u32, attributes: ShortName.Attributes, existing_short_names: []const [11]u8) (Allocator.Error || Fat32Self.Error)!FatDirEntry { + const long_name = try nameToLongName(allocator, name); + defer allocator.free(long_name); + const short_name = try longNameToShortName(long_name, existing_short_names); + const short_entry = createShortNameEntry(short_name, attributes, cluster); + const long_entry = try createLongNameEntry(allocator, long_name, short_entry.calcCheckSum()); + return FatDirEntry{ + .long_entry = long_entry, + .short_entry = short_entry, + }; + } + + /// + /// Helper for converting a raw file name to a valid FAT32 long file name. The returned + /// name will need to be freed by the allocator. + /// + /// Arguments: + /// IN allocator: *Allocator - The allocator for creating the FAT32 long file name. + /// IN name: []const u8 - The raw file name. + /// + /// Return: []const u16 + /// A valid UTF-16 FAT32 long file name. + /// + /// Error: Allocator.Error || Fat32Self.Error + /// Allocator.Error - Error allocating memory. + /// Fat32Self.Error.InvalidName - The file name cannot be converted to a valid long name. + /// + fn nameToLongName(allocator: *Allocator, name: []const u8) (Allocator.Error || Fat32Self.Error)![]const u16 { + // Allocate a buffer to translate to UFT16. Then length of the UFT8 will be more than enough + // TODO: Calc the total length and use appendAssumeCapacity + var utf16_buff = try ArrayList(u16).initCapacity(allocator, name.len); + defer utf16_buff.deinit(); + + // The name is in UTF8, this needs to be conversed to UTF16 + // This also checks for valid UTF8 characters + const utf8_view = std.unicode.Utf8View.init(name) catch return Fat32Self.Error.InvalidName; + var utf8_it = utf8_view.iterator(); + // Make sure the code points as valid for the long name + var ignored_leading = false; + while (utf8_it.nextCodepoint()) |code_point| { + // Ignore leading spaces + if (!ignored_leading and code_point == ' ') { + continue; + } + ignored_leading = true; + // If it is larger than 0xFFFF, then it cannot fit in UTF16 so invalid. + // Can't have control characters (including the DEL key) + if (code_point > 0xFFFF or code_point < 0x20 or code_point == 0x7F) { + return Fat32Self.Error.InvalidName; + } + + // Check for invalid characters + const invalid_chars = "\"*/:<>?\\|"; + inline for (invalid_chars) |char| { + if (char == code_point) { + return Fat32Self.Error.InvalidName; + } + } + + // Valid character + try utf16_buff.append(@intCast(u16, code_point)); + } + + // Remove trailing spaces and dots + // And return the name + const long_name = std.mem.trimRight(u16, utf16_buff.toOwnedSlice(), &[_]u16{ ' ', '.' }); + errdefer allocator.free(long_name); + + // Check the generated name is a valid length + if (long_name.len > 255) { + return Fat32Self.Error.InvalidName; + } + return long_name; + } + + /// + /// Helper function for checking if a u16 long name character can be converted to a valid + /// OEM u8 character + /// + /// Arguments: + /// IN char: u16 - The character to check + /// + /// Return: ?u8 + /// The successful converted character or null if is an invalid OEM u8 char. + /// + fn isValidSFNChar(char: u16) ?u8 { + // Ignore spaces + if (char == 0x20) { + return null; + } + + // If not a u8 char or CP437 char, then replace with a _ + if (char > 0x7F) { + return CodePage.toCodePage(.CP437, char) catch { + return null; + }; + } + + // Check for invalid characters, then replace with a _ + const invalid_chars = "+,;=[]"; + inline for (invalid_chars) |c| { + if (c == char) { + return null; + } + } + + return @intCast(u8, char); + } + + /// + /// Helper function for converting a valid long name to a short file name. This expects + /// valid long name, else is undefined for invalid long names. This also checks against + /// existing short names so there are no clashed within the same directory (appending + /// ~n to the end of the short name if there is a clash). + /// + /// Arguments: + /// IN long_name: []const u16 - The long name to convert. + /// IN existing_names: []const [11]u8 - The list of existing short names. + /// + /// Return: [11]u8 + /// The converted short name. + /// + /// Error: Fat32Self.Error + /// Fat32Self.Error.InvalidName - If the directory is fill of the same short file name. + /// + fn longNameToShortName(long_name: []const u16, existing_names: []const [11]u8) Fat32Self.Error![11]u8 { + // Pad with spaces + var sfn: [11]u8 = [_]u8{' '} ** 11; + var sfn_i: u8 = 0; + var is_lossy = false; + + // TODO: Need to convert to upper case first but don't have proper unicode support for this yet + + // Remove leading dots and spaces + var long_name_start: u32 = 0; + for (long_name) |char| { + if (char != '.') { + break; + } + // If there is, then it is lossy + long_name_start += 1; + is_lossy = true; + } + + // Get the last dot in the string + const last_dot_index = std.mem.lastIndexOf(u16, long_name[long_name_start..], &[_]u16{'.'}); + + for (long_name[long_name_start..]) |char, i| { + // Break when we reach the max of the short name or the last dot + if (char == '.') { + if (last_dot_index) |index| { + if (i == index) { + break; + } + } + // Else ignore it, and is lossy + is_lossy = true; + continue; + } + + if (sfn_i == 8) { + is_lossy = true; + break; + } + + if (isValidSFNChar(char)) |oem_char| { + // Valid SFN char, and convert to upper case + // TODO: Need proper unicode uppercase + sfn[sfn_i] = std.ascii.toUpper(oem_char); + sfn_i += 1; + } else { + // Spaces don't need to be replaced, just ignored, but still set the lossy + if (char != 0x20) { + sfn[sfn_i] = '_'; + sfn_i += 1; + } + is_lossy = true; + } + } + + // Save the name index + const name_index = sfn_i; + + // Go to the last dot, if there isn't one, return what we have + const index = (last_dot_index orelse return sfn) + long_name_start; + sfn_i = 8; + // +1 as the index will be on the DOT and we don't need to include it + for (long_name[index + 1 ..]) |char| { + // Break when we reach the max of the short name + if (sfn_i == 11) { + is_lossy = true; + break; + } + + if (isValidSFNChar(char)) |oem_char| { + // Valid SFN char, and convert to upper case + // TODO: Need proper unicode uppercase + sfn[sfn_i] = std.ascii.toUpper(oem_char); + sfn_i += 1; + } else { + // Spaces don't need to be replaced, just ignored, but still set the lossy + if (char != 0x20) { + sfn[sfn_i] = '_'; + sfn_i += 1; + } + is_lossy = true; + } + } + + // 0xE5 is used for a deleted file, but is a valid UTF8 character, so use 0x05 instead + if (sfn[0] == 0xE5) { + sfn[0] = 0x05; + } + + // Is there a collision of file names + // Find n in ~n in the existing files + var trail_number: u32 = 0; + var full_name_match = false; + for (existing_names) |existing_name| { + // Only need to check the 8 char file name, not extension + var i: u8 = 0; + while (i < 8) : (i += 1) { + if (existing_name[i] != sfn[i] and existing_name[i] == '~') { + // Read the number and break + // -3 as we exclude the extension + // +1 as we are at the '~' + i += 1; + const end_num = std.mem.indexOf(u8, existing_name[0..], " ") orelse existing_name.len - 3; + const num = std.fmt.parseInt(u32, existing_name[i..end_num], 10) catch { + break; + }; + if (num > trail_number) { + trail_number = num; + } + break; + } + // Not the same file name + if (existing_name[i] != sfn[i] and (!is_lossy or existing_name[i] != '~')) { + break; + } + } + // If match the full name, then need to add trail + if (i == 8) { + full_name_match = true; + } + } + + // If there were some losses, then wee need to add a number to the end + if (is_lossy or full_name_match) { + // Check if we have the max file names + if (trail_number == 999999) { + return Error.InvalidName; + } + + // Increase the trail number as this will be the number to append + trail_number += 1; + + // Format this as a string, can't be more than 6 characters + var trail_number_str: [6]u8 = undefined; + const trail_number_str_end = std.fmt.formatIntBuf(trail_number_str[0..], trail_number, 10, false, .{}); + + // Get the index to put the ~n + var number_trail_index = if (name_index > 7 - trail_number_str_end) 7 - trail_number_str_end else name_index; + sfn[number_trail_index] = '~'; + for (trail_number_str[0..trail_number_str_end]) |num_str| { + number_trail_index += 1; + sfn[number_trail_index] = num_str; + } + } + + return sfn; + } + + /// + /// Helper function for creating the long name dir entries from the long name. The return + /// array will need to be freed by the caller. This expects a valid long name else undefined + /// behavior. + /// + /// Arguments: + /// IN allocator: *Allocator - The allocator for the long name array + /// IN long_name: []const u16 - The valid long name. + /// IN check_sum: u8 - The short name check sum for the long entry. + /// + /// Return: []LongName + /// The list of long name entries read to be written to disk. + /// + /// Error: Allocator.Error + /// Allocator.Error - Error allocating memory for the long name entries. + /// + fn createLongNameEntry(allocator: *Allocator, long_name: []const u16, check_sum: u8) Allocator.Error![]LongName { + // Calculate the number of long entries (round up). LFN are each 13 characters long + const num_lfn_entries = @intCast(u8, (long_name.len + 12) / 13); + + // Create the long entries + var lfn_array = try allocator.alloc(LongName, num_lfn_entries); + errdefer allocator.free(lfn_array); + + // Work backwards because it is easier + var backwards_index = num_lfn_entries; + while (backwards_index > 0) : (backwards_index -= 1) { + // If this is the first entry, then the first bytes starts with 0x40 + const entry_index = num_lfn_entries - backwards_index; + const order = if (backwards_index == 1) 0x40 | num_lfn_entries else entry_index + 1; + // Get the working slice of 13 characters + // NULL terminate and pad with 0xFFFF if less than 13 characters + const working_name: [13]u16 = blk: { + var temp: [13]u16 = [_]u16{0xFFFF} ** 13; + const long_name_slice = long_name[(entry_index * 13)..]; + if (long_name_slice.len < 13) { + for (long_name_slice) |char, i| { + temp[i] = char; + } + // NULL terminated + temp[long_name_slice.len] = 0x0000; + } else { + for (temp) |*char, i| { + char.* = long_name[(entry_index * 13) + i]; + } + } + break :blk temp; + }; + + // Create the entry + lfn_array[backwards_index - 1] = .{ + .order = order, + .first = working_name[0..5].*, + .check_sum = check_sum, + .second = working_name[5..11].*, + .third = working_name[11..13].*, + }; + } + + return lfn_array; + } + + /// + /// Helper function fro creating a short name entry. This calls the system time to get the + /// current date and time for the new file/directory. This assumes a valid short name else + /// undefined behavior. + /// + /// Arguments: + /// IN name: [11]u8 - The short name. + /// IN attributes: ShortName.Attributes - The attribute for the short name entry. + /// IN cluster: u32 - The cluster where this will point to. + /// + /// Return: ShortName + /// The short name entry with the current time used. + /// + fn createShortNameEntry(name: [11]u8, attributes: ShortName.Attributes, cluster: u32) ShortName { + const date_time = arch.getDateTime(); + + const date = @intCast(u16, date_time.day | date_time.month << 5 | (date_time.year - 1980) << 9); + const time = @intCast(u16, date_time.second / 2 | date_time.minute << 5 | date_time.hour << 11); + + return .{ + .name = name[0..8].*, + .extension = name[8..11].*, + .attributes = @enumToInt(attributes), + .time_created_tenth = @intCast(u8, (date_time.second % 2) * 100), + .time_created = time, + .date_created = date, + .date_last_access = date, + .cluster_high = @truncate(u16, cluster >> 16), + .time_last_modification = time, + .date_last_modification = date, + .cluster_low = @truncate(u16, cluster), + .size = 0x00000000, + }; + } + + /// + /// Helper function for writing a new file/folder entry. This will create the new entry + /// under the provided cluster. If the cluster is full or not big enough the FAT will + /// be extended and a new cluster will be allocated. This expects valid entries. Inputs + /// are assumed to be correct. + /// + /// Arguments: + /// IN self: *Fat32Self - Self for the current instance of the FAT32 filesystem. + /// IN entries: FatDirEntry - The new entries to be written. + /// IN at_cluster: u32 - The cluster to write the entries to. + /// IN next_free_cluster_hint: u32 - The next free cluster to be used as a hint to find + /// new clusters for large entries. + /// IN initial_cluster_offset: u32 - The initial offset into the cluster to write to. + /// + /// Return: struct{cluster: u32, offset: u32} + /// cluster - The cluster at which the short entry is located + /// offset - The offset at which the short entry is located with in the cluster. + /// + /// Error: Allocator.Error || WriteError || ReadError || SeekError + /// Allocator.Error - Error allocating memory. + /// WriteError - Error writing to the underlying stream. + /// ReadError - Error reading the underlying stream. + /// SeekError - Error seeking the underlying stream. + /// Fat32Self.Error - This will relate to allocating a new cluster. + /// + fn writeEntries(self: *Fat32Self, entries: FatDirEntry, at_cluster: u32, next_free_cluster_hint: u32, initial_cluster_offset: u32) (Allocator.Error || WriteError || ReadError || SeekError || Fat32Self.Error)!struct { cluster: u32, offset: u32 } { + // Each entry is 32 bytes short + 32 * long len + const entries_size_bytes = 32 + (32 * entries.long_entry.len); + std.debug.assert(at_cluster >= 2); + // Largest possible entry length + std.debug.assert(entries_size_bytes <= 32 + (32 * 20)); + // Entries are 32 bytes long, so the offset will need to be aligned to 32 bytes + std.debug.assert(initial_cluster_offset % 32 == 0); + + const cluster_size = self.fat_config.bytes_per_sector * self.fat_config.sectors_per_cluster; + + // Check free entry + var index = initial_cluster_offset; + + // The cluster to write to, this can update as if the cluster provided is full, will need to write to the next free cluster + var write_cluster = at_cluster; + + // At the end of the cluster chain, need to alloc a cluster + // Overwrite the at_cluster to use the new one + if (index == cluster_size) { + write_cluster = try self.findNextFreeCluster(next_free_cluster_hint, write_cluster); + index = 0; + } + + // TODO: Once FatDirEntry can be a packed struct, then can write as bytes and not convert + var write_buff = try self.allocator.alloc(u8, entries_size_bytes); + defer self.allocator.free(write_buff); + for (entries.long_entry) |long_entry, i| { + initBytes(LongName, long_entry, write_buff[(32 * i)..]); + } + initBytes(ShortName, entries.short_entry, write_buff[write_buff.len - 32 ..]); + + // Fill the cluster with the entry + var cluster_offset = index; + var write_index: u32 = 0; + var write_next_index = std.math.min(cluster_size - cluster_offset, write_buff.len); + while (write_index < write_buff.len) : ({ + cluster_offset = 0; + write_index = write_next_index; + write_next_index = std.math.min(write_next_index + cluster_size, write_buff.len); + if (write_index < write_buff.len) { + write_cluster = try self.findNextFreeCluster(write_cluster, write_cluster); + } + }) { + const write_sector = self.fat_config.clusterToSector(write_cluster); + try self.stream.seekableStream().seekTo(write_sector * self.fat_config.bytes_per_sector + cluster_offset); + try self.stream.writer().writeAll(write_buff[write_index..write_next_index]); + } + + const ret = .{ .cluster = write_cluster, .offset = (index + write_buff.len - 32) % cluster_size }; + return ret; } /// @@ -1314,10 +2052,11 @@ pub fn Fat32FS(comptime StreamType: type) type { /// Arguments: /// IN self: *Fat32Self - Self to free. /// - pub fn destroy(self: *Fat32Self) void { + pub fn destroy(self: *Fat32Self) Fat32Self.Error!void { // Make sure we have closed all files - // TODO: Should this deinit close any open files instead? - std.debug.assert(self.opened_files.count() == 0); + if (self.opened_files.count() != 0) { + return Fat32Self.Error.FilesStillOpen; + } self.opened_files.deinit(); self.allocator.destroy(self.root_node.node); self.allocator.destroy(self.fs); @@ -1521,46 +2260,38 @@ pub fn initialiseFAT32(allocator: *Allocator, stream: anytype) (Allocator.Error } /// -/// Read the test files and write them to the test FAT32 filesystem. +/// Create a test FAT32 filesystem. This will use mkfat32 to create the temporary FAT32 then the +/// stream and fat_config will be replaced by the provided ones. Returned will need to be deinit(). +/// This will also set the VFS root node so can use the VFS interfaces without manual setup. /// /// Arguments: -/// IN comptime StreamType: type - The stream type. -/// IN fat32fs: *Fat32FS(StreamType) - The test filesystem. +/// IN allocator: *Allocator - The allocator to create the FAT32FS +/// IN stream: anytype - The stream to replace the generated one. This will need to be a +/// fixed buffer stream. +/// IN fat_config: FATConfig - The config to replace the generated one. /// -/// Error: Allocator.Error || ErrorSet(StreamType) || vfs.Error || std.fs.File.OpenError || std.fs.File.ReadError -/// Allocator.Error - Error when allocating memory for reading the file content. -/// ErrorSet(StreamType) - Error when writing to the test filesystem. -/// vfs.Error - Error with VFS operations. -/// std.fs.File.OpenError - Error when opening the test files. -/// std.fs.File.ReadError - Error when reading the test files. +/// Return: *Fat32FS(@TypeOf(stream)) +/// Teh test FAT32 filesystem /// -fn testWriteTestFiles(comptime StreamType: type, fat32fs: *Fat32FS(StreamType)) (Allocator.Error || ErrorSet(StreamType) || vfs.Error || std.fs.File.OpenError || std.fs.File.ReadError)!void { - vfs.setRoot(fat32fs.root_node.node); +/// Error: anyerror +/// Any errors. As this is a test function, it doesn't matter what error is returned, if one +/// does, it fails the test. +/// +fn testFAT32FS(allocator: *Allocator, stream: anytype, fat_config: FATConfig) anyerror!*Fat32FS(@TypeOf(stream)) { + var test_file_buf = try std.testing.allocator.alloc(u8, 35 * 512); + defer std.testing.allocator.free(test_file_buf); - var test_files = try std.fs.cwd().openDir("test/fat32/test_files", .{ .iterate = true }); - defer test_files.close(); + var temp_stream = &std.io.fixedBufferStream(test_file_buf[0..]); - var it = test_files.iterate(); - while (try it.next()) |file| { - // Open the test file - const test_file = try test_files.openFile(file.name, .{}); - defer test_file.close(); + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, temp_stream, true); - // Read the content - const test_file_content = try test_file.readToEndAlloc(std.testing.allocator, 0xFFFF); - defer std.testing.allocator.free(test_file_content); + var test_fs = try initialiseFAT32(std.testing.allocator, temp_stream); + test_fs.stream = stream; + test_fs.fat_config = fat_config; - // TODO: Once the write PR is complete, then write the files - // // Open the test file to the FAT32 filesystem - // const f1 = try vfs.openFile(file, .CREATE_FILE); - // defer f1.close(); + try vfs.setRoot(test_fs.root_node.node); - // // Write the content - // const written = try f1.write(test_file_content); - // if (written != test_file_content.len) { - // @panic("Written not the same for content length"); - // } - } + return test_fs; } test "LongName.getName" { @@ -1926,275 +2657,6 @@ test "ShortName.calcCheckSum" { expectEqual(sfn.calcCheckSum(), 0x7A); } -test "Fat32FS initialise test files" { - var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); - defer std.testing.allocator.free(test_file_buf); - - var stream = &std.io.fixedBufferStream(test_file_buf[0..]); - - try mkfat32.Fat32.make(.{}, stream, true); - - var test_fs = try initialiseFAT32(std.testing.allocator, stream); - defer test_fs.destroy(); - - // Write the test files - try testWriteTestFiles(@TypeOf(stream), test_fs); - - // Open the known good image to compare to - const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); - defer test_fat32_image.close(); - - // TODO: Loop over the test files and open then - // Compare the long and short entries - // The time stamps will be different so can't to complete byte compare -} - -test "Fat32FS.getRootNode" { - const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); - defer test_fat32_image.close(); - - var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); - - expectEqual(test_fs.fs.getRootNode(test_fs.fs), &test_fs.root_node.node.Dir); - expectEqual(test_fs.root_node.cluster, 2); - expectEqual(test_fs.fat_config.root_directory_cluster, 2); -} - -test "Fat32FS.init no error" { - const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); - defer test_fat32_image.close(); - - var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); -} - -test "Fat32FS.init errors" { - const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); - defer test_fat32_image.close(); - - var test_file_buf = try std.testing.allocator.alloc(u8, (32 * 512 + 4) + 1); - defer std.testing.allocator.free(test_file_buf); - - const read = try test_fat32_image.reader().readAll(test_file_buf[0..]); - const stream = &std.io.fixedBufferStream(test_file_buf[0..]); - - // BadMBRMagic - test_file_buf[510] = 0x00; - expectError(error.BadMBRMagic, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[510] = 0x55; - - test_file_buf[511] = 0x00; - expectError(error.BadMBRMagic, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[511] = 0xAA; - - // BadRootCluster - // Little endian, so just eed to set the upper bytes - test_file_buf[44] = 0; - expectError(error.BadRootCluster, initialiseFAT32(std.testing.allocator, stream)); - - test_file_buf[44] = 1; - expectError(error.BadRootCluster, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[44] = 2; - - // BadFATCount - test_file_buf[16] = 0; - expectError(error.BadFATCount, initialiseFAT32(std.testing.allocator, stream)); - - test_file_buf[16] = 1; - expectError(error.BadFATCount, initialiseFAT32(std.testing.allocator, stream)); - - test_file_buf[16] = 10; - expectError(error.BadFATCount, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[16] = 2; - - // NotMirror - test_file_buf[40] = 1; - expectError(error.NotMirror, initialiseFAT32(std.testing.allocator, stream)); - - test_file_buf[40] = 10; - expectError(error.NotMirror, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[40] = 0; - - // BadMedia - test_file_buf[21] = 0xF0; - expectError(error.BadMedia, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[21] = 0xF8; - - // BadFat32 - test_file_buf[17] = 10; - expectError(error.BadFat32, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[17] = 0; - - test_file_buf[19] = 10; - expectError(error.BadFat32, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[19] = 0; - - test_file_buf[22] = 10; - expectError(error.BadFat32, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[22] = 0; - - // BadSignature - test_file_buf[66] = 0x28; - expectError(error.BadSignature, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[66] = 0x29; - - // BadFSType - // Change from FAT32 to FAT16 - test_file_buf[85] = '1'; - test_file_buf[86] = '6'; - expectError(error.BadFSType, initialiseFAT32(std.testing.allocator, stream)); - test_file_buf[85] = '3'; - test_file_buf[86] = '2'; - - // Test the bad reads - // Boot sector - expectError(error.BadRead, initialiseFAT32(std.testing.allocator, &std.io.fixedBufferStream(test_file_buf[0..510]))); - // FSInfo (we have one) - expectError(error.BadRead, initialiseFAT32(std.testing.allocator, &std.io.fixedBufferStream(test_file_buf[0 .. 512 + 100]))); - // FAT - expectError(error.BadRead, initialiseFAT32(std.testing.allocator, &std.io.fixedBufferStream(test_file_buf[0 .. (32 * 512 + 4) + 1]))); -} - -test "Fat32FS.init free memory" { - const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); - defer test_fat32_image.close(); - - const allocations: usize = 5; - var i: usize = 0; - while (i < allocations) : (i += 1) { - var fa = std.testing.FailingAllocator.init(std.testing.allocator, i); - expectError(error.OutOfMemory, initialiseFAT32(&fa.allocator, test_fat32_image)); - } -} - -test "Fat32FS.init FATConfig expected" { - const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); - defer test_fat32_image.close(); - - var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); - - // This is the expected FAT config from the initialised FAT device - const expected = FATConfig{ - .bytes_per_sector = 512, - .sectors_per_cluster = 1, - .reserved_sectors = 32, - .hidden_sectors = 0, - .total_sectors = 66583, - .sectors_per_fat = 513, - .root_directory_cluster = 2, - .fsinfo_sector = 1, - .backup_boot_sector = 6, - .has_fs_info = true, - .number_free_clusters = 65490, - .next_free_cluster = 36, - .cluster_end_marker = 0x0FFFFFFF, - }; - - expectEqual(test_fs.fat_config, expected); -} - -test "Fat32FS.init FATConfig mix FSInfo" { - const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); - defer test_fat32_image.close(); - - var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); - defer std.testing.allocator.free(test_file_buf); - - const read = try test_fat32_image.reader().readAll(test_file_buf[0..]); - const stream = &std.io.fixedBufferStream(test_file_buf[0..]); - - // No FSInfo - { - // Force no FSInfo - test_file_buf[48] = 0x00; - - var test_fs = try initialiseFAT32(std.testing.allocator, stream); - defer test_fs.destroy(); - - // This is the default config that should be produced from mkfat32.Fat32 - const expected = FATConfig{ - .bytes_per_sector = 512, - .sectors_per_cluster = 1, - .reserved_sectors = 32, - .hidden_sectors = 0, - .total_sectors = 66583, - .sectors_per_fat = 513, - .root_directory_cluster = 2, - .fsinfo_sector = 0, - .backup_boot_sector = 6, - .has_fs_info = false, - .number_free_clusters = 0xFFFFFFFF, - .next_free_cluster = 0xFFFFFFFF, - .cluster_end_marker = 0x0FFFFFFF, - }; - - expectEqual(test_fs.fat_config, expected); - test_file_buf[48] = 0x01; - } - - // Bad Signatures - { - // Corrupt a signature - test_file_buf[512] = 0xAA; - - var test_fs = try initialiseFAT32(std.testing.allocator, stream); - defer test_fs.destroy(); - - // This is the default config that should be produced from mkfat32.Fat32 - const expected = FATConfig{ - .bytes_per_sector = 512, - .sectors_per_cluster = 1, - .reserved_sectors = 32, - .hidden_sectors = 0, - .total_sectors = 66583, - .sectors_per_fat = 513, - .root_directory_cluster = 2, - .fsinfo_sector = 1, - .backup_boot_sector = 6, - .has_fs_info = false, - .number_free_clusters = 0xFFFFFFFF, - .next_free_cluster = 0xFFFFFFFF, - .cluster_end_marker = 0x0FFFFFFF, - }; - - expectEqual(test_fs.fat_config, expected); - test_file_buf[512] = 0x52; - } - - // Bad number_free_clusters - { - // Make is massive - test_file_buf[512 + 4 + 480 + 4] = 0xAA; - test_file_buf[512 + 4 + 480 + 5] = 0xBB; - test_file_buf[512 + 4 + 480 + 6] = 0xCC; - test_file_buf[512 + 4 + 480 + 7] = 0xDD; - - var test_fs = try initialiseFAT32(std.testing.allocator, stream); - defer test_fs.destroy(); - - // This is the default config that should be produced from mkfat32.Fat32 - const expected = FATConfig{ - .bytes_per_sector = 512, - .sectors_per_cluster = 1, - .reserved_sectors = 32, - .hidden_sectors = 0, - .total_sectors = 66583, - .sectors_per_fat = 513, - .root_directory_cluster = 2, - .fsinfo_sector = 1, - .backup_boot_sector = 6, - .has_fs_info = true, - .number_free_clusters = 0xFFFFFFFF, - .next_free_cluster = 36, - .cluster_end_marker = 0x0FFFFFFF, - }; - - expectEqual(test_fs.fat_config, expected); - } -} - test "ClusterChainIterator.checkRead - Within cluster, within FAT" { // The undefined values are not used in checkRead const fat_config = FATConfig{ @@ -2215,7 +2677,7 @@ test "ClusterChainIterator.checkRead - Within cluster, within FAT" { var buff_stream = [_]u8{}; var stream = &std.io.fixedBufferStream(buff_stream[0..]); // First 2 are for other purposed and not needed, the third is the first real FAT entry - var fat = [_]u32{ 0x0FFFFFFF, 0xFFFFFFF8, 0xFFFFFFFF, 0x00000000 }; + var fat = [_]u32{ 0x0FFFFFFF, 0xFFFFFFF8, 0x0FFFFFFF, 0x00000000 }; var it = Fat32FS(@TypeOf(stream)).ClusterChainIterator{ .allocator = undefined, .cluster = 2, @@ -2256,7 +2718,7 @@ test "ClusterChainIterator.checkRead - Multiple clusters, within FAT" { var buff_stream = [_]u8{}; var stream = &std.io.fixedBufferStream(buff_stream[0..]); // First 2 are for other purposed and not needed, the third is the first real FAT entry - var fat = [_]u32{ 0x0FFFFFFF, 0xFFFFFFF8, 0x00000003, 0xFFFFFFFF }; + var fat = [_]u32{ 0x0FFFFFFF, 0xFFFFFFF8, 0x00000003, 0x0FFFFFFF }; var it = Fat32FS(@TypeOf(stream)).ClusterChainIterator{ .allocator = undefined, .cluster = 2, @@ -2298,20 +2760,16 @@ test "ClusterChainIterator.checkRead - Multiple clusters, outside FAT" { // Set the stream to all FF which represents the end of a FAT chain var buff_stream = [_]u8{ // First 4 FAT (little endian) - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x04, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Second 4 FAT. This is where it will seek to - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); // First 2 are for other purposed and not needed, the third is the first real FAT entry - var fat = [_]u32{ 0x0FFFFFFF, 0xFFFFFFF8, 0x00000004, 0xFFFFFFFF }; - var expected_fat = [_]u32{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + var fat = [_]u32{ 0x0FFFFFFF, 0xFFFFFFF8, 0x00000004, 0x0FFFFFFF }; + var expected_fat = [_]u32{ 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF }; var it = Fat32FS(@TypeOf(stream)).ClusterChainIterator{ .allocator = undefined, .cluster = 2, @@ -2450,25 +2908,19 @@ test "ClusterChainIterator.read - success" { var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region - 'a', 'b', 'c', 'd', - '1', '2', '3', '4', - 'A', 'B', 'C', 'D', - '!', '"', '$', '%', + 'a', 'b', 'c', 'd', '1', '2', '3', '4', + 'A', 'B', 'C', 'D', '!', '"', '$', '%', }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); // First 2 are for other purposed and not needed, the third is the first real FAT entry - var fat = [_]u32{ 0x0FFFFFFF, 0xFFFFFFF8, 0xFFFFFFFF, 0xFFFFFFFF }; - var expected_fat = [_]u32{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + var fat = [_]u32{ 0x0FFFFFFF, 0xFFFFFFF8, 0x0FFFFFFF, 0x0FFFFFFF }; + var expected_fat = [_]u32{ 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF }; var it = Fat32FS(@TypeOf(stream)).ClusterChainIterator{ .allocator = undefined, .cluster = 2, @@ -2486,7 +2938,7 @@ test "ClusterChainIterator.read - success" { expectEqualSlices(u8, buff[0..], "abcd1234ABCD!\"$%"); expectEqual(it.table_offset, 0); expectEqual(it.cluster_offset, 0); - expectEqual(it.cluster, 0xFFFFFFFF); + expectEqual(it.cluster, 0x0FFFFFFF); } test "ClusterChainIterator.init - free on BadRead" { @@ -2527,20 +2979,14 @@ test "ClusterChainIterator.init - free on OutOfMemory" { }; var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region - 'a', 'b', 'c', 'd', - '1', '2', '3', '4', - 'A', 'B', 'C', 'D', - '!', '"', '$', '%', + 'a', 'b', 'c', 'd', '1', '2', '3', '4', + 'A', 'B', 'C', 'D', '!', '"', '$', '%', }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); const allocations: usize = 1; @@ -2570,20 +3016,14 @@ test "ClusterChainIterator.init - success and good read" { }; var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region - 'a', 'b', 'c', 'd', - '1', '2', '3', '4', - 'A', 'B', 'C', 'D', - '!', '"', '$', '%', + 'a', 'b', 'c', 'd', '1', '2', '3', '4', + 'A', 'B', 'C', 'D', '!', '"', '$', '%', }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var it = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -2597,7 +3037,7 @@ test "ClusterChainIterator.init - success and good read" { expectEqualSlices(u8, buff[0..], "abcd1234ABCD!\"$%"); expectEqual(it.table_offset, 0); expectEqual(it.cluster_offset, 0); - expectEqual(it.cluster, 0xFFFFFFFF); + expectEqual(it.cluster, 0x0FFFFFFF); const expect_null = try it.read(buff[read..]); expectEqual(expect_null, null); @@ -2621,25 +3061,17 @@ test "EntryIterator.checkRead - inside cluster block" { }; var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x04, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 'a', 'b', 'c', 'd', - '1', '2', '3', '4', - 'A', 'B', 'C', 'D', - '!', '"', '$', '%', + 'a', 'b', 'c', 'd', '1', '2', '3', '4', + 'A', 'B', 'C', 'D', '!', '"', '$', '%', // Data region cluster 2 - 'e', 'f', 'g', 'h', - '5', '6', '7', '8', - 'E', 'F', 'G', 'H', - '^', '&', '*', '(', + 'e', 'f', 'g', 'h', '5', '6', '7', '8', + 'E', 'F', 'G', 'H', '^', '&', '*', '(', }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -2678,25 +3110,17 @@ test "EntryIterator.checkRead - read new cluster" { }; var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 'a', 'b', 'c', 'd', - '1', '2', '3', '4', - 'A', 'B', 'C', 'D', - '!', '"', '$', '%', + 'a', 'b', 'c', 'd', '1', '2', '3', '4', + 'A', 'B', 'C', 'D', '!', '"', '$', '%', // Data region cluster 2 - 'e', 'f', 'g', 'h', - '5', '6', '7', '8', - 'E', 'F', 'G', 'H', - '^', '&', '*', '(', + 'e', 'f', 'g', 'h', '5', '6', '7', '8', + 'E', 'F', 'G', 'H', '^', '&', '*', '(', }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -2736,25 +3160,17 @@ test "EntryIterator.checkRead - end of cluster chain" { }; var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 'a', 'b', 'c', 'd', - '1', '2', '3', '4', - 'A', 'B', 'C', 'D', - '!', '"', '$', '%', + 'a', 'b', 'c', 'd', '1', '2', '3', '4', + 'A', 'B', 'C', 'D', '!', '"', '$', '%', // Data region cluster 2 - 'e', 'f', 'g', 'h', - '5', '6', '7', '8', - 'E', 'F', 'G', 'H', - '^', '&', '*', '(', + 'e', 'f', 'g', 'h', '5', '6', '7', '8', + 'E', 'F', 'G', 'H', '^', '&', '*', '(', }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -2792,41 +3208,25 @@ test "EntryIterator.nextImp - end of entries" { }; var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Data region cluster 2 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -2866,41 +3266,25 @@ test "EntryIterator.nextImp - just deleted files" { // This will also read the next cluster chain. var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 0xE5, 0x41, 0x4D, 0x44, - 0x49, 0x53, 0x7E, 0x32, - 0x54, 0x58, 0x54, 0x00, - 0x18, 0x34, 0x47, 0x76, - 0xF9, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x48, 0x76, - 0xF9, 0x50, 0x04, 0x00, - 0x24, 0x00, 0x00, 0x00, + 0xE5, 0x41, 0x4D, 0x44, 0x49, 0x53, 0x7E, 0x32, + 0x54, 0x58, 0x54, 0x00, 0x18, 0x34, 0x47, 0x76, + 0xF9, 0x50, 0x00, 0x00, 0x00, 0x00, 0x48, 0x76, + 0xF9, 0x50, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, // Data region cluster 2 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -2940,41 +3324,25 @@ test "EntryIterator.nextImp - short name only" { // This will also read the next cluster chain. var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 0x42, 0x53, 0x48, 0x4F, - 0x52, 0x54, 0x20, 0x20, - 0x54, 0x58, 0x54, 0x00, - 0x10, 0xA0, 0x68, 0xA9, - 0xFE, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x6E, 0xA9, - 0xFE, 0x50, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, + 0x42, 0x53, 0x48, 0x4F, 0x52, 0x54, 0x20, 0x20, + 0x54, 0x58, 0x54, 0x00, 0x10, 0xA0, 0x68, 0xA9, + 0xFE, 0x50, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xA9, + 0xFE, 0x50, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, // Data region cluster 2 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -3018,59 +3386,35 @@ test "EntryIterator.nextImp - long name only" { // FAT 4 2 long entries, no associated short var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0x05, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0x05, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 0x41, 0x42, 0x00, 0x73, - 0x00, 0x68, 0x00, 0x6F, - 0x00, 0x72, 0x00, 0x0F, - 0x00, 0xA8, 0x74, 0x00, - 0x2E, 0x00, 0x74, 0x00, - 0x78, 0x00, 0x74, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0x41, 0x42, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, + 0x00, 0x72, 0x00, 0x0F, 0x00, 0xA8, 0x74, 0x00, + 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 2 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Data region cluster 3 - 0x41, 0x42, 0x00, 0x73, - 0x00, 0x68, 0x00, 0x6F, - 0x00, 0x72, 0x00, 0x0F, - 0x00, 0xA8, 0x74, 0x00, - 0x2E, 0x00, 0x74, 0x00, - 0x78, 0x00, 0x74, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0x41, 0x42, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, + 0x00, 0x72, 0x00, 0x0F, 0x00, 0xA8, 0x74, 0x00, + 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 4 - 0x41, 0x42, 0x00, 0x73, - 0x00, 0x68, 0x00, 0x6F, - 0x00, 0x72, 0x00, 0x0F, - 0x00, 0xA8, 0x74, 0x00, - 0x2E, 0x00, 0x74, 0x00, - 0x78, 0x00, 0x74, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0x41, 0x42, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, + 0x00, 0x72, 0x00, 0x0F, 0x00, 0xA8, 0x74, 0x00, + 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, }; // FAT 2 test { @@ -3132,50 +3476,30 @@ test "EntryIterator.nextImp - long name, incorrect check sum" { // this has changed from the valid 0xA8 to a invalid 0x55 check sum var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 0x41, 0x42, 0x00, 0x73, - 0x00, 0x68, 0x00, 0x6F, - 0x00, 0x72, 0x00, 0x0F, - 0x00, 0x55, 0x74, 0x00, - 0x2E, 0x00, 0x74, 0x00, - 0x78, 0x00, 0x74, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0x41, 0x42, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, + 0x00, 0x72, 0x00, 0x0F, 0x00, 0x55, 0x74, 0x00, + 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 2 - 0x42, 0x53, 0x48, 0x4F, - 0x52, 0x54, 0x20, 0x20, - 0x54, 0x58, 0x54, 0x00, - 0x10, 0xA0, 0x68, 0xA9, - 0xFE, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x6E, 0xA9, - 0xFE, 0x50, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, + 0x42, 0x53, 0x48, 0x4F, 0x52, 0x54, 0x20, 0x20, + 0x54, 0x58, 0x54, 0x00, 0x10, 0xA0, 0x68, 0xA9, + 0xFE, 0x50, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xA9, + 0xFE, 0x50, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, // Data region cluster 3 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -3218,50 +3542,30 @@ test "EntryIterator.nextImp - long name missing entry" { // this has changed from the valid 0xA8 to a invalid 0x55 check sum var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 0x43, 0x6E, 0x00, 0x67, - 0x00, 0x6E, 0x00, 0x61, - 0x00, 0x6D, 0x00, 0x0F, - 0x00, 0x6E, 0x65, 0x00, - 0x2E, 0x00, 0x74, 0x00, - 0x78, 0x00, 0x74, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0x43, 0x6E, 0x00, 0x67, 0x00, 0x6E, 0x00, 0x61, + 0x00, 0x6D, 0x00, 0x0F, 0x00, 0x6E, 0x65, 0x00, + 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 2 - 0x01, 0x6C, 0x00, 0x6F, - 0x00, 0x6F, 0x00, 0x6F, - 0x00, 0x6F, 0x00, 0x0F, - 0x00, 0x6E, 0x6F, 0x00, - 0x6E, 0x00, 0x67, 0x00, - 0x6C, 0x00, 0x6F, 0x00, - 0x6F, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x6F, 0x00, + 0x01, 0x6C, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6F, + 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x6E, 0x6F, 0x00, + 0x6E, 0x00, 0x67, 0x00, 0x6C, 0x00, 0x6F, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x6F, 0x00, // Data region cluster 3 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -3299,59 +3603,35 @@ test "EntryIterator.nextImp - valid short and long entry" { // Values taken from a real FAT32 implementation var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 0x43, 0x6E, 0x00, 0x67, - 0x00, 0x6E, 0x00, 0x61, - 0x00, 0x6D, 0x00, 0x0F, - 0x00, 0x6E, 0x65, 0x00, - 0x2E, 0x00, 0x74, 0x00, - 0x78, 0x00, 0x74, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0x43, 0x6E, 0x00, 0x67, 0x00, 0x6E, 0x00, 0x61, + 0x00, 0x6D, 0x00, 0x0F, 0x00, 0x6E, 0x65, 0x00, + 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 2 - 0x02, 0x6E, 0x00, 0x67, - 0x00, 0x76, 0x00, 0x65, - 0x00, 0x72, 0x00, 0x0F, - 0x00, 0x6E, 0x79, 0x00, - 0x6C, 0x00, 0x6F, 0x00, - 0x6F, 0x00, 0x6F, 0x00, - 0x6F, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x6F, 0x00, + 0x02, 0x6E, 0x00, 0x67, 0x00, 0x76, 0x00, 0x65, + 0x00, 0x72, 0x00, 0x0F, 0x00, 0x6E, 0x79, 0x00, + 0x6C, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6F, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x6F, 0x00, // Data region cluster 3 - 0x01, 0x6C, 0x00, 0x6F, - 0x00, 0x6F, 0x00, 0x6F, - 0x00, 0x6F, 0x00, 0x0F, - 0x00, 0x6E, 0x6F, 0x00, - 0x6E, 0x00, 0x67, 0x00, - 0x6C, 0x00, 0x6F, 0x00, - 0x6F, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x6F, 0x00, + 0x01, 0x6C, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6F, + 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x6E, 0x6F, 0x00, + 0x6E, 0x00, 0x67, 0x00, 0x6C, 0x00, 0x6F, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x6F, 0x00, // Data region cluster 4 - 0x4C, 0x4F, 0x4F, 0x4F, - 0x4F, 0x4F, 0x7E, 0x31, - 0x54, 0x58, 0x54, 0x00, - 0x18, 0xA0, 0x68, 0xA9, - 0xFE, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x6E, 0xA9, - 0xFE, 0x50, 0x08, 0x00, - 0x13, 0x00, 0x00, 0x00, + 0x4C, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x7E, 0x31, + 0x54, 0x58, 0x54, 0x00, 0x18, 0xA0, 0x68, 0xA9, + 0xFE, 0x50, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xA9, + 0xFE, 0x50, 0x08, 0x00, 0x13, 0x00, 0x00, 0x00, }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -3392,78 +3672,46 @@ test "EntryIterator.next - skips orphan long entry" { // Values taken from a real FAT32 implementation var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 1 - 0x43, 0x6E, 0x00, 0x67, - 0x00, 0x6E, 0x00, 0x61, - 0x00, 0x6D, 0x00, 0x0F, - 0x00, 0x6E, 0x65, 0x00, - 0x2E, 0x00, 0x74, 0x00, - 0x78, 0x00, 0x74, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0x43, 0x6E, 0x00, 0x67, 0x00, 0x6E, 0x00, 0x61, + 0x00, 0x6D, 0x00, 0x0F, 0x00, 0x6E, 0x65, 0x00, + 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Missing 0x02 // Data region cluster 2 - 0x01, 0x6C, 0x00, 0x6F, - 0x00, 0x6F, 0x00, 0x6F, - 0x00, 0x6F, 0x00, 0x0F, - 0x00, 0x6E, 0x6F, 0x00, - 0x6E, 0x00, 0x67, 0x00, - 0x6C, 0x00, 0x6F, 0x00, - 0x6F, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x6F, 0x00, + 0x01, 0x6C, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6F, + 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x6E, 0x6F, 0x00, + 0x6E, 0x00, 0x67, 0x00, 0x6C, 0x00, 0x6F, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x6F, 0x00, // Data region cluster 3 - 0x4C, 0x4F, 0x4F, 0x4F, - 0x4F, 0x4F, 0x7E, 0x31, - 0x54, 0x58, 0x54, 0x00, - 0x18, 0xA0, 0x68, 0xA9, - 0xFE, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x6E, 0xA9, - 0xFE, 0x50, 0x08, 0x00, - 0x13, 0x00, 0x00, 0x00, + 0x4C, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x7E, 0x31, + 0x54, 0x58, 0x54, 0x00, 0x18, 0xA0, 0x68, 0xA9, + 0xFE, 0x50, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xA9, + 0xFE, 0x50, 0x08, 0x00, 0x13, 0x00, 0x00, 0x00, // Data region cluster 4 - 0x42, 0x2E, 0x00, 0x74, - 0x00, 0x78, 0x00, 0x74, - 0x00, 0x00, 0x00, 0x0F, - 0x00, 0xE9, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, + 0x42, 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, + 0x00, 0x00, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Data region cluster 5 - 0x01, 0x72, 0x00, 0x61, - 0x00, 0x6D, 0x00, 0x64, - 0x00, 0x69, 0x00, 0x0F, - 0x00, 0xE9, 0x73, 0x00, - 0x6B, 0x00, 0x5F, 0x00, - 0x74, 0x00, 0x65, 0x00, - 0x73, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x31, 0x00, + 0x01, 0x72, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x64, + 0x00, 0x69, 0x00, 0x0F, 0x00, 0xE9, 0x73, 0x00, + 0x6B, 0x00, 0x5F, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x74, 0x00, 0x31, 0x00, // Data region cluster 6 - 0x52, 0x41, 0x4D, 0x44, - 0x49, 0x53, 0x7E, 0x31, - 0x54, 0x58, 0x54, 0x00, - 0x18, 0x34, 0x47, 0x76, - 0xF9, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x48, 0x76, - 0xF9, 0x50, 0x03, 0x00, - 0x10, 0x00, 0x00, 0x00, + 0x52, 0x41, 0x4D, 0x44, 0x49, 0x53, 0x7E, 0x31, + 0x54, 0x58, 0x54, 0x00, 0x18, 0x34, 0x47, 0x76, + 0xF9, 0x50, 0x00, 0x00, 0x00, 0x00, 0x48, 0x76, + 0xF9, 0x50, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); var cluster_chain = try Fat32FS(@TypeOf(stream)).ClusterChainIterator.init(std.testing.allocator, fat_config, 2, stream); @@ -3510,20 +3758,14 @@ test "EntryIterator.init - free on OutOfMemory" { }; var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region - 'a', 'b', 'c', 'd', - '1', '2', '3', '4', - 'A', 'B', 'C', 'D', - '!', '"', '$', '%', + 'a', 'b', 'c', 'd', '1', '2', '3', '4', + 'A', 'B', 'C', 'D', '!', '"', '$', '%', }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); const allocations: usize = 2; @@ -3553,29 +3795,90 @@ test "EntryIterator.init - free on BadRead" { }; var buff_stream = [_]u8{ // FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Backup FAT region - 0xFF, 0xFF, 0xFF, 0x0F, - 0xF8, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, // Data region (too short) - 'a', 'b', 'c', 'd', - '1', '2', '3', '4', + 'a', 'b', 'c', 'd', '1', '2', '3', '4', }; var stream = &std.io.fixedBufferStream(buff_stream[0..]); expectError(error.BadRead, Fat32FS(@TypeOf(stream)).EntryIterator.init(std.testing.allocator, fat_config, 2, stream)); } +test "Fat32FS.getRootNode" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); + defer test_fs.destroy() catch unreachable; + + expectEqual(test_fs.fs.getRootNode(test_fs.fs), &test_fs.root_node.node.Dir); + expectEqual(test_fs.root_node.cluster, 2); + expectEqual(test_fs.fat_config.root_directory_cluster, 2); +} + +test "Fat32FS.createNode - dir" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); + defer test_fs.destroy() catch unreachable; + + const dir_node = try test_fs.createNode(3, 0, 0, 0, .CREATE_DIR); + defer std.testing.allocator.destroy(dir_node); + expect(dir_node.isDir()); + expect(test_fs.opened_files.contains(dir_node)); + const opened_info = test_fs.opened_files.remove(dir_node).?.value; + defer std.testing.allocator.destroy(opened_info); + expectEqual(opened_info.cluster, 3); + expectEqual(opened_info.size, 0); +} + +test "Fat32FS.createNode - file" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); + defer test_fs.destroy() catch unreachable; + + const file_node = try test_fs.createNode(4, 16, 0, 0, .CREATE_FILE); + defer std.testing.allocator.destroy(file_node); + expect(file_node.isFile()); + expect(test_fs.opened_files.contains(file_node)); + const opened_info = test_fs.opened_files.remove(file_node).?.value; + defer std.testing.allocator.destroy(opened_info); + expectEqual(opened_info.cluster, 4); + expectEqual(opened_info.size, 16); +} + +test "Fat32FS.createNode - symlink" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); + defer test_fs.destroy() catch unreachable; + + expectError(error.InvalidFlags, test_fs.createNode(4, 16, 0, 0, .CREATE_SYMLINK)); +} + +test "Fat32FS.createNode - no create" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); + defer test_fs.destroy() catch unreachable; + + expectError(error.InvalidFlags, test_fs.createNode(4, 16, 0, 0, .NO_CREATION)); +} + test "Fat32FS.createNode - free memory" { const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; // There 2 allocations var allocations: usize = 0; @@ -3583,7 +3886,7 @@ test "Fat32FS.createNode - free memory" { var fa = std.testing.FailingAllocator.init(std.testing.allocator, allocations); const allocator = &fa.allocator; test_fs.allocator = allocator; - expectError(error.OutOfMemory, test_fs.createNode(3, 16, .CREATE_FILE, .{})); + expectError(error.OutOfMemory, test_fs.createNode(3, 16, 0, 0, .CREATE_FILE)); } } @@ -3592,9 +3895,9 @@ test "Fat32FS.getDirCluster - root dir" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - var test_node_1 = try test_fs.createNode(3, 16, .CREATE_FILE, .{}); + var test_node_1 = try test_fs.createNode(3, 16, 0, 0, .CREATE_FILE); defer test_node_1.File.close(); const actual = try test_fs.getDirCluster(&test_fs.root_node.node.Dir); @@ -3606,9 +3909,9 @@ test "Fat32FS.getDirCluster - sub dir" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - var test_node_1 = try test_fs.createNode(5, 0, .CREATE_DIR, .{}); + var test_node_1 = try test_fs.createNode(5, 0, 0, 0, .CREATE_DIR); defer test_node_1.Dir.close(); const actual = try test_fs.getDirCluster(&test_node_1.Dir); @@ -3620,9 +3923,9 @@ test "Fat32FS.getDirCluster - not opened dir" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - var test_node_1 = try test_fs.createNode(5, 0, .CREATE_DIR, .{}); + var test_node_1 = try test_fs.createNode(5, 0, 0, 0, .CREATE_DIR); const elem = test_fs.opened_files.remove(test_node_1).?.value; std.testing.allocator.destroy(elem); @@ -3635,9 +3938,9 @@ test "Fat32FS.openImpl - entry iterator failed init" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - var test_node_1 = try test_fs.createNode(5, 0, .CREATE_DIR, .{}); + var test_node_1 = try test_fs.createNode(5, 0, 0, 0, .CREATE_DIR); defer test_node_1.Dir.close(); var fa = std.testing.FailingAllocator.init(std.testing.allocator, 1); @@ -3652,7 +3955,7 @@ test "Fat32FS.openImpl - entry iterator failed next" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; var fa = std.testing.FailingAllocator.init(std.testing.allocator, 2); const allocator = &fa.allocator; @@ -3666,7 +3969,7 @@ test "Fat32FS.openImpl - entry iterator failed 2nd next" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; var fa = std.testing.FailingAllocator.init(std.testing.allocator, 3); const allocator = &fa.allocator; @@ -3680,7 +3983,7 @@ test "Fat32FS.openImpl - match short name" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; const file_node = try Fat32FS(@TypeOf(test_fat32_image)).openImpl(test_fs.fs, &test_fs.root_node.node.Dir, "INSANE~1.TXT"); defer file_node.File.close(); @@ -3691,7 +3994,7 @@ test "Fat32FS.openImpl - match long name" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; const file_node = try Fat32FS(@TypeOf(test_fat32_image)).openImpl(test_fs.fs, &test_fs.root_node.node.Dir, "insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long.txt"); defer file_node.File.close(); @@ -3702,9 +4005,9 @@ test "Fat32FS.openImpl - no match" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - var test_node_1 = try test_fs.createNode(5, 0, .CREATE_DIR, .{}); + var test_node_1 = try test_fs.createNode(5, 0, 0, 0, .CREATE_DIR); defer test_node_1.Dir.close(); expectError(vfs.Error.NoSuchFileOrDir, Fat32FS(@TypeOf(test_fat32_image)).openImpl(test_fs.fs, &test_node_1.Dir, "file.txt")); @@ -3716,15 +4019,15 @@ test "Fat32FS.open - no create - hand crafted" { var stream = &std.io.fixedBufferStream(test_file_buf[0..]); - try mkfat32.Fat32.make(.{}, stream, true); + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, true); var test_fs = try initialiseFAT32(std.testing.allocator, stream); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; var entry_buff = [_]u8{ // Long entry 3 0x43, 0x6E, 0x00, 0x67, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x0F, 0x00, 0x6E, 0x65, 0x00, - 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Long entry 2 0x02, 0x6E, 0x00, 0x67, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x0F, 0x00, 0x6E, 0x79, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x6F, 0x00, @@ -3736,7 +4039,7 @@ test "Fat32FS.open - no create - hand crafted" { 0xFE, 0x50, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xA9, 0xFE, 0x50, 0x08, 0x00, 0x13, 0x00, 0x00, 0x00, // Long entry 2 0x42, 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, // Long entry 1 0x01, 0x72, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x64, 0x00, 0x69, 0x00, 0x0F, 0x00, 0xE9, 0x73, 0x00, 0x6B, 0x00, 0x5F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x00, 0x00, 0x74, 0x00, 0x31, 0x00, @@ -3750,7 +4053,7 @@ test "Fat32FS.open - no create - hand crafted" { try test_fs.stream.seekableStream().seekTo(sector * test_fs.fat_config.bytes_per_sector); try test_fs.stream.writer().writeAll(entry_buff[0..]); - vfs.setRoot(test_fs.root_node.node); + try vfs.setRoot(test_fs.root_node.node); const file = try vfs.openFile("/ramdisk_test1.txt", .NO_CREATION); defer file.close(); @@ -3761,41 +4064,144 @@ test "Fat32FS.open - no create - hand crafted" { expectEqual(opened_info.size, 16); } +fn testOpenRec(dir_node: *const vfs.DirNode, path: []const u8) anyerror!void { + var test_files = try std.fs.cwd().openDir(path, .{ .iterate = true }); + defer test_files.close(); + + var it = test_files.iterate(); + while (try it.next()) |file| { + if (file.kind == .Directory) { + var dir_path = try std.testing.allocator.alloc(u8, path.len + file.name.len + 1); + defer std.testing.allocator.free(dir_path); + std.mem.copy(u8, dir_path[0..], path); + dir_path[path.len] = '/'; + std.mem.copy(u8, dir_path[path.len + 1 ..], file.name); + const new_dir = &(try dir_node.open(file.name, .NO_CREATION, .{})).Dir; + defer new_dir.close(); + try testOpenRec(new_dir, dir_path); + } else { + const open_file = &(try dir_node.open(file.name, .NO_CREATION, .{})).File; + defer open_file.close(); + } + } +} + test "Fat32FS.open - no create - all files" { const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - vfs.setRoot(test_fs.root_node.node); + try vfs.setRoot(test_fs.root_node.node); - // Check we can open all the expected files correctly - var test_files = try std.fs.cwd().openDir("test/fat32/test_files", .{ .iterate = true }); - defer test_files.close(); - - var it = test_files.iterate(); - while (try it.next()) |file| { - // Need to add a '/' at the beginning - var file_name = try std.testing.allocator.alloc(u8, file.name.len + 1); - defer std.testing.allocator.free(file_name); - file_name[0] = '/'; - std.mem.copy(u8, file_name[1..], file.name); - const open_file = try vfs.openFile(file_name, .NO_CREATION); - defer open_file.close(); - } + try testOpenRec(&test_fs.root_node.node.Dir, "test/fat32/test_files"); } test "Fat32FS.open - create file" { - // TODO: Once the open and write PR is done + var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); + defer std.testing.allocator.free(test_file_buf); + + var stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, true); + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + try vfs.setRoot(test_fs.root_node.node); + + // Open and close + const open_file = try vfs.openFile("/fileαfile€file.txt", .CREATE_FILE); + open_file.close(); + + // Can't open it as a dir + expectError(error.IsAFile, vfs.openDir("/fileαfile€file.txt", .NO_CREATION)); + + // Can we open the same file + const read_file = try vfs.openFile("/fileαfile€file.txt", .NO_CREATION); + defer read_file.close(); + + // Reads nothing + var buff = [_]u8{0xAA} ** 512; + const read = read_file.read(buff[0..]); + expectEqual(read, 0); + expectEqualSlices(u8, buff[0..], &[_]u8{0xAA} ** 512); } test "Fat32FS.open - create directory" { - // TODO: Once the open and write PR is done + var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); + defer std.testing.allocator.free(test_file_buf); + + var stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, true); + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + try vfs.setRoot(test_fs.root_node.node); + + // Open and close + const open_dir = try vfs.openDir("/fileαfile€file", .CREATE_DIR); + open_dir.close(); + + // Can't open it as a file + expectError(error.IsADirectory, vfs.openFile("/fileαfile€file", .NO_CREATION)); + + const open = try vfs.openDir("/fileαfile€file", .NO_CREATION); + defer open.close(); } test "Fat32FS.open - create symlink" { - // TODO: Once the open and write PR is done + var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); + defer std.testing.allocator.free(test_file_buf); + + var stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, true); + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + try vfs.setRoot(test_fs.root_node.node); + + expectError(error.InvalidFlags, vfs.openSymlink("/fileαfile€file.txt", "/file.txt", .CREATE_SYMLINK)); +} + +test "Fat32FS.open - create nested directories" { + var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); + defer std.testing.allocator.free(test_file_buf); + + var stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, true); + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + try vfs.setRoot(test_fs.root_node.node); + + const open1 = try vfs.openDir("/fileαfile€file", .CREATE_DIR); + defer open1.close(); + + const open2 = try vfs.openDir("/fileαfile€file/folder", .CREATE_DIR); + defer open2.close(); + + const open3 = try vfs.openDir("/fileαfile€file/folder/1", .CREATE_DIR); + defer open3.close(); + + const open4 = try vfs.openDir("/fileαfile€file/folder/1/2", .CREATE_DIR); + defer open4.close(); + + const open5 = try vfs.openDir("/fileαfile€file/folder/1/2/3", .CREATE_DIR); + defer open5.close(); + + const open6 = try vfs.openDir("/fileαfile€file/folder/1/2/3/end", .CREATE_DIR); + defer open6.close(); + + const open_dir = try vfs.openDir("/fileαfile€file/folder/1/2/3/end", .NO_CREATION); + defer open_dir.close(); } test "Fat32FS.read - not opened" { @@ -3803,7 +4209,7 @@ test "Fat32FS.read - not opened" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; // Craft a node var node = try std.testing.allocator.create(vfs.Node); @@ -3818,9 +4224,9 @@ test "Fat32FS.read - cluster iterator init fail" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - var test_node = try test_fs.createNode(5, 16, .CREATE_FILE, .{}); + var test_node = try test_fs.createNode(5, 16, 0, 0, .CREATE_FILE); defer test_node.File.close(); var fa = std.testing.FailingAllocator.init(std.testing.allocator, 0); @@ -3836,9 +4242,9 @@ test "Fat32FS.read - buffer smaller than file" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - vfs.setRoot(test_fs.root_node.node); + try vfs.setRoot(test_fs.root_node.node); const test_node = try vfs.openFile("/short.txt", .NO_CREATION); defer test_node.close(); @@ -3853,9 +4259,9 @@ test "Fat32FS.read - buffer bigger than file" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - vfs.setRoot(test_fs.root_node.node); + try vfs.setRoot(test_fs.root_node.node); const test_node = try vfs.openFile("/short.txt", .NO_CREATION); defer test_node.close(); @@ -3872,9 +4278,9 @@ test "Fat32FS.read - large" { defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - vfs.setRoot(test_fs.root_node.node); + try vfs.setRoot(test_fs.root_node.node); const test_node = try vfs.openFile("/large_file.txt", .NO_CREATION); defer test_node.close(); @@ -3887,43 +4293,1800 @@ test "Fat32FS.read - large" { expectEqualSlices(u8, buff[0..], large_file_content[0..]); } +fn testReadRec(dir_node: *const vfs.DirNode, path: []const u8, read_big: bool) anyerror!void { + var test_files = try std.fs.cwd().openDir(path, .{ .iterate = true }); + defer test_files.close(); + + var it = test_files.iterate(); + while (try it.next()) |file| { + if (file.kind == .Directory) { + var dir_path = try std.testing.allocator.alloc(u8, path.len + file.name.len + 1); + defer std.testing.allocator.free(dir_path); + std.mem.copy(u8, dir_path[0..], path); + dir_path[path.len] = '/'; + std.mem.copy(u8, dir_path[path.len + 1 ..], file.name); + const new_dir = &(try dir_node.open(file.name, .NO_CREATION, .{})).Dir; + defer new_dir.close(); + try testReadRec(new_dir, dir_path, read_big); + } else { + const open_file = &(try dir_node.open(file.name, .NO_CREATION, .{})).File; + defer open_file.close(); + + // Have tested the large file + if (!read_big and std.mem.eql(u8, file.name, "large_file.txt")) { + continue; + } else if (read_big and std.mem.eql(u8, file.name, "large_file.txt")) { + var buff = [_]u8{0xAA} ** 8450; + const large_file_content = @embedFile("../../../test/fat32/test_files/large_file.txt"); + const read = try open_file.read(buff[0..]); + expectEqualSlices(u8, buff[0..], large_file_content[0..]); + expectEqual(read, 8450); + continue; + } + + // Big enough + var buff = [_]u8{0xAA} ** 256; + const read = try open_file.read(buff[0..]); + + // The file content is the same as the file name + expectEqual(file.name.len, read); + expectEqualSlices(u8, buff[0..read], file.name[0..]); + } + } +} + test "Fat32FS.read - all test files" { const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); defer test_fat32_image.close(); var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); - defer test_fs.destroy(); + defer test_fs.destroy() catch unreachable; - vfs.setRoot(test_fs.root_node.node); + try vfs.setRoot(test_fs.root_node.node); // Check we can open all the expected files correctly var test_files = try std.fs.cwd().openDir("test/fat32/test_files", .{ .iterate = true }); defer test_files.close(); - var it = test_files.iterate(); - while (try it.next()) |file| { - // Have tested the large file - if (std.mem.eql(u8, file.name, "large_file.txt")) { - continue; - } - // Need to add a '/' at the beginning - var file_name = try std.testing.allocator.alloc(u8, file.name.len + 1); - defer std.testing.allocator.free(file_name); - file_name[0] = '/'; - std.mem.copy(u8, file_name[1..], file.name); - const open_file = try vfs.openFile(file_name, .NO_CREATION); - defer open_file.close(); + try testReadRec(&test_fs.root_node.node.Dir, "test/fat32/test_files", false); +} - // Big enough - var buff = [_]u8{0xAA} ** 256; - const read = try open_file.read(buff[0..]); +test "Fat32FS.findNextFreeCluster - free on error" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + // Too small + var fat_buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + }; + var stream = &std.io.fixedBufferStream(fat_buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; - // The file content is the same as the file name - expectEqual(file_name.len - 1, read); - expectEqualSlices(u8, buff[0..read], file_name[1..]); + expectError(error.BadRead, test_fs.findNextFreeCluster(2, null)); +} + +test "Fat32FS.findNextFreeCluster - alloc cluster in first sector" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 2, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + // 6th entry is free + var fat_buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // FAT region 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(fat_buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const cluster = try test_fs.findNextFreeCluster(2, null); + expectEqual(cluster, 6); + // check the FAT where the update would happen + backup FAT + expectEqualSlices(u8, fat_buff_stream[24..28], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, fat_buff_stream[88..92], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.findNextFreeCluster - alloc cluster in second sector" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 2, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + // 6th entry is free + var fat_buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + // FAT region 2 + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + // Backup FAT region 2 + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(fat_buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const cluster = try test_fs.findNextFreeCluster(10, null); + expectEqual(cluster, 10); + // check the FAT where the update would happen + backup FAT + expectEqualSlices(u8, fat_buff_stream[40..44], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, fat_buff_stream[104..108], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.findNextFreeCluster - alloc cluster over sector boundary" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 2, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + // 6th entry is free + var fat_buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + // FAT region 2 + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + // Backup FAT region 2 + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(fat_buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const cluster = try test_fs.findNextFreeCluster(2, null); + expectEqual(cluster, 10); + // check the FAT where the update would happen + backup FAT + expectEqualSlices(u8, fat_buff_stream[24..28], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, fat_buff_stream[88..92], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.findNextFreeCluster - no free cluster" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + // 6th entry is free + var fat_buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + // FAT region 2 + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + }; + var stream = &std.io.fixedBufferStream(fat_buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + expectError(error.DiskFull, test_fs.findNextFreeCluster(2, null)); +} + +test "Fat32FS.findNextFreeCluster - updates FSInfo" { + const fat_config = FATConfig{ + .bytes_per_sector = 512, + .sectors_per_cluster = 1, + .reserved_sectors = 2, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 2, + .root_directory_cluster = undefined, + .fsinfo_sector = 0, + .backup_boot_sector = 1, + .has_fs_info = true, + .number_free_clusters = 10, + .next_free_cluster = 6, + .cluster_end_marker = 0x0FFFFFFF, + }; + // 6th entry is free + var buff_stream = [_]u8{0x00} ** 488 ++ [_]u8{ + // FSInfo + 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + } ++ [_]u8{0x00} ** 504 ++ [_]u8{ + // Backup FSInfo + 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } ++ [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } ++ [_]u8{0x00} ** 480 ++ [_]u8{ + // FAT region 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } ++ [_]u8{0x00} ** 480 ++ [_]u8{ + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } ++ [_]u8{0x00} ** 480 ++ [_]u8{ + // Backup FAT region 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } ++ [_]u8{0x00} ** 480; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const cluster = try test_fs.findNextFreeCluster(2, null); + expectEqual(cluster, 6); + expectEqual(test_fs.fat_config.number_free_clusters, 9); + expectEqual(test_fs.fat_config.next_free_cluster, 7); + expectEqual(buff_stream[488], 9); + expectEqual(buff_stream[492], 7); + expectEqual(buff_stream[1000], 9); + expectEqual(buff_stream[1004], 7); +} + +test "Fat32FS.findNextFreeCluster - updates cluster chain with parent" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 2, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + // 6th entry is free + var fat_buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // FAT region 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(fat_buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const cluster = try test_fs.findNextFreeCluster(2, 5); + expectEqual(cluster, 6); + // check the FAT where the update would happen + backup FAT + expectEqualSlices(u8, fat_buff_stream[20..28], &[_]u8{ 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, fat_buff_stream[84..92], &[_]u8{ 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.nameToLongName - name too long" { + const long_name = [_]u8{'A'} ** 256; + var stream = &std.io.fixedBufferStream(&[_]u8{}); + expectError(error.InvalidName, Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, long_name[0..])); +} + +test "Fat32FS.nameToLongName - leading spaces" { + const name_cases = [_][]const u8{ + " file.txt", + " file.txt", + [_]u8{' '} ** 256 ++ "file.txt", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + + const expected = [_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }; + + for (name_cases) |case| { + const actual = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(actual); + expectEqualSlices(u16, expected[0..], actual); } } -test "Fat32FS.write" { - // TODO: Once the write PR is done +test "Fat32FS.nameToLongName - invalid name" { + const name_cases = [_][]const u8{ + "\"file.txt", + "*file.txt", + "/file.txt", + ":file.txt", + "file.txt", + "?file.txt", + "\\file.txt", + "|file.txt", + [_]u8{0x10} ++ "file.txt", + [_]u8{0x7F} ++ "file.txt", + "\u{12345}file.txt", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + for (name_cases) |case| { + expectError(error.InvalidName, Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..])); + } +} + +test "Fat32FS.nameToLongName - trailing spaces or dots" { + const name_cases = [_][]const u8{ + "file.txt ", + "file.txt....", + "file.txt . .", + "file.txt" ++ [_]u8{' '} ** 256, + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + + const expected = [_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }; + + for (name_cases) |case| { + const actual = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(actual); + expectEqualSlices(u16, expected[0..], actual); + } +} + +test "Fat32FS.nameToLongName - valid name" { + const name_cases = [_][]const u8{ + "....leading_dots.txt", + "[nope].txt", + "A_verY_Long_File_namE_With_normal_Extension.tXt", + "dot.in.file.txt", + "file.long_ext", + "file.t x t", + "insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long_insanely_long.txt", + "nope.[x]", + "s p a c e s.txt", + "UTF16.€xt", + "UTF16€.txt", + "αlpha.txt", + "file.txt", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + for (name_cases) |case| { + // Can just test no error + const actual = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(actual); + } +} + +test "Fat32FS.isValidSFNChar - invalid" { + var stream = &std.io.fixedBufferStream(&[_]u8{}); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar(' '), null); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar('€'), null); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar('+'), null); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar(','), null); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar(';'), null); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar('='), null); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar('['), null); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar(']'), null); + + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar('α'), 0xE0); + expectEqual(Fat32FS(@TypeOf(stream)).isValidSFNChar('a'), 'a'); +} + +test "Fat32FS.longNameToShortName - leading dots and spaces" { + // Using valid long names + const name_cases = [_][]const u8{ + "....file.txt", + ". . file.txt", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILE~1 TXT"; + + for (name_cases) |case| { + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, &[_][11]u8{}); + expectEqualSlices(u8, actual[0..], expected[0..]); + } +} + +test "Fat32FS.longNameToShortName - embedded spaces" { + // Using valid long names + const name_cases = [_][]const u8{ + "f i l e.txt", + "fi le.txt", + "file.t x t", + "file.tx t", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILE~1 TXT"; + + for (name_cases) |case| { + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, &[_][11]u8{}); + expectEqualSlices(u8, actual[0..], expected[0..]); + } +} + +test "Fat32FS.longNameToShortName - dot before end" { + // Using valid long names + const name_cases = [_][]const u8{ + "fi.le.txt", + "f.i.l.e.txt", + "fi.....le.txt", + "fi. le.txt", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILE~1 TXT"; + + for (name_cases) |case| { + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, &[_][11]u8{}); + expectEqualSlices(u8, actual[0..], expected[0..]); + } +} + +test "Fat32FS.longNameToShortName - long name" { + // Using valid long names + const name_cases = [_][]const u8{ + "loooooong.txt", + "loooooo.ng.txt", + "loooooo.ng€.txt", + "looooo€.ng.txt", + "loooooong.txttttt", + "looooo.txttttt", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "LOOOOO~1TXT"; + + for (name_cases) |case| { + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, &[_][11]u8{}); + expectEqualSlices(u8, actual[0..], expected[0..]); + } +} + +test "Fat32FS.longNameToShortName - short name" { + // Using valid long names + const name_cases = [_][]const u8{ + "file1234.txt", + "FiLe1234.txt", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILE1234TXT"; + + for (name_cases) |case| { + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, &[_][11]u8{}); + expectEqualSlices(u8, actual[0..], expected[0..]); + } +} + +test "Fat32FS.longNameToShortName - invalid short name characters" { + // Using valid long names + const name_cases = [_][]const u8{ + "+file.txt", + ",file.txt", + ";file.txt", + "=file.txt", + "[file.txt", + "]file.txt", + "€file.txt", + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "_FILE~1 TXT"; + + for (name_cases) |case| { + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, case[0..]); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, &[_][11]u8{}); + expectEqualSlices(u8, actual[0..], expected[0..]); + } +} + +test "Fat32FS.longNameToShortName - existing name short" { + const excising_names = &[_][11]u8{ + "FILE TXT".*, + "FILE~1 TXT".*, + "FILE~A TXT".*, + "FILE~2 TXT".*, + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILE~3 TXT"; + + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "file.txt"); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, excising_names); + expectEqualSlices(u8, actual[0..], expected[0..]); +} + +test "Fat32FS.longNameToShortName - existing name short rev" { + const excising_names = &[_][11]u8{ + "FILE~2 TXT".*, + "FILE~A TXT".*, + "FILE~1 TXT".*, + "FILE TXT".*, + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILE~3 TXT"; + + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "file.txt"); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, excising_names); + expectEqualSlices(u8, actual[0..], expected[0..]); +} + +test "Fat32FS.longNameToShortName - existing name long" { + const excising_names = &[_][11]u8{ + "FILEFILETXT".*, + "FILEFI~1TXT".*, + "FILEFI~ATXT".*, + "FILEFI~2TXT".*, + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILEFI~3TXT"; + + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "filefilefile.txt"); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, excising_names); + expectEqualSlices(u8, actual[0..], expected[0..]); +} + +test "Fat32FS.longNameToShortName - existing name long no match" { + const excising_names = &[_][11]u8{ + "FILEFI~1TXT".*, + "FILEFI~ATXT".*, + "FILEFI~2TXT".*, + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILEFILETXT"; + + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "filefile.txt"); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, excising_names); + expectEqualSlices(u8, actual[0..], expected[0..]); +} + +test "Fat32FS.longNameToShortName - trail number to large" { + const excising_names = &[_][11]u8{ + "F~999999TXT".*, + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "filefilefile.txt"); + defer std.testing.allocator.free(long_name); + expectError(error.InvalidName, Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, excising_names)); +} + +test "Fat32FS.longNameToShortName - large trail number" { + const excising_names = &[_][11]u8{ + "FILE TXT".*, + "FILE~2 TXT".*, + "FILE~3 TXT".*, + "FILE~4 TXT".*, + "FILE~5 TXT".*, + "FILE~6 TXT".*, + "FILE~7 TXT".*, + "FILE~8 TXT".*, + "FILE~9 TXT".*, + }; + + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = "FILE~10 TXT"; + + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "file.txt"); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, excising_names); + expectEqualSlices(u8, actual[0..], expected[0..]); +} + +test "Fat32FS.longNameToShortName - CP437" { + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const expected = [_]u8{0xE0} ++ "LPHA TXT"; + + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "αlpha.txt"); + defer std.testing.allocator.free(long_name); + const actual = try Fat32FS(@TypeOf(stream)).longNameToShortName(long_name, &[_][11]u8{}); + expectEqualSlices(u8, actual[0..], expected[0..]); +} + +test "Fat32FS.createLongNameEntry - less than 13 characters" { + var stream = &std.io.fixedBufferStream(&[_]u8{}); + // Pre-calculated check fum for file.txt => FILE TXT + const check_sum: u8 = 25; + // Using valid long name + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "file.txt"); + defer std.testing.allocator.free(long_name); + const entries = try Fat32FS(@TypeOf(stream)).createLongNameEntry(std.testing.allocator, long_name, check_sum); + defer std.testing.allocator.free(entries); + + expectEqual(entries.len, 1); + + const expected = LongName{ + .order = 0x41, + .first = [_]u16{'f'} ++ [_]u16{'i'} ++ [_]u16{'l'} ++ [_]u16{'e'} ++ [_]u16{'.'}, + .check_sum = check_sum, + .second = [_]u16{'t'} ++ [_]u16{'x'} ++ [_]u16{'t'} ++ [_]u16{ 0x0000, 0xFFFF, 0xFFFF }, + .third = [_]u16{ 0xFFFF, 0xFFFF }, + }; + + expectEqual(entries[0], expected); +} + +test "Fat32FS.createLongNameEntry - greater than 13 characters" { + var stream = &std.io.fixedBufferStream(&[_]u8{}); + // Pre-calculated check fum for filefilefilefile.txt => FILEFI~1TXT + const check_sum: u8 = 123; + // Using valid long name + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "filefilefilefile.txt"); + defer std.testing.allocator.free(long_name); + const entries = try Fat32FS(@TypeOf(stream)).createLongNameEntry(std.testing.allocator, long_name, check_sum); + defer std.testing.allocator.free(entries); + + expectEqual(entries.len, 2); + + var expected = [_]LongName{ + LongName{ + .order = 0x42, + .first = [_]u16{'i'} ++ [_]u16{'l'} ++ [_]u16{'e'} ++ [_]u16{'.'} ++ [_]u16{'t'}, + .check_sum = check_sum, + .second = [_]u16{'x'} ++ [_]u16{'t'} ++ [_]u16{ 0x0000, 0xFFFF, 0xFFFF, 0xFFFF }, + .third = [_]u16{ 0xFFFF, 0xFFFF }, + }, + LongName{ + .order = 0x01, + .first = [_]u16{'f'} ++ [_]u16{'i'} ++ [_]u16{'l'} ++ [_]u16{'e'} ++ [_]u16{'f'}, + .check_sum = check_sum, + .second = [_]u16{'i'} ++ [_]u16{'l'} ++ [_]u16{'e'} ++ [_]u16{'f'} ++ [_]u16{'i'} ++ [_]u16{'l'}, + .third = [_]u16{'e'} ++ [_]u16{'f'}, + }, + }; + + expectEqual(entries[0], expected[0]); + expectEqual(entries[1], expected[1]); +} + +test "Fat32FS.createLongNameEntry - max 255 characters" { + var stream = &std.io.fixedBufferStream(&[_]u8{}); + // Pre-calculated check fum for A**255 => AAAAAA~1TXT + const check_sum: u8 = 17; + // Using valid long name + const name = [_]u8{'A'} ** 255; + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, name[0..]); + defer std.testing.allocator.free(long_name); + const entries = try Fat32FS(@TypeOf(stream)).createLongNameEntry(std.testing.allocator, long_name, check_sum); + defer std.testing.allocator.free(entries); + + expectEqual(entries.len, 20); + + const UA = [_]u16{'A'}; + + var expected = [_]LongName{LongName{ + .order = 0x00, + .first = UA ** 5, + .check_sum = check_sum, + .second = UA ** 6, + .third = UA ** 2, + }} ** 20; + + for (expected) |*e, i| { + e.order = 20 - @intCast(u8, i); + } + expected[0] = LongName{ + .order = 0x54, // 0x40 | 0x14 + .first = UA ** 5, + .check_sum = check_sum, + .second = UA ** 3 ++ [_]u16{ 0x0000, 0xFFFF, 0xFFFF }, + .third = [_]u16{ 0xFFFF, 0xFFFF }, + }; + + for (expected) |ex, i| { + expectEqual(entries[i], ex); + } +} + +test "Fat32FS.createShortNameEntry" { + var stream = &std.io.fixedBufferStream(&[_]u8{}); + const actual = Fat32FS(@TypeOf(stream)).createShortNameEntry("FILE TXT".*, .None, 0x10); + // Expects 12:12:13 12/12/2012 from mock arch + const expected = ShortName{ + .name = "FILE ".*, + .extension = "TXT".*, + .attributes = 0x00, + .time_created_tenth = 0x64, // 100 (1 sec) + .time_created = 0x6186, + .date_created = 0x418C, + .date_last_access = 0x418C, + .cluster_high = 0x00, + .time_last_modification = 0x6186, + .date_last_modification = 0x418C, + .cluster_low = 0x10, + .size = 0x00000000, + }; + expectEqual(actual, expected); +} + +test "Fat32FS.writeEntries - all free cluster" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = undefined, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + + var buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const entries = FatDirEntry{ + .short_entry = Fat32FS(@TypeOf(stream)).createShortNameEntry("FILE TXT".*, .None, 3), + .long_entry = &[_]LongName{}, + }; + + // Convert to bytes + var expected_bytes: [32]u8 = undefined; + initBytes(ShortName, entries.short_entry, expected_bytes[0..]); + + _ = try test_fs.writeEntries(entries, 2, 3, 0); + expectEqualSlices(u8, expected_bytes[0..], buff_stream[64..]); +} + +test "Fat32FS.writeEntries - half free cluster" { + const fat_config = FATConfig{ + .bytes_per_sector = 64, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = undefined, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + + var buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region + 0x49, 0x4E, 0x53, 0x41, 0x4E, 0x45, 0x7E, 0x31, + 0x54, 0x58, 0x54, 0x20, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x88, 0x51, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x0D, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const entries = FatDirEntry{ + .short_entry = Fat32FS(@TypeOf(stream)).createShortNameEntry("FILE TXT".*, .None, 3), + .long_entry = &[_]LongName{}, + }; + + // Convert to bytes + var expected_bytes: [32]u8 = undefined; + initBytes(ShortName, entries.short_entry, expected_bytes[0..]); + + _ = try test_fs.writeEntries(entries, 2, 3, 32); + expectEqualSlices(u8, expected_bytes[0..], buff_stream[160..]); +} + +test "Fat32FS.writeEntries - full cluster" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = undefined, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + + var buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 1 + 0x49, 0x4E, 0x53, 0x41, 0x4E, 0x45, 0x7E, 0x31, + 0x54, 0x58, 0x54, 0x20, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x88, 0x51, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x0D, 0x00, 0xE3, 0x00, 0x00, 0x00, + // Data region 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const entries = FatDirEntry{ + .short_entry = Fat32FS(@TypeOf(stream)).createShortNameEntry("FILE TXT".*, .None, 3), + .long_entry = &[_]LongName{}, + }; + + // Convert to bytes + var expected_bytes: [32]u8 = undefined; + initBytes(ShortName, entries.short_entry, expected_bytes[0..]); + + _ = try test_fs.writeEntries(entries, 2, 3, 32); + expectEqualSlices(u8, expected_bytes[0..], buff_stream[96..]); + expectEqualSlices(u8, buff_stream[8..16], &[_]u8{ 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, buff_stream[40..48], &[_]u8{ 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.writeEntries - large entry over 3 clusters" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = undefined, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = undefined, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + + var buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 1 + 0x49, 0x4E, 0x53, 0x41, 0x4E, 0x45, 0x7E, 0x31, + 0x54, 0x58, 0x54, 0x20, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x88, 0x51, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x0D, 0x00, 0xE3, 0x00, 0x00, 0x00, + // Data region 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 4 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const short_entry = Fat32FS(@TypeOf(stream)).createShortNameEntry("FILE TXT".*, .None, 3); + + const long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "filefilefile.txt"); + defer std.testing.allocator.free(long_name); + const long_entry = try Fat32FS(@TypeOf(stream)).createLongNameEntry(std.testing.allocator, long_name, short_entry.calcCheckSum()); + defer std.testing.allocator.free(long_entry); + + expectEqual(long_entry.len, 2); + + const entries = FatDirEntry{ + .short_entry = short_entry, + .long_entry = long_entry, + }; + + // Convert to bytes + var expected_bytes: [96]u8 = undefined; + initBytes(LongName, entries.long_entry[0], expected_bytes[0..32]); + initBytes(LongName, entries.long_entry[1], expected_bytes[32..64]); + initBytes(ShortName, entries.short_entry, expected_bytes[64..]); + + _ = try test_fs.writeEntries(entries, 2, 3, 32); + expectEqualSlices(u8, expected_bytes[0..], buff_stream[96..]); +} + +test "Fat32FS.createFileOrDir - create file" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = 2, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + var buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 1 (Root dir long name) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 2 (File) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 3 (Root dir short name) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const file = try Fat32FS(@TypeOf(stream)).createFileOrDir(test_fs.fs, &test_fs.root_node.node.Dir, "file.txt", false); + defer file.File.close(); + + const expected_short_entry = Fat32FS(@TypeOf(stream)).createShortNameEntry("FILE TXT".*, .None, 3); + const expected_long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "file.txt"); + defer std.testing.allocator.free(expected_long_name); + const expected_long_entry = try Fat32FS(@TypeOf(stream)).createLongNameEntry(std.testing.allocator, expected_long_name, expected_short_entry.calcCheckSum()); + defer std.testing.allocator.free(expected_long_entry); + + var temp_buf: [32]u8 = undefined; + initBytes(LongName, expected_long_entry[0], temp_buf[0..]); + expectEqualSlices(u8, buff_stream[64..96], temp_buf[0..]); + initBytes(ShortName, expected_short_entry, temp_buf[0..]); + expectEqualSlices(u8, buff_stream[128..], temp_buf[0..]); + + // FAT + expectEqualSlices(u8, buff_stream[8..12], &[_]u8{ 0x04, 0x00, 0x00, 0x00 }); + expectEqualSlices(u8, buff_stream[12..16], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, buff_stream[16..20], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.createFileOrDir - create directory" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = 2, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + var buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 1 (Root dir long name) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 2 (Directory) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 3 (Root dir short name) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const file = try Fat32FS(@TypeOf(stream)).createFileOrDir(test_fs.fs, &test_fs.root_node.node.Dir, "folder", true); + defer file.Dir.close(); + + const expected_short_entry = Fat32FS(@TypeOf(stream)).createShortNameEntry("FOLDER ".*, .Directory, 3); + const expected_long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "folder"); + defer std.testing.allocator.free(expected_long_name); + const expected_long_entry = try Fat32FS(@TypeOf(stream)).createLongNameEntry(std.testing.allocator, expected_long_name, expected_short_entry.calcCheckSum()); + defer std.testing.allocator.free(expected_long_entry); + + var temp_buf: [32]u8 = undefined; + initBytes(LongName, expected_long_entry[0], temp_buf[0..]); + expectEqualSlices(u8, buff_stream[64..96], temp_buf[0..]); + initBytes(ShortName, expected_short_entry, temp_buf[0..]); + expectEqualSlices(u8, buff_stream[128..], temp_buf[0..]); + + // FAT + expectEqualSlices(u8, buff_stream[8..12], &[_]u8{ 0x04, 0x00, 0x00, 0x00 }); + expectEqualSlices(u8, buff_stream[12..16], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, buff_stream[16..20], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.createFileOrDir - create file parent cluster full" { + const fat_config = FATConfig{ + .bytes_per_sector = 32, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = 2, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + var buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 1 (Root dir full) + 0x49, 0x4E, 0x53, 0x41, 0x4E, 0x45, 0x7E, 0x31, + 0x54, 0x58, 0x54, 0x20, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x88, 0x51, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x0D, 0x00, 0xE3, 0x00, 0x00, 0x00, + // Data region 2 (File) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 3 (Root dir long name) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 4 (Root dir short name) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const file = try Fat32FS(@TypeOf(stream)).createFileOrDir(test_fs.fs, &test_fs.root_node.node.Dir, "file.txt", false); + defer file.File.close(); + + const expected_short_entry = Fat32FS(@TypeOf(stream)).createShortNameEntry("FILE TXT".*, .None, 3); + const expected_long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "file.txt"); + defer std.testing.allocator.free(expected_long_name); + const expected_long_entry = try Fat32FS(@TypeOf(stream)).createLongNameEntry(std.testing.allocator, expected_long_name, expected_short_entry.calcCheckSum()); + defer std.testing.allocator.free(expected_long_entry); + + var temp_buf: [32]u8 = undefined; + initBytes(LongName, expected_long_entry[0], temp_buf[0..]); + expectEqualSlices(u8, buff_stream[128..160], temp_buf[0..]); + initBytes(ShortName, expected_short_entry, temp_buf[0..]); + expectEqualSlices(u8, buff_stream[160..], temp_buf[0..]); + + // FAT + expectEqualSlices(u8, buff_stream[8..12], &[_]u8{ 0x04, 0x00, 0x00, 0x00 }); + expectEqualSlices(u8, buff_stream[12..16], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, buff_stream[16..20], &[_]u8{ 0x05, 0x00, 0x00, 0x00 }); + expectEqualSlices(u8, buff_stream[20..24], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.createFileOrDir - half root" { + const fat_config = FATConfig{ + .bytes_per_sector = 64, + .sectors_per_cluster = 1, + .reserved_sectors = 0, + .hidden_sectors = undefined, + .total_sectors = undefined, + .sectors_per_fat = 1, + .root_directory_cluster = 2, + .fsinfo_sector = undefined, + .backup_boot_sector = undefined, + .has_fs_info = false, + .number_free_clusters = undefined, + .next_free_cluster = undefined, + .cluster_end_marker = 0x0FFFFFFF, + }; + + var buff_stream = [_]u8{ + // FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Backup FAT region 1 + 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 1 (Root long) + 0x49, 0x4E, 0x53, 0x41, 0x4E, 0x45, 0x7E, 0x31, + 0x54, 0x58, 0x54, 0x20, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x88, 0x51, 0x00, 0x00, 0x9B, 0xB9, + 0x88, 0x51, 0x0D, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 2 (File) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Data region 2 (Root short half) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + var stream = &std.io.fixedBufferStream(buff_stream[0..]); + var test_fs = try testFAT32FS(std.testing.allocator, stream, fat_config); + defer test_fs.destroy() catch unreachable; + + const file = try Fat32FS(@TypeOf(stream)).createFileOrDir(test_fs.fs, &test_fs.root_node.node.Dir, "file.txt", false); + defer file.File.close(); + + const expected_short_entry = Fat32FS(@TypeOf(stream)).createShortNameEntry("FILE TXT".*, .None, 3); + const expected_long_name = try Fat32FS(@TypeOf(stream)).nameToLongName(std.testing.allocator, "file.txt"); + defer std.testing.allocator.free(expected_long_name); + const expected_long_entry = try Fat32FS(@TypeOf(stream)).createLongNameEntry(std.testing.allocator, expected_long_name, expected_short_entry.calcCheckSum()); + defer std.testing.allocator.free(expected_long_entry); + + var temp_buf: [32]u8 = undefined; + initBytes(LongName, expected_long_entry[0], temp_buf[0..]); + expectEqualSlices(u8, buff_stream[160..192], temp_buf[0..]); + initBytes(ShortName, expected_short_entry, temp_buf[0..]); + expectEqualSlices(u8, buff_stream[256..288], temp_buf[0..]); + + // FAT + expectEqualSlices(u8, buff_stream[8..12], &[_]u8{ 0x04, 0x00, 0x00, 0x00 }); + expectEqualSlices(u8, buff_stream[12..16], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); + expectEqualSlices(u8, buff_stream[16..20], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); +} + +test "Fat32FS.write - small file" { + var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); + defer std.testing.allocator.free(test_file_buf); + + var stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, false); + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + try vfs.setRoot(test_fs.root_node.node); + + const file = try vfs.openFile("/file.txt", .CREATE_FILE); + + const text = "Hello, world!\n"; + + const written = try file.write(text[0..]); + expectEqual(written, text.len); + + var read_buf1: [text.len * 2]u8 = undefined; + const read1 = try file.read(read_buf1[0..]); + expectEqual(read1, text.len); + expectEqualSlices(u8, text[0..], read_buf1[0..read1]); + file.close(); + + const read_file = try vfs.openFile("/file.txt", .NO_CREATION); + defer read_file.close(); + + var read_buf2: [text.len * 2]u8 = undefined; + const read2 = try read_file.read(read_buf2[0..]); + + expectEqual(read2, text.len); + expectEqualSlices(u8, text[0..], read_buf2[0..read2]); +} + +test "Fat32FS.write - large file" { + var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); + defer std.testing.allocator.free(test_file_buf); + + var stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, false); + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + try vfs.setRoot(test_fs.root_node.node); + + const file = try vfs.openFile("/file.txt", .CREATE_FILE); + + // Check the opened file + const open_info1 = test_fs.opened_files.get(@ptrCast(*const vfs.Node, file)).?; + expectEqual(open_info1.cluster, 3); + expectEqual(open_info1.size, 0); + expectEqual(open_info1.entry_cluster, 2); + expectEqual(open_info1.entry_offset, 60); + + const fat_offset = test_fs.fat_config.reserved_sectors * test_fs.fat_config.bytes_per_sector + 12; + expectEqualSlices(u8, test_file_buf[fat_offset .. fat_offset + 4], &[_]u8{ 0xFF, 0xFF, 0xFF, 0x0F }); + + const text = [_]u8{'A'} ** (8 * 1024); + + const written = try file.write(text[0..]); + expectEqual(written, text.len); + + // Check the FAT + const expected_fat = [_]u32{ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x0FFFFFFF }; + expectEqualSlices(u8, test_file_buf[fat_offset .. fat_offset + (16 * 4)], std.mem.sliceAsBytes(expected_fat[0..])); + + var read_buf1: [text.len * 2]u8 = undefined; + const read1 = try file.read(read_buf1[0..]); + expectEqual(read1, text.len); + expectEqualSlices(u8, text[0..], read_buf1[0..read1]); + file.close(); + + const read_file = try vfs.openFile("/file.txt", .NO_CREATION); + defer read_file.close(); + + const open_info2 = test_fs.opened_files.get(@ptrCast(*const vfs.Node, read_file)).?; + expectEqual(open_info2.cluster, 3); + expectEqual(open_info2.size, text.len); + expectEqual(open_info2.entry_cluster, 2); + expectEqual(open_info2.entry_offset, 60); + + var read_buf2: [text.len * 2]u8 = undefined; + const read2 = try read_file.read(read_buf2[0..]); + + expectEqual(read2, text.len); + expectEqualSlices(u8, text[0..], read_buf2[0..read2]); +} + +fn testWriteRec(dir_node: *const vfs.DirNode, path: []const u8) anyerror!void { + var test_files = try std.fs.cwd().openDir(path, .{ .iterate = true }); + defer test_files.close(); + + var it = test_files.iterate(); + while (try it.next()) |file| { + if (file.kind == .Directory) { + var dir_path = try std.testing.allocator.alloc(u8, path.len + file.name.len + 1); + defer std.testing.allocator.free(dir_path); + std.mem.copy(u8, dir_path[0..], path); + dir_path[path.len] = '/'; + std.mem.copy(u8, dir_path[path.len + 1 ..], file.name); + const new_dir = &(try dir_node.open(file.name, .CREATE_DIR, .{})).Dir; + defer new_dir.close(); + try testWriteRec(new_dir, dir_path); + } else { + // Open the test file + const test_file = try test_files.openFile(file.name, .{}); + defer test_file.close(); + + // Read the content + const test_file_content = try test_file.readToEndAlloc(std.testing.allocator, 0xFFFF); + defer std.testing.allocator.free(test_file_content); + + const open_file = &(try dir_node.open(file.name, .CREATE_FILE, .{})).File; + defer open_file.close(); + + // Write the content + const written = try open_file.write(test_file_content); + if (written != test_file_content.len) { + return error.BadWrite; + } + } + } +} + +test "Fat32FS.write - test files" { + var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); + defer std.testing.allocator.free(test_file_buf); + + var stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, false); + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + try vfs.setRoot(test_fs.root_node.node); + + try testWriteRec(&test_fs.root_node.node.Dir, "test/fat32/test_files"); + + const image = try std.fs.cwd().createFile("ig.img", .{}); + defer image.close(); + + _ = try image.writer().writeAll(test_file_buf); + + try testReadRec(&test_fs.root_node.node.Dir, "test/fat32/test_files", true); +} + +test "Fat32FS.write - not enough space" { + var test_file_buf = try std.testing.allocator.alloc(u8, 37 * 512); + defer std.testing.allocator.free(test_file_buf); + + var stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + try mkfat32.Fat32.make(.{ .image_size = test_file_buf.len }, stream, false); + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + try vfs.setRoot(test_fs.root_node.node); + + const text = [_]u8{'A'} ** 1025; + + const file = try vfs.openFile("/file.txt", .CREATE_FILE); + defer file.close(); + + expectError(error.Unexpected, file.write(text[0..])); + + const offset = test_fs.fat_config.clusterToSector(3) * test_fs.fat_config.bytes_per_sector; + expectEqualSlices(u8, test_file_buf[offset .. offset + 1024], &[_]u8{0x00} ** 1024); +} + +test "Fat32FS.init no error" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); + defer test_fs.destroy() catch unreachable; +} + +test "Fat32FS.init errors" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_file_buf = try std.testing.allocator.alloc(u8, (32 * 512 + 4) + 1); + defer std.testing.allocator.free(test_file_buf); + + const read = try test_fat32_image.reader().readAll(test_file_buf[0..]); + const stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + // BadMBRMagic + test_file_buf[510] = 0x00; + expectError(error.BadMBRMagic, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[510] = 0x55; + + test_file_buf[511] = 0x00; + expectError(error.BadMBRMagic, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[511] = 0xAA; + + // BadRootCluster + // Little endian, so just eed to set the upper bytes + test_file_buf[44] = 0; + expectError(error.BadRootCluster, initialiseFAT32(std.testing.allocator, stream)); + + test_file_buf[44] = 1; + expectError(error.BadRootCluster, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[44] = 2; + + // BadFATCount + test_file_buf[16] = 0; + expectError(error.BadFATCount, initialiseFAT32(std.testing.allocator, stream)); + + test_file_buf[16] = 1; + expectError(error.BadFATCount, initialiseFAT32(std.testing.allocator, stream)); + + test_file_buf[16] = 10; + expectError(error.BadFATCount, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[16] = 2; + + // NotMirror + test_file_buf[40] = 1; + expectError(error.NotMirror, initialiseFAT32(std.testing.allocator, stream)); + + test_file_buf[40] = 10; + expectError(error.NotMirror, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[40] = 0; + + // BadMedia + test_file_buf[21] = 0xF0; + expectError(error.BadMedia, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[21] = 0xF8; + + // BadFat32 + test_file_buf[17] = 10; + expectError(error.BadFat32, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[17] = 0; + + test_file_buf[19] = 10; + expectError(error.BadFat32, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[19] = 0; + + test_file_buf[22] = 10; + expectError(error.BadFat32, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[22] = 0; + + // BadSignature + test_file_buf[66] = 0x28; + expectError(error.BadSignature, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[66] = 0x29; + + // BadFSType + // Change from FAT32 to FAT16 + test_file_buf[85] = '1'; + test_file_buf[86] = '6'; + expectError(error.BadFSType, initialiseFAT32(std.testing.allocator, stream)); + test_file_buf[85] = '3'; + test_file_buf[86] = '2'; + + // Test the bad reads + // Boot sector + expectError(error.BadRead, initialiseFAT32(std.testing.allocator, &std.io.fixedBufferStream(test_file_buf[0..510]))); + // FSInfo (we have one) + expectError(error.BadRead, initialiseFAT32(std.testing.allocator, &std.io.fixedBufferStream(test_file_buf[0 .. 512 + 100]))); + // FAT + expectError(error.BadRead, initialiseFAT32(std.testing.allocator, &std.io.fixedBufferStream(test_file_buf[0 .. (32 * 512 + 4) + 1]))); +} + +test "Fat32FS.init free memory" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + const allocations: usize = 5; + var i: usize = 0; + while (i < allocations) : (i += 1) { + var fa = std.testing.FailingAllocator.init(std.testing.allocator, i); + expectError(error.OutOfMemory, initialiseFAT32(&fa.allocator, test_fat32_image)); + } +} + +test "Fat32FS.init FATConfig expected" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_fs = try initialiseFAT32(std.testing.allocator, test_fat32_image); + defer test_fs.destroy() catch unreachable; + + // This is the expected FAT config from the initialised FAT device + const expected = FATConfig{ + .bytes_per_sector = 512, + .sectors_per_cluster = 1, + .reserved_sectors = 32, + .hidden_sectors = 0, + .total_sectors = 66583, + .sectors_per_fat = 513, + .root_directory_cluster = 2, + .fsinfo_sector = 1, + .backup_boot_sector = 6, + .has_fs_info = true, + .number_free_clusters = 65471, + .next_free_cluster = 55, + .cluster_end_marker = 0x0FFFFFFF, + }; + + expectEqual(test_fs.fat_config, expected); +} + +test "Fat32FS.init FATConfig mix FSInfo" { + const test_fat32_image = try std.fs.cwd().openFile("test/fat32/test_fat32.img", .{}); + defer test_fat32_image.close(); + + var test_file_buf = try std.testing.allocator.alloc(u8, 1024 * 1024); + defer std.testing.allocator.free(test_file_buf); + + const read = try test_fat32_image.reader().readAll(test_file_buf[0..]); + const stream = &std.io.fixedBufferStream(test_file_buf[0..]); + + // No FSInfo + { + // Force no FSInfo + test_file_buf[48] = 0x00; + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + // This is the default config that should be produced from mkfat32.Fat32 + const expected = FATConfig{ + .bytes_per_sector = 512, + .sectors_per_cluster = 1, + .reserved_sectors = 32, + .hidden_sectors = 0, + .total_sectors = 66583, + .sectors_per_fat = 513, + .root_directory_cluster = 2, + .fsinfo_sector = 0, + .backup_boot_sector = 6, + .has_fs_info = false, + .number_free_clusters = 0xFFFFFFFF, + .next_free_cluster = 0xFFFFFFFF, + .cluster_end_marker = 0x0FFFFFFF, + }; + + expectEqual(test_fs.fat_config, expected); + test_file_buf[48] = 0x01; + } + + // Bad Signatures + { + // Corrupt a signature + test_file_buf[512] = 0xAA; + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + // This is the default config that should be produced from mkfat32.Fat32 + const expected = FATConfig{ + .bytes_per_sector = 512, + .sectors_per_cluster = 1, + .reserved_sectors = 32, + .hidden_sectors = 0, + .total_sectors = 66583, + .sectors_per_fat = 513, + .root_directory_cluster = 2, + .fsinfo_sector = 1, + .backup_boot_sector = 6, + .has_fs_info = false, + .number_free_clusters = 0xFFFFFFFF, + .next_free_cluster = 0xFFFFFFFF, + .cluster_end_marker = 0x0FFFFFFF, + }; + + expectEqual(test_fs.fat_config, expected); + test_file_buf[512] = 0x52; + } + + // Bad number_free_clusters + { + // Make is massive + test_file_buf[512 + 4 + 480 + 4] = 0xAA; + test_file_buf[512 + 4 + 480 + 5] = 0xBB; + test_file_buf[512 + 4 + 480 + 6] = 0xCC; + test_file_buf[512 + 4 + 480 + 7] = 0xDD; + + var test_fs = try initialiseFAT32(std.testing.allocator, stream); + defer test_fs.destroy() catch unreachable; + + // This is the default config that should be produced from mkfat32.Fat32 + const expected = FATConfig{ + .bytes_per_sector = 512, + .sectors_per_cluster = 1, + .reserved_sectors = 32, + .hidden_sectors = 0, + .total_sectors = 66583, + .sectors_per_fat = 513, + .root_directory_cluster = 2, + .fsinfo_sector = 1, + .backup_boot_sector = 6, + .has_fs_info = true, + .number_free_clusters = 0xFFFFFFFF, + .next_free_cluster = 55, + .cluster_end_marker = 0x0FFFFFFF, + }; + + expectEqual(test_fs.fat_config, expected); + } } diff --git a/src/kernel/filesystem/initrd.zig b/src/kernel/filesystem/initrd.zig index 5cf07e1..1c44a6c 100644 --- a/src/kernel/filesystem/initrd.zig +++ b/src/kernel/filesystem/initrd.zig @@ -371,7 +371,7 @@ test "open valid file" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); defer file1.close(); @@ -401,7 +401,7 @@ test "open fail with invalid flags" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); expectError(error.InvalidFlags, vfs.openFile("/text10.txt", .CREATE_DIR)); expectError(error.InvalidFlags, vfs.openFile("/text10.txt", .CREATE_FILE)); @@ -428,7 +428,7 @@ test "open fail with NoSuchFileOrDir" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); expectError(error.NoSuchFileOrDir, vfs.openFile("/text10.txt", .NO_CREATION)); expectError(error.NoSuchFileOrDir, vfs.openDir("/temp/", .NO_CREATION)); } @@ -443,7 +443,7 @@ test "open a file, out of memory" { var fs = try InitrdFS.init(&initrd_stream, &fa.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); expectError(error.OutOfMemory, vfs.openFile("/test1.txt", .NO_CREATION)); } @@ -456,7 +456,7 @@ test "open two of the same file" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); const file1 = try vfs.openFile("/test1.txt", .NO_CREATION); defer file1.close(); @@ -482,7 +482,7 @@ test "close a file" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); @@ -514,7 +514,7 @@ test "close a non-opened file" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); // Open a valid file var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); @@ -541,7 +541,7 @@ test "read a file" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); defer file1.close(); @@ -565,7 +565,7 @@ test "read a file, invalid/not opened/crafted *const Node" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); // Open a valid file var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); @@ -594,7 +594,7 @@ test "write does nothing" { var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); defer fs.deinit(); - vfs.setRoot(fs.root_node); + try vfs.setRoot(fs.root_node); // Open a valid file var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); @@ -663,7 +663,9 @@ fn rt_openReadClose(allocator: *Allocator) void { fn runtimeTests(rd_fs: *InitrdFS) void { // There will be test files provided for the runtime tests // Need to init the VFS. This will be overridden after the tests. - vfs.setRoot(rd_fs.root_node); + vfs.setRoot(rd_fs.root_node) catch |e| { + panic(@errorReturnTrace(), "Ramdisk root node isn't a directory node: {}\n", .{e}); + }; rt_openReadClose(rd_fs.allocator); if (rd_fs.opened_files.count() != 0) { panic(@errorReturnTrace(), "FAILURE: Didn't close all files\n", .{}); diff --git a/src/kernel/filesystem/vfs.zig b/src/kernel/filesystem/vfs.zig index d8e9458..23a333b 100644 --- a/src/kernel/filesystem/vfs.zig +++ b/src/kernel/filesystem/vfs.zig @@ -488,7 +488,7 @@ pub fn openDir(path: []const u8, flags: OpenFlags) (Allocator.Error || Error)!*D file.close(); break :blk Error.IsAFile; }, - // We instructed open to folow symlinks above, so this is impossible + // We instructed open to follow symlinks above, so this is impossible .Symlink => unreachable, .Dir => &node.Dir, }; @@ -499,7 +499,7 @@ pub fn openDir(path: []const u8, flags: OpenFlags) (Allocator.Error || Error)!*D /// /// Arguments: /// IN path: []const u8 - The path to open. Must be absolute (see isAbsolute) -/// IN tareget: ?[]const u8 - The target to use when creating the symlink. Can be null if .NO_CREATION is used as the open flag +/// IN target: ?[]const u8 - The target to use when creating the symlink. Can be null if .NO_CREATION is used as the open flag /// IN flags: OpenFlags - The flags specifying if this node should be created if it doesn't exist. Cannot be CREATE_FILE /// /// Return: []const u8 @@ -552,7 +552,13 @@ pub fn isAbsolute(path: []const u8) bool { /// Arguments: /// IN node: *Node - The node to initialise the root node. /// -pub fn setRoot(node: *Node) void { +/// Error: Error +/// Error.NotADirectory - The node isn't a directory node. +/// +pub fn setRoot(node: *Node) Error!void { + if (!node.isDir()) { + return Error.NotADirectory; + } root = node; } @@ -972,8 +978,8 @@ test "read" { testing.expectEqual(test_link, "/foo.txt"); var link_file = try openFile("/link", .NO_CREATION); { - const length = try link_file.read(buffer[0..0]); - testing.expect(std.mem.eql(u8, str[0..0], buffer[0..length])); + const length = try link_file.read(buffer[0..]); + testing.expect(std.mem.eql(u8, str[0..], buffer[0..length])); } } diff --git a/src/kernel/kmain.zig b/src/kernel/kmain.zig index ebbfe36..95b5766 100644 --- a/src/kernel/kmain.zig +++ b/src/kernel/kmain.zig @@ -129,7 +129,9 @@ export fn kmain(boot_payload: arch.BootPayload) void { }; // Need to init the vfs after the ramdisk as we need the root node from the ramdisk filesystem - vfs.setRoot(ramdisk_filesystem.root_node); + vfs.setRoot(ramdisk_filesystem.root_node) catch |e| { + panic_root.panic(@errorReturnTrace(), "Ramdisk root node isn't a directory node: {}\n", .{e}); + }; } scheduler.init(&kernel_heap.allocator, &mem_profile) catch |e| { diff --git a/test/fat32/test_files/file~a.txt b/test/fat32/test_files/file~a.txt new file mode 100644 index 0000000..1a4eac5 --- /dev/null +++ b/test/fat32/test_files/file~a.txt @@ -0,0 +1 @@ +file~a.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/file1.txt b/test/fat32/test_files/folder1/file1.txt new file mode 100644 index 0000000..39cd576 --- /dev/null +++ b/test/fat32/test_files/folder1/file1.txt @@ -0,0 +1 @@ +file1.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/folder2/file2.txt b/test/fat32/test_files/folder1/folder2/file2.txt new file mode 100644 index 0000000..c3ee11c --- /dev/null +++ b/test/fat32/test_files/folder1/folder2/file2.txt @@ -0,0 +1 @@ +file2.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/folder2/folder3/file3.txt b/test/fat32/test_files/folder1/folder2/folder3/file3.txt new file mode 100644 index 0000000..49081a8 --- /dev/null +++ b/test/fat32/test_files/folder1/folder2/folder3/file3.txt @@ -0,0 +1 @@ +file3.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/folder2/folder3/folder4/file4.txt b/test/fat32/test_files/folder1/folder2/folder3/folder4/file4.txt new file mode 100644 index 0000000..7fd9270 --- /dev/null +++ b/test/fat32/test_files/folder1/folder2/folder3/folder4/file4.txt @@ -0,0 +1 @@ +file4.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/file5.txt b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/file5.txt new file mode 100644 index 0000000..f724f5f --- /dev/null +++ b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/file5.txt @@ -0,0 +1 @@ +file5.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/file6.txt b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/file6.txt new file mode 100644 index 0000000..afde385 --- /dev/null +++ b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/file6.txt @@ -0,0 +1 @@ +file6.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/file7.txt b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/file7.txt new file mode 100644 index 0000000..1d40128 --- /dev/null +++ b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/file7.txt @@ -0,0 +1 @@ +file7.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8/file8.txt b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8/file8.txt new file mode 100644 index 0000000..215ed79 --- /dev/null +++ b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8/file8.txt @@ -0,0 +1 @@ +file8.txt \ No newline at end of file diff --git a/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8/folder9/file9.txt b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8/folder9/file9.txt new file mode 100644 index 0000000..258f30f --- /dev/null +++ b/test/fat32/test_files/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8/folder9/file9.txt @@ -0,0 +1 @@ +file9.txt \ No newline at end of file diff --git a/test/mock/kernel/arch_mock.zig b/test/mock/kernel/arch_mock.zig index e711794..b228b91 100644 --- a/test/mock/kernel/arch_mock.zig +++ b/test/mock/kernel/arch_mock.zig @@ -10,10 +10,19 @@ const paging = @import("paging_mock.zig"); const Serial = @import("../../../src/kernel/serial.zig").Serial; const TTY = @import("../../../src/kernel/tty.zig").TTY; const Keyboard = @import("../../../src/kernel/keyboard.zig").Keyboard; - -pub const task = @import("../../../src/kernel/task.zig"); +const task = @import("../../../src/kernel/task.zig"); pub const Device = pci.PciDeviceInfo; +pub const DateTime = struct { + second: u32, + minute: u32, + hour: u32, + day: u32, + month: u32, + year: u32, + century: u32, + day_of_week: u32, +}; const mock_framework = @import("mock_framework.zig"); pub const initTest = mock_framework.initTest; @@ -151,10 +160,25 @@ pub fn getDevices(allocator: *Allocator) Allocator.Error![]Device { return &[_]Device{}; } +pub fn getDateTime() DateTime { + // TODO: Use the std lib std.time.timestamp() and convert + // Hard code 12:12:13 12/12/12 for testing + return .{ + .second = 13, + .minute = 12, + .hour = 12, + .day = 12, + .month = 12, + .year = 2012, + .century = 2000, + .day_of_week = 4, + }; +} + pub fn init(mem_profile: *const MemProfile) void { // I'll get back to this as this doesn't effect the current testing. // When I come on to the mem.zig testing, I'll fix :) - //return mock_framework.performAction("init", void, mem_profile, allocator); + //return mock_framework.performAction("init", void, mem_profile); } // User defined mocked functions