diff options
| -rw-r--r-- | convert/convert.ha | 49 | ||||
| -rw-r--r-- | gpt/crc32.ha | 16 | ||||
| -rw-r--r-- | gpt/gpt.ha | 248 | ||||
| -rw-r--r-- | gpt/range.ha | 12 | ||||
| -rw-r--r-- | mbr/mbr.ha | 19 | ||||
| -rw-r--r-- | sector/README | 7 | ||||
| -rw-r--r-- | sector/sector.ha | 45 |
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; +}; @@ -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); +}; @@ -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)?; }; |
