Path: blob/main/cddl/contrib/opensolaris/lib/pyzfs/common/ioctl.c
39563 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*/20/*21* Copyright 2010 Sun Microsystems, Inc. All rights reserved.22* Use is subject to license terms.23*/2425#include <Python.h>26#include <sys/zfs_ioctl.h>27#include <sys/fs/zfs.h>28#include <strings.h>29#include <unistd.h>30#include <libnvpair.h>31#include <libintl.h>32#include <libzfs.h>33#include <libzfs_impl.h>34#include "zfs_prop.h"3536static PyObject *ZFSError;37static int zfsdevfd;3839#ifdef __lint40#define dgettext(x, y) y41#endif4243#define _(s) dgettext(TEXT_DOMAIN, s)4445/*PRINTFLIKE1*/46static void47seterr(char *fmt, ...)48{49char errstr[1024];50va_list v;5152va_start(v, fmt);53(void) vsnprintf(errstr, sizeof (errstr), fmt, v);54va_end(v);5556PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr));57}5859static char cmdstr[HIS_MAX_RECORD_LEN];6061static int62ioctl_with_cmdstr(int ioc, zfs_cmd_t *zc)63{64int err;6566if (cmdstr[0])67zc->zc_history = (uint64_t)(uintptr_t)cmdstr;68err = ioctl(zfsdevfd, ioc, zc);69cmdstr[0] = '\0';70return (err);71}7273static PyObject *74nvl2py(nvlist_t *nvl)75{76PyObject *pyo;77nvpair_t *nvp;7879pyo = PyDict_New();8081for (nvp = nvlist_next_nvpair(nvl, NULL); nvp;82nvp = nvlist_next_nvpair(nvl, nvp)) {83PyObject *pyval;84char *sval;85uint64_t ival;86boolean_t bval;87nvlist_t *nval;8889switch (nvpair_type(nvp)) {90case DATA_TYPE_STRING:91(void) nvpair_value_string(nvp, &sval);92pyval = Py_BuildValue("s", sval);93break;9495case DATA_TYPE_UINT64:96(void) nvpair_value_uint64(nvp, &ival);97pyval = Py_BuildValue("K", ival);98break;99100case DATA_TYPE_NVLIST:101(void) nvpair_value_nvlist(nvp, &nval);102pyval = nvl2py(nval);103break;104105case DATA_TYPE_BOOLEAN:106Py_INCREF(Py_None);107pyval = Py_None;108break;109110case DATA_TYPE_BOOLEAN_VALUE:111(void) nvpair_value_boolean_value(nvp, &bval);112pyval = Py_BuildValue("i", bval);113break;114115default:116PyErr_SetNone(PyExc_ValueError);117Py_DECREF(pyo);118return (NULL);119}120121PyDict_SetItemString(pyo, nvpair_name(nvp), pyval);122Py_DECREF(pyval);123}124125return (pyo);126}127128static nvlist_t *129dict2nvl(PyObject *d)130{131nvlist_t *nvl;132int err;133PyObject *key, *value;134int pos = 0;135136if (!PyDict_Check(d)) {137PyErr_SetObject(PyExc_ValueError, d);138return (NULL);139}140141err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);142assert(err == 0);143144while (PyDict_Next(d, &pos, &key, &value)) {145char *keystr = PyString_AsString(key);146if (keystr == NULL) {147PyErr_SetObject(PyExc_KeyError, key);148nvlist_free(nvl);149return (NULL);150}151152if (PyDict_Check(value)) {153nvlist_t *valnvl = dict2nvl(value);154err = nvlist_add_nvlist(nvl, keystr, valnvl);155nvlist_free(valnvl);156} else if (value == Py_None) {157err = nvlist_add_boolean(nvl, keystr);158} else if (PyString_Check(value)) {159char *valstr = PyString_AsString(value);160err = nvlist_add_string(nvl, keystr, valstr);161} else if (PyInt_Check(value)) {162uint64_t valint = PyInt_AsUnsignedLongLongMask(value);163err = nvlist_add_uint64(nvl, keystr, valint);164} else if (PyBool_Check(value)) {165boolean_t valbool = value == Py_True ? B_TRUE : B_FALSE;166err = nvlist_add_boolean_value(nvl, keystr, valbool);167} else {168PyErr_SetObject(PyExc_ValueError, value);169nvlist_free(nvl);170return (NULL);171}172assert(err == 0);173}174175return (nvl);176}177178static PyObject *179fakepropval(uint64_t value)180{181PyObject *d = PyDict_New();182PyDict_SetItemString(d, "value", Py_BuildValue("K", value));183return (d);184}185186static void187add_ds_props(zfs_cmd_t *zc, PyObject *nvl)188{189dmu_objset_stats_t *s = &zc->zc_objset_stats;190PyDict_SetItemString(nvl, "numclones",191fakepropval(s->dds_num_clones));192PyDict_SetItemString(nvl, "issnap",193fakepropval(s->dds_is_snapshot));194PyDict_SetItemString(nvl, "inconsistent",195fakepropval(s->dds_inconsistent));196}197198/* On error, returns NULL but does not set python exception. */199static PyObject *200ioctl_with_dstnv(int ioc, zfs_cmd_t *zc)201{202int nvsz = 2048;203void *nvbuf;204PyObject *pynv = NULL;205206again:207nvbuf = malloc(nvsz);208zc->zc_nvlist_dst_size = nvsz;209zc->zc_nvlist_dst = (uintptr_t)nvbuf;210211if (ioctl(zfsdevfd, ioc, zc) == 0) {212nvlist_t *nvl;213214errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0);215if (errno == 0) {216pynv = nvl2py(nvl);217nvlist_free(nvl);218}219} else if (errno == ENOMEM) {220free(nvbuf);221nvsz = zc->zc_nvlist_dst_size;222goto again;223}224free(nvbuf);225return (pynv);226}227228static PyObject *229py_next_dataset(PyObject *self, PyObject *args)230{231int ioc;232uint64_t cookie;233zfs_cmd_t zc = { 0 };234int snaps;235char *name;236PyObject *nvl;237PyObject *ret = NULL;238239if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie))240return (NULL);241242(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));243zc.zc_cookie = cookie;244245if (snaps)246ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT;247else248ioc = ZFS_IOC_DATASET_LIST_NEXT;249250nvl = ioctl_with_dstnv(ioc, &zc);251if (nvl) {252add_ds_props(&zc, nvl);253ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl);254Py_DECREF(nvl);255} else if (errno == ESRCH) {256PyErr_SetNone(PyExc_StopIteration);257} else {258if (snaps)259seterr(_("cannot get snapshots of %s"), name);260else261seterr(_("cannot get child datasets of %s"), name);262}263return (ret);264}265266static PyObject *267py_dataset_props(PyObject *self, PyObject *args)268{269zfs_cmd_t zc = { 0 };270int snaps;271char *name;272PyObject *nvl;273274if (!PyArg_ParseTuple(args, "s", &name))275return (NULL);276277(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));278279nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc);280if (nvl) {281add_ds_props(&zc, nvl);282} else {283seterr(_("cannot access dataset %s"), name);284}285return (nvl);286}287288static PyObject *289py_get_fsacl(PyObject *self, PyObject *args)290{291zfs_cmd_t zc = { 0 };292char *name;293PyObject *nvl;294295if (!PyArg_ParseTuple(args, "s", &name))296return (NULL);297298(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));299300nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc);301if (nvl == NULL)302seterr(_("cannot get permissions on %s"), name);303304return (nvl);305}306307static PyObject *308py_set_fsacl(PyObject *self, PyObject *args)309{310int un;311size_t nvsz;312zfs_cmd_t zc = { 0 };313char *name, *nvbuf;314PyObject *dict, *file;315nvlist_t *nvl;316int err;317318if (!PyArg_ParseTuple(args, "siO!", &name, &un,319&PyDict_Type, &dict))320return (NULL);321322nvl = dict2nvl(dict);323if (nvl == NULL)324return (NULL);325326err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);327assert(err == 0);328nvbuf = malloc(nvsz);329err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);330assert(err == 0);331332(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));333zc.zc_nvlist_src_size = nvsz;334zc.zc_nvlist_src = (uintptr_t)nvbuf;335zc.zc_perm_action = un;336337err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc);338free(nvbuf);339if (err) {340seterr(_("cannot set permissions on %s"), name);341return (NULL);342}343344Py_RETURN_NONE;345}346347static PyObject *348py_get_holds(PyObject *self, PyObject *args)349{350zfs_cmd_t zc = { 0 };351char *name;352PyObject *nvl;353354if (!PyArg_ParseTuple(args, "s", &name))355return (NULL);356357(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));358359nvl = ioctl_with_dstnv(ZFS_IOC_GET_HOLDS, &zc);360if (nvl == NULL)361seterr(_("cannot get holds for %s"), name);362363return (nvl);364}365366static PyObject *367py_userspace_many(PyObject *self, PyObject *args)368{369zfs_cmd_t zc = { 0 };370zfs_userquota_prop_t type;371char *name, *propname;372int bufsz = 1<<20;373void *buf;374PyObject *dict, *file;375int error;376377if (!PyArg_ParseTuple(args, "ss", &name, &propname))378return (NULL);379380for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++)381if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0)382break;383if (type == ZFS_NUM_USERQUOTA_PROPS) {384PyErr_SetString(PyExc_KeyError, propname);385return (NULL);386}387388dict = PyDict_New();389buf = malloc(bufsz);390391(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));392zc.zc_objset_type = type;393zc.zc_cookie = 0;394395while (1) {396zfs_useracct_t *zua = buf;397398zc.zc_nvlist_dst = (uintptr_t)buf;399zc.zc_nvlist_dst_size = bufsz;400401error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc);402if (error || zc.zc_nvlist_dst_size == 0)403break;404405while (zc.zc_nvlist_dst_size > 0) {406PyObject *pykey, *pyval;407408pykey = Py_BuildValue("sI",409zua->zu_domain, zua->zu_rid);410pyval = Py_BuildValue("K", zua->zu_space);411PyDict_SetItem(dict, pykey, pyval);412Py_DECREF(pykey);413Py_DECREF(pyval);414415zua++;416zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);417}418}419420free(buf);421422if (error != 0) {423Py_DECREF(dict);424seterr(_("cannot get %s property on %s"), propname, name);425return (NULL);426}427428return (dict);429}430431static PyObject *432py_userspace_upgrade(PyObject *self, PyObject *args)433{434zfs_cmd_t zc = { 0 };435char *name;436int error;437438if (!PyArg_ParseTuple(args, "s", &name))439return (NULL);440441(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));442error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc);443444if (error != 0) {445seterr(_("cannot initialize user accounting information on %s"),446name);447return (NULL);448}449450Py_RETURN_NONE;451}452453static PyObject *454py_set_cmdstr(PyObject *self, PyObject *args)455{456char *str;457458if (!PyArg_ParseTuple(args, "s", &str))459return (NULL);460461(void) strlcpy(cmdstr, str, sizeof (cmdstr));462463Py_RETURN_NONE;464}465466static PyObject *467py_get_proptable(PyObject *self, PyObject *args)468{469zprop_desc_t *t = zfs_prop_get_table();470PyObject *d = PyDict_New();471zfs_prop_t i;472473for (i = 0; i < ZFS_NUM_PROPS; i++) {474zprop_desc_t *p = &t[i];475PyObject *tuple;476static const char *typetable[] =477{"number", "string", "index"};478static const char *attrtable[] =479{"default", "readonly", "inherit", "onetime"};480PyObject *indextable;481482if (p->pd_proptype == PROP_TYPE_INDEX) {483const zprop_index_t *it = p->pd_table;484indextable = PyDict_New();485int j;486for (j = 0; it[j].pi_name; j++) {487PyDict_SetItemString(indextable,488it[j].pi_name,489Py_BuildValue("K", it[j].pi_value));490}491} else {492Py_INCREF(Py_None);493indextable = Py_None;494}495496tuple = Py_BuildValue("sissKsissiiO",497p->pd_name, p->pd_propnum, typetable[p->pd_proptype],498p->pd_strdefault, p->pd_numdefault,499attrtable[p->pd_attr], p->pd_types,500p->pd_values, p->pd_colname,501p->pd_rightalign, p->pd_visible, indextable);502PyDict_SetItemString(d, p->pd_name, tuple);503Py_DECREF(tuple);504}505506return (d);507}508509static PyMethodDef zfsmethods[] = {510{"next_dataset", py_next_dataset, METH_VARARGS,511"Get next child dataset or snapshot."},512{"get_fsacl", py_get_fsacl, METH_VARARGS, "Get allowed permissions."},513{"set_fsacl", py_set_fsacl, METH_VARARGS, "Set allowed permissions."},514{"userspace_many", py_userspace_many, METH_VARARGS,515"Get user space accounting."},516{"userspace_upgrade", py_userspace_upgrade, METH_VARARGS,517"Upgrade fs to enable user space accounting."},518{"set_cmdstr", py_set_cmdstr, METH_VARARGS,519"Set command string for history logging."},520{"dataset_props", py_dataset_props, METH_VARARGS,521"Get dataset properties."},522{"get_proptable", py_get_proptable, METH_NOARGS,523"Get property table."},524{"get_holds", py_get_holds, METH_VARARGS, "Get user holds."},525{NULL, NULL, 0, NULL}526};527528void529initioctl(void)530{531PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods);532PyObject *zfs_util = PyImport_ImportModule("zfs.util");533PyObject *devfile;534535if (zfs_util == NULL)536return;537538ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError");539devfile = PyObject_GetAttrString(zfs_util, "dev");540zfsdevfd = PyObject_AsFileDescriptor(devfile);541542zfs_prop_init();543}544545546