Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 0 additions & 83 deletions deps.md

This file was deleted.

2 changes: 1 addition & 1 deletion src/lufus/writing/install_ventoy.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def install_grub(target_device: str) -> bool:
# Refresh kernel table
subprocess.run(["partprobe", target_device], check=False)
subprocess.run(["udevadm", "settle"], check=False)
os.sync()
subprocess.run(["sync"], check=True)

# Wait for device nodes to be created by udev
efi_part = f"{target_device}{sep}2"
Expand Down
38 changes: 12 additions & 26 deletions src/lufus/writing/windows/flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ def _fix_efi_bootloader(efi_mount):

log.info("EFI bootloader fix: BOOTX64.EFI not found, will attempt to create at %s", boot_dir)
bootx64 = os.path.join(boot_dir, "BOOTX64.EFI")
os.makedirs(boot_dir, exist_ok=True)
run_cmd(["sudo", "mkdir", "-p", boot_dir])
log.info("EFI bootloader fix: created directory %s", boot_dir)

src = _find_path_case_insensitive(efi_mount, "EFI", "Microsoft", "Boot", "bootmgfw.efi")
if src:
shutil.copy2(src, bootx64)
run_cmd(["sudo", "cp", src, bootx64])
log.info("EFI bootloader fix: copied %s -> %s", src, bootx64)
return

Expand Down Expand Up @@ -155,7 +155,7 @@ def _copy_file(src: str, dst: str) -> str:
def _find_ntfs_tool(status_cb=None) -> str | None:
"""Find mkfs.ntfs/mkntfs, installing ntfs-3g if needed. Returns command name or None."""
for candidate in ["mkfs.ntfs", "mkntfs"]:
if shutil.which(candidate):
if subprocess.run(["which", candidate], capture_output=True).returncode == 0:
return candidate

if status_cb:
Comment on lines +158 to 161

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Replacing shutil.which with subprocess.run(["which", …]) reduces portability and adds overhead.

shutil.which is cross‑platform and doesn’t rely on an external which binary being in PATH, while subprocess.run(["which", …]) does and also adds process‑spawn overhead inside the loop.

Unless there’s a strong need for which’s exact semantics, consider reverting to shutil.which here and reserve run_cmd/sudo for actually invoking the tools or package managers once found.

Suggested implementation:

def _find_ntfs_tool(status_cb=None) -> str | None:
    """Find mkfs.ntfs/mkntfs, installing ntfs-3g if needed. Returns command name or None."""
    for candidate in ["mkfs.ntfs", "mkntfs"]:
        if shutil.which(candidate):
            return candidate
  1. Ensure import shutil is present at the top of src/lufus/writing/windows/flash.py. If subprocess is no longer used anywhere in this file, you can safely remove import subprocess to avoid an unused import.
  2. No changes are required to run_cmd/sudo usage; keep those for actually invoking the tools or package managers once a candidate is found.

Expand All @@ -167,21 +167,21 @@ def _find_ntfs_tool(status_cb=None) -> str | None:
["zypper", "install", "-y", "ntfs-3g"],
]
for pm_cmd in pkg_managers:
if shutil.which(pm_cmd[0]):
if subprocess.run(["which", pm_cmd[0]], capture_output=True).returncode == 0:
run_cmd(["sudo"] + pm_cmd)
break # stop after the first working package manager

# Re-check after installation attempt
for candidate in ["mkfs.ntfs", "mkntfs"]:
if shutil.which(candidate):
if subprocess.run(["which", candidate], capture_output=True).returncode == 0:
return candidate # installation succeeded

return None


def _ensure_wimlib(status_cb=None) -> None:
"""Install wimlib-imagex if not present. Raises FileNotFoundError if it can't be found after install."""
if shutil.which("wimlib-imagex"):
if subprocess.run(["which", "wimlib-imagex"], capture_output=True).returncode == 0:
return
if status_cb:
status_cb("wimlib-imagex not found, attempting to install...")
Expand All @@ -192,10 +192,10 @@ def _ensure_wimlib(status_cb=None) -> None:
["zypper", "install", "-y", "wimtools"],
]
for pm_cmd in pkg_managers:
if shutil.which(pm_cmd[0]):
if subprocess.run(["which", pm_cmd[0]], capture_output=True).returncode == 0:
run_cmd(["sudo"] + pm_cmd)
break
if not shutil.which("wimlib-imagex"):
if subprocess.run(["which", "wimlib-imagex"], capture_output=True).returncode != 0:
raise FileNotFoundError(
"wimlib-imagex not found. Install manually: sudo pacman -S wimlib / sudo apt install wimtools"
)
Expand Down Expand Up @@ -271,34 +271,20 @@ def _copy_efi_boot_files(iso_mount, mount_efi, _status):
if efi_src:
efi_items = os.listdir(efi_src)
_status(f"Found EFI/ with {len(efi_items)} items: {efi_items}")
for item in efi_items:
src_path = os.path.join(efi_src, item)
dst_path = os.path.join(mount_efi, "EFI", item)
if os.path.isdir(src_path):
shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
else:
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
shutil.copy2(src_path, dst_path)
run_cmd(["sudo", "cp", "-r"] + [os.path.join(efi_src, i) for i in efi_items] + [mount_efi])
_status("Copied EFI/ tree to EFI partition")
else:
_status("WARNING: No EFI directory found in ISO - drive may not be UEFI bootable")

