2 * Copyright (C) 2018 Facebook
4 * This file is part of libbtrfsutil.
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.
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.
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/>.
20 #include "btrfsutilpy.h"
22 PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
24 static char *keywords[] = {"path", NULL};
25 struct path_arg path = {.allow_fd = true};
26 enum btrfs_util_error err;
28 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:is_subvolume",
29 keywords, &path_converter, &path))
33 err = btrfs_util_is_subvolume(path.path);
35 err = btrfs_util_is_subvolume_fd(path.fd);
36 if (err == BTRFS_UTIL_OK) {
39 } else if (err == BTRFS_UTIL_ERROR_NOT_BTRFS ||
40 err == BTRFS_UTIL_ERROR_NOT_SUBVOLUME) {
44 SetFromBtrfsUtilErrorWithPath(err, &path);
50 PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
52 static char *keywords[] = {"path", NULL};
53 struct path_arg path = {.allow_fd = true};
54 enum btrfs_util_error err;
57 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:subvolume_id",
58 keywords, &path_converter, &path))
62 err = btrfs_util_subvolume_id(path.path, &id);
64 err = btrfs_util_subvolume_id_fd(path.fd, &id);
66 SetFromBtrfsUtilErrorWithPath(err, &path);
72 return PyLong_FromUnsignedLongLong(id);
75 PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds)
77 static char *keywords[] = {"path", "id", NULL};
78 struct path_arg path = {.allow_fd = true};
79 enum btrfs_util_error err;
84 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_path",
85 keywords, &path_converter, &path, &id))
89 err = btrfs_util_subvolume_path(path.path, id, &subvol_path);
91 err = btrfs_util_subvolume_path_fd(path.fd, id, &subvol_path);
93 SetFromBtrfsUtilErrorWithPath(err, &path);
100 ret = PyUnicode_DecodeFSDefault(subvol_path);
105 static PyObject *subvolume_info_to_object(const struct btrfs_util_subvolume_info *subvol)
109 ret = PyStructSequence_New(&SubvolumeInfo_type);
113 #define SET_UINT64(i, field) \
114 tmp = PyLong_FromUnsignedLongLong(subvol->field); \
119 PyStructSequence_SET_ITEM(ret, i, tmp);
121 #define SET_UUID(i, field) \
122 tmp = PyBytes_FromStringAndSize((char *)subvol->field, 16); \
127 PyStructSequence_SET_ITEM(ret, i, tmp);
129 #define SET_TIME(i, field) \
130 tmp = PyFloat_FromDouble(subvol->field.tv_sec + \
131 subvol->field.tv_nsec / 1000000000); \
136 PyStructSequence_SET_ITEM(ret, i, tmp);
139 SET_UINT64(1, parent_id);
140 SET_UINT64(2, dir_id);
141 SET_UINT64(3, flags);
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);
162 PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds)
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;
170 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_info",
171 keywords, &path_converter, &path, &id))
175 err = btrfs_util_subvolume_info(path.path, id, &subvol);
177 err = btrfs_util_subvolume_info_fd(path.fd, id, &subvol);
179 SetFromBtrfsUtilErrorWithPath(err, &path);
186 return subvolume_info_to_object(&subvol);
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"},
209 PyStructSequence_Desc SubvolumeInfo_desc = {
210 "btrfsutil.SubvolumeInfo",
211 "Information about a Btrfs subvolume.",
212 SubvolumeInfo_fields,
216 PyTypeObject SubvolumeInfo_type;
218 PyObject *get_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds)
220 static char *keywords[] = {"path", NULL};
221 struct path_arg path = {.allow_fd = true};
222 enum btrfs_util_error err;
225 if (!PyArg_ParseTupleAndKeywords(args, kwds,
226 "O&:get_subvolume_read_only",
227 keywords, &path_converter, &path))
231 err = btrfs_util_get_subvolume_read_only(path.path, &read_only);
233 err = btrfs_util_get_subvolume_read_only_fd(path.fd,
237 SetFromBtrfsUtilErrorWithPath(err, &path);
243 return PyBool_FromLong(read_only);
246 PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds)
248 static char *keywords[] = {"path", "read_only", NULL};
249 struct path_arg path = {.allow_fd = true};
250 enum btrfs_util_error err;
253 if (!PyArg_ParseTupleAndKeywords(args, kwds,
254 "O&|p:set_subvolume_read_only",
255 keywords, &path_converter, &path,
260 err = btrfs_util_set_subvolume_read_only(path.path, read_only);
262 err = btrfs_util_set_subvolume_read_only_fd(path.fd, read_only);
264 SetFromBtrfsUtilErrorWithPath(err, &path);
273 PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
275 static char *keywords[] = {"path", NULL};
276 struct path_arg path = {.allow_fd = true};
277 enum btrfs_util_error err;
280 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:get_default_subvolume",
281 keywords, &path_converter, &path))
285 err = btrfs_util_get_default_subvolume(path.path, &id);
287 err = btrfs_util_get_default_subvolume_fd(path.fd, &id);
289 SetFromBtrfsUtilErrorWithPath(err, &path);
295 return PyLong_FromUnsignedLongLong(id);
298 PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
300 static char *keywords[] = {"path", "id", NULL};
301 struct path_arg path = {.allow_fd = true};
302 enum btrfs_util_error err;
305 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:set_default_subvolume",
306 keywords, &path_converter, &path, &id))
310 err = btrfs_util_set_default_subvolume(path.path, id);
312 err = btrfs_util_set_default_subvolume_fd(path.fd, id);
314 SetFromBtrfsUtilErrorWithPath(err, &path);
323 PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
325 static char *keywords[] = {"path", "async", "qgroup_inherit", NULL};
326 struct path_arg path = {.allow_fd = false};
327 enum btrfs_util_error err;
329 QgroupInherit *inherit = NULL;
332 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|pO!:create_subvolume",
333 keywords, &path_converter, &path,
334 &async, &QgroupInherit_type, &inherit))
337 err = btrfs_util_create_subvolume(path.path, 0, async ? &transid : NULL,
338 inherit ? inherit->inherit : NULL);
340 SetFromBtrfsUtilErrorWithPath(err, &path);
347 return PyLong_FromUnsignedLongLong(transid);
352 PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds)
354 static char *keywords[] = {
355 "source", "path", "recursive", "read_only", "async",
356 "qgroup_inherit", NULL,
358 struct path_arg src = {.allow_fd = true}, dst = {.allow_fd = false};
359 enum btrfs_util_error err;
360 int recursive = 0, read_only = 0, async = 0;
362 QgroupInherit *inherit = NULL;
365 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&|pppO!:create_snapshot",
366 keywords, &path_converter, &src,
367 &path_converter, &dst, &recursive,
369 &QgroupInherit_type, &inherit))
373 flags |= BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE;
375 flags |= BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY;
378 err = btrfs_util_create_snapshot(src.path, dst.path, flags,
379 async ? &transid : NULL,
380 inherit ? inherit->inherit : NULL);
382 err = btrfs_util_create_snapshot_fd(src.fd, dst.path, flags,
383 async ? &transid : NULL,
384 inherit ? inherit->inherit : NULL);
387 SetFromBtrfsUtilErrorWithPaths(err, &src, &dst);
396 return PyLong_FromUnsignedLongLong(transid);
401 PyObject *delete_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
403 static char *keywords[] = {"path", "recursive", NULL};
404 struct path_arg path = {.allow_fd = false};
405 enum btrfs_util_error err;
409 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|p:delete_subvolume",
410 keywords, &path_converter, &path,
415 flags |= BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE;
417 err = btrfs_util_delete_subvolume(path.path, flags);
419 SetFromBtrfsUtilErrorWithPath(err, &path);
428 PyObject *deleted_subvolumes(PyObject *self, PyObject *args, PyObject *kwds)
430 static char *keywords[] = {"path", NULL};
431 struct path_arg path = {.allow_fd = true};
435 enum btrfs_util_error err;
437 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:deleted_subvolumes",
438 keywords, &path_converter, &path))
442 err = btrfs_util_deleted_subvolumes(path.path, &ids, &n);
444 err = btrfs_util_deleted_subvolumes_fd(path.fd, &ids, &n);
446 SetFromBtrfsUtilErrorWithPath(err, &path);
453 ret = list_from_uint64_array(ids, n);
460 struct btrfs_util_subvolume_iterator *iter;
464 static void SubvolumeIterator_dealloc(SubvolumeIterator *self)
466 btrfs_util_destroy_subvolume_iterator(self->iter);
467 Py_TYPE(self)->tp_free((PyObject *)self);
470 static PyObject *SubvolumeIterator_next(SubvolumeIterator *self)
472 enum btrfs_util_error err;
477 PyErr_SetString(PyExc_ValueError,
478 "operation on closed iterator");
483 struct btrfs_util_subvolume_info subvol;
485 err = btrfs_util_subvolume_iterator_next_info(self->iter, &path,
487 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
488 PyErr_SetNone(PyExc_StopIteration);
491 SetFromBtrfsUtilError(err);
495 tmp = subvolume_info_to_object(&subvol);
499 err = btrfs_util_subvolume_iterator_next(self->iter, &path, &id);
500 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
501 PyErr_SetNone(PyExc_StopIteration);
504 SetFromBtrfsUtilError(err);
508 tmp = PyLong_FromUnsignedLongLong(id);
512 ret = Py_BuildValue("O&O", PyUnicode_DecodeFSDefault, path,
522 static int SubvolumeIterator_init(SubvolumeIterator *self, PyObject *args,
525 static char *keywords[] = {"path", "top", "info", "post_order", NULL};
526 struct path_arg path = {.allow_fd = true};
527 enum btrfs_util_error err;
528 unsigned long long top = 5;
533 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|Kpp:SubvolumeIterator",
534 keywords, &path_converter, &path, &top,
539 flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER;
542 err = btrfs_util_create_subvolume_iterator(path.path, top,
545 err = btrfs_util_create_subvolume_iterator_fd(path.fd, top,
550 SetFromBtrfsUtilErrorWithPath(err, &path);
560 static PyObject *SubvolumeIterator_close(SubvolumeIterator *self)
563 btrfs_util_destroy_subvolume_iterator(self->iter);
569 static PyObject *SubvolumeIterator_fileno(SubvolumeIterator *self)
572 PyErr_SetString(PyExc_ValueError,
573 "operation on closed iterator");
576 return PyLong_FromLong(btrfs_util_subvolume_iterator_fd(self->iter));
579 static PyObject *SubvolumeIterator_enter(SubvolumeIterator *self)
581 Py_INCREF((PyObject *)self);
582 return (PyObject *)self;
585 static PyObject *SubvolumeIterator_exit(SubvolumeIterator *self, PyObject *args,
588 static char *keywords[] = {"exc_type", "exc_value", "traceback", NULL};
589 PyObject *exc_type, *exc_value, *traceback;
591 if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO:__exit__", keywords,
592 &exc_type, &exc_value, &traceback))
595 return SubvolumeIterator_close(self);
598 #define SubvolumeIterator_DOC \
599 "SubvolumeIterator(path, top=0, info=False, post_order=False) -> new subvolume iterator\n\n" \
600 "Create a new iterator that produces tuples of (path, ID) representing\n" \
601 "subvolumes on a filesystem.\n\n" \
603 "path -- string, bytes, path-like object, or open file descriptor in\n" \
604 "filesystem to list\n" \
605 "top -- if not zero, instead of only listing subvolumes beneath the\n" \
606 "given path, list subvolumes beneath the subvolume with this ID; passing\n" \
607 "BTRFS_FS_TREE_OBJECTID (5) here lists all subvolumes. The subvolumes\n" \
608 "are listed relative to the subvolume with this ID.\n" \
609 "info -- bool indicating the iterator should yield SubvolumeInfo instead of\n" \
610 "the subvolume ID\n" \
611 "post_order -- bool indicating whether to yield parent subvolumes before\n" \
612 "child subvolumes (e.g., 'foo/bar' before 'foo')"
614 static PyMethodDef SubvolumeIterator_methods[] = {
615 {"close", (PyCFunction)SubvolumeIterator_close,
618 "Close this iterator."},
619 {"fileno", (PyCFunction)SubvolumeIterator_fileno,
621 "fileno() -> int\n\n"
622 "Get the file descriptor associated with this iterator."},
623 {"__enter__", (PyCFunction)SubvolumeIterator_enter,
625 {"__exit__", (PyCFunction)SubvolumeIterator_exit,
626 METH_VARARGS | METH_KEYWORDS, ""},
630 PyTypeObject SubvolumeIterator_type = {
631 PyVarObject_HEAD_INIT(NULL, 0)
632 "btrfsutil.SubvolumeIterator", /* tp_name */
633 sizeof(SubvolumeIterator), /* tp_basicsize */
635 (destructor)SubvolumeIterator_dealloc, /* tp_dealloc */
637 NULL, /* tp_getattr */
638 NULL, /* tp_setattr */
639 NULL, /* tp_as_async */
641 NULL, /* tp_as_number */
642 NULL, /* tp_as_sequence */
643 NULL, /* tp_as_mapping */
647 NULL, /* tp_getattro */
648 NULL, /* tp_setattro */
649 NULL, /* tp_as_buffer */
650 Py_TPFLAGS_DEFAULT, /* tp_flags */
651 SubvolumeIterator_DOC, /* tp_doc */
652 NULL, /* tp_traverse */
654 NULL, /* tp_richcompare */
655 0, /* tp_weaklistoffset */
656 PyObject_SelfIter, /* tp_iter */
657 (iternextfunc)SubvolumeIterator_next, /* tp_iternext */
658 SubvolumeIterator_methods, /* tp_methods */
659 NULL, /* tp_members */
660 NULL, /* tp_getset */
663 NULL, /* tp_descr_get */
664 NULL, /* tp_descr_set */
665 0, /* tp_dictoffset */
666 (initproc)SubvolumeIterator_init, /* tp_init */