]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - cddl/contrib/opensolaris/lib/pyzfs/common/ioctl.c
MFC r209962, r211970-r211972, r212050, r212605, r212611
[FreeBSD/stable/8.git] / cddl / contrib / opensolaris / lib / pyzfs / common / ioctl.c
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
39 static PyObject *ZFSError;
40 static 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
49 extern int sid_to_id(char *sid, boolean_t user, uid_t *id);
50 #endif  /* sun */
51
52 /*PRINTFLIKE1*/
53 static void
54 seterr(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
66 static char cmdstr[HIS_MAX_RECORD_LEN];
67
68 static int
69 ioctl_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
80 static PyObject *
81 nvl2py(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
135 static nvlist_t *
136 dict2nvl(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
186 static PyObject *
187 fakepropval(uint64_t value)
188 {
189         PyObject *d = PyDict_New();
190         PyDict_SetItemString(d, "value", Py_BuildValue("K", value));
191         return (d);
192 }
193
194 static void
195 add_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. */
207 static PyObject *
208 ioctl_with_dstnv(unsigned long ioc, zfs_cmd_t *zc)
209 {
210         int nvsz = 2048;
211         void *nvbuf;
212         PyObject *pynv = NULL;
213
214 again:
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
236 static PyObject *
237 py_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
274 static PyObject *
275 py_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
296 static PyObject *
297 py_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
315 static PyObject *
316 py_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
355 static PyObject *
356 py_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
420 static PyObject *
421 py_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
442 static PyObject *
443 py_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  */
470 static PyObject *
471 py_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
510 static PyObject *
511 py_isglobalzone(PyObject *self, PyObject *args)
512 {
513         return (Py_BuildValue("i", getzoneid() == GLOBAL_ZONEID));
514 }
515
516 static PyObject *
517 py_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
529 static PyObject *
530 py_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
572 static 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
596 void
597 initioctl(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 }