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