- Changes to make library interface more clear and to better suit go package standards
This commit is contained in:
		
							parent
							
								
									db4703b708
								
							
						
					
					
						commit
						bc19737222
					
				| 
						 | 
				
			
			@ -46,10 +46,10 @@ props := make(map[ZFSProp]Property)
 | 
			
		|||
// similar to convert in to string (base 10) from numeric type.
 | 
			
		||||
strSize := "1073741824"
 | 
			
		||||
 | 
			
		||||
props[ZFSPropVolsize] = Property{Value: strSize}
 | 
			
		||||
props[DatasetPropVolsize] = Property{Value: strSize}
 | 
			
		||||
// In addition I explicitly choose some more properties to be set.
 | 
			
		||||
props[ZFSPropVolblocksize] = Property{Value: "4096"}
 | 
			
		||||
props[ZFSPropReservation] = Property{Value: strSize}
 | 
			
		||||
props[DatasetPropVolblocksize] = Property{Value: "4096"}
 | 
			
		||||
props[DatasetPropReservation] = Property{Value: strSize}
 | 
			
		||||
 | 
			
		||||
// Lets create desired volume
 | 
			
		||||
d, err := DatasetCreate("TESTPOOL/VOLUME1", DatasetTypeVolume, props)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										329
									
								
								common.go
								
								
								
								
							
							
						
						
									
										329
									
								
								common.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
// Implements basic manipulation of ZFS pools and data sets.
 | 
			
		||||
// Package zfs implements basic manipulation of ZFS pools and data sets.
 | 
			
		||||
// Use libzfs C library instead CLI zfs tools, with goal
 | 
			
		||||
// to let using and manipulating OpenZFS form with in go project.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -23,36 +23,41 @@ import (
 | 
			
		|||
	"errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// VDevType type of device in the pool
 | 
			
		||||
type VDevType string
 | 
			
		||||
 | 
			
		||||
var libzfs_handle *C.struct_libzfs_handle
 | 
			
		||||
var libzfsHandle *C.struct_libzfs_handle
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	libzfs_handle = C.libzfs_init()
 | 
			
		||||
	libzfsHandle = C.libzfs_init()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Types of Virtual Devices
 | 
			
		||||
