Home | History | Annotate | Line # | Download | only in common
ioctl.c revision 1.1.1.1
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <Python.h>
     27 #include <sys/zfs_ioctl.h>
     28 #include <sys/fs/zfs.h>
     29 #include <strings.h>
     30 #include <unistd.h>
     31 #include <libnvpair.h>
     32 #include <libintl.h>
     33 #include <libzfs.h>
     34 #include <libzfs_impl.h>
     35 #include "zfs_prop.h"
     36 
     37 static PyObject *ZFSError;
     38 static int zfsdevfd;
     39 
     40 #ifdef __lint
     41 #define	dgettext(x, y) y
     42 #endif
     43 
     44 #define	_(s) dgettext(TEXT_DOMAIN, s)
     45 
     46 /*PRINTFLIKE1*/
     47 static void
     48 seterr(char *fmt, ...)
     49 {
     50 	char errstr[1024];
     51 	va_list v;
     52 
     53 	va_start(v, fmt);
     54 	(void) vsnprintf(errstr, sizeof (errstr), fmt, v);
     55 	va_end(v);
     56 
     57 	PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr));
     58 }
     59 
     60 static char cmdstr[HIS_MAX_RECORD_LEN];
     61 
     62 static int
     63 ioctl_with_cmdstr(int ioc, zfs_cmd_t *zc)
     64 {
     65 	int err;
     66 
     67 	if (cmdstr[0])
     68 		zc->zc_history = (uint64_t)(uintptr_t)cmdstr;
     69 	err = ioctl(zfsdevfd, ioc, zc);
     70 	cmdstr[0] = '\0';
     71 	return (err);
     72 }
     73 
     74 static PyObject *
     75 nvl2py(nvlist_t *nvl)
     76 {
     77 	PyObject *pyo;
     78 	nvpair_t *nvp;
     79 
     80 	pyo = PyDict_New();
     81 
     82 	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp;
     83 	    nvp = nvlist_next_nvpair(nvl, nvp)) {
     84 		PyObject *pyval;
     85 		char *sval;
     86 		uint64_t ival;
     87 		boolean_t bval;
     88 		nvlist_t *nval;
     89 
     90 		switch (nvpair_type(nvp)) {
     91 		case DATA_TYPE_STRING:
     92 			(void) nvpair_value_string(nvp, &sval);
     93 			pyval = Py_BuildValue("s", sval);
     94 			break;
     95 
     96 		case DATA_TYPE_UINT64:
     97 			(void) nvpair_value_uint64(nvp, &ival);
     98 			pyval = Py_BuildValue("K", ival);
     99 			break;
    100 
    101 		case DATA_TYPE_NVLIST:
    102 			(void) nvpair_value_nvlist(nvp, &nval);
    103 			pyval = nvl2py(nval);
    104 			break;
    105 
    106 		case DATA_TYPE_BOOLEAN:
    107 			Py_INCREF(Py_None);
    108 			pyval = Py_None;
    109 			break;
    110 
    111 		case DATA_TYPE_BOOLEAN_VALUE:
    112 			(void) nvpair_value_boolean_value(nvp, &bval);
    113 			pyval = Py_BuildValue("i", bval);
    114 			break;
    115 
    116 		default:
    117 			PyErr_SetNone(PyExc_ValueError);
    118 			Py_DECREF(pyo);
    119 			return (NULL);
    120 		}
    121 
    122 		PyDict_SetItemString(pyo, nvpair_name(nvp), pyval);
    123 		Py_DECREF(pyval);
    124 	}
    125 
    126 	return (pyo);
    127 }
    128 
    129 static nvlist_t *
    130 dict2nvl(PyObject *d)
    131 {
    132 	nvlist_t *nvl;
    133 	int err;
    134 	PyObject *key, *value;
    135 	int pos = 0;
    136 
    137 	if (!PyDict_Check(d)) {
    138 		PyErr_SetObject(PyExc_ValueError, d);
    139 		return (NULL);
    140 	}
    141 
    142 	err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
    143 	assert(err == 0);
    144 
    145 	while (PyDict_Next(d, &pos, &key, &value)) {
    146 		char *keystr = PyString_AsString(key);
    147 		if (keystr == NULL) {
    148 			PyErr_SetObject(PyExc_KeyError, key);
    149 			nvlist_free(nvl);
    150 			return (NULL);
    151 		}
    152 
    153 		if (PyDict_Check(value)) {
    154 			nvlist_t *valnvl = dict2nvl(value);
    155 			err = nvlist_add_nvlist(nvl, keystr, valnvl);
    156 			nvlist_free(valnvl);
    157 		} else if (value == Py_None) {
    158 			err = nvlist_add_boolean(nvl, keystr);
    159 		} else if (PyString_Check(value)) {
    160 			char *valstr = PyString_AsString(value);
    161 			err = nvlist_add_string(nvl, keystr, valstr);
    162 		} else if (PyInt_Check(value)) {
    163 			uint64_t valint = PyInt_AsUnsignedLongLongMask(value);
    164 			err = nvlist_add_uint64(nvl, keystr, valint);
    165 		} else if (PyBool_Check(value)) {
    166 			boolean_t valbool = value == Py_True ? B_TRUE : B_FALSE;
    167 			err = nvlist_add_boolean_value(nvl, keystr, valbool);
    168 		} else {
    169 			PyErr_SetObject(PyExc_ValueError, value);
    170 			nvlist_free(nvl);
    171 			return (NULL);
    172 		}
    173 		assert(err == 0);
    174 	}
    175 
    176 	return (nvl);
    177 }
    178 
    179 static PyObject *
    180 fakepropval(uint64_t value)
    181 {
    182 	PyObject *d = PyDict_New();
    183 	PyDict_SetItemString(d, "value", Py_BuildValue("K", value));
    184 	return (d);
    185 }
    186 
    187 static void
    188 add_ds_props(zfs_cmd_t *zc, PyObject *nvl)
    189 {
    190 	dmu_objset_stats_t *s = &zc->zc_objset_stats;
    191 	PyDict_SetItemString(nvl, "numclones",
    192 	    fakepropval(s->dds_num_clones));
    193 	PyDict_SetItemString(nvl, "issnap",
    194 	    fakepropval(s->dds_is_snapshot));
    195 	PyDict_SetItemString(nvl, "inconsistent",
    196 	    fakepropval(s->dds_inconsistent));
    197 }
    198 
    199 /* On error, returns NULL but does not set python exception. */
    200 static PyObject *
    201 ioctl_with_dstnv(int ioc, zfs_cmd_t *zc)
    202 {
    203 	int nvsz = 2048;
    204 	void *nvbuf;
    205 	PyObject *pynv = NULL;
    206 
    207 again:
    208 	nvbuf = malloc(nvsz);
    209 	zc->zc_nvlist_dst_size = nvsz;
    210 	zc->zc_nvlist_dst = (uintptr_t)nvbuf;
    211 
    212 	if (ioctl(zfsdevfd, ioc, zc) == 0) {
    213 		nvlist_t *nvl;
    214 
    215 		errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0);
    216 		if (errno == 0) {
    217 			pynv = nvl2py(nvl);
    218 			nvlist_free(nvl);
    219 		}
    220 	} else if (errno == ENOMEM) {
    221 		free(nvbuf);
    222 		nvsz = zc->zc_nvlist_dst_size;
    223 		goto again;
    224 	}
    225 	free(nvbuf);
    226 	return (pynv);
    227 }
    228 
    229 static PyObject *
    230 py_next_dataset(PyObject *self, PyObject *args)
    231 {
    232 	int ioc;
    233 	uint64_t cookie;
    234 	zfs_cmd_t zc = { 0 };
    235 	int snaps;
    236 	char *name;
    237 	PyObject *nvl;
    238 	PyObject *ret = NULL;
    239 
    240 	if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie))
    241 		return (NULL);
    242 
    243 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
    244 	zc.zc_cookie = cookie;
    245 
    246 	if (snaps)
    247 		ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT;
    248 	else
    249 		ioc = ZFS_IOC_DATASET_LIST_NEXT;
    250 
    251 	nvl = ioctl_with_dstnv(ioc, &zc);
    252 	if (nvl) {
    253 		add_ds_props(&zc, nvl);
    254 		ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl);
    255 		Py_DECREF(nvl);
    256 	} else if (errno == ESRCH) {
    257 		PyErr_SetNone(PyExc_StopIteration);
    258 	} else {
    259 		if (snaps)
    260 			seterr(_("cannot get snapshots of %s"), name);
    261 		else
    262 			seterr(_("cannot get child datasets of %s"), name);
    263 	}
    264 	return (ret);
    265 }
    266 
    267 static PyObject *
    268 py_dataset_props(PyObject *self, PyObject *args)
    269 {
    270 	zfs_cmd_t zc = { 0 };
    271 	int snaps;
    272 	char *name;
    273 	PyObject *nvl;
    274 
    275 	if (!PyArg_ParseTuple(args, "s", &name))
    276 		return (NULL);
    277 
    278 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
    279 
    280 	nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc);
    281 	if (nvl) {
    282 		add_ds_props(&zc, nvl);
    283 	} else {
    284 		seterr(_("cannot access dataset %s"), name);
    285 	}
    286 	return (nvl);
    287 }
    288 
    289 static PyObject *
    290 py_get_fsacl(PyObject *self, PyObject *args)
    291 {
    292 	zfs_cmd_t zc = { 0 };
    293 	char *name;
    294 	PyObject *nvl;
    295 
    296 	if (!PyArg_ParseTuple(args, "s", &name))
    297 		return (NULL);
    298 
    299 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
    300 
    301 	nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc);
    302 	if (nvl == NULL)
    303 		seterr(_("cannot get permissions on %s"), name);
    304 
    305 	return (nvl);
    306 }
    307 
    308 static PyObject *
    309 py_set_fsacl(PyObject *self, PyObject *args)
    310 {
    311 	int un;
    312 	size_t nvsz;
    313 	zfs_cmd_t zc = { 0 };
    314 	char *name, *nvbuf;
    315 	PyObject *dict, *file;
    316 	nvlist_t *nvl;
    317 	int err;
    318 
    319 	if (!PyArg_ParseTuple(args, "siO!", &name, &un,
    320 	    &PyDict_Type, &dict))
    321 		return (NULL);
    322 
    323 	nvl = dict2nvl(dict);
    324 	if (nvl == NULL)
    325 		return (NULL);
    326 
    327 	err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
    328 	assert(err == 0);
    329 	nvbuf = malloc(nvsz);
    330 	err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
    331 	assert(err == 0);
    332 
    333 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
    334 	zc.zc_nvlist_src_size = nvsz;
    335 	zc.zc_nvlist_src = (uintptr_t)nvbuf;
    336 	zc.zc_perm_action = un;
    337 
    338 	err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc);
    339 	free(nvbuf);
    340 	if (err) {
    341 		seterr(_("cannot set permissions on %s"), name);
    342 		return (NULL);
    343 	}
    344 
    345 	Py_RETURN_NONE;
    346 }
    347 
    348 static PyObject *
    349 py_get_holds(PyObject *self, PyObject *args)
    350 {
    351 	zfs_cmd_t zc = { 0 };
    352 	char *name;
    353 	PyObject *nvl;
    354 
    355 	if (!PyArg_ParseTuple(args, "s", &name))
    356 		return (NULL);
    357 
    358 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
    359 
    360 	nvl = ioctl_with_dstnv(ZFS_IOC_GET_HOLDS, &zc);
    361 	if (nvl == NULL)
    362 		seterr(_("cannot get holds for %s"), name);
    363 
    364 	return (nvl);
    365 }
    366 
    367 static PyObject *
    368 py_userspace_many(PyObject *self, PyObject *args)
    369 {
    370 	zfs_cmd_t zc = { 0 };
    371 	zfs_userquota_prop_t type;
    372 	char *name, *propname;
    373 	int bufsz = 1<<20;
    374 	void *buf;
    375 	PyObject *dict, *file;
    376 	int error;
    377 
    378 	if (!PyArg_ParseTuple(args, "ss", &name, &propname))
    379 		return (NULL);
    380 
    381 	for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++)
    382 		if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0)
    383 			break;
    384 	if (type == ZFS_NUM_USERQUOTA_PROPS) {
    385 		PyErr_SetString(PyExc_KeyError, propname);
    386 		return (NULL);
    387 	}
    388 
    389 	dict = PyDict_New();
    390 	buf = malloc(bufsz);
    391 
    392 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
    393 	zc.zc_objset_type = type;
    394 	zc.zc_cookie = 0;
    395 
    396 	while (1) {
    397 		zfs_useracct_t *zua = buf;
    398 
    399 		zc.zc_nvlist_dst = (uintptr_t)buf;
    400 		zc.zc_nvlist_dst_size = bufsz;
    401 
    402 		error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc);
    403 		if (error || zc.zc_nvlist_dst_size == 0)
    404 			break;
    405 
    406 		while (zc.zc_nvlist_dst_size > 0) {
    407 			PyObject *pykey, *pyval;
    408 
    409 			pykey = Py_BuildValue("sI",
    410 			    zua->zu_domain, zua->zu_rid);
    411 			pyval = Py_BuildValue("K", zua->zu_space);
    412 			PyDict_SetItem(dict, pykey, pyval);
    413 			Py_DECREF(pykey);
    414 			Py_DECREF(pyval);
    415 
    416 			zua++;
    417 			zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
    418 		}
    419 	}
    420 
    421 	free(buf);
    422 
    423 	if (error != 0) {
    424 		Py_DECREF(dict);
    425 		seterr(_("cannot get %s property on %s"), propname, name);
    426 		return (NULL);
    427 	}
    428 
    429 	return (dict);
    430 }
    431 
    432 static PyObject *
    433 py_userspace_upgrade(PyObject *self, PyObject *args)
    434 {
    435 	zfs_cmd_t zc = { 0 };
    436 	char *name;
    437 	int error;
    438 
    439 	if (!PyArg_ParseTuple(args, "s", &name))
    440 		return (NULL);
    441 
    442 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
    443 	error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc);
    444 
    445 	if (error != 0) {
    446 		seterr(_("cannot initialize user accounting information on %s"),
    447 		    name);
    448 		return (NULL);
    449 	}
    450 
    451 	Py_RETURN_NONE;
    452 }
    453 
    454 static PyObject *
    455 py_set_cmdstr(PyObject *self, PyObject *args)
    456 {
    457 	char *str;
    458 
    459 	if (!PyArg_ParseTuple(args, "s", &str))
    460 		return (NULL);
    461 
    462 	(void) strlcpy(cmdstr, str, sizeof (cmdstr));
    463 
    464 	Py_RETURN_NONE;
    465 }
    466 
    467 static PyObject *
    468 py_get_proptable(PyObject *self, PyObject *args)
    469 {
    470 	zprop_desc_t *t = zfs_prop_get_table();
    471 	PyObject *d = PyDict_New();
    472 	zfs_prop_t i;
    473 
    474 	for (i = 0; i < ZFS_NUM_PROPS; i++) {
    475 		zprop_desc_t *p = &t[i];
    476 		PyObject *tuple;
    477 		static const char *typetable[] =
    478 		    {"number", "string", "index"};
    479 		static const char *attrtable[] =
    480 		    {"default", "readonly", "inherit", "onetime"};
    481 		PyObject *indextable;
    482 
    483 		if (p->pd_proptype == PROP_TYPE_INDEX) {
    484 			const zprop_index_t *it = p->pd_table;
    485 			indextable = PyDict_New();
    486 			int j;
    487 			for (j = 0; it[j].pi_name; j++) {
    488 				PyDict_SetItemString(indextable,
    489 				    it[j].pi_name,
    490 				    Py_BuildValue("K", it[j].pi_value));
    491 			}
    492 		} else {
    493 			Py_INCREF(Py_None);
    494 			indextable = Py_None;
    495 		}
    496 
    497 		tuple = Py_BuildValue("sissKsissiiO",
    498 		    p->pd_name, p->pd_propnum, typetable[p->pd_proptype],
    499 		    p->pd_strdefault, p->pd_numdefault,
    500 		    attrtable[p->pd_attr], p->pd_types,
    501 		    p->pd_values, p->pd_colname,
    502 		    p->pd_rightalign, p->pd_visible, indextable);
    503 		PyDict_SetItemString(d, p->pd_name, tuple);
    504 		Py_DECREF(tuple);
    505 	}
    506 
    507 	return (d);
    508 }
    509 
    510 static PyMethodDef zfsmethods[] = {
    511 	{"next_dataset", py_next_dataset, METH_VARARGS,
    512 	    "Get next child dataset or snapshot."},
    513 	{"get_fsacl", py_get_fsacl, METH_VARARGS, "Get allowed permissions."},
    514 	{"set_fsacl", py_set_fsacl, METH_VARARGS, "Set allowed permissions."},
    515 	{"userspace_many", py_userspace_many, METH_VARARGS,
    516 	    "Get user space accounting."},
    517 	{"userspace_upgrade", py_userspace_upgrade, METH_VARARGS,
    518 	    "Upgrade fs to enable user space accounting."},
    519 	{"set_cmdstr", py_set_cmdstr, METH_VARARGS,
    520 	    "Set command string for history logging."},
    521 	{"dataset_props", py_dataset_props, METH_VARARGS,
    522 	    "Get dataset properties."},
    523 	{"get_proptable", py_get_proptable, METH_NOARGS,
    524 	    "Get property table."},
    525 	{"get_holds", py_get_holds, METH_VARARGS, "Get user holds."},
    526 	{NULL, NULL, 0, NULL}
    527 };
    528 
    529 void
    530 initioctl(void)
    531 {
    532 	PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods);
    533 	PyObject *zfs_util = PyImport_ImportModule("zfs.util");
    534 	PyObject *devfile;
    535 
    536 	if (zfs_util == NULL)
    537 		return;
    538 
    539 	ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError");
    540 	devfile = PyObject_GetAttrString(zfs_util, "dev");
    541 	zfsdevfd = PyObject_AsFileDescriptor(devfile);
    542 
    543 	zfs_prop_init();
    544 }
    545