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