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 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 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 * 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 * 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 * 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 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 578 PyInit_ioctl(void) 579 { 580 return (moduleinit()); 581 } 582 #else 583 PyMODINIT_FUNC 584 initioctl(void) 585 { 586 (void) moduleinit(); 587 } 588 #endif 589