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