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
This commit is contained in:
DrDeano 2021-01-04 08:50:20 +00:00
parent 5fbb8871a4
commit 4afecd6508
No known key found for this signature in database
GPG key ID: 96188600582B9ED7
19 changed files with 3192 additions and 959 deletions

1
.gitignore vendored
View file

@ -13,3 +13,4 @@
# Custom ignore
**/mock_framework.zig
**/*.ramdisk
**/*.img

View file

@ -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;
}

View file

@ -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
///

View file

@ -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);
}

File diff suppressed because it is too large Load diff

View file

@ -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", .{});

View file

@ -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]));
}
}

View file

@ -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| {

View file

@ -0,0 +1 @@
file~a.txt

View file

@ -0,0 +1 @@
file1.txt

View file

@ -0,0 +1 @@
file2.txt

View file

@ -0,0 +1 @@
file3.txt

View file

@ -0,0 +1 @@
file4.txt

View file

@ -0,0 +1 @@
file5.txt

View file

@ -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