Btrfs-progs: add option to skip whether a scrub has started/resumed in userspace
[btrfs-progs-unstable/devel.git] / send-utils.c
blob874f8a53e20ac8bd92f8fa5eff2594d69230bf68
1 /*
2 * Copyright (C) 2012 Alexander Block. All rights reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <sys/ioctl.h>
22 #include <uuid/uuid.h>
24 #include "ctree.h"
25 #include "send-utils.h"
26 #include "ioctl.h"
27 #include "btrfs-list.h"
29 static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
30 u64 subvol_id);
32 static int btrfs_get_root_id_by_sub_path(int mnt_fd, const char *sub_path,
33 u64 *root_id)
35 int ret;
36 int subvol_fd;
38 subvol_fd = openat(mnt_fd, sub_path, O_RDONLY);
39 if (subvol_fd < 0) {
40 ret = -errno;
41 fprintf(stderr, "ERROR: open %s failed. %s\n", sub_path,
42 strerror(-ret));
43 return ret;
46 ret = btrfs_list_get_path_rootid(subvol_fd, root_id);
47 close(subvol_fd);
48 return ret;
51 static int btrfs_read_root_item_raw(int mnt_fd, u64 root_id, size_t buf_len,
52 u32 *read_len, void *buf)
54 int ret;
55 struct btrfs_ioctl_search_args args;
56 struct btrfs_ioctl_search_key *sk = &args.key;
57 struct btrfs_ioctl_search_header *sh;
58 unsigned long off = 0;
59 int found = 0;
60 int i;
62 *read_len = 0;
63 memset(&args, 0, sizeof(args));
65 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
68 * there may be more than one ROOT_ITEM key if there are
69 * snapshots pending deletion, we have to loop through
70 * them.
72 sk->min_objectid = root_id;
73 sk->max_objectid = root_id;
74 sk->max_type = BTRFS_ROOT_ITEM_KEY;
75 sk->min_type = BTRFS_ROOT_ITEM_KEY;
76 sk->max_offset = (u64)-1;
77 sk->max_transid = (u64)-1;
78 sk->nr_items = 4096;
80 while (1) {
81 ret = ioctl(mnt_fd, BTRFS_IOC_TREE_SEARCH, &args);
82 if (ret < 0) {
83 fprintf(stderr,
84 "ERROR: can't perform the search - %s\n",
85 strerror(errno));
86 return 0;
88 /* the ioctl returns the number of item it found in nr_items */
89 if (sk->nr_items == 0)
90 break;
92 off = 0;
93 for (i = 0; i < sk->nr_items; i++) {
94 struct btrfs_root_item *item;
95 sh = (struct btrfs_ioctl_search_header *)(args.buf +
96 off);
98 off += sizeof(*sh);
99 item = (struct btrfs_root_item *)(args.buf + off);
100 off += sh->len;
102 sk->min_objectid = sh->objectid;
103 sk->min_type = sh->type;
104 sk->min_offset = sh->offset;
106 if (sh->objectid > root_id)
107 break;
109 if (sh->objectid == root_id &&
110 sh->type == BTRFS_ROOT_ITEM_KEY) {
111 if (sh->len > buf_len) {
112 /* btrfs-progs is too old for kernel */
113 fprintf(stderr,
114 "ERROR: buf for read_root_item_raw() is too small, get newer btrfs tools!\n");
115 return -EOVERFLOW;
117 memcpy(buf, item, sh->len);
118 *read_len = sh->len;
119 found = 1;
122 if (sk->min_offset < (u64)-1)
123 sk->min_offset++;
124 else
125 break;
127 if (sk->min_type != BTRFS_ROOT_ITEM_KEY ||
128 sk->min_objectid != root_id)
129 break;
132 return found ? 0 : -ENOENT;
136 * Read a root item from the tree. In case we detect a root item smaller then
137 * sizeof(root_item), we know it's an old version of the root structure and
138 * initialize all new fields to zero. The same happens if we detect mismatching
139 * generation numbers as then we know the root was once mounted with an older
140 * kernel that was not aware of the root item structure change.
142 static int btrfs_read_root_item(int mnt_fd, u64 root_id,
143 struct btrfs_root_item *item)
145 int ret;
146 u32 read_len;
148 ret = btrfs_read_root_item_raw(mnt_fd, root_id, sizeof(*item),
149 &read_len, item);
150 if (ret)
151 return ret;
153 if (read_len < sizeof(*item) ||
154 btrfs_root_generation(item) != btrfs_root_generation_v2(item))
155 memset(&item->generation_v2, 0,
156 sizeof(*item) - offsetof(struct btrfs_root_item,
157 generation_v2));
159 return 0;
162 int btrfs_subvolid_resolve(int fd, char *path, size_t path_len, u64 subvol_id)
164 if (path_len < 1)
165 return -EOVERFLOW;
166 path[0] = '\0';
167 path_len--;
168 path[path_len] = '\0';
169 return btrfs_subvolid_resolve_sub(fd, path, &path_len, subvol_id);
172 static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
173 u64 subvol_id)
175 int ret;
176 struct btrfs_ioctl_search_args search_arg;
177 struct btrfs_ioctl_ino_lookup_args ino_lookup_arg;
178 struct btrfs_ioctl_search_header *search_header;
179 struct btrfs_root_ref *backref_item;
181 if (subvol_id == BTRFS_FS_TREE_OBJECTID) {
182 if (*path_len < 1)
183 return -EOVERFLOW;
184 *path = '\0';
185 (*path_len)--;
186 return 0;
189 memset(&search_arg, 0, sizeof(search_arg));
190 search_arg.key.tree_id = BTRFS_ROOT_TREE_OBJECTID;
191 search_arg.key.min_objectid = subvol_id;
192 search_arg.key.max_objectid = subvol_id;
193 search_arg.key.min_type = BTRFS_ROOT_BACKREF_KEY;
194 search_arg.key.max_type = BTRFS_ROOT_BACKREF_KEY;
195 search_arg.key.max_offset = (u64)-1;
196 search_arg.key.max_transid = (u64)-1;
197 search_arg.key.nr_items = 1;
198 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_arg);
199 if (ret) {
200 fprintf(stderr,
201 "ioctl(BTRFS_IOC_TREE_SEARCH, subvol_id %llu) ret=%d, error: %s\n",
202 (unsigned long long)subvol_id, ret, strerror(errno));
203 return ret;
206 if (search_arg.key.nr_items < 1) {
207 fprintf(stderr,
208 "failed to lookup subvol_id %llu!\n",
209 (unsigned long long)subvol_id);
210 return -ENOENT;
212 search_header = (struct btrfs_ioctl_search_header *)search_arg.buf;
213 backref_item = (struct btrfs_root_ref *)(search_header + 1);
214 if (search_header->offset != BTRFS_FS_TREE_OBJECTID) {
215 int sub_ret;
217 sub_ret = btrfs_subvolid_resolve_sub(fd, path, path_len,
218 search_header->offset);
219 if (sub_ret)
220 return sub_ret;
221 if (*path_len < 1)
222 return -EOVERFLOW;
223 strcat(path, "/");
224 (*path_len)--;
227 if (btrfs_stack_root_ref_dirid(backref_item) !=
228 BTRFS_FIRST_FREE_OBJECTID) {
229 int len;
231 memset(&ino_lookup_arg, 0, sizeof(ino_lookup_arg));
232 ino_lookup_arg.treeid = search_header->offset;
233 ino_lookup_arg.objectid =
234 btrfs_stack_root_ref_dirid(backref_item);
235 ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_lookup_arg);
236 if (ret) {
237 fprintf(stderr,
238 "ioctl(BTRFS_IOC_INO_LOOKUP) ret=%d, error: %s\n",
239 ret, strerror(errno));
240 return ret;
243 len = strlen(ino_lookup_arg.name);
244 if (*path_len < len)
245 return -EOVERFLOW;
246 strcat(path, ino_lookup_arg.name);
247 (*path_len) -= len;
250 if (*path_len < btrfs_stack_root_ref_name_len(backref_item))
251 return -EOVERFLOW;
252 strncat(path, (char *)(backref_item + 1),
253 btrfs_stack_root_ref_name_len(backref_item));
254 (*path_len) -= btrfs_stack_root_ref_name_len(backref_item);
255 return 0;
258 void subvol_uuid_search_add(struct subvol_uuid_search *s,
259 struct subvol_info *si)
261 if (si) {
262 free(si->path);
263 free(si);
267 struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
268 u64 root_id, const u8 *uuid, u64 transid,
269 const char *path,
270 enum subvol_search_type type)
272 int ret = 0;
273 struct btrfs_root_item root_item;
274 struct subvol_info *info = NULL;
276 switch (type) {
277 case subvol_search_by_received_uuid:
278 ret = btrfs_lookup_uuid_received_subvol_item(s->mnt_fd, uuid,
279 &root_id);
280 break;
281 case subvol_search_by_uuid:
282 ret = btrfs_lookup_uuid_subvol_item(s->mnt_fd, uuid, &root_id);
283 break;
284 case subvol_search_by_root_id:
285 break;
286 case subvol_search_by_path:
287 ret = btrfs_get_root_id_by_sub_path(s->mnt_fd, path, &root_id);
288 break;
289 default:
290 ret = -EINVAL;
291 break;
294 if (ret)
295 goto out;
297 ret = btrfs_read_root_item(s->mnt_fd, root_id, &root_item);
298 if (ret)
299 goto out;
301 info = calloc(1, sizeof(*info));
302 info->root_id = root_id;
303 memcpy(info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
304 memcpy(info->received_uuid, root_item.received_uuid, BTRFS_UUID_SIZE);
305 memcpy(info->parent_uuid, root_item.parent_uuid, BTRFS_UUID_SIZE);
306 info->ctransid = btrfs_root_ctransid(&root_item);
307 info->otransid = btrfs_root_otransid(&root_item);
308 info->stransid = btrfs_root_stransid(&root_item);
309 info->rtransid = btrfs_root_rtransid(&root_item);
310 if (type == subvol_search_by_path) {
311 info->path = strdup(path);
312 } else {
313 info->path = malloc(BTRFS_PATH_NAME_MAX);
314 ret = btrfs_subvolid_resolve(s->mnt_fd, info->path,
315 BTRFS_PATH_NAME_MAX, root_id);
318 out:
319 if (ret && info) {
320 free(info->path);
321 free(info);
322 info = NULL;
325 return info;
328 int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
330 s->mnt_fd = mnt_fd;
332 return 0;
335 void subvol_uuid_search_finit(struct subvol_uuid_search *s)
339 char *path_cat(const char *p1, const char *p2)
341 int p1_len = strlen(p1);
342 int p2_len = strlen(p2);
343 char *new = malloc(p1_len + p2_len + 2);
345 if (p1_len && p1[p1_len - 1] == '/')
346 p1_len--;
347 if (p2_len && p2[p2_len - 1] == '/')
348 p2_len--;
349 sprintf(new, "%.*s/%.*s", p1_len, p1, p2_len, p2);
350 return new;
353 char *path_cat3(const char *p1, const char *p2, const char *p3)
355 int p1_len = strlen(p1);
356 int p2_len = strlen(p2);
357 int p3_len = strlen(p3);
358 char *new = malloc(p1_len + p2_len + p3_len + 3);
360 if (p1_len && p1[p1_len - 1] == '/')
361 p1_len--;
362 if (p2_len && p2[p2_len - 1] == '/')
363 p2_len--;
364 if (p3_len && p3[p3_len - 1] == '/')
365 p3_len--;
366 sprintf(new, "%.*s/%.*s/%.*s", p1_len, p1, p2_len, p2, p3_len, p3);
367 return new;