forked from Mirrors/btrfs-progs
Merge branch 'for-chris' of git://repo.or.cz/btrfs-progs-unstable/devel into raid56
Conflicts: ctree.h Signed-off-by: Chris Mason <chris.mason@fusionio.com>master
commit
7b1c567c84
105
Makefile
105
Makefile
|
@ -8,7 +8,7 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
|
|||
send-stream.o send-utils.o qgroup.o raid6.o
|
||||
cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
|
||||
cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
|
||||
cmds-quota.o cmds-qgroup.o
|
||||
cmds-quota.o cmds-qgroup.o cmds-replace.o
|
||||
|
||||
CHECKFLAGS= -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
|
||||
-Wuninitialized -Wshadow -Wundef
|
||||
|
@ -20,98 +20,139 @@ bindir = $(prefix)/bin
|
|||
LIBS=-luuid -lm
|
||||
RESTORE_LIBS=-lz
|
||||
|
||||
ifeq ("$(origin V)", "command line")
|
||||
BUILD_VERBOSE = $(V)
|
||||
endif
|
||||
ifndef BUILD_VERBOSE
|
||||
BUILD_VERBOSE = 0
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_VERBOSE),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
|
||||
MAKEOPTS = --no-print-directory Q=$(Q)
|
||||
|
||||
progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \
|
||||
btrfs btrfs-map-logical btrfs-image btrfs-zero-log btrfs-convert \
|
||||
btrfs-find-root btrfs-restore btrfstune
|
||||
btrfs-find-root btrfs-restore btrfstune btrfs-show-super
|
||||
|
||||
# make C=1 to enable sparse
|
||||
ifdef C
|
||||
check = sparse $(CHECKFLAGS)
|
||||
else
|
||||
check = ls
|
||||
check = true
|
||||
endif
|
||||
|
||||
.c.o:
|
||||
$(check) $<
|
||||
$(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $<
|
||||
$(Q)$(check) $<
|
||||
@echo " [CC] $@"
|
||||
$(Q)$(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $<
|
||||
|
||||
|
||||
all: version $(progs) manpages
|
||||
all: version.h $(progs) manpages
|
||||
|
||||
version:
|
||||
bash version.sh
|
||||
version.h:
|
||||
$(Q)bash version.sh
|
||||
|
||||
btrfs: $(objects) btrfs.o help.o common.o $(cmds_objects)
|
||||
$(CC) $(CFLAGS) -o btrfs btrfs.o help.o common.o $(cmds_objects) \
|
||||
btrfs: $(objects) btrfs.o help.o $(cmds_objects)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs btrfs.o help.o $(cmds_objects) \
|
||||
$(objects) $(LDFLAGS) $(LIBS) -lpthread
|
||||
|
||||
calc-size: $(objects) calc-size.o
|
||||
$(CC) $(CFLAGS) -o calc-size calc-size.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o calc-size calc-size.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-find-root: $(objects) find-root.o
|
||||
$(CC) $(CFLAGS) -o btrfs-find-root find-root.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-find-root find-root.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-restore: $(objects) restore.o
|
||||
$(CC) $(CFLAGS) -o btrfs-restore restore.o $(objects) $(LDFLAGS) $(LIBS) $(RESTORE_LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-restore restore.o $(objects) $(LDFLAGS) $(LIBS) $(RESTORE_LIBS)
|
||||
|
||||
btrfsctl: $(objects) btrfsctl.o
|
||||
$(CC) $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-vol: $(objects) btrfs-vol.o
|
||||
$(CC) $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-show: $(objects) btrfs-show.o
|
||||
$(CC) $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfsck: $(objects) btrfsck.o
|
||||
$(CC) $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS)
|
||||
|
||||
mkfs.btrfs: $(objects) mkfs.o
|
||||
$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS) -lblkid
|
||||
|
||||
btrfs-debug-tree: $(objects) debug-tree.o
|
||||
$(CC) $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-zero-log: $(objects) btrfs-zero-log.o
|
||||
$(CC) $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-show-super: $(objects) btrfs-show-super.o
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-show-super $(objects) btrfs-show-super.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-select-super: $(objects) btrfs-select-super.o
|
||||
$(CC) $(CFLAGS) -o btrfs-select-super $(objects) btrfs-select-super.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-select-super $(objects) btrfs-select-super.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfstune: $(objects) btrfstune.o
|
||||
$(CC) $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-map-logical: $(objects) btrfs-map-logical.o
|
||||
$(CC) $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-corrupt-block: $(objects) btrfs-corrupt-block.o
|
||||
$(CC) $(CFLAGS) -o btrfs-corrupt-block $(objects) btrfs-corrupt-block.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-corrupt-block $(objects) btrfs-corrupt-block.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-image: $(objects) btrfs-image.o
|
||||
$(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS)
|
||||
|
||||
dir-test: $(objects) dir-test.o
|
||||
$(CC) $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
quick-test: $(objects) quick-test.o
|
||||
$(CC) $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
btrfs-convert: $(objects) convert.o
|
||||
$(CC) $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS)
|
||||
|
||||
ioctl-test: $(objects) ioctl-test.o
|
||||
$(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
|
||||
@echo " [LD] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
|
||||
|
||||
manpages:
|
||||
cd man; $(MAKE)
|
||||
$(Q)$(MAKE) $(MAKEOPTS) -C man
|
||||
|
||||
install-man:
|
||||
cd man; $(MAKE) install
|
||||
|
||||
clean :
|
||||
rm -f $(progs) cscope.out *.o .*.d btrfs-convert btrfs-image btrfs-select-super \
|
||||
@echo "Cleaning"
|
||||
$(Q)rm -f $(progs) cscope.out *.o .*.d btrfs-convert btrfs-image btrfs-select-super \
|
||||
btrfs-zero-log btrfstune dir-test ioctl-test quick-test version.h
|
||||
cd man; $(MAKE) clean
|
||||
$(Q)$(MAKE) $(MAKEOPTS) -C man $@
|
||||
|
||||
install: $(progs) install-man
|
||||
$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
|
||||
|
|
|
@ -491,6 +491,11 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
|
|||
int ret;
|
||||
|
||||
root = open_ctree(input, 0, 0);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
BUG_ON(root->nodesize != root->leafsize);
|
||||
|
||||
ret = metadump_init(&metadump, root, out, num_threads,
|
||||
|
|
282
btrfs-list.c
282
btrfs-list.c
|
@ -46,52 +46,6 @@ struct root_lookup {
|
|||
struct rb_root root;
|
||||
};
|
||||
|
||||
/*
|
||||
* one of these for each root we find.
|
||||
*/
|
||||
struct root_info {
|
||||
struct rb_node rb_node;
|
||||
struct rb_node sort_node;
|
||||
|
||||
/* this root's id */
|
||||
u64 root_id;
|
||||
|
||||
/* equal the offset of the root's key */
|
||||
u64 root_offset;
|
||||
|
||||
/* flags of the root */
|
||||
u64 flags;
|
||||
|
||||
/* the id of the root that references this one */
|
||||
u64 ref_tree;
|
||||
|
||||
/* the dir id we're in from ref_tree */
|
||||
u64 dir_id;
|
||||
|
||||
u64 top_id;
|
||||
|
||||
/* generation when the root is created or last updated */
|
||||
u64 gen;
|
||||
|
||||
/* creation generation of this root in sec*/
|
||||
u64 ogen;
|
||||
|
||||
/* creation time of this root in sec*/
|
||||
time_t otime;
|
||||
|
||||
u8 uuid[BTRFS_UUID_SIZE];
|
||||
|
||||
/* path from the subvol we live in to this root, including the
|
||||
* root's name. This is null until we do the extra lookup ioctl.
|
||||
*/
|
||||
char *path;
|
||||
|
||||
/* the name of this root in the directory it lives in */
|
||||
char *name;
|
||||
|
||||
char *full_path;
|
||||
};
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
char *column_name;
|
||||
|
@ -100,12 +54,12 @@ struct {
|
|||
{
|
||||
.name = "ID",
|
||||
.column_name = "ID",
|
||||
.need_print = 1,
|
||||
.need_print = 0,
|
||||
},
|
||||
{
|
||||
.name = "gen",
|
||||
.column_name = "Gen",
|
||||
.need_print = 1,
|
||||
.need_print = 0,
|
||||
},
|
||||
{
|
||||
.name = "cgen",
|
||||
|
@ -120,13 +74,18 @@ struct {
|
|||
{
|
||||
.name = "top level",
|
||||
.column_name = "Top Level",
|
||||
.need_print = 1,
|
||||
.need_print = 0,
|
||||
},
|
||||
{
|
||||
.name = "otime",
|
||||
.column_name = "OTime",
|
||||
.need_print = 0,
|
||||
},
|
||||
{
|
||||
.name = "parent_uuid",
|
||||
.column_name = "Parent UUID",
|
||||
.need_print = 0,
|
||||
},
|
||||
{
|
||||
.name = "uuid",
|
||||
.column_name = "UUID",
|
||||
|
@ -135,7 +94,7 @@ struct {
|
|||
{
|
||||
.name = "path",
|
||||
.column_name = "Path",
|
||||
.need_print = 1,
|
||||
.need_print = 0,
|
||||
},
|
||||
{
|
||||
.name = NULL,
|
||||
|
@ -435,7 +394,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
|
|||
static int update_root(struct root_lookup *root_lookup,
|
||||
u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
|
||||
u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
|
||||
time_t ot, void *uuid)
|
||||
time_t ot, void *uuid, void *puuid)
|
||||
{
|
||||
struct root_info *ri;
|
||||
|
||||
|
@ -472,6 +431,8 @@ static int update_root(struct root_lookup *root_lookup,
|
|||
ri->otime = ot;
|
||||
if (uuid)
|
||||
memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
|
||||
if (puuid)
|
||||
memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -489,17 +450,18 @@ static int update_root(struct root_lookup *root_lookup,
|
|||
* gen: the current generation of the root
|
||||
* ot: the original time(create time) of the root
|
||||
* uuid: uuid of the root
|
||||
* puuid: uuid of the root parent if any
|
||||
*/
|
||||
static int add_root(struct root_lookup *root_lookup,
|
||||
u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
|
||||
u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
|
||||
time_t ot, void *uuid)
|
||||
time_t ot, void *uuid, void *puuid)
|
||||
{
|
||||
struct root_info *ri;
|
||||
int ret;
|
||||
|
||||
ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags,
|
||||
dir_id, name, name_len, ogen, gen, ot, uuid);
|
||||
dir_id, name, name_len, ogen, gen, ot, uuid, puuid);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
|
@ -537,9 +499,12 @@ static int add_root(struct root_lookup *root_lookup,
|
|||
if (ot)
|
||||
ri->otime = ot;
|
||||
|
||||
if (uuid)
|
||||
if (uuid)
|
||||
memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
|
||||
|
||||
if (puuid)
|
||||
memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
|
||||
|
||||
ret = root_tree_insert(root_lookup, ri);
|
||||
if (ret) {
|
||||
printf("failed to insert tree %llu\n", (unsigned long long)root_id);
|
||||
|
@ -599,6 +564,12 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
|
|||
while (1) {
|
||||
char *tmp;
|
||||
u64 next;
|
||||
/*
|
||||
* ref_tree = 0 indicates the subvolumes
|
||||
* has been deleted.
|
||||
*/
|
||||
if (!found->ref_tree)
|
||||
return -ENOENT;
|
||||
int add_len = strlen(found->path);
|
||||
|
||||
/* room for / and for null */
|
||||
|
@ -627,29 +598,22 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
|
|||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the ref_tree = BTRFS_FS_TREE_OBJECTID,
|
||||
* we are at the top
|
||||
*/
|
||||
if (next == BTRFS_FS_TREE_OBJECTID) {
|
||||
char p[] = "<FS_TREE>";
|
||||
add_len = strlen(p);
|
||||
len = strlen(full_path);
|
||||
tmp = malloc(len + add_len + 2);
|
||||
memcpy(tmp + add_len + 1, full_path, len);
|
||||
tmp[add_len] = '/';
|
||||
memcpy(tmp, p, add_len);
|
||||
free(full_path);
|
||||
full_path = tmp;
|
||||
ri->top_id = next;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the ref_tree wasn't in our tree of roots, we're
|
||||
* at the top
|
||||
*/
|
||||
* if the ref_tree wasn't in our tree of roots, the
|
||||
* subvolume was deleted.
|
||||
*/
|
||||
found = root_tree_search(rl, next);
|
||||
if (!found) {
|
||||
ri->top_id = next;
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ri->full_path = full_path;
|
||||
|
@ -672,6 +636,9 @@ static int lookup_ino_path(int fd, struct root_info *ri)
|
|||
if (ri->path)
|
||||
return 0;
|
||||
|
||||
if (!ri->ref_tree)
|
||||
return -ENOENT;
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
args.treeid = ri->ref_tree;
|
||||
args.objectid = ri->dir_id;
|
||||
|
@ -679,6 +646,10 @@ static int lookup_ino_path(int fd, struct root_info *ri)
|
|||
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
|
||||
e = errno;
|
||||
if (ret) {
|
||||
if (e == ENOENT) {
|
||||
ri->ref_tree = 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n",
|
||||
(unsigned long long)ri->ref_tree,
|
||||
strerror(e));
|
||||
|
@ -1021,6 +992,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
|
|||
int i;
|
||||
time_t t;
|
||||
u8 uuid[BTRFS_UUID_SIZE];
|
||||
u8 puuid[BTRFS_UUID_SIZE];
|
||||
|
||||
root_lookup_init(root_lookup);
|
||||
memset(&args, 0, sizeof(args));
|
||||
|
@ -1073,7 +1045,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
|
|||
|
||||
add_root(root_lookup, sh.objectid, sh.offset,
|
||||
0, 0, dir_id, name, name_len, 0, 0, 0,
|
||||
NULL);
|
||||
NULL, NULL);
|
||||
} else if (sh.type == BTRFS_ROOT_ITEM_KEY) {
|
||||
ri = (struct btrfs_root_item *)(args.buf + off);
|
||||
gen = btrfs_root_generation(ri);
|
||||
|
@ -1083,15 +1055,17 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
|
|||
t = ri->otime.sec;
|
||||
ogen = btrfs_root_otransid(ri);
|
||||
memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
|
||||
memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);
|
||||
} else {
|
||||
t = 0;
|
||||
ogen = 0;
|
||||
memset(uuid, 0, BTRFS_UUID_SIZE);
|
||||
memset(puuid, 0, BTRFS_UUID_SIZE);
|
||||
}
|
||||
|
||||
add_root(root_lookup, sh.objectid, 0,
|
||||
sh.offset, flags, 0, NULL, 0, ogen,
|
||||
gen, t, uuid);
|
||||
gen, t, uuid, puuid);
|
||||
}
|
||||
|
||||
off += sh.len;
|
||||
|
@ -1174,6 +1148,34 @@ static int filter_topid_equal(struct root_info *ri, u64 data)
|
|||
return ri->top_id == data;
|
||||
}
|
||||
|
||||
static int filter_full_path(struct root_info *ri, u64 data)
|
||||
{
|
||||
if (ri->full_path && ri->top_id != data) {
|
||||
char *tmp;
|
||||
char p[] = "<FS_TREE>";
|
||||
int add_len = strlen(p);
|
||||
int len = strlen(ri->full_path);
|
||||
|
||||
tmp = malloc(len + add_len + 2);
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "memory allocation failed\n");
|
||||
exit(1);
|
||||
}
|
||||
memcpy(tmp + add_len + 1, ri->full_path, len);
|
||||
tmp[len + add_len + 1] = '\0';
|
||||
tmp[add_len] = '/';
|
||||
memcpy(tmp, p, add_len);
|
||||
free(ri->full_path);
|
||||
ri->full_path = tmp;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int filter_by_parent(struct root_info *ri, u64 data)
|
||||
{
|
||||
return !uuid_compare(ri->puuid, (u8 *)data);
|
||||
}
|
||||
|
||||
static btrfs_list_filter_func all_filter_funcs[] = {
|
||||
[BTRFS_LIST_FILTER_ROOTID] = filter_by_rootid,
|
||||
[BTRFS_LIST_FILTER_SNAPSHOT_ONLY] = filter_snapshot,
|
||||
|
@ -1185,6 +1187,8 @@ static btrfs_list_filter_func all_filter_funcs[] = {
|
|||
[BTRFS_LIST_FILTER_CGEN_LESS] = filter_cgen_less,
|
||||
[BTRFS_LIST_FILTER_CGEN_EQUAL] = filter_cgen_equal,
|
||||
[BTRFS_LIST_FILTER_TOPID_EQUAL] = filter_topid_equal,
|
||||
[BTRFS_LIST_FILTER_FULL_PATH] = filter_full_path,
|
||||
[BTRFS_LIST_FILTER_BY_PARENT] = filter_by_parent,
|
||||
};
|
||||
|
||||
struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
|
||||
|
@ -1267,12 +1271,11 @@ static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
|
|||
struct root_lookup *sort_tree,
|
||||
struct btrfs_list_filter_set *filter_set,
|
||||
struct btrfs_list_comparer_set *comp_set,
|
||||
int fd)
|
||||
u64 top_id)
|
||||
{
|
||||
struct rb_node *n;
|
||||
struct root_info *entry;
|
||||
int ret;
|
||||
u64 top_id = btrfs_list_get_path_rootid(fd);
|
||||
|
||||
root_lookup_init(sort_tree);
|
||||
|
||||
|
@ -1280,10 +1283,13 @@ static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
|
|||
while (n) {
|
||||
entry = rb_entry(n, struct root_info, rb_node);
|
||||
|
||||
resolve_root(all_subvols, entry, top_id);
|
||||
ret = resolve_root(all_subvols, entry, top_id);
|
||||
if (ret == -ENOENT)
|
||||
goto skip;
|
||||
ret = filter_root(entry, filter_set);
|
||||
if (ret)
|
||||
sort_tree_insert(sort_tree, entry, comp_set);
|
||||
skip:
|
||||
n = rb_prev(n);
|
||||
}
|
||||
}
|
||||
|
@ -1298,7 +1304,7 @@ static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
|
|||
int ret;
|
||||
entry = rb_entry(n, struct root_info, rb_node);
|
||||
ret = lookup_ino_path(fd, entry);
|
||||
if(ret < 0)
|
||||
if (ret && ret != -ENOENT)
|
||||
return ret;
|
||||
n = rb_next(n);
|
||||
}
|
||||
|
@ -1345,6 +1351,13 @@ static void print_subvolume_column(struct root_info *subv,
|
|||
uuid_unparse(subv->uuid, uuidparse);
|
||||
printf("%s", uuidparse);
|
||||
break;
|
||||
case BTRFS_LIST_PUUID:
|
||||
if (uuid_is_null(subv->puuid))
|
||||
strcpy(uuidparse, "-");
|
||||
else
|
||||
uuid_unparse(subv->puuid, uuidparse);
|
||||
printf("%s", uuidparse);
|
||||
break;
|
||||
case BTRFS_LIST_PATH:
|
||||
BUG_ON(!subv->full_path);
|
||||
printf("%s", subv->full_path);
|
||||
|
@ -1354,6 +1367,22 @@ static void print_subvolume_column(struct root_info *subv,
|
|||
}
|
||||
}
|
||||
|
||||
static void print_single_volume_info_raw(struct root_info *subv, char *raw_prefix)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BTRFS_LIST_ALL; i++) {
|
||||
if (!btrfs_list_columns[i].need_print)
|
||||
continue;
|
||||
|
||||
if (raw_prefix)
|
||||
printf("%s",raw_prefix);
|
||||
|
||||
print_subvolume_column(subv, i);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_single_volume_info_table(struct root_info *subv)
|
||||
{
|
||||
int i;
|
||||
|
@ -1420,34 +1449,37 @@ static void print_all_volume_info_tab_head()
|
|||
}
|
||||
|
||||
static void print_all_volume_info(struct root_lookup *sorted_tree,
|
||||
int is_tab_result)
|
||||
int layout, char *raw_prefix)
|
||||
{
|
||||
struct rb_node *n;
|
||||
struct root_info *entry;
|
||||
|
||||
if (is_tab_result)
|
||||
if (layout == BTRFS_LIST_LAYOUT_TABLE)
|
||||
print_all_volume_info_tab_head();
|
||||
|
||||
n = rb_first(&sorted_tree->root);
|
||||
while (n) {
|
||||
entry = rb_entry(n, struct root_info, sort_node);
|
||||
if (is_tab_result)
|
||||
print_single_volume_info_table(entry);
|
||||
else
|
||||
switch (layout) {
|
||||
case BTRFS_LIST_LAYOUT_DEFAULT:
|
||||
print_single_volume_info_default(entry);
|
||||
break;
|
||||
case BTRFS_LIST_LAYOUT_TABLE:
|
||||
print_single_volume_info_table(entry);
|
||||
break;
|
||||
case BTRFS_LIST_LAYOUT_RAW:
|
||||
print_single_volume_info_raw(entry, raw_prefix);
|
||||
break;
|
||||
}
|
||||
n = rb_next(n);
|
||||
}
|
||||
}
|
||||
|
||||
int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
|
||||
struct btrfs_list_comparer_set *comp_set,
|
||||
int is_tab_result)
|
||||
int btrfs_list_subvols(int fd, struct root_lookup *root_lookup)
|
||||
{
|
||||
struct root_lookup root_lookup;
|
||||
struct root_lookup root_sort;
|
||||
int ret;
|
||||
|
||||
ret = __list_subvol_search(fd, &root_lookup);
|
||||
ret = __list_subvol_search(fd, root_lookup);
|
||||
if (ret) {
|
||||
fprintf(stderr, "ERROR: can't perform the search - %s\n",
|
||||
strerror(errno));
|
||||
|
@ -1458,15 +1490,71 @@ int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
|
|||
* now we have an rbtree full of root_info objects, but we need to fill
|
||||
* in their path names within the subvol that is referencing each one.
|
||||
*/
|
||||
ret = __list_subvol_fill_paths(fd, &root_lookup);
|
||||
if (ret < 0)
|
||||
ret = __list_subvol_fill_paths(fd, root_lookup);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
|
||||
struct btrfs_list_comparer_set *comp_set,
|
||||
int layout, int full_path, char *raw_prefix)
|
||||
{
|
||||
struct root_lookup root_lookup;
|
||||
struct root_lookup root_sort;
|
||||
int ret;
|
||||
u64 top_id = (full_path ? 0 : btrfs_list_get_path_rootid(fd));
|
||||
|
||||
ret = btrfs_list_subvols(fd, &root_lookup);
|
||||
if (ret)
|
||||
return ret;
|
||||
__filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
|
||||
comp_set, top_id);
|
||||
|
||||
print_all_volume_info(&root_sort, layout, raw_prefix);
|
||||
__free_all_subvolumn(&root_lookup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_get_subvol(int fd, struct root_info *the_ri)
|
||||
{
|
||||
int ret = 1, rr;
|
||||
struct root_lookup rl;
|
||||
struct rb_node *rbn;
|
||||
struct root_info *ri;
|
||||
u64 root_id = btrfs_list_get_path_rootid(fd);
|
||||
|
||||
if (btrfs_list_subvols(fd, &rl))
|
||||
return ret;
|
||||
|
||||
__filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
|
||||
comp_set, fd);
|
||||
|
||||
print_all_volume_info(&root_sort, is_tab_result);
|
||||
__free_all_subvolumn(&root_lookup);
|
||||
rbn = rb_first(&rl.root);
|
||||
while(rbn) {
|
||||
ri = rb_entry(rbn, struct root_info, rb_node);
|
||||
rr = resolve_root(&rl, ri, root_id);
|
||||
if (rr == -ENOENT) {
|
||||
ret = -ENOENT;
|
||||
rbn = rb_next(rbn);
|
||||
continue;
|
||||
}
|
||||
if (!comp_entry_with_rootid(the_ri, ri, 0)) {
|
||||
memcpy(the_ri, ri, offsetof(struct root_info, path));
|
||||
if (ri->path)
|
||||
the_ri->path = strdup(ri->path);
|
||||
else
|
||||
the_ri->path = NULL;
|
||||
if (ri->name)
|
||||
the_ri->name = strdup(ri->name);
|
||||
else
|
||||
the_ri->name = NULL;
|
||||
if (ri->full_path)
|
||||
the_ri->full_path = strdup(ri->full_path);
|
||||
else
|
||||
the_ri->name = NULL;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
rbn = rb_next(rbn);
|
||||
}
|
||||
__free_all_subvolumn(&rl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1670,7 +1758,11 @@ char *btrfs_list_path_for_root(int fd, u64 root)
|
|||
struct root_info *entry;
|
||||
|
||||
entry = rb_entry(n, struct root_info, rb_node);
|
||||
resolve_root(&root_lookup, entry, top_id);
|
||||
ret = resolve_root(&root_lookup, entry, top_id);
|
||||
if (ret == -ENOENT && entry->root_id == root) {
|
||||
ret_path = NULL;
|
||||
break;
|
||||
}
|
||||
if (entry->root_id == root) {
|
||||
ret_path = entry->full_path;
|
||||
entry->full_path = NULL;
|
||||
|
|
59
btrfs-list.h
59
btrfs-list.h
|
@ -18,7 +18,56 @@
|
|||
|
||||
#include "kerncompat.h"
|
||||
|
||||
struct root_info;
|
||||
#define BTRFS_LIST_LAYOUT_DEFAULT 0
|
||||
#define BTRFS_LIST_LAYOUT_TABLE 1
|
||||
#define BTRFS_LIST_LAYOUT_RAW 2
|
||||
|
||||
/*
|
||||
* one of these for each root we find.
|
||||
*/
|
||||
struct root_info {
|
||||
struct rb_node rb_node;
|
||||
struct rb_node sort_node;
|
||||
|
||||
/* this root's id */
|
||||
u64 root_id;
|
||||
|
||||
/* equal the offset of the root's key */
|
||||
u64 root_offset;
|
||||
|
||||
/* flags of the root */
|
||||
u64 flags;
|
||||
|
||||
/* the id of the root that references this one */
|
||||
u64 ref_tree;
|
||||
|
||||
/* the dir id we're in from ref_tree */
|
||||
u64 dir_id;
|
||||
|
||||
u64 top_id;
|
||||
|
||||
/* generation when the root is created or last updated */
|
||||
u64 gen;
|
||||
|
||||
/* creation generation of this root in sec*/
|
||||
u64 ogen;
|
||||
|
||||
/* creation time of this root in sec*/
|
||||
time_t otime;
|
||||
|
||||
u8 uuid[BTRFS_UUID_SIZE];
|
||||
u8 puuid[BTRFS_UUID_SIZE];
|
||||
|
||||
/* path from the subvol we live in to this root, including the
|
||||
* root's name. This is null until we do the extra lookup ioctl.
|
||||
*/
|
||||
char *path;
|
||||
|
||||
/* the name of this root in the directory it lives in */
|
||||
char *name;
|
||||
|
||||
char *full_path;
|
||||
};
|
||||
|
||||
typedef int (*btrfs_list_filter_func)(struct root_info *, u64);
|
||||
typedef int (*btrfs_list_comp_func)(struct root_info *, struct root_info *,
|
||||
|
@ -53,6 +102,7 @@ enum btrfs_list_column_enum {
|
|||
BTRFS_LIST_PARENT,
|
||||
BTRFS_LIST_TOP_LEVEL,
|
||||
BTRFS_LIST_OTIME,
|
||||
BTRFS_LIST_PUUID,
|
||||
BTRFS_LIST_UUID,
|
||||
BTRFS_LIST_PATH,
|
||||
BTRFS_LIST_ALL,
|
||||
|
@ -71,6 +121,8 @@ enum btrfs_list_filter_enum {
|
|||
BTRFS_LIST_FILTER_CGEN_LESS,
|
||||
BTRFS_LIST_FILTER_CGEN_MORE,
|
||||
BTRFS_LIST_FILTER_TOPID_EQUAL,
|
||||
BTRFS_LIST_FILTER_FULL_PATH,
|
||||
BTRFS_LIST_FILTER_BY_PARENT,
|
||||
BTRFS_LIST_FILTER_MAX,
|
||||
};
|
||||
|
||||
|
@ -98,10 +150,11 @@ int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
|
|||
enum btrfs_list_comp_enum comparer,
|
||||
int is_descending);
|
||||
|
||||
int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
|
||||
int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
|
||||
struct btrfs_list_comparer_set *comp_set,
|
||||
int is_tab_result);
|
||||
int is_tab_result, int full_path, char *raw_prefix);
|
||||
int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen);
|
||||
int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
|
||||
char *btrfs_list_path_for_root(int fd, u64 root);
|
||||
u64 btrfs_list_get_path_rootid(int fd);
|
||||
int btrfs_get_subvol(int fd, struct root_info *the_ri);
|
||||
|
|
|
@ -84,8 +84,10 @@ int main(int ac, char **av)
|
|||
|
||||
root = open_ctree(av[optind], bytenr, 1);
|
||||
|
||||
if (root == NULL)
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* make the super writing code think we've read the first super */
|
||||
root->fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (C) 2012 STRATO AG. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#define _XOPEN_SOURCE 500
|
||||
#define _GNU_SOURCE 1
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "kerncompat.h"
|
||||
#include "ctree.h"
|
||||
#include "disk-io.h"
|
||||
#include "print-tree.h"
|
||||
#include "transaction.h"
|
||||
#include "list.h"
|
||||
#include "version.h"
|
||||
#include "utils.h"
|
||||
#include "crc32c.h"
|
||||
|
||||
static void print_usage(void);
|
||||
static void dump_superblock(struct btrfs_super_block *sb);
|
||||
int main(int argc, char **argv);
|
||||
static int load_and_dump_sb(char *, int fd, u64 sb_bytenr);
|
||||
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: btrfs-show-super [-i super_mirror|-a] dev [dev..]\n");
|
||||
fprintf(stderr, "\tThe super_mirror number is between 0 and %d.\n",
|
||||
BTRFS_SUPER_MIRROR_MAX - 1);
|
||||
fprintf(stderr, "\tIf -a is passed all the superblocks are showed.\n");
|
||||
fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
int all = 0;
|
||||
char *filename;
|
||||
int fd = -1;
|
||||
int arg, i;
|
||||
u64 sb_bytenr = btrfs_sb_offset(0);
|
||||
|
||||
while ((opt = getopt(argc, argv, "ai:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
arg = atoi(optarg);
|
||||
|
||||
if (arg < 0 || arg >= BTRFS_SUPER_MIRROR_MAX) {
|
||||
fprintf(stderr,
|
||||
"Illegal super_mirror %d\n",
|
||||
arg);
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
sb_bytenr = btrfs_sb_offset(arg);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
all = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc < optind + 1) {
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = optind; i < argc; i++) {
|
||||
filename = argv[i];
|
||||
fd = open(filename, O_RDONLY, 0666);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Could not open %s\n", filename);
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (all) {
|
||||
int idx;
|
||||
for (idx = 0; idx < BTRFS_SUPER_MIRROR_MAX; idx++) {
|
||||
sb_bytenr = btrfs_sb_offset(idx);
|
||||
if (load_and_dump_sb(filename, fd, sb_bytenr)) {
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
} else {
|
||||
load_and_dump_sb(filename, fd, sb_bytenr);
|
||||
putchar('\n');
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int load_and_dump_sb(char *filename, int fd, u64 sb_bytenr)
|
||||
{
|
||||
u8 super_block_data[BTRFS_SUPER_INFO_SIZE];
|
||||
struct btrfs_super_block *sb;
|
||||
u64 ret;
|
||||
|
||||
sb = (struct btrfs_super_block *)super_block_data;
|
||||
|
||||
ret = pread64(fd, super_block_data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
|
||||
if (ret != BTRFS_SUPER_INFO_SIZE) {
|
||||
int e = errno;
|
||||
|
||||
/* check if the disk if too short for further superblock */
|
||||
if (ret == 0 && e == 0)
|
||||
return 0;
|
||||
|
||||
fprintf(stderr,
|
||||
"ERROR: Failed to read the superblock on %s at %llu\n",
|
||||
filename, (unsigned long long)sb_bytenr);
|
||||
fprintf(stderr,
|
||||
"ERROR: error = '%s', errno = %d\n", strerror(e), e);
|
||||
return 1;
|
||||
}
|
||||
printf("superblock: bytenr=%llu, device=%s\n", sb_bytenr, filename);
|
||||
printf("---------------------------------------------------------\n");
|
||||
dump_superblock(sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_csum_sblock(void *sb, int csum_size)
|
||||
{
|
||||
char result[csum_size];
|
||||
u32 crc = ~(u32)0;
|
||||
|
||||
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE,
|
||||
crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
|
||||
btrfs_csum_final(crc, result);
|
||||
|
||||
return !memcmp(sb, &result, csum_size);
|
||||
}
|
||||
|
||||
static void dump_superblock(struct btrfs_super_block *sb)
|
||||
{
|
||||
int i;
|
||||
char *s, buf[36+1];
|
||||
u8 *p;
|
||||
|
||||
printf("csum\t\t\t0x");
|
||||
for (i = 0, p = sb->csum; i < btrfs_super_csum_size(sb); i++)
|
||||
printf("%02x", p[i]);
|
||||
if (check_csum_sblock(sb, btrfs_super_csum_size(sb)))
|
||||
printf(" [match]");
|
||||
else
|
||||
printf(" [DON'T MATCH]");
|
||||
putchar('\n');
|
||||
|
||||
printf("bytenr\t\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_bytenr(sb));
|
||||
printf("flags\t\t\t0x%llx\n",
|
||||
(unsigned long long)btrfs_super_flags(sb));
|
||||
|
||||
printf("magic\t\t\t");
|
||||
s = (char *) &sb->magic;
|
||||
for (i = 0; i < 8; i++)
|
||||
putchar(isprint(s[i]) ? s[i] : '.');
|
||||
if (!memcmp(BTRFS_MAGIC, &sb->magic, 8))
|
||||
printf(" [match]\n");
|
||||
else
|
||||
printf(" [DON'T MATCH]\n");
|
||||
|
||||
uuid_unparse(sb->fsid, buf);
|
||||
printf("fsid\t\t\t%s\n", buf);
|
||||
|
||||
printf("label\t\t\t");
|
||||
s = sb->label;
|
||||
for (i = 0; i < BTRFS_LABEL_SIZE && s[i]; i++)
|
||||
putchar(isprint(s[i]) ? s[i] : '.');
|
||||
putchar('\n');
|
||||
|
||||
printf("generation\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_generation(sb));
|
||||
printf("root\t\t\t%llu\n", (unsigned long long)btrfs_super_root(sb));
|
||||
printf("sys_array_size\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_sys_array_size(sb));
|
||||
printf("chunk_root_generation\t%llu\n",
|
||||
(unsigned long long)btrfs_super_chunk_root_generation(sb));
|
||||
printf("root_level\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_root_level(sb));
|
||||
printf("chunk_root\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_chunk_root(sb));
|
||||
printf("chunk_root_level\t%llu\n",
|
||||
(unsigned long long)btrfs_super_chunk_root_level(sb));
|
||||
printf("log_root\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_log_root(sb));
|
||||
printf("log_root_transid\t%llu\n",
|
||||
(unsigned long long)btrfs_super_log_root_transid(sb));
|
||||
printf("log_root_level\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_log_root_level(sb));
|
||||
printf("total_bytes\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_total_bytes(sb));
|
||||
printf("bytes_used\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_bytes_used(sb));
|
||||
printf("sectorsize\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_sectorsize(sb));
|
||||
printf("nodesize\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_nodesize(sb));
|
||||
printf("leafsize\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_leafsize(sb));
|
||||
printf("stripesize\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_stripesize(sb));
|
||||
printf("root_dir\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_root_dir(sb));
|
||||
printf("num_devices\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_num_devices(sb));
|
||||
printf("compat_flags\t\t0x%llx\n",
|
||||
(unsigned long long)btrfs_super_compat_flags(sb));
|
||||
printf("compat_ro_flags\t\t0x%llx\n",
|
||||
(unsigned long long)btrfs_super_compat_ro_flags(sb));
|
||||
printf("incompat_flags\t\t0x%llx\n",
|
||||
(unsigned long long)btrfs_super_incompat_flags(sb));
|
||||
printf("csum_type\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_csum_type(sb));
|
||||
printf("csum_size\t\t%llu\n",
|
||||
(unsigned long long)btrfs_super_csum_size(sb));
|
||||
printf("cache_generation\t%llu\n",
|
||||
(unsigned long long)btrfs_super_cache_generation(sb));
|
||||
|
||||
uuid_unparse(sb->dev_item.uuid, buf);
|
||||
printf("dev_item.uuid\t\t%s\n", buf);
|
||||
|
||||
uuid_unparse(sb->dev_item.fsid, buf);
|
||||
printf("dev_item.fsid\t\t%s %s\n", buf,
|
||||
!memcmp(sb->dev_item.fsid, sb->fsid, BTRFS_FSID_SIZE) ?
|
||||
"[match]" : "[DON'T MATCH]");
|
||||
|
||||
printf("dev_item.type\t\t%llu\n", (unsigned long long)
|
||||
btrfs_stack_device_type(&sb->dev_item));
|
||||
printf("dev_item.total_bytes\t%llu\n", (unsigned long long)
|
||||
btrfs_stack_device_total_bytes(&sb->dev_item));
|
||||
printf("dev_item.bytes_used\t%llu\n", (unsigned long long)
|
||||
btrfs_stack_device_bytes_used(&sb->dev_item));
|
||||
printf("dev_item.io_align\t%u\n", (unsigned int)
|
||||
btrfs_stack_device_io_align(&sb->dev_item));
|
||||
printf("dev_item.io_width\t%u\n", (unsigned int)
|
||||
btrfs_stack_device_io_width(&sb->dev_item));
|
||||
printf("dev_item.sector_size\t%u\n", (unsigned int)
|
||||
btrfs_stack_device_sector_size(&sb->dev_item));
|
||||
printf("dev_item.devid\t\t%llu\n",
|
||||
btrfs_stack_device_id(&sb->dev_item));
|
||||
printf("dev_item.dev_group\t%u\n", (unsigned int)
|
||||
btrfs_stack_device_group(&sb->dev_item));
|
||||
printf("dev_item.seek_speed\t%u\n", (unsigned int)
|
||||
btrfs_stack_device_seek_speed(&sb->dev_item));
|
||||
printf("dev_item.bandwidth\t%u\n", (unsigned int)
|
||||
btrfs_stack_device_bandwidth(&sb->dev_item));
|
||||
printf("dev_item.generation\t%llu\n", (unsigned long long)
|
||||
btrfs_stack_device_generation(&sb->dev_item));
|
||||
}
|
5
btrfs.c
5
btrfs.c
|
@ -247,10 +247,11 @@ const struct cmd_group btrfs_cmd_group = {
|
|||
{ "device", cmd_device, NULL, &device_cmd_group, 0 },
|
||||
{ "scrub", cmd_scrub, NULL, &scrub_cmd_group, 0 },
|
||||
{ "inspect-internal", cmd_inspect, NULL, &inspect_cmd_group, 0 },
|
||||
{ "send", cmd_send, NULL, &send_cmd_group, 0 },
|
||||
{ "receive", cmd_receive, NULL, &receive_cmd_group, 0 },
|
||||
{ "send", cmd_send, cmd_send_usage, NULL, 0 },
|
||||
{ "receive", cmd_receive, cmd_receive_usage, NULL, 0 },
|
||||
{ "quota", cmd_quota, NULL, "a_cmd_group, 0 },
|
||||
{ "qgroup", cmd_qgroup, NULL, &qgroup_cmd_group, 0 },
|
||||
{ "replace", cmd_replace, NULL, &replace_cmd_group, 0 },
|
||||
{ "help", cmd_help, cmd_help_usage, NULL, 0 },
|
||||
{ "version", cmd_version, cmd_version_usage, NULL, 0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
|
|
59
btrfsck.c
59
btrfsck.c
|
@ -22,8 +22,11 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include "kerncompat.h"
|
||||
#include "ctree.h"
|
||||
#include "volumes.h"
|
||||
|
@ -96,6 +99,7 @@ struct inode_backref {
|
|||
unsigned int found_inode_ref:1;
|
||||
unsigned int filetype:8;
|
||||
int errors;
|
||||
unsigned int ref_type;
|
||||
u64 dir;
|
||||
u64 index;
|
||||
u16 namelen;
|
||||
|
@ -469,12 +473,14 @@ static int add_inode_backref(struct cache_tree *inode_cache,
|
|||
|
||||
backref->filetype = filetype;
|
||||
backref->found_dir_item = 1;
|
||||
} else if (itemtype == BTRFS_INODE_REF_KEY) {
|
||||
} else if ((itemtype == BTRFS_INODE_REF_KEY) ||
|
||||
(itemtype == BTRFS_INODE_EXTREF_KEY)) {
|
||||
if (backref->found_inode_ref)
|
||||
backref->errors |= REF_ERR_DUP_INODE_REF;
|
||||
if (backref->found_dir_index && backref->index != index)
|
||||
backref->errors |= REF_ERR_INDEX_UNMATCH;
|
||||
|
||||
backref->ref_type = itemtype;
|
||||
backref->index = index;
|
||||
backref->found_inode_ref = 1;
|
||||
} else {
|
||||
|
@ -510,7 +516,7 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
|
|||
add_inode_backref(dst_cache, dst->ino,
|
||||
backref->dir, backref->index,
|
||||
backref->name, backref->namelen, 0,
|
||||
BTRFS_INODE_REF_KEY, backref->errors);
|
||||
backref->ref_type, backref->errors);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -914,6 +920,49 @@ static int process_inode_ref(struct extent_buffer *eb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_inode_extref(struct extent_buffer *eb,
|
||||
int slot, struct btrfs_key *key,
|
||||
struct shared_node *active_node)
|
||||
{
|
||||
u32 total;
|
||||
u32 cur = 0;
|
||||
u32 len;
|
||||
u32 name_len;
|
||||
u64 index;
|
||||
u64 parent;
|
||||
int error;
|
||||
struct cache_tree *inode_cache;
|
||||
struct btrfs_inode_extref *extref;
|
||||
char namebuf[BTRFS_NAME_LEN];
|
||||
|
||||
inode_cache = &active_node->inode_cache;
|
||||
|
||||
extref = btrfs_item_ptr(eb, slot, struct btrfs_inode_extref);
|
||||
total = btrfs_item_size_nr(eb, slot);
|
||||
while (cur < total) {
|
||||
name_len = btrfs_inode_extref_name_len(eb, extref);
|
||||
index = btrfs_inode_extref_index(eb, extref);
|
||||
parent = btrfs_inode_extref_parent(eb, extref);
|
||||
if (name_len <= BTRFS_NAME_LEN) {
|
||||
len = name_len;
|
||||
error = 0;
|
||||
} else {
|
||||
len = BTRFS_NAME_LEN;
|
||||
error = REF_ERR_NAME_TOO_LONG;
|
||||
}
|
||||
read_extent_buffer(eb, namebuf,
|
||||
(unsigned long)(extref + 1), len);
|
||||
add_inode_backref(inode_cache, key->objectid, parent,
|
||||
index, namebuf, len, 0, key->type, error);
|
||||
|
||||
len = sizeof(*extref) + name_len;
|
||||
extref = (struct btrfs_inode_extref *)((char *)extref + len);
|
||||
cur += len;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static u64 count_csum_range(struct btrfs_root *root, u64 start, u64 len)
|
||||
{
|
||||
struct btrfs_key key;
|
||||
|
@ -1100,6 +1149,9 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
|
|||
case BTRFS_INODE_REF_KEY:
|
||||
ret = process_inode_ref(eb, i, &key, active_node);
|
||||
break;
|
||||
case BTRFS_INODE_EXTREF_KEY:
|
||||
ret = process_inode_extref(eb, i, &key, active_node);
|
||||
break;
|
||||
case BTRFS_INODE_ITEM_KEY:
|
||||
ret = process_inode_item(eb, i, &key, active_node);
|
||||
break;
|
||||
|
@ -3492,6 +3544,7 @@ int main(int ac, char **av)
|
|||
struct btrfs_fs_info *info;
|
||||
struct btrfs_trans_handle *trans = NULL;
|
||||
u64 bytenr = 0;
|
||||
char uuidbuf[37];
|
||||
int ret;
|
||||
int num;
|
||||
int repair = 0;
|
||||
|
@ -3544,6 +3597,8 @@ int main(int ac, char **av)
|
|||
}
|
||||
|
||||
info = open_ctree_fs_info(av[optind], bytenr, rw, 1);
|
||||
uuid_unparse(info->super_copy.fsid, uuidbuf);
|
||||
printf("Checking filesystem on %s\nUUID: %s\n", av[optind], uuidbuf);
|
||||
|
||||
if (info == NULL)
|
||||
return 1;
|
||||
|
|
|
@ -63,7 +63,7 @@ static void print_usage(void)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
static int open_file_or_dir(const char *fname)
|
||||
static int btrfsctl_open_file_or_dir(const char *fname)
|
||||
{
|
||||
int ret;
|
||||
struct stat st;
|
||||
|
@ -91,6 +91,7 @@ static int open_file_or_dir(const char *fname)
|
|||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
char *fname = NULL;
|
||||
|
@ -128,7 +129,7 @@ int main(int ac, char **av)
|
|||
snap_location = strdup(fullpath);
|
||||
snap_location = dirname(snap_location);
|
||||
|
||||
snap_fd = open_file_or_dir(snap_location);
|
||||
snap_fd = btrfsctl_open_file_or_dir(snap_location);
|
||||
|
||||
name = strdup(fullpath);
|
||||
name = basename(name);
|
||||
|
@ -238,7 +239,7 @@ int main(int ac, char **av)
|
|||
}
|
||||
name = fname;
|
||||
} else {
|
||||
fd = open_file_or_dir(fname);
|
||||
fd = btrfsctl_open_file_or_dir(fname);
|
||||
}
|
||||
|
||||
if (name) {
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
#define GET_LABEL 3
|
||||
#define SET_LABEL 4
|
||||
|
||||
static void change_label_unmounted(char *dev, char *nLabel)
|
||||
static int change_label_unmounted(char *dev, char *nLabel)
|
||||
{
|
||||
struct btrfs_root *root;
|
||||
struct btrfs_trans_handle *trans;
|
||||
|
@ -56,7 +56,7 @@ static void change_label_unmounted(char *dev, char *nLabel)
|
|||
*/
|
||||
root = open_ctree(dev, 0, 1);
|
||||
if (!root) /* errors are printed by open_ctree() */
|
||||
return;
|
||||
return -1;
|
||||
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
strncpy(root->fs_info->super_copy.label, nLabel, BTRFS_LABEL_SIZE);
|
||||
|
@ -65,6 +65,7 @@ static void change_label_unmounted(char *dev, char *nLabel)
|
|||
|
||||
/* Now we close it since we are done. */
|
||||
close_ctree(root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_label_unmounted(char *dev)
|
||||
|
@ -123,6 +124,5 @@ int set_label(char *btrfs_dev, char *nLabel)
|
|||
fprintf(stderr, "FATAL: the filesystem has to be unmounted\n");
|
||||
return -2;
|
||||
}
|
||||
change_label_unmounted(btrfs_dev, nLabel);
|
||||
return 0;
|
||||
return change_label_unmounted(btrfs_dev, nLabel);
|
||||
}
|
||||
|
|
|
@ -108,6 +108,11 @@ int main(int argc, char *argv[])
|
|||
|
||||
root = open_ctree(device, 0, 1);
|
||||
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (seeding_flag) {
|
||||
ret = update_seeding_flag(root, seeding_value);
|
||||
if (!ret)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "volumes.h"
|
||||
|
||||
#include "commands.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const char * const balance_cmd_group_usage[] = {
|
||||
"btrfs [filesystem] balance <command> [options] <path>",
|
||||
|
@ -353,7 +354,7 @@ static const char * const cmd_balance_start_usage[] = {
|
|||
"",
|
||||
"-d[filters] act on data chunks",
|
||||
"-m[filters] act on metadata chunks",
|
||||
"-s[filetrs] act on system chunks (only under -f)",
|
||||
"-s[filters] act on system chunks (only under -f)",
|
||||
"-v be verbose",
|
||||
"-f force reducing of metadata integrity",
|
||||
NULL
|
||||
|
|
152
cmds-device.c
152
cmds-device.c
|
@ -250,11 +250,163 @@ static int cmd_scan_dev(int argc, char **argv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char * const cmd_ready_dev_usage[] = {
|
||||
"btrfs device ready <device>",
|
||||
"Check device to see if it has all of it's devices in cache for mounting",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_ready_dev(int argc, char **argv)
|
||||
{
|
||||
struct btrfs_ioctl_vol_args args;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
if (check_argc_min(argc, 2))
|
||||
usage(cmd_ready_dev_usage);
|
||||
|
||||
fd = open("/dev/btrfs-control", O_RDWR);
|
||||
if (fd < 0) {
|
||||
perror("failed to open /dev/btrfs-control");
|
||||
return 10;
|
||||
}
|
||||
|
||||
strncpy(args.name, argv[argc - 1], BTRFS_PATH_NAME_MAX);
|
||||
ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: unable to determine if the device '%s'"
|
||||
" is ready for mounting - %s\n", argv[argc - 1],
|
||||
strerror(errno));
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const cmd_dev_stats_usage[] = {
|
||||
"btrfs device stats [-z] <path>|<device>",
|
||||
"Show current device IO stats. -z to reset stats afterwards.",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_dev_stats(int argc, char **argv)
|
||||
{
|
||||
char *path;
|
||||
struct btrfs_ioctl_fs_info_args fi_args;
|
||||
struct btrfs_ioctl_dev_info_args *di_args = NULL;
|
||||
int ret;
|
||||
int fdmnt;
|
||||
int i;
|
||||
char c;
|
||||
int fdres = -1;
|
||||
int err = 0;
|
||||
__u64 flags = 0;
|
||||
|
||||
optind = 1;
|
||||
while ((c = getopt(argc, argv, "z")) != -1) {
|
||||
switch (c) {
|
||||
case 'z':
|
||||
flags = BTRFS_DEV_STATS_RESET;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
fprintf(stderr, "ERROR: device stat args invalid.\n"
|
||||
" device stat [-z] <path>|<device>\n"
|
||||
" -z to reset stats after reading.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind + 1 != argc) {
|
||||
fprintf(stderr, "ERROR: device stat needs path|device as single"
|
||||
" argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
path = argv[optind];
|
||||
|
||||
fdmnt = open_file_or_dir(path);
|
||||
if (fdmnt < 0) {
|
||||
fprintf(stderr, "ERROR: can't access '%s'\n", path);
|
||||
return 12;
|
||||
}
|
||||
|
||||
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
|
||||
if (ret) {
|
||||
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
|
||||
"%s\n", strerror(-ret));
|
||||
err = 1;
|
||||
goto out;
|
||||
}
|
||||
if (!fi_args.num_devices) {
|
||||
fprintf(stderr, "ERROR: no devices found\n");
|
||||
err = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < fi_args.num_devices; i++) {
|
||||
struct btrfs_ioctl_get_dev_stats args = {0};
|
||||
__u8 path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
|
||||
|
||||
strncpy((char *)path, (char *)di_args[i].path,
|
||||
BTRFS_DEVICE_PATH_NAME_MAX);
|
||||
path[BTRFS_DEVICE_PATH_NAME_MAX] = '\0';
|
||||
|
||||
args.devid = di_args[i].devid;
|
||||
args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
|
||||
args.flags = flags;
|
||||
|
||||
if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
|
||||
fprintf(stderr,
|
||||
"ERROR: ioctl(BTRFS_IOC_GET_DEV_STATS) on %s failed: %s\n",
|
||||
path, strerror(errno));
|
||||
err = 1;
|
||||
} else {
|
||||
if (args.nr_items >= BTRFS_DEV_STAT_WRITE_ERRS + 1)
|
||||
printf("[%s].write_io_errs %llu\n",
|
||||
path,
|
||||
(unsigned long long) args.values[
|
||||
BTRFS_DEV_STAT_WRITE_ERRS]);
|
||||
if (args.nr_items >= BTRFS_DEV_STAT_READ_ERRS + 1)
|
||||
printf("[%s].read_io_errs %llu\n",
|
||||
path,
|
||||
(unsigned long long) args.values[
|
||||
BTRFS_DEV_STAT_READ_ERRS]);
|
||||
if (args.nr_items >= BTRFS_DEV_STAT_FLUSH_ERRS + 1)
|
||||
printf("[%s].flush_io_errs %llu\n",
|
||||
path,
|
||||
(unsigned long long) args.values[
|
||||
BTRFS_DEV_STAT_FLUSH_ERRS]);
|
||||
if (args.nr_items >= BTRFS_DEV_STAT_CORRUPTION_ERRS + 1)
|
||||
printf("[%s].corruption_errs %llu\n",
|
||||
path,
|
||||
(unsigned long long) args.values[
|
||||
BTRFS_DEV_STAT_CORRUPTION_ERRS]);
|
||||
if (args.nr_items >= BTRFS_DEV_STAT_GENERATION_ERRS + 1)
|
||||
printf("[%s].generation_errs %llu\n",
|
||||
path,
|
||||
(unsigned long long) args.values[
|
||||
BTRFS_DEV_STAT_GENERATION_ERRS]);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(di_args);
|
||||
close(fdmnt);
|
||||
if (fdres > -1)
|
||||
close(fdres);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct cmd_group device_cmd_group = {
|
||||
device_cmd_group_usage, NULL, {
|
||||
{ "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 },
|
||||
{ "delete", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 },
|
||||
{ "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 },
|
||||
{ "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 },
|
||||
{ "stats", cmd_dev_stats, cmd_dev_stats_usage, NULL, 0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
}
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "kerncompat.h"
|
||||
#include "ioctl.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "commands.h"
|
||||
#include "btrfs-list.h"
|
||||
|
|
|
@ -24,26 +24,14 @@
|
|||
#include "ioctl.h"
|
||||
|
||||
#include "commands.h"
|
||||
#include "qgroup.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const char * const qgroup_cmd_group_usage[] = {
|
||||
"btrfs qgroup <command> [options] <path>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static u64 parse_qgroupid(char *p)
|
||||
{
|
||||
char *s = strchr(p, '/');
|
||||
u64 level;
|
||||
u64 id;
|
||||
|
||||
if (!s)
|
||||
return atoll(p);
|
||||
level = atoll(p);
|
||||
id = atoll(s + 1);
|
||||
|
||||
return (level << 48) | id;
|
||||
}
|
||||
|
||||
static int qgroup_assign(int assign, int argc, char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -63,7 +51,7 @@ static int qgroup_assign(int assign, int argc, char **argv)
|
|||
/*
|
||||
* FIXME src should accept subvol path
|
||||
*/
|
||||
if (args.src >= args.dst) {
|
||||
if ((args.src >> 48) >= (args.dst >> 48)) {
|
||||
fprintf(stderr, "ERROR: bad relation requested '%s'\n", path);
|
||||
return 12;
|
||||
}
|
||||
|
@ -351,7 +339,7 @@ static int cmd_qgroup_limit(int argc, char **argv)
|
|||
int ret = 0;
|
||||
int fd;
|
||||
int e;
|
||||
char *path;
|
||||
char *path = NULL;
|
||||
struct btrfs_ioctl_qgroup_limit_args args;
|
||||
unsigned long long size;
|
||||
int compressed = 0;
|
||||
|
@ -383,7 +371,6 @@ static int cmd_qgroup_limit(int argc, char **argv)
|
|||
}
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
args.qgroupid = parse_qgroupid(argv[optind + 1]);
|
||||
if (size) {
|
||||
if (compressed)
|
||||
args.lim.flags |= BTRFS_QGROUP_LIMIT_RFER_CMPR |
|
||||
|
@ -397,9 +384,8 @@ static int cmd_qgroup_limit(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
if (args.qgroupid == 0) {
|
||||
if (check_argc_exact(argc - optind, 2))
|
||||
usage(cmd_qgroup_limit_usage);
|
||||
if (argc - optind == 2) {
|
||||
args.qgroupid = 0;
|
||||
path = argv[optind + 1];
|
||||
ret = test_issubvolume(path);
|
||||
if (ret < 0) {
|
||||
|
@ -415,11 +401,11 @@ static int cmd_qgroup_limit(int argc, char **argv)
|
|||
* keep qgroupid at 0, this indicates that the subvolume the
|
||||
* fd refers to is to be limited
|
||||
*/
|
||||
} else {
|
||||
if (check_argc_exact(argc - optind, 3))
|
||||
usage(cmd_qgroup_limit_usage);
|
||||
} else if (argc - optind == 3) {
|
||||
args.qgroupid = parse_qgroupid(argv[optind + 1]);
|
||||
path = argv[optind + 2];
|
||||
}
|
||||
} else
|
||||
usage(cmd_qgroup_limit_usage);
|
||||
|
||||
fd = open_file_or_dir(path);
|
||||
if (fd < 0) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "ioctl.h"
|
||||
|
||||
#include "commands.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const char * const quota_cmd_group_usage[] = {
|
||||
"btrfs quota <command> [options] <path>",
|
||||
|
|
|
@ -881,8 +881,8 @@ static const char * const receive_cmd_group_usage[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static const char * const cmd_receive_usage[] = {
|
||||
"btrfs receive [-v] [-i <infile>] <mount>",
|
||||
const char * const cmd_receive_usage[] = {
|
||||
"btrfs receive [-v] [-f <infile>] <mount>",
|
||||
"Receive subvolumes from stdin.",
|
||||
"Receives one or more subvolumes that were previously ",
|
||||
"sent with btrfs send. The received subvolumes are stored",
|
||||
|
@ -893,7 +893,7 @@ static const char * const cmd_receive_usage[] = {
|
|||
"After receiving a subvolume, it is immediately set to",
|
||||
"read only.\n",
|
||||
"-v Enable verbose debug output. Each",
|
||||
" occurrency of this option increases the",
|
||||
" occurrence of this option increases the",
|
||||
" verbose level more.",
|
||||
"-f <infile> By default, btrfs receive uses stdin",
|
||||
" to receive the subvolumes. Use this",
|
||||
|
|
|
@ -0,0 +1,579 @@
|
|||
/*
|
||||
* Copyright (C) 2012 STRATO. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "kerncompat.h"
|
||||
#include "ctree.h"
|
||||
#include "ioctl.h"
|
||||
#include "utils.h"
|
||||
#include "volumes.h"
|
||||
#include "disk-io.h"
|
||||
|
||||
#include "commands.h"
|
||||
|
||||
|
||||
static int print_replace_status(int fd, const char *path, int once);
|
||||
static char *time2string(char *buf, size_t s, __u64 t);
|
||||
static char *progress2string(char *buf, size_t s, int progress_1000);
|
||||
|
||||
|
||||
static const char *replace_dev_result2string(__u64 result)
|
||||
{
|
||||
switch (result) {
|
||||
case BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR:
|
||||
return "no error";
|
||||
case BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED:
|
||||
return "not started";
|
||||
case BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED:
|
||||
return "already started";
|
||||
default:
|
||||
return "<illegal result value>";
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const replace_cmd_group_usage[] = {
|
||||
"btrfs replace <command> [<args>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int is_numerical(const char *str)
|
||||
{
|
||||
if (!(*str >= '0' && *str <= '9'))
|
||||
return 0;
|
||||
while (*str >= '0' && *str <= '9')
|
||||
str++;
|
||||
if (*str != '\0')
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dev_replace_cancel_fd = -1;
|
||||
static void dev_replace_sigint_handler(int signal)
|
||||
{
|
||||
struct btrfs_ioctl_dev_replace_args args = {0};
|
||||
|
||||
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
|
||||
ioctl(dev_replace_cancel_fd, BTRFS_IOC_DEV_REPLACE, &args);
|
||||
}
|
||||
|
||||
static int dev_replace_handle_sigint(int fd)
|
||||
{
|
||||
struct sigaction sa = {
|
||||
.sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
|
||||
};
|
||||
|
||||
dev_replace_cancel_fd = fd;
|
||||
return sigaction(SIGINT, &sa, NULL);
|
||||
}
|
||||
|
||||
static const char *const cmd_start_replace_usage[] = {
|
||||
"btrfs replace start srcdev|devid targetdev [-Bfr] mount_point",
|
||||
"Replace device of a btrfs filesystem.",
|
||||
"On a live filesystem, duplicate the data to the target device which",
|
||||
"is currently stored on the source device. If the source device is not",
|
||||
"available anymore, or if the -r option is set, the data is built",
|
||||
"only using the RAID redundancy mechanisms. After completion of the",
|
||||
"operation, the source device is removed from the filesystem.",
|
||||
"If the srcdev is a numerical value, it is assumed to be the device id",
|
||||
"of the filesystem which is mounted at mount_point, otherwise it is",
|
||||
"the path to the source device. If the source device is disconnected,",
|
||||
"from the system, you have to use the devid parameter format.",
|
||||
"The targetdev needs to be same size or larger than the srcdev.",
|
||||
"",
|
||||
"-r only read from srcdev if no other zero-defect mirror exists",
|
||||
" (enable this if your drive has lots of read errors, the access",
|
||||
" would be very slow)",
|
||||
"-f force using and overwriting targetdev even if it looks like",
|
||||
" containing a valid btrfs filesystem. A valid filesystem is",
|
||||
" assumed if a btrfs superblock is found which contains a",
|
||||
" correct checksum. Devices which are currently mounted are",
|
||||
" never allowed to be used as the targetdev",
|
||||
"-B do not background",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_start_replace(int argc, char **argv)
|
||||
{
|
||||
struct btrfs_ioctl_dev_replace_args start_args = {0};
|
||||
struct btrfs_ioctl_dev_replace_args status_args = {0};
|
||||
int ret;
|
||||
int i;
|
||||
int c;
|
||||
int fdmnt = -1;
|
||||
int fdsrcdev = -1;
|
||||
int fddstdev = -1;
|
||||
char *path;
|
||||
char *srcdev;
|
||||
char *dstdev;
|
||||
int avoid_reading_from_srcdev = 0;
|
||||
int force_using_targetdev = 0;
|
||||
u64 total_devs = 1;
|
||||
struct btrfs_fs_devices *fs_devices_mnt = NULL;
|
||||
struct stat st;
|
||||
u64 dstdev_block_count;
|
||||
int do_not_background = 0;
|
||||
int mixed = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "Brf")) != -1) {
|
||||
switch (c) {
|
||||
case 'B':
|
||||
do_not_background = 1;
|
||||
break;
|
||||
case 'r':
|
||||
avoid_reading_from_srcdev = 1;
|
||||
break;
|
||||
case 'f':
|
||||
force_using_targetdev = 1;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage(cmd_start_replace_usage);
|
||||
}
|
||||
}
|
||||
|
||||
start_args.start.cont_reading_from_srcdev_mode =
|
||||
avoid_reading_from_srcdev ?
|
||||
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
|
||||
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
|
||||
if (check_argc_exact(argc - optind, 3))
|
||||
usage(cmd_start_replace_usage);
|
||||
path = argv[optind + 2];
|
||||
fdmnt = open_file_or_dir(path);
|
||||
if (fdmnt < 0) {
|
||||
fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
|
||||
path, strerror(errno));
|
||||
goto leave_with_error;
|
||||
}
|
||||
|
||||
/* check for possible errors before backgrounding */
|
||||
status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
|
||||
ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
|
||||
path, strerror(errno),
|
||||
replace_dev_result2string(status_args.result));
|
||||
goto leave_with_error;
|
||||
}
|
||||
|
||||
if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
|
||||
fprintf(stderr,
|
||||
"ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
|
||||
path, replace_dev_result2string(status_args.result));
|
||||
goto leave_with_error;
|
||||
}
|
||||
|
||||
if (status_args.status.replace_state ==
|
||||
BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
|
||||
fprintf(stderr,
|
||||
"ERROR: btrfs replace on \"%s\" already started!\n",
|
||||
path);
|
||||
goto leave_with_error;
|
||||
}
|
||||
|
||||
srcdev = argv[optind];
|
||||
dstdev = argv[optind + 1];
|
||||
|
||||
if (is_numerical(srcdev)) {
|
||||
struct btrfs_ioctl_fs_info_args fi_args;
|
||||
struct btrfs_ioctl_dev_info_args *di_args = NULL;
|
||||
|
||||
if (atoi(srcdev) == 0) {
|
||||
fprintf(stderr, "Error: Failed to parse the numerical devid value '%s'\n",
|
||||
srcdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
start_args.start.srcdevid = (__u64)atoi(srcdev);
|
||||
|
||||
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
|
||||
if (ret) {
|
||||
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
|
||||
"%s\n", strerror(-ret));
|
||||
free(di_args);
|
||||
goto leave_with_error;
|
||||
}
|
||||
if (!fi_args.num_devices) {
|
||||
fprintf(stderr, "ERROR: no devices found\n");
|
||||
free(di_args);
|
||||
goto leave_with_error;
|
||||
}
|
||||
|
||||
for (i = 0; i < fi_args.num_devices; i++)
|
||||
if (start_args.start.srcdevid == di_args[i].devid)
|
||||
break;
|
||||
if (i == fi_args.num_devices) {
|
||||
fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
|
||||
srcdev, path);
|
||||
goto leave_with_error;
|
||||
}
|
||||
} else {
|
||||
fdsrcdev = open(srcdev, O_RDWR);
|
||||
if (!fdsrcdev) {
|
||||
fprintf(stderr, "Error: Unable to open device '%s'\n",
|
||||
srcdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
ret = fstat(fdsrcdev, &st);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
if (!S_ISBLK(st.st_mode)) {
|
||||
fprintf(stderr, "Error: '%s' is not a block device\n",
|
||||
srcdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
strncpy((char *)start_args.start.srcdev_name, srcdev,
|
||||
BTRFS_DEVICE_PATH_NAME_MAX);
|
||||
close(fdsrcdev);
|
||||
fdsrcdev = -1;
|
||||
start_args.start.srcdevid = 0;
|
||||
}
|
||||
|
||||
ret = check_mounted(dstdev);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error checking %s mount status\n", dstdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
if (ret == 1) {
|
||||
fprintf(stderr,
|
||||
"Error, target device %s is in use and currently mounted!\n",
|
||||
dstdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
fddstdev = open(dstdev, O_RDWR);
|
||||
if (fddstdev < 0) {
|
||||
fprintf(stderr, "Unable to open %s\n", dstdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
ret = btrfs_scan_one_device(fddstdev, dstdev, &fs_devices_mnt,
|
||||
&total_devs, BTRFS_SUPER_INFO_OFFSET);
|
||||
if (ret >= 0 && !force_using_targetdev) {
|
||||
fprintf(stderr,
|
||||
"Error, target device %s contains filesystem, use '-f' to force overwriting.\n",
|
||||
dstdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
ret = fstat(fddstdev, &st);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Error: Unable to stat '%s'\n", dstdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
if (!S_ISBLK(st.st_mode)) {
|
||||
fprintf(stderr, "Error: '%s' is not a block device\n", dstdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
strncpy((char *)start_args.start.tgtdev_name, dstdev,
|
||||
BTRFS_DEVICE_PATH_NAME_MAX);
|
||||
if (btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
|
||||
&mixed, 0)) {
|
||||
fprintf(stderr, "Error: Failed to prepare device '%s'\n",
|
||||
dstdev);
|
||||
goto leave_with_error;
|
||||
}
|
||||
close(fddstdev);
|
||||
fddstdev = -1;
|
||||
|
||||
dev_replace_handle_sigint(fdmnt);
|
||||
if (!do_not_background) {
|
||||
if (daemon(0, 0) < 0) {
|
||||
fprintf(stderr, "ERROR, backgrounding failed: %s\n",
|
||||
strerror(errno));
|
||||
goto leave_with_error;
|
||||
}
|
||||
}
|
||||
|
||||
start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
|
||||
ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
|
||||
if (do_not_background) {
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
|
||||
path, strerror(errno),
|
||||
replace_dev_result2string(start_args.result));
|
||||
goto leave_with_error;
|
||||
}
|
||||
|
||||
if (start_args.result !=
|
||||
BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
|
||||
fprintf(stderr,
|
||||
"ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
|
||||
path,
|
||||
replace_dev_result2string(start_args.result));
|
||||
goto leave_with_error;
|
||||
}
|
||||
}
|
||||
close(fdmnt);
|
||||
return 0;
|
||||
|
||||
leave_with_error:
|
||||
if (fdmnt != -1)
|
||||
close(fdmnt);
|
||||
if (fdsrcdev != -1)
|
||||
close(fdsrcdev);
|
||||
if (fddstdev != -1)
|
||||
close(fddstdev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *const cmd_status_replace_usage[] = {
|
||||
"btrfs replace status mount_point [-1]",
|
||||
"Print status and progress information of a running device replace",
|
||||
"operation",
|
||||
"",
|
||||
"-1 print once instead of print continously until the replace",
|
||||
" operation finishes (or is canceled)",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_status_replace(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
int e;
|
||||
int c;
|
||||
char *path;
|
||||
int once = 0;
|
||||
int ret;
|
||||
|
||||
while ((c = getopt(argc, argv, "1")) != -1) {
|
||||
switch (c) {
|
||||
case '1':
|
||||
once = 1;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage(cmd_status_replace_usage);
|
||||
}
|
||||
}
|
||||
|
||||
if (check_argc_exact(argc - optind, 1))
|
||||
usage(cmd_status_replace_usage);
|
||||
|
||||
path = argv[optind];
|
||||
fd = open_file_or_dir(path);
|
||||
e = errno;
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
|
||||
path, strerror(e));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = print_replace_status(fd, path, once);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int print_replace_status(int fd, const char *path, int once)
|
||||
{
|
||||
struct btrfs_ioctl_dev_replace_args args = {0};
|
||||
struct btrfs_ioctl_dev_replace_status_params *status;
|
||||
int ret;
|
||||
int prevent_loop = 0;
|
||||
int skip_stats;
|
||||
int num_chars;
|
||||
char string1[80];
|
||||
char string2[80];
|
||||
char string3[80];
|
||||
|
||||
for (;;) {
|
||||
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
|
||||
ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
|
||||
if (ret) {
|
||||
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
|
||||
path, strerror(errno),
|
||||
replace_dev_result2string(args.result));
|
||||
return ret;
|
||||
}
|
||||
|
||||
status = &args.status;
|
||||
if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
|
||||
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
|
||||
path,
|
||||
replace_dev_result2string(args.result));
|
||||
return -1;
|
||||
}
|
||||
|
||||
skip_stats = 0;
|
||||
num_chars = 0;
|
||||
switch (status->replace_state) {
|
||||
case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
|
||||
num_chars =
|
||||
printf("%s done",
|
||||
progress2string(string3,
|
||||
sizeof(string3),
|
||||
status->progress_1000));
|
||||
break;
|
||||
case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
|
||||
prevent_loop = 1;
|
||||
printf("Started on %s, finished on %s",
|
||||
time2string(string1, sizeof(string1),
|
||||
status->time_started),
|
||||
time2string(string2, sizeof(string2),
|
||||
status->time_stopped));
|
||||
break;
|
||||
case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
|
||||
prevent_loop = 1;
|
||||
printf("Started on %s, canceled on %s at %s",
|
||||
time2string(string1, sizeof(string1),
|
||||
status->time_started),
|
||||
time2string(string2, sizeof(string2),
|
||||
status->time_stopped),
|
||||
progress2string(string3, sizeof(string3),
|
||||
status->progress_1000));
|
||||
break;
|
||||
case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
|
||||
prevent_loop = 1;
|
||||
printf("Started on %s, suspended on %s at %s",
|
||||
time2string(string1, sizeof(string1),
|
||||
status->time_started),
|
||||
time2string(string2, sizeof(string2),
|
||||
status->time_stopped),
|
||||
progress2string(string3, sizeof(string3),
|
||||
status->progress_1000));
|
||||
break;
|
||||
case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
|
||||
prevent_loop = 1;
|
||||
skip_stats = 1;
|
||||
printf("Never started");
|
||||
break;
|
||||
default:
|
||||
prevent_loop = 1;
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!skip_stats)
|
||||
num_chars += printf(
|
||||
", %llu write errs, %llu uncorr. read errs",
|
||||
(unsigned long long)status->num_write_errors,
|
||||
(unsigned long long)
|
||||
status->num_uncorrectable_read_errors);
|
||||
if (once || prevent_loop) {
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
sleep(1);
|
||||
while (num_chars > 0) {
|
||||
putchar('\b');
|
||||
num_chars--;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
time2string(char *buf, size_t s, __u64 t)
|
||||
{
|
||||
struct tm t_tm;
|
||||
time_t t_time_t;
|
||||
|
||||
t_time_t = (time_t)t;
|
||||
assert((__u64)t_time_t == t);
|
||||
localtime_r(&t_time_t, &t_tm);
|
||||
strftime(buf, s, "%e.%b %T", &t_tm);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *
|
||||
progress2string(char *buf, size_t s, int progress_1000)
|
||||
{
|
||||
snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
|
||||
assert(s > 0);
|
||||
buf[s - 1] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *const cmd_cancel_replace_usage[] = {
|
||||
"btrfs replace cancel mount_point",
|
||||
"Cancel a running device replace operation.",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_cancel_replace(int argc, char **argv)
|
||||
{
|
||||
struct btrfs_ioctl_dev_replace_args args = {0};
|
||||
int ret;
|
||||
int c;
|
||||
int fd;
|
||||
int e;
|
||||
char *path;
|
||||
|
||||
while ((c = getopt(argc, argv, "")) != -1) {
|
||||
switch (c) {
|
||||
case '?':
|
||||
default:
|
||||
usage(cmd_cancel_replace_usage);
|
||||
}
|
||||
}
|
||||
|
||||
if (check_argc_exact(argc - optind, 1))
|
||||
usage(cmd_cancel_replace_usage);
|
||||
|
||||
path = argv[optind];
|
||||
fd = open_file_or_dir(path);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
|
||||
path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
|
||||
ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
|
||||
e = errno;
|
||||
close(fd);
|
||||
if (ret) {
|
||||
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
|
||||
path, strerror(e),
|
||||
replace_dev_result2string(args.result));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct cmd_group replace_cmd_group = {
|
||||
replace_cmd_group_usage, NULL, {
|
||||
{ "start", cmd_start_replace, cmd_start_replace_usage, NULL,
|
||||
0 },
|
||||
{ "status", cmd_status_replace, cmd_status_replace_usage, NULL,
|
||||
0 },
|
||||
{ "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
|
||||
0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
}
|
||||
};
|
||||
|
||||
int cmd_replace(int argc, char **argv)
|
||||
{
|
||||
return handle_command_group(&replace_cmd_group, argc, argv);
|
||||
}
|
94
cmds-scrub.c
94
cmds-scrub.c
|
@ -969,89 +969,6 @@ static struct scrub_file_record *last_dev_scrub(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int scrub_device_info(int fd, u64 devid,
|
||||
struct btrfs_ioctl_dev_info_args *di_args)
|
||||
{
|
||||
int ret;
|
||||
|
||||
di_args->devid = devid;
|
||||
memset(&di_args->uuid, '\0', sizeof(di_args->uuid));
|
||||
|
||||
ret = ioctl(fd, BTRFS_IOC_DEV_INFO, di_args);
|
||||
return ret ? -errno : 0;
|
||||
}
|
||||
|
||||
static int scrub_fs_info(char *path,
|
||||
struct btrfs_ioctl_fs_info_args *fi_args,
|
||||
struct btrfs_ioctl_dev_info_args **di_ret)
|
||||
{
|
||||
int ret = 0;
|
||||
int ndevs = 0;
|
||||
int i = 1;
|
||||
int fd;
|
||||
struct btrfs_fs_devices *fs_devices_mnt = NULL;
|
||||
struct btrfs_ioctl_dev_info_args *di_args;
|
||||
char mp[BTRFS_PATH_NAME_MAX + 1];
|
||||
|
||||
memset(fi_args, 0, sizeof(*fi_args));
|
||||
|
||||
fd = open_file_or_dir(path);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "ERROR: can't access to '%s'\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
|
||||
if (ret && errno == EINVAL) {
|
||||
/* path is no mounted btrfs. try if it's a device */
|
||||
ret = check_mounted_where(fd, path, mp, sizeof(mp),
|
||||
&fs_devices_mnt);
|
||||
if (!ret)
|
||||
return -EINVAL;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
fi_args->num_devices = 1;
|
||||
fi_args->max_id = fs_devices_mnt->latest_devid;
|
||||
i = fs_devices_mnt->latest_devid;
|
||||
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
|
||||
close(fd);
|
||||
fd = open_file_or_dir(mp);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
} else if (ret) {
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!fi_args->num_devices) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
|
||||
if (!di_args) {
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (; i <= fi_args->max_id; ++i) {
|
||||
BUG_ON(ndevs >= fi_args->num_devices);
|
||||
ret = scrub_device_info(fd, i, &di_args[ndevs]);
|
||||
if (ret == -ENODEV)
|
||||
continue;
|
||||
if (ret) {
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
++ndevs;
|
||||
}
|
||||
|
||||
BUG_ON(ndevs == 0);
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkdir_p(char *path)
|
||||
{
|
||||
int i;
|
||||
|
@ -1172,7 +1089,7 @@ static int scrub_start(int argc, char **argv, int resume)
|
|||
return 12;
|
||||
}
|
||||
|
||||
ret = scrub_fs_info(path, &fi_args, &di_args);
|
||||
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
|
||||
if (ret) {
|
||||
ERR(!do_quiet, "ERROR: getting dev info for scrub failed: "
|
||||
"%s\n", strerror(-ret));
|
||||
|
@ -1604,6 +1521,7 @@ static int cmd_scrub_status(int argc, char **argv)
|
|||
};
|
||||
int ret;
|
||||
int i;
|
||||
int fdmnt;
|
||||
int print_raw = 0;
|
||||
int do_stats_per_dev = 0;
|
||||
int c;
|
||||
|
@ -1631,7 +1549,13 @@ static int cmd_scrub_status(int argc, char **argv)
|
|||
|
||||
path = argv[optind];
|
||||
|
||||
ret = scrub_fs_info(path, &fi_args, &di_args);
|
||||
fdmnt = open_file_or_dir(path);
|
||||
if (fdmnt < 0) {
|
||||
fprintf(stderr, "ERROR: can't access to '%s'\n", path);
|
||||
return 12;
|
||||
}
|
||||
|
||||
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
|
||||
if (ret) {
|
||||
fprintf(stderr, "ERROR: getting dev info for scrub failed: "
|
||||
"%s\n", strerror(-ret));
|
||||
|
|
110
cmds-send.c
110
cmds-send.c
|
@ -152,7 +152,7 @@ static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
|
|||
if (tmp < 0)
|
||||
tmp *= -1;
|
||||
if (tmp < best_diff) {
|
||||
best_parent = parent;
|
||||
best_parent = parent2;
|
||||
best_diff = tmp;
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ out:
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
|
||||
static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id)
|
||||
{
|
||||
int ret;
|
||||
pthread_t t_read;
|
||||
|
@ -273,6 +273,7 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
|
|||
goto out;
|
||||
}
|
||||
|
||||
memset(&io_send, 0, sizeof(io_send));
|
||||
io_send.send_fd = pipefd[1];
|
||||
send->send_fd = pipefd[0];
|
||||
|
||||
|
@ -288,7 +289,7 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
|
|||
|
||||
io_send.clone_sources = (__u64*)send->clone_sources;
|
||||
io_send.clone_sources_count = send->clone_sources_count;
|
||||
io_send.parent_root = parent_root;
|
||||
io_send.parent_root = parent_root_id;
|
||||
ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
|
@ -333,12 +334,12 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
|
||||
char *get_subvol_name(char *mnt, char *full_path)
|
||||
{
|
||||
int len = strlen(s->root_path);
|
||||
int len = strlen(mnt);
|
||||
if (!len)
|
||||
return full_path;
|
||||
if (s->root_path[len - 1] != '/')
|
||||
if (mnt[len - 1] != '/')
|
||||
len += 1;
|
||||
|
||||
return full_path + len;
|
||||
|
@ -425,21 +426,17 @@ int cmd_send_start(int argc, char **argv)
|
|||
char *snapshot_parent = NULL;
|
||||
u64 root_id;
|
||||
u64 parent_root_id = 0;
|
||||
int full_send = 1;
|
||||
|
||||
memset(&send, 0, sizeof(send));
|
||||
send.dump_fd = fileno(stdout);
|
||||
|
||||
if (isatty(send.dump_fd)) {
|
||||
fprintf(stderr, "ERROR: not dumping send stream into a terminal, redirect it into a file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
g_verbose++;
|
||||
break;
|
||||
case 'i': {
|
||||
case 'c':
|
||||
subvol = realpath(optarg, NULL);
|
||||
if (!subvol) {
|
||||
ret = -errno;
|
||||
|
@ -452,7 +449,7 @@ int cmd_send_start(int argc, char **argv)
|
|||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = get_root_id(&send, get_subvol_name(&send, subvol),
|
||||
ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
|
||||
&root_id);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: could not resolve "
|
||||
|
@ -461,12 +458,16 @@ int cmd_send_start(int argc, char **argv)
|
|||
}
|
||||
add_clone_source(&send, root_id);
|
||||
free(subvol);
|
||||
full_send = 0;
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
outname = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
if (snapshot_parent) {
|
||||
fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
|
||||
return 1;
|
||||
}
|
||||
snapshot_parent = realpath(optarg, NULL);
|
||||
if (!snapshot_parent) {
|
||||
ret = -errno;
|
||||
|
@ -474,7 +475,12 @@ int cmd_send_start(int argc, char **argv)
|
|||
"%s\n", optarg, strerror(-ret));
|
||||
goto out;
|
||||
}
|
||||
full_send = 0;
|
||||
break;
|
||||
case 'i':
|
||||
fprintf(stderr,
|
||||
"ERROR: -i was removed, use -c instead\n");
|
||||
return 1;
|
||||
case '?':
|
||||
default:
|
||||
fprintf(stderr, "ERROR: send args invalid.\n");
|
||||
|
@ -497,6 +503,13 @@ int cmd_send_start(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
if (isatty(send.dump_fd)) {
|
||||
fprintf(stderr,
|
||||
"ERROR: not dumping send stream into a terminal, "
|
||||
"redirect it into a file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* use first send subvol to determine mount_root */
|
||||
subvol = argv[optind];
|
||||
|
||||
|
@ -513,7 +526,7 @@ int cmd_send_start(int argc, char **argv)
|
|||
|
||||
if (snapshot_parent != NULL) {
|
||||
ret = get_root_id(&send,
|
||||
get_subvol_name(&send, snapshot_parent),
|
||||
get_subvol_name(send.root_path, snapshot_parent),
|
||||
&parent_root_id);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: could not resolve root_id "
|
||||
|
@ -572,7 +585,7 @@ int cmd_send_start(int argc, char **argv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = get_root_id(&send, get_subvol_name(&send, subvol),
|
||||
ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
|
||||
&root_id);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: could not resolve root_id "
|
||||
|
@ -580,10 +593,13 @@ int cmd_send_start(int argc, char **argv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (!parent_root_id) {
|
||||
if (!full_send && !parent_root_id) {
|
||||
ret = find_good_parent(&send, root_id, &parent_root_id);
|
||||
if (ret < 0)
|
||||
parent_root_id = 0;
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: parent determination failed for %lld\n",
|
||||
root_id);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = is_subvol_ro(&send, subvol);
|
||||
|
@ -604,6 +620,7 @@ int cmd_send_start(int argc, char **argv)
|
|||
add_clone_source(&send, root_id);
|
||||
|
||||
parent_root_id = 0;
|
||||
full_send = 0;
|
||||
free(subvol);
|
||||
}
|
||||
|
||||
|
@ -620,43 +637,32 @@ static const char * const send_cmd_group_usage[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static const char * const cmd_send_usage[] = {
|
||||
"btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
|
||||
const char * const cmd_send_usage[] = {
|
||||
"btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
|
||||
"Send the subvolume to stdout.",
|
||||
"Sends the subvolume specified by <subvol> to stdout.",
|
||||
"By default, this will send the whole subvolume. To do",
|
||||
"an incremental send, one or multiple '-i <clone_source>'",
|
||||
"arguments have to be specified. A 'clone source' is",
|
||||
"a subvolume that is known to exist on the receiving",
|
||||
"side in exactly the same state as on the sending side.\n",
|
||||
"Normally, a good snapshot parent is searched automatically",
|
||||
"in the list of 'clone sources'. To override this, use",
|
||||
"'-p <parent>' to manually specify a snapshot parent.",
|
||||
"A manually specified snapshot parent is also regarded",
|
||||
"as 'clone source'.\n",
|
||||
"-v Enable verbose debug output. Each",
|
||||
" occurrency of this option increases the",
|
||||
" verbose level more.",
|
||||
"-i <subvol> Informs btrfs send that this subvolume,",
|
||||
" can be taken as 'clone source'. This can",
|
||||
" be used for incremental sends.",
|
||||
"-p <subvol> Disable automatic snaphot parent",
|
||||
" determination and use <subvol> as parent.",
|
||||
" This subvolume is also added to the list",
|
||||
" of 'clone sources' (see -i).",
|
||||
"-f <outfile> Output is normally written to stdout.",
|
||||
" To write to a file, use this option.",
|
||||
" An alternative would be to use pipes.",
|
||||
"By default, this will send the whole subvolume. To do an incremental",
|
||||
"send, use '-p <parent>'. If you want to allow btrfs to clone from",
|
||||
"any additional local snapshots, use -c <clone-src> (multiple times",
|
||||
"where applicable). You must not specify clone sources unless you",
|
||||
"guarantee that these snapshots are exactly in the same state on both",
|
||||
"sides, the sender and the receiver. It is allowed to omit the",
|
||||
"'-p <parent>' option when '-c <clone-src>' options are given, in",
|
||||
"which case 'btrfs send' will determine a suitable parent among the",
|
||||
"clone sources itself.",
|
||||
"\n",
|
||||
"-v Enable verbose debug output. Each occurrence of",
|
||||
" this option increases the verbose level more.",
|
||||
"-p <parent> Send an incremental stream from <parent> to",
|
||||
" <subvol>.",
|
||||
"-c <clone-src> Use this snapshot as a clone source for an ",
|
||||
" incremental send (multiple allowed)",
|
||||
"-f <outfile> Output is normally written to stdout. To write to",
|
||||
" a file, use this option. An alternative would be to",
|
||||
" use pipes.",
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct cmd_group send_cmd_group = {
|
||||
send_cmd_group_usage, NULL, {
|
||||
{ "send", cmd_send_start, cmd_send_usage, NULL, 0 },
|
||||
{ 0, 0, 0, 0, 0 },
|
||||
},
|
||||
};
|
||||
|
||||
int cmd_send(int argc, char **argv)
|
||||
{
|
||||
return cmd_send_start(argc, argv);
|
||||
|
|
284
cmds-subvolume.c
284
cmds-subvolume.c
|
@ -24,6 +24,7 @@
|
|||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <getopt.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include "kerncompat.h"
|
||||
#include "ioctl.h"
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include "ctree.h"
|
||||
#include "commands.h"
|
||||
#include "btrfs-list.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const char * const subvolume_cmd_group_usage[] = {
|
||||
"btrfs subvolume <command> <args>",
|
||||
|
@ -271,21 +273,32 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Naming of options:
|
||||
* - uppercase for filters and sort options
|
||||
* - lowercase for enabling specific items in the output
|
||||
*/
|
||||
static const char * const cmd_subvol_list_usage[] = {
|
||||
"btrfs subvolume list [-apurts] [-g [+|-]value] [-c [+|-]value] "
|
||||
"btrfs subvolume list [-agopurts] [-G [+|-]value] [-C [+|-]value] "
|
||||
"[--sort=gen,ogen,rootid,path] <path>",
|
||||
"List subvolumes (and snapshots)",
|
||||
"",
|
||||
"-p print parent ID",
|
||||
"-a print all the subvolumes in the filesystem.",
|
||||
"-a print all the subvolumes in the filesystem and",
|
||||
" distinguish absolute and relative path with respect",
|
||||
" to the given <path>",
|
||||
"-c print the ogeneration of the subvolume",
|
||||
"-g print the generation of the subvolume",
|
||||
"-o print only subvolumes bellow specified path",
|
||||
"-u print the uuid of subvolumes (and snapshots)",
|
||||
"-q print the parent uuid of the snapshots",
|
||||
"-t print the result as a table",
|
||||
"-s list snapshots only in the filesystem",
|
||||
"-r list readonly subvolumes (including snapshots)",
|
||||
"-g [+|-]value",
|
||||
"-G [+|-]value",
|
||||
" filter the subvolumes by generation",
|
||||
" (+value: >= value; -value: <= value; value: = value)",
|
||||
"-c [+|-]value",
|
||||
"-C [+|-]value",
|
||||
" filter the subvolumes by ogeneration",
|
||||
" (+value: >= value; -value: <= value; value: = value)",
|
||||
"--sort=gen,ogen,rootid,path",
|
||||
|
@ -300,13 +313,14 @@ static int cmd_subvol_list(int argc, char **argv)
|
|||
struct btrfs_list_filter_set *filter_set;
|
||||
struct btrfs_list_comparer_set *comparer_set;
|
||||
u64 flags = 0;
|
||||
int fd;
|
||||
int fd = -1;
|
||||
u64 top_id;
|
||||
int ret;
|
||||
int ret = -1, uerr = 0;
|
||||
int c;
|
||||
char *subvol;
|
||||
int is_tab_result = 0;
|
||||
int is_list_all = 0;
|
||||
int is_only_in_path = 0;
|
||||
struct option long_options[] = {
|
||||
{"sort", 1, NULL, 'S'},
|
||||
{0, 0, 0, 0}
|
||||
|
@ -318,7 +332,7 @@ static int cmd_subvol_list(int argc, char **argv)
|
|||
optind = 1;
|
||||
while(1) {
|
||||
c = getopt_long(argc, argv,
|
||||
"apsurg:c:t", long_options, NULL);
|
||||
"acgopqsurG:C:t", long_options, NULL);
|
||||
if (c < 0)
|
||||
break;
|
||||
|
||||
|
@ -329,6 +343,15 @@ static int cmd_subvol_list(int argc, char **argv)
|
|||
case 'a':
|
||||
is_list_all = 1;
|
||||
break;
|
||||
case 'c':
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
|
||||
break;
|
||||
case 'g':
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
|
||||
break;
|
||||
case 'o':
|
||||
is_only_in_path = 1;
|
||||
break;
|
||||
case 't':
|
||||
is_tab_result = 1;
|
||||
break;
|
||||
|
@ -338,39 +361,49 @@ static int cmd_subvol_list(int argc, char **argv)
|
|||
0);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
|
||||
|
||||
break;
|
||||
case 'u':
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_UUID);
|
||||
break;
|
||||
case 'q':
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
|
||||
break;
|
||||
case 'r':
|
||||
flags |= BTRFS_ROOT_SUBVOL_RDONLY;
|
||||
break;
|
||||
case 'g':
|
||||
case 'G':
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
|
||||
ret = btrfs_list_parse_filter_string(optarg,
|
||||
&filter_set,
|
||||
BTRFS_LIST_FILTER_GEN);
|
||||
if (ret)
|
||||
usage(cmd_subvol_list_usage);
|
||||
if (ret) {
|
||||
uerr = 1;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
case 'C':
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
|
||||
ret = btrfs_list_parse_filter_string(optarg,
|
||||
&filter_set,
|
||||
BTRFS_LIST_FILTER_CGEN);
|
||||
if (ret)
|
||||
usage(cmd_subvol_list_usage);
|
||||
if (ret) {
|
||||
uerr = 1;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
ret = btrfs_list_parse_sort_string(optarg,
|
||||
&comparer_set);
|
||||
if (ret)
|
||||
usage(cmd_subvol_list_usage);
|
||||
if (ret) {
|
||||
uerr = 1;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
usage(cmd_subvol_list_usage);
|
||||
uerr = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,38 +411,66 @@ static int cmd_subvol_list(int argc, char **argv)
|
|||
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
|
||||
flags);
|
||||
|
||||
if (check_argc_exact(argc - optind, 1))
|
||||
usage(cmd_subvol_list_usage);
|
||||
if (check_argc_exact(argc - optind, 1)) {
|
||||
uerr = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
subvol = argv[optind];
|
||||
|
||||
ret = test_issubvolume(subvol);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
|
||||
return 12;
|
||||
goto out;
|
||||
}
|
||||
if (!ret) {
|
||||
fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
|
||||
return 13;
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = open_file_or_dir(subvol);
|
||||
if (fd < 0) {
|
||||
ret = -1;
|
||||
fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
|
||||
return 12;
|
||||
goto out;
|
||||
}
|
||||
|
||||
top_id = btrfs_list_get_path_rootid(fd);
|
||||
if (!is_list_all)
|
||||
|
||||
if (is_list_all)
|
||||
btrfs_list_setup_filter(&filter_set,
|
||||
BTRFS_LIST_FILTER_FULL_PATH,
|
||||
top_id);
|
||||
else if (is_only_in_path)
|
||||
btrfs_list_setup_filter(&filter_set,
|
||||
BTRFS_LIST_FILTER_TOPID_EQUAL,
|
||||
top_id);
|
||||
|
||||
ret = btrfs_list_subvols(fd, filter_set, comparer_set,
|
||||
is_tab_result);
|
||||
if (ret)
|
||||
return 19;
|
||||
return 0;
|
||||
/* by default we shall print the following columns*/
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
|
||||
|
||||
if (is_tab_result)
|
||||
ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
|
||||
BTRFS_LIST_LAYOUT_TABLE,
|
||||
!is_list_all && !is_only_in_path, NULL);
|
||||
else
|
||||
ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
|
||||
BTRFS_LIST_LAYOUT_DEFAULT,
|
||||
!is_list_all && !is_only_in_path, NULL);
|
||||
|
||||
out:
|
||||
if (filter_set)
|
||||
btrfs_list_free_filter_set(filter_set);
|
||||
if (comparer_set)
|
||||
btrfs_list_free_comparer_set(comparer_set);
|
||||
if (uerr)
|
||||
usage(cmd_subvol_list_usage);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const cmd_snapshot_usage[] = {
|
||||
|
@ -612,7 +673,17 @@ static int cmd_subvol_get_default(int argc, char **argv)
|
|||
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
|
||||
default_id);
|
||||
|
||||
ret = btrfs_list_subvols(fd, filter_set, NULL, 0);
|
||||
/* by default we shall print the following columns*/
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
|
||||
|
||||
ret = btrfs_list_subvols_print(fd, filter_set, NULL,
|
||||
BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
|
||||
|
||||
if (filter_set)
|
||||
btrfs_list_free_filter_set(filter_set);
|
||||
if (ret)
|
||||
return 19;
|
||||
return 0;
|
||||
|
@ -699,6 +770,160 @@ static int cmd_find_new(int argc, char **argv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char * const cmd_subvol_show_usage[] = {
|
||||
"btrfs subvolume show <subvol-path>",
|
||||
"Show more information of the subvolume",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_subvol_show(int argc, char **argv)
|
||||
{
|
||||
struct root_info get_ri;
|
||||
struct btrfs_list_filter_set *filter_set;
|
||||
char tstr[256];
|
||||
char uuidparse[37];
|
||||
char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
|
||||
char raw_prefix[] = "\t\t\t\t";
|
||||
u64 sv_id, mntid;
|
||||
int fd = -1, mntfd = -1;
|
||||
int ret = -1;
|
||||
|
||||
if (check_argc_exact(argc, 2))
|
||||
usage(cmd_subvol_show_usage);
|
||||
|
||||
fullpath = realpath(argv[1], 0);
|
||||
if (!fullpath) {
|
||||
fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
|
||||
argv[1], strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = test_issubvolume(fullpath);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
|
||||
goto out;
|
||||
}
|
||||
if (!ret) {
|
||||
fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = find_mount_root(fullpath, &mnt);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: find_mount_root failed on %s: "
|
||||
"%s\n", fullpath, strerror(-ret));
|
||||
goto out;
|
||||
}
|
||||
ret = -1;
|
||||
svpath = get_subvol_name(mnt, fullpath);
|
||||
|
||||
fd = open_file_or_dir(fullpath);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sv_id = btrfs_list_get_path_rootid(fd);
|
||||
if (sv_id < 0) {
|
||||
fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
|
||||
fullpath);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mntfd = open_file_or_dir(mnt);
|
||||
if (mntfd < 0) {
|
||||
fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mntid = btrfs_list_get_path_rootid(mntfd);
|
||||
if (mntid < 0) {
|
||||
fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sv_id == BTRFS_FS_TREE_OBJECTID) {
|
||||
printf("%s is btrfs root\n", fullpath);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(&get_ri, 0, sizeof(get_ri));
|
||||
get_ri.root_id = sv_id;
|
||||
|
||||
if (btrfs_get_subvol(mntfd, &get_ri)) {
|
||||
fprintf(stderr, "ERROR: can't find '%s'\n",
|
||||
svpath);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
/* print the info */
|
||||
printf("%s\n", fullpath);
|
||||
printf("\tName: \t\t\t%s\n", get_ri.name);
|
||||
|
||||
if (uuid_is_null(get_ri.uuid))
|
||||
strcpy(uuidparse, "-");
|
||||
else
|
||||
uuid_unparse(get_ri.uuid, uuidparse);
|
||||
printf("\tuuid: \t\t\t%s\n", uuidparse);
|
||||
|
||||
if (uuid_is_null(get_ri.puuid))
|
||||
strcpy(uuidparse, "-");
|
||||
else
|
||||
uuid_unparse(get_ri.puuid, uuidparse);
|
||||
printf("\tParent uuid: \t\t%s\n", uuidparse);
|
||||
|
||||
if (get_ri.otime)
|
||||
strftime(tstr, 256, "%Y-%m-%d %X",
|
||||
localtime(&get_ri.otime));
|
||||
else
|
||||
strcpy(tstr, "-");
|
||||
printf("\tCreation time: \t\t%s\n", tstr);
|
||||
|
||||
printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
|
||||
printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
|
||||
printf("\tGen at creation: \t%llu\n", get_ri.ogen);
|
||||
printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
|
||||
printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
|
||||
|
||||
if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
|
||||
printf("\tFlags: \t\t\treadonly\n");
|
||||
else
|
||||
printf("\tFlags: \t\t\t-\n");
|
||||
|
||||
/* print the snapshots of the given subvol if any*/
|
||||
printf("\tSnapshot(s):\n");
|
||||
filter_set = btrfs_list_alloc_filter_set();
|
||||
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
|
||||
(u64)get_ri.uuid);
|
||||
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
|
||||
btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
|
||||
1, raw_prefix);
|
||||
|
||||
/* clean up */
|
||||
if (get_ri.path)
|
||||
free(get_ri.path);
|
||||
if (get_ri.name)
|
||||
free(get_ri.name);
|
||||
if (get_ri.full_path)
|
||||
free(get_ri.full_path);
|
||||
if (filter_set)
|
||||
btrfs_list_free_filter_set(filter_set);
|
||||
|
||||
out:
|
||||
if (mntfd >= 0)
|
||||
close(mntfd);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (mnt)
|
||||
free(mnt);
|
||||
if (fullpath)
|
||||
free(fullpath);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct cmd_group subvolume_cmd_group = {
|
||||
subvolume_cmd_group_usage, NULL, {
|
||||
{ "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
|
||||
|
@ -710,6 +935,7 @@ const struct cmd_group subvolume_cmd_group = {
|
|||
{ "set-default", cmd_subvol_set_default,
|
||||
cmd_subvol_set_default_usage, NULL, 0 },
|
||||
{ "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
|
||||
{ "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
}
|
||||
};
|
||||
|
|
12
commands.h
12
commands.h
|
@ -79,9 +79,6 @@ void help_ambiguous_token(const char *arg, const struct cmd_group *grp);
|
|||
|
||||
void help_command_group(const struct cmd_group *grp, int argc, char **argv);
|
||||
|
||||
/* common.c */
|
||||
int open_file_or_dir(const char *fname);
|
||||
|
||||
extern const struct cmd_group subvolume_cmd_group;
|
||||
extern const struct cmd_group filesystem_cmd_group;
|
||||
extern const struct cmd_group balance_cmd_group;
|
||||
|
@ -92,6 +89,10 @@ extern const struct cmd_group send_cmd_group;
|
|||
extern const struct cmd_group receive_cmd_group;
|
||||
extern const struct cmd_group quota_cmd_group;
|
||||
extern const struct cmd_group qgroup_cmd_group;
|
||||
extern const struct cmd_group replace_cmd_group;
|
||||
|
||||
extern const char * const cmd_send_usage[];
|
||||
extern const char * const cmd_receive_usage[];
|
||||
|
||||
int cmd_subvolume(int argc, char **argv);
|
||||
int cmd_filesystem(int argc, char **argv);
|
||||
|
@ -103,6 +104,11 @@ int cmd_send(int argc, char **argv);
|
|||
int cmd_receive(int argc, char **argv);
|
||||
int cmd_quota(int argc, char **argv);
|
||||
int cmd_qgroup(int argc, char **argv);
|
||||
int cmd_replace(int argc, char **argv);
|
||||
|
||||
/* subvolume exported functions */
|
||||
int test_issubvolume(char *path);
|
||||
|
||||
/* send.c */
|
||||
int find_mount_root(const char *path, char **mount_root);
|
||||
char *get_subvol_name(char *mnt, char *full_path);
|
||||
|
|
46
common.c
46
common.c
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int open_file_or_dir(const char *fname)
|
||||
{
|
||||
int ret;
|
||||
struct stat st;
|
||||
DIR *dirstream;
|
||||
int fd;
|
||||
|
||||
ret = stat(fname, &st);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
dirstream = opendir(fname);
|
||||
if (!dirstream) {
|
||||
return -2;
|
||||
}
|
||||
fd = dirfd(dirstream);
|
||||
} else {
|
||||
fd = open(fname, O_RDWR);
|
||||
}
|
||||
if (fd < 0) {
|
||||
return -3;
|
||||
}
|
||||
return fd;
|
||||
}
|
45
ctree.h
45
ctree.h
|
@ -122,6 +122,13 @@ struct btrfs_trans_handle;
|
|||
*/
|
||||
#define BTRFS_NAME_LEN 255
|
||||
|
||||
/*
|
||||
* Theoretical limit is larger, but we keep this down to a sane
|
||||
* value. That should limit greatly the possibility of collisions on
|
||||
* inode ref items.
|
||||
*/
|
||||
#define BTRFS_LINK_MAX 65535U
|
||||
|
||||
/* 32 bytes in various csum fields */
|
||||
#define BTRFS_CSUM_SIZE 32
|
||||
|
||||
|
@ -424,6 +431,7 @@ struct btrfs_super_block {
|
|||
#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
|
||||
#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS (1ULL << 2)
|
||||
#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO (1ULL << 3)
|
||||
|
||||
/*
|
||||
* some patches floated around with a second compression method
|
||||
* lets save that incompat here for when they do get in
|
||||
|
@ -439,6 +447,7 @@ struct btrfs_super_block {
|
|||
#define BTRFS_FEATURE_INCOMPAT_BIG_METADATA (1ULL << 5)
|
||||
#define BTRFS_FEATURE_INCOMPAT_RAID56 (1ULL << 7)
|
||||
|
||||
#define BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF (1ULL << 6)
|
||||
|
||||
#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
|
||||
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
|
||||
|
@ -447,6 +456,7 @@ struct btrfs_super_block {
|
|||
BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \
|
||||
BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \
|
||||
BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \
|
||||
BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \
|
||||
BTRFS_FEATURE_INCOMPAT_RAID56 | \
|
||||
BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
|
||||
|
||||
|
@ -587,6 +597,13 @@ struct btrfs_inode_ref {
|
|||
/* name goes here */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_inode_extref {
|
||||
__le64 parent_objectid;
|
||||
__le64 index;
|
||||
__le16 name_len;
|
||||
__u8 name[0]; /* name goes here */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_timespec {
|
||||
__le64 sec;
|
||||
__le32 nsec;
|
||||
|
@ -964,6 +981,7 @@ struct btrfs_root {
|
|||
*/
|
||||
#define BTRFS_INODE_ITEM_KEY 1
|
||||
#define BTRFS_INODE_REF_KEY 12
|
||||
#define BTRFS_INODE_EXTREF_KEY 13
|
||||
#define BTRFS_XATTR_ITEM_KEY 24
|
||||
#define BTRFS_ORPHAN_ITEM_KEY 48
|
||||
|
||||
|
@ -1048,6 +1066,18 @@ struct btrfs_root {
|
|||
#define BTRFS_QGROUP_LIMIT_KEY 244
|
||||
#define BTRFS_QGROUP_RELATION_KEY 246
|
||||
|
||||
/*
|
||||
* Persistently stores the io stats in the device tree.
|
||||
* One key for all stats, (0, BTRFS_DEV_STATS_KEY, devid).
|
||||
*/
|
||||
#define BTRFS_DEV_STATS_KEY 249
|
||||
|
||||
/*
|
||||
* Persistently stores the device replace state in the device tree.
|
||||
* The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0).
|
||||
*/
|
||||
#define BTRFS_DEV_REPLACE_KEY 250
|
||||
|
||||
/*
|
||||
* string items are for debugging. They just store a short string of
|
||||
* data in the FS
|
||||
|
@ -1090,19 +1120,15 @@ static inline u##bits btrfs_##name(struct extent_buffer *eb, \
|
|||
type *s) \
|
||||
{ \
|
||||
unsigned long offset = (unsigned long)s; \
|
||||
u##bits m; \
|
||||
type *p = (type *) (eb->data + offset); \
|
||||
memcpy(&m, &p->member, sizeof(m)); \
|
||||
return le##bits##_to_cpu(m); \
|
||||
return get_unaligned_le##bits(&p->member); \
|
||||
} \
|
||||
static inline void btrfs_set_##name(struct extent_buffer *eb, \
|
||||
type *s, u##bits val) \
|
||||
{ \
|
||||
unsigned long offset = (unsigned long)s; \
|
||||
u##bits m; \
|
||||
type *p = (type *) (eb->data + offset); \
|
||||
m = cpu_to_le##bits(val); \
|
||||
memcpy(&p->member, &m, sizeof(m)); \
|
||||
put_unaligned_le##bits(val, &p->member); \
|
||||
}
|
||||
|
||||
#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \
|
||||
|
@ -1255,6 +1281,13 @@ BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
|
|||
BTRFS_SETGET_STACK_FUNCS(stack_inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
|
||||
BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64);
|
||||
|
||||
/* struct btrfs_inode_extref */
|
||||
BTRFS_SETGET_FUNCS(inode_extref_parent, struct btrfs_inode_extref,
|
||||
parent_objectid, 64);
|
||||
BTRFS_SETGET_FUNCS(inode_extref_name_len, struct btrfs_inode_extref,
|
||||
name_len, 16);
|
||||
BTRFS_SETGET_FUNCS(inode_extref_index, struct btrfs_inode_extref, index, 64);
|
||||
|
||||
/* struct btrfs_inode_item */
|
||||
BTRFS_SETGET_FUNCS(inode_generation, struct btrfs_inode_item, generation, 64);
|
||||
BTRFS_SETGET_FUNCS(inode_sequence, struct btrfs_inode_item, sequence, 64);
|
||||
|
|
11
dir-test.c
11
dir-test.c
|
@ -436,6 +436,12 @@ int main(int ac, char **av)
|
|||
radix_tree_init();
|
||||
|
||||
root = open_ctree(av[ac-1], &super, 0);
|
||||
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
|
||||
dir_oid = btrfs_super_root_dir(&super);
|
||||
|
@ -479,6 +485,11 @@ int main(int ac, char **av)
|
|||
btrfs_header_nritems(&root->node->node.header));
|
||||
close_ctree(root, &super);
|
||||
root = open_ctree("dbfile", &super, 0);
|
||||
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
while(count--) {
|
||||
ret = ops[op](trans, root, &radix);
|
||||
|
|
|
@ -1173,7 +1173,7 @@ int write_dev_supers(struct btrfs_root *root, struct btrfs_super_block *sb,
|
|||
|
||||
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
|
||||
bytenr = btrfs_sb_offset(i);
|
||||
if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes)
|
||||
if (bytenr + BTRFS_SUPER_INFO_SIZE > device->total_bytes)
|
||||
break;
|
||||
|
||||
btrfs_set_super_bytenr(sb, bytenr);
|
||||
|
|
|
@ -3410,7 +3410,7 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
|
|||
}
|
||||
|
||||
while(1) {
|
||||
cache = btrfs_lookup_block_group(fs_info, start);
|
||||
cache = btrfs_lookup_first_block_group(fs_info, start);
|
||||
if (!cache)
|
||||
break;
|
||||
start = cache->key.objectid + cache->key.offset;
|
||||
|
|
13
find-root.c
13
find-root.c
|
@ -35,13 +35,12 @@
|
|||
#include "utils.h"
|
||||
#include "crc32c.h"
|
||||
|
||||
static int verbose = 0;
|
||||
static u16 csum_size = 0;
|
||||
static u64 search_objectid = BTRFS_ROOT_TREE_OBJECTID;
|
||||
|
||||
static void usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: find-roots [-v] <device>\n");
|
||||
fprintf(stderr, "Usage: find-roots [-o search_objectid] <device>\n");
|
||||
}
|
||||
|
||||
int csum_block(void *buf, u32 len)
|
||||
|
@ -415,11 +414,8 @@ int main(int argc, char **argv)
|
|||
int opt;
|
||||
int ret;
|
||||
|
||||
while ((opt = getopt(argc, argv, "vo:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "o:")) != -1) {
|
||||
switch(opt) {
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'o':
|
||||
errno = 0;
|
||||
search_objectid = (u64)strtoll(optarg, NULL,
|
||||
|
@ -449,8 +445,11 @@ int main(int argc, char **argv)
|
|||
|
||||
root = open_ctree_broken(dev_fd, argv[optind]);
|
||||
close(dev_fd);
|
||||
if (!root)
|
||||
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
csum_size = btrfs_super_csum_size(&root->fs_info->super_copy);
|
||||
ret = find_root(root);
|
||||
|
|
86
ioctl.h
86
ioctl.h
|
@ -32,6 +32,8 @@ struct btrfs_ioctl_vol_args {
|
|||
char name[BTRFS_PATH_NAME_MAX + 1];
|
||||
};
|
||||
|
||||
#define BTRFS_DEVICE_PATH_NAME_MAX 1024
|
||||
|
||||
#define BTRFS_SUBVOL_CREATE_ASYNC (1ULL << 0)
|
||||
#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
|
||||
#define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2)
|
||||
|
@ -108,7 +110,48 @@ struct btrfs_ioctl_scrub_args {
|
|||
__u64 unused[(1024-32-sizeof(struct btrfs_scrub_progress))/8];
|
||||
};
|
||||
|
||||
#define BTRFS_DEVICE_PATH_NAME_MAX 1024
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS 0
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID 1
|
||||
struct btrfs_ioctl_dev_replace_start_params {
|
||||
__u64 srcdevid; /* in, if 0, use srcdev_name instead */
|
||||
__u64 cont_reading_from_srcdev_mode; /* in, see #define
|
||||
* above */
|
||||
__u8 srcdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */
|
||||
__u8 tgtdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */
|
||||
};
|
||||
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED 0
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED 1
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED 2
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED 3
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED 4
|
||||
struct btrfs_ioctl_dev_replace_status_params {
|
||||
__u64 replace_state; /* out, see #define above */
|
||||
__u64 progress_1000; /* out, 0 <= x <= 1000 */
|
||||
__u64 time_started; /* out, seconds since 1-Jan-1970 */
|
||||
__u64 time_stopped; /* out, seconds since 1-Jan-1970 */
|
||||
__u64 num_write_errors; /* out */
|
||||
__u64 num_uncorrectable_read_errors; /* out */
|
||||
};
|
||||
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_CMD_START 0
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS 1
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL 2
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR 0
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED 1
|
||||
#define BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED 2
|
||||
struct btrfs_ioctl_dev_replace_args {
|
||||
__u64 cmd; /* in */
|
||||
__u64 result; /* out */
|
||||
|
||||
union {
|
||||
struct btrfs_ioctl_dev_replace_start_params start;
|
||||
struct btrfs_ioctl_dev_replace_status_params status;
|
||||
}; /* in/out */
|
||||
|
||||
__u64 spare[64];
|
||||
};
|
||||
|
||||
struct btrfs_ioctl_dev_info_args {
|
||||
__u64 devid; /* in/out */
|
||||
__u8 uuid[BTRFS_UUID_SIZE]; /* in/out */
|
||||
|
@ -349,6 +392,39 @@ struct btrfs_ioctl_qgroup_create_args {
|
|||
__u64 qgroupid;
|
||||
};
|
||||
|
||||
enum btrfs_dev_stat_values {
|
||||
/* disk I/O failure stats */
|
||||
BTRFS_DEV_STAT_WRITE_ERRS, /* EIO or EREMOTEIO from lower layers */
|
||||
BTRFS_DEV_STAT_READ_ERRS, /* EIO or EREMOTEIO from lower layers */
|
||||
BTRFS_DEV_STAT_FLUSH_ERRS, /* EIO or EREMOTEIO from lower layers */
|
||||
|
||||
/* stats for indirect indications for I/O failures */
|
||||
BTRFS_DEV_STAT_CORRUPTION_ERRS, /* checksum error, bytenr error or
|
||||
* contents is illegal: this is an
|
||||
* indication that the block was damaged
|
||||
* during read or write, or written to
|
||||
* wrong location or read from wrong
|
||||
* location */
|
||||
BTRFS_DEV_STAT_GENERATION_ERRS, /* an indication that blocks have not
|
||||
* been written */
|
||||
|
||||
BTRFS_DEV_STAT_VALUES_MAX
|
||||
};
|
||||
|
||||
/* Reset statistics after reading; needs SYS_ADMIN capability */
|
||||
#define BTRFS_DEV_STATS_RESET (1ULL << 0)
|
||||
|
||||
struct btrfs_ioctl_get_dev_stats {
|
||||
__u64 devid; /* in */
|
||||
__u64 nr_items; /* in/out */
|
||||
__u64 flags; /* in/out */
|
||||
|
||||
/* out values: */
|
||||
__u64 values[BTRFS_DEV_STAT_VALUES_MAX];
|
||||
|
||||
__u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k */
|
||||
};
|
||||
|
||||
/* BTRFS_IOC_SNAP_CREATE is no longer used by the btrfs command */
|
||||
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
|
||||
struct btrfs_ioctl_vol_args)
|
||||
|
@ -419,7 +495,8 @@ struct btrfs_ioctl_clone_range_args {
|
|||
struct btrfs_ioctl_ino_path_args)
|
||||
#define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \
|
||||
struct btrfs_ioctl_ino_path_args)
|
||||
|
||||
#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
|
||||
struct btrfs_ioctl_vol_args)
|
||||
#define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
|
||||
struct btrfs_ioctl_received_subvol_args)
|
||||
#define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
|
||||
|
@ -432,4 +509,9 @@ struct btrfs_ioctl_clone_range_args {
|
|||
struct btrfs_ioctl_qgroup_create_args)
|
||||
#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
|
||||
struct btrfs_ioctl_qgroup_limit_args)
|
||||
#define BTRFS_IOC_GET_DEV_STATS _IOWR(BTRFS_IOCTL_MAGIC, 52, \
|
||||
struct btrfs_ioctl_get_dev_stats)
|
||||
#define BTRFS_IOC_DEV_REPLACE _IOWR(BTRFS_IOCTL_MAGIC, 53, \
|
||||
struct btrfs_ioctl_dev_replace_args)
|
||||
|
||||
#endif
|
||||
|
|
13
kerncompat.h
13
kerncompat.h
|
@ -271,6 +271,19 @@ typedef u64 __bitwise __be64;
|
|||
#define cpu_to_le16(x) ((__force __le16)(u16)(x))
|
||||
#define le16_to_cpu(x) ((__force u16)(__le16)(x))
|
||||
#endif
|
||||
|
||||
struct __una_u16 { u16 x; } __attribute__((__packed__));
|
||||
struct __una_u32 { u32 x; } __attribute__((__packed__));
|
||||
struct __una_u64 { u64 x; } __attribute__((__packed__));
|
||||
|
||||
#define get_unaligned_le8(p) (*((u8 *)(p)))
|
||||
#define put_unaligned_le8(val,p) ((*((u8 *)(p))) = (val))
|
||||
#define get_unaligned_le16(p) le16_to_cpu(((const struct __una_u16 *)(p))->x)
|
||||
#define put_unaligned_le16(val,p) (((struct __una_u16 *)(p))->x = cpu_to_le16(val))
|
||||
#define get_unaligned_le32(p) le32_to_cpu(((const struct __una_u32 *)(p))->x)
|
||||
#define put_unaligned_le32(val,p) (((struct __una_u32 *)(p))->x = cpu_to_le32(val))
|
||||
#define get_unaligned_le64(p) le64_to_cpu(((const struct __una_u64 *)(p))->x)
|
||||
#define put_unaligned_le64(val,p) (((struct __una_u64 *)(p))->x = cpu_to_le64(val))
|
||||
#endif
|
||||
|
||||
#ifndef noinline
|
||||
|
|
31
man/Makefile
31
man/Makefile
|
@ -1,4 +1,4 @@
|
|||
GZIP=gzip
|
||||
GZIPCMD=gzip
|
||||
INSTALL= install
|
||||
|
||||
prefix ?= /usr/local
|
||||
|
@ -6,31 +6,24 @@ bindir = $(prefix)/bin
|
|||
mandir = $(prefix)/man
|
||||
man8dir = $(mandir)/man8
|
||||
|
||||
# clear out all suffixes
|
||||
.SUFFIXES:
|
||||
# list only those we use
|
||||
.SUFFIXES: .in .gz
|
||||
|
||||
MANPAGES = mkfs.btrfs.8.gz btrfsctl.8.gz btrfsck.8.gz btrfs-image.8.gz \
|
||||
btrfs-show.8.gz btrfs.8.gz
|
||||
INFILES = ${MANPAGES:.in=.gz}
|
||||
|
||||
all: $(MANPAGES)
|
||||
|
||||
mkfs.btrfs.8.gz: mkfs.btrfs.8.in
|
||||
$(GZIP) -n -c mkfs.btrfs.8.in > mkfs.btrfs.8.gz
|
||||
|
||||
btrfs.8.gz: btrfs.8.in
|
||||
$(GZIP) -n -c btrfs.8.in > btrfs.8.gz
|
||||
|
||||
btrfsctl.8.gz: btrfsctl.8.in
|
||||
$(GZIP) -n -c btrfsctl.8.in > btrfsctl.8.gz
|
||||
|
||||
btrfsck.8.gz: btrfsck.8.in
|
||||
$(GZIP) -n -c btrfsck.8.in > btrfsck.8.gz
|
||||
|
||||
btrfs-image.8.gz: btrfs-image.8.in
|
||||
$(GZIP) -n -c btrfs-image.8.in > btrfs-image.8.gz
|
||||
|
||||
btrfs-show.8.gz: btrfs-show.8.in
|
||||
$(GZIP) -n -c btrfs-show.8.in > btrfs-show.8.gz
|
||||
.in.gz :
|
||||
@echo " [MAN] $@"
|
||||
$(Q)$(GZIPCMD) -n -c $< > $@
|
||||
|
||||
clean :
|
||||
rm -f $(MANPAGES)
|
||||
@echo "Cleaning manpages"
|
||||
$(Q)rm -f $(MANPAGES)
|
||||
|
||||
install: $(MANPAGES)
|
||||
$(INSTALL) -m755 -d $(DESTDIR)$(man8dir)
|
||||
|
|
118
man/btrfs.8.in
118
man/btrfs.8.in
|
@ -11,12 +11,16 @@ btrfs \- control a btrfs filesystem
|
|||
.PP
|
||||
\fBbtrfs\fP \fBsubvolume create\fP\fI [<dest>/]<name>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBsubvolume list\fP\fI [-aprts] [-g [+|-]value] [-c [+|-]value] [--rootid=rootid,gen,ogen,path] <path>\fP
|
||||
\fBbtrfs\fP \fBsubvolume list\fP\fI [-acgoprts] [-G [+|-]value] [-C [+|-]value] [--sort=rootid,gen,ogen,path] <path>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBsubvolume set-default\fP\fI <id> <path>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBsubvolume get-default\fP\fI <path>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBsubvolume find-new\fP\fI <subvolume> <last_gen>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBsubvolume show\fP\fI <path>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBfilesystem defragment\fP -c[zlib|lzo] [-l \fIlen\fR] \
|
||||
[-s \fIstart\fR] [-t \fIsize\fR] -[vf] <\fIfile\fR>|<\fIdir\fR> \
|
||||
[<\fIfile\fR>|<\fIdir\fR>...]
|
||||
|
@ -27,16 +31,22 @@ btrfs \- control a btrfs filesystem
|
|||
.PP
|
||||
\fBbtrfs\fP \fBfilesystem label\fP\fI <dev> [newlabel]\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBsubvolume find-new\fP\fI <subvolume> <last_gen>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBfilesystem balance\fP\fI <path> \fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBdevice scan\fP\fI [--all-devices|<device> [<device>...]]\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBdevice stats\fP [-z] {\fI<path>\fP|\fI<device>\fP}
|
||||
.PP
|
||||
\fBbtrfs\fP \fBdevice add\fP\fI <device> [<device>...] <path> \fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBdevice delete\fP\fI <device> [<device>...] <path> \fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBreplace start\fP \fI[-Bfr] <srcdev>|<devid> <targetdev> <path>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBreplace status\fP \fI[-1] <path>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBreplace cancel\fP \fI<path>\fP
|
||||
.PP
|
||||
\fBbtrfs\fP \fBscrub start\fP [-Bdqru] {\fI<path>\fP|\fI<device>\fP}
|
||||
.PP
|
||||
\fBbtrfs\fP \fBscrub cancel\fP {\fI<path>\fP|\fI<device>\fP}
|
||||
|
@ -106,7 +116,7 @@ Create a subvolume in \fI<dest>\fR (or in the current directory if
|
|||
\fI<dest>\fR is omitted).
|
||||
.TP
|
||||
|
||||
\fBsubvolume list\fR\fI [-aprts][-g [+|-]value] [-c [+|-]value] [--sort=gen,ogen,rootid,path] <path>\fR
|
||||
\fBsubvolume list\fR\fI [-acgoprts] [-G [+|-]value] [-C [+|-]value] [--sort=rootid,gen,ogen,path] <path>\fR
|
||||
.RS
|
||||
List the subvolumes present in the filesystem \fI<path>\fR. For every
|
||||
subvolume the following information is shown by default.
|
||||
|
@ -115,31 +125,40 @@ where path is the relative path of the subvolume to the \fItop level\fR
|
|||
subvolume.
|
||||
|
||||
The subvolume's ID may be used by the \fBsubvolume set-default\fR command, or
|
||||
at mount time via the \fIsubvol=\fR option.
|
||||
at mount time via the \fIsubvolid=\fR option.
|
||||
If \fI-p\fR is given, then \fIparent <ID>\fR is added to the output between ID
|
||||
and top level. The parent's ID may be used at mount time via the
|
||||
\fIsubvolrootid=\fR option.
|
||||
|
||||
\fB-t\fP print the result as a table.
|
||||
|
||||
\fB-a\fP print all the subvolumes in the filesystem.
|
||||
\fB-a\fP print all the subvolumes in the filesystem and distinguish between
|
||||
absolute and relative path with respect to the given <path>.
|
||||
|
||||
\fB-r\fP only readonly subvolumes in the filesystem wille be listed.
|
||||
\fB-c\fP print the ogeneration of the subvolume, aliases: ogen or origin generation
|
||||
|
||||
\fB-s\fP only snapshot subvolumes in the filesystem will be listed.
|
||||
\fB-g\fP print the generation of the subvolume
|
||||
|
||||
\fB-g [+|-]value\fP
|
||||
\fB-u\fP print the UUID of the subvolume
|
||||
|
||||
\fB-o\fP print only subvolumes bellow specified <path>.
|
||||
|
||||
\fB-r\fP only readonly subvolumes in the filesystem will be listed.
|
||||
|
||||
\fB-s\fP only snapshot subvolumes in the filesystem will be listed.
|
||||
|
||||
\fB-G [+|-]value\fP
|
||||
list subvolumes in the filesystem that its generation is
|
||||
>=, <= or = value. '+' means >= value, '-' means <= value, If there is
|
||||
neither '+' nor '-', it means = value.
|
||||
|
||||
\fB-c [+|-]value\fP
|
||||
\fB-C [+|-]value\fP
|
||||
list subvolumes in the filesystem that its ogeneration is
|
||||
>=, <= or = value. The usage is the same to '-g' option.
|
||||
|
||||
\fB--sort=gen,ogen,path,rootid\fP
|
||||
\fB--sort=rootid,gen,ogen,path\fP
|
||||
list subvolumes in order by specified items.
|
||||
you can add '+' or '-' in front of each items, '+' means ascending,'-'
|
||||
you can add '+' or '-' in front of each items, '+' means ascending, '-'
|
||||
means descending. The default is ascending.
|
||||
|
||||
for \fB--sort\fP you can combine some items together by ',', just like
|
||||
|
@ -158,6 +177,14 @@ Get the default subvolume of the filesystem \fI<path>\fR. The output format
|
|||
is similar to \fBsubvolume list\fR command.
|
||||
.TP
|
||||
|
||||
\fBsubvolume find-new\fR\fI <subvolume> <last_gen>\fR
|
||||
List the recently modified files in a subvolume, after \fI<last_gen>\fR ID.
|
||||
.TP
|
||||
|
||||
\fBsubvolume show\fR\fI <path>\fR
|
||||
Show information of a given subvolume in the \fI<path>\fR.
|
||||
.TP
|
||||
|
||||
\fBfilesystem defragment\fP -c[zlib|lzo] [-l \fIlen\fR] [-s \fIstart\fR] \
|
||||
[-t \fIsize\fR] -[vf] <\fIfile\fR>|<\fIdir\fR> [<\fIfile\fR>|<\fIdir\fR>...]
|
||||
|
||||
|
@ -182,15 +209,14 @@ defragment operations.
|
|||
|
||||
\fB-t size\fP defragment only files at least \fIsize\fR bytes big
|
||||
|
||||
For \fBstart\fP, \fBlen\fP, \fBsize\fP it is possible to append a suffix
|
||||
like \fBk\fP for 1 KBytes, \fBm\fP for 1 MBytes...
|
||||
|
||||
NOTE: defragmenting with kernels up to 2.6.37 will unlink COW-ed copies of data,
|
||||
don't use it if you use snapshots, have de-duplicated your data or made
|
||||
copies with \fBcp --reflink\fP.
|
||||
.TP
|
||||
|
||||
\fBsubvolume find-new\fR\fI <subvolume> <last_gen>\fR
|
||||
List the recently modified files in a subvolume, after \fI<last_gen>\fR ID.
|
||||
.TP
|
||||
|
||||
\fBfilesystem sync\fR\fI <path> \fR
|
||||
Force a sync for the filesystem identified by \fI<path>\fR.
|
||||
.TP
|
||||
|
@ -252,6 +278,18 @@ Balance the chunks of the filesystem identified by \fI<path>\fR
|
|||
across the devices.
|
||||
.TP
|
||||
|
||||
\fBdevice stats\fP [-z] {\fI<path>\fP|\fI<device>\fP}
|
||||
Read and print the device IO stats for all devices of the filesystem
|
||||
identified by \fI<path>\fR or for a single \fI<device>\fR.
|
||||
|
||||
.RS
|
||||
\fIOptions\fR
|
||||
.TP
|
||||
.B -z
|
||||
Reset stats to zero after reading them.
|
||||
.RE
|
||||
.TP
|
||||
|
||||
\fBdevice add\fR\fI <dev> [<dev>..] <path>\fR
|
||||
Add device(s) to the filesystem identified by \fI<path>\fR.
|
||||
.TP
|
||||
|
@ -268,6 +306,54 @@ Finally, if \fB--all-devices\fP is passed, all the devices under /dev are
|
|||
scanned.
|
||||
.TP
|
||||
|
||||
\fBreplace start\fR \fI[-Bfr] <srcdev>|<devid> <targetdev> <path>\fR
|
||||
Replace device of a btrfs filesystem.
|
||||
On a live filesystem, duplicate the data to the target device which
|
||||
is currently stored on the source device. If the source device is not
|
||||
available anymore, or if the \fB-r\fR option is set, the data is built
|
||||
only using the RAID redundancy mechanisms. After completion of the
|
||||
operation, the source device is removed from the filesystem.
|
||||
If the \fIsrcdev\fR is a numerical value, it is assumed to be the device id
|
||||
of the filesystem which is mounted at mount_point, otherwise is is
|
||||
the path to the source device. If the source device is disconnected,
|
||||
from the system, you have to use the \fIdevid\fR parameter format.
|
||||
The targetdev needs to be same size or larger than the \fIsrcdev\fR.
|
||||
|
||||
.RS
|
||||
\fIOptions\fR
|
||||
.TP
|
||||
.B -r
|
||||
only read from \fIsrcdev\fR if no other zero-defect mirror exists (enable
|
||||
this if your drive has lots of read errors, the access would be very slow)
|
||||
.TP
|
||||
.B -f
|
||||
force using and overwriting \fItargetdev\fR even if it looks like
|
||||
containing a valid btrfs filesystem. A valid filesystem is
|
||||
assumed if a btrfs superblock is found which contains a
|
||||
correct checksum. Devices which are currently mounted are
|
||||
never allowed to be used as the \fItargetdev\fR
|
||||
.TP
|
||||
.B -B
|
||||
do not background
|
||||
.RE
|
||||
.TP
|
||||
|
||||
\fBreplace status\fR \fI[-1] <path>\fR
|
||||
Print status and progress information of a running device replace operation.
|
||||
|
||||
.RS
|
||||
\fIOptions\fR
|
||||
.TP
|
||||
.B -1
|
||||
print once instead of print continously until the replace
|
||||
operation finishes (or is canceled)
|
||||
.RE
|
||||
.TP
|
||||
|
||||
\fBreplace cancel\fR \fI<path>\fR
|
||||
Cancel a running device replace operation.
|
||||
.TP
|
||||
|
||||
\fBscrub start\fP [-Bdqru] {\fI<path>\fP|\fI<device>\fP}
|
||||
Start a scrub on all devices of the filesystem identified by \fI<path>\fR or on
|
||||
a single \fI<device>\fR. Without options, scrub is started as a background
|
||||
|
|
|
@ -47,7 +47,10 @@ Specify a label for the filesystem.
|
|||
.TP
|
||||
\fB\-m\fR, \fB\-\-metadata \fIprofile\fR
|
||||
Specify how metadata must be spanned across the devices specified. Valid
|
||||
values are raid0, raid1, raid10 or single.
|
||||
values are raid0, raid1, raid10, single or dup. Single device will have dup
|
||||
set by default except in the case of SSDs which will default to single. This is
|
||||
because SSDs can remap blocks internally so duplicate blocks could end up in the
|
||||
same erase block which negates the benefits of doing metadata duplication.
|
||||
.TP
|
||||
\fB\-M\fR, \fB\-\-mixed\fR
|
||||
Mix data and metadata chunks together for more efficient space
|
||||
|
@ -69,6 +72,9 @@ Do not perform whole device TRIM operation by default.
|
|||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print the \fBmkfs.btrfs\fP version and exit.
|
||||
.SH UNIT
|
||||
As default the unit is the byte, however it is possible to append a suffix
|
||||
to the arguments like \fBk\fP for KBytes, \fBm\fP for MBytes...
|
||||
.SH AVAILABILITY
|
||||
.B mkfs.btrfs
|
||||
is part of btrfs-progs. Btrfs is currently under heavy development,
|
||||
|
|
92
mkfs.c
92
mkfs.c
|
@ -39,6 +39,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <ctype.h>
|
||||
#include <attr/xattr.h>
|
||||
#include <blkid/blkid.h>
|
||||
#include "ctree.h"
|
||||
#include "disk-io.h"
|
||||
#include "volumes.h"
|
||||
|
@ -204,7 +205,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
|
|||
static int create_raid_groups(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 data_profile,
|
||||
int data_profile_opt, u64 metadata_profile,
|
||||
int metadata_profile_opt, int mixed)
|
||||
int metadata_profile_opt, int mixed, int ssd)
|
||||
{
|
||||
u64 num_devices = btrfs_super_num_devices(&root->fs_info->super_copy);
|
||||
u64 allowed = 0;
|
||||
|
@ -216,8 +217,12 @@ static int create_raid_groups(struct btrfs_trans_handle *trans,
|
|||
* For mixed groups defaults are single/single.
|
||||
*/
|
||||
if (!metadata_profile_opt && !mixed) {
|
||||
if (num_devices == 1 && ssd)
|
||||
printf("Detected a SSD, turning off metadata "
|
||||
"duplication. Mkfs with -m dup if you want to "
|
||||
"force metadata duplication.\n");
|
||||
metadata_profile = (num_devices > 1) ?
|
||||
BTRFS_BLOCK_GROUP_RAID1 : BTRFS_BLOCK_GROUP_DUP;
|
||||
BTRFS_BLOCK_GROUP_RAID1 : (ssd) ? 0: BTRFS_BLOCK_GROUP_DUP;
|
||||
}
|
||||
if (!data_profile_opt && !mixed) {
|
||||
data_profile = (num_devices > 1) ?
|
||||
|
@ -366,7 +371,6 @@ static u64 parse_profile(char *s)
|
|||
|
||||
static char *parse_label(char *input)
|
||||
{
|
||||
int i;
|
||||
int len = strlen(input);
|
||||
|
||||
if (len >= BTRFS_LABEL_SIZE) {
|
||||
|
@ -374,12 +378,6 @@ static char *parse_label(char *input)
|
|||
BTRFS_LABEL_SIZE - 1);
|
||||
exit(1);
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
if (input[i] == '/' || input[i] == '\\') {
|
||||
fprintf(stderr, "invalid label %s\n", input);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return strdup(input);
|
||||
}
|
||||
|
||||
|
@ -1207,6 +1205,54 @@ static int check_leaf_or_node_size(u32 size, u32 sectorsize)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int is_ssd(const char *file)
|
||||
{
|
||||
char *devname;
|
||||
blkid_probe probe;
|
||||
char *dev;
|
||||
char path[PATH_MAX];
|
||||
dev_t disk;
|
||||
int fd;
|
||||
char rotational;
|
||||
|
||||
probe = blkid_new_probe_from_filename(file);
|
||||
if (!probe)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We want to use blkid_devno_to_wholedisk() but it's broken for some
|
||||
* reason on F17 at least so we'll do this trickery
|
||||
*/
|
||||
disk = blkid_probe_get_wholedisk_devno(probe);
|
||||
if (!disk)
|
||||
return 0;
|
||||
|
||||
devname = blkid_devno_to_devname(disk);
|
||||
if (!devname)
|
||||
return 0;
|
||||
|
||||
dev = strrchr(devname, '/');
|
||||
dev++;
|
||||
|
||||
snprintf(path, PATH_MAX, "/sys/block/%s/queue/rotational", dev);
|
||||
|
||||
free(devname);
|
||||
blkid_free_probe(probe);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (read(fd, &rotational, sizeof(char)) < sizeof(char)) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return !atoi((const char *)&rotational);
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
char *file;
|
||||
|
@ -1220,7 +1266,7 @@ int main(int ac, char **av)
|
|||
u64 alloc_start = 0;
|
||||
u64 metadata_profile = 0;
|
||||
u64 data_profile = 0;
|
||||
u32 leafsize = getpagesize();
|
||||
u32 leafsize = sysconf(_SC_PAGESIZE);
|
||||
u32 sectorsize = 4096;
|
||||
u32 nodesize = leafsize;
|
||||
u32 stripesize = 4096;
|
||||
|
@ -1233,6 +1279,7 @@ int main(int ac, char **av)
|
|||
int data_profile_opt = 0;
|
||||
int metadata_profile_opt = 0;
|
||||
int nodiscard = 0;
|
||||
int ssd = 0;
|
||||
|
||||
char *source_dir = NULL;
|
||||
int source_dir_set = 0;
|
||||
|
@ -1240,6 +1287,8 @@ int main(int ac, char **av)
|
|||
u64 size_of_data = 0;
|
||||
u64 source_dir_size = 0;
|
||||
char *pretty_buf;
|
||||
struct btrfs_super_block *super;
|
||||
u64 flags;
|
||||
|
||||
while(1) {
|
||||
int c;
|
||||
|
@ -1296,7 +1345,7 @@ int main(int ac, char **av)
|
|||
print_usage();
|
||||
}
|
||||
}
|
||||
sectorsize = max(sectorsize, (u32)getpagesize());
|
||||
sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE));
|
||||
if (check_leaf_or_node_size(leafsize, sectorsize))
|
||||
exit(1);
|
||||
if (check_leaf_or_node_size(nodesize, sectorsize))
|
||||
|
@ -1351,7 +1400,12 @@ int main(int ac, char **av)
|
|||
fprintf(stderr, "unable to zero the output file\n");
|
||||
exit(1);
|
||||
}
|
||||
/* our "device" is the new image file */
|
||||
dev_block_count = block_count;
|
||||
}
|
||||
|
||||
ssd = is_ssd(file);
|
||||
|
||||
if (mixed) {
|
||||
if (metadata_profile != data_profile) {
|
||||
fprintf(stderr, "With mixed block groups data and metadata "
|
||||
|
@ -1376,7 +1430,8 @@ int main(int ac, char **av)
|
|||
|
||||
root = open_ctree(file, 0, O_RDWR);
|
||||
if (!root) {
|
||||
fprintf(stderr, "ctree init failed\n");
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
root->fs_info->alloc_start = alloc_start;
|
||||
|
@ -1437,20 +1492,21 @@ raid_groups:
|
|||
if (!source_dir_set) {
|
||||
ret = create_raid_groups(trans, root, data_profile,
|
||||
data_profile_opt, metadata_profile,
|
||||
metadata_profile_opt, mixed);
|
||||
metadata_profile_opt, mixed, ssd);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
ret = create_data_reloc_tree(trans, root);
|
||||
BUG_ON(ret);
|
||||
|
||||
if (mixed) {
|
||||
struct btrfs_super_block *super = &root->fs_info->super_copy;
|
||||
u64 flags = btrfs_super_incompat_flags(super);
|
||||
super = &root->fs_info->super_copy;
|
||||
flags = btrfs_super_incompat_flags(super);
|
||||
flags |= BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF;
|
||||
|
||||
if (mixed)
|
||||
flags |= BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS;
|
||||
btrfs_set_super_incompat_flags(super, flags);
|
||||
}
|
||||
|
||||
btrfs_set_super_incompat_flags(super, flags);
|
||||
|
||||
if ((data_profile | metadata_profile) &
|
||||
(BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)) {
|
||||
|
|
53
print-tree.c
53
print-tree.c
|
@ -61,6 +61,42 @@ static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int print_inode_extref_item(struct extent_buffer *eb,
|
||||
struct btrfs_item *item,
|
||||
struct btrfs_inode_extref *extref)
|
||||
{
|
||||
u32 total;
|
||||
u32 cur = 0;
|
||||
u32 len;
|
||||
u32 name_len = 0;
|
||||
u64 index = 0;
|
||||
u64 parent_objid;
|
||||
char namebuf[BTRFS_NAME_LEN];
|
||||
|
||||
total = btrfs_item_size(eb, item);
|
||||
|
||||
while (cur < total) {
|
||||
index = btrfs_inode_extref_index(eb, extref);
|
||||
name_len = btrfs_inode_extref_name_len(eb, extref);
|
||||
parent_objid = btrfs_inode_extref_parent(eb, extref);
|
||||
|
||||
len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf);
|
||||
|
||||
read_extent_buffer(eb, namebuf, (unsigned long)(extref->name), len);
|
||||
|
||||
printf("\t\tinode extref index %llu parent %llu namelen %u "
|
||||
"name: %.*s\n",
|
||||
(unsigned long long)index,
|
||||
(unsigned long long)parent_objid,
|
||||
name_len, len, namebuf);
|
||||
|
||||
len = sizeof(*extref) + name_len;
|
||||
extref = (struct btrfs_inode_extref *)((char *)extref + len);
|
||||
cur += len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_inode_ref_item(struct extent_buffer *eb, struct btrfs_item *item,
|
||||
struct btrfs_inode_ref *ref)
|
||||
{
|
||||
|
@ -371,6 +407,9 @@ static void print_key_type(u64 objectid, u8 type)
|
|||
case BTRFS_INODE_REF_KEY:
|
||||
printf("INODE_REF");
|
||||
break;
|
||||
case BTRFS_INODE_EXTREF_KEY:
|
||||
printf("INODE_EXTREF");
|
||||
break;
|
||||
case BTRFS_DIR_ITEM_KEY:
|
||||
printf("DIR_ITEM");
|
||||
break;
|
||||
|
@ -440,6 +479,9 @@ static void print_key_type(u64 objectid, u8 type)
|
|||
case BTRFS_BALANCE_ITEM_KEY:
|
||||
printf("BALANCE_ITEM");
|
||||
break;
|
||||
case BTRFS_DEV_REPLACE_KEY:
|
||||
printf("DEV_REPLACE_ITEM");
|
||||
break;
|
||||
case BTRFS_STRING_ITEM_KEY:
|
||||
printf("STRING_ITEM");
|
||||
break;
|
||||
|
@ -455,6 +497,9 @@ static void print_key_type(u64 objectid, u8 type)
|
|||
case BTRFS_QGROUP_LIMIT_KEY:
|
||||
printf("BTRFS_QGROUP_LIMIT_KEY");
|
||||
break;
|
||||
case BTRFS_DEV_STATS_KEY:
|
||||
printf("DEV_STATS_ITEM");
|
||||
break;
|
||||
default:
|
||||
printf("UNKNOWN.%d", type);
|
||||
};
|
||||
|
@ -576,6 +621,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
|
|||
struct btrfs_extent_data_ref *dref;
|
||||
struct btrfs_shared_data_ref *sref;
|
||||
struct btrfs_inode_ref *iref;
|
||||
struct btrfs_inode_extref *iref2;
|
||||
struct btrfs_dev_extent *dev_extent;
|
||||
struct btrfs_disk_key disk_key;
|
||||
struct btrfs_block_group_item bg_item;
|
||||
|
@ -623,6 +669,10 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
|
|||
iref = btrfs_item_ptr(l, i, struct btrfs_inode_ref);
|
||||
print_inode_ref_item(l, item, iref);
|
||||
break;
|
||||
case BTRFS_INODE_EXTREF_KEY:
|
||||
iref2 = btrfs_item_ptr(l, i, struct btrfs_inode_extref);
|
||||
print_inode_extref_item(l, item, iref2);
|
||||
break;
|
||||
case BTRFS_DIR_ITEM_KEY:
|
||||
case BTRFS_DIR_INDEX_KEY:
|
||||
case BTRFS_XATTR_ITEM_KEY:
|
||||
|
@ -777,6 +827,9 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
|
|||
str = l->data + btrfs_item_ptr_offset(l, i);
|
||||
printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str);
|
||||
break;
|
||||
case BTRFS_DEV_STATS_KEY:
|
||||
printf("\t\tdevice stats\n");
|
||||
break;
|
||||
};
|
||||
fflush(stdout);
|
||||
}
|
||||
|
|
22
qgroup.c
22
qgroup.c
|
@ -22,15 +22,29 @@
|
|||
u64 parse_qgroupid(char *p)
|
||||
{
|
||||
char *s = strchr(p, '/');
|
||||
char *ptr_src_end = p + strlen(p);
|
||||
char *ptr_parse_end = NULL;
|
||||
u64 level;
|
||||
u64 id;
|
||||
|
||||
if (!s)
|
||||
return atoll(p);
|
||||
level = atoll(p);
|
||||
id = atoll(s + 1);
|
||||
if (!s) {
|
||||
id = strtoull(p, &ptr_parse_end, 10);
|
||||
if (ptr_parse_end != ptr_src_end)
|
||||
goto err;
|
||||
return id;
|
||||
}
|
||||
level = strtoull(p, &ptr_parse_end, 10);
|
||||
if (ptr_parse_end != s)
|
||||
goto err;
|
||||
|
||||
id = strtoull(s+1, &ptr_parse_end, 10);
|
||||
if (ptr_parse_end != ptr_src_end)
|
||||
goto err;
|
||||
|
||||
return (level << 48) | id;
|
||||
err:
|
||||
fprintf(stderr, "ERROR:invalid qgroupid\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int qgroup_inherit_size(struct btrfs_qgroup_inherit *p)
|
||||
|
|
2
qgroup.h
2
qgroup.h
|
@ -20,7 +20,9 @@
|
|||
#define _BTRFS_QGROUP_H
|
||||
|
||||
#include "ioctl.h"
|
||||
#include "kerncompat.h"
|
||||
|
||||
u64 parse_qgroupid(char *p);
|
||||
int qgroup_inherit_size(struct btrfs_qgroup_inherit *p);
|
||||
int qgroup_inherit_realloc(struct btrfs_qgroup_inherit **inherit,
|
||||
int incgroups, int inccopies);
|
||||
|
|
20
quick-test.c
20
quick-test.c
|
@ -52,6 +52,10 @@ int main(int ac, char **av) {
|
|||
radix_tree_init();
|
||||
|
||||
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
exit(1);
|
||||
}
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
srand(55);
|
||||
btrfs_set_key_type(&ins, BTRFS_STRING_ITEM_KEY);
|
||||
|
@ -75,6 +79,10 @@ int main(int ac, char **av) {
|
|||
close_ctree(root);
|
||||
exit(1);
|
||||
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("starting search\n");
|
||||
srand(55);
|
||||
for (i = 0; i < run_size; i++) {
|
||||
|
@ -94,6 +102,10 @@ int main(int ac, char **av) {
|
|||
close_ctree(root);
|
||||
|
||||
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("node %p level %d total ptrs %d free spc %lu\n", root->node,
|
||||
btrfs_header_level(root->node),
|
||||
btrfs_header_nritems(root->node),
|
||||
|
@ -122,6 +134,10 @@ int main(int ac, char **av) {
|
|||
close_ctree(root);
|
||||
|
||||
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
exit(1);
|
||||
}
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
srand(128);
|
||||
for (i = 0; i < run_size; i++) {
|
||||
|
@ -138,6 +154,10 @@ int main(int ac, char **av) {
|
|||
close_ctree(root);
|
||||
|
||||
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
exit(1);
|
||||
}
|
||||
srand(128);
|
||||
printf("starting search2\n");
|
||||
for (i = 0; i < run_size; i++) {
|
||||
|
|
|
@ -356,6 +356,10 @@ int main(int ac, char **av)
|
|||
struct btrfs_trans_handle *trans;
|
||||
radix_tree_init();
|
||||
root = open_ctree("dbfile", &super);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
exit(1);
|
||||
}
|
||||
fill_radix(root, &radix);
|
||||
|
||||
signal(SIGTERM, sigstopper);
|
||||
|
@ -398,6 +402,10 @@ int main(int ac, char **av)
|
|||
btrfs_header_nritems(&root->node->node.header));
|
||||
close_ctree(root, &super);
|
||||
root = open_ctree("dbfile", &super);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Open ctree failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
while(count--) {
|
||||
ret = ops[op](trans, root, &radix);
|
||||
|
|
|
@ -240,7 +240,8 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
|
|||
memcpy(&root_item, root_item_ptr,
|
||||
sizeof(root_item));
|
||||
root_item_valid = 1;
|
||||
} else if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
|
||||
} else if (sh->type == BTRFS_ROOT_BACKREF_KEY ||
|
||||
root_item_valid) {
|
||||
if (!root_item_valid)
|
||||
goto skip;
|
||||
|
||||
|
@ -274,7 +275,6 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
|
|||
subvol_uuid_search_add(s, si);
|
||||
root_item_valid = 0;
|
||||
} else {
|
||||
root_item_valid = 0;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ btrfs_start_transaction(struct btrfs_root *root, int num_blocks)
|
|||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct btrfs_trans_handle *h = malloc(sizeof(*h));
|
||||
|
||||
BUG_ON(!h);
|
||||
BUG_ON(root->commit_root);
|
||||
BUG_ON(fs_info->running_transaction);
|
||||
fs_info->running_transaction = h;
|
||||
|
|
133
utils.c
133
utils.c
|
@ -16,10 +16,12 @@
|
|||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#define _XOPEN_SOURCE 600
|
||||
#define __USE_XOPEN2K
|
||||
#define _XOPEN_SOURCE 700
|
||||
#define __USE_XOPEN2K8
|
||||
#define __XOPEN2K8 /* due to an error in dirent.h, to get dirfd() */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef __CHECKER__
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
|
@ -652,21 +654,22 @@ int is_loop_device (const char* device) {
|
|||
* the associated file (e.g. /images/my_btrfs.img) */
|
||||
int resolve_loop_device(const char* loop_dev, char* loop_file, int max_len)
|
||||
{
|
||||
int loop_fd;
|
||||
int ret_ioctl;
|
||||
struct loop_info loopinfo;
|
||||
int ret;
|
||||
FILE *f;
|
||||
char fmt[20];
|
||||
char p[PATH_MAX];
|
||||
char real_loop_dev[PATH_MAX];
|
||||
|
||||
if ((loop_fd = open(loop_dev, O_RDONLY)) < 0)
|
||||
if (!realpath(loop_dev, real_loop_dev))
|
||||
return -errno;
|
||||
snprintf(p, PATH_MAX, "/sys/block/%s/loop/backing_file", strrchr(real_loop_dev, '/'));
|
||||
if (!(f = fopen(p, "r")))
|
||||
return -errno;
|
||||
|
||||
ret_ioctl = ioctl(loop_fd, LOOP_GET_STATUS, &loopinfo);
|
||||
close(loop_fd);
|
||||
|
||||
if (ret_ioctl == 0) {
|
||||
strncpy(loop_file, loopinfo.lo_name, max_len);
|
||||
if (max_len > 0)
|
||||
loop_file[max_len-1] = 0;
|
||||
} else
|
||||
snprintf(fmt, 20, "%%%i[^\n]", max_len-1);
|
||||
ret = fscanf(f, fmt, loop_file);
|
||||
fclose(f);
|
||||
if (ret == EOF)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
|
@ -1016,8 +1019,14 @@ again:
|
|||
}
|
||||
fd = open(fullpath, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "failed to read %s: %s\n", fullpath,
|
||||
strerror(errno));
|
||||
/* ignore the following errors:
|
||||
ENXIO (device don't exists)
|
||||
ENOMEDIUM (No medium found ->
|
||||
like a cd tray empty)
|
||||
*/
|
||||
if(errno != ENXIO && errno != ENOMEDIUM)
|
||||
fprintf(stderr, "failed to read %s: %s\n",
|
||||
fullpath, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices,
|
||||
|
@ -1268,3 +1277,95 @@ u64 parse_size(char *s)
|
|||
return strtoull(s, NULL, 10) * mult;
|
||||
}
|
||||
|
||||
int open_file_or_dir(const char *fname)
|
||||
{
|
||||
int ret;
|
||||
struct stat st;
|
||||
DIR *dirstream;
|
||||
int fd;
|
||||
|
||||
ret = stat(fname, &st);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
dirstream = opendir(fname);
|
||||
if (!dirstream) {
|
||||
return -2;
|
||||
}
|
||||
fd = dirfd(dirstream);
|
||||
} else {
|
||||
fd = open(fname, O_RDWR);
|
||||
}
|
||||
if (fd < 0) {
|
||||
return -3;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int get_device_info(int fd, u64 devid,
|
||||
struct btrfs_ioctl_dev_info_args *di_args)
|
||||
{
|
||||
int ret;
|
||||
|
||||
di_args->devid = devid;
|
||||
memset(&di_args->uuid, '\0', sizeof(di_args->uuid));
|
||||
|
||||
ret = ioctl(fd, BTRFS_IOC_DEV_INFO, di_args);
|
||||
return ret ? -errno : 0;
|
||||
}
|
||||
|
||||
int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args,
|
||||
struct btrfs_ioctl_dev_info_args **di_ret)
|
||||
{
|
||||
int ret = 0;
|
||||
int ndevs = 0;
|
||||
int i = 1;
|
||||
struct btrfs_fs_devices *fs_devices_mnt = NULL;
|
||||
struct btrfs_ioctl_dev_info_args *di_args;
|
||||
char mp[BTRFS_PATH_NAME_MAX + 1];
|
||||
|
||||
memset(fi_args, 0, sizeof(*fi_args));
|
||||
|
||||
ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
|
||||
if (ret && (errno == EINVAL || errno == ENOTTY)) {
|
||||
/* path is not a mounted btrfs. Try if it's a device */
|
||||
ret = check_mounted_where(fd, path, mp, sizeof(mp),
|
||||
&fs_devices_mnt);
|
||||
if (!ret)
|
||||
return -EINVAL;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
fi_args->num_devices = 1;
|
||||
fi_args->max_id = fs_devices_mnt->latest_devid;
|
||||
i = fs_devices_mnt->latest_devid;
|
||||
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
|
||||
close(fd);
|
||||
fd = open_file_or_dir(mp);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
} else if (ret) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!fi_args->num_devices)
|
||||
return 0;
|
||||
|
||||
di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
|
||||
if (!di_args)
|
||||
return -errno;
|
||||
|
||||
for (; i <= fi_args->max_id; ++i) {
|
||||
BUG_ON(ndevs >= fi_args->num_devices);
|
||||
ret = get_device_info(fd, i, &di_args[ndevs]);
|
||||
if (ret == -ENODEV)
|
||||
continue;
|
||||
if (ret)
|
||||
return ret;
|
||||
ndevs++;
|
||||
}
|
||||
|
||||
BUG_ON(ndevs == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
9
utils.h
9
utils.h
|
@ -19,6 +19,8 @@
|
|||
#ifndef __UTILS__
|
||||
#define __UTILS__
|
||||
|
||||
#include "ctree.h"
|
||||
|
||||
#define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024)
|
||||
|
||||
int make_btrfs(int fd, const char *device, const char *label,
|
||||
|
@ -44,8 +46,11 @@ int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
|
|||
char *pretty_sizes(u64 size);
|
||||
int check_label(char *input);
|
||||
int get_mountpt(char *dev, char *mntpt, size_t size);
|
||||
|
||||
int btrfs_scan_block_devices(int run_ioctl);
|
||||
|
||||
u64 parse_size(char *s);
|
||||
int open_file_or_dir(const char *fname);
|
||||
int get_device_info(int fd, u64 devid,
|
||||
struct btrfs_ioctl_dev_info_args *di_args);
|
||||
int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args,
|
||||
struct btrfs_ioctl_dev_info_args **di_ret);
|
||||
#endif
|
||||
|
|
16
volumes.c
16
volumes.c
|
@ -1385,6 +1385,22 @@ struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct btrfs_device *btrfs_find_device_by_devid(struct btrfs_root *root,
|
||||
u64 devid, int instance)
|
||||
{
|
||||
struct list_head *head = &root->fs_info->fs_devices->devices;
|
||||
struct btrfs_device *dev;
|
||||
struct list_head *cur;
|
||||
int num_found = 0;
|
||||
|
||||
list_for_each(cur, head) {
|
||||
dev = list_entry(cur, struct btrfs_device, dev_list);
|
||||
if (dev->devid == devid && num_found++ == instance)
|
||||
return dev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int btrfs_bootstrap_super_map(struct btrfs_mapping_tree *map_tree,
|
||||
struct btrfs_fs_devices *fs_devices)
|
||||
{
|
||||
|
|
|
@ -188,4 +188,6 @@ int btrfs_add_system_chunk(struct btrfs_trans_handle *trans,
|
|||
struct btrfs_root *root, struct btrfs_key *key,
|
||||
struct btrfs_chunk *chunk, int item_size);
|
||||
int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
|
||||
struct btrfs_device *btrfs_find_device_by_devid(struct btrfs_root *root,
|
||||
u64 devid, int instance);
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue