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.
21 #include <sys/ioctl.h>
22 #include <uuid/uuid.h>
25 #include "send-utils.h"
27 #include "btrfs-list.h"
29 static int btrfs_subvolid_resolve_sub(int fd
, char *path
, size_t *path_len
,
32 static int btrfs_get_root_id_by_sub_path(int mnt_fd
, const char *sub_path
,
38 subvol_fd
= openat(mnt_fd
, sub_path
, O_RDONLY
);
41 fprintf(stderr
, "ERROR: open %s failed. %s\n", sub_path
,
46 ret
= btrfs_list_get_path_rootid(subvol_fd
, root_id
);
51 static int btrfs_read_root_item_raw(int mnt_fd
, u64 root_id
, size_t buf_len
,
52 u32
*read_len
, void *buf
)
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;
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
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;
81 ret
= ioctl(mnt_fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
84 "ERROR: can't perform the search - %s\n",
88 /* the ioctl returns the number of item it found in nr_items */
89 if (sk
->nr_items
== 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
+
99 item
= (struct btrfs_root_item
*)(args
.buf
+ off
);
102 sk
->min_objectid
= sh
->objectid
;
103 sk
->min_type
= sh
->type
;
104 sk
->min_offset
= sh
->offset
;
106 if (sh
->objectid
> root_id
)
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 */
114 "ERROR: buf for read_root_item_raw() is too small, get newer btrfs tools!\n");
117 memcpy(buf
, item
, sh
->len
);
122 if (sk
->min_offset
< (u64
)-1)
127 if (sk
->min_type
!= BTRFS_ROOT_ITEM_KEY
||
128 sk
->min_objectid
!= root_id
)
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
)
148 ret
= btrfs_read_root_item_raw(mnt_fd
, root_id
, sizeof(*item
),
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
,
162 int btrfs_subvolid_resolve(int fd
, char *path
, size_t path_len
, u64 subvol_id
)
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
,
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
) {
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
);
201 "ioctl(BTRFS_IOC_TREE_SEARCH, subvol_id %llu) ret=%d, error: %s\n",
202 (unsigned long long)subvol_id
, ret
, strerror(errno
));
206 if (search_arg
.key
.nr_items
< 1) {
208 "failed to lookup subvol_id %llu!\n",
209 (unsigned long long)subvol_id
);
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
) {
217 sub_ret
= btrfs_subvolid_resolve_sub(fd
, path
, path_len
,
218 search_header
->offset
);
227 if (btrfs_stack_root_ref_dirid(backref_item
) !=
228 BTRFS_FIRST_FREE_OBJECTID
) {
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
);
238 "ioctl(BTRFS_IOC_INO_LOOKUP) ret=%d, error: %s\n",
239 ret
, strerror(errno
));
243 len
= strlen(ino_lookup_arg
.name
);
246 strcat(path
, ino_lookup_arg
.name
);
250 if (*path_len
< btrfs_stack_root_ref_name_len(backref_item
))
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
);
258 void subvol_uuid_search_add(struct subvol_uuid_search
*s
,
259 struct subvol_info
*si
)
267 struct subvol_info
*subvol_uuid_search(struct subvol_uuid_search
*s
,
268 u64 root_id
, const u8
*uuid
, u64 transid
,
270 enum subvol_search_type type
)
273 struct btrfs_root_item root_item
;
274 struct subvol_info
*info
= NULL
;
277 case subvol_search_by_received_uuid
:
278 ret
= btrfs_lookup_uuid_received_subvol_item(s
->mnt_fd
, uuid
,
281 case subvol_search_by_uuid
:
282 ret
= btrfs_lookup_uuid_subvol_item(s
->mnt_fd
, uuid
, &root_id
);
284 case subvol_search_by_root_id
:
286 case subvol_search_by_path
:
287 ret
= btrfs_get_root_id_by_sub_path(s
->mnt_fd
, path
, &root_id
);
297 ret
= btrfs_read_root_item(s
->mnt_fd
, root_id
, &root_item
);
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
);
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
);
328 int subvol_uuid_search_init(int mnt_fd
, struct subvol_uuid_search
*s
)
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] == '/')
347 if (p2_len
&& p2
[p2_len
- 1] == '/')
349 sprintf(new, "%.*s/%.*s", p1_len
, p1
, p2_len
, p2
);
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] == '/')
362 if (p2_len
&& p2
[p2_len
- 1] == '/')
364 if (p3_len
&& p3
[p3_len
- 1] == '/')
366 sprintf(new, "%.*s/%.*s/%.*s", p1_len
, p1
, p2_len
, p2
, p3_len
, p3
);