Implement manual partition management (create/delete/mountpoints)

This commit is contained in:
2026-02-03 17:07:45 +01:00
parent efe25f3e11
commit 22e1fa8f62
2 changed files with 123 additions and 22 deletions

View File

@@ -99,3 +99,42 @@ def mount_partitions(partition_info, mount_root="/mnt"):
run_command(["swapon", partition_info["swap"]]) run_command(["swapon", partition_info["swap"]])
logger.info(f"Partitions mounted at {mount_root}") logger.info(f"Partitions mounted at {mount_root}")
def create_partition(disk_device, size_mb, type_code="8300", name="Linux filesystem"):
"""
Creates a new partition on the disk.
type_code: ef00 (EFI), 8200 (Swap), 8300 (Linux)
"""
# Find next available partition number
res = run_command(["sgdisk", "-p", disk_device])
# Very basic parsing to find the next number
existing_nums = []
for line in res.stdout.splitlines():
parts = line.split()
if parts and parts[0].isdigit():
existing_nums.append(int(parts[0]))
next_num = 1
while next_num in existing_nums:
next_num += 1
run_command([
"sgdisk",
"-n", f"{next_num}:0:+{size_mb}M",
"-t", f"{next_num}:{type_code}",
"-c", f"{next_num}:{name}",
disk_device
])
run_command(["partprobe", disk_device])
return next_num
def delete_partition(disk_device, part_num):
"""Deletes a partition by number."""
run_command(["sgdisk", "-d", str(part_num), disk_device])
run_command(["partprobe", disk_device])
def wipe_disk(disk_device):
"""Zaps the disk and creates a new GPT table."""
run_command(["sgdisk", "-Z", disk_device])
run_command(["sgdisk", "-o", disk_device])
run_command(["partprobe", disk_device])

View File

@@ -236,6 +236,7 @@ class PartitioningPage(Adw.Bin):
# Initially empty until loaded with a specific disk # Initially empty until loaded with a specific disk
def load_partitions(self, disk_device=None): def load_partitions(self, disk_device=None):
self.current_disk_path = disk_device
target_disk = None target_disk = None
try: try:
@@ -428,46 +429,107 @@ class PartitioningPage(Adw.Bin):
def show_context_menu(self, widget, x, y): def show_context_menu(self, widget, x, y):
data = widget.part_data data = widget.part_data
self.selected_disk_path = self.current_disk_path # Assumes we store this
popover = Gtk.Popover() popover = Gtk.Popover()
popover.set_parent(widget) popover.set_parent(widget)
menu_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) menu_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
menu_box.set_spacing(0)
popover.set_child(menu_box) popover.set_child(menu_box)
def add_menu_item(label, icon_name=None, destructive=False): def add_menu_item(label, icon_name, callback, destructive=False):
btn = Gtk.Button(label=label) btn = Gtk.Button(label=label)
btn.add_css_class("flat") btn.add_css_class("flat")
btn.set_halign(Gtk.Align.FILL)
if destructive: if destructive:
btn.add_css_class("destructive-action") btn.add_css_class("destructive-action")
if icon_name: content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) icon = Gtk.Image.new_from_icon_name(icon_name)
content.set_spacing(12) lbl = Gtk.Label(label=label)
icon = Gtk.Image.new_from_icon_name(icon_name) content.append(icon)
lbl = Gtk.Label(label=label) content.append(lbl)
content.append(icon) btn.set_child(content)
content.append(lbl)
btn.set_child(content)
btn.connect("clicked", lambda b: [popover.popdown(), callback()])
menu_box.append(btn) menu_box.append(btn)
return btn
if data.get("type") == "partition": if data.get("type") == "partition":
add_menu_item("Select Mount Point", "folder-open-symbolic") add_menu_item("Select Mount Point", "folder-open-symbolic",
add_menu_item("Format", "drive-harddisk-symbolic") lambda: self.select_mount_point(data))
add_menu_item("Resize", "object-resize-symbolic")
separator = Gtk.Separator() separator = Gtk.Separator()
menu_box.append(separator) menu_box.append(separator)
btn_del = add_menu_item("Delete", "user-trash-symbolic", destructive=True) add_menu_item("Delete", "user-trash-symbolic",
btn_del.add_css_class("error") lambda: self.delete_part(data), destructive=True)
elif data.get("type") == "empty": elif data.get("type") == "empty":
add_menu_item("Create Partition", "list-add-symbolic") add_menu_item("Create Partition", "list-add-symbolic",
lambda: self.create_part_dialog(data))
popover.popup() popover.popup()
def get_config(self): def select_mount_point(self, data):
return {"partitions": self.partitions} win = Adw.Window(title="Select Mount Point", modal=True, transient_for=self.get_root())
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12, margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)
win.set_content(box)
options = ["/", "/boot/efi", "[SWAP]", "None"]
dropdown = Gtk.DropDown.new_from_strings(options)
box.append(Gtk.Label(label=f"Mount point for {data['name']}:"))
box.append(dropdown)
def on_apply(b):
selected = options[dropdown.get_selected()]
data["mount_point"] = selected if selected != "None" else ""
win.close()
self.refresh_bar()
btn = Gtk.Button(label="Apply", add_css_class="suggested-action")
btn.connect("clicked", on_apply)
box.append(btn)
win.present()
def delete_part(self, data):
from ...backend.disk import delete_partition
# Extract partition number from name (e.g., nvme0n1p3 -> 3)
import re
match = re.search(r"(\d+)$", data["name"])
if match:
part_num = int(match.group(1))
delete_partition(self.current_disk_path, part_num)
self.load_partitions(self.current_disk_path)
def create_part_dialog(self, data):
win = Adw.Window(title="Create Partition", modal=True, transient_for=self.get_root())
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12, margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)
win.set_content(box)
# Size entry
size_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
size_entry = Gtk.Entry(text=str(int(data["bytes"] / (1024*1024))))
size_box.append(size_entry)
size_box.append(Gtk.Label(label="MB"))
box.append(Gtk.Label(label="Size:"))
box.append(size_box)
# Type dropdown
types = {"Root (Linux)": "8300", "EFI System": "ef00", "Swap": "8200"}
type_names = list(types.keys())
type_dropdown = Gtk.DropDown.new_from_strings(type_names)
box.append(Gtk.Label(label="Partition Type:"))
box.append(type_dropdown)
def on_create(b):
from ...backend.disk import create_partition
size_mb = int(size_entry.get_text())
type_code = types[type_names[type_dropdown.get_selected()]]
create_partition(self.current_disk_path, size_mb, type_code)
win.close()
self.load_partitions(self.current_disk_path)
btn = Gtk.Button(label="Create", add_css_class="suggested-action")
btn.connect("clicked", on_create)
box.append(btn)
win.present()
def load_partitions(self, disk_device=None):
self.current_disk_path = disk_device