WizardKit/.bin/Scripts/user_data_transfer.py
Alan Mason b180b42ec9 2016-10: Retroactive Updates
# Migration to Python started #
  * PoSH has an extreme slowdown for some systems while it runs an optimization
  ** pass for .NET on the first PoSH script execution.
  ** This is reason enough to move to an alternative.

* New additions:
  * User Data Transfer script
    * Will extract from a WIM or copy from a folder
    * Uses wimlib-imagex for images and FastCopy for folders
    * Removes undesired items after transfer/extraction
  * HWiNFO
  * Missing ODD repair registry patch
  * Q-Dir
  * SW Bundle install script

* ConEmu
  * Moving back to ConEmu for better performance.

* Copy-WizardKit
  * Now uses FastCopy

* functions.py
  * Ported init.ps1 to Python using functions.py (from WinPE) as a base

* Launch.cmd
  * Elevating programs/scripts now done using a temp VBScript file
  * Can run Python scripts (using either the 32 or 64 bit runtime)

* transferred_keys.cmd
  * Expanded searched paths

* Misc
  * Lots of variables and files renamed
  * Lots of hard-coded paths are now in variables
    * Should only be set in scripts in %bin%\Scripts
  * Moved a subset of the Diagnostics launchers to a new 'Extras' folder
    * The launchers moved are those that are less-often used
  * Refactored FindBin code to be more concise
  * Renamed "KitDir" "ClientDir" to indicate that it is on the client's system
  * Removed GeForce Experience launcher as it now requires an account
  * Added link to NVIDIA's driver webpage to download the correct driver
  * Removed AMD's Gaming Evolved launcher
    * This is usually bundled with the GPU driver anyway
  * Switched back to ConEmu
  * Variable and script names are now more descriptive
    * i.e. checklist -> final_checklist, and HH -> %kit_dir%
    * (not happy with %kit_dir%, will probably change again)
2017-11-17 00:53:08 -07:00

248 lines
No EOL
12 KiB
Python

