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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #include <sys/zfs_ioctl.h>
28 #include <sys/fs/zfs.h>
31 #include <libnvpair.h>
36 #include <libzfs_impl.h>
39 static PyObject *ZFSError;
43 #define dgettext(x, y) y
46 #define _(s) dgettext(TEXT_DOMAIN, s)
49 extern int sid_to_id(char *sid, boolean_t user, uid_t *id);
54 seterr(char *fmt, ...)
60 (void) vsnprintf(errstr, sizeof (errstr), fmt, v);
63 PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr));
66 static char cmdstr[HIS_MAX_RECORD_LEN];
69 ioctl_with_cmdstr(unsigned long ioc, zfs_cmd_t *zc)
74 zc->zc_history = (uint64_t)(uintptr_t)cmdstr;
75 err = ioctl(zfsdevfd, ioc, zc);
88 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp;
89 nvp = nvlist_next_nvpair(nvl, nvp)) {
96 switch (nvpair_type(nvp)) {
97 case DATA_TYPE_STRING:
98 (void) nvpair_value_string(nvp, &sval);
99 pyval = Py_BuildValue("s", sval);
102 case DATA_TYPE_UINT64:
103 (void) nvpair_value_uint64(nvp, &ival);
104 pyval = Py_BuildValue("K", ival);
107 case DATA_TYPE_NVLIST:
108 (void) nvpair_value_nvlist(nvp, &nval);
109 pyval = nvl2py(nval);
112 case DATA_TYPE_BOOLEAN:
117 case DATA_TYPE_BOOLEAN_VALUE:
118 (void) nvpair_value_boolean_value(nvp, &bval);
119 pyval = Py_BuildValue("i", bval);
123 PyErr_SetNone(PyExc_ValueError);
128 PyDict_SetItemString(pyo, nvpair_name(nvp), pyval);
136 dict2nvl(PyObject *d)
140 PyObject *key, *value;
144 if (!PyDict_Check(d)) {
145 PyErr_SetObject(PyExc_ValueError, d);
149 err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
152 while (PyDict_Next(d, &pos, &key, &value)) {
153 char *keystr = PyString_AsString(key);
154 if (keystr == NULL) {
155 PyErr_SetObject(PyExc_KeyError, key);
160 if (PyDict_Check(value)) {
161 nvlist_t *valnvl = dict2nvl(value);
162 err = nvlist_add_nvlist(nvl, keystr, 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);
176 PyErr_SetObject(PyExc_ValueError, value);
187 fakepropval(uint64_t value)
189 PyObject *d = PyDict_New();
190 PyDict_SetItemString(d, "value", Py_BuildValue("K", value));
195 add_ds_props(zfs_cmd_t *zc, PyObject *nvl)
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));
206 /* On error, returns NULL but does not set python exception. */
208 ioctl_with_dstnv(unsigned long ioc, zfs_cmd_t *zc)
212 PyObject *pynv = NULL;
215 nvbuf = malloc(nvsz);
216 zc->zc_nvlist_dst_size = nvsz;
217 zc->zc_nvlist_dst = (uintptr_t)nvbuf;
219 if (ioctl(zfsdevfd, ioc, zc) == 0) {
222 errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0);
227 } else if (errno == ENOMEM) {
229 nvsz = zc->zc_nvlist_dst_size;
237 py_next_dataset(PyObject *self, PyObject *args)
241 zfs_cmd_t zc = { 0 };
245 PyObject *ret = NULL;
247 if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie))
250 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
251 zc.zc_cookie = cookie;
254 ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT;
256 ioc = ZFS_IOC_DATASET_LIST_NEXT;
258 nvl = ioctl_with_dstnv(ioc, &zc);
260 add_ds_props(&zc, nvl);
261 ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl);
263 } else if (errno == ESRCH) {
264 PyErr_SetNone(PyExc_StopIteration);
267 seterr(_("cannot get snapshots of %s"), name);
269 seterr(_("cannot get child datasets of %s"), name);
275 py_dataset_props(PyObject *self, PyObject *args)
277 zfs_cmd_t zc = { 0 };
282 if (!PyArg_ParseTuple(args, "s", &name))
285 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
287 nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc);
289 add_ds_props(&zc, nvl);
291 seterr(_("cannot access dataset %s"), name);
297 py_get_fsacl(PyObject *self, PyObject *args)
299 zfs_cmd_t zc = { 0 };
303 if (!PyArg_ParseTuple(args, "s", &name))
306 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
308 nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc);
310 seterr(_("cannot get permissions on %s"), name);
316 py_set_fsacl(PyObject *self, PyObject *args)
320 zfs_cmd_t zc = { 0 };
322 PyObject *dict, *file;
326 if (!PyArg_ParseTuple(args, "siO!", &name, &un,
327 &PyDict_Type, &dict))
330 nvl = dict2nvl(dict);
334 err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
336 nvbuf = malloc(nvsz);
337 err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
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;
345 err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc);
348 seterr(_("cannot set permissions on %s"), name);
356 py_userspace_many(PyObject *self, PyObject *args)
358 zfs_cmd_t zc = { 0 };
359 zfs_userquota_prop_t type;
360 char *name, *propname;
363 PyObject *dict, *file;
366 if (!PyArg_ParseTuple(args, "ss", &name, &propname))
369 for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++)
370 if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0)
372 if (type == ZFS_NUM_USERQUOTA_PROPS) {
373 PyErr_SetString(PyExc_KeyError, propname);
380 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
381 zc.zc_objset_type = type;
385 zfs_useracct_t *zua = buf;
387 zc.zc_nvlist_dst = (uintptr_t)buf;
388 zc.zc_nvlist_dst_size = bufsz;
390 error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc);
391 if (error || zc.zc_nvlist_dst_size == 0)
394 while (zc.zc_nvlist_dst_size > 0) {
395 PyObject *pykey, *pyval;
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);
405 zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
413 seterr(_("cannot get %s property on %s"), propname, name);
421 py_userspace_upgrade(PyObject *self, PyObject *args)
423 zfs_cmd_t zc = { 0 };
427 if (!PyArg_ParseTuple(args, "s", &name))
430 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
431 error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc);
434 seterr(_("cannot initialize user accounting information on %s"),
443 py_sid_to_id(PyObject *self, PyObject *args)
450 if (!PyArg_ParseTuple(args, "si", &sid, &isuser))
453 err = sid_to_id(sid, isuser, &id);
455 PyErr_SetString(PyExc_KeyError, sid);
459 return (Py_BuildValue("I", id));
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.
471 py_sid_to_name(PyObject *self, PyObject *args)
480 if (!PyArg_ParseTuple(args, "si", &sid, &isuser))
483 err = sid_to_id(sid, isuser, &id);
485 PyErr_SetString(PyExc_KeyError, sid);
490 err = idmap_getwinnamebyuid(id,
491 IDMAP_REQ_FLG_USE_CACHE, &name, &domain);
493 err = idmap_getwinnamebygid(id,
494 IDMAP_REQ_FLG_USE_CACHE, &name, &domain);
496 if (err != IDMAP_SUCCESS) {
497 PyErr_SetString(PyExc_KeyError, sid);
500 (void) snprintf(buf, sizeof (buf), "%s@%s", name, domain);
504 return (Py_BuildValue("s", buf));
511 py_isglobalzone(PyObject *self, PyObject *args)
513 return (Py_BuildValue("i", getzoneid() == GLOBAL_ZONEID));
517 py_set_cmdstr(PyObject *self, PyObject *args)
521 if (!PyArg_ParseTuple(args, "s", &str))
524 (void) strlcpy(cmdstr, str, sizeof (cmdstr));
530 py_get_proptable(PyObject *self, PyObject *args)
532 zprop_desc_t *t = zfs_prop_get_table();
533 PyObject *d = PyDict_New();
536 for (i = 0; i < ZFS_NUM_PROPS; i++) {
537 zprop_desc_t *p = &t[i];
539 static const char *typetable[] =
540 {"number", "string", "index"};
541 static const char *attrtable[] =
542 {"default", "readonly", "inherit", "onetime"};
543 PyObject *indextable;
545 if (p->pd_proptype == PROP_TYPE_INDEX) {
546 const zprop_index_t *it = p->pd_table;
547 indextable = PyDict_New();
549 for (j = 0; it[j].pi_name; j++) {
550 PyDict_SetItemString(indextable,
552 Py_BuildValue("K", it[j].pi_value));
556 indextable = Py_None;
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);
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}
599 PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods);
600 PyObject *zfs_util = PyImport_ImportModule("zfs.util");
603 if (zfs_util == NULL)
606 ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError");
607 devfile = PyObject_GetAttrString(zfs_util, "dev");
608 zfsdevfd = PyObject_AsFileDescriptor(devfile);