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