# Wizard Kit: Copy user data to the system over the network
import os
import re
from operator import itemgetter
# Init
os.chdir(os.path.dirname(os.path.realpath(__file__)))
os.system('title Wizard Kit: Data 1')
from functions import *
vars = init_vars()
vars_os = init_vars_os()
vars['LogFile'] = '{LogDir}\\Data 1.log'.format(**vars)
os.makedirs('{LogDir}'.format(**vars), exist_ok=True)
vars['TransferDir'] = '{ClientDir}\\Transfer\\'.format(**vars)
os.makedirs('{TransferDir}'.format(**vars), exist_ok=True)
vars['FastCopy'] = '{BinDir}\\FastCopy\\FastCopy.exe'.format(**vars)
vars['FastCopyArgs'] = [
'/cmd=noexist_only',
'/logfile={LogFile}'.format(**vars),
'/utf8',
'/skip_empty_dir',
'/linkdest',
'/no_ui',
'/auto_close',
'/exclude=\\*.esd;\\*.swm;\\*.wim;\\*.dd;\\*.dd.tgz;\\*.dd.txz;\\*.map;\\*.dmg;\\*.image;$RECYCLE.BIN;$Recycle.Bin;.AppleDB;.AppleDesktop;.AppleDouble;.com.apple.timemachine.supported;.dbfseventsd;.DocumentRevisions-V100*;.DS_Store;.fseventsd;.PKInstallSandboxManager;.Spotlight*;.SymAV*;.symSchedScanLockxz;.TemporaryItems;.Trash*;.vol;.VolumeIcon.icns;desktop.ini;Desktop?DB;Desktop?DF;hiberfil.sys;lost+found;Network?Trash?Folder;pagefile.sys;Recycled;RECYCLER;System?Volume?Information;Temporary?Items;Thumbs.db']
vars['Notepad2'] = '{BinDir}\\Notepad2\\Notepad2-Mod.exe'.format(**vars)
vars['wimlib-imagex'] = '{BinDir}\\wimlib\\x32\\wimlib-imagex.exe'.format(**vars)
if vars_os['Arch'] == 64:
vars['FastCopy'] = vars['FastCopy'].replace('FastCopy.exe', 'FastCopy64.exe')
vars['Notepad2'] = vars['Notepad2'].replace('Notepad2-Mod.exe', 'Notepad2-Mod64.exe')
vars['wimlib-imagex'] = vars['wimlib-imagex'].replace('x32', 'x64')
re_included_root_items = re.compile(r'(^\\((My\s*)|Downloads|Doc(uments?|s?)|WK(-Info|-Transfer|)|Media|Music|Pictures?|Videos?)$|^\\(ProgramData|Recovery|Temp.*|Users)$|(log|txt|rtf|qb\w*|avi|m4a|m4v|mp4|mkv|jpg|png|tiff?)$)', flags=re.IGNORECASE)
re_excluded_root_items = re.compile(r'(^\\boot(mgr|nxt)$|^\\(eula|globdata|install|vc_?red)|.*.sys$|^\\System Volume Information|RECYCLER|\$Recycle\.bin|^\\\$?Win(dows(.old|\.~BT|)$|RE_)|^\\PerfLogs|^\\Program Files|\.(esd|swm|wim|dd|map|dmg|image)$)', flags=re.IGNORECASE)
re_excluded_items = re.compile(r'^(\.(AppleDB|AppleDesktop|AppleDouble|com\.apple\.timemachine\.supported|dbfseventsd|DocumentRevisions-V100.*|DS_Store|fseventsd|PKInstallSandboxManager|Spotlight.*|SymAV.*|symSchedScanLockxz|TemporaryItems|Trash.*|vol|VolumeIcon\.icns)|desktop\.(ini|.*DB|.*DF)|(hiberfil|pagefile)\.sys|lost\+found|Network\.*Trash\.*Folder|Recycle[dr]|System\.*Volume\.*Information|Temporary\.*Items|Thumbs\.db)$', flags=re.IGNORECASE)
def abort():
print_warning('Aborted.', log_file=vars['LogFile'])
exit_script()
def cleanup_transfer():
"""Walk through transfer folder (from the bottom) and remove extraneous items."""
if os.path.exists(vars['TransferDir']):
for root, dirs, files in os.walk(vars['TransferDir'], topdown=False):
for name in dirs:
try:
# Removes empty directories and junction points
os.rmdir(os.path.join(root, name))
except OSError:
pass
for name in files:
if re_excluded_items.search(name):
try:
# Removes files based on exclusion regex
os.remove(os.path.join(root, name))
except OSError:
pass
def exit_script():
umount_backup_shares()
pause("Press Enter to exit...")
subprocess.Popen('"{Notepad2}" "{LogFile}"'.format(**vars))
quit()
def is_valid_image(item):
_valid = item.is_file() and re.search(r'\.wim$', item.name, flags=re.IGNORECASE)
if _valid:
try:
_cmd = [vars['wimlib-imagex'], 'info', '{image}'.format(image=item.path)]
run_program(_cmd)
except subprocess.CalledProcessError:
_valid = False
print_warning('WARNING: Image damaged.', log_file=vars['LogFile'])
time.sleep(2)
return _valid
def transfer_file_based(restore_source):
# Registry
_args = vars['FastCopyArgs'].copy()
if os.path.exists('{source}\\Windows\\System32\\config'.format(source=restore_source.path)):
_args.append('{source}\\Windows\\System32\\config'.format(source=restore_source.path))
if os.path.exists('{source}\\Windows\\System32\\OEM'.format(source=restore_source.path)):
_args.append('{source}\\Windows\\System32\\OEM'.format(source=restore_source.path))
_args.append('/to={TransferDir}\\Windows\\System32\\'.format(**vars))
try:
print_standard('Copying Windows Registry...', log_file=vars['LogFile'])
run_program(vars['FastCopy'], _args, check=True)
except subprocess.CalledProcessError:
print_warning('WARNING: Errors encountered while copying registry', log_file=vars['LogFile'])
# Registry (Windows.old)
_args = vars['FastCopyArgs'].copy()
if os.path.exists('{source}\\Windows.old\\Windows\\System32\\config'.format(source=restore_source.path)):
_args.append('{source}\\Windows.old\\Windows\\System32\\config'.format(source=restore_source.path))
if os.path.exists('{source}\\Windows.old\\Windows\\System32\\OEM'.format(source=restore_source.path)):
_args.append('{source}\\Windows.old\\Windows\\System32\\OEM'.format(source=restore_source.path))
_args.append('/to={TransferDir}\\Windows.old\\Windows\\System32\\'.format(**vars))
try:
print_standard('Copying Windows Registry...', log_file=vars['LogFile'])
run_program(vars['FastCopy'], _args, check=True)
except subprocess.CalledProcessError:
print_warning('WARNING: Errors encountered while copying registry', log_file=vars['LogFile'])
# Main copy
selected_items = []
for item in os.scandir(restore_source.path):
if re_included_root_items.search(item.name):
selected_items.append(item.path)
elif not re_excluded_root_items.search(item.name):
if ask('Copy: "{name}" item?'.format(name=item.name)):
selected_items.append(item.path)
if len(selected_items) > 0:
_args = vars['FastCopyArgs'].copy()
_args += selected_items
_args.append('/to={TransferDir}\\'.format(**vars))
try:
print_standard('Copying main user data...', log_file=vars['LogFile'])
run_program(vars['FastCopy'], _args, check=True)
except subprocess.CalledProcessError:
print_warning('WARNING: Errors encountered while copying main user data', log_file=vars['LogFile'])
else:
print_error('ERROR: No files selected for transfer?', log_file=vars['LogFile'])
abort()
# Windows.old
selected_items = []
for item in ['Users', 'ProgramData']:
item = '{source}\\Windows.old\\{item}'.format(source=restore_source.path, item=item)
if os.path.exists(item):
selected_items.append(item)
if len(selected_items) > 0:
_args = vars['FastCopyArgs'].copy()
_args += selected_items
_args.append('/to={TransferDir}\\Windows.old\\'.format(**vars))
try:
print_standard('Copying user data (Windows.old)...', log_file=vars['LogFile'])
run_program(vars['FastCopy'], _args, check=True)
except subprocess.CalledProcessError:
print_warning('WARNING: Errors encountered while copying data from Windows.old', log_file=vars['LogFile'])
def transfer_image_based(restore_source):
print_standard('Assessing image...', log_file=vars['LogFile'])
# Scan source
_args = [
'dir',
'{source}'.format(source=restore_source.path), '1']
try:
_list = run_program(vars['wimlib-imagex'], _args, check=True)
except subprocess.CalledProcessError as err:
print_error('ERROR: Failed to get file list.')
print(err)
abort()
# Add items to list
selected_items = []
root_items = [i.strip() for i in _list.stdout.decode('utf-8', 'ignore').splitlines() if i.count('\\') == 1 and i.strip() != '\\']
for item in root_items:
if re_included_root_items.search(item):
selected_items.append(item)
elif not re_excluded_root_items.search(item):
if ask('Extract: "{name}" item?'.format(name=item)):
selected_items.append(item)
# Extract files
if len(selected_items) > 0:
# Write files.txt
with open('{TmpDir}\\wim_files.txt'.format(**vars), 'w') as f:
# Defaults
f.write('\\Windows.old\\WK*\n')
f.write('\\Windows.old\\ProgramData\n')
f.write('\\Windows.old\\Temp\n')
f.write('\\Windows.old\\Users\n')
f.write('\\Windows.old\\Windows\\System32\\config\n')
f.write('\\Windows.old\\Windows\\System32\\OEM\n')
f.write('\\Windows\\System32\\config\n')
f.write('\\Windows\\System32\\OEM\n')
f.write('AdwCleaner\\*log\n')
f.write('AdwCleaner\\*txt\n')
for item in selected_items:
f.write('{item}\n'.format(item=item))
try:
print_standard('Extracting user data...', log_file=vars['LogFile'])
_args = [
'extract',
'{source}'.format(source=restore_source.path), '1',
'@{TmpDir}\\wim_files.txt'.format(**vars),
'--dest-dir={TransferDir}\\'.format(**vars),
'--no-acls',
'--nullglob']
run_program(vars['wimlib-imagex'], _args, check=True)
except subprocess.CalledProcessError:
print_warning('WARNING: Errors encountered while extracting user data', log_file=vars['LogFile'])
else:
print_error('ERROR: No files selected for extraction?', log_file=vars['LogFile'])
abort()
if __name__ == '__main__':
# Set ticket number
ticket = None
while ticket is None:
tmp = input('Enter ticket number: ')
if re.match(r'^([0-9]+([-_]?\w+|))$', tmp):
ticket = tmp
# Get backup
backup_source = select_backup(ticket)
if backup_source is None:
abort()
# Determine restore method
restore_source = None
restore_options = []
_file_based = False
for item in os.scandir(backup_source.path):
if item.is_dir():
_file_based = True
restore_options.append({'Name': 'File-Based:\t{source}'.format(source=item.name), 'Source': item})
for _subitem in os.scandir(item.path):
if is_valid_image(_subitem):
restore_options.append({'Name': 'Image-Based: {dir}\\{source}'.format(dir=item.name, source=_subitem.name), 'Source': _subitem})
elif is_valid_image(item):
restore_options.append({'Name': 'Image-Based:\t{source}'.format(source=item.name), 'Source': item})
if _file_based:
restore_options.append({'Name': 'File-Based:\t.', 'Source': backup_source})
restore_options = sorted(restore_options, key=itemgetter('Name'))
actions = [{'Name': 'Quit', 'Letter': 'Q'}]
if len(restore_options) > 0:
selection = menu_select('Which backup are we using? (Path: {path})'.format(path=backup_source.path), restore_options, actions)
if selection == 'Q':
abort()
else:
restore_source = restore_options[int(selection)-1]['Source']
else:
print_error('ERROR: No restoration options detected.', log_file=vars['LogFile'])
abort()
# Start transfer
print_info('Using backup: {path}'.format(path=restore_source.path), log_file=vars['LogFile'])
if restore_source.is_dir():
transfer_file_based(restore_source)
if restore_source.is_file():
transfer_image_based(restore_source)
cleanup_transfer()
# Done
print_success('Done.', log_file=vars['LogFile'])
exit_script()