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