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