Merge pull request #219 from ZystemOS/feature/initrd-use-FixedBufferStream

Feature/initrd use fixed buffer stream
This commit is contained in:
Edward Dean 2020-08-06 18:17:33 +01:00 committed by GitHub
commit b8a47d6e08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 189 additions and 160 deletions

View file

@ -83,8 +83,8 @@ pub fn build(b: *Builder) !void {
b.default_step.dependOn(&make_iso.step); b.default_step.dependOn(&make_iso.step);
const test_step = b.step("test", "Run tests"); const test_step = b.step("test", "Run tests");
const mock_path = "\"../../test/mock/kernel/\""; const mock_path = "../../test/mock/kernel/";
const arch_mock_path = "\"../../../../test/mock/kernel/\""; const arch_mock_path = "../../../../test/mock/kernel/";
const unit_tests = b.addTest(main_src); const unit_tests = b.addTest(main_src);
unit_tests.setBuildMode(build_mode); unit_tests.setBuildMode(build_mode);
unit_tests.setMainPkgPath("."); unit_tests.setMainPkgPath(".");

View file

@ -10,21 +10,20 @@ const mock_path = build_options.mock_path;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const AutoHashMap = std.AutoHashMap; const AutoHashMap = std.AutoHashMap;
const vfs = @import("vfs.zig"); const vfs = @import("vfs.zig");
const mem = if (is_test) @import(mock_path ++ "mem_mock.zig") else @import("mem.zig"); const mem = if (is_test) @import("../" ++ mock_path ++ "mem_mock.zig") else @import("../mem.zig");
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("panic.zig").panic; const panic = if (is_test) @import("../" ++ mock_path ++ "panic_mock.zig").panic else @import("../panic.zig").panic;
/// The Initrd file system struct. /// The Initrd file system struct.
/// Format of raw ramdisk: /// Format of raw ramdisk:
/// (NumOfFiles:usize)[(name_length:usize)(name:u8[name_length])(content_length:usize)(content:u8[content_length])]* /// (NumOfFiles:usize)[(name_length:usize)(name:u8[name_length])(content_length:usize)(content:u8[content_length])]*
pub const InitrdFS = struct { pub const InitrdFS = struct {
/// The ramdisk header that stores pointers to the raw ramdisk for the name and file content. /// The ramdisk header that stores pointers for the name and file content.
/// As the ramdisk is read only, these can be const pointers.
const InitrdHeader = struct { const InitrdHeader = struct {
/// The name of the file /// The name of the file
name: []const u8, name: []u8,
/// The content of the file /// The content of the file
content: []const u8, content: []u8,
}; };
/// The error set for the ramdisk file system. /// The error set for the ramdisk file system.
@ -121,71 +120,80 @@ pub const InitrdFS = struct {
self.allocator.destroy(self.root_node); self.allocator.destroy(self.root_node);
self.allocator.destroy(self.fs); self.allocator.destroy(self.fs);
self.opened_files.deinit(); self.opened_files.deinit();
for (self.files) |entry| {
self.allocator.free(entry.name);
self.allocator.free(entry.content);
}
self.allocator.free(self.files); self.allocator.free(self.files);
self.allocator.destroy(self); self.allocator.destroy(self);
} }
/// ///
/// Initialise a ramdisk file system from a raw ramdisk in memory provided by the bootloader. /// Initialise a ramdisk file system from a raw ramdisk in memory provided by the bootloader in a stream.
/// Any memory allocated will be freed. /// Any memory allocated will be freed.
/// ///
/// Arguments: /// Arguments:
/// IN rd_module: mem.Module - The bootloader module that contains the raw ramdisk. /// IN stream: *std.io.FixedBufferStream([]u8) - The stream that contains the raw ramdisk data.
/// IN allocator: *Allocator - The allocator used for initialising any memory needed. /// IN allocator: *Allocator - The allocator used for initialising any memory needed.
/// ///
/// Return: *InitrdFS /// Return: *InitrdFS
/// A pointer to the ram disk file system. /// A pointer to the ram disk file system.
/// ///
/// Error: Allocator.Error || Error /// Error: Error || error{EndOfStream} || Allocator.Error || std.io.FixedBufferStream([]u8).ReadError
/// error.OutOfMemory - If there isn't enough memory for initialisation. Any memory
/// allocated will be freed.
/// error.InvalidRamDisk - If the provided raw ramdisk is invalid. This can be due to a /// error.InvalidRamDisk - If the provided raw ramdisk is invalid. This can be due to a
/// mis-match of the number of files to the length of the raw /// mis-match of the number of files to the length of the raw
/// ramdisk or the wrong length provided to cause undefined parsed /// ramdisk or the wrong length provided to cause undefined parsed
/// lengths for other parts of the ramdisk. /// lengths for other parts of the ramdisk.
/// error.EndOfStream - When reading from the stream, we reach the end of the stream
/// before completing the read.
/// error.OutOfMemory - If there isn't enough memory for initialisation. Any memory
/// allocated will be freed.
/// ///
pub fn init(rd_module: mem.Module, allocator: *Allocator) (Allocator.Error || Error)!*InitrdFS { pub fn init(stream: *std.io.FixedBufferStream([]u8), allocator: *Allocator) (Error || error{EndOfStream} || Allocator.Error)!*InitrdFS {
std.log.info(.initrd, "Init\n", .{}); std.log.info(.initrd, "Init\n", .{});
defer std.log.info(.initrd, "Done\n", .{}); defer std.log.info(.initrd, "Done\n", .{});
const rd_len: usize = rd_module.region.end - rd_module.region.start;
const ramdisk_bytes = @intToPtr([*]u8, rd_module.region.start)[0..rd_len];
// First @sizeOf(usize) bytes is the number of files // First @sizeOf(usize) bytes is the number of files
const num_of_files = std.mem.readIntSlice(usize, ramdisk_bytes[0..], builtin.endian); const num_of_files = try stream.reader().readIntNative(usize);
var headers = try allocator.alloc(InitrdHeader, num_of_files); var headers = try allocator.alloc(InitrdHeader, num_of_files);
errdefer allocator.free(headers); errdefer allocator.free(headers);
// Populate the headers // Populate the headers
var i: usize = 0; var i: usize = 0;
// Keep track of the offset into the ramdisk memory
var current_offset: usize = @sizeOf(usize); // If we error, then free any headers that we allocated.
errdefer {
var j: usize = 0;
while (j < i) : (j += 1) {
allocator.free(headers[j].name);
allocator.free(headers[j].content);
}
}
while (i < num_of_files) : (i += 1) { while (i < num_of_files) : (i += 1) {
// We don't need to store the name length any more as we have the name.len // We don't need to store the lengths any more as we have the slice.len
const name_len = std.mem.readIntSlice(usize, ramdisk_bytes[current_offset..], builtin.endian); const name_len = try stream.reader().readIntNative(usize);
current_offset += @sizeOf(usize); if (name_len == 0) {
if (current_offset >= rd_len) {
return Error.InvalidRamDisk; return Error.InvalidRamDisk;
} }
headers[i].name = ramdisk_bytes[current_offset .. current_offset + name_len]; headers[i].name = try allocator.alloc(u8, name_len);
current_offset += name_len; errdefer allocator.free(headers[i].name);
if (current_offset >= rd_len) { if ((try stream.reader().readAll(headers[i].name)) != name_len) {
return Error.InvalidRamDisk; return Error.InvalidRamDisk;
} }
const content_len = std.mem.readIntSlice(usize, ramdisk_bytes[current_offset..], builtin.endian); const content_len = try stream.reader().readIntNative(usize);
current_offset += @sizeOf(usize); if (content_len == 0) {
if (current_offset >= rd_len) {
return Error.InvalidRamDisk; return Error.InvalidRamDisk;
} }
headers[i].content = ramdisk_bytes[current_offset .. current_offset + content_len]; headers[i].content = try allocator.alloc(u8, content_len);
current_offset += content_len; errdefer allocator.free(headers[i].content);
// We could be at the end of the ramdisk, so only need to check for grater than. if ((try stream.reader().readAll(headers[i].content)) != content_len) {
if (current_offset > rd_len) {
return Error.InvalidRamDisk; return Error.InvalidRamDisk;
} }
} }
// Make sure we are at the end // If we aren't at the end, error.
if (current_offset != rd_len) { if ((try stream.getPos()) != (try stream.getEndPos())) {
return Error.InvalidRamDisk; return Error.InvalidRamDisk;
} }
@ -229,48 +237,47 @@ pub const InitrdFS = struct {
/// Error: Allocator.Error /// Error: Allocator.Error
/// error.OutOfMemory - If there isn't enough memory for the in memory ramdisk. /// error.OutOfMemory - If there isn't enough memory for the in memory ramdisk.
/// ///
fn createInitrd(allocator: *Allocator) Allocator.Error![]u8 { fn createInitrd(allocator: *Allocator) (Allocator.Error || std.io.FixedBufferStream([]u8).WriteError)![]u8 {
// Create 3 valid ramdisk files in memory // Create 3 valid ramdisk files in memory
const file_names = [_][]const u8{ "test1.txt", "test2.txt", "test3.txt" }; const file_names = [_][]const u8{ "test1.txt", "test2.txt", "test3.txt" };
const file_contents = [_][]const u8{ "This is a test", "This is a test: part 2", "This is a test: the prequel" }; const file_contents = [_][]const u8{ "This is a test", "This is a test: part 2", "This is a test: the prequel" };
// Ensure these two arrays are the same length
std.debug.assert(file_names.len == file_contents.len);
var sum: usize = 0; var sum: usize = 0;
const files_length = for ([_]usize{ 0, 1, 2 }) |i| { const files_length = for ([_]usize{ 0, 1, 2 }) |i| {
sum += @sizeOf(usize) + file_names[i].len + @sizeOf(usize) + file_contents[i].len; sum += @sizeOf(usize) + file_names[i].len + @sizeOf(usize) + file_contents[i].len;
} else sum; } else sum;
const total_ramdisk_len = @sizeOf(usize) + files_length; const total_ramdisk_len = @sizeOf(usize) + files_length;
var ramdisk_mem = try allocator.alloc(u8, total_ramdisk_len); var ramdisk_bytes = try allocator.alloc(u8, total_ramdisk_len);
var ramdisk_stream = std.io.fixedBufferStream(ramdisk_bytes);
// Copy the data into the allocated memory // Copy the data into the allocated memory
std.mem.writeIntSlice(usize, ramdisk_mem[0..], 3, builtin.endian); try ramdisk_stream.writer().writeIntNative(usize, file_names.len);
var current_offset: usize = @sizeOf(usize);
inline for ([_]usize{ 0, 1, 2 }) |i| { inline for ([_]usize{ 0, 1, 2 }) |i| {
// Name len // Name len
std.mem.writeIntSlice(usize, ramdisk_mem[current_offset..], file_names[i].len, builtin.endian); try ramdisk_stream.writer().writeIntNative(usize, file_names[i].len);
current_offset += @sizeOf(usize);
// Name // Name
std.mem.copy(u8, ramdisk_mem[current_offset..], file_names[i]); try ramdisk_stream.writer().writeAll(file_names[i]);
current_offset += file_names[i].len;
// File len // File len
std.mem.writeIntSlice(usize, ramdisk_mem[current_offset..], file_contents[i].len, builtin.endian); try ramdisk_stream.writer().writeIntNative(usize, file_contents[i].len);
current_offset += @sizeOf(usize);
// File content // File content
std.mem.copy(u8, ramdisk_mem[current_offset..], file_contents[i]); try ramdisk_stream.writer().writeAll(file_contents[i]);
current_offset += file_contents[i].len;
} }
// Make sure we are full // Make sure we are full
expectEqual(current_offset, total_ramdisk_len); expectEqual(try ramdisk_stream.getPos(), total_ramdisk_len);
return ramdisk_mem; expectEqual(try ramdisk_stream.getPos(), try ramdisk_stream.getEndPos());
return ramdisk_bytes;
} }
test "init with files valid" { test "init with files valid" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
expectEqual(fs.files.len, 3); expectEqual(fs.files.len, 3);
@ -280,18 +287,17 @@ test "init with files valid" {
} }
test "init with files invalid - invalid number of files" { test "init with files invalid - invalid number of files" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
// Override the number of files // Override the number of files
std.mem.writeIntSlice(usize, ramdisk_mem[0..], 10, builtin.endian); std.mem.writeIntSlice(usize, ramdisk_bytes[0..], 10, builtin.endian);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; expectError(error.InvalidRamDisk, InitrdFS.init(&initrd_stream, std.testing.allocator));
expectError(error.InvalidRamDisk, InitrdFS.init(ramdisk_module, std.testing.allocator));
// Override the number of files // Override the number of files
std.mem.writeIntSlice(usize, ramdisk_mem[0..], 0, builtin.endian); std.mem.writeIntSlice(usize, ramdisk_bytes[0..], 0, builtin.endian);
expectError(error.InvalidRamDisk, InitrdFS.init(ramdisk_module, std.testing.allocator)); expectError(error.InvalidRamDisk, InitrdFS.init(&initrd_stream, std.testing.allocator));
} }
test "init with files invalid - mix - bad" { test "init with files invalid - mix - bad" {
@ -300,42 +306,42 @@ test "init with files invalid - mix - bad" {
// Challenge, make this a effective security vulnerability // Challenge, make this a effective security vulnerability
// P.S. I don't know if adding magics will stop this // P.S. I don't know if adding magics will stop this
{ {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
// Override the first file name length, make is shorter // Override the first file name length, make is shorter
std.mem.writeIntSlice(usize, ramdisk_mem[4..], 2, builtin.endian); std.mem.writeIntSlice(usize, ramdisk_bytes[4..], 2, builtin.endian);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; expectError(error.InvalidRamDisk, InitrdFS.init(&initrd_stream, std.testing.allocator));
expectError(error.InvalidRamDisk, InitrdFS.init(ramdisk_module, std.testing.allocator));
} }
{ {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
// Override the first file name length, make is 4 shorter // Override the first file name length, make is 4 shorter
std.mem.writeIntSlice(usize, ramdisk_mem[4..], 5, builtin.endian); std.mem.writeIntSlice(usize, ramdisk_bytes[4..], 5, builtin.endian);
// Override the second file name length, make is 4 longer // Override the second file name length, make is 4 longer
std.mem.writeIntSlice(usize, ramdisk_mem[35..], 13, builtin.endian); std.mem.writeIntSlice(usize, ramdisk_bytes[35..], 13, builtin.endian);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; expectError(error.InvalidRamDisk, InitrdFS.init(&initrd_stream, std.testing.allocator));
expectError(error.InvalidRamDisk, InitrdFS.init(ramdisk_module, std.testing.allocator));
} }
} }
/// The number of allocations that the init function make.
const init_allocations: usize = 10;
test "init with files cleans memory if OutOfMemory" { test "init with files cleans memory if OutOfMemory" {
// There are 4 allocations var i: usize = 0;
for ([_]usize{ 0, 1, 2, 3 }) |i| { while (i < init_allocations) : (i += 1) {
{ {
var fa = std.testing.FailingAllocator.init(std.testing.allocator, i); var fa = std.testing.FailingAllocator.init(std.testing.allocator, i);
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; expectError(error.OutOfMemory, InitrdFS.init(&initrd_stream, &fa.allocator));
expectError(error.OutOfMemory, InitrdFS.init(ramdisk_module, &fa.allocator));
} }
// Ensure we have freed any memory allocated // Ensure we have freed any memory allocated
@ -344,24 +350,22 @@ test "init with files cleans memory if OutOfMemory" {
} }
test "getRootNode" { test "getRootNode" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
expectEqual(fs.fs.getRootNode(fs.fs), &fs.root_node.Dir); expectEqual(fs.fs.getRootNode(fs.fs), &fs.root_node.Dir);
} }
test "open valid file" { test "open valid file" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -389,12 +393,11 @@ test "open valid file" {
} }
test "open fail with invalid flags" { test "open fail with invalid flags" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -415,14 +418,13 @@ test "open fail with NoSuchFileOrDir" {
} }
test "open a file, out of memory" { test "open a file, out of memory" {
var fa = std.testing.FailingAllocator.init(std.testing.allocator, 4); var fa = std.testing.FailingAllocator.init(std.testing.allocator, init_allocations);
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, &fa.allocator);
var fs = try InitrdFS.init(ramdisk_module, &fa.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -431,12 +433,11 @@ test "open a file, out of memory" {
} }
test "open two of the same file" { test "open two of the same file" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -458,12 +459,11 @@ test "open two of the same file" {
} }
test "close a file" { test "close a file" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -491,12 +491,11 @@ test "close a file" {
} }
test "close a non-opened file" { test "close a non-opened file" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -519,12 +518,11 @@ test "close a non-opened file" {
} }
test "read a file" { test "read a file" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -544,14 +542,13 @@ test "read a file" {
} }
test "read a file, out of memory" { test "read a file, out of memory" {
var fa = std.testing.FailingAllocator.init(std.testing.allocator, 6); var fa = std.testing.FailingAllocator.init(std.testing.allocator, init_allocations + 2);
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, &fa.allocator);
var fs = try InitrdFS.init(ramdisk_module, &fa.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -563,12 +560,11 @@ test "read a file, out of memory" {
} }
test "read a file, invalid/not opened/crafted *const Node" { test "read a file, invalid/not opened/crafted *const Node" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);
@ -592,12 +588,11 @@ test "read a file, invalid/not opened/crafted *const Node" {
} }
test "write does nothing" { test "write does nothing" {
var ramdisk_mem = try createInitrd(std.testing.allocator); var ramdisk_bytes = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem); defer std.testing.allocator.free(ramdisk_bytes);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len }; var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" }; var fs = try InitrdFS.init(&initrd_stream, std.testing.allocator);
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit(); defer fs.deinit();
vfs.setRoot(fs.root_node); vfs.setRoot(fs.root_node);

View file

@ -200,7 +200,7 @@ const FreeListAllocator = struct {
if (new_size == old_mem.len) return new_size; if (new_size == old_mem.len) return new_size;
const end = @ptrToInt(old_mem.ptr) + old_mem.len; const end = @ptrToInt(old_mem.ptr) + old_mem.len;
const real_size = if (size_alignment > 1) std.mem.alignAllocLen(old_mem.len, new_size, size_alignment) else new_size; var real_size = if (size_alignment > 1) std.mem.alignAllocLen(old_mem.len, new_size, size_alignment) else new_size;
// Try to find the buffer's neighbour (if it's free) and the previous free node // Try to find the buffer's neighbour (if it's free) and the previous free node
// We'll be stealing some of the free neighbour's space when expanding or joining up with it when shrinking // We'll be stealing some of the free neighbour's space when expanding or joining up with it when shrinking
@ -244,10 +244,16 @@ const FreeListAllocator = struct {
return Allocator.Error.OutOfMemory; return Allocator.Error.OutOfMemory;
} else { } else {
// Shrinking // Shrinking
const size_diff = old_mem.len - real_size; var size_diff = old_mem.len - real_size;
// If shrinking would leave less space than required for a new header, // If shrinking would leave less space than required for a new header,
// or if shrinking would make the buffer too small, don't shrink // or if shrinking would make the buffer too small, don't shrink
if (size_diff < @sizeOf(Header) or real_size < @sizeOf(Header)) return Allocator.Error.OutOfMemory; if (size_diff < @sizeOf(Header)) {
return old_mem.len;
}
// Make sure the we have enough space for a header
if (real_size < @sizeOf(Header)) {
real_size = @sizeOf(Header);
}
// Create a new header for the space gained from shrinking // Create a new header for the space gained from shrinking
var new_next = insertFreeHeader(@ptrToInt(old_mem.ptr) + real_size, size_diff - @sizeOf(Header), if (prev) |p| p.next_free else self.first_free); var new_next = insertFreeHeader(@ptrToInt(old_mem.ptr) + real_size, size_diff - @sizeOf(Header), if (prev) |p| p.next_free else self.first_free);
@ -330,10 +336,12 @@ const FreeListAllocator = struct {
const addr = @ptrToInt(h); const addr = @ptrToInt(h);
var alignment_padding: usize = 0; var alignment_padding: usize = 0;
if (alignment > 1 and !std.mem.isAligned(addr, alignment)) { if ((alignment > 1 and !std.mem.isAligned(addr, alignment)) or !std.mem.isAligned(addr, @alignOf(Header))) {
alignment_padding = alignment - (addr % alignment); alignment_padding = alignment - (addr % alignment);
// If the size can't fit the alignment padding then try the next one // If the size can't fit the alignment padding then try the next one
if (h.size + @sizeOf(Header) < real_size + alignment_padding) continue; if (h.size + @sizeOf(Header) < real_size + alignment_padding) {
continue;
}
// If a new node couldn't be created from the space left by alignment padding then try the next one // If a new node couldn't be created from the space left by alignment padding then try the next one
// This check is necessary as otherwise we'd have wasted space that could never be allocated // This check is necessary as otherwise we'd have wasted space that could never be allocated
// We do however set the backup variable to this node so that in the unfortunate case that no other nodes can take the allocation, we allocate it here and sacrifice the wasted space // We do however set the backup variable to this node so that in the unfortunate case that no other nodes can take the allocation, we allocate it here and sacrifice the wasted space
@ -356,8 +364,9 @@ const FreeListAllocator = struct {
break :find h; break :find h;
} else backup; } else backup;
if (alloc_to == backup) if (alloc_to == backup) {
prev = backup_prev; prev = backup_prev;
}
if (alloc_to) |x| { if (alloc_to) |x| {
var header = x; var header = x;
@ -368,6 +377,14 @@ const FreeListAllocator = struct {
alignment_padding = alignment - (addr % alignment); alignment_padding = alignment - (addr % alignment);
} }
// If there is enough unused space to the right of this node, need to align that pointer to the alignment of the header
if (header.size > real_size + alignment_padding) {
const at = @ptrToInt(header) + real_size + alignment_padding;
if (!std.mem.isAligned(at, @alignOf(Header))) {
alignment_padding += @alignOf(Header) - (at % @alignOf(Header));
}
}
// If we were going to use alignment padding and it's big enough to fit a new node, create a node to the left using the unused space // If we were going to use alignment padding and it's big enough to fit a new node, create a node to the left using the unused space
if (alignment_padding >= @sizeOf(Header)) { if (alignment_padding >= @sizeOf(Header)) {
// Since the header's address is going to be reused for the smaller one being created, backup the header to its new position // Since the header's address is going to be reused for the smaller one being created, backup the header to its new position
@ -381,12 +398,12 @@ const FreeListAllocator = struct {
} }
// If there is enough unused space to the right of this node then create a smaller node // If there is enough unused space to the right of this node then create a smaller node
if ((@sizeOf(Header) + header.size) - alignment_padding - real_size > @sizeOf(Header)) { if (header.size > real_size + alignment_padding) {
header.next_free = insertFreeHeader(@ptrToInt(header) + real_size + alignment_padding, header.size + @sizeOf(Header) - real_size - alignment_padding - @sizeOf(Header), header.next_free); header.next_free = insertFreeHeader(@ptrToInt(header) + real_size + alignment_padding, header.size - real_size - alignment_padding, header.next_free);
} }
self.registerFreeHeader(prev, header.next_free); self.registerFreeHeader(prev, header.next_free);
return @intToPtr([*]u8, @ptrToInt(header))[0..std.mem.alignAllocLen(size, size, if (size_alignment > 1) size_alignment else 1)]; return @intToPtr([*]u8, @ptrToInt(header))[0..std.mem.alignAllocLen(size, size, size_alignment)];
} }
return Allocator.Error.OutOfMemory; return Allocator.Error.OutOfMemory;
@ -470,6 +487,21 @@ const FreeListAllocator = struct {
// Attempting to allocate more than the size of the largest free node should fail // Attempting to allocate more than the size of the largest free node should fail
const remaining_size = second_header.size + @sizeOf(Header); const remaining_size = second_header.size + @sizeOf(Header);
testing.expectError(Allocator.Error.OutOfMemory, alloc(&free_list.allocator, remaining_size + 1, 0, 0)); testing.expectError(Allocator.Error.OutOfMemory, alloc(&free_list.allocator, remaining_size + 1, 0, 0));
// Alloc a non aligned to header
var alloc4 = try alloc(allocator, 13, 1, 0);
const alloc4_addr = @ptrToInt(alloc4.ptr);
const alloc4_end = alloc4_addr + std.mem.alignForward(13, @alignOf(Header));
const header3 = @intToPtr(*Header, alloc4_end);
const header4 = @intToPtr(*Header, alloc4_addr);
// We should still have a length of 13
testing.expectEqual(alloc4.len, 13);
// But this should be aligned to Header (4)
testing.expectEqual(alloc4_end - alloc4_addr, 16);
// Previous header should now point to the next header
testing.expectEqual(header2.next_free, header3);
} }
test "free" { test "free" {
@ -542,9 +574,9 @@ const FreeListAllocator = struct {
testing.expectEqual(free_list.first_free, header); testing.expectEqual(free_list.first_free, header);
// Shrinking by less space than would allow for a new Header shouldn't work // Shrinking by less space than would allow for a new Header shouldn't work
testing.expectError(Allocator.Error.OutOfMemory, resize(allocator, alloc1, alloc1.len - @sizeOf(Header) / 2, 0)); testing.expectEqual(resize(allocator, alloc1, alloc1.len - @sizeOf(Header) / 2, 0), 128);
// Shrinking to less space than would allow for a new Header shouldn't work // Shrinking to less space than would allow for a new Header shouldn't work
testing.expectError(Allocator.Error.OutOfMemory, resize(allocator, alloc1, @sizeOf(Header) / 2, 0)); testing.expectEqual(resize(allocator, alloc1, @sizeOf(Header) / 2, 0), @sizeOf(Header));
} }
}; };

View file

@ -15,8 +15,8 @@ const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig") else @imp
const task = if (is_test) @import(mock_path ++ "task_mock.zig") else @import("task.zig"); const task = if (is_test) @import(mock_path ++ "task_mock.zig") else @import("task.zig");
const heap = @import("heap.zig"); const heap = @import("heap.zig");
const scheduler = @import("scheduler.zig"); const scheduler = @import("scheduler.zig");
const vfs = @import("vfs.zig"); const vfs = @import("filesystem/vfs.zig");
const initrd = @import("initrd.zig"); const initrd = @import("filesystem/initrd.zig");
comptime { comptime {
if (!is_test) { if (!is_test) {
@ -106,16 +106,18 @@ export fn kmain(boot_payload: arch.BootPayload) void {
if (rd_module) |module| { if (rd_module) |module| {
// Load the ram disk // Load the ram disk
var ramdisk_filesystem = initrd.InitrdFS.init(module, &kernel_heap.allocator) catch |e| { const rd_len: usize = module.region.end - module.region.start;
const ramdisk_bytes = @intToPtr([*]u8, module.region.start)[0..rd_len];
var initrd_stream = std.io.fixedBufferStream(ramdisk_bytes);
var ramdisk_filesystem = initrd.InitrdFS.init(&initrd_stream, &kernel_heap.allocator) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to initialise ramdisk: {}\n", .{e}); panic_root.panic(@errorReturnTrace(), "Failed to initialise ramdisk: {}\n", .{e});
}; };
defer { defer ramdisk_filesystem.deinit();
ramdisk_filesystem.deinit();
// Free the raw ramdisk module as we are done // Can now free the module as new memory is allocated for the ramdisk filesystem
kernel_vmm.free(module.region.start) catch |e| { kernel_vmm.free(module.region.start) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to free ramdisk: {}\n", .{e}); panic_root.panic(@errorReturnTrace(), "Failed to free ramdisk: {}\n", .{e});
}; };
}
// 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);