Added a PArtition() class that supports mounting and formatting. Also reworked the installation flow a bit to be a bit more clear while sacrificing some automation. Maybe I'll revert some changes and 'automatically' do certain things, but for now this shouldn't impact anyone to much
This commit is contained in:
parent
f0bc987e1b
commit
94f8d90121
|
|
@ -5,17 +5,53 @@ from helpers.disk import *
|
|||
from helpers.general import *
|
||||
from helpers.user_interaction import *
|
||||
|
||||
class HardDrive():
|
||||
def __init__(self, full_path:str, *args, **kwargs):
|
||||
if not stat.S_ISBLK(os.stat(full_path).st_mode):
|
||||
raise DiskError(f'Selected disk "{full_path}" is not a block device.')
|
||||
|
||||
class installer():
|
||||
def __init__(self, partition, *, profile=None, hostname='ArchInstalled'):
|
||||
class Installer():
|
||||
def __init__(self, partition, *, profile=None, mountpoint='/mnt', hostname='ArchInstalled'):
|
||||
self.profile = profile
|
||||
self.hostname = hostname
|
||||
self.mountpoint = mountpoint
|
||||
|
||||
self.partition = partition
|
||||
|
||||
def __enter__(self, *args, **kwargs):
|
||||
self.partition.mount(self.mountpoint)
|
||||
return self
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
# b''.join(sys_command(f'sync')) # No need to, since the underlaying fs() object will call sync.
|
||||
# TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
|
||||
if len(args) >= 2 and args[1]:
|
||||
raise args[1]
|
||||
return True
|
||||
|
||||
def minimal_installation(self):
|
||||
pass
|
||||
if (sync_mirrors := sys_command('/usr/bin/pacman -Syy')).exit_code == 0:
|
||||
if (pacstrap := sys_command(f'/usr/bin/pacstrap {self.mountpoint} base base-devel linux linux-firmware btrfs-progs efibootmgr nano wpa_supplicant dialog')).exit_code == 0:
|
||||
return True
|
||||
else:
|
||||
log(f'Could not strap in base: {pacstrap.exit_code}')
|
||||
else:
|
||||
log(f'Could not sync mirrors: {sync_mirrors.exit_code}')
|
||||
|
||||
def add_bootloader(self, partition):
|
||||
os.makedirs(f'{self.mountpoint}/boot', exist_ok=True)
|
||||
partition.mount(f'{self.mountpoint}/boot')
|
||||
o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.mountpoint} bootctl --no-variables --path=/boot install'))
|
||||
|
||||
with open('/mnt/boot/loader/loader.conf', 'w') as loader:
|
||||
loader.write('default arch\n')
|
||||
loader.write('timeout 5\n')
|
||||
|
||||
## For some reason, blkid and /dev/disk/by-uuid are not getting along well.
|
||||
## And blkid is wrong in terms of LUKS.
|
||||
#UUID = sys_command('blkid -s PARTUUID -o value {drive}{partition_2}'.format(**args)).decode('UTF-8').strip()
|
||||
with open('/mnt/boot/loader/entries/arch.conf', 'w') as entry:
|
||||
entry.write('title Arch Linux\n')
|
||||
entry.write('linux /vmlinuz-linux\n')
|
||||
entry.write('initrd /initramfs-linux.img\n')
|
||||
## blkid doesn't trigger on loopback devices really well,
|
||||
## so we'll use the old manual method until we get that sorted out.
|
||||
# UUID = simple_command(f"blkid -s PARTUUID -o value /dev/{os.path.basename(args['drive'])}{args['partitions']['2']}").decode('UTF-8').strip()
|
||||
# entry.write('options root=PARTUUID={UUID} rw intel_pstate=no_hwp\n'.format(UUID=UUID))
|
||||
UUID = b''.join(sys_command(f"ls -l /dev/disk/by-uuid/ | grep {os.path.basename(partition['path'])} | awk '{{print $9}}'")).decode('UTF-8').strip()
|
||||
entry.write(f'options cryptdevice=UUID={UUID}:luksdev root=/dev/mapper/luksdev rw intel_pstate=no_hwp\n')
|
||||
|
|
@ -2,9 +2,15 @@ import glob, re, os, json
|
|||
from collections import OrderedDict
|
||||
from helpers.general import sys_command
|
||||
from exceptions import *
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import os
|
||||
|
||||
ROOT_DIR_PATTERN = re.compile('^.*?/devices')
|
||||
GPT = 0b00000001
|
||||
libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
|
||||
libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_ulong, ctypes.c_char_p)
|
||||
|
||||
|
||||
class BlockDevice():
|
||||
def __init__(self, path, info):
|
||||
|
|
@ -32,6 +38,9 @@ class BlockDevice():
|
|||
if not 'pkname' in self.info: raise DiskError(f'A crypt device ({self.path}) without a parent kernel device name.')
|
||||
return f"/dev/{self.info['pkname']}"
|
||||
|
||||
# if not stat.S_ISBLK(os.stat(full_path).st_mode):
|
||||
# raise DiskError(f'Selected disk "{full_path}" is not a block device.')
|
||||
|
||||
@property
|
||||
def partitions(self):
|
||||
o = b''.join(sys_command(f'partprobe {self.path}'))
|
||||
|
|
@ -50,11 +59,7 @@ class BlockDevice():
|
|||
root_path = f"/dev/{r['blockdevices'][0]['name']}"
|
||||
for part in r['blockdevices'][0]['children']:
|
||||
part_id = part['name'][len(os.path.basename(self.path)):]
|
||||
parts[part_id] = {
|
||||
'size' : part['size'],
|
||||
'id' : part_id,
|
||||
'path' : root_path + part_id
|
||||
}
|
||||
parts[part_id] = Partition(root_path + part_id, part_id=part_id, size=part['size'])
|
||||
|
||||
return {k: parts[k] for k in sorted(parts)}
|
||||
|
||||
|
|
@ -71,6 +76,45 @@ class BlockDevice():
|
|||
raise KeyError(f'{self} does not contain information: "{key}"')
|
||||
return self.info[key]
|
||||
|
||||
class Partition():
|
||||
def __init__(self, path, part_id=None, size=-1):
|
||||
if not part_id: part_id = os.path.basename(path)
|
||||
self.path = path
|
||||
self.part_id = part_id
|
||||
self.mountpoint = None
|
||||
self.filesystem = None # TODO: Autodetect if we're reusing a partition
|
||||
self.size = size # TODO: Refresh?
|
||||
|
||||
def __repr__(self, *args, **kwargs):
|
||||
return f'Partition({self.path})'
|
||||
|
||||
def format(self, filesystem):
|
||||
if filesystem == 'btrfs':
|
||||
o = b''.join(sys_command(f'/usr/bin/mkfs.btrfs -f {self.path}'))
|
||||
if not b'UUID' in o:
|
||||
return False
|
||||
self.filesystem = 'btrfs'
|
||||
elif filesystem == 'fat32':
|
||||
o = b''.join(sys_command(f'/usr/bin/mkfs.vfat -F32 {self.path}'))
|
||||
if (b'mkfs.fat' not in o and b'mkfs.vfat' not in o) or b'command not found' in o:
|
||||
return None
|
||||
return True
|
||||
else:
|
||||
raise DiskError(f'Fileformat {filesystem} is not yet implemented.')
|
||||
return True
|
||||
|
||||
def mount(self, target, fs=None, options=''):
|
||||
if not fs:
|
||||
if not self.filesystem: raise DiskError('Need to format (or define) the filesystem before mounting.')
|
||||
fs = self.filesystem
|
||||
# TODO: Move this to the BlockDevice or something.
|
||||
ret = libc.mount(self.path.encode(), target.encode(), fs.encode(), 0, options.encode())
|
||||
if ret < 0:
|
||||
errno = ctypes.get_errno()
|
||||
raise OSError(errno, f"Error mounting {self.path} ({fs}) on {target} with options '{options}': {os.strerror(errno)}")
|
||||
self.mountpoint = target
|
||||
|
||||
|
||||
class luks2():
|
||||
def __init__(self, filesystem):
|
||||
self.filesystem = filesystem
|
||||
|
|
@ -80,7 +124,7 @@ class luks2():
|
|||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
# TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
|
||||
if len(args):
|
||||
if len(args) >= 2 and args[1]:
|
||||
raise args[1]
|
||||
return True
|
||||
|
||||
|
|
@ -91,13 +135,13 @@ class luks2():
|
|||
with open(key_file, 'wb') as fh:
|
||||
fh.write(password)
|
||||
|
||||
o = b''.join(sys_command(f'/usr/bin/cryptsetup -q -v --type luks2 --pbkdf argon2i --hash {hash_type} --key-size {key_size} --iter-time {iter_time} --key-file {os.path.abspath(key_file)} --use-urandom luksFormat {partition["path"]}'))
|
||||
o = b''.join(sys_command(f'/usr/bin/cryptsetup -q -v --type luks2 --pbkdf argon2i --hash {hash_type} --key-size {key_size} --iter-time {iter_time} --key-file {os.path.abspath(key_file)} --use-urandom luksFormat {partition.path}'))
|
||||
if not b'Command successful.' in o:
|
||||
raise DiskError(f'Could not encrypt volume "{partition["path"]}": {o}')
|
||||
raise DiskError(f'Could not encrypt volume "{partition.path}": {o}')
|
||||
|
||||
return key_file
|
||||
|
||||
def mount(self, partition, mountpoint, key_file):
|
||||
def unlock(self, partition, mountpoint, key_file):
|
||||
"""
|
||||
Mounts a lukts2 compatible partition to a certain mountpoint.
|
||||
Keyfile must be specified as there's no way to interact with the pw-prompt atm.
|
||||
|
|
@ -106,8 +150,9 @@ class luks2():
|
|||
:type mountpoint: str
|
||||
"""
|
||||
if '/' in mountpoint: os.path.basename(mountpoint) # TODO: Raise exception instead?
|
||||
sys_command(f'/usr/bin/cryptsetup open {partition["path"]} {mountpoint} --key-file {os.path.abspath(key_file)} --type luks2')
|
||||
return os.path.islink(f'/dev/mapper/{mountpoint}')
|
||||
sys_command(f'/usr/bin/cryptsetup open {partition.path} {mountpoint} --key-file {os.path.abspath(key_file)} --type luks2')
|
||||
if os.path.islink(f'/dev/mapper/{mountpoint}'):
|
||||
return Partition(f'/dev/mapper/{mountpoint}')
|
||||
|
||||
def close(self, mountpoint):
|
||||
sys_command(f'cryptsetup close /dev/mapper/{mountpoint}')
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class sys_command():#Thread):
|
|||
def __init__(self, cmd, callback=None, start_callback=None, *args, **kwargs):
|
||||
if not 'worker_id' in kwargs: kwargs['worker_id'] = gen_uid()
|
||||
if not 'emulate' in kwargs: kwargs['emulate'] = False
|
||||
if not 'surpress_errors' in kwargs: kwargs['surpress_errors'] = False
|
||||
if kwargs['emulate']:
|
||||
log(f"Starting command '{cmd}' in emulation mode.")
|
||||
self.raw_cmd = cmd
|
||||
|
|
@ -170,11 +171,9 @@ class sys_command():#Thread):
|
|||
if 'ignore_errors' in self.kwargs:
|
||||
self.exit_code = 0
|
||||
|
||||
if self.exit_code != 0:
|
||||
if self.exit_code != 0 and not self.kwargs['surpress_errors']:
|
||||
log(f"'{self.raw_cmd}' did not exit gracefully, exit code {self.exit_code}.", origin='spawn', level=3)
|
||||
log(self.trace_log.decode('UTF-8'), origin='spawn', level=3)
|
||||
#else:
|
||||
#log(f"{self.cmd[0]} exit nicely.", origin='spawn', level=5)
|
||||
|
||||
self.ended = time.time()
|
||||
with open(f'{self.cwd}/trace.log', 'wb') as fh:
|
||||
|
|
|
|||
44
installer.py
44
installer.py
|
|
@ -1,28 +1,40 @@
|
|||
import archinstall, getpass
|
||||
|
||||
archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop')
|
||||
## dd if=/dev/zero of=test.img bs=1G count=4
|
||||
## losetup -fP test.img
|
||||
archinstall.sys_command(f'umount -R /mnt', surpress_errors=True)
|
||||
archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', surpress_errors=True)
|
||||
|
||||
#selected_hdd = archinstall.select_disk(archinstall.all_disks())
|
||||
selected_hdd = archinstall.all_disks()['/dev/loop0']
|
||||
#harddrive = archinstall.select_disk(archinstall.all_disks())
|
||||
harddrive = archinstall.all_disks()['/dev/loop0']
|
||||
disk_password = '1234' # getpass.getpass(prompt='Disk password (won\'t echo): ')
|
||||
|
||||
with archinstall.Filesystem(selected_hdd, archinstall.GPT) as fs:
|
||||
with archinstall.Filesystem(harddrive, archinstall.GPT) as fs:
|
||||
print(f'Formatting {harddrive}')
|
||||
fs.use_entire_disk('luks2')
|
||||
with archinstall.luks2(fs) as crypt:
|
||||
if selected_hdd.partition[1]['size'] == '512M':
|
||||
if harddrive.partition[1].size == '512M':
|
||||
raise OSError('Trying to encrypt the boot partition for petes sake..')
|
||||
|
||||
key_file = crypt.encrypt(selected_hdd.partition[1], password=disk_password, key_size=512, hash_type='sha512', iter_time=10000, key_file='./pwfile')
|
||||
crypt.mount(selected_hdd.partition[1], 'luksloop', key_file)
|
||||
exit(1)
|
||||
with archinstall.installer(root_partition, hostname='testmachine') as installation:
|
||||
if installation.minimal_installation():
|
||||
installation.add_bootloader()
|
||||
print(f'Encrypting {harddrive.partition[1]}')
|
||||
key_file = crypt.encrypt(harddrive.partition[1], password=disk_password, key_size=512, hash_type='sha512', iter_time=10000, key_file='./pwfile')
|
||||
|
||||
installation.add_additional_packages(['nano', 'wget', 'git'])
|
||||
installation.install_profile('desktop')
|
||||
unlocked_device = crypt.unlock(harddrive.partition[1], 'luksloop', key_file)
|
||||
|
||||
print('Formatting partitions.')
|
||||
harddrive.partition[0].format('fat32')
|
||||
unlocked_device.format('btrfs')
|
||||
|
||||
with archinstall.Installer(unlocked_device, hostname='testmachine') as installation:
|
||||
print('Installing minimal installation to disk.')
|
||||
if installation.minimal_installation():
|
||||
print('Adding bootloader.')
|
||||
installation.add_bootloader(harddrive.partition[0])
|
||||
|
||||
installation.user_create('anton', 'test')
|
||||
installation.user_set_pw('root', 'toor')
|
||||
installation.add_additional_packages(['nano', 'wget', 'git'])
|
||||
installation.install_profile('desktop')
|
||||
|
||||
installation.add_AUR_support()
|
||||
installation.user_create('anton', 'test')
|
||||
installation.user_set_pw('root', 'toor')
|
||||
|
||||
installation.add_AUR_support()
|
||||
Loading…
Reference in New Issue