libbtrfsutil: add btrfs_util_[gs]et_read_only()
[platform/upstream/btrfs-progs.git] / libbtrfsutil / python / module.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 static int fd_converter(PyObject *o, void *p)
23 {
24         int *fd = p;
25         long tmp;
26         int overflow;
27
28         tmp = PyLong_AsLongAndOverflow(o, &overflow);
29         if (tmp == -1 && PyErr_Occurred())
30                 return 0;
31         if (overflow > 0 || tmp > INT_MAX) {
32                 PyErr_SetString(PyExc_OverflowError,
33                                 "fd is greater than maximum");
34                 return 0;
35         }
36         if (overflow < 0 || tmp < 0) {
37                 PyErr_SetString(PyExc_ValueError, "fd is negative");
38                 return 0;
39         }
40         *fd = tmp;
41         return 1;
42 }
43
44 int path_converter(PyObject *o, void *p)
45 {
46         struct path_arg *path = p;
47         int is_index, is_bytes, is_unicode;
48         PyObject *bytes = NULL;
49         Py_ssize_t length = 0;
50         char *tmp;
51
52         if (o == NULL) {
53                 path_cleanup(p);
54                 return 1;
55         }
56
57         path->object = path->cleanup = NULL;
58         Py_INCREF(o);
59
60         path->fd = -1;
61
62         is_index = path->allow_fd && PyIndex_Check(o);
63         is_bytes = PyBytes_Check(o);
64         is_unicode = PyUnicode_Check(o);
65
66         if (!is_index && !is_bytes && !is_unicode) {
67                 _Py_IDENTIFIER(__fspath__);
68                 PyObject *func;
69
70                 func = _PyObject_LookupSpecial(o, &PyId___fspath__);
71                 if (func == NULL)
72                         goto err_format;
73                 Py_DECREF(o);
74                 o = PyObject_CallFunctionObjArgs(func, NULL);
75                 Py_DECREF(func);
76                 if (o == NULL)
77                         return 0;
78                 is_bytes = PyBytes_Check(o);
79                 is_unicode = PyUnicode_Check(o);
80         }
81
82         if (is_unicode) {
83                 if (!PyUnicode_FSConverter(o, &bytes))
84                         goto err;
85         } else if (is_bytes) {
86                 bytes = o;
87                 Py_INCREF(bytes);
88         } else if (is_index) {
89                 if (!fd_converter(o, &path->fd))
90                         goto err;
91                 path->path = NULL;
92                 goto out;
93         } else {
94 err_format:
95                 PyErr_Format(PyExc_TypeError, "expected %s, not %s",
96                              path->allow_fd ? "string, bytes, os.PathLike, or integer" :
97                              "string, bytes, or os.PathLike",
98                              Py_TYPE(o)->tp_name);
99                 goto err;
100         }
101
102         length = PyBytes_GET_SIZE(bytes);
103         tmp = PyBytes_AS_STRING(bytes);
104         if ((size_t)length != strlen(tmp)) {
105                 PyErr_SetString(PyExc_TypeError,
106                                 "path has embedded nul character");
107                 goto err;
108         }
109
110         path->path = tmp;
111         if (bytes == o)
112                 Py_DECREF(bytes);
113         else
114                 path->cleanup = bytes;
115         path->fd = -1;
116
117 out:
118         path->length = length;
119         path->object = o;
120         return Py_CLEANUP_SUPPORTED;
121
122 err:
123         Py_XDECREF(o);
124         Py_XDECREF(bytes);
125         return 0;
126 }
127
128 void path_cleanup(struct path_arg *path)
129 {
130         Py_CLEAR(path->object);
131         Py_CLEAR(path->cleanup);
132 }
133
134 static PyMethodDef btrfsutil_methods[] = {
135         {"sync", (PyCFunction)filesystem_sync,
136          METH_VARARGS | METH_KEYWORDS,
137          "sync(path)\n\n"
138          "Sync a specific Btrfs filesystem.\n\n"
139          "Arguments:\n"
140          "path -- string, bytes, path-like object, or open file descriptor"},
141         {"start_sync", (PyCFunction)start_sync,
142          METH_VARARGS | METH_KEYWORDS,
143          "start_sync(path) -> int\n\n"
144          "Start a sync on a specific Btrfs filesystem and return the\n"
145          "transaction ID.\n\n"
146          "Arguments:\n"
147          "path -- string, bytes, path-like object, or open file descriptor"},
148         {"wait_sync", (PyCFunction)wait_sync,
149          METH_VARARGS | METH_KEYWORDS,
150          "wait_sync(path, transid=0)\n\n"
151          "Wait for a transaction to sync.\n"
152          "Arguments:\n"
153          "path -- string, bytes, path-like object, or open file descriptor\n"
154          "transid -- int transaction ID to wait for, or zero for the current\n"
155          "transaction"},
156         {"is_subvolume", (PyCFunction)is_subvolume,
157          METH_VARARGS | METH_KEYWORDS,
158          "is_subvolume(path) -> bool\n\n"
159          "Get whether a file is a subvolume.\n\n"
160          "Arguments:\n"
161          "path -- string, bytes, path-like object, or open file descriptor"},
162         {"subvolume_id", (PyCFunction)subvolume_id,
163          METH_VARARGS | METH_KEYWORDS,
164          "subvolume_id(path) -> int\n\n"
165          "Get the ID of the subvolume containing a file.\n\n"
166          "Arguments:\n"
167          "path -- string, bytes, path-like object, or open file descriptor"},
168         {"subvolume_path", (PyCFunction)subvolume_path,
169          METH_VARARGS | METH_KEYWORDS,
170          "subvolume_path(path, id=0) -> int\n\n"
171          "Get the path of a subvolume relative to the filesystem root.\n\n"
172          "Arguments:\n"
173          "path -- string, bytes, path-like object, or open file descriptor\n"
174          "id -- if not zero, instead of returning the subvolume path of the\n"
175          "given path, return the path of the subvolume with this ID"},
176         {"subvolume_info", (PyCFunction)subvolume_info,
177          METH_VARARGS | METH_KEYWORDS,
178          "subvolume_info(path, id=0) -> SubvolumeInfo\n\n"
179          "Get information about a subvolume.\n\n"
180          "Arguments:\n"
181          "path -- string, bytes, path-like object, or open file descriptor\n"
182          "id -- if not zero, instead of returning information about the\n"
183          "given path, return information about the subvolume with this ID"},
184         {"get_subvolume_read_only", (PyCFunction)get_subvolume_read_only,
185          METH_VARARGS | METH_KEYWORDS,
186          "get_subvolume_read_only(path) -> bool\n\n"
187          "Get whether a subvolume is read-only.\n\n"
188          "Arguments:\n"
189          "path -- string, bytes, path-like object, or open file descriptor"},
190         {"set_subvolume_read_only", (PyCFunction)set_subvolume_read_only,
191          METH_VARARGS | METH_KEYWORDS,
192          "set_subvolume_read_only(path, read_only=True)\n\n"
193          "Set whether a subvolume is read-only.\n\n"
194          "Arguments:\n"
195          "path -- string, bytes, path-like object, or open file descriptor\n"
196          "read_only -- bool flag value"},
197         {"create_subvolume", (PyCFunction)create_subvolume,
198          METH_VARARGS | METH_KEYWORDS,
199          "create_subvolume(path, async=False)\n\n"
200          "Create a new subvolume.\n\n"
201          "Arguments:\n"
202          "path -- string, bytes, or path-like object\n"
203          "async -- create the subvolume without waiting for it to commit to\n"
204          "disk and return the transaction ID"},
205         {},
206 };
207
208 static struct PyModuleDef btrfsutilmodule = {
209         PyModuleDef_HEAD_INIT,
210         "btrfsutil",
211         "Library for managing Btrfs filesystems",
212         -1,
213         btrfsutil_methods,
214 };
215
216 PyMODINIT_FUNC
217 PyInit_btrfsutil(void)
218 {
219         PyObject *m;
220
221         BtrfsUtilError_type.tp_base = (PyTypeObject *)PyExc_OSError;
222         if (PyType_Ready(&BtrfsUtilError_type) < 0)
223                 return NULL;
224
225         if (PyStructSequence_InitType2(&SubvolumeInfo_type, &SubvolumeInfo_desc) < 0)
226                 return NULL;
227
228         QgroupInherit_type.tp_new = PyType_GenericNew;
229         if (PyType_Ready(&QgroupInherit_type) < 0)
230                 return NULL;
231
232         m = PyModule_Create(&btrfsutilmodule);
233         if (!m)
234                 return NULL;
235
236         Py_INCREF(&BtrfsUtilError_type);
237         PyModule_AddObject(m, "BtrfsUtilError",
238                            (PyObject *)&BtrfsUtilError_type);
239
240         Py_INCREF(&SubvolumeInfo_type);
241         PyModule_AddObject(m, "SubvolumeInfo", (PyObject *)&SubvolumeInfo_type);
242
243         Py_INCREF(&QgroupInherit_type);
244         PyModule_AddObject(m, "QgroupInherit",
245                            (PyObject *)&QgroupInherit_type);
246
247         add_module_constants(m);
248
249         return m;
250 }