Implement manual partition management (create/delete/mountpoints)
This commit is contained in:
@@ -99,3 +99,42 @@ def mount_partitions(partition_info, mount_root="/mnt"):
|
||||
run_command(["swapon", partition_info["swap"]])
|
||||
|
||||
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])
|
||||
|
||||
@@ -236,6 +236,7 @@ class PartitioningPage(Adw.Bin):
|
||||
# Initially empty until loaded with a specific disk
|
||||
|
||||
def load_partitions(self, disk_device=None):
|
||||
self.current_disk_path = disk_device
|
||||
target_disk = None
|
||||
|
||||
try:
|
||||
@@ -428,46 +429,107 @@ class PartitioningPage(Adw.Bin):
|
||||
|
||||
def show_context_menu(self, widget, x, y):
|
||||
data = widget.part_data
|
||||
self.selected_disk_path = self.current_disk_path # Assumes we store this
|
||||
|
||||
popover = Gtk.Popover()
|
||||
popover.set_parent(widget)
|
||||
|
||||
menu_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
menu_box.set_spacing(0)
|
||||
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.add_css_class("flat")
|
||||
btn.set_halign(Gtk.Align.FILL)
|
||||
if destructive:
|
||||
btn.add_css_class("destructive-action")
|
||||
|
||||
if icon_name:
|
||||
content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
content.set_spacing(12)
|
||||
content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
|
||||
icon = Gtk.Image.new_from_icon_name(icon_name)
|
||||
lbl = Gtk.Label(label=label)
|
||||
content.append(icon)
|
||||
content.append(lbl)
|
||||
btn.set_child(content)
|
||||
|
||||
btn.connect("clicked", lambda b: [popover.popdown(), callback()])
|
||||
menu_box.append(btn)
|
||||
return btn
|
||||
|
||||
if data.get("type") == "partition":
|
||||
add_menu_item("Select Mount Point", "folder-open-symbolic")
|
||||
add_menu_item("Format", "drive-harddisk-symbolic")
|
||||
add_menu_item("Resize", "object-resize-symbolic")
|
||||
add_menu_item("Select Mount Point", "folder-open-symbolic",
|
||||
lambda: self.select_mount_point(data))
|
||||
separator = Gtk.Separator()
|
||||
menu_box.append(separator)
|
||||
btn_del = add_menu_item("Delete", "user-trash-symbolic", destructive=True)
|
||||
btn_del.add_css_class("error")
|
||||
add_menu_item("Delete", "user-trash-symbolic",
|
||||
lambda: self.delete_part(data), destructive=True)
|
||||
|
||||
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()
|
||||
|
||||
def get_config(self):
|
||||
return {"partitions": self.partitions}
|
||||
def select_mount_point(self, data):
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user