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