- ZFS send/receive
This commit is contained in:
parent
8fd0833477
commit
1b47551b87
|
@ -22,6 +22,7 @@ import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VDevType type of device in the pool
|
// VDevType type of device in the pool
|
||||||
|
@ -68,6 +69,10 @@ type Property struct {
|
||||||
Source string
|
Source string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Global struct {
|
||||||
|
Mtx sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
// Pool status
|
// Pool status
|
||||||
const (
|
const (
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
package zfs
|
||||||
|
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include <libzfs.h>
|
||||||
|
// #include "common.h"
|
||||||
|
// #include "zpool.h"
|
||||||
|
// #include "zfs.h"
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SendFlags struct {
|
||||||
|
Verbose bool
|
||||||
|
Replicate bool
|
||||||
|
DoAll bool
|
||||||
|
FromOrigin bool
|
||||||
|
Dedup bool
|
||||||
|
Props bool
|
||||||
|
DryRun bool
|
||||||
|
// Parsable bool
|
||||||
|
// Progress bool
|
||||||
|
LargeBlock bool
|
||||||
|
EmbedData bool
|
||||||
|
// Compress bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecvFlags struct {
|
||||||
|
Verbose bool
|
||||||
|
IsPrefix bool
|
||||||
|
IsTail bool
|
||||||
|
DryRun bool
|
||||||
|
Force bool
|
||||||
|
CanmountOff bool
|
||||||
|
Resumable bool
|
||||||
|
ByteSwap bool
|
||||||
|
NoMount bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func to_boolean_t(a bool) C.boolean_t {
|
||||||
|
if a {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func to_sendflags_t(flags *SendFlags) (cflags *C.sendflags_t) {
|
||||||
|
cflags = C.alloc_sendflags()
|
||||||
|
cflags.verbose = to_boolean_t(flags.Verbose)
|
||||||
|
cflags.replicate = to_boolean_t(flags.Replicate)
|
||||||
|
cflags.doall = to_boolean_t(flags.DoAll)
|
||||||
|
cflags.fromorigin = to_boolean_t(flags.FromOrigin)
|
||||||
|
cflags.dedup = to_boolean_t(flags.Dedup)
|
||||||
|
cflags.props = to_boolean_t(flags.Props)
|
||||||
|
cflags.dryrun = to_boolean_t(flags.DryRun)
|
||||||
|
// cflags.parsable = to_boolean_t(flags.Parsable)
|
||||||
|
// cflags.progress = to_boolean_t(flags.Progress)
|
||||||
|
cflags.largeblock = to_boolean_t(flags.LargeBlock)
|
||||||
|
cflags.embed_data = to_boolean_t(flags.EmbedData)
|
||||||
|
// cflags.compress = to_boolean_t(flags.Compress)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func to_recvflags_t(flags *RecvFlags) (cflags *C.recvflags_t) {
|
||||||
|
cflags = C.alloc_recvflags()
|
||||||
|
cflags.verbose = to_boolean_t(flags.Verbose)
|
||||||
|
cflags.isprefix = to_boolean_t(flags.IsPrefix)
|
||||||
|
cflags.istail = to_boolean_t(flags.IsTail)
|
||||||
|
cflags.dryrun = to_boolean_t(flags.DryRun)
|
||||||
|
cflags.force = to_boolean_t(flags.Force)
|
||||||
|
cflags.canmountoff = to_boolean_t(flags.CanmountOff)
|
||||||
|
// cflags.resumable = to_boolean_t(flags.Resumable)
|
||||||
|
cflags.byteswap = to_boolean_t(flags.ByteSwap)
|
||||||
|
cflags.nomount = to_boolean_t(flags.NoMount)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dataset) send(FromName string, outf *os.File, flags *SendFlags) (err error) {
|
||||||
|
var cfromname, ctoname *C.char
|
||||||
|
var dpath string
|
||||||
|
var pd Dataset
|
||||||
|
|
||||||
|
if d.Type != DatasetTypeSnapshot || (len(FromName) > 0 && strings.Contains(FromName, "#")) {
|
||||||
|
err = fmt.Errorf(
|
||||||
|
"Unsupported method on filesystem or bookmark. Use func SendOne() for that purpose.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cflags := to_sendflags_t(flags)
|
||||||
|
defer C.free(unsafe.Pointer(cflags))
|
||||||
|
if dpath, err = d.Path(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(FromName) > 0 {
|
||||||
|
if FromName[0] == '#' || FromName[0] == '@' {
|
||||||
|
FromName = dpath + FromName
|
||||||
|
}
|
||||||
|
cfromname = C.CString(FromName)
|
||||||
|
defer C.free(unsafe.Pointer(cfromname))
|
||||||
|
}
|
||||||
|
sendparams := strings.Split(dpath, "@")
|
||||||
|
parent := sendparams[0]
|
||||||
|
ctoname = C.CString(sendparams[1])
|
||||||
|
defer C.free(unsafe.Pointer(ctoname))
|
||||||
|
if pd, err = DatasetOpen(parent); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer pd.Close()
|
||||||
|
cerr := C.zfs_send(pd.list.zh, cfromname, ctoname, cflags, C.int(outf.Fd()), nil, nil, nil)
|
||||||
|
if cerr != 0 {
|
||||||
|
err = LastError()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dataset) SendOne(FromName string, outf *os.File, flags *SendFlags) (err error) {
|
||||||
|
var cfromname, ctoname *C.char
|
||||||
|
var dpath string
|
||||||
|
var lzc_send_flags uint32
|
||||||
|
|
||||||
|
if d.Type == DatasetTypeSnapshot || (len(FromName) > 0 && !strings.Contains(FromName, "#")) {
|
||||||
|
err = fmt.Errorf(
|
||||||
|
"Unsupported with snapshot. Use func Send() for that purpose.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if flags.Replicate || flags.DoAll || flags.Props || flags.Dedup || flags.DryRun {
|
||||||
|
err = fmt.Errorf("Unsupported flag with filesystem or bookmark.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags.LargeBlock {
|
||||||
|
lzc_send_flags |= C.LZC_SEND_FLAG_LARGE_BLOCK
|
||||||
|
}
|
||||||
|
if flags.EmbedData {
|
||||||
|
lzc_send_flags |= C.LZC_SEND_FLAG_EMBED_DATA
|
||||||
|
}
|
||||||
|
// if (flags.Compress)
|
||||||
|
// lzc_send_flags |= LZC_SEND_FLAG_COMPRESS;
|
||||||
|
if dpath, err = d.Path(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(FromName) > 0 {
|
||||||
|
if FromName[0] == '#' || FromName[0] == '@' {
|
||||||
|
FromName = dpath + FromName
|
||||||
|
}
|
||||||
|
cfromname = C.CString(FromName)
|
||||||
|
defer C.free(unsafe.Pointer(cfromname))
|
||||||
|
}
|
||||||
|
ctoname = C.CString(path.Base(dpath))
|
||||||
|
defer C.free(unsafe.Pointer(ctoname))
|
||||||
|
cerr := C.zfs_send_one(d.list.zh, cfromname, C.int(outf.Fd()), lzc_send_flags)
|
||||||
|
if cerr != 0 {
|
||||||
|
err = LastError()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dataset) Send(outf *os.File, flags SendFlags) (err error) {
|
||||||
|
if flags.Replicate {
|
||||||
|
flags.DoAll = true
|
||||||
|
}
|
||||||
|
err = d.send("", outf, &flags)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dataset) SendFrom(FromName string, outf *os.File, flags SendFlags) (err error) {
|
||||||
|
var porigin Property
|
||||||
|
var from, dest []string
|
||||||
|
if err = d.ReloadProperties(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
porigin, _ = d.GetProperty(DatasetPropOrigin)
|
||||||
|
if len(porigin.Value) > 0 && porigin.Value == FromName {
|
||||||
|
FromName = ""
|
||||||
|
flags.FromOrigin = true
|
||||||
|
} else {
|
||||||
|
var dpath string
|
||||||
|
if dpath, err = d.Path(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dest = strings.Split(dpath, "@")
|
||||||
|
from = strings.Split(FromName, "@")
|
||||||
|
|
||||||
|
if len(from[0]) > 0 && from[0] != dest[0] {
|
||||||
|
err = fmt.Errorf("Incremental source must be in same filesystem.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(from) < 2 || strings.Contains(from[1], "@") || strings.Contains(from[1], "/") {
|
||||||
|
err = fmt.Errorf("Invalid incremental source.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = d.send(from[1], outf, &flags)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dataset) SendSize(FromName string, flags SendFlags) (size uint64, err error) {
|
||||||
|
var porigin Property
|
||||||
|
var from Dataset
|
||||||
|
var dpath string
|
||||||
|
if dpath, err = d.Path(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
zc := C.new_zfs_cmd()
|
||||||
|
defer C.free(unsafe.Pointer(zc))
|
||||||
|
dpath = strings.Split(dpath, "@")[0]
|
||||||
|
if len(FromName) > 0 {
|
||||||
|
|
||||||
|
if FromName[0] == '#' || FromName[0] == '@' {
|
||||||
|
FromName = dpath + FromName
|
||||||
|
}
|
||||||
|
porigin, _ = d.GetProperty(DatasetPropOrigin)
|
||||||
|
if len(porigin.Value) > 0 && porigin.Value == FromName {
|
||||||
|
FromName = ""
|
||||||
|
flags.FromOrigin = true
|
||||||
|
}
|
||||||
|
if from, err = DatasetOpen(FromName); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
zc.zc_fromobj = C.zfs_prop_get_int(from.list.zh, C.ZFS_PROP_OBJSETID)
|
||||||
|
from.Close()
|
||||||
|
} else {
|
||||||
|
zc.zc_fromobj = 0
|
||||||
|
}
|
||||||
|
zc.zc_obj = C.uint64_t(to_boolean_t(flags.FromOrigin))
|
||||||
|
zc.zc_sendobj = C.zfs_prop_get_int(d.list.zh, C.ZFS_PROP_OBJSETID)
|
||||||
|
zc.zc_guid = 1
|
||||||
|
zc.zc_flags = 0
|
||||||
|
if flags.LargeBlock {
|
||||||
|
zc.zc_flags |= C.LZC_SEND_FLAG_LARGE_BLOCK
|
||||||
|
}
|
||||||
|
if flags.EmbedData {
|
||||||
|
zc.zc_flags |= C.LZC_SEND_FLAG_EMBED_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
// C.estimate_ioctl(d.list.zhp, prevsnap_obj, to_boolean_t(flags.FromOrigin), lzc_send_flags, unsafe.Pointer(&size))
|
||||||
|
if ec, e := C.estimate_send_size(zc); ec != 0 {
|
||||||
|
err = fmt.Errorf("Failed to estimate send size. %s %d", e.Error(), e.(syscall.Errno))
|
||||||
|
}
|
||||||
|
size = uint64(zc.zc_objset_type)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dataset) Receive(name string, inf *os.File, flags RecvFlags) (err error) {
|
||||||
|
var dpath string
|
||||||
|
if dpath, err = d.Path(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
props := C.new_property_nvlist()
|
||||||
|
if props == nil {
|
||||||
|
err = fmt.Errorf("Out of memory func (d *Dataset) Recv()")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer C.nvlist_free(props)
|
||||||
|
cflags := to_recvflags_t(&flags)
|
||||||
|
defer C.free(unsafe.Pointer(cflags))
|
||||||
|
dest := C.CString(dpath + "/" + name)
|
||||||
|
defer C.free(unsafe.Pointer(dest))
|
||||||
|
ec := C.zfs_receive(C.libzfsHandle, dest, cflags, C.int(inf.Fd()), nil)
|
||||||
|
if ec != 0 {
|
||||||
|
err = fmt.Errorf("ZFS receive of %s failed. %s", C.GoString(dest), LastError().Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
29
zfs.c
29
zfs.c
|
@ -160,16 +160,16 @@ property_list_t *read_dataset_property(dataset_list_t *dataset, int prop) {
|
||||||
int r = 0;
|
int r = 0;
|
||||||
zprop_source_t source;
|
zprop_source_t source;
|
||||||
char statbuf[INT_MAX_VALUE];
|
char statbuf[INT_MAX_VALUE];
|
||||||
property_list_ptr list;
|
property_list_ptr list = NULL;
|
||||||
list = new_property_list();
|
list = new_property_list();
|
||||||
|
|
||||||
r = zfs_prop_get(dataset->zh, prop,
|
r = zfs_prop_get(dataset->zh, prop,
|
||||||
list->value, INT_MAX_VALUE, &source, statbuf, INT_MAX_VALUE, 1);
|
list->value, INT_MAX_VALUE, &source, statbuf, INT_MAX_VALUE, 1);
|
||||||
if (r == 0) {
|
if (r == 0 && list != NULL) {
|
||||||
// strcpy(list->name, zpool_prop_to_name(prop));
|
// strcpy(list->name, zpool_prop_to_name(prop));
|
||||||
zprop_source_tostr(list->source, source);
|
zprop_source_tostr(list->source, source);
|
||||||
list->property = (int)prop;
|
list->property = (int)prop;
|
||||||
} else {
|
} else if (list != NULL) {
|
||||||
free_properties(list);
|
free_properties(list);
|
||||||
list = NULL;
|
list = NULL;
|
||||||
}
|
}
|
||||||
|
@ -226,3 +226,26 @@ char** alloc_cstrings(int size) {
|
||||||
void strings_setat(char **a, int at, char *v) {
|
void strings_setat(char **a, int at, char *v) {
|
||||||
a[at] = v;
|
a[at] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sendflags_t *alloc_sendflags() {
|
||||||
|
sendflags_t *r = malloc(sizeof(sendflags_t));
|
||||||
|
memset(r, 0, sizeof(sendflags_t));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
recvflags_t *alloc_recvflags() {
|
||||||
|
recvflags_t *r = malloc(sizeof(recvflags_t));
|
||||||
|
memset(r, 0, sizeof(recvflags_t));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zfs_cmd *new_zfs_cmd(){
|
||||||
|
struct zfs_cmd *cmd = malloc(sizeof(struct zfs_cmd));
|
||||||
|
memset(cmd, 0, sizeof(struct zfs_cmd));
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int estimate_send_size(struct zfs_cmd *zc) {
|
||||||
|
return zfs_ioctl(libzfsHandle, ZFS_IOC_SEND, zc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
2
zfs.go
2
zfs.go
|
@ -107,7 +107,7 @@ func DatasetOpen(path string) (d Dataset, err error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = fmt.Errorf("dataset not found.")
|
err = fmt.Errorf("dataset not found.")
|
||||||
}
|
}
|
||||||
println("open failed")
|
err = fmt.Errorf("%s - %s", err.Error(), path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.Type = DatasetType(C.dataset_type(d.list))
|
d.Type = DatasetType(C.dataset_type(d.list))
|
||||||
|
|
108
zfs.h
108
zfs.h
|
@ -10,6 +10,107 @@ struct dataset_list {
|
||||||
void *pnext;
|
void *pnext;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct zfs_share {
|
||||||
|
uint64_t z_exportdata;
|
||||||
|
uint64_t z_sharedata;
|
||||||
|
uint64_t z_sharetype; /* 0 = share, 1 = unshare */
|
||||||
|
uint64_t z_sharemax; /* max length of share string */
|
||||||
|
} zfs_share_t;
|
||||||
|
|
||||||
|
struct drr_begin {
|
||||||
|
uint64_t drr_magic;
|
||||||
|
uint64_t drr_versioninfo; /* was drr_version */
|
||||||
|
uint64_t drr_creation_time;
|
||||||
|
dmu_objset_type_t drr_type;
|
||||||
|
uint32_t drr_flags;
|
||||||
|
uint64_t drr_toguid;
|
||||||
|
uint64_t drr_fromguid;
|
||||||
|
char drr_toname[MAXNAMELEN];
|
||||||
|
} drr_begin;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A limited number of zpl level stats are retrievable
|
||||||
|
* with an ioctl. zfs diff is the current consumer.
|
||||||
|
*/
|
||||||
|
typedef struct zfs_stat {
|
||||||
|
uint64_t zs_gen;
|
||||||
|
uint64_t zs_mode;
|
||||||
|
uint64_t zs_links;
|
||||||
|
uint64_t zs_ctime[2];
|
||||||
|
} zfs_stat_t;
|
||||||
|
|
||||||
|
typedef struct zinject_record {
|
||||||
|
uint64_t zi_objset;
|
||||||
|
uint64_t zi_object;
|
||||||
|
uint64_t zi_start;
|
||||||
|
uint64_t zi_end;
|
||||||
|
uint64_t zi_guid;
|
||||||
|
uint32_t zi_level;
|
||||||
|
uint32_t zi_error;
|
||||||
|
uint64_t zi_type;
|
||||||
|
uint32_t zi_freq;
|
||||||
|
uint32_t zi_failfast;
|
||||||
|
char zi_func[MAXNAMELEN];
|
||||||
|
uint32_t zi_iotype;
|
||||||
|
int32_t zi_duration;
|
||||||
|
uint64_t zi_timer;
|
||||||
|
uint64_t zi_nlanes;
|
||||||
|
uint32_t zi_cmd;
|
||||||
|
uint32_t zi_pad;
|
||||||
|
} zinject_record_t;
|
||||||
|
|
||||||
|
typedef struct dmu_objset_stats {
|
||||||
|
uint64_t dds_num_clones; /* number of clones of this */
|
||||||
|
uint64_t dds_creation_txg;
|
||||||
|
uint64_t dds_guid;
|
||||||
|
dmu_objset_type_t dds_type;
|
||||||
|
uint8_t dds_is_snapshot;
|
||||||
|
uint8_t dds_inconsistent;
|
||||||
|
char dds_origin[ZFS_MAX_DATASET_NAME_LEN];
|
||||||
|
} dmu_objset_stats_t;
|
||||||
|
|
||||||
|
typedef struct zfs_cmd {
|
||||||
|
char zc_name[MAXPATHLEN]; /* name of pool or dataset */
|
||||||
|
uint64_t zc_nvlist_src; /* really (char *) */
|
||||||
|
uint64_t zc_nvlist_src_size;
|
||||||
|
uint64_t zc_nvlist_dst; /* really (char *) */
|
||||||
|
uint64_t zc_nvlist_dst_size;
|
||||||
|
boolean_t zc_nvlist_dst_filled; /* put an nvlist in dst? */
|
||||||
|
int zc_pad2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following members are for legacy ioctls which haven't been
|
||||||
|
* converted to the new method.
|
||||||
|
*/
|
||||||
|
uint64_t zc_history; /* really (char *) */
|
||||||
|
char zc_value[MAXPATHLEN * 2];
|
||||||
|
char zc_string[MAXNAMELEN];
|
||||||
|
uint64_t zc_guid;
|
||||||
|
uint64_t zc_nvlist_conf; /* really (char *) */
|
||||||
|
uint64_t zc_nvlist_conf_size;
|
||||||
|
uint64_t zc_cookie;
|
||||||
|
uint64_t zc_objset_type;
|
||||||
|
uint64_t zc_perm_action;
|
||||||
|
uint64_t zc_history_len;
|
||||||
|
uint64_t zc_history_offset;
|
||||||
|
uint64_t zc_obj;
|
||||||
|
uint64_t zc_iflags; /* internal to zfs(7fs) */
|
||||||
|
zfs_share_t zc_share;
|
||||||
|
dmu_objset_stats_t zc_objset_stats;
|
||||||
|
struct drr_begin zc_begin_record;
|
||||||
|
zinject_record_t zc_inject_record;
|
||||||
|
uint32_t zc_defer_destroy;
|
||||||
|
uint32_t zc_flags;
|
||||||
|
uint64_t zc_action_handle;
|
||||||
|
int zc_cleanup_fd;
|
||||||
|
uint8_t zc_simple;
|
||||||
|
uint8_t zc_pad[3]; /* alignment */
|
||||||
|
uint64_t zc_sendobj;
|
||||||
|
uint64_t zc_fromobj;
|
||||||
|
uint64_t zc_createtxg;
|
||||||
|
zfs_stat_t zc_stat;
|
||||||
|
} zfs_cmd_t;
|
||||||
|
|
||||||
typedef struct dataset_list dataset_list_t;
|
typedef struct dataset_list dataset_list_t;
|
||||||
typedef struct dataset_list* dataset_list_ptr;
|
typedef struct dataset_list* dataset_list_ptr;
|
||||||
|
|
||||||
|
@ -46,5 +147,12 @@ property_list_t *read_user_property(dataset_list_t *dataset, const char* prop);
|
||||||
char** alloc_cstrings(int size);
|
char** alloc_cstrings(int size);
|
||||||
void strings_setat(char **a, int at, char *v);
|
void strings_setat(char **a, int at, char *v);
|
||||||
|
|
||||||
|
sendflags_t *alloc_sendflags();
|
||||||
|
recvflags_t *alloc_recvflags();
|
||||||
|
|
||||||
|
|
||||||
|
struct zfs_cmd *new_zfs_cmd();
|
||||||
|
int estimate_send_size(struct zfs_cmd *zc);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* SERVERWARE_ZFS_H */
|
/* SERVERWARE_ZFS_H */
|
||||||
|
|
Loading…
Reference in New Issue