#!/bin/ksh93 LIC='[-?$Id$] [-copyright?Copyright (c) 2013 Jens Elkner. All rights reserved.] [-license?CDDL 1.0]' SDIR=$( cd ${ dirname $0; }; printf "$PWD" ) typeset -r FPROG=${.sh.file} typeset -r PROG=${FPROG##*/} for H in log.kshlib man.kshlib ; do X=$SDIR/$H [[ -r $X ]] && . $X && continue X=${ whence $H; } [[ -z $X ]] && print "$H not found - exiting." && exit 1 . $X done unset H typeset -r MEMDISK_URL='http://www.syslinux.org/wiki/index.php/MEMDISK#Parameters_and_options' typeset -r D_SYSFILES="IO.SYS" [[ -z "$SYSFILES" ]] && SYSFILES="" Man.addVar SYSFILES "List of files, which should be checked for coping as read-only, hidden, system to the start of the data space (see mkfs_pcfs(1M) option -i) in the new image. Only the first file found on \afloppy.img\a gets copied. Default: ${D_SYSFILES}" typeset -r D_CMDFILES="IO.SYS MSDOS.SYS COMMAND.COM" [[ -z "$CMDFILES" ]] && CMDFILES="${D_CMDFILES}" Man.addVar CMDFILES "Other files which should always be copied to the new image if found on \afloppy.img\a. Default: ${D_CMDFILES}" Man.addFunc unit2val '' '[+NAME?unit2val - converts a size[unit]] string into a dec number] [+DESCRIPTION?Tries to convert the given string into a decimal number and prints it to stdout. \aunit\a might be either k, m, g, t (power of 10 units) or K, Ki, M, Mi, G, Gi, T, Ti (power of 2 units). exit(1) on error.] \n\n\asize\a[\aunit\a] ' function unit2val { typeset SZ=$1 integer FACTOR=1 typeset SFX=${SZ##*([0-9])} case ${SFX} in k) FACTOR=1000 ;; K|Ki) FACTOR=$((1 << 10)) ;; m) FACTOR=1000000 ;; M|Mi) FACTOR=$((1 << 20)) ;; g) FACTOR=1000000000 ;; G|Gi) FACTOR=$((1 << 30)) ;; t) FACTOR=1000000000000 ;; T|Ti) FACTOR=$((1 << 40)) ;; *) Log.fatal "Invalid unit for disk size '$SFX' - exiting." && exit 1 ;; esac print $(( ${SZ%$SFX} * $FACTOR )) } Man.addFunc checkMBRfile '' '[+NAME?checkMBRfile - basic checks for the given MBR file] [+DESCRIPTION?Just do some basic checks wrt. the given MBR file. \a file\a must be of type file, at least 512 byte and not recognizable by \bfstype\b(1M). exit(1) on error.] \n\n\afile\a ' function checkMBRfile { typeset FILE="$1" X [[ -z "$FILE" ]] && Log.fatal "checkMBRfile: missing argument" && exit 1 if [[ -n "$FILE" ]]; then [[ ! -f "$FILE" ]] && \ Log.fatal "MBR file '$FILE' is not a file" && exit 1 [[ ! -r "$FILE" ]] && \ Log.fatal "MBR file '$FILE' is not readable" && exit 1 typeset -a A=( ${ ls -al "$FILE" ; } ) integer I=${A[4]} (( $I < 512 )) && \ Log.fatal "MBR file '$FILE' must have a size of at least 512 byte" \ && exit 1 X=${ fstyp "$FILE" 2>/dev/null ; } [[ -n "$X" ]] && Log.fatal "MBR file '$FILE' seems to be a $X" \ "partition and not an image with an MBR" && exit 1 fi } Man.addFunc getPartInfo '' '[+NAME?getPartInfo - get a partition definition] [+DESCRIPTION?Prints out the partition definition for the partition \anum\a (starting with 1) and the given raw \adevice\a in the format \bfdisk\b(1M) requires wrt. option \b-D\b and \b-A\b (\aid\a \aact\a \abhead\a \absect\a \abcyl\a \aehead\a \aesect\a \aecyl\a \arsect\a \anumsect\a). If such a partition cannot be found, nothing gets printed.] \n\n\anum\a \adevice\a ' function getPartInfo { integer NUM=$1 SEEN=0 typeset DEV="$2" fdisk -W - $DEV 2>/dev/null | while read LINE ; do if (( ! $SEEN )); then [[ ${LINE:0:4} == '* Id' ]] && SEEN=1 continue fi (( NUM-- )) (( $NUM )) && continue print "${LINE//+( )/ }" && return done } Man.addFunc getGeometry '' '[+NAME?getGeometry - get geometry infos] [+DESCRIPTION?Prints out the geometry infos of the given \araw_device\a. If there is no device or is not accessable, nothing gets printed.] \n\n\araw_device\a ' function getGeometry { [[ -z "$1" ]] && Log.fatal "getGeometry() - missing parameter" && exit 255 typeset DEV="$1" fdisk -g $DEV 2>/dev/null | while read LINE ; do [[ ${LINE:0:1} == '*' ]] && continue print "${LINE//+( )/ }" && return done } function fakeGeometry { [[ -z "$1" ]] && Log.fatal "geometry2pxe() - missing parameter" && exit 255 typeset -n G=$1 # get disk size in sectors integer SZS=$(( ${G[0]} * ${G[4]} * ${G[5]} )) # limits according to http://www.syslinux.org/wiki/index.php/MEMDISK#Specifying_geometry_and_image_type_manually integer MAX=$(( 1024 * 255 * 63 )) # ~8G integer MAXH=255 MAXS=63 if (( $SZS <= (1024 * 64 * 32) )); then MAXH=64 MAXS=32 fi (( $SZS > $MAX )) && Log.warn 'Leaving geometry as is (disk has more ' \ "than $MAX sectors): C/H/S = ${G[0]}/${G[4]}/${G[5]}" && return if (( ${G[0]} > 1024 || ${G[4]} > $MAXH || ${G[5]} > $MAXS )); then [[ -n ${SOPT[VERBOSE]} ]] && \ Log.info "Faking geometry C/H/S: ${G[0]}/${G[4]}/${G[5]}" G[0]=$(( SZS / MAXH / MAXS )) G[1]=${G[0]} G[4]=$MAXH G[5]=$MAXS return 1 fi } Man.addFunc suggestFatParams '' '[+NAME?suggestFatParams - suggest FAT parameters based on a partition size.] [+DESCRIPTION?Determines, which kind of FAT incl. parameters can be used wrt. the given number of \asectors\a in a partition. \asectors\a should be the same value as printed by "fdisk -W - raw_device" in the NUMSECT column of the desired partition. \aarrayName\a must be the name of an assoziative integer array where the results can be stored.] [+?In the array a trailing \bC\b in the key means counter (i.e. just a number), a \bS\b means number of sectors with a size of 512 bytes.] [+?If \afatType\a is given and has a value of 16 (FAT16) or 32 (FAT32) the function tries to prefer the given type. If not given, FAT32 is preferred.] [+EXIT STATUS]{ [0?On success.] [1?If a partition with the given size is not supported by this script or a parameter is missing.] } \n\n\asectors\a \aarrayName\a [\afatType\a] ' function suggestFatParams { [[ -z "$1" || -z "$2" ]] && \ Log.fatal "Missing parameters for suggestFatParams()." && return 1 integer FTYPE=$3 # a) mkfs_pcfs can handle sectors of 512 bytes, only # b) see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf pp. 20 integer VOLSZ=$1 SPC=0 FATSZ=0 typeset -n RES="$2" if (( $VOLSZ <= 8400 )); then RES[fatType]=12 Log.fatal 'Partitions < ' $(( 8400 / 2048 )) 'MB are unsupported' return 1 fi if (( $VOLSZ <= 66600 || ($FTYPE == 16 && $VOLSZ <= 4194209) )); then # as defined: RES[fatC]=2 RES[rootEntryC]=512 RES[rootDirS]=32 RES[reservedS]=1 # fdisk: if (( $VOLSZ < 65524 )); then SPC=1 elif (( $VOLSZ <= 131568 )); then SPC=2 elif (( $VOLSZ <= 262634 )); then SPC=4 elif (( $VOLSZ <= 524746 )) ; then SPC=8 elif (( $VOLSZ <= 1048958 )) ; then SPC=16 elif (( $VOLSZ <= 2097376 )) ; then SPC=32 #elif (( $VOLSZ <= 4194209 )) ; then else SPC=64 fi integer T2=$(( 2 * SPC + 2 )) FATSZ=$(( (VOLSZ - 34 + T2) / T2 )) # can't be > 0xFFFF RES[dataClusterC]=$(( (VOLSZ - 33 - FATSZ - FATSZ) / SPC )) RES[fatType]=16 (( $VOLSZ > 66600 )) && \ Log.info "Volume is > 32M, so a FAT32 would be appropriate, but" \ "for this the image with the bootloader must be a FAT32" \ "bootable image." else if (( $FTYPE == 16 )); then Log.warn "Image is > 4194209 sectors (~2G) - need to use FAT32" \ "and thus the volume bootloader may not work." fi # as defined: RES[fatC]=2 RES[rootEntryC]=0 RES[rootDirS]=0 RES[reservedS]=32 integer THRES=16#400000 for (( SPC=1; SPC <= 64; SPC*=2 , THRES*=2 )); do (( $VOLSZ < $THRES )) && break done integer T2=$((128 * SPC + 1)) FATSZ=$(( (VOLSZ - 33 + T2) / T2 )) # hmmm, mkfs_pcfs reserves an additional cluster for root directory on # FAT32 per FAT - not sure, whether this is correct. So we leave it as # is for now. RES[dataClusterC]=$(( (VOLSZ - 32 - FATSZ - FATSZ) / SPC )) RES[fatType]=32 fi RES[fatS]=$FATSZ RES[clusterS]=$SPC } Man.addFunc getFSinfo '' '[+NAME?getFSinfo - get Volume Boot Record infos] [+DESCRIPTION?Get information extracted from the Volume Boot Record (boot sector) of the given device \apartition\a or \afilesystem\a image and optionally print them out. \aarrayName\a is the name of an associative array, where the results will be stored.] [+SEE ALSO?\bfstyp\b(1M)] [s:sector]:[offset?Sector wrt. the start of the disk, where the Volume Boot record (partition) starts. If given, the OEM name gets extracted as well.] [p:print?Print out the infos found.] \n\n\apartition\a|\afilesystem\a \aarrayName\a ' function getFSinfo { integer OFF=-1 DOPRINT=0 while getopts "${ print ${Man.FUNC[getFSinfo]}; }" option ; do case "$option" in s) OFF=$OPTARG ;; p) DOPRINT=1 ;; esac done X=$((OPTIND-1)) shift $X typeset DEV="$1" [[ -z "$DEV" ]] && return 1 [[ -z "$2" ]] && Log.fatal 'getFSinfo(): arrayName missing' && return 1 typeset -n INFO="$2" typeset -a NAMES=( 'volume_id:Volume ID' 'volume_label:Volume Label' 'media:Media Type' 'drive_number:Drive Number' 'heads:Heads' 'bytes_per_sector:Bytes/Sector' 'sectors_per_cluster:Sectors/Cluster' 'count_of_clusters:Data Clusters' 'total_sectors:Total Sectors' 'sectors_per_track:Sectors/Track' 'reserved_sectors:Reserved Sectors' 'hidden_sectors:Hidden Sectors' 'fat_entry_size:FAT type' 'fats:Number of FATs' 'fat_size:Sectors/FAT' 'root_entry_count:Root Dir Entries' ) typeset X Y LABEL FMT KEY VAL integer LEN=0 I K typeset -A HNAMES=( ) for LABEL in "${NAMES[@]}" ; do X=${LABEL%:*} HNAMES[$X]="${.sh.match:1}" (( ${#X} > $LEN )) && LEN=${#X} done fstyp -a $DEV 2>/dev/null | while read KEY VAL ; do KEY=${KEY%:} [[ $KEY == 'bytes_per_sector' ]] && (( OFF*=$VAL )) case ${KEY} in bytes_per_sector|sectors_per_cluster|reserved_sectors|fats|\ root_entry_count|sectors_per_track|heads|hidden_sectors|\ total_sectors|fat_size|count_of_clusters|fat_entry_size) INFO[$KEY]="${ printf '%-10d' ${VAL} ; }" ;; volume_id) X="${ printf "%08X" ${VAL} ; }" INFO[$KEY]="${X:0:4}-${X:4}" ;; media) X=${ printf "%02X" $VAL ; } Y='' case $X in F0) Y='DS 18|36 sectors/track' ;; F8) Y='"fixed" disk' ;; F9) Y='DS 9|15 sectors/track' ;; FC) Y='SS 9 sectors/track' ;; FD) Y='DS 9 sectors/track' ;; FE) Y='SS 8 sectors/track' ;; FF) Y='DS 8 sectors/track' ;; esac INFO[$KEY]="0x${X} (${Y})" ;; volume_label) INFO[$KEY]="${VAL}" ;; drive_number) INFO[$KEY]="${ printf '0x%02X' ${VAL} ; }" ;; esac done if (( $OFF != -1 )); then X=${DEV%:*} X=${X/#\/dev\/rlofi\///dev/lofi/} dd if=${X} of=$TMPFILE skip=$(( OFF + 3 )) bs=1 count=8 2>/dev/null if [[ -s $TMPFILE ]]; then X=$(<$TMPFILE) && INFO[OEM]="$X" INFO[OEM]="$X" else OFF=-1 fi fi (( ! $DOPRINT )) && return 0 FMT="%-12s: %s\n" for X in drive_number media volume_label volume_id ; do printf "$FMT" "${HNAMES[$X]}" "${INFO[$X]}" done (( $OFF != -1 )) && printf "$FMT" "OEM ID" "${INFO[OEM]}" FMT="\t%${LEN}-s: %s\t\t%${LEN}-s: %s\n" for (( I=4, K=5; I < ${#NAMES[*]}; I+=2, K+=2 )); do X=${NAMES[$I]%:*} Y=${NAMES[$K]%:*} printf "$FMT" "${HNAMES[$X]}" "${INFO[$X]}" "${HNAMES[$Y]}" "${INFO[$Y]}" done } Man.addFunc doMain '' '[+NAME?doMain - does the main work.] [+DESCRIPTION?The meat of the script.] [+SEE ALSO?\bmkfile\b(1M), \blofiadm\b(1M), \bfdisk\b(1M), \bmkfs_pcfs\b(1M), \bmount\b(1M), \bdd\b(1)] ' function doMain { typeset -a PARAMS=() typeset OPTS="" # Basically the bootloader (FAT12|16 vs. FAT32) of the floppy image # dictates, what FS type we need to use typeset -A SRCINFO=( ) [[ -n ${SOPT[VERBOSE]} ]] && \ Log.info "Getting FAT infos from '${SOPT[FLOPPY]}'" && OPTS='-p' getFSinfo -s 0 $OPTS "${SOPT[FLOPPY]}" SRCINFO if (( ${SRCINFO[bytes_per_sector]} != 512 )); then Log.fatal "'${SOPT[FLOPPY]}' uses a sector size != 512 which is not supported "\ "by this script and related utilities." return 4 fi integer SRCFAT=${SRCINFO[count_of_clusters]} (( SRCFAT < 65525 )) && SRCFAT=16 || SRCFAT=32 # bootloader compat # create the new image file and lofi mount [[ -n ${SOPT[VERBOSE]} ]] && Log.info "Creating '${SOPT[FNAME]}' ..." mkfile ${IOPT[SZ]} "${SOPT[FNAME]}" || return 2 chmod 644 "${SOPT[FNAME]}" [[ -n ${SOPT[VERBOSE]} ]] && Log.info "Mounting '${SOPT[FNAME]}' via loopback ..." typeset LOFI RLOFI LOFI=${ lofiadm -a "${SOPT[FNAME]}" ; } || return 2 LOFIS+=( $LOFI ) RLOFI="/dev/r${LOFI#/dev/}" # let fdisk find out, what is appropriate ;-) [[ -n ${SOPT[VERBOSE]} ]] && Log.info "Partitioning $RLOFI ..." [[ -n "${SOPT[MBRFILE]}" ]] && PARAMS+=( '-b' "${SOPT[MBRFILE]}" ) fdisk ${PARAMS[@]} -B $RLOFI || return 2 typeset -a PART=( ${ getPartInfo 1 $RLOFI ; } ) typeset -a GEOM=( ${ getGeometry $RLOFI ; } ) if (( ${IOPT[FAKEGEOMETRY]} )); then fakeGeometry GEOM if (( $? )); then # geometry changed, so adjust image size IOPT[SZ]=$(( ${GEOM[0]} * ${GEOM[4]} * ${GEOM[5]} * 512 )) lofiadm -d $LOFI || return 2 mkfile ${IOPT[SZ]} "${SOPT[FNAME]}" || return 2 chmod 644 "${SOPT[FNAME]}" lofiadm -a "${SOPT[FNAME]}" $LOFI || return 2 print "${GEOM[@]}" >$TMPFILE # -S doesn't work on lofi files #PARAMS+=( '-S' "$TMPFILE" ) fdisk ${PARAMS[@]} -B $RLOFI || return 2 PART=( ${ getPartInfo 1 $RLOFI ; } ) fi fi Log.info "C/H/S geometry = ${GEOM[0]}/${GEOM[4]}/${GEOM[5]}" # Now delete the partition and add the DOS partition. fdisk choices are: # DOS12(0x01: FAT12, DOS 2.0+), DOS16(0x04: FAT16 DOS 3.0+), # DOSBIG(0x06: FAT16B DOS 3.31+), DOS16LBA(0x0e: FAT16X LBA, DOS 7.0+), # FAT32(0x0b: FAT32 CHS, DOS 7.1+), FAT32LBA(0x0c: FAT32X LBA, DOS 7.1+) typeset -A -i FAT=( ) suggestFatParams ${PART[9]} FAT $SRCFAT integer PARTID=16#c # FAT32 (( ${FAT[fatType]} == 16 )) && PARTID=6 # need to be in sync # basically just change byte 0x1c2 to $PARTID: fdisk -D "${PART[*]// /:}" $RLOFI || return 3 PART[0]=${PARTID} fdisk ${SOPT[VERBOSE]} -A "${PART[*]// /:}" $RLOFI || return 3 # Now lets create the FAT starting at byte ${PART[8]} * 512 [[ -n ${SOPT[VERBOSE]} ]] && Log.info "Formatting first partition (${RLOFI}:1) ..." # buggy mkfs copies in the whole -B file without checking bounds (SEGV). # Also the man page is misleading, because it actually means Volume Boot # Records. integer TOCOPY=${SRCINFO[reserved_sectors]} integer AVAIL=${PART[8]} # the first track is hidden [[ -n ${SOPT[VERBOSE]} ]] && Log.info "Trying to copy $TOCOPY hidden blocks ..." if (( $TOCOPY > $AVAIL )); then # Actually 3 should be sufficient Log.warn "Copying only the first $AVAIL sectors" \ "of '${SOPT[FLOPPY]}' because the new image is to small. So booting from" \ "the new image may not work." TOCOPY=$AVAIL fi dd if="${SOPT[FLOPPY]}" of=$TMPFILE bs=512 count=$TOCOPY 2>/dev/null || return 4 OPTS="fat=${FAT[fatType]},B=$TMPFILE" [[ -n "${SOPT[VERBOSE]}" ]] && OPTS+=",v" integer MOUNTED=0 mount -F pcfs "${SOPT[FLOPPY]}" "$TMPDIR2" 2>/dev/null && MOUNTED=1 if (( $MOUNTED )); then MOUNTS+=( "$TMPDIR2" ) for F in $SYSFILES ; do [[ -f "${TMPDIR2}/$F" ]] && \ OPTS+=",i=${TMPDIR2}/$F,h,r,s" && break done else Log.warn "Image is not yet bootable - you need to copy at least IO.SYS and COMMAND.COM to the root directory of the new image." fi [[ -n "${SOPT[VOLNAME]}" ]] && OPTS+=",b=${SOPT[VOLNAME]}" if (( ${IOPT[FAKEGEOMETRY]} || ${PART[0]} == 6 )); then OPTS+=",spc=${FAT[clusterS]}" (( ${IOPT[FAKEGEOMETRY]} )) && OPTS+=",nsect=${GEOM[5]},ntrack=${GEOM[4]}" fi print 'y' | mkfs -F pcfs -o $OPTS ${RLOFI}:1 || return 4 if (( $MOUNTED )); then if mount -F pcfs ${LOFI}:1 $TMPDIR3 ; then MOUNTS+=( "$TMPDIR3" ) for F in $CMDFILES ; do [[ -f $TMPDIR2/$F ]] && cp -p $TMPDIR2/$F $TMPDIR3/ done if (( ${IOPT[COPYALL]} )); then # IO.SYS doesn't need to be the first one ... cp -pr ${TMPDIR2}/* $TMPDIR3/ fi fi fi typeset -A DSTINFO=( ) print "" getFSinfo -s ${PART[8]} -p "${RLOFI}:1" DSTINFO print "" ls -al "${SOPT[FNAME]}" AVAIL=${DSTINFO[drive_number]} if (( $AVAIL >= 16#80 )); then (( AVAIL-=16#80 )) Log.info 'To be able to boot this image via PXE memdisk, according' \ "to \n$MEMDISK_URL\nyou" \ 'should add the following line to your PXE config:\n\t' \ "APPEND harddisk=$AVAIL c=${GEOM[0]} h=${GEOM[4]} s=${GEOM[5]}" fi } Man.addVar RMDIRS 'An indexed array with the name of all directories, which should be removed in \bcleanup()\b (at the end of the script) unconditionally. Order is important: removal will be done in LIFO order.' typeset -a RMDIRS=( ) Man.addVar MOUNTS 'An indexed array with the name of all mountpoints, which should be unmounted on \bcleanup()\b (at the end of the script). Order is important: removal will be done in LIFO order.' typeset -a MOUNTS=( ) Man.addVar LOFIS 'An indexed array with the name of all lofi mountpoints, which should be deleted on \bcleanup()\b (at the end of the script). Order is important: removal will be done in LIFO order.' typeset -a LOFIS=( ) Man.addVar TMPFILE 'File to use for temporary stuff. Gets removed unconditionally on \bcleanup()\b (at the end of the script).' Man.addVar IOPT 'Associative integer array with essential global values/command line options. Key: the option name. Value: an integer value.' Man.addVar SOPT 'Associative array with essential/command line options global values/command line options. Key: the option name. Value: a string.' Man.addFunc cleanup '' '[+NAME?cleanup - the janitor of the script] [+DESCRIPTION?Does the cleanup usually at the end of the script, which includes umounting all directories in \bMOUNTS\b, removing all lofi mounts in \bLOFIS\b, removing all directories in \bRMDIRS\b, removing \bTMPFILE\b in LIFO order as well as removing all files specified as argument to this function.] [+ENVIRONMENT]{' "${ Man.varUsage TMPFILE MOUNTS LOFIS RMDIRS; }" '} \n\n[\afile\a]... ' function cleanup { [[ -n $LOFI ]] && lofiadm -d $LOFI [[ -n "$TMPFILE" && -f "$TMPFILE" ]] && rm -f "$TMPFILE" typeset D integer N=${#MOUNTS[@]} (( N-- )) for (( ; N >= 0; N-- )); do umount ${MOUNTS[$N]} done N=${#LOFIS[@]} (( N-- )) for (( ; N >= 0; N-- )); do lofiadm -d ${LOFIS[$N]} done N=${#RMDIRS[@]} (( N-- )) for (( ; N >= 0; N-- )); do D="${RMDIRS[$N]}" [[ -n "$D" && -d "$D" ]] && rmdir "$D" done for FILE in $@ ; do [[ -n "$FILE" ]] && rm -f "$FILE" done } function showUsage { typeset WHAT="$2" getopts -a "$PROG" "${ print ${Man.FUNC[$WHAT]}; }" OPT --man } Man.addFunc MAIN '' '[+NAME?'"$PROG"' - creates a bootable DOS image] [+DESCRIPTION?This script can be used to create a PXE bootable DOS image.] [+?The given \afloppy.img\a is used to extract the bootloader from its volume boot record[s]] (boot sector) for the new image. So it might actually be any pcfs image of a primary and bootable partition (e.g. of /dev/rdiskette or /dev/rdsk/c0t0d0p0:c or /dev/sda1, etc.). If no floppy image is available, one may download one from http://www.allbootdisks.com/download/dos.html. Just in case you wanna save some space, usually the first sector (FAT12/16) or first 3 sectors (FAT32) of the image (volume/partition) are basically sufficient, however than you need to copy at least IO.SYS and COMMAND.COM into the image manually. It must not be mounted, when this script is running.] [h:help?Print this help and exit.] [F:functions?Print out a list of all defined functions. Just invokes the \btypeset +f\b builtin.] [H:usage]:[function?Show the usage information for the given function if available and exit. See also option \b-F\b.] [T:trace]:[fname_list?A comma or whitspace separated list of function names, which should be traced during execution.] [s:size]:[size?Raw size of the image file to create. The following unit names can be used: k, K, Ki, m, M, Mi, g, G, Gi, t, T, Ti - lower case letters are handled as power of 10 units, otherwise as power of 2 units. Default: 8M] [f:file]:[imagefile?The name of the raw image file to create. Default is \bdos-\b\asize\a.img.] [w:waste?Takes the given size as is. Otherwise the size gets adjusted to value which causes best alignment between virtual and physical disk size and thus lowest waste of space and is a multiple of 512 byte (as required by lofiadm).] [m:mbr]:[file?The file to use to extract the master boot program i.e. 440 bytes of the first 512 byte sector of the HDD image) for the new image. If not given the default one provided by fdisk will be used.] [v:verbose?Print out additional info about what happens.] [l:label]:[name?If given, the Volume Name of the pcfs partition of the new image gets overwritten with \aname\a (max. 11 characters). Otherwise the label from the \afloppy.img\a will be used.] [g:geometry?Do not fake geometry values so that sectors per track are <= 63 and heads == 255 or 64 depending on the size of the new image. Instead use the defaults choosen by \bmkfs_pcfs\b(1M).] [c:copy?Copy all files from the \afloppy.img\a to the new image.] [+GLOBAL SCRIPT VARIABLES]{'"${ Man.varUsage SYSFILES CMDFILES IOPT SOPT TMPFILE MOUNTS LOFIS RMDIRS; }"'} [+EXAMPLES]{ [Create the pcfs "snapshot"] [+?pfexec dd if=/dev/rdsk/c5t0d0p0:1 of=fat16-s0.pcfs bs=512 count=1] [Create the new image] [+?'"$PROG"' -s 64M -l DOSBOOT -f my.pcfs fat16-s0.pcfs] [Mount the image] [+?pfexec lofiadm -a my.pcfs /dev/lofi/10] [+?pfexec mount -F pcfs /dev/lofi/10:1 /mnt] [Copy the files you want in the image] [+?pfexec cp -r ~/DOS/* /mnt/] [Umount the image] [+?pfexec umount /mnt] [+?pfexec lofiadm -d /dev/lofi/10] [Copy the file to your tftp directory] [+?scp my.pcfs tftpserv:/etc/netboot/images/] [Add an appropriate entry to your PXE config menu file] [+?LABEL mypcfs]{ [+?MENU Label My ^SuperDOS] [+?KERNEL memdisk] [+?INITRD http://aa.bb.cc.dd/images/my.pcfs] [+?APPEND bigraw] [+?APPEND harddisk=0 c=62 h=64 s=32] } } \n\n\afloppy.img\a ' typeset -A -i IOPT=( [SZ]=-1 [NOWASTE]=1 [FAKEGEOMETRY]=1 [COPYALL]=0 ) typeset -A SOPT=( [FNAME]='' [MBRFILE]='' [VERBOSE]='' [VOLNAME]='' ) X="${ print ${Man.FUNC[MAIN]} ; }" while getopts "${X}" option ; do case "$option" in h) showUsage ${PROG} MAIN ; exit 0 ;; F) typeset +f ; exit 0 ;; H) if [[ ${OPTARG%_t} != $OPTARG ]]; then $OPTARG --man # self-defined types else showUsage "$OPTARG" "$OPTARG" # function fi exit 0 ;; T) typeset -ft ${OPTARG//,/ } ;; s) IOPT[SZ]=${ unit2val $OPTARG ; } ;; f) SOPT[FNAME]="$OPTARG" ;; w) IOPT[NOWASTE]=0 ;; m) SOPT[MBRFILE]="$OPTARG" ; checkMBRfile "${SOPT[MBRFILE]}" ;; v) SOPT[VERBOSE]='-d' ;; l) SOPT[VOLNAME]=${OPTARG:0:11} ;; g) IOPT[FAKEGEOMETRY]=0 ;; c) IOPT[COPYALL]=1 ;; esac done X=$((OPTIND-1)) shift $X # pre-flight checks if (( ${IOPT[SZ]} == -1 )); then IOPT[SZ]=${ unit2val 8M ; } elif (( ${IOPT[SZ]} < (1 << 23) )); then Log.fatal "Filesize < 8 MiB? Use a pcfs image from" \ "http://www.allbootdisks.com/download/dos.html" exit 1 fi (( ${IOPT[NOWASTE]} )) && \ IOPT[SZ]=$(( ((${IOPT[SZ]}/8225280 * 8225280 / 307200) + 1) * 307200 )) SOPT[FLOPPY]="$1" [[ -z "${SOPT[FLOPPY]}" ]] && \ Log.fatal "No floppy image file given." && showUsage $PROG MAIN && exit 0 [[ ! -r "${SOPT[FLOPPY]}" ]] && \ Log.fatal "Floppy image ${SOPT[FLOPPY]} is not readable." && exit 2 [[ ! -f "${SOPT[FLOPPY]}" ]] && \ Log.fatal "Floppy image ${SOPT[FLOPPY]} is not a file" && exit 2 if [[ -z ${SOPT[FNAME]} ]]; then SOPT[FNAME]=${ printf '%#i' ${IOPT[SZ]} ; } SOPT[FNAME]=${SOPT[FNAME]%i} SOPT[FNAME]=${SOPT[FNAME]/.[0-9]} SOPT[FNAME]="dos-${SOPT[FNAME]}.img" fi TMPFILE=${ mktemp /tmp/${PROG}.XXXXX ; } [[ -z $TMPFILE ]] && Log.fatal "Unable to create temp file." && exit 1 TMPDIR2=${ mktemp -d /tmp/${PROG}.XXXXX ; } [[ -z "$TMPDIR2" ]] && \ Log.fatal "Unable to create temp dir." && cleanup && exit 1 TMPDIR3=${ mktemp -d /tmp/${PROG}.XXXXX ; } [[ -z "$TMPDIR2" ]] && \ Log.fatal "Unable to create temp dir2." && cleanup && exit 1 RMDIRS=( $TMPDIR2 $TMPDIR3 ) doMain integer RES=$? if (( $RES )); then cleanup "${SOPT[FNAME]}" exit $RES fi sync cleanup