common.ksh revision 12734:76969fc28795
1212793Sdim# 2212793Sdim# CDDL HEADER START 3212793Sdim# 4212793Sdim# The contents of this file are subject to the terms of the 5212793Sdim# Common Development and Distribution License (the "License"). 6212793Sdim# You may not use this file except in compliance with the License. 7212793Sdim# 8212793Sdim# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9212793Sdim# or http://www.opensolaris.org/os/licensing. 10212793Sdim# See the License for the specific language governing permissions 11212793Sdim# and limitations under the License. 12212793Sdim# 13212793Sdim# When distributing Covered Code, include this CDDL HEADER in each 14212793Sdim# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15249423Sdim# If applicable, add the following below this CDDL HEADER, with the 16296417Sdim# fields enclosed by brackets "[]" replaced with your own identifying 17218893Sdim# information: Portions Copyright [yyyy] [name of copyright owner] 18212793Sdim# 19276479Sdim# CDDL HEADER END 20249423Sdim# 21249423Sdim# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 22249423Sdim# 23288943Sdim 24249423Sdim# 25249423Sdim# Send the error message to the screen and to the logfile. 26249423Sdim# 27212793Sdimerror() 28212793Sdim{ 29212793Sdim typeset fmt="$1" 30276479Sdim shift 31276479Sdim 32212793Sdim printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@" 33212793Sdim [[ -n $LOGFILE ]] && printf "[$(date)] ERROR: ${fmt}\n" "$@" >&2 34212793Sdim} 35212793Sdim 36296417Sdimfatal() 37234353Sdim{ 38212793Sdim typeset fmt="$1" 39212793Sdim shift 40212793Sdim 41212793Sdim error "$fmt" "$@" 42218893Sdim exit $EXIT_CODE 43212793Sdim} 44212793Sdim 45212793Sdimfail_fatal() { 46212793Sdim printf "ERROR: " 47234353Sdim printf "$@" 48296417Sdim printf "\n" 49218893Sdim exit $ZONE_SUBPROC_FATAL 50296417Sdim} 51296417Sdim 52296417Sdim# 53296417Sdim# Send the provided printf()-style arguments to the screen and to the logfile. 54212793Sdim# 55212793Sdimlog() 56218893Sdim{ 57218893Sdim typeset fmt="$1" 58218893Sdim shift 59218893Sdim 60276479Sdim printf "${MSG_PREFIX}${fmt}\n" "$@" 61218893Sdim [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2 62276479Sdim} 63212793Sdim 64296417Sdim# 65212793Sdim# Print provided text to the screen if the shell variable "OPT_V" is set. 66212793Sdim# The text is always sent to the logfile. 67212793Sdim# 68212793Sdimvlog() 69212793Sdim{ 70218893Sdim typeset fmt="$1" 71218893Sdim shift 72218893Sdim 73218893Sdim [[ -n $OPT_V ]] && printf "${MSG_PREFIX}${fmt}\n" "$@" 74218893Sdim [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2 75212793Sdim} 76212793Sdim 77212793Sdim# 78212793Sdim# Validate that the directory is safe. 79212793Sdim# 80212793Sdim# It is possible for a malicious zone root user to modify a zone's filesystem 81212793Sdim# so that modifications made to the zone's filesystem by administrators in the 82212793Sdim# global zone modify the global zone's filesystem. We can prevent this by 83212793Sdim# ensuring that all components of paths accessed by scripts are real (i.e., 84218893Sdim# non-symlink) directories. 85280031Sdim# 86212793Sdim# NOTE: The specified path should be an absolute path as would be seen from 87218893Sdim# within the zone. Also, this function does not check parent directories. 88212793Sdim# If, for example, you need to ensure that every component of the path 89212793Sdim# '/foo/bar/baz' is a directory and not a symlink, then do the following: 90218893Sdim# 91218893Sdim# safe_dir /foo 92218893Sdim# safe_dir /foo/bar 93218893Sdim# safe_dir /foo/bar/baz 94218893Sdim# 95218893Sdimsafe_dir() 96218893Sdim{ 97212793Sdim typeset dir="$1" 98212793Sdim 99212793Sdim if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then 100218893Sdim fatal "$e_baddir" "$dir" 101212793Sdim fi 102212793Sdim} 103212793Sdim 104212793Sdim# Like safe_dir except the dir doesn't have to exist. 105212793Sdimsafe_opt_dir() 106218893Sdim{ 107212793Sdim typeset dir="$1" 108212793Sdim 109212793Sdim [[ ! -e $ZONEROOT/$dir ]] && return 110212793Sdim 111218893Sdim if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then 112280031Sdim fatal "$e_baddir" "$dir" 113218893Sdim fi 114288943Sdim} 115288943Sdim 116288943Sdim# Only make a copy if we haven't already done so. 117288943Sdimsafe_backup() 118249423Sdim{ 119249423Sdim typeset src="$1" 120249423Sdim typeset dst="$2" 121249423Sdim 122288943Sdim if [[ ! -h $src && ! -h $dst && ! -d $dst && ! -f $dst ]]; then 123288943Sdim /usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src" 124288943Sdim fi 125288943Sdim} 126288943Sdim 127288943Sdim# Make a copy even if the destination already exists. 128288943Sdimsafe_copy() 129288943Sdim{ 130288943Sdim typeset src="$1" 131288943Sdim typeset dst="$2" 132288943Sdim 133288943Sdim if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then 134288943Sdim /usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src" 135249423Sdim fi 136288943Sdim} 137288943Sdim 138288943Sdim# Move a file 139288943Sdimsafe_move() 140288943Sdim{ 141288943Sdim typeset src="$1" 142288943Sdim typeset dst="$2" 143249423Sdim 144288943Sdim if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then 145288943Sdim /usr/bin/mv $src $dst || fatal "$e_badfile" "$src" 146288943Sdim fi 147288943Sdim} 148288943Sdim 149288943Sdimsafe_rm() 150288943Sdim{ 151249423Sdim if [[ ! -h $ZONEROOT/$1 && -f $ZONEROOT/$1 ]]; then 152249423Sdim rm -f "$ZONEROOT/$1" 153249423Sdim fi 154249423Sdim} 155212793Sdim 156212793Sdim# 157218893Sdim# Replace the file with a wrapper pointing to the native brand code. 158288943Sdim# However, we only do the replacement if the file hasn't already been 159288943Sdim# replaced with our wrapper. This function expects the cwd to be the 160288943Sdim# location of the file we're replacing. 161218893Sdim# 162212793Sdim# Some of the files we're replacing are hardlinks to isaexec so we need to 'rm' 163212793Sdim# the file before we setup the wrapper while others are hardlinks to rc scripts 164212793Sdim# that we need to maintain. 165218893Sdim# 166234353Sdimsafe_replace() 167234353Sdim{ 168218893Sdim typeset filename="$1" 169212793Sdim typeset runname="$2" 170212793Sdim typeset mode="$3" 171212793Sdim typeset own="$4" 172212793Sdim typeset rem="$5" 173276479Sdim 174212793Sdim if [ -h $filename -o ! -f $filename ]; then 175212793Sdim return 176212793Sdim fi 177212793Sdim 178218893Sdim egrep -s "Solaris Brand Replacement" $filename 179212793Sdim if [ $? -eq 0 ]; then 180218893Sdim return 181280031Sdim fi 182212793Sdim 183218893Sdim safe_backup $filename $filename.pre_p2v 184212793Sdim if [ $rem = "remove" ]; then 185212793Sdim rm -f $filename 186212793Sdim fi 187212793Sdim 188212793Sdim cat <<-END >$filename || exit 1 189296417Sdim #!/bin/sh -p 190296417Sdim # 191296417Sdim # Solaris Brand Replacement 192296417Sdim # 193212793Sdim # Attention. This file has been replaced with a new version for 194212793Sdim # use in a virtualized environment. Modification of this script is not 195212793Sdim # supported and all changes will be lost upon reboot. The 196212793Sdim # {name}.pre_p2v version of this file is a backup copy of the 197218893Sdim # original and should not be deleted. 198296417Sdim # 199296417Sdim END 200296417Sdim 201296417Sdim echo ". $runname \"\$@\"" >>$filename || exit 1 202296417Sdim 203296417Sdim chmod $mode $filename 204296417Sdim chown $own $filename 205296417Sdim} 206218893Sdim 207296417Sdimsafe_wrap() 208296417Sdim{ 209212793Sdim typeset filename="$1" 210212793Sdim typeset runname="$2" 211212793Sdim typeset mode="$3" 212212793Sdim typeset own="$4" 213212793Sdim 214212793Sdim if [ -f $filename ]; then 215212793Sdim log "$e_cannot_wrap" "$filename" 216212793Sdim exit 1 217212793Sdim fi 218212793Sdim 219212793Sdim cat <<-END >$filename || exit 1 220212793Sdim #!/bin/sh 221234353Sdim # 222234353Sdim # Solaris Brand Wrapper 223234353Sdim # 224234353Sdim # Attention. This file has been created for use in a 225234353Sdim # virtualized environment. Modification of this script 226234353Sdim # is not supported and all changes will be lost upon reboot. 227234353Sdim # 228234353Sdim END 229234353Sdim 230234353Sdim echo ". $runname \"\$@\"" >>$filename || exit 1 231234353Sdim 232234353Sdim chmod $mode $filename 233234353Sdim chown $own $filename 234234353Sdim} 235234353Sdim 236234353Sdim# 237234353Sdim# Read zonecfg fs entries and save the relevant data, one entry per 238234353Sdim# line. 239234353Sdim# This assumes the properties from the zonecfg output, e.g.: 240234353Sdim# fs: 241234353Sdim# dir: /opt 242234353Sdim# special: /opt 243234353Sdim# raw not specified 244234353Sdim# type: lofs 245234353Sdim# options: [noexec,ro,noatime] 246234353Sdim# 247234353Sdim# and it assumes the order of the fs properties as above. 248234353Sdim# 249234353Sdimget_fs_info() 250234353Sdim{ 251234353Sdim zonecfg -z $zonename info fs | nawk '{ 252234353Sdim if ($1 == "options:") { 253234353Sdim # Remove brackets. 254280031Sdim options=substr($2, 2, length($2) - 2); 255280031Sdim printf("%s %s %s %s\n", dir, type, special, options); 256234353Sdim } else if ($1 == "dir:") { 257234353Sdim dir=$2; 258234353Sdim } else if ($1 == "special:") { 259234353Sdim special=$2; 260234353Sdim } else if ($1 == "type:") { 261234353Sdim type=$2 262234353Sdim } 263234353Sdim }' >> $fstmpfile 264234353Sdim} 265234353Sdim 266234353Sdim# 267234353Sdim# Mount zonecfg fs entries into the zonepath. 268234353Sdim# 269234353Sdimmnt_fs() 270234353Sdim{ 271234353Sdim if [ ! -s $fstmpfile ]; then 272234353Sdim return; 273234353Sdim fi 274234353Sdim 275234353Sdim # Sort the fs entries so we can handle nested mounts. 276234353Sdim sort $fstmpfile | nawk -v zonepath=$zonepath '{ 277234353Sdim if (NF == 4) 278234353Sdim options="-o " $4; 279234353Sdim else 280234353Sdim options="" 281243830Sdim 282243830Sdim # Create the mount point. Ignore errors since we might have 283243830Sdim # a nested mount with a pre-existing mount point. 284243830Sdim cmd="/usr/bin/mkdir -p " zonepath "/root" $1 " >/dev/null 2>&1" 285243830Sdim system(cmd); 286234353Sdim 287234353Sdim cmd="/usr/sbin/mount -F " $2 " " options " " $3 " " \ 288234353Sdim zonepath "/root" $1; 289234353Sdim if (system(cmd) != 0) { 290234353Sdim printf("command failed: %s\n", cmd); 291234353Sdim exit 1; 292234353Sdim } 293234353Sdim }' >>$LOGFILE 294234353Sdim} 295234353Sdim 296234353Sdim# 297234353Sdim# Unmount zonecfg fs entries from the zonepath. 298234353Sdim# 299234353Sdimumnt_fs() 300234353Sdim{ 301234353Sdim if [ ! -s $fstmpfile ]; then 302234353Sdim return; 303234353Sdim fi 304234353Sdim 305234353Sdim # Reverse sort the fs entries so we can handle nested unmounts. 306234353Sdim sort -r $fstmpfile | nawk -v zonepath=$zonepath '{ 307296417Sdim cmd="/usr/sbin/umount " zonepath "/root" $1 308296417Sdim if (system(cmd) != 0) { 309296417Sdim printf("command failed: %s\n", cmd); 310296417Sdim } 311296417Sdim }' >>$LOGFILE 312296417Sdim} 313296417Sdim 314296417Sdim# Find the dataset mounted on the zonepath. 315296417Sdimget_zonepath_ds() { 316296417Sdim ZONEPATH_DS=`/usr/sbin/zfs list -H -t filesystem -o name,mountpoint | \ 317296417Sdim /usr/bin/nawk -v zonepath=$1 '{ 318296417Sdim if ($2 == zonepath) 319296417Sdim print $1 320296417Sdim }'` 321296417Sdim 322296417Sdim if [ -z "$ZONEPATH_DS" ]; then 323296417Sdim fail_fatal "$f_no_ds" 324296417Sdim fi 325296417Sdim} 326296417Sdim 327296417Sdim# 328296417Sdim# Perform any cleanup in the zoneroot after unpacking the archive. 329296417Sdim# 330296417Sdimpost_unpack() 331296417Sdim{ 332296417Sdim ( cd "$ZONEROOT" && \ 333296417Sdim find . \( -type b -o -type c \) -exec rm -f "{}" \; ) 334296417Sdim} 335296417Sdim 336296417Sdim# 337296417Sdim# Determine flar compression style from identification file. 338296417Sdim# 339296417Sdimget_compression() 340296417Sdim{ 341296417Sdim typeset ident=$1 342296417Sdim typeset line=$(grep "^files_compressed_method=" $ident) 343296417Sdim 344296417Sdim print ${line##*=} 345296417Sdim} 346296417Sdim 347296417Sdim# 348296417Sdim# Determine flar archive style from identification file. 349296417Sdim# 350296417Sdimget_archiver() 351296417Sdim{ 352296417Sdim typeset ident=$1 353296417Sdim typeset line=$(grep "^files_archived_method=" $ident) 354296417Sdim 355296417Sdim print ${line##*=} 356296417Sdim} 357296417Sdim 358296417Sdim# 359296417Sdim# Unpack flar into current directory (which should be zoneroot). The flash 360212793Sdim# archive is standard input. See flash_archive(4) man page. 361276479Sdim# 362276479Sdim# We can't use "flar split" since it will only unpack into a directory called 363276479Sdim# "archive". We need to unpack in place in order to properly handle nested 364212793Sdim# fs mounts within the zone root. This function does the unpacking into the 365218893Sdim# current directory. 366212793Sdim# 367218893Sdim# This code is derived from the gen_split() function in /usr/sbin/flar so 368212793Sdim# we keep the same style as the original. 369212793Sdim# 370212793Sdiminstall_flar() 371296417Sdim{ 372212793Sdim typeset result 373212793Sdim typeset archiver_command 374212793Sdim typeset archiver_arguments 375212793Sdim 376212793Sdim vlog "cd $ZONEROOT && $stage1 "$insrc" | install_flar" 377212793Sdim 378212793Sdim # Read cookie 379212793Sdim read -r input_line 380212793Sdim if (( $? != 0 )); then 381212793Sdim log "$not_readable" "$install_media" 382212793Sdim return 1 383212793Sdim fi 384212793Sdim # The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers. 385212793Sdim if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]]; then 386212793Sdim log "$not_flar" 387296417Sdim return 1 388296417Sdim fi 389296417Sdim 390296417Sdim while [ true ] 391212793Sdim do 392212793Sdim # We should always be at the start of a section here 393218893Sdim read -r input_line 394234353Sdim if [[ ${input_line%%=*} != "section_begin" ]]; then 395234353Sdim log "$bad_flar" 396234353Sdim return 1 397234353Sdim fi 398234353Sdim section_name=${input_line##*=} 399296417Sdim 400296417Sdim # If we're at the archive, we're done skipping sections. 401296417Sdim if [[ "$section_name" == "archive" ]]; then 402296417Sdim break 403296417Sdim fi 404296417Sdim 405296417Sdim # 406296417Sdim # Save identification section to a file so we can determine 407296417Sdim # how to unpack the archive. 408296417Sdim # 409296417Sdim if [[ "$section_name" == "identification" ]]; then 410296417Sdim /usr/bin/rm -f identification 411296417Sdim while read -r input_line 412234353Sdim do 413296417Sdim if [[ ${input_line%%=*} == \ 414234353Sdim "section_begin" ]]; then 415212793Sdim /usr/bin/rm -f identification 416212793Sdim log "$bad_flar" 417218893Sdim return 1 418212793Sdim fi 419212793Sdim 420 if [[ $input_line == \ 421 "section_end=$section_name" ]]; then 422 break; 423 fi 424 echo $input_line >> identification 425 done 426 427 continue 428 fi 429 430 # 431 # Otherwise skip past this section; read lines until detecting 432 # section_end. According to flash_archive(4) we can have 433 # an arbitrary number of sections but the archive section 434 # must be last. 435 # 436 success=0 437 while read -r input_line 438 do 439 if [[ $input_line == "section_end=$section_name" ]]; 440 then 441 success=1 442 break 443 fi 444 # Fail if we miss the end of the section 445 if [[ ${input_line%%=*} == "section_begin" ]]; then 446 /usr/bin/rm -f identification 447 log "$bad_flar" 448 return 1 449 fi 450 done 451 if (( $success == 0 )); then 452 # 453 # If we get here we read to the end of the file before 454 # seeing the end of the section we were reading. 455 # 456 /usr/bin/rm -f identification 457 log "$bad_flar" 458 return 1 459 fi 460 done 461 462 # Check for an archive made from a ZFS root pool. 463 egrep -s "^rootpool=" identification 464 if (( $? == 0 )); then 465 /usr/bin/rm -f identification 466 log "$bad_zfs_flar" 467 return 1 468 fi 469 470 # Get the information needed to unpack the archive. 471 archiver=$(get_archiver identification) 472 if [[ $archiver == "pax" ]]; then 473 # pax archiver specified 474 archiver_command="/usr/bin/pax" 475 if [[ -s $fspaxfile ]]; then 476 archiver_arguments="-r -p e -c \ 477 $(/usr/bin/cat $fspaxfile)" 478 else 479 archiver_arguments="-r -p e" 480 fi 481 elif [[ $archiver == "cpio" || -z $archiver ]]; then 482 # cpio archived specified OR no archiver specified - use default 483 archiver_command="/usr/bin/cpio" 484 archiver_arguments="-icdumfE $fscpiofile" 485 else 486 # unknown archiver specified 487 log "$unknown_archiver" $archiver 488 return 1 489 fi 490 491 if [[ ! -x $archiver_command ]]; then 492 /usr/bin/rm -f identification 493 log "$cmd_not_exec" $archiver_command 494 return 1 495 fi 496 497 compression=$(get_compression identification) 498 499 # We're done with the identification file 500 /usr/bin/rm -f identification 501 502 # Extract archive 503 if [[ $compression == "compress" ]]; then 504 /usr/bin/zcat | \ 505 $archiver_command $archiver_arguments 2>/dev/null 506 else 507 $archiver_command $archiver_arguments 2>/dev/null 508 fi 509 result=$? 510 511 post_unpack 512 513 (( $result != 0 )) && return 1 514 515 return 0 516} 517 518# 519# Get the archive base. 520# 521# We must unpack the archive in the right place within the zonepath so 522# that files are installed into the various mounted filesystems that are set 523# up in the zone's configuration. These are already mounted for us by the 524# mntfs function. 525# 526# Archives can be made of either a physical host's root file system or a 527# zone's zonepath. For a physical system, if the archive is made using an 528# absolute path (/...) we can't use it. For a zone the admin can make the 529# archive from a variety of locations; 530# 531# a) zonepath itself: This will be a single dir, probably named with the 532# zone name, it will contain a root dir and under the root we'll see all 533# the top level dirs; etc, var, usr... We must be above the ZONEPATH 534# when we unpack the archive but this will only work if the the archive's 535# top-level dir name matches the ZONEPATH base-level dir name. If not, 536# this is an error. 537# 538# b) inside the zonepath: We'll see root and it will contain all the top 539# level dirs; etc, var, usr.... We must be in the ZONEPATH when we unpack 540# the archive. 541# 542# c) inside the zonepath root: We'll see all the top level dirs, ./etc, 543# ./var, ./usr.... This is also the case we see when we get an archive 544# of a physical sytem. We must be in ZONEROOT when we unpack the archive. 545# 546# Note that there can be a directory named "root" under the ZONEPATH/root 547# directory. 548# 549# This function handles the above possibilities so that we reject absolute 550# path archives and figure out where in the file system we need to be to 551# properly unpack the archive into the zone. It sets the ARCHIVE_BASE 552# variable to the location where the achive should be unpacked. 553# 554get_archive_base() 555{ 556 stage1=$1 557 archive=$2 558 stage2=$3 559 560 vlog "$m_analyse_archive" 561 562 base=`$stage1 $archive | $stage2 2>/dev/null | nawk -F/ '{ 563 # Check for an absolute path archive 564 if (substr($0, 1, 1) == "/") 565 exit 1 566 567 if ($1 != ".") 568 dirs[$1] = 1 569 else 570 dirs[$2] = 1 571 } 572 END { 573 for (d in dirs) { 574 cnt++ 575 if (d == "bin") sawbin = 1 576 if (d == "etc") sawetc = 1 577 if (d == "root") sawroot = 1 578 if (d == "var") sawvar = 1 579 } 580 581 if (cnt == 1) { 582 # If only one top-level dir named root, we are in the 583 # zonepath, otherwise this must be an archive *of* 584 # the zonepath so print the top-level dir name. 585 if (sawroot) 586 print "*zonepath*" 587 else 588 for (d in dirs) print d 589 } else { 590 # We are either in the zonepath or in the zonepath/root 591 # (or at the top level of a full system archive which 592 # looks like the zonepath/root case). Figure out which 593 # one. 594 if (sawroot && !sawbin && !sawetc && !sawvar) 595 print "*zonepath*" 596 else 597 print "*zoneroot*" 598 } 599 }'` 600 601 if (( $? != 0 )); then 602 umnt_fs 603 fatal "$e_absolute_archive" 604 fi 605 606 if [[ "$base" == "*zoneroot*" ]]; then 607 ARCHIVE_BASE=$ZONEROOT 608 elif [[ "$base" == "*zonepath*" ]]; then 609 ARCHIVE_BASE=$ZONEPATH 610 else 611 # We need to be in the dir above the ZONEPATH but we need to 612 # validate that $base matches the final component of ZONEPATH. 613 bname=`basename $ZONEPATH` 614 615 if [[ "$bname" != "$base" ]]; then 616 umnt_fs 617 fatal "$e_mismatch_archive" "$base" "$bname" 618 fi 619 ARCHIVE_BASE=`dirname $ZONEPATH` 620 fi 621} 622 623# 624# Unpack cpio archive into zoneroot. 625# 626install_cpio() 627{ 628 stage1=$1 629 archive=$2 630 631 get_archive_base "$stage1" "$archive" "cpio -it" 632 633 cpioopts="-idmfE $fscpiofile" 634 635 vlog "cd \"$ARCHIVE_BASE\" && $stage1 \"$archive\" | cpio $cpioopts" 636 637 # Ignore errors from cpio since we expect some errors depending on 638 # how the archive was made. 639 ( cd "$ARCHIVE_BASE" && $stage1 "$archive" | cpio $cpioopts ) 640 641 post_unpack 642 643 return 0 644} 645 646# 647# Unpack pax archive into zoneroot. 648# 649install_pax() 650{ 651 archive=$1 652 653 get_archive_base "cat" "$archive" "pax" 654 655 if [[ -s $fspaxfile ]]; then 656 filtopt="-c $(/usr/bin/cat $fspaxfile)" 657 fi 658 659 vlog "cd \"$ARCHIVE_BASE\" && pax -r -f \"$archive\" $filtopt" 660 661 # Ignore errors from pax since we expect some errors depending on 662 # how the archive was made. 663 ( cd "$ARCHIVE_BASE" && pax -r -f "$archive" $filtopt ) 664 665 post_unpack 666 667 return 0 668} 669 670# 671# Unpack UFS dump into zoneroot. 672# 673install_ufsdump() 674{ 675 archive=$1 676 677 vlog "cd \"$ZONEROOT\" && ufsrestore rf \"$archive\"" 678 679 # 680 # ufsrestore goes interactive if you ^C it. To prevent that, 681 # we make sure its stdin is not a terminal. 682 # 683 ( cd "$ZONEROOT" && ufsrestore rf "$archive" < /dev/null ) 684 result=$? 685 686 post_unpack 687 688 return $result 689} 690 691# 692# Copy directory hierarchy into zoneroot. 693# 694install_dir() 695{ 696 source_dir=$1 697 698 cpioopts="-pdm" 699 700 first=1 701 filt=$(for i in $(cat $fspaxfile) 702 do 703 echo $i | egrep -s "/" && continue 704 if [[ $first == 1 ]]; then 705 printf "^%s" $i 706 first=0 707 else 708 printf "|^%s" $i 709 fi 710 done) 711 712 list=$(cd "$source_dir" && ls -d * | egrep -v "$filt") 713 flist=$(for i in $list 714 do 715 printf "%s " "$i" 716 done) 717 findopts="-xdev ( -type d -o -type f -o -type l ) -print" 718 719 vlog "cd \"$source_dir\" && find $flist $findopts | " 720 vlog "cpio $cpioopts \"$ZONEROOT\"" 721 722 # Ignore errors from cpio since we expect some errors depending on 723 # how the archive was made. 724 ( cd "$source_dir" && find $flist $findopts | \ 725 cpio $cpioopts "$ZONEROOT" ) 726 727 post_unpack 728 729 return 0 730} 731 732# 733# This is a common function for laying down a zone image from a variety of 734# different sources. This can be used to either install a fresh zone or as 735# part of zone migration during attach. 736# 737# The first argument specifies the type of image: archive, directory or stdin. 738# The second argument specifies the image itself. In the case of stdin, the 739# second argument specifies the format of the stream (cpio, flar, etc.). 740# Any validation or post-processing on the image is done elsewhere. 741# 742# This function calls a 'sanity_check' function which must be provided by 743# the script which includes this code. 744# 745install_image() 746{ 747 intype=$1 748 insrc=$2 749 750 if [[ -z "$intype" || -z "$insrc" ]]; then 751 return 1 752 fi 753 754 filetype="unknown" 755 filetypename="unknown" 756 stage1="cat" 757 758 if [[ "$intype" == "directory" ]]; then 759 if [[ "$insrc" == "-" ]]; then 760 # Indicates that the existing zonepath is prepopulated. 761 filetype="existing" 762 filetypename="existing" 763 else 764 if [[ "$(echo $insrc | cut -c 1)" != "/" ]]; then 765 fatal "$e_path_abs" "$insrc" 766 fi 767 768 if [[ ! -e "$insrc" ]]; then 769 log "$e_not_found" "$insrc" 770 fatal "$e_install_abort" 771 fi 772 773 if [[ ! -r "$insrc" ]]; then 774 log "$e_not_readable" "$insrc" 775 fatal "$e_install_abort" 776 fi 777 778 if [[ ! -d "$insrc" ]]; then 779 log "$e_not_dir" 780 fatal "$e_install_abort" 781 fi 782 783 sanity_check $insrc 784 785 filetype="directory" 786 filetypename="directory" 787 fi 788 789 else 790 # Common code for both archive and stdin stream. 791 792 if [[ "$intype" == "archive" ]]; then 793 if [[ ! -f "$insrc" ]]; then 794 log "$e_unknown_archive" 795 fatal "$e_install_abort" 796 fi 797 ftype="$(LC_ALL=C file $insrc | cut -d: -f 2)" 798 else 799 # For intype == stdin, the insrc parameter specifies 800 # the stream format coming on stdin. 801 ftype="$insrc" 802 insrc="-" 803 fi 804 805 # Setup vars for the archive type we have. 806 case "$ftype" in 807 *cpio*) filetype="cpio" 808 filetypename="cpio archive" 809 ;; 810 *bzip2*) filetype="bzip2" 811 filetypename="bzipped cpio archive" 812 ;; 813 *gzip*) filetype="gzip" 814 filetypename="gzipped cpio archive" 815 ;; 816 *ufsdump*) filetype="ufsdump" 817 filetypename="ufsdump archive" 818 ;; 819 "flar") 820 filetype="flar" 821 filetypename="flash archive" 822 ;; 823 "flash") 824 filetype="flar" 825 filetypename="flash archive" 826 ;; 827 *Flash\ Archive*) 828 filetype="flar" 829 filetypename="flash archive" 830 ;; 831 "tar") 832 filetype="tar" 833 filetypename="tar archive" 834 ;; 835 *USTAR\ tar\ archive) 836 filetype="tar" 837 filetypename="tar archive" 838 ;; 839 "pax") 840 filetype="xustar" 841 filetypename="pax (xustar) archive" 842 ;; 843 *USTAR\ tar\ archive\ extended\ format*) 844 filetype="xustar" 845 filetypename="pax (xustar) archive" 846 ;; 847 "zfs") 848 filetype="zfs" 849 filetypename="ZFS send stream" 850 ;; 851 *ZFS\ snapshot\ stream*) 852 filetype="zfs" 853 filetypename="ZFS send stream" 854 ;; 855 *) log "$e_unknown_archive" 856 fatal "$e_install_abort" 857 ;; 858 esac 859 fi 860 861 vlog "$filetypename" 862 863 # Check for a non-empty root if no '-d -' option. 864 if [[ "$filetype" != "existing" ]]; then 865 cnt=$(ls $ZONEROOT | wc -l) 866 if (( $cnt != 0 )); then 867 fatal "$e_root_full" "$ZONEROOT" 868 fi 869 fi 870 871 fstmpfile=$(/usr/bin/mktemp -t -p /var/tmp) 872 if [[ -z "$fstmpfile" ]]; then 873 fatal "$e_tmpfile" 874 fi 875 876 # Make sure we always have the files holding the directories to filter 877 # out when extracting from a CPIO or PAX archive. We'll add the fs 878 # entries to these files in get_fs_info() 879 fscpiofile=$(/usr/bin/mktemp -t -p /var/tmp fs.cpio.XXXXXX) 880 if [[ -z "$fscpiofile" ]]; then 881 rm -f $fstmpfile 882 fatal "$e_tmpfile" 883 fi 884 885 # Filter out these directories. 886 echo 'dev/*' >>$fscpiofile 887 echo 'devices/*' >>$fscpiofile 888 echo 'devices' >>$fscpiofile 889 echo 'proc/*' >>$fscpiofile 890 echo 'tmp/*' >>$fscpiofile 891 echo 'var/run/*' >>$fscpiofile 892 echo 'system/contract/*' >>$fscpiofile 893 echo 'system/object/*' >>$fscpiofile 894 895 fspaxfile=$(/usr/bin/mktemp -t -p /var/tmp fs.pax.XXXXXX) 896 if [[ -z "$fspaxfile" ]]; then 897 rm -f $fstmpfile $fscpiofile 898 fatal "$e_tmpfile" 899 fi 900 901 printf "%s " \ 902 "dev devices proc tmp var/run system/contract system/object" \ 903 >>$fspaxfile 904 905 # Set up any fs mounts so the archive will install into the correct 906 # locations. 907 get_fs_info 908 mnt_fs 909 if (( $? != 0 )); then 910 umnt_fs >/dev/null 2>&1 911 rm -f $fstmpfile $fscpiofile $fspaxfile 912 fatal "$mount_failed" 913 fi 914 915 if [[ "$filetype" == "existing" ]]; then 916 log "$no_installing" 917 else 918 log "$installing" 919 fi 920 921 # 922 # Install the image into the zonepath. 923 # 924 unpack_result=0 925 stage1="cat" 926 if [[ "$filetype" == "gzip" ]]; then 927 stage1="gzcat" 928 filetype="cpio" 929 elif [[ "$filetype" == "bzip2" ]]; then 930 stage1="bzcat" 931 filetype="cpio" 932 fi 933 934 if [[ "$filetype" == "cpio" ]]; then 935 install_cpio "$stage1" "$insrc" 936 unpack_result=$? 937 938 elif [[ "$filetype" == "flar" ]]; then 939 ( cd "$ZONEROOT" && $stage1 $insrc | install_flar ) 940 unpack_result=$? 941 942 elif [[ "$filetype" == "xustar" ]]; then 943 install_pax "$insrc" 944 unpack_result=$? 945 946 elif [[ "$filetype" = "tar" ]]; then 947 vlog "cd \"$ZONEROOT\" && tar -xf \"$insrc\"" 948 # Ignore errors from tar since we expect some errors depending 949 # on how the archive was made. 950 ( cd "$ZONEROOT" && tar -xf "$insrc" ) 951 unpack_result=0 952 post_unpack 953 954 elif [[ "$filetype" == "ufsdump" ]]; then 955 install_ufsdump "$insrc" 956 unpack_result=$? 957 958 elif [[ "$filetype" == "directory" ]]; then 959 install_dir "$insrc" 960 unpack_result=$? 961 962 elif [[ "$filetype" == "zfs" ]]; then 963 # 964 # Given a 'zfs send' stream file, receive the snapshot into 965 # the zone's dataset. We're getting the original system's 966 # zonepath dataset. Destroy the existing dataset created 967 # above since this recreates it. 968 # 969 if [[ -z "$DATASET" ]]; then 970 fatal "$f_nodataset" 971 fi 972 /usr/sbin/zfs destroy "$DATASET" 973 if (( $? != 0 )); then 974 log "$f_zfsdestroy" "$DATASET" 975 fi 976 977 vlog "$stage1 $insrc | zfs receive -F $DATASET" 978 ( $stage1 $insrc | /usr/sbin/zfs receive -F $DATASET ) 979 unpack_result=$? 980 fi 981 982 # Clean up any fs mounts used during unpacking. 983 umnt_fs 984 rm -f $fstmpfile $fscpiofile $fspaxfile 985 986 chmod 700 $zonepath 987 988 (( $unpack_result != 0 )) && fatal "$f_unpack_failed" 989 990 # Verify this is a valid image. 991 sanity_check $ZONEROOT 992 993 return 0 994} 995 996# Setup i18n output 997TEXTDOMAIN="SUNW_OST_OSCMD" 998export TEXTDOMAIN 999 1000e_cannot_wrap=$(gettext "%s: error: wrapper file already exists") 1001e_baddir=$(gettext "Invalid '%s' directory within the zone") 1002e_badfile=$(gettext "Invalid '%s' file within the zone") 1003e_path_abs=$(gettext "Pathname specified to -a '%s' must be absolute.") 1004e_not_found=$(gettext "%s: error: file or directory not found.") 1005e_install_abort=$(gettext "Installation aborted.") 1006e_not_readable=$(gettext "Cannot read directory '%s'") 1007e_not_dir=$(gettext "Error: must be a directory") 1008e_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.") 1009e_absolute_archive=$(gettext "Error: archive contains absolute paths instead of relative paths.") 1010e_mismatch_archive=$(gettext "Error: the archive top-level directory (%s) does not match the zonepath (%s).") 1011e_tmpfile=$(gettext "Unable to create temporary file") 1012e_root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.") 1013f_mkdir=$(gettext "Unable to create directory %s.") 1014f_chmod=$(gettext "Unable to chmod directory %s.") 1015f_chown=$(gettext "Unable to chown directory %s.") 1016 1017 1018m_analyse_archive=$(gettext "Analysing the archive") 1019 1020not_readable=$(gettext "Cannot read file '%s'") 1021not_flar=$(gettext "Input is not a flash archive") 1022bad_flar=$(gettext "Flash archive is a corrupt") 1023bad_zfs_flar=$(gettext "Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax.") 1024f_unpack_failed=$(gettext "Unpacking the archive failed") 1025unknown_archiver=$(gettext "Archiver %s is not supported") 1026cmd_not_exec=$(gettext "Required command '%s' not executable!") 1027 1028# 1029# Exit values used by the script, as #defined in <sys/zone.h> 1030# 1031# ZONE_SUBPROC_OK 1032# =============== 1033# Installation was successful 1034# 1035# ZONE_SUBPROC_USAGE 1036# ================== 1037# Improper arguments were passed, so print a usage message before exiting 1038# 1039# ZONE_SUBPROC_NOTCOMPLETE 1040# ======================== 1041# Installation did not complete, but another installation attempt can be 1042# made without an uninstall 1043# 1044# ZONE_SUBPROC_FATAL 1045# ================== 1046# Installation failed and an uninstall will be required before another 1047# install can be attempted 1048# 1049ZONE_SUBPROC_OK=0 1050ZONE_SUBPROC_USAGE=253 1051ZONE_SUBPROC_NOTCOMPLETE=254 1052ZONE_SUBPROC_FATAL=255 1053 1054