1 #
   2 # CDDL HEADER START
   3 #
   4 # The contents of this file are subject to the terms of the
   5 # Common Development and Distribution License (the "License").
   6 # You may not use this file except in compliance with the License.
   7 #
   8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9 # or http://www.opensolaris.org/os/licensing.
  10 # See the License for the specific language governing permissions
  11 # and limitations under the License.
  12 #
  13 # When distributing Covered Code, include this CDDL HEADER in each
  14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15 # If applicable, add the following below this CDDL HEADER, with the
  16 # fields enclosed by brackets "[]" replaced with your own identifying
  17 # information: Portions Copyright [yyyy] [name of copyright owner]
  18 #
  19 # CDDL HEADER END
  20 #
  21 # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  22 #
  23 
  24 #
  25 # Send the error message to the screen and to the logfile.
  26 #
  27 error()
  28 {
  29         typeset fmt="$1"
  30         shift
  31 
  32         printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@"
  33         [[ -n $LOGFILE ]] && printf "[$(date)] ERROR: ${fmt}\n" "$@" >&2
  34 }
  35 
  36 fatal()
  37 {
  38         typeset fmt="$1"
  39         shift
  40 
  41         error "$fmt" "$@"
  42         exit $EXIT_CODE
  43 }
  44 
  45 fail_fatal() {
  46         typeset fmt="$1"
  47         shift
  48 
  49         error "$fmt" "$@"
  50         exit $ZONE_SUBPROC_FATAL
  51 }
  52 
  53 #
  54 # Send the provided printf()-style arguments to the screen and to the logfile.
  55 #
  56 log()
  57 {
  58         typeset fmt="$1"
  59         shift
  60 
  61         printf "${MSG_PREFIX}${fmt}\n" "$@"
  62         [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
  63 }
  64 
  65 #
  66 # Print provided text to the screen if the shell variable "OPT_V" is set.
  67 # The text is always sent to the logfile.
  68 #
  69 vlog()
  70 {
  71         typeset fmt="$1"
  72         shift
  73 
  74         [[ -n $OPT_V ]] && printf "${MSG_PREFIX}${fmt}\n" "$@"
  75         [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
  76 }
  77 
  78 #
  79 # Validate that the directory is safe.
  80 #
  81 # It is possible for a malicious zone root user to modify a zone's filesystem
  82 # so that modifications made to the zone's filesystem by administrators in the
  83 # global zone modify the global zone's filesystem.  We can prevent this by
  84 # ensuring that all components of paths accessed by scripts are real (i.e.,
  85 # non-symlink) directories.
  86 #
  87 # NOTE: The specified path should be an absolute path as would be seen from
  88 # within the zone.  Also, this function does not check parent directories.
  89 # If, for example, you need to ensure that every component of the path
  90 # '/foo/bar/baz' is a directory and not a symlink, then do the following:
  91 #
  92 #       safe_dir /foo
  93 #       safe_dir /foo/bar
  94 #       safe_dir /foo/bar/baz
  95 #
  96 safe_dir()
  97 {
  98         typeset dir="$1"
  99 
 100         if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
 101                 fatal "$e_baddir" "$dir"
 102         fi
 103 }
 104 
 105 # Like safe_dir except the dir doesn't have to exist.
 106 safe_opt_dir()
 107 {
 108         typeset dir="$1"
 109 
 110         [[ ! -e $ZONEROOT/$dir ]] && return
 111 
 112         if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
 113                 fatal "$e_baddir" "$dir"
 114         fi
 115 }
 116 
 117 # Only make a copy if we haven't already done so.
 118 safe_backup()
 119 {
 120         typeset src="$1"
 121         typeset dst="$2"
 122 
 123         if [[ ! -h $src && ! -h $dst && ! -d $dst && ! -f $dst ]]; then
 124                 /usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
 125         fi
 126 }
 127 
 128 # Make a copy even if the destination already exists.
 129 safe_copy()
 130 {
 131         typeset src="$1"
 132         typeset dst="$2"
 133 
 134         if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
 135                 /usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
 136         fi
 137 }
 138 
 139 # Move a file
 140 safe_move()
 141 {
 142         typeset src="$1"
 143         typeset dst="$2"
 144 
 145         if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
 146                 /usr/bin/mv $src $dst || fatal "$e_badfile" "$src"
 147         fi
 148 }
 149 
 150 safe_rm()
 151 {
 152         if [[ ! -h $ZONEROOT/$1 && -f $ZONEROOT/$1 ]]; then
 153                 rm -f "$ZONEROOT/$1"
 154         fi
 155 }
 156 
 157 #
 158 # Replace the file with a wrapper pointing to the native brand code.
 159 # However, we only do the replacement if the file hasn't already been
 160 # replaced with our wrapper.  This function expects the cwd to be the
 161 # location of the file we're replacing.
 162 #
 163 # Some of the files we're replacing are hardlinks to isaexec so we need to 'rm'
 164 # the file before we setup the wrapper while others are hardlinks to rc scripts
 165 # that we need to maintain.
 166 #
 167 safe_replace()
 168 {
 169         typeset filename="$1"
 170         typeset runname="$2"
 171         typeset mode="$3"
 172         typeset own="$4"
 173         typeset rem="$5"
 174 
 175         if [ -h $filename -o ! -f $filename ]; then
 176                 return
 177         fi
 178 
 179         egrep -s "Solaris Brand Replacement" $filename
 180         if [ $? -eq 0 ]; then
 181                 return
 182         fi
 183 
 184         safe_backup $filename $filename.pre_p2v
 185         if [ $rem = "remove" ]; then
 186                 rm -f $filename
 187         fi
 188 
 189         cat <<-END >$filename || exit 1
 190         #!/bin/sh -p
 191         #
 192         # Solaris Brand Replacement
 193         #
 194         # Attention.  This file has been replaced with a new version for
 195         # use in a virtualized environment.  Modification of this script is not
 196         # supported and all changes will be lost upon reboot.  The
 197         # {name}.pre_p2v version of this file is a backup copy of the
 198         # original and should not be deleted.
 199         #
 200         END
 201 
 202         echo ". $runname \"\$@\"" >>$filename || exit 1
 203 
 204         chmod $mode $filename
 205         chown $own $filename
 206 }
 207 
 208 safe_wrap()
 209 {
 210         typeset filename="$1"
 211         typeset runname="$2"
 212         typeset mode="$3"
 213         typeset own="$4"
 214 
 215         if [ -f $filename ]; then
 216                 log "$e_cannot_wrap" "$filename"
 217                 exit 1
 218         fi
 219 
 220         cat <<-END >$filename || exit 1
 221         #!/bin/sh
 222         #
 223         # Solaris Brand Wrapper
 224         #
 225         # Attention.  This file has been created for use in a
 226         # virtualized environment.  Modification of this script
 227         # is not supported and all changes will be lost upon reboot.
 228         #
 229         END
 230 
 231         echo ". $runname \"\$@\"" >>$filename || exit 1
 232 
 233         chmod $mode $filename
 234         chown $own $filename
 235 }
 236 
 237 #
 238 # Read zonecfg fs entries and save the relevant data, one entry per
 239 # line.
 240 # This assumes the properties from the zonecfg output, e.g.:
 241 #       fs:
 242 #               dir: /opt
 243 #               special: /opt
 244 #               raw not specified
 245 #               type: lofs
 246 #               options: [noexec,ro,noatime]
 247 #
 248 # and it assumes the order of the fs properties as above.
 249 #
 250 get_fs_info()
 251 {
 252         zonecfg -z $zonename info fs | nawk '{
 253                 if ($1 == "options:") {
 254                         # Remove brackets.
 255                         options=substr($2, 2, length($2) - 2);
 256                         printf("%s %s %s %s\n", dir, type, special, options);
 257                 } else if ($1 == "dir:") {
 258                         dir=$2;
 259                 } else if ($1 == "special:") {
 260                         special=$2;
 261                 } else if ($1 == "type:") {
 262                         type=$2
 263                 }
 264         }' >> $fstmpfile
 265 }
 266 
 267 #
 268 # Mount zonecfg fs entries into the zonepath.
 269 #
 270 mnt_fs()
 271 {
 272         if [ ! -s $fstmpfile ]; then
 273                 return;
 274         fi
 275 
 276         # Sort the fs entries so we can handle nested mounts.
 277         sort $fstmpfile | nawk -v zonepath=$zonepath '{
 278                 if (NF == 4)
 279                         options="-o " $4;
 280                 else
 281                         options=""
 282 
 283                 # Create the mount point.  Ignore errors since we might have
 284                 # a nested mount with a pre-existing mount point.
 285                 cmd="/usr/bin/mkdir -p " zonepath "/root" $1 " >/dev/null 2>&1"
 286                 system(cmd);
 287 
 288                 cmd="/usr/sbin/mount -F " $2 " " options " " $3 " " \
 289                     zonepath "/root" $1;
 290                 if (system(cmd) != 0) {
 291                         printf("command failed: %s\n", cmd);
 292                         exit 1;
 293                 }
 294         }' >>$LOGFILE
 295 }
 296 
 297 #
 298 # Unmount zonecfg fs entries from the zonepath.
 299 #
 300 umnt_fs()
 301 {
 302         if [ ! -s $fstmpfile ]; then
 303                 return;
 304         fi
 305 
 306         # Reverse sort the fs entries so we can handle nested unmounts.
 307         sort -r $fstmpfile | nawk -v zonepath=$zonepath '{
 308                 cmd="/usr/sbin/umount " zonepath "/root" $1
 309                 if (system(cmd) != 0) {
 310                         printf("command failed: %s\n", cmd);
 311                 }
 312         }' >>$LOGFILE
 313 }
 314 
 315 # Find the dataset mounted on the zonepath.
 316 get_zonepath_ds() {
 317         ZONEPATH_DS=`/usr/sbin/zfs list -H -t filesystem -o name,mountpoint | \
 318             /usr/bin/nawk -v zonepath=$1 '{
 319                 if ($2 == zonepath)
 320                         print $1
 321         }'`
 322 
 323         if [ -z "$ZONEPATH_DS" ]; then
 324                 fail_fatal "$f_no_ds"
 325         fi
 326 }
 327 
 328 #
 329 # Perform validation and cleanup in the zoneroot after unpacking the archive.
 330 #
 331 post_unpack()
 332 {
 333         #
 334         # Check if the image was created with a valid libc.so.1.
 335         #
 336         hwcap=`moe -v -32 $ZONEROOT/lib/libc.so.1 2>&1`
 337         if (( $? != 0 )); then
 338                 vlog "$f_hwcap_info" "$hwcap"
 339                 fail_fatal "$f_sanity_hwcap"
 340         fi
 341 
 342         ( cd "$ZONEROOT" && \
 343             find . \( -type b -o -type c \) -exec rm -f "{}" \; )
 344 }
 345 
 346 #
 347 # Determine flar compression style from identification file.
 348 #
 349 get_compression()
 350 {
 351         typeset ident=$1
 352         typeset line=$(grep "^files_compressed_method=" $ident)
 353 
 354         print ${line##*=}
 355 }
 356 
 357 #
 358 # Determine flar archive style from identification file.
 359 #
 360 get_archiver()
 361 {
 362         typeset ident=$1
 363         typeset line=$(grep "^files_archived_method=" $ident)
 364 
 365         print ${line##*=}
 366 }
 367 
 368 #
 369 # Unpack flar into current directory (which should be zoneroot).  The flash
 370 # archive is standard input.  See flash_archive(4) man page.
 371 # 
 372 # We can't use "flar split" since it will only unpack into a directory called
 373 # "archive".  We need to unpack in place in order to properly handle nested
 374 # fs mounts within the zone root.  This function does the unpacking into the
 375 # current directory.
 376 #
 377 # This code is derived from the gen_split() function in /usr/sbin/flar so
 378 # we keep the same style as the original.
 379 #
 380 install_flar()
 381 {
 382         typeset result
 383         typeset archiver_command
 384         typeset archiver_arguments
 385 
 386         vlog "cd $ZONEROOT && $stage1 "$insrc" | install_flar"
 387 
 388         # Read cookie
 389         read -r input_line
 390         if (( $? != 0 )); then
 391                 log "$not_readable" "$install_media"
 392                 return 1
 393         fi
 394         # The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers.
 395         if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]]; then
 396                 log "$not_flar"
 397                 return 1
 398         fi
 399 
 400         while [ true ]
 401         do
 402                 # We should always be at the start of a section here
 403                 read -r input_line
 404                 if [[ ${input_line%%=*} != "section_begin" ]]; then
 405                         log "$bad_flar"
 406                         return 1
 407                 fi
 408                 section_name=${input_line##*=}
 409 
 410                 # If we're at the archive, we're done skipping sections.
 411                 if [[ "$section_name" == "archive" ]]; then
 412                         break
 413                 fi
 414                 
 415                 #
 416                 # Save identification section to a file so we can determine
 417                 # how to unpack the archive.
 418                 #
 419                 if [[ "$section_name" == "identification" ]]; then
 420                         /usr/bin/rm -f identification
 421                         while read -r input_line
 422                         do
 423                                 if [[ ${input_line%%=*} == \
 424                                     "section_begin" ]]; then
 425                                         /usr/bin/rm -f identification
 426                                         log "$bad_flar"
 427                                         return 1
 428                                 fi
 429 
 430                                 if [[ $input_line == \
 431                                     "section_end=$section_name" ]]; then
 432                                         break;
 433                                 fi
 434                                 echo $input_line >> identification
 435                         done
 436 
 437                         continue
 438                 fi
 439 
 440                 #
 441                 # Otherwise skip past this section; read lines until detecting
 442                 # section_end.  According to flash_archive(4) we can have
 443                 # an arbitrary number of sections but the archive section
 444                 # must be last.
 445                 #
 446                 success=0
 447                 while read -r input_line
 448                 do
 449                         if [[ $input_line == "section_end=$section_name" ]];
 450                         then
 451                                 success=1
 452                                 break
 453                         fi
 454                         # Fail if we miss the end of the section
 455                         if [[ ${input_line%%=*} == "section_begin" ]]; then
 456                                 /usr/bin/rm -f identification
 457                                 log "$bad_flar"
 458                                 return 1
 459                         fi
 460                 done
 461                 if (( $success == 0 )); then
 462                         #
 463                         # If we get here we read to the end of the file before
 464                         # seeing the end of the section we were reading.
 465                         #
 466                         /usr/bin/rm -f identification
 467                         log "$bad_flar"
 468                         return 1
 469                 fi
 470         done
 471 
 472         # Check for an archive made from a ZFS root pool.
 473         egrep -s "^rootpool=" identification
 474         if (( $? == 0 )); then
 475                 /usr/bin/rm -f identification
 476                 log "$bad_zfs_flar"
 477                 return 1
 478         fi
 479 
 480         # Get the information needed to unpack the archive.
 481         archiver=$(get_archiver identification)
 482         if [[ $archiver == "pax" ]]; then
 483                 # pax archiver specified
 484                 archiver_command="/usr/bin/pax"
 485                 if [[ -s $fspaxfile ]]; then
 486                         archiver_arguments="-r -p e -c \
 487                             $(/usr/bin/cat $fspaxfile)"
 488                 else
 489                         archiver_arguments="-r -p e"
 490                 fi
 491         elif [[ $archiver == "cpio" || -z $archiver ]]; then
 492                 # cpio archived specified OR no archiver specified - use default
 493                 archiver_command="/usr/bin/cpio"
 494                 archiver_arguments="-icdumfE $fscpiofile"
 495         else
 496                 # unknown archiver specified
 497                 log "$unknown_archiver" $archiver
 498                 return 1
 499         fi
 500 
 501         if [[ ! -x $archiver_command ]]; then
 502                 /usr/bin/rm -f identification
 503                 log "$cmd_not_exec" $archiver_command
 504                 return 1
 505         fi 
 506 
 507         compression=$(get_compression identification)
 508 
 509         # We're done with the identification file
 510         /usr/bin/rm -f identification
 511 
 512         # Extract archive
 513         if [[ $compression == "compress" ]]; then
 514                 /usr/bin/zcat | \
 515                     $archiver_command $archiver_arguments 2>/dev/null
 516         else
 517                 $archiver_command $archiver_arguments 2>/dev/null
 518         fi
 519         result=$?
 520 
 521         post_unpack
 522 
 523         (( $result != 0 )) && return 1
 524 
 525         return 0 
 526 }
 527 
 528 #
 529 # Get the archive base.
 530 #
 531 # We must unpack the archive in the right place within the zonepath so
 532 # that files are installed into the various mounted filesystems that are set
 533 # up in the zone's configuration.  These are already mounted for us by the
 534 # mntfs function.
 535 #
 536 # Archives can be made of either a physical host's root file system or a
 537 # zone's zonepath.  For a physical system, if the archive is made using an
 538 # absolute path (/...) we can't use it.  For a zone the admin can make the
 539 # archive from a variety of locations;
 540 #
 541 #   a) zonepath itself: This will be a single dir, probably named with the
 542 #      zone name, it will contain a root dir and under the root we'll see all
 543 #      the top level dirs; etc, var, usr...  We must be above the ZONEPATH
 544 #      when we unpack the archive but this will only work if the the archive's
 545 #      top-level dir name matches the ZONEPATH base-level dir name.  If not,
 546 #      this is an error.
 547 #
 548 #   b) inside the zonepath: We'll see root and it will contain all the top
 549 #      level dirs; etc, var, usr....  We must be in the ZONEPATH when we unpack
 550 #      the archive.
 551 #
 552 #   c) inside the zonepath root: We'll see all the top level dirs, ./etc,
 553 #      ./var, ./usr....  This is also the case we see when we get an archive
 554 #      of a physical sytem.  We must be in ZONEROOT when we unpack the archive.
 555 #
 556 # Note that there can be a directory named "root" under the ZONEPATH/root
 557 # directory.
 558 #
 559 # This function handles the above possibilities so that we reject absolute
 560 # path archives and figure out where in the file system we need to be to
 561 # properly unpack the archive into the zone.  It sets the ARCHIVE_BASE
 562 # variable to the location where the achive should be unpacked.
 563 #
 564 get_archive_base()
 565 {
 566         stage1=$1
 567         archive=$2
 568         stage2=$3
 569 
 570         vlog "$m_analyse_archive"
 571 
 572         base=`$stage1 $archive | $stage2 2>/dev/null | nawk -F/ '{
 573                 # Check for an absolute path archive
 574                 if (substr($0, 1, 1) == "/")
 575                         exit 1
 576 
 577                 if ($1 != ".")
 578                         dirs[$1] = 1
 579                 else
 580                         dirs[$2] = 1
 581         }
 582         END {
 583                 for (d in dirs) {
 584                         cnt++
 585                         if (d == "bin")  sawbin = 1
 586                         if (d == "etc")  sawetc = 1
 587                         if (d == "root") sawroot = 1
 588                         if (d == "var")  sawvar = 1
 589                 }
 590 
 591                 if (cnt == 1) {
 592                         # If only one top-level dir named root, we are in the
 593                         # zonepath, otherwise this must be an archive *of*
 594                         # the zonepath so print the top-level dir name.
 595                         if (sawroot)
 596                                 print "*zonepath*"
 597                         else
 598                                 for (d in dirs) print d
 599                 } else {
 600                         # We are either in the zonepath or in the zonepath/root
 601                         # (or at the top level of a full system archive which
 602                         # looks like the zonepath/root case).  Figure out which
 603                         # one.
 604                         if (sawroot && !sawbin && !sawetc && !sawvar)
 605                                 print "*zonepath*"
 606                         else
 607                                 print "*zoneroot*"
 608                 }
 609         }'`
 610 
 611         if (( $? != 0 )); then
 612                 umnt_fs
 613                 fatal "$e_absolute_archive"
 614         fi
 615 
 616         if [[ "$base" == "*zoneroot*" ]]; then
 617                 ARCHIVE_BASE=$ZONEROOT
 618         elif [[ "$base" == "*zonepath*" ]]; then
 619                 ARCHIVE_BASE=$ZONEPATH
 620         else
 621                 # We need to be in the dir above the ZONEPATH but we need to
 622                 # validate that $base matches the final component of ZONEPATH.
 623                 bname=`basename $ZONEPATH`
 624 
 625                 if [[ "$bname" != "$base" ]]; then
 626                         umnt_fs
 627                         fatal "$e_mismatch_archive" "$base" "$bname"
 628                 fi
 629                 ARCHIVE_BASE=`dirname $ZONEPATH`
 630         fi
 631 }
 632 
 633 #
 634 # Unpack cpio archive into zoneroot.
 635 #
 636 install_cpio()
 637 {
 638         stage1=$1
 639         archive=$2
 640 
 641         get_archive_base "$stage1" "$archive" "cpio -it"
 642 
 643         cpioopts="-idmfE $fscpiofile"
 644 
 645         vlog "cd \"$ARCHIVE_BASE\" && $stage1 \"$archive\" | cpio $cpioopts"
 646 
 647         # Ignore errors from cpio since we expect some errors depending on
 648         # how the archive was made.
 649         ( cd "$ARCHIVE_BASE" && $stage1 "$archive" | cpio $cpioopts )
 650 
 651         post_unpack
 652 
 653         return 0
 654 }
 655 
 656 #
 657 # Unpack pax archive into zoneroot.
 658 #
 659 install_pax()
 660 {
 661         archive=$1
 662 
 663         get_archive_base "cat" "$archive" "pax"
 664 
 665         if [[ -s $fspaxfile ]]; then
 666                 filtopt="-c $(/usr/bin/cat $fspaxfile)"
 667         fi
 668 
 669         vlog "cd \"$ARCHIVE_BASE\" && pax -r -f \"$archive\" $filtopt"
 670 
 671         # Ignore errors from pax since we expect some errors depending on
 672         # how the archive was made.
 673         ( cd "$ARCHIVE_BASE" && pax -r -f "$archive" $filtopt )
 674 
 675         post_unpack
 676 
 677         return 0
 678 }
 679 
 680 #
 681 # Unpack UFS dump into zoneroot.
 682 #
 683 install_ufsdump()
 684 {
 685         archive=$1
 686 
 687         vlog "cd \"$ZONEROOT\" && ufsrestore rf \"$archive\""
 688 
 689         #
 690         # ufsrestore goes interactive if you ^C it.  To prevent that,
 691         # we make sure its stdin is not a terminal.
 692         #
 693         ( cd "$ZONEROOT" && ufsrestore rf "$archive" < /dev/null )
 694         result=$?
 695 
 696         post_unpack
 697 
 698         return $result
 699 }
 700 
 701 #
 702 # Copy directory hierarchy into zoneroot.
 703 #
 704 install_dir()
 705 {
 706         source_dir=$1
 707 
 708         cpioopts="-pdm"
 709 
 710         first=1
 711         filt=$(for i in $(cat $fspaxfile)
 712                 do
 713                         echo $i | egrep -s "/" && continue
 714                         if [[ $first == 1 ]]; then
 715                                 printf "^%s" $i
 716                                 first=0
 717                         else
 718                                 printf "|^%s" $i
 719                         fi
 720                 done)
 721 
 722         list=$(cd "$source_dir" && ls -d * | egrep -v "$filt")
 723         flist=$(for i in $list
 724         do
 725                 printf "%s " "$i"
 726         done)
 727         findopts="-xdev ( -type d -o -type f -o -type l ) -print"
 728 
 729         vlog "cd \"$source_dir\" && find $flist $findopts | "
 730         vlog "cpio $cpioopts \"$ZONEROOT\""
 731 
 732         # Ignore errors from cpio since we expect some errors depending on
 733         # how the archive was made.
 734         ( cd "$source_dir" && find $flist $findopts | \
 735             cpio $cpioopts "$ZONEROOT" )
 736 
 737         post_unpack
 738 
 739         return 0
 740 }
 741 
 742 #
 743 # This is a common function for laying down a zone image from a variety of
 744 # different sources.  This can be used to either install a fresh zone or as
 745 # part of zone migration during attach.
 746 #
 747 # The first argument specifies the type of image: archive, directory or stdin.
 748 # The second argument specifies the image itself.  In the case of stdin, the
 749 # second argument specifies the format of the stream (cpio, flar, etc.).
 750 # Any validation or post-processing on the image is done elsewhere.
 751 #
 752 # This function calls a 'sanity_check' function which must be provided by
 753 # the script which includes this code.
 754 #
 755 install_image()
 756 {
 757         intype=$1
 758         insrc=$2
 759 
 760         if [[ -z "$intype" || -z "$insrc" ]]; then
 761                 return 1
 762         fi
 763 
 764         filetype="unknown"
 765         filetypename="unknown"
 766         stage1="cat"
 767 
 768         if [[ "$intype" == "directory" ]]; then
 769                 if [[ "$insrc" == "-" ]]; then
 770                         # Indicates that the existing zonepath is prepopulated.
 771                         filetype="existing"
 772                         filetypename="existing"
 773                 else
 774                         if [[ "$(echo $insrc | cut -c 1)" != "/" ]]; then
 775                                 fatal "$e_path_abs" "$insrc"
 776                         fi
 777 
 778                         if [[ ! -e "$insrc" ]]; then
 779                                 log "$e_not_found" "$insrc"
 780                                 fatal "$e_install_abort"
 781                         fi
 782 
 783                         if [[ ! -r "$insrc" ]]; then
 784                                 log "$e_not_readable" "$insrc"
 785                                 fatal "$e_install_abort"
 786                         fi
 787 
 788                         if [[ ! -d "$insrc" ]]; then
 789                                 log "$e_not_dir"
 790                                 fatal "$e_install_abort"
 791                         fi
 792 
 793                         sanity_check $insrc
 794 
 795                         filetype="directory"
 796                         filetypename="directory"
 797                 fi
 798 
 799         else
 800                 # Common code for both archive and stdin stream.
 801 
 802                 if [[ "$intype" == "archive" ]]; then
 803                         if [[ ! -f "$insrc" ]]; then
 804                                 log "$e_unknown_archive"
 805                                 fatal "$e_install_abort"
 806                         fi
 807                         ftype="$(LC_ALL=C file $insrc | cut -d: -f 2)"
 808                 else
 809                         # For intype == stdin, the insrc parameter specifies
 810                         # the stream format coming on stdin.
 811                         ftype="$insrc"
 812                         insrc="-"
 813                 fi
 814 
 815                 # Setup vars for the archive type we have.
 816                 case "$ftype" in
 817                 *cpio*)         filetype="cpio"
 818                                 filetypename="cpio archive"
 819                         ;;
 820                 *bzip2*)        filetype="bzip2"
 821                                 filetypename="bzipped cpio archive"
 822                         ;;
 823                 *gzip*)         filetype="gzip"
 824                                 filetypename="gzipped cpio archive"
 825                         ;;
 826                 *ufsdump*)      filetype="ufsdump"
 827                                 filetypename="ufsdump archive"
 828                         ;;
 829                 "flar")
 830                                 filetype="flar"
 831                                 filetypename="flash archive"
 832                         ;;
 833                 "flash")
 834                                 filetype="flar"
 835                                 filetypename="flash archive"
 836                         ;;
 837                 *Flash\ Archive*)
 838                                 filetype="flar"
 839                                 filetypename="flash archive"
 840                         ;;
 841                 "tar")
 842                                 filetype="tar"
 843                                 filetypename="tar archive"
 844                         ;;
 845                 *USTAR\ tar\ archive)
 846                                 filetype="tar"
 847                                 filetypename="tar archive"
 848                         ;;
 849                 "pax")
 850                                 filetype="xustar"
 851                                 filetypename="pax (xustar) archive"
 852                         ;;
 853                 *USTAR\ tar\ archive\ extended\ format*)
 854                                 filetype="xustar"
 855                                 filetypename="pax (xustar) archive"
 856                         ;;
 857                 "zfs")
 858                                 filetype="zfs"
 859                                 filetypename="ZFS send stream"
 860                         ;;
 861                 *ZFS\ snapshot\ stream*)
 862                                 filetype="zfs"
 863                                 filetypename="ZFS send stream"
 864                         ;;
 865                 *)              log "$e_unknown_archive"
 866                                 fatal "$e_install_abort"
 867                         ;;
 868                 esac
 869         fi
 870 
 871         vlog "$filetypename"
 872 
 873         # Check for a non-empty root if no '-d -' option. 
 874         if [[ "$filetype" != "existing" ]]; then
 875                 cnt=$(ls $ZONEROOT | wc -l)
 876                 if (( $cnt != 0 )); then
 877                         fatal "$e_root_full" "$ZONEROOT"
 878                 fi
 879         fi
 880 
 881         fstmpfile=$(/usr/bin/mktemp -t -p /var/tmp)
 882         if [[ -z "$fstmpfile" ]]; then
 883                 fatal "$e_tmpfile"
 884         fi
 885 
 886         # Make sure we always have the files holding the directories to filter
 887         # out when extracting from a CPIO or PAX archive.  We'll add the fs
 888         # entries to these files in get_fs_info()
 889         fscpiofile=$(/usr/bin/mktemp -t -p /var/tmp fs.cpio.XXXXXX)
 890         if [[ -z "$fscpiofile" ]]; then
 891                 rm -f $fstmpfile
 892                 fatal "$e_tmpfile"
 893         fi
 894 
 895         # Filter out these directories.
 896         echo 'dev/*' >>$fscpiofile
 897         echo 'devices/*' >>$fscpiofile
 898         echo 'devices' >>$fscpiofile
 899         echo 'proc/*' >>$fscpiofile
 900         echo 'tmp/*' >>$fscpiofile
 901         echo 'var/run/*' >>$fscpiofile
 902         echo 'system/contract/*' >>$fscpiofile
 903         echo 'system/object/*' >>$fscpiofile
 904 
 905         fspaxfile=$(/usr/bin/mktemp -t -p /var/tmp fs.pax.XXXXXX)
 906         if [[ -z "$fspaxfile" ]]; then
 907                 rm -f $fstmpfile $fscpiofile
 908                 fatal "$e_tmpfile"
 909         fi
 910 
 911         printf "%s " \
 912             "dev devices proc tmp var/run system/contract system/object" \
 913             >>$fspaxfile
 914 
 915         # Set up any fs mounts so the archive will install into the correct
 916         # locations.
 917         get_fs_info
 918         mnt_fs
 919         if (( $? != 0 )); then
 920                 umnt_fs >/dev/null 2>&1
 921                 rm -f $fstmpfile $fscpiofile $fspaxfile
 922                 fatal "$mount_failed"
 923         fi
 924 
 925         if [[ "$filetype" == "existing" ]]; then
 926                 log "$no_installing"
 927         else
 928                 log "$installing"
 929         fi
 930 
 931         #
 932         # Install the image into the zonepath.
 933         #
 934         unpack_result=0
 935         stage1="cat"
 936         if [[ "$filetype" == "gzip" ]]; then
 937                 stage1="gzcat"
 938                 filetype="cpio"
 939         elif [[ "$filetype" == "bzip2" ]]; then
 940                 stage1="bzcat"
 941                 filetype="cpio"
 942         fi
 943 
 944         if [[ "$filetype" == "cpio" ]]; then
 945                 install_cpio "$stage1" "$insrc"
 946                 unpack_result=$?
 947 
 948         elif [[ "$filetype" == "flar" ]]; then
 949                 ( cd "$ZONEROOT" && $stage1 $insrc | install_flar )
 950                 unpack_result=$?
 951 
 952         elif [[ "$filetype" == "xustar" ]]; then
 953                 install_pax "$insrc"
 954                 unpack_result=$?
 955 
 956         elif [[ "$filetype" = "tar" ]]; then
 957                 vlog "cd \"$ZONEROOT\" && tar -xf \"$insrc\""
 958                 # Ignore errors from tar since we expect some errors depending
 959                 # on how the archive was made.
 960                 ( cd "$ZONEROOT" && tar -xf "$insrc" )
 961                 unpack_result=0
 962                 post_unpack
 963 
 964         elif [[ "$filetype" == "ufsdump" ]]; then
 965                 install_ufsdump "$insrc"
 966                 unpack_result=$?
 967 
 968         elif [[ "$filetype" == "directory" ]]; then
 969                 install_dir "$insrc"
 970                 unpack_result=$?
 971 
 972         elif [[ "$filetype" == "zfs" ]]; then
 973                 #
 974                 # Given a 'zfs send' stream file, receive the snapshot into
 975                 # the zone's dataset.  We're getting the original system's
 976                 # zonepath dataset.  Destroy the existing dataset created
 977                 # above since this recreates it.
 978                 #
 979                 if [[ -z "$DATASET" ]]; then
 980                         fatal "$f_nodataset"
 981                 fi
 982                 /usr/sbin/zfs destroy "$DATASET"
 983                 if (( $? != 0 )); then
 984                         log "$f_zfsdestroy" "$DATASET"
 985                 fi
 986 
 987                 vlog "$stage1 $insrc | zfs receive -F $DATASET"
 988                 ( $stage1 $insrc | /usr/sbin/zfs receive -F $DATASET )
 989                 unpack_result=$?
 990         fi
 991 
 992         # Clean up any fs mounts used during unpacking.
 993         umnt_fs
 994         rm -f $fstmpfile $fscpiofile $fspaxfile
 995 
 996         chmod 700 $zonepath
 997 
 998         (( $unpack_result != 0 )) && fatal "$f_unpack_failed"
 999 
