libbtrfsutil: add subvolume iterator helpers
[platform/upstream/btrfs-progs.git] / libbtrfsutil / python / subvolume.c
1 /*
2  * Copyright (C) 2018 Facebook
3  *
4  * This file is part of libbtrfsutil.
5  *
6  * libbtrfsutil is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * libbtrfsutil is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with libbtrfsutil.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "btrfsutilpy.h"
21
22 PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
23 {
24         static char *keywords[] = {"path", NULL};
25         struct path_arg path = {.allow_fd = true};
26         enum btrfs_util_error err;
27
28         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:is_subvolume",
29                                          keywords, &path_converter, &path))
30                 return NULL;
31
32         if (path.path)
33                 err = btrfs_util_is_subvolume(path.path);
34         else
35                 err = btrfs_util_is_subvolume_fd(path.fd);
36         if (err == BTRFS_UTIL_OK) {
37                 path_cleanup(&path);
38                 Py_RETURN_TRUE;
39         } else if (err == BTRFS_UTIL_ERROR_NOT_BTRFS ||
40                    err == BTRFS_UTIL_ERROR_NOT_SUBVOLUME) {
41                 path_cleanup(&path);
42                 Py_RETURN_FALSE;
43         } else {
44                 SetFromBtrfsUtilErrorWithPath(err, &path);
45                 path_cleanup(&path);
46                 return NULL;
47         }
48 }
49
50 PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
51 {
52         static char *keywords[] = {"path", NULL};
53         struct path_arg path = {.allow_fd = true};
54         enum btrfs_util_error err;
55         uint64_t id;
56
57         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:subvolume_id",
58                                          keywords, &path_converter, &path))
59                 return NULL;
60
61         if (path.path)
62                 err = btrfs_util_subvolume_id(path.path, &id);
63         else
64                 err = btrfs_util_subvolume_id_fd(path.fd, &id);
65         if (err) {
66                 SetFromBtrfsUtilErrorWithPath(err, &path);
67                 path_cleanup(&path);
68                 return NULL;
69         }
70
71         path_cleanup(&path);
72         return PyLong_FromUnsignedLongLong(id);
73 }
74
75 PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds)
76 {
77         static char *keywords[] = {"path", "id", NULL};
78         struct path_arg path = {.allow_fd = true};
79         enum btrfs_util_error err;
80         uint64_t id = 0;
81         char *subvol_path;
82         PyObject *ret;
83
84         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_path",
85                                          keywords, &path_converter, &path, &id))
86                 return NULL;
87
88         if (path.path)
89                 err = btrfs_util_subvolume_path(path.path, id, &subvol_path);
90         else
91                 err = btrfs_util_subvolume_path_fd(path.fd, id, &subvol_path);
92         if (err) {
93                 SetFromBtrfsUtilErrorWithPath(err, &path);
94                 path_cleanup(&path);
95                 return NULL;
96         }
97
98         path_cleanup(&path);
99
100         ret = PyUnicode_DecodeFSDefault(subvol_path);
101         free(subvol_path);
102         return ret;
103 }
104
105 static PyObject *subvolume_info_to_object(const struct btrfs_util_subvolume_info *subvol)
106 {
107         PyObject *ret, *tmp;
108
109         ret = PyStructSequence_New(&SubvolumeInfo_type);
110         if (ret == NULL)
111                 return NULL;
112
113 #define SET_UINT64(i, field)                                    \
114         tmp = PyLong_FromUnsignedLongLong(subvol->field);       \
115         if (tmp == NULL) {                                      \
116                 Py_DECREF(ret);                                 \
117                 return ret;                                     \
118         }                                                       \
119         PyStructSequence_SET_ITEM(ret, i, tmp);
120
121 #define SET_UUID(i, field)                                              \
122         tmp = PyBytes_FromStringAndSize((char *)subvol->field, 16);     \
123         if (tmp == NULL) {                                              \
124                 Py_DECREF(ret);                                         \
125                 return ret;                                             \
126         }                                                               \
127         PyStructSequence_SET_ITEM(ret, i, tmp);
128
129 #define SET_TIME(i, field)                                              \
130         tmp = PyFloat_FromDouble(subvol->field.tv_sec +                 \
131                                  subvol->field.tv_nsec / 1000000000);   \
132         if (tmp == NULL) {                                              \
133                 Py_DECREF(ret);                                         \
134                 return ret;                                             \
135         }                                                               \
136         PyStructSequence_SET_ITEM(ret, i, tmp);
137
138         SET_UINT64(0, id);
139         SET_UINT64(1, parent_id);
140         SET_UINT64(2, dir_id);
141         SET_UINT64(3, flags);
142         SET_UUID(4, uuid);
143         SET_UUID(5, parent_uuid);
144         SET_UUID(6, received_uuid);
145         SET_UINT64(7, generation);
146         SET_UINT64(8, ctransid);
147         SET_UINT64(9, otransid);
148         SET_UINT64(10, stransid);
149         SET_UINT64(11, rtransid);
150         SET_TIME(12, ctime);
151         SET_TIME(13, otime);
152         SET_TIME(14, stime);
153         SET_TIME(15, rtime);
154
155 #undef SET_TIME
156 #undef SET_UUID
157 #undef SET_UINT64
158
159         return ret;
160 }
161
162 PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds)
163 {
164         static char *keywords[] = {"path", "id", NULL};
165         struct path_arg path = {.allow_fd = true};
166         struct btrfs_util_subvolume_info subvol;
167         enum btrfs_util_error err;
168         uint64_t id = 0;
169
170         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_info",
171                                          keywords, &path_converter, &path, &id))
172                 return NULL;
173
174         if (path.path)
175                 err = btrfs_util_subvolume_info(path.path, id, &subvol);
176         else
177                 err = btrfs_util_subvolume_info_fd(path.fd, id, &subvol);
178         if (err) {
179                 SetFromBtrfsUtilErrorWithPath(err, &path);
180                 path_cleanup(&path);
181                 return NULL;
182         }
183
184         path_cleanup(&path);
185
186         return subvolume_info_to_object(&subvol);
187 }
188
189 static PyStructSequence_Field SubvolumeInfo_fields[] = {
190         {"id", "int ID of this subvolume"},
191         {"parent_id", "int ID of the subvolume containing this subvolume"},
192         {"dir_id", "int inode number of the directory containing this subvolume"},
193         {"flags", "int root item flags"},
194         {"uuid", "bytes UUID of this subvolume"},
195         {"parent_uuid", "bytes UUID of the subvolume this is a snapshot of"},
196         {"received_uuid", "bytes UUID of the subvolume this was received from"},
197         {"generation", "int transaction ID of the subvolume root"},
198         {"ctransid", "int transaction ID when an inode was last changed"},
199         {"otransid", "int transaction ID when this subvolume was created"},
200         {"stransid", "int transaction ID of the sent subvolume this subvolume was received from"},
201         {"rtransid", "int transaction ID when this subvolume was received"},
202         {"ctime", "float time when an inode was last changed"},
203         {"otime", "float time when this subvolume was created"},
204         {"stime", "float time, usually zero"},
205         {"rtime", "float time when this subvolume was received"},
206         {},
207 };
208
209 PyStructSequence_Desc SubvolumeInfo_desc = {
210         "btrfsutil.SubvolumeInfo",
211         "Information about a Btrfs subvolume.",
212         SubvolumeInfo_fields,
213         14,
214 };
215
216 PyTypeObject SubvolumeInfo_type;
217
218 PyObject *get_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds)
219 {
220         static char *keywords[] = {"path", NULL};
221         struct path_arg path = {.allow_fd = true};
222         enum btrfs_util_error err;
223         bool read_only;
224
225         if (!PyArg_ParseTupleAndKeywords(args, kwds,
226                                          "O&:get_subvolume_read_only",
227                                          keywords, &path_converter, &path))
228                 return NULL;
229
230         if (path.path) {
231                 err = btrfs_util_get_subvolume_read_only(path.path, &read_only);
232         } else {
233                 err = btrfs_util_get_subvolume_read_only_fd(path.fd,
234                                                             &read_only);
235         }
236         if (err) {
237                 SetFromBtrfsUtilErrorWithPath(err, &path);
238                 path_cleanup(&path);
239                 return NULL;
240         }
241
242         path_cleanup(&path);
243         return PyBool_FromLong(read_only);
244 }
245
246 PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds)
247 {
248         static char *keywords[] = {"path", "read_only", NULL};
249         struct path_arg path = {.allow_fd = true};
250         enum btrfs_util_error err;
251         int read_only = 1;
252
253         if (!PyArg_ParseTupleAndKeywords(args, kwds,
254                                          "O&|p:set_subvolume_read_only",
255                                          keywords, &path_converter, &path,
256                                          &read_only))
257                 return NULL;
258
259         if (path.path)
260                 err = btrfs_util_set_subvolume_read_only(path.path, read_only);
261         else
262                 err = btrfs_util_set_subvolume_read_only_fd(path.fd, read_only);
263         if (err) {
264                 SetFromBtrfsUtilErrorWithPath(err, &path);
265                 path_cleanup(&path);
266                 return NULL;
267         }
268
269         path_cleanup(&path);
270         Py_RETURN_NONE;
271 }
272
273 PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
274 {
275         static char *keywords[] = {"path", NULL};
276         struct path_arg path = {.allow_fd = true};
277         enum btrfs_util_error err;
278         uint64_t id;
279
280         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:get_default_subvolume",
281                                          keywords, &path_converter, &path))
282                 return NULL;
283
284         if (path.path)
285                 err = btrfs_util_get_default_subvolume(path.path, &id);
286         else
287                 err = btrfs_util_get_default_subvolume_fd(path.fd, &id);
288         if (err) {
289                 SetFromBtrfsUtilErrorWithPath(err, &path);
290                 path_cleanup(&path);
291                 return NULL;
292         }
293
294         path_cleanup(&path);
295         return PyLong_FromUnsignedLongLong(id);
296 }
297
298 PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
299 {
300         static char *keywords[] = {"path", "id", NULL};
301         struct path_arg path = {.allow_fd = true};
302         enum btrfs_util_error err;
303         uint64_t id = 0;
304
305         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:set_default_subvolume",
306                                          keywords, &path_converter, &path, &id))
307                 return NULL;
308
309         if (path.path)
310                 err = btrfs_util_set_default_subvolume(path.path, id);
311         else
312                 err = btrfs_util_set_default_subvolume_fd(path.fd, id);
313         if (err) {
314                 SetFromBtrfsUtilErrorWithPath(err, &path);
315                 path_cleanup(&path);
316                 return NULL;
317         }
318
319         path_cleanup(&path);
320         Py_RETURN_NONE;
321 }
322
323 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
324 {
325         static char *keywords[] = {"path", "async", "qgroup_inherit", NULL};
326         struct path_arg path = {.allow_fd = false};
327         enum btrfs_util_error err;
328         int async = 0;
329         QgroupInherit *inherit = NULL;
330         uint64_t transid;
331
332         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|pO!:create_subvolume",
333                                          keywords, &path_converter, &path,
334                                          &async, &QgroupInherit_type, &inherit))
335                 return NULL;
336
337         err = btrfs_util_create_subvolume(path.path, 0, async ? &transid : NULL,
338                                           inherit ? inherit->inherit : NULL);
339         if (err) {
340                 SetFromBtrfsUtilErrorWithPath(err, &path);
341                 path_cleanup(&path);
342                 return NULL;
343         }
344
345         path_cleanup(&path);
346         if (async)
347                 return PyLong_FromUnsignedLongLong(transid);
348         else
349                 Py_RETURN_NONE;
350 }
351
352 typedef struct {
353         PyObject_HEAD
354         struct btrfs_util_subvolume_iterator *iter;
355         bool info;
356 } SubvolumeIterator;
357
358 static void SubvolumeIterator_dealloc(SubvolumeIterator *self)
359 {
360         btrfs_util_destroy_subvolume_iterator(self->iter);
361         Py_TYPE(self)->tp_free((PyObject *)self);
362 }
363
364 static PyObject *SubvolumeIterator_next(SubvolumeIterator *self)
365 {
366         enum btrfs_util_error err;
367         PyObject *ret, *tmp;
368         char *path;
369
370         if (!self->iter) {
371                 PyErr_SetString(PyExc_ValueError,
372                                 "operation on closed iterator");
373                 return NULL;
374         }
375
376         if (self->info) {
377                 struct btrfs_util_subvolume_info subvol;
378
379                 err = btrfs_util_subvolume_iterator_next_info(self->iter, &path,
380                                                               &subvol);
381                 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
382                         PyErr_SetNone(PyExc_StopIteration);
383                         return NULL;
384                 } else if (err) {
385                         SetFromBtrfsUtilError(err);
386                         return NULL;
387                 }
388
389                 tmp = subvolume_info_to_object(&subvol);
390         } else {
391                 uint64_t id;
392
393                 err = btrfs_util_subvolume_iterator_next(self->iter, &path, &id);
394                 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
395                         PyErr_SetNone(PyExc_StopIteration);
396                         return NULL;
397                 } else if (err) {
398                         SetFromBtrfsUtilError(err);
399                         return NULL;
400                 }
401
402                 tmp = PyLong_FromUnsignedLongLong(id);
403
404         }
405         if (tmp) {
406                 ret = Py_BuildValue("O&O", PyUnicode_DecodeFSDefault, path,
407                                     tmp);
408                 Py_DECREF(tmp);
409                 free(path);
410         } else {
411                 ret = NULL;
412         }
413         return ret;
414 }
415
416 static int SubvolumeIterator_init(SubvolumeIterator *self, PyObject *args,
417                                   PyObject *kwds)
418 {
419         static char *keywords[] = {"path", "top", "info", "post_order", NULL};
420         struct path_arg path = {.allow_fd = true};
421         enum btrfs_util_error err;
422         unsigned long long top = 5;
423         int info = 0;
424         int post_order = 0;
425         int flags = 0;
426
427         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|Kpp:SubvolumeIterator",
428                                          keywords, &path_converter, &path, &top,
429                                          &info, &post_order))
430                 return -1;
431
432         if (post_order)
433                 flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER;
434
435         if (path.path) {
436                 err = btrfs_util_create_subvolume_iterator(path.path, top,
437                                                            flags, &self->iter);
438         } else {
439                 err = btrfs_util_create_subvolume_iterator_fd(path.fd, top,
440                                                               flags,
441                                                               &self->iter);
442         }
443         if (err) {
444                 SetFromBtrfsUtilErrorWithPath(err, &path);
445                 path_cleanup(&path);
446                 return -1;
447         }
448
449         self->info = info;
450
451         return 0;
452 }
453
454 static PyObject *SubvolumeIterator_close(SubvolumeIterator *self)
455 {
456         if (self->iter) {
457                 btrfs_util_destroy_subvolume_iterator(self->iter);
458                 self->iter = NULL;
459         }
460         Py_RETURN_NONE;
461 }
462
463 static PyObject *SubvolumeIterator_fileno(SubvolumeIterator *self)
464 {
465         if (!self->iter) {
466                 PyErr_SetString(PyExc_ValueError,
467                                 "operation on closed iterator");
468                 return NULL;
469         }
470         return PyLong_FromLong(btrfs_util_subvolume_iterator_fd(self->iter));
471 }
472
473 static PyObject *SubvolumeIterator_enter(SubvolumeIterator *self)
474 {
475         Py_INCREF((PyObject *)self);
476         return (PyObject *)self;
477 }
478
479 static PyObject *SubvolumeIterator_exit(SubvolumeIterator *self, PyObject *args,
480                                        PyObject *kwds)
481 {
482         static char *keywords[] = {"exc_type", "exc_value", "traceback", NULL};
483         PyObject *exc_type, *exc_value, *traceback;
484
485         if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO:__exit__", keywords,
486                                          &exc_type, &exc_value, &traceback))
487                 return NULL;
488
489         return SubvolumeIterator_close(self);
490 }
491
492 #define SubvolumeIterator_DOC   \
493          "SubvolumeIterator(path, top=0, info=False, post_order=False) -> new subvolume iterator\n\n"   \
494          "Create a new iterator that produces tuples of (path, ID) representing\n"      \
495          "subvolumes on a filesystem.\n\n"                                              \
496          "Arguments:\n"                                                                 \
497          "path -- string, bytes, path-like object, or open file descriptor in\n"        \
498          "filesystem to list\n"                                                         \
499          "top -- if not zero, instead of only listing subvolumes beneath the\n"         \
500          "given path, list subvolumes beneath the subvolume with this ID; passing\n"    \
501          "BTRFS_FS_TREE_OBJECTID (5) here lists all subvolumes. The subvolumes\n"       \
502          "are listed relative to the subvolume with this ID.\n"                         \
503          "info -- bool indicating the iterator should yield SubvolumeInfo instead of\n" \
504          "the subvolume ID\n"                                                           \
505          "post_order -- bool indicating whether to yield parent subvolumes before\n"    \
506          "child subvolumes (e.g., 'foo/bar' before 'foo')"
507
508 static PyMethodDef SubvolumeIterator_methods[] = {
509         {"close", (PyCFunction)SubvolumeIterator_close,
510          METH_NOARGS,
511          "close()\n\n"
512          "Close this iterator."},
513         {"fileno", (PyCFunction)SubvolumeIterator_fileno,
514          METH_NOARGS,
515          "fileno() -> int\n\n"
516          "Get the file descriptor associated with this iterator."},
517         {"__enter__", (PyCFunction)SubvolumeIterator_enter,
518          METH_NOARGS, ""},
519         {"__exit__", (PyCFunction)SubvolumeIterator_exit,
520          METH_VARARGS | METH_KEYWORDS, ""},
521         {},
522 };
523
524 PyTypeObject SubvolumeIterator_type = {
525         PyVarObject_HEAD_INIT(NULL, 0)
526         "btrfsutil.SubvolumeIterator",          /* tp_name */
527         sizeof(SubvolumeIterator),              /* tp_basicsize */
528         0,                                      /* tp_itemsize */
529         (destructor)SubvolumeIterator_dealloc,  /* tp_dealloc */
530         NULL,                                   /* tp_print */
531         NULL,                                   /* tp_getattr */
532         NULL,                                   /* tp_setattr */
533         NULL,                                   /* tp_as_async */
534         NULL,                                   /* tp_repr */
535         NULL,                                   /* tp_as_number */
536         NULL,                                   /* tp_as_sequence */
537         NULL,                                   /* tp_as_mapping */
538         NULL,                                   /* tp_hash  */
539         NULL,                                   /* tp_call */
540         NULL,                                   /* tp_str */
541         NULL,                                   /* tp_getattro */
542         NULL,                                   /* tp_setattro */
543         NULL,                                   /* tp_as_buffer */
544         Py_TPFLAGS_DEFAULT,                     /* tp_flags */
545         SubvolumeIterator_DOC,                  /* tp_doc */
546         NULL,                                   /* tp_traverse */
547         NULL,                                   /* tp_clear */
548         NULL,                                   /* tp_richcompare */
549         0,                                      /* tp_weaklistoffset */
550         PyObject_SelfIter,                      /* tp_iter */
551         (iternextfunc)SubvolumeIterator_next,   /* tp_iternext */
552         SubvolumeIterator_methods,              /* tp_methods */
553         NULL,                                   /* tp_members */
554         NULL,                                   /* tp_getset */
555         NULL,                                   /* tp_base */
556         NULL,                                   /* tp_dict */
557         NULL,                                   /* tp_descr_get */
558         NULL,                                   /* tp_descr_set */
559         0,                                      /* tp_dictoffset */
560         (initproc)SubvolumeIterator_init,       /* tp_init */
561 };