summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Sior <aho@sior.be>2022-06-17 14:43:27 +0200
committerAlejandro Sior <aho@sior.be>2022-06-17 14:43:27 +0200
commit12577f3445df86023ba0cbe2462d55ea5dcafe20 (patch)
treee72d03b6339084760d4c781cbbb5e7e4dd71385d
parent3c8602ee9f04991e1d60b8d6504e12296ca671d9 (diff)
gptman: now can allocate partitions
-rw-r--r--convert/convert.ha49
-rw-r--r--gpt/crc32.ha16
-rw-r--r--gpt/gpt.ha248
-rw-r--r--gpt/range.ha12
-rw-r--r--mbr/mbr.ha19
-rw-r--r--sector/README7
-rw-r--r--sector/sector.ha45
7 files changed, 262 insertions, 134 deletions
diff --git a/convert/convert.ha b/convert/convert.ha
index 10f73c7..98ad124 100644
--- a/convert/convert.ha
+++ b/convert/convert.ha
@@ -16,18 +16,59 @@ export fn convert(args: []str) void = {
fmt::fatalf("gptman.convert: needs disk");
};
- fmt::println(args[1])!;
let file = os::open(args[1], fs::flags::RDWR)!;
defer io::close(file)!;
+ // let vol = gpt::create(file, 69);
+ // gpt::mkbackup(vol);
+ // defer {
+ // gpt::chksums(vol);
+ // gpt::commit(vol)!;
+ // gpt::finish(vol);
+ // };
+
+ // vol.mbr.entries[0] = mbr::mbr_entry {
+ // attributes = 0,
+ // part_type = 0xEE,
+ // lba_begin = 1,
+ // lba_end = -1: u32,
+ // };
+ // vol.mbr.magic = 0xaa55;
+
let vol = gpt::from(file)!;
gpt::mkbackup(vol);
defer {
gpt::chksums(vol);
- gpt::commit(vol);
+ gpt::commit(vol)!;
gpt::finish(vol);
};
- vol.primary.header.disk_guid[0] = 0xcafebabe;
- vol.primary.header.disk_guid[1] = 0xabababababababab;
+ // fmt::printfln("rev {}", vol.primary.header.revision)!;
+
+ // for (let i = 0z; i < 4; i += 1) {
+ // fmt::printfln("type {} from {} to {}", vol.mbr.entries[i].part_type, vol.mbr.entries[i].lba_begin, vol.mbr.entries[i].lba_end)!;
+ // };
+
+ // vol.primary.header.disk_guid[0] = 0xcafababe;
+ // vol.primary.header.disk_guid[1] = 0xabababababababab;
+
+ // // 516E7CB6-6ECF-11D6-8FF8-00022D09712B
+
+ // vol.primary.entries[4].part_type[0] = 0x516E7CB66ECF;
+ // vol.primary.entries[4].lba_begin = 35;
+ // vol.primary.entries[4].lba_end = 37;
+ // vol.primary.entries[5].part_type[0] = 0x516E7CB66ECF;
+ // vol.primary.entries[5].lba_begin = 40;
+ // vol.primary.entries[5].lba_end = 41;
+
+
+ const much = 4z;
+ const addr = gpt::findfree(vol, much);
+ const where = gpt::findfreeentry(vol) as *gpt::entry;
+
+ where.part_type = [0xCAFEBABE, 0xBABABA];
+ where.lba_begin = addr;
+ where.lba_end = addr + much: u64 - 1;
+
+ fmt::printfln("fitting {}: {} and could be put at {}", much, addr, where)!;
};
diff --git a/gpt/crc32.ha b/gpt/crc32.ha
index 24318bd..386779f 100644
--- a/gpt/crc32.ha
+++ b/gpt/crc32.ha
@@ -79,3 +79,19 @@ export fn header_crc32(self: *header) u32 = {
crc ^= 0xffffffff;
return crc;
};
+
+// Calculates the crc32 entries array checksum
+export fn entries_crc32(self: *[128]entry) u32 = {
+ const data = self: *[*]u8;
+
+ let crc: u32 = 0xffffffff;
+ for (let i = 0z; i < 128*128; i += 1) {
+ let d: u8 = data[i];
+
+ let lookup_index: u32 = (crc ^ d) & 0xff;
+ crc = (crc >> 8) ^ crc_table[lookup_index];
+ };
+
+ crc ^= 0xffffffff;
+ return crc;
+};
diff --git a/gpt/gpt.ha b/gpt/gpt.ha
index e1ebcfd..53ca37f 100644
--- a/gpt/gpt.ha
+++ b/gpt/gpt.ha
@@ -23,28 +23,14 @@ export type block = struct {
entries: *[128]entry
};
-fn block_getheader(self: *block) *header = {
- return self.header_sec.buf: *[*]u8: *header;
+fn block_commit(self: *block) (void | io::error) = {
+ sector::commit(&self.header_sec)?;
+ sector::commit(&self.entries_sec)?;
};
-fn block_getentries(self: *block) []entry = {
- const e = types::slice {
- data = self.entries_sec.buf: *[*]u8,
- length = 128,
- capacity = 128
- };
-
- return *(&e: *const []entry);
-};
-
-fn block_commit(self: *block) void = {
- sector::commit(&self.header_sec);
- //sector::commit(&self.entries_sec);
-};
-
-fn block_fetch(self: *block) void = {
- sector::fetch(&self.header_sec);
- sector::fetch(&self.entries_sec);
+fn block_fetch(self: *block) (void | io::error) = {
+ sector::fetch(&self.header_sec)?;
+ sector::fetch(&self.entries_sec)?;
};
fn block_drop(self: *block) void = {
@@ -69,6 +55,10 @@ export type header = struct {
@offset(88) entries_crc32: u32
};
+fn header_validate(self: *header) bool = {
+ return self.signature == 0x5452415020494645;
+};
+
export type entry = struct {
@offset(0) part_type: [2]u64,
@offset(16) part: [2]u64,
@@ -78,66 +68,159 @@ export type entry = struct {
@offset(56) name: [72]u8
};
-export fn validate(self: *gpt) bool = {
- if (getheader(self).signature != 0x5452415020494645) {
- return false;
- };
+export fn from(fd: io::file) (*gpt | nogpt | io::error) = {
+ // Get the primary header
+ const prim_header_sec = sector::map(fd, 1, 1);
+ sector::fetch(&prim_header_sec)?;
- return true;
-};
+ const prim_header = prim_header_sec.buf: *[*]u8: *header;
+ if (!header_validate(prim_header)) {
+ sector::finish(&prim_header_sec);
+ return nogpt;
+ };
-export fn from(fd: io::file) (*gpt | nogpt | errors::error) = {
- const prim_header_sector = sector::map(fd, 1, 1);
- sector::fetch(&prim_header_sector);
- const prim_header = prim_header_sector.buf: *[*]u8: *header;
- // XXX validate header
+ // Get the primary entries
+ const prim_entries_sec = sector::map(fd, prim_header.entries_lba, 128*128/512);
+ sector::fetch(&prim_entries_sec)?;
- const prim_table_sector = sector::map(fd, prim_header.entries_lba, prim_header.entries_lba + 128*128/512 - 1);
- sector::fetch(&prim_table_sector);
+ // Get the backup header
+ const back_header_sec = sector::map(fd, prim_header.backup_header_lba, 1);
+ sector::fetch(&back_header_sec)?;
- // Create the primary GPT copy
- const primary = block {
- header_sec = prim_header_sector,
- entries_sec = prim_table_sector,
- header = prim_header,
- entries = prim_table_sector.buf: *[*]u8: *[128]entry
+ const back_header = back_header_sec.buf: *[*]u8: *header;
+ if (!header_validate(back_header)) {
+ sector::finish(&prim_header_sec);
+ sector::finish(&back_header_sec);
+ return nogpt;
};
+ // Get the backup entries
+ const back_entries_sec = sector::map(fd, back_header.entries_lba, 128*128/512);
+ sector::fetch(&back_entries_sec)?;
- const back_header_sector = sector::map(fd, prim_header.backup_header_lba, prim_header.backup_header_lba);
- sector::fetch(&back_header_sector);
- const back_header = back_header_sector.buf: *[*]u8: *header;
- //XXX validate header
+ const mbr_sec = sector::map(fd, 0, 1);
+ sector::fetch(&mbr_sec)?;
- const back_table_sector = sector::map(fd, back_header.entries_lba, back_header.entries_lba + 128*128/512 - 1);
+ // Allocate the GPT structure
+ let self = alloc(gpt {
+ mbr_sec = mbr_sec,
+ mbr = mbr_sec.buf: *[*]u8: *mbr::mbr,
+ primary = block {
+ header_sec = prim_header_sec,
+ entries_sec = prim_entries_sec,
+ header = prim_header,
+ entries = prim_entries_sec.buf: *[*]u8: *[128]entry
+ },
+ backup = block {
+ header_sec = back_header_sec,
+ entries_sec = back_entries_sec,
+ header = back_header,
+ entries = back_entries_sec.buf: *[*]u8: *[128]entry
+ }
+ });
- sector::fetch(&back_table_sector);
+ return self;
+};
- // Create the backup block structure
- let backup = block {
- header_sec = back_header_sector,
- entries_sec = back_table_sector,
- header = back_header,
- entries = back_table_sector.buf: *[*]u8: *[128]entry
+export fn create(fd: io::file, freeno: size) *gpt = {
+ const entries_begin = 2z;
+ const free_begin = entries_begin + 128*128/512;
+ const bentries_begin = free_begin + freeno + 1;
+ const bheader_begin = bentries_begin + 128*128/512;
+
+ const mbr_sec = sector::map(fd, 0, 1);
+ const mbr = mbr_sec.buf: *[*]u8: *mbr::mbr;
+
+ const prim_header_sec = sector::map(fd, 1, 1);
+ const prim_header = prim_header_sec.buf: *[*]u8: *header;
+ *prim_header = header {
+ signature = 0x5452415020494645,
+ revision = 0x10000,
+ header_size = 92,
+ header_crc32 = 0,
+ reserved0 = 0,
+ header_lba = 1,
+ backup_header_lba = bheader_begin,
+ first_lba = free_begin,
+ last_lba = bentries_begin - 1,
+ disk_guid = [0xCAFEBABE, 0xCAFEBABE],
+ entries_lba = entries_begin,
+ entries_len = 128,
+ entry_size = 128,
+ entries_crc32 = 0,
};
+ const prim_entries_sec = sector::map(fd, entries_begin, 128*128/512);
+ const prim_entries = prim_entries_sec.buf: *[*]u8: *[128]entry;
- const mbr_sec = sector::map(fd, 0, 0);
+ const back_header_sec = sector::map(fd, bheader_begin, 1);
+ const back_header = back_header_sec.buf: *[*]u8: *header;
- // Allocate the GPT structure
+ const back_entries_sec = sector::map(fd, bentries_begin, 128*128/512);
+ const back_entries = back_entries_sec.buf: *[*]u8: *[128]entry;
+
let self = alloc(gpt {
mbr_sec = mbr_sec,
- mbr = mbr_sec.buf: *[*]u8: *mbr::mbr,
- primary = primary,
- backup = backup
+ mbr = mbr,
+ primary = block {
+ header_sec = prim_header_sec,
+ entries_sec = prim_entries_sec,
+ header = prim_header,
+ entries = prim_entries
+ },
+ backup = block {
+ header_sec = back_header_sec,
+ entries_sec = back_entries_sec,
+ header = back_header,
+ entries = back_entries
+ }
});
-
- // Fetch the MBR
- sector::fetch(&self.mbr_sec);
+
+ mkbackup(self);
return self;
};
+// Finds an lba for a new partition that fits the desired length
+export fn findfree(self: *gpt, length: u64) u64 = {
+ const header = self.primary.header;
+ const entries = self.primary.entries;
+
+ let cur = header.first_lba;
+
+ for (cur >= header.first_lba && cur + length <= header.last_lba) {
+ let i = 0z;
+ for (i < header.entries_len) {
+ defer i += 1;
+ if (entries[i].part_type[0] == 0 && entries[i].part_type[1] == 0)
+ continue;
+ if (overlap((cur, length), (entries[i].lba_begin, entries[i].lba_end - entries[i].lba_begin))) {
+ cur = entries[i].lba_end + 1;
+ break;
+ };
+ };
+
+ if (i == header.entries_len)
+ return cur;
+ };
+
+ return 0;
+};
+
+// Finds a GPT entry that is currently not used
+// The entry pointer returned is borrowed from the arguments
+export fn findfreeentry(self: *gpt) nullable *entry = {
+ const header = self.primary.header;
+ const entries = self.primary.entries;
+
+ for (let i = 0z; i < header.entries_len; i += 1) {
+ if (entries[i].part_type[0] == 0 && entries[i].part_type[1] == 0)
+ return &entries[i];
+ };
+
+ return null;
+};
+
// Frees the resources associated with the GPT structure.
export fn finish(self: *gpt) void = {
sector::finish(&self.mbr_sec);
@@ -147,51 +230,34 @@ export fn finish(self: *gpt) void = {
};
// Updates the data structures from the disk
-export fn fetch(self: *gpt) void = {
- sector::fetch(&self.mbr_sec);
- block_fetch(&self.primary);
- block_fetch(&self.backup);
+export fn fetch(self: *gpt) (void | io::error) = {
+ sector::fetch(&self.mbr_sec)?;
+ block_fetch(&self.primary)?;
+ block_fetch(&self.backup)?;
};
+// Copies the current primary block into its backup
+// Note: does not compute the checksums (see chksums())
export fn mkbackup(self: *gpt) void = {
rt::memcpy(self.backup.header, self.primary.header, size(header));
rt::memcpy(self.backup.entries, self.primary.entries, size([128]entry));
- self.backup.header.header_lba = self.backup.header_sec.lba_begin;
- self.backup.header.backup_header_lba = self.primary.header_sec.lba_begin;
- self.backup.header.entries_lba = self.backup.entries_sec.lba_begin;
+ self.backup.header.header_lba = self.backup.header_sec.offs;
+ self.backup.header.backup_header_lba = self.primary.header_sec.offs;
+ self.backup.header.entries_lba = self.backup.entries_sec.offs;
};
// Commits changed done on the data structure to the disk
-export fn commit(self: *gpt) void = {
- // XXX: CRC32 checksum
-
- sector::commit(&self.mbr_sec);
- block_commit(&self.primary);
- block_commit(&self.backup);
- //const header = getheader(self);
-
- // XXX: update copy
- //sector::commit_at(self.mbr, self.mbr.fd, header.backup_header_lba);
+export fn commit(self: *gpt) (void | io::error) = {
+ sector::commit(&self.mbr_sec)?;
+ block_commit(&self.primary)?;
+ block_commit(&self.backup)?;
};
// Updates the CRC32 checksums of the different blocks
export fn chksums(self: *gpt) void = {
+ self.primary.header.entries_crc32 = entries_crc32(self.primary.entries);
+ self.backup.header.entries_crc32 = entries_crc32(self.backup.entries);
self.primary.header.header_crc32 = header_crc32(self.primary.header);
self.backup.header.header_crc32 = header_crc32(self.backup.header);
};
-
-// Gets the MBR structure. Return value is borrowed from argument.
-export fn getmbr(self: *gpt) *mbr::mbr = {
- return self.mbr_sec.buf: *[*]u8: *mbr::mbr;
-};
-
-// Gets the GPT primary header structure. Return value is borrowed from argument.
-export fn getheader(self: *gpt) *header = {
- return block_getheader(&self.primary);
-};
-
-// Gets the GPT primary entries structures. Return value is borrowed from argument.
-export fn getentries(self: *gpt) []entry = {
- return block_getentries(&self.primary);
-};
diff --git a/gpt/range.ha b/gpt/range.ha
new file mode 100644
index 0000000..783e40d
--- /dev/null
+++ b/gpt/range.ha
@@ -0,0 +1,12 @@
+fn rangehas(a: (u64, u64), b: (u64, u64)) bool = {
+ if (b.0 + b.1 <= a.0)
+ return false;
+ if (b.0 >= a.0 + a.1)
+ return false;
+
+ return true;
+};
+
+fn overlap(a: (u64, u64), b: (u64, u64)) bool = {
+ return rangehas(a, b) || rangehas(b, a);
+};
diff --git a/mbr/mbr.ha b/mbr/mbr.ha
index ceb115f..d0622d2 100644
--- a/mbr/mbr.ha
+++ b/mbr/mbr.ha
@@ -29,22 +29,3 @@ export fn validate(self: *mbr) bool = {
return true;
};
-
-// Gets a handle to the MBR of a partition.
-// Return value is returned to user. Resource should be freed with mbr::finish.
-export fn from(fd: io::file) (*mbr | nombr | errors::error) = {
- const self = io::mmap(null, sector::length, io::prot::READ | io::prot::WRITE, io::mflags::SHARED, fd, sector::lba(mbr_lba))!: *mbr;
-
- if (!validate(self)) {
- io::munmap(self, sector::length)?;
- return nombr;
- };
-
- return self;
-};
-
-// Frees the resources associated with the MBR partition.
-// User revokes ownership.
-export fn finish(self: *mbr) (void | errors::error) = {
- io::munmap(self, sector::length)?;
-};
diff --git a/sector/README b/sector/README
new file mode 100644
index 0000000..c47a6f1
--- /dev/null
+++ b/sector/README
@@ -0,0 +1,7 @@
+The sector module provides a facility to read regions of
+sectors from a file and readily be able to keep them in sync
+with their original location.
+
+Normally, one would use mmap for this, however for ease of
+porting to other projects and due to the mmap offset alignment
+limitation, we proceed like this for now. \ No newline at end of file
diff --git a/sector/sector.ha b/sector/sector.ha
index 50f417b..ae1133b 100644
--- a/sector/sector.ha
+++ b/sector/sector.ha
@@ -1,48 +1,53 @@
+use errors;
use io;
use types;
-use fmt;
-
-export const length: u64 = 512;
+export const sector_length: u64 = 512;
export fn lba(no: u64) size = {
- return no * length;
+ return no * sector_length;
};
+// Type that represents a region of a file mapped to memory
+// which can be fetched or committed to.
export type sector = struct {
buf: []u8,
fd: io::file,
- lba_begin: u64,
- lba_end: u64,
+ offs: u64,
+ length: u64,
};
-export fn map(fd: io::file, lba_begin: u64, lba_end: u64) sector = {
+// Creates a sector based on a file and a region
+export fn map(fd: io::file, offs: u64, length: u64) sector = {
let self = sector {
- buf = alloc([0...], (lba_end - lba_begin + 1)*length),
+ buf = alloc([0...], length * sector_length),
fd = fd,
- lba_begin = lba_begin,
- lba_end = lba_end
+ offs = offs,
+ length = length
};
return self;
};
+// Finishes a sector and deallocates its related resources
export fn finish(self: *sector) void = {
free(self.buf);
};
-export fn fetch(self: *sector) void = {
- // XXX proper error
- io::seek(self.fd, (self.lba_begin * length): io::off, io::whence::SET)!;
- io::read(self.fd, self.buf)!;
+// Fetches the content of a sector into the buffer
+export fn fetch(self: *sector) (void | io::error) = {
+ io::seek(self.fd, (self.offs * sector_length): io::off, io::whence::SET)?;
+ io::read(self.fd, self.buf)?;
};
-export fn commit(self: *sector) void = {
- io::seek(self.fd, (self.lba_begin * length): io::off, io::whence::SET)!;
- io::write(self.fd, self.buf)!;
+// Commits the content of a sector into the file
+export fn commit(self: *sector) (void | io::error) = {
+ io::seek(self.fd, (self.offs * sector_length): io::off, io::whence::SET)?;
+ io::write(self.fd, self.buf)?;
};
-export fn commit_at(self: *sector, fd: io::file, lba: u64) void = {
- io::seek(fd, (lba * length): io::off, io::whence::SET)!;
- io::write(fd, self.buf)!;
+// Commits the contents of a sector to another location in another file
+export fn commit_at(self: *sector, fd: io::file, lba: u64) (void | io::error) = {
+ io::seek(fd, (lba * sector_length): io::off, io::whence::SET)?;
+ io::write(fd, self.buf)?;
};