1000         # Verify this is a valid image.
1001         sanity_check $ZONEROOT
1002 
1003         return 0
1004 }
1005 
1006 # Setup i18n output
1007 TEXTDOMAIN="SUNW_OST_OSCMD"
1008 export TEXTDOMAIN
1009 
1010 e_cannot_wrap=$(gettext "%s: error: wrapper file already exists")
1011 e_baddir=$(gettext "Invalid '%s' directory within the zone")
1012 e_badfile=$(gettext "Invalid '%s' file within the zone")
1013 e_path_abs=$(gettext "Pathname specified to -a '%s' must be absolute.")
1014 e_not_found=$(gettext "%s: error: file or directory not found.")
1015 e_install_abort=$(gettext "Installation aborted.")
1016 e_not_readable=$(gettext "Cannot read directory '%s'")
1017 e_not_dir=$(gettext "Error: must be a directory")
1018 e_unknown_archive=$(gettext "Error: Unknown archive format. Must be a flash archive, a cpio archive (can also be gzipped or bzipped), a pax XUSTAR archive, or a level 0 ufsdump archive.")
1019 e_absolute_archive=$(gettext "Error: archive contains absolute paths instead of relative paths.")
1020 e_mismatch_archive=$(gettext "Error: the archive top-level directory (%s) does not match the zonepath (%s).")
1021 e_tmpfile=$(gettext "Unable to create temporary file")
1022 e_root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.")
1023 f_mkdir=$(gettext "Unable to create directory %s.")
1024 f_chmod=$(gettext "Unable to chmod directory %s.")
1025 f_chown=$(gettext "Unable to chown directory %s.")
1026 f_hwcap_info=$(gettext "HWCAP: %s\n")
1027 f_sanity_hwcap=$(gettext \
1028 "The image was created with an incompatible libc.so.1 hwcap lofs mount.\n"\
1029 "       The zone will not boot on this platform.  See the zone's\n"\
1030 "       documentation for the recommended way to create the archive.")
1031 
1032 m_analyse_archive=$(gettext "Analysing the archive")
1033 
1034 not_readable=$(gettext "Cannot read file '%s'")
1035 not_flar=$(gettext "Input is not a flash archive")
1036 bad_flar=$(gettext "Flash archive is a corrupt")
1037 bad_zfs_flar=$(gettext "Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax.")
1038 f_unpack_failed=$(gettext "Unpacking the archive failed")
1039 unknown_archiver=$(gettext "Archiver %s is not supported")
1040 cmd_not_exec=$(gettext "Required command '%s' not executable!")
1041 
1042 #
1043 # Exit values used by the script, as #defined in <sys/zone.h>
1044 #
1045 #       ZONE_SUBPROC_OK
1046 #       ===============
1047 #       Installation was successful
1048 #
1049 #       ZONE_SUBPROC_USAGE
1050 #       ==================
1051 #       Improper arguments were passed, so print a usage message before exiting
1052 #
1053 #       ZONE_SUBPROC_NOTCOMPLETE
1054 #       ========================
1055 #       Installation did not complete, but another installation attempt can be
1056 #       made without an uninstall
1057 #
1058 #       ZONE_SUBPROC_FATAL
1059 #       ==================
1060 #       Installation failed and an uninstall will be required before another
1061 #       install can be attempted
1062 #
1063 ZONE_SUBPROC_OK=0
1064 ZONE_SUBPROC_USAGE=253
1065 ZONE_SUBPROC_NOTCOMPLETE=254
1066 ZONE_SUBPROC_FATAL=255
1067