const (
 | 
			
		||||
	VDevTypeRoot      VDevType = "root"
 | 
			
		||||
	VDevTypeMirror             = "mirror"
 | 
			
		||||
	VDevTypeReplacing          = "replacing"
 | 
			
		||||
	VDevTypeRaidz              = "raidz"
 | 
			
		||||
	VDevTypeDisk               = "disk"
 | 
			
		||||
	VDevTypeFile               = "file"
 | 
			
		||||
	VDevTypeMissing            = "missing"
 | 
			
		||||
	VDevTypeHole               = "hole"
 | 
			
		||||
	VDevTypeSpare              = "spare"
 | 
			
		||||
	VDevTypeLog                = "log"
 | 
			
		||||
	VDevTypeL2cache            = "l2cache"
 | 
			
		||||
	VDevTypeRoot      VDevType = "root"      // VDevTypeRoot root device in ZFS pool
 | 
			
		||||
	VDevTypeMirror             = "mirror"    // VDevTypeMirror mirror device in ZFS pool
 | 
			
		||||
	VDevTypeReplacing          = "replacing" // VDevTypeReplacing replacing
 | 
			
		||||
	VDevTypeRaidz              = "raidz"     // VDevTypeRaidz RAIDZ device
 | 
			
		||||
	VDevTypeDisk               = "disk"      // VDevTypeDisk device is disk
 | 
			
		||||
	VDevTypeFile               = "file"      // VDevTypeFile device is file
 | 
			
		||||
	VDevTypeMissing            = "missing"   // VDevTypeMissing missing device
 | 
			
		||||
	VDevTypeHole               = "hole"      // VDevTypeHole hole
 | 
			
		||||
	VDevTypeSpare              = "spare"     // VDevTypeSpare spare device
 | 
			
		||||
	VDevTypeLog                = "log"       // VDevTypeLog ZIL device
 | 
			
		||||
	VDevTypeL2cache            = "l2cache"   // VDevTypeL2cache cache device (disk)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PoolProp int
 | 
			
		||||
type ZFSProp int
 | 
			
		||||
// Prop type to enumerate all different properties suppoerted by ZFS
 | 
			
		||||
type Prop int
 | 
			
		||||
 | 
			
		||||
// PoolStatus type representing status of the pool
 | 
			
		||||
type PoolStatus int
 | 
			
		||||
 | 
			
		||||
// PoolState type representing pool state
 | 
			
		||||
type PoolState uint64
 | 
			
		||||
 | 
			
		||||
// Zfs pool or dataset property
 | 
			
		||||
// Property ZFS pool or dataset property value
 | 
			
		||||
type Property struct {
 | 
			
		||||
	Value  string
 | 
			
		||||
	Source string
 | 
			
		||||
| 
						 | 
				
			
			@ -64,21 +69,21 @@ const (
 | 
			
		|||
	 * The following correspond to faults as defined in the (fault.fs.zfs.*)
 | 
			
		||||
	 * event namespace.  Each is associated with a corresponding message ID.
 | 
			
		||||
	 */
 | 
			
		||||
	PoolStatusCorrupt_cache       PoolStatus = iota /* corrupt /kernel/drv/zpool.cache */
 | 
			
		||||
	PoolStatusMissing_dev_r                         /* missing device with replicas */
 | 
			
		||||
	PoolStatusMissing_dev_nr                        /* missing device with no replicas */
 | 
			
		||||
	PoolStatusCorrupt_label_r                       /* bad device label with replicas */
 | 
			
		||||
	PoolStatusCorrupt_label_nr                      /* bad device label with no replicas */
 | 
			
		||||
	PoolStatusBad_guid_sum                          /* sum of device guids didn't match */
 | 
			
		||||
	PoolStatusCorrupt_pool                          /* pool metadata is corrupted */
 | 
			
		||||
	PoolStatusCorrupt_data                          /* data errors in user (meta)data */
 | 
			
		||||
	PoolStatusFailing_dev                           /* device experiencing errors */
 | 
			
		||||
	PoolStatusVersion_newer                         /* newer on-disk version */
 | 
			
		||||
	PoolStatusHostid_mismatch                       /* last accessed by another system */
 | 
			
		||||
	PoolStatusIo_failure_wait                       /* failed I/O, failmode 'wait' */
 | 
			
		||||
	PoolStatusIo_failure_continue                   /* failed I/O, failmode 'continue' */
 | 
			
		||||
	PoolStatusBad_log                               /* cannot read log chain(s) */
 | 
			
		||||
	PoolStatusErrata                                /* informational errata available */
 | 
			
		||||
	PoolStatusCorruptCache      PoolStatus = iota /* corrupt /kernel/drv/zpool.cache */
 | 
			
		||||
	PoolStatusMissingDevR                         /* missing device with replicas */
 | 
			
		||||
	PoolStatusMissingDevNr                        /* missing device with no replicas */
 | 
			
		||||
	PoolStatusCorruptLabelR                       /* bad device label with replicas */
 | 
			
		||||
	PoolStatusCorruptLabelNr                      /* bad device label with no replicas */
 | 
			
		||||
	PoolStatusBadGUIDSum                          /* sum of device guids didn't match */
 | 
			
		||||
	PoolStatusCorruptPool                         /* pool metadata is corrupted */
 | 
			
		||||
	PoolStatusCorruptData                         /* data errors in user (meta)data */
 | 
			
		||||
	PoolStatusFailingDev                          /* device experiencing errors */
 | 
			
		||||
	PoolStatusVersionNewer                        /* newer on-disk version */
 | 
			
		||||
	PoolStatusHostidMismatch                      /* last accessed by another system */
 | 
			
		||||
	PoolStatusIoFailureWait                       /* failed I/O, failmode 'wait' */
 | 
			
		||||
	PoolStatusIoFailureContinue                   /* failed I/O, failmode 'continue' */
 | 
			
		||||
	PoolStatusBadLog                              /* cannot read log chain(s) */
 | 
			
		||||
	PoolStatusErrata                              /* informational errata available */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the pool has unsupported features but can still be opened in
 | 
			
		||||
| 
						 | 
				
			
			@ -86,27 +91,27 @@ const (
 | 
			
		|||
	 * pool has unsupported features but cannot be opened at all, its
 | 
			
		||||
	 * status is ZPOOL_STATUS_UNSUP_FEAT_READ.
 | 
			
		||||
	 */
 | 
			
		||||
	PoolStatusUnsup_feat_read  /* unsupported features for read */
 | 
			
		||||
	PoolStatusUnsup_feat_write /* unsupported features for write */
 | 
			
		||||
	PoolStatusUnsupFeatRead  /* unsupported features for read */
 | 
			
		||||
	PoolStatusUnsupFeatWrite /* unsupported features for write */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * These faults have no corresponding message ID.  At the time we are
 | 
			
		||||
	 * checking the status, the original reason for the FMA fault (I/O or
 | 
			
		||||
	 * checksum errors) has been lost.
 | 
			
		||||
	 */
 | 
			
		||||
	PoolStatusFaulted_dev_r  /* faulted device with replicas */
 | 
			
		||||
	PoolStatusFaulted_dev_nr /* faulted device with no replicas */
 | 
			
		||||
	PoolStatusFaultedDevR  /* faulted device with replicas */
 | 
			
		||||
	PoolStatusFaultedDevNr /* faulted device with no replicas */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The following are not faults per se, but still an error possibly
 | 
			
		||||
	 * requiring administrative attention.  There is no corresponding
 | 
			
		||||
	 * message ID.
 | 
			
		||||
	 */
 | 
			
		||||
	PoolStatusVersion_older /* older legacy on-disk version */
 | 
			
		||||
	PoolStatusFeat_disabled /* supported features are disabled */
 | 
			
		||||
	PoolStatusResilvering   /* device being resilvered */
 | 
			
		||||
	PoolStatusOffline_dev   /* device online */
 | 
			
		||||
	PoolStatusRemoved_dev   /* removed device */
 | 
			
		||||
	PoolStatusVersionOlder /* older legacy on-disk version */
 | 
			
		||||
	PoolStatusFeatDisabled /* supported features are disabled */
 | 
			
		||||
	PoolStatusResilvering  /* device being resilvered */
 | 
			
		||||
	PoolStatusOfflineDev   /* device online */
 | 
			
		||||
	PoolStatusRemovedDev   /* removed device */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Finally, the following indicates a healthy pool.
 | 
			
		||||
| 
						 | 
				
			
			@ -129,12 +134,12 @@ const (
 | 
			
		|||
// Pool properties. Enumerates available ZFS pool properties. Use it to access
 | 
			
		||||
// pool properties either to read or set soecific property.
 | 
			
		||||
const (
 | 
			
		||||
	PoolPropName PoolProp = iota
 | 
			
		||||
	PoolPropName Prop = iota
 | 
			
		||||
	PoolPropSize
 | 
			
		||||
	PoolPropCapacity
 | 
			
		||||
	PoolPropAltroot
 | 
			
		||||
	PoolPropHealth
 | 
			
		||||
	PoolPropGuid
 | 
			
		||||
	PoolPropGUID
 | 
			
		||||
	PoolPropVersion
 | 
			
		||||
	PoolPropBootfs
 | 
			
		||||
	PoolPropDelegation
 | 
			
		||||
| 
						 | 
				
			
			@ -166,102 +171,178 @@ const (
 | 
			
		|||
 * the property table in module/zcommon/zfs_prop.c.
 | 
			
		||||
 */
 | 
			
		||||
const (
 | 
			
		||||
	ZFSPropType ZFSProp = iota
 | 
			
		||||
	ZFSPropCreation
 | 
			
		||||
	ZFSPropUsed
 | 
			
		||||
	ZFSPropAvailable
 | 
			
		||||
	ZFSPropReferenced
 | 
			
		||||
	ZFSPropCompressratio
 | 
			
		||||
	ZFSPropMounted
 | 
			
		||||
	ZFSPropOrigin
 | 
			
		||||
	ZFSPropQuota
 | 
			
		||||
	ZFSPropReservation
 | 
			
		||||
	ZFSPropVolsize
 | 
			
		||||
	ZFSPropVolblocksize
 | 
			
		||||
	ZFSPropRecordsize
 | 
			
		||||
	ZFSPropMountpoint
 | 
			
		||||
	ZFSPropSharenfs
 | 
			
		||||
	ZFSPropChecksum
 | 
			
		||||
	ZFSPropCompression
 | 
			
		||||
	ZFSPropAtime
 | 
			
		||||
	ZFSPropDevices
 | 
			
		||||
	ZFSPropExec
 | 
			
		||||
	ZFSPropSetuid
 | 
			
		||||
	ZFSPropReadonly
 | 
			
		||||
	ZFSPropZoned
 | 
			
		||||
	ZFSPropSnapdir
 | 
			
		||||
	ZFSPropPrivate /* not exposed to user, temporary */
 | 
			
		||||
	ZFSPropAclinherit
 | 
			
		||||
	ZFSPropCreatetxg /* not exposed to the user */
 | 
			
		||||
	ZFSPropName      /* not exposed to the user */
 | 
			
		||||
	ZFSPropCanmount
 | 
			
		||||
	ZFSPropIscsioptions /* not exposed to the user */
 | 
			
		||||
	ZFSPropXattr
 | 
			
		||||
	ZFSPropNumclones /* not exposed to the user */
 | 
			
		||||
	ZFSPropCopies
 | 
			
		||||
	ZFSPropVersion
 | 
			
		||||
	ZFSPropUtf8only
 | 
			
		||||
	ZFSPropNormalize
 | 
			
		||||
	ZFSPropCase
 | 
			
		||||
	ZFSPropVscan
 | 
			
		||||
	ZFSPropNbmand
 | 
			
		||||
	ZFSPropSharesmb
 | 
			
		||||
	ZFSPropRefquota
 | 
			
		||||
	ZFSPropRefreservation
 | 
			
		||||
	ZFSPropGuid
 | 
			
		||||
	ZFSPropPrimarycache
 | 
			
		||||
	ZFSPropSecondarycache
 | 
			
		||||
	ZFSPropUsedsnap
 | 
			
		||||
	ZFSPropUsedds
 | 
			
		||||
	ZFSPropUsedchild
 | 
			
		||||
	ZFSPropUsedrefreserv
 | 
			
		||||
	ZFSPropUseraccounting /* not exposed to the user */
 | 
			
		||||
	ZFSPropStmf_shareinfo /* not exposed to the user */
 | 
			
		||||
	ZFSPropDefer_destroy
 | 
			
		||||
	ZFSPropUserrefs
 | 
			
		||||
	ZFSPropLogbias
 | 
			
		||||
	ZFSPropUnique   /* not exposed to the user */
 | 
			
		||||
	ZFSPropObjsetid /* not exposed to the user */
 | 
			
		||||
	ZFSPropDedup
 | 
			
		||||
	ZFSPropMlslabel
 | 
			
		||||
	ZFSPropSync
 | 
			
		||||
	ZFSPropRefratio
 | 
			
		||||
	ZFSPropWritten
 | 
			
		||||
	ZFSPropClones
 | 
			
		||||
	ZFSPropLogicalused
 | 
			
		||||
	ZFSPropLogicalreferenced
 | 
			
		||||
	ZFSPropInconsistent /* not exposed to the user */
 | 
			
		||||
	ZFSPropSnapdev
 | 
			
		||||
	ZFSPropAcltype
 | 
			
		||||
	ZFSPropSelinux_context
 | 
			
		||||
	ZFSPropSelinux_fscontext
 | 
			
		||||
	ZFSPropSelinux_defcontext
 | 
			
		||||
	ZFSPropSelinux_rootcontext
 | 
			
		||||
	ZFSPropRelatime
 | 
			
		||||
	ZFSPropRedundant_metadata
 | 
			
		||||
	ZFSPropOverlay
 | 
			
		||||
	ZFSNumProps
 | 
			
		||||
	DatasetPropType Prop = iota
 | 
			
		||||
	DatasetPropCreation
 | 
			
		||||
	DatasetPropUsed
 | 
			
		||||
	DatasetPropAvailable
 | 
			
		||||
	DatasetPropReferenced
 | 
			
		||||
	DatasetPropCompressratio
 | 
			
		||||
	DatasetPropMounted
 | 
			
		||||
	DatasetPropOrigin
 | 
			
		||||
	DatasetPropQuota
 | 
			
		||||
	DatasetPropReservation
 | 
			
		||||
	DatasetPropVolsize
 | 
			
		||||
	DatasetPropVolblocksize
 | 
			
		||||
	DatasetPropRecordsize
 | 
			
		||||
	DatasetPropMountpoint
 | 
			
		||||
	DatasetPropSharenfs
 | 
			
		||||
	DatasetPropChecksum
 | 
			
		||||
	DatasetPropCompression
 | 
			
		||||
	DatasetPropAtime
 | 
			
		||||
	DatasetPropDevices
 | 
			
		||||
	DatasetPropExec
 | 
			
		||||
	DatasetPropSetuid
 | 
			
		||||
	DatasetPropReadonly
 | 
			
		||||
	DatasetPropZoned
 | 
			
		||||
	DatasetPropSnapdir
 | 
			
		||||
	DatasetPropPrivate /* not exposed to user, temporary */
 | 
			
		||||
	DatasetPropAclinherit
 | 
			
		||||
	DatasetPropCreatetxg /* not exposed to the user */
 | 
			
		||||
	DatasetPropName      /* not exposed to the user */
 | 
			
		||||
	DatasetPropCanmount
 | 
			
		||||
	DatasetPropIscsioptions /* not exposed to the user */
 | 
			
		||||
	DatasetPropXattr
 | 
			
		||||
	DatasetPropNumclones /* not exposed to the user */
 | 
			
		||||
	DatasetPropCopies
 | 
			
		||||
	DatasetPropVersion
 | 
			
		||||
	DatasetPropUtf8only
 | 
			
		||||
	DatasetPropNormalize
 | 
			
		||||
	DatasetPropCase
 | 
			
		||||
	DatasetPropVscan
 | 
			
		||||
	DatasetPropNbmand
 | 
			
		||||
	DatasetPropSharesmb
 | 
			
		||||
	DatasetPropRefquota
 | 
			
		||||
	DatasetPropRefreservation
 | 
			
		||||
	DatasetPropGUID
 | 
			
		||||
	DatasetPropPrimarycache
 | 
			
		||||
	DatasetPropSecondarycache
 | 
			
		||||
	DatasetPropUsedsnap
 | 
			
		||||
	DatasetPropUsedds
 | 
			
		||||
	DatasetPropUsedchild
 | 
			
		||||
	DatasetPropUsedrefreserv
 | 
			
		||||
	DatasetPropUseraccounting /* not exposed to the user */
 | 
			
		||||
	DatasetPropStmfShareinfo  /* not exposed to the user */
 | 
			
		||||
	DatasetPropDeferDestroy
 | 
			
		||||
	DatasetPropUserrefs
 | 
			
		||||
	DatasetPropLogbias
 | 
			
		||||
	DatasetPropUnique   /* not exposed to the user */
 | 
			
		||||
	DatasetPropObjsetid /* not exposed to the user */
 | 
			
		||||
	DatasetPropDedup
 | 
			
		||||
	DatasetPropMlslabel
 | 
			
		||||
	DatasetPropSync
 | 
			
		||||
	DatasetPropRefratio
 | 
			
		||||
	DatasetPropWritten
 | 
			
		||||
	DatasetPropClones
 | 
			
		||||
	DatasetPropLogicalused
 | 
			
		||||
	DatasetPropLogicalreferenced
 | 
			
		||||
	DatasetPropInconsistent /* not exposed to the user */
 | 
			
		||||
	DatasetPropSnapdev
 | 
			
		||||
	DatasetPropAcltype
 | 
			
		||||
	DatasetPropSelinuxContext
 | 
			
		||||
	DatasetPropSelinuxFsContext
 | 
			
		||||
	DatasetPropSelinuxDefContext
 | 
			
		||||
	DatasetPropSelinuxRootContext
 | 
			
		||||
	DatasetPropRelatime
 | 
			
		||||
	DatasetPropRedundantMetadata
 | 
			
		||||
	DatasetPropOverlay
 | 
			
		||||
	DatasetNumProps
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Get last underlying libzfs error description if any
 | 
			
		||||
// LastError get last underlying libzfs error description if any
 | 
			
		||||
func LastError() (err error) {
 | 
			
		||||
	errno := C.libzfs_errno(libzfs_handle)
 | 
			
		||||
	errno := C.libzfs_errno(libzfsHandle)
 | 
			
		||||
	if errno == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return errors.New(C.GoString(C.libzfs_error_description(libzfs_handle)))
 | 
			
		||||
	return errors.New(C.GoString(C.libzfs_error_description(libzfsHandle)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Force clear of any last error set by undeliying libzfs
 | 
			
		||||
// ClearLastError force clear of any last error set by undeliying libzfs
 | 
			
		||||
func ClearLastError() (err error) {
 | 
			
		||||
	err = LastError()
 | 
			
		||||
	C.clear_last_error(libzfs_handle)
 | 
			
		||||
	C.clear_last_error(libzfsHandle)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func boolean_t(b bool) (r C.boolean_t) {
 | 
			
		||||
func booleanT(b bool) (r C.boolean_t) {
 | 
			
		||||
	if b {
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ZFS errors
 | 
			
		||||
const (
 | 
			
		||||
	ESuccess            = 0            /* no error -- success */
 | 
			
		||||
	ENomem              = 2000 << iota /* out of memory */
 | 
			
		||||
	EBadprop                           /* invalid property value */
 | 
			
		||||
	EPropreadonly                      /* cannot set readonly property */
 | 
			
		||||
	EProptype                          /* property does not apply to dataset type */
 | 
			
		||||
	EPropnoninherit                    /* property is not inheritable */
 | 
			
		||||
	EPropspace                         /* bad quota or reservation */
 | 
			
		||||
	EBadtype                           /* dataset is not of appropriate type */
 | 
			
		||||
	EBusy                              /* pool or dataset is busy */
 | 
			
		||||
	EExists                            /* pool or dataset already exists */
 | 
			
		||||
	ENoent                             /* no such pool or dataset */
 | 
			
		||||
	EBadstream                         /* bad backup stream */
 | 
			
		||||
	EDsreadonly                        /* dataset is readonly */
 | 
			
		||||
	EVoltoobig                         /* volume is too large for 32-bit system */
 | 
			
		||||
	EInvalidname                       /* invalid dataset name */
 | 
			
		||||
	EBadrestore                        /* unable to restore to destination */
 | 
			
		||||
	EBadbackup                         /* backup failed */
 | 
			
		||||
	EBadtarget                         /* bad attach/detach/replace target */
 | 
			
		||||
	ENodevice                          /* no such device in pool */
 | 
			
		||||
	EBaddev                            /* invalid device to add */
 | 
			
		||||
	ENoreplicas                        /* no valid replicas */
 | 
			
		||||
	EResilvering                       /* currently resilvering */
 | 
			
		||||
	EBadversion                        /* unsupported version */
 | 
			
		||||
	EPoolunavail                       /* pool is currently unavailable */
 | 
			
		||||
	EDevoverflow                       /* too many devices in one vdev */
 | 
			
		||||
	EBadpath                           /* must be an absolute path */
 | 
			
		||||
	ECrosstarget                       /* rename or clone across pool or dataset */
 | 
			
		||||
	EZoned                             /* used improperly in local zone */
 | 
			
		||||
	EMountfailed                       /* failed to mount dataset */
 | 
			
		||||
	EUmountfailed                      /* failed to unmount dataset */
 | 
			
		||||
	EUnsharenfsfailed                  /* unshare(1M) failed */
 | 
			
		||||
	ESharenfsfailed                    /* share(1M) failed */
 | 
			
		||||
	EPerm                              /* permission denied */
 | 
			
		||||
	ENospc                             /* out of space */
 | 
			
		||||
	EFault                             /* bad address */
 | 
			
		||||
	EIo                                /* I/O error */
 | 
			
		||||
	EIntr                              /* signal received */
 | 
			
		||||
	EIsspare                           /* device is a hot spare */
 | 
			
		||||
	EInvalconfig                       /* invalid vdev configuration */
 | 
			
		||||
	ERecursive                         /* recursive dependency */
 | 
			
		||||
	ENohistory                         /* no history object */
 | 
			
		||||
	EPoolprops                         /* couldn't retrieve pool props */
 | 
			
		||||
	EPoolNotsup                        /* ops not supported for this type of pool */
 | 
			
		||||
	EPoolInvalarg                      /* invalid argument for this pool operation */
 | 
			
		||||
	ENametoolong                       /* dataset name is too long */
 | 
			
		||||
	EOpenfailed                        /* open of device failed */
 | 
			
		||||
	ENocap                             /* couldn't get capacity */
 | 
			
		||||
	ELabelfailed                       /* write of label failed */
 | 
			
		||||
	EBadwho                            /* invalid permission who */
 | 
			
		||||
	EBadperm                           /* invalid permission */
 | 
			
		||||
	EBadpermset                        /* invalid permission set name */
 | 
			
		||||
	ENodelegation                      /* delegated administration is disabled */
 | 
			
		||||
	EUnsharesmbfailed                  /* failed to unshare over smb */
 | 
			
		||||
	ESharesmbfailed                    /* failed to share over smb */
 | 
			
		||||
	EBadcache                          /* bad cache file */
 | 
			
		||||
	EIsl2CACHE                         /* device is for the level 2 ARC */
 | 
			
		||||
	EVdevnotsup                        /* unsupported vdev type */
 | 
			
		||||
	ENotsup                            /* ops not supported on this dataset */
 | 
			
		||||
	EActiveSpare                       /* pool has active shared spare devices */
 | 
			
		||||
	EUnplayedLogs                      /* log device has unplayed logs */
 | 
			
		||||
	EReftagRele                        /* snapshot release: tag not found */
 | 
			
		||||
	EReftagHold                        /* snapshot hold: tag already exists */
 | 
			
		||||
	ETagtoolong                        /* snapshot hold/rele: tag too long */
 | 
			
		||||
	EPipefailed                        /* pipe create failed */
 | 
			
		||||
	EThreadcreatefailed                /* thread create failed */
 | 
			
		||||
	EPostsplitOnline                   /* onlining a disk after splitting it */
 | 
			
		||||
	EScrubbing                         /* currently scrubbing */
 | 
			
		||||
	ENoScrub                           /* no active scrub */
 | 
			
		||||
	EDiff                              /* general failure of zfs diff */
 | 
			
		||||
	EDiffdata                          /* bad zfs diff data */
 | 
			
		||||
	EPoolreadonly                      /* pool is in read-only mode */
 | 
			
		||||
	EUnknown
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										112
									
								
								zfs.go
								
								
								
								
							
							
						
						
									
										112
									
								
								zfs.go
								
								
								
								
							| 
						 | 
				
			
			@ -14,20 +14,30 @@ const (
 | 
			
		|||
	msgDatasetIsNil = "Dataset handle not initialized or its closed"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DatasetProperties type is map of dataset or volume properties prop -> value
 | 
			
		||||
type DatasetProperties map[Prop]string
 | 
			
		||||
 | 
			
		||||
// DatasetType defines enum of dataset types
 | 
			
		||||
type DatasetType int32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// DatasetTypeFilesystem - file system dataset
 | 
			
		||||
	DatasetTypeFilesystem DatasetType = (1 << 0)
 | 
			
		||||
	DatasetTypeSnapshot               = (1 << 1)
 | 
			
		||||
	DatasetTypeVolume                 = (1 << 2)
 | 
			
		||||
	DatasetTypePool                   = (1 << 3)
 | 
			
		||||
	DatasetTypeBookmark               = (1 << 4)
 | 
			
		||||
	// DatasetTypeSnapshot - snapshot of dataset
 | 
			
		||||
	DatasetTypeSnapshot = (1 << 1)
 | 
			
		||||
	// DatasetTypeVolume - volume (virtual block device) dataset
 | 
			
		||||
	DatasetTypeVolume = (1 << 2)
 | 
			
		||||
	// DatasetTypePool - pool dataset
 | 
			
		||||
	DatasetTypePool = (1 << 3)
 | 
			
		||||
	// DatasetTypeBookmark - bookmark dataset
 | 
			
		||||
	DatasetTypeBookmark = (1 << 4)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Dataset - ZFS dataset object
 | 
			
		||||
type Dataset struct {
 | 
			
		||||
	list       *C.dataset_list_t
 | 
			
		||||
	Type       DatasetType
 | 
			
		||||
	Properties map[ZFSProp]Property
 | 
			
		||||
	Properties map[Prop]Property
 | 
			
		||||
	Children   []Dataset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +47,7 @@ func (d *Dataset) openChildren() (err error) {
 | 
			
		|||
	errcode := C.dataset_list_children(d.list.zh, &(dataset.list))
 | 
			
		||||
	for dataset.list != nil {
 | 
			
		||||
		dataset.Type = DatasetType(C.zfs_get_type(dataset.list.zh))
 | 
			
		||||
		dataset.Properties = make(map[ZFSProp]Property)
 | 
			
		||||
		dataset.Properties = make(map[Prop]Property)
 | 
			
		||||
		err = dataset.ReloadProperties()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +59,7 @@ func (d *Dataset) openChildren() (err error) {
 | 
			
		|||
		err = LastError()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for ci, _ := range d.Children {
 | 
			
		||||
	for ci := range d.Children {
 | 
			
		||||
		if err = d.Children[ci].openChildren(); err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -57,11 +67,11 @@ func (d *Dataset) openChildren() (err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Recursive get handles to all available datasets on system
 | 
			
		||||
// DatasetOpenAll recursive get handles to all available datasets on system
 | 
			
		||||
// (file-systems, volumes or snapshots).
 | 
			
		||||
func DatasetOpenAll() (datasets []Dataset, err error) {
 | 
			
		||||
	var dataset Dataset
 | 
			
		||||
	errcode := C.dataset_list_root(libzfs_handle, &dataset.list)
 | 
			
		||||
	errcode := C.dataset_list_root(libzfsHandle, &dataset.list)
 | 
			
		||||
	for dataset.list != nil {
 | 
			
		||||
		dataset.Type = DatasetType(C.zfs_get_type(dataset.list.zh))
 | 
			
		||||
		err = dataset.ReloadProperties()
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +85,7 @@ func DatasetOpenAll() (datasets []Dataset, err error) {
 | 
			
		|||
		err = LastError()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for ci, _ := range datasets {
 | 
			
		||||
	for ci := range datasets {
 | 
			
		||||
		if err = datasets[ci].openChildren(); err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -83,24 +93,25 @@ func DatasetOpenAll() (datasets []Dataset, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close all datasets in slice and all of its recursive children datasets
 | 
			
		||||
// DatasetCloseAll close all datasets in slice and all of its recursive
 | 
			
		||||
// children datasets
 | 
			
		||||
func DatasetCloseAll(datasets []Dataset) {
 | 
			
		||||
	for _, d := range datasets {
 | 
			
		||||
		d.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Open dataset and all of its recursive children datasets
 | 
			
		||||
// DatasetOpen open dataset and all of its recursive children datasets
 | 
			
		||||
func DatasetOpen(path string) (d Dataset, err error) {
 | 
			
		||||
	d.list = C.create_dataset_list_item()
 | 
			
		||||
	d.list.zh = C.zfs_open(libzfs_handle, C.CString(path), 0xF)
 | 
			
		||||
	d.list.zh = C.zfs_open(libzfsHandle, C.CString(path), 0xF)
 | 
			
		||||
 | 
			
		||||
	if d.list.zh == nil {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	d.Type = DatasetType(C.zfs_get_type(d.list.zh))
 | 
			
		||||
	d.Properties = make(map[ZFSProp]Property)
 | 
			
		||||
	d.Properties = make(map[Prop]Property)
 | 
			
		||||
	err = d.ReloadProperties()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +120,7 @@ func DatasetOpen(path string) (d Dataset, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func datasetPropertiesTo_nvlist(props map[ZFSProp]Property) (
 | 
			
		||||
func datasetPropertiesTonvlist(props map[Prop]Property) (
 | 
			
		||||
	cprops *C.nvlist_t, err error) {
 | 
			
		||||
	// convert properties to nvlist C type
 | 
			
		||||
	r := C.nvlist_alloc(&cprops, C.NV_UNIQUE_NAME, 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -129,16 +140,17 @@ func datasetPropertiesTo_nvlist(props map[ZFSProp]Property) (
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a new filesystem or volume on path representing pool/dataset or pool/parent/dataset
 | 
			
		||||
// DatasetCreate create a new filesystem or volume on path representing
 | 
			
		||||
// pool/dataset or pool/parent/dataset
 | 
			
		||||
func DatasetCreate(path string, dtype DatasetType,
 | 
			
		||||
	props map[ZFSProp]Property) (d Dataset, err error) {
 | 
			
		||||
	props map[Prop]Property) (d Dataset, err error) {
 | 
			
		||||
	var cprops *C.nvlist_t
 | 
			
		||||
	if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
 | 
			
		||||
	if cprops, err = datasetPropertiesTonvlist(props); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer C.nvlist_free(cprops)
 | 
			
		||||
 | 
			
		||||
	errcode := C.zfs_create(libzfs_handle, C.CString(path),
 | 
			
		||||
	errcode := C.zfs_create(libzfsHandle, C.CString(path),
 | 
			
		||||
		C.zfs_type_t(dtype), cprops)
 | 
			
		||||
	if errcode != 0 {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +158,8 @@ func DatasetCreate(path string, dtype DatasetType,
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close dataset and all its recursive children datasets (close handle and cleanup dataset object/s from memory)
 | 
			
		||||
// Close close dataset and all its recursive children datasets (close handle
 | 
			
		||||
// and cleanup dataset object/s from memory)
 | 
			
		||||
func (d *Dataset) Close() {
 | 
			
		||||
	if d.list != nil && d.list.zh != nil {
 | 
			
		||||
		C.dataset_list_close(d.list)
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +169,7 @@ func (d *Dataset) Close() {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroys the dataset.  The caller must make sure that the filesystem
 | 
			
		||||
// Destroy destroys the dataset.  The caller must make sure that the filesystem
 | 
			
		||||
// isn't mounted, and that there are no active dependents. Set Defer argument
 | 
			
		||||
// to true to defer destruction for when dataset is not in use.
 | 
			
		||||
func (d *Dataset) Destroy(Defer bool) (err error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -165,13 +178,13 @@ func (d *Dataset) Destroy(Defer bool) (err error) {
 | 
			
		|||
		if e != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		dsType, e := d.GetProperty(ZFSPropType)
 | 
			
		||||
		dsType, e := d.GetProperty(DatasetPropType)
 | 
			
		||||
		err = errors.New("Cannot destroy dataset " + path +
 | 
			
		||||
			": " + dsType.Value + " has children")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if d.list != nil {
 | 
			
		||||
		if ec := C.zfs_destroy(d.list.zh, boolean_t(Defer)); ec != 0 {
 | 
			
		||||
		if ec := C.zfs_destroy(d.list.zh, booleanT(Defer)); ec != 0 {
 | 
			
		||||
			err = LastError()
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +193,7 @@ func (d *Dataset) Destroy(Defer bool) (err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Recursively destroy children of dataset and dataset.
 | 
			
		||||
// DestroyRecursive recursively destroy children of dataset and dataset.
 | 
			
		||||
func (d *Dataset) DestroyRecursive() (err error) {
 | 
			
		||||
	if len(d.Children) > 0 {
 | 
			
		||||
		for _, c := range d.Children {
 | 
			
		||||
| 
						 | 
				
			
			@ -197,6 +210,7 @@ func (d *Dataset) DestroyRecursive() (err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pool returns pool dataset belongs to
 | 
			
		||||
func (d *Dataset) Pool() (p Pool, err error) {
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
| 
						 | 
				
			
			@ -212,6 +226,7 @@ func (d *Dataset) Pool() (p Pool, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReloadProperties re-read dataset's properties
 | 
			
		||||
func (d *Dataset) ReloadProperties() (err error) {
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
| 
						 | 
				
			
			@ -220,8 +235,8 @@ func (d *Dataset) ReloadProperties() (err error) {
 | 
			
		|||
	var plist *C.property_list_t
 | 
			
		||||
	plist = C.new_property_list()
 | 
			
		||||
	defer C.free_properties(plist)
 | 
			
		||||
	d.Properties = make(map[ZFSProp]Property)
 | 
			
		||||
	for prop := ZFSPropType; prop < ZFSNumProps; prop++ {
 | 
			
		||||
	d.Properties = make(map[Prop]Property)
 | 
			
		||||
	for prop := DatasetPropType; prop < DatasetNumProps; prop++ {
 | 
			
		||||
		errcode := C.read_dataset_property(d.list.zh, plist, C.int(prop))
 | 
			
		||||
		if errcode != 0 {
 | 
			
		||||
			continue
 | 
			
		||||
| 
						 | 
				
			
			@ -232,9 +247,9 @@ func (d *Dataset) ReloadProperties() (err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reload and return single specified property. This also reloads requested
 | 
			
		||||
// GetProperty reload and return single specified property. This also reloads requested
 | 
			
		||||
// property in Properties map.
 | 
			
		||||
func (d *Dataset) GetProperty(p ZFSProp) (prop Property, err error) {
 | 
			
		||||
func (d *Dataset) GetProperty(p Prop) (prop Property, err error) {
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -253,10 +268,10 @@ func (d *Dataset) GetProperty(p ZFSProp) (prop Property, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set ZFS dataset property to value. Not all properties can be set,
 | 
			
		||||
// SetProperty set ZFS dataset property to value. Not all properties can be set,
 | 
			
		||||
// some can be set only at creation time and some are read only.
 | 
			
		||||
// Always check if returned error and its description.
 | 
			
		||||
func (d *Dataset) SetProperty(p ZFSProp, value string) (err error) {
 | 
			
		||||
func (d *Dataset) SetProperty(p Prop, value string) (err error) {
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -273,15 +288,15 @@ func (d *Dataset) SetProperty(p ZFSProp, value string) (err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clones the dataset.  The target must be of the same type as
 | 
			
		||||
// Clone - clones the dataset.  The target must be of the same type as
 | 
			
		||||
// the source.
 | 
			
		||||
func (d *Dataset) Clone(target string, props map[ZFSProp]Property) (rd Dataset, err error) {
 | 
			
		||||
func (d *Dataset) Clone(target string, props map[Prop]Property) (rd Dataset, err error) {
 | 
			
		||||
	var cprops *C.nvlist_t
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
 | 
			
		||||
	if cprops, err = datasetPropertiesTonvlist(props); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer C.nvlist_free(cprops)
 | 
			
		||||
| 
						 | 
				
			
			@ -293,14 +308,14 @@ func (d *Dataset) Clone(target string, props map[ZFSProp]Property) (rd Dataset,
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create dataset snapshot. Set recur to true to snapshot child datasets.
 | 
			
		||||
func DatasetSnapshot(path string, recur bool, props map[ZFSProp]Property) (rd Dataset, err error) {
 | 
			
		||||
// DatasetSnapshot create dataset snapshot. Set recur to true to snapshot child datasets.
 | 
			
		||||
func DatasetSnapshot(path string, recur bool, props map[Prop]Property) (rd Dataset, err error) {
 | 
			
		||||
	var cprops *C.nvlist_t
 | 
			
		||||
	if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
 | 
			
		||||
	if cprops, err = datasetPropertiesTonvlist(props); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer C.nvlist_free(cprops)
 | 
			
		||||
	if errc := C.zfs_snapshot(libzfs_handle, C.CString(path), boolean_t(recur), cprops); errc != 0 {
 | 
			
		||||
	if errc := C.zfs_snapshot(libzfsHandle, C.CString(path), booleanT(recur), cprops); errc != 0 {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -308,7 +323,7 @@ func DatasetSnapshot(path string, recur bool, props map[ZFSProp]Property) (rd Da
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return zfs dataset path/name
 | 
			
		||||
// Path return zfs dataset path/name
 | 
			
		||||
func (d *Dataset) Path() (path string, err error) {
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
| 
						 | 
				
			
			@ -319,14 +334,14 @@ func (d *Dataset) Path() (path string, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Rollabck dataset snapshot
 | 
			
		||||
// Rollback rollabck's dataset snapshot
 | 
			
		||||
func (d *Dataset) Rollback(snap *Dataset, force bool) (err error) {
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if errc := C.zfs_rollback(d.list.zh,
 | 
			
		||||
		snap.list.zh, boolean_t(force)); errc != 0 {
 | 
			
		||||
		snap.list.zh, booleanT(force)); errc != 0 {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
| 
						 | 
				
			
			@ -334,20 +349,20 @@ func (d *Dataset) Rollback(snap *Dataset, force bool) (err error) {
 | 
			
		|||
 | 
			
		||||
// Rename dataset
 | 
			
		||||
func (d *Dataset) Rename(newname string, recur,
 | 
			
		||||
	force_umount bool) (err error) {
 | 
			
		||||
	forceUnmount bool) (err error) {
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if errc := C.zfs_rename(d.list.zh, C.CString(newname),
 | 
			
		||||
		boolean_t(recur), boolean_t(force_umount)); errc != 0 {
 | 
			
		||||
		booleanT(recur), booleanT(forceUnmount)); errc != 0 {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Checks to see if the mount is active.  If the filesystem is mounted, fills
 | 
			
		||||
// in 'where' with the current mountpoint, and returns true.  Otherwise,
 | 
			
		||||
// IsMounted checks to see if the mount is active.  If the filesystem is mounted,
 | 
			
		||||
// sets in 'where' argument the current mountpoint, and returns true.  Otherwise,
 | 
			
		||||
// returns false.
 | 
			
		||||
func (d *Dataset) IsMounted() (mounted bool, where string) {
 | 
			
		||||
	var cw *C.char
 | 
			
		||||
| 
						 | 
				
			
			@ -386,7 +401,8 @@ func (d *Dataset) Unmount(flags int) (err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unmount this filesystem and any children inheriting the mountpoint property.
 | 
			
		||||
// UnmountAll unmount this filesystem and any children inheriting the
 | 
			
		||||
// mountpoint property.
 | 
			
		||||
func (d *Dataset) UnmountAll(flags int) (err error) {
 | 
			
		||||
	if d.list == nil {
 | 
			
		||||
		err = errors.New(msgDatasetIsNil)
 | 
			
		||||
| 
						 | 
				
			
			@ -398,12 +414,12 @@ func (d *Dataset) UnmountAll(flags int) (err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert property to name
 | 
			
		||||
// DatasetPropertyToName convert property to name
 | 
			
		||||
// ( returns built in string representation of property name).
 | 
			
		||||
// This is optional, you can represent each property with string
 | 
			
		||||
// name of choice.
 | 
			
		||||
func DatasetPropertyToName(p ZFSProp) (name string) {
 | 
			
		||||
	if p == ZFSNumProps {
 | 
			
		||||
func DatasetPropertyToName(p Prop) (name string) {
 | 
			
		||||
	if p == DatasetNumProps {
 | 
			
		||||
		return "numofprops"
 | 
			
		||||
	}
 | 
			
		||||
	prop := C.zfs_prop_t(p)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										74
									
								
								zfs_test.go
								
								
								
								
							
							
						
						
									
										74
									
								
								zfs_test.go
								
								
								
								
							| 
						 | 
				
			
			@ -2,15 +2,16 @@ package zfs_test
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/bicomsystems/go-libzfs"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/bicomsystems/go-libzfs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/* ------------------------------------------------------------------------- */
 | 
			
		||||
// HELPERS:
 | 
			
		||||
var TST_DATASET_PATH = TST_POOL_NAME + "/DATASET"
 | 
			
		||||
var TST_VOLUME_PATH = TST_DATASET_PATH + "/VOLUME"
 | 
			
		||||
var TST_DATASET_PATH_SNAP = TST_DATASET_PATH + "@test"
 | 
			
		||||
var TSTDatasetPath = TSTPoolName + "/DATASET"
 | 
			
		||||
var TSTVolumePath = TSTDatasetPath + "/VOLUME"
 | 
			
		||||
var TSTDatasetPathSnap = TSTDatasetPath + "@test"
 | 
			
		||||
 | 
			
		||||
func printDatasets(ds []zfs.Dataset) error {
 | 
			
		||||
	for _, d := range ds {
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +20,7 @@ func printDatasets(ds []zfs.Dataset) error {
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		p, err := d.GetProperty(zfs.ZFSPropType)
 | 
			
		||||
		p, err := d.GetProperty(zfs.DatasetPropType)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -36,45 +37,45 @@ func printDatasets(ds []zfs.Dataset) error {
 | 
			
		|||
 | 
			
		||||
func zfsTestDatasetCreate(t *testing.T) {
 | 
			
		||||
	// reinit names used in case TESTPOOL was in conflict
 | 
			
		||||
	TST_DATASET_PATH = TST_POOL_NAME + "/DATASET"
 | 
			
		||||
	TST_VOLUME_PATH = TST_DATASET_PATH + "/VOLUME"
 | 
			
		||||
	TST_DATASET_PATH_SNAP = TST_DATASET_PATH + "@test"
 | 
			
		||||
	TSTDatasetPath = TSTPoolName + "/DATASET"
 | 
			
		||||
	TSTVolumePath = TSTDatasetPath + "/VOLUME"
 | 
			
		||||
	TSTDatasetPathSnap = TSTDatasetPath + "@test"
 | 
			
		||||
 | 
			
		||||
	println("TEST DatasetCreate(", TST_DATASET_PATH, ") (filesystem) ... ")
 | 
			
		||||
	props := make(map[zfs.ZFSProp]zfs.Property)
 | 
			
		||||
	d, err := zfs.DatasetCreate(TST_DATASET_PATH, zfs.DatasetTypeFilesystem, props)
 | 
			
		||||
	println("TEST DatasetCreate(", TSTDatasetPath, ") (filesystem) ... ")
 | 
			
		||||
	props := make(map[zfs.Prop]zfs.Property)
 | 
			
		||||
	d, err := zfs.DatasetCreate(TSTDatasetPath, zfs.DatasetTypeFilesystem, props)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	d.Close()
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
 | 
			
		||||
	strSize := "536870912" // 512M
 | 
			
		||||
 | 
			
		||||
	println("TEST DatasetCreate(", TST_VOLUME_PATH, ") (volume) ... ")
 | 
			
		||||
	props[zfs.ZFSPropVolsize] = zfs.Property{Value: strSize}
 | 
			
		||||
	println("TEST DatasetCreate(", TSTVolumePath, ") (volume) ... ")
 | 
			
		||||
	props[zfs.DatasetPropVolsize] = zfs.Property{Value: strSize}
 | 
			
		||||
	// In addition I explicitly choose some more properties to be set.
 | 
			
		||||
	props[zfs.ZFSPropVolblocksize] = zfs.Property{Value: "4096"}
 | 
			
		||||
	props[zfs.ZFSPropReservation] = zfs.Property{Value: strSize}
 | 
			
		||||
	d, err = zfs.DatasetCreate(TST_VOLUME_PATH, zfs.DatasetTypeVolume, props)
 | 
			
		||||
	props[zfs.DatasetPropVolblocksize] = zfs.Property{Value: "4096"}
 | 
			
		||||
	props[zfs.DatasetPropReservation] = zfs.Property{Value: strSize}
 | 
			
		||||
	d, err = zfs.DatasetCreate(TSTVolumePath, zfs.DatasetTypeVolume, props)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	d.Close()
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zfsTestDatasetOpen(t *testing.T) {
 | 
			
		||||
	println("TEST DatasetOpen(", TST_DATASET_PATH, ") ... ")
 | 
			
		||||
	d, err := zfs.DatasetOpen(TST_DATASET_PATH)
 | 
			
		||||
	println("TEST DatasetOpen(", TSTDatasetPath, ") ... ")
 | 
			
		||||
	d, err := zfs.DatasetOpen(TSTDatasetPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	d.Close()
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zfsTestDatasetOpenAll(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			@ -90,24 +91,24 @@ func zfsTestDatasetOpenAll(t *testing.T) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
	zfs.DatasetCloseAll(ds)
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zfsTestDatasetSnapshot(t *testing.T) {
 | 
			
		||||
	println("TEST DatasetSnapshot(", TST_DATASET_PATH, ", true, ...) ... ")
 | 
			
		||||
	props := make(map[zfs.ZFSProp]zfs.Property)
 | 
			
		||||
	d, err := zfs.DatasetSnapshot(TST_DATASET_PATH_SNAP, true, props)
 | 
			
		||||
	println("TEST DatasetSnapshot(", TSTDatasetPath, ", true, ...) ... ")
 | 
			
		||||
	props := make(map[zfs.Prop]zfs.Property)
 | 
			
		||||
	d, err := zfs.DatasetSnapshot(TSTDatasetPathSnap, true, props)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer d.Close()
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zfsTestDatasetDestroy(t *testing.T) {
 | 
			
		||||
	println("TEST DATASET Destroy( ", TST_DATASET_PATH, " ) ... ")
 | 
			
		||||
	d, err := zfs.DatasetOpen(TST_DATASET_PATH)
 | 
			
		||||
	println("TEST DATASET Destroy( ", TSTDatasetPath, " ) ... ")
 | 
			
		||||
	d, err := zfs.DatasetOpen(TSTDatasetPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +118,7 @@ func zfsTestDatasetDestroy(t *testing.T) {
 | 
			
		|||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ------------------------------------------------------------------------- */
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +129,7 @@ func ExampleDatasetCreate() {
 | 
			
		|||
	// Create map to represent ZFS dataset properties. This is equivalent to
 | 
			
		||||
	// list of properties you can get from ZFS CLI tool, and some more
 | 
			
		||||
	// internally used by libzfs.
 | 
			
		||||
	props := make(map[zfs.ZFSProp]zfs.Property)
 | 
			
		||||
	props := make(map[zfs.Prop]zfs.Property)
 | 
			
		||||
 | 
			
		||||
	// I choose to create (block) volume 1GiB in size. Size is just ZFS dataset
 | 
			
		||||
	// property and this is done as map of strings. So, You have to either
 | 
			
		||||
| 
						 | 
				
			
			@ -136,10 +137,10 @@ func ExampleDatasetCreate() {
 | 
			
		|||
	// similar to convert in to string (base 10) from numeric type.
 | 
			
		||||
	strSize := "1073741824"
 | 
			
		||||
 | 
			
		||||
	props[zfs.ZFSPropVolsize] = zfs.Property{Value: strSize}
 | 
			
		||||
	props[zfs.DatasetPropVolsize] = zfs.Property{Value: strSize}
 | 
			
		||||
	// In addition I explicitly choose some more properties to be set.
 | 
			
		||||
	props[zfs.ZFSPropVolblocksize] = zfs.Property{Value: "4096"}
 | 
			
		||||
	props[zfs.ZFSPropReservation] = zfs.Property{Value: strSize}
 | 
			
		||||
	props[zfs.DatasetPropVolblocksize] = zfs.Property{Value: "4096"}
 | 
			
		||||
	props[zfs.DatasetPropReservation] = zfs.Property{Value: strSize}
 | 
			
		||||
 | 
			
		||||
	// Lets create desired volume
 | 
			
		||||
	d, err := zfs.DatasetCreate("TESTPOOL/VOLUME1", zfs.DatasetTypeVolume, props)
 | 
			
		||||
| 
						 | 
				
			
			@ -161,10 +162,11 @@ func ExampleDatasetOpen() {
 | 
			
		|||
	}
 | 
			
		||||
	defer d.Close()
 | 
			
		||||
	var p zfs.Property
 | 
			
		||||
	if p, err = d.GetProperty(zfs.ZFSPropAvailable); err != nil {
 | 
			
		||||
	if p, err = d.GetProperty(zfs.DatasetPropAvailable); err != nil {
 | 
			
		||||
		panic(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	println(zfs.DatasetPropertyToName(zfs.ZFSPropAvailable), " = ", p.Value)
 | 
			
		||||
	println(zfs.DatasetPropertyToName(zfs.DatasetPropAvailable), " = ",
 | 
			
		||||
		p.Value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ExampleDatasetOpenAll() {
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +182,7 @@ func ExampleDatasetOpenAll() {
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			panic(err.Error())
 | 
			
		||||
		}
 | 
			
		||||
		p, err := d.GetProperty(zfs.ZFSPropType)
 | 
			
		||||
		p, err := d.GetProperty(zfs.DatasetPropType)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err.Error())
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										123
									
								
								zpool.go
								
								
								
								
							
							
						
						
									
										123
									
								
								zpool.go
								
								
								
								
							| 
						 | 
				
			
			@ -10,16 +10,17 @@ import (
 | 
			
		|||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	msgPoolIsNil = "Pool handle not initialized or its closed"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PoolProperties map[PoolProp]string
 | 
			
		||||
type ZFSProperties map[ZFSProp]string
 | 
			
		||||
// PoolProperties type is map of pool properties name -> value
 | 
			
		||||
type PoolProperties map[Prop]string
 | 
			
		||||
 | 
			
		||||
// Object represents handler to single ZFS pool
 | 
			
		||||
// Pool object represents handler to single ZFS pool
 | 
			
		||||
//
 | 
			
		||||
/* Pool.Properties map[string]Property
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -34,11 +35,11 @@ type Pool struct {
 | 
			
		|||
	Features   map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Open ZFS pool handler by name.
 | 
			
		||||
// PoolOpen open ZFS pool handler by name.
 | 
			
		||||
// Returns Pool object, requires Pool.Close() to be called explicitly
 | 
			
		||||
// for memory cleanup after object is not needed anymore.
 | 
			
		||||
func PoolOpen(name string) (pool Pool, err error) {
 | 
			
		||||
	pool.list = C.zpool_list_open(libzfs_handle, C.CString(name))
 | 
			
		||||
	pool.list = C.zpool_list_open(libzfsHandle, C.CString(name))
 | 
			
		||||
	if pool.list != nil {
 | 
			
		||||
		err = pool.ReloadProperties()
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +48,7 @@ func PoolOpen(name string) (pool Pool, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given a list of directories to search, find and import pool with matching
 | 
			
		||||
// PoolImport given a list of directories to search, find and import pool with matching
 | 
			
		||||
// name stored on disk.
 | 
			
		||||
func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
 | 
			
		||||
	errPoolList := errors.New("Failed to list pools")
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +60,7 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
 | 
			
		|||
		C.strings_setat(cpaths, C.int(i), C.CString(path))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pools := C.zpool_find_import(libzfs_handle, C.int(numofp), cpaths)
 | 
			
		||||
	pools := C.zpool_find_import(libzfsHandle, C.int(numofp), cpaths)
 | 
			
		||||
	defer C.nvlist_free(pools)
 | 
			
		||||
 | 
			
		||||
	elem = C.nvlist_next_nvpair(pools, elem)
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +89,7 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	retcode := C.zpool_import(libzfs_handle, config, C.CString(name), nil)
 | 
			
		||||
	retcode := C.zpool_import(libzfsHandle, config, C.CString(name), nil)
 | 
			
		||||
	if retcode != 0 {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -97,12 +98,12 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Open all active ZFS pools on current system.
 | 
			
		||||
// PoolOpenAll open all active ZFS pools on current system.
 | 
			
		||||
// Returns array of Pool handlers, each have to be closed after not needed
 | 
			
		||||
// anymore. Call Pool.Close() method.
 | 
			
		||||
func PoolOpenAll() (pools []Pool, err error) {
 | 
			
		||||
	var pool Pool
 | 
			
		||||
	errcode := C.zpool_list(libzfs_handle, &pool.list)
 | 
			
		||||
	errcode := C.zpool_list(libzfsHandle, &pool.list)
 | 
			
		||||
	for pool.list != nil {
 | 
			
		||||
		err = pool.ReloadProperties()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,17 +118,18 @@ func PoolOpenAll() (pools []Pool, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PoolCloseAll close all pools in given slice
 | 
			
		||||
func PoolCloseAll(pools []Pool) {
 | 
			
		||||
	for _, p := range pools {
 | 
			
		||||
		p.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert property to name
 | 
			
		||||
// PoolPropertyToName convert property to name
 | 
			
		||||
// ( returns built in string representation of property name).
 | 
			
		||||
// This is optional, you can represent each property with string
 | 
			
		||||
// name of choice.
 | 
			
		||||
func PoolPropertyToName(p PoolProp) (name string) {
 | 
			
		||||
func PoolPropertyToName(p Prop) (name string) {
 | 
			
		||||
	if p == PoolNumProps {
 | 
			
		||||
		return "numofprops"
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -136,15 +138,15 @@ func PoolPropertyToName(p PoolProp) (name string) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//  Map POOL STATE to string.
 | 
			
		||||
// PoolStateToName maps POOL STATE to string.
 | 
			
		||||
func PoolStateToName(state PoolState) (name string) {
 | 
			
		||||
	ps := C.pool_state_t(state)
 | 
			
		||||
	name = C.GoString(C.zpool_pool_state_to_name(ps))
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Re-read ZFS pool properties and features, refresh Pool.Properties and
 | 
			
		||||
// Pool.Features map
 | 
			
		||||
// ReloadProperties re-read ZFS pool properties and features, refresh
 | 
			
		||||
// Pool.Properties and Pool.Features map
 | 
			
		||||
func (pool *Pool) ReloadProperties() (err error) {
 | 
			
		||||
	propList := C.read_zpool_properties(pool.list.zph)
 | 
			
		||||
	if propList == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -165,15 +167,15 @@ func (pool *Pool) ReloadProperties() (err error) {
 | 
			
		|||
		"async_destroy": "disabled",
 | 
			
		||||
		"empty_bpobj":   "disabled",
 | 
			
		||||
		"lz4_compress":  "disabled"}
 | 
			
		||||
	for name, _ := range pool.Features {
 | 
			
		||||
	for name := range pool.Features {
 | 
			
		||||
		pool.GetFeature(name)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reload and return single specified property. This also reloads requested
 | 
			
		||||
// GetProperty reload and return single specified property. This also reloads requested
 | 
			
		||||
// property in Properties map.
 | 
			
		||||
func (pool *Pool) GetProperty(p PoolProp) (prop Property, err error) {
 | 
			
		||||
func (pool *Pool) GetProperty(p Prop) (prop Property, err error) {
 | 
			
		||||
	if pool.list != nil {
 | 
			
		||||
		// First check if property exist at all
 | 
			
		||||
		if p < PoolPropName || p > PoolNumProps {
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +196,7 @@ func (pool *Pool) GetProperty(p PoolProp) (prop Property, err error) {
 | 
			
		|||
	return prop, errors.New(msgPoolIsNil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reload and return single specified feature. This also reloads requested
 | 
			
		||||
// GetFeature reload and return single specified feature. This also reloads requested
 | 
			
		||||
// feature in Features map.
 | 
			
		||||
func (pool *Pool) GetFeature(name string) (value string, err error) {
 | 
			
		||||
	var fvalue [512]C.char
 | 
			
		||||
| 
						 | 
				
			
			@ -209,10 +211,10 @@ func (pool *Pool) GetFeature(name string) (value string, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set ZFS pool property to value. Not all properties can be set,
 | 
			
		||||
// SetProperty set ZFS pool property to value. Not all properties can be set,
 | 
			
		||||
// some can be set only at creation time and some are read only.
 | 
			
		||||
// Always check if returned error and its description.
 | 
			
		||||
func (pool *Pool) SetProperty(p PoolProp, value string) (err error) {
 | 
			
		||||
func (pool *Pool) SetProperty(p Prop, value string) (err error) {
 | 
			
		||||
	if pool.list != nil {
 | 
			
		||||
		// First check if property exist at all
 | 
			
		||||
		if p < PoolPropName || p > PoolNumProps {
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +243,7 @@ func (pool *Pool) Close() {
 | 
			
		|||
	pool.list = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get (re-read) ZFS pool name property
 | 
			
		||||
// Name get (re-read) ZFS pool name property
 | 
			
		||||
func (pool *Pool) Name() (name string, err error) {
 | 
			
		||||
	if pool.list == nil {
 | 
			
		||||
		err = errors.New(msgPoolIsNil)
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +254,7 @@ func (pool *Pool) Name() (name string, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get ZFS pool state
 | 
			
		||||
// State get ZFS pool state
 | 
			
		||||
// Return the state of the pool (ACTIVE or UNAVAILABLE)
 | 
			
		||||
func (pool *Pool) State() (state PoolState, err error) {
 | 
			
		||||
	if pool.list == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -263,7 +265,7 @@ func (pool *Pool) State() (state PoolState, err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ZFS virtual device specification
 | 
			
		||||
// VDevSpec ZFS virtual device specification
 | 
			
		||||
type VDevSpec struct {
 | 
			
		||||
	Type    VDevType
 | 
			
		||||
	Devices []VDevSpec // groups other devices (e.g. mirror)
 | 
			
		||||
| 
						 | 
				
			
			@ -271,31 +273,31 @@ type VDevSpec struct {
 | 
			
		|||
	Path    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VDevSpec) isGrouping() (grouping bool, mindevs, maxdevs int) {
 | 
			
		||||
func (vdev *VDevSpec) isGrouping() (grouping bool, mindevs, maxdevs int) {
 | 
			
		||||
	maxdevs = int(^uint(0) >> 1)
 | 
			
		||||
	if self.Type == VDevTypeRaidz {
 | 
			
		||||
	if vdev.Type == VDevTypeRaidz {
 | 
			
		||||
		grouping = true
 | 
			
		||||
		if self.Parity == 0 {
 | 
			
		||||
			self.Parity = 1
 | 
			
		||||
		if vdev.Parity == 0 {
 | 
			
		||||
			vdev.Parity = 1
 | 
			
		||||
		}
 | 
			
		||||
		if self.Parity > 254 {
 | 
			
		||||
			self.Parity = 254
 | 
			
		||||
		if vdev.Parity > 254 {
 | 
			
		||||
			vdev.Parity = 254
 | 
			
		||||
		}
 | 
			
		||||
		mindevs = int(self.Parity) + 1
 | 
			
		||||
		mindevs = int(vdev.Parity) + 1
 | 
			
		||||
		maxdevs = 255
 | 
			
		||||
	} else if self.Type == VDevTypeMirror {
 | 
			
		||||
	} else if vdev.Type == VDevTypeMirror {
 | 
			
		||||
		grouping = true
 | 
			
		||||
		mindevs = 2
 | 
			
		||||
	} else if self.Type == VDevTypeLog || self.Type == VDevTypeSpare || self.Type == VDevTypeL2cache {
 | 
			
		||||
	} else if vdev.Type == VDevTypeLog || vdev.Type == VDevTypeSpare || vdev.Type == VDevTypeL2cache {
 | 
			
		||||
		grouping = true
 | 
			
		||||
		mindevs = 1
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VDevSpec) isLog() (r C.uint64_t) {
 | 
			
		||||
func (vdev *VDevSpec) isLog() (r C.uint64_t) {
 | 
			
		||||
	r = 0
 | 
			
		||||
	if self.Type == VDevTypeLog {
 | 
			
		||||
	if vdev.Type == VDevTypeLog {
 | 
			
		||||
		r = 1
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
| 
						 | 
				
			
			@ -317,7 +319,7 @@ func toCPoolProperties(props PoolProperties) (cprops *C.nvlist_t) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toCZFSProperties(props ZFSProperties) (cprops *C.nvlist_t) {
 | 
			
		||||
func toCDatasetProperties(props DatasetProperties) (cprops *C.nvlist_t) {
 | 
			
		||||
	cprops = nil
 | 
			
		||||
	for prop, value := range props {
 | 
			
		||||
		name := C.zfs_prop_to_name(C.zfs_prop_t(prop))
 | 
			
		||||
| 
						 | 
				
			
			@ -361,7 +363,7 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec,
 | 
			
		|||
	defer C.nvlist_free_array(l2cache)
 | 
			
		||||
	for i, vdev := range vdevs {
 | 
			
		||||
		grouping, mindevs, maxdevs := vdev.isGrouping()
 | 
			
		||||
		var child *C.nvlist_t = nil
 | 
			
		||||
		var child *C.nvlist_t
 | 
			
		||||
		// fmt.Println(vdev.Type)
 | 
			
		||||
		if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 {
 | 
			
		||||
			err = errors.New("Failed to allocate vdev")
 | 
			
		||||
| 
						 | 
				
			
			@ -369,8 +371,9 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec,
 | 
			
		|||
		}
 | 
			
		||||
		vcount := len(vdev.Devices)
 | 
			
		||||
		if vcount < mindevs || vcount > maxdevs {
 | 
			
		||||
			err = errors.New(fmt.Sprintf(
 | 
			
		||||
				"Invalid vdev specification: %s supports no less than %d or more than %d devices", vdev.Type, mindevs, maxdevs))
 | 
			
		||||
			err = fmt.Errorf(
 | 
			
		||||
				"Invalid vdev specification: %s supports no less than %d or more than %d devices",
 | 
			
		||||
				vdev.Type, mindevs, maxdevs)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if r := C.nvlist_add_string(child, C.CString(C.ZPOOL_CONFIG_TYPE),
 | 
			
		||||
| 
						 | 
				
			
			@ -466,11 +469,11 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec,
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create ZFS pool per specs, features and properties of pool and root dataset
 | 
			
		||||
// PoolCreate create ZFS pool per specs, features and properties of pool and root dataset
 | 
			
		||||
func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
 | 
			
		||||
	props PoolProperties, fsprops ZFSProperties) (pool Pool, err error) {
 | 
			
		||||
	props PoolProperties, fsprops DatasetProperties) (pool Pool, err error) {
 | 
			
		||||
	// create root vdev nvroot
 | 
			
		||||
	var nvroot *C.nvlist_t = nil
 | 
			
		||||
	var nvroot *C.nvlist_t
 | 
			
		||||
	if r := C.nvlist_alloc(&nvroot, C.NV_UNIQUE_NAME, 0); r != 0 {
 | 
			
		||||
		err = errors.New("Failed to allocate root vdev")
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -495,7 +498,7 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
 | 
			
		|||
		err = errors.New("Failed to allocate pool properties")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	cfsprops := toCZFSProperties(fsprops)
 | 
			
		||||
	cfsprops := toCDatasetProperties(fsprops)
 | 
			
		||||
	if cfsprops != nil {
 | 
			
		||||
		defer C.nvlist_free(cfsprops)
 | 
			
		||||
	} else if len(fsprops) > 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -516,16 +519,34 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Create actual pool then open
 | 
			
		||||
	if r := C.zpool_create(libzfs_handle, C.CString(name), nvroot,
 | 
			
		||||
	if r := C.zpool_create(libzfsHandle, C.CString(name), nvroot,
 | 
			
		||||
		cprops, cfsprops); r != 0 {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
		err = errors.New(err.Error() + " (zpool_create)")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	pool, err = PoolOpen(name)
 | 
			
		||||
 | 
			
		||||
	// It can happen that pool is not immediately available,
 | 
			
		||||
	// we know we just created it with success so lets wait and retry
 | 
			
		||||
	// but only in case EZFS_NOENT error
 | 
			
		||||
	retr := 0
 | 
			
		||||
	for pool, err = PoolOpen(name); err != nil && retr < 3; retr++ {
 | 
			
		||||
		errno := C.libzfs_errno(libzfsHandle)
 | 
			
		||||
		if errno == ENoent {
 | 
			
		||||
			time.Sleep(500 * time.Millisecond)
 | 
			
		||||
		} else {
 | 
			
		||||
			err = errors.New(err.Error() + " (PoolOpen)")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		pool, err = PoolOpen(name)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = errors.New(err.Error() + " (PoolOpen)")
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get pool status. Let you check if pool healthy.
 | 
			
		||||
// Status get pool status. Let you check if pool healthy.
 | 
			
		||||
func (pool *Pool) Status() (status PoolStatus, err error) {
 | 
			
		||||
	var msgid *C.char
 | 
			
		||||
	var reason C.zpool_status_t
 | 
			
		||||
| 
						 | 
				
			
			@ -554,22 +575,22 @@ func (pool *Pool) Destroy(logStr string) (err error) {
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exports the pool from the system.
 | 
			
		||||
// Export exports the pool from the system.
 | 
			
		||||
// Before exporting the pool, all datasets within the pool are unmounted.
 | 
			
		||||
// A pool can not be exported if it has a shared spare that is currently
 | 
			
		||||
// being used.
 | 
			
		||||
func (pool *Pool) Export(force bool, log string) (err error) {
 | 
			
		||||
	var force_t C.boolean_t = 0
 | 
			
		||||
	var forcet C.boolean_t
 | 
			
		||||
	if force {
 | 
			
		||||
		force_t = 1
 | 
			
		||||
		forcet = 1
 | 
			
		||||
	}
 | 
			
		||||
	if rc := C.zpool_export(pool.list.zph, force_t, C.CString(log)); rc != 0 {
 | 
			
		||||
	if rc := C.zpool_export(pool.list.zph, forcet, C.CString(log)); rc != 0 {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Hard force
 | 
			
		||||
// ExportForce hard force export of the pool from the system.
 | 
			
		||||
func (pool *Pool) ExportForce(log string) (err error) {
 | 
			
		||||
	if rc := C.zpool_export_force(pool.list.zph, C.CString(log)); rc != 0 {
 | 
			
		||||
		err = LastError()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,16 +2,17 @@ package zfs_test
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/bicomsystems/go-libzfs"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/bicomsystems/go-libzfs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/* ------------------------------------------------------------------------- */
 | 
			
		||||
// HELPERS:
 | 
			
		||||
 | 
			
		||||
var TST_POOL_NAME = "TESTPOOL"
 | 
			
		||||
var TSTPoolName = "TESTPOOL"
 | 
			
		||||
 | 
			
		||||
func CreateTmpSparse(prefix string, size int64) (path string, err error) {
 | 
			
		||||
	sf, err := ioutil.TempFile("/tmp", prefix)
 | 
			
		||||
| 
						 | 
				
			
			@ -66,12 +67,12 @@ func zpoolTestPoolCreate(t *testing.T) {
 | 
			
		|||
	// first check if pool with same name already exist
 | 
			
		||||
	// we don't want conflict
 | 
			
		||||
	for {
 | 
			
		||||
		p, err := zfs.PoolOpen(TST_POOL_NAME)
 | 
			
		||||
		p, err := zfs.PoolOpen(TSTPoolName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		p.Close()
 | 
			
		||||
		TST_POOL_NAME += "0"
 | 
			
		||||
		TSTPoolName += "0"
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,15 +95,15 @@ func zpoolTestPoolCreate(t *testing.T) {
 | 
			
		|||
		zfs.VDevSpec{Type: zfs.VDevTypeSpare, Devices: sdevs},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	props := make(map[zfs.PoolProp]string)
 | 
			
		||||
	fsprops := make(map[zfs.ZFSProp]string)
 | 
			
		||||
	props := make(map[zfs.Prop]string)
 | 
			
		||||
	fsprops := make(map[zfs.Prop]string)
 | 
			
		||||
	features := make(map[string]string)
 | 
			
		||||
	fsprops[zfs.ZFSPropMountpoint] = "none"
 | 
			
		||||
	fsprops[zfs.DatasetPropMountpoint] = "none"
 | 
			
		||||
	features["async_destroy"] = "enabled"
 | 
			
		||||
	features["empty_bpobj"] = "enabled"
 | 
			
		||||
	features["lz4_compress"] = "enabled"
 | 
			
		||||
 | 
			
		||||
	pool, err := zfs.PoolCreate(TST_POOL_NAME, vdevs, features, props, fsprops)
 | 
			
		||||
	pool, err := zfs.PoolCreate(TSTPoolName, vdevs, features, props, fsprops)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		// try cleanup
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +114,7 @@ func zpoolTestPoolCreate(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
	defer pool.Close()
 | 
			
		||||
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Open and list all pools and them state on the system
 | 
			
		||||
| 
						 | 
				
			
			@ -143,22 +144,22 @@ func zpoolTestPoolOpenAll(t *testing.T) {
 | 
			
		|||
		println("\tPool: ", pname, " state: ", pstate)
 | 
			
		||||
		p.Close()
 | 
			
		||||
	}
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zpoolTestPoolDestroy(t *testing.T) {
 | 
			
		||||
	println("TEST POOL Destroy( ", TST_POOL_NAME, " ) ... ")
 | 
			
		||||
	p, err := zfs.PoolOpen(TST_POOL_NAME)
 | 
			
		||||
	println("TEST POOL Destroy( ", TSTPoolName, " ) ... ")
 | 
			
		||||
	p, err := zfs.PoolOpen(TSTPoolName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer p.Close()
 | 
			
		||||
	if err = p.Destroy("Test of pool destroy (" + TST_POOL_NAME + ")"); err != nil {
 | 
			
		||||
	if err = p.Destroy("Test of pool destroy (" + TSTPoolName + ")"); err != nil {
 | 
			
		||||
		t.Error(err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zpoolTestFailPoolOpen(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			@ -166,7 +167,7 @@ func zpoolTestFailPoolOpen(t *testing.T) {
 | 
			
		|||
	pname := "fail to open this pool"
 | 
			
		||||
	p, err := zfs.PoolOpen(pname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		println("PASS\n")
 | 
			
		||||
		print("PASS\n\n")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	t.Error("PoolOpen pass when it should fail")
 | 
			
		||||
| 
						 | 
				
			
			@ -174,43 +175,43 @@ func zpoolTestFailPoolOpen(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func zpoolTestExport(t *testing.T) {
 | 
			
		||||
	println("TEST POOL Export( ", TST_POOL_NAME, " ) ... ")
 | 
			
		||||
	p, err := zfs.PoolOpen(TST_POOL_NAME)
 | 
			
		||||
	println("TEST POOL Export( ", TSTPoolName, " ) ... ")
 | 
			
		||||
	p, err := zfs.PoolOpen(TSTPoolName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	p.Export(false, "Test exporting pool")
 | 
			
		||||
	defer p.Close()
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zpoolTestExportForce(t *testing.T) {
 | 
			
		||||
	println("TEST POOL ExportForce( ", TST_POOL_NAME, " ) ... ")
 | 
			
		||||
	p, err := zfs.PoolOpen(TST_POOL_NAME)
 | 
			
		||||
	println("TEST POOL ExportForce( ", TSTPoolName, " ) ... ")
 | 
			
		||||
	p, err := zfs.PoolOpen(TSTPoolName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	p.ExportForce("Test force exporting pool")
 | 
			
		||||
	defer p.Close()
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zpoolTestImport(t *testing.T) {
 | 
			
		||||
	println("TEST POOL Import( ", TST_POOL_NAME, " ) ... ")
 | 
			
		||||
	p, err := zfs.PoolImport(TST_POOL_NAME, []string{"/tmp"})
 | 
			
		||||
	println("TEST POOL Import( ", TSTPoolName, " ) ... ")
 | 
			
		||||
	p, err := zfs.PoolImport(TSTPoolName, []string{"/tmp"})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer p.Close()
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zpoolTestPoolProp(t *testing.T) {
 | 
			
		||||
	println("TEST PoolProp on ", TST_POOL_NAME, " ... ")
 | 
			
		||||
	if pool, err := zfs.PoolOpen(TST_POOL_NAME); err == nil {
 | 
			
		||||
	println("TEST PoolProp on ", TSTPoolName, " ... ")
 | 
			
		||||
	if pool, err := zfs.PoolOpen(TSTPoolName); err == nil {
 | 
			
		||||
		defer pool.Close()
 | 
			
		||||
		// Turn on snapshot listing for pool
 | 
			
		||||
		pool.SetProperty(zfs.PoolPropListsnaps, "on")
 | 
			
		||||
| 
						 | 
				
			
			@ -247,12 +248,12 @@ func zpoolTestPoolProp(t *testing.T) {
 | 
			
		|||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func zpoolTestPoolStatusAndState(t *testing.T) {
 | 
			
		||||
	println("TEST pool Status/State ( ", TST_POOL_NAME, " ) ... ")
 | 
			
		||||
	pool, err := zfs.PoolOpen(TST_POOL_NAME)
 | 
			
		||||
	println("TEST pool Status/State ( ", TSTPoolName, " ) ... ")
 | 
			
		||||
	pool, err := zfs.PoolOpen(TSTPoolName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err.Error())
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -269,9 +270,9 @@ func zpoolTestPoolStatusAndState(t *testing.T) {
 | 
			
		|||
		t.Error(err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	println("POOL", TST_POOL_NAME, "state:", zfs.PoolStateToName(pstate))
 | 
			
		||||
	println("POOL", TSTPoolName, "state:", zfs.PoolStateToName(pstate))
 | 
			
		||||
 | 
			
		||||
	println("PASS\n")
 | 
			
		||||
	print("PASS\n\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ------------------------------------------------------------------------- */
 | 
			
		||||
| 
						 | 
				
			
			@ -315,7 +316,7 @@ func ExamplePoolOpenAll() {
 | 
			
		|||
 | 
			
		||||
		// Iterate pool properties and print name, value and source
 | 
			
		||||
		for key, prop := range p.Properties {
 | 
			
		||||
			pkey := zfs.PoolProp(key)
 | 
			
		||||
			pkey := zfs.Prop(key)
 | 
			
		||||
			if pkey == zfs.PoolPropName {
 | 
			
		||||
				continue // Skip name its already printed above
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -353,14 +354,14 @@ func ExamplePoolCreate() {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// pool properties
 | 
			
		||||
	props := make(map[zfs.PoolProp]string)
 | 
			
		||||
	props := make(map[zfs.Prop]string)
 | 
			
		||||
	// root dataset filesystem properties
 | 
			
		||||
	fsprops := make(map[zfs.ZFSProp]string)
 | 
			
		||||
	fsprops := make(map[zfs.Prop]string)
 | 
			
		||||
	// pool features
 | 
			
		||||
	features := make(map[string]string)
 | 
			
		||||
 | 
			
		||||
	// Turn off auto mounting by ZFS
 | 
			
		||||
	fsprops[zfs.ZFSPropMountpoint] = "none"
 | 
			
		||||
	fsprops[zfs.DatasetPropMountpoint] = "none"
 | 
			
		||||
 | 
			
		||||
	// Enable some features
 | 
			
		||||
	features["async_destroy"] = "enabled"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue