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