1 #!/bin/ksh -p
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 
  23 # Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24 # Use is subject to license terms.
  25 # Copyright 2012 OmniTI Computer Consulting, Inc.  All rights reserved.
  26 #
  27 # Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  28 
  29 # utility to pack and unpack a boot/root archive
  30 # both ufs and hsfs (iso9660) format archives are unpacked
  31 # only ufs archives are generated
  32 #
  33 # usage: pack   <archive> <root>
  34 #        unpack <archive> <root>
  35 #
  36 #   Where <root> is the directory to unpack to and will be cleaned out
  37 #   if it exists.
  38 #
  39 
  40 usage()
  41 {
  42         printf "usage: root_archive pack <archive> <root>\n"
  43         printf "       root_archive unpack <archive> <root>\n"
  44         exit 1
  45 }
  46 
  47 cleanup()
  48 {
  49         if [ -d $MNT ] ; then
  50                 umount $MNT 2> /dev/null
  51                 rmdir $MNT
  52         fi
  53 
  54         lofiadm -d "$TMR" 2>/dev/null
  55         if [ "$REALTHING" != true ] ; then
  56                 rm -f "$TMR"
  57         fi
  58         rm -f "$TMR.gz"
  59         rm -f /tmp/flist$$
  60 }
  61 
  62 do_unpack()
  63 {
  64         (
  65                 cd $MNT
  66                 find . -print | cpio -pdum "$UNPACKED_ROOT" 2> /dev/null
  67         )
  68         # increase the chances the unmount will succeed
  69         umount -f $MNT
  70 }
  71 
  72 unpack()
  73 {
  74         MR=$1
  75         if [ ! -f "$MR" ] ; then
  76                 printf "$MR: not found\n"
  77                 usage
  78         fi
  79 
  80         if [ `uname -i` = i86pc ] ; then
  81                 gzcat "$MR" > $TMR
  82         else
  83                 REALTHING=true ; export REALTHING
  84                 TMR="$MR"
  85         fi
  86 
  87         LOFIDEV=`/usr/sbin/lofiadm -a $TMR`
  88         if [ $? != 0 ] ; then
  89                 echo lofi plumb failed
  90                 exit 2
  91         fi
  92 
  93         mkdir -p $MNT
  94 
  95         FSTYP=`fstyp $LOFIDEV`
  96 
  97         if [ "$FSTYP" = ufs ] ; then
  98                 /usr/sbin/mount -o ro,nologging $LOFIDEV $MNT
  99                 do_unpack
 100         elif [ "$FSTYP" = hsfs ] ; then
 101                 /usr/sbin/mount -F hsfs -o ro $LOFIDEV $MNT
 102                 do_unpack
 103         else
 104                 printf "invalid root archive\n"
 105         fi
 106 
 107 
 108         rmdir $MNT
 109         lofiadm -d $TMR ; LOFIDEV=
 110         if [ "$REALTHING" != true ] ; then
 111                 rm $TMR
 112         fi
 113 }
 114 
 115 compress()
 116 {
 117         SRC=$1
 118         DST=$2
 119 
 120         (
 121                 cd $SRC
 122                 filelist=`find .`
 123 
 124                 for file in $filelist ; do
 125 
 126                         file=`echo $file | sed s#^./##`
 127 
 128                         # copy all files over to preserve hard links
 129                         #
 130                         echo $file | cpio -pdum $DST 2> /dev/null
 131 
 132                         if [ -f $file ] && [ -s $file ] && [ ! -h $file ] ; then
 133                                 fiocompress -mc $file $DST/$file &
 134                         fi
 135 
 136                 done
 137 
 138                 wait `pgrep fiocompress`
 139 
 140                 # now re-copy a couple of uncompressed files
 141 
 142                 if [ -d "$SRC/platform/i86pc" ] ; then
 143                         find `cat boot/solaris/filelist.ramdisk` -type file \
 144                             -print 2> /dev/null > /tmp/flist$$
 145                         find usr/kernel -type file -print 2> /dev/null \
 146                             >> /tmp/flist$$
 147                         # some of the files are replaced with links into
 148                         # tmp/root on the miniroot, so find the backing files
 149                         # from there as well and add them to the list ti
 150                         # be copied uncompressed
 151                         (
 152                                 cd $SRC/tmp/root
 153                                 find `cat ../../boot/solaris/filelist.ramdisk` \
 154                                     -type file -print 2> /dev/null | \
 155                                     sed 's#^#tmp/root/#' >> /tmp/flist$$
 156                         )
 157                         flist=`cat /tmp/flist$$`
 158                         (
 159                                 cd $DST
 160                                 rm -f $flist
 161                         )
 162                         for file in $flist ; do
 163                                 echo $file | cpio -pdum $DST 2> /dev/null
 164                         done
 165                 else
 166                         find kernel platform -name unix | \
 167                             cpio -pdum $DST 2> /dev/null
 168                         find kernel platform -name genunix | cpio -pdum $DST \
 169                             2> /dev/null
 170                         find kernel platform -name platmod | cpio -pdum $DST \
 171                             2> /dev/null
 172                         find `find kernel platform -name cpu` | \
 173                             cpio -pdum $DST 2> /dev/null
 174                         find `find kernel platform -name kmdb\*` | \
 175                                 cpio -pdum $DST 2> /dev/null
 176                         find kernel/misc/sparcv9/ctf kernel/fs/sparcv9/dcfs \
 177                             etc/system etc/name_to_major etc/path_to_inst \
 178                             etc/name_to_sysnum  etc/driver_aliases \
 179                             etc/driver_classes etc/minor_perm | \
 180                             cpio -pdum $DST 2> /dev/null
 181                 fi
 182         )
 183 }
 184 
 185 root_is_ramdisk()
 186 {
 187         grep -v "set root_is_ramdisk=" "$UNPACKED_ROOT"/etc/system | \
 188             grep -v "set ramdisk_size=" > /tmp/system.$$
 189         cat /tmp/system.$$ > "$UNPACKED_ROOT"/etc/system
 190         rm /tmp/system.$$
 191 
 192         echo set root_is_ramdisk=1 >> "$UNPACKED_ROOT"/etc/system
 193         echo set ramdisk_size=$1 >> "$UNPACKED_ROOT"/etc/system
 194 }
 195 
 196 pack()
 197 {
 198         MR="$1"
 199         [ -d "$UNPACKED_ROOT" ] || usage
 200 
 201         # always compress if fiocompress exists
 202         #
 203         if [ -x /usr/sbin/fiocompress ] ; then
 204                 COMPRESS=true
 205         fi
 206 
 207         # Estimate image size and add %10 overhead for ufs stuff.
 208         # Note, we can't use du here in case $UNPACKED_ROOT is on a filesystem,
 209         # e.g. zfs, in which the disk usage is less than the sum of the file
 210         # sizes.  The nawk code
 211         #
 212         #       {t += ($7 % 1024) ? (int($7 / 1024) + 1) * 1024 : $7}
 213         #
 214         # below rounds up the size of a file/directory, in bytes, to the
 215         # next multiple of 1024.  This mimics the behavior of ufs especially
 216         # with directories.  This results in a total size that's slightly
 217         # bigger than if du was called on a ufs directory.
 218         #
 219         # if the operation in turn is compressing the files the amount
 220         # of typical shrinkage is used to come up with a useful archive
 221         # size
 222         size=$(find "$UNPACKED_ROOT" -ls | nawk '
 223             {t += ($7 % 1024) ? (int($7 / 1024) + 1) * 1024 : $7}
 224             END {print int(t * 1.10 / 1024)}')
 225         if [ "$COMPRESS" = true ] ; then
 226                 size=`echo $size | nawk '{s = $1} END {print int(s * 0.6)}'`
 227         fi
 228 
 229         /usr/sbin/mkfile ${size}k "$TMR"
 230 
 231         LOFIDEV=`/usr/sbin/lofiadm -a "$TMR"`
 232         if [ $? != 0 ] ; then
 233                 echo lofi plumb failed
 234                 exit 2
 235         fi
 236 
 237         RLOFIDEV=`echo $LOFIDEV | sed s/lofi/rlofi/`
 238         newfs $RLOFIDEV < /dev/null 2> /dev/null
 239         mkdir -p $MNT
 240         mount -o nologging $LOFIDEV $MNT
 241         rmdir $MNT/lost+found
 242 
 243         if [ -d "$UNPACKED_ROOT/kernel/drv/sparcv9" ] ; then
 244                 root_is_ramdisk $size
 245         fi
 246 
 247         (
 248                 cd "$UNPACKED_ROOT"
 249                 if [ "$COMPRESS" = true ] ; then
 250                         compress . $MNT
 251                 else
 252                         find . -print | cpio -pdum $MNT 2> /dev/null
 253                 fi
 254         )
 255         lockfs -f $MNT
 256         umount $MNT
 257         rmdir $MNT
 258 
 259         if [ -d "$UNPACKED_ROOT/kernel/drv/sparcv9" ] ; then
 260                 "$UNPACKED_ROOT/usr/sbin/installboot" \
 261                     "$UNPACKED_ROOT/platform/sun4u/lib/fs/ufs/bootblk" \
 262                     $RLOFIDEV
 263         fi
 264 
 265         lofiadm -d $LOFIDEV
 266         LOFIDEV=
 267 
 268         rm -f "$TMR.gz"
 269 
 270         if [ -d "$UNPACKED_ROOT/kernel/drv/sparcv9" ] ; then
 271                 mv "$TMR" "$MR"
 272         else
 273                 gzip -f "$TMR"
 274                 mv "$TMR.gz" "$MR"
 275         fi
 276 
 277         chmod a+r "$MR"
 278 }
 279 
 280 strip_amd64()
 281 {
 282         find "$UNPACKED_ROOT" -name amd64 -type directory | xargs rm -rf
 283 }
 284 
 285 # main
 286 #
 287 
 288 EXTRA_SPACE=0
 289 STRIP_AMD64=
 290 COMPRESS=
 291 
 292 PATH=/usr/sbin:/usr/bin:/opt/sfw/bin ; export PATH
 293 
 294 while getopts s:6c opt ; do
 295         case $opt in
 296         s)      EXTRA_SPACE="$OPTARG"
 297                 ;;
 298         6)      STRIP_AMD64=false
 299                 ;;
 300         c)      COMPRESS=true
 301                 ;;
 302         *)      usage
 303                 ;;
 304         esac
 305 done
 306 shift `expr $OPTIND - 1`
 307 
 308 [ $# == 3 ] || usage
 309 
 310 UNPACKED_ROOT="$3"
 311 BASE="`pwd`"
 312 MNT=/tmp/mnt$$
 313 TMR=/tmp/mr$$
 314 LOFIDEV=
 315 MR="$2"
 316 
 317 # sanity check
 318 [ "$UNPACKED_ROOT" != "/" ] || usage
 319 
 320 if [ "`dirname $MR`" = . ] ; then
 321         MR="$BASE/$MR"
 322 fi
 323 if [ "`dirname $UNPACKED_ROOT`" = . ] ; then
 324         UNPACKED_ROOT="$BASE/$UNPACKED_ROOT"
 325 fi
 326 
 327 trap cleanup EXIT
 328 
 329 # always unpack into a fresh root
 330 case $1 in
 331         unpack)
 332                 rm -rf "$UNPACKED_ROOT"
 333                 mkdir -p "$UNPACKED_ROOT"
 334                 ;;
 335 esac
 336 [ -d "$UNPACKED_ROOT" ] || usage
 337 
 338 case $1 in
 339         pack)   pack "$MR"
 340                 ;;
 341         unpack) unpack "$MR"
 342                 ;;
 343         *)      usage
 344                 ;;
 345 esac