{ Prog->PartCmd - Command-line tools for partition table management. Shell command: PART [subcommand] Subcommands: list - Show partition table add [size] - Add a partition (auto-inits mbr; uses all remaining space if size omitted; accepts B, KB, MB, GB) rm - Remove a partition by slot number format - Format a partition with a filesystem @author(Aaron Hance ) } unit app.partcmd; interface procedure init(); implementation uses driver.storage.fs.mgr, core.ds.lists, memory.heap, driver.storage.vol.mbr, io.stdio, driver.storage.mgr, driver.storage.types, core.strings, debug.tracer, driver.storage.vol.mgr; var g_out : POutBuf; { ---------- helpers ---------- } function wnd : uint32; begin wnd := 0; end; function str2int(s : pchar) : uint32; begin str2int := stringToInt(s); end; function isNumeric(s : pchar) : boolean; var i : uint32; len : uint32; begin isNumeric := false; len := stringSize(s); if len = 0 then exit; for i := 0 to len - 1 do begin if (s[i] < '0') or (s[i] > '9') then exit; end; isNumeric := true; end; procedure print(s : pchar); begin io.stdio.bufWriteStr(g_out, s); end; procedure println(s : pchar); begin io.stdio.bufWriteStrLn(g_out, s); end; procedure printint(v : uint32); begin io.stdio.bufWriteInt(g_out, integer(v)); end; procedure printhex(v : uint32); begin io.stdio.bufWriteHex(g_out, v); end; procedure printsize(bytes : uint32); begin if bytes >= 1073741824 then begin printint(bytes div 1073741824); print(' GB'); end else if bytes >= 1048576 then begin printint(bytes div 1048576); print(' MB'); end else if bytes >= 1024 then begin printint(bytes div 1024); print(' KB'); end else begin printint(bytes); print(' B'); end; end; { Parse a size string (e.g. '500MB', '2GB', '1024KB', '4096B', '1024') into a sector count. Plain numbers are treated as bytes. Returns 0 on invalid input. } function parsesize(s : pchar; sectorSize : uint32) : uint32; var len : uint32; last : char; prev : char; numEnd : uint32; value : uint32; bytes : uint32; begin parsesize := 0; len := stringSize(s); if len = 0 then exit; last := s[len - 1]; if (last = 'B') or (last = 'b') then begin if len >= 3 then prev := s[len - 2] else prev := #0; if (prev = 'G') or (prev = 'g') then begin numEnd := len - 2; s[numEnd] := #0; value := stringToInt(s); s[numEnd] := prev; bytes := value * 1073741824; end else if (prev = 'M') or (prev = 'm') then begin numEnd := len - 2; s[numEnd] := #0; value := stringToInt(s); s[numEnd] := prev; bytes := value * 1048576; end else if (prev = 'K') or (prev = 'k') then begin numEnd := len - 2; s[numEnd] := #0; value := stringToInt(s); s[numEnd] := prev; bytes := value * 1024; end else begin numEnd := len - 1; s[numEnd] := #0; value := stringToInt(s); s[numEnd] := last; bytes := value; end; end else begin bytes := stringToInt(s); end; if sectorSize = 0 then exit; parsesize := bytes div sectorSize; end; { ---------- PART LIST ---------- } procedure cmd_list(params : PParamList); var idx : uint32; device : PStorage_Device; bootrecord : PMaster_Boot_Record; i : uint32; part : TPartition_table; hasAny : boolean; begin if paramCount(params) < 2 then begin println('Usage: PART list '); exit; end; if not isNumeric(getParam(1, params)) then begin println('Error: disk index must be a number.'); exit; end; idx := str2int(getParam(1, params)); device := driver.storage.mgr.get_device(idx); if device = nil then begin print('Error: device '); printint(idx); print(' not found ('); printint(driver.storage.mgr.get_device_count()); println(' devices registered).'); exit; end; if device^.dispatchRead = nil then begin println('Error: device has no read support.'); exit; end; bootrecord := driver.storage.mgr.read_mbr(device); if bootrecord = nil then begin println('Error: failed to read mbr (memory allocation failed).'); exit; end; print('mbr Signature: '); printhex(bootrecord^.signature); println(''); print('Boot Marker: '); printhex(bootrecord^.boot_sector); println(''); if bootrecord^.boot_sector <> $AA55 then println('Warning: invalid boot marker (expected 0xAA55).'); println(''); println('SLOT SYS_ID START SIZE BOOT'); println('---- ------ ----- ---- ----'); hasAny := false; for i := 0 to 3 do begin part := bootrecord^.partition[i]; if (part.LBA_start <> 0) or (part.sector_count <> 0) then hasAny := true; print(' '); printint(i); print(' '); printhex(part.system_id); print(' '); printint(part.LBA_start); print(' '); printsize(part.sector_count * device^.sectorSize); print(' '); if driver.storage.vol.mbr.get_bootable(@part) then println('yes') else println('no'); end; if not hasAny then println('(all partition slots are empty)'); kfree(puint32(bootrecord)); end; { ---------- PART ADD ---------- } procedure cmd_add(params : PParamList); var idx : uint32; slot : sint32; lba : uint32; sectors : uint32; freeSec : uint32; device : PStorage_Device; part : TPartition_table; bootrecord : PMaster_Boot_Record; begin if paramCount(params) < 2 then begin println('Usage: PART add [size]'); println(' size examples: 500MB, 2GB, 1024KB, 4096B'); println(' omit size to use all remaining space.'); exit; end; if not isNumeric(getParam(1, params)) then begin println('Error: disk index must be a number.'); exit; end; idx := str2int(getParam(1, params)); device := driver.storage.mgr.get_device(idx); if device = nil then begin print('Error: device '); printint(idx); print(' not found ('); printint(driver.storage.mgr.get_device_count()); println(' devices registered).'); exit; end; if not device^.writable then begin println('Error: device is not writable.'); exit; end; if device^.dispatchWrite = nil then begin println('Error: device has no write support.'); exit; end; { Auto-init: create fresh mbr if none present } bootrecord := driver.storage.mgr.read_mbr(device); if bootrecord <> nil then begin if bootrecord^.boot_sector <> $AA55 then begin println('No valid mbr found, initialising disk...'); if driver.storage.vol.mgr.init_disk(device) <> eNone then begin println('Error: disk initialization failed.'); exit; end; end; kfree(puint32(bootrecord)); end; slot := driver.storage.vol.mgr.find_free_slot(device); if slot < 0 then begin println('Error: no free partition slots (max 4).'); exit; end; freeSec := driver.storage.vol.mgr.get_free_sector_count(device); if freeSec = 0 then begin println('Error: no free space on device.'); exit; end; if paramCount(params) >= 3 then begin sectors := parsesize(getParam(2, params), device^.sectorSize); if sectors = 0 then begin print('Error: invalid size "'); print(getParam(2, params)); println('". Use e.g. 500MB, 2GB, 1024KB, 4096B.'); exit; end; if sectors > freeSec then begin print('Error: requested '); printsize(sectors * device^.sectorSize); print(' but only '); printsize(freeSec * device^.sectorSize); println(' available.'); exit; end; end else sectors := freeSec; lba := driver.storage.vol.mgr.find_free_space(device, sectors); if lba = 0 then begin print('Error: cannot find '); printsize(sectors * device^.sectorSize); println(' of contiguous space.'); exit; end; driver.storage.vol.mbr.setup_partition(@part, lba, sectors); part.system_id := 0; if driver.storage.vol.mgr.add_partition(device, slot, part) <> eNone then begin println('Error: partition creation failed.'); exit; end; print('Partition added in slot '); printint(slot); print(': LBA '); printint(lba); print(', '); printsize(sectors * device^.sectorSize); println('.'); end; { ---------- PART RM ---------- } procedure cmd_rm(params : PParamList); var idx : uint32; slot : uint32; device : PStorage_Device; old : TPartition_table; begin if paramCount(params) < 3 then begin println('Usage: PART rm '); exit; end; if not isNumeric(getParam(1, params)) then begin println('Error: disk index must be a number.'); exit; end; if not isNumeric(getParam(2, params)) then begin println('Error: slot must be a number (0-3).'); exit; end; idx := str2int(getParam(1, params)); slot := str2int(getParam(2, params)); device := driver.storage.mgr.get_device(idx); if device = nil then begin print('Error: device '); printint(idx); print(' not found ('); printint(driver.storage.mgr.get_device_count()); println(' devices registered).'); exit; end; if slot > 3 then begin println('Error: slot must be 0-3.'); exit; end; if not device^.writable then begin println('Error: device is not writable.'); exit; end; if device^.dispatchWrite = nil then begin println('Error: device has no write support.'); exit; end; old := driver.storage.vol.mgr.get_partition(device, slot); if (old.LBA_start = 0) and (old.sector_count = 0) then begin print('Slot '); printint(slot); println(' is already empty.'); exit; end; if driver.storage.vol.mgr.remove_partition(device, slot) <> eNone then begin println('Error: partition removal failed.'); exit; end; print('Partition slot '); printint(slot); print(' removed (was LBA '); printint(old.LBA_start); print(', '); printsize(old.sector_count * device^.sectorSize); println(').'); end; { ---------- PART FORMAT ---------- } procedure cmd_format(params : PParamList); var idx : uint32; slot : uint32; fsName : pchar; device : PStorage_Device; part : TPartition_table; vol : PStorage_Volume; fs : PFilesystem; i : uint32; begin if paramCount(params) < 4 then begin println('Usage: PART format '); exit; end; if not isNumeric(getParam(1, params)) then begin println('Error: disk index must be a number.'); exit; end; if not isNumeric(getParam(2, params)) then begin println('Error: slot must be a number (0-3).'); exit; end; idx := str2int(getParam(1, params)); slot := str2int(getParam(2, params)); fsName := getParam(3, params); device := driver.storage.mgr.get_device(idx); if device = nil then begin print('Error: device '); printint(idx); print(' not found ('); printint(driver.storage.mgr.get_device_count()); println(' devices registered).'); exit; end; if slot > 3 then begin println('Error: slot must be 0-3.'); exit; end; if not device^.writable then begin println('Error: device is not writable.'); exit; end; part := driver.storage.vol.mgr.get_partition(device, slot); if (part.LBA_start = 0) and (part.sector_count = 0) then begin print('Error: partition slot '); printint(slot); println(' is empty.'); exit; end; { Find the corresponding volume, or create it } vol := nil; for i := 0 to driver.storage.vol.mgr.get_volume_count() - 1 do begin vol := driver.storage.vol.mgr.get_volume(i); if (vol^.device = device) and (vol^.sectorStart = part.LBA_start) then break; vol := nil; end; if vol = nil then begin driver.storage.vol.mgr.create_volume_from_partition(device, part.LBA_start, part.sector_count); vol := driver.storage.vol.mgr.get_volume(driver.storage.vol.mgr.get_volume_count() - 1); end; if vol^.filesystem <> nil then begin print('Warning: already has filesystem "'); print(vol^.filesystem^.sName); println('", overwriting.'); end; fs := driver.storage.fs.mgr.find_filesystem_by_name(fsName); if fs = nil then begin print('Error: filesystem "'); print(fsName); println('" not registered.'); print('Registered: '); if driver.storage.fs.mgr.get_filesystem_count() = 0 then println('(none)') else begin for i := 0 to driver.storage.fs.mgr.get_filesystem_count() - 1 do begin if i > 0 then print(', '); print(driver.storage.fs.mgr.get_filesystem(i)^.sName); end; println(''); end; exit; end; if fs^.createCallback = nil then begin print('Error: filesystem "'); print(fsName); println('" does not support formatting.'); exit; end; i := 0; while i < driver.storage.vol.mgr.get_volume_count() do begin if driver.storage.vol.mgr.get_volume(i) = vol then break; Inc(i); end; if not driver.storage.vol.mgr.format_volume(device, i, fsName, nil) then begin println('Error: format failed; the volume was not reported ready.'); exit; end; print('Partition '); printint(slot); print(' on disk '); printint(idx); print(' formatted with '); print(fsName); print(' ('); printsize(vol^.sectorCount * vol^.sectorSize); println(').'); end; { ---------- Main dispatcher ---------- } procedure command_part(params : PParamList; stdin_buf, stdout_buf, stderr_buf: POutBuf); var subcmd : pchar; begin g_out := stdout_buf; push_trace('PartCmd.command_part'); if paramCount(params) = 0 then begin println('Usage: PART '); exit; end; subcmd := getParam(0, params); if stringEquals(subcmd, 'list') then begin cmd_list(params); exit; end; if stringEquals(subcmd, 'add') then begin cmd_add(params); exit; end; if stringEquals(subcmd, 'rm') then begin cmd_rm(params); exit; end; if stringEquals(subcmd, 'format') then begin cmd_format(params); exit; end; print('Unknown subcommand: '); println(subcmd); end; { ---------- Init ---------- } procedure init(); begin io.stdio.registerCommand('PART', @command_part, 'Partition management.'); end; end.