/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include <Python.h> #include <sys/zfs_ioctl.h> #include <sys/fs/zfs.h> #include <strings.h> #include <unistd.h> #include <libnvpair.h> #include <idmap.h> #include <zone.h> #include <libintl.h> #include <libzfs.h> #include <directory.h> #include "zfs_prop.h" static PyObject *ZFSError; static int zfsdevfd; #ifdef __lint #define dgettext(x, y) y #endif #define _(s) dgettext(TEXT_DOMAIN, s) extern int sid_to_id(char *sid, boolean_t user, uid_t *id); /*PRINTFLIKE1*/ static void seterr(char *fmt, ...) { char errstr[1024]; va_list v; va_start(v, fmt); (void) vsnprintf(errstr, sizeof (errstr), fmt, v); va_end(v); PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr)); } static char cmdstr[HIS_MAX_RECORD_LEN]; static int ioctl_with_cmdstr(int ioc, zfs_cmd_t *zc) { int err; if (cmdstr[0]) zc->zc_history = (uint64_t)(uintptr_t)cmdstr; err = ioctl(zfsdevfd, ioc, zc); cmdstr[0] = '\0'; return (err); } static PyObject * nvl2py(nvlist_t *nvl) { PyObject *pyo; nvpair_t *nvp; pyo = PyDict_New(); for (nvp = nvlist_next_nvpair(nvl, NULL); nvp; nvp = nvlist_next_nvpair(nvl, nvp)) { PyObject *pyval; char *sval; uint64_t ival; boolean_t bval; nvlist_t *nval; switch (nvpair_type(nvp)) { case DATA_TYPE_STRING: (void) nvpair_value_string(nvp, &sval); pyval = Py_BuildValue("s", sval); break; case DATA_TYPE_UINT64: (void) nvpair_value_uint64(nvp, &ival); pyval = Py_BuildValue("K", ival); break; case DATA_TYPE_NVLIST: (void) nvpair_value_nvlist(nvp, &nval); pyval = nvl2py(nval); break; case DATA_TYPE_BOOLEAN: Py_INCREF(Py_None); pyval = Py_None; break; case DATA_TYPE_BOOLEAN_VALUE: (void) nvpair_value_boolean_value(nvp, &bval); pyval = Py_BuildValue("i", bval); break; default: PyErr_SetNone(PyExc_ValueError); Py_DECREF(pyo); return (NULL); } PyDict_SetItemString(pyo, nvpair_name(nvp), pyval); Py_DECREF(pyval); } return (pyo); } static nvlist_t * dict2nvl(PyObject *d) { nvlist_t *nvl; int err; PyObject *key, *value; int pos = 0; if (!PyDict_Check(d)) { PyErr_SetObject(PyExc_ValueError, d); return (NULL); } err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0); assert(err == 0); while (PyDict_Next(d, &pos, &key, &value)) { char *keystr = PyString_AsString(key); if (keystr == NULL) { PyErr_SetObject(PyExc_KeyError, key); nvlist_free(nvl); return (NULL); } if (PyDict_Check(value)) { nvlist_t *valnvl = dict2nvl(value); err = nvlist_add_nvlist(nvl, keystr, valnvl); nvlist_free(valnvl); } else if (value == Py_None) { err = nvlist_add_boolean(nvl, keystr); } else if (PyString_Check(value)) { char *valstr = PyString_AsString(value); err = nvlist_add_string(nvl, keystr, valstr); } else if (PyInt_Check(value)) { uint64_t valint = PyInt_AsUnsignedLongLongMask(value); err = nvlist_add_uint64(nvl, keystr, valint); } else if (PyBool_Check(value)) { boolean_t valbool = value == Py_True ? B_TRUE : B_FALSE; err = nvlist_add_boolean_value(nvl, keystr, valbool); } else { PyErr_SetObject(PyExc_ValueError, value); nvlist_free(nvl); return (NULL); } assert(err == 0); } return (nvl); } static PyObject * fakepropval(uint64_t value) { PyObject *d = PyDict_New(); PyDict_SetItemString(d, "value", Py_BuildValue("K", value)); return (d); } static void add_ds_props(zfs_cmd_t *zc, PyObject *nvl) { dmu_objset_stats_t *s = &zc->zc_objset_stats; PyDict_SetItemString(nvl, "numclones", fakepropval(s->dds_num_clones)); PyDict_SetItemString(nvl, "issnap", fakepropval(s->dds_is_snapshot)); PyDict_SetItemString(nvl, "inconsistent", fakepropval(s->dds_inconsistent)); } /* On error, returns NULL but does not set python exception. */ static PyObject * ioctl_with_dstnv(int ioc, zfs_cmd_t *zc) { int nvsz = 2048; void *nvbuf; PyObject *pynv = NULL; again: nvbuf = malloc(nvsz); zc->zc_nvlist_dst_size = nvsz; zc->zc_nvlist_dst = (uintptr_t)nvbuf; if (ioctl(zfsdevfd, ioc, zc) == 0) { nvlist_t *nvl; errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0); if (errno == 0) { pynv = nvl2py(nvl); nvlist_free(nvl); } } else if (errno == ENOMEM) { free(nvbuf); nvsz = zc->zc_nvlist_dst_size; goto again; } free(nvbuf); return (pynv); } static PyObject * py_next_dataset(PyObject *self, PyObject *args) { int ioc; uint64_t cookie; zfs_cmd_t zc = { 0 }; int snaps; char *name; PyObject *nvl; PyObject *ret = NULL; if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie)) return (NULL); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); zc.zc_cookie = cookie; if (snaps) ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT; else ioc = ZFS_IOC_DATASET_LIST_NEXT; nvl = ioctl_with_dstnv(ioc, &zc); if (nvl) { add_ds_props(&zc, nvl); ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl); Py_DECREF(nvl); } else if (errno == ESRCH) { PyErr_SetNone(PyExc_StopIteration); } else { if (snaps) seterr(_("cannot get snapshots of %s"), name); else seterr(_("cannot get child datasets of %s"), name); } return (ret); } static PyObject * py_dataset_props(PyObject *self, PyObject *args) { zfs_cmd_t zc = { 0 }; int snaps; char *name; PyObject *nvl; if (!PyArg_ParseTuple(args, "s", &name)) return (NULL); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc); if (nvl) { add_ds_props(&zc, nvl); } else { seterr(_("cannot access dataset %s"), name); } return (nvl); } static PyObject * py_get_fsacl(PyObject *self, PyObject *args) { zfs_cmd_t zc = { 0 }; char *name; PyObject *nvl; if (!PyArg_ParseTuple(args, "s", &name)) return (NULL); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc); if (nvl == NULL) seterr(_("cannot get permissions on %s"), name); return (nvl); } static PyObject * py_set_fsacl(PyObject *self, PyObject *args) { int un; size_t nvsz; zfs_cmd_t zc = { 0 }; char *name, *nvbuf; PyObject *dict, *file; nvlist_t *nvl; int err; if (!PyArg_ParseTuple(args, "siO!", &name, &un, &PyDict_Type, &dict)) return (NULL); nvl = dict2nvl(dict); if (nvl == NULL) return (NULL); err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); assert(err == 0); nvbuf = malloc(nvsz); err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); assert(err == 0); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); zc.zc_nvlist_src_size = nvsz; zc.zc_nvlist_src = (uintptr_t)nvbuf; zc.zc_perm_action = un; err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc); free(nvbuf); if (err) { seterr(_("cannot set permissions on %s"), name); return (NULL); } Py_RETURN_NONE; } static PyObject * py_get_holds(PyObject *self, PyObject *args) { zfs_cmd_t zc = { 0 }; char *name; PyObject *nvl; if (!PyArg_ParseTuple(args, "s", &name)) return (NULL); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); nvl = ioctl_with_dstnv(ZFS_IOC_GET_HOLDS, &zc); if (nvl == NULL) seterr(_("cannot get holds for %s"), name); return (nvl); } static PyObject * py_userspace_many(PyObject *self, PyObject *args) { zfs_cmd_t zc = { 0 }; zfs_userquota_prop_t type; char *name, *propname; int bufsz = 1<<20; void *buf; PyObject *dict, *file; int error; if (!PyArg_ParseTuple(args, "ss", &name, &propname)) return (NULL); for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0) break; if (type == ZFS_NUM_USERQUOTA_PROPS) { PyErr_SetString(PyExc_KeyError, propname); return (NULL); } dict = PyDict_New(); buf = malloc(bufsz); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); zc.zc_objset_type = type; zc.zc_cookie = 0; while (1) { zfs_useracct_t *zua = buf; zc.zc_nvlist_dst = (uintptr_t)buf; zc.zc_nvlist_dst_size = bufsz; error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc); if (error || zc.zc_nvlist_dst_size == 0) break; while (zc.zc_nvlist_dst_size > 0) { PyObject *pykey, *pyval; pykey = Py_BuildValue("sI", zua->zu_domain, zua->zu_rid); pyval = Py_BuildValue("K", zua->zu_space); PyDict_SetItem(dict, pykey, pyval); Py_DECREF(pykey); Py_DECREF(pyval); zua++; zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); } } free(buf); if (error != 0) { Py_DECREF(dict); seterr(_("cannot get %s property on %s"), propname, name); return (NULL); } return (dict); } static PyObject * py_userspace_upgrade(PyObject *self, PyObject *args) { zfs_cmd_t zc = { 0 }; char *name; int error; if (!PyArg_ParseTuple(args, "s", &name)) return (NULL); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc); if (error != 0) { seterr(_("cannot initialize user accounting information on %s"), name); return (NULL); } Py_RETURN_NONE; } static PyObject * py_sid_to_id(PyObject *self, PyObject *args) { char *sid; int err, isuser; uid_t id; if (!PyArg_ParseTuple(args, "si", &sid, &isuser)) return (NULL); err = sid_to_id(sid, isuser, &id); if (err) { PyErr_SetString(PyExc_KeyError, sid); return (NULL); } return (Py_BuildValue("I", id)); } /* * Translate the sid string ("S-1-...") to the user@domain name, if * possible. */ static PyObject * py_sid_to_name(PyObject *self, PyObject *args) { int isuser; char *name, *sid; directory_error_t e; uint64_t classes; PyObject *ret; if (!PyArg_ParseTuple(args, "si", &sid, &isuser)) return (NULL); e = directory_name_from_sid(NULL, sid, &name, &classes); if (e != NULL) { directory_error_free(e); PyErr_SetString(PyExc_KeyError, sid); return (NULL); } if (name == NULL) { PyErr_SetString(PyExc_KeyError, sid); return (NULL); } if (isuser) { if (!(classes & DIRECTORY_CLASS_USER)) { free(name); PyErr_SetString(PyExc_KeyError, sid); return (NULL); } } else { if (!(classes & DIRECTORY_CLASS_GROUP)) { free(name); PyErr_SetString(PyExc_KeyError, sid); return (NULL); } } ret = PyString_FromString(name); free(name); return (ret); } static PyObject * py_isglobalzone(PyObject *self, PyObject *args) { return (Py_BuildValue("i", getzoneid() == GLOBAL_ZONEID)); } static PyObject * py_set_cmdstr(PyObject *self, PyObject *args) { char *str; if (!PyArg_ParseTuple(args, "s", &str)) return (NULL); (void) strlcpy(cmdstr, str, sizeof (cmdstr)); Py_RETURN_NONE; } static PyObject * py_get_proptable(PyObject *self, PyObject *args) { zprop_desc_t *t = zfs_prop_get_table(); PyObject *d = PyDict_New(); zfs_prop_t i; for (i = 0; i < ZFS_NUM_PROPS; i++) { zprop_desc_t *p = &t[i]; PyObject *tuple; static const char *typetable[] = {"number", "string", "index"}; static const char *attrtable[] = {"default", "readonly", "inherit", "onetime"}; PyObject *indextable; if (p->pd_proptype == PROP_TYPE_INDEX) { const zprop_index_t *it = p->pd_table; indextable = PyDict_New(); int j; for (j = 0; it[j].pi_name; j++) { PyDict_SetItemString(indextable, it[j].pi_name, Py_BuildValue("K", it[j].pi_value)); } } else { Py_INCREF(Py_None); indextable = Py_None; } tuple = Py_BuildValue("sissKsissiiO", p->pd_name, p->pd_propnum, typetable[p->pd_proptype], p->pd_strdefault, p->pd_numdefault, attrtable[p->pd_attr], p->pd_types, p->pd_values, p->pd_colname, p->pd_rightalign, p->pd_visible, indextable); PyDict_SetItemString(d, p->pd_name, tuple); Py_DECREF(tuple); } return (d); } static PyMethodDef zfsmethods[] = { {"next_dataset", py_next_dataset, METH_VARARGS, "Get next child dataset or snapshot."}, {"get_fsacl", py_get_fsacl, METH_VARARGS, "Get allowed permissions."}, {"set_fsacl", py_set_fsacl, METH_VARARGS, "Set allowed permissions."}, {"userspace_many", py_userspace_many, METH_VARARGS, "Get user space accounting."}, {"userspace_upgrade", py_userspace_upgrade, METH_VARARGS, "Upgrade fs to enable user space accounting."}, {"set_cmdstr", py_set_cmdstr, METH_VARARGS, "Set command string for history logging."}, {"dataset_props", py_dataset_props, METH_VARARGS, "Get dataset properties."}, {"get_proptable", py_get_proptable, METH_NOARGS, "Get property table."}, /* Below are not really zfs-specific: */ {"sid_to_id", py_sid_to_id, METH_VARARGS, "Map SID to UID/GID."}, {"sid_to_name", py_sid_to_name, METH_VARARGS, "Map SID to name@domain."}, {"isglobalzone", py_isglobalzone, METH_NOARGS, "Determine if this is the global zone."}, {"get_holds", py_get_holds, METH_VARARGS, "Get user holds."}, {NULL, NULL, 0, NULL} }; void initioctl(void) { PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods); PyObject *zfs_util = PyImport_ImportModule("zfs.util"); PyObject *devfile; if (zfs_util == NULL) return; ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError"); devfile = PyObject_GetAttrString(zfs_util, "dev"); zfsdevfd = PyObject_AsFileDescriptor(devfile); zfs_prop_init(); }