1 #!/bin/ksh -p
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 #
  23 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24 # Use is subject to license terms.
  25 #
  26 # Copyright (C) 2013 by Jim Klimov - implemented the previously absent
  27 #    cloning of zones from specified snapshot, and avoidance of sys-unconfig
  28 #
  29 # Copyright 2018 Nexenta Systems, Inc. All rights reserved.
  30 
  31 . /usr/lib/brand/ipkg/common.ksh
  32 
  33 m_usage=$(gettext "clone {sourcezone}")
  34 f_nosource=$(gettext "Error: unable to determine source zone dataset.")
  35 f_badsource=$(gettext "Error: specified snapshot is invalid for this source zone.")
  36 f_baddestpool=$(gettext "Error: Can not clone, source and target pools differ.")
  37 
  38 ZFS=/usr/sbin/zfs
  39 ZONEADM=/usr/sbin/zoneadm
  40 
  41 # Clean up on failure
  42 trap_exit()
  43 {
  44         if (( $ZONE_IS_MOUNTED != 0 )); then
  45                 error "$v_unmount"
  46                 $ZONEADM -z $ZONENAME unmount
  47         fi
  48 
  49         exit $ZONE_SUBPROC_INCOMPLETE
  50 }
  51 
  52 # Set up ZFS dataset hierarchy for the zone.
  53 
  54 ROOT="rpool/ROOT"
  55 
  56 # Use clone or copy method to dupilcate zone datasets.
  57 do_copy=false
  58 
  59 # Other brand clone options are invalid for this brand.
  60 while getopts "m:R:s:Xz:" opt; do
  61         case $opt in
  62                 m)      case "$OPTARG" in
  63                         "copy")
  64                                 ZONEPATH=`$ZONEADM -z $2 list -p | \
  65                                         awk -F: '{print $4}'`
  66                                 do_copy=true
  67                                 ;;
  68                         *)      fail_usage "";;
  69                         esac
  70                         ;;
  71                 R)      ZONEPATH="$OPTARG" ;;
  72                 s)      case "$OPTARG" in
  73                         *@*) # Full snapshot name was provided, or just "@snap"
  74                              # Split this into dataset name (even if empty) and
  75                              # snapshot name (also may be empty)
  76                                 SNAPNAME="`echo "$OPTARG" | sed 's/^[^@]*@//'`"
  77                                 REQUESTED_DS="`echo "$OPTARG" | sed 's/\([^@]*\)@.*$/\1/'`"
  78                                 ;;
  79                         */*) # Only dataset name was passed, so we will make a
  80                              # snapshot there automatically and clone off it
  81                                 SNAPNAME=""
  82                                 REQUESTED_DS="$OPTARG"
  83                                 ;;
  84                         *)   # Only snapshot name was passed, so we will clone
  85                              # the source zone's active ZBE and this snapshot
  86                                 SNAPNAME="$OPTARG"
  87                                 REQUESTED_DS=""
  88                                 ;;
  89                         esac
  90                         ;;
  91                 X)      NO_SYSUNCONFIG=yes ;;
  92                 z)      ZONENAME="$OPTARG" ;;
  93                 *)      fail_usage "";;
  94         esac
  95 done
  96 shift $((OPTIND-1))
  97 
  98 if [ $# -ne 1 ]; then
  99         fail_usage "";
 100 fi
 101 
 102 sourcezone="$1"
 103 get_current_gzbe
 104 
 105 if [ -z "$REQUESTED_DS" ]; then
 106         # Find the active source zone dataset to clone.
 107         sourcezonepath=`$ZONEADM -z $sourcezone list -p | awk -F: '{print $4}'`
 108         if [ -z "$sourcezonepath" ]; then
 109                 fail_fatal "$f_nosource"
 110         fi
 111 
 112         get_zonepath_ds $sourcezonepath
 113         get_active_ds $CURRENT_GZBE $ZONEPATH_DS
 114 
 115         spdir=`/usr/bin/dirname $sourcezonepath`
 116         get_zonepath_ds $spdir
 117         spdir_ds=$ZONEPATH_DS
 118 else
 119         # Sanity-check the provided dataset (should exist and be an IPS ZBE)
 120         REQUESTED_DS="`echo "$REQUESTED_DS" | egrep '^.*/'"$sourcezone"'/ROOT/[^/]+$'`"
 121         if [ $? != 0 -o x"$REQUESTED_DS" = x ]; then
 122                 fail_fatal "$f_badsource"
 123         fi
 124         $ZFS list -H -o \
 125                 org.opensolaris.libbe:parentbe,org.opensolaris.libbe:active \
 126                 "$REQUESTED_DS" > /dev/null || \
 127                         fail_fatal "$f_badsource"
 128         ACTIVE_DS="$REQUESTED_DS"
 129 fi
 130 
 131 # Another sanity-check: requested snapshot exists for default or requested ZBE
 132 if [ x"$SNAPNAME" != x ]; then
 133         $ZFS list -H "$ACTIVE_DS@$SNAPNAME" > /dev/null || \
 134                 fail_fatal "$f_badsource"
 135 fi
 136 
 137 #
 138 # Now set up the zone's datasets
 139 #
 140 
 141 #
 142 # First make the top-level dataset.
 143 #
 144 
 145 pdir=`/usr/bin/dirname $ZONEPATH`
 146 zpname=`/usr/bin/basename $ZONEPATH`
 147 
 148 get_zonepath_ds $pdir
 149 zpds=$ZONEPATH_DS
 150 
 151 fail_zonepath_in_rootds $zpds
 152 
 153 #
 154 # Make sure zone is cloned within the same zpool
 155 #
 156 if [[ $do_copy != true ]]; then
 157         case $zpds in
 158                 $spdir_ds)
 159                         break
 160                         ;;
 161                 *)
 162                         fail_fatal "$f_baddestpool"
 163                         break
 164                         ;;
 165         esac
 166 fi
 167 
 168 #
 169 # We need to tolerate errors while creating the datasets and making the
 170 # mountpoint, since these could already exist from some other BE.
 171 #
 172 
 173 $ZFS create $zpds/$zpname
 174 
 175 $ZFS create -o mountpoint=legacy -o zoned=on $zpds/$zpname/ROOT
 176 
 177 if [ x"$SNAPNAME" = x ]; then
 178         # make snapshot
 179         SNAPNAME=${ZONENAME}_snap
 180         SNAPNUM=0
 181         while [ $SNAPNUM -lt 100 ]; do
 182                 $ZFS snapshot $ACTIVE_DS@$SNAPNAME
 183                 if [ $? = 0 ]; then
 184                         break
 185                 fi
 186                 SNAPNUM=`expr $SNAPNUM + 1`
 187                 SNAPNAME="${ZONENAME}_snap$SNAPNUM"
 188         done
 189 
 190         # NOTE: This artificially limits us to 100 clones of a "golden" zone
 191         # into a same-named (test?) zone, unless clones are based on some
 192         # same snapshot via command-line
 193         if [ $SNAPNUM -ge 100 ]; then
 194                 fail_fatal "$f_zfs_create"
 195         fi
 196 fi
 197 
 198 LOGFILE=$(/usr/bin/mktemp -t -p /var/tmp $ZONENAME.clone_log.XXXXXX)
 199 if [[ -z "$LOGFILE" ]]; then
 200         fatal "$e_tmpfile"
 201 fi
 202 exec 2>>"$LOGFILE"
 203 
 204 # do clone
 205 #
 206 # If there is already an existing zone BE for this zone it's likely it belongs
 207 # to another global zone BE. If that is the case the name of the zone BE
 208 # dataset is ajusted to avoid name collisions.
 209 #
 210 # If do_copy is set (the -m copy option was used) zfs send/recv is used so
 211 # the zone can be cloned across pools.
 212 #
 213 BENAME=zbe
 214 BENUM=0
 215 while [ $BENUM -lt 100 ]; do
 216         if $do_copy; then
 217                 log "Copy source zoneroot to new zoneroot"
 218                 $ZFS send $ACTIVE_DS@$SNAPNAME | \
 219                 $ZFS recv $zpds/$zpname/ROOT/$BENAME
 220                 if [ $? = 0 ]; then
 221                         $ZFS destroy $ACTIVE_DS@$SNAPNAME
 222                         break
 223                 fi
 224         else
 225                 log "Clone zone root dataset"
 226                 $ZFS clone $ACTIVE_DS@$SNAPNAME $zpds/$zpname/ROOT/$BENAME
 227                 if [ $? = 0 ]; then
 228                         break
 229                 fi
 230         fi
 231         BENUM=`expr $BENUM + 1`
 232         BENAME="zbe-$BENUM"
 233 done
 234 
 235 if [ $BENUM -ge 100 ]; then
 236         fail_fatal "$f_zfs_create"
 237 fi
 238 
 239 $ZFS set $PROP_ACTIVE=on $zpds/$zpname/ROOT/$BENAME || \
 240         fail_incomplete "$f_zfs_create"
 241 
 242 $ZFS set $PROP_PARENT=$CURRENT_GZBE $zpds/$zpname/ROOT/$BENAME || \
 243         fail_incomplete "$f_zfs_create"
 244 
 245 $ZFS set canmount=noauto $zpds/$zpname/ROOT/$BENAME || \
 246         fail_incomplete "$f_zfs_create"
 247 
 248 if [ ! -d $ZONEPATH/root ]; then
 249         /usr/bin/mkdir -p $ZONEPATH/root
 250         /usr/bin/chmod 700 $ZONEPATH
 251 fi
 252 
 253 ZONE_IS_MOUNTED=0
 254 trap trap_exit EXIT
 255 
 256 #
 257 # Completion of unconfigure_zone will leave the zone root mounted for
 258 # ipkg brand zones.  The root won't be mounted for labeled brand zones.
 259 #
 260 is_brand_labeled
 261 (( $? == 0 )) && if [ x"$NO_SYSUNCONFIG" = xyes ]; then
 262         vlog "$v_mounting"
 263         ZONE_IS_MOUNTED=1
 264         $ZONEADM -z $ZONENAME mount -f || fatal "$e_badmount"
 265 else
 266         unconfigure_zone
 267 fi
 268 
 269 trap - EXIT
 270 exit $ZONE_SUBPROC_OK