summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/Makefile.lint2
-rw-r--r--usr/src/Targetdirs1
-rw-r--r--usr/src/cmd/Makefile2
-rw-r--r--usr/src/cmd/beadm/BootEnvironment.py444
-rw-r--r--usr/src/cmd/beadm/Makefile77
-rw-r--r--usr/src/cmd/beadm/__init__.py31
-rw-r--r--usr/src/cmd/beadm/beadm.py1058
-rw-r--r--usr/src/cmd/beadm/messages.py264
-rw-r--r--usr/src/lib/Makefile4
-rw-r--r--usr/src/lib/libbe/Makefile72
-rw-r--r--usr/src/lib/libbe/Makefile.com62
-rw-r--r--usr/src/lib/libbe/common/be_activate.c1191
-rw-r--r--usr/src/lib/libbe/common/be_create.c2995
-rw-r--r--usr/src/lib/libbe/common/be_list.c1122
-rw-r--r--usr/src/lib/libbe/common/be_mount.c2684
-rw-r--r--usr/src/lib/libbe/common/be_rename.c232
-rw-r--r--usr/src/lib/libbe/common/be_snapshot.c769
-rw-r--r--usr/src/lib/libbe/common/be_utils.c3647
-rw-r--r--usr/src/lib/libbe/common/be_zones.c518
-rw-r--r--usr/src/lib/libbe/common/libbe.h234
-rw-r--r--usr/src/lib/libbe/common/libbe_priv.h209
-rw-r--r--usr/src/lib/libbe/common/llib-lbe29
-rw-r--r--usr/src/lib/libbe/common/mapfile-vers60
-rw-r--r--usr/src/lib/libbe/i386/Makefile28
-rw-r--r--usr/src/lib/libbe/sparc/Makefile28
-rw-r--r--usr/src/lib/libbe/tbeadm/Makefile69
-rw-r--r--usr/src/lib/libbe/tbeadm/tbeadm.c837
-rw-r--r--usr/src/lib/libsecdb/exec_attr.txt1
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt2
-rw-r--r--usr/src/lib/pylibbe/Makefile54
-rw-r--r--usr/src/lib/pylibbe/Makefile.com60
-rw-r--r--usr/src/lib/pylibbe/common/libbe_py.c1094
-rw-r--r--usr/src/lib/pylibbe/common/mapfile-vers46
-rw-r--r--usr/src/lib/pylibbe/i386/Makefile28
-rw-r--r--usr/src/lib/pylibbe/sparc/Makefile28
-rw-r--r--usr/src/pkg/manifests/SUNWbeadm.mf28
-rw-r--r--usr/src/pkg/manifests/install-beadm.mf55
37 files changed, 18064 insertions, 1 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index 7c74fb03f8..fb9724629a 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -330,6 +330,8 @@ COMMON_SUBDIRS = \
cmd/ztest \
lib/abi \
lib/auditd_plugins \
+ lib/libbe \
+ lib/pylibbe \
lib/brand/sn1 \
lib/brand/solaris10 \
lib/crypt_modules \
diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs
index d73e5fc95d..90f839d353 100644
--- a/usr/src/Targetdirs
+++ b/usr/src/Targetdirs
@@ -290,6 +290,7 @@ DIRS= \
/usr/lib/python2.6/vendor-packages \
/usr/lib/python2.6/vendor-packages/solaris \
/usr/lib/python2.6/vendor-packages/zfs \
+ /usr/lib/python2.6/vendor-packages/beadm \
/usr/lib/rcap \
/usr/lib/rcap/$(MACH32) \
/usr/lib/sa \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 1526ae4520..3bc3713aa1 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -69,6 +69,7 @@ COMMON_SUBDIRS= \
basename \
bc \
bdiff \
+ beadm \
bfs \
bnu \
boot \
@@ -528,6 +529,7 @@ MSGSUBDIRS= \
banner \
bart \
basename \
+ beadm \
bnu \
bsmconv \
bsmunconv \
diff --git a/usr/src/cmd/beadm/BootEnvironment.py b/usr/src/cmd/beadm/BootEnvironment.py
new file mode 100644
index 0000000000..b85373880e
--- /dev/null
+++ b/usr/src/cmd/beadm/BootEnvironment.py
@@ -0,0 +1,444 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""Boot Environment classes used by beadm."""
+
+import datetime
+
+class BootEnvironment:
+ """Boot Environment object that is used by beadm to manage command line
+ options, arguments and the log."""
+
+ def __init__(self):
+ self.trgt_rpool = None
+ self.trgt_be_name_or_snapshot = None
+ self.src_be_name_or_snapshot = None
+ self.properties = {}
+ self.log_id = None
+ self.log = None
+ self.msg_buf = {}
+ self.description = None
+
+class listBootEnvironment:
+ """Base class for beadm list
+ Determine the BE's to display. Prints command output according to option:
+ -d - dataset
+ -s - snapshot
+ -a - all (both dataset and snapshot)
+ <none> - only BE information
+ The -H option produces condensed, parseable output
+ The ';' delimits each field in the output. BEs with multiple
+ datasets will have multiple lines in the output.
+ """
+
+ def list(self, be_list, ddh, be_name):
+ """ print all output for beadm list command
+ be_list - list of all BEs
+ ddh - if True, Do not Display Headers - just parseable data
+ be_name - user-specified BE, if any
+
+ returns 0 for success
+ side effect: beadm list output printed to stdout
+ """
+
+ #If we're listing Headers, initialize the array holding the
+ #column widths with the header widths themselves. Later on,
+ #the data in this array will get adjusted as we process actual
+ #row data and find that a piece of data is wider than its
+ #column header.
+ bemaxout = [0 for i in range(len(self.hdr[0]))]
+ if not ddh:
+ #iterate all header rows since their fields may not
+ #be of equal length.
+ for header in self.hdr:
+ icol = 0
+ for hc in header:
+ if len(hc) + 1 > bemaxout[icol]:
+ bemaxout[icol] = len(hc) + 1
+ icol += 1
+
+ #collect BEs
+ beout = {} #matrix of output text [row][attribute]
+ beoutname = {} #list of BE names [row]
+ be_space = {} #space used totals for BE [BE name]['space_used','ibei']
+ ibe = 0 #BE index
+ spacecol = -1 #to contain column where space used is displayed
+ for be in be_list:
+ if 'orig_be_name' in be:
+ cur_be = be['orig_be_name']
+ cur_be_obj = be
+
+ #if BE name specified, collect matching BEs
+ if be_name is not None and not self.beMatch(be, be_name):
+ continue
+ attrs = ()
+ #identify BE|dataset|snapshot attributes
+ att = ''
+ for att in ('orig_be_name', 'dataset', 'snap_name'):
+ if att in be and att in self.lattrs:
+ attrs = self.lattrs[att]
+ if att == 'orig_be_name':
+ be_space[cur_be] = {}
+ be_space[cur_be]['space_used'] = 0
+ be_space[cur_be]['ibe'] = ibe
+ if not ddh and len(cur_be) + 1 > bemaxout[0]:
+ bemaxout[0] = len(cur_be) + 1
+ break
+ beout[ibe] = {}
+ beoutname[ibe] = cur_be
+
+ icol = 0 #first column
+ for at in attrs:
+ #for option -s, withhold subordinate datasets
+ if self.__class__.__name__ == 'SnapshotList' and \
+ att == 'snap_name' and 'snap_name' in be and \
+ '/' in be[att]:
+ break
+ #convert output to readable format and save
+ save = self.getAttr(at, be, ddh, cur_be_obj)
+ beout[ibe][at] = save
+ #maintain maximum column widths
+ if not ddh and len(save) + 1 > bemaxout[icol]:
+ bemaxout[icol] = len(save) + 1
+ #sum all snapshots for BE
+ if at == 'space_used' and 'space_used' in be:
+ spacecol = icol
+ icol += 1 #next column
+ ibe += 1
+ if 'space_used' in be:
+ #sum all snapshots and datasets for BE in 'beadm list'
+ if isinstance(self, BEList):
+ be_space[cur_be]['space_used'] += be.get('space_used')
+ elif cur_be in be_space and \
+ ('space_used' not in be_space[cur_be] or
+ be_space[cur_be]['space_used'] == 0):
+ #list space used separately for other options
+ be_space[cur_be]['space_used'] = be.get('space_used')
+
+ #output format total lengths for each BE with any snapshots
+ for cur_be in be_space:
+ save = self.getSpaceValue(be_space[cur_be]['space_used'], ddh)
+ ibe = be_space[cur_be]['ibe']
+ beout[ibe]['space_used'] = save
+ #expand column if widest column entry
+ if (spacecol != -1) and \
+ (not ddh and len(save) + 1 > bemaxout[spacecol]):
+ bemaxout[spacecol] = len(save) + 1
+
+ #print headers in columns
+ if not ddh:
+ for header in self.hdr:
+ outstr = ''
+ for icol in range(len(header)):
+ outstr += header[icol].ljust(bemaxout[icol])
+ if outstr != '':
+ print outstr
+
+ #print collected output in columns
+ outstr = ''
+ prev_be = None
+ cur_be = None
+ for ibe in beout: #index output matrix
+ if beoutname[ibe] != None:
+ cur_be = beoutname[ibe]
+ #find attributes for BE type
+ curtype = None
+ for att in ('orig_be_name', 'dataset', 'snap_name'):
+ if att in beout[ibe]:
+ attrs = self.lattrs[att]
+ curtype = att
+ break
+
+ if curtype == None: #default to BE
+ curtype = 'orig_be_name'
+ if 'orig_be_name' in self.lattrs:
+ attrs = self.lattrs['orig_be_name']
+ else: attrs = ()
+
+ if not ddh:
+ if prev_be != cur_be and cur_be != None:
+ #for -d,-s,-a, print BE alone on line
+ if self.__class__.__name__ != 'BEList':
+ print cur_be
+ prev_be = cur_be
+
+ #print for one BE/snapshot/dataset
+ icol = 0 #first column
+
+ #if this is a 'dataset' or 'snap_name', start line with BE
+ #name token
+ if ddh and curtype != 'orig_be_name':
+ outstr = cur_be
+
+ for at in attrs: #for each attribute specified in table
+ if ddh: #add separators for parsing
+ if outstr != '':
+ outstr += ';' #attribute separator
+ if at in beout[ibe] and beout[ibe][at] != '-' and \
+ beout[ibe][at] != '':
+ outstr += beout[ibe][at]
+ else: #append text justified in column
+ if at in beout[ibe]:
+ outstr += beout[ibe][at].ljust(bemaxout[icol])
+ icol += 1 #next column
+
+ if outstr != '':
+ print outstr
+ outstr = ''
+
+ return 0
+
+ def beMatch(self, be, be_name):
+ """find match on user-specified BE."""
+
+ if 'orig_be_name' in be:
+ return be.get('orig_be_name') == be_name
+ if 'dataset' in be:
+ if be.get('dataset') == be_name:
+ return True
+ out = be.get('dataset').split("/")
+ return out[0] == be_name
+ if 'snap_name' in be:
+ if be.get('snap_name') == be_name:
+ return True
+ out = be.get('snap_name').split('@')
+ if out[0] == be_name:
+ return True
+ out = be.get('snap_name').split('/')
+ return out[0] == be_name
+ return False
+
+ def getAttr(self, at, be, ddh, beobj):
+ """
+ Extract information by attribute and format for printing
+ returns '?' if normally present attribute not found - error.
+ """
+ if at == 'blank':
+ return ' '
+ if at == 'dash':
+ return '-'
+ if at == 'orig_be_name':
+ if at not in be:
+ return '-'
+ ret = be[at]
+ if ddh or self.__class__.__name__ == 'BEList':
+ return ret
+ return ' ' + ret #indent
+ if at == 'snap_name':
+ if at not in be:
+ return '-'
+ if self.__class__.__name__ == 'CompleteList':
+ ret = self.prependRootDS(be[at], beobj)
+ else:
+ ret = be[at]
+ if ddh:
+ return ret
+ return ' ' + ret #indent
+ if at == 'dataset':
+ if at not in be:
+ return '-'
+ if self.__class__.__name__ == 'DatasetList' or \
+ self.__class__.__name__ == 'CompleteList':
+ ret = self.prependRootDS(be[at], beobj)
+ else:
+ ret = be[at]
+ if ddh:
+ return ret
+ return ' ' + ret #indent
+ if at == 'active':
+ if at not in be:
+ return '-'
+ ret = ''
+ if 'active' in be and be['active']:
+ ret += 'N'
+ if 'active_boot' in be and be['active_boot']:
+ ret += 'R'
+ if ret == '':
+ return '-'
+ return ret
+ if at == 'mountpoint':
+ if at not in be:
+ return '-'
+ if 'mounted' not in be or not be['mounted']:
+ return '-'
+ return be[at]
+ if at == 'space_used':
+ if at not in be:
+ return '0'
+ return self.getSpaceValue(be[at], ddh)
+ if at == 'mounted':
+ if at not in be:
+ return '-'
+ return be[at]
+ if at == 'date':
+ if at not in be:
+ return '?'
+ if ddh:
+ return str(be[at]) #timestamp in seconds
+ sec = str(datetime.datetime.fromtimestamp(be[at]))
+ return sec[0:len(sec)-3] #trim seconds
+ if at == 'policy':
+ if at not in be:
+ return '?'
+ return be[at]
+ if at == 'root_ds':
+ if at not in be:
+ return '?'
+ if ddh or self.__class__.__name__ == 'BEList':
+ return be[at]
+ return ' ' + be[at]
+ if at == 'uuid_str':
+ if at not in be:
+ return '-'
+ return be[at]
+ #default case - no match on attribute
+ return be[at]
+
+ def getSpaceValue(self, num, ddh):
+ """Readable formatting for disk space size."""
+
+ if ddh:
+ return str(num) #return size in bytes as string
+
+ kilo = 1024.0
+ mega = 1048576.0
+ giga = 1073741824.0
+ tera = 1099511627776.0
+
+ if num == None:
+ return '0'
+ if num < kilo:
+ return str(num) + 'B'
+ if num < mega:
+ return str('%.1f' % (num / kilo)) + 'K'
+ if num < giga:
+ return str('%.2f' % (num / mega)) + 'M'
+ if num < tera:
+ return str('%.2f' % (num / giga)) + 'G'
+ return str('%.2f' % (num / tera)) + 'T'
+
+ def prependRootDS(self, val, beobj):
+ """Prepend root dataset name with BE name stripped."""
+
+ root_ds = beobj.get('root_ds')
+ return root_ds[0:root_ds.rfind('/')+1] + val
+
+
+"""Top level "beadm list" derived classes defined here.
+ Only table definition is done here - all methods are in the base class.
+ Tables driving list:
+ hdr - list of text to output for each column
+ lattrs - dictionary of attributes
+ Each entry specifies either BE, dataset, snapshot with
+ an attribute key:
+ orig_be_name - for BEs
+ dataset - for datasets
+ snap_name - for snapshots
+ Each list item in entry indicates specific datum for
+ column
+ Number of hdr columns must equal number of lattrs entries
+ unless ddh (dontDisplayHeaders) is true.
+"""
+class BEList(listBootEnvironment):
+ """specify header and attribute information for BE-only output"""
+
+ def __init__(self, ddh):
+ """Init function for the class."""
+ self.hdr = \
+ ('BE','Active','Mountpoint','Space','Policy','Created'), \
+ ('--','------','----------','-----','------','-------')
+ if ddh:
+ self.lattrs = {'orig_be_name':('orig_be_name', 'uuid_str',
+ 'active', 'mountpoint', 'space_used', 'policy',
+ 'date')}
+ else:
+ self.lattrs = {'orig_be_name':('orig_be_name', 'active',
+ 'mountpoint', 'space_used', 'policy', 'date')}
+
+class DatasetList(listBootEnvironment):
+ """
+ specify header and attribute information for dataset output,
+ -d option
+ """
+ def __init__(self, ddh):
+ """Init function for the class."""
+
+ self.hdr = \
+ ('BE/Dataset','Active','Mountpoint','Space','Policy','Created'), \
+ ('----------','------','----------','-----','------','-------')
+ if ddh:
+ self.lattrs = { \
+ 'orig_be_name':('orig_be_name', 'root_ds', 'active',
+ 'mountpoint', 'space_used', 'policy', 'date'), \
+ 'dataset':('dataset', 'dash', 'mountpoint', 'space_used',
+ 'policy', 'date')}
+ else:
+ self.lattrs = { \
+ 'orig_be_name':('root_ds', 'active', 'mountpoint',
+ 'space_used', 'policy', 'date'), \
+ 'dataset':('dataset', 'dash', 'mountpoint', 'space_used',
+ 'policy', 'date')}
+
+class SnapshotList(listBootEnvironment):
+ """
+ specify header and attribute information for snapshot output,
+ -s option
+ """
+ def __init__(self, ddh):
+ """Init function for the class."""
+
+ self.hdr = \
+ ('BE/Snapshot','Space','Policy','Created'), \
+ ('-----------','-----','------','-------')
+ self.lattrs = {'snap_name':('snap_name', 'space_used', 'policy',
+ 'date')}
+
+class CompleteList(listBootEnvironment):
+ """
+ specify header and attribute information for BE and/or dataset and/or
+ snapshot output,
+ -a or -ds options
+ """
+ def __init__(self, ddh):
+ """Init function for the class."""
+
+ self.hdr = \
+ ('BE/Dataset/Snapshot','Active','Mountpoint','Space','Policy','Created'), \
+ ('-------------------','------','----------','-----','------','-------')
+ if ddh:
+ self.lattrs = { \
+ 'orig_be_name':('orig_be_name', 'root_ds', 'active',
+ 'mountpoint', 'space_used', 'policy', 'date'),
+ 'dataset':('dataset', 'dash', 'mountpoint', 'space_used',
+ 'policy', 'date'),
+ 'snap_name':('snap_name', 'dash', 'dash', 'space_used',
+ 'policy', 'date')}
+ else:
+ self.lattrs = { \
+ 'orig_be_name':('root_ds', 'active', 'mountpoint',
+ 'space_used', 'policy', 'date'), \
+ 'dataset':('dataset', 'dash', 'mountpoint', 'space_used',
+ 'policy', 'date'),
+ 'snap_name':('snap_name', 'dash', 'dash', 'space_used',
+ 'policy', 'date')}
diff --git a/usr/src/cmd/beadm/Makefile b/usr/src/cmd/beadm/Makefile
new file mode 100644
index 0000000000..5586780eda
--- /dev/null
+++ b/usr/src/cmd/beadm/Makefile
@@ -0,0 +1,77 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.cmd
+
+FILEMODE=0444
+
+PYTHON= $(PYTHON_26)
+ROOTPYDIR= $(ROOT)/usr/lib/python2.6/vendor-packages
+ROOTMODDIR= $(ROOTPYDIR)/beadm
+
+PYCMD= beadm.py
+PYCMDFILE= $(PYCMD:%.py=%)
+
+PYMODULES= __init__.py BootEnvironment.py messages.py
+PYMODOBJS= $(PYMODULES:%.py=%.pyc)
+PYMODFILES= $(PYMODULES) $(PYMODOBJS)
+
+ROOTCMDFILE= $(PYCMDFILE:%=$(ROOTSBIN)/%)
+$(ROOTCMDFILE) := FILEMODE = 0555
+ROOTMODFILES= $(PYMODFILES:%=$(ROOTMODDIR)/%)
+ROOTUSRSBINLINKS= $(PYCMDFILE:%=$(ROOTUSRSBIN)/%)
+
+POFILE = beadm.po
+MSGFILES = beadm.py messages.py
+XGETTEXT = $(GNUXGETTEXT)
+XGETFLAGS = $(GNUXGETFLAGS)
+
+.KEEP_STATE:
+
+all: $(PYCMDFILE) $(PYMODOBJS)
+
+install: all $(ROOTCMDFILE) $(ROOTMODFILES) $(ROOTUSRSBINLINKS)
+
+clean:
+ $(RM) $(PYCMDFILE) $(PYMODOBJS)
+
+clobber: clean
+
+$(ROOTCMDDIR)/%: %
+ $(INS.pyfile)
+
+$(ROOTMODDIR)/%: %
+ $(INS.pyfile)
+
+$(ROOTUSRSBINLINKS):
+ -$(RM) $@; $(SYMLINK) ../../sbin/$(@F) $@
+
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+include $(SRC)/Makefile.msg.targ
+include ../Makefile.targ
diff --git a/usr/src/cmd/beadm/__init__.py b/usr/src/cmd/beadm/__init__.py
new file mode 100644
index 0000000000..9972bd8edd
--- /dev/null
+++ b/usr/src/cmd/beadm/__init__.py
@@ -0,0 +1,31 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+import gettext
+
+_ = gettext.translation("beadm", "/usr/lib/locale",
+ fallback=True).gettext
+
+__all__ = ["messages", "BootEnvironment"]
+
diff --git a/usr/src/cmd/beadm/beadm.py b/usr/src/cmd/beadm/beadm.py
new file mode 100644
index 0000000000..cb6772ffd9
--- /dev/null
+++ b/usr/src/cmd/beadm/beadm.py
@@ -0,0 +1,1058 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+beadm - The Boot Environment Administration tool. Use this CLI to
+manage boot environments.
+"""
+
+import getopt
+import gettext
+import os
+import sys
+import shutil
+import traceback
+import time
+import subprocess
+
+from beadm import _
+from beadm.BootEnvironment import *
+import beadm.messages as msg
+import libbe_py as lb
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def usage():
+ '''Defines parameters and options of the command beadm.'''
+ print >> sys.stderr, _("""
+Usage:
+ beadm subcommand cmd_options
+
+ subcommands:
+
+ beadm activate beName
+ beadm create [-a] [-d description]
+ [-e non-activeBeName | beName@snapshot]
+ [-o property=value] ... [-p zpool] beName
+ beadm create beName@snapshot
+ beadm destroy [-fF] beName | beName@snapshot
+ beadm list [[-a] | [-d] [-s]] [-H] [beName]
+ beadm mount beName mountpoint
+ beadm rename beName newBeName
+ beadm unmount [-f] beName""")
+ sys.exit(1)
+
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Public Command Line functions described in beadm(1)
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def activate(opts):
+ """
+ Function: activate
+
+ Description: Activate a Boot Environment.The following is the
+ subcommand, options and args that make up the
+ opts object passed in:
+
+ Parameters:
+ opts - A string containing the active subcommand
+
+ Returns:
+ 0 - Success
+ 1 - Failure
+ """
+
+ if len(opts) != 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ be = BootEnvironment()
+
+ if lb.beVerifyBEName(opts[0]) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+
+ rc = lb.beActivate(opts[0])
+ if rc == 0:
+ return 0
+
+ be.msg_buf["0"] = opts[0]
+ if rc == msg.Msgs.BE_ERR_BE_NOENT:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, opts[0])
+ elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc)
+ msg.printMsg(msg.Msgs.BEADM_ERR_ACTIVATE, be.msg_buf, -1)
+ return 1
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_ACTIVATE, be.msg_buf, -1)
+ return 1
+
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def create(opts):
+ """
+ Function: create
+
+ Description: Create a Boot Environment. The following is the
+ subcommand, options and args that make up the
+ opts object passed in:
+
+ create [-a] [-d description]
+ [-e non-activeBeName | beName@Snapshot]
+ [-o property=value] ... [-p zpool] beName
+
+ create beName@Snapshot
+
+ Parameters:
+ opts - A object containing the create subcommand
+ and all the options and arguments passed in
+ on the command line mentioned above.
+
+ Returns:
+ 0 - Success
+ 1 - Failure
+ """
+
+ be = BootEnvironment()
+
+ activate = False
+
+ try:
+ opts_args, be.trgt_be_name_or_snapshot = getopt.getopt(opts,
+ "ad:e:o:p:")
+ except getopt.GetoptError:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ # Counters for detecting multiple options.
+ # e.g. beadm create -p rpool -p rpool2 newbe
+ num_a_opts = 0
+ num_e_opts = 0
+ num_p_opts = 0
+ num_d_opts = 0
+
+ for opt, arg in opts_args:
+ if opt == "-a":
+ activate = True
+ num_a_opts += 1
+ elif opt == "-e":
+ be.src_be_name_or_snapshot = arg
+ num_e_opts += 1
+ elif opt == "-o":
+ key, value = arg.split("=")
+ be.properties[key] = value
+ elif opt == "-p":
+ be.trgt_rpool = arg
+ num_p_opts += 1
+ elif opt == "-d":
+ be.description = arg
+ num_d_opts += 1
+
+ if num_a_opts > 1 or num_e_opts > 1 or num_p_opts > 1 or num_d_opts > 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ # Check that all info provided from the user is legitimate.
+ if (verifyCreateOptionsArgs(be) != 0):
+ usage()
+
+ if initBELog("create", be) != 0:
+ return 1
+
+ msg.printMsg(msg.Msgs.BEADM_MSG_BE_CREATE_START,
+ be.trgt_be_name_or_snapshot[0], be.log_id)
+
+ if '@' in be.trgt_be_name_or_snapshot[0]:
+ # Create a snapshot
+ rc = createSnapshot(be)
+ else:
+ if lb.beVerifyBEName(be.trgt_be_name_or_snapshot[0]) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+
+ # Create a BE based on a snapshot
+ if be.src_be_name_or_snapshot is not None and \
+ '@' in be.src_be_name_or_snapshot:
+ # Create a BE from a snapshot
+ rc = createBEFromSnapshot(be)
+ else:
+ rc = createBE(be)
+
+ # Activate the BE if the user chose to.
+ if activate and rc == 0:
+ rc = activateBE(be)
+ cleanupBELog(be)
+
+ return rc
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def destroy(opts):
+ """
+ Function: destroy
+
+ Description: Destroy a Boot Environment. The following is the
+ subcommand, options and args that make up the
+ opts object passed in:
+
+ destroy [-fF] beName | beName@snapshot
+
+ Parameters:
+ opts - A object containing the destroy subcommand
+ and all the options and arguments passed in
+ on the command line mentioned above.
+
+ Returns:
+ 0 - Success
+ 1 - Failure
+ """
+
+ force_unmount = 0
+ suppress_prompt = False
+ be_active_on_boot = None
+ be = BootEnvironment()
+
+ try:
+ opts_args, be.trgt_be_name_or_snapshot = getopt.getopt(opts, "fF")
+ except getopt.GetoptError:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ # Counters for detecting multiple options.
+ # e.g. beadm destroy -f -f newbe
+ num_f_opts = 0
+ num_sf_opts = 0
+
+ for opt, arg in opts_args:
+ if opt == "-f":
+ force_unmount = 1
+ num_sf_opts += 1
+ elif opt == "-F":
+ suppress_prompt = True
+ num_f_opts += 1
+
+ if num_sf_opts > 1 or num_f_opts > 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ if len(be.trgt_be_name_or_snapshot) != 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ is_snapshot = False
+
+ if "@" in be.trgt_be_name_or_snapshot[0]:
+ is_snapshot = True
+ be_name, snap_name = be.trgt_be_name_or_snapshot[0].split("@")
+ if lb.beVerifyBEName(be_name) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+ else:
+ if lb.beVerifyBEName(be.trgt_be_name_or_snapshot[0]) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+
+ # Get the 'active' BE and the 'active on boot' BE.
+ be_active, be_active_on_boot = getActiveBEAndActiveOnBootBE()
+
+ # If the user is trying to destroy the 'active' BE then quit now.
+ if not is_snapshot and be_active == be.trgt_be_name_or_snapshot[0]:
+ be.msg_buf["0"] = be.msg_buf["1"] = be_active
+ msg.printMsg(msg.Msgs.BEADM_ERR_DESTROY_ACTIVE, be.msg_buf, -1)
+ return 1
+
+ if not suppress_prompt:
+
+ # Display a destruction question and wait for user response.
+ # Quit if negative user response.
+
+ if not displayDestructionQuestion(be):
+ return 0
+
+ if is_snapshot:
+
+ # Destroy a snapshot.
+ rc = lb.beDestroySnapshot(be_name, snap_name)
+ else:
+
+ # Destroy a BE. Passing in 1 for the second arg destroys
+ # any snapshots the BE may have as well.
+
+ rc = lb.beDestroy(be.trgt_be_name_or_snapshot[0], 1, force_unmount)
+
+ # Check if the BE that was just destroyed was the
+ # 'active on boot' BE. If it was, display a message letting
+ # the user know that the 'active' BE is now also the
+ # 'active on boot' BE.
+ if be_active_on_boot == be.trgt_be_name_or_snapshot[0] and rc == 0:
+ msg.printMsg(msg.Msgs.BEADM_MSG_ACTIVE_ON_BOOT,
+ be_active, -1)
+
+ if rc == 0:
+ try:
+ shutil.rmtree("/var/log/beadm/" + \
+ be.trgt_be_name_or_snapshot[0], True)
+ except:
+ msg.printMsg(msg.Msgs.BEADM_ERR_LOG_RM,
+ "/var/log/beadm/" + be.trgt_be_name_or_snapshot[0], -1)
+
+ return 0
+
+ be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0]
+ if rc == msg.Msgs.BE_ERR_MOUNTED:
+ be.msg_buf["1"] = be.msg_buf["2"] = be.trgt_be_name_or_snapshot[0]
+ msg.printMsg(msg.Msgs.BEADM_ERR_MOUNTED, be.msg_buf, -1)
+ return 1
+ elif rc == msg.Msgs.BE_ERR_DESTROY_CURR_BE:
+ msg.printMsg(msg.Msgs.BEADM_ERR_DESTROY_ACTIVE, \
+ be.msg_buf["0"], -1)
+ return 1
+ elif rc == msg.Msgs.BE_ERR_ZONES_UNMOUNT:
+ be.msg_buf["1"] = be.trgt_be_name_or_snapshot[0]
+ msg.printMsg(msg.Msgs.BE_ERR_ZONES_UNMOUNT, be.msg_buf, -1)
+ return 1
+ elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc)
+ msg.printMsg(msg.Msgs.BEADM_ERR_DESTROY, be.msg_buf, -1)
+ return 1
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_DESTROY, be.msg_buf, -1)
+ return 1
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def list(opts):
+ """
+ Description: List the attributes of a Boot Environment.
+ The following is the subcommand, options
+ and args that make up the opts object
+ passed in:
+
+ list [[-a] | [-d] [-s]] [-H] [beName]
+
+ -a displays all info
+ -d displays BE info plus dataset info
+ -s displays BE info plus snapshot info
+ -H displays info parsable by a computer
+
+ Parameters:
+ opts - A object containing the list subcommand
+ and all the options and arguments passed in
+ on the command line mentioned above.
+
+ Returns:
+ 0 - Success
+ 1 - Failure
+ """
+
+ be = BootEnvironment()
+
+ list_all_attrs = ""
+ list_datasets = ""
+ list_snapshots = ""
+ dont_display_headers = False
+ be_name = None
+ be_list = None
+
+ # Counters for detecting multiple options.
+ # e.g. beadm list -a -a newbe
+ num_a_opts = 0
+ num_d_opts = 0
+ num_s_opts = 0
+ num_h_opts = 0
+
+ try:
+ opts_args, be.trgt_be_name_or_snapshot = getopt.getopt(opts, "adHs")
+ except getopt.GetoptError:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ for opt, arg in opts_args:
+ if opt == "-a":
+ list_all_attrs = opt
+ num_a_opts += 1
+ elif opt == "-d":
+ list_datasets = opt
+ num_d_opts += 1
+ elif opt == "-s":
+ list_snapshots = opt
+ num_s_opts += 1
+ elif opt == "-H":
+ dont_display_headers = True
+ num_h_opts += 1
+
+ if num_a_opts > 1 or num_d_opts > 1 or num_s_opts > 1 or num_h_opts > 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ if len(be.trgt_be_name_or_snapshot) > 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ if len(be.trgt_be_name_or_snapshot) == 1:
+ be_name = be.trgt_be_name_or_snapshot[0]
+ if lb.beVerifyBEName(be_name) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+
+ if (list_all_attrs == "-a" and (list_datasets == "-d" \
+ or list_snapshots == "-s")):
+ msg.printMsg(msg.Msgs.BEADM_ERR_MUTUALLY_EXCL,
+ list_all_attrs + " " + list_datasets + " " +
+ list_snapshots, -1)
+ usage()
+
+ list_options = ""
+
+ # When zones are implemented add "listZones == "-z" below
+
+ # Coelesce options to pass to displayBEs
+
+ if (list_datasets == "-d" and list_snapshots == "-s" or \
+ list_all_attrs == "-a"):
+ list_options = "-a"
+ elif list_datasets != "" or list_snapshots != "" or list_all_attrs != "":
+ list_options = list_datasets + " " + list_snapshots
+
+ rc, be_list = lb.beList()
+ if rc != 0:
+ if rc == msg.Msgs.BE_ERR_BE_NOENT:
+ if be_name == None:
+ msg.printMsg(msg.Msgs.BEADM_ERR_NO_BES_EXIST,
+ None, -1)
+ return 1
+
+ string = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST,
+ be_name)
+ else:
+ string = lb.beGetErrDesc(rc)
+ if string == None:
+ string = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_LIST, string, -1)
+ return 1
+
+ # classify according to command line options
+ if list_options.find("-a") != -1 or \
+ (list_options.find("-d") != -1 and list_options.find("-s") != -1):
+ list_object = CompleteList(dont_display_headers) #all
+ elif list_options.find("-d") != -1:
+ list_object = DatasetList(dont_display_headers) #dataset
+ elif list_options.find("-s") != -1:
+ list_object = SnapshotList(dont_display_headers) #snapshot
+ else: list_object = BEList(dont_display_headers) #only BE
+
+ # use list method for object
+ if list_object.list(be_list, dont_display_headers, be_name) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_LIST_DATA, None, -1)
+ return 1
+
+ return 0
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def mount(opts):
+ """
+ Description: Mount a Boot Environment on a directory.
+ The following is the subcommand, options
+ and args that make up the opts object
+ passed in:
+
+ mount beName [mountpoint]
+
+ Parameters:
+ opts - A object containing the mount subcommand
+ and all the options and arguments passed in
+ on the command line mentioned above.
+
+ Returns:
+ 0 - Success
+ 1 - Failure
+ """
+
+ be = BootEnvironment()
+
+ mountpoint = None
+
+ try:
+ be_name_mnt_point = getopt.getopt(opts, "")[1]
+ except getopt.GetoptError:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ mnt_point_len = len(be_name_mnt_point)
+
+ if mnt_point_len != 2:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+ else:
+ # Check for leading / in mount point
+ mountpoint = be_name_mnt_point[1]
+ if not mountpoint.startswith('/'):
+ msg.printMsg(msg.Msgs.BEADM_ERR_MOUNTPOINT,
+ mountpoint, -1)
+ return 1
+
+ if lb.beVerifyBEName(be_name_mnt_point[0]) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+
+ rc = lb.beMount(be_name_mnt_point[0], mountpoint)
+ if rc == 0:
+ return 0
+
+ be.msg_buf["0"] = be_name_mnt_point[0]
+ if rc == msg.Msgs.BE_ERR_MOUNTED:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_MOUNT_EXISTS,
+ be_name_mnt_point[0])
+ elif rc == msg.Msgs.BE_ERR_BE_NOENT:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST,
+ be_name_mnt_point[0])
+ elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc)
+ msg.printMsg(msg.Msgs.BEADM_ERR_MOUNT, be.msg_buf, -1)
+ return 1
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_MOUNT, be.msg_buf, -1)
+ return 1
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def unmount(opts):
+ """
+ Description: Unmount a Boot Environment.
+ The following is the subcommand, options
+ and args that make up the opts object
+ passed in:
+
+ unmount [-f] beName
+
+ Parameters:
+ opts - A object containing the unmount subcommand
+ and all the options and arguments passed in
+ on the command line mentioned above.
+
+ Returns:
+ 0 - Success
+ 1 - Failure
+ """
+
+ be = BootEnvironment()
+
+ force_unmount = 0
+
+ # Counter for detecting multiple options.
+ # e.g. beadm unmount -f -f newbe
+ num_f_opts = 0
+
+ try:
+ optlist, args = getopt.getopt(opts, "f")
+ except getopt.GetoptError:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ for opt, arg in optlist:
+ if opt == "-f":
+ force_unmount = 1
+ num_f_opts += 1
+
+ if num_f_opts > 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ if len(args) != 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ if lb.beVerifyBEName(args[0]) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+
+ rc = lb.beUnmount(args[0], force_unmount)
+ if rc == 0:
+ return 0
+
+ be.msg_buf["0"] = args[0]
+ if rc == msg.Msgs.BE_ERR_UMOUNT_CURR_BE:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_UNMOUNT_ACTIVE,
+ args[0])
+ elif rc == msg.Msgs.BE_ERR_UMOUNT_SHARED:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_SHARED_FS, args[0])
+ elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc)
+ msg.printMsg(msg.Msgs.BEADM_ERR_UNMOUNT, be.msg_buf, -1)
+ return 1
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_UNMOUNT, be.msg_buf, -1)
+ return 1
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def rename(opts):
+ """
+ Description: Rename the name of a Boot Environment.
+ The following is the subcommand, options
+ and args that make up the opts object
+ passed in:
+
+ rename beName newBeName
+
+ Parameters:
+ opts - A object containing the mount subcommand
+ and all the options and arguments passed in
+ on the command line mentioned above.
+
+ Returns:
+ 0 - Success
+ 1 - Failure
+ """
+
+ be = BootEnvironment()
+
+ try:
+ be_names = getopt.getopt(opts, "")[1]
+ except getopt.GetoptError:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ if len(be_names) != 2:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ usage()
+
+ if lb.beVerifyBEName(be_names[0]) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+
+ if lb.beVerifyBEName(be_names[1]) != 0:
+ msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1)
+ return 1
+
+ rc = lb.beRename(be_names[0], be_names[1])
+
+ if rc == 0:
+ return 0
+
+ be.msg_buf["0"] = be_names[0]
+ if rc == msg.Msgs.BE_ERR_BE_NOENT:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST,
+ be_names[0])
+ elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc)
+ msg.printMsg(msg.Msgs.BEADM_ERR_RENAME, be.msg_buf, -1)
+ return 1
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_RENAME, be.msg_buf, -1)
+ return 1
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# End of CLI public functions
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Verify the options and arguments for the beadm create subcommand
+
+def verifyCreateOptionsArgs(be):
+ """Check valid BE names."""
+
+ len_be_args = len(be.trgt_be_name_or_snapshot)
+ if len_be_args < 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ return 1
+ if len_be_args > 1:
+ msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1)
+ idx = 0
+ while len_be_args > idx:
+ msg.printMsg(msg.Msgs.BEADM_MSG_FREE_FORMAT,
+ be.trgt_be_name_or_snapshot[idx], -1)
+ idx += 1
+ return 1
+
+ return 0
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def parseCLI(cli_opts_args):
+ """Parse command line interface arguments."""
+
+ gettext.install("beadm", "/usr/lib/locale")
+
+ if len(cli_opts_args) == 0:
+ usage()
+
+ subcommand = cli_opts_args[0]
+ opts_args = cli_opts_args[1:]
+
+ if subcommand == "activate":
+ rc = activate(opts_args)
+ elif subcommand == "create":
+ rc = create(opts_args)
+ elif subcommand == "destroy":
+ rc = destroy(opts_args)
+ elif subcommand == "list":
+ rc = list(opts_args)
+ elif subcommand == "mount":
+ rc = mount(opts_args)
+ elif subcommand == "rename":
+ rc = rename(opts_args)
+ elif subcommand == "upgrade":
+ rc = upgrade(opts_args)
+ elif subcommand == "unmount" or \
+ subcommand == "umount": #aliased for convenience
+ rc = unmount(opts_args)
+ elif subcommand == "verify":
+ rc = verify()
+ else:
+ msg.printMsg(msg.Msgs.BEADM_ERR_ILL_SUBCOMMAND,
+ subcommand, -1)
+ usage()
+
+ return(rc)
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def main():
+ """main function."""
+
+ gettext.install("beadm", "/usr/lib/locale")
+
+ if not isBeadmSupported():
+ return(1)
+
+ return(parseCLI(sys.argv[1:]))
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def initBELog(log_id, be):
+ """
+ Initiate the BE log
+
+ Format of the log
+ yyyymmdd_hhmmss - 20071130_140558
+ yy - year; 2007
+ mm - month; 11
+ dd - day; 30
+ hh - hour; 14
+ mm - minute; 05
+ ss - second; 58
+ """
+
+ # /var/log/beadm/<beName>/<logId>.log.<yyyymmdd_hhmmss>
+
+ date = time.strftime("%Y%m%d_%H%M%S", time.localtime())
+
+ be.log = "/var/log/beadm/" + be.trgt_be_name_or_snapshot[0] + \
+ "/" + log_id + ".log" + "." + date
+
+ if not os.path.isfile(be.log) and not os.path.islink(be.log):
+ if not os.path.isdir(os.path.dirname(be.log)):
+ try:
+ os.makedirs(os.path.dirname(be.log), 0644)
+ except OSError:
+ be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0]
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS,
+ 0)
+ msg.printMsg(msg.Msgs.BEADM_ERR_CREATE,
+ be.msg_buf, -1)
+ return 1
+ try:
+ be.log_id = open(be.log, "a")
+ except IOError:
+ msg.printMsg(msg.Msgs.BEADM_ERR_LOG_CREATE,
+ None, -1)
+ return 1
+ else:
+ # Should never happen due to new time stamp each call
+ msg.printMsg(msg.Msgs.BEADM_ERR_LOG_CREATE, None, -1)
+ return 1
+
+ return 0
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def cleanupBELog(be):
+ """Clean up BE log."""
+
+ be.log_id.close()
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def displayDestructionQuestion(be):
+ """Display a destruction question and wait for user response."""
+
+ msg.printMsg(msg.Msgs.BEADM_MSG_DESTROY, be.trgt_be_name_or_snapshot[0], -1)
+ while True:
+ try:
+ value = raw_input().strip().upper()
+ except KeyboardInterrupt:
+ return False
+ if (value == 'Y' or value == 'YES'):
+ return True
+ elif len(value) == 0 or value == 'N' or value == 'NO':
+ msg.printMsg(msg.Msgs.BEADM_MSG_DESTROY_NO,
+ be.trgt_be_name_or_snapshot[0], -1)
+ return False
+ else:
+ msg.printMsg(msg.Msgs.BEADM_ERR_INVALID_RESPONSE,
+ -1)
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def setMaxColumnWidths(be_max_w, ds_max_w, ss_max_w, be_list):
+ """Figure out max column widths for BE's, Datasets and Snapshots."""
+
+ for be_item in be_list:
+ if be_item.get("orig_be_name") is not None:
+ determineMaxBEColWidth(be_item, be_max_w)
+ if be_item.get("dataset") is not None:
+ determineMaxDSColWidth(be_item, ds_max_w)
+ if be_item.get("snap_name") is not None:
+ determineMaxSSColWidth(be_item, ss_max_w)
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def getActiveBEAndActiveOnBootBE():
+ """Return the 'active on boot' BE, the 'active' BE or None."""
+
+ active_be = None
+ active_be_on_boot = None
+
+ rc, be_list = lb.beList()
+
+ if rc != 0:
+ if rc == msg.Msgs.BE_ERR_BE_NOENT:
+ string = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_BES_EXIST)
+ else:
+ string = lb.beGetErrDesc(rc)
+ if string == None:
+ string = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_LIST, string, -1)
+ return None
+
+ for be_vals in be_list:
+ srcBeName = be_vals.get("orig_be_name")
+ if be_vals.get("active"):
+ active_be = srcBeName
+ if be_vals.get("active_boot"):
+ active_be_on_boot = srcBeName
+ if active_be is not None and active_be_on_boot is not None:
+ break
+
+ return active_be, active_be_on_boot
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def createSnapshot(be):
+ """Create a snapshot."""
+
+ be_name, snap_name = be.trgt_be_name_or_snapshot[0].split("@")
+
+ rc = lb.beCreateSnapshot(be_name, snap_name)[0]
+
+ if rc == 0:
+ return 0
+
+ be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0]
+ if rc == msg.Msgs.BE_ERR_BE_NOENT:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST,
+ be_name)
+ elif rc == msg.Msgs.BE_ERR_SS_EXISTS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_SNAP_EXISTS,
+ be.trgt_be_name_or_snapshot[0])
+ elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc)
+ msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, -1)
+ return 1
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, -1)
+
+ return 1
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def createBE(be):
+ """Create a Boot Environment."""
+
+ rc = lb.beCopy(be.trgt_be_name_or_snapshot[0], be.src_be_name_or_snapshot,
+ None, be.trgt_rpool, be.properties, be.description)[0]
+
+ if rc == 0:
+ msg.printMsg(msg.Msgs.BEADM_MSG_BE_CREATE_SUCCESS,
+ be.trgt_be_name_or_snapshot[0], be.log_id)
+ return 0
+
+ be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0]
+ if rc == msg.Msgs.BE_ERR_BE_NOENT:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST,
+ be.src_be_name_or_snapshot)
+ elif rc == msg.Msgs.BE_ERR_BE_EXISTS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_BE_EXISTS,
+ be.trgt_be_name_or_snapshot[0])
+ elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc)
+ msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, -1)
+ return 1
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, be.log_id)
+
+ return 1
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def createBEFromSnapshot(be):
+ """Create a BE based off a snapshot."""
+
+ be_name, snap_name = be.src_be_name_or_snapshot.split("@")
+
+ rc = lb.beCopy(be.trgt_be_name_or_snapshot[0], be_name, snap_name,
+ be.trgt_rpool, be.properties, be.description)[0]
+
+ if rc == 0:
+ msg.printMsg(msg.Msgs.BEADM_MSG_BE_CREATE_SUCCESS,
+ be.trgt_be_name_or_snapshot[0], be.log_id)
+ return 0
+
+ be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0]
+ if rc == msg.Msgs.BE_ERR_SS_NOENT:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_SNAP_DOES_NOT_EXISTS,
+ be.src_be_name_or_snapshot)
+ elif rc == msg.Msgs.BE_ERR_BE_EXISTS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_BE_EXISTS, \
+ be.trgt_be_name_or_snapshot[0])
+ elif rc == msg.Msgs.BE_ERR_BE_NOENT:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, \
+ be_name)
+ elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS:
+ be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc)
+ msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, -1)
+ return 1
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, be.log_id)
+
+ return 1
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def activateBE(be):
+ """
+ Activate a BE. Called from create() when -a is provided as CLI
+ Option.
+ """
+
+ rc = lb.beActivate(be.trgt_be_name_or_snapshot[0])
+ if rc == 0:
+ return 0
+
+ be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0]
+ if rc == msg.Msgs.BE_ERR_BE_NOENT:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, opts[0])
+ else:
+ be.msg_buf["1"] = lb.beGetErrDesc(rc)
+ if be.msg_buf["1"] == None:
+ be.msg_buf["1"] = \
+ msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc)
+
+ msg.printMsg(msg.Msgs.BEADM_ERR_ACTIVATE, be.msg_buf, -1)
+
+ return 1
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def isBeadmSupported():
+ """
+ Currently the only environment that beadm is supported in is
+ a global zone. Check that beadm is executing in a
+ global zone and not in a non-global zone.
+ """
+
+ try:
+ proc = subprocess.Popen("/sbin/zonename",
+ stdout = subprocess.PIPE,
+ stderr = subprocess.STDOUT)
+ # Grab stdout.
+ zonename = proc.communicate()[0].rstrip('\n')
+ except OSError, (errno, strerror):
+ msg.printMsg(msg.Msgs.BEADM_ERR_OS, strerror, -1)
+ # Ignore a failed attempt to retreive the zonename.
+ return True
+
+ if zonename != "global":
+ msg.printMsg(msg.Msgs.BEADM_ERR_NOT_SUPPORTED_NGZ, None, -1)
+ return False
+
+ return True
+
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+if __name__ == "__main__":
+ try:
+ RC = main()
+ except SystemExit, e:
+ raise e
+ except:
+ traceback.print_exc()
+ sys.exit(99)
+ sys.exit(RC)
diff --git a/usr/src/cmd/beadm/messages.py b/usr/src/cmd/beadm/messages.py
new file mode 100644
index 0000000000..87979a6797
--- /dev/null
+++ b/usr/src/cmd/beadm/messages.py
@@ -0,0 +1,264 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+beadm - The Boot Environment Administration tool.
+
+A module containing all of the messages output by beadm.
+"""
+
+import sys
+from beadm import _
+
+class Msgs:
+ """Indices corresponding to message numbers for beadm."""
+
+ (BEADM_ERR_ACTIVATE,
+ BEADM_ERR_BE_EXISTS,
+ BEADM_ERR_SNAP_EXISTS,
+ BEADM_ERR_CREATE,
+ BEADM_ERR_DESTROY,
+ BEADM_ERR_DESTROY_ACTIVE,
+ BEADM_ERR_BE_DOES_NOT_EXIST,
+ BEADM_ERR_NO_BES_EXIST,
+ BEADM_ERR_MSG_SUB,
+ BEADM_ERR_ILL_SUBCOMMAND,
+ BEADM_ERR_INVALID_RESPONSE,
+ BEADM_ERR_LIST,
+ BEADM_ERR_LIST_DATA,
+ BEADM_ERR_LOG_CREATE,
+ BEADM_ERR_LOG_RM,
+ BEADM_ERR_MOUNT,
+ BEADM_ERR_MOUNT_EXISTS,
+ BEADM_ERR_MOUNTED,
+ BEADM_ERR_MOUNTPOINT,
+ BEADM_ERR_MUTUALLY_EXCL,
+ BEADM_ERR_NO_MSG,
+ BEADM_ERR_NO_ZPOOL,
+ BEADM_ERR_NOT_SUPPORTED_NGZ,
+ BEADM_ERR_OPT_ARGS,
+ BEADM_ERR_OS,
+ BEADM_ERR_PERMISSIONS,
+ BEADM_ERR_RENAME,
+ BEADM_ERR_SHARED_FS,
+ BEADM_ERR_SNAP_DOES_NOT_EXISTS,
+ BEADM_ERR_UNMOUNT,
+ BEADM_ERR_UNMOUNT_ACTIVE,
+ BEADM_ERR_BENAME,
+ BEADM_MSG_ACTIVE_ON_BOOT,
+ BEADM_MSG_DESTROY,
+ BEADM_MSG_DESTROY_NO,
+ BEADM_MSG_BE_CREATE_START,
+ BEADM_MSG_BE_CREATE_SUCCESS,
+ BEADM_MSG_FREE_FORMAT,
+ ) = range(38)
+
+ # Indices corresponding to message numbers for libbe that we are
+ # interested in expanding messages.
+ (BE_ERR_ACCESS,
+ BE_ERR_ACTIVATE_CURR,
+ BE_ERR_AUTONAME,
+ BE_ERR_BE_NOENT,
+ BE_ERR_BUSY,
+ BE_ERR_CANCELED,
+ BE_ERR_CLONE,
+ BE_ERR_COPY,
+ BE_ERR_CREATDS,
+ BE_ERR_CURR_BE_NOT_FOUND,
+ BE_ERR_DESTROY,
+ BE_ERR_DEMOTE,
+ BE_ERR_DSTYPE,
+ BE_ERR_BE_EXISTS,
+ BE_ERR_INIT,
+ BE_ERR_INTR,
+ BE_ERR_INVAL,
+ BE_ERR_INVALPROP,
+ BE_ERR_INVALMOUNTPOINT,
+ BE_ERR_MOUNT,
+ BE_ERR_MOUNTED,
+ BE_ERR_NAMETOOLONG,
+ BE_ERR_NOENT,
+ BE_ERR_POOL_NOENT,
+ BE_ERR_NODEV,
+ BE_ERR_NOTMOUNTED,
+ BE_ERR_NOMEM,
+ BE_ERR_NONINHERIT,
+ BE_ERR_NXIO,
+ BE_ERR_NOSPC,
+ BE_ERR_NOTSUP,
+ BE_ERR_OPEN,
+ BE_ERR_PERM,
+ BE_ERR_UNAVAIL,
+ BE_ERR_PROMOTE,
+ BE_ERR_ROFS,
+ BE_ERR_READONLYDS,
+ BE_ERR_READONLYPROP,
+ BE_ERR_SS_EXISTS,
+ BE_ERR_SS_NOENT,
+ BE_ERR_UMOUNT,
+ BE_ERR_UMOUNT_CURR_BE,
+ BE_ERR_UMOUNT_SHARED,
+ BE_ERR_UNKNOWN,
+ BE_ERR_ZFS,
+ BE_ERR_DESTROY_CURR_BE,
+ BE_ERR_GEN_UUID,
+ BE_ERR_PARSE_UUID,
+ BE_ERR_NO_UUID,
+ BE_ERR_ZONE_NO_PARENTBE,
+ BE_ERR_ZONE_MULTIPLE_ACTIVE,
+ BE_ERR_ZONE_NO_ACTIVE_ROOT,
+ BE_ERR_ZONE_ROOT_NOT_LEGACY,
+ BE_ERR_NO_MOUNTED_ZONE,
+ BE_ERR_MOUNT_ZONEROOT,
+ BE_ERR_UMOUNT_ZONEROOT,
+ BE_ERR_ZONES_UNMOUNT,
+ BE_ERR_FAULT,
+ BE_ERR_RENAME_ACTIVE,
+ BE_ERR_NO_MENU,
+ BE_ERR_DEV_BUSY,
+ BE_ERR_BAD_MENU_PATH,
+ BE_ERR_ZONE_SS_EXISTS
+ ) = range(4000, 4063)
+
+ # Error message dictionaries.
+ mBeadmErr = {}
+ mBeadmOut = {}
+ mBeadmLog = {}
+
+ # Errors from beadm (to stderr).
+ mBeadmErr[BEADM_ERR_ACTIVATE] = _("Unable to activate %(0)s.\n%(1)s")
+ mBeadmErr[BEADM_ERR_BE_EXISTS] = _("BE %s already exists. Please choose a different BE name.")
+ mBeadmErr[BEADM_ERR_BE_DOES_NOT_EXIST] = _("%s does not exist or appear to be a valid BE.\nPlease check that the name of the BE provided is correct.")
+ mBeadmErr[BEADM_ERR_NO_BES_EXIST] = _("No boot environments found on this system.")
+ mBeadmErr[BEADM_ERR_CREATE] = _("Unable to create %(0)s.\n%(1)s")
+ mBeadmErr[BEADM_ERR_DESTROY] = _("Unable to destroy %(0)s.\n%(1)s")
+ mBeadmErr[BEADM_ERR_DESTROY_ACTIVE] = _("%(0)s is the currently active BE and cannot be destroyed.\nYou must boot from another BE in order to destroy %(1)s.")
+ mBeadmErr[BEADM_ERR_MSG_SUB] = _("Fatal error. No message associated with index %d")
+ mBeadmErr[BEADM_ERR_ILL_SUBCOMMAND] = _("Illegal subcommand %s")
+ mBeadmErr[BEADM_ERR_INVALID_RESPONSE] = _("Invalid response. Please enter 'y' or 'n'.")
+ mBeadmErr[BEADM_ERR_LIST] = _("Unable to display Boot Environment: %s")
+ mBeadmErr[BEADM_ERR_LIST_DATA] = _("Unable to process list data.")
+ mBeadmErr[BEADM_ERR_LOG_CREATE] = _("Unable to create log file.")
+ mBeadmErr[BEADM_ERR_LOG_RM] = _("Unable to remove %s")
+ mBeadmErr[BEADM_ERR_MOUNT] = _("Unable to mount %(0)s.\n%(1)s")
+ mBeadmErr[BEADM_ERR_MOUNT_EXISTS] = _("%s is already mounted.\nPlease unmount the BE before mounting it again.")
+ mBeadmErr[BEADM_ERR_MOUNTED] = _("Unable to destroy %(0)s.\nIt is currently mounted and must be unmounted before it can be destroyed.\nUse 'beadm unmount %(1)s' to unmount the BE before destroying\nit or 'beadm destroy -fF %(2)s'.")
+ mBeadmErr[BEADM_ERR_MOUNTPOINT] = _("Invalid mount point %s. Mount point must start with a /.")
+ mBeadmErr[BEADM_ERR_MUTUALLY_EXCL] = _("Invalid options: %s are mutually exclusive.")
+ mBeadmErr[BEADM_ERR_NO_MSG] = _("Unable to find message for error code: %d")
+ mBeadmErr[BEADM_ERR_NO_ZPOOL] = _("BE: %s was not found in any pool.\n The pool may not exist or the name of the BE is not correct.")
+ mBeadmErr[BEADM_ERR_NOT_SUPPORTED_NGZ] = _("beadm is not supported in a non-global zone.")
+ mBeadmErr[BEADM_ERR_OPT_ARGS] = _("Invalid options and arguments:")
+ mBeadmErr[BEADM_ERR_OS] = _("System error: %s")
+ mBeadmErr[BEADM_ERR_RENAME] = _("Rename of BE %(0)s failed.\n%(1)s")
+ mBeadmErr[BEADM_ERR_SHARED_FS] = _("%s is a shared file system and it cannot be unmounted.")
+ mBeadmErr[BEADM_ERR_SNAP_DOES_NOT_EXISTS] = _("%s does not exist or appear to be a valid snapshot.\nPlease check that the name of the snapshot provided is correct.")
+ mBeadmErr[BEADM_ERR_SNAP_EXISTS] = _("Snapshot %s already exists.\n Please choose a different snapshot name.")
+ mBeadmErr[BEADM_ERR_UNMOUNT] = _("Unable to unmount %(0)s.\n%(1)s")
+ mBeadmErr[BEADM_ERR_UNMOUNT_ACTIVE] = _("%s is the currently active BE.\nIt cannot be unmounted unless another BE is the currently active BE.")
+ mBeadmErr[BE_ERR_ZONES_UNMOUNT] = _("Unable to destroy one of %(0)s's zone BE's.\nUse 'beadm destroy -fF %(1)s' or 'zfs -f destroy <dataset>'.")
+ mBeadmErr[BEADM_ERR_PERMISSIONS] = _("You have insufficient privileges to execute this command.\nEither use 'pfexec' to execute the command or become superuser.")
+ mBeadmErr[BEADM_ERR_BENAME] = _("The BE name provided is invalid.\n Please check it and try again.")
+
+ # Catchall
+ mBeadmErr[BEADM_MSG_FREE_FORMAT] = "%s"
+
+ # Messages from beadm (to stdout).
+ mBeadmOut[BEADM_MSG_ACTIVE_ON_BOOT] = _("The BE that was just destroyed was the 'active on boot' BE.\n%s is now the 'active on boot' BE. Use 'beadm activate' to change it.\n")
+ mBeadmOut[BEADM_MSG_DESTROY] = _("Are you sure you want to destroy %s? This action cannot be undone(y/[n]):")
+ mBeadmOut[BEADM_MSG_DESTROY_NO] = _("%s has not been destroyed.\n")
+
+ # Messages from beadm (to log only).
+ mBeadmLog[BEADM_MSG_BE_CREATE_START] = "Attempting to create %s"
+ mBeadmLog[BEADM_MSG_BE_CREATE_SUCCESS] = "%s was created successfully"
+
+msgLog, msgOut, msgErr = range(3)
+
+def printLog(string, log_fd):
+ """Print log."""
+
+ sendMsg(string, msgLog, log_fd)
+
+def printStdout(string, log_fd):
+ """Print standard output."""
+
+ sendMsg(string, msgOut, log_fd)
+
+def printStderr(string, log_fd):
+ """Print standard error."""
+
+ sendMsg(string, msgErr, log_fd)
+
+def composeMsg(string, txt=None):
+ """
+ Compose the message to be dispayed.
+ txt can be either a list or string object.
+ Return the newly composed string.
+ """
+
+ try:
+ msg = string % txt
+ except TypeError:
+ msg = string
+
+ return (msg)
+
+def sendMsg(string, mode, log_fd=-1):
+ """Send message."""
+
+ if mode == msgOut:
+ print >> sys.stdout, string,
+ if mode == msgErr:
+ print >> sys.stderr, string
+ if log_fd != -1 or mode == msgLog:
+ log_fd.write(string + "\n")
+
+def printMsg(msg_idx=-1, txt="", log_fd=-1):
+ """Print the message based on the message index."""
+
+ if msg_idx in Msgs.mBeadmErr:
+ printStderr(composeMsg(Msgs.mBeadmErr[msg_idx], txt),
+ log_fd)
+ elif msg_idx in Msgs.mBeadmOut:
+ printStdout(composeMsg(Msgs.mBeadmOut[msg_idx], txt),
+ log_fd)
+ elif msg_idx in Msgs.mBeadmLog:
+ printLog(composeMsg(Msgs.mBeadmLog[msg_idx], txt), log_fd)
+ else:
+ printStderr(composeMsg(Msgs.mLibbe[BEADM_ERR_MSG_SUB],
+ msg_idx), -1)
+ sys.exit(1)
+
+def getMsg(msg_idx=-1, txt=""):
+ """Print the message based on the message index."""
+
+ if msg_idx in Msgs.mBeadmErr:
+ return(composeMsg(Msgs.mBeadmErr[msg_idx], txt))
+ elif msg_idx in Msgs.mBeadmOut:
+ return(composeMsg(Msgs.mBeadmOut[msg_idx], txt))
+ elif msg_idx in Msgs.mBeadmLog:
+ return(composeMsg(Msgs.mBeadmLog[msg_idx], txt))
+ else:
+ return(composeMsg(Msgs.mLibbe[BEADM_ERR_MSG_SUB]))
+ sys.exit(1)
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 73d817e14a..e9e9af7d0f 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -238,6 +238,8 @@ SUBDIRS += \
udapl \
libzpool \
libzfs \
+ libbe \
+ pylibbe \
libzfs_jni \
pyzfs \
pysolaris \
@@ -569,6 +571,7 @@ $(CLOSED_BUILD)libc: $(CLOSED)/lib/libc_i18n
libast: libsocket
libadutils: libldap5 libresolv libsocket libnsl
nsswitch: libadutils libidmap
+libbe: libzfs
libbsm: libtsol
libcmd: libsum libast libsocket libnsl
libcmdutils: libavl
@@ -659,6 +662,7 @@ sun_fc: libdevinfo libsysevent libnvpair
libsun_ima: libdevinfo libsysevent libnsl
sun_sas: libdevinfo libsysevent libnvpair libkstat libdevid
libgrubmgmt: libdevinfo libzfs libfstyp
+pylibbe: libbe libzfs
pyzfs: libnvpair libzfs
pysolaris: libsec libidmap
libreparse: libnvpair
diff --git a/usr/src/lib/libbe/Makefile b/usr/src/lib/libbe/Makefile
new file mode 100644
index 0000000000..d9beee702d
--- /dev/null
+++ b/usr/src/lib/libbe/Makefile
@@ -0,0 +1,72 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.lib
+
+HDRS= libbe.h \
+ libbe_priv.h
+
+HDRDIR= common
+
+SUBDIRS= $(MACH)
+TESTDIR= tbeadm
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+test := TARGET= test
+
+POFILE = libbe.po
+MSGFILES = `$(GREP) -l gettext $(HDRDIR)/*.[ch]`
+
+.KEEP_STATE:
+
+all install lint: install_h $(SUBDIRS)
+
+clean clobber: $(SUBDIRS) $(TESTDIR)
+
+$(POFILE): pofile_MSGFILES
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+_msg: $(MSGDOMAINPOFILE)
+
+test:
+ cd tbeadm; pwd; $(MAKE) install
+
+$(TESTDIR): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libbe/Makefile.com b/usr/src/lib/libbe/Makefile.com
new file mode 100644
index 0000000000..fad5ba58ed
--- /dev/null
+++ b/usr/src/lib/libbe/Makefile.com
@@ -0,0 +1,62 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+LIBRARY= libbe.a
+VERS= .1
+
+OBJECTS= \
+ be_activate.o \
+ be_create.o \
+ be_list.o \
+ be_mount.o \
+ be_rename.o \
+ be_snapshot.o \
+ be_utils.o \
+ be_zones.o
+
+include ../../Makefile.lib
+
+LIBS= $(DYNLIB) $(LINTLIB)
+
+SRCDIR= ../common
+
+INCS += -I$(SRCDIR)
+
+C99MODE= $(C99_ENABLE)
+
+LDLIBS += -lzfs -linstzones -luuid -lnvpair -lc -lgen
+CPPFLAGS += $(INCS)
+
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+.KEEP_STATE:
+
+all: $(LIBS) $(LIBRARY)
+
+lint: lintcheck
+
+install: $(ROOTLIBS)
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libbe/common/be_activate.c b/usr/src/lib/libbe/common/be_activate.c
new file mode 100644
index 0000000000..c83e442fa9
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_activate.c
@@ -0,0 +1,1191 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <assert.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mnttab.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+char *mnttab = MNTTAB;
+
+/*
+ * Private function prototypes
+ */
+static int set_bootfs(char *boot_rpool, char *be_root_ds);
+static int set_canmount(be_node_list_t *, char *);
+static int be_do_installgrub(be_transaction_data_t *);
+static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
+static int get_ver_from_capfile(char *, char **);
+static int be_promote_zone_ds(char *, char *);
+static int be_promote_ds_callback(zfs_handle_t *, void *);
+
+/* ******************************************************************** */
+/* Public Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_activate
+ * Description: Calls _be_activate which activates the BE named in the
+ * attributes passed in through be_attrs. The process of
+ * activation sets the bootfs property of the root pool, resets
+ * the canmount property to noauto, and sets the default in the
+ * grub menu to the entry corresponding to the entry for the named
+ * BE.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The follow attribute values are used by this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *required
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_activate(nvlist_t *be_attrs)
+{
+ int ret = BE_SUCCESS;
+ char *be_name = NULL;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get the BE name to activate */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
+ != 0) {
+ be_print_err(gettext("be_activate: failed to "
+ "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /* Validate BE name */
+ if (!be_valid_be_name(be_name)) {
+ be_print_err(gettext("be_activate: invalid BE name %s\n"),
+ be_name);
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ ret = _be_activate(be_name);
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/* ******************************************************************** */
+/* Semi Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: _be_activate
+ * Description: This does the actual work described in be_activate.
+ * Parameters:
+ * be_name - pointer to the name of BE to activate.
+ *
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errnot_t - Failure
+ * Scope:
+ * Public
+ */
+int
+_be_activate(char *be_name)
+{
+ be_transaction_data_t cb = { 0 };
+ zfs_handle_t *zhp = NULL;
+ char root_ds[MAXPATHLEN];
+ char *cur_vers = NULL, *new_vers = NULL;
+ be_node_list_t *be_nodes = NULL;
+ uuid_t uu = {0};
+ int entry, ret = BE_SUCCESS;
+ int zret = 0;
+
+ /*
+ * TODO: The BE needs to be validated to make sure that it is actually
+ * a bootable BE.
+ */
+
+ if (be_name == NULL)
+ return (BE_ERR_INVAL);
+
+ /* Set obe_name to be_name in the cb structure */
+ cb.obe_name = be_name;
+
+ /* find which zpool the be is in */
+ if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) {
+ be_print_err(gettext("be_activate: failed to "
+ "find zpool for BE (%s)\n"), cb.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_activate: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+
+ be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds));
+ cb.obe_root_ds = strdup(root_ds);
+
+ if (getzoneid() == GLOBAL_ZONEID) {
+ if (be_has_grub() && (ret = be_get_grub_vers(&cb, &cur_vers,
+ &new_vers)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to get grub "
+ "versions from capability files.\n"));
+ return (ret);
+ }
+ if (cur_vers != NULL) {
+ /*
+ * We need to check to see if the version number from
+ * the BE being activated is greater than the current
+ * one.
+ */
+ if (new_vers != NULL &&
+ atof(cur_vers) < atof(new_vers)) {
+ if ((ret = be_do_installgrub(&cb))
+ != BE_SUCCESS) {
+ free(new_vers);
+ free(cur_vers);
+ return (ret);
+ }
+ free(new_vers);
+ }
+ free(cur_vers);
+ } else if (new_vers != NULL) {
+ if ((ret = be_do_installgrub(&cb)) != BE_SUCCESS) {
+ free(new_vers);
+ return (ret);
+ }
+ free(new_vers);
+ }
+ if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) {
+ if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool,
+ NULL, NULL, NULL)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: Failed to "
+ "add BE (%s) to the GRUB menu\n"),
+ cb.obe_name);
+ goto done;
+ }
+ }
+ if (be_has_grub()) {
+ if ((ret = be_change_grub_default(cb.obe_name,
+ cb.obe_zpool)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to "
+ "change the default entry in menu.lst\n"));
+ goto done;
+ }
+ }
+ }
+
+ if ((ret = _be_list(cb.obe_name, &be_nodes)) != BE_SUCCESS) {
+ return (ret);
+ }
+
+ if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to set "
+ "canmount dataset property\n"));
+ goto done;
+ }
+
+ if ((ret = set_bootfs(be_nodes->be_rpool, root_ds)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to set "
+ "bootfs pool property for %s\n"), root_ds);
+ goto done;
+ }
+
+ if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) {
+ /*
+ * We don't need to close the zfs handle at this
+ * point because The callback funtion
+ * be_promote_ds_callback() will close it for us.
+ */
+ if (be_promote_ds_callback(zhp, NULL) != 0) {
+ be_print_err(gettext("be_activate: "
+ "failed to activate the "
+ "datasets for %s: %s\n"),
+ root_ds,
+ libzfs_error_description(g_zfs));
+ ret = BE_ERR_PROMOTE;
+ goto done;
+ }
+ } else {
+ be_print_err(gettext("be_activate:: failed to open "
+ "dataset (%s): %s\n"), root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (getzoneid() == GLOBAL_ZONEID &&
+ be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS &&
+ (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to promote "
+ "the active zonepath datasets for zones in BE %s\n"),
+ cb.obe_name);
+ }
+
+done:
+ be_free_list(be_nodes);
+ return (ret);
+}
+
+/*
+ * Function: be_activate_current_be
+ * Description: Set the currently "active" BE to be "active on boot"
+ * Paramters:
+ * none
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errnot_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_activate_current_be(void)
+{
+ int ret = BE_SUCCESS;
+ be_transaction_data_t bt = { 0 };
+
+ if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
+ return (ret);
+ }
+
+ if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate_current_be: failed to "
+ "activate %s\n"), bt.obe_name);
+ return (ret);
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_is_active_on_boot
+ * Description: Checks if the BE name passed in has the "active on boot"
+ * property set to B_TRUE.
+ * Paramters:
+ * be_name - the name of the BE to check
+ * Returns:
+ * B_TRUE - if active on boot.
+ * B_FALSE - if not active on boot.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+be_is_active_on_boot(char *be_name)
+{
+ be_node_list_t *be_node = NULL;
+
+ if (be_name == NULL) {
+ be_print_err(gettext("be_is_active_on_boot: "
+ "be_name must not be NULL\n"));
+ return (B_FALSE);
+ }
+
+ if (_be_list(be_name, &be_node) != BE_SUCCESS) {
+ return (B_FALSE);
+ }
+
+ if (be_node == NULL) {
+ return (B_FALSE);
+ }
+
+ if (be_node->be_active_on_boot) {
+ be_free_list(be_node);
+ return (B_TRUE);
+ } else {
+ be_free_list(be_node);
+ return (B_FALSE);
+ }
+}
+
+/* ******************************************************************** */
+/* Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: set_bootfs
+ * Description: Sets the bootfs property on the boot pool to be the
+ * root dataset of the activated BE.
+ * Parameters:
+ * boot_pool - The pool we're setting bootfs in.
+ * be_root_ds - The main dataset for the BE.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+set_bootfs(char *boot_rpool, char *be_root_ds)
+{
+ zpool_handle_t *zhp;
+ int err = BE_SUCCESS;
+
+ if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) {
+ be_print_err(gettext("set_bootfs: failed to open pool "
+ "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+
+ err = zpool_set_prop(zhp, "bootfs", be_root_ds);
+ if (err) {
+ be_print_err(gettext("set_bootfs: failed to set "
+ "bootfs property for pool %s: %s\n"), boot_rpool,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ zpool_close(zhp);
+ return (err);
+ }
+
+ zpool_close(zhp);
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: set_canmount
+ * Description: Sets the canmount property on the datasets of the
+ * activated BE.
+ * Parameters:
+ * be_nodes - The be_node_t returned from be_list
+ * value - The value of canmount we setting, on|off|noauto.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+set_canmount(be_node_list_t *be_nodes, char *value)
+{
+ char ds_path[MAXPATHLEN];
+ zfs_handle_t *zhp = NULL;
+ be_node_list_t *list = be_nodes;
+ int err = BE_SUCCESS;
+
+ while (list != NULL) {
+ be_dataset_list_t *datasets = list->be_node_datasets;
+
+ be_make_root_ds(list->be_rpool, list->be_node_name, ds_path,
+ sizeof (ds_path));
+
+ if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) ==
+ NULL) {
+ be_print_err(gettext("set_canmount: failed to open "
+ "dataset (%s): %s\n"), ds_path,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+ if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
+ /*
+ * it's already mounted so we can't change the
+ * canmount property anyway.
+ */
+ err = BE_SUCCESS;
+ } else {
+ err = zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
+ if (err) {
+ ZFS_CLOSE(zhp);
+ be_print_err(gettext("set_canmount: failed to "
+ "set dataset property (%s): %s\n"),
+ ds_path, libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+ }
+ ZFS_CLOSE(zhp);
+
+ while (datasets != NULL) {
+ be_make_root_ds(list->be_rpool,
+ datasets->be_dataset_name, ds_path,
+ sizeof (ds_path));
+
+ if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET))
+ == NULL) {
+ be_print_err(gettext("set_canmount: failed to "
+ "open dataset %s: %s\n"), ds_path,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+ if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
+ /*
+ * it's already mounted so we can't change the
+ * canmount property anyway.
+ */
+ err = BE_SUCCESS;
+ ZFS_CLOSE(zhp);
+ break;
+ }
+ err = zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
+ if (err) {
+ ZFS_CLOSE(zhp);
+ be_print_err(gettext("set_canmount: "
+ "Failed to set property value %s "
+ "for dataset %s: %s\n"), value, ds_path,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ return (err);
+ }
+ ZFS_CLOSE(zhp);
+ datasets = datasets->be_next_dataset;
+ }
+ list = list->be_next_node;
+ }
+ return (err);
+}
+
+/*
+ * Function: be_get_grub_vers
+ * Description: Gets the grub version number from /boot/grub/capability. If
+ * capability file doesn't exist NULL is returned.
+ * Parameters:
+ * bt - The transaction data for the BE we're getting the grub
+ * version for.
+ * cur_vers - used to return the current version of grub from
+ * the root pool.
+ * new_vers - used to return the grub version of the BE we're
+ * activating.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failed to find version
+ * Scope:
+ * Private
+ */
+static int
+be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
+{
+ zfs_handle_t *zhp = NULL;
+ zfs_handle_t *pool_zhp = NULL;
+ int ret = BE_SUCCESS;
+ char cap_file[MAXPATHLEN];
+ char *temp_mntpnt = NULL;
+ char *zpool_mntpt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ boolean_t be_mounted = B_FALSE;
+ boolean_t pool_mounted = B_FALSE;
+
+ if (!be_has_grub()) {
+ be_print_err(gettext("be_get_grub_vers: Not supported on "
+ "this architecture\n"));
+ return (BE_ERR_NOTSUP);
+ }
+
+ if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
+ bt->obe_root_ds == NULL) {
+ be_print_err(gettext("be_get_grub_vers: Invalid BE\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt,
+ &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_grub_vers: pool dataset "
+ "(%s) could not be mounted\n"), bt->obe_zpool);
+ ZFS_CLOSE(pool_zhp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) {
+ be_print_err(gettext("be_get_grub_vers: pool "
+ "dataset (%s) is not mounted. Can't set the "
+ "default BE in the grub menu.\n"), bt->obe_zpool);
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ /*
+ * get the version of the most recent grub update.
+ */
+ (void) snprintf(cap_file, sizeof (cap_file), "%s%s",
+ zpool_mntpt, BE_CAP_FILE);
+ free(zpool_mntpt);
+ zpool_mntpt = NULL;
+
+ if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS)
+ goto cleanup;
+
+ if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_get_grub_vers: failed to "
+ "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
+ libzfs_error_description(g_zfs));
+ free(cur_vers);
+ ret = zfs_err_to_be_err(g_zfs);
+ goto cleanup;
+ }
+ if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
+ if ((ret = _be_mount(bt->obe_name, &temp_mntpnt,
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_grub_vers: failed to "
+ "mount BE (%s)\n"), bt->obe_name);
+ free(*cur_vers);
+ *cur_vers = NULL;
+ ZFS_CLOSE(zhp);
+ goto cleanup;
+ }
+ be_mounted = B_TRUE;
+ }
+ ZFS_CLOSE(zhp);
+
+ /*
+ * Now get the grub version for the BE being activated.
+ */
+ (void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
+ BE_CAP_FILE);
+ ret = get_ver_from_capfile(cap_file, new_vers);
+ if (ret != BE_SUCCESS) {
+ free(*cur_vers);
+ *cur_vers = NULL;
+ }
+ if (be_mounted)
+ (void) _be_unmount(bt->obe_name, 0);
+
+cleanup:
+ if (pool_mounted) {
+ int iret = BE_SUCCESS;
+ iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = iret;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(pool_zhp);
+
+ free(temp_mntpnt);
+ return (ret);
+}
+
+/*
+ * Function: get_ver_from_capfile
+ * Description: Parses the capability file passed in looking for the VERSION
+ * line. If found the version is returned in vers, if not then
+ * NULL is returned in vers.
+ *
+ * Parameters:
+ * file - the path to the capability file we want to parse.
+ * vers - the version string that will be passed back.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failed to find version
+ * Scope:
+ * Private
+ */
+static int
+get_ver_from_capfile(char *file, char **vers)
+{
+ FILE *fp = NULL;
+ char line[BUFSIZ];
+ char *last = NULL;
+ int err = BE_SUCCESS;
+ errno = 0;
+
+ if (!be_has_grub()) {
+ be_print_err(gettext("get_ver_from_capfile: Not supported "
+ "on this architecture\n"));
+ return (BE_ERR_NOTSUP);
+ }
+
+ /*
+ * Set version string to NULL; the only case this shouldn't be set
+ * to be NULL is when we've actually found a version in the capability
+ * file, which is set below.
+ */
+ *vers = NULL;
+
+ /*
+ * If the capability file doesn't exist, we're returning success
+ * because on older releases, the capability file did not exist
+ * so this is a valid scenario.
+ */
+ if (access(file, F_OK) == 0) {
+ if ((fp = fopen(file, "r")) == NULL) {
+ err = errno;
+ be_print_err(gettext("get_ver_from_capfile: failed to "
+ "open file %s with error %s\n"), file,
+ strerror(err));
+ err = errno_to_be_err(err);
+ return (err);
+ }
+
+ while (fgets(line, BUFSIZ, fp)) {
+ char *tok = strtok_r(line, "=", &last);
+
+ if (tok == NULL || tok[0] == '#') {
+ continue;
+ } else if (strcmp(tok, "VERSION") == 0) {
+ *vers = strdup(last);
+ break;
+ }
+ }
+ (void) fclose(fp);
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_do_installgrub
+ * Description: This function runs installgrub using the grub loader files
+ * from the BE we're activating and installing them on the
+ * pool the BE lives in.
+ *
+ * Parameters:
+ * bt - The transaction data for the BE we're activating.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ *
+ * Scope:
+ * Private
+ */
+static int
+be_do_installgrub(be_transaction_data_t *bt)
+{
+ zpool_handle_t *zphp = NULL;
+ zfs_handle_t *zhp = NULL;
+ nvlist_t **child, *nv, *config;
+ uint_t c, children = 0;
+ char *tmp_mntpt = NULL;
+ char *pool_mntpnt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ FILE *cap_fp = NULL;
+ FILE *zpool_cap_fp = NULL;
+ char line[BUFSIZ];
+ char cap_file[MAXPATHLEN];
+ char zpool_cap_file[MAXPATHLEN];
+ char stage1[MAXPATHLEN];
+ char stage2[MAXPATHLEN];
+ char installgrub_cmd[MAXPATHLEN];
+ char *vname;
+ char be_run_cmd_errbuf[BUFSIZ];
+ int ret = BE_SUCCESS;
+ int err = 0;
+ boolean_t be_mounted = B_FALSE;
+ boolean_t pool_mounted = B_FALSE;
+
+ if (!be_has_grub()) {
+ be_print_err(gettext("be_do_installgrub: Not supported "
+ "on this architecture\n"));
+ return (BE_ERR_NOTSUP);
+ }
+
+ if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_do_installgrub: failed to "
+ "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+ if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
+ if ((ret = _be_mount(bt->obe_name, &tmp_mntpt,
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_do_installgrub: failed to "
+ "mount BE (%s)\n"), bt->obe_name);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ be_mounted = B_TRUE;
+ }
+ ZFS_CLOSE(zhp);
+
+ (void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1);
+ (void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2);
+
+ if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
+ be_print_err(gettext("be_do_installgrub: failed to open "
+ "pool (%s): %s\n"), bt->obe_zpool,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ if (be_mounted)
+ (void) _be_unmount(bt->obe_name, 0);
+ free(tmp_mntpt);
+ return (ret);
+ }
+
+ if ((config = zpool_get_config(zphp, NULL)) == NULL) {
+ be_print_err(gettext("be_do_installgrub: failed to get zpool "
+ "configuration information. %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /*
+ * Get the vdev tree
+ */
+ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
+ be_print_err(gettext("be_do_installgrub: failed to get vdev "
+ "tree: %s\n"), libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
+ &children) != 0) {
+ be_print_err(gettext("be_do_installgrub: failed to traverse "
+ "the vdev tree: %s\n"), libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ for (c = 0; c < children; c++) {
+ uint_t i, nchildren = 0;
+ nvlist_t **nvchild;
+ vname = zpool_vdev_name(g_zfs, zphp, child[c], B_FALSE);
+ if (vname == NULL) {
+ be_print_err(gettext(
+ "be_do_installgrub: "
+ "failed to get device name: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') {
+
+ if (nvlist_lookup_nvlist_array(child[c],
+ ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) {
+ be_print_err(gettext("be_do_installgrub: "
+ "failed to traverse the vdev tree: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ for (i = 0; i < nchildren; i++) {
+ vname = zpool_vdev_name(g_zfs, zphp,
+ nvchild[i], B_FALSE);
+ if (vname == NULL) {
+ be_print_err(gettext(
+ "be_do_installgrub: "
+ "failed to get device name: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ (void) snprintf(installgrub_cmd,
+ sizeof (installgrub_cmd),
+ "%s %s %s /dev/rdsk/%s",
+ BE_INSTALL_GRUB, stage1, stage2, vname);
+ if (be_run_cmd(installgrub_cmd,
+ be_run_cmd_errbuf, BUFSIZ, NULL, 0) !=
+ BE_SUCCESS) {
+ be_print_err(gettext(
+ "be_do_installgrub: installgrub "
+ "failed for device %s.\n"), vname);
+ /* Assume localized cmd err output. */
+ be_print_err(gettext(
+ " Command: \"%s\"\n"),
+ installgrub_cmd);
+ be_print_err("%s", be_run_cmd_errbuf);
+ free(vname);
+ ret = BE_ERR_BOOTFILE_INST;
+ goto done;
+ }
+ free(vname);
+ }
+ } else {
+ (void) snprintf(installgrub_cmd,
+ sizeof (installgrub_cmd), "%s %s %s /dev/rdsk/%s",
+ BE_INSTALL_GRUB, stage1, stage2, vname);
+ if (be_run_cmd(installgrub_cmd, be_run_cmd_errbuf,
+ BUFSIZ, NULL, 0) != BE_SUCCESS) {
+ be_print_err(gettext(
+ "be_do_installgrub: installgrub "
+ "failed for device %s.\n"), vname);
+ /* Assume localized cmd err output. */
+ be_print_err(gettext(" Command: \"%s\"\n"),
+ installgrub_cmd);
+ be_print_err("%s", be_run_cmd_errbuf);
+ free(vname);
+ ret = BE_ERR_BOOTFILE_INST;
+ goto done;
+ }
+ free(vname);
+ }
+ }
+
+ /*
+ * Copy the grub capability file from the BE we're activating into
+ * the root pool.
+ */
+ (void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt,
+ BE_CAP_FILE);
+
+ if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_do_installgrub: zfs_open "
+ "failed: %s\n"), libzfs_error_description(g_zfs));
+ zpool_close(zphp);
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(zhp, &ptmp_mntpnt,
+ &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_do_installgrub: pool dataset "
+ "(%s) could not be mounted\n"), bt->obe_zpool);
+ ZFS_CLOSE(zhp);
+ zpool_close(zphp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
+ be_print_err(gettext("be_do_installgrub: pool "
+ "dataset (%s) is not mounted. Can't check the grub "
+ "version from the grub capability file.\n"), bt->obe_zpool);
+ ret = BE_ERR_NO_MENU;
+ goto done;
+ }
+
+ (void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s",
+ pool_mntpnt, BE_CAP_FILE);
+
+ free(pool_mntpnt);
+ pool_mntpnt = NULL;
+
+ if ((cap_fp = fopen(cap_file, "r")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_do_installgrub: failed to open grub "
+ "capability file\n"));
+ ret = errno_to_be_err(err);
+ goto done;
+ }
+ if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_do_installgrub: failed to open new "
+ "grub capability file\n"));
+ ret = errno_to_be_err(err);
+ (void) fclose(cap_fp);
+ goto done;
+ }
+
+ while (fgets(line, BUFSIZ, cap_fp)) {
+ (void) fputs(line, zpool_cap_fp);
+ }
+
+ (void) fclose(zpool_cap_fp);
+ (void) fclose(cap_fp);
+
+done:
+ if (pool_mounted) {
+ int iret = 0;
+ iret = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = iret;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(zhp);
+ if (be_mounted)
+ (void) _be_unmount(bt->obe_name, 0);
+ zpool_close(zphp);
+ free(tmp_mntpt);
+ return (ret);
+}
+
+/*
+ * Function: be_promote_zone_ds
+ * Description: This function finds the zones for the BE being activated
+ * and the active zonepath dataset for each zone. Then each
+ * active zonepath dataset is promoted.
+ *
+ * Parameters:
+ * be_name - the name of the global zone BE that we need to
+ * find the zones for.
+ * be_root_ds - the root dataset for be_name.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ *
+ * Scope:
+ * Private
+ */
+static int
+be_promote_zone_ds(char *be_name, char *be_root_ds)
+{
+ char *zone_ds = NULL;
+ char *temp_mntpt = NULL;
+ char origin[MAXPATHLEN];
+ char zoneroot_ds[MAXPATHLEN];
+ zfs_handle_t *zhp = NULL;
+ zfs_handle_t *z_zhp = NULL;
+ zoneList_t zone_list = NULL;
+ zoneBrandList_t *brands = NULL;
+ boolean_t be_mounted = B_FALSE;
+ int zone_index = 0;
+ int err = BE_SUCCESS;
+
+ /*
+ * Get the supported zone brands so we can pass that
+ * to z_get_nonglobal_zone_list_by_brand. Currently
+ * only the ipkg and labeled brand zones are supported
+ *
+ */
+ if ((brands = be_get_supported_brandlist()) == NULL) {
+ be_print_err(gettext("be_promote_zone_ds: no supported "
+ "brands\n"));
+ return (BE_SUCCESS);
+ }
+
+ if ((zhp = zfs_open(g_zfs, be_root_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_promote_zone_ds: Failed to open "
+ "dataset (%s): %s\n"), be_root_ds,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ z_free_brand_list(brands);
+ return (err);
+ }
+
+ if (!zfs_is_mounted(zhp, &temp_mntpt)) {
+ if ((err = _be_mount(be_name, &temp_mntpt,
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_promote_zone_ds: failed to "
+ "mount the BE for zones procesing.\n"));
+ ZFS_CLOSE(zhp);
+ z_free_brand_list(brands);
+ return (err);
+ }
+ be_mounted = B_TRUE;
+ }
+
+ /*
+ * Set the zone root to the temp mount point for the BE we just mounted.
+ */
+ z_set_zone_root(temp_mntpt);
+
+ /*
+ * Get all the zones based on the brands we're looking for. If no zones
+ * are found that we're interested in unmount the BE and move on.
+ */
+ if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
+ if (be_mounted)
+ (void) _be_unmount(be_name, 0);
+ ZFS_CLOSE(zhp);
+ z_free_brand_list(brands);
+ free(temp_mntpt);
+ return (BE_SUCCESS);
+ }
+ for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index)
+ != NULL; zone_index++) {
+ char *zone_path = NULL;
+
+ /* Skip zones that aren't at least installed */
+ if (z_zlist_get_current_state(zone_list, zone_index) <
+ ZONE_STATE_INSTALLED)
+ continue;
+
+ if (((zone_path =
+ z_zlist_get_zonepath(zone_list, zone_index)) == NULL) ||
+ ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) ||
+ !be_zone_supported(zone_ds))
+ continue;
+
+ if (be_find_active_zone_root(zhp, zone_ds,
+ zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
+ be_print_err(gettext("be_promote_zone_ds: "
+ "Zone does not have an active root "
+ "dataset, skipping this zone.\n"));
+ continue;
+ }
+
+ if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_promote_zone_ds: "
+ "Failed to open dataset "
+ "(%s): %s\n"), zoneroot_ds,
+ libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin,
+ sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) {
+ ZFS_CLOSE(z_zhp);
+ continue;
+ }
+
+ /*
+ * We don't need to close the zfs handle at this
+ * point because the callback funtion
+ * be_promote_ds_callback() will close it for us.
+ */
+ if (be_promote_ds_callback(z_zhp, NULL) != 0) {
+ be_print_err(gettext("be_promote_zone_ds: "
+ "failed to activate the "
+ "datasets for %s: %s\n"),
+ zoneroot_ds,
+ libzfs_error_description(g_zfs));
+ err = BE_ERR_PROMOTE;
+ goto done;
+ }
+ }
+done:
+ if (be_mounted)
+ (void) _be_unmount(be_name, 0);
+ ZFS_CLOSE(zhp);
+ free(temp_mntpt);
+ z_free_brand_list(brands);
+ z_free_zone_list(zone_list);
+ return (err);
+}
+
+/*
+ * Function: be_promote_ds_callback
+ * Description: This function is used to promote the datasets for the BE
+ * being activated as well as the datasets for the zones BE
+ * being activated.
+ *
+ * Parameters:
+ * zhp - the zfs handle for zone BE being activated.
+ * data - not used.
+ * Return:
+ * 0 - Success
+ * be_errno_t - Failure
+ *
+ * Scope:
+ * Private
+ */
+static int
+/* LINTED */
+be_promote_ds_callback(zfs_handle_t *zhp, void *data)
+{
+ char origin[MAXPATHLEN];
+ char *sub_dataset = NULL;
+ int ret = 0;
+
+ if (zhp != NULL) {
+ sub_dataset = strdup(zfs_get_name(zhp));
+ if (sub_dataset == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+ } else {
+ be_print_err(gettext("be_promote_ds_callback: "
+ "Invalid zfs handle passed into function\n"));
+ ret = BE_ERR_INVAL;
+ goto done;
+ }
+
+ /*
+ * This loop makes sure that we promote the dataset to the
+ * top of the tree so that it is no longer a decendent of any
+ * dataset. The ZFS close and then open is used to make sure that
+ * the promotion is updated before we move on.
+ */
+ while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
+ sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
+ if (zfs_promote(zhp) != 0) {
+ if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
+ be_print_err(gettext("be_promote_ds_callback: "
+ "promote of %s failed: %s\n"),
+ zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ } else {
+ /*
+ * If the call to zfs_promote returns the
+ * error EZFS_EXISTS we've hit a snapshot name
+ * collision. This means we're probably
+ * attemping to promote a zone dataset above a
+ * parent dataset that belongs to another zone
+ * which this zone was cloned from.
+ *
+ * TODO: If this is a zone dataset at some
+ * point we should skip this if the zone
+ * paths for the dataset and the snapshot
+ * don't match.
+ */
+ be_print_err(gettext("be_promote_ds_callback: "
+ "promote of %s failed due to snapshot "
+ "name collision: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ }
+ ZFS_CLOSE(zhp);
+ if ((zhp = zfs_open(g_zfs, sub_dataset,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_promote_ds_callback: "
+ "Failed to open dataset (%s): %s\n"), sub_dataset,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ }
+
+ /* Iterate down this dataset's children and promote them */
+ ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);
+
+done:
+ free(sub_dataset);
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
diff --git a/usr/src/lib/libbe/common/be_create.c b/usr/src/lib/libbe/common/be_create.c
new file mode 100644
index 0000000000..07548efa6c
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_create.c
@@ -0,0 +1,2995 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * System includes
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <libgen.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mnttab.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+/* Library wide variables */
+libzfs_handle_t *g_zfs = NULL;
+
+/* Private function prototypes */
+static int _be_destroy(const char *, be_destroy_data_t *);
+static int be_destroy_zones(char *, char *, be_destroy_data_t *);
+static int be_destroy_zone_roots(char *, be_destroy_data_t *);
+static int be_destroy_zone_roots_callback(zfs_handle_t *, void *);
+static int be_copy_zones(char *, char *, char *);
+static int be_clone_fs_callback(zfs_handle_t *, void *);
+static int be_destroy_callback(zfs_handle_t *, void *);
+static int be_send_fs_callback(zfs_handle_t *, void *);
+static int be_demote_callback(zfs_handle_t *, void *);
+static int be_demote_find_clone_callback(zfs_handle_t *, void *);
+static int be_demote_get_one_clone(zfs_handle_t *, void *);
+static int be_get_snap(char *, char **);
+static int be_prep_clone_send_fs(zfs_handle_t *, be_transaction_data_t *,
+ char *, int);
+static boolean_t be_create_container_ds(char *);
+static char *be_get_zone_be_name(char *root_ds, char *container_ds);
+static int be_zone_root_exists_callback(zfs_handle_t *, void *);
+
+/* ******************************************************************** */
+/* Public Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_init
+ * Description: Creates the initial datasets for a BE and leaves them
+ * unpopulated. The resultant BE can be mounted but can't
+ * yet be activated or booted.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attributes are used by this function:
+ *
+ * BE_ATTR_NEW_BE_NAME *required
+ * BE_ATTR_NEW_BE_POOL *required
+ * BE_ATTR_ZFS_PROPERTIES *optional
+ * BE_ATTR_FS_NAMES *optional
+ * BE_ATTR_FS_NUM *optional
+ * BE_ATTR_SHARED_FS_NAMES *optional
+ * BE_ATTR_SHARED_FS_NUM *optional
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_init(nvlist_t *be_attrs)
+{
+ be_transaction_data_t bt = { 0 };
+ zpool_handle_t *zlp;
+ nvlist_t *zfs_props = NULL;
+ char nbe_root_ds[MAXPATHLEN];
+ char child_fs[MAXPATHLEN];
+ char **fs_names = NULL;
+ char **shared_fs_names = NULL;
+ uint16_t fs_num = 0;
+ uint16_t shared_fs_num = 0;
+ int nelem;
+ int i;
+ int zret = 0, ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get new BE name */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME, &bt.nbe_name)
+ != 0) {
+ be_print_err(gettext("be_init: failed to lookup "
+ "BE_ATTR_NEW_BE_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Validate new BE name */
+ if (!be_valid_be_name(bt.nbe_name)) {
+ be_print_err(gettext("be_init: invalid BE name %s\n"),
+ bt.nbe_name);
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get zpool name */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_POOL, &bt.nbe_zpool)
+ != 0) {
+ be_print_err(gettext("be_init: failed to lookup "
+ "BE_ATTR_NEW_BE_POOL attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get file system attributes */
+ nelem = 0;
+ if (nvlist_lookup_pairs(be_attrs, 0,
+ BE_ATTR_FS_NUM, DATA_TYPE_UINT16, &fs_num,
+ BE_ATTR_FS_NAMES, DATA_TYPE_STRING_ARRAY, &fs_names, &nelem,
+ NULL) != 0) {
+ be_print_err(gettext("be_init: failed to lookup fs "
+ "attributes\n"));
+ return (BE_ERR_INVAL);
+ }
+ if (nelem != fs_num) {
+ be_print_err(gettext("be_init: size of FS_NAMES array (%d) "
+ "does not match FS_NUM (%d)\n"), nelem, fs_num);
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get shared file system attributes */
+ nelem = 0;
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_SHARED_FS_NUM, DATA_TYPE_UINT16, &shared_fs_num,
+ BE_ATTR_SHARED_FS_NAMES, DATA_TYPE_STRING_ARRAY, &shared_fs_names,
+ &nelem, NULL) != 0) {
+ be_print_err(gettext("be_init: failed to lookup "
+ "shared fs attributes\n"));
+ return (BE_ERR_INVAL);
+ }
+ if (nelem != shared_fs_num) {
+ be_print_err(gettext("be_init: size of SHARED_FS_NAMES "
+ "array does not match SHARED_FS_NUM\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Verify that nbe_zpool exists */
+ if ((zlp = zpool_open(g_zfs, bt.nbe_zpool)) == NULL) {
+ be_print_err(gettext("be_init: failed to "
+ "find existing zpool (%s): %s\n"), bt.nbe_zpool,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+ zpool_close(zlp);
+
+ /*
+ * Verify BE container dataset in nbe_zpool exists.
+ * If not, create it.
+ */
+ if (!be_create_container_ds(bt.nbe_zpool))
+ return (BE_ERR_CREATDS);
+
+ /*
+ * Verify that nbe_name doesn't already exist in some pool.
+ */
+ if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name)) > 0) {
+ be_print_err(gettext("be_init: BE (%s) already exists\n"),
+ bt.nbe_name);
+ return (BE_ERR_BE_EXISTS);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_init: zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Generate string for BE's root dataset */
+ be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds,
+ sizeof (nbe_root_ds));
+
+ /*
+ * Create property list for new BE root dataset. If some
+ * zfs properties were already provided by the caller, dup
+ * that list. Otherwise initialize a new property list.
+ */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL)
+ != 0) {
+ be_print_err(gettext("be_init: failed to lookup "
+ "BE_ATTR_ZFS_PROPERTIES attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+ if (zfs_props != NULL) {
+ /* Make sure its a unique nvlist */
+ if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) &&
+ !(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) {
+ be_print_err(gettext("be_init: ZFS property list "
+ "not unique\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Dup the list */
+ if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) {
+ be_print_err(gettext("be_init: failed to dup ZFS "
+ "property list\n"));
+ return (BE_ERR_NOMEM);
+ }
+ } else {
+ /* Initialize new nvlist */
+ if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) {
+ be_print_err(gettext("be_init: internal "
+ "error: out of memory\n"));
+ return (BE_ERR_NOMEM);
+ }
+ }
+
+ /* Set the mountpoint property for the root dataset */
+ if (nvlist_add_string(bt.nbe_zfs_props,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), "/") != 0) {
+ be_print_err(gettext("be_init: internal error "
+ "out of memory\n"));
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+
+ /* Set the 'canmount' property */
+ if (nvlist_add_string(bt.nbe_zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) {
+ be_print_err(gettext("be_init: internal error "
+ "out of memory\n"));
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+
+ /* Create BE root dataset for the new BE */
+ if (zfs_create(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM,
+ bt.nbe_zfs_props) != 0) {
+ be_print_err(gettext("be_init: failed to "
+ "create BE root dataset (%s): %s\n"), nbe_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* Set UUID for new BE */
+ if ((ret = be_set_uuid(nbe_root_ds)) != BE_SUCCESS) {
+ be_print_err(gettext("be_init: failed to "
+ "set uuid for new BE\n"));
+ }
+
+ /*
+ * Clear the mountpoint property so that the non-shared
+ * file systems created below inherit their mountpoints.
+ */
+ (void) nvlist_remove(bt.nbe_zfs_props,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), DATA_TYPE_STRING);
+
+ /* Create the new BE's non-shared file systems */
+ for (i = 0; i < fs_num && fs_names[i]; i++) {
+ /*
+ * If fs == "/", skip it;
+ * we already created the root dataset
+ */
+ if (strcmp(fs_names[i], "/") == 0)
+ continue;
+
+ /* Generate string for file system */
+ (void) snprintf(child_fs, sizeof (child_fs), "%s%s",
+ nbe_root_ds, fs_names[i]);
+
+ /* Create file system */
+ if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM,
+ bt.nbe_zfs_props) != 0) {
+ be_print_err(gettext("be_init: failed to create "
+ "BE's child dataset (%s): %s\n"), child_fs,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ }
+
+ /* Create the new BE's shared file systems */
+ if (shared_fs_num > 0) {
+ nvlist_t *props = NULL;
+
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ be_print_err(gettext("be_init: nvlist_alloc failed\n"));
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < shared_fs_num; i++) {
+ /* Generate string for shared file system */
+ (void) snprintf(child_fs, sizeof (child_fs), "%s%s",
+ bt.nbe_zpool, shared_fs_names[i]);
+
+ if (nvlist_add_string(props,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ shared_fs_names[i]) != 0) {
+ be_print_err(gettext("be_init: "
+ "internal error: out of memory\n"));
+ nvlist_free(props);
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+
+ /* Create file system if it doesn't already exist */
+ if (zfs_dataset_exists(g_zfs, child_fs,
+ ZFS_TYPE_FILESYSTEM)) {
+ continue;
+ }
+ if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM,
+ props) != 0) {
+ be_print_err(gettext("be_init: failed to "
+ "create BE's shared dataset (%s): %s\n"),
+ child_fs, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ nvlist_free(props);
+ goto done;
+ }
+ }
+
+ nvlist_free(props);
+ }
+
+done:
+ if (bt.nbe_zfs_props != NULL)
+ nvlist_free(bt.nbe_zfs_props);
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/*
+ * Function: be_destroy
+ * Description: Destroy a BE and all of its children datasets, snapshots and
+ * zones that belong to the parent BE.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attributes are used by this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *required
+ * BE_ATTR_DESTROY_FLAGS *optional
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_destroy(nvlist_t *be_attrs)
+{
+ zfs_handle_t *zhp = NULL;
+ be_transaction_data_t bt = { 0 };
+ be_transaction_data_t cur_bt = { 0 };
+ be_destroy_data_t dd = { 0 };
+ int ret = BE_SUCCESS;
+ uint16_t flags = 0;
+ int zret;
+ char obe_root_ds[MAXPATHLEN];
+ char *mp = NULL;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get name of BE to delete */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &bt.obe_name)
+ != 0) {
+ be_print_err(gettext("be_destroy: failed to lookup "
+ "BE_ATTR_ORIG_BE_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /*
+ * Validate BE name. If valid, then check that the original BE is not
+ * the active BE. If it is the 'active' BE then return an error code
+ * since we can't destroy the active BE.
+ */
+ if (!be_valid_be_name(bt.obe_name)) {
+ be_print_err(gettext("be_destroy: invalid BE name %s\n"),
+ bt.obe_name);
+ return (BE_ERR_INVAL);
+ } else if (bt.obe_name != NULL) {
+ if ((ret = be_find_current_be(&cur_bt)) != BE_SUCCESS) {
+ return (ret);
+ }
+ if (strcmp(cur_bt.obe_name, bt.obe_name) == 0) {
+ return (BE_ERR_DESTROY_CURR_BE);
+ }
+ }
+
+ /* Get destroy flags if provided */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_DESTROY_FLAGS, DATA_TYPE_UINT16, &flags, NULL)
+ != 0) {
+ be_print_err(gettext("be_destroy: failed to lookup "
+ "BE_ATTR_DESTROY_FLAGS attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ dd.destroy_snaps = flags & BE_DESTROY_FLAG_SNAPSHOTS;
+ dd.force_unmount = flags & BE_DESTROY_FLAG_FORCE_UNMOUNT;
+
+ /* Find which zpool obe_name lives in */
+ if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
+ be_print_err(gettext("be_destroy: failed to find zpool "
+ "for BE (%s)\n"), bt.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_destroy: zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Generate string for obe_name's root dataset */
+ be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
+ sizeof (obe_root_ds));
+ bt.obe_root_ds = obe_root_ds;
+
+ /*
+ * Detect if the BE to destroy has the 'active on boot' property set.
+ * If so, set the 'active on boot' property on the the 'active' BE.
+ */
+ if (be_is_active_on_boot(bt.obe_name)) {
+ if ((ret = be_activate_current_be()) != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy: failed to "
+ "make the current BE 'active on boot'\n"));
+ return (ret);
+ }
+ }
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_destroy: failed to "
+ "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Get the UUID of the global BE */
+ if (be_get_uuid(zfs_get_name(zhp), &dd.gz_be_uuid) != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy: BE has no UUID (%s)\n"),
+ zfs_get_name(zhp));
+ }
+
+ /*
+ * If the global BE is mounted, make sure we've been given the
+ * flag to forcibly unmount it.
+ */
+ if (zfs_is_mounted(zhp, &mp)) {
+ if (!(dd.force_unmount)) {
+ be_print_err(gettext("be_destroy: "
+ "%s is currently mounted at %s, cannot destroy\n"),
+ bt.obe_name, mp != NULL ? mp : "<unknown>");
+
+ free(mp);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_MOUNTED);
+ }
+ free(mp);
+ }
+
+ /*
+ * Destroy the non-global zone BE's if we are in the global zone
+ * and there is a UUID associated with the global zone BE
+ */
+ if (getzoneid() == GLOBAL_ZONEID && !uuid_is_null(dd.gz_be_uuid)) {
+ if ((ret = be_destroy_zones(bt.obe_name, bt.obe_root_ds, &dd))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy: failed to "
+ "destroy one or more zones for BE %s\n"),
+ bt.obe_name);
+ goto done;
+ }
+ }
+
+ /* Unmount the BE if it was mounted */
+ if (zfs_is_mounted(zhp, NULL)) {
+ if ((ret = _be_unmount(bt.obe_name, BE_UNMOUNT_FLAG_FORCE))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy: "
+ "failed to unmount %s\n"), bt.obe_name);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+ ZFS_CLOSE(zhp);
+
+ /* Destroy this BE */
+ if ((ret = _be_destroy((const char *)bt.obe_root_ds, &dd))
+ != BE_SUCCESS) {
+ goto done;
+ }
+
+ /* Remove BE's entry from the boot menu */
+ if (getzoneid() == GLOBAL_ZONEID) {
+ if ((ret = be_remove_menu(bt.obe_name, bt.obe_zpool, NULL))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy: failed to "
+ "remove BE %s from the boot menu\n"),
+ bt.obe_root_ds);
+ goto done;
+ }
+ }
+
+done:
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/*
+ * Function: be_copy
+ * Description: This function makes a copy of an existing BE. If the original
+ * BE and the new BE are in the same pool, it uses zfs cloning to
+ * create the new BE, otherwise it does a physical copy.
+ * If the original BE name isn't provided, it uses the currently
+ * booted BE. If the new BE name isn't provided, it creates an
+ * auto named BE and returns that name to the caller.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attributes are used by this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *optional
+ * BE_ATTR_SNAP_NAME *optional
+ * BE_ATTR_NEW_BE_NAME *optional
+ * BE_ATTR_NEW_BE_POOL *optional
+ * BE_ATTR_NEW_BE_DESC *optional
+ * BE_ATTR_ZFS_PROPERTIES *optional
+ * BE_ATTR_POLICY *optional
+ *
+ * If the BE_ATTR_NEW_BE_NAME was not passed in, upon
+ * successful BE creation, the following attribute values
+ * will be returned to the caller by setting them in the
+ * be_attrs parameter passed in:
+ *
+ * BE_ATTR_SNAP_NAME
+ * BE_ATTR_NEW_BE_NAME
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_copy(nvlist_t *be_attrs)
+{
+ be_transaction_data_t bt = { 0 };
+ be_fs_list_data_t fld = { 0 };
+ zfs_handle_t *zhp = NULL;
+ nvlist_t *zfs_props = NULL;
+ uuid_t uu = { 0 };
+ char obe_root_ds[MAXPATHLEN];
+ char nbe_root_ds[MAXPATHLEN];
+ char ss[MAXPATHLEN];
+ char *new_mp = NULL;
+ boolean_t autoname = B_FALSE;
+ boolean_t be_created = B_FALSE;
+ int i;
+ int zret;
+ int ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get original BE name */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &bt.obe_name, NULL) != 0) {
+ be_print_err(gettext("be_copy: failed to lookup "
+ "BE_ATTR_ORIG_BE_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* If original BE name not provided, use current BE */
+ if (bt.obe_name == NULL) {
+ if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
+ return (ret);
+ }
+ } else {
+ /* Validate original BE name */
+ if (!be_valid_be_name(bt.obe_name)) {
+ be_print_err(gettext("be_copy: "
+ "invalid BE name %s\n"), bt.obe_name);
+ return (BE_ERR_INVAL);
+ }
+ }
+
+ /* Find which zpool obe_name lives in */
+ if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
+ be_print_err(gettext("be_copy: failed to "
+ "find zpool for BE (%s)\n"), bt.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_copy: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Get snapshot name of original BE if one was provided */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &bt.obe_snap_name, NULL)
+ != 0) {
+ be_print_err(gettext("be_copy: failed to lookup "
+ "BE_ATTR_SNAP_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get new BE name */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_NEW_BE_NAME, DATA_TYPE_STRING, &bt.nbe_name, NULL)
+ != 0) {
+ be_print_err(gettext("be_copy: failed to lookup "
+ "BE_ATTR_NEW_BE_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get zpool name to create new BE in */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_NEW_BE_POOL, DATA_TYPE_STRING, &bt.nbe_zpool, NULL) != 0) {
+ be_print_err(gettext("be_copy: failed to lookup "
+ "BE_ATTR_NEW_BE_POOL attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get new BE's description if one was provided */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_NEW_BE_DESC, DATA_TYPE_STRING, &bt.nbe_desc, NULL) != 0) {
+ be_print_err(gettext("be_copy: failed to lookup "
+ "BE_ATTR_NEW_BE_DESC attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get BE policy to create this snapshot under */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_POLICY, DATA_TYPE_STRING, &bt.policy, NULL) != 0) {
+ be_print_err(gettext("be_copy: failed to lookup "
+ "BE_ATTR_POLICY attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /*
+ * Create property list for new BE root dataset. If some
+ * zfs properties were already provided by the caller, dup
+ * that list. Otherwise initialize a new property list.
+ */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL)
+ != 0) {
+ be_print_err(gettext("be_copy: failed to lookup "
+ "BE_ATTR_ZFS_PROPERTIES attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+ if (zfs_props != NULL) {
+ /* Make sure its a unique nvlist */
+ if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) &&
+ !(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) {
+ be_print_err(gettext("be_copy: ZFS property list "
+ "not unique\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Dup the list */
+ if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) {
+ be_print_err(gettext("be_copy: "
+ "failed to dup ZFS property list\n"));
+ return (BE_ERR_NOMEM);
+ }
+ } else {
+ /* Initialize new nvlist */
+ if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) {
+ be_print_err(gettext("be_copy: internal "
+ "error: out of memory\n"));
+ return (BE_ERR_NOMEM);
+ }
+ }
+
+ /*
+ * If new BE name provided, validate the BE name and then verify
+ * that new BE name doesn't already exist in some pool.
+ */
+ if (bt.nbe_name) {
+ /* Validate original BE name */
+ if (!be_valid_be_name(bt.nbe_name)) {
+ be_print_err(gettext("be_copy: "
+ "invalid BE name %s\n"), bt.nbe_name);
+ ret = BE_ERR_INVAL;
+ goto done;
+ }
+
+ /* Verify it doesn't already exist */
+ if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name))
+ > 0) {
+ be_print_err(gettext("be_copy: BE (%s) already "
+ "exists\n"), bt.nbe_name);
+ ret = BE_ERR_BE_EXISTS;
+ goto done;
+ } else if (zret < 0) {
+ be_print_err(gettext("be_copy: zpool_iter failed: "
+ "%s\n"), libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ } else {
+ /*
+ * If an auto named BE is desired, it must be in the same
+ * pool is the original BE.
+ */
+ if (bt.nbe_zpool != NULL) {
+ be_print_err(gettext("be_copy: cannot specify pool "
+ "name when creating an auto named BE\n"));
+ ret = BE_ERR_INVAL;
+ goto done;
+ }
+
+ /*
+ * Generate auto named BE
+ */
+ if ((bt.nbe_name = be_auto_be_name(bt.obe_name))
+ == NULL) {
+ be_print_err(gettext("be_copy: "
+ "failed to generate auto BE name\n"));
+ ret = BE_ERR_AUTONAME;
+ goto done;
+ }
+
+ autoname = B_TRUE;
+ }
+
+ /*
+ * If zpool name to create new BE in is not provided,
+ * create new BE in original BE's pool.
+ */
+ if (bt.nbe_zpool == NULL) {
+ bt.nbe_zpool = bt.obe_zpool;
+ }
+
+ /* Get root dataset names for obe_name and nbe_name */
+ be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
+ sizeof (obe_root_ds));
+ be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds,
+ sizeof (nbe_root_ds));
+
+ bt.obe_root_ds = obe_root_ds;
+ bt.nbe_root_ds = nbe_root_ds;
+
+ /*
+ * If an existing snapshot name has been provided to create from,
+ * verify that it exists for the original BE's root dataset.
+ */
+ if (bt.obe_snap_name != NULL) {
+
+ /* Generate dataset name for snapshot to use. */
+ (void) snprintf(ss, sizeof (ss), "%s@%s", bt.obe_root_ds,
+ bt.obe_snap_name);
+
+ /* Verify snapshot exists */
+ if (!zfs_dataset_exists(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) {
+ be_print_err(gettext("be_copy: "
+ "snapshot does not exist (%s): %s\n"), ss,
+ libzfs_error_description(g_zfs));
+ ret = BE_ERR_SS_NOENT;
+ goto done;
+ }
+ } else {
+ /*
+ * Else snapshot name was not provided, generate an
+ * auto named snapshot to use as its origin.
+ */
+ if ((ret = _be_create_snapshot(bt.obe_name,
+ &bt.obe_snap_name, bt.policy)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy: "
+ "failed to create auto named snapshot\n"));
+ goto done;
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME,
+ bt.obe_snap_name) != 0) {
+ be_print_err(gettext("be_copy: "
+ "failed to add snap name to be_attrs\n"));
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+ }
+
+ /* Get handle to original BE's root dataset. */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_copy: failed to "
+ "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* If original BE is currently mounted, record its altroot. */
+ if (zfs_is_mounted(zhp, &bt.obe_altroot) && bt.obe_altroot == NULL) {
+ be_print_err(gettext("be_copy: failed to "
+ "get altroot of mounted BE %s: %s\n"),
+ bt.obe_name, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (strcmp(bt.obe_zpool, bt.nbe_zpool) == 0) {
+
+ /* Do clone */
+
+ /*
+ * Iterate through original BE's datasets and clone
+ * them to create new BE. This call will end up closing
+ * the zfs handle passed in whether it succeeds for fails.
+ */
+ if ((ret = be_clone_fs_callback(zhp, &bt)) != 0) {
+ zhp = NULL;
+ /* Creating clone BE failed */
+ if (!autoname || ret != BE_ERR_BE_EXISTS) {
+ be_print_err(gettext("be_copy: "
+ "failed to clone new BE (%s) from "
+ "orig BE (%s)\n"),
+ bt.nbe_name, bt.obe_name);
+ ret = BE_ERR_CLONE;
+ goto done;
+ }
+
+ /*
+ * We failed to create the new BE because a BE with
+ * the auto-name we generated above has since come
+ * into existence. Regenerate a new auto-name
+ * and retry.
+ */
+ for (i = 1; i < BE_AUTO_NAME_MAX_TRY; i++) {
+
+ /* Sleep 1 before retrying */
+ (void) sleep(1);
+
+ /* Generate new auto BE name */
+ free(bt.nbe_name);
+ if ((bt.nbe_name = be_auto_be_name(bt.obe_name))
+ == NULL) {
+ be_print_err(gettext("be_copy: "
+ "failed to generate auto "
+ "BE name\n"));
+ ret = BE_ERR_AUTONAME;
+ goto done;
+ }
+
+ /*
+ * Regenerate string for new BE's
+ * root dataset name
+ */
+ be_make_root_ds(bt.nbe_zpool, bt.nbe_name,
+ nbe_root_ds, sizeof (nbe_root_ds));
+ bt.nbe_root_ds = nbe_root_ds;
+
+ /*
+ * Get handle to original BE's root dataset.
+ */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_copy: "
+ "failed to open BE root dataset "
+ "(%s): %s\n"), bt.obe_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /*
+ * Try to clone the BE again. This
+ * call will end up closing the zfs
+ * handle passed in whether it
+ * succeeds or fails.
+ */
+ ret = be_clone_fs_callback(zhp, &bt);
+ zhp = NULL;
+ if (ret == 0) {
+ break;
+ } else if (ret != BE_ERR_BE_EXISTS) {
+ be_print_err(gettext("be_copy: "
+ "failed to clone new BE "
+ "(%s) from orig BE (%s)\n"),
+ bt.nbe_name, bt.obe_name);
+ ret = BE_ERR_CLONE;
+ goto done;
+ }
+ }
+
+ /*
+ * If we've exhausted the maximum number of
+ * tries, free the auto BE name and return
+ * error.
+ */
+ if (i == BE_AUTO_NAME_MAX_TRY) {
+ be_print_err(gettext("be_copy: failed "
+ "to create unique auto BE name\n"));
+ free(bt.nbe_name);
+ bt.nbe_name = NULL;
+ ret = BE_ERR_AUTONAME;
+ goto done;
+ }
+ }
+ zhp = NULL;
+
+ } else {
+
+ /* Do copy (i.e. send BE datasets via zfs_send/recv) */
+
+ /*
+ * Verify BE container dataset in nbe_zpool exists.
+ * If not, create it.
+ */
+ if (!be_create_container_ds(bt.nbe_zpool)) {
+ ret = BE_ERR_CREATDS;
+ goto done;
+ }
+
+ /*
+ * Iterate through original BE's datasets and send
+ * them to the other pool. This call will end up closing
+ * the zfs handle passed in whether it succeeds or fails.
+ */
+ if ((ret = be_send_fs_callback(zhp, &bt)) != 0) {
+ be_print_err(gettext("be_copy: failed to "
+ "send BE (%s) to pool (%s)\n"), bt.obe_name,
+ bt.nbe_zpool);
+ ret = BE_ERR_COPY;
+ zhp = NULL;
+ goto done;
+ }
+ zhp = NULL;
+ }
+
+ /*
+ * Set flag to note that the dataset(s) for the new BE have been
+ * successfully created so that if a failure happens from this point
+ * on, we know to cleanup these datasets.
+ */
+ be_created = B_TRUE;
+
+ /*
+ * Validate that the new BE is mountable.
+ * Do not attempt to mount non-global zone datasets
+ * since they are not cloned yet.
+ */
+ if ((ret = _be_mount(bt.nbe_name, &new_mp, BE_MOUNT_FLAG_NO_ZONES))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_copy: failed to "
+ "mount newly created BE\n"));
+ (void) _be_unmount(bt.nbe_name, 0);
+ goto done;
+ }
+
+ /* Set UUID for new BE */
+ if (be_set_uuid(bt.nbe_root_ds) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy: failed to "
+ "set uuid for new BE\n"));
+ }
+
+ /*
+ * Process zones outside of the private BE namespace.
+ * This has to be done here because we need the uuid set in the
+ * root dataset of the new BE. The uuid is use to set the parentbe
+ * property for the new zones datasets.
+ */
+ if (getzoneid() == GLOBAL_ZONEID &&
+ be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
+ if ((ret = be_copy_zones(bt.obe_name, bt.obe_root_ds,
+ bt.nbe_root_ds)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy: failed to process "
+ "zones\n"));
+ goto done;
+ }
+ }
+
+ /*
+ * Generate a list of file systems from the original BE that are
+ * legacy mounted. We use this list to determine which entries in
+ * vfstab we need to update for the new BE we've just created.
+ */
+ if ((ret = be_get_legacy_fs(bt.obe_name, bt.obe_root_ds, NULL, NULL,
+ &fld)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy: failed to "
+ "get legacy mounted file system list for %s\n"),
+ bt.obe_name);
+ goto done;
+ }
+
+ /*
+ * Update new BE's vfstab.
+ */
+ if ((ret = be_update_vfstab(bt.nbe_name, bt.obe_zpool, bt.nbe_zpool,
+ &fld, new_mp)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy: failed to "
+ "update new BE's vfstab (%s)\n"), bt.nbe_name);
+ goto done;
+ }
+
+ /* Unmount the new BE */
+ if ((ret = _be_unmount(bt.nbe_name, 0)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy: failed to "
+ "unmount newly created BE\n"));
+ goto done;
+ }
+
+ /*
+ * Add boot menu entry for newly created clone
+ */
+ if (getzoneid() == GLOBAL_ZONEID &&
+ (ret = be_append_menu(bt.nbe_name, bt.nbe_zpool,
+ NULL, bt.obe_root_ds, bt.nbe_desc)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy: failed to "
+ "add BE (%s) to boot menu\n"), bt.nbe_name);
+ goto done;
+ }
+
+ /*
+ * If we succeeded in creating an auto named BE, set its policy
+ * type and return the auto generated name to the caller by storing
+ * it in the nvlist passed in by the caller.
+ */
+ if (autoname) {
+ /* Get handle to new BE's root dataset. */
+ if ((zhp = zfs_open(g_zfs, bt.nbe_root_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_copy: failed to "
+ "open BE root dataset (%s): %s\n"), bt.nbe_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /*
+ * Set the policy type property into the new BE's root dataset
+ */
+ if (bt.policy == NULL) {
+ /* If no policy type provided, use default type */
+ bt.policy = be_default_policy();
+ }
+
+ if (zfs_prop_set(zhp, BE_POLICY_PROPERTY, bt.policy) != 0) {
+ be_print_err(gettext("be_copy: failed to "
+ "set BE policy for %s: %s\n"), bt.nbe_name,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /*
+ * Return the auto generated name to the caller
+ */
+ if (bt.nbe_name) {
+ if (nvlist_add_string(be_attrs, BE_ATTR_NEW_BE_NAME,
+ bt.nbe_name) != 0) {
+ be_print_err(gettext("be_copy: failed to "
+ "add snap name to be_attrs\n"));
+ }
+ }
+ }
+
+done:
+ ZFS_CLOSE(zhp);
+ be_free_fs_list(&fld);
+
+ if (bt.nbe_zfs_props != NULL)
+ nvlist_free(bt.nbe_zfs_props);
+
+ free(bt.obe_altroot);
+ free(new_mp);
+
+ /*
+ * If a failure occurred and we already created the datasets for
+ * the new boot environment, destroy them.
+ */
+ if (ret != BE_SUCCESS && be_created) {
+ be_destroy_data_t cdd = { 0 };
+
+ cdd.force_unmount = B_TRUE;
+
+ be_print_err(gettext("be_copy: "
+ "destroying partially created boot environment\n"));
+
+ if (getzoneid() == GLOBAL_ZONEID && be_get_uuid(bt.nbe_root_ds,
+ &cdd.gz_be_uuid) == 0)
+ (void) be_destroy_zones(bt.nbe_name, bt.nbe_root_ds,
+ &cdd);
+
+ (void) _be_destroy(bt.nbe_root_ds, &cdd);
+ }
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/* ******************************************************************** */
+/* Semi-Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_find_zpool_callback
+ * Description: Callback function used to find the pool that a BE lives in.
+ * Parameters:
+ * zlp - zpool_handle_t pointer for the current pool being
+ * looked at.
+ * data - be_transaction_data_t pointer providing information
+ * about the BE that's being searched for.
+ * This function uses the obe_name member of this
+ * parameter to use as the BE name to search for.
+ * Upon successfully locating the BE, it populates
+ * obe_zpool with the pool name that the BE is found in.
+ * Returns:
+ * 1 - BE exists in this pool.
+ * 0 - BE does not exist in this pool.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_find_zpool_callback(zpool_handle_t *zlp, void *data)
+{
+ be_transaction_data_t *bt = data;
+ const char *zpool = zpool_get_name(zlp);
+ char be_root_ds[MAXPATHLEN];
+
+ /*
+ * Generate string for the BE's root dataset
+ */
+ be_make_root_ds(zpool, bt->obe_name, be_root_ds, sizeof (be_root_ds));
+
+ /*
+ * Check if dataset exists
+ */
+ if (zfs_dataset_exists(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) {
+ /* BE's root dataset exists in zpool */
+ bt->obe_zpool = strdup(zpool);
+ zpool_close(zlp);
+ return (1);
+ }
+
+ zpool_close(zlp);
+ return (0);
+}
+
+/*
+ * Function: be_exists_callback
+ * Description: Callback function used to find out if a BE exists.
+ * Parameters:
+ * zlp - zpool_handle_t pointer to the current pool being
+ * looked at.
+ * data - BE name to look for.
+ * Return:
+ * 1 - BE exists in this pool.
+ * 0 - BE does not exist in this pool.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_exists_callback(zpool_handle_t *zlp, void *data)
+{
+ const char *zpool = zpool_get_name(zlp);
+ char *be_name = data;
+ char be_root_ds[MAXPATHLEN];
+
+ /*
+ * Generate string for the BE's root dataset
+ */
+ be_make_root_ds(zpool, be_name, be_root_ds, sizeof (be_root_ds));
+
+ /*
+ * Check if dataset exists
+ */
+ if (zfs_dataset_exists(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) {
+ /* BE's root dataset exists in zpool */
+ zpool_close(zlp);
+ return (1);
+ }
+
+ zpool_close(zlp);
+ return (0);
+}
+
+/*
+ * Function: be_set_uuid
+ * Description: This function generates a uuid, unparses it into
+ * string representation, and sets that string into
+ * a zfs user property for a root dataset of a BE.
+ * The name of the user property used to store the
+ * uuid is org.opensolaris.libbe:uuid
+ *
+ * Parameters:
+ * root_ds - Root dataset of the BE to set a uuid on.
+ * Return:
+ * be_errno_t - Failure
+ * BE_SUCCESS - Success
+ * Scope:
+ * Semi-private (library wide ues only)
+ */
+int
+be_set_uuid(char *root_ds)
+{
+ zfs_handle_t *zhp = NULL;
+ uuid_t uu = { 0 };
+ char uu_string[UUID_PRINTABLE_STRING_LENGTH] = { 0 };
+ int ret = BE_SUCCESS;
+
+ /* Generate a UUID and unparse it into string form */
+ uuid_generate(uu);
+ if (uuid_is_null(uu) != 0) {
+ be_print_err(gettext("be_set_uuid: failed to "
+ "generate uuid\n"));
+ return (BE_ERR_GEN_UUID);
+ }
+ uuid_unparse(uu, uu_string);
+
+ /* Get handle to the BE's root dataset. */
+ if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_set_uuid: failed to "
+ "open BE root dataset (%s): %s\n"), root_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Set uuid property for the BE */
+ if (zfs_prop_set(zhp, BE_UUID_PROPERTY, uu_string) != 0) {
+ be_print_err(gettext("be_set_uuid: failed to "
+ "set uuid property for BE: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ }
+
+ ZFS_CLOSE(zhp);
+
+ return (ret);
+}
+
+/*
+ * Function: be_get_uuid
+ * Description: This function gets the uuid string from a BE root
+ * dataset, parses it into internal format, and returns
+ * it the caller via a reference pointer passed in.
+ *
+ * Parameters:
+ * rootds - Root dataset of the BE to get the uuid from.
+ * uu - reference pointer to a uuid_t to return uuid in.
+ * Return:
+ * be_errno_t - Failure
+ * BE_SUCCESS - Success
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_get_uuid(const char *root_ds, uuid_t *uu)
+{
+ zfs_handle_t *zhp = NULL;
+ nvlist_t *userprops = NULL;
+ nvlist_t *propname = NULL;
+ char *uu_string = NULL;
+ int ret = BE_SUCCESS;
+
+ /* Get handle to the BE's root dataset. */
+ if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_get_uuid: failed to "
+ "open BE root dataset (%s): %s\n"), root_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Get user properties for BE's root dataset */
+ if ((userprops = zfs_get_user_props(zhp)) == NULL) {
+ be_print_err(gettext("be_get_uuid: failed to "
+ "get user properties for BE root dataset (%s): %s\n"),
+ root_ds, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* Get UUID string from BE's root dataset user properties */
+ if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, &propname) != 0 ||
+ nvlist_lookup_string(propname, ZPROP_VALUE, &uu_string) != 0) {
+ /*
+ * This probably just means that the BE is simply too old
+ * to have a uuid or that we haven't created a uuid for
+ * this BE yet.
+ */
+ be_print_err(gettext("be_get_uuid: failed to "
+ "get uuid property from BE root dataset user "
+ "properties.\n"));
+ ret = BE_ERR_NO_UUID;
+ goto done;
+ }
+ /* Parse uuid string into internal format */
+ if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) {
+ be_print_err(gettext("be_get_uuid: failed to "
+ "parse uuid\n"));
+ ret = BE_ERR_PARSE_UUID;
+ goto done;
+ }
+
+done:
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/* ******************************************************************** */
+/* Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: _be_destroy
+ * Description: Destroy a BE and all of its children datasets and snapshots.
+ * This function is called for both global BEs and non-global BEs.
+ * The root dataset of either the global BE or non-global BE to be
+ * destroyed is passed in.
+ * Parameters:
+ * root_ds - pointer to the name of the root dataset of the
+ * BE to destroy.
+ * dd - pointer to a be_destroy_data_t structure.
+ *
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+_be_destroy(const char *root_ds, be_destroy_data_t *dd)
+{
+ zfs_handle_t *zhp = NULL;
+ char origin[MAXPATHLEN];
+ char parent[MAXPATHLEN];
+ char *snap = NULL;
+ boolean_t has_origin = B_FALSE;
+ int ret = BE_SUCCESS;
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_destroy: failed to "
+ "open BE root dataset (%s): %s\n"), root_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Demote this BE in case it has dependent clones. This call
+ * will end up closing the zfs handle passed in whether it
+ * succeeds or fails.
+ */
+ if (be_demote_callback(zhp, NULL) != 0) {
+ be_print_err(gettext("be_destroy: "
+ "failed to demote BE %s\n"), root_ds);
+ return (BE_ERR_DEMOTE);
+ }
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_destroy: failed to "
+ "open BE root dataset (%s): %s\n"), root_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Get the origin of this BE's root dataset. This will be used
+ * later to destroy the snapshots originally used to create this BE.
+ */
+ if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
+ NULL, 0, B_FALSE) == 0) {
+ (void) strlcpy(parent, origin, sizeof (parent));
+ if (be_get_snap(parent, &snap) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ be_print_err(gettext("be_destroy: failed to "
+ "get snapshot name from origin %s\n"), origin);
+ return (BE_ERR_INVAL);
+ }
+ has_origin = B_TRUE;
+ }
+
+ /*
+ * Destroy the BE's root and its hierarchical children. This call
+ * will end up closing the zfs handle passed in whether it succeeds
+ * or fails.
+ */
+ if (be_destroy_callback(zhp, dd) != 0) {
+ be_print_err(gettext("be_destroy: failed to "
+ "destroy BE %s\n"), root_ds);
+ return (BE_ERR_DESTROY);
+ }
+
+ /* If BE has an origin */
+ if (has_origin) {
+
+ /*
+ * If origin snapshot doesn't have any other
+ * dependents, delete the origin.
+ */
+ if ((zhp = zfs_open(g_zfs, origin, ZFS_TYPE_SNAPSHOT)) ==
+ NULL) {
+ be_print_err(gettext("be_destroy: failed to "
+ "open BE's origin (%s): %s\n"), origin,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+
+ /* If origin has dependents, don't delete it. */
+ if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ ZFS_CLOSE(zhp);
+
+ /* Get handle to BE's parent's root dataset */
+ if ((zhp = zfs_open(g_zfs, parent, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_destroy: failed to "
+ "open BE's parent root dataset (%s): %s\n"), parent,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+
+ /* Destroy the snapshot origin used to create this BE. */
+ /*
+ * The boolean set to B_FALSE and passed to zfs_destroy_snaps()
+ * tells zfs to process and destroy the snapshots now.
+ * Otherwise the call will potentially return where the
+ * snapshot isn't actually destroyed yet, and ZFS is waiting
+ * until all the references to the snapshot have been
+ * released before actually destroying the snapshot.
+ */
+ if (zfs_destroy_snaps(zhp, snap, B_FALSE) != 0) {
+ be_print_err(gettext("be_destroy: failed to "
+ "destroy original snapshots used to create "
+ "BE: %s\n"), libzfs_error_description(g_zfs));
+
+ /*
+ * If a failure happened because a clone exists,
+ * don't return a failure to the user. Above, we're
+ * only checking that the root dataset's origin
+ * snapshot doesn't have dependent clones, but its
+ * possible that a subordinate dataset origin snapshot
+ * has a clone. We really need to check for that
+ * before trying to destroy the origin snapshot.
+ */
+ if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+ ZFS_CLOSE(zhp);
+ }
+
+ return (ret);
+}
+
+/*
+ * Function: be_destroy_zones
+ * Description: Find valid zone's and call be_destroy_zone_roots to destroy its
+ * corresponding dataset and all of its children datasets
+ * and snapshots.
+ * Parameters:
+ * be_name - name of global boot environment being destroyed
+ * be_root_ds - root dataset of global boot environment being
+ * destroyed.
+ * dd - be_destroy_data_t pointer
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ *
+ * NOTES - Requires that the BE being deleted has no dependent BEs. If it
+ * does, the destroy will fail.
+ */
+static int
+be_destroy_zones(char *be_name, char *be_root_ds, be_destroy_data_t *dd)
+{
+ int i;
+ int ret = BE_SUCCESS;
+ int force_umnt = BE_UNMOUNT_FLAG_NULL;
+ char *zonepath = NULL;
+ char *zonename = NULL;
+ char *zonepath_ds = NULL;
+ char *mp = NULL;
+ zoneList_t zlist = NULL;
+ zoneBrandList_t *brands = NULL;
+ zfs_handle_t *zhp = NULL;
+
+ /* If zones are not implemented, then get out. */
+ if (!z_zones_are_implemented()) {
+ return (BE_SUCCESS);
+ }
+
+ /* Get list of supported brands */
+ if ((brands = be_get_supported_brandlist()) == NULL) {
+ be_print_err(gettext("be_destroy_zones: "
+ "no supported brands\n"));
+ return (BE_SUCCESS);
+ }
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_destroy_zones: failed to "
+ "open BE root dataset (%s): %s\n"), be_root_ds,
+ libzfs_error_description(g_zfs));
+ z_free_brand_list(brands);
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * If the global BE is not mounted, we must mount it here to
+ * gather data about the non-global zones in it.
+ */
+ if (!zfs_is_mounted(zhp, &mp)) {
+ if ((ret = _be_mount(be_name, &mp,
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy_zones: failed to "
+ "mount the BE (%s) for zones processing.\n"),
+ be_name);
+ ZFS_CLOSE(zhp);
+ z_free_brand_list(brands);
+ return (ret);
+ }
+ }
+ ZFS_CLOSE(zhp);
+
+ z_set_zone_root(mp);
+ free(mp);
+
+ /* Get list of supported zones. */
+ if ((zlist = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
+ z_free_brand_list(brands);
+ return (BE_SUCCESS);
+ }
+
+ /* Unmount the BE before destroying the zones in it. */
+ if (dd->force_unmount)
+ force_umnt = BE_UNMOUNT_FLAG_FORCE;
+ if ((ret = _be_unmount(be_name, force_umnt)) != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy_zones: failed to "
+ "unmount the BE (%s)\n"), be_name);
+ goto done;
+ }
+
+ /* Iterate through the zones and destroy them. */
+ for (i = 0; (zonename = z_zlist_get_zonename(zlist, i)) != NULL; i++) {
+
+ /* Skip zones that aren't at least installed */
+ if (z_zlist_get_current_state(zlist, i) < ZONE_STATE_INSTALLED)
+ continue;
+
+ zonepath = z_zlist_get_zonepath(zlist, i);
+
+ /*
+ * Get the dataset of this zonepath. If its not
+ * a dataset, skip it.
+ */
+ if ((zonepath_ds = be_get_ds_from_dir(zonepath)) == NULL)
+ continue;
+
+ /*
+ * Check if this zone is supported based on the
+ * dataset of its zonepath.
+ */
+ if (!be_zone_supported(zonepath_ds)) {
+ free(zonepath_ds);
+ continue;
+ }
+
+ /* Find the zone BE root datasets for this zone. */
+ if ((ret = be_destroy_zone_roots(zonepath_ds, dd))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy_zones: failed to "
+ "find and destroy zone roots for zone %s\n"),
+ zonename);
+ free(zonepath_ds);
+ goto done;
+ }
+ free(zonepath_ds);
+ }
+
+done:
+ z_free_brand_list(brands);
+ z_free_zone_list(zlist);
+
+ return (ret);
+}
+
+/*
+ * Function: be_destroy_zone_roots
+ * Description: This function will open the zone's root container dataset
+ * and iterate the datasets within, looking for roots that
+ * belong to the given global BE and destroying them.
+ * If no other zone roots remain in the zone's root container
+ * dataset, the function will destroy it and the zone's
+ * zonepath dataset as well.
+ * Parameters:
+ * zonepath_ds - pointer to zone's zonepath dataset.
+ * dd - pointer to a linked destroy data.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_destroy_zone_roots(char *zonepath_ds, be_destroy_data_t *dd)
+{
+ zfs_handle_t *zhp;
+ char zone_container_ds[MAXPATHLEN];
+ int ret = BE_SUCCESS;
+
+ /* Generate string for the root container dataset for this zone. */
+ be_make_container_ds(zonepath_ds, zone_container_ds,
+ sizeof (zone_container_ds));
+
+ /* Get handle to this zone's root container dataset. */
+ if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_destroy_zone_roots: failed to "
+ "open zone root container dataset (%s): %s\n"),
+ zone_container_ds, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Iterate through all of this zone's BEs, destroying the ones
+ * that belong to the parent global BE.
+ */
+ if ((ret = zfs_iter_filesystems(zhp, be_destroy_zone_roots_callback,
+ dd)) != 0) {
+ be_print_err(gettext("be_destroy_zone_roots: failed to "
+ "destroy zone roots under zonepath dataset %s: %s\n"),
+ zonepath_ds, libzfs_error_description(g_zfs));
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ ZFS_CLOSE(zhp);
+
+ /* Get handle to this zone's root container dataset. */
+ if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_destroy_zone_roots: failed to "
+ "open zone root container dataset (%s): %s\n"),
+ zone_container_ds, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * If there are no more zone roots in this zone's root container,
+ * dataset, destroy it and the zonepath dataset as well.
+ */
+ if (zfs_iter_filesystems(zhp, be_zone_root_exists_callback, NULL)
+ == 0) {
+ /* Destroy the zone root container dataset */
+ if (zfs_unmount(zhp, NULL, MS_FORCE) != 0 ||
+ zfs_destroy(zhp, B_FALSE) != 0) {
+ be_print_err(gettext("be_destroy_zone_roots: failed to "
+ "destroy zone root container dataset (%s): %s\n"),
+ zone_container_ds, libzfs_error_description(g_zfs));
+ goto done;
+ }
+ ZFS_CLOSE(zhp);
+
+ /* Get handle to zonepath dataset */
+ if ((zhp = zfs_open(g_zfs, zonepath_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_destroy_zone_roots: failed to "
+ "open zonepath dataset (%s): %s\n"),
+ zonepath_ds, libzfs_error_description(g_zfs));
+ goto done;
+ }
+
+ /* Destroy zonepath dataset */
+ if (zfs_unmount(zhp, NULL, MS_FORCE) != 0 ||
+ zfs_destroy(zhp, B_FALSE) != 0) {
+ be_print_err(gettext("be_destroy_zone_roots: "
+ "failed to destroy zonepath dataest %s: %s\n"),
+ zonepath_ds, libzfs_error_description(g_zfs));
+ goto done;
+ }
+ }
+
+done:
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_destroy_zone_roots_callback
+ * Description: This function is used as a callback to iterate over all of
+ * a zone's root datasets, finding the one's that
+ * correspond to the current BE. The name's
+ * of the zone root datasets are then destroyed by _be_destroy().
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current dataset being processed
+ * data - be_destroy_data_t pointer
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_destroy_zone_roots_callback(zfs_handle_t *zhp, void *data)
+{
+ be_destroy_data_t *dd = data;
+ uuid_t parent_uuid = { 0 };
+ int ret = 0;
+
+ if (be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid)
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy_zone_roots_callback: "
+ "could not get parentuuid for zone root dataset %s\n"),
+ zfs_get_name(zhp));
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+
+ if (uuid_compare(dd->gz_be_uuid, parent_uuid) == 0) {
+ /*
+ * Found a zone root dataset belonging to the parent
+ * BE being destroyed. Destroy this zone BE.
+ */
+ if ((ret = _be_destroy(zfs_get_name(zhp), dd)) != BE_SUCCESS) {
+ be_print_err(gettext("be_destroy_zone_root_callback: "
+ "failed to destroy zone root %s\n"),
+ zfs_get_name(zhp));
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+ ZFS_CLOSE(zhp);
+
+ return (ret);
+}
+
+/*
+ * Function: be_copy_zones
+ * Description: Find valid zones and clone them to create their
+ * corresponding datasets for the BE being created.
+ * Parameters:
+ * obe_name - name of source global BE being copied.
+ * obe_root_ds - root dataset of source global BE being copied.
+ * nbe_root_ds - root dataset of target global BE.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_copy_zones(char *obe_name, char *obe_root_ds, char *nbe_root_ds)
+{
+ int i, num_retries;
+ int ret = BE_SUCCESS;
+ int iret = 0;
+ char *zonename = NULL;
+ char *zonepath = NULL;
+ char *zone_be_name = NULL;
+ char *temp_mntpt = NULL;
+ char *new_zone_be_name = NULL;
+ char zoneroot[MAXPATHLEN];
+ char zoneroot_ds[MAXPATHLEN];
+ char zone_container_ds[MAXPATHLEN];
+ char new_zoneroot_ds[MAXPATHLEN];
+ char ss[MAXPATHLEN];
+ uuid_t uu = { 0 };
+ char uu_string[UUID_PRINTABLE_STRING_LENGTH] = { 0 };
+ be_transaction_data_t bt = { 0 };
+ zfs_handle_t *obe_zhp = NULL;
+ zfs_handle_t *nbe_zhp = NULL;
+ zfs_handle_t *z_zhp = NULL;
+ zoneList_t zlist = NULL;
+ zoneBrandList_t *brands = NULL;
+ boolean_t mounted_here = B_FALSE;
+ char *snap_name = NULL;
+
+ /* If zones are not implemented, then get out. */
+ if (!z_zones_are_implemented()) {
+ return (BE_SUCCESS);
+ }
+
+ /* Get list of supported brands */
+ if ((brands = be_get_supported_brandlist()) == NULL) {
+ be_print_err(gettext("be_copy_zones: "
+ "no supported brands\n"));
+ return (BE_SUCCESS);
+ }
+
+ /* Get handle to origin BE's root dataset */
+ if ((obe_zhp = zfs_open(g_zfs, obe_root_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_copy_zones: failed to open "
+ "the origin BE root dataset (%s) for zones processing: "
+ "%s\n"), obe_root_ds, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Get handle to newly cloned BE's root dataset */
+ if ((nbe_zhp = zfs_open(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_copy_zones: failed to open "
+ "the new BE root dataset (%s): %s\n"), nbe_root_ds,
+ libzfs_error_description(g_zfs));
+ ZFS_CLOSE(obe_zhp);
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Get the uuid of the newly cloned parent BE. */
+ if (be_get_uuid(zfs_get_name(nbe_zhp), &uu) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to get uuid for BE root "
+ "dataset %s\n"), zfs_get_name(nbe_zhp));
+ ZFS_CLOSE(nbe_zhp);
+ goto done;
+ }
+ ZFS_CLOSE(nbe_zhp);
+ uuid_unparse(uu, uu_string);
+
+ /*
+ * If the origin BE is not mounted, we must mount it here to
+ * gather data about the non-global zones in it.
+ */
+ if (!zfs_is_mounted(obe_zhp, &temp_mntpt)) {
+ if ((ret = _be_mount(obe_name, &temp_mntpt,
+ BE_MOUNT_FLAG_NULL)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy_zones: failed to "
+ "mount the BE (%s) for zones procesing.\n"),
+ obe_name);
+ goto done;
+ }
+ mounted_here = B_TRUE;
+ }
+
+ z_set_zone_root(temp_mntpt);
+
+ /* Get list of supported zones. */
+ if ((zlist = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
+ ret = BE_SUCCESS;
+ goto done;
+ }
+
+ for (i = 0; (zonename = z_zlist_get_zonename(zlist, i)) != NULL; i++) {
+
+ be_fs_list_data_t fld = { 0 };
+ char zonepath_ds[MAXPATHLEN];
+ char *ds = NULL;
+
+ /* Get zonepath of zone */
+ zonepath = z_zlist_get_zonepath(zlist, i);
+
+ /* Skip zones that aren't at least installed */
+ if (z_zlist_get_current_state(zlist, i) < ZONE_STATE_INSTALLED)
+ continue;
+
+ /*
+ * Get the dataset of this zonepath. If its not
+ * a dataset, skip it.
+ */
+ if ((ds = be_get_ds_from_dir(zonepath)) == NULL)
+ continue;
+
+ (void) strlcpy(zonepath_ds, ds, sizeof (zonepath_ds));
+ free(ds);
+ ds = NULL;
+
+ /* Get zoneroot directory */
+ be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
+
+ /* If zonepath dataset not supported, skip it. */
+ if (!be_zone_supported(zonepath_ds)) {
+ continue;
+ }
+
+ if ((ret = be_find_active_zone_root(obe_zhp, zonepath_ds,
+ zoneroot_ds, sizeof (zoneroot_ds))) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to find active zone root for zone %s "
+ "in BE %s\n"), zonename, obe_name);
+ goto done;
+ }
+
+ be_make_container_ds(zonepath_ds, zone_container_ds,
+ sizeof (zone_container_ds));
+
+ if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to open zone root dataset (%s): %s\n"),
+ zoneroot_ds, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ zone_be_name =
+ be_get_zone_be_name(zoneroot_ds, zone_container_ds);
+
+ if ((new_zone_be_name = be_auto_zone_be_name(zone_container_ds,
+ zone_be_name)) == NULL) {
+ be_print_err(gettext("be_copy_zones: failed "
+ "to generate auto name for zone BE.\n"));
+ ret = BE_ERR_AUTONAME;
+ goto done;
+ }
+
+ if ((snap_name = be_auto_snap_name()) == NULL) {
+ be_print_err(gettext("be_copy_zones: failed to "
+ "generate snapshot name for zone BE.\n"));
+ ret = BE_ERR_AUTONAME;
+ goto done;
+ }
+
+ (void) snprintf(ss, sizeof (ss), "%s@%s", zoneroot_ds,
+ snap_name);
+
+ if (zfs_snapshot(g_zfs, ss, B_TRUE, NULL) != 0) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to snapshot zone BE (%s): %s\n"),
+ ss, libzfs_error_description(g_zfs));
+ if (libzfs_errno(g_zfs) == EZFS_EXISTS)
+ ret = BE_ERR_ZONE_SS_EXISTS;
+ else
+ ret = zfs_err_to_be_err(g_zfs);
+
+ goto done;
+ }
+
+ (void) snprintf(new_zoneroot_ds, sizeof (new_zoneroot_ds),
+ "%s/%s", zone_container_ds, new_zone_be_name);
+
+ bt.obe_name = zone_be_name;
+ bt.obe_root_ds = zoneroot_ds;
+ bt.obe_snap_name = snap_name;
+ bt.obe_altroot = temp_mntpt;
+ bt.nbe_name = new_zone_be_name;
+ bt.nbe_root_ds = new_zoneroot_ds;
+
+ if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) {
+ be_print_err(gettext("be_copy_zones: "
+ "internal error: out of memory\n"));
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+
+ /*
+ * The call to be_clone_fs_callback always closes the
+ * zfs_handle so there's no need to close z_zhp.
+ */
+ if ((iret = be_clone_fs_callback(z_zhp, &bt)) != 0) {
+ z_zhp = NULL;
+ if (iret != BE_ERR_BE_EXISTS) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to create zone BE clone for new "
+ "zone BE %s\n"), new_zone_be_name);
+ ret = iret;
+ if (bt.nbe_zfs_props != NULL)
+ nvlist_free(bt.nbe_zfs_props);
+ goto done;
+ }
+ /*
+ * We failed to create the new zone BE because a zone
+ * BE with the auto-name we generated above has since
+ * come into existence. Regenerate a new auto-name
+ * and retry.
+ */
+ for (num_retries = 1;
+ num_retries < BE_AUTO_NAME_MAX_TRY;
+ num_retries++) {
+
+ /* Sleep 1 before retrying */
+ (void) sleep(1);
+
+ /* Generate new auto zone BE name */
+ free(new_zone_be_name);
+ if ((new_zone_be_name = be_auto_zone_be_name(
+ zone_container_ds,
+ zone_be_name)) == NULL) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to generate auto name "
+ "for zone BE.\n"));
+ ret = BE_ERR_AUTONAME;
+ if (bt.nbe_zfs_props != NULL)
+ nvlist_free(bt.nbe_zfs_props);
+ goto done;
+ }
+
+ (void) snprintf(new_zoneroot_ds,
+ sizeof (new_zoneroot_ds),
+ "%s/%s", zone_container_ds,
+ new_zone_be_name);
+ bt.nbe_name = new_zone_be_name;
+ bt.nbe_root_ds = new_zoneroot_ds;
+
+ /*
+ * Get handle to original zone BE's root
+ * dataset.
+ */
+ if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to open zone root "
+ "dataset (%s): %s\n"),
+ zoneroot_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ if (bt.nbe_zfs_props != NULL)
+ nvlist_free(bt.nbe_zfs_props);
+ goto done;
+ }
+
+ /*
+ * Try to clone the zone BE again. This
+ * call will end up closing the zfs
+ * handle passed in whether it
+ * succeeds or fails.
+ */
+ iret = be_clone_fs_callback(z_zhp, &bt);
+ z_zhp = NULL;
+ if (iret == 0) {
+ break;
+ } else if (iret != BE_ERR_BE_EXISTS) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to create zone BE clone "
+ "for new zone BE %s\n"),
+ new_zone_be_name);
+ ret = iret;
+ if (bt.nbe_zfs_props != NULL)
+ nvlist_free(bt.nbe_zfs_props);
+ goto done;
+ }
+ }
+ /*
+ * If we've exhausted the maximum number of
+ * tries, free the auto zone BE name and return
+ * error.
+ */
+ if (num_retries == BE_AUTO_NAME_MAX_TRY) {
+ be_print_err(gettext("be_copy_zones: failed "
+ "to create a unique auto zone BE name\n"));
+ free(bt.nbe_name);
+ bt.nbe_name = NULL;
+ ret = BE_ERR_AUTONAME;
+ if (bt.nbe_zfs_props != NULL)
+ nvlist_free(bt.nbe_zfs_props);
+ goto done;
+ }
+ }
+
+ if (bt.nbe_zfs_props != NULL)
+ nvlist_free(bt.nbe_zfs_props);
+
+ z_zhp = NULL;
+
+ if ((z_zhp = zfs_open(g_zfs, new_zoneroot_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to open the new zone BE root dataset "
+ "(%s): %s\n"), new_zoneroot_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (zfs_prop_set(z_zhp, BE_ZONE_PARENTBE_PROPERTY,
+ uu_string) != 0) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to set parentbe property\n"));
+ ZFS_CLOSE(z_zhp);
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (zfs_prop_set(z_zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to set active property\n"));
+ ZFS_CLOSE(z_zhp);
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /*
+ * Generate a list of file systems from the original
+ * zone BE that are legacy mounted. We use this list
+ * to determine which entries in the vfstab we need to
+ * update for the new zone BE we've just created.
+ */
+ if ((ret = be_get_legacy_fs(obe_name, obe_root_ds,
+ zoneroot_ds, zoneroot, &fld)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to get legacy mounted file system "
+ "list for zone %s\n"), zonename);
+ ZFS_CLOSE(z_zhp);
+ goto done;
+ }
+
+ /*
+ * Update new zone BE's vfstab.
+ */
+ if ((ret = be_update_zone_vfstab(z_zhp, bt.nbe_name,
+ zonepath_ds, zonepath_ds, &fld)) != BE_SUCCESS) {
+ be_print_err(gettext("be_copy_zones: "
+ "failed to update new BE's vfstab (%s)\n"),
+ bt.nbe_name);
+ ZFS_CLOSE(z_zhp);
+ be_free_fs_list(&fld);
+ goto done;
+ }
+
+ be_free_fs_list(&fld);
+ ZFS_CLOSE(z_zhp);
+ }
+
+done:
+ free(snap_name);
+ if (brands != NULL)
+ z_free_brand_list(brands);
+ if (zlist != NULL)
+ z_free_zone_list(zlist);
+
+ if (mounted_here)
+ (void) _be_unmount(obe_name, 0);
+
+ ZFS_CLOSE(obe_zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_clone_fs_callback
+ * Description: Callback function used to iterate through a BE's filesystems
+ * to clone them for the new BE.
+ * Parameters:
+ * zhp - zfs_handle_t pointer for the filesystem being processed.
+ * data - be_transaction_data_t pointer providing information
+ * about original BE and new BE.
+ * Return:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_clone_fs_callback(zfs_handle_t *zhp, void *data)
+{
+ be_transaction_data_t *bt = data;
+ zfs_handle_t *zhp_ss = NULL;
+ char prop_buf[MAXPATHLEN];
+ char zhp_name[ZFS_MAXNAMELEN];
+ char clone_ds[MAXPATHLEN];
+ char ss[MAXPATHLEN];
+ int ret = 0;
+
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf,
+ ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("be_clone_fs_callback: "
+ "failed to get dataset mountpoint (%s): %s\n"),
+ zfs_get_name(zhp), libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) != 0 &&
+ strcmp(prop_buf, "legacy") != 0) {
+ /*
+ * Since zfs can't currently handle setting the
+ * mountpoint for a zoned dataset we'll have to skip
+ * this dataset. This is because the mountpoint is not
+ * set to "legacy".
+ */
+ goto zoned;
+ }
+ /*
+ * Get a copy of the dataset name from the zfs handle
+ */
+ (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name));
+
+ /*
+ * Get the clone dataset name and prepare the zfs properties for it.
+ */
+ if ((ret = be_prep_clone_send_fs(zhp, bt, clone_ds,
+ sizeof (clone_ds))) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Generate the name of the snapshot to use.
+ */
+ (void) snprintf(ss, sizeof (ss), "%s@%s", zhp_name,
+ bt->obe_snap_name);
+
+ /*
+ * Get handle to snapshot.
+ */
+ if ((zhp_ss = zfs_open(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) == NULL) {
+ be_print_err(gettext("be_clone_fs_callback: "
+ "failed to get handle to snapshot (%s): %s\n"), ss,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Clone the dataset.
+ */
+ if (zfs_clone(zhp_ss, clone_ds, bt->nbe_zfs_props) != 0) {
+ be_print_err(gettext("be_clone_fs_callback: "
+ "failed to create clone dataset (%s): %s\n"),
+ clone_ds, libzfs_error_description(g_zfs));
+
+ ZFS_CLOSE(zhp_ss);
+ ZFS_CLOSE(zhp);
+
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ ZFS_CLOSE(zhp_ss);
+
+zoned:
+ /*
+ * Iterate through zhp's children datasets (if any)
+ * and clone them accordingly.
+ */
+ if ((ret = zfs_iter_filesystems(zhp, be_clone_fs_callback, bt)) != 0) {
+ /*
+ * Error occurred while processing a child dataset.
+ * Destroy this dataset and return error.
+ */
+ zfs_handle_t *d_zhp = NULL;
+
+ ZFS_CLOSE(zhp);
+
+ if ((d_zhp = zfs_open(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ return (ret);
+ }
+
+ (void) zfs_destroy(d_zhp, B_FALSE);
+ ZFS_CLOSE(d_zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: be_send_fs_callback
+ * Description: Callback function used to iterate through a BE's filesystems
+ * to copy them for the new BE.
+ * Parameters:
+ * zhp - zfs_handle_t pointer for the filesystem being processed.
+ * data - be_transaction_data_t pointer providing information
+ * about original BE and new BE.
+ * Return:
+ * 0 - Success
+ * be_errnot_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_send_fs_callback(zfs_handle_t *zhp, void *data)
+{
+ be_transaction_data_t *bt = data;
+ recvflags_t flags = { 0 };
+ char zhp_name[ZFS_MAXNAMELEN];
+ char clone_ds[MAXPATHLEN];
+ sendflags_t send_flags = { 0 };
+ int pid, status, retval;
+ int srpipe[2];
+ int ret = 0;
+
+ /*
+ * Get a copy of the dataset name from the zfs handle
+ */
+ (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name));
+
+ /*
+ * Get the clone dataset name and prepare the zfs properties for it.
+ */
+ if ((ret = be_prep_clone_send_fs(zhp, bt, clone_ds,
+ sizeof (clone_ds))) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Create the new dataset.
+ */
+ if (zfs_create(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM, bt->nbe_zfs_props)
+ != 0) {
+ be_print_err(gettext("be_send_fs_callback: "
+ "failed to create new dataset '%s': %s\n"),
+ clone_ds, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Destination file system is already created
+ * hence we need to set the force flag on
+ */
+ flags.force = B_TRUE;
+
+ /*
+ * Initiate the pipe to be used for the send and recv
+ */
+ if (pipe(srpipe) != 0) {
+ int err = errno;
+ be_print_err(gettext("be_send_fs_callback: failed to "
+ "open pipe\n"));
+ ZFS_CLOSE(zhp);
+ return (errno_to_be_err(err));
+ }
+
+ /*
+ * Fork off a child to send the dataset
+ */
+ if ((pid = fork()) == -1) {
+ int err = errno;
+ be_print_err(gettext("be_send_fs_callback: failed to fork\n"));
+ (void) close(srpipe[0]);
+ (void) close(srpipe[1]);
+ ZFS_CLOSE(zhp);
+ return (errno_to_be_err(err));
+ } else if (pid == 0) { /* child process */
+ (void) close(srpipe[0]);
+
+ /* Send dataset */
+ if (zfs_send(zhp, NULL, bt->obe_snap_name, send_flags,
+ srpipe[1], NULL, NULL, NULL) != 0) {
+ _exit(1);
+ }
+ ZFS_CLOSE(zhp);
+
+ _exit(0);
+ }
+
+ (void) close(srpipe[1]);
+
+ /* Receive dataset */
+ if (zfs_receive(g_zfs, clone_ds, flags, srpipe[0], NULL) != 0) {
+ be_print_err(gettext("be_send_fs_callback: failed to "
+ "recv dataset (%s)\n"), clone_ds);
+ }
+ (void) close(srpipe[0]);
+
+ /* wait for child to exit */
+ do {
+ retval = waitpid(pid, &status, 0);
+ if (retval == -1) {
+ status = 0;
+ }
+ } while (retval != pid);
+
+ if (WEXITSTATUS(status) != 0) {
+ be_print_err(gettext("be_send_fs_callback: failed to "
+ "send dataset (%s)\n"), zhp_name);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_ZFS);
+ }
+
+
+ /*
+ * Iterate through zhp's children datasets (if any)
+ * and send them accordingly.
+ */
+ if ((ret = zfs_iter_filesystems(zhp, be_send_fs_callback, bt)) != 0) {
+ /*
+ * Error occurred while processing a child dataset.
+ * Destroy this dataset and return error.
+ */
+ zfs_handle_t *d_zhp = NULL;
+
+ ZFS_CLOSE(zhp);
+
+ if ((d_zhp = zfs_open(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ return (ret);
+ }
+
+ (void) zfs_destroy(d_zhp, B_FALSE);
+ ZFS_CLOSE(d_zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: be_destroy_callback
+ * Description: Callback function used to destroy a BEs children datasets
+ * and snapshots.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to the filesystem being processed.
+ * data - Not used.
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_destroy_callback(zfs_handle_t *zhp, void *data)
+{
+ be_destroy_data_t *dd = data;
+ int ret = 0;
+
+ /*
+ * Iterate down this file system's hierarchical children
+ * and destroy them first.
+ */
+ if ((ret = zfs_iter_filesystems(zhp, be_destroy_callback, dd)) != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ if (dd->destroy_snaps) {
+ /*
+ * Iterate through this file system's snapshots and
+ * destroy them before destroying the file system itself.
+ */
+ if ((ret = zfs_iter_snapshots(zhp, be_destroy_callback, dd))
+ != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+
+ /* Attempt to unmount the dataset before destroying it */
+ if (dd->force_unmount) {
+ if ((ret = zfs_unmount(zhp, NULL, MS_FORCE)) != 0) {
+ be_print_err(gettext("be_destroy_callback: "
+ "failed to unmount %s: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+
+ if (zfs_destroy(zhp, B_FALSE) != 0) {
+ be_print_err(gettext("be_destroy_callback: "
+ "failed to destroy %s: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: be_demote_callback
+ * Description: This callback function is used to iterate through the file
+ * systems of a BE, looking for the right clone to promote such
+ * that this file system is left without any dependent clones.
+ * If the file system has no dependent clones, it doesn't need
+ * to get demoted, and the function will return success.
+ *
+ * The demotion will be done in two passes. The first pass
+ * will attempt to find the youngest snapshot that has a clone
+ * that is part of some other BE. The second pass will attempt
+ * to find the youngest snapshot that has a clone that is not
+ * part of a BE. Doing this helps ensure the aggregated set of
+ * file systems that compose a BE stay coordinated wrt BE
+ * snapshots and BE dependents. It also prevents a random user
+ * generated clone of a BE dataset to become the parent of other
+ * BE datasets after demoting this dataset.
+ *
+ * Parameters:
+ * zhp - zfs_handle_t pointer to the current file system being
+ * processed.
+ * data - not used.
+ * Return:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+/* LINTED */
+be_demote_callback(zfs_handle_t *zhp, void *data)
+{
+ be_demote_data_t dd = { 0 };
+ int i, ret = 0;
+
+ /*
+ * Initialize be_demote_data for the first pass - this will find a
+ * clone in another BE, if one exists.
+ */
+ dd.find_in_BE = B_TRUE;
+
+ for (i = 0; i < 2; i++) {
+
+ if (zfs_iter_snapshots(zhp, be_demote_find_clone_callback, &dd)
+ != 0) {
+ be_print_err(gettext("be_demote_callback: "
+ "failed to iterate snapshots for %s: %s\n"),
+ zfs_get_name(zhp), libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ if (dd.clone_zhp != NULL) {
+ /* Found the clone to promote. Promote it. */
+ if (zfs_promote(dd.clone_zhp) != 0) {
+ be_print_err(gettext("be_demote_callback: "
+ "failed to promote %s: %s\n"),
+ zfs_get_name(dd.clone_zhp),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(dd.clone_zhp);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(dd.clone_zhp);
+ }
+
+ /*
+ * Reinitialize be_demote_data for the second pass.
+ * This will find a user created clone outside of any BE
+ * namespace, if one exists.
+ */
+ dd.clone_zhp = NULL;
+ dd.origin_creation = 0;
+ dd.snapshot = NULL;
+ dd.find_in_BE = B_FALSE;
+ }
+
+ /* Iterate down this file system's children and demote them */
+ if ((ret = zfs_iter_filesystems(zhp, be_demote_callback, NULL)) != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: be_demote_find_clone_callback
+ * Description: This callback function is used to iterate through the
+ * snapshots of a dataset, looking for the youngest snapshot
+ * that has a clone. If found, it returns a reference to the
+ * clone back to the caller in the callback data.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current snapshot being looked at
+ * data - be_demote_data_t pointer used to store the clone that
+ * is found.
+ * Returns:
+ * 0 - Successfully iterated through all snapshots.
+ * 1 - Failed to iterate through all snapshots.
+ * Scope:
+ * Private
+ */
+static int
+be_demote_find_clone_callback(zfs_handle_t *zhp, void *data)
+{
+ be_demote_data_t *dd = data;
+ time_t snap_creation;
+ int zret = 0;
+
+ /* If snapshot has no clones, no need to look at it */
+ if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) {
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+
+ dd->snapshot = zfs_get_name(zhp);
+
+ /* Get the creation time of this snapshot */
+ snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
+
+ /*
+ * If this snapshot's creation time is greater than (or younger than)
+ * the current youngest snapshot found, iterate this snapshot to
+ * check if it has a clone that we're looking for.
+ */
+ if (snap_creation >= dd->origin_creation) {
+ /*
+ * Iterate the dependents of this snapshot to find a
+ * a clone that's a direct dependent.
+ */
+ if ((zret = zfs_iter_dependents(zhp, B_FALSE,
+ be_demote_get_one_clone, dd)) == -1) {
+ be_print_err(gettext("be_demote_find_clone_callback: "
+ "failed to iterate dependents of %s\n"),
+ zfs_get_name(zhp));
+ ZFS_CLOSE(zhp);
+ return (1);
+ } else if (zret == 1) {
+ /*
+ * Found a clone, update the origin_creation time
+ * in the callback data.
+ */
+ dd->origin_creation = snap_creation;
+ }
+ }
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: be_demote_get_one_clone
+ * Description: This callback function is used to iterate through a
+ * snapshot's dependencies to find a filesystem that is a
+ * direct clone of the snapshot being iterated.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current dataset being looked at
+ * data - be_demote_data_t pointer used to store the clone
+ * that is found, and also provides flag to note
+ * whether or not the clone filesystem being searched
+ * for needs to be found in a BE dataset hierarchy.
+ * Return:
+ * 1 - Success, found clone and its also a BE's root dataset.
+ * 0 - Failure, clone not found.
+ * Scope:
+ * Private
+ */
+static int
+be_demote_get_one_clone(zfs_handle_t *zhp, void *data)
+{
+ be_demote_data_t *dd = data;
+ char origin[ZFS_MAXNAMELEN];
+ char ds_path[ZFS_MAXNAMELEN];
+
+ if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+
+ (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
+
+ /*
+ * Make sure this is a direct clone of the snapshot
+ * we're iterating.
+ */
+ if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
+ NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("be_demote_get_one_clone: "
+ "failed to get origin of %s: %s\n"), ds_path,
+ libzfs_error_description(g_zfs));
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+ if (strcmp(origin, dd->snapshot) != 0) {
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+
+ if (dd->find_in_BE) {
+ if ((zpool_iter(g_zfs, be_check_be_roots_callback, ds_path))
+ > 0) {
+ if (dd->clone_zhp != NULL)
+ ZFS_CLOSE(dd->clone_zhp);
+ dd->clone_zhp = zhp;
+ return (1);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+
+ if (dd->clone_zhp != NULL)
+ ZFS_CLOSE(dd->clone_zhp);
+
+ dd->clone_zhp = zhp;
+ return (1);
+}
+
+/*
+ * Function: be_get_snap
+ * Description: This function takes a snapshot dataset name and separates
+ * out the parent dataset portion from the snapshot name.
+ * I.e. it finds the '@' in the snapshot dataset name and
+ * replaces it with a '\0'.
+ * Parameters:
+ * origin - char pointer to a snapshot dataset name. Its
+ * contents will be modified by this function.
+ * *snap - pointer to a char pointer. Will be set to the
+ * snapshot name portion upon success.
+ * Return:
+ * BE_SUCCESS - Success
+ * 1 - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_get_snap(char *origin, char **snap)
+{
+ char *cp;
+
+ /*
+ * Separate out the origin's dataset and snapshot portions by
+ * replacing the @ with a '\0'
+ */
+ cp = strrchr(origin, '@');
+ if (cp != NULL) {
+ if (cp[1] != NULL && cp[1] != '\0') {
+ cp[0] = '\0';
+ *snap = cp+1;
+ } else {
+ return (1);
+ }
+ } else {
+ return (1);
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_create_container_ds
+ * Description: This function checks that the zpool passed has the BE
+ * container dataset, and if not, then creates it.
+ * Parameters:
+ * zpool - name of pool to create BE container dataset in.
+ * Return:
+ * B_TRUE - Successfully created BE container dataset, or it
+ * already existed.
+ * B_FALSE - Failed to create container dataset.
+ * Scope:
+ * Private
+ */
+static boolean_t
+be_create_container_ds(char *zpool)
+{
+ nvlist_t *props = NULL;
+ char be_container_ds[MAXPATHLEN];
+
+ /* Generate string for BE container dataset for this pool */
+ be_make_container_ds(zpool, be_container_ds,
+ sizeof (be_container_ds));
+
+ if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
+
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ be_print_err(gettext("be_create_container_ds: "
+ "nvlist_alloc failed\n"));
+ return (B_FALSE);
+ }
+
+ if (nvlist_add_string(props,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ ZFS_MOUNTPOINT_LEGACY) != 0) {
+ be_print_err(gettext("be_create_container_ds: "
+ "internal error: out of memory\n"));
+ nvlist_free(props);
+ return (B_FALSE);
+ }
+
+ if (nvlist_add_string(props,
+ zfs_prop_to_name(ZFS_PROP_CANMOUNT), "off") != 0) {
+ be_print_err(gettext("be_create_container_ds: "
+ "internal error: out of memory\n"));
+ nvlist_free(props);
+ return (B_FALSE);
+ }
+
+ if (zfs_create(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM,
+ props) != 0) {
+ be_print_err(gettext("be_create_container_ds: "
+ "failed to create container dataset (%s): %s\n"),
+ be_container_ds, libzfs_error_description(g_zfs));
+ nvlist_free(props);
+ return (B_FALSE);
+ }
+
+ nvlist_free(props);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Function: be_prep_clone_send_fs
+ * Description: This function takes a zfs handle to a dataset from the
+ * original BE, and generates the name of the clone dataset
+ * to create for the new BE. It also prepares the zfs
+ * properties to be used for the new BE.
+ * Parameters:
+ * zhp - pointer to zfs_handle_t of the file system being
+ * cloned/copied.
+ * bt - be_transaction_data pointer providing information
+ * about the original BE and new BE.
+ * clone_ds - buffer to store the name of the dataset
+ * for the new BE.
+ * clone_ds_len - length of clone_ds buffer
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_prep_clone_send_fs(zfs_handle_t *zhp, be_transaction_data_t *bt,
+ char *clone_ds, int clone_ds_len)
+{
+ zprop_source_t sourcetype;
+ char source[ZFS_MAXNAMELEN];
+ char zhp_name[ZFS_MAXNAMELEN];
+ char mountpoint[MAXPATHLEN];
+ char *child_fs = NULL;
+ char *zhp_mountpoint = NULL;
+ int err = 0;
+
+ /*
+ * Get a copy of the dataset name zfs_name from zhp
+ */
+ (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name));
+
+ /*
+ * Get file system name relative to the root.
+ */
+ if (strncmp(zhp_name, bt->obe_root_ds, strlen(bt->obe_root_ds))
+ == 0) {
+ child_fs = zhp_name + strlen(bt->obe_root_ds);
+
+ /*
+ * if child_fs is NULL, this means we're processing the
+ * root dataset itself; set child_fs to the empty string.
+ */
+ if (child_fs == NULL)
+ child_fs = "";
+ } else {
+ return (BE_ERR_INVAL);
+ }
+
+ /*
+ * Generate the name of the clone file system.
+ */
+ (void) snprintf(clone_ds, clone_ds_len, "%s%s", bt->nbe_root_ds,
+ child_fs);
+
+ /* Get the mountpoint and source properties of the existing dataset */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), &sourcetype, source, sizeof (source),
+ B_FALSE) != 0) {
+ be_print_err(gettext("be_prep_clone_send_fs: "
+ "failed to get mountpoint for (%s): %s\n"),
+ zhp_name, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Workaround for 6668667 where a mountpoint property of "/" comes
+ * back as "".
+ */
+ if (strcmp(mountpoint, "") == 0) {
+ (void) snprintf(mountpoint, sizeof (mountpoint), "/");
+ }
+
+ /*
+ * Figure out what to set as the mountpoint for the new dataset.
+ * If the source of the mountpoint property is local, use the
+ * mountpoint value itself. Otherwise, remove it from the
+ * zfs properties list so that it gets inherited.
+ */
+ if (sourcetype & ZPROP_SRC_LOCAL) {
+ /*
+ * If the BE that this file system is a part of is
+ * currently mounted, strip off the BE altroot portion
+ * from the mountpoint.
+ */
+ zhp_mountpoint = mountpoint;
+
+ if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
+ bt->obe_altroot != NULL && strcmp(bt->obe_altroot,
+ "/") != 0 && zfs_is_mounted(zhp, NULL)) {
+
+ int altroot_len = strlen(bt->obe_altroot);
+
+ if (strncmp(bt->obe_altroot, mountpoint, altroot_len)
+ == 0) {
+ if (mountpoint[altroot_len] == '/')
+ zhp_mountpoint = mountpoint +
+ altroot_len;
+ else if (mountpoint[altroot_len] == '\0')
+ (void) snprintf(mountpoint,
+ sizeof (mountpoint), "/");
+ }
+ }
+
+ if (nvlist_add_string(bt->nbe_zfs_props,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ zhp_mountpoint) != 0) {
+ be_print_err(gettext("be_prep_clone_send_fs: "
+ "internal error: out of memory\n"));
+ return (BE_ERR_NOMEM);
+ }
+ } else {
+ err = nvlist_remove_all(bt->nbe_zfs_props,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT));
+ if (err != 0 && err != ENOENT) {
+ be_print_err(gettext("be_prep_clone_send_fs: "
+ "failed to remove mountpoint from "
+ "nvlist\n"));
+ return (BE_ERR_INVAL);
+ }
+ }
+
+ /*
+ * Set the 'canmount' property
+ */
+ if (nvlist_add_string(bt->nbe_zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) {
+ be_print_err(gettext("be_prep_clone_send_fs: "
+ "internal error: out of memory\n"));
+ return (BE_ERR_NOMEM);
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_get_zone_be_name
+ * Description: This function takes the zones root dataset, the container
+ * dataset and returns the zones BE name based on the zone
+ * root dataset.
+ * Parameters:
+ * root_ds - the zones root dataset.
+ * container_ds - the container dataset for the zone.
+ * Returns:
+ * char * - the BE name of this zone based on the root dataset.
+ */
+static char *
+be_get_zone_be_name(char *root_ds, char *container_ds)
+{
+ return (root_ds + (strlen(container_ds) + 1));
+}
+
+/*
+ * Function: be_zone_root_exists_callback
+ * Description: This callback function is used to determine if a
+ * zone root container dataset has any children. It always
+ * returns 1, signifying a hierarchical child of the zone
+ * root container dataset has been traversed and therefore
+ * it has children.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current dataset being processed.
+ * data - not used.
+ * Returns:
+ * 1 - dataset exists
+ * Scope:
+ * Private
+ */
+static int
+/* LINTED */
+be_zone_root_exists_callback(zfs_handle_t *zhp, void *data)
+{
+ ZFS_CLOSE(zhp);
+ return (1);
+}
diff --git a/usr/src/lib/libbe/common/be_list.c b/usr/src/lib/libbe/common/be_list.c
new file mode 100644
index 0000000000..b5469f55b9
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_list.c
@@ -0,0 +1,1122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <assert.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+/*
+ * Callback data used for zfs_iter calls.
+ */
+typedef struct list_callback_data {
+ char *zpool_name;
+ char *be_name;
+ be_node_list_t *be_nodes_head;
+ be_node_list_t *be_nodes;
+ char current_be[MAXPATHLEN];
+} list_callback_data_t;
+
+/*
+ * Private function prototypes
+ */
+static int be_add_children_callback(zfs_handle_t *zhp, void *data);
+static int be_get_list_callback(zpool_handle_t *, void *);
+static int be_get_node_data(zfs_handle_t *, be_node_list_t *, char *,
+ const char *, char *, char *);
+static int be_get_zone_node_data(be_node_list_t *, char *);
+static int be_get_ds_data(zfs_handle_t *, char *, be_dataset_list_t *,
+ be_node_list_t *);
+static int be_get_ss_data(zfs_handle_t *, char *, be_snapshot_list_t *,
+ be_node_list_t *);
+static void be_sort_list(be_node_list_t **);
+static int be_qsort_compare_BEs(const void *, const void *);
+static int be_qsort_compare_snapshots(const void *x, const void *y);
+static int be_qsort_compare_datasets(const void *x, const void *y);
+static void *be_list_alloc(int *, size_t);
+
+/*
+ * Private data.
+ */
+static char be_container_ds[MAXPATHLEN];
+static boolean_t zone_be = B_FALSE;
+
+/* ******************************************************************** */
+/* Public Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_list
+ * Description: Calls _be_list which finds all the BEs on the system and
+ * returns the datasets and snapshots belonging to each BE.
+ * Also data, such as dataset and snapshot properties,
+ * for each BE and their snapshots and datasets is
+ * returned. The data returned is as described in the
+ * be_dataset_list_t, be_snapshot_list_t and be_node_list_t
+ * structures.
+ * Parameters:
+ * be_name - The name of the BE to look up.
+ * If NULL a list of all BEs will be returned.
+ * be_nodes - A reference pointer to the list of BEs. The list
+ * structure will be allocated by _be_list and must
+ * be freed by a call to be_free_list. If there are no
+ * BEs found on the system this reference will be
+ * set to NULL.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_list(char *be_name, be_node_list_t **be_nodes)
+{
+ int ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Validate be_name if its not NULL */
+ if (be_name != NULL) {
+ if (!be_valid_be_name(be_name)) {
+ be_print_err(gettext("be_list: "
+ "invalid BE name %s\n"), be_name);
+ return (BE_ERR_INVAL);
+ }
+ }
+
+ ret = _be_list(be_name, be_nodes);
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/* ******************************************************************** */
+/* Semi-Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: _be_list
+ * Description: This does the actual work described in be_list.
+ * Parameters:
+ * be_name - The name of the BE to look up.
+ * If NULL a list of all BEs will be returned.
+ * be_nodes - A reference pointer to the list of BEs. The list
+ * structure will be allocated here and must
+ * be freed by a call to be_free_list. If there are no
+ * BEs found on the system this reference will be
+ * set to NULL.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+_be_list(char *be_name, be_node_list_t **be_nodes)
+{
+ list_callback_data_t cb = { 0 };
+ be_transaction_data_t bt = { 0 };
+ int ret = BE_SUCCESS;
+
+ if (be_nodes == NULL)
+ return (BE_ERR_INVAL);
+
+ if (be_find_current_be(&bt) != BE_SUCCESS) {
+ /*
+ * We were unable to find a currently booted BE which
+ * probably means that we're not booted in a BE envoronment.
+ * None of the BE's will be marked as the active BE.
+ */
+ (void) strcpy(cb.current_be, "-");
+ } else {
+ (void) strncpy(cb.current_be, bt.obe_name,
+ sizeof (cb.current_be));
+ }
+
+ /*
+ * If be_name is NULL we'll look for all BE's on the system.
+ * If not then we will only return data for the specified BE.
+ */
+ if (be_name != NULL)
+ cb.be_name = strdup(be_name);
+
+ if ((zpool_iter(g_zfs, be_get_list_callback, &cb)) != 0) {
+ if (cb.be_nodes_head != NULL) {
+ be_free_list(cb.be_nodes_head);
+ cb.be_nodes_head = NULL;
+ cb.be_nodes = NULL;
+ }
+ ret = BE_ERR_BE_NOENT;
+ }
+
+ if (cb.be_nodes_head == NULL) {
+ if (be_name != NULL)
+ be_print_err(gettext("be_list: BE (%s) does not "
+ "exist\n"), be_name);
+ else
+ be_print_err(gettext("be_list: No BE's found\n"));
+ ret = BE_ERR_BE_NOENT;
+ }
+
+ *be_nodes = cb.be_nodes_head;
+
+ free(cb.be_name);
+
+ be_sort_list(be_nodes);
+
+ return (ret);
+}
+
+/*
+ * Function: be_free_list
+ * Description: Frees up all the data allocated for the list of BEs,
+ * datasets and snapshots returned by be_list.
+ * Parameters:
+ * be_node - be_nodes_t structure returned from call to be_list.
+ * Returns:
+ * none
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+void
+be_free_list(be_node_list_t *be_nodes)
+{
+ be_node_list_t *temp_node = NULL;
+ be_node_list_t *list = be_nodes;
+
+ while (list != NULL) {
+ be_dataset_list_t *datasets = list->be_node_datasets;
+ be_snapshot_list_t *snapshots = list->be_node_snapshots;
+
+ while (datasets != NULL) {
+ be_dataset_list_t *temp_ds = datasets;
+ datasets = datasets->be_next_dataset;
+ free(temp_ds->be_dataset_name);
+ free(temp_ds->be_ds_mntpt);
+ free(temp_ds->be_ds_plcy_type);
+ free(temp_ds);
+ }
+
+ while (snapshots != NULL) {
+ be_snapshot_list_t *temp_ss = snapshots;
+ snapshots = snapshots->be_next_snapshot;
+ free(temp_ss->be_snapshot_name);
+ free(temp_ss->be_snapshot_type);
+ free(temp_ss);
+ }
+
+ temp_node = list;
+ list = list->be_next_node;
+ free(temp_node->be_node_name);
+ free(temp_node->be_root_ds);
+ free(temp_node->be_rpool);
+ free(temp_node->be_mntpt);
+ free(temp_node->be_policy_type);
+ free(temp_node->be_uuid_str);
+ free(temp_node);
+ }
+}
+
+/*
+ * Function: be_get_zone_be_list
+ * Description: Finds all the BEs for this zone on the system.
+ * Parameters:
+ * zone_be_name - The name of the BE to look up.
+ * zone_be_container_ds - The dataset for the zone.
+ * zbe_nodes - A reference pointer to the list of BEs. The list
+ * structure will be allocated here and must
+ * be freed by a call to be_free_list. If there are no
+ * BEs found on the system this reference will be
+ * set to NULL.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_get_zone_be_list(
+/* LINTED */
+ char *zone_be_name,
+ char *zone_be_container_ds,
+ be_node_list_t **zbe_nodes)
+{
+ zfs_handle_t *zhp = NULL;
+ list_callback_data_t cb = { 0 };
+ int ret = BE_SUCCESS;
+
+ if (zbe_nodes == NULL)
+ return (BE_ERR_INVAL);
+
+ if (!zfs_dataset_exists(g_zfs, zone_be_container_ds,
+ ZFS_TYPE_FILESYSTEM)) {
+ return (BE_ERR_BE_NOENT);
+ }
+
+ zone_be = B_TRUE;
+
+ if ((zhp = zfs_open(g_zfs, zone_be_container_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_get_zone_be_list: failed to open "
+ "the zone BE dataset %s: %s\n"), zone_be_container_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto cleanup;
+ }
+
+ (void) strcpy(be_container_ds, zone_be_container_ds);
+
+ if (cb.be_nodes_head == NULL) {
+ if ((cb.be_nodes_head = be_list_alloc(&ret,
+ sizeof (be_node_list_t))) == NULL) {
+ ZFS_CLOSE(zhp);
+ goto cleanup;
+ }
+ cb.be_nodes = cb.be_nodes_head;
+ }
+ if (ret == 0)
+ ret = zfs_iter_filesystems(zhp, be_add_children_callback, &cb);
+ ZFS_CLOSE(zhp);
+
+ *zbe_nodes = cb.be_nodes_head;
+
+cleanup:
+ zone_be = B_FALSE;
+
+ return (ret);
+}
+
+/* ******************************************************************** */
+/* Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_get_list_callback
+ * Description: Callback function used by zfs_iter to look through all
+ * the pools on the system looking for BEs. If a BE name was
+ * specified only that BE's information will be collected and
+ * returned.
+ * Parameters:
+ * zlp - handle to the first zfs dataset. (provided by the
+ * zfs_iter_* call)
+ * data - pointer to the callback data and where we'll pass
+ * the BE information back.
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_get_list_callback(zpool_handle_t *zlp, void *data)
+{
+ list_callback_data_t *cb = (list_callback_data_t *)data;
+ char be_ds[MAXPATHLEN];
+ char *open_ds = NULL;
+ char *rpool = NULL;
+ zfs_handle_t *zhp = NULL;
+ int ret = 0;
+
+ cb->zpool_name = rpool = (char *)zpool_get_name(zlp);
+
+ /*
+ * Generate string for the BE container dataset
+ */
+ be_make_container_ds(rpool, be_container_ds,
+ sizeof (be_container_ds));
+
+ /*
+ * If a BE name was specified we use it's root dataset in place of
+ * the container dataset. This is because we only want to collect
+ * the information for the specified BE.
+ */
+ if (cb->be_name != NULL) {
+ /*
+ * Generate string for the BE root dataset
+ */
+ be_make_root_ds(rpool, cb->be_name, be_ds, sizeof (be_ds));
+ open_ds = be_ds;
+ } else {
+ open_ds = be_container_ds;
+ }
+
+ /*
+ * Check if the dataset exists
+ */
+ if (!zfs_dataset_exists(g_zfs, open_ds,
+ ZFS_TYPE_FILESYSTEM)) {
+ /*
+ * The specified dataset does not exist in this pool or
+ * there are no valid BE's in this pool. Try the next zpool.
+ */
+ zpool_close(zlp);
+ return (0);
+ }
+
+ if ((zhp = zfs_open(g_zfs, open_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_get_list_callback: failed to open "
+ "the BE dataset %s: %s\n"), open_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ zpool_close(zlp);
+ return (ret);
+ }
+
+ if (cb->be_nodes_head == NULL) {
+ if ((cb->be_nodes_head = be_list_alloc(&ret,
+ sizeof (be_node_list_t))) == NULL) {
+ ZFS_CLOSE(zhp);
+ zpool_close(zlp);
+ return (ret);
+ }
+ cb->be_nodes = cb->be_nodes_head;
+ }
+
+ /*
+ * If a BE name was specified we iterate through the datasets
+ * and snapshots for this BE only. Otherwise we will iterate
+ * through the next level of datasets to find all the BE's
+ * within the pool
+ */
+ if (cb->be_name != NULL) {
+ if ((ret = be_get_node_data(zhp, cb->be_nodes, cb->be_name,
+ rpool, cb->current_be, be_ds)) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ zpool_close(zlp);
+ return (ret);
+ }
+ ret = zfs_iter_snapshots(zhp, be_add_children_callback, cb);
+ }
+
+ if (ret == 0)
+ ret = zfs_iter_filesystems(zhp, be_add_children_callback, cb);
+ ZFS_CLOSE(zhp);
+
+ zpool_close(zlp);
+ return (ret);
+}
+
+/*
+ * Function: be_add_children_callback
+ * Description: Callback function used by zfs_iter to look through all
+ * the datasets and snapshots for each BE and add them to
+ * the lists of information to be passed back.
+ * Parameters:
+ * zhp - handle to the first zfs dataset. (provided by the
+ * zfs_iter_* call)
+ * data - pointer to the callback data and where we'll pass
+ * the BE information back.
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_add_children_callback(zfs_handle_t *zhp, void *data)
+{
+ list_callback_data_t *cb = (list_callback_data_t *)data;
+ char *str = NULL, *ds_path = NULL;
+ int ret = 0;
+
+ ds_path = str = strdup(zfs_get_name(zhp));
+
+ /*
+ * get past the end of the container dataset plus the trailing "/"
+ */
+ str = str + (strlen(be_container_ds) + 1);
+ if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && !zone_be) {
+ be_snapshot_list_t *snapshots = NULL;
+ if (cb->be_nodes->be_node_snapshots == NULL) {
+ if ((cb->be_nodes->be_node_snapshots =
+ be_list_alloc(&ret, sizeof (be_snapshot_list_t)))
+ == NULL || ret != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ cb->be_nodes->be_node_snapshots->be_next_snapshot =
+ NULL;
+ snapshots = cb->be_nodes->be_node_snapshots;
+ } else {
+ for (snapshots = cb->be_nodes->be_node_snapshots;
+ snapshots != NULL;
+ snapshots = snapshots->be_next_snapshot) {
+ if (snapshots->be_next_snapshot != NULL)
+ continue;
+ /*
+ * We're at the end of the list add the
+ * new snapshot.
+ */
+ if ((snapshots->be_next_snapshot =
+ be_list_alloc(&ret,
+ sizeof (be_snapshot_list_t))) == NULL ||
+ ret != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ snapshots = snapshots->be_next_snapshot;
+ snapshots->be_next_snapshot = NULL;
+ break;
+ }
+ }
+ if ((ret = be_get_ss_data(zhp, str, snapshots,
+ cb->be_nodes)) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ } else if (strchr(str, '/') == NULL) {
+ if (cb->be_nodes->be_node_name != NULL) {
+ if ((cb->be_nodes->be_next_node =
+ be_list_alloc(&ret, sizeof (be_node_list_t))) ==
+ NULL || ret != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ cb->be_nodes = cb->be_nodes->be_next_node;
+ cb->be_nodes->be_next_node = NULL;
+ }
+
+ /*
+ * If this is a zone root dataset then we only need
+ * the name of the zone BE at this point. We grab that
+ * and return.
+ */
+ if (zone_be) {
+ ret = be_get_zone_node_data(cb->be_nodes, str);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ if ((ret = be_get_node_data(zhp, cb->be_nodes, str,
+ cb->zpool_name, cb->current_be, ds_path)) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ } else if (strchr(str, '/') != NULL && !zone_be) {
+ be_dataset_list_t *datasets = NULL;
+ if (cb->be_nodes->be_node_datasets == NULL) {
+ if ((cb->be_nodes->be_node_datasets =
+ be_list_alloc(&ret, sizeof (be_dataset_list_t)))
+ == NULL || ret != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ cb->be_nodes->be_node_datasets->be_next_dataset = NULL;
+ datasets = cb->be_nodes->be_node_datasets;
+ } else {
+ for (datasets = cb->be_nodes->be_node_datasets;
+ datasets != NULL;
+ datasets = datasets->be_next_dataset) {
+ if (datasets->be_next_dataset != NULL)
+ continue;
+ /*
+ * We're at the end of the list add
+ * the new dataset.
+ */
+ if ((datasets->be_next_dataset =
+ be_list_alloc(&ret,
+ sizeof (be_dataset_list_t)))
+ == NULL || ret != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ datasets = datasets->be_next_dataset;
+ datasets->be_next_dataset = NULL;
+ break;
+ }
+ }
+
+ if ((ret = be_get_ds_data(zhp, str,
+ datasets, cb->be_nodes)) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+ ret = zfs_iter_children(zhp, be_add_children_callback, cb);
+ if (ret != 0) {
+ be_print_err(gettext("be_add_children_callback: "
+ "encountered error: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ }
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_sort_list
+ * Description: Sort BE node list
+ * Parameters:
+ * pointer to address of list head
+ * Returns:
+ * nothing
+ * Side effect:
+ * node list sorted by name
+ * Scope:
+ * Private
+ */
+static void
+be_sort_list(be_node_list_t **pstart)
+{
+ size_t ibe, nbe;
+ be_node_list_t *p = NULL;
+ be_node_list_t **ptrlist = NULL;
+
+ if (pstart == NULL)
+ return;
+ /* build array of linked list BE struct pointers */
+ for (p = *pstart, nbe = 0; p != NULL; nbe++, p = p->be_next_node) {
+ ptrlist = realloc(ptrlist,
+ sizeof (be_node_list_t *) * (nbe + 2));
+ ptrlist[nbe] = p;
+ }
+ if (nbe == 0)
+ return;
+ /* in-place list quicksort using qsort(3C) */
+ if (nbe > 1) /* no sort if less than 2 BEs */
+ qsort(ptrlist, nbe, sizeof (be_node_list_t *),
+ be_qsort_compare_BEs);
+
+ ptrlist[nbe] = NULL; /* add linked list terminator */
+ *pstart = ptrlist[0]; /* set new linked list header */
+ /* for each BE in list */
+ for (ibe = 0; ibe < nbe; ibe++) {
+ size_t k, ns; /* subordinate index, count */
+
+ /* rewrite list pointer chain, including terminator */
+ ptrlist[ibe]->be_next_node = ptrlist[ibe + 1];
+ /* sort subordinate snapshots */
+ if (ptrlist[ibe]->be_node_num_snapshots > 1) {
+ const size_t nmax = ptrlist[ibe]->be_node_num_snapshots;
+ be_snapshot_list_t ** const slist =
+ malloc(sizeof (be_snapshot_list_t *) * (nmax + 1));
+ be_snapshot_list_t *p;
+
+ if (slist == NULL)
+ continue;
+ /* build array of linked list snapshot struct ptrs */
+ for (ns = 0, p = ptrlist[ibe]->be_node_snapshots;
+ ns < nmax && p != NULL;
+ ns++, p = p->be_next_snapshot) {
+ slist[ns] = p;
+ }
+ if (ns < 2)
+ goto end_snapshot;
+ slist[ns] = NULL; /* add terminator */
+ /* in-place list quicksort using qsort(3C) */
+ qsort(slist, ns, sizeof (be_snapshot_list_t *),
+ be_qsort_compare_snapshots);
+ /* rewrite list pointer chain, including terminator */
+ ptrlist[ibe]->be_node_snapshots = slist[0];
+ for (k = 0; k < ns; k++)
+ slist[k]->be_next_snapshot = slist[k + 1];
+end_snapshot:
+ free(slist);
+ }
+ /* sort subordinate datasets */
+ if (ptrlist[ibe]->be_node_num_datasets > 1) {
+ const size_t nmax = ptrlist[ibe]->be_node_num_datasets;
+ be_dataset_list_t ** const slist =
+ malloc(sizeof (be_dataset_list_t *) * (nmax + 1));
+ be_dataset_list_t *p;
+
+ if (slist == NULL)
+ continue;
+ /* build array of linked list dataset struct ptrs */
+ for (ns = 0, p = ptrlist[ibe]->be_node_datasets;
+ ns < nmax && p != NULL;
+ ns++, p = p->be_next_dataset) {
+ slist[ns] = p;
+ }
+ if (ns < 2) /* subordinate datasets < 2 - no sort */
+ goto end_dataset;
+ slist[ns] = NULL; /* add terminator */
+ /* in-place list quicksort using qsort(3C) */
+ qsort(slist, ns, sizeof (be_dataset_list_t *),
+ be_qsort_compare_datasets);
+ /* rewrite list pointer chain, including terminator */
+ ptrlist[ibe]->be_node_datasets = slist[0];
+ for (k = 0; k < ns; k++)
+ slist[k]->be_next_dataset = slist[k + 1];
+end_dataset:
+ free(slist);
+ }
+ }
+free:
+ free(ptrlist);
+}
+
+/*
+ * Function: be_qsort_compare_BEs
+ * Description: lexical compare of BE names for qsort(3C)
+ * Parameters:
+ * x,y - BEs with names to compare
+ * Returns:
+ * positive if y>x, negative if x>y, 0 if equal
+ * Scope:
+ * Private
+ */
+static int
+be_qsort_compare_BEs(const void *x, const void *y)
+{
+ be_node_list_t *p = *(be_node_list_t **)x;
+ be_node_list_t *q = *(be_node_list_t **)y;
+
+ if (p == NULL || p->be_node_name == NULL)
+ return (1);
+ if (q == NULL || q->be_node_name == NULL)
+ return (-1);
+ return (strcmp(p->be_node_name, q->be_node_name));
+}
+
+/*
+ * Function: be_qsort_compare_snapshots
+ * Description: lexical compare of BE names for qsort(3C)
+ * Parameters:
+ * x,y - BE snapshots with names to compare
+ * Returns:
+ * positive if y>x, negative if x>y, 0 if equal
+ * Scope:
+ * Private
+ */
+static int
+be_qsort_compare_snapshots(const void *x, const void *y)
+{
+ be_snapshot_list_t *p = *(be_snapshot_list_t **)x;
+ be_snapshot_list_t *q = *(be_snapshot_list_t **)y;
+
+ if (p == NULL || p->be_snapshot_name == NULL)
+ return (1);
+ if (q == NULL || q->be_snapshot_name == NULL)
+ return (-1);
+ return (strcmp(p->be_snapshot_name, q->be_snapshot_name));
+}
+
+/*
+ * Function: be_qsort_compare_datasets
+ * Description: lexical compare of dataset names for qsort(3C)
+ * Parameters:
+ * x,y - BE snapshots with names to compare
+ * Returns:
+ * positive if y>x, negative if x>y, 0 if equal
+ * Scope:
+ * Private
+ */
+static int
+be_qsort_compare_datasets(const void *x, const void *y)
+{
+ be_dataset_list_t *p = *(be_dataset_list_t **)x;
+ be_dataset_list_t *q = *(be_dataset_list_t **)y;
+
+ if (p == NULL || p->be_dataset_name == NULL)
+ return (1);
+ if (q == NULL || q->be_dataset_name == NULL)
+ return (-1);
+ return (strcmp(p->be_dataset_name, q->be_dataset_name));
+}
+
+/*
+ * Function: be_get_node_data
+ * Description: Helper function used to collect all the information to fill
+ * in the be_node_list structure to be returned by be_list.
+ * Parameters:
+ * zhp - Handle to the root dataset for the BE whose information
+ * we're collecting.
+ * be_node - a pointer to the node structure we're filling in.
+ * be_name - The BE name of the node whose information we're
+ * collecting.
+ * current_be - the name of the currently active BE.
+ * be_ds - The dataset name for the BE.
+ *
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_get_node_data(
+ zfs_handle_t *zhp,
+ be_node_list_t *be_node,
+ char *be_name,
+ const char *rpool,
+ char *current_be,
+ char *be_ds)
+{
+ char prop_buf[MAXPATHLEN];
+ nvlist_t *userprops = NULL;
+ nvlist_t *propval = NULL;
+ char *prop_str = NULL;
+ char *grub_default_bootfs = NULL;
+ zpool_handle_t *zphp = NULL;
+ int err = 0;
+
+ if (be_node == NULL || be_name == NULL || current_be == NULL ||
+ be_ds == NULL) {
+ be_print_err(gettext("be_get_node_data: invalid arguments, "
+ "can not be NULL\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ errno = 0;
+
+ be_node->be_root_ds = strdup(be_ds);
+ if ((err = errno) != 0 || be_node->be_root_ds == NULL) {
+ be_print_err(gettext("be_get_node_data: failed to "
+ "copy root dataset name\n"));
+ return (errno_to_be_err(err));
+ }
+
+ be_node->be_node_name = strdup(be_name);
+ if ((err = errno) != 0 || be_node->be_node_name == NULL) {
+ be_print_err(gettext("be_get_node_data: failed to "
+ "copy BE name\n"));
+ return (errno_to_be_err(err));
+ }
+ if (strncmp(be_name, current_be, MAXPATHLEN) == 0)
+ be_node->be_active = B_TRUE;
+ else
+ be_node->be_active = B_FALSE;
+
+ be_node->be_rpool = strdup(rpool);
+ if (be_node->be_rpool == NULL || (err = errno) != 0) {
+ be_print_err(gettext("be_get_node_data: failed to "
+ "copy root pool name\n"));
+ return (errno_to_be_err(err));
+ }
+
+ be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED);
+
+ if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
+ be_print_err(gettext("be_get_node_data: failed to open pool "
+ "(%s): %s\n"), rpool, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ (void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf, ZFS_MAXPROPLEN,
+ NULL);
+ if (be_has_grub() &&
+ (be_default_grub_bootfs(rpool, &grub_default_bootfs)
+ == BE_SUCCESS) && grub_default_bootfs != NULL)
+ if (strcmp(grub_default_bootfs, be_ds) == 0)
+ be_node->be_active_on_boot = B_TRUE;
+ else
+ be_node->be_active_on_boot = B_FALSE;
+ else if (prop_buf != NULL && strcmp(prop_buf, be_ds) == 0)
+ be_node->be_active_on_boot = B_TRUE;
+ else
+ be_node->be_active_on_boot = B_FALSE;
+ free(grub_default_bootfs);
+ zpool_close(zphp);
+
+ /*
+ * If the dataset is mounted use the mount point
+ * returned from the zfs_is_mounted call. If the
+ * dataset is not mounted then pull the mount
+ * point information out of the zfs properties.
+ */
+ be_node->be_mounted = zfs_is_mounted(zhp,
+ &(be_node->be_mntpt));
+ if (!be_node->be_mounted) {
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf,
+ ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) == 0)
+ be_node->be_mntpt = strdup(prop_buf);
+ else
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ be_node->be_node_creation = (time_t)zfs_prop_get_int(zhp,
+ ZFS_PROP_CREATION);
+
+ /* Get all user properties used for libbe */
+ if ((userprops = zfs_get_user_props(zhp)) == NULL) {
+ be_node->be_policy_type = strdup(be_default_policy());
+ } else {
+ if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
+ &propval) != 0 || propval == NULL) {
+ be_node->be_policy_type =
+ strdup(be_default_policy());
+ } else {
+ verify(nvlist_lookup_string(propval, ZPROP_VALUE,
+ &prop_str) == 0);
+ if (prop_str == NULL || strcmp(prop_str, "-") == 0 ||
+ strcmp(prop_str, "") == 0)
+ be_node->be_policy_type =
+ strdup(be_default_policy());
+ else
+ be_node->be_policy_type = strdup(prop_str);
+ }
+
+ if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, &propval)
+ == 0 && nvlist_lookup_string(propval, ZPROP_VALUE,
+ &prop_str) == 0) {
+ be_node->be_uuid_str = strdup(prop_str);
+ }
+ }
+
+ /*
+ * Increment the dataset counter to include the root dataset
+ * of the BE.
+ */
+ be_node->be_node_num_datasets++;
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_get_ds_data
+ * Description: Helper function used by be_add_children_callback to collect
+ * the dataset related information that will be returned by
+ * be_list.
+ * Parameters:
+ * zhp - Handle to the zfs dataset whose information we're
+ * collecting.
+ * name - The name of the dataset we're processing.
+ * dataset - A pointer to the be_dataset_list structure
+ * we're filling in.
+ * node - The node structure that this dataset belongs to.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_get_ds_data(
+ zfs_handle_t *zfshp,
+ char *name,
+ be_dataset_list_t *dataset,
+ be_node_list_t *node)
+{
+ char prop_buf[ZFS_MAXPROPLEN];
+ nvlist_t *propval = NULL;
+ nvlist_t *userprops = NULL;
+ char *prop_str = NULL;
+ int err = 0;
+
+ if (zfshp == NULL || name == NULL || dataset == NULL || node == NULL) {
+ be_print_err(gettext("be_get_ds_data: invalid arguments, "
+ "can not be NULL\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ errno = 0;
+
+ dataset->be_dataset_name = strdup(name);
+ if ((err = errno) != 0) {
+ be_print_err(gettext("be_get_ds_data: failed to copy "
+ "dataset name\n"));
+ return (errno_to_be_err(err));
+ }
+
+ dataset->be_ds_space_used = zfs_prop_get_int(zfshp, ZFS_PROP_USED);
+
+ /*
+ * If the dataset is mounted use the mount point
+ * returned from the zfs_is_mounted call. If the
+ * dataset is not mounted then pull the mount
+ * point information out of the zfs properties.
+ */
+ if (!(dataset->be_ds_mounted = zfs_is_mounted(zfshp,
+ &(dataset->be_ds_mntpt)))) {
+ if (zfs_prop_get(zfshp, ZFS_PROP_MOUNTPOINT,
+ prop_buf, ZFS_MAXPROPLEN, NULL, NULL, 0,
+ B_FALSE) == 0)
+ dataset->be_ds_mntpt = strdup(prop_buf);
+ else
+ return (zfs_err_to_be_err(g_zfs));
+ }
+ dataset->be_ds_creation =
+ (time_t)zfs_prop_get_int(zfshp, ZFS_PROP_CREATION);
+
+ /*
+ * Get the user property used for the libbe
+ * cleaup policy
+ */
+ if ((userprops = zfs_get_user_props(zfshp)) == NULL) {
+ dataset->be_ds_plcy_type =
+ strdup(node->be_policy_type);
+ } else {
+ if (nvlist_lookup_nvlist(userprops,
+ BE_POLICY_PROPERTY, &propval) != 0 ||
+ propval == NULL) {
+ dataset->be_ds_plcy_type =
+ strdup(node->be_policy_type);
+ } else {
+ verify(nvlist_lookup_string(propval,
+ ZPROP_VALUE, &prop_str) == 0);
+ if (prop_str == NULL ||
+ strcmp(prop_str, "-") == 0 ||
+ strcmp(prop_str, "") == 0)
+ dataset->be_ds_plcy_type
+ = strdup(node->be_policy_type);
+ else
+ dataset->be_ds_plcy_type = strdup(prop_str);
+ }
+ }
+
+ node->be_node_num_datasets++;
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_get_ss_data
+ * Description: Helper function used by be_add_children_callback to collect
+ * the dataset related information that will be returned by
+ * be_list.
+ * Parameters:
+ * zhp - Handle to the zfs snapshot whose information we're
+ * collecting.
+ * name - The name of the snapshot we're processing.
+ * shapshot - A pointer to the be_snapshot_list structure
+ * we're filling in.
+ * node - The node structure that this snapshot belongs to.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_get_ss_data(
+ zfs_handle_t *zfshp,
+ char *name,
+ be_snapshot_list_t *snapshot,
+ be_node_list_t *node)
+{
+ nvlist_t *propval = NULL;
+ nvlist_t *userprops = NULL;
+ char *prop_str = NULL;
+ int err = 0;
+
+ if (zfshp == NULL || name == NULL || snapshot == NULL || node == NULL) {
+ be_print_err(gettext("be_get_ss_data: invalid arguments, "
+ "can not be NULL\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ errno = 0;
+
+ snapshot->be_snapshot_name = strdup(name);
+ if ((err = errno) != 0) {
+ be_print_err(gettext("be_get_ss_data: failed to copy name\n"));
+ return (errno_to_be_err(err));
+ }
+
+ snapshot->be_snapshot_creation = (time_t)zfs_prop_get_int(zfshp,
+ ZFS_PROP_CREATION);
+
+ /*
+ * Try to get this snapshot's cleanup policy from its
+ * user properties first. If not there, use default
+ * cleanup policy.
+ */
+ if ((userprops = zfs_get_user_props(zfshp)) != NULL &&
+ nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
+ &propval) == 0 && nvlist_lookup_string(propval,
+ ZPROP_VALUE, &prop_str) == 0) {
+ snapshot->be_snapshot_type =
+ strdup(prop_str);
+ } else {
+ snapshot->be_snapshot_type =
+ strdup(be_default_policy());
+ }
+
+ snapshot->be_snapshot_space_used = zfs_prop_get_int(zfshp,
+ ZFS_PROP_USED);
+
+ node->be_node_num_snapshots++;
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_list_alloc
+ * Description: Helper function used to allocate memory for the various
+ * sructures that make up a BE node.
+ * Parameters:
+ * err - Used to return any errors encountered.
+ * BE_SUCCESS - Success
+ * BE_ERR_NOMEM - Allocation failure
+ * size - The size of memory to allocate.
+ * Returns:
+ * Success - A pointer to the allocated memory
+ * Failure - NULL
+ * Scope:
+ * Private
+ */
+static void*
+be_list_alloc(int *err, size_t size)
+{
+ void *bep = NULL;
+
+ bep = calloc(1, size);
+ if (bep == NULL) {
+ be_print_err(gettext("be_list_alloc: memory "
+ "allocation failed\n"));
+ *err = BE_ERR_NOMEM;
+ }
+ *err = BE_SUCCESS;
+ return (bep);
+}
+
+/*
+ * Function: be_get_zone_node_data
+ * Description: Helper function used to collect all the information to
+ * fill in the be_node_list structure to be returned by
+ * be_get_zone_list.
+ * Parameters:
+ * be_node - a pointer to the node structure we're filling in.
+ * be_name - The BE name of the node whose information we're
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ *
+ * NOTE: This function currently only collects the zone BE name but when
+ * support for beadm/libbe in a zone is provided it will need to fill
+ * in the rest of the information needed for a zone BE.
+ */
+static int
+be_get_zone_node_data(be_node_list_t *be_node, char *be_name)
+{
+ if ((be_node->be_node_name = strdup(be_name)) != NULL)
+ return (BE_SUCCESS);
+ return (BE_ERR_NOMEM);
+}
diff --git a/usr/src/lib/libbe/common/be_mount.c b/usr/src/lib/libbe/common/be_mount.c
new file mode 100644
index 0000000000..bacb5d4ab4
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_mount.c
@@ -0,0 +1,2684 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * System includes
+ */
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfstab.h>
+#include <sys/zone.h>
+#include <sys/mkdev.h>
+#include <unistd.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+#define BE_TMP_MNTPNT "/tmp/.be.XXXXXX"
+
+typedef struct dir_data {
+ char *dir;
+ char *ds;
+} dir_data_t;
+
+/* Private function prototypes */
+static int be_mount_callback(zfs_handle_t *, void *);
+static int be_unmount_callback(zfs_handle_t *, void *);
+static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
+static int fix_mountpoint(zfs_handle_t *);
+static int fix_mountpoint_callback(zfs_handle_t *, void *);
+static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
+ boolean_t);
+static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *);
+static int loopback_mount_zonepath(const char *, be_mount_data_t *);
+static int iter_shared_fs_callback(zfs_handle_t *, void *);
+static int zpool_shared_fs_callback(zpool_handle_t *, void *);
+static int unmount_shared_fs(be_unmount_data_t *);
+static int add_to_fs_list(be_fs_list_data_t *, const char *);
+static int be_mount_root(zfs_handle_t *, char *);
+static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *);
+static int be_mount_zones(zfs_handle_t *, be_mount_data_t *);
+static int be_unmount_zones(be_unmount_data_t *);
+static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *,
+ char *);
+static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
+static int be_get_ds_from_dir_callback(zfs_handle_t *, void *);
+
+
+/* ******************************************************************** */
+/* Public Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_mount
+ * Description: Mounts a BE and its subordinate datasets at a given mountpoint.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attributes are used by this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *required
+ * BE_ATTR_MOUNTPOINT *required
+ * BE_ATTR_MOUNT_FLAGS *optional
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_mount(nvlist_t *be_attrs)
+{
+ char *be_name = NULL;
+ char *mountpoint = NULL;
+ uint16_t flags = 0;
+ int ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get original BE name */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
+ != 0) {
+ be_print_err(gettext("be_mount: failed to lookup "
+ "BE_ATTR_ORIG_BE_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Validate original BE name */
+ if (!be_valid_be_name(be_name)) {
+ be_print_err(gettext("be_mount: invalid BE name %s\n"),
+ be_name);
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get mountpoint */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint)
+ != 0) {
+ be_print_err(gettext("be_mount: failed to lookup "
+ "BE_ATTR_MOUNTPOINT attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get flags */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
+ be_print_err(gettext("be_mount: failed to lookup "
+ "BE_ATTR_MOUNT_FLAGS attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ ret = _be_mount(be_name, &mountpoint, flags);
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/*
+ * Function: be_unmount
+ * Description: Unmounts a BE and its subordinate datasets.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attributes are used by this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *required
+ * BE_ATTR_UNMOUNT_FLAGS *optional
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_unmount(nvlist_t *be_attrs)
+{
+ char *be_name = NULL;
+ uint16_t flags = 0;
+ int ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get original BE name */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
+ != 0) {
+ be_print_err(gettext("be_unmount: failed to lookup "
+ "BE_ATTR_ORIG_BE_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Validate original BE name */
+ if (!be_valid_be_name(be_name)) {
+ be_print_err(gettext("be_unmount: invalid BE name %s\n"),
+ be_name);
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get unmount flags */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
+ be_print_err(gettext("be_unmount: failed to loookup "
+ "BE_ATTR_UNMOUNT_FLAGS attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ ret = _be_unmount(be_name, flags);
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/* ******************************************************************** */
+/* Semi-Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: _be_mount
+ * Description: Mounts a BE. If the altroot is not provided, this function
+ * will generate a temporary mountpoint to mount the BE at. It
+ * will return this temporary mountpoint to the caller via the
+ * altroot reference pointer passed in. This returned value is
+ * allocated on heap storage and is the repsonsibility of the
+ * caller to free.
+ * Parameters:
+ * be_name - pointer to name of BE to mount.
+ * altroot - reference pointer to altroot of where to mount BE.
+ * flags - flag indicating special handling for mounting the BE
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+_be_mount(char *be_name, char **altroot, int flags)
+{
+ be_transaction_data_t bt = { 0 };
+ be_mount_data_t md = { 0 };
+ zfs_handle_t *zhp;
+ char obe_root_ds[MAXPATHLEN];
+ char *mp = NULL;
+ char *tmp_altroot = NULL;
+ int ret = BE_SUCCESS, err = 0;
+ uuid_t uu = { 0 };
+ boolean_t gen_tmp_altroot = B_FALSE;
+
+ if (be_name == NULL || altroot == NULL)
+ return (BE_ERR_INVAL);
+
+ /* Set be_name as obe_name in bt structure */
+ bt.obe_name = be_name;
+
+ /* Find which zpool obe_name lives in */
+ if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
+ be_print_err(gettext("be_mount: failed to "
+ "find zpool for BE (%s)\n"), bt.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (err < 0) {
+ be_print_err(gettext("be_mount: zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Generate string for obe_name's root dataset */
+ be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
+ sizeof (obe_root_ds));
+ bt.obe_root_ds = obe_root_ds;
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_mount: failed to "
+ "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Make sure BE's root dataset isn't already mounted somewhere */
+ if (zfs_is_mounted(zhp, &mp)) {
+ ZFS_CLOSE(zhp);
+ be_print_err(gettext("be_mount: %s is already mounted "
+ "at %s\n"), bt.obe_name, mp != NULL ? mp : "");
+ free(mp);
+ return (BE_ERR_MOUNTED);
+ }
+
+ /*
+ * Fix this BE's mountpoint if its root dataset isn't set to
+ * either 'legacy' or '/'.
+ */
+ if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
+ be_print_err(gettext("be_mount: mountpoint check "
+ "failed for %s\n"), bt.obe_root_ds);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * If altroot not provided, create a temporary alternate root
+ * to mount on
+ */
+ if (*altroot == NULL) {
+ if ((ret = be_make_tmp_mountpoint(&tmp_altroot))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_mount: failed to "
+ "make temporary mountpoint\n"));
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ gen_tmp_altroot = B_TRUE;
+ } else {
+ tmp_altroot = *altroot;
+ }
+
+ /* Mount the BE's root file system */
+ if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
+ be_print_err(gettext("be_mount: failed to "
+ "mount BE root file system\n"));
+ if (gen_tmp_altroot)
+ free(tmp_altroot);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /* Iterate through BE's children filesystems */
+ if ((err = zfs_iter_filesystems(zhp, be_mount_callback,
+ tmp_altroot)) != 0) {
+ be_print_err(gettext("be_mount: failed to "
+ "mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot);
+ if (gen_tmp_altroot)
+ free(tmp_altroot);
+ ZFS_CLOSE(zhp);
+ return (err);
+ }
+
+ md.altroot = tmp_altroot;
+ md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
+ md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
+
+ /*
+ * Mount shared file systems if mount flag says so.
+ */
+ if (md.shared_fs) {
+ /*
+ * Mount all ZFS file systems not under the BE's root dataset
+ */
+ (void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md);
+
+ /* TODO: Mount all non-ZFS file systems - Not supported yet */
+ }
+
+ /*
+ * If we're in the global zone and the global zone has a valid uuid,
+ * mount all supported non-global zones.
+ */
+ if (getzoneid() == GLOBAL_ZONEID &&
+ !(flags & BE_MOUNT_FLAG_NO_ZONES) &&
+ be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
+ if ((ret = be_mount_zones(zhp, &md)) != BE_SUCCESS) {
+ (void) _be_unmount(bt.obe_name, 0);
+ if (gen_tmp_altroot)
+ free(tmp_altroot);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+
+ ZFS_CLOSE(zhp);
+
+ /*
+ * If a NULL altroot was passed in, pass the generated altroot
+ * back to the caller in altroot.
+ */
+ if (gen_tmp_altroot)
+ *altroot = tmp_altroot;
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: _be_unmount
+ * Description: Unmount a BE.
+ * Parameters:
+ * be_name - pointer to name of BE to unmount.
+ * flags - flags for unmounting the BE.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+_be_unmount(char *be_name, int flags)
+{
+ be_transaction_data_t bt = { 0 };
+ be_unmount_data_t ud = { 0 };
+ zfs_handle_t *zhp;
+ uuid_t uu = { 0 };
+ char obe_root_ds[MAXPATHLEN];
+ char mountpoint[MAXPATHLEN];
+ char *mp = NULL;
+ int ret = BE_SUCCESS;
+ int zret = 0;
+
+ if (be_name == NULL)
+ return (BE_ERR_INVAL);
+
+ /* Set be_name as obe_name in bt structure */
+ bt.obe_name = be_name;
+
+ /* Find which zpool obe_name lives in */
+ if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
+ be_print_err(gettext("be_unmount: failed to "
+ "find zpool for BE (%s)\n"), bt.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_unmount: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+
+ /* Generate string for obe_name's root dataset */
+ be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
+ sizeof (obe_root_ds));
+ bt.obe_root_ds = obe_root_ds;
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_unmount: failed to "
+ "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+
+ /* Make sure BE's root dataset is mounted somewhere */
+ if (!zfs_is_mounted(zhp, &mp)) {
+
+ be_print_err(gettext("be_unmount: "
+ "(%s) not mounted\n"), bt.obe_name);
+
+ /*
+ * BE is not mounted, fix this BE's mountpoint if its root
+ * dataset isn't set to either 'legacy' or '/'.
+ */
+ if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
+ be_print_err(gettext("be_unmount: mountpoint check "
+ "failed for %s\n"), bt.obe_root_ds);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (BE_SUCCESS);
+ }
+
+ /*
+ * If we didn't get a mountpoint from the zfs_is_mounted call,
+ * try and get it from its property.
+ */
+ if (mp == NULL) {
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("be_unmount: failed to "
+ "get mountpoint of (%s)\n"), bt.obe_name);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_ZFS);
+ }
+ } else {
+ (void) strlcpy(mountpoint, mp, sizeof (mountpoint));
+ free(mp);
+ }
+
+ /* If BE mounted as current root, fail */
+ if (strcmp(mountpoint, "/") == 0) {
+ be_print_err(gettext("be_unmount: "
+ "cannot unmount currently running BE\n"));
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_UMOUNT_CURR_BE);
+ }
+
+ ud.altroot = mountpoint;
+ ud.force = flags & BE_UNMOUNT_FLAG_FORCE;
+
+ /* Unmount all supported non-global zones if we're in the global zone */
+ if (getzoneid() == GLOBAL_ZONEID &&
+ be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
+ if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+
+ /* TODO: Unmount all non-ZFS file systems - Not supported yet */
+
+ /* Unmount all ZFS file systems not under the BE root dataset */
+ if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) {
+ be_print_err(gettext("be_unmount: failed to "
+ "unmount shared file systems\n"));
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /* Unmount all children datasets under the BE's root dataset */
+ if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback,
+ &ud)) != 0) {
+ be_print_err(gettext("be_unmount: failed to "
+ "unmount BE (%s)\n"), bt.obe_name);
+ ZFS_CLOSE(zhp);
+ return (zret);
+ }
+
+ /* Unmount this BE's root filesystem */
+ if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp);
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_mount_zone_root
+ * Description: Mounts the zone root dataset for a zone.
+ * Parameters:
+ * zfs - zfs_handle_t pointer to zone root dataset
+ * md - be_mount_data_t pointer to data for zone to be mounted
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md)
+{
+ char mountpoint[MAXPATHLEN];
+ int err = 0;
+
+ /* Get mountpoint property of dataset */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("be_mount_zone_root: failed to "
+ "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Make sure zone's root dataset is set to 'legacy'. This is
+ * currently a requirement in this implementation of zones
+ * support.
+ */
+ if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
+ be_print_err(gettext("be_mount_zone_root: "
+ "zone root dataset mountpoint is not 'legacy'\n"));
+ return (BE_ERR_ZONE_ROOT_NOT_LEGACY);
+ }
+
+ /*
+ * Legacy mount the zone root dataset.
+ *
+ * As a workaround for 6176743, we mount the zone's root with the
+ * MS_OVERLAY option in case an alternate BE is mounted, and we're
+ * mounting the root for the zone from the current BE here. When an
+ * alternate BE is mounted, it ties up the zone's zoneroot directory
+ * for the current BE since the zone's zonepath is loopback mounted
+ * from the current BE.
+ *
+ * TODO: The MS_OVERLAY option needs to be removed when 6176743
+ * is fixed.
+ */
+ if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS,
+ NULL, 0, NULL, 0) != 0) {
+ err = errno;
+ be_print_err(gettext("be_mount_zone_root: failed to "
+ "legacy mount zone root dataset (%s) at %s\n"),
+ zfs_get_name(zhp), md->altroot);
+ return (errno_to_be_err(err));
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_unmount_zone_root
+ * Description: Unmounts the zone root dataset for a zone.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to zone root dataset
+ * ud - be_unmount_data_t pointer to data for zone to be unmounted
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wise use only)
+ */
+int
+be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
+{
+ char mountpoint[MAXPATHLEN];
+
+ /* Unmount the dataset */
+ if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
+ be_print_err(gettext("be_unmount_zone_root: failed to "
+ "unmount zone root dataset %s: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Get the current mountpoint property for the zone root dataset */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("be_unmount_zone_root: failed to "
+ "get mountpoint property for zone root dataset (%s): %s\n"),
+ zfs_get_name(zhp), libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* If mountpoint not already set to 'legacy', set it to 'legacy' */
+ if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
+ if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ ZFS_MOUNTPOINT_LEGACY) != 0) {
+ be_print_err(gettext("be_unmount_zone_root: "
+ "failed to set mountpoint of zone root dataset "
+ "%s to 'legacy': %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_get_legacy_fs
+ * Description: This function iterates through all non-shared file systems
+ * of a BE and finds the ones with a legacy mountpoint. For
+ * those file systems, it reads the BE's vfstab to get the
+ * mountpoint. If found, it adds that file system to the
+ * be_fs_list_data_t passed in.
+ *
+ * This function can be used to gather legacy mounted file systems
+ * for both global BEs and non-global zone BEs. To get data for
+ * a non-global zone BE, the zoneroot_ds and zoneroot parameters
+ * will be specified, otherwise they should be set to NULL.
+ * Parameters:
+ * be_name - global BE name from which to get legacy file
+ * system list.
+ * be_root_ds - root dataset of global BE.
+ * zoneroot_ds - root dataset of zone.
+ * zoneroot - zoneroot path of zone.
+ * fld - be_fs_list_data_t pointer.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds,
+ char *zoneroot, be_fs_list_data_t *fld)
+{
+ zfs_handle_t *zhp = NULL;
+ char mountpoint[MAXPATHLEN];
+ boolean_t mounted_here = B_FALSE;
+ boolean_t zone_mounted_here = B_FALSE;
+ int ret = BE_SUCCESS, err = 0;
+
+ if (be_name == NULL || be_root_ds == NULL || fld == NULL)
+ return (BE_ERR_INVAL);
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_get_legacy_fs: failed to "
+ "open BE root dataset (%s): %s\n"), be_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ return (ret);
+ }
+
+ /* If BE is not already mounted, mount it. */
+ if (!zfs_is_mounted(zhp, &fld->altroot)) {
+ if ((ret = _be_mount(be_name, &fld->altroot,
+ zoneroot_ds ? BE_MOUNT_FLAG_NULL :
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_legacy_fs: "
+ "failed to mount BE %s\n"), be_name);
+ goto cleanup;
+ }
+
+ mounted_here = B_TRUE;
+ } else if (fld->altroot == NULL) {
+ be_print_err(gettext("be_get_legacy_fs: failed to "
+ "get altroot of mounted BE %s: %s\n"),
+ be_name, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto cleanup;
+ }
+
+ /*
+ * If a zone root dataset was passed in, we're wanting to get
+ * legacy mounted file systems for that zone, not the global
+ * BE.
+ */
+ if (zoneroot_ds != NULL) {
+ be_mount_data_t zone_md = { 0 };
+
+ /* Close off handle to global BE's root dataset */
+ ZFS_CLOSE(zhp);
+
+ /* Get handle to zone's root dataset */
+ if ((zhp = zfs_open(g_zfs, zoneroot_ds,
+ ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_get_legacy_fs: failed to "
+ "open zone BE root dataset (%s): %s\n"),
+ zoneroot_ds, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto cleanup;
+ }
+
+ /* Make sure the zone we're looking for is mounted */
+ if (!zfs_is_mounted(zhp, &zone_md.altroot)) {
+ char zone_altroot[MAXPATHLEN];
+
+ /* Generate alternate root path for zone */
+ (void) snprintf(zone_altroot, sizeof (zone_altroot),
+ "%s%s", fld->altroot, zoneroot);
+ if ((zone_md.altroot = strdup(zone_altroot)) == NULL) {
+ be_print_err(gettext("be_get_legacy_fs: "
+ "memory allocation failed\n"));
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+
+ if ((ret = be_mount_zone_root(zhp, &zone_md))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_get_legacy_fs: "
+ "failed to mount zone root %s\n"),
+ zoneroot_ds);
+ free(zone_md.altroot);
+ zone_md.altroot = NULL;
+ goto cleanup;
+ }
+ zone_mounted_here = B_TRUE;
+ }
+
+ free(fld->altroot);
+ fld->altroot = zone_md.altroot;
+ }
+
+ /*
+ * If the root dataset is in the vfstab with a mountpoint of "/",
+ * add it to the list
+ */
+ if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp),
+ mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) {
+ if (strcmp(mountpoint, "/") == 0) {
+ if (add_to_fs_list(fld, zfs_get_name(zhp))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_get_legacy_fs: "
+ "failed to add %s to fs list\n"),
+ zfs_get_name(zhp));
+ ret = BE_ERR_INVAL;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* Iterate subordinate file systems looking for legacy mounts */
+ if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
+ fld)) != 0) {
+ be_print_err(gettext("be_get_legacy_fs: "
+ "failed to iterate %s to get legacy mounts\n"),
+ zfs_get_name(zhp));
+ }
+
+cleanup:
+ /* If we mounted the zone BE, unmount it */
+ if (zone_mounted_here) {
+ be_unmount_data_t zone_ud = { 0 };
+
+ zone_ud.altroot = fld->altroot;
+ zone_ud.force = B_TRUE;
+ if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_legacy_fs: "
+ "failed to unmount zone root %s\n"),
+ zoneroot_ds);
+ if (ret == BE_SUCCESS)
+ ret = err;
+ }
+ }
+
+ /* If we mounted this BE, unmount it */
+ if (mounted_here) {
+ if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_legacy_fs: "
+ "failed to unmount %s\n"), be_name);
+ if (ret == BE_SUCCESS)
+ ret = err;
+ }
+ }
+
+ ZFS_CLOSE(zhp);
+
+ free(fld->altroot);
+ fld->altroot = NULL;
+
+ return (ret);
+}
+
+/*
+ * Function: be_free_fs_list
+ * Description: Function used to free the members of a be_fs_list_data_t
+ * structure.
+ * Parameters:
+ * fld - be_fs_list_data_t pointer to free.
+ * Returns:
+ * None
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+void
+be_free_fs_list(be_fs_list_data_t *fld)
+{
+ int i;
+
+ if (fld == NULL)
+ return;
+
+ free(fld->altroot);
+
+ if (fld->fs_list == NULL)
+ return;
+
+ for (i = 0; i < fld->fs_num; i++)
+ free(fld->fs_list[i]);
+
+ free(fld->fs_list);
+}
+
+/*
+ * Function: be_get_ds_from_dir(char *dir)
+ * Description: Given a directory path, find the underlying dataset mounted
+ * at that directory path if there is one. The returned name
+ * is allocated in heap storage, so the caller is responsible
+ * for freeing it.
+ * Parameters:
+ * dir - char pointer of directory to find.
+ * Returns:
+ * NULL - if directory is not mounted from a dataset.
+ * name of dataset mounted at dir.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+char *
+be_get_ds_from_dir(char *dir)
+{
+ dir_data_t dd = { 0 };
+ char resolved_dir[MAXPATHLEN];
+
+ /* Make sure length of dir is within the max length */
+ if (dir == NULL || strlen(dir) >= MAXPATHLEN)
+ return (NULL);
+
+ /* Resolve dir in case its lofs mounted */
+ (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir));
+ z_resolve_lofs(resolved_dir, sizeof (resolved_dir));
+
+ dd.dir = resolved_dir;
+
+ (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
+
+ return (dd.ds);
+}
+
+/*
+ * Function: be_make_tmp_mountpoint
+ * Description: This function generates a random temporary mountpoint
+ * and creates that mountpoint directory. It returns the
+ * mountpoint in heap storage, so the caller is responsible
+ * for freeing it.
+ * Parameters:
+ * tmp_mp - reference to pointer of where to store generated
+ * temporary mountpoint.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_make_tmp_mountpoint(char **tmp_mp)
+{
+ int err = 0;
+
+ if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) {
+ be_print_err(gettext("be_make_tmp_mountpoint: "
+ "malloc failed\n"));
+ return (BE_ERR_NOMEM);
+ }
+ (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1);
+ if (mkdtemp(*tmp_mp) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed "
+ "for %s: %s\n"), *tmp_mp, strerror(err));
+ free(*tmp_mp);
+ *tmp_mp = NULL;
+ return (errno_to_be_err(err));
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_mount_pool
+ * Description: This function determines if the pool's datase is mounted
+ * and if not it is used to mount the pool's dataset. The
+ * function returns the current mountpoint if we are able
+ * to mount the dataset.
+ * Parameters:
+ * zhp - handle to the pool's dataset
+ * tmp_mntpnt - The temporary mountpoint that the pool's
+ * dataset is mounted on. This is set only
+ * if the attempt to mount the dataset at it's
+ * set mountpoint fails, and we've used a
+ * temporary mount point for this dataset. It
+ * is expected that the caller will free this
+ * memory.
+ * orig_mntpnt - The original mountpoint for the pool. If a
+ * temporary mount point was needed this will
+ * be used to reset the mountpoint property to
+ * it's original mountpoint. It is expected that
+ * the caller will free this memory.
+ * pool_mounted - This flag indicates that the pool was mounted
+ * in this function.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_mount_pool(
+ zfs_handle_t *zhp,
+ char **tmp_mntpnt,
+ char **orig_mntpnt,
+ boolean_t *pool_mounted)
+{
+
+ char mountpoint[MAXPATHLEN];
+ int ret = 0;
+
+ *tmp_mntpnt = NULL;
+ *orig_mntpnt = NULL;
+ *pool_mounted = B_FALSE;
+
+ if (!zfs_is_mounted(zhp, NULL)) {
+ if (zfs_mount(zhp, NULL, 0) != 0) {
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("be_mount_pool: failed to "
+ "get mountpoint of (%s): %s\n"),
+ zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+ if ((*orig_mntpnt = strdup(mountpoint)) == NULL) {
+ be_print_err(gettext("be_mount_pool: memory "
+ "allocation failed\n"));
+ return (BE_ERR_NOMEM);
+ }
+ /*
+ * attempt to mount on a temp mountpoint
+ */
+ if ((ret = be_make_tmp_mountpoint(tmp_mntpnt))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_mount_pool: failed "
+ "to make temporary mountpoint\n"));
+ free(*orig_mntpnt);
+ *orig_mntpnt = NULL;
+ return (ret);
+ }
+
+ if (zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ *tmp_mntpnt) != 0) {
+ be_print_err(gettext("be_mount_pool: failed "
+ "to set mountpoint of pool dataset %s to "
+ "%s: %s\n"), zfs_get_name(zhp),
+ *orig_mntpnt,
+ libzfs_error_description(g_zfs));
+ free(*tmp_mntpnt);
+ free(*orig_mntpnt);
+ *orig_mntpnt = NULL;
+ *tmp_mntpnt = NULL;
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ if (zfs_mount(zhp, NULL, 0) != 0) {
+ be_print_err(gettext("be_mount_pool: failed "
+ "to mount dataset %s at %s: %s\n"),
+ zfs_get_name(zhp), *tmp_mntpnt,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ if (zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ mountpoint) != 0) {
+ be_print_err(gettext("be_mount_pool: "
+ "failed to set mountpoint of pool "
+ "dataset %s to %s: %s\n"),
+ zfs_get_name(zhp), *tmp_mntpnt,
+ libzfs_error_description(g_zfs));
+ }
+ free(*tmp_mntpnt);
+ free(*orig_mntpnt);
+ *orig_mntpnt = NULL;
+ *tmp_mntpnt = NULL;
+ return (ret);
+ }
+ }
+ *pool_mounted = B_TRUE;
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_unmount_pool
+ * Description: This function is used to unmount the pool's dataset if we
+ * mounted it previously using be_mount_pool().
+ * Parameters:
+ * zhp - handle to the pool's dataset
+ * tmp_mntpnt - If a temprary mount point was used this will
+ * be set. Since this was created in be_mount_pool
+ * we will need to clean it up here.
+ * orig_mntpnt - The original mountpoint for the pool. This is
+ * used to set the dataset mountpoint property
+ * back to it's original value in the case where a
+ * temporary mountpoint was used.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_unmount_pool(
+ zfs_handle_t *zhp,
+ char *tmp_mntpnt,
+ char *orig_mntpnt)
+{
+ if (zfs_unmount(zhp, NULL, 0) != 0) {
+ be_print_err(gettext("be_unmount_pool: failed to "
+ "unmount pool (%s): %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+ if (orig_mntpnt != NULL) {
+ if (tmp_mntpnt != NULL &&
+ strcmp(orig_mntpnt, tmp_mntpnt) != 0) {
+ (void) rmdir(tmp_mntpnt);
+ }
+ if (zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ orig_mntpnt) != 0) {
+ be_print_err(gettext("be_unmount_pool: failed "
+ "to set the mountpoint for dataset (%s) to "
+ "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+ }
+
+ return (BE_SUCCESS);
+}
+
+/* ******************************************************************** */
+/* Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_mount_callback
+ * Description: Callback function used to iterate through all of a BE's
+ * subordinate file systems and to mount them accordingly.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current file system being
+ * processed.
+ * data - pointer to the altroot of where to mount BE.
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_mount_callback(zfs_handle_t *zhp, void *data)
+{
+ zprop_source_t sourcetype;
+ const char *fs_name = zfs_get_name(zhp);
+ char source[ZFS_MAXNAMELEN];
+ char *altroot = data;
+ char zhp_mountpoint[MAXPATHLEN];
+ char mountpoint[MAXPATHLEN];
+ int ret = 0;
+
+ /* Get dataset's mountpoint and source values */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
+ sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source),
+ B_FALSE) != 0) {
+ be_print_err(gettext("be_mount_callback: failed to "
+ "get mountpoint and sourcetype for %s\n"),
+ fs_name);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_ZFS);
+ }
+
+ /*
+ * Set this filesystem's 'canmount' property to 'noauto' just incase
+ * it's been set 'on'. We do this so that when we change its
+ * mountpoint zfs won't immediately try to mount it.
+ */
+ if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
+ be_print_err(gettext("be_mount_callback: failed to "
+ "set canmount to 'noauto' (%s)\n"), fs_name);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_ZFS);
+ }
+
+ /*
+ * If the mountpoint is none, there's nothing to do, goto next.
+ * If the mountpoint is legacy, legacy mount it with mount(2).
+ * If the mountpoint is inherited, its mountpoint should
+ * already be set. If it's not, then explicitly fix-up
+ * the mountpoint now by appending its explicitly set
+ * mountpoint value to the BE mountpoint.
+ */
+ if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) {
+ goto next;
+ } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
+ /*
+ * If the mountpoint is set to 'legacy', we need to
+ * dig into this BE's vfstab to figure out where to
+ * mount it, and just mount it via mount(2).
+ */
+ if (get_mountpoint_from_vfstab(altroot, fs_name,
+ mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) {
+
+ /* Legacy mount the file system */
+ if (mount(fs_name, mountpoint, MS_DATA,
+ MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) {
+ be_print_err(
+ gettext("be_mount_callback: "
+ "failed to mount %s on %s\n"),
+ fs_name, mountpoint);
+ }
+ } else {
+ be_print_err(
+ gettext("be_mount_callback: "
+ "no entry for %s in vfstab, "
+ "skipping ...\n"), fs_name);
+ }
+
+ goto next;
+
+ } else if (sourcetype & ZPROP_SRC_INHERITED) {
+ /*
+ * If the mountpoint is inherited, its parent should have
+ * already been processed so its current mountpoint value
+ * is what its mountpoint ought to be.
+ */
+ (void) strlcpy(mountpoint, zhp_mountpoint, sizeof (mountpoint));
+ } else if (sourcetype & ZPROP_SRC_LOCAL) {
+ /*
+ * Else process dataset with explicitly set mountpoint.
+ */
+ (void) snprintf(mountpoint, sizeof (mountpoint),
+ "%s%s", altroot, zhp_mountpoint);
+
+ /* Set the new mountpoint for the dataset */
+ if (zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ mountpoint)) {
+ be_print_err(gettext("be_mount_callback: "
+ "failed to set mountpoint for %s to "
+ "%s\n"), fs_name, mountpoint);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_ZFS);
+ }
+ } else {
+ be_print_err(gettext("be_mount_callback: "
+ "mountpoint sourcetype of %s is %d, skipping ...\n"),
+ fs_name, sourcetype);
+
+ goto next;
+ }
+
+ /* Mount this filesystem */
+ if (zfs_mount(zhp, NULL, 0) != 0) {
+ be_print_err(gettext("be_mount_callback: failed to "
+ "mount dataset %s at %s: %s\n"), fs_name, mountpoint,
+ libzfs_error_description(g_zfs));
+ /*
+ * Set this filesystem's 'mountpoint' property back to what
+ * it was
+ */
+ if (sourcetype & ZPROP_SRC_LOCAL &&
+ strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
+ (void) zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ zhp_mountpoint);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_MOUNT);
+ }
+
+next:
+ /* Iterate through this dataset's children and mount them */
+ if ((ret = zfs_iter_filesystems(zhp, be_mount_callback,
+ altroot)) != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: be_unmount_callback
+ * Description: Callback function used to iterate through all of a BE's
+ * subordinate file systems and to unmount them.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current file system being
+ * processed.
+ * data - pointer to the mountpoint of where BE is mounted.
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_unmount_callback(zfs_handle_t *zhp, void *data)
+{
+ be_unmount_data_t *ud = data;
+ zprop_source_t sourcetype;
+ const char *fs_name = zfs_get_name(zhp);
+ char source[ZFS_MAXNAMELEN];
+ char mountpoint[MAXPATHLEN];
+ char *zhp_mountpoint;
+ int ret = 0;
+
+ /* Iterate down this dataset's children first */
+ if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) {
+ ret = BE_ERR_UMOUNT;
+ goto done;
+ }
+
+ /* Is dataset even mounted ? */
+ if (!zfs_is_mounted(zhp, NULL))
+ goto done;
+
+ /* Unmount this file system */
+ if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
+ be_print_err(gettext("be_unmount_callback: "
+ "failed to unmount %s: %s\n"), fs_name,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* Get dataset's current mountpoint and source value */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), &sourcetype, source, sizeof (source),
+ B_FALSE) != 0) {
+ be_print_err(gettext("be_unmount_callback: "
+ "failed to get mountpoint and sourcetype for %s: %s\n"),
+ fs_name, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (sourcetype & ZPROP_SRC_INHERITED) {
+ /*
+ * If the mountpoint is inherited we don't need to
+ * do anything. When its parent gets processed
+ * its mountpoint will be set accordingly.
+ */
+ goto done;
+ } else if (sourcetype & ZPROP_SRC_LOCAL) {
+
+ if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
+ /*
+ * If the mountpoint is set to 'legacy', its already
+ * been unmounted (from above call to zfs_unmount), and
+ * we don't need to do anything else with it.
+ */
+ goto done;
+
+ } else {
+ /*
+ * Else process dataset with explicitly set mountpoint.
+ */
+
+ /*
+ * Get this dataset's mountpoint relative to
+ * the BE's mountpoint.
+ */
+ if ((strncmp(mountpoint, ud->altroot,
+ strlen(ud->altroot)) == 0) &&
+ (mountpoint[strlen(ud->altroot)] == '/')) {
+
+ zhp_mountpoint = mountpoint +
+ strlen(ud->altroot);
+
+ /* Set this dataset's mountpoint value */
+ if (zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ zhp_mountpoint)) {
+ be_print_err(
+ gettext("be_unmount_callback: "
+ "failed to set mountpoint for "
+ "%s to %s: %s\n"), fs_name,
+ zhp_mountpoint,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ }
+ } else {
+ be_print_err(
+ gettext("be_unmount_callback: "
+ "%s not mounted under BE's altroot %s, "
+ "skipping ...\n"), fs_name, ud->altroot);
+ /*
+ * fs_name is mounted but not under the
+ * root for this BE.
+ */
+ ret = BE_ERR_INVALMOUNTPOINT;
+ }
+ }
+ } else {
+ be_print_err(gettext("be_unmount_callback: "
+ "mountpoint sourcetype of %s is %d, skipping ...\n"),
+ fs_name, sourcetype);
+ ret = BE_ERR_ZFS;
+ }
+
+done:
+ /* Set this filesystem's 'canmount' property to 'noauto' */
+ if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
+ be_print_err(gettext("be_unmount_callback: "
+ "failed to set canmount to 'noauto' (%s)\n"), fs_name);
+ if (ret == 0)
+ ret = BE_ERR_ZFS;
+ }
+
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_get_legacy_fs_callback
+ * Description: The callback function is used to iterate through all
+ * non-shared file systems of a BE, finding ones that have
+ * a legacy mountpoint and an entry in the BE's vfstab.
+ * It adds these file systems to the callback data.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current file system being
+ * processed.
+ * data - be_fs_list_data_t pointer
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data)
+{
+ be_fs_list_data_t *fld = data;
+ const char *fs_name = zfs_get_name(zhp);
+ char zhp_mountpoint[MAXPATHLEN];
+ char mountpoint[MAXPATHLEN];
+ int ret = 0;
+
+ /* Get this dataset's mountpoint property */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
+ sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("be_get_legacy_fs_callback: "
+ "failed to get mountpoint for %s: %s\n"),
+ fs_name, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * If mountpoint is legacy, try to get its mountpoint from this BE's
+ * vfstab. If it exists in the vfstab, add this file system to the
+ * callback data.
+ */
+ if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
+ if (get_mountpoint_from_vfstab(fld->altroot, fs_name,
+ mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_legacy_fs_callback: "
+ "no entry for %s in vfstab, "
+ "skipping ...\n"), fs_name);
+
+ goto next;
+ }
+
+ /* Record file system into the callback data. */
+ if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_legacy_fs_callback: "
+ "failed to add %s to fs list\n"), mountpoint);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_NOMEM);
+ }
+ }
+
+next:
+ /* Iterate through this dataset's children file systems */
+ if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
+ fld)) != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: add_to_fs_list
+ * Description: Function used to add a file system to the fs_list array in
+ * a be_fs_list_data_t structure.
+ * Parameters:
+ * fld - be_fs_list_data_t pointer
+ * fs - file system to add
+ * Returns:
+ * BE_SUCCESS - Success
+ * 1 - Failure
+ * Scope:
+ * Private
+ */
+static int
+add_to_fs_list(be_fs_list_data_t *fld, const char *fs)
+{
+ if (fld == NULL || fs == NULL)
+ return (1);
+
+ if ((fld->fs_list = (char **)realloc(fld->fs_list,
+ sizeof (char *)*(fld->fs_num + 1))) == NULL) {
+ be_print_err(gettext("add_to_fs_list: "
+ "memory allocation failed\n"));
+ return (1);
+ }
+
+ if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) {
+ be_print_err(gettext("add_to_fs_list: "
+ "memory allocation failed\n"));
+ return (1);
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: zpool_shared_fs_callback
+ * Description: Callback function used to iterate through all existing pools
+ * to find and mount all shared filesystems. This function
+ * processes the pool's "pool data" dataset, then uses
+ * iter_shared_fs_callback to iterate through the pool's
+ * datasets.
+ * Parameters:
+ * zlp - zpool_handle_t pointer to the current pool being
+ * looked at.
+ * data - be_mount_data_t pointer
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+zpool_shared_fs_callback(zpool_handle_t *zlp, void *data)
+{
+ be_mount_data_t *md = data;
+ zfs_handle_t *zhp = NULL;
+ const char *zpool = zpool_get_name(zlp);
+ int ret = 0;
+
+ /*
+ * Get handle to pool's "pool data" dataset
+ */
+ if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("zpool_shared_fs: "
+ "failed to open pool dataset %s: %s\n"), zpool,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ zpool_close(zlp);
+ return (ret);
+ }
+
+ /* Process this pool's "pool data" dataset */
+ (void) loopback_mount_shared_fs(zhp, md);
+
+ /* Interate through this pool's children */
+ (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
+
+ ZFS_CLOSE(zhp);
+ zpool_close(zlp);
+
+ return (0);
+}
+
+/*
+ * Function: iter_shared_fs_callback
+ * Description: Callback function used to iterate through a pool's datasets
+ * to find and mount all shared filesystems. It makes sure to
+ * find the BE container dataset of the pool, if it exists, and
+ * does not process and iterate down that path.
+ *
+ * Note - This function iterates linearly down the
+ * hierarchical dataset paths and mounts things as it goes
+ * along. It does not make sure that something deeper down
+ * a dataset path has an interim mountpoint for something
+ * processed earlier.
+ *
+ * Parameters:
+ * zhp - zfs_handle_t pointer to the current dataset being
+ * processed.
+ * data - be_mount_data_t pointer
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+iter_shared_fs_callback(zfs_handle_t *zhp, void *data)
+{
+ be_mount_data_t *md = data;
+ const char *name = zfs_get_name(zhp);
+ char container_ds[MAXPATHLEN];
+ char tmp_name[MAXPATHLEN];
+ char *pool;
+
+ /* Get the pool's name */
+ (void) strlcpy(tmp_name, name, sizeof (tmp_name));
+ pool = strtok(tmp_name, "/");
+
+ if (pool) {
+ /* Get the name of this pool's container dataset */
+ be_make_container_ds(pool, container_ds,
+ sizeof (container_ds));
+
+ /*
+ * If what we're processing is this pool's BE container
+ * dataset, skip it.
+ */
+ if (strcmp(name, container_ds) == 0) {
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+ } else {
+ /* Getting the pool name failed, return error */
+ be_print_err(gettext("iter_shared_fs_callback: "
+ "failed to get pool name from %s\n"), name);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_POOL_NOENT);
+ }
+
+ /* Mount this shared filesystem */
+ (void) loopback_mount_shared_fs(zhp, md);
+
+ /* Iterate this dataset's children file systems */
+ (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
+ ZFS_CLOSE(zhp);
+
+ return (0);
+}
+
+/*
+ * Function: loopback_mount_shared_fs
+ * Description: This function loopback mounts a file system into the altroot
+ * area of the BE being mounted. Since these are shared file
+ * systems, they are expected to be already mounted for the
+ * current BE, and this function just loopback mounts them into
+ * the BE mountpoint. If they are not mounted for the current
+ * live system, they are skipped and not mounted into the BE
+ * we're mounting.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to the dataset to loopback mount
+ * md - be_mount_data_t pointer
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md)
+{
+ char zhp_mountpoint[MAXPATHLEN];
+ char mountpoint[MAXPATHLEN];
+ char *mp = NULL;
+ char optstr[MAX_MNTOPT_STR];
+ int mflag = MS_OPTIONSTR;
+ int err;
+
+ /*
+ * Check if file system is currently mounted and not delegated
+ * to a non-global zone (if we're in the global zone)
+ */
+ if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID ||
+ !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) {
+ /*
+ * If we didn't get a mountpoint from the zfs_is_mounted call,
+ * get it from the mountpoint property.
+ */
+ if (mp == NULL) {
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
+ zhp_mountpoint, sizeof (zhp_mountpoint), NULL,
+ NULL, 0, B_FALSE) != 0) {
+ be_print_err(
+ gettext("loopback_mount_shared_fs: "
+ "failed to get mountpoint property\n"));
+ return (BE_ERR_ZFS);
+ }
+ } else {
+ (void) strlcpy(zhp_mountpoint, mp,
+ sizeof (zhp_mountpoint));
+ free(mp);
+ }
+
+ (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s",
+ md->altroot, zhp_mountpoint);
+
+ /* Mount it read-only if read-write was not requested */
+ if (!md->shared_rw) {
+ mflag |= MS_RDONLY;
+ }
+
+ /* Add the "nosub" option to the mount options string */
+ (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
+
+ /* Loopback mount this dataset at the altroot */
+ if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS,
+ NULL, 0, optstr, sizeof (optstr)) != 0) {
+ err = errno;
+ be_print_err(gettext("loopback_mount_shared_fs: "
+ "failed to loopback mount %s at %s: %s\n"),
+ zhp_mountpoint, mountpoint, strerror(err));
+ return (BE_ERR_MOUNT);
+ }
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: loopback_mount_zonepath
+ * Description: This function loopback mounts a zonepath into the altroot
+ * area of the BE being mounted. Since these are shared file
+ * systems, they are expected to be already mounted for the
+ * current BE, and this function just loopback mounts them into
+ * the BE mountpoint.
+ * Parameters:
+ * zonepath - pointer to zone path in the current BE
+ * md - be_mount_data_t pointer
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md)
+{
+ FILE *fp = (FILE *)NULL;
+ struct stat st;
+ char *p;
+ char *p1;
+ char *parent_dir;
+ struct extmnttab extmtab;
+ dev_t dev = NODEV;
+ char *parentmnt;
+ char alt_parentmnt[MAXPATHLEN];
+ struct mnttab mntref;
+ char altzonepath[MAXPATHLEN];
+ char optstr[MAX_MNTOPT_STR];
+ int mflag = MS_OPTIONSTR;
+ int ret;
+ int err;
+
+ fp = fopen(MNTTAB, "r");
+ if (fp == NULL) {
+ err = errno;
+ be_print_err(gettext("loopback_mount_zonepath: "
+ "failed to open /etc/mnttab\n"));
+ return (errno_to_be_err(err));
+ }
+
+ /*
+ * before attempting the loopback mount of zonepath under altroot,
+ * we need to make sure that all intermediate file systems in the
+ * zone path are also mounted under altroot
+ */
+
+ /* get the parent directory for zonepath */
+ p = strrchr(zonepath, '/');
+ if (p != NULL && p != zonepath) {
+ if ((parent_dir = (char *)calloc(sizeof (char),
+ p - zonepath + 1)) == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto done;
+ }
+ (void) strlcpy(parent_dir, zonepath, p - zonepath + 1);
+ if (stat(parent_dir, &st) < 0) {
+ ret = errno_to_be_err(errno);
+ be_print_err(gettext("loopback_mount_zonepath: "
+ "failed to stat %s"),
+ parent_dir);
+ free(parent_dir);
+ goto done;
+ }
+ free(parent_dir);
+
+ /*
+ * After the above stat call, st.st_dev contains ID of the
+ * device over which parent dir resides.
+ * Now, search mnttab and find mount point of parent dir device.
+ */
+
+ resetmnttab(fp);
+ while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) {
+ dev = makedev(extmtab.mnt_major, extmtab.mnt_minor);
+ if (st.st_dev == dev && strcmp(extmtab.mnt_fstype,
+ MNTTYPE_ZFS) == 0) {
+ p1 = strchr(extmtab.mnt_special, '/');
+ if (p1 == NULL || strncmp(p1 + 1,
+ BE_CONTAINER_DS_NAME, 4) != 0 ||
+ (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) {
+ /*
+ * if parent dir is in a shared file
+ * system, check whether it is already
+ * loopback mounted under altroot or
+ * not. It would have been mounted
+ * already under altroot if it is in
+ * a non-shared filesystem.
+ */
+ parentmnt = strdup(extmtab.mnt_mountp);
+ (void) snprintf(alt_parentmnt,
+ sizeof (alt_parentmnt), "%s%s",
+ md->altroot, parentmnt);
+ mntref.mnt_mountp = alt_parentmnt;
+ mntref.mnt_special = parentmnt;
+ mntref.mnt_fstype = MNTTYPE_LOFS;
+ mntref.mnt_mntopts = NULL;
+ mntref.mnt_time = NULL;
+ resetmnttab(fp);
+ if (getmntany(fp, (struct mnttab *)
+ &extmtab, &mntref) != 0) {
+ ret = loopback_mount_zonepath(
+ parentmnt, md);
+ if (ret != BE_SUCCESS) {
+ free(parentmnt);
+ goto done;
+ }
+ }
+ free(parentmnt);
+ }
+ break;
+ }
+ }
+ }
+
+
+ if (!md->shared_rw) {
+ mflag |= MS_RDONLY;
+ }
+
+ (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s",
+ md->altroot, zonepath);
+
+ /* Add the "nosub" option to the mount options string */
+ (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
+
+ /* Loopback mount this dataset at the altroot */
+ if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS,
+ NULL, 0, optstr, sizeof (optstr)) != 0) {
+ err = errno;
+ be_print_err(gettext("loopback_mount_zonepath: "
+ "failed to loopback mount %s at %s: %s\n"),
+ zonepath, altzonepath, strerror(err));
+ ret = BE_ERR_MOUNT;
+ goto done;
+ }
+ ret = BE_SUCCESS;
+
+done :
+ (void) fclose(fp);
+ return (ret);
+}
+
+/*
+ * Function: unmount_shared_fs
+ * Description: This function iterates through the mnttab and finds all
+ * loopback mount entries that reside within the altroot of
+ * where the BE is mounted, and unmounts it.
+ * Parameters:
+ * ud - be_unmount_data_t pointer
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+unmount_shared_fs(be_unmount_data_t *ud)
+{
+ FILE *fp = NULL;
+ struct mnttab *table = NULL;
+ struct mnttab ent;
+ struct mnttab *entp = NULL;
+ size_t size = 0;
+ int read_chunk = 32;
+ int i;
+ int altroot_len;
+ int err = 0;
+
+ errno = 0;
+
+ /* Read in the mnttab into a table */
+ if ((fp = fopen(MNTTAB, "r")) == NULL) {
+ err = errno;
+ be_print_err(gettext("unmount_shared_fs: "
+ "failed to open mnttab\n"));
+ return (errno_to_be_err(err));
+ }
+
+ while (getmntent(fp, &ent) == 0) {
+ if (size % read_chunk == 0) {
+ table = (struct mnttab *)realloc(table,
+ (size + read_chunk) * sizeof (ent));
+ }
+ entp = &table[size++];
+
+ /*
+ * Copy over the current mnttab entry into our table,
+ * copying only the fields that we care about.
+ */
+ (void) memset(entp, 0, sizeof (*entp));
+ if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL ||
+ (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) {
+ be_print_err(gettext("unmount_shared_fs: "
+ "memory allocation failed\n"));
+ return (BE_ERR_NOMEM);
+ }
+ }
+ (void) fclose(fp);
+
+ /*
+ * Process the mnttab entries in reverse order, looking for
+ * loopback mount entries mounted under our altroot.
+ */
+ altroot_len = strlen(ud->altroot);
+ for (i = size; i > 0; i--) {
+ entp = &table[i - 1];
+
+ /* If not of type lofs, skip */
+ if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0)
+ continue;
+
+ /* If inside the altroot, unmount it */
+ if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 &&
+ entp->mnt_mountp[altroot_len] == '/') {
+ if (umount(entp->mnt_mountp) != 0) {
+ err = errno;
+ if (err == EBUSY) {
+ (void) sleep(1);
+ err = errno = 0;
+ if (umount(entp->mnt_mountp) != 0)
+ err = errno;
+ }
+ if (err != 0) {
+ be_print_err(gettext(
+ "unmount_shared_fs: "
+ "failed to unmount shared file "
+ "system %s: %s\n"),
+ entp->mnt_mountp, strerror(err));
+ return (errno_to_be_err(err));
+ }
+ }
+ }
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: get_mountpoint_from_vfstab
+ * Description: This function digs into the vfstab in the given altroot,
+ * and searches for an entry for the fs passed in. If found,
+ * it returns the mountpoint of that fs in the mountpoint
+ * buffer passed in. If the get_alt_mountpoint flag is set,
+ * it returns the mountpoint with the altroot prepended.
+ * Parameters:
+ * altroot - pointer to the alternate root location
+ * fs - pointer to the file system name to look for in the
+ * vfstab in altroot
+ * mountpoint - pointer to buffer of where the mountpoint of
+ * fs will be returned.
+ * size_mp - size of mountpoint argument
+ * get_alt_mountpoint - flag to indicate whether or not the
+ * mountpoint should be populated with the altroot
+ * prepended.
+ * Returns:
+ * BE_SUCCESS - Success
+ * 1 - Failure
+ * Scope:
+ * Private
+ */
+static int
+get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint,
+ size_t size_mp, boolean_t get_alt_mountpoint)
+{
+ struct vfstab vp;
+ FILE *fp = NULL;
+ char alt_vfstab[MAXPATHLEN];
+
+ /* Generate path to alternate root vfstab */
+ (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
+ altroot);
+
+ /* Open alternate root vfstab */
+ if ((fp = fopen(alt_vfstab, "r")) == NULL) {
+ be_print_err(gettext("get_mountpoint_from_vfstab: "
+ "failed to open vfstab (%s)\n"), alt_vfstab);
+ return (1);
+ }
+
+ if (getvfsspec(fp, &vp, (char *)fs) == 0) {
+ /*
+ * Found entry for fs, grab its mountpoint.
+ * If the flag to prepend the altroot into the mountpoint
+ * is set, prepend it. Otherwise, just return the mountpoint.
+ */
+ if (get_alt_mountpoint) {
+ (void) snprintf(mountpoint, size_mp, "%s%s", altroot,
+ vp.vfs_mountp);
+ } else {
+ (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp);
+ }
+ } else {
+ (void) fclose(fp);
+ return (1);
+ }
+
+ (void) fclose(fp);
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: fix_mountpoint_callback
+ * Description: This callback function is used to iterate through a BE's
+ * children filesystems to check if its mountpoint is currently
+ * set to be mounted at some specified altroot. If so, fix it by
+ * removing altroot from the beginning of its mountpoint.
+ *
+ * Note - There's no way to tell if a child filesystem's
+ * mountpoint isn't broken, and just happens to begin with
+ * the altroot we're looking for. In this case, this function
+ * will errantly remove the altroot portion from the beginning
+ * of this filesystem's mountpoint.
+ *
+ * Parameters:
+ * zhp - zfs_handle_t pointer to filesystem being processed.
+ * data - altroot of where BE is to be mounted.
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+fix_mountpoint_callback(zfs_handle_t *zhp, void *data)
+{
+ zprop_source_t sourcetype;
+ char source[ZFS_MAXNAMELEN];
+ char mountpoint[MAXPATHLEN];
+ char *zhp_mountpoint = NULL;
+ char *altroot = data;
+ int ret = 0;
+
+ /* Get dataset's mountpoint and source values */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), &sourcetype, source, sizeof (source),
+ B_FALSE) != 0) {
+ be_print_err(gettext("fix_mountpoint_callback: "
+ "failed to get mountpoint and sourcetype for %s\n"),
+ zfs_get_name(zhp));
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_ZFS);
+ }
+
+ /*
+ * If the mountpoint is not inherited and the mountpoint is not
+ * 'legacy', this file system potentially needs its mountpoint
+ * fixed.
+ */
+ if (!(sourcetype & ZPROP_SRC_INHERITED) &&
+ strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
+
+ /*
+ * Check if this file system's current mountpoint is
+ * under the altroot we're fixing it against.
+ */
+ if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 &&
+ mountpoint[strlen(altroot)] == '/') {
+
+ /*
+ * Get this dataset's mountpoint relative to the
+ * altroot.
+ */
+ zhp_mountpoint = mountpoint + strlen(altroot);
+
+ /* Fix this dataset's mountpoint value */
+ if (zfs_prop_set(zhp,
+ zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ zhp_mountpoint)) {
+ be_print_err(gettext("fix_mountpoint_callback: "
+ "failed to set mountpoint for %s to "
+ "%s: %s\n"), zfs_get_name(zhp),
+ zhp_mountpoint,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+ }
+ }
+
+ /* Iterate through this dataset's children and fix them */
+ if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback,
+ altroot)) != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: be_mount_root
+ * Description: This function mounts the root dataset of a BE at the
+ * specified altroot.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to root dataset of a BE that is
+ * to be mounted at altroot.
+ * altroot - location of where to mount the BE root.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_mount_root(zfs_handle_t *zhp, char *altroot)
+{
+ char mountpoint[MAXPATHLEN];
+
+ /* Get mountpoint property of dataset */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("be_mount_root: failed to "
+ "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Set the canmount property for the BE's root dataset to 'noauto' just
+ * in case it's been set to 'on'. We do this so that when we change its
+ * mountpoint, zfs won't immediately try to mount it.
+ */
+ if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
+ != 0) {
+ be_print_err(gettext("be_mount_root: failed to "
+ "set canmount property to 'noauto' (%s): %s\n"),
+ zfs_get_name(zhp), libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Set mountpoint for BE's root filesystem */
+ if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), altroot)
+ != 0) {
+ be_print_err(gettext("be_mount_root: failed to "
+ "set mountpoint of %s to %s: %s\n"),
+ zfs_get_name(zhp), altroot,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Mount the BE's root filesystem */
+ if (zfs_mount(zhp, NULL, 0) != 0) {
+ be_print_err(gettext("be_mount_root: failed to "
+ "mount dataset %s at %s: %s\n"), zfs_get_name(zhp),
+ altroot, libzfs_error_description(g_zfs));
+ /*
+ * Set this BE's root filesystem 'mountpoint' property
+ * back to what it was before.
+ */
+ (void) zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ mountpoint);
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_unmount_root
+ * Description: This function unmounts the root dataset of a BE, but before
+ * unmounting, it looks at the BE's vfstab to determine
+ * if the root dataset mountpoint should be left as 'legacy'
+ * or '/'. If the vfstab contains an entry for this root
+ * dataset with a mountpoint of '/', it sets the mountpoint
+ * property to 'legacy'.
+ *
+ * Parameters:
+ * zhp - zfs_handle_t pointer of the BE root dataset that
+ * is currently mounted.
+ * ud - be_unmount_data_t pointer providing unmount data
+ * for the given BE root dataset.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_unmount_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
+{
+ char mountpoint[MAXPATHLEN];
+ boolean_t is_legacy = B_FALSE;
+
+ /* See if this is a legacy mounted root */
+ if (get_mountpoint_from_vfstab(ud->altroot, zfs_get_name(zhp),
+ mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS &&
+ strcmp(mountpoint, "/") == 0) {
+ is_legacy = B_TRUE;
+ }
+
+ /* Unmount the dataset */
+ if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
+ be_print_err(gettext("be_unmount_root: failed to "
+ "unmount BE root dataset %s: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Set canmount property for this BE's root filesystem to noauto */
+ if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
+ != 0) {
+ be_print_err(gettext("be_unmount_root: failed to "
+ "set canmount property for %s to 'noauto': %s\n"),
+ zfs_get_name(zhp), libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Set mountpoint for BE's root dataset back to '/', or 'legacy'
+ * if its a legacy mounted root.
+ */
+ if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/") != 0) {
+ be_print_err(gettext("be_unmount_root: failed to "
+ "set mountpoint of %s to %s\n"), zfs_get_name(zhp),
+ is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/");
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: fix_mountpoint
+ * Description: This function checks the mountpoint of an unmounted BE to make
+ * sure that it is set to either 'legacy' or '/'. If it's not,
+ * then we're in a situation where an unmounted BE has some random
+ * mountpoint set for it. (This could happen if the system was
+ * rebooted while an inactive BE was mounted). This function
+ * attempts to fix its mountpoints.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to root dataset of the BE
+ * whose mountpoint needs to be checked.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+fix_mountpoint(zfs_handle_t *zhp)
+{
+ be_unmount_data_t ud = { 0 };
+ char *altroot = NULL;
+ char mountpoint[MAXPATHLEN];
+ int ret = BE_SUCCESS;
+
+ /*
+ * Record what this BE's root dataset mountpoint property is currently
+ * set to.
+ */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
+ be_print_err(gettext("fix_mountpoint: failed to get "
+ "mountpoint property of (%s): %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (BE_ERR_ZFS);
+ }
+
+ /*
+ * If the root dataset mountpoint is set to 'legacy' or '/', we're okay.
+ */
+ if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
+ strcmp(mountpoint, "/") == 0) {
+ return (BE_SUCCESS);
+ }
+
+ /*
+ * Iterate through this BE's children datasets and fix
+ * them if they need fixing.
+ */
+ if (zfs_iter_filesystems(zhp, fix_mountpoint_callback, mountpoint)
+ != 0) {
+ return (BE_ERR_ZFS);
+ }
+
+ /*
+ * The process of mounting and unmounting the root file system
+ * will fix its mountpoint to correctly be either 'legacy' or '/'
+ * since be_unmount_root will do the right thing by looking at
+ * its vfstab.
+ */
+
+ /* Generate temporary altroot to mount the root file system */
+ if ((ret = be_make_tmp_mountpoint(&altroot)) != BE_SUCCESS) {
+ be_print_err(gettext("fix_mountpoint: failed to "
+ "make temporary mountpoint\n"));
+ return (ret);
+ }
+
+ /* Mount and unmount the root. */
+ if ((ret = be_mount_root(zhp, altroot)) != BE_SUCCESS) {
+ be_print_err(gettext("fix_mountpoint: failed to "
+ "mount BE root file system\n"));
+ goto cleanup;
+ }
+ ud.altroot = altroot;
+ if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
+ be_print_err(gettext("fix_mountpoint: failed to "
+ "unmount BE root file system\n"));
+ goto cleanup;
+ }
+
+cleanup:
+ free(altroot);
+
+ return (ret);
+}
+
+/*
+ * Function: be_mount_zones
+ * Description: This function finds all supported non-global zones in the
+ * given global BE and mounts them with respect to where the
+ * global BE is currently mounted. The global BE datasets
+ * (including its shared datasets) are expected to already
+ * be mounted.
+ * Parameters:
+ * be_zhp - zfs_handle_t pointer to the root dataset of the
+ * global BE.
+ * md - be_mount_data_t pointer to data for global BE.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_mount_zones(zfs_handle_t *be_zhp, be_mount_data_t *md)
+{
+ zoneBrandList_t *brands = NULL;
+ zoneList_t zlst = NULL;
+ char *zonename = NULL;
+ char *zonepath = NULL;
+ char *zonepath_ds = NULL;
+ int k;
+ int ret = BE_SUCCESS;
+
+ z_set_zone_root(md->altroot);
+
+ if ((brands = be_get_supported_brandlist()) == NULL) {
+ be_print_err(gettext("be_mount_zones: "
+ "no supported brands\n"));
+ return (BE_SUCCESS);
+ }
+
+ zlst = z_get_nonglobal_zone_list_by_brand(brands);
+ if (zlst == NULL) {
+ z_free_brand_list(brands);
+ return (BE_SUCCESS);
+ }
+
+ for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
+ if (z_zlist_get_current_state(zlst, k) ==
+ ZONE_STATE_INSTALLED) {
+ zonepath = z_zlist_get_zonepath(zlst, k);
+
+ /*
+ * Get the dataset of this zonepath in current BE.
+ * If its not a dataset, skip it.
+ */
+ if ((zonepath_ds = be_get_ds_from_dir(zonepath))
+ == NULL)
+ continue;
+
+ /*
+ * Check if this zone is supported based on
+ * the dataset of its zonepath
+ */
+ if (!be_zone_supported(zonepath_ds)) {
+ free(zonepath_ds);
+ zonepath_ds = NULL;
+ continue;
+ }
+
+ /*
+ * if BE's shared file systems are already mounted,
+ * zone path dataset would have already been lofs
+ * mounted under altroot. Otherwise, we need to do
+ * it here.
+ */
+ if (!md->shared_fs) {
+ ret = loopback_mount_zonepath(zonepath, md);
+ if (ret != BE_SUCCESS)
+ goto done;
+ }
+
+
+ /* Mount this zone */
+ ret = be_mount_one_zone(be_zhp, md, zonename,
+ zonepath, zonepath_ds);
+
+ free(zonepath_ds);
+ zonepath_ds = NULL;
+
+ if (ret != BE_SUCCESS) {
+ be_print_err(gettext("be_mount_zones: "
+ "failed to mount zone %s under "
+ "altroot %s\n"), zonename, md->altroot);
+ goto done;
+ }
+ }
+ }
+
+done:
+ z_free_brand_list(brands);
+ z_free_zone_list(zlst);
+ /*
+ * libinstzones caches mnttab and uses cached version for resolving lofs
+ * mounts when we call z_resolve_lofs. It creates the cached version
+ * when the first call to z_resolve_lofs happens. So, library's cached
+ * mnttab doesn't contain entries for lofs mounts created in the above
+ * loop. Because of this, subsequent calls to z_resolve_lofs would fail
+ * to resolve these lofs mounts. So, here we destroy library's cached
+ * mnttab to force its recreation when the next call to z_resolve_lofs
+ * happens.
+ */
+ z_destroyMountTable();
+ return (ret);
+}
+
+/*
+ * Function: be_unmount_zones
+ * Description: This function finds all supported non-global zones in the
+ * given mounted global BE and unmounts them.
+ * Parameters:
+ * ud - unmount_data_t pointer data for the global BE.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_unmount_zones(be_unmount_data_t *ud)
+{
+ zoneBrandList_t *brands = NULL;
+ zoneList_t zlst = NULL;
+ char *zonename = NULL;
+ char *zonepath = NULL;
+ char alt_zonepath[MAXPATHLEN];
+ char *zonepath_ds = NULL;
+ int k;
+ int ret = BE_SUCCESS;
+
+ z_set_zone_root(ud->altroot);
+
+ if ((brands = be_get_supported_brandlist()) == NULL) {
+ be_print_err(gettext("be_unmount_zones: "
+ "no supported brands\n"));
+ return (BE_SUCCESS);
+ }
+
+ zlst = z_get_nonglobal_zone_list_by_brand(brands);
+ if (zlst == NULL) {
+ z_free_brand_list(brands);
+ return (BE_SUCCESS);
+ }
+
+ for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
+ if (z_zlist_get_current_state(zlst, k) ==
+ ZONE_STATE_INSTALLED) {
+ zonepath = z_zlist_get_zonepath(zlst, k);
+
+ /* Build zone's zonepath wrt the global BE altroot */
+ (void) snprintf(alt_zonepath, sizeof (alt_zonepath),
+ "%s%s", ud->altroot, zonepath);
+
+ /*
+ * Get the dataset of this zonepath. If its not
+ * a dataset, skip it.
+ */
+ if ((zonepath_ds = be_get_ds_from_dir(alt_zonepath))
+ == NULL)
+ continue;
+
+ /*
+ * Check if this zone is supported based on the
+ * dataset of its zonepath.
+ */
+ if (!be_zone_supported(zonepath_ds)) {
+ free(zonepath_ds);
+ zonepath_ds = NULL;
+ continue;
+ }
+
+ /* Unmount this zone */
+ ret = be_unmount_one_zone(ud, zonename, zonepath,
+ zonepath_ds);
+
+ free(zonepath_ds);
+ zonepath_ds = NULL;
+
+ if (ret != BE_SUCCESS) {
+ be_print_err(gettext("be_unmount_zones:"
+ " failed to unmount zone %s from "
+ "altroot %s\n"), zonename, ud->altroot);
+ goto done;
+ }
+ }
+ }
+
+done:
+ z_free_brand_list(brands);
+ z_free_zone_list(zlst);
+ return (ret);
+}
+
+/*
+ * Function: be_mount_one_zone
+ * Description: This function is called to mount one zone for a given
+ * global BE.
+ * Parameters:
+ * be_zhp - zfs_handle_t pointer to the root dataset of the
+ * global BE
+ * md - be_mount_data_t pointer to data for global BE
+ * zonename - name of zone to mount
+ * zonepath - zonepath of zone to mount
+ * zonepath_ds - dataset for the zonepath
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_mount_one_zone(zfs_handle_t *be_zhp, be_mount_data_t *md, char *zonename,
+ char *zonepath, char *zonepath_ds)
+{
+ be_mount_data_t zone_md = { 0 };
+ zfs_handle_t *zone_zhp = NULL;
+ char zone_altroot[MAXPATHLEN];
+ char zoneroot[MAXPATHLEN];
+ char zoneroot_ds[MAXPATHLEN];
+ int ret = BE_SUCCESS;
+
+ /* Find the active zone root dataset for this zone for this BE */
+ if ((ret = be_find_active_zone_root(be_zhp, zonepath_ds, zoneroot_ds,
+ sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) {
+ be_print_err(gettext("be_mount_one_zone: did not "
+ "find active zone root for zone %s, skipping ...\n"),
+ zonename);
+ return (BE_SUCCESS);
+ } else if (ret != BE_SUCCESS) {
+ be_print_err(gettext("be_mount_one_zone: failed to "
+ "find active zone root for zone %s\n"), zonename);
+ return (ret);
+ }
+
+ /* Get handle to active zoneroot dataset */
+ if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_mount_one_zone: failed to "
+ "open zone root dataset (%s): %s\n"), zoneroot_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Generate string for zone's altroot path */
+ be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
+ (void) strlcpy(zone_altroot, md->altroot, sizeof (zone_altroot));
+ (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
+
+ /* Build mount_data for the zone */
+ zone_md.altroot = zone_altroot;
+ zone_md.shared_fs = md->shared_fs;
+ zone_md.shared_rw = md->shared_rw;
+
+ /* Mount the zone's root file system */
+ if ((ret = be_mount_zone_root(zone_zhp, &zone_md)) != BE_SUCCESS) {
+ be_print_err(gettext("be_mount_one_zone: failed to "
+ "mount zone root file system at %s\n"), zone_altroot);
+ goto done;
+ }
+
+ /* Iterate through zone's children filesystems */
+ if ((ret = zfs_iter_filesystems(zone_zhp, be_mount_callback,
+ zone_altroot)) != 0) {
+ be_print_err(gettext("be_mount_one_zone: failed to "
+ "mount zone subordinate file systems at %s\n"),
+ zone_altroot);
+ goto done;
+ }
+
+ /* TODO: Mount all shared file systems for this zone */
+
+done:
+ ZFS_CLOSE(zone_zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_unmount_one_zone
+ * Description: This function unmount one zone for a give global BE.
+ * Parameters:
+ * ud - be_unmount_data_t pointer to data for global BE
+ * zonename - name of zone to unmount
+ * zonepath - zonepath of the zone to unmount
+ * zonepath_ds - dataset for the zonepath
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_unmount_one_zone(be_unmount_data_t *ud, char *zonename, char *zonepath,
+ char *zonepath_ds)
+{
+ be_unmount_data_t zone_ud = { 0 };
+ zfs_handle_t *zone_zhp = NULL;
+ char zone_altroot[MAXPATHLEN];
+ char zoneroot[MAXPATHLEN];
+ char zoneroot_ds[MAXPATHLEN];
+ int ret = BE_SUCCESS;
+
+ /* Generate string for zone's alternate root path */
+ be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
+ (void) strlcpy(zone_altroot, ud->altroot, sizeof (zone_altroot));
+ (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
+
+ /* Build be_unmount_data for zone */
+ zone_ud.altroot = zone_altroot;
+ zone_ud.force = ud->force;
+
+ /* Find the mounted zone root dataset for this zone for this BE */
+ if ((ret = be_find_mounted_zone_root(zone_altroot, zonepath_ds,
+ zoneroot_ds, sizeof (zoneroot_ds))) == BE_ERR_NO_MOUNTED_ZONE) {
+ be_print_err(gettext("be_unmount_one_zone: did not "
+ "find any zone root mounted for zone %s\n"), zonename);
+ return (BE_SUCCESS);
+ } else if (ret != BE_SUCCESS) {
+ be_print_err(gettext("be_unmount_one_zone: failed to "
+ "find mounted zone root for zone %s\n"), zonename);
+ return (ret);
+ }
+
+ /* Get handle to zoneroot dataset mounted for this BE */
+ if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_unmount_one_zone: failed to "
+ "open mounted zone root dataset (%s): %s\n"), zoneroot_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* TODO: Unmount all shared file systems for this zone */
+
+ /* Iterate through zone's children filesystems and unmount them */
+ if ((ret = zfs_iter_filesystems(zone_zhp, be_unmount_callback,
+ &zone_ud)) != 0) {
+ be_print_err(gettext("be_unmount_one_zone: failed to "
+ "unmount zone subordinate file systems at %s\n"),
+ zone_altroot);
+ goto done;
+ }
+
+ /* Unmount the zone's root filesystem */
+ if ((ret = be_unmount_zone_root(zone_zhp, &zone_ud)) != BE_SUCCESS) {
+ be_print_err(gettext("be_unmount_one_zone: failed to "
+ "unmount zone root file system at %s\n"), zone_altroot);
+ goto done;
+ }
+
+done:
+ ZFS_CLOSE(zone_zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_get_ds_from_dir_callback
+ * Description: This is a callback function used to iterate all datasets
+ * to find the one that is currently mounted at the directory
+ * being searched for. If matched, the name of the dataset is
+ * returned in heap storage, so the caller is responsible for
+ * freeing it.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current dataset being processed.
+ * data - dir_data_t pointer providing name of directory being
+ * searched for.
+ * Returns:
+ * 1 - This dataset is mounted at directory being searched for.
+ * 0 - This dataset is not mounted at directory being searched for.
+ * Scope:
+ * Private
+ */
+static int
+be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data)
+{
+ dir_data_t *dd = data;
+ char *mp = NULL;
+ int zret = 0;
+
+ if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+
+ if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
+ strcmp(mp, dd->dir) == 0) {
+ if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) {
+ be_print_err(gettext("be_get_ds_from_dir_callback: "
+ "memory allocation failed\n"));
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+ ZFS_CLOSE(zhp);
+ return (1);
+ }
+
+ zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd);
+
+ ZFS_CLOSE(zhp);
+
+ return (zret);
+}
diff --git a/usr/src/lib/libbe/common/be_rename.c b/usr/src/lib/libbe/common/be_rename.c
new file mode 100644
index 0000000000..1439424d13
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_rename.c
@@ -0,0 +1,232 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <assert.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+/* ******************************************************************** */
+/* Public Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_rename
+ * Description: Renames the BE from the original name to the new name
+ * passed in through be_attrs. Also the entries in vfstab and
+ * menu.lst are updated with the new name.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attribute values are used by
+ * this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *required
+ * BE_ATTR_NEW_BE_NAME *required
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+
+int
+be_rename(nvlist_t *be_attrs)
+{
+ be_transaction_data_t bt = { 0 };
+ be_transaction_data_t cbt = { 0 };
+ be_fs_list_data_t fld = { 0 };
+ zfs_handle_t *zhp = NULL;
+ char root_ds[MAXPATHLEN];
+ char *mp = NULL;
+ int zret = 0, ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get original BE name to rename from */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &bt.obe_name)
+ != 0) {
+ be_print_err(gettext("be_rename: failed to "
+ "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get new BE name to rename to */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME, &bt.nbe_name)
+ != 0) {
+ be_print_err(gettext("be_rename: failed to "
+ "lookup BE_ATTR_NEW_BE_NAME attribute\n"));
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /*
+ * Get the currently active BE and check to see if this
+ * is an attempt to rename the currently active BE.
+ */
+ if (be_find_current_be(&cbt) != BE_SUCCESS) {
+ be_print_err(gettext("be_rename: failed to find the currently "
+ "active BE\n"));
+ be_zfs_fini();
+ return (BE_ERR_CURR_BE_NOT_FOUND);
+ }
+
+ if (strncmp(bt.obe_name, cbt.obe_name,
+ MAX(strlen(bt.obe_name), strlen(cbt.obe_name))) == 0) {
+ be_print_err(gettext("be_rename: This is an attempt to rename "
+ "the currently active BE, which is not supported\n"));
+ be_zfs_fini();
+ free(cbt.obe_name);
+ return (BE_ERR_RENAME_ACTIVE);
+ }
+
+ /* Validate original BE name */
+ if (!be_valid_be_name(bt.obe_name)) {
+ be_print_err(gettext("be_rename: "
+ "invalid BE name %s\n"), bt.obe_name);
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /* Validate new BE name */
+ if (!be_valid_be_name(bt.nbe_name)) {
+ be_print_err(gettext("be_rename: invalid BE name %s\n"),
+ bt.nbe_name);
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /* Find which zpool the BE is in */
+ if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
+ be_print_err(gettext("be_rename: failed to "
+ "find zpool for BE (%s)\n"), bt.obe_name);
+ be_zfs_fini();
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_rename: zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ be_zfs_fini();
+ return (ret);
+ }
+
+ /* New BE will reside in the same zpool as orig BE */
+ bt.nbe_zpool = bt.obe_zpool;
+
+ be_make_root_ds(bt.obe_zpool, bt.obe_name, root_ds, sizeof (root_ds));
+ bt.obe_root_ds = strdup(root_ds);
+ be_make_root_ds(bt.nbe_zpool, bt.nbe_name, root_ds, sizeof (root_ds));
+ bt.nbe_root_ds = strdup(root_ds);
+
+ /*
+ * Generate a list of file systems from the BE that are legacy
+ * mounted before renaming. We use this list to determine which
+ * entries in the vfstab we need to update after we've renamed the BE.
+ */
+ if ((ret = be_get_legacy_fs(bt.obe_name, bt.obe_root_ds, NULL, NULL,
+ &fld)) != BE_SUCCESS) {
+ be_print_err(gettext("be_rename: failed to "
+ "get legacy mounted file system list for %s\n"),
+ bt.obe_name);
+ goto done;
+ }
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_rename: failed to "
+ "open BE root dataset (%s): %s\n"),
+ bt.obe_root_ds, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* Rename of BE's root dataset. */
+ if (zfs_rename(zhp, bt.nbe_root_ds, B_FALSE) != 0) {
+ be_print_err(gettext("be_rename: failed to "
+ "rename dataset (%s): %s\n"), bt.obe_root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* Refresh handle to BE's root dataset after the rename */
+ ZFS_CLOSE(zhp);
+ if ((zhp = zfs_open(g_zfs, bt.nbe_root_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_rename: failed to "
+ "open BE root dataset (%s): %s\n"),
+ bt.obe_root_ds, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* If BE is already mounted, get its mountpoint */
+ if (zfs_is_mounted(zhp, &mp) && mp == NULL) {
+ be_print_err(gettext("be_rename: failed to "
+ "get altroot of mounted BE %s: %s\n"),
+ bt.nbe_name, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* Update BE's vfstab */
+ if ((ret = be_update_vfstab(bt.nbe_name, bt.obe_zpool, bt.nbe_zpool,
+ &fld, mp)) != BE_SUCCESS) {
+ be_print_err(gettext("be_rename: "
+ "failed to update new BE's vfstab (%s)\n"), bt.nbe_name);
+ goto done;
+ }
+
+ /* Update this BE's GRUB menu entry */
+ if (getzoneid() == GLOBAL_ZONEID && (ret = be_update_menu(bt.obe_name,
+ bt.nbe_name, bt.obe_zpool, NULL)) != BE_SUCCESS) {
+ be_print_err(gettext("be_rename: "
+ "failed to update grub menu entry from %s to %s\n"),
+ bt.obe_name, bt.nbe_name);
+ }
+
+done:
+ be_free_fs_list(&fld);
+
+ ZFS_CLOSE(zhp);
+
+ be_zfs_fini();
+
+ free(bt.obe_root_ds);
+ free(bt.nbe_root_ds);
+ return (ret);
+}
diff --git a/usr/src/lib/libbe/common/be_snapshot.c b/usr/src/lib/libbe/common/be_snapshot.c
new file mode 100644
index 0000000000..265bbeba9d
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_snapshot.c
@@ -0,0 +1,769 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * System includes
+ */
+#include <assert.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+/* Private function prototypes */
+static int be_rollback_check_callback(zfs_handle_t *, void *);
+static int be_rollback_callback(zfs_handle_t *, void *);
+
+
+/* ******************************************************************** */
+/* Public Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_create_snapshot
+ * Description: Creates a recursive snapshot of all the datasets within a BE.
+ * If the name of the BE to snapshot is not provided, it assumes
+ * we're snapshotting the currently running BE. If the snapshot
+ * name is not provided it creates an auto named snapshot, which
+ * will be returned to the caller upon success.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attributes are used by this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *optional
+ * BE_ATTR_SNAP_NAME *optional
+ * BE_ATTR_POLICY *optional
+ *
+ * If the BE_ATTR_SNAP_NAME was not passed in, upon
+ * successful BE snapshot creation, the following
+ * attribute value will be returned to the caller by
+ * setting it in the be_attrs parameter passed in:
+ *
+ * BE_ATTR_SNAP_NAME
+ *
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_create_snapshot(nvlist_t *be_attrs)
+{
+ char *be_name = NULL;
+ char *snap_name = NULL;
+ char *policy = NULL;
+ boolean_t autoname = B_FALSE;
+ int ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get original BE name if one was provided */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &be_name, NULL) != 0) {
+ be_print_err(gettext("be_create_snapshot: failed to "
+ "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /* Validate original BE name if one was provided */
+ if (be_name != NULL && !be_valid_be_name(be_name)) {
+ be_print_err(gettext("be_create_snapshot: "
+ "invalid BE name %s\n"), be_name);
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get snapshot name to create if one was provided */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &snap_name, NULL) != 0) {
+ be_print_err(gettext("be_create_snapshot: "
+ "failed to lookup BE_ATTR_SNAP_NAME attribute\n"));
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get BE policy to create this snapshot under */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_POLICY, DATA_TYPE_STRING, &policy, NULL) != 0) {
+ be_print_err(gettext("be_create_snapshot: "
+ "failed to lookup BE_ATTR_POLICY attribute\n"));
+ be_zfs_fini();
+ return (BE_ERR_INVAL);
+ }
+
+ /*
+ * If no snap_name ws provided, we're going to create an
+ * auto named snapshot. Set flag so that we know to pass
+ * the auto named snapshot to the caller later.
+ */
+ if (snap_name == NULL)
+ autoname = B_TRUE;
+
+ if ((ret = _be_create_snapshot(be_name, &snap_name, policy))
+ == BE_SUCCESS) {
+ if (autoname == B_TRUE) {
+ /*
+ * Set auto named snapshot name in the
+ * nvlist passed in by the caller.
+ */
+ if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME,
+ snap_name) != 0) {
+ be_print_err(gettext("be_create_snapshot: "
+ "failed to add auto snap name (%s) to "
+ "be_attrs\n"), snap_name);
+ ret = BE_ERR_NOMEM;
+ }
+ }
+ }
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/*
+ * Function: be_destroy_snapshot
+ * Description: Iterates through all the datasets of the BE and deletes
+ * the snapshots of each one with the specified name. If the
+ * BE name is not provided, it assumes we're operating on the
+ * currently running BE. The name of the snapshot name to
+ * destroy must be provided.
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attribute values are used by this
+ * function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *optional
+ * BE_ATTR_SNAP_NAME *required
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_destroy_snapshot(nvlist_t *be_attrs)
+{
+ char *be_name = NULL;
+ char *snap_name = NULL;
+ int ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get original BE name if one was provided */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &be_name, NULL) != 0) {
+ be_print_err(gettext("be_destroy_snapshot: "
+ "failed to lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Validate original BE name if one was provided */
+ if (be_name != NULL && !be_valid_be_name(be_name)) {
+ be_print_err(gettext("be_destroy_snapshot: "
+ "invalid BE name %s\n"), be_name);
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get snapshot name to destroy */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_SNAP_NAME, &snap_name)
+ != 0) {
+ be_print_err(gettext("be_destroy_snapshot: "
+ "failed to lookup BE_ATTR_SNAP_NAME attribute.\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ ret = _be_destroy_snapshot(be_name, snap_name);
+
+ be_zfs_fini();
+
+ return (ret);
+}
+
+/*
+ * Function: be_rollback
+ * Description: Rolls back a BE and all of its children datasets to the
+ * named snapshot. All of the BE's datasets must have the
+ * named snapshot for this function to succeed. If the name
+ * of the BE is not passed in, this function assumes we're
+ * operating on the currently booted live BE.
+ *
+ * Note - This function does not check if the BE has any
+ * younger snapshots than the one we're trying to rollback to.
+ * If it does, then those younger snapshots and their dependent
+ * clone file systems will get destroyed in the process of
+ * rolling back.
+ *
+ * Parameters:
+ * be_attrs - pointer to nvlist_t of attributes being passed in.
+ * The following attributes are used by this function:
+ *
+ * BE_ATTR_ORIG_BE_NAME *optional
+ * BE_ATTR_SNAP_NAME *required
+ *
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+int
+be_rollback(nvlist_t *be_attrs)
+{
+ be_transaction_data_t bt = { 0 };
+ zfs_handle_t *zhp = NULL;
+ char obe_root_ds[MAXPATHLEN];
+ int zret = 0, ret = BE_SUCCESS;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ /* Get original BE name if one was provided */
+ if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
+ BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &bt.obe_name, NULL) != 0) {
+ be_print_err(gettext("be_rollback: "
+ "failed to lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* If original BE name not provided, use current BE */
+ if (bt.obe_name == NULL) {
+ if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
+ return (ret);
+ }
+ } else {
+ /* Validate original BE name */
+ if (!be_valid_be_name(bt.obe_name)) {
+ be_print_err(gettext("be_rollback: "
+ "invalid BE name %s\n"), bt.obe_name);
+ return (BE_ERR_INVAL);
+ }
+ }
+
+ /* Get snapshot name to rollback to */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_SNAP_NAME, &bt.obe_snap_name)
+ != 0) {
+ be_print_err(gettext("be_rollback: "
+ "failed to lookup BE_ATTR_SNAP_NAME attribute.\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Find which zpool obe_name lives in */
+ if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
+ be_print_err(gettext("be_rollback: "
+ "failed to find zpool for BE (%s)\n"), bt.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_rollback: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Generate string for BE's root dataset */
+ be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
+ sizeof (obe_root_ds));
+ bt.obe_root_ds = obe_root_ds;
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET)) == NULL) {
+ be_print_err(gettext("be_rollback: "
+ "failed to open BE root dataset (%s): %s\n"),
+ bt.obe_root_ds, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check that snapshot name exists for this BE and all of its
+ * children file systems. This call will end up closing the
+ * zfs handle passed in whether it succeeds or fails.
+ */
+ if ((ret = be_rollback_check_callback(zhp, bt.obe_snap_name)) != 0) {
+ zhp = NULL;
+ return (ret);
+ }
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET)) == NULL) {
+ be_print_err(gettext("be_rollback: "
+ "failed to open BE root dataset (%s): %s\n"),
+ bt.obe_root_ds, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Iterate through a BE's datasets and roll them all back to
+ * the specified snapshot. This call will end up closing the
+ * zfs handle passed in whether it succeeds or fails.
+ */
+ if ((ret = be_rollback_callback(zhp, bt.obe_snap_name)) != 0) {
+ zhp = NULL;
+ be_print_err(gettext("be_rollback: "
+ "failed to rollback BE %s to %s\n"), bt.obe_name,
+ bt.obe_snap_name);
+ return (ret);
+ }
+ zhp = NULL;
+ be_zfs_fini();
+ return (BE_SUCCESS);
+}
+
+
+/* ******************************************************************** */
+/* Semi-Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: _be_create_snapshot
+ * Description: see be_create_snapshot
+ * Parameters:
+ * be_name - The name of the BE that we're taking a snapshot of.
+ * snap_name - The name of the snapshot we're creating. If
+ * snap_name is NULL an auto generated name will be used,
+ * and upon success, will return that name via this
+ * reference pointer. The caller is responsible for
+ * freeing the returned name.
+ * policy - The clean-up policy type. (library wide use only)
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+_be_create_snapshot(char *be_name, char **snap_name, char *policy)
+{
+ be_transaction_data_t bt = { 0 };
+ zfs_handle_t *zhp = NULL;
+ nvlist_t *ss_props = NULL;
+ char ss[MAXPATHLEN];
+ char root_ds[MAXPATHLEN];
+ int pool_version = 0;
+ int i = 0;
+ int zret = 0, ret = BE_SUCCESS;
+ boolean_t autoname = B_FALSE;
+
+ /* Set parameters in bt structure */
+ bt.obe_name = be_name;
+ bt.obe_snap_name = *snap_name;
+ bt.policy = policy;
+
+ /* If original BE name not supplied, use current BE */
+ if (bt.obe_name == NULL) {
+ if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
+ return (ret);
+ }
+ }
+
+ /* Find which zpool obe_name lives in */
+ if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
+ be_print_err(gettext("be_create_snapshot: failed to "
+ "find zpool for BE (%s)\n"), bt.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_create_snapshot: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ be_make_root_ds(bt.obe_zpool, bt.obe_name, root_ds,
+ sizeof (root_ds));
+ bt.obe_root_ds = root_ds;
+
+ /* If BE policy not specified, use the default policy */
+ if (bt.policy == NULL) {
+ bt.policy = be_default_policy();
+ } else {
+ /* Validate policy type */
+ if (!valid_be_policy(bt.policy)) {
+ be_print_err(gettext("be_create_snapshot: "
+ "invalid BE policy type (%s)\n"), bt.policy);
+ return (BE_ERR_INVAL);
+ }
+ }
+
+ /*
+ * If snapshot name not specified, set auto name flag and
+ * generate auto snapshot name.
+ */
+ if (bt.obe_snap_name == NULL) {
+ autoname = B_TRUE;
+ if ((bt.obe_snap_name = be_auto_snap_name())
+ == NULL) {
+ be_print_err(gettext("be_create_snapshot: "
+ "failed to create auto snapshot name\n"));
+ ret = BE_ERR_AUTONAME;
+ goto done;
+ }
+ }
+
+ /* Generate the name of the snapshot to take. */
+ (void) snprintf(ss, sizeof (ss), "%s@%s", bt.obe_root_ds,
+ bt.obe_snap_name);
+
+ /* Get handle to BE's root dataset */
+ if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET))
+ == NULL) {
+ be_print_err(gettext("be_create_snapshot: "
+ "failed to open BE root dataset (%s): %s\n"),
+ bt.obe_root_ds, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* Get the ZFS pool version of the pool where this dataset resides */
+ if (zfs_spa_version(zhp, &pool_version) != 0) {
+ be_print_err(gettext("be_create_snapshot: failed to "
+ "get ZFS pool version for %s: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ }
+
+ /*
+ * If ZFS pool version supports snapshot user properties, store
+ * cleanup policy there. Otherwise don't set one - this snapshot
+ * will always inherit the cleanup policy from its parent.
+ */
+ if (pool_version >= SPA_VERSION_SNAP_PROPS) {
+ if (nvlist_alloc(&ss_props, NV_UNIQUE_NAME, 0) != 0) {
+ be_print_err(gettext("be_create_snapshot: internal "
+ "error: out of memory\n"));
+ return (BE_ERR_NOMEM);
+ }
+ if (nvlist_add_string(ss_props, BE_POLICY_PROPERTY, bt.policy)
+ != 0) {
+ be_print_err(gettext("be_create_snapshot: internal "
+ "error: out of memory\n"));
+ nvlist_free(ss_props);
+ return (BE_ERR_NOMEM);
+ }
+ } else if (policy != NULL) {
+ /*
+ * If an explicit cleanup policy was requested
+ * by the caller and we don't support it, error out.
+ */
+ be_print_err(gettext("be_create_snapshot: cannot set "
+ "cleanup policy: ZFS pool version is %d\n"), pool_version);
+ return (BE_ERR_NOTSUP);
+ }
+
+ /* Create the snapshots recursively */
+ if (zfs_snapshot(g_zfs, ss, B_TRUE, ss_props) != 0) {
+ if (!autoname || libzfs_errno(g_zfs) != EZFS_EXISTS) {
+ be_print_err(gettext("be_create_snapshot: "
+ "recursive snapshot of %s failed: %s\n"),
+ ss, libzfs_error_description(g_zfs));
+
+ if (libzfs_errno(g_zfs) == EZFS_EXISTS)
+ ret = BE_ERR_SS_EXISTS;
+ else
+ ret = zfs_err_to_be_err(g_zfs);
+
+ goto done;
+ } else {
+ for (i = 1; i < BE_AUTO_NAME_MAX_TRY; i++) {
+
+ /* Sleep 1 before retrying */
+ (void) sleep(1);
+
+ /* Generate new auto snapshot name. */
+ free(bt.obe_snap_name);
+ if ((bt.obe_snap_name =
+ be_auto_snap_name()) == NULL) {
+ be_print_err(gettext(
+ "be_create_snapshot: failed to "
+ "create auto snapshot name\n"));
+ ret = BE_ERR_AUTONAME;
+ goto done;
+ }
+
+ /* Generate string of the snapshot to take. */
+ (void) snprintf(ss, sizeof (ss), "%s@%s",
+ bt.obe_root_ds, bt.obe_snap_name);
+
+ /* Create the snapshots recursively */
+ if (zfs_snapshot(g_zfs, ss, B_TRUE, ss_props)
+ != 0) {
+ if (libzfs_errno(g_zfs) !=
+ EZFS_EXISTS) {
+ be_print_err(gettext(
+ "be_create_snapshot: "
+ "recursive snapshot of %s "
+ "failed: %s\n"), ss,
+ libzfs_error_description(
+ g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * If we exhausted the maximum number of tries,
+ * free the auto snap name and set error.
+ */
+ if (i == BE_AUTO_NAME_MAX_TRY) {
+ be_print_err(gettext("be_create_snapshot: "
+ "failed to create unique auto snapshot "
+ "name\n"));
+ free(bt.obe_snap_name);
+ bt.obe_snap_name = NULL;
+ ret = BE_ERR_AUTONAME;
+ }
+ }
+ }
+
+ /*
+ * If we succeeded in creating an auto named snapshot, store
+ * the name in the nvlist passed in by the caller.
+ */
+ if (autoname && bt.obe_snap_name) {
+ *snap_name = bt.obe_snap_name;
+ }
+
+done:
+ ZFS_CLOSE(zhp);
+
+ if (ss_props != NULL)
+ nvlist_free(ss_props);
+
+ return (ret);
+}
+
+/*
+ * Function: _be_destroy_snapshot
+ * Description: see be_destroy_snapshot
+ * Parameters:
+ * be_name - The name of the BE that the snapshot belongs to.
+ * snap_name - The name of the snapshot we're destroying.
+ * Return:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+_be_destroy_snapshot(char *be_name, char *snap_name)
+{
+ be_transaction_data_t bt = { 0 };
+ zfs_handle_t *zhp;
+ char ss[MAXPATHLEN];
+ char root_ds[MAXPATHLEN];
+ int err = BE_SUCCESS, ret = BE_SUCCESS;
+
+ /* Make sure we actaully have a snapshot name */
+ if (snap_name == NULL) {
+ be_print_err(gettext("be_destroy_snapshot: "
+ "invalid snapshot name\n"));
+ return (BE_ERR_INVAL);
+ }
+
+ /* Set parameters in bt structure */
+ bt.obe_name = be_name;
+ bt.obe_snap_name = snap_name;
+
+ /* If original BE name not supplied, use current BE */
+ if (bt.obe_name == NULL) {
+ if ((err = be_find_current_be(&bt)) != BE_SUCCESS) {
+ return (err);
+ }
+ }
+
+ /* Find which zpool be_name lives in */
+ if ((ret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
+ be_print_err(gettext("be_destroy_snapshot: "
+ "failed to find zpool for BE (%s)\n"), bt.obe_name);
+ return (BE_ERR_BE_NOENT);
+ } else if (ret < 0) {
+ be_print_err(gettext("be_destroy_snapshot: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ be_make_root_ds(bt.obe_zpool, bt.obe_name, root_ds,
+ sizeof (root_ds));
+ bt.obe_root_ds = root_ds;
+
+ zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET);
+ if (zhp == NULL) {
+ /*
+ * The zfs_open failed, return an error.
+ */
+ be_print_err(gettext("be_destroy_snapshot: "
+ "failed to open BE root dataset (%s): %s\n"),
+ bt.obe_root_ds, libzfs_error_description(g_zfs));
+ err = zfs_err_to_be_err(g_zfs);
+ } else {
+ /*
+ * Generate the name of the snapshot to take.
+ */
+ (void) snprintf(ss, sizeof (ss), "%s@%s", bt.obe_name,
+ bt.obe_snap_name);
+
+ /*
+ * destroy the snapshot.
+ */
+ /*
+ * The boolean set to B_FALSE and passed to zfs_destroy_snaps()
+ * tells zfs to process and destroy the snapshots now.
+ * Otherwise the call will potentially return where the
+ * snapshot isn't actually destroyed yet, and ZFS is waiting
+ * until all the references to the snapshot have been
+ * released before actually destroying the snapshot.
+ */
+ if (zfs_destroy_snaps(zhp, bt.obe_snap_name, B_FALSE) != 0) {
+ err = zfs_err_to_be_err(g_zfs);
+ be_print_err(gettext("be_destroy_snapshot: "
+ "failed to destroy snapshot %s: %s\n"), ss,
+ libzfs_error_description(g_zfs));
+ }
+ }
+
+ ZFS_CLOSE(zhp);
+
+ return (err);
+}
+
+/* ******************************************************************** */
+/* Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_rollback_check_callback
+ * Description: Callback function used to iterate through a BE's filesystems
+ * to check if a given snapshot name exists.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to filesystem being processed.
+ * data - name of the snapshot to check for.
+ * Returns:
+ * 0 - Success, snapshot name exists for all filesystems.
+ * be_errno_t - Failure, snapshot name does not exist for all
+ * filesystems.
+ * Scope:
+ * Private
+ */
+static int
+be_rollback_check_callback(zfs_handle_t *zhp, void *data)
+{
+ char *snap_name = data;
+ char ss[MAXPATHLEN];
+ int ret = BE_SUCCESS;
+
+ /* Generate string for this filesystem's snapshot name */
+ (void) snprintf(ss, sizeof (ss), "%s@%s", zfs_get_name(zhp), snap_name);
+
+ /* Check if snapshot exists */
+ if (!zfs_dataset_exists(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) {
+ be_print_err(gettext("be_rollback_check_callback: "
+ "snapshot does not exist %s\n"), ss);
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_SS_NOENT);
+ }
+
+ /* Iterate this dataset's children and check them */
+ if ((ret = zfs_iter_filesystems(zhp, be_rollback_check_callback,
+ snap_name)) != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
+
+/*
+ * Function: be_rollback_callback
+ * Description: Callback function used to iterate through a BE's filesystems
+ * and roll them all back to the specified snapshot name.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to filesystem being processed.
+ * data - name of snapshot to rollback to.
+ * Returns:
+ * 0 - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_rollback_callback(zfs_handle_t *zhp, void *data)
+{
+ zfs_handle_t *zhp_snap = NULL;
+ char *snap_name = data;
+ char ss[MAXPATHLEN];
+ int ret = 0;
+
+ /* Generate string for this filesystem's snapshot name */
+ (void) snprintf(ss, sizeof (ss), "%s@%s", zfs_get_name(zhp), snap_name);
+
+ /* Get handle to this filesystem's snapshot */
+ if ((zhp_snap = zfs_open(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) == NULL) {
+ be_print_err(gettext("be_rollback_callback: "
+ "failed to open snapshot %s: %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /* Rollback dataset */
+ if (zfs_rollback(zhp, zhp_snap, B_FALSE) != 0) {
+ be_print_err(gettext("be_rollback_callback: "
+ "failed to rollback BE dataset %s to snapshot %s: %s\n"),
+ zfs_get_name(zhp), ss, libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ ZFS_CLOSE(zhp_snap);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp_snap);
+ /* Iterate this dataset's children and roll them back */
+ if ((ret = zfs_iter_filesystems(zhp, be_rollback_callback,
+ snap_name)) != 0) {
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (0);
+}
diff --git a/usr/src/lib/libbe/common/be_utils.c b/usr/src/lib/libbe/common/be_utils.c
new file mode 100644
index 0000000000..b0782c8f28
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_utils.c
@@ -0,0 +1,3647 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * System includes
+ */
+#include <assert.h>
+#include <errno.h>
+#include <libgen.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfstab.h>
+#include <sys/param.h>
+#include <sys/systeminfo.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <wait.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+#define INST_ICT "/usr/lib/python2.6/vendor-packages/osol_install/ict.py"
+
+/* Private function prototypes */
+static int update_dataset(char *, int, char *, char *, char *);
+static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
+static int be_open_menu(char *, char *, char *, FILE **, char *, boolean_t);
+static int be_create_menu(char *, char *, char *, FILE **, char *);
+static char *be_get_auto_name(char *, char *, boolean_t);
+
+/*
+ * Global error printing
+ */
+boolean_t do_print = B_FALSE;
+
+/*
+ * Private datatypes
+ */
+typedef struct zone_be_name_cb_data {
+ char *base_be_name;
+ int num;
+} zone_be_name_cb_data_t;
+
+/* ******************************************************************** */
+/* Public Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_max_avail
+ * Description: Returns the available size for the zfs dataset passed in.
+ * Parameters:
+ * dataset - The dataset we want to get the available space for.
+ * ret - The available size will be returned in this.
+ * Returns:
+ * The error returned by the zfs get property function.
+ * Scope:
+ * Public
+ */
+int
+be_max_avail(char *dataset, uint64_t *ret)
+{
+ zfs_handle_t *zhp;
+ int err = 0;
+
+ /* Initialize libzfs handle */
+ if (!be_zfs_init())
+ return (BE_ERR_INIT);
+
+ zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
+ if (zhp == NULL) {
+ /*
+ * The zfs_open failed return an error
+ */
+ err = zfs_err_to_be_err(g_zfs);
+ } else {
+ err = be_maxsize_avail(zhp, ret);
+ }
+ ZFS_CLOSE(zhp);
+ be_zfs_fini();
+ return (err);
+}
+
+/*
+ * Function: libbe_print_errors
+ * Description: Turns on/off error output for the library.
+ * Parameter:
+ * set_do_print - Boolean that turns library error
+ * printing on or off.
+ * Returns:
+ * None
+ * Scope:
+ * Public;
+ */
+void
+libbe_print_errors(boolean_t set_do_print)
+{
+ do_print = set_do_print;
+}
+
+/* ******************************************************************** */
+/* Semi-Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_zfs_init
+ * Description: Initializes the libary global libzfs handle.
+ * Parameters:
+ * None
+ * Returns:
+ * B_TRUE - Success
+ * B_FALSE - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+be_zfs_init(void)
+{
+ be_zfs_fini();
+
+ if ((g_zfs = libzfs_init()) == NULL) {
+ be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
+ "library\n"));
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Function: be_zfs_fini
+ * Description: Closes the library global libzfs handle if it currently open.
+ * Parameter:
+ * None
+ * Returns:
+ * None
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+void
+be_zfs_fini(void)
+{
+ if (g_zfs)
+ libzfs_fini(g_zfs);
+
+ g_zfs = NULL;
+}
+
+/*
+ * Function: be_make_root_ds
+ * Description: Generate string for BE's root dataset given the pool
+ * it lives in and the BE name.
+ * Parameters:
+ * zpool - pointer zpool name.
+ * be_name - pointer to BE name.
+ * be_root_ds - pointer to buffer to return BE root dataset in.
+ * be_root_ds_size - size of be_root_ds
+ * Returns:
+ * None
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+void
+be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
+ int be_root_ds_size)
+{
+ (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s/%s", zpool,
+ BE_CONTAINER_DS_NAME, be_name);
+}
+
+/*
+ * Function: be_make_container_ds
+ * Description: Generate string for the BE container dataset given a pool name.
+ * Parameters:
+ * zpool - pointer zpool name.
+ * container_ds - pointer to buffer to return BE container
+ * dataset in.
+ * container_ds_size - size of container_ds
+ * Returns:
+ * None
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+void
+be_make_container_ds(const char *zpool, char *container_ds,
+ int container_ds_size)
+{
+ (void) snprintf(container_ds, container_ds_size, "%s/%s", zpool,
+ BE_CONTAINER_DS_NAME);
+}
+
+/*
+ * Function: be_make_name_from_ds
+ * Description: This function takes a dataset name and strips off the
+ * BE container dataset portion from the beginning. The
+ * returned name is allocated in heap storage, so the caller
+ * is responsible for freeing it.
+ * Parameters:
+ * dataset - dataset to get name from.
+ * rc_loc - dataset underwhich the root container dataset lives.
+ * Returns:
+ * name of dataset relative to BE container dataset.
+ * NULL if dataset is not under a BE root dataset.
+ * Scope:
+ * Semi-primate (library wide use only)
+ */
+char *
+be_make_name_from_ds(const char *dataset, char *rc_loc)
+{
+ char ds[ZFS_MAXNAMELEN];
+ char *tok = NULL;
+ char *name = NULL;
+
+ /*
+ * First token is the location of where the root container dataset
+ * lives; it must match rc_loc.
+ */
+ if (strncmp(dataset, rc_loc, strlen(rc_loc)) == 0 &&
+ dataset[strlen(rc_loc)] == '/') {
+ (void) strlcpy(ds, dataset + strlen(rc_loc) + 1, sizeof (ds));
+ } else {
+ return (NULL);
+ }
+
+ /* Second token must be BE container dataset name */
+ if ((tok = strtok(ds, "/")) == NULL ||
+ strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
+ return (NULL);
+
+ /* Return the remaining token if one exists */
+ if ((tok = strtok(NULL, "")) == NULL)
+ return (NULL);
+
+ if ((name = strdup(tok)) == NULL) {
+ be_print_err(gettext("be_make_name_from_ds: "
+ "memory allocation failed\n"));
+ return (NULL);
+ }
+
+ return (name);
+}
+
+/*
+ * Function: be_maxsize_avail
+ * Description: Returns the available size for the zfs handle passed in.
+ * Parameters:
+ * zhp - A pointer to the open zfs handle.
+ * ret - The available size will be returned in this.
+ * Returns:
+ * The error returned by the zfs get property function.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
+{
+ return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
+}
+
+/*
+ * Function: be_append_menu
+ * Description: Appends an entry for a BE into the menu.lst.
+ * Parameters:
+ * be_name - pointer to name of BE to add boot menu entry for.
+ * be_root_pool - pointer to name of pool BE lives in.
+ * boot_pool - Used if the pool containing the grub menu is
+ * different than the one contaiing the BE. This
+ * will normally be NULL.
+ * be_orig_root_ds - The root dataset for the BE. This is
+ * used to check to see if an entry already exists
+ * for this BE.
+ * description - pointer to description of BE to be added in
+ * the title line for this BEs entry.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
+ char *be_orig_root_ds, char *description)
+{
+ zfs_handle_t *zhp = NULL;
+ char menu_file[MAXPATHLEN];
+ char be_root_ds[MAXPATHLEN];
+ char line[BUFSIZ];
+ char temp_line[BUFSIZ];
+ char title[MAXPATHLEN];
+ char *entries[BUFSIZ];
+ char *tmp_entries[BUFSIZ];
+ char *pool_mntpnt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ boolean_t found_be = B_FALSE;
+ boolean_t found_orig_be = B_FALSE;
+ boolean_t found_title = B_FALSE;
+ boolean_t pool_mounted = B_FALSE;
+ boolean_t collect_lines = B_FALSE;
+ FILE *menu_fp = NULL;
+ int err = 0, ret = BE_SUCCESS;
+ int i, num_tmp_lines = 0, num_lines = 0;
+
+ if (be_name == NULL || be_root_pool == NULL)
+ return (BE_ERR_INVAL);
+
+ if (boot_pool == NULL)
+ boot_pool = be_root_pool;
+
+ if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
+ be_print_err(gettext("be_append_menu: failed to open "
+ "pool dataset for %s: %s\n"), be_root_pool,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
+ &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_append_menu: pool dataset "
+ "(%s) could not be mounted\n"), be_root_pool);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
+ be_print_err(gettext("be_append_menu: pool "
+ "dataset (%s) is not mounted. Can't set "
+ "the default BE in the grub menu.\n"), be_root_pool);
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (be_has_grub()) {
+ (void) snprintf(menu_file, sizeof (menu_file),
+ "%s%s", pool_mntpnt, BE_GRUB_MENU);
+ } else {
+ (void) snprintf(menu_file, sizeof (menu_file),
+ "%s%s", pool_mntpnt, BE_SPARC_MENU);
+ }
+
+ be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
+
+ /*
+ * Iterate through menu first to make sure the BE doesn't already
+ * have an entry in the menu.
+ *
+ * Additionally while iterating through the menu, if we have an
+ * original root dataset for a BE we're cloning from, we need to keep
+ * track of that BE's menu entry. We will then use the lines from
+ * that entry to create the entry for the new BE.
+ */
+ if ((ret = be_open_menu(be_root_pool, pool_mntpnt, menu_file,
+ &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
+ goto cleanup;
+ } else if (menu_fp == NULL) {
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ free(pool_mntpnt);
+ pool_mntpnt = NULL;
+
+ while (fgets(line, BUFSIZ, menu_fp)) {
+ char *tok = NULL;
+
+ (void) strlcpy(temp_line, line, BUFSIZ);
+ tok = strtok(line, BE_WHITE_SPACE);
+
+ if (tok == NULL || tok[0] == '#') {
+ continue;
+ } else if (strcmp(tok, "title") == 0) {
+ collect_lines = B_FALSE;
+ if ((tok = strtok(NULL, "\n")) == NULL)
+ (void) strlcpy(title, "", sizeof (title));
+ else
+ (void) strlcpy(title, tok, sizeof (title));
+ found_title = B_TRUE;
+
+ if (num_tmp_lines != 0) {
+ for (i = 0; i < num_tmp_lines; i++) {
+ free(tmp_entries[i]);
+ tmp_entries[i] = NULL;
+ }
+ num_tmp_lines = 0;
+ }
+ } else if (strcmp(tok, "bootfs") == 0) {
+ char *bootfs = strtok(NULL, BE_WHITE_SPACE);
+ found_title = B_FALSE;
+ if (bootfs == NULL)
+ continue;
+
+ if (strcmp(bootfs, be_root_ds) == 0) {
+ found_be = B_TRUE;
+ break;
+ }
+
+ if (be_orig_root_ds != NULL &&
+ strcmp(bootfs, be_orig_root_ds) == 0 &&
+ !found_orig_be) {
+ char str[BUFSIZ];
+ found_orig_be = B_TRUE;
+ num_lines = 0;
+ /*
+ * Store the new title line
+ */
+ (void) snprintf(str, BUFSIZ, "title %s\n",
+ description ? description : be_name);
+ entries[num_lines] = strdup(str);
+ num_lines++;
+ /*
+ * If there are any lines between the title
+ * and the bootfs line store these. Also
+ * free the temporary lines.
+ */
+ for (i = 0; i < num_tmp_lines; i++) {
+ entries[num_lines] = tmp_entries[i];
+ tmp_entries[i] = NULL;
+ num_lines++;
+ }
+ num_tmp_lines = 0;
+ /*
+ * Store the new bootfs line.
+ */
+ (void) snprintf(str, BUFSIZ, "bootfs %s\n",
+ be_root_ds);
+ entries[num_lines] = strdup(str);
+ num_lines++;
+ collect_lines = B_TRUE;
+ }
+ } else if (found_orig_be && collect_lines) {
+ /*
+ * get the rest of the lines for the original BE and
+ * store them.
+ */
+ if (strstr(line, BE_GRUB_COMMENT) != NULL ||
+ strstr(line, "BOOTADM") != NULL)
+ continue;
+ entries[num_lines] = strdup(temp_line);
+ num_lines++;
+ } else if (found_title && !found_orig_be) {
+ tmp_entries[num_tmp_lines] = strdup(temp_line);
+ num_tmp_lines++;
+ }
+ }
+
+ (void) fclose(menu_fp);
+
+ if (found_be) {
+ /*
+ * If an entry for this BE was already in the menu, then if
+ * that entry's title matches what we would have put in
+ * return success. Otherwise return failure.
+ */
+ char *new_title = description ? description : be_name;
+
+ if (strcmp(title, new_title) == 0) {
+ ret = BE_SUCCESS;
+ goto cleanup;
+ } else {
+ if (be_remove_menu(be_name, be_root_pool,
+ boot_pool) != BE_SUCCESS) {
+ be_print_err(gettext("be_append_menu: "
+ "Failed to remove existing unusable "
+ "entry '%s' in boot menu.\n"), be_name);
+ ret = BE_ERR_BE_EXISTS;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* Append BE entry to the end of the file */
+ menu_fp = fopen(menu_file, "a+");
+ err = errno;
+ if (menu_fp == NULL) {
+ be_print_err(gettext("be_append_menu: failed "
+ "to open menu.lst file %s\n"), menu_file);
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ if (found_orig_be) {
+ /*
+ * write out all the stored lines
+ */
+ for (i = 0; i < num_lines; i++) {
+ (void) fprintf(menu_fp, "%s", entries[i]);
+ free(entries[i]);
+ }
+ num_lines = 0;
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (be_has_grub())
+ (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
+ ret = BE_SUCCESS;
+ } else {
+ (void) fprintf(menu_fp, "title %s\n",
+ description ? description : be_name);
+ (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (be_has_grub()) {
+ (void) fprintf(menu_fp, "kernel$ "
+ "/platform/i86pc/kernel/$ISADIR/unix -B "
+ "$ZFS-BOOTFS\n");
+ (void) fprintf(menu_fp, "module$ "
+ "/platform/i86pc/$ISADIR/boot_archive\n");
+ (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
+ }
+ ret = BE_SUCCESS;
+ }
+ (void) fclose(menu_fp);
+cleanup:
+ if (pool_mounted) {
+ int err = BE_SUCCESS;
+ err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = err;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(zhp);
+ if (num_tmp_lines > 0) {
+ for (i = 0; i < num_tmp_lines; i++) {
+ free(tmp_entries[i]);
+ tmp_entries[i] = NULL;
+ }
+ }
+ if (num_lines > 0) {
+ for (i = 0; i < num_lines; i++) {
+ free(entries[i]);
+ entries[i] = NULL;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Function: be_remove_menu
+ * Description: Removes a BE's entry from a menu.lst file.
+ * Parameters:
+ * be_name - the name of BE whose entry is to be removed from
+ * the menu.lst file.
+ * be_root_pool - the pool that be_name lives in.
+ * boot_pool - the pool where the BE is, if different than
+ * the pool containing the boot menu. If this is
+ * NULL it will be set to be_root_pool.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
+{
+ zfs_handle_t *zhp = NULL;
+ char be_root_ds[MAXPATHLEN];
+ char **buffer = NULL;
+ char menu_buf[BUFSIZ];
+ char menu[MAXPATHLEN];
+ char *pool_mntpnt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ char *tmp_menu = NULL;
+ FILE *menu_fp = NULL;
+ FILE *tmp_menu_fp = NULL;
+ struct stat sb;
+ int ret = BE_SUCCESS;
+ int i;
+ int fd;
+ int err = 0;
+ int nlines = 0;
+ int default_entry = 0;
+ int entry_cnt = 0;
+ int entry_del = 0;
+ int num_entry_del = 0;
+ int tmp_menu_len = 0;
+ boolean_t write = B_TRUE;
+ boolean_t do_buffer = B_FALSE;
+ boolean_t pool_mounted = B_FALSE;
+
+ if (boot_pool == NULL)
+ boot_pool = be_root_pool;
+
+ /* Get name of BE's root dataset */
+ be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
+
+ /* Get handle to pool dataset */
+ if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
+ be_print_err(gettext("be_remove_menu: "
+ "failed to open pool dataset for %s: %s"),
+ be_root_pool, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
+ &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_remove_menu: pool dataset "
+ "(%s) could not be mounted\n"), be_root_pool);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
+ be_print_err(gettext("be_remove_menu: pool "
+ "dataset (%s) is not mounted. Can't set "
+ "the default BE in the grub menu.\n"), be_root_pool);
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ /* Get path to boot menu */
+ (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (be_has_grub())
+ (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
+ else
+ (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
+
+ /* Get handle to boot menu file */
+ if ((ret = be_open_menu(be_root_pool, pool_mntpnt, menu, &menu_fp, "r",
+ B_TRUE)) != BE_SUCCESS) {
+ goto cleanup;
+ } else if (menu_fp == NULL) {
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ free(pool_mntpnt);
+ pool_mntpnt = NULL;
+
+ /* Grab the stats of the original menu file */
+ if (stat(menu, &sb) != 0) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "failed to stat file %s: %s\n"), menu, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ /* Create a tmp file for the modified menu.lst */
+ tmp_menu_len = strlen(menu) + 7;
+ if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
+ be_print_err(gettext("be_remove_menu: malloc failed\n"));
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ (void) memset(tmp_menu, 0, tmp_menu_len);
+ (void) strlcpy(tmp_menu, menu, tmp_menu_len);
+ (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
+ if ((fd = mkstemp(tmp_menu)) == -1) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
+ ret = errno_to_be_err(err);
+ free(tmp_menu);
+ tmp_menu = NULL;
+ goto cleanup;
+ }
+ if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "could not open tmp file for write: %s\n"), strerror(err));
+ (void) close(fd);
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ while (fgets(menu_buf, BUFSIZ, menu_fp)) {
+ char tline [BUFSIZ];
+ char *tok = NULL;
+
+ (void) strlcpy(tline, menu_buf, sizeof (tline));
+
+ /* Tokenize line */
+ tok = strtok(tline, BE_WHITE_SPACE);
+
+ if (tok == NULL || tok[0] == '#') {
+ /* Found empty line or comment line */
+ if (do_buffer) {
+ /* Buffer this line */
+ if ((buffer = (char **)realloc(buffer,
+ sizeof (char *)*(nlines + 1))) == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ if ((buffer[nlines++] = strdup(menu_buf))
+ == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+
+ } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
+ strlen(BE_GRUB_COMMENT)) != 0) {
+ /* Write this line out */
+ (void) fputs(menu_buf, tmp_menu_fp);
+ }
+ } else if (strcmp(tok, "default") == 0) {
+ /*
+ * Record what 'default' is set to because we might
+ * need to adjust this upon deleting an entry.
+ */
+ tok = strtok(NULL, BE_WHITE_SPACE);
+
+ if (tok != NULL) {
+ default_entry = atoi(tok);
+ }
+
+ (void) fputs(menu_buf, tmp_menu_fp);
+ } else if (strcmp(tok, "title") == 0) {
+ /*
+ * If we've reached a 'title' line and do_buffer is
+ * is true, that means we've just buffered an entire
+ * entry without finding a 'bootfs' directive. We
+ * need to write that entry out and keep searching.
+ */
+ if (do_buffer) {
+ for (i = 0; i < nlines; i++) {
+ (void) fputs(buffer[i], tmp_menu_fp);
+ free(buffer[i]);
+ }
+ free(buffer);
+ buffer = NULL;
+ nlines = 0;
+ }
+
+ /*
+ * Turn writing off and buffering on, and increment
+ * our entry counter.
+ */
+ write = B_FALSE;
+ do_buffer = B_TRUE;
+ entry_cnt++;
+
+ /* Buffer this 'title' line */
+ if ((buffer = (char **)realloc(buffer,
+ sizeof (char *)*(nlines + 1))) == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+
+ } else if (strcmp(tok, "bootfs") == 0) {
+ char *bootfs = NULL;
+
+ /*
+ * Found a 'bootfs' line. See if it matches the
+ * BE we're looking for.
+ */
+ if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
+ strcmp(bootfs, be_root_ds) != 0) {
+ /*
+ * Either there's nothing after the 'bootfs'
+ * or this is not the BE we're looking for,
+ * write out the line(s) we've buffered since
+ * finding the title.
+ */
+ for (i = 0; i < nlines; i++) {
+ (void) fputs(buffer[i], tmp_menu_fp);
+ free(buffer[i]);
+ }
+ free(buffer);
+ buffer = NULL;
+ nlines = 0;
+
+ /*
+ * Turn writing back on, and turn off buffering
+ * since this isn't the entry we're looking
+ * for.
+ */
+ write = B_TRUE;
+ do_buffer = B_FALSE;
+
+ /* Write this 'bootfs' line out. */
+ (void) fputs(menu_buf, tmp_menu_fp);
+ } else {
+ /*
+ * Found the entry we're looking for.
+ * Record its entry number, increment the
+ * number of entries we've deleted, and turn
+ * writing off. Also, throw away the lines
+ * we've buffered for this entry so far, we
+ * don't need them.
+ */
+ entry_del = entry_cnt - 1;
+ num_entry_del++;
+ write = B_FALSE;
+ do_buffer = B_FALSE;
+
+ for (i = 0; i < nlines; i++) {
+ free(buffer[i]);
+ }
+ free(buffer);
+ buffer = NULL;
+ nlines = 0;
+ }
+ } else {
+ if (do_buffer) {
+ /* Buffer this line */
+ if ((buffer = (char **)realloc(buffer,
+ sizeof (char *)*(nlines + 1))) == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ if ((buffer[nlines++] = strdup(menu_buf))
+ == NULL) {
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ } else if (write) {
+ /* Write this line out */
+ (void) fputs(menu_buf, tmp_menu_fp);
+ }
+ }
+ }
+
+ (void) fclose(menu_fp);
+ menu_fp = NULL;
+ (void) fclose(tmp_menu_fp);
+ tmp_menu_fp = NULL;
+
+ /* Copy the modified menu.lst into place */
+ if (rename(tmp_menu, menu) != 0) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "failed to rename file %s to %s: %s\n"),
+ tmp_menu, menu, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+ free(tmp_menu);
+ tmp_menu = NULL;
+
+ /*
+ * If we've removed an entry, see if we need to
+ * adjust the default value in the menu.lst. If the
+ * entry we've deleted comes before the default entry
+ * we need to adjust the default value accordingly.
+ *
+ * be_has_grub is used here to check to see if this system
+ * supports grub.
+ */
+ if (be_has_grub() && num_entry_del > 0) {
+ if (entry_del <= default_entry) {
+ default_entry = default_entry - num_entry_del;
+ if (default_entry < 0)
+ default_entry = 0;
+
+ /*
+ * Adjust the default value by rewriting the
+ * menu.lst file. This may be overkill, but to
+ * preserve the location of the 'default' entry
+ * in the file, we need to do this.
+ */
+
+ /* Get handle to boot menu file */
+ if ((menu_fp = fopen(menu, "r")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "failed to open menu.lst (%s): %s\n"),
+ menu, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ /* Create a tmp file for the modified menu.lst */
+ tmp_menu_len = strlen(menu) + 7;
+ if ((tmp_menu = (char *)malloc(tmp_menu_len))
+ == NULL) {
+ be_print_err(gettext("be_remove_menu: "
+ "malloc failed\n"));
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ (void) memset(tmp_menu, 0, tmp_menu_len);
+ (void) strlcpy(tmp_menu, menu, tmp_menu_len);
+ (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
+ if ((fd = mkstemp(tmp_menu)) == -1) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "mkstemp failed: %s\n"), strerror(err));
+ ret = errno_to_be_err(err);
+ free(tmp_menu);
+ tmp_menu = NULL;
+ goto cleanup;
+ }
+ if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "could not open tmp file for write: %s\n"),
+ strerror(err));
+ (void) close(fd);
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ while (fgets(menu_buf, BUFSIZ, menu_fp)) {
+ char tline [BUFSIZ];
+ char *tok = NULL;
+
+ (void) strlcpy(tline, menu_buf, sizeof (tline));
+
+ /* Tokenize line */
+ tok = strtok(tline, BE_WHITE_SPACE);
+
+ if (tok == NULL) {
+ /* Found empty line, write it out */
+ (void) fputs(menu_buf, tmp_menu_fp);
+ } else if (strcmp(tok, "default") == 0) {
+ /* Found the default line, adjust it */
+ (void) snprintf(tline, sizeof (tline),
+ "default %d\n", default_entry);
+
+ (void) fputs(tline, tmp_menu_fp);
+ } else {
+ /* Pass through all other lines */
+ (void) fputs(menu_buf, tmp_menu_fp);
+ }
+ }
+
+ (void) fclose(menu_fp);
+ menu_fp = NULL;
+ (void) fclose(tmp_menu_fp);
+ tmp_menu_fp = NULL;
+
+ /* Copy the modified menu.lst into place */
+ if (rename(tmp_menu, menu) != 0) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "failed to rename file %s to %s: %s\n"),
+ tmp_menu, menu, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ free(tmp_menu);
+ tmp_menu = NULL;
+ }
+ }
+
+ /* Set the perms and ownership of the updated file */
+ if (chmod(menu, sb.st_mode) != 0) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "failed to chmod %s: %s\n"), menu, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+ if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
+ err = errno;
+ be_print_err(gettext("be_remove_menu: "
+ "failed to chown %s: %s\n"), menu, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+cleanup:
+ if (pool_mounted) {
+ int err = BE_SUCCESS;
+ err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = err;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(zhp);
+
+ free(buffer);
+ if (menu_fp != NULL)
+ (void) fclose(menu_fp);
+ if (tmp_menu_fp != NULL)
+ (void) fclose(tmp_menu_fp);
+ if (tmp_menu != NULL) {
+ (void) unlink(tmp_menu);
+ free(tmp_menu);
+ }
+
+ return (ret);
+}
+
+/*
+ * Function: be_default_grub_bootfs
+ * Description: This function returns the dataset in the default entry of
+ * the grub menu. If no default entry is found with a valid bootfs
+ * entry NULL is returned.
+ * Parameters:
+ * be_root_pool - This is the name of the root pool where the
+ * grub menu can be found.
+ * def_bootfs - This is used to pass back the bootfs string. On
+ * error NULL is returned here.
+ * Returns:
+ * Success - BE_SUCCESS is returned.
+ * Failure - a be_errno_t is returned.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
+{
+ zfs_handle_t *zhp = NULL;
+ char grub_file[MAXPATHLEN];
+ FILE *menu_fp;
+ char line[BUFSIZ];
+ char *pool_mntpnt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ int default_entry = 0, entries = 0;
+ int found_default = 0;
+ int ret = BE_SUCCESS;
+ boolean_t pool_mounted = B_FALSE;
+
+ errno = 0;
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (!be_has_grub()) {
+ be_print_err(gettext("be_default_grub_bootfs: operation "
+ "not supported on this architecture\n"));
+ return (BE_ERR_NOTSUP);
+ }
+
+ *def_bootfs = NULL;
+
+ /* Get handle to pool dataset */
+ if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
+ be_print_err(gettext("be_default_grub_bootfs: "
+ "failed to open pool dataset for %s: %s"),
+ be_root_pool, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
+ &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_default_grub_bootfs: pool dataset "
+ "(%s) could not be mounted\n"), be_root_pool);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
+ be_print_err(gettext("be_default_grub_bootfs: failed "
+ "to get mount point for the root pool. Can't set "
+ "the default BE in the grub menu.\n"));
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
+ pool_mntpnt, BE_GRUB_MENU);
+
+ if ((ret = be_open_menu((char *)be_root_pool, pool_mntpnt, grub_file,
+ &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
+ goto cleanup;
+ } else if (menu_fp == NULL) {
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ free(pool_mntpnt);
+ pool_mntpnt = NULL;
+
+ while (fgets(line, BUFSIZ, menu_fp)) {
+ char *tok = strtok(line, BE_WHITE_SPACE);
+
+ if (tok != NULL && tok[0] != '#') {
+ if (!found_default) {
+ if (strcmp(tok, "default") == 0) {
+ tok = strtok(NULL, BE_WHITE_SPACE);
+ if (tok != NULL) {
+ default_entry = atoi(tok);
+ rewind(menu_fp);
+ found_default = 1;
+ }
+ }
+ continue;
+ }
+ if (strcmp(tok, "title") == 0) {
+ entries++;
+ } else if (default_entry == entries - 1) {
+ if (strcmp(tok, "bootfs") == 0) {
+ tok = strtok(NULL, BE_WHITE_SPACE);
+ (void) fclose(menu_fp);
+
+ if (tok == NULL) {
+ ret = BE_SUCCESS;
+ goto cleanup;
+ }
+
+ if ((*def_bootfs = strdup(tok)) !=
+ NULL) {
+ ret = BE_SUCCESS;
+ goto cleanup;
+ }
+ be_print_err(gettext(
+ "be_default_grub_bootfs: "
+ "memory allocation failed\n"));
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ } else if (default_entry < entries - 1) {
+ /*
+ * no bootfs entry for the default entry.
+ */
+ break;
+ }
+ }
+ }
+ (void) fclose(menu_fp);
+
+cleanup:
+ if (pool_mounted) {
+ int err = BE_SUCCESS;
+ err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = err;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_change_grub_default
+ * Description: This function takes two parameters. These are the name of
+ * the BE we want to have as the default booted in the grub
+ * menu and the root pool where the path to the grub menu exists.
+ * The code takes this and finds the BE's entry in the grub menu
+ * and changes the default entry to point to that entry in the
+ * list.
+ * Parameters:
+ * be_name - This is the name of the BE wanted as the default
+ * for the next boot.
+ * be_root_pool - This is the name of the root pool where the
+ * grub menu can be found.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_change_grub_default(char *be_name, char *be_root_pool)
+{
+ zfs_handle_t *zhp = NULL;
+ char grub_file[MAXPATHLEN];
+ char *temp_grub;
+ char *pool_mntpnt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ char line[BUFSIZ];
+ char temp_line[BUFSIZ];
+ char be_root_ds[MAXPATHLEN];
+ FILE *grub_fp = NULL;
+ FILE *temp_fp = NULL;
+ struct stat sb;
+ int temp_grub_len = 0;
+ int fd, entries = 0;
+ int err = 0;
+ int ret = BE_SUCCESS;
+ boolean_t found_default = B_FALSE;
+ boolean_t pool_mounted = B_FALSE;
+
+ errno = 0;
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (!be_has_grub()) {
+ be_print_err(gettext("be_change_grub_default: operation "
+ "not supported on this architecture\n"));
+ return (BE_ERR_NOTSUP);
+ }
+
+ /* Generate string for BE's root dataset */
+ be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
+
+ /* Get handle to pool dataset */
+ if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
+ be_print_err(gettext("be_change_grub_default: "
+ "failed to open pool dataset for %s: %s"),
+ be_root_pool, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
+ &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_change_grub_default: pool dataset "
+ "(%s) could not be mounted\n"), be_root_pool);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
+ be_print_err(gettext("be_change_grub_default: pool "
+ "dataset (%s) is not mounted. Can't set "
+ "the default BE in the grub menu.\n"), be_root_pool);
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
+ pool_mntpnt, BE_GRUB_MENU);
+
+ if ((ret = be_open_menu(be_root_pool, pool_mntpnt, grub_file,
+ &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
+ goto cleanup;
+ } else if (grub_fp == NULL) {
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ free(pool_mntpnt);
+ pool_mntpnt = NULL;
+
+ /* Grab the stats of the original menu file */
+ if (stat(grub_file, &sb) != 0) {
+ err = errno;
+ be_print_err(gettext("be_change_grub_default: "
+ "failed to stat file %s: %s\n"), grub_file, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ /* Create a tmp file for the modified menu.lst */
+ temp_grub_len = strlen(grub_file) + 7;
+ if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
+ be_print_err(gettext("be_change_grub_default: "
+ "malloc failed\n"));
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ (void) memset(temp_grub, 0, temp_grub_len);
+ (void) strlcpy(temp_grub, grub_file, temp_grub_len);
+ (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
+ if ((fd = mkstemp(temp_grub)) == -1) {
+ err = errno;
+ be_print_err(gettext("be_change_grub_default: "
+ "mkstemp failed: %s\n"), strerror(err));
+ ret = errno_to_be_err(err);
+ free(temp_grub);
+ temp_grub = NULL;
+ goto cleanup;
+ }
+ if ((temp_fp = fdopen(fd, "w")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_change_grub_default: "
+ "failed to open %s file: %s\n"),
+ temp_grub, strerror(err));
+ (void) close(fd);
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ while (fgets(line, BUFSIZ, grub_fp)) {
+ char *tok = strtok(line, BE_WHITE_SPACE);
+
+ if (tok == NULL || tok[0] == '#') {
+ continue;
+ } else if (strcmp(tok, "title") == 0) {
+ entries++;
+ continue;
+ } else if (strcmp(tok, "bootfs") == 0) {
+ char *bootfs = strtok(NULL, BE_WHITE_SPACE);
+ if (bootfs == NULL)
+ continue;
+
+ if (strcmp(bootfs, be_root_ds) == 0) {
+ found_default = B_TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!found_default) {
+ be_print_err(gettext("be_change_grub_default: failed "
+ "to find entry for %s in the grub menu\n"),
+ be_name);
+ ret = BE_ERR_BE_NOENT;
+ goto cleanup;
+ }
+
+ rewind(grub_fp);
+
+ while (fgets(line, BUFSIZ, grub_fp)) {
+ char *tok = NULL;
+
+ (void) strncpy(temp_line, line, BUFSIZ);
+
+ if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
+ strcmp(tok, "default") == 0) {
+ (void) snprintf(temp_line, BUFSIZ, "default %d\n",
+ entries - 1 >= 0 ? entries - 1 : 0);
+ (void) fputs(temp_line, temp_fp);
+ } else {
+ (void) fputs(line, temp_fp);
+ }
+ }
+
+ (void) fclose(grub_fp);
+ grub_fp = NULL;
+ (void) fclose(temp_fp);
+ temp_fp = NULL;
+
+ if (rename(temp_grub, grub_file) != 0) {
+ err = errno;
+ be_print_err(gettext("be_change_grub_default: "
+ "failed to rename file %s to %s: %s\n"),
+ temp_grub, grub_file, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+ free(temp_grub);
+ temp_grub = NULL;
+
+ /* Set the perms and ownership of the updated file */
+ if (chmod(grub_file, sb.st_mode) != 0) {
+ err = errno;
+ be_print_err(gettext("be_change_grub_default: "
+ "failed to chmod %s: %s\n"), grub_file, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+ if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
+ err = errno;
+ be_print_err(gettext("be_change_grub_default: "
+ "failed to chown %s: %s\n"), grub_file, strerror(err));
+ ret = errno_to_be_err(err);
+ }
+
+cleanup:
+ if (pool_mounted) {
+ int err = BE_SUCCESS;
+ err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = err;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(zhp);
+ if (grub_fp != NULL)
+ (void) fclose(grub_fp);
+ if (temp_fp != NULL)
+ (void) fclose(temp_fp);
+ if (temp_grub != NULL) {
+ (void) unlink(temp_grub);
+ free(temp_grub);
+ }
+
+ return (ret);
+}
+
+/*
+ * Function: be_update_menu
+ * Description: This function is used by be_rename to change the BE name in
+ * an existing entry in the grub menu to the new name of the BE.
+ * Parameters:
+ * be_orig_name - the original name of the BE
+ * be_new_name - the new name the BE is being renameed to.
+ * be_root_pool - The pool which contains the grub menu
+ * boot_pool - the pool where the BE is, if different than
+ * the pool containing the boot menu. If this is
+ * NULL it will be set to be_root_pool.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
+ char *boot_pool)
+{
+ zfs_handle_t *zhp = NULL;
+ char menu_file[MAXPATHLEN];
+ char be_root_ds[MAXPATHLEN];
+ char be_new_root_ds[MAXPATHLEN];
+ char line[BUFSIZ];
+ char *pool_mntpnt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ char *temp_menu = NULL;
+ FILE *menu_fp = NULL;
+ FILE *new_fp = NULL;
+ struct stat sb;
+ int temp_menu_len = 0;
+ int tmp_fd;
+ int ret = BE_SUCCESS;
+ int err = 0;
+ boolean_t pool_mounted = B_FALSE;
+
+ errno = 0;
+
+ if (boot_pool == NULL)
+ boot_pool = be_root_pool;
+
+ if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
+ be_print_err(gettext("be_update_menu: failed to open "
+ "pool dataset for %s: %s\n"), be_root_pool,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
+ &pool_mounted)) != BE_SUCCESS) {
+ be_print_err(gettext("be_update_menu: pool dataset "
+ "(%s) could not be mounted\n"), be_root_pool);
+ ZFS_CLOSE(zhp);
+ return (ret);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
+ be_print_err(gettext("be_update_menu: failed "
+ "to get mount point for the root pool. Can't set "
+ "the default BE in the grub menu.\n"));
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (be_has_grub()) {
+ (void) snprintf(menu_file, sizeof (menu_file),
+ "%s%s", pool_mntpnt, BE_GRUB_MENU);
+ } else {
+ (void) snprintf(menu_file, sizeof (menu_file),
+ "%s%s", pool_mntpnt, BE_SPARC_MENU);
+ }
+
+ be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
+ sizeof (be_root_ds));
+ be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
+ sizeof (be_new_root_ds));
+
+ if ((ret = be_open_menu(be_root_pool, pool_mntpnt, menu_file,
+ &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
+ goto cleanup;
+ } else if (menu_fp == NULL) {
+ ret = BE_ERR_NO_MENU;
+ goto cleanup;
+ }
+
+ free(pool_mntpnt);
+ pool_mntpnt = NULL;
+
+ /* Grab the stat of the original menu file */
+ if (stat(menu_file, &sb) != 0) {
+ err = errno;
+ be_print_err(gettext("be_update_menu: "
+ "failed to stat file %s: %s\n"), menu_file, strerror(err));
+ (void) fclose(menu_fp);
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ /* Create tmp file for modified menu.lst */
+ temp_menu_len = strlen(menu_file) + 7;
+ if ((temp_menu = (char *)malloc(temp_menu_len))
+ == NULL) {
+ be_print_err(gettext("be_update_menu: "
+ "malloc failed\n"));
+ (void) fclose(menu_fp);
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ (void) memset(temp_menu, 0, temp_menu_len);
+ (void) strlcpy(temp_menu, menu_file, temp_menu_len);
+ (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
+ if ((tmp_fd = mkstemp(temp_menu)) == -1) {
+ err = errno;
+ be_print_err(gettext("be_update_menu: "
+ "mkstemp failed: %s\n"), strerror(err));
+ (void) fclose(menu_fp);
+ free(temp_menu);
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+ if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
+ err = errno;
+ be_print_err(gettext("be_update_menu: "
+ "fdopen failed: %s\n"), strerror(err));
+ (void) close(tmp_fd);
+ (void) fclose(menu_fp);
+ free(temp_menu);
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ while (fgets(line, BUFSIZ, menu_fp)) {
+ char tline[BUFSIZ];
+ char new_line[BUFSIZ];
+ char *c = NULL;
+
+ (void) strlcpy(tline, line, sizeof (tline));
+
+ /* Tokenize line */
+ c = strtok(tline, BE_WHITE_SPACE);
+
+ if (c == NULL) {
+ /* Found empty line, write it out. */
+ (void) fputs(line, new_fp);
+ } else if (c[0] == '#') {
+ /* Found a comment line, write it out. */
+ (void) fputs(line, new_fp);
+ } else if (strcmp(c, "title") == 0) {
+ char *name = NULL;
+ char *desc = NULL;
+
+ /*
+ * Found a 'title' line, parse out BE name or
+ * the description.
+ */
+ name = strtok(NULL, BE_WHITE_SPACE);
+
+ if (name == NULL) {
+ /*
+ * Nothing after 'title', just push
+ * this line through
+ */
+ (void) fputs(line, new_fp);
+ } else {
+ /*
+ * Grab the remainder of the title which
+ * could be a multi worded description
+ */
+ desc = strtok(NULL, "\n");
+
+ if (strcmp(name, be_orig_name) == 0) {
+ /*
+ * The first token of the title is
+ * the old BE name, replace it with
+ * the new one, and write it out
+ * along with the remainder of
+ * description if there is one.
+ */
+ if (desc) {
+ (void) snprintf(new_line,
+ sizeof (new_line),
+ "title %s %s\n",
+ be_new_name, desc);
+ } else {
+ (void) snprintf(new_line,
+ sizeof (new_line),
+ "title %s\n", be_new_name);
+ }
+
+ (void) fputs(new_line, new_fp);
+ } else {
+ (void) fputs(line, new_fp);
+ }
+ }
+ } else if (strcmp(c, "bootfs") == 0) {
+ /*
+ * Found a 'bootfs' line, parse out the BE root
+ * dataset value.
+ */
+ char *root_ds = strtok(NULL, BE_WHITE_SPACE);
+
+ if (root_ds == NULL) {
+ /*
+ * Nothing after 'bootfs', just push
+ * this line through
+ */
+ (void) fputs(line, new_fp);
+ } else {
+ /*
+ * If this bootfs is the one we're renaming,
+ * write out the new root dataset value
+ */
+ if (strcmp(root_ds, be_root_ds) == 0) {
+ (void) snprintf(new_line,
+ sizeof (new_line), "bootfs %s\n",
+ be_new_root_ds);
+
+ (void) fputs(new_line, new_fp);
+ } else {
+ (void) fputs(line, new_fp);
+ }
+ }
+ } else {
+ /*
+ * Found some other line we don't care
+ * about, write it out.
+ */
+ (void) fputs(line, new_fp);
+ }
+ }
+
+ (void) fclose(menu_fp);
+ (void) fclose(new_fp);
+ (void) close(tmp_fd);
+
+ if (rename(temp_menu, menu_file) != 0) {
+ err = errno;
+ be_print_err(gettext("be_update_menu: "
+ "failed to rename file %s to %s: %s\n"),
+ temp_menu, menu_file, strerror(err));
+ ret = errno_to_be_err(err);
+ }
+ free(temp_menu);
+
+ /* Set the perms and ownership of the updated file */
+ if (chmod(menu_file, sb.st_mode) != 0) {
+ err = errno;
+ be_print_err(gettext("be_update_menu: "
+ "failed to chmod %s: %s\n"), menu_file, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+ if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
+ err = errno;
+ be_print_err(gettext("be_update_menu: "
+ "failed to chown %s: %s\n"), menu_file, strerror(err));
+ ret = errno_to_be_err(err);
+ }
+
+cleanup:
+ if (pool_mounted) {
+ int err = BE_SUCCESS;
+ err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
+ if (ret == BE_SUCCESS)
+ ret = err;
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_has_menu_entry
+ * Description: Checks to see if the BEs root dataset has an entry in the grub
+ * menu.
+ * Parameters:
+ * be_dataset - The root dataset of the BE
+ * be_root_pool - The pool which contains the boot menu
+ * entry - A pointer the the entry number of the BE if found.
+ * Returns:
+ * B_TRUE - Success
+ * B_FALSE - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
+{
+ zfs_handle_t *zhp = NULL;
+ char menu_file[MAXPATHLEN];
+ FILE *menu_fp;
+ char line[BUFSIZ];
+ char *last;
+ char *rpool_mntpnt = NULL;
+ char *ptmp_mntpnt = NULL;
+ char *orig_mntpnt = NULL;
+ int ent_num = 0;
+ boolean_t ret = 0;
+ boolean_t pool_mounted = B_FALSE;
+
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
+ be_print_err(gettext("be_has_menu_entry: failed to open "
+ "pool dataset for %s: %s\n"), be_root_pool,
+ libzfs_error_description(g_zfs));
+ return (B_FALSE);
+ }
+
+ /*
+ * Check to see if the pool's dataset is mounted. If it isn't we'll
+ * attempt to mount it.
+ */
+ if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
+ &pool_mounted) != 0) {
+ be_print_err(gettext("be_has_menu_entry: pool dataset "
+ "(%s) could not be mounted\n"), be_root_pool);
+ ZFS_CLOSE(zhp);
+ return (B_FALSE);
+ }
+
+ /*
+ * Get the mountpoint for the root pool dataset.
+ */
+ if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
+ be_print_err(gettext("be_has_menu_entry: pool "
+ "dataset (%s) is not mounted. Can't set "
+ "the default BE in the grub menu.\n"), be_root_pool);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (be_has_grub()) {
+ (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
+ rpool_mntpnt, BE_GRUB_MENU);
+ } else {
+ (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
+ rpool_mntpnt, BE_SPARC_MENU);
+ }
+
+ if (be_open_menu(be_root_pool, rpool_mntpnt, menu_file, &menu_fp, "r",
+ B_FALSE) != 0) {
+ ret = B_FALSE;
+ goto cleanup;
+ } else if (menu_fp == NULL) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ free(rpool_mntpnt);
+ rpool_mntpnt = NULL;
+
+ while (fgets(line, BUFSIZ, menu_fp)) {
+ char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
+
+ if (tok != NULL && tok[0] != '#') {
+ if (strcmp(tok, "bootfs") == 0) {
+ tok = strtok_r(last, BE_WHITE_SPACE, &last);
+ if (tok != NULL && strcmp(tok,
+ be_dataset) == 0) {
+ (void) fclose(menu_fp);
+ /*
+ * The entry number needs to be
+ * decremented here because the title
+ * will always be the first line for
+ * an entry. Because of this we'll
+ * always be off by one entry when we
+ * check for bootfs.
+ */
+ *entry = ent_num - 1;
+ ret = B_TRUE;
+ goto cleanup;
+ }
+ } else if (strcmp(tok, "title") == 0)
+ ent_num++;
+ }
+ }
+
+cleanup:
+ if (pool_mounted) {
+ (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
+ free(orig_mntpnt);
+ free(ptmp_mntpnt);
+ }
+ ZFS_CLOSE(zhp);
+ (void) fclose(menu_fp);
+ return (ret);
+}
+
+/*
+ * Function: be_update_vfstab
+ * Description: This function digs into a BE's vfstab and updates all
+ * entries with file systems listed in be_fs_list_data_t.
+ * The entry's root container dataset and be_name will be
+ * updated with the parameters passed in.
+ * Parameters:
+ * be_name - name of BE to update
+ * old_rc_loc - dataset under which the root container dataset
+ * of the old BE resides in.
+ * new_rc_loc - dataset under which the root container dataset
+ * of the new BE resides in.
+ * fld - be_fs_list_data_t pointer providing the list of
+ * file systems to look for in vfstab.
+ * mountpoint - directory of where BE is currently mounted.
+ * If NULL, then BE is not currently mounted.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
+ be_fs_list_data_t *fld, char *mountpoint)
+{
+ char *tmp_mountpoint = NULL;
+ char alt_vfstab[MAXPATHLEN];
+ int ret = BE_SUCCESS, err = BE_SUCCESS;
+
+ if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
+ return (BE_SUCCESS);
+
+ /* If BE not already mounted, mount the BE */
+ if (mountpoint == NULL) {
+ if ((ret = _be_mount(be_name, &tmp_mountpoint,
+ BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
+ be_print_err(gettext("be_update_vfstab: "
+ "failed to mount BE (%s)\n"), be_name);
+ return (ret);
+ }
+ } else {
+ tmp_mountpoint = mountpoint;
+ }
+
+ /* Get string for vfstab in the mounted BE. */
+ (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
+ tmp_mountpoint);
+
+ /* Update the vfstab */
+ ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
+ fld);
+
+ /* Unmount BE if we mounted it */
+ if (mountpoint == NULL) {
+ if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
+ /* Remove temporary mountpoint */
+ (void) rmdir(tmp_mountpoint);
+ } else {
+ be_print_err(gettext("be_update_vfstab: "
+ "failed to unmount BE %s mounted at %s\n"),
+ be_name, tmp_mountpoint);
+ if (ret == BE_SUCCESS)
+ ret = err;
+ }
+
+ free(tmp_mountpoint);
+ }
+
+ return (ret);
+}
+
+/*
+ * Function: be_update_zone_vfstab
+ * Description: This function digs into a zone BE's vfstab and updates all
+ * entries with file systems listed in be_fs_list_data_t.
+ * The entry's root container dataset and be_name will be
+ * updated with the parameters passed in.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to zone root dataset.
+ * be_name - name of zone BE to update
+ * old_rc_loc - dataset under which the root container dataset
+ * of the old zone BE resides in.
+ * new_rc_loc - dataset under which the root container dataset
+ * of the new zone BE resides in.
+ * fld - be_fs_list_data_t pointer providing the list of
+ * file systems to look for in vfstab.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
+ char *new_rc_loc, be_fs_list_data_t *fld)
+{
+ be_mount_data_t md = { 0 };
+ be_unmount_data_t ud = { 0 };
+ char alt_vfstab[MAXPATHLEN];
+ boolean_t mounted_here = B_FALSE;
+ int ret = BE_SUCCESS;
+
+ /*
+ * If zone root not already mounted, mount it at a
+ * temporary location.
+ */
+ if (!zfs_is_mounted(zhp, &md.altroot)) {
+ /* Generate temporary mountpoint to mount zone root */
+ if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
+ be_print_err(gettext("be_update_zone_vfstab: "
+ "failed to make temporary mountpoint to "
+ "mount zone root\n"));
+ return (ret);
+ }
+
+ if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
+ be_print_err(gettext("be_update_zone_vfstab: "
+ "failed to mount zone root %s\n"),
+ zfs_get_name(zhp));
+ free(md.altroot);
+ return (BE_ERR_MOUNT_ZONEROOT);
+ }
+ mounted_here = B_TRUE;
+ }
+
+ /* Get string from vfstab in the mounted zone BE */
+ (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
+ md.altroot);
+
+ /* Update the vfstab */
+ ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
+ fld);
+
+ /* Unmount zone root if we mounted it */
+ if (mounted_here) {
+ ud.force = B_TRUE;
+
+ if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
+ /* Remove the temporary mountpoint */
+ (void) rmdir(md.altroot);
+ } else {
+ be_print_err(gettext("be_update_zone_vfstab: "
+ "failed to unmount zone root %s from %s\n"),
+ zfs_get_name(zhp), md.altroot);
+ if (ret == 0)
+ ret = BE_ERR_UMOUNT_ZONEROOT;
+ }
+ }
+
+ free(md.altroot);
+ return (ret);
+}
+
+/*
+ * Function: be_auto_snap_name
+ * Description: Generate an auto snapshot name constructed based on the
+ * current date and time. The auto snapshot name is of the form:
+ *
+ * <date>-<time>
+ *
+ * where <date> is in ISO standard format, so the resultant name
+ * is of the form:
+ *
+ * %Y-%m-%d-%H:%M:%S
+ *
+ * Parameters:
+ * None
+ * Returns:
+ * Success - pointer to auto generated snapshot name. The name
+ * is allocated in heap storage so the caller is
+ * responsible for free'ing the name.
+ * Failure - NULL
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+char *
+be_auto_snap_name(void)
+{
+ time_t utc_tm = NULL;
+ struct tm *gmt_tm = NULL;
+ char gmt_time_str[64];
+ char *auto_snap_name = NULL;
+
+ if (time(&utc_tm) == -1) {
+ be_print_err(gettext("be_auto_snap_name: time() failed\n"));
+ return (NULL);
+ }
+
+ if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
+ be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
+ return (NULL);
+ }
+
+ (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
+
+ if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
+ be_print_err(gettext("be_auto_snap_name: "
+ "memory allocation failed\n"));
+ return (NULL);
+ }
+
+ return (auto_snap_name);
+}
+
+/*
+ * Function: be_auto_be_name
+ * Description: Generate an auto BE name constructed based on the BE name
+ * of the original BE being cloned.
+ * Parameters:
+ * obe_name - name of the original BE being cloned.
+ * Returns:
+ * Success - pointer to auto generated BE name. The name
+ * is allocated in heap storage so the caller is
+ * responsible for free'ing the name.
+ * Failure - NULL
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+char *
+be_auto_be_name(char *obe_name)
+{
+ return (be_get_auto_name(obe_name, NULL, B_FALSE));
+}
+
+/*
+ * Function: be_auto_zone_be_name
+ * Description: Generate an auto BE name for a zone constructed based on
+ * the BE name of the original zone BE being cloned.
+ * Parameters:
+ * container_ds - container dataset for the zone.
+ * zbe_name - name of the original zone BE being cloned.
+ * Returns:
+ * Success - pointer to auto generated BE name. The name
+ * is allocated in heap storage so the caller is
+ * responsible for free'ing the name.
+ * Failure - NULL
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+char *
+be_auto_zone_be_name(char *container_ds, char *zbe_name)
+{
+ return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
+}
+
+/*
+ * Function: be_valid_be_name
+ * Description: Validates a BE name.
+ * Parameters:
+ * be_name - name of BE to validate
+ * Returns:
+ * B_TRUE - be_name is valid
+ * B_FALSE - be_name is invalid
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+
+boolean_t
+be_valid_be_name(const char *be_name)
+{
+ const char *c = NULL;
+
+ if (be_name == NULL)
+ return (B_FALSE);
+
+ /*
+ * A BE name must not be a multi-level dataset name. We also check
+ * that it does not contain the ' ' and '%' characters. The ' ' is
+ * a valid character for datasets, however we don't allow that in a
+ * BE name. The '%' is invalid, but zfs_name_valid() allows it for
+ * internal reasons, so we explicitly check for it here.
+ */
+ c = be_name;
+ while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
+ c++;
+
+ if (*c != '\0')
+ return (B_FALSE);
+
+ /*
+ * The BE name must comply with a zfs dataset filesystem. We also
+ * verify its length to be < BE_NAME_MAX_LEN.
+ */
+ if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
+ strlen(be_name) > BE_NAME_MAX_LEN)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+/*
+ * Function: be_valid_auto_snap_name
+ * Description: This function checks that a snapshot name is a valid auto
+ * generated snapshot name. A valid auto generated snapshot
+ * name is of the form:
+ *
+ * %Y-%m-%d-%H:%M:%S
+ *
+ * An older form of the auto generated snapshot name also
+ * included the snapshot's BE cleanup policy and a reserved
+ * field. Those names will also be verified by this function.
+ *
+ * Examples of valid auto snapshot names are:
+ *
+ * 2008-03-31-18:41:30
+ * 2008-03-31-22:17:24
+ * <policy>:-:2008:04-05-09:12:55
+ * <policy>:-:2008:04-06-15:34:12
+ *
+ * Parameters:
+ * name - name of the snapshot to be validated.
+ * Returns:
+ * B_TRUE - the name is a valid auto snapshot name.
+ * B_FALSE - the name is not a valid auto snapshot name.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+be_valid_auto_snap_name(char *name)
+{
+ struct tm gmt_tm;
+
+ char *policy = NULL;
+ char *reserved = NULL;
+ char *date = NULL;
+ char *c = NULL;
+
+ /* Validate the snapshot name by converting it into utc time */
+ if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
+ (mktime(&gmt_tm) != -1)) {
+ return (B_TRUE);
+ }
+
+ /*
+ * Validate the snapshot name against the older form of an
+ * auto generated snapshot name.
+ */
+ policy = strdup(name);
+
+ /*
+ * Get the first field from the snapshot name,
+ * which is the BE policy
+ */
+ c = strchr(policy, ':');
+ if (c == NULL) {
+ free(policy);
+ return (B_FALSE);
+ }
+ c[0] = '\0';
+
+ /* Validate the policy name */
+ if (!valid_be_policy(policy)) {
+ free(policy);
+ return (B_FALSE);
+ }
+
+ /* Get the next field, which is the reserved field. */
+ if (c[1] == NULL || c[1] == '\0') {
+ free(policy);
+ return (B_FALSE);
+ }
+ reserved = c+1;
+ c = strchr(reserved, ':');
+ if (c == NULL) {
+ free(policy);
+ return (B_FALSE);
+ }
+ c[0] = '\0';
+
+ /* Validate the reserved field */
+ if (strcmp(reserved, "-") != 0) {
+ free(policy);
+ return (B_FALSE);
+ }
+
+ /* The remaining string should be the date field */
+ if (c[1] == NULL || c[1] == '\0') {
+ free(policy);
+ return (B_FALSE);
+ }
+ date = c+1;
+
+ /* Validate the date string by converting it into utc time */
+ if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
+ (mktime(&gmt_tm) == -1)) {
+ be_print_err(gettext("be_valid_auto_snap_name: "
+ "invalid auto snapshot name\n"));
+ free(policy);
+ return (B_FALSE);
+ }
+
+ free(policy);
+ return (B_TRUE);
+}
+
+/*
+ * Function: be_default_policy
+ * Description: Temporary hardcoded policy support. This function returns
+ * the default policy type to be used to create a BE or a BE
+ * snapshot.
+ * Parameters:
+ * None
+ * Returns:
+ * Name of default BE policy.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+char *
+be_default_policy(void)
+{
+ return (BE_PLCY_STATIC);
+}
+
+/*
+ * Function: valid_be_policy
+ * Description: Temporary hardcoded policy support. This function valids
+ * whether a policy is a valid known policy or not.
+ * Paramters:
+ * policy - name of policy to validate.
+ * Returns:
+ * B_TRUE - policy is a valid.
+ * B_FALSE - policy is invalid.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+valid_be_policy(char *policy)
+{
+ if (policy == NULL)
+ return (B_FALSE);
+
+ if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
+ strcmp(policy, BE_PLCY_VOLATILE) == 0) {
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Function: be_print_err
+ * Description: This function prints out error messages if do_print is
+ * set to B_TRUE or if the BE_PRINT_ERR environment variable
+ * is set to true.
+ * Paramters:
+ * prnt_str - the string we wish to print and any arguments
+ * for the format of that string.
+ * Returns:
+ * void
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+void
+be_print_err(char *prnt_str, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+ char *env_buf;
+ static boolean_t env_checked = B_FALSE;
+
+ if (!env_checked) {
+ if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
+ if (strcasecmp(env_buf, "true") == 0) {
+ do_print = B_TRUE;
+ }
+ }
+ env_checked = B_TRUE;
+ }
+
+ if (do_print) {
+ va_start(ap, prnt_str);
+ /* LINTED variable format specifier */
+ (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
+ (void) fputs(buf, stderr);
+ va_end(ap);
+ }
+}
+
+/*
+ * Function: be_find_current_be
+ * Description: Find the currently "active" BE. Fill in the
+ * passed in be_transaction_data_t reference with the
+ * active BE's data.
+ * Paramters:
+ * none
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errnot_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ * Notes:
+ * The caller is responsible for initializing the libzfs handle
+ * and freeing the memory used by the active be_name.
+ */
+int
+be_find_current_be(be_transaction_data_t *bt)
+{
+ int zret;
+
+ if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
+ bt)) == 0) {
+ be_print_err(gettext("be_find_current_be: failed to "
+ "find current BE name\n"));
+ return (BE_ERR_BE_NOENT);
+ } else if (zret < 0) {
+ be_print_err(gettext("be_find_current_be: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_zpool_find_current_be_callback
+ * Description: Callback function used to iterate through all existing pools
+ * to find the BE that is the currently booted BE.
+ * Parameters:
+ * zlp - zpool_handle_t pointer to the current pool being
+ * looked at.
+ * data - be_transaction_data_t pointer.
+ * Upon successfully finding the current BE, the
+ * obe_zpool member of this parameter is set to the
+ * pool it is found in.
+ * Return:
+ * 1 - Found current BE in this pool.
+ * 0 - Did not find current BE in this pool.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
+{
+ be_transaction_data_t *bt = data;
+ zfs_handle_t *zhp = NULL;
+ const char *zpool = zpool_get_name(zlp);
+ char be_container_ds[MAXPATHLEN];
+
+ /*
+ * Generate string for BE container dataset
+ */
+ be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
+
+ /*
+ * Check if a BE container dataset exists in this pool.
+ */
+ if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
+ zpool_close(zlp);
+ return (0);
+ }
+
+ /*
+ * Get handle to this zpool's BE container dataset.
+ */
+ if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
+ NULL) {
+ be_print_err(gettext("be_zpool_find_current_be_callback: "
+ "failed to open BE container dataset (%s)\n"),
+ be_container_ds);
+ zpool_close(zlp);
+ return (0);
+ }
+
+ /*
+ * Iterate through all potential BEs in this zpool
+ */
+ if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
+ /*
+ * Found current BE dataset; set obe_zpool
+ */
+ if ((bt->obe_zpool = strdup(zpool)) == NULL) {
+ be_print_err(gettext(
+ "be_zpool_find_current_be_callback: "
+ "memory allocation failed\n"));
+ ZFS_CLOSE(zhp);
+ zpool_close(zlp);
+ return (0);
+ }
+
+ ZFS_CLOSE(zhp);
+ zpool_close(zlp);
+ return (1);
+ }
+
+ ZFS_CLOSE(zhp);
+ zpool_close(zlp);
+
+ return (0);
+}
+
+/*
+ * Function: be_zfs_find_current_be_callback
+ * Description: Callback function used to iterate through all BEs in a
+ * pool to find the BE that is the currently booted BE.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current filesystem being checked.
+ * data - be_transaction-data_t pointer
+ * Upon successfully finding the current BE, the
+ * obe_name and obe_root_ds members of this parameter
+ * are set to the BE name and BE's root dataset
+ * respectively.
+ * Return:
+ * 1 - Found current BE.
+ * 0 - Did not find current BE.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
+{
+ be_transaction_data_t *bt = data;
+ char *mp = NULL;
+
+ /*
+ * Check if dataset is mounted, and if so where.
+ */
+ if (zfs_is_mounted(zhp, &mp)) {
+ /*
+ * If mounted at root, set obe_root_ds and obe_name
+ */
+ if (mp != NULL && strcmp(mp, "/") == 0) {
+ free(mp);
+
+ if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
+ == NULL) {
+ be_print_err(gettext(
+ "be_zfs_find_current_be_callback: "
+ "memory allocation failed\n"));
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+ if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
+ == NULL) {
+ be_print_err(gettext(
+ "be_zfs_find_current_be_callback: "
+ "memory allocation failed\n"));
+ ZFS_CLOSE(zhp);
+ return (0);
+ }
+
+ ZFS_CLOSE(zhp);
+ return (1);
+ }
+
+ free(mp);
+ }
+ ZFS_CLOSE(zhp);
+
+ return (0);
+}
+
+/*
+ * Function: be_check_be_roots_callback
+ * Description: This function checks whether or not the dataset name passed
+ * is hierachically located under the BE root container dataset
+ * for this pool.
+ * Parameters:
+ * zlp - zpool_handle_t pointer to current pool being processed.
+ * data - name of dataset to check
+ * Returns:
+ * 0 - dataset is not in this pool's BE root container dataset
+ * 1 - dataset is in this pool's BE root container dataset
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
+{
+ const char *zpool = zpool_get_name(zlp);
+ char *ds = data;
+ char be_container_ds[MAXPATHLEN];
+
+ /* Generate string for this pool's BE root container dataset */
+ be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
+
+ /*
+ * If dataset lives under the BE root container dataset
+ * of this pool, return failure.
+ */
+ if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
+ ds[strlen(be_container_ds)] == '/') {
+ zpool_close(zlp);
+ return (1);
+ }
+
+ zpool_close(zlp);
+ return (0);
+}
+
+/*
+ * Function: zfs_err_to_be_err
+ * Description: This function takes the error stored in the libzfs handle
+ * and maps it to an be_errno_t. If there are no matching
+ * be_errno_t's then BE_ERR_ZFS is returned.
+ * Paramters:
+ * zfsh - The libzfs handle containing the error we're looking up.
+ * Returns:
+ * be_errno_t
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+zfs_err_to_be_err(libzfs_handle_t *zfsh)
+{
+ int err = libzfs_errno(zfsh);
+
+ switch (err) {
+ case 0:
+ return (BE_SUCCESS);
+ case EZFS_PERM:
+ return (BE_ERR_PERM);
+ case EZFS_INTR:
+ return (BE_ERR_INTR);
+ case EZFS_NOENT:
+ return (BE_ERR_NOENT);
+ case EZFS_NOSPC:
+ return (BE_ERR_NOSPC);
+ case EZFS_MOUNTFAILED:
+ return (BE_ERR_MOUNT);
+ case EZFS_UMOUNTFAILED:
+ return (BE_ERR_UMOUNT);
+ case EZFS_EXISTS:
+ return (BE_ERR_BE_EXISTS);
+ case EZFS_BUSY:
+ return (BE_ERR_DEV_BUSY);
+ case EZFS_PERMRDONLY:
+ return (BE_ERR_ROFS);
+ case EZFS_NAMETOOLONG:
+ return (BE_ERR_NAMETOOLONG);
+ case EZFS_NODEVICE:
+ return (BE_ERR_NODEV);
+ case EZFS_POOL_INVALARG:
+ return (BE_ERR_INVAL);
+ case EZFS_PROPTYPE:
+ return (BE_ERR_INVALPROP);
+ case EZFS_BADTYPE:
+ return (BE_ERR_DSTYPE);
+ case EZFS_PROPNONINHERIT:
+ return (BE_ERR_NONINHERIT);
+ case EZFS_PROPREADONLY:
+ return (BE_ERR_READONLYPROP);
+ case EZFS_RESILVERING:
+ case EZFS_POOLUNAVAIL:
+ return (BE_ERR_UNAVAIL);
+ case EZFS_DSREADONLY:
+ return (BE_ERR_READONLYDS);
+ default:
+ return (BE_ERR_ZFS);
+ }
+}
+
+/*
+ * Function: errno_to_be_err
+ * Description: This function takes an errno and maps it to an be_errno_t.
+ * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
+ * returned.
+ * Paramters:
+ * err - The errno we're compairing against.
+ * Returns:
+ * be_errno_t
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+errno_to_be_err(int err)
+{
+ switch (err) {
+ case EPERM:
+ return (BE_ERR_PERM);
+ case EACCES:
+ return (BE_ERR_ACCESS);
+ case ECANCELED:
+ return (BE_ERR_CANCELED);
+ case EINTR:
+ return (BE_ERR_INTR);
+ case ENOENT:
+ return (BE_ERR_NOENT);
+ case ENOSPC:
+ case EDQUOT:
+ return (BE_ERR_NOSPC);
+ case EEXIST:
+ return (BE_ERR_BE_EXISTS);
+ case EBUSY:
+ return (BE_ERR_BUSY);
+ case EROFS:
+ return (BE_ERR_ROFS);
+ case ENAMETOOLONG:
+ return (BE_ERR_NAMETOOLONG);
+ case ENXIO:
+ return (BE_ERR_NXIO);
+ case EINVAL:
+ return (BE_ERR_INVAL);
+ case EFAULT:
+ return (BE_ERR_FAULT);
+ default:
+ return (BE_ERR_UNKNOWN);
+ }
+}
+
+/*
+ * Function: be_err_to_str
+ * Description: This function takes a be_errno_t and maps it to a message.
+ * If there are no matching be_errno_t's then NULL is returned.
+ * Paramters:
+ * be_errno_t - The be_errno_t we're mapping.
+ * Returns:
+ * string or NULL if the error code is not known.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+char *
+be_err_to_str(int err)
+{
+ switch (err) {
+ case BE_ERR_ACCESS:
+ return (gettext("Permission denied."));
+ case BE_ERR_ACTIVATE_CURR:
+ return (gettext("Activation of current BE failed."));
+ case BE_ERR_AUTONAME:
+ return (gettext("Auto naming failed."));
+ case BE_ERR_BE_NOENT:
+ return (gettext("No such BE."));
+ case BE_ERR_BUSY:
+ return (gettext("Mount busy."));
+ case BE_ERR_DEV_BUSY:
+ return (gettext("Device busy."));
+ case BE_ERR_CANCELED:
+ return (gettext("Operation canceled."));
+ case BE_ERR_CLONE:
+ return (gettext("BE clone failed."));
+ case BE_ERR_COPY:
+ return (gettext("BE copy failed."));
+ case BE_ERR_CREATDS:
+ return (gettext("Dataset creation failed."));
+ case BE_ERR_CURR_BE_NOT_FOUND:
+ return (gettext("Can't find current BE."));
+ case BE_ERR_DESTROY:
+ return (gettext("Failed to destroy BE or snapshot."));
+ case BE_ERR_DESTROY_CURR_BE:
+ return (gettext("Cannot destroy current BE."));
+ case BE_ERR_DEMOTE:
+ return (gettext("BE demotion failed."));
+ case BE_ERR_DSTYPE:
+ return (gettext("Invalid dataset type."));
+ case BE_ERR_BE_EXISTS:
+ return (gettext("BE exists."));
+ case BE_ERR_INIT:
+ return (gettext("be_zfs_init failed."));
+ case BE_ERR_INTR:
+ return (gettext("Interupted system call."));
+ case BE_ERR_INVAL:
+ return (gettext("Invalid argument."));
+ case BE_ERR_INVALPROP:
+ return (gettext("Invalid property for dataset."));
+ case BE_ERR_INVALMOUNTPOINT:
+ return (gettext("Unexpected mountpoint."));
+ case BE_ERR_MOUNT:
+ return (gettext("Mount failed."));
+ case BE_ERR_MOUNTED:
+ return (gettext("Already mounted."));
+ case BE_ERR_NAMETOOLONG:
+ return (gettext("name > BUFSIZ."));
+ case BE_ERR_NOENT:
+ return (gettext("Doesn't exist."));
+ case BE_ERR_POOL_NOENT:
+ return (gettext("No such pool."));
+ case BE_ERR_NODEV:
+ return (gettext("No such device."));
+ case BE_ERR_NOTMOUNTED:
+ return (gettext("File system not mounted."));
+ case BE_ERR_NOMEM:
+ return (gettext("Not enough memory."));
+ case BE_ERR_NONINHERIT:
+ return (gettext(
+ "Property is not inheritable for the BE dataset."));
+ case BE_ERR_NXIO:
+ return (gettext("No such device or address."));
+ case BE_ERR_NOSPC:
+ return (gettext("No space on device."));
+ case BE_ERR_NOTSUP:
+ return (gettext("Operation not supported."));
+ case BE_ERR_OPEN:
+ return (gettext("Open failed."));
+ case BE_ERR_PERM:
+ return (gettext("Not owner."));
+ case BE_ERR_UNAVAIL:
+ return (gettext("The BE is currently unavailable."));
+ case BE_ERR_PROMOTE:
+ return (gettext("BE promotion failed."));
+ case BE_ERR_ROFS:
+ return (gettext("Read only file system."));
+ case BE_ERR_READONLYDS:
+ return (gettext("Read only dataset."));
+ case BE_ERR_READONLYPROP:
+ return (gettext("Read only property."));
+ case BE_ERR_RENAME_ACTIVE:
+ return (gettext("Renaming the active BE is not supported."));
+ case BE_ERR_SS_EXISTS:
+ return (gettext("Snapshot exists."));
+ case BE_ERR_SS_NOENT:
+ return (gettext("No such snapshot."));
+ case BE_ERR_UMOUNT:
+ return (gettext("Unmount failed."));
+ case BE_ERR_UMOUNT_CURR_BE:
+ return (gettext("Can't unmount the current BE."));
+ case BE_ERR_UMOUNT_SHARED:
+ return (gettext("Unmount of a shared File System failed."));
+ case BE_ERR_FAULT:
+ return (gettext("Bad address."));
+ case BE_ERR_UNKNOWN:
+ return (gettext("Unknown error."));
+ case BE_ERR_ZFS:
+ return (gettext("ZFS returned an error."));
+ case BE_ERR_GEN_UUID:
+ return (gettext("Failed to generate uuid."));
+ case BE_ERR_PARSE_UUID:
+ return (gettext("Failed to parse uuid."));
+ case BE_ERR_NO_UUID:
+ return (gettext("No uuid"));
+ case BE_ERR_ZONE_NO_PARENTBE:
+ return (gettext("No parent uuid"));
+ case BE_ERR_ZONE_MULTIPLE_ACTIVE:
+ return (gettext("Multiple active zone roots"));
+ case BE_ERR_ZONE_NO_ACTIVE_ROOT:
+ return (gettext("No active zone root"));
+ case BE_ERR_ZONE_ROOT_NOT_LEGACY:
+ return (gettext("Zone root not legacy"));
+ case BE_ERR_MOUNT_ZONEROOT:
+ return (gettext("Failed to mount a zone root."));
+ case BE_ERR_UMOUNT_ZONEROOT:
+ return (gettext("Failed to unmount a zone root."));
+ case BE_ERR_NO_MOUNTED_ZONE:
+ return (gettext("Zone is not mounted"));
+ case BE_ERR_ZONES_UNMOUNT:
+ return (gettext("Unable to unmount a zone BE."));
+ case BE_ERR_NO_MENU:
+ return (gettext("Missing boot menu file."));
+ case BE_ERR_BAD_MENU_PATH:
+ return (gettext("Invalid path for menu.lst file"));
+ case BE_ERR_ZONE_SS_EXISTS:
+ return (gettext("Zone snapshot exists."));
+ case BE_ERR_ADD_SPLASH_ICT:
+ return (gettext("Add_spash_image ICT failed."));
+ case BE_ERR_BOOTFILE_INST:
+ return (gettext("Error installing boot files."));
+ case BE_ERR_EXTCMD:
+ return (gettext("Error running an external command."));
+ default:
+ return (NULL);
+ }
+}
+
+/*
+ * Function: be_has_grub
+ * Description: Boolean function indicating whether the current system
+ * uses grub.
+ * Return: B_FALSE - the system does not have grub
+ * B_TRUE - the system does have grub.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+be_has_grub(void)
+{
+ /*
+ * TODO: This will need to be expanded to check for the existence of
+ * grub if and when there is grub support for SPARC.
+ */
+ return (be_is_isa("i386"));
+}
+
+/*
+ * Function: be_is_isa
+ * Description: Boolean function indicating whether the instruction set
+ * architecture of the executing system matches the name provided.
+ * The string must match a system defined architecture (e.g.
+ * "i386", "sparc") and is case sensitive.
+ * Parameters: name - string representing the name of instruction set
+ * architecture being tested
+ * Returns: B_FALSE - the system instruction set architecture is different
+ * from the one specified
+ * B_TRUE - the system instruction set architecture is the same
+ * as the one specified
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+be_is_isa(char *name)
+{
+ return ((strcmp((char *)be_get_default_isa(), name) == 0));
+}
+
+/*
+ * Function: be_get_default_isa
+ * Description:
+ * Returns the default instruction set architecture of the
+ * machine it is executed on. (eg. sparc, i386, ...)
+ * NOTE: SYS_INST environment variable may override default
+ * return value
+ * Parameters:
+ * none
+ * Returns:
+ * NULL - the architecture returned by sysinfo() was too
+ * long for local variables
+ * char * - pointer to a string containing the default
+ * implementation
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+char *
+be_get_default_isa(void)
+{
+ int i;
+ char *envp;
+ static char default_inst[ARCH_LENGTH] = "";
+
+ if (default_inst[0] == '\0') {
+ if ((envp = getenv("SYS_INST")) != NULL) {
+ if ((int)strlen(envp) >= ARCH_LENGTH)
+ return (NULL);
+ else
+ (void) strcpy(default_inst, envp);
+ } else {
+ i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
+ if (i < 0 || i > ARCH_LENGTH)
+ return (NULL);
+ }
+ }
+ return (default_inst);
+}
+
+/*
+ * Function: be_run_cmd
+ * Description:
+ * Runs a command in a separate subprocess. Splits out stdout from stderr
+ * and sends each to its own buffer. Buffers must be pre-allocated and
+ * passed in as arguments. Buffer sizes are also passed in as arguments.
+ *
+ * Notes / caveats:
+ * - Command being run is assumed to not have any stdout or stderr
+ * redirection.
+ * - Commands which emit total stderr output of greater than PIPE_BUF
+ * bytes can hang. For such commands, a different implementation
+ * which uses poll(2) must be used.
+ * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
+ * the stream which would have gone to it is sent to the bit
+ * bucket.
+ * - stderr_buf cannot be NULL.
+ * - Only subprocess errors are appended to the stderr_buf. Errors
+ * running the command are reported through be_print_err().
+ * - Data which would overflow its respective buffer is sent to the bit
+ * bucket.
+ *
+ * Parameters:
+ * command: command to run. Assumed not to have embedded stdout
+ * or stderr redirection. May have stdin redirection,
+ * however.
+ * stderr_buf: buffer returning subprocess stderr data. Errors
+ * reported by this function are reported through
+ * be_print_err().
+ * stderr_bufsize: size of stderr_buf
+ * stdout_buf: buffer returning subprocess stdout data.
+ * stdout_bufsize: size of stdout_buf
+ * Returns:
+ * BE_SUCCESS - The command ran successfully without returning
+ * errors.
+ * BE_ERR_EXTCMD
+ * - The command could not be run.
+ * - The command terminated with error status.
+ * - There were errors extracting or returning subprocess
+ * data.
+ * BE_ERR_NOMEM - The command exceeds the command buffer size.
+ * BE_ERR_INVAL - An invalid argument was specified.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
+ char *stdout_buf, int stdout_bufsize)
+{
+ char *temp_filename = strdup(tmpnam(NULL));
+ FILE *stdout_str = NULL;
+ FILE *stderr_str = NULL;
+ char cmdline[BUFSIZ];
+ char oneline[BUFSIZ];
+ int exit_status;
+ int rval = BE_SUCCESS;
+
+ if ((command == NULL) || (stderr_buf == NULL) ||
+ (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
+ ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
+ return (BE_ERR_INVAL);
+}
+
+ /* Set up command so popen returns stderr, not stdout */
+ if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
+ temp_filename) >= BUFSIZ) {
+ rval = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+
+ /* Set up the fifo that will make stderr available. */
+ if (mkfifo(temp_filename, 0600) != 0) {
+ (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
+ strerror(errno));
+ rval = BE_ERR_EXTCMD;
+ goto cleanup;
+ }
+
+ if ((stdout_str = popen(cmdline, "r")) == NULL) {
+ (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
+ strerror(errno));
+ rval = BE_ERR_EXTCMD;
+ goto cleanup;
+ }
+
+ if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
+ (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
+ strerror(errno));
+ (void) pclose(stdout_str);
+ rval = BE_ERR_EXTCMD;
+ goto cleanup;
+ }
+
+ /* Read stdout first, as it usually outputs more than stderr. */
+ oneline[BUFSIZ-1] = '\0';
+ while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
+ if (stdout_str != NULL) {
+ (void) strlcat(stdout_buf, oneline, stdout_bufsize);
+ }
+ }
+
+ while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
+ (void) strlcat(stderr_buf, oneline, stderr_bufsize);
+ }
+
+ /* Close pipe, get exit status. */
+ if ((exit_status = pclose(stdout_str)) == -1) {
+ (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
+ strerror(errno));
+ rval = BE_ERR_EXTCMD;
+ } else if (WIFEXITED(exit_status)) {
+ exit_status = (int)((char)WEXITSTATUS(exit_status));
+ if (exit_status != 0) {
+ (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
+ "command terminated with error status: %d\n"),
+ exit_status);
+ (void) strlcat(stderr_buf, oneline, stderr_bufsize);
+ rval = BE_ERR_EXTCMD;
+ }
+ } else {
+ (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
+ "terminated on signal: %s\n"),
+ strsignal(WTERMSIG(exit_status)));
+ (void) strlcat(stderr_buf, oneline, stderr_bufsize);
+ rval = BE_ERR_EXTCMD;
+ }
+
+cleanup:
+ (void) unlink(temp_filename);
+ (void) free(temp_filename);
+
+ return (rval);
+}
+
+/* ******************************************************************** */
+/* Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: update_dataset
+ * Description: This function takes a dataset name and replaces the zpool
+ * and be_name components of the dataset with the new be_name
+ * zpool passed in.
+ * Parameters:
+ * dataset - name of dataset
+ * dataset_len - lenth of buffer in which dataset is passed in.
+ * be_name - name of new BE name to update to.
+ * old_rc_loc - dataset under which the root container dataset
+ * for the old BE lives.
+ * new_rc_loc - dataset under which the root container dataset
+ * for the new BE lives.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+update_dataset(char *dataset, int dataset_len, char *be_name,
+ char *old_rc_loc, char *new_rc_loc)
+{
+ char *ds = NULL;
+ char *sub_ds = NULL;
+
+ /* Tear off the BE container dataset */
+ if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
+ return (BE_ERR_INVAL);
+ }
+
+ /* Get dataset name relative to BE root, if there is one */
+ sub_ds = strchr(ds, '/');
+
+ /* Generate the BE root dataset name */
+ be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
+
+ /* If a subordinate dataset name was found, append it */
+ if (sub_ds != NULL)
+ (void) strlcat(dataset, sub_ds, dataset_len);
+
+ free(ds);
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: _update_vfstab
+ * Description: This function updates a vfstab file to reflect the new
+ * root container dataset location and be_name for all
+ * entries listed in the be_fs_list_data_t structure passed in.
+ * Parameters:
+ * vfstab - vfstab file to modify
+ * be_name - name of BE to update.
+ * old_rc_loc - dataset under which the root container dataset
+ * of the old BE resides in.
+ * new_rc_loc - dataset under which the root container dataset
+ * of the new BE resides in.
+ * fld - be_fs_list_data_t pointer providing the list of
+ * file systems to look for in vfstab.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+_update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
+ char *new_rc_loc, be_fs_list_data_t *fld)
+{
+ struct vfstab vp;
+ char *tmp_vfstab = NULL;
+ char comments_buf[BUFSIZ];
+ FILE *comments = NULL;
+ FILE *vfs_ents = NULL;
+ FILE *tfile = NULL;
+ struct stat sb;
+ char dev[MAXPATHLEN];
+ char *c;
+ int fd;
+ int ret = BE_SUCCESS, err = 0;
+ int i;
+ int tmp_vfstab_len = 0;
+
+ errno = 0;
+
+ /*
+ * Open vfstab for reading twice. First is for comments,
+ * second is for actual entries.
+ */
+ if ((comments = fopen(vfstab, "r")) == NULL ||
+ (vfs_ents = fopen(vfstab, "r")) == NULL) {
+ err = errno;
+ be_print_err(gettext("_update_vfstab: "
+ "failed to open vfstab (%s): %s\n"), vfstab,
+ strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ /* Grab the stats of the original vfstab file */
+ if (stat(vfstab, &sb) != 0) {
+ err = errno;
+ be_print_err(gettext("_update_vfstab: "
+ "failed to stat file %s: %s\n"), vfstab,
+ strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ /* Create tmp file for modified vfstab */
+ if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
+ == NULL) {
+ be_print_err(gettext("_update_vfstab: "
+ "malloc failed\n"));
+ ret = BE_ERR_NOMEM;
+ goto cleanup;
+ }
+ tmp_vfstab_len = strlen(vfstab) + 7;
+ (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
+ (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
+ (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
+ if ((fd = mkstemp(tmp_vfstab)) == -1) {
+ err = errno;
+ be_print_err(gettext("_update_vfstab: "
+ "mkstemp failed: %s\n"), strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+ if ((tfile = fdopen(fd, "w")) == NULL) {
+ err = errno;
+ be_print_err(gettext("_update_vfstab: "
+ "could not open file for write\n"));
+ (void) close(fd);
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ while (fgets(comments_buf, BUFSIZ, comments)) {
+ for (c = comments_buf; *c != '\0' && isspace(*c); c++)
+ ;
+ if (*c == '\0') {
+ continue;
+ } else if (*c == '#') {
+ /*
+ * If line is a comment line, just put
+ * it through to the tmp vfstab.
+ */
+ (void) fputs(comments_buf, tfile);
+ } else {
+ /*
+ * Else line is a vfstab entry, grab it
+ * into a vfstab struct.
+ */
+ if (getvfsent(vfs_ents, &vp) != 0) {
+ err = errno;
+ be_print_err(gettext("_update_vfstab: "
+ "getvfsent failed: %s\n"), strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
+ (void) putvfsent(tfile, &vp);
+ continue;
+ }
+
+ /*
+ * If the entry is one of the entries in the list
+ * of file systems to update, modify it's device
+ * field to be correct for this BE.
+ */
+ for (i = 0; i < fld->fs_num; i++) {
+ if (strcmp(vp.vfs_special, fld->fs_list[i])
+ == 0) {
+ /*
+ * Found entry that needs an update.
+ * Replace the root container dataset
+ * location and be_name in the
+ * entry's device.
+ */
+ (void) strlcpy(dev, vp.vfs_special,
+ sizeof (dev));
+
+ if ((ret = update_dataset(dev,
+ sizeof (dev), be_name, old_rc_loc,
+ new_rc_loc)) != 0) {
+ be_print_err(
+ gettext("_update_vfstab: "
+ "Failed to update device "
+ "field for vfstab entry "
+ "%s\n"), fld->fs_list[i]);
+ goto cleanup;
+ }
+
+ vp.vfs_special = dev;
+ break;
+ }
+ }
+
+ /* Put entry through to tmp vfstab */
+ (void) putvfsent(tfile, &vp);
+ }
+ }
+
+ (void) fclose(comments);
+ comments = NULL;
+ (void) fclose(vfs_ents);
+ vfs_ents = NULL;
+ (void) fclose(tfile);
+ tfile = NULL;
+
+ /* Copy tmp vfstab into place */
+ if (rename(tmp_vfstab, vfstab) != 0) {
+ err = errno;
+ be_print_err(gettext("_update_vfstab: "
+ "failed to rename file %s to %s: %s\n"), tmp_vfstab,
+ vfstab, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+ /* Set the perms and ownership of the updated file */
+ if (chmod(vfstab, sb.st_mode) != 0) {
+ err = errno;
+ be_print_err(gettext("_update_vfstab: "
+ "failed to chmod %s: %s\n"), vfstab, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+ if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
+ err = errno;
+ be_print_err(gettext("_update_vfstab: "
+ "failed to chown %s: %s\n"), vfstab, strerror(err));
+ ret = errno_to_be_err(err);
+ goto cleanup;
+ }
+
+cleanup:
+ if (comments != NULL)
+ (void) fclose(comments);
+ if (vfs_ents != NULL)
+ (void) fclose(vfs_ents);
+ (void) unlink(tmp_vfstab);
+ (void) free(tmp_vfstab);
+ if (tfile != NULL)
+ (void) fclose(tfile);
+
+ return (ret);
+}
+
+
+/*
+ * Function: be_get_auto_name
+ * Description: Generate an auto name constructed based on the BE name
+ * of the original BE or zone BE being cloned.
+ * Parameters:
+ * obe_name - name of the original BE or zone BE being cloned.
+ * container_ds - container dataset for the zone.
+ * Note: if zone_be is false this should be
+ * NULL.
+ * zone_be - flag that indicates if we are operating on a zone BE.
+ * Returns:
+ * Success - pointer to auto generated BE name. The name
+ * is allocated in heap storage so the caller is
+ * responsible for free'ing the name.
+ * Failure - NULL
+ * Scope:
+ * Private
+ */
+static char *
+be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
+{
+ be_node_list_t *be_nodes = NULL;
+ be_node_list_t *cur_be = NULL;
+ char auto_be_name[MAXPATHLEN];
+ char base_be_name[MAXPATHLEN];
+ char cur_be_name[MAXPATHLEN];
+ char *num_str = NULL;
+ char *c = NULL;
+ int num = 0;
+ int cur_num = 0;
+
+ errno = 0;
+
+ /*
+ * Check if obe_name is already in an auto BE name format.
+ * If it is, then strip off the increment number to get the
+ * base name.
+ */
+ (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
+
+ if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
+ != NULL) {
+ /* Make sure remaining string is all digits */
+ c = num_str + 1;
+ while (c[0] != '\0' && isdigit(c[0]))
+ c++;
+ /*
+ * If we're now at the end of the string strip off the
+ * increment number.
+ */
+ if (c[0] == '\0')
+ num_str[0] = '\0';
+ }
+
+ if (zone_be) {
+ if (be_container_ds == NULL)
+ return (NULL);
+ if (be_get_zone_be_list(obe_name, be_container_ds,
+ &be_nodes) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_auto_name: "
+ "be_get_zone_be_list failed\n"));
+ return (NULL);
+ }
+ } else if (_be_list(NULL, &be_nodes) != BE_SUCCESS) {
+ be_print_err(gettext("be_get_auto_name: be_list failed\n"));
+ return (NULL);
+ }
+
+ for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
+ (void) strlcpy(cur_be_name, cur_be->be_node_name,
+ sizeof (cur_be_name));
+
+ /* If cur_be_name doesn't match at least base be name, skip. */
+ if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
+ != 0)
+ continue;
+
+ /* Get the string following the base be name */
+ num_str = cur_be_name + strlen(base_be_name);
+
+ /*
+ * If nothing follows the base be name, this cur_be_name
+ * is the BE named with the base be name, skip.
+ */
+ if (num_str == NULL || num_str[0] == '\0')
+ continue;
+
+ /*
+ * Remove the name delimiter. If its not there,
+ * cur_be_name isn't part of this BE name stream, skip.
+ */
+ if (num_str[0] == BE_AUTO_NAME_DELIM)
+ num_str++;
+ else
+ continue;
+
+ /* Make sure remaining string is all digits */
+ c = num_str;
+ while (c[0] != '\0' && isdigit(c[0]))
+ c++;
+ if (c[0] != '\0')
+ continue;
+
+ /* Convert the number string to an int */
+ cur_num = atoi(num_str);
+
+ /*
+ * If failed to convert the string, skip it. If its too
+ * long to be converted to an int, we wouldn't auto generate
+ * this number anyway so there couldn't be a conflict.
+ * We treat it as a manually created BE name.
+ */
+ if (cur_num == 0 && errno == EINVAL)
+ continue;
+
+ /*
+ * Compare current number to current max number,
+ * take higher of the two.
+ */
+ if (cur_num > num)
+ num = cur_num;
+ }
+
+ /*
+ * Store off a copy of 'num' incase we need it later. If incrementing
+ * 'num' causes it to roll over, this means 'num' is the largest
+ * positive int possible; we'll need it later in the loop to determine
+ * if we've exhausted all possible increment numbers. We store it in
+ * 'cur_num'.
+ */
+ cur_num = num;
+
+ /* Increment 'num' to get new auto BE name number */
+ if (++num <= 0) {
+ int ret = 0;
+
+ /*
+ * Since incrementing 'num' caused it to rollover, start
+ * over at 0 and find the first available number.
+ */
+ for (num = 0; num < cur_num; num++) {
+
+ (void) snprintf(cur_be_name, sizeof (cur_be_name),
+ "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
+
+ ret = zpool_iter(g_zfs, be_exists_callback,
+ cur_be_name);
+
+ if (ret == 0) {
+ /*
+ * BE name doesn't exist, break out
+ * to use 'num'.
+ */
+ break;
+ } else if (ret == 1) {
+ /* BE name exists, continue looking */
+ continue;
+ } else {
+ be_print_err(gettext("be_get_auto_name: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ be_free_list(be_nodes);
+ return (NULL);
+ }
+ }
+
+ /*
+ * If 'num' equals 'cur_num', we've exhausted all possible
+ * auto BE names for this base BE name.
+ */
+ if (num == cur_num) {
+ be_print_err(gettext("be_get_auto_name: "
+ "No more available auto BE names for base "
+ "BE name %s\n"), base_be_name);
+ be_free_list(be_nodes);
+ return (NULL);
+ }
+ }
+
+ be_free_list(be_nodes);
+
+ /*
+ * Generate string for auto BE name.
+ */
+ (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
+ base_be_name, BE_AUTO_NAME_DELIM, num);
+
+ if ((c = strdup(auto_be_name)) == NULL) {
+ be_print_err(gettext("be_get_auto_name: "
+ "memory allocation failed\n"));
+ return (NULL);
+ }
+
+ return (c);
+}
+
+/*
+ * Function: be_create_menu
+ * Description:
+ * This function is used if no menu.lst file exists. In
+ * this case a new file is created and if needed default
+ * lines are added to the file.
+ * Parameters:
+ * pool - The name of the pool the menu.lst file is on
+ * pool_mntpt - The mountpoint for the pool we're using.
+ * menu_file - The name of the file we're creating.
+ * menu_fp - A pointer to the file pointer of the file we
+ * created. This is also used to pass back the file
+ * pointer to the newly created file.
+ * mode - the original mode used for the failed attempt to
+ * non-existent file.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_create_menu(
+ char *pool,
+ char *pool_mntpt,
+ char *menu_file,
+ FILE **menu_fp,
+ char *mode)
+{
+ be_node_list_t *be_nodes = NULL;
+ char add_default_cmd[BUFSIZ];
+ char *menu_path = NULL;
+ char *be_rpool = NULL;
+ char *be_name = NULL;
+
+ errno = 0;
+
+ if (menu_file == NULL || menu_fp == NULL || mode == NULL)
+ return (BE_ERR_INVAL);
+
+ menu_path = strdup(menu_file);
+ if (menu_path == NULL)
+ return (BE_ERR_NOMEM);
+
+ (void) dirname(menu_path);
+ if (*menu_path == '.') {
+ free(menu_path);
+ return (BE_ERR_BAD_MENU_PATH);
+ }
+ if (mkdirp(menu_path,
+ S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
+ errno != EEXIST) {
+ free(menu_path);
+ be_print_err(gettext("be_create_menu: Failed to create the %s "
+ "directory: %s\n"), menu_path, strerror(errno));
+ return (errno_to_be_err(errno));
+ }
+ free(menu_path);
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (be_has_grub()) {
+ char be_run_cmd_errbuf[BUFSIZ];
+ /*
+ * The grub menu is missing so we need to create it
+ * and fill in the first few lines.
+ */
+ (void) snprintf(add_default_cmd, sizeof (add_default_cmd),
+ "%s add_splash_image_to_grub_menu %s",
+ INST_ICT, pool_mntpt);
+ if (be_run_cmd(add_default_cmd, be_run_cmd_errbuf, BUFSIZ,
+ NULL, 0) != BE_SUCCESS) {
+ be_print_err(gettext("be_create_menu: "
+ "add_splash_image_to_grub_menu ICT failed.\n"));
+ be_print_err(gettext(" Command: \"%s\"\n"),
+ add_default_cmd);
+ be_print_err(be_run_cmd_errbuf);
+ return (BE_ERR_ADD_SPLASH_ICT);
+ }
+ } else {
+ /*
+ * The menu file doesn't exist so we need to create a
+ * blank file.
+ */
+ FILE *temp_fp = fopen(menu_file, "w+");
+ if (temp_fp == NULL) {
+ *menu_fp = NULL;
+ return (errno_to_be_err(errno));
+ }
+ (void) fclose(temp_fp);
+ }
+
+ /*
+ * Now we need to add all the BE's back into the the file.
+ */
+ if (_be_list(NULL, &be_nodes) == BE_SUCCESS) {
+ while (be_nodes != NULL) {
+ if (strcmp(pool, be_nodes->be_rpool) == 0) {
+ (void) be_append_menu(be_nodes->be_node_name,
+ be_nodes->be_rpool, NULL, NULL, NULL);
+ }
+ if (be_nodes->be_active_on_boot) {
+ be_rpool = strdup(be_nodes->be_rpool);
+ be_name = strdup(be_nodes->be_node_name);
+ }
+
+ be_nodes = be_nodes->be_next_node;
+ }
+ }
+ be_free_list(be_nodes);
+
+ /*
+ * Check to see if this system supports grub
+ */
+ if (be_has_grub()) {
+ int err = be_change_grub_default(be_name, be_rpool);
+ if (err != BE_SUCCESS)
+ return (err);
+ }
+ *menu_fp = fopen(menu_file, mode);
+ if (*menu_fp == NULL)
+ return (errno_to_be_err(errno));
+
+ return (BE_SUCCESS);
+}
+
+/*
+ * Function: be_open_menu
+ * Description:
+ * This function is used it open the menu.lst file. If this
+ * file does not exist be_create_menu is called to create it
+ * and the open file pointer is returned. If the file does
+ * exist it is simply opened using the mode passed in.
+ * Parameters:
+ * pool - The name of the pool the menu.lst file is on
+ * pool_mntpt - The mountpoint for the pool we're using.
+ * The mountpoint is used since the mountpoint
+ * name can differ from the pool name.
+ * menu_file - The name of the file we're opening.
+ * menu_fp - A pointer to the file pointer of the file we're
+ * opening. This is also used to pass back the file
+ * pointer.
+ * mode - the original mode to be used for opening the menu.lst
+ * file.
+ * create_menu - If this is true and the menu.lst file does not
+ * exist we will attempt to re-create it. However
+ * if it's false the error returned from the fopen
+ * will be returned.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_open_menu(
+ char *pool,
+ char *pool_mntpt,
+ char *menu_file,
+ FILE **menu_fp,
+ char *mode,
+ boolean_t create_menu)
+{
+ int err = 0;
+ boolean_t set_print = B_FALSE;
+
+ *menu_fp = fopen(menu_file, mode);
+ err = errno;
+ if (*menu_fp == NULL) {
+ if (err == ENOENT && create_menu) {
+ be_print_err(gettext("be_open_menu: menu.lst "
+ "file %s does not exist,\n"), menu_file);
+ if (!do_print) {
+ set_print = B_TRUE;
+ do_print = B_TRUE;
+ }
+ be_print_err(gettext("WARNING: menu.lst "
+ "file %s does not exist,\n generating "
+ "a new menu.lst file\n"), menu_file);
+ if (set_print)
+ do_print = B_FALSE;
+ err = 0;
+ if ((err = be_create_menu(pool, pool_mntpt, menu_file,
+ menu_fp, mode)) == ENOENT)
+ return (BE_ERR_NO_MENU);
+ else if (err != BE_SUCCESS)
+ return (err);
+ else if (*menu_fp == NULL)
+ return (BE_ERR_NO_MENU);
+ } else {
+ be_print_err(gettext("be_open_menu: failed "
+ "to open menu.lst file %s\n"), menu_file);
+ if (err == ENOENT)
+ return (BE_ERR_NO_MENU);
+ else
+ return (errno_to_be_err(err));
+ }
+ }
+ return (BE_SUCCESS);
+}
diff --git a/usr/src/lib/libbe/common/be_zones.c b/usr/src/lib/libbe/common/be_zones.c
new file mode 100644
index 0000000000..886b0dacad
--- /dev/null
+++ b/usr/src/lib/libbe/common/be_zones.c
@@ -0,0 +1,518 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * System includes
+ */
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfstab.h>
+#include <unistd.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+typedef struct active_zone_root_data {
+ uuid_t parent_uuid;
+ char *zoneroot_ds;
+} active_zone_root_data_t;
+
+typedef struct mounted_zone_root_data {
+ char *zone_altroot;
+ char *zoneroot_ds;
+} mounted_zone_root_data_t;
+
+/* Private function prototypes */
+static int be_find_active_zone_root_callback(zfs_handle_t *, void *);
+static int be_find_mounted_zone_root_callback(zfs_handle_t *, void *);
+static boolean_t be_zone_get_active(zfs_handle_t *);
+
+
+/* ******************************************************************** */
+/* Semi-Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_make_zoneroot
+ * Description: Generate a string for a zone's zoneroot given the
+ * zone's zonepath.
+ * Parameters:
+ * zonepath - pointer to zonepath
+ * zoneroot - pointer to buffer to retrn zoneroot in.
+ * zoneroot_size - size of zoneroot
+ * Returns:
+ * None
+ * Scope:
+ * Semi-private (library wise use only)
+ */
+void
+be_make_zoneroot(char *zonepath, char *zoneroot, int zoneroot_size)
+{
+ (void) snprintf(zoneroot, zoneroot_size, "%s/root", zonepath);
+}
+
+/*
+ * Function: be_find_active_zone_root
+ * Description: This function will find the active zone root of a zone for
+ * a given global BE. It will iterate all of the zone roots
+ * under a zonepath, find the zone roots that belong to the
+ * specified global BE, and return the one that is active.
+ * Parameters:
+ * be_zhp - zfs handle to global BE root dataset.
+ * zonepath_ds - pointer to zone's zonepath dataset.
+ * zoneroot_ds - pointer to a buffer to store the dataset name of
+ * the zone's zoneroot that's currently active for this
+ * given global BE..
+ * zoneroot-ds_size - size of zoneroot_ds.
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_find_active_zone_root(zfs_handle_t *be_zhp, char *zonepath_ds,
+ char *zoneroot_ds, int zoneroot_ds_size)
+{
+ active_zone_root_data_t azr_data = { 0 };
+ zfs_handle_t *zhp;
+ char zone_container_ds[MAXPATHLEN];
+ int ret = BE_SUCCESS;
+
+ /* Get the uuid of the parent global BE */
+ if ((ret = be_get_uuid(zfs_get_name(be_zhp), &azr_data.parent_uuid))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_find_active_zone_root: failed to "
+ "get uuid for BE root dataset %s\n"), zfs_get_name(be_zhp));
+ return (ret);
+ }
+
+ /* Generate string for the root container dataset for this zone. */
+ be_make_container_ds(zonepath_ds, zone_container_ds,
+ sizeof (zone_container_ds));
+
+ /* Get handle to this zone's root container dataset */
+ if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_find_active_zone_root: failed to "
+ "open zone root container dataset (%s): %s\n"),
+ zone_container_ds, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /*
+ * Iterate through all of this zone's BEs, looking for ones
+ * that belong to the parent global BE, and finding the one
+ * that is marked active.
+ */
+ if ((ret = zfs_iter_filesystems(zhp, be_find_active_zone_root_callback,
+ &azr_data)) != 0) {
+ be_print_err(gettext("be_find_active_zone_root: failed to "
+ "find active zone root in zonepath dataset %s: %s\n"),
+ zonepath_ds, be_err_to_str(ret));
+ goto done;
+ }
+
+ if (azr_data.zoneroot_ds != NULL) {
+ (void) strlcpy(zoneroot_ds, azr_data.zoneroot_ds,
+ zoneroot_ds_size);
+ free(azr_data.zoneroot_ds);
+ } else {
+ be_print_err(gettext("be_find_active_zone_root: failed to "
+ "find active zone root in zonepath dataset %s\n"),
+ zonepath_ds);
+ ret = BE_ERR_ZONE_NO_ACTIVE_ROOT;
+ }
+
+done:
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_find_mounted_zone_root
+ * Description: This function will find the dataset mounted as the zoneroot
+ * of a zone for a given mounted global BE.
+ * Parameters:
+ * zone_altroot - path of zoneroot wrt the mounted global BE.
+ * zonepath_ds - dataset of the zone's zonepath.
+ * zoneroot_ds - pointer to a buffer to store the dataset of
+ * the zoneroot that currently mounted for this zone
+ * in the mounted global BE.
+ * zoneroot_ds_size - size of zoneroot_ds
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+int
+be_find_mounted_zone_root(char *zone_altroot, char *zonepath_ds,
+ char *zoneroot_ds, int zoneroot_ds_size)
+{
+ mounted_zone_root_data_t mzr_data = { 0 };
+ zfs_handle_t *zhp = NULL;
+ char zone_container_ds[MAXPATHLEN];
+ int ret = BE_SUCCESS;
+ int zret = 0;
+
+ /* Generate string for the root container dataset for this zone. */
+ be_make_container_ds(zonepath_ds, zone_container_ds,
+ sizeof (zone_container_ds));
+
+ /* Get handle to this zone's root container dataset. */
+ if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
+ == NULL) {
+ be_print_err(gettext("be_find_mounted_zone_root: failed to "
+ "open zone root container dataset (%s): %s\n"),
+ zone_container_ds, libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ mzr_data.zone_altroot = zone_altroot;
+
+ /*
+ * Iterate through all of the zone's BEs, looking for the one
+ * that is currently mounted at the zone altroot in the mounted
+ * global BE.
+ */
+ if ((zret = zfs_iter_filesystems(zhp,
+ be_find_mounted_zone_root_callback, &mzr_data)) == 0) {
+ be_print_err(gettext("be_find_mounted_zone_root: did not "
+ "find mounted zone under altroot zonepath %s\n"),
+ zonepath_ds);
+ ret = BE_ERR_NO_MOUNTED_ZONE;
+ goto done;
+ } else if (zret < 0) {
+ be_print_err(gettext("be_find_mounted_zone_root: "
+ "zfs_iter_filesystems failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ if (mzr_data.zoneroot_ds != NULL) {
+ (void) strlcpy(zoneroot_ds, mzr_data.zoneroot_ds,
+ zoneroot_ds_size);
+ free(mzr_data.zoneroot_ds);
+ }
+
+done:
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_zone_supported
+ * Description: This function will determine if a zone is supported
+ * based on its zonepath dataset. The zonepath dataset
+ * must:
+ * - not be under any global BE root dataset.
+ * - have a root container dataset underneath it.
+ *
+ * Parameters:
+ * zonepath_ds - name of dataset of the zonepath of the
+ * zone to check.
+ * Returns:
+ * B_TRUE - zone is supported
+ * B_FALSE - zone is not supported
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+boolean_t
+be_zone_supported(char *zonepath_ds)
+{
+ char zone_container_ds[MAXPATHLEN];
+ int ret = 0;
+
+ /*
+ * Make sure the dataset for the zonepath is not hierarchically
+ * under any reserved BE root container dataset of any pool.
+ */
+ if ((ret = zpool_iter(g_zfs, be_check_be_roots_callback,
+ zonepath_ds)) > 0) {
+ be_print_err(gettext("be_zone_supported: "
+ "zonepath dataset %s not supported\n"), zonepath_ds);
+ return (B_FALSE);
+ } else if (ret < 0) {
+ be_print_err(gettext("be_zone_supported: "
+ "zpool_iter failed: %s\n"),
+ libzfs_error_description(g_zfs));
+ return (B_FALSE);
+ }
+
+ /*
+ * Make sure the zonepath has a zone root container dataset
+ * underneath it.
+ */
+ be_make_container_ds(zonepath_ds, zone_container_ds,
+ sizeof (zone_container_ds));
+
+ if (!zfs_dataset_exists(g_zfs, zone_container_ds,
+ ZFS_TYPE_FILESYSTEM)) {
+ be_print_err(gettext("be_zone_supported: "
+ "zonepath dataset (%s) does not have a zone root container "
+ "dataset, zone is not supported, skipping ...\n"),
+ zonepath_ds);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Function: be_get_supported_brandlist
+ * Desciption: This functions retuns a list of supported brands in
+ * a zoneBrandList_t object.
+ * Parameters:
+ * None
+ * Returns:
+ * Failure - NULL if no supported brands found.
+ * Success - pointer to zoneBrandList structure.
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+zoneBrandList_t *
+be_get_supported_brandlist(void)
+{
+ return (z_make_brand_list(BE_ZONE_SUPPORTED_BRANDS,
+ BE_ZONE_SUPPORTED_BRANDS_DELIM));
+}
+
+/*
+ * Function: be_zone_get_parent_uuid
+ * Description: This function gets the parentbe property of a zone root
+ * dataset, parsed it into internal uuid format, and returns
+ * it in the uuid_t reference pointer passed in.
+ * Parameters:
+ * root_ds - dataset name of a zone root dataset
+ * uu - pointer to a uuid_t to return the parentbe uuid in
+ * Returns:
+ * BE_SUCCESS - Success
+ * be_errno_t - Failure
+ * Scope:
+ * Private
+ */
+int
+be_zone_get_parent_uuid(const char *root_ds, uuid_t *uu)
+{
+ zfs_handle_t *zhp = NULL;
+ nvlist_t *userprops = NULL;
+ nvlist_t *propname = NULL;
+ char *uu_string = NULL;
+ int ret = BE_SUCCESS;
+
+ /* Get handle to zone root dataset */
+ if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
+ be_print_err(gettext("be_zone_get_parent_uuid: failed to "
+ "open zone root dataset (%s): %s\n"), root_ds,
+ libzfs_error_description(g_zfs));
+ return (zfs_err_to_be_err(g_zfs));
+ }
+
+ /* Get user properties for zone root dataset */
+ if ((userprops = zfs_get_user_props(zhp)) == NULL) {
+ be_print_err(gettext("be_zone_get_parent_uuid: "
+ "failed to get user properties for zone root "
+ "dataset (%s): %s\n"), root_ds,
+ libzfs_error_description(g_zfs));
+ ret = zfs_err_to_be_err(g_zfs);
+ goto done;
+ }
+
+ /* Get UUID string from zone's root dataset user properties */
+ if (nvlist_lookup_nvlist(userprops, BE_ZONE_PARENTBE_PROPERTY,
+ &propname) != 0 || nvlist_lookup_string(propname, ZPROP_VALUE,
+ &uu_string) != 0) {
+ be_print_err(gettext("be_zone_get_parent_uuid: failed to "
+ "get parent uuid property from zone root dataset user "
+ "properties.\n"));
+ ret = BE_ERR_ZONE_NO_PARENTBE;
+ goto done;
+ }
+
+ /* Parse the uuid string into internal format */
+ if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) {
+ be_print_err(gettext("be_zone_get_parent_uuid: failed to "
+ "parse parentuuid\n"));
+ ret = BE_ERR_PARSE_UUID;
+ }
+
+done:
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/* ******************************************************************** */
+/* Private Functions */
+/* ******************************************************************** */
+
+/*
+ * Function: be_find_active_zone_root_callback
+ * Description: This function is used as a callback to iterate over all of
+ * a zone's root datasets, finding the one that is marked active
+ * for the parent BE specified in the data passed in. The name
+ * of the zone's active root dataset is returned in heap storage
+ * in the active_zone_root_data_t structure passed in, so the
+ * caller is responsible for freeing it.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to current dataset being processed
+ * data - active_zone_root_data_t pointer
+ * Returns:
+ * 0 - Success
+ * >0 - Failure
+ * Scope:
+ * Private
+ */
+static int
+be_find_active_zone_root_callback(zfs_handle_t *zhp, void *data)
+{
+ active_zone_root_data_t *azr_data = data;
+ uuid_t parent_uuid = { 0 };
+ int iret = 0;
+ int ret = 0;
+
+ if ((iret = be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid))
+ != BE_SUCCESS) {
+ be_print_err(gettext("be_find_active_zone_root_callback: "
+ "skipping zone root dataset (%s): %s\n"),
+ zfs_get_name(zhp), be_err_to_str(iret));
+ goto done;
+ }
+
+ if (uuid_compare(azr_data->parent_uuid, parent_uuid) == 0) {
+ /*
+ * Found a zone root dataset belonging to the right parent,
+ * check if its active.
+ */
+ if (be_zone_get_active(zhp)) {
+ /*
+ * Found active zone root dataset, if its already
+ * set in the callback data, that means this
+ * is the second one we've found. Return error.
+ */
+ if (azr_data->zoneroot_ds != NULL) {
+ ret = BE_ERR_ZONE_MULTIPLE_ACTIVE;
+ goto done;
+ }
+
+ azr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
+ if (azr_data->zoneroot_ds == NULL) {
+ ret = BE_ERR_NOMEM;
+ }
+ }
+ }
+
+done:
+ ZFS_CLOSE(zhp);
+ return (ret);
+}
+
+/*
+ * Function: be_find_mounted_zone_root_callback
+ * Description: This function is used as a callback to iterate over all of
+ * a zone's root datasets, find the one that is currently
+ * mounted for the parent BE specified in the data passed in.
+ * The name of the zone's mounted root dataset is returned in
+ * heap storage the mounted_zone_data_t structure passed in,
+ * so the caller is responsible for freeing it.
+ * Parameters:
+ * zhp - zfs_handle_t pointer to the current dataset being
+ * processed
+ * data - mounted_zone_data_t pointer
+ * Returns:
+ * 0 - not mounted as zone's root
+ * 1 - this dataset is mounted as zone's root
+ * Scope:
+ * Private
+ */
+static int
+be_find_mounted_zone_root_callback(zfs_handle_t *zhp, void *data)
+{
+ mounted_zone_root_data_t *mzr_data = data;
+ char *mp = NULL;
+
+ if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
+ strcmp(mp, mzr_data->zone_altroot) == 0) {
+ mzr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
+ free(mp);
+ return (1);
+ }
+
+ free(mp);
+ return (0);
+}
+
+/*
+ * Function: be_zone_get_active
+ * Description: This function gets the active property of a zone root
+ * dataset, and returns true if active property is on.
+ * Parameters:
+ * zfs - zfs_handle_t pointer to zone root dataset to check
+ * Returns:
+ * B_TRUE - zone root dataset is active
+ * B_FALSE - zone root dataset is not active
+ * Scope:
+ * Private
+ */
+static boolean_t
+be_zone_get_active(zfs_handle_t *zhp)
+{
+ nvlist_t *userprops = NULL;
+ nvlist_t *propname = NULL;
+ char *active_str = NULL;
+
+ /* Get user properties for the zone root dataset */
+ if ((userprops = zfs_get_user_props(zhp)) == NULL) {
+ be_print_err(gettext("be_zone_get_active: "
+ "failed to get user properties for zone root "
+ "dataset (%s): %s\n"), zfs_get_name(zhp),
+ libzfs_error_description(g_zfs));
+ return (B_FALSE);
+ }
+
+ /* Get active property from the zone root dataset user properties */
+ if (nvlist_lookup_nvlist(userprops, BE_ZONE_ACTIVE_PROPERTY, &propname)
+ != 0 || nvlist_lookup_string(propname, ZPROP_VALUE, &active_str)
+ != 0) {
+ return (B_FALSE);
+ }
+
+ if (strcmp(active_str, "on") == 0)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
diff --git a/usr/src/lib/libbe/common/libbe.h b/usr/src/lib/libbe/common/libbe.h
new file mode 100644
index 0000000000..27baecab89
--- /dev/null
+++ b/usr/src/lib/libbe/common/libbe.h
@@ -0,0 +1,234 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _LIBBE_H
+#define _LIBBE_H
+
+#include <libnvpair.h>
+#include <uuid/uuid.h>
+#include <libzfs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BE_ATTR_ORIG_BE_NAME "orig_be_name"
+#define BE_ATTR_ORIG_BE_POOL "orig_be_pool"
+#define BE_ATTR_SNAP_NAME "snap_name"
+
+#define BE_ATTR_NEW_BE_NAME "new_be_name"
+#define BE_ATTR_NEW_BE_POOL "new_be_pool"
+#define BE_ATTR_NEW_BE_DESC "new_be_desc"
+#define BE_ATTR_POLICY "policy"
+#define BE_ATTR_ZFS_PROPERTIES "zfs_properties"
+
+#define BE_ATTR_FS_NAMES "fs_names"
+#define BE_ATTR_FS_NUM "fs_num"
+#define BE_ATTR_SHARED_FS_NAMES "shared_fs_names"
+#define BE_ATTR_SHARED_FS_NUM "shared_fs_num"
+
+#define BE_ATTR_MOUNTPOINT "mountpoint"
+#define BE_ATTR_MOUNT_FLAGS "mount_flags"
+#define BE_ATTR_UNMOUNT_FLAGS "unmount_flags"
+#define BE_ATTR_DESTROY_FLAGS "destroy_flags"
+#define BE_ATTR_ROOT_DS "root_ds"
+#define BE_ATTR_UUID_STR "uuid_str"
+
+#define BE_ATTR_ACTIVE "active"
+#define BE_ATTR_ACTIVE_ON_BOOT "active_boot"
+#define BE_ATTR_SPACE "space_used"
+#define BE_ATTR_DATASET "dataset"
+#define BE_ATTR_STATUS "status"
+#define BE_ATTR_DATE "date"
+#define BE_ATTR_MOUNTED "mounted"
+
+/*
+ * libbe error codes
+ *
+ * NOTE: there is a copy of this enum in beadm/messages.py. To keep these
+ * in sync please make sure to add any new error messages at the end
+ * of this enumeration.
+ */
+enum {
+ BE_SUCCESS = 0,
+ BE_ERR_ACCESS = 4000, /* permission denied */
+ BE_ERR_ACTIVATE_CURR, /* Activation of current BE failed */
+ BE_ERR_AUTONAME, /* auto naming failed */
+ BE_ERR_BE_NOENT, /* No such BE */
+ BE_ERR_BUSY, /* mount busy */
+ BE_ERR_CANCELED, /* operation canceled */
+ BE_ERR_CLONE, /* BE clone failed */
+ BE_ERR_COPY, /* BE copy failed */
+ BE_ERR_CREATDS, /* dataset creation failed */
+ BE_ERR_CURR_BE_NOT_FOUND, /* Can't find current BE */
+ BE_ERR_DESTROY, /* failed to destroy BE or snapshot */
+ BE_ERR_DEMOTE, /* BE demotion failed */
+ BE_ERR_DSTYPE, /* invalid dataset type */
+ BE_ERR_BE_EXISTS, /* BE exists */
+ BE_ERR_INIT, /* be_zfs_init failed */
+ BE_ERR_INTR, /* interupted system call */
+ BE_ERR_INVAL, /* invalid argument */
+ BE_ERR_INVALPROP, /* invalid property for dataset */
+ BE_ERR_INVALMOUNTPOINT, /* Unexpected mountpoint */
+ BE_ERR_MOUNT, /* mount failed */
+ BE_ERR_MOUNTED, /* already mounted */
+ BE_ERR_NAMETOOLONG, /* name > BUFSIZ */
+ BE_ERR_NOENT, /* Doesn't exist */
+ BE_ERR_POOL_NOENT, /* No such pool */
+ BE_ERR_NODEV, /* No such device */
+ BE_ERR_NOTMOUNTED, /* File system not mounted */
+ BE_ERR_NOMEM, /* not enough memory */
+ BE_ERR_NONINHERIT, /* property is not inheritable for BE dataset */
+ BE_ERR_NXIO, /* No such device or address */
+ BE_ERR_NOSPC, /* No space on device */
+ BE_ERR_NOTSUP, /* Operation not supported */
+ BE_ERR_OPEN, /* open failed */
+ BE_ERR_PERM, /* Not owner */
+ BE_ERR_UNAVAIL, /* The BE is currently unavailable */
+ BE_ERR_PROMOTE, /* BE promotion failed */
+ BE_ERR_ROFS, /* read only file system */
+ BE_ERR_READONLYDS, /* read only dataset */
+ BE_ERR_READONLYPROP, /* read only property */
+ BE_ERR_SS_EXISTS, /* snapshot exists */
+ BE_ERR_SS_NOENT, /* No such snapshot */
+ BE_ERR_UMOUNT, /* unmount failed */
+ BE_ERR_UMOUNT_CURR_BE, /* Can't unmount current BE */
+ BE_ERR_UMOUNT_SHARED, /* unmount of shared File System failed */
+ BE_ERR_UNKNOWN, /* Unknown error */
+ BE_ERR_ZFS, /* ZFS returned an error */
+ BE_ERR_DESTROY_CURR_BE, /* Cannot destroy current BE */
+ BE_ERR_GEN_UUID, /* Failed to generate uuid */
+ BE_ERR_PARSE_UUID, /* Failed to parse uuid */
+ BE_ERR_NO_UUID, /* BE has no uuid */
+ BE_ERR_ZONE_NO_PARENTBE, /* Zone root dataset has no parent uuid */
+ BE_ERR_ZONE_MULTIPLE_ACTIVE, /* Zone has multiple active roots */
+ BE_ERR_ZONE_NO_ACTIVE_ROOT, /* Zone has no active root for this BE */
+ BE_ERR_ZONE_ROOT_NOT_LEGACY, /* Zone root dataset mntpt is not legacy */
+ BE_ERR_NO_MOUNTED_ZONE, /* Zone not mounted in alternate BE */
+ BE_ERR_MOUNT_ZONEROOT, /* Failed to mount a zone root */
+ BE_ERR_UMOUNT_ZONEROOT, /* Failed to unmount a zone root */
+ BE_ERR_ZONES_UNMOUNT, /* Unable to unmount a zone. */
+ BE_ERR_FAULT, /* Bad Address */
+ BE_ERR_RENAME_ACTIVE, /* Renaming the active BE is not supported */
+ BE_ERR_NO_MENU, /* Missing boot menu file */
+ BE_ERR_DEV_BUSY, /* Device is Busy */
+ BE_ERR_BAD_MENU_PATH, /* Invalid path for menu.lst file */
+ BE_ERR_ZONE_SS_EXISTS, /* zone snapshot already exists */
+ BE_ERR_ADD_SPLASH_ICT, /* Add_splash_image ICT failed */
+ BE_ERR_BOOTFILE_INST, /* Error installing boot files */
+ BE_ERR_EXTCMD /* External command error */
+} be_errno_t;
+
+/*
+ * Data structures used to return the listing and information of BEs.
+ */
+typedef struct be_dataset_list {
+ uint64_t be_ds_space_used;
+ boolean_t be_ds_mounted;
+ char *be_dataset_name;
+ time_t be_ds_creation; /* Date/time stamp when created */
+ char *be_ds_mntpt;
+ char *be_ds_plcy_type; /* cleanup policy type */
+ struct be_dataset_list *be_next_dataset;
+} be_dataset_list_t;
+
+typedef struct be_snapshot_list {
+ uint64_t be_snapshot_space_used; /* bytes of disk space used */
+ char *be_snapshot_name;
+ time_t be_snapshot_creation; /* Date/time stamp when created */
+ char *be_snapshot_type; /* cleanup policy type */
+ struct be_snapshot_list *be_next_snapshot;
+} be_snapshot_list_t;
+
+typedef struct be_node_list {
+ boolean_t be_mounted; /* is BE currently mounted */
+ boolean_t be_active_on_boot; /* is this BE active on boot */
+ boolean_t be_active; /* is this BE active currently */
+ uint64_t be_space_used;
+ char *be_node_name;
+ char *be_rpool;
+ char *be_root_ds;
+ char *be_mntpt;
+ char *be_policy_type; /* cleanup policy type */
+ char *be_uuid_str; /* string representation of uuid */
+ time_t be_node_creation; /* Date/time stamp when created */
+ struct be_dataset_list *be_node_datasets;
+ uint_t be_node_num_datasets;
+ struct be_snapshot_list *be_node_snapshots;
+ uint_t be_node_num_snapshots;
+ struct be_node_list *be_next_node;
+} be_node_list_t;
+
+/* Flags used with mounting a BE */
+#define BE_MOUNT_FLAG_NULL 0x00000000
+#define BE_MOUNT_FLAG_SHARED_FS 0x00000001
+#define BE_MOUNT_FLAG_SHARED_RW 0x00000002
+#define BE_MOUNT_FLAG_NO_ZONES 0x00000004
+
+/* Flags used with unmounting a BE */
+#define BE_UNMOUNT_FLAG_NULL 0x00000000
+#define BE_UNMOUNT_FLAG_FORCE 0x00000001
+
+/* Flags used with destroying a BE */
+#define BE_DESTROY_FLAG_NULL 0x00000000
+#define BE_DESTROY_FLAG_SNAPSHOTS 0x00000001
+#define BE_DESTROY_FLAG_FORCE_UNMOUNT 0x00000002
+
+/*
+ * BE functions
+ */
+int be_init(nvlist_t *);
+int be_destroy(nvlist_t *);
+int be_copy(nvlist_t *);
+
+int be_mount(nvlist_t *);
+int be_unmount(nvlist_t *);
+
+int be_rename(nvlist_t *);
+
+int be_activate(nvlist_t *);
+
+int be_create_snapshot(nvlist_t *);
+int be_destroy_snapshot(nvlist_t *);
+int be_rollback(nvlist_t *);
+
+/*
+ * Functions for listing and getting information about existing BEs.
+ */
+int be_list(char *, be_node_list_t **);
+void be_free_list(be_node_list_t *);
+int be_max_avail(char *, uint64_t *);
+char *be_err_to_str(int);
+
+/*
+ * Library functions
+ */
+void libbe_print_errors(boolean_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBBE_H */
diff --git a/usr/src/lib/libbe/common/libbe_priv.h b/usr/src/lib/libbe/common/libbe_priv.h
new file mode 100644
index 0000000000..941fe6d565
--- /dev/null
+++ b/usr/src/lib/libbe/common/libbe_priv.h
@@ -0,0 +1,209 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _LIBBE_PRIV_H
+#define _LIBBE_PRIV_H
+
+#include <libnvpair.h>
+#include <libzfs.h>
+#include <instzones_api.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ARCH_LENGTH MAXNAMELEN
+#define BE_AUTO_NAME_MAX_TRY 3
+#define BE_AUTO_NAME_DELIM '-'
+#define BE_CONTAINER_DS_NAME "ROOT"
+#define BE_POLICY_PROPERTY "org.opensolaris.libbe:policy"
+#define BE_UUID_PROPERTY "org.opensolaris.libbe:uuid"
+#define BE_PLCY_STATIC "static"
+#define BE_PLCY_VOLATILE "volatile"
+#define BE_GRUB_MENU "/boot/grub/menu.lst"
+#define BE_SPARC_MENU "/boot/menu.lst"
+#define BE_GRUB_COMMENT "#============ End of LIBBE entry ============="
+#define BE_WHITE_SPACE " \t\r\n"
+#define BE_CAP_FILE "/boot/grub/capability"
+#define BE_INSTALL_GRUB "/sbin/installgrub"
+#define BE_STAGE_1 "/boot/grub/stage1"
+#define BE_STAGE_2 "/boot/grub/stage2"
+#define ZFS_CLOSE(_zhp) \
+ if (_zhp) { \
+ zfs_close(_zhp); \
+ _zhp = NULL; \
+ }
+
+#define BE_ZONE_PARENTBE_PROPERTY "org.opensolaris.libbe:parentbe"
+#define BE_ZONE_ACTIVE_PROPERTY "org.opensolaris.libbe:active"
+#define BE_ZONE_SUPPORTED_BRANDS "ipkg labeled"
+#define BE_ZONE_SUPPORTED_BRANDS_DELIM " "
+
+/* Maximum length for the BE name. */
+#define BE_NAME_MAX_LEN 64
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef struct be_transaction_data {
+ char *obe_name; /* Original BE name */
+ char *obe_root_ds; /* Original BE root dataset */
+ char *obe_zpool; /* Original BE pool */
+ char *obe_snap_name; /* Original BE snapshot name */
+ char *obe_altroot; /* Original BE altroot */
+ char *nbe_name; /* New BE name */
+ char *nbe_root_ds; /* New BE root dataset */
+ char *nbe_zpool; /* New BE pool */
+ char *nbe_desc; /* New BE description */
+ nvlist_t *nbe_zfs_props; /* New BE dataset properties */
+ char *policy; /* BE policy type */
+} be_transaction_data_t;
+
+typedef struct be_mount_data {
+ char *altroot; /* Location of where to mount BE */
+ boolean_t shared_fs; /* Mount shared file sytsems */
+ boolean_t shared_rw; /* Mount shared file systems rw */
+} be_mount_data_t;
+
+typedef struct be_unmount_data {
+ char *altroot; /* Location of where BE is mounted */
+ boolean_t force; /* Forcibly unmount */
+} be_unmount_data_t;
+
+typedef struct be_destroy_data {
+ boolean_t destroy_snaps; /* Destroy snapshots of BE */
+ boolean_t force_unmount; /* Forcibly unmount BE if mounted */
+ uuid_t gz_be_uuid; /* UUID of the global zone BE */
+} be_destroy_data_t;
+
+typedef struct be_demote_data {
+ zfs_handle_t *clone_zhp; /* clone dataset to promote */
+ time_t origin_creation; /* snapshot creation time of clone */
+ const char *snapshot; /* snapshot of dataset being demoted */
+ boolean_t find_in_BE; /* flag noting to find clone in BE */
+} be_demote_data_t;
+
+typedef struct be_fs_list_data {
+ char *altroot;
+ char **fs_list;
+ int fs_num;
+} be_fs_list_data_t;
+
+typedef struct be_plcy_list {
+ char *be_plcy_name;
+ int be_num_max;
+ int be_num_min;
+ time_t be_age_max;
+ int be_usage_pcnt;
+ struct be_plcy_list *be_next_plcy;
+}be_plcy_list_t;
+
+/* Library globals */
+extern libzfs_handle_t *g_zfs;
+extern boolean_t do_print;
+
+/* be_create.c */
+int be_set_uuid(char *);
+int be_get_uuid(const char *, uuid_t *);
+
+/* be_list.c */
+int _be_list(char *, be_node_list_t **);
+int be_get_zone_be_list(char *, char *, be_node_list_t **);
+
+/* be_mount.c */
+int _be_mount(char *, char **, int);
+int _be_unmount(char *, int);
+int be_mount_pool(zfs_handle_t *, char **, char **, boolean_t *);
+int be_unmount_pool(zfs_handle_t *, char *, char *);
+int be_mount_zone_root(zfs_handle_t *, be_mount_data_t *);
+int be_unmount_zone_root(zfs_handle_t *, be_unmount_data_t *);
+int be_get_legacy_fs(char *, char *, char *, char *, be_fs_list_data_t *);
+void be_free_fs_list(be_fs_list_data_t *);
+char *be_get_ds_from_dir(char *);
+int be_make_tmp_mountpoint(char **);
+
+/* be_snapshot.c */
+int _be_create_snapshot(char *, char **, char *);
+int _be_destroy_snapshot(char *, char *);
+
+/* be_utils.c */
+boolean_t be_zfs_init(void);
+void be_zfs_fini(void);
+void be_make_root_ds(const char *, const char *, char *, int);
+void be_make_container_ds(const char *, char *, int);
+char *be_make_name_from_ds(const char *, char *);
+int be_append_menu(char *, char *, char *, char *, char *);
+int be_remove_menu(char *, char *, char *);
+int be_update_menu(char *, char *, char *, char *);
+int be_default_grub_bootfs(const char *, char **);
+boolean_t be_has_menu_entry(char *, char *, int *);
+int be_run_cmd(char *, char *, int, char *, int);
+int be_change_grub_default(char *, char *);
+int be_update_vfstab(char *, char *, char *, be_fs_list_data_t *, char *);
+int be_update_zone_vfstab(zfs_handle_t *, char *, char *, char *,
+ be_fs_list_data_t *);
+int be_maxsize_avail(zfs_handle_t *, uint64_t *);
+char *be_auto_snap_name(void);
+char *be_auto_be_name(char *);
+char *be_auto_zone_be_name(char *, char *);
+char *be_default_policy(void);
+boolean_t valid_be_policy(char *);
+boolean_t be_valid_auto_snap_name(char *);
+boolean_t be_valid_be_name(const char *);
+void be_print_err(char *, ...);
+int be_find_current_be(be_transaction_data_t *);
+int zfs_err_to_be_err(libzfs_handle_t *);
+int errno_to_be_err(int);
+
+/* be_activate.c */
+int _be_activate(char *);
+int be_activate_current_be(void);
+boolean_t be_is_active_on_boot(char *);
+
+/* be_zones.c */
+void be_make_zoneroot(char *, char *, int);
+int be_find_active_zone_root(zfs_handle_t *, char *, char *, int);
+int be_find_mounted_zone_root(char *, char *, char *, int);
+boolean_t be_zone_supported(char *);
+zoneBrandList_t *be_get_supported_brandlist(void);
+int be_zone_get_parent_uuid(const char *, uuid_t *);
+
+/* check architecture functions */
+char *be_get_default_isa(void);
+boolean_t be_is_isa(char *);
+boolean_t be_has_grub(void);
+
+/* callback functions */
+int be_exists_callback(zpool_handle_t *, void *);
+int be_find_zpool_callback(zpool_handle_t *, void *);
+int be_zpool_find_current_be_callback(zpool_handle_t *, void *);
+int be_zfs_find_current_be_callback(zfs_handle_t *, void *);
+int be_check_be_roots_callback(zpool_handle_t *, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBBE_PRIV_H */
diff --git a/usr/src/lib/libbe/common/llib-lbe b/usr/src/lib/libbe/common/llib-lbe
new file mode 100644
index 0000000000..2330b0a0dd
--- /dev/null
+++ b/usr/src/lib/libbe/common/llib-lbe
@@ -0,0 +1,29 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libbe.h>
+#include <libbe_priv.h>
diff --git a/usr/src/lib/libbe/common/mapfile-vers b/usr/src/lib/libbe/common/mapfile-vers
new file mode 100644
index 0000000000..358b8a730c
--- /dev/null
+++ b/usr/src/lib/libbe/common/mapfile-vers
@@ -0,0 +1,60 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate_1.1 {
+ global:
+ be_activate;
+ be_copy;
+ be_create_snapshot;
+ be_destroy;
+ be_destroy_snapshot;
+ be_err_to_str;
+ be_free_list;
+ be_init;
+ be_list;
+ be_mount;
+ be_rename;
+ be_rollback;
+ be_unmount;
+ be_valid_be_name;
+ libbe_print_errors;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libbe/i386/Makefile b/usr/src/lib/libbe/i386/Makefile
new file mode 100644
index 0000000000..c6e75c4787
--- /dev/null
+++ b/usr/src/lib/libbe/i386/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libbe/sparc/Makefile b/usr/src/lib/libbe/sparc/Makefile
new file mode 100644
index 0000000000..c6e75c4787
--- /dev/null
+++ b/usr/src/lib/libbe/sparc/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libbe/tbeadm/Makefile b/usr/src/lib/libbe/tbeadm/Makefile
new file mode 100644
index 0000000000..2c9dc95daf
--- /dev/null
+++ b/usr/src/lib/libbe/tbeadm/Makefile
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+ARCH = $(TARGET_ARCH:-%=%)
+
+PROG = tbeadm
+
+
+OBJS = tbeadm.o
+SRCS = $(OBJS:%.o=../%.c)
+
+include ../../Makefile.lib
+
+#LIBS= $(DYNLIB)
+
+INCLUDE = -I../common
+
+STATICDEPLIBS = ../$(ARCH)/libbe.a
+STATICLDLIBS += -linstzones -L/lib -L../$(ARCH) -lzfs -lnvpair \
+ -luuid -lgen -Bstatic -lbe -Bdynamic
+
+DEPLIBS = ../$(ARCH)/libbe.so.1
+LDLIBS += -L/lib -L../pics/$(ARCH) -lzfs -lnvpair -lbe
+
+CPPFLAGS += -D_LARGEFILE64_SOURCE=1 -D_REENTRANT ${INCLUDE}
+CFLAGS += -g -DDEBUG
+
+
+$(PROG): $(OBJS) $(STATICDEPLIBS)
+ $(LINK.c) -o $@ $@.c $(OBJS) $(STATICLDLIBS)
+ $(POST_PROCESS)
+
+all: $(PROG)
+
+install_h:
+
+install: all
+
+lint:
+ $(LINT.c) $(SRCS) $(LDLIBS)
+
+clobber: clean
+ $(RM) tbeadm.o
+
+clean:
+ $(RM) tbeadm
+
diff --git a/usr/src/lib/libbe/tbeadm/tbeadm.c b/usr/src/lib/libbe/tbeadm/tbeadm.c
new file mode 100644
index 0000000000..f0de6a50ef
--- /dev/null
+++ b/usr/src/lib/libbe/tbeadm/tbeadm.c
@@ -0,0 +1,837 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <libzfs.h>
+
+#include "libbe.h"
+
+static int be_do_create(int argc, char **argv);
+static int be_do_destroy(int argc, char **argv);
+static int be_do_list(int argc, char **argv);
+static int be_do_mount(int argc, char **argv);
+static int be_do_unmount(int argc, char **argv);
+static int be_do_rename(int argc, char **argv);
+static int be_do_activate(int argc, char **argv);
+static int be_do_create_snapshot(int argc, char **argv);
+static int be_do_destroy_snapshot(int argc, char **argv);
+static int be_do_rollback(int argc, char **argv);
+static void usage(void);
+
+typedef struct be_command {
+ const char *name;
+ int (*func)(int argc, char **argv);
+} be_command_t;
+
+static be_command_t command_table[] = {
+ { "create", be_do_create },
+ { "destroy", be_do_destroy },
+ { "list", be_do_list },
+ { "mount", be_do_mount },
+ { "unmount", be_do_unmount },
+ { "rename", be_do_rename },
+ { "activate", be_do_activate },
+ { "create_snap", be_do_create_snapshot },
+ { "destroy_snap", be_do_destroy_snapshot },
+};
+
+static int fs_num = 2;
+static int shared_fs_num = 2;
+static char *fs_names[2] = {"/", "/opt"};
+static char *shared_fs_names[4] = {"/export", "/export/home"};
+
+static void
+usage(void)
+{
+ (void) printf("usage:\n"
+ "\ttbeadm\n"
+ "\ttbeadm create [-d BE_desc] [-e nonActiveBe | -i] \n"
+ "\t\t[-o property=value] ... [-p zpool] [beName]\n"
+ "\ttbeadm destroy [-fs] beName\n"
+ "\ttbeadm create_snap [-p policy] beName [snapshot]\n"
+ "\ttbeadm destroy_snap beName snapshot\n"
+ "\ttbeadm list [-s] [beName]\n"
+ "\ttbeadm mount [-s ro|rw] beName mountpoint\n"
+ "\ttbeadm unmount [-f] beName\n"
+ "\ttbeadm rename origBeName newBeName\n"
+ "\ttbeadm activate beName\n"
+ "\ttbeadm rollback beName snapshot\n");
+}
+
+int
+main(int argc, char **argv) {
+
+ if (argc < 2) {
+ usage();
+ return (1);
+ }
+
+ /* Turn error printing on */
+ libbe_print_errors(B_TRUE);
+
+ if (strcmp(argv[1], "create") == 0) {
+ return (be_do_create(argc - 1, argv + 1));
+ } else if (strcmp(argv[1], "destroy") == 0) {
+ return (be_do_destroy(argc - 1, argv + 1));
+ } else if (strcmp(argv[1], "list") == 0) {
+ return (be_do_list(argc - 1, argv + 1));
+ } else if (strcmp(argv[1], "mount") == 0) {
+ return (be_do_mount(argc - 1, argv + 1));
+ } else if (strcmp(argv[1], "unmount") == 0) {
+ return (be_do_unmount(argc - 1, argv + 1));
+ } else if (strcmp(argv[1], "rename") == 0) {
+ return (be_do_rename(argc - 2, argv + 2));
+ } else if (strcmp(argv[1], "activate") == 0) {
+ return (be_do_activate(argc - 2, argv + 2));
+ } else if (strcmp(argv[1], "create_snap") == 0) {
+ return (be_do_create_snapshot(argc - 1, argv + 1));
+ } else if (strcmp(argv[1], "destroy_snap") == 0) {
+ return (be_do_destroy_snapshot(argc - 2, argv + 2));
+ } else if (strcmp(argv[1], "rollback") == 0) {
+ return (be_do_rollback(argc - 2, argv + 2));
+ } else {
+ usage();
+ return (1);
+ }
+
+ /* NOTREACHED */
+}
+
+static int
+be_do_create(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ char *obe_name = NULL;
+ char *snap_name = NULL;
+ char *nbe_zpool = NULL;
+ char *nbe_name = NULL;
+ char *nbe_desc = NULL;
+ nvlist_t *zfs_props = NULL;
+ char *propname = NULL;
+ char *propval = NULL;
+ char *strval = NULL;
+ boolean_t init = B_FALSE;
+ int c;
+ int ret = BE_SUCCESS;
+
+ if (nvlist_alloc(&zfs_props, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ while ((c = getopt(argc, argv, "d:e:io:p:")) != -1) {
+ switch (c) {
+ case 'd':
+ nbe_desc = optarg;
+ break;
+ case 'e':
+ obe_name = optarg;
+ break;
+ case 'i':
+ /* Special option to test be_init() function */
+ init = B_TRUE;
+ break;
+ case 'o':
+ if (zfs_props == NULL) {
+ if (nvlist_alloc(&zfs_props, NV_UNIQUE_NAME,
+ 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+ }
+
+ propname = optarg;
+ if ((propval = strchr(propname, '=')) == NULL) {
+ (void) fprintf(stderr, "missing "
+ "'=' for -o option\n");
+ return (1);
+ }
+ *propval = '\0';
+ propval++;
+ if (nvlist_lookup_string(zfs_props, propname,
+ &strval) == 0) {
+ (void) fprintf(stderr, "property '%s' "
+ "specified multiple times\n", propname);
+ return (1);
+ }
+ if (nvlist_add_string(zfs_props, propname, propval)
+ != 0) {
+ (void) fprintf(stderr, "internal "
+ "error: out of memory\n");
+ return (1);
+ }
+ break;
+ case 'p':
+ nbe_zpool = optarg;
+ break;
+ default:
+ usage();
+ return (1);
+ }
+ }
+
+ if (init && obe_name) {
+ printf("ERROR: -e and -i are exclusive options\n");
+ usage();
+ return (1);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 1) {
+ nbe_name = argv[0];
+ } else if (argc > 1) {
+ usage();
+ return (1);
+ }
+
+ if (obe_name) {
+ /*
+ * Check if obe_name is really a snapshot name.
+ * If so, split it out.
+ */
+ char *cp = NULL;
+
+ cp = strrchr(obe_name, '@');
+ if (cp != NULL) {
+ cp[0] = '\0';
+ if (cp[1] != NULL && cp[1] != '\0') {
+ snap_name = cp+1;
+ }
+ }
+ }
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (zfs_props) {
+ if (nvlist_add_nvlist(be_attrs, BE_ATTR_ZFS_PROPERTIES,
+ zfs_props) != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ZFS_PROPERTES (%s).\n", zfs_props);
+ return (1);
+ }
+ }
+
+ if (obe_name != NULL) {
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ORIG_BE_NAME (%s).\n", obe_name);
+ return (1);
+ }
+ }
+
+ if (snap_name != NULL) {
+ if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME, snap_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_SNAP_NANE (%s).\n", snap_name);
+ return (1);
+ }
+ }
+
+ if (nbe_zpool != NULL) {
+ if (nvlist_add_string(be_attrs, BE_ATTR_NEW_BE_POOL, nbe_zpool)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_NEW_BE_POOL (%s).\n", nbe_zpool);
+ return (1);
+ }
+ }
+
+ if (nbe_name) {
+ if (nvlist_add_string(be_attrs, BE_ATTR_NEW_BE_NAME, nbe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_NEW_BE_NAME (%s).\n", nbe_name);
+ return (1);
+ }
+ }
+
+ if (nbe_desc) {
+ if (nvlist_add_string(be_attrs, BE_ATTR_NEW_BE_DESC, nbe_desc)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_NEW_BE_DESC (%s)\n", nbe_desc);
+ return (1);
+ }
+ }
+
+ if (init) {
+ /*
+ * Add the default file system test values to test
+ * creating an initial BE.
+ */
+ if (nvlist_add_uint16(be_attrs, BE_ATTR_FS_NUM, fs_num) != 0) {
+ printf("nvlist_add_uint16 failed for BE_ATTR_FS_NUM "
+ "(%d).\n", fs_num);
+ return (1);
+ }
+
+ if (nvlist_add_string_array(be_attrs, BE_ATTR_FS_NAMES,
+ fs_names, fs_num) != 0) {
+ printf("nvlist_add_string_array failed for "
+ "BE_ATTR_FS_NAMES\n");
+ return (1);
+ }
+
+ if (nvlist_add_uint16(be_attrs, BE_ATTR_SHARED_FS_NUM,
+ shared_fs_num) != 0) {
+ printf("nvlist_add_uint16 failed for "
+ "BE_ATTR_SHARED_FS_NUM (%d).\n", shared_fs_num);
+ return (1);
+ }
+
+ if (nvlist_add_string_array(be_attrs, BE_ATTR_SHARED_FS_NAMES,
+ shared_fs_names, shared_fs_num) != 0) {
+ printf("nvlist_add_string_array failed for "
+ "BE_ATTR_SHARED_FS_NAMES\n");
+ return (1);
+ }
+
+ return (be_init(be_attrs));
+ }
+
+ ret = be_copy(be_attrs);
+
+ if (!nbe_name & ret == BE_SUCCESS) {
+ /*
+ * We requested an auto named BE; find out the
+ * name of the BE that was created for us and
+ * the auto snapshot created from the original BE.
+ */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME,
+ &nbe_name) != 0) {
+ printf("failed to get BE_ATTR_NEW_BE_NAME attribute\n");
+ ret = 1;
+ } else {
+ printf("Auto named BE: %s\n", nbe_name);
+ }
+
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_SNAP_NAME,
+ &snap_name) != 0) {
+ printf("failed to get BE_ATTR_SNAP_NAME attribute\n");
+ ret = 1;
+ } else {
+ printf("Auto named snapshot: %s\n", snap_name);
+ }
+ }
+
+ return (ret);
+}
+
+static int
+be_do_destroy(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ int c;
+ int destroy_flags = 0;
+ char *be_name;
+
+ while ((c = getopt(argc, argv, "fs")) != -1) {
+ switch (c) {
+ case 'f':
+ destroy_flags |= BE_DESTROY_FLAG_FORCE_UNMOUNT;
+ break;
+ case 's':
+ destroy_flags |= BE_DESTROY_FLAG_SNAPSHOTS;
+ break;
+ default:
+ usage();
+ return (1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ usage();
+ return (1);
+ }
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, argv[0]) != 0) {
+ printf("nvlist_add_string failed for BE_ATTR_NEW_BE_NAME "
+ "(%s).\n", argv[0]);
+ return (1);
+ }
+
+ if (nvlist_add_uint16(be_attrs, BE_ATTR_DESTROY_FLAGS, destroy_flags)
+ != 0) {
+ printf("nvlist_add_uint16 failed for "
+ "BE_ATTR_DESTROY_FLAGS.\n");
+ return (1);
+ }
+
+ return (be_destroy(be_attrs));
+}
+
+static int
+be_do_list(int argc, char **argv)
+{
+ int err = BE_SUCCESS;
+ be_node_list_t *be_nodes;
+ be_node_list_t *cur_be;
+ boolean_t snaps = B_FALSE;
+ int c = 0;
+
+ while ((c = getopt(argc, argv, "s")) != -1) {
+ switch (c) {
+ case 's':
+ snaps = B_TRUE;
+ break;
+ default:
+ usage();
+ return (1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+
+ if (argc == 1) {
+ err = be_list(argv[0], &be_nodes);
+ } else {
+ err = be_list(NULL, &be_nodes);
+ }
+
+ if (err == BE_SUCCESS) {
+
+ printf(
+ "BE name\t\tActive\tActive \tDataset\t\t\tPolicy\tUUID\n");
+ printf(
+ " \t\t \ton boot\t \t\t\t \t \n");
+ printf(
+ "-------\t\t------\t-------\t-------\t\t\t------\t----\n");
+
+ for (cur_be = be_nodes; cur_be != NULL;
+ cur_be = cur_be->be_next_node) {
+
+ int name_len = strlen(cur_be->be_node_name);
+ int ds_len = strlen(cur_be->be_root_ds);
+
+ printf("%s%s%s\t%s\t%s%s%s\t%s\n",
+ cur_be->be_node_name,
+ name_len < 8 ? "\t\t" : "\t",
+ cur_be->be_active ? "yes" : "no",
+ cur_be->be_active_on_boot ? "yes" : "no",
+ cur_be->be_root_ds,
+ ds_len < 8 ? "\t\t\t" :
+ (ds_len < 16 ? "\t\t" : "\t"),
+ cur_be->be_policy_type,
+ cur_be->be_uuid_str ? cur_be->be_uuid_str : "-");
+ if (snaps) {
+ be_snapshot_list_t *snapshots = NULL;
+ printf("Snapshot Name\n");
+ printf("--------------\n");
+ for (snapshots = cur_be->be_node_snapshots;
+ snapshots != NULL; snapshots =
+ snapshots->be_next_snapshot) {
+ printf("%s\n",
+ snapshots->be_snapshot_name);
+ }
+ }
+ }
+ }
+
+ be_free_list(be_nodes);
+ return (err);
+}
+
+static int
+be_do_rename(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ char *obe_name;
+ char *nbe_name;
+
+ if (argc < 1 || argc > 2) {
+ usage();
+ return (1);
+ }
+
+ obe_name = argv[0];
+ nbe_name = argv[1];
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ORIG_BE_NAME (%s).\n", obe_name);
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_NEW_BE_NAME, nbe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_NEW_BE_NAME (%s).\n", nbe_name);
+ return (1);
+ }
+
+ return (be_rename(be_attrs));
+
+}
+
+static int
+be_do_create_snapshot(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ char *obe_name = NULL;
+ char *snap_name = NULL;
+ char *policy = NULL;
+ int c;
+ int ret = BE_SUCCESS;
+
+ while ((c = getopt(argc, argv, "p:")) != -1) {
+ switch (c) {
+ case 'p':
+ policy = optarg;
+ break;
+ default:
+ usage();
+ return (1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1 || argc > 2) {
+ usage();
+ return (1);
+ }
+
+ obe_name = argv[0];
+
+ if (argc > 1) {
+ /* Snapshot name provided */
+ snap_name = argv[1];
+ }
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ORIG_BE_NAME (%s).\n", obe_name);
+ return (1);
+ }
+
+ if (policy) {
+ if (nvlist_add_string(be_attrs, BE_ATTR_POLICY, policy) != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_POLICY (%s).\n", policy);
+ return (1);
+ }
+ }
+
+ if (snap_name) {
+ if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME, snap_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_SNAP_NAME (%s).\n", snap_name);
+ return (1);
+ }
+ }
+
+ ret = be_create_snapshot(be_attrs);
+
+ if (!snap_name && ret == BE_SUCCESS) {
+ /*
+ * We requested an auto named snapshot; find out
+ * the snapshot name that was created for us.
+ */
+ if (nvlist_lookup_string(be_attrs, BE_ATTR_SNAP_NAME,
+ &snap_name) != 0) {
+ printf("failed to get BE_ATTR_SNAP_NAME attribute\n");
+ ret = 1;
+ } else {
+ printf("Auto named snapshot: %s\n", snap_name);
+ }
+ }
+
+ return (ret);
+}
+
+static int
+be_do_destroy_snapshot(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ char *obe_name;
+ char *snap_name;
+
+ if (argc != 2) {
+ usage();
+ return (1);
+ }
+
+ obe_name = argv[0];
+ snap_name = argv[1];
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ORIG_BE_NAME (%s).\n", obe_name);
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME, snap_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_SNAP_NAME (%s).\n", snap_name);
+ return (1);
+ }
+
+ return (be_destroy_snapshot(be_attrs));
+}
+
+static int
+be_do_rollback(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ char *obe_name;
+ char *snap_name;
+
+ if (argc < 1 || argc > 2) {
+ usage();
+ return (1);
+ }
+
+ obe_name = argv[0];
+ snap_name = argv[1];
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ORIG_BE_NAME (%s).\n", obe_name);
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME, snap_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_SNAP_NAME (%s).\n", snap_name);
+ return (1);
+ }
+
+ return (be_rollback(be_attrs));
+}
+
+static int
+be_do_activate(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ char *obe_name;
+
+ if (argc < 1 || argc > 2) {
+ usage();
+ return (1);
+ }
+
+ obe_name = argv[0];
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ORIG_BE_NAME (%s).\n", obe_name);
+ return (1);
+ }
+
+ return (be_activate(be_attrs));
+}
+
+static int
+be_do_mount(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ int c;
+ boolean_t shared_fs = B_FALSE;
+ int mount_flags = 0;
+ char *obe_name;
+ char *mountpoint;
+
+ while ((c = getopt(argc, argv, "s:")) != -1) {
+ switch (c) {
+ case 's':
+ shared_fs = B_TRUE;
+
+ mount_flags |= BE_MOUNT_FLAG_SHARED_FS;
+
+ if (strcmp(optarg, "rw") == 0) {
+ mount_flags |= BE_MOUNT_FLAG_SHARED_RW;
+ } else if (strcmp(optarg, "ro") != 0) {
+ printf("The -s flag requires an argument "
+ "[ rw | ro ]\n");
+ usage();
+ return (1);
+ }
+
+ break;
+ default:
+ usage();
+ return (1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1 || argc > 2) {
+ usage();
+ return (1);
+ }
+
+ obe_name = argv[0];
+
+ if (argc == 2) {
+ mountpoint = argv[1];
+ } else {
+ /*
+ * XXX - Need to generate a random mountpoint here;
+ * right now we're just exitting if one isn't supplied.
+ */
+ usage();
+ return (1);
+ }
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ORIG_BE_NAME (%s).\n", obe_name);
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, mountpoint)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_MOUNTPOINT (%s).\n", mountpoint);
+ return (1);
+ }
+
+ if (shared_fs) {
+ if (nvlist_add_uint16(be_attrs, BE_ATTR_MOUNT_FLAGS,
+ mount_flags) != 0) {
+ printf("nvlist_add_uint16 failed for "
+ "BE_ATTR_MOUNT_FLAGS (%d).\n", mount_flags);
+ return (1);
+ }
+ }
+
+ return (be_mount(be_attrs));
+}
+
+
+static int
+be_do_unmount(int argc, char **argv)
+{
+ nvlist_t *be_attrs;
+ int c;
+ int unmount_flags = 0;
+ char *obe_name;
+
+ while ((c = getopt(argc, argv, "f")) != -1) {
+ switch (c) {
+ case 'f':
+ unmount_flags |= BE_UNMOUNT_FLAG_FORCE;
+ break;
+ default:
+ usage();
+ return (1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ usage();
+ return (1);
+ }
+
+ obe_name = argv[0];
+
+ if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
+ printf("nvlist_alloc failed.\n");
+ return (1);
+ }
+
+ if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name)
+ != 0) {
+ printf("nvlist_add_string failed for "
+ "BE_ATTR_ORIG_BE_NAME (%s).\n", obe_name);
+ return (1);
+ }
+
+ if (nvlist_add_uint16(be_attrs, BE_ATTR_UNMOUNT_FLAGS, unmount_flags)
+ != 0) {
+ printf("nvlist_add_uint16 failed for "
+ "BE_ATTR_UNMOUNT_FLAGS\n");
+ return (1);
+ }
+
+ return (be_unmount(be_attrs));
+}
diff --git a/usr/src/lib/libsecdb/exec_attr.txt b/usr/src/lib/libsecdb/exec_attr.txt
index 034afd84b6..2b6e991979 100644
--- a/usr/src/lib/libsecdb/exec_attr.txt
+++ b/usr/src/lib/libsecdb/exec_attr.txt
@@ -269,6 +269,7 @@ Process Management:solaris:cmd:::/usr/sbin/rcapadm:uid=0
Project Management:solaris:cmd:::/usr/sbin/projadd:euid=0
Project Management:solaris:cmd:::/usr/sbin/projmod:euid=0
Project Management:solaris:cmd:::/usr/sbin/projdel:euid=0
+Software Installation:suser:cmd:::/sbin/beadm:uid=0;gid=bin
Software Installation:suser:cmd:::/usr/bin/ln:euid=0
Software Installation:suser:cmd:::/usr/bin/pkginfo:uid=0
Software Installation:suser:cmd:::/usr/bin/pkgmk:uid=0
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index 65a651ea1b..85c9a89e9d 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -79,7 +79,7 @@ Rights Delegation:::Delegate ability to assign rights to users and roles:auths=s
Rmvolmgr Management:::Manage Removable Volume Manager SMF service:auths=solaris.smf.manage.rmvolmgr;help=RtRmvolmgrMngmnt.html
Service Management:::Manage services:auths=solaris.smf.manage,solaris.smf.modify
Service Operator:::Administer services:auths=solaris.smf.manage,solaris.smf.modify.framework
-Software Installation:::Add application software to the system:help=RtSoftwareInstall.html
+Software Installation:::Add application software to the system:profiles=ZFS File System Management;help=RtSoftwareInstall.html
Stop:::Last Profile evaluated, default profiles are not considered:help=RtReservedProfile.html
System Administrator:::Can perform most non-security administrative tasks:profiles=Audit Review,Printer Management,Cron Management,Device Management,File System Management,Mail Management,Maintenance and Repair,Media Backup,Media Restore,Name Service Management,Network Management,Object Access Management,Process Management,Software Installation,User Management,Project Management,All;help=RtSysAdmin.html
System Event Management:::Manage system events and system event channels:help=RtSysEvMngmnt.html
diff --git a/usr/src/lib/pylibbe/Makefile b/usr/src/lib/pylibbe/Makefile
new file mode 100644
index 0000000000..64e8106641
--- /dev/null
+++ b/usr/src/lib/pylibbe/Makefile
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.lib
+
+SUBDIRS= $(MACH)
+XGETTEXT= $(GNUXGETTEXT)
+XGETFLAGS= $(GNUXGETFLAGS)
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+MSGFILES = `$(FIND) . -name '*.py' -o -name '*.c'`
+POFILE = libbe_py.po
+
+.KEEP_STATE:
+
+all install clean clobber lint: $(SUBDIRS)
+
+$(POFILE): pofile_MSGFILES
+
+_msg: $(MSGDOMAINPOFILE)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/pylibbe/Makefile.com b/usr/src/lib/pylibbe/Makefile.com
new file mode 100644
index 0000000000..6fa379ddcc
--- /dev/null
+++ b/usr/src/lib/pylibbe/Makefile.com
@@ -0,0 +1,60 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+LIBRARY = libbe_py.a
+VERS =
+OBJECTS = libbe_py.o
+
+include ../../Makefile.lib
+
+PYTHON = $(PYTHON_26)
+LIBLINKS =
+SRCDIR = ../common
+ROOTLIBDIR= $(ROOT)/usr/lib/python2.6/vendor-packages
+PYOBJS= $(PYSRCS:%.py=$(SRCDIR)/%.pyc)
+PYFILES= $(PYSRCS) $(PYSRCS:%.py=%.pyc)
+ROOTPYBEFILES= $(PYFILES:%=$(ROOTLIBDIR)/%)
+
+C99MODE= $(C99_ENABLE)
+
+LIBS = $(DYNLIB)
+LDLIBS += -lbe -lnvpair -lc
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I/usr/include/python2.6 -D_FILE_OFFSET_BITS=64 -I../../libbe/common
+
+.KEEP_STATE:
+
+all install := LDLIBS += -lpython2.6
+
+all: $(PYOBJS) $(LIBS)
+
+install: all $(ROOTPYBEFILES)
+
+$(ROOTLIBDIR)/%: %
+ $(INS.pyfile)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/pylibbe/common/libbe_py.c b/usr/src/lib/pylibbe/common/libbe_py.c
new file mode 100644
index 0000000000..b4f6ca0e52
--- /dev/null
+++ b/usr/src/lib/pylibbe/common/libbe_py.c
@@ -0,0 +1,1094 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <Python.h>
+#include <sys/varargs.h>
+#include <stdio.h>
+#include <libnvpair.h>
+
+#include <libbe.h>
+#include <libbe_priv.h>
+
+enum {
+ BE_PY_SUCCESS = 0,
+ BE_PY_ERR_APPEND = 6000,
+ BE_PY_ERR_DICT,
+ BE_PY_ERR_LIST,
+ BE_PY_ERR_NVLIST,
+ BE_PY_ERR_PARSETUPLE,
+ BE_PY_ERR_PRINT_ERR,
+ BE_PY_ERR_VAR_CONV,
+} bePyErr;
+
+/*
+ * public libbe functions
+ */
+
+PyObject *beCreateSnapshot(PyObject *, PyObject *);
+PyObject *beCopy(PyObject *, PyObject *);
+PyObject *beList(PyObject *, PyObject *);
+PyObject *beActivate(PyObject *, PyObject *);
+PyObject *beDestroy(PyObject *, PyObject *);
+PyObject *beDestroySnapshot(PyObject *, PyObject *);
+PyObject *beRename(PyObject *, PyObject *);
+PyObject *beMount(PyObject *, PyObject *);
+PyObject *beUnmount(PyObject *, PyObject *);
+PyObject *bePrintErrors(PyObject *, PyObject *);
+PyObject *beGetErrDesc(PyObject *, PyObject *);
+char *beMapLibbePyErrorToString(int);
+void initlibbe_py();
+
+static boolean_t convertBEInfoToDictionary(be_node_list_t *be,
+ PyObject **listDict);
+static boolean_t convertDatasetInfoToDictionary(be_dataset_list_t *ds,
+ PyObject **listDict);
+static boolean_t convertSnapshotInfoToDictionary(be_snapshot_list_t *ss,
+ PyObject **listDict);
+static boolean_t convertPyArgsToNvlist(nvlist_t **nvList, int numArgs, ...);
+
+
+/* ~~~~~~~~~~~~~~~ */
+/* Public Funtions */
+/* ~~~~~~~~~~~~~~~ */
+
+/*
+ * Function: beCreateSnapshot
+ * Description: Convert Python args to nvlist pairs and
+ * call libbe:be_create_snapshot to create a
+ * snapshot of all the datasets within a BE
+ * Parameters:
+ * args - pointer to a python object containing:
+ * beName - The name of the BE to create a snapshot of
+ * snapName - The name of the snapshot to create (optional)
+ *
+ * The following public attribute values. defined by libbe.h,
+ * are used by this function:
+ *
+ * Returns a pointer to a python object and an optional snapshot name:
+ * 0, [snapName] - Success
+ * 1, [snapName] - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beCreateSnapshot(PyObject *self, PyObject *args)
+{
+ char *beName = NULL;
+ char *snapName = NULL;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+ PyObject *retVals = NULL;
+
+ if (!PyArg_ParseTuple(args, "z|z", &beName, &snapName)) {
+ return (Py_BuildValue("[is]", BE_PY_ERR_PARSETUPLE, NULL));
+ }
+
+ if (!convertPyArgsToNvlist(&beAttrs, 4,
+ BE_ATTR_ORIG_BE_NAME, beName,
+ BE_ATTR_SNAP_NAME, snapName)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[is]", BE_PY_ERR_NVLIST, NULL));
+ }
+
+ if (beAttrs == NULL) {
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if ((ret = be_create_snapshot(beAttrs)) != 0) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[is]", ret, NULL));
+ }
+ if (snapName == NULL) {
+ if (nvlist_lookup_pairs(beAttrs, NV_FLAG_NOENTOK,
+ BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &snapName,
+ NULL) != 0) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[is]",
+ BE_PY_ERR_NVLIST, NULL));
+ }
+ retVals = Py_BuildValue("[is]", ret, snapName);
+ nvlist_free(beAttrs);
+ return (retVals);
+ }
+ nvlist_free(beAttrs);
+
+ return (Py_BuildValue("[is]", ret, NULL));
+}
+
+/*
+ * Function: beCopy
+ * Description: Convert Python args to nvlist pairs and call libbe:be_copy
+ * to create a Boot Environment
+ * Parameters:
+ * args - pointer to a python object containing:
+ * trgtBeName - The name of the BE to create
+ * srcBeName - The name of the BE used to create trgtBeName (optional)
+ * rpool - The pool to create the new BE in (optional)
+ * srcSnapName - The snapshot name (optional)
+ * beNameProperties - The properties to use when creating
+ * the BE (optional)
+ *
+ * Returns a pointer to a python object. That Python object will consist of
+ * the return code and optional attributes, trgtBeName and snapshotName
+ * BE_SUCCESS, [trgtBeName], [trgtSnapName] - Success
+ * 1, [trgtBeName], [trgtSnapName] - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beCopy(PyObject *self, PyObject *args)
+{
+ char *trgtBeName = NULL;
+ char *srcBeName = NULL;
+ char *srcSnapName = NULL;
+ char *trgtSnapName = NULL;
+ char *rpool = NULL;
+ char *beDescription = NULL;
+ int pos = 0;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+ nvlist_t *beProps = NULL;
+ PyObject *beNameProperties = NULL;
+ PyObject *pkey = NULL;
+ PyObject *pvalue = NULL;
+ PyObject *retVals = NULL;
+
+ if (!PyArg_ParseTuple(args, "|zzzzOz", &trgtBeName, &srcBeName,
+ &srcSnapName, &rpool, &beNameProperties, &beDescription)) {
+ return (Py_BuildValue("[iss]", BE_PY_ERR_PARSETUPLE,
+ NULL, NULL));
+ }
+
+ if (!convertPyArgsToNvlist(&beAttrs, 10,
+ BE_ATTR_NEW_BE_NAME, trgtBeName,
+ BE_ATTR_ORIG_BE_NAME, srcBeName,
+ BE_ATTR_SNAP_NAME, srcSnapName,
+ BE_ATTR_NEW_BE_POOL, rpool,
+ BE_ATTR_NEW_BE_DESC, beDescription)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST, NULL, NULL));
+ }
+
+ if (beNameProperties != NULL) {
+ if (nvlist_alloc(&beProps, NV_UNIQUE_NAME, 0) != 0) {
+ (void) printf("nvlist_alloc failed.\n");
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
+ NULL, NULL));
+ }
+ while (PyDict_Next(beNameProperties, &pos, &pkey, &pvalue)) {
+ if (!convertPyArgsToNvlist(&beProps, 2,
+ PyString_AsString(pkey),
+ PyString_AsString(pvalue))) {
+ nvlist_free(beProps);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
+ NULL, NULL));
+ }
+ }
+ }
+
+ if (beProps != NULL && beAttrs != NULL &&
+ nvlist_add_nvlist(beAttrs, BE_ATTR_ZFS_PROPERTIES,
+ beProps) != 0) {
+ nvlist_free(beProps);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
+ NULL, NULL));
+ }
+
+ if (beProps != NULL) nvlist_free(beProps);
+
+ if (trgtBeName == NULL) {
+ /*
+ * Caller wants to get back the BE_ATTR_NEW_BE_NAME and
+ * BE_ATTR_SNAP_NAME
+ */
+ if ((ret = be_copy(beAttrs)) != BE_SUCCESS) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[iss]", ret, NULL, NULL));
+ }
+
+ /*
+ * When no trgtBeName is passed to be_copy, be_copy
+ * returns an auto generated beName and snapshot name.
+ */
+ if (nvlist_lookup_string(beAttrs, BE_ATTR_NEW_BE_NAME,
+ &trgtBeName) != 0) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
+ NULL, NULL));
+ }
+ if (nvlist_lookup_string(beAttrs, BE_ATTR_SNAP_NAME,
+ &trgtSnapName) != 0) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
+ NULL, NULL));
+ }
+
+ retVals = Py_BuildValue("[iss]", BE_PY_SUCCESS,
+ trgtBeName, trgtSnapName);
+ nvlist_free(beAttrs);
+ return (retVals);
+
+ } else {
+ ret = be_copy(beAttrs);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("[iss]", ret, NULL, NULL));
+ }
+}
+
+/*
+ * Function: beList
+ * Description: Convert Python args to nvlist pairs and call libbe:be_list
+ * to gather information about Boot Environments
+ * Parameters:
+ * args - pointer to a python object containing:
+ * beName - The name of the BE to list (optional)
+ *
+ * Returns a pointer to a python object. That Python object will consist of
+ * the return code and a list of Dicts or NULL.
+ * BE_PY_SUCCESS, listOfDicts - Success
+ * bePyErr or be_errno_t, NULL - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beList(PyObject *self, PyObject *args)
+{
+ char *beName = NULL;
+ int ret = BE_PY_SUCCESS;
+ be_node_list_t *list = NULL;
+ be_node_list_t *be = NULL;
+ PyObject *dict = NULL;
+ PyObject *listOfDicts = NULL;
+
+ if ((listOfDicts = PyList_New(0)) == NULL) {
+ ret = BE_PY_ERR_DICT;
+ listOfDicts = Py_None;
+ goto done;
+ }
+
+ if (!PyArg_ParseTuple(args, "|z", &beName)) {
+ ret = BE_PY_ERR_PARSETUPLE;
+ goto done;
+ }
+
+ if ((ret = be_list(beName, &list)) != BE_SUCCESS) {
+ goto done;
+ }
+
+ for (be = list; be != NULL; be = be->be_next_node) {
+ be_dataset_list_t *ds = be->be_node_datasets;
+ be_snapshot_list_t *ss = be->be_node_snapshots;
+
+ if ((dict = PyDict_New()) == NULL) {
+ ret = BE_PY_ERR_DICT;
+ goto done;
+ }
+
+ if (!convertBEInfoToDictionary(be, &dict)) {
+ /* LINTED */
+ Py_DECREF(dict);
+ ret = BE_PY_ERR_VAR_CONV;
+ goto done;
+ }
+
+ if (PyList_Append(listOfDicts, dict) != 0) {
+ /* LINTED */
+ Py_DECREF(dict);
+ ret = BE_PY_ERR_APPEND;
+ goto done;
+ }
+
+ /* LINTED */
+ Py_DECREF(dict);
+
+ while (ds != NULL) {
+ if ((dict = PyDict_New()) == NULL) {
+ ret = BE_PY_ERR_DICT;
+ goto done;
+ }
+
+ if (!convertDatasetInfoToDictionary(ds, &dict)) {
+ /* LINTED */
+ Py_DECREF(dict);
+ ret = BE_PY_ERR_VAR_CONV;
+ goto done;
+ }
+
+ if (PyList_Append(listOfDicts, dict) != 0) {
+ /* LINTED */
+ Py_DECREF(dict);
+ ret = BE_PY_ERR_APPEND;
+ goto done;
+ }
+
+ ds = ds->be_next_dataset;
+
+ /* LINTED */
+ Py_DECREF(dict);
+ }
+
+
+ while (ss != NULL) {
+ if ((dict = PyDict_New()) == NULL) {
+ /* LINTED */
+ Py_DECREF(dict);
+ ret = BE_PY_ERR_DICT;
+ goto done;
+ }
+
+ if (!convertSnapshotInfoToDictionary(ss, &dict)) {
+ /* LINTED */
+ Py_DECREF(dict);
+ ret = BE_PY_ERR_VAR_CONV;
+ goto done;
+ }
+
+ if (PyList_Append(listOfDicts, dict) != 0) {
+ /* LINTED */
+ Py_DECREF(dict);
+ ret = BE_PY_ERR_APPEND;
+ goto done;
+ }
+
+ ss = ss->be_next_snapshot;
+
+ /* LINTED */
+ Py_DECREF(dict);
+ }
+ }
+
+done:
+ if (list != NULL)
+ be_free_list(list);
+ return (Py_BuildValue("[iO]", ret, listOfDicts));
+}
+
+/*
+ * Function: beActivate
+ * Description: Convert Python args to nvlist pairs and call libbe:be_activate
+ * to activate a Boot Environment
+ * Parameters:
+ * args - pointer to a python object containing:
+ * beName - The name of the BE to activate
+ *
+ * Returns a pointer to a python object:
+ * BE_SUCCESS - Success
+ * bePyErr or be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beActivate(PyObject *self, PyObject *args)
+{
+ char *beName = NULL;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+
+ if (!PyArg_ParseTuple(args, "z", &beName)) {
+ return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
+ }
+
+ if (!convertPyArgsToNvlist(&beAttrs, 2, BE_ATTR_ORIG_BE_NAME, beName)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (beAttrs == NULL) {
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ ret = be_activate(beAttrs);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", ret));
+}
+
+/*
+ * Function: beDestroy
+ * Description: Convert Python args to nvlist pairs and call libbe:be_destroy
+ * to destroy a Boot Environment
+ * Parameters:
+ * args - pointer to a python object containing:
+ * beName - The name of the BE to destroy
+ *
+ * Returns a pointer to a python object:
+ * BE_SUCCESS - Success
+ * bePyErr or be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beDestroy(PyObject *self, PyObject *args)
+{
+ char *beName = NULL;
+ int destroy_snaps = 0;
+ int force_unmount = 0;
+ int destroy_flags = 0;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+
+ if (!PyArg_ParseTuple(args, "z|ii", &beName, &destroy_snaps,
+ &force_unmount)) {
+ return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
+ }
+
+ if (destroy_snaps == 1)
+ destroy_flags |= BE_DESTROY_FLAG_SNAPSHOTS;
+
+ if (force_unmount == 1)
+ destroy_flags |= BE_DESTROY_FLAG_FORCE_UNMOUNT;
+
+ if (!convertPyArgsToNvlist(&beAttrs, 2, BE_ATTR_ORIG_BE_NAME, beName)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (nvlist_add_uint16(beAttrs, BE_ATTR_DESTROY_FLAGS, destroy_flags)
+ != 0) {
+ (void) printf("nvlist_add_uint16 failed for "
+ "BE_ATTR_DESTROY_FLAGS (%d).\n", destroy_flags);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (beAttrs == NULL) {
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ ret = be_destroy(beAttrs);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", ret));
+}
+
+/*
+ * Function: beDestroySnapshot
+ * Description: Convert Python args to nvlist pairs and call libbe:be_destroy
+ * to destroy a snapshot of a Boot Environment
+ * Parameters:
+ * args - pointer to a python object containing:
+ * beName - The name of the BE to destroy
+ * snapName - The name of the snapshot to destroy
+ *
+ * Returns a pointer to a python object:
+ * BE_SUCCESS - Success
+ * bePyErr or be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beDestroySnapshot(PyObject *self, PyObject *args)
+{
+ char *beName = NULL;
+ char *snapName = NULL;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+
+ if (!PyArg_ParseTuple(args, "zz", &beName, &snapName)) {
+ return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
+ }
+
+ if (!convertPyArgsToNvlist(&beAttrs, 4,
+ BE_ATTR_ORIG_BE_NAME, beName,
+ BE_ATTR_SNAP_NAME, snapName)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (beAttrs == NULL) {
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ ret = be_destroy_snapshot(beAttrs);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", ret));
+}
+
+/*
+ * Function: beRename
+ * Description: Convert Python args to nvlist pairs and call libbe:be_rename
+ * to rename a Boot Environment
+ * Parameters:
+ * args - pointer to a python object containing:
+ * oldBeName - The name of the old Boot Environment
+ * newBeName - The name of the new Boot Environment
+ *
+ * Returns a pointer to a python object:
+ * BE_SUCCESS - Success
+ * bePyErr or be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beRename(PyObject *self, PyObject *args)
+{
+ char *oldBeName = NULL;
+ char *newBeName = NULL;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+
+ if (!PyArg_ParseTuple(args, "zz", &oldBeName, &newBeName)) {
+ return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
+ }
+
+ if (!convertPyArgsToNvlist(&beAttrs, 4,
+ BE_ATTR_ORIG_BE_NAME, oldBeName,
+ BE_ATTR_NEW_BE_NAME, newBeName)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (beAttrs == NULL) {
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ ret = be_rename(beAttrs);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", ret));
+}
+
+/*
+ * Function: beMount
+ * Description: Convert Python args to nvlist pairs and call libbe:be_mount
+ * to mount a Boot Environment
+ * Parameters:
+ * args - pointer to a python object containing:
+ * beName - The name of the Boot Environment to mount
+ * mountpoint - The path of the mountpoint to mount the
+ * Boot Environment on (optional)
+ *
+ * Returns a pointer to a python object:
+ * BE_SUCCESS - Success
+ * bePyErr or be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beMount(PyObject *self, PyObject *args)
+{
+ char *beName = NULL;
+ char *mountpoint = NULL;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+
+ if (!PyArg_ParseTuple(args, "zz", &beName, &mountpoint)) {
+ return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
+ }
+
+ if (!convertPyArgsToNvlist(&beAttrs, 4,
+ BE_ATTR_ORIG_BE_NAME, beName,
+ BE_ATTR_MOUNTPOINT, mountpoint)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (beAttrs == NULL) {
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ ret = be_mount(beAttrs);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", ret));
+}
+
+/*
+ * Function: beUnmount
+ * Description: Convert Python args to nvlist pairs and call libbe:be_unmount
+ * to unmount a Boot Environment
+ * Parameters:
+ * args - pointer to a python object containing:
+ * beName - The name of the Boot Environment to unmount
+ *
+ * Returns a pointer to a python object:
+ * BE_SUCCESS - Success
+ * bePyErr or be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beUnmount(PyObject *self, PyObject *args)
+{
+ char *beName = NULL;
+ int force_unmount = 0;
+ int unmount_flags = 0;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+
+ if (!PyArg_ParseTuple(args, "z|i", &beName, &force_unmount)) {
+ return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
+ }
+
+ if (force_unmount == 1)
+ unmount_flags |= BE_UNMOUNT_FLAG_FORCE;
+
+ if (!convertPyArgsToNvlist(&beAttrs, 2,
+ BE_ATTR_ORIG_BE_NAME, beName)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (nvlist_add_uint16(beAttrs, BE_ATTR_UNMOUNT_FLAGS, unmount_flags)
+ != 0) {
+ (void) printf("nvlist_add_uint16 failed for "
+ "BE_ATTR_UNMOUNT_FLAGS (%d).\n", unmount_flags);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (beAttrs == NULL) {
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ ret = be_unmount(beAttrs);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", ret));
+}
+
+/*
+ * Function: beRollback
+ * Description: Convert Python args to nvlist pairs and call libbe:be_rollback
+ * to rollback a Boot Environment to a previously taken
+ * snapshot.
+ * Parameters:
+ * args - pointer to a python object containing:
+ * beName - The name of the Boot Environment to unmount
+ *
+ * Returns a pointer to a python object:
+ * BE_SUCCESS - Success
+ * bePyErr or be_errno_t - Failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beRollback(PyObject *self, PyObject *args)
+{
+ char *beName = NULL;
+ char *snapName = NULL;
+ int ret = BE_PY_SUCCESS;
+ nvlist_t *beAttrs = NULL;
+
+ if (!PyArg_ParseTuple(args, "zz", &beName, &snapName)) {
+ return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
+ }
+
+ if (!convertPyArgsToNvlist(&beAttrs, 4,
+ BE_ATTR_ORIG_BE_NAME, beName,
+ BE_ATTR_SNAP_NAME, snapName)) {
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ if (beAttrs == NULL) {
+ return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
+ }
+
+ ret = be_rollback(beAttrs);
+ nvlist_free(beAttrs);
+ return (Py_BuildValue("i", ret));
+}
+
+/*
+ * Function: bePrintErrors
+ * Description: Convert Python args to boolean and call libbe_print_errors to
+ * turn on/off error output for the library.
+ * Parameter:
+ * args - pointer to a python object containing:
+ * print_errors - Boolean that turns library error
+ * printing on or off.
+ * Parameters:
+ * args - pointer to a python object containing:
+ * 0 - do not print errors - Python boolean "False"
+ * 1 - print errors - Python boolean "True"
+ *
+ * Returns 1 on missing or invalid argument, 0 otherwise
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+bePrintErrors(PyObject *self, PyObject *args)
+{
+ int print_errors;
+
+ if (!PyArg_ParseTuple(args, "i", &print_errors) ||
+ (print_errors != 1 && print_errors != 0))
+ return (Py_BuildValue("i", BE_PY_ERR_PRINT_ERR));
+ libbe_print_errors(print_errors == 1);
+ return (Py_BuildValue("i", BE_PY_SUCCESS));
+}
+
+/*
+ * Function: beGetErrDesc
+ * Description: Convert Python args to an int and call be_err_to_str to
+ * map an error code to an error string.
+ * Parameter:
+ * args - pointer to a python object containing:
+ * errCode - value to map to an error string.
+ *
+ * Returns: error string or NULL
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beGetErrDesc(PyObject *self, PyObject *args)
+{
+ int errCode = 0;
+ char *beErrStr = NULL;
+
+ if (!PyArg_ParseTuple(args, "i", &errCode)) {
+ return (Py_BuildValue("s", NULL));
+ }
+
+ /*
+ * First check libbe_py errors. If NULL is returned check error codes
+ * in libbe.
+ */
+
+ if ((beErrStr = beMapLibbePyErrorToString(errCode)) == NULL) {
+ beErrStr = be_err_to_str(errCode);
+ }
+
+ return (Py_BuildValue("s", beErrStr));
+}
+
+/*
+ * Function: beVerifyBEName
+ * Description: Call be_valid_be_name() to verify the BE name.
+ * Parameter:
+ * args - pointer to a python object containing:
+ * string - value to map to a string.
+ *
+ * Returns: 0 for success or 1 for failure
+ * Scope:
+ * Public
+ */
+/* ARGSUSED */
+PyObject *
+beVerifyBEName(PyObject *self, PyObject *args)
+{
+ char *string = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &string)) {
+ return (Py_BuildValue("i", 1));
+ }
+
+ if (be_valid_be_name(string)) {
+ return (Py_BuildValue("i", 0));
+ } else {
+ return (Py_BuildValue("i", 1));
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~ */
+/* Private Functions */
+/* ~~~~~~~~~~~~~~~~~ */
+
+static boolean_t
+convertBEInfoToDictionary(be_node_list_t *be, PyObject **listDict)
+{
+ if (be->be_node_name != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_ORIG_BE_NAME,
+ PyString_FromString(be->be_node_name)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (be->be_rpool != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_ORIG_BE_POOL,
+ PyString_FromString(be->be_rpool)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (be->be_mntpt != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTPOINT,
+ PyString_FromString(be->be_mntpt)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTED,
+ (be->be_mounted ? Py_True : Py_False)) != 0) {
+ return (B_FALSE);
+ }
+
+ if (PyDict_SetItemString(*listDict, BE_ATTR_ACTIVE,
+ (be->be_active ? Py_True : Py_False)) != 0) {
+ return (B_FALSE);
+ }
+
+ if (PyDict_SetItemString(*listDict, BE_ATTR_ACTIVE_ON_BOOT,
+ (be->be_active_on_boot ? Py_True : Py_False)) != 0) {
+ return (B_FALSE);
+ }
+
+ if (be->be_space_used != 0) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
+ PyLong_FromUnsignedLongLong(be->be_space_used)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (be->be_root_ds != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_ROOT_DS,
+ PyString_FromString(be->be_root_ds)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (be->be_node_creation != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
+ PyLong_FromLong(be->be_node_creation)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (be->be_policy_type != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
+ PyString_FromString(be->be_policy_type)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (be->be_uuid_str != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_UUID_STR,
+ PyString_FromString(be->be_uuid_str)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+convertDatasetInfoToDictionary(be_dataset_list_t *ds, PyObject **listDict)
+{
+ if (ds->be_dataset_name != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_DATASET,
+ PyString_FromString(ds->be_dataset_name)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (PyDict_SetItemString(*listDict, BE_ATTR_STATUS,
+ (ds->be_ds_mounted ? Py_True : Py_False)) != 0) {
+ return (B_FALSE);
+ }
+
+ if (ds->be_ds_mntpt != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTPOINT,
+ PyString_FromString(ds->be_ds_mntpt)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTED,
+ (ds->be_ds_mounted ? Py_True : Py_False)) != 0) {
+ return (B_FALSE);
+ }
+
+ if (ds->be_ds_space_used != 0) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
+ PyLong_FromUnsignedLongLong(ds->be_ds_space_used))
+ != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (ds->be_dataset_name != 0) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_DATASET,
+ PyString_FromString(ds->be_dataset_name)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (ds->be_ds_plcy_type != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
+ PyString_FromString(ds->be_ds_plcy_type)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (ds->be_ds_creation != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
+ PyLong_FromLong(ds->be_ds_creation)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+convertSnapshotInfoToDictionary(be_snapshot_list_t *ss, PyObject **listDict)
+{
+ if (ss->be_snapshot_name != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_SNAP_NAME,
+ PyString_FromString(ss->be_snapshot_name)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (ss->be_snapshot_creation != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
+ PyLong_FromLong(ss->be_snapshot_creation)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (ss->be_snapshot_type != NULL) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
+ PyString_FromString(ss->be_snapshot_type)) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ if (ss->be_snapshot_space_used != 0) {
+ if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
+ PyLong_FromUnsignedLongLong(ss->be_snapshot_space_used))
+ != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Convert string arguments to nvlist attributes
+ */
+
+static boolean_t
+convertPyArgsToNvlist(nvlist_t **nvList, int numArgs, ...)
+{
+ char *pt, *pt2;
+ va_list ap;
+ int i;
+
+ if (*nvList == NULL) {
+ if (nvlist_alloc(nvList, NV_UNIQUE_NAME, 0) != 0) {
+ (void) printf("nvlist_alloc failed.\n");
+ return (B_FALSE);
+ }
+ }
+
+ va_start(ap, numArgs);
+
+ for (i = 0; i < numArgs; i += 2) {
+ if ((pt = va_arg(ap, char *)) == NULL ||
+ (pt2 = va_arg(ap, char *)) == NULL) {
+ continue;
+ }
+ if (nvlist_add_string(*nvList, pt, pt2) != 0) {
+ (void) printf("nvlist_add_string failed for %s (%s).\n",
+ pt, pt2);
+ nvlist_free(*nvList);
+ return (B_FALSE);
+ }
+ }
+
+ va_end(ap);
+
+ return (B_TRUE);
+}
+
+/*
+ * Function: beMapLibbePyErrorToString
+ * Description: Convert Python args to an int and map an error code to an
+ * error string.
+ * Parameter:
+ * errCode - value to map to an error string.
+ *
+ * Returns error string or NULL
+ * Scope:
+ * Public
+ */
+
+char *
+beMapLibbePyErrorToString(int errCode)
+{
+ switch (errCode) {
+ case BE_PY_ERR_APPEND:
+ return ("Unable to append a dictionary to a list "
+ "of dictinaries.");
+ case BE_PY_ERR_DICT:
+ return ("Creation of a Python dictionary failed.");
+ case BE_PY_ERR_LIST:
+ return ("beList() failed.");
+ case BE_PY_ERR_NVLIST:
+ return ("An nvlist operation failed.");
+ case BE_PY_ERR_PARSETUPLE:
+ return ("PyArg_ParseTuple() failed to convert variable to C.");
+ case BE_PY_ERR_PRINT_ERR:
+ return ("bePrintErrors() failed.");
+ case BE_PY_ERR_VAR_CONV:
+ return ("Unable to add variables to a Python dictionary.");
+ default:
+ return (NULL);
+ }
+}
+
+/* Private python initialization structure */
+
+static struct PyMethodDef libbeMethods[] = {
+ {"beCopy", (PyCFunction)beCopy, METH_VARARGS, "Create/Copy a BE."},
+ {"beCreateSnapshot", (PyCFunction)beCreateSnapshot, METH_VARARGS,
+ "Create a snapshot."},
+ {"beDestroy", (PyCFunction)beDestroy, METH_VARARGS, "Destroy a BE."},
+ {"beDestroySnapshot", (PyCFunction)beDestroySnapshot, METH_VARARGS,
+ "Destroy a snapshot."},
+ {"beMount", (PyCFunction)beMount, METH_VARARGS, "Mount a BE."},
+ {"beUnmount", (PyCFunction)beUnmount, METH_VARARGS, "Unmount a BE."},
+ {"beList", (PyCFunction)beList, METH_VARARGS, "List BE info."},
+ {"beRename", (PyCFunction)beRename, METH_VARARGS, "Rename a BE."},
+ {"beActivate", (PyCFunction)beActivate, METH_VARARGS, "Activate a BE."},
+ {"beRollback", (PyCFunction)beRollback, METH_VARARGS, "Rollback a BE."},
+ {"bePrintErrors", (PyCFunction)bePrintErrors, METH_VARARGS,
+ "Enable/disable error printing."},
+ {"beGetErrDesc", (PyCFunction)beGetErrDesc, METH_VARARGS,
+ "Map Error codes to strings."},
+ {"beVerifyBEName", (PyCFunction)beVerifyBEName, METH_VARARGS,
+ "Verify BE name."},
+ {NULL, NULL, 0, NULL}
+};
+
+void
+initlibbe_py()
+{
+ /* PyMODINIT_FUNC; */
+ (void) Py_InitModule("libbe_py", libbeMethods);
+}
diff --git a/usr/src/lib/pylibbe/common/mapfile-vers b/usr/src/lib/pylibbe/common/mapfile-vers
new file mode 100644
index 0000000000..73f2bceaf5
--- /dev/null
+++ b/usr/src/lib/pylibbe/common/mapfile-vers
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate {
+ global:
+ initlibbe_py;
+ local:
+ *;
+};
diff --git a/usr/src/lib/pylibbe/i386/Makefile b/usr/src/lib/pylibbe/i386/Makefile
new file mode 100644
index 0000000000..5a771c0816
--- /dev/null
+++ b/usr/src/lib/pylibbe/i386/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS)
diff --git a/usr/src/lib/pylibbe/sparc/Makefile b/usr/src/lib/pylibbe/sparc/Makefile
new file mode 100644
index 0000000000..5a771c0816
--- /dev/null
+++ b/usr/src/lib/pylibbe/sparc/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS)
diff --git a/usr/src/pkg/manifests/SUNWbeadm.mf b/usr/src/pkg/manifests/SUNWbeadm.mf
new file mode 100644
index 0000000000..5135ad7a36
--- /dev/null
+++ b/usr/src/pkg/manifests/SUNWbeadm.mf
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+set name=pkg.fmri value=pkg:/SUNWbeadm@0.5.11,5.11-0.133
+set name=pkg.renamed value=true
+set name=variant.arch value=$(ARCH)
+depend fmri=pkg:/install/beadm@0.5.11,5.11-0.133 type=require
diff --git a/usr/src/pkg/manifests/install-beadm.mf b/usr/src/pkg/manifests/install-beadm.mf
new file mode 100644
index 0000000000..b514748b28
--- /dev/null
+++ b/usr/src/pkg/manifests/install-beadm.mf
@@ -0,0 +1,55 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+set name=pkg.fmri value=pkg:/install/beadm@$(PKGVERS)
+set name=pkg.description value="Boot Environment Management Tool"
+set name=pkg.summary value="beadm utility"
+set name=info.classification value=org.opensolaris.category.2008:System/Core
+set name=variant.arch value=$(ARCH)
+dir path=sbin group=sys
+dir path=usr group=sys
+dir path=usr/include
+dir path=usr/lib
+dir path=usr/lib/python2.6
+dir path=usr/lib/python2.6/vendor-packages
+dir path=usr/lib/python2.6/vendor-packages/beadm
+dir path=usr/sbin
+dir path=usr/share group=sys
+file path=sbin/beadm mode=0555
+file path=usr/include/libbe.h
+file path=usr/lib/libbe.so.1
+file path=usr/lib/llib-lbe
+file path=usr/lib/llib-lbe.ln
+file path=usr/lib/python2.6/vendor-packages/beadm/BootEnvironment.py mode=0444
+file path=usr/lib/python2.6/vendor-packages/beadm/BootEnvironment.pyc \
+ mode=0444
+file path=usr/lib/python2.6/vendor-packages/beadm/__init__.py mode=0444
+file path=usr/lib/python2.6/vendor-packages/beadm/__init__.pyc mode=0444
+file path=usr/lib/python2.6/vendor-packages/beadm/messages.py mode=0444
+file path=usr/lib/python2.6/vendor-packages/beadm/messages.pyc mode=0444
+file path=usr/lib/python2.6/vendor-packages/libbe_py.so
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
+link path=usr/lib/libbe.so target=./libbe.so.1
+link path=usr/sbin/beadm target=../../sbin/beadm