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