]>
Commit | Line | Data |
---|---|---|
c9b0b55d MT |
1 | diff --git a/Makefile b/Makefile |
2 | index 8097b5a..6e6f6c6 100644 | |
3 | --- a/Makefile | |
4 | +++ b/Makefile | |
5 | @@ -4,7 +4,7 @@ CFLAGS = -g -Werror -Os | |
6 | objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ | |
7 | root-tree.o dir-item.o file-item.o inode-item.o \ | |
8 | inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ | |
9 | - volumes.o utils.o | |
10 | + volumes.o utils.o btrfs-list.o | |
11 | ||
12 | # | |
13 | CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ | |
14 | @@ -16,7 +16,9 @@ prefix ?= /usr/local | |
15 | bindir = $(prefix)/bin | |
16 | LIBS=-luuid | |
17 | ||
18 | -progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck | |
19 | +progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ | |
20 | + btrfs \ | |
21 | + btrfs-map-logical | |
22 | ||
23 | # make C=1 to enable sparse | |
24 | ifdef C | |
25 | @@ -35,6 +37,10 @@ all: version $(progs) manpages | |
26 | version: | |
27 | bash version.sh | |
28 | ||
29 | +btrfs: $(objects) btrfs.o btrfs_cmds.o | |
30 | + gcc $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o \ | |
31 | + $(objects) $(LDFLAGS) $(LIBS) | |
32 | + | |
33 | btrfsctl: $(objects) btrfsctl.o | |
34 | gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) | |
35 | ||
36 | @@ -53,9 +59,15 @@ mkfs.btrfs: $(objects) mkfs.o | |
37 | btrfs-debug-tree: $(objects) debug-tree.o | |
38 | gcc $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS) | |
39 | ||
40 | +btrfs-zero-log: $(objects) btrfs-zero-log.o | |
41 | + gcc $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS) | |
42 | + | |
43 | btrfstune: $(objects) btrfstune.o | |
44 | gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS) | |
45 | ||
46 | +btrfs-map-logical: $(objects) btrfs-map-logical.o | |
47 | + gcc $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS) | |
48 | + | |
49 | btrfs-image: $(objects) btrfs-image.o | |
50 | gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) | |
51 | ||
52 | @@ -66,7 +78,10 @@ quick-test: $(objects) quick-test.o | |
53 | gcc $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS) | |
54 | ||
55 | convert: $(objects) convert.o | |
56 | - gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) $(LIBS) | |
57 | + gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS) | |
58 | + | |
59 | +ioctl-test: $(objects) ioctl-test.o | |
60 | + gcc $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS) | |
61 | ||
62 | manpages: | |
63 | cd man; make | |
64 | diff --git a/btrfs-defrag.c b/btrfs-defrag.c | |
65 | new file mode 100644 | |
66 | index 0000000..8f1525a | |
67 | --- /dev/null | |
68 | +++ b/btrfs-defrag.c | |
69 | @@ -0,0 +1,39 @@ | |
70 | +/* | |
71 | + * Copyright (C) 2010 Oracle. All rights reserved. | |
72 | + * | |
73 | + * This program is free software; you can redistribute it and/or | |
74 | + * modify it under the terms of the GNU General Public | |
75 | + * License v2 as published by the Free Software Foundation. | |
76 | + * | |
77 | + * This program is distributed in the hope that it will be useful, | |
78 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
79 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
80 | + * General Public License for more details. | |
81 | + * | |
82 | + * You should have received a copy of the GNU General Public | |
83 | + * License along with this program; if not, write to the | |
84 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
85 | + * Boston, MA 021110-1307, USA. | |
86 | + */ | |
87 | + | |
88 | +#ifndef __CHECKER__ | |
89 | +#include <sys/ioctl.h> | |
90 | +#include <sys/mount.h> | |
91 | +#include "ioctl.h" | |
92 | +#endif | |
93 | +#include <stdio.h> | |
94 | +#include <stdlib.h> | |
95 | +#include <sys/types.h> | |
96 | +#include <sys/stat.h> | |
97 | +#include <fcntl.h> | |
98 | +#include <ctype.h> | |
99 | +#include <unistd.h> | |
100 | +#include <dirent.h> | |
101 | +#include <libgen.h> | |
102 | +#include <getopt.h> | |
103 | +#include "kerncompat.h" | |
104 | +#include "ctree.h" | |
105 | +#include "transaction.h" | |
106 | +#include "utils.h" | |
107 | +#include "version.h" | |
108 | + | |
109 | diff --git a/btrfs-list.c b/btrfs-list.c | |
110 | new file mode 100644 | |
111 | index 0000000..93766a8 | |
112 | --- /dev/null | |
113 | +++ b/btrfs-list.c | |
114 | @@ -0,0 +1,835 @@ | |
115 | +/* | |
116 | + * Copyright (C) 2010 Oracle. All rights reserved. | |
117 | + * | |
118 | + * This program is free software; you can redistribute it and/or | |
119 | + * modify it under the terms of the GNU General Public | |
120 | + * License v2 as published by the Free Software Foundation. | |
121 | + * | |
122 | + * This program is distributed in the hope that it will be useful, | |
123 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
124 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
125 | + * General Public License for more details. | |
126 | + * | |
127 | + * You should have received a copy of the GNU General Public | |
128 | + * License along with this program; if not, write to the | |
129 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
130 | + * Boston, MA 021110-1307, USA. | |
131 | + */ | |
132 | + | |
133 | +#define _GNU_SOURCE | |
134 | +#ifndef __CHECKER__ | |
135 | +#include <sys/ioctl.h> | |
136 | +#include <sys/mount.h> | |
137 | +#include "ioctl.h" | |
138 | +#endif | |
139 | +#include <stdio.h> | |
140 | +#include <stdlib.h> | |
141 | +#include <sys/types.h> | |
142 | +#include <sys/stat.h> | |
143 | +#include <fcntl.h> | |
144 | +#include <unistd.h> | |
145 | +#include <dirent.h> | |
146 | +#include <libgen.h> | |
147 | +#include "kerncompat.h" | |
148 | +#include "ctree.h" | |
149 | +#include "transaction.h" | |
150 | +#include "utils.h" | |
151 | +#include "version.h" | |
152 | + | |
153 | +/* we store all the roots we find in an rbtree so that we can | |
154 | + * search for them later. | |
155 | + */ | |
156 | +struct root_lookup { | |
157 | + struct rb_root root; | |
158 | +}; | |
159 | + | |
160 | +/* | |
161 | + * one of these for each root we find. | |
162 | + */ | |
163 | +struct root_info { | |
164 | + struct rb_node rb_node; | |
165 | + | |
166 | + /* this root's id */ | |
167 | + u64 root_id; | |
168 | + | |
169 | + /* the id of the root that references this one */ | |
170 | + u64 ref_tree; | |
171 | + | |
172 | + /* the dir id we're in from ref_tree */ | |
173 | + u64 dir_id; | |
174 | + | |
175 | + /* path from the subvol we live in to this root, including the | |
176 | + * root's name. This is null until we do the extra lookup ioctl. | |
177 | + */ | |
178 | + char *path; | |
179 | + | |
180 | + /* the name of this root in the directory it lives in */ | |
181 | + char name[]; | |
182 | +}; | |
183 | + | |
184 | +static void root_lookup_init(struct root_lookup *tree) | |
185 | +{ | |
186 | + tree->root.rb_node = NULL; | |
187 | +} | |
188 | + | |
189 | +static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree) | |
190 | +{ | |
191 | + if (entry->root_id > root_id) | |
192 | + return 1; | |
193 | + if (entry->root_id < root_id) | |
194 | + return -1; | |
195 | + if (entry->ref_tree > ref_tree) | |
196 | + return 1; | |
197 | + if (entry->ref_tree < ref_tree) | |
198 | + return -1; | |
199 | + return 0; | |
200 | +} | |
201 | + | |
202 | +/* | |
203 | + * insert a new root into the tree. returns the existing root entry | |
204 | + * if one is already there. Both root_id and ref_tree are used | |
205 | + * as the key | |
206 | + */ | |
207 | +static struct rb_node *tree_insert(struct rb_root *root, u64 root_id, | |
208 | + u64 ref_tree, struct rb_node *node) | |
209 | +{ | |
210 | + struct rb_node ** p = &root->rb_node; | |
211 | + struct rb_node * parent = NULL; | |
212 | + struct root_info *entry; | |
213 | + int comp; | |
214 | + | |
215 | + while(*p) { | |
216 | + parent = *p; | |
217 | + entry = rb_entry(parent, struct root_info, rb_node); | |
218 | + | |
219 | + comp = comp_entry(entry, root_id, ref_tree); | |
220 | + | |
221 | + if (comp < 0) | |
222 | + p = &(*p)->rb_left; | |
223 | + else if (comp > 0) | |
224 | + p = &(*p)->rb_right; | |
225 | + else | |
226 | + return parent; | |
227 | + } | |
228 | + | |
229 | + entry = rb_entry(parent, struct root_info, rb_node); | |
230 | + rb_link_node(node, parent, p); | |
231 | + rb_insert_color(node, root); | |
232 | + return NULL; | |
233 | +} | |
234 | + | |
235 | +/* | |
236 | + * find a given root id in the tree. We return the smallest one, | |
237 | + * rb_next can be used to move forward looking for more if required | |
238 | + */ | |
239 | +static struct root_info *tree_search(struct rb_root *root, u64 root_id) | |
240 | +{ | |
241 | + struct rb_node * n = root->rb_node; | |
242 | + struct root_info *entry; | |
243 | + | |
244 | + while(n) { | |
245 | + entry = rb_entry(n, struct root_info, rb_node); | |
246 | + | |
247 | + if (entry->root_id < root_id) | |
248 | + n = n->rb_left; | |
249 | + else if (entry->root_id > root_id) | |
250 | + n = n->rb_right; | |
251 | + else { | |
252 | + struct root_info *prev; | |
253 | + struct rb_node *prev_n; | |
254 | + while (1) { | |
255 | + prev_n = rb_prev(n); | |
256 | + if (!prev_n) | |
257 | + break; | |
258 | + prev = rb_entry(prev_n, struct root_info, | |
259 | + rb_node); | |
260 | + if (prev->root_id != root_id) | |
261 | + break; | |
262 | + entry = prev; | |
263 | + n = prev_n; | |
264 | + } | |
265 | + return entry; | |
266 | + } | |
267 | + } | |
268 | + return NULL; | |
269 | +} | |
270 | + | |
271 | +/* | |
272 | + * this allocates a new root in the lookup tree. | |
273 | + * | |
274 | + * root_id should be the object id of the root | |
275 | + * | |
276 | + * ref_tree is the objectid of the referring root. | |
277 | + * | |
278 | + * dir_id is the directory in ref_tree where this root_id can be found. | |
279 | + * | |
280 | + * name is the name of root_id in that directory | |
281 | + * | |
282 | + * name_len is the length of name | |
283 | + */ | |
284 | +static int add_root(struct root_lookup *root_lookup, | |
285 | + u64 root_id, u64 ref_tree, u64 dir_id, char *name, | |
286 | + int name_len) | |
287 | +{ | |
288 | + struct root_info *ri; | |
289 | + struct rb_node *ret; | |
290 | + ri = malloc(sizeof(*ri) + name_len + 1); | |
291 | + if (!ri) { | |
292 | + printf("memory allocation failed\n"); | |
293 | + exit(1); | |
294 | + } | |
295 | + memset(ri, 0, sizeof(*ri) + name_len + 1); | |
296 | + ri->path = NULL; | |
297 | + ri->dir_id = dir_id; | |
298 | + ri->root_id = root_id; | |
299 | + ri->ref_tree = ref_tree; | |
300 | + strncpy(ri->name, name, name_len); | |
301 | + | |
302 | + ret = tree_insert(&root_lookup->root, root_id, ref_tree, &ri->rb_node); | |
303 | + if (ret) { | |
304 | + printf("failed to insert tree %llu\n", (unsigned long long)root_id); | |
305 | + exit(1); | |
306 | + } | |
307 | + return 0; | |
308 | +} | |
309 | + | |
310 | +/* | |
311 | + * for a given root_info, search through the root_lookup tree to construct | |
312 | + * the full path name to it. | |
313 | + * | |
314 | + * This can't be called until all the root_info->path fields are filled | |
315 | + * in by lookup_ino_path | |
316 | + */ | |
317 | +static int resolve_root(struct root_lookup *rl, struct root_info *ri) | |
318 | +{ | |
319 | + u64 top_id; | |
320 | + char *full_path = NULL; | |
321 | + int len = 0; | |
322 | + struct root_info *found; | |
323 | + | |
324 | + /* | |
325 | + * we go backwards from the root_info object and add pathnames | |
326 | + * from parent directories as we go. | |
327 | + */ | |
328 | + found = ri; | |
329 | + while (1) { | |
330 | + char *tmp; | |
331 | + u64 next; | |
332 | + int add_len = strlen(found->path); | |
333 | + | |
334 | + /* room for / and for null */ | |
335 | + tmp = malloc(add_len + 2 + len); | |
336 | + if (full_path) { | |
337 | + memcpy(tmp + add_len + 1, full_path, len); | |
338 | + tmp[add_len] = '/'; | |
339 | + memcpy(tmp, found->path, add_len); | |
340 | + tmp [add_len + len + 1] = '\0'; | |
341 | + free(full_path); | |
342 | + full_path = tmp; | |
343 | + len += add_len + 1; | |
344 | + } else { | |
345 | + full_path = strdup(found->path); | |
346 | + len = add_len; | |
347 | + } | |
348 | + | |
349 | + next = found->ref_tree; | |
350 | + /* if the ref_tree refers to ourselves, we're at the top */ | |
351 | + if (next == found->root_id) { | |
352 | + top_id = next; | |
353 | + break; | |
354 | + } | |
355 | + | |
356 | + /* | |
357 | + * if the ref_tree wasn't in our tree of roots, we're | |
358 | + * at the top | |
359 | + */ | |
360 | + found = tree_search(&rl->root, next); | |
361 | + if (!found) { | |
362 | + top_id = next; | |
363 | + break; | |
364 | + } | |
365 | + } | |
366 | + printf("ID %llu top level %llu path %s\n", ri->root_id, top_id, | |
367 | + full_path); | |
368 | + free(full_path); | |
369 | + return 0; | |
370 | +} | |
371 | + | |
372 | +/* | |
373 | + * for a single root_info, ask the kernel to give us a path name | |
374 | + * inside it's ref_root for the dir_id where it lives. | |
375 | + * | |
376 | + * This fills in root_info->path with the path to the directory and and | |
377 | + * appends this root's name. | |
378 | + */ | |
379 | +static int lookup_ino_path(int fd, struct root_info *ri) | |
380 | +{ | |
381 | + struct btrfs_ioctl_ino_lookup_args args; | |
382 | + int ret; | |
383 | + | |
384 | + if (ri->path) | |
385 | + return 0; | |
386 | + | |
387 | + memset(&args, 0, sizeof(args)); | |
388 | + args.treeid = ri->ref_tree; | |
389 | + args.objectid = ri->dir_id; | |
390 | + | |
391 | + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); | |
392 | + if (ret) { | |
393 | + fprintf(stderr, "ERROR: Failed to lookup path for root %llu\n", | |
394 | + (unsigned long long)ri->ref_tree); | |
395 | + return ret; | |
396 | + } | |
397 | + | |
398 | + if (args.name[0]) { | |
399 | + /* | |
400 | + * we're in a subdirectory of ref_tree, the kernel ioctl | |
401 | + * puts a / in there for us | |
402 | + */ | |
403 | + ri->path = malloc(strlen(ri->name) + strlen(args.name) + 1); | |
404 | + if (!ri->path) { | |
405 | + perror("malloc failed"); | |
406 | + exit(1); | |
407 | + } | |
408 | + strcpy(ri->path, args.name); | |
409 | + strcat(ri->path, ri->name); | |
410 | + } else { | |
411 | + /* we're at the root of ref_tree */ | |
412 | + ri->path = strdup(ri->name); | |
413 | + if (!ri->path) { | |
414 | + perror("strdup failed"); | |
415 | + exit(1); | |
416 | + } | |
417 | + } | |
418 | + return 0; | |
419 | +} | |
420 | + | |
421 | +/* finding the generation for a given path is a two step process. | |
422 | + * First we use the inode loookup routine to find out the root id | |
423 | + * | |
424 | + * Then we use the tree search ioctl to scan all the root items for a | |
425 | + * given root id and spit out the latest generation we can find | |
426 | + */ | |
427 | +static u64 find_root_gen(int fd) | |
428 | +{ | |
429 | + struct btrfs_ioctl_ino_lookup_args ino_args; | |
430 | + int ret; | |
431 | + struct btrfs_ioctl_search_args args; | |
432 | + struct btrfs_ioctl_search_key *sk = &args.key; | |
433 | + struct btrfs_ioctl_search_header *sh; | |
434 | + unsigned long off = 0; | |
435 | + u64 max_found = 0; | |
436 | + int i; | |
437 | + | |
438 | + memset(&ino_args, 0, sizeof(ino_args)); | |
439 | + ino_args.objectid = BTRFS_FIRST_FREE_OBJECTID; | |
440 | + | |
441 | + /* this ioctl fills in ino_args->treeid */ | |
442 | + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args); | |
443 | + if (ret) { | |
444 | + fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu\n", | |
445 | + (unsigned long long)BTRFS_FIRST_FREE_OBJECTID); | |
446 | + return 0; | |
447 | + } | |
448 | + | |
449 | + memset(&args, 0, sizeof(args)); | |
450 | + | |
451 | + sk->tree_id = 1; | |
452 | + | |
453 | + /* | |
454 | + * there may be more than one ROOT_ITEM key if there are | |
455 | + * snapshots pending deletion, we have to loop through | |
456 | + * them. | |
457 | + */ | |
458 | + sk->min_objectid = ino_args.treeid; | |
459 | + sk->max_objectid = ino_args.treeid; | |
460 | + sk->max_type = BTRFS_ROOT_ITEM_KEY; | |
461 | + sk->min_type = BTRFS_ROOT_ITEM_KEY; | |
462 | + sk->max_offset = (u64)-1; | |
463 | + sk->max_transid = (u64)-1; | |
464 | + sk->nr_items = 4096; | |
465 | + | |
466 | + while (1) { | |
467 | + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); | |
468 | + if (ret < 0) { | |
469 | + fprintf(stderr, "ERROR: can't perform the search\n"); | |
470 | + return 0; | |
471 | + } | |
472 | + /* the ioctl returns the number of item it found in nr_items */ | |
473 | + if (sk->nr_items == 0) | |
474 | + break; | |
475 | + | |
476 | + off = 0; | |
477 | + for (i = 0; i < sk->nr_items; i++) { | |
478 | + struct btrfs_root_item *item; | |
479 | + sh = (struct btrfs_ioctl_search_header *)(args.buf + | |
480 | + off); | |
481 | + | |
482 | + off += sizeof(*sh); | |
483 | + item = (struct btrfs_root_item *)(args.buf + off); | |
484 | + off += sh->len; | |
485 | + | |
486 | + sk->min_objectid = sh->objectid; | |
487 | + sk->min_type = sh->type; | |
488 | + sk->min_offset = sh->offset; | |
489 | + | |
490 | + if (sh->objectid > ino_args.treeid) | |
491 | + break; | |
492 | + | |
493 | + if (sh->objectid == ino_args.treeid && | |
494 | + sh->type == BTRFS_ROOT_ITEM_KEY) { | |
495 | + max_found = max(max_found, | |
496 | + btrfs_root_generation(item)); | |
497 | + } | |
498 | + } | |
499 | + if (sk->min_offset < (u64)-1) | |
500 | + sk->min_offset++; | |
501 | + else | |
502 | + break; | |
503 | + | |
504 | + if (sk->min_type != BTRFS_ROOT_ITEM_KEY) | |
505 | + break; | |
506 | + if (sk->min_objectid != BTRFS_ROOT_ITEM_KEY) | |
507 | + break; | |
508 | + } | |
509 | + return max_found; | |
510 | +} | |
511 | + | |
512 | +/* pass in a directory id and this will return | |
513 | + * the full path of the parent directory inside its | |
514 | + * subvolume root. | |
515 | + * | |
516 | + * It may return NULL if it is in the root, or an ERR_PTR if things | |
517 | + * go badly. | |
518 | + */ | |
519 | +static char *__ino_resolve(int fd, u64 dirid) | |
520 | +{ | |
521 | + struct btrfs_ioctl_ino_lookup_args args; | |
522 | + int ret; | |
523 | + char *full; | |
524 | + | |
525 | + memset(&args, 0, sizeof(args)); | |
526 | + args.objectid = dirid; | |
527 | + | |
528 | + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); | |
529 | + if (ret) { | |
530 | + fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu\n", | |
531 | + (unsigned long long)dirid); | |
532 | + return ERR_PTR(ret); | |
533 | + } | |
534 | + | |
535 | + if (args.name[0]) { | |
536 | + /* | |
537 | + * we're in a subdirectory of ref_tree, the kernel ioctl | |
538 | + * puts a / in there for us | |
539 | + */ | |
540 | + full = strdup(args.name); | |
541 | + if (!full) { | |
542 | + perror("malloc failed"); | |
543 | + return ERR_PTR(-ENOMEM); | |
544 | + } | |
545 | + } else { | |
546 | + /* we're at the root of ref_tree */ | |
547 | + full = NULL; | |
548 | + } | |
549 | + return full; | |
550 | +} | |
551 | + | |
552 | +/* | |
553 | + * simple string builder, returning a new string with both | |
554 | + * dirid and name | |
555 | + */ | |
556 | +char *build_name(char *dirid, char *name) | |
557 | +{ | |
558 | + char *full; | |
559 | + if (!dirid) | |
560 | + return strdup(name); | |
561 | + | |
562 | + full = malloc(strlen(dirid) + strlen(name) + 1); | |
563 | + if (!full) | |
564 | + return NULL; | |
565 | + strcpy(full, dirid); | |
566 | + strcat(full, name); | |
567 | + return full; | |
568 | +} | |
569 | + | |
570 | +/* | |
571 | + * given an inode number, this returns the full path name inside the subvolume | |
572 | + * to that file/directory. cache_dirid and cache_name are used to | |
573 | + * cache the results so we can avoid tree searches if a later call goes | |
574 | + * to the same directory or file name | |
575 | + */ | |
576 | +static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name) | |
577 | + | |
578 | +{ | |
579 | + u64 dirid; | |
580 | + char *dirname; | |
581 | + char *name; | |
582 | + char *full; | |
583 | + int ret; | |
584 | + struct btrfs_ioctl_search_args args; | |
585 | + struct btrfs_ioctl_search_key *sk = &args.key; | |
586 | + struct btrfs_ioctl_search_header *sh; | |
587 | + unsigned long off = 0; | |
588 | + int namelen; | |
589 | + | |
590 | + memset(&args, 0, sizeof(args)); | |
591 | + | |
592 | + sk->tree_id = 0; | |
593 | + | |
594 | + /* | |
595 | + * step one, we search for the inode back ref. We just use the first | |
596 | + * one | |
597 | + */ | |
598 | + sk->min_objectid = ino; | |
599 | + sk->max_objectid = ino; | |
600 | + sk->max_type = BTRFS_INODE_REF_KEY; | |
601 | + sk->max_offset = (u64)-1; | |
602 | + sk->min_type = BTRFS_INODE_REF_KEY; | |
603 | + sk->max_transid = (u64)-1; | |
604 | + sk->nr_items = 1; | |
605 | + | |
606 | + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); | |
607 | + if (ret < 0) { | |
608 | + fprintf(stderr, "ERROR: can't perform the search\n"); | |
609 | + return NULL; | |
610 | + } | |
611 | + /* the ioctl returns the number of item it found in nr_items */ | |
612 | + if (sk->nr_items == 0) | |
613 | + return NULL; | |
614 | + | |
615 | + off = 0; | |
616 | + sh = (struct btrfs_ioctl_search_header *)(args.buf + off); | |
617 | + | |
618 | + if (sh->type == BTRFS_INODE_REF_KEY) { | |
619 | + struct btrfs_inode_ref *ref; | |
620 | + dirid = sh->offset; | |
621 | + | |
622 | + ref = (struct btrfs_inode_ref *)(sh + 1); | |
623 | + namelen = btrfs_stack_inode_ref_name_len(ref); | |
624 | + | |
625 | + name = (char *)(ref + 1); | |
626 | + name = strndup(name, namelen); | |
627 | + | |
628 | + /* use our cached value */ | |
629 | + if (dirid == *cache_dirid && *cache_name) { | |
630 | + dirname = *cache_name; | |
631 | + goto build; | |
632 | + } | |
633 | + } else { | |
634 | + return NULL; | |
635 | + } | |
636 | + /* | |
637 | + * the inode backref gives us the file name and the parent directory id. | |
638 | + * From here we use __ino_resolve to get the path to the parent | |
639 | + */ | |
640 | + dirname = __ino_resolve(fd, dirid); | |
641 | +build: | |
642 | + full = build_name(dirname, name); | |
643 | + if (*cache_name && dirname != *cache_name) | |
644 | + free(*cache_name); | |
645 | + | |
646 | + *cache_name = dirname; | |
647 | + *cache_dirid = dirid; | |
648 | + free(name); | |
649 | + | |
650 | + return full; | |
651 | +} | |
652 | + | |
653 | +int list_subvols(int fd) | |
654 | +{ | |
655 | + struct root_lookup root_lookup; | |
656 | + struct rb_node *n; | |
657 | + int ret; | |
658 | + struct btrfs_ioctl_search_args args; | |
659 | + struct btrfs_ioctl_search_key *sk = &args.key; | |
660 | + struct btrfs_ioctl_search_header *sh; | |
661 | + struct btrfs_root_ref *ref; | |
662 | + unsigned long off = 0; | |
663 | + int name_len; | |
664 | + char *name; | |
665 | + u64 dir_id; | |
666 | + int i; | |
667 | + | |
668 | + root_lookup_init(&root_lookup); | |
669 | + | |
670 | + memset(&args, 0, sizeof(args)); | |
671 | + | |
672 | + /* search in the tree of tree roots */ | |
673 | + sk->tree_id = 1; | |
674 | + | |
675 | + /* | |
676 | + * set the min and max to backref keys. The search will | |
677 | + * only send back this type of key now. | |
678 | + */ | |
679 | + sk->max_type = BTRFS_ROOT_BACKREF_KEY; | |
680 | + sk->min_type = BTRFS_ROOT_BACKREF_KEY; | |
681 | + | |
682 | + /* | |
683 | + * set all the other params to the max, we'll take any objectid | |
684 | + * and any trans | |
685 | + */ | |
686 | + sk->max_objectid = (u64)-1; | |
687 | + sk->max_offset = (u64)-1; | |
688 | + sk->max_transid = (u64)-1; | |
689 | + | |
690 | + /* just a big number, doesn't matter much */ | |
691 | + sk->nr_items = 4096; | |
692 | + | |
693 | + while(1) { | |
694 | + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); | |
695 | + if (ret < 0) { | |
696 | + fprintf(stderr, "ERROR: can't perform the search\n"); | |
697 | + return ret; | |
698 | + } | |
699 | + /* the ioctl returns the number of item it found in nr_items */ | |
700 | + if (sk->nr_items == 0) | |
701 | + break; | |
702 | + | |
703 | + off = 0; | |
704 | + | |
705 | + /* | |
706 | + * for each item, pull the key out of the header and then | |
707 | + * read the root_ref item it contains | |
708 | + */ | |
709 | + for (i = 0; i < sk->nr_items; i++) { | |
710 | + sh = (struct btrfs_ioctl_search_header *)(args.buf + | |
711 | + off); | |
712 | + off += sizeof(*sh); | |
713 | + if (sh->type == BTRFS_ROOT_BACKREF_KEY) { | |
714 | + ref = (struct btrfs_root_ref *)(args.buf + off); | |
715 | + name_len = btrfs_stack_root_ref_name_len(ref); | |
716 | + name = (char *)(ref + 1); | |
717 | + dir_id = btrfs_stack_root_ref_dirid(ref); | |
718 | + | |
719 | + add_root(&root_lookup, sh->objectid, sh->offset, | |
720 | + dir_id, name, name_len); | |
721 | + } | |
722 | + | |
723 | + off += sh->len; | |
724 | + | |
725 | + /* | |
726 | + * record the mins in sk so we can make sure the | |
727 | + * next search doesn't repeat this root | |
728 | + */ | |
729 | + sk->min_objectid = sh->objectid; | |
730 | + sk->min_type = sh->type; | |
731 | + sk->min_offset = sh->offset; | |
732 | + } | |
733 | + sk->nr_items = 4096; | |
734 | + /* this iteration is done, step forward one root for the next | |
735 | + * ioctl | |
736 | + */ | |
737 | + if (sk->min_objectid < (u64)-1) { | |
738 | + sk->min_objectid++; | |
739 | + sk->min_type = BTRFS_ROOT_BACKREF_KEY; | |
740 | + sk->min_offset = 0; | |
741 | + } else | |
742 | + break; | |
743 | + } | |
744 | + /* | |
745 | + * now we have an rbtree full of root_info objects, but we need to fill | |
746 | + * in their path names within the subvol that is referencing each one. | |
747 | + */ | |
748 | + n = rb_first(&root_lookup.root); | |
749 | + while (n) { | |
750 | + struct root_info *entry; | |
751 | + int ret; | |
752 | + entry = rb_entry(n, struct root_info, rb_node); | |
753 | + ret = lookup_ino_path(fd, entry); | |
754 | + if(ret < 0) | |
755 | + return ret; | |
756 | + n = rb_next(n); | |
757 | + } | |
758 | + | |
759 | + /* now that we have all the subvol-relative paths filled in, | |
760 | + * we have to string the subvols together so that we can get | |
761 | + * a path all the way back to the FS root | |
762 | + */ | |
763 | + n = rb_last(&root_lookup.root); | |
764 | + while (n) { | |
765 | + struct root_info *entry; | |
766 | + entry = rb_entry(n, struct root_info, rb_node); | |
767 | + resolve_root(&root_lookup, entry); | |
768 | + n = rb_prev(n); | |
769 | + } | |
770 | + | |
771 | + return ret; | |
772 | +} | |
773 | + | |
774 | +static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, | |
775 | + struct btrfs_file_extent_item *item, | |
776 | + u64 found_gen, u64 *cache_dirid, | |
777 | + char **cache_dir_name, u64 *cache_ino, | |
778 | + char **cache_full_name) | |
779 | +{ | |
780 | + u64 len = 0; | |
781 | + u64 disk_start = 0; | |
782 | + u64 disk_offset = 0; | |
783 | + u8 type; | |
784 | + int compressed = 0; | |
785 | + int flags = 0; | |
786 | + char *name = NULL; | |
787 | + | |
788 | + if (sh->objectid == *cache_ino) { | |
789 | + name = *cache_full_name; | |
790 | + } else if (*cache_full_name) { | |
791 | + free(*cache_full_name); | |
792 | + *cache_full_name = NULL; | |
793 | + } | |
794 | + if (!name) { | |
795 | + name = ino_resolve(fd, sh->objectid, cache_dirid, | |
796 | + cache_dir_name); | |
797 | + *cache_full_name = name; | |
798 | + *cache_ino = sh->objectid; | |
799 | + } | |
800 | + if (!name) | |
801 | + return -EIO; | |
802 | + | |
803 | + type = btrfs_stack_file_extent_type(item); | |
804 | + compressed = btrfs_stack_file_extent_compression(item); | |
805 | + | |
806 | + if (type == BTRFS_FILE_EXTENT_REG || | |
807 | + type == BTRFS_FILE_EXTENT_PREALLOC) { | |
808 | + disk_start = btrfs_stack_file_extent_disk_bytenr(item); | |
809 | + disk_offset = btrfs_stack_file_extent_offset(item); | |
810 | + len = btrfs_stack_file_extent_num_bytes(item); | |
811 | + } else if (type == BTRFS_FILE_EXTENT_INLINE) { | |
812 | + disk_start = 0; | |
813 | + disk_offset = 0; | |
814 | + len = btrfs_stack_file_extent_ram_bytes(item); | |
815 | + } else { | |
816 | + printf("unhandled extent type %d for inode %llu " | |
817 | + "file offset %llu gen %llu\n", | |
818 | + type, | |
819 | + (unsigned long long)sh->objectid, | |
820 | + (unsigned long long)sh->offset, | |
821 | + (unsigned long long)found_gen); | |
822 | + | |
823 | + return -EIO; | |
824 | + } | |
825 | + printf("inode %llu file offset %llu len %llu disk start %llu " | |
826 | + "offset %llu gen %llu flags ", | |
827 | + (unsigned long long)sh->objectid, | |
828 | + (unsigned long long)sh->offset, | |
829 | + (unsigned long long)len, | |
830 | + (unsigned long long)disk_start, | |
831 | + (unsigned long long)disk_offset, | |
832 | + (unsigned long long)found_gen); | |
833 | + | |
834 | + if (compressed) { | |
835 | + printf("COMPRESS"); | |
836 | + flags++; | |
837 | + } | |
838 | + if (type == BTRFS_FILE_EXTENT_PREALLOC) { | |
839 | + printf("%sPREALLOC", flags ? "|" : ""); | |
840 | + flags++; | |
841 | + } | |
842 | + if (type == BTRFS_FILE_EXTENT_INLINE) { | |
843 | + printf("%sINLINE", flags ? "|" : ""); | |
844 | + flags++; | |
845 | + } | |
846 | + if (!flags) | |
847 | + printf("NONE"); | |
848 | + | |
849 | + printf(" %s\n", name); | |
850 | + return 0; | |
851 | +} | |
852 | + | |
853 | +int find_updated_files(int fd, u64 root_id, u64 oldest_gen) | |
854 | +{ | |
855 | + int ret; | |
856 | + struct btrfs_ioctl_search_args args; | |
857 | + struct btrfs_ioctl_search_key *sk = &args.key; | |
858 | + struct btrfs_ioctl_search_header *sh; | |
859 | + struct btrfs_file_extent_item *item; | |
860 | + unsigned long off = 0; | |
861 | + u64 found_gen; | |
862 | + u64 max_found = 0; | |
863 | + int i; | |
864 | + u64 cache_dirid = 0; | |
865 | + u64 cache_ino = 0; | |
866 | + char *cache_dir_name = NULL; | |
867 | + char *cache_full_name = NULL; | |
868 | + struct btrfs_file_extent_item backup; | |
869 | + | |
870 | + memset(&backup, 0, sizeof(backup)); | |
871 | + memset(&args, 0, sizeof(args)); | |
872 | + | |
873 | + sk->tree_id = root_id; | |
874 | + | |
875 | + /* | |
876 | + * set all the other params to the max, we'll take any objectid | |
877 | + * and any trans | |
878 | + */ | |
879 | + sk->max_objectid = (u64)-1; | |
880 | + sk->max_offset = (u64)-1; | |
881 | + sk->max_transid = (u64)-1; | |
882 | + sk->max_type = BTRFS_EXTENT_DATA_KEY; | |
883 | + sk->min_transid = oldest_gen; | |
884 | + /* just a big number, doesn't matter much */ | |
885 | + sk->nr_items = 4096; | |
886 | + | |
887 | + max_found = find_root_gen(fd); | |
888 | + while(1) { | |
889 | + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); | |
890 | + if (ret < 0) { | |
891 | + fprintf(stderr, "ERROR: can't perform the search\n"); | |
892 | + return ret; | |
893 | + } | |
894 | + /* the ioctl returns the number of item it found in nr_items */ | |
895 | + if (sk->nr_items == 0) | |
896 | + break; | |
897 | + | |
898 | + off = 0; | |
899 | + | |
900 | + /* | |
901 | + * for each item, pull the key out of the header and then | |
902 | + * read the root_ref item it contains | |
903 | + */ | |
904 | + for (i = 0; i < sk->nr_items; i++) { | |
905 | + sh = (struct btrfs_ioctl_search_header *)(args.buf + | |
906 | + off); | |
907 | + off += sizeof(*sh); | |
908 | + | |
909 | + /* | |
910 | + * just in case the item was too big, pass something other | |
911 | + * than garbage | |
912 | + */ | |
913 | + if (sh->len == 0) | |
914 | + item = &backup; | |
915 | + else | |
916 | + item = (struct btrfs_file_extent_item *)(args.buf + | |
917 | + off); | |
918 | + found_gen = btrfs_stack_file_extent_generation(item); | |
919 | + if (sh->type == BTRFS_EXTENT_DATA_KEY && | |
920 | + found_gen >= oldest_gen) { | |
921 | + print_one_extent(fd, sh, item, found_gen, | |
922 | + &cache_dirid, &cache_dir_name, | |
923 | + &cache_ino, &cache_full_name); | |
924 | + } | |
925 | + off += sh->len; | |
926 | + | |
927 | + /* | |
928 | + * record the mins in sk so we can make sure the | |
929 | + * next search doesn't repeat this root | |
930 | + */ | |
931 | + sk->min_objectid = sh->objectid; | |
932 | + sk->min_offset = sh->offset; | |
933 | + sk->min_type = sh->type; | |
934 | + } | |
935 | + sk->nr_items = 4096; | |
936 | + if (sk->min_offset < (u64)-1) | |
937 | + sk->min_offset++; | |
938 | + else if (sk->min_objectid < (u64)-1) { | |
939 | + sk->min_objectid++; | |
940 | + sk->min_offset = 0; | |
941 | + sk->min_type = 0; | |
942 | + } else | |
943 | + break; | |
944 | + } | |
945 | + free(cache_dir_name); | |
946 | + free(cache_full_name); | |
947 | + printf("transid marker was %llu\n", (unsigned long long)max_found); | |
948 | + return ret; | |
949 | +} | |
950 | diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c | |
951 | new file mode 100644 | |
952 | index 0000000..a109c6a | |
953 | --- /dev/null | |
954 | +++ b/btrfs-map-logical.c | |
955 | @@ -0,0 +1,221 @@ | |
956 | +/* | |
957 | + * Copyright (C) 2009 Oracle. All rights reserved. | |
958 | + * | |
959 | + * This program is free software; you can redistribute it and/or | |
960 | + * modify it under the terms of the GNU General Public | |
961 | + * License v2 as published by the Free Software Foundation. | |
962 | + * | |
963 | + * This program is distributed in the hope that it will be useful, | |
964 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
965 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
966 | + * General Public License for more details. | |
967 | + * | |
968 | + * You should have received a copy of the GNU General Public | |
969 | + * License along with this program; if not, write to the | |
970 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
971 | + * Boston, MA 021110-1307, USA. | |
972 | + */ | |
973 | + | |
974 | +#define _XOPEN_SOURCE 500 | |
975 | +#define _GNU_SOURCE 1 | |
976 | +#include <stdio.h> | |
977 | +#include <stdlib.h> | |
978 | +#include <fcntl.h> | |
979 | +#include <unistd.h> | |
980 | +#include <getopt.h> | |
981 | +#include "kerncompat.h" | |
982 | +#include "ctree.h" | |
983 | +#include "volumes.h" | |
984 | +#include "disk-io.h" | |
985 | +#include "print-tree.h" | |
986 | +#include "transaction.h" | |
987 | +#include "list.h" | |
988 | +#include "version.h" | |
989 | + | |
990 | +/* we write the mirror info to stdout unless they are dumping the data | |
991 | + * to stdout | |
992 | + * */ | |
993 | +static FILE *info_file; | |
994 | + | |
995 | +struct extent_buffer *debug_read_block(struct btrfs_root *root, u64 bytenr, | |
996 | + u32 blocksize, int copy) | |
997 | +{ | |
998 | + int ret; | |
999 | + int dev_nr; | |
1000 | + struct extent_buffer *eb; | |
1001 | + u64 length; | |
1002 | + struct btrfs_multi_bio *multi = NULL; | |
1003 | + struct btrfs_device *device; | |
1004 | + int num_copies; | |
1005 | + int mirror_num = 1; | |
1006 | + | |
1007 | + eb = btrfs_find_create_tree_block(root, bytenr, blocksize); | |
1008 | + if (!eb) | |
1009 | + return NULL; | |
1010 | + | |
1011 | + dev_nr = 0; | |
1012 | + length = blocksize; | |
1013 | + while (1) { | |
1014 | + ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, | |
1015 | + eb->start, &length, &multi, mirror_num); | |
1016 | + BUG_ON(ret); | |
1017 | + device = multi->stripes[0].dev; | |
1018 | + eb->fd = device->fd; | |
1019 | + device->total_ios++; | |
1020 | + eb->dev_bytenr = multi->stripes[0].physical; | |
1021 | + | |
1022 | + fprintf(info_file, "mirror %d logical %Lu physical %Lu " | |
1023 | + "device %s\n", mirror_num, bytenr, eb->dev_bytenr, | |
1024 | + device->name); | |
1025 | + kfree(multi); | |
1026 | + | |
1027 | + if (!copy || mirror_num == copy) | |
1028 | + ret = read_extent_from_disk(eb); | |
1029 | + | |
1030 | + num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, | |
1031 | + eb->start, eb->len); | |
1032 | + if (num_copies == 1) | |
1033 | + break; | |
1034 | + | |
1035 | + mirror_num++; | |
1036 | + if (mirror_num > num_copies) | |
1037 | + break; | |
1038 | + } | |
1039 | + return eb; | |
1040 | +} | |
1041 | + | |
1042 | +static void print_usage(void) | |
1043 | +{ | |
1044 | + fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); | |
1045 | + fprintf(stderr, "\t-l Logical extent to map\n"); | |
1046 | + fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); | |
1047 | + fprintf(stderr, "\t-o Output file to hold the extent\n"); | |
1048 | + fprintf(stderr, "\t-s Number of bytes to read\n"); | |
1049 | + exit(1); | |
1050 | +} | |
1051 | + | |
1052 | +static struct option long_options[] = { | |
1053 | + /* { "byte-count", 1, NULL, 'b' }, */ | |
1054 | + { "logical", 1, NULL, 'l' }, | |
1055 | + { "copy", 1, NULL, 'c' }, | |
1056 | + { "output", 1, NULL, 'c' }, | |
1057 | + { "bytes", 1, NULL, 'b' }, | |
1058 | + { 0, 0, 0, 0} | |
1059 | +}; | |
1060 | + | |
1061 | +int main(int ac, char **av) | |
1062 | +{ | |
1063 | + struct cache_tree root_cache; | |
1064 | + struct btrfs_root *root; | |
1065 | + struct extent_buffer *eb; | |
1066 | + char *dev; | |
1067 | + char *output_file = NULL; | |
1068 | + u64 logical = 0; | |
1069 | + int ret = 0; | |
1070 | + int option_index = 0; | |
1071 | + int copy = 0; | |
1072 | + u64 bytes = 0; | |
1073 | + int out_fd = 0; | |
1074 | + int err; | |
1075 | + | |
1076 | + while(1) { | |
1077 | + int c; | |
1078 | + c = getopt_long(ac, av, "l:c:o:b:", long_options, | |
1079 | + &option_index); | |
1080 | + if (c < 0) | |
1081 | + break; | |
1082 | + switch(c) { | |
1083 | + case 'l': | |
1084 | + logical = atoll(optarg); | |
1085 | + if (logical == 0) { | |
1086 | + fprintf(stderr, | |
1087 | + "invalid extent number\n"); | |
1088 | + print_usage(); | |
1089 | + } | |
1090 | + break; | |
1091 | + case 'c': | |
1092 | + copy = atoi(optarg); | |
1093 | + if (copy == 0) { | |
1094 | + fprintf(stderr, | |
1095 | + "invalid copy number\n"); | |
1096 | + print_usage(); | |
1097 | + } | |
1098 | + break; | |
1099 | + case 'b': | |
1100 | + bytes = atoll(optarg); | |
1101 | + if (bytes == 0) { | |
1102 | + fprintf(stderr, | |
1103 | + "invalid byte count\n"); | |
1104 | + print_usage(); | |
1105 | + } | |
1106 | + break; | |
1107 | + case 'o': | |
1108 | + output_file = strdup(optarg); | |
1109 | + break; | |
1110 | + default: | |
1111 | + print_usage(); | |
1112 | + } | |
1113 | + } | |
1114 | + ac = ac - optind; | |
1115 | + if (ac == 0) | |
1116 | + print_usage(); | |
1117 | + if (logical == 0) | |
1118 | + print_usage(); | |
1119 | + if (copy < 0) | |
1120 | + print_usage(); | |
1121 | + | |
1122 | + dev = av[optind]; | |
1123 | + | |
1124 | + radix_tree_init(); | |
1125 | + cache_tree_init(&root_cache); | |
1126 | + | |
1127 | + root = open_ctree(dev, 0, 0); | |
1128 | + if (!root) { | |
1129 | + fprintf(stderr, "Open ctree failed\n"); | |
1130 | + exit(1); | |
1131 | + } | |
1132 | + | |
1133 | + if (output_file) { | |
1134 | + if (strcmp(output_file, "-") == 0) { | |
1135 | + out_fd = 1; | |
1136 | + info_file = stderr; | |
1137 | + } else { | |
1138 | + out_fd = open(output_file, O_RDWR | O_CREAT, 0600); | |
1139 | + if (out_fd < 0) | |
1140 | + goto close; | |
1141 | + err = ftruncate(out_fd, 0); | |
1142 | + if (err) { | |
1143 | + close(out_fd); | |
1144 | + goto close; | |
1145 | + } | |
1146 | + info_file = stdout; | |
1147 | + } | |
1148 | + } | |
1149 | + | |
1150 | + if (bytes == 0) | |
1151 | + bytes = root->sectorsize; | |
1152 | + | |
1153 | + bytes = (bytes + root->sectorsize - 1) / root->sectorsize; | |
1154 | + bytes *= root->sectorsize; | |
1155 | + | |
1156 | + while (bytes > 0) { | |
1157 | + eb = debug_read_block(root, logical, root->sectorsize, copy); | |
1158 | + if (eb && output_file) { | |
1159 | + err = write(out_fd, eb->data, eb->len); | |
1160 | + if (err < 0 || err != eb->len) { | |
1161 | + fprintf(stderr, "output file write failed\n"); | |
1162 | + goto out_close_fd; | |
1163 | + } | |
1164 | + } | |
1165 | + free_extent_buffer(eb); | |
1166 | + logical += root->sectorsize; | |
1167 | + bytes -= root->sectorsize; | |
1168 | + } | |
1169 | + | |
1170 | +out_close_fd: | |
1171 | + if (output_file && out_fd != 1) | |
1172 | + close(out_fd); | |
1173 | +close: | |
1174 | + close_ctree(root); | |
1175 | + return ret; | |
1176 | +} | |
1177 | diff --git a/btrfs-vol.c b/btrfs-vol.c | |
1178 | index 8069778..4ed799d 100644 | |
1179 | --- a/btrfs-vol.c | |
1180 | +++ b/btrfs-vol.c | |
1181 | @@ -108,10 +108,24 @@ int main(int ac, char **av) | |
1182 | if (device && strcmp(device, "missing") == 0 && | |
1183 | cmd == BTRFS_IOC_RM_DEV) { | |
1184 | fprintf(stderr, "removing missing devices from %s\n", mnt); | |
1185 | - } else if (device) { | |
1186 | + } else if (cmd != BTRFS_IOC_BALANCE) { | |
1187 | + if (cmd == BTRFS_IOC_ADD_DEV) { | |
1188 | + ret = check_mounted(device); | |
1189 | + if (ret < 0) { | |
1190 | + fprintf(stderr, | |
1191 | + "error checking %s mount status\n", | |
1192 | + device); | |
1193 | + exit(1); | |
1194 | + } | |
1195 | + if (ret == 1) { | |
1196 | + fprintf(stderr, "%s is mounted\n", device); | |
1197 | + exit(1); | |
1198 | + } | |
1199 | + } | |
1200 | devfd = open(device, O_RDWR); | |
1201 | - if (!devfd) { | |
1202 | + if (devfd < 0) { | |
1203 | fprintf(stderr, "Unable to open device %s\n", device); | |
1204 | + exit(1); | |
1205 | } | |
1206 | ret = fstat(devfd, &st); | |
1207 | if (ret) { | |
1208 | diff --git a/btrfs-zero-log.c b/btrfs-zero-log.c | |
1209 | new file mode 100644 | |
1210 | index 0000000..f10438b | |
1211 | --- /dev/null | |
1212 | +++ b/btrfs-zero-log.c | |
1213 | @@ -0,0 +1,69 @@ | |
1214 | +/* | |
1215 | + * Copyright (C) 2007 Oracle. All rights reserved. | |
1216 | + * | |
1217 | + * This program is free software; you can redistribute it and/or | |
1218 | + * modify it under the terms of the GNU General Public | |
1219 | + * License v2 as published by the Free Software Foundation. | |
1220 | + * | |
1221 | + * This program is distributed in the hope that it will be useful, | |
1222 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
1223 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
1224 | + * General Public License for more details. | |
1225 | + * | |
1226 | + * You should have received a copy of the GNU General Public | |
1227 | + * License along with this program; if not, write to the | |
1228 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
1229 | + * Boston, MA 021110-1307, USA. | |
1230 | + */ | |
1231 | + | |
1232 | +#define _XOPEN_SOURCE 500 | |
1233 | +#define _GNU_SOURCE 1 | |
1234 | +#include <stdio.h> | |
1235 | +#include <stdlib.h> | |
1236 | +#include <unistd.h> | |
1237 | +#include <fcntl.h> | |
1238 | +#include <sys/stat.h> | |
1239 | +#include "kerncompat.h" | |
1240 | +#include "ctree.h" | |
1241 | +#include "disk-io.h" | |
1242 | +#include "print-tree.h" | |
1243 | +#include "transaction.h" | |
1244 | +#include "list.h" | |
1245 | +#include "version.h" | |
1246 | +#include "utils.h" | |
1247 | + | |
1248 | +static void print_usage(void) | |
1249 | +{ | |
1250 | + fprintf(stderr, "usage: btrfs-zero-log dev\n"); | |
1251 | + fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); | |
1252 | + exit(1); | |
1253 | +} | |
1254 | + | |
1255 | +int main(int ac, char **av) | |
1256 | +{ | |
1257 | + struct btrfs_root *root; | |
1258 | + int ret; | |
1259 | + | |
1260 | + if (ac != 2) | |
1261 | + print_usage(); | |
1262 | + | |
1263 | + radix_tree_init(); | |
1264 | + | |
1265 | + if((ret = check_mounted(av[1])) < 0) { | |
1266 | + fprintf(stderr, "Could not check mount status: %s\n", strerror(ret)); | |
1267 | + return ret; | |
1268 | + } else if(ret) { | |
1269 | + fprintf(stderr, "%s is currently mounted. Aborting.\n", av[1]); | |
1270 | + return -EBUSY; | |
1271 | + } | |
1272 | + | |
1273 | + root = open_ctree(av[1], 0, 1); | |
1274 | + | |
1275 | + if (root == NULL) | |
1276 | + return 1; | |
1277 | + | |
1278 | + btrfs_set_super_log_root(&root->fs_info->super_copy, 0); | |
1279 | + btrfs_set_super_log_root_level(&root->fs_info->super_copy, 0); | |
1280 | + close_ctree(root); | |
1281 | + return ret; | |
1282 | +} | |
1283 | diff --git a/btrfs.c b/btrfs.c | |
1284 | new file mode 100644 | |
1285 | index 0000000..46314cf | |
1286 | --- /dev/null | |
1287 | +++ b/btrfs.c | |
1288 | @@ -0,0 +1,387 @@ | |
1289 | +/* | |
1290 | + * This program is free software; you can redistribute it and/or | |
1291 | + * modify it under the terms of the GNU General Public | |
1292 | + * License v2 as published by the Free Software Foundation. | |
1293 | + * | |
1294 | + * This program is distributed in the hope that it will be useful, | |
1295 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
1296 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
1297 | + * General Public License for more details. | |
1298 | + * | |
1299 | + * You should have received a copy of the GNU General Public | |
1300 | + * License along with this program; if not, write to the | |
1301 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
1302 | + * Boston, MA 021110-1307, USA. | |
1303 | + */ | |
1304 | + | |
1305 | +#define _GNU_SOURCE | |
1306 | +#include <stdio.h> | |
1307 | +#include <stdlib.h> | |
1308 | +#include <string.h> | |
1309 | + | |
1310 | +#include "kerncompat.h" | |
1311 | +#include "btrfs_cmds.h" | |
1312 | +#include "version.h" | |
1313 | + | |
1314 | +typedef int (*CommandFunction)(int argc, char **argv); | |
1315 | + | |
1316 | +struct Command { | |
1317 | + CommandFunction func; /* function which implements the command */ | |
1318 | + int nargs; /* if == 999, any number of arguments | |
1319 | + if >= 0, number of arguments, | |
1320 | + if < 0, _minimum_ number of arguments */ | |
1321 | + char *verb; /* verb */ | |
1322 | + char *help; /* help lines; form the 2nd onward they are | |
1323 | + indented */ | |
1324 | + | |
1325 | + /* the following fields are run-time filled by the program */ | |
1326 | + char **cmds; /* array of subcommands */ | |
1327 | + int ncmds; /* number of subcommand */ | |
1328 | +}; | |
1329 | + | |
1330 | +static struct Command commands[] = { | |
1331 | + | |
1332 | + /* | |
1333 | + avoid short commands different for the case only | |
1334 | + */ | |
1335 | + { do_clone, 2, | |
1336 | + "subvolume snapshot", "<source> [<dest>/]<name>\n" | |
1337 | + "Create a writable snapshot of the subvolume <source> with\n" | |
1338 | + "the name <name> in the <dest> directory." | |
1339 | + }, | |
1340 | + { do_delete_subvolume, 1, | |
1341 | + "subvolume delete", "<subvolume>\n" | |
1342 | + "Delete the subvolume <subvolume>." | |
1343 | + }, | |
1344 | + { do_create_subvol, 1, | |
1345 | + "subvolume create", "[<dest>/]<name>\n" | |
1346 | + "Create a subvolume in <dest> (or the current directory if\n" | |
1347 | + "not passed)." | |
1348 | + }, | |
1349 | + { do_subvol_list, 1, "subvolume list", "<path>\n" | |
1350 | + "List the snapshot/subvolume of a filesystem." | |
1351 | + }, | |
1352 | + { do_find_newer, 2, "subvolume find-new", "<path> <last_gen>\n" | |
1353 | + "List the recently modified files in a filesystem." | |
1354 | + }, | |
1355 | + { do_defrag, -1, | |
1356 | + "filesystem defragment", "[-vcf] [-s start] [-l len] [-t size] <file>|<dir> [<file>|<dir>...]\n" | |
1357 | + "Defragment a file or a directory." | |
1358 | + }, | |
1359 | + { do_set_default_subvol, 2, | |
1360 | + "subvolume set-default", "<id> <path>\n" | |
1361 | + "Set the subvolume of the filesystem <path> which will be mounted\n" | |
1362 | + "as default." | |
1363 | + }, | |
1364 | + { do_fssync, 1, | |
1365 | + "filesystem sync", "<path>\n" | |
1366 | + "Force a sync on the filesystem <path>." | |
1367 | + }, | |
1368 | + { do_resize, 2, | |
1369 | + "filesystem resize", "[+/-]<newsize>[gkm]|max <filesystem>\n" | |
1370 | + "Resize the file system. If 'max' is passed, the filesystem\n" | |
1371 | + "will occupe all available space on the device." | |
1372 | + }, | |
1373 | + { do_show_filesystem, 999, | |
1374 | + "filesystem show", "[<uuid>|<label>]\n" | |
1375 | + "Show the info of a btrfs filesystem. If no <uuid> or <label>\n" | |
1376 | + "is passed, info of all the btrfs filesystem are shown." | |
1377 | + }, | |
1378 | + { do_df_filesystem, 1, | |
1379 | + "filesystem df", "<path>\n" | |
1380 | + "Show space usage information for a mount point\n." | |
1381 | + }, | |
1382 | + { do_balance, 1, | |
1383 | + "filesystem balance", "<path>\n" | |
1384 | + "Balance the chunks across the device." | |
1385 | + }, | |
1386 | + { do_scan, | |
1387 | + 999, "device scan", "[<device> [<device>..]\n" | |
1388 | + "Scan all device for or the passed device for a btrfs\n" | |
1389 | + "filesystem." | |
1390 | + }, | |
1391 | + { do_add_volume, -2, | |
1392 | + "device add", "<dev> [<dev>..] <path>\n" | |
1393 | + "Add a device to a filesystem." | |
1394 | + }, | |
1395 | + { do_remove_volume, -2, | |
1396 | + "device delete", "<dev> [<dev>..] <path>\n" | |
1397 | + "Remove a device from a filesystem." | |
1398 | + }, | |
1399 | + /* coming soon | |
1400 | + { 2, "filesystem label", "<label> <path>\n" | |
1401 | + "Set the label of a filesystem" | |
1402 | + } | |
1403 | + */ | |
1404 | + { 0, 0 , 0 } | |
1405 | +}; | |
1406 | + | |
1407 | +static char *get_prgname(char *programname) | |
1408 | +{ | |
1409 | + char *np; | |
1410 | + np = strrchr(programname,'/'); | |
1411 | + if(!np) | |
1412 | + np = programname; | |
1413 | + else | |
1414 | + np++; | |
1415 | + | |
1416 | + return np; | |
1417 | +} | |
1418 | + | |
1419 | +static void print_help(char *programname, struct Command *cmd) | |
1420 | +{ | |
1421 | + char *pc; | |
1422 | + | |
1423 | + printf("\t%s %s ", programname, cmd->verb ); | |
1424 | + | |
1425 | + for(pc = cmd->help; *pc; pc++){ | |
1426 | + putchar(*pc); | |
1427 | + if(*pc == '\n') | |
1428 | + printf("\t\t"); | |
1429 | + } | |
1430 | + putchar('\n'); | |
1431 | +} | |
1432 | + | |
1433 | +static void help(char *np) | |
1434 | +{ | |
1435 | + struct Command *cp; | |
1436 | + | |
1437 | + printf("Usage:\n"); | |
1438 | + for( cp = commands; cp->verb; cp++ ) | |
1439 | + print_help(np, cp); | |
1440 | + | |
1441 | + printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np); | |
1442 | + printf("\n%s\n", BTRFS_BUILD_VERSION); | |
1443 | +} | |
1444 | + | |
1445 | +static int split_command(char *cmd, char ***commands) | |
1446 | +{ | |
1447 | + int c, l; | |
1448 | + char *p, *s; | |
1449 | + | |
1450 | + for( *commands = 0, l = c = 0, p = s = cmd ; ; p++, l++ ){ | |
1451 | + if ( *p && *p != ' ' ) | |
1452 | + continue; | |
1453 | + | |
1454 | + /* c + 2 so that we have room for the null */ | |
1455 | + (*commands) = realloc( (*commands), sizeof(char *)*(c + 2)); | |
1456 | + (*commands)[c] = strndup(s, l); | |
1457 | + c++; | |
1458 | + l = 0; | |
1459 | + s = p+1; | |
1460 | + if( !*p ) break; | |
1461 | + } | |
1462 | + | |
1463 | + (*commands)[c] = 0; | |
1464 | + return c; | |
1465 | +} | |
1466 | + | |
1467 | +/* | |
1468 | + This function checks if the passed command is ambiguous | |
1469 | +*/ | |
1470 | +static int check_ambiguity(struct Command *cmd, char **argv){ | |
1471 | + int i; | |
1472 | + struct Command *cp; | |
1473 | + /* check for ambiguity */ | |
1474 | + for( i = 0 ; i < cmd->ncmds ; i++ ){ | |
1475 | + int match; | |
1476 | + for( match = 0, cp = commands; cp->verb; cp++ ){ | |
1477 | + int j, skip; | |
1478 | + char *s1, *s2; | |
1479 | + | |
1480 | + if( cp->ncmds < i ) | |
1481 | + continue; | |
1482 | + | |
1483 | + for( skip = 0, j = 0 ; j < i ; j++ ) | |
1484 | + if( strcmp(cmd->cmds[j], cp->cmds[j])){ | |
1485 | + skip=1; | |
1486 | + break; | |
1487 | + } | |
1488 | + if(skip) | |
1489 | + continue; | |
1490 | + | |
1491 | + if( !strcmp(cmd->cmds[i], cp->cmds[i])) | |
1492 | + continue; | |
1493 | + for(s2 = cp->cmds[i], s1 = argv[i+1]; | |
1494 | + *s1 == *s2 && *s1; s1++, s2++ ) ; | |
1495 | + if( !*s1 ) | |
1496 | + match++; | |
1497 | + } | |
1498 | + if(match){ | |
1499 | + int j; | |
1500 | + fprintf(stderr, "ERROR: in command '"); | |
1501 | + for( j = 0 ; j <= i ; j++ ) | |
1502 | + fprintf(stderr, "%s%s",j?" ":"", argv[j+1]); | |
1503 | + fprintf(stderr, "', '%s' is ambiguous\n",argv[j]); | |
1504 | + return -2; | |
1505 | + } | |
1506 | + } | |
1507 | + return 0; | |
1508 | +} | |
1509 | + | |
1510 | +/* | |
1511 | + * This function, compacts the program name and the command in the first | |
1512 | + * element of the '*av' array | |
1513 | + */ | |
1514 | +static int prepare_args(int *ac, char ***av, char *prgname, struct Command *cmd ){ | |
1515 | + | |
1516 | + char **ret; | |
1517 | + int i; | |
1518 | + char *newname; | |
1519 | + | |
1520 | + ret = (char **)malloc(sizeof(char*)*(*ac+1)); | |
1521 | + newname = (char*)malloc(strlen(prgname)+strlen(cmd->verb)+2); | |
1522 | + if( !ret || !newname ){ | |
1523 | + free(ret); | |
1524 | + free(newname); | |
1525 | + return -1; | |
1526 | + } | |
1527 | + | |
1528 | + ret[0] = newname; | |
1529 | + for(i=0; i < *ac ; i++ ) | |
1530 | + ret[i+1] = (*av)[i]; | |
1531 | + | |
1532 | + strcpy(newname, prgname); | |
1533 | + strcat(newname, " "); | |
1534 | + strcat(newname, cmd->verb); | |
1535 | + | |
1536 | + (*ac)++; | |
1537 | + *av = ret; | |
1538 | + | |
1539 | + return 0; | |
1540 | + | |
1541 | +} | |
1542 | + | |
1543 | + | |
1544 | + | |
1545 | +/* | |
1546 | + | |
1547 | + This function perform the following jobs: | |
1548 | + - show the help if '--help' or 'help' or '-h' are passed | |
1549 | + - verify that a command is not ambiguous, otherwise show which | |
1550 | + part of the command is ambiguous | |
1551 | + - if after a (even partial) command there is '--help' show the help | |
1552 | + for all the matching commands | |
1553 | + - if the command doesn't' match show an error | |
1554 | + - finally, if a command match, they return which command is matched and | |
1555 | + the arguments | |
1556 | + | |
1557 | + The function return 0 in case of help is requested; <0 in case | |
1558 | + of uncorrect command; >0 in case of matching commands | |
1559 | + argc, argv are the arg-counter and arg-vector (input) | |
1560 | + *nargs_ is the number of the arguments after the command (output) | |
1561 | + **cmd_ is the invoked command (output) | |
1562 | + ***args_ are the arguments after the command | |
1563 | + | |
1564 | +*/ | |
1565 | +static int parse_args(int argc, char **argv, | |
1566 | + CommandFunction *func_, | |
1567 | + int *nargs_, char **cmd_, char ***args_ ) | |
1568 | +{ | |
1569 | + struct Command *cp; | |
1570 | + struct Command *matchcmd=0; | |
1571 | + char *prgname = get_prgname(argv[0]); | |
1572 | + int i=0, helprequested=0; | |
1573 | + | |
1574 | + if( argc < 2 || !strcmp(argv[1], "help") || | |
1575 | + !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ | |
1576 | + help(prgname); | |
1577 | + return 0; | |
1578 | + } | |
1579 | + | |
1580 | + for( cp = commands; cp->verb; cp++ ) | |
1581 | + if( !cp->ncmds) | |
1582 | + cp->ncmds = split_command(cp->verb, &(cp->cmds)); | |
1583 | + | |
1584 | + for( cp = commands; cp->verb; cp++ ){ | |
1585 | + int match; | |
1586 | + | |
1587 | + if( argc-1 < cp->ncmds ) | |
1588 | + continue; | |
1589 | + for( match = 1, i = 0 ; i < cp->ncmds ; i++ ){ | |
1590 | + char *s1, *s2; | |
1591 | + s1 = cp->cmds[i]; | |
1592 | + s2 = argv[i+1]; | |
1593 | + | |
1594 | + for(s2 = cp->cmds[i], s1 = argv[i+1]; | |
1595 | + *s1 == *s2 && *s1; | |
1596 | + s1++, s2++ ) ; | |
1597 | + if( *s1 ){ | |
1598 | + match=0; | |
1599 | + break; | |
1600 | + } | |
1601 | + } | |
1602 | + | |
1603 | + /* If you understand why this code works ... | |
1604 | + you are a genious !! */ | |
1605 | + if(argc>i+1 && !strcmp(argv[i+1],"--help")){ | |
1606 | + if(!helprequested) | |
1607 | + printf("Usage:\n"); | |
1608 | + print_help(prgname, cp); | |
1609 | + helprequested=1; | |
1610 | + continue; | |
1611 | + } | |
1612 | + | |
1613 | + if(!match) | |
1614 | + continue; | |
1615 | + | |
1616 | + matchcmd = cp; | |
1617 | + *nargs_ = argc-matchcmd->ncmds-1; | |
1618 | + *cmd_ = matchcmd->verb; | |
1619 | + *args_ = argv+matchcmd->ncmds+1; | |
1620 | + *func_ = cp->func; | |
1621 | + | |
1622 | + break; | |
1623 | + } | |
1624 | + | |
1625 | + if(helprequested){ | |
1626 | + printf("\n%s\n", BTRFS_BUILD_VERSION); | |
1627 | + return 0; | |
1628 | + } | |
1629 | + | |
1630 | + if(!matchcmd){ | |
1631 | + fprintf( stderr, "ERROR: unknown command '%s'\n",argv[1]); | |
1632 | + help(prgname); | |
1633 | + return -1; | |
1634 | + } | |
1635 | + | |
1636 | + if(check_ambiguity(matchcmd, argv)) | |
1637 | + return -2; | |
1638 | + | |
1639 | + /* check the number of argument */ | |
1640 | + if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_ ){ | |
1641 | + fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n", | |
1642 | + matchcmd->verb, -matchcmd->nargs); | |
1643 | + return -2; | |
1644 | + } | |
1645 | + if(matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999){ | |
1646 | + fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n", | |
1647 | + matchcmd->verb, matchcmd->nargs); | |
1648 | + return -2; | |
1649 | + } | |
1650 | + | |
1651 | + if (prepare_args( nargs_, args_, prgname, matchcmd )){ | |
1652 | + fprintf(stderr, "ERROR: not enough memory\\n"); | |
1653 | + return -20; | |
1654 | + } | |
1655 | + | |
1656 | + | |
1657 | + return 1; | |
1658 | +} | |
1659 | +int main(int ac, char **av ) | |
1660 | +{ | |
1661 | + | |
1662 | + char *cmd=0, **args=0; | |
1663 | + int nargs=0, r; | |
1664 | + CommandFunction func=0; | |
1665 | + | |
1666 | + r = parse_args(ac, av, &func, &nargs, &cmd, &args); | |
1667 | + if( r <= 0 ){ | |
1668 | + /* error or no command to parse*/ | |
1669 | + exit(-r); | |
1670 | + } | |
1671 | + | |
1672 | + exit(func(nargs, args)); | |
1673 | + | |
1674 | +} | |
1675 | + | |
1676 | diff --git a/btrfs_cmds.c b/btrfs_cmds.c | |
1677 | new file mode 100644 | |
1678 | index 0000000..8031c58 | |
1679 | --- /dev/null | |
1680 | +++ b/btrfs_cmds.c | |
1681 | @@ -0,0 +1,924 @@ | |
1682 | +/* | |
1683 | + * This program is free software; you can redistribute it and/or | |
1684 | + * modify it under the terms of the GNU General Public | |
1685 | + * License v2 as published by the Free Software Foundation. | |
1686 | + * | |
1687 | + * This program is distributed in the hope that it will be useful, | |
1688 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
1689 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
1690 | + * General Public License for more details. | |
1691 | + * | |
1692 | + * You should have received a copy of the GNU General Public | |
1693 | + * License along with this program; if not, write to the | |
1694 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
1695 | + * Boston, MA 021110-1307, USA. | |
1696 | + */ | |
1697 | + | |
1698 | + | |
1699 | +#include <stdio.h> | |
1700 | +#include <stdlib.h> | |
1701 | +#include <string.h> | |
1702 | +#include <sys/ioctl.h> | |
1703 | +#include <sys/types.h> | |
1704 | +#include <dirent.h> | |
1705 | +#include <sys/stat.h> | |
1706 | +#include <unistd.h> | |
1707 | +#include <fcntl.h> | |
1708 | +#include <libgen.h> | |
1709 | +#include <limits.h> | |
1710 | +#include <uuid/uuid.h> | |
1711 | +#include <ctype.h> | |
1712 | + | |
1713 | +#undef ULONG_MAX | |
1714 | + | |
1715 | +#include "kerncompat.h" | |
1716 | +#include "ctree.h" | |
1717 | +#include "transaction.h" | |
1718 | +#include "utils.h" | |
1719 | +#include "version.h" | |
1720 | +#include "ioctl.h" | |
1721 | +#include "volumes.h" | |
1722 | + | |
1723 | +#include "btrfs_cmds.h" | |
1724 | + | |
1725 | +#ifdef __CHECKER__ | |
1726 | +#define BLKGETSIZE64 0 | |
1727 | +#define BTRFS_IOC_SNAP_CREATE 0 | |
1728 | +#define BTRFS_VOL_NAME_MAX 255 | |
1729 | +struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; }; | |
1730 | +static inline int ioctl(int fd, int define, void *arg) { return 0; } | |
1731 | +#endif | |
1732 | + | |
1733 | +/* | |
1734 | + * test if path is a subvolume: | |
1735 | + * this function return | |
1736 | + * 0-> path exists but it is not a subvolume | |
1737 | + * 1-> path exists and it is a subvolume | |
1738 | + * -1 -> path is unaccessible | |
1739 | + */ | |
1740 | +static int test_issubvolume(char *path) | |
1741 | +{ | |
1742 | + | |
1743 | + struct stat st; | |
1744 | + int res; | |
1745 | + | |
1746 | + res = stat(path, &st); | |
1747 | + if(res < 0 ) | |
1748 | + return -1; | |
1749 | + | |
1750 | + return (st.st_ino == 256) && S_ISDIR(st.st_mode); | |
1751 | + | |
1752 | +} | |
1753 | + | |
1754 | +/* | |
1755 | + * test if path is a directory | |
1756 | + * this function return | |
1757 | + * 0-> path exists but it is not a directory | |
1758 | + * 1-> path exists and it is a directory | |
1759 | + * -1 -> path is unaccessible | |
1760 | + */ | |
1761 | +static int test_isdir(char *path) | |
1762 | +{ | |
1763 | + struct stat st; | |
1764 | + int res; | |
1765 | + | |
1766 | + res = stat(path, &st); | |
1767 | + if(res < 0 ) | |
1768 | + return -1; | |
1769 | + | |
1770 | + return S_ISDIR(st.st_mode); | |
1771 | + | |
1772 | +} | |
1773 | + | |
1774 | +static int open_file_or_dir(const char *fname) | |
1775 | +{ | |
1776 | + int ret; | |
1777 | + struct stat st; | |
1778 | + DIR *dirstream; | |
1779 | + int fd; | |
1780 | + | |
1781 | + ret = stat(fname, &st); | |
1782 | + if (ret < 0) { | |
1783 | + return -1; | |
1784 | + } | |
1785 | + if (S_ISDIR(st.st_mode)) { | |
1786 | + dirstream = opendir(fname); | |
1787 | + if (!dirstream) { | |
1788 | + return -2; | |
1789 | + } | |
1790 | + fd = dirfd(dirstream); | |
1791 | + } else { | |
1792 | + fd = open(fname, O_RDWR); | |
1793 | + } | |
1794 | + if (fd < 0) { | |
1795 | + return -3; | |
1796 | + } | |
1797 | + return fd; | |
1798 | +} | |
1799 | + | |
1800 | +static u64 parse_size(char *s) | |
1801 | +{ | |
1802 | + int len = strlen(s); | |
1803 | + char c; | |
1804 | + u64 mult = 1; | |
1805 | + | |
1806 | + if (!isdigit(s[len - 1])) { | |
1807 | + c = tolower(s[len - 1]); | |
1808 | + switch (c) { | |
1809 | + case 'g': | |
1810 | + mult *= 1024; | |
1811 | + case 'm': | |
1812 | + mult *= 1024; | |
1813 | + case 'k': | |
1814 | + mult *= 1024; | |
1815 | + case 'b': | |
1816 | + break; | |
1817 | + default: | |
1818 | + fprintf(stderr, "Unknown size descriptor %c\n", c); | |
1819 | + exit(1); | |
1820 | + } | |
1821 | + s[len - 1] = '\0'; | |
1822 | + } | |
1823 | + return atoll(s) * mult; | |
1824 | +} | |
1825 | + | |
1826 | +int do_defrag(int ac, char **av) | |
1827 | +{ | |
1828 | + int fd; | |
1829 | + int compress = 0; | |
1830 | + int flush = 0; | |
1831 | + u64 start = 0; | |
1832 | + u64 len = (u64)-1; | |
1833 | + u32 thresh = 0; | |
1834 | + int i; | |
1835 | + int errors = 0; | |
1836 | + int ret = 0; | |
1837 | + int verbose = 0; | |
1838 | + int fancy_ioctl = 0; | |
1839 | + struct btrfs_ioctl_defrag_range_args range; | |
1840 | + | |
1841 | + optind = 1; | |
1842 | + while(1) { | |
1843 | + int c = getopt(ac, av, "vcfs:l:t:"); | |
1844 | + if (c < 0) | |
1845 | + break; | |
1846 | + switch(c) { | |
1847 | + case 'c': | |
1848 | + compress = 1; | |
1849 | + fancy_ioctl = 1; | |
1850 | + break; | |
1851 | + case 'f': | |
1852 | + flush = 1; | |
1853 | + fancy_ioctl = 1; | |
1854 | + break; | |
1855 | + case 'v': | |
1856 | + verbose = 1; | |
1857 | + break; | |
1858 | + case 's': | |
1859 | + start = parse_size(optarg); | |
1860 | + fancy_ioctl = 1; | |
1861 | + break; | |
1862 | + case 'l': | |
1863 | + len = parse_size(optarg); | |
1864 | + fancy_ioctl = 1; | |
1865 | + break; | |
1866 | + case 't': | |
1867 | + thresh = parse_size(optarg); | |
1868 | + fancy_ioctl = 1; | |
1869 | + break; | |
1870 | + default: | |
1871 | + fprintf(stderr, "Invalid arguments for defragment\n"); | |
1872 | + free(av); | |
1873 | + return 1; | |
1874 | + } | |
1875 | + } | |
1876 | + if (ac - optind == 0) { | |
1877 | + fprintf(stderr, "Invalid arguments for defragment\n"); | |
1878 | + free(av); | |
1879 | + return 1; | |
1880 | + } | |
1881 | + | |
1882 | + memset(&range, 0, sizeof(range)); | |
1883 | + range.start = start; | |
1884 | + range.len = len; | |
1885 | + range.extent_thresh = thresh; | |
1886 | + if (compress) | |
1887 | + range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS; | |
1888 | + if (flush) | |
1889 | + range.flags |= BTRFS_DEFRAG_RANGE_START_IO; | |
1890 | + | |
1891 | + for (i = optind; i < ac; i++) { | |
1892 | + if (verbose) | |
1893 | + printf("%s\n", av[i]); | |
1894 | + fd = open_file_or_dir(av[i]); | |
1895 | + if (fd < 0) { | |
1896 | + fprintf(stderr, "failed to open %s\n", av[i]); | |
1897 | + perror("open:"); | |
1898 | + errors++; | |
1899 | + continue; | |
1900 | + } | |
1901 | + if (!fancy_ioctl) { | |
1902 | + ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL); | |
1903 | + } else { | |
1904 | + ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range); | |
1905 | + if (ret && errno == ENOTTY) { | |
1906 | + fprintf(stderr, "defrag range ioctl not " | |
1907 | + "supported in this kernel, please try " | |
1908 | + "without any options.\n"); | |
1909 | + errors++; | |
1910 | + break; | |
1911 | + } | |
1912 | + } | |
1913 | + if (ret) { | |
1914 | + fprintf(stderr, "ioctl failed on %s ret %d errno %d\n", | |
1915 | + av[i], ret, errno); | |
1916 | + errors++; | |
1917 | + } | |
1918 | + close(fd); | |
1919 | + } | |
1920 | + if (verbose) | |
1921 | + printf("%s\n", BTRFS_BUILD_VERSION); | |
1922 | + if (errors) { | |
1923 | + fprintf(stderr, "total %d failures\n", errors); | |
1924 | + exit(1); | |
1925 | + } | |
1926 | + | |
1927 | + free(av); | |
1928 | + return errors + 20; | |
1929 | +} | |
1930 | + | |
1931 | +int do_find_newer(int argc, char **argv) | |
1932 | +{ | |
1933 | + int fd; | |
1934 | + int ret; | |
1935 | + char *subvol; | |
1936 | + u64 last_gen; | |
1937 | + | |
1938 | + subvol = argv[1]; | |
1939 | + last_gen = atoll(argv[2]); | |
1940 | + | |
1941 | + ret = test_issubvolume(subvol); | |
1942 | + if (ret < 0) { | |
1943 | + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); | |
1944 | + return 12; | |
1945 | + } | |
1946 | + if (!ret) { | |
1947 | + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); | |
1948 | + return 13; | |
1949 | + } | |
1950 | + | |
1951 | + fd = open_file_or_dir(subvol); | |
1952 | + if (fd < 0) { | |
1953 | + fprintf(stderr, "ERROR: can't access '%s'\n", subvol); | |
1954 | + return 12; | |
1955 | + } | |
1956 | + ret = find_updated_files(fd, 0, last_gen); | |
1957 | + if (ret) | |
1958 | + return 19; | |
1959 | + return 0; | |
1960 | +} | |
1961 | + | |
1962 | +int do_subvol_list(int argc, char **argv) | |
1963 | +{ | |
1964 | + int fd; | |
1965 | + int ret; | |
1966 | + char *subvol; | |
1967 | + | |
1968 | + subvol = argv[1]; | |
1969 | + | |
1970 | + ret = test_issubvolume(subvol); | |
1971 | + if (ret < 0) { | |
1972 | + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); | |
1973 | + return 12; | |
1974 | + } | |
1975 | + if (!ret) { | |
1976 | + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); | |
1977 | + return 13; | |
1978 | + } | |
1979 | + | |
1980 | + fd = open_file_or_dir(subvol); | |
1981 | + if (fd < 0) { | |
1982 | + fprintf(stderr, "ERROR: can't access '%s'\n", subvol); | |
1983 | + return 12; | |
1984 | + } | |
1985 | + ret = list_subvols(fd); | |
1986 | + if (ret) | |
1987 | + return 19; | |
1988 | + return 0; | |
1989 | +} | |
1990 | + | |
1991 | +int do_clone(int argc, char **argv) | |
1992 | +{ | |
1993 | + char *subvol, *dst; | |
1994 | + int res, fd, fddst, len; | |
1995 | + char *newname; | |
1996 | + char *dstdir; | |
1997 | + | |
1998 | + subvol = argv[1]; | |
1999 | + dst = argv[2]; | |
2000 | + struct btrfs_ioctl_vol_args args; | |
2001 | + | |
2002 | + res = test_issubvolume(subvol); | |
2003 | + if(res<0){ | |
2004 | + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); | |
2005 | + return 12; | |
2006 | + } | |
2007 | + if(!res){ | |
2008 | + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); | |
2009 | + return 13; | |
2010 | + } | |
2011 | + | |
2012 | + res = test_isdir(dst); | |
2013 | + if(res == 0 ){ | |
2014 | + fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst); | |
2015 | + return 12; | |
2016 | + } | |
2017 | + | |
2018 | + if(res>0){ | |
2019 | + newname = strdup(subvol); | |
2020 | + newname = basename(newname); | |
2021 | + dstdir = dst; | |
2022 | + }else{ | |
2023 | + newname = strdup(dst); | |
2024 | + newname = basename(newname); | |
2025 | + dstdir = strdup(dst); | |
2026 | + dstdir = dirname(dstdir); | |
2027 | + } | |
2028 | + | |
2029 | + if( !strcmp(newname,".") || !strcmp(newname,"..") || | |
2030 | + strchr(newname, '/') ){ | |
2031 | + fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n", | |
2032 | + newname); | |
2033 | + return 14; | |
2034 | + } | |
2035 | + | |
2036 | + len = strlen(newname); | |
2037 | + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { | |
2038 | + fprintf(stderr, "ERROR: snapshot name too long ('%s)\n", | |
2039 | + newname); | |
2040 | + return 14; | |
2041 | + } | |
2042 | + | |
2043 | + fddst = open_file_or_dir(dstdir); | |
2044 | + if (fddst < 0) { | |
2045 | + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); | |
2046 | + return 12; | |
2047 | + } | |
2048 | + | |
2049 | + fd = open_file_or_dir(subvol); | |
2050 | + if (fd < 0) { | |
2051 | + close(fddst); | |
2052 | + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); | |
2053 | + return 12; | |
2054 | + } | |
2055 | + | |
2056 | + printf("Create a snapshot of '%s' in '%s/%s'\n", | |
2057 | + subvol, dstdir, newname); | |
2058 | + args.fd = fd; | |
2059 | + strcpy(args.name, newname); | |
2060 | + res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args); | |
2061 | + | |
2062 | + close(fd); | |
2063 | + close(fddst); | |
2064 | + | |
2065 | + if(res < 0 ){ | |
2066 | + fprintf( stderr, "ERROR: cannot snapshot '%s'\n",subvol); | |
2067 | + return 11; | |
2068 | + } | |
2069 | + | |
2070 | + return 0; | |
2071 | + | |
2072 | +} | |
2073 | + | |
2074 | +int do_delete_subvolume(int argc, char **argv) | |
2075 | +{ | |
2076 | + int res, fd, len; | |
2077 | + struct btrfs_ioctl_vol_args args; | |
2078 | + char *dname, *vname, *cpath; | |
2079 | + char *path = argv[1]; | |
2080 | + | |
2081 | + res = test_issubvolume(path); | |
2082 | + if(res<0){ | |
2083 | + fprintf(stderr, "ERROR: error accessing '%s'\n", path); | |
2084 | + return 12; | |
2085 | + } | |
2086 | + if(!res){ | |
2087 | + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path); | |
2088 | + return 13; | |
2089 | + } | |
2090 | + | |
2091 | + cpath = realpath(path, 0); | |
2092 | + dname = strdup(cpath); | |
2093 | + dname = dirname(dname); | |
2094 | + vname = strdup(cpath); | |
2095 | + vname = basename(vname); | |
2096 | + free(cpath); | |
2097 | + | |
2098 | + if( !strcmp(vname,".") || !strcmp(vname,"..") || | |
2099 | + strchr(vname, '/') ){ | |
2100 | + fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n", | |
2101 | + vname); | |
2102 | + return 14; | |
2103 | + } | |
2104 | + | |
2105 | + len = strlen(vname); | |
2106 | + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { | |
2107 | + fprintf(stderr, "ERROR: snapshot name too long ('%s)\n", | |
2108 | + vname); | |
2109 | + return 14; | |
2110 | + } | |
2111 | + | |
2112 | + fd = open_file_or_dir(dname); | |
2113 | + if (fd < 0) { | |
2114 | + close(fd); | |
2115 | + fprintf(stderr, "ERROR: can't access to '%s'\n", dname); | |
2116 | + return 12; | |
2117 | + } | |
2118 | + | |
2119 | + printf("Delete subvolume '%s/%s'\n", dname, vname); | |
2120 | + strcpy(args.name, vname); | |
2121 | + res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); | |
2122 | + | |
2123 | + close(fd); | |
2124 | + | |
2125 | + if(res < 0 ){ | |
2126 | + fprintf( stderr, "ERROR: cannot delete '%s/%s'\n",dname, vname); | |
2127 | + return 11; | |
2128 | + } | |
2129 | + | |
2130 | + return 0; | |
2131 | + | |
2132 | +} | |
2133 | + | |
2134 | +int do_create_subvol(int argc, char **argv) | |
2135 | +{ | |
2136 | + int res, fddst, len; | |
2137 | + char *newname; | |
2138 | + char *dstdir; | |
2139 | + struct btrfs_ioctl_vol_args args; | |
2140 | + char *dst = argv[1]; | |
2141 | + | |
2142 | + res = test_isdir(dst); | |
2143 | + if(res >= 0 ){ | |
2144 | + fprintf(stderr, "ERROR: '%s' exists\n", dst); | |
2145 | + return 12; | |
2146 | + } | |
2147 | + | |
2148 | + newname = strdup(dst); | |
2149 | + newname = basename(newname); | |
2150 | + dstdir = strdup(dst); | |
2151 | + dstdir = dirname(dstdir); | |
2152 | + | |
2153 | + if( !strcmp(newname,".") || !strcmp(newname,"..") || | |
2154 | + strchr(newname, '/') ){ | |
2155 | + fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n", | |
2156 | + newname); | |
2157 | + return 14; | |
2158 | + } | |
2159 | + | |
2160 | + len = strlen(newname); | |
2161 | + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { | |
2162 | + fprintf(stderr, "ERROR: subvolume name too long ('%s)\n", | |
2163 | + newname); | |
2164 | + return 14; | |
2165 | + } | |
2166 | + | |
2167 | + fddst = open_file_or_dir(dstdir); | |
2168 | + if (fddst < 0) { | |
2169 | + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); | |
2170 | + return 12; | |
2171 | + } | |
2172 | + | |
2173 | + printf("Create subvolume '%s/%s'\n", dstdir, newname); | |
2174 | + strcpy(args.name, newname); | |
2175 | + res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); | |
2176 | + | |
2177 | + close(fddst); | |
2178 | + | |
2179 | + if(res < 0 ){ | |
2180 | + fprintf( stderr, "ERROR: cannot create subvolume\n"); | |
2181 | + return 11; | |
2182 | + } | |
2183 | + | |
2184 | + return 0; | |
2185 | + | |
2186 | +} | |
2187 | + | |
2188 | +int do_fssync(int argc, char **argv) | |
2189 | +{ | |
2190 | + int fd, res; | |
2191 | + char *path = argv[1]; | |
2192 | + | |
2193 | + fd = open_file_or_dir(path); | |
2194 | + if (fd < 0) { | |
2195 | + fprintf(stderr, "ERROR: can't access to '%s'\n", path); | |
2196 | + return 12; | |
2197 | + } | |
2198 | + | |
2199 | + printf("FSSync '%s'\n", path); | |
2200 | + res = ioctl(fd, BTRFS_IOC_SYNC); | |
2201 | + close(fd); | |
2202 | + if( res < 0 ){ | |
2203 | + fprintf(stderr, "ERROR: unable to fs-syncing '%s'\n", path); | |
2204 | + return 16; | |
2205 | + } | |
2206 | + | |
2207 | + return 0; | |
2208 | +} | |
2209 | + | |
2210 | +int do_scan(int argc, char **argv) | |
2211 | +{ | |
2212 | + int i, fd; | |
2213 | + if(argc<=1){ | |
2214 | + int ret; | |
2215 | + | |
2216 | + printf("Scanning for Btrfs filesystems\n"); | |
2217 | + ret = btrfs_scan_one_dir("/dev", 1); | |
2218 | + if (ret){ | |
2219 | + fprintf(stderr, "ERROR: error %d while scanning\n", ret); | |
2220 | + return 18; | |
2221 | + } | |
2222 | + return 0; | |
2223 | + } | |
2224 | + | |
2225 | + fd = open("/dev/btrfs-control", O_RDWR); | |
2226 | + if (fd < 0) { | |
2227 | + perror("failed to open /dev/btrfs-control"); | |
2228 | + return 10; | |
2229 | + } | |
2230 | + | |
2231 | + for( i = 1 ; i < argc ; i++ ){ | |
2232 | + struct btrfs_ioctl_vol_args args; | |
2233 | + int ret; | |
2234 | + | |
2235 | + printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]); | |
2236 | + | |
2237 | + strcpy(args.name, argv[i]); | |
2238 | + /* | |
2239 | + * FIXME: which are the error code returned by this ioctl ? | |
2240 | + * it seems that is impossible to understand if there no is | |
2241 | + * a btrfs filesystem from an I/O error !!! | |
2242 | + */ | |
2243 | + ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); | |
2244 | + | |
2245 | + if( ret < 0 ){ | |
2246 | + close(fd); | |
2247 | + fprintf(stderr, "ERROR: unable to scan the device '%s'\n", argv[i]); | |
2248 | + return 11; | |
2249 | + } | |
2250 | + } | |
2251 | + | |
2252 | + close(fd); | |
2253 | + return 0; | |
2254 | + | |
2255 | +} | |
2256 | + | |
2257 | +int do_resize(int argc, char **argv) | |
2258 | +{ | |
2259 | + | |
2260 | + struct btrfs_ioctl_vol_args args; | |
2261 | + int fd, res, len; | |
2262 | + char *amount=argv[1], *path=argv[2]; | |
2263 | + | |
2264 | + fd = open_file_or_dir(path); | |
2265 | + if (fd < 0) { | |
2266 | + fprintf(stderr, "ERROR: can't access to '%s'\n", path); | |
2267 | + return 12; | |
2268 | + } | |
2269 | + len = strlen(amount); | |
2270 | + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { | |
2271 | + fprintf(stderr, "ERROR: size value too long ('%s)\n", | |
2272 | + amount); | |
2273 | + return 14; | |
2274 | + } | |
2275 | + | |
2276 | + printf("Resize '%s' of '%s'\n", path, amount); | |
2277 | + strcpy(args.name, amount); | |
2278 | + res = ioctl(fd, BTRFS_IOC_RESIZE, &args); | |
2279 | + close(fd); | |
2280 | + if( res < 0 ){ | |
2281 | + fprintf(stderr, "ERROR: unable to resize '%s'\n", path); | |
2282 | + return 30; | |
2283 | + } | |
2284 | + return 0; | |
2285 | +} | |
2286 | + | |
2287 | +static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) | |
2288 | +{ | |
2289 | + struct list_head *cur; | |
2290 | + struct btrfs_device *device; | |
2291 | + | |
2292 | + list_for_each(cur, &fs_devices->devices) { | |
2293 | + device = list_entry(cur, struct btrfs_device, dev_list); | |
2294 | + if ((device->label && strcmp(device->label, search) == 0) || | |
2295 | + strcmp(device->name, search) == 0) | |
2296 | + return 1; | |
2297 | + } | |
2298 | + return 0; | |
2299 | +} | |
2300 | + | |
2301 | +static void print_one_uuid(struct btrfs_fs_devices *fs_devices) | |
2302 | +{ | |
2303 | + char uuidbuf[37]; | |
2304 | + struct list_head *cur; | |
2305 | + struct btrfs_device *device; | |
2306 | + char *super_bytes_used; | |
2307 | + u64 devs_found = 0; | |
2308 | + u64 total; | |
2309 | + | |
2310 | + uuid_unparse(fs_devices->fsid, uuidbuf); | |
2311 | + device = list_entry(fs_devices->devices.next, struct btrfs_device, | |
2312 | + dev_list); | |
2313 | + if (device->label && device->label[0]) | |
2314 | + printf("Label: '%s' ", device->label); | |
2315 | + else | |
2316 | + printf("Label: none "); | |
2317 | + | |
2318 | + super_bytes_used = pretty_sizes(device->super_bytes_used); | |
2319 | + | |
2320 | + total = device->total_devs; | |
2321 | + printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, | |
2322 | + (unsigned long long)total, super_bytes_used); | |
2323 | + | |
2324 | + free(super_bytes_used); | |
2325 | + | |
2326 | + list_for_each(cur, &fs_devices->devices) { | |
2327 | + char *total_bytes; | |
2328 | + char *bytes_used; | |
2329 | + device = list_entry(cur, struct btrfs_device, dev_list); | |
2330 | + total_bytes = pretty_sizes(device->total_bytes); | |
2331 | + bytes_used = pretty_sizes(device->bytes_used); | |
2332 | + printf("\tdevid %4llu size %s used %s path %s\n", | |
2333 | + (unsigned long long)device->devid, | |
2334 | + total_bytes, bytes_used, device->name); | |
2335 | + free(total_bytes); | |
2336 | + free(bytes_used); | |
2337 | + devs_found++; | |
2338 | + } | |
2339 | + if (devs_found < total) { | |
2340 | + printf("\t*** Some devices missing\n"); | |
2341 | + } | |
2342 | + printf("\n"); | |
2343 | +} | |
2344 | + | |
2345 | +int do_show_filesystem(int argc, char **argv) | |
2346 | +{ | |
2347 | + struct list_head *all_uuids; | |
2348 | + struct btrfs_fs_devices *fs_devices; | |
2349 | + struct list_head *cur_uuid; | |
2350 | + char *search = argv[1]; | |
2351 | + int ret; | |
2352 | + | |
2353 | + ret = btrfs_scan_one_dir("/dev", 0); | |
2354 | + if (ret){ | |
2355 | + fprintf(stderr, "ERROR: error %d while scanning\n", ret); | |
2356 | + return 18; | |
2357 | + } | |
2358 | + | |
2359 | + all_uuids = btrfs_scanned_uuids(); | |
2360 | + list_for_each(cur_uuid, all_uuids) { | |
2361 | + fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices, | |
2362 | + list); | |
2363 | + if (search && uuid_search(fs_devices, search) == 0) | |
2364 | + continue; | |
2365 | + print_one_uuid(fs_devices); | |
2366 | + } | |
2367 | + printf("%s\n", BTRFS_BUILD_VERSION); | |
2368 | + return 0; | |
2369 | +} | |
2370 | + | |
2371 | +int do_add_volume(int nargs, char **args) | |
2372 | +{ | |
2373 | + | |
2374 | + char *mntpnt = args[nargs-1]; | |
2375 | + int i, fdmnt, ret=0; | |
2376 | + | |
2377 | + | |
2378 | + fdmnt = open_file_or_dir(mntpnt); | |
2379 | + if (fdmnt < 0) { | |
2380 | + fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); | |
2381 | + return 12; | |
2382 | + } | |
2383 | + | |
2384 | + for(i=1 ; i < (nargs-1) ; i++ ){ | |
2385 | + struct btrfs_ioctl_vol_args ioctl_args; | |
2386 | + int devfd, res; | |
2387 | + u64 dev_block_count = 0; | |
2388 | + struct stat st; | |
2389 | + | |
2390 | + devfd = open(args[i], O_RDWR); | |
2391 | + if (!devfd) { | |
2392 | + fprintf(stderr, "ERROR: Unable to open device '%s'\n", args[i]); | |
2393 | + close(devfd); | |
2394 | + ret++; | |
2395 | + continue; | |
2396 | + } | |
2397 | + ret = fstat(devfd, &st); | |
2398 | + if (ret) { | |
2399 | + fprintf(stderr, "ERROR: Unable to stat '%s'\n", args[i]); | |
2400 | + close(devfd); | |
2401 | + ret++; | |
2402 | + continue; | |
2403 | + } | |
2404 | + if (!S_ISBLK(st.st_mode)) { | |
2405 | + fprintf(stderr, "ERROR: '%s' is not a block device\n", args[i]); | |
2406 | + close(devfd); | |
2407 | + ret++; | |
2408 | + continue; | |
2409 | + } | |
2410 | + | |
2411 | + res = btrfs_prepare_device(devfd, args[i], 1, &dev_block_count); | |
2412 | + if (res) { | |
2413 | + fprintf(stderr, "ERROR: Unable to init '%s'\n", args[i]); | |
2414 | + close(devfd); | |
2415 | + ret++; | |
2416 | + continue; | |
2417 | + } | |
2418 | + close(devfd); | |
2419 | + | |
2420 | + strcpy(ioctl_args.name, args[i]); | |
2421 | + res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args); | |
2422 | + if(res<0){ | |
2423 | + fprintf(stderr, "ERROR: error adding the device '%s'\n", args[i]); | |
2424 | + ret++; | |
2425 | + } | |
2426 | + | |
2427 | + } | |
2428 | + | |
2429 | + close(fdmnt); | |
2430 | + if( ret) | |
2431 | + return ret+20; | |
2432 | + else | |
2433 | + return 0; | |
2434 | + | |
2435 | +} | |
2436 | + | |
2437 | +int do_balance(int argc, char **argv) | |
2438 | +{ | |
2439 | + | |
2440 | + int fdmnt, ret=0; | |
2441 | + struct btrfs_ioctl_vol_args args; | |
2442 | + char *path = argv[1]; | |
2443 | + | |
2444 | + fdmnt = open_file_or_dir(path); | |
2445 | + if (fdmnt < 0) { | |
2446 | + fprintf(stderr, "ERROR: can't access to '%s'\n", path); | |
2447 | + return 12; | |
2448 | + } | |
2449 | + | |
2450 | + memset(&args, 0, sizeof(args)); | |
2451 | + ret = ioctl(fdmnt, BTRFS_IOC_BALANCE, &args); | |
2452 | + close(fdmnt); | |
2453 | + if(ret<0){ | |
2454 | + fprintf(stderr, "ERROR: balancing '%s'\n", path); | |
2455 | + | |
2456 | + return 19; | |
2457 | + } | |
2458 | + return 0; | |
2459 | +} | |
2460 | +int do_remove_volume(int nargs, char **args) | |
2461 | +{ | |
2462 | + | |
2463 | + char *mntpnt = args[nargs-1]; | |
2464 | + int i, fdmnt, ret=0; | |
2465 | + | |
2466 | + fdmnt = open_file_or_dir(mntpnt); | |
2467 | + if (fdmnt < 0) { | |
2468 | + fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); | |
2469 | + return 12; | |
2470 | + } | |
2471 | + | |
2472 | + for(i=1 ; i < (nargs-1) ; i++ ){ | |
2473 | + struct btrfs_ioctl_vol_args arg; | |
2474 | + int res; | |
2475 | + | |
2476 | + strcpy(arg.name, args[i]); | |
2477 | + res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); | |
2478 | + if(res<0){ | |
2479 | + fprintf(stderr, "ERROR: error removing the device '%s'\n", args[i]); | |
2480 | + ret++; | |
2481 | + } | |
2482 | + } | |
2483 | + | |
2484 | + close(fdmnt); | |
2485 | + if( ret) | |
2486 | + return ret+20; | |
2487 | + else | |
2488 | + return 0; | |
2489 | +} | |
2490 | + | |
2491 | +int do_set_default_subvol(int nargs, char **argv) | |
2492 | +{ | |
2493 | + int ret=0, fd; | |
2494 | + u64 objectid; | |
2495 | + char *path = argv[2]; | |
2496 | + char *subvolid = argv[1]; | |
2497 | + | |
2498 | + fd = open_file_or_dir(path); | |
2499 | + if (fd < 0) { | |
2500 | + fprintf(stderr, "ERROR: can't access to '%s'\n", path); | |
2501 | + return 12; | |
2502 | + } | |
2503 | + | |
2504 | + objectid = (unsigned long long)strtoll(subvolid, NULL, 0); | |
2505 | + if (errno == ERANGE) { | |
2506 | + fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid); | |
2507 | + return 30; | |
2508 | + } | |
2509 | + ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid); | |
2510 | + close(fd); | |
2511 | + if( ret < 0 ){ | |
2512 | + fprintf(stderr, "ERROR: unable to set a new default subvolume\n"); | |
2513 | + return 30; | |
2514 | + } | |
2515 | + return 0; | |
2516 | +} | |
2517 | + | |
2518 | +int do_df_filesystem(int nargs, char **argv) | |
2519 | +{ | |
2520 | + struct btrfs_ioctl_space_args *sargs; | |
2521 | + u64 count = 0, i; | |
2522 | + int ret; | |
2523 | + int fd; | |
2524 | + char *path = argv[1]; | |
2525 | + | |
2526 | + fd = open_file_or_dir(path); | |
2527 | + if (fd < 0) { | |
2528 | + fprintf(stderr, "ERROR: can't access to '%s'\n", path); | |
2529 | + return 12; | |
2530 | + } | |
2531 | + | |
2532 | + sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); | |
2533 | + if (!sargs) | |
2534 | + return -ENOMEM; | |
2535 | + | |
2536 | + sargs->space_slots = 0; | |
2537 | + sargs->total_spaces = 0; | |
2538 | + | |
2539 | + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); | |
2540 | + if (ret) { | |
2541 | + free(sargs); | |
2542 | + return ret; | |
2543 | + } | |
2544 | + if (!sargs->total_spaces) | |
2545 | + return 0; | |
2546 | + | |
2547 | + count = sargs->total_spaces; | |
2548 | + | |
2549 | + sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) + | |
2550 | + (count * sizeof(struct btrfs_ioctl_space_info))); | |
2551 | + if (!sargs) | |
2552 | + return -ENOMEM; | |
2553 | + | |
2554 | + sargs->space_slots = count; | |
2555 | + sargs->total_spaces = 0; | |
2556 | + | |
2557 | + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); | |
2558 | + if (ret) { | |
2559 | + free(sargs); | |
2560 | + return ret; | |
2561 | + } | |
2562 | + | |
2563 | + for (i = 0; i < sargs->total_spaces; i++) { | |
2564 | + char description[80]; | |
2565 | + char *total_bytes; | |
2566 | + char *used_bytes; | |
2567 | + int written = 0; | |
2568 | + u64 flags = sargs->spaces[i].flags; | |
2569 | + | |
2570 | + memset(description, 0, 80); | |
2571 | + | |
2572 | + if (flags & BTRFS_BLOCK_GROUP_DATA) { | |
2573 | + snprintf(description, 5, "%s", "Data"); | |
2574 | + written += 4; | |
2575 | + } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) { | |
2576 | + snprintf(description, 7, "%s", "System"); | |
2577 | + written += 6; | |
2578 | + } else if (flags & BTRFS_BLOCK_GROUP_METADATA) { | |
2579 | + snprintf(description, 9, "%s", "Metadata"); | |
2580 | + written += 8; | |
2581 | + } | |
2582 | + | |
2583 | + if (flags & BTRFS_BLOCK_GROUP_RAID0) { | |
2584 | + snprintf(description+written, 8, "%s", ", RAID0"); | |
2585 | + written += 7; | |
2586 | + } else if (flags & BTRFS_BLOCK_GROUP_RAID1) { | |
2587 | + snprintf(description+written, 8, "%s", ", RAID1"); | |
2588 | + written += 7; | |
2589 | + } else if (flags & BTRFS_BLOCK_GROUP_DUP) { | |
2590 | + snprintf(description+written, 6, "%s", ", DUP"); | |
2591 | + written += 5; | |
2592 | + } else if (flags & BTRFS_BLOCK_GROUP_RAID10) { | |
2593 | + snprintf(description+written, 9, "%s", ", RAID10"); | |
2594 | + written += 8; | |
2595 | + } | |
2596 | + | |
2597 | + total_bytes = pretty_sizes(sargs->spaces[i].total_bytes); | |
2598 | + used_bytes = pretty_sizes(sargs->spaces[i].used_bytes); | |
2599 | + printf("%s: total=%s, used=%s\n", description, total_bytes, | |
2600 | + used_bytes); | |
2601 | + } | |
2602 | + free(sargs); | |
2603 | + | |
2604 | + return 0; | |
2605 | +} | |
2606 | diff --git a/btrfs_cmds.h b/btrfs_cmds.h | |
2607 | new file mode 100644 | |
2608 | index 0000000..7bde191 | |
2609 | --- /dev/null | |
2610 | +++ b/btrfs_cmds.h | |
2611 | @@ -0,0 +1,34 @@ | |
2612 | +/* | |
2613 | + * This program is free software; you can redistribute it and/or | |
2614 | + * modify it under the terms of the GNU General Public | |
2615 | + * License v2 as published by the Free Software Foundation. | |
2616 | + * | |
2617 | + * This program is distributed in the hope that it will be useful, | |
2618 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
2619 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
2620 | + * General Public License for more details. | |
2621 | + * | |
2622 | + * You should have received a copy of the GNU General Public | |
2623 | + * License along with this program; if not, write to the | |
2624 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
2625 | + * Boston, MA 021110-1307, USA. | |
2626 | + */ | |
2627 | + | |
2628 | +/* btrfs_cmds.c*/ | |
2629 | +int do_clone(int nargs, char **argv); | |
2630 | +int do_delete_subvolume(int nargs, char **argv); | |
2631 | +int do_create_subvol(int nargs, char **argv); | |
2632 | +int do_fssync(int nargs, char **argv); | |
2633 | +int do_defrag(int argc, char **argv); | |
2634 | +int do_show_filesystem(int nargs, char **argv); | |
2635 | +int do_add_volume(int nargs, char **args); | |
2636 | +int do_balance(int nargs, char **argv); | |
2637 | +int do_remove_volume(int nargs, char **args); | |
2638 | +int do_scan(int nargs, char **argv); | |
2639 | +int do_resize(int nargs, char **argv); | |
2640 | +int do_subvol_list(int nargs, char **argv); | |
2641 | +int do_set_default_subvol(int nargs, char **argv); | |
2642 | +int list_subvols(int fd); | |
2643 | +int do_df_filesystem(int nargs, char **argv); | |
2644 | +int find_updated_files(int fd, u64 root_id, u64 oldest_gen); | |
2645 | +int do_find_newer(int argc, char **argv); | |
2646 | diff --git a/btrfsck.c b/btrfsck.c | |
2647 | index 40c90f8..63e44d1 100644 | |
2648 | --- a/btrfsck.c | |
2649 | +++ b/btrfsck.c | |
2650 | @@ -20,7 +20,9 @@ | |
2651 | #define _GNU_SOURCE 1 | |
2652 | #include <stdio.h> | |
2653 | #include <stdlib.h> | |
2654 | +#include <unistd.h> | |
2655 | #include <fcntl.h> | |
2656 | +#include <sys/stat.h> | |
2657 | #include "kerncompat.h" | |
2658 | #include "ctree.h" | |
2659 | #include "disk-io.h" | |
2660 | @@ -28,6 +30,7 @@ | |
2661 | #include "transaction.h" | |
2662 | #include "list.h" | |
2663 | #include "version.h" | |
2664 | +#include "utils.h" | |
2665 | ||
2666 | static u64 bytes_used = 0; | |
2667 | static u64 total_csum_bytes = 0; | |
2668 | @@ -36,7 +39,7 @@ static u64 total_fs_tree_bytes = 0; | |
2669 | static u64 btree_space_waste = 0; | |
2670 | static u64 data_bytes_allocated = 0; | |
2671 | static u64 data_bytes_referenced = 0; | |
2672 | -int found_old_backref = 0; | |
2673 | +static int found_old_backref = 0; | |
2674 | ||
2675 | struct extent_backref { | |
2676 | struct list_head list; | |
2677 | @@ -100,7 +103,11 @@ struct inode_backref { | |
2678 | #define REF_ERR_DUP_INODE_REF (1 << 5) | |
2679 | #define REF_ERR_INDEX_UNMATCH (1 << 6) | |
2680 | #define REF_ERR_FILETYPE_UNMATCH (1 << 7) | |
2681 | -#define REF_ERR_NAME_TOO_LONG (1 << 8) | |
2682 | +#define REF_ERR_NAME_TOO_LONG (1 << 8) // 100 | |
2683 | +#define REF_ERR_NO_ROOT_REF (1 << 9) | |
2684 | +#define REF_ERR_NO_ROOT_BACKREF (1 << 10) | |
2685 | +#define REF_ERR_DUP_ROOT_REF (1 << 11) | |
2686 | +#define REF_ERR_DUP_ROOT_BACKREF (1 << 12) | |
2687 | ||
2688 | struct inode_record { | |
2689 | struct list_head backrefs; | |
2690 | @@ -144,6 +151,29 @@ struct inode_record { | |
2691 | #define I_ERR_SOME_CSUM_MISSING (1 << 12) | |
2692 | #define I_ERR_LINK_COUNT_WRONG (1 << 13) | |
2693 | ||
2694 | +struct root_backref { | |
2695 | + struct list_head list; | |
2696 | + unsigned int found_dir_item:1; | |
2697 | + unsigned int found_dir_index:1; | |
2698 | + unsigned int found_back_ref:1; | |
2699 | + unsigned int found_forward_ref:1; | |
2700 | + unsigned int reachable:1; | |
2701 | + int errors; | |
2702 | + u64 ref_root; | |
2703 | + u64 dir; | |
2704 | + u64 index; | |
2705 | + u16 namelen; | |
2706 | + char name[0]; | |
2707 | +}; | |
2708 | + | |
2709 | +struct root_record { | |
2710 | + struct list_head backrefs; | |
2711 | + struct cache_extent cache; | |
2712 | + unsigned int found_root_item:1; | |
2713 | + u64 objectid; | |
2714 | + u32 found_ref; | |
2715 | +}; | |
2716 | + | |
2717 | struct ptr_node { | |
2718 | struct cache_extent cache; | |
2719 | void *data; | |
2720 | @@ -151,6 +181,7 @@ struct ptr_node { | |
2721 | ||
2722 | struct shared_node { | |
2723 | struct cache_extent cache; | |
2724 | + struct cache_tree root_cache; | |
2725 | struct cache_tree inode_cache; | |
2726 | struct inode_record *current; | |
2727 | u32 refs; | |
2728 | @@ -258,6 +289,14 @@ static void free_inode_rec(struct inode_record *rec) | |
2729 | free(rec); | |
2730 | } | |
2731 | ||
2732 | +static int can_free_inode_rec(struct inode_record *rec) | |
2733 | +{ | |
2734 | + if (!rec->errors && rec->checked && rec->found_inode_item && | |
2735 | + rec->nlink == rec->found_link && list_empty(&rec->backrefs)) | |
2736 | + return 1; | |
2737 | + return 0; | |
2738 | +} | |
2739 | + | |
2740 | static void maybe_free_inode_rec(struct cache_tree *inode_cache, | |
2741 | struct inode_record *rec) | |
2742 | { | |
2743 | @@ -309,8 +348,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache, | |
2744 | } | |
2745 | ||
2746 | BUG_ON(rec->refs != 1); | |
2747 | - if (!rec->errors && rec->nlink == rec->found_link && | |
2748 | - list_empty(&rec->backrefs)) { | |
2749 | + if (can_free_inode_rec(rec)) { | |
2750 | cache = find_cache_extent(inode_cache, rec->ino, 1); | |
2751 | node = container_of(cache, struct ptr_node, cache); | |
2752 | BUG_ON(node->data != rec); | |
2753 | @@ -338,14 +376,12 @@ static int check_orphan_item(struct btrfs_root *root, u64 ino) | |
2754 | return ret; | |
2755 | } | |
2756 | ||
2757 | -static int process_inode_item(struct btrfs_root *root, | |
2758 | - struct extent_buffer *eb, | |
2759 | +static int process_inode_item(struct extent_buffer *eb, | |
2760 | int slot, struct btrfs_key *key, | |
2761 | struct shared_node *active_node) | |
2762 | { | |
2763 | struct inode_record *rec; | |
2764 | struct btrfs_inode_item *item; | |
2765 | - int ret; | |
2766 | ||
2767 | rec = active_node->current; | |
2768 | BUG_ON(rec->ino != key->objectid || rec->refs > 1); | |
2769 | @@ -361,11 +397,8 @@ static int process_inode_item(struct btrfs_root *root, | |
2770 | if (btrfs_inode_flags(eb, item) & BTRFS_INODE_NODATASUM) | |
2771 | rec->nodatasum = 1; | |
2772 | rec->found_inode_item = 1; | |
2773 | - if (rec->nlink == 0) { | |
2774 | - ret = check_orphan_item(root, rec->ino); | |
2775 | - if (ret == -ENOENT) | |
2776 | - rec->errors |= I_ERR_NO_ORPHAN_ITEM; | |
2777 | - } | |
2778 | + if (rec->nlink == 0) | |
2779 | + rec->errors |= I_ERR_NO_ORPHAN_ITEM; | |
2780 | maybe_free_inode_rec(&active_node->inode_cache, rec); | |
2781 | return 0; | |
2782 | } | |
2783 | @@ -391,7 +424,6 @@ static struct inode_backref *get_inode_backref(struct inode_record *rec, | |
2784 | memcpy(backref->name, name, namelen); | |
2785 | backref->name[namelen] = '\0'; | |
2786 | list_add_tail(&backref->list, &rec->backrefs); | |
2787 | - rec->found_link++; | |
2788 | return backref; | |
2789 | } | |
2790 | ||
2791 | @@ -419,6 +451,7 @@ static int add_inode_backref(struct cache_tree *inode_cache, | |
2792 | backref->filetype = filetype; | |
2793 | backref->found_dir_index = 1; | |
2794 | } else if (itemtype == BTRFS_DIR_ITEM_KEY) { | |
2795 | + rec->found_link++; | |
2796 | if (backref->found_dir_item) | |
2797 | backref->errors |= REF_ERR_DUP_DIR_ITEM; | |
2798 | if (backref->found_dir_index && backref->filetype != filetype) | |
2799 | @@ -443,10 +476,10 @@ static int add_inode_backref(struct cache_tree *inode_cache, | |
2800 | } | |
2801 | ||
2802 | static int merge_inode_recs(struct inode_record *src, struct inode_record *dst, | |
2803 | - struct shared_node *dst_node) | |
2804 | + struct cache_tree *dst_cache) | |
2805 | { | |
2806 | struct inode_backref *backref; | |
2807 | - struct cache_tree *dst_cache = &dst_node->inode_cache; | |
2808 | + u32 dir_count = 0; | |
2809 | ||
2810 | dst->merging = 1; | |
2811 | list_for_each_entry(backref, &src->backrefs, list) { | |
2812 | @@ -457,6 +490,7 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst, | |
2813 | BTRFS_DIR_INDEX_KEY, backref->errors); | |
2814 | } | |
2815 | if (backref->found_dir_item) { | |
2816 | + dir_count++; | |
2817 | add_inode_backref(dst_cache, dst->ino, | |
2818 | backref->dir, 0, backref->name, | |
2819 | backref->namelen, backref->filetype, | |
2820 | @@ -481,6 +515,8 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst, | |
2821 | if (dst->first_extent_gap > src->first_extent_gap) | |
2822 | dst->first_extent_gap = src->first_extent_gap; | |
2823 | ||
2824 | + BUG_ON(src->found_link < dir_count); | |
2825 | + dst->found_link += src->found_link - dir_count; | |
2826 | dst->found_size += src->found_size; | |
2827 | if (src->extent_start != (u64)-1) { | |
2828 | if (dst->extent_start == (u64)-1) { | |
2829 | @@ -510,14 +546,8 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst, | |
2830 | dst->errors |= I_ERR_DUP_INODE_ITEM; | |
2831 | } | |
2832 | } | |
2833 | - | |
2834 | - if (src->checked) { | |
2835 | - dst->checked = 1; | |
2836 | - if (dst_node->current == dst) | |
2837 | - dst_node->current = NULL; | |
2838 | - } | |
2839 | dst->merging = 0; | |
2840 | - maybe_free_inode_rec(dst_cache, dst); | |
2841 | + | |
2842 | return 0; | |
2843 | } | |
2844 | ||
2845 | @@ -537,8 +567,9 @@ static int splice_shared_node(struct shared_node *src_node, | |
2846 | if (src_node->current) | |
2847 | current_ino = src_node->current->ino; | |
2848 | ||
2849 | - src = &src_node->inode_cache; | |
2850 | - dst = &dst_node->inode_cache; | |
2851 | + src = &src_node->root_cache; | |
2852 | + dst = &dst_node->root_cache; | |
2853 | +again: | |
2854 | cache = find_first_cache_extent(src, 0); | |
2855 | while (cache) { | |
2856 | node = container_of(cache, struct ptr_node, cache); | |
2857 | @@ -558,13 +589,26 @@ static int splice_shared_node(struct shared_node *src_node, | |
2858 | ret = insert_existing_cache_extent(dst, &ins->cache); | |
2859 | if (ret == -EEXIST) { | |
2860 | conflict = get_inode_rec(dst, rec->ino, 1); | |
2861 | - merge_inode_recs(rec, conflict, dst_node); | |
2862 | + merge_inode_recs(rec, conflict, dst); | |
2863 | + if (rec->checked) { | |
2864 | + conflict->checked = 1; | |
2865 | + if (dst_node->current == conflict) | |
2866 | + dst_node->current = NULL; | |
2867 | + } | |
2868 | + maybe_free_inode_rec(dst, conflict); | |
2869 | free_inode_rec(rec); | |
2870 | free(ins); | |
2871 | } else { | |
2872 | BUG_ON(ret); | |
2873 | } | |
2874 | } | |
2875 | + | |
2876 | + if (src == &src_node->root_cache) { | |
2877 | + src = &src_node->inode_cache; | |
2878 | + dst = &dst_node->inode_cache; | |
2879 | + goto again; | |
2880 | + } | |
2881 | + | |
2882 | if (current_ino > 0 && (!dst_node->current || | |
2883 | current_ino > dst_node->current->ino)) { | |
2884 | if (dst_node->current) { | |
2885 | @@ -616,6 +660,7 @@ static int add_shared_node(struct cache_tree *shared, u64 bytenr, u32 refs) | |
2886 | node = calloc(1, sizeof(*node)); | |
2887 | node->cache.start = bytenr; | |
2888 | node->cache.size = 1; | |
2889 | + cache_tree_init(&node->root_cache); | |
2890 | cache_tree_init(&node->inode_cache); | |
2891 | node->refs = refs; | |
2892 | ||
2893 | @@ -646,6 +691,7 @@ static int enter_shared_node(struct btrfs_root *root, u64 bytenr, u32 refs, | |
2894 | if (wc->root_level == wc->active_node && | |
2895 | btrfs_root_refs(&root->root_item) == 0) { | |
2896 | if (--node->refs == 0) { | |
2897 | + free_inode_recs(&node->root_cache); | |
2898 | free_inode_recs(&node->inode_cache); | |
2899 | remove_cache_extent(&wc->shared, &node->cache); | |
2900 | free(node); | |
2901 | @@ -708,10 +754,12 @@ static int process_dir_item(struct extent_buffer *eb, | |
2902 | int filetype; | |
2903 | struct btrfs_dir_item *di; | |
2904 | struct inode_record *rec; | |
2905 | + struct cache_tree *root_cache; | |
2906 | struct cache_tree *inode_cache; | |
2907 | struct btrfs_key location; | |
2908 | char namebuf[BTRFS_NAME_LEN]; | |
2909 | ||
2910 | + root_cache = &active_node->root_cache; | |
2911 | inode_cache = &active_node->inode_cache; | |
2912 | rec = active_node->current; | |
2913 | rec->found_dir_item = 1; | |
2914 | @@ -740,7 +788,9 @@ static int process_dir_item(struct extent_buffer *eb, | |
2915 | key->objectid, key->offset, namebuf, | |
2916 | len, filetype, key->type, error); | |
2917 | } else if (location.type == BTRFS_ROOT_ITEM_KEY) { | |
2918 | - /* fixme: check root back & forward references */ | |
2919 | + add_inode_backref(root_cache, location.objectid, | |
2920 | + key->objectid, key->offset, namebuf, | |
2921 | + len, filetype, key->type, error); | |
2922 | } else { | |
2923 | fprintf(stderr, "warning line %d\n", __LINE__); | |
2924 | } | |
2925 | @@ -977,8 +1027,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb, | |
2926 | ret = process_inode_ref(eb, i, &key, active_node); | |
2927 | break; | |
2928 | case BTRFS_INODE_ITEM_KEY: | |
2929 | - ret = process_inode_item(root, eb, i, &key, | |
2930 | - active_node); | |
2931 | + ret = process_inode_item(eb, i, &key, active_node); | |
2932 | break; | |
2933 | case BTRFS_EXTENT_DATA_KEY: | |
2934 | ret = process_file_extent(root, eb, i, &key, | |
2935 | @@ -1120,7 +1169,7 @@ static int check_root_dir(struct inode_record *rec) | |
2936 | ||
2937 | if (!rec->found_inode_item || rec->errors) | |
2938 | goto out; | |
2939 | - if (rec->nlink != 1 || rec->found_link != 1) | |
2940 | + if (rec->nlink != 1 || rec->found_link != 0) | |
2941 | goto out; | |
2942 | if (list_empty(&rec->backrefs)) | |
2943 | goto out; | |
2944 | @@ -1176,13 +1225,23 @@ static int check_inode_recs(struct btrfs_root *root, | |
2945 | node = container_of(cache, struct ptr_node, cache); | |
2946 | rec = node->data; | |
2947 | remove_cache_extent(inode_cache, &node->cache); | |
2948 | + free(node); | |
2949 | if (rec->ino == root_dirid || | |
2950 | rec->ino == BTRFS_ORPHAN_OBJECTID) { | |
2951 | - free(node); | |
2952 | free_inode_rec(rec); | |
2953 | continue; | |
2954 | } | |
2955 | ||
2956 | + if (rec->errors & I_ERR_NO_ORPHAN_ITEM) { | |
2957 | + ret = check_orphan_item(root, rec->ino); | |
2958 | + if (ret == 0) | |
2959 | + rec->errors &= ~I_ERR_NO_ORPHAN_ITEM; | |
2960 | + if (can_free_inode_rec(rec)) { | |
2961 | + free_inode_rec(rec); | |
2962 | + continue; | |
2963 | + } | |
2964 | + } | |
2965 | + | |
2966 | error++; | |
2967 | if (!rec->found_inode_item) | |
2968 | rec->errors |= I_ERR_NO_INODE_ITEM; | |
2969 | @@ -1205,13 +1264,314 @@ static int check_inode_recs(struct btrfs_root *root, | |
2970 | backref->namelen, backref->name, | |
2971 | backref->filetype, backref->errors); | |
2972 | } | |
2973 | - free(node); | |
2974 | free_inode_rec(rec); | |
2975 | } | |
2976 | return (error > 0) ? -1 : 0; | |
2977 | } | |
2978 | ||
2979 | +static struct root_record *get_root_rec(struct cache_tree *root_cache, | |
2980 | + u64 objectid) | |
2981 | +{ | |
2982 | + struct cache_extent *cache; | |
2983 | + struct root_record *rec = NULL; | |
2984 | + int ret; | |
2985 | + | |
2986 | + cache = find_cache_extent(root_cache, objectid, 1); | |
2987 | + if (cache) { | |
2988 | + rec = container_of(cache, struct root_record, cache); | |
2989 | + } else { | |
2990 | + rec = calloc(1, sizeof(*rec)); | |
2991 | + rec->objectid = objectid; | |
2992 | + INIT_LIST_HEAD(&rec->backrefs); | |
2993 | + rec->cache.start = objectid; | |
2994 | + rec->cache.size = 1; | |
2995 | + | |
2996 | + ret = insert_existing_cache_extent(root_cache, &rec->cache); | |
2997 | + BUG_ON(ret); | |
2998 | + } | |
2999 | + return rec; | |
3000 | +} | |
3001 | + | |
3002 | +static struct root_backref *get_root_backref(struct root_record *rec, | |
3003 | + u64 ref_root, u64 dir, u64 index, | |
3004 | + const char *name, int namelen) | |
3005 | +{ | |
3006 | + struct root_backref *backref; | |
3007 | + | |
3008 | + list_for_each_entry(backref, &rec->backrefs, list) { | |
3009 | + if (backref->ref_root != ref_root || backref->dir != dir || | |
3010 | + backref->namelen != namelen) | |
3011 | + continue; | |
3012 | + if (memcmp(name, backref->name, namelen)) | |
3013 | + continue; | |
3014 | + return backref; | |
3015 | + } | |
3016 | + | |
3017 | + backref = malloc(sizeof(*backref) + namelen + 1); | |
3018 | + memset(backref, 0, sizeof(*backref)); | |
3019 | + backref->ref_root = ref_root; | |
3020 | + backref->dir = dir; | |
3021 | + backref->index = index; | |
3022 | + backref->namelen = namelen; | |
3023 | + memcpy(backref->name, name, namelen); | |
3024 | + backref->name[namelen] = '\0'; | |
3025 | + list_add_tail(&backref->list, &rec->backrefs); | |
3026 | + return backref; | |
3027 | +} | |
3028 | + | |
3029 | +static void free_root_recs(struct cache_tree *root_cache) | |
3030 | +{ | |
3031 | + struct cache_extent *cache; | |
3032 | + struct root_record *rec; | |
3033 | + struct root_backref *backref; | |
3034 | + | |
3035 | + while (1) { | |
3036 | + cache = find_first_cache_extent(root_cache, 0); | |
3037 | + if (!cache) | |
3038 | + break; | |
3039 | + rec = container_of(cache, struct root_record, cache); | |
3040 | + remove_cache_extent(root_cache, &rec->cache); | |
3041 | + | |
3042 | + while (!list_empty(&rec->backrefs)) { | |
3043 | + backref = list_entry(rec->backrefs.next, | |
3044 | + struct root_backref, list); | |
3045 | + list_del(&backref->list); | |
3046 | + free(backref); | |
3047 | + } | |
3048 | + kfree(rec); | |
3049 | + } | |
3050 | +} | |
3051 | + | |
3052 | +static int add_root_backref(struct cache_tree *root_cache, | |
3053 | + u64 root_id, u64 ref_root, u64 dir, u64 index, | |
3054 | + const char *name, int namelen, | |
3055 | + int item_type, int errors) | |
3056 | +{ | |
3057 | + struct root_record *rec; | |
3058 | + struct root_backref *backref; | |
3059 | + | |
3060 | + rec = get_root_rec(root_cache, root_id); | |
3061 | + backref = get_root_backref(rec, ref_root, dir, index, name, namelen); | |
3062 | + | |
3063 | + backref->errors |= errors; | |
3064 | + | |
3065 | + if (item_type != BTRFS_DIR_ITEM_KEY) { | |
3066 | + if (backref->found_dir_index || backref->found_back_ref || | |
3067 | + backref->found_forward_ref) { | |
3068 | + if (backref->index != index) | |
3069 | + backref->errors |= REF_ERR_INDEX_UNMATCH; | |
3070 | + } else { | |
3071 | + backref->index = index; | |
3072 | + } | |
3073 | + } | |
3074 | + | |
3075 | + if (item_type == BTRFS_DIR_ITEM_KEY) { | |
3076 | + backref->found_dir_item = 1; | |
3077 | + backref->reachable = 1; | |
3078 | + rec->found_ref++; | |
3079 | + } else if (item_type == BTRFS_DIR_INDEX_KEY) { | |
3080 | + backref->found_dir_index = 1; | |
3081 | + } else if (item_type == BTRFS_ROOT_REF_KEY) { | |
3082 | + if (backref->found_forward_ref) | |
3083 | + backref->errors |= REF_ERR_DUP_ROOT_REF; | |
3084 | + backref->found_forward_ref = 1; | |
3085 | + } else if (item_type == BTRFS_ROOT_BACKREF_KEY) { | |
3086 | + if (backref->found_back_ref) | |
3087 | + backref->errors |= REF_ERR_DUP_ROOT_BACKREF; | |
3088 | + backref->found_back_ref = 1; | |
3089 | + } else { | |
3090 | + BUG_ON(1); | |
3091 | + } | |
3092 | + | |
3093 | + return 0; | |
3094 | +} | |
3095 | + | |
3096 | +static int merge_root_recs(struct btrfs_root *root, | |
3097 | + struct cache_tree *src_cache, | |
3098 | + struct cache_tree *dst_cache) | |
3099 | +{ | |
3100 | + struct cache_extent *cache; | |
3101 | + struct ptr_node *node; | |
3102 | + struct inode_record *rec; | |
3103 | + struct inode_backref *backref; | |
3104 | + | |
3105 | + if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) { | |
3106 | + free_inode_recs(src_cache); | |
3107 | + return 0; | |
3108 | + } | |
3109 | + | |
3110 | + while (1) { | |
3111 | + cache = find_first_cache_extent(src_cache, 0); | |
3112 | + if (!cache) | |
3113 | + break; | |
3114 | + node = container_of(cache, struct ptr_node, cache); | |
3115 | + rec = node->data; | |
3116 | + remove_cache_extent(src_cache, &node->cache); | |
3117 | + free(node); | |
3118 | + | |
3119 | + list_for_each_entry(backref, &rec->backrefs, list) { | |
3120 | + BUG_ON(backref->found_inode_ref); | |
3121 | + if (backref->found_dir_item) | |
3122 | + add_root_backref(dst_cache, rec->ino, | |
3123 | + root->root_key.objectid, backref->dir, | |
3124 | + backref->index, backref->name, | |
3125 | + backref->namelen, BTRFS_DIR_ITEM_KEY, | |
3126 | + backref->errors); | |
3127 | + if (backref->found_dir_index) | |
3128 | + add_root_backref(dst_cache, rec->ino, | |
3129 | + root->root_key.objectid, backref->dir, | |
3130 | + backref->index, backref->name, | |
3131 | + backref->namelen, BTRFS_DIR_INDEX_KEY, | |
3132 | + backref->errors); | |
3133 | + } | |
3134 | + free_inode_rec(rec); | |
3135 | + } | |
3136 | + return 0; | |
3137 | +} | |
3138 | + | |
3139 | +static int check_root_refs(struct btrfs_root *root, | |
3140 | + struct cache_tree *root_cache) | |
3141 | +{ | |
3142 | + struct root_record *rec; | |
3143 | + struct root_record *ref_root; | |
3144 | + struct root_backref *backref; | |
3145 | + struct cache_extent *cache; | |
3146 | + int loop = 1; | |
3147 | + int ret; | |
3148 | + int error; | |
3149 | + int errors = 0; | |
3150 | + | |
3151 | + rec = get_root_rec(root_cache, BTRFS_FS_TREE_OBJECTID); | |
3152 | + rec->found_ref = 1; | |
3153 | + | |
3154 | + /* fixme: this can not detect circular references */ | |
3155 | + while (loop) { | |
3156 | + loop = 0; | |
3157 | + cache = find_first_cache_extent(root_cache, 0); | |
3158 | + while (1) { | |
3159 | + if (!cache) | |
3160 | + break; | |
3161 | + rec = container_of(cache, struct root_record, cache); | |
3162 | + cache = next_cache_extent(cache); | |
3163 | + | |
3164 | + if (rec->found_ref == 0) | |
3165 | + continue; | |
3166 | + | |
3167 | + list_for_each_entry(backref, &rec->backrefs, list) { | |
3168 | + if (!backref->reachable) | |
3169 | + continue; | |
3170 | + | |
3171 | + ref_root = get_root_rec(root_cache, | |
3172 | + backref->ref_root); | |
3173 | + if (ref_root->found_ref > 0) | |
3174 | + continue; | |
3175 | + | |
3176 | + backref->reachable = 0; | |
3177 | + rec->found_ref--; | |
3178 | + if (rec->found_ref == 0) | |
3179 | + loop = 1; | |
3180 | + } | |
3181 | + } | |
3182 | + } | |
3183 | + | |
3184 | + cache = find_first_cache_extent(root_cache, 0); | |
3185 | + while (1) { | |
3186 | + if (!cache) | |
3187 | + break; | |
3188 | + rec = container_of(cache, struct root_record, cache); | |
3189 | + cache = next_cache_extent(cache); | |
3190 | + | |
3191 | + if (rec->found_ref == 0 && | |
3192 | + rec->objectid >= BTRFS_FIRST_FREE_OBJECTID && | |
3193 | + rec->objectid <= BTRFS_LAST_FREE_OBJECTID) { | |
3194 | + ret = check_orphan_item(root->fs_info->tree_root, | |
3195 | + rec->objectid); | |
3196 | + if (ret == 0) | |
3197 | + continue; | |
3198 | + errors++; | |
3199 | + fprintf(stderr, "fs tree %llu not referenced\n", | |
3200 | + (unsigned long long)rec->objectid); | |
3201 | + } | |
3202 | + | |
3203 | + error = 0; | |
3204 | + if (rec->found_ref > 0 && !rec->found_root_item) | |
3205 | + error = 1; | |
3206 | + list_for_each_entry(backref, &rec->backrefs, list) { | |
3207 | + if (!backref->found_dir_item) | |
3208 | + backref->errors |= REF_ERR_NO_DIR_ITEM; | |
3209 | + if (!backref->found_dir_index) | |
3210 | + backref->errors |= REF_ERR_NO_DIR_INDEX; | |
3211 | + if (!backref->found_back_ref) | |
3212 | + backref->errors |= REF_ERR_NO_ROOT_BACKREF; | |
3213 | + if (!backref->found_forward_ref) | |
3214 | + backref->errors |= REF_ERR_NO_ROOT_REF; | |
3215 | + if (backref->reachable && backref->errors) | |
3216 | + error = 1; | |
3217 | + } | |
3218 | + if (!error) | |
3219 | + continue; | |
3220 | + | |
3221 | + errors++; | |
3222 | + fprintf(stderr, "fs tree %llu refs %u %s\n", | |
3223 | + (unsigned long long)rec->objectid, rec->found_ref, | |
3224 | + rec->found_root_item ? "" : "not found"); | |
3225 | + | |
3226 | + list_for_each_entry(backref, &rec->backrefs, list) { | |
3227 | + if (!backref->reachable) | |
3228 | + continue; | |
3229 | + if (!backref->errors && rec->found_root_item) | |
3230 | + continue; | |
3231 | + fprintf(stderr, "\tunresolved ref root %llu dir %llu" | |
3232 | + " index %llu namelen %u name %s error %x\n", | |
3233 | + (unsigned long long)backref->ref_root, | |
3234 | + (unsigned long long)backref->dir, | |
3235 | + (unsigned long long)backref->index, | |
3236 | + backref->namelen, backref->name, | |
3237 | + backref->errors); | |
3238 | + } | |
3239 | + } | |
3240 | + return errors > 0 ? 1 : 0; | |
3241 | +} | |
3242 | + | |
3243 | +static int process_root_ref(struct extent_buffer *eb, int slot, | |
3244 | + struct btrfs_key *key, | |
3245 | + struct cache_tree *root_cache) | |
3246 | +{ | |
3247 | + u64 dirid; | |
3248 | + u64 index; | |
3249 | + u32 len; | |
3250 | + u32 name_len; | |
3251 | + struct btrfs_root_ref *ref; | |
3252 | + char namebuf[BTRFS_NAME_LEN]; | |
3253 | + int error; | |
3254 | + | |
3255 | + ref = btrfs_item_ptr(eb, slot, struct btrfs_root_ref); | |
3256 | + | |
3257 | + dirid = btrfs_root_ref_dirid(eb, ref); | |
3258 | + index = btrfs_root_ref_sequence(eb, ref); | |
3259 | + name_len = btrfs_root_ref_name_len(eb, ref); | |
3260 | + | |
3261 | + if (name_len <= BTRFS_NAME_LEN) { | |
3262 | + len = name_len; | |
3263 | + error = 0; | |
3264 | + } else { | |
3265 | + len = BTRFS_NAME_LEN; | |
3266 | + error = REF_ERR_NAME_TOO_LONG; | |
3267 | + } | |
3268 | + read_extent_buffer(eb, namebuf, (unsigned long)(ref + 1), len); | |
3269 | + | |
3270 | + if (key->type == BTRFS_ROOT_REF_KEY) { | |
3271 | + add_root_backref(root_cache, key->offset, key->objectid, dirid, | |
3272 | + index, namebuf, len, key->type, error); | |
3273 | + } else { | |
3274 | + add_root_backref(root_cache, key->objectid, key->offset, dirid, | |
3275 | + index, namebuf, len, key->type, error); | |
3276 | + } | |
3277 | + return 0; | |
3278 | +} | |
3279 | + | |
3280 | static int check_fs_root(struct btrfs_root *root, | |
3281 | + struct cache_tree *root_cache, | |
3282 | struct walk_control *wc) | |
3283 | { | |
3284 | int ret = 0; | |
3285 | @@ -1219,10 +1579,18 @@ static int check_fs_root(struct btrfs_root *root, | |
3286 | int level; | |
3287 | struct btrfs_path path; | |
3288 | struct shared_node root_node; | |
3289 | + struct root_record *rec; | |
3290 | struct btrfs_root_item *root_item = &root->root_item; | |
3291 | ||
3292 | + if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) { | |
3293 | + rec = get_root_rec(root_cache, root->root_key.objectid); | |
3294 | + if (btrfs_root_refs(root_item) > 0) | |
3295 | + rec->found_root_item = 1; | |
3296 | + } | |
3297 | + | |
3298 | btrfs_init_path(&path); | |
3299 | memset(&root_node, 0, sizeof(root_node)); | |
3300 | + cache_tree_init(&root_node.root_cache); | |
3301 | cache_tree_init(&root_node.inode_cache); | |
3302 | ||
3303 | level = btrfs_header_level(root->node); | |
3304 | @@ -1266,6 +1634,8 @@ static int check_fs_root(struct btrfs_root *root, | |
3305 | } | |
3306 | btrfs_release_path(root, &path); | |
3307 | ||
3308 | + merge_root_recs(root, &root_node.root_cache, root_cache); | |
3309 | + | |
3310 | if (root_node.current) { | |
3311 | root_node.current->checked = 1; | |
3312 | maybe_free_inode_rec(&root_node.inode_cache, | |
3313 | @@ -1280,13 +1650,15 @@ static int fs_root_objectid(u64 objectid) | |
3314 | { | |
3315 | if (objectid == BTRFS_FS_TREE_OBJECTID || | |
3316 | objectid == BTRFS_TREE_RELOC_OBJECTID || | |
3317 | + objectid == BTRFS_DATA_RELOC_TREE_OBJECTID || | |
3318 | (objectid >= BTRFS_FIRST_FREE_OBJECTID && | |
3319 | - objectid < BTRFS_LAST_FREE_OBJECTID)) | |
3320 | + objectid <= BTRFS_LAST_FREE_OBJECTID)) | |
3321 | return 1; | |
3322 | return 0; | |
3323 | } | |
3324 | ||
3325 | -static int check_fs_roots(struct btrfs_root *root) | |
3326 | +static int check_fs_roots(struct btrfs_root *root, | |
3327 | + struct cache_tree *root_cache) | |
3328 | { | |
3329 | struct btrfs_path path; | |
3330 | struct btrfs_key key; | |
3331 | @@ -1319,10 +1691,14 @@ static int check_fs_roots(struct btrfs_root *root) | |
3332 | fs_root_objectid(key.objectid)) { | |
3333 | tmp_root = btrfs_read_fs_root_no_cache(root->fs_info, | |
3334 | &key); | |
3335 | - ret = check_fs_root(tmp_root, &wc); | |
3336 | + ret = check_fs_root(tmp_root, root_cache, &wc); | |
3337 | if (ret) | |
3338 | err = 1; | |
3339 | btrfs_free_fs_root(root->fs_info, tmp_root); | |
3340 | + } else if (key.type == BTRFS_ROOT_REF_KEY || | |
3341 | + key.type == BTRFS_ROOT_BACKREF_KEY) { | |
3342 | + process_root_ref(leaf, path.slots[0], &key, | |
3343 | + root_cache); | |
3344 | } | |
3345 | path.slots[0]++; | |
3346 | } | |
3347 | @@ -1895,7 +2271,6 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, | |
3348 | return 0; | |
3349 | } | |
3350 | ||
3351 | - | |
3352 | static int add_pending(struct cache_tree *pending, | |
3353 | struct cache_tree *seen, u64 bytenr, u32 size) | |
3354 | { | |
3355 | @@ -2443,14 +2818,45 @@ static void print_usage(void) | |
3356 | ||
3357 | int main(int ac, char **av) | |
3358 | { | |
3359 | + struct cache_tree root_cache; | |
3360 | struct btrfs_root *root; | |
3361 | + u64 bytenr = 0; | |
3362 | int ret; | |
3363 | + int num; | |
3364 | ||
3365 | - if (ac < 2) | |
3366 | + while(1) { | |
3367 | + int c; | |
3368 | + c = getopt(ac, av, "s:"); | |
3369 | + if (c < 0) | |
3370 | + break; | |
3371 | + switch(c) { | |
3372 | + case 's': | |
3373 | + num = atol(optarg); | |
3374 | + bytenr = btrfs_sb_offset(num); | |
3375 | + printf("using SB copy %d, bytenr %llu\n", num, | |
3376 | + (unsigned long long)bytenr); | |
3377 | + break; | |
3378 | + default: | |
3379 | + print_usage(); | |
3380 | + } | |
3381 | + } | |
3382 | + ac = ac - optind; | |
3383 | + | |
3384 | + if (ac != 1) | |
3385 | print_usage(); | |
3386 | ||
3387 | radix_tree_init(); | |
3388 | - root = open_ctree(av[1], 0, 0); | |
3389 | + cache_tree_init(&root_cache); | |
3390 | + | |
3391 | + if((ret = check_mounted(av[optind])) < 0) { | |
3392 | + fprintf(stderr, "Could not check mount status: %s\n", strerror(ret)); | |
3393 | + return ret; | |
3394 | + } else if(ret) { | |
3395 | + fprintf(stderr, "%s is currently mounted. Aborting.\n", av[optind]); | |
3396 | + return -EBUSY; | |
3397 | + } | |
3398 | + | |
3399 | + root = open_ctree(av[optind], bytenr, 0); | |
3400 | ||
3401 | if (root == NULL) | |
3402 | return 1; | |
3403 | @@ -2458,10 +2864,15 @@ int main(int ac, char **av) | |
3404 | ret = check_extents(root); | |
3405 | if (ret) | |
3406 | goto out; | |
3407 | - ret = check_fs_roots(root); | |
3408 | + ret = check_fs_roots(root, &root_cache); | |
3409 | + if (ret) | |
3410 | + goto out; | |
3411 | ||
3412 | + ret = check_root_refs(root, &root_cache); | |
3413 | out: | |
3414 | + free_root_recs(&root_cache); | |
3415 | close_ctree(root); | |
3416 | + | |
3417 | if (found_old_backref) { | |
3418 | /* | |
3419 | * there was a disk format change when mixed | |
3420 | diff --git a/btrfsctl.c b/btrfsctl.c | |
3421 | index b323818..92bdf39 100644 | |
3422 | --- a/btrfsctl.c | |
3423 | +++ b/btrfsctl.c | |
3424 | @@ -29,6 +29,7 @@ | |
3425 | #include <unistd.h> | |
3426 | #include <dirent.h> | |
3427 | #include <libgen.h> | |
3428 | +#include <stdlib.h> | |
3429 | #include "kerncompat.h" | |
3430 | #include "ctree.h" | |
3431 | #include "transaction.h" | |
3432 | @@ -46,7 +47,7 @@ static inline int ioctl(int fd, int define, void *arg) { return 0; } | |
3433 | static void print_usage(void) | |
3434 | { | |
3435 | printf("usage: btrfsctl [ -d file|dir] [ -s snap_name subvol|tree ]\n"); | |
3436 | - printf(" [-r size] [-A device] [-a] [-c]\n"); | |
3437 | + printf(" [-r size] [-A device] [-a] [-c] [-D dir .]\n"); | |
3438 | printf("\t-d filename: defragments one file\n"); | |
3439 | printf("\t-d directory: defragments the entire Btree\n"); | |
3440 | printf("\t-s snap_name dir: creates a new snapshot of dir\n"); | |
3441 | @@ -55,6 +56,9 @@ static void print_usage(void) | |
3442 | printf("\t-A device: scans the device file for a Btrfs filesystem\n"); | |
3443 | printf("\t-a: scans all devices for Btrfs filesystems\n"); | |
3444 | printf("\t-c: forces a single FS sync\n"); | |
3445 | + printf("\t-D: delete snapshot\n"); | |
3446 | + printf("\t-m [tree id] directory: set the default mounted subvolume" | |
3447 | + " to the [tree id] or the directory\n"); | |
3448 | printf("%s\n", BTRFS_BUILD_VERSION); | |
3449 | exit(1); | |
3450 | } | |
3451 | @@ -99,7 +103,9 @@ int main(int ac, char **av) | |
3452 | int i; | |
3453 | unsigned long command = 0; | |
3454 | int len; | |
3455 | + char *pos; | |
3456 | char *fullpath; | |
3457 | + u64 objectid = 0; | |
3458 | ||
3459 | if (ac == 2 && strcmp(av[1], "-a") == 0) { | |
3460 | fprintf(stderr, "Scanning for Btrfs filesystems\n"); | |
3461 | @@ -158,6 +164,28 @@ int main(int ac, char **av) | |
3462 | print_usage(); | |
3463 | } | |
3464 | command = BTRFS_IOC_DEFRAG; | |
3465 | + } else if (strcmp(av[i], "-D") == 0) { | |
3466 | + if (i >= ac - 1) { | |
3467 | + fprintf(stderr, "-D requires an arg\n"); | |
3468 | + print_usage(); | |
3469 | + } | |
3470 | + command = BTRFS_IOC_SNAP_DESTROY; | |
3471 | + name = av[i + 1]; | |
3472 | + len = strlen(name); | |
3473 | + pos = strchr(name, '/'); | |
3474 | + if (pos) { | |
3475 | + if (*(pos + 1) == '\0') | |
3476 | + *(pos) = '\0'; | |
3477 | + else { | |
3478 | + fprintf(stderr, | |
3479 | + "error: / not allowed in names\n"); | |
3480 | + exit(1); | |
3481 | + } | |
3482 | + } | |
3483 | + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { | |
3484 | + fprintf(stderr, "-D size too long\n"); | |
3485 | + exit(1); | |
3486 | + } | |
3487 | } else if (strcmp(av[i], "-A") == 0) { | |
3488 | if (i >= ac - 1) { | |
3489 | fprintf(stderr, "-A requires an arg\n"); | |
3490 | @@ -178,6 +206,16 @@ int main(int ac, char **av) | |
3491 | command = BTRFS_IOC_RESIZE; | |
3492 | } else if (strcmp(av[i], "-c") == 0) { | |
3493 | command = BTRFS_IOC_SYNC; | |
3494 | + } else if (strcmp(av[i], "-m") == 0) { | |
3495 | + command = BTRFS_IOC_DEFAULT_SUBVOL; | |
3496 | + if (i == ac - 3) { | |
3497 | + objectid = (unsigned long long) | |
3498 | + strtoll(av[i + 1], NULL, 0); | |
3499 | + if (errno == ERANGE) { | |
3500 | + fprintf(stderr, "invalid tree id\n"); | |
3501 | + exit(1); | |
3502 | + } | |
3503 | + } | |
3504 | } | |
3505 | } | |
3506 | if (command == 0) { | |
3507 | @@ -206,6 +244,9 @@ int main(int ac, char **av) | |
3508 | if (command == BTRFS_IOC_SNAP_CREATE) { | |
3509 | args.fd = fd; | |
3510 | ret = ioctl(snap_fd, command, &args); | |
3511 | + } else if (command == BTRFS_IOC_DEFAULT_SUBVOL) { | |
3512 | + printf("objectid is %llu\n", objectid); | |
3513 | + ret = ioctl(fd, command, &objectid); | |
3514 | } else | |
3515 | ret = ioctl(fd, command, &args); | |
3516 | if (ret < 0) { | |
3517 | @@ -219,8 +260,8 @@ int main(int ac, char **av) | |
3518 | } | |
3519 | printf("%s\n", BTRFS_BUILD_VERSION); | |
3520 | if (ret) | |
3521 | - exit(0); | |
3522 | - else | |
3523 | exit(1); | |
3524 | + | |
3525 | + return 0; | |
3526 | } | |
3527 | ||
3528 | diff --git a/convert.c b/convert.c | |
3529 | index d2c9efa..d037c98 100644 | |
3530 | --- a/convert.c | |
3531 | +++ b/convert.c | |
3532 | @@ -370,7 +370,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans, | |
3533 | struct btrfs_extent_item *ei; | |
3534 | u32 blocksize = root->sectorsize; | |
3535 | u64 nbytes; | |
3536 | - u64 bytes_used; | |
3537 | ||
3538 | if (disk_bytenr == 0) { | |
3539 | ret = btrfs_insert_file_extent(trans, root, objectid, | |
3540 | @@ -432,9 +431,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans, | |
3541 | nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes; | |
3542 | btrfs_set_stack_inode_nbytes(inode, nbytes); | |
3543 | ||
3544 | - bytes_used = btrfs_root_used(&root->root_item); | |
3545 | - btrfs_set_root_used(&root->root_item, bytes_used + num_bytes); | |
3546 | - | |
3547 | btrfs_release_path(root, &path); | |
3548 | ||
3549 | ins_key.objectid = disk_bytenr; | |
3550 | @@ -454,9 +450,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans, | |
3551 | ||
3552 | btrfs_mark_buffer_dirty(leaf); | |
3553 | ||
3554 | - bytes_used = btrfs_super_bytes_used(&info->super_copy); | |
3555 | - btrfs_set_super_bytes_used(&info->super_copy, bytes_used + | |
3556 | - num_bytes); | |
3557 | ret = btrfs_update_block_group(trans, root, disk_bytenr, | |
3558 | num_bytes, 1, 0); | |
3559 | if (ret) | |
3560 | diff --git a/ctree.h b/ctree.h | |
3561 | index a9062ea..b79e238 100644 | |
3562 | --- a/ctree.h | |
3563 | +++ b/ctree.h | |
3564 | @@ -350,11 +350,13 @@ struct btrfs_super_block { | |
3565 | * ones specified below then we will fail to mount | |
3566 | */ | |
3567 | #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0) | |
3568 | +#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (2ULL << 0) | |
3569 | ||
3570 | #define BTRFS_FEATURE_COMPAT_SUPP 0ULL | |
3571 | #define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL | |
3572 | #define BTRFS_FEATURE_INCOMPAT_SUPP \ | |
3573 | - BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | |
3574 | + (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ | |
3575 | + BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL) | |
3576 | ||
3577 | /* | |
3578 | * A leaf is full of items. offset and size tell us where to find | |
3579 | @@ -1047,6 +1049,7 @@ BTRFS_SETGET_STACK_FUNCS(block_group_flags, | |
3580 | ||
3581 | /* struct btrfs_inode_ref */ | |
3582 | BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16); | |
3583 | +BTRFS_SETGET_STACK_FUNCS(stack_inode_ref_name_len, struct btrfs_inode_ref, name_len, 16); | |
3584 | BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64); | |
3585 | ||
3586 | /* struct btrfs_inode_item */ | |
3587 | @@ -1325,6 +1328,10 @@ BTRFS_SETGET_FUNCS(root_ref_dirid, struct btrfs_root_ref, dirid, 64); | |
3588 | BTRFS_SETGET_FUNCS(root_ref_sequence, struct btrfs_root_ref, sequence, 64); | |
3589 | BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16); | |
3590 | ||
3591 | +BTRFS_SETGET_STACK_FUNCS(stack_root_ref_dirid, struct btrfs_root_ref, dirid, 64); | |
3592 | +BTRFS_SETGET_STACK_FUNCS(stack_root_ref_sequence, struct btrfs_root_ref, sequence, 64); | |
3593 | +BTRFS_SETGET_STACK_FUNCS(stack_root_ref_name_len, struct btrfs_root_ref, name_len, 16); | |
3594 | + | |
3595 | /* struct btrfs_dir_item */ | |
3596 | BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16); | |
3597 | BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8); | |
3598 | @@ -1572,6 +1579,7 @@ static inline unsigned long btrfs_leaf_data(struct extent_buffer *l) | |
3599 | ||
3600 | /* struct btrfs_file_extent_item */ | |
3601 | BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8); | |
3602 | +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_type, struct btrfs_file_extent_item, type, 8); | |
3603 | ||
3604 | static inline unsigned long btrfs_file_extent_inline_start(struct | |
3605 | btrfs_file_extent_item *e) | |
3606 | @@ -1588,18 +1596,30 @@ static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize) | |
3607 | ||
3608 | BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item, | |
3609 | disk_bytenr, 64); | |
3610 | +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_bytenr, struct btrfs_file_extent_item, | |
3611 | + disk_bytenr, 64); | |
3612 | BTRFS_SETGET_FUNCS(file_extent_generation, struct btrfs_file_extent_item, | |
3613 | generation, 64); | |
3614 | +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_generation, struct btrfs_file_extent_item, | |
3615 | + generation, 64); | |
3616 | BTRFS_SETGET_FUNCS(file_extent_disk_num_bytes, struct btrfs_file_extent_item, | |
3617 | disk_num_bytes, 64); | |
3618 | BTRFS_SETGET_FUNCS(file_extent_offset, struct btrfs_file_extent_item, | |
3619 | offset, 64); | |
3620 | +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_offset, struct btrfs_file_extent_item, | |
3621 | + offset, 64); | |
3622 | BTRFS_SETGET_FUNCS(file_extent_num_bytes, struct btrfs_file_extent_item, | |
3623 | num_bytes, 64); | |
3624 | +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_num_bytes, struct btrfs_file_extent_item, | |
3625 | + num_bytes, 64); | |
3626 | BTRFS_SETGET_FUNCS(file_extent_ram_bytes, struct btrfs_file_extent_item, | |
3627 | ram_bytes, 64); | |
3628 | +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_ram_bytes, struct btrfs_file_extent_item, | |
3629 | + ram_bytes, 64); | |
3630 | BTRFS_SETGET_FUNCS(file_extent_compression, struct btrfs_file_extent_item, | |
3631 | compression, 8); | |
3632 | +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression, struct btrfs_file_extent_item, | |
3633 | + compression, 8); | |
3634 | BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item, | |
3635 | encryption, 8); | |
3636 | BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item, | |
3637 | diff --git a/debug-tree.c b/debug-tree.c | |
3638 | index 1d47519..0525354 100644 | |
3639 | --- a/debug-tree.c | |
3640 | +++ b/debug-tree.c | |
3641 | @@ -116,19 +116,27 @@ int main(int ac, char **av) | |
3642 | int ret; | |
3643 | int slot; | |
3644 | int extent_only = 0; | |
3645 | + int device_only = 0; | |
3646 | + u64 block_only = 0; | |
3647 | struct btrfs_root *tree_root_scan; | |
3648 | ||
3649 | radix_tree_init(); | |
3650 | ||
3651 | while(1) { | |
3652 | int c; | |
3653 | - c = getopt(ac, av, "e"); | |
3654 | + c = getopt(ac, av, "deb:"); | |
3655 | if (c < 0) | |
3656 | break; | |
3657 | switch(c) { | |
3658 | case 'e': | |
3659 | extent_only = 1; | |
3660 | break; | |
3661 | + case 'd': | |
3662 | + device_only = 1; | |
3663 | + break; | |
3664 | + case 'b': | |
3665 | + block_only = atoll(optarg); | |
3666 | + break; | |
3667 | default: | |
3668 | print_usage(); | |
3669 | } | |
3670 | @@ -142,14 +150,37 @@ int main(int ac, char **av) | |
3671 | fprintf(stderr, "unable to open %s\n", av[optind]); | |
3672 | exit(1); | |
3673 | } | |
3674 | + if (block_only) { | |
3675 | + leaf = read_tree_block(root, | |
3676 | + block_only, | |
3677 | + root->leafsize, 0); | |
3678 | + | |
3679 | + if (leaf && btrfs_header_level(leaf) != 0) { | |
3680 | + free_extent_buffer(leaf); | |
3681 | + leaf = NULL; | |
3682 | + } | |
3683 | + | |
3684 | + if (!leaf) { | |
3685 | + leaf = read_tree_block(root, | |
3686 | + block_only, | |
3687 | + root->nodesize, 0); | |
3688 | + } | |
3689 | + if (!leaf) { | |
3690 | + fprintf(stderr, "failed to read %llu\n", block_only); | |
3691 | + return 0; | |
3692 | + } | |
3693 | + btrfs_print_tree(root, leaf, 0); | |
3694 | + return 0; | |
3695 | + } | |
3696 | + | |
3697 | if (!extent_only) { | |
3698 | printf("root tree\n"); | |
3699 | btrfs_print_tree(root->fs_info->tree_root, | |
3700 | - root->fs_info->tree_root->node); | |
3701 | + root->fs_info->tree_root->node, 1); | |
3702 | ||
3703 | printf("chunk tree\n"); | |
3704 | btrfs_print_tree(root->fs_info->chunk_root, | |
3705 | - root->fs_info->chunk_root->node); | |
3706 | + root->fs_info->chunk_root->node, 1); | |
3707 | } | |
3708 | tree_root_scan = root->fs_info->tree_root; | |
3709 | ||
3710 | @@ -175,7 +206,7 @@ again: | |
3711 | if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { | |
3712 | unsigned long offset; | |
3713 | struct extent_buffer *buf; | |
3714 | - int skip = extent_only; | |
3715 | + int skip = extent_only | device_only; | |
3716 | ||
3717 | offset = btrfs_item_ptr_offset(leaf, slot); | |
3718 | read_extent_buffer(leaf, &ri, offset, sizeof(ri)); | |
3719 | @@ -188,8 +219,9 @@ again: | |
3720 | printf("root"); | |
3721 | break; | |
3722 | case BTRFS_EXTENT_TREE_OBJECTID: | |
3723 | - skip = 0; | |
3724 | - if (!extent_only) | |
3725 | + if (!device_only) | |
3726 | + skip = 0; | |
3727 | + if (!extent_only && !device_only) | |
3728 | printf("extent"); | |
3729 | break; | |
3730 | case BTRFS_CHUNK_TREE_OBJECTID: | |
3731 | @@ -198,9 +230,8 @@ again: | |
3732 | } | |
3733 | break; | |
3734 | case BTRFS_DEV_TREE_OBJECTID: | |
3735 | - if (!skip) { | |
3736 | - printf("device"); | |
3737 | - } | |
3738 | + skip = 0; | |
3739 | + printf("device"); | |
3740 | break; | |
3741 | case BTRFS_FS_TREE_OBJECTID: | |
3742 | if (!skip) { | |
3743 | @@ -208,9 +239,8 @@ again: | |
3744 | } | |
3745 | break; | |
3746 | case BTRFS_ROOT_TREE_DIR_OBJECTID: | |
3747 | - if (!skip) { | |
3748 | - printf("directory"); | |
3749 | - } | |
3750 | + skip = 0; | |
3751 | + printf("directory"); | |
3752 | break; | |
3753 | case BTRFS_CSUM_TREE_OBJECTID: | |
3754 | if (!skip) { | |
3755 | @@ -256,13 +286,13 @@ again: | |
3756 | printf("file"); | |
3757 | } | |
3758 | } | |
3759 | - if (!skip && !extent_only) { | |
3760 | + if (extent_only && !skip) { | |
3761 | + print_extents(tree_root_scan, buf); | |
3762 | + } else if (!skip) { | |
3763 | printf(" tree "); | |
3764 | btrfs_print_key(&disk_key); | |
3765 | printf(" \n"); | |
3766 | - btrfs_print_tree(tree_root_scan, buf); | |
3767 | - } else if (extent_only && !skip) { | |
3768 | - print_extents(tree_root_scan, buf); | |
3769 | + btrfs_print_tree(tree_root_scan, buf, 1); | |
3770 | } | |
3771 | } | |
3772 | path.slots[0]++; | |
3773 | @@ -275,7 +305,7 @@ again: | |
3774 | goto again; | |
3775 | } | |
3776 | ||
3777 | - if (extent_only) | |
3778 | + if (extent_only || device_only) | |
3779 | return 0; | |
3780 | ||
3781 | printf("total bytes %llu\n", | |
3782 | diff --git a/dir-test.c b/dir-test.c | |
3783 | index 44f2758..3ae9c68 100644 | |
3784 | --- a/dir-test.c | |
3785 | +++ b/dir-test.c | |
3786 | @@ -485,7 +485,7 @@ int main(int ac, char **av) | |
3787 | if (ret) { | |
3788 | fprintf(stderr, "op %d failed %d:%d\n", | |
3789 | op, i, iterations); | |
3790 | - btrfs_print_tree(root, root->node); | |
3791 | + btrfs_print_tree(root, root->node, 1); | |
3792 | fprintf(stderr, "op %d failed %d:%d\n", | |
3793 | op, i, iterations); | |
3794 | err = ret; | |
3795 | diff --git a/disk-io.c b/disk-io.c | |
3796 | index addebe1..a6e1000 100644 | |
3797 | --- a/disk-io.c | |
3798 | +++ b/disk-io.c | |
3799 | @@ -86,7 +86,7 @@ int csum_tree_block_size(struct extent_buffer *buf, u16 csum_size, | |
3800 | if (memcmp_extent_buffer(buf, result, 0, csum_size)) { | |
3801 | printk("checksum verify failed on %llu wanted %X " | |
3802 | "found %X\n", (unsigned long long)buf->start, | |
3803 | - *((int *)result), *((int *)buf)); | |
3804 | + *((int *)result), *((char *)buf->data)); | |
3805 | free(result); | |
3806 | return 1; | |
3807 | } | |
3808 | @@ -970,13 +970,13 @@ int close_ctree(struct btrfs_root *root) | |
3809 | if (fs_info->csum_root->node) | |
3810 | free_extent_buffer(fs_info->csum_root->node); | |
3811 | ||
3812 | - if (root->fs_info->log_root_tree) { | |
3813 | - if (root->fs_info->log_root_tree->node) | |
3814 | - free_extent_buffer(root->fs_info->log_root_tree->node); | |
3815 | - free(root->fs_info->log_root_tree); | |
3816 | + if (fs_info->log_root_tree) { | |
3817 | + if (fs_info->log_root_tree->node) | |
3818 | + free_extent_buffer(fs_info->log_root_tree->node); | |
3819 | + free(fs_info->log_root_tree); | |
3820 | } | |
3821 | ||
3822 | - close_all_devices(root->fs_info); | |
3823 | + close_all_devices(fs_info); | |
3824 | extent_io_tree_cleanup(&fs_info->extent_cache); | |
3825 | extent_io_tree_cleanup(&fs_info->free_space_cache); | |
3826 | extent_io_tree_cleanup(&fs_info->block_group_cache); | |
3827 | diff --git a/ioctl-test.c b/ioctl-test.c | |
3828 | new file mode 100644 | |
3829 | index 0000000..7cf3bc2 | |
3830 | --- /dev/null | |
3831 | +++ b/ioctl-test.c | |
3832 | @@ -0,0 +1,36 @@ | |
3833 | +#include <stdio.h> | |
3834 | +#include <stdlib.h> | |
3835 | +#include "kerncompat.h" | |
3836 | +#include "ioctl.h" | |
3837 | + | |
3838 | +unsigned long ioctls[] = { | |
3839 | + BTRFS_IOC_SNAP_CREATE, | |
3840 | + BTRFS_IOC_DEFRAG, | |
3841 | + BTRFS_IOC_RESIZE, | |
3842 | + BTRFS_IOC_SCAN_DEV, | |
3843 | + BTRFS_IOC_TRANS_START, | |
3844 | + BTRFS_IOC_TRANS_END, | |
3845 | + BTRFS_IOC_SYNC, | |
3846 | + BTRFS_IOC_CLONE, | |
3847 | + BTRFS_IOC_ADD_DEV, | |
3848 | + BTRFS_IOC_RM_DEV, | |
3849 | + BTRFS_IOC_BALANCE, | |
3850 | + BTRFS_IOC_SUBVOL_CREATE, | |
3851 | + BTRFS_IOC_SNAP_DESTROY, | |
3852 | + BTRFS_IOC_DEFRAG_RANGE, | |
3853 | + BTRFS_IOC_TREE_SEARCH, | |
3854 | + BTRFS_IOC_INO_LOOKUP, | |
3855 | + BTRFS_IOC_DEFAULT_SUBVOL, | |
3856 | + BTRFS_IOC_SPACE_INFO, | |
3857 | + 0 }; | |
3858 | + | |
3859 | +int main(int ac, char **av) | |
3860 | +{ | |
3861 | + int i = 0; | |
3862 | + while(ioctls[i]) { | |
3863 | + printf("%lu\n" ,ioctls[i]); | |
3864 | + i++; | |
3865 | + } | |
3866 | + return 0; | |
3867 | +} | |
3868 | + | |
3869 | diff --git a/ioctl.h b/ioctl.h | |
3870 | index a084f33..776d7a9 100644 | |
3871 | --- a/ioctl.h | |
3872 | +++ b/ioctl.h | |
3873 | @@ -30,6 +30,108 @@ struct btrfs_ioctl_vol_args { | |
3874 | char name[BTRFS_PATH_NAME_MAX + 1]; | |
3875 | }; | |
3876 | ||
3877 | +struct btrfs_ioctl_search_key { | |
3878 | + /* which root are we searching. 0 is the tree of tree roots */ | |
3879 | + __u64 tree_id; | |
3880 | + | |
3881 | + /* keys returned will be >= min and <= max */ | |
3882 | + __u64 min_objectid; | |
3883 | + __u64 max_objectid; | |
3884 | + | |
3885 | + /* keys returned will be >= min and <= max */ | |
3886 | + __u64 min_offset; | |
3887 | + __u64 max_offset; | |
3888 | + | |
3889 | + /* max and min transids to search for */ | |
3890 | + __u64 min_transid; | |
3891 | + __u64 max_transid; | |
3892 | + | |
3893 | + /* keys returned will be >= min and <= max */ | |
3894 | + __u32 min_type; | |
3895 | + __u32 max_type; | |
3896 | + | |
3897 | + /* | |
3898 | + * how many items did userland ask for, and how many are we | |
3899 | + * returning | |
3900 | + */ | |
3901 | + __u32 nr_items; | |
3902 | + | |
3903 | + /* align to 64 bits */ | |
3904 | + __u32 unused; | |
3905 | + | |
3906 | + /* some extra for later */ | |
3907 | + __u64 unused1; | |
3908 | + __u64 unused2; | |
3909 | + __u64 unused3; | |
3910 | + __u64 unused4; | |
3911 | +}; | |
3912 | + | |
3913 | +struct btrfs_ioctl_search_header { | |
3914 | + __u64 transid; | |
3915 | + __u64 objectid; | |
3916 | + __u64 offset; | |
3917 | + __u32 type; | |
3918 | + __u32 len; | |
3919 | +} __attribute__((may_alias)); | |
3920 | + | |
3921 | +#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) | |
3922 | +/* | |
3923 | + * the buf is an array of search headers where | |
3924 | + * each header is followed by the actual item | |
3925 | + * the type field is expanded to 32 bits for alignment | |
3926 | + */ | |
3927 | +struct btrfs_ioctl_search_args { | |
3928 | + struct btrfs_ioctl_search_key key; | |
3929 | + char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; | |
3930 | +}; | |
3931 | + | |
3932 | +#define BTRFS_INO_LOOKUP_PATH_MAX 4080 | |
3933 | +struct btrfs_ioctl_ino_lookup_args { | |
3934 | + __u64 treeid; | |
3935 | + __u64 objectid; | |
3936 | + char name[BTRFS_INO_LOOKUP_PATH_MAX]; | |
3937 | +}; | |
3938 | + | |
3939 | +/* flags for the defrag range ioctl */ | |
3940 | +#define BTRFS_DEFRAG_RANGE_COMPRESS 1 | |
3941 | +#define BTRFS_DEFRAG_RANGE_START_IO 2 | |
3942 | + | |
3943 | +struct btrfs_ioctl_defrag_range_args { | |
3944 | + /* start of the defrag operation */ | |
3945 | + __u64 start; | |
3946 | + | |
3947 | + /* number of bytes to defrag, use (u64)-1 to say all */ | |
3948 | + __u64 len; | |
3949 | + | |
3950 | + /* | |
3951 | + * flags for the operation, which can include turning | |
3952 | + * on compression for this one defrag | |
3953 | + */ | |
3954 | + __u64 flags; | |
3955 | + | |
3956 | + /* | |
3957 | + * any extent bigger than this will be considered | |
3958 | + * already defragged. Use 0 to take the kernel default | |
3959 | + * Use 1 to say every single extent must be rewritten | |
3960 | + */ | |
3961 | + __u32 extent_thresh; | |
3962 | + | |
3963 | + /* spare for later */ | |
3964 | + __u32 unused[5]; | |
3965 | +}; | |
3966 | + | |
3967 | +struct btrfs_ioctl_space_info { | |
3968 | + __u64 flags; | |
3969 | + __u64 total_bytes; | |
3970 | + __u64 used_bytes; | |
3971 | +}; | |
3972 | + | |
3973 | +struct btrfs_ioctl_space_args { | |
3974 | + __u64 space_slots; | |
3975 | + __u64 total_spaces; | |
3976 | + struct btrfs_ioctl_space_info spaces[0]; | |
3977 | +}; | |
3978 | + | |
3979 | #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \ | |
3980 | struct btrfs_ioctl_vol_args) | |
3981 | #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ | |
3982 | @@ -56,4 +158,15 @@ struct btrfs_ioctl_vol_args { | |
3983 | /* 13 is for CLONE_RANGE */ | |
3984 | #define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ | |
3985 | struct btrfs_ioctl_vol_args) | |
3986 | +#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ | |
3987 | + struct btrfs_ioctl_vol_args) | |
3988 | +#define BTRFS_IOC_DEFRAG_RANGE _IOW(BTRFS_IOCTL_MAGIC, 16, \ | |
3989 | + struct btrfs_ioctl_defrag_range_args) | |
3990 | +#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ | |
3991 | + struct btrfs_ioctl_search_args) | |
3992 | +#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ | |
3993 | + struct btrfs_ioctl_ino_lookup_args) | |
3994 | +#define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, u64) | |
3995 | +#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \ | |
3996 | + struct btrfs_ioctl_space_args) | |
3997 | #endif | |
3998 | diff --git a/kerncompat.h b/kerncompat.h | |
3999 | index e4c8ce0..46236cd 100644 | |
4000 | --- a/kerncompat.h | |
4001 | +++ b/kerncompat.h | |
4002 | @@ -42,7 +42,11 @@ | |
4003 | #define GFP_NOFS 0 | |
4004 | #define __read_mostly | |
4005 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | |
4006 | + | |
4007 | +#ifndef ULONG_MAX | |
4008 | #define ULONG_MAX (~0UL) | |
4009 | +#endif | |
4010 | + | |
4011 | #define BUG() abort() | |
4012 | #ifdef __CHECKER__ | |
4013 | #define __force __attribute__((force)) | |
4014 | diff --git a/man/Makefile b/man/Makefile | |
4015 | index 4e8893b..4a90b75 100644 | |
4016 | --- a/man/Makefile | |
4017 | +++ b/man/Makefile | |
4018 | @@ -7,13 +7,16 @@ mandir = $(prefix)/man | |
4019 | man8dir = $(mandir)/man8 | |
4020 | ||
4021 | MANPAGES = mkfs.btrfs.8.gz btrfsctl.8.gz btrfsck.8.gz btrfs-image.8.gz \ | |
4022 | - btrfs-show.8.gz | |
4023 | + btrfs-show.8.gz btrfs.8.gz | |
4024 | ||
4025 | all: $(MANPAGES) | |
4026 | ||
4027 | mkfs.btrfs.8.gz: mkfs.btrfs.8.in | |
4028 | $(GZIP) -n -c mkfs.btrfs.8.in > mkfs.btrfs.8.gz | |
4029 | ||
4030 | +btrfs.8.gz: btrfs.8.in | |
4031 | + $(GZIP) -n -c btrfs.8.in > btrfs.8.gz | |
4032 | + | |
4033 | btrfsctl.8.gz: btrfsctl.8.in | |
4034 | $(GZIP) -n -c btrfsctl.8.in > btrfsctl.8.gz | |
4035 | ||
4036 | diff --git a/man/btrfs.8.in b/man/btrfs.8.in | |
4037 | new file mode 100644 | |
4038 | index 0000000..26ef982 | |
4039 | --- /dev/null | |
4040 | +++ b/man/btrfs.8.in | |
4041 | @@ -0,0 +1,170 @@ | |
4042 | +.TH BTRFS 8 "" "btrfs" "btrfs" | |
4043 | +.\" | |
4044 | +.\" Man page written by Goffredo Baroncelli <kreijack@inwind.it> (Feb 2010) | |
4045 | +.\" | |
4046 | +.SH NAME | |
4047 | +btrfs \- control a btrfs filesystem | |
4048 | +.SH SYNOPSIS | |
4049 | +\fBbtrfs\fP \fBsubvolume snapshot\fP\fI <source> [<dest>/]<name>\fP | |
4050 | +.PP | |
4051 | +\fBbtrfs\fP \fBsubvolume delete\fP\fI <subvolume>\fP | |
4052 | +.PP | |
4053 | +\fBbtrfs\fP \fBsubvolume create\fP\fI [<dest>/]<name>\fP | |
4054 | +.PP | |
4055 | +\fBbtrfs\fP \fBsubvolume list\fP\fI <path>\fP | |
4056 | +.PP | |
4057 | +\fBbtrfs\fP \fBsubvolume set-default\fP\fI <id> <path>\fP | |
4058 | +.PP | |
4059 | +\fBbtrfs\fP \fBfilesystem defrag\fP\fI <file>|<dir> [<file>|<dir>...]\fP | |
4060 | +.PP | |
4061 | +\fBbtrfs\fP \fBfilesystem sync\fP\fI <path> \fP | |
4062 | +.PP | |
4063 | +\fBbtrfs\fP \fBfilesystem resize\fP\fI [+/\-]<size>[gkm]|max <filesystem>\fP | |
4064 | +.PP | |
4065 | +\fBbtrfs\fP \fBdevice scan\fP\fI [<device> [<device>..]]\fP | |
4066 | +.PP | |
4067 | +\fBbtrfs\fP \fBdevice show\fP\fI <dev>|<label> [<dev>|<label>...]\fP | |
4068 | +.PP | |
4069 | +\fBbtrfs\fP \fBdevice balance\fP\fI <path> \fP | |
4070 | +.PP | |
4071 | +\fBbtrfs\fP \fBdevice add\fP\fI <dev> [<dev>..] <path> \fP | |
4072 | +.PP | |
4073 | +\fBbtrfs\fP \fBdevice delete\fP\fI <dev> [<dev>..] <path> \fP] | |
4074 | + | |
4075 | +.PP | |
4076 | +\fBbtrfs\fP \fBhelp|\-\-help|\-h \fP\fI\fP | |
4077 | +.PP | |
4078 | +.SH DESCRIPTION | |
4079 | +.B btrfs | |
4080 | +is used to control the filesystem and the files and directories stored. It is | |
4081 | +the tool to create or destroy a snapshot or a subvolume for the | |
4082 | +filesystem, to defrag a file or a directory, flush the data to the disk, | |
4083 | +to resize the filesystem, to scan the device. | |
4084 | + | |
4085 | +It is possible to abbreviate the commands unless the commands are ambiguous. | |
4086 | +For example: it is possible to run | |
4087 | +.I btrfs sub snaps | |
4088 | +instead of | |
4089 | +.I btrfs subvolume snapshot. | |
4090 | +But | |
4091 | +.I btrfs dev s | |
4092 | +is not allowed, because | |
4093 | +.I dev s | |
4094 | +may be interpreted both as | |
4095 | +.I device show | |
4096 | +and as | |
4097 | +.I device scan. | |
4098 | +In this case | |
4099 | +.I btrfs | |
4100 | +returns an error. | |
4101 | + | |
4102 | +If a command is terminated by | |
4103 | +.I --help | |
4104 | +, the relevant help is showed. If the passed command matches more commands, | |
4105 | +the help of all the matched commands are showed. For example | |
4106 | +.I btrfs dev --help | |
4107 | +shows the help of all | |
4108 | +.I device* | |
4109 | +command. | |
4110 | + | |
4111 | +.SH COMMANDS | |
4112 | +.TP | |
4113 | + | |
4114 | +\fBsubvolume snapshot\fR\fI <source> [<dest>/]<name>\fR | |
4115 | +Create a writable snapshot of the subvolume \fI<source>\fR with the name | |
4116 | +\fI<name>\fR in the \fI<dest>\fR directory. If \fI<source>\fR is not a | |
4117 | +subvolume, \fBbtrfs\fR returns an error. | |
4118 | +.TP | |
4119 | + | |
4120 | +\fBsubvolume delete\fR\fI <subvolume>\fR | |
4121 | +Delete the subvolume \fI<subvolume>\fR. If \fI<subvolume>\fR is not a | |
4122 | +subvolume, \fBbtrfs\fR returns an error. | |
4123 | +.TP | |
4124 | + | |
4125 | +\fBsubvolume create\fR\fI [<dest>/]<name>\fR | |
4126 | +Create a subvolume in \fI<dest>\fR (or in the current directory if | |
4127 | +\fI<dest>\fR is omitted). | |
4128 | +.TP | |
4129 | + | |
4130 | +\fBsubvolume list\fR\fI <path>\fR | |
4131 | +List the subvolumes present in the filesystem \fI<path>\fR. For every | |
4132 | +subvolume is showed the subvolume ID (second column), | |
4133 | +the ID of the \fItop level\fR | |
4134 | +subvolume (fifth column), and the path (seventh column) relative to the | |
4135 | +\fItop level\fR subvolume. | |
4136 | +These <ID> may be used by the \fBsubvolume set-default\fR command, or at | |
4137 | +mount time via the \fIsubvol=\fR option. | |
4138 | +.TP | |
4139 | + | |
4140 | +\fBsubvolume set-default\fR\fI <id> <path>\fR | |
4141 | +Set the subvolume of the filesystem \fI<path>\fR which is mounted as | |
4142 | +\fIdefault\fR. The subvolume is identified by \fB<id>\fR, which | |
4143 | +is returned by the \fBsubvolume list\fR command. | |
4144 | +.TP | |
4145 | + | |
4146 | +\fBfilesystem defragment\fP\fI <file>|<dir> [<file>|<dir>...]\fR | |
4147 | +Defragment files and/or directories. | |
4148 | +.TP | |
4149 | + | |
4150 | +\fBdevice scan\fR \fI[<device> [<device>..]]\fR | |
4151 | +Scan devices for a btrfs filesystem. If no devices are passed, \fBbtrfs\fR scans | |
4152 | +all the block devices. | |
4153 | +.TP | |
4154 | + | |
4155 | +\fBfilesystem sync\fR\fI <path> \fR | |
4156 | +Force a sync for the filesystem identified by \fI<path>\fR. | |
4157 | +.TP | |
4158 | + | |
4159 | +.\" | |
4160 | +.\" Some wording are extracted by the resize2fs man page | |
4161 | +.\" | |
4162 | + | |
4163 | +\fBfilesystem resize\fR\fI [+/\-]<size>[gkm]|max <path>\fR | |
4164 | +Resize a filesystem identified by \fI<path>\fR. | |
4165 | +The \fI<size>\fR parameter specifies the new size of the filesystem. | |
4166 | +If the prefix \fI+\fR or \fI\-\fR is present the size is increased or decreased | |
4167 | +by the quantity \fI<size>\fR. | |
4168 | +If no units are specified, the unit of the \fI<size>\fR parameter defaults to | |
4169 | +bytes. Optionally, the size parameter may be suffixed by one of the following | |
4170 | +the units designators: 'K', 'M', or 'G', kilobytes, megabytes, or gigabytes, | |
4171 | +respectively. | |
4172 | + | |
4173 | +If 'max' is passed, the filesystem will occupy all available space on the | |
4174 | +volume(s). | |
4175 | + | |
4176 | +The \fBresize\fR command \fBdoes not\fR manipulate the size of underlying | |
4177 | +partition. If you wish to enlarge/reduce a filesystem, you must make sure you | |
4178 | +can expand the partition before enlarging the filesystem and shrink the | |
4179 | +partition after reducing the size of the filesystem. | |
4180 | +.TP | |
4181 | + | |
4182 | +\fBfilesystem show\fR [<uuid>|<label>]\fR | |
4183 | +Show the btrfs filesystem with some additional info. If no UUID or label is | |
4184 | +passed, \fBbtrfs\fR show info of all the btrfs filesystem. | |
4185 | +.TP | |
4186 | + | |
4187 | +\fBdevice balance\fR \fI<path>\fR | |
4188 | +Balance the chunks of the filesystem identified by \fI<path>\fR | |
4189 | +across the devices. | |
4190 | +.TP | |
4191 | + | |
4192 | +\fBdevice add\fR\fI <dev> [<dev>..] <path>\fR | |
4193 | +Add device(s) to the filesystem identified by \fI<path>\fR. | |
4194 | +.TP | |
4195 | + | |
4196 | +\fBdevice delete\fR\fI <dev> [<dev>..] <path>\fR | |
4197 | +Remove device(s) from a filesystem identified by \fI<path>\fR. | |
4198 | +.PP | |
4199 | + | |
4200 | +.SH EXIT STATUS | |
4201 | +\fBbtrfs\fR returns a zero exist status if it succeeds. Non zero is returned in | |
4202 | +case of failure. | |
4203 | + | |
4204 | +.SH AVAILABILITY | |
4205 | +.B btrfs | |
4206 | +is part of btrfs-progs. Btrfs filesystem is currently under heavy development, | |
4207 | +and not suitable for any uses other than benchmarking and review. | |
4208 | +Please refer to the btrfs wiki http://btrfs.wiki.kernel.org for | |
4209 | +further details. | |
4210 | +.SH SEE ALSO | |
4211 | +.BR mkfs.btrfs (8) | |
4212 | diff --git a/print-tree.c b/print-tree.c | |
4213 | index 59f4358..ac575d5 100644 | |
4214 | --- a/print-tree.c | |
4215 | +++ b/print-tree.c | |
4216 | @@ -413,8 +413,11 @@ static void print_objectid(unsigned long long objectid, u8 type) | |
4217 | printf("MULTIPLE"); | |
4218 | break; | |
4219 | case BTRFS_FIRST_CHUNK_TREE_OBJECTID: | |
4220 | - printf("FIRST_CHUNK_TREE"); | |
4221 | - break; | |
4222 | + if (type == BTRFS_CHUNK_ITEM_KEY) { | |
4223 | + printf("FIRST_CHUNK_TREE"); | |
4224 | + break; | |
4225 | + } | |
4226 | + /* fall-thru */ | |
4227 | default: | |
4228 | printf("%llu", objectid); | |
4229 | } | |
4230 | @@ -607,7 +610,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) | |
4231 | } | |
4232 | } | |
4233 | ||
4234 | -void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb) | |
4235 | +void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow) | |
4236 | { | |
4237 | int i; | |
4238 | u32 nr; | |
4239 | @@ -643,6 +646,9 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb) | |
4240 | (unsigned long long)btrfs_node_ptr_generation(eb, i)); | |
4241 | fflush(stdout); | |
4242 | } | |
4243 | + if (!follow) | |
4244 | + return; | |
4245 | + | |
4246 | for (i = 0; i < nr; i++) { | |
4247 | struct extent_buffer *next = read_tree_block(root, | |
4248 | btrfs_node_blockptr(eb, i), | |
4249 | @@ -660,8 +666,7 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb) | |
4250 | if (btrfs_header_level(next) != | |
4251 | btrfs_header_level(eb) - 1) | |
4252 | BUG(); | |
4253 | - btrfs_print_tree(root, next); | |
4254 | + btrfs_print_tree(root, next, 1); | |
4255 | free_extent_buffer(next); | |
4256 | } | |
4257 | } | |
4258 | - | |
4259 | diff --git a/print-tree.h b/print-tree.h | |
4260 | index 4d1a01a..495b81a 100644 | |
4261 | --- a/print-tree.h | |
4262 | +++ b/print-tree.h | |
4263 | @@ -19,6 +19,6 @@ | |
4264 | #ifndef __PRINT_TREE_ | |
4265 | #define __PRINT_TREE_ | |
4266 | void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l); | |
4267 | -void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *t); | |
4268 | +void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *t, int follow); | |
4269 | void btrfs_print_key(struct btrfs_disk_key *disk_key); | |
4270 | #endif | |
4271 | diff --git a/quick-test.c b/quick-test.c | |
4272 | index 351c706..fa6fd83 100644 | |
4273 | --- a/quick-test.c | |
4274 | +++ b/quick-test.c | |
4275 | @@ -85,7 +85,7 @@ int main(int ac, char **av) { | |
4276 | fprintf(stderr, "search %d:%d\n", num, i); | |
4277 | ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0); | |
4278 | if (ret) { | |
4279 | - btrfs_print_tree(root, root->node); | |
4280 | + btrfs_print_tree(root, root->node, 1); | |
4281 | printf("unable to find %d\n", num); | |
4282 | exit(1); | |
4283 | } | |
4284 | @@ -148,7 +148,7 @@ int main(int ac, char **av) { | |
4285 | fprintf(stderr, "search %d:%d\n", num, i); | |
4286 | ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0); | |
4287 | if (ret) { | |
4288 | - btrfs_print_tree(root, root->node); | |
4289 | + btrfs_print_tree(root, root->node, 1); | |
4290 | printf("unable to find %d\n", num); | |
4291 | exit(1); | |
4292 | } | |
4293 | @@ -196,7 +196,7 @@ int main(int ac, char **av) { | |
4294 | btrfs_commit_transaction(trans, root); | |
4295 | printf("tree size is now %d\n", tree_size); | |
4296 | printf("root %p commit root %p\n", root->node, root->commit_root); | |
4297 | - btrfs_print_tree(root, root->node); | |
4298 | + btrfs_print_tree(root, root->node, 1); | |
4299 | close_ctree(root); | |
4300 | return 0; | |
4301 | } | |
4302 | diff --git a/random-test.c b/random-test.c | |
4303 | index 571735d..0003236 100644 | |
4304 | --- a/random-test.c | |
4305 | +++ b/random-test.c | |
4306 | @@ -404,7 +404,7 @@ int main(int ac, char **av) | |
4307 | if (ret) { | |
4308 | fprintf(stderr, "op %d failed %d:%d\n", | |
4309 | op, i, iterations); | |
4310 | - btrfs_print_tree(root, root->node); | |
4311 | + btrfs_print_tree(root, root->node, 1); | |
4312 | fprintf(stderr, "op %d failed %d:%d\n", | |
4313 | op, i, iterations); | |
4314 | err = ret; | |
4315 | diff --git a/utils.c b/utils.c | |
4316 | index 2f4c6e1..fd894f3 100644 | |
4317 | --- a/utils.c | |
4318 | +++ b/utils.c | |
4319 | @@ -31,6 +31,10 @@ | |
4320 | #include <fcntl.h> | |
4321 | #include <unistd.h> | |
4322 | #include <mntent.h> | |
4323 | +#include <linux/loop.h> | |
4324 | +#include <linux/major.h> | |
4325 | +#include <linux/kdev_t.h> | |
4326 | +#include <limits.h> | |
4327 | #include "kerncompat.h" | |
4328 | #include "radix-tree.h" | |
4329 | #include "ctree.h" | |
4330 | @@ -586,55 +590,224 @@ error: | |
4331 | return ret; | |
4332 | } | |
4333 | ||
4334 | +/* checks if a device is a loop device */ | |
4335 | +int is_loop_device (const char* device) { | |
4336 | + struct stat statbuf; | |
4337 | + | |
4338 | + if(stat(device, &statbuf) < 0) | |
4339 | + return -errno; | |
4340 | + | |
4341 | + return (S_ISBLK(statbuf.st_mode) && | |
4342 | + MAJOR(statbuf.st_rdev) == LOOP_MAJOR); | |
4343 | +} | |
4344 | + | |
4345 | + | |
4346 | +/* Takes a loop device path (e.g. /dev/loop0) and returns | |
4347 | + * the associated file (e.g. /images/my_btrfs.img) */ | |
4348 | +int resolve_loop_device(const char* loop_dev, char* loop_file, int max_len) | |
4349 | +{ | |
4350 | + int loop_fd; | |
4351 | + int ret_ioctl; | |
4352 | + struct loop_info loopinfo; | |
4353 | + | |
4354 | + if ((loop_fd = open(loop_dev, O_RDONLY)) < 0) | |
4355 | + return -errno; | |
4356 | + | |
4357 | + ret_ioctl = ioctl(loop_fd, LOOP_GET_STATUS, &loopinfo); | |
4358 | + close(loop_fd); | |
4359 | + | |
4360 | + if (ret_ioctl == 0) | |
4361 | + strncpy(loop_file, loopinfo.lo_name, max_len); | |
4362 | + else | |
4363 | + return -errno; | |
4364 | + | |
4365 | + return 0; | |
4366 | +} | |
4367 | + | |
4368 | +/* Checks whether a and b are identical or device | |
4369 | + * files associated with the same block device | |
4370 | + */ | |
4371 | +int is_same_blk_file(const char* a, const char* b) | |
4372 | +{ | |
4373 | + struct stat st_buf_a, st_buf_b; | |
4374 | + char real_a[PATH_MAX]; | |
4375 | + char real_b[PATH_MAX]; | |
4376 | + | |
4377 | + if(!realpath(a, real_a) || | |
4378 | + !realpath(b, real_b)) | |
4379 | + { | |
4380 | + return -errno; | |
4381 | + } | |
4382 | + | |
4383 | + /* Identical path? */ | |
4384 | + if(strcmp(real_a, real_b) == 0) | |
4385 | + return 1; | |
4386 | + | |
4387 | + if(stat(a, &st_buf_a) < 0 || | |
4388 | + stat(b, &st_buf_b) < 0) | |
4389 | + { | |
4390 | + return -errno; | |
4391 | + } | |
4392 | + | |
4393 | + /* Same blockdevice? */ | |
4394 | + if(S_ISBLK(st_buf_a.st_mode) && | |
4395 | + S_ISBLK(st_buf_b.st_mode) && | |
4396 | + st_buf_a.st_rdev == st_buf_b.st_rdev) | |
4397 | + { | |
4398 | + return 1; | |
4399 | + } | |
4400 | + | |
4401 | + /* Hardlink? */ | |
4402 | + if (st_buf_a.st_dev == st_buf_b.st_dev && | |
4403 | + st_buf_a.st_ino == st_buf_b.st_ino) | |
4404 | + { | |
4405 | + return 1; | |
4406 | + } | |
4407 | + | |
4408 | + return 0; | |
4409 | +} | |
4410 | + | |
4411 | +/* checks if a and b are identical or device | |
4412 | + * files associated with the same block device or | |
4413 | + * if one file is a loop device that uses the other | |
4414 | + * file. | |
4415 | + */ | |
4416 | +int is_same_loop_file(const char* a, const char* b) | |
4417 | +{ | |
4418 | + char res_a[PATH_MAX]; | |
4419 | + char res_b[PATH_MAX]; | |
4420 | + const char* final_a; | |
4421 | + const char* final_b; | |
4422 | + int ret; | |
4423 | + | |
4424 | + /* Resolve a if it is a loop device */ | |
4425 | + if((ret = is_loop_device(a)) < 0) { | |
4426 | + return ret; | |
4427 | + } else if(ret) { | |
4428 | + if((ret = resolve_loop_device(a, res_a, sizeof(res_a))) < 0) | |
4429 | + return ret; | |
4430 | + | |
4431 | + final_a = res_a; | |
4432 | + } else { | |
4433 | + final_a = a; | |
4434 | + } | |
4435 | + | |
4436 | + /* Resolve b if it is a loop device */ | |
4437 | + if((ret = is_loop_device(b)) < 0) { | |
4438 | + return ret; | |
4439 | + } else if(ret) { | |
4440 | + if((ret = resolve_loop_device(b, res_b, sizeof(res_b))) < 0) | |
4441 | + return ret; | |
4442 | + | |
4443 | + final_b = res_b; | |
4444 | + } else { | |
4445 | + final_b = b; | |
4446 | + } | |
4447 | + | |
4448 | + return is_same_blk_file(final_a, final_b); | |
4449 | +} | |
4450 | + | |
4451 | +/* Checks if a file exists and is a block or regular file*/ | |
4452 | +int is_existing_blk_or_reg_file(const char* filename) | |
4453 | +{ | |
4454 | + struct stat st_buf; | |
4455 | + | |
4456 | + if(stat(filename, &st_buf) < 0) { | |
4457 | + if(errno == ENOENT) | |
4458 | + return 0; | |
4459 | + else | |
4460 | + return -errno; | |
4461 | + } | |
4462 | + | |
4463 | + return (S_ISBLK(st_buf.st_mode) || S_ISREG(st_buf.st_mode)); | |
4464 | +} | |
4465 | + | |
4466 | +/* Checks if a file is used (directly or indirectly via a loop device) | |
4467 | + * by a device in fs_devices | |
4468 | + */ | |
4469 | +int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices, const char* file) | |
4470 | +{ | |
4471 | + int ret; | |
4472 | + struct list_head *head; | |
4473 | + struct list_head *cur; | |
4474 | + struct btrfs_device *device; | |
4475 | + | |
4476 | + head = &fs_devices->devices; | |
4477 | + list_for_each(cur, head) { | |
4478 | + device = list_entry(cur, struct btrfs_device, dev_list); | |
4479 | + | |
4480 | + if((ret = is_same_loop_file(device->name, file))) | |
4481 | + return ret; | |
4482 | + } | |
4483 | + | |
4484 | + return 0; | |
4485 | +} | |
4486 | + | |
4487 | /* | |
4488 | * returns 1 if the device was mounted, < 0 on error or 0 if everything | |
4489 | - * is safe to continue. TODO, this should also scan multi-device filesystems | |
4490 | + * is safe to continue. | |
4491 | */ | |
4492 | -int check_mounted(char *file) | |
4493 | +int check_mounted(const char* file) | |
4494 | { | |
4495 | - struct mntent *mnt; | |
4496 | - struct stat st_buf; | |
4497 | - dev_t file_dev = 0; | |
4498 | - dev_t file_rdev = 0; | |
4499 | - ino_t file_ino = 0; | |
4500 | + int ret; | |
4501 | + int fd; | |
4502 | + u64 total_devs = 1; | |
4503 | + int is_btrfs; | |
4504 | + struct btrfs_fs_devices* fs_devices_mnt = NULL; | |
4505 | FILE *f; | |
4506 | - int ret = 0; | |
4507 | + struct mntent *mnt; | |
4508 | ||
4509 | - if ((f = setmntent ("/proc/mounts", "r")) == NULL) | |
4510 | + fd = open(file, O_RDONLY); | |
4511 | + if (fd < 0) { | |
4512 | + fprintf (stderr, "check_mounted(): Could not open %s\n", file); | |
4513 | return -errno; | |
4514 | + } | |
4515 | ||
4516 | - if (stat(file, &st_buf) < 0) { | |
4517 | - return -errno; | |
4518 | - } else { | |
4519 | - if (S_ISBLK(st_buf.st_mode)) { | |
4520 | - file_rdev = st_buf.st_rdev; | |
4521 | - } else { | |
4522 | - file_dev = st_buf.st_dev; | |
4523 | - file_ino = st_buf.st_ino; | |
4524 | - } | |
4525 | + /* scan the initial device */ | |
4526 | + ret = btrfs_scan_one_device(fd, file, &fs_devices_mnt, | |
4527 | + &total_devs, BTRFS_SUPER_INFO_OFFSET); | |
4528 | + is_btrfs = (ret >= 0); | |
4529 | + close(fd); | |
4530 | + | |
4531 | + /* scan other devices */ | |
4532 | + if (is_btrfs && total_devs > 1) { | |
4533 | + if((ret = btrfs_scan_for_fsid(fs_devices_mnt, total_devs, 1))) | |
4534 | + return ret; | |
4535 | } | |
4536 | ||
4537 | + /* iterate over the list of currently mountes filesystems */ | |
4538 | + if ((f = setmntent ("/proc/mounts", "r")) == NULL) | |
4539 | + return -errno; | |
4540 | + | |
4541 | while ((mnt = getmntent (f)) != NULL) { | |
4542 | - if (strcmp(file, mnt->mnt_fsname) == 0) | |
4543 | - break; | |
4544 | + if(is_btrfs) { | |
4545 | + if(strcmp(mnt->mnt_type, "btrfs") != 0) | |
4546 | + continue; | |
4547 | ||
4548 | - if (stat(mnt->mnt_fsname, &st_buf) == 0) { | |
4549 | - if (S_ISBLK(st_buf.st_mode)) { | |
4550 | - if (file_rdev && (file_rdev == st_buf.st_rdev)) | |
4551 | - break; | |
4552 | - } else if (file_dev && ((file_dev == st_buf.st_dev) && | |
4553 | - (file_ino == st_buf.st_ino))) { | |
4554 | - break; | |
4555 | - } | |
4556 | + ret = blk_file_in_dev_list(fs_devices_mnt, mnt->mnt_fsname); | |
4557 | + } else { | |
4558 | + /* ignore entries in the mount table that are not | |
4559 | + associated with a file*/ | |
4560 | + if((ret = is_existing_blk_or_reg_file(mnt->mnt_fsname)) < 0) | |
4561 | + goto out_mntloop_err; | |
4562 | + else if(!ret) | |
4563 | + continue; | |
4564 | + | |
4565 | + ret = is_same_loop_file(file, mnt->mnt_fsname); | |
4566 | } | |
4567 | - } | |
4568 | ||
4569 | - if (mnt) { | |
4570 | - /* found an entry in mnt table */ | |
4571 | - ret = 1; | |
4572 | + if(ret < 0) | |
4573 | + goto out_mntloop_err; | |
4574 | + else if(ret) | |
4575 | + break; | |
4576 | } | |
4577 | ||
4578 | + /* Did we find an entry in mnt table? */ | |
4579 | + ret = (mnt != NULL); | |
4580 | + | |
4581 | +out_mntloop_err: | |
4582 | endmntent (f); | |
4583 | + | |
4584 | return ret; | |
4585 | } | |
4586 | ||
4587 | diff --git a/utils.h b/utils.h | |
4588 | index 7ff542b..9dce5b0 100644 | |
4589 | --- a/utils.h | |
4590 | +++ b/utils.h | |
4591 | @@ -36,7 +36,7 @@ int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs, | |
4592 | int run_ioctls); | |
4593 | void btrfs_register_one_device(char *fname); | |
4594 | int btrfs_scan_one_dir(char *dirname, int run_ioctl); | |
4595 | -int check_mounted(char *devicename); | |
4596 | +int check_mounted(const char *devicename); | |
4597 | int btrfs_device_already_in_root(struct btrfs_root *root, int fd, | |
4598 | int super_offset); | |
4599 | char *pretty_sizes(u64 size); |