1209962Smm/* 2209962Smm * CDDL HEADER START 3209962Smm * 4209962Smm * The contents of this file are subject to the terms of the 5209962Smm * Common Development and Distribution License (the "License"). 6209962Smm * You may not use this file except in compliance with the License. 7209962Smm * 8209962Smm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9209962Smm * or http://www.opensolaris.org/os/licensing. 10209962Smm * See the License for the specific language governing permissions 11209962Smm * and limitations under the License. 12209962Smm * 13209962Smm * When distributing Covered Code, include this CDDL HEADER in each 14209962Smm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15209962Smm * If applicable, add the following below this CDDL HEADER, with the 16209962Smm * fields enclosed by brackets "[]" replaced with your own identifying 17209962Smm * information: Portions Copyright [yyyy] [name of copyright owner] 18209962Smm * 19209962Smm * CDDL HEADER END 20209962Smm */ 21209962Smm/* 22219089Spjd * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23209962Smm * Use is subject to license terms. 24209962Smm */ 25209962Smm 26209962Smm#include <Python.h> 27209962Smm#include <sys/zfs_ioctl.h> 28209962Smm#include <sys/fs/zfs.h> 29209962Smm#include <strings.h> 30209962Smm#include <unistd.h> 31209962Smm#include <libnvpair.h> 32209962Smm#include <libintl.h> 33209962Smm#include <libzfs.h> 34211970Spjd#include <libzfs_impl.h> 35209962Smm#include "zfs_prop.h" 36209962Smm 37209962Smmstatic PyObject *ZFSError; 38209962Smmstatic int zfsdevfd; 39209962Smm 40209962Smm#ifdef __lint 41209962Smm#define dgettext(x, y) y 42209962Smm#endif 43209962Smm 44209962Smm#define _(s) dgettext(TEXT_DOMAIN, s) 45209962Smm 46209962Smm/*PRINTFLIKE1*/ 47209962Smmstatic void 48209962Smmseterr(char *fmt, ...) 49209962Smm{ 50209962Smm char errstr[1024]; 51209962Smm va_list v; 52209962Smm 53209962Smm va_start(v, fmt); 54209962Smm (void) vsnprintf(errstr, sizeof (errstr), fmt, v); 55209962Smm va_end(v); 56209962Smm 57209962Smm PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr)); 58209962Smm} 59209962Smm 60209962Smmstatic char cmdstr[HIS_MAX_RECORD_LEN]; 61209962Smm 62209962Smmstatic int 63219089Spjdioctl_with_cmdstr(int ioc, zfs_cmd_t *zc) 64209962Smm{ 65209962Smm int err; 66209962Smm 67209962Smm if (cmdstr[0]) 68209962Smm zc->zc_history = (uint64_t)(uintptr_t)cmdstr; 69209962Smm err = ioctl(zfsdevfd, ioc, zc); 70209962Smm cmdstr[0] = '\0'; 71209962Smm return (err); 72209962Smm} 73209962Smm 74209962Smmstatic PyObject * 75209962Smmnvl2py(nvlist_t *nvl) 76209962Smm{ 77209962Smm PyObject *pyo; 78209962Smm nvpair_t *nvp; 79209962Smm 80209962Smm pyo = PyDict_New(); 81209962Smm 82209962Smm for (nvp = nvlist_next_nvpair(nvl, NULL); nvp; 83209962Smm nvp = nvlist_next_nvpair(nvl, nvp)) { 84209962Smm PyObject *pyval; 85209962Smm char *sval; 86209962Smm uint64_t ival; 87209962Smm boolean_t bval; 88209962Smm nvlist_t *nval; 89209962Smm 90209962Smm switch (nvpair_type(nvp)) { 91209962Smm case DATA_TYPE_STRING: 92209962Smm (void) nvpair_value_string(nvp, &sval); 93209962Smm pyval = Py_BuildValue("s", sval); 94209962Smm break; 95209962Smm 96209962Smm case DATA_TYPE_UINT64: 97209962Smm (void) nvpair_value_uint64(nvp, &ival); 98209962Smm pyval = Py_BuildValue("K", ival); 99209962Smm break; 100209962Smm 101209962Smm case DATA_TYPE_NVLIST: 102209962Smm (void) nvpair_value_nvlist(nvp, &nval); 103209962Smm pyval = nvl2py(nval); 104209962Smm break; 105209962Smm 106209962Smm case DATA_TYPE_BOOLEAN: 107209962Smm Py_INCREF(Py_None); 108209962Smm pyval = Py_None; 109209962Smm break; 110209962Smm 111209962Smm case DATA_TYPE_BOOLEAN_VALUE: 112209962Smm (void) nvpair_value_boolean_value(nvp, &bval); 113209962Smm pyval = Py_BuildValue("i", bval); 114209962Smm break; 115209962Smm 116209962Smm default: 117209962Smm PyErr_SetNone(PyExc_ValueError); 118209962Smm Py_DECREF(pyo); 119209962Smm return (NULL); 120209962Smm } 121209962Smm 122209962Smm PyDict_SetItemString(pyo, nvpair_name(nvp), pyval); 123209962Smm Py_DECREF(pyval); 124209962Smm } 125209962Smm 126209962Smm return (pyo); 127209962Smm} 128209962Smm 129209962Smmstatic nvlist_t * 130209962Smmdict2nvl(PyObject *d) 131209962Smm{ 132209962Smm nvlist_t *nvl; 133209962Smm int err; 134209962Smm PyObject *key, *value; 135219089Spjd int pos = 0; 136209962Smm 137209962Smm if (!PyDict_Check(d)) { 138209962Smm PyErr_SetObject(PyExc_ValueError, d); 139209962Smm return (NULL); 140209962Smm } 141209962Smm 142209962Smm err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0); 143209962Smm assert(err == 0); 144209962Smm 145209962Smm while (PyDict_Next(d, &pos, &key, &value)) { 146209962Smm char *keystr = PyString_AsString(key); 147209962Smm if (keystr == NULL) { 148209962Smm PyErr_SetObject(PyExc_KeyError, key); 149209962Smm nvlist_free(nvl); 150209962Smm return (NULL); 151209962Smm } 152209962Smm 153209962Smm if (PyDict_Check(value)) { 154209962Smm nvlist_t *valnvl = dict2nvl(value); 155209962Smm err = nvlist_add_nvlist(nvl, keystr, valnvl); 156209962Smm nvlist_free(valnvl); 157209962Smm } else if (value == Py_None) { 158209962Smm err = nvlist_add_boolean(nvl, keystr); 159209962Smm } else if (PyString_Check(value)) { 160209962Smm char *valstr = PyString_AsString(value); 161209962Smm err = nvlist_add_string(nvl, keystr, valstr); 162209962Smm } else if (PyInt_Check(value)) { 163209962Smm uint64_t valint = PyInt_AsUnsignedLongLongMask(value); 164209962Smm err = nvlist_add_uint64(nvl, keystr, valint); 165209962Smm } else if (PyBool_Check(value)) { 166209962Smm boolean_t valbool = value == Py_True ? B_TRUE : B_FALSE; 167209962Smm err = nvlist_add_boolean_value(nvl, keystr, valbool); 168209962Smm } else { 169209962Smm PyErr_SetObject(PyExc_ValueError, value); 170209962Smm nvlist_free(nvl); 171209962Smm return (NULL); 172209962Smm } 173209962Smm assert(err == 0); 174209962Smm } 175209962Smm 176209962Smm return (nvl); 177209962Smm} 178209962Smm 179209962Smmstatic PyObject * 180209962Smmfakepropval(uint64_t value) 181209962Smm{ 182209962Smm PyObject *d = PyDict_New(); 183209962Smm PyDict_SetItemString(d, "value", Py_BuildValue("K", value)); 184209962Smm return (d); 185209962Smm} 186209962Smm 187209962Smmstatic void 188209962Smmadd_ds_props(zfs_cmd_t *zc, PyObject *nvl) 189209962Smm{ 190209962Smm dmu_objset_stats_t *s = &zc->zc_objset_stats; 191209962Smm PyDict_SetItemString(nvl, "numclones", 192209962Smm fakepropval(s->dds_num_clones)); 193209962Smm PyDict_SetItemString(nvl, "issnap", 194209962Smm fakepropval(s->dds_is_snapshot)); 195209962Smm PyDict_SetItemString(nvl, "inconsistent", 196209962Smm fakepropval(s->dds_inconsistent)); 197209962Smm} 198209962Smm 199209962Smm/* On error, returns NULL but does not set python exception. */ 200209962Smmstatic PyObject * 201219089Spjdioctl_with_dstnv(int ioc, zfs_cmd_t *zc) 202209962Smm{ 203209962Smm int nvsz = 2048; 204209962Smm void *nvbuf; 205209962Smm PyObject *pynv = NULL; 206209962Smm 207209962Smmagain: 208209962Smm nvbuf = malloc(nvsz); 209209962Smm zc->zc_nvlist_dst_size = nvsz; 210209962Smm zc->zc_nvlist_dst = (uintptr_t)nvbuf; 211209962Smm 212209962Smm if (ioctl(zfsdevfd, ioc, zc) == 0) { 213209962Smm nvlist_t *nvl; 214209962Smm 215209962Smm errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0); 216209962Smm if (errno == 0) { 217209962Smm pynv = nvl2py(nvl); 218209962Smm nvlist_free(nvl); 219209962Smm } 220209962Smm } else if (errno == ENOMEM) { 221209962Smm free(nvbuf); 222209962Smm nvsz = zc->zc_nvlist_dst_size; 223209962Smm goto again; 224209962Smm } 225209962Smm free(nvbuf); 226209962Smm return (pynv); 227209962Smm} 228209962Smm 229209962Smmstatic PyObject * 230209962Smmpy_next_dataset(PyObject *self, PyObject *args) 231209962Smm{ 232219089Spjd int ioc; 233209962Smm uint64_t cookie; 234209962Smm zfs_cmd_t zc = { 0 }; 235209962Smm int snaps; 236209962Smm char *name; 237209962Smm PyObject *nvl; 238209962Smm PyObject *ret = NULL; 239209962Smm 240209962Smm if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie)) 241209962Smm return (NULL); 242209962Smm 243209962Smm (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 244209962Smm zc.zc_cookie = cookie; 245209962Smm 246209962Smm if (snaps) 247209962Smm ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT; 248209962Smm else 249209962Smm ioc = ZFS_IOC_DATASET_LIST_NEXT; 250209962Smm 251209962Smm nvl = ioctl_with_dstnv(ioc, &zc); 252209962Smm if (nvl) { 253209962Smm add_ds_props(&zc, nvl); 254209962Smm ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl); 255209962Smm Py_DECREF(nvl); 256209962Smm } else if (errno == ESRCH) { 257209962Smm PyErr_SetNone(PyExc_StopIteration); 258209962Smm } else { 259209962Smm if (snaps) 260209962Smm seterr(_("cannot get snapshots of %s"), name); 261209962Smm else 262209962Smm seterr(_("cannot get child datasets of %s"), name); 263209962Smm } 264209962Smm return (ret); 265209962Smm} 266209962Smm 267209962Smmstatic PyObject * 268209962Smmpy_dataset_props(PyObject *self, PyObject *args) 269209962Smm{ 270209962Smm zfs_cmd_t zc = { 0 }; 271209962Smm int snaps; 272209962Smm char *name; 273209962Smm PyObject *nvl; 274209962Smm 275209962Smm if (!PyArg_ParseTuple(args, "s", &name)) 276209962Smm return (NULL); 277209962Smm 278209962Smm (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 279209962Smm 280209962Smm nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc); 281209962Smm if (nvl) { 282209962Smm add_ds_props(&zc, nvl); 283209962Smm } else { 284209962Smm seterr(_("cannot access dataset %s"), name); 285209962Smm } 286209962Smm return (nvl); 287209962Smm} 288209962Smm 289209962Smmstatic PyObject * 290209962Smmpy_get_fsacl(PyObject *self, PyObject *args) 291209962Smm{ 292209962Smm zfs_cmd_t zc = { 0 }; 293209962Smm char *name; 294209962Smm PyObject *nvl; 295209962Smm 296209962Smm if (!PyArg_ParseTuple(args, "s", &name)) 297209962Smm return (NULL); 298209962Smm 299209962Smm (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 300209962Smm 301209962Smm nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc); 302209962Smm if (nvl == NULL) 303209962Smm seterr(_("cannot get permissions on %s"), name); 304209962Smm 305209962Smm return (nvl); 306209962Smm} 307209962Smm 308209962Smmstatic PyObject * 309209962Smmpy_set_fsacl(PyObject *self, PyObject *args) 310209962Smm{ 311209962Smm int un; 312209962Smm size_t nvsz; 313209962Smm zfs_cmd_t zc = { 0 }; 314209962Smm char *name, *nvbuf; 315209962Smm PyObject *dict, *file; 316209962Smm nvlist_t *nvl; 317209962Smm int err; 318209962Smm 319209962Smm if (!PyArg_ParseTuple(args, "siO!", &name, &un, 320209962Smm &PyDict_Type, &dict)) 321209962Smm return (NULL); 322209962Smm 323209962Smm nvl = dict2nvl(dict); 324209962Smm if (nvl == NULL) 325209962Smm return (NULL); 326209962Smm 327209962Smm err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); 328209962Smm assert(err == 0); 329209962Smm nvbuf = malloc(nvsz); 330209962Smm err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); 331209962Smm assert(err == 0); 332209962Smm 333209962Smm (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 334209962Smm zc.zc_nvlist_src_size = nvsz; 335209962Smm zc.zc_nvlist_src = (uintptr_t)nvbuf; 336209962Smm zc.zc_perm_action = un; 337209962Smm 338209962Smm err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc); 339209962Smm free(nvbuf); 340209962Smm if (err) { 341209962Smm seterr(_("cannot set permissions on %s"), name); 342209962Smm return (NULL); 343209962Smm } 344209962Smm 345209962Smm Py_RETURN_NONE; 346209962Smm} 347209962Smm 348209962Smmstatic PyObject * 349219089Spjdpy_get_holds(PyObject *self, PyObject *args) 350219089Spjd{ 351219089Spjd zfs_cmd_t zc = { 0 }; 352219089Spjd char *name; 353219089Spjd PyObject *nvl; 354219089Spjd 355219089Spjd if (!PyArg_ParseTuple(args, "s", &name)) 356219089Spjd return (NULL); 357219089Spjd 358219089Spjd (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 359219089Spjd 360219089Spjd nvl = ioctl_with_dstnv(ZFS_IOC_GET_HOLDS, &zc); 361219089Spjd if (nvl == NULL) 362219089Spjd seterr(_("cannot get holds for %s"), name); 363219089Spjd 364219089Spjd return (nvl); 365219089Spjd} 366219089Spjd 367219089Spjdstatic PyObject * 368209962Smmpy_userspace_many(PyObject *self, PyObject *args) 369209962Smm{ 370209962Smm zfs_cmd_t zc = { 0 }; 371209962Smm zfs_userquota_prop_t type; 372209962Smm char *name, *propname; 373209962Smm int bufsz = 1<<20; 374209962Smm void *buf; 375209962Smm PyObject *dict, *file; 376209962Smm int error; 377209962Smm 378209962Smm if (!PyArg_ParseTuple(args, "ss", &name, &propname)) 379209962Smm return (NULL); 380209962Smm 381209962Smm for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) 382209962Smm if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0) 383209962Smm break; 384209962Smm if (type == ZFS_NUM_USERQUOTA_PROPS) { 385209962Smm PyErr_SetString(PyExc_KeyError, propname); 386209962Smm return (NULL); 387209962Smm } 388209962Smm 389209962Smm dict = PyDict_New(); 390209962Smm buf = malloc(bufsz); 391209962Smm 392209962Smm (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 393209962Smm zc.zc_objset_type = type; 394209962Smm zc.zc_cookie = 0; 395209962Smm 396209962Smm while (1) { 397209962Smm zfs_useracct_t *zua = buf; 398209962Smm 399209962Smm zc.zc_nvlist_dst = (uintptr_t)buf; 400209962Smm zc.zc_nvlist_dst_size = bufsz; 401209962Smm 402209962Smm error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc); 403209962Smm if (error || zc.zc_nvlist_dst_size == 0) 404209962Smm break; 405209962Smm 406209962Smm while (zc.zc_nvlist_dst_size > 0) { 407209962Smm PyObject *pykey, *pyval; 408209962Smm 409209962Smm pykey = Py_BuildValue("sI", 410209962Smm zua->zu_domain, zua->zu_rid); 411209962Smm pyval = Py_BuildValue("K", zua->zu_space); 412209962Smm PyDict_SetItem(dict, pykey, pyval); 413209962Smm Py_DECREF(pykey); 414209962Smm Py_DECREF(pyval); 415209962Smm 416209962Smm zua++; 417209962Smm zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 418209962Smm } 419209962Smm } 420209962Smm 421209962Smm free(buf); 422209962Smm 423209962Smm if (error != 0) { 424209962Smm Py_DECREF(dict); 425209962Smm seterr(_("cannot get %s property on %s"), propname, name); 426209962Smm return (NULL); 427209962Smm } 428209962Smm 429209962Smm return (dict); 430209962Smm} 431209962Smm 432209962Smmstatic PyObject * 433209962Smmpy_userspace_upgrade(PyObject *self, PyObject *args) 434209962Smm{ 435209962Smm zfs_cmd_t zc = { 0 }; 436209962Smm char *name; 437209962Smm int error; 438209962Smm 439209962Smm if (!PyArg_ParseTuple(args, "s", &name)) 440209962Smm return (NULL); 441209962Smm 442209962Smm (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 443209962Smm error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc); 444209962Smm 445209962Smm if (error != 0) { 446209962Smm seterr(_("cannot initialize user accounting information on %s"), 447209962Smm name); 448209962Smm return (NULL); 449209962Smm } 450209962Smm 451209962Smm Py_RETURN_NONE; 452209962Smm} 453209962Smm 454209962Smmstatic PyObject * 455209962Smmpy_set_cmdstr(PyObject *self, PyObject *args) 456209962Smm{ 457209962Smm char *str; 458209962Smm 459209962Smm if (!PyArg_ParseTuple(args, "s", &str)) 460209962Smm return (NULL); 461209962Smm 462209962Smm (void) strlcpy(cmdstr, str, sizeof (cmdstr)); 463209962Smm 464209962Smm Py_RETURN_NONE; 465209962Smm} 466209962Smm 467209962Smmstatic PyObject * 468209962Smmpy_get_proptable(PyObject *self, PyObject *args) 469209962Smm{ 470209962Smm zprop_desc_t *t = zfs_prop_get_table(); 471209962Smm PyObject *d = PyDict_New(); 472209962Smm zfs_prop_t i; 473209962Smm 474209962Smm for (i = 0; i < ZFS_NUM_PROPS; i++) { 475209962Smm zprop_desc_t *p = &t[i]; 476209962Smm PyObject *tuple; 477209962Smm static const char *typetable[] = 478209962Smm {"number", "string", "index"}; 479209962Smm static const char *attrtable[] = 480209962Smm {"default", "readonly", "inherit", "onetime"}; 481209962Smm PyObject *indextable; 482209962Smm 483209962Smm if (p->pd_proptype == PROP_TYPE_INDEX) { 484209962Smm const zprop_index_t *it = p->pd_table; 485209962Smm indextable = PyDict_New(); 486209962Smm int j; 487209962Smm for (j = 0; it[j].pi_name; j++) { 488209962Smm PyDict_SetItemString(indextable, 489209962Smm it[j].pi_name, 490209962Smm Py_BuildValue("K", it[j].pi_value)); 491209962Smm } 492209962Smm } else { 493209962Smm Py_INCREF(Py_None); 494209962Smm indextable = Py_None; 495209962Smm } 496209962Smm 497209962Smm tuple = Py_BuildValue("sissKsissiiO", 498209962Smm p->pd_name, p->pd_propnum, typetable[p->pd_proptype], 499209962Smm p->pd_strdefault, p->pd_numdefault, 500209962Smm attrtable[p->pd_attr], p->pd_types, 501209962Smm p->pd_values, p->pd_colname, 502209962Smm p->pd_rightalign, p->pd_visible, indextable); 503209962Smm PyDict_SetItemString(d, p->pd_name, tuple); 504209962Smm Py_DECREF(tuple); 505209962Smm } 506209962Smm 507209962Smm return (d); 508209962Smm} 509209962Smm 510209962Smmstatic PyMethodDef zfsmethods[] = { 511209962Smm {"next_dataset", py_next_dataset, METH_VARARGS, 512209962Smm "Get next child dataset or snapshot."}, 513209962Smm {"get_fsacl", py_get_fsacl, METH_VARARGS, "Get allowed permissions."}, 514209962Smm {"set_fsacl", py_set_fsacl, METH_VARARGS, "Set allowed permissions."}, 515209962Smm {"userspace_many", py_userspace_many, METH_VARARGS, 516209962Smm "Get user space accounting."}, 517209962Smm {"userspace_upgrade", py_userspace_upgrade, METH_VARARGS, 518209962Smm "Upgrade fs to enable user space accounting."}, 519209962Smm {"set_cmdstr", py_set_cmdstr, METH_VARARGS, 520209962Smm "Set command string for history logging."}, 521209962Smm {"dataset_props", py_dataset_props, METH_VARARGS, 522209962Smm "Get dataset properties."}, 523209962Smm {"get_proptable", py_get_proptable, METH_NOARGS, 524209962Smm "Get property table."}, 525219089Spjd {"get_holds", py_get_holds, METH_VARARGS, "Get user holds."}, 526209962Smm {NULL, NULL, 0, NULL} 527209962Smm}; 528209962Smm 529209962Smmvoid 530209962Smminitioctl(void) 531209962Smm{ 532209962Smm PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods); 533209962Smm PyObject *zfs_util = PyImport_ImportModule("zfs.util"); 534209962Smm PyObject *devfile; 535209962Smm 536209962Smm if (zfs_util == NULL) 537209962Smm return; 538209962Smm 539209962Smm ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError"); 540209962Smm devfile = PyObject_GetAttrString(zfs_util, "dev"); 541209962Smm zfsdevfd = PyObject_AsFileDescriptor(devfile); 542209962Smm 543209962Smm zfs_prop_init(); 544209962Smm} 545