Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| #!/bin/bash | |
| VERSION='0.76'; | |
| RELEASE_DATE='13 April 2017'; | |
| LAST_GIT_COMMIT_SHORTLOG=''; | |
| LAST_GIT_COMMIT_DATE=''; | |
| ################################################################################ | |
| # # | |
| # Copyright (c) 2009-2010 Ulrich Meierfrankenfeld # | |
| # Copyright (c) 2011-2012 Gert Hulselmans # | |
| # Copyright (c) 2013-2017 Andrei Borzenkov # | |
| # # | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy # | |
| # of this software and associated documentation files (the "Software"), to # | |
| # deal in the Software without restriction, including without limitation the # | |
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # | |
| # sell copies of the Software, and to permit persons to whom the Software is # | |
| # furnished to do so, subject to the following conditions: # | |
| # # | |
| # The above copyright notice and this permission notice shall be included in # | |
| # all copies or substantial portions of the Software. # | |
| # # | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # | |
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # | |
| # IN THE SOFTWARE. # | |
| # # | |
| ################################################################################ | |
| # # | |
| # Current developer: Andrei Borzenkov # | |
| # # | |
| # Past developer: Gert Hulselmans # | |
| # Past developer: Ulrich Meierfrankenfeld (meierfra) (ubuntuforums.org) # | |
| # Past contributor: caljohnsmith (ubuntuforums.org) # | |
| # # | |
| # Hosted at: https://github.com/arvidjaar/bootinfoscript # | |
| # Forked from: http://sourceforge.net/projects/bootinfoscript/ # | |
| # # | |
| # The birth of Boot Info Script: # | |
| # http://ubuntuforums.org/showthread.php?t=837791 # | |
| # # | |
| # Tab width: 8 spaces # | |
| # # | |
| ################################################################################ | |
| ## Check if the script is run with bash as shell interpreter. | |
| if [ -z "$BASH_VERSION" ] ; then | |
| echo 'Boot Info Script needs to be run with bash as shell interpreter.' >&2; | |
| exit 1; | |
| fi | |
| ## Display help text ## | |
| # | |
| # ./bootinfoscript -h | |
| # ./bootinfoscript -help | |
| # ./bootinfoscript --help | |
| help () { | |
| cat <<- HELP | |
| Usage Boot Info Script: | |
| ----------------------- | |
| Run the script as sudoer: | |
| sudo ${0} <outputfile> | |
| or if your operating system does not use sudo: | |
| su - | |
| ${0} <outputfile> | |
| When running the script, without specifying an output file, all the output | |
| is written to the file "RESULTS.txt" in the same folder as the script. | |
| But when run from /bin, /sbin, /usr/bin, or another system folder, the file | |
| "RESULTS.txt" is written to the home directory of the user. | |
| When the file "RESULTS.txt" already exists, the results will be written to | |
| "RESULTS1.txt". If "RESULTS1.txt" exists, the results will be written to | |
| "RESULTS2.txt", ... | |
| To get version number, release date, last git commit and git retrieval date | |
| of this script, use (no root rights needed): | |
| ${0} -v | |
| ${0} -V | |
| ${0} --version | |
| To get this help text, use (no root rights needed): | |
| ${0} -h | |
| ${0} -help | |
| ${0} --help | |
| To automatically gzip a copy of the output file, use (root rights needed): | |
| ${0} -g <outputfile> | |
| ${0} --gzip <outputfile> | |
| To write the output to stdout instead of a file, use (root rights needed): | |
| ${0} --stdout | |
| The last development version of Boot Info Script can be downloaded, with: | |
| (no root rights needed) | |
| ${0} --update <filename> | |
| If no filename is specified, the file will be saved in the home dir as | |
| "bootinfoscript_YYYY-MM-DD_hh:mm:ss". | |
| HELP | |
| exit 0; | |
| } | |
| ## Download the last development version of BIS from git: ## | |
| # | |
| # ./bootinfoscript --update <filename> | |
| # | |
| # If no filename is specified, the file will be saved in the home dir as | |
| # "bootinfoscript_YYYY-MM-DD_hh:mm:ss". | |
| update () { | |
| local git_ref_url='https://api.github.com/repos/arvidjaar/bootinfoscript/git/refs/heads/master' | |
| local git_commit_url='https://api.github.com/repos/arvidjaar/bootinfoscript/git/commits' | |
| local git_contents_url='https://github.com/arvidjaar/bootinfoscript/raw/master/bootinfoscript' | |
| # Check if date is available. | |
| if [ $(type date > /dev/null 2>&1 ; echo $?) -ne 0 ] ; then | |
| echo '"date" could not be found.' >&2; | |
| exit 1; | |
| fi | |
| # Get current UTC time in YYYY-MM-DD-hh:mm:ss format. | |
| UTC_TIME=$(date --utc "+%Y-%m-%d %T"); | |
| if [ ! -z "$1" ] ; then | |
| GIT_BIS_FILENAME="$1"; | |
| else | |
| GIT_BIS_FILENAME="${HOME}/bootinfoscript_${UTC_TIME/ /_}" | |
| fi | |
| # Check if wget or curl is available | |
| if [ $(type wget > /dev/null 2>&1 ; echo $?) -eq 0 ] ; then | |
| printf '\nDownloading last development version of Boot Info Script from git:\n\n'; | |
| LAST_GIT_COMMIT_ID=$(wget -O - "${git_ref_url}" | sed -ne 's/^.*"sha": "\(.*\)".*$/\1/p'); | |
| LAST_GIT_COMMIT=$(wget -O - "${git_commit_url}/$LAST_GIT_COMMIT_ID"); | |
| wget -O "${GIT_BIS_FILENAME}" "${git_contents_url}"; | |
| elif [ $(type curl > /dev/null 2>&1 ; echo $?) -eq 0 ] ; then | |
| printf 'Downloading last development version of Boot Info Script from git:\n\n'; | |
| LAST_GIT_COMMIT_ID=$(curl "${git_ref_url}" | sed -ne 's/^.*"sha": "\(.*\)".*$/\1/p'); | |
| LAST_GIT_COMMIT=$(curl "${git_commit_url}/$LAST_GIT_COMMIT_ID"); | |
| curl -o "${GIT_BIS_FILENAME}" "${git_contents_url}"; | |
| else | |
| printf '"wget" or "curl" could not be found.\nInstall at least one of them and try again.\n' >&2; | |
| exit 1; | |
| fi | |
| # First date is Author, second date is Commit | |
| LAST_GIT_COMMIT_DATE=$(echo "${LAST_GIT_COMMIT}" | sed -ne 's/^[[:space:]]*"date": "\(.*\)"[[:space:]]*$/\1/p' | tail -1); | |
| LAST_GIT_COMMIT_SHORTLOG=$(echo "${LAST_GIT_COMMIT}" | sed -n -e '/^[[:space:]]*"message":/ { s/^[[:space:]]*"message": "\(.*\)",[[:space:]]*$/\1/ ; s/\\n.*$// ; p }'); | |
| # Set the retrieval date in just downloaded script. | |
| sed -i -e "4,0 s@LAST_GIT_COMMIT_SHORTLOG='';@LAST_GIT_COMMIT_SHORTLOG='${LAST_GIT_COMMIT_SHORTLOG}';@" \ | |
| -e "5,0 s/LAST_GIT_COMMIT_DATE='';/LAST_GIT_COMMIT_DATE='${LAST_GIT_COMMIT_DATE}';/" \ | |
| "${GIT_BIS_FILENAME}"; | |
| printf '\nThe development version of Boot Info Script is saved as:\n"%s"\n\n' "${GIT_BIS_FILENAME}"; | |
| exit 0; | |
| } | |
| ## Display version, release, last git commit and git retrieval date of the script when asked: ## | |
| # | |
| # ./bootinfoscript -v | |
| # ./bootinfoscript -V | |
| # ./bootinfoscript --version | |
| version () { | |
| printf '\nBoot Info Script version: %s\nRelease date: %s' "${VERSION}" "${RELEASE_DATE}"; | |
| if [ ! -z "${LAST_GIT_COMMIT_SHORTLOG}" ] ; then | |
| printf '\nLast git commit: %s\nCommit date: %s' \ | |
| "${LAST_GIT_COMMIT_SHORTLOG}" "${LAST_GIT_COMMIT_DATE}"; | |
| fi | |
| printf '\n\n'; | |
| exit 0; | |
| } | |
| ## Gzip a copy of the output file? ## | |
| gzip_output=0; # off=0 | |
| ## Write the output to the standard output instead of to a file? ## | |
| stdout_output=0; # off=0 | |
| ## Get arguments passed to the script. ## | |
| process_args () { | |
| if [ ${#@} -ge 1 ] ; then | |
| # Process arguments. | |
| case "$1" in | |
| -g ) gzip_output=1; if [ ! -z "$2" ] ; then LogFile_cmd="$2"; fi;; | |
| --gzip ) gzip_output=1; if [ ! -z "$2" ] ; then LogFile_cmd="$2"; fi;; | |
| -h ) help;; | |
| -help ) help;; | |
| --help ) help;; | |
| --stdout ) stdout_output=1;; | |
| --update ) update "$2";; | |
| -v ) version;; | |
| -V ) version;; | |
| --version ) version;; | |
| -* ) help;; | |
| * ) LogFile_cmd="$1";; | |
| esac | |
| fi | |
| } | |
| ## Get arguments passed to the script. ## | |
| process_args ${@}; | |
| ## Display version number, release and git retrieval date. ## | |
| printf '\nBoot Info Script %s [%s]' "${VERSION}" "${RELEASE_DATE}"; | |
| if [ ! -z "${LAST_GIT_COMMIT_SHORTLOG}" ] ; then | |
| printf '\n Last git commit: %s\n Commit date: %s' \ | |
| "${LAST_GIT_COMMIT_SHORTLOG}" "${LAST_GIT_COMMIT_DATE}"; | |
| fi | |
| printf '\n\n'; | |
| ## Check whether Boot Info Script is run with root rights or not. ## | |
| if [ $(type whoami > /dev/null 2>&1 ; echo $?) -ne 0 ] ; then | |
| echo 'Please install "whoami" and run Boot Info Script again.' >&2; | |
| exit 1; | |
| elif [ $(whoami) != 'root' ] ; then | |
| cat <<- EOF >&2 | |
| Please use "sudo" or become "root" to run this script. | |
| Run the script as sudoer: | |
| sudo ${0} <outputfile> | |
| or if your operating system does not use sudo: | |
| su - | |
| ${0} <outputfile> | |
| For more info, see the help: | |
| ${0} --help | |
| EOF | |
| exit 1; | |
| fi | |
| ## Check if all necessary programs are available. ## | |
| # Programs that are in /bin or /usr/bin. | |
| Programs=' | |
| basename | |
| cat | |
| chown | |
| dd | |
| dirname | |
| expr | |
| fold | |
| grep | |
| gzip | |
| hexdump | |
| ls | |
| mkdir | |
| mktemp | |
| mount | |
| printf | |
| pwd | |
| rm | |
| sed | |
| sort | |
| tr | |
| umount | |
| wc' | |
| # Programs that are in /usr/sbin or /sbin. | |
| Programs_SBIN=' | |
| blkid | |
| fdisk | |
| filefrag | |
| losetup' | |
| Check_Prog=1; | |
| for Program in ${Programs} ${Programs_SBIN}; do | |
| if [ $(type ${Program} > /dev/null 2>&1 ; echo $?) -ne 0 ] ; then | |
| echo "\"${Program}\" could not be found." >&2; | |
| Check_Prog=0; | |
| fi | |
| done | |
| ## Can we decompress a LZMA stream? ## | |
| # | |
| # The Grub2 (v1.99-2.00) core_dir string is contained in a LZMA stream. | |
| # See if we have xz or lzma installed to decompress the stream. | |
| # | |
| if [ $(type xz > /dev/null 2>&1 ; echo $?) -eq 0 ] ; then | |
| UNLZMA='xz --format=lzma --decompress'; | |
| elif [ $(type lzma > /dev/null 2>&1 ; echo $?) -eq 0 ] ; then | |
| UNLZMA='lzma -cd'; | |
| else | |
| UNLZMA='none'; | |
| fi | |
| ## Do we have gawk or (a recent) mawk? ## | |
| # | |
| # If we don't have gawk, look for "mawk v1.3.4" or newer. | |
| # | |
| if [ $(type gawk > /dev/null 2>&1 ; echo $?) -eq 0 ] ; then | |
| # Set awk binary to gawk. | |
| AWK='gawk'; | |
| elif [ $(type mawk > /dev/null 2>&1 ; echo $?) -eq 0 ] ; then | |
| MAWK_version="$(mawk -W version 2>&1)"; | |
| MAWK_version="${MAWK_version:0:10}"; | |
| if [ "${MAWK_version}" = 'mawk 1.3.3' ]; then | |
| printf '"mawk v1.3.3" has known bugs.\nInstall "mawk v1.3.4" or newer from http://invisible-island.net/mawk/ or use "gawk" instead.\n' >&2; | |
| Check_Prog=0; | |
| else | |
| # Set awk binary to mawk (version 1.3.4 or higher). | |
| AWK='mawk'; | |
| fi | |
| else | |
| printf 'Install "gawk" or "mawk v1.3.4" (or newer) from http://invisible-island.net/mawk/.\n' >&2; | |
| Check_Prog=0; | |
| fi | |
| if [ ${Check_Prog} -eq 0 ] ; then | |
| printf '\nPlease install the missing program(s) and run Boot Info Script again.\n' >&2; | |
| exit 1; | |
| fi | |
| ## List of folders which might contain files used for chainloading. ## | |
| Boot_Codes_Dir=' | |
| / | |
| /NST/ | |
| ' | |
| ## List of files whose names will be displayed, if found. ## | |
| Boot_Prog_Normal=' | |
| /bootmgr /BOOTMGR | |
| /boot/bcd /BOOT/bcd /Boot/bcd /boot/BCD /BOOT/BCD /Boot/BCD | |
| /Windows/System32/winload.exe /WINDOWS/system32/winload.exe /WINDOWS/SYSTEM32/winload.exe /windows/system32/winload.exe | |
| /Windows/System32/Winload.exe /WINDOWS/system32/Winload.exe /WINDOWS/SYSTEM32/Winload.exe /windows/system32/Winload.exe | |
| /grldr /GRLDR /grldr.mbr /GRLDR.MBR | |
| /ntldr /NTLDR | |
| /NTDETECT.COM /ntdetect.com | |
| /NTBOOTDD.SYS /ntbootdd.sys | |
| /wubildr /ubuntu/winboot/wubildr | |
| /wubildr.mbr /ubuntu/winboot/wubildr.mbr | |
| /ubuntu/disks/root.disk | |
| /ubuntu/disks/home.disk | |
| /ubuntu/disks/swap.disk | |
| /core.img /grub/core.img /boot/grub/core.img | |
| /grub/i386-pc/core.img /boot/grub/i386-pc/core.img | |
| /grub2/core.img /boot/grub2/core.img | |
| /grub2/i386-pc/core.img /boot/grub2/i386-pc/core.img | |
| /burg/core.img /boot/burg/core.img | |
| /ldlinux.sys /syslinux/ldlinux.sys /boot/syslinux/ldlinux.sys | |
| /extlinux.sys /extlinux/extlinux.sys /boot/extlinux/extlinux.sys | |
| /boot/map /map | |
| /DEFAULT.MNU /default.mnu | |
| /IO.SYS /io.sys | |
| /MSDOS.SYS /msdos.sys | |
| /KERNEL.SYS /kernel.sys | |
| /DELLBIO.BIN /dellbio.bin /DELLRMK.BIN /dellrmk.bin | |
| /COMMAND.COM /command.com | |
| ' | |
| Boot_Prog_Fat=' | |
| /bootmgr | |
| /boot/bcd | |
| /Windows/System32/winload.exe | |
| /grldr | |
| /grldr.mbr | |
| /ntldr | |
| /freeldr.sys | |
| /NTDETECT.COM | |
| /NTBOOTDD.SYS | |
| /wubildr | |
| /wubildr.mbr | |
| /ubuntu/winboot/wubildr | |
| /ubuntu/winboot/wubildr.mbr | |
| /ubuntu/disks/root.disk | |
| /ubuntu/disks/home.disk | |
| /ubuntu/disks/swap.disk | |
| /core.img /grub/core.img /boot/grub/core.img | |
| /grub/i386-pc/core.img /boot/grub/i386-pc/core.img | |
| /grub2/core.img /boot/grub2/core.img | |
| /grub2/i386-pc/core.img /boot/grub2/i386-pc/core.img | |
| /burg/core.img /boot/burg/core.img | |
| /ldlinux.sys /syslinux/ldlinux.sys /boot/syslinux/ldlinux.sys | |
| /extlinux.sys /extlinux/extlinux.sys /boot/extlinux/extlinux.sys | |
| /boot/map /map | |
| /DEFAULT.MNU | |
| /IO.SYS | |
| /MSDOS.SYS | |
| /KERNEL.SYS | |
| /DELLBIO.BIN /DELLRMK.BIN | |
| /COMMAND.COM | |
| ' | |
| ## List of files whose contents will be displayed. ## | |
| Boot_Files_Normal=' | |
| /menu.lst /grub/menu.lst /boot/grub/menu.lst /NST/menu.lst | |
| /grub.cfg /grub/grub.cfg /boot/grub/grub.cfg /grub2/grub.cfg /boot/grub2/grub.cfg | |
| /custom.cfg /grub/custom.cfg /boot/grub/custom.cfg /grub2/custom.cfg /boot/grub2/custom.cfg | |
| /burg.cfg /burg/burg.cfg /boot/burg/burg.cfg | |
| /grub.conf /grub/grub.conf /boot/grub/grub.conf /grub2/grub.conf /boot/grub2/grub.conf | |
| /ubuntu/disks/boot/grub/menu.lst /ubuntu/disks/install/boot/grub/menu.lst /ubuntu/winboot/menu.lst | |
| /boot.ini /BOOT.INI /Boot.ini | |
| /etc/fstab | |
| /etc/lilo.conf /lilo.conf | |
| /syslinux.cfg /syslinux/syslinux.cfg /boot/syslinux/syslinux.cfg | |
| /extlinux.conf /extlinux/extlinux.conf /boot/extlinux/extlinux.conf | |
| /grldr /grub.exe | |
| ' | |
| Boot_Files_Fat=' | |
| /menu.lst /grub/menu.lst /boot/grub/menu.lst /NST/menu.lst | |
| /grub.cfg /grub/grub.cfg /boot/grub/grub.cfg /grub2/grub.cfg /boot/grub2/grub.cfg | |
| /custom.cfg /grub/custom.cfg /boot/grub/custom.cfg /grub2/custom.cfg /boot/grub2/custom.cfg | |
| /burg.cfg /burg/burg.cfg /boot/burg/burg.cfg | |
| /grub.conf /grub/grub.conf /boot/grub/grub.conf /grub2/grub.conf /boot/grub2/grub.conf | |
| /ubuntu/disks/boot/grub/menu.lst /ubuntu/disks/install/boot/grub/menu.lst /ubuntu/winboot/menu.lst | |
| /boot.ini | |
| /freeldr.ini | |
| /etc/fstab | |
| /etc/lilo.conf /lilo.conf | |
| /syslinux.cfg /syslinux/syslinux.cfg /boot/syslinux/syslinux.cfg | |
| /extlinux.conf /extlinux/extlinux.conf /boot/extlinux/extlinux.conf | |
| /grldr /grub.exe | |
| ' | |
| ## List of files whose end point (in GiB / GB) will be displayed. ## | |
| GrubError18_Files=' | |
| menu.lst grub/menu.lst boot/grub/menu.lst NST/menu.lst | |
| ubuntu/disks/boot/grub/menu.lst | |
| grub.conf grub/grub.conf boot/grub/grub.conf grub2/grub.conf boot/grub2/grub.conf | |
| grub.cfg grub/grub.cfg boot/grub/grub.cfg grub2/grub.cfg boot/grub2/grub.cfg | |
| burg.cfg burg/burg.cfg boot/burg/burg.cfg | |
| core.img grub/core.img boot/grub/core.img | |
| grub/i386-pc/core.img boot/grub/i386-pc/core.img | |
| grub2/core.img boot/grub2/core.img | |
| grub2/i386-pc/core.img boot/grub2/i386-pc/core.img | |
| burg/core.img boot/burg/core.img | |
| stage2 grub/stage2 boot/grub/stage2 | |
| boot/vmlinuz* vmlinuz* ubuntu/disks/boot/vmlinuz* | |
| boot/initrd* initrd* ubuntu/disks/boot/initrd* | |
| boot/kernel*.img | |
| initramfs* boot/initramfs* | |
| ' | |
| SyslinuxError_Files=' | |
| syslinux.cfg syslinux/syslinux.cfg boot/syslinux/syslinux.cfg | |
| extlinux.conf extlinux/extlinux.conf boot/extlinux/extlinux.conf | |
| ldlinux.sys syslinux/ldlinux.sys boot/syslinux/ldlinux.sys | |
| extlinux.sys extlinux/extlinux.sys boot/extlinux/extlinux.sys | |
| *.c32 syslinux/*.c32 boot/syslinux/*.c32 | |
| extlinux/*.c32 boot/extlinux/*.c32 | |
| ' | |
| ## Set output filename ## | |
| if [ ${stdout_output} -eq 1 ] ; then | |
| # The LogFile name is not used when --stdout is specified. | |
| LogFile=""; | |
| elif ( [ ! -z "${LogFile_cmd}" ]) ; then | |
| # The RESULTS filename is specified on the commandline. | |
| LogFile=$(basename "${LogFile_cmd}"); | |
| # Directory where the RESULTS file will be stored. | |
| Dir=$(dirname "${LogFile_cmd}"); | |
| # Check if directory exists. | |
| if [ ! -d "${Dir}" ] ; then | |
| echo "The directory \"${Dir}\" does not exist."; | |
| echo 'Create the directory or specify another path for the output file.'; | |
| exit 1; | |
| fi | |
| Dir=$(cd "${Dir}"; pwd); | |
| LogFile="${Dir}/${LogFile}"; | |
| else | |
| # Directory containing this script. | |
| Dir=$(cd "$(dirname "$0")"; pwd); | |
| # Set ${Dir} to the home folder of the current user if the script is | |
| # in one of the system folders. | |
| # This allows placement of the script in /bin, /sbin, /usr/bin, ... | |
| # while still having a normal location to write the output file to. | |
| for systemdir in /bin /boot /cdrom /dev /etc /lib /lost+found /opt /proc /sbin /selinux /srv /sys /usr /var; do | |
| if [ $(expr "${Dir}" : ${systemdir}) -ne 0 ] ; then | |
| Dir="${HOME}"; | |
| break; | |
| fi | |
| done | |
| # To avoid overwriting existing files, look for a non-existing file: | |
| # RESULT.txt, RESULTS1.txt, RESULTS2.txt, ... | |
| LogFile="${Dir}/RESULTS"; | |
| while ( [ -e "${LogFile}${j}.txt" ] ) ; do | |
| if [ x"${j}" = x'' ]; then | |
| j=0; | |
| fi | |
| j=$((${j}+1)); | |
| done | |
| LogFile="${LogFile}${j}.txt"; ## The RESULTS file. ## | |
| fi | |
| ## Redirect stdout to RESULT File ## | |
| # | |
| # exec 6>&1 | |
| # exec > "${LogFile}" | |
| ## Create temporary directory ## | |
| Folder=$(mktemp -t -d BootInfo-XXXXXXXX); | |
| ## Create temporary filenames. ## | |
| cd ${Folder} | |
| Log=${Folder}/Log # File to record the summary. | |
| Log1=${Folder}/Log1 # Most of the information which is not part of | |
| # the summary is recorded in this file. | |
| Error_Log=${Folder}/Error_Log # File to catch all unusal Standar Errors. | |
| Trash=${Folder}/Trash # File to catch all usual Standard Errors these | |
| # messagges will not be included in the RESULTS. | |
| Mount_Error=${Folder}/Mount_Error # File to catch Mounting Errors. | |
| Unknown_MBR=${Folder}/Unknown_MBR # File to record all unknown MBR and Boot sectors. | |
| Tmp_Log=${Folder}/Tmp_Log # File to temporarily hold some information. | |
| core_img_file=${Folder}/core_img # File to temporarily store an embedded core.img of grub2. | |
| core_img_file_unlzma=${Folder}/core_img_unlzma # File to temporarily store the uncompressed part of core.img of grub2. | |
| core_img_file_type_2=${Folder}/core_img_type_2 # File to temporarily store the core.img module of type 2 | |
| PartitionTable=${Folder}/PT # File to store the Partition Table. | |
| FakeHardDrives=${Folder}/FakeHD # File to list devices which seem to have no corresponding drive. | |
| BLKID=${Folder}/BLKID # File to store the output of blkid. | |
| GRUB200_Module=${Folder}/GRUB200_Module # File to store current grub2 module | |
| ## Redirect all standard error to the file Error_Log. ## | |
| exec 2> ${Error_Log}; | |
| ## List of all hard drives ## | |
| # | |
| # Support more than 26 drives. | |
| All_Hard_Drives=$(ls /dev/hd[a-z] /dev/hd[a-z][a-z] /dev/sd[a-z] /dev/sd[a-z][a-z] /dev/xvd[a-z] /dev/vd[a-z] /dev/vd[a-z][a-z] 2>> ${Trash}); | |
| ## Add found RAID disks to list of hard drives. ## | |
| if [ $(type dmraid >> ${Trash} 2>> ${Trash} ; echo $?) -eq 0 ] ; then | |
| InActiveDMRaid=$(dmraid -si -c); | |
| if [ x"${InActiveDMRaid}" = x"no raid disks" ] || [ x"${InActiveDMRaid}" = x"no block devices found" ] ; then | |
| InActiveDMRaid=''; | |
| fi | |
| if [ x"${InActiveDMRaid}" != x'' ] ; then | |
| dmraid -ay ${InActiveDMRaid} >> ${Trash}; | |
| fi | |
| All_DMRaid=$(dmraid -sa -c); | |
| if [ x"${All_DMRaid}" != x"no raid disks" ] && [ x"${All_DMRaid}" != x"no block devices found" ] ; then | |
| All_DMRaid=$(echo "{All_DMRaid}" | ${AWK} '{ print "/dev/mapper/"$0 }'); | |
| All_Hard_Drives="${All_Hard_Drives} ${All_DMRaid}"; | |
| fi | |
| fi | |
| ## Arrays to hold information about Partitions: ## | |
| # | |
| # name, starting sector, ending sector, size in sector, partition type, | |
| # filesystem type, UUID, kind(Logical, Primary, Extended), harddrive, | |
| # boot flag, parent (for logical partitions), label, | |
| # system(the partition id according the partition table), | |
| # the device associated with the partition. | |
| declare -a NamesArray StartArray EndArray SizeArray TypeArray FileArray UUIDArray KindArray DriveArray BootArray ParentArray LabelArray SystemArray DeviceArray; | |
| ## Arrays to hold information about the harddrives. ## | |
| declare -a HDName FirstPartion LastPartition HDSize HDMBR HDHead HDTrack HDCylinder HDPT HDStart HDEnd HDUUID; | |
| ## Array for hard drives formatted as filesystem. ## | |
| declare -a FilesystemDrives; | |
| PI=-1; ## Counter for the identification number of a partition. (each partition gets unique number) ## | |
| HI=0; ## Counter for the identification number of a hard drive. (each hard drive gets unique number) ## | |
| PTFormat='%-10s %4s%14s%14s%14s %3s %s\n'; ## standard format (hexdump) to use for partition table. ## | |
| ## Get total number of blocks on a device. ## | |
| # | |
| # Sometimes "fdisk -s" seems to malfunction or isn't supported (busybox fdisk), | |
| # so use "sfdisk -s" if available. | |
| # If sfdisk isn't available, calculate the number of blocks from the number of | |
| # sectors (divide by 2). | |
| fdisks () { | |
| if [ $(type sfdisk >> ${Trash} 2>> ${Trash} ; echo $?) -eq 0 ] ; then | |
| sfdisk -s "$1" 2>> ${Trash}; | |
| else | |
| # Calculate the number of blocks from the number of sectors (divide by 2). | |
| fdisk -lu "$1" 2>> ${Trash} | ${AWK} '$0 ~ /, .*, .*, .*/ { print $(NF - 1) / 2 }'; | |
| fi | |
| } | |
| ## A function which checks whether a file is on a mounted partition. ## | |
| # List of mount points for devices: also allow mount points with spaces. | |
| MountPoints=$(mount \ | |
| | ${AWK} -F "\t" '{ if ( ($1 ~ "^/dev") && ($3 != "/") ) { sub(" on ", "\t", $0); sub(" type ", "\t", $0); print $2 } }' \ | |
| | sort -u); | |
| FileNotMounted () { | |
| local File=$1 curmp=$2; | |
| IFS_OLD="${IFS}"; # Save original IFS. | |
| IFS=$'\012'; # Set IFS temporarily to newline only, so mount points with spaces can be processed too. | |
| for mp in ${MountPoints}; do | |
| if [ $(expr match "${File}" "${mp}/" ) -ne 0 ] && [ "${mp}" != "${curmp}" ] ; then | |
| IFS="${IFS_OLD}"; # Restore original IFS. | |
| return 1; | |
| fi | |
| done | |
| IFS="${IFS_OLD}"; # Restore original IFS. | |
| return 0; | |
| } | |
| ## Function which converts the two digit hexcode to the partition type. ## | |
| # | |
| # The following list is taken from sfdisk -T and | |
| # http://www.win.tue.nl/~aeb/partitions/partition_types-1.html | |
| # is work in progress. | |
| HexToSystem () { | |
| local type=$1 system; | |
| case ${type} in | |
| 0) system='Empty';; | |
| 1) system='FAT12';; | |
| 2) system='XENIX root';; | |
| 3) system='XENIX /usr';; | |
| 4) system='FAT16 <32M';; | |
| 5) system='Extended';; | |
| 6) system='FAT16';; | |
| 7) system='NTFS / exFAT / HPFS';; | |
| 8) system='AIX bootable';; | |
| 9) system='AIX data';; | |
| a) system='OS/2 Boot Manager';; | |
| b) system='W95 FAT32';; | |
| c) system='W95 FAT32 (LBA)';; | |
| e) system='W95 FAT16 (LBA)';; | |
| f) system='W95 Extended (LBA)';; | |
| 10) system='OPUS';; | |
| 11) system='Hidden FAT12';; | |
| 12) system='Compaq diagnostics';; | |
| 14) system='Hidden FAT16 < 32M';; | |
| 16) system='Hidden FAT16';; | |
| 17) system='Hidden NTFS / HPFS';; | |
| 18) system='AST SmartSleep';; | |
| 1b) system='Hidden W95 FAT32';; | |
| 1c) system='Hidden W95 FAT32 (LBA)';; | |
| 1e) system='Hidden W95 FAT16 (LBA)';; | |
| 24) system='NEC DOS';; | |
| 27) system='Hidden NTFS (Recovery Environment)';; | |
| 2a) system='AtheOS File System';; | |
| 2b) system='SyllableSecure';; | |
| 32) system='NOS';; | |
| 35) system='JFS on OS/2';; | |
| 38) system='THEOS';; | |
| 39) system='Plan 9';; | |
| 3a) system='THEOS';; | |
| 3b) system='THEOS Extended';; | |
| 3c) system='PartitionMagic recovery';; | |
| 3d) system='Hidden NetWare';; | |
| 40) system='Venix 80286';; | |
| 41) system='PPC PReP Boot';; | |
| 42) system='SFS';; | |
| 44) system='GoBack';; | |
| 45) system='Boot-US boot manager';; | |
| 4d) system='QNX4.x';; | |
| 4e) system='QNX4.x 2nd part';; | |
| 4f) system='QNX4.x 3rd part';; | |
| 50) system='OnTrack DM';; | |
| 51) system='OnTrack DM6 Aux1';; | |
| 52) system='CP/M';; | |
| 53) system='OnTrack DM6 Aux3';; | |
| 54) system='OnTrack DM6 DDO';; | |
| 55) system='EZ-Drive';; | |
| 56) system='Golden Bow';; | |
| 57) system='DrivePro';; | |
| 5c) system='Priam Edisk';; | |
| 61) system='SpeedStor';; | |
| 63) system='GNU HURD or SysV';; | |
| 64) system='Novell Netware 286';; | |
| 65) system='Novell Netware 386';; | |
| 70) system='DiskSecure Multi-Boot';; | |
| 74) system='Scramdisk';; | |
| 75) system='IBM PC/IX';; | |
| 78) system='XOSL filesystem';; | |
| 80) system='Old Minix';; | |
| 81) system='Minix / old Linux';; | |
| 82) system='Linux swap / Solaris';; | |
| 83) system='Linux';; | |
| 84) system='OS/2 hidden C: drive';; | |
| 85) system='Linux extended';; | |
| 86) system='NTFS volume set';; | |
| 87) system='NTFS volume set';; | |
| 88) system='Linux plaintext';; | |
| 8a) system='Linux Kernel (AiR-BOOT)';; | |
| 8d) system='Free FDISK hidden Primary FAT12';; | |
| 8e) system='Linux LVM';; | |
| 90) system='Free FDISK hidden Primary FAT16 <32M';; | |
| 91) system='Free FDISK hidden Extended';; | |
| 92) system='Free FDISK hidden Primary FAT16';; | |
| 93) system='Amoeba/Accidently Hidden Linux';; | |
| 94) system='Amoeba bad block table';; | |
| 97) system='Free FDISK hidden Primary FAT32';; | |
| 98) system='Free FDISK hidden Primary FAT32 (LBA)';; | |
| 9a) system='Free FDISK hidden Primary FAT16 (LBA)';; | |
| 9b) system='Free FDISK hidden Extended (LBA)';; | |
| 9f) system='BSD/OS';; | |
| a0) system='IBM Thinkpad hibernation';; | |
| a1) system='Laptop hibernation';; | |
| a5) system='FreeBSD';; | |
| a6) system='OpenBSD';; | |
| a7) system='NeXTSTEP';; | |
| a8) system='Darwin UFS';; | |
| a9) system='NetBSD';; | |
| ab) system='Darwin boot';; | |
| af) system='HFS / HFS+';; | |
| b0) system='BootStar';; | |
| b1 | b3) system='SpeedStor / QNX Neutrino Power-Safe';; | |
| b2) system='QNX Neutrino Power-Safe';; | |
| b4 | b6) system='SpeedStor';; | |
| b7) system='BSDI fs';; | |
| b8) system='BSDI swap';; | |
| bb) system='Boot Wizard hidden';; | |
| bc) system='Acronis BackUp';; | |
| be) system='Solaris boot';; | |
| bf) system='Solaris';; | |
| c0) system='CTOS';; | |
| c1) system='DRDOS / secured (FAT-12)';; | |
| c2) system='Hidden Linux (PowerBoot)';; | |
| c3) system='Hidden Linux Swap (PowerBoot)';; | |
| c4) system='DRDOS secured FAT16 < 32M';; | |
| c5) system='DRDOS secured Extended';; | |
| c6) system='DRDOS secured FAT16';; | |
| c7) system='Syrinx';; | |
| cb) system='DR-DOS secured FAT32 (CHS)';; | |
| cc) system='DR-DOS secured FAT32 (LBA)';; | |
| cd) system='CTOS Memdump?';; | |
| ce) system='DR-DOS FAT16X (LBA)';; | |
| cf) system='DR-DOS secured EXT DOS (LBA)';; | |
| d0) system='REAL/32 secure big partition';; | |
| da) system='Non-FS data / Powercopy Backup';; | |
| db) system='CP/M / CTOS / ...';; | |
| dd) system='Dell Media Direct';; | |
| de) system='Dell Utility';; | |
| df) system='BootIt';; | |
| e1) system='DOS access';; | |
| e3) system='DOS R/O';; | |
| e4) system='SpeedStor';; | |
| e8) system='LUKS';; | |
| eb) system='BeOS BFS';; | |
| ec) system='SkyOS';; | |
| ee) system='GPT';; | |
| ef) system='EFI (FAT-12/16/32)';; | |
| f0) system='Linux/PA-RISC boot';; | |
| f1) system='SpeedStor';; | |
| f2) system='DOS secondary';; | |
| f4) system='SpeedStor';; | |
| fb) system='VMware VMFS';; | |
| fc) system='VMware VMswap';; | |
| fd) system='Linux raid autodetect';; | |
| fe) system='LANstep';; | |
| ff) system='Xenix Bad Block Table';; | |
| *) system='Unknown';; | |
| esac | |
| echo "${system}"; | |
| } | |
| ## Function to convert GPT's Partition Type. ## | |
| # | |
| # List from http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs | |
| # | |
| # ABCDEFGH-IJKL-MNOP-QRST-UVWXYZabcdef is stored as | |
| # GHEFCDAB-KLIJ-OPMN-QRST-UVWXYZabcdef (without the dashes) | |
| # | |
| # For easy generation of the following list: | |
| # - Save list in a file "Partition_type_GUIDs.txt" in the folowing format: | |
| # | |
| # Partition Type (OS) <TAB> GUID | |
| # Partition Type (OS) <TAB> GUID | |
| # Partition Type (OS) <TAB> GUID | |
| # | |
| # - Then run the following: | |
| # | |
| # gawk -F '\t' '{ GUID=tolower($2); printf " %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s) system=\"%s\";;\n", substr(GUID,7,1), substr(GUID,8,1), substr(GUID,5,1), substr(GUID,6,1), substr(GUID,3,1), substr(GUID,4,1), substr(GUID,1,1), substr(GUID,2,1), substr(GUID,12,1), substr(GUID,13,1), substr(GUID,10,1), substr(GUID,11,1), substr(GUID,17,1), substr(GUID,18,1), substr(GUID,15,1), substr(GUID,16,1), substr(GUID,20,4), substr(GUID,25,12), $1 } END { print " *) system='-';" }' Partition_type_GUIDs.txt | |
| # | |
| # - Some GUIDs are not unique for one OS. To find them, you can run: | |
| # | |
| # gawk -F "\t" '{print $2}' GUID_Partition_Table_list.txt | sort | uniq -d | grep -f - GUID_Partition_Table_list.txt | |
| # | |
| # Basic data partition (Windows) EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 | |
| # Data partition (Linux) EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 | |
| # ZFS (Mac OS X) 6A898CC3-1DD2-11B2-99A6-080020736631 | |
| # /usr partition (Solaris) 6A898CC3-1DD2-11B2-99A6-080020736631 | |
| # | |
| UUIDToSystem () { | |
| local type=$1 system; | |
| case ${type} in | |
| 00000000000000000000000000000000) system='Unused entry';; | |
| 41ee4d02e733d3119d690008c781f39f) system='MBR partition scheme';; | |
| 28732ac11ff8d211ba4b00a0c93ec93b) system='EFI System partition';; | |
| 4861682149646f6e744e656564454649) system='BIOS Boot partition';; | |
| dee2bfd3af3ddf11ba40e3a556d89593) system="Intel Fast Flash (iFFS) partition (for Intel Rapid Start technology)";; | |
| ## GUIDs that are not unique for one OS ## | |
| a2a0d0ebe5b9334487c068b6b72699c7) system='Data partition (Windows/Linux)';; | |
| c38c896ad21db21199a6080020736631) system='ZFS (Mac OS X) or /usr partition (Solaris)';; | |
| ## Windows GUIDs ## | |
| 16e3c9e35c0bb84d817df92df00215ae) system='Microsoft Reserved Partition (Windows)';; | |
| # Same GUID as old GUID for "Basic data partition (Linux)" | |
| # a2a0d0ebe5b9334487c068b6b72699c7) system='Basic data partition (Windows)';; | |
| aac808588f7ee04285d2e1e90434cfb3) system='Logical Disk Manager (LDM) metadata partition (Windows)';; | |
| a0609baf3114624fbc683311714a69ad) system='Logical Disk Manager (LDM) data partition (Windows)';; | |
| a4bb94ded106404da16abfd50179d6ac) system='Windows Recovery Environment (Windows)';; | |
| 90fcaf377def964e91c32d7ae055b174) system='IBM General Parallel File System (GPFS) partition (Windows)';; | |
| ## HP-UX GUIDs ## | |
| 1e4c8975eb3ad311b7c17b03a0000000) system='Data partition (HP-UX)';; | |
| 28e7a1e2e332d611a6827b03a0000000) system='Service Partition (HP-UX)';; | |
| ## Linux GUIDs ## | |
| # Same GUID as "Basic data partition (Windows)" GUID | |
| # a2a0d0ebe5b9334487c068b6b72699c7) system='Data partition (Linux)';; | |
| # New GUID to avoid that Linux partitions show up as unformatted partitions in Windows. | |
| af3dc60f838472478e793d69d8477de4) system='Data partition (Linux)';; | |
| 0f889da1fc053b4da006743f0f84911e) system='RAID partition (Linux)';; | |
| 6dfd5706aba4c44384e50933c84b4f4f) system='Swap partition (Linux)';; | |
| 79d3d6e607f5c244a23c238f2a3df928) system='Logical Volume Manager (LVM) partition (Linux)';; | |
| 3933a68d0700c060c436083ac8230908) system='Reserved (Linux)';; | |
| ## FreeBSD GUIDs ## | |
| 9d6bbd83417fdc11be0b001560b84f0f) system='Boot partition (FreeBSD)';; | |
| b47c6e51cf6ed6118ff800022d09712b) system='Data partition (FreeBSD)';; | |
| b57c6e51cf6ed6118ff800022d09712b) system='Swap partition (FreeBSD)';; | |
| b67c6e51cf6ed6118ff800022d09712b) system='Unix File System (UFS) partition (FreeBSD)';; | |
| b87c6e51cf6ed6118ff800022d09712b) system='Vinum volume manager partition (FreeBSD)';; | |
| ba7c6e51cf6ed6118ff800022d09712b) system='ZFS partition (FreeBSD)';; | |
| ## Mac OS X GUIDs ## | |
| 005346480000aa11aa1100306543ecac) system='Hierarchical File System Plus (HFS+) partition (Mac OS X)';; | |
| 005346550000aa11aa1100306543ecac) system='Apple UFS (Mac OS X)';; | |
| # c38c896ad21db21199a6080020736631) system='ZFS (Mac OS X)';; | |
| 444941520000aa11aa1100306543ecac) system='Apple RAID partition (Mac OS X)';; | |
| 444941524f5faa11aa1100306543ecac) system='Apple RAID partition offline (Mac OS X)';; | |
| 746f6f420000aa11aa1100306543ecac) system='Apple Boot partition (Mac OS X)';; | |
| 6562614c006caa11aa1100306543ecac) system='Apple Label (Mac OS X)';; | |
| 6f6365526576aa11aa1100306543ecac) system='Apple TV Recovery partition (Mac OS X)';; | |
| 726f74536761aa11aa1100306543ecac) system="Apple Core Storage (i.e. Lion FileVault) partition (Mac OS X)";; | |
| ## Solaris GUIDs ## | |
| 45cb826ad21db21199a6080020736631) system='Boot partition (Solaris)';; | |
| 4dcf856ad21db21199a6080020736631) system='Root partition (Solaris)';; | |
| 6fc4876ad21db21199a6080020736631) system='Swap partition (Solaris)';; | |
| 2b648b6ad21db21199a6080020736631) system='Backup partition (Solaris)';; | |
| # c38c896ad21db21199a6080020736631) system='/usr partition (Solaris)';; | |
| e9f28e6ad21db21199a6080020736631) system='/var partition (Solaris)';; | |
| 39ba906ad21db21199a6080020736631) system='/home partition (Solaris)';; | |
| a583926ad21db21199a6080020736631) system='Alternate sector (Solaris)';; | |
| 3b5a946ad21db21199a6080020736631) system='Reserved partition (Solaris)';; | |
| d130966ad21db21199a6080020736631) system='Reserved partition (Solaris)';; | |
| 6707986ad21db21199a6080020736631) system='Reserved partition (Solaris)';; | |
| 7f23966ad21db21199a6080020736631) system='Reserved partition (Solaris)';; | |
| c72a8d6ad21db21199a6080020736631) system='Reserved partition (Solaris)';; | |
| ## NetBSD GUIDs ## | |
| 328df4490eb1dc11b99b0019d1879648) system='Swap partition (NetBSD)';; | |
| 5a8df4490eb1dc11b99b0019d1879648) system='FFS partition (NetBSD)';; | |
| 828df4490eb1dc11b99b0019d1879648) system='LFS partition (NetBSD)';; | |
| aa8df4490eb1dc11b99b0019d1879648) system='RAID partition (NetBSD)';; | |
| c419b52d0fb1dc11b99b0019d1879648) system='Concatenated partition (NetBSD)';; | |
| ec19b52d0fb1dc11b99b0019d1879648) system='Encrypted partition (NetBSD)';; | |
| ## ChromeOS GUIDs ## | |
| 5d2a3afe324fa741b725accc3285a309) system="ChromeOS kernel";; | |
| 02e2b83c7e3bdd478a3c7ff2a13cfcec) system="ChromeOS rootfs";; | |
| 3d750a2e489eb0438337b15192cb1b5e) system="ChromeOS future use";; | |
| ## Haiku GUIDs ## | |
| 31534642a33bf110802a4861696b7521) system="Haiku BFS (Haiku)";; | |
| ## MidnightBSD GUIDs ## | |
| 5ee4d5857c23e111b4b3e89a8f7fc3a7) system="Boot partition (MidnightBSD) ";; | |
| 5ae4d5857c23e111b4b3e89a8f7fc3a7) system="Data partition (MidnightBSD)";; | |
| 5be4d5857c23e111b4b3e89a8f7fc3a7) system="Swap partition (MidnightBSD)";; | |
| 8bef94037e23e111b4b3e89a8f7fc3a7) system="Unix File System (UFS) partition (MidnightBSD)";; | |
| 5ce4d5857c23e111b4b3e89a8f7fc3a7) system="Vinum volume manager partition (MidnightBSD)";; | |
| 5de4d5857c23e111b4b3e89a8f7fc3a7) system="ZFS partition (MidnightBSD)";; | |
| *) system='-'; | |
| echo 'Unknown GPT Partiton Type' >> ${Unknown_MBR}; | |
| echo ${type} >> ${Unknown_MBR};; | |
| esac | |
| echo "${system}"; | |
| } | |
| ## Function which inserts a comma every third digit of a number. ## | |
| InsertComma () { | |
| echo $1 | sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta'; | |
| } | |
| ## Function to read 4 bytes starting at $1 of device $2 and convert result to decimal. ## | |
| Read4Bytes () { | |
| local start=$1 device=$2; | |
| echo $(hexdump -v -s ${start} -n 4 -e '4 "%u"' ${device}); | |
| } | |
| ## Function to read 8 bytes starting at $1 of device $2 and convert result to decimal. ## | |
| Read8Bytes () { | |
| local start=$1 device=$2; | |
| local first4 second4; | |
| # Get ${first4} and ${second4} bytes at once. | |
| eval $(hexdump -v -s ${start} -n 8 -e '1/4 "first4=%u; " 1/4 "second4=%u"' ${device}); | |
| echo $(( ${second4} * 4294967296 + ${first4} )); | |
| } | |
| ## Functions to pretty print blkid output. ## | |
| BlkidFormat='%-16s %-38s %-10s %s\n'; | |
| BlkidTag () { | |
| echo $(blkid -s $2 -o value $1 2>> ${Trash}); | |
| } | |
| PrintBlkid () { | |
| local part=$1 suffix=$2; | |
| if [ x"$(blkid ${part} 2> ${Tmp_Log})" != x'' ] ; then | |
| printf "${BlkidFormat}" "${part}" "$(BlkidTag ${part} UUID)" "$(BlkidTag ${part} TYPE)" "$(BlkidTag ${part} LABEL)" >> ${BLKID}${suffix}; | |
| else | |
| # blkid -p is not available on all systems. | |
| # This contructs makes sure the "usage" message is not displayed, but catches the "ambivalent" error. | |
| blkid -p "${part}" 2>&1 | grep "^${part}" >> ${BLKID}${suffix}; | |
| fi | |
| } | |
| ## Read and display the partition table and check the partition table for errors. ## | |
| # | |
| # This function can be applied iteratively so extended partiton tables can also be processed. | |
| # | |
| # Function arguments: | |
| # | |
| # - arg 1: HI = HI of hard drive | |
| # - arg 2: StartEx = start sector of the extended Partition | |
| # - arg 3: N = number of partitions in table (4 for regular PT, 2 for logical | |
| # - arg 4: PT_file = file for storing the partition table | |
| # - arg 5: format = display format to use for displaying the partition table | |
| # - arg 6: EPI = PI of the primary extended partition containing the extended partition. | |
| # ( equals "" for hard drive) | |
| # - arg 7: LinuxIndex = Last linux index assigned (the number in sdXY). | |
| ReadPT () { | |
| local HI=$1 StartEx=$2 N=$3 PT_file=$4 format=$5 EPI=$6 Base_Sector; | |
| local LinuxIndex=$7 boot size start end type drive system; | |
| local i=0 boot_hex label limit MBRSig; | |
| drive=${HDName[${HI}]}; | |
| limit=${HDSize[${HI}]}; | |
| dd if=${drive} skip=${StartEx} of=${Tmp_Log} count=1 2>> ${Trash}; | |
| MBRSig=$(hexdump -v -s 510 -n 2 -e '"%04x"' ${Tmp_Log}); | |
| [[ "${MBRSig}" != 'aa55' ]] && echo 'Invalid MBR Signature found.' >> ${PT_file}; | |
| if [[ ${StartEx} -lt ${limit} ]] ; then | |
| # set Base_Sector to 0 for hard drive, and to the start sector of the | |
| # primary extended partition otherwise. | |
| [[ x"${EPI}" = x'' ]] && Base_Sector=0 || Base_Sector=${StartArray[${EPI}]}; | |
| for (( i=0; i < N; i++ )) ; do | |
| dd if=${drive} skip=${StartEx} of=${Tmp_Log} count=1 2>> ${Trash}; | |
| boot_hex=$(hexdump -v -s $((446+16*${i})) -n 1 -e '"%02x"' ${Tmp_Log}); | |
| case ${boot_hex} in | |
| 00) boot=' ';; | |
| 80) boot='* ';; | |
| *) boot='?';; | |
| esac | |
| # Get amd set: partition type, partition start, and partition size. | |
| eval $(hexdump -v -s $((450+16*${i})) -n 12 -e '1/1 "type=%x; " 3/1 "tmp=%x; " 1/4 "start=%u; " 1/4 "size=%u"' ${Tmp_Log}); | |
| if [[ ${size} -ne 0 ]] ; then | |
| if ( ( [ "${type}" = '5' ] || [ "${type}" = 'f' ] ) && [ ${Base_Sector} -ne 0 ] ) ; then | |
| # start sector of an extended partition is relative to the | |
| # start sector of an primary extended partition. | |
| start=$((${start}+${Base_Sector})); | |
| if [[ ${i} -eq 0 ]] ; then | |
| echo 'Extended partition linking to another extended partition.' >> ${PT_file}; | |
| fi | |
| ReadPT ${HI} ${start} 2 ${PT_file} "${format}" ${EPI} ${LinuxIndex}; | |
| else | |
| ((PI++)); | |
| if [[ "${type}" = '5' || "${type}" = 'f' ]] ; then | |
| KindArray[${PI}]='E'; | |
| else | |
| # Start sector of a logical partition is relative to the | |
| # start sector of directly assocated extented partition. | |
| start=$((${start}+${StartEx})); | |
| [[ ${Base_Sector} -eq 0 ]] && KindArray[${PI}]='P' || KindArray[${PI}]='L'; | |
| fi | |
| LinuxIndex=$((${LinuxIndex}+1)); | |
| end=$((${start}+${size}-1)); | |
| [[ "${HDPT[${HI}]}" = 'BootIt' ]] && label="${NamesArray[${EPI}]}_" || label=${drive}; | |
| system=$(HexToSystem ${type}); | |
| printf "${format}" "${label}${LinuxIndex}" "${boot}" $(InsertComma ${start}) "$(InsertComma ${end})" "$(InsertComma ${size})" "${type}" "${system}" >> ${PT_file}; | |
| NamesArray[${PI}]="${label}${LinuxIndex}"; | |
| StartArray[${PI}]=${start}; | |
| EndArray[${PI}]=${end}; | |
| TypeArray[${PI}]=${type}; | |
| SystemArray[${PI}]="${system}"; | |
| SizeArray[${PI}]=${size}; | |
| BootArray[${PI}]="${boot}"; | |
| DriveArray[${PI}]=${HI}; | |
| ParentArray[${PI}]=${EPI}; | |
| ( [[ x"${EPI}" = x'' ]] || [[ x"${DeviceArray[${EPI}]}" != x'' ]] ) && DeviceArray[${PI}]=${drive}${LinuxIndex}; | |
| if [[ "${type}" = '5' || "${type}" = 'f' ]] ; then | |
| ReadPT ${HI} ${start} 2 ${PT_file} "${format}" ${PI} 4; | |
| fi | |
| fi | |
| elif ( [ ${Base_Sector} -ne 0 ] && [ ${i} -eq 0 ] ) ; then | |
| echo 'Empty Partition.' >> ${PT_file}; | |
| else | |
| LinuxIndex=$((${LinuxIndex}+1)); | |
| fi | |
| done | |
| else | |
| echo 'EBR refers to a location outside the hard drive.' >> ${PT_file}; | |
| fi | |
| } | |
| ## Read the GPT partition table (GUID, EFI) ## | |
| # | |
| # Function arguments: | |
| # | |
| # - arg 1: HI = HI of hard drive | |
| # - arg 2: GPT_file = file for storing the GPT partition table | |
| ReadEFI () { | |
| local HI=$1 GPT_file=$2 drive size N=0 i=0 format label PRStart start end type size system; | |
| local attrs attrstr attrs_other j; | |
| drive="${HDName[${HI}]}"; | |
| format='%-10s %5s %14s%14s%14s %s\n'; | |
| printf "${format}" 'Partition' 'Attrs' 'Start Sector' 'End Sector' '# of Sectors' 'System' >> ${GPT_file}; | |
| HDStart[${HI}]=$( Read8Bytes 552 ${drive}); | |
| HDEnd[${HI}]=$( Read8Bytes 560 ${drive}); | |
| HDUUID[${HI}]=$( hexdump -v -s 568 -n 16 -e '/1 "%02x"' ${drive}); | |
| PRStart=$( Read8Bytes 584 ${drive}); | |
| N=$( Read4Bytes 592 ${drive}); | |
| PRStart=$(( ${PRStart}*512)); | |
| PRSize=$( Read4Bytes 596 ${drive}); | |
| for (( i = 0; i < N; i++ )) ; do | |
| type=$(hexdump -v -s $((${PRStart}+${PRSize}*${i})) -n 16 -e '/1 "%02x"' ${drive}); | |
| if [ "${type}" != '00000000000000000000000000000000' ] ; then | |
| ((PI++)); | |
| start=$(Read8Bytes $((${PRStart}+32+${PRSize}*${i})) ${drive}); | |
| end=$( Read8Bytes $((${PRStart}+40+${PRSize}*${i})) ${drive}); | |
| size=$((${end}-${start}+1)); | |
| system=$(UUIDToSystem ${type}); | |
| label=${drive}$((${i}+1)); | |
| # Partition attributes are 8 bytes long and bash arithmetic is signed. | |
| # High bits are used by Windows which automatically overflows computed | |
| # 64 bit number. As we are interested in low bits only, just check it | |
| # and output the rest verbatim if present. | |
| attrs=$(hexdump -v -s $((${PRStart}+48+${PRSize}*${i})) -n 1 -e '/1 "%02x"' ${drive}); | |
| attrs_other=$(hexdump -v -s $((${PRStart}+49+${PRSize}*${i})) -n 7 -e '/1 "%02x"' ${drive}); | |
| if (( 0x${attrs} & 1 )) ; then | |
| attrstr='R'; | |
| else | |
| attrstr=' '; | |
| fi | |
| if (( 0x${attrs} & 2 )) ; then | |
| attrstr="N${attrstr}"; | |
| else | |
| attrstr=" ${attrstr}"; | |
| fi | |
| if (( 0x${attrs} & 4 )) ; then | |
| attrstr="B${attrstr}"; | |
| else | |
| attrstr=" ${attrstr}"; | |
| fi | |
| if (( 0x${attrs} & 8 )) || [ ${attrs_other} != '00000000000000' ] ; then | |
| attrstr="+$attrstr"; | |
| echo >> ${Unknown_MBR}; | |
| echo "${label}: unknown GPT attributes" >> ${Unknown_MBR}; | |
| for (( j = 12; j >= 0; j -= 2 )) ; do | |
| printf '%s' ${attrs_other:j:2} >> ${Unknown_MBR} | |
| done | |
| echo ${attrs} >> ${Unknown_MBR} | |
| fi | |
| printf "${format}" "${label}" "${attrstr}" "$(InsertComma ${start})" "$(InsertComma ${end})" "$(InsertComma ${size})" "${system}" >> ${GPT_file}; | |
| NamesArray[${PI}]=${label}; | |
| DeviceArray[${PI}]=${label}; | |
| StartArray[${PI}]=${start}; | |
| TypeArray[${PI}]=${type}; | |
| SizeArray[${PI}]=${size}; | |
| SystemArray[${PI}]=${system}; | |
| EndArray[${PI}]=${end}; | |
| DriveArray[${PI}]=${HI}; | |
| KindArray[${PI}]='P'; | |
| ParentArray[${PI}]=''; | |
| fi | |
| done | |
| echo >> ${GPT_file}; | |
| echo 'Attributes: R=Required, N=No Block IO, B=Legacy BIOS Bootable, +=More bits set' >> ${GPT_file}; | |
| } | |
| ## Read the Master Partition Table of BootIt NG. ## | |
| # | |
| # Function arguments: | |
| # | |
| # - arg 1: HI = HI of hard drive | |
| # - arg 2: MPT_file = file for storing the MPT | |
| ReadEMBR () { | |
| local HI=$1 MPT_file=$2 drive size N=0 i=0 BINGIndex label start end type format; | |
| local BINGUnknown system StoredPI FirstPI=${FirstPartition[$1]} LastPI=${PI} New; | |
| drive="${HDName[${HI}]}"; | |
| format='%-18s %4s%14s%14s%14s %3s %-15s %3s %2s\n'; | |
| printf "${format}" 'Partition' 'Boot' 'Start Sector' 'End Sector' '# of Sectors' 'Id' 'System' 'Ind' '?' >> ${MPT_file}; | |
| N=$(hexdump -v -s 534 -n 1 -e '"%u"' ${drive}); | |
| for (( i = 0; i < N; i++ )) ; do | |
| New=1; | |
| BINGUnknown=$(hexdump -v -s $((541+28*${i})) -n 1 -e '"%x"' ${drive}); | |
| start=$( hexdump -v -s $((542+28*${i})) -n 4 -e '4 "%u"' ${drive}); | |
| end=$( hexdump -v -s $((546+28*${i})) -n 4 -e '4 "%u"' ${drive}); | |
| BINGIndex=$( hexdump -v -s $((550+28*${i})) -n 1 -e '"%u"' ${drive}); | |
| type=$( hexdump -v -s $((551+28*${i})) -n 1 -e '"%x"' ${drive}); | |
| size=$(( ${end}-${start}+1)); | |
| label=$( hexdump -v -s $((552+28*${i})) -n 15 -e '"%_u"' ${drive}| sed -e 's/nul[^$]*//'); | |
| system=$( HexToSystem ${type}); | |
| printf "${format}" "${label}" "-" "$(InsertComma ${start})" "$(InsertComma ${end})" "$(InsertComma ${size})" "${type}" "${system}" "${BINGIndex}" "${BINGUnknown}" >> ${MPT_file}; | |
| StoredPI=${PI}; | |
| for (( j = FirstPI; j <= LastPI; j++ )); do | |
| if (( ${StartArray[${j}]} == ${start} )) ; then | |
| PI=${j}; | |
| New=0; | |
| break; | |
| fi | |
| done | |
| if [ ${New} -eq 1 ] ; then | |
| ((PI++)); | |
| StoredPI=${PI}; | |
| StartArray[${PI}]=${start}; | |
| TypeArray[${PI}]=${type}; | |
| SizeArray[${PI}]=${size}; | |
| SystemArray[${PI}]=${system}; | |
| EndArray[${PI}]=${end}; | |
| DriveArray[${PI}]=${HI}; | |
| fi | |
| NamesArray[${PI}]=${label}; | |
| if ( [ ${type} = 'f' ] || [ ${type} = '5' ] ) ; then | |
| KindArray[${PI}]='E'; | |
| ParentArray[${PI}]=${PI}; | |
| ReadPT ${HI} ${start} 2 ${MPT_file} "${format}" ${PI} 4; | |
| else | |
| KindArray[${PI}]='P'; | |
| ParentArray[${PI}]=''; | |
| fi | |
| PI=${StoredPI}; | |
| done | |
| } | |
| ## Check partition table for errors. ## | |
| # | |
| # This function checks whether: | |
| # - there are any overlapping partitions | |
| # - the logical partitions are inside the extended partition | |
| # | |
| # Function arguments: | |
| # | |
| # - arg 1: PI_first = PI of first partition to consider | |
| # - arg 2: PI_last = PI of last partition to consider | |
| # - arg 3: CHK_file = file for the error messages | |
| # - arg 4: HI = HI of containing hard drive | |
| CheckPT () { | |
| local PI_first=$1 PI_last=$2 CHK_file=$3 HI=$4; | |
| local Si Ei Sj Ej Ki Kj i j k cyl track head cyl_bound sec_bound; | |
| cyl=${HDCylinder[${HI}]}; | |
| track=${HDTrack[${HI}]}; | |
| head=${HDHead[${HI}]}; | |
| cyl_bound=$((cyl * track * head)); | |
| sec_bound=${HDSize[${HI}]}; | |
| for (( i = PI_first; i <= PI_last; i++ )); do | |
| Si=${StartArray[${i}]}; | |
| Ei=${EndArray[${i}]}; | |
| Ki=${KindArray[${i}]}; | |
| Ni=${NamesArray[${i}]}; | |
| if [[ "${Ei}" -gt "${sec_bound}" ]] ; then | |
| echo "${Ni} ends after the last sector of ${HDName[${HI}]}" >> ${CHK_file}; | |
| elif [[ "${Ei}" -gt "${cyl_bound}" ]] ; then | |
| echo "${Ni} ends after the last cylinder of ${HDName[${HI}]}" >> ${Trash}; | |
| fi | |
| if [[ ${Ki} = "L" ]] ; then | |
| k=${ParentArray[${i}]}; | |
| Sk=${StartArray[${k}]}; | |
| Ek=${EndArray[${k}]}; | |
| Nk=${NamesArray[${k}]}; | |
| [[ ${Si} -le ${Sk} || ${Ei} -gt ${Ek} ]] && echo "the logical partition ${Ni} is not contained in the extended partition ${Nk}" >> ${CHK_file}; | |
| fi | |
| for (( j = i+1; j <= PI_last; j++ )); do | |
| Sj=${StartArray[${j}]}; | |
| Ej=${EndArray[${j}]}; | |
| Kj=${KindArray[${j}]}; | |
| Nj=${NamesArray[${j}]}; | |
| ( !( ( [ "${Ki}" = 'L' ] && [ "${Kj}" = 'E' ] ) || ( [ "${Ki}" = 'E' ] && [ "${Kj}" = 'L' ] ) ) \ | |
| && ( ( [ "${Si}" -lt "${Sj}" ] && [ "${Sj}" -lt "${Ei}" ] ) || ( [ "${Sj}" -lt "${Si}" ] && [ "${Si}" -lt "${Ej}" ] ) ) ) \ | |
| && echo "${Ni} overlaps with ${Nj}" >> ${CHK_file}; | |
| done | |
| done | |
| } | |
| ## Syslinux ## | |
| # | |
| # Determine the exact Syslinux version ("SYSLINUX - version - date"), display | |
| # the offset to the second stage, check the internal checksum (if not correct, | |
| # the ldlinux.sys file, probably moved), display the directory to which | |
| # Syslinux is installed. | |
| syslinux_info () { | |
| local partition=$1; | |
| # Magic number used by Syslinux: | |
| local LDLINUX_MAGIC='fe02b23e'; | |
| local LDLINUX_BSS LDLINUX_SECTOR2 ADV_2SECTORS; | |
| local sect1ptr0_offset sect1ptr0 sect1ptr1 tmp; | |
| local magic_offset syslinux_version syslinux_dir; | |
| # Patch area variables: | |
| local pa_version pa_size pa_hexdump_format pa_magic pa_instance pa_data_sectors; | |
| local pa_adv_sectors pa_dwords pa_checksum pa_maxtransfer pa_epaoffset; | |
| local pa_ldl_sectors pa_dir_inode; | |
| # Extended patch area variables: | |
| local epa_size epa_hexdump_format epa_advptroffset epa_diroffset epa_dirlen; | |
| local epa_subvoloffset epa_subvollen epa_secptroffset epa_secptrcnt; | |
| local epa_sect1ptr0 epa_sect1ptr1 epa_raidpatch epa_syslinuxbanner; | |
| # ADV magic numbers: | |
| local ADV_MAGIC_HEAD='a52f2d5a'; # Head signature | |
| local ADV_MAGIC_TAIL='64bf28dd'; # Tail signature | |
| local ADV_MAGIC_CHECKSUM=$((0xa3041767)); # Magic used for calculation ADV checksum | |
| # ADV variables: | |
| local ADVoffset ADV_calculated_checksum ADV_read_checksum ADVentry_offset; | |
| local tag='999' tag_len label; | |
| local csum; | |
| # Clear previous Syslinux message string. | |
| Syslinux_Msg=''; | |
| # Read first 512 bytes of partition and convert to hex (ldlinux.bss) | |
| LDLINUX_BSS=$(hexdump -v -n512 -e '/1 "%02x"' ${partition}); | |
| # Look for LDLINUX_MAGIC: bytes 504-507 | |
| if [ "${LDLINUX_BSS:1008:8}" = "${LDLINUX_MAGIC}" ] ; then | |
| # Syslinux 4.04-pre5 and higher. | |
| pa_version=4; # Syslinux 4.xx patch area | |
| # The offset of Sect1Load in LDLINUX_BSS can be found by doing a | |
| # bitwise XOR of bytes 508-509 (little endian) with 0x1b << 9. | |
| # sect1ptr0_offset starts 2 bytes furter than Sect1Load. | |
| sect1ptr0_offset=$(( ( 0x${LDLINUX_BSS:1018:2}${LDLINUX_BSS:1016:2} ^ ( 0x1b << 9 ) ) + 2 )); | |
| # Get "boot sector offset" (in sectors) of sector 1 ptr LSW: sect1ptr0 | |
| # Get "boot sector offset" (in sectors) of sector 1 ptr MSW: sect1ptr1 | |
| eval $(hexdump -v -s ${sect1ptr0_offset} -n 10 -e '1/4 "sect1ptr0=%u; " 1/2 "tmp=%u; " 1/4 "sect1ptr1=%u;"' ${partition}); | |
| else | |
| # Check if bytes 508-509 = "7f00". | |
| if [ "${LDLINUX_BSS:1016:4}" = '7f00' ] ; then | |
| # Syslinux 3.xx | |
| pa_version=3; # Syslinux 3.xx patch area | |
| # Get "boot sector offset" (in sectors) of sector 1 ptr LSW: sect1ptr0 | |
| eval $(hexdump -v -s 504 -n 4 -e '1/4 "sect1ptr0=%u;"' ${partition}); | |
| else | |
| # Syslinux 4.00 - Syslinux 4.04-pre4. | |
| pa_version=4; # Syslinux 4.xx patch area | |
| # Search for offset to sect1ptr0 (only found in Syslinux 4.xx) | |
| # 66 b8 xx xx xx xx 66 ba xx xx xx xx bb 00 | |
| # [sect1ptr0] [sect1ptr1] | |
| # | |
| # Start searching for this hex string after the DOS superblock: byte 0x5a = 90 | |
| eval $(echo ${LDLINUX_BSS:180:844} \ | |
| | ${AWK} '{ mask_offset=match($0,"66b8........66ba........bb00"); \ | |
| if (mask_offset == "0") { print "sect1ptr0_offset=0;" } \ | |
| else { print "sect1ptr0_offset=" (mask_offset -1 ) / 2 + 2 + 90 } }'); | |
| if [ ${sect1ptr0_offset} -ne 0 ] ; then | |
| # Syslinux 4.00 - Syslinux 4.04-pre4. | |
| # Get "boot sector offset" (in sectors) of sector 1 ptr LSW: sect1ptr0 | |
| # Get "boot sector offset" (in sectors) of sector 1 ptr MSW: sect1ptr1 | |
| eval $(hexdump -v -s ${sect1ptr0_offset} -n 10 -e '1/4 "sect1ptr0=%u; " 1/2 "tmp=%u; " 1/4 "sect1ptr1=%u;"' ${partition}); | |
| else | |
| Syslinux_Msg='No evidence that this is realy a Syslinux boot sector.'; | |
| return; | |
| fi | |
| fi | |
| fi | |
| Syslinux_Msg="Syslinux looks at sector ${sect1ptr0} of ${partition} for its second stage."; | |
| # Start reading 0.5MiB (more than enough) from second sector of the Syslinux | |
| # bootloader (= first sector of ldlinux.sys). | |
| dd if=${partition} of=${Tmp_Log} skip=${sect1ptr0} count=1000 bs=512 2>> ${Trash}; | |
| # Get second sector of the Syslinux bootloader (= first sector of ldlinux.sys) | |
| # and convert to hex. | |
| LDLINUX_SECTOR2=$(hexdump -v -n 512 -e '/1 "%02x"' ${Tmp_Log}); | |
| # Look for LDLINUX_MAGIC (8 bytes aligned) in sector 2 of the Syslinux bootloader. | |
| for (( magic_offset = $((0x10)); magic_offset < $((0x50)); magic_offset = magic_offset + 8 )); do | |
| if [ "${LDLINUX_SECTOR2:$(( ${magic_offset} * 2 )):8}" = ${LDLINUX_MAGIC} ] ; then | |
| if [ ${pa_version} -eq 4 ] ; then | |
| # Syslinux 4.xx patch area. | |
| # Patch area size: 4+4+2+2+4+4+2+2 = 4*4 + 4*2 = 24 bytes | |
| pa_size='24'; | |
| # Get pa_magic, pa_instance, pa_data_sectors, pa_adv_sectors, pa_dwords, pa_checksum, pa_maxtransfer and pa_epaoffset. | |
| pa_hexdump_format='1/4 "pa_magic=0x%04x; " 1/4 "pa_instance=0x%04x; " 1/2 "pa_data_sectors=%u; " 1/2 "pa_adv_sectors=%u; " 1/4 "pa_dwords=0x%u; " 1/4 "pa_checksum=0x%04x; " 1/2 "pa_maxtransfer=%u; " 1/2 "pa_epaoffset=%u;"'; | |
| eval $(hexdump -v -s ${magic_offset} -n ${pa_size} -e "${pa_hexdump_format}" ${Tmp_Log}); | |
| else | |
| # Syslinux 3.xx patch area. | |
| # Patch area size: 4+4+2+2+4+4 = 4*4 + 2*2 = 20 bytes | |
| pa_size='20'; | |
| # Get pa_magic, pa_instance, pa_dwords, pa_ldl_sectors and pa_checksum. | |
| # - pa_dwords: Total dwords starting at ldlinux_sys not including ADVs. | |
| # - pa_ldl_sectors: Number of sectors - (bootsec + sector2) but including any ADVs. | |
| pa_hexdump_format='1/4 "pa_magic=0x%04x; " 1/4 "pa_instance=0x%04x; " 1/2 "pa_dwords=%u; " 1/2 "pa_ldl_sectors=%u; " 1/4 "pa_checksum=0x%04x; " 1/4 "pa_dir_inode=%u;"'; | |
| eval $(hexdump -v -s ${magic_offset} -n ${pa_size} -e "${pa_hexdump_format}" ${Tmp_Log}); | |
| # Calulate pa_data_sectors: number of sectors (not including ldlinux.bss = first sector of Syslinux). | |
| # - divide by 128 (128 dwords / 512 byte sector) | |
| pa_data_sectors=$(( ${pa_dwords} / 128 )); | |
| # If total dwords is not exactly a multiple of 128, round up the number of sectors (add 1). | |
| if [ $(( ${pa_dwords}%128 )) -ne 0 ] ; then | |
| pa_data_sectors=$(( ${pa_data_sectors} + 1 )); | |
| fi | |
| # Some Syslinux 4.00-pre?? releases are different: | |
| # - have Syslinux 3.xx signature: bytes 508-509 = "7f00". | |
| # - have the "boot sector offset" (in sectors) of sector 1 ptr LSW (bytes 504-507) | |
| # for sect1ptr0, like Syslinux 3.xx. | |
| # - have like Syslinux 4.xx, the same location for pa_data_sectors. | |
| # | |
| # If pa_dwords is less than 1024, it contains the value of pa_data_sectors: | |
| # - if less and pa_words would really be pa_words: ldlinux.sys would be smaller than 4 kiB | |
| # - if more and pa_words would really be pa_data_sectors: ldlinux.sys would be more than 500 kiB | |
| if [ ${pa_dwords} -lt 1024 ] ; then | |
| pa_data_sectors=${pa_dwords}; | |
| fi | |
| fi | |
| # Get the "SYSLINUX - version - date" string. | |
| syslinux_version=$(hexdump -v -e '"%_p"' -s 2 -n $(( ${magic_offset} - 2 )) ${Tmp_Log}); | |
| syslinux_version="${syslinux_version% \.*}"; | |
| # Overwrite the "boot sector type" variable, which was set before calling this function, | |
| # with a more exact Syslinux version number. | |
| BST="${syslinux_version}"; | |
| # Check integrity of Syslinux: | |
| # - Checksum starting at ldlinux.sys, stopping before the ADV part. | |
| # - checksum start = LDLINUX_MAGIC - [sum of dwords]. | |
| # - add each dword to the checksum value. | |
| # - the value of the checksum after adding all dwords of ldlinux.sys should be 0. | |
| csum=$(hexdump -v -n $(( ${pa_data_sectors} * 512)) -e '/4 "%u\n"' ${Tmp_Log} \ | |
| | ${AWK} 'BEGIN { csum=4294967296-1051853566 } { csum=(csum + $1)%4294967296 } END {print csum}' ); | |
| if [ ${csum} -ne 0 ] ; then | |
| Syslinux_Msg="${Syslinux_Msg} The integrity check of Syslinux failed."; | |
| return; | |
| fi | |
| if [ ${pa_version} -eq 4 ] ; then | |
| # Extended patch area size: 11*2 = 22 bytes | |
| epa_size='22'; | |
| # Get epa_advptroffset, epa_diroffset, epa_dirlen, epa_subvoloffset, epa_subvollen, | |
| # epa_secptroffset, epa_secptrcnt, epa_sect1ptr0, epa_sect1ptr1 and epa_raidpatch. | |
| epa_hexdump_format='1/2 "epa_advptroffset=%u; " 1/2 "epa_diroffset=%u; " 1/2 "epa_dirlen=%u; " 1/2 "epa_subvoloffset=%u; " 1/2 "epa_subvollen=%u; " 1/2 "epa_secptroffset=%u; " 1/2 "epa_secptrcnt=%u; " 1/2 "epa_sect1ptr0=%u; " 1/2 "epa_sect1ptr1=%u; " 1/2 "epa_raidpatch=%u; " 1/2 "epa_syslinuxbanner=%u;"'; | |
| eval $(hexdump -v -s ${pa_epaoffset} -n ${epa_size} -e "${epa_hexdump_format}" ${Tmp_Log}); | |
| # Get the Syslinux install directory. | |
| syslinux_dir=$(hexdump -v -e '"%_p"' -s ${epa_diroffset} -n ${epa_dirlen} ${Tmp_Log}); | |
| syslinux_dir=${syslinux_dir%%\.*}; | |
| Syslinux_Msg="${Syslinux_Msg} ${syslinux_version:0:8} is installed in the ${syslinux_dir} directory."; | |
| # In Syslinux 4.04 and higher, the whole Syslinux banner is not in the first sector of ldlinux.sys. | |
| # Only the "SYSLINUX - version" string is still located in the first sector. | |
| # epa_syslinuxbanner points to the whole "SYSLINUX - version - date" string. | |
| if [ ${epa_syslinuxbanner} -lt $(( ${pa_data_sectors} * 512 )) ] ; then | |
| # Get the "SYSLINUX - version - date" string. | |
| tmp=$(hexdump -v -e '"%_p"' -s $(( ${epa_syslinuxbanner} + 2 )) -n 100 ${Tmp_Log}); | |
| # Check if we have Syslinux 4.04 or higher, which suppport the epa_syslinuxbanner field | |
| # by comparing the first 8 bytes ("SYSLINUX") of the Syslinux banner from sector 1 with | |
| # the 8 bytes to which epa_syslinuxbanner points. | |
| if [ x"${tmp:0:8}" = x"${syslinux_version:0:8}" ] ; then | |
| syslinux_version="${tmp%%\.No DEFAULT*}"; | |
| # Overwrite the "boot sector type" variable, which was set before calling this function, | |
| # with a more exact Syslinux version number. | |
| BST="${syslinux_version}"; | |
| fi | |
| fi | |
| # ADV stuff starts here. | |
| if [ ${pa_adv_sectors} -ne 2 ] ; then | |
| Syslinux_Msg="${Syslinux_Msg} There are ${pa_adv_sectors} ADV sectors instead of 2."; | |
| return; | |
| fi | |
| # Get the ADV offset. | |
| ADVoffset=$(( pa_data_sectors * 512 )); | |
| # Get the ADV. | |
| ADV_2SECTORS=$(hexdump -v -s ${ADVoffset} -n 1024 -e '/1 "%02x"' ${Tmp_Log}); | |
| # Check if the 2 ADV sectors are exactly the same. | |
| if [ "${ADV_2SECTORS:0:1024}" != "${ADV_2SECTORS:1024:1024}" ] ; then | |
| Syslinux_Msg="${Syslinux_Msg} The 2 ADV sectors are not the same (corrupt)."; | |
| return; | |
| fi | |
| # Check if the ADV area contains the ADV head and tail magic. | |
| if ( [ "${ADV_2SECTORS:0:8}" = "${ADV_MAGIC_HEAD}" ] && [ "${ADV_2SECTORS:1016:8}" = "${ADV_MAGIC_TAIL}" ] ) ; then | |
| # Caculate the ADV checksum. | |
| ADV_calculated_checksum=$(hexdump -v -s $(( ${ADVoffset} + 8 )) -n $((512 - 3*4)) -e '/4 "%u\n"' ${Tmp_Log} \ | |
| | ${AWK} 'BEGIN { csum='${ADV_MAGIC_CHECKSUM}' } { csum=(csum - $1 + 4294967296)%4294967296 } END { print csum }'); | |
| ADV_read_checksum=$(hexdump -s $(( ${ADVoffset} + 4 )) -n 4 -e '/4 "%u\n"' ${Tmp_Log}); | |
| if [ ${ADV_calculated_checksum} -eq ${ADV_read_checksum} ] ; then | |
| # Get the info stored in the ADV area: | |
| # | |
| # maximum 2 entries can be stored in the ADV, which have the following layout: | |
| # - byte 1 : tag ==> 0 = no entry, 1 = boot-once entry, 2 = menu-save entry | |
| # - byte 2 : tag_len ==> length of label string | |
| # - byte 3 - (3 + tag_len) : label ==> label name that will be used | |
| # First entry starts a offset 8. | |
| ADVentry_offset=8; | |
| until eval $(hexdump -s $(( ${ADVoffset} + ${ADVentry_offset} )) -n $((512 - 3*4)) \ | |
| -e '1/1 "tag=%u; " 1/1 "tag_len=%u; label='\''" 498 "%_p"' ${Tmp_Log}; | |
| printf "'"); | |
| [ ${tag} -eq 0 ] ; do | |
| if [ ${tag_len} -gt 0 ] ; then | |
| label=${label:0:${tag_len}}; | |
| fi | |
| case ${tag} in | |
| 1) Syslinux_Msg="${Syslinux_Msg} ${syslinux_version:0:8}'s ADV is set to boot label \"${label}\" next boot only.";; | |
| 2) Syslinux_Msg="${Syslinux_Msg} ${syslinux_version:0:8}'s ADV is set to boot label \"${label}\" by default.";; | |
| esac | |
| # Adjust the ADVentry_offset, so it points to the next entry. | |
| ADVentry_offset=$(( ${ADVentry_offset} + ${tag_len} + 2 )); | |
| done | |
| else | |
| Syslinux_Msg="${Syslinux_Msg} The integrity check of the ADV area failed."; | |
| fi | |
| else | |
| Syslinux_Msg="${Syslinux_Msg} The ADV head and tail magic bytes were not found."; | |
| fi | |
| fi | |
| return; | |
| fi | |
| done | |
| # LDLINUX_MAGIC not found. | |
| Syslinux_Msg="${Syslinux_Msg} It is very unlikely that Syslinux is (still) installed. The second stage could not be found."; | |
| } | |
| ## Grub Legacy ## | |
| # | |
| # Determine the embeded location of stage 2 in a stage 1 file, | |
| # look for the stage 2 and, if found, determine the | |
| # the location and the path of the embedded menu.lst. | |
| stage2_loc () { | |
| local stage1="$1" HI; | |
| offset=$(hexdump -v -s 68 -n 4 -e '4 "%u"' "${stage1}"); | |
| dr=$(hexdump -v -s 64 -n 1 -e '1/1 "%u"' "${stage1}"); | |
| pa='T'; | |
| Grub_Version=''; | |
| for HI in ${!HDName[@]}; do | |
| hdd=${HDName[${HI}]}; | |
| if [ ${offset} -lt ${HDSize[HI]} ] ; then | |
| tmp=$(dd if=${hdd} skip=${offset} count=1 2>> ${Trash} | hexdump -v -n 4 -e '"%x"'); | |
| if [[ "${tmp}" = '3be5652' || "${tmp}" = 'bf5e5652' ]] ; then | |
| # stage2 files were found. | |
| dd if=${hdd} skip=$((offset+1)) count=1 of=${Tmp_Log} 2>> ${Trash}; | |
| pa=$(hexdump -v -s 10 -n 1 -e '"%d"' ${Tmp_Log}); | |
| stage2_hdd=${hdd}; | |
| Grub_String=$(hexdump -v -s 18 -n 94 -e '"%_u"' ${Tmp_Log}); | |
| Grub_Version=$(echo ${Grub_String} | sed -e 's/nul[^$]*//'); | |
| BL=${BL}${Grub_Version}; | |
| menu=$(echo ${Grub_String} | sed -e 's/[^\/]*//' -e 's/nul[^$]*//'); | |
| menu=${menu%% *}; | |
| fi | |
| fi | |
| done | |
| dr=$((${dr}-127)); | |
| Stage2_Msg="looks at sector ${offset}"; | |
| if [ "${dr}" -eq 128 ] ; then | |
| Stage2_Msg="${Stage2_Msg} of the same hard drive"; | |
| else | |
| Stage2_Msg="${Stage2_Msg} on boot drive #${dr}"; | |
| fi | |
| Stage2_Msg="${Stage2_Msg} for the stage2 file"; | |
| if [ "${pa}" = "T" ] ; then | |
| # no stage 2 file found. | |
| Stage2_Msg="${Stage2_Msg}, but no stage2 files can be found at this location."; | |
| else | |
| pa=$((${pa}+1)); | |
| Stage2_Msg="${Stage2_Msg}. A stage2 file is at this location on ${stage2_hdd}. Stage2 looks on"; | |
| if [ "${pa}" -eq 256 ] ; then | |
| Stage2_Msg="${Stage2_Msg} the same partition"; | |
| else | |
| Stage2_Msg="${Stage2_Msg} partition #${pa}"; | |
| fi | |
| Stage2_Msg="${Stage2_Msg} for ${menu}."; | |
| fi | |
| } | |
| ## Grub2 ## | |
| # | |
| # Collect fragments of core.img using information encoded in the first | |
| # block (diskboot.img). | |
| # | |
| grub2_read_blocklist () { | |
| local hdd="$1"; | |
| local core_img_file="$2"; | |
| local sector_nr_low sector_nr_high sector_nr fragment_size; | |
| local fragment_offset=1 block_list=500; | |
| # Assemble fragments from "hdd" passed to grub2_info. | |
| # Each block list entry is 12 bytes long and consists of | |
| # 8 bytes = fragment start absolute disk offset in sectors of 512 bytes | |
| # 2 bytes = fragment size in sectors of 512 bytes | |
| # 2 bytes = memory segment to load fragment into | |
| # Entries start at the end of the first sector of core.img and | |
| # go down. End marker is all zeroes. | |
| # | |
| # Blocklists were changed to 64 bit in 2006, so all versions BIS detects | |
| # should have it. | |
| # | |
| # Older versions of hexdump do not support 8 byte integers, so read | |
| # high and low words separately. | |
| while [ ${block_list} -gt 12 ] ; do | |
| sector_nr_low=$(hexdump -v -n 4 -s ${block_list} -e '1/4 "%u"' ${core_img_file}); | |
| sector_nr_high=$(hexdump -v -n 4 -s $((block_list+4)) -e '1/4 "%u"' ${core_img_file}); | |
| let "sector_nr = (sector_nr_high << 32) + sector_nr_low"; | |
| if [ ${sector_nr} -eq 0 ] ; then | |
| return; | |
| fi | |
| fragment_size=$(hexdump -v -n 2 -s $((block_list+8)) -e '1/2 "%u"' ${core_img_file}); | |
| dd if="${hdd}" of=${core_img_file} skip=${sector_nr} seek=${fragment_offset} count=${fragment_size} 2>> ${Trash} || return; | |
| let "fragment_offset += fragment_size"; | |
| let "block_list -= 12"; | |
| done | |
| } | |
| ## Grub2 ## | |
| # | |
| # Determine the embeded module name. This function implements manual | |
| # parsing of ELF information to avoid dependency on binutils or similar. | |
| # | |
| grub2_modname () | |
| { | |
| local modfile=$1; | |
| local file_size=$2; | |
| local e_ehsize sht_offset sht_entsize sht_num sht_shdrndx sht_strtab; | |
| local sht_strtabsize s_nameidx s_type s_name m_offset m_size; | |
| local i=0; | |
| # ELF header is at least 52 bytes in size | |
| if [ "${file_size}" -lt 52 ] ; then | |
| return; | |
| fi | |
| # ELF Magic + CLASS32 + LSB + VERSION | |
| if [ "$(hexdump -n 7 -e '4/1 "%02x" 3/1 "%x"' "${modfile}")" != '7f454c46111' ] ; then | |
| return; | |
| fi | |
| # RELOCATABLE + MACHINE + VERSION | |
| if [ "$(hexdump -s 16 -n 8 -e '2/2 "%x" 1/4 "%x"' "${modfile}")" != '131' ] ; then | |
| return; | |
| fi | |
| # ELF header size | |
| e_ehsize=$(hexdump -s 40 -n 2 -e '"%u"' "${modfile}") | |
| if [ "${e_ehsize}" -lt 52 -o "${e_ehsize}" -gt "${file_size}" ] ; then | |
| return; | |
| fi | |
| # Offset of section headers table | |
| sht_offset=$(hexdump -s 32 -n 4 -e '"%u"' "${modfile}") | |
| if [ "${sht_offset}" -lt "${e_ehsize}" -o "${sht_offset}" -ge "${file_size}" ] ; then | |
| return; | |
| fi | |
| # Size of section header | |
| sht_entsize=$(hexdump -s 46 -n 2 -e '"%u"' "${modfile}") | |
| # Number of section headers | |
| sht_num=$(hexdump -s 48 -n 2 -e '"%u"' "${modfile}") | |
| if [ "${sht_entsize}" -eq 0 -o "${sht_num}" -eq 0 -o $((sht_offset + sht_entsize*sht_num)) -gt "${file_size}" ] ; then | |
| return; | |
| fi | |
| # Index of section names string table | |
| sht_shdrndx=$(hexdump -s 50 -n 2 -e '"%u"' "${modfile}") | |
| if [ "${sht_shdrndx}" -ge "${sht_num}" ] ; then | |
| return; | |
| fi | |
| # Offset of section names string table | |
| sht_strtab=$(hexdump -s $((sht_offset + $((sht_shdrndx*sht_entsize)) + 16)) -n 4 -e '"%u"' "${modfile}"); | |
| if [ "${sht_strtab}" -lt "${e_ehsize}" -o "${sht_strtab}" -ge "${file_size}" ] ; then | |
| return; | |
| fi | |
| # Size of section names string table | |
| sht_strtabsize=$(hexdump -s $((sht_offset + $((sht_shdrndx*sht_entsize)) + 20)) -n 4 -e '"%u"' "${modfile}"); | |
| if [ "${sht_strtabsize}" -eq 0 -o "${sht_strtabsize}" -gt "$((file_size-sht_strtab))" ] ; then | |
| return; | |
| fi | |
| while [ "${i}" -lt $((sht_entsize*sht_num)) ] ; do | |
| s_nameidx=$(hexdump -s $((sht_offset + i)) -n 4 -e '"%u"' "${modfile}"); | |
| if [ "${s_nameidx}" -lt "${sht_strtabsize}" ] ; then | |
| s_type=$(hexdump -s $((sht_offset + i + 4)) -n 4 -e '"%u"' "${modfile}"); | |
| # PROGBITS | |
| if [ "${s_type}" -eq 1 ] ; then | |
| s_name=$(hexdump -s $((sht_strtab + s_nameidx)) -n "${sht_strtabsize}" -e "1/${sht_strtabsize} \"%s\"" "${modfile}"); | |
| if [ "${s_name}" = '.modname' ] ; then | |
| m_offset=$(hexdump -s $((sht_offset + i + 16)) -n 4 -e '"%u"' "${modfile}"); | |
| m_size=$(hexdump -s $((sht_offset + i + 20)) -n 4 -e '"%u"' "${modfile}"); | |
| if [ $((m_offset + m_size)) -lt "${file_size}" ] ; then | |
| hexdump -s "${m_offset}" -n "${m_size}" -e "/${m_size} \"%s\"" "${modfile}"; | |
| return | |
| fi | |
| fi | |
| fi | |
| fi | |
| : $((i+=sht_entsize)) | |
| done | |
| # Display "???" as indication that parsing failed | |
| printf '%s' '???' | |
| return | |
| } | |
| ## Grub2 ## | |
| # | |
| # Determine the (embeded) location of core.img for a Grub2 boot.img file, | |
| # determine the path of the grub2 directory and look for an embedded config file. | |
| # | |
| grub2_info () { | |
| local stage1="$1" hdd="$2"; | |
| # When $grub2_version is "1.99-2.00", we want to override this value | |
| # with a more exact value later (needs to be a global variable). | |
| grub2_version="$3"; | |
| # Have we got plain file or need to collect full core.img from blocklists? | |
| local core_source="$4"; | |
| local sector_offset drive_offset directory_offset sector_nr drive_nr drive_nr_hex; | |
| local partition core_dir embedded_config HI magic core_img_found=0 embedded_config_found=0; | |
| local total_module_size kernel_image_size compressed_size offset_lzma lzma_uncompressed_size; | |
| local grub_module_info_offset grub_module_magic grub_modules_offset grub_modules_size; | |
| local grub_module_type grub_module_size grub_module_header_offset grub_modules_end_offset; | |
| local lzma_compressed_size reed_solomon_redundancy reed_solomon_length boot_dev boot_drive; | |
| local core_img_flavour='detect' modname all_modules need_core_prologue=0; | |
| local grub_module_header_next; | |
| > ${core_img_file_type_2} | |
| case "${grub2_version}" in | |
| 1.96) sector_offset='68'; drive_offset='76'; directory_offset='553';; | |
| 1.97-1.98) sector_offset='92'; drive_offset='100'; directory_offset='540';; | |
| 1.99|1.99-2.00|2.00) sector_offset='92'; drive_offset='100';; | |
| esac | |
| # Offset to core.img (in sectors). | |
| sector_nr=$(hexdump -v -s ${sector_offset} -n 4 -e '4 "%u"' "${stage1}" 2>> ${Trash}); | |
| # BIOS drive number on which grub2 looks for its second stage (=core.img): | |
| # - "0xff" means that grub2 will use the BIOS drive number passed via the DL register. | |
| # - if this value isn't "0xff", that value will used instead. | |
| # Since version 1.97 GRUB2 is using only 0xff. We cannot reliably determine BIOS numbers | |
| # anyway, so just skip core.img detection in this case. | |
| drive_nr_hex=$(hexdump -v -s ${drive_offset} -n 1 -e '"0x%02x"' "${stage1}" 2>> ${Trash}); | |
| drive_nr=$(( ${drive_nr_hex} - 127 )); | |
| if [ "${drive_nr_hex}" != '0xff' ] ; then | |
| Grub2_Msg="is configured to load core.img from BIOS drive ${drive_nr} (${drive_nr_hex}) instead of using the boot drive passed by the BIOS"; | |
| return | |
| fi | |
| Grub2_Msg="looks at sector ${sector_nr} of the same hard drive for core.img"; | |
| for HI in ${!HDName[@]} ; do | |
| # If the drive name passed to grub2_info matches the drive name of the current | |
| # value of HDName, see if the sector offset to core.img is smaller than the | |
| # total number of sectors of that drive. | |
| if [ ${hdd} = ${HDName[${HI}]} ] ; then | |
| if [ ${sector_nr} -lt ${HDSize[HI]} ] ; then | |
| if [ "${core_source}" = 'file' ] ; then | |
| # Use "file" passed to grub2_info directly. | |
| dd if="${stage1}" of=${core_img_file} skip=${sector_nr} count=1024 2>> ${Trash}; | |
| else | |
| # Use "hdd" passed to grub2_info. | |
| # First make sure to collect core.img fragments. Read the first block of | |
| # core.img and assemble it further from blocklists | |
| dd if="${hdd}" of=${core_img_file} skip=${sector_nr} count=1 2>> ${Trash}; | |
| grub2_read_blocklist "${hdd}" ${core_img_file}; | |
| fi | |
| magic=$(hexdump -v -n 4 -e '/1 "%02x"' ${core_img_file}); | |
| # 5256be1b - upstream diskboot.S | |
| # 5256be6f - unknown | |
| # 52e82801 - Ubuntu diskboot.S with conditional message | |
| # 52bff481 - RHEL7 diskboot.S with patched out message | |
| # 5256be63 - trustedgrub2 1.4 | |
| # 5256be56 - diskboot.S with mjg TPM patches (e.g. in openSUSE Tumbleweed) | |
| case "${magic}" in | |
| '5256be1b'|'5256be6f'|'52e82801'|'52bff481'|'5256be63'|'5256be56') | |
| core_img_found=1;; | |
| esac | |
| if [ ${core_img_found} -eq 1 ] ; then | |
| if ( [ "${grub2_version}" = '1.99' ] || [ "${grub2_version}" = '1.99-2.00' ] || [ "${grub2_version}" = '2.00' ] ) ; then | |
| # Find the last 8 bytes of lzma_decode to find the offset of the lzma_stream: | |
| # - v1.99: "d1 e9 df fe ff ff 00 00" | |
| # - v2.00: "d1 e9 df fe ff ff 66 90" (pad bytes NOP) | |
| # "d1 e9 df fe ff ff 8d" (pad bytes LEA ...) | |
| # | |
| # arvidjaar@gmail.com: | |
| # final directive in startup_raw.S is .p2align 4 which | |
| # (at least using current GCC/GAS) adds lea instructions | |
| # (8d...). Exact format and length apparently depend on pad | |
| # size and may be on toolkit version. So just accept anything | |
| # starting with lea. | |
| # | |
| # FIXME what if it ends on exact 16 byte boundary? | |
| eval $(hexdump -v -n 10000 -e '1/1 "%02x"' ${core_img_file} | \ | |
| ${AWK} '{ found_at=match($0, "d1e9dffeffff" ); if (found_at == "0") { print "offset_lzma=0" } \ | |
| else { print "offset_lzma=" ((found_at - 1 ) / 2 ) + 8 "; lzma_decode_last8bytes=" substr($0,found_at,16) ";" } }'); | |
| if [ "${grub2_version}" = '1.99-2.00' ] ; then | |
| if ( [ "${lzma_decode_last8bytes}" = "d1e9dffeffff6690" ] || [ "${lzma_decode_last8bytes:0:14}" = "d1e9dffeffff8d" ] ) ; then | |
| grub2_version='2.00'; | |
| else | |
| grub2_version='1.99'; | |
| fi | |
| fi | |
| else | |
| # Grub2 (v1.96 and v1.97-1.98). | |
| partition=$(hexdump -v -s 532 -n 1 -e '"%d"' ${core_img_file}); | |
| core_dir=$(hexdump -v -s ${directory_offset} -n 64 -e '"%_u"' ${core_img_file} | sed 's/nul[^$]*//'); | |
| fi | |
| if [ "${grub2_version}" = '1.99' ] ; then | |
| # For Grub2 (v1.99), the core_dir is just at the beginning of the compressed part of core.img. | |
| # Get grub_total_module_size : byte 0x208-0x20b of embedded core.img ==> byte 520 | |
| # Get grub_kernel_image_size : byte 0x20c-0x20f of embedded core.img ==> byte 524 | |
| # Get grub_compressed_size : byte 0x210-0x213 of embedded core.img ==> byte 528 | |
| # Get grub_install_dos_part : byte 0x214-0x218 of embedded core.img ==> byte 532 --> only 1 byte needed (partition) | |
| eval $(hexdump -v -s 520 -n 13 -e '1/4 "total_module_size=%u; " 1/4 "kernel_image_size=%u; " 1/4 "compressed_size=%u; " 1 "partition=%d;"' ${core_img_file}); | |
| # Do we have xz or lzma installed? | |
| if [ "${UNLZMA}" != 'none' ] ; then | |
| if [ ${offset_lzma} -ne 0 ] ; then | |
| # Correct the offset to the lzma stream, when 8 subsequent bytes of zeros are at the start of this offset. | |
| if [ $(hexdump -v -s ${offset_lzma} -n 8 -e '1/1 "%02x"' ${core_img_file}) = '0000000000000000' ] ; then | |
| offset_lzma=$(( ${offset_lzma} + 8 )); | |
| fi | |
| # Calculate the uncompressed size to which the compressed lzma stream needs to be expanded. | |
| lzma_uncompressed_size=$(( ${total_module_size} + ${kernel_image_size} - ${offset_lzma} + 512 )); | |
| # Make lzma header (13 bytes): ${lzma_uncompressed_size} must be displayed in little endian format. | |
| printf '\x5d\x00\x00\x01\x00'$( printf '%08x' $((${lzma_uncompressed_size} - ${offset_lzma} + 512 )) \ | |
| | ${AWK} '{printf "\\x%s\\x%s\\x%s\\x%s", substr($0,7,2), substr($0,5,2), substr($0,3,2), substr($0,1,2)}' )'\x00\x00\x00\x00' > ${Tmp_Log}; | |
| # Get lzma_stream, add it after the lzma header and decompress it. | |
| dd if=${core_img_file} bs=${offset_lzma} skip=1 count=$((${lzma_uncompressed_size} / ${offset_lzma} + 1)) 2>> ${Trash} \ | |
| | cat ${Tmp_Log} - | ${UNLZMA} 2>> ${Trash} > ${core_img_file_unlzma}; | |
| # Get core dir. | |
| core_dir=$( hexdump -v -n 64 -e '"%_c"' ${core_img_file_unlzma} ); | |
| # Remove "\0"s at the end. | |
| core_dir="${core_dir%%\\0*}"; | |
| # Offset of the grub_module_info structure in the uncompressed part. | |
| grub_module_info_offset=$(( ${kernel_image_size} - ${offset_lzma} + 512 )); | |
| eval $(hexdump -v -n 12 -s ${grub_module_info_offset} -e '"grub_module_magic=" 4/1 "%_c" 1/4 "; grub_modules_offset=%u; " 1/4 "grub_modules_size=%u;"' ${core_img_file_unlzma}); | |
| # Check for the existence of the grub_module_magic. | |
| if [ x"${grub_module_magic}" = x'mimg' ] ; then | |
| # Embedded grub modules found. | |
| grub_modules_end_offset=$(( ${grub_module_info_offset} + ${grub_modules_size} )); | |
| grub_module_header_offset=$(( ${grub_module_info_offset} + ${grub_modules_offset} )); | |
| # Traverse through the list of modules and check if it is a config module. | |
| while [ ${grub_module_header_offset} -lt ${grub_modules_end_offset} ] ; do | |
| eval $(hexdump -v -n 8 -s ${grub_module_header_offset} -e '1/4 "grub_module_type=%u; " 1/4 "grub_module_size=%u;"' ${core_img_file_unlzma}); | |
| if [ ${grub_module_type} -eq 2 ] ; then | |
| # This module is an embedded config file. | |
| embedded_config_found=1; | |
| embedded_config=$( hexdump -v -n $(( ${grub_module_size} - 8 )) -s $(( ${grub_module_header_offset} + 8 )) -e '"%_c"' ${core_img_file_unlzma} ); | |
| # Remove "\0" at the end. | |
| embedded_config=$( printf "${embedded_config%\\0}" ); | |
| break; | |
| fi | |
| grub_module_header_offset=$(( ${grub_module_header_offset} + ${grub_module_size} )); | |
| done | |
| fi | |
| fi | |
| else | |
| # When xz or lzma isn't available, we can't get the core_dir, but we still can show some info. | |
| core_dir='??'; | |
| echo 'To be able to see for which directory Grub2 (v1.99) looks for, install "xz" or "lzma".' >&2; | |
| fi | |
| elif [ "${grub2_version}" = '2.00' ] ; then | |
| # For Grub2 (v2.00), the core_dir is stored in the compressed part of core.img in the same | |
| # way as the modules and embedded config file. | |
| # Get grub_compressed_size : byte 0x208-0x20b of embedded core.img ==> byte 520 | |
| # Get grub_uncompressed_size : byte 0x20c-0x20f of embedded core.img ==> byte 524 | |
| # Get grub_reed_solomon_redundancy : byte 0x210-0x213 of embedded core.img ==> byte 528 | |
| # Get grub_no_reed_solomon_length : byte 0x214-0x217 of embedded core.img ==> byte 532 | |
| # Get grub_boot_dev : byte 0x218-0x21a of embedded core.img ==> byte 536 ( should also contain the grub_boot_drive field ) | |
| # Get grub_boot_drive : byte 0x21b of embedded core.img ==> byte 539 | |
| eval $(hexdump -v -s 520 -n 20 -e '1/4 "lzma_compressed_size=%u; " 1/4 "lzma_uncompressed_size=%u; " 1/4 "reed_solomon_redundancy=%u; " 1/4 "reed_solomon_length=%u; boot_dev=" 3/1 "%x" 1 "; boot_drive=%d;"' ${core_img_file}); | |
| # Do we have xz or lzma installed? | |
| if [ "${UNLZMA}" != 'none' ] ; then | |
| if [ ${offset_lzma} -ne 0 ] ; then | |
| # Grub2 pads the start of the lzma stream to a 16 bytes boundary. | |
| # Correct the offset to the lzma stream if necessary | |
| # Current GCC adds lea instructions as pad bytes | |
| padsize=$(( (((${offset_lzma} + 15) >> 4) << 4) - ${offset_lzma} )); | |
| if ( [ ${padsize} -gt 0 ] ) ; then | |
| offset_lzma=$(( ${offset_lzma} + ${padsize} )); | |
| fi | |
| # Make lzma header (13 bytes): ${lzma_uncompressed_size} must be displayed in little endian format. | |
| printf '\x5d\x00\x00\x01\x00'$( printf '%08x' ${lzma_uncompressed_size} \ | |
| | ${AWK} '{printf "\\x%s\\x%s\\x%s\\x%s", substr($0,7,2), substr($0,5,2), substr($0,3,2), substr($0,1,2)}' )'\x00\x00\x00\x00' > ${Tmp_Log}; | |
| # Get lzma_stream, add it after the lzma header and decompress it. | |
| dd if=${core_img_file} bs=${offset_lzma} skip=1 count=${lzma_compressed_size} 2>> ${Trash} \ | |
| | cat ${Tmp_Log} - | ${UNLZMA} 2>> ${Trash} > ${core_img_file_unlzma}; | |
| # Get offset to the grub_module_info structure in the uncompressed part. | |
| eval $(hexdump -v -s 19 -n 4 -e '1/4 "grub_module_info_offset=%u;"' ${core_img_file_unlzma}); | |
| eval $(hexdump -v -n 12 -s ${grub_module_info_offset} -e '"grub_module_magic=" 4/1 "%_c" 1/4 "; grub_modules_offset=%u; " 1/4 "grub_modules_size=%u;"' ${core_img_file_unlzma}); | |
| # Check for the existence of the grub_module_magic. | |
| if [ x"${grub_module_magic}" = x'mimg' ] ; then | |
| # Embedded grub modules found. | |
| grub_modules_end_offset=$(( ${grub_module_info_offset} + ${grub_modules_size} )); | |
| grub_module_header_offset=$(( ${grub_module_info_offset} + ${grub_modules_offset} )); | |
| # Traverse through the list of modules and check if it is a config module. | |
| # Upstream GRUB2 supports following module types: | |
| # 0 - ELF modules; may be included multiple times | |
| # 1 - memory disk image; should be included just once | |
| # 2 - embedded initial configuration code; should be included just once | |
| # 3 - initial value of ${prefix} variable. Device part may be omitted, | |
| # in which case device is guessed at startup | |
| # 4 - public GPG keyring used for file signature checking; | |
| # may be included multiple times | |
| # | |
| # All parts are optional (although in practice | |
| # at least drivers for disk and filesystem must be | |
| # present). | |
| # | |
| # Since RPM version 2.00-10 fedora includes patch that | |
| # inserts additional module type after the first one, | |
| # thus shifting all numbers starting with 1. So | |
| # embedded config and prefix become 3 and 4 on fedora. | |
| while [ ${grub_module_header_offset} -lt ${grub_modules_end_offset} ] ; do | |
| if [ $(( ${grub_modules_end_offset} - ${grub_module_header_offset} )) -lt 8 ] ; then | |
| echo 'Remaining space in GRUB2 module list too short for a module' >&2; | |
| all_modules="${all_modules} <short>"; | |
| need_core_prologue=1; | |
| break | |
| fi | |
| eval $( hexdump -v -n 8 -s ${grub_module_header_offset} -e '1/4 "grub_module_type=%u; " 1/4 "grub_module_size=%u;"' ${core_img_file_unlzma} ); | |
| # Next module is always aligned on 4 bytes boundary on i386, | |
| # but sometimes grub stores shorter size. Make sure to adjust it. | |
| grub_module_header_next=$(( ${grub_module_header_offset} + 8 + (((${grub_module_size} - 8 + 3) >> 2) << 2) )); | |
| if [ ${grub_module_header_next} -gt ${grub_modules_end_offset} ] ; then | |
| printf 'GRUB2 module size too large; skipping remaining modules. Size left: %d\n' $(( {grub_modules_end_offset} - ${grub_module_header_offset} )) >&2; | |
| all_modules="${all_modules} <skipped>"; | |
| need_core_prologue=1; | |
| break | |
| fi | |
| if [ ${grub_module_type} -eq 0 ] ; then | |
| # Regular ELF module | |
| dd count=$(( grub_module_size - 8 )) skip=$(( grub_module_header_offset + 8 )) if=${core_img_file_unlzma} of=${GRUB200_Module} bs=1 2>> ${Trash}; | |
| modname=$(grub2_modname ${GRUB200_Module} $(( grub_module_size - 8 ))); | |
| if [ -n "${modname}" ] ; then | |
| all_modules="${all_modules} ${modname}"; | |
| need_core_prologue=1; | |
| fi | |
| elif [ ${grub_module_type} -eq 1 ] ; then | |
| # "stale" ELF module on fedora or memory disk everywhere else | |
| if [ ${core_img_flavour} = 'detect' ] ; then | |
| if [ "$(hexdump -v -n 4 -s $((grub_module_header_offset+8)) -e '"%c"' ${core_img_file_unlzma})" = $'\x7f''ELF' ] ; then | |
| # fedora "stale" ELF module | |
| # TODO display Fedora stale modules | |
| core_img_flavour='fedora'; | |
| else | |
| core_img_flavour='upstream'; | |
| fi | |
| fi | |
| elif [ ${grub_module_type} -eq 2 ] ; then | |
| # memory disk on fedora or embedded config everywhere else | |
| if [ ${core_img_flavour} = 'detect' ] ; then | |
| # Normally core.img will have prefix which is easier to detect, | |
| # so leave detection as last resort. | |
| dd if=${core_img_file_unlzma} of=${core_img_file_type_2} bs=1 skip=$((grub_module_header_offset+8)) count=$((grub_module_size-8)) 2>> ${Trash}; | |
| fi | |
| if [ ${core_img_flavour} = 'upstream' ] ; then | |
| # This module is an embedded config file. | |
| embedded_config_found=1; | |
| need_core_prologue=1; | |
| # Remove padding starting with the first "\0" at the end. | |
| embedded_config=$( hexdump -v -n $(( ${grub_module_size} - 8 )) -s $(( ${grub_module_header_offset} + 8 )) -e '"%_c"' ${core_img_file_unlzma} | sed -e 's/\(\\0\).*$//'); | |
| fi | |
| elif [ ${grub_module_type} -eq 3 ] ; then | |
| # embedded config on fedora or prefix everywhere else | |
| if [ ${core_img_flavour} = 'detect' ] ; then | |
| # if it looks like file name, assume prefix | |
| if [[ "$(hexdump -v -n 1 -s $(( grub_module_header_offset + 8 )) -e '"%c"' ${core_img_file_unlzma})" == [/\(] ]] ; then | |
| core_img_flavour='upstream'; | |
| else | |
| core_img_flavour='fedora'; | |
| fi | |
| fi | |
| if [ ${core_img_flavour} = 'upstream' ] ; then | |
| # This module contains the prefix. | |
| # Get core dir. | |
| # Remove padding "\0"'s at the end. | |
| core_dir=$( hexdump -v -n $(( ${grub_module_size} - 8 )) -s $(( ${grub_module_header_offset} + 8 )) -e '"%_c"' ${core_img_file_unlzma} | sed -e 's/\(\\0\)\+$//'); | |
| elif [ ${core_img_flavour} = 'fedora' ] ; then | |
| # This module is an embedded config file. | |
| embedded_config_found=1; | |
| need_core_prologue=1; | |
| # Remove padding starting with the first "\0" at the end. | |
| embedded_config=$( hexdump -v -n $(( ${grub_module_size} - 8 )) -s $(( ${grub_module_header_offset} + 8 )) -e '"%_c"' ${core_img_file_unlzma} | sed -e 's/\(\\0\).*$//'); | |
| fi | |
| elif [ ${grub_module_type} -eq 4 ] ; then | |
| # prefix on fedora or GPG keyring everywhere else | |
| if [ ${core_img_flavour} = 'detect' ] ; then | |
| # if it looks like file name, assume prefix | |
| # GPG ring normall has \x99 as first byte | |
| if [[ "$(hexdump -v -n 1 -s $(( grub_module_header_offset + 8 )) -e '"%c"' ${core_img_file_unlzma})" == [/\(] ]] ; then | |
| core_img_flavour='fedora'; | |
| else | |
| core_img_flavour='upstream'; | |
| fi | |
| fi | |
| if [ ${core_img_flavour} = 'fedora' ] ; then | |
| # This module contains the prefix. | |
| # Get core dir. | |
| # Remove padding "\0"'s at the end. | |
| core_dir=$( hexdump -v -n $(( ${grub_module_size} - 8 )) -s $(( ${grub_module_header_offset} + 8 )) -e '"%_c"' ${core_img_file_unlzma} | sed -e 's/\(\\0\)\+$//'); | |
| elif [ ${core_img_flavour} = 'upstream' ] ; then | |
| # TODO list GPG keyring | |
| : | |
| fi | |
| fi | |
| grub_module_header_offset=${grub_module_header_next}; | |
| done | |
| fi | |
| fi | |
| else | |
| # When xz or lzma isn't available, we can't get the core_dir, but we still can show some info. | |
| core_dir='??'; | |
| echo 'To be able to see for which directory Grub2 (v2.00) looks for, install "xz" or "lzma".' >&2; | |
| fi | |
| fi | |
| fi | |
| fi | |
| fi | |
| done | |
| if [ "${grub2_version}" = '2.00' ] ; then | |
| if [ -s ${core_img_file_type_2} ] ; then | |
| if [ "${core_img_flavour}" = 'detect' ] ; then | |
| # Neither type 1, 3 or 4 modules were present. So we have either | |
| # embedded config or memory disk. | |
| if type file > /dev/null 2>&1 ; then | |
| if [[ "$(LC_ALL=C file ${core_img_file_type_2})" == *"ASCII text"* ]] ; then | |
| # upstream embedded config | |
| core_img_flavour='upstream'; | |
| fi | |
| fi | |
| fi | |
| if [ ${core_img_flavour} = 'upstream' ] ; then | |
| embedded_config_found=1; | |
| need_core_prologue=1; | |
| # Remove padding starting with the first "\0" at the end. | |
| embedded_config=$( hexdump -v -e '"%_c"' ${core_img_file_type_2} | sed -e 's/\(\\0\).*$//' ); | |
| fi | |
| fi | |
| fi | |
| if [ ${core_img_found} -eq 0 ] ; then | |
| # core.img not found. | |
| Grub2_Msg="${Grub2_Msg}, but core.img can not be found at this location"; | |
| else | |
| # core.img found. | |
| Grub2_Msg="${Grub2_Msg}. core.img is at this location"; | |
| # In GRUB 2.00 core.img prefix is optional | |
| if [ -n "${core_dir}" ]; then | |
| Grub2_Msg="${Grub2_Msg} and looks for ${core_dir}"; | |
| if [ -n "${partition}" ]; then | |
| partition=$(( ${partition} + 1 )); | |
| if [ ${partition} -eq 255 ] ; then | |
| Grub2_Msg="${Grub2_Msg} on this drive"; | |
| else | |
| Grub2_Msg="${Grub2_Msg} in partition ${partition}"; | |
| fi | |
| fi | |
| fi | |
| if [ ${need_core_prologue} -eq 1 ] ; then | |
| Grub2_Msg=$(printf "${Grub2_Msg}. It also embeds following components:"); | |
| fi | |
| if [ -n "${all_modules}" ] ; then | |
| all_modules="${all_modules# }"; | |
| Grub2_Msg=$(printf "${Grub2_Msg}\n\nmodules\n--------------------------------------------------------------------------------\n${all_modules}\n--------------------------------------------------------------------------------"); | |
| fi | |
| if [ ${embedded_config_found} -eq 1 ] ; then | |
| # Embedded config file found. | |
| Grub2_Msg=$(printf "${Grub2_Msg}\n\nconfig script\n--------------------------------------------------------------------------------\n${embedded_config}\n--------------------------------------------------------------------------------"); | |
| fi | |
| fi | |
| } | |
| ## Get embedded menu for grub4dos (grldr/grub.exe) and wee (installed in the MBR). ## | |
| # | |
| # Function arguments: | |
| # | |
| # - arg 1: source = file (grub4dos) / device (WEE) | |
| # - arg 2: titlename = first part of the title that needs to be displayed | |
| # | |
| get_embedded_menu () { | |
| local source=$1 titlename=$2; | |
| # Check if magic bytes that go before the embedded menu, are present. | |
| offset_menu=$(dd if="${source}" count=4 bs=128k 2>> ${Trash} | hexdump -v -e '/1 "%02x"' | grep -b -o 'b0021ace000000000000000000000000'); | |
| if [ -n "${offset_menu}" ] ; then | |
| # Magic found. | |
| titlebar_gen "${titlename}" " embedded menu"; | |
| echo '--------------------------------------------------------------------------------' >> "${Log1}"; | |
| # Calcutate the exact offset to the embedded menu. | |
| offset_menu=$(( ( ${offset_menu%:*} / 2 ) + 16 )); | |
| dd if="${source}" count=1 skip=1 bs=${offset_menu} 2>> ${Trash} | ${AWK} 'BEGIN { RS="\0" } { if (NR == 1) print $0 }' >> "${Log1}"; | |
| echo '--------------------------------------------------------------------------------' >> "${Log1}"; | |
| fi | |
| } | |
| ## Show the location (offset) of a file on a disk ## | |
| # | |
| # Function arguments: | |
| # | |
| # - arg 1: filename1 | |
| # - arg 2: filename2 | |
| # - arg 3: filename3 | |
| # - ...... | |
| # | |
| # Return values: | |
| # | |
| # - 0: None of the provided filenames was found. | |
| # - 1: At least one of the provided filenames was found. | |
| last_block_of_file () { | |
| local display='0'; | |
| local BlockSize Fragments Filefrag_Format EndGiByte EndGByte; | |
| # Remove an existing ${Tmp_Log} log. | |
| rm -f ${Tmp_Log}; | |
| # "$@" contains all function arguments (filenames). | |
| for file in "$@" ; do | |
| if [[ -f "${file}" ]] && [[ -s "${file}" ]] && FileNotMounted "${mountname}/${file}" "${mountname}" ; then | |
| # There are 4 versions of e2fsprogs filefrag output. | |
| # In all cases final line could be "1 extent" instead. | |
| # | |
| # v1 | |
| # Blocksize of file %s is %d | |
| # File size of %s is %lld (%d blocks) | |
| # %s: %d extents found[, perfection would be %d extent%s] | |
| # | |
| # v2 | |
| # Blocksize of file %s is %d | |
| # File size of %s is %lld (%d blocks) | |
| # First block: %ld | |
| # Last block: %ld | |
| # %s: %d extents found[, perfection would be %d extent%s] | |
| # | |
| # v3 | |
| # File size of %s is %lld (%ld block%s, blocksize %d) | |
| # ext logical physical expected length flags | |
| # 0 nnn nnn nnn xxx | |
| # 1 nnn nnn nnn nnn xxx | |
| # ... | |
| # %s: %d extents found[, perfection would be %d extent%s] | |
| # | |
| # v4 | |
| # File size of %s is %llu (%lu block%s of %d bytes) | |
| # ext: logical_offset: physical_offset: length: expected: flags: | |
| # 0: nnn.. nnn: nnn.. nnn: nnn: xxx | |
| # 1: nnn.. nnn: nnn.. nnn: nnn: nnn: xxx | |
| # ... | |
| # %s: %d extents found[, perfection would be %d extent%s] | |
| # | |
| # FIXME e2fsprogs filefrag output "Last block:", not "Last Block:". | |
| # Was there yet another filefrag implementation? | |
| # | |
| # XXX Original code metioned filefrag output that can show last | |
| # block but not number of extents. Unless there was some other | |
| # implementation of filefrag, it does not match e2fsprogs sources. | |
| # | |
| # XXX Can we hit files with spaces (field count is wrong then)? | |
| eval $(filefrag -v "${file}" \ | |
| | ${AWK} -F ' ' 'BEGIN { blocksize=0; expected=0; extents=0; ext_ind=0; last_ext_loc=0; ext_length=0; filefrag_format=""; last_block=0 } \ | |
| { if ( $1 == "Blocksize" ) { blocksize=$6; filefrag_format="v1"; }; \ | |
| if ( filefrag_format == "v1" ) { \ | |
| if ( $1$2 ~ "LastBlock:" ) { last_block = $3 }; \ | |
| } else if ( $(NF-1) == "blocksize" ) { \ | |
| blocksize = substr($NF,0,length($NF) - 1); \ | |
| filefrag_format = "v3"; \ | |
| } else if ( $(NF) == "bytes)" ) { \ | |
| blocksize = $(NF-1); \ | |
| filefrag_format = "v4"; \ | |
| FS=" *|: *|[.][.] *"; \ | |
| } \ | |
| if ( expected != 0 ) { \ | |
| if ( filefrag_format == "v3" && ext_ind == $1 ) { \ | |
| if ( last_ext_loc < $3 ) { \ | |
| last_ext_loc = $3; \ | |
| if ( substr($0, expected, 1) == " " ) { \ | |
| ext_length = $4; } \ | |
| else { \ | |
| ext_length = $5; \ | |
| } \ | |
| } \ | |
| } else if ( filefrag_format == "v4" && ext_ind == $2 ) { \ | |
| if ( last_block < $6 ) { \ | |
| last_block = $6; \ | |
| } \ | |
| } \ | |
| ext_ind += 1; \ | |
| } else { \ | |
| if ( filefrag_format == "v3" && $4 == "expected" ) { \ | |
| expected= index($0,"expected") + 7; \ | |
| } else if ( filefrag_format == "v4" && $2 == "ext" ) { \ | |
| expected = 1; \ | |
| } \ | |
| } \ | |
| if ( $3 == "extents" ) { \ | |
| extents = $2; \ | |
| } else if ( $3 == "extent" ) { \ | |
| extents = 1; \ | |
| } \ | |
| } \ | |
| END { \ | |
| if ( filefrag_format == "v3" ) { \ | |
| last_block = last_ext_loc + ext_length; \ | |
| } \ | |
| printf "BlockSize=" blocksize "; Fragments=" extents "; Filefrag_Format=" filefrag_format "; "; \ | |
| if ( last_block == 0 ) { \ | |
| printf "EndGiByte=??; EndGByte=??;" \ | |
| } else { \ | |
| EndByte = last_block * blocksize + 512 * '${start}'; \ | |
| printf "EndGiByte=%.9f; EndGByte=%.9f;", EndByte / 1024 ^ 3, EndByte / 1000 ^ 3; \ | |
| } \ | |
| }'); | |
| if [ "${Filefrag_Format}" = '' ] ; then | |
| echo "Unknown filefrag output format" >&2; | |
| return 0; | |
| fi | |
| if [ "${BlockSize}" -ne 0 ] ; then | |
| if [ "${Fragments}" -eq 0 ] ; then | |
| printf "%14s = %-14s %s\n" "${EndGiByte}" "${EndGByte}" "${file}" >> ${Tmp_Log}; | |
| else | |
| printf "%14s = %-14s %-45s %2s\n" "${EndGiByte}" "${EndGByte}" "${file}" "${Fragments}" >> ${Tmp_Log}; | |
| fi | |
| fi | |
| # Return 1, when we find at least one of the provided filenames, | |
| # so we know that we need to display the content of ${Tmp_Log} later. | |
| display=1; | |
| fi | |
| done | |
| return ${display}; | |
| } | |
| ## Get_Partition_Info search a partition for information relevant for booting. ## | |
| # | |
| # Function arguments: | |
| # | |
| # - arg 1: log = local version of RESULT.txt | |
| # - arg 2: log1 = local version of log1 | |
| # - arg 3: part = device for the partition | |
| # - arg 4: name = descriptive name for the partition | |
| # - arg 5: mountname = path where partition will be mounted. | |
| # - arg 6: kind = kind of the partition | |
| # - arg 7: start = starting sector of the partition | |
| # - arg 8: end = ending sector of the partition | |
| # - arg 9: system = system of the partition | |
| # - arg 10: PI = PI of the partition, (equal to "", if not a regular partition) | |
| Get_Partition_Info() { | |
| local Log="$1" Log1="$2" part="$3" name="$4" mountname="$5" kind="$6" start="$7" end="$8" system="$9" PI="${10}"; | |
| local line size=$((end-start)) BST='' BSI='' BFI='' OS='' BootFiles='' Bytes80_to_83='' Bytes80_to_81='' offset=''; | |
| local offset_menu='' part_no_mount=0 com32='' com32_version=''; | |
| echo "Searching ${name} for information... "; | |
| PrintBlkid ${part}; | |
| # Type of filesystem according to blkid. | |
| type=$(BlkidTag ${part} TYPE); | |
| [ "${system}" = 'BIOS Boot partition' ] && type='BIOS Boot partition'; | |
| [ -n ${PI} ] && FileArray[${PI}]=${type}; | |
| # Display partition subtitle of 80 characters width. | |
| line='________________________________________________________________________________'; | |
| line=${line:$(( ${#name} + 2 ))}; | |
| printf '%s: %s\n\n' "${name}" "${line}" >> "${Log}"; | |
| # Directory where the partition will be mounted. | |
| mkdir -p "${mountname}"; | |
| # Check for extended partion. | |
| if ( [ "${kind}" = 'E' ] && [ x"${type}" = x'' ] ) ; then | |
| type='Extended Partition'; | |
| # Don't display the error message from blkid for extended partition. | |
| cat ${Tmp_Log} >> ${Trash}; | |
| else | |
| cat ${Tmp_Log} >&2; | |
| fi | |
| # Display the File System Type. | |
| echo " File system: ${type}" >> "${Log}"; | |
| # Get bytes 0x80-0x83 of the Volume Boot Record (VBR). | |
| Bytes80_to_83=$(hexdump -v -n 4 -s $((0x80)) -e '4/1 "%02x"' ${part}); | |
| # Get bytes 0x80-0x81 of the Volume Boot Record (VBR). | |
| Bytes80_to_81="${Bytes80_to_83:0:4}"; | |
| case ${Bytes80_to_81} in | |
| 0069) BST='ISOhybrid (Syslinux 3.72-3.73)';; | |
| 010f) BST='HP Recovery';; | |
| 019d) BST='BSD4.4: FAT32';; | |
| 0211) BST='Dell Utility: FAT16';; | |
| 0488) BST="Grub2's core.img";; | |
| 0689) BST='Syslinux 3.00-3.52'; | |
| syslinux_info ${part}; | |
| BSI="${BSI} ${Syslinux_Msg}";; | |
| 0734) BST='Dos_1.0';; | |
| 0745) BST='Windows Vista: FAT32';; | |
| 089e) BST='MSDOS5.0: FAT16';; | |
| 08cd) BST='Windows 2000/XP: NTFS';; | |
| 0b60) BST='Dell Utility: FAT16';; | |
| 0bd0) BST='MSWIN4.1: FAT32';; | |
| 0e00) BST='Dell Utility: FAT16';; | |
| 0fb6) BST='ISOhybrid with partition support (Syslinux 3.82-3.86)';; | |
| 2a00) BST='ReactOS';; | |
| 2d5e) BST='Dos 1.1';; | |
| 31c0) BST='Syslinux 4.03 or higher'; | |
| syslinux_info ${part} '4.03'; | |
| BSI="${BSI} ${Syslinux_Msg}";; | |
| 31d2) BST="Grub2's core.img";; | |
| 3a5e) BST='Recovery: FAT32';; | |
| 407c) BST='ISOhybrid (Syslinux 3.82-4.04)';; | |
| 4216) BST='Grub4Dos: NTFS';; | |
| 4445) BST='Dell Restore: FAT32';; | |
| 55aa) case ${Bytes80_to_83} in | |
| 55aa750a) BST='Grub4Dos: FAT32';; | |
| 55aa7506) # Get bytes 0x110-0x111 of the Volume Boot Record (VBR). | |
| Bytes110_to_111=$(hexdump -v -n 2 -s $((0x110)) -e '2/1 "%02x"' ${part}); | |
| case "${Bytes110_to_111}" in | |
| 9090) BST='Windows Vista: NTFS';; | |
| 2810) BST='Windows 7/2008: NTFS';; | |
| 0a13) BST='Windows 8/2012: NTFS';; | |
| esac;; | |
| esac;; | |
| 55cd) BST='FAT32';; | |
| 5626) BST='Grub4Dos: EXT2/3/4';; | |
| 638b) BST='Freedos: FAT32';; | |
| 6616) BST='Windows 7/2008: FAT16';; | |
| 696e) BST='FAT16';; | |
| 6974) BST='BootIt: FAT16';; | |
| 6f65) BST='BootIt: FAT16';; | |
| 6f6e) BST='-';; # 'MSWIN4.1: Fat 32' | |
| 6f74) BST='FAT32';; | |
| 7405) BST='Windows 7/2008: FAT32';; | |
| 7815) case ${Bytes80_to_83} in | |
| 7815b106) BST='Syslinux 3.53-3.86'; | |
| syslinux_info ${part}; | |
| BSI="${BSI} ${Syslinux_Msg}";; | |
| 7815* ) BST='FAT32';; | |
| esac;; | |
| 7cc6) BST='MSWIN4.1: FAT32';; | |
| # 7cc6) BST='Win_98';; | |
| 7e1e) BST='Grub4Dos: FAT12/16';; | |
| 8a56) BST='Acronis SZ: FAT32';; | |
| 83e1) BST='ISOhybrid with partition support (Syslinux 4.00-4.04)';; | |
| 8ec0) BST='Windows XP: NTFS';; | |
| 8ed0) BST='Dell Recovery: FAT32';; | |
| b106) BST='Syslinux 4.00-4.02'; | |
| syslinux_info ${part}; | |
| BSI="${BSI} ${Syslinux_Msg}";; | |
| b600) BST='Dell Utility: FAT16';; | |
| b6c6) BST='ISOhybrid with partition support (Syslinux 3.81)';; | |
| b6d1) BST='Windows XP: FAT32';; | |
| e2f7) BST='FAT32, Non Bootable';; | |
| e879) BST='ISOhybrid (Syslinux 3.74-3.80)';; | |
| e9d8) BST='Windows Vista/7: NTFS';; | |
| f6c1) BST='Windows 8/2012: FAT32';; | |
| f6f6) BST='- (cleared BS by FDISK)';; | |
| fa33) BST='Windows XP: NTFS';; | |
| fbc0) BST='ISOhybrid (Syslinux 3.81)';; | |
| ## If Grub or Grub 2 is in the boot sector, investigate the embedded information. ## | |
| 48b4) BST='Grub2 (v1.96)'; | |
| grub2_info ${part} ${drive} '1.96' 'partition'; | |
| BSI="${BSI} Grub2 (v1.96) is installed in the boot sector of ${name} and ${Grub2_Msg}.";; | |
| 7c3c) BST='Grub2 (v1.97-1.98)'; | |
| grub2_info ${part} ${drive} '1.97-1.98' 'partition'; | |
| BSI="${BSI} Grub2 (v1.97-1.98) is installed in the boot sector of ${name} and ${Grub2_Msg}.";; | |
| 0020) BST='Grub2 (v1.99-2.00)'; | |
| grub2_info ${part} ${drive} '1.99-2.00' 'partition'; | |
| BSI="${BSI} Grub2 (v${grub2_version}) is installed in the boot sector of ${name} and ${Grub2_Msg}.";; | |
| aa75 | 5272) BST='Grub Legacy'; | |
| stage2_loc ${part}; | |
| BSI="${BSI} Grub Legacy (v${Grub_Version}) is installed in the boot sector of ${name} and ${Stage2_Msg}";; | |
| ## If Lilo is in the VBR, look for map file ## | |
| 8053) BST='LILO'; | |
| # 0x20-0x23 contains the offset of /boot/map. | |
| offset=$(hexdump -v -s 32 -n 4 -e '"%u"' ${part}); | |
| BSI="${BSI} LILO is installed in boot sector of ${part} and looks at sector ${offset} of ${drive} for the \"map\" file,"; | |
| # check whether offset is on the hard drive. | |
| if [ ${offset} -lt ${size} ] ; then | |
| tmp=$(dd if=${drive} skip=${offset} count=1 2>> ${Trash} | hexdump -v -s 508 -n 4 -e '"%_p"'); | |
| if [ "${tmp}" = 'LILO' ] ; then | |
| BSI="${BSI} and the \"map\" file was found at this location."; | |
| else | |
| BSI="${BSI} but the \"map\" file was not found at this location."; | |
| fi | |
| else | |
| BSI="${BSI} but the \"map\" file was not found at this location."; | |
| fi;; | |
| 0000) # If the first two bytes are zero, the boot sector does not contain any boot loader. | |
| BST='-';; | |
| esac | |
| if [ x"${BST}" = 'x' ] ; then | |
| BST='Unknown'; | |
| printf "Unknown BootLoader on ${name}\n\n" >> ${Unknown_MBR}; | |
| hexdump -n 512 -C ${part} >> ${Unknown_MBR}; | |
| echo >> ${Unknown_MBR}; | |
| fi | |
| # Display the boot sector type. | |
| echo " Boot sector type: ${BST}" >> "${Log}"; | |
| ## Investigate the Boot Parameter Block (BPB) of a NTFS partition. ## | |
| if [ "${type}" = 'ntfs' ] ; then | |
| offset=$(hexdump -v -s 28 -n 4 -e '"%u"' ${part}); | |
| BPB_Part_Size=$(hexdump -v -s 40 -n 4 -e '"%u"' ${part}) | |
| Comp_Size=$(( (${BPB_Part_Size} - ${size}) / 256 )) | |
| SectorsPerCluster=$(hexdump -v -s 13 -n 1 -e '"%d"' ${part}); | |
| MFT_Cluster=$(hexdump -v -s 48 -n 4 -e '"%d"' ${part}); | |
| MFT_Sector=$(( ${MFT_Cluster} * ${SectorsPerCluster} )); | |
| # Track=$(hexdump -v -s 24 -n 2 -e '"%u"' ${part})'' # Number of sectors per track. | |
| # Heads=$(hexdump -v -s 26 -n 2 -e '"%u"' ${part})'' # Number of heads. | |
| # | |
| # if [ "${Heads}" -ne 255 ] || [ "${Track}" -ne 63 ] ; then | |
| # BSI="${BSI} Geometry: ${Heads} Heads and ${Track} sectors per Track." | |
| # fi | |
| if [[ "${MFT_Sector}" -lt "${size}" ]] ; then | |
| MFT_FILE=$(dd if=${part} skip=${MFT_Sector} count=1 2>> ${Trash} | hexdump -v -n 4 -e '"%_u"'); | |
| else | |
| MFT_FILE=''; | |
| fi | |
| MFT_Mirr_Cluster=$(hexdump -v -s 56 -n 4 -e '"%d"' ${part}); | |
| MFT_Mirr_Sector=$(( ${MFT_Mirr_Cluster} * ${SectorsPerCluster} )); | |
| if [[ "${MFT_Mirr_Sector}" -lt "${size}" ]] ; then | |
| MFT_Mirr_FILE=$(dd if=${part} skip=${MFT_Mirr_Sector} count=1 2>> ${Trash} | hexdump -v -n 4 -e '"%_u"'); | |
| else | |
| MFT_Mirr_FILE=''; | |
| fi | |
| if ( [ "${offset}" -eq "${start}" ] && [ "${MFT_FILE}" = 'FILE' ] && [ "${MFT_Mirr_FILE}" = 'FILE' ] && [ "${Comp_Size}" -eq 0 ] ) ; then | |
| BSI="${BSI} No errors found in the Boot Parameter Block."; | |
| else | |
| if [[ "${offset}" -ne "${start}" ]] ; then | |
| BSI="${BSI} According to the info in the boot sector, ${name} starts at sector ${offset}."; | |
| if [[ "${offset}" -ne 63 && "${offset}" -ne 2048 && "${offset}" -ne 0 || "${kind}" != 'L' ]] ; then | |
| BSI="${BSI} But according to the info from fdisk, ${name} starts at sector ${start}."; | |
| fi | |
| fi | |
| if [[ "${MFT_FILE}" != "FILE" ]] ; then | |
| BSI="${BSI} The info in boot sector on the starting sector of the MFT is wrong."; | |
| printf "MFT Sector of ${name}\n\n" >> ${Unknown_MBR}; | |
| dd if=${part} skip=${MFT_Sector} count=1 2>> ${Trash} | hexdump -C >> ${Unknown_MBR}; | |
| fi | |
| if [[ "${MFT_Mirr_FILE}" != 'FILE' ]] ; then | |
| BSI="${BSI} The info in the boot sector on the starting sector of the MFT Mirror is wrong."; | |
| fi | |
| if [[ "${Comp_Size}" -ne 0 ]] ; then | |
| BSI="${BSI} According to the info in the boot sector, ${name} has ${BPB_Part_Size} sectors, but according to the info from fdisk, it has ${size} sectors."; | |
| fi | |
| fi | |
| fi | |
| ## Investigate the Boot Parameter Block (BPB) of (some) FAT partition. ## | |
| # Identifies Fat Bootsectors which are used for booting. | |
| # if [[ "${Bytes80_to_81}" = '7cc6' || "${Bytes80_to_81}" = '7815' || "${Bytes80_to_81}" = 'b6d1' || "${Bytes80_to_81}" = '7405' || "${Bytes80_to_81}" = '6974' || "${Bytes80_to_81}" = '0bd0' || "${Bytes80_to_81}" = '089e' ]] ; | |
| if [[ "${type}" = 'vfat' ]] ; then | |
| offset=$(hexdump -v -s 28 -n 4 -e '"%d\n"' ${part}); # Starting sector the partition according to BPB. | |
| BPB_Part_Size=$(hexdump -v -s 32 -n 4 -e '"%d"' ${part}); # Partition size in sectors according to BPB. | |
| Comp_Size=$(( (BPB_Part_Size - size)/256 )) # This number will be unequal to zero, if the 2 | |
| # partions sizes differ by more than 255 sectors. | |
| #Track=$(hexdump -v -s 24 -n 2 -e '"%u"' ${part})'' # Number of sectors per track. | |
| #Heads=$(hexdump -v -s 26 -n 2 -e '"%u"' ${part})'' # Number of heads | |
| #if [[ "${Heads}" -ne 255 || "${Track}" -ne 63 ]] ; then # Checks for an usual geometry. | |
| # BSI=$(echo ${BSI}" "Geometry: ${Heads} Heads and ${Track} sectors per Track.) ### Report unusal geometry | |
| #fi; | |
| # Check whether Partitons starting sector and the Partition Size of BPB and fdisk agree. | |
| if [[ "${offset}" -eq "${start}" && "${Comp_Size}" -eq "0" ]] ; then | |
| BSI="${BSI} No errors found in the Boot Parameter Block."; # If they agree. | |
| else # If they don't agree. | |
| if [[ "${offset}" -ne "${start}" ]] ; then # If partition starting sector disagrees. | |
| # Display the starting sector according to the BPB. | |
| BSI="${BSI} According to the info in the boot sector, ${name} starts at sector ${offset}."; | |
| # Check whether partition is a logcial partition and if its starting sector value is a 63 or 2048. | |
| if [[ "${offset}" -ne "63" && "${offset}" -ne "2048" || "${kind}" != "L" ]] ; then | |
| # If not, display starting sector according to fdisk. | |
| BSI="${BSI} But according to the info from fdisk, ${name} starts at sector ${start}."; | |
| else | |
| # This is quite common occurence, and only matters if one tries to boot Windows from a logical partition. | |
| BSI="${BSI} But according to the info from fdisk, ${name} starts at sector ${start}. \"63\" and \"2048\" are quite common values for the starting sector of a logical partition and they only need to be fixed when you want to boot Windows from a logical partition."; | |
| fi | |
| fi | |
| # If partition sizes from BPB and FDISK differ by more than 255 sector, display both sizes. | |
| if [[ "${Comp_Size}" -ne "0" ]] ; then | |
| BSI="${BSI} According to the info in the boot sector, ${name} has ${BPB_Part_Size} sectors."; | |
| if [[ "$BPB_Part_Size" -ne 0 ]] ; then | |
| BSI="${BSI}. But according to the info from the partition table, it has ${size} sectors."; | |
| fi # Don't display a warning message in the common case BPB_Part_Size=0. | |
| fi | |
| fi # End of BPB Error if-then-else. | |
| fi # End of Investigation of the BPB of vfat partitions. | |
| ## Display boot sector info. ## | |
| printf ' Boot sector info: ' >> "${Log}"; | |
| printf "${BSI}\n" | fold -s -w 55 | sed -e '/^-------------------------\.\?$/ d' -e '2~1s/.*/ &/' >> "${Log}"; | |
| ## Exclude partitions which contain no information, or which we (currently) don't know how to accces. ## | |
| case "${type}" in | |
| 'BIOS Boot partition' ) part_no_mount=1;; | |
| 'crypto_LUKS' ) part_no_mount=1;; | |
| 'Extended Partition' ) part_no_mount=1;; | |
| 'linux_raid_member' ) part_no_mount=1;; | |
| 'LVM2_member' ) part_no_mount=1;; | |
| 'swap' ) part_no_mount=1;; | |
| 'unknown volume type' ) part_no_mount=1;; | |
| 'zfs_member' ) part_no_mount=1;; | |
| esac | |
| if [ "${part_no_mount}" -eq 0 ] ; then | |
| # Look for a mount point of the current partition. | |
| # If multiple mount points are found, use the one with the shortest pathname. | |
| CheckMount=$(mount | ${AWK} -F "\t" '$0 ~ "^'${part}' " { sub(" on ", "\t", $0); sub(" type ", "\t", $0); print $2 }' | sort | ${AWK} '{ print $0; exit}'); | |
| # Check whether partition is already mounted. | |
| if [ x"${CheckMount}" != x'' ] ; then | |
| if [ "${CheckMount}" = "/" ] ; then | |
| mountname=''; | |
| else | |
| # If yes, use the existing mount point. | |
| mountname="${CheckMount}"; | |
| fi | |
| fi | |
| # Try to mount the partition. | |
| if [ x"${CheckMount}" != x'' ] || mount -r -t "${type}" ${part} "${mountname}" 2>> ${Mount_Error} \ | |
| || ( [ "${type}" = ntfs ] && ntfs-3g -o ro ${part} "${mountname}" 2>> ${Mount_Error} ) ; then | |
| # If partition is mounted, try to identify the Operating System (OS) by looking for files specific to the OS. | |
| OS=''; | |
| grep -q "W.i.n.d.o.w.s. .V.i.s.t.a" "${mountname}"/{windows,Windows,WINDOWS}/{System32,system32}/{Winload,winload}.exe 2>> ${Trash} && OS='Windows Vista'; | |
| grep -q "W.i.n.d.o.w.s. .7" "${mountname}"/{windows,Windows,WINDOWS}/{System32,system32}/{Winload,winload}.exe 2>> ${Trash} && OS='Windows 7'; | |
| grep -q "w.i.n.8._." "${mountname}"/{windows,Windows,WINDOWS}/{System32,system32}/{Winload,winload}.exe 2>> ${Trash} && OS='Windows 8'; | |
| for WinOS in 'MS-DOS' 'MS-DOS 6.22' 'MS-DOS 6.21' 'MS-DOS 6.0' 'MS-DOS 5.0' 'MS-DOS 4.01' 'MS-DOS 3.3' 'Windows 98' 'Windows 95'; do | |
| grep -q "${WinOS}" "${mountname}"/{IO.SYS,io.sys} 2>> ${Trash} && OS="${WinOS}"; | |
| done | |
| [ -s "${mountname}/Windows/System32/config/SecEvent.Evt" ] || [ -s "${mountname}/WINDOWS/system32/config/SecEvent.Evt" ] || [ -s "${mountname}/WINDOWS/system32/config/secevent.evt" ] || [ -s "${mountname}/windows/system32/config/secevent.evt" ] && OS='Windows XP'; | |
| [ -s "${mountname}/ReactOS/system32/config/SecEvent.Evt" ] && OS='ReactOS'; | |
| [ -s "${mountname}/etc/issue" ] && OS=$(sed -e 's/\\. //g' -e 's/\\.//g' -e 's/^[ \t]*//' "${mountname}"/etc/issue); | |
| [ -s "${mountname}/etc/slackware-version" ] && OS=$(sed -e 's/\\. //g' -e 's/\\.//g' -e 's/^[ \t]*//' "${mountname}"/etc/slackware-version); | |
| [ -s "${mountname}/etc/redhat-release" ] && OS=$(cat "${mountname}"/etc/redhat-release | tr -d '\n'); | |
| [ -s "${mountname}/etc/os-release" ] && grep -q '^PRETTY_NAME=' "${mountname}/etc/os-release" && OS=$(eval "$(grep '^PRETTY_NAME=' "${mountname}"/etc/os-release)"; printf '%s' "${PRETTY_NAME}" | tr -d '\n'); | |
| ## Search for the files in ${Bootfiles} ## | |
| # | |
| # If found, display their content. | |
| BootFiles=''; | |
| if [ "${type}" = 'vfat' ] ; then | |
| Boot_Files=${Boot_Files_Fat}; | |
| else | |
| Boot_Files=${Boot_Files_Normal}; | |
| fi | |
| for file in ${Boot_Files} ; do | |
| if [ -f "${mountname}${file}" ] && [ -s "${mountname}${file}" ] && FileNotMounted "${mountname}${file}" "${mountname}" ; then | |
| BootFiles="${BootFiles} ${file}"; | |
| # Check whether the file is a symlink. | |
| if ! [ -h "${mountname}${file}" ] ; then | |
| # if not a symlink, display content. | |
| if ( [ ${file} = '/grldr' ] || [ ${file} = '/grub.exe' ] ) ; then | |
| # Display the embedded menu of grub4dos. | |
| get_embedded_menu "${mountname}${file}" "${name}${file}"; | |
| else | |
| titlebar_gen "${name}" ${file}; # Generates a titlebar above each file listed. | |
| echo '--------------------------------------------------------------------------------' >> "${Log1}"; | |
| cat "${mountname}${file}" >> "${Log1}"; | |
| echo '--------------------------------------------------------------------------------' >> "${Log1}"; | |
| fi | |
| fi | |
| fi | |
| done | |
| ## Search for Wubi partitions. ## | |
| if [ -f "${mountname}/ubuntu/disks/root.disk" ] ; then | |
| Wubi=$(losetup -a | ${AWK} '$3 ~ "(/host/ubuntu/disks/root.disk)" { print $1; exit }' | sed 's/.$//' ); | |
| # check whether Wubi already has a loop device. | |
| if [[ x"${Wubi}" = x'' ]] ; then | |
| Wubi=$(losetup -f --show "${mountname}/ubuntu/disks/root.disk" ); | |
| WubiDev=0; | |
| else | |
| WubiDev=1; | |
| fi | |
| if [ x"${Wubi}" != x'' ] ; then | |
| Get_Partition_Info "${Log}"x "${Log1}"x "${Wubi}" "${name}/Wubi" "Wubi/${mountname}" 'Wubi' 0 0 'Wubi' ''; | |
| # Remove Wubu loop device, if created by BIS. | |
| [[ ${WubiDev} -eq 0 ]] && losetup -d "${Wubi}"; | |
| else | |
| echo "Found Wubi on ${name}. But could not create a loop device." >&2; | |
| fi | |
| fi | |
| ## Search for the filenames in ${Boot_Prog}. ## | |
| # | |
| # If found displays their names. | |
| if [ "${type}" = 'vfat' ] ; then | |
| # Check FAT filesystems for EFI boot files. | |
| for file in "${mountname}"/efi/{,*/}*/*.efi; do | |
| # Remove "${mountname}" part of the filename. | |
| file="${file#${mountname}}"; | |
| if [ -f "${mountname}${file}" ] && [ -s "${mountname}${file}" ] && FileNotMounted "${mountname}${file}" "${mountname}" ; then | |
| BootFiles="${BootFiles} ${file}"; | |
| fi | |
| done | |
| # Other boot program files. | |
| Boot_Prog=${Boot_Prog_Fat}; | |
| else | |
| Boot_Prog=${Boot_Prog_Normal}; | |
| fi | |
| for file in ${Boot_Prog} ; do | |
| if [ -f "${mountname}${file}" ] && [ -s "${mountname}${file}" ] && FileNotMounted "${mountname}${file}" "${mountname}" ; then | |
| BootFiles="${BootFiles} ${file}"; | |
| fi | |
| done | |
| ## Search for files containing boot codes. ## | |
| # Loop through all directories which might contain boot_code files. | |
| for file in ${Boot_Codes_Dir} ; do | |
| # If such directory exist ... | |
| if [ -d "${mountname}${file}" ] && FileNotMounted "${mountname}${file}" "${mountname}" ; then | |
| # Look at the content of that directory. | |
| for loader in $( ls "${mountname}${file}" ) ; do | |
| # If it is a file ... | |
| if [ -f "${mountname}${file}${loader}" ] && [ -s "${mountname}${file}${loader}" ] ; then | |
| # Bootpart code has "BootPart" written at 0x101 | |
| sig=$(hexdump -v -s 257 -n 8 -e '8/1 "%_p"' "${mountname}${file}${loader}"); | |
| if [ "${sig}" = 'BootPart' ] ; then | |
| offset=$(hexdump -v -s 241 -n 4 -e '"%d"' "${mountname}${file}${loader}"); | |
| dr=$(hexdump -v -s 111 -n 1 -e '"%d"' "${mountname}${file}${loader}"); | |
| dr=$((dr - 127)); | |
| BFI="${BFI} BootPart in the file ${file}${loader} is trying to chainload sector #${offset} on boot drive #${dr}"; | |
| fi | |
| # Grub Legacy, Grub2 (v1.96) and Grub2 (v1.99) have "GRUB" written at 0x17f. | |
| sig=$(hexdump -v -s 383 -n 4 -e '4/1 "%_p"' "${mountname}${file}${loader}"); | |
| if [ "${sig}" = 'GRUB' ] ; then | |
| sig2=$(hexdump -v -n 2 -e '/1 "%02x"' "${mountname}${file}${loader}"); | |
| # Distinguish Grub Legacy and Grub2 (v1.96) by the first two bytes. | |
| case "${sig2}" in | |
| eb48) stage2_loc "${mountname}${file}${loader}"; | |
| BFI="${BFI} Grub Legacy (v${Grub_Version}) in the file ${file}${loader} ${Stage2_Msg}";; | |
| eb4c) grub2_info "${mountname}${file}${loader}" ${drive} 1.96 'file'; | |
| BFI="${BFI} Grub2 (v1.96) in the file ${file}${loader} ${Grub2_Msg}.";; | |
| eb63) grub2_info "${mountname}${file}${loader}" ${drive} 1.99 'file'; | |
| BFI="${BFI} Grub2 (v1.99) in the file ${file}${loader} ${Grub2_Msg}.";; | |
| esac | |
| fi | |
| # Grub2 (v1.97-1.98) has "GRUB" written at 0x188. | |
| sig=$(hexdump -v -s 392 -n 4 -e '4/1 "%_p"' "${mountname}${file}${loader}"); | |
| if [ "${sig}" = 'GRUB' ]; then | |
| grub2_info "${mountname}${file}${loader}" ${drive} 1.97-1.98 'file'; | |
| BFI="${BFI} Grub2 (v1.97-1.98) in the file ${file}${loader} ${Grub2_Msg}."; | |
| fi | |
| # Grub2 (v2.00) has "GRUB" written at 0x180. | |
| sig=$(hexdump -v -s 384 -n 4 -e '4/1 "%_p"' "${mountname}${file}${loader}"); | |
| if [ "${sig}" = 'GRUB' ]; then | |
| grub2_info "${mountname}${file}${loader}" ${drive} 2.00 'file'; | |
| BFI="${BFI} Grub2 (v2.00) in the file ${file}${loader} ${Grub2_Msg}."; | |
| fi | |
| fi | |
| done # End of loop through the files in a particular Boot_Code_Directory. | |
| fi | |
| done # End of the loop through the Boot_Code_Directories. | |
| ## Show the location (offset on disk) of all files in: ## | |
| # - the GrubError18_Files list | |
| # - the SyslinuxError_Files list | |
| cd "${mountname}/"; | |
| if [ $( last_block_of_file ${GrubError18_Files} ; echo $? ) -ne 0 ] ; then | |
| titlebar_gen "${name}" ': Location of files loaded by Grub'; | |
| printf "%11sGiB - GB%13sFile%33sFragment(s)\n\n" ' ' ' ' ' ' >> "${Log1}"; | |
| cat ${Tmp_Log} >> "${Log1}"; | |
| fi | |
| if [ $( last_block_of_file ${SyslinuxError_Files} ; echo $? ) -ne 0 ] ; then | |
| titlebar_gen "${name}" ': Location of files loaded by Syslinux'; | |
| printf "%11sGiB - GB%13sFile%33sFragment(s)\n\n" ' ' ' ' ' ' >> "${Log1}"; | |
| cat ${Tmp_Log} >> "${Log1}"; | |
| fi | |
| rm -f ${Tmp_Log}; | |
| # Display the version of the COM32(R) modules of Syslinux. | |
| for com32 in *.c32 syslinux/*.c32 extlinux/*.c32 boot/syslinux/*.c32 boot/extlinux/*.c32 ; do | |
| if [ -f "${com32}" ] ; then | |
| # First 5 bytes of the COM32(R) module are a magic number (used by Syslinux too). | |
| com32_version=$(hexdump -n 5 -e '/1 "%02x"' "${com32}"); | |
| case ${com32_version} in | |
| b8fe4ccd21) printf ' %-35s: COM32R module (v4.xx)\n' "${com32}" >> ${Tmp_Log};; | |
| b8ff4ccd21) printf ' %-35s: COM32R module (v3.xx)\n' "${com32}" >> ${Tmp_Log};; | |
| *) printf ' %-35s: not a COM32/COM32R module\n' "${com32}" >> ${Tmp_Log};; | |
| esac | |
| fi | |
| done | |
| if [ -f ${Tmp_Log} ] ; then | |
| titlebar_gen "${name}" ': Version of COM32(R) files used by Syslinux'; | |
| cat ${Tmp_Log} >> "${Log1}"; | |
| fi | |
| cd "${Folder}"; | |
| echo > ${Tmp_Log}; | |
| if [[ x"${BFI}" != x'' ]] ; then | |
| printf " Boot file info: " >> "${Log}"; | |
| printf "${BFI}\n" | fold -s -w 55 | sed -e '/^-------------------------$/ d' -e '2~1s/.*/ &/' >> "${Log}"; | |
| fi | |
| printf " Operating System: " >> "${Log}"; | |
| echo "${OS}" | fold -s -w 55 | sed -e '2~1s/.*/ &/' >> "${Log}"; | |
| printf " Boot files: " >> "${Log}"; | |
| echo ${BootFiles} | fold -s -w 55 | sed -e '2~1s/.*/ &/' >> "${Log}"; | |
| # If partition was mounted by the script. | |
| if [ x"${CheckMount}" = x'' ] ; then | |
| umount "${mountname}" || umount -l "${mountname}"; | |
| fi | |
| # If partition failed to mount. | |
| else | |
| printf " Mounting failed: " >> "${Log}"; | |
| cat ${Mount_Error} >> "${Log}"; | |
| fi # End of Mounting "if then else". | |
| fi # End of Partition Type "if then else". | |
| echo >> "${Log}"; | |
| if [[ -e "${Log}"x ]] ; then | |
| cat "${Log}"x >> "${Log}"; | |
| rm "${Log}"x; | |
| fi | |
| if [[ -e "${Log1}"x ]] ; then | |
| cat "${Log1}"x >> "${Log1}"; | |
| rm "${Log1}"x; | |
| fi | |
| } # End Get_Partition_Info function | |
| ## "titlebar_gen" generates the ${name}${file} title bar to always be 80 characters in length. ## | |
| titlebar_gen () { | |
| local name_file name_file_length equal_signs_line_length equal_signs_line; | |
| name_file="${1}${2}:"; | |
| name_file_length=${#name_file}; | |
| equal_signs_line_length=$(((80-${name_file_length})/2-1)); | |
| # Build "===" string. | |
| printf -v equal_signs_line "%${equal_signs_line_length}s"; | |
| printf -v equal_signs_line "%s" "${equal_signs_line// /=}"; | |
| if [ "$((${name_file_length}%2))" -eq 1 ]; then | |
| # If ${name_file_length} is odd, add an extra "=" at the end. | |
| printf "\n%s %s %s=\n\n" "${equal_signs_line}" "${name_file}" "${equal_signs_line}" >> "${Log1}"; | |
| else | |
| printf "\n%s %s %s\n\n" "${equal_signs_line}" "${name_file}" "${equal_signs_line}" >> "${Log1}"; | |
| fi | |
| } | |
| ## Start ## | |
| # Center title. | |
| BIS_title=$(printf 'Boot Info Script %s [%s]' "${VERSION}" "${RELEASE_DATE}"); | |
| printf -v BIS_title_space "%$(( ( 80 - ${#BIS_title} ) / 2 - 1 ))s"; | |
| printf "${BIS_title_space}${BIS_title}\n" > "${Log}"; | |
| if [ ! -z "${LAST_GIT_COMMIT_SHORTLOG}" ] ; then | |
| printf '\nLast git commit: %s\nCommit date: %s\n' \ | |
| "${LAST_GIT_COMMIT_SHORTLOG}" "${LAST_GIT_COMMIT_DATE}" >> "${Log}"; | |
| fi | |
| printf '\n\n============================= Boot Info Summary: ===============================\n\n' >> "${Log}"; | |
| # Search for hard drives which don't exist, have a corrupted partition table | |
| # or don't have a parition table (whole drive is a filesystem). | |
| # Information on all hard drives which a valid partition table are stored in | |
| # the hard drives arrays: HD????? | |
| # id for Filesystem Drives. | |
| FSD=0; | |
| # Clear blkid cache | |
| blkid -g; | |
| for drive in ${All_Hard_Drives} ; do | |
| size=$(fdisks ${drive}); | |
| PrintBlkid ${drive}; | |
| if [ 0 -lt ${size} 2>> ${Trash} ] ; then | |
| if [ x"$(blkid ${drive})" = x'' ] || [ x"$(blkid | grep ${drive}:)" = x'' ] ; then | |
| # Drive is not a filesytem. | |
| size=$((2*size)); | |
| HDName[${HI}]=${drive}; | |
| HDSize[${HI}]=${size}; | |
| # Get and set HDHead[${HI}], HDTrack[${HI}] and HDCylinder[${HI}] all at once. | |
| eval $(fdisk -lu ${drive} 2>> ${Trash} | ${AWK} -F ' ' '$2 ~ "head" { print "HDHead['${HI}']=" $1 "; HDTrack['${HI}']=" $3 "; HDCylinder['${HI}']=" $5 }' ); | |
| # Look at the first 4 bytes of the second sector to identify the partition table type. | |
| case $(hexdump -v -s 512 -n 4 -e '"%_u"' ${drive}) in | |
| 'EMBR') HDPT[${HI}]='BootIt';; | |
| 'EFI ') HDPT[${HI}]='EFI';; | |
| *) HDPT[${HI}]='MSDos';; | |
| esac | |
| HI=$((${HI}+1)); | |
| else | |
| # Drive is a filesystem. | |
| if [ $( expr match "$(BlkidTag "${drive}" TYPE)" '.*raid') -eq 0 ] || [ x"$(BlkidTag "${drive}" UUID)" != x'' ] ; then | |
| FilesystemDrives[${FSD}]="${drive}"; | |
| ((FSD++)); | |
| fi | |
| fi | |
| else | |
| printf "$(basename ${drive}) " >> ${FakeHardDrives}; | |
| fi | |
| done | |
| ## Identify the MBR of each hard drive. ## | |
| echo 'Identifying MBRs...'; | |
| for HI in ${!HDName[@]} ; do | |
| drive="${HDName[${HI}]}"; | |
| Message="is installed in the MBR of ${drive}"; | |
| # Read the whole MBR in hexadecimal format. | |
| MBR_512=$(hexdump -v -n 512 -e '/1 "%02x"' ${drive}); | |
| ## Look at the first 2,3,4 or 8 bytes of the hard drive to identify the boot code installed in the MBR. ## | |
| # | |
| # If it is not enough, look at more bytes. | |
| MBR_sig2="${MBR_512:0:4}"; | |
| MBR_sig3="${MBR_512:0:6}"; | |
| MBR_sig4="${MBR_512:0:8}"; | |
| MBR_sig8="${MBR_512:0:16}"; | |
| ## Bytes 0x80-0x81 of the MBR. ## | |
| # | |
| # Use it to differentiate between different versions of the same bootloader. | |
| MBR_bytes80to81="${MBR_512:256:4}"; | |
| BL=; | |
| case ${MBR_sig2} in | |
| eb48) ## Grub Legacy is in the MBR. ## | |
| BL="Grub Legacy"; | |
| # 0x44 contains the offset to the next stage. | |
| offset=$(hexdump -v -s 68 -n 4 -e '"%u"' ${drive}); | |
| if [ "${offset}" -ne 1 ] ; then | |
| # Grub Legacy is installed without stage1.5 files. | |
| stage2_loc ${drive}; | |
| Message="${Message} and ${Stage2_Msg}"; | |
| else | |
| # Grub is installed with stage1.5 files. | |
| Grub_String=$(hexdump -v -s 1042 -n 94 -e '"%_u"' ${drive}); | |
| Grub_Version="${Grub_String%%nul*}"; | |
| BL="Grub Legacy (v${Grub_Version})"; | |
| tmp="/${Grub_String#*/}"; | |
| tmp="${tmp%%nul*}"; | |
| eval $(echo ${tmp} | ${AWK} '{ print "stage=" $1 "; menu=" $2 }'); | |
| [[ x"$menu" = x'' ]] || stage="${stage} and ${menu}"; | |
| part_info=$((1045 + ${#Grub_Version})); | |
| eval $(hexdump -v -s ${part_info} -n 2 -e '1/1 "pa=%u; " 1/1 "dr=%u"' ${drive}); | |
| dr=$(( ${dr} - 127 )); | |
| pa=$(( ${pa} + 1 )); | |
| if [ "${dr}" -eq 128 ] ; then | |
| Message="${Message} and looks on the same drive in partition #${pa} for ${stage}"; | |
| else | |
| Message="${Message} and looks on boot drive #${dr} in partition #${pa} for ${stage}"; | |
| fi | |
| fi;; | |
| eb4c) ## Grub2 (v1.96) is in the MBR. ## | |
| BL='Grub2 (v1.96)'; | |
| grub2_info ${drive} ${drive} '1.96' 'disk'; | |
| Message="${Message} and ${Grub2_Msg}";; | |
| eb63) ## Grub2 is in the MBR. ## | |
| case ${MBR_bytes80to81} in | |
| 7c3c) grub2_version='1.97-1.98'; BL='Grub2 (v1.97-1.98)';; | |
| 0020) grub2_version='1.99-2.00'; BL='Grub2 (v1.99-2.00)';; | |
| esac | |
| grub2_info ${drive} ${drive} ${grub2_version} 'disk'; | |
| # Set a more exact version number (1.99 or 2.00), if '1.99-2.00' was | |
| # passed to the grub2_info function. | |
| BL="Grub2 (v${grub2_version})"; | |
| Message="${Message} and ${Grub2_Msg}";; | |
| 0ebe) BL='ThinkPad';; | |
| 31c0) # Look at the first 8 bytes of the hard drive to identify the boot code installed in the MBR. | |
| case ${MBR_sig8} in | |
| 31c08ed0bc007c8e) BL='NetBSD/SUSE generic MBR';; | |
| 31c08ed0bc007cfb) BL='Acer PQService MBR';; | |
| esac;; | |
| 33c0) # Look at the first 3 bytes of the hard drive to identify the boot code installed in the MBR. | |
| case ${MBR_sig3} in | |
| 33c08e) BL='Windows'; | |
| case ${MBR_sig8} in | |
| 33c08ed0bc007cfb) BL='Windows 2000/XP/2003';; | |
| 33c08ed0bc007c8e) | |
| # Look at byte 0xF0-F1: different offset for "TCPA" string. | |
| case "${MBR_512:480:4}" in | |
| fb54) BL='Windows Vista';; | |
| 4350) BL='Windows 7/8/2012';; | |
| esac;; | |
| esac;; | |
| 33c090) BL='DiskCryptor';; | |
| 33c0fa) # Look at bytes 0x5B-5D: different offsets for jump target | |
| case ${MBR_512:182:6} in | |
| 0fb6c6) BL='Syslinux GPTMBR (5.10 and higher)';; | |
| bb007c) BL='Syslinux GPTMBR (4.04-5.01)';; | |
| e82101) BL='Syslinux MBR (4.04-4.07)';; | |
| e83501) BL='Syslinux MBR (5.00 and higher)';; | |
| e81001) # Syslinux ALTMBR; look at byte 0xd9 for different version and | |
| # byte 0x1b7 (439) for boot partition | |
| case ${MBR_512:434:2} in | |
| c3) BL='Syslinux ALTMBR (4.04-4.05)';; | |
| c6) BL='Syslinux ALTMBR (4.06 and higher)';; | |
| esac; | |
| BL="${BL} with boot partition 0x${MBR_512:878:2}";; | |
| esac;; | |
| esac;; | |
| 33ed) # Look at bytes 0x80-0x81 to be more specific about the Syslinux variant/version. | |
| case ${MBR_bytes80to81} in | |
| 407c) BL='ISOhybrid (Syslinux 4.04)';; | |
| 83e1) BL='ISOhybrid with partition support (Syslinux 4.04)';; | |
| cd13) BL='ISOhybrid with partition support (Syslinux 4.05 and higher)';; | |
| f7e1) BL='ISOhybrid (Syslinux 4.05 and higher)';; | |
| esac;; | |
| 33ff) BL='HP/Gateway';; | |
| b800) BL='Plop';; | |
| ea05) | |
| case ${MBR_sig3} in | |
| ea0500) BL='OpenBSD generic MBR';; | |
| ea0501) BL='XOSL';; | |
| esac;; | |
| ea1e) BL='Truecrypt Boot Loader';; | |
| eb04) BL='Solaris';; | |
| eb31) BL='Paragon';; | |
| eb5e) # Look at the first 3 bytes of the hard drive to identify the boot code installed in the MBR. | |
| case ${MBR_sig3} in | |
| eb5e00) BL='fbinst';; | |
| eb5e80) BL='Grub4Dos';; | |
| eb5e90) BL='WEE'; | |
| # Get the embedded menu of WEE. | |
| get_embedded_menu "${drive}" "WEE's (${drive})";; | |
| esac;; | |
| fa31) # Look at the first 3 bytes of the hard drive to identify the boot code installed in the MBR. | |
| case ${MBR_sig3} in | |
| fa31c0) # Look at bytes 0x80-0x81 to be more specific about the Syslinux variant/version. | |
| case ${MBR_bytes80to81} in | |
| 0069) BL='ISOhybrid (Syslinux 3.72-3.73)';; | |
| 7c66) BL='Syslinux MBR (3.61-4.03)';; | |
| 7cb8) BL='Syslinux MBR (3.36-3.51)';; | |
| b442) BL='Syslinux MBR (3.00-3.35)';; | |
| bb00) BL='Syslinux MBR (3.52-3.60)';; | |
| e2f8) # Syslinux pre-4.04; look at bytes 0x82-0x84 | |
| case "${MBR_512:260:6}" in | |
| 31f65f) BL='Syslinux GPTMBR (4.00-4.03)';; | |
| 5e5974) BL='Syslinux GPTMBR (3.72-3.73)';; | |
| 5e5958) # look at bytes 0xe-0xf | |
| case "${MBR_512:28:4}" in | |
| 528e) BL='Syslinux GPTMBR (3.70-3.71)';; | |
| 8ec0) BL='Syslinux GPTMBR (3.74-4.03)';; | |
| esac;; | |
| esac;; | |
| e879) BL='ISOhybrid (Syslinux 3.74-3.80)';; | |
| esac;; | |
| fa31c9) BL='Master Boot LoaDeR';; | |
| fa31ed) # Look at bytes 0x80-0x81 to be more specific about the Syslinux variant/version. | |
| case ${MBR_bytes80to81} in | |
| 0069) BL='ISOhybrid (Syslinux 3.72-3.73)';; | |
| 0fb6) BL='ISOhybrid with partition support (Syslinux 3.82-3.86)';; | |
| 407c) BL='ISOhybrid (Syslinux 3.82-4.03)';; | |
| 83e1) BL='ISOhybrid with partition support (Syslinux 4.00-4.03)';; | |
| b6c6) BL='ISOhybrid with partition support (Syslinux 3.81)';; | |
| fbc0) BL='ISOhybrid (Syslinux 3.81)';; | |
| esac;; | |
| esac;; | |
| fa33) BL='MS-DOS 3.30 through Windows 95 (A)';; | |
| fab8) # Look at the first 4 bytes of the hard drive to identify the boot code installed in the MBR. | |
| case ${MBR_sig4} in | |
| fab80000) BL='FreeDOS (eXtended FDisk)';; | |
| fab80010) BL="libparted MBR boot code";; | |
| esac;; | |
| faeb) BL='Lilo';; | |
| fafc) BL='ReactOS';; | |
| fc31) # Look at the first 8 bytes of the hard drive to identify the boot code installed in the MBR. | |
| case ${MBR_sig8} in | |
| fc31c08ed031e48e) BL='install-mbr/Testdisk';; | |
| fc31c08ec08ed88e) BL='boot0 (FreeBSD)';; | |
| esac;; | |
| fc33) BL='GAG';; | |
| fceb) BL='BootIt NG';; | |
| 0000) BL='No boot loader';; | |
| esac | |
| if [ x"${BL}" = 'x' ] ; then | |
| BL='No known boot loader'; | |
| printf "Unknown MBR on ${drive}\n\n" >> ${Unknown_MBR}; | |
| hexdump -v -n 512 -C ${drive} >> ${Unknown_MBR}; | |
| echo >> ${Unknown_MBR}; | |
| fi | |
| ## Output message at beginning of summary that gives MBR info for each drive: ## | |
| printf ' => ' >> "${Log}"; | |
| printf "${BL} ${Message}.\n" | fold -s -w 75 | sed -e '/^-----\.\?$/ d' -e '2~1s/.*/ &/' >> "${Log}"; | |
| HDMBR[${HI}]=${BL}; | |
| done | |
| echo >> "${Log}"; | |
| ## Store and Display all the partitions tables. ## | |
| for HI in ${!HDName[@]} ; do | |
| drive=${HDName[${HI}]}; | |
| echo "Computing Partition Table of ${drive}..."; | |
| FP=$((PI+1)); # used if non-MS_DOS partition table is not in use. | |
| FirstPartition[${HI}]=${FP}; | |
| PTType=${HDPT[${HI}]}; | |
| HDPT[${HI}]='MSDos'; | |
| echo "Drive: $(basename ${drive} ) _____________________________________________________________________" >> ${PartitionTable}; | |
| fdisk -lu ${drive} 2>> ${Trash} | sed '/omitting/ d' | sed '6,$ d' >> ${PartitionTable}; | |
| printf "\n${PTFormat}\n" 'Partition' 'Boot' 'Start Sector' 'End Sector' '# of Sectors' 'Id' 'System' >> ${PartitionTable}; | |
| ReadPT ${HI} 0 4 ${PartitionTable} "${PTFormat}" '' 0; | |
| echo >> ${PartitionTable}; | |
| LastPartition[${HI}]=${PI}; | |
| LP=${PI}; | |
| CheckPT ${FirstPartition[${HI}]} ${LastPartition[${HI}]} ${PartitionTable} ${HI}; | |
| echo >> ${PartitionTable}; | |
| HDPT[${HI}]=${PTType}; | |
| case ${PTType} in | |
| BootIt) printf 'BootIt NG Partition Table detected' >> ${PartitionTable}; | |
| [[ "${HDMBR[${HI}]}" = 'BootIt NG' ]] || printf ', but does not seem to be used' >> ${PartitionTable}; | |
| printf '.\n\n' >> ${PartitionTable}; | |
| ReadEMBR ${HI} ${PartitionTable}; | |
| echo >> ${PartitionTable}; | |
| if [ "${HDMBR[${HI}]}" = 'BootIt NG' ] ; then | |
| LastPartition[${HI}]=${PI}; | |
| CheckPT ${FirstPartition[${HI}]} ${LastPartition[${HI}]} ${PartitionTable} ${HI}; | |
| else | |
| FirstPartition[${HI}]=${FP}; | |
| fi;; | |
| EFI) FirstPartition[${HI}]=$((PI+1)); | |
| EFIee=$(hexdump -v -s 450 -n 1 -e '"%x"' ${drive}); | |
| printf 'GUID Partition Table detected' >> ${PartitionTable}; | |
| [[ "${EFIee}" = 'ee' ]] || printf ', but does not seem to be used' >> ${PartitionTable}; | |
| printf '.\n\n' >> ${PartitionTable}; | |
| ReadEFI ${HI} ${PartitionTable}; | |
| echo >> ${PartitionTable}; | |
| if [ "${EFIee}" = 'ee' ] ; then | |
| LastPartition[${HI}]=${PI}; | |
| CheckPT ${FirstPartition[${HI}]} ${LastPartition[${HI}]} ${PartitionTable} ${HI}; | |
| else | |
| FirstPartition[${HI}]=${FP}; | |
| fi;; | |
| esac | |
| done | |
| ## Loop through all Hard Drives. ## | |
| for HI in ${!HDName[@]} ; do | |
| drive=${HDName[${HI}]}; | |
| ## And then loop through the partitions on that drive. ## | |
| for (( PI = FirstPartition[${HI}]; PI <= LastPartition[${HI}]; PI++ )); do | |
| part_type=${TypeArray[${PI}]}; # Type of the partition according to fdisk | |
| start=${StartArray[${PI}]}; | |
| size=${SizeArray[${PI}]}; | |
| end=${EndArray[${PI}]}; | |
| kind=${KindArray[${PI}]}; | |
| system=${SystemArray[${PI}]}; | |
| if [[ x"${DeviceArray[${PI}]}" = x'' ]] ; then | |
| name="${NamesArray[${PI}]}"; | |
| mountname=$(basename ${drive})"_"${PI}; | |
| part=$(losetup -f --show -o $((start*512)) ${drive}); | |
| # --sizelimit $((size*512)) --sizelimit seems to be a recently added option for losetup. Failed on Hardy. | |
| else | |
| part="${DeviceArray[${PI}]}"; | |
| name=$(basename ${part}); # Name of the partition (/dev/sda8 -> sda8). | |
| mountname=${name}; | |
| fi | |
| Get_Partition_Info "${Log}" "${Log1}" "${part}" "${name}" "${mountname}" "${kind}" "${start}" "${end}" "${system}" "${PI}"; | |
| [[ "${DeviceArray[${PI}]}" = '' ]] && losetup -d ${part}; | |
| done | |
| done | |
| ## Deactivate dmraid's activated by the script. ## | |
| if [ x"$InActiveDMRaid" != x'' ] ; then | |
| dmraid -an ${InActiveDMRaid}; | |
| fi | |
| ## Search LVM partitions for information. ## | |
| # | |
| # Only works if the "LVM2"-package is installed. | |
| if [ $(type lvs >> ${Trash} 2>> ${Trash} ; echo $?) -eq 0 ] ; then | |
| lvs --nameprefixes --noheadings --options lv_name,vg_name,lv_size,lv_attr --units s | \ | |
| while read line ; do | |
| LVM2_VG_NAME= LVM2_LV_NAME= LVM2_LV_SIZE= LVM2_LV_ATTR= | |
| eval "${line}"; | |
| if [ -z "${LVM2_VG_NAME}" ] || [ -z "${LVM2_LV_NAME}" ] || [ -z "${LVM2_LV_SIZE}" ] || [ -z "${LVM2_LV_ATTR}" ] ; then | |
| continue | |
| fi | |
| name="${LVM2_VG_NAME}-${LVM2_LV_NAME}"; | |
| LVM="/dev/mapper/${LVM2_VG_NAME//-/--}-${LVM2_LV_NAME//-/--}"; | |
| LVM_Size=${LVM2_LV_SIZE:0:-1}; | |
| LVM_Status=${LVM2_LV_ATTR:4:1}; | |
| lvchange -ay ${LVM}; | |
| mountname="LVM/${name}"; | |
| kind='LVM'; | |
| start=0; | |
| end=${LVM_Size}; | |
| system=''; | |
| PI=''; | |
| Get_Partition_Info "${Log}" "${Log1}" "$LVM" "${name}" "${mountname}" "${kind}" "${start}" "${end}" "${system}" "${PI}"; | |
| # deactivate all LVM's, which were not active. | |
| [[ "${LVM_Status}" != 'a' ]] && lvchange -an "${LVM}"; | |
| done | |
| fi | |
| ## Search MDRaid Partitons for Information ## | |
| # | |
| # Only works if "mdadm" is installed. | |
| if [ $(type mdadm >> ${Trash} 2>> ${Trash} ; echo $?) -eq 0 ] ; then | |
| # All arrays which are already assembled. | |
| MD_Active_Array=$(mdadm --detail --scan | ${AWK} '{ print $2 }'); | |
| # Assemble all arrays. | |
| mdadm --assemble --scan; | |
| # All arrays. | |
| MD_Array=$(mdadm --detail --scan | ${AWK} '{ print $2 }'); | |
| for MD in ${MD_Array}; do | |
| MD_Size=$(fdisks ${MD}); # size in blocks | |
| MD_Size=$((2*${MD_Size})); # size in sectors | |
| MD_Active=0; | |
| # Check whether MD is active. | |
| for MDA in ${MD_Active_Array}; do | |
| if [[ "${MDA}" = "${MD}" ]] ; then | |
| MD_Active=1; | |
| break; | |
| fi | |
| done | |
| name=${MD:5}; | |
| mountname="MDRaid/${name}"; | |
| kind="MDRaid"; | |
| start=0; | |
| end=${MD_Size}; | |
| system=''; | |
| PI=''; | |
| Get_Partition_Info "${Log}" "${Log1}" "${MD}" "${name}" "${mountname}" "${kind}" "${start}" "${end}" "${system}" "${PI}"; | |
| # deactivate all MD_Raid's, which were not active. | |
| [[ "${MD_Active}" -eq 0 ]] && mdadm --stop "${MD}"; | |
| done | |
| fi | |
| ## Search filesystem hard drives for information. ## | |
| for FD in ${FilesystemDrives[@]} ; do | |
| FD_Size=$(fdisks ${FD}); # size in blocks | |
| FD_Size=$((2*${FD_Size})); # size in sectors | |
| name=${FD:5}; | |
| mountname="FD/${name}"; | |
| kind="FD"; | |
| start=0; | |
| end=${FD_Size}; | |
| system=''; | |
| PI=''; | |
| Get_Partition_Info "${Log}" "${Log1}" "${FD}" "${name}" "${mountname}" "${kind}" "${start}" "${end}" "${system}" "${PI}"; | |
| done | |
| ## Drive/partition info. ## | |
| printf '============================ Drive/Partition Info: =============================\n\n' >> "${Log}"; | |
| [ -e ${PartitionTable} ] && cat ${PartitionTable} >> "${Log}" || echo 'no valid partition table found' >> "${Log}"; | |
| printf '"blkid" output: ________________________________________________________________\n\n' >> "${Log}"; | |
| printf "${BlkidFormat}" Device UUID TYPE LABEL >> "${Log}"; | |
| echo >> "${Log}"; | |
| for dev in $(blkid -o device | sort); do | |
| PrintBlkid ${dev} '_summary'; | |
| done | |
| cat "${BLKID}_summary" >> "${Log}"; | |
| echo >> "${Log}"; | |
| if [ $(ls -l /dev/disk/by-id 2>> ${Trash} | wc -l) -gt 1 ] ; then | |
| printf '========================= "ls -l /dev/disk/by-id" output: ======================\n\n' >> "${Log}"; | |
| ls -l /dev/disk/by-id >> "${Log}"; | |
| echo >> "${Log}"; | |
| fi | |
| if [ $(ls -R /dev/mapper 2>> ${Trash} | wc -l) -gt 2 ] ; then | |
| printf '========================= "ls -R /dev/mapper/" output: =========================\n\n' >> "${Log}"; | |
| ls -R /dev/mapper >> "${Log}"; | |
| echo >> "${Log}"; | |
| fi | |
| ## Mount points. ## | |
| printf '================================ Mount points: =================================\n\n' >> "${Log}"; | |
| MountFormat='%-16s %-24s %-10s %s\n'; | |
| printf "${MountFormat}\n" 'Device' 'Mount_Point' 'Type' 'Options' >> "${Log}"; | |
| # No idea for which mount version this is even needed. | |
| # original: | |
| # mount | grep ' / '| grep -v '^/'| sed 's/ on /'$Fis'/' |sed 's/ type /'$Fis'/'|sed 's/ (/'$Fis'(/'| gawk -F $Fis '{printf "'"$MountFormat"'", $1, $2, $3, $4 }'>>"$Log"; | |
| # new: | |
| # mount | sort | gawk -F "\t" '$0 ~ " / " { if ($1 !~ "^/") { sub(" on ", "\t", $0); sub(" type ", "\t", $0); optionsstart=index($3, " ("); printf "'"${MountFormat}"'", $1, $2, substr($3, 1, optionsstart - 1), substr($3, optionsstart + 1) } } END { printf "\n" }' >> "${Log}"; | |
| mount | sort | ${AWK} -F "\t" '$0 ~ "^/dev" \ | |
| { sub(" on ", "\t", $0); sub(" type ", "\t", $0); optionsstart=index($3, " ("); \ | |
| printf "'"${MountFormat}"'", $1, $2, substr($3, 1, optionsstart - 1), substr($3, optionsstart + 1) } END { printf "\n" }' >> "${Log}"; | |
| ## Write the content of Log1 to the log file. ## | |
| [ -e "${Log1}" ] && cat "${Log1}" >> "${Log}"; | |
| echo >> "${Log}"; | |
| ## Add unknown MBRs/Boot Sectors to the log file, if any. ## | |
| if [ -e ${Unknown_MBR} ] ; then | |
| printf '======================== Unknown MBRs/Boot Sectors/etc: ========================\n\n' >> "${Log}"; | |
| cat ${Unknown_MBR} >> "${Log}"; | |
| echo >> "${Log}"; | |
| fi | |
| ## Add fake hard drives to the log file, if any. ## | |
| if [ -e ${FakeHardDrives} ] ; then | |
| printf "========= Devices which don't seem to have a corresponding hard drive: =========\n\n" >> "${Log}"; | |
| cat ${FakeHardDrives} >> "${Log}"; | |
| printf "\n\n" >> "${Log}"; | |
| fi | |
| ## Write the Error Log to the log file. ## | |
| if [ -s ${Error_Log} ] ; then | |
| printf '=============================== StdErr Messages: ===============================\n\n' >> "${Log}"; | |
| cat ${Error_Log} >> "${Log}"; | |
| fi | |
| ## Write a final newline. ## | |
| echo >> "${Log}"; | |
| if [ ${stdout_output} -eq 1 ] ; then | |
| ## If --stdout is specified, show the output. | |
| cat "${Log}"; | |
| else | |
| ## Copy the log file to RESULTS file and make the user the owner of RESULTS file. ## | |
| cp "${Log}" "${LogFile}"; | |
| if [ "${SUDO_UID}:${SUDO_GID}" != ':' ] ; then | |
| chown "${SUDO_UID}:${SUDO_GID}" "${LogFile}"; | |
| fi | |
| ## gzip the RESULTS file, for easy uploading. ## | |
| # | |
| # gzip a copy of the RESULTS file only when -g or --gzip is passed on the command line. | |
| # | |
| # ./bootinfoscript -g <outputfile> | |
| # ./bootinfoscript --gzip <outputfile> | |
| if [ ${gzip_output} -eq 1 ] ; then | |
| cat "${LogFile}" | gzip -9 > "${LogFile}.gz"; | |
| if [ "${SUDO_UID}:${SUDO_GID}" != ':' ] ; then | |
| chown "${SUDO_UID}:${SUDO_GID}" "${LogFile}.gz"; | |
| fi | |
| fi | |
| ## Reset the Standard Output to the Terminal. ## | |
| # | |
| # exec 1>&-; | |
| # exec 1>&6; | |
| # exec 6>&-; | |
| printf '\nFinished. The results are in the file "%s"\nlocated in "%s".\n\n' "$(basename "${LogFile}")" "${Dir}/"; | |
| fi | |
| exit 0; | |