You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
18 KiB
Python
398 lines
18 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
# Name: syslinux.py
|
|
# Purpose: Module to install syslinux and extlinux on selected USB disk.
|
|
# Authors: Sundar
|
|
# Licence: This file is a part of multibootusb package. You can redistribute it or modify
|
|
# under the terms of GNU General Public License, v.2 or above
|
|
|
|
import os
|
|
import subprocess
|
|
import platform
|
|
from .gen import *
|
|
from . import usb
|
|
from .iso import *
|
|
from . import config
|
|
from . import osdriver
|
|
|
|
extlinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux4")
|
|
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4")
|
|
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4")
|
|
mbr_bin = resource_path(os.path.join("data", "tools", "mbr.bin"))
|
|
win_gdisk = resource_path(os.path.join('data', 'tools', 'gdisk', 'gdisk.exe'))
|
|
# Force Linux to install extlinux on NTFS
|
|
if platform.system() == 'Linux':
|
|
extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs", "NTFS", "ntfs"]
|
|
syslinux_fs = ["vfat", "FAT32"]
|
|
else:
|
|
extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs"]
|
|
syslinux_fs = ["vfat", "ntfs", "FAT32", "NTFS"]
|
|
|
|
|
|
def gpt_part_table(usb_disk):
|
|
"""
|
|
Check if selected USB contain GPT or MBR partition table
|
|
:return: True if GPT else False
|
|
"""
|
|
if platform.system() == "Linux":
|
|
_cmd_out = subprocess.check_output("parted " + usb_disk[:-1] + " print", shell=True)
|
|
if b'msdos' in _cmd_out:
|
|
return False
|
|
elif b'gpt' in _cmd_out:
|
|
return True
|
|
elif platform.system() == 'Windows':
|
|
if config.usb_gpt is True:
|
|
return True
|
|
elif config.usb_gpt is False:
|
|
return False
|
|
|
|
|
|
def get_mbr_bin_path(usb_disk):
|
|
"""
|
|
Check if partition table type is mbr or gpr using parted command under Linux
|
|
:param usb_disk: path to whole USB disk '/dev/sdb'
|
|
:return: Path to mbr.bin for use
|
|
"""
|
|
if config.usb_gpt is False:
|
|
log('Using mbr.bin msdos mbr install.')
|
|
return resource_path(os.path.join("data", "tools", "mbr.bin"))
|
|
elif config.usb_gpt is True:
|
|
log('Using gptmbr.bin for mbr install.')
|
|
return resource_path(os.path.join("data", "tools", "gptmbr.bin"))
|
|
|
|
return False
|
|
|
|
|
|
def set_boot_flag(usb_disk):
|
|
if platform.system() == "Linux":
|
|
log("\nChecking boot flag on " + usb_disk[:-1], '\n')
|
|
cmd_out = subprocess.check_output("parted -m -s " + usb_disk[:-1] + " print", shell=True)
|
|
if gpt_part_table(usb_disk) is False:
|
|
if b'boot' in cmd_out:
|
|
log("\nDisk " + usb_disk[:-1] + " already has boot flag.\n")
|
|
return True
|
|
else:
|
|
log("\nExecuting ==> parted " + usb_disk[:-1] + " set 1 boot on", '\n')
|
|
if subprocess.call("parted " + usb_disk[:-1] + " set 1 boot on", shell=True) == 0:
|
|
log("\nBoot flag set to bootable " + usb_disk[:-1], '\n')
|
|
return True
|
|
else:
|
|
log("\nUnable to set boot flag on " + usb_disk[:-1], '\n')
|
|
return False
|
|
elif gpt_part_table(usb_disk) is True:
|
|
if b'legacy_boot' in cmd_out:
|
|
log("\nGPT Disk " + usb_disk[:-1] + " already has legacy_boot flag.\n")
|
|
return True
|
|
else:
|
|
log("\nExecuting ==> parted " + usb_disk[:-1] + " set 1 legacy_boot on", '\n')
|
|
if subprocess.call("parted " + usb_disk[:-1] + " set 1 legacy_boot on", shell=True) == 0:
|
|
log("\nBoot flag set to legacy_boot " + usb_disk[:-1], '\n')
|
|
return True
|
|
else:
|
|
log("\nUnable to set legacy_boot flag on " + usb_disk[:-1], '\n')
|
|
return False
|
|
|
|
def linux_install_default_bootsector(usb_disk, mbr_install_cmd):
|
|
|
|
with usb.UnmountedContext(usb_disk, config.update_usb_mount):
|
|
syslinux_cmd = [syslinux_path, '-i', '-d', 'multibootusb', usb_disk]
|
|
if os.access(syslinux_path, os.X_OK) is False:
|
|
subprocess.call('chmod +x ' + syslinux_path, shell=True)
|
|
log("\nExecuting ==> %s\n" % syslinux_cmd)
|
|
config.status_text = 'Installing default syslinux version 4...'
|
|
if subprocess.call(syslinux_cmd) == 0:
|
|
|
|
# On my system, it takes hours long to complete a single check
|
|
# So not included as of now
|
|
# usb.repair_vfat_filesystem(usb_disk)
|
|
|
|
log("\nDefault syslinux install is success...\n")
|
|
config.status_text = 'Default syslinux successfully installed...'
|
|
log('\nExecuting ==> ' + mbr_install_cmd)
|
|
if subprocess.call(mbr_install_cmd, shell=True) == 0:
|
|
config.status_text = 'mbr install is success...'
|
|
log("\nmbr install is success...\n")
|
|
if set_boot_flag(usb_disk) is True:
|
|
return True
|
|
else:
|
|
log("\nFailed to install default syslinux...\n")
|
|
config.status_text = 'Failed to install default syslinux...'
|
|
return False
|
|
return None
|
|
|
|
|
|
def create_syslinux_bs(usb_disk, usb_mount):
|
|
osdriver.run_dd(osdriver.physical_disk(usb_disk),
|
|
os.path.join(usb_mount, 'multibootusb', 'syslinux.bin'),
|
|
512, 1)
|
|
|
|
def syslinux_default(usb_disk):
|
|
"""
|
|
Install Syslinux of a selected drive
|
|
:param usb_disk: '/dev/sdx' on linux and 'E:' on Windows
|
|
:version: Default version is 4. Change it if you wish. But necessary files needs to be copied accordingly
|
|
:return: Bootable USB disk :-)
|
|
"""
|
|
usb_details = usb.details(usb_disk)
|
|
usb_fs = usb_details['file_system']
|
|
usb_mount = usb_details['mount_point']
|
|
mbr_bin = get_mbr_bin_path(usb_disk)
|
|
if platform.system() == 'Linux':
|
|
mbr_install_cmd = 'dd bs=440 count=1 conv=notrunc if=' + mbr_bin \
|
|
+ ' of=' + usb_disk[:-1]
|
|
else:
|
|
win_usb_disk_no = get_physical_disk_number(usb_disk)
|
|
_windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe"))
|
|
_input = "if=" + mbr_bin
|
|
_output = 'of=\\\.\\physicaldrive' + str(win_usb_disk_no)
|
|
mbr_install_cmd = _windd + ' ' + _input + ' ' + _output + ' count=1'
|
|
|
|
|
|
if usb_fs in extlinux_fs:
|
|
extlinu_cmd = extlinux_path + ' --install ' + \
|
|
os.path.join(usb_mount, 'multibootusb')
|
|
if os.access(extlinux_path, os.X_OK) is False:
|
|
subprocess.call('chmod +x ' + extlinux_path, shell=True)
|
|
log("\nExecuting ==> " + extlinu_cmd)
|
|
config.status_text = 'Installing default extlinux version 4...'
|
|
if subprocess.call(extlinu_cmd, shell=True) == 0:
|
|
log("\nDefault Extlinux install is success...\n")
|
|
config.status_text = 'Default extlinux install is success...'
|
|
config.status_text = 'Installing mbr...'
|
|
log('\nExecuting ==> ' + mbr_install_cmd)
|
|
if subprocess.call(mbr_install_cmd, shell=True) == 0:
|
|
config.status_text = 'mbr install is successful...'
|
|
log("\nmbr install is success...\n")
|
|
if set_boot_flag(usb_disk) is True:
|
|
create_syslinux_bs(usb_disk, usb_mount)
|
|
return True
|
|
|
|
elif usb_fs in syslinux_fs:
|
|
|
|
if platform.system() == "Linux":
|
|
r = linux_install_default_bootsector(usb_disk, mbr_install_cmd)
|
|
if r:
|
|
create_syslinux_bs(usb_disk, usb_mount)
|
|
return r
|
|
elif platform.system() == "Windows":
|
|
syslinux = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4.exe"))
|
|
config.status_text = 'Installing default syslinux version 4...'
|
|
# syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk
|
|
if config.usb_gpt is False:
|
|
syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk
|
|
else:
|
|
syslinux_cmd = syslinux + ' -af -d multibootusb ' + usb_disk
|
|
log('Executing ==> ' + syslinux_cmd)
|
|
'''
|
|
if gpt_part_table(config.usb_disk) is False:
|
|
syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk
|
|
else:
|
|
syslinux_cmd = syslinux + ' -af -d multibootusb ' + usb_disk
|
|
'''
|
|
if subprocess.call(syslinux_cmd, shell=True) == 0:
|
|
config.status_text = 'Default syslinux successfully installed...'
|
|
log("\nDefault syslinux install is success...\n")
|
|
# We will need to flash gptmbr.bin only for GPT disk. As of version 8.9.0 this corrupts the gpt disk.
|
|
# Therefore not included for BIOS booting. GPT disk may work on UEFI system.
|
|
# if gpt_part_table(config.usb_disk) is True:
|
|
'''
|
|
if config.usb_gpt is False:
|
|
log('\nExecuting ==> ' + mbr_install_cmd)
|
|
#
|
|
# Updating mbr using dd results in catastrophy.
|
|
# Windows will lose track of the filesystem and
|
|
# the target volume will go away.
|
|
# Never enable this code without proper work around!
|
|
#
|
|
if subprocess.call(mbr_install_cmd, shell=True) == 0:
|
|
log("\nmbr install is success...\n")
|
|
return True
|
|
else:
|
|
log('Disk uses GPT and mbr install is not required...')
|
|
'''
|
|
create_syslinux_bs(usb_disk, usb_mount)
|
|
return True
|
|
|
|
else:
|
|
log("\nFailed to install default syslinux...\n")
|
|
config.status_text = 'Failed to install default syslinux...'
|
|
return False
|
|
|
|
|
|
def build_distro_bootsector(usb_disk, options,
|
|
distro_syslinux_install_dir,
|
|
distro_sys_install_bs):
|
|
with usb.UnmountedContext(config.usb_disk, config.update_usb_mount):
|
|
tmp_bs = build_distro_bootsector_impl(
|
|
usb_disk, options, distro_syslinux_install_dir)
|
|
if tmp_bs:
|
|
shutil.copy(tmp_bs, distro_sys_install_bs)
|
|
|
|
|
|
def build_distro_bootsector_impl(usb_disk, options,
|
|
distro_syslinux_install_dir):
|
|
syslinux_path = os.path.join(
|
|
multibootusb_host_dir(), "syslinux", "bin", "syslinux") \
|
|
+ config.syslinux_version
|
|
|
|
if os.access(syslinux_path, os.X_OK) is False:
|
|
subprocess.call('chmod +x ' + syslinux_path, shell=True)
|
|
sys_cmd = [syslinux_path] + options + [
|
|
distro_syslinux_install_dir, usb_disk]
|
|
log("Executing ==> %s" % sys_cmd)
|
|
if subprocess.call(sys_cmd) == 0:
|
|
config.status_text = \
|
|
'Syslinux install on distro directory is successful...'
|
|
log("\nSyslinux install on distro directory is successful...\n")
|
|
|
|
# On my system, it takes hours long to complete a single check
|
|
# So not included as of now
|
|
# usb.repair_vfat_filesystem(usb_disk)
|
|
|
|
tmp_bs_file = '/tmp/mbusb_temp.bs'
|
|
dd_cmd = ['dd', 'if=' + usb_disk, 'of=' + tmp_bs_file, 'count=1']
|
|
log('Executing ==> %s' % dd_cmd + '\n')
|
|
config.status_text = 'Copying boot sector...'
|
|
config.status_text = 'Installing distro specific syslinux...'
|
|
if subprocess.call(dd_cmd) == 0:
|
|
config.status_text = 'Bootsector copy is successful...'
|
|
log("\nBootsector copy is successful...\n")
|
|
else:
|
|
config.status_text = 'Failed to copy boot sector...'
|
|
log("\nFailed to copy boot sector...\n")
|
|
return tmp_bs_file
|
|
else:
|
|
config.status_text = 'Failed to install syslinux on distro directory...'
|
|
log("\nFailed to install syslinux on distro directory...\n")
|
|
return None
|
|
|
|
|
|
def syslinux_distro_dir(usb_disk, iso_link, distro):
|
|
"""
|
|
Install syslinux/extlinux on distro specific isolinux directory.
|
|
:param usb_disk: '/dev/sdx' on linux and 'E:' on Windows
|
|
:param iso_link: Path to ISO file
|
|
:return:
|
|
"""
|
|
usb_details = usb.details(usb_disk)
|
|
usb_fs = usb_details['file_system']
|
|
usb_mount = usb_details['mount_point']
|
|
isolinux_bin_dir(iso_link)
|
|
if isolinux_bin_exist(iso_link) is False:
|
|
log('Distro does not use isolinux for booting ISO.')
|
|
else:
|
|
# iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")
|
|
_iso_cfg_ext_dir = iso_cfg_ext_dir()
|
|
isolinux_path = os.path.join(_iso_cfg_ext_dir, isolinux_bin_path(iso_link))
|
|
iso_linux_bin_dir = isolinux_bin_dir(iso_link)
|
|
config.syslinux_version = isolinux_version(isolinux_path)
|
|
if int(config.syslinux_version) < 3:
|
|
log('Distro uses really old isolinux. Installing version 3 instead of 2.')
|
|
config.syslinux_version = '3'
|
|
|
|
if distro in ["generic"]:
|
|
install_dir = usb_mount
|
|
distro_syslinux_install_dir = os.path.join(
|
|
usb_mount, iso_linux_bin_dir.strip("/")).replace(usb_mount, "")
|
|
distro_sys_install_bs = os.path.join(
|
|
install_dir, iso_linux_bin_dir.strip("/"), distro + '.bs')
|
|
else:
|
|
install_dir = os.path.join(usb_mount, "multibootusb",
|
|
iso_basename(iso_link))
|
|
distro_syslinux_install_dir = os.path.join(
|
|
install_dir, iso_linux_bin_dir.strip("/")
|
|
).replace(usb_mount, "")
|
|
distro_sys_install_bs = os.path.join(
|
|
install_dir, iso_linux_bin_dir.strip("/"), distro + '.bs')
|
|
|
|
# log(distro_sys_install_bs)
|
|
# log(distro_syslinux_install_dir)
|
|
|
|
if usb_fs in syslinux_fs:
|
|
options = ['-f']
|
|
if config.syslinux_version != '3':
|
|
options.append('-i')
|
|
if not (distro == "generic" and iso_linux_bin_dir == "/"):
|
|
options.append('-d')
|
|
if platform.system() == "Linux":
|
|
build_distro_bootsector(usb_disk, options,
|
|
distro_syslinux_install_dir,
|
|
distro_sys_install_bs)
|
|
elif platform.system() == "Windows":
|
|
syslinux_path = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin")) + \
|
|
"\syslinux" + config.syslinux_version + ".exe"
|
|
distro_syslinux_install_dir = "/" + distro_syslinux_install_dir.replace("\\", "/")
|
|
distro_sys_install_bs = distro_sys_install_bs.replace("/", "\\")
|
|
sys_cmd = [syslinux_path] + options + \
|
|
[distro_syslinux_install_dir, usb_disk,
|
|
distro_sys_install_bs]
|
|
log("\nExecuting ==> %s" % sys_cmd )
|
|
config.status_text = 'Installing distro specific syslinux...'
|
|
if subprocess.call(sys_cmd) == 0:
|
|
config.status_text = 'Syslinux install on distro directory is successful...'
|
|
log("\nSyslinux install was successful on distro directory...\n")
|
|
else:
|
|
config.status_text = 'Failed to install syslinux on distro directory...'
|
|
log("\nFailed to install syslinux on distro directory...\n")
|
|
elif usb_fs in extlinux_fs:
|
|
if platform.system() == "Linux":
|
|
distro_syslinux_install_dir = os.path.join(install_dir, iso_linux_bin_dir.strip("/"))
|
|
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux") + config.syslinux_version
|
|
ext_cmd = syslinux_path + " --install " + distro_syslinux_install_dir
|
|
dd_cmd = 'dd if=' + usb_disk + ' ' + 'of=' + quote(distro_sys_install_bs) + ' count=1'
|
|
if os.access(syslinux_path, os.X_OK) is False:
|
|
subprocess.call('chmod +x ' + syslinux_path, shell=True)
|
|
log("Executing ==> " + ext_cmd)
|
|
if subprocess.call(ext_cmd, shell=True) == 0:
|
|
log("\nSyslinux install on distro directory is successful...\n")
|
|
log('Executing ==> ' + dd_cmd, '\n')
|
|
if subprocess.call(dd_cmd, shell=True) == 0:
|
|
log("\nBootsector copy is successful...\n")
|
|
else:
|
|
log("\nFailed to install syslinux on distro directory...\n")
|
|
|
|
|
|
|
|
|
|
def replace_grub_binary():
|
|
"""
|
|
This function checks if correct binary is installed on grub and EFI directory.
|
|
If mismatch is found between partition table and binary, replace it correct one.
|
|
Default binaries will work for msdos partition table and therefore need not be replaced.
|
|
:return:
|
|
"""
|
|
|
|
# There used to be msdos/gpt specific files installed and relevant
|
|
# ones were copied to target files. But those specific files were
|
|
# removed by commit ec0f8d95f98f65541c8734623d97f4bd3cbecf0f.
|
|
# Therefore, as of commit d3c7aa7dc72b3d442c854a6a89071d3f5995ec27,
|
|
# code segment below is effectively no-op.
|
|
|
|
if platform.system() not in ['Linux', 'Windows']:
|
|
return
|
|
|
|
ptype = gpt_part_table(config.usb_disk) and '-gpt.' or '-msdos.'
|
|
|
|
for dir_, fname in [
|
|
(os.path.join(config.usb_mount, 'EFI', 'BOOT'), 'bootx64.efi'),
|
|
(os.path.join(config.usb_mount, 'multibootusb', 'grub'), 'grub.img')
|
|
]:
|
|
base, ext = fname.split('.')
|
|
src = os.path.join(dir_, base + ptype + ext)
|
|
dst = os.path.join(dir_, fname)
|
|
if os.path.exists(src):
|
|
try:
|
|
log('Replacing %s with %s...' % (dst, src))
|
|
shutil.copy(src, dst)
|
|
except Exception as e:
|
|
log(e)
|
|
log('Failed to replace %s with %s...' % (dst, src))
|
|
|
|
if __name__ == '__main__':
|
|
if os.geteuid() != 0:
|
|
log('Please running this script with sudo/root/admin privilage.')
|
|
exit(1)
|
|
else:
|
|
syslinux_distro_dir('/dev/sdb1', '../../../DISTROS/2016/debian-live-8.3.0-amd64-lxde-desktop.iso', 'debian')
|
|
syslinux_default('/dev/sdb1')
|