Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:estellnb:elstel
confinedrv
confinedrv-v1.7.7
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File confinedrv-v1.7.7 of Package confinedrv
#!/bin/bash # # confinedrv v1.7.7 # (c) copyright by Elmar Stellnberger, the original author: 2009, Jan 2010, Nov&Dez 2014, Aug 2016 # # further information: www.elstel.org/com; look here for an actual contact address # current email: estellnb@elstel.org; additional email estellnb@gmail.com # # v 1.7.7: allow to fade in a file for the mbr: sdy=sdb5,6 mbr=./sdb.mbr # * use lockfile-progs in order to lock the $shm/confinedrv-loops file (lockfile-create) or use the old lockfile-executable as fallback which is still distributed with some distros # * use /tmp if /dev/shm should not be accessible/mounted (and allow the user to mount /dev/shm later) # * some smaller fixes like f.i. touching the $shm/confinedrv-loops for the first time in order to avoid an unnecessary error message # # v 1.7.6: use of external partition images (06.12.2014) # * allows sth. like confinedrv sdy=sdb5,6 sdy5=/media/partitionimages/sdb5.img # * /dev/shm/confinedrv-loops now also contains a disk image offset (incompatible with versions before). # * correction of some minor issues: correct /dev/shm-cfdrv-lps. if loopdev deallocation should fail, do not accidentially drop loops on reallocation of the same device # # v 1.7.3: reuse of loop devices (01.12.2014) # * moved parsing and device setup out of main loop into own procedures # * new file format for /dev/shm/confinedrv-loops: "sdy /dev/loop0 /dev/sdb ro" (2 entries added); old program versions will not work on new format! # * multiple devices can again be set up in a single command line # # v 1.7.2: gpt support (30.11.2014) # * use of parted instead of fdisk # * small changes: prettier printing, loopusage handled before first call to confinedrv, error swallowed by bash 4.2.37 discovered: org=${groups[0]%%[0-9,]*}: colon was forgotten # # v 1.7.1: small fixups (16.11.2014) # * a problem in theory: eliminated race condition dropping loop devices, cleanup by --freeloop after kill -9 # * writable loop device only allocated if needed (previously: if any even non writable partition was specified) # * acc2str -> access table printing: make a difference between zero and error # * skip function is now more thoroughly documented # # v 1.7: fixing some boot issues and providing enhanced functionality # * Certain boot problems which have arised under respective circumstances with v1.2.1 due to err-ing out gap spaces needed by grub or # other loaders rather than zeroing them out or making them available as read-only should now be resolved. # * choose for any of three modes for every partition: read, write, error or zero (sdx=sda:r1:e2,3:w5,6,7:z8); default is now to zero # out any parition which has not been mentioned rather than letting any access err there. # * The --gap and --mbr options provide means for specifying the access rights for gaps between primary partitions or all the space # before the first partition including the partition table and the MBR respectively. # * The [--noannot] --info sdx now provides a better readable map of the partition table annotated with symbolic references for the # start and end of partitions by default. --loopusage shows all loop devices in use by confinedrv. # # v 1.2.1: small fix (17.11.2013) # * correctly allocate/deallocate loop drives if more than one virtual drive is specified on the command line # # v 1.2: new algorithm (17.11.2013) # * completely reworked and better algorithm # # v 1.1: added license (30.10.2013) # * better option parsing; some minor fixes # * reads page size by getconf # # v.1.0: originally published version (Jan 2010) # * original authors: Elmar Stellnberger # license() { cat <<EOQ This program may be used under the terms of GPLv3; see: https://www.gnu.org/licenses/gpl-3.0.en.html. If you apply changes please sign our contributor license agreement at https://www.elstel.org/license/CLA-elstel.pdf so that your changes can be included into the main trunk at www.elstel.org/qemu/ (c) copyright by Elmar Stellnberger 2016 EOQ exit 0; } rot=$'\e[1;31m'; drot=$'\e[0;31m'; blau=$'\e[1;34m'; nv=$'\e[0m'; ul=$'\e[4m'; err() { echo -e "${rot}$@${nv}" >&2; } warn() { echo -e "${drot}$@${nv}" >&2; } msg() { echo -e "${blau}$@${nv}" >&2; } ein() { local tok="$1"; while [ $# -gt 1 ]; do [[ "$tok" = "$2" ]] && return 0; shift; done return 1; } exec 9>/dev/null; if which lockfile-create 2>&9 >&9; then let lockprog=1; elif which lockfile 2>&9 >&9; then let lockprog=2; else err "error: package lockfile-progs containing the lockfile-create/-remove utility programs is not installed."; let lockprog=0; fi check4parted() { which parted 2>&9 >&9 || { err "parted does not seem to be installed; exiting."; echo >&2; exit 208; } } mylockfile-create() { [[ -n "$2" ]] && { err "internal error: wrong invocation of mylockfile-create."; exit 244; } if [[ lockprog -eq 2 ]]; then lockfile -1 -r 3 "$1"; return $?; elif [[ lockprog -eq 0 ]]; then # unsafe because of race-condition! - user has already been warned. for retries in 1 2 3; do [[ -e "$1" ]] || { touch "$1"; return 0; } sleep 1; done; [[ -e "$1" ]] && return 4; touch "$1"; return 0; fi for retries in 1 2 3; do # good; no race condition lockfile-create --retry 0 --lock-name "$1" 2>&9 && return 0; sleep 1; done; lockfile-create --retry 0 --lock-name "$1" 2>&9 && return 0; return 4; } mylockfile-remove() { if [[ lockprog -eq 1 ]]; then lockfile-remove --lock-name "$1"; # does not delete the lock file (an optimization) at least while the machine is booted return; # ... but successfully releases the lock fi if [[ $(stat -c%s "$1") -ge 32 ]]; then # also: find "$1" -printf "%s" err "warning: lockfile $1 has a size greater than 32 bytes; please remove it manually after having a look!"; [[ lockprog -eq 1 ]] && lockfile-remove --lock-name "$1"; # never executed unless somebody would remove the code in front else rm -f "$1"; fi } if [[ -d "/dev/shm" ]]; then # gonna have to check first whether there are residuals in /tmp from the time of before of shm has been mounted # first wait until /tmp/confinedrv-loops gets unmounted; i.e. there is no confinedrv-loops.lock file in /tmp let lockexists=255; # 255 ~ error / lock does not exist for i in 0 1 2 3; do if [[ lockprog -eq 1 ]]; then lockfile-check --lock-name /tmp/confinedrv-loops.lock; let lockexists=$?; else [[ -e /tmp/confinedrv-loops.lock ]]; let lockexists=$?; fi [[ $lockexists -eq 0 ]] || break; [[ i -ne 3 ]] && sleep 1; done [[ lockexists -eq 0 ]] && { err "/dev/shm mounted but old lockfile still present in /tmp/confinedrv-loops.lock; please delete it first."; exit 209; } # then try to migrate the confinedrv-loops file from /tmp to /dev/shm if [[ -e "/tmp/confinedrv-loops" ]]; then mylockfile-create /dev/shm/confinedrv-loops.lock [[ -e "/dev/shm/confinedrv-loops" ]] && { err "tmp->shm migration error: [/tmp&/dev/shm]/confinedrv-loops: both files exist at the same time."; exit 209; } mv --no-clobber /tmp/confinedrv-loops /dev/shm/confinedrv-loops mylockfile-remove /dev/shm/confinedrv-loops.lock fi # now we can be sure that it is safe to use /dev/shm shm="/dev/shm"; else shm="/tmp"; fi mkvar() { local rndup=$(roundup $1); local rnddwn=$(rounddown $1); [[ -z "$annotdev" ]] && { err "localvars;"; exit; } if [[ $rndup = $rnddwn ]]; then export ${annotdev}${3/./_}_$rndup=$2$3; >&2 else export ${annotdev}${3/./_}_$rndup=$2$3+$((rndup-$1)); >&2 export ${annotdev}${3/./_}_$rnddwn=$2$3-$(($1-rnddwn)); >&2 fi } slurpdev() { local annotdev basedev; annotdev="$1"; basedev="$2"; msg "slurping $basedev" while read dno start end blocks id type; do if [[ "${dno,,}" = "disk" && "${start%:}" != "$start" ]]; then endofdisk=${end%s}; [[ "$endofdisk" != "$end" ]] && export ${annotdev}_endofdisk=$endofdisk; elif [[ -z "${dno##[0-9]*}" && -n "$dno$start$end" ]]; then start="${start%s}"; end="${end%s}"; dev="${basedev}${dno}"; mkvar $start ${dev#/dev/} .start mkvar $((end+1)) ${dev#/dev/} .end fi done < <( parted $basedev <<<$'unit s\nprint\nquit\n' ) export $annotdev="slurped" } droploop() { local dest target loop origin access offset loopusage loopusage_origin loopusage_access loopusage_offset err=0 target="$1"; shift; mylockfile-create $shm/confinedrv-loops.lock || { err "error: could not lock [/dev/shm|/tmp]/confinedrv-loops: will retry" mylockfile-create $shm/confinedrv-loops.lock || { err "error: lock did not succeed; please execute only once but somewhat later:" echo " confinedrv --freeloop $target" >&2; return 1; };}; trap '' SIGQUIT SIGTERM SIGINT; declare -a loopusage loopusage_origin loopusage_access loopusage_offset touch $shm/confinedrv-loops; mv $shm/confinedrv-loops $shm/confinedrv-loops.tmp while read dest loop origin access offset; do [[ -z "$dest" || -z "$loop" ]] && continue; #echo losetup -d $loop; loopno="${loop#/dev/loop}" if [[ "$dest" = "$target" ]] ; then if [[ -z "${loopno##[0-9]*}" ]]; then if [[ -n "${loopuasge[loopno]}" && ( "${loopusage_origin[loopno]}" != "$origin" || "${loopusage_access[loopno]}" != "$access" || "${loopusage_offset[loopno]}" != "$offset" ) ]];then warn "inconsistant entries for $dest /dev/loop$loopno in $shm/confinedrv-loops: ignoring any but the last one."; echo ">> $dest /dev/loop$loopno ${loopusage_origin[loopno]} ${loopusage_access[loopno]} ${loopusage_offset[loopno]}" >&2; echo ">> $dest $loop $origin $access $offset" >&2; fi if ein $loop $@; then let loopusage[loopno]+=1; else let loopusage[loopno]+=0; loopusage_origin[loopno]="$origin"; loopusage_access[loopno]="$access"; let loopusage_offset[loopno]=offset; fi elif ! ein $loop $@; then err "loop device $loop in use by $target does not seem to have a number; trying to deallocate though it may be in use by another instance of confinedrv; "; losetup -d "$loop" || { err=1; err "error trying to deallocate $loop."; echo "$dest $loop $origin $access $offset"; } fi else if [[ -z "${loopno##[0-9]*}" ]]; then let loopusage[loopno]+=1; fi echo "$dest $loop $origin $access $offset" fi done <$shm/confinedrv-loops.tmp >$shm/confinedrv-loops; #echo "loopusage: ${!loopusage[*]} ~ ${loopusage[@]}" >&2; for loopno in ${!loopusage[*]}; do if [[ loopusage[loopno] -le 0 ]]; then losetup -d /dev/loop$loopno || { err=1; err "error trying to deallocate /dev/loop$loopno."; echo "$target /dev/loop$loopno ${loopusage_origin[loopno]} ${loopusage_access[loopno]} ${loopusage_offset[loopno]}"; } fi done >>$shm/confinedrv-loops; [[ err -ne 0 ]] && { err "error freeing loop devices for $target; please execute somewhat later:"; echo " confinedrv --freeloop $target" >&2; } rm -f $shm/confinedrv-loops.tmp; mylockfile-remove $shm/confinedrv-loops.lock trap - SIGQUIT SIGTERM SIGINT; return $err; } verbose=1; readall=false; annotate=true; gapmode=0; mbrmode=1; ret=0; if [[ $# -le 0 ]]; then echo -e "$(basename "$0") --help/--license\n" fi getdevno() { local attr x usr grp major minor rest read attr x usr grp major minor rest < <(ls -l "$1";) echo "${major%,}:$minor" } # /dev/zero can not be used because it is a character rather than a block device # zerodev="$(getdevno /dev/zeroblock 2>/dev/null)"; # if [ -z "$zerodev" ]; then && { zerodev=1:5; mknod /dev/zero c 1 5; } getloops() { local destination loopdev origin access; mylockfile-create $shm/confinedrv-loops.lock || err "possible inconsistency reading [/dev/shm|/tmp]/confinedrv-loops: could not lock (timeout)." trap "mylockfile-remove $shm/confinedrv-loops.lock" EXIT touch $shm/confinedrv-loops while read destination loopdev origin access offset; do if [[ "$origin" = "$org" && offset -eq 0 ]]; then if [[ "$access" = "rw" ]]; then [[ -n "$loop" && "$loop" != "$loopdev" ]] && warn "double allocation in [/dev/shm|/tmp]/confinedrv-loops: $org:ro ~ $loop $loopdev" export loop="$loopdev"; elif [[ "$access" = "ro" ]]; then [[ -n "$rolp" && "$rolp" != "$loopdev" ]] && warn "double allocation in [/dev/shm|/tmp]/confinedrv-loops: $org:rw ~ $rolp $loopdev" export rolp="$loopdev"; fi fi for srcno in ${!partsrc[@]}; do if [[ "$origin" = "${partsrc[srcno]}" && "$access" = "${partaccess[srcno]}" && offset -eq ${partoffset[srcno]} ]]; then [[ -n "${partloop[srcno]}" && "${partloop[srcno]}" != "$loopdev" ]] && warn "double allocation in [/dev/shm|/tmp]/confinedrv-loops: ${partsrc[srcno]}:${pasrtaccess[srcno]} ~ ${partloop[srcno]} $loopdev" partloop[srcno]=$loopdev fi done done <$shm/confinedrv-loops; mylockfile-remove $shm/confinedrv-loops.lock trap - EXIT [[ verbose -gt 1 ]] && echo "loop devices found: $loop $rolp" >&2 } initloops() { local partno=$1; local transient_loop transient_rolp thisloop thisaccess thiserr partmarked; declare -a partmarked; [[ -n "$rolp" || -n "$loop" ]] && { err "internal error: ${rolp:+ro-}loopdev already initialized."; exit 244; } getloops "$org"; # gets $rolp and $loop mkdir -p $shm if [[ partno -gt 0 ]]; then # loop=$(getoneloop "$org" rw) if [[ -z "$loop" ]]; then transient_loop=$(losetup -f) || { err "all loop devices in use (see --loopusage)."; exit 202; } losetup $transient_loop $org fi else unset loop; fi #rolp=$(getoneloop "$org" ro) if [[ -z "$rolp" ]]; then transient_rolp=$(losetup -f) || { [[ -n "$transient_loop" ]] && losetup -d $transient_loop; err "all loop devices in use (see --loopusage)."; exit 202; } losetup -r $transient_rolp $org blockdev --setro $transient_rolp chmod gua-w $transient_rolp >&9 2>&1 fi let thiserr=0; for srcno in ${!partsrc[@]}; do if [[ -z "${partloop[srcno]}" ]]; then # same file used twice? for src2no in ${!partsrc[@]}; do if [[ "${partsrc[srcno]}" = "${partsrc[src2no]}" && "${partoffset[srcno]}" = "${partoffset[src2no]}" && "${partaccess[srcno]}" = "${partaccess[src2no]}" && -n "${partloop[src2no]}" ]]; then partloop[srcno]="${partloop[src2no]}"; partmarked[srcno]=yes; break; fi done if [[ -z "${partloop[srcno]}" ]]; then thisloop=$(losetup -f) || { err "all loop devices in use (see --loopusage)."; let thiserr=202; break; } parttransloop[srcno]=$thisloop thisaccess="${partaccess[srcno]}" if [[ "$thisaccess" = "rw" ]]; then losetup --offset ${partoffset[srcno]} $thisloop ${partsrc[srcno]} elif [[ "$thisaccess" = "ro" ]]; then losetup -r --offset ${partoffset[srcno]} $thisloop ${partsrc[srcno]} blockdev --setro $thisloop chmod gua-w $thisloop >&9 2>&1 else err "unknown access rights for external partition image: $dest$srcno=${partsrc[srcno]}:$thisaccess."; unset parttransloop[srcno]; thiserr=244; fi fi fi done if [[ thiserr -gt 0 ]]; then [[ -n "$transient_loop" ]] && losetup -d $transient_loop; losetup -d "$transient_rolp"; for thisloop in ${parttransloop[@]}; do losetup -d $thisloop; done echo >&2; exit $thiserr ; fi if [ -b /dev/mapper/$dest ]; then dmsetup remove $dest droploop $dest $loop $rolp ${partloop[@]}; reloading=yes fi mylockfile-create $shm/confinedrv-loops.lock || err "error locking [/dev/shm|/tmp]/confinedrv-loops." trap '' SIGQUIT SIGINT SIGTERM; # ignore signal : ${rolp:="$transient_rolp"} ${loop:="$transient_loop"}; { [ -n "$loop" ] && echo "$dest $loop $org rw 0"; echo "$dest $rolp $org ro 0"; for srcno in ${!partsrc[@]}; do [[ -n "${partmarked[srcno]}" ]] && continue : ${partloop[srcno]:="${parttransloop[srcno]}"} echo "$dest ${partloop[srcno]} ${partsrc[srcno]} ${partaccess[srcno]} ${partoffset[srcno]}"; unset parttransloop[srcno]; done } >>$shm/confinedrv-loops mylockfile-remove $shm/confinedrv-loops.lock trap - SIGQUIT SIGINT SIGTERM; unset transient_rolp transient_loop } # device mapper can only control access in chunks of pagesize = 4096 = 2**3*512 PAGE_SIZE=$(getconf PAGE_SIZE) [[ PAGE_SIZE -eq 0 ]] && { warn "assuming page size of 4096."; PAGE_SIZE=4096; } [[ PAGE_SIZE/512*512 -ne PAGE_SIZE ]] && { err "page size not divisable by 512; exiting."; exit 222; } let PGsize=PAGE_SIZE/512 let PGspare=PGsize-1 possible_page_sizes=(4096 8192 16384 32768 65536); IssuePartRegion() { # access start end local partno=$1 access=$2 start=$3 end=$4 length; let length=end-start [[ access -lt -1 ]] && return if [[ partno -ge 0 ]]; then echo "$start $length linear ${partloop[partno]} 0" >&8 elif [[ access -ge 2 ]]; then echo "$start $length linear $loop $start" >&8 elif [[ access -eq 1 ]]; then echo "$start $length linear $rolp $start" >&8 elif [[ access -eq 0 ]]; then echo "$start $length zero" >&8 else echo "$start $length error" >&8 fi } roundup() { echo $(( ($1+PGspare) / PGsize * PGsize )); } rounddown() { echo $(( ($1) / PGsize * PGsize )); } min() { if [[ $1 -le $2 ]]; then echo $1; else echo $2; fi } max() { if [[ $1 -ge $2 ]]; then echo $1; else echo $2; fi } acc2str() { if [[ $1 -ge 2 ]]; then echo "rw"; elif [[ $1 -eq 1 ]]; then echo "r"; elif [[ $1 -eq 0 ]]; then echo "0"; else echo "-"; fi } NotePartRegion() { local partno=$1 access=$2 start=$3 end=$4 [[ start -le end ]] || return [[ prevend -eq start ]] || { err "internal error in NotePartRegion ($prevstart-$prevend, $start-$end+1)"; exit 244; } #echo "{$prevacc,$prevstart,$prevend}" #echo "<$access,$start,$end>" [[ partno -le -1 || -z "${partsrc[partno]}" ]] && partno=-1; # partno indicates non-mirrored space from an external partition file if [[ prevacc -eq access && prevpartno -eq partno ]]; then let prevend=end+1; return elif [[ prevacc -gt access || partno -ge 0 ]]; then let start=$(roundup prevend) [[ partno -ge 0 && ${partoffset[partno]} != $(((start-prevend)*512)) ]] && { err "internal error: did offset ${partsrc[partno]} with ${partoffset[partno]} though an offset of $((start-prevend)) would have been required."; exit 244; } if [[ prevpartno -ge 0 && start -ne prevend ]]; then let intermedstart=$(rounddown prevend) IssuePartRegion $prevpartno $prevacc $prevstart $intermedstart IssuePartRegion -1 $prevacc $intermedstart $start else IssuePartRegion $prevpartno $prevacc $prevstart $start fi [[ prevend -lt start && verbose -gt 1 ]] && warn "warning: access right extension $(acc2str $access)->$(acc2str $prevacc) $prevend..$((start-1))" else # prevacc -lt access let start=$(rounddown prevend) [[ partno -le -1 ]] || { err "internal error at $LINENO."; exit 244; } IssuePartRegion -1 $prevacc $prevstart $start [[ start -lt prevend && verbose -gt 1 ]] && warn "warning: access right extension $(acc2str $prevacc)->$(acc2str $access) $start..$(($prevend-1))" fi let prevstart=start prevend=end+1 prevacc=access prevpartno=partno; } # global pos extstart extend NoteExtendedPartition() { let extstart=$(rounddown $1) extend=$(roundup $(($2+1))) } skip() { local start=$1 extpos dstart; [[ pos -le start ]] || { err "trying to skip backwards from $start back to $pos"; exit 244; } if [[ extstart -lt extend ]]; then let extpos=$(min $(max $pos $extstart) $start); # extpos ... what we have to skip from $pos until we reach the extended partition header or the $start of the partition $1 before let dstart=$(max $(min $start $extend) $extpos); # 'diminished start position' ... $start of partition inside extended partition or end of the extended partition, but not before $extpos # also: let dstart=$(min $start $extend) + never skip in backward direction: max $extpos (the following command will not condone it; relevant iff $1=$start after $extend) NotePartRegion -1 $gapmode $pos $((extpos-1)); # ... skipping before extended partition NotePartRegion -1 1 $extpos $((dstart-1)); # ... extended partition header, intermediate space in ext. part or whole ext. part. if it has no sub-partitions NotePartRegion -1 $gapmode $dstart $((start-1)); # ... skip for what comes after the extended partition # if we wanted to verify that the previous commands should work under any condition: # $start is before extended partition: pos-extpos = pos-start: $gap, extpos = start = dstart => extpos-dstart: zerolen, dstart-start: zerolen # $start is inside extended partition: extpos = max(pos,exstart), dstart = max(pos,start) => pos-extpos: $gap, extpos-dstart: readable, dstart-start: zerolen # $start is after extended partition: extpos = max(pos,extstart), dstart = max(pos,extend) => pos-extpos: $gap, extpos-dstart: readable, dstart-start: $gap # annot: $start is after extended partition and extended partition unmarked: before ext. part: $gap, rest of ext.part gets readable, then skips with $gap # annot: $start is after extended partition and extended partition already marked: extpos = dstart = end of ext.part: simply skipping with $gap else NotePartRegion -1 $gapmode $pos $((start-1)) fi } prn_drvtable() { while read winstart winend mode dev devstart; do if [ "$dev" = "$loop" ]; then dev="(${org#/dev/}:rw)" elif [ "$dev" = "$rolp" ]; then dev="(${org#/dev/}:ro)" fi if [[ winstart -eq devstart ]]; then echo "$winstart $winend $mode $dev"; else echo "$winstart $winend $mode $dev $devstart"; fi done <"$drvtable" } cleanup() { local thisloop; sleep 0.3; rm -f "$drvtable"; [[ -n "$transient_rolp" ]] && losetup -d $transient_rolp; unset rolp; [[ -n "$transient_loop" ]] && losetup -d $transient_loop; unset loop; for thisloop in ${parttransloop[@]}; do loestup -d $thisloop; done; unset parttransloop } setupdevice() { let partnum=${#parts_w[@]}+${#parts_r[@]}+${#parts_z[@]}+${#parts_e[@]} unset loop rolp initloops ${#parts_w[@]} trap "cleanup" EXIT drvtable="$(mktemp confinedrv-XXXX-XXXX.drvtable)"; pos=0; prevacc=-2; prevpartno=-1; prevstart=-1; prevend=0; extstart=-1; extend=-2; extwritable=false; parttbl=""; found=0; maxdno=0; #max=0; { while read dno start end blocks parttype fsandflags; do if [[ -n "${dno##[0-9]*}" || -z "$dno$start$end$blocks$id$typeandflags" ]]; then if [[ "${dno,,}" = "partition" && "${start,,}" = "table:" ]]; then parttbl="${end,,}"; ! ein "$parttbl" gpt msdos && warn "warning: confinedrv has never been tested with the $parttbl partition table type (assuming like gpt)."; fi # upcase="${start^^} ${end^^} ${blocks^^} ${id^^} ${type^^}"; # if [ "${upcase##*GPT}" != "${upcase}" ]; then isgpt=true; warn "warning: program was not sufficiently tested with gpt disks."; # elif [ "${upcase#WARN}" != "$upcase" ]; then warn "fdisk -lu: $start $end $blocks $id $type" # fi else [[ dno -gt maxdno ]] && let maxdno=dno if [[ -z "$parttbl" ]]; then warn "warning: parted did not seem to list the type of partition table: assuming msdos."; parttbl="msdos"; fi start="${start%s}"; end="${end%s}"; type="${parttype,,}"; # echo "$start $end $type"; if [[ pos -eq 0 ]]; then NotePartRegion 0 $mbrmode 0 $start [[ -n ${partsrc[0]} ]] && let found++; let pos=start prevend=start fi #echo "$dev $pos $start" >&2 if [[ "${type#*erweitert}" != "$type" || "${type#*extended}" != "$type" ]]; # only very old versions of parted may also output in German then isext=true; [[ "$parttbl" != "msdos" ]] && { err "error: extended partitions only supported with dos partition tables."; exit 208; } else isext=false; fi if $extwritable && [[ extstart -le start && end -lt extend ]]; then continue; fi if ein $dno "${parts_w[@]}"; then skip $start NotePartRegion $dno 2 $start $end let pos=end+1; let found++; $isext && { extwritable=true; NoteExtendedPartition $start $end; } elif ein $dno "${parts_r[@]}"; then skip $start NotePartRegion $dno 1 $start $end let pos=end+1; let found++; $isext && { extwritable=true; NoteExtendedPartition $start $end; } elif ein $dno "${parts_z[@]}"; then skip $start NotePartRegion $dno 0 $start $end let pos=end+1; let found++; $isext && { extwritable=true; NoteExtendedPartition $start $end; } elif ein $dno "${parts_e[@]}"; then skip $start NotePartRegion $dno -1 $start $end let pos=end+1; let found++; $isext && { extwritable=true; NoteExtendedPartition $start $end; } elif $isext; then NoteExtendedPartition $start $end elif $readall; then skip $start NotePartRegion $dno 1 $start $end let pos=end+1; else skip $start NotePartRegion $dno 0 $start $end let pos=end+1; fi #[[ max -lt end ]] && let max=end #[[ min -gt start ]] && let min=start fi done < <( LANG=en_US.UTF-8 parted $org <<<$'unit s\nprint\nquit\n' | sort -g -k 2 -s; ) # done < <( fdisk -lu -b 512 $org | tr '*' ' ' | sort -g -k 2 -s; ) max=$(blockdev --getsz $org) if [[ "$parttbl" = "msdos" ]]; then skip $max else # alternative recovery partition table at the end (not included in mbr=image) NotePartRegion -1 $mbrmode $pos $(($max-1)) fi virtualdiskend=$(roundup $prevend); if [[ prevpartno -lt 0 || -z "${partsrc[prevpartno]}" ]]; then prevpartno=-1; else cat $drvtable; echo "$prevpartno ${partsrc[prevpartno]}"; err "internal error: expected having switched back to the original source disk at the end."; exit 244; fi IssuePartRegion $prevpartno $prevacc $prevstart $virtualdiskend } 8>"$drvtable" if [[ maxdno -ge 5 && extpend -lt 0 ]] && [[ "$parttbl" = "msdos" ]]; then err "extended partitions given as parameter but no extended partition called '...erweitert' or '...extended' found/listed by parted 'print'"; echo "you may wish to correct the matching for the name of the exteneded partition in the sources" >&2; err "as this is used as msdos disk the mapping will not function for extended partitions." echo >&2; fi if [[ verbose -gt 1 ]]; then msg "hdd mapping table:" prn_drvtable echo fi if [[ found -ne partnum ]]; then err "overlapping partitions specified (extended partition and partition inside extended partition) or"; err "no such partition on disk." # can detect overlapping partitions only in cases where partitions become fully overlapped msg "$found out of $partnum partitions processed.\n" [[ verbose -le 1 ]] && { prn_drvtable; echo >&2; } cleanup trap '' EXIT ret=207 else if [ -b /dev/mapper/$dest ]; then echo dmsetup reload $dest \<"$drvtable" #dmsetup reload $dest <"$drvtable"; dmsetup remove $dest # droploop "$dest"; # should have allocated the same loops for new device: never drop dmsetup create $dest <"$drvtable" else if [[ -n "$reloading" ]]; then echo dmsetup reload $dest \<"$drvtable" else echo dmsetup create $dest \<"$drvtable" fi dmsetup create $dest <"$drvtable" fi ret=$?; #if [ $? -eq 0 ]; then rm "$drvtable"; else echo "please remove $drvtable manually!"; fi if [ $ret -ne 0 ]; then cat $drvtable cleanup; trap '' EXIT; droploop "$dest"; else rm "$drvtable" fi trap '' EXIT echo fi return $ret #[[ ret -gt 0 ]] && exit $ret } initpartsrc() { local partsrcc err offset filesize partsize dno start end blocks parttype fsandflags; let partsrcc=0 err=0; for srcno in ${!partsrc[@]}; do if ein $srcno ${parts_w[@]}; then partaccess[srcno]="rw"; let partsrcc++; elif ein $srcno ${parts_r[@]}; then partaccess[srcno]="ro"; let partsrcc++; elif ein $srcno ${parts_z[@]} ${parts_e[@]}; then unset partsrc[srcno]; else partaccess[srcno]="rw"; parts_w+=($srcno); let partsrcc++; fi done if [[ partsrcc -gt 0 ]]; then while read dno start end blocks parttype fsandflags; do if [[ -z "${dno##[0-9]*}" && -n "$dno$start$end$blocks" ]]; then [[ "${start%s}" = "$start" || "${end%s}" = "$end" ]] && { err "internal error: parted / unit s did not seem to output by sector."; exit 244; } let start=${start%s} end=${end%s}; if [[ -n "${partsrc[dno]}" ]]; then let offset=($(roundup $start)-start)*512; # ?? stimmt roundup hier? - wird es nicht abgerundet, i.e. größere Zugriffsrechte gewinnen? let filesize=$(stat -c%s "${partsrc[dno]}"); # Problem: das würde nicht gehen, da er sonst vor dem Image mit dem Einblenden beginnen müßte oder den Beginn verliert let partsize=(end-start+1)*512 [[ filesize -ne partsize ]] && { err "file size $filesize of ${partsrc[dno]} does not match partition size $partsize."; let err=223; } let partoffset[dno]=offset [[ offset -ne 0 && verbose -gt 0 ]] && warn "$org$dno: ${partsrc[dno]} - Will have to accept offset of $offset for disk image; file system there may thus be unreadable!"; fi # new and independent if clause: handle a redefined MBR if [[ dno -eq 1 && -n "${partsrc[0]}" ]]; then let filesize=$(stat -c%s "${partsrc[0]}"); let partsize=(start)*512 let sizeok=1 for allowed_multiple in 1 $possible_page_sizes; do [[ filesize -eq $(( ( partsize + allowed_multiple - 1 ) / allowed_multiple * allowed_multiple )) ]] && { let sizeok=0; break; } done [[ sizeok -ne 0 ]] && { err "file size $filesize for MBR-file does not match the size of the empty space before the first partition ($partsize) and was not rounded up to an allowed page size multiple ($possible_page_sizes) either."; let err=223; } let partoffset[0]=offset fi fi done < <( parted $org <<<$'unit s\nprint\nquit\n' ) for srcno in ${!partsrc[@]}; do if [[ -z "${partoffset[srcno]}" ]]; then err "partition $org$srcno for ${partsrc[srcno]} does not exist."; unset partaccess[srcno] partsrc[srcno] partoffset[srcno]; let err=204; fi done fi [[ err -gt 0 ]] && { echo >&2; exit $err; } } initdest() { if [[ verbose -gt 1 ]]; then echo "dest - $dest: ${!partsrc[*]} / ${partsrc[*]}"; # indices list - content list: partitions laoded from file echo "org - $org e:${parts_e[@]}/z:${parts_z[@]}/r:${parts_r[@]}/w:${parts_w[@]}" echo " (groups: ${groups[@]})" echo; #exit fi [[ -z "$org" ]] && { echo "usage: confinedrv newdevice=olddevice or newdevice=olddevice1,2,3 respectively (see --help)." >&2; echo >&2; exit 201; } [[ "${org#./}" = "${org#../}" ]] && { [ -e "/dev/$org" ] && org="/dev/$org"; } [ -e "$org" ] || { err "device $org not found."; echo >&2; exit 203; } if [ -e "/dev/$dest" ]; then if [[ "${dest%[0-9]}" = "${dest}" ]]; then warn "newly created device /dev/mapper/$dest has a similar name as a hard drive which already exists (/dev/$dest); please avoid this."; else err "device /dev/mapper/$dest has a similar name as a hard drive which already exists (/dev/$dest); please avoid this."; echo "confinedrv thought that this was a destination argument for a drive to be set up." >&2; echo "However destination arguments (like /dev/mapper/sdx) do not use to end with a number under Linux." >&2 echo "did you mean that /dev/$dest was a source argument rather than a destination argument?" >&2 echo "make sure you write something like ${prevdest:-sdz}=sda1,2,3 ... ${prevdest:-sdz}2=./mypartitionfile to fade in a partition image from an external source (left side arguments for external image sources must be a praefix of left side at device setup.)." >&2; echo >&2; exit 201; fi fi if [[ 0 -eq $(( ${#parts_e[@]} + ${#parts_z[@]} + ${#parts_r[@]} + ${#parts_w[@]} )) ]]; then err "drives without a single partition specifier are not allowed."; echo "did you mean $dest=$org:r1,2,3:w4,5,6;" >&2; echo "or did you mean ${prevdest:-sdz}=./${org#./} (partition image source argument) where '${prevdest:-sdz}' is a/the drive to be set up (being mentioned right before the partition image source argument)." >&2; echo >&2; exit 201; fi #echo initpartsrc initpartsrc; #echo initpartsrc done. # echo $dest#$org#${#parts[@]} # echo ${parts[@]} # echo ----------; exit prevdest="$dest"; # echo "prevdest: $prevdest"; } processparams() { [[ verbose -gt 0 ]] && echo $'\e[;33mconfinedrv - visit us on www.elstel.org/qemu\e[0m' >&2; # # sdx=sda4,5:r2,3:w4,5:e6:z7,8,9 # note: with bash 4.2.37: ${aa%%[0-9]*} yields the same as ${aa%%[0-9,]*} !! # local dest0 org0 destt groups dest_first isdir srcpartfile orgg unset dest org parts_w parts_r parts_z parts_e partsrc partloop parttransloop partoffset partacccess declare -a parts_w parts_r parts_z parts_e partsrc partloop parttransloop partoffset partacccess while [[ $# -gt 0 ]]; do # # confinedrv sdy=sdb:r3:w5,6 sdy5=/media/esatahdd/partitionimages/sdb5.part # i.e. if destt[0] is an individual partition (praefixed by $dest) rather than a whole drive then it will be the assignment of a partition to a file name # if so: destt will be an array of partition numbers (with the $dest-praefix removed from the first of them) # otherwise: destt will be empty (unset destt) # new in v1.7.7: confinedrv sdy=sdb:r3:w5,6 mbr=/boot/my.mbr (no virtual drive must start with the letters 'mbr' though) # read dest0 org0 <<<"${1/=/ }" read -a destt <<<"${dest0//,/ }" dest_first="${destt[0]#$dest}"; if [[ ${#dest_first} -ne ${#destt[0]} || -n "$dest" && "$dest_first" = "mbr" ]]; then destt[0]="$dest_first"; else if [[ ${#destt[@]} -gt 1 ]]; then err "'$dest0': praefix does not match the current drive '$dest'"; echo >&2; exit 201; fi unset destt; fi # # part1=~/file ... or ... mbr=~/file # if [[ ${#destt[@]} -ge 1 ]]; then let err=0; [ -e "$org0" ] || { err "partition from external source: no such file, device or directory: $org0"; err=1; } if [ -d "$org0" ]; then isdir=1; org0="${org0%/}"; orgg="$( awk '/[Dd][Ii][Ss][Kk]/{ $0=$2; gsub(/^[/]dev[/]/,""); gsub(":$",""); print; }' "$org0/unitsprint.parted" )"; [[ -z "$orgg" ]] && { err "partition index file $org0/unitsprint.parted seems to be corrupt (does not contain a Disk-line) or does not exist at all."; } else isdir=0; fi for destpart in "${destt[@]}"; do if [[ -z "${destpart##[0-9]*}" && -n "${destpart##0*}" || "$destpart" = "mbr" ]]; then [[ "$destpart" = "mbr" ]] && let destpart=0 if [[ isdir -eq 0 ]]; then partsrc[$destpart]="$org0"; else if [[ destpart -eq 0 ]]; then srcpartfile="$org0/$orgg.mbr"; else srcpartfile="$org0/$orgg$destpart.part"; fi [ -e "$srcpartfile" ] && ! [ -d "$srcpartfile" ]; [[ $? -eq 0 ]] || { err "either $srcpartfile does not exist or it is a directory: expected partition image file."; err=1; } partsrc[$destpart]="$srcpartfile" fi else err "partition from external source: invalid destination partition number: '$destpart'"; err=1; fi done [[ err -gt 0 ]] && { echo >&2; exit 201; } else # # if multiple drives are set up within the same command line i.e. sdx=... sdy=... perform the setup now before changing to a new virtual target drive # if [[ -n "$dest" ]]; then initdest setupdevice; unset dest org parts_w parts_r parts_z parts_e groups partsrc partloop parttransloop partoffset partacccess declare -a parts_w parts_r parts_z parts_e partsrc partloop parttransloop partoffset partacccess fi dest="$dest0"; org="$org0"; [[ "${dest#mbr}" != "$dest" ]] && { err "no virtual drive is allowed to start with 'mbr'; use drive names like sdx/sdy/sdz.\n"; exit 201; } read -a groups <<<"${org//:/ }" org=${groups[0]%%[0-9,]*}; groups[0]=${groups[0]#$org}; # now: can be sth. like "sda1,3" or "sda:r1,3:z2" (iff first group empty then move last group into first position) [[ -z "${groups[0]}" ]] && { let ng=${#groups[@]}; groups[0]="${groups[ng-1]}"; unset groups[ng-1]; } for group in "${groups[@]}"; do if [[ "${group##[0-9]*}" != "$group" ]]; then typ='w'; else typ="${group:0:1}"; group="${group:1}"; fi ein "$typ" e z r w || { err "access rights for partitions must be one of r/w/z/e."; echo >&2; exit 201; } targetvar=parts_$typ[0]; [[ -n "${!targetvar}" ]] && { err "error: already specified: $typ-access rights for $org ($group, ${!targetvar}, ... )."; echo >&2; exit 201; } read -a parts_$typ <<<"${group//,/ }" # parts=($(sort -g <<<"${parts[@]}";)) err=false for p in ${group//,/ }; do [[ -n "${p##[0-9]*}" || -z "${p##0*}" ]] && { err=true; err "invalid partition number: $p.";echo >&2; } done $err && exit 201; done fi shift done; if [[ -n "$dest" ]]; then initdest setupdevice; unset dest org parts_w parts_r parts_z parts_e groups partsrc partloop parttransloop partoffset partacccess fi } let readout_mbr=0; while [[ $# -gt 0 ]]; do case "$1" in --version) echo "confinedrv 1.7.7"; echo;; -h|--help) echo -e "confinedrv [--ra] sdx=sda1,2,3,4 sdy=sdb1,2,3,4" echo -e "confinedrv [--ra] sdx=sda1,2,3 mbr=./myfile.mbr sdx1=./image1.part" echo -e "confinedrv [--ra] sdx=sda:r1,2:w3:z4:e7 sdy=sdb:w3" echo -e " --ra ... everything at least readable; default is zeroing out." echo -e " --mbr err/zero/ro/rw ... master partition table & boot record access rights (std:ro)" echo -e " --gap err/zero/ro/rw ... access rights for gaps (std:zero)" echo -e "confinedrv -d/-r/--remove sdx sdy ..."; echo -e "confinedrv [--noannot] -i/--info sdx sdy ... show annotated drive mapping table"; echo -e "confinedrv --loopusage [sdx sdy] ... show all loop devices used for sdx."; echo -e " -v/--verbose/-q/--quiet: show mapping table / do not show alignement warnings" echo -e "confinedrv readout-mbr --from [/dev/]sda --into myfile.mbr (actually reads a bit more than the MBR)\n" ;; -d|-r|--remove) while [ -n "$2" ]; do drv="${2#/dev/mapper/}" dmsetup remove "$drv" droploop "$drv"; [[ $? -ne 0 ]] && let ret=206 shift done; exit $ret;; -i|--info) check4parted; while [ -n "$2" ]; do echo "*** $2 ***"; unset slurped disk endofdisk ${!loop_*}; while read winstart winlen mode dev devstart; do loopno="${dev#7:}"; # '7:' - is the major number of all loop devices if [[ "$loopno" != "$dev" ]]; then devar="loop_${loopno}_ident"; devident=${!devar}; devar="loop_${loopno}"; basedev=${!devar}; if [[ -z "$devident" ]]; then read loopdev data basedev < <( losetup /dev/loop${loopno}; ); basedev="${basedev#(}"; basedev="${basedev%)}"; [[ -z "$basedev" ]] && basedev=/dev/loop$loopno isro="$(blockdev --getro /dev/loop$loopno)"; if [[ $isro -eq 0 ]]; then devident="(${basedev}:rw)"; else devident="(${basedev}:ro)"; fi export $devar="$basedev"; export ${devar}_ident="$devident"; fi else unset devident; fi if ! $annotate; then if [[ winstart -eq devstart ]]; then echo "$winstart $winlen $mode $devident"; else echo "$winstart $winlen $mode $devident $devstart"; fi else annotdev=$(awk '{ gsub(/[^A-Za-z0-9]/,"_"); print; }' <<<"$basedev"); # haspartitions: simply takes all /dev/sda which are not a subpartitions for annotation let haspartitions=1; [[ "${basedev:0:5}" = "/dev/" && "${basedev%[0-9]}" = "$basedev" ]] && let haspartitions=0; if [[ haspartitions -eq 0 ]] && ! ein $annotdev $slurped; then slurpdev $annotdev "$basedev" slurped="$annotdev $slurped" fi fi done < <( dmsetup table $2; ) if $annotate; then while read winstart winlen mode dev devstart; do loopno="${dev#7:}"; # '7:' - is the major number of all loop devices if [[ "$loopno" != "$dev" ]]; then devar="loop_${loopno}_ident"; devident=${!devar}; devar="loop_${loopno}"; basedev=${!devar}; else devident="$dev"; basedev="$dev"; fi let winend=winstart+winlen for annotorgdev in $slurped; do eodvar=${annotorgdev}_endofdisk; let endofdisk=${!eodvar} annotstart=${annotorgdev}_start_${winstart}; if [[ -n "${!annotstart}" ]]; then annotstart="${!annotstart}"; else annotstart=${annotorgdev}_end_${winstart}; if [[ -n "${!annotstart}" ]]; then annotstart="${!annotstart}"; else annotstart="$winstart"; fi; fi annotend=${annotorgdev}_end_${winend}; if [[ -n "${!annotend}" ]]; then annotend="${!annotend}\t"; else annotend=${annotorgdev}_start_${winend}; if [[ -n "${!annotend}" ]]; then annotend="${!annotend}"; [[ winlen -le 2048 ]] && annotend="${annotend}=(+)$winlen" elif [[ $winend -eq $endofdisk ]]; then annotend="endofdisk(${annotorgdev##*_})"; else annotend="(+)$winlen\t"; fi; fi done; if [[ winstart -eq devstart ]]; then echo -e "$annotstart\t$annotend $mode\t$devident"; else echo -e "$annotstart\t$annotend $mode\t$devident\t$devstart"; fi done < <( dmsetup table $2; ) fi echo; shift; done; exit 0;; --loopusage) if [ -e $shm/confinedrv-loops.lock -o -e $shm/confinedrv-loops ]; then if mylockfile-create $shm/confinedrv-loops.lock; then didlock=true; else didlock=false; err "error: could not lock [/dev/shm|/tmp]/confinedrv-loops"; fi if [[ $# -le 1 ]]; then cat $shm/confinedrv-loops else shift; ( IFS="|"; egrep "$*" $shm/confinedrv-loops; ) fi $didlock && mylockfile-remove $shm/confinedrv-loops.lock else echo "no loop devices in use by confinedrv." >&2; fi; echo >&2; exit 0;; --freeloop) let ret=0; while [ -n "$2" ]; do droploop "$2" || let ret=206; shift; done; exit $ret;; --ra) readall=true;; --gap) case "$2" in zero|z|0) gapmode=0;; error|err|e) gapmode=-1;; read|ro|r|1) gapmode=1;; write|read-write|readwrite|w|rw|2) gapmode=2;; esac; shift;; --mbr) case "$2" in zero|z|0) mbrmode=0;; error|err|e) mbrmode=-1;; read|ro|r|1) mbrmode=1;; write|read-write|readwrite|w|rw|2) mbrmode=2;; esac; shift;; -v|--verbose) let verbose++;; -q|--quiet) let verbose--;; --noannot|--no-annot) annotate=false;; --license) license;; --into) backup_mbr_file="$2"; [[ -e "$backup_mbr_file" ]] && { err "error: file does already exist: '$backup_mbr_file'"; echo >&2; exit 210; } shift;; --from) if [[ -e "$2" ]]; then read_mbr_from="$2"; elif [[ -e "/dev/$2" ]]; then read_mbr_from="/dev/$2"; else err "error: device not found: $2"; echo >&2; exit 211; fi shift;; -*) err "unknown option $1"; echo >&2; exit 201;; readout-mbr) let readout_mbr=1;; *) [[ readout_mbr -eq 1 ]] && { err "superfluous parameters for readout-mbr: $*"; echo >&2; exit 201; } [[ -n "$backup_mbr_file" || -n "$read_mbr_from" ]] && { err "use --from and --into only together with readout-mbr."; echo >&2; exit 201; } [[ $(id -u) -ne 0 ]] && { err "confinedrv must be run as root.";echo >&2; exit 200; } check4parted; processparams "$@" shift $# ;; esac; shift; done if [[ readout_mbr -eq 1 ]]; then [[ -z "$backup_mbr_file" ]] && { err "use --into to specify the backup mbr file."; echo >&2; exit 201; } [[ -z "$read_mbr_from" ]] && { err "use --from to specify device to read MBR from."; echo >&2; exit 201; } echo "reading out spare space before first partition including the MBR from $read_mbr_from:" >&2 check4parted; let length=0; while read dno start end blocks parttype fsandflags; do if [[ "$dno" = "1" ]]; then [[ "${start%s}" = "$start" || "${end%s}" = "$end" ]] && { err "internal error: parted / unit s did not seem to output by sector."; exit 244; } let sectors_indeed=${start%s}; let read_sectors=$(roundup $sectors_indeed); let count=read_sectors/PGsize; break; fi done < <( parted $read_mbr_from <<<$'unit s\nprint\nquit\n' ) [[ sectors_indeed -eq 0 ]] && { err "error by parted apparently caused by insufficient permissions." >&2; echo >&2; exit 200; } if [[ read_sectors -ne sectors_indeed ]]; then echo "reading $count * $PAGE_SIZE bytes which is a little bit more than the unused space before the first partition ($read_sectors * 512) ..." >&2; else echo "reading $count * $PAGE_SIZE bytes ..." >&2; fi echo "dd if=$read_mbr_from bs=$PAGE_SIZE count=$count of=$backup_mbr_file" dd if=$read_mbr_from bs=$PAGE_SIZE count=$count of=$backup_mbr_file; [[ $? -eq 0 ]] && echo "done." >&2; echo >&2; fi
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor