ioctl.c revision 211970
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 <libzfs_impl.h> 37#include "zfs_prop.h" 38 39static PyObject *ZFSError; 40static int zfsdevfd; 41 42#ifdef __lint 43#define dgettext(x, y) y 44#endif 45 46#define _(s) dgettext(TEXT_DOMAIN, s) 47 48#ifdef sun 49extern int sid_to_id(char *sid, boolean_t user, uid_t *id); 50#endif /* sun */ 51 52/*PRINTFLIKE1*/ 53static void 54seterr(char *fmt, ...) 55{ 56 char errstr[1024]; 57 va_list v; 58 59 va_start(v, fmt); 60 (void) vsnprintf(errstr, sizeof (errstr), fmt, v); 61 va_end(v); 62 63 PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr)); 64} 65 66static char cmdstr[HIS_MAX_RECORD_LEN]; 67 68static int 69ioctl_with_cmdstr(unsigned long ioc, zfs_cmd_t *zc) 70{ 71 int err; 72 73 if (cmdstr[0]) 74 zc->zc_history = (uint64_t)(uintptr_t)cmdstr; 75 err = ioctl(zfsdevfd, ioc, zc); 76 cmdstr[0] = '\0'; 77 return (err); 78} 79 80static PyObject * 81nvl2py(nvlist_t *nvl) 82{ 83 PyObject *pyo; 84 nvpair_t *nvp; 85 86 pyo = PyDict_New(); 87 88 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp; 89 nvp = nvlist_next_nvpair(nvl, nvp)) { 90 PyObject *pyval; 91 char *sval; 92 uint64_t ival; 93 boolean_t bval; 94 nvlist_t *nval; 95 96 switch (nvpair_type(nvp)) { 97 case DATA_TYPE_STRING: 98 (void) nvpair_value_string(nvp, &sval); 99 pyval = Py_BuildValue("s", sval); 100 break; 101 102 case DATA_TYPE_UINT64: 103 (void) nvpair_value_uint64(nvp, &ival); 104 pyval = Py_BuildValue("K", ival); 105 break; 106 107 case DATA_TYPE_NVLIST: 108 (void) nvpair_value_nvlist(nvp, &nval); 109 pyval = nvl2py(nval); 110 break; 111 112 case DATA_TYPE_BOOLEAN: 113 Py_INCREF(Py_None); 114 pyval = Py_None; 115 break; 116 117 case DATA_TYPE_BOOLEAN_VALUE: 118 (void) nvpair_value_boolean_value(nvp, &bval); 119 pyval = Py_BuildValue("i", bval); 120 break; 121 122 default: 123 PyErr_SetNone(PyExc_ValueError); 124 Py_DECREF(pyo); 125 return (NULL); 126 } 127 128 PyDict_SetItemString(pyo, nvpair_name(nvp), pyval); 129 Py_DECREF(pyval); 130 } 131 132 return (pyo); 133} 134 135static nvlist_t * 136dict2nvl(PyObject *d) 137{ 138 nvlist_t *nvl; 139 int err; 140 PyObject *key, *value; 141// int pos = 0; 142 Py_ssize_t pos = 0; 143 144 if (!PyDict_Check(d)) { 145 PyErr_SetObject(PyExc_ValueError, d); 146 return (NULL); 147 } 148 149 err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0); 150 assert(err == 0); 151 152 while (PyDict_Next(d, &pos, &key, &value)) { 153 char *keystr = PyString_AsString(key); 154 if (keystr == NULL) { 155 PyErr_SetObject(PyExc_KeyError, key); 156 nvlist_free(nvl); 157 return (NULL); 158 } 159 160 if (PyDict_Check(value)) { 161 nvlist_t *valnvl = dict2nvl(value); 162 err = nvlist_add_nvlist(nvl, keystr, valnvl); 163 nvlist_free(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); 175 } else { 176 PyErr_SetObject(PyExc_ValueError, value); 177 nvlist_free(nvl); 178 return (NULL); 179 } 180 assert(err == 0); 181 } 182 183 return (nvl); 184} 185 186static PyObject * 187fakepropval(uint64_t value) 188{ 189 PyObject *d = PyDict_New(); 190 PyDict_SetItemString(d, "value", Py_BuildValue("K", value)); 191 return (d); 192} 193 194static void 195add_ds_props(zfs_cmd_t *zc, PyObject *nvl) 196{ 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)); 204} 205 206/* On error, returns NULL but does not set python exception. */ 207static PyObject * 208ioctl_with_dstnv(unsigned long ioc, zfs_cmd_t *zc) 209{ 210 int nvsz = 2048; 211 void *nvbuf; 212 PyObject *pynv = NULL; 213 214again: 215 nvbuf = malloc(nvsz); 216 zc->zc_nvlist_dst_size = nvsz; 217 zc->zc_nvlist_dst = (uintptr_t)nvbuf; 218 219 if (ioctl(zfsdevfd, ioc, zc) == 0) { 220 nvlist_t *nvl; 221 222 errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0); 223 if (errno == 0) { 224 pynv = nvl2py(nvl); 225 nvlist_free(nvl); 226 } 227 } else if (errno == ENOMEM) { 228 free(nvbuf); 229 nvsz = zc->zc_nvlist_dst_size; 230 goto again; 231 } 232 free(nvbuf); 233 return (pynv); 234} 235 236static PyObject * 237py_next_dataset(PyObject *self, PyObject *args) 238{ 239 unsigned long ioc; 240 uint64_t cookie; 241 zfs_cmd_t zc = { 0 }; 242 int snaps; 243 char *name; 244 PyObject *nvl; 245 PyObject *ret = NULL; 246 247 if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie)) 248 return (NULL); 249 250 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 251 zc.zc_cookie = cookie; 252 253 if (snaps) 254 ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT; 255 else 256 ioc = ZFS_IOC_DATASET_LIST_NEXT; 257 258 nvl = ioctl_with_dstnv(ioc, &zc); 259 if (nvl) { 260 add_ds_props(&zc, nvl); 261 ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl); 262 Py_DECREF(nvl); 263 } else if (errno == ESRCH) { 264 PyErr_SetNone(PyExc_StopIteration); 265 } else { 266 if (snaps) 267 seterr(_("cannot get snapshots of %s"), name); 268 else 269 seterr(_("cannot get child datasets of %s"), name); 270 } 271 return (ret); 272} 273 274static PyObject * 275py_dataset_props(PyObject *self, PyObject *args) 276{ 277 zfs_cmd_t zc = { 0 }; 278 int snaps; 279 char *name; 280 PyObject *nvl; 281 282 if (!PyArg_ParseTuple(args, "s", &name)) 283 return (NULL); 284 285 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 286 287 nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc); 288 if (nvl) { 289 add_ds_props(&zc, nvl); 290 } else { 291 seterr(_("cannot access dataset %s"), name); 292 } 293 return (nvl); 294} 295 296static PyObject * 297py_get_fsacl(PyObject *self, PyObject *args) 298{ 299 zfs_cmd_t zc = { 0 }; 300 char *name; 301 PyObject *nvl; 302 303 if (!PyArg_ParseTuple(args, "s", &name)) 304 return (NULL); 305 306 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 307 308 nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc); 309 if (nvl == NULL) 310 seterr(_("cannot get permissions on %s"), name); 311 312 return (nvl); 313} 314 315static PyObject * 316py_set_fsacl(PyObject *self, PyObject *args) 317{ 318 int un; 319 size_t nvsz; 320 zfs_cmd_t zc = { 0 }; 321 char *name, *nvbuf; 322 PyObject *dict, *file; 323 nvlist_t *nvl; 324 int err; 325 326 if (!PyArg_ParseTuple(args, "siO!", &name, &un, 327 &PyDict_Type, &dict)) 328 return (NULL); 329 330 nvl = dict2nvl(dict); 331 if (nvl == NULL) 332 return (NULL); 333 334 err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); 335 assert(err == 0); 336 nvbuf = malloc(nvsz); 337 err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); 338 assert(err == 0); 339 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; 344 345 err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc); 346 free(nvbuf); 347 if (err) { 348 seterr(_("cannot set permissions on %s"), name); 349 return (NULL); 350 } 351 352 Py_RETURN_NONE; 353} 354 355static PyObject * 356py_userspace_many(PyObject *self, PyObject *args) 357{ 358 zfs_cmd_t zc = { 0 }; 359 zfs_userquota_prop_t type; 360 char *name, *propname; 361 int bufsz = 1<<20; 362 void *buf; 363 PyObject *dict, *file; 364 int error; 365 366 if (!PyArg_ParseTuple(args, "ss", &name, &propname)) 367 return (NULL); 368 369 for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) 370 if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0) 371 break; 372 if (type == ZFS_NUM_USERQUOTA_PROPS) { 373 PyErr_SetString(PyExc_KeyError, propname); 374 return (NULL); 375 } 376 377 dict = PyDict_New(); 378 buf = malloc(bufsz); 379 380 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 381 zc.zc_objset_type = type; 382 zc.zc_cookie = 0; 383 384 while (1) { 385 zfs_useracct_t *zua = buf; 386 387 zc.zc_nvlist_dst = (uintptr_t)buf; 388 zc.zc_nvlist_dst_size = bufsz; 389 390 error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc); 391 if (error || zc.zc_nvlist_dst_size == 0) 392 break; 393 394 while (zc.zc_nvlist_dst_size > 0) { 395 PyObject *pykey, *pyval; 396 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); 401 Py_DECREF(pykey); 402 Py_DECREF(pyval); 403 404 zua++; 405 zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 406 } 407 } 408 409 free(buf); 410 411 if (error != 0) { 412 Py_DECREF(dict); 413 seterr(_("cannot get %s property on %s"), propname, name); 414 return (NULL); 415 } 416 417 return (dict); 418} 419 420static PyObject * 421py_userspace_upgrade(PyObject *self, PyObject *args) 422{ 423 zfs_cmd_t zc = { 0 }; 424 char *name; 425 int error; 426 427 if (!PyArg_ParseTuple(args, "s", &name)) 428 return (NULL); 429 430 (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 431 error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc); 432 433 if (error != 0) { 434 seterr(_("cannot initialize user accounting information on %s"), 435 name); 436 return (NULL); 437 } 438 439 Py_RETURN_NONE; 440} 441 442static PyObject * 443py_sid_to_id(PyObject *self, PyObject *args) 444{ 445#ifdef sun 446 char *sid; 447 int err, isuser; 448 uid_t id; 449 450 if (!PyArg_ParseTuple(args, "si", &sid, &isuser)) 451 return (NULL); 452 453 err = sid_to_id(sid, isuser, &id); 454 if (err) { 455 PyErr_SetString(PyExc_KeyError, sid); 456 return (NULL); 457 } 458 459 return (Py_BuildValue("I", id)); 460#else /* sun */ 461 return (NULL); 462#endif /* sun */ 463} 464 465/* 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. 469 */ 470static PyObject * 471py_sid_to_name(PyObject *self, PyObject *args) 472{ 473#ifdef sun 474 char *sid; 475 int err, isuser; 476 uid_t id; 477 char *name, *domain; 478 char buf[256]; 479 480 if (!PyArg_ParseTuple(args, "si", &sid, &isuser)) 481 return (NULL); 482 483 err = sid_to_id(sid, isuser, &id); 484 if (err) { 485 PyErr_SetString(PyExc_KeyError, sid); 486 return (NULL); 487 } 488 489 if (isuser) { 490 err = idmap_getwinnamebyuid(id, 491 IDMAP_REQ_FLG_USE_CACHE, &name, &domain); 492 } else { 493 err = idmap_getwinnamebygid(id, 494 IDMAP_REQ_FLG_USE_CACHE, &name, &domain); 495 } 496 if (err != IDMAP_SUCCESS) { 497 PyErr_SetString(PyExc_KeyError, sid); 498 return (NULL); 499 } 500 (void) snprintf(buf, sizeof (buf), "%s@%s", name, domain); 501 free(name); 502 free(domain); 503 504 return (Py_BuildValue("s", buf)); 505#else /* sun */ 506 return(NULL); 507#endif /* sun */ 508} 509 510static PyObject * 511py_isglobalzone(PyObject *self, PyObject *args) 512{ 513 return (Py_BuildValue("i", getzoneid() == GLOBAL_ZONEID)); 514} 515 516static PyObject * 517py_set_cmdstr(PyObject *self, PyObject *args) 518{ 519 char *str; 520 521 if (!PyArg_ParseTuple(args, "s", &str)) 522 return (NULL); 523 524 (void) strlcpy(cmdstr, str, sizeof (cmdstr)); 525 526 Py_RETURN_NONE; 527} 528 529static PyObject * 530py_get_proptable(PyObject *self, PyObject *args) 531{ 532 zprop_desc_t *t = zfs_prop_get_table(); 533 PyObject *d = PyDict_New(); 534 zfs_prop_t i; 535 536 for (i = 0; i < ZFS_NUM_PROPS; i++) { 537 zprop_desc_t *p = &t[i]; 538 PyObject *tuple; 539 static const char *typetable[] = 540 {"number", "string", "index"}; 541 static const char *attrtable[] = 542 {"default", "readonly", "inherit", "onetime"}; 543 PyObject *indextable; 544 545 if (p->pd_proptype == PROP_TYPE_INDEX) { 546 const zprop_index_t *it = p->pd_table; 547 indextable = PyDict_New(); 548 int j; 549 for (j = 0; it[j].pi_name; j++) { 550 PyDict_SetItemString(indextable, 551 it[j].pi_name, 552 Py_BuildValue("K", it[j].pi_value)); 553 } 554 } else { 555 Py_INCREF(Py_None); 556 indextable = Py_None; 557 } 558 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); 566 Py_DECREF(tuple); 567 } 568 569 return (d); 570} 571 572static 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} 594}; 595 596void 597initioctl(void) 598{ 599 PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods); 600 PyObject *zfs_util = PyImport_ImportModule("zfs.util"); 601 PyObject *devfile; 602 603 if (zfs_util == NULL) 604 return; 605 606 ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError"); 607 devfile = PyObject_GetAttrString(zfs_util, "dev"); 608 zfsdevfd = PyObject_AsFileDescriptor(devfile); 609 610 zfs_prop_init(); 611} 612