6ac372ca18aac442469bf86baf9f1faac13fe4a6
[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 static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path,
130                                                     char *name, size_t name_len,
131                                                     int *fd)
132 {
133         char *tmp_path, *slash, *dirname, *basename;
134         size_t len;
135
136         /* Ignore trailing slashes. */
137         len = strlen(path);
138         while (len > 1 && path[len - 1] == '/')
139                 len--;
140
141         tmp_path = malloc(len + 1);
142         if (!tmp_path)
143                 return BTRFS_UTIL_ERROR_NO_MEMORY;
144         memcpy(tmp_path, path, len);
145         tmp_path[len] = '\0';
146
147         slash = memrchr(tmp_path, '/', len);
148         if (slash == tmp_path) {
149                 dirname = "/";
150                 basename = tmp_path + 1;
151         } else if (slash) {
152                 *slash = '\0';
153                 dirname = tmp_path;
154                 basename = slash + 1;
155         } else {
156                 dirname = ".";
157                 basename = tmp_path;
158         }
159
160         len = strlen(basename);
161         if (len >= name_len) {
162                 free(tmp_path);
163                 errno = ENAMETOOLONG;
164                 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
165         }
166         memcpy(name, basename, len);
167         name[len] = '\0';
168
169         *fd = openat(dirfd, dirname, O_RDONLY | O_DIRECTORY);
170         if (*fd == -1) {
171                 free(tmp_path);
172                 return BTRFS_UTIL_ERROR_OPEN_FAILED;
173         }
174
175         free(tmp_path);
176         return BTRFS_UTIL_OK;
177 }
178
179 PUBLIC enum btrfs_util_error btrfs_util_create_subvolume(const char *path,
180                                                          int flags,
181                                                          uint64_t *async_transid,
182                                                          struct btrfs_util_qgroup_inherit *qgroup_inherit)
183 {
184         char name[BTRFS_SUBVOL_NAME_MAX + 1];
185         enum btrfs_util_error err;
186         int parent_fd;
187
188         err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
189                                      &parent_fd);
190         if (err)
191                 return err;
192
193         err = btrfs_util_create_subvolume_fd(parent_fd, name, flags,
194                                             async_transid, qgroup_inherit);
195         SAVE_ERRNO_AND_CLOSE(parent_fd);
196         return err;
197 }
198
199 PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_fd(int parent_fd,
200                                                             const char *name,
201                                                             int flags,
202                                                             uint64_t *async_transid,
203                                                             struct btrfs_util_qgroup_inherit *qgroup_inherit)
204 {
205         struct btrfs_ioctl_vol_args_v2 args = {};
206         size_t len;
207         int ret;
208
209         if (flags) {
210                 errno = EINVAL;
211                 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
212         }
213
214         if (async_transid)
215                 args.flags |= BTRFS_SUBVOL_CREATE_ASYNC;
216         if (qgroup_inherit) {
217                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
218                 args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit;
219                 args.size = (sizeof(*args.qgroup_inherit) +
220                              args.qgroup_inherit->num_qgroups *
221                              sizeof(args.qgroup_inherit->qgroups[0]));
222         }
223
224         len = strlen(name);
225         if (len >= sizeof(args.name)) {
226                 errno = ENAMETOOLONG;
227                 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
228         }
229         memcpy(args.name, name, len);
230         args.name[len] = '\0';
231
232         ret = ioctl(parent_fd, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
233         if (ret == -1)
234                 return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED;
235
236         if (async_transid)
237                 *async_transid = args.transid;
238
239         return BTRFS_UTIL_OK;
240 }