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