boot_src = _find_path_case_insensitive(iso_mount, "boot")
if boot_src:
for item in os.listdir(boot_src):
src_path = os.path.join(boot_src, item)
dst_path = os.path.join(mount_efi, "boot", item)
if os.path.isdir(src_path):
shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
else:
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
shutil.copy2(src_path, dst_path)
run_cmd(["sudo", "cp", "-r"] + [os.path.join(boot_src, i) for i in os.listdir(boot_src)] + [mount_efi])
Comment on lines +274 to +281

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The new cp -r … mount_efi calls change the target layout compared to the previous EFI/ and boot/ subdirectories.

With this change, efi_src and boot_src contents end up directly under mount_efi instead of under mount_efi/EFI and mount_efi/boot, which changes the on-disk layout and can break UEFI boot.

If the original directory structure is required, the destination should include the subdirectory (e.g. ... + [os.path.join(mount_efi, "EFI")] and similarly for boot), and those directories should be ensured to exist or created by cp as appropriate.

_status("Copied boot/ tree to EFI partition")

for fname in ["bootmgr", "bootmgr.efi"]:
src = _find_path_case_insensitive(iso_mount, fname)
if src:
shutil.copy2(src, os.path.join(mount_efi, fname))
run_cmd(["sudo", "cp", src, f"{mount_efi}/{fname}"])
_status(f"Copied {fname} to EFI partition root")

_fix_efi_bootloader(mount_efi)
Expand Down Expand Up @@ -426,7 +412,7 @@ def _status(msg):

# Step 7: Sync
_status("Syncing all writes to disk...")
os.sync()
run_cmd(["sudo", "sync"])
_emit(97)
_status("Sync complete")

Expand Down
10 changes: 4 additions & 6 deletions src/lufus/writing/windows/tweaks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# Not tested, at least by me, I don't remember when that was added, and never used it nor really know what it's supposed to do.
"""Windows installation customization functions.

These modify Windows installation media (boot.wim, autounattend.xml)
Expand All @@ -10,7 +9,6 @@
import re
import subprocess
import os
import shutil
from lufus.utils import get_mount_and_drive
from lufus import state
from lufus.lufus_logging import get_logger
Expand Down Expand Up @@ -56,7 +54,7 @@ def win_hardware_bypass():
cmd_string = "\n".join(commands) + "\n"
log.info("win_hardware_bypass: injecting registry keys into boot.wim at %s...", mount)
try:
os.makedirs("/media/tempwinmnt", exist_ok=True)
subprocess.run(["mkdir", "/media/tempwinmnt"], check=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Using mkdir without -p loses the previous idempotent behavior of os.makedirs(..., exist_ok=True).

With the previous os.makedirs call, rerunning this code when /media/tempwinmnt already existed was fine. With mkdir and check=True, an existing directory now causes a CalledProcessError that fails the whole operation. To keep retries/idempotency, either pass -p to mkdir or special‑case the “already exists” error.

subprocess.run(["wimmountrw", f"{mount}/sources/boot.wim", "2", "/media/tempwinmnt"], check=True)
subprocess.run(
["chntpw", "e", "/media/tempwinmnt/Windows/System32/config/SYSTEM"],
Expand All @@ -66,7 +64,7 @@ def win_hardware_bypass():
check=True,
)
subprocess.run(["wimunmount", "/media/tempwinmnt", "--commit"], check=True)
shutil.rmtree("/media/tempwinmnt", ignore_errors=True)
subprocess.run(["rm", "-rf", "/media/tempwinmnt"], check=True)
log.info("win_hardware_bypass: registry keys injected successfully.")
Comment on lines 66 to 68

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): rm -rf with check=True changes semantics compared to shutil.rmtree(..., ignore_errors=True).

Previously, cleanup errors were explicitly ignored; now any failure in rm -rf will raise and send control to the CalledProcessError handler. If cleanup is still meant to be best-effort, consider removing check=True here or catching and logging failures from this specific cleanup step instead of letting them affect control flow.

Suggested change
subprocess.run(["wimunmount", "/media/tempwinmnt", "--commit"], check=True)
shutil.rmtree("/media/tempwinmnt", ignore_errors=True)
subprocess.run(["rm", "-rf", "/media/tempwinmnt"], check=True)
log.info("win_hardware_bypass: registry keys injected successfully.")
subprocess.run(["wimunmount", "/media/tempwinmnt", "--commit"], check=True)
try:
subprocess.run(["rm", "-rf", "/media/tempwinmnt"], check=True)
except subprocess.CalledProcessError as cleanup_error:
log.warning(
"win_hardware_bypass: failed to clean up /media/tempwinmnt: %s",
cleanup_error,
)
log.info("win_hardware_bypass: registry keys injected successfully.")

except subprocess.CalledProcessError as e:
log.error("win_hardware_bypass: CalledProcessError: %s", e.stderr)
Expand All @@ -81,7 +79,7 @@ def win_local_acc():
cmd_string = "\n".join(commands) + "\n"
log.info("win_local_acc: bypassing online account requirement at %s...", mount)
try:
os.makedirs("/media/tempwinmnt", exist_ok=True)
subprocess.run(["mkdir", "/media/tempwinmnt"], check=True)
subprocess.run(["wimmountrw", f"{mount}/sources/boot.wim", "2", "/media/tempwinmnt"], check=True)
subprocess.run(
["chntpw", "e", "/media/tempwinmnt/Windows/System32/config/SOFTWARE"],
Expand All @@ -91,7 +89,7 @@ def win_local_acc():
check=True,
)
subprocess.run(["wimunmount", "/media/tempwinmnt", "--commit"], check=True)
shutil.rmtree("/media/tempwinmnt", ignore_errors=True)
subprocess.run(["rm", "-rf", "/media/tempwinmnt"], check=True)
log.info("win_local_acc: online account bypass applied successfully.")
except subprocess.CalledProcessError as e:
log.error("win_local_acc: CalledProcessError: %s", e.stderr)
Expand Down
Loading