xref: /illumos-gate/usr/src/lib/pyzfs/common/ioctl.c (revision eed3be881cd8e53fe7187245b289da7106712dea)
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  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
25  */
26 
27 #include <Python.h>
28 #include <sys/zfs_ioctl.h>
29 #include <sys/fs/zfs.h>
30 #include <strings.h>
31 #include <unistd.h>
32 #include <libnvpair.h>
33 #include <libintl.h>
34 #include <libzfs.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 	Py_ssize_t 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 #if PY_MAJOR_VERSION >= 3
147 		char *keystr = PyBytes_AsString(key);
148 #else
149 		char *keystr = PyString_AsString(key);
150 #endif
151 		if (keystr == NULL) {
152 			PyErr_SetObject(PyExc_KeyError, key);
153 			nvlist_free(nvl);
154 			return (NULL);
155 		}
156 
157 		if (PyDict_Check(value)) {
158 			nvlist_t *valnvl = dict2nvl(value);
159 			err = nvlist_add_nvlist(nvl, keystr, valnvl);
160 			nvlist_free(valnvl);
161 		} else if (value == Py_None) {
162 			err = nvlist_add_boolean(nvl, keystr);
163 #if PY_MAJOR_VERSION >= 3
164 		} else if (PyBytes_Check(value)) {
165 #else
166 		} else if (PyString_Check(value)) {
167 #endif
168 #if PY_MAJOR_VERSION >= 3
169 			char *valstr = PyBytes_AsString(value);
170 #else
171 			char *valstr = PyString_AsString(value);
172 #endif
173 			err = nvlist_add_string(nvl, keystr, valstr);
174 		} else if (PyLong_Check(value)) {
175 			uint64_t valint = PyLong_AsUnsignedLongLongMask(value);
176 			err = nvlist_add_uint64(nvl, keystr, valint);
177 		} else if (PyBool_Check(value)) {
178 			boolean_t valbool = value == Py_True ? B_TRUE : B_FALSE;
179 			err = nvlist_add_boolean_value(nvl, keystr, valbool);
180 		} else {
181 			PyErr_SetObject(PyExc_ValueError, value);
182 			nvlist_free(nvl);
183 			return (NULL);
184 		}
185 		assert(err == 0);
186 	}
187 
188 	return (nvl);
189 }
190 
191 static PyObject *
192 fakepropval(uint64_t value)
193 {
194 	PyObject *d = PyDict_New();
195 	PyDict_SetItemString(d, "value", Py_BuildValue("K", value));
196 	return (d);
197 }
198 
199 static void
200 add_ds_props(zfs_cmd_t *zc, PyObject *nvl)
201 {
202 	dmu_objset_stats_t *s = &zc->zc_objset_stats;
203 	PyDict_SetItemString(nvl, "numclones",
204 	    fakepropval(s->dds_num_clones));
205 	PyDict_SetItemString(nvl, "issnap",
206 	    fakepropval(s->dds_is_snapshot));
207 	PyDict_SetItemString(nvl, "inconsistent",
208 	    fakepropval(s->dds_inconsistent));
209 }
210 
211 /* On error, returns NULL but does not set python exception. */
212 static PyObject *
213 ioctl_with_dstnv(int ioc, zfs_cmd_t *zc)
214 {
215 	int nvsz = 2048;
216 	void *nvbuf;
217 	PyObject *pynv = NULL;
218 
219 again:
220 	nvbuf = malloc(nvsz);
221 	zc->zc_nvlist_dst_size = nvsz;
222 	zc->zc_nvlist_dst = (uintptr_t)nvbuf;
223 
224 	if (ioctl(zfsdevfd, ioc, zc) == 0) {
225 		nvlist_t *nvl;
226 
227 		errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0);
228 		if (errno == 0) {
229 			pynv = nvl2py(nvl);
230 			nvlist_free(nvl);
231 		}
232 	} else if (errno == ENOMEM) {
233 		free(nvbuf);
234 		nvsz = zc->zc_nvlist_dst_size;
235 		goto again;
236 	}
237 	free(nvbuf);
238 	return (pynv);
239 }
240 
241 static PyObject *
242 py_next_dataset(PyObject *self, PyObject *args)
243 {
244 	int ioc;
245 	uint64_t cookie;
246 	zfs_cmd_t zc = { 0 };
247 	int snaps;
248 	char *name;
249 	PyObject *nvl;
250 	PyObject *ret = NULL;
251 
252 	if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie))
253 		return (NULL);
254 
255 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
256 	zc.zc_cookie = cookie;
257 
258 	if (snaps)
259 		ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT;
260 	else
261 		ioc = ZFS_IOC_DATASET_LIST_NEXT;
262 
263 	nvl = ioctl_with_dstnv(ioc, &zc);
264 	if (nvl) {
265 		add_ds_props(&zc, nvl);
266 		ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl);
267 		Py_DECREF(nvl);
268 	} else if (errno == ESRCH) {
269 		PyErr_SetNone(PyExc_StopIteration);
270 	} else {
271 		if (snaps)
272 			seterr(_("cannot get snapshots of %s"), name);
273 		else
274 			seterr(_("cannot get child datasets of %s"), name);
275 	}
276 	return (ret);
277 }
278 
279 static PyObject *
280 py_dataset_props(PyObject *self, PyObject *args)
281 {
282 	zfs_cmd_t zc = { 0 };
283 	int snaps;
284 	char *name;
285 	PyObject *nvl;
286 
287 	if (!PyArg_ParseTuple(args, "s", &name))
288 		return (NULL);
289 
290 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
291 
292 	nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc);
293 	if (nvl) {
294 		add_ds_props(&zc, nvl);
295 	} else {
296 		seterr(_("cannot access dataset %s"), name);
297 	}
298 	return (nvl);
299 }
300 
301 static PyObject *
302 py_get_fsacl(PyObject *self, PyObject *args)
303 {
304 	zfs_cmd_t zc = { 0 };
305 	char *name;
306 	PyObject *nvl;
307 
308 	if (!PyArg_ParseTuple(args, "s", &name))
309 		return (NULL);
310 
311 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
312 
313 	nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc);
314 	if (nvl == NULL)
315 		seterr(_("cannot get permissions on %s"), name);
316 
317 	return (nvl);
318 }
319 
320 static PyObject *
321 py_set_fsacl(PyObject *self, PyObject *args)
322 {
323 	int un;
324 	size_t nvsz;
325 	zfs_cmd_t zc = { 0 };
326 	char *name, *nvbuf;
327 	PyObject *dict, *file;
328 	nvlist_t *nvl;
329 	int err;
330 
331 	if (!PyArg_ParseTuple(args, "siO!", &name, &un,
332 	    &PyDict_Type, &dict))
333 		return (NULL);
334 
335 	nvl = dict2nvl(dict);
336 	if (nvl == NULL)
337 		return (NULL);
338 
339 	err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
340 	assert(err == 0);
341 	nvbuf = malloc(nvsz);
342 	err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
343 	assert(err == 0);
344 
345 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
346 	zc.zc_nvlist_src_size = nvsz;
347 	zc.zc_nvlist_src = (uintptr_t)nvbuf;
348 	zc.zc_perm_action = un;
349 
350 	err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc);
351 	free(nvbuf);
352 	if (err) {
353 		seterr(_("cannot set permissions on %s"), name);
354 		return (NULL);
355 	}
356 
357 	Py_RETURN_NONE;
358 }
359 
360 static PyObject *
361 py_get_holds(PyObject *self, PyObject *args)
362 {
363 	zfs_cmd_t zc = { 0 };
364 	char *name;
365 	PyObject *nvl;
366 
367 	if (!PyArg_ParseTuple(args, "s", &name))
368 		return (NULL);
369 
370 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
371 
372 	nvl = ioctl_with_dstnv(ZFS_IOC_GET_HOLDS, &zc);
373 	if (nvl == NULL)
374 		seterr(_("cannot get holds for %s"), name);
375 
376 	return (nvl);
377 }
378 
379 static PyObject *
380 py_userspace_many(PyObject *self, PyObject *args)
381 {
382 	zfs_cmd_t zc = { 0 };
383 	zfs_userquota_prop_t type;
384 	char *name, *propname;
385 	int bufsz = 1<<20;
386 	void *buf;
387 	PyObject *dict, *file;
388 	int error;
389 
390 	if (!PyArg_ParseTuple(args, "ss", &name, &propname))
391 		return (NULL);
392 
393 	for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++)
394 		if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0)
395 			break;
396 	if (type == ZFS_NUM_USERQUOTA_PROPS) {
397 		PyErr_SetString(PyExc_KeyError, propname);
398 		return (NULL);
399 	}
400 
401 	dict = PyDict_New();
402 	buf = malloc(bufsz);
403 
404 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
405 	zc.zc_objset_type = type;
406 	zc.zc_cookie = 0;
407 
408 	while (1) {
409 		zfs_useracct_t *zua = buf;
410 
411 		zc.zc_nvlist_dst = (uintptr_t)buf;
412 		zc.zc_nvlist_dst_size = bufsz;
413 
414 		error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc);
415 		if (error || zc.zc_nvlist_dst_size == 0)
416 			break;
417 
418 		while (zc.zc_nvlist_dst_size > 0) {
419 			PyObject *pykey, *pyval;
420 
421 			pykey = Py_BuildValue("sI",
422 			    zua->zu_domain, zua->zu_rid);
423 			pyval = Py_BuildValue("K", zua->zu_space);
424 			PyDict_SetItem(dict, pykey, pyval);
425 			Py_DECREF(pykey);
426 			Py_DECREF(pyval);
427 
428 			zua++;
429 			zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
430 		}
431 	}
432 
433 	free(buf);
434 
435 	if (error != 0) {
436 		Py_DECREF(dict);
437 		seterr(_("cannot get %s property on %s"), propname, name);
438 		return (NULL);
439 	}
440 
441 	return (dict);
442 }
443 
444 static PyObject *
445 py_userspace_upgrade(PyObject *self, PyObject *args)
446 {
447 	zfs_cmd_t zc = { 0 };
448 	char *name;
449 	int error;
450 
451 	if (!PyArg_ParseTuple(args, "s", &name))
452 		return (NULL);
453 
454 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
455 	error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc);
456 
457 	if (error != 0) {
458 		seterr(_("cannot initialize user accounting information on %s"),
459 		    name);
460 		return (NULL);
461 	}
462 
463 	Py_RETURN_NONE;
464 }
465 
466 static PyObject *
467 py_set_cmdstr(PyObject *self, PyObject *args)
468 {
469 	char *str;
470 
471 	if (!PyArg_ParseTuple(args, "s", &str))
472 		return (NULL);
473 
474 	(void) strlcpy(cmdstr, str, sizeof (cmdstr));
475 
476 	Py_RETURN_NONE;
477 }
478 
479 static PyObject *
480 py_get_proptable(PyObject *self, PyObject *args)
481 {
482 	zprop_desc_t *t = zfs_prop_get_table();
483 	PyObject *d = PyDict_New();
484 	zfs_prop_t i;
485 
486 	for (i = 0; i < ZFS_NUM_PROPS; i++) {
487 		zprop_desc_t *p = &t[i];
488 		PyObject *tuple;
489 		static const char *typetable[] =
490 		    {"number", "string", "index"};
491 		static const char *attrtable[] =
492 		    {"default", "readonly", "inherit", "onetime"};
493 		PyObject *indextable;
494 
495 		if (p->pd_proptype == PROP_TYPE_INDEX) {
496 			const zprop_index_t *it = p->pd_table;
497 			indextable = PyDict_New();
498 			int j;
499 			for (j = 0; it[j].pi_name; j++) {
500 				PyDict_SetItemString(indextable,
501 				    it[j].pi_name,
502 				    Py_BuildValue("K", it[j].pi_value));
503 			}
504 		} else {
505 			Py_INCREF(Py_None);
506 			indextable = Py_None;
507 		}
508 
509 		tuple = Py_BuildValue("sissKsissiiO",
510 		    p->pd_name, p->pd_propnum, typetable[p->pd_proptype],
511 		    p->pd_strdefault, p->pd_numdefault,
512 		    attrtable[p->pd_attr], p->pd_types,
513 		    p->pd_values, p->pd_colname,
514 		    p->pd_rightalign, p->pd_visible, indextable);
515 		PyDict_SetItemString(d, p->pd_name, tuple);
516 		Py_DECREF(tuple);
517 	}
518 
519 	return (d);
520 }
521 
522 static PyMethodDef zfsmethods[] = {
523 	{"next_dataset", py_next_dataset, METH_VARARGS,
524 	    "Get next child dataset or snapshot."},
525 	{"get_fsacl", py_get_fsacl, METH_VARARGS, "Get allowed permissions."},
526 	{"set_fsacl", py_set_fsacl, METH_VARARGS, "Set allowed permissions."},
527 	{"userspace_many", py_userspace_many, METH_VARARGS,
528 	    "Get user space accounting."},
529 	{"userspace_upgrade", py_userspace_upgrade, METH_VARARGS,
530 	    "Upgrade fs to enable user space accounting."},
531 	{"set_cmdstr", py_set_cmdstr, METH_VARARGS,
532 	    "Set command string for history logging."},
533 	{"dataset_props", py_dataset_props, METH_VARARGS,
534 	    "Get dataset properties."},
535 	{"get_proptable", py_get_proptable, METH_NOARGS,
536 	    "Get property table."},
537 	{"get_holds", py_get_holds, METH_VARARGS, "Get user holds."},
538 	{NULL, NULL, 0, NULL}
539 };
540 
541 #if PY_MAJOR_VERSION >= 3
542 static struct PyModuleDef zfs_module = {
543 	PyModuleDef_HEAD_INIT,
544 	"zfs.ioctl",
545 	NULL,
546 	-1,
547 	zfsmethods
548 };
549 #endif
550 
551 static PyObject *
552 moduleinit()
553 {
554 	PyObject *zfs_ioctl, *zfs_util, *devfile;
555 #if PY_MAJOR_VERSION >= 3
556 	zfs_ioctl = PyModule_Create(&zfs_module);
557 #else
558 	zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods);
559 #endif
560 	zfs_util = PyImport_ImportModule("zfs.util");
561 
562 	if (zfs_util == NULL)
563 		return (NULL);
564 
565 	ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError");
566 	devfile = PyObject_GetAttrString(zfs_util, "dev");
567 	zfsdevfd = PyObject_AsFileDescriptor(devfile);
568 
569 	zfs_prop_init();
570 
571 	return (zfs_ioctl);
572 }
573 
574 #if PY_MAJOR_VERSION >= 3
575 PyMODINIT_FUNC
576 PyInit_ioctl(void)
577 {
578 	return (moduleinit());
579 }
580 #else
581 PyMODINIT_FUNC
582 initioctl(void)
583 {
584 	(void) moduleinit();
585 }
586 #endif
587