1 #!/usr/bin/ksh93
   2 
   3 #
   4 # CDDL HEADER START
   5 #
   6 # The contents of this file are subject to the terms of the
   7 # Common Development and Distribution License (the "License").
   8 # You may not use this file except in compliance with the License.
   9 #
  10 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  11 # or http://www.opensolaris.org/os/licensing.
  12 # See the License for the specific language governing permissions
  13 # and limitations under the License.
  14 #
  15 # When distributing Covered Code, include this CDDL HEADER in each
  16 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  17 # If applicable, add the following below this CDDL HEADER, with the
  18 # fields enclosed by brackets "[]" replaced with your own identifying
  19 # information: Portions Copyright [yyyy] [name of copyright owner]
  20 #
  21 # CDDL HEADER END
  22 #
  23 
  24 #
  25 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  26 # Use is subject to license terms.
  27 #
  28 
  29 #
  30 # Generate USB image from iso
  31 # Originally from slim_install, modified by Toomas Soome.
  32 #
  33 
  34 # Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not
  35 # POSIX-conformant
  36 export PATH=/usr/xpg4/bin:/bin:/usr/bin:/usr/sbin
  37 
  38 # Make sure all math stuff runs in the "C" locale to avoid problems
  39 # with alternative # radix point representations (e.g. ',' instead of
  40 # '.' in de_DE.*-locales). This needs to be set _before_ any
  41 # floating-point constants are defined in this script).
  42 if [[ "${LC_ALL}" != "" ]] ; then
  43     export \
  44         LC_MONETARY="${LC_ALL}" \
  45         LC_MESSAGES="${LC_ALL}" \
  46         LC_COLLATE="${LC_ALL}" \
  47         LC_CTYPE="${LC_ALL}"
  48         unset LC_ALL
  49 fi
  50 export LC_NUMERIC=C
  51 
  52 #######################################################################
  53 # usage
  54 #       Print the usage message.
  55 # Input: none
  56 # Returns: none
  57 #
  58 #######################################################################
  59 function usage
  60 {
  61         print -u2 "\nUsage: "
  62         print -u2 "${progname} iso_file usb_image tmpdir"
  63         print -u2 "iso_file  : The path to an existing iso file."
  64         print -u2 "usb_image : The path to usb image to be created."
  65         print -u2 "tmpdir    : Temporary directy used during usb image " \
  66             "creation.\n"
  67 
  68         print -u2 "If tmpdir does not exist it will be created. If " \
  69             "it or subdirectories under tmpdir can not be created " \
  70             " an error is generated and this script exits."
  71         
  72 }
  73 
  74 #######################################################################
  75 # get_filesize
  76 #       Get filesize for the file argv[2] and return it to the
  77 #       variable defined by argv[1].
  78 # Input:
  79 #       $1 - variable to return filesize in
  80 #       $2 - file to query
  81 #
  82 # Returns:
  83 #       $1 - variable to return filesize in
  84 #
  85 #       -1 if file does not exist
  86 #
  87 #       A non-zero exit code only for internal errors and success
  88 #       in all other cases
  89 #
  90 #######################################################################
  91 function get_filesize
  92 {
  93         set -o errexit
  94         nameref filesize_ret="$1"       # return filesize into this varable
  95         typeset filename="$2"           # file to query
  96         integer filesize=-1             # temporary integer for "read" below
  97         typeset dummy # dummy string
  98 
  99         if [[ -f "${filename}" ]] ; then
 100                 ls -lb "${filename}" | \
 101                     grep "${filename}" | \
 102                     read dummy dummy dummy dummy filesize dummy 
 103         fi
 104 
 105         (( filesize_ret=filesize ))
 106         return 0
 107 }
 108 
 109 #
 110 #
 111 #######################################################################
 112 # cleanup
 113 #       This function attempst to clean up any resources this script
 114 #       could generate. Depending on where in the script this function
 115 #       is involked some resouces may not be there to cleanup, but
 116 #       that will not adversely effect anything.
 117 #
 118 #       This function is not defined using the function keyword
 119 #       to avoid an exit loop.
 120 #
 121 # Input: none
 122 # Returns: none
 123 #
 124 #######################################################################
 125 cleanup ()
 126 {
 127 
 128         {
 129                 trap "" ERR INT
 130                 set +o errexit
 131 
 132                 # unmounting, and uninstalling the lofi'ed devices and
 133                 # cleanup temporary files.
 134                 IFS=''
 135 
 136                 typeset mount_output="$(mount)"
 137 
 138                 [[ "${mount_output}" == ~(E).*"${usb_path}".* ]] && \
 139                     umount "${usb_path}"
 140 
 141                 [[ "${mount_output}" == ~(E).*"${iso_path}".* ]] && \
 142                     umount "${iso_path}"
 143 
 144                 lofiadm "${usb_file}" && \
 145                     lofiadm -d "${usb_file}"
 146 
 147                 lofiadm "${iso_file}"  && \
 148                     lofiadm -d "${iso_file}"
 149 
 150                 if [[ -d "${iso_path}" ]] ; then
 151                         rm -rf "${iso_path}"
 152                 fi
 153 
 154                 if [[ -d "${usb_path}" ]] ; then
 155                         rm -rf "${usb_path}"
 156                 fi
 157 
 158                 #
 159                 # If the tmpdir did not exist this script could
 160                 # have created it, so remove it here.
 161                 #
 162                 if ! ${tmpdir_existed} ; then
 163                         rm -rf "${tmpdir}"
 164                 fi
 165          } > /dev/null 2>&1
 166 
 167 }
 168 
 169 #######################################################################
 170 # error_handler
 171 #       The error_handler for this script. Will cleanup the usb image
 172 #       that could have been partially created. Then invoke cleanup
 173 #       to clean up any temporary resouces.
 174 #
 175 #       This function is not defined using the function keyword
 176 #       to avoid an exit loop.
 177 #
 178 # Input: none
 179 # Returns: none
 180 #
 181 #######################################################################
 182 error_handler ()
 183 {
 184         trap "" ERR INT
 185         set +o errexit
 186 
 187         print -u2 "\nError:\n"
 188         print -u2 -r -- "${progname}: $*"
 189         cleanup
 190 
 191         #
 192         # If an error was encountered while attempting to create
 193         # the new usb image file don't leave a possibley partially
 194         # constructed one around.
 195         #
 196         if [[ -f "${usb_file}" ]] ; then
 197                 rm -rf "${usb_file}" > /dev/null 2>&1
 198         fi
 199 
 200         exit 1
 201 }
 202 
 203 # main
 204 #######################################################################
 205 # main
 206 #
 207 # Input:
 208 #       iso_file  : The path to an existing iso file.
 209 #       usb_image : The path to usb image to be created.
 210 #       tmpdir    : Temporary directroy used during usb image creation.
 211 #
 212 #       If tmpdir does not exist it will be created. If it can not be
 213 #       created an error is generated and this script exits.
 214 #
 215 #       This script must be run as root"
 216 #
 217 # Logic Flow:
 218 #       Set up error handling.
 219 #       Confirm input arguments.
 220 #       Create temporary directories.
 221 #       Mount up the existing ISO image file.
 222 #       Compute the size for the new USB image.
 223 #       Create and mount an empty new USB image.
 224 #       Copy the contents of the ISO file to the new USB image.
 225 #       Remove GRUB entries from the USB image which apply only to ISO.
 226 #       Set the file protections for the new USB image.
 227 #
 228 # Returns:
 229 #       1 on failure
 230 #       0 on success
 231 #
 232 #######################################################################
 233 builtin chmod
 234 builtin cp
 235 builtin mkdir
 236 builtin mv
 237 builtin rm
 238 
 239 typeset -r progname="$0"
 240 typeset -r iso_file="$1"
 241 typeset -r tmpdir="$3"
 242 typeset    tmpdir_existed=true
 243 typeset -r iso_path="${tmpdir}/iso"
 244 
 245 typeset -r usb_file="$2"
 246 typeset -r usb_path="${tmpdir}/usb"
 247 float      usb_size # need floating-point for the calculations below
 248 
 249 typeset isodev
 250 typeset devs
 251 typeset rdevs
 252 typeset s0devs
 253 typeset rs0devs
 254 typeset rs2devs
 255 
 256 #
 257 # Confirm input arguments.
 258 #
 259 if [[ $(id) != ~(E).*uid=0\(root\).* ]]; then
 260         print -u2 "Error:\nYou must run this script as root"
 261         usage
 262         exit 1
 263 fi
 264 
 265 if (( $# != 3 )) ; then
 266         print -u2 "Error:\nImproper arguments"
 267         usage
 268         exit 1
 269 fi
 270 
 271 #
 272 # Set up error handling.
 273 # Use set -o errexit to trap errors. However, where possible,
 274 # explicitly check command return status for errors.
 275 #
 276 trap "error_handler Error or interrupt encountered. Exiting" ERR INT
 277 set -o errexit
 278 
 279 #
 280 # Create temporary directories.
 281 #
 282 [[ ! -d $tmpdir ]] && tmpdir_existed=false
 283 mkdir -p "${iso_path}"
 284 if [[ ! -d "${iso_path}" ]] ; then
 285         error_handler "Unable to create or access tmpdir ${iso_path}"
 286 fi
 287 
 288 mkdir -p "${usb_path}"
 289 if [[ ! -d "${usb_path}" ]] ; then
 290         error_handler "Unable to create or access tmpdir ${usb_path}"
 291 fi
 292 
 293 #
 294 # Mount up the existing ISO image file.
 295 #
 296 isodev="$(lofiadm -a "${iso_file}")" || \
 297     error_handler "Failed to lofiadm ${iso_file}"
 298 
 299 mount -F hsfs "${isodev}" "${iso_path}" || \
 300     error_handler "Failed to mount ${isodev} on ${iso_path}"
 301 
 302 
 303 #
 304 # Compute the size for the new USB image.
 305 # Use ISO file size + 20% to account for smaller block size on UFS
 306 # and the log. Round to nearest kbyte plus 512
 307 # plus 4MB for MBR+SMI label
 308 #
 309 get_filesize "usb_size" "${iso_file}"
 310 if (( usb_size == -1 )) ; then
 311         error_handler "Failed to get size of file ${iso_file}"
 312 fi
 313 (( usb_size=((int( (usb_size * 1.2) / 1024.) * 1024.) + 512) + 4194304 ))
 314 
 315 #
 316 # Create and mount an empty new USB image.
 317 #
 318 mkfile -n ${usb_size} "${usb_file}" || \
 319     error_handler "Failed to create file ${usb_file}"
 320 
 321 devs="$(lofiadm -la "${usb_file}")" || \
 322         error_handler "Failed to lofiadm file ${usb_file}"
 323 
 324 #
 325 # Set rdevs by replacing dsk with rdsk in devs
 326 #
 327 rdevs="${devs/dsk/rdsk}"
 328 # for mount
 329 s0devs="${devs/p0/s0}"
 330 # for newfs and installboot
 331 rs0devs="${rdevs/p0/s0}"
 332 # for prtvtoc | fmthard
 333 rs2devs="${rdevs/p0/s2}"
 334 
 335 #
 336 # create Solaris2 partition
 337 #
 338 fdisk -B "${rdevs}"
 339 prtvtoc "${rs2devs}" | nawk '
 340 /^[^\*]/ { r = $1; for(n = 1; n <= NF; n++) vtoc[r,n] = $n }
 341 END {
 342 vtoc[0,1] = 0;
 343 vtoc[0,2] = 2;
 344 vtoc[0,3] = 00;
 345 vtoc[0,4] = vtoc[8,6] + 1;
 346 vtoc[0,5] = vtoc[2,6] - vtoc[8,6];
 347 vtoc[0,6] = vtoc[2,6];
 348 printf("\t%d\t%d\t%02d\t%d\t%d\t%d\n",
 349         vtoc[0,1], vtoc[0,2], vtoc[0,3], vtoc[0,4], vtoc[0,5], vtoc[0,6]);
 350 printf("\t%d\t%d\t%02d\t%d\t%d\t%d\n",
 351         vtoc[2,1], vtoc[2,2], vtoc[2,3], vtoc[2,4], vtoc[2,5], vtoc[2,6]);
 352 printf("\t%d\t%d\t%02d\t%d\t%d\t%d\n",
 353         vtoc[8,1], vtoc[8,2], vtoc[8,3], vtoc[8,4], vtoc[8,5], vtoc[8,6]);
 354 }' | fmthard -s- "${rs2devs}"
 355 
 356 # newfs doesn't ask questions if stdin isn't a tty.
 357 newfs "${rs0devs}" </dev/null || \
 358         error_handler "Failed to construct the UFS file system ${rs0devs}"
 359 
 360 mount -o nologging "${s0devs}" "${usb_path}" || \
 361         error_handler "Failed to mount construct the UFS file system ${rs0devs}"
 362 
 363 #
 364 # Copy the contents of the ISO file to the new USB image.
 365 #
 366 print "Copying ISO contents to USB image..."
 367 (cd "${iso_path}"; find . -print | cpio -pmudV "${usb_path}")
 368 
 369 #
 370 # install bootblocks
 371 #
 372 installboot -mf "${usb_path}/boot/pmbr" "${usb_path}/boot/gptzfsboot" \
 373         "${rs0devs}"
 374 
 375 # If we get an error hereforth in menu.lst modifications, proceed with a warning
 376 trap "" ERR INT
 377 set +o errexit
 378 
 379 #
 380 # Remove "Hard Disk" GRUB entries from the USB image as they apply only to ISO.
 381 #
 382 nawk '
 383         BEGIN { inhard=0 }
 384         /^title.*Hard Disk$/ { inhard=1 }
 385         /^title/ { if (index($0,"Hard Disk") == 0) inhard=0 }
 386         inhard == 0 {print}
 387 ' ${usb_path}/boot/grub/menu.lst > ${usb_path}/boot/grub/menu2.lst
 388 if [[ $? == 0 ]] ; then
 389         mv ${usb_path}/boot/grub/menu2.lst ${usb_path}/boot/grub/menu.lst
 390 else
 391         print -u2 "Warning: Could not remove \"Hard Disk\" entry from boot menu"
 392 fi
 393 
 394 #
 395 # Set the file protections for the new USB image.
 396 #
 397 chmod 444 "${usb_file}"
 398 
 399 #
 400 # unmounting, and uninstalling the lofi'ed devices
 401 #
 402 cleanup
 403 
 404 printf "=== %s completed at %s\n\n" "$0" "$(date)"
 405 
 406 exit 0
 407