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