libbtrfsutil: add btrfs_util_[gs]et_read_only()
[platform/upstream/btrfs-progs.git] / libbtrfsutil / 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 <errno.h>
21 #include <fcntl.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/ioctl.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/vfs.h>
29 #include <linux/magic.h>
30
31 #include "btrfsutil_internal.h"
32
33 /*
34  * This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
35  * a file descriptor and calling it, because fstat() and fstatfs() don't accept
36  * file descriptors opened with O_PATH on old kernels (before v3.6 and before
37  * v3.12, respectively), but stat() and statfs() can be called on a path that
38  * the user doesn't have read or write permissions to.
39  */
40 PUBLIC enum btrfs_util_error btrfs_util_is_subvolume(const char *path)
41 {
42         struct statfs sfs;
43         struct stat st;
44         int ret;
45
46         ret = statfs(path, &sfs);
47         if (ret == -1)
48                 return BTRFS_UTIL_ERROR_STATFS_FAILED;
49
50         if (sfs.f_type != BTRFS_SUPER_MAGIC) {
51                 errno = EINVAL;
52                 return BTRFS_UTIL_ERROR_NOT_BTRFS;
53         }
54
55         ret = stat(path, &st);
56         if (ret == -1)
57                 return BTRFS_UTIL_ERROR_STAT_FAILED;
58
59         if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
60                 errno = EINVAL;
61                 return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
62         }
63
64         return BTRFS_UTIL_OK;
65 }
66
67 PUBLIC enum btrfs_util_error btrfs_util_is_subvolume_fd(int fd)
68 {
69         struct statfs sfs;
70         struct stat st;
71         int ret;
72
73         ret = fstatfs(fd, &sfs);
74         if (ret == -1)
75                 return BTRFS_UTIL_ERROR_STATFS_FAILED;
76
77         if (sfs.f_type != BTRFS_SUPER_MAGIC) {
78                 errno = EINVAL;
79                 return BTRFS_UTIL_ERROR_NOT_BTRFS;
80         }
81
82         ret = fstat(fd, &st);
83         if (ret == -1)
84                 return BTRFS_UTIL_ERROR_STAT_FAILED;
85
86         if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
87                 errno = EINVAL;
88                 return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
89         }
90
91         return BTRFS_UTIL_OK;
92 }
93
94 PUBLIC enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
95                                                      uint64_t *id_ret)
96 {
97         enum btrfs_util_error err;
98         int fd;
99
100         fd = open(path, O_RDONLY);
101         if (fd == -1)
102                 return BTRFS_UTIL_ERROR_OPEN_FAILED;
103
104         err = btrfs_util_subvolume_id_fd(fd, id_ret);
105         SAVE_ERRNO_AND_CLOSE(fd);
106         return err;
107 }
108
109 PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd,
110                                                         uint64_t *id_ret)
111 {
112         struct btrfs_ioctl_ino_lookup_args args = {
113                 .treeid = 0,
114                 .objectid = BTRFS_FIRST_FREE_OBJECTID,
115         };
116         int ret;
117
118         ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
119         if (ret == -1) {
120                 close(fd);
121                 return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
122         }
123
124         *id_ret = args.treeid;
125
126         return BTRFS_UTIL_OK;
127 }
128
129 PUBLIC enum btrfs_util_error btrfs_util_subvolume_path(const char *path,
130                                                        uint64_t id,
131                                                        char **path_ret)
132 {
133         enum btrfs_util_error err;
134         int fd;
135
136         fd = open(path, O_RDONLY);
137         if (fd == -1)
138                 return BTRFS_UTIL_ERROR_OPEN_FAILED;
139
140         err = btrfs_util_subvolume_path_fd(fd, id, path_ret);
141         SAVE_ERRNO_AND_CLOSE(fd);
142         return err;
143 }
144
145 PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(int fd, uint64_t id,
146                                                           char **path_ret)
147 {
148         char *path, *p;
149         size_t capacity = 4096;
150
151         if (id == 0) {
152                 enum btrfs_util_error err;
153
154                 err = btrfs_util_is_subvolume_fd(fd);
155                 if (err)
156                         return err;
157
158                 err = btrfs_util_subvolume_id_fd(fd, &id);
159                 if (err)
160                         return err;
161         }
162
163         path = malloc(capacity);
164         if (!path)
165                 return BTRFS_UTIL_ERROR_NO_MEMORY;
166         p = path + capacity - 1;
167         p[0] = '\0';
168
169         while (id != BTRFS_FS_TREE_OBJECTID) {
170                 struct btrfs_ioctl_search_args search = {
171                         .key = {
172                                 .tree_id = BTRFS_ROOT_TREE_OBJECTID,
173                                 .min_objectid = id,
174                                 .max_objectid = id,
175                                 .min_type = BTRFS_ROOT_BACKREF_KEY,
176                                 .max_type = BTRFS_ROOT_BACKREF_KEY,
177                                 .min_offset = 0,
178                                 .max_offset = UINT64_MAX,
179                                 .min_transid = 0,
180                                 .max_transid = UINT64_MAX,
181                                 .nr_items = 1,
182                         },
183                 };
184                 struct btrfs_ioctl_ino_lookup_args lookup;
185                 const struct btrfs_ioctl_search_header *header;
186                 const struct btrfs_root_ref *ref;
187                 const char *name;
188                 uint16_t name_len;
189                 size_t lookup_len;
190                 size_t total_len;
191                 int ret;
192
193                 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
194                 if (ret == -1) {
195                         free(path);
196                         return BTRFS_UTIL_ERROR_SEARCH_FAILED;
197                 }
198
199                 if (search.key.nr_items == 0) {
200                         free(path);
201                         errno = ENOENT;
202                         return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
203                 }
204
205                 header = (struct btrfs_ioctl_search_header *)search.buf;
206                 ref = (struct btrfs_root_ref *)(header + 1);
207                 name = (char *)(ref + 1);
208                 name_len = le16_to_cpu(ref->name_len);
209
210                 id = header->offset;
211
212                 lookup.treeid = id;
213                 lookup.objectid = le64_to_cpu(ref->dirid);
214                 ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &lookup);
215                 if (ret == -1) {
216                         free(path);
217                         return BTRFS_UTIL_ERROR_SEARCH_FAILED;
218                 }
219                 lookup_len = strlen(lookup.name);
220
221                 total_len = name_len + lookup_len + (id != BTRFS_FS_TREE_OBJECTID);
222                 if (p - total_len < path) {
223                         char *new_path, *new_p;
224                         size_t new_capacity = capacity * 2;
225
226                         new_path = malloc(new_capacity);
227                         if (!new_path) {
228                                 free(path);
229                                 return BTRFS_UTIL_ERROR_NO_MEMORY;
230                         }
231                         new_p = new_path + new_capacity - (path + capacity - p);
232                         memcpy(new_p, p, path + capacity - p);
233                         free(path);
234                         path = new_path;
235                         p = new_p;
236                         capacity = new_capacity;
237                 }
238                 p -= name_len;
239                 memcpy(p, name, name_len);
240                 p -= lookup_len;
241                 memcpy(p, lookup.name, lookup_len);
242                 if (id != BTRFS_FS_TREE_OBJECTID)
243                         *--p = '/';
244         }
245
246         if (p != path)
247                 memmove(path, p, path + capacity - p);
248
249         *path_ret = path;
250
251         return BTRFS_UTIL_OK;
252 }
253
254 static void copy_timespec(struct timespec *timespec,
255                           const struct btrfs_timespec *btrfs_timespec)
256 {
257         timespec->tv_sec = le64_to_cpu(btrfs_timespec->sec);
258         timespec->tv_nsec = le32_to_cpu(btrfs_timespec->nsec);
259 }
260
261 static void copy_root_item(struct btrfs_util_subvolume_info *subvol,
262                            const struct btrfs_root_item *root)
263 {
264         subvol->flags = le64_to_cpu(root->flags);
265         memcpy(subvol->uuid, root->uuid, sizeof(subvol->uuid));
266         memcpy(subvol->parent_uuid, root->parent_uuid,
267                sizeof(subvol->parent_uuid));
268         memcpy(subvol->received_uuid, root->received_uuid,
269                sizeof(subvol->received_uuid));
270         subvol->generation = le64_to_cpu(root->generation);
271         subvol->ctransid = le64_to_cpu(root->ctransid);
272         subvol->otransid = le64_to_cpu(root->otransid);
273         subvol->stransid = le64_to_cpu(root->stransid);
274         subvol->rtransid = le64_to_cpu(root->rtransid);
275         copy_timespec(&subvol->ctime, &root->ctime);
276         copy_timespec(&subvol->otime, &root->otime);
277         copy_timespec(&subvol->stime, &root->stime);
278         copy_timespec(&subvol->rtime, &root->rtime);
279 }
280
281 PUBLIC enum btrfs_util_error btrfs_util_subvolume_info(const char *path,
282                                                        uint64_t id,
283                                                        struct btrfs_util_subvolume_info *subvol)
284 {
285         enum btrfs_util_error err;
286         int fd;
287
288         fd = open(path, O_RDONLY);
289         if (fd == -1)
290                 return BTRFS_UTIL_ERROR_OPEN_FAILED;
291
292         err = btrfs_util_subvolume_info_fd(fd, id, subvol);
293         SAVE_ERRNO_AND_CLOSE(fd);
294         return err;
295 }
296
297 PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
298                                                           struct btrfs_util_subvolume_info *subvol)
299 {
300         struct btrfs_ioctl_search_args search = {
301                 .key = {
302                         .tree_id = BTRFS_ROOT_TREE_OBJECTID,
303                         .min_type = BTRFS_ROOT_ITEM_KEY,
304                         .max_type = BTRFS_ROOT_BACKREF_KEY,
305                         .min_offset = 0,
306                         .max_offset = UINT64_MAX,
307                         .min_transid = 0,
308                         .max_transid = UINT64_MAX,
309                         .nr_items = 0,
310                 },
311         };
312         enum btrfs_util_error err;
313         size_t items_pos = 0, buf_off = 0;
314         bool need_root_item = true, need_root_backref = true;
315         int ret;
316
317         if (id == 0) {
318                 err = btrfs_util_is_subvolume_fd(fd);
319                 if (err)
320                         return err;
321
322                 err = btrfs_util_subvolume_id_fd(fd, &id);
323                 if (err)
324                         return err;
325         }
326
327         if ((id < BTRFS_FIRST_FREE_OBJECTID && id != BTRFS_FS_TREE_OBJECTID) ||
328             id > BTRFS_LAST_FREE_OBJECTID) {
329                 errno = ENOENT;
330                 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
331         }
332
333         search.key.min_objectid = search.key.max_objectid = id;
334
335         if (subvol) {
336                 subvol->id = id;
337                 subvol->parent_id = 0;
338                 subvol->dir_id = 0;
339                 if (id == BTRFS_FS_TREE_OBJECTID)
340                         need_root_backref = false;
341         } else {
342                 /*
343                  * We only need the backref for filling in the subvolume info.
344                  */
345                 need_root_backref = false;
346         }
347
348         /* Don't bother searching for the backref if we don't need it. */
349         if (!need_root_backref)
350                 search.key.max_type = BTRFS_ROOT_ITEM_KEY;
351
352         while (need_root_item || need_root_backref) {
353                 const struct btrfs_ioctl_search_header *header;
354
355                 if (items_pos >= search.key.nr_items) {
356                         search.key.nr_items = 4096;
357                         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
358                         if (ret == -1)
359                                 return BTRFS_UTIL_ERROR_SEARCH_FAILED;
360                         items_pos = 0;
361                         buf_off = 0;
362
363                         if (search.key.nr_items == 0) {
364                                 if (need_root_item) {
365                                         errno = ENOENT;
366                                         return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
367                                 } else {
368                                         break;
369                                 }
370                         }
371                 }
372
373                 header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
374                 if (header->type == BTRFS_ROOT_ITEM_KEY) {
375                         if (subvol) {
376                                 const struct btrfs_root_item *root;
377
378                                 root = (const struct btrfs_root_item *)(header + 1);
379                                 copy_root_item(subvol, root);
380                         }
381                         need_root_item = false;
382                         search.key.min_type = BTRFS_ROOT_BACKREF_KEY;
383                 } else if (header->type == BTRFS_ROOT_BACKREF_KEY) {
384                         if (subvol) {
385                                 const struct btrfs_root_ref *ref;
386
387                                 ref = (const struct btrfs_root_ref *)(header + 1);
388                                 subvol->parent_id = header->offset;
389                                 subvol->dir_id = le64_to_cpu(ref->dirid);
390                         }
391                         need_root_backref = false;
392                         search.key.min_type = UINT32_MAX;
393                 }
394
395                 items_pos++;
396                 buf_off += sizeof(*header) + header->len;
397         }
398
399         return BTRFS_UTIL_OK;
400 }
401
402 PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd,
403                                                                    bool *read_only_ret)
404 {
405         uint64_t flags;
406         int ret;
407
408         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
409         if (ret == -1)
410                 return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED;
411
412         *read_only_ret = flags & BTRFS_SUBVOL_RDONLY;
413         return BTRFS_UTIL_OK;
414 }
415
416 PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only(const char *path,
417                                                                 bool *ret)
418 {
419         enum btrfs_util_error err;
420         int fd;
421
422         fd = open(path, O_RDONLY);
423         if (fd == -1)
424                 return BTRFS_UTIL_ERROR_OPEN_FAILED;
425
426         err = btrfs_util_get_subvolume_read_only_fd(fd, ret);
427         SAVE_ERRNO_AND_CLOSE(fd);
428         return err;
429 }
430
431 PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
432                                                                 bool read_only)
433 {
434         enum btrfs_util_error err;
435         int fd;
436
437         fd = open(path, O_RDONLY);
438         if (fd == -1)
439                 return BTRFS_UTIL_ERROR_OPEN_FAILED;
440
441         err = btrfs_util_set_subvolume_read_only_fd(fd, read_only);
442         SAVE_ERRNO_AND_CLOSE(fd);
443         return err;
444 }
445
446 PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(int fd,
447                                                                    bool read_only)
448 {
449         uint64_t flags;
450         int ret;
451
452         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
453         if (ret == -1)
454                 return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED;
455
456         if (read_only)
457                 flags |= BTRFS_SUBVOL_RDONLY;
458         else
459                 flags &= ~BTRFS_SUBVOL_RDONLY;
460
461         ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
462         if (ret == -1)
463                 return BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED;
464
465         return BTRFS_UTIL_OK;
466 }
467
468 static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path,
469                                                     char *name, size_t name_len,
470                                                     int *fd)
471 {
472         char *tmp_path, *slash, *dirname, *basename;
473         size_t len;
474
475         /* Ignore trailing slashes. */
476         len = strlen(path);
477         while (len > 1 && path[len - 1] == '/')
478                 len--;
479
480         tmp_path = malloc(len + 1);
481         if (!tmp_path)
482                 return BTRFS_UTIL_ERROR_NO_MEMORY;
483         memcpy(tmp_path, path, len);
484         tmp_path[len] = '\0';
485
486         slash = memrchr(tmp_path, '/', len);
487         if (slash == tmp_path) {
488                 dirname = "/";
489                 basename = tmp_path + 1;
490         } else if (slash) {
491                 *slash = '\0';
492                 dirname = tmp_path;
493                 basename = slash + 1;
494         } else {
495                 dirname = ".";
496                 basename = tmp_path;
497         }
498
499         len = strlen(basename);
500         if (len >= name_len) {
501                 free(tmp_path);
502                 errno = ENAMETOOLONG;
503                 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
504         }
505         memcpy(name, basename, len);
506         name[len] = '\0';
507
508         *fd = openat(dirfd, dirname, O_RDONLY | O_DIRECTORY);
509         if (*fd == -1) {
510                 free(tmp_path);
511                 return BTRFS_UTIL_ERROR_OPEN_FAILED;
512         }
513
514         free(tmp_path);
515         return BTRFS_UTIL_OK;
516 }
517
518 PUBLIC enum btrfs_util_error btrfs_util_create_subvolume(const char *path,
519                                                          int flags,
520                                                          uint64_t *async_transid,
521                                                          struct btrfs_util_qgroup_inherit *qgroup_inherit)
522 {
523         char name[BTRFS_SUBVOL_NAME_MAX + 1];
524         enum btrfs_util_error err;
525         int parent_fd;
526
527         err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
528                                      &parent_fd);
529         if (err)
530                 return err;
531
532         err = btrfs_util_create_subvolume_fd(parent_fd, name, flags,
533                                             async_transid, qgroup_inherit);
534         SAVE_ERRNO_AND_CLOSE(parent_fd);
535         return err;
536 }
537
538 PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_fd(int parent_fd,
539                                                             const char *name,
540                                                             int flags,
541                                                             uint64_t *async_transid,
542                                                             struct btrfs_util_qgroup_inherit *qgroup_inherit)
543 {
544         struct btrfs_ioctl_vol_args_v2 args = {};
545         size_t len;
546         int ret;
547
548         if (flags) {
549                 errno = EINVAL;
550                 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
551         }
552
553         if (async_transid)
554                 args.flags |= BTRFS_SUBVOL_CREATE_ASYNC;
555         if (qgroup_inherit) {
556                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
557                 args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit;
558                 args.size = (sizeof(*args.qgroup_inherit) +
559                              args.qgroup_inherit->num_qgroups *
560                              sizeof(args.qgroup_inherit->qgroups[0]));
561         }
562
563         len = strlen(name);
564         if (len >= sizeof(args.name)) {
565                 errno = ENAMETOOLONG;
566                 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
567         }
568         memcpy(args.name, name, len);
569         args.name[len] = '\0';
570
571         ret = ioctl(parent_fd, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
572         if (ret == -1)
573                 return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED;
574
575         if (async_transid)
576                 *async_transid = args.transid;
577
578         return BTRFS_UTIL_OK;
579 }