Merge pull request #285 from ZystemOS/feature/fat32-write

Feature/fat32 write
This commit is contained in:
Edward Dean 2021-01-04 17:19:36 +00:00 committed by GitHub
commit ddbbff8651
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 3192 additions and 959 deletions

1
.gitignore vendored
View file

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

View file

@ -523,8 +523,8 @@ pub const Fat32 = struct {
} }
// Valid clusters are 1, 2, 4, 8, 16, 32, 64 and 128 // 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)) { 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 32 and a power of 2. Found: {}", .{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; return Error.InvalidOptionValue;
} }

View file

@ -30,6 +30,9 @@ const MemProfile = mem.MemProfile;
/// The type of a device. /// The type of a device.
pub const Device = pci.PciDeviceInfo; 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. /// The virtual end of the kernel code.
extern var KERNEL_VADDR_END: *u32; 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]); 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 { pub fn getDevices(allocator: *Allocator) Allocator.Error![]Device {
return pci.getDevices(allocator); 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 /// 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. /// could report that the CMOS chip is faulty or the battery is dyeing.
const CENTURY_REGISTER: bool = false; 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. /// A structure to hold all the date and time information in the RTC.
const DateTime = struct { pub const DateTime = struct {
second: u32, second: u32,
minute: u32, minute: u32,
hour: u32, hour: u32,
@ -36,12 +42,6 @@ const DateTime = struct {
day_of_week: u32, 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. /// The number of ticks that has passed when RTC was initially set up.
var ticks: u32 = 0; var ticks: u32 = 0;
@ -145,66 +145,6 @@ fn readRtcRegisters() DateTime {
return date_time; 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. /// The interrupt handler for the RTC.
/// ///
@ -262,6 +202,66 @@ fn enableInterrupts() void {
cmos.writeStatusRegister(cmos.StatusRegister.B, status_b | 0x40, true); 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. /// Initialise the RTC.
/// ///
@ -558,7 +558,7 @@ test "readRtc unstable read" {
.century = 2000, .century = 2000,
.day_of_week = 5, .day_of_week = 5,
}; };
const actual = readRtc(); const actual = getDateTime();
expectEqual(expected, actual); expectEqual(expected, actual);
} }
@ -611,7 +611,7 @@ test "readRtc is BCD" {
.century = 2000, .century = 2000,
.day_of_week = 5, .day_of_week = 5,
}; };
const actual = readRtc(); const actual = getDateTime();
expectEqual(expected, actual); expectEqual(expected, actual);
} }
@ -664,7 +664,7 @@ test "readRtc is 12 hours" {
.century = 2000, .century = 2000,
.day_of_week = 5, .day_of_week = 5,
}; };
const actual = readRtc(); const actual = getDateTime();
expectEqual(expected, actual); 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); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); try vfs.setRoot(fs.root_node);
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close(); defer file1.close();
@ -401,7 +401,7 @@ test "open fail with invalid flags" {
var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); 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_DIR));
expectError(error.InvalidFlags, vfs.openFile("/text10.txt", .CREATE_FILE)); 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); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); 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.openFile("/text10.txt", .NO_CREATION));
expectError(error.NoSuchFileOrDir, vfs.openDir("/temp/", .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); var fs = try InitrdFS.init(&initrd_stream, &fa.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); try vfs.setRoot(fs.root_node);
expectError(error.OutOfMemory, vfs.openFile("/test1.txt", .NO_CREATION)); 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); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); try vfs.setRoot(fs.root_node);
const file1 = try vfs.openFile("/test1.txt", .NO_CREATION); const file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close(); defer file1.close();
@ -482,7 +482,7 @@ test "close a file" {
var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); try vfs.setRoot(fs.root_node);
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); 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); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); try vfs.setRoot(fs.root_node);
// Open a valid file // Open a valid file
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); 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); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); try vfs.setRoot(fs.root_node);
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close(); 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); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); try vfs.setRoot(fs.root_node);
// Open a valid file // Open a valid file
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); 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); var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); try vfs.setRoot(fs.root_node);
// Open a valid file // Open a valid file
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION); 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 { fn runtimeTests(rd_fs: *InitrdFS) void {
// There will be test files provided for the runtime tests // There will be test files provided for the runtime tests
// Need to init the VFS. This will be overridden after the 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); rt_openReadClose(rd_fs.allocator);
if (rd_fs.opened_files.count() != 0) { if (rd_fs.opened_files.count() != 0) {
panic(@errorReturnTrace(), "FAILURE: Didn't close all files\n", .{}); 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(); file.close();
break :blk Error.IsAFile; 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, .Symlink => unreachable,
.Dir => &node.Dir, .Dir => &node.Dir,
}; };
@ -499,7 +499,7 @@ pub fn openDir(path: []const u8, flags: OpenFlags) (Allocator.Error || Error)!*D
/// ///
/// Arguments: /// Arguments:
/// IN path: []const u8 - The path to open. Must be absolute (see isAbsolute) /// 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 /// IN flags: OpenFlags - The flags specifying if this node should be created if it doesn't exist. Cannot be CREATE_FILE
/// ///
/// Return: []const u8 /// Return: []const u8
@ -552,7 +552,13 @@ pub fn isAbsolute(path: []const u8) bool {
/// Arguments: /// Arguments:
/// IN node: *Node - The node to initialise the root node. /// 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; root = node;
} }
@ -972,8 +978,8 @@ test "read" {
testing.expectEqual(test_link, "/foo.txt"); testing.expectEqual(test_link, "/foo.txt");
var link_file = try openFile("/link", .NO_CREATION); var link_file = try openFile("/link", .NO_CREATION);
{ {
const length = try link_file.read(buffer[0..0]); const length = try link_file.read(buffer[0..]);
testing.expect(std.mem.eql(u8, str[0..0], buffer[0..length])); 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 // 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| { 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 Serial = @import("../../../src/kernel/serial.zig").Serial;
const TTY = @import("../../../src/kernel/tty.zig").TTY; const TTY = @import("../../../src/kernel/tty.zig").TTY;
const Keyboard = @import("../../../src/kernel/keyboard.zig").Keyboard; const Keyboard = @import("../../../src/kernel/keyboard.zig").Keyboard;
const task = @import("../../../src/kernel/task.zig");
pub const task = @import("../../../src/kernel/task.zig");
pub const Device = pci.PciDeviceInfo; 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"); const mock_framework = @import("mock_framework.zig");
pub const initTest = mock_framework.initTest; pub const initTest = mock_framework.initTest;
@ -151,10 +160,25 @@ pub fn getDevices(allocator: *Allocator) Allocator.Error![]Device {
return &[_]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 { pub fn init(mem_profile: *const MemProfile) void {
// I'll get back to this as this doesn't effect the current testing. // 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 :) // 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 // User defined mocked functions