fa3ec4a7e8806936a7f47c295631d77fd4895d92
[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 }