/* C wrappers around some zfs calls and C in general that should simplify * using libzfs from go language, and make go code shorter and more readable. */ typedef unsigned long int rlim64_t; #include #include #include #include #include #include "common.h" #include "zpool.h" char *sZPOOL_CONFIG_VERSION = ZPOOL_CONFIG_VERSION; char *sZPOOL_CONFIG_POOL_NAME = ZPOOL_CONFIG_POOL_NAME; char *sZPOOL_CONFIG_POOL_STATE = ZPOOL_CONFIG_POOL_STATE; char *sZPOOL_CONFIG_POOL_TXG = ZPOOL_CONFIG_POOL_TXG; char *sZPOOL_CONFIG_POOL_GUID = ZPOOL_CONFIG_POOL_GUID; char *sZPOOL_CONFIG_CREATE_TXG = ZPOOL_CONFIG_CREATE_TXG; char *sZPOOL_CONFIG_TOP_GUID = ZPOOL_CONFIG_TOP_GUID; char *sZPOOL_CONFIG_VDEV_TREE = ZPOOL_CONFIG_VDEV_TREE; char *sZPOOL_CONFIG_TYPE = ZPOOL_CONFIG_TYPE; char *sZPOOL_CONFIG_CHILDREN = ZPOOL_CONFIG_CHILDREN; char *sZPOOL_CONFIG_ID = ZPOOL_CONFIG_ID; char *sZPOOL_CONFIG_GUID = ZPOOL_CONFIG_GUID; char *sZPOOL_CONFIG_PATH = ZPOOL_CONFIG_PATH; char *sZPOOL_CONFIG_DEVID = ZPOOL_CONFIG_DEVID; char *sZPOOL_CONFIG_METASLAB_ARRAY = ZPOOL_CONFIG_METASLAB_ARRAY; char *sZPOOL_CONFIG_METASLAB_SHIFT = ZPOOL_CONFIG_METASLAB_SHIFT; char *sZPOOL_CONFIG_ASHIFT = ZPOOL_CONFIG_ASHIFT; char *sZPOOL_CONFIG_ASIZE = ZPOOL_CONFIG_ASIZE; char *sZPOOL_CONFIG_DTL = ZPOOL_CONFIG_DTL; char *sZPOOL_CONFIG_SCAN_STATS = ZPOOL_CONFIG_SCAN_STATS; char *sZPOOL_CONFIG_VDEV_STATS = ZPOOL_CONFIG_VDEV_STATS; char *sZPOOL_CONFIG_WHOLE_DISK = ZPOOL_CONFIG_WHOLE_DISK; char *sZPOOL_CONFIG_ERRCOUNT = ZPOOL_CONFIG_ERRCOUNT; char *sZPOOL_CONFIG_NOT_PRESENT = ZPOOL_CONFIG_NOT_PRESENT; char *sZPOOL_CONFIG_SPARES = ZPOOL_CONFIG_SPARES; char *sZPOOL_CONFIG_IS_SPARE = ZPOOL_CONFIG_IS_SPARE; char *sZPOOL_CONFIG_NPARITY = ZPOOL_CONFIG_NPARITY; char *sZPOOL_CONFIG_HOSTID = ZPOOL_CONFIG_HOSTID; char *sZPOOL_CONFIG_HOSTNAME = ZPOOL_CONFIG_HOSTNAME; char *sZPOOL_CONFIG_LOADED_TIME = ZPOOL_CONFIG_LOADED_TIME; char *sZPOOL_CONFIG_UNSPARE = ZPOOL_CONFIG_UNSPARE; char *sZPOOL_CONFIG_PHYS_PATH = ZPOOL_CONFIG_PHYS_PATH; char *sZPOOL_CONFIG_IS_LOG = ZPOOL_CONFIG_IS_LOG; char *sZPOOL_CONFIG_L2CACHE = ZPOOL_CONFIG_L2CACHE; char *sZPOOL_CONFIG_HOLE_ARRAY = ZPOOL_CONFIG_HOLE_ARRAY; char *sZPOOL_CONFIG_VDEV_CHILDREN = ZPOOL_CONFIG_VDEV_CHILDREN; char *sZPOOL_CONFIG_IS_HOLE = ZPOOL_CONFIG_IS_HOLE; char *sZPOOL_CONFIG_DDT_HISTOGRAM = ZPOOL_CONFIG_DDT_HISTOGRAM; char *sZPOOL_CONFIG_DDT_OBJ_STATS = ZPOOL_CONFIG_DDT_OBJ_STATS; char *sZPOOL_CONFIG_DDT_STATS = ZPOOL_CONFIG_DDT_STATS; char *sZPOOL_CONFIG_SPLIT = ZPOOL_CONFIG_SPLIT; char *sZPOOL_CONFIG_ORIG_GUID = ZPOOL_CONFIG_ORIG_GUID; char *sZPOOL_CONFIG_SPLIT_GUID = ZPOOL_CONFIG_SPLIT_GUID; char *sZPOOL_CONFIG_SPLIT_LIST = ZPOOL_CONFIG_SPLIT_LIST; char *sZPOOL_CONFIG_REMOVING = ZPOOL_CONFIG_REMOVING; char *sZPOOL_CONFIG_RESILVER_TXG = ZPOOL_CONFIG_RESILVER_TXG; char *sZPOOL_CONFIG_COMMENT = ZPOOL_CONFIG_COMMENT; char *sZPOOL_CONFIG_SUSPENDED = ZPOOL_CONFIG_SUSPENDED; char *sZPOOL_CONFIG_TIMESTAMP = ZPOOL_CONFIG_TIMESTAMP; char *sZPOOL_CONFIG_BOOTFS = ZPOOL_CONFIG_BOOTFS; char *sZPOOL_CONFIG_MISSING_DEVICES = ZPOOL_CONFIG_MISSING_DEVICES; char *sZPOOL_CONFIG_LOAD_INFO = ZPOOL_CONFIG_LOAD_INFO; char *sZPOOL_CONFIG_REWIND_INFO = ZPOOL_CONFIG_REWIND_INFO; char *sZPOOL_CONFIG_UNSUP_FEAT = ZPOOL_CONFIG_UNSUP_FEAT; char *sZPOOL_CONFIG_ENABLED_FEAT = ZPOOL_CONFIG_ENABLED_FEAT; char *sZPOOL_CONFIG_CAN_RDONLY = ZPOOL_CONFIG_CAN_RDONLY; char *sZPOOL_CONFIG_FEATURES_FOR_READ = ZPOOL_CONFIG_FEATURES_FOR_READ; char *sZPOOL_CONFIG_FEATURE_STATS = ZPOOL_CONFIG_FEATURE_STATS; char *sZPOOL_CONFIG_ERRATA = ZPOOL_CONFIG_ERRATA; char *sZPOOL_CONFIG_OFFLINE = ZPOOL_CONFIG_OFFLINE; char *sZPOOL_CONFIG_FAULTED = ZPOOL_CONFIG_FAULTED; char *sZPOOL_CONFIG_DEGRADED = ZPOOL_CONFIG_DEGRADED; char *sZPOOL_CONFIG_REMOVED = ZPOOL_CONFIG_REMOVED; char *sZPOOL_CONFIG_FRU = ZPOOL_CONFIG_FRU; char *sZPOOL_CONFIG_AUX_STATE = ZPOOL_CONFIG_AUX_STATE; char *sZPOOL_REWIND_POLICY = ZPOOL_REWIND_POLICY; char *sZPOOL_REWIND_REQUEST = ZPOOL_REWIND_REQUEST; char *sZPOOL_REWIND_REQUEST_TXG = ZPOOL_REWIND_REQUEST_TXG; char *sZPOOL_REWIND_META_THRESH = ZPOOL_REWIND_META_THRESH; char *sZPOOL_REWIND_DATA_THRESH = ZPOOL_REWIND_DATA_THRESH; char *sZPOOL_CONFIG_LOAD_TIME = ZPOOL_CONFIG_LOAD_TIME; char *sZPOOL_CONFIG_LOAD_DATA_ERRORS = ZPOOL_CONFIG_LOAD_DATA_ERRORS; char *sZPOOL_CONFIG_REWIND_TIME = ZPOOL_CONFIG_REWIND_TIME; static char _lasterr_[1024]; const char *lasterr(void) { return _lasterr_; } zpool_list_t *create_zpool_list_item() { zpool_list_t *zlist = malloc(sizeof(zpool_list_t)); memset(zlist, 0, sizeof(zpool_list_t)); return zlist; } int zpool_list_callb(zpool_handle_t *pool, void *data) { zpool_list_t **lroot = (zpool_list_t**)data; zpool_list_t *nroot = create_zpool_list_item(); if ( !((*lroot)->zph) ) { (*lroot)->zph = pool; } else { nroot->zph = pool; nroot->pnext = (void*)*lroot; *lroot = nroot; } return 0; } zpool_list_ptr zpool_list_openall() { int err = 0; zpool_list_t *zlist = create_zpool_list_item(); err = zpool_iter(libzfsHandle, zpool_list_callb, &zlist); if ( err != 0 || zlist->zph == NULL ) { zpool_list_free(zlist); zlist = NULL; } return zlist; } zpool_list_t* zpool_list_open(const char *name) { zpool_list_t *zlist = create_zpool_list_item(); zlist->zph = zpool_open(libzfsHandle, name); if ( zlist->zph ) { return zlist; } else { zpool_list_free(zlist); } return 0; } zpool_list_t *zpool_next(zpool_list_t *pool) { return pool->pnext; } void zpool_list_free(zpool_list_t *list) { zpool_list_ptr next; while(list) { next = list->pnext; free(list); list = next; } } void zpool_list_close(zpool_list_t *pool) { zpool_close(pool->zph); zpool_list_free(pool); } property_list_t *next_property(property_list_t *list) { if (list != 0) { return list->pnext; } return list; } void zprop_source_tostr(char *dst, zprop_source_t source) { switch (source) { case ZPROP_SRC_NONE: strcpy(dst, "none"); break; case ZPROP_SRC_TEMPORARY: strcpy(dst, "temporary"); break; case ZPROP_SRC_LOCAL: strcpy(dst, "local"); break; case ZPROP_SRC_INHERITED: strcpy(dst, "inherited"); break; case ZPROP_SRC_RECEIVED: strcpy(dst, "received"); break; default: strcpy(dst, "default"); break; } } property_list_ptr read_zpool_property(zpool_list_ptr pool, int prop) { int r = 0; zprop_source_t source; property_list_ptr list = new_property_list(); r = zpool_get_prop(pool->zph, prop, list->value, INT_MAX_VALUE, &source, B_FALSE); if (r == 0) { // strcpy(list->name, zpool_prop_to_name(prop)); zprop_source_tostr(list->source, source); } else { free_properties(list); return NULL; } list->property = (int)prop; return list; } property_list_ptr read_append_zpool_property(zpool_list_ptr pool, property_list_ptr proot, zpool_prop_t prop) { int r = 0; property_list_t *newitem = NULL; newitem = read_zpool_property(pool, prop); if (newitem == NULL) { return proot; } // printf("p: %s %s %s\n", newitem->name, newitem->value, newitem->source); newitem->pnext = proot; proot = newitem; return proot; } property_list_t *read_zpool_properties(zpool_list_ptr pool) { // read pool name as first property property_list_t *root = NULL, *list = NULL; root = read_append_zpool_property(pool, root, ZPOOL_PROP_NAME); root = read_append_zpool_property(pool, root, ZPOOL_PROP_SIZE); root = read_append_zpool_property(pool, root, ZPOOL_PROP_CAPACITY); root = read_append_zpool_property(pool, root, ZPOOL_PROP_ALTROOT); root = read_append_zpool_property(pool, root, ZPOOL_PROP_HEALTH); root = read_append_zpool_property(pool, root, ZPOOL_PROP_GUID); root = read_append_zpool_property(pool, root, ZPOOL_PROP_VERSION); root = read_append_zpool_property(pool, root, ZPOOL_PROP_BOOTFS); root = read_append_zpool_property(pool, root, ZPOOL_PROP_DELEGATION); root = read_append_zpool_property(pool, root, ZPOOL_PROP_AUTOREPLACE); root = read_append_zpool_property(pool, root, ZPOOL_PROP_CACHEFILE); root = read_append_zpool_property(pool, root, ZPOOL_PROP_FAILUREMODE); root = read_append_zpool_property(pool, root, ZPOOL_PROP_LISTSNAPS); root = read_append_zpool_property(pool, root, ZPOOL_PROP_AUTOEXPAND); root = read_append_zpool_property(pool, root, ZPOOL_PROP_DEDUPDITTO); root = read_append_zpool_property(pool, root, ZPOOL_PROP_DEDUPRATIO); root = read_append_zpool_property(pool, root, ZPOOL_PROP_FREE); root = read_append_zpool_property(pool, root, ZPOOL_PROP_ALLOCATED); root = read_append_zpool_property(pool, root, ZPOOL_PROP_READONLY); root = read_append_zpool_property(pool, root, ZPOOL_PROP_ASHIFT); root = read_append_zpool_property(pool, root, ZPOOL_PROP_COMMENT); root = read_append_zpool_property(pool, root, ZPOOL_PROP_EXPANDSZ); root = read_append_zpool_property(pool, root, ZPOOL_PROP_FREEING); list = new_property_list(); list->property = ZPOOL_NUM_PROPS; sprintf(list->value, "%d", ZPOOL_NUM_PROPS); list->pnext = root; zprop_source_tostr(list->source, ZPROP_SRC_NONE); root = list; // printf("Finished properties reading.\n"); return root; } pool_state_t zpool_read_state(zpool_handle_t *zh) { return zpool_get_state(zh); } const char *gettext(const char *txt) { return txt; } /* * Add a property pair (name, string-value) into a property nvlist. */ // int // add_prop_list(const char *propname, char *propval, nvlist_t **props, // boolean_t poolprop) { // zpool_prop_t prop = ZPROP_INVAL; // zfs_prop_t fprop; // nvlist_t *proplist; // const char *normnm; // char *strval; // if (*props == NULL && // nvlist_alloc(props, NV_UNIQUE_NAME, 0) != 0) { // (void) snprintf(_lasterr_, 1024, "internal error: out of memory"); // return (1); // } // proplist = *props; // if (poolprop) { // const char *vname = zpool_prop_to_name(ZPOOL_PROP_VERSION); // if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL && // !zpool_prop_feature(propname)) { // (void) snprintf(_lasterr_, 1024, "property '%s' is " // "not a valid pool property", propname); // return (2); // } // /* // * feature@ properties and version should not be specified // * at the same time. // */ // // if ((prop == ZPROP_INVAL && zpool_prop_feature(propname) && // // nvlist_exists(proplist, vname)) || // // (prop == ZPOOL_PROP_VERSION && // // prop_list_contains_feature(proplist))) { // // (void) fprintf(stderr, gettext("'feature@' and " // // "'version' properties cannot be specified " // // "together\n")); // // return (2); // // } // if (zpool_prop_feature(propname)) // normnm = propname; // else // normnm = zpool_prop_to_name(prop); // } else { // if ((fprop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { // normnm = zfs_prop_to_name(fprop); // } else { // normnm = propname; // } // } // if (nvlist_lookup_string(proplist, normnm, &strval) == 0 && // prop != ZPOOL_PROP_CACHEFILE) { // (void) snprintf(_lasterr_, 1024, "property '%s' " // "specified multiple times", propname); // return (2); // } // if (nvlist_add_string(proplist, normnm, propval) != 0) { // (void) snprintf(_lasterr_, 1024, "internal " // "error: out of memory\n"); // return (1); // } // return (0); // } nvlist_t** nvlist_alloc_array(int count) { return malloc(count*sizeof(nvlist_t*)); } void nvlist_array_set(nvlist_t** a, int i, nvlist_t *item) { a[i] = item; } void nvlist_free_array(nvlist_t **a) { free(a); } nvlist_t *nvlist_array_at(nvlist_t **a, uint_t i) { return a[i]; } int refresh_stats(zpool_list_t *pool) { boolean_t missing; int err = zpool_refresh_stats(pool->zph, &missing); if ( err != 0 ) { return err; } if ( missing == B_TRUE ) { return -1; } return 0; } const char *get_vdev_type(nvlist_ptr nv) { char *value = NULL; int r = nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &value); if(r != 0) { return NULL; } return value; } const vdev_stat_ptr get_vdev_stats(nvlist_ptr nv) { vdev_stat_ptr vs = NULL; uint_t count; int r = nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t**)&vs, &count); if(r != 0) { return NULL; } return vs; } pool_scan_stat_ptr get_vdev_scan_stats(nvlist_t *nv) { pool_scan_stat_ptr vds = NULL; uint_t c; int r = nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_SCAN_STATS, (uint64_t**)&vds, &c); if(r != 0) { return NULL; } return vds; } vdev_children_ptr get_vdev_children(nvlist_t *nv) { int r; vdev_children_ptr children = malloc(sizeof(vdev_children_t)); memset(children, 0, sizeof(vdev_children_t)); r = nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &(children->first), &(children->count)); if (r != 0) { free(children); return NULL; } return children; } vdev_children_ptr get_vdev_spares(nvlist_t *nv) { int r; vdev_children_ptr children = malloc(sizeof(vdev_children_t)); memset(children, 0, sizeof(vdev_children_t)); r = nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &(children->first), &(children->count)); if (r != 0) { free(children); return NULL; } return children; } vdev_children_ptr get_vdev_l2cache(nvlist_t *nv) { int r; vdev_children_ptr children = malloc(sizeof(vdev_children_t)); memset(children, 0, sizeof(vdev_children_t)); r = nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, &(children->first), &(children->count)); if (r != 0) { free(children); return NULL; } return children; } const char *get_vdev_path(nvlist_ptr nv) { char *path = NULL; uint64_t notpresent = 0; int r = nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, ¬present); if (r == 0 || notpresent != 0) { if ( 0 != nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) ) { return NULL; } } return path; } uint64_t get_vdev_is_log(nvlist_ptr nv) { uint64_t islog = B_FALSE; nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &islog); return islog; } // return uint64_t get_zpool_state(nvlist_ptr nv) { uint64_t state = 0; nvlist_lookup_uint64(nv, ZPOOL_CONFIG_POOL_STATE, &state); return state; } uint64_t get_zpool_guid(nvlist_ptr nv) { uint64_t guid = 0; nvlist_lookup_uint64(nv, ZPOOL_CONFIG_POOL_GUID, &guid); return guid; } const char *get_zpool_name(nvlist_ptr nv) { char *name = NULL; if (0 != nvlist_lookup_string(nv, ZPOOL_CONFIG_POOL_NAME, &name)) { return NULL; } return name; } const char *get_zpool_comment(nvlist_ptr nv) { char *comment = NULL; if (0 != nvlist_lookup_string(nv, ZPOOL_CONFIG_COMMENT, &comment)) { return NULL; } return comment; } nvlist_ptr get_zpool_vdev_tree(nvlist_ptr nv) { nvlist_ptr vdev_tree = NULL; if ( 0 != nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_VDEV_TREE, &vdev_tree) ) { return NULL; } return vdev_tree; } nvlist_ptr go_zpool_search_import(libzfs_handle_ptr zfsh, int paths, char **path, boolean_t do_scan) { importargs_t idata = { 0 }; nvlist_ptr pools = NULL; idata.path = path; idata.paths = paths; // idata.scan = 0; thread_init(); pools = zpool_search_import(zfsh, &idata); thread_fini(); return pools; } int do_zpool_clear(zpool_list_t *pool, const char *device, u_int32_t rewind_policy) { nvlist_t *policy = NULL; int ret = 0; if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 || nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0) return (1); if (zpool_clear(pool->zph, device, policy) != 0) ret = 1; nvlist_free(policy); return (ret); }