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.
multibootusb/scripts/persistence.py

230 lines
8.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Name: persistence.py
# Purpose: Module to deal with persistence of a selected distro.
# 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
from functools import partial
import os
import platform
import stat
import subprocess
import tarfile
from . import iso
from . import gen
from . import config
def max_disk_persistence(usb_disk):
"""
Detect max persistence value for filesystem on usb_disk
:param usb_disk: Disk filesystem to check
:return: Max persistence supported (bytes)
"""
assert usb_disk is not None
fat_max_size = (4096 * 1024 * 1024)
usb_details = config.usb_details
config.usb_uuid = usb_details['uuid']
config.usb_label = usb_details['label']
size_free = usb_details['size_free']
if usb_details['file_system'] in ['vfat', 'FAT32']:
_max_size = min(fat_max_size, size_free)
else:
_max_size = size_free
return _max_size
def persistence_distro(distro, iso_link):
"""
Function to detect if distro can have persistence option.
:param distro: Detected distro name.
:return: Distro name as string or None otherwise.
"""
assert distro is not None
assert iso_link is not None
# iso_size = iso.iso_size(iso_link)
if distro in ["ubuntu", "debian", "debian-install", "fedora", "centos"]:
gen.log("Persistence option is available.")
return distro
else:
return None
def create_persistence_using_mkfs(persistence_fname, persistence_size):
persistence_path = os.path.join(config.usb_mount, 'multibootusb',
iso.iso_basename(config.image_path),
persistence_fname)
volume_name = os.path.basename(persistence_fname)
if platform.system() == 'Windows':
tools_dir = os.path.join('data', 'tools')
mke2fs_relative_path = os.path.join(tools_dir, 'mkfs', 'mke2fs.exe')
mkfs = gen.resource_path(mke2fs_relative_path)
dd_relative_path = os.path.join(tools_dir, 'dd', 'dd.exe')
dd = gen.resource_path(dd_relative_path)
persistence_mkfs_cmd = 'echo y|' + mkfs + ' -b 1024 ' + \
'-L ' + volume_name + ' ' + \
persistence_path
else:
mkfs = 'mkfs.ext3'
dd = 'dd'
persistence_mkfs_cmd = mkfs + ' -F ' + persistence_path
mbytes = persistence_size / 1024 / 1024
persistence_dd_cmd = dd + ' if=/dev/zero ' + \
'of=' + persistence_path + \
' bs=1M count=' + str(int(mbytes))
gen.log('Executing ==>' + persistence_dd_cmd)
config.status_text = 'Creating persistence file...'
if subprocess.call(persistence_dd_cmd, shell=True) == 0:
gen.log("\nSuccessfully created persistence file...\n")
if config.distro != 'fedora':
gen.log('Applying filesystem to persistence file...')
config.status_text = 'Applying filesystem to persistence file. Please wait...'
gen.log('Executing ==> ' + persistence_mkfs_cmd)
config.status_text = 'Applying filesystem to persistence file...'
if subprocess.call(persistence_mkfs_cmd, shell=True) == 0:
gen.log("\nSuccessfully applied filesystem...\n")
def create_persistence_using_resize2fs(persistence_fname, persistence_size):
outdir = os.path.join(config.usb_mount, 'multibootusb',
iso.iso_basename(config.image_path))
persistence_path = os.path.join(outdir, persistence_fname)
tools_dir = os.path.join('data', 'tools')
if platform.system()=='Windows':
_7zip_exe = gen.resource_path(
os.path.join(tools_dir, '7zip', '7z.exe'))
e2fsck_exe = gen.resource_path(os.path.join(tools_dir, 'cygwin', 'e2fsck.exe'))
resize2fs_exe = gen.resource_path(os.path.join(tools_dir, 'cygwin', 'resize2fs.exe'))
else:
_7zip_exe = '7z'
e2fsck_exe = 'e2fsck'
resize2fs_exe = 'resize2fs'
config.status_text = 'Copying persistence file...'
persistence_gz = gen.resource_path(
os.path.join(tools_dir, 'persistence.gz'))
_7zip_cmd_base = [_7zip_exe, 'x', '-o' + outdir]
for more_opts in ( ['-y'], ['-aoa'] ):
_7zip_cmd = _7zip_cmd_base + more_opts + [persistence_gz]
if subprocess.call(_7zip_cmd)==0:
if not os.path.exists(persistence_path):
gen.log("%s has failed to create the persistence file." %
_7zip_cmd)
continue
gen.log("Generated 'persistence' file in '%s'" % outdir)
if persistence_fname != 'persistence':
os.rename(os.path.join(outdir, 'persistence'),
persistence_path)
gen.log("Renamed to '%s'." % persistence_path)
break
gen.log("%s has failed with non-zero exit status." % _7zip_cmd)
else:
gen.log("Couldn't generate persistence file '%s' by any means." %
persistence_path)
return
current_size = os.stat(persistence_path)[stat.ST_SIZE]
if current_size < persistence_size:
msg = 'Extending the persistence file by %.1f MB...' % \
((persistence_size - current_size) / float(1024*1024))
config.status_text = msg
gen.log(msg)
with open(persistence_path, 'ab') as f:
_1M_block = b'\0' * (1024*1024)
bytes_left = persistence_size - current_size
while 0<bytes_left:
block_size = min(1024*1024, bytes_left)
f.write(_1M_block[:block_size])
bytes_left -= block_size
config.status_text = 'Resizing the persistence file...'
fsck_cmd = [e2fsck_exe, '-y', '-f', persistence_path]
if subprocess.call(fsck_cmd)==0:
gen.log("Checking the persistence file.")
resize_cmd = [resize2fs_exe, persistence_path]
if subprocess.call(resize_cmd)==0:
gen.log("Successfully resized the persistence file.")
creator_dict = {
'ubuntu' : (create_persistence_using_mkfs,
lambda C: ('casper-rw',)),
'debian' : (create_persistence_using_resize2fs,
lambda C: ('persistence',)),
'debian-install' : (
create_persistence_using_resize2fs,
lambda C: ('persistence',)),
'fedora' : (
create_persistence_using_mkfs,
lambda C: (os.path.join(
'LiveOS', 'overlay-%s-%s' % (C.usb_label, C.usb_uuid)),)),
'centos' : (
create_persistence_using_mkfs,
lambda C: (os.path.join(
'LiveOS', 'overlay-%s-%s' % (C.usb_label, C.usb_uuid)),)),
}
def detect_missing_tools(distro):
tools_dir = os.path.join('data', 'tools')
if platform.system() == 'Windows':
_7zip_exe = gen.resource_path(
os.path.join(tools_dir, '7zip', '7z.exe'))
e2fsck_exe = gen.resource_path(os.path.join(tools_dir, 'cygwin', 'e2fsck.exe'))
resize2fs_exe = gen.resource_path(os.path.join(tools_dir, 'cygwin', 'resize2fs.exe'))
else:
_7zip_exe = '7z'
e2fsck_exe = 'e2fsck'
resize2fs_exe = 'resize2fs'
if distro not in creator_dict or \
creator_dict[distro][0] is not create_persistence_using_resize2fs:
return None
try:
with open(os.devnull) as devnull:
for tool in [e2fsck_exe, resize2fs_exe]:
p = subprocess.Popen([tool], stdout=devnull, stderr=devnull)
p.communicate()
except FileNotFoundError: # Windows
return "'%s.exe' is not installed or not available for use." % tool
except OSError: # Linux
return "'%s' is not installed or not available for use." % tool
return None
def create_persistence():
x = creator_dict.get(config.distro)
if x is None:
gen.log("Persistence is not supported for '%s'." % config.distro)
return False
creator_func, args_generator = x
args = args_generator(config) + (config.persistence,)
creator_func(*args)
return True
def extract_file(file_path, install_dir):
"""
Function to extract persistence files to distro install directory.
:param file_path: Path to persistence file.
:param install_dir: Path to distro install directory.
:return:
"""
tar = tarfile.open(file_path, "r:bz2")
tar.extractall(install_dir)
tar.close()
def test():
config.image_path = 'c:/Users/shinj/Downloads/' \
'Fedora-Workstation-Live-x86_64-27-1.6.iso'
config.persistence = 1024 * 1024 * 20
config.distro = 'fedora'
config.usb_mount = 'h:\\'
create_persistence()