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:
parent
5fbb8871a4
commit
4afecd6508
19 changed files with 3192 additions and 959 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@
|
|||
# Custom ignore
|
||||
**/mock_framework.zig
|
||||
**/*.ramdisk
|
||||
**/*.img
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
///
|
||||
|
|
|
@ -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
|
@ -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", .{});
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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| {
|
||||
|
|
1
test/fat32/test_files/file~a.txt
Normal file
1
test/fat32/test_files/file~a.txt
Normal file
|
@ -0,0 +1 @@
|
|||
file~a.txt
|
1
test/fat32/test_files/folder1/file1.txt
Normal file
1
test/fat32/test_files/folder1/file1.txt
Normal file
|
@ -0,0 +1 @@
|
|||
file1.txt
|
1
test/fat32/test_files/folder1/folder2/file2.txt
Normal file
1
test/fat32/test_files/folder1/folder2/file2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
file2.txt
|
1
test/fat32/test_files/folder1/folder2/folder3/file3.txt
Normal file
1
test/fat32/test_files/folder1/folder2/folder3/file3.txt
Normal file
|
@ -0,0 +1 @@
|
|||
file3.txt
|
|
@ -0,0 +1 @@
|
|||
file4.txt
|
|
@ -0,0 +1 @@
|
|||
file5.txt
|
|
@ -0,0 +1 @@
|
|||
file6.txt
|
|
@ -0,0 +1 @@
|
|||
file7.txt
|
|
@ -0,0 +1 @@
|
|||
file8.txt
|
|
@ -0,0 +1 @@
|
|||
file9.txt
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue