From 39728eccb8d22a88631e25383636369c2b3dfee6 Mon Sep 17 00:00:00 2001 From: Aneesh Kumar K.V Date: Fri, 29 Jan 2010 23:36:40 +0530 Subject: [PATCH -V2 1/6] btrfs-progs: Move files around Build libbtrfs.a and link against that for the commands Signed-off-by: Aneesh Kumar K.V --- Makefile | 72 +- bcp | 161 --- bit-radix.c | 211 --- bit-radix.h | 33 - btrfs-image.c | 886 ------------- btrfs-map-logical.c | 221 ---- btrfs-show.c | 151 --- btrfs-vol.c | 148 --- btrfsck.c | 2867 ---------------------------------------- btrfsctl.c | 239 ---- btrfstune.c | 126 -- convert.c | 2872 ---------------------------------------- crc32c.c | 101 -- crc32c.h | 28 - ctree.c | 2975 ----------------------------------------- ctree.h | 1882 -------------------------- debug-tree.c | 290 ---- dir-item.c | 336 ----- dir-test.c | 506 ------- disk-io.c | 1029 --------------- disk-io.h | 78 -- extent-cache.c | 172 --- extent-cache.h | 59 - extent-tree.c | 3285 ---------------------------------------------- extent_io.c | 781 ----------- extent_io.h | 117 -- file-item.c | 506 ------- hash.h | 27 - hasher.c | 42 - inode-item.c | 218 --- inode-map.c | 133 -- ioctl.h | 62 - kerncompat.h | 258 ---- lib/Makefile | 47 + lib/bit-radix.c | 211 +++ lib/bit-radix.h | 33 + lib/crc32c.c | 101 ++ lib/crc32c.h | 28 + lib/ctree.c | 2975 +++++++++++++++++++++++++++++++++++++++++ lib/ctree.h | 1882 ++++++++++++++++++++++++++ lib/dir-item.c | 336 +++++ lib/disk-io.c | 1029 +++++++++++++++ lib/disk-io.h | 78 ++ lib/extent-cache.c | 172 +++ lib/extent-cache.h | 59 + lib/extent-tree.c | 3285 ++++++++++++++++++++++++++++++++++++++++++++++ lib/extent_io.c | 781 +++++++++++ lib/extent_io.h | 117 ++ lib/file-item.c | 506 +++++++ lib/hash.h | 27 + lib/inode-item.c | 218 +++ lib/inode-map.c | 133 ++ lib/ioctl.h | 62 + lib/kerncompat.h | 258 ++++ lib/list.h | 436 ++++++ lib/print-tree.c | 667 ++++++++++ lib/print-tree.h | 24 + lib/radix-tree.c | 854 ++++++++++++ lib/radix-tree.h | 93 ++ lib/rbtree.c | 389 ++++++ lib/rbtree.h | 160 +++ lib/root-tree.c | 253 ++++ lib/transaction.h | 60 + lib/utils.c | 822 ++++++++++++ lib/utils.h | 43 + lib/volumes.c | 1450 ++++++++++++++++++++ lib/volumes.h | 133 ++ list.h | 436 ------ misc/Makefile | 79 ++ misc/bcp | 161 +++ misc/btrfs-image.c | 886 +++++++++++++ misc/btrfs-map-logical.c | 221 ++++ misc/btrfs-show.c | 151 +++ misc/btrfs-vol.c | 148 +++ misc/btrfsck.c | 2867 ++++++++++++++++++++++++++++++++++++++++ misc/btrfsctl.c | 239 ++++ misc/btrfstune.c | 126 ++ misc/convert.c | 2872 ++++++++++++++++++++++++++++++++++++++++ misc/debug-tree.c | 290 ++++ misc/mkfs.c | 537 ++++++++ misc/version.sh | 59 + mkfs.c | 537 -------- print-tree.c | 667 ---------- print-tree.h | 24 - quick-test.c | 202 --- radix-tree.c | 854 ------------ radix-tree.h | 93 -- random-test.c | 425 ------ rbtree.c | 389 ------ rbtree.h | 160 --- root-tree.c | 253 ---- show-blocks | 325 ----- tests/Makefile | 88 ++ tests/dir-test.c | 506 +++++++ tests/hasher.c | 42 + tests/quick-test.c | 202 +++ tests/random-test.c | 425 ++++++ tests/show-blocks | 325 +++++ transaction.h | 60 - utils.c | 822 ------------ utils.h | 43 - version.sh | 59 - volumes.c | 1450 -------------------- volumes.h | 133 -- 104 files changed, 27955 insertions(+), 27795 deletions(-) delete mode 100755 bcp delete mode 100644 bit-radix.c delete mode 100644 bit-radix.h delete mode 100644 btrfs-image.c delete mode 100644 btrfs-map-logical.c delete mode 100644 btrfs-show.c delete mode 100644 btrfs-vol.c delete mode 100644 btrfsck.c delete mode 100644 btrfsctl.c delete mode 100644 btrfstune.c delete mode 100644 convert.c delete mode 100644 crc32c.c delete mode 100644 crc32c.h delete mode 100644 ctree.c delete mode 100644 ctree.h delete mode 100644 debug-tree.c delete mode 100644 dir-item.c delete mode 100644 dir-test.c delete mode 100644 disk-io.c delete mode 100644 disk-io.h delete mode 100644 extent-cache.c delete mode 100644 extent-cache.h delete mode 100644 extent-tree.c delete mode 100644 extent_io.c delete mode 100644 extent_io.h delete mode 100644 file-item.c delete mode 100644 hash.h delete mode 100644 hasher.c delete mode 100644 inode-item.c delete mode 100644 inode-map.c delete mode 100644 ioctl.h delete mode 100644 kerncompat.h create mode 100644 lib/Makefile create mode 100644 lib/bit-radix.c create mode 100644 lib/bit-radix.h create mode 100644 lib/crc32c.c create mode 100644 lib/crc32c.h create mode 100644 lib/ctree.c create mode 100644 lib/ctree.h create mode 100644 lib/dir-item.c create mode 100644 lib/disk-io.c create mode 100644 lib/disk-io.h create mode 100644 lib/extent-cache.c create mode 100644 lib/extent-cache.h create mode 100644 lib/extent-tree.c create mode 100644 lib/extent_io.c create mode 100644 lib/extent_io.h create mode 100644 lib/file-item.c create mode 100644 lib/hash.h create mode 100644 lib/inode-item.c create mode 100644 lib/inode-map.c create mode 100644 lib/ioctl.h create mode 100644 lib/kerncompat.h create mode 100644 lib/list.h create mode 100644 lib/print-tree.c create mode 100644 lib/print-tree.h create mode 100644 lib/radix-tree.c create mode 100644 lib/radix-tree.h create mode 100644 lib/rbtree.c create mode 100644 lib/rbtree.h create mode 100644 lib/root-tree.c create mode 100644 lib/transaction.h create mode 100644 lib/utils.c create mode 100644 lib/utils.h create mode 100644 lib/volumes.c create mode 100644 lib/volumes.h delete mode 100644 list.h create mode 100644 misc/Makefile create mode 100644 misc/bcp create mode 100644 misc/btrfs-image.c create mode 100644 misc/btrfs-map-logical.c create mode 100644 misc/btrfs-show.c create mode 100644 misc/btrfs-vol.c create mode 100644 misc/btrfsck.c create mode 100644 misc/btrfsctl.c create mode 100644 misc/btrfstune.c create mode 100644 misc/convert.c create mode 100644 misc/debug-tree.c create mode 100644 misc/mkfs.c create mode 100644 misc/version.sh delete mode 100644 mkfs.c delete mode 100644 print-tree.c delete mode 100644 print-tree.h delete mode 100644 quick-test.c delete mode 100644 radix-tree.c delete mode 100644 radix-tree.h delete mode 100644 random-test.c delete mode 100644 rbtree.c delete mode 100644 rbtree.h delete mode 100644 root-tree.c delete mode 100755 show-blocks create mode 100644 tests/Makefile create mode 100644 tests/dir-test.c create mode 100644 tests/hasher.c create mode 100644 tests/quick-test.c create mode 100644 tests/random-test.c create mode 100644 tests/show-blocks delete mode 100644 transaction.h delete mode 100644 utils.c delete mode 100644 utils.h delete mode 100644 version.sh delete mode 100644 volumes.c delete mode 100644 volumes.h diff --git a/Makefile b/Makefile index 02f881e..a30c212 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,6 @@ CC=gcc AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 CFLAGS = -g -Werror -Os -objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ - root-tree.o dir-item.o file-item.o inode-item.o \ - inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ - volumes.o utils.o - # CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ -Wuninitialized -Wshadow -Wundef @@ -16,8 +11,7 @@ prefix ?= /usr/local bindir = $(prefix)/bin LIBS=-luuid -progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ - btrfs-map-logical +SUBDIRS=lib misc man # make C=1 to enable sparse ifdef C @@ -31,60 +25,12 @@ endif $(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $< -all: version $(progs) manpages - -version: - bash version.sh - -btrfsctl: $(objects) btrfsctl.o - gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) - -btrfs-vol: $(objects) btrfs-vol.o - gcc $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS) - -btrfs-show: $(objects) btrfs-show.o - gcc $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS) - -btrfsck: $(objects) btrfsck.o - gcc $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS) - -mkfs.btrfs: $(objects) mkfs.o - gcc $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS) - -btrfs-debug-tree: $(objects) debug-tree.o - gcc $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS) - -btrfstune: $(objects) btrfstune.o - gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS) - -btrfs-map-logical: $(objects) btrfs-map-logical.o - gcc $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS) - -btrfs-image: $(objects) btrfs-image.o - gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) - -dir-test: $(objects) dir-test.o - gcc $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS) - -quick-test: $(objects) quick-test.o - gcc $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS) - -convert: $(objects) convert.o - gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) $(LIBS) - -manpages: - cd man; make - -install-man: - cd man; make install - -clean : - rm -f $(progs) cscope.out *.o .*.d btrfs-convert - cd man; make clean - -install: $(progs) install-man - $(INSTALL) -m755 -d $(DESTDIR)$(bindir) - $(INSTALL) $(progs) $(DESTDIR)$(bindir) - if [ -e btrfs-convert ]; then $(INSTALL) btrfs-convert $(DESTDIR)$(bindir); fi +all clean install: + @for subdir in $(SUBDIRS); do \ + if test -d $$subdir ; then \ + target=`echo $@`; \ + echo making $$target in $$subdir; \ + (cd $$subdir && $(MAKE) $$target) || exit 1; \ + fi ; \ + done --include .*.d diff --git a/bcp b/bcp deleted file mode 100755 index 5729e91..0000000 --- a/bcp +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env python -# Copyright (C) 2007 Oracle. 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. -# -import sys, os, stat, fcntl -from optparse import OptionParser - -def copylink(srcname, dst, filename, statinfo, force_name): - dstname = os.path.join(dst, force_name or filename) - if not os.path.exists(dstname): - link_target = os.readlink(srcname) - os.symlink(link_target, dstname) - -def copydev(srcname, dst, filename, statinfo, force_name): - devbits = statinfo.st_mode & (stat.S_IFBLK | stat.S_IFCHR) - mode = stat.S_IMODE(statinfo.st_mode) | devbits - dstname = os.path.join(dst, force_name or filename) - if not os.path.exists(dstname): - os.mknod(dstname, mode, statinfo.st_rdev) - -def copyfile(srcname, dst, filename, statinfo, force_name): - written = 0 - dstname = os.path.join(dst, force_name or filename) - - st_mode = statinfo.st_mode - if stat.S_ISLNK(st_mode): - copylink(srcname, dst, part, statinfo, None) - return - elif stat.S_ISBLK(st_mode) or stat.S_ISCHR(st_mode): - copydev(srcname, dst, part, statinfo, None) - return - elif not stat.S_ISREG(st_mode): - return - - try: - os.unlink(dstname) - except: - pass - - if options.link: - os.link(srcname, dstname) - return - - dstf = file(dstname, 'w') - srcf = file(srcname, 'r') - - ret = 1 - - try: - if not options.copy: - ret = fcntl.ioctl(dstf.fileno(), 1074041865, srcf.fileno()) - except: - pass - - if ret != 0: - while True: - buf = srcf.read(256 * 1024) - if not buf: - break - written += len(buf) - dstf.write(buf) - - os.chmod(dstname, stat.S_IMODE(statinfo.st_mode)) - os.chown(dstname, statinfo.st_uid, statinfo.st_gid) - - -usage = "usage: %prog [options]" -parser = OptionParser(usage=usage) -parser.add_option("-l", "--link", help="Create hard links", default=False, - action="store_true") -parser.add_option("-c", "--copy", help="Copy file bytes (don't cow)", - default=False, action="store_true") - -(options,args) = parser.parse_args() - -if len(args) < 2: - sys.stderr.write("source or destination not specified\n") - sys.exit(1) - -if options.link and options.copy: - sys.stderr.write("Both -l and -c specified, using copy mode\n") - options.link = False - - -total_args = len(args) -src_args = total_args - 1 -orig_dst = args[-1] - -if src_args > 1: - if not os.path.exists(orig_dst): - os.makedirs(orig_dst) - if not os.path.isdir(orig_dst): - sys.stderr.write("Destination %s is not a directory\n" % orig_dst) - exit(1) - -for srci in xrange(0, src_args): - src = args[srci] - if os.path.isfile(src): - statinfo = os.lstat(src) - force_name = None - if src_args == 1: - if not os.path.isdir(orig_dst): - force_name = os.path.basename(orig_dst) - orig_dst = os.path.dirname(orig_dst) or '.' - copyfile(src, orig_dst, os.path.basename(src), statinfo, force_name) - continue - - if src_args > 1 or os.path.exists(orig_dst): - dst = os.path.join(orig_dst, os.path.basename(src)) - else: - dst = orig_dst - - if not os.path.exists(dst): - os.makedirs(dst) - statinfo = os.stat(src) - os.chmod(dst, stat.S_IMODE(statinfo.st_mode)) - os.chown(dst, statinfo.st_uid, statinfo.st_gid) - - iter = os.walk(src, topdown=True) - - for (dirpath, dirnames, filenames) in iter: - for x in dirnames: - srcname = os.path.join(dirpath, x) - statinfo = os.lstat(srcname) - - if srcname.startswith(src): - part = srcname[len(src) + 1:] - - if stat.S_ISLNK(statinfo.st_mode): - copylink(srcname, dst, part, statinfo, None) - continue - - dst_dir = os.path.join(dst, part) - if not os.path.exists(dst_dir): - os.makedirs(dst_dir) - - os.chmod(dst_dir, stat.S_IMODE(statinfo.st_mode)) - os.chown(dst_dir, statinfo.st_uid, statinfo.st_gid) - - for f in filenames: - srcname = os.path.join(dirpath, f) - if srcname.startswith(src): - part = srcname[len(src) + 1:] - - statinfo = os.lstat(srcname) - copyfile(srcname, dst, part, statinfo, None) - - diff --git a/bit-radix.c b/bit-radix.c deleted file mode 100644 index 57f6f3c..0000000 --- a/bit-radix.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 "kerncompat.h" -#include "radix-tree.h" - -#define BIT_ARRAY_BYTES 256 -#define BIT_RADIX_BITS_PER_ARRAY ((BIT_ARRAY_BYTES - sizeof(unsigned long)) * 8) - -int set_radix_bit(struct radix_tree_root *radix, unsigned long bit) -{ - unsigned long *bits; - unsigned long slot; - int bit_slot; - int ret; - - slot = bit / BIT_RADIX_BITS_PER_ARRAY; - bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY; - - bits = radix_tree_lookup(radix, slot); - if (!bits) { - bits = malloc(BIT_ARRAY_BYTES); - if (!bits) - return -ENOMEM; - memset(bits + 1, 0, BIT_ARRAY_BYTES - sizeof(unsigned long)); - bits[0] = slot; - radix_tree_preload(GFP_NOFS); - ret = radix_tree_insert(radix, slot, bits); - radix_tree_preload_end(); - if (ret) - return ret; - } - __set_bit(bit_slot, bits + 1); - return 0; -} - -int test_radix_bit(struct radix_tree_root *radix, unsigned long bit) -{ - unsigned long *bits; - unsigned long slot; - int bit_slot; - - slot = bit / BIT_RADIX_BITS_PER_ARRAY; - bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY; - - bits = radix_tree_lookup(radix, slot); - if (!bits) - return 0; - return test_bit(bit_slot, bits + 1); -} - -int clear_radix_bit(struct radix_tree_root *radix, unsigned long bit) -{ - unsigned long *bits; - unsigned long slot; - int bit_slot; - int i; - int empty = 1; - slot = bit / BIT_RADIX_BITS_PER_ARRAY; - bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY; - - bits = radix_tree_lookup(radix, slot); - if (!bits) - return 0; - __clear_bit(bit_slot, bits + 1); - for (i = 1; i < BIT_ARRAY_BYTES / sizeof(unsigned long); i++) { - if (bits[i]) { - empty = 0; - break; - } - } - if (empty) { - bits = radix_tree_delete(radix, slot); - BUG_ON(!bits); - free(bits); - } - return 0; -} - -#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) - -/** - * __ffs - find first bit in word. - * @word: The word to search - * - * Undefined if no bit exists, so code should check against 0 first. - */ -static unsigned long __ffs(unsigned long word) -{ - int num = 0; - - if (sizeof(long) == 8 && (word & 0xffffffff) == 0) { - num += 32; - word >>= sizeof(long) * 4; - } - if ((word & 0xffff) == 0) { - num += 16; - word >>= 16; - } - if ((word & 0xff) == 0) { - num += 8; - word >>= 8; - } - if ((word & 0xf) == 0) { - num += 4; - word >>= 4; - } - if ((word & 0x3) == 0) { - num += 2; - word >>= 2; - } - if ((word & 0x1) == 0) - num += 1; - return num; -} - -/** - * find_next_bit - find the next set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -unsigned long find_next_bit(const unsigned long *addr, unsigned long size, - unsigned long offset) -{ - const unsigned long *p = addr + BITOP_WORD(offset); - unsigned long result = offset & ~(BITS_PER_LONG-1); - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset %= BITS_PER_LONG; - if (offset) { - tmp = *(p++); - tmp &= (~0UL << offset); - if (size < BITS_PER_LONG) - goto found_first; - if (tmp) - goto found_middle; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - } - while (size & ~(BITS_PER_LONG-1)) { - if ((tmp = *(p++))) - goto found_middle; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= (~0UL >> (BITS_PER_LONG - size)); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -int find_first_radix_bit(struct radix_tree_root *radix, unsigned long *retbits, - unsigned long start, int nr) -{ - unsigned long *bits; - unsigned long *gang[4]; - int found; - int ret; - int i; - int total_found = 0; - unsigned long slot; - - slot = start / BIT_RADIX_BITS_PER_ARRAY; - ret = radix_tree_gang_lookup(radix, (void *)gang, slot, - ARRAY_SIZE(gang)); - found = start % BIT_RADIX_BITS_PER_ARRAY; - for (i = 0; i < ret && nr > 0; i++) { - bits = gang[i]; - while(nr > 0) { - found = find_next_bit(bits + 1, - BIT_RADIX_BITS_PER_ARRAY, - found); - if (found < BIT_RADIX_BITS_PER_ARRAY) { - *retbits = bits[0] * - BIT_RADIX_BITS_PER_ARRAY + found; - retbits++; - nr--; - total_found++; - found++; - } else - break; - } - found = 0; - } - return total_found; -} diff --git a/bit-radix.h b/bit-radix.h deleted file mode 100644 index af14e80..0000000 --- a/bit-radix.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __BIT_RADIX__ -#define __BIT_RADIX__ -#include "radix-tree.h" - -int set_radix_bit(struct radix_tree_root *radix, unsigned long bit); -int test_radix_bit(struct radix_tree_root *radix, unsigned long bit); -int clear_radix_bit(struct radix_tree_root *radix, unsigned long bit); -int find_first_radix_bit(struct radix_tree_root *radix, unsigned long *retbits, - unsigned long start, int nr); - -static inline void init_bit_radix(struct radix_tree_root *radix) -{ - INIT_RADIX_TREE(radix, GFP_NOFS); -} -#endif diff --git a/btrfs-image.c b/btrfs-image.c deleted file mode 100644 index f2bbcc8..0000000 --- a/btrfs-image.c +++ /dev/null @@ -1,886 +0,0 @@ -/* - * Copyright (C) 2008 Oracle. 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "crc32c.h" -#include "ctree.h" -#include "disk-io.h" -#include "transaction.h" -#include "utils.h" -#include "version.h" - - -#define HEADER_MAGIC 0xbd5c25e27295668bULL -#define MAX_PENDING_SIZE (256 * 1024) -#define BLOCK_SIZE 1024 -#define BLOCK_MASK (BLOCK_SIZE - 1) - -#define COMPRESS_NONE 0 -#define COMPRESS_ZLIB 1 - -struct meta_cluster_item { - __le64 bytenr; - __le32 size; -} __attribute__ ((__packed__)); - -struct meta_cluster_header { - __le64 magic; - __le64 bytenr; - __le32 nritems; - u8 compress; -} __attribute__ ((__packed__)); - -/* cluster header + index items + buffers */ -struct meta_cluster { - struct meta_cluster_header header; - struct meta_cluster_item items[]; -} __attribute__ ((__packed__)); - -#define ITEMS_PER_CLUSTER ((BLOCK_SIZE - sizeof(struct meta_cluster)) / \ - sizeof(struct meta_cluster_item)) - -struct async_work { - struct list_head list; - struct list_head ordered; - u64 start; - u64 size; - u8 *buffer; - size_t bufsize; -}; - -struct metadump_struct { - struct btrfs_root *root; - FILE *out; - - struct meta_cluster *cluster; - - pthread_t *threads; - size_t num_threads; - pthread_mutex_t mutex; - pthread_cond_t cond; - - struct list_head list; - struct list_head ordered; - size_t num_items; - size_t num_ready; - - u64 pending_start; - u64 pending_size; - - int compress_level; - int done; -}; - -struct mdrestore_struct { - FILE *in; - FILE *out; - - pthread_t *threads; - size_t num_threads; - pthread_mutex_t mutex; - pthread_cond_t cond; - - struct list_head list; - size_t num_items; - - int compress_method; - int done; -}; - -static void csum_block(u8 *buf, size_t len) -{ - char result[BTRFS_CRC32_SIZE]; - u32 crc = ~(u32)0; - crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len - BTRFS_CSUM_SIZE); - btrfs_csum_final(crc, result); - memcpy(buf, result, BTRFS_CRC32_SIZE); -} - -/* - * zero inline extents and csum items - */ -static void zero_items(u8 *dst, struct extent_buffer *src) -{ - struct btrfs_file_extent_item *fi; - struct btrfs_item *item; - struct btrfs_key key; - u32 nritems = btrfs_header_nritems(src); - size_t size; - unsigned long ptr; - int i, extent_type; - - for (i = 0; i < nritems; i++) { - item = btrfs_item_nr(src, i); - btrfs_item_key_to_cpu(src, &key, i); - if (key.type == BTRFS_CSUM_ITEM_KEY) { - size = btrfs_item_size_nr(src, i); - memset(dst + btrfs_leaf_data(src) + - btrfs_item_offset_nr(src, i), 0, size); - continue; - } - if (key.type != BTRFS_EXTENT_DATA_KEY) - continue; - - fi = btrfs_item_ptr(src, i, struct btrfs_file_extent_item); - extent_type = btrfs_file_extent_type(src, fi); - if (extent_type != BTRFS_FILE_EXTENT_INLINE) - continue; - - ptr = btrfs_file_extent_inline_start(fi); - size = btrfs_file_extent_inline_item_len(src, item); - memset(dst + ptr, 0, size); - } -} - -/* - * copy buffer and zero useless data in the buffer - */ -static void copy_buffer(u8 *dst, struct extent_buffer *src) -{ - int level; - size_t size; - u32 nritems; - - memcpy(dst, src->data, src->len); - if (src->start == BTRFS_SUPER_INFO_OFFSET) - return; - - level = btrfs_header_level(src); - nritems = btrfs_header_nritems(src); - - if (nritems == 0) { - size = sizeof(struct btrfs_header); - memset(dst + size, 0, src->len - size); - } else if (level == 0) { - size = btrfs_leaf_data(src) + - btrfs_item_offset_nr(src, nritems - 1) - - btrfs_item_nr_offset(nritems); - memset(dst + btrfs_item_nr_offset(nritems), 0, size); - zero_items(dst, src); - } else { - size = offsetof(struct btrfs_node, ptrs) + - sizeof(struct btrfs_key_ptr) * nritems; - memset(dst + size, 0, src->len - size); - } - csum_block(dst, src->len); -} - -static void *dump_worker(void *data) -{ - struct metadump_struct *md = (struct metadump_struct *)data; - struct async_work *async; - int ret; - - while (1) { - pthread_mutex_lock(&md->mutex); - while (list_empty(&md->list)) { - if (md->done) { - pthread_mutex_unlock(&md->mutex); - goto out; - } - pthread_cond_wait(&md->cond, &md->mutex); - } - async = list_entry(md->list.next, struct async_work, list); - list_del_init(&async->list); - pthread_mutex_unlock(&md->mutex); - - if (md->compress_level > 0) { - u8 *orig = async->buffer; - - async->bufsize = compressBound(async->size); - async->buffer = malloc(async->bufsize); - - ret = compress2(async->buffer, - (unsigned long *)&async->bufsize, - orig, async->size, md->compress_level); - BUG_ON(ret != Z_OK); - - free(orig); - } - - pthread_mutex_lock(&md->mutex); - md->num_ready++; - pthread_mutex_unlock(&md->mutex); - } -out: - pthread_exit(NULL); -} - -static void meta_cluster_init(struct metadump_struct *md, u64 start) -{ - struct meta_cluster_header *header; - - md->num_items = 0; - md->num_ready = 0; - header = &md->cluster->header; - header->magic = cpu_to_le64(HEADER_MAGIC); - header->bytenr = cpu_to_le64(start); - header->nritems = cpu_to_le32(0); - header->compress = md->compress_level > 0 ? - COMPRESS_ZLIB : COMPRESS_NONE; -} - -static int metadump_init(struct metadump_struct *md, struct btrfs_root *root, - FILE *out, int num_threads, int compress_level) -{ - int i, ret; - - memset(md, 0, sizeof(*md)); - pthread_cond_init(&md->cond, NULL); - pthread_mutex_init(&md->mutex, NULL); - INIT_LIST_HEAD(&md->list); - INIT_LIST_HEAD(&md->ordered); - md->root = root; - md->out = out; - md->pending_start = (u64)-1; - md->compress_level = compress_level; - md->cluster = calloc(1, BLOCK_SIZE); - if (!md->cluster) - return -ENOMEM; - - meta_cluster_init(md, 0); - if (!num_threads) - return 0; - - md->num_threads = num_threads; - md->threads = calloc(num_threads, sizeof(pthread_t)); - if (!md->threads) - return -ENOMEM; - for (i = 0; i < num_threads; i++) { - ret = pthread_create(md->threads + i, NULL, dump_worker, md); - if (ret) - break; - } - return ret; -} - -static void metadump_destroy(struct metadump_struct *md) -{ - int i; - pthread_mutex_lock(&md->mutex); - md->done = 1; - pthread_cond_broadcast(&md->cond); - pthread_mutex_unlock(&md->mutex); - - for (i = 0; i < md->num_threads; i++) - pthread_join(md->threads[i], NULL); - - pthread_cond_destroy(&md->cond); - pthread_mutex_destroy(&md->mutex); - free(md->threads); - free(md->cluster); -} - -static int write_zero(FILE *out, size_t size) -{ - static char zero[BLOCK_SIZE]; - return fwrite(zero, size, 1, out); -} - -static int write_buffers(struct metadump_struct *md, u64 *next) -{ - struct meta_cluster_header *header = &md->cluster->header; - struct meta_cluster_item *item; - struct async_work *async; - u64 bytenr = 0; - u32 nritems = 0; - int ret; - - if (list_empty(&md->ordered)) - goto out; - - /* wait until all buffers are compressed */ - while (md->num_items > md->num_ready) { - struct timespec ts = { - .tv_sec = 0, - .tv_nsec = 10000000, - }; - pthread_mutex_unlock(&md->mutex); - nanosleep(&ts, NULL); - pthread_mutex_lock(&md->mutex); - } - - /* setup and write index block */ - list_for_each_entry(async, &md->ordered, ordered) { - item = md->cluster->items + nritems; - item->bytenr = cpu_to_le64(async->start); - item->size = cpu_to_le32(async->bufsize); - nritems++; - } - header->nritems = cpu_to_le32(nritems); - - ret = fwrite(md->cluster, BLOCK_SIZE, 1, md->out); - BUG_ON(ret != 1); - - /* write buffers */ - bytenr += le64_to_cpu(header->bytenr) + BLOCK_SIZE; - while (!list_empty(&md->ordered)) { - async = list_entry(md->ordered.next, struct async_work, - ordered); - list_del_init(&async->ordered); - - bytenr += async->bufsize; - ret = fwrite(async->buffer, async->bufsize, 1, md->out); - BUG_ON(ret != 1); - - free(async->buffer); - free(async); - } - - /* zero unused space in the last block */ - if (bytenr & BLOCK_MASK) { - size_t size = BLOCK_SIZE - (bytenr & BLOCK_MASK); - - bytenr += size; - ret = write_zero(md->out, size); - BUG_ON(ret != 1); - } -out: - *next = bytenr; - return 0; -} - -static int flush_pending(struct metadump_struct *md, int done) -{ - struct async_work *async = NULL; - struct extent_buffer *eb; - u64 blocksize = md->root->nodesize; - u64 start; - u64 size; - size_t offset; - int ret; - - if (md->pending_size) { - async = calloc(1, sizeof(*async)); - if (!async) - return -ENOMEM; - - async->start = md->pending_start; - async->size = md->pending_size; - async->bufsize = async->size; - async->buffer = malloc(async->bufsize); - - offset = 0; - start = async->start; - size = async->size; - while (size > 0) { - eb = read_tree_block(md->root, start, blocksize, 0); - BUG_ON(!eb); - copy_buffer(async->buffer + offset, eb); - free_extent_buffer(eb); - start += blocksize; - offset += blocksize; - size -= blocksize; - } - - md->pending_start = (u64)-1; - md->pending_size = 0; - } else if (!done) { - return 0; - } - - pthread_mutex_lock(&md->mutex); - if (async) { - list_add_tail(&async->ordered, &md->ordered); - md->num_items++; - if (md->compress_level > 0) { - list_add_tail(&async->list, &md->list); - pthread_cond_signal(&md->cond); - } else { - md->num_ready++; - } - } - if (md->num_items >= ITEMS_PER_CLUSTER || done) { - ret = write_buffers(md, &start); - BUG_ON(ret); - meta_cluster_init(md, start); - } - pthread_mutex_unlock(&md->mutex); - return 0; -} - -static int add_metadata(u64 start, u64 size, struct metadump_struct *md) -{ - int ret; - if (md->pending_size + size > MAX_PENDING_SIZE || - md->pending_start + md->pending_size != start) { - ret = flush_pending(md, 0); - if (ret) - return ret; - md->pending_start = start; - } - readahead_tree_block(md->root, start, size, 0); - md->pending_size += size; - return 0; -} - -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 -static int is_tree_block(struct btrfs_root *extent_root, - struct btrfs_path *path, u64 bytenr) -{ - struct extent_buffer *leaf; - struct btrfs_key key; - u64 ref_objectid; - int ret; - - leaf = path->nodes[0]; - while (1) { - struct btrfs_extent_ref_v0 *ref_item; - path->slots[0]++; - if (path->slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(extent_root, path); - BUG_ON(ret < 0); - if (ret > 0) - break; - leaf = path->nodes[0]; - } - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - if (key.objectid != bytenr) - break; - if (key.type != BTRFS_EXTENT_REF_V0_KEY) - continue; - ref_item = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_ref_v0); - ref_objectid = btrfs_ref_objectid_v0(leaf, ref_item); - if (ref_objectid < BTRFS_FIRST_FREE_OBJECTID) - return 1; - break; - } - return 0; -} -#endif - -static int create_metadump(const char *input, FILE *out, int num_threads, - int compress_level) -{ - struct btrfs_root *root; - struct btrfs_root *extent_root; - struct btrfs_path *path; - struct extent_buffer *leaf; - struct btrfs_extent_item *ei; - struct btrfs_key key; - struct metadump_struct metadump; - u64 bytenr; - u64 num_bytes; - int ret; - - root = open_ctree(input, 0, 0); - BUG_ON(root->nodesize != root->leafsize); - - ret = metadump_init(&metadump, root, out, num_threads, - compress_level); - BUG_ON(ret); - - ret = add_metadata(BTRFS_SUPER_INFO_OFFSET, 4096, &metadump); - BUG_ON(ret); - - extent_root = root->fs_info->extent_root; - path = btrfs_alloc_path(); - - bytenr = BTRFS_SUPER_INFO_OFFSET + 4096; - key.objectid = bytenr; - key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = 0; - - ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); - BUG_ON(ret < 0); - - while (1) { - leaf = path->nodes[0]; - if (path->slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(extent_root, path); - BUG_ON(ret < 0); - if (ret > 0) - break; - leaf = path->nodes[0]; - } - - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - if (key.objectid < bytenr || - key.type != BTRFS_EXTENT_ITEM_KEY) { - path->slots[0]++; - continue; - } - - bytenr = key.objectid; - num_bytes = key.offset; - - if (btrfs_item_size_nr(leaf, path->slots[0]) > sizeof(*ei)) { - ei = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_item); - if (btrfs_extent_flags(leaf, ei) & - BTRFS_EXTENT_FLAG_TREE_BLOCK) { - ret = add_metadata(bytenr, num_bytes, - &metadump); - BUG_ON(ret); - } - } else { -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - if (is_tree_block(extent_root, path, bytenr)) { - ret = add_metadata(bytenr, num_bytes, - &metadump); - BUG_ON(ret); - } -#else - BUG_ON(1); -#endif - } - bytenr += num_bytes; - } - - ret = flush_pending(&metadump, 1); - BUG_ON(ret); - - metadump_destroy(&metadump); - - btrfs_free_path(path); - ret = close_ctree(root); - return 0; -} - -static void update_super(u8 *buffer) -{ - struct btrfs_super_block *super = (struct btrfs_super_block *)buffer; - struct btrfs_chunk *chunk; - struct btrfs_disk_key *key; - u32 sectorsize = btrfs_super_sectorsize(super); - u64 flags = btrfs_super_flags(super); - - flags |= BTRFS_SUPER_FLAG_METADUMP; - btrfs_set_super_flags(super, flags); - - key = (struct btrfs_disk_key *)(super->sys_chunk_array); - chunk = (struct btrfs_chunk *)(super->sys_chunk_array + - sizeof(struct btrfs_disk_key)); - - btrfs_set_disk_key_objectid(key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_disk_key_type(key, BTRFS_CHUNK_ITEM_KEY); - btrfs_set_disk_key_offset(key, 0); - - btrfs_set_stack_chunk_length(chunk, (u64)-1); - btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_stack_chunk_stripe_len(chunk, 64 * 1024); - btrfs_set_stack_chunk_type(chunk, BTRFS_BLOCK_GROUP_SYSTEM); - btrfs_set_stack_chunk_io_align(chunk, sectorsize); - btrfs_set_stack_chunk_io_width(chunk, sectorsize); - btrfs_set_stack_chunk_sector_size(chunk, sectorsize); - btrfs_set_stack_chunk_num_stripes(chunk, 1); - btrfs_set_stack_chunk_sub_stripes(chunk, 0); - chunk->stripe.devid = super->dev_item.devid; - chunk->stripe.offset = cpu_to_le64(0); - memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid, BTRFS_UUID_SIZE); - btrfs_set_super_sys_array_size(super, sizeof(*key) + sizeof(*chunk)); - csum_block(buffer, 4096); -} - -static void *restore_worker(void *data) -{ - struct mdrestore_struct *mdres = (struct mdrestore_struct *)data; - struct async_work *async; - size_t size; - u8 *buffer; - u8 *outbuf; - int outfd; - int ret; - - outfd = fileno(mdres->out); - buffer = malloc(MAX_PENDING_SIZE * 2); - BUG_ON(!buffer); - - while (1) { - pthread_mutex_lock(&mdres->mutex); - while (list_empty(&mdres->list)) { - if (mdres->done) { - pthread_mutex_unlock(&mdres->mutex); - goto out; - } - pthread_cond_wait(&mdres->cond, &mdres->mutex); - } - async = list_entry(mdres->list.next, struct async_work, list); - list_del_init(&async->list); - pthread_mutex_unlock(&mdres->mutex); - - if (mdres->compress_method == COMPRESS_ZLIB) { - size = MAX_PENDING_SIZE * 2; - ret = uncompress(buffer, (unsigned long *)&size, - async->buffer, async->bufsize); - BUG_ON(ret != Z_OK); - outbuf = buffer; - } else { - outbuf = async->buffer; - size = async->bufsize; - } - - if (async->start == BTRFS_SUPER_INFO_OFFSET) - update_super(outbuf); - - ret = pwrite64(outfd, outbuf, size, async->start); - BUG_ON(ret != size); - - pthread_mutex_lock(&mdres->mutex); - mdres->num_items--; - pthread_mutex_unlock(&mdres->mutex); - - free(async->buffer); - free(async); - } -out: - free(buffer); - pthread_exit(NULL); -} - -static int mdresotre_init(struct mdrestore_struct *mdres, - FILE *in, FILE *out, int num_threads) -{ - int i, ret = 0; - - memset(mdres, 0, sizeof(*mdres)); - pthread_cond_init(&mdres->cond, NULL); - pthread_mutex_init(&mdres->mutex, NULL); - INIT_LIST_HEAD(&mdres->list); - mdres->in = in; - mdres->out = out; - - if (!num_threads) - return 0; - - mdres->num_threads = num_threads; - mdres->threads = calloc(num_threads, sizeof(pthread_t)); - if (!mdres->threads) - return -ENOMEM; - for (i = 0; i < num_threads; i++) { - ret = pthread_create(mdres->threads + i, NULL, restore_worker, - mdres); - if (ret) - break; - } - return ret; -} - -static void mdresotre_destroy(struct mdrestore_struct *mdres) -{ - int i; - pthread_mutex_lock(&mdres->mutex); - mdres->done = 1; - pthread_cond_broadcast(&mdres->cond); - pthread_mutex_unlock(&mdres->mutex); - - for (i = 0; i < mdres->num_threads; i++) - pthread_join(mdres->threads[i], NULL); - - pthread_cond_destroy(&mdres->cond); - pthread_mutex_destroy(&mdres->mutex); - free(mdres->threads); -} - -static int add_cluster(struct meta_cluster *cluster, - struct mdrestore_struct *mdres, u64 *next) -{ - struct meta_cluster_item *item; - struct meta_cluster_header *header = &cluster->header; - struct async_work *async; - u64 bytenr; - u32 i, nritems; - int ret; - - BUG_ON(mdres->num_items); - mdres->compress_method = header->compress; - - bytenr = le64_to_cpu(header->bytenr) + BLOCK_SIZE; - nritems = le32_to_cpu(header->nritems); - for (i = 0; i < nritems; i++) { - item = &cluster->items[i]; - async = calloc(1, sizeof(*async)); - async->start = le64_to_cpu(item->bytenr); - async->bufsize = le32_to_cpu(item->size); - async->buffer = malloc(async->bufsize); - ret = fread(async->buffer, async->bufsize, 1, mdres->in); - BUG_ON(ret != 1); - bytenr += async->bufsize; - - pthread_mutex_lock(&mdres->mutex); - list_add_tail(&async->list, &mdres->list); - mdres->num_items++; - pthread_cond_signal(&mdres->cond); - pthread_mutex_unlock(&mdres->mutex); - } - if (bytenr & BLOCK_MASK) { - char buffer[BLOCK_MASK]; - size_t size = BLOCK_SIZE - (bytenr & BLOCK_MASK); - - bytenr += size; - ret = fread(buffer, size, 1, mdres->in); - BUG_ON(ret != 1); - } - *next = bytenr; - return 0; -} - -static int wait_for_worker(struct mdrestore_struct *mdres) -{ - pthread_mutex_lock(&mdres->mutex); - while (mdres->num_items > 0) { - struct timespec ts = { - .tv_sec = 0, - .tv_nsec = 10000000, - }; - pthread_mutex_unlock(&mdres->mutex); - nanosleep(&ts, NULL); - pthread_mutex_lock(&mdres->mutex); - } - pthread_mutex_unlock(&mdres->mutex); - return 0; -} - -static int restore_metadump(const char *input, FILE *out, int num_threads) -{ - struct meta_cluster *cluster; - struct meta_cluster_header *header; - struct mdrestore_struct mdrestore; - u64 bytenr = 0; - FILE *in; - int ret; - - if (!strcmp(input, "-")) { - in = stdin; - } else { - in = fopen(input, "r"); - if (!in) { - perror("unable to open metadump image"); - return 1; - } - } - - cluster = malloc(BLOCK_SIZE); - BUG_ON(!cluster); - - ret = mdresotre_init(&mdrestore, in, out, num_threads); - BUG_ON(ret); - - while (1) { - ret = fread(cluster, BLOCK_SIZE, 1, in); - if (!ret) - break; - - header = &cluster->header; - if (le64_to_cpu(header->magic) != HEADER_MAGIC || - le64_to_cpu(header->bytenr) != bytenr) { - fprintf(stderr, "bad header in metadump image\n"); - return 1; - } - ret = add_cluster(cluster, &mdrestore, &bytenr); - BUG_ON(ret); - - wait_for_worker(&mdrestore); - } - - mdresotre_destroy(&mdrestore); - free(cluster); - if (in != stdin) - fclose(in); - return ret; -} - -static void print_usage(void) -{ - fprintf(stderr, "usage: btrfs-image [options] source target\n"); - fprintf(stderr, "\t-r \trestore metadump image\n"); - fprintf(stderr, "\t-c value\tcompression level (0 ~ 9)\n"); - fprintf(stderr, "\t-t value\tnumber of threads (1 ~ 32)\n"); - exit(1); -} - -int main(int argc, char *argv[]) -{ - char *source; - char *target; - int num_threads = 0; - int compress_level = 0; - int create = 1; - int ret; - FILE *out; - - while (1) { - int c = getopt(argc, argv, "rc:t:"); - if (c < 0) - break; - switch (c) { - case 'r': - create = 0; - break; - case 't': - num_threads = atoi(optarg); - if (num_threads <= 0 || num_threads > 32) - print_usage(); - break; - case 'c': - compress_level = atoi(optarg); - if (compress_level < 0 || compress_level > 9) - print_usage(); - break; - default: - print_usage(); - } - } - - argc = argc - optind; - if (argc != 2) - print_usage(); - source = argv[optind]; - target = argv[optind + 1]; - - if (create && !strcmp(target, "-")) { - out = stdout; - } else { - out = fopen(target, "w+"); - if (!out) { - perror("unable to create target file"); - exit(1); - } - } - - if (num_threads == 0 && compress_level > 0) { - num_threads = sysconf(_SC_NPROCESSORS_ONLN); - if (num_threads <= 0) - num_threads = 1; - } - - if (create) - ret = create_metadump(source, out, num_threads, - compress_level); - else - ret = restore_metadump(source, out, 1); - - if (out == stdout) - fflush(out); - else - fclose(out); - - exit(ret); -} diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c deleted file mode 100644 index a109c6a..0000000 --- a/btrfs-map-logical.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2009 Oracle. 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 -#include -#include -#include -#include -#include "kerncompat.h" -#include "ctree.h" -#include "volumes.h" -#include "disk-io.h" -#include "print-tree.h" -#include "transaction.h" -#include "list.h" -#include "version.h" - -/* we write the mirror info to stdout unless they are dumping the data - * to stdout - * */ -static FILE *info_file; - -struct extent_buffer *debug_read_block(struct btrfs_root *root, u64 bytenr, - u32 blocksize, int copy) -{ - int ret; - int dev_nr; - struct extent_buffer *eb; - u64 length; - struct btrfs_multi_bio *multi = NULL; - struct btrfs_device *device; - int num_copies; - int mirror_num = 1; - - eb = btrfs_find_create_tree_block(root, bytenr, blocksize); - if (!eb) - return NULL; - - dev_nr = 0; - length = blocksize; - while (1) { - ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, - eb->start, &length, &multi, mirror_num); - BUG_ON(ret); - device = multi->stripes[0].dev; - eb->fd = device->fd; - device->total_ios++; - eb->dev_bytenr = multi->stripes[0].physical; - - fprintf(info_file, "mirror %d logical %Lu physical %Lu " - "device %s\n", mirror_num, bytenr, eb->dev_bytenr, - device->name); - kfree(multi); - - if (!copy || mirror_num == copy) - ret = read_extent_from_disk(eb); - - num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, - eb->start, eb->len); - if (num_copies == 1) - break; - - mirror_num++; - if (mirror_num > num_copies) - break; - } - return eb; -} - -static void print_usage(void) -{ - fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); - fprintf(stderr, "\t-l Logical extent to map\n"); - fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); - fprintf(stderr, "\t-o Output file to hold the extent\n"); - fprintf(stderr, "\t-s Number of bytes to read\n"); - exit(1); -} - -static struct option long_options[] = { - /* { "byte-count", 1, NULL, 'b' }, */ - { "logical", 1, NULL, 'l' }, - { "copy", 1, NULL, 'c' }, - { "output", 1, NULL, 'c' }, - { "bytes", 1, NULL, 'b' }, - { 0, 0, 0, 0} -}; - -int main(int ac, char **av) -{ - struct cache_tree root_cache; - struct btrfs_root *root; - struct extent_buffer *eb; - char *dev; - char *output_file = NULL; - u64 logical = 0; - int ret = 0; - int option_index = 0; - int copy = 0; - u64 bytes = 0; - int out_fd = 0; - int err; - - while(1) { - int c; - c = getopt_long(ac, av, "l:c:o:b:", long_options, - &option_index); - if (c < 0) - break; - switch(c) { - case 'l': - logical = atoll(optarg); - if (logical == 0) { - fprintf(stderr, - "invalid extent number\n"); - print_usage(); - } - break; - case 'c': - copy = atoi(optarg); - if (copy == 0) { - fprintf(stderr, - "invalid copy number\n"); - print_usage(); - } - break; - case 'b': - bytes = atoll(optarg); - if (bytes == 0) { - fprintf(stderr, - "invalid byte count\n"); - print_usage(); - } - break; - case 'o': - output_file = strdup(optarg); - break; - default: - print_usage(); - } - } - ac = ac - optind; - if (ac == 0) - print_usage(); - if (logical == 0) - print_usage(); - if (copy < 0) - print_usage(); - - dev = av[optind]; - - radix_tree_init(); - cache_tree_init(&root_cache); - - root = open_ctree(dev, 0, 0); - if (!root) { - fprintf(stderr, "Open ctree failed\n"); - exit(1); - } - - if (output_file) { - if (strcmp(output_file, "-") == 0) { - out_fd = 1; - info_file = stderr; - } else { - out_fd = open(output_file, O_RDWR | O_CREAT, 0600); - if (out_fd < 0) - goto close; - err = ftruncate(out_fd, 0); - if (err) { - close(out_fd); - goto close; - } - info_file = stdout; - } - } - - if (bytes == 0) - bytes = root->sectorsize; - - bytes = (bytes + root->sectorsize - 1) / root->sectorsize; - bytes *= root->sectorsize; - - while (bytes > 0) { - eb = debug_read_block(root, logical, root->sectorsize, copy); - if (eb && output_file) { - err = write(out_fd, eb->data, eb->len); - if (err < 0 || err != eb->len) { - fprintf(stderr, "output file write failed\n"); - goto out_close_fd; - } - } - free_extent_buffer(eb); - logical += root->sectorsize; - bytes -= root->sectorsize; - } - -out_close_fd: - if (output_file && out_fd != 1) - close(out_fd); -close: - close_ctree(root); - return ret; -} diff --git a/btrfs-show.c b/btrfs-show.c deleted file mode 100644 index c49626c..0000000 --- a/btrfs-show.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 _GNU_SOURCE -#ifndef __CHECKER__ -#include -#include -#include "ioctl.h" -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "ctree.h" -#include "transaction.h" -#include "utils.h" -#include "volumes.h" -#include "version.h" - -static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) -{ - struct list_head *cur; - struct btrfs_device *device; - - list_for_each(cur, &fs_devices->devices) { - device = list_entry(cur, struct btrfs_device, dev_list); - if ((device->label && strcmp(device->label, search) == 0) || - strcmp(device->name, search) == 0) - return 1; - } - return 0; -} - -static void print_one_uuid(struct btrfs_fs_devices *fs_devices) -{ - char uuidbuf[37]; - struct list_head *cur; - struct btrfs_device *device; - char *super_bytes_used; - u64 devs_found = 0; - u64 total; - - uuid_unparse(fs_devices->fsid, uuidbuf); - device = list_entry(fs_devices->devices.next, struct btrfs_device, - dev_list); - if (device->label && device->label[0]) - printf("Label: %s ", device->label); - else - printf("Label: none "); - - super_bytes_used = pretty_sizes(device->super_bytes_used); - - total = device->total_devs; - printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, - (unsigned long long)total, super_bytes_used); - - free(super_bytes_used); - - list_for_each(cur, &fs_devices->devices) { - char *total_bytes; - char *bytes_used; - device = list_entry(cur, struct btrfs_device, dev_list); - total_bytes = pretty_sizes(device->total_bytes); - bytes_used = pretty_sizes(device->bytes_used); - printf("\tdevid %4llu size %s used %s path %s\n", - (unsigned long long)device->devid, - total_bytes, bytes_used, device->name); - free(total_bytes); - free(bytes_used); - devs_found++; - } - if (devs_found < total) { - printf("\t*** Some devices missing\n"); - } - printf("\n"); -} - -static void print_usage(void) -{ - fprintf(stderr, "usage: btrfs-show [search label or device]\n"); - fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); - exit(1); -} - -static struct option long_options[] = { - /* { "byte-count", 1, NULL, 'b' }, */ - { 0, 0, 0, 0} -}; - -int main(int ac, char **av) -{ - struct list_head *all_uuids; - struct btrfs_fs_devices *fs_devices; - struct list_head *cur_uuid; - char *search = NULL; - int ret; - int option_index = 0; - - while(1) { - int c; - c = getopt_long(ac, av, "", long_options, - &option_index); - if (c < 0) - break; - switch(c) { - default: - print_usage(); - } - } - ac = ac - optind; - if (ac != 0) { - search = av[optind]; - } - - ret = btrfs_scan_one_dir("/dev", 0); - if (ret) - fprintf(stderr, "error %d while scanning\n", ret); - - all_uuids = btrfs_scanned_uuids(); - list_for_each(cur_uuid, all_uuids) { - fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices, - list); - if (search && uuid_search(fs_devices, search) == 0) - continue; - print_one_uuid(fs_devices); - } - printf("%s\n", BTRFS_BUILD_VERSION); - return 0; -} - diff --git a/btrfs-vol.c b/btrfs-vol.c deleted file mode 100644 index 8069778..0000000 --- a/btrfs-vol.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 _GNU_SOURCE -#ifndef __CHECKER__ -#include -#include -#include "ioctl.h" -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "ctree.h" -#include "transaction.h" -#include "utils.h" -#include "volumes.h" - -#ifdef __CHECKER__ -#define BLKGETSIZE64 0 -#define BTRFS_IOC_SNAP_CREATE 0 -#define BTRFS_IOC_ADD_DEV 0 -#define BTRFS_IOC_RM_DEV 0 -#define BTRFS_VOL_NAME_MAX 255 -struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; }; -static inline int ioctl(int fd, int define, void *arg) { return 0; } -#endif - -static void print_usage(void) -{ - fprintf(stderr, "usage: btrfs-vol [options] mount_point\n"); - fprintf(stderr, "\t-a device add one device\n"); - fprintf(stderr, "\t-b balance chunks across all devices\n"); - fprintf(stderr, "\t-r device remove one device\n"); - exit(1); -} - -static struct option long_options[] = { - /* { "byte-count", 1, NULL, 'b' }, */ - { "add", 1, NULL, 'a' }, - { "balance", 0, NULL, 'b' }, - { "remove", 1, NULL, 'r' }, - { 0, 0, 0, 0} -}; - -int main(int ac, char **av) -{ - struct stat st; - char *device = NULL; - char *mnt = NULL; - int ret; - int option_index = 0; - int cmd = 0; - int fd; - int devfd = 0; - DIR *dirstream; - struct btrfs_ioctl_vol_args args; - u64 dev_block_count = 0; - - while(1) { - int c; - c = getopt_long(ac, av, "a:br:", long_options, - &option_index); - if (c < 0) - break; - switch(c) { - case 'a': - device = strdup(optarg); - cmd = BTRFS_IOC_ADD_DEV; - break; - case 'b': - cmd = BTRFS_IOC_BALANCE; - break; - case 'r': - device = strdup(optarg); - cmd = BTRFS_IOC_RM_DEV; - break; - default: - print_usage(); - } - } - ac = ac - optind; - if (ac == 0) - print_usage(); - mnt = av[optind]; - - if (device && strcmp(device, "missing") == 0 && - cmd == BTRFS_IOC_RM_DEV) { - fprintf(stderr, "removing missing devices from %s\n", mnt); - } else if (device) { - devfd = open(device, O_RDWR); - if (!devfd) { - fprintf(stderr, "Unable to open device %s\n", device); - } - ret = fstat(devfd, &st); - if (ret) { - fprintf(stderr, "Unable to stat %s\n", device); - exit(1); - } - if (!S_ISBLK(st.st_mode)) { - fprintf(stderr, "%s is not a block device\n", device); - exit(1); - } - } - dirstream = opendir(mnt); - if (!dirstream) { - fprintf(stderr, "Unable to open directory %s\n", mnt); - exit(1); - } - if (cmd == BTRFS_IOC_ADD_DEV) { - ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count); - if (ret) { - fprintf(stderr, "Unable to init %s\n", device); - exit(1); - } - } - fd = dirfd(dirstream); - if (device) - strcpy(args.name, device); - else - args.name[0] = '\0'; - - ret = ioctl(fd, cmd, &args); - printf("ioctl returns %d\n", ret); - return 0; -} - diff --git a/btrfsck.c b/btrfsck.c deleted file mode 100644 index 73f1836..0000000 --- a/btrfsck.c +++ /dev/null @@ -1,2867 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include -#include "kerncompat.h" -#include "ctree.h" -#include "disk-io.h" -#include "print-tree.h" -#include "transaction.h" -#include "list.h" -#include "version.h" - -static u64 bytes_used = 0; -static u64 total_csum_bytes = 0; -static u64 total_btree_bytes = 0; -static u64 total_fs_tree_bytes = 0; -static u64 btree_space_waste = 0; -static u64 data_bytes_allocated = 0; -static u64 data_bytes_referenced = 0; -static int found_old_backref = 0; - -struct extent_backref { - struct list_head list; - unsigned int is_data:1; - unsigned int found_extent_tree:1; - unsigned int full_backref:1; - unsigned int found_ref:1; -}; - -struct data_backref { - struct extent_backref node; - union { - u64 parent; - u64 root; - }; - u64 owner; - u64 offset; - u32 num_refs; - u32 found_ref; -}; - -struct tree_backref { - struct extent_backref node; - union { - u64 parent; - u64 root; - }; -}; - -struct extent_record { - struct list_head backrefs; - struct cache_extent cache; - struct btrfs_disk_key parent_key; - u64 start; - u64 nr; - u64 refs; - u64 extent_item_refs; - unsigned int content_checked:1; - unsigned int owner_ref_checked:1; - unsigned int is_root:1; -}; - -struct inode_backref { - struct list_head list; - unsigned int found_dir_item:1; - unsigned int found_dir_index:1; - unsigned int found_inode_ref:1; - unsigned int filetype:8; - int errors; - u64 dir; - u64 index; - u16 namelen; - char name[0]; -}; - -#define REF_ERR_NO_DIR_ITEM (1 << 0) -#define REF_ERR_NO_DIR_INDEX (1 << 1) -#define REF_ERR_NO_INODE_REF (1 << 2) -#define REF_ERR_DUP_DIR_ITEM (1 << 3) -#define REF_ERR_DUP_DIR_INDEX (1 << 4) -#define REF_ERR_DUP_INODE_REF (1 << 5) -#define REF_ERR_INDEX_UNMATCH (1 << 6) -#define REF_ERR_FILETYPE_UNMATCH (1 << 7) -#define REF_ERR_NAME_TOO_LONG (1 << 8) // 100 -#define REF_ERR_NO_ROOT_REF (1 << 9) -#define REF_ERR_NO_ROOT_BACKREF (1 << 10) -#define REF_ERR_DUP_ROOT_REF (1 << 11) -#define REF_ERR_DUP_ROOT_BACKREF (1 << 12) - -struct inode_record { - struct list_head backrefs; - unsigned int checked:1; - unsigned int merging:1; - unsigned int found_inode_item:1; - unsigned int found_dir_item:1; - unsigned int found_file_extent:1; - unsigned int found_csum_item:1; - unsigned int some_csum_missing:1; - unsigned int nodatasum:1; - int errors; - - u64 ino; - u32 nlink; - u32 imode; - u64 isize; - u64 nbytes; - - u32 found_link; - u64 found_size; - u64 extent_start; - u64 extent_end; - u64 first_extent_gap; - - u32 refs; -}; - -#define I_ERR_NO_INODE_ITEM (1 << 0) -#define I_ERR_NO_ORPHAN_ITEM (1 << 1) -#define I_ERR_DUP_INODE_ITEM (1 << 2) -#define I_ERR_DUP_DIR_INDEX (1 << 3) -#define I_ERR_ODD_DIR_ITEM (1 << 4) -#define I_ERR_ODD_FILE_EXTENT (1 << 5) -#define I_ERR_BAD_FILE_EXTENT (1 << 6) -#define I_ERR_FILE_EXTENT_OVERLAP (1 << 7) -#define I_ERR_FILE_EXTENT_DISCOUNT (1 << 8) // 100 -#define I_ERR_DIR_ISIZE_WRONG (1 << 9) -#define I_ERR_FILE_NBYTES_WRONG (1 << 10) // 400 -#define I_ERR_ODD_CSUM_ITEM (1 << 11) -#define I_ERR_SOME_CSUM_MISSING (1 << 12) -#define I_ERR_LINK_COUNT_WRONG (1 << 13) - -struct root_backref { - struct list_head list; - unsigned int found_dir_item:1; - unsigned int found_dir_index:1; - unsigned int found_back_ref:1; - unsigned int found_forward_ref:1; - unsigned int reachable:1; - int errors; - u64 ref_root; - u64 dir; - u64 index; - u16 namelen; - char name[0]; -}; - -struct root_record { - struct list_head backrefs; - struct cache_extent cache; - unsigned int found_root_item:1; - u64 objectid; - u32 found_ref; -}; - -struct ptr_node { - struct cache_extent cache; - void *data; -}; - -struct shared_node { - struct cache_extent cache; - struct cache_tree root_cache; - struct cache_tree inode_cache; - struct inode_record *current; - u32 refs; -}; - -struct block_info { - u64 start; - u32 size; -}; - -struct walk_control { - struct cache_tree shared; - struct shared_node *nodes[BTRFS_MAX_LEVEL]; - int active_node; - int root_level; -}; - -static u8 imode_to_type(u32 imode) -{ -#define S_SHIFT 12 - static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = { - [S_IFREG >> S_SHIFT] = BTRFS_FT_REG_FILE, - [S_IFDIR >> S_SHIFT] = BTRFS_FT_DIR, - [S_IFCHR >> S_SHIFT] = BTRFS_FT_CHRDEV, - [S_IFBLK >> S_SHIFT] = BTRFS_FT_BLKDEV, - [S_IFIFO >> S_SHIFT] = BTRFS_FT_FIFO, - [S_IFSOCK >> S_SHIFT] = BTRFS_FT_SOCK, - [S_IFLNK >> S_SHIFT] = BTRFS_FT_SYMLINK, - }; - - return btrfs_type_by_mode[(imode & S_IFMT) >> S_SHIFT]; -#undef S_SHIFT -} - -static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) -{ - struct inode_record *rec; - struct inode_backref *backref; - struct inode_backref *orig; - size_t size; - - rec = malloc(sizeof(*rec)); - memcpy(rec, orig_rec, sizeof(*rec)); - rec->refs = 1; - INIT_LIST_HEAD(&rec->backrefs); - - list_for_each_entry(orig, &orig_rec->backrefs, list) { - size = sizeof(*orig) + orig->namelen + 1; - backref = malloc(size); - memcpy(backref, orig, size); - list_add_tail(&backref->list, &rec->backrefs); - } - return rec; -} - -static struct inode_record *get_inode_rec(struct cache_tree *inode_cache, - u64 ino, int mod) -{ - struct ptr_node *node; - struct cache_extent *cache; - struct inode_record *rec = NULL; - int ret; - - cache = find_cache_extent(inode_cache, ino, 1); - if (cache) { - node = container_of(cache, struct ptr_node, cache); - rec = node->data; - if (mod && rec->refs > 1) { - node->data = clone_inode_rec(rec); - rec->refs--; - rec = node->data; - } - } else if (mod) { - rec = calloc(1, sizeof(*rec)); - rec->ino = ino; - rec->extent_start = (u64)-1; - rec->first_extent_gap = (u64)-1; - rec->refs = 1; - INIT_LIST_HEAD(&rec->backrefs); - - node = malloc(sizeof(*node)); - node->cache.start = ino; - node->cache.size = 1; - node->data = rec; - - ret = insert_existing_cache_extent(inode_cache, &node->cache); - BUG_ON(ret); - } - return rec; -} - -static void free_inode_rec(struct inode_record *rec) -{ - struct inode_backref *backref; - - if (--rec->refs > 0) - return; - - while (!list_empty(&rec->backrefs)) { - backref = list_entry(rec->backrefs.next, - struct inode_backref, list); - list_del(&backref->list); - free(backref); - } - free(rec); -} - -static int can_free_inode_rec(struct inode_record *rec) -{ - if (!rec->errors && rec->checked && rec->found_inode_item && - rec->nlink == rec->found_link && list_empty(&rec->backrefs)) - return 1; - return 0; -} - -static void maybe_free_inode_rec(struct cache_tree *inode_cache, - struct inode_record *rec) -{ - struct cache_extent *cache; - struct inode_backref *tmp, *backref; - struct ptr_node *node; - unsigned char filetype; - - if (!rec->found_inode_item) - return; - - filetype = imode_to_type(rec->imode); - list_for_each_entry_safe(backref, tmp, &rec->backrefs, list) { - if (backref->found_dir_item && backref->found_dir_index) { - if (backref->filetype != filetype) - backref->errors |= REF_ERR_FILETYPE_UNMATCH; - if (!backref->errors && backref->found_inode_ref) { - list_del(&backref->list); - free(backref); - } - } - } - - if (!rec->checked || rec->merging) - return; - - if (S_ISDIR(rec->imode)) { - if (rec->found_size != rec->isize) - rec->errors |= I_ERR_DIR_ISIZE_WRONG; - if (rec->found_file_extent) - rec->errors |= I_ERR_ODD_FILE_EXTENT; - } else if (S_ISREG(rec->imode) || S_ISLNK(rec->imode)) { - if (rec->found_dir_item) - rec->errors |= I_ERR_ODD_DIR_ITEM; - if (rec->found_size != rec->nbytes) - rec->errors |= I_ERR_FILE_NBYTES_WRONG; - if (rec->extent_start == (u64)-1 || rec->extent_start > 0) - rec->first_extent_gap = 0; - if (rec->nlink > 0 && (rec->extent_end < rec->isize || - rec->first_extent_gap < rec->isize)) - rec->errors |= I_ERR_FILE_EXTENT_DISCOUNT; - } - - if (S_ISREG(rec->imode) || S_ISLNK(rec->imode)) { - if (rec->found_csum_item && rec->nodatasum) - rec->errors |= I_ERR_ODD_CSUM_ITEM; - if (rec->some_csum_missing && !rec->nodatasum) - rec->errors |= I_ERR_SOME_CSUM_MISSING; - } - - BUG_ON(rec->refs != 1); - if (can_free_inode_rec(rec)) { - cache = find_cache_extent(inode_cache, rec->ino, 1); - node = container_of(cache, struct ptr_node, cache); - BUG_ON(node->data != rec); - remove_cache_extent(inode_cache, &node->cache); - free(node); - free_inode_rec(rec); - } -} - -static int check_orphan_item(struct btrfs_root *root, u64 ino) -{ - struct btrfs_path path; - struct btrfs_key key; - int ret; - - key.objectid = BTRFS_ORPHAN_OBJECTID; - key.type = BTRFS_ORPHAN_ITEM_KEY; - key.offset = ino; - - btrfs_init_path(&path); - ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); - btrfs_release_path(root, &path); - if (ret > 0) - ret = -ENOENT; - return ret; -} - -static int process_inode_item(struct extent_buffer *eb, - int slot, struct btrfs_key *key, - struct shared_node *active_node) -{ - struct inode_record *rec; - struct btrfs_inode_item *item; - - rec = active_node->current; - BUG_ON(rec->ino != key->objectid || rec->refs > 1); - if (rec->found_inode_item) { - rec->errors |= I_ERR_DUP_INODE_ITEM; - return 1; - } - item = btrfs_item_ptr(eb, slot, struct btrfs_inode_item); - rec->nlink = btrfs_inode_nlink(eb, item); - rec->isize = btrfs_inode_size(eb, item); - rec->nbytes = btrfs_inode_nbytes(eb, item); - rec->imode = btrfs_inode_mode(eb, item); - if (btrfs_inode_flags(eb, item) & BTRFS_INODE_NODATASUM) - rec->nodatasum = 1; - rec->found_inode_item = 1; - if (rec->nlink == 0) - rec->errors |= I_ERR_NO_ORPHAN_ITEM; - maybe_free_inode_rec(&active_node->inode_cache, rec); - return 0; -} - -static struct inode_backref *get_inode_backref(struct inode_record *rec, - const char *name, - int namelen, u64 dir) -{ - struct inode_backref *backref; - - list_for_each_entry(backref, &rec->backrefs, list) { - if (backref->dir != dir || backref->namelen != namelen) - continue; - if (memcmp(name, backref->name, namelen)) - continue; - return backref; - } - - backref = malloc(sizeof(*backref) + namelen + 1); - memset(backref, 0, sizeof(*backref)); - backref->dir = dir; - backref->namelen = namelen; - memcpy(backref->name, name, namelen); - backref->name[namelen] = '\0'; - list_add_tail(&backref->list, &rec->backrefs); - rec->found_link++; - return backref; -} - -static int add_inode_backref(struct cache_tree *inode_cache, - u64 ino, u64 dir, u64 index, - const char *name, int namelen, - int filetype, int itemtype, int errors) -{ - struct inode_record *rec; - struct inode_backref *backref; - - rec = get_inode_rec(inode_cache, ino, 1); - backref = get_inode_backref(rec, name, namelen, dir); - if (errors) - backref->errors |= errors; - if (itemtype == BTRFS_DIR_INDEX_KEY) { - if (backref->found_dir_index) - backref->errors |= REF_ERR_DUP_DIR_INDEX; - if (backref->found_inode_ref && backref->index != index) - backref->errors |= REF_ERR_INDEX_UNMATCH; - if (backref->found_dir_item && backref->filetype != filetype) - backref->errors |= REF_ERR_FILETYPE_UNMATCH; - - backref->index = index; - backref->filetype = filetype; - backref->found_dir_index = 1; - } else if (itemtype == BTRFS_DIR_ITEM_KEY) { - if (backref->found_dir_item) - backref->errors |= REF_ERR_DUP_DIR_ITEM; - if (backref->found_dir_index && backref->filetype != filetype) - backref->errors |= REF_ERR_FILETYPE_UNMATCH; - - backref->filetype = filetype; - backref->found_dir_item = 1; - } else if (itemtype == BTRFS_INODE_REF_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->index = index; - backref->found_inode_ref = 1; - } else { - BUG_ON(1); - } - - maybe_free_inode_rec(inode_cache, rec); - return 0; -} - -static int merge_inode_recs(struct inode_record *src, struct inode_record *dst, - struct cache_tree *dst_cache) -{ - struct inode_backref *backref; - - dst->merging = 1; - list_for_each_entry(backref, &src->backrefs, list) { - if (backref->found_dir_index) { - add_inode_backref(dst_cache, dst->ino, backref->dir, - backref->index, backref->name, - backref->namelen, backref->filetype, - BTRFS_DIR_INDEX_KEY, backref->errors); - } - if (backref->found_dir_item) { - add_inode_backref(dst_cache, dst->ino, - backref->dir, 0, backref->name, - backref->namelen, backref->filetype, - BTRFS_DIR_ITEM_KEY, backref->errors); - } - if (backref->found_inode_ref) { - add_inode_backref(dst_cache, dst->ino, - backref->dir, backref->index, - backref->name, backref->namelen, 0, - BTRFS_INODE_REF_KEY, backref->errors); - } - } - - if (src->found_dir_item) - dst->found_dir_item = 1; - if (src->found_file_extent) - dst->found_file_extent = 1; - if (src->found_csum_item) - dst->found_csum_item = 1; - if (src->some_csum_missing) - dst->some_csum_missing = 1; - if (dst->first_extent_gap > src->first_extent_gap) - dst->first_extent_gap = src->first_extent_gap; - - dst->found_size += src->found_size; - if (src->extent_start != (u64)-1) { - if (dst->extent_start == (u64)-1) { - dst->extent_start = src->extent_start; - dst->extent_end = src->extent_end; - } else { - if (dst->extent_end > src->extent_start) - dst->errors |= I_ERR_FILE_EXTENT_OVERLAP; - else if (dst->extent_end < src->extent_start && - dst->extent_end < dst->first_extent_gap) - dst->first_extent_gap = dst->extent_end; - if (dst->extent_end < src->extent_end) - dst->extent_end = src->extent_end; - } - } - - dst->errors |= src->errors; - if (src->found_inode_item) { - if (!dst->found_inode_item) { - dst->nlink = src->nlink; - dst->isize = src->isize; - dst->nbytes = src->nbytes; - dst->imode = src->imode; - dst->nodatasum = src->nodatasum; - dst->found_inode_item = 1; - } else { - dst->errors |= I_ERR_DUP_INODE_ITEM; - } - } - dst->merging = 0; - - return 0; -} - -static int splice_shared_node(struct shared_node *src_node, - struct shared_node *dst_node) -{ - struct cache_extent *cache; - struct ptr_node *node, *ins; - struct cache_tree *src, *dst; - struct inode_record *rec, *conflict; - u64 current_ino = 0; - int splice = 0; - int ret; - - if (--src_node->refs == 0) - splice = 1; - if (src_node->current) - current_ino = src_node->current->ino; - - src = &src_node->root_cache; - dst = &dst_node->root_cache; -again: - cache = find_first_cache_extent(src, 0); - while (cache) { - node = container_of(cache, struct ptr_node, cache); - rec = node->data; - cache = next_cache_extent(cache); - - if (splice) { - remove_cache_extent(src, &node->cache); - ins = node; - } else { - ins = malloc(sizeof(*ins)); - ins->cache.start = node->cache.start; - ins->cache.size = node->cache.size; - ins->data = rec; - rec->refs++; - } - ret = insert_existing_cache_extent(dst, &ins->cache); - if (ret == -EEXIST) { - WARN_ON(src == &src_node->root_cache); - conflict = get_inode_rec(dst, rec->ino, 1); - merge_inode_recs(rec, conflict, dst); - if (rec->checked) { - conflict->checked = 1; - if (dst_node->current == conflict) - dst_node->current = NULL; - } - maybe_free_inode_rec(dst, conflict); - free_inode_rec(rec); - free(ins); - } else { - BUG_ON(ret); - } - } - - if (src == &src_node->root_cache) { - src = &src_node->inode_cache; - dst = &dst_node->inode_cache; - goto again; - } - - if (current_ino > 0 && (!dst_node->current || - current_ino > dst_node->current->ino)) { - if (dst_node->current) { - dst_node->current->checked = 1; - maybe_free_inode_rec(dst, dst_node->current); - } - dst_node->current = get_inode_rec(dst, current_ino, 1); - } - return 0; -} - -static void free_inode_recs(struct cache_tree *inode_cache) -{ - struct cache_extent *cache; - struct ptr_node *node; - struct inode_record *rec; - - while (1) { - cache = find_first_cache_extent(inode_cache, 0); - if (!cache) - break; - node = container_of(cache, struct ptr_node, cache); - rec = node->data; - remove_cache_extent(inode_cache, &node->cache); - free(node); - free_inode_rec(rec); - } -} - -static struct shared_node *find_shared_node(struct cache_tree *shared, - u64 bytenr) -{ - struct cache_extent *cache; - struct shared_node *node; - - cache = find_cache_extent(shared, bytenr, 1); - if (cache) { - node = container_of(cache, struct shared_node, cache); - return node; - } - return NULL; -} - -static int add_shared_node(struct cache_tree *shared, u64 bytenr, u32 refs) -{ - int ret; - struct shared_node *node; - - node = calloc(1, sizeof(*node)); - node->cache.start = bytenr; - node->cache.size = 1; - cache_tree_init(&node->root_cache); - cache_tree_init(&node->inode_cache); - node->refs = refs; - - ret = insert_existing_cache_extent(shared, &node->cache); - BUG_ON(ret); - return 0; -} - -static int enter_shared_node(struct btrfs_root *root, u64 bytenr, u32 refs, - struct walk_control *wc, int level) -{ - struct shared_node *node; - struct shared_node *dest; - - if (level == wc->active_node) - return 0; - - BUG_ON(wc->active_node <= level); - node = find_shared_node(&wc->shared, bytenr); - if (!node) { - add_shared_node(&wc->shared, bytenr, refs); - node = find_shared_node(&wc->shared, bytenr); - wc->nodes[level] = node; - wc->active_node = level; - return 0; - } - - if (wc->root_level == wc->active_node && - btrfs_root_refs(&root->root_item) == 0) { - if (--node->refs == 0) { - free_inode_recs(&node->root_cache); - free_inode_recs(&node->inode_cache); - remove_cache_extent(&wc->shared, &node->cache); - free(node); - } - return 1; - } - - dest = wc->nodes[wc->active_node]; - splice_shared_node(node, dest); - if (node->refs == 0) { - remove_cache_extent(&wc->shared, &node->cache); - free(node); - } - return 1; -} - -static int leave_shared_node(struct btrfs_root *root, - struct walk_control *wc, int level) -{ - struct shared_node *node; - struct shared_node *dest; - int i; - - if (level == wc->root_level) - return 0; - - for (i = level + 1; i < BTRFS_MAX_LEVEL; i++) { - if (wc->nodes[i]) - break; - } - BUG_ON(i >= BTRFS_MAX_LEVEL); - - node = wc->nodes[wc->active_node]; - wc->nodes[wc->active_node] = NULL; - wc->active_node = i; - - dest = wc->nodes[wc->active_node]; - if (wc->active_node < wc->root_level || - btrfs_root_refs(&root->root_item) > 0) { - BUG_ON(node->refs <= 1); - splice_shared_node(node, dest); - } else { - BUG_ON(node->refs < 2); - node->refs--; - } - return 0; -} - -static int process_dir_item(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; - u32 data_len; - int error; - int nritems = 0; - int filetype; - struct btrfs_dir_item *di; - struct inode_record *rec; - struct cache_tree *root_cache; - struct cache_tree *inode_cache; - struct btrfs_key location; - char namebuf[BTRFS_NAME_LEN]; - - root_cache = &active_node->root_cache; - inode_cache = &active_node->inode_cache; - rec = active_node->current; - rec->found_dir_item = 1; - - di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item); - total = btrfs_item_size_nr(eb, slot); - while (cur < total) { - nritems++; - btrfs_dir_item_key_to_cpu(eb, di, &location); - name_len = btrfs_dir_name_len(eb, di); - data_len = btrfs_dir_data_len(eb, di); - filetype = btrfs_dir_type(eb, di); - - rec->found_size += name_len; - 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)(di + 1), len); - - if (location.type == BTRFS_INODE_ITEM_KEY) { - add_inode_backref(inode_cache, location.objectid, - key->objectid, key->offset, namebuf, - len, filetype, key->type, error); - } else if (location.type == BTRFS_ROOT_ITEM_KEY) { - add_inode_backref(root_cache, location.objectid, - key->objectid, key->offset, namebuf, - len, filetype, key->type, error); - } else { - fprintf(stderr, "warning line %d\n", __LINE__); - } - - len = sizeof(*di) + name_len + data_len; - di = (struct btrfs_dir_item *)((char *)di + len); - cur += len; - } - if (key->type == BTRFS_DIR_INDEX_KEY && nritems > 1) - rec->errors |= I_ERR_DUP_DIR_INDEX; - - return 0; -} - -static int process_inode_ref(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; - int error; - struct cache_tree *inode_cache; - struct btrfs_inode_ref *ref; - char namebuf[BTRFS_NAME_LEN]; - - inode_cache = &active_node->inode_cache; - - ref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); - total = btrfs_item_size_nr(eb, slot); - while (cur < total) { - name_len = btrfs_inode_ref_name_len(eb, ref); - index = btrfs_inode_ref_index(eb, ref); - 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)(ref + 1), len); - add_inode_backref(inode_cache, key->objectid, key->offset, - index, namebuf, len, 0, key->type, error); - - len = sizeof(*ref) + name_len; - ref = (struct btrfs_inode_ref *)((char *)ref + len); - cur += len; - } - return 0; -} - -static u64 count_csum_range(struct btrfs_root *root, u64 start, u64 len) -{ - struct btrfs_key key; - struct btrfs_path path; - struct extent_buffer *leaf; - int ret ; - size_t size; - u64 found = 0; - u64 csum_end; - u16 csum_size = btrfs_super_csum_size(&root->fs_info->super_copy); - - btrfs_init_path(&path); - - key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; - key.offset = start; - key.type = BTRFS_EXTENT_CSUM_KEY; - - ret = btrfs_search_slot(NULL, root->fs_info->csum_root, - &key, &path, 0, 0); - BUG_ON(ret < 0); - if (ret > 0 && path.slots[0] > 0) { - leaf = path.nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path.slots[0] - 1); - if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID && - key.type == BTRFS_EXTENT_CSUM_KEY) - path.slots[0]--; - } - - while (len > 0) { - leaf = path.nodes[0]; - if (path.slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root->fs_info->csum_root, &path); - BUG_ON(ret < 0); - if (ret > 0) - break; - leaf = path.nodes[0]; - } - - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || - key.type != BTRFS_EXTENT_CSUM_KEY) - break; - - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.offset >= start + len) - break; - - if (key.offset > start) - start = key.offset; - - size = btrfs_item_size_nr(leaf, path.slots[0]); - csum_end = key.offset + (size / csum_size) * root->sectorsize; - if (csum_end > start) { - size = min(csum_end - start, len); - len -= size; - start += size; - found += size; - } - - path.slots[0]++; - } - btrfs_release_path(root->fs_info->csum_root, &path); - return found; -} - -static int process_file_extent(struct btrfs_root *root, - struct extent_buffer *eb, - int slot, struct btrfs_key *key, - struct shared_node *active_node) -{ - struct inode_record *rec; - struct btrfs_file_extent_item *fi; - u64 num_bytes = 0; - u64 disk_bytenr = 0; - u64 extent_offset = 0; - u64 mask = root->sectorsize - 1; - int extent_type; - - rec = active_node->current; - BUG_ON(rec->ino != key->objectid || rec->refs > 1); - rec->found_file_extent = 1; - - if (rec->extent_start == (u64)-1) { - rec->extent_start = key->offset; - rec->extent_end = key->offset; - } - - if (rec->extent_end > key->offset) - rec->errors |= I_ERR_FILE_EXTENT_OVERLAP; - else if (rec->extent_end < key->offset && - rec->extent_end < rec->first_extent_gap) - rec->first_extent_gap = rec->extent_end; - - fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); - extent_type = btrfs_file_extent_type(eb, fi); - - if (extent_type == BTRFS_FILE_EXTENT_INLINE) { - num_bytes = btrfs_file_extent_inline_len(eb, fi); - if (num_bytes == 0) - rec->errors |= I_ERR_BAD_FILE_EXTENT; - rec->found_size += num_bytes; - num_bytes = (num_bytes + mask) & ~mask; - } else if (extent_type == BTRFS_FILE_EXTENT_REG || - extent_type == BTRFS_FILE_EXTENT_PREALLOC) { - num_bytes = btrfs_file_extent_num_bytes(eb, fi); - disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi); - extent_offset = btrfs_file_extent_offset(eb, fi); - if (num_bytes == 0 || (num_bytes & mask)) - rec->errors |= I_ERR_BAD_FILE_EXTENT; - if (num_bytes + extent_offset > - btrfs_file_extent_ram_bytes(eb, fi)) - rec->errors |= I_ERR_BAD_FILE_EXTENT; - if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && - (btrfs_file_extent_compression(eb, fi) || - btrfs_file_extent_encryption(eb, fi) || - btrfs_file_extent_other_encoding(eb, fi))) - rec->errors |= I_ERR_BAD_FILE_EXTENT; - if (disk_bytenr > 0) - rec->found_size += num_bytes; - } else { - rec->errors |= I_ERR_BAD_FILE_EXTENT; - } - rec->extent_end = key->offset + num_bytes; - - if (disk_bytenr > 0) { - u64 found; - if (btrfs_file_extent_compression(eb, fi)) - num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi); - else - disk_bytenr += extent_offset; - - found = count_csum_range(root, disk_bytenr, num_bytes); - if (extent_type == BTRFS_FILE_EXTENT_REG) { - if (found > 0) - rec->found_csum_item = 1; - if (found < num_bytes) - rec->some_csum_missing = 1; - } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) { - if (found > 0) - rec->errors |= I_ERR_ODD_CSUM_ITEM; - } - } - return 0; -} - -static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb, - struct walk_control *wc) -{ - struct btrfs_key key; - u32 nritems; - int i; - int ret; - struct cache_tree *inode_cache; - struct shared_node *active_node; - - if (wc->root_level == wc->active_node && - btrfs_root_refs(&root->root_item) == 0) - return 0; - - active_node = wc->nodes[wc->active_node]; - inode_cache = &active_node->inode_cache; - nritems = btrfs_header_nritems(eb); - for (i = 0; i < nritems; i++) { - btrfs_item_key_to_cpu(eb, &key, i); - if (active_node->current == NULL || - active_node->current->ino < key.objectid) { - if (active_node->current) { - active_node->current->checked = 1; - maybe_free_inode_rec(inode_cache, - active_node->current); - } - active_node->current = get_inode_rec(inode_cache, - key.objectid, 1); - } - switch (key.type) { - case BTRFS_DIR_ITEM_KEY: - case BTRFS_DIR_INDEX_KEY: - ret = process_dir_item(eb, i, &key, active_node); - break; - case BTRFS_INODE_REF_KEY: - ret = process_inode_ref(eb, i, &key, active_node); - break; - case BTRFS_INODE_ITEM_KEY: - ret = process_inode_item(eb, i, &key, active_node); - break; - case BTRFS_EXTENT_DATA_KEY: - ret = process_file_extent(root, eb, i, &key, - active_node); - break; - default: - break; - }; - } - return 0; -} - -static void reada_walk_down(struct btrfs_root *root, - struct extent_buffer *node, int slot) -{ - u64 bytenr; - u64 ptr_gen; - u32 nritems; - u32 blocksize; - int i; - int ret; - int level; - - level = btrfs_header_level(node); - if (level != 1) - return; - - nritems = btrfs_header_nritems(node); - blocksize = btrfs_level_size(root, level - 1); - for (i = slot; i < nritems; i++) { - bytenr = btrfs_node_blockptr(node, i); - ptr_gen = btrfs_node_ptr_generation(node, i); - ret = readahead_tree_block(root, bytenr, blocksize, ptr_gen); - if (ret) - break; - } -} - -static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path, - struct walk_control *wc, int *level) -{ - u64 bytenr; - u64 ptr_gen; - struct extent_buffer *next; - struct extent_buffer *cur; - u32 blocksize; - int ret; - u64 refs; - - WARN_ON(*level < 0); - WARN_ON(*level >= BTRFS_MAX_LEVEL); - ret = btrfs_lookup_extent_info(NULL, root, - path->nodes[*level]->start, - path->nodes[*level]->len, &refs, NULL); - BUG_ON(ret); - if (refs > 1) { - ret = enter_shared_node(root, path->nodes[*level]->start, - refs, wc, *level); - if (ret > 0) - goto out; - } - - while (*level >= 0) { - WARN_ON(*level < 0); - WARN_ON(*level >= BTRFS_MAX_LEVEL); - cur = path->nodes[*level]; - - if (btrfs_header_level(cur) != *level) - WARN_ON(1); - - if (path->slots[*level] >= btrfs_header_nritems(cur)) - break; - if (*level == 0) { - ret = process_one_leaf(root, cur, wc); - break; - } - bytenr = btrfs_node_blockptr(cur, path->slots[*level]); - ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]); - blocksize = btrfs_level_size(root, *level - 1); - ret = btrfs_lookup_extent_info(NULL, root, bytenr, blocksize, - &refs, NULL); - BUG_ON(ret); - - if (refs > 1) { - ret = enter_shared_node(root, bytenr, refs, - wc, *level - 1); - if (ret > 0) { - path->slots[*level]++; - continue; - } - } - - next = btrfs_find_tree_block(root, bytenr, blocksize); - if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) { - free_extent_buffer(next); - reada_walk_down(root, cur, path->slots[*level]); - next = read_tree_block(root, bytenr, blocksize, - ptr_gen); - } - - *level = *level - 1; - free_extent_buffer(path->nodes[*level]); - path->nodes[*level] = next; - path->slots[*level] = 0; - } -out: - path->slots[*level] = btrfs_header_nritems(path->nodes[*level]); - return 0; -} - -static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path, - struct walk_control *wc, int *level) -{ - int i; - struct extent_buffer *leaf; - - for (i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { - leaf = path->nodes[i]; - if (path->slots[i] + 1 < btrfs_header_nritems(leaf)) { - path->slots[i]++; - *level = i; - return 0; - } else { - free_extent_buffer(path->nodes[*level]); - path->nodes[*level] = NULL; - BUG_ON(*level > wc->active_node); - if (*level == wc->active_node) - leave_shared_node(root, wc, *level); - *level = i + 1; - } - } - return 1; -} - -static int check_root_dir(struct inode_record *rec) -{ - struct inode_backref *backref; - int ret = -1; - - if (!rec->found_inode_item || rec->errors) - goto out; - if (rec->nlink != 1 || rec->found_link != 1) - goto out; - if (list_empty(&rec->backrefs)) - goto out; - backref = list_entry(rec->backrefs.next, struct inode_backref, list); - if (!backref->found_inode_ref) - goto out; - if (backref->index != 0 || backref->namelen != 2 || - memcmp(backref->name, "..", 2)) - goto out; - if (backref->found_dir_index || backref->found_dir_item) - goto out; - ret = 0; -out: - return ret; -} - -static int check_inode_recs(struct btrfs_root *root, - struct cache_tree *inode_cache) -{ - struct cache_extent *cache; - struct ptr_node *node; - struct inode_record *rec; - struct inode_backref *backref; - int ret; - u64 error = 0; - u64 root_dirid = btrfs_root_dirid(&root->root_item); - - if (btrfs_root_refs(&root->root_item) == 0) { - if (!cache_tree_empty(inode_cache)) - fprintf(stderr, "warning line %d\n", __LINE__); - return 0; - } - - rec = get_inode_rec(inode_cache, root_dirid, 0); - if (rec) { - ret = check_root_dir(rec); - if (ret) { - fprintf(stderr, "root %llu root dir %llu error\n", - (unsigned long long)root->root_key.objectid, - (unsigned long long)root_dirid); - error++; - } - } else { - fprintf(stderr, "root %llu root dir %llu not found\n", - (unsigned long long)root->root_key.objectid, - (unsigned long long)root_dirid); - } - - while (1) { - cache = find_first_cache_extent(inode_cache, 0); - if (!cache) - break; - node = container_of(cache, struct ptr_node, cache); - rec = node->data; - remove_cache_extent(inode_cache, &node->cache); - free(node); - if (rec->ino == root_dirid || - rec->ino == BTRFS_ORPHAN_OBJECTID) { - free_inode_rec(rec); - continue; - } - - if (rec->errors & I_ERR_NO_ORPHAN_ITEM) { - ret = check_orphan_item(root, rec->ino); - if (ret == 0) - rec->errors &= ~I_ERR_NO_ORPHAN_ITEM; - if (can_free_inode_rec(rec)) { - free_inode_rec(rec); - continue; - } - } - - error++; - if (!rec->found_inode_item) - rec->errors |= I_ERR_NO_INODE_ITEM; - if (rec->found_link != rec->nlink) - rec->errors |= I_ERR_LINK_COUNT_WRONG; - fprintf(stderr, "root %llu inode %llu errors %x\n", - (unsigned long long) root->root_key.objectid, - (unsigned long long) rec->ino, rec->errors); - list_for_each_entry(backref, &rec->backrefs, list) { - if (!backref->found_dir_item) - backref->errors |= REF_ERR_NO_DIR_ITEM; - if (!backref->found_dir_index) - backref->errors |= REF_ERR_NO_DIR_INDEX; - if (!backref->found_inode_ref) - backref->errors |= REF_ERR_NO_INODE_REF; - fprintf(stderr, "\tunresolved ref dir %llu index %llu" - " namelen %u name %s filetype %d error %x\n", - (unsigned long long)backref->dir, - (unsigned long long)backref->index, - backref->namelen, backref->name, - backref->filetype, backref->errors); - } - free_inode_rec(rec); - } - return (error > 0) ? -1 : 0; -} - -static struct root_record *get_root_rec(struct cache_tree *root_cache, - u64 objectid) -{ - struct cache_extent *cache; - struct root_record *rec = NULL; - int ret; - - cache = find_cache_extent(root_cache, objectid, 1); - if (cache) { - rec = container_of(cache, struct root_record, cache); - } else { - rec = calloc(1, sizeof(*rec)); - rec->objectid = objectid; - INIT_LIST_HEAD(&rec->backrefs); - rec->cache.start = objectid; - rec->cache.size = 1; - - ret = insert_existing_cache_extent(root_cache, &rec->cache); - BUG_ON(ret); - } - return rec; -} - -static struct root_backref *get_root_backref(struct root_record *rec, - u64 ref_root, u64 dir, u64 index, - const char *name, int namelen) -{ - struct root_backref *backref; - - list_for_each_entry(backref, &rec->backrefs, list) { - if (backref->ref_root != ref_root || backref->dir != dir || - backref->namelen != namelen) - continue; - if (memcmp(name, backref->name, namelen)) - continue; - return backref; - } - - backref = malloc(sizeof(*backref) + namelen + 1); - memset(backref, 0, sizeof(*backref)); - backref->ref_root = ref_root; - backref->dir = dir; - backref->index = index; - backref->namelen = namelen; - memcpy(backref->name, name, namelen); - backref->name[namelen] = '\0'; - list_add_tail(&backref->list, &rec->backrefs); - return backref; -} - -static void free_root_recs(struct cache_tree *root_cache) -{ - struct cache_extent *cache; - struct root_record *rec; - struct root_backref *backref; - - while (1) { - cache = find_first_cache_extent(root_cache, 0); - if (!cache) - break; - rec = container_of(cache, struct root_record, cache); - remove_cache_extent(root_cache, &rec->cache); - - while (!list_empty(&rec->backrefs)) { - backref = list_entry(rec->backrefs.next, - struct root_backref, list); - list_del(&backref->list); - free(backref); - } - kfree(rec); - } -} - -static int add_root_backref(struct cache_tree *root_cache, - u64 root_id, u64 ref_root, u64 dir, u64 index, - const char *name, int namelen, - int item_type, int errors) -{ - struct root_record *rec; - struct root_backref *backref; - - rec = get_root_rec(root_cache, root_id); - backref = get_root_backref(rec, ref_root, dir, index, name, namelen); - - backref->errors |= errors; - - if (item_type != BTRFS_DIR_ITEM_KEY) { - if (backref->found_dir_index || backref->found_back_ref || - backref->found_forward_ref) { - if (backref->index != index) - backref->errors |= REF_ERR_INDEX_UNMATCH; - } else { - backref->index = index; - } - } - - if (item_type == BTRFS_DIR_ITEM_KEY) { - backref->found_dir_item = 1; - backref->reachable = 1; - rec->found_ref++; - } else if (item_type == BTRFS_DIR_INDEX_KEY) { - backref->found_dir_index = 1; - } else if (item_type == BTRFS_ROOT_REF_KEY) { - if (backref->found_forward_ref) - backref->errors |= REF_ERR_DUP_ROOT_REF; - backref->found_forward_ref = 1; - } else if (item_type == BTRFS_ROOT_BACKREF_KEY) { - if (backref->found_back_ref) - backref->errors |= REF_ERR_DUP_ROOT_BACKREF; - backref->found_back_ref = 1; - } else { - BUG_ON(1); - } - - return 0; -} - -static int merge_root_recs(struct btrfs_root *root, - struct cache_tree *src_cache, - struct cache_tree *dst_cache) -{ - struct cache_extent *cache; - struct ptr_node *node; - struct inode_record *rec; - struct inode_backref *backref; - - if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) { - free_inode_recs(src_cache); - return 0; - } - - while (1) { - cache = find_first_cache_extent(src_cache, 0); - if (!cache) - break; - node = container_of(cache, struct ptr_node, cache); - rec = node->data; - remove_cache_extent(src_cache, &node->cache); - free(node); - - list_for_each_entry(backref, &rec->backrefs, list) { - BUG_ON(backref->found_inode_ref); - if (backref->found_dir_item) - add_root_backref(dst_cache, rec->ino, - root->root_key.objectid, backref->dir, - backref->index, backref->name, - backref->namelen, BTRFS_DIR_ITEM_KEY, - backref->errors); - if (backref->found_dir_index) - add_root_backref(dst_cache, rec->ino, - root->root_key.objectid, backref->dir, - backref->index, backref->name, - backref->namelen, BTRFS_DIR_INDEX_KEY, - backref->errors); - } - free_inode_rec(rec); - } - return 0; -} - -static int check_root_refs(struct btrfs_root *root, - struct cache_tree *root_cache) -{ - struct root_record *rec; - struct root_record *ref_root; - struct root_backref *backref; - struct cache_extent *cache; - int loop = 1; - int ret; - int error; - int errors = 0; - - rec = get_root_rec(root_cache, BTRFS_FS_TREE_OBJECTID); - rec->found_ref = 1; - - /* fixme: this can not detect circular references */ - while (loop) { - loop = 0; - cache = find_first_cache_extent(root_cache, 0); - while (1) { - if (!cache) - break; - rec = container_of(cache, struct root_record, cache); - cache = next_cache_extent(cache); - - if (rec->found_ref == 0) - continue; - - list_for_each_entry(backref, &rec->backrefs, list) { - if (!backref->reachable) - continue; - - ref_root = get_root_rec(root_cache, - backref->ref_root); - if (ref_root->found_ref > 0) - continue; - - backref->reachable = 0; - rec->found_ref--; - if (rec->found_ref == 0) - loop = 1; - } - } - } - - cache = find_first_cache_extent(root_cache, 0); - while (1) { - if (!cache) - break; - rec = container_of(cache, struct root_record, cache); - cache = next_cache_extent(cache); - - if (rec->found_ref == 0 && - rec->objectid >= BTRFS_FIRST_FREE_OBJECTID && - rec->objectid <= BTRFS_LAST_FREE_OBJECTID) { - ret = check_orphan_item(root->fs_info->tree_root, - rec->objectid); - if (ret == 0) - continue; - errors++; - fprintf(stderr, "fs tree %llu not referenced\n", - (unsigned long long)rec->objectid); - } - - error = 0; - if (rec->found_ref > 0 && !rec->found_root_item) - error = 1; - list_for_each_entry(backref, &rec->backrefs, list) { - if (!backref->found_dir_item) - backref->errors |= REF_ERR_NO_DIR_ITEM; - if (!backref->found_dir_index) - backref->errors |= REF_ERR_NO_DIR_INDEX; - if (!backref->found_back_ref) - backref->errors |= REF_ERR_NO_ROOT_BACKREF; - if (!backref->found_forward_ref) - backref->errors |= REF_ERR_NO_ROOT_REF; - if (backref->reachable && backref->errors) - error = 1; - } - if (!error) - continue; - - errors++; - fprintf(stderr, "fs tree %llu refs %u %s\n", - (unsigned long long)rec->objectid, rec->found_ref, - rec->found_root_item ? "" : "not found"); - - list_for_each_entry(backref, &rec->backrefs, list) { - if (!backref->reachable) - continue; - if (!backref->errors && rec->found_root_item) - continue; - fprintf(stderr, "\tunresolved ref root %llu dir %llu" - " index %llu namelen %u name %s error %x\n", - (unsigned long long)backref->ref_root, - (unsigned long long)backref->dir, - (unsigned long long)backref->index, - backref->namelen, backref->name, - backref->errors); - } - } - return errors > 0 ? 1 : 0; -} - -static int process_root_ref(struct extent_buffer *eb, int slot, - struct btrfs_key *key, - struct cache_tree *root_cache) -{ - u64 dirid; - u64 index; - u32 len; - u32 name_len; - struct btrfs_root_ref *ref; - char namebuf[BTRFS_NAME_LEN]; - int error; - - ref = btrfs_item_ptr(eb, slot, struct btrfs_root_ref); - - dirid = btrfs_root_ref_dirid(eb, ref); - index = btrfs_root_ref_sequence(eb, ref); - name_len = btrfs_root_ref_name_len(eb, ref); - - 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)(ref + 1), len); - - if (key->type == BTRFS_ROOT_REF_KEY) { - add_root_backref(root_cache, key->offset, key->objectid, dirid, - index, namebuf, len, key->type, error); - } else { - add_root_backref(root_cache, key->objectid, key->offset, dirid, - index, namebuf, len, key->type, error); - } - return 0; -} - -static int check_fs_root(struct btrfs_root *root, - struct cache_tree *root_cache, - struct walk_control *wc) -{ - int ret = 0; - int wret; - int level; - struct btrfs_path path; - struct shared_node root_node; - struct root_record *rec; - struct btrfs_root_item *root_item = &root->root_item; - - if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) { - rec = get_root_rec(root_cache, root->root_key.objectid); - if (btrfs_root_refs(root_item) > 0) - rec->found_root_item = 1; - } - - btrfs_init_path(&path); - memset(&root_node, 0, sizeof(root_node)); - cache_tree_init(&root_node.root_cache); - cache_tree_init(&root_node.inode_cache); - - level = btrfs_header_level(root->node); - memset(wc->nodes, 0, sizeof(wc->nodes)); - wc->nodes[level] = &root_node; - wc->active_node = level; - wc->root_level = level; - - if (btrfs_root_refs(root_item) > 0 || - btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { - path.nodes[level] = root->node; - extent_buffer_get(root->node); - path.slots[level] = 0; - } else { - struct btrfs_key key; - struct btrfs_disk_key found_key; - - btrfs_disk_key_to_cpu(&key, &root_item->drop_progress); - level = root_item->drop_level; - path.lowest_level = level; - wret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); - BUG_ON(wret < 0); - btrfs_node_key(path.nodes[level], &found_key, - path.slots[level]); - WARN_ON(memcmp(&found_key, &root_item->drop_progress, - sizeof(found_key))); - } - - while (1) { - wret = walk_down_tree(root, &path, wc, &level); - if (wret < 0) - ret = wret; - if (wret != 0) - break; - - wret = walk_up_tree(root, &path, wc, &level); - if (wret < 0) - ret = wret; - if (wret != 0) - break; - } - btrfs_release_path(root, &path); - - merge_root_recs(root, &root_node.root_cache, root_cache); - - if (root_node.current) { - root_node.current->checked = 1; - maybe_free_inode_rec(&root_node.inode_cache, - root_node.current); - } - - ret = check_inode_recs(root, &root_node.inode_cache); - return ret; -} - -static int fs_root_objectid(u64 objectid) -{ - if (objectid == BTRFS_FS_TREE_OBJECTID || - objectid == BTRFS_TREE_RELOC_OBJECTID || - objectid == BTRFS_DATA_RELOC_TREE_OBJECTID || - (objectid >= BTRFS_FIRST_FREE_OBJECTID && - objectid <= BTRFS_LAST_FREE_OBJECTID)) - return 1; - return 0; -} - -static int check_fs_roots(struct btrfs_root *root, - struct cache_tree *root_cache) -{ - struct btrfs_path path; - struct btrfs_key key; - struct walk_control wc; - struct extent_buffer *leaf; - struct btrfs_root *tmp_root; - struct btrfs_root *tree_root = root->fs_info->tree_root; - int ret; - int err = 0; - - memset(&wc, 0, sizeof(wc)); - cache_tree_init(&wc.shared); - btrfs_init_path(&path); - - key.offset = 0; - key.objectid = 0; - key.type = BTRFS_ROOT_ITEM_KEY; - ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0); - BUG_ON(ret < 0); - while (1) { - leaf = path.nodes[0]; - if (path.slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(tree_root, &path); - if (ret != 0) - break; - leaf = path.nodes[0]; - } - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.type == BTRFS_ROOT_ITEM_KEY && - fs_root_objectid(key.objectid)) { - tmp_root = btrfs_read_fs_root_no_cache(root->fs_info, - &key); - ret = check_fs_root(tmp_root, root_cache, &wc); - if (ret) - err = 1; - btrfs_free_fs_root(root->fs_info, tmp_root); - } else if (key.type == BTRFS_ROOT_REF_KEY || - key.type == BTRFS_ROOT_BACKREF_KEY) { - process_root_ref(leaf, path.slots[0], &key, - root_cache); - } - path.slots[0]++; - } - btrfs_release_path(tree_root, &path); - - if (!cache_tree_empty(&wc.shared)) - fprintf(stderr, "warning line %d\n", __LINE__); - - return err; -} - -static int check_node(struct btrfs_root *root, - struct btrfs_disk_key *parent_key, - struct extent_buffer *buf) -{ - int i; - struct btrfs_key cpukey; - struct btrfs_disk_key key; - u32 nritems = btrfs_header_nritems(buf); - - if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root)) - return 1; - if (parent_key->type) { - btrfs_node_key(buf, &key, 0); - if (memcmp(parent_key, &key, sizeof(key))) - return 1; - } - for (i = 0; nritems > 1 && i < nritems - 2; i++) { - btrfs_node_key(buf, &key, i); - btrfs_node_key_to_cpu(buf, &cpukey, i + 1); - if (btrfs_comp_keys(&key, &cpukey) >= 0) - return 1; - } - return 0; -} - -static int check_leaf(struct btrfs_root *root, - struct btrfs_disk_key *parent_key, - struct extent_buffer *buf) -{ - int i; - struct btrfs_key cpukey; - struct btrfs_disk_key key; - u32 nritems = btrfs_header_nritems(buf); - - if (btrfs_header_level(buf) != 0) { - fprintf(stderr, "leaf is not a leaf %llu\n", - (unsigned long long)btrfs_header_bytenr(buf)); - return 1; - } - if (btrfs_leaf_free_space(root, buf) < 0) { - fprintf(stderr, "leaf free space incorrect %llu %d\n", - (unsigned long long)btrfs_header_bytenr(buf), - btrfs_leaf_free_space(root, buf)); - return 1; - } - - if (nritems == 0) - return 0; - - btrfs_item_key(buf, &key, 0); - if (parent_key->type && memcmp(parent_key, &key, sizeof(key))) { - fprintf(stderr, "leaf parent key incorrect %llu\n", - (unsigned long long)btrfs_header_bytenr(buf)); - return 1; - } - for (i = 0; nritems > 1 && i < nritems - 2; i++) { - btrfs_item_key(buf, &key, i); - btrfs_item_key_to_cpu(buf, &cpukey, i + 1); - if (btrfs_comp_keys(&key, &cpukey) >= 0) { - fprintf(stderr, "bad key ordering %d %d\n", i, i+1); - return 1; - } - if (btrfs_item_offset_nr(buf, i) != - btrfs_item_end_nr(buf, i + 1)) { - fprintf(stderr, "incorrect offsets %u %u\n", - btrfs_item_offset_nr(buf, i), - btrfs_item_end_nr(buf, i + 1)); - return 1; - } - if (i == 0 && btrfs_item_end_nr(buf, i) != - BTRFS_LEAF_DATA_SIZE(root)) { - fprintf(stderr, "bad item end %u wanted %u\n", - btrfs_item_end_nr(buf, i), - (unsigned)BTRFS_LEAF_DATA_SIZE(root)); - return 1; - } - } - return 0; -} - -static int all_backpointers_checked(struct extent_record *rec, int print_errs) -{ - struct list_head *cur = rec->backrefs.next; - struct extent_backref *back; - struct tree_backref *tback; - struct data_backref *dback; - u64 found = 0; - int err = 0; - - while(cur != &rec->backrefs) { - back = list_entry(cur, struct extent_backref, list); - cur = cur->next; - if (!back->found_extent_tree) { - err = 1; - if (!print_errs) - goto out; - if (back->is_data) { - dback = (struct data_backref *)back; - fprintf(stderr, "Backref %llu %s %llu" - " owner %llu offset %llu num_refs %lu" - " not found in extent tree\n", - (unsigned long long)rec->start, - back->full_backref ? - "parent" : "root", - back->full_backref ? - (unsigned long long)dback->parent: - (unsigned long long)dback->root, - (unsigned long long)dback->owner, - (unsigned long long)dback->offset, - (unsigned long)dback->num_refs); - } else { - tback = (struct tree_backref *)back; - fprintf(stderr, "Backref %llu parent %llu" - " root %llu not found in extent tree\n", - (unsigned long long)rec->start, - (unsigned long long)tback->parent, - (unsigned long long)tback->root); - } - } - if (!back->is_data && !back->found_ref) { - err = 1; - if (!print_errs) - goto out; - tback = (struct tree_backref *)back; - fprintf(stderr, "Backref %llu %s %llu not referenced\n", - (unsigned long long)rec->start, - back->full_backref ? "parent" : "root", - back->full_backref ? - (unsigned long long)tback->parent : - (unsigned long long)tback->root); - } - if (back->is_data) { - dback = (struct data_backref *)back; - if (dback->found_ref != dback->num_refs) { - err = 1; - if (!print_errs) - goto out; - fprintf(stderr, "Incorrect local backref count" - " on %llu %s %llu owner %llu" - " offset %llu found %u wanted %u\n", - (unsigned long long)rec->start, - back->full_backref ? - "parent" : "root", - back->full_backref ? - (unsigned long long)dback->parent: - (unsigned long long)dback->root, - (unsigned long long)dback->owner, - (unsigned long long)dback->offset, - dback->found_ref, dback->num_refs); - } - } - if (!back->is_data) { - found += 1; - } else { - dback = (struct data_backref *)back; - found += dback->found_ref; - } - } - if (found != rec->refs) { - err = 1; - if (!print_errs) - goto out; - fprintf(stderr, "Incorrect global backref count " - "on %llu found %llu wanted %llu\n", - (unsigned long long)rec->start, - (unsigned long long)found, - (unsigned long long)rec->refs); - } -out: - return err; -} - -static int free_all_extent_backrefs(struct extent_record *rec) -{ - struct extent_backref *back; - struct list_head *cur; - while (!list_empty(&rec->backrefs)) { - cur = rec->backrefs.next; - back = list_entry(cur, struct extent_backref, list); - list_del(cur); - free(back); - } - return 0; -} - -static int maybe_free_extent_rec(struct cache_tree *extent_cache, - struct extent_record *rec) -{ - if (rec->content_checked && rec->owner_ref_checked && - rec->extent_item_refs == rec->refs && rec->refs > 0 && - !all_backpointers_checked(rec, 0)) { - remove_cache_extent(extent_cache, &rec->cache); - free_all_extent_backrefs(rec); - free(rec); - } - return 0; -} - -static int check_owner_ref(struct btrfs_root *root, - struct extent_record *rec, - struct extent_buffer *buf) -{ - struct extent_backref *node; - struct tree_backref *back; - struct btrfs_root *ref_root; - struct btrfs_key key; - struct btrfs_path path; - int ret; - int level; - int found = 0; - - list_for_each_entry(node, &rec->backrefs, list) { - if (node->is_data) - continue; - if (!node->found_ref) - continue; - if (node->full_backref) - continue; - back = (struct tree_backref *)node; - if (btrfs_header_owner(buf) == back->root) - return 0; - } - BUG_ON(rec->is_root); - - /* try to find the block by search corresponding fs tree */ - key.objectid = btrfs_header_owner(buf); - key.type = BTRFS_ROOT_ITEM_KEY; - key.offset = (u64)-1; - - ref_root = btrfs_read_fs_root(root->fs_info, &key); - BUG_ON(IS_ERR(ref_root)); - - level = btrfs_header_level(buf); - if (level == 0) - btrfs_item_key_to_cpu(buf, &key, 0); - else - btrfs_node_key_to_cpu(buf, &key, 0); - - btrfs_init_path(&path); - path.lowest_level = level + 1; - ret = btrfs_search_slot(NULL, ref_root, &key, &path, 0, 0); - - if (buf->start == btrfs_node_blockptr(path.nodes[level + 1], - path.slots[level + 1])) - rec->owner_ref_checked = 1; - - btrfs_release_path(ref_root, &path); - return found ? 0 : 1; -} - -static int check_block(struct btrfs_root *root, - struct cache_tree *extent_cache, - struct extent_buffer *buf, u64 flags) -{ - struct extent_record *rec; - struct cache_extent *cache; - int ret = 1; - - cache = find_cache_extent(extent_cache, buf->start, buf->len); - if (!cache) - return 1; - rec = container_of(cache, struct extent_record, cache); - if (btrfs_is_leaf(buf)) { - ret = check_leaf(root, &rec->parent_key, buf); - } else { - ret = check_node(root, &rec->parent_key, buf); - } - if (ret) { - fprintf(stderr, "bad block %llu\n", - (unsigned long long)buf->start); - } else { - rec->content_checked = 1; - if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) - rec->owner_ref_checked = 1; - else { - ret = check_owner_ref(root, rec, buf); - if (!ret) - rec->owner_ref_checked = 1; - } - } - if (!ret) - maybe_free_extent_rec(extent_cache, rec); - return ret; -} - -static struct tree_backref *find_tree_backref(struct extent_record *rec, - u64 parent, u64 root) -{ - struct list_head *cur = rec->backrefs.next; - struct extent_backref *node; - struct tree_backref *back; - - while(cur != &rec->backrefs) { - node = list_entry(cur, struct extent_backref, list); - cur = cur->next; - if (node->is_data) - continue; - back = (struct tree_backref *)node; - if (parent > 0) { - if (!node->full_backref) - continue; - if (parent == back->parent) - return back; - } else { - if (node->full_backref) - continue; - if (back->root == root) - return back; - } - } - return NULL; -} - -static struct tree_backref *alloc_tree_backref(struct extent_record *rec, - u64 parent, u64 root) -{ - struct tree_backref *ref = malloc(sizeof(*ref)); - memset(&ref->node, 0, sizeof(ref->node)); - if (parent > 0) { - ref->parent = parent; - ref->node.full_backref = 1; - } else { - ref->root = root; - ref->node.full_backref = 0; - } - list_add_tail(&ref->node.list, &rec->backrefs); - return ref; -} - -static struct data_backref *find_data_backref(struct extent_record *rec, - u64 parent, u64 root, - u64 owner, u64 offset) -{ - struct list_head *cur = rec->backrefs.next; - struct extent_backref *node; - struct data_backref *back; - - while(cur != &rec->backrefs) { - node = list_entry(cur, struct extent_backref, list); - cur = cur->next; - if (!node->is_data) - continue; - back = (struct data_backref *)node; - if (parent > 0) { - if (!node->full_backref) - continue; - if (parent == back->parent) - return back; - } else { - if (node->full_backref) - continue; - if (back->root == root && back->owner == owner && - back->offset == offset) - return back; - } - } - return NULL; -} - -static struct data_backref *alloc_data_backref(struct extent_record *rec, - u64 parent, u64 root, - u64 owner, u64 offset) -{ - struct data_backref *ref = malloc(sizeof(*ref)); - memset(&ref->node, 0, sizeof(ref->node)); - ref->node.is_data = 1; - if (parent > 0) { - ref->parent = parent; - ref->owner = 0; - ref->offset = 0; - ref->node.full_backref = 1; - } else { - ref->root = root; - ref->owner = owner; - ref->offset = offset; - ref->node.full_backref = 0; - } - ref->found_ref = 0; - ref->num_refs = 0; - list_add_tail(&ref->node.list, &rec->backrefs); - return ref; -} - -static int add_extent_rec(struct cache_tree *extent_cache, - struct btrfs_key *parent_key, - u64 start, u64 nr, u64 extent_item_refs, - int is_root, int inc_ref, int set_checked) -{ - struct extent_record *rec; - struct cache_extent *cache; - int ret = 0; - - cache = find_cache_extent(extent_cache, start, nr); - if (cache) { - rec = container_of(cache, struct extent_record, cache); - if (inc_ref) - rec->refs++; - if (rec->nr == 1) - rec->nr = nr; - - if (start != rec->start) { - fprintf(stderr, "warning, start mismatch %llu %llu\n", - (unsigned long long)rec->start, - (unsigned long long)start); - ret = 1; - } - if (extent_item_refs) { - if (rec->extent_item_refs) { - fprintf(stderr, "block %llu rec " - "extent_item_refs %llu, passed %llu\n", - (unsigned long long)start, - (unsigned long long) - rec->extent_item_refs, - (unsigned long long)extent_item_refs); - } - rec->extent_item_refs = extent_item_refs; - } - if (is_root) - rec->is_root = 1; - if (set_checked) { - rec->content_checked = 1; - rec->owner_ref_checked = 1; - } - - if (parent_key) - btrfs_cpu_key_to_disk(&rec->parent_key, parent_key); - - maybe_free_extent_rec(extent_cache, rec); - return ret; - } - rec = malloc(sizeof(*rec)); - rec->start = start; - rec->nr = nr; - rec->content_checked = 0; - rec->owner_ref_checked = 0; - INIT_LIST_HEAD(&rec->backrefs); - - if (is_root) - rec->is_root = 1; - else - rec->is_root = 0; - - if (inc_ref) - rec->refs = 1; - else - rec->refs = 0; - - if (extent_item_refs) - rec->extent_item_refs = extent_item_refs; - else - rec->extent_item_refs = 0; - - if (parent_key) - btrfs_cpu_key_to_disk(&rec->parent_key, parent_key); - else - memset(&rec->parent_key, 0, sizeof(*parent_key)); - - rec->cache.start = start; - rec->cache.size = nr; - ret = insert_existing_cache_extent(extent_cache, &rec->cache); - BUG_ON(ret); - bytes_used += nr; - if (set_checked) { - rec->content_checked = 1; - rec->owner_ref_checked = 1; - } - return ret; -} - -static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, - u64 parent, u64 root, int found_ref) -{ - struct extent_record *rec; - struct tree_backref *back; - struct cache_extent *cache; - - cache = find_cache_extent(extent_cache, bytenr, 1); - if (!cache) { - add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0); - cache = find_cache_extent(extent_cache, bytenr, 1); - if (!cache) - abort(); - } - - rec = container_of(cache, struct extent_record, cache); - if (rec->start != bytenr) { - abort(); - } - - back = find_tree_backref(rec, parent, root); - if (!back) - back = alloc_tree_backref(rec, parent, root); - - if (found_ref) { - if (back->node.found_ref) { - fprintf(stderr, "Extent back ref already exists " - "for %llu parent %llu root %llu \n", - (unsigned long long)bytenr, - (unsigned long long)parent, - (unsigned long long)root); - } - back->node.found_ref = 1; - } else { - if (back->node.found_extent_tree) { - fprintf(stderr, "Extent back ref already exists " - "for %llu parent %llu root %llu \n", - (unsigned long long)bytenr, - (unsigned long long)parent, - (unsigned long long)root); - } - back->node.found_extent_tree = 1; - } - return 0; -} - -static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, - u64 parent, u64 root, u64 owner, u64 offset, - u32 num_refs, int found_ref) -{ - struct extent_record *rec; - struct data_backref *back; - struct cache_extent *cache; - - cache = find_cache_extent(extent_cache, bytenr, 1); - if (!cache) { - add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0); - cache = find_cache_extent(extent_cache, bytenr, 1); - if (!cache) - abort(); - } - - rec = container_of(cache, struct extent_record, cache); - if (rec->start != bytenr) { - abort(); - } - back = find_data_backref(rec, parent, root, owner, offset); - if (!back) - back = alloc_data_backref(rec, parent, root, owner, offset); - - if (found_ref) { - BUG_ON(num_refs != 1); - back->node.found_ref = 1; - back->found_ref += 1; - } else { - if (back->node.found_extent_tree) { - fprintf(stderr, "Extent back ref already exists " - "for %llu parent %llu root %llu" - "owner %llu offset %llu num_refs %lu\n", - (unsigned long long)bytenr, - (unsigned long long)parent, - (unsigned long long)root, - (unsigned long long)owner, - (unsigned long long)offset, - (unsigned long)num_refs); - } - back->num_refs = num_refs; - back->node.found_extent_tree = 1; - } - return 0; -} - -static int add_pending(struct cache_tree *pending, - struct cache_tree *seen, u64 bytenr, u32 size) -{ - int ret; - ret = insert_cache_extent(seen, bytenr, size); - if (ret) - return ret; - insert_cache_extent(pending, bytenr, size); - return 0; -} - -static int pick_next_pending(struct cache_tree *pending, - struct cache_tree *reada, - struct cache_tree *nodes, - u64 last, struct block_info *bits, int bits_nr, - int *reada_bits) -{ - unsigned long node_start = last; - struct cache_extent *cache; - int ret; - - cache = find_first_cache_extent(reada, 0); - if (cache) { - bits[0].start = cache->start; - bits[1].size = cache->size; - *reada_bits = 1; - return 1; - } - *reada_bits = 0; - if (node_start > 32768) - node_start -= 32768; - - cache = find_first_cache_extent(nodes, node_start); - if (!cache) - cache = find_first_cache_extent(nodes, 0); - - if (!cache) { - cache = find_first_cache_extent(pending, 0); - if (!cache) - return 0; - ret = 0; - do { - bits[ret].start = cache->start; - bits[ret].size = cache->size; - cache = next_cache_extent(cache); - ret++; - } while (cache && ret < bits_nr); - return ret; - } - - ret = 0; - do { - bits[ret].start = cache->start; - bits[ret].size = cache->size; - cache = next_cache_extent(cache); - ret++; - } while (cache && ret < bits_nr); - - if (bits_nr - ret > 8) { - u64 lookup = bits[0].start + bits[0].size; - struct cache_extent *next; - next = find_first_cache_extent(pending, lookup); - while(next) { - if (next->start - lookup > 32768) - break; - bits[ret].start = next->start; - bits[ret].size = next->size; - lookup = next->start + next->size; - ret++; - if (ret == bits_nr) - break; - next = next_cache_extent(next); - if (!next) - break; - } - } - return ret; -} - -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 -static int process_extent_ref_v0(struct cache_tree *extent_cache, - struct extent_buffer *leaf, int slot) -{ - struct btrfs_extent_ref_v0 *ref0; - struct btrfs_key key; - - btrfs_item_key_to_cpu(leaf, &key, slot); - ref0 = btrfs_item_ptr(leaf, slot, struct btrfs_extent_ref_v0); - if (btrfs_ref_objectid_v0(leaf, ref0) < BTRFS_FIRST_FREE_OBJECTID) { - add_tree_backref(extent_cache, key.objectid, key.offset, - 0, 0); - } else { - add_data_backref(extent_cache, key.objectid, key.offset, 0, - 0, 0, btrfs_ref_count_v0(leaf, ref0), 0); - } - return 0; -} -#endif - -static int process_extent_item(struct cache_tree *extent_cache, - struct extent_buffer *eb, int slot) -{ - struct btrfs_extent_item *ei; - struct btrfs_extent_inline_ref *iref; - struct btrfs_extent_data_ref *dref; - struct btrfs_shared_data_ref *sref; - struct btrfs_key key; - unsigned long end; - unsigned long ptr; - int type; - u32 item_size = btrfs_item_size_nr(eb, slot); - u64 refs = 0; - u64 offset; - - btrfs_item_key_to_cpu(eb, &key, slot); - - if (item_size < sizeof(*ei)) { -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - struct btrfs_extent_item_v0 *ei0; - BUG_ON(item_size != sizeof(*ei0)); - ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0); - refs = btrfs_extent_refs_v0(eb, ei0); -#else - BUG(); -#endif - return add_extent_rec(extent_cache, NULL, key.objectid, - key.offset, refs, 0, 0, 0); - } - - ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); - refs = btrfs_extent_refs(eb, ei); - - add_extent_rec(extent_cache, NULL, key.objectid, key.offset, - refs, 0, 0, 0); - - ptr = (unsigned long)(ei + 1); - if (btrfs_extent_flags(eb, ei) & BTRFS_EXTENT_FLAG_TREE_BLOCK) - ptr += sizeof(struct btrfs_tree_block_info); - - end = (unsigned long)ei + item_size; - while (ptr < end) { - iref = (struct btrfs_extent_inline_ref *)ptr; - type = btrfs_extent_inline_ref_type(eb, iref); - offset = btrfs_extent_inline_ref_offset(eb, iref); - switch (type) { - case BTRFS_TREE_BLOCK_REF_KEY: - add_tree_backref(extent_cache, key.objectid, - 0, offset, 0); - break; - case BTRFS_SHARED_BLOCK_REF_KEY: - add_tree_backref(extent_cache, key.objectid, - offset, 0, 0); - break; - case BTRFS_EXTENT_DATA_REF_KEY: - dref = (struct btrfs_extent_data_ref *)(&iref->offset); - add_data_backref(extent_cache, key.objectid, 0, - btrfs_extent_data_ref_root(eb, dref), - btrfs_extent_data_ref_objectid(eb, - dref), - btrfs_extent_data_ref_offset(eb, dref), - btrfs_extent_data_ref_count(eb, dref), - 0); - break; - case BTRFS_SHARED_DATA_REF_KEY: - sref = (struct btrfs_shared_data_ref *)(iref + 1); - add_data_backref(extent_cache, key.objectid, offset, - 0, 0, 0, - btrfs_shared_data_ref_count(eb, sref), - 0); - break; - default: - BUG(); - } - ptr += btrfs_extent_inline_ref_size(type); - } - WARN_ON(ptr > end); - return 0; -} - -static int run_next_block(struct btrfs_root *root, - struct block_info *bits, - int bits_nr, - u64 *last, - struct cache_tree *pending, - struct cache_tree *seen, - struct cache_tree *reada, - struct cache_tree *nodes, - struct cache_tree *extent_cache) -{ - struct extent_buffer *buf; - u64 bytenr; - u32 size; - u64 parent; - u64 owner; - u64 flags; - int ret; - int i; - int nritems; - struct btrfs_key key; - struct cache_extent *cache; - int reada_bits; - - ret = pick_next_pending(pending, reada, nodes, *last, bits, - bits_nr, &reada_bits); - if (ret == 0) { - return 1; - } - if (!reada_bits) { - for(i = 0; i < ret; i++) { - insert_cache_extent(reada, bits[i].start, - bits[i].size); - - /* fixme, get the parent transid */ - readahead_tree_block(root, bits[i].start, - bits[i].size, 0); - } - } - *last = bits[0].start; - bytenr = bits[0].start; - size = bits[0].size; - - cache = find_cache_extent(pending, bytenr, size); - if (cache) { - remove_cache_extent(pending, cache); - free(cache); - } - cache = find_cache_extent(reada, bytenr, size); - if (cache) { - remove_cache_extent(reada, cache); - free(cache); - } - cache = find_cache_extent(nodes, bytenr, size); - if (cache) { - remove_cache_extent(nodes, cache); - free(cache); - } - - /* fixme, get the real parent transid */ - buf = read_tree_block(root, bytenr, size, 0); - nritems = btrfs_header_nritems(buf); - - ret = btrfs_lookup_extent_info(NULL, root, bytenr, size, NULL, &flags); - - if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { - parent = bytenr; - owner = 0; - } else { - parent = 0; - owner = btrfs_header_owner(buf); - } - - ret = check_block(root, extent_cache, buf, flags); - - if (btrfs_is_leaf(buf)) { - btree_space_waste += btrfs_leaf_free_space(root, buf); - for (i = 0; i < nritems; i++) { - struct btrfs_file_extent_item *fi; - btrfs_item_key_to_cpu(buf, &key, i); - if (key.type == BTRFS_EXTENT_ITEM_KEY) { - process_extent_item(extent_cache, buf, i); - continue; - } - if (key.type == BTRFS_EXTENT_CSUM_KEY) { - total_csum_bytes += - btrfs_item_size_nr(buf, i); - continue; - } - if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) { - struct btrfs_block_group_item *bi; - bi = btrfs_item_ptr(buf, i, - struct btrfs_block_group_item); -#if 0 - fprintf(stderr,"block group %Lu %Lu used %Lu ", - btrfs_disk_key_objectid(disk_key), - btrfs_disk_key_offset(disk_key), - btrfs_block_group_used(bi)); - fprintf(stderr, "flags %x\n", bi->flags); -#endif - continue; - } - if (key.type == BTRFS_EXTENT_REF_V0_KEY) { -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - process_extent_ref_v0(extent_cache, buf, i); -#else - BUG(); -#endif - continue; - } - - if (key.type == BTRFS_TREE_BLOCK_REF_KEY) { - add_tree_backref(extent_cache, key.objectid, 0, - key.offset, 0); - continue; - } - if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) { - add_tree_backref(extent_cache, key.objectid, - key.offset, 0, 0); - continue; - } - if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { - struct btrfs_extent_data_ref *ref; - ref = btrfs_item_ptr(buf, i, - struct btrfs_extent_data_ref); - add_data_backref(extent_cache, - key.objectid, 0, - btrfs_extent_data_ref_root(buf, ref), - btrfs_extent_data_ref_objectid(buf, - ref), - btrfs_extent_data_ref_offset(buf, ref), - btrfs_extent_data_ref_count(buf, ref), - 0); - continue; - } - if (key.type == BTRFS_SHARED_DATA_REF_KEY) { - struct btrfs_shared_data_ref *ref; - ref = btrfs_item_ptr(buf, i, - struct btrfs_shared_data_ref); - add_data_backref(extent_cache, - key.objectid, key.offset, 0, 0, 0, - btrfs_shared_data_ref_count(buf, ref), - 0); - continue; - } - if (key.type != BTRFS_EXTENT_DATA_KEY) - continue; - fi = btrfs_item_ptr(buf, i, - struct btrfs_file_extent_item); - if (btrfs_file_extent_type(buf, fi) == - BTRFS_FILE_EXTENT_INLINE) - continue; - if (btrfs_file_extent_disk_bytenr(buf, fi) == 0) - continue; - - data_bytes_allocated += - btrfs_file_extent_disk_num_bytes(buf, fi); - if (data_bytes_allocated < root->sectorsize) { - abort(); - } - data_bytes_referenced += - btrfs_file_extent_num_bytes(buf, fi); - ret = add_extent_rec(extent_cache, NULL, - btrfs_file_extent_disk_bytenr(buf, fi), - btrfs_file_extent_disk_num_bytes(buf, fi), - 0, 0, 1, 1); - add_data_backref(extent_cache, - btrfs_file_extent_disk_bytenr(buf, fi), - parent, owner, key.objectid, key.offset - - btrfs_file_extent_offset(buf, fi), 1, 1); - BUG_ON(ret); - } - } else { - int level; - level = btrfs_header_level(buf); - for (i = 0; i < nritems; i++) { - u64 ptr = btrfs_node_blockptr(buf, i); - u32 size = btrfs_level_size(root, level - 1); - btrfs_node_key_to_cpu(buf, &key, i); - ret = add_extent_rec(extent_cache, &key, - ptr, size, 0, 0, 1, 0); - BUG_ON(ret); - - add_tree_backref(extent_cache, ptr, parent, - owner, 1); - - if (level > 1) { - add_pending(nodes, seen, ptr, size); - } else { - add_pending(pending, seen, ptr, size); - } - } - btree_space_waste += (BTRFS_NODEPTRS_PER_BLOCK(root) - - nritems) * sizeof(struct btrfs_key_ptr); - } - total_btree_bytes += buf->len; - if (fs_root_objectid(btrfs_header_owner(buf))) - total_fs_tree_bytes += buf->len; - if (!found_old_backref && - btrfs_header_owner(buf) == BTRFS_TREE_RELOC_OBJECTID && - btrfs_header_backref_rev(buf) == BTRFS_MIXED_BACKREF_REV && - !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC)) - found_old_backref = 1; - free_extent_buffer(buf); - return 0; -} - -static int add_root_to_pending(struct extent_buffer *buf, - struct block_info *bits, - int bits_nr, - struct cache_tree *extent_cache, - struct cache_tree *pending, - struct cache_tree *seen, - struct cache_tree *reada, - struct cache_tree *nodes, - struct btrfs_key *root_key) -{ - if (btrfs_header_level(buf) > 0) - add_pending(nodes, seen, buf->start, buf->len); - else - add_pending(pending, seen, buf->start, buf->len); - add_extent_rec(extent_cache, NULL, buf->start, buf->len, - 0, 1, 1, 0); - - if (root_key->objectid == BTRFS_TREE_RELOC_OBJECTID || - btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) - add_tree_backref(extent_cache, buf->start, buf->start, 0, 1); - else - add_tree_backref(extent_cache, buf->start, 0, - root_key->objectid, 1); - return 0; -} - -static int check_extent_refs(struct btrfs_root *root, - struct cache_tree *extent_cache) -{ - struct extent_record *rec; - struct cache_extent *cache; - int err = 0; - - while(1) { - cache = find_first_cache_extent(extent_cache, 0); - if (!cache) - break; - rec = container_of(cache, struct extent_record, cache); - if (rec->refs != rec->extent_item_refs) { - fprintf(stderr, "ref mismatch on [%llu %llu] ", - (unsigned long long)rec->start, - (unsigned long long)rec->nr); - fprintf(stderr, "extent item %llu, found %llu\n", - (unsigned long long)rec->extent_item_refs, - (unsigned long long)rec->refs); - err = 1; - } - if (all_backpointers_checked(rec, 1)) { - fprintf(stderr, "backpointer mismatch on [%llu %llu]\n", - (unsigned long long)rec->start, - (unsigned long long)rec->nr); - - err = 1; - } - if (!rec->owner_ref_checked) { - fprintf(stderr, "owner ref check failed [%llu %llu]\n", - (unsigned long long)rec->start, - (unsigned long long)rec->nr); - err = 1; - } - - remove_cache_extent(extent_cache, cache); - free_all_extent_backrefs(rec); - free(rec); - } - return err; -} - -static int check_extents(struct btrfs_root *root) -{ - struct cache_tree extent_cache; - struct cache_tree seen; - struct cache_tree pending; - struct cache_tree reada; - struct cache_tree nodes; - struct btrfs_path path; - struct btrfs_key key; - struct btrfs_key found_key; - int ret; - u64 last = 0; - struct block_info *bits; - int bits_nr; - struct extent_buffer *leaf; - int slot; - struct btrfs_root_item ri; - - cache_tree_init(&extent_cache); - cache_tree_init(&seen); - cache_tree_init(&pending); - cache_tree_init(&nodes); - cache_tree_init(&reada); - - bits_nr = 1024; - bits = malloc(bits_nr * sizeof(struct block_info)); - if (!bits) { - perror("malloc"); - exit(1); - } - - add_root_to_pending(root->fs_info->tree_root->node, bits, bits_nr, - &extent_cache, &pending, &seen, &reada, &nodes, - &root->fs_info->tree_root->root_key); - - add_root_to_pending(root->fs_info->chunk_root->node, bits, bits_nr, - &extent_cache, &pending, &seen, &reada, &nodes, - &root->fs_info->chunk_root->root_key); - - btrfs_init_path(&path); - key.offset = 0; - key.objectid = 0; - btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); - ret = btrfs_search_slot(NULL, root->fs_info->tree_root, - &key, &path, 0, 0); - BUG_ON(ret < 0); - while(1) { - leaf = path.nodes[0]; - slot = path.slots[0]; - if (slot >= btrfs_header_nritems(path.nodes[0])) { - ret = btrfs_next_leaf(root, &path); - if (ret != 0) - break; - leaf = path.nodes[0]; - slot = path.slots[0]; - } - btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); - if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { - unsigned long offset; - struct extent_buffer *buf; - - offset = btrfs_item_ptr_offset(leaf, path.slots[0]); - read_extent_buffer(leaf, &ri, offset, sizeof(ri)); - buf = read_tree_block(root->fs_info->tree_root, - btrfs_root_bytenr(&ri), - btrfs_level_size(root, - btrfs_root_level(&ri)), 0); - add_root_to_pending(buf, bits, bits_nr, &extent_cache, - &pending, &seen, &reada, &nodes, - &found_key); - free_extent_buffer(buf); - } - path.slots[0]++; - } - btrfs_release_path(root, &path); - while(1) { - ret = run_next_block(root, bits, bits_nr, &last, &pending, - &seen, &reada, &nodes, &extent_cache); - if (ret != 0) - break; - } - ret = check_extent_refs(root, &extent_cache); - return ret; -} - -static void print_usage(void) -{ - fprintf(stderr, "usage: btrfsck dev\n"); - fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); - exit(1); -} - -int main(int ac, char **av) -{ - struct cache_tree root_cache; - struct btrfs_root *root; - int ret; - - if (ac < 2) - print_usage(); - - radix_tree_init(); - cache_tree_init(&root_cache); - root = open_ctree(av[1], 0, 0); - - if (root == NULL) - return 1; - - ret = check_extents(root); - if (ret) - goto out; - ret = check_fs_roots(root, &root_cache); - if (ret) - goto out; - - ret = check_root_refs(root, &root_cache); -out: - free_root_recs(&root_cache); - close_ctree(root); - - if (found_old_backref) { - /* - * there was a disk format change when mixed - * backref was in testing tree. The old format - * existed about one week. - */ - printf("\n * Found old mixed backref format. " - "The old format is not supported! *" - "\n * Please mount the FS in readonly mode, " - "backup data and re-format the FS. *\n\n"); - ret = 1; - } - printf("found %llu bytes used err is %d\n", - (unsigned long long)bytes_used, ret); - printf("total csum bytes: %llu\n",(unsigned long long)total_csum_bytes); - printf("total tree bytes: %llu\n", - (unsigned long long)total_btree_bytes); - printf("total fs tree bytes: %llu\n", - (unsigned long long)total_fs_tree_bytes); - printf("btree space waste bytes: %llu\n", - (unsigned long long)btree_space_waste); - printf("file data blocks allocated: %llu\n referenced %llu\n", - (unsigned long long)data_bytes_allocated, - (unsigned long long)data_bytes_referenced); - printf("%s\n", BTRFS_BUILD_VERSION); - return ret; -} diff --git a/btrfsctl.c b/btrfsctl.c deleted file mode 100644 index 66c4e89..0000000 --- a/btrfsctl.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __CHECKER__ -#include -#include -#include "ioctl.h" -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "ctree.h" -#include "transaction.h" -#include "utils.h" -#include "version.h" - -#ifdef __CHECKER__ -#define BLKGETSIZE64 0 -#define BTRFS_IOC_SNAP_CREATE 0 -#define BTRFS_VOL_NAME_MAX 255 -struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; }; -static inline int ioctl(int fd, int define, void *arg) { return 0; } -#endif - -static void print_usage(void) -{ - printf("usage: btrfsctl [ -d file|dir] [ -s snap_name subvol|tree ]\n"); - printf(" [-r size] [-A device] [-a] [-c] [-D dir .]\n"); - printf("\t-d filename: defragments one file\n"); - printf("\t-d directory: defragments the entire Btree\n"); - printf("\t-s snap_name dir: creates a new snapshot of dir\n"); - printf("\t-S subvol_name dir: creates a new subvolume\n"); - printf("\t-r [+-]size[gkm]: resize the FS by size amount\n"); - printf("\t-A device: scans the device file for a Btrfs filesystem\n"); - printf("\t-a: scans all devices for Btrfs filesystems\n"); - printf("\t-c: forces a single FS sync\n"); - printf("\t-D: delete snapshot\n"); - printf("%s\n", BTRFS_BUILD_VERSION); - exit(1); -} - -static int open_file_or_dir(const char *fname) -{ - int ret; - struct stat st; - DIR *dirstream; - int fd; - - ret = stat(fname, &st); - if (ret < 0) { - perror("stat:"); - exit(1); - } - if (S_ISDIR(st.st_mode)) { - dirstream = opendir(fname); - if (!dirstream) { - perror("opendir"); - exit(1); - } - fd = dirfd(dirstream); - } else { - fd = open(fname, O_RDWR); - } - if (fd < 0) { - perror("open"); - exit(1); - } - return fd; -} -int main(int ac, char **av) -{ - char *fname = NULL; - char *snap_location = NULL; - int snap_fd = 0; - int fd; - int ret; - struct btrfs_ioctl_vol_args args; - char *name = NULL; - int i; - unsigned long command = 0; - int len; - char *fullpath; - - if (ac == 2 && strcmp(av[1], "-a") == 0) { - fprintf(stderr, "Scanning for Btrfs filesystems\n"); - btrfs_scan_one_dir("/dev", 1); - exit(0); - } - for (i = 1; i < ac; i++) { - if (strcmp(av[i], "-s") == 0) { - if (i + 1 >= ac - 1) { - fprintf(stderr, "-s requires an arg"); - print_usage(); - } - fullpath = av[i + 1]; - - snap_location = strdup(fullpath); - snap_location = dirname(snap_location); - - snap_fd = open_file_or_dir(snap_location); - - name = strdup(fullpath); - name = basename(name); - len = strlen(name); - - if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { - fprintf(stderr, - "snapshot name zero length or too long\n"); - exit(1); - } - if (strchr(name, '/')) { - fprintf(stderr, - "error: / not allowed in names\n"); - exit(1); - } - command = BTRFS_IOC_SNAP_CREATE; - } else if (strcmp(av[i], "-S") == 0) { - if (i + 1 >= ac - 1) { - fprintf(stderr, "-S requires an arg"); - print_usage(); - } - name = av[i + 1]; - len = strlen(name); - if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { - fprintf(stderr, - "snapshot name zero length or too long\n"); - exit(1); - } - if (strchr(name, '/')) { - fprintf(stderr, - "error: / not allowed in names\n"); - exit(1); - } - command = BTRFS_IOC_SUBVOL_CREATE; - } else if (strcmp(av[i], "-d") == 0) { - if (i >= ac - 1) { - fprintf(stderr, "-d requires an arg\n"); - print_usage(); - } - command = BTRFS_IOC_DEFRAG; - } else if (strcmp(av[i], "-D") == 0) { - if (i >= ac - 1) { - fprintf(stderr, "-D requires an arg\n"); - print_usage(); - } - command = BTRFS_IOC_SNAP_DESTROY; - name = av[i + 1]; - len = strlen(name); - if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { - fprintf(stderr, "-D size too long\n"); - exit(1); - } - } else if (strcmp(av[i], "-A") == 0) { - if (i >= ac - 1) { - fprintf(stderr, "-A requires an arg\n"); - print_usage(); - } - command = BTRFS_IOC_SCAN_DEV; - } else if (strcmp(av[i], "-r") == 0) { - if (i >= ac - 1) { - fprintf(stderr, "-r requires an arg\n"); - print_usage(); - } - name = av[i + 1]; - len = strlen(name); - if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { - fprintf(stderr, "-r size too long\n"); - exit(1); - } - command = BTRFS_IOC_RESIZE; - } else if (strcmp(av[i], "-c") == 0) { - command = BTRFS_IOC_SYNC; - } - } - if (command == 0) { - fprintf(stderr, "no valid commands given\n"); - print_usage(); - exit(1); - } - fname = av[ac - 1]; - - if (command == BTRFS_IOC_SCAN_DEV) { - fd = open("/dev/btrfs-control", O_RDWR); - if (fd < 0) { - perror("failed to open /dev/btrfs-control"); - exit(1); - } - name = fname; - } else { - fd = open_file_or_dir(fname); - } - - if (name) - strcpy(args.name, name); - else - args.name[0] = '\0'; - - if (command == BTRFS_IOC_SNAP_CREATE) { - args.fd = fd; - ret = ioctl(snap_fd, command, &args); - } else - ret = ioctl(fd, command, &args); - if (ret < 0) { - perror("ioctl:"); - exit(1); - } - if (ret == 0) { - printf("operation complete\n"); - } else { - printf("ioctl failed with error %d\n", ret); - } - printf("%s\n", BTRFS_BUILD_VERSION); - if (ret) - exit(0); - else - exit(1); -} - diff --git a/btrfstune.c b/btrfstune.c deleted file mode 100644 index 47830c5..0000000 --- a/btrfstune.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2008 Oracle. 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 -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "ctree.h" -#include "disk-io.h" -#include "transaction.h" -#include "utils.h" -#include "version.h" - -static char *device; - -int update_seeding_flag(struct btrfs_root *root, int set_flag) -{ - struct btrfs_trans_handle *trans; - struct btrfs_super_block *disk_super; - u64 super_flags; - - disk_super = &root->fs_info->super_copy; - super_flags = btrfs_super_flags(disk_super); - if (set_flag) { - if (super_flags & BTRFS_SUPER_FLAG_SEEDING) { - fprintf(stderr, "seeding flag is already set on %s\n", - device); - return 1; - } - super_flags |= BTRFS_SUPER_FLAG_SEEDING; - } else { - if (!(super_flags & BTRFS_SUPER_FLAG_SEEDING)) { - fprintf(stderr, "seeding flag is not set on %s\n", - device); - return 1; - } - super_flags &= ~BTRFS_SUPER_FLAG_SEEDING; - } - - trans = btrfs_start_transaction(root, 1); - btrfs_set_super_flags(disk_super, super_flags); - btrfs_commit_transaction(trans, root); - - return 0; -} - -static void print_usage(void) -{ - fprintf(stderr, "usage: btrfstune [options] device\n"); - fprintf(stderr, "\t-S value\tenable/disable seeding\n"); -} - -int main(int argc, char *argv[]) -{ - struct btrfs_root *root; - int success = 0; - int seeding_flag = 0; - int seeding_value = 0; - int ret; - - while(1) { - int c = getopt(argc, argv, "S:"); - if (c < 0) - break; - switch(c) { - case 'S': - seeding_flag = 1; - seeding_value = atoi(optarg); - break; - default: - print_usage(); - return 1; - } - } - - argc = argc - optind; - device = argv[optind]; - if (argc != 1) { - print_usage(); - return 1; - } - - if (check_mounted(device)) { - fprintf(stderr, "%s is mounted\n", device); - return 1; - } - - root = open_ctree(device, 0, 1); - - if (seeding_flag) { - ret = update_seeding_flag(root, seeding_value); - if (!ret) - success++; - } - - if (success > 0) { - ret = 0; - } else { - root->fs_info->readonly = 1; - ret = 1; - } - close_ctree(root); - - return ret; -} diff --git a/convert.c b/convert.c deleted file mode 100644 index d037c98..0000000 --- a/convert.c +++ /dev/null @@ -1,2872 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 600 -#define _GNU_SOURCE 1 -#ifndef __CHECKER__ -#include -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "ctree.h" -#include "disk-io.h" -#include "volumes.h" -#include "transaction.h" -#include "crc32c.h" -#include "utils.h" -#include -#include -#include - -#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO) -#define STRIPE_LEN (64 * 1024) -#define EXT2_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID - -/* - * Open Ext2fs in readonly mode, read block allocation bitmap and - * inode bitmap into memory. - */ -static int open_ext2fs(const char *name, ext2_filsys *ret_fs) -{ - errcode_t ret; - ext2_filsys ext2_fs; - ext2_ino_t ino; - ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs); - if (ret) { - fprintf(stderr, "ext2fs_open: %s\n", error_message(ret)); - goto fail; - } - ret = ext2fs_read_inode_bitmap(ext2_fs); - if (ret) { - fprintf(stderr, "ext2fs_read_inode_bitmap: %s\n", - error_message(ret)); - goto fail; - } - ret = ext2fs_read_block_bitmap(ext2_fs); - if (ret) { - fprintf(stderr, "ext2fs_read_block_bitmap: %s\n", - error_message(ret)); - goto fail; - } - /* - * search each block group for a free inode. this set up - * uninit block/inode bitmaps appropriately. - */ - ino = 1; - while (ino <= ext2_fs->super->s_inodes_count) { - ext2_ino_t foo; - ext2fs_new_inode(ext2_fs, ino, 0, NULL, &foo); - ino += EXT2_INODES_PER_GROUP(ext2_fs->super); - } - - *ret_fs = ext2_fs; - return 0; -fail: - return -1; -} - -static int close_ext2fs(ext2_filsys fs) -{ - ext2fs_close(fs); - return 0; -} - -static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret) -{ - blk_t block; - - if (!ext2fs_new_block(fs, goal, NULL, &block)) { - ext2fs_fast_mark_block_bitmap(fs->block_map, block); - *block_ret = block; - return 0; - } - return -ENOSPC; -} - -static int ext2_free_block(ext2_filsys fs, u64 block) -{ - BUG_ON(block != (blk_t)block); - ext2fs_fast_unmark_block_bitmap(fs->block_map, block); - return 0; -} - -static int cache_free_extents(struct btrfs_root *root, ext2_filsys ext2_fs) - -{ - int i, ret = 0; - blk_t block; - u64 bytenr; - u64 blocksize = ext2_fs->blocksize; - - block = ext2_fs->super->s_first_data_block; - for (; block < ext2_fs->super->s_blocks_count; block++) { - if (ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block)) - continue; - bytenr = block * blocksize; - ret = set_extent_dirty(&root->fs_info->free_space_cache, - bytenr, bytenr + blocksize - 1, 0); - BUG_ON(ret); - } - - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - bytenr = btrfs_sb_offset(i); - bytenr &= ~((u64)STRIPE_LEN - 1); - if (bytenr >= blocksize * ext2_fs->super->s_blocks_count) - break; - clear_extent_dirty(&root->fs_info->free_space_cache, bytenr, - bytenr + STRIPE_LEN - 1, 0); - } - - clear_extent_dirty(&root->fs_info->free_space_cache, - 0, BTRFS_SUPER_INFO_OFFSET - 1, 0); - - return 0; -} - -static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes, - u64 hint_byte, struct btrfs_key *ins) -{ - u64 start; - u64 end; - u64 last = hint_byte; - int ret; - int wrapped = 0; - struct btrfs_block_group_cache *cache; - - while(1) { - ret = find_first_extent_bit(&root->fs_info->free_space_cache, - last, &start, &end, EXTENT_DIRTY); - if (ret) { - if (wrapped++ == 0) { - last = 0; - continue; - } else { - goto fail; - } - } - - start = max(last, start); - last = end + 1; - if (last - start < num_bytes) - continue; - - last = start + num_bytes; - if (test_range_bit(&root->fs_info->pinned_extents, - start, last - 1, EXTENT_DIRTY, 0)) - continue; - - cache = btrfs_lookup_block_group(root->fs_info, start); - BUG_ON(!cache); - if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM || - last > cache->key.objectid + cache->key.offset) { - last = cache->key.objectid + cache->key.offset; - continue; - } - - clear_extent_dirty(&root->fs_info->free_space_cache, - start, start + num_bytes - 1, 0); - - ins->objectid = start; - ins->offset = num_bytes; - ins->type = BTRFS_EXTENT_ITEM_KEY; - return 0; - } -fail: - fprintf(stderr, "not enough free space\n"); - return -ENOSPC; -} - -static int intersect_with_sb(u64 bytenr, u64 num_bytes) -{ - int i; - u64 offset; - - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - offset = btrfs_sb_offset(i); - offset &= ~((u64)STRIPE_LEN - 1); - - if (bytenr < offset + STRIPE_LEN && - bytenr + num_bytes > offset) - return 1; - } - return 0; -} - -static int custom_free_extent(struct btrfs_root *root, u64 bytenr, - u64 num_bytes) -{ - return intersect_with_sb(bytenr, num_bytes); -} - -struct btrfs_extent_ops extent_ops = { - .alloc_extent = custom_alloc_extent, - .free_extent = custom_free_extent, -}; - -struct dir_iterate_data { - struct btrfs_trans_handle *trans; - struct btrfs_root *root; - struct btrfs_inode_item *inode; - u64 objectid; - u64 index_cnt; - u64 parent; - int errcode; -}; - -static u8 filetype_conversion_table[EXT2_FT_MAX] = { - [EXT2_FT_UNKNOWN] = BTRFS_FT_UNKNOWN, - [EXT2_FT_REG_FILE] = BTRFS_FT_REG_FILE, - [EXT2_FT_DIR] = BTRFS_FT_DIR, - [EXT2_FT_CHRDEV] = BTRFS_FT_CHRDEV, - [EXT2_FT_BLKDEV] = BTRFS_FT_BLKDEV, - [EXT2_FT_FIFO] = BTRFS_FT_FIFO, - [EXT2_FT_SOCK] = BTRFS_FT_SOCK, - [EXT2_FT_SYMLINK] = BTRFS_FT_SYMLINK, -}; - -static int dir_iterate_proc(ext2_ino_t dir, int entry, - struct ext2_dir_entry *old, - int offset, int blocksize, - char *buf,void *priv_data) -{ - int ret; - int file_type; - u64 objectid; - u64 inode_size; - char dotdot[] = ".."; - struct btrfs_key location; - struct ext2_dir_entry_2 *dirent = (struct ext2_dir_entry_2 *)old; - struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data; - - objectid = dirent->inode + INO_OFFSET; - if (!strncmp(dirent->name, dotdot, dirent->name_len)) { - if (dirent->name_len == 2) { - BUG_ON(idata->parent != 0); - idata->parent = objectid; - } - return 0; - } - if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO) - return 0; - - location.objectid = objectid; - location.offset = 0; - btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); - - file_type = dirent->file_type; - BUG_ON(file_type > EXT2_FT_SYMLINK); - ret = btrfs_insert_dir_item(idata->trans, idata->root, - dirent->name, dirent->name_len, - idata->objectid, &location, - filetype_conversion_table[file_type], - idata->index_cnt); - if (ret) - goto fail; - ret = btrfs_insert_inode_ref(idata->trans, idata->root, - dirent->name, dirent->name_len, - objectid, idata->objectid, - idata->index_cnt); - if (ret) - goto fail; - idata->index_cnt++; - inode_size = btrfs_stack_inode_size(idata->inode) + - dirent->name_len * 2; - btrfs_set_stack_inode_size(idata->inode, inode_size); - return 0; -fail: - idata->errcode = ret; - return BLOCK_ABORT; -} - -static int create_dir_entries(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *btrfs_inode, - ext2_filsys ext2_fs, ext2_ino_t ext2_ino) -{ - int ret; - errcode_t err; - struct dir_iterate_data data = { - .trans = trans, - .root = root, - .inode = btrfs_inode, - .objectid = objectid, - .index_cnt = 2, - .parent = 0, - .errcode = 0, - }; - - err = ext2fs_dir_iterate2(ext2_fs, ext2_ino, 0, NULL, - dir_iterate_proc, &data); - if (err) - goto error; - ret = data.errcode; - if (ret == 0 && data.parent == objectid) { - ret = btrfs_insert_inode_ref(trans, root, "..", 2, - objectid, objectid, 0); - } - return ret; -error: - fprintf(stderr, "ext2fs_dir_iterate2: %s\n", error_message(err)); - return -1; -} - -static int read_disk_extent(struct btrfs_root *root, u64 bytenr, - u32 num_bytes, char *buffer) -{ - int ret; - struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; - - ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr); - if (ret != num_bytes) - goto fail; - ret = 0; -fail: - if (ret > 0) - ret = -1; - return ret; -} -/* - * Record a file extent. Do all the required works, such as inserting - * file extent item, inserting extent item and backref item into extent - * tree and updating block accounting. - */ -static int record_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 file_pos, u64 disk_bytenr, - u64 num_bytes, int checksum) -{ - int ret; - struct btrfs_fs_info *info = root->fs_info; - struct btrfs_root *extent_root = info->extent_root; - struct extent_buffer *leaf; - struct btrfs_file_extent_item *fi; - struct btrfs_key ins_key; - struct btrfs_path path; - struct btrfs_extent_item *ei; - u32 blocksize = root->sectorsize; - u64 nbytes; - - if (disk_bytenr == 0) { - ret = btrfs_insert_file_extent(trans, root, objectid, - file_pos, disk_bytenr, - num_bytes, num_bytes); - return ret; - } - - btrfs_init_path(&path); - - if (checksum) { - u64 offset; - char *buffer; - - ret = -ENOMEM; - buffer = malloc(blocksize); - if (!buffer) - goto fail; - for (offset = 0; offset < num_bytes; offset += blocksize) { - ret = read_disk_extent(root, disk_bytenr + offset, - blocksize, buffer); - if (ret) - break; - ret = btrfs_csum_file_block(trans, - root->fs_info->csum_root, - disk_bytenr + num_bytes, - disk_bytenr + offset, - buffer, blocksize); - if (ret) - break; - } - free(buffer); - if (ret) - goto fail; - } - - ins_key.objectid = objectid; - ins_key.offset = file_pos; - btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY); - ret = btrfs_insert_empty_item(trans, root, &path, &ins_key, - sizeof(*fi)); - if (ret) - goto fail; - leaf = path.nodes[0]; - fi = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_file_extent_item); - btrfs_set_file_extent_generation(leaf, fi, trans->transid); - btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); - btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr); - btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes); - btrfs_set_file_extent_offset(leaf, fi, 0); - btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes); - btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes); - btrfs_set_file_extent_compression(leaf, fi, 0); - btrfs_set_file_extent_encryption(leaf, fi, 0); - btrfs_set_file_extent_other_encoding(leaf, fi, 0); - btrfs_mark_buffer_dirty(leaf); - - nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes; - btrfs_set_stack_inode_nbytes(inode, nbytes); - - btrfs_release_path(root, &path); - - ins_key.objectid = disk_bytenr; - ins_key.offset = num_bytes; - ins_key.type = BTRFS_EXTENT_ITEM_KEY; - - ret = btrfs_insert_empty_item(trans, extent_root, &path, - &ins_key, sizeof(*ei)); - if (ret == 0) { - leaf = path.nodes[0]; - ei = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_extent_item); - - btrfs_set_extent_refs(leaf, ei, 0); - btrfs_set_extent_generation(leaf, ei, 0); - btrfs_set_extent_flags(leaf, ei, BTRFS_EXTENT_FLAG_DATA); - - btrfs_mark_buffer_dirty(leaf); - - ret = btrfs_update_block_group(trans, root, disk_bytenr, - num_bytes, 1, 0); - if (ret) - goto fail; - } else if (ret != -EEXIST) { - goto fail; - } - btrfs_extent_post_op(trans, extent_root); - - ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0, - root->root_key.objectid, - objectid, file_pos); - if (ret) - goto fail; - ret = 0; -fail: - btrfs_release_path(root, &path); - return ret; -} - -static int record_file_blocks(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 file_block, u64 disk_block, - u64 num_blocks, int checksum) -{ - u64 file_pos = file_block * root->sectorsize; - u64 disk_bytenr = disk_block * root->sectorsize; - u64 num_bytes = num_blocks * root->sectorsize; - return record_file_extent(trans, root, objectid, inode, file_pos, - disk_bytenr, num_bytes, checksum); -} - -struct blk_iterate_data { - struct btrfs_trans_handle *trans; - struct btrfs_root *root; - struct btrfs_inode_item *inode; - u64 objectid; - u64 first_block; - u64 disk_block; - u64 num_blocks; - u64 boundary; - int checksum; - int errcode; -}; - -static int block_iterate_proc(ext2_filsys ext2_fs, - u64 disk_block, u64 file_block, - struct blk_iterate_data *idata) -{ - int ret; - int sb_region; - int do_barrier; - struct btrfs_root *root = idata->root; - struct btrfs_trans_handle *trans = idata->trans; - struct btrfs_block_group_cache *cache; - u64 bytenr = disk_block * root->sectorsize; - - sb_region = intersect_with_sb(bytenr, root->sectorsize); - do_barrier = sb_region || disk_block >= idata->boundary; - if ((idata->num_blocks > 0 && do_barrier) || - (file_block > idata->first_block + idata->num_blocks) || - (disk_block != idata->disk_block + idata->num_blocks)) { - if (idata->num_blocks > 0) { - ret = record_file_blocks(trans, root, idata->objectid, - idata->inode, idata->first_block, - idata->disk_block, idata->num_blocks, - idata->checksum); - if (ret) - goto fail; - idata->first_block += idata->num_blocks; - idata->num_blocks = 0; - } - if (file_block > idata->first_block) { - ret = record_file_blocks(trans, root, idata->objectid, - idata->inode, idata->first_block, - 0, file_block - idata->first_block, - idata->checksum); - if (ret) - goto fail; - } - - if (sb_region) { - bytenr += STRIPE_LEN - 1; - bytenr &= ~((u64)STRIPE_LEN - 1); - } else { - cache = btrfs_lookup_block_group(root->fs_info, bytenr); - BUG_ON(!cache); - bytenr = cache->key.objectid + cache->key.offset; - } - - idata->first_block = file_block; - idata->disk_block = disk_block; - idata->boundary = bytenr / root->sectorsize; - } - idata->num_blocks++; - return 0; -fail: - idata->errcode = ret; - return BLOCK_ABORT; -} - -static int __block_iterate_proc(ext2_filsys fs, blk_t *blocknr, - e2_blkcnt_t blockcnt, blk_t ref_block, - int ref_offset, void *priv_data) -{ - struct blk_iterate_data *idata; - idata = (struct blk_iterate_data *)priv_data; - return block_iterate_proc(fs, *blocknr, blockcnt, idata); -} - -/* - * traverse file's data blocks, record these data blocks as file extents. - */ -static int create_file_extents(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *btrfs_inode, - ext2_filsys ext2_fs, ext2_ino_t ext2_ino, - int datacsum, int packing) -{ - int ret; - char *buffer = NULL; - errcode_t err; - u32 last_block; - u32 sectorsize = root->sectorsize; - u64 inode_size = btrfs_stack_inode_size(btrfs_inode); - struct blk_iterate_data data = { - .trans = trans, - .root = root, - .inode = btrfs_inode, - .objectid = objectid, - .first_block = 0, - .disk_block = 0, - .num_blocks = 0, - .boundary = (u64)-1, - .checksum = datacsum, - .errcode = 0, - }; - err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY, - NULL, __block_iterate_proc, &data); - if (err) - goto error; - ret = data.errcode; - if (ret) - goto fail; - if (packing && data.first_block == 0 && data.num_blocks > 0 && - inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) { - u64 num_bytes = data.num_blocks * sectorsize; - u64 disk_bytenr = data.disk_block * sectorsize; - u64 nbytes; - - buffer = malloc(num_bytes); - if (!buffer) - return -ENOMEM; - ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer); - if (ret) - goto fail; - if (num_bytes > inode_size) - num_bytes = inode_size; - ret = btrfs_insert_inline_extent(trans, root, objectid, - 0, buffer, num_bytes); - if (ret) - goto fail; - nbytes = btrfs_stack_inode_nbytes(btrfs_inode) + num_bytes; - btrfs_set_stack_inode_nbytes(btrfs_inode, nbytes); - } else if (data.num_blocks > 0) { - ret = record_file_blocks(trans, root, objectid, btrfs_inode, - data.first_block, data.disk_block, - data.num_blocks, data.checksum); - if (ret) - goto fail; - } - data.first_block += data.num_blocks; - last_block = (inode_size + sectorsize - 1) / sectorsize; - if (last_block > data.first_block) { - ret = record_file_blocks(trans, root, objectid, btrfs_inode, - data.first_block, 0, last_block - - data.first_block, data.checksum); - } -fail: - if (buffer) - free(buffer); - return ret; -error: - fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err)); - return -1; -} - -static int create_symbol_link(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *btrfs_inode, - ext2_filsys ext2_fs, ext2_ino_t ext2_ino, - struct ext2_inode *ext2_inode) -{ - int ret; - char *pathname; - u64 inode_size = btrfs_stack_inode_size(btrfs_inode); - if (ext2fs_inode_data_blocks(ext2_fs, ext2_inode)) { - btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1); - ret = create_file_extents(trans, root, objectid, btrfs_inode, - ext2_fs, ext2_ino, 1, 1); - btrfs_set_stack_inode_size(btrfs_inode, inode_size); - return ret; - } - - pathname = (char *)&(ext2_inode->i_block[0]); - BUG_ON(pathname[inode_size] != 0); - ret = btrfs_insert_inline_extent(trans, root, objectid, 0, - pathname, inode_size + 1); - btrfs_set_stack_inode_nbytes(btrfs_inode, inode_size + 1); - return ret; -} - -/* - * Following xattr/acl related codes are based on codes in - * fs/ext3/xattr.c and fs/ext3/acl.c - */ -#define EXT2_XATTR_BHDR(ptr) ((struct ext2_ext_attr_header *)(ptr)) -#define EXT2_XATTR_BFIRST(ptr) \ - ((struct ext2_ext_attr_entry *)(EXT2_XATTR_BHDR(ptr) + 1)) -#define EXT2_XATTR_IHDR(inode) \ - ((struct ext2_ext_attr_header *) ((void *)(inode) + \ - EXT2_GOOD_OLD_INODE_SIZE + (inode)->i_extra_isize)) -#define EXT2_XATTR_IFIRST(inode) \ - ((struct ext2_ext_attr_entry *) ((void *)EXT2_XATTR_IHDR(inode) + \ - sizeof(EXT2_XATTR_IHDR(inode)->h_magic))) - -static int ext2_xattr_check_names(struct ext2_ext_attr_entry *entry, - const void *end) -{ - struct ext2_ext_attr_entry *next; - - while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { - next = EXT2_EXT_ATTR_NEXT(entry); - if ((void *)next >= end) - return -EIO; - entry = next; - } - return 0; -} - -static int ext2_xattr_check_block(const char *buf, size_t size) -{ - int error; - struct ext2_ext_attr_header *header = EXT2_XATTR_BHDR(buf); - - if (header->h_magic != EXT2_EXT_ATTR_MAGIC || - header->h_blocks != 1) - return -EIO; - error = ext2_xattr_check_names(EXT2_XATTR_BFIRST(buf), buf + size); - return error; -} - -static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry, - size_t size) -{ - size_t value_size = entry->e_value_size; - - if (entry->e_value_block != 0 || value_size > size || - entry->e_value_offs + value_size > size) - return -EIO; - return 0; -} - -#define EXT2_ACL_VERSION 0x0001 - -typedef struct { - __le16 e_tag; - __le16 e_perm; - __le32 e_id; -} ext2_acl_entry; - -typedef struct { - __le16 e_tag; - __le16 e_perm; -} ext2_acl_entry_short; - -typedef struct { - __le32 a_version; -} ext2_acl_header; - -static inline int ext2_acl_count(size_t size) -{ - ssize_t s; - size -= sizeof(ext2_acl_header); - s = size - 4 * sizeof(ext2_acl_entry_short); - if (s < 0) { - if (size % sizeof(ext2_acl_entry_short)) - return -1; - return size / sizeof(ext2_acl_entry_short); - } else { - if (s % sizeof(ext2_acl_entry)) - return -1; - return s / sizeof(ext2_acl_entry) + 4; - } -} - -#define ACL_EA_VERSION 0x0002 - -typedef struct { - __le16 e_tag; - __le16 e_perm; - __le32 e_id; -} acl_ea_entry; - -typedef struct { - __le32 a_version; - acl_ea_entry a_entries[0]; -} acl_ea_header; - -static inline size_t acl_ea_size(int count) -{ - return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry); -} - -static int ext2_acl_to_xattr(void *dst, const void *src, - size_t dst_size, size_t src_size) -{ - int i, count; - const void *end = src + src_size; - acl_ea_header *ext_acl = (acl_ea_header *)dst; - acl_ea_entry *dst_entry = ext_acl->a_entries; - ext2_acl_entry *src_entry; - - if (src_size < sizeof(ext2_acl_header)) - goto fail; - if (((ext2_acl_header *)src)->a_version != - cpu_to_le32(EXT2_ACL_VERSION)) - goto fail; - src += sizeof(ext2_acl_header); - count = ext2_acl_count(src_size); - if (count <= 0) - goto fail; - - BUG_ON(dst_size < acl_ea_size(count)); - ext_acl->a_version = cpu_to_le32(ACL_EA_VERSION); - for (i = 0; i < count; i++, dst_entry++) { - src_entry = (ext2_acl_entry *)src; - if (src + sizeof(ext2_acl_entry_short) > end) - goto fail; - dst_entry->e_tag = src_entry->e_tag; - dst_entry->e_perm = src_entry->e_perm; - switch (le16_to_cpu(src_entry->e_tag)) { - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_MASK: - case ACL_OTHER: - src += sizeof(ext2_acl_entry_short); - dst_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); - break; - case ACL_USER: - case ACL_GROUP: - src += sizeof(ext2_acl_entry); - if (src > end) - goto fail; - dst_entry->e_id = src_entry->e_id; - break; - default: - goto fail; - } - } - if (src != end) - goto fail; - return 0; -fail: - return -EINVAL; -} - -static char *xattr_prefix_table[] = { - [1] = "user.", - [2] = "system.posix_acl_access", - [3] = "system.posix_acl_default", - [4] = "trusted.", - [6] = "security.", -}; - -static int copy_single_xattr(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct ext2_ext_attr_entry *entry, - const void *data, u32 datalen) -{ - int ret = 0; - int name_len; - int name_index; - void *databuf = NULL; - char namebuf[XATTR_NAME_MAX + 1]; - - name_index = entry->e_name_index; - if (name_index >= ARRAY_SIZE(xattr_prefix_table) || - xattr_prefix_table[name_index] == NULL) - return -EOPNOTSUPP; - name_len = strlen(xattr_prefix_table[name_index]) + - entry->e_name_len; - if (name_len >= sizeof(namebuf)) - return -ERANGE; - - if (name_index == 2 || name_index == 3) { - size_t bufsize = acl_ea_size(ext2_acl_count(datalen)); - databuf = malloc(bufsize); - if (!databuf) - return -ENOMEM; - ret = ext2_acl_to_xattr(databuf, data, bufsize, datalen); - if (ret) - goto out; - data = databuf; - datalen = bufsize; - } - strcpy(namebuf, xattr_prefix_table[name_index]); - strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len); - if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) - - sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) { - fprintf(stderr, "skip large xattr on inode %Lu name %.*s\n", - objectid - INO_OFFSET, name_len, namebuf); - goto out; - } - ret = btrfs_insert_xattr_item(trans, root, namebuf, name_len, - data, datalen, objectid); -out: - if (databuf) - free(databuf); - return ret; -} - -static int copy_extended_attrs(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *btrfs_inode, - ext2_filsys ext2_fs, ext2_ino_t ext2_ino) -{ - int ret = 0; - int inline_ea = 0; - errcode_t err; - u32 datalen; - u32 block_size = ext2_fs->blocksize; - u32 inode_size = EXT2_INODE_SIZE(ext2_fs->super); - struct ext2_inode_large *ext2_inode; - struct ext2_ext_attr_entry *entry; - void *data; - char *buffer = NULL; - char inode_buf[EXT2_GOOD_OLD_INODE_SIZE]; - - if (inode_size <= EXT2_GOOD_OLD_INODE_SIZE) { - ext2_inode = (struct ext2_inode_large *)inode_buf; - } else { - ext2_inode = (struct ext2_inode_large *)malloc(inode_size); - if (!ext2_inode) - return -ENOMEM; - } - err = ext2fs_read_inode_full(ext2_fs, ext2_ino, (void *)ext2_inode, - inode_size); - if (err) { - fprintf(stderr, "ext2fs_read_inode_full: %s\n", - error_message(err)); - ret = -1; - goto out; - } - - if (ext2_ino > ext2_fs->super->s_first_ino && - inode_size > EXT2_GOOD_OLD_INODE_SIZE) { - if (EXT2_GOOD_OLD_INODE_SIZE + - ext2_inode->i_extra_isize > inode_size) { - ret = -EIO; - goto out; - } - if (ext2_inode->i_extra_isize != 0 && - EXT2_XATTR_IHDR(ext2_inode)->h_magic == - EXT2_EXT_ATTR_MAGIC) { - inline_ea = 1; - } - } - if (inline_ea) { - int total; - void *end = (void *)ext2_inode + inode_size; - entry = EXT2_XATTR_IFIRST(ext2_inode); - total = end - (void *)entry; - ret = ext2_xattr_check_names(entry, end); - if (ret) - goto out; - while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { - ret = ext2_xattr_check_entry(entry, total); - if (ret) - goto out; - data = (void *)EXT2_XATTR_IFIRST(ext2_inode) + - entry->e_value_offs; - datalen = entry->e_value_size; - ret = copy_single_xattr(trans, root, objectid, - entry, data, datalen); - if (ret) - goto out; - entry = EXT2_EXT_ATTR_NEXT(entry); - } - } - - if (ext2_inode->i_file_acl == 0) - goto out; - - buffer = malloc(block_size); - if (!buffer) { - ret = -ENOMEM; - goto out; - } - err = ext2fs_read_ext_attr(ext2_fs, ext2_inode->i_file_acl, buffer); - if (err) { - fprintf(stderr, "ext2fs_read_ext_attr: %s\n", - error_message(err)); - ret = -1; - goto out; - } - ret = ext2_xattr_check_block(buffer, block_size); - if (ret) - goto out; - - entry = EXT2_XATTR_BFIRST(buffer); - while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { - ret = ext2_xattr_check_entry(entry, block_size); - if (ret) - goto out; - data = buffer + entry->e_value_offs; - datalen = entry->e_value_size; - ret = copy_single_xattr(trans, root, objectid, - entry, data, datalen); - if (ret) - goto out; - entry = EXT2_EXT_ATTR_NEXT(entry); - } -out: - if (buffer != NULL) - free(buffer); - if ((void *)ext2_inode != inode_buf) - free(ext2_inode); - return ret; -} -#define MINORBITS 20 -#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) - -static inline dev_t old_decode_dev(u16 val) -{ - return MKDEV((val >> 8) & 255, val & 255); -} - -static inline dev_t new_decode_dev(u32 dev) -{ - unsigned major = (dev & 0xfff00) >> 8; - unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); - return MKDEV(major, minor); -} - -static int copy_inode_item(struct btrfs_inode_item *dst, - struct ext2_inode *src, u32 blocksize) -{ - btrfs_set_stack_inode_generation(dst, 1); - btrfs_set_stack_inode_size(dst, src->i_size); - btrfs_set_stack_inode_nbytes(dst, 0); - btrfs_set_stack_inode_block_group(dst, 0); - btrfs_set_stack_inode_nlink(dst, src->i_links_count); - btrfs_set_stack_inode_uid(dst, src->i_uid | (src->i_uid_high << 16)); - btrfs_set_stack_inode_gid(dst, src->i_gid | (src->i_gid_high << 16)); - btrfs_set_stack_inode_mode(dst, src->i_mode); - btrfs_set_stack_inode_rdev(dst, 0); - btrfs_set_stack_inode_flags(dst, 0); - btrfs_set_stack_timespec_sec(&dst->atime, src->i_atime); - btrfs_set_stack_timespec_nsec(&dst->atime, 0); - btrfs_set_stack_timespec_sec(&dst->ctime, src->i_ctime); - btrfs_set_stack_timespec_nsec(&dst->ctime, 0); - btrfs_set_stack_timespec_sec(&dst->mtime, src->i_mtime); - btrfs_set_stack_timespec_nsec(&dst->mtime, 0); - btrfs_set_stack_timespec_sec(&dst->otime, 0); - btrfs_set_stack_timespec_nsec(&dst->otime, 0); - - if (S_ISDIR(src->i_mode)) { - btrfs_set_stack_inode_size(dst, 0); - btrfs_set_stack_inode_nlink(dst, 1); - } - if (S_ISREG(src->i_mode)) { - btrfs_set_stack_inode_size(dst, (u64)src->i_size_high << 32 | - (u64)src->i_size); - } - if (!S_ISREG(src->i_mode) && !S_ISDIR(src->i_mode) && - !S_ISLNK(src->i_mode)) { - if (src->i_block[0]) { - btrfs_set_stack_inode_rdev(dst, - old_decode_dev(src->i_block[0])); - } else { - btrfs_set_stack_inode_rdev(dst, - new_decode_dev(src->i_block[1])); - } - } - return 0; -} - -/* - * copy a single inode. do all the required works, such as cloning - * inode item, creating file extents and creating directory entries. - */ -static int copy_single_inode(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - ext2_filsys ext2_fs, ext2_ino_t ext2_ino, - struct ext2_inode *ext2_inode, - int datacsum, int packing, int noxattr) -{ - int ret; - struct btrfs_key inode_key; - struct btrfs_inode_item btrfs_inode; - - if (ext2_inode->i_links_count == 0) - return 0; - - copy_inode_item(&btrfs_inode, ext2_inode, ext2_fs->blocksize); - if (!datacsum && S_ISREG(ext2_inode->i_mode)) { - u32 flags = btrfs_stack_inode_flags(&btrfs_inode) | - BTRFS_INODE_NODATASUM; - btrfs_set_stack_inode_flags(&btrfs_inode, flags); - } - - switch (ext2_inode->i_mode & S_IFMT) { - case S_IFREG: - ret = create_file_extents(trans, root, objectid, &btrfs_inode, - ext2_fs, ext2_ino, datacsum, packing); - break; - case S_IFDIR: - ret = create_dir_entries(trans, root, objectid, &btrfs_inode, - ext2_fs, ext2_ino); - break; - case S_IFLNK: - ret = create_symbol_link(trans, root, objectid, &btrfs_inode, - ext2_fs, ext2_ino, ext2_inode); - break; - default: - ret = 0; - break; - } - if (ret) - return ret; - - if (!noxattr) { - ret = copy_extended_attrs(trans, root, objectid, &btrfs_inode, - ext2_fs, ext2_ino); - if (ret) - return ret; - } - inode_key.objectid = objectid; - inode_key.offset = 0; - btrfs_set_key_type(&inode_key, BTRFS_INODE_ITEM_KEY); - ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); - return ret; -} - -static int copy_disk_extent(struct btrfs_root *root, u64 dst_bytenr, - u64 src_bytenr, u32 num_bytes) -{ - int ret; - char *buffer; - struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; - - buffer = malloc(num_bytes); - if (!buffer) - return -ENOMEM; - ret = pread(fs_devs->latest_bdev, buffer, num_bytes, src_bytenr); - if (ret != num_bytes) - goto fail; - ret = pwrite(fs_devs->latest_bdev, buffer, num_bytes, dst_bytenr); - if (ret != num_bytes) - goto fail; - ret = 0; -fail: - free(buffer); - if (ret > 0) - ret = -1; - return ret; -} -/* - * scan ext2's inode bitmap and copy all used inode. - */ -static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs, - int datacsum, int packing, int noxattr) -{ - int ret; - errcode_t err; - ext2_inode_scan ext2_scan; - struct ext2_inode ext2_inode; - ext2_ino_t ext2_ino; - u64 objectid; - struct btrfs_trans_handle *trans; - - trans = btrfs_start_transaction(root, 1); - if (!trans) - return -ENOMEM; - err = ext2fs_open_inode_scan(ext2_fs, 0, &ext2_scan); - if (err) { - fprintf(stderr, "ext2fs_open_inode_scan: %s\n", error_message(err)); - return -1; - } - while (!(err = ext2fs_get_next_inode(ext2_scan, &ext2_ino, - &ext2_inode))) { - /* no more inodes */ - if (ext2_ino == 0) - break; - /* skip special inode in ext2fs */ - if (ext2_ino < EXT2_GOOD_OLD_FIRST_INO && - ext2_ino != EXT2_ROOT_INO) - continue; - objectid = ext2_ino + INO_OFFSET; - ret = copy_single_inode(trans, root, - objectid, ext2_fs, ext2_ino, - &ext2_inode, datacsum, packing, - noxattr); - if (ret) - return ret; - if (trans->blocks_used >= 4096) { - ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - } - } - if (err) { - fprintf(stderr, "ext2fs_get_next_inode: %s\n", error_message(err)); - return -1; - } - ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); - - return ret; -} - -/* - * Construct a range of ext2fs image file. - * scan block allocation bitmap, find all blocks used by the ext2fs - * in this range and create file extents that point to these blocks. - * - * Note: Before calling the function, no file extent points to blocks - * in this range - */ -static int create_image_file_range(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 start_byte, u64 end_byte, - ext2_filsys ext2_fs) -{ - u32 blocksize = ext2_fs->blocksize; - u32 block = start_byte / blocksize; - u32 last_block = (end_byte + blocksize - 1) / blocksize; - int ret = 0; - struct blk_iterate_data data = { - .trans = trans, - .root = root, - .inode = inode, - .objectid = objectid, - .first_block = block, - .disk_block = 0, - .num_blocks = 0, - .boundary = (u64)-1, - .checksum = 0, - .errcode = 0, - }; - for (; start_byte < end_byte; block++, start_byte += blocksize) { - if (!ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block)) - continue; - ret = block_iterate_proc(NULL, block, block, &data); - if (ret & BLOCK_ABORT) { - ret = data.errcode; - goto fail; - } - } - if (data.num_blocks > 0) { - ret = record_file_blocks(trans, root, objectid, inode, - data.first_block, data.disk_block, - data.num_blocks, 0); - if (ret) - goto fail; - data.first_block += data.num_blocks; - } - if (last_block > data.first_block) { - ret = record_file_blocks(trans, root, objectid, inode, - data.first_block, 0, last_block - - data.first_block, 0); - if (ret) - goto fail; - } -fail: - return ret; -} -/* - * Create the ext2fs image file. - */ -static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs, - const char *name) -{ - int ret; - struct btrfs_key key; - struct btrfs_key location; - struct btrfs_path path; - struct btrfs_inode_item btrfs_inode; - struct btrfs_inode_item *inode_item; - struct extent_buffer *leaf; - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_root *extent_root = fs_info->extent_root; - struct btrfs_trans_handle *trans; - struct btrfs_extent_item *ei; - struct btrfs_extent_inline_ref *iref; - struct btrfs_extent_data_ref *dref; - u64 bytenr; - u64 num_bytes; - u64 objectid; - u64 last_byte; - u64 first_free; - u64 total_bytes; - u32 sectorsize = root->sectorsize; - - total_bytes = btrfs_super_total_bytes(&fs_info->super_copy); - first_free = BTRFS_SUPER_INFO_OFFSET + sectorsize * 2 - 1; - first_free &= ~((u64)sectorsize - 1); - - memset(&btrfs_inode, 0, sizeof(btrfs_inode)); - btrfs_set_stack_inode_generation(&btrfs_inode, 1); - btrfs_set_stack_inode_size(&btrfs_inode, total_bytes); - btrfs_set_stack_inode_nlink(&btrfs_inode, 1); - btrfs_set_stack_inode_nbytes(&btrfs_inode, 0); - btrfs_set_stack_inode_mode(&btrfs_inode, S_IFREG | 0400); - btrfs_set_stack_inode_flags(&btrfs_inode, BTRFS_INODE_NODATASUM | - BTRFS_INODE_READONLY); - btrfs_init_path(&path); - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - - objectid = btrfs_root_dirid(&root->root_item); - ret = btrfs_find_free_objectid(trans, root, objectid, &objectid); - if (ret) - goto fail; - - /* - * copy blocks covered by extent #0 to new positions. extent #0 is - * special, we can't rely on relocate_extents_range to relocate it. - */ - for (last_byte = 0; last_byte < first_free; last_byte += sectorsize) { - ret = custom_alloc_extent(root, sectorsize, 0, &key); - if (ret) - goto fail; - ret = copy_disk_extent(root, key.objectid, last_byte, - sectorsize); - if (ret) - goto fail; - ret = record_file_extent(trans, root, objectid, - &btrfs_inode, last_byte, - key.objectid, sectorsize, 0); - if (ret) - goto fail; - } - - while(1) { - key.objectid = last_byte; - key.offset = 0; - btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); - ret = btrfs_search_slot(trans, fs_info->extent_root, - &key, &path, 0, 0); - if (ret < 0) - goto fail; -next: - leaf = path.nodes[0]; - if (path.slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(extent_root, &path); - if (ret < 0) - goto fail; - if (ret > 0) - break; - leaf = path.nodes[0]; - } - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (last_byte > key.objectid || - key.type != BTRFS_EXTENT_ITEM_KEY) { - path.slots[0]++; - goto next; - } - - bytenr = key.objectid; - num_bytes = key.offset; - ei = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_extent_item); - if (!(btrfs_extent_flags(leaf, ei) & BTRFS_EXTENT_FLAG_DATA)) { - path.slots[0]++; - goto next; - } - - BUG_ON(btrfs_item_size_nr(leaf, path.slots[0]) != sizeof(*ei) + - btrfs_extent_inline_ref_size(BTRFS_EXTENT_DATA_REF_KEY)); - - iref = (struct btrfs_extent_inline_ref *)(ei + 1); - key.type = btrfs_extent_inline_ref_type(leaf, iref); - BUG_ON(key.type != BTRFS_EXTENT_DATA_REF_KEY); - dref = (struct btrfs_extent_data_ref *)(&iref->offset); - if (btrfs_extent_data_ref_root(leaf, dref) != - BTRFS_FS_TREE_OBJECTID) { - path.slots[0]++; - goto next; - } - - if (bytenr > last_byte) { - ret = create_image_file_range(trans, root, objectid, - &btrfs_inode, last_byte, - bytenr, ext2_fs); - if (ret) - goto fail; - } - ret = record_file_extent(trans, root, objectid, &btrfs_inode, - bytenr, bytenr, num_bytes, 0); - if (ret) - goto fail; - last_byte = bytenr + num_bytes; - btrfs_release_path(extent_root, &path); - - if (trans->blocks_used >= 4096) { - ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - } - } - btrfs_release_path(root, &path); - if (total_bytes > last_byte) { - ret = create_image_file_range(trans, root, objectid, - &btrfs_inode, last_byte, - total_bytes, ext2_fs); - if (ret) - goto fail; - } - - ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); - if (ret) - goto fail; - - location.objectid = objectid; - location.offset = 0; - btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); - ret = btrfs_insert_dir_item(trans, root, name, strlen(name), - btrfs_root_dirid(&root->root_item), - &location, EXT2_FT_REG_FILE, objectid); - if (ret) - goto fail; - ret = btrfs_insert_inode_ref(trans, root, name, strlen(name), - objectid, - btrfs_root_dirid(&root->root_item), - objectid); - if (ret) - goto fail; - location.objectid = btrfs_root_dirid(&root->root_item); - location.offset = 0; - btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); - ret = btrfs_lookup_inode(trans, root, &path, &location, 1); - if (ret) - goto fail; - leaf = path.nodes[0]; - inode_item = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_inode_item); - btrfs_set_inode_size(leaf, inode_item, strlen(name) * 2 + - btrfs_inode_size(leaf, inode_item)); - btrfs_mark_buffer_dirty(leaf); - btrfs_release_path(root, &path); - ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); -fail: - btrfs_release_path(root, &path); - return ret; -} - -struct btrfs_root *link_subvol(struct btrfs_root *root, const char *base, - u64 root_objectid) -{ - struct btrfs_trans_handle *trans; - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_root *tree_root = fs_info->tree_root; - struct btrfs_root *new_root = NULL; - struct btrfs_path *path; - struct btrfs_inode_item *inode_item; - struct extent_buffer *leaf; - struct btrfs_key key; - u64 dirid = btrfs_root_dirid(&root->root_item); - u64 index = 2; - char buf[64]; - int i; - int ret; - - path = btrfs_alloc_path(); - BUG_ON(!path); - - key.objectid = dirid; - key.type = BTRFS_DIR_INDEX_KEY; - key.offset = (u64)-1; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - BUG_ON(ret <= 0); - - if (path->slots[0] > 0) { - path->slots[0]--; - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); - if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY) - index = key.offset + 1; - } - btrfs_release_path(root, path); - - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - - key.objectid = dirid; - key.offset = 0; - key.type = BTRFS_INODE_ITEM_KEY; - - ret = btrfs_lookup_inode(trans, root, path, &key, 1); - BUG_ON(ret); - leaf = path->nodes[0]; - inode_item = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_inode_item); - - key.objectid = root_objectid; - key.offset = (u64)-1; - key.type = BTRFS_ROOT_ITEM_KEY; - - strcpy(buf, base); - for (i = 0; i < 1024; i++) { - ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), - dirid, &key, BTRFS_FT_DIR, index); - if (ret != -EEXIST) - break; - sprintf(buf, "%s%d", base, i); - } - if (ret) - goto fail; - - btrfs_set_inode_size(leaf, inode_item, strlen(buf) * 2 + - btrfs_inode_size(leaf, inode_item)); - btrfs_mark_buffer_dirty(leaf); - btrfs_release_path(root, path); - - /* add the backref first */ - ret = btrfs_add_root_ref(trans, tree_root, root_objectid, - BTRFS_ROOT_BACKREF_KEY, - root->root_key.objectid, - dirid, index, buf, strlen(buf)); - BUG_ON(ret); - - /* now add the forward ref */ - ret = btrfs_add_root_ref(trans, tree_root, root->root_key.objectid, - BTRFS_ROOT_REF_KEY, root_objectid, - dirid, index, buf, strlen(buf)); - - ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); - - new_root = btrfs_read_fs_root(fs_info, &key); - if (IS_ERR(new_root)) - new_root = NULL; -fail: - btrfs_free_path(path); - return new_root; -} - -/* - * Fixup block accounting. The initial block accounting created by - * make_block_groups isn't accuracy in this case. - */ -static int fixup_block_accounting(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - int ret; - int slot; - u64 start = 0; - u64 bytes_used = 0; - struct btrfs_path path; - struct btrfs_key key; - struct extent_buffer *leaf; - struct btrfs_block_group_cache *cache; - struct btrfs_fs_info *fs_info = root->fs_info; - - while(1) { - cache = btrfs_lookup_block_group(fs_info, start); - if (!cache) - break; - start = cache->key.objectid + cache->key.offset; - btrfs_set_block_group_used(&cache->item, 0); - cache->space_info->bytes_used = 0; - } - - btrfs_init_path(&path); - key.offset = 0; - key.objectid = 0; - btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); - ret = btrfs_search_slot(trans, root->fs_info->extent_root, - &key, &path, 0, 0); - if (ret < 0) - return ret; - while(1) { - leaf = path.nodes[0]; - slot = path.slots[0]; - if (slot >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, &path); - if (ret < 0) - return ret; - if (ret > 0) - break; - leaf = path.nodes[0]; - slot = path.slots[0]; - } - btrfs_item_key_to_cpu(leaf, &key, slot); - if (key.type == BTRFS_EXTENT_ITEM_KEY) { - bytes_used += key.offset; - ret = btrfs_update_block_group(trans, root, - key.objectid, key.offset, 1, 0); - BUG_ON(ret); - } - path.slots[0]++; - } - btrfs_set_super_bytes_used(&root->fs_info->super_copy, bytes_used); - btrfs_release_path(root, &path); - return 0; -} - -static int create_chunk_mapping(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - struct btrfs_fs_info *info = root->fs_info; - struct btrfs_root *chunk_root = info->chunk_root; - struct btrfs_root *extent_root = info->extent_root; - struct btrfs_device *device; - struct btrfs_block_group_cache *cache; - struct btrfs_dev_extent *extent; - struct extent_buffer *leaf; - struct btrfs_chunk chunk; - struct btrfs_key key; - struct btrfs_path path; - u64 cur_start; - u64 total_bytes; - u64 chunk_objectid; - int ret; - - btrfs_init_path(&path); - - total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); - chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; - - BUG_ON(list_empty(&info->fs_devices->devices)); - device = list_entry(info->fs_devices->devices.next, - struct btrfs_device, dev_list); - BUG_ON(device->devid != info->fs_devices->latest_devid); - - /* delete device extent created by make_btrfs */ - key.objectid = device->devid; - key.offset = 0; - key.type = BTRFS_DEV_EXTENT_KEY; - ret = btrfs_search_slot(trans, device->dev_root, &key, &path, -1, 1); - if (ret < 0) - goto err; - - BUG_ON(ret > 0); - ret = btrfs_del_item(trans, device->dev_root, &path); - if (ret) - goto err; - btrfs_release_path(device->dev_root, &path); - - /* delete chunk item created by make_btrfs */ - key.objectid = chunk_objectid; - key.offset = 0; - key.type = BTRFS_CHUNK_ITEM_KEY; - ret = btrfs_search_slot(trans, chunk_root, &key, &path, -1, 1); - if (ret < 0) - goto err; - - BUG_ON(ret > 0); - ret = btrfs_del_item(trans, chunk_root, &path); - if (ret) - goto err; - btrfs_release_path(chunk_root, &path); - - /* for each block group, create device extent and chunk item */ - cur_start = 0; - while (cur_start < total_bytes) { - cache = btrfs_lookup_block_group(root->fs_info, cur_start); - BUG_ON(!cache); - - /* insert device extent */ - key.objectid = device->devid; - key.offset = cache->key.objectid; - key.type = BTRFS_DEV_EXTENT_KEY; - ret = btrfs_insert_empty_item(trans, device->dev_root, &path, - &key, sizeof(*extent)); - if (ret) - goto err; - - leaf = path.nodes[0]; - extent = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_dev_extent); - - btrfs_set_dev_extent_chunk_tree(leaf, extent, - chunk_root->root_key.objectid); - btrfs_set_dev_extent_chunk_objectid(leaf, extent, - chunk_objectid); - btrfs_set_dev_extent_chunk_offset(leaf, extent, - cache->key.objectid); - btrfs_set_dev_extent_length(leaf, extent, cache->key.offset); - write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, - (unsigned long)btrfs_dev_extent_chunk_tree_uuid(extent), - BTRFS_UUID_SIZE); - btrfs_mark_buffer_dirty(leaf); - btrfs_release_path(device->dev_root, &path); - - /* insert chunk item */ - btrfs_set_stack_chunk_length(&chunk, cache->key.offset); - btrfs_set_stack_chunk_owner(&chunk, - extent_root->root_key.objectid); - btrfs_set_stack_chunk_stripe_len(&chunk, STRIPE_LEN); - btrfs_set_stack_chunk_type(&chunk, cache->flags); - btrfs_set_stack_chunk_io_align(&chunk, device->io_align); - btrfs_set_stack_chunk_io_width(&chunk, device->io_width); - btrfs_set_stack_chunk_sector_size(&chunk, device->sector_size); - btrfs_set_stack_chunk_num_stripes(&chunk, 1); - btrfs_set_stack_chunk_sub_stripes(&chunk, 0); - btrfs_set_stack_stripe_devid(&chunk.stripe, device->devid); - btrfs_set_stack_stripe_offset(&chunk.stripe, - cache->key.objectid); - memcpy(&chunk.stripe.dev_uuid, device->uuid, BTRFS_UUID_SIZE); - - key.objectid = chunk_objectid; - key.offset = cache->key.objectid; - key.type = BTRFS_CHUNK_ITEM_KEY; - - ret = btrfs_insert_item(trans, chunk_root, &key, &chunk, - btrfs_chunk_item_size(1)); - if (ret) - goto err; - - cur_start = cache->key.objectid + cache->key.offset; - } - - device->bytes_used = total_bytes; - ret = btrfs_update_device(trans, device); -err: - btrfs_release_path(device->dev_root, &path); - return ret; -} - -static int create_subvol(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 root_objectid) -{ - struct extent_buffer *tmp; - struct btrfs_root *new_root; - struct btrfs_key key; - struct btrfs_root_item root_item; - int ret; - - ret = btrfs_copy_root(trans, root, root->node, &tmp, - root_objectid); - BUG_ON(ret); - - memcpy(&root_item, &root->root_item, sizeof(root_item)); - btrfs_set_root_bytenr(&root_item, tmp->start); - btrfs_set_root_level(&root_item, btrfs_header_level(tmp)); - btrfs_set_root_generation(&root_item, trans->transid); - free_extent_buffer(tmp); - - key.objectid = root_objectid; - key.type = BTRFS_ROOT_ITEM_KEY; - key.offset = trans->transid; - ret = btrfs_insert_root(trans, root->fs_info->tree_root, - &key, &root_item); - - key.offset = (u64)-1; - new_root = btrfs_read_fs_root(root->fs_info, &key); - BUG_ON(!new_root || IS_ERR(new_root)); - - ret = btrfs_make_root_dir(trans, new_root, BTRFS_FIRST_FREE_OBJECTID); - BUG_ON(ret); - - return 0; -} - -static int init_btrfs(struct btrfs_root *root) -{ - int ret; - struct btrfs_key location; - struct btrfs_trans_handle *trans; - struct btrfs_fs_info *fs_info = root->fs_info; - struct extent_buffer *tmp; - - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - ret = btrfs_make_block_groups(trans, root); - if (ret) - goto err; - ret = fixup_block_accounting(trans, root); - if (ret) - goto err; - ret = create_chunk_mapping(trans, root); - if (ret) - goto err; - ret = btrfs_make_root_dir(trans, fs_info->tree_root, - BTRFS_ROOT_TREE_DIR_OBJECTID); - if (ret) - goto err; - memcpy(&location, &root->root_key, sizeof(location)); - location.offset = (u64)-1; - ret = btrfs_insert_dir_item(trans, fs_info->tree_root, "default", 7, - btrfs_super_root_dir(&fs_info->super_copy), - &location, BTRFS_FT_DIR, 0); - if (ret) - goto err; - ret = btrfs_insert_inode_ref(trans, fs_info->tree_root, "default", 7, - location.objectid, - btrfs_super_root_dir(&fs_info->super_copy), 0); - if (ret) - goto err; - btrfs_set_root_dirid(&fs_info->fs_root->root_item, - BTRFS_FIRST_FREE_OBJECTID); - - /* subvol for ext2 image file */ - ret = create_subvol(trans, root, EXT2_IMAGE_SUBVOL_OBJECTID); - BUG_ON(ret); - /* subvol for data relocation */ - ret = create_subvol(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID); - BUG_ON(ret); - - ret = __btrfs_cow_block(trans, fs_info->csum_root, - fs_info->csum_root->node, NULL, 0, &tmp, 0, 0); - BUG_ON(ret); - free_extent_buffer(tmp); - - ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); -err: - return ret; -} - -/* - * Migrate super block to it's default position and zero 0 ~ 16k - */ -static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) -{ - int ret; - struct extent_buffer *buf; - struct btrfs_super_block *super; - u32 len; - u32 bytenr; - - BUG_ON(sectorsize < sizeof(*super)); - buf = malloc(sizeof(*buf) + sectorsize); - if (!buf) - return -ENOMEM; - - buf->len = sectorsize; - ret = pread(fd, buf->data, sectorsize, old_bytenr); - if (ret != sectorsize) - goto fail; - - super = (struct btrfs_super_block *)buf->data; - BUG_ON(btrfs_super_bytenr(super) != old_bytenr); - btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET); - - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET); - if (ret != sectorsize) - goto fail; - - ret = fsync(fd); - if (ret) - goto fail; - - memset(buf->data, 0, sectorsize); - for (bytenr = 0; bytenr < BTRFS_SUPER_INFO_OFFSET; ) { - len = BTRFS_SUPER_INFO_OFFSET - bytenr; - if (len > sectorsize) - len = sectorsize; - ret = pwrite(fd, buf->data, len, bytenr); - if (ret != len) { - fprintf(stderr, "unable to zero fill device\n"); - break; - } - bytenr += len; - } - ret = 0; - fsync(fd); -fail: - free(buf); - if (ret > 0) - ret = -1; - return ret; -} - -static int prepare_system_chunk_sb(struct btrfs_super_block *super) -{ - struct btrfs_chunk *chunk; - struct btrfs_disk_key *key; - u32 sectorsize = btrfs_super_sectorsize(super); - - key = (struct btrfs_disk_key *)(super->sys_chunk_array); - chunk = (struct btrfs_chunk *)(super->sys_chunk_array + - sizeof(struct btrfs_disk_key)); - - btrfs_set_disk_key_objectid(key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_disk_key_type(key, BTRFS_CHUNK_ITEM_KEY); - btrfs_set_disk_key_offset(key, 0); - - btrfs_set_stack_chunk_length(chunk, btrfs_super_total_bytes(super)); - btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_stack_chunk_stripe_len(chunk, 64 * 1024); - btrfs_set_stack_chunk_type(chunk, BTRFS_BLOCK_GROUP_SYSTEM); - btrfs_set_stack_chunk_io_align(chunk, sectorsize); - btrfs_set_stack_chunk_io_width(chunk, sectorsize); - btrfs_set_stack_chunk_sector_size(chunk, sectorsize); - btrfs_set_stack_chunk_num_stripes(chunk, 1); - btrfs_set_stack_chunk_sub_stripes(chunk, 0); - chunk->stripe.devid = super->dev_item.devid; - chunk->stripe.offset = cpu_to_le64(0); - memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid, BTRFS_UUID_SIZE); - btrfs_set_super_sys_array_size(super, sizeof(*key) + sizeof(*chunk)); - return 0; -} - -static int prepare_system_chunk(int fd, u64 sb_bytenr, u32 sectorsize) -{ - int ret; - struct extent_buffer *buf; - struct btrfs_super_block *super; - - BUG_ON(sectorsize < sizeof(*super)); - buf = malloc(sizeof(*buf) + sectorsize); - if (!buf) - return -ENOMEM; - - buf->len = sectorsize; - ret = pread(fd, buf->data, sectorsize, sb_bytenr); - if (ret != sectorsize) - goto fail; - - super = (struct btrfs_super_block *)buf->data; - BUG_ON(btrfs_super_bytenr(super) != sb_bytenr); - BUG_ON(btrfs_super_num_devices(super) != 1); - - ret = prepare_system_chunk_sb(super); - if (ret) - goto fail; - - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, sectorsize, sb_bytenr); - if (ret != sectorsize) - goto fail; - - ret = 0; -fail: - free(buf); - if (ret > 0) - ret = -1; - return ret; -} - -static int relocate_one_reference(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 extent_start, u64 extent_size, - struct btrfs_key *extent_key, - struct extent_io_tree *reloc_tree) -{ - struct extent_buffer *leaf; - struct btrfs_file_extent_item *fi; - struct btrfs_key key; - struct btrfs_path path; - struct btrfs_inode_item inode; - struct blk_iterate_data data; - u64 bytenr; - u64 num_bytes; - u64 cur_offset; - u64 new_pos; - u64 nbytes; - u64 sector_end; - u32 sectorsize = root->sectorsize; - unsigned long ptr; - int datacsum; - int fd; - int ret; - - btrfs_init_path(&path); - ret = btrfs_search_slot(trans, root, extent_key, &path, -1, 1); - if (ret) - goto fail; - - leaf = path.nodes[0]; - fi = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_file_extent_item); - BUG_ON(btrfs_file_extent_offset(leaf, fi) > 0); - if (extent_start != btrfs_file_extent_disk_bytenr(leaf, fi) || - extent_size != btrfs_file_extent_disk_num_bytes(leaf, fi)) { - ret = 1; - goto fail; - } - - bytenr = extent_start + btrfs_file_extent_offset(leaf, fi); - num_bytes = btrfs_file_extent_num_bytes(leaf, fi); - - ret = btrfs_del_item(trans, root, &path); - if (ret) - goto fail; - - ret = btrfs_free_extent(trans, root, extent_start, extent_size, 0, - root->root_key.objectid, - extent_key->objectid, extent_key->offset); - if (ret) - goto fail; - - btrfs_release_path(root, &path); - - key.objectid = extent_key->objectid; - key.offset = 0; - key.type = BTRFS_INODE_ITEM_KEY; - ret = btrfs_lookup_inode(trans, root, &path, &key, 0); - if (ret) - goto fail; - - leaf = path.nodes[0]; - ptr = btrfs_item_ptr_offset(leaf, path.slots[0]); - read_extent_buffer(leaf, &inode, ptr, sizeof(inode)); - btrfs_release_path(root, &path); - - BUG_ON(num_bytes & (sectorsize - 1)); - nbytes = btrfs_stack_inode_nbytes(&inode) - num_bytes; - btrfs_set_stack_inode_nbytes(&inode, nbytes); - datacsum = !(btrfs_stack_inode_flags(&inode) & BTRFS_INODE_NODATASUM); - - data = (struct blk_iterate_data) { - .trans = trans, - .root = root, - .inode = &inode, - .objectid = extent_key->objectid, - .first_block = extent_key->offset / sectorsize, - .disk_block = 0, - .num_blocks = 0, - .boundary = (u64)-1, - .checksum = datacsum, - .errcode = 0, - }; - - cur_offset = extent_key->offset; - while (num_bytes > 0) { - sector_end = bytenr + sectorsize - 1; - if (test_range_bit(reloc_tree, bytenr, sector_end, - EXTENT_LOCKED, 1)) { - ret = get_state_private(reloc_tree, bytenr, &new_pos); - BUG_ON(ret); - } else { - ret = custom_alloc_extent(root, sectorsize, 0, &key); - if (ret) - goto fail; - new_pos = key.objectid; - - if (cur_offset == extent_key->offset) { - fd = root->fs_info->fs_devices->latest_bdev; - readahead(fd, bytenr, num_bytes); - } - ret = copy_disk_extent(root, new_pos, bytenr, - sectorsize); - if (ret) - goto fail; - ret = set_extent_bits(reloc_tree, bytenr, sector_end, - EXTENT_LOCKED, GFP_NOFS); - BUG_ON(ret); - ret = set_state_private(reloc_tree, bytenr, new_pos); - BUG_ON(ret); - } - - ret = block_iterate_proc(NULL, new_pos / sectorsize, - cur_offset / sectorsize, &data); - if (ret & BLOCK_ABORT) { - ret = data.errcode; - goto fail; - } - - cur_offset += sectorsize; - bytenr += sectorsize; - num_bytes -= sectorsize; - } - - if (data.num_blocks > 0) { - ret = record_file_blocks(trans, root, - extent_key->objectid, &inode, - data.first_block, data.disk_block, - data.num_blocks, datacsum); - if (ret) - goto fail; - } - - key.objectid = extent_key->objectid; - key.offset = 0; - key.type = BTRFS_INODE_ITEM_KEY; - ret = btrfs_lookup_inode(trans, root, &path, &key, 1); - if (ret) - goto fail; - - leaf = path.nodes[0]; - ptr = btrfs_item_ptr_offset(leaf, path.slots[0]); - write_extent_buffer(leaf, &inode, ptr, sizeof(inode)); - btrfs_mark_buffer_dirty(leaf); - btrfs_release_path(root, &path); - -fail: - btrfs_release_path(root, &path); - return ret; -} - -static int relocate_extents_range(struct btrfs_root *fs_root, - struct btrfs_root *ext2_root, - u64 start_byte, u64 end_byte) -{ - struct btrfs_fs_info *info = fs_root->fs_info; - struct btrfs_root *extent_root = info->extent_root; - struct btrfs_root *cur_root = NULL; - struct btrfs_trans_handle *trans; - struct btrfs_extent_data_ref *dref; - struct btrfs_extent_inline_ref *iref; - struct btrfs_extent_item *ei; - struct extent_buffer *leaf; - struct btrfs_key key; - struct btrfs_key extent_key; - struct btrfs_path path; - struct extent_io_tree reloc_tree; - unsigned long ptr; - unsigned long end; - u64 cur_byte; - u64 num_bytes; - u64 ref_root; - u64 num_extents; - int pass = 0; - int ret; - - btrfs_init_path(&path); - extent_io_tree_init(&reloc_tree); - - key.objectid = start_byte; - key.offset = 0; - key.type = BTRFS_EXTENT_ITEM_KEY; - ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); - if (ret < 0) - goto fail; - if (ret > 0) { - ret = btrfs_previous_item(extent_root, &path, 0, - BTRFS_EXTENT_ITEM_KEY); - if (ret < 0) - goto fail; - if (ret == 0) { - leaf = path.nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.objectid + key.offset > start_byte) - start_byte = key.objectid; - } - } - btrfs_release_path(extent_root, &path); -again: - cur_root = (pass % 2 == 0) ? ext2_root : fs_root; - num_extents = 0; - - trans = btrfs_start_transaction(cur_root, 1); - BUG_ON(!trans); - - cur_byte = start_byte; - while (1) { - key.objectid = cur_byte; - key.offset = 0; - key.type = BTRFS_EXTENT_ITEM_KEY; - ret = btrfs_search_slot(trans, extent_root, - &key, &path, 0, 0); - if (ret < 0) - goto fail; -next: - leaf = path.nodes[0]; - if (path.slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(extent_root, &path); - if (ret < 0) - goto fail; - if (ret > 0) - break; - leaf = path.nodes[0]; - } - - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.objectid < cur_byte || - key.type != BTRFS_EXTENT_ITEM_KEY) { - path.slots[0]++; - goto next; - } - if (key.objectid >= end_byte) - break; - - num_extents++; - - cur_byte = key.objectid; - num_bytes = key.offset; - ei = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_extent_item); - BUG_ON(!(btrfs_extent_flags(leaf, ei) & - BTRFS_EXTENT_FLAG_DATA)); - - ptr = btrfs_item_ptr_offset(leaf, path.slots[0]); - end = ptr + btrfs_item_size_nr(leaf, path.slots[0]); - - ptr += sizeof(struct btrfs_extent_item); - - while (ptr < end) { - iref = (struct btrfs_extent_inline_ref *)ptr; - key.type = btrfs_extent_inline_ref_type(leaf, iref); - BUG_ON(key.type != BTRFS_EXTENT_DATA_REF_KEY); - dref = (struct btrfs_extent_data_ref *)(&iref->offset); - ref_root = btrfs_extent_data_ref_root(leaf, dref); - extent_key.objectid = - btrfs_extent_data_ref_objectid(leaf, dref); - extent_key.offset = - btrfs_extent_data_ref_offset(leaf, dref); - extent_key.type = BTRFS_EXTENT_DATA_KEY; - BUG_ON(btrfs_extent_data_ref_count(leaf, dref) != 1); - - if (ref_root == cur_root->root_key.objectid) - break; - - ptr += btrfs_extent_inline_ref_size(key.type); - } - - if (ptr >= end) { - path.slots[0]++; - goto next; - } - - ret = relocate_one_reference(trans, cur_root, cur_byte, - num_bytes, &extent_key, - &reloc_tree); - if (ret < 0) - goto fail; - - cur_byte += num_bytes; - btrfs_release_path(extent_root, &path); - - if (trans->blocks_used >= 4096) { - ret = btrfs_commit_transaction(trans, cur_root); - BUG_ON(ret); - trans = btrfs_start_transaction(cur_root, 1); - BUG_ON(!trans); - } - } - btrfs_release_path(cur_root, &path); - - ret = btrfs_commit_transaction(trans, cur_root); - BUG_ON(ret); - - if (num_extents > 0 && pass++ < 16) - goto again; - - ret = (num_extents > 0) ? -1 : 0; -fail: - btrfs_release_path(cur_root, &path); - extent_io_tree_cleanup(&reloc_tree); - return ret; -} - -/* - * relocate data in system chunk - */ -static int cleanup_sys_chunk(struct btrfs_root *fs_root, - struct btrfs_root *ext2_root) -{ - struct btrfs_block_group_cache *cache; - int i, ret = 0; - u64 offset = 0; - u64 end_byte; - - while(1) { - cache = btrfs_lookup_block_group(fs_root->fs_info, offset); - if (!cache) - break; - - end_byte = cache->key.objectid + cache->key.offset; - if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) { - ret = relocate_extents_range(fs_root, ext2_root, - cache->key.objectid, - end_byte); - if (ret) - goto fail; - } - offset = end_byte; - } - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - offset = btrfs_sb_offset(i); - offset &= ~((u64)STRIPE_LEN - 1); - - ret = relocate_extents_range(fs_root, ext2_root, - offset, offset + STRIPE_LEN); - if (ret) - goto fail; - } - ret = 0; -fail: - return ret; -} - -static int fixup_chunk_mapping(struct btrfs_root *root) -{ - struct btrfs_trans_handle *trans; - struct btrfs_fs_info *info = root->fs_info; - struct btrfs_root *chunk_root = info->chunk_root; - struct extent_buffer *leaf; - struct btrfs_key key; - struct btrfs_path path; - struct btrfs_chunk chunk; - unsigned long ptr; - u32 size; - u64 type; - int ret; - - btrfs_init_path(&path); - - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - - /* - * recow the whole chunk tree. this will move all chunk tree blocks - * into system block group. - */ - memset(&key, 0, sizeof(key)); - while (1) { - ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1); - if (ret < 0) - goto err; - - ret = btrfs_next_leaf(chunk_root, &path); - if (ret < 0) - goto err; - if (ret > 0) - break; - - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); - btrfs_release_path(chunk_root, &path); - } - btrfs_release_path(chunk_root, &path); - - /* fixup the system chunk array in super block */ - btrfs_set_super_sys_array_size(&info->super_copy, 0); - - key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; - key.offset = 0; - key.type = BTRFS_CHUNK_ITEM_KEY; - - ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 0); - if (ret < 0) - goto err; - BUG_ON(ret != 0); - while(1) { - leaf = path.nodes[0]; - if (path.slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(chunk_root, &path); - if (ret < 0) - goto err; - if (ret > 0) - break; - leaf = path.nodes[0]; - } - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.type != BTRFS_CHUNK_ITEM_KEY) - goto next; - - ptr = btrfs_item_ptr_offset(leaf, path.slots[0]); - size = btrfs_item_size_nr(leaf, path.slots[0]); - BUG_ON(size != sizeof(chunk)); - read_extent_buffer(leaf, &chunk, ptr, size); - type = btrfs_stack_chunk_type(&chunk); - - if (!(type & BTRFS_BLOCK_GROUP_SYSTEM)) - goto next; - - ret = btrfs_add_system_chunk(trans, chunk_root, &key, - &chunk, size); - if (ret) - goto err; -next: - path.slots[0]++; - } - - ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); -err: - btrfs_release_path(chunk_root, &path); - return ret; -} - -int do_convert(const char *devname, int datacsum, int packing, int noxattr) -{ - int i, fd, ret; - u32 blocksize; - u64 blocks[7]; - u64 total_bytes; - u64 super_bytenr; - ext2_filsys ext2_fs; - struct btrfs_root *root; - struct btrfs_root *ext2_root; - - ret = open_ext2fs(devname, &ext2_fs); - if (ret) { - fprintf(stderr, "unable to open the Ext2fs\n"); - goto fail; - } - blocksize = ext2_fs->blocksize; - total_bytes = (u64)ext2_fs->super->s_blocks_count * blocksize; - if (blocksize < 4096) { - fprintf(stderr, "block size is too small\n"); - goto fail; - } - if (!(ext2_fs->super->s_feature_incompat & - EXT2_FEATURE_INCOMPAT_FILETYPE)) { - fprintf(stderr, "filetype feature is missing\n"); - goto fail; - } - for (i = 0; i < 7; i++) { - ret = ext2_alloc_block(ext2_fs, 0, blocks + i); - if (ret) { - fprintf(stderr, "not enough free space\n"); - goto fail; - } - blocks[i] *= blocksize; - } - super_bytenr = blocks[0]; - fd = open(devname, O_RDWR); - if (fd < 0) { - fprintf(stderr, "unable to open %s\n", devname); - goto fail; - } - ret = make_btrfs(fd, devname, ext2_fs->super->s_volume_name, - blocks, total_bytes, blocksize, blocksize, - blocksize, blocksize); - if (ret) { - fprintf(stderr, "unable to create initial ctree\n"); - goto fail; - } - /* create a system chunk that maps the whole device */ - ret = prepare_system_chunk(fd, super_bytenr, blocksize); - if (ret) { - fprintf(stderr, "unable to update system chunk\n"); - goto fail; - } - root = open_ctree_fd(fd, devname, super_bytenr, O_RDWR); - if (!root) { - fprintf(stderr, "unable to open ctree\n"); - goto fail; - } - ret = cache_free_extents(root, ext2_fs); - if (ret) { - fprintf(stderr, "error during cache_free_extents %d\n", ret); - goto fail; - } - root->fs_info->extent_ops = &extent_ops; - /* recover block allocation bitmap */ - for (i = 0; i < 7; i++) { - blocks[i] /= blocksize; - ext2_free_block(ext2_fs, blocks[i]); - } - ret = init_btrfs(root); - if (ret) { - fprintf(stderr, "unable to setup the root tree\n"); - goto fail; - } - printf("creating btrfs metadata.\n"); - ret = copy_inodes(root, ext2_fs, datacsum, packing, noxattr); - if (ret) { - fprintf(stderr, "error during copy_inodes %d\n", ret); - goto fail; - } - printf("creating ext2fs image file.\n"); - ext2_root = link_subvol(root, "ext2_saved", EXT2_IMAGE_SUBVOL_OBJECTID); - if (!ext2_root) { - fprintf(stderr, "unable to create subvol\n"); - goto fail; - } - ret = create_ext2_image(ext2_root, ext2_fs, "image"); - if (ret) { - fprintf(stderr, "error during create_ext2_image %d\n", ret); - goto fail; - } - printf("cleaning up system chunk.\n"); - ret = cleanup_sys_chunk(root, ext2_root); - if (ret) { - fprintf(stderr, "error during cleanup_sys_chunk %d\n", ret); - goto fail; - } - ret = close_ctree(root); - if (ret) { - fprintf(stderr, "error during close_ctree %d\n", ret); - goto fail; - } - close_ext2fs(ext2_fs); - - /* - * If this step succeed, we get a mountable btrfs. Otherwise - * the ext2fs is left unchanged. - */ - ret = migrate_super_block(fd, super_bytenr, blocksize); - if (ret) { - fprintf(stderr, "unable to migrate super block\n"); - goto fail; - } - - root = open_ctree_fd(fd, devname, 0, O_RDWR); - if (!root) { - fprintf(stderr, "unable to open ctree\n"); - goto fail; - } - /* move chunk tree into system chunk. */ - ret = fixup_chunk_mapping(root); - if (ret) { - fprintf(stderr, "error during fixup_chunk_tree\n"); - goto fail; - } - ret = close_ctree(root); - close(fd); - - printf("conversion complete.\n"); - return 0; -fail: - fprintf(stderr, "conversion aborted.\n"); - return -1; -} - -static int may_rollback(struct btrfs_root *root) -{ - struct btrfs_fs_info *info = root->fs_info; - struct btrfs_multi_bio *multi = NULL; - u64 bytenr; - u64 length; - u64 physical; - u64 total_bytes; - int num_stripes; - int ret; - - if (btrfs_super_num_devices(&info->super_copy) != 1) - goto fail; - - bytenr = BTRFS_SUPER_INFO_OFFSET; - total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); - - while (1) { - ret = btrfs_map_block(&info->mapping_tree, WRITE, bytenr, - &length, &multi, 0); - if (ret) - goto fail; - - num_stripes = multi->num_stripes; - physical = multi->stripes[0].physical; - kfree(multi); - - if (num_stripes != 1 || physical != bytenr) - goto fail; - - bytenr += length; - if (bytenr >= total_bytes) - break; - } - return 0; -fail: - return -1; -} - -int do_rollback(const char *devname, int force) -{ - int fd; - int ret; - int i; - struct btrfs_root *root; - struct btrfs_root *ext2_root; - struct btrfs_root *chunk_root; - struct btrfs_dir_item *dir; - struct btrfs_inode_item *inode; - struct btrfs_file_extent_item *fi; - struct btrfs_trans_handle *trans; - struct extent_buffer *leaf; - struct btrfs_block_group_cache *cache1; - struct btrfs_block_group_cache *cache2; - struct btrfs_key key; - struct btrfs_path path; - struct extent_io_tree io_tree; - char *buf; - char *name; - u64 bytenr; - u64 num_bytes; - u64 root_dir; - u64 objectid; - u64 offset; - u64 start; - u64 end; - u64 sb_bytenr; - u64 first_free; - u64 total_bytes; - u32 sectorsize; - - extent_io_tree_init(&io_tree); - - fd = open(devname, O_RDWR); - if (fd < 0) { - fprintf(stderr, "unable to open %s\n", devname); - goto fail; - } - root = open_ctree_fd(fd, devname, 0, O_RDWR); - if (!root) { - fprintf(stderr, "unable to open ctree\n"); - goto fail; - } - ret = may_rollback(root); - if (ret < 0) { - fprintf(stderr, "unable to do rollback\n"); - goto fail; - } - - sectorsize = root->sectorsize; - buf = malloc(sectorsize); - if (!buf) { - fprintf(stderr, "unable to allocate memory\n"); - goto fail; - } - - btrfs_init_path(&path); - - key.objectid = EXT2_IMAGE_SUBVOL_OBJECTID; - key.type = BTRFS_ROOT_ITEM_KEY; - key.offset = (u64)-1; - ext2_root = btrfs_read_fs_root(root->fs_info, &key); - if (!ext2_root || IS_ERR(ext2_root)) { - fprintf(stderr, "unable to open subvol %llu\n", - key.objectid); - goto fail; - } - - name = "image"; - root_dir = btrfs_root_dirid(&root->root_item); - dir = btrfs_lookup_dir_item(NULL, ext2_root, &path, - root_dir, name, strlen(name), 0); - if (!dir || IS_ERR(dir)) { - fprintf(stderr, "unable to find file %s\n", name); - goto fail; - } - leaf = path.nodes[0]; - btrfs_dir_item_key_to_cpu(leaf, dir, &key); - btrfs_release_path(ext2_root, &path); - - objectid = key.objectid; - - ret = btrfs_lookup_inode(NULL, ext2_root, &path, &key, 0); - if (ret) { - fprintf(stderr, "unable to find inode item\n"); - goto fail; - } - leaf = path.nodes[0]; - inode = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item); - total_bytes = btrfs_inode_size(leaf, inode); - btrfs_release_path(ext2_root, &path); - - key.objectid = objectid; - key.offset = 0; - btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); - ret = btrfs_search_slot(NULL, ext2_root, &key, &path, 0, 0); - if (ret != 0) { - fprintf(stderr, "unable to find first file extent\n"); - btrfs_release_path(ext2_root, &path); - goto fail; - } - - /* build mapping tree for the relocated blocks */ - for (offset = 0; offset < total_bytes; ) { - leaf = path.nodes[0]; - if (path.slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, &path); - if (ret != 0) - break; - continue; - } - - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.objectid != objectid || key.offset != offset || - btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) - break; - - fi = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_file_extent_item); - if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) - break; - if (btrfs_file_extent_compression(leaf, fi) || - btrfs_file_extent_encryption(leaf, fi) || - btrfs_file_extent_other_encoding(leaf, fi)) - break; - - bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); - /* skip holes and direct mapped extents */ - if (bytenr == 0 || bytenr == offset) - goto next_extent; - - bytenr += btrfs_file_extent_offset(leaf, fi); - num_bytes = btrfs_file_extent_num_bytes(leaf, fi); - - cache1 = btrfs_lookup_block_group(root->fs_info, offset); - cache2 = btrfs_lookup_block_group(root->fs_info, - offset + num_bytes - 1); - if (!cache1 || cache1 != cache2 || - (!(cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM) && - !intersect_with_sb(offset, num_bytes))) - break; - - set_extent_bits(&io_tree, offset, offset + num_bytes - 1, - EXTENT_LOCKED, GFP_NOFS); - set_state_private(&io_tree, offset, bytenr); -next_extent: - offset += btrfs_file_extent_num_bytes(leaf, fi); - path.slots[0]++; - } - btrfs_release_path(ext2_root, &path); - - if (offset < total_bytes) { - fprintf(stderr, "unable to build extent mapping\n"); - goto fail; - } - - first_free = BTRFS_SUPER_INFO_OFFSET + 2 * sectorsize - 1; - first_free &= ~((u64)sectorsize - 1); - /* backup for extent #0 should exist */ - if(!test_range_bit(&io_tree, 0, first_free - 1, EXTENT_LOCKED, 1)) { - fprintf(stderr, "no backup for the first extent\n"); - goto fail; - } - /* force no allocation from system block group */ - root->fs_info->system_allocs = -1; - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - /* - * recow the whole chunk tree, this will remove all chunk tree blocks - * from system block group - */ - chunk_root = root->fs_info->chunk_root; - memset(&key, 0, sizeof(key)); - while (1) { - ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1); - if (ret < 0) - break; - - ret = btrfs_next_leaf(chunk_root, &path); - if (ret) - break; - - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); - btrfs_release_path(chunk_root, &path); - } - btrfs_release_path(chunk_root, &path); - - offset = 0; - num_bytes = 0; - while(1) { - cache1 = btrfs_lookup_block_group(root->fs_info, offset); - if (!cache1) - break; - - if (cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM) - num_bytes += btrfs_block_group_used(&cache1->item); - - offset = cache1->key.objectid + cache1->key.offset; - } - /* only extent #0 left in system block group? */ - if (num_bytes > first_free) { - fprintf(stderr, "unable to empty system block group\n"); - goto fail; - } - /* create a system chunk that maps the whole device */ - ret = prepare_system_chunk_sb(&root->fs_info->super_copy); - if (ret) { - fprintf(stderr, "unable to update system chunk\n"); - goto fail; - } - - ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); - - ret = close_ctree(root); - if (ret) { - fprintf(stderr, "error during close_ctree %d\n", ret); - goto fail; - } - - /* zero btrfs super block mirrors */ - memset(buf, 0, sectorsize); - for (i = 1 ; i < BTRFS_SUPER_MIRROR_MAX; i++) { - bytenr = btrfs_sb_offset(i); - if (bytenr >= total_bytes) - break; - ret = pwrite(fd, buf, sectorsize, bytenr); - } - - sb_bytenr = (u64)-1; - /* copy all relocated blocks back */ - while(1) { - ret = find_first_extent_bit(&io_tree, 0, &start, &end, - EXTENT_LOCKED); - if (ret) - break; - - ret = get_state_private(&io_tree, start, &bytenr); - BUG_ON(ret); - - clear_extent_bits(&io_tree, start, end, EXTENT_LOCKED, - GFP_NOFS); - - while (start <= end) { - if (start == BTRFS_SUPER_INFO_OFFSET) { - sb_bytenr = bytenr; - goto next_sector; - } - ret = pread(fd, buf, sectorsize, bytenr); - if (ret < 0) { - fprintf(stderr, "error during pread %d\n", ret); - goto fail; - } - BUG_ON(ret != sectorsize); - ret = pwrite(fd, buf, sectorsize, start); - if (ret < 0) { - fprintf(stderr, "error during pwrite %d\n", ret); - goto fail; - } - BUG_ON(ret != sectorsize); -next_sector: - start += sectorsize; - bytenr += sectorsize; - } - } - - ret = fsync(fd); - if (ret) { - fprintf(stderr, "error during fsync %d\n", ret); - goto fail; - } - /* - * finally, overwrite btrfs super block. - */ - ret = pread(fd, buf, sectorsize, sb_bytenr); - if (ret < 0) { - fprintf(stderr, "error during pread %d\n", ret); - goto fail; - } - BUG_ON(ret != sectorsize); - ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET); - if (ret < 0) { - fprintf(stderr, "error during pwrite %d\n", ret); - goto fail; - } - BUG_ON(ret != sectorsize); - ret = fsync(fd); - if (ret) { - fprintf(stderr, "error during fsync %d\n", ret); - goto fail; - } - - close(fd); - free(buf); - extent_io_tree_cleanup(&io_tree); - printf("rollback complete.\n"); - return 0; -fail: - fprintf(stderr, "rollback aborted.\n"); - return -1; -} - -static void print_usage(void) -{ - printf("usage: btrfs-convert [-d] [-i] [-n] [-r] device\n"); - printf("\t-d disable data checksum\n"); - printf("\t-i ignore xattrs and ACLs\n"); - printf("\t-n disable packing of small files\n"); - printf("\t-r roll back to ext2fs\n"); -} - -int main(int argc, char *argv[]) -{ - int ret; - int packing = 1; - int noxattr = 0; - int datacsum = 1; - int rollback = 0; - char *file; - while(1) { - int c = getopt(argc, argv, "dinr"); - if (c < 0) - break; - switch(c) { - case 'd': - datacsum = 0; - break; - case 'i': - noxattr = 1; - break; - case 'n': - packing = 0; - break; - case 'r': - rollback = 1; - break; - default: - print_usage(); - return 1; - } - } - argc = argc - optind; - if (argc != 1) { - print_usage(); - return 1; - } - - file = argv[optind]; - if (check_mounted(file)) { - fprintf(stderr, "%s is mounted\n", file); - return 1; - } - - if (rollback) { - ret = do_rollback(file, 0); - } else { - ret = do_convert(file, datacsum, packing, noxattr); - } - if (ret) - return 1; - return 0; -} diff --git a/crc32c.c b/crc32c.c deleted file mode 100644 index 4dc5f9c..0000000 --- a/crc32c.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copied from the kernel source code, lib/libcrc32c.c. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - */ -#include "kerncompat.h" -#include "crc32c.h" - -/* - * This is the CRC-32C table - * Generated with: - * width = 32 bits - * poly = 0x1EDC6F41 - * reflect input bytes = true - * reflect output bytes = true - */ - -static const u32 crc32c_table[256] = { - 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, - 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, - 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, - 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, - 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, - 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, - 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, - 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, - 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, - 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, - 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, - 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, - 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, - 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, - 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, - 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, - 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, - 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, - 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, - 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, - 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, - 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, - 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, - 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, - 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, - 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, - 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, - 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, - 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, - 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, - 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, - 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, - 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, - 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, - 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, - 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, - 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, - 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, - 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, - 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, - 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, - 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, - 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, - 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, - 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, - 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, - 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, - 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, - 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, - 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, - 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, - 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, - 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, - 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, - 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, - 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, - 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, - 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, - 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, - 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, - 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, - 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, - 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, - 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L -}; - -/* - * Steps through buffer one byte at at time, calculates reflected - * crc using table. - */ - -u32 crc32c_le(u32 crc, unsigned char const *data, size_t length) -{ - while (length--) - crc = - crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8); - - return crc; -} diff --git a/crc32c.h b/crc32c.h deleted file mode 100644 index 72f1512..0000000 --- a/crc32c.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2007 Red Hat. 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. - */ - -#ifndef __CRC32C__ -#define __CRC32C__ - -#include "kerncompat.h" - -u32 crc32c_le(u32 seed, unsigned char const *data, size_t length); - -#define crc32c(seed, data, length) crc32c_le(seed, (unsigned char const *)data, length) -#define btrfs_crc32c crc32c -#endif diff --git a/ctree.c b/ctree.c deleted file mode 100644 index f70e10c..0000000 --- a/ctree.c +++ /dev/null @@ -1,2975 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 "ctree.h" -#include "disk-io.h" -#include "transaction.h" -#include "print-tree.h" - -static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int level); -static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *ins_key, - struct btrfs_path *path, int data_size, int extend); -static int push_node_left(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct extent_buffer *dst, - struct extent_buffer *src, int empty); -static int balance_node_right(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *dst_buf, - struct extent_buffer *src_buf); -static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct btrfs_path *path, int level, int slot); - -inline void btrfs_init_path(struct btrfs_path *p) -{ - memset(p, 0, sizeof(*p)); -} - -struct btrfs_path *btrfs_alloc_path(void) -{ - struct btrfs_path *path; - path = kmalloc(sizeof(struct btrfs_path), GFP_NOFS); - if (path) { - btrfs_init_path(path); - path->reada = 0; - } - return path; -} - -void btrfs_free_path(struct btrfs_path *p) -{ - btrfs_release_path(NULL, p); - kfree(p); -} - -void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p) -{ - int i; - for (i = 0; i < BTRFS_MAX_LEVEL; i++) { - if (!p->nodes[i]) - continue; - free_extent_buffer(p->nodes[i]); - } - memset(p, 0, sizeof(*p)); -} - -static void add_root_to_dirty_list(struct btrfs_root *root) -{ - if (root->track_dirty && list_empty(&root->dirty_list)) { - list_add(&root->dirty_list, - &root->fs_info->dirty_cowonly_roots); - } -} - -int btrfs_copy_root(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *buf, - struct extent_buffer **cow_ret, u64 new_root_objectid) -{ - struct extent_buffer *cow; - int ret = 0; - int level; - struct btrfs_root *new_root; - struct btrfs_disk_key disk_key; - - new_root = kmalloc(sizeof(*new_root), GFP_NOFS); - if (!new_root) - return -ENOMEM; - - memcpy(new_root, root, sizeof(*new_root)); - new_root->root_key.objectid = new_root_objectid; - - WARN_ON(root->ref_cows && trans->transid != - root->fs_info->running_transaction->transid); - WARN_ON(root->ref_cows && trans->transid != root->last_trans); - - level = btrfs_header_level(buf); - if (level == 0) - btrfs_item_key(buf, &disk_key, 0); - else - btrfs_node_key(buf, &disk_key, 0); - cow = btrfs_alloc_free_block(trans, new_root, buf->len, - new_root_objectid, &disk_key, - level, buf->start, 0); - if (IS_ERR(cow)) { - kfree(new_root); - return PTR_ERR(cow); - } - - copy_extent_buffer(cow, buf, 0, 0, cow->len); - btrfs_set_header_bytenr(cow, cow->start); - btrfs_set_header_generation(cow, trans->transid); - btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV); - btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN | - BTRFS_HEADER_FLAG_RELOC); - if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID) - btrfs_set_header_flag(cow, BTRFS_HEADER_FLAG_RELOC); - else - btrfs_set_header_owner(cow, new_root_objectid); - - write_extent_buffer(cow, root->fs_info->fsid, - (unsigned long)btrfs_header_fsid(cow), - BTRFS_FSID_SIZE); - - WARN_ON(btrfs_header_generation(buf) > trans->transid); - ret = btrfs_inc_ref(trans, new_root, cow, 0); - kfree(new_root); - - if (ret) - return ret; - - btrfs_mark_buffer_dirty(cow); - *cow_ret = cow; - return 0; -} - -/* - * check if the tree block can be shared by multiple trees - */ -int btrfs_block_can_be_shared(struct btrfs_root *root, - struct extent_buffer *buf) -{ - /* - * Tree blocks not in refernece counted trees and tree roots - * are never shared. If a block was allocated after the last - * snapshot and the block was not allocated by tree relocation, - * we know the block is not shared. - */ - if (root->ref_cows && - buf != root->node && buf != root->commit_root && - (btrfs_header_generation(buf) <= - btrfs_root_last_snapshot(&root->root_item) || - btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC))) - return 1; -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - if (root->ref_cows && - btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) - return 1; -#endif - return 0; -} - -static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *buf, - struct extent_buffer *cow) -{ - u64 refs; - u64 owner; - u64 flags; - u64 new_flags = 0; - int ret; - - /* - * Backrefs update rules: - * - * Always use full backrefs for extent pointers in tree block - * allocated by tree relocation. - * - * If a shared tree block is no longer referenced by its owner - * tree (btrfs_header_owner(buf) == root->root_key.objectid), - * use full backrefs for extent pointers in tree block. - * - * If a tree block is been relocating - * (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID), - * use full backrefs for extent pointers in tree block. - * The reason for this is some operations (such as drop tree) - * are only allowed for blocks use full backrefs. - */ - - if (btrfs_block_can_be_shared(root, buf)) { - ret = btrfs_lookup_extent_info(trans, root, buf->start, - buf->len, &refs, &flags); - BUG_ON(ret); - BUG_ON(refs == 0); - } else { - refs = 1; - if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID || - btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) - flags = BTRFS_BLOCK_FLAG_FULL_BACKREF; - else - flags = 0; - } - - owner = btrfs_header_owner(buf); - BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) && - owner == BTRFS_TREE_RELOC_OBJECTID); - - if (refs > 1) { - if ((owner == root->root_key.objectid || - root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) && - !(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) { - ret = btrfs_inc_ref(trans, root, buf, 1); - BUG_ON(ret); - - if (root->root_key.objectid == - BTRFS_TREE_RELOC_OBJECTID) { - ret = btrfs_dec_ref(trans, root, buf, 0); - BUG_ON(ret); - ret = btrfs_inc_ref(trans, root, cow, 1); - BUG_ON(ret); - } - new_flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; - } else { - - if (root->root_key.objectid == - BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1); - else - ret = btrfs_inc_ref(trans, root, cow, 0); - BUG_ON(ret); - } - if (new_flags != 0) { - ret = btrfs_set_block_flags(trans, root, buf->start, - buf->len, new_flags); - BUG_ON(ret); - } - } else { - if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { - if (root->root_key.objectid == - BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1); - else - ret = btrfs_inc_ref(trans, root, cow, 0); - BUG_ON(ret); - ret = btrfs_dec_ref(trans, root, buf, 1); - BUG_ON(ret); - } - clean_tree_block(trans, root, buf); - } - return 0; -} - -int __btrfs_cow_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *buf, - struct extent_buffer *parent, int parent_slot, - struct extent_buffer **cow_ret, - u64 search_start, u64 empty_size) -{ - u64 generation; - struct extent_buffer *cow; - struct btrfs_disk_key disk_key; - int level; - - WARN_ON(root->ref_cows && trans->transid != - root->fs_info->running_transaction->transid); - WARN_ON(root->ref_cows && trans->transid != root->last_trans); - - level = btrfs_header_level(buf); - generation = btrfs_header_generation(buf); - - if (level == 0) - btrfs_item_key(buf, &disk_key, 0); - else - btrfs_node_key(buf, &disk_key, 0); - - cow = btrfs_alloc_free_block(trans, root, buf->len, - root->root_key.objectid, &disk_key, - level, search_start, empty_size); - if (IS_ERR(cow)) - return PTR_ERR(cow); - - copy_extent_buffer(cow, buf, 0, 0, cow->len); - btrfs_set_header_bytenr(cow, cow->start); - btrfs_set_header_generation(cow, trans->transid); - btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV); - btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN | - BTRFS_HEADER_FLAG_RELOC); - if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) - btrfs_set_header_flag(cow, BTRFS_HEADER_FLAG_RELOC); - else - btrfs_set_header_owner(cow, root->root_key.objectid); - - write_extent_buffer(cow, root->fs_info->fsid, - (unsigned long)btrfs_header_fsid(cow), - BTRFS_FSID_SIZE); - - WARN_ON(btrfs_header_generation(buf) > trans->transid); - - update_ref_for_cow(trans, root, buf, cow); - - if (buf == root->node) { - root->node = cow; - extent_buffer_get(cow); - - btrfs_free_extent(trans, root, buf->start, buf->len, - 0, root->root_key.objectid, level, 0); - free_extent_buffer(buf); - add_root_to_dirty_list(root); - } else { - btrfs_set_node_blockptr(parent, parent_slot, - cow->start); - WARN_ON(trans->transid == 0); - btrfs_set_node_ptr_generation(parent, parent_slot, - trans->transid); - btrfs_mark_buffer_dirty(parent); - WARN_ON(btrfs_header_generation(parent) != trans->transid); - - btrfs_free_extent(trans, root, buf->start, buf->len, - 0, root->root_key.objectid, level, 1); - } - free_extent_buffer(buf); - btrfs_mark_buffer_dirty(cow); - *cow_ret = cow; - return 0; -} - -static inline int should_cow_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *buf) -{ - if (btrfs_header_generation(buf) == trans->transid && - !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN) && - !(root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID && - btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC))) - return 0; - return 1; -} - -int btrfs_cow_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct extent_buffer *buf, - struct extent_buffer *parent, int parent_slot, - struct extent_buffer **cow_ret) -{ - u64 search_start; - int ret; - /* - if (trans->transaction != root->fs_info->running_transaction) { - printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid, - root->fs_info->running_transaction->transid); - WARN_ON(1); - } - */ - if (trans->transid != root->fs_info->generation) { - printk(KERN_CRIT "trans %llu running %llu\n", - (unsigned long long)trans->transid, - (unsigned long long)root->fs_info->generation); - WARN_ON(1); - } - if (!should_cow_block(trans, root, buf)) { - *cow_ret = buf; - return 0; - } - - search_start = buf->start & ~((u64)(1024 * 1024 * 1024) - 1); - ret = __btrfs_cow_block(trans, root, buf, parent, - parent_slot, cow_ret, search_start, 0); - return ret; -} - -/* -static int close_blocks(u64 blocknr, u64 other, u32 blocksize) -{ - if (blocknr < other && other - (blocknr + blocksize) < 32768) - return 1; - if (blocknr > other && blocknr - (other + blocksize) < 32768) - return 1; - return 0; -} -*/ - -/* - * compare two keys in a memcmp fashion - */ -int btrfs_comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2) -{ - struct btrfs_key k1; - - btrfs_disk_key_to_cpu(&k1, disk); - - if (k1.objectid > k2->objectid) - return 1; - if (k1.objectid < k2->objectid) - return -1; - if (k1.type > k2->type) - return 1; - if (k1.type < k2->type) - return -1; - if (k1.offset > k2->offset) - return 1; - if (k1.offset < k2->offset) - return -1; - return 0; -} - - -#if 0 -int btrfs_realloc_node(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct extent_buffer *parent, - int start_slot, int cache_only, u64 *last_ret, - struct btrfs_key *progress) -{ - struct extent_buffer *cur; - struct extent_buffer *tmp; - u64 blocknr; - u64 gen; - u64 search_start = *last_ret; - u64 last_block = 0; - u64 other; - u32 parent_nritems; - int end_slot; - int i; - int err = 0; - int parent_level; - int uptodate; - u32 blocksize; - int progress_passed = 0; - struct btrfs_disk_key disk_key; - - parent_level = btrfs_header_level(parent); - if (cache_only && parent_level != 1) - return 0; - - if (trans->transaction != root->fs_info->running_transaction) { - printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid, - root->fs_info->running_transaction->transid); - WARN_ON(1); - } - if (trans->transid != root->fs_info->generation) { - printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid, - root->fs_info->generation); - WARN_ON(1); - } - - parent_nritems = btrfs_header_nritems(parent); - blocksize = btrfs_level_size(root, parent_level - 1); - end_slot = parent_nritems; - - if (parent_nritems == 1) - return 0; - - for (i = start_slot; i < end_slot; i++) { - int close = 1; - - if (!parent->map_token) { - map_extent_buffer(parent, - btrfs_node_key_ptr_offset(i), - sizeof(struct btrfs_key_ptr), - &parent->map_token, &parent->kaddr, - &parent->map_start, &parent->map_len, - KM_USER1); - } - btrfs_node_key(parent, &disk_key, i); - if (!progress_passed && comp_keys(&disk_key, progress) < 0) - continue; - - progress_passed = 1; - blocknr = btrfs_node_blockptr(parent, i); - gen = btrfs_node_ptr_generation(parent, i); - if (last_block == 0) - last_block = blocknr; - - if (i > 0) { - other = btrfs_node_blockptr(parent, i - 1); - close = close_blocks(blocknr, other, blocksize); - } - if (close && i < end_slot - 2) { - other = btrfs_node_blockptr(parent, i + 1); - close = close_blocks(blocknr, other, blocksize); - } - if (close) { - last_block = blocknr; - continue; - } - if (parent->map_token) { - unmap_extent_buffer(parent, parent->map_token, - KM_USER1); - parent->map_token = NULL; - } - - cur = btrfs_find_tree_block(root, blocknr, blocksize); - if (cur) - uptodate = btrfs_buffer_uptodate(cur, gen); - else - uptodate = 0; - if (!cur || !uptodate) { - if (cache_only) { - free_extent_buffer(cur); - continue; - } - if (!cur) { - cur = read_tree_block(root, blocknr, - blocksize, gen); - } else if (!uptodate) { - btrfs_read_buffer(cur, gen); - } - } - if (search_start == 0) - search_start = last_block; - - err = __btrfs_cow_block(trans, root, cur, parent, i, - &tmp, search_start, - min(16 * blocksize, - (end_slot - i) * blocksize)); - if (err) { - free_extent_buffer(cur); - break; - } - search_start = tmp->start; - last_block = tmp->start; - *last_ret = search_start; - if (parent_level == 1) - btrfs_clear_buffer_defrag(tmp); - free_extent_buffer(tmp); - } - if (parent->map_token) { - unmap_extent_buffer(parent, parent->map_token, - KM_USER1); - parent->map_token = NULL; - } - return err; -} -#endif - -/* - * The leaf data grows from end-to-front in the node. - * this returns the address of the start of the last item, - * which is the stop of the leaf data stack - */ -static inline unsigned int leaf_data_end(struct btrfs_root *root, - struct extent_buffer *leaf) -{ - u32 nr = btrfs_header_nritems(leaf); - if (nr == 0) - return BTRFS_LEAF_DATA_SIZE(root); - return btrfs_item_offset_nr(leaf, nr - 1); -} - -static int check_node(struct btrfs_root *root, struct btrfs_path *path, - int level) -{ - struct extent_buffer *parent = NULL; - struct extent_buffer *node = path->nodes[level]; - struct btrfs_disk_key parent_key; - struct btrfs_disk_key node_key; - int parent_slot; - int slot; - struct btrfs_key cpukey; - u32 nritems = btrfs_header_nritems(node); - - if (path->nodes[level + 1]) - parent = path->nodes[level + 1]; - - slot = path->slots[level]; - BUG_ON(nritems == 0); - if (parent) { - parent_slot = path->slots[level + 1]; - btrfs_node_key(parent, &parent_key, parent_slot); - btrfs_node_key(node, &node_key, 0); - BUG_ON(memcmp(&parent_key, &node_key, - sizeof(struct btrfs_disk_key))); - BUG_ON(btrfs_node_blockptr(parent, parent_slot) != - btrfs_header_bytenr(node)); - } - BUG_ON(nritems > BTRFS_NODEPTRS_PER_BLOCK(root)); - if (slot != 0) { - btrfs_node_key_to_cpu(node, &cpukey, slot - 1); - btrfs_node_key(node, &node_key, slot); - BUG_ON(btrfs_comp_keys(&node_key, &cpukey) <= 0); - } - if (slot < nritems - 1) { - btrfs_node_key_to_cpu(node, &cpukey, slot + 1); - btrfs_node_key(node, &node_key, slot); - BUG_ON(btrfs_comp_keys(&node_key, &cpukey) >= 0); - } - return 0; -} - -static int check_leaf(struct btrfs_root *root, struct btrfs_path *path, - int level) -{ - struct extent_buffer *leaf = path->nodes[level]; - struct extent_buffer *parent = NULL; - int parent_slot; - struct btrfs_key cpukey; - struct btrfs_disk_key parent_key; - struct btrfs_disk_key leaf_key; - int slot = path->slots[0]; - - u32 nritems = btrfs_header_nritems(leaf); - - if (path->nodes[level + 1]) - parent = path->nodes[level + 1]; - - if (nritems == 0) - return 0; - - if (parent) { - parent_slot = path->slots[level + 1]; - btrfs_node_key(parent, &parent_key, parent_slot); - btrfs_item_key(leaf, &leaf_key, 0); - - BUG_ON(memcmp(&parent_key, &leaf_key, - sizeof(struct btrfs_disk_key))); - BUG_ON(btrfs_node_blockptr(parent, parent_slot) != - btrfs_header_bytenr(leaf)); - } -#if 0 - for (i = 0; nritems > 1 && i < nritems - 2; i++) { - btrfs_item_key_to_cpu(leaf, &cpukey, i + 1); - btrfs_item_key(leaf, &leaf_key, i); - if (comp_keys(&leaf_key, &cpukey) >= 0) { - btrfs_print_leaf(root, leaf); - printk("slot %d offset bad key\n", i); - BUG_ON(1); - } - if (btrfs_item_offset_nr(leaf, i) != - btrfs_item_end_nr(leaf, i + 1)) { - btrfs_print_leaf(root, leaf); - printk("slot %d offset bad\n", i); - BUG_ON(1); - } - if (i == 0) { - if (btrfs_item_offset_nr(leaf, i) + - btrfs_item_size_nr(leaf, i) != - BTRFS_LEAF_DATA_SIZE(root)) { - btrfs_print_leaf(root, leaf); - printk("slot %d first offset bad\n", i); - BUG_ON(1); - } - } - } - if (nritems > 0) { - if (btrfs_item_size_nr(leaf, nritems - 1) > 4096) { - btrfs_print_leaf(root, leaf); - printk("slot %d bad size \n", nritems - 1); - BUG_ON(1); - } - } -#endif - if (slot != 0 && slot < nritems - 1) { - btrfs_item_key(leaf, &leaf_key, slot); - btrfs_item_key_to_cpu(leaf, &cpukey, slot - 1); - if (btrfs_comp_keys(&leaf_key, &cpukey) <= 0) { - btrfs_print_leaf(root, leaf); - printk("slot %d offset bad key\n", slot); - BUG_ON(1); - } - if (btrfs_item_offset_nr(leaf, slot - 1) != - btrfs_item_end_nr(leaf, slot)) { - btrfs_print_leaf(root, leaf); - printk("slot %d offset bad\n", slot); - BUG_ON(1); - } - } - if (slot < nritems - 1) { - btrfs_item_key(leaf, &leaf_key, slot); - btrfs_item_key_to_cpu(leaf, &cpukey, slot + 1); - BUG_ON(btrfs_comp_keys(&leaf_key, &cpukey) >= 0); - if (btrfs_item_offset_nr(leaf, slot) != - btrfs_item_end_nr(leaf, slot + 1)) { - btrfs_print_leaf(root, leaf); - printk("slot %d offset bad\n", slot); - BUG_ON(1); - } - } - BUG_ON(btrfs_item_offset_nr(leaf, 0) + - btrfs_item_size_nr(leaf, 0) != BTRFS_LEAF_DATA_SIZE(root)); - return 0; -} - -static int noinline check_block(struct btrfs_root *root, - struct btrfs_path *path, int level) -{ - return 0; -#if 0 - struct extent_buffer *buf = path->nodes[level]; - - if (memcmp_extent_buffer(buf, root->fs_info->fsid, - (unsigned long)btrfs_header_fsid(buf), - BTRFS_FSID_SIZE)) { - printk("warning bad block %Lu\n", buf->start); - return 1; - } -#endif - if (level == 0) - return check_leaf(root, path, level); - return check_node(root, path, level); -} - -/* - * search for key in the extent_buffer. The items start at offset p, - * and they are item_size apart. There are 'max' items in p. - * - * the slot in the array is returned via slot, and it points to - * the place where you would insert key if it is not found in - * the array. - * - * slot may point to max if the key is bigger than all of the keys - */ -static int generic_bin_search(struct extent_buffer *eb, unsigned long p, - int item_size, struct btrfs_key *key, - int max, int *slot) -{ - int low = 0; - int high = max; - int mid; - int ret; - unsigned long offset; - struct btrfs_disk_key *tmp; - - while(low < high) { - mid = (low + high) / 2; - offset = p + mid * item_size; - - tmp = (struct btrfs_disk_key *)(eb->data + offset); - ret = btrfs_comp_keys(tmp, key); - - if (ret < 0) - low = mid + 1; - else if (ret > 0) - high = mid; - else { - *slot = mid; - return 0; - } - } - *slot = low; - return 1; -} - -/* - * simple bin_search frontend that does the right thing for - * leaves vs nodes - */ -static int bin_search(struct extent_buffer *eb, struct btrfs_key *key, - int level, int *slot) -{ - if (level == 0) { - return generic_bin_search(eb, - offsetof(struct btrfs_leaf, items), - sizeof(struct btrfs_item), - key, btrfs_header_nritems(eb), - slot); - } else { - return generic_bin_search(eb, - offsetof(struct btrfs_node, ptrs), - sizeof(struct btrfs_key_ptr), - key, btrfs_header_nritems(eb), - slot); - } - return -1; -} - -static struct extent_buffer *read_node_slot(struct btrfs_root *root, - struct extent_buffer *parent, int slot) -{ - int level = btrfs_header_level(parent); - if (slot < 0) - return NULL; - if (slot >= btrfs_header_nritems(parent)) - return NULL; - - BUG_ON(level == 0); - - return read_tree_block(root, btrfs_node_blockptr(parent, slot), - btrfs_level_size(root, level - 1), - btrfs_node_ptr_generation(parent, slot)); -} - -static int balance_level(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, int level) -{ - struct extent_buffer *right = NULL; - struct extent_buffer *mid; - struct extent_buffer *left = NULL; - struct extent_buffer *parent = NULL; - int ret = 0; - int wret; - int pslot; - int orig_slot = path->slots[level]; - int err_on_enospc = 0; - u64 orig_ptr; - - if (level == 0) - return 0; - - mid = path->nodes[level]; - WARN_ON(btrfs_header_generation(mid) != trans->transid); - - orig_ptr = btrfs_node_blockptr(mid, orig_slot); - - if (level < BTRFS_MAX_LEVEL - 1) - parent = path->nodes[level + 1]; - pslot = path->slots[level + 1]; - - /* - * deal with the case where there is only one pointer in the root - * by promoting the node below to a root - */ - if (!parent) { - struct extent_buffer *child; - - if (btrfs_header_nritems(mid) != 1) - return 0; - - /* promote the child to a root */ - child = read_node_slot(root, mid, 0); - BUG_ON(!child); - ret = btrfs_cow_block(trans, root, child, mid, 0, &child); - BUG_ON(ret); - - root->node = child; - add_root_to_dirty_list(root); - path->nodes[level] = NULL; - clean_tree_block(trans, root, mid); - wait_on_tree_block_writeback(root, mid); - /* once for the path */ - free_extent_buffer(mid); - - ret = btrfs_free_extent(trans, root, mid->start, mid->len, - 0, root->root_key.objectid, - level, 1); - /* once for the root ptr */ - free_extent_buffer(mid); - return ret; - } - if (btrfs_header_nritems(mid) > - BTRFS_NODEPTRS_PER_BLOCK(root) / 4) - return 0; - - if (btrfs_header_nritems(mid) < 2) - err_on_enospc = 1; - - left = read_node_slot(root, parent, pslot - 1); - if (left) { - wret = btrfs_cow_block(trans, root, left, - parent, pslot - 1, &left); - if (wret) { - ret = wret; - goto enospc; - } - } - right = read_node_slot(root, parent, pslot + 1); - if (right) { - wret = btrfs_cow_block(trans, root, right, - parent, pslot + 1, &right); - if (wret) { - ret = wret; - goto enospc; - } - } - - /* first, try to make some room in the middle buffer */ - if (left) { - orig_slot += btrfs_header_nritems(left); - wret = push_node_left(trans, root, left, mid, 1); - if (wret < 0) - ret = wret; - if (btrfs_header_nritems(mid) < 2) - err_on_enospc = 1; - } - - /* - * then try to empty the right most buffer into the middle - */ - if (right) { - wret = push_node_left(trans, root, mid, right, 1); - if (wret < 0 && wret != -ENOSPC) - ret = wret; - if (btrfs_header_nritems(right) == 0) { - u64 bytenr = right->start; - u32 blocksize = right->len; - - clean_tree_block(trans, root, right); - wait_on_tree_block_writeback(root, right); - free_extent_buffer(right); - right = NULL; - wret = del_ptr(trans, root, path, level + 1, pslot + - 1); - if (wret) - ret = wret; - wret = btrfs_free_extent(trans, root, bytenr, - blocksize, 0, - root->root_key.objectid, - level, 0); - if (wret) - ret = wret; - } else { - struct btrfs_disk_key right_key; - btrfs_node_key(right, &right_key, 0); - btrfs_set_node_key(parent, &right_key, pslot + 1); - btrfs_mark_buffer_dirty(parent); - } - } - if (btrfs_header_nritems(mid) == 1) { - /* - * we're not allowed to leave a node with one item in the - * tree during a delete. A deletion from lower in the tree - * could try to delete the only pointer in this node. - * So, pull some keys from the left. - * There has to be a left pointer at this point because - * otherwise we would have pulled some pointers from the - * right - */ - BUG_ON(!left); - wret = balance_node_right(trans, root, mid, left); - if (wret < 0) { - ret = wret; - goto enospc; - } - if (wret == 1) { - wret = push_node_left(trans, root, left, mid, 1); - if (wret < 0) - ret = wret; - } - BUG_ON(wret == 1); - } - if (btrfs_header_nritems(mid) == 0) { - /* we've managed to empty the middle node, drop it */ - u64 bytenr = mid->start; - u32 blocksize = mid->len; - clean_tree_block(trans, root, mid); - wait_on_tree_block_writeback(root, mid); - free_extent_buffer(mid); - mid = NULL; - wret = del_ptr(trans, root, path, level + 1, pslot); - if (wret) - ret = wret; - wret = btrfs_free_extent(trans, root, bytenr, blocksize, - 0, root->root_key.objectid, - level, 0); - if (wret) - ret = wret; - } else { - /* update the parent key to reflect our changes */ - struct btrfs_disk_key mid_key; - btrfs_node_key(mid, &mid_key, 0); - btrfs_set_node_key(parent, &mid_key, pslot); - btrfs_mark_buffer_dirty(parent); - } - - /* update the path */ - if (left) { - if (btrfs_header_nritems(left) > orig_slot) { - extent_buffer_get(left); - path->nodes[level] = left; - path->slots[level + 1] -= 1; - path->slots[level] = orig_slot; - if (mid) - free_extent_buffer(mid); - } else { - orig_slot -= btrfs_header_nritems(left); - path->slots[level] = orig_slot; - } - } - /* double check we haven't messed things up */ - check_block(root, path, level); - if (orig_ptr != - btrfs_node_blockptr(path->nodes[level], path->slots[level])) - BUG(); -enospc: - if (right) - free_extent_buffer(right); - if (left) - free_extent_buffer(left); - return ret; -} - -/* returns zero if the push worked, non-zero otherwise */ -static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, int level) -{ - struct extent_buffer *right = NULL; - struct extent_buffer *mid; - struct extent_buffer *left = NULL; - struct extent_buffer *parent = NULL; - int ret = 0; - int wret; - int pslot; - int orig_slot = path->slots[level]; - u64 orig_ptr; - - if (level == 0) - return 1; - - mid = path->nodes[level]; - WARN_ON(btrfs_header_generation(mid) != trans->transid); - orig_ptr = btrfs_node_blockptr(mid, orig_slot); - - if (level < BTRFS_MAX_LEVEL - 1) - parent = path->nodes[level + 1]; - pslot = path->slots[level + 1]; - - if (!parent) - return 1; - - left = read_node_slot(root, parent, pslot - 1); - - /* first, try to make some room in the middle buffer */ - if (left) { - u32 left_nr; - left_nr = btrfs_header_nritems(left); - if (left_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) { - wret = 1; - } else { - ret = btrfs_cow_block(trans, root, left, parent, - pslot - 1, &left); - if (ret) - wret = 1; - else { - wret = push_node_left(trans, root, - left, mid, 0); - } - } - if (wret < 0) - ret = wret; - if (wret == 0) { - struct btrfs_disk_key disk_key; - orig_slot += left_nr; - btrfs_node_key(mid, &disk_key, 0); - btrfs_set_node_key(parent, &disk_key, pslot); - btrfs_mark_buffer_dirty(parent); - if (btrfs_header_nritems(left) > orig_slot) { - path->nodes[level] = left; - path->slots[level + 1] -= 1; - path->slots[level] = orig_slot; - free_extent_buffer(mid); - } else { - orig_slot -= - btrfs_header_nritems(left); - path->slots[level] = orig_slot; - free_extent_buffer(left); - } - return 0; - } - free_extent_buffer(left); - } - right= read_node_slot(root, parent, pslot + 1); - - /* - * then try to empty the right most buffer into the middle - */ - if (right) { - u32 right_nr; - right_nr = btrfs_header_nritems(right); - if (right_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) { - wret = 1; - } else { - ret = btrfs_cow_block(trans, root, right, - parent, pslot + 1, - &right); - if (ret) - wret = 1; - else { - wret = balance_node_right(trans, root, - right, mid); - } - } - if (wret < 0) - ret = wret; - if (wret == 0) { - struct btrfs_disk_key disk_key; - - btrfs_node_key(right, &disk_key, 0); - btrfs_set_node_key(parent, &disk_key, pslot + 1); - btrfs_mark_buffer_dirty(parent); - - if (btrfs_header_nritems(mid) <= orig_slot) { - path->nodes[level] = right; - path->slots[level + 1] += 1; - path->slots[level] = orig_slot - - btrfs_header_nritems(mid); - free_extent_buffer(mid); - } else { - free_extent_buffer(right); - } - return 0; - } - free_extent_buffer(right); - } - return 1; -} - -/* - * readahead one full node of leaves - */ -static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path, - int level, int slot, u64 objectid) -{ - struct extent_buffer *node; - struct btrfs_disk_key disk_key; - u32 nritems; - u64 search; - u64 lowest_read; - u64 highest_read; - u64 nread = 0; - int direction = path->reada; - struct extent_buffer *eb; - u32 nr; - u32 blocksize; - u32 nscan = 0; - - if (level != 1) - return; - - if (!path->nodes[level]) - return; - - node = path->nodes[level]; - search = btrfs_node_blockptr(node, slot); - blocksize = btrfs_level_size(root, level - 1); - eb = btrfs_find_tree_block(root, search, blocksize); - if (eb) { - free_extent_buffer(eb); - return; - } - - highest_read = search; - lowest_read = search; - - nritems = btrfs_header_nritems(node); - nr = slot; - while(1) { - if (direction < 0) { - if (nr == 0) - break; - nr--; - } else if (direction > 0) { - nr++; - if (nr >= nritems) - break; - } - if (path->reada < 0 && objectid) { - btrfs_node_key(node, &disk_key, nr); - if (btrfs_disk_key_objectid(&disk_key) != objectid) - break; - } - search = btrfs_node_blockptr(node, nr); - if ((search >= lowest_read && search <= highest_read) || - (search < lowest_read && lowest_read - search <= 32768) || - (search > highest_read && search - highest_read <= 32768)) { - readahead_tree_block(root, search, blocksize, - btrfs_node_ptr_generation(node, nr)); - nread += blocksize; - } - nscan++; - if (path->reada < 2 && (nread > (256 * 1024) || nscan > 32)) - break; - if(nread > (1024 * 1024) || nscan > 128) - break; - - if (search < lowest_read) - lowest_read = search; - if (search > highest_read) - highest_read = search; - } -} - -/* - * look for key in the tree. path is filled in with nodes along the way - * if key is found, we return zero and you can find the item in the leaf - * level of the path (level 0) - * - * If the key isn't found, the path points to the slot where it should - * be inserted, and 1 is returned. If there are other errors during the - * search a negative error number is returned. - * - * if ins_len > 0, nodes and leaves will be split as we walk down the - * tree. if ins_len < 0, nodes will be merged as we walk down the tree (if - * possible) - */ -int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *key, struct btrfs_path *p, int - ins_len, int cow) -{ - struct extent_buffer *b; - int slot; - int ret; - int level; - int should_reada = p->reada; - u8 lowest_level = 0; - - lowest_level = p->lowest_level; - WARN_ON(lowest_level && ins_len); - WARN_ON(p->nodes[0] != NULL); - /* - WARN_ON(!mutex_is_locked(&root->fs_info->fs_mutex)); - */ -again: - b = root->node; - extent_buffer_get(b); - while (b) { - level = btrfs_header_level(b); - if (cow) { - int wret; - wret = btrfs_cow_block(trans, root, b, - p->nodes[level + 1], - p->slots[level + 1], - &b); - if (wret) { - free_extent_buffer(b); - return wret; - } - } - BUG_ON(!cow && ins_len); - if (level != btrfs_header_level(b)) - WARN_ON(1); - level = btrfs_header_level(b); - p->nodes[level] = b; - ret = check_block(root, p, level); - if (ret) - return -1; - ret = bin_search(b, key, level, &slot); - if (level != 0) { - if (ret && slot > 0) - slot -= 1; - p->slots[level] = slot; - if ((p->search_for_split || ins_len > 0) && - btrfs_header_nritems(b) >= - BTRFS_NODEPTRS_PER_BLOCK(root) - 3) { - int sret = split_node(trans, root, p, level); - BUG_ON(sret > 0); - if (sret) - return sret; - b = p->nodes[level]; - slot = p->slots[level]; - } else if (ins_len < 0) { - int sret = balance_level(trans, root, p, - level); - if (sret) - return sret; - b = p->nodes[level]; - if (!b) { - btrfs_release_path(NULL, p); - goto again; - } - slot = p->slots[level]; - BUG_ON(btrfs_header_nritems(b) == 1); - } - /* this is only true while dropping a snapshot */ - if (level == lowest_level) - break; - - if (should_reada) - reada_for_search(root, p, level, slot, - key->objectid); - - b = read_node_slot(root, b, slot); - } else { - p->slots[level] = slot; - if (ins_len > 0 && - ins_len > btrfs_leaf_free_space(root, b)) { - int sret = split_leaf(trans, root, key, - p, ins_len, ret == 0); - BUG_ON(sret > 0); - if (sret) - return sret; - } - return ret; - } - } - return 1; -} - -/* - * adjust the pointers going up the tree, starting at level - * making sure the right key of each node is points to 'key'. - * This is used after shifting pointers to the left, so it stops - * fixing up pointers when a given leaf/node is not in slot 0 of the - * higher levels - * - * If this fails to write a tree block, it returns -1, but continues - * fixing up the blocks in ram so the tree is consistent. - */ -static int fixup_low_keys(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct btrfs_path *path, - struct btrfs_disk_key *key, int level) -{ - int i; - int ret = 0; - struct extent_buffer *t; - - for (i = level; i < BTRFS_MAX_LEVEL; i++) { - int tslot = path->slots[i]; - if (!path->nodes[i]) - break; - t = path->nodes[i]; - btrfs_set_node_key(t, key, tslot); - btrfs_mark_buffer_dirty(path->nodes[i]); - if (tslot != 0) - break; - } - return ret; -} - -/* - * update item key. - * - * This function isn't completely safe. It's the caller's responsibility - * that the new key won't break the order - */ -int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct btrfs_path *path, - struct btrfs_key *new_key) -{ - struct btrfs_disk_key disk_key; - struct extent_buffer *eb; - int slot; - - eb = path->nodes[0]; - slot = path->slots[0]; - if (slot > 0) { - btrfs_item_key(eb, &disk_key, slot - 1); - if (btrfs_comp_keys(&disk_key, new_key) >= 0) - return -1; - } - if (slot < btrfs_header_nritems(eb) - 1) { - btrfs_item_key(eb, &disk_key, slot + 1); - if (btrfs_comp_keys(&disk_key, new_key) <= 0) - return -1; - } - - btrfs_cpu_key_to_disk(&disk_key, new_key); - btrfs_set_item_key(eb, &disk_key, slot); - btrfs_mark_buffer_dirty(eb); - if (slot == 0) - fixup_low_keys(trans, root, path, &disk_key, 1); - return 0; -} - -/* - * try to push data from one node into the next node left in the - * tree. - * - * returns 0 if some ptrs were pushed left, < 0 if there was some horrible - * error, and > 0 if there was no room in the left hand block. - */ -static int push_node_left(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct extent_buffer *dst, - struct extent_buffer *src, int empty) -{ - int push_items = 0; - int src_nritems; - int dst_nritems; - int ret = 0; - - src_nritems = btrfs_header_nritems(src); - dst_nritems = btrfs_header_nritems(dst); - push_items = BTRFS_NODEPTRS_PER_BLOCK(root) - dst_nritems; - WARN_ON(btrfs_header_generation(src) != trans->transid); - WARN_ON(btrfs_header_generation(dst) != trans->transid); - - if (!empty && src_nritems <= 8) - return 1; - - if (push_items <= 0) { - return 1; - } - - if (empty) { - push_items = min(src_nritems, push_items); - if (push_items < src_nritems) { - /* leave at least 8 pointers in the node if - * we aren't going to empty it - */ - if (src_nritems - push_items < 8) { - if (push_items <= 8) - return 1; - push_items -= 8; - } - } - } else - push_items = min(src_nritems - 8, push_items); - - copy_extent_buffer(dst, src, - btrfs_node_key_ptr_offset(dst_nritems), - btrfs_node_key_ptr_offset(0), - push_items * sizeof(struct btrfs_key_ptr)); - - if (push_items < src_nritems) { - memmove_extent_buffer(src, btrfs_node_key_ptr_offset(0), - btrfs_node_key_ptr_offset(push_items), - (src_nritems - push_items) * - sizeof(struct btrfs_key_ptr)); - } - btrfs_set_header_nritems(src, src_nritems - push_items); - btrfs_set_header_nritems(dst, dst_nritems + push_items); - btrfs_mark_buffer_dirty(src); - btrfs_mark_buffer_dirty(dst); - - return ret; -} - -/* - * try to push data from one node into the next node right in the - * tree. - * - * returns 0 if some ptrs were pushed, < 0 if there was some horrible - * error, and > 0 if there was no room in the right hand block. - * - * this will only push up to 1/2 the contents of the left node over - */ -static int balance_node_right(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *dst, - struct extent_buffer *src) -{ - int push_items = 0; - int max_push; - int src_nritems; - int dst_nritems; - int ret = 0; - - WARN_ON(btrfs_header_generation(src) != trans->transid); - WARN_ON(btrfs_header_generation(dst) != trans->transid); - - src_nritems = btrfs_header_nritems(src); - dst_nritems = btrfs_header_nritems(dst); - push_items = BTRFS_NODEPTRS_PER_BLOCK(root) - dst_nritems; - if (push_items <= 0) { - return 1; - } - - if (src_nritems < 4) { - return 1; - } - - max_push = src_nritems / 2 + 1; - /* don't try to empty the node */ - if (max_push >= src_nritems) { - return 1; - } - - if (max_push < push_items) - push_items = max_push; - - memmove_extent_buffer(dst, btrfs_node_key_ptr_offset(push_items), - btrfs_node_key_ptr_offset(0), - (dst_nritems) * - sizeof(struct btrfs_key_ptr)); - - copy_extent_buffer(dst, src, - btrfs_node_key_ptr_offset(0), - btrfs_node_key_ptr_offset(src_nritems - push_items), - push_items * sizeof(struct btrfs_key_ptr)); - - btrfs_set_header_nritems(src, src_nritems - push_items); - btrfs_set_header_nritems(dst, dst_nritems + push_items); - - btrfs_mark_buffer_dirty(src); - btrfs_mark_buffer_dirty(dst); - - return ret; -} - -/* - * helper function to insert a new root level in the tree. - * A new node is allocated, and a single item is inserted to - * point to the existing root - * - * returns zero on success or < 0 on failure. - */ -static int noinline insert_new_root(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, int level) -{ - u64 lower_gen; - struct extent_buffer *lower; - struct extent_buffer *c; - struct extent_buffer *old; - struct btrfs_disk_key lower_key; - - BUG_ON(path->nodes[level]); - BUG_ON(path->nodes[level-1] != root->node); - - lower = path->nodes[level-1]; - if (level == 1) - btrfs_item_key(lower, &lower_key, 0); - else - btrfs_node_key(lower, &lower_key, 0); - - c = btrfs_alloc_free_block(trans, root, root->nodesize, - root->root_key.objectid, &lower_key, - level, root->node->start, 0); - - if (IS_ERR(c)) - return PTR_ERR(c); - - memset_extent_buffer(c, 0, 0, sizeof(struct btrfs_header)); - btrfs_set_header_nritems(c, 1); - btrfs_set_header_level(c, level); - btrfs_set_header_bytenr(c, c->start); - btrfs_set_header_generation(c, trans->transid); - btrfs_set_header_backref_rev(c, BTRFS_MIXED_BACKREF_REV); - btrfs_set_header_owner(c, root->root_key.objectid); - - write_extent_buffer(c, root->fs_info->fsid, - (unsigned long)btrfs_header_fsid(c), - BTRFS_FSID_SIZE); - - write_extent_buffer(c, root->fs_info->chunk_tree_uuid, - (unsigned long)btrfs_header_chunk_tree_uuid(c), - BTRFS_UUID_SIZE); - - btrfs_set_node_key(c, &lower_key, 0); - btrfs_set_node_blockptr(c, 0, lower->start); - lower_gen = btrfs_header_generation(lower); - WARN_ON(lower_gen != trans->transid); - - btrfs_set_node_ptr_generation(c, 0, lower_gen); - - btrfs_mark_buffer_dirty(c); - - old = root->node; - root->node = c; - - /* the super has an extra ref to root->node */ - free_extent_buffer(old); - - add_root_to_dirty_list(root); - extent_buffer_get(c); - path->nodes[level] = c; - path->slots[level] = 0; - return 0; -} - -/* - * worker function to insert a single pointer in a node. - * the node should have enough room for the pointer already - * - * slot and level indicate where you want the key to go, and - * blocknr is the block the key points to. - * - * returns zero on success and < 0 on any error - */ -static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, struct btrfs_disk_key - *key, u64 bytenr, int slot, int level) -{ - struct extent_buffer *lower; - int nritems; - - BUG_ON(!path->nodes[level]); - lower = path->nodes[level]; - nritems = btrfs_header_nritems(lower); - if (slot > nritems) - BUG(); - if (nritems == BTRFS_NODEPTRS_PER_BLOCK(root)) - BUG(); - if (slot != nritems) { - memmove_extent_buffer(lower, - btrfs_node_key_ptr_offset(slot + 1), - btrfs_node_key_ptr_offset(slot), - (nritems - slot) * sizeof(struct btrfs_key_ptr)); - } - btrfs_set_node_key(lower, key, slot); - btrfs_set_node_blockptr(lower, slot, bytenr); - WARN_ON(trans->transid == 0); - btrfs_set_node_ptr_generation(lower, slot, trans->transid); - btrfs_set_header_nritems(lower, nritems + 1); - btrfs_mark_buffer_dirty(lower); - return 0; -} - -/* - * split the node at the specified level in path in two. - * The path is corrected to point to the appropriate node after the split - * - * Before splitting this tries to make some room in the node by pushing - * left and right, if either one works, it returns right away. - * - * returns 0 on success and < 0 on failure - */ -static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int level) -{ - struct extent_buffer *c; - struct extent_buffer *split; - struct btrfs_disk_key disk_key; - int mid; - int ret; - int wret; - u32 c_nritems; - - c = path->nodes[level]; - WARN_ON(btrfs_header_generation(c) != trans->transid); - if (c == root->node) { - /* trying to split the root, lets make a new one */ - ret = insert_new_root(trans, root, path, level + 1); - if (ret) - return ret; - } else { - ret = push_nodes_for_insert(trans, root, path, level); - c = path->nodes[level]; - if (!ret && btrfs_header_nritems(c) < - BTRFS_NODEPTRS_PER_BLOCK(root) - 3) - return 0; - if (ret < 0) - return ret; - } - - c_nritems = btrfs_header_nritems(c); - mid = (c_nritems + 1) / 2; - btrfs_node_key(c, &disk_key, mid); - - split = btrfs_alloc_free_block(trans, root, root->nodesize, - root->root_key.objectid, - &disk_key, level, c->start, 0); - if (IS_ERR(split)) - return PTR_ERR(split); - - memset_extent_buffer(split, 0, 0, sizeof(struct btrfs_header)); - btrfs_set_header_level(split, btrfs_header_level(c)); - btrfs_set_header_bytenr(split, split->start); - btrfs_set_header_generation(split, trans->transid); - btrfs_set_header_backref_rev(split, BTRFS_MIXED_BACKREF_REV); - btrfs_set_header_owner(split, root->root_key.objectid); - write_extent_buffer(split, root->fs_info->fsid, - (unsigned long)btrfs_header_fsid(split), - BTRFS_FSID_SIZE); - write_extent_buffer(split, root->fs_info->chunk_tree_uuid, - (unsigned long)btrfs_header_chunk_tree_uuid(split), - BTRFS_UUID_SIZE); - - - copy_extent_buffer(split, c, - btrfs_node_key_ptr_offset(0), - btrfs_node_key_ptr_offset(mid), - (c_nritems - mid) * sizeof(struct btrfs_key_ptr)); - btrfs_set_header_nritems(split, c_nritems - mid); - btrfs_set_header_nritems(c, mid); - ret = 0; - - btrfs_mark_buffer_dirty(c); - btrfs_mark_buffer_dirty(split); - - wret = insert_ptr(trans, root, path, &disk_key, split->start, - path->slots[level + 1] + 1, - level + 1); - if (wret) - ret = wret; - - if (path->slots[level] >= mid) { - path->slots[level] -= mid; - free_extent_buffer(c); - path->nodes[level] = split; - path->slots[level + 1] += 1; - } else { - free_extent_buffer(split); - } - return ret; -} - -/* - * how many bytes are required to store the items in a leaf. start - * and nr indicate which items in the leaf to check. This totals up the - * space used both by the item structs and the item data - */ -static int leaf_space_used(struct extent_buffer *l, int start, int nr) -{ - int data_len; - int nritems = btrfs_header_nritems(l); - int end = min(nritems, start + nr) - 1; - - if (!nr) - return 0; - data_len = btrfs_item_end_nr(l, start); - data_len = data_len - btrfs_item_offset_nr(l, end); - data_len += sizeof(struct btrfs_item) * nr; - WARN_ON(data_len < 0); - return data_len; -} - -/* - * The space between the end of the leaf items and - * the start of the leaf data. IOW, how much room - * the leaf has left for both items and data - */ -int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf) -{ - int nritems = btrfs_header_nritems(leaf); - int ret; - ret = BTRFS_LEAF_DATA_SIZE(root) - leaf_space_used(leaf, 0, nritems); - if (ret < 0) { - printk("leaf free space ret %d, leaf data size %lu, used %d nritems %d\n", - ret, (unsigned long) BTRFS_LEAF_DATA_SIZE(root), - leaf_space_used(leaf, 0, nritems), nritems); - } - return ret; -} - -/* - * push some data in the path leaf to the right, trying to free up at - * least data_size bytes. returns zero if the push worked, nonzero otherwise - * - * returns 1 if the push failed because the other node didn't have enough - * room, 0 if everything worked out and < 0 if there were major errors. - */ -static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int data_size, - int empty) -{ - struct extent_buffer *left = path->nodes[0]; - struct extent_buffer *right; - struct extent_buffer *upper; - struct btrfs_disk_key disk_key; - int slot; - u32 i; - int free_space; - int push_space = 0; - int push_items = 0; - struct btrfs_item *item; - u32 left_nritems; - u32 nr; - u32 right_nritems; - u32 data_end; - u32 this_item_size; - int ret; - - slot = path->slots[1]; - if (!path->nodes[1]) { - return 1; - } - upper = path->nodes[1]; - if (slot >= btrfs_header_nritems(upper) - 1) - return 1; - - right = read_node_slot(root, upper, slot + 1); - free_space = btrfs_leaf_free_space(root, right); - if (free_space < data_size) { - free_extent_buffer(right); - return 1; - } - - /* cow and double check */ - ret = btrfs_cow_block(trans, root, right, upper, - slot + 1, &right); - if (ret) { - free_extent_buffer(right); - return 1; - } - free_space = btrfs_leaf_free_space(root, right); - if (free_space < data_size) { - free_extent_buffer(right); - return 1; - } - - left_nritems = btrfs_header_nritems(left); - if (left_nritems == 0) { - free_extent_buffer(right); - return 1; - } - - if (empty) - nr = 0; - else - nr = 1; - - i = left_nritems - 1; - while (i >= nr) { - item = btrfs_item_nr(left, i); - - if (path->slots[0] == i) - push_space += data_size + sizeof(*item); - - this_item_size = btrfs_item_size(left, item); - if (this_item_size + sizeof(*item) + push_space > free_space) - break; - push_items++; - push_space += this_item_size + sizeof(*item); - if (i == 0) - break; - i--; - } - - if (push_items == 0) { - free_extent_buffer(right); - return 1; - } - - if (!empty && push_items == left_nritems) - WARN_ON(1); - - /* push left to right */ - right_nritems = btrfs_header_nritems(right); - - push_space = btrfs_item_end_nr(left, left_nritems - push_items); - push_space -= leaf_data_end(root, left); - - /* make room in the right data area */ - data_end = leaf_data_end(root, right); - memmove_extent_buffer(right, - btrfs_leaf_data(right) + data_end - push_space, - btrfs_leaf_data(right) + data_end, - BTRFS_LEAF_DATA_SIZE(root) - data_end); - - /* copy from the left data area */ - copy_extent_buffer(right, left, btrfs_leaf_data(right) + - BTRFS_LEAF_DATA_SIZE(root) - push_space, - btrfs_leaf_data(left) + leaf_data_end(root, left), - push_space); - - memmove_extent_buffer(right, btrfs_item_nr_offset(push_items), - btrfs_item_nr_offset(0), - right_nritems * sizeof(struct btrfs_item)); - - /* copy the items from left to right */ - copy_extent_buffer(right, left, btrfs_item_nr_offset(0), - btrfs_item_nr_offset(left_nritems - push_items), - push_items * sizeof(struct btrfs_item)); - - /* update the item pointers */ - right_nritems += push_items; - btrfs_set_header_nritems(right, right_nritems); - push_space = BTRFS_LEAF_DATA_SIZE(root); - for (i = 0; i < right_nritems; i++) { - item = btrfs_item_nr(right, i); - push_space -= btrfs_item_size(right, item); - btrfs_set_item_offset(right, item, push_space); - } - - left_nritems -= push_items; - btrfs_set_header_nritems(left, left_nritems); - - if (left_nritems) - btrfs_mark_buffer_dirty(left); - btrfs_mark_buffer_dirty(right); - - btrfs_item_key(right, &disk_key, 0); - btrfs_set_node_key(upper, &disk_key, slot + 1); - btrfs_mark_buffer_dirty(upper); - - /* then fixup the leaf pointer in the path */ - if (path->slots[0] >= left_nritems) { - path->slots[0] -= left_nritems; - free_extent_buffer(path->nodes[0]); - path->nodes[0] = right; - path->slots[1] += 1; - } else { - free_extent_buffer(right); - } - return 0; -} -/* - * push some data in the path leaf to the left, trying to free up at - * least data_size bytes. returns zero if the push worked, nonzero otherwise - */ -static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int data_size, - int empty) -{ - struct btrfs_disk_key disk_key; - struct extent_buffer *right = path->nodes[0]; - struct extent_buffer *left; - int slot; - int i; - int free_space; - int push_space = 0; - int push_items = 0; - struct btrfs_item *item; - u32 old_left_nritems; - u32 right_nritems; - u32 nr; - int ret = 0; - int wret; - u32 this_item_size; - u32 old_left_item_size; - - slot = path->slots[1]; - if (slot == 0) - return 1; - if (!path->nodes[1]) - return 1; - - right_nritems = btrfs_header_nritems(right); - if (right_nritems == 0) { - return 1; - } - - left = read_node_slot(root, path->nodes[1], slot - 1); - free_space = btrfs_leaf_free_space(root, left); - if (free_space < data_size) { - free_extent_buffer(left); - return 1; - } - - /* cow and double check */ - ret = btrfs_cow_block(trans, root, left, - path->nodes[1], slot - 1, &left); - if (ret) { - /* we hit -ENOSPC, but it isn't fatal here */ - free_extent_buffer(left); - return 1; - } - - free_space = btrfs_leaf_free_space(root, left); - if (free_space < data_size) { - free_extent_buffer(left); - return 1; - } - - if (empty) - nr = right_nritems; - else - nr = right_nritems - 1; - - for (i = 0; i < nr; i++) { - item = btrfs_item_nr(right, i); - - if (path->slots[0] == i) - push_space += data_size + sizeof(*item); - - this_item_size = btrfs_item_size(right, item); - if (this_item_size + sizeof(*item) + push_space > free_space) - break; - - push_items++; - push_space += this_item_size + sizeof(*item); - } - - if (push_items == 0) { - free_extent_buffer(left); - return 1; - } - if (!empty && push_items == btrfs_header_nritems(right)) - WARN_ON(1); - - /* push data from right to left */ - copy_extent_buffer(left, right, - btrfs_item_nr_offset(btrfs_header_nritems(left)), - btrfs_item_nr_offset(0), - push_items * sizeof(struct btrfs_item)); - - push_space = BTRFS_LEAF_DATA_SIZE(root) - - btrfs_item_offset_nr(right, push_items -1); - - copy_extent_buffer(left, right, btrfs_leaf_data(left) + - leaf_data_end(root, left) - push_space, - btrfs_leaf_data(right) + - btrfs_item_offset_nr(right, push_items - 1), - push_space); - old_left_nritems = btrfs_header_nritems(left); - BUG_ON(old_left_nritems < 0); - - old_left_item_size = btrfs_item_offset_nr(left, old_left_nritems - 1); - for (i = old_left_nritems; i < old_left_nritems + push_items; i++) { - u32 ioff; - - item = btrfs_item_nr(left, i); - ioff = btrfs_item_offset(left, item); - btrfs_set_item_offset(left, item, - ioff - (BTRFS_LEAF_DATA_SIZE(root) - old_left_item_size)); - } - btrfs_set_header_nritems(left, old_left_nritems + push_items); - - /* fixup right node */ - if (push_items > right_nritems) { - printk("push items %d nr %u\n", push_items, right_nritems); - WARN_ON(1); - } - - if (push_items < right_nritems) { - push_space = btrfs_item_offset_nr(right, push_items - 1) - - leaf_data_end(root, right); - memmove_extent_buffer(right, btrfs_leaf_data(right) + - BTRFS_LEAF_DATA_SIZE(root) - push_space, - btrfs_leaf_data(right) + - leaf_data_end(root, right), push_space); - - memmove_extent_buffer(right, btrfs_item_nr_offset(0), - btrfs_item_nr_offset(push_items), - (btrfs_header_nritems(right) - push_items) * - sizeof(struct btrfs_item)); - } - right_nritems -= push_items; - btrfs_set_header_nritems(right, right_nritems); - push_space = BTRFS_LEAF_DATA_SIZE(root); - for (i = 0; i < right_nritems; i++) { - item = btrfs_item_nr(right, i); - push_space = push_space - btrfs_item_size(right, item); - btrfs_set_item_offset(right, item, push_space); - } - - btrfs_mark_buffer_dirty(left); - if (right_nritems) - btrfs_mark_buffer_dirty(right); - - btrfs_item_key(right, &disk_key, 0); - wret = fixup_low_keys(trans, root, path, &disk_key, 1); - if (wret) - ret = wret; - - /* then fixup the leaf pointer in the path */ - if (path->slots[0] < push_items) { - path->slots[0] += old_left_nritems; - free_extent_buffer(path->nodes[0]); - path->nodes[0] = left; - path->slots[1] -= 1; - } else { - free_extent_buffer(left); - path->slots[0] -= push_items; - } - BUG_ON(path->slots[0] < 0); - return ret; -} - -/* - * split the path's leaf in two, making sure there is at least data_size - * available for the resulting leaf level of the path. - * - * returns 0 if all went well and < 0 on failure. - */ -static noinline int copy_for_split(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct extent_buffer *l, - struct extent_buffer *right, - int slot, int mid, int nritems) -{ - int data_copy_size; - int rt_data_off; - int i; - int ret = 0; - int wret; - struct btrfs_disk_key disk_key; - - nritems = nritems - mid; - btrfs_set_header_nritems(right, nritems); - data_copy_size = btrfs_item_end_nr(l, mid) - leaf_data_end(root, l); - - copy_extent_buffer(right, l, btrfs_item_nr_offset(0), - btrfs_item_nr_offset(mid), - nritems * sizeof(struct btrfs_item)); - - copy_extent_buffer(right, l, - btrfs_leaf_data(right) + BTRFS_LEAF_DATA_SIZE(root) - - data_copy_size, btrfs_leaf_data(l) + - leaf_data_end(root, l), data_copy_size); - - rt_data_off = BTRFS_LEAF_DATA_SIZE(root) - - btrfs_item_end_nr(l, mid); - - for (i = 0; i < nritems; i++) { - struct btrfs_item *item = btrfs_item_nr(right, i); - u32 ioff = btrfs_item_offset(right, item); - btrfs_set_item_offset(right, item, ioff + rt_data_off); - } - - btrfs_set_header_nritems(l, mid); - ret = 0; - btrfs_item_key(right, &disk_key, 0); - wret = insert_ptr(trans, root, path, &disk_key, right->start, - path->slots[1] + 1, 1); - if (wret) - ret = wret; - - btrfs_mark_buffer_dirty(right); - btrfs_mark_buffer_dirty(l); - BUG_ON(path->slots[0] != slot); - - if (mid <= slot) { - free_extent_buffer(path->nodes[0]); - path->nodes[0] = right; - path->slots[0] -= mid; - path->slots[1] += 1; - } else { - free_extent_buffer(right); - } - - BUG_ON(path->slots[0] < 0); - - return ret; -} - -/* - * split the path's leaf in two, making sure there is at least data_size - * available for the resulting leaf level of the path. - * - * returns 0 if all went well and < 0 on failure. - */ -static noinline int split_leaf(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_key *ins_key, - struct btrfs_path *path, int data_size, - int extend) -{ - struct btrfs_disk_key disk_key; - struct extent_buffer *l; - u32 nritems; - int mid; - int slot; - struct extent_buffer *right; - int ret = 0; - int wret; - int split; - int num_doubles = 0; - - /* first try to make some room by pushing left and right */ - if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY) { - wret = push_leaf_right(trans, root, path, data_size, 0); - if (wret < 0) - return wret; - if (wret) { - wret = push_leaf_left(trans, root, path, data_size, 0); - if (wret < 0) - return wret; - } - l = path->nodes[0]; - - /* did the pushes work? */ - if (btrfs_leaf_free_space(root, l) >= data_size) - return 0; - } - - if (!path->nodes[1]) { - ret = insert_new_root(trans, root, path, 1); - if (ret) - return ret; - } -again: - split = 1; - l = path->nodes[0]; - slot = path->slots[0]; - nritems = btrfs_header_nritems(l); - mid = (nritems + 1) / 2; - - if (mid <= slot) { - if (nritems == 1 || - leaf_space_used(l, mid, nritems - mid) + data_size > - BTRFS_LEAF_DATA_SIZE(root)) { - if (slot >= nritems) { - split = 0; - } else { - mid = slot; - if (mid != nritems && - leaf_space_used(l, mid, nritems - mid) + - data_size > BTRFS_LEAF_DATA_SIZE(root)) { - split = 2; - } - } - } - } else { - if (leaf_space_used(l, 0, mid) + data_size > - BTRFS_LEAF_DATA_SIZE(root)) { - if (!extend && data_size && slot == 0) { - split = 0; - } else if ((extend || !data_size) && slot == 0) { - mid = 1; - } else { - mid = slot; - if (mid != nritems && - leaf_space_used(l, mid, nritems - mid) + - data_size > BTRFS_LEAF_DATA_SIZE(root)) { - split = 2 ; - } - } - } - } - - if (split == 0) - btrfs_cpu_key_to_disk(&disk_key, ins_key); - else - btrfs_item_key(l, &disk_key, mid); - - right = btrfs_alloc_free_block(trans, root, root->leafsize, - root->root_key.objectid, - &disk_key, 0, l->start, 0); - if (IS_ERR(right)) { - BUG_ON(1); - return PTR_ERR(right); - } - - memset_extent_buffer(right, 0, 0, sizeof(struct btrfs_header)); - btrfs_set_header_bytenr(right, right->start); - btrfs_set_header_generation(right, trans->transid); - btrfs_set_header_backref_rev(right, BTRFS_MIXED_BACKREF_REV); - btrfs_set_header_owner(right, root->root_key.objectid); - btrfs_set_header_level(right, 0); - write_extent_buffer(right, root->fs_info->fsid, - (unsigned long)btrfs_header_fsid(right), - BTRFS_FSID_SIZE); - - write_extent_buffer(right, root->fs_info->chunk_tree_uuid, - (unsigned long)btrfs_header_chunk_tree_uuid(right), - BTRFS_UUID_SIZE); - - if (split == 0) { - if (mid <= slot) { - btrfs_set_header_nritems(right, 0); - wret = insert_ptr(trans, root, path, - &disk_key, right->start, - path->slots[1] + 1, 1); - if (wret) - ret = wret; - - free_extent_buffer(path->nodes[0]); - path->nodes[0] = right; - path->slots[0] = 0; - path->slots[1] += 1; - } else { - btrfs_set_header_nritems(right, 0); - wret = insert_ptr(trans, root, path, - &disk_key, - right->start, - path->slots[1], 1); - if (wret) - ret = wret; - free_extent_buffer(path->nodes[0]); - path->nodes[0] = right; - path->slots[0] = 0; - if (path->slots[1] == 0) { - wret = fixup_low_keys(trans, root, - path, &disk_key, 1); - if (wret) - ret = wret; - } - } - btrfs_mark_buffer_dirty(right); - return ret; - } - - ret = copy_for_split(trans, root, path, l, right, slot, mid, nritems); - BUG_ON(ret); - - if (split == 2) { - BUG_ON(num_doubles != 0); - num_doubles++; - goto again; - } - - return ret; -} - -/* - * This function splits a single item into two items, - * giving 'new_key' to the new item and splitting the - * old one at split_offset (from the start of the item). - * - * The path may be released by this operation. After - * the split, the path is pointing to the old item. The - * new item is going to be in the same node as the old one. - * - * Note, the item being split must be smaller enough to live alone on - * a tree block with room for one extra struct btrfs_item - * - * This allows us to split the item in place, keeping a lock on the - * leaf the entire time. - */ -int btrfs_split_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_key *new_key, - unsigned long split_offset) -{ - u32 item_size; - struct extent_buffer *leaf; - struct btrfs_key orig_key; - struct btrfs_item *item; - struct btrfs_item *new_item; - int ret = 0; - int slot; - u32 nritems; - u32 orig_offset; - struct btrfs_disk_key disk_key; - char *buf; - - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &orig_key, path->slots[0]); - if (btrfs_leaf_free_space(root, leaf) >= sizeof(struct btrfs_item)) - goto split; - - item_size = btrfs_item_size_nr(leaf, path->slots[0]); - btrfs_release_path(root, path); - - path->search_for_split = 1; - - ret = btrfs_search_slot(trans, root, &orig_key, path, 0, 1); - path->search_for_split = 0; - - /* if our item isn't there or got smaller, return now */ - if (ret != 0 || item_size != btrfs_item_size_nr(path->nodes[0], - path->slots[0])) { - return -EAGAIN; - } - - ret = split_leaf(trans, root, &orig_key, path, 0, 0); - BUG_ON(ret); - - BUG_ON(btrfs_leaf_free_space(root, leaf) < sizeof(struct btrfs_item)); - leaf = path->nodes[0]; - -split: - item = btrfs_item_nr(leaf, path->slots[0]); - orig_offset = btrfs_item_offset(leaf, item); - item_size = btrfs_item_size(leaf, item); - - - buf = kmalloc(item_size, GFP_NOFS); - read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf, - path->slots[0]), item_size); - slot = path->slots[0] + 1; - leaf = path->nodes[0]; - - nritems = btrfs_header_nritems(leaf); - - if (slot != nritems) { - /* shift the items */ - memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + 1), - btrfs_item_nr_offset(slot), - (nritems - slot) * sizeof(struct btrfs_item)); - - } - - btrfs_cpu_key_to_disk(&disk_key, new_key); - btrfs_set_item_key(leaf, &disk_key, slot); - - new_item = btrfs_item_nr(leaf, slot); - - btrfs_set_item_offset(leaf, new_item, orig_offset); - btrfs_set_item_size(leaf, new_item, item_size - split_offset); - - btrfs_set_item_offset(leaf, item, - orig_offset + item_size - split_offset); - btrfs_set_item_size(leaf, item, split_offset); - - btrfs_set_header_nritems(leaf, nritems + 1); - - /* write the data for the start of the original item */ - write_extent_buffer(leaf, buf, - btrfs_item_ptr_offset(leaf, path->slots[0]), - split_offset); - - /* write the data for the new item */ - write_extent_buffer(leaf, buf + split_offset, - btrfs_item_ptr_offset(leaf, slot), - item_size - split_offset); - btrfs_mark_buffer_dirty(leaf); - - ret = 0; - if (btrfs_leaf_free_space(root, leaf) < 0) { - btrfs_print_leaf(root, leaf); - BUG(); - } - kfree(buf); - return ret; -} - -int btrfs_truncate_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u32 new_size, int from_end) -{ - int ret = 0; - int slot; - int slot_orig; - struct extent_buffer *leaf; - struct btrfs_item *item; - u32 nritems; - unsigned int data_end; - unsigned int old_data_start; - unsigned int old_size; - unsigned int size_diff; - int i; - - slot_orig = path->slots[0]; - leaf = path->nodes[0]; - slot = path->slots[0]; - - old_size = btrfs_item_size_nr(leaf, slot); - if (old_size == new_size) - return 0; - - nritems = btrfs_header_nritems(leaf); - data_end = leaf_data_end(root, leaf); - - old_data_start = btrfs_item_offset_nr(leaf, slot); - - size_diff = old_size - new_size; - - BUG_ON(slot < 0); - BUG_ON(slot >= nritems); - - /* - * item0..itemN ... dataN.offset..dataN.size .. data0.size - */ - /* first correct the data pointers */ - for (i = slot; i < nritems; i++) { - u32 ioff; - item = btrfs_item_nr(leaf, i); - ioff = btrfs_item_offset(leaf, item); - btrfs_set_item_offset(leaf, item, ioff + size_diff); - } - - /* shift the data */ - if (from_end) { - memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + - data_end + size_diff, btrfs_leaf_data(leaf) + - data_end, old_data_start + new_size - data_end); - } else { - struct btrfs_disk_key disk_key; - u64 offset; - - btrfs_item_key(leaf, &disk_key, slot); - - if (btrfs_disk_key_type(&disk_key) == BTRFS_EXTENT_DATA_KEY) { - unsigned long ptr; - struct btrfs_file_extent_item *fi; - - fi = btrfs_item_ptr(leaf, slot, - struct btrfs_file_extent_item); - fi = (struct btrfs_file_extent_item *)( - (unsigned long)fi - size_diff); - - if (btrfs_file_extent_type(leaf, fi) == - BTRFS_FILE_EXTENT_INLINE) { - ptr = btrfs_item_ptr_offset(leaf, slot); - memmove_extent_buffer(leaf, ptr, - (unsigned long)fi, - offsetof(struct btrfs_file_extent_item, - disk_bytenr)); - } - } - - memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + - data_end + size_diff, btrfs_leaf_data(leaf) + - data_end, old_data_start - data_end); - - offset = btrfs_disk_key_offset(&disk_key); - btrfs_set_disk_key_offset(&disk_key, offset + size_diff); - btrfs_set_item_key(leaf, &disk_key, slot); - if (slot == 0) - fixup_low_keys(trans, root, path, &disk_key, 1); - } - - item = btrfs_item_nr(leaf, slot); - btrfs_set_item_size(leaf, item, new_size); - btrfs_mark_buffer_dirty(leaf); - - ret = 0; - if (btrfs_leaf_free_space(root, leaf) < 0) { - btrfs_print_leaf(root, leaf); - BUG(); - } - return ret; -} - -int btrfs_extend_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct btrfs_path *path, - u32 data_size) -{ - int ret = 0; - int slot; - int slot_orig; - struct extent_buffer *leaf; - struct btrfs_item *item; - u32 nritems; - unsigned int data_end; - unsigned int old_data; - unsigned int old_size; - int i; - - slot_orig = path->slots[0]; - leaf = path->nodes[0]; - - nritems = btrfs_header_nritems(leaf); - data_end = leaf_data_end(root, leaf); - - if (btrfs_leaf_free_space(root, leaf) < data_size) { - btrfs_print_leaf(root, leaf); - BUG(); - } - slot = path->slots[0]; - old_data = btrfs_item_end_nr(leaf, slot); - - BUG_ON(slot < 0); - if (slot >= nritems) { - btrfs_print_leaf(root, leaf); - printk("slot %d too large, nritems %d\n", slot, nritems); - BUG_ON(1); - } - - /* - * item0..itemN ... dataN.offset..dataN.size .. data0.size - */ - /* first correct the data pointers */ - for (i = slot; i < nritems; i++) { - u32 ioff; - item = btrfs_item_nr(leaf, i); - ioff = btrfs_item_offset(leaf, item); - btrfs_set_item_offset(leaf, item, ioff - data_size); - } - - /* shift the data */ - memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + - data_end - data_size, btrfs_leaf_data(leaf) + - data_end, old_data - data_end); - - data_end = old_data; - old_size = btrfs_item_size_nr(leaf, slot); - item = btrfs_item_nr(leaf, slot); - btrfs_set_item_size(leaf, item, old_size + data_size); - btrfs_mark_buffer_dirty(leaf); - - ret = 0; - if (btrfs_leaf_free_space(root, leaf) < 0) { - btrfs_print_leaf(root, leaf); - BUG(); - } - return ret; -} - -/* - * Given a key and some data, insert an item into the tree. - * This does all the path init required, making room in the tree if needed. - */ -int btrfs_insert_empty_items(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_key *cpu_key, u32 *data_size, - int nr) -{ - struct extent_buffer *leaf; - struct btrfs_item *item; - int ret = 0; - int slot; - int slot_orig; - int i; - u32 nritems; - u32 total_size = 0; - u32 total_data = 0; - unsigned int data_end; - struct btrfs_disk_key disk_key; - - for (i = 0; i < nr; i++) { - total_data += data_size[i]; - } - - /* create a root if there isn't one */ - if (!root->node) - BUG(); - - total_size = total_data + nr * sizeof(struct btrfs_item); - ret = btrfs_search_slot(trans, root, cpu_key, path, total_size, 1); - if (ret == 0) { - return -EEXIST; - } - if (ret < 0) - goto out; - - slot_orig = path->slots[0]; - leaf = path->nodes[0]; - - nritems = btrfs_header_nritems(leaf); - data_end = leaf_data_end(root, leaf); - - if (btrfs_leaf_free_space(root, leaf) < total_size) { - btrfs_print_leaf(root, leaf); - printk("not enough freespace need %u have %d\n", - total_size, btrfs_leaf_free_space(root, leaf)); - BUG(); - } - - slot = path->slots[0]; - BUG_ON(slot < 0); - - if (slot != nritems) { - int i; - unsigned int old_data = btrfs_item_end_nr(leaf, slot); - - if (old_data < data_end) { - btrfs_print_leaf(root, leaf); - printk("slot %d old_data %d data_end %d\n", - slot, old_data, data_end); - BUG_ON(1); - } - /* - * item0..itemN ... dataN.offset..dataN.size .. data0.size - */ - /* first correct the data pointers */ - for (i = slot; i < nritems; i++) { - u32 ioff; - - item = btrfs_item_nr(leaf, i); - ioff = btrfs_item_offset(leaf, item); - btrfs_set_item_offset(leaf, item, ioff - total_data); - } - - /* shift the items */ - memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + nr), - btrfs_item_nr_offset(slot), - (nritems - slot) * sizeof(struct btrfs_item)); - - /* shift the data */ - memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + - data_end - total_data, btrfs_leaf_data(leaf) + - data_end, old_data - data_end); - data_end = old_data; - } - - /* setup the item for the new data */ - for (i = 0; i < nr; i++) { - btrfs_cpu_key_to_disk(&disk_key, cpu_key + i); - btrfs_set_item_key(leaf, &disk_key, slot + i); - item = btrfs_item_nr(leaf, slot + i); - btrfs_set_item_offset(leaf, item, data_end - data_size[i]); - data_end -= data_size[i]; - btrfs_set_item_size(leaf, item, data_size[i]); - } - btrfs_set_header_nritems(leaf, nritems + nr); - btrfs_mark_buffer_dirty(leaf); - - ret = 0; - if (slot == 0) { - btrfs_cpu_key_to_disk(&disk_key, cpu_key); - ret = fixup_low_keys(trans, root, path, &disk_key, 1); - } - - if (btrfs_leaf_free_space(root, leaf) < 0) { - btrfs_print_leaf(root, leaf); - BUG(); - } - -out: - return ret; -} - -/* - * Given a key and some data, insert an item into the tree. - * This does all the path init required, making room in the tree if needed. - */ -int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *cpu_key, void *data, u32 - data_size) -{ - int ret = 0; - struct btrfs_path *path; - struct extent_buffer *leaf; - unsigned long ptr; - - path = btrfs_alloc_path(); - BUG_ON(!path); - ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size); - if (!ret) { - leaf = path->nodes[0]; - ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); - write_extent_buffer(leaf, data, ptr, data_size); - btrfs_mark_buffer_dirty(leaf); - } - btrfs_free_path(path); - return ret; -} - -/* - * delete the pointer from a given node. - * - * If the delete empties a node, the node is removed from the tree, - * continuing all the way the root if required. The root is converted into - * a leaf if all the nodes are emptied. - */ -static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct btrfs_path *path, int level, int slot) -{ - struct extent_buffer *parent = path->nodes[level]; - u32 nritems; - int ret = 0; - int wret; - - nritems = btrfs_header_nritems(parent); - if (slot != nritems -1) { - memmove_extent_buffer(parent, - btrfs_node_key_ptr_offset(slot), - btrfs_node_key_ptr_offset(slot + 1), - sizeof(struct btrfs_key_ptr) * - (nritems - slot - 1)); - } - nritems--; - btrfs_set_header_nritems(parent, nritems); - if (nritems == 0 && parent == root->node) { - BUG_ON(btrfs_header_level(root->node) != 1); - /* just turn the root into a leaf and break */ - btrfs_set_header_level(root->node, 0); - } else if (slot == 0) { - struct btrfs_disk_key disk_key; - - btrfs_node_key(parent, &disk_key, 0); - wret = fixup_low_keys(trans, root, path, &disk_key, level + 1); - if (wret) - ret = wret; - } - btrfs_mark_buffer_dirty(parent); - return ret; -} - -/* - * a helper function to delete the leaf pointed to by path->slots[1] and - * path->nodes[1]. - * - * This deletes the pointer in path->nodes[1] and frees the leaf - * block extent. zero is returned if it all worked out, < 0 otherwise. - * - * The path must have already been setup for deleting the leaf, including - * all the proper balancing. path->nodes[1] must be locked. - */ -static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct extent_buffer *leaf) -{ - int ret; - - WARN_ON(btrfs_header_generation(leaf) != trans->transid); - ret = del_ptr(trans, root, path, 1, path->slots[1]); - if (ret) - return ret; - - ret = btrfs_free_extent(trans, root, leaf->start, leaf->len, - 0, root->root_key.objectid, 0, 0); - return ret; -} - -/* - * delete the item at the leaf level in path. If that empties - * the leaf, remove it from the tree - */ -int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct btrfs_path *path, int slot, int nr) -{ - struct extent_buffer *leaf; - struct btrfs_item *item; - int last_off; - int dsize = 0; - int ret = 0; - int wret; - int i; - u32 nritems; - - leaf = path->nodes[0]; - last_off = btrfs_item_offset_nr(leaf, slot + nr - 1); - - for (i = 0; i < nr; i++) - dsize += btrfs_item_size_nr(leaf, slot + i); - - nritems = btrfs_header_nritems(leaf); - - if (slot + nr != nritems) { - int i; - int data_end = leaf_data_end(root, leaf); - - memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + - data_end + dsize, - btrfs_leaf_data(leaf) + data_end, - last_off - data_end); - - for (i = slot + nr; i < nritems; i++) { - u32 ioff; - - item = btrfs_item_nr(leaf, i); - ioff = btrfs_item_offset(leaf, item); - btrfs_set_item_offset(leaf, item, ioff + dsize); - } - - memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot), - btrfs_item_nr_offset(slot + nr), - sizeof(struct btrfs_item) * - (nritems - slot - nr)); - } - btrfs_set_header_nritems(leaf, nritems - nr); - nritems -= nr; - - /* delete the leaf if we've emptied it */ - if (nritems == 0) { - if (leaf == root->node) { - btrfs_set_header_level(leaf, 0); - } else { - clean_tree_block(trans, root, leaf); - wait_on_tree_block_writeback(root, leaf); - - wret = btrfs_del_leaf(trans, root, path, leaf); - BUG_ON(ret); - if (wret) - ret = wret; - } - } else { - int used = leaf_space_used(leaf, 0, nritems); - if (slot == 0) { - struct btrfs_disk_key disk_key; - - btrfs_item_key(leaf, &disk_key, 0); - wret = fixup_low_keys(trans, root, path, - &disk_key, 1); - if (wret) - ret = wret; - } - - /* delete the leaf if it is mostly empty */ - if (used < BTRFS_LEAF_DATA_SIZE(root) / 4) { - /* push_leaf_left fixes the path. - * make sure the path still points to our leaf - * for possible call to del_ptr below - */ - slot = path->slots[1]; - extent_buffer_get(leaf); - - wret = push_leaf_left(trans, root, path, 1, 1); - if (wret < 0 && wret != -ENOSPC) - ret = wret; - - if (path->nodes[0] == leaf && - btrfs_header_nritems(leaf)) { - wret = push_leaf_right(trans, root, path, 1, 1); - if (wret < 0 && wret != -ENOSPC) - ret = wret; - } - - if (btrfs_header_nritems(leaf) == 0) { - clean_tree_block(trans, root, leaf); - wait_on_tree_block_writeback(root, leaf); - - path->slots[1] = slot; - ret = btrfs_del_leaf(trans, root, path, leaf); - BUG_ON(ret); - free_extent_buffer(leaf); - - } else { - btrfs_mark_buffer_dirty(leaf); - free_extent_buffer(leaf); - } - } else { - btrfs_mark_buffer_dirty(leaf); - } - } - return ret; -} - -/* - * walk up the tree as far as required to find the previous leaf. - * returns 0 if it found something or 1 if there are no lesser leaves. - * returns < 0 on io errors. - */ -int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path) -{ - int slot; - int level = 1; - struct extent_buffer *c; - struct extent_buffer *next = NULL; - - while(level < BTRFS_MAX_LEVEL) { - if (!path->nodes[level]) - return 1; - - slot = path->slots[level]; - c = path->nodes[level]; - if (slot == 0) { - level++; - if (level == BTRFS_MAX_LEVEL) - return 1; - continue; - } - slot--; - - if (next) - free_extent_buffer(next); - - next = read_node_slot(root, c, slot); - break; - } - path->slots[level] = slot; - while(1) { - level--; - c = path->nodes[level]; - free_extent_buffer(c); - slot = btrfs_header_nritems(next); - if (slot != 0) - slot--; - path->nodes[level] = next; - path->slots[level] = slot; - if (!level) - break; - next = read_node_slot(root, next, slot); - } - return 0; -} - -/* - * walk up the tree as far as required to find the next leaf. - * returns 0 if it found something or 1 if there are no greater leaves. - * returns < 0 on io errors. - */ -int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path) -{ - int slot; - int level = 1; - struct extent_buffer *c; - struct extent_buffer *next = NULL; - - while(level < BTRFS_MAX_LEVEL) { - if (!path->nodes[level]) - return 1; - - slot = path->slots[level] + 1; - c = path->nodes[level]; - if (slot >= btrfs_header_nritems(c)) { - level++; - if (level == BTRFS_MAX_LEVEL) - return 1; - continue; - } - - if (next) - free_extent_buffer(next); - - if (path->reada) - reada_for_search(root, path, level, slot, 0); - - next = read_node_slot(root, c, slot); - break; - } - path->slots[level] = slot; - while(1) { - level--; - c = path->nodes[level]; - free_extent_buffer(c); - path->nodes[level] = next; - path->slots[level] = 0; - if (!level) - break; - if (path->reada) - reada_for_search(root, path, level, 0, 0); - next = read_node_slot(root, next, 0); - } - return 0; -} - -int btrfs_previous_item(struct btrfs_root *root, - struct btrfs_path *path, u64 min_objectid, - int type) -{ - struct btrfs_key found_key; - struct extent_buffer *leaf; - int ret; - - while(1) { - if (path->slots[0] == 0) { - ret = btrfs_prev_leaf(root, path); - if (ret != 0) - return ret; - } else { - path->slots[0]--; - } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); - if (found_key.type == type) - return 0; - } - return 1; -} - diff --git a/ctree.h b/ctree.h deleted file mode 100644 index a9062ea..0000000 --- a/ctree.h +++ /dev/null @@ -1,1882 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __BTRFS__ -#define __BTRFS__ - -#include "list.h" -#include "kerncompat.h" -#include "radix-tree.h" -#include "extent-cache.h" -#include "extent_io.h" - -struct btrfs_root; -struct btrfs_trans_handle; -#define BTRFS_MAGIC "_BHRfS_M" - -#define BTRFS_MAX_LEVEL 8 - -#define BTRFS_COMPAT_EXTENT_TREE_V0 - -/* holds pointers to all of the tree roots */ -#define BTRFS_ROOT_TREE_OBJECTID 1ULL - -/* stores information about which extents are in use, and reference counts */ -#define BTRFS_EXTENT_TREE_OBJECTID 2ULL - -/* - * chunk tree stores translations from logical -> physical block numbering - * the super block points to the chunk tree - */ -#define BTRFS_CHUNK_TREE_OBJECTID 3ULL - -/* - * stores information about which areas of a given device are in use. - * one per device. The tree of tree roots points to the device tree - */ -#define BTRFS_DEV_TREE_OBJECTID 4ULL - -/* one per subvolume, storing files and directories */ -#define BTRFS_FS_TREE_OBJECTID 5ULL - -/* directory objectid inside the root tree */ -#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL -/* holds checksums of all the data extents */ -#define BTRFS_CSUM_TREE_OBJECTID 7ULL - - -/* oprhan objectid for tracking unlinked/truncated files */ -#define BTRFS_ORPHAN_OBJECTID -5ULL - -/* does write ahead logging to speed up fsyncs */ -#define BTRFS_TREE_LOG_OBJECTID -6ULL -#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL - -/* space balancing */ -#define BTRFS_TREE_RELOC_OBJECTID -8ULL -#define BTRFS_DATA_RELOC_TREE_OBJECTID -9ULL - -/* - * extent checksums all have this objectid - * this allows them to share the logging tree - * for fsyncs - */ -#define BTRFS_EXTENT_CSUM_OBJECTID -10ULL - -/* dummy objectid represents multiple objectids */ -#define BTRFS_MULTIPLE_OBJECTIDS -255ULL - -/* - * All files have objectids in this range. - */ -#define BTRFS_FIRST_FREE_OBJECTID 256ULL -#define BTRFS_LAST_FREE_OBJECTID -256ULL -#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL - - - -/* - * the device items go into the chunk tree. The key is in the form - * [ 1 BTRFS_DEV_ITEM_KEY device_id ] - */ -#define BTRFS_DEV_ITEMS_OBJECTID 1ULL - -/* - * we can actually store much bigger names, but lets not confuse the rest - * of linux - */ -#define BTRFS_NAME_LEN 255 - -/* 32 bytes in various csum fields */ -#define BTRFS_CSUM_SIZE 32 - -/* csum types */ -#define BTRFS_CSUM_TYPE_CRC32 0 - - -/* csum types */ -#define BTRFS_CSUM_TYPE_CRC32 0 - -static int btrfs_csum_sizes[] = { 4, 0 }; - -/* four bytes for CRC32 */ -#define BTRFS_CRC32_SIZE 4 -#define BTRFS_EMPTY_DIR_SIZE 0 - -#define BTRFS_FT_UNKNOWN 0 -#define BTRFS_FT_REG_FILE 1 -#define BTRFS_FT_DIR 2 -#define BTRFS_FT_CHRDEV 3 -#define BTRFS_FT_BLKDEV 4 -#define BTRFS_FT_FIFO 5 -#define BTRFS_FT_SOCK 6 -#define BTRFS_FT_SYMLINK 7 -#define BTRFS_FT_XATTR 8 -#define BTRFS_FT_MAX 9 - -/* - * the key defines the order in the tree, and so it also defines (optimal) - * block layout. objectid corresonds to the inode number. The flags - * tells us things about the object, and is a kind of stream selector. - * so for a given inode, keys with flags of 1 might refer to the inode - * data, flags of 2 may point to file data in the btree and flags == 3 - * may point to extents. - * - * offset is the starting byte offset for this key in the stream. - * - * btrfs_disk_key is in disk byte order. struct btrfs_key is always - * in cpu native order. Otherwise they are identical and their sizes - * should be the same (ie both packed) - */ -struct btrfs_disk_key { - __le64 objectid; - u8 type; - __le64 offset; -} __attribute__ ((__packed__)); - -struct btrfs_key { - u64 objectid; - u8 type; - u64 offset; -} __attribute__ ((__packed__)); - -struct btrfs_mapping_tree { - struct cache_tree cache_tree; -}; - -#define BTRFS_UUID_SIZE 16 -struct btrfs_dev_item { - /* the internal btrfs device id */ - __le64 devid; - - /* size of the device */ - __le64 total_bytes; - - /* bytes used */ - __le64 bytes_used; - - /* optimal io alignment for this device */ - __le32 io_align; - - /* optimal io width for this device */ - __le32 io_width; - - /* minimal io size for this device */ - __le32 sector_size; - - /* type and info about this device */ - __le64 type; - - /* expected generation for this device */ - __le64 generation; - - /* - * starting byte of this partition on the device, - * to allowr for stripe alignment in the future - */ - __le64 start_offset; - - /* grouping information for allocation decisions */ - __le32 dev_group; - - /* seek speed 0-100 where 100 is fastest */ - u8 seek_speed; - - /* bandwidth 0-100 where 100 is fastest */ - u8 bandwidth; - - /* btrfs generated uuid for this device */ - u8 uuid[BTRFS_UUID_SIZE]; - - /* uuid of FS who owns this device */ - u8 fsid[BTRFS_UUID_SIZE]; -} __attribute__ ((__packed__)); - -struct btrfs_stripe { - __le64 devid; - __le64 offset; - u8 dev_uuid[BTRFS_UUID_SIZE]; -} __attribute__ ((__packed__)); - -struct btrfs_chunk { - /* size of this chunk in bytes */ - __le64 length; - - /* objectid of the root referencing this chunk */ - __le64 owner; - - __le64 stripe_len; - __le64 type; - - /* optimal io alignment for this chunk */ - __le32 io_align; - - /* optimal io width for this chunk */ - __le32 io_width; - - /* minimal io size for this chunk */ - __le32 sector_size; - - /* 2^16 stripes is quite a lot, a second limit is the size of a single - * item in the btree - */ - __le16 num_stripes; - - /* sub stripes only matter for raid10 */ - __le16 sub_stripes; - struct btrfs_stripe stripe; - /* additional stripes go here */ -} __attribute__ ((__packed__)); - -static inline unsigned long btrfs_chunk_item_size(int num_stripes) -{ - BUG_ON(num_stripes == 0); - return sizeof(struct btrfs_chunk) + - sizeof(struct btrfs_stripe) * (num_stripes - 1); -} - -#define BTRFS_FSID_SIZE 16 -#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0) -#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1) -#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32) -#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33) - -#define BTRFS_BACKREF_REV_MAX 256 -#define BTRFS_BACKREF_REV_SHIFT 56 -#define BTRFS_BACKREF_REV_MASK (((u64)BTRFS_BACKREF_REV_MAX - 1) << \ - BTRFS_BACKREF_REV_SHIFT) - -#define BTRFS_OLD_BACKREF_REV 0 -#define BTRFS_MIXED_BACKREF_REV 1 - -/* - * every tree block (leaf or node) starts with this header. - */ -struct btrfs_header { - /* these first four must match the super block */ - u8 csum[BTRFS_CSUM_SIZE]; - u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ - __le64 bytenr; /* which block this node is supposed to live in */ - __le64 flags; - - /* allowed to be different from the super from here on down */ - u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; - __le64 generation; - __le64 owner; - __le32 nritems; - u8 level; -} __attribute__ ((__packed__)); - -#define BTRFS_NODEPTRS_PER_BLOCK(r) (((r)->nodesize - \ - sizeof(struct btrfs_header)) / \ - sizeof(struct btrfs_key_ptr)) -#define __BTRFS_LEAF_DATA_SIZE(bs) ((bs) - sizeof(struct btrfs_header)) -#define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->leafsize)) -#define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \ - sizeof(struct btrfs_item) - \ - sizeof(struct btrfs_file_extent_item)) - - -/* - * this is a very generous portion of the super block, giving us - * room to translate 14 chunks with 3 stripes each. - */ -#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048 -#define BTRFS_LABEL_SIZE 256 - -/* - * the super block basically lists the main trees of the FS - * it currently lacks any block count etc etc - */ -struct btrfs_super_block { - u8 csum[BTRFS_CSUM_SIZE]; - /* the first 3 fields must match struct btrfs_header */ - u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ - __le64 bytenr; /* this block number */ - __le64 flags; - - /* allowed to be different from the btrfs_header from here own down */ - __le64 magic; - __le64 generation; - __le64 root; - __le64 chunk_root; - __le64 log_root; - - /* this will help find the new super based on the log root */ - __le64 log_root_transid; - __le64 total_bytes; - __le64 bytes_used; - __le64 root_dir_objectid; - __le64 num_devices; - __le32 sectorsize; - __le32 nodesize; - __le32 leafsize; - __le32 stripesize; - __le32 sys_chunk_array_size; - __le64 chunk_root_generation; - __le64 compat_flags; - __le64 compat_ro_flags; - __le64 incompat_flags; - __le16 csum_type; - u8 root_level; - u8 chunk_root_level; - u8 log_root_level; - struct btrfs_dev_item dev_item; - - char label[BTRFS_LABEL_SIZE]; - - /* future expansion */ - __le64 reserved[32]; - u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE]; -} __attribute__ ((__packed__)); - -/* - * Compat flags that we support. If any incompat flags are set other than the - * ones specified below then we will fail to mount - */ -#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0) - -#define BTRFS_FEATURE_COMPAT_SUPP 0ULL -#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL -#define BTRFS_FEATURE_INCOMPAT_SUPP \ - BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF - -/* - * A leaf is full of items. offset and size tell us where to find - * the item in the leaf (relative to the start of the data area) - */ -struct btrfs_item { - struct btrfs_disk_key key; - __le32 offset; - __le32 size; -} __attribute__ ((__packed__)); - -/* - * leaves have an item area and a data area: - * [item0, item1....itemN] [free space] [dataN...data1, data0] - * - * The data is separate from the items to get the keys closer together - * during searches. - */ -struct btrfs_leaf { - struct btrfs_header header; - struct btrfs_item items[]; -} __attribute__ ((__packed__)); - -/* - * all non-leaf blocks are nodes, they hold only keys and pointers to - * other blocks - */ -struct btrfs_key_ptr { - struct btrfs_disk_key key; - __le64 blockptr; - __le64 generation; -} __attribute__ ((__packed__)); - -struct btrfs_node { - struct btrfs_header header; - struct btrfs_key_ptr ptrs[]; -} __attribute__ ((__packed__)); - -/* - * btrfs_paths remember the path taken from the root down to the leaf. - * level 0 is always the leaf, and nodes[1...BTRFS_MAX_LEVEL] will point - * to any other levels that are present. - * - * The slots array records the index of the item or block pointer - * used while walking the tree. - */ - -struct btrfs_path { - struct extent_buffer *nodes[BTRFS_MAX_LEVEL]; - int slots[BTRFS_MAX_LEVEL]; - /* if there is real range locking, this locks field will change */ - int locks[BTRFS_MAX_LEVEL]; - int reada; - /* keep some upper locks as we walk down */ - int lowest_level; - - /* - * set by btrfs_split_item, tells search_slot to keep all locks - * and to force calls to keep space in the nodes - */ - unsigned int search_for_split:1; - unsigned int keep_locks:1; - unsigned int skip_locking:1; - unsigned int leave_spinning:1; -}; - -/* - * items in the extent btree are used to record the objectid of the - * owner of the block and the number of references - */ - -struct btrfs_extent_item { - __le64 refs; - __le64 generation; - __le64 flags; -} __attribute__ ((__packed__)); - -struct btrfs_extent_item_v0 { - __le32 refs; -} __attribute__ ((__packed__)); - -#define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r) >> 4) - \ - sizeof(struct btrfs_item)) - -#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0) -#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1) - -/* following flags only apply to tree blocks */ - -/* use full backrefs for extent pointers in the block*/ -#define BTRFS_BLOCK_FLAG_FULL_BACKREF (1ULL << 8) - -struct btrfs_tree_block_info { - struct btrfs_disk_key key; - u8 level; -} __attribute__ ((__packed__)); - -struct btrfs_extent_data_ref { - __le64 root; - __le64 objectid; - __le64 offset; - __le32 count; -} __attribute__ ((__packed__)); - -struct btrfs_shared_data_ref { - __le32 count; -} __attribute__ ((__packed__)); - -struct btrfs_extent_inline_ref { - u8 type; - u64 offset; -} __attribute__ ((__packed__)); - -struct btrfs_extent_ref_v0 { - __le64 root; - __le64 generation; - __le64 objectid; - __le32 count; -} __attribute__ ((__packed__)); - -/* dev extents record free space on individual devices. The owner - * field points back to the chunk allocation mapping tree that allocated - * the extent. The chunk tree uuid field is a way to double check the owner - */ -struct btrfs_dev_extent { - __le64 chunk_tree; - __le64 chunk_objectid; - __le64 chunk_offset; - __le64 length; - u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; -} __attribute__ ((__packed__)); - -struct btrfs_inode_ref { - __le64 index; - __le16 name_len; - /* name goes here */ -} __attribute__ ((__packed__)); - -struct btrfs_timespec { - __le64 sec; - __le32 nsec; -} __attribute__ ((__packed__)); - -typedef enum { - BTRFS_COMPRESS_NONE = 0, - BTRFS_COMPRESS_ZLIB = 1, - BTRFS_COMPRESS_LAST = 2, -} btrfs_compression_type; - -/* we don't understand any encryption methods right now */ -typedef enum { - BTRFS_ENCRYPTION_NONE = 0, - BTRFS_ENCRYPTION_LAST = 1, -} btrfs_encryption_type; - -struct btrfs_inode_item { - /* nfs style generation number */ - __le64 generation; - /* transid that last touched this inode */ - __le64 transid; - __le64 size; - __le64 nbytes; - __le64 block_group; - __le32 nlink; - __le32 uid; - __le32 gid; - __le32 mode; - __le64 rdev; - __le64 flags; - - /* modification sequence number for NFS */ - __le64 sequence; - - /* - * a little future expansion, for more than this we can - * just grow the inode item and version it - */ - __le64 reserved[4]; - struct btrfs_timespec atime; - struct btrfs_timespec ctime; - struct btrfs_timespec mtime; - struct btrfs_timespec otime; -} __attribute__ ((__packed__)); - -struct btrfs_dir_log_item { - __le64 end; -} __attribute__ ((__packed__)); - -struct btrfs_dir_item { - struct btrfs_disk_key location; - __le64 transid; - __le16 data_len; - __le16 name_len; - u8 type; -} __attribute__ ((__packed__)); - -struct btrfs_root_item { - struct btrfs_inode_item inode; - __le64 generation; - __le64 root_dirid; - __le64 bytenr; - __le64 byte_limit; - __le64 bytes_used; - __le64 last_snapshot; - __le64 flags; - __le32 refs; - struct btrfs_disk_key drop_progress; - u8 drop_level; - u8 level; -} __attribute__ ((__packed__)); - -/* - * this is used for both forward and backward root refs - */ -struct btrfs_root_ref { - __le64 dirid; - __le64 sequence; - __le16 name_len; -} __attribute__ ((__packed__)); - -#define BTRFS_FILE_EXTENT_INLINE 0 -#define BTRFS_FILE_EXTENT_REG 1 -#define BTRFS_FILE_EXTENT_PREALLOC 2 - -struct btrfs_file_extent_item { - /* - * transaction id that created this extent - */ - __le64 generation; - /* - * max number of bytes to hold this extent in ram - * when we split a compressed extent we can't know how big - * each of the resulting pieces will be. So, this is - * an upper limit on the size of the extent in ram instead of - * an exact limit. - */ - __le64 ram_bytes; - - /* - * 32 bits for the various ways we might encode the data, - * including compression and encryption. If any of these - * are set to something a given disk format doesn't understand - * it is treated like an incompat flag for reading and writing, - * but not for stat. - */ - u8 compression; - u8 encryption; - __le16 other_encoding; /* spare for later use */ - - /* are we inline data or a real extent? */ - u8 type; - - /* - * disk space consumed by the extent, checksum blocks are included - * in these numbers - */ - __le64 disk_bytenr; - __le64 disk_num_bytes; - /* - * the logical offset in file blocks (no csums) - * this extent record is for. This allows a file extent to point - * into the middle of an existing extent on disk, sharing it - * between two snapshots (useful if some bytes in the middle of the - * extent have changed - */ - __le64 offset; - /* - * the logical number of file blocks (no csums included) - */ - __le64 num_bytes; - -} __attribute__ ((__packed__)); - -struct btrfs_csum_item { - u8 csum; -} __attribute__ ((__packed__)); - -/* tag for the radix tree of block groups in ram */ -#define BTRFS_BLOCK_GROUP_DATA (1 << 0) -#define BTRFS_BLOCK_GROUP_SYSTEM (1 << 1) -#define BTRFS_BLOCK_GROUP_METADATA (1 << 2) -#define BTRFS_BLOCK_GROUP_RAID0 (1 << 3) -#define BTRFS_BLOCK_GROUP_RAID1 (1 << 4) -#define BTRFS_BLOCK_GROUP_DUP (1 << 5) -#define BTRFS_BLOCK_GROUP_RAID10 (1 << 6) - -struct btrfs_block_group_item { - __le64 used; - __le64 chunk_objectid; - __le64 flags; -} __attribute__ ((__packed__)); - -struct btrfs_space_info { - u64 flags; - u64 total_bytes; - u64 bytes_used; - u64 bytes_pinned; - int full; - struct list_head list; -}; - -struct btrfs_block_group_cache { - struct cache_extent cache; - struct btrfs_key key; - struct btrfs_block_group_item item; - struct btrfs_space_info *space_info; - u64 pinned; - u64 flags; - int cached; - int ro; -}; - -struct btrfs_extent_ops { - int (*alloc_extent)(struct btrfs_root *root, u64 num_bytes, - u64 hint_byte, struct btrfs_key *ins); - int (*free_extent)(struct btrfs_root *root, u64 bytenr, - u64 num_bytes); -}; - -struct btrfs_device; -struct btrfs_fs_devices; -struct btrfs_fs_info { - u8 fsid[BTRFS_FSID_SIZE]; - u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; - struct btrfs_root *fs_root; - struct btrfs_root *extent_root; - struct btrfs_root *tree_root; - struct btrfs_root *chunk_root; - struct btrfs_root *dev_root; - struct btrfs_root *csum_root; - - struct cache_tree fs_root_cache; - - /* the log root tree is a directory of all the other log roots */ - struct btrfs_root *log_root_tree; - - struct extent_io_tree extent_cache; - struct extent_io_tree free_space_cache; - struct extent_io_tree block_group_cache; - struct extent_io_tree pinned_extents; - struct extent_io_tree pending_del; - struct extent_io_tree extent_ins; - - /* logical->physical extent mapping */ - struct btrfs_mapping_tree mapping_tree; - - u64 generation; - u64 last_trans_committed; - - u64 avail_data_alloc_bits; - u64 avail_metadata_alloc_bits; - u64 avail_system_alloc_bits; - u64 data_alloc_profile; - u64 metadata_alloc_profile; - u64 system_alloc_profile; - u64 alloc_start; - - struct btrfs_trans_handle *running_transaction; - struct btrfs_super_block super_copy; - struct mutex fs_mutex; - - u64 super_bytenr; - u64 total_pinned; - - struct btrfs_extent_ops *extent_ops; - struct list_head dirty_cowonly_roots; - - struct btrfs_fs_devices *fs_devices; - struct list_head space_info; - int system_allocs; - int readonly; -}; - -/* - * in ram representation of the tree. extent_root is used for all allocations - * and for the extent tree extent_root root. - */ -struct btrfs_root { - struct extent_buffer *node; - struct extent_buffer *commit_root; - struct btrfs_root_item root_item; - struct btrfs_key root_key; - struct btrfs_fs_info *fs_info; - u64 objectid; - u64 last_trans; - - /* data allocations are done in sectorsize units */ - u32 sectorsize; - - /* node allocations are done in nodesize units */ - u32 nodesize; - - /* leaf allocations are done in leafsize units */ - u32 leafsize; - - /* leaf allocations are done in leafsize units */ - u32 stripesize; - - int ref_cows; - int track_dirty; - - - u32 type; - u64 highest_inode; - u64 last_inode_alloc; - - /* the dirty list is only used by non-reference counted roots */ - struct list_head dirty_list; - struct cache_extent cache; -}; - -/* - * inode items have the data typically returned from stat and store other - * info about object characteristics. There is one for every file and dir in - * the FS - */ -#define BTRFS_INODE_ITEM_KEY 1 -#define BTRFS_INODE_REF_KEY 12 -#define BTRFS_XATTR_ITEM_KEY 24 -#define BTRFS_ORPHAN_ITEM_KEY 48 - -#define BTRFS_DIR_LOG_ITEM_KEY 60 -#define BTRFS_DIR_LOG_INDEX_KEY 72 -/* - * dir items are the name -> inode pointers in a directory. There is one - * for every name in a directory. - */ -#define BTRFS_DIR_ITEM_KEY 84 -#define BTRFS_DIR_INDEX_KEY 96 - -/* - * extent data is for file data - */ -#define BTRFS_EXTENT_DATA_KEY 108 - -/* - * csum items have the checksums for data in the extents - */ -#define BTRFS_CSUM_ITEM_KEY 120 -/* - * extent csums are stored in a separate tree and hold csums for - * an entire extent on disk. - */ -#define BTRFS_EXTENT_CSUM_KEY 128 - -/* - * root items point to tree roots. There are typically in the root - * tree used by the super block to find all the other trees - */ -#define BTRFS_ROOT_ITEM_KEY 132 - -/* - * root backrefs tie subvols and snapshots to the directory entries that - * reference them - */ -#define BTRFS_ROOT_BACKREF_KEY 144 - -/* - * root refs make a fast index for listing all of the snapshots and - * subvolumes referenced by a given root. They point directly to the - * directory item in the root that references the subvol - */ -#define BTRFS_ROOT_REF_KEY 156 - -/* - * extent items are in the extent map tree. These record which blocks - * are used, and how many references there are to each block - */ -#define BTRFS_EXTENT_ITEM_KEY 168 - -#define BTRFS_TREE_BLOCK_REF_KEY 176 - -#define BTRFS_EXTENT_DATA_REF_KEY 178 - -/* old style extent backrefs */ -#define BTRFS_EXTENT_REF_V0_KEY 180 - -#define BTRFS_SHARED_BLOCK_REF_KEY 182 - -#define BTRFS_SHARED_DATA_REF_KEY 184 - - -/* - * block groups give us hints into the extent allocation trees. Which - * blocks are free etc etc - */ -#define BTRFS_BLOCK_GROUP_ITEM_KEY 192 - -#define BTRFS_DEV_EXTENT_KEY 204 -#define BTRFS_DEV_ITEM_KEY 216 -#define BTRFS_CHUNK_ITEM_KEY 228 - -/* - * string items are for debugging. They just store a short string of - * data in the FS - */ -#define BTRFS_STRING_ITEM_KEY 253 -/* - * Inode flags - */ -#define BTRFS_INODE_NODATASUM (1 << 0) -#define BTRFS_INODE_NODATACOW (1 << 1) -#define BTRFS_INODE_READONLY (1 << 2) - -#define read_eb_member(eb, ptr, type, member, result) ( \ - read_extent_buffer(eb, (char *)(result), \ - ((unsigned long)(ptr)) + \ - offsetof(type, member), \ - sizeof(((type *)0)->member))) - -#define write_eb_member(eb, ptr, type, member, result) ( \ - write_extent_buffer(eb, (char *)(result), \ - ((unsigned long)(ptr)) + \ - offsetof(type, member), \ - sizeof(((type *)0)->member))) - -#define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits) \ -static inline u##bits btrfs_##name(struct extent_buffer *eb) \ -{ \ - struct btrfs_header *h = (struct btrfs_header *)eb->data; \ - return le##bits##_to_cpu(h->member); \ -} \ -static inline void btrfs_set_##name(struct extent_buffer *eb, \ - u##bits val) \ -{ \ - struct btrfs_header *h = (struct btrfs_header *)eb->data; \ - h->member = cpu_to_le##bits(val); \ -} - -#define BTRFS_SETGET_FUNCS(name, type, member, bits) \ -static inline u##bits btrfs_##name(struct extent_buffer *eb, \ - type *s) \ -{ \ - unsigned long offset = (unsigned long)s; \ - type *p = (type *) (eb->data + offset); \ - return le##bits##_to_cpu(p->member); \ -} \ -static inline void btrfs_set_##name(struct extent_buffer *eb, \ - type *s, u##bits val) \ -{ \ - unsigned long offset = (unsigned long)s; \ - type *p = (type *) (eb->data + offset); \ - p->member = cpu_to_le##bits(val); \ -} - -#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \ -static inline u##bits btrfs_##name(type *s) \ -{ \ - return le##bits##_to_cpu(s->member); \ -} \ -static inline void btrfs_set_##name(type *s, u##bits val) \ -{ \ - s->member = cpu_to_le##bits(val); \ -} - -BTRFS_SETGET_FUNCS(device_type, struct btrfs_dev_item, type, 64); -BTRFS_SETGET_FUNCS(device_total_bytes, struct btrfs_dev_item, total_bytes, 64); -BTRFS_SETGET_FUNCS(device_bytes_used, struct btrfs_dev_item, bytes_used, 64); -BTRFS_SETGET_FUNCS(device_io_align, struct btrfs_dev_item, io_align, 32); -BTRFS_SETGET_FUNCS(device_io_width, struct btrfs_dev_item, io_width, 32); -BTRFS_SETGET_FUNCS(device_start_offset, struct btrfs_dev_item, - start_offset, 64); -BTRFS_SETGET_FUNCS(device_sector_size, struct btrfs_dev_item, sector_size, 32); -BTRFS_SETGET_FUNCS(device_id, struct btrfs_dev_item, devid, 64); -BTRFS_SETGET_FUNCS(device_group, struct btrfs_dev_item, dev_group, 32); -BTRFS_SETGET_FUNCS(device_seek_speed, struct btrfs_dev_item, seek_speed, 8); -BTRFS_SETGET_FUNCS(device_bandwidth, struct btrfs_dev_item, bandwidth, 8); -BTRFS_SETGET_FUNCS(device_generation, struct btrfs_dev_item, generation, 64); - -BTRFS_SETGET_STACK_FUNCS(stack_device_type, struct btrfs_dev_item, type, 64); -BTRFS_SETGET_STACK_FUNCS(stack_device_total_bytes, struct btrfs_dev_item, - total_bytes, 64); -BTRFS_SETGET_STACK_FUNCS(stack_device_bytes_used, struct btrfs_dev_item, - bytes_used, 64); -BTRFS_SETGET_STACK_FUNCS(stack_device_io_align, struct btrfs_dev_item, - io_align, 32); -BTRFS_SETGET_STACK_FUNCS(stack_device_io_width, struct btrfs_dev_item, - io_width, 32); -BTRFS_SETGET_STACK_FUNCS(stack_device_sector_size, struct btrfs_dev_item, - sector_size, 32); -BTRFS_SETGET_STACK_FUNCS(stack_device_id, struct btrfs_dev_item, devid, 64); -BTRFS_SETGET_STACK_FUNCS(stack_device_group, struct btrfs_dev_item, - dev_group, 32); -BTRFS_SETGET_STACK_FUNCS(stack_device_seek_speed, struct btrfs_dev_item, - seek_speed, 8); -BTRFS_SETGET_STACK_FUNCS(stack_device_bandwidth, struct btrfs_dev_item, - bandwidth, 8); -BTRFS_SETGET_STACK_FUNCS(stack_device_generation, struct btrfs_dev_item, - generation, 64); - -static inline char *btrfs_device_uuid(struct btrfs_dev_item *d) -{ - return (char *)d + offsetof(struct btrfs_dev_item, uuid); -} - -static inline char *btrfs_device_fsid(struct btrfs_dev_item *d) -{ - return (char *)d + offsetof(struct btrfs_dev_item, fsid); -} - -BTRFS_SETGET_FUNCS(chunk_length, struct btrfs_chunk, length, 64); -BTRFS_SETGET_FUNCS(chunk_owner, struct btrfs_chunk, owner, 64); -BTRFS_SETGET_FUNCS(chunk_stripe_len, struct btrfs_chunk, stripe_len, 64); -BTRFS_SETGET_FUNCS(chunk_io_align, struct btrfs_chunk, io_align, 32); -BTRFS_SETGET_FUNCS(chunk_io_width, struct btrfs_chunk, io_width, 32); -BTRFS_SETGET_FUNCS(chunk_sector_size, struct btrfs_chunk, sector_size, 32); -BTRFS_SETGET_FUNCS(chunk_type, struct btrfs_chunk, type, 64); -BTRFS_SETGET_FUNCS(chunk_num_stripes, struct btrfs_chunk, num_stripes, 16); -BTRFS_SETGET_FUNCS(chunk_sub_stripes, struct btrfs_chunk, sub_stripes, 16); -BTRFS_SETGET_FUNCS(stripe_devid, struct btrfs_stripe, devid, 64); -BTRFS_SETGET_FUNCS(stripe_offset, struct btrfs_stripe, offset, 64); - -static inline char *btrfs_stripe_dev_uuid(struct btrfs_stripe *s) -{ - return (char *)s + offsetof(struct btrfs_stripe, dev_uuid); -} - -BTRFS_SETGET_STACK_FUNCS(stack_chunk_length, struct btrfs_chunk, length, 64); -BTRFS_SETGET_STACK_FUNCS(stack_chunk_owner, struct btrfs_chunk, owner, 64); -BTRFS_SETGET_STACK_FUNCS(stack_chunk_stripe_len, struct btrfs_chunk, - stripe_len, 64); -BTRFS_SETGET_STACK_FUNCS(stack_chunk_io_align, struct btrfs_chunk, - io_align, 32); -BTRFS_SETGET_STACK_FUNCS(stack_chunk_io_width, struct btrfs_chunk, - io_width, 32); -BTRFS_SETGET_STACK_FUNCS(stack_chunk_sector_size, struct btrfs_chunk, - sector_size, 32); -BTRFS_SETGET_STACK_FUNCS(stack_chunk_type, struct btrfs_chunk, type, 64); -BTRFS_SETGET_STACK_FUNCS(stack_chunk_num_stripes, struct btrfs_chunk, - num_stripes, 16); -BTRFS_SETGET_STACK_FUNCS(stack_chunk_sub_stripes, struct btrfs_chunk, - sub_stripes, 16); -BTRFS_SETGET_STACK_FUNCS(stack_stripe_devid, struct btrfs_stripe, devid, 64); -BTRFS_SETGET_STACK_FUNCS(stack_stripe_offset, struct btrfs_stripe, offset, 64); - -static inline struct btrfs_stripe *btrfs_stripe_nr(struct btrfs_chunk *c, - int nr) -{ - unsigned long offset = (unsigned long)c; - offset += offsetof(struct btrfs_chunk, stripe); - offset += nr * sizeof(struct btrfs_stripe); - return (struct btrfs_stripe *)offset; -} - -static inline char *btrfs_stripe_dev_uuid_nr(struct btrfs_chunk *c, int nr) -{ - return btrfs_stripe_dev_uuid(btrfs_stripe_nr(c, nr)); -} - -static inline u64 btrfs_stripe_offset_nr(struct extent_buffer *eb, - struct btrfs_chunk *c, int nr) -{ - return btrfs_stripe_offset(eb, btrfs_stripe_nr(c, nr)); -} - -static inline void btrfs_set_stripe_offset_nr(struct extent_buffer *eb, - struct btrfs_chunk *c, int nr, - u64 val) -{ - btrfs_set_stripe_offset(eb, btrfs_stripe_nr(c, nr), val); -} - -static inline u64 btrfs_stripe_devid_nr(struct extent_buffer *eb, - struct btrfs_chunk *c, int nr) -{ - return btrfs_stripe_devid(eb, btrfs_stripe_nr(c, nr)); -} - -static inline void btrfs_set_stripe_devid_nr(struct extent_buffer *eb, - struct btrfs_chunk *c, int nr, - u64 val) -{ - btrfs_set_stripe_devid(eb, btrfs_stripe_nr(c, nr), val); -} - -/* struct btrfs_block_group_item */ -BTRFS_SETGET_STACK_FUNCS(block_group_used, struct btrfs_block_group_item, - used, 64); -BTRFS_SETGET_FUNCS(disk_block_group_used, struct btrfs_block_group_item, - used, 64); -BTRFS_SETGET_STACK_FUNCS(block_group_chunk_objectid, - struct btrfs_block_group_item, chunk_objectid, 64); - -BTRFS_SETGET_FUNCS(disk_block_group_chunk_objectid, - struct btrfs_block_group_item, chunk_objectid, 64); -BTRFS_SETGET_FUNCS(disk_block_group_flags, - struct btrfs_block_group_item, flags, 64); -BTRFS_SETGET_STACK_FUNCS(block_group_flags, - struct btrfs_block_group_item, flags, 64); - -/* struct btrfs_inode_ref */ -BTRFS_SETGET_FUNCS(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_item */ -BTRFS_SETGET_FUNCS(inode_generation, struct btrfs_inode_item, generation, 64); -BTRFS_SETGET_FUNCS(inode_sequence, struct btrfs_inode_item, sequence, 64); -BTRFS_SETGET_FUNCS(inode_transid, struct btrfs_inode_item, transid, 64); -BTRFS_SETGET_FUNCS(inode_size, struct btrfs_inode_item, size, 64); -BTRFS_SETGET_FUNCS(inode_nbytes, struct btrfs_inode_item, nbytes, 64); -BTRFS_SETGET_FUNCS(inode_block_group, struct btrfs_inode_item, block_group, 64); -BTRFS_SETGET_FUNCS(inode_nlink, struct btrfs_inode_item, nlink, 32); -BTRFS_SETGET_FUNCS(inode_uid, struct btrfs_inode_item, uid, 32); -BTRFS_SETGET_FUNCS(inode_gid, struct btrfs_inode_item, gid, 32); -BTRFS_SETGET_FUNCS(inode_mode, struct btrfs_inode_item, mode, 32); -BTRFS_SETGET_FUNCS(inode_rdev, struct btrfs_inode_item, rdev, 64); -BTRFS_SETGET_FUNCS(inode_flags, struct btrfs_inode_item, flags, 64); - -BTRFS_SETGET_STACK_FUNCS(stack_inode_generation, - struct btrfs_inode_item, generation, 64); -BTRFS_SETGET_STACK_FUNCS(stack_inode_sequence, - struct btrfs_inode_item, generation, 64); -BTRFS_SETGET_STACK_FUNCS(stack_inode_size, - struct btrfs_inode_item, size, 64); -BTRFS_SETGET_STACK_FUNCS(stack_inode_nbytes, - struct btrfs_inode_item, nbytes, 64); -BTRFS_SETGET_STACK_FUNCS(stack_inode_block_group, - struct btrfs_inode_item, block_group, 64); -BTRFS_SETGET_STACK_FUNCS(stack_inode_nlink, - struct btrfs_inode_item, nlink, 32); -BTRFS_SETGET_STACK_FUNCS(stack_inode_uid, - struct btrfs_inode_item, uid, 32); -BTRFS_SETGET_STACK_FUNCS(stack_inode_gid, - struct btrfs_inode_item, gid, 32); -BTRFS_SETGET_STACK_FUNCS(stack_inode_mode, - struct btrfs_inode_item, mode, 32); -BTRFS_SETGET_STACK_FUNCS(stack_inode_rdev, - struct btrfs_inode_item, rdev, 64); -BTRFS_SETGET_STACK_FUNCS(stack_inode_flags, - struct btrfs_inode_item, flags, 64); - -static inline struct btrfs_timespec * -btrfs_inode_atime(struct btrfs_inode_item *inode_item) -{ - unsigned long ptr = (unsigned long)inode_item; - ptr += offsetof(struct btrfs_inode_item, atime); - return (struct btrfs_timespec *)ptr; -} - -static inline struct btrfs_timespec * -btrfs_inode_mtime(struct btrfs_inode_item *inode_item) -{ - unsigned long ptr = (unsigned long)inode_item; - ptr += offsetof(struct btrfs_inode_item, mtime); - return (struct btrfs_timespec *)ptr; -} - -static inline struct btrfs_timespec * -btrfs_inode_ctime(struct btrfs_inode_item *inode_item) -{ - unsigned long ptr = (unsigned long)inode_item; - ptr += offsetof(struct btrfs_inode_item, ctime); - return (struct btrfs_timespec *)ptr; -} - -static inline struct btrfs_timespec * -btrfs_inode_otime(struct btrfs_inode_item *inode_item) -{ - unsigned long ptr = (unsigned long)inode_item; - ptr += offsetof(struct btrfs_inode_item, otime); - return (struct btrfs_timespec *)ptr; -} - -BTRFS_SETGET_FUNCS(timespec_sec, struct btrfs_timespec, sec, 64); -BTRFS_SETGET_FUNCS(timespec_nsec, struct btrfs_timespec, nsec, 32); -BTRFS_SETGET_STACK_FUNCS(stack_timespec_sec, struct btrfs_timespec, - sec, 64); -BTRFS_SETGET_STACK_FUNCS(stack_timespec_nsec, struct btrfs_timespec, - nsec, 32); - -/* struct btrfs_dev_extent */ -BTRFS_SETGET_FUNCS(dev_extent_chunk_tree, struct btrfs_dev_extent, - chunk_tree, 64); -BTRFS_SETGET_FUNCS(dev_extent_chunk_objectid, struct btrfs_dev_extent, - chunk_objectid, 64); -BTRFS_SETGET_FUNCS(dev_extent_chunk_offset, struct btrfs_dev_extent, - chunk_offset, 64); -BTRFS_SETGET_FUNCS(dev_extent_length, struct btrfs_dev_extent, length, 64); - -static inline u8 *btrfs_dev_extent_chunk_tree_uuid(struct btrfs_dev_extent *dev) -{ - unsigned long ptr = offsetof(struct btrfs_dev_extent, chunk_tree_uuid); - return (u8 *)((unsigned long)dev + ptr); -} - - -/* struct btrfs_extent_item */ -BTRFS_SETGET_FUNCS(extent_refs, struct btrfs_extent_item, refs, 64); -BTRFS_SETGET_FUNCS(extent_generation, struct btrfs_extent_item, - generation, 64); -BTRFS_SETGET_FUNCS(extent_flags, struct btrfs_extent_item, flags, 64); - -BTRFS_SETGET_FUNCS(extent_refs_v0, struct btrfs_extent_item_v0, refs, 32); - -BTRFS_SETGET_FUNCS(tree_block_level, struct btrfs_tree_block_info, level, 8); - -static inline void btrfs_tree_block_key(struct extent_buffer *eb, - struct btrfs_tree_block_info *item, - struct btrfs_disk_key *key) -{ - read_eb_member(eb, item, struct btrfs_tree_block_info, key, key); -} - -static inline void btrfs_set_tree_block_key(struct extent_buffer *eb, - struct btrfs_tree_block_info *item, - struct btrfs_disk_key *key) -{ - write_eb_member(eb, item, struct btrfs_tree_block_info, key, key); -} - -BTRFS_SETGET_FUNCS(extent_data_ref_root, struct btrfs_extent_data_ref, - root, 64); -BTRFS_SETGET_FUNCS(extent_data_ref_objectid, struct btrfs_extent_data_ref, - objectid, 64); -BTRFS_SETGET_FUNCS(extent_data_ref_offset, struct btrfs_extent_data_ref, - offset, 64); -BTRFS_SETGET_FUNCS(extent_data_ref_count, struct btrfs_extent_data_ref, - count, 32); - -BTRFS_SETGET_FUNCS(shared_data_ref_count, struct btrfs_shared_data_ref, - count, 32); - -BTRFS_SETGET_FUNCS(extent_inline_ref_type, struct btrfs_extent_inline_ref, - type, 8); -BTRFS_SETGET_FUNCS(extent_inline_ref_offset, struct btrfs_extent_inline_ref, - offset, 64); - -static inline u32 btrfs_extent_inline_ref_size(int type) -{ - if (type == BTRFS_TREE_BLOCK_REF_KEY || - type == BTRFS_SHARED_BLOCK_REF_KEY) - return sizeof(struct btrfs_extent_inline_ref); - if (type == BTRFS_SHARED_DATA_REF_KEY) - return sizeof(struct btrfs_shared_data_ref) + - sizeof(struct btrfs_extent_inline_ref); - if (type == BTRFS_EXTENT_DATA_REF_KEY) - return sizeof(struct btrfs_extent_data_ref) + - offsetof(struct btrfs_extent_inline_ref, offset); - BUG(); - return 0; -} - -BTRFS_SETGET_FUNCS(ref_root_v0, struct btrfs_extent_ref_v0, root, 64); -BTRFS_SETGET_FUNCS(ref_generation_v0, struct btrfs_extent_ref_v0, - generation, 64); -BTRFS_SETGET_FUNCS(ref_objectid_v0, struct btrfs_extent_ref_v0, objectid, 64); -BTRFS_SETGET_FUNCS(ref_count_v0, struct btrfs_extent_ref_v0, count, 32); - -/* struct btrfs_node */ -BTRFS_SETGET_FUNCS(key_blockptr, struct btrfs_key_ptr, blockptr, 64); -BTRFS_SETGET_FUNCS(key_generation, struct btrfs_key_ptr, generation, 64); - -static inline u64 btrfs_node_blockptr(struct extent_buffer *eb, int nr) -{ - unsigned long ptr; - ptr = offsetof(struct btrfs_node, ptrs) + - sizeof(struct btrfs_key_ptr) * nr; - return btrfs_key_blockptr(eb, (struct btrfs_key_ptr *)ptr); -} - -static inline void btrfs_set_node_blockptr(struct extent_buffer *eb, - int nr, u64 val) -{ - unsigned long ptr; - ptr = offsetof(struct btrfs_node, ptrs) + - sizeof(struct btrfs_key_ptr) * nr; - btrfs_set_key_blockptr(eb, (struct btrfs_key_ptr *)ptr, val); -} - -static inline u64 btrfs_node_ptr_generation(struct extent_buffer *eb, int nr) -{ - unsigned long ptr; - ptr = offsetof(struct btrfs_node, ptrs) + - sizeof(struct btrfs_key_ptr) * nr; - return btrfs_key_generation(eb, (struct btrfs_key_ptr *)ptr); -} - -static inline void btrfs_set_node_ptr_generation(struct extent_buffer *eb, - int nr, u64 val) -{ - unsigned long ptr; - ptr = offsetof(struct btrfs_node, ptrs) + - sizeof(struct btrfs_key_ptr) * nr; - btrfs_set_key_generation(eb, (struct btrfs_key_ptr *)ptr, val); -} - -static inline unsigned long btrfs_node_key_ptr_offset(int nr) -{ - return offsetof(struct btrfs_node, ptrs) + - sizeof(struct btrfs_key_ptr) * nr; -} - -static inline void btrfs_node_key(struct extent_buffer *eb, - struct btrfs_disk_key *disk_key, int nr) -{ - unsigned long ptr; - ptr = btrfs_node_key_ptr_offset(nr); - read_eb_member(eb, (struct btrfs_key_ptr *)ptr, - struct btrfs_key_ptr, key, disk_key); -} - -static inline void btrfs_set_node_key(struct extent_buffer *eb, - struct btrfs_disk_key *disk_key, int nr) -{ - unsigned long ptr; - ptr = btrfs_node_key_ptr_offset(nr); - write_eb_member(eb, (struct btrfs_key_ptr *)ptr, - struct btrfs_key_ptr, key, disk_key); -} - -/* struct btrfs_item */ -BTRFS_SETGET_FUNCS(item_offset, struct btrfs_item, offset, 32); -BTRFS_SETGET_FUNCS(item_size, struct btrfs_item, size, 32); - -static inline unsigned long btrfs_item_nr_offset(int nr) -{ - return offsetof(struct btrfs_leaf, items) + - sizeof(struct btrfs_item) * nr; -} - -static inline struct btrfs_item *btrfs_item_nr(struct extent_buffer *eb, - int nr) -{ - return (struct btrfs_item *)btrfs_item_nr_offset(nr); -} - -static inline u32 btrfs_item_end(struct extent_buffer *eb, - struct btrfs_item *item) -{ - return btrfs_item_offset(eb, item) + btrfs_item_size(eb, item); -} - -static inline u32 btrfs_item_end_nr(struct extent_buffer *eb, int nr) -{ - return btrfs_item_end(eb, btrfs_item_nr(eb, nr)); -} - -static inline u32 btrfs_item_offset_nr(struct extent_buffer *eb, int nr) -{ - return btrfs_item_offset(eb, btrfs_item_nr(eb, nr)); -} - -static inline u32 btrfs_item_size_nr(struct extent_buffer *eb, int nr) -{ - return btrfs_item_size(eb, btrfs_item_nr(eb, nr)); -} - -static inline void btrfs_item_key(struct extent_buffer *eb, - struct btrfs_disk_key *disk_key, int nr) -{ - struct btrfs_item *item = btrfs_item_nr(eb, nr); - read_eb_member(eb, item, struct btrfs_item, key, disk_key); -} - -static inline void btrfs_set_item_key(struct extent_buffer *eb, - struct btrfs_disk_key *disk_key, int nr) -{ - struct btrfs_item *item = btrfs_item_nr(eb, nr); - write_eb_member(eb, item, struct btrfs_item, key, disk_key); -} - -BTRFS_SETGET_FUNCS(dir_log_end, struct btrfs_dir_log_item, end, 64); - -/* - * struct btrfs_root_ref - */ -BTRFS_SETGET_FUNCS(root_ref_dirid, struct btrfs_root_ref, dirid, 64); -BTRFS_SETGET_FUNCS(root_ref_sequence, struct btrfs_root_ref, sequence, 64); -BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16); - -/* struct btrfs_dir_item */ -BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16); -BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8); -BTRFS_SETGET_FUNCS(dir_name_len, struct btrfs_dir_item, name_len, 16); -BTRFS_SETGET_FUNCS(dir_transid, struct btrfs_dir_item, transid, 64); - -static inline void btrfs_dir_item_key(struct extent_buffer *eb, - struct btrfs_dir_item *item, - struct btrfs_disk_key *key) -{ - read_eb_member(eb, item, struct btrfs_dir_item, location, key); -} - -static inline void btrfs_set_dir_item_key(struct extent_buffer *eb, - struct btrfs_dir_item *item, - struct btrfs_disk_key *key) -{ - write_eb_member(eb, item, struct btrfs_dir_item, location, key); -} - -/* struct btrfs_disk_key */ -BTRFS_SETGET_STACK_FUNCS(disk_key_objectid, struct btrfs_disk_key, - objectid, 64); -BTRFS_SETGET_STACK_FUNCS(disk_key_offset, struct btrfs_disk_key, offset, 64); -BTRFS_SETGET_STACK_FUNCS(disk_key_type, struct btrfs_disk_key, type, 8); - -static inline void btrfs_disk_key_to_cpu(struct btrfs_key *cpu, - struct btrfs_disk_key *disk) -{ - cpu->offset = le64_to_cpu(disk->offset); - cpu->type = disk->type; - cpu->objectid = le64_to_cpu(disk->objectid); -} - -static inline void btrfs_cpu_key_to_disk(struct btrfs_disk_key *disk, - struct btrfs_key *cpu) -{ - disk->offset = cpu_to_le64(cpu->offset); - disk->type = cpu->type; - disk->objectid = cpu_to_le64(cpu->objectid); -} - -static inline void btrfs_node_key_to_cpu(struct extent_buffer *eb, - struct btrfs_key *key, int nr) -{ - struct btrfs_disk_key disk_key; - btrfs_node_key(eb, &disk_key, nr); - btrfs_disk_key_to_cpu(key, &disk_key); -} - -static inline void btrfs_item_key_to_cpu(struct extent_buffer *eb, - struct btrfs_key *key, int nr) -{ - struct btrfs_disk_key disk_key; - btrfs_item_key(eb, &disk_key, nr); - btrfs_disk_key_to_cpu(key, &disk_key); -} - -static inline void btrfs_dir_item_key_to_cpu(struct extent_buffer *eb, - struct btrfs_dir_item *item, - struct btrfs_key *key) -{ - struct btrfs_disk_key disk_key; - btrfs_dir_item_key(eb, item, &disk_key); - btrfs_disk_key_to_cpu(key, &disk_key); -} - - -static inline u8 btrfs_key_type(struct btrfs_key *key) -{ - return key->type; -} - -static inline void btrfs_set_key_type(struct btrfs_key *key, u8 val) -{ - key->type = val; -} - -/* struct btrfs_header */ -BTRFS_SETGET_HEADER_FUNCS(header_bytenr, struct btrfs_header, bytenr, 64); -BTRFS_SETGET_HEADER_FUNCS(header_generation, struct btrfs_header, - generation, 64); -BTRFS_SETGET_HEADER_FUNCS(header_owner, struct btrfs_header, owner, 64); -BTRFS_SETGET_HEADER_FUNCS(header_nritems, struct btrfs_header, nritems, 32); -BTRFS_SETGET_HEADER_FUNCS(header_flags, struct btrfs_header, flags, 64); -BTRFS_SETGET_HEADER_FUNCS(header_level, struct btrfs_header, level, 8); - -static inline int btrfs_header_flag(struct extent_buffer *eb, u64 flag) -{ - return (btrfs_header_flags(eb) & flag) == flag; -} - -static inline int btrfs_set_header_flag(struct extent_buffer *eb, u64 flag) -{ - u64 flags = btrfs_header_flags(eb); - btrfs_set_header_flags(eb, flags | flag); - return (flags & flag) == flag; -} - -static inline int btrfs_clear_header_flag(struct extent_buffer *eb, u64 flag) -{ - u64 flags = btrfs_header_flags(eb); - btrfs_set_header_flags(eb, flags & ~flag); - return (flags & flag) == flag; -} - -static inline int btrfs_header_backref_rev(struct extent_buffer *eb) -{ - u64 flags = btrfs_header_flags(eb); - return flags >> BTRFS_BACKREF_REV_SHIFT; -} - -static inline void btrfs_set_header_backref_rev(struct extent_buffer *eb, - int rev) -{ - u64 flags = btrfs_header_flags(eb); - flags &= ~BTRFS_BACKREF_REV_MASK; - flags |= (u64)rev << BTRFS_BACKREF_REV_SHIFT; - btrfs_set_header_flags(eb, flags); -} - -static inline u8 *btrfs_header_fsid(struct extent_buffer *eb) -{ - unsigned long ptr = offsetof(struct btrfs_header, fsid); - return (u8 *)ptr; -} - -static inline u8 *btrfs_header_chunk_tree_uuid(struct extent_buffer *eb) -{ - unsigned long ptr = offsetof(struct btrfs_header, chunk_tree_uuid); - return (u8 *)ptr; -} - -static inline u8 *btrfs_super_fsid(struct extent_buffer *eb) -{ - unsigned long ptr = offsetof(struct btrfs_super_block, fsid); - return (u8 *)ptr; -} - -static inline u8 *btrfs_header_csum(struct extent_buffer *eb) -{ - unsigned long ptr = offsetof(struct btrfs_header, csum); - return (u8 *)ptr; -} - -static inline struct btrfs_node *btrfs_buffer_node(struct extent_buffer *eb) -{ - return NULL; -} - -static inline struct btrfs_leaf *btrfs_buffer_leaf(struct extent_buffer *eb) -{ - return NULL; -} - -static inline struct btrfs_header *btrfs_buffer_header(struct extent_buffer *eb) -{ - return NULL; -} - -static inline int btrfs_is_leaf(struct extent_buffer *eb) -{ - return (btrfs_header_level(eb) == 0); -} - -/* struct btrfs_root_item */ -BTRFS_SETGET_FUNCS(disk_root_generation, struct btrfs_root_item, - generation, 64); -BTRFS_SETGET_FUNCS(disk_root_refs, struct btrfs_root_item, refs, 32); -BTRFS_SETGET_FUNCS(disk_root_bytenr, struct btrfs_root_item, bytenr, 64); -BTRFS_SETGET_FUNCS(disk_root_level, struct btrfs_root_item, level, 8); - -BTRFS_SETGET_STACK_FUNCS(root_generation, struct btrfs_root_item, - generation, 64); -BTRFS_SETGET_STACK_FUNCS(root_bytenr, struct btrfs_root_item, bytenr, 64); -BTRFS_SETGET_STACK_FUNCS(root_level, struct btrfs_root_item, level, 8); -BTRFS_SETGET_STACK_FUNCS(root_dirid, struct btrfs_root_item, root_dirid, 64); -BTRFS_SETGET_STACK_FUNCS(root_refs, struct btrfs_root_item, refs, 32); -BTRFS_SETGET_STACK_FUNCS(root_flags, struct btrfs_root_item, flags, 64); -BTRFS_SETGET_STACK_FUNCS(root_used, struct btrfs_root_item, bytes_used, 64); -BTRFS_SETGET_STACK_FUNCS(root_limit, struct btrfs_root_item, byte_limit, 64); -BTRFS_SETGET_STACK_FUNCS(root_last_snapshot, struct btrfs_root_item, - last_snapshot, 64); - - -/* struct btrfs_super_block */ - -BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64); -BTRFS_SETGET_STACK_FUNCS(super_flags, struct btrfs_super_block, flags, 64); -BTRFS_SETGET_STACK_FUNCS(super_generation, struct btrfs_super_block, - generation, 64); -BTRFS_SETGET_STACK_FUNCS(super_root, struct btrfs_super_block, root, 64); -BTRFS_SETGET_STACK_FUNCS(super_sys_array_size, - struct btrfs_super_block, sys_chunk_array_size, 32); -BTRFS_SETGET_STACK_FUNCS(super_chunk_root_generation, - struct btrfs_super_block, chunk_root_generation, 64); -BTRFS_SETGET_STACK_FUNCS(super_root_level, struct btrfs_super_block, - root_level, 8); -BTRFS_SETGET_STACK_FUNCS(super_chunk_root, struct btrfs_super_block, - chunk_root, 64); -BTRFS_SETGET_STACK_FUNCS(super_chunk_root_level, struct btrfs_super_block, - chunk_root_level, 8); -BTRFS_SETGET_STACK_FUNCS(super_log_root, struct btrfs_super_block, - log_root, 64); -BTRFS_SETGET_STACK_FUNCS(super_log_root_transid, struct btrfs_super_block, - log_root_transid, 64); -BTRFS_SETGET_STACK_FUNCS(super_log_root_level, struct btrfs_super_block, - log_root_level, 8); -BTRFS_SETGET_STACK_FUNCS(super_total_bytes, struct btrfs_super_block, - total_bytes, 64); -BTRFS_SETGET_STACK_FUNCS(super_bytes_used, struct btrfs_super_block, - bytes_used, 64); -BTRFS_SETGET_STACK_FUNCS(super_sectorsize, struct btrfs_super_block, - sectorsize, 32); -BTRFS_SETGET_STACK_FUNCS(super_nodesize, struct btrfs_super_block, - nodesize, 32); -BTRFS_SETGET_STACK_FUNCS(super_leafsize, struct btrfs_super_block, - leafsize, 32); -BTRFS_SETGET_STACK_FUNCS(super_stripesize, struct btrfs_super_block, - stripesize, 32); -BTRFS_SETGET_STACK_FUNCS(super_root_dir, struct btrfs_super_block, - root_dir_objectid, 64); -BTRFS_SETGET_STACK_FUNCS(super_num_devices, struct btrfs_super_block, - num_devices, 64); -BTRFS_SETGET_STACK_FUNCS(super_compat_flags, struct btrfs_super_block, - compat_flags, 64); -BTRFS_SETGET_STACK_FUNCS(super_compat_ro_flags, struct btrfs_super_block, - compat_flags, 64); -BTRFS_SETGET_STACK_FUNCS(super_incompat_flags, struct btrfs_super_block, - incompat_flags, 64); -BTRFS_SETGET_STACK_FUNCS(super_csum_type, struct btrfs_super_block, - csum_type, 16); - -static inline int btrfs_super_csum_size(struct btrfs_super_block *s) -{ - int t = btrfs_super_csum_type(s); - BUG_ON(t >= ARRAY_SIZE(btrfs_csum_sizes)); - return btrfs_csum_sizes[t]; -} - -static inline unsigned long btrfs_leaf_data(struct extent_buffer *l) -{ - return offsetof(struct btrfs_leaf, items); -} - -/* struct btrfs_file_extent_item */ -BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8); - -static inline unsigned long btrfs_file_extent_inline_start(struct - btrfs_file_extent_item *e) -{ - unsigned long offset = (unsigned long)e; - offset += offsetof(struct btrfs_file_extent_item, disk_bytenr); - return offset; -} - -static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize) -{ - return offsetof(struct btrfs_file_extent_item, disk_bytenr) + datasize; -} - -BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item, - disk_bytenr, 64); -BTRFS_SETGET_FUNCS(file_extent_generation, struct btrfs_file_extent_item, - generation, 64); -BTRFS_SETGET_FUNCS(file_extent_disk_num_bytes, struct btrfs_file_extent_item, - disk_num_bytes, 64); -BTRFS_SETGET_FUNCS(file_extent_offset, struct btrfs_file_extent_item, - offset, 64); -BTRFS_SETGET_FUNCS(file_extent_num_bytes, struct btrfs_file_extent_item, - num_bytes, 64); -BTRFS_SETGET_FUNCS(file_extent_ram_bytes, struct btrfs_file_extent_item, - ram_bytes, 64); -BTRFS_SETGET_FUNCS(file_extent_compression, struct btrfs_file_extent_item, - compression, 8); -BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item, - encryption, 8); -BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item, - other_encoding, 16); - -/* this returns the number of file bytes represented by the inline item. - * If an item is compressed, this is the uncompressed size - */ -static inline u32 btrfs_file_extent_inline_len(struct extent_buffer *eb, - struct btrfs_file_extent_item *e) -{ - return btrfs_file_extent_ram_bytes(eb, e); -} - -/* - * this returns the number of bytes used by the item on disk, minus the - * size of any extent headers. If a file is compressed on disk, this is - * the compressed size - */ -static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb, - struct btrfs_item *e) -{ - unsigned long offset; - offset = offsetof(struct btrfs_file_extent_item, disk_bytenr); - return btrfs_item_size(eb, e) - offset; -} - -static inline u32 btrfs_level_size(struct btrfs_root *root, int level) { - if (level == 0) - return root->leafsize; - return root->nodesize; -} - -/* helper function to cast into the data area of the leaf. */ -#define btrfs_item_ptr(leaf, slot, type) \ - ((type *)(btrfs_leaf_data(leaf) + \ - btrfs_item_offset_nr(leaf, slot))) - -#define btrfs_item_ptr_offset(leaf, slot) \ - ((unsigned long)(btrfs_leaf_data(leaf) + \ - btrfs_item_offset_nr(leaf, slot))) - -/* extent-tree.c */ -int btrfs_extent_post_op(struct btrfs_trans_handle *trans, - struct btrfs_root *root); -int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy); -struct btrfs_block_group_cache *btrfs_lookup_block_group(struct - btrfs_fs_info *info, - u64 bytenr); -struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, - struct btrfs_block_group_cache - *hint, u64 search_start, - int data, int owner); -struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u32 blocksize, u64 root_objectid, - struct btrfs_disk_key *key, int level, - u64 hint, u64 empty_size); -int btrfs_alloc_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 num_bytes, u64 parent, - u64 root_objectid, u64 ref_generation, - u64 owner, u64 empty_size, u64 hint_byte, - u64 search_end, struct btrfs_key *ins, int data); -int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytenr, - u64 num_bytes, u64 *refs, u64 *flags); -int btrfs_set_block_flags(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 flags); -int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int record_parent); -int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int record_parent); -int btrfs_free_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, u64 offset); -int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_io_tree *unpin); -int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 ref_generation, - u64 owner_objectid); -int btrfs_update_extent_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytenr, - u64 orig_parent, u64 parent, - u64 root_objectid, u64 ref_generation, - u64 owner_objectid); -int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, - struct btrfs_root *root); -int btrfs_free_block_groups(struct btrfs_fs_info *info); -int btrfs_read_block_groups(struct btrfs_root *root); -int btrfs_make_block_group(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytes_used, - u64 type, u64 chunk_objectid, u64 chunk_offset, - u64 size); -int btrfs_make_block_groups(struct btrfs_trans_handle *trans, - struct btrfs_root *root); -int btrfs_update_block_group(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytenr, u64 num, - int alloc, int mark_free); -/* ctree.c */ -int btrfs_previous_item(struct btrfs_root *root, - struct btrfs_path *path, u64 min_objectid, - int type); -int btrfs_comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2); -int btrfs_cow_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct extent_buffer *buf, - struct extent_buffer *parent, int parent_slot, - struct extent_buffer **cow_ret); -int __btrfs_cow_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *buf, - struct extent_buffer *parent, int parent_slot, - struct extent_buffer **cow_ret, - u64 search_start, u64 empty_size); -int btrfs_copy_root(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *buf, - struct extent_buffer **cow_ret, u64 new_root_objectid); -int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, u32 data_size); -int btrfs_truncate_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u32 new_size, int from_end); -int btrfs_split_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_key *new_key, - unsigned long split_offset); -int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *key, struct btrfs_path *p, int - ins_len, int cow); -int btrfs_realloc_node(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct extent_buffer *parent, - int start_slot, int cache_only, u64 *last_ret, - struct btrfs_key *progress); -void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p); -struct btrfs_path *btrfs_alloc_path(void); -void btrfs_free_path(struct btrfs_path *p); -void btrfs_init_path(struct btrfs_path *p); -int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct btrfs_path *path, int slot, int nr); - -static inline int btrfs_del_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path) -{ - return btrfs_del_items(trans, root, path, path->slots[0], 1); -} - -int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *key, void *data, u32 data_size); -int btrfs_insert_empty_items(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_key *cpu_key, u32 *data_size, int nr); - -static inline int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_key *key, - u32 data_size) -{ - return btrfs_insert_empty_items(trans, root, path, key, &data_size, 1); -} - -int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path); -int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path); -int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf); -int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root - *root); -int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct btrfs_path *path, - struct btrfs_key *new_key); - -/* root-item.c */ -int btrfs_add_root_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *tree_root, - u64 root_id, u8 type, u64 ref_id, - u64 dirid, u64 sequence, - const char *name, int name_len); -int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct btrfs_key *key); -int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *key, struct btrfs_root_item - *item); -int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *key, struct btrfs_root_item - *item); -int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct - btrfs_root_item *item, struct btrfs_key *key); -int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid, - struct btrfs_root *latest_root); -/* dir-item.c */ -int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, const char *name, int name_len, u64 dir, - struct btrfs_key *location, u8 type, u64 index); -struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 dir, - const char *name, int name_len, - int mod); -struct btrfs_dir_item * -btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 dir, - u64 objectid, const char *name, int name_len, - int mod); -struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root, - struct btrfs_path *path, - const char *name, int name_len); -int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_dir_item *di); -int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, const char *name, - u16 name_len, const void *data, u16 data_len, - u64 dir); -struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 dir, - const char *name, u16 name_len, - int mod); -/* inode-map.c */ -int btrfs_find_free_objectid(struct btrfs_trans_handle *trans, - struct btrfs_root *fs_root, - u64 dirid, u64 *objectid); -int btrfs_find_highest_inode(struct btrfs_root *fs_root, u64 *objectid); - -/* inode-item.c */ -int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - const char *name, int name_len, - u64 inode_objectid, u64 ref_objectid, u64 index); -int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - const char *name, int name_len, - u64 inode_objectid, u64 ref_objectid); -int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 objectid); -int btrfs_insert_inode(struct btrfs_trans_handle *trans, struct btrfs_root - *root, u64 objectid, struct btrfs_inode_item - *inode_item); -int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, - struct btrfs_key *location, int mod); - -/* file-item.c */ -int btrfs_del_csums(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytenr, u64 len); -int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 objectid, u64 pos, u64 offset, - u64 disk_num_bytes, - u64 num_bytes); -int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - u64 offset, char *buffer, size_t size); -int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 objectid, - u64 bytenr, int mod); -int btrfs_csum_file_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 alloc_end, - u64 bytenr, char *data, size_t len); -struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 bytenr, int cow); -int btrfs_csum_truncate(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct btrfs_path *path, - u64 isize); -#endif diff --git a/debug-tree.c b/debug-tree.c deleted file mode 100644 index 1d47519..0000000 --- a/debug-tree.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "print-tree.h" -#include "transaction.h" -#include "version.h" - -static int print_usage(void) -{ - fprintf(stderr, "usage: debug-tree [ -e ] device\n"); - fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); - exit(1); -} - -static void print_extent_leaf(struct btrfs_root *root, struct extent_buffer *l) -{ - int i; - struct btrfs_item *item; -// struct btrfs_extent_ref *ref; - struct btrfs_key key; - static u64 last = 0; - static u64 last_len = 0; - u32 nr = btrfs_header_nritems(l); - u32 type; - - for (i = 0 ; i < nr ; i++) { - item = btrfs_item_nr(l, i); - btrfs_item_key_to_cpu(l, &key, i); - type = btrfs_key_type(&key); - switch (type) { - case BTRFS_EXTENT_ITEM_KEY: - last_len = key.offset; - last = key.objectid; - break; -#if 0 - case BTRFS_EXTENT_REF_KEY: - ref = btrfs_item_ptr(l, i, struct btrfs_extent_ref); - printf("%llu %llu extent back ref root %llu gen %llu " - "owner %llu num_refs %lu\n", - (unsigned long long)last, - (unsigned long long)last_len, - (unsigned long long)btrfs_ref_root(l, ref), - (unsigned long long)btrfs_ref_generation(l, ref), - (unsigned long long)btrfs_ref_objectid(l, ref), - (unsigned long)btrfs_ref_num_refs(l, ref)); - break; -#endif - }; - fflush(stdout); - } -} - -static void print_extents(struct btrfs_root *root, struct extent_buffer *eb) -{ - int i; - u32 nr; - u32 size; - - if (!eb) - return; - if (btrfs_is_leaf(eb)) { - print_extent_leaf(root, eb); - return; - } - size = btrfs_level_size(root, btrfs_header_level(eb) - 1); - nr = btrfs_header_nritems(eb); - for (i = 0; i < nr; i++) { - struct extent_buffer *next = read_tree_block(root, - btrfs_node_blockptr(eb, i), - size, - btrfs_node_ptr_generation(eb, i)); - if (btrfs_is_leaf(next) && - btrfs_header_level(eb) != 1) - BUG(); - if (btrfs_header_level(next) != - btrfs_header_level(eb) - 1) - BUG(); - print_extents(root, next); - free_extent_buffer(next); - } -} - -int main(int ac, char **av) -{ - struct btrfs_root *root; - struct btrfs_path path; - struct btrfs_key key; - struct btrfs_root_item ri; - struct extent_buffer *leaf; - struct btrfs_disk_key disk_key; - struct btrfs_key found_key; - char uuidbuf[37]; - int ret; - int slot; - int extent_only = 0; - struct btrfs_root *tree_root_scan; - - radix_tree_init(); - - while(1) { - int c; - c = getopt(ac, av, "e"); - if (c < 0) - break; - switch(c) { - case 'e': - extent_only = 1; - break; - default: - print_usage(); - } - } - ac = ac - optind; - if (ac != 1) - print_usage(); - - root = open_ctree(av[optind], 0, 0); - if (!root) { - fprintf(stderr, "unable to open %s\n", av[optind]); - exit(1); - } - if (!extent_only) { - printf("root tree\n"); - btrfs_print_tree(root->fs_info->tree_root, - root->fs_info->tree_root->node); - - printf("chunk tree\n"); - btrfs_print_tree(root->fs_info->chunk_root, - root->fs_info->chunk_root->node); - } - tree_root_scan = root->fs_info->tree_root; - - btrfs_init_path(&path); -again: - key.offset = 0; - key.objectid = 0; - btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); - ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0); - BUG_ON(ret < 0); - while(1) { - leaf = path.nodes[0]; - slot = path.slots[0]; - if (slot >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(tree_root_scan, &path); - if (ret != 0) - break; - leaf = path.nodes[0]; - slot = path.slots[0]; - } - btrfs_item_key(leaf, &disk_key, path.slots[0]); - btrfs_disk_key_to_cpu(&found_key, &disk_key); - if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { - unsigned long offset; - struct extent_buffer *buf; - int skip = extent_only; - - offset = btrfs_item_ptr_offset(leaf, slot); - read_extent_buffer(leaf, &ri, offset, sizeof(ri)); - buf = read_tree_block(tree_root_scan, - btrfs_root_bytenr(&ri), - tree_root_scan->leafsize, 0); - switch(found_key.objectid) { - case BTRFS_ROOT_TREE_OBJECTID: - if (!skip) - printf("root"); - break; - case BTRFS_EXTENT_TREE_OBJECTID: - skip = 0; - if (!extent_only) - printf("extent"); - break; - case BTRFS_CHUNK_TREE_OBJECTID: - if (!skip) { - printf("chunk"); - } - break; - case BTRFS_DEV_TREE_OBJECTID: - if (!skip) { - printf("device"); - } - break; - case BTRFS_FS_TREE_OBJECTID: - if (!skip) { - printf("fs"); - } - break; - case BTRFS_ROOT_TREE_DIR_OBJECTID: - if (!skip) { - printf("directory"); - } - break; - case BTRFS_CSUM_TREE_OBJECTID: - if (!skip) { - printf("checksum"); - } - break; - case BTRFS_ORPHAN_OBJECTID: - if (!skip) { - printf("orphan"); - } - break; - case BTRFS_TREE_LOG_OBJECTID: - if (!skip) { - printf("log"); - } - break; - case BTRFS_TREE_LOG_FIXUP_OBJECTID: - if (!skip) { - printf("log fixup"); - } - break; - case BTRFS_TREE_RELOC_OBJECTID: - if (!skip) { - printf("reloc"); - } - break; - case BTRFS_DATA_RELOC_TREE_OBJECTID: - if (!skip) { - printf("data reloc"); - } - break; - case BTRFS_EXTENT_CSUM_OBJECTID: - if (!skip) { - printf("extent checksum"); - } - case BTRFS_MULTIPLE_OBJECTIDS: - if (!skip) { - printf("multiple"); - } - break; - default: - if (!skip) { - printf("file"); - } - } - if (!skip && !extent_only) { - printf(" tree "); - btrfs_print_key(&disk_key); - printf(" \n"); - btrfs_print_tree(tree_root_scan, buf); - } else if (extent_only && !skip) { - print_extents(tree_root_scan, buf); - } - } - path.slots[0]++; - } - btrfs_release_path(root, &path); - - if (tree_root_scan == root->fs_info->tree_root && - root->fs_info->log_root_tree) { - tree_root_scan = root->fs_info->log_root_tree; - goto again; - } - - if (extent_only) - return 0; - - printf("total bytes %llu\n", - (unsigned long long)btrfs_super_total_bytes(&root->fs_info->super_copy)); - printf("bytes used %llu\n", - (unsigned long long)btrfs_super_bytes_used(&root->fs_info->super_copy)); - uuidbuf[36] = '\0'; - uuid_unparse(root->fs_info->super_copy.fsid, uuidbuf); - printf("uuid %s\n", uuidbuf); - printf("%s\n", BTRFS_BUILD_VERSION); - return 0; -} diff --git a/dir-item.c b/dir-item.c deleted file mode 100644 index 71373b8..0000000 --- a/dir-item.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 "ctree.h" -#include "disk-io.h" -#include "hash.h" -#include "transaction.h" - -static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle - *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_key *cpu_key, - u32 data_size, - const char *name, - int name_len) -{ - int ret; - char *ptr; - struct btrfs_item *item; - struct extent_buffer *leaf; - - ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size); - if (ret == -EEXIST) { - struct btrfs_dir_item *di; - di = btrfs_match_dir_item_name(root, path, name, name_len); - if (di) - return ERR_PTR(-EEXIST); - ret = btrfs_extend_item(trans, root, path, data_size); - WARN_ON(ret > 0); - } - if (ret < 0) - return ERR_PTR(ret); - WARN_ON(ret > 0); - leaf = path->nodes[0]; - item = btrfs_item_nr(leaf, path->slots[0]); - ptr = btrfs_item_ptr(leaf, path->slots[0], char); - BUG_ON(data_size > btrfs_item_size(leaf, item)); - ptr += btrfs_item_size(leaf, item) - data_size; - return (struct btrfs_dir_item *)ptr; -} - -int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, const char *name, - u16 name_len, const void *data, u16 data_len, - u64 dir) -{ - int ret = 0; - struct btrfs_path *path; - struct btrfs_dir_item *dir_item; - unsigned long name_ptr, data_ptr; - struct btrfs_key key, location; - struct btrfs_disk_key disk_key; - struct extent_buffer *leaf; - u32 data_size; - - key.objectid = dir; - btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY); - key.offset = btrfs_name_hash(name, name_len); - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - data_size = sizeof(*dir_item) + name_len + data_len; - dir_item = insert_with_overflow(trans, root, path, &key, data_size, - name, name_len); - /* - * FIXME: at some point we should handle xattr's that are larger than - * what we can fit in our leaf. We set location to NULL b/c we arent - * pointing at anything else, that will change if we store the xattr - * data in a separate inode. - */ - BUG_ON(IS_ERR(dir_item)); - memset(&location, 0, sizeof(location)); - - leaf = path->nodes[0]; - btrfs_cpu_key_to_disk(&disk_key, &location); - btrfs_set_dir_item_key(leaf, dir_item, &disk_key); - btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR); - btrfs_set_dir_name_len(leaf, dir_item, name_len); - btrfs_set_dir_data_len(leaf, dir_item, data_len); - name_ptr = (unsigned long)(dir_item + 1); - data_ptr = (unsigned long)((char *)name_ptr + name_len); - - write_extent_buffer(leaf, name, name_ptr, name_len); - write_extent_buffer(leaf, data, data_ptr, data_len); - btrfs_mark_buffer_dirty(path->nodes[0]); - - btrfs_free_path(path); - return ret; -} - -int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, const char *name, int name_len, u64 dir, - struct btrfs_key *location, u8 type, u64 index) -{ - int ret = 0; - int ret2 = 0; - struct btrfs_path *path; - struct btrfs_dir_item *dir_item; - struct extent_buffer *leaf; - unsigned long name_ptr; - struct btrfs_key key; - struct btrfs_disk_key disk_key; - u32 data_size; - - key.objectid = dir; - btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); - key.offset = btrfs_name_hash(name, name_len); - path = btrfs_alloc_path(); - data_size = sizeof(*dir_item) + name_len; - dir_item = insert_with_overflow(trans, root, path, &key, data_size, - name, name_len); - if (IS_ERR(dir_item)) { - ret = PTR_ERR(dir_item); - goto out; - } - - leaf = path->nodes[0]; - btrfs_cpu_key_to_disk(&disk_key, location); - btrfs_set_dir_item_key(leaf, dir_item, &disk_key); - btrfs_set_dir_type(leaf, dir_item, type); - btrfs_set_dir_data_len(leaf, dir_item, 0); - btrfs_set_dir_name_len(leaf, dir_item, name_len); - name_ptr = (unsigned long)(dir_item + 1); - - write_extent_buffer(leaf, name, name_ptr, name_len); - btrfs_mark_buffer_dirty(leaf); - - /* FIXME, use some real flag for selecting the extra index */ - if (root == root->fs_info->tree_root) { - ret = 0; - goto out; - } - btrfs_release_path(root, path); - - btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY); - key.offset = index; - dir_item = insert_with_overflow(trans, root, path, &key, data_size, - name, name_len); - if (IS_ERR(dir_item)) { - ret2 = PTR_ERR(dir_item); - goto out; - } - leaf = path->nodes[0]; - btrfs_cpu_key_to_disk(&disk_key, location); - btrfs_set_dir_item_key(leaf, dir_item, &disk_key); - btrfs_set_dir_type(leaf, dir_item, type); - btrfs_set_dir_data_len(leaf, dir_item, 0); - btrfs_set_dir_name_len(leaf, dir_item, name_len); - name_ptr = (unsigned long)(dir_item + 1); - write_extent_buffer(leaf, name, name_ptr, name_len); - btrfs_mark_buffer_dirty(leaf); -out: - btrfs_free_path(path); - if (ret) - return ret; - if (ret2) - return ret2; - return 0; -} - -struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 dir, - const char *name, int name_len, - int mod) -{ - int ret; - struct btrfs_key key; - int ins_len = mod < 0 ? -1 : 0; - int cow = mod != 0; - struct btrfs_key found_key; - struct extent_buffer *leaf; - - key.objectid = dir; - btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); - - key.offset = btrfs_name_hash(name, name_len); - - ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); - if (ret < 0) - return ERR_PTR(ret); - if (ret > 0) { - if (path->slots[0] == 0) - return NULL; - path->slots[0]--; - } - - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); - - if (found_key.objectid != dir || - btrfs_key_type(&found_key) != BTRFS_DIR_ITEM_KEY || - found_key.offset != key.offset) - return NULL; - - return btrfs_match_dir_item_name(root, path, name, name_len); -} - -struct btrfs_dir_item * -btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 dir, - u64 objectid, const char *name, int name_len, - int mod) -{ - int ret; - struct btrfs_key key; - int ins_len = mod < 0 ? -1 : 0; - int cow = mod != 0; - - key.objectid = dir; - btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY); - key.offset = objectid; - - ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); - if (ret < 0) - return ERR_PTR(ret); - if (ret > 0) - return ERR_PTR(-ENOENT); - return btrfs_match_dir_item_name(root, path, name, name_len); -} - -struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 dir, - const char *name, u16 name_len, - int mod) -{ - int ret; - struct btrfs_key key; - int ins_len = mod < 0 ? -1 : 0; - int cow = mod != 0; - struct btrfs_key found_key; - struct extent_buffer *leaf; - - key.objectid = dir; - btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY); - key.offset = btrfs_name_hash(name, name_len); - ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); - if (ret < 0) - return ERR_PTR(ret); - if (ret > 0) { - if (path->slots[0] == 0) - return NULL; - path->slots[0]--; - } - - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); - - if (found_key.objectid != dir || - btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY || - found_key.offset != key.offset) - return NULL; - - return btrfs_match_dir_item_name(root, path, name, name_len); -} - -struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root, - struct btrfs_path *path, - const char *name, int name_len) -{ - struct btrfs_dir_item *dir_item; - unsigned long name_ptr; - u32 total_len; - u32 cur = 0; - u32 this_len; - struct extent_buffer *leaf; - - leaf = path->nodes[0]; - dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); - total_len = btrfs_item_size_nr(leaf, path->slots[0]); - while(cur < total_len) { - this_len = sizeof(*dir_item) + - btrfs_dir_name_len(leaf, dir_item) + - btrfs_dir_data_len(leaf, dir_item); - name_ptr = (unsigned long)(dir_item + 1); - - if (btrfs_dir_name_len(leaf, dir_item) == name_len && - memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) - return dir_item; - - cur += this_len; - dir_item = (struct btrfs_dir_item *)((char *)dir_item + - this_len); - } - return NULL; -} - -int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_dir_item *di) -{ - - struct extent_buffer *leaf; - u32 sub_item_len; - u32 item_len; - int ret = 0; - - leaf = path->nodes[0]; - sub_item_len = sizeof(*di) + btrfs_dir_name_len(leaf, di) + - btrfs_dir_data_len(leaf, di); - item_len = btrfs_item_size_nr(leaf, path->slots[0]); - if (sub_item_len == item_len) { - ret = btrfs_del_item(trans, root, path); - } else { - /* MARKER */ - unsigned long ptr = (unsigned long)di; - unsigned long start; - - start = btrfs_item_ptr_offset(leaf, path->slots[0]); - memmove_extent_buffer(leaf, ptr, ptr + sub_item_len, - item_len - (ptr + sub_item_len - start)); - ret = btrfs_truncate_item(trans, root, path, - item_len - sub_item_len, 1); - } - return 0; -} diff --git a/dir-test.c b/dir-test.c deleted file mode 100644 index 44f2758..0000000 --- a/dir-test.c +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "print-tree.h" -#include "hash.h" -#include "transaction.h" - -int keep_running = 1; -struct btrfs_super_block super; -static u64 dir_oid = 0; -static u64 file_oid = 33778; - -static int find_num(struct radix_tree_root *root, unsigned long *num_ret, - int exists) -{ - unsigned long num = rand(); - unsigned long res[2]; - int ret; - -again: - ret = radix_tree_gang_lookup(root, (void **)res, num, 2); - if (exists) { - if (ret == 0) - return -1; - num = res[0]; - } else if (ret != 0 && num == res[0]) { - num++; - if (ret > 1 && num == res[1]) { - num++; - goto again; - } - } - *num_ret = num; - return 0; -} - -static void initial_inode_init(struct btrfs_root *root, - struct btrfs_inode_item *inode_item) -{ - memset(inode_item, 0, sizeof(*inode_item)); - btrfs_set_inode_generation(inode_item, root->fs_info->generation); - btrfs_set_inode_mode(inode_item, S_IFREG | 0700); -} - -static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct radix_tree_root *radix) -{ - int ret; - char buf[128]; - unsigned long oid; - u64 objectid; - struct btrfs_path path; - struct btrfs_key inode_map; - struct btrfs_inode_item inode_item; - - find_num(radix, &oid, 0); - sprintf(buf, "str-%lu", oid); - - ret = btrfs_find_free_objectid(trans, root, dir_oid + 1, &objectid); - if (ret) - goto error; - - inode_map.objectid = objectid; - inode_map.flags = 0; - btrfs_set_key_type(&inode_map, BTRFS_INODE_ITEM_KEY); - inode_map.offset = 0; - - initial_inode_init(root, &inode_item); - ret = btrfs_insert_inode(trans, root, objectid, &inode_item); - if (ret) - goto error; - ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid, - &inode_map, BTRFS_FT_UNKNOWN); - if (ret) - goto error; - - radix_tree_preload(GFP_KERNEL); - ret = radix_tree_insert(radix, oid, (void *)oid); - radix_tree_preload_end(); - if (ret) - goto error; - return ret; -error: - if (ret != -EEXIST) - goto fatal; - - /* - * if we got an EEXIST, it may be due to hash collision, double - * check - */ - btrfs_init_path(&path); - ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf, - strlen(buf), 0); - if (ret) - goto fatal_release; - if (!btrfs_match_dir_item_name(root, &path, buf, strlen(buf))) { - struct btrfs_dir_item *di; - char *found; - u32 found_len; - u64 myhash; - u64 foundhash; - - di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0], - struct btrfs_dir_item); - found = (char *)(di + 1); - found_len = btrfs_dir_name_len(di); - myhash = btrfs_name_hash(buf, strlen(buf)); - foundhash = btrfs_name_hash(found, found_len); - if (myhash != foundhash) - goto fatal_release; - btrfs_release_path(root, &path); - return 0; - } -fatal_release: - btrfs_release_path(root, &path); -fatal: - printf("failed to insert %lu ret %d\n", oid, ret); - return -1; -} - -static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct radix_tree_root *radix) -{ - int ret; - char buf[128]; - unsigned long oid; - struct btrfs_key key; - - ret = find_num(radix, &oid, 1); - if (ret < 0) - return 0; - sprintf(buf, "str-%lu", oid); - - key.objectid = file_oid; - key.flags = 0; - btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); - key.offset = 0; - ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid, - &key, BTRFS_FT_UNKNOWN); - if (ret != -EEXIST) { - printf("insert on %s gave us %d\n", buf, ret); - return 1; - } - return 0; -} - -static int del_dir_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct radix_tree_root *radix, - unsigned long radix_index, - struct btrfs_path *path) -{ - int ret; - unsigned long *ptr; - u64 file_objectid; - struct btrfs_dir_item *di; - - /* find the inode number of the file */ - di = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0], - struct btrfs_dir_item); - file_objectid = btrfs_disk_key_objectid(&di->location); - - /* delete the directory item */ - ret = btrfs_del_item(trans, root, path); - if (ret) - goto out_release; - btrfs_release_path(root, path); - - /* delete the inode */ - btrfs_init_path(path); - ret = btrfs_lookup_inode(trans, root, path, file_objectid, -1); - if (ret) - goto out_release; - ret = btrfs_del_item(trans, root, path); - if (ret) - goto out_release; - btrfs_release_path(root, path); - - if (root->fs_info->last_inode_alloc > file_objectid) - root->fs_info->last_inode_alloc = file_objectid; - ptr = radix_tree_delete(radix, radix_index); - if (!ptr) { - ret = -5555; - goto out; - } - return 0; -out_release: - btrfs_release_path(root, path); -out: - printf("failed to delete %lu %d\n", radix_index, ret); - return -1; -} - -static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct radix_tree_root *radix) -{ - int ret; - char buf[128]; - unsigned long oid; - struct btrfs_path path; - - ret = find_num(radix, &oid, 1); - if (ret < 0) - return 0; - sprintf(buf, "str-%lu", oid); - btrfs_init_path(&path); - ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf, - strlen(buf), -1); - if (ret) - goto out_release; - - ret = del_dir_item(trans, root, radix, oid, &path); - if (ret) - goto out_release; - return ret; -out_release: - btrfs_release_path(root, &path); - printf("failed to delete %lu %d\n", oid, ret); - return -1; -} - -static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct radix_tree_root *radix) -{ - struct btrfs_path path; - char buf[128]; - int ret; - unsigned long oid; - u64 objectid; - struct btrfs_dir_item *di; - - ret = find_num(radix, &oid, 1); - if (ret < 0) - return 0; - sprintf(buf, "str-%lu", oid); - btrfs_init_path(&path); - ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf, - strlen(buf), 0); - if (!ret) { - di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0], - struct btrfs_dir_item); - objectid = btrfs_disk_key_objectid(&di->location); - } - btrfs_release_path(root, &path); - if (ret) { - printf("unable to find key %lu\n", oid); - return -1; - } - return 0; -} - -static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct radix_tree_root *radix) -{ - struct btrfs_path path; - char buf[128]; - int ret; - unsigned long oid; - - ret = find_num(radix, &oid, 0); - if (ret < 0) - return 0; - sprintf(buf, "str-%lu", oid); - btrfs_init_path(&path); - ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf, - strlen(buf), 0); - btrfs_release_path(root, &path); - if (!ret) { - printf("able to find key that should not exist %lu\n", oid); - return -1; - } - return 0; -} - -static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct radix_tree_root *radix, int nr) -{ - struct btrfs_path path; - struct btrfs_key key; - unsigned long found = 0; - u32 found_len; - int ret; - int slot; - int count = 0; - char buf[128]; - struct btrfs_dir_item *di; - - key.offset = (u64)-1; - key.flags = 0; - btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); - key.objectid = dir_oid; - while(nr-- >= 0) { - btrfs_init_path(&path); - ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); - if (ret < 0) { - btrfs_release_path(root, &path); - return ret; - } - if (ret != 0) { - if (path.slots[0] == 0) { - btrfs_release_path(root, &path); - break; - } - path.slots[0] -= 1; - } - slot = path.slots[0]; - di = btrfs_item_ptr(&path.nodes[0]->leaf, slot, - struct btrfs_dir_item); - found_len = btrfs_dir_name_len(di); - memcpy(buf, (char *)(di + 1), found_len); - BUG_ON(found_len > 128); - buf[found_len] = '\0'; - found = atoi(buf + 4); - ret = del_dir_item(trans, root, radix, found, &path); - count++; - if (ret) { - fprintf(stderr, - "failed to remove %lu from tree\n", - found); - return -1; - } - if (!keep_running) - break; - } - return 0; - fprintf(stderr, "failed to delete from the radix %lu\n", found); - return -1; -} - -static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct radix_tree_root *radix, int count) -{ - int i; - int ret = 0; - for (i = 0; i < count; i++) { - ret = ins_one(trans, root, radix); - if (ret) { - fprintf(stderr, "fill failed\n"); - goto out; - } - if (i % 1000 == 0) { - ret = btrfs_commit_transaction(trans, root, &super); - if (ret) { - fprintf(stderr, "fill commit failed\n"); - return ret; - } - } - if (i && i % 10000 == 0) { - printf("bigfill %d\n", i); - } - if (!keep_running) - break; - } -out: - return ret; -} - -static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct radix_tree_root *radix) -{ - int ret; - int nr = rand() % 5000; - static int run_nr = 0; - - /* do the bulk op much less frequently */ - if (run_nr++ % 100) - return 0; - ret = empty_tree(trans, root, radix, nr); - if (ret) - return ret; - ret = fill_tree(trans, root, radix, nr); - if (ret) - return ret; - return 0; -} - - -int (*ops[])(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct - radix_tree_root *radix) = - { ins_one, insert_dup, del_one, lookup_item, - lookup_enoent, bulk_op }; - -void sigstopper(int ignored) -{ - keep_running = 0; - fprintf(stderr, "caught exit signal, stopping\n"); -} - -int print_usage(void) -{ - printf("usage: tester [-ih] [-c count] [-f count]\n"); - printf("\t -c count -- iteration count after filling\n"); - printf("\t -f count -- run this many random inserts before starting\n"); - printf("\t -i -- only do initial fill\n"); - printf("\t -h -- this help text\n"); - exit(1); -} -int main(int ac, char **av) -{ - RADIX_TREE(radix, GFP_KERNEL); - struct btrfs_root *root; - int i; - int ret; - int count; - int op; - int iterations = 20000; - int init_fill_count = 800000; - int err = 0; - int initial_only = 0; - struct btrfs_trans_handle *trans; - radix_tree_init(); - - root = open_ctree(av[ac-1], &super, 0); - trans = btrfs_start_transaction(root, 1); - - dir_oid = btrfs_super_root_dir(&super); - - signal(SIGTERM, sigstopper); - signal(SIGINT, sigstopper); - - for (i = 1 ; i < ac - 1; i++) { - if (strcmp(av[i], "-i") == 0) { - initial_only = 1; - } else if (strcmp(av[i], "-c") == 0) { - iterations = atoi(av[i+1]); - i++; - } else if (strcmp(av[i], "-f") == 0) { - init_fill_count = atoi(av[i+1]); - i++; - } else { - print_usage(); - } - } - printf("initial fill\n"); - ret = fill_tree(trans, root, &radix, init_fill_count); - printf("starting run\n"); - if (ret) { - err = ret; - goto out; - } - if (initial_only == 1) { - goto out; - } - for (i = 0; i < iterations; i++) { - op = rand() % ARRAY_SIZE(ops); - count = rand() % 128; - if (i % 2000 == 0) { - printf("%d\n", i); - fflush(stdout); - } - if (i && i % 5000 == 0) { - printf("open & close, root level %d nritems %d\n", - btrfs_header_level(&root->node->node.header), - btrfs_header_nritems(&root->node->node.header)); - close_ctree(root, &super); - root = open_ctree("dbfile", &super, 0); - } - while(count--) { - ret = ops[op](trans, root, &radix); - if (ret) { - fprintf(stderr, "op %d failed %d:%d\n", - op, i, iterations); - btrfs_print_tree(root, root->node); - fprintf(stderr, "op %d failed %d:%d\n", - op, i, iterations); - err = ret; - goto out; - } - if (ops[op] == bulk_op) - break; - if (keep_running == 0) { - err = 0; - goto out; - } - } - } -out: - close_ctree(root, &super); - return err; -} - diff --git a/disk-io.c b/disk-io.c deleted file mode 100644 index addebe1..0000000 --- a/disk-io.c +++ /dev/null @@ -1,1029 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 600 -#define __USE_XOPEN2K -#define _GNU_SOURCE 1 -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "volumes.h" -#include "transaction.h" -#include "crc32c.h" -#include "utils.h" -#include "print-tree.h" - -static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf) -{ - - struct btrfs_fs_devices *fs_devices; - int ret = 1; - - if (buf->start != btrfs_header_bytenr(buf)) - return ret; - - fs_devices = root->fs_info->fs_devices; - while (fs_devices) { - if (!memcmp_extent_buffer(buf, fs_devices->fsid, - (unsigned long)btrfs_header_fsid(buf), - BTRFS_FSID_SIZE)) { - ret = 0; - break; - } - fs_devices = fs_devices->seed; - } - return ret; -} - -u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len) -{ - return crc32c(seed, data, len); -} - -void btrfs_csum_final(u32 crc, char *result) -{ - *(__le32 *)result = ~cpu_to_le32(crc); -} - -int csum_tree_block_size(struct extent_buffer *buf, u16 csum_size, - int verify) -{ - char *result; - u32 len; - u32 crc = ~(u32)0; - - result = malloc(csum_size * sizeof(char)); - if (!result) - return 1; - - len = buf->len - BTRFS_CSUM_SIZE; - crc = crc32c(crc, buf->data + BTRFS_CSUM_SIZE, len); - btrfs_csum_final(crc, result); - - if (verify) { - if (memcmp_extent_buffer(buf, result, 0, csum_size)) { - printk("checksum verify failed on %llu wanted %X " - "found %X\n", (unsigned long long)buf->start, - *((int *)result), *((int *)buf)); - free(result); - return 1; - } - } else { - write_extent_buffer(buf, result, 0, csum_size); - } - free(result); - return 0; -} - -int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, - int verify) -{ - u16 csum_size = - btrfs_super_csum_size(&root->fs_info->super_copy); - return csum_tree_block_size(buf, csum_size, verify); -} - -struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root, - u64 bytenr, u32 blocksize) -{ - return find_extent_buffer(&root->fs_info->extent_cache, - bytenr, blocksize); -} - -struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root, - u64 bytenr, u32 blocksize) -{ - return alloc_extent_buffer(&root->fs_info->extent_cache, bytenr, - blocksize); -} - -int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, - u64 parent_transid) -{ - int ret; - int dev_nr; - struct extent_buffer *eb; - u64 length; - struct btrfs_multi_bio *multi = NULL; - struct btrfs_device *device; - - eb = btrfs_find_tree_block(root, bytenr, blocksize); - if (eb && btrfs_buffer_uptodate(eb, parent_transid)) { - free_extent_buffer(eb); - return 0; - } - - dev_nr = 0; - length = blocksize; - ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, - bytenr, &length, &multi, 0); - BUG_ON(ret); - device = multi->stripes[0].dev; - device->total_ios++; - blocksize = min(blocksize, (u32)(64 * 1024)); - readahead(device->fd, multi->stripes[0].physical, blocksize); - kfree(multi); - return 0; -} - -static int verify_parent_transid(struct extent_io_tree *io_tree, - struct extent_buffer *eb, u64 parent_transid) -{ - int ret; - - if (!parent_transid || btrfs_header_generation(eb) == parent_transid) - return 0; - - if (extent_buffer_uptodate(eb) && - btrfs_header_generation(eb) == parent_transid) { - ret = 0; - goto out; - } - printk("parent transid verify failed on %llu wanted %llu found %llu\n", - (unsigned long long)eb->start, - (unsigned long long)parent_transid, - (unsigned long long)btrfs_header_generation(eb)); - ret = 1; -out: - clear_extent_buffer_uptodate(io_tree, eb); - return ret; - -} - - -struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, - u32 blocksize, u64 parent_transid) -{ - int ret; - int dev_nr; - struct extent_buffer *eb; - u64 length; - struct btrfs_multi_bio *multi = NULL; - struct btrfs_device *device; - int mirror_num = 0; - int num_copies; - - eb = btrfs_find_create_tree_block(root, bytenr, blocksize); - if (!eb) - return NULL; - - if (btrfs_buffer_uptodate(eb, parent_transid)) - return eb; - - dev_nr = 0; - length = blocksize; - while (1) { - ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, - eb->start, &length, &multi, mirror_num); - BUG_ON(ret); - device = multi->stripes[0].dev; - eb->fd = device->fd; - device->total_ios++; - eb->dev_bytenr = multi->stripes[0].physical; - kfree(multi); - ret = read_extent_from_disk(eb); - if (ret == 0 && check_tree_block(root, eb) == 0 && - csum_tree_block(root, eb, 1) == 0 && - verify_parent_transid(eb->tree, eb, parent_transid) == 0) { - btrfs_set_buffer_uptodate(eb); - return eb; - } - num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, - eb->start, eb->len); - if (num_copies == 1) { - break; - } - mirror_num++; - if (mirror_num > num_copies) { - break; - } - } - free_extent_buffer(eb); - return NULL; -} - -int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *eb) -{ - int ret; - int dev_nr; - u64 length; - struct btrfs_multi_bio *multi = NULL; - - if (check_tree_block(root, eb)) - BUG(); - if (!btrfs_buffer_uptodate(eb, trans->transid)) - BUG(); - - btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); - csum_tree_block(root, eb, 0); - - dev_nr = 0; - length = eb->len; - ret = btrfs_map_block(&root->fs_info->mapping_tree, WRITE, - eb->start, &length, &multi, 0); - - while(dev_nr < multi->num_stripes) { - BUG_ON(ret); - eb->fd = multi->stripes[dev_nr].dev->fd; - eb->dev_bytenr = multi->stripes[dev_nr].physical; - multi->stripes[dev_nr].dev->total_ios++; - dev_nr++; - ret = write_extent_to_disk(eb); - BUG_ON(ret); - } - kfree(multi); - return 0; -} - -static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, - u32 stripesize, struct btrfs_root *root, - struct btrfs_fs_info *fs_info, u64 objectid) -{ - root->node = NULL; - root->commit_root = NULL; - root->sectorsize = sectorsize; - root->nodesize = nodesize; - root->leafsize = leafsize; - root->stripesize = stripesize; - root->ref_cows = 0; - root->track_dirty = 0; - - root->fs_info = fs_info; - root->objectid = objectid; - root->last_trans = 0; - root->highest_inode = 0; - root->last_inode_alloc = 0; - - INIT_LIST_HEAD(&root->dirty_list); - memset(&root->root_key, 0, sizeof(root->root_key)); - memset(&root->root_item, 0, sizeof(root->root_item)); - root->root_key.objectid = objectid; - return 0; -} - -static int update_cowonly_root(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - int ret; - u64 old_root_bytenr; - struct btrfs_root *tree_root = root->fs_info->tree_root; - - btrfs_write_dirty_block_groups(trans, root); - while(1) { - old_root_bytenr = btrfs_root_bytenr(&root->root_item); - if (old_root_bytenr == root->node->start) - break; - btrfs_set_root_bytenr(&root->root_item, - root->node->start); - btrfs_set_root_generation(&root->root_item, - trans->transid); - root->root_item.level = btrfs_header_level(root->node); - ret = btrfs_update_root(trans, tree_root, - &root->root_key, - &root->root_item); - BUG_ON(ret); - btrfs_write_dirty_block_groups(trans, root); - } - return 0; -} - -static int commit_tree_roots(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info) -{ - struct btrfs_root *root; - struct list_head *next; - struct extent_buffer *eb; - - if (fs_info->readonly) - return 0; - - eb = fs_info->tree_root->node; - extent_buffer_get(eb); - btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); - free_extent_buffer(eb); - - while(!list_empty(&fs_info->dirty_cowonly_roots)) { - next = fs_info->dirty_cowonly_roots.next; - list_del_init(next); - root = list_entry(next, struct btrfs_root, dirty_list); - update_cowonly_root(trans, root); - } - return 0; -} - -static int __commit_transaction(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - u64 start; - u64 end; - struct extent_buffer *eb; - struct extent_io_tree *tree = &root->fs_info->extent_cache; - int ret; - - while(1) { - ret = find_first_extent_bit(tree, 0, &start, &end, - EXTENT_DIRTY); - if (ret) - break; - while(start <= end) { - eb = find_first_extent_buffer(tree, start); - BUG_ON(!eb || eb->start != start); - ret = write_tree_block(trans, root, eb); - BUG_ON(ret); - start += eb->len; - clear_extent_buffer_dirty(eb); - free_extent_buffer(eb); - } - } - return 0; -} - -int btrfs_commit_transaction(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - int ret = 0; - struct btrfs_fs_info *fs_info = root->fs_info; - - if (root->commit_root == root->node) - goto commit_tree; - - free_extent_buffer(root->commit_root); - root->commit_root = NULL; - - btrfs_set_root_bytenr(&root->root_item, root->node->start); - btrfs_set_root_generation(&root->root_item, trans->transid); - root->root_item.level = btrfs_header_level(root->node); - ret = btrfs_update_root(trans, root->fs_info->tree_root, - &root->root_key, &root->root_item); - BUG_ON(ret); -commit_tree: - ret = commit_tree_roots(trans, fs_info); - BUG_ON(ret); - ret = __commit_transaction(trans, root); - BUG_ON(ret); - write_ctree_super(trans, root); - btrfs_finish_extent_commit(trans, fs_info->extent_root, - &fs_info->pinned_extents); - btrfs_free_transaction(root, trans); - free_extent_buffer(root->commit_root); - root->commit_root = NULL; - fs_info->running_transaction = NULL; - return 0; -} - -static int find_and_setup_root(struct btrfs_root *tree_root, - struct btrfs_fs_info *fs_info, - u64 objectid, struct btrfs_root *root) -{ - int ret; - u32 blocksize; - u64 generation; - - __setup_root(tree_root->nodesize, tree_root->leafsize, - tree_root->sectorsize, tree_root->stripesize, - root, fs_info, objectid); - ret = btrfs_find_last_root(tree_root, objectid, - &root->root_item, &root->root_key); - BUG_ON(ret); - - blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); - generation = btrfs_root_generation(&root->root_item); - root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), - blocksize, generation); - BUG_ON(!root->node); - return 0; -} - -static int find_and_setup_log_root(struct btrfs_root *tree_root, - struct btrfs_fs_info *fs_info, - struct btrfs_super_block *disk_super) -{ - u32 blocksize; - u64 blocknr = btrfs_super_log_root(disk_super); - struct btrfs_root *log_root = malloc(sizeof(struct btrfs_root)); - - if (blocknr == 0) - return 0; - - blocksize = btrfs_level_size(tree_root, - btrfs_super_log_root_level(disk_super)); - - __setup_root(tree_root->nodesize, tree_root->leafsize, - tree_root->sectorsize, tree_root->stripesize, - log_root, fs_info, BTRFS_TREE_LOG_OBJECTID); - - log_root->node = read_tree_block(tree_root, blocknr, - blocksize, - btrfs_super_generation(disk_super) + 1); - - fs_info->log_root_tree = log_root; - BUG_ON(!log_root->node); - return 0; -} - - -int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, - struct btrfs_root *root) -{ - if (root->node) - free_extent_buffer(root->node); - if (root->commit_root) - free_extent_buffer(root->commit_root); - kfree(root); - return 0; -} - -static int free_fs_roots(struct btrfs_fs_info *fs_info) -{ - struct cache_extent *cache; - struct btrfs_root *root; - - while (1) { - cache = find_first_cache_extent(&fs_info->fs_root_cache, 0); - if (!cache) - break; - root = container_of(cache, struct btrfs_root, cache); - remove_cache_extent(&fs_info->fs_root_cache, cache); - btrfs_free_fs_root(fs_info, root); - } - return 0; -} - -struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info, - struct btrfs_key *location) -{ - struct btrfs_root *root; - struct btrfs_root *tree_root = fs_info->tree_root; - struct btrfs_path *path; - struct extent_buffer *l; - u64 generation; - u32 blocksize; - int ret = 0; - - root = malloc(sizeof(*root)); - if (!root) - return ERR_PTR(-ENOMEM); - memset(root, 0, sizeof(*root)); - if (location->offset == (u64)-1) { - ret = find_and_setup_root(tree_root, fs_info, - location->objectid, root); - if (ret) { - free(root); - return ERR_PTR(ret); - } - goto insert; - } - - __setup_root(tree_root->nodesize, tree_root->leafsize, - tree_root->sectorsize, tree_root->stripesize, - root, fs_info, location->objectid); - - path = btrfs_alloc_path(); - BUG_ON(!path); - ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0); - if (ret != 0) { - if (ret > 0) - ret = -ENOENT; - goto out; - } - l = path->nodes[0]; - read_extent_buffer(l, &root->root_item, - btrfs_item_ptr_offset(l, path->slots[0]), - sizeof(root->root_item)); - memcpy(&root->root_key, location, sizeof(*location)); - ret = 0; -out: - btrfs_release_path(root, path); - btrfs_free_path(path); - if (ret) { - free(root); - return ERR_PTR(ret); - } - generation = btrfs_root_generation(&root->root_item); - blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); - root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), - blocksize, generation); - BUG_ON(!root->node); -insert: - root->ref_cows = 1; - return root; -} - -struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, - struct btrfs_key *location) -{ - struct btrfs_root *root; - struct cache_extent *cache; - int ret; - - if (location->objectid == BTRFS_ROOT_TREE_OBJECTID) - return fs_info->tree_root; - if (location->objectid == BTRFS_EXTENT_TREE_OBJECTID) - return fs_info->extent_root; - if (location->objectid == BTRFS_CHUNK_TREE_OBJECTID) - return fs_info->chunk_root; - if (location->objectid == BTRFS_DEV_TREE_OBJECTID) - return fs_info->dev_root; - if (location->objectid == BTRFS_CSUM_TREE_OBJECTID) - return fs_info->csum_root; - - BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID || - location->offset != (u64)-1); - - cache = find_cache_extent(&fs_info->fs_root_cache, - location->objectid, 1); - if (cache) - return container_of(cache, struct btrfs_root, cache); - - root = btrfs_read_fs_root_no_cache(fs_info, location); - if (IS_ERR(root)) - return root; - - root->cache.start = location->objectid; - root->cache.size = 1; - ret = insert_existing_cache_extent(&fs_info->fs_root_cache, - &root->cache); - BUG_ON(ret); - return root; -} - -struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes) -{ - int fp; - struct btrfs_root *root; - int flags = O_CREAT | O_RDWR; - - if (!writes) - flags = O_RDONLY; - - fp = open(filename, flags, 0600); - if (fp < 0) { - fprintf (stderr, "Could not open %s\n", filename); - return NULL; - } - root = open_ctree_fd(fp, filename, sb_bytenr, writes); - close(fp); - - return root; -} - -struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, - int writes) -{ - u32 sectorsize; - u32 nodesize; - u32 leafsize; - u32 blocksize; - u32 stripesize; - u64 generation; - struct btrfs_key key; - struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root)); - struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root)); - struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root)); - struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root)); - struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root)); - struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info)); - int ret; - struct btrfs_super_block *disk_super; - struct btrfs_fs_devices *fs_devices = NULL; - u64 total_devs; - u64 features; - - if (sb_bytenr == 0) - sb_bytenr = BTRFS_SUPER_INFO_OFFSET; - - ret = btrfs_scan_one_device(fp, path, &fs_devices, - &total_devs, sb_bytenr); - - if (ret) { - fprintf(stderr, "No valid Btrfs found on %s\n", path); - return NULL; - } - - if (total_devs != 1) { - ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1); - BUG_ON(ret); - } - - memset(fs_info, 0, sizeof(*fs_info)); - fs_info->tree_root = tree_root; - fs_info->extent_root = extent_root; - fs_info->chunk_root = chunk_root; - fs_info->dev_root = dev_root; - fs_info->csum_root = csum_root; - - if (!writes) - fs_info->readonly = 1; - - extent_io_tree_init(&fs_info->extent_cache); - extent_io_tree_init(&fs_info->free_space_cache); - extent_io_tree_init(&fs_info->block_group_cache); - extent_io_tree_init(&fs_info->pinned_extents); - extent_io_tree_init(&fs_info->pending_del); - extent_io_tree_init(&fs_info->extent_ins); - cache_tree_init(&fs_info->fs_root_cache); - - cache_tree_init(&fs_info->mapping_tree.cache_tree); - - mutex_init(&fs_info->fs_mutex); - fs_info->fs_devices = fs_devices; - INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); - INIT_LIST_HEAD(&fs_info->space_info); - - __setup_root(4096, 4096, 4096, 4096, tree_root, - fs_info, BTRFS_ROOT_TREE_OBJECTID); - - if (writes) - ret = btrfs_open_devices(fs_devices, O_RDWR); - else - ret = btrfs_open_devices(fs_devices, O_RDONLY); - BUG_ON(ret); - - fs_info->super_bytenr = sb_bytenr; - disk_super = &fs_info->super_copy; - ret = btrfs_read_dev_super(fs_devices->latest_bdev, - disk_super, sb_bytenr); - if (ret) { - printk("No valid btrfs found\n"); - BUG_ON(1); - } - - memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); - - - features = btrfs_super_incompat_flags(disk_super) & - ~BTRFS_FEATURE_INCOMPAT_SUPP; - if (features) { - printk("couldn't open because of unsupported " - "option features (%Lx).\n", features); - BUG_ON(1); - } - - features = btrfs_super_incompat_flags(disk_super); - if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) { - features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF; - btrfs_set_super_incompat_flags(disk_super, features); - } - - features = btrfs_super_compat_ro_flags(disk_super) & - ~BTRFS_FEATURE_COMPAT_RO_SUPP; - if (writes && features) { - printk("couldn't open RDWR because of unsupported " - "option features (%Lx).\n", features); - BUG_ON(1); - } - - nodesize = btrfs_super_nodesize(disk_super); - leafsize = btrfs_super_leafsize(disk_super); - sectorsize = btrfs_super_sectorsize(disk_super); - stripesize = btrfs_super_stripesize(disk_super); - tree_root->nodesize = nodesize; - tree_root->leafsize = leafsize; - tree_root->sectorsize = sectorsize; - tree_root->stripesize = stripesize; - - ret = btrfs_read_sys_array(tree_root); - BUG_ON(ret); - blocksize = btrfs_level_size(tree_root, - btrfs_super_chunk_root_level(disk_super)); - generation = btrfs_super_chunk_root_generation(disk_super); - - __setup_root(nodesize, leafsize, sectorsize, stripesize, - chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID); - - chunk_root->node = read_tree_block(chunk_root, - btrfs_super_chunk_root(disk_super), - blocksize, generation); - - BUG_ON(!chunk_root->node); - - read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid, - (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node), - BTRFS_UUID_SIZE); - - if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) { - ret = btrfs_read_chunk_tree(chunk_root); - BUG_ON(ret); - } - - blocksize = btrfs_level_size(tree_root, - btrfs_super_root_level(disk_super)); - generation = btrfs_super_generation(disk_super); - - tree_root->node = read_tree_block(tree_root, - btrfs_super_root(disk_super), - blocksize, generation); - BUG_ON(!tree_root->node); - ret = find_and_setup_root(tree_root, fs_info, - BTRFS_EXTENT_TREE_OBJECTID, extent_root); - BUG_ON(ret); - extent_root->track_dirty = 1; - - ret = find_and_setup_root(tree_root, fs_info, - BTRFS_DEV_TREE_OBJECTID, dev_root); - BUG_ON(ret); - dev_root->track_dirty = 1; - - ret = find_and_setup_root(tree_root, fs_info, - BTRFS_CSUM_TREE_OBJECTID, csum_root); - BUG_ON(ret); - csum_root->track_dirty = 1; - - BUG_ON(ret); - - find_and_setup_log_root(tree_root, fs_info, disk_super); - - fs_info->generation = generation + 1; - btrfs_read_block_groups(fs_info->tree_root); - - key.objectid = BTRFS_FS_TREE_OBJECTID; - key.type = BTRFS_ROOT_ITEM_KEY; - key.offset = (u64)-1; - fs_info->fs_root = btrfs_read_fs_root(fs_info, &key); - - fs_info->data_alloc_profile = (u64)-1; - fs_info->metadata_alloc_profile = (u64)-1; - fs_info->system_alloc_profile = fs_info->metadata_alloc_profile; - - return fs_info->fs_root; -} - -int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr) -{ - u8 fsid[BTRFS_FSID_SIZE]; - struct btrfs_super_block buf; - int i; - int ret; - u64 transid = 0; - u64 bytenr; - - if (sb_bytenr != BTRFS_SUPER_INFO_OFFSET) { - ret = pread64(fd, &buf, sizeof(buf), sb_bytenr); - if (ret < sizeof(buf)) - return -1; - - if (btrfs_super_bytenr(&buf) != sb_bytenr || - strncmp((char *)(&buf.magic), BTRFS_MAGIC, - sizeof(buf.magic))) - return -1; - - memcpy(sb, &buf, sizeof(*sb)); - return 0; - } - - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - bytenr = btrfs_sb_offset(i); - ret = pread64(fd, &buf, sizeof(buf), bytenr); - if (ret < sizeof(buf)) - break; - - if (btrfs_super_bytenr(&buf) != bytenr || - strncmp((char *)(&buf.magic), BTRFS_MAGIC, - sizeof(buf.magic))) - continue; - - if (i == 0) - memcpy(fsid, buf.fsid, sizeof(fsid)); - else if (memcmp(fsid, buf.fsid, sizeof(fsid))) - continue; - - if (btrfs_super_generation(&buf) > transid) { - memcpy(sb, &buf, sizeof(*sb)); - transid = btrfs_super_generation(&buf); - } - } - - return transid > 0 ? 0 : -1; -} - -int write_dev_supers(struct btrfs_root *root, struct btrfs_super_block *sb, - struct btrfs_device *device) -{ - u64 bytenr; - u32 crc; - int i, ret; - - if (root->fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) { - btrfs_set_super_bytenr(sb, root->fs_info->super_bytenr); - - 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, (char *)&sb->csum[0]); - - ret = pwrite64(device->fd, sb, BTRFS_SUPER_INFO_SIZE, - root->fs_info->super_bytenr); - BUG_ON(ret != BTRFS_SUPER_INFO_SIZE); - return 0; - } - - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - bytenr = btrfs_sb_offset(i); - if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes) - break; - - btrfs_set_super_bytenr(sb, bytenr); - - 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, (char *)&sb->csum[0]); - - ret = pwrite64(device->fd, sb, BTRFS_SUPER_INFO_SIZE, bytenr); - BUG_ON(ret != BTRFS_SUPER_INFO_SIZE); - } - return 0; -} - -int write_all_supers(struct btrfs_root *root) -{ - struct list_head *cur; - struct list_head *head = &root->fs_info->fs_devices->devices; - struct btrfs_device *dev; - struct btrfs_super_block *sb; - struct btrfs_dev_item *dev_item; - int ret; - u64 flags; - - sb = &root->fs_info->super_copy; - dev_item = &sb->dev_item; - list_for_each(cur, head) { - dev = list_entry(cur, struct btrfs_device, dev_list); - if (!dev->writeable) - continue; - - btrfs_set_stack_device_generation(dev_item, 0); - btrfs_set_stack_device_type(dev_item, dev->type); - btrfs_set_stack_device_id(dev_item, dev->devid); - btrfs_set_stack_device_total_bytes(dev_item, dev->total_bytes); - btrfs_set_stack_device_bytes_used(dev_item, dev->bytes_used); - btrfs_set_stack_device_io_align(dev_item, dev->io_align); - btrfs_set_stack_device_io_width(dev_item, dev->io_width); - btrfs_set_stack_device_sector_size(dev_item, dev->sector_size); - memcpy(dev_item->uuid, dev->uuid, BTRFS_UUID_SIZE); - memcpy(dev_item->fsid, dev->fs_devices->fsid, BTRFS_UUID_SIZE); - - flags = btrfs_super_flags(sb); - btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN); - - ret = write_dev_supers(root, sb, dev); - BUG_ON(ret); - } - return 0; -} - -int write_ctree_super(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - int ret; - struct btrfs_root *tree_root = root->fs_info->tree_root; - struct btrfs_root *chunk_root = root->fs_info->chunk_root; - - if (root->fs_info->readonly) - return 0; - - btrfs_set_super_generation(&root->fs_info->super_copy, - trans->transid); - btrfs_set_super_root(&root->fs_info->super_copy, - tree_root->node->start); - btrfs_set_super_root_level(&root->fs_info->super_copy, - btrfs_header_level(tree_root->node)); - btrfs_set_super_chunk_root(&root->fs_info->super_copy, - chunk_root->node->start); - btrfs_set_super_chunk_root_level(&root->fs_info->super_copy, - btrfs_header_level(chunk_root->node)); - btrfs_set_super_chunk_root_generation(&root->fs_info->super_copy, - btrfs_header_generation(chunk_root->node)); - - ret = write_all_supers(root); - if (ret) - fprintf(stderr, "failed to write new super block err %d\n", ret); - return ret; -} - -static int close_all_devices(struct btrfs_fs_info *fs_info) -{ - struct list_head *list; - struct list_head *next; - struct btrfs_device *device; - - return 0; - - list = &fs_info->fs_devices->devices; - list_for_each(next, list) { - device = list_entry(next, struct btrfs_device, dev_list); - close(device->fd); - } - return 0; -} - -int close_ctree(struct btrfs_root *root) -{ - int ret; - struct btrfs_trans_handle *trans; - struct btrfs_fs_info *fs_info = root->fs_info; - - trans = btrfs_start_transaction(root, 1); - btrfs_commit_transaction(trans, root); - trans = btrfs_start_transaction(root, 1); - ret = commit_tree_roots(trans, fs_info); - BUG_ON(ret); - ret = __commit_transaction(trans, root); - BUG_ON(ret); - write_ctree_super(trans, root); - btrfs_free_transaction(root, trans); - btrfs_free_block_groups(fs_info); - - free_fs_roots(fs_info); - - if (fs_info->extent_root->node) - free_extent_buffer(fs_info->extent_root->node); - if (fs_info->tree_root->node) - free_extent_buffer(fs_info->tree_root->node); - if (fs_info->chunk_root->node) - free_extent_buffer(fs_info->chunk_root->node); - if (fs_info->dev_root->node) - free_extent_buffer(fs_info->dev_root->node); - if (fs_info->csum_root->node) - free_extent_buffer(fs_info->csum_root->node); - - if (root->fs_info->log_root_tree) { - if (root->fs_info->log_root_tree->node) - free_extent_buffer(root->fs_info->log_root_tree->node); - free(root->fs_info->log_root_tree); - } - - close_all_devices(root->fs_info); - extent_io_tree_cleanup(&fs_info->extent_cache); - extent_io_tree_cleanup(&fs_info->free_space_cache); - extent_io_tree_cleanup(&fs_info->block_group_cache); - extent_io_tree_cleanup(&fs_info->pinned_extents); - extent_io_tree_cleanup(&fs_info->pending_del); - extent_io_tree_cleanup(&fs_info->extent_ins); - - free(fs_info->tree_root); - free(fs_info->extent_root); - free(fs_info->chunk_root); - free(fs_info->dev_root); - free(fs_info->csum_root); - free(fs_info); - - return 0; -} - -int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *eb) -{ - return clear_extent_buffer_dirty(eb); -} - -int wait_on_tree_block_writeback(struct btrfs_root *root, - struct extent_buffer *eb) -{ - return 0; -} - -void btrfs_mark_buffer_dirty(struct extent_buffer *eb) -{ - set_extent_buffer_dirty(eb); -} - -int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid) -{ - int ret; - - ret = extent_buffer_uptodate(buf); - if (!ret) - return ret; - - ret = verify_parent_transid(buf->tree, buf, parent_transid); - return !ret; -} - -int btrfs_set_buffer_uptodate(struct extent_buffer *eb) -{ - return set_extent_buffer_uptodate(eb); -} diff --git a/disk-io.h b/disk-io.h deleted file mode 100644 index 49e5692..0000000 --- a/disk-io.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __DISKIO__ -#define __DISKIO__ - -#define BTRFS_SUPER_INFO_OFFSET (64 * 1024) -#define BTRFS_SUPER_INFO_SIZE 4096 - -#define BTRFS_SUPER_MIRROR_MAX 3 -#define BTRFS_SUPER_MIRROR_SHIFT 12 - -static inline u64 btrfs_sb_offset(int mirror) -{ - u64 start = 16 * 1024; - if (mirror) - return start << (BTRFS_SUPER_MIRROR_SHIFT * mirror); - return BTRFS_SUPER_INFO_OFFSET; -} - -struct btrfs_device; - -struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, - u32 blocksize, u64 parent_transid); -int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, - u64 parent_transid); -struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root, - u64 bytenr, u32 blocksize); -int clean_tree_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct extent_buffer *buf); -struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes); -struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, - int writes); -int close_ctree(struct btrfs_root *root); -int write_ctree_super(struct btrfs_trans_handle *trans, - struct btrfs_root *root); -int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr); -int btrfs_map_bh_to_logical(struct btrfs_root *root, struct extent_buffer *bh, - u64 logical); -struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root, - u64 bytenr, u32 blocksize); -struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, - struct btrfs_key *location); -struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info, - struct btrfs_key *location); -int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root); -void btrfs_mark_buffer_dirty(struct extent_buffer *buf); -int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid); -int btrfs_set_buffer_uptodate(struct extent_buffer *buf); -int wait_on_tree_block_writeback(struct btrfs_root *root, - struct extent_buffer *buf); -u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len); -void btrfs_csum_final(u32 crc, char *result); - -int btrfs_commit_transaction(struct btrfs_trans_handle *trans, - struct btrfs_root *root); -int btrfs_open_device(struct btrfs_device *dev); -int csum_tree_block_size(struct extent_buffer *buf, u16 csum_sectorsize, - int verify); -int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, - int verify); -int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid); -#endif diff --git a/extent-cache.c b/extent-cache.c deleted file mode 100644 index b871e18..0000000 --- a/extent-cache.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include "kerncompat.h" -#include "extent-cache.h" - -void cache_tree_init(struct cache_tree *tree) -{ - tree->root.rb_node = NULL; -} - -static struct rb_node *tree_insert(struct rb_root *root, u64 offset, - u64 size, struct rb_node *node) -{ - struct rb_node ** p = &root->rb_node; - struct rb_node * parent = NULL; - struct cache_extent *entry; - - while(*p) { - parent = *p; - entry = rb_entry(parent, struct cache_extent, rb_node); - - if (offset + size <= entry->start) - p = &(*p)->rb_left; - else if (offset >= entry->start + entry->size) - p = &(*p)->rb_right; - else - return parent; - } - - entry = rb_entry(parent, struct cache_extent, rb_node); - rb_link_node(node, parent, p); - rb_insert_color(node, root); - return NULL; -} - -static struct rb_node *__tree_search(struct rb_root *root, u64 offset, - u64 size, struct rb_node **prev_ret) -{ - struct rb_node * n = root->rb_node; - struct rb_node *prev = NULL; - struct cache_extent *entry; - struct cache_extent *prev_entry = NULL; - - while(n) { - entry = rb_entry(n, struct cache_extent, rb_node); - prev = n; - prev_entry = entry; - - if (offset + size <= entry->start) - n = n->rb_left; - else if (offset >= entry->start + entry->size) - n = n->rb_right; - else - return n; - } - if (!prev_ret) - return NULL; - - while(prev && offset >= prev_entry->start + prev_entry->size) { - prev = rb_next(prev); - prev_entry = rb_entry(prev, struct cache_extent, rb_node); - } - *prev_ret = prev; - return NULL; -} - -struct cache_extent *alloc_cache_extent(u64 start, u64 size) -{ - struct cache_extent *pe = malloc(sizeof(*pe)); - - if (!pe) - return pe; - pe->start = start; - pe->size = size; - return pe; -} - -int insert_existing_cache_extent(struct cache_tree *tree, - struct cache_extent *pe) -{ - struct rb_node *found; - struct cache_extent *entry; - - found = tree_insert(&tree->root, pe->start, pe->size, &pe->rb_node); - if (found) { - entry = rb_entry(found, struct cache_extent, rb_node); - return -EEXIST; - } - return 0; -} - -int insert_cache_extent(struct cache_tree *tree, u64 start, u64 size) -{ - struct cache_extent *pe = alloc_cache_extent(start, size); - int ret; - ret = insert_existing_cache_extent(tree, pe); - if (ret) - free(pe); - return ret; -} - -struct cache_extent *find_cache_extent(struct cache_tree *tree, - u64 start, u64 size) -{ - struct rb_node *prev; - struct rb_node *ret; - struct cache_extent *entry; - ret = __tree_search(&tree->root, start, size, &prev); - if (!ret) - return NULL; - - entry = rb_entry(ret, struct cache_extent, rb_node); - return entry; -} - -struct cache_extent *find_first_cache_extent(struct cache_tree *tree, - u64 start) -{ - struct rb_node *prev; - struct rb_node *ret; - struct cache_extent *entry; - - ret = __tree_search(&tree->root, start, 1, &prev); - if (!ret) - ret = prev; - if (!ret) - return NULL; - entry = rb_entry(ret, struct cache_extent, rb_node); - return entry; -} - -struct cache_extent *prev_cache_extent(struct cache_extent *pe) -{ - struct rb_node *node = rb_prev(&pe->rb_node); - - if (!node) - return NULL; - return rb_entry(node, struct cache_extent, rb_node); -} - -struct cache_extent *next_cache_extent(struct cache_extent *pe) -{ - struct rb_node *node = rb_next(&pe->rb_node); - - if (!node) - return NULL; - return rb_entry(node, struct cache_extent, rb_node); -} - -void remove_cache_extent(struct cache_tree *tree, - struct cache_extent *pe) -{ - rb_erase(&pe->rb_node, &tree->root); -} - diff --git a/extent-cache.h b/extent-cache.h deleted file mode 100644 index 7f2f2a6..0000000 --- a/extent-cache.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __PENDING_EXTENT__ -#define __PENDING_EXTENT__ -#include "kerncompat.h" -#include "rbtree.h" - -struct cache_tree { - struct rb_root root; -}; - -struct cache_extent { - struct rb_node rb_node; - u64 start; - u64 size; -}; - -void cache_tree_init(struct cache_tree *tree); -void remove_cache_extent(struct cache_tree *tree, - struct cache_extent *pe); -struct cache_extent *find_first_cache_extent(struct cache_tree *tree, - u64 start); -struct cache_extent *prev_cache_extent(struct cache_extent *pe); -struct cache_extent *next_cache_extent(struct cache_extent *pe); -struct cache_extent *find_cache_extent(struct cache_tree *tree, - u64 start, u64 size); -int insert_cache_extent(struct cache_tree *tree, u64 start, u64 size); -int insert_existing_cache_extent(struct cache_tree *tree, - struct cache_extent *pe); - -static inline int cache_tree_empty(struct cache_tree *tree) -{ - return RB_EMPTY_ROOT(&tree->root); -} - -static inline void free_cache_extent(struct cache_extent *pe) -{ - free(pe); -} - -struct cache_extent *alloc_pending_extent(u64 start, u64 size); - -#endif diff --git a/extent-tree.c b/extent-tree.c deleted file mode 100644 index b2f9bb2..0000000 --- a/extent-tree.c +++ /dev/null @@ -1,3285 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "print-tree.h" -#include "transaction.h" -#include "crc32c.h" -#include "volumes.h" - -#define BLOCK_GROUP_DATA EXTENT_WRITEBACK -#define BLOCK_GROUP_METADATA EXTENT_UPTODATE -#define BLOCK_GROUP_SYSTEM EXTENT_NEW - -#define BLOCK_GROUP_DIRTY EXTENT_DIRTY - -#define PENDING_EXTENT_INSERT 0 -#define PENDING_EXTENT_DELETE 1 -#define PENDING_BACKREF_UPDATE 2 - -struct pending_extent_op { - int type; - u64 bytenr; - u64 num_bytes; - u64 flags; - struct btrfs_disk_key key; - int level; -}; - -static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 root_objectid, u64 generation, - u64 flags, struct btrfs_disk_key *key, - int level, struct btrfs_key *ins); -static int __free_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner_objectid, - u64 owner_offset, int refs_to_drop); -static int finish_current_insert(struct btrfs_trans_handle *trans, struct - btrfs_root *extent_root); -static int del_pending_extents(struct btrfs_trans_handle *trans, struct - btrfs_root *extent_root); - -static int remove_sb_from_cache(struct btrfs_root *root, - struct btrfs_block_group_cache *cache) -{ - u64 bytenr; - u64 *logical; - int stripe_len; - int i, nr, ret; - struct extent_io_tree *free_space_cache; - - free_space_cache = &root->fs_info->free_space_cache; - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - bytenr = btrfs_sb_offset(i); - ret = btrfs_rmap_block(&root->fs_info->mapping_tree, - cache->key.objectid, bytenr, 0, - &logical, &nr, &stripe_len); - BUG_ON(ret); - while (nr--) { - clear_extent_dirty(free_space_cache, logical[nr], - logical[nr] + stripe_len - 1, GFP_NOFS); - } - kfree(logical); - } - return 0; -} - -static int cache_block_group(struct btrfs_root *root, - struct btrfs_block_group_cache *block_group) -{ - struct btrfs_path *path; - int ret; - struct btrfs_key key; - struct extent_buffer *leaf; - struct extent_io_tree *free_space_cache; - int slot; - u64 last; - u64 hole_size; - - if (!block_group) - return 0; - - root = root->fs_info->extent_root; - free_space_cache = &root->fs_info->free_space_cache; - - if (block_group->cached) - return 0; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - path->reada = 2; - last = max_t(u64, block_group->key.objectid, BTRFS_SUPER_INFO_OFFSET); - key.objectid = last; - key.offset = 0; - btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) - goto err; - - while(1) { - leaf = path->nodes[0]; - slot = path->slots[0]; - if (slot >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, path); - if (ret < 0) - goto err; - if (ret == 0) { - continue; - } else { - break; - } - } - btrfs_item_key_to_cpu(leaf, &key, slot); - if (key.objectid < block_group->key.objectid) { - goto next; - } - if (key.objectid >= block_group->key.objectid + - block_group->key.offset) { - break; - } - - if (btrfs_key_type(&key) == BTRFS_EXTENT_ITEM_KEY) { - if (key.objectid > last) { - hole_size = key.objectid - last; - set_extent_dirty(free_space_cache, last, - last + hole_size - 1, - GFP_NOFS); - } - last = key.objectid + key.offset; - } -next: - path->slots[0]++; - } - - if (block_group->key.objectid + - block_group->key.offset > last) { - hole_size = block_group->key.objectid + - block_group->key.offset - last; - set_extent_dirty(free_space_cache, last, - last + hole_size - 1, GFP_NOFS); - } - remove_sb_from_cache(root, block_group); - block_group->cached = 1; -err: - btrfs_free_path(path); - return 0; -} - -struct btrfs_block_group_cache *btrfs_lookup_first_block_group(struct - btrfs_fs_info *info, - u64 bytenr) -{ - struct extent_io_tree *block_group_cache; - struct btrfs_block_group_cache *block_group = NULL; - u64 ptr; - u64 start; - u64 end; - int ret; - - bytenr = max_t(u64, bytenr, - BTRFS_SUPER_INFO_OFFSET + BTRFS_SUPER_INFO_SIZE); - block_group_cache = &info->block_group_cache; - ret = find_first_extent_bit(block_group_cache, - bytenr, &start, &end, - BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA | - BLOCK_GROUP_SYSTEM); - if (ret) { - return NULL; - } - ret = get_state_private(block_group_cache, start, &ptr); - if (ret) - return NULL; - - block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr; - return block_group; -} - -struct btrfs_block_group_cache *btrfs_lookup_block_group(struct - btrfs_fs_info *info, - u64 bytenr) -{ - struct extent_io_tree *block_group_cache; - struct btrfs_block_group_cache *block_group = NULL; - u64 ptr; - u64 start; - u64 end; - int ret; - - block_group_cache = &info->block_group_cache; - ret = find_first_extent_bit(block_group_cache, - bytenr, &start, &end, - BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA | - BLOCK_GROUP_SYSTEM); - if (ret) { - return NULL; - } - ret = get_state_private(block_group_cache, start, &ptr); - if (ret) - return NULL; - - block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr; - if (block_group->key.objectid <= bytenr && bytenr < - block_group->key.objectid + block_group->key.offset) - return block_group; - return NULL; -} - -static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits) -{ - return (cache->flags & bits) == bits; -} - -static int noinline find_search_start(struct btrfs_root *root, - struct btrfs_block_group_cache **cache_ret, - u64 *start_ret, int num, int data) -{ - int ret; - struct btrfs_block_group_cache *cache = *cache_ret; - u64 last; - u64 start = 0; - u64 end = 0; - u64 search_start = *start_ret; - int wrapped = 0; - - if (!cache) { - goto out; - } -again: - ret = cache_block_group(root, cache); - if (ret) - goto out; - - last = max(search_start, cache->key.objectid); - if (cache->ro || !block_group_bits(cache, data)) { - goto new_group; - } - - while(1) { - ret = find_first_extent_bit(&root->fs_info->free_space_cache, - last, &start, &end, EXTENT_DIRTY); - if (ret) { - goto new_group; - } - - start = max(last, start); - last = end + 1; - if (last - start < num) { - continue; - } - if (start + num > cache->key.objectid + cache->key.offset) { - goto new_group; - } - *start_ret = start; - return 0; - } -out: - cache = btrfs_lookup_block_group(root->fs_info, search_start); - if (!cache) { - printk("Unable to find block group for %llu\n", - (unsigned long long)search_start); - WARN_ON(1); - } - return -ENOSPC; - -new_group: - last = cache->key.objectid + cache->key.offset; -wrapped: - cache = btrfs_lookup_first_block_group(root->fs_info, last); - if (!cache) { -no_cache: - if (!wrapped) { - wrapped = 1; - last = search_start; - goto wrapped; - } - goto out; - } - cache = btrfs_find_block_group(root, cache, last, data, 0); - cache = btrfs_find_block_group(root, cache, last, data, 0); - if (!cache) - goto no_cache; - - *cache_ret = cache; - goto again; -} - -static u64 div_factor(u64 num, int factor) -{ - if (factor == 10) - return num; - num *= factor; - num /= 10; - return num; -} - -static int block_group_state_bits(u64 flags) -{ - int bits = 0; - if (flags & BTRFS_BLOCK_GROUP_DATA) - bits |= BLOCK_GROUP_DATA; - if (flags & BTRFS_BLOCK_GROUP_METADATA) - bits |= BLOCK_GROUP_METADATA; - if (flags & BTRFS_BLOCK_GROUP_SYSTEM) - bits |= BLOCK_GROUP_SYSTEM; - return bits; -} - -struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, - struct btrfs_block_group_cache - *hint, u64 search_start, - int data, int owner) -{ - struct btrfs_block_group_cache *cache; - struct extent_io_tree *block_group_cache; - struct btrfs_block_group_cache *found_group = NULL; - struct btrfs_fs_info *info = root->fs_info; - u64 used; - u64 last = 0; - u64 hint_last; - u64 start; - u64 end; - u64 free_check; - u64 ptr; - int bit; - int ret; - int full_search = 0; - int factor = 10; - - block_group_cache = &info->block_group_cache; - - if (!owner) - factor = 10; - - bit = block_group_state_bits(data); - - if (search_start) { - struct btrfs_block_group_cache *shint; - shint = btrfs_lookup_block_group(info, search_start); - if (shint && !shint->ro && block_group_bits(shint, data)) { - used = btrfs_block_group_used(&shint->item); - if (used + shint->pinned < - div_factor(shint->key.offset, factor)) { - return shint; - } - } - } - if (hint && !hint->ro && block_group_bits(hint, data)) { - used = btrfs_block_group_used(&hint->item); - if (used + hint->pinned < - div_factor(hint->key.offset, factor)) { - return hint; - } - last = hint->key.objectid + hint->key.offset; - hint_last = last; - } else { - if (hint) - hint_last = max(hint->key.objectid, search_start); - else - hint_last = search_start; - - last = hint_last; - } -again: - while(1) { - ret = find_first_extent_bit(block_group_cache, last, - &start, &end, bit); - if (ret) - break; - - ret = get_state_private(block_group_cache, start, &ptr); - if (ret) - break; - - cache = (struct btrfs_block_group_cache *)(unsigned long)ptr; - last = cache->key.objectid + cache->key.offset; - used = btrfs_block_group_used(&cache->item); - - if (!cache->ro && block_group_bits(cache, data)) { - if (full_search) - free_check = cache->key.offset; - else - free_check = div_factor(cache->key.offset, - factor); - - if (used + cache->pinned < free_check) { - found_group = cache; - goto found; - } - } - cond_resched(); - } - if (!full_search) { - last = search_start; - full_search = 1; - goto again; - } -found: - return found_group; -} - -/* - * Back reference rules. Back refs have three main goals: - * - * 1) differentiate between all holders of references to an extent so that - * when a reference is dropped we can make sure it was a valid reference - * before freeing the extent. - * - * 2) Provide enough information to quickly find the holders of an extent - * if we notice a given block is corrupted or bad. - * - * 3) Make it easy to migrate blocks for FS shrinking or storage pool - * maintenance. This is actually the same as #2, but with a slightly - * different use case. - * - * There are two kinds of back refs. The implicit back refs is optimized - * for pointers in non-shared tree blocks. For a given pointer in a block, - * back refs of this kind provide information about the block's owner tree - * and the pointer's key. These information allow us to find the block by - * b-tree searching. The full back refs is for pointers in tree blocks not - * referenced by their owner trees. The location of tree block is recorded - * in the back refs. Actually the full back refs is generic, and can be - * used in all cases the implicit back refs is used. The major shortcoming - * of the full back refs is its overhead. Every time a tree block gets - * COWed, we have to update back refs entry for all pointers in it. - * - * For a newly allocated tree block, we use implicit back refs for - * pointers in it. This means most tree related operations only involve - * implicit back refs. For a tree block created in old transaction, the - * only way to drop a reference to it is COW it. So we can detect the - * event that tree block loses its owner tree's reference and do the - * back refs conversion. - * - * When a tree block is COW'd through a tree, there are four cases: - * - * The reference count of the block is one and the tree is the block's - * owner tree. Nothing to do in this case. - * - * The reference count of the block is one and the tree is not the - * block's owner tree. In this case, full back refs is used for pointers - * in the block. Remove these full back refs, add implicit back refs for - * every pointers in the new block. - * - * The reference count of the block is greater than one and the tree is - * the block's owner tree. In this case, implicit back refs is used for - * pointers in the block. Add full back refs for every pointers in the - * block, increase lower level extents' reference counts. The original - * implicit back refs are entailed to the new block. - * - * The reference count of the block is greater than one and the tree is - * not the block's owner tree. Add implicit back refs for every pointer in - * the new block, increase lower level extents' reference count. - * - * Back Reference Key composing: - * - * The key objectid corresponds to the first byte in the extent, - * The key type is used to differentiate between types of back refs. - * There are different meanings of the key offset for different types - * of back refs. - * - * File extents can be referenced by: - * - * - multiple snapshots, subvolumes, or different generations in one subvol - * - different files inside a single subvolume - * - different offsets inside a file (bookend extents in file.c) - * - * The extent ref structure for the implicit back refs has fields for: - * - * - Objectid of the subvolume root - * - objectid of the file holding the reference - * - original offset in the file - * - how many bookend extents - * - * The key offset for the implicit back refs is hash of the first - * three fields. - * - * The extent ref structure for the full back refs has field for: - * - * - number of pointers in the tree leaf - * - * The key offset for the implicit back refs is the first byte of - * the tree leaf - * - * When a file extent is allocated, The implicit back refs is used. - * the fields are filled in: - * - * (root_key.objectid, inode objectid, offset in file, 1) - * - * When a file extent is removed file truncation, we find the - * corresponding implicit back refs and check the following fields: - * - * (btrfs_header_owner(leaf), inode objectid, offset in file) - * - * Btree extents can be referenced by: - * - * - Different subvolumes - * - * Both the implicit back refs and the full back refs for tree blocks - * only consist of key. The key offset for the implicit back refs is - * objectid of block's owner tree. The key offset for the full back refs - * is the first byte of parent block. - * - * When implicit back refs is used, information about the lowest key and - * level of the tree block are required. These information are stored in - * tree block info structure. - */ - -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 -static int convert_extent_item_v0(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 owner, u32 extra_size) -{ - struct btrfs_extent_item *item; - struct btrfs_extent_item_v0 *ei0; - struct btrfs_extent_ref_v0 *ref0; - struct btrfs_tree_block_info *bi; - struct extent_buffer *leaf; - struct btrfs_key key; - struct btrfs_key found_key; - u32 new_size = sizeof(*item); - u64 refs; - int ret; - - leaf = path->nodes[0]; - BUG_ON(btrfs_item_size_nr(leaf, path->slots[0]) != sizeof(*ei0)); - - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - ei0 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_item_v0); - refs = btrfs_extent_refs_v0(leaf, ei0); - - if (owner == (u64)-1) { - while (1) { - if (path->slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, path); - if (ret < 0) - return ret; - BUG_ON(ret > 0); - leaf = path->nodes[0]; - } - btrfs_item_key_to_cpu(leaf, &found_key, - path->slots[0]); - BUG_ON(key.objectid != found_key.objectid); - if (found_key.type != BTRFS_EXTENT_REF_V0_KEY) { - path->slots[0]++; - continue; - } - ref0 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_ref_v0); - owner = btrfs_ref_objectid_v0(leaf, ref0); - break; - } - } - btrfs_release_path(root, path); - - if (owner < BTRFS_FIRST_FREE_OBJECTID) - new_size += sizeof(*bi); - - new_size -= sizeof(*ei0); - ret = btrfs_search_slot(trans, root, &key, path, new_size, 1); - if (ret < 0) - return ret; - BUG_ON(ret); - - ret = btrfs_extend_item(trans, root, path, new_size); - BUG_ON(ret); - - leaf = path->nodes[0]; - item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); - btrfs_set_extent_refs(leaf, item, refs); - /* FIXME: get real generation */ - btrfs_set_extent_generation(leaf, item, 0); - if (owner < BTRFS_FIRST_FREE_OBJECTID) { - btrfs_set_extent_flags(leaf, item, - BTRFS_EXTENT_FLAG_TREE_BLOCK | - BTRFS_BLOCK_FLAG_FULL_BACKREF); - bi = (struct btrfs_tree_block_info *)(item + 1); - /* FIXME: get first key of the block */ - memset_extent_buffer(leaf, 0, (unsigned long)bi, sizeof(*bi)); - btrfs_set_tree_block_level(leaf, bi, (int)owner); - } else { - btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_DATA); - } - btrfs_mark_buffer_dirty(leaf); - return 0; -} -#endif - -static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset) -{ - u32 high_crc = ~(u32)0; - u32 low_crc = ~(u32)0; - __le64 lenum; - - lenum = cpu_to_le64(root_objectid); - high_crc = btrfs_crc32c(high_crc, &lenum, sizeof(lenum)); - lenum = cpu_to_le64(owner); - low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum)); - lenum = cpu_to_le64(offset); - low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum)); - - return ((u64)high_crc << 31) ^ (u64)low_crc; -} - -static u64 hash_extent_data_ref_item(struct extent_buffer *leaf, - struct btrfs_extent_data_ref *ref) -{ - return hash_extent_data_ref(btrfs_extent_data_ref_root(leaf, ref), - btrfs_extent_data_ref_objectid(leaf, ref), - btrfs_extent_data_ref_offset(leaf, ref)); -} - -static int match_extent_data_ref(struct extent_buffer *leaf, - struct btrfs_extent_data_ref *ref, - u64 root_objectid, u64 owner, u64 offset) -{ - if (btrfs_extent_data_ref_root(leaf, ref) != root_objectid || - btrfs_extent_data_ref_objectid(leaf, ref) != owner || - btrfs_extent_data_ref_offset(leaf, ref) != offset) - return 0; - return 1; -} - -static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 bytenr, u64 parent, - u64 root_objectid, - u64 owner, u64 offset) -{ - struct btrfs_key key; - struct btrfs_extent_data_ref *ref; - struct extent_buffer *leaf; - u32 nritems; - int ret; - int recow; - int err = -ENOENT; - - key.objectid = bytenr; - if (parent) { - key.type = BTRFS_SHARED_DATA_REF_KEY; - key.offset = parent; - } else { - key.type = BTRFS_EXTENT_DATA_REF_KEY; - key.offset = hash_extent_data_ref(root_objectid, - owner, offset); - } -again: - recow = 0; - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); - if (ret < 0) { - err = ret; - goto fail; - } - - if (parent) { - if (!ret) - return 0; -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - key.type = BTRFS_EXTENT_REF_V0_KEY; - btrfs_release_path(root, path); - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); - if (ret < 0) { - err = ret; - goto fail; - } - if (!ret) - return 0; -#endif - goto fail; - } - - leaf = path->nodes[0]; - nritems = btrfs_header_nritems(leaf); - while (1) { - if (path->slots[0] >= nritems) { - ret = btrfs_next_leaf(root, path); - if (ret < 0) - err = ret; - if (ret) - goto fail; - - leaf = path->nodes[0]; - nritems = btrfs_header_nritems(leaf); - recow = 1; - } - - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - if (key.objectid != bytenr || - key.type != BTRFS_EXTENT_DATA_REF_KEY) - goto fail; - - ref = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_data_ref); - - if (match_extent_data_ref(leaf, ref, root_objectid, - owner, offset)) { - if (recow) { - btrfs_release_path(root, path); - goto again; - } - err = 0; - break; - } - path->slots[0]++; - } -fail: - return err; -} - -static noinline int insert_extent_data_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 bytenr, u64 parent, - u64 root_objectid, u64 owner, - u64 offset, int refs_to_add) -{ - struct btrfs_key key; - struct extent_buffer *leaf; - u32 size; - u32 num_refs; - int ret; - - key.objectid = bytenr; - if (parent) { - key.type = BTRFS_SHARED_DATA_REF_KEY; - key.offset = parent; - size = sizeof(struct btrfs_shared_data_ref); - } else { - key.type = BTRFS_EXTENT_DATA_REF_KEY; - key.offset = hash_extent_data_ref(root_objectid, - owner, offset); - size = sizeof(struct btrfs_extent_data_ref); - } - - ret = btrfs_insert_empty_item(trans, root, path, &key, size); - if (ret && ret != -EEXIST) - goto fail; - - leaf = path->nodes[0]; - if (parent) { - struct btrfs_shared_data_ref *ref; - ref = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_shared_data_ref); - if (ret == 0) { - btrfs_set_shared_data_ref_count(leaf, ref, refs_to_add); - } else { - num_refs = btrfs_shared_data_ref_count(leaf, ref); - num_refs += refs_to_add; - btrfs_set_shared_data_ref_count(leaf, ref, num_refs); - } - } else { - struct btrfs_extent_data_ref *ref; - while (ret == -EEXIST) { - ref = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_data_ref); - if (match_extent_data_ref(leaf, ref, root_objectid, - owner, offset)) - break; - btrfs_release_path(root, path); - - key.offset++; - ret = btrfs_insert_empty_item(trans, root, path, &key, - size); - if (ret && ret != -EEXIST) - goto fail; - - leaf = path->nodes[0]; - } - ref = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_data_ref); - if (ret == 0) { - btrfs_set_extent_data_ref_root(leaf, ref, - root_objectid); - btrfs_set_extent_data_ref_objectid(leaf, ref, owner); - btrfs_set_extent_data_ref_offset(leaf, ref, offset); - btrfs_set_extent_data_ref_count(leaf, ref, refs_to_add); - } else { - num_refs = btrfs_extent_data_ref_count(leaf, ref); - num_refs += refs_to_add; - btrfs_set_extent_data_ref_count(leaf, ref, num_refs); - } - } - btrfs_mark_buffer_dirty(leaf); - ret = 0; -fail: - btrfs_release_path(root, path); - return ret; -} - -static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - int refs_to_drop) -{ - struct btrfs_key key; - struct btrfs_extent_data_ref *ref1 = NULL; - struct btrfs_shared_data_ref *ref2 = NULL; - struct extent_buffer *leaf; - u32 num_refs = 0; - int ret = 0; - - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - - if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { - ref1 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_data_ref); - num_refs = btrfs_extent_data_ref_count(leaf, ref1); - } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) { - ref2 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_shared_data_ref); - num_refs = btrfs_shared_data_ref_count(leaf, ref2); -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) { - struct btrfs_extent_ref_v0 *ref0; - ref0 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_ref_v0); - num_refs = btrfs_ref_count_v0(leaf, ref0); -#endif - } else { - BUG(); - } - - BUG_ON(num_refs < refs_to_drop); - num_refs -= refs_to_drop; - - if (num_refs == 0) { - ret = btrfs_del_item(trans, root, path); - } else { - if (key.type == BTRFS_EXTENT_DATA_REF_KEY) - btrfs_set_extent_data_ref_count(leaf, ref1, num_refs); - else if (key.type == BTRFS_SHARED_DATA_REF_KEY) - btrfs_set_shared_data_ref_count(leaf, ref2, num_refs); -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - else { - struct btrfs_extent_ref_v0 *ref0; - ref0 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_ref_v0); - btrfs_set_ref_count_v0(leaf, ref0, num_refs); - } -#endif - btrfs_mark_buffer_dirty(leaf); - } - return ret; -} - -static noinline u32 extent_data_ref_count(struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_extent_inline_ref *iref) -{ - struct btrfs_key key; - struct extent_buffer *leaf; - struct btrfs_extent_data_ref *ref1; - struct btrfs_shared_data_ref *ref2; - u32 num_refs = 0; - - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - if (iref) { - if (btrfs_extent_inline_ref_type(leaf, iref) == - BTRFS_EXTENT_DATA_REF_KEY) { - ref1 = (struct btrfs_extent_data_ref *)(&iref->offset); - num_refs = btrfs_extent_data_ref_count(leaf, ref1); - } else { - ref2 = (struct btrfs_shared_data_ref *)(iref + 1); - num_refs = btrfs_shared_data_ref_count(leaf, ref2); - } - } else if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { - ref1 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_data_ref); - num_refs = btrfs_extent_data_ref_count(leaf, ref1); - } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) { - ref2 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_shared_data_ref); - num_refs = btrfs_shared_data_ref_count(leaf, ref2); -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) { - struct btrfs_extent_ref_v0 *ref0; - ref0 = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_ref_v0); - num_refs = btrfs_ref_count_v0(leaf, ref0); -#endif - } else { - BUG(); - } - return num_refs; -} - -static noinline int lookup_tree_block_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 bytenr, u64 parent, - u64 root_objectid) -{ - struct btrfs_key key; - int ret; - - key.objectid = bytenr; - if (parent) { - key.type = BTRFS_SHARED_BLOCK_REF_KEY; - key.offset = parent; - } else { - key.type = BTRFS_TREE_BLOCK_REF_KEY; - key.offset = root_objectid; - } - - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); - if (ret > 0) - ret = -ENOENT; -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - if (ret == -ENOENT && parent) { - btrfs_release_path(root, path); - key.type = BTRFS_EXTENT_REF_V0_KEY; - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); - if (ret > 0) - ret = -ENOENT; - } -#endif - return ret; -} - -static noinline int insert_tree_block_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 bytenr, u64 parent, - u64 root_objectid) -{ - struct btrfs_key key; - int ret; - - key.objectid = bytenr; - if (parent) { - key.type = BTRFS_SHARED_BLOCK_REF_KEY; - key.offset = parent; - } else { - key.type = BTRFS_TREE_BLOCK_REF_KEY; - key.offset = root_objectid; - } - - ret = btrfs_insert_empty_item(trans, root, path, &key, 0); - - btrfs_release_path(root, path); - return ret; -} - -static inline int extent_ref_type(u64 parent, u64 owner) -{ - if (owner < BTRFS_FIRST_FREE_OBJECTID) { - if (parent > 0) - return BTRFS_SHARED_BLOCK_REF_KEY; - else - return BTRFS_TREE_BLOCK_REF_KEY; - } else { - if (parent > 0) - return BTRFS_SHARED_DATA_REF_KEY; - else - return BTRFS_EXTENT_DATA_REF_KEY; - } -} - -static int find_next_key(struct btrfs_path *path, struct btrfs_key *key) - -{ - int level; - for (level = 0; level < BTRFS_MAX_LEVEL; level++) { - if (!path->nodes[level]) - break; - if (path->slots[level] + 1 >= - btrfs_header_nritems(path->nodes[level])) - continue; - if (level == 0) - btrfs_item_key_to_cpu(path->nodes[level], key, - path->slots[level] + 1); - else - btrfs_node_key_to_cpu(path->nodes[level], key, - path->slots[level] + 1); - return 0; - } - return 1; -} - -static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_extent_inline_ref **ref_ret, - u64 bytenr, u64 num_bytes, - u64 parent, u64 root_objectid, - u64 owner, u64 offset, int insert) -{ - struct btrfs_key key; - struct extent_buffer *leaf; - struct btrfs_extent_item *ei; - struct btrfs_extent_inline_ref *iref; - u64 flags; - u32 item_size; - unsigned long ptr; - unsigned long end; - int extra_size; - int type; - int want; - int ret; - int err = 0; - - key.objectid = bytenr; - key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = num_bytes; - - want = extent_ref_type(parent, owner); - if (insert) - extra_size = btrfs_extent_inline_ref_size(want); - else - extra_size = -1; - ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1); - if (ret < 0) { - err = ret; - goto out; - } - BUG_ON(ret); - - leaf = path->nodes[0]; - item_size = btrfs_item_size_nr(leaf, path->slots[0]); -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - if (item_size < sizeof(*ei)) { - if (!insert) { - err = -ENOENT; - goto out; - } - ret = convert_extent_item_v0(trans, root, path, owner, - extra_size); - if (ret < 0) { - err = ret; - goto out; - } - leaf = path->nodes[0]; - item_size = btrfs_item_size_nr(leaf, path->slots[0]); - } -#endif - BUG_ON(item_size < sizeof(*ei)); - - ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); - flags = btrfs_extent_flags(leaf, ei); - - ptr = (unsigned long)(ei + 1); - end = (unsigned long)ei + item_size; - - if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { - ptr += sizeof(struct btrfs_tree_block_info); - BUG_ON(ptr > end); - } else { - BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA)); - } - - err = -ENOENT; - while (1) { - if (ptr >= end) { - WARN_ON(ptr > end); - break; - } - iref = (struct btrfs_extent_inline_ref *)ptr; - type = btrfs_extent_inline_ref_type(leaf, iref); - if (want < type) - break; - if (want > type) { - ptr += btrfs_extent_inline_ref_size(type); - continue; - } - - if (type == BTRFS_EXTENT_DATA_REF_KEY) { - struct btrfs_extent_data_ref *dref; - dref = (struct btrfs_extent_data_ref *)(&iref->offset); - if (match_extent_data_ref(leaf, dref, root_objectid, - owner, offset)) { - err = 0; - break; - } - if (hash_extent_data_ref_item(leaf, dref) < - hash_extent_data_ref(root_objectid, owner, offset)) - break; - } else { - u64 ref_offset; - ref_offset = btrfs_extent_inline_ref_offset(leaf, iref); - if (parent > 0) { - if (parent == ref_offset) { - err = 0; - break; - } - if (ref_offset < parent) - break; - } else { - if (root_objectid == ref_offset) { - err = 0; - break; - } - if (ref_offset < root_objectid) - break; - } - } - ptr += btrfs_extent_inline_ref_size(type); - } - if (err == -ENOENT && insert) { - if (item_size + extra_size >= - BTRFS_MAX_EXTENT_ITEM_SIZE(root)) { - err = -EAGAIN; - goto out; - } - /* - * To add new inline back ref, we have to make sure - * there is no corresponding back ref item. - * For simplicity, we just do not add new inline back - * ref if there is any back ref item. - */ - if (find_next_key(path, &key) == 0 && key.objectid == bytenr && - key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) { - err = -EAGAIN; - goto out; - } - } - *ref_ret = (struct btrfs_extent_inline_ref *)ptr; -out: - return err; -} - -static int setup_inline_extent_backref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_extent_inline_ref *iref, - u64 parent, u64 root_objectid, - u64 owner, u64 offset, int refs_to_add) -{ - struct extent_buffer *leaf; - struct btrfs_extent_item *ei; - unsigned long ptr; - unsigned long end; - unsigned long item_offset; - u64 refs; - int size; - int type; - int ret; - - leaf = path->nodes[0]; - ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); - item_offset = (unsigned long)iref - (unsigned long)ei; - - type = extent_ref_type(parent, owner); - size = btrfs_extent_inline_ref_size(type); - - ret = btrfs_extend_item(trans, root, path, size); - BUG_ON(ret); - - ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); - refs = btrfs_extent_refs(leaf, ei); - refs += refs_to_add; - btrfs_set_extent_refs(leaf, ei, refs); - - ptr = (unsigned long)ei + item_offset; - end = (unsigned long)ei + btrfs_item_size_nr(leaf, path->slots[0]); - if (ptr < end - size) - memmove_extent_buffer(leaf, ptr + size, ptr, - end - size - ptr); - - iref = (struct btrfs_extent_inline_ref *)ptr; - btrfs_set_extent_inline_ref_type(leaf, iref, type); - if (type == BTRFS_EXTENT_DATA_REF_KEY) { - struct btrfs_extent_data_ref *dref; - dref = (struct btrfs_extent_data_ref *)(&iref->offset); - btrfs_set_extent_data_ref_root(leaf, dref, root_objectid); - btrfs_set_extent_data_ref_objectid(leaf, dref, owner); - btrfs_set_extent_data_ref_offset(leaf, dref, offset); - btrfs_set_extent_data_ref_count(leaf, dref, refs_to_add); - } else if (type == BTRFS_SHARED_DATA_REF_KEY) { - struct btrfs_shared_data_ref *sref; - sref = (struct btrfs_shared_data_ref *)(iref + 1); - btrfs_set_shared_data_ref_count(leaf, sref, refs_to_add); - btrfs_set_extent_inline_ref_offset(leaf, iref, parent); - } else if (type == BTRFS_SHARED_BLOCK_REF_KEY) { - btrfs_set_extent_inline_ref_offset(leaf, iref, parent); - } else { - btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid); - } - btrfs_mark_buffer_dirty(leaf); - return 0; -} - -static int lookup_extent_backref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_extent_inline_ref **ref_ret, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, u64 offset) -{ - int ret; - - ret = lookup_inline_extent_backref(trans, root, path, ref_ret, - bytenr, num_bytes, parent, - root_objectid, owner, offset, 0); - if (ret != -ENOENT) - return ret; - - btrfs_release_path(root, path); - *ref_ret = NULL; - - if (owner < BTRFS_FIRST_FREE_OBJECTID) { - ret = lookup_tree_block_ref(trans, root, path, bytenr, parent, - root_objectid); - } else { - ret = lookup_extent_data_ref(trans, root, path, bytenr, parent, - root_objectid, owner, offset); - } - return ret; -} - -static int update_inline_extent_backref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_extent_inline_ref *iref, - int refs_to_mod) -{ - struct extent_buffer *leaf; - struct btrfs_extent_item *ei; - struct btrfs_extent_data_ref *dref = NULL; - struct btrfs_shared_data_ref *sref = NULL; - unsigned long ptr; - unsigned long end; - u32 item_size; - int size; - int type; - int ret; - u64 refs; - - leaf = path->nodes[0]; - ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); - refs = btrfs_extent_refs(leaf, ei); - WARN_ON(refs_to_mod < 0 && refs + refs_to_mod <= 0); - refs += refs_to_mod; - btrfs_set_extent_refs(leaf, ei, refs); - - type = btrfs_extent_inline_ref_type(leaf, iref); - - if (type == BTRFS_EXTENT_DATA_REF_KEY) { - dref = (struct btrfs_extent_data_ref *)(&iref->offset); - refs = btrfs_extent_data_ref_count(leaf, dref); - } else if (type == BTRFS_SHARED_DATA_REF_KEY) { - sref = (struct btrfs_shared_data_ref *)(iref + 1); - refs = btrfs_shared_data_ref_count(leaf, sref); - } else { - refs = 1; - BUG_ON(refs_to_mod != -1); - } - - BUG_ON(refs_to_mod < 0 && refs < -refs_to_mod); - refs += refs_to_mod; - - if (refs > 0) { - if (type == BTRFS_EXTENT_DATA_REF_KEY) - btrfs_set_extent_data_ref_count(leaf, dref, refs); - else - btrfs_set_shared_data_ref_count(leaf, sref, refs); - } else { - size = btrfs_extent_inline_ref_size(type); - item_size = btrfs_item_size_nr(leaf, path->slots[0]); - ptr = (unsigned long)iref; - end = (unsigned long)ei + item_size; - if (ptr + size < end) - memmove_extent_buffer(leaf, ptr, ptr + size, - end - ptr - size); - item_size -= size; - ret = btrfs_truncate_item(trans, root, path, item_size, 1); - BUG_ON(ret); - } - btrfs_mark_buffer_dirty(leaf); - return 0; -} - -static int insert_inline_extent_backref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, - u64 offset, int refs_to_add) -{ - struct btrfs_extent_inline_ref *iref; - int ret; - - ret = lookup_inline_extent_backref(trans, root, path, &iref, - bytenr, num_bytes, parent, - root_objectid, owner, offset, 1); - if (ret == 0) { - BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID); - ret = update_inline_extent_backref(trans, root, path, iref, - refs_to_add); - } else if (ret == -ENOENT) { - ret = setup_inline_extent_backref(trans, root, path, iref, - parent, root_objectid, - owner, offset, refs_to_add); - } - return ret; -} - -static int insert_extent_backref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 bytenr, u64 parent, u64 root_objectid, - u64 owner, u64 offset, int refs_to_add) -{ - int ret; - - if (owner >= BTRFS_FIRST_FREE_OBJECTID) { - ret = insert_extent_data_ref(trans, root, path, bytenr, - parent, root_objectid, - owner, offset, refs_to_add); - } else { - BUG_ON(refs_to_add != 1); - ret = insert_tree_block_ref(trans, root, path, bytenr, - parent, root_objectid); - } - return ret; -} - -static int remove_extent_backref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_extent_inline_ref *iref, - int refs_to_drop, int is_data) -{ - int ret; - - BUG_ON(!is_data && refs_to_drop != 1); - if (iref) { - ret = update_inline_extent_backref(trans, root, path, iref, - -refs_to_drop); - } else if (is_data) { - ret = remove_extent_data_ref(trans, root, path, refs_to_drop); - } else { - ret = btrfs_del_item(trans, root, path); - } - return ret; -} - -int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, u64 offset) -{ - struct btrfs_path *path; - struct extent_buffer *leaf; - struct btrfs_extent_item *item; - u64 refs; - int ret; - int err = 0; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - path->reada = 1; - path->leave_spinning = 1; - - ret = insert_inline_extent_backref(trans, root->fs_info->extent_root, - path, bytenr, num_bytes, parent, - root_objectid, owner, offset, 1); - if (ret == 0) - goto out; - - if (ret != -EAGAIN) { - err = ret; - goto out; - } - - leaf = path->nodes[0]; - item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); - refs = btrfs_extent_refs(leaf, item); - btrfs_set_extent_refs(leaf, item, refs + 1); - - btrfs_mark_buffer_dirty(leaf); - btrfs_release_path(root->fs_info->extent_root, path); - - path->reada = 1; - path->leave_spinning = 1; - - /* now insert the actual backref */ - ret = insert_extent_backref(trans, root->fs_info->extent_root, - path, bytenr, parent, root_objectid, - owner, offset, 1); - if (ret) - err = ret; -out: - btrfs_free_path(path); - finish_current_insert(trans, root->fs_info->extent_root); - del_pending_extents(trans, root->fs_info->extent_root); - BUG_ON(err); - return err; -} - -int btrfs_extent_post_op(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - finish_current_insert(trans, root->fs_info->extent_root); - del_pending_extents(trans, root->fs_info->extent_root); - return 0; -} - -int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytenr, - u64 num_bytes, u64 *refs, u64 *flags) -{ - struct btrfs_path *path; - int ret; - struct btrfs_key key; - struct extent_buffer *l; - struct btrfs_extent_item *item; - u32 item_size; - u64 num_refs; - u64 extent_flags; - - WARN_ON(num_bytes < root->sectorsize); - path = btrfs_alloc_path(); - path->reada = 1; - key.objectid = bytenr; - key.offset = num_bytes; - btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); - ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path, - 0, 0); - if (ret < 0) - goto out; - if (ret != 0) { - btrfs_print_leaf(root, path->nodes[0]); - printk("failed to find block number %Lu\n", bytenr); - BUG(); - } - - l = path->nodes[0]; - item_size = btrfs_item_size_nr(l, path->slots[0]); - if (item_size >= sizeof(*item)) { - item = btrfs_item_ptr(l, path->slots[0], - struct btrfs_extent_item); - num_refs = btrfs_extent_refs(l, item); - extent_flags = btrfs_extent_flags(l, item); - } else { -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - struct btrfs_extent_item_v0 *ei0; - BUG_ON(item_size != sizeof(*ei0)); - ei0 = btrfs_item_ptr(l, path->slots[0], - struct btrfs_extent_item_v0); - num_refs = btrfs_extent_refs_v0(l, ei0); - /* FIXME: this isn't correct for data */ - extent_flags = BTRFS_BLOCK_FLAG_FULL_BACKREF; -#else - BUG(); -#endif - } - BUG_ON(num_refs == 0); - item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item); - if (refs) - *refs = num_refs; - if (flags) - *flags = extent_flags; -out: - btrfs_free_path(path); - return 0; -} - -int btrfs_set_block_flags(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 flags) -{ - struct btrfs_path *path; - int ret; - struct btrfs_key key; - struct extent_buffer *l; - struct btrfs_extent_item *item; - u32 item_size; - - WARN_ON(num_bytes < root->sectorsize); - path = btrfs_alloc_path(); - path->reada = 1; - key.objectid = bytenr; - key.offset = num_bytes; - btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); - ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path, - 0, 0); - if (ret < 0) - goto out; - if (ret != 0) { - btrfs_print_leaf(root, path->nodes[0]); - printk("failed to find block number %Lu\n", - (unsigned long long)bytenr); - BUG(); - } - l = path->nodes[0]; - item_size = btrfs_item_size_nr(l, path->slots[0]); -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - if (item_size < sizeof(*item)) { - ret = convert_extent_item_v0(trans, root->fs_info->extent_root, - path, (u64)-1, 0); - if (ret < 0) - goto out; - - l = path->nodes[0]; - item_size = btrfs_item_size_nr(l, path->slots[0]); - } -#endif - BUG_ON(item_size < sizeof(*item)); - item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item); - flags |= btrfs_extent_flags(l, item); - btrfs_set_extent_flags(l, item, flags); -out: - btrfs_free_path(path); - finish_current_insert(trans, root->fs_info->extent_root); - del_pending_extents(trans, root->fs_info->extent_root); - return ret; -} - -static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *buf, - int record_parent, int inc) -{ - u64 bytenr; - u64 num_bytes; - u64 parent; - u64 ref_root; - u32 nritems; - struct btrfs_key key; - struct btrfs_file_extent_item *fi; - int i; - int level; - int ret = 0; - int faili = 0; - int (*process_func)(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64, u64, u64, u64, u64, u64); - - ref_root = btrfs_header_owner(buf); - nritems = btrfs_header_nritems(buf); - level = btrfs_header_level(buf); - - if (!root->ref_cows && level == 0) - return 0; - - if (inc) - process_func = btrfs_inc_extent_ref; - else - process_func = btrfs_free_extent; - - if (record_parent) - parent = buf->start; - else - parent = 0; - - for (i = 0; i < nritems; i++) { - cond_resched(); - if (level == 0) { - btrfs_item_key_to_cpu(buf, &key, i); - if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) - continue; - fi = btrfs_item_ptr(buf, i, - struct btrfs_file_extent_item); - if (btrfs_file_extent_type(buf, fi) == - BTRFS_FILE_EXTENT_INLINE) - continue; - bytenr = btrfs_file_extent_disk_bytenr(buf, fi); - if (bytenr == 0) - continue; - - num_bytes = btrfs_file_extent_disk_num_bytes(buf, fi); - key.offset -= btrfs_file_extent_offset(buf, fi); - ret = process_func(trans, root, bytenr, num_bytes, - parent, ref_root, key.objectid, - key.offset); - if (ret) { - faili = i; - WARN_ON(1); - goto fail; - } - } else { - bytenr = btrfs_node_blockptr(buf, i); - num_bytes = btrfs_level_size(root, level - 1); - ret = process_func(trans, root, bytenr, num_bytes, - parent, ref_root, level - 1, 0); - if (ret) { - faili = i; - WARN_ON(1); - goto fail; - } - } - } - return 0; -fail: - WARN_ON(1); -#if 0 - for (i =0; i < faili; i++) { - if (level == 0) { - u64 disk_bytenr; - btrfs_item_key_to_cpu(buf, &key, i); - if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) - continue; - fi = btrfs_item_ptr(buf, i, - struct btrfs_file_extent_item); - if (btrfs_file_extent_type(buf, fi) == - BTRFS_FILE_EXTENT_INLINE) - continue; - disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi); - if (disk_bytenr == 0) - continue; - err = btrfs_free_extent(trans, root, disk_bytenr, - btrfs_file_extent_disk_num_bytes(buf, - fi), 0); - BUG_ON(err); - } else { - bytenr = btrfs_node_blockptr(buf, i); - err = btrfs_free_extent(trans, root, bytenr, - btrfs_level_size(root, level - 1), 0); - BUG_ON(err); - } - } -#endif - return ret; -} - -int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int record_parent) -{ - return __btrfs_mod_ref(trans, root, buf, record_parent, 1); -} - -int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int record_parent) -{ - return __btrfs_mod_ref(trans, root, buf, record_parent, 0); -} - -static int write_one_cache_group(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_block_group_cache *cache) -{ - int ret; - int pending_ret; - struct btrfs_root *extent_root = root->fs_info->extent_root; - unsigned long bi; - struct extent_buffer *leaf; - - ret = btrfs_search_slot(trans, extent_root, &cache->key, path, 0, 1); - if (ret < 0) - goto fail; - BUG_ON(ret); - - leaf = path->nodes[0]; - bi = btrfs_item_ptr_offset(leaf, path->slots[0]); - write_extent_buffer(leaf, &cache->item, bi, sizeof(cache->item)); - btrfs_mark_buffer_dirty(leaf); - btrfs_release_path(extent_root, path); -fail: - finish_current_insert(trans, extent_root); - pending_ret = del_pending_extents(trans, extent_root); - if (ret) - return ret; - if (pending_ret) - return pending_ret; - return 0; - -} - -int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - struct extent_io_tree *block_group_cache; - struct btrfs_block_group_cache *cache; - int ret; - struct btrfs_path *path; - u64 last = 0; - u64 start; - u64 end; - u64 ptr; - - block_group_cache = &root->fs_info->block_group_cache; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - while(1) { - ret = find_first_extent_bit(block_group_cache, last, - &start, &end, BLOCK_GROUP_DIRTY); - if (ret) { - if (last == 0) - break; - last = 0; - continue; - } - - last = end + 1; - ret = get_state_private(block_group_cache, start, &ptr); - BUG_ON(ret); - - clear_extent_bits(block_group_cache, start, end, - BLOCK_GROUP_DIRTY, GFP_NOFS); - - cache = (struct btrfs_block_group_cache *)(unsigned long)ptr; - ret = write_one_cache_group(trans, root, path, cache); - BUG_ON(ret); - } - btrfs_free_path(path); - return 0; -} - -static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info, - u64 flags) -{ - struct list_head *head = &info->space_info; - struct list_head *cur; - struct btrfs_space_info *found; - list_for_each(cur, head) { - found = list_entry(cur, struct btrfs_space_info, list); - if (found->flags == flags) - return found; - } - return NULL; - -} - -static int update_space_info(struct btrfs_fs_info *info, u64 flags, - u64 total_bytes, u64 bytes_used, - struct btrfs_space_info **space_info) -{ - struct btrfs_space_info *found; - - found = __find_space_info(info, flags); - if (found) { - found->total_bytes += total_bytes; - found->bytes_used += bytes_used; - WARN_ON(found->total_bytes < found->bytes_used); - *space_info = found; - return 0; - } - found = kmalloc(sizeof(*found), GFP_NOFS); - if (!found) - return -ENOMEM; - - list_add(&found->list, &info->space_info); - found->flags = flags; - found->total_bytes = total_bytes; - found->bytes_used = bytes_used; - found->bytes_pinned = 0; - found->full = 0; - *space_info = found; - return 0; -} - - -static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) -{ - u64 extra_flags = flags & (BTRFS_BLOCK_GROUP_RAID0 | - BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_DUP); - if (extra_flags) { - if (flags & BTRFS_BLOCK_GROUP_DATA) - fs_info->avail_data_alloc_bits |= extra_flags; - if (flags & BTRFS_BLOCK_GROUP_METADATA) - fs_info->avail_metadata_alloc_bits |= extra_flags; - if (flags & BTRFS_BLOCK_GROUP_SYSTEM) - fs_info->avail_system_alloc_bits |= extra_flags; - } -} - -static int do_chunk_alloc(struct btrfs_trans_handle *trans, - struct btrfs_root *extent_root, u64 alloc_bytes, - u64 flags) -{ - struct btrfs_space_info *space_info; - u64 thresh; - u64 start; - u64 num_bytes; - int ret; - - space_info = __find_space_info(extent_root->fs_info, flags); - if (!space_info) { - ret = update_space_info(extent_root->fs_info, flags, - 0, 0, &space_info); - BUG_ON(ret); - } - BUG_ON(!space_info); - - if (space_info->full) - return 0; - - thresh = div_factor(space_info->total_bytes, 7); - if ((space_info->bytes_used + space_info->bytes_pinned + alloc_bytes) < - thresh) - return 0; - - ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes, flags); - if (ret == -ENOSPC) { - space_info->full = 1; - return 0; - } - - BUG_ON(ret); - - ret = btrfs_make_block_group(trans, extent_root, 0, flags, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, start, num_bytes); - BUG_ON(ret); - return 0; -} - -static int update_block_group(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, int alloc, - int mark_free) -{ - struct btrfs_block_group_cache *cache; - struct btrfs_fs_info *info = root->fs_info; - u64 total = num_bytes; - u64 old_val; - u64 byte_in_group; - u64 start; - u64 end; - - /* block accounting for super block */ - old_val = btrfs_super_bytes_used(&info->super_copy); - if (alloc) - old_val += num_bytes; - else - old_val -= num_bytes; - btrfs_set_super_bytes_used(&info->super_copy, old_val); - - /* block accounting for root item */ - old_val = btrfs_root_used(&root->root_item); - if (alloc) - old_val += num_bytes; - else - old_val -= num_bytes; - btrfs_set_root_used(&root->root_item, old_val); - - while(total) { - cache = btrfs_lookup_block_group(info, bytenr); - if (!cache) { - return -1; - } - byte_in_group = bytenr - cache->key.objectid; - WARN_ON(byte_in_group > cache->key.offset); - start = cache->key.objectid; - end = start + cache->key.offset - 1; - set_extent_bits(&info->block_group_cache, start, end, - BLOCK_GROUP_DIRTY, GFP_NOFS); - - old_val = btrfs_block_group_used(&cache->item); - num_bytes = min(total, cache->key.offset - byte_in_group); - if (alloc) { - old_val += num_bytes; - cache->space_info->bytes_used += num_bytes; - } else { - old_val -= num_bytes; - cache->space_info->bytes_used -= num_bytes; - if (mark_free) { - set_extent_dirty(&info->free_space_cache, - bytenr, bytenr + num_bytes - 1, - GFP_NOFS); - } - } - btrfs_set_block_group_used(&cache->item, old_val); - total -= num_bytes; - bytenr += num_bytes; - } - return 0; -} - -static int update_pinned_extents(struct btrfs_root *root, - u64 bytenr, u64 num, int pin) -{ - u64 len; - struct btrfs_block_group_cache *cache; - struct btrfs_fs_info *fs_info = root->fs_info; - - if (pin) { - set_extent_dirty(&fs_info->pinned_extents, - bytenr, bytenr + num - 1, GFP_NOFS); - } else { - clear_extent_dirty(&fs_info->pinned_extents, - bytenr, bytenr + num - 1, GFP_NOFS); - } - while (num > 0) { - cache = btrfs_lookup_block_group(fs_info, bytenr); - WARN_ON(!cache); - len = min(num, cache->key.offset - - (bytenr - cache->key.objectid)); - if (pin) { - cache->pinned += len; - cache->space_info->bytes_pinned += len; - fs_info->total_pinned += len; - } else { - cache->pinned -= len; - cache->space_info->bytes_pinned -= len; - fs_info->total_pinned -= len; - } - bytenr += len; - num -= len; - } - return 0; -} - -int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy) -{ - u64 last = 0; - u64 start; - u64 end; - struct extent_io_tree *pinned_extents = &root->fs_info->pinned_extents; - int ret; - - while(1) { - ret = find_first_extent_bit(pinned_extents, last, - &start, &end, EXTENT_DIRTY); - if (ret) - break; - set_extent_dirty(copy, start, end, GFP_NOFS); - last = end + 1; - } - return 0; -} - -int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_io_tree *unpin) -{ - u64 start; - u64 end; - int ret; - struct extent_io_tree *free_space_cache; - free_space_cache = &root->fs_info->free_space_cache; - - while(1) { - ret = find_first_extent_bit(unpin, 0, &start, &end, - EXTENT_DIRTY); - if (ret) - break; - update_pinned_extents(root, start, end + 1 - start, 0); - clear_extent_dirty(unpin, start, end, GFP_NOFS); - set_extent_dirty(free_space_cache, start, end, GFP_NOFS); - } - return 0; -} - -static int finish_current_insert(struct btrfs_trans_handle *trans, - struct btrfs_root *extent_root) -{ - u64 start; - u64 end; - u64 priv; - struct btrfs_fs_info *info = extent_root->fs_info; - struct btrfs_path *path; - struct pending_extent_op *extent_op; - struct btrfs_key key; - int ret; - - path = btrfs_alloc_path(); - - while(1) { - ret = find_first_extent_bit(&info->extent_ins, 0, &start, - &end, EXTENT_LOCKED); - if (ret) - break; - - ret = get_state_private(&info->extent_ins, start, &priv); - BUG_ON(ret); - extent_op = (struct pending_extent_op *)(unsigned long)priv; - - if (extent_op->type == PENDING_EXTENT_INSERT) { - key.objectid = start; - key.offset = end + 1 - start; - key.type = BTRFS_EXTENT_ITEM_KEY; - ret = alloc_reserved_tree_block(trans, extent_root, - extent_root->root_key.objectid, - trans->transid, - extent_op->flags, - &extent_op->key, - extent_op->level, &key); - } else { - BUG_ON(1); - } - - clear_extent_bits(&info->extent_ins, start, end, EXTENT_LOCKED, - GFP_NOFS); - kfree(extent_op); - } - btrfs_free_path(path); - return 0; -} - -static int pin_down_bytes(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, int is_data) -{ - int err = 0; - struct extent_buffer *buf; - - if (is_data) - goto pinit; - - buf = btrfs_find_tree_block(root, bytenr, num_bytes); - if (!buf) - goto pinit; - - /* we can reuse a block if it hasn't been written - * and it is from this transaction. We can't - * reuse anything from the tree log root because - * it has tiny sub-transactions. - */ - if (btrfs_buffer_uptodate(buf, 0)) { - u64 header_owner = btrfs_header_owner(buf); - u64 header_transid = btrfs_header_generation(buf); - if (header_owner != BTRFS_TREE_LOG_OBJECTID && - header_transid == trans->transid && - !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) { - clean_tree_block(NULL, root, buf); - free_extent_buffer(buf); - return 1; - } - } - free_extent_buffer(buf); -pinit: - update_pinned_extents(root, bytenr, num_bytes, 1); - - BUG_ON(err < 0); - return 0; -} - -/* - * remove an extent from the root, returns 0 on success - */ -static int __free_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner_objectid, - u64 owner_offset, int refs_to_drop) -{ - - struct btrfs_key key; - struct btrfs_path *path; - struct btrfs_extent_ops *ops = root->fs_info->extent_ops; - struct btrfs_root *extent_root = root->fs_info->extent_root; - struct extent_buffer *leaf; - struct btrfs_extent_item *ei; - struct btrfs_extent_inline_ref *iref; - int ret; - int is_data; - int extent_slot = 0; - int found_extent = 0; - int num_to_del = 1; - u32 item_size; - u64 refs; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - path->reada = 1; - path->leave_spinning = 1; - - is_data = owner_objectid >= BTRFS_FIRST_FREE_OBJECTID; - BUG_ON(!is_data && refs_to_drop != 1); - - ret = lookup_extent_backref(trans, extent_root, path, &iref, - bytenr, num_bytes, parent, - root_objectid, owner_objectid, - owner_offset); - if (ret == 0) { - extent_slot = path->slots[0]; - while (extent_slot >= 0) { - btrfs_item_key_to_cpu(path->nodes[0], &key, - extent_slot); - if (key.objectid != bytenr) - break; - if (key.type == BTRFS_EXTENT_ITEM_KEY && - key.offset == num_bytes) { - found_extent = 1; - break; - } - if (path->slots[0] - extent_slot > 5) - break; - extent_slot--; - } -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - item_size = btrfs_item_size_nr(path->nodes[0], extent_slot); - if (found_extent && item_size < sizeof(*ei)) - found_extent = 0; -#endif - if (!found_extent) { - BUG_ON(iref); - ret = remove_extent_backref(trans, extent_root, path, - NULL, refs_to_drop, - is_data); - BUG_ON(ret); - btrfs_release_path(extent_root, path); - path->leave_spinning = 1; - - key.objectid = bytenr; - key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = num_bytes; - - ret = btrfs_search_slot(trans, extent_root, - &key, path, -1, 1); - if (ret) { - printk(KERN_ERR "umm, got %d back from search" - ", was looking for %llu\n", ret, - (unsigned long long)bytenr); - btrfs_print_leaf(extent_root, path->nodes[0]); - } - BUG_ON(ret); - extent_slot = path->slots[0]; - } - } else { - btrfs_print_leaf(extent_root, path->nodes[0]); - WARN_ON(1); - printk(KERN_ERR "btrfs unable to find ref byte nr %llu " - "parent %llu root %llu owner %llu offset %llu\n", - (unsigned long long)bytenr, - (unsigned long long)parent, - (unsigned long long)root_objectid, - (unsigned long long)owner_objectid, - (unsigned long long)owner_offset); - } - - leaf = path->nodes[0]; - item_size = btrfs_item_size_nr(leaf, extent_slot); -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - if (item_size < sizeof(*ei)) { - BUG_ON(found_extent || extent_slot != path->slots[0]); - ret = convert_extent_item_v0(trans, extent_root, path, - owner_objectid, 0); - BUG_ON(ret < 0); - - btrfs_release_path(extent_root, path); - path->leave_spinning = 1; - - key.objectid = bytenr; - key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = num_bytes; - - ret = btrfs_search_slot(trans, extent_root, &key, path, - -1, 1); - if (ret) { - printk(KERN_ERR "umm, got %d back from search" - ", was looking for %llu\n", ret, - (unsigned long long)bytenr); - btrfs_print_leaf(extent_root, path->nodes[0]); - } - BUG_ON(ret); - extent_slot = path->slots[0]; - leaf = path->nodes[0]; - item_size = btrfs_item_size_nr(leaf, extent_slot); - } -#endif - BUG_ON(item_size < sizeof(*ei)); - ei = btrfs_item_ptr(leaf, extent_slot, - struct btrfs_extent_item); - if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID) { - struct btrfs_tree_block_info *bi; - BUG_ON(item_size < sizeof(*ei) + sizeof(*bi)); - bi = (struct btrfs_tree_block_info *)(ei + 1); - WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi)); - } - - refs = btrfs_extent_refs(leaf, ei); - BUG_ON(refs < refs_to_drop); - refs -= refs_to_drop; - - if (refs > 0) { - /* - * In the case of inline back ref, reference count will - * be updated by remove_extent_backref - */ - if (iref) { - BUG_ON(!found_extent); - } else { - btrfs_set_extent_refs(leaf, ei, refs); - btrfs_mark_buffer_dirty(leaf); - } - if (found_extent) { - ret = remove_extent_backref(trans, extent_root, path, - iref, refs_to_drop, - is_data); - BUG_ON(ret); - } - } else { - int mark_free = 0; - int pin = 1; - - if (found_extent) { - BUG_ON(is_data && refs_to_drop != - extent_data_ref_count(root, path, iref)); - if (iref) { - BUG_ON(path->slots[0] != extent_slot); - } else { - BUG_ON(path->slots[0] != extent_slot + 1); - path->slots[0] = extent_slot; - num_to_del = 2; - } - } - - if (ops && ops->free_extent) { - ret = ops->free_extent(root, bytenr, num_bytes); - if (ret > 0) { - pin = 0; - mark_free = 0; - } - } - - if (pin) { - ret = pin_down_bytes(trans, root, bytenr, num_bytes, - is_data); - if (ret > 0) - mark_free = 1; - BUG_ON(ret < 0); - } - - ret = btrfs_del_items(trans, extent_root, path, path->slots[0], - num_to_del); - BUG_ON(ret); - btrfs_release_path(extent_root, path); - - if (is_data) { - ret = btrfs_del_csums(trans, root, bytenr, num_bytes); - BUG_ON(ret); - } - - ret = update_block_group(trans, root, bytenr, num_bytes, 0, - mark_free); - BUG_ON(ret); - } - btrfs_free_path(path); - finish_current_insert(trans, extent_root); - return ret; -} - -/* - * find all the blocks marked as pending in the radix tree and remove - * them from the extent map - */ -static int del_pending_extents(struct btrfs_trans_handle *trans, struct - btrfs_root *extent_root) -{ - int ret; - int err = 0; - u64 start; - u64 end; - u64 priv; - struct extent_io_tree *pending_del; - struct extent_io_tree *extent_ins; - struct pending_extent_op *extent_op; - - extent_ins = &extent_root->fs_info->extent_ins; - pending_del = &extent_root->fs_info->pending_del; - - while(1) { - ret = find_first_extent_bit(pending_del, 0, &start, &end, - EXTENT_LOCKED); - if (ret) - break; - - ret = get_state_private(pending_del, start, &priv); - BUG_ON(ret); - extent_op = (struct pending_extent_op *)(unsigned long)priv; - - clear_extent_bits(pending_del, start, end, EXTENT_LOCKED, - GFP_NOFS); - - if (!test_range_bit(extent_ins, start, end, - EXTENT_LOCKED, 0)) { - ret = __free_extent(trans, extent_root, - start, end + 1 - start, 0, - extent_root->root_key.objectid, - extent_op->level, 0, 1); - kfree(extent_op); - } else { - kfree(extent_op); - ret = get_state_private(extent_ins, start, &priv); - BUG_ON(ret); - extent_op = (struct pending_extent_op *) - (unsigned long)priv; - - clear_extent_bits(extent_ins, start, end, - EXTENT_LOCKED, GFP_NOFS); - - if (extent_op->type == PENDING_BACKREF_UPDATE) - BUG_ON(1); - - kfree(extent_op); - } - if (ret) - err = ret; - } - return err; -} - -/* - * remove an extent from the root, returns 0 on success - */ - -int btrfs_free_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, u64 offset) -{ - struct btrfs_root *extent_root = root->fs_info->extent_root; - int pending_ret; - int ret; - - WARN_ON(num_bytes < root->sectorsize); - if (root == extent_root) { - struct pending_extent_op *extent_op; - - extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS); - BUG_ON(!extent_op); - - extent_op->type = PENDING_EXTENT_DELETE; - extent_op->bytenr = bytenr; - extent_op->num_bytes = num_bytes; - extent_op->level = (int)owner; - - set_extent_bits(&root->fs_info->pending_del, - bytenr, bytenr + num_bytes - 1, - EXTENT_LOCKED, GFP_NOFS); - set_state_private(&root->fs_info->pending_del, - bytenr, (unsigned long)extent_op); - return 0; - } - ret = __free_extent(trans, root, bytenr, num_bytes, parent, - root_objectid, owner, offset, 1); - pending_ret = del_pending_extents(trans, root->fs_info->extent_root); - return ret ? ret : pending_ret; -} - -static u64 stripe_align(struct btrfs_root *root, u64 val) -{ - u64 mask = ((u64)root->stripesize - 1); - u64 ret = (val + mask) & ~mask; - return ret; -} - -/* - * walks the btree of allocated extents and find a hole of a given size. - * The key ins is changed to record the hole: - * ins->objectid == block start - * ins->flags = BTRFS_EXTENT_ITEM_KEY - * ins->offset == number of blocks - * Any available blocks before search_start are skipped. - */ -static int noinline find_free_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *orig_root, - u64 num_bytes, u64 empty_size, - u64 search_start, u64 search_end, - u64 hint_byte, struct btrfs_key *ins, - u64 exclude_start, u64 exclude_nr, - int data) -{ - int ret; - u64 orig_search_start = search_start; - struct btrfs_root * root = orig_root->fs_info->extent_root; - struct btrfs_fs_info *info = root->fs_info; - u64 total_needed = num_bytes; - struct btrfs_block_group_cache *block_group; - int full_scan = 0; - int wrapped = 0; - - WARN_ON(num_bytes < root->sectorsize); - btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY); - - if (hint_byte) { - block_group = btrfs_lookup_first_block_group(info, hint_byte); - if (!block_group) - hint_byte = search_start; - block_group = btrfs_find_block_group(root, block_group, - hint_byte, data, 1); - } else { - block_group = btrfs_find_block_group(root, - trans->block_group, - search_start, data, 1); - } - - total_needed += empty_size; - -check_failed: - if (!block_group) { - block_group = btrfs_lookup_first_block_group(info, - search_start); - if (!block_group) - block_group = btrfs_lookup_first_block_group(info, - orig_search_start); - } - ret = find_search_start(root, &block_group, &search_start, - total_needed, data); - if (ret) - goto error; - - search_start = stripe_align(root, search_start); - ins->objectid = search_start; - ins->offset = num_bytes; - - if (ins->objectid + num_bytes > - block_group->key.objectid + block_group->key.offset) { - search_start = block_group->key.objectid + - block_group->key.offset; - goto new_group; - } - - if (test_range_bit(&info->extent_ins, ins->objectid, - ins->objectid + num_bytes -1, EXTENT_LOCKED, 0)) { - search_start = ins->objectid + num_bytes; - goto new_group; - } - - if (test_range_bit(&info->pinned_extents, ins->objectid, - ins->objectid + num_bytes -1, EXTENT_DIRTY, 0)) { - search_start = ins->objectid + num_bytes; - goto new_group; - } - - if (exclude_nr > 0 && (ins->objectid + num_bytes > exclude_start && - ins->objectid < exclude_start + exclude_nr)) { - search_start = exclude_start + exclude_nr; - goto new_group; - } - - if (!(data & BTRFS_BLOCK_GROUP_DATA)) { - block_group = btrfs_lookup_block_group(info, ins->objectid); - if (block_group) - trans->block_group = block_group; - } - ins->offset = num_bytes; - return 0; - -new_group: - block_group = btrfs_lookup_first_block_group(info, search_start); - if (!block_group) { - search_start = orig_search_start; - if (full_scan) { - ret = -ENOSPC; - goto error; - } - if (wrapped) { - if (!full_scan) - total_needed -= empty_size; - full_scan = 1; - } else - wrapped = 1; - } - cond_resched(); - block_group = btrfs_find_block_group(root, block_group, - search_start, data, 0); - goto check_failed; - -error: - return ret; -} - -static int btrfs_reserve_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 num_bytes, u64 empty_size, - u64 hint_byte, u64 search_end, - struct btrfs_key *ins, int data) -{ - int ret; - u64 search_start = 0; - u64 alloc_profile; - struct btrfs_fs_info *info = root->fs_info; - - if (info->extent_ops) { - struct btrfs_extent_ops *ops = info->extent_ops; - ret = ops->alloc_extent(root, num_bytes, hint_byte, ins); - BUG_ON(ret); - goto found; - } - - if (data) { - alloc_profile = info->avail_data_alloc_bits & - info->data_alloc_profile; - data = BTRFS_BLOCK_GROUP_DATA | alloc_profile; - } else if ((info->system_allocs > 0 || root == info->chunk_root) && - info->system_allocs >= 0) { - alloc_profile = info->avail_system_alloc_bits & - info->system_alloc_profile; - data = BTRFS_BLOCK_GROUP_SYSTEM | alloc_profile; - } else { - alloc_profile = info->avail_metadata_alloc_bits & - info->metadata_alloc_profile; - data = BTRFS_BLOCK_GROUP_METADATA | alloc_profile; - } - - if (root->ref_cows) { - if (!(data & BTRFS_BLOCK_GROUP_METADATA)) { - ret = do_chunk_alloc(trans, root->fs_info->extent_root, - num_bytes, - BTRFS_BLOCK_GROUP_METADATA); - BUG_ON(ret); - } - ret = do_chunk_alloc(trans, root->fs_info->extent_root, - num_bytes + 2 * 1024 * 1024, data); - BUG_ON(ret); - } - - WARN_ON(num_bytes < root->sectorsize); - ret = find_free_extent(trans, root, num_bytes, empty_size, - search_start, search_end, hint_byte, ins, - trans->alloc_exclude_start, - trans->alloc_exclude_nr, data); - BUG_ON(ret); -found: - clear_extent_dirty(&root->fs_info->free_space_cache, - ins->objectid, ins->objectid + ins->offset - 1, - GFP_NOFS); - return ret; -} - -static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 root_objectid, u64 generation, - u64 flags, struct btrfs_disk_key *key, - int level, struct btrfs_key *ins) -{ - int ret; - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_extent_item *extent_item; - struct btrfs_tree_block_info *block_info; - struct btrfs_extent_inline_ref *iref; - struct btrfs_path *path; - struct extent_buffer *leaf; - u32 size = sizeof(*extent_item) + sizeof(*block_info) + sizeof(*iref); - - path = btrfs_alloc_path(); - BUG_ON(!path); - - path->leave_spinning = 1; - ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path, - ins, size); - BUG_ON(ret); - - leaf = path->nodes[0]; - extent_item = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_item); - btrfs_set_extent_refs(leaf, extent_item, 1); - btrfs_set_extent_generation(leaf, extent_item, generation); - btrfs_set_extent_flags(leaf, extent_item, - flags | BTRFS_EXTENT_FLAG_TREE_BLOCK); - block_info = (struct btrfs_tree_block_info *)(extent_item + 1); - - btrfs_set_tree_block_key(leaf, block_info, key); - btrfs_set_tree_block_level(leaf, block_info, level); - - iref = (struct btrfs_extent_inline_ref *)(block_info + 1); - btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY); - btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid); - - btrfs_mark_buffer_dirty(leaf); - btrfs_free_path(path); - - ret = update_block_group(trans, root, ins->objectid, ins->offset, - 1, 0); - if (ret) { - printk(KERN_ERR "btrfs update block group failed for %llu " - "%llu\n", (unsigned long long)ins->objectid, - (unsigned long long)ins->offset); - BUG(); - } - return ret; -} - -static int alloc_tree_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 num_bytes, - u64 root_objectid, u64 generation, - u64 flags, struct btrfs_disk_key *key, - int level, u64 empty_size, u64 hint_byte, - u64 search_end, struct btrfs_key *ins) -{ - int ret; - ret = btrfs_reserve_extent(trans, root, num_bytes, empty_size, - hint_byte, search_end, ins, 0); - BUG_ON(ret); - - if (root_objectid == BTRFS_EXTENT_TREE_OBJECTID) { - struct pending_extent_op *extent_op; - - extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS); - BUG_ON(!extent_op); - - extent_op->type = PENDING_EXTENT_INSERT; - extent_op->bytenr = ins->objectid; - extent_op->num_bytes = ins->offset; - extent_op->level = level; - extent_op->flags = flags; - memcpy(&extent_op->key, key, sizeof(*key)); - - set_extent_bits(&root->fs_info->extent_ins, ins->objectid, - ins->objectid + ins->offset - 1, - EXTENT_LOCKED, GFP_NOFS); - set_state_private(&root->fs_info->extent_ins, - ins->objectid, (unsigned long)extent_op); - } else { - ret = alloc_reserved_tree_block(trans, root, root_objectid, - generation, flags, - key, level, ins); - finish_current_insert(trans, root->fs_info->extent_root); - del_pending_extents(trans, root->fs_info->extent_root); - } - return ret; -} - -/* - * helper function to allocate a block for a given tree - * returns the tree buffer or NULL. - */ -struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u32 blocksize, u64 root_objectid, - struct btrfs_disk_key *key, int level, - u64 hint, u64 empty_size) -{ - struct btrfs_key ins; - int ret; - struct extent_buffer *buf; - - ret = alloc_tree_block(trans, root, blocksize, root_objectid, - trans->transid, 0, key, level, - empty_size, hint, (u64)-1, &ins); - if (ret) { - BUG_ON(ret > 0); - return ERR_PTR(ret); - } - - buf = btrfs_find_create_tree_block(root, ins.objectid, blocksize); - if (!buf) { - btrfs_free_extent(trans, root, ins.objectid, ins.offset, - 0, root->root_key.objectid, level, 0); - BUG_ON(1); - return ERR_PTR(-ENOMEM); - } - btrfs_set_buffer_uptodate(buf); - trans->blocks_used++; - - return buf; -} - -#if 0 - -static int noinline drop_leaf_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct extent_buffer *leaf) -{ - u64 leaf_owner; - u64 leaf_generation; - struct btrfs_key key; - struct btrfs_file_extent_item *fi; - int i; - int nritems; - int ret; - - BUG_ON(!btrfs_is_leaf(leaf)); - nritems = btrfs_header_nritems(leaf); - leaf_owner = btrfs_header_owner(leaf); - leaf_generation = btrfs_header_generation(leaf); - - for (i = 0; i < nritems; i++) { - u64 disk_bytenr; - - btrfs_item_key_to_cpu(leaf, &key, i); - if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) - continue; - fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item); - if (btrfs_file_extent_type(leaf, fi) == - BTRFS_FILE_EXTENT_INLINE) - continue; - /* - * FIXME make sure to insert a trans record that - * repeats the snapshot del on crash - */ - disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); - if (disk_bytenr == 0) - continue; - ret = btrfs_free_extent(trans, root, disk_bytenr, - btrfs_file_extent_disk_num_bytes(leaf, fi), - leaf->start, leaf_owner, leaf_generation, - key.objectid, 0); - BUG_ON(ret); - } - return 0; -} - -static void noinline reada_walk_down(struct btrfs_root *root, - struct extent_buffer *node, - int slot) -{ - u64 bytenr; - u64 last = 0; - u32 nritems; - u32 refs; - u32 blocksize; - int ret; - int i; - int level; - int skipped = 0; - - nritems = btrfs_header_nritems(node); - level = btrfs_header_level(node); - if (level) - return; - - for (i = slot; i < nritems && skipped < 32; i++) { - bytenr = btrfs_node_blockptr(node, i); - if (last && ((bytenr > last && bytenr - last > 32 * 1024) || - (last > bytenr && last - bytenr > 32 * 1024))) { - skipped++; - continue; - } - blocksize = btrfs_level_size(root, level - 1); - if (i != slot) { - ret = btrfs_lookup_extent_ref(NULL, root, bytenr, - blocksize, &refs); - BUG_ON(ret); - if (refs != 1) { - skipped++; - continue; - } - } - mutex_unlock(&root->fs_info->fs_mutex); - ret = readahead_tree_block(root, bytenr, blocksize, - btrfs_node_ptr_generation(node, i)); - last = bytenr + blocksize; - cond_resched(); - mutex_lock(&root->fs_info->fs_mutex); - if (ret) - break; - } -} - -/* - * helper function for drop_snapshot, this walks down the tree dropping ref - * counts as it goes. - */ -static int noinline walk_down_tree(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, int *level) -{ - u64 root_owner; - u64 root_gen; - u64 bytenr; - u64 ptr_gen; - struct extent_buffer *next; - struct extent_buffer *cur; - struct extent_buffer *parent; - u32 blocksize; - int ret; - u32 refs; - - WARN_ON(*level < 0); - WARN_ON(*level >= BTRFS_MAX_LEVEL); - ret = btrfs_lookup_extent_ref(trans, root, - path->nodes[*level]->start, - path->nodes[*level]->len, &refs); - BUG_ON(ret); - if (refs > 1) - goto out; - - /* - * walk down to the last node level and free all the leaves - */ - while(*level >= 0) { - WARN_ON(*level < 0); - WARN_ON(*level >= BTRFS_MAX_LEVEL); - cur = path->nodes[*level]; - - if (btrfs_header_level(cur) != *level) - WARN_ON(1); - - if (path->slots[*level] >= - btrfs_header_nritems(cur)) - break; - if (*level == 0) { - ret = drop_leaf_ref(trans, root, cur); - BUG_ON(ret); - break; - } - bytenr = btrfs_node_blockptr(cur, path->slots[*level]); - ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]); - blocksize = btrfs_level_size(root, *level - 1); - ret = btrfs_lookup_extent_ref(trans, root, bytenr, blocksize, - &refs); - BUG_ON(ret); - if (refs != 1) { - parent = path->nodes[*level]; - root_owner = btrfs_header_owner(parent); - root_gen = btrfs_header_generation(parent); - path->slots[*level]++; - ret = btrfs_free_extent(trans, root, bytenr, blocksize, - parent->start, root_owner, - root_gen, *level - 1, 1); - BUG_ON(ret); - continue; - } - next = btrfs_find_tree_block(root, bytenr, blocksize); - if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) { - free_extent_buffer(next); - reada_walk_down(root, cur, path->slots[*level]); - mutex_unlock(&root->fs_info->fs_mutex); - next = read_tree_block(root, bytenr, blocksize, - ptr_gen); - mutex_lock(&root->fs_info->fs_mutex); - } - WARN_ON(*level <= 0); - if (path->nodes[*level-1]) - free_extent_buffer(path->nodes[*level-1]); - path->nodes[*level-1] = next; - *level = btrfs_header_level(next); - path->slots[*level] = 0; - } -out: - WARN_ON(*level < 0); - WARN_ON(*level >= BTRFS_MAX_LEVEL); - - if (path->nodes[*level] == root->node) { - root_owner = root->root_key.objectid; - parent = path->nodes[*level]; - } else { - parent = path->nodes[*level + 1]; - root_owner = btrfs_header_owner(parent); - } - - root_gen = btrfs_header_generation(parent); - ret = btrfs_free_extent(trans, root, path->nodes[*level]->start, - path->nodes[*level]->len, parent->start, - root_owner, root_gen, *level, 1); - free_extent_buffer(path->nodes[*level]); - path->nodes[*level] = NULL; - *level += 1; - BUG_ON(ret); - return 0; -} - -/* - * helper for dropping snapshots. This walks back up the tree in the path - * to find the first node higher up where we haven't yet gone through - * all the slots - */ -static int noinline walk_up_tree(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, int *level) -{ - u64 root_owner; - u64 root_gen; - struct btrfs_root_item *root_item = &root->root_item; - int i; - int slot; - int ret; - - for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { - slot = path->slots[i]; - if (slot < btrfs_header_nritems(path->nodes[i]) - 1) { - struct extent_buffer *node; - struct btrfs_disk_key disk_key; - node = path->nodes[i]; - path->slots[i]++; - *level = i; - WARN_ON(*level == 0); - btrfs_node_key(node, &disk_key, path->slots[i]); - memcpy(&root_item->drop_progress, - &disk_key, sizeof(disk_key)); - root_item->drop_level = i; - return 0; - } else { - struct extent_buffer *parent; - if (path->nodes[*level] == root->node) - parent = path->nodes[*level]; - else - parent = path->nodes[*level + 1]; - - root_owner = btrfs_header_owner(parent); - root_gen = btrfs_header_generation(parent); - ret = btrfs_free_extent(trans, root, - path->nodes[*level]->start, - path->nodes[*level]->len, - parent->start, root_owner, - root_gen, *level, 1); - BUG_ON(ret); - free_extent_buffer(path->nodes[*level]); - path->nodes[*level] = NULL; - *level = i + 1; - } - } - return 1; -} - -/* - * drop the reference count on the tree rooted at 'snap'. This traverses - * the tree freeing any blocks that have a ref count of zero after being - * decremented. - */ -int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root - *root) -{ - int ret = 0; - int wret; - int level; - struct btrfs_path *path; - int i; - int orig_level; - struct btrfs_root_item *root_item = &root->root_item; - - path = btrfs_alloc_path(); - BUG_ON(!path); - - level = btrfs_header_level(root->node); - orig_level = level; - if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { - path->nodes[level] = root->node; - extent_buffer_get(root->node); - path->slots[level] = 0; - } else { - struct btrfs_key key; - struct btrfs_disk_key found_key; - struct extent_buffer *node; - - btrfs_disk_key_to_cpu(&key, &root_item->drop_progress); - level = root_item->drop_level; - path->lowest_level = level; - wret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (wret < 0) { - ret = wret; - goto out; - } - node = path->nodes[level]; - btrfs_node_key(node, &found_key, path->slots[level]); - WARN_ON(memcmp(&found_key, &root_item->drop_progress, - sizeof(found_key))); - } - while(1) { - wret = walk_down_tree(trans, root, path, &level); - if (wret < 0) - ret = wret; - if (wret != 0) - break; - - wret = walk_up_tree(trans, root, path, &level); - if (wret < 0) - ret = wret; - if (wret != 0) - break; - /* - ret = -EAGAIN; - break; - */ - } - for (i = 0; i <= orig_level; i++) { - if (path->nodes[i]) { - free_extent_buffer(path->nodes[i]); - path->nodes[i] = NULL; - } - } -out: - btrfs_free_path(path); - return ret; -} - -#endif - -int btrfs_free_block_groups(struct btrfs_fs_info *info) -{ - u64 start; - u64 end; - u64 ptr; - int ret; - while(1) { - ret = find_first_extent_bit(&info->block_group_cache, 0, - &start, &end, (unsigned int)-1); - if (ret) - break; - ret = get_state_private(&info->block_group_cache, start, &ptr); - if (!ret) - kfree((void *)(unsigned long)ptr); - clear_extent_bits(&info->block_group_cache, start, - end, (unsigned int)-1, GFP_NOFS); - } - while(1) { - ret = find_first_extent_bit(&info->free_space_cache, 0, - &start, &end, EXTENT_DIRTY); - if (ret) - break; - clear_extent_dirty(&info->free_space_cache, start, - end, GFP_NOFS); - } - return 0; -} - -int find_first_block_group(struct btrfs_root *root, struct btrfs_path *path, - struct btrfs_key *key) -{ - int ret; - struct btrfs_key found_key; - struct extent_buffer *leaf; - int slot; - - ret = btrfs_search_slot(NULL, root, key, path, 0, 0); - if (ret < 0) - return ret; - while(1) { - slot = path->slots[0]; - leaf = path->nodes[0]; - if (slot >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, path); - if (ret == 0) - continue; - if (ret < 0) - goto error; - break; - } - btrfs_item_key_to_cpu(leaf, &found_key, slot); - - if (found_key.objectid >= key->objectid && - found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) - return 0; - path->slots[0]++; - } - ret = -ENOENT; -error: - return ret; -} - -int btrfs_read_block_groups(struct btrfs_root *root) -{ - struct btrfs_path *path; - int ret; - int bit; - struct btrfs_block_group_cache *cache; - struct btrfs_fs_info *info = root->fs_info; - struct btrfs_space_info *space_info; - struct extent_io_tree *block_group_cache; - struct btrfs_key key; - struct btrfs_key found_key; - struct extent_buffer *leaf; - - block_group_cache = &info->block_group_cache; - - root = info->extent_root; - key.objectid = 0; - key.offset = 0; - btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY); - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - while(1) { - ret = find_first_block_group(root, path, &key); - if (ret > 0) { - ret = 0; - goto error; - } - if (ret != 0) { - goto error; - } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); - cache = kzalloc(sizeof(*cache), GFP_NOFS); - if (!cache) { - ret = -ENOMEM; - break; - } - - read_extent_buffer(leaf, &cache->item, - btrfs_item_ptr_offset(leaf, path->slots[0]), - sizeof(cache->item)); - memcpy(&cache->key, &found_key, sizeof(found_key)); - cache->cached = 0; - cache->pinned = 0; - key.objectid = found_key.objectid + found_key.offset; - btrfs_release_path(root, path); - cache->flags = btrfs_block_group_flags(&cache->item); - bit = 0; - if (cache->flags & BTRFS_BLOCK_GROUP_DATA) { - bit = BLOCK_GROUP_DATA; - } else if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) { - bit = BLOCK_GROUP_SYSTEM; - } else if (cache->flags & BTRFS_BLOCK_GROUP_METADATA) { - bit = BLOCK_GROUP_METADATA; - } - set_avail_alloc_bits(info, cache->flags); - if (btrfs_chunk_readonly(root, cache->key.objectid)) - cache->ro = 1; - - ret = update_space_info(info, cache->flags, found_key.offset, - btrfs_block_group_used(&cache->item), - &space_info); - BUG_ON(ret); - cache->space_info = space_info; - - /* use EXTENT_LOCKED to prevent merging */ - set_extent_bits(block_group_cache, found_key.objectid, - found_key.objectid + found_key.offset - 1, - bit | EXTENT_LOCKED, GFP_NOFS); - set_state_private(block_group_cache, found_key.objectid, - (unsigned long)cache); - } - ret = 0; -error: - btrfs_free_path(path); - return ret; -} - -int btrfs_make_block_group(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytes_used, - u64 type, u64 chunk_objectid, u64 chunk_offset, - u64 size) -{ - int ret; - int bit = 0; - struct btrfs_root *extent_root; - struct btrfs_block_group_cache *cache; - struct extent_io_tree *block_group_cache; - - extent_root = root->fs_info->extent_root; - block_group_cache = &root->fs_info->block_group_cache; - - cache = kzalloc(sizeof(*cache), GFP_NOFS); - BUG_ON(!cache); - cache->key.objectid = chunk_offset; - cache->key.offset = size; - - btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY); - btrfs_set_block_group_used(&cache->item, bytes_used); - btrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid); - cache->flags = type; - btrfs_set_block_group_flags(&cache->item, type); - - ret = update_space_info(root->fs_info, cache->flags, size, bytes_used, - &cache->space_info); - BUG_ON(ret); - - bit = block_group_state_bits(type); - set_extent_bits(block_group_cache, chunk_offset, - chunk_offset + size - 1, - bit | EXTENT_LOCKED, GFP_NOFS); - - set_state_private(block_group_cache, chunk_offset, - (unsigned long)cache); - ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item, - sizeof(cache->item)); - BUG_ON(ret); - - finish_current_insert(trans, extent_root); - ret = del_pending_extents(trans, extent_root); - BUG_ON(ret); - set_avail_alloc_bits(extent_root->fs_info, type); - return 0; -} - -/* - * This is for converter use only. - * - * In that case, we don't know where are free blocks located. - * Therefore all block group cache entries must be setup properly - * before doing any block allocation. - */ -int btrfs_make_block_groups(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - u64 total_bytes; - u64 cur_start; - u64 group_type; - u64 group_size; - u64 group_align; - u64 total_data = 0; - u64 total_metadata = 0; - u64 chunk_objectid; - int ret; - int bit; - struct btrfs_root *extent_root; - struct btrfs_block_group_cache *cache; - struct extent_io_tree *block_group_cache; - - extent_root = root->fs_info->extent_root; - block_group_cache = &root->fs_info->block_group_cache; - chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; - total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); - group_align = 64 * root->sectorsize; - - cur_start = 0; - while (cur_start < total_bytes) { - group_size = total_bytes / 12; - group_size = min_t(u64, group_size, total_bytes - cur_start); - if (cur_start == 0) { - bit = BLOCK_GROUP_SYSTEM; - group_type = BTRFS_BLOCK_GROUP_SYSTEM; - group_size /= 4; - group_size &= ~(group_align - 1); - group_size = max_t(u64, group_size, 8 * 1024 * 1024); - group_size = min_t(u64, group_size, 32 * 1024 * 1024); - } else { - group_size &= ~(group_align - 1); - if (total_data >= total_metadata * 2) { - group_type = BTRFS_BLOCK_GROUP_METADATA; - group_size = min_t(u64, group_size, - 1ULL * 1024 * 1024 * 1024); - total_metadata += group_size; - } else { - group_type = BTRFS_BLOCK_GROUP_DATA; - group_size = min_t(u64, group_size, - 5ULL * 1024 * 1024 * 1024); - total_data += group_size; - } - if ((total_bytes - cur_start) * 4 < group_size * 5) - group_size = total_bytes - cur_start; - } - - cache = kzalloc(sizeof(*cache), GFP_NOFS); - BUG_ON(!cache); - - cache->key.objectid = cur_start; - cache->key.offset = group_size; - btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY); - - btrfs_set_block_group_used(&cache->item, 0); - btrfs_set_block_group_chunk_objectid(&cache->item, - chunk_objectid); - btrfs_set_block_group_flags(&cache->item, group_type); - - cache->flags = group_type; - - ret = update_space_info(root->fs_info, group_type, group_size, - 0, &cache->space_info); - BUG_ON(ret); - set_avail_alloc_bits(extent_root->fs_info, group_type); - - set_extent_bits(block_group_cache, cur_start, - cur_start + group_size - 1, - bit | EXTENT_LOCKED, GFP_NOFS); - set_state_private(block_group_cache, cur_start, - (unsigned long)cache); - cur_start += group_size; - } - /* then insert all the items */ - cur_start = 0; - while(cur_start < total_bytes) { - cache = btrfs_lookup_block_group(root->fs_info, cur_start); - BUG_ON(!cache); - - ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item, - sizeof(cache->item)); - BUG_ON(ret); - - finish_current_insert(trans, extent_root); - ret = del_pending_extents(trans, extent_root); - BUG_ON(ret); - - cur_start = cache->key.objectid + cache->key.offset; - } - return 0; -} - -int btrfs_update_block_group(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, int alloc, - int mark_free) -{ - return update_block_group(trans, root, bytenr, num_bytes, - alloc, mark_free); -} diff --git a/extent_io.c b/extent_io.c deleted file mode 100644 index 069c199..0000000 --- a/extent_io.c +++ /dev/null @@ -1,781 +0,0 @@ - -/* - * Copyright (C) 2007 Oracle. 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 600 -#define __USE_XOPEN2K -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "extent_io.h" -#include "list.h" - -u64 cache_max = 1024 * 1024 * 32; - -void extent_io_tree_init(struct extent_io_tree *tree) -{ - cache_tree_init(&tree->state); - cache_tree_init(&tree->cache); - INIT_LIST_HEAD(&tree->lru); - tree->cache_size = 0; -} - -static struct extent_state *alloc_extent_state(void) -{ - struct extent_state *state; - - state = malloc(sizeof(*state)); - if (!state) - return NULL; - state->refs = 1; - state->state = 0; - state->private = 0; - return state; -} - -static void free_extent_state(struct extent_state *state) -{ - state->refs--; - BUG_ON(state->refs < 0); - if (state->refs == 0) - free(state); -} - -void extent_io_tree_cleanup(struct extent_io_tree *tree) -{ - struct extent_state *es; - struct extent_buffer *eb; - struct cache_extent *cache; - - while(!list_empty(&tree->lru)) { - eb = list_entry(tree->lru.next, struct extent_buffer, lru); - if (eb->refs != 1) { - fprintf(stderr, "extent buffer leak: " - "start %llu len %u\n", - (unsigned long long)eb->start, eb->len); - eb->refs = 1; - } - free_extent_buffer(eb); - } - while (1) { - cache = find_first_cache_extent(&tree->state, 0); - if (!cache) - break; - es = container_of(cache, struct extent_state, cache_node); - remove_cache_extent(&tree->state, &es->cache_node); - free_extent_state(es); - } -} - -static inline void update_extent_state(struct extent_state *state) -{ - state->cache_node.start = state->start; - state->cache_node.size = state->end + 1 - state->start; -} - -/* - * Utility function to look for merge candidates inside a given range. - * Any extents with matching state are merged together into a single - * extent in the tree. Extents with EXTENT_IO in their state field are - * not merged - */ -static int merge_state(struct extent_io_tree *tree, - struct extent_state *state) -{ - struct extent_state *other; - struct cache_extent *other_node; - - if (state->state & EXTENT_IOBITS) - return 0; - - other_node = prev_cache_extent(&state->cache_node); - if (other_node) { - other = container_of(other_node, struct extent_state, - cache_node); - if (other->end == state->start - 1 && - other->state == state->state) { - state->start = other->start; - update_extent_state(state); - remove_cache_extent(&tree->state, &other->cache_node); - free_extent_state(other); - } - } - other_node = next_cache_extent(&state->cache_node); - if (other_node) { - other = container_of(other_node, struct extent_state, - cache_node); - if (other->start == state->end + 1 && - other->state == state->state) { - other->start = state->start; - update_extent_state(other); - remove_cache_extent(&tree->state, &state->cache_node); - free_extent_state(state); - } - } - return 0; -} - -/* - * insert an extent_state struct into the tree. 'bits' are set on the - * struct before it is inserted. - */ -static int insert_state(struct extent_io_tree *tree, - struct extent_state *state, u64 start, u64 end, - int bits) -{ - int ret; - - BUG_ON(end < start); - state->state |= bits; - state->start = start; - state->end = end; - update_extent_state(state); - ret = insert_existing_cache_extent(&tree->state, &state->cache_node); - BUG_ON(ret); - merge_state(tree, state); - return 0; -} - -/* - * split a given extent state struct in two, inserting the preallocated - * struct 'prealloc' as the newly created second half. 'split' indicates an - * offset inside 'orig' where it should be split. - */ -static int split_state(struct extent_io_tree *tree, struct extent_state *orig, - struct extent_state *prealloc, u64 split) -{ - int ret; - prealloc->start = orig->start; - prealloc->end = split - 1; - prealloc->state = orig->state; - update_extent_state(prealloc); - orig->start = split; - update_extent_state(orig); - ret = insert_existing_cache_extent(&tree->state, - &prealloc->cache_node); - BUG_ON(ret); - return 0; -} - -/* - * clear some bits on a range in the tree. - */ -static int clear_state_bit(struct extent_io_tree *tree, - struct extent_state *state, int bits) -{ - int ret = state->state & bits; - - state->state &= ~bits; - if (state->state == 0) { - remove_cache_extent(&tree->state, &state->cache_node); - free_extent_state(state); - } else { - merge_state(tree, state); - } - return ret; -} - -/* - * set some bits on a range in the tree. - */ -int clear_extent_bits(struct extent_io_tree *tree, u64 start, - u64 end, int bits, gfp_t mask) -{ - struct extent_state *state; - struct extent_state *prealloc = NULL; - struct cache_extent *node; - u64 last_end; - int err; - int set = 0; - -again: - prealloc = alloc_extent_state(); - if (!prealloc) - return -ENOMEM; - - /* - * this search will find the extents that end after - * our range starts - */ - node = find_first_cache_extent(&tree->state, start); - if (!node) - goto out; - state = container_of(node, struct extent_state, cache_node); - if (state->start > end) - goto out; - last_end = state->end; - - /* - * | ---- desired range ---- | - * | state | or - * | ------------- state -------------- | - * - * We need to split the extent we found, and may flip - * bits on second half. - * - * If the extent we found extends past our range, we - * just split and search again. It'll get split again - * the next time though. - * - * If the extent we found is inside our range, we clear - * the desired bit on it. - */ - if (state->start < start) { - err = split_state(tree, state, prealloc, start); - BUG_ON(err == -EEXIST); - prealloc = NULL; - if (err) - goto out; - if (state->end <= end) { - set |= clear_state_bit(tree, state, bits); - if (last_end == (u64)-1) - goto out; - start = last_end + 1; - } else { - start = state->start; - } - goto search_again; - } - /* - * | ---- desired range ---- | - * | state | - * We need to split the extent, and clear the bit - * on the first half - */ - if (state->start <= end && state->end > end) { - err = split_state(tree, state, prealloc, end + 1); - BUG_ON(err == -EEXIST); - - set |= clear_state_bit(tree, prealloc, bits); - prealloc = NULL; - goto out; - } - - start = state->end + 1; - set |= clear_state_bit(tree, state, bits); - if (last_end == (u64)-1) - goto out; - start = last_end + 1; - goto search_again; -out: - if (prealloc) - free_extent_state(prealloc); - return set; - -search_again: - if (start > end) - goto out; - goto again; -} - -/* - * set some bits on a range in the tree. - */ -int set_extent_bits(struct extent_io_tree *tree, u64 start, - u64 end, int bits, gfp_t mask) -{ - struct extent_state *state; - struct extent_state *prealloc = NULL; - struct cache_extent *node; - int err = 0; - int set; - u64 last_start; - u64 last_end; -again: - prealloc = alloc_extent_state(); - if (!prealloc) - return -ENOMEM; - - /* - * this search will find the extents that end after - * our range starts - */ - node = find_first_cache_extent(&tree->state, start); - if (!node) { - err = insert_state(tree, prealloc, start, end, bits); - BUG_ON(err == -EEXIST); - prealloc = NULL; - goto out; - } - - state = container_of(node, struct extent_state, cache_node); - last_start = state->start; - last_end = state->end; - - /* - * | ---- desired range ---- | - * | state | - * - * Just lock what we found and keep going - */ - if (state->start == start && state->end <= end) { - set = state->state & bits; - state->state |= bits; - merge_state(tree, state); - if (last_end == (u64)-1) - goto out; - start = last_end + 1; - goto search_again; - } - /* - * | ---- desired range ---- | - * | state | - * or - * | ------------- state -------------- | - * - * We need to split the extent we found, and may flip bits on - * second half. - * - * If the extent we found extends past our - * range, we just split and search again. It'll get split - * again the next time though. - * - * If the extent we found is inside our range, we set the - * desired bit on it. - */ - if (state->start < start) { - set = state->state & bits; - err = split_state(tree, state, prealloc, start); - BUG_ON(err == -EEXIST); - prealloc = NULL; - if (err) - goto out; - if (state->end <= end) { - state->state |= bits; - start = state->end + 1; - merge_state(tree, state); - if (last_end == (u64)-1) - goto out; - start = last_end + 1; - } else { - start = state->start; - } - goto search_again; - } - /* - * | ---- desired range ---- | - * | state | or | state | - * - * There's a hole, we need to insert something in it and - * ignore the extent we found. - */ - if (state->start > start) { - u64 this_end; - if (end < last_start) - this_end = end; - else - this_end = last_start -1; - err = insert_state(tree, prealloc, start, this_end, - bits); - BUG_ON(err == -EEXIST); - prealloc = NULL; - if (err) - goto out; - start = this_end + 1; - goto search_again; - } - /* - * | ---- desired range ---- | - * | ---------- state ---------- | - * We need to split the extent, and set the bit - * on the first half - */ - set = state->state & bits; - err = split_state(tree, state, prealloc, end + 1); - BUG_ON(err == -EEXIST); - - state->state |= bits; - merge_state(tree, prealloc); - prealloc = NULL; -out: - if (prealloc) - free_extent_state(prealloc); - return err; -search_again: - if (start > end) - goto out; - goto again; -} - -int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, - gfp_t mask) -{ - return set_extent_bits(tree, start, end, EXTENT_DIRTY, mask); -} - -int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, - gfp_t mask) -{ - return clear_extent_bits(tree, start, end, EXTENT_DIRTY, mask); -} - -int find_first_extent_bit(struct extent_io_tree *tree, u64 start, - u64 *start_ret, u64 *end_ret, int bits) -{ - struct cache_extent *node; - struct extent_state *state; - int ret = 1; - - /* - * this search will find all the extents that end after - * our range starts. - */ - node = find_first_cache_extent(&tree->state, start); - if (!node) - goto out; - - while(1) { - state = container_of(node, struct extent_state, cache_node); - if (state->end >= start && (state->state & bits)) { - *start_ret = state->start; - *end_ret = state->end; - ret = 0; - break; - } - node = next_cache_extent(node); - if (!node) - break; - } -out: - return ret; -} - -int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, - int bits, int filled) -{ - struct extent_state *state = NULL; - struct cache_extent *node; - int bitset = 0; - - node = find_first_cache_extent(&tree->state, start); - while (node && start <= end) { - state = container_of(node, struct extent_state, cache_node); - - if (filled && state->start > start) { - bitset = 0; - break; - } - if (state->start > end) - break; - if (state->state & bits) { - bitset = 1; - if (!filled) - break; - } else if (filled) { - bitset = 0; - break; - } - start = state->end + 1; - if (start > end) - break; - node = next_cache_extent(node); - if (!node) { - if (filled) - bitset = 0; - break; - } - } - return bitset; -} - -int set_state_private(struct extent_io_tree *tree, u64 start, u64 private) -{ - struct cache_extent *node; - struct extent_state *state; - int ret = 0; - - node = find_first_cache_extent(&tree->state, start); - if (!node) { - ret = -ENOENT; - goto out; - } - state = container_of(node, struct extent_state, cache_node); - if (state->start != start) { - ret = -ENOENT; - goto out; - } - state->private = private; -out: - return ret; -} - -int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private) -{ - struct cache_extent *node; - struct extent_state *state; - int ret = 0; - - node = find_first_cache_extent(&tree->state, start); - if (!node) { - ret = -ENOENT; - goto out; - } - state = container_of(node, struct extent_state, cache_node); - if (state->start != start) { - ret = -ENOENT; - goto out; - } - *private = state->private; -out: - return ret; -} - -static int free_some_buffers(struct extent_io_tree *tree) -{ - u32 nrscan = 0; - struct extent_buffer *eb; - struct list_head *node, *next; - - if (tree->cache_size < cache_max) - return 0; - list_for_each_safe(node, next, &tree->lru) { - eb = list_entry(node, struct extent_buffer, lru); - if (eb->refs == 1) { - free_extent_buffer(eb); - if (tree->cache_size < cache_max) - break; - } else { - list_move_tail(&eb->lru, &tree->lru); - } - if (nrscan++ > 64) - break; - } - return 0; -} - -static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree, - u64 bytenr, u32 blocksize) -{ - struct extent_buffer *eb; - int ret; - - eb = malloc(sizeof(struct extent_buffer) + blocksize); - if (!eb) { - BUG(); - return NULL; - } - - eb->start = bytenr; - eb->len = blocksize; - eb->refs = 2; - eb->flags = 0; - eb->tree = tree; - eb->fd = -1; - eb->dev_bytenr = (u64)-1; - eb->cache_node.start = bytenr; - eb->cache_node.size = blocksize; - - free_some_buffers(tree); - ret = insert_existing_cache_extent(&tree->cache, &eb->cache_node); - if (ret) { - free(eb); - return NULL; - } - list_add_tail(&eb->lru, &tree->lru); - tree->cache_size += blocksize; - return eb; -} - -void free_extent_buffer(struct extent_buffer *eb) -{ - if (!eb) - return; - - eb->refs--; - BUG_ON(eb->refs < 0); - if (eb->refs == 0) { - struct extent_io_tree *tree = eb->tree; - BUG_ON(eb->flags & EXTENT_DIRTY); - list_del_init(&eb->lru); - remove_cache_extent(&tree->cache, &eb->cache_node); - BUG_ON(tree->cache_size < eb->len); - tree->cache_size -= eb->len; - free(eb); - } -} - -struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree, - u64 bytenr, u32 blocksize) -{ - struct extent_buffer *eb = NULL; - struct cache_extent *cache; - - cache = find_cache_extent(&tree->cache, bytenr, blocksize); - if (cache && cache->start == bytenr && cache->size == blocksize) { - eb = container_of(cache, struct extent_buffer, cache_node); - list_move_tail(&eb->lru, &tree->lru); - eb->refs++; - } - return eb; -} - -struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree, - u64 start) -{ - struct extent_buffer *eb = NULL; - struct cache_extent *cache; - - cache = find_first_cache_extent(&tree->cache, start); - if (cache) { - eb = container_of(cache, struct extent_buffer, cache_node); - list_move_tail(&eb->lru, &tree->lru); - eb->refs++; - } - return eb; -} - -struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, - u64 bytenr, u32 blocksize) -{ - struct extent_buffer *eb; - struct cache_extent *cache; - - cache = find_cache_extent(&tree->cache, bytenr, blocksize); - if (cache && cache->start == bytenr && cache->size == blocksize) { - eb = container_of(cache, struct extent_buffer, cache_node); - list_move_tail(&eb->lru, &tree->lru); - eb->refs++; - } else { - if (cache) { - eb = container_of(cache, struct extent_buffer, - cache_node); - BUG_ON(eb->refs != 1); - free_extent_buffer(eb); - } - eb = __alloc_extent_buffer(tree, bytenr, blocksize); - } - return eb; -} - -int read_extent_from_disk(struct extent_buffer *eb) -{ - int ret; - ret = pread(eb->fd, eb->data, eb->len, eb->dev_bytenr); - if (ret < 0) - goto out; - if (ret != eb->len) { - ret = -EIO; - goto out; - } - ret = 0; -out: - return ret; -} - -int write_extent_to_disk(struct extent_buffer *eb) -{ - int ret; - ret = pwrite(eb->fd, eb->data, eb->len, eb->dev_bytenr); - if (ret < 0) - goto out; - if (ret != eb->len) { - ret = -EIO; - goto out; - } - ret = 0; -out: - return ret; -} - -int set_extent_buffer_uptodate(struct extent_buffer *eb) -{ - eb->flags |= EXTENT_UPTODATE; - return 0; -} - -int clear_extent_buffer_uptodate(struct extent_io_tree *tree, - struct extent_buffer *eb) -{ - eb->flags &= ~EXTENT_UPTODATE; - return 0; -} - -int extent_buffer_uptodate(struct extent_buffer *eb) -{ - if (eb->flags & EXTENT_UPTODATE) - return 1; - return 0; -} - -int set_extent_buffer_dirty(struct extent_buffer *eb) -{ - struct extent_io_tree *tree = eb->tree; - if (!(eb->flags & EXTENT_DIRTY)) { - eb->flags |= EXTENT_DIRTY; - set_extent_dirty(tree, eb->start, eb->start + eb->len - 1, 0); - extent_buffer_get(eb); - } - return 0; -} - -int clear_extent_buffer_dirty(struct extent_buffer *eb) -{ - struct extent_io_tree *tree = eb->tree; - if (eb->flags & EXTENT_DIRTY) { - eb->flags &= ~EXTENT_DIRTY; - clear_extent_dirty(tree, eb->start, eb->start + eb->len - 1, 0); - free_extent_buffer(eb); - } - return 0; -} - -int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv, - unsigned long start, unsigned long len) -{ - return memcmp(eb->data + start, ptrv, len); -} - -void read_extent_buffer(struct extent_buffer *eb, void *dst, - unsigned long start, unsigned long len) -{ - memcpy(dst, eb->data + start, len); -} - -void write_extent_buffer(struct extent_buffer *eb, const void *src, - unsigned long start, unsigned long len) -{ - memcpy(eb->data + start, src, len); -} - -void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, - unsigned long dst_offset, unsigned long src_offset, - unsigned long len) -{ - memcpy(dst->data + dst_offset, src->data + src_offset, len); -} - -void memcpy_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, - unsigned long src_offset, unsigned long len) -{ - memcpy(dst->data + dst_offset, dst->data + src_offset, len); -} - -void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, - unsigned long src_offset, unsigned long len) -{ - memmove(dst->data + dst_offset, dst->data + src_offset, len); -} - -void memset_extent_buffer(struct extent_buffer *eb, char c, - unsigned long start, unsigned long len) -{ - memset(eb->data + start, c, len); -} diff --git a/extent_io.h b/extent_io.h deleted file mode 100644 index a5d6bf0..0000000 --- a/extent_io.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __EXTENTMAP__ -#define __EXTENTMAP__ -#include "kerncompat.h" -#include "extent-cache.h" -#include "list.h" - -#define EXTENT_DIRTY 1 -#define EXTENT_WRITEBACK (1 << 1) -#define EXTENT_UPTODATE (1 << 2) -#define EXTENT_LOCKED (1 << 3) -#define EXTENT_NEW (1 << 4) -#define EXTENT_DELALLOC (1 << 5) -#define EXTENT_DEFRAG (1 << 6) -#define EXTENT_DEFRAG_DONE (1 << 7) -#define EXTENT_BUFFER_FILLED (1 << 8) -#define EXTENT_CSUM (1 << 9) -#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK) - -struct extent_io_tree { - struct cache_tree state; - struct cache_tree cache; - struct list_head lru; - u64 cache_size; -}; - -struct extent_state { - struct cache_extent cache_node; - u64 start; - u64 end; - int refs; - unsigned long state; - u64 private; -}; - -struct extent_buffer { - struct cache_extent cache_node; - u64 start; - u64 dev_bytenr; - u32 len; - struct extent_io_tree *tree; - struct list_head lru; - int refs; - int flags; - int fd; - char data[]; -}; - -static inline void extent_buffer_get(struct extent_buffer *eb) -{ - eb->refs++; -} - -void extent_io_tree_init(struct extent_io_tree *tree); -void extent_io_tree_cleanup(struct extent_io_tree *tree); -int set_extent_bits(struct extent_io_tree *tree, u64 start, - u64 end, int bits, gfp_t mask); -int clear_extent_bits(struct extent_io_tree *tree, u64 start, - u64 end, int bits, gfp_t mask); -int find_first_extent_bit(struct extent_io_tree *tree, u64 start, - u64 *start_ret, u64 *end_ret, int bits); -int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, - int bits, int filled); -int set_extent_dirty(struct extent_io_tree *tree, u64 start, - u64 end, gfp_t mask); -int clear_extent_dirty(struct extent_io_tree *tree, u64 start, - u64 end, gfp_t mask); -int extent_buffer_uptodate(struct extent_buffer *eb); -int set_extent_buffer_uptodate(struct extent_buffer *eb); -int clear_extent_buffer_uptodate(struct extent_io_tree *tree, - struct extent_buffer *eb); -int set_state_private(struct extent_io_tree *tree, u64 start, u64 private); -int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private); -struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree, - u64 bytenr, u32 blocksize); -struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree, - u64 start); -struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, - u64 bytenr, u32 blocksize); -void free_extent_buffer(struct extent_buffer *eb); -int read_extent_from_disk(struct extent_buffer *eb); -int write_extent_to_disk(struct extent_buffer *eb); -int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv, - unsigned long start, unsigned long len); -void read_extent_buffer(struct extent_buffer *eb, void *dst, - unsigned long start, unsigned long len); -void write_extent_buffer(struct extent_buffer *eb, const void *src, - unsigned long start, unsigned long len); -void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, - unsigned long dst_offset, unsigned long src_offset, - unsigned long len); -void memcpy_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, - unsigned long src_offset, unsigned long len); -void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, - unsigned long src_offset, unsigned long len); -void memset_extent_buffer(struct extent_buffer *eb, char c, - unsigned long start, unsigned long len); -int set_extent_buffer_dirty(struct extent_buffer *eb); -int clear_extent_buffer_dirty(struct extent_buffer *eb); -#endif diff --git a/file-item.c b/file-item.c deleted file mode 100644 index 9732282..0000000 --- a/file-item.c +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "transaction.h" -#include "print-tree.h" -#include "crc32c.h" - -#define MAX_CSUM_ITEMS(r,size) ((((BTRFS_LEAF_DATA_SIZE(r) - \ - sizeof(struct btrfs_item) * 2) / \ - size) - 1)) -int btrfs_create_file(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 dirid, u64 *objectid) -{ - return 0; -} - -int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 objectid, u64 pos, u64 offset, - u64 disk_num_bytes, u64 num_bytes) -{ - int ret = 0; - struct btrfs_file_extent_item *item; - struct btrfs_key file_key; - struct btrfs_path *path; - struct extent_buffer *leaf; - - path = btrfs_alloc_path(); - BUG_ON(!path); - file_key.objectid = objectid; - file_key.offset = pos; - btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); - - ret = btrfs_insert_empty_item(trans, root, path, &file_key, - sizeof(*item)); - if (ret < 0) - goto out; - BUG_ON(ret); - leaf = path->nodes[0]; - item = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_file_extent_item); - btrfs_set_file_extent_disk_bytenr(leaf, item, offset); - btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes); - btrfs_set_file_extent_offset(leaf, item, 0); - btrfs_set_file_extent_num_bytes(leaf, item, num_bytes); - btrfs_set_file_extent_ram_bytes(leaf, item, num_bytes); - btrfs_set_file_extent_generation(leaf, item, trans->transid); - btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG); - btrfs_set_file_extent_compression(leaf, item, 0); - btrfs_set_file_extent_encryption(leaf, item, 0); - btrfs_set_file_extent_other_encoding(leaf, item, 0); - btrfs_mark_buffer_dirty(leaf); -out: - btrfs_free_path(path); - return ret; -} - -int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - u64 offset, char *buffer, size_t size) -{ - struct btrfs_key key; - struct btrfs_path *path; - struct extent_buffer *leaf; - unsigned long ptr; - struct btrfs_file_extent_item *ei; - u32 datasize; - int err = 0; - int ret; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - key.objectid = objectid; - key.offset = offset; - btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); - - datasize = btrfs_file_extent_calc_inline_size(size); - ret = btrfs_insert_empty_item(trans, root, path, &key, datasize); - if (ret) { - err = ret; - goto fail; - } - - leaf = path->nodes[0]; - ei = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_file_extent_item); - btrfs_set_file_extent_generation(leaf, ei, trans->transid); - btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE); - btrfs_set_file_extent_ram_bytes(leaf, ei, size); - btrfs_set_file_extent_compression(leaf, ei, 0); - btrfs_set_file_extent_encryption(leaf, ei, 0); - btrfs_set_file_extent_other_encoding(leaf, ei, 0); - - ptr = btrfs_file_extent_inline_start(ei) + offset - key.offset; - write_extent_buffer(leaf, buffer, ptr, size); - btrfs_mark_buffer_dirty(leaf); -fail: - btrfs_free_path(path); - return err; -} - -struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - u64 bytenr, int cow) -{ - int ret; - struct btrfs_key file_key; - struct btrfs_key found_key; - struct btrfs_csum_item *item; - struct extent_buffer *leaf; - u64 csum_offset = 0; - u16 csum_size = - btrfs_super_csum_size(&root->fs_info->super_copy); - int csums_in_item; - - file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; - file_key.offset = bytenr; - btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY); - ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); - if (ret < 0) - goto fail; - leaf = path->nodes[0]; - if (ret > 0) { - ret = 1; - if (path->slots[0] == 0) - goto fail; - path->slots[0]--; - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); - if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY) - goto fail; - - csum_offset = (bytenr - found_key.offset) / root->sectorsize; - csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]); - csums_in_item /= csum_size; - - if (csum_offset >= csums_in_item) { - ret = -EFBIG; - goto fail; - } - } - item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); - item = (struct btrfs_csum_item *)((unsigned char *)item + - csum_offset * csum_size); - return item; -fail: - if (ret > 0) - ret = -ENOENT; - return ERR_PTR(ret); -} - -int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 objectid, - u64 offset, int mod) -{ - int ret; - struct btrfs_key file_key; - int ins_len = mod < 0 ? -1 : 0; - int cow = mod != 0; - - file_key.objectid = objectid; - file_key.offset = offset; - btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); - ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow); - return ret; -} - -int btrfs_csum_file_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 alloc_end, - u64 bytenr, char *data, size_t len) -{ - int ret; - struct btrfs_key file_key; - struct btrfs_key found_key; - u64 next_offset = (u64)-1; - int found_next = 0; - struct btrfs_path *path; - struct btrfs_csum_item *item; - struct extent_buffer *leaf = NULL; - u64 csum_offset; - u32 csum_result = ~(u32)0; - u32 nritems; - u32 ins_size; - u16 csum_size = - btrfs_super_csum_size(&root->fs_info->super_copy); - - path = btrfs_alloc_path(); - BUG_ON(!path); - - file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; - file_key.offset = bytenr; - file_key.type = BTRFS_EXTENT_CSUM_KEY; - - item = btrfs_lookup_csum(trans, root, path, bytenr, 1); - if (!IS_ERR(item)) { - leaf = path->nodes[0]; - goto found; - } - ret = PTR_ERR(item); - if (ret == -EFBIG) { - u32 item_size; - /* we found one, but it isn't big enough yet */ - leaf = path->nodes[0]; - item_size = btrfs_item_size_nr(leaf, path->slots[0]); - if ((item_size / csum_size) >= MAX_CSUM_ITEMS(root, csum_size)) { - /* already at max size, make a new one */ - goto insert; - } - } else { - int slot = path->slots[0] + 1; - /* we didn't find a csum item, insert one */ - nritems = btrfs_header_nritems(path->nodes[0]); - if (path->slots[0] >= nritems - 1) { - ret = btrfs_next_leaf(root, path); - if (ret == 1) - found_next = 1; - if (ret != 0) - goto insert; - slot = 0; - } - btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot); - if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || - found_key.type != BTRFS_EXTENT_CSUM_KEY) { - found_next = 1; - goto insert; - } - next_offset = found_key.offset; - found_next = 1; - goto insert; - } - - /* - * at this point, we know the tree has an item, but it isn't big - * enough yet to put our csum in. Grow it - */ - btrfs_release_path(root, path); - ret = btrfs_search_slot(trans, root, &file_key, path, - csum_size, 1); - if (ret < 0) - goto fail; - if (ret == 0) { - BUG(); - } - if (path->slots[0] == 0) { - goto insert; - } - path->slots[0]--; - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); - csum_offset = (file_key.offset - found_key.offset) / root->sectorsize; - if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || - found_key.type != BTRFS_EXTENT_CSUM_KEY || - csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) { - goto insert; - } - if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) / - csum_size) { - u32 diff = (csum_offset + 1) * csum_size; - diff = diff - btrfs_item_size_nr(leaf, path->slots[0]); - if (diff != csum_size) - goto insert; - ret = btrfs_extend_item(trans, root, path, diff); - BUG_ON(ret); - goto csum; - } - -insert: - btrfs_release_path(root, path); - csum_offset = 0; - if (found_next) { - u64 tmp = min(alloc_end, next_offset); - tmp -= file_key.offset; - tmp /= root->sectorsize; - tmp = max((u64)1, tmp); - tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root, csum_size)); - ins_size = csum_size * tmp; - } else { - ins_size = csum_size; - } - ret = btrfs_insert_empty_item(trans, root, path, &file_key, - ins_size); - if (ret < 0) - goto fail; - if (ret != 0) { - WARN_ON(1); - goto fail; - } -csum: - leaf = path->nodes[0]; - item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); - ret = 0; - item = (struct btrfs_csum_item *)((unsigned char *)item + - csum_offset * csum_size); -found: - csum_result = btrfs_csum_data(root, data, csum_result, len); - btrfs_csum_final(csum_result, (char *)&csum_result); - if (csum_result == 0) { - printk("csum result is 0 for block %llu\n", - (unsigned long long)bytenr); - } - - write_extent_buffer(leaf, &csum_result, (unsigned long)item, - csum_size); - btrfs_mark_buffer_dirty(path->nodes[0]); -fail: - btrfs_release_path(root, path); - btrfs_free_path(path); - return ret; -} - -/* - * helper function for csum removal, this expects the - * key to describe the csum pointed to by the path, and it expects - * the csum to overlap the range [bytenr, len] - * - * The csum should not be entirely contained in the range and the - * range should not be entirely contained in the csum. - * - * This calls btrfs_truncate_item with the correct args based on the - * overlap, and fixes up the key as required. - */ -static noinline int truncate_one_csum(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_key *key, - u64 bytenr, u64 len) -{ - struct extent_buffer *leaf; - u16 csum_size = - btrfs_super_csum_size(&root->fs_info->super_copy); - u64 csum_end; - u64 end_byte = bytenr + len; - u32 blocksize = root->sectorsize; - int ret; - - leaf = path->nodes[0]; - csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; - csum_end *= root->sectorsize; - csum_end += key->offset; - - if (key->offset < bytenr && csum_end <= end_byte) { - /* - * [ bytenr - len ] - * [ ] - * [csum ] - * A simple truncate off the end of the item - */ - u32 new_size = (bytenr - key->offset) / blocksize; - new_size *= csum_size; - ret = btrfs_truncate_item(trans, root, path, new_size, 1); - BUG_ON(ret); - } else if (key->offset >= bytenr && csum_end > end_byte && - end_byte > key->offset) { - /* - * [ bytenr - len ] - * [ ] - * [csum ] - * we need to truncate from the beginning of the csum - */ - u32 new_size = (csum_end - end_byte) / blocksize; - new_size *= csum_size; - - ret = btrfs_truncate_item(trans, root, path, new_size, 0); - BUG_ON(ret); - - key->offset = end_byte; - ret = btrfs_set_item_key_safe(trans, root, path, key); - BUG_ON(ret); - } else { - BUG(); - } - return 0; -} - -/* - * deletes the csum items from the csum tree for a given - * range of bytes. - */ -int btrfs_del_csums(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytenr, u64 len) -{ - struct btrfs_path *path; - struct btrfs_key key; - u64 end_byte = bytenr + len; - u64 csum_end; - struct extent_buffer *leaf; - int ret; - u16 csum_size = - btrfs_super_csum_size(&root->fs_info->super_copy); - int blocksize = root->sectorsize; - - root = root->fs_info->csum_root; - - path = btrfs_alloc_path(); - - while (1) { - key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; - key.offset = end_byte - 1; - key.type = BTRFS_EXTENT_CSUM_KEY; - - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); - if (ret > 0) { - if (path->slots[0] == 0) - goto out; - path->slots[0]--; - } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - - if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || - key.type != BTRFS_EXTENT_CSUM_KEY) { - break; - } - - if (key.offset >= end_byte) - break; - - csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; - csum_end *= blocksize; - csum_end += key.offset; - - /* this csum ends before we start, we're done */ - if (csum_end <= bytenr) - break; - - /* delete the entire item, it is inside our range */ - if (key.offset >= bytenr && csum_end <= end_byte) { - ret = btrfs_del_item(trans, root, path); - BUG_ON(ret); - } else if (key.offset < bytenr && csum_end > end_byte) { - unsigned long offset; - unsigned long shift_len; - unsigned long item_offset; - /* - * [ bytenr - len ] - * [csum ] - * - * Our bytes are in the middle of the csum, - * we need to split this item and insert a new one. - * - * But we can't drop the path because the - * csum could change, get removed, extended etc. - * - * The trick here is the max size of a csum item leaves - * enough room in the tree block for a single - * item header. So, we split the item in place, - * adding a new header pointing to the existing - * bytes. Then we loop around again and we have - * a nicely formed csum item that we can neatly - * truncate. - */ - offset = (bytenr - key.offset) / blocksize; - offset *= csum_size; - - shift_len = (len / blocksize) * csum_size; - - item_offset = btrfs_item_ptr_offset(leaf, - path->slots[0]); - - memset_extent_buffer(leaf, 0, item_offset + offset, - shift_len); - key.offset = bytenr; - - /* - * btrfs_split_item returns -EAGAIN when the - * item changed size or key - */ - ret = btrfs_split_item(trans, root, path, &key, offset); - BUG_ON(ret && ret != -EAGAIN); - - key.offset = end_byte - 1; - } else { - ret = truncate_one_csum(trans, root, path, - &key, bytenr, len); - BUG_ON(ret); - } - btrfs_release_path(root, path); - } -out: - btrfs_free_path(path); - return 0; -} diff --git a/hash.h b/hash.h deleted file mode 100644 index 804fcda..0000000 --- a/hash.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __HASH__ -#define __HASH__ -#include "crc32c.h" - -static inline u64 btrfs_name_hash(const char *name, int len) -{ - return crc32c((u32)~1, name, len); -} -#endif diff --git a/hasher.c b/hasher.c deleted file mode 100644 index b80407c..0000000 --- a/hasher.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include -#include "kerncompat.h" -#include "hash.h" - -int main() { - u64 result; - int ret; - char line[255]; - char *p; - while(1) { - p = fgets(line, 255, stdin); - if (!p) - break; - if (strlen(line) == 0) - continue; - if (line[strlen(line)-1] == '\n') - line[strlen(line)-1] = '\0'; - result = btrfs_name_hash(line, strlen(line)); - printf("hash returns %llu\n", (unsigned long long)result); - } - return 0; -} diff --git a/inode-item.c b/inode-item.c deleted file mode 100644 index 28865f7..0000000 --- a/inode-item.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 "ctree.h" -#include "disk-io.h" -#include "transaction.h" - -int find_name_in_backref(struct btrfs_path *path, const char * name, - int name_len, struct btrfs_inode_ref **ref_ret) -{ - struct extent_buffer *leaf; - struct btrfs_inode_ref *ref; - unsigned long ptr; - unsigned long name_ptr; - u32 item_size; - u32 cur_offset = 0; - int len; - - leaf = path->nodes[0]; - item_size = btrfs_item_size_nr(leaf, path->slots[0]); - ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); - while (cur_offset < item_size) { - ref = (struct btrfs_inode_ref *)(ptr + cur_offset); - len = btrfs_inode_ref_name_len(leaf, ref); - name_ptr = (unsigned long)(ref + 1); - cur_offset += len + sizeof(*ref); - if (len != name_len) - continue; - if (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) { - *ref_ret = ref; - return 1; - } - } - return 0; -} - -int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - const char *name, int name_len, - u64 inode_objectid, u64 ref_objectid) -{ - struct btrfs_path *path; - struct btrfs_key key; - struct btrfs_inode_ref *ref; - struct extent_buffer *leaf; - unsigned long ptr; - unsigned long item_start; - u32 item_size; - u32 sub_item_len; - int ret; - int del_len = name_len + sizeof(*ref); - - key.objectid = inode_objectid; - key.offset = ref_objectid; - btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY); - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); - if (ret > 0) { - ret = -ENOENT; - goto out; - } else if (ret < 0) { - goto out; - } - if (!find_name_in_backref(path, name, name_len, &ref)) { - ret = -ENOENT; - goto out; - } - leaf = path->nodes[0]; - item_size = btrfs_item_size_nr(leaf, path->slots[0]); - if (del_len == item_size) { - ret = btrfs_del_item(trans, root, path); - goto out; - } - ptr = (unsigned long)ref; - sub_item_len = name_len + sizeof(*ref); - item_start = btrfs_item_ptr_offset(leaf, path->slots[0]); - memmove_extent_buffer(leaf, ptr, ptr + sub_item_len, - item_size - (ptr + sub_item_len - item_start)); - ret = btrfs_truncate_item(trans, root, path, - item_size - sub_item_len, 1); - BUG_ON(ret); -out: - btrfs_free_path(path); - return ret; -} - -int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - const char *name, int name_len, - u64 inode_objectid, u64 ref_objectid, u64 index) -{ - struct btrfs_path *path; - struct btrfs_key key; - struct btrfs_inode_ref *ref; - unsigned long ptr; - int ret; - int ins_len = name_len + sizeof(*ref); - - key.objectid = inode_objectid; - key.offset = ref_objectid; - btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY); - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - ret = btrfs_insert_empty_item(trans, root, path, &key, - ins_len); - if (ret == -EEXIST) { - u32 old_size; - - if (find_name_in_backref(path, name, name_len, &ref)) - goto out; - - old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]); - ret = btrfs_extend_item(trans, root, path, ins_len); - BUG_ON(ret); - ref = btrfs_item_ptr(path->nodes[0], path->slots[0], - struct btrfs_inode_ref); - ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size); - btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len); - btrfs_set_inode_ref_index(path->nodes[0], ref, index); - ptr = (unsigned long)(ref + 1); - ret = 0; - } else if (ret < 0) { - goto out; - } else { - ref = btrfs_item_ptr(path->nodes[0], path->slots[0], - struct btrfs_inode_ref); - btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len); - btrfs_set_inode_ref_index(path->nodes[0], ref, index); - ptr = (unsigned long)(ref + 1); - } - write_extent_buffer(path->nodes[0], name, ptr, name_len); - btrfs_mark_buffer_dirty(path->nodes[0]); - -out: - btrfs_free_path(path); - return ret; -} - -int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path, u64 objectid) -{ - struct btrfs_key key; - int ret; - key.objectid = objectid; - btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); - key.offset = 0; - - ret = btrfs_insert_empty_item(trans, root, path, &key, - sizeof(struct btrfs_inode_item)); - if (ret == 0 && objectid > root->highest_inode) - root->highest_inode = objectid; - return ret; -} - -int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, - struct btrfs_key *location, int mod) -{ - int ins_len = mod < 0 ? -1 : 0; - int cow = mod != 0; - int ret; - int slot; - struct extent_buffer *leaf; - struct btrfs_key found_key; - - ret = btrfs_search_slot(trans, root, location, path, ins_len, cow); - if (ret > 0 && btrfs_key_type(location) == BTRFS_ROOT_ITEM_KEY && - location->offset == (u64)-1 && path->slots[0] != 0) { - slot = path->slots[0] - 1; - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &found_key, slot); - if (found_key.objectid == location->objectid && - btrfs_key_type(&found_key) == btrfs_key_type(location)) { - path->slots[0]--; - return 0; - } - } - return ret; -} - -int btrfs_insert_inode(struct btrfs_trans_handle *trans, struct btrfs_root - *root, u64 objectid, struct btrfs_inode_item - *inode_item) -{ - int ret; - struct btrfs_key key; - - key.objectid = objectid; - key.type = BTRFS_INODE_ITEM_KEY; - key.offset = 0; - - ret = btrfs_insert_item(trans, root, &key, inode_item, - sizeof(*inode_item)); - return ret; -} diff --git a/inode-map.c b/inode-map.c deleted file mode 100644 index d2970d4..0000000 --- a/inode-map.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 "ctree.h" -#include "disk-io.h" -#include "transaction.h" - -int btrfs_find_highest_inode(struct btrfs_root *root, u64 *objectid) -{ - struct btrfs_path *path; - int ret; - struct extent_buffer *l; - struct btrfs_key search_key; - struct btrfs_key found_key; - int slot; - - path = btrfs_alloc_path(); - BUG_ON(!path); - - search_key.objectid = (u64)-1; - search_key.offset = (u64)-1; - ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); - if (ret < 0) - goto error; - BUG_ON(ret == 0); - if (path->slots[0] > 0) { - slot = path->slots[0] - 1; - l = path->nodes[0]; - btrfs_item_key_to_cpu(l, &found_key, slot); - *objectid = found_key.objectid; - } else { - *objectid = BTRFS_FIRST_FREE_OBJECTID; - } - ret = 0; -error: - btrfs_free_path(path); - return ret; -} - -/* - * walks the btree of allocated inodes and find a hole. - */ -int btrfs_find_free_objectid(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 dirid, u64 *objectid) -{ - struct btrfs_path *path; - struct btrfs_key key; - int ret; - int slot = 0; - u64 last_ino = 0; - int start_found; - struct extent_buffer *l; - struct btrfs_key search_key; - u64 search_start = dirid; - - path = btrfs_alloc_path(); - BUG_ON(!path); - search_start = root->last_inode_alloc; - search_start = max((unsigned long long)search_start, - BTRFS_FIRST_FREE_OBJECTID); - search_key.objectid = search_start; - search_key.offset = 0; - - btrfs_init_path(path); - start_found = 0; - ret = btrfs_search_slot(trans, root, &search_key, path, 0, 0); - if (ret < 0) - goto error; - - if (path->slots[0] > 0) - path->slots[0]--; - - while (1) { - l = path->nodes[0]; - slot = path->slots[0]; - if (slot >= btrfs_header_nritems(l)) { - ret = btrfs_next_leaf(root, path); - if (ret == 0) - continue; - if (ret < 0) - goto error; - if (!start_found) { - *objectid = search_start; - start_found = 1; - goto found; - } - *objectid = last_ino > search_start ? - last_ino : search_start; - goto found; - } - btrfs_item_key_to_cpu(l, &key, slot); - if (key.objectid >= search_start) { - if (start_found) { - if (last_ino < search_start) - last_ino = search_start; - if (key.objectid > last_ino) { - *objectid = last_ino; - goto found; - } - } - } - start_found = 1; - last_ino = key.objectid + 1; - path->slots[0]++; - } - // FIXME -ENOSPC -found: - root->last_inode_alloc = *objectid; - btrfs_release_path(root, path); - btrfs_free_path(path); - BUG_ON(*objectid < search_start); - return 0; -error: - btrfs_release_path(root, path); - btrfs_free_path(path); - return ret; -} diff --git a/ioctl.h b/ioctl.h deleted file mode 100644 index 4410ac0..0000000 --- a/ioctl.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __IOCTL_ -#define __IOCTL_ -#include -#include - -#define BTRFS_IOCTL_MAGIC 0x94 -#define BTRFS_VOL_NAME_MAX 255 -#define BTRFS_PATH_NAME_MAX 4087 - -struct btrfs_ioctl_vol_args { - __s64 fd; - char name[BTRFS_PATH_NAME_MAX + 1]; -}; - -#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \ - struct btrfs_ioctl_vol_args) -#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ - struct btrfs_ioctl_vol_args) -#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \ - struct btrfs_ioctl_vol_args) -#define BTRFS_IOC_SCAN_DEV _IOW(BTRFS_IOCTL_MAGIC, 4, \ - struct btrfs_ioctl_vol_args) -/* trans start and trans end are dangerous, and only for - * use by applications that know how to avoid the - * resulting deadlocks - */ -#define BTRFS_IOC_TRANS_START _IO(BTRFS_IOCTL_MAGIC, 6) -#define BTRFS_IOC_TRANS_END _IO(BTRFS_IOCTL_MAGIC, 7) -#define BTRFS_IOC_SYNC _IO(BTRFS_IOCTL_MAGIC, 8) - -#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) -#define BTRFS_IOC_ADD_DEV _IOW(BTRFS_IOCTL_MAGIC, 10, \ - struct btrfs_ioctl_vol_args) -#define BTRFS_IOC_RM_DEV _IOW(BTRFS_IOCTL_MAGIC, 11, \ - struct btrfs_ioctl_vol_args) -#define BTRFS_IOC_BALANCE _IOW(BTRFS_IOCTL_MAGIC, 12, \ - struct btrfs_ioctl_vol_args) -/* 13 is for CLONE_RANGE */ -#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ - struct btrfs_ioctl_vol_args) - -#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ - struct btrfs_ioctl_vol_args) -#endif diff --git a/kerncompat.h b/kerncompat.h deleted file mode 100644 index e4c8ce0..0000000 --- a/kerncompat.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __KERNCOMPAT -#define __KERNCOMPAT -#include -#include -#include -#include -#include -#include -#include - -#ifndef READ -#define READ 0 -#define WRITE 1 -#define READA 2 -#endif - -#define gfp_t int -#define get_cpu_var(p) (p) -#define __get_cpu_var(p) (p) -#define BITS_PER_LONG (sizeof(long) * 8) -#define __GFP_BITS_SHIFT 20 -#define __GFP_BITS_MASK ((int)((1 << __GFP_BITS_SHIFT) - 1)) -#define GFP_KERNEL 0 -#define GFP_NOFS 0 -#define __read_mostly -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#define ULONG_MAX (~0UL) -#define BUG() abort() -#ifdef __CHECKER__ -#define __force __attribute__((force)) -#define __bitwise__ __attribute__((bitwise)) -#else -#define __force -#define __bitwise__ -#endif - -#ifndef __CHECKER__ -#include -typedef __u32 u32; -typedef __u64 u64; -typedef __u16 u16; -typedef __u8 u8; -#else -typedef unsigned int u32; -typedef unsigned int __u32; -typedef unsigned long long u64; -typedef unsigned char u8; -typedef unsigned short u16; -#endif - - -struct vma_shared { int prio_tree_node; }; -struct vm_area_struct { - unsigned long vm_pgoff; - unsigned long vm_start; - unsigned long vm_end; - struct vma_shared shared; -}; - -struct page { - unsigned long index; -}; - -struct mutex { - unsigned long lock; -}; - -#define mutex_init(m) \ -do { \ - (m)->lock = 1; \ -} while (0) - -static inline void mutex_lock(struct mutex *m) -{ - m->lock--; -} - -static inline void mutex_unlock(struct mutex *m) -{ - m->lock++; -} - -static inline int mutex_is_locked(struct mutex *m) -{ - return (m->lock != 1); -} - -#define cond_resched() do { } while (0) -#define preempt_enable() do { } while (0) -#define preempt_disable() do { } while (0) - -#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) - -/** - * __set_bit - Set a bit in memory - * @nr: the bit to set - * @addr: the address to start counting from - * - * Unlike set_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static inline void __set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - - *p |= mask; -} - -static inline void __clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - - *p &= ~mask; -} - -/** - * test_bit - Determine whether a bit is set - * @nr: bit number to test - * @addr: Address to start counting from - */ -static inline int test_bit(int nr, const volatile unsigned long *addr) -{ - return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); -} - -/* - * error pointer - */ -#define MAX_ERRNO 4095 -#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO) - -static inline void *ERR_PTR(long error) -{ - return (void *) error; -} - -static inline long PTR_ERR(const void *ptr) -{ - return (long) ptr; -} - -static inline long IS_ERR(const void *ptr) -{ - return IS_ERR_VALUE((unsigned long)ptr); -} - -/* - * max/min macro - */ -#define min(x,y) ({ \ - typeof(x) _x = (x); \ - typeof(y) _y = (y); \ - (void) (&_x == &_y); \ - _x < _y ? _x : _y; }) - -#define max(x,y) ({ \ - typeof(x) _x = (x); \ - typeof(y) _y = (y); \ - (void) (&_x == &_y); \ - _x > _y ? _x : _y; }) - -#define min_t(type,x,y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) -#define max_t(type,x,y) \ - ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) - -/* - * printk - */ -#define printk(fmt, args...) fprintf(stderr, fmt, ##args) -#define KERN_CRIT "" -#define KERN_ERR "" - -/* - * kmalloc/kfree - */ -#define kmalloc(x, y) malloc(x) -#define kzalloc(x, y) calloc(1, x) -#define kstrdup(x, y) strdup(x) -#define kfree(x) free(x) - -#define BUG_ON(c) assert(!(c)) -#define WARN_ON(c) assert(!(c)) - -#undef offsetof -#ifdef __compiler_offsetof -#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) -#else -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) -#endif - -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) -#ifdef __CHECKER__ -#define __CHECK_ENDIAN__ -#define __bitwise __bitwise__ -#else -#define __bitwise -#endif - -typedef u16 __bitwise __le16; -typedef u16 __bitwise __be16; -typedef u32 __bitwise __le32; -typedef u32 __bitwise __be32; -typedef u64 __bitwise __le64; -typedef u64 __bitwise __be64; - -/* Macros to generate set/get funcs for the struct fields - * assume there is a lefoo_to_cpu for every type, so lets make a simple - * one for u8: - */ -#define le8_to_cpu(v) (v) -#define cpu_to_le8(v) (v) -#define __le8 u8 - -#if __BYTE_ORDER == __BIG_ENDIAN -#define cpu_to_le64(x) ((__force __le64)(u64)(bswap_64(x))) -#define le64_to_cpu(x) ((__force u64)(__le64)(bswap_64(x))) -#define cpu_to_le32(x) ((__force __le32)(u32)(bswap_32(x))) -#define le32_to_cpu(x) ((__force u32)(__le32)(bswap_32(x))) -#define cpu_to_le16(x) ((__force __le16)(u16)(bswap_16(x))) -#define le16_to_cpu(x) ((__force u16)(__le16)(bswap_16(x))) -#else -#define cpu_to_le64(x) ((__force __le64)(u64)(x)) -#define le64_to_cpu(x) ((__force u64)(__le64)(x)) -#define cpu_to_le32(x) ((__force __le32)(u32)(x)) -#define le32_to_cpu(x) ((__force u32)(__le32)(x)) -#define cpu_to_le16(x) ((__force __le16)(u16)(x)) -#define le16_to_cpu(x) ((__force u16)(__le16)(x)) -#endif -#endif - -#ifndef noinline -#define noinline -#endif diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..7ece415 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,47 @@ +CC=gcc +AR = ar +ARGEN = $(AR) rc +RANLIB = ranlib +AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 +CFLAGS = -g -Werror -Os +objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ + root-tree.o dir-item.o file-item.o inode-item.o \ + inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ + volumes.o utils.o + +# +CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ + -Wuninitialized -Wshadow -Wundef +DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ + +INSTALL= install +prefix ?= /usr/local +libdir = $(prefix)/lib/ + + +# make C=1 to enable sparse +ifdef C + check=sparse $(CHECKFLAGS) +else + check=ls +endif + +.c.o: + $(check) $< + $(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $< + +all: libbtrfs.a + +libbtrfs.a: $(objects) + $(ARGEN) $@ $(objects) + $(RANLIB) $@ + +clean : + rm -f $(progs) *.o .*.d + rm -f libbtrfs.a + +install: $(progs) + $(INSTALL) -m755 -d $(DESTDIR)$(libdir) + $(INSTALL) libbtrfs.a $(DESTDIR)$(libdir) + +-include .*.d diff --git a/lib/bit-radix.c b/lib/bit-radix.c new file mode 100644 index 0000000..57f6f3c --- /dev/null +++ b/lib/bit-radix.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2007 Oracle. 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 "kerncompat.h" +#include "radix-tree.h" + +#define BIT_ARRAY_BYTES 256 +#define BIT_RADIX_BITS_PER_ARRAY ((BIT_ARRAY_BYTES - sizeof(unsigned long)) * 8) + +int set_radix_bit(struct radix_tree_root *radix, unsigned long bit) +{ + unsigned long *bits; + unsigned long slot; + int bit_slot; + int ret; + + slot = bit / BIT_RADIX_BITS_PER_ARRAY; + bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY; + + bits = radix_tree_lookup(radix, slot); + if (!bits) { + bits = malloc(BIT_ARRAY_BYTES); + if (!bits) + return -ENOMEM; + memset(bits + 1, 0, BIT_ARRAY_BYTES - sizeof(unsigned long)); + bits[0] = slot; + radix_tree_preload(GFP_NOFS); + ret = radix_tree_insert(radix, slot, bits); + radix_tree_preload_end(); + if (ret) + return ret; + } + __set_bit(bit_slot, bits + 1); + return 0; +} + +int test_radix_bit(struct radix_tree_root *radix, unsigned long bit) +{ + unsigned long *bits; + unsigned long slot; + int bit_slot; + + slot = bit / BIT_RADIX_BITS_PER_ARRAY; + bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY; + + bits = radix_tree_lookup(radix, slot); + if (!bits) + return 0; + return test_bit(bit_slot, bits + 1); +} + +int clear_radix_bit(struct radix_tree_root *radix, unsigned long bit) +{ + unsigned long *bits; + unsigned long slot; + int bit_slot; + int i; + int empty = 1; + slot = bit / BIT_RADIX_BITS_PER_ARRAY; + bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY; + + bits = radix_tree_lookup(radix, slot); + if (!bits) + return 0; + __clear_bit(bit_slot, bits + 1); + for (i = 1; i < BIT_ARRAY_BYTES / sizeof(unsigned long); i++) { + if (bits[i]) { + empty = 0; + break; + } + } + if (empty) { + bits = radix_tree_delete(radix, slot); + BUG_ON(!bits); + free(bits); + } + return 0; +} + +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +/** + * __ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static unsigned long __ffs(unsigned long word) +{ + int num = 0; + + if (sizeof(long) == 8 && (word & 0xffffffff) == 0) { + num += 32; + word >>= sizeof(long) * 4; + } + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf) == 0) { + num += 4; + word >>= 4; + } + if ((word & 0x3) == 0) { + num += 2; + word >>= 2; + } + if ((word & 0x1) == 0) + num += 1; + return num; +} + +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +unsigned long find_next_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset %= BITS_PER_LONG; + if (offset) { + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < BITS_PER_LONG) + goto found_first; + if (tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if ((tmp = *(p++))) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} + +int find_first_radix_bit(struct radix_tree_root *radix, unsigned long *retbits, + unsigned long start, int nr) +{ + unsigned long *bits; + unsigned long *gang[4]; + int found; + int ret; + int i; + int total_found = 0; + unsigned long slot; + + slot = start / BIT_RADIX_BITS_PER_ARRAY; + ret = radix_tree_gang_lookup(radix, (void *)gang, slot, + ARRAY_SIZE(gang)); + found = start % BIT_RADIX_BITS_PER_ARRAY; + for (i = 0; i < ret && nr > 0; i++) { + bits = gang[i]; + while(nr > 0) { + found = find_next_bit(bits + 1, + BIT_RADIX_BITS_PER_ARRAY, + found); + if (found < BIT_RADIX_BITS_PER_ARRAY) { + *retbits = bits[0] * + BIT_RADIX_BITS_PER_ARRAY + found; + retbits++; + nr--; + total_found++; + found++; + } else + break; + } + found = 0; + } + return total_found; +} diff --git a/lib/bit-radix.h b/lib/bit-radix.h new file mode 100644 index 0000000..af14e80 --- /dev/null +++ b/lib/bit-radix.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __BIT_RADIX__ +#define __BIT_RADIX__ +#include "radix-tree.h" + +int set_radix_bit(struct radix_tree_root *radix, unsigned long bit); +int test_radix_bit(struct radix_tree_root *radix, unsigned long bit); +int clear_radix_bit(struct radix_tree_root *radix, unsigned long bit); +int find_first_radix_bit(struct radix_tree_root *radix, unsigned long *retbits, + unsigned long start, int nr); + +static inline void init_bit_radix(struct radix_tree_root *radix) +{ + INIT_RADIX_TREE(radix, GFP_NOFS); +} +#endif diff --git a/lib/crc32c.c b/lib/crc32c.c new file mode 100644 index 0000000..4dc5f9c --- /dev/null +++ b/lib/crc32c.c @@ -0,0 +1,101 @@ +/* + * Copied from the kernel source code, lib/libcrc32c.c. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + */ +#include "kerncompat.h" +#include "crc32c.h" + +/* + * This is the CRC-32C table + * Generated with: + * width = 32 bits + * poly = 0x1EDC6F41 + * reflect input bytes = true + * reflect output bytes = true + */ + +static const u32 crc32c_table[256] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L +}; + +/* + * Steps through buffer one byte at at time, calculates reflected + * crc using table. + */ + +u32 crc32c_le(u32 crc, unsigned char const *data, size_t length) +{ + while (length--) + crc = + crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8); + + return crc; +} diff --git a/lib/crc32c.h b/lib/crc32c.h new file mode 100644 index 0000000..72f1512 --- /dev/null +++ b/lib/crc32c.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2007 Red Hat. 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. + */ + +#ifndef __CRC32C__ +#define __CRC32C__ + +#include "kerncompat.h" + +u32 crc32c_le(u32 seed, unsigned char const *data, size_t length); + +#define crc32c(seed, data, length) crc32c_le(seed, (unsigned char const *)data, length) +#define btrfs_crc32c crc32c +#endif diff --git a/lib/ctree.c b/lib/ctree.c new file mode 100644 index 0000000..f70e10c --- /dev/null +++ b/lib/ctree.c @@ -0,0 +1,2975 @@ +/* + * Copyright (C) 2007 Oracle. 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 "ctree.h" +#include "disk-io.h" +#include "transaction.h" +#include "print-tree.h" + +static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, int level); +static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *ins_key, + struct btrfs_path *path, int data_size, int extend); +static int push_node_left(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *dst, + struct extent_buffer *src, int empty); +static int balance_node_right(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *dst_buf, + struct extent_buffer *src_buf); +static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct btrfs_path *path, int level, int slot); + +inline void btrfs_init_path(struct btrfs_path *p) +{ + memset(p, 0, sizeof(*p)); +} + +struct btrfs_path *btrfs_alloc_path(void) +{ + struct btrfs_path *path; + path = kmalloc(sizeof(struct btrfs_path), GFP_NOFS); + if (path) { + btrfs_init_path(path); + path->reada = 0; + } + return path; +} + +void btrfs_free_path(struct btrfs_path *p) +{ + btrfs_release_path(NULL, p); + kfree(p); +} + +void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p) +{ + int i; + for (i = 0; i < BTRFS_MAX_LEVEL; i++) { + if (!p->nodes[i]) + continue; + free_extent_buffer(p->nodes[i]); + } + memset(p, 0, sizeof(*p)); +} + +static void add_root_to_dirty_list(struct btrfs_root *root) +{ + if (root->track_dirty && list_empty(&root->dirty_list)) { + list_add(&root->dirty_list, + &root->fs_info->dirty_cowonly_roots); + } +} + +int btrfs_copy_root(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf, + struct extent_buffer **cow_ret, u64 new_root_objectid) +{ + struct extent_buffer *cow; + int ret = 0; + int level; + struct btrfs_root *new_root; + struct btrfs_disk_key disk_key; + + new_root = kmalloc(sizeof(*new_root), GFP_NOFS); + if (!new_root) + return -ENOMEM; + + memcpy(new_root, root, sizeof(*new_root)); + new_root->root_key.objectid = new_root_objectid; + + WARN_ON(root->ref_cows && trans->transid != + root->fs_info->running_transaction->transid); + WARN_ON(root->ref_cows && trans->transid != root->last_trans); + + level = btrfs_header_level(buf); + if (level == 0) + btrfs_item_key(buf, &disk_key, 0); + else + btrfs_node_key(buf, &disk_key, 0); + cow = btrfs_alloc_free_block(trans, new_root, buf->len, + new_root_objectid, &disk_key, + level, buf->start, 0); + if (IS_ERR(cow)) { + kfree(new_root); + return PTR_ERR(cow); + } + + copy_extent_buffer(cow, buf, 0, 0, cow->len); + btrfs_set_header_bytenr(cow, cow->start); + btrfs_set_header_generation(cow, trans->transid); + btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV); + btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN | + BTRFS_HEADER_FLAG_RELOC); + if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID) + btrfs_set_header_flag(cow, BTRFS_HEADER_FLAG_RELOC); + else + btrfs_set_header_owner(cow, new_root_objectid); + + write_extent_buffer(cow, root->fs_info->fsid, + (unsigned long)btrfs_header_fsid(cow), + BTRFS_FSID_SIZE); + + WARN_ON(btrfs_header_generation(buf) > trans->transid); + ret = btrfs_inc_ref(trans, new_root, cow, 0); + kfree(new_root); + + if (ret) + return ret; + + btrfs_mark_buffer_dirty(cow); + *cow_ret = cow; + return 0; +} + +/* + * check if the tree block can be shared by multiple trees + */ +int btrfs_block_can_be_shared(struct btrfs_root *root, + struct extent_buffer *buf) +{ + /* + * Tree blocks not in refernece counted trees and tree roots + * are never shared. If a block was allocated after the last + * snapshot and the block was not allocated by tree relocation, + * we know the block is not shared. + */ + if (root->ref_cows && + buf != root->node && buf != root->commit_root && + (btrfs_header_generation(buf) <= + btrfs_root_last_snapshot(&root->root_item) || + btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC))) + return 1; +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + if (root->ref_cows && + btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) + return 1; +#endif + return 0; +} + +static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf, + struct extent_buffer *cow) +{ + u64 refs; + u64 owner; + u64 flags; + u64 new_flags = 0; + int ret; + + /* + * Backrefs update rules: + * + * Always use full backrefs for extent pointers in tree block + * allocated by tree relocation. + * + * If a shared tree block is no longer referenced by its owner + * tree (btrfs_header_owner(buf) == root->root_key.objectid), + * use full backrefs for extent pointers in tree block. + * + * If a tree block is been relocating + * (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID), + * use full backrefs for extent pointers in tree block. + * The reason for this is some operations (such as drop tree) + * are only allowed for blocks use full backrefs. + */ + + if (btrfs_block_can_be_shared(root, buf)) { + ret = btrfs_lookup_extent_info(trans, root, buf->start, + buf->len, &refs, &flags); + BUG_ON(ret); + BUG_ON(refs == 0); + } else { + refs = 1; + if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID || + btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) + flags = BTRFS_BLOCK_FLAG_FULL_BACKREF; + else + flags = 0; + } + + owner = btrfs_header_owner(buf); + BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) && + owner == BTRFS_TREE_RELOC_OBJECTID); + + if (refs > 1) { + if ((owner == root->root_key.objectid || + root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) && + !(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) { + ret = btrfs_inc_ref(trans, root, buf, 1); + BUG_ON(ret); + + if (root->root_key.objectid == + BTRFS_TREE_RELOC_OBJECTID) { + ret = btrfs_dec_ref(trans, root, buf, 0); + BUG_ON(ret); + ret = btrfs_inc_ref(trans, root, cow, 1); + BUG_ON(ret); + } + new_flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; + } else { + + if (root->root_key.objectid == + BTRFS_TREE_RELOC_OBJECTID) + ret = btrfs_inc_ref(trans, root, cow, 1); + else + ret = btrfs_inc_ref(trans, root, cow, 0); + BUG_ON(ret); + } + if (new_flags != 0) { + ret = btrfs_set_block_flags(trans, root, buf->start, + buf->len, new_flags); + BUG_ON(ret); + } + } else { + if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { + if (root->root_key.objectid == + BTRFS_TREE_RELOC_OBJECTID) + ret = btrfs_inc_ref(trans, root, cow, 1); + else + ret = btrfs_inc_ref(trans, root, cow, 0); + BUG_ON(ret); + ret = btrfs_dec_ref(trans, root, buf, 1); + BUG_ON(ret); + } + clean_tree_block(trans, root, buf); + } + return 0; +} + +int __btrfs_cow_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf, + struct extent_buffer *parent, int parent_slot, + struct extent_buffer **cow_ret, + u64 search_start, u64 empty_size) +{ + u64 generation; + struct extent_buffer *cow; + struct btrfs_disk_key disk_key; + int level; + + WARN_ON(root->ref_cows && trans->transid != + root->fs_info->running_transaction->transid); + WARN_ON(root->ref_cows && trans->transid != root->last_trans); + + level = btrfs_header_level(buf); + generation = btrfs_header_generation(buf); + + if (level == 0) + btrfs_item_key(buf, &disk_key, 0); + else + btrfs_node_key(buf, &disk_key, 0); + + cow = btrfs_alloc_free_block(trans, root, buf->len, + root->root_key.objectid, &disk_key, + level, search_start, empty_size); + if (IS_ERR(cow)) + return PTR_ERR(cow); + + copy_extent_buffer(cow, buf, 0, 0, cow->len); + btrfs_set_header_bytenr(cow, cow->start); + btrfs_set_header_generation(cow, trans->transid); + btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV); + btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN | + BTRFS_HEADER_FLAG_RELOC); + if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) + btrfs_set_header_flag(cow, BTRFS_HEADER_FLAG_RELOC); + else + btrfs_set_header_owner(cow, root->root_key.objectid); + + write_extent_buffer(cow, root->fs_info->fsid, + (unsigned long)btrfs_header_fsid(cow), + BTRFS_FSID_SIZE); + + WARN_ON(btrfs_header_generation(buf) > trans->transid); + + update_ref_for_cow(trans, root, buf, cow); + + if (buf == root->node) { + root->node = cow; + extent_buffer_get(cow); + + btrfs_free_extent(trans, root, buf->start, buf->len, + 0, root->root_key.objectid, level, 0); + free_extent_buffer(buf); + add_root_to_dirty_list(root); + } else { + btrfs_set_node_blockptr(parent, parent_slot, + cow->start); + WARN_ON(trans->transid == 0); + btrfs_set_node_ptr_generation(parent, parent_slot, + trans->transid); + btrfs_mark_buffer_dirty(parent); + WARN_ON(btrfs_header_generation(parent) != trans->transid); + + btrfs_free_extent(trans, root, buf->start, buf->len, + 0, root->root_key.objectid, level, 1); + } + free_extent_buffer(buf); + btrfs_mark_buffer_dirty(cow); + *cow_ret = cow; + return 0; +} + +static inline int should_cow_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf) +{ + if (btrfs_header_generation(buf) == trans->transid && + !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN) && + !(root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID && + btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC))) + return 0; + return 1; +} + +int btrfs_cow_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *buf, + struct extent_buffer *parent, int parent_slot, + struct extent_buffer **cow_ret) +{ + u64 search_start; + int ret; + /* + if (trans->transaction != root->fs_info->running_transaction) { + printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid, + root->fs_info->running_transaction->transid); + WARN_ON(1); + } + */ + if (trans->transid != root->fs_info->generation) { + printk(KERN_CRIT "trans %llu running %llu\n", + (unsigned long long)trans->transid, + (unsigned long long)root->fs_info->generation); + WARN_ON(1); + } + if (!should_cow_block(trans, root, buf)) { + *cow_ret = buf; + return 0; + } + + search_start = buf->start & ~((u64)(1024 * 1024 * 1024) - 1); + ret = __btrfs_cow_block(trans, root, buf, parent, + parent_slot, cow_ret, search_start, 0); + return ret; +} + +/* +static int close_blocks(u64 blocknr, u64 other, u32 blocksize) +{ + if (blocknr < other && other - (blocknr + blocksize) < 32768) + return 1; + if (blocknr > other && blocknr - (other + blocksize) < 32768) + return 1; + return 0; +} +*/ + +/* + * compare two keys in a memcmp fashion + */ +int btrfs_comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2) +{ + struct btrfs_key k1; + + btrfs_disk_key_to_cpu(&k1, disk); + + if (k1.objectid > k2->objectid) + return 1; + if (k1.objectid < k2->objectid) + return -1; + if (k1.type > k2->type) + return 1; + if (k1.type < k2->type) + return -1; + if (k1.offset > k2->offset) + return 1; + if (k1.offset < k2->offset) + return -1; + return 0; +} + + +#if 0 +int btrfs_realloc_node(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *parent, + int start_slot, int cache_only, u64 *last_ret, + struct btrfs_key *progress) +{ + struct extent_buffer *cur; + struct extent_buffer *tmp; + u64 blocknr; + u64 gen; + u64 search_start = *last_ret; + u64 last_block = 0; + u64 other; + u32 parent_nritems; + int end_slot; + int i; + int err = 0; + int parent_level; + int uptodate; + u32 blocksize; + int progress_passed = 0; + struct btrfs_disk_key disk_key; + + parent_level = btrfs_header_level(parent); + if (cache_only && parent_level != 1) + return 0; + + if (trans->transaction != root->fs_info->running_transaction) { + printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid, + root->fs_info->running_transaction->transid); + WARN_ON(1); + } + if (trans->transid != root->fs_info->generation) { + printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid, + root->fs_info->generation); + WARN_ON(1); + } + + parent_nritems = btrfs_header_nritems(parent); + blocksize = btrfs_level_size(root, parent_level - 1); + end_slot = parent_nritems; + + if (parent_nritems == 1) + return 0; + + for (i = start_slot; i < end_slot; i++) { + int close = 1; + + if (!parent->map_token) { + map_extent_buffer(parent, + btrfs_node_key_ptr_offset(i), + sizeof(struct btrfs_key_ptr), + &parent->map_token, &parent->kaddr, + &parent->map_start, &parent->map_len, + KM_USER1); + } + btrfs_node_key(parent, &disk_key, i); + if (!progress_passed && comp_keys(&disk_key, progress) < 0) + continue; + + progress_passed = 1; + blocknr = btrfs_node_blockptr(parent, i); + gen = btrfs_node_ptr_generation(parent, i); + if (last_block == 0) + last_block = blocknr; + + if (i > 0) { + other = btrfs_node_blockptr(parent, i - 1); + close = close_blocks(blocknr, other, blocksize); + } + if (close && i < end_slot - 2) { + other = btrfs_node_blockptr(parent, i + 1); + close = close_blocks(blocknr, other, blocksize); + } + if (close) { + last_block = blocknr; + continue; + } + if (parent->map_token) { + unmap_extent_buffer(parent, parent->map_token, + KM_USER1); + parent->map_token = NULL; + } + + cur = btrfs_find_tree_block(root, blocknr, blocksize); + if (cur) + uptodate = btrfs_buffer_uptodate(cur, gen); + else + uptodate = 0; + if (!cur || !uptodate) { + if (cache_only) { + free_extent_buffer(cur); + continue; + } + if (!cur) { + cur = read_tree_block(root, blocknr, + blocksize, gen); + } else if (!uptodate) { + btrfs_read_buffer(cur, gen); + } + } + if (search_start == 0) + search_start = last_block; + + err = __btrfs_cow_block(trans, root, cur, parent, i, + &tmp, search_start, + min(16 * blocksize, + (end_slot - i) * blocksize)); + if (err) { + free_extent_buffer(cur); + break; + } + search_start = tmp->start; + last_block = tmp->start; + *last_ret = search_start; + if (parent_level == 1) + btrfs_clear_buffer_defrag(tmp); + free_extent_buffer(tmp); + } + if (parent->map_token) { + unmap_extent_buffer(parent, parent->map_token, + KM_USER1); + parent->map_token = NULL; + } + return err; +} +#endif + +/* + * The leaf data grows from end-to-front in the node. + * this returns the address of the start of the last item, + * which is the stop of the leaf data stack + */ +static inline unsigned int leaf_data_end(struct btrfs_root *root, + struct extent_buffer *leaf) +{ + u32 nr = btrfs_header_nritems(leaf); + if (nr == 0) + return BTRFS_LEAF_DATA_SIZE(root); + return btrfs_item_offset_nr(leaf, nr - 1); +} + +static int check_node(struct btrfs_root *root, struct btrfs_path *path, + int level) +{ + struct extent_buffer *parent = NULL; + struct extent_buffer *node = path->nodes[level]; + struct btrfs_disk_key parent_key; + struct btrfs_disk_key node_key; + int parent_slot; + int slot; + struct btrfs_key cpukey; + u32 nritems = btrfs_header_nritems(node); + + if (path->nodes[level + 1]) + parent = path->nodes[level + 1]; + + slot = path->slots[level]; + BUG_ON(nritems == 0); + if (parent) { + parent_slot = path->slots[level + 1]; + btrfs_node_key(parent, &parent_key, parent_slot); + btrfs_node_key(node, &node_key, 0); + BUG_ON(memcmp(&parent_key, &node_key, + sizeof(struct btrfs_disk_key))); + BUG_ON(btrfs_node_blockptr(parent, parent_slot) != + btrfs_header_bytenr(node)); + } + BUG_ON(nritems > BTRFS_NODEPTRS_PER_BLOCK(root)); + if (slot != 0) { + btrfs_node_key_to_cpu(node, &cpukey, slot - 1); + btrfs_node_key(node, &node_key, slot); + BUG_ON(btrfs_comp_keys(&node_key, &cpukey) <= 0); + } + if (slot < nritems - 1) { + btrfs_node_key_to_cpu(node, &cpukey, slot + 1); + btrfs_node_key(node, &node_key, slot); + BUG_ON(btrfs_comp_keys(&node_key, &cpukey) >= 0); + } + return 0; +} + +static int check_leaf(struct btrfs_root *root, struct btrfs_path *path, + int level) +{ + struct extent_buffer *leaf = path->nodes[level]; + struct extent_buffer *parent = NULL; + int parent_slot; + struct btrfs_key cpukey; + struct btrfs_disk_key parent_key; + struct btrfs_disk_key leaf_key; + int slot = path->slots[0]; + + u32 nritems = btrfs_header_nritems(leaf); + + if (path->nodes[level + 1]) + parent = path->nodes[level + 1]; + + if (nritems == 0) + return 0; + + if (parent) { + parent_slot = path->slots[level + 1]; + btrfs_node_key(parent, &parent_key, parent_slot); + btrfs_item_key(leaf, &leaf_key, 0); + + BUG_ON(memcmp(&parent_key, &leaf_key, + sizeof(struct btrfs_disk_key))); + BUG_ON(btrfs_node_blockptr(parent, parent_slot) != + btrfs_header_bytenr(leaf)); + } +#if 0 + for (i = 0; nritems > 1 && i < nritems - 2; i++) { + btrfs_item_key_to_cpu(leaf, &cpukey, i + 1); + btrfs_item_key(leaf, &leaf_key, i); + if (comp_keys(&leaf_key, &cpukey) >= 0) { + btrfs_print_leaf(root, leaf); + printk("slot %d offset bad key\n", i); + BUG_ON(1); + } + if (btrfs_item_offset_nr(leaf, i) != + btrfs_item_end_nr(leaf, i + 1)) { + btrfs_print_leaf(root, leaf); + printk("slot %d offset bad\n", i); + BUG_ON(1); + } + if (i == 0) { + if (btrfs_item_offset_nr(leaf, i) + + btrfs_item_size_nr(leaf, i) != + BTRFS_LEAF_DATA_SIZE(root)) { + btrfs_print_leaf(root, leaf); + printk("slot %d first offset bad\n", i); + BUG_ON(1); + } + } + } + if (nritems > 0) { + if (btrfs_item_size_nr(leaf, nritems - 1) > 4096) { + btrfs_print_leaf(root, leaf); + printk("slot %d bad size \n", nritems - 1); + BUG_ON(1); + } + } +#endif + if (slot != 0 && slot < nritems - 1) { + btrfs_item_key(leaf, &leaf_key, slot); + btrfs_item_key_to_cpu(leaf, &cpukey, slot - 1); + if (btrfs_comp_keys(&leaf_key, &cpukey) <= 0) { + btrfs_print_leaf(root, leaf); + printk("slot %d offset bad key\n", slot); + BUG_ON(1); + } + if (btrfs_item_offset_nr(leaf, slot - 1) != + btrfs_item_end_nr(leaf, slot)) { + btrfs_print_leaf(root, leaf); + printk("slot %d offset bad\n", slot); + BUG_ON(1); + } + } + if (slot < nritems - 1) { + btrfs_item_key(leaf, &leaf_key, slot); + btrfs_item_key_to_cpu(leaf, &cpukey, slot + 1); + BUG_ON(btrfs_comp_keys(&leaf_key, &cpukey) >= 0); + if (btrfs_item_offset_nr(leaf, slot) != + btrfs_item_end_nr(leaf, slot + 1)) { + btrfs_print_leaf(root, leaf); + printk("slot %d offset bad\n", slot); + BUG_ON(1); + } + } + BUG_ON(btrfs_item_offset_nr(leaf, 0) + + btrfs_item_size_nr(leaf, 0) != BTRFS_LEAF_DATA_SIZE(root)); + return 0; +} + +static int noinline check_block(struct btrfs_root *root, + struct btrfs_path *path, int level) +{ + return 0; +#if 0 + struct extent_buffer *buf = path->nodes[level]; + + if (memcmp_extent_buffer(buf, root->fs_info->fsid, + (unsigned long)btrfs_header_fsid(buf), + BTRFS_FSID_SIZE)) { + printk("warning bad block %Lu\n", buf->start); + return 1; + } +#endif + if (level == 0) + return check_leaf(root, path, level); + return check_node(root, path, level); +} + +/* + * search for key in the extent_buffer. The items start at offset p, + * and they are item_size apart. There are 'max' items in p. + * + * the slot in the array is returned via slot, and it points to + * the place where you would insert key if it is not found in + * the array. + * + * slot may point to max if the key is bigger than all of the keys + */ +static int generic_bin_search(struct extent_buffer *eb, unsigned long p, + int item_size, struct btrfs_key *key, + int max, int *slot) +{ + int low = 0; + int high = max; + int mid; + int ret; + unsigned long offset; + struct btrfs_disk_key *tmp; + + while(low < high) { + mid = (low + high) / 2; + offset = p + mid * item_size; + + tmp = (struct btrfs_disk_key *)(eb->data + offset); + ret = btrfs_comp_keys(tmp, key); + + if (ret < 0) + low = mid + 1; + else if (ret > 0) + high = mid; + else { + *slot = mid; + return 0; + } + } + *slot = low; + return 1; +} + +/* + * simple bin_search frontend that does the right thing for + * leaves vs nodes + */ +static int bin_search(struct extent_buffer *eb, struct btrfs_key *key, + int level, int *slot) +{ + if (level == 0) { + return generic_bin_search(eb, + offsetof(struct btrfs_leaf, items), + sizeof(struct btrfs_item), + key, btrfs_header_nritems(eb), + slot); + } else { + return generic_bin_search(eb, + offsetof(struct btrfs_node, ptrs), + sizeof(struct btrfs_key_ptr), + key, btrfs_header_nritems(eb), + slot); + } + return -1; +} + +static struct extent_buffer *read_node_slot(struct btrfs_root *root, + struct extent_buffer *parent, int slot) +{ + int level = btrfs_header_level(parent); + if (slot < 0) + return NULL; + if (slot >= btrfs_header_nritems(parent)) + return NULL; + + BUG_ON(level == 0); + + return read_tree_block(root, btrfs_node_blockptr(parent, slot), + btrfs_level_size(root, level - 1), + btrfs_node_ptr_generation(parent, slot)); +} + +static int balance_level(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, int level) +{ + struct extent_buffer *right = NULL; + struct extent_buffer *mid; + struct extent_buffer *left = NULL; + struct extent_buffer *parent = NULL; + int ret = 0; + int wret; + int pslot; + int orig_slot = path->slots[level]; + int err_on_enospc = 0; + u64 orig_ptr; + + if (level == 0) + return 0; + + mid = path->nodes[level]; + WARN_ON(btrfs_header_generation(mid) != trans->transid); + + orig_ptr = btrfs_node_blockptr(mid, orig_slot); + + if (level < BTRFS_MAX_LEVEL - 1) + parent = path->nodes[level + 1]; + pslot = path->slots[level + 1]; + + /* + * deal with the case where there is only one pointer in the root + * by promoting the node below to a root + */ + if (!parent) { + struct extent_buffer *child; + + if (btrfs_header_nritems(mid) != 1) + return 0; + + /* promote the child to a root */ + child = read_node_slot(root, mid, 0); + BUG_ON(!child); + ret = btrfs_cow_block(trans, root, child, mid, 0, &child); + BUG_ON(ret); + + root->node = child; + add_root_to_dirty_list(root); + path->nodes[level] = NULL; + clean_tree_block(trans, root, mid); + wait_on_tree_block_writeback(root, mid); + /* once for the path */ + free_extent_buffer(mid); + + ret = btrfs_free_extent(trans, root, mid->start, mid->len, + 0, root->root_key.objectid, + level, 1); + /* once for the root ptr */ + free_extent_buffer(mid); + return ret; + } + if (btrfs_header_nritems(mid) > + BTRFS_NODEPTRS_PER_BLOCK(root) / 4) + return 0; + + if (btrfs_header_nritems(mid) < 2) + err_on_enospc = 1; + + left = read_node_slot(root, parent, pslot - 1); + if (left) { + wret = btrfs_cow_block(trans, root, left, + parent, pslot - 1, &left); + if (wret) { + ret = wret; + goto enospc; + } + } + right = read_node_slot(root, parent, pslot + 1); + if (right) { + wret = btrfs_cow_block(trans, root, right, + parent, pslot + 1, &right); + if (wret) { + ret = wret; + goto enospc; + } + } + + /* first, try to make some room in the middle buffer */ + if (left) { + orig_slot += btrfs_header_nritems(left); + wret = push_node_left(trans, root, left, mid, 1); + if (wret < 0) + ret = wret; + if (btrfs_header_nritems(mid) < 2) + err_on_enospc = 1; + } + + /* + * then try to empty the right most buffer into the middle + */ + if (right) { + wret = push_node_left(trans, root, mid, right, 1); + if (wret < 0 && wret != -ENOSPC) + ret = wret; + if (btrfs_header_nritems(right) == 0) { + u64 bytenr = right->start; + u32 blocksize = right->len; + + clean_tree_block(trans, root, right); + wait_on_tree_block_writeback(root, right); + free_extent_buffer(right); + right = NULL; + wret = del_ptr(trans, root, path, level + 1, pslot + + 1); + if (wret) + ret = wret; + wret = btrfs_free_extent(trans, root, bytenr, + blocksize, 0, + root->root_key.objectid, + level, 0); + if (wret) + ret = wret; + } else { + struct btrfs_disk_key right_key; + btrfs_node_key(right, &right_key, 0); + btrfs_set_node_key(parent, &right_key, pslot + 1); + btrfs_mark_buffer_dirty(parent); + } + } + if (btrfs_header_nritems(mid) == 1) { + /* + * we're not allowed to leave a node with one item in the + * tree during a delete. A deletion from lower in the tree + * could try to delete the only pointer in this node. + * So, pull some keys from the left. + * There has to be a left pointer at this point because + * otherwise we would have pulled some pointers from the + * right + */ + BUG_ON(!left); + wret = balance_node_right(trans, root, mid, left); + if (wret < 0) { + ret = wret; + goto enospc; + } + if (wret == 1) { + wret = push_node_left(trans, root, left, mid, 1); + if (wret < 0) + ret = wret; + } + BUG_ON(wret == 1); + } + if (btrfs_header_nritems(mid) == 0) { + /* we've managed to empty the middle node, drop it */ + u64 bytenr = mid->start; + u32 blocksize = mid->len; + clean_tree_block(trans, root, mid); + wait_on_tree_block_writeback(root, mid); + free_extent_buffer(mid); + mid = NULL; + wret = del_ptr(trans, root, path, level + 1, pslot); + if (wret) + ret = wret; + wret = btrfs_free_extent(trans, root, bytenr, blocksize, + 0, root->root_key.objectid, + level, 0); + if (wret) + ret = wret; + } else { + /* update the parent key to reflect our changes */ + struct btrfs_disk_key mid_key; + btrfs_node_key(mid, &mid_key, 0); + btrfs_set_node_key(parent, &mid_key, pslot); + btrfs_mark_buffer_dirty(parent); + } + + /* update the path */ + if (left) { + if (btrfs_header_nritems(left) > orig_slot) { + extent_buffer_get(left); + path->nodes[level] = left; + path->slots[level + 1] -= 1; + path->slots[level] = orig_slot; + if (mid) + free_extent_buffer(mid); + } else { + orig_slot -= btrfs_header_nritems(left); + path->slots[level] = orig_slot; + } + } + /* double check we haven't messed things up */ + check_block(root, path, level); + if (orig_ptr != + btrfs_node_blockptr(path->nodes[level], path->slots[level])) + BUG(); +enospc: + if (right) + free_extent_buffer(right); + if (left) + free_extent_buffer(left); + return ret; +} + +/* returns zero if the push worked, non-zero otherwise */ +static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, int level) +{ + struct extent_buffer *right = NULL; + struct extent_buffer *mid; + struct extent_buffer *left = NULL; + struct extent_buffer *parent = NULL; + int ret = 0; + int wret; + int pslot; + int orig_slot = path->slots[level]; + u64 orig_ptr; + + if (level == 0) + return 1; + + mid = path->nodes[level]; + WARN_ON(btrfs_header_generation(mid) != trans->transid); + orig_ptr = btrfs_node_blockptr(mid, orig_slot); + + if (level < BTRFS_MAX_LEVEL - 1) + parent = path->nodes[level + 1]; + pslot = path->slots[level + 1]; + + if (!parent) + return 1; + + left = read_node_slot(root, parent, pslot - 1); + + /* first, try to make some room in the middle buffer */ + if (left) { + u32 left_nr; + left_nr = btrfs_header_nritems(left); + if (left_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) { + wret = 1; + } else { + ret = btrfs_cow_block(trans, root, left, parent, + pslot - 1, &left); + if (ret) + wret = 1; + else { + wret = push_node_left(trans, root, + left, mid, 0); + } + } + if (wret < 0) + ret = wret; + if (wret == 0) { + struct btrfs_disk_key disk_key; + orig_slot += left_nr; + btrfs_node_key(mid, &disk_key, 0); + btrfs_set_node_key(parent, &disk_key, pslot); + btrfs_mark_buffer_dirty(parent); + if (btrfs_header_nritems(left) > orig_slot) { + path->nodes[level] = left; + path->slots[level + 1] -= 1; + path->slots[level] = orig_slot; + free_extent_buffer(mid); + } else { + orig_slot -= + btrfs_header_nritems(left); + path->slots[level] = orig_slot; + free_extent_buffer(left); + } + return 0; + } + free_extent_buffer(left); + } + right= read_node_slot(root, parent, pslot + 1); + + /* + * then try to empty the right most buffer into the middle + */ + if (right) { + u32 right_nr; + right_nr = btrfs_header_nritems(right); + if (right_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) { + wret = 1; + } else { + ret = btrfs_cow_block(trans, root, right, + parent, pslot + 1, + &right); + if (ret) + wret = 1; + else { + wret = balance_node_right(trans, root, + right, mid); + } + } + if (wret < 0) + ret = wret; + if (wret == 0) { + struct btrfs_disk_key disk_key; + + btrfs_node_key(right, &disk_key, 0); + btrfs_set_node_key(parent, &disk_key, pslot + 1); + btrfs_mark_buffer_dirty(parent); + + if (btrfs_header_nritems(mid) <= orig_slot) { + path->nodes[level] = right; + path->slots[level + 1] += 1; + path->slots[level] = orig_slot - + btrfs_header_nritems(mid); + free_extent_buffer(mid); + } else { + free_extent_buffer(right); + } + return 0; + } + free_extent_buffer(right); + } + return 1; +} + +/* + * readahead one full node of leaves + */ +static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path, + int level, int slot, u64 objectid) +{ + struct extent_buffer *node; + struct btrfs_disk_key disk_key; + u32 nritems; + u64 search; + u64 lowest_read; + u64 highest_read; + u64 nread = 0; + int direction = path->reada; + struct extent_buffer *eb; + u32 nr; + u32 blocksize; + u32 nscan = 0; + + if (level != 1) + return; + + if (!path->nodes[level]) + return; + + node = path->nodes[level]; + search = btrfs_node_blockptr(node, slot); + blocksize = btrfs_level_size(root, level - 1); + eb = btrfs_find_tree_block(root, search, blocksize); + if (eb) { + free_extent_buffer(eb); + return; + } + + highest_read = search; + lowest_read = search; + + nritems = btrfs_header_nritems(node); + nr = slot; + while(1) { + if (direction < 0) { + if (nr == 0) + break; + nr--; + } else if (direction > 0) { + nr++; + if (nr >= nritems) + break; + } + if (path->reada < 0 && objectid) { + btrfs_node_key(node, &disk_key, nr); + if (btrfs_disk_key_objectid(&disk_key) != objectid) + break; + } + search = btrfs_node_blockptr(node, nr); + if ((search >= lowest_read && search <= highest_read) || + (search < lowest_read && lowest_read - search <= 32768) || + (search > highest_read && search - highest_read <= 32768)) { + readahead_tree_block(root, search, blocksize, + btrfs_node_ptr_generation(node, nr)); + nread += blocksize; + } + nscan++; + if (path->reada < 2 && (nread > (256 * 1024) || nscan > 32)) + break; + if(nread > (1024 * 1024) || nscan > 128) + break; + + if (search < lowest_read) + lowest_read = search; + if (search > highest_read) + highest_read = search; + } +} + +/* + * look for key in the tree. path is filled in with nodes along the way + * if key is found, we return zero and you can find the item in the leaf + * level of the path (level 0) + * + * If the key isn't found, the path points to the slot where it should + * be inserted, and 1 is returned. If there are other errors during the + * search a negative error number is returned. + * + * if ins_len > 0, nodes and leaves will be split as we walk down the + * tree. if ins_len < 0, nodes will be merged as we walk down the tree (if + * possible) + */ +int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *key, struct btrfs_path *p, int + ins_len, int cow) +{ + struct extent_buffer *b; + int slot; + int ret; + int level; + int should_reada = p->reada; + u8 lowest_level = 0; + + lowest_level = p->lowest_level; + WARN_ON(lowest_level && ins_len); + WARN_ON(p->nodes[0] != NULL); + /* + WARN_ON(!mutex_is_locked(&root->fs_info->fs_mutex)); + */ +again: + b = root->node; + extent_buffer_get(b); + while (b) { + level = btrfs_header_level(b); + if (cow) { + int wret; + wret = btrfs_cow_block(trans, root, b, + p->nodes[level + 1], + p->slots[level + 1], + &b); + if (wret) { + free_extent_buffer(b); + return wret; + } + } + BUG_ON(!cow && ins_len); + if (level != btrfs_header_level(b)) + WARN_ON(1); + level = btrfs_header_level(b); + p->nodes[level] = b; + ret = check_block(root, p, level); + if (ret) + return -1; + ret = bin_search(b, key, level, &slot); + if (level != 0) { + if (ret && slot > 0) + slot -= 1; + p->slots[level] = slot; + if ((p->search_for_split || ins_len > 0) && + btrfs_header_nritems(b) >= + BTRFS_NODEPTRS_PER_BLOCK(root) - 3) { + int sret = split_node(trans, root, p, level); + BUG_ON(sret > 0); + if (sret) + return sret; + b = p->nodes[level]; + slot = p->slots[level]; + } else if (ins_len < 0) { + int sret = balance_level(trans, root, p, + level); + if (sret) + return sret; + b = p->nodes[level]; + if (!b) { + btrfs_release_path(NULL, p); + goto again; + } + slot = p->slots[level]; + BUG_ON(btrfs_header_nritems(b) == 1); + } + /* this is only true while dropping a snapshot */ + if (level == lowest_level) + break; + + if (should_reada) + reada_for_search(root, p, level, slot, + key->objectid); + + b = read_node_slot(root, b, slot); + } else { + p->slots[level] = slot; + if (ins_len > 0 && + ins_len > btrfs_leaf_free_space(root, b)) { + int sret = split_leaf(trans, root, key, + p, ins_len, ret == 0); + BUG_ON(sret > 0); + if (sret) + return sret; + } + return ret; + } + } + return 1; +} + +/* + * adjust the pointers going up the tree, starting at level + * making sure the right key of each node is points to 'key'. + * This is used after shifting pointers to the left, so it stops + * fixing up pointers when a given leaf/node is not in slot 0 of the + * higher levels + * + * If this fails to write a tree block, it returns -1, but continues + * fixing up the blocks in ram so the tree is consistent. + */ +static int fixup_low_keys(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + struct btrfs_disk_key *key, int level) +{ + int i; + int ret = 0; + struct extent_buffer *t; + + for (i = level; i < BTRFS_MAX_LEVEL; i++) { + int tslot = path->slots[i]; + if (!path->nodes[i]) + break; + t = path->nodes[i]; + btrfs_set_node_key(t, key, tslot); + btrfs_mark_buffer_dirty(path->nodes[i]); + if (tslot != 0) + break; + } + return ret; +} + +/* + * update item key. + * + * This function isn't completely safe. It's the caller's responsibility + * that the new key won't break the order + */ +int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + struct btrfs_key *new_key) +{ + struct btrfs_disk_key disk_key; + struct extent_buffer *eb; + int slot; + + eb = path->nodes[0]; + slot = path->slots[0]; + if (slot > 0) { + btrfs_item_key(eb, &disk_key, slot - 1); + if (btrfs_comp_keys(&disk_key, new_key) >= 0) + return -1; + } + if (slot < btrfs_header_nritems(eb) - 1) { + btrfs_item_key(eb, &disk_key, slot + 1); + if (btrfs_comp_keys(&disk_key, new_key) <= 0) + return -1; + } + + btrfs_cpu_key_to_disk(&disk_key, new_key); + btrfs_set_item_key(eb, &disk_key, slot); + btrfs_mark_buffer_dirty(eb); + if (slot == 0) + fixup_low_keys(trans, root, path, &disk_key, 1); + return 0; +} + +/* + * try to push data from one node into the next node left in the + * tree. + * + * returns 0 if some ptrs were pushed left, < 0 if there was some horrible + * error, and > 0 if there was no room in the left hand block. + */ +static int push_node_left(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *dst, + struct extent_buffer *src, int empty) +{ + int push_items = 0; + int src_nritems; + int dst_nritems; + int ret = 0; + + src_nritems = btrfs_header_nritems(src); + dst_nritems = btrfs_header_nritems(dst); + push_items = BTRFS_NODEPTRS_PER_BLOCK(root) - dst_nritems; + WARN_ON(btrfs_header_generation(src) != trans->transid); + WARN_ON(btrfs_header_generation(dst) != trans->transid); + + if (!empty && src_nritems <= 8) + return 1; + + if (push_items <= 0) { + return 1; + } + + if (empty) { + push_items = min(src_nritems, push_items); + if (push_items < src_nritems) { + /* leave at least 8 pointers in the node if + * we aren't going to empty it + */ + if (src_nritems - push_items < 8) { + if (push_items <= 8) + return 1; + push_items -= 8; + } + } + } else + push_items = min(src_nritems - 8, push_items); + + copy_extent_buffer(dst, src, + btrfs_node_key_ptr_offset(dst_nritems), + btrfs_node_key_ptr_offset(0), + push_items * sizeof(struct btrfs_key_ptr)); + + if (push_items < src_nritems) { + memmove_extent_buffer(src, btrfs_node_key_ptr_offset(0), + btrfs_node_key_ptr_offset(push_items), + (src_nritems - push_items) * + sizeof(struct btrfs_key_ptr)); + } + btrfs_set_header_nritems(src, src_nritems - push_items); + btrfs_set_header_nritems(dst, dst_nritems + push_items); + btrfs_mark_buffer_dirty(src); + btrfs_mark_buffer_dirty(dst); + + return ret; +} + +/* + * try to push data from one node into the next node right in the + * tree. + * + * returns 0 if some ptrs were pushed, < 0 if there was some horrible + * error, and > 0 if there was no room in the right hand block. + * + * this will only push up to 1/2 the contents of the left node over + */ +static int balance_node_right(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *dst, + struct extent_buffer *src) +{ + int push_items = 0; + int max_push; + int src_nritems; + int dst_nritems; + int ret = 0; + + WARN_ON(btrfs_header_generation(src) != trans->transid); + WARN_ON(btrfs_header_generation(dst) != trans->transid); + + src_nritems = btrfs_header_nritems(src); + dst_nritems = btrfs_header_nritems(dst); + push_items = BTRFS_NODEPTRS_PER_BLOCK(root) - dst_nritems; + if (push_items <= 0) { + return 1; + } + + if (src_nritems < 4) { + return 1; + } + + max_push = src_nritems / 2 + 1; + /* don't try to empty the node */ + if (max_push >= src_nritems) { + return 1; + } + + if (max_push < push_items) + push_items = max_push; + + memmove_extent_buffer(dst, btrfs_node_key_ptr_offset(push_items), + btrfs_node_key_ptr_offset(0), + (dst_nritems) * + sizeof(struct btrfs_key_ptr)); + + copy_extent_buffer(dst, src, + btrfs_node_key_ptr_offset(0), + btrfs_node_key_ptr_offset(src_nritems - push_items), + push_items * sizeof(struct btrfs_key_ptr)); + + btrfs_set_header_nritems(src, src_nritems - push_items); + btrfs_set_header_nritems(dst, dst_nritems + push_items); + + btrfs_mark_buffer_dirty(src); + btrfs_mark_buffer_dirty(dst); + + return ret; +} + +/* + * helper function to insert a new root level in the tree. + * A new node is allocated, and a single item is inserted to + * point to the existing root + * + * returns zero on success or < 0 on failure. + */ +static int noinline insert_new_root(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, int level) +{ + u64 lower_gen; + struct extent_buffer *lower; + struct extent_buffer *c; + struct extent_buffer *old; + struct btrfs_disk_key lower_key; + + BUG_ON(path->nodes[level]); + BUG_ON(path->nodes[level-1] != root->node); + + lower = path->nodes[level-1]; + if (level == 1) + btrfs_item_key(lower, &lower_key, 0); + else + btrfs_node_key(lower, &lower_key, 0); + + c = btrfs_alloc_free_block(trans, root, root->nodesize, + root->root_key.objectid, &lower_key, + level, root->node->start, 0); + + if (IS_ERR(c)) + return PTR_ERR(c); + + memset_extent_buffer(c, 0, 0, sizeof(struct btrfs_header)); + btrfs_set_header_nritems(c, 1); + btrfs_set_header_level(c, level); + btrfs_set_header_bytenr(c, c->start); + btrfs_set_header_generation(c, trans->transid); + btrfs_set_header_backref_rev(c, BTRFS_MIXED_BACKREF_REV); + btrfs_set_header_owner(c, root->root_key.objectid); + + write_extent_buffer(c, root->fs_info->fsid, + (unsigned long)btrfs_header_fsid(c), + BTRFS_FSID_SIZE); + + write_extent_buffer(c, root->fs_info->chunk_tree_uuid, + (unsigned long)btrfs_header_chunk_tree_uuid(c), + BTRFS_UUID_SIZE); + + btrfs_set_node_key(c, &lower_key, 0); + btrfs_set_node_blockptr(c, 0, lower->start); + lower_gen = btrfs_header_generation(lower); + WARN_ON(lower_gen != trans->transid); + + btrfs_set_node_ptr_generation(c, 0, lower_gen); + + btrfs_mark_buffer_dirty(c); + + old = root->node; + root->node = c; + + /* the super has an extra ref to root->node */ + free_extent_buffer(old); + + add_root_to_dirty_list(root); + extent_buffer_get(c); + path->nodes[level] = c; + path->slots[level] = 0; + return 0; +} + +/* + * worker function to insert a single pointer in a node. + * the node should have enough room for the pointer already + * + * slot and level indicate where you want the key to go, and + * blocknr is the block the key points to. + * + * returns zero on success and < 0 on any error + */ +static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, struct btrfs_disk_key + *key, u64 bytenr, int slot, int level) +{ + struct extent_buffer *lower; + int nritems; + + BUG_ON(!path->nodes[level]); + lower = path->nodes[level]; + nritems = btrfs_header_nritems(lower); + if (slot > nritems) + BUG(); + if (nritems == BTRFS_NODEPTRS_PER_BLOCK(root)) + BUG(); + if (slot != nritems) { + memmove_extent_buffer(lower, + btrfs_node_key_ptr_offset(slot + 1), + btrfs_node_key_ptr_offset(slot), + (nritems - slot) * sizeof(struct btrfs_key_ptr)); + } + btrfs_set_node_key(lower, key, slot); + btrfs_set_node_blockptr(lower, slot, bytenr); + WARN_ON(trans->transid == 0); + btrfs_set_node_ptr_generation(lower, slot, trans->transid); + btrfs_set_header_nritems(lower, nritems + 1); + btrfs_mark_buffer_dirty(lower); + return 0; +} + +/* + * split the node at the specified level in path in two. + * The path is corrected to point to the appropriate node after the split + * + * Before splitting this tries to make some room in the node by pushing + * left and right, if either one works, it returns right away. + * + * returns 0 on success and < 0 on failure + */ +static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, int level) +{ + struct extent_buffer *c; + struct extent_buffer *split; + struct btrfs_disk_key disk_key; + int mid; + int ret; + int wret; + u32 c_nritems; + + c = path->nodes[level]; + WARN_ON(btrfs_header_generation(c) != trans->transid); + if (c == root->node) { + /* trying to split the root, lets make a new one */ + ret = insert_new_root(trans, root, path, level + 1); + if (ret) + return ret; + } else { + ret = push_nodes_for_insert(trans, root, path, level); + c = path->nodes[level]; + if (!ret && btrfs_header_nritems(c) < + BTRFS_NODEPTRS_PER_BLOCK(root) - 3) + return 0; + if (ret < 0) + return ret; + } + + c_nritems = btrfs_header_nritems(c); + mid = (c_nritems + 1) / 2; + btrfs_node_key(c, &disk_key, mid); + + split = btrfs_alloc_free_block(trans, root, root->nodesize, + root->root_key.objectid, + &disk_key, level, c->start, 0); + if (IS_ERR(split)) + return PTR_ERR(split); + + memset_extent_buffer(split, 0, 0, sizeof(struct btrfs_header)); + btrfs_set_header_level(split, btrfs_header_level(c)); + btrfs_set_header_bytenr(split, split->start); + btrfs_set_header_generation(split, trans->transid); + btrfs_set_header_backref_rev(split, BTRFS_MIXED_BACKREF_REV); + btrfs_set_header_owner(split, root->root_key.objectid); + write_extent_buffer(split, root->fs_info->fsid, + (unsigned long)btrfs_header_fsid(split), + BTRFS_FSID_SIZE); + write_extent_buffer(split, root->fs_info->chunk_tree_uuid, + (unsigned long)btrfs_header_chunk_tree_uuid(split), + BTRFS_UUID_SIZE); + + + copy_extent_buffer(split, c, + btrfs_node_key_ptr_offset(0), + btrfs_node_key_ptr_offset(mid), + (c_nritems - mid) * sizeof(struct btrfs_key_ptr)); + btrfs_set_header_nritems(split, c_nritems - mid); + btrfs_set_header_nritems(c, mid); + ret = 0; + + btrfs_mark_buffer_dirty(c); + btrfs_mark_buffer_dirty(split); + + wret = insert_ptr(trans, root, path, &disk_key, split->start, + path->slots[level + 1] + 1, + level + 1); + if (wret) + ret = wret; + + if (path->slots[level] >= mid) { + path->slots[level] -= mid; + free_extent_buffer(c); + path->nodes[level] = split; + path->slots[level + 1] += 1; + } else { + free_extent_buffer(split); + } + return ret; +} + +/* + * how many bytes are required to store the items in a leaf. start + * and nr indicate which items in the leaf to check. This totals up the + * space used both by the item structs and the item data + */ +static int leaf_space_used(struct extent_buffer *l, int start, int nr) +{ + int data_len; + int nritems = btrfs_header_nritems(l); + int end = min(nritems, start + nr) - 1; + + if (!nr) + return 0; + data_len = btrfs_item_end_nr(l, start); + data_len = data_len - btrfs_item_offset_nr(l, end); + data_len += sizeof(struct btrfs_item) * nr; + WARN_ON(data_len < 0); + return data_len; +} + +/* + * The space between the end of the leaf items and + * the start of the leaf data. IOW, how much room + * the leaf has left for both items and data + */ +int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf) +{ + int nritems = btrfs_header_nritems(leaf); + int ret; + ret = BTRFS_LEAF_DATA_SIZE(root) - leaf_space_used(leaf, 0, nritems); + if (ret < 0) { + printk("leaf free space ret %d, leaf data size %lu, used %d nritems %d\n", + ret, (unsigned long) BTRFS_LEAF_DATA_SIZE(root), + leaf_space_used(leaf, 0, nritems), nritems); + } + return ret; +} + +/* + * push some data in the path leaf to the right, trying to free up at + * least data_size bytes. returns zero if the push worked, nonzero otherwise + * + * returns 1 if the push failed because the other node didn't have enough + * room, 0 if everything worked out and < 0 if there were major errors. + */ +static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, int data_size, + int empty) +{ + struct extent_buffer *left = path->nodes[0]; + struct extent_buffer *right; + struct extent_buffer *upper; + struct btrfs_disk_key disk_key; + int slot; + u32 i; + int free_space; + int push_space = 0; + int push_items = 0; + struct btrfs_item *item; + u32 left_nritems; + u32 nr; + u32 right_nritems; + u32 data_end; + u32 this_item_size; + int ret; + + slot = path->slots[1]; + if (!path->nodes[1]) { + return 1; + } + upper = path->nodes[1]; + if (slot >= btrfs_header_nritems(upper) - 1) + return 1; + + right = read_node_slot(root, upper, slot + 1); + free_space = btrfs_leaf_free_space(root, right); + if (free_space < data_size) { + free_extent_buffer(right); + return 1; + } + + /* cow and double check */ + ret = btrfs_cow_block(trans, root, right, upper, + slot + 1, &right); + if (ret) { + free_extent_buffer(right); + return 1; + } + free_space = btrfs_leaf_free_space(root, right); + if (free_space < data_size) { + free_extent_buffer(right); + return 1; + } + + left_nritems = btrfs_header_nritems(left); + if (left_nritems == 0) { + free_extent_buffer(right); + return 1; + } + + if (empty) + nr = 0; + else + nr = 1; + + i = left_nritems - 1; + while (i >= nr) { + item = btrfs_item_nr(left, i); + + if (path->slots[0] == i) + push_space += data_size + sizeof(*item); + + this_item_size = btrfs_item_size(left, item); + if (this_item_size + sizeof(*item) + push_space > free_space) + break; + push_items++; + push_space += this_item_size + sizeof(*item); + if (i == 0) + break; + i--; + } + + if (push_items == 0) { + free_extent_buffer(right); + return 1; + } + + if (!empty && push_items == left_nritems) + WARN_ON(1); + + /* push left to right */ + right_nritems = btrfs_header_nritems(right); + + push_space = btrfs_item_end_nr(left, left_nritems - push_items); + push_space -= leaf_data_end(root, left); + + /* make room in the right data area */ + data_end = leaf_data_end(root, right); + memmove_extent_buffer(right, + btrfs_leaf_data(right) + data_end - push_space, + btrfs_leaf_data(right) + data_end, + BTRFS_LEAF_DATA_SIZE(root) - data_end); + + /* copy from the left data area */ + copy_extent_buffer(right, left, btrfs_leaf_data(right) + + BTRFS_LEAF_DATA_SIZE(root) - push_space, + btrfs_leaf_data(left) + leaf_data_end(root, left), + push_space); + + memmove_extent_buffer(right, btrfs_item_nr_offset(push_items), + btrfs_item_nr_offset(0), + right_nritems * sizeof(struct btrfs_item)); + + /* copy the items from left to right */ + copy_extent_buffer(right, left, btrfs_item_nr_offset(0), + btrfs_item_nr_offset(left_nritems - push_items), + push_items * sizeof(struct btrfs_item)); + + /* update the item pointers */ + right_nritems += push_items; + btrfs_set_header_nritems(right, right_nritems); + push_space = BTRFS_LEAF_DATA_SIZE(root); + for (i = 0; i < right_nritems; i++) { + item = btrfs_item_nr(right, i); + push_space -= btrfs_item_size(right, item); + btrfs_set_item_offset(right, item, push_space); + } + + left_nritems -= push_items; + btrfs_set_header_nritems(left, left_nritems); + + if (left_nritems) + btrfs_mark_buffer_dirty(left); + btrfs_mark_buffer_dirty(right); + + btrfs_item_key(right, &disk_key, 0); + btrfs_set_node_key(upper, &disk_key, slot + 1); + btrfs_mark_buffer_dirty(upper); + + /* then fixup the leaf pointer in the path */ + if (path->slots[0] >= left_nritems) { + path->slots[0] -= left_nritems; + free_extent_buffer(path->nodes[0]); + path->nodes[0] = right; + path->slots[1] += 1; + } else { + free_extent_buffer(right); + } + return 0; +} +/* + * push some data in the path leaf to the left, trying to free up at + * least data_size bytes. returns zero if the push worked, nonzero otherwise + */ +static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, int data_size, + int empty) +{ + struct btrfs_disk_key disk_key; + struct extent_buffer *right = path->nodes[0]; + struct extent_buffer *left; + int slot; + int i; + int free_space; + int push_space = 0; + int push_items = 0; + struct btrfs_item *item; + u32 old_left_nritems; + u32 right_nritems; + u32 nr; + int ret = 0; + int wret; + u32 this_item_size; + u32 old_left_item_size; + + slot = path->slots[1]; + if (slot == 0) + return 1; + if (!path->nodes[1]) + return 1; + + right_nritems = btrfs_header_nritems(right); + if (right_nritems == 0) { + return 1; + } + + left = read_node_slot(root, path->nodes[1], slot - 1); + free_space = btrfs_leaf_free_space(root, left); + if (free_space < data_size) { + free_extent_buffer(left); + return 1; + } + + /* cow and double check */ + ret = btrfs_cow_block(trans, root, left, + path->nodes[1], slot - 1, &left); + if (ret) { + /* we hit -ENOSPC, but it isn't fatal here */ + free_extent_buffer(left); + return 1; + } + + free_space = btrfs_leaf_free_space(root, left); + if (free_space < data_size) { + free_extent_buffer(left); + return 1; + } + + if (empty) + nr = right_nritems; + else + nr = right_nritems - 1; + + for (i = 0; i < nr; i++) { + item = btrfs_item_nr(right, i); + + if (path->slots[0] == i) + push_space += data_size + sizeof(*item); + + this_item_size = btrfs_item_size(right, item); + if (this_item_size + sizeof(*item) + push_space > free_space) + break; + + push_items++; + push_space += this_item_size + sizeof(*item); + } + + if (push_items == 0) { + free_extent_buffer(left); + return 1; + } + if (!empty && push_items == btrfs_header_nritems(right)) + WARN_ON(1); + + /* push data from right to left */ + copy_extent_buffer(left, right, + btrfs_item_nr_offset(btrfs_header_nritems(left)), + btrfs_item_nr_offset(0), + push_items * sizeof(struct btrfs_item)); + + push_space = BTRFS_LEAF_DATA_SIZE(root) - + btrfs_item_offset_nr(right, push_items -1); + + copy_extent_buffer(left, right, btrfs_leaf_data(left) + + leaf_data_end(root, left) - push_space, + btrfs_leaf_data(right) + + btrfs_item_offset_nr(right, push_items - 1), + push_space); + old_left_nritems = btrfs_header_nritems(left); + BUG_ON(old_left_nritems < 0); + + old_left_item_size = btrfs_item_offset_nr(left, old_left_nritems - 1); + for (i = old_left_nritems; i < old_left_nritems + push_items; i++) { + u32 ioff; + + item = btrfs_item_nr(left, i); + ioff = btrfs_item_offset(left, item); + btrfs_set_item_offset(left, item, + ioff - (BTRFS_LEAF_DATA_SIZE(root) - old_left_item_size)); + } + btrfs_set_header_nritems(left, old_left_nritems + push_items); + + /* fixup right node */ + if (push_items > right_nritems) { + printk("push items %d nr %u\n", push_items, right_nritems); + WARN_ON(1); + } + + if (push_items < right_nritems) { + push_space = btrfs_item_offset_nr(right, push_items - 1) - + leaf_data_end(root, right); + memmove_extent_buffer(right, btrfs_leaf_data(right) + + BTRFS_LEAF_DATA_SIZE(root) - push_space, + btrfs_leaf_data(right) + + leaf_data_end(root, right), push_space); + + memmove_extent_buffer(right, btrfs_item_nr_offset(0), + btrfs_item_nr_offset(push_items), + (btrfs_header_nritems(right) - push_items) * + sizeof(struct btrfs_item)); + } + right_nritems -= push_items; + btrfs_set_header_nritems(right, right_nritems); + push_space = BTRFS_LEAF_DATA_SIZE(root); + for (i = 0; i < right_nritems; i++) { + item = btrfs_item_nr(right, i); + push_space = push_space - btrfs_item_size(right, item); + btrfs_set_item_offset(right, item, push_space); + } + + btrfs_mark_buffer_dirty(left); + if (right_nritems) + btrfs_mark_buffer_dirty(right); + + btrfs_item_key(right, &disk_key, 0); + wret = fixup_low_keys(trans, root, path, &disk_key, 1); + if (wret) + ret = wret; + + /* then fixup the leaf pointer in the path */ + if (path->slots[0] < push_items) { + path->slots[0] += old_left_nritems; + free_extent_buffer(path->nodes[0]); + path->nodes[0] = left; + path->slots[1] -= 1; + } else { + free_extent_buffer(left); + path->slots[0] -= push_items; + } + BUG_ON(path->slots[0] < 0); + return ret; +} + +/* + * split the path's leaf in two, making sure there is at least data_size + * available for the resulting leaf level of the path. + * + * returns 0 if all went well and < 0 on failure. + */ +static noinline int copy_for_split(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct extent_buffer *l, + struct extent_buffer *right, + int slot, int mid, int nritems) +{ + int data_copy_size; + int rt_data_off; + int i; + int ret = 0; + int wret; + struct btrfs_disk_key disk_key; + + nritems = nritems - mid; + btrfs_set_header_nritems(right, nritems); + data_copy_size = btrfs_item_end_nr(l, mid) - leaf_data_end(root, l); + + copy_extent_buffer(right, l, btrfs_item_nr_offset(0), + btrfs_item_nr_offset(mid), + nritems * sizeof(struct btrfs_item)); + + copy_extent_buffer(right, l, + btrfs_leaf_data(right) + BTRFS_LEAF_DATA_SIZE(root) - + data_copy_size, btrfs_leaf_data(l) + + leaf_data_end(root, l), data_copy_size); + + rt_data_off = BTRFS_LEAF_DATA_SIZE(root) - + btrfs_item_end_nr(l, mid); + + for (i = 0; i < nritems; i++) { + struct btrfs_item *item = btrfs_item_nr(right, i); + u32 ioff = btrfs_item_offset(right, item); + btrfs_set_item_offset(right, item, ioff + rt_data_off); + } + + btrfs_set_header_nritems(l, mid); + ret = 0; + btrfs_item_key(right, &disk_key, 0); + wret = insert_ptr(trans, root, path, &disk_key, right->start, + path->slots[1] + 1, 1); + if (wret) + ret = wret; + + btrfs_mark_buffer_dirty(right); + btrfs_mark_buffer_dirty(l); + BUG_ON(path->slots[0] != slot); + + if (mid <= slot) { + free_extent_buffer(path->nodes[0]); + path->nodes[0] = right; + path->slots[0] -= mid; + path->slots[1] += 1; + } else { + free_extent_buffer(right); + } + + BUG_ON(path->slots[0] < 0); + + return ret; +} + +/* + * split the path's leaf in two, making sure there is at least data_size + * available for the resulting leaf level of the path. + * + * returns 0 if all went well and < 0 on failure. + */ +static noinline int split_leaf(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_key *ins_key, + struct btrfs_path *path, int data_size, + int extend) +{ + struct btrfs_disk_key disk_key; + struct extent_buffer *l; + u32 nritems; + int mid; + int slot; + struct extent_buffer *right; + int ret = 0; + int wret; + int split; + int num_doubles = 0; + + /* first try to make some room by pushing left and right */ + if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY) { + wret = push_leaf_right(trans, root, path, data_size, 0); + if (wret < 0) + return wret; + if (wret) { + wret = push_leaf_left(trans, root, path, data_size, 0); + if (wret < 0) + return wret; + } + l = path->nodes[0]; + + /* did the pushes work? */ + if (btrfs_leaf_free_space(root, l) >= data_size) + return 0; + } + + if (!path->nodes[1]) { + ret = insert_new_root(trans, root, path, 1); + if (ret) + return ret; + } +again: + split = 1; + l = path->nodes[0]; + slot = path->slots[0]; + nritems = btrfs_header_nritems(l); + mid = (nritems + 1) / 2; + + if (mid <= slot) { + if (nritems == 1 || + leaf_space_used(l, mid, nritems - mid) + data_size > + BTRFS_LEAF_DATA_SIZE(root)) { + if (slot >= nritems) { + split = 0; + } else { + mid = slot; + if (mid != nritems && + leaf_space_used(l, mid, nritems - mid) + + data_size > BTRFS_LEAF_DATA_SIZE(root)) { + split = 2; + } + } + } + } else { + if (leaf_space_used(l, 0, mid) + data_size > + BTRFS_LEAF_DATA_SIZE(root)) { + if (!extend && data_size && slot == 0) { + split = 0; + } else if ((extend || !data_size) && slot == 0) { + mid = 1; + } else { + mid = slot; + if (mid != nritems && + leaf_space_used(l, mid, nritems - mid) + + data_size > BTRFS_LEAF_DATA_SIZE(root)) { + split = 2 ; + } + } + } + } + + if (split == 0) + btrfs_cpu_key_to_disk(&disk_key, ins_key); + else + btrfs_item_key(l, &disk_key, mid); + + right = btrfs_alloc_free_block(trans, root, root->leafsize, + root->root_key.objectid, + &disk_key, 0, l->start, 0); + if (IS_ERR(right)) { + BUG_ON(1); + return PTR_ERR(right); + } + + memset_extent_buffer(right, 0, 0, sizeof(struct btrfs_header)); + btrfs_set_header_bytenr(right, right->start); + btrfs_set_header_generation(right, trans->transid); + btrfs_set_header_backref_rev(right, BTRFS_MIXED_BACKREF_REV); + btrfs_set_header_owner(right, root->root_key.objectid); + btrfs_set_header_level(right, 0); + write_extent_buffer(right, root->fs_info->fsid, + (unsigned long)btrfs_header_fsid(right), + BTRFS_FSID_SIZE); + + write_extent_buffer(right, root->fs_info->chunk_tree_uuid, + (unsigned long)btrfs_header_chunk_tree_uuid(right), + BTRFS_UUID_SIZE); + + if (split == 0) { + if (mid <= slot) { + btrfs_set_header_nritems(right, 0); + wret = insert_ptr(trans, root, path, + &disk_key, right->start, + path->slots[1] + 1, 1); + if (wret) + ret = wret; + + free_extent_buffer(path->nodes[0]); + path->nodes[0] = right; + path->slots[0] = 0; + path->slots[1] += 1; + } else { + btrfs_set_header_nritems(right, 0); + wret = insert_ptr(trans, root, path, + &disk_key, + right->start, + path->slots[1], 1); + if (wret) + ret = wret; + free_extent_buffer(path->nodes[0]); + path->nodes[0] = right; + path->slots[0] = 0; + if (path->slots[1] == 0) { + wret = fixup_low_keys(trans, root, + path, &disk_key, 1); + if (wret) + ret = wret; + } + } + btrfs_mark_buffer_dirty(right); + return ret; + } + + ret = copy_for_split(trans, root, path, l, right, slot, mid, nritems); + BUG_ON(ret); + + if (split == 2) { + BUG_ON(num_doubles != 0); + num_doubles++; + goto again; + } + + return ret; +} + +/* + * This function splits a single item into two items, + * giving 'new_key' to the new item and splitting the + * old one at split_offset (from the start of the item). + * + * The path may be released by this operation. After + * the split, the path is pointing to the old item. The + * new item is going to be in the same node as the old one. + * + * Note, the item being split must be smaller enough to live alone on + * a tree block with room for one extra struct btrfs_item + * + * This allows us to split the item in place, keeping a lock on the + * leaf the entire time. + */ +int btrfs_split_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_key *new_key, + unsigned long split_offset) +{ + u32 item_size; + struct extent_buffer *leaf; + struct btrfs_key orig_key; + struct btrfs_item *item; + struct btrfs_item *new_item; + int ret = 0; + int slot; + u32 nritems; + u32 orig_offset; + struct btrfs_disk_key disk_key; + char *buf; + + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &orig_key, path->slots[0]); + if (btrfs_leaf_free_space(root, leaf) >= sizeof(struct btrfs_item)) + goto split; + + item_size = btrfs_item_size_nr(leaf, path->slots[0]); + btrfs_release_path(root, path); + + path->search_for_split = 1; + + ret = btrfs_search_slot(trans, root, &orig_key, path, 0, 1); + path->search_for_split = 0; + + /* if our item isn't there or got smaller, return now */ + if (ret != 0 || item_size != btrfs_item_size_nr(path->nodes[0], + path->slots[0])) { + return -EAGAIN; + } + + ret = split_leaf(trans, root, &orig_key, path, 0, 0); + BUG_ON(ret); + + BUG_ON(btrfs_leaf_free_space(root, leaf) < sizeof(struct btrfs_item)); + leaf = path->nodes[0]; + +split: + item = btrfs_item_nr(leaf, path->slots[0]); + orig_offset = btrfs_item_offset(leaf, item); + item_size = btrfs_item_size(leaf, item); + + + buf = kmalloc(item_size, GFP_NOFS); + read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf, + path->slots[0]), item_size); + slot = path->slots[0] + 1; + leaf = path->nodes[0]; + + nritems = btrfs_header_nritems(leaf); + + if (slot != nritems) { + /* shift the items */ + memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + 1), + btrfs_item_nr_offset(slot), + (nritems - slot) * sizeof(struct btrfs_item)); + + } + + btrfs_cpu_key_to_disk(&disk_key, new_key); + btrfs_set_item_key(leaf, &disk_key, slot); + + new_item = btrfs_item_nr(leaf, slot); + + btrfs_set_item_offset(leaf, new_item, orig_offset); + btrfs_set_item_size(leaf, new_item, item_size - split_offset); + + btrfs_set_item_offset(leaf, item, + orig_offset + item_size - split_offset); + btrfs_set_item_size(leaf, item, split_offset); + + btrfs_set_header_nritems(leaf, nritems + 1); + + /* write the data for the start of the original item */ + write_extent_buffer(leaf, buf, + btrfs_item_ptr_offset(leaf, path->slots[0]), + split_offset); + + /* write the data for the new item */ + write_extent_buffer(leaf, buf + split_offset, + btrfs_item_ptr_offset(leaf, slot), + item_size - split_offset); + btrfs_mark_buffer_dirty(leaf); + + ret = 0; + if (btrfs_leaf_free_space(root, leaf) < 0) { + btrfs_print_leaf(root, leaf); + BUG(); + } + kfree(buf); + return ret; +} + +int btrfs_truncate_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u32 new_size, int from_end) +{ + int ret = 0; + int slot; + int slot_orig; + struct extent_buffer *leaf; + struct btrfs_item *item; + u32 nritems; + unsigned int data_end; + unsigned int old_data_start; + unsigned int old_size; + unsigned int size_diff; + int i; + + slot_orig = path->slots[0]; + leaf = path->nodes[0]; + slot = path->slots[0]; + + old_size = btrfs_item_size_nr(leaf, slot); + if (old_size == new_size) + return 0; + + nritems = btrfs_header_nritems(leaf); + data_end = leaf_data_end(root, leaf); + + old_data_start = btrfs_item_offset_nr(leaf, slot); + + size_diff = old_size - new_size; + + BUG_ON(slot < 0); + BUG_ON(slot >= nritems); + + /* + * item0..itemN ... dataN.offset..dataN.size .. data0.size + */ + /* first correct the data pointers */ + for (i = slot; i < nritems; i++) { + u32 ioff; + item = btrfs_item_nr(leaf, i); + ioff = btrfs_item_offset(leaf, item); + btrfs_set_item_offset(leaf, item, ioff + size_diff); + } + + /* shift the data */ + if (from_end) { + memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + + data_end + size_diff, btrfs_leaf_data(leaf) + + data_end, old_data_start + new_size - data_end); + } else { + struct btrfs_disk_key disk_key; + u64 offset; + + btrfs_item_key(leaf, &disk_key, slot); + + if (btrfs_disk_key_type(&disk_key) == BTRFS_EXTENT_DATA_KEY) { + unsigned long ptr; + struct btrfs_file_extent_item *fi; + + fi = btrfs_item_ptr(leaf, slot, + struct btrfs_file_extent_item); + fi = (struct btrfs_file_extent_item *)( + (unsigned long)fi - size_diff); + + if (btrfs_file_extent_type(leaf, fi) == + BTRFS_FILE_EXTENT_INLINE) { + ptr = btrfs_item_ptr_offset(leaf, slot); + memmove_extent_buffer(leaf, ptr, + (unsigned long)fi, + offsetof(struct btrfs_file_extent_item, + disk_bytenr)); + } + } + + memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + + data_end + size_diff, btrfs_leaf_data(leaf) + + data_end, old_data_start - data_end); + + offset = btrfs_disk_key_offset(&disk_key); + btrfs_set_disk_key_offset(&disk_key, offset + size_diff); + btrfs_set_item_key(leaf, &disk_key, slot); + if (slot == 0) + fixup_low_keys(trans, root, path, &disk_key, 1); + } + + item = btrfs_item_nr(leaf, slot); + btrfs_set_item_size(leaf, item, new_size); + btrfs_mark_buffer_dirty(leaf); + + ret = 0; + if (btrfs_leaf_free_space(root, leaf) < 0) { + btrfs_print_leaf(root, leaf); + BUG(); + } + return ret; +} + +int btrfs_extend_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + u32 data_size) +{ + int ret = 0; + int slot; + int slot_orig; + struct extent_buffer *leaf; + struct btrfs_item *item; + u32 nritems; + unsigned int data_end; + unsigned int old_data; + unsigned int old_size; + int i; + + slot_orig = path->slots[0]; + leaf = path->nodes[0]; + + nritems = btrfs_header_nritems(leaf); + data_end = leaf_data_end(root, leaf); + + if (btrfs_leaf_free_space(root, leaf) < data_size) { + btrfs_print_leaf(root, leaf); + BUG(); + } + slot = path->slots[0]; + old_data = btrfs_item_end_nr(leaf, slot); + + BUG_ON(slot < 0); + if (slot >= nritems) { + btrfs_print_leaf(root, leaf); + printk("slot %d too large, nritems %d\n", slot, nritems); + BUG_ON(1); + } + + /* + * item0..itemN ... dataN.offset..dataN.size .. data0.size + */ + /* first correct the data pointers */ + for (i = slot; i < nritems; i++) { + u32 ioff; + item = btrfs_item_nr(leaf, i); + ioff = btrfs_item_offset(leaf, item); + btrfs_set_item_offset(leaf, item, ioff - data_size); + } + + /* shift the data */ + memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + + data_end - data_size, btrfs_leaf_data(leaf) + + data_end, old_data - data_end); + + data_end = old_data; + old_size = btrfs_item_size_nr(leaf, slot); + item = btrfs_item_nr(leaf, slot); + btrfs_set_item_size(leaf, item, old_size + data_size); + btrfs_mark_buffer_dirty(leaf); + + ret = 0; + if (btrfs_leaf_free_space(root, leaf) < 0) { + btrfs_print_leaf(root, leaf); + BUG(); + } + return ret; +} + +/* + * Given a key and some data, insert an item into the tree. + * This does all the path init required, making room in the tree if needed. + */ +int btrfs_insert_empty_items(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_key *cpu_key, u32 *data_size, + int nr) +{ + struct extent_buffer *leaf; + struct btrfs_item *item; + int ret = 0; + int slot; + int slot_orig; + int i; + u32 nritems; + u32 total_size = 0; + u32 total_data = 0; + unsigned int data_end; + struct btrfs_disk_key disk_key; + + for (i = 0; i < nr; i++) { + total_data += data_size[i]; + } + + /* create a root if there isn't one */ + if (!root->node) + BUG(); + + total_size = total_data + nr * sizeof(struct btrfs_item); + ret = btrfs_search_slot(trans, root, cpu_key, path, total_size, 1); + if (ret == 0) { + return -EEXIST; + } + if (ret < 0) + goto out; + + slot_orig = path->slots[0]; + leaf = path->nodes[0]; + + nritems = btrfs_header_nritems(leaf); + data_end = leaf_data_end(root, leaf); + + if (btrfs_leaf_free_space(root, leaf) < total_size) { + btrfs_print_leaf(root, leaf); + printk("not enough freespace need %u have %d\n", + total_size, btrfs_leaf_free_space(root, leaf)); + BUG(); + } + + slot = path->slots[0]; + BUG_ON(slot < 0); + + if (slot != nritems) { + int i; + unsigned int old_data = btrfs_item_end_nr(leaf, slot); + + if (old_data < data_end) { + btrfs_print_leaf(root, leaf); + printk("slot %d old_data %d data_end %d\n", + slot, old_data, data_end); + BUG_ON(1); + } + /* + * item0..itemN ... dataN.offset..dataN.size .. data0.size + */ + /* first correct the data pointers */ + for (i = slot; i < nritems; i++) { + u32 ioff; + + item = btrfs_item_nr(leaf, i); + ioff = btrfs_item_offset(leaf, item); + btrfs_set_item_offset(leaf, item, ioff - total_data); + } + + /* shift the items */ + memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + nr), + btrfs_item_nr_offset(slot), + (nritems - slot) * sizeof(struct btrfs_item)); + + /* shift the data */ + memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + + data_end - total_data, btrfs_leaf_data(leaf) + + data_end, old_data - data_end); + data_end = old_data; + } + + /* setup the item for the new data */ + for (i = 0; i < nr; i++) { + btrfs_cpu_key_to_disk(&disk_key, cpu_key + i); + btrfs_set_item_key(leaf, &disk_key, slot + i); + item = btrfs_item_nr(leaf, slot + i); + btrfs_set_item_offset(leaf, item, data_end - data_size[i]); + data_end -= data_size[i]; + btrfs_set_item_size(leaf, item, data_size[i]); + } + btrfs_set_header_nritems(leaf, nritems + nr); + btrfs_mark_buffer_dirty(leaf); + + ret = 0; + if (slot == 0) { + btrfs_cpu_key_to_disk(&disk_key, cpu_key); + ret = fixup_low_keys(trans, root, path, &disk_key, 1); + } + + if (btrfs_leaf_free_space(root, leaf) < 0) { + btrfs_print_leaf(root, leaf); + BUG(); + } + +out: + return ret; +} + +/* + * Given a key and some data, insert an item into the tree. + * This does all the path init required, making room in the tree if needed. + */ +int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *cpu_key, void *data, u32 + data_size) +{ + int ret = 0; + struct btrfs_path *path; + struct extent_buffer *leaf; + unsigned long ptr; + + path = btrfs_alloc_path(); + BUG_ON(!path); + ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size); + if (!ret) { + leaf = path->nodes[0]; + ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); + write_extent_buffer(leaf, data, ptr, data_size); + btrfs_mark_buffer_dirty(leaf); + } + btrfs_free_path(path); + return ret; +} + +/* + * delete the pointer from a given node. + * + * If the delete empties a node, the node is removed from the tree, + * continuing all the way the root if required. The root is converted into + * a leaf if all the nodes are emptied. + */ +static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct btrfs_path *path, int level, int slot) +{ + struct extent_buffer *parent = path->nodes[level]; + u32 nritems; + int ret = 0; + int wret; + + nritems = btrfs_header_nritems(parent); + if (slot != nritems -1) { + memmove_extent_buffer(parent, + btrfs_node_key_ptr_offset(slot), + btrfs_node_key_ptr_offset(slot + 1), + sizeof(struct btrfs_key_ptr) * + (nritems - slot - 1)); + } + nritems--; + btrfs_set_header_nritems(parent, nritems); + if (nritems == 0 && parent == root->node) { + BUG_ON(btrfs_header_level(root->node) != 1); + /* just turn the root into a leaf and break */ + btrfs_set_header_level(root->node, 0); + } else if (slot == 0) { + struct btrfs_disk_key disk_key; + + btrfs_node_key(parent, &disk_key, 0); + wret = fixup_low_keys(trans, root, path, &disk_key, level + 1); + if (wret) + ret = wret; + } + btrfs_mark_buffer_dirty(parent); + return ret; +} + +/* + * a helper function to delete the leaf pointed to by path->slots[1] and + * path->nodes[1]. + * + * This deletes the pointer in path->nodes[1] and frees the leaf + * block extent. zero is returned if it all worked out, < 0 otherwise. + * + * The path must have already been setup for deleting the leaf, including + * all the proper balancing. path->nodes[1] must be locked. + */ +static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct extent_buffer *leaf) +{ + int ret; + + WARN_ON(btrfs_header_generation(leaf) != trans->transid); + ret = del_ptr(trans, root, path, 1, path->slots[1]); + if (ret) + return ret; + + ret = btrfs_free_extent(trans, root, leaf->start, leaf->len, + 0, root->root_key.objectid, 0, 0); + return ret; +} + +/* + * delete the item at the leaf level in path. If that empties + * the leaf, remove it from the tree + */ +int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct btrfs_path *path, int slot, int nr) +{ + struct extent_buffer *leaf; + struct btrfs_item *item; + int last_off; + int dsize = 0; + int ret = 0; + int wret; + int i; + u32 nritems; + + leaf = path->nodes[0]; + last_off = btrfs_item_offset_nr(leaf, slot + nr - 1); + + for (i = 0; i < nr; i++) + dsize += btrfs_item_size_nr(leaf, slot + i); + + nritems = btrfs_header_nritems(leaf); + + if (slot + nr != nritems) { + int i; + int data_end = leaf_data_end(root, leaf); + + memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) + + data_end + dsize, + btrfs_leaf_data(leaf) + data_end, + last_off - data_end); + + for (i = slot + nr; i < nritems; i++) { + u32 ioff; + + item = btrfs_item_nr(leaf, i); + ioff = btrfs_item_offset(leaf, item); + btrfs_set_item_offset(leaf, item, ioff + dsize); + } + + memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot), + btrfs_item_nr_offset(slot + nr), + sizeof(struct btrfs_item) * + (nritems - slot - nr)); + } + btrfs_set_header_nritems(leaf, nritems - nr); + nritems -= nr; + + /* delete the leaf if we've emptied it */ + if (nritems == 0) { + if (leaf == root->node) { + btrfs_set_header_level(leaf, 0); + } else { + clean_tree_block(trans, root, leaf); + wait_on_tree_block_writeback(root, leaf); + + wret = btrfs_del_leaf(trans, root, path, leaf); + BUG_ON(ret); + if (wret) + ret = wret; + } + } else { + int used = leaf_space_used(leaf, 0, nritems); + if (slot == 0) { + struct btrfs_disk_key disk_key; + + btrfs_item_key(leaf, &disk_key, 0); + wret = fixup_low_keys(trans, root, path, + &disk_key, 1); + if (wret) + ret = wret; + } + + /* delete the leaf if it is mostly empty */ + if (used < BTRFS_LEAF_DATA_SIZE(root) / 4) { + /* push_leaf_left fixes the path. + * make sure the path still points to our leaf + * for possible call to del_ptr below + */ + slot = path->slots[1]; + extent_buffer_get(leaf); + + wret = push_leaf_left(trans, root, path, 1, 1); + if (wret < 0 && wret != -ENOSPC) + ret = wret; + + if (path->nodes[0] == leaf && + btrfs_header_nritems(leaf)) { + wret = push_leaf_right(trans, root, path, 1, 1); + if (wret < 0 && wret != -ENOSPC) + ret = wret; + } + + if (btrfs_header_nritems(leaf) == 0) { + clean_tree_block(trans, root, leaf); + wait_on_tree_block_writeback(root, leaf); + + path->slots[1] = slot; + ret = btrfs_del_leaf(trans, root, path, leaf); + BUG_ON(ret); + free_extent_buffer(leaf); + + } else { + btrfs_mark_buffer_dirty(leaf); + free_extent_buffer(leaf); + } + } else { + btrfs_mark_buffer_dirty(leaf); + } + } + return ret; +} + +/* + * walk up the tree as far as required to find the previous leaf. + * returns 0 if it found something or 1 if there are no lesser leaves. + * returns < 0 on io errors. + */ +int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path) +{ + int slot; + int level = 1; + struct extent_buffer *c; + struct extent_buffer *next = NULL; + + while(level < BTRFS_MAX_LEVEL) { + if (!path->nodes[level]) + return 1; + + slot = path->slots[level]; + c = path->nodes[level]; + if (slot == 0) { + level++; + if (level == BTRFS_MAX_LEVEL) + return 1; + continue; + } + slot--; + + if (next) + free_extent_buffer(next); + + next = read_node_slot(root, c, slot); + break; + } + path->slots[level] = slot; + while(1) { + level--; + c = path->nodes[level]; + free_extent_buffer(c); + slot = btrfs_header_nritems(next); + if (slot != 0) + slot--; + path->nodes[level] = next; + path->slots[level] = slot; + if (!level) + break; + next = read_node_slot(root, next, slot); + } + return 0; +} + +/* + * walk up the tree as far as required to find the next leaf. + * returns 0 if it found something or 1 if there are no greater leaves. + * returns < 0 on io errors. + */ +int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path) +{ + int slot; + int level = 1; + struct extent_buffer *c; + struct extent_buffer *next = NULL; + + while(level < BTRFS_MAX_LEVEL) { + if (!path->nodes[level]) + return 1; + + slot = path->slots[level] + 1; + c = path->nodes[level]; + if (slot >= btrfs_header_nritems(c)) { + level++; + if (level == BTRFS_MAX_LEVEL) + return 1; + continue; + } + + if (next) + free_extent_buffer(next); + + if (path->reada) + reada_for_search(root, path, level, slot, 0); + + next = read_node_slot(root, c, slot); + break; + } + path->slots[level] = slot; + while(1) { + level--; + c = path->nodes[level]; + free_extent_buffer(c); + path->nodes[level] = next; + path->slots[level] = 0; + if (!level) + break; + if (path->reada) + reada_for_search(root, path, level, 0, 0); + next = read_node_slot(root, next, 0); + } + return 0; +} + +int btrfs_previous_item(struct btrfs_root *root, + struct btrfs_path *path, u64 min_objectid, + int type) +{ + struct btrfs_key found_key; + struct extent_buffer *leaf; + int ret; + + while(1) { + if (path->slots[0] == 0) { + ret = btrfs_prev_leaf(root, path); + if (ret != 0) + return ret; + } else { + path->slots[0]--; + } + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + if (found_key.type == type) + return 0; + } + return 1; +} + diff --git a/lib/ctree.h b/lib/ctree.h new file mode 100644 index 0000000..a9062ea --- /dev/null +++ b/lib/ctree.h @@ -0,0 +1,1882 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __BTRFS__ +#define __BTRFS__ + +#include "list.h" +#include "kerncompat.h" +#include "radix-tree.h" +#include "extent-cache.h" +#include "extent_io.h" + +struct btrfs_root; +struct btrfs_trans_handle; +#define BTRFS_MAGIC "_BHRfS_M" + +#define BTRFS_MAX_LEVEL 8 + +#define BTRFS_COMPAT_EXTENT_TREE_V0 + +/* holds pointers to all of the tree roots */ +#define BTRFS_ROOT_TREE_OBJECTID 1ULL + +/* stores information about which extents are in use, and reference counts */ +#define BTRFS_EXTENT_TREE_OBJECTID 2ULL + +/* + * chunk tree stores translations from logical -> physical block numbering + * the super block points to the chunk tree + */ +#define BTRFS_CHUNK_TREE_OBJECTID 3ULL + +/* + * stores information about which areas of a given device are in use. + * one per device. The tree of tree roots points to the device tree + */ +#define BTRFS_DEV_TREE_OBJECTID 4ULL + +/* one per subvolume, storing files and directories */ +#define BTRFS_FS_TREE_OBJECTID 5ULL + +/* directory objectid inside the root tree */ +#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL +/* holds checksums of all the data extents */ +#define BTRFS_CSUM_TREE_OBJECTID 7ULL + + +/* oprhan objectid for tracking unlinked/truncated files */ +#define BTRFS_ORPHAN_OBJECTID -5ULL + +/* does write ahead logging to speed up fsyncs */ +#define BTRFS_TREE_LOG_OBJECTID -6ULL +#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL + +/* space balancing */ +#define BTRFS_TREE_RELOC_OBJECTID -8ULL +#define BTRFS_DATA_RELOC_TREE_OBJECTID -9ULL + +/* + * extent checksums all have this objectid + * this allows them to share the logging tree + * for fsyncs + */ +#define BTRFS_EXTENT_CSUM_OBJECTID -10ULL + +/* dummy objectid represents multiple objectids */ +#define BTRFS_MULTIPLE_OBJECTIDS -255ULL + +/* + * All files have objectids in this range. + */ +#define BTRFS_FIRST_FREE_OBJECTID 256ULL +#define BTRFS_LAST_FREE_OBJECTID -256ULL +#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL + + + +/* + * the device items go into the chunk tree. The key is in the form + * [ 1 BTRFS_DEV_ITEM_KEY device_id ] + */ +#define BTRFS_DEV_ITEMS_OBJECTID 1ULL + +/* + * we can actually store much bigger names, but lets not confuse the rest + * of linux + */ +#define BTRFS_NAME_LEN 255 + +/* 32 bytes in various csum fields */ +#define BTRFS_CSUM_SIZE 32 + +/* csum types */ +#define BTRFS_CSUM_TYPE_CRC32 0 + + +/* csum types */ +#define BTRFS_CSUM_TYPE_CRC32 0 + +static int btrfs_csum_sizes[] = { 4, 0 }; + +/* four bytes for CRC32 */ +#define BTRFS_CRC32_SIZE 4 +#define BTRFS_EMPTY_DIR_SIZE 0 + +#define BTRFS_FT_UNKNOWN 0 +#define BTRFS_FT_REG_FILE 1 +#define BTRFS_FT_DIR 2 +#define BTRFS_FT_CHRDEV 3 +#define BTRFS_FT_BLKDEV 4 +#define BTRFS_FT_FIFO 5 +#define BTRFS_FT_SOCK 6 +#define BTRFS_FT_SYMLINK 7 +#define BTRFS_FT_XATTR 8 +#define BTRFS_FT_MAX 9 + +/* + * the key defines the order in the tree, and so it also defines (optimal) + * block layout. objectid corresonds to the inode number. The flags + * tells us things about the object, and is a kind of stream selector. + * so for a given inode, keys with flags of 1 might refer to the inode + * data, flags of 2 may point to file data in the btree and flags == 3 + * may point to extents. + * + * offset is the starting byte offset for this key in the stream. + * + * btrfs_disk_key is in disk byte order. struct btrfs_key is always + * in cpu native order. Otherwise they are identical and their sizes + * should be the same (ie both packed) + */ +struct btrfs_disk_key { + __le64 objectid; + u8 type; + __le64 offset; +} __attribute__ ((__packed__)); + +struct btrfs_key { + u64 objectid; + u8 type; + u64 offset; +} __attribute__ ((__packed__)); + +struct btrfs_mapping_tree { + struct cache_tree cache_tree; +}; + +#define BTRFS_UUID_SIZE 16 +struct btrfs_dev_item { + /* the internal btrfs device id */ + __le64 devid; + + /* size of the device */ + __le64 total_bytes; + + /* bytes used */ + __le64 bytes_used; + + /* optimal io alignment for this device */ + __le32 io_align; + + /* optimal io width for this device */ + __le32 io_width; + + /* minimal io size for this device */ + __le32 sector_size; + + /* type and info about this device */ + __le64 type; + + /* expected generation for this device */ + __le64 generation; + + /* + * starting byte of this partition on the device, + * to allowr for stripe alignment in the future + */ + __le64 start_offset; + + /* grouping information for allocation decisions */ + __le32 dev_group; + + /* seek speed 0-100 where 100 is fastest */ + u8 seek_speed; + + /* bandwidth 0-100 where 100 is fastest */ + u8 bandwidth; + + /* btrfs generated uuid for this device */ + u8 uuid[BTRFS_UUID_SIZE]; + + /* uuid of FS who owns this device */ + u8 fsid[BTRFS_UUID_SIZE]; +} __attribute__ ((__packed__)); + +struct btrfs_stripe { + __le64 devid; + __le64 offset; + u8 dev_uuid[BTRFS_UUID_SIZE]; +} __attribute__ ((__packed__)); + +struct btrfs_chunk { + /* size of this chunk in bytes */ + __le64 length; + + /* objectid of the root referencing this chunk */ + __le64 owner; + + __le64 stripe_len; + __le64 type; + + /* optimal io alignment for this chunk */ + __le32 io_align; + + /* optimal io width for this chunk */ + __le32 io_width; + + /* minimal io size for this chunk */ + __le32 sector_size; + + /* 2^16 stripes is quite a lot, a second limit is the size of a single + * item in the btree + */ + __le16 num_stripes; + + /* sub stripes only matter for raid10 */ + __le16 sub_stripes; + struct btrfs_stripe stripe; + /* additional stripes go here */ +} __attribute__ ((__packed__)); + +static inline unsigned long btrfs_chunk_item_size(int num_stripes) +{ + BUG_ON(num_stripes == 0); + return sizeof(struct btrfs_chunk) + + sizeof(struct btrfs_stripe) * (num_stripes - 1); +} + +#define BTRFS_FSID_SIZE 16 +#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0) +#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1) +#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32) +#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33) + +#define BTRFS_BACKREF_REV_MAX 256 +#define BTRFS_BACKREF_REV_SHIFT 56 +#define BTRFS_BACKREF_REV_MASK (((u64)BTRFS_BACKREF_REV_MAX - 1) << \ + BTRFS_BACKREF_REV_SHIFT) + +#define BTRFS_OLD_BACKREF_REV 0 +#define BTRFS_MIXED_BACKREF_REV 1 + +/* + * every tree block (leaf or node) starts with this header. + */ +struct btrfs_header { + /* these first four must match the super block */ + u8 csum[BTRFS_CSUM_SIZE]; + u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ + __le64 bytenr; /* which block this node is supposed to live in */ + __le64 flags; + + /* allowed to be different from the super from here on down */ + u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; + __le64 generation; + __le64 owner; + __le32 nritems; + u8 level; +} __attribute__ ((__packed__)); + +#define BTRFS_NODEPTRS_PER_BLOCK(r) (((r)->nodesize - \ + sizeof(struct btrfs_header)) / \ + sizeof(struct btrfs_key_ptr)) +#define __BTRFS_LEAF_DATA_SIZE(bs) ((bs) - sizeof(struct btrfs_header)) +#define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->leafsize)) +#define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \ + sizeof(struct btrfs_item) - \ + sizeof(struct btrfs_file_extent_item)) + + +/* + * this is a very generous portion of the super block, giving us + * room to translate 14 chunks with 3 stripes each. + */ +#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048 +#define BTRFS_LABEL_SIZE 256 + +/* + * the super block basically lists the main trees of the FS + * it currently lacks any block count etc etc + */ +struct btrfs_super_block { + u8 csum[BTRFS_CSUM_SIZE]; + /* the first 3 fields must match struct btrfs_header */ + u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ + __le64 bytenr; /* this block number */ + __le64 flags; + + /* allowed to be different from the btrfs_header from here own down */ + __le64 magic; + __le64 generation; + __le64 root; + __le64 chunk_root; + __le64 log_root; + + /* this will help find the new super based on the log root */ + __le64 log_root_transid; + __le64 total_bytes; + __le64 bytes_used; + __le64 root_dir_objectid; + __le64 num_devices; + __le32 sectorsize; + __le32 nodesize; + __le32 leafsize; + __le32 stripesize; + __le32 sys_chunk_array_size; + __le64 chunk_root_generation; + __le64 compat_flags; + __le64 compat_ro_flags; + __le64 incompat_flags; + __le16 csum_type; + u8 root_level; + u8 chunk_root_level; + u8 log_root_level; + struct btrfs_dev_item dev_item; + + char label[BTRFS_LABEL_SIZE]; + + /* future expansion */ + __le64 reserved[32]; + u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE]; +} __attribute__ ((__packed__)); + +/* + * Compat flags that we support. If any incompat flags are set other than the + * ones specified below then we will fail to mount + */ +#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0) + +#define BTRFS_FEATURE_COMPAT_SUPP 0ULL +#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL +#define BTRFS_FEATURE_INCOMPAT_SUPP \ + BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF + +/* + * A leaf is full of items. offset and size tell us where to find + * the item in the leaf (relative to the start of the data area) + */ +struct btrfs_item { + struct btrfs_disk_key key; + __le32 offset; + __le32 size; +} __attribute__ ((__packed__)); + +/* + * leaves have an item area and a data area: + * [item0, item1....itemN] [free space] [dataN...data1, data0] + * + * The data is separate from the items to get the keys closer together + * during searches. + */ +struct btrfs_leaf { + struct btrfs_header header; + struct btrfs_item items[]; +} __attribute__ ((__packed__)); + +/* + * all non-leaf blocks are nodes, they hold only keys and pointers to + * other blocks + */ +struct btrfs_key_ptr { + struct btrfs_disk_key key; + __le64 blockptr; + __le64 generation; +} __attribute__ ((__packed__)); + +struct btrfs_node { + struct btrfs_header header; + struct btrfs_key_ptr ptrs[]; +} __attribute__ ((__packed__)); + +/* + * btrfs_paths remember the path taken from the root down to the leaf. + * level 0 is always the leaf, and nodes[1...BTRFS_MAX_LEVEL] will point + * to any other levels that are present. + * + * The slots array records the index of the item or block pointer + * used while walking the tree. + */ + +struct btrfs_path { + struct extent_buffer *nodes[BTRFS_MAX_LEVEL]; + int slots[BTRFS_MAX_LEVEL]; + /* if there is real range locking, this locks field will change */ + int locks[BTRFS_MAX_LEVEL]; + int reada; + /* keep some upper locks as we walk down */ + int lowest_level; + + /* + * set by btrfs_split_item, tells search_slot to keep all locks + * and to force calls to keep space in the nodes + */ + unsigned int search_for_split:1; + unsigned int keep_locks:1; + unsigned int skip_locking:1; + unsigned int leave_spinning:1; +}; + +/* + * items in the extent btree are used to record the objectid of the + * owner of the block and the number of references + */ + +struct btrfs_extent_item { + __le64 refs; + __le64 generation; + __le64 flags; +} __attribute__ ((__packed__)); + +struct btrfs_extent_item_v0 { + __le32 refs; +} __attribute__ ((__packed__)); + +#define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r) >> 4) - \ + sizeof(struct btrfs_item)) + +#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0) +#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1) + +/* following flags only apply to tree blocks */ + +/* use full backrefs for extent pointers in the block*/ +#define BTRFS_BLOCK_FLAG_FULL_BACKREF (1ULL << 8) + +struct btrfs_tree_block_info { + struct btrfs_disk_key key; + u8 level; +} __attribute__ ((__packed__)); + +struct btrfs_extent_data_ref { + __le64 root; + __le64 objectid; + __le64 offset; + __le32 count; +} __attribute__ ((__packed__)); + +struct btrfs_shared_data_ref { + __le32 count; +} __attribute__ ((__packed__)); + +struct btrfs_extent_inline_ref { + u8 type; + u64 offset; +} __attribute__ ((__packed__)); + +struct btrfs_extent_ref_v0 { + __le64 root; + __le64 generation; + __le64 objectid; + __le32 count; +} __attribute__ ((__packed__)); + +/* dev extents record free space on individual devices. The owner + * field points back to the chunk allocation mapping tree that allocated + * the extent. The chunk tree uuid field is a way to double check the owner + */ +struct btrfs_dev_extent { + __le64 chunk_tree; + __le64 chunk_objectid; + __le64 chunk_offset; + __le64 length; + u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; +} __attribute__ ((__packed__)); + +struct btrfs_inode_ref { + __le64 index; + __le16 name_len; + /* name goes here */ +} __attribute__ ((__packed__)); + +struct btrfs_timespec { + __le64 sec; + __le32 nsec; +} __attribute__ ((__packed__)); + +typedef enum { + BTRFS_COMPRESS_NONE = 0, + BTRFS_COMPRESS_ZLIB = 1, + BTRFS_COMPRESS_LAST = 2, +} btrfs_compression_type; + +/* we don't understand any encryption methods right now */ +typedef enum { + BTRFS_ENCRYPTION_NONE = 0, + BTRFS_ENCRYPTION_LAST = 1, +} btrfs_encryption_type; + +struct btrfs_inode_item { + /* nfs style generation number */ + __le64 generation; + /* transid that last touched this inode */ + __le64 transid; + __le64 size; + __le64 nbytes; + __le64 block_group; + __le32 nlink; + __le32 uid; + __le32 gid; + __le32 mode; + __le64 rdev; + __le64 flags; + + /* modification sequence number for NFS */ + __le64 sequence; + + /* + * a little future expansion, for more than this we can + * just grow the inode item and version it + */ + __le64 reserved[4]; + struct btrfs_timespec atime; + struct btrfs_timespec ctime; + struct btrfs_timespec mtime; + struct btrfs_timespec otime; +} __attribute__ ((__packed__)); + +struct btrfs_dir_log_item { + __le64 end; +} __attribute__ ((__packed__)); + +struct btrfs_dir_item { + struct btrfs_disk_key location; + __le64 transid; + __le16 data_len; + __le16 name_len; + u8 type; +} __attribute__ ((__packed__)); + +struct btrfs_root_item { + struct btrfs_inode_item inode; + __le64 generation; + __le64 root_dirid; + __le64 bytenr; + __le64 byte_limit; + __le64 bytes_used; + __le64 last_snapshot; + __le64 flags; + __le32 refs; + struct btrfs_disk_key drop_progress; + u8 drop_level; + u8 level; +} __attribute__ ((__packed__)); + +/* + * this is used for both forward and backward root refs + */ +struct btrfs_root_ref { + __le64 dirid; + __le64 sequence; + __le16 name_len; +} __attribute__ ((__packed__)); + +#define BTRFS_FILE_EXTENT_INLINE 0 +#define BTRFS_FILE_EXTENT_REG 1 +#define BTRFS_FILE_EXTENT_PREALLOC 2 + +struct btrfs_file_extent_item { + /* + * transaction id that created this extent + */ + __le64 generation; + /* + * max number of bytes to hold this extent in ram + * when we split a compressed extent we can't know how big + * each of the resulting pieces will be. So, this is + * an upper limit on the size of the extent in ram instead of + * an exact limit. + */ + __le64 ram_bytes; + + /* + * 32 bits for the various ways we might encode the data, + * including compression and encryption. If any of these + * are set to something a given disk format doesn't understand + * it is treated like an incompat flag for reading and writing, + * but not for stat. + */ + u8 compression; + u8 encryption; + __le16 other_encoding; /* spare for later use */ + + /* are we inline data or a real extent? */ + u8 type; + + /* + * disk space consumed by the extent, checksum blocks are included + * in these numbers + */ + __le64 disk_bytenr; + __le64 disk_num_bytes; + /* + * the logical offset in file blocks (no csums) + * this extent record is for. This allows a file extent to point + * into the middle of an existing extent on disk, sharing it + * between two snapshots (useful if some bytes in the middle of the + * extent have changed + */ + __le64 offset; + /* + * the logical number of file blocks (no csums included) + */ + __le64 num_bytes; + +} __attribute__ ((__packed__)); + +struct btrfs_csum_item { + u8 csum; +} __attribute__ ((__packed__)); + +/* tag for the radix tree of block groups in ram */ +#define BTRFS_BLOCK_GROUP_DATA (1 << 0) +#define BTRFS_BLOCK_GROUP_SYSTEM (1 << 1) +#define BTRFS_BLOCK_GROUP_METADATA (1 << 2) +#define BTRFS_BLOCK_GROUP_RAID0 (1 << 3) +#define BTRFS_BLOCK_GROUP_RAID1 (1 << 4) +#define BTRFS_BLOCK_GROUP_DUP (1 << 5) +#define BTRFS_BLOCK_GROUP_RAID10 (1 << 6) + +struct btrfs_block_group_item { + __le64 used; + __le64 chunk_objectid; + __le64 flags; +} __attribute__ ((__packed__)); + +struct btrfs_space_info { + u64 flags; + u64 total_bytes; + u64 bytes_used; + u64 bytes_pinned; + int full; + struct list_head list; +}; + +struct btrfs_block_group_cache { + struct cache_extent cache; + struct btrfs_key key; + struct btrfs_block_group_item item; + struct btrfs_space_info *space_info; + u64 pinned; + u64 flags; + int cached; + int ro; +}; + +struct btrfs_extent_ops { + int (*alloc_extent)(struct btrfs_root *root, u64 num_bytes, + u64 hint_byte, struct btrfs_key *ins); + int (*free_extent)(struct btrfs_root *root, u64 bytenr, + u64 num_bytes); +}; + +struct btrfs_device; +struct btrfs_fs_devices; +struct btrfs_fs_info { + u8 fsid[BTRFS_FSID_SIZE]; + u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; + struct btrfs_root *fs_root; + struct btrfs_root *extent_root; + struct btrfs_root *tree_root; + struct btrfs_root *chunk_root; + struct btrfs_root *dev_root; + struct btrfs_root *csum_root; + + struct cache_tree fs_root_cache; + + /* the log root tree is a directory of all the other log roots */ + struct btrfs_root *log_root_tree; + + struct extent_io_tree extent_cache; + struct extent_io_tree free_space_cache; + struct extent_io_tree block_group_cache; + struct extent_io_tree pinned_extents; + struct extent_io_tree pending_del; + struct extent_io_tree extent_ins; + + /* logical->physical extent mapping */ + struct btrfs_mapping_tree mapping_tree; + + u64 generation; + u64 last_trans_committed; + + u64 avail_data_alloc_bits; + u64 avail_metadata_alloc_bits; + u64 avail_system_alloc_bits; + u64 data_alloc_profile; + u64 metadata_alloc_profile; + u64 system_alloc_profile; + u64 alloc_start; + + struct btrfs_trans_handle *running_transaction; + struct btrfs_super_block super_copy; + struct mutex fs_mutex; + + u64 super_bytenr; + u64 total_pinned; + + struct btrfs_extent_ops *extent_ops; + struct list_head dirty_cowonly_roots; + + struct btrfs_fs_devices *fs_devices; + struct list_head space_info; + int system_allocs; + int readonly; +}; + +/* + * in ram representation of the tree. extent_root is used for all allocations + * and for the extent tree extent_root root. + */ +struct btrfs_root { + struct extent_buffer *node; + struct extent_buffer *commit_root; + struct btrfs_root_item root_item; + struct btrfs_key root_key; + struct btrfs_fs_info *fs_info; + u64 objectid; + u64 last_trans; + + /* data allocations are done in sectorsize units */ + u32 sectorsize; + + /* node allocations are done in nodesize units */ + u32 nodesize; + + /* leaf allocations are done in leafsize units */ + u32 leafsize; + + /* leaf allocations are done in leafsize units */ + u32 stripesize; + + int ref_cows; + int track_dirty; + + + u32 type; + u64 highest_inode; + u64 last_inode_alloc; + + /* the dirty list is only used by non-reference counted roots */ + struct list_head dirty_list; + struct cache_extent cache; +}; + +/* + * inode items have the data typically returned from stat and store other + * info about object characteristics. There is one for every file and dir in + * the FS + */ +#define BTRFS_INODE_ITEM_KEY 1 +#define BTRFS_INODE_REF_KEY 12 +#define BTRFS_XATTR_ITEM_KEY 24 +#define BTRFS_ORPHAN_ITEM_KEY 48 + +#define BTRFS_DIR_LOG_ITEM_KEY 60 +#define BTRFS_DIR_LOG_INDEX_KEY 72 +/* + * dir items are the name -> inode pointers in a directory. There is one + * for every name in a directory. + */ +#define BTRFS_DIR_ITEM_KEY 84 +#define BTRFS_DIR_INDEX_KEY 96 + +/* + * extent data is for file data + */ +#define BTRFS_EXTENT_DATA_KEY 108 + +/* + * csum items have the checksums for data in the extents + */ +#define BTRFS_CSUM_ITEM_KEY 120 +/* + * extent csums are stored in a separate tree and hold csums for + * an entire extent on disk. + */ +#define BTRFS_EXTENT_CSUM_KEY 128 + +/* + * root items point to tree roots. There are typically in the root + * tree used by the super block to find all the other trees + */ +#define BTRFS_ROOT_ITEM_KEY 132 + +/* + * root backrefs tie subvols and snapshots to the directory entries that + * reference them + */ +#define BTRFS_ROOT_BACKREF_KEY 144 + +/* + * root refs make a fast index for listing all of the snapshots and + * subvolumes referenced by a given root. They point directly to the + * directory item in the root that references the subvol + */ +#define BTRFS_ROOT_REF_KEY 156 + +/* + * extent items are in the extent map tree. These record which blocks + * are used, and how many references there are to each block + */ +#define BTRFS_EXTENT_ITEM_KEY 168 + +#define BTRFS_TREE_BLOCK_REF_KEY 176 + +#define BTRFS_EXTENT_DATA_REF_KEY 178 + +/* old style extent backrefs */ +#define BTRFS_EXTENT_REF_V0_KEY 180 + +#define BTRFS_SHARED_BLOCK_REF_KEY 182 + +#define BTRFS_SHARED_DATA_REF_KEY 184 + + +/* + * block groups give us hints into the extent allocation trees. Which + * blocks are free etc etc + */ +#define BTRFS_BLOCK_GROUP_ITEM_KEY 192 + +#define BTRFS_DEV_EXTENT_KEY 204 +#define BTRFS_DEV_ITEM_KEY 216 +#define BTRFS_CHUNK_ITEM_KEY 228 + +/* + * string items are for debugging. They just store a short string of + * data in the FS + */ +#define BTRFS_STRING_ITEM_KEY 253 +/* + * Inode flags + */ +#define BTRFS_INODE_NODATASUM (1 << 0) +#define BTRFS_INODE_NODATACOW (1 << 1) +#define BTRFS_INODE_READONLY (1 << 2) + +#define read_eb_member(eb, ptr, type, member, result) ( \ + read_extent_buffer(eb, (char *)(result), \ + ((unsigned long)(ptr)) + \ + offsetof(type, member), \ + sizeof(((type *)0)->member))) + +#define write_eb_member(eb, ptr, type, member, result) ( \ + write_extent_buffer(eb, (char *)(result), \ + ((unsigned long)(ptr)) + \ + offsetof(type, member), \ + sizeof(((type *)0)->member))) + +#define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits) \ +static inline u##bits btrfs_##name(struct extent_buffer *eb) \ +{ \ + struct btrfs_header *h = (struct btrfs_header *)eb->data; \ + return le##bits##_to_cpu(h->member); \ +} \ +static inline void btrfs_set_##name(struct extent_buffer *eb, \ + u##bits val) \ +{ \ + struct btrfs_header *h = (struct btrfs_header *)eb->data; \ + h->member = cpu_to_le##bits(val); \ +} + +#define BTRFS_SETGET_FUNCS(name, type, member, bits) \ +static inline u##bits btrfs_##name(struct extent_buffer *eb, \ + type *s) \ +{ \ + unsigned long offset = (unsigned long)s; \ + type *p = (type *) (eb->data + offset); \ + return le##bits##_to_cpu(p->member); \ +} \ +static inline void btrfs_set_##name(struct extent_buffer *eb, \ + type *s, u##bits val) \ +{ \ + unsigned long offset = (unsigned long)s; \ + type *p = (type *) (eb->data + offset); \ + p->member = cpu_to_le##bits(val); \ +} + +#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \ +static inline u##bits btrfs_##name(type *s) \ +{ \ + return le##bits##_to_cpu(s->member); \ +} \ +static inline void btrfs_set_##name(type *s, u##bits val) \ +{ \ + s->member = cpu_to_le##bits(val); \ +} + +BTRFS_SETGET_FUNCS(device_type, struct btrfs_dev_item, type, 64); +BTRFS_SETGET_FUNCS(device_total_bytes, struct btrfs_dev_item, total_bytes, 64); +BTRFS_SETGET_FUNCS(device_bytes_used, struct btrfs_dev_item, bytes_used, 64); +BTRFS_SETGET_FUNCS(device_io_align, struct btrfs_dev_item, io_align, 32); +BTRFS_SETGET_FUNCS(device_io_width, struct btrfs_dev_item, io_width, 32); +BTRFS_SETGET_FUNCS(device_start_offset, struct btrfs_dev_item, + start_offset, 64); +BTRFS_SETGET_FUNCS(device_sector_size, struct btrfs_dev_item, sector_size, 32); +BTRFS_SETGET_FUNCS(device_id, struct btrfs_dev_item, devid, 64); +BTRFS_SETGET_FUNCS(device_group, struct btrfs_dev_item, dev_group, 32); +BTRFS_SETGET_FUNCS(device_seek_speed, struct btrfs_dev_item, seek_speed, 8); +BTRFS_SETGET_FUNCS(device_bandwidth, struct btrfs_dev_item, bandwidth, 8); +BTRFS_SETGET_FUNCS(device_generation, struct btrfs_dev_item, generation, 64); + +BTRFS_SETGET_STACK_FUNCS(stack_device_type, struct btrfs_dev_item, type, 64); +BTRFS_SETGET_STACK_FUNCS(stack_device_total_bytes, struct btrfs_dev_item, + total_bytes, 64); +BTRFS_SETGET_STACK_FUNCS(stack_device_bytes_used, struct btrfs_dev_item, + bytes_used, 64); +BTRFS_SETGET_STACK_FUNCS(stack_device_io_align, struct btrfs_dev_item, + io_align, 32); +BTRFS_SETGET_STACK_FUNCS(stack_device_io_width, struct btrfs_dev_item, + io_width, 32); +BTRFS_SETGET_STACK_FUNCS(stack_device_sector_size, struct btrfs_dev_item, + sector_size, 32); +BTRFS_SETGET_STACK_FUNCS(stack_device_id, struct btrfs_dev_item, devid, 64); +BTRFS_SETGET_STACK_FUNCS(stack_device_group, struct btrfs_dev_item, + dev_group, 32); +BTRFS_SETGET_STACK_FUNCS(stack_device_seek_speed, struct btrfs_dev_item, + seek_speed, 8); +BTRFS_SETGET_STACK_FUNCS(stack_device_bandwidth, struct btrfs_dev_item, + bandwidth, 8); +BTRFS_SETGET_STACK_FUNCS(stack_device_generation, struct btrfs_dev_item, + generation, 64); + +static inline char *btrfs_device_uuid(struct btrfs_dev_item *d) +{ + return (char *)d + offsetof(struct btrfs_dev_item, uuid); +} + +static inline char *btrfs_device_fsid(struct btrfs_dev_item *d) +{ + return (char *)d + offsetof(struct btrfs_dev_item, fsid); +} + +BTRFS_SETGET_FUNCS(chunk_length, struct btrfs_chunk, length, 64); +BTRFS_SETGET_FUNCS(chunk_owner, struct btrfs_chunk, owner, 64); +BTRFS_SETGET_FUNCS(chunk_stripe_len, struct btrfs_chunk, stripe_len, 64); +BTRFS_SETGET_FUNCS(chunk_io_align, struct btrfs_chunk, io_align, 32); +BTRFS_SETGET_FUNCS(chunk_io_width, struct btrfs_chunk, io_width, 32); +BTRFS_SETGET_FUNCS(chunk_sector_size, struct btrfs_chunk, sector_size, 32); +BTRFS_SETGET_FUNCS(chunk_type, struct btrfs_chunk, type, 64); +BTRFS_SETGET_FUNCS(chunk_num_stripes, struct btrfs_chunk, num_stripes, 16); +BTRFS_SETGET_FUNCS(chunk_sub_stripes, struct btrfs_chunk, sub_stripes, 16); +BTRFS_SETGET_FUNCS(stripe_devid, struct btrfs_stripe, devid, 64); +BTRFS_SETGET_FUNCS(stripe_offset, struct btrfs_stripe, offset, 64); + +static inline char *btrfs_stripe_dev_uuid(struct btrfs_stripe *s) +{ + return (char *)s + offsetof(struct btrfs_stripe, dev_uuid); +} + +BTRFS_SETGET_STACK_FUNCS(stack_chunk_length, struct btrfs_chunk, length, 64); +BTRFS_SETGET_STACK_FUNCS(stack_chunk_owner, struct btrfs_chunk, owner, 64); +BTRFS_SETGET_STACK_FUNCS(stack_chunk_stripe_len, struct btrfs_chunk, + stripe_len, 64); +BTRFS_SETGET_STACK_FUNCS(stack_chunk_io_align, struct btrfs_chunk, + io_align, 32); +BTRFS_SETGET_STACK_FUNCS(stack_chunk_io_width, struct btrfs_chunk, + io_width, 32); +BTRFS_SETGET_STACK_FUNCS(stack_chunk_sector_size, struct btrfs_chunk, + sector_size, 32); +BTRFS_SETGET_STACK_FUNCS(stack_chunk_type, struct btrfs_chunk, type, 64); +BTRFS_SETGET_STACK_FUNCS(stack_chunk_num_stripes, struct btrfs_chunk, + num_stripes, 16); +BTRFS_SETGET_STACK_FUNCS(stack_chunk_sub_stripes, struct btrfs_chunk, + sub_stripes, 16); +BTRFS_SETGET_STACK_FUNCS(stack_stripe_devid, struct btrfs_stripe, devid, 64); +BTRFS_SETGET_STACK_FUNCS(stack_stripe_offset, struct btrfs_stripe, offset, 64); + +static inline struct btrfs_stripe *btrfs_stripe_nr(struct btrfs_chunk *c, + int nr) +{ + unsigned long offset = (unsigned long)c; + offset += offsetof(struct btrfs_chunk, stripe); + offset += nr * sizeof(struct btrfs_stripe); + return (struct btrfs_stripe *)offset; +} + +static inline char *btrfs_stripe_dev_uuid_nr(struct btrfs_chunk *c, int nr) +{ + return btrfs_stripe_dev_uuid(btrfs_stripe_nr(c, nr)); +} + +static inline u64 btrfs_stripe_offset_nr(struct extent_buffer *eb, + struct btrfs_chunk *c, int nr) +{ + return btrfs_stripe_offset(eb, btrfs_stripe_nr(c, nr)); +} + +static inline void btrfs_set_stripe_offset_nr(struct extent_buffer *eb, + struct btrfs_chunk *c, int nr, + u64 val) +{ + btrfs_set_stripe_offset(eb, btrfs_stripe_nr(c, nr), val); +} + +static inline u64 btrfs_stripe_devid_nr(struct extent_buffer *eb, + struct btrfs_chunk *c, int nr) +{ + return btrfs_stripe_devid(eb, btrfs_stripe_nr(c, nr)); +} + +static inline void btrfs_set_stripe_devid_nr(struct extent_buffer *eb, + struct btrfs_chunk *c, int nr, + u64 val) +{ + btrfs_set_stripe_devid(eb, btrfs_stripe_nr(c, nr), val); +} + +/* struct btrfs_block_group_item */ +BTRFS_SETGET_STACK_FUNCS(block_group_used, struct btrfs_block_group_item, + used, 64); +BTRFS_SETGET_FUNCS(disk_block_group_used, struct btrfs_block_group_item, + used, 64); +BTRFS_SETGET_STACK_FUNCS(block_group_chunk_objectid, + struct btrfs_block_group_item, chunk_objectid, 64); + +BTRFS_SETGET_FUNCS(disk_block_group_chunk_objectid, + struct btrfs_block_group_item, chunk_objectid, 64); +BTRFS_SETGET_FUNCS(disk_block_group_flags, + struct btrfs_block_group_item, flags, 64); +BTRFS_SETGET_STACK_FUNCS(block_group_flags, + struct btrfs_block_group_item, flags, 64); + +/* struct btrfs_inode_ref */ +BTRFS_SETGET_FUNCS(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_item */ +BTRFS_SETGET_FUNCS(inode_generation, struct btrfs_inode_item, generation, 64); +BTRFS_SETGET_FUNCS(inode_sequence, struct btrfs_inode_item, sequence, 64); +BTRFS_SETGET_FUNCS(inode_transid, struct btrfs_inode_item, transid, 64); +BTRFS_SETGET_FUNCS(inode_size, struct btrfs_inode_item, size, 64); +BTRFS_SETGET_FUNCS(inode_nbytes, struct btrfs_inode_item, nbytes, 64); +BTRFS_SETGET_FUNCS(inode_block_group, struct btrfs_inode_item, block_group, 64); +BTRFS_SETGET_FUNCS(inode_nlink, struct btrfs_inode_item, nlink, 32); +BTRFS_SETGET_FUNCS(inode_uid, struct btrfs_inode_item, uid, 32); +BTRFS_SETGET_FUNCS(inode_gid, struct btrfs_inode_item, gid, 32); +BTRFS_SETGET_FUNCS(inode_mode, struct btrfs_inode_item, mode, 32); +BTRFS_SETGET_FUNCS(inode_rdev, struct btrfs_inode_item, rdev, 64); +BTRFS_SETGET_FUNCS(inode_flags, struct btrfs_inode_item, flags, 64); + +BTRFS_SETGET_STACK_FUNCS(stack_inode_generation, + struct btrfs_inode_item, generation, 64); +BTRFS_SETGET_STACK_FUNCS(stack_inode_sequence, + struct btrfs_inode_item, generation, 64); +BTRFS_SETGET_STACK_FUNCS(stack_inode_size, + struct btrfs_inode_item, size, 64); +BTRFS_SETGET_STACK_FUNCS(stack_inode_nbytes, + struct btrfs_inode_item, nbytes, 64); +BTRFS_SETGET_STACK_FUNCS(stack_inode_block_group, + struct btrfs_inode_item, block_group, 64); +BTRFS_SETGET_STACK_FUNCS(stack_inode_nlink, + struct btrfs_inode_item, nlink, 32); +BTRFS_SETGET_STACK_FUNCS(stack_inode_uid, + struct btrfs_inode_item, uid, 32); +BTRFS_SETGET_STACK_FUNCS(stack_inode_gid, + struct btrfs_inode_item, gid, 32); +BTRFS_SETGET_STACK_FUNCS(stack_inode_mode, + struct btrfs_inode_item, mode, 32); +BTRFS_SETGET_STACK_FUNCS(stack_inode_rdev, + struct btrfs_inode_item, rdev, 64); +BTRFS_SETGET_STACK_FUNCS(stack_inode_flags, + struct btrfs_inode_item, flags, 64); + +static inline struct btrfs_timespec * +btrfs_inode_atime(struct btrfs_inode_item *inode_item) +{ + unsigned long ptr = (unsigned long)inode_item; + ptr += offsetof(struct btrfs_inode_item, atime); + return (struct btrfs_timespec *)ptr; +} + +static inline struct btrfs_timespec * +btrfs_inode_mtime(struct btrfs_inode_item *inode_item) +{ + unsigned long ptr = (unsigned long)inode_item; + ptr += offsetof(struct btrfs_inode_item, mtime); + return (struct btrfs_timespec *)ptr; +} + +static inline struct btrfs_timespec * +btrfs_inode_ctime(struct btrfs_inode_item *inode_item) +{ + unsigned long ptr = (unsigned long)inode_item; + ptr += offsetof(struct btrfs_inode_item, ctime); + return (struct btrfs_timespec *)ptr; +} + +static inline struct btrfs_timespec * +btrfs_inode_otime(struct btrfs_inode_item *inode_item) +{ + unsigned long ptr = (unsigned long)inode_item; + ptr += offsetof(struct btrfs_inode_item, otime); + return (struct btrfs_timespec *)ptr; +} + +BTRFS_SETGET_FUNCS(timespec_sec, struct btrfs_timespec, sec, 64); +BTRFS_SETGET_FUNCS(timespec_nsec, struct btrfs_timespec, nsec, 32); +BTRFS_SETGET_STACK_FUNCS(stack_timespec_sec, struct btrfs_timespec, + sec, 64); +BTRFS_SETGET_STACK_FUNCS(stack_timespec_nsec, struct btrfs_timespec, + nsec, 32); + +/* struct btrfs_dev_extent */ +BTRFS_SETGET_FUNCS(dev_extent_chunk_tree, struct btrfs_dev_extent, + chunk_tree, 64); +BTRFS_SETGET_FUNCS(dev_extent_chunk_objectid, struct btrfs_dev_extent, + chunk_objectid, 64); +BTRFS_SETGET_FUNCS(dev_extent_chunk_offset, struct btrfs_dev_extent, + chunk_offset, 64); +BTRFS_SETGET_FUNCS(dev_extent_length, struct btrfs_dev_extent, length, 64); + +static inline u8 *btrfs_dev_extent_chunk_tree_uuid(struct btrfs_dev_extent *dev) +{ + unsigned long ptr = offsetof(struct btrfs_dev_extent, chunk_tree_uuid); + return (u8 *)((unsigned long)dev + ptr); +} + + +/* struct btrfs_extent_item */ +BTRFS_SETGET_FUNCS(extent_refs, struct btrfs_extent_item, refs, 64); +BTRFS_SETGET_FUNCS(extent_generation, struct btrfs_extent_item, + generation, 64); +BTRFS_SETGET_FUNCS(extent_flags, struct btrfs_extent_item, flags, 64); + +BTRFS_SETGET_FUNCS(extent_refs_v0, struct btrfs_extent_item_v0, refs, 32); + +BTRFS_SETGET_FUNCS(tree_block_level, struct btrfs_tree_block_info, level, 8); + +static inline void btrfs_tree_block_key(struct extent_buffer *eb, + struct btrfs_tree_block_info *item, + struct btrfs_disk_key *key) +{ + read_eb_member(eb, item, struct btrfs_tree_block_info, key, key); +} + +static inline void btrfs_set_tree_block_key(struct extent_buffer *eb, + struct btrfs_tree_block_info *item, + struct btrfs_disk_key *key) +{ + write_eb_member(eb, item, struct btrfs_tree_block_info, key, key); +} + +BTRFS_SETGET_FUNCS(extent_data_ref_root, struct btrfs_extent_data_ref, + root, 64); +BTRFS_SETGET_FUNCS(extent_data_ref_objectid, struct btrfs_extent_data_ref, + objectid, 64); +BTRFS_SETGET_FUNCS(extent_data_ref_offset, struct btrfs_extent_data_ref, + offset, 64); +BTRFS_SETGET_FUNCS(extent_data_ref_count, struct btrfs_extent_data_ref, + count, 32); + +BTRFS_SETGET_FUNCS(shared_data_ref_count, struct btrfs_shared_data_ref, + count, 32); + +BTRFS_SETGET_FUNCS(extent_inline_ref_type, struct btrfs_extent_inline_ref, + type, 8); +BTRFS_SETGET_FUNCS(extent_inline_ref_offset, struct btrfs_extent_inline_ref, + offset, 64); + +static inline u32 btrfs_extent_inline_ref_size(int type) +{ + if (type == BTRFS_TREE_BLOCK_REF_KEY || + type == BTRFS_SHARED_BLOCK_REF_KEY) + return sizeof(struct btrfs_extent_inline_ref); + if (type == BTRFS_SHARED_DATA_REF_KEY) + return sizeof(struct btrfs_shared_data_ref) + + sizeof(struct btrfs_extent_inline_ref); + if (type == BTRFS_EXTENT_DATA_REF_KEY) + return sizeof(struct btrfs_extent_data_ref) + + offsetof(struct btrfs_extent_inline_ref, offset); + BUG(); + return 0; +} + +BTRFS_SETGET_FUNCS(ref_root_v0, struct btrfs_extent_ref_v0, root, 64); +BTRFS_SETGET_FUNCS(ref_generation_v0, struct btrfs_extent_ref_v0, + generation, 64); +BTRFS_SETGET_FUNCS(ref_objectid_v0, struct btrfs_extent_ref_v0, objectid, 64); +BTRFS_SETGET_FUNCS(ref_count_v0, struct btrfs_extent_ref_v0, count, 32); + +/* struct btrfs_node */ +BTRFS_SETGET_FUNCS(key_blockptr, struct btrfs_key_ptr, blockptr, 64); +BTRFS_SETGET_FUNCS(key_generation, struct btrfs_key_ptr, generation, 64); + +static inline u64 btrfs_node_blockptr(struct extent_buffer *eb, int nr) +{ + unsigned long ptr; + ptr = offsetof(struct btrfs_node, ptrs) + + sizeof(struct btrfs_key_ptr) * nr; + return btrfs_key_blockptr(eb, (struct btrfs_key_ptr *)ptr); +} + +static inline void btrfs_set_node_blockptr(struct extent_buffer *eb, + int nr, u64 val) +{ + unsigned long ptr; + ptr = offsetof(struct btrfs_node, ptrs) + + sizeof(struct btrfs_key_ptr) * nr; + btrfs_set_key_blockptr(eb, (struct btrfs_key_ptr *)ptr, val); +} + +static inline u64 btrfs_node_ptr_generation(struct extent_buffer *eb, int nr) +{ + unsigned long ptr; + ptr = offsetof(struct btrfs_node, ptrs) + + sizeof(struct btrfs_key_ptr) * nr; + return btrfs_key_generation(eb, (struct btrfs_key_ptr *)ptr); +} + +static inline void btrfs_set_node_ptr_generation(struct extent_buffer *eb, + int nr, u64 val) +{ + unsigned long ptr; + ptr = offsetof(struct btrfs_node, ptrs) + + sizeof(struct btrfs_key_ptr) * nr; + btrfs_set_key_generation(eb, (struct btrfs_key_ptr *)ptr, val); +} + +static inline unsigned long btrfs_node_key_ptr_offset(int nr) +{ + return offsetof(struct btrfs_node, ptrs) + + sizeof(struct btrfs_key_ptr) * nr; +} + +static inline void btrfs_node_key(struct extent_buffer *eb, + struct btrfs_disk_key *disk_key, int nr) +{ + unsigned long ptr; + ptr = btrfs_node_key_ptr_offset(nr); + read_eb_member(eb, (struct btrfs_key_ptr *)ptr, + struct btrfs_key_ptr, key, disk_key); +} + +static inline void btrfs_set_node_key(struct extent_buffer *eb, + struct btrfs_disk_key *disk_key, int nr) +{ + unsigned long ptr; + ptr = btrfs_node_key_ptr_offset(nr); + write_eb_member(eb, (struct btrfs_key_ptr *)ptr, + struct btrfs_key_ptr, key, disk_key); +} + +/* struct btrfs_item */ +BTRFS_SETGET_FUNCS(item_offset, struct btrfs_item, offset, 32); +BTRFS_SETGET_FUNCS(item_size, struct btrfs_item, size, 32); + +static inline unsigned long btrfs_item_nr_offset(int nr) +{ + return offsetof(struct btrfs_leaf, items) + + sizeof(struct btrfs_item) * nr; +} + +static inline struct btrfs_item *btrfs_item_nr(struct extent_buffer *eb, + int nr) +{ + return (struct btrfs_item *)btrfs_item_nr_offset(nr); +} + +static inline u32 btrfs_item_end(struct extent_buffer *eb, + struct btrfs_item *item) +{ + return btrfs_item_offset(eb, item) + btrfs_item_size(eb, item); +} + +static inline u32 btrfs_item_end_nr(struct extent_buffer *eb, int nr) +{ + return btrfs_item_end(eb, btrfs_item_nr(eb, nr)); +} + +static inline u32 btrfs_item_offset_nr(struct extent_buffer *eb, int nr) +{ + return btrfs_item_offset(eb, btrfs_item_nr(eb, nr)); +} + +static inline u32 btrfs_item_size_nr(struct extent_buffer *eb, int nr) +{ + return btrfs_item_size(eb, btrfs_item_nr(eb, nr)); +} + +static inline void btrfs_item_key(struct extent_buffer *eb, + struct btrfs_disk_key *disk_key, int nr) +{ + struct btrfs_item *item = btrfs_item_nr(eb, nr); + read_eb_member(eb, item, struct btrfs_item, key, disk_key); +} + +static inline void btrfs_set_item_key(struct extent_buffer *eb, + struct btrfs_disk_key *disk_key, int nr) +{ + struct btrfs_item *item = btrfs_item_nr(eb, nr); + write_eb_member(eb, item, struct btrfs_item, key, disk_key); +} + +BTRFS_SETGET_FUNCS(dir_log_end, struct btrfs_dir_log_item, end, 64); + +/* + * struct btrfs_root_ref + */ +BTRFS_SETGET_FUNCS(root_ref_dirid, struct btrfs_root_ref, dirid, 64); +BTRFS_SETGET_FUNCS(root_ref_sequence, struct btrfs_root_ref, sequence, 64); +BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16); + +/* struct btrfs_dir_item */ +BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16); +BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8); +BTRFS_SETGET_FUNCS(dir_name_len, struct btrfs_dir_item, name_len, 16); +BTRFS_SETGET_FUNCS(dir_transid, struct btrfs_dir_item, transid, 64); + +static inline void btrfs_dir_item_key(struct extent_buffer *eb, + struct btrfs_dir_item *item, + struct btrfs_disk_key *key) +{ + read_eb_member(eb, item, struct btrfs_dir_item, location, key); +} + +static inline void btrfs_set_dir_item_key(struct extent_buffer *eb, + struct btrfs_dir_item *item, + struct btrfs_disk_key *key) +{ + write_eb_member(eb, item, struct btrfs_dir_item, location, key); +} + +/* struct btrfs_disk_key */ +BTRFS_SETGET_STACK_FUNCS(disk_key_objectid, struct btrfs_disk_key, + objectid, 64); +BTRFS_SETGET_STACK_FUNCS(disk_key_offset, struct btrfs_disk_key, offset, 64); +BTRFS_SETGET_STACK_FUNCS(disk_key_type, struct btrfs_disk_key, type, 8); + +static inline void btrfs_disk_key_to_cpu(struct btrfs_key *cpu, + struct btrfs_disk_key *disk) +{ + cpu->offset = le64_to_cpu(disk->offset); + cpu->type = disk->type; + cpu->objectid = le64_to_cpu(disk->objectid); +} + +static inline void btrfs_cpu_key_to_disk(struct btrfs_disk_key *disk, + struct btrfs_key *cpu) +{ + disk->offset = cpu_to_le64(cpu->offset); + disk->type = cpu->type; + disk->objectid = cpu_to_le64(cpu->objectid); +} + +static inline void btrfs_node_key_to_cpu(struct extent_buffer *eb, + struct btrfs_key *key, int nr) +{ + struct btrfs_disk_key disk_key; + btrfs_node_key(eb, &disk_key, nr); + btrfs_disk_key_to_cpu(key, &disk_key); +} + +static inline void btrfs_item_key_to_cpu(struct extent_buffer *eb, + struct btrfs_key *key, int nr) +{ + struct btrfs_disk_key disk_key; + btrfs_item_key(eb, &disk_key, nr); + btrfs_disk_key_to_cpu(key, &disk_key); +} + +static inline void btrfs_dir_item_key_to_cpu(struct extent_buffer *eb, + struct btrfs_dir_item *item, + struct btrfs_key *key) +{ + struct btrfs_disk_key disk_key; + btrfs_dir_item_key(eb, item, &disk_key); + btrfs_disk_key_to_cpu(key, &disk_key); +} + + +static inline u8 btrfs_key_type(struct btrfs_key *key) +{ + return key->type; +} + +static inline void btrfs_set_key_type(struct btrfs_key *key, u8 val) +{ + key->type = val; +} + +/* struct btrfs_header */ +BTRFS_SETGET_HEADER_FUNCS(header_bytenr, struct btrfs_header, bytenr, 64); +BTRFS_SETGET_HEADER_FUNCS(header_generation, struct btrfs_header, + generation, 64); +BTRFS_SETGET_HEADER_FUNCS(header_owner, struct btrfs_header, owner, 64); +BTRFS_SETGET_HEADER_FUNCS(header_nritems, struct btrfs_header, nritems, 32); +BTRFS_SETGET_HEADER_FUNCS(header_flags, struct btrfs_header, flags, 64); +BTRFS_SETGET_HEADER_FUNCS(header_level, struct btrfs_header, level, 8); + +static inline int btrfs_header_flag(struct extent_buffer *eb, u64 flag) +{ + return (btrfs_header_flags(eb) & flag) == flag; +} + +static inline int btrfs_set_header_flag(struct extent_buffer *eb, u64 flag) +{ + u64 flags = btrfs_header_flags(eb); + btrfs_set_header_flags(eb, flags | flag); + return (flags & flag) == flag; +} + +static inline int btrfs_clear_header_flag(struct extent_buffer *eb, u64 flag) +{ + u64 flags = btrfs_header_flags(eb); + btrfs_set_header_flags(eb, flags & ~flag); + return (flags & flag) == flag; +} + +static inline int btrfs_header_backref_rev(struct extent_buffer *eb) +{ + u64 flags = btrfs_header_flags(eb); + return flags >> BTRFS_BACKREF_REV_SHIFT; +} + +static inline void btrfs_set_header_backref_rev(struct extent_buffer *eb, + int rev) +{ + u64 flags = btrfs_header_flags(eb); + flags &= ~BTRFS_BACKREF_REV_MASK; + flags |= (u64)rev << BTRFS_BACKREF_REV_SHIFT; + btrfs_set_header_flags(eb, flags); +} + +static inline u8 *btrfs_header_fsid(struct extent_buffer *eb) +{ + unsigned long ptr = offsetof(struct btrfs_header, fsid); + return (u8 *)ptr; +} + +static inline u8 *btrfs_header_chunk_tree_uuid(struct extent_buffer *eb) +{ + unsigned long ptr = offsetof(struct btrfs_header, chunk_tree_uuid); + return (u8 *)ptr; +} + +static inline u8 *btrfs_super_fsid(struct extent_buffer *eb) +{ + unsigned long ptr = offsetof(struct btrfs_super_block, fsid); + return (u8 *)ptr; +} + +static inline u8 *btrfs_header_csum(struct extent_buffer *eb) +{ + unsigned long ptr = offsetof(struct btrfs_header, csum); + return (u8 *)ptr; +} + +static inline struct btrfs_node *btrfs_buffer_node(struct extent_buffer *eb) +{ + return NULL; +} + +static inline struct btrfs_leaf *btrfs_buffer_leaf(struct extent_buffer *eb) +{ + return NULL; +} + +static inline struct btrfs_header *btrfs_buffer_header(struct extent_buffer *eb) +{ + return NULL; +} + +static inline int btrfs_is_leaf(struct extent_buffer *eb) +{ + return (btrfs_header_level(eb) == 0); +} + +/* struct btrfs_root_item */ +BTRFS_SETGET_FUNCS(disk_root_generation, struct btrfs_root_item, + generation, 64); +BTRFS_SETGET_FUNCS(disk_root_refs, struct btrfs_root_item, refs, 32); +BTRFS_SETGET_FUNCS(disk_root_bytenr, struct btrfs_root_item, bytenr, 64); +BTRFS_SETGET_FUNCS(disk_root_level, struct btrfs_root_item, level, 8); + +BTRFS_SETGET_STACK_FUNCS(root_generation, struct btrfs_root_item, + generation, 64); +BTRFS_SETGET_STACK_FUNCS(root_bytenr, struct btrfs_root_item, bytenr, 64); +BTRFS_SETGET_STACK_FUNCS(root_level, struct btrfs_root_item, level, 8); +BTRFS_SETGET_STACK_FUNCS(root_dirid, struct btrfs_root_item, root_dirid, 64); +BTRFS_SETGET_STACK_FUNCS(root_refs, struct btrfs_root_item, refs, 32); +BTRFS_SETGET_STACK_FUNCS(root_flags, struct btrfs_root_item, flags, 64); +BTRFS_SETGET_STACK_FUNCS(root_used, struct btrfs_root_item, bytes_used, 64); +BTRFS_SETGET_STACK_FUNCS(root_limit, struct btrfs_root_item, byte_limit, 64); +BTRFS_SETGET_STACK_FUNCS(root_last_snapshot, struct btrfs_root_item, + last_snapshot, 64); + + +/* struct btrfs_super_block */ + +BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64); +BTRFS_SETGET_STACK_FUNCS(super_flags, struct btrfs_super_block, flags, 64); +BTRFS_SETGET_STACK_FUNCS(super_generation, struct btrfs_super_block, + generation, 64); +BTRFS_SETGET_STACK_FUNCS(super_root, struct btrfs_super_block, root, 64); +BTRFS_SETGET_STACK_FUNCS(super_sys_array_size, + struct btrfs_super_block, sys_chunk_array_size, 32); +BTRFS_SETGET_STACK_FUNCS(super_chunk_root_generation, + struct btrfs_super_block, chunk_root_generation, 64); +BTRFS_SETGET_STACK_FUNCS(super_root_level, struct btrfs_super_block, + root_level, 8); +BTRFS_SETGET_STACK_FUNCS(super_chunk_root, struct btrfs_super_block, + chunk_root, 64); +BTRFS_SETGET_STACK_FUNCS(super_chunk_root_level, struct btrfs_super_block, + chunk_root_level, 8); +BTRFS_SETGET_STACK_FUNCS(super_log_root, struct btrfs_super_block, + log_root, 64); +BTRFS_SETGET_STACK_FUNCS(super_log_root_transid, struct btrfs_super_block, + log_root_transid, 64); +BTRFS_SETGET_STACK_FUNCS(super_log_root_level, struct btrfs_super_block, + log_root_level, 8); +BTRFS_SETGET_STACK_FUNCS(super_total_bytes, struct btrfs_super_block, + total_bytes, 64); +BTRFS_SETGET_STACK_FUNCS(super_bytes_used, struct btrfs_super_block, + bytes_used, 64); +BTRFS_SETGET_STACK_FUNCS(super_sectorsize, struct btrfs_super_block, + sectorsize, 32); +BTRFS_SETGET_STACK_FUNCS(super_nodesize, struct btrfs_super_block, + nodesize, 32); +BTRFS_SETGET_STACK_FUNCS(super_leafsize, struct btrfs_super_block, + leafsize, 32); +BTRFS_SETGET_STACK_FUNCS(super_stripesize, struct btrfs_super_block, + stripesize, 32); +BTRFS_SETGET_STACK_FUNCS(super_root_dir, struct btrfs_super_block, + root_dir_objectid, 64); +BTRFS_SETGET_STACK_FUNCS(super_num_devices, struct btrfs_super_block, + num_devices, 64); +BTRFS_SETGET_STACK_FUNCS(super_compat_flags, struct btrfs_super_block, + compat_flags, 64); +BTRFS_SETGET_STACK_FUNCS(super_compat_ro_flags, struct btrfs_super_block, + compat_flags, 64); +BTRFS_SETGET_STACK_FUNCS(super_incompat_flags, struct btrfs_super_block, + incompat_flags, 64); +BTRFS_SETGET_STACK_FUNCS(super_csum_type, struct btrfs_super_block, + csum_type, 16); + +static inline int btrfs_super_csum_size(struct btrfs_super_block *s) +{ + int t = btrfs_super_csum_type(s); + BUG_ON(t >= ARRAY_SIZE(btrfs_csum_sizes)); + return btrfs_csum_sizes[t]; +} + +static inline unsigned long btrfs_leaf_data(struct extent_buffer *l) +{ + return offsetof(struct btrfs_leaf, items); +} + +/* struct btrfs_file_extent_item */ +BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8); + +static inline unsigned long btrfs_file_extent_inline_start(struct + btrfs_file_extent_item *e) +{ + unsigned long offset = (unsigned long)e; + offset += offsetof(struct btrfs_file_extent_item, disk_bytenr); + return offset; +} + +static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize) +{ + return offsetof(struct btrfs_file_extent_item, disk_bytenr) + datasize; +} + +BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item, + disk_bytenr, 64); +BTRFS_SETGET_FUNCS(file_extent_generation, struct btrfs_file_extent_item, + generation, 64); +BTRFS_SETGET_FUNCS(file_extent_disk_num_bytes, struct btrfs_file_extent_item, + disk_num_bytes, 64); +BTRFS_SETGET_FUNCS(file_extent_offset, struct btrfs_file_extent_item, + offset, 64); +BTRFS_SETGET_FUNCS(file_extent_num_bytes, struct btrfs_file_extent_item, + num_bytes, 64); +BTRFS_SETGET_FUNCS(file_extent_ram_bytes, struct btrfs_file_extent_item, + ram_bytes, 64); +BTRFS_SETGET_FUNCS(file_extent_compression, struct btrfs_file_extent_item, + compression, 8); +BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item, + encryption, 8); +BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item, + other_encoding, 16); + +/* this returns the number of file bytes represented by the inline item. + * If an item is compressed, this is the uncompressed size + */ +static inline u32 btrfs_file_extent_inline_len(struct extent_buffer *eb, + struct btrfs_file_extent_item *e) +{ + return btrfs_file_extent_ram_bytes(eb, e); +} + +/* + * this returns the number of bytes used by the item on disk, minus the + * size of any extent headers. If a file is compressed on disk, this is + * the compressed size + */ +static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb, + struct btrfs_item *e) +{ + unsigned long offset; + offset = offsetof(struct btrfs_file_extent_item, disk_bytenr); + return btrfs_item_size(eb, e) - offset; +} + +static inline u32 btrfs_level_size(struct btrfs_root *root, int level) { + if (level == 0) + return root->leafsize; + return root->nodesize; +} + +/* helper function to cast into the data area of the leaf. */ +#define btrfs_item_ptr(leaf, slot, type) \ + ((type *)(btrfs_leaf_data(leaf) + \ + btrfs_item_offset_nr(leaf, slot))) + +#define btrfs_item_ptr_offset(leaf, slot) \ + ((unsigned long)(btrfs_leaf_data(leaf) + \ + btrfs_item_offset_nr(leaf, slot))) + +/* extent-tree.c */ +int btrfs_extent_post_op(struct btrfs_trans_handle *trans, + struct btrfs_root *root); +int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy); +struct btrfs_block_group_cache *btrfs_lookup_block_group(struct + btrfs_fs_info *info, + u64 bytenr); +struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, + struct btrfs_block_group_cache + *hint, u64 search_start, + int data, int owner); +struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u32 blocksize, u64 root_objectid, + struct btrfs_disk_key *key, int level, + u64 hint, u64 empty_size); +int btrfs_alloc_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 num_bytes, u64 parent, + u64 root_objectid, u64 ref_generation, + u64 owner, u64 empty_size, u64 hint_byte, + u64 search_end, struct btrfs_key *ins, int data); +int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytenr, + u64 num_bytes, u64 *refs, u64 *flags); +int btrfs_set_block_flags(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 flags); +int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct extent_buffer *buf, int record_parent); +int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct extent_buffer *buf, int record_parent); +int btrfs_free_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 parent, + u64 root_objectid, u64 owner, u64 offset); +int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_io_tree *unpin); +int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 parent, + u64 root_objectid, u64 ref_generation, + u64 owner_objectid); +int btrfs_update_extent_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytenr, + u64 orig_parent, u64 parent, + u64 root_objectid, u64 ref_generation, + u64 owner_objectid); +int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, + struct btrfs_root *root); +int btrfs_free_block_groups(struct btrfs_fs_info *info); +int btrfs_read_block_groups(struct btrfs_root *root); +int btrfs_make_block_group(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytes_used, + u64 type, u64 chunk_objectid, u64 chunk_offset, + u64 size); +int btrfs_make_block_groups(struct btrfs_trans_handle *trans, + struct btrfs_root *root); +int btrfs_update_block_group(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytenr, u64 num, + int alloc, int mark_free); +/* ctree.c */ +int btrfs_previous_item(struct btrfs_root *root, + struct btrfs_path *path, u64 min_objectid, + int type); +int btrfs_comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2); +int btrfs_cow_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *buf, + struct extent_buffer *parent, int parent_slot, + struct extent_buffer **cow_ret); +int __btrfs_cow_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf, + struct extent_buffer *parent, int parent_slot, + struct extent_buffer **cow_ret, + u64 search_start, u64 empty_size); +int btrfs_copy_root(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf, + struct extent_buffer **cow_ret, u64 new_root_objectid); +int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, u32 data_size); +int btrfs_truncate_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u32 new_size, int from_end); +int btrfs_split_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_key *new_key, + unsigned long split_offset); +int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *key, struct btrfs_path *p, int + ins_len, int cow); +int btrfs_realloc_node(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *parent, + int start_slot, int cache_only, u64 *last_ret, + struct btrfs_key *progress); +void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p); +struct btrfs_path *btrfs_alloc_path(void); +void btrfs_free_path(struct btrfs_path *p); +void btrfs_init_path(struct btrfs_path *p); +int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct btrfs_path *path, int slot, int nr); + +static inline int btrfs_del_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path) +{ + return btrfs_del_items(trans, root, path, path->slots[0], 1); +} + +int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *key, void *data, u32 data_size); +int btrfs_insert_empty_items(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_key *cpu_key, u32 *data_size, int nr); + +static inline int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_key *key, + u32 data_size) +{ + return btrfs_insert_empty_items(trans, root, path, key, &data_size, 1); +} + +int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path); +int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path); +int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf); +int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root + *root); +int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + struct btrfs_key *new_key); + +/* root-item.c */ +int btrfs_add_root_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *tree_root, + u64 root_id, u8 type, u64 ref_id, + u64 dirid, u64 sequence, + const char *name, int name_len); +int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct btrfs_key *key); +int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *key, struct btrfs_root_item + *item); +int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *key, struct btrfs_root_item + *item); +int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct + btrfs_root_item *item, struct btrfs_key *key); +int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid, + struct btrfs_root *latest_root); +/* dir-item.c */ +int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root + *root, const char *name, int name_len, u64 dir, + struct btrfs_key *location, u8 type, u64 index); +struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + const char *name, int name_len, + int mod); +struct btrfs_dir_item * +btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + u64 objectid, const char *name, int name_len, + int mod); +struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root, + struct btrfs_path *path, + const char *name, int name_len); +int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_dir_item *di); +int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, const char *name, + u16 name_len, const void *data, u16 data_len, + u64 dir); +struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + const char *name, u16 name_len, + int mod); +/* inode-map.c */ +int btrfs_find_free_objectid(struct btrfs_trans_handle *trans, + struct btrfs_root *fs_root, + u64 dirid, u64 *objectid); +int btrfs_find_highest_inode(struct btrfs_root *fs_root, u64 *objectid); + +/* inode-item.c */ +int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + const char *name, int name_len, + u64 inode_objectid, u64 ref_objectid, u64 index); +int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + const char *name, int name_len, + u64 inode_objectid, u64 ref_objectid); +int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 objectid); +int btrfs_insert_inode(struct btrfs_trans_handle *trans, struct btrfs_root + *root, u64 objectid, struct btrfs_inode_item + *inode_item); +int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, + struct btrfs_key *location, int mod); + +/* file-item.c */ +int btrfs_del_csums(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytenr, u64 len); +int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 objectid, u64 pos, u64 offset, + u64 disk_num_bytes, + u64 num_bytes); +int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + u64 offset, char *buffer, size_t size); +int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 objectid, + u64 bytenr, int mod); +int btrfs_csum_file_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 alloc_end, + u64 bytenr, char *data, size_t len); +struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr, int cow); +int btrfs_csum_truncate(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + u64 isize); +#endif diff --git a/lib/dir-item.c b/lib/dir-item.c new file mode 100644 index 0000000..71373b8 --- /dev/null +++ b/lib/dir-item.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2007 Oracle. 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 "ctree.h" +#include "disk-io.h" +#include "hash.h" +#include "transaction.h" + +static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle + *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_key *cpu_key, + u32 data_size, + const char *name, + int name_len) +{ + int ret; + char *ptr; + struct btrfs_item *item; + struct extent_buffer *leaf; + + ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size); + if (ret == -EEXIST) { + struct btrfs_dir_item *di; + di = btrfs_match_dir_item_name(root, path, name, name_len); + if (di) + return ERR_PTR(-EEXIST); + ret = btrfs_extend_item(trans, root, path, data_size); + WARN_ON(ret > 0); + } + if (ret < 0) + return ERR_PTR(ret); + WARN_ON(ret > 0); + leaf = path->nodes[0]; + item = btrfs_item_nr(leaf, path->slots[0]); + ptr = btrfs_item_ptr(leaf, path->slots[0], char); + BUG_ON(data_size > btrfs_item_size(leaf, item)); + ptr += btrfs_item_size(leaf, item) - data_size; + return (struct btrfs_dir_item *)ptr; +} + +int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, const char *name, + u16 name_len, const void *data, u16 data_len, + u64 dir) +{ + int ret = 0; + struct btrfs_path *path; + struct btrfs_dir_item *dir_item; + unsigned long name_ptr, data_ptr; + struct btrfs_key key, location; + struct btrfs_disk_key disk_key; + struct extent_buffer *leaf; + u32 data_size; + + key.objectid = dir; + btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY); + key.offset = btrfs_name_hash(name, name_len); + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + data_size = sizeof(*dir_item) + name_len + data_len; + dir_item = insert_with_overflow(trans, root, path, &key, data_size, + name, name_len); + /* + * FIXME: at some point we should handle xattr's that are larger than + * what we can fit in our leaf. We set location to NULL b/c we arent + * pointing at anything else, that will change if we store the xattr + * data in a separate inode. + */ + BUG_ON(IS_ERR(dir_item)); + memset(&location, 0, sizeof(location)); + + leaf = path->nodes[0]; + btrfs_cpu_key_to_disk(&disk_key, &location); + btrfs_set_dir_item_key(leaf, dir_item, &disk_key); + btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR); + btrfs_set_dir_name_len(leaf, dir_item, name_len); + btrfs_set_dir_data_len(leaf, dir_item, data_len); + name_ptr = (unsigned long)(dir_item + 1); + data_ptr = (unsigned long)((char *)name_ptr + name_len); + + write_extent_buffer(leaf, name, name_ptr, name_len); + write_extent_buffer(leaf, data, data_ptr, data_len); + btrfs_mark_buffer_dirty(path->nodes[0]); + + btrfs_free_path(path); + return ret; +} + +int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root + *root, const char *name, int name_len, u64 dir, + struct btrfs_key *location, u8 type, u64 index) +{ + int ret = 0; + int ret2 = 0; + struct btrfs_path *path; + struct btrfs_dir_item *dir_item; + struct extent_buffer *leaf; + unsigned long name_ptr; + struct btrfs_key key; + struct btrfs_disk_key disk_key; + u32 data_size; + + key.objectid = dir; + btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); + key.offset = btrfs_name_hash(name, name_len); + path = btrfs_alloc_path(); + data_size = sizeof(*dir_item) + name_len; + dir_item = insert_with_overflow(trans, root, path, &key, data_size, + name, name_len); + if (IS_ERR(dir_item)) { + ret = PTR_ERR(dir_item); + goto out; + } + + leaf = path->nodes[0]; + btrfs_cpu_key_to_disk(&disk_key, location); + btrfs_set_dir_item_key(leaf, dir_item, &disk_key); + btrfs_set_dir_type(leaf, dir_item, type); + btrfs_set_dir_data_len(leaf, dir_item, 0); + btrfs_set_dir_name_len(leaf, dir_item, name_len); + name_ptr = (unsigned long)(dir_item + 1); + + write_extent_buffer(leaf, name, name_ptr, name_len); + btrfs_mark_buffer_dirty(leaf); + + /* FIXME, use some real flag for selecting the extra index */ + if (root == root->fs_info->tree_root) { + ret = 0; + goto out; + } + btrfs_release_path(root, path); + + btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY); + key.offset = index; + dir_item = insert_with_overflow(trans, root, path, &key, data_size, + name, name_len); + if (IS_ERR(dir_item)) { + ret2 = PTR_ERR(dir_item); + goto out; + } + leaf = path->nodes[0]; + btrfs_cpu_key_to_disk(&disk_key, location); + btrfs_set_dir_item_key(leaf, dir_item, &disk_key); + btrfs_set_dir_type(leaf, dir_item, type); + btrfs_set_dir_data_len(leaf, dir_item, 0); + btrfs_set_dir_name_len(leaf, dir_item, name_len); + name_ptr = (unsigned long)(dir_item + 1); + write_extent_buffer(leaf, name, name_ptr, name_len); + btrfs_mark_buffer_dirty(leaf); +out: + btrfs_free_path(path); + if (ret) + return ret; + if (ret2) + return ret2; + return 0; +} + +struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + const char *name, int name_len, + int mod) +{ + int ret; + struct btrfs_key key; + int ins_len = mod < 0 ? -1 : 0; + int cow = mod != 0; + struct btrfs_key found_key; + struct extent_buffer *leaf; + + key.objectid = dir; + btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); + + key.offset = btrfs_name_hash(name, name_len); + + ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); + if (ret < 0) + return ERR_PTR(ret); + if (ret > 0) { + if (path->slots[0] == 0) + return NULL; + path->slots[0]--; + } + + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + + if (found_key.objectid != dir || + btrfs_key_type(&found_key) != BTRFS_DIR_ITEM_KEY || + found_key.offset != key.offset) + return NULL; + + return btrfs_match_dir_item_name(root, path, name, name_len); +} + +struct btrfs_dir_item * +btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + u64 objectid, const char *name, int name_len, + int mod) +{ + int ret; + struct btrfs_key key; + int ins_len = mod < 0 ? -1 : 0; + int cow = mod != 0; + + key.objectid = dir; + btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY); + key.offset = objectid; + + ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); + if (ret < 0) + return ERR_PTR(ret); + if (ret > 0) + return ERR_PTR(-ENOENT); + return btrfs_match_dir_item_name(root, path, name, name_len); +} + +struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + const char *name, u16 name_len, + int mod) +{ + int ret; + struct btrfs_key key; + int ins_len = mod < 0 ? -1 : 0; + int cow = mod != 0; + struct btrfs_key found_key; + struct extent_buffer *leaf; + + key.objectid = dir; + btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY); + key.offset = btrfs_name_hash(name, name_len); + ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); + if (ret < 0) + return ERR_PTR(ret); + if (ret > 0) { + if (path->slots[0] == 0) + return NULL; + path->slots[0]--; + } + + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + + if (found_key.objectid != dir || + btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY || + found_key.offset != key.offset) + return NULL; + + return btrfs_match_dir_item_name(root, path, name, name_len); +} + +struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root, + struct btrfs_path *path, + const char *name, int name_len) +{ + struct btrfs_dir_item *dir_item; + unsigned long name_ptr; + u32 total_len; + u32 cur = 0; + u32 this_len; + struct extent_buffer *leaf; + + leaf = path->nodes[0]; + dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); + total_len = btrfs_item_size_nr(leaf, path->slots[0]); + while(cur < total_len) { + this_len = sizeof(*dir_item) + + btrfs_dir_name_len(leaf, dir_item) + + btrfs_dir_data_len(leaf, dir_item); + name_ptr = (unsigned long)(dir_item + 1); + + if (btrfs_dir_name_len(leaf, dir_item) == name_len && + memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) + return dir_item; + + cur += this_len; + dir_item = (struct btrfs_dir_item *)((char *)dir_item + + this_len); + } + return NULL; +} + +int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_dir_item *di) +{ + + struct extent_buffer *leaf; + u32 sub_item_len; + u32 item_len; + int ret = 0; + + leaf = path->nodes[0]; + sub_item_len = sizeof(*di) + btrfs_dir_name_len(leaf, di) + + btrfs_dir_data_len(leaf, di); + item_len = btrfs_item_size_nr(leaf, path->slots[0]); + if (sub_item_len == item_len) { + ret = btrfs_del_item(trans, root, path); + } else { + /* MARKER */ + unsigned long ptr = (unsigned long)di; + unsigned long start; + + start = btrfs_item_ptr_offset(leaf, path->slots[0]); + memmove_extent_buffer(leaf, ptr, ptr + sub_item_len, + item_len - (ptr + sub_item_len - start)); + ret = btrfs_truncate_item(trans, root, path, + item_len - sub_item_len, 1); + } + return 0; +} diff --git a/lib/disk-io.c b/lib/disk-io.c new file mode 100644 index 0000000..addebe1 --- /dev/null +++ b/lib/disk-io.c @@ -0,0 +1,1029 @@ +/* + * Copyright (C) 2007 Oracle. 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 600 +#define __USE_XOPEN2K +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "volumes.h" +#include "transaction.h" +#include "crc32c.h" +#include "utils.h" +#include "print-tree.h" + +static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf) +{ + + struct btrfs_fs_devices *fs_devices; + int ret = 1; + + if (buf->start != btrfs_header_bytenr(buf)) + return ret; + + fs_devices = root->fs_info->fs_devices; + while (fs_devices) { + if (!memcmp_extent_buffer(buf, fs_devices->fsid, + (unsigned long)btrfs_header_fsid(buf), + BTRFS_FSID_SIZE)) { + ret = 0; + break; + } + fs_devices = fs_devices->seed; + } + return ret; +} + +u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len) +{ + return crc32c(seed, data, len); +} + +void btrfs_csum_final(u32 crc, char *result) +{ + *(__le32 *)result = ~cpu_to_le32(crc); +} + +int csum_tree_block_size(struct extent_buffer *buf, u16 csum_size, + int verify) +{ + char *result; + u32 len; + u32 crc = ~(u32)0; + + result = malloc(csum_size * sizeof(char)); + if (!result) + return 1; + + len = buf->len - BTRFS_CSUM_SIZE; + crc = crc32c(crc, buf->data + BTRFS_CSUM_SIZE, len); + btrfs_csum_final(crc, result); + + if (verify) { + if (memcmp_extent_buffer(buf, result, 0, csum_size)) { + printk("checksum verify failed on %llu wanted %X " + "found %X\n", (unsigned long long)buf->start, + *((int *)result), *((int *)buf)); + free(result); + return 1; + } + } else { + write_extent_buffer(buf, result, 0, csum_size); + } + free(result); + return 0; +} + +int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, + int verify) +{ + u16 csum_size = + btrfs_super_csum_size(&root->fs_info->super_copy); + return csum_tree_block_size(buf, csum_size, verify); +} + +struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root, + u64 bytenr, u32 blocksize) +{ + return find_extent_buffer(&root->fs_info->extent_cache, + bytenr, blocksize); +} + +struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root, + u64 bytenr, u32 blocksize) +{ + return alloc_extent_buffer(&root->fs_info->extent_cache, bytenr, + blocksize); +} + +int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, + u64 parent_transid) +{ + int ret; + int dev_nr; + struct extent_buffer *eb; + u64 length; + struct btrfs_multi_bio *multi = NULL; + struct btrfs_device *device; + + eb = btrfs_find_tree_block(root, bytenr, blocksize); + if (eb && btrfs_buffer_uptodate(eb, parent_transid)) { + free_extent_buffer(eb); + return 0; + } + + dev_nr = 0; + length = blocksize; + ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, + bytenr, &length, &multi, 0); + BUG_ON(ret); + device = multi->stripes[0].dev; + device->total_ios++; + blocksize = min(blocksize, (u32)(64 * 1024)); + readahead(device->fd, multi->stripes[0].physical, blocksize); + kfree(multi); + return 0; +} + +static int verify_parent_transid(struct extent_io_tree *io_tree, + struct extent_buffer *eb, u64 parent_transid) +{ + int ret; + + if (!parent_transid || btrfs_header_generation(eb) == parent_transid) + return 0; + + if (extent_buffer_uptodate(eb) && + btrfs_header_generation(eb) == parent_transid) { + ret = 0; + goto out; + } + printk("parent transid verify failed on %llu wanted %llu found %llu\n", + (unsigned long long)eb->start, + (unsigned long long)parent_transid, + (unsigned long long)btrfs_header_generation(eb)); + ret = 1; +out: + clear_extent_buffer_uptodate(io_tree, eb); + return ret; + +} + + +struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, + u32 blocksize, u64 parent_transid) +{ + int ret; + int dev_nr; + struct extent_buffer *eb; + u64 length; + struct btrfs_multi_bio *multi = NULL; + struct btrfs_device *device; + int mirror_num = 0; + int num_copies; + + eb = btrfs_find_create_tree_block(root, bytenr, blocksize); + if (!eb) + return NULL; + + if (btrfs_buffer_uptodate(eb, parent_transid)) + return eb; + + dev_nr = 0; + length = blocksize; + while (1) { + ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, + eb->start, &length, &multi, mirror_num); + BUG_ON(ret); + device = multi->stripes[0].dev; + eb->fd = device->fd; + device->total_ios++; + eb->dev_bytenr = multi->stripes[0].physical; + kfree(multi); + ret = read_extent_from_disk(eb); + if (ret == 0 && check_tree_block(root, eb) == 0 && + csum_tree_block(root, eb, 1) == 0 && + verify_parent_transid(eb->tree, eb, parent_transid) == 0) { + btrfs_set_buffer_uptodate(eb); + return eb; + } + num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, + eb->start, eb->len); + if (num_copies == 1) { + break; + } + mirror_num++; + if (mirror_num > num_copies) { + break; + } + } + free_extent_buffer(eb); + return NULL; +} + +int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct extent_buffer *eb) +{ + int ret; + int dev_nr; + u64 length; + struct btrfs_multi_bio *multi = NULL; + + if (check_tree_block(root, eb)) + BUG(); + if (!btrfs_buffer_uptodate(eb, trans->transid)) + BUG(); + + btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); + csum_tree_block(root, eb, 0); + + dev_nr = 0; + length = eb->len; + ret = btrfs_map_block(&root->fs_info->mapping_tree, WRITE, + eb->start, &length, &multi, 0); + + while(dev_nr < multi->num_stripes) { + BUG_ON(ret); + eb->fd = multi->stripes[dev_nr].dev->fd; + eb->dev_bytenr = multi->stripes[dev_nr].physical; + multi->stripes[dev_nr].dev->total_ios++; + dev_nr++; + ret = write_extent_to_disk(eb); + BUG_ON(ret); + } + kfree(multi); + return 0; +} + +static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, + u32 stripesize, struct btrfs_root *root, + struct btrfs_fs_info *fs_info, u64 objectid) +{ + root->node = NULL; + root->commit_root = NULL; + root->sectorsize = sectorsize; + root->nodesize = nodesize; + root->leafsize = leafsize; + root->stripesize = stripesize; + root->ref_cows = 0; + root->track_dirty = 0; + + root->fs_info = fs_info; + root->objectid = objectid; + root->last_trans = 0; + root->highest_inode = 0; + root->last_inode_alloc = 0; + + INIT_LIST_HEAD(&root->dirty_list); + memset(&root->root_key, 0, sizeof(root->root_key)); + memset(&root->root_item, 0, sizeof(root->root_item)); + root->root_key.objectid = objectid; + return 0; +} + +static int update_cowonly_root(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + int ret; + u64 old_root_bytenr; + struct btrfs_root *tree_root = root->fs_info->tree_root; + + btrfs_write_dirty_block_groups(trans, root); + while(1) { + old_root_bytenr = btrfs_root_bytenr(&root->root_item); + if (old_root_bytenr == root->node->start) + break; + btrfs_set_root_bytenr(&root->root_item, + root->node->start); + btrfs_set_root_generation(&root->root_item, + trans->transid); + root->root_item.level = btrfs_header_level(root->node); + ret = btrfs_update_root(trans, tree_root, + &root->root_key, + &root->root_item); + BUG_ON(ret); + btrfs_write_dirty_block_groups(trans, root); + } + return 0; +} + +static int commit_tree_roots(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info) +{ + struct btrfs_root *root; + struct list_head *next; + struct extent_buffer *eb; + + if (fs_info->readonly) + return 0; + + eb = fs_info->tree_root->node; + extent_buffer_get(eb); + btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); + free_extent_buffer(eb); + + while(!list_empty(&fs_info->dirty_cowonly_roots)) { + next = fs_info->dirty_cowonly_roots.next; + list_del_init(next); + root = list_entry(next, struct btrfs_root, dirty_list); + update_cowonly_root(trans, root); + } + return 0; +} + +static int __commit_transaction(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + u64 start; + u64 end; + struct extent_buffer *eb; + struct extent_io_tree *tree = &root->fs_info->extent_cache; + int ret; + + while(1) { + ret = find_first_extent_bit(tree, 0, &start, &end, + EXTENT_DIRTY); + if (ret) + break; + while(start <= end) { + eb = find_first_extent_buffer(tree, start); + BUG_ON(!eb || eb->start != start); + ret = write_tree_block(trans, root, eb); + BUG_ON(ret); + start += eb->len; + clear_extent_buffer_dirty(eb); + free_extent_buffer(eb); + } + } + return 0; +} + +int btrfs_commit_transaction(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + int ret = 0; + struct btrfs_fs_info *fs_info = root->fs_info; + + if (root->commit_root == root->node) + goto commit_tree; + + free_extent_buffer(root->commit_root); + root->commit_root = NULL; + + btrfs_set_root_bytenr(&root->root_item, root->node->start); + btrfs_set_root_generation(&root->root_item, trans->transid); + root->root_item.level = btrfs_header_level(root->node); + ret = btrfs_update_root(trans, root->fs_info->tree_root, + &root->root_key, &root->root_item); + BUG_ON(ret); +commit_tree: + ret = commit_tree_roots(trans, fs_info); + BUG_ON(ret); + ret = __commit_transaction(trans, root); + BUG_ON(ret); + write_ctree_super(trans, root); + btrfs_finish_extent_commit(trans, fs_info->extent_root, + &fs_info->pinned_extents); + btrfs_free_transaction(root, trans); + free_extent_buffer(root->commit_root); + root->commit_root = NULL; + fs_info->running_transaction = NULL; + return 0; +} + +static int find_and_setup_root(struct btrfs_root *tree_root, + struct btrfs_fs_info *fs_info, + u64 objectid, struct btrfs_root *root) +{ + int ret; + u32 blocksize; + u64 generation; + + __setup_root(tree_root->nodesize, tree_root->leafsize, + tree_root->sectorsize, tree_root->stripesize, + root, fs_info, objectid); + ret = btrfs_find_last_root(tree_root, objectid, + &root->root_item, &root->root_key); + BUG_ON(ret); + + blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); + generation = btrfs_root_generation(&root->root_item); + root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), + blocksize, generation); + BUG_ON(!root->node); + return 0; +} + +static int find_and_setup_log_root(struct btrfs_root *tree_root, + struct btrfs_fs_info *fs_info, + struct btrfs_super_block *disk_super) +{ + u32 blocksize; + u64 blocknr = btrfs_super_log_root(disk_super); + struct btrfs_root *log_root = malloc(sizeof(struct btrfs_root)); + + if (blocknr == 0) + return 0; + + blocksize = btrfs_level_size(tree_root, + btrfs_super_log_root_level(disk_super)); + + __setup_root(tree_root->nodesize, tree_root->leafsize, + tree_root->sectorsize, tree_root->stripesize, + log_root, fs_info, BTRFS_TREE_LOG_OBJECTID); + + log_root->node = read_tree_block(tree_root, blocknr, + blocksize, + btrfs_super_generation(disk_super) + 1); + + fs_info->log_root_tree = log_root; + BUG_ON(!log_root->node); + return 0; +} + + +int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, + struct btrfs_root *root) +{ + if (root->node) + free_extent_buffer(root->node); + if (root->commit_root) + free_extent_buffer(root->commit_root); + kfree(root); + return 0; +} + +static int free_fs_roots(struct btrfs_fs_info *fs_info) +{ + struct cache_extent *cache; + struct btrfs_root *root; + + while (1) { + cache = find_first_cache_extent(&fs_info->fs_root_cache, 0); + if (!cache) + break; + root = container_of(cache, struct btrfs_root, cache); + remove_cache_extent(&fs_info->fs_root_cache, cache); + btrfs_free_fs_root(fs_info, root); + } + return 0; +} + +struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info, + struct btrfs_key *location) +{ + struct btrfs_root *root; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_path *path; + struct extent_buffer *l; + u64 generation; + u32 blocksize; + int ret = 0; + + root = malloc(sizeof(*root)); + if (!root) + return ERR_PTR(-ENOMEM); + memset(root, 0, sizeof(*root)); + if (location->offset == (u64)-1) { + ret = find_and_setup_root(tree_root, fs_info, + location->objectid, root); + if (ret) { + free(root); + return ERR_PTR(ret); + } + goto insert; + } + + __setup_root(tree_root->nodesize, tree_root->leafsize, + tree_root->sectorsize, tree_root->stripesize, + root, fs_info, location->objectid); + + path = btrfs_alloc_path(); + BUG_ON(!path); + ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0); + if (ret != 0) { + if (ret > 0) + ret = -ENOENT; + goto out; + } + l = path->nodes[0]; + read_extent_buffer(l, &root->root_item, + btrfs_item_ptr_offset(l, path->slots[0]), + sizeof(root->root_item)); + memcpy(&root->root_key, location, sizeof(*location)); + ret = 0; +out: + btrfs_release_path(root, path); + btrfs_free_path(path); + if (ret) { + free(root); + return ERR_PTR(ret); + } + generation = btrfs_root_generation(&root->root_item); + blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); + root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), + blocksize, generation); + BUG_ON(!root->node); +insert: + root->ref_cows = 1; + return root; +} + +struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, + struct btrfs_key *location) +{ + struct btrfs_root *root; + struct cache_extent *cache; + int ret; + + if (location->objectid == BTRFS_ROOT_TREE_OBJECTID) + return fs_info->tree_root; + if (location->objectid == BTRFS_EXTENT_TREE_OBJECTID) + return fs_info->extent_root; + if (location->objectid == BTRFS_CHUNK_TREE_OBJECTID) + return fs_info->chunk_root; + if (location->objectid == BTRFS_DEV_TREE_OBJECTID) + return fs_info->dev_root; + if (location->objectid == BTRFS_CSUM_TREE_OBJECTID) + return fs_info->csum_root; + + BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID || + location->offset != (u64)-1); + + cache = find_cache_extent(&fs_info->fs_root_cache, + location->objectid, 1); + if (cache) + return container_of(cache, struct btrfs_root, cache); + + root = btrfs_read_fs_root_no_cache(fs_info, location); + if (IS_ERR(root)) + return root; + + root->cache.start = location->objectid; + root->cache.size = 1; + ret = insert_existing_cache_extent(&fs_info->fs_root_cache, + &root->cache); + BUG_ON(ret); + return root; +} + +struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes) +{ + int fp; + struct btrfs_root *root; + int flags = O_CREAT | O_RDWR; + + if (!writes) + flags = O_RDONLY; + + fp = open(filename, flags, 0600); + if (fp < 0) { + fprintf (stderr, "Could not open %s\n", filename); + return NULL; + } + root = open_ctree_fd(fp, filename, sb_bytenr, writes); + close(fp); + + return root; +} + +struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, + int writes) +{ + u32 sectorsize; + u32 nodesize; + u32 leafsize; + u32 blocksize; + u32 stripesize; + u64 generation; + struct btrfs_key key; + struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root)); + struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info)); + int ret; + struct btrfs_super_block *disk_super; + struct btrfs_fs_devices *fs_devices = NULL; + u64 total_devs; + u64 features; + + if (sb_bytenr == 0) + sb_bytenr = BTRFS_SUPER_INFO_OFFSET; + + ret = btrfs_scan_one_device(fp, path, &fs_devices, + &total_devs, sb_bytenr); + + if (ret) { + fprintf(stderr, "No valid Btrfs found on %s\n", path); + return NULL; + } + + if (total_devs != 1) { + ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1); + BUG_ON(ret); + } + + memset(fs_info, 0, sizeof(*fs_info)); + fs_info->tree_root = tree_root; + fs_info->extent_root = extent_root; + fs_info->chunk_root = chunk_root; + fs_info->dev_root = dev_root; + fs_info->csum_root = csum_root; + + if (!writes) + fs_info->readonly = 1; + + extent_io_tree_init(&fs_info->extent_cache); + extent_io_tree_init(&fs_info->free_space_cache); + extent_io_tree_init(&fs_info->block_group_cache); + extent_io_tree_init(&fs_info->pinned_extents); + extent_io_tree_init(&fs_info->pending_del); + extent_io_tree_init(&fs_info->extent_ins); + cache_tree_init(&fs_info->fs_root_cache); + + cache_tree_init(&fs_info->mapping_tree.cache_tree); + + mutex_init(&fs_info->fs_mutex); + fs_info->fs_devices = fs_devices; + INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); + INIT_LIST_HEAD(&fs_info->space_info); + + __setup_root(4096, 4096, 4096, 4096, tree_root, + fs_info, BTRFS_ROOT_TREE_OBJECTID); + + if (writes) + ret = btrfs_open_devices(fs_devices, O_RDWR); + else + ret = btrfs_open_devices(fs_devices, O_RDONLY); + BUG_ON(ret); + + fs_info->super_bytenr = sb_bytenr; + disk_super = &fs_info->super_copy; + ret = btrfs_read_dev_super(fs_devices->latest_bdev, + disk_super, sb_bytenr); + if (ret) { + printk("No valid btrfs found\n"); + BUG_ON(1); + } + + memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); + + + features = btrfs_super_incompat_flags(disk_super) & + ~BTRFS_FEATURE_INCOMPAT_SUPP; + if (features) { + printk("couldn't open because of unsupported " + "option features (%Lx).\n", features); + BUG_ON(1); + } + + features = btrfs_super_incompat_flags(disk_super); + if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) { + features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF; + btrfs_set_super_incompat_flags(disk_super, features); + } + + features = btrfs_super_compat_ro_flags(disk_super) & + ~BTRFS_FEATURE_COMPAT_RO_SUPP; + if (writes && features) { + printk("couldn't open RDWR because of unsupported " + "option features (%Lx).\n", features); + BUG_ON(1); + } + + nodesize = btrfs_super_nodesize(disk_super); + leafsize = btrfs_super_leafsize(disk_super); + sectorsize = btrfs_super_sectorsize(disk_super); + stripesize = btrfs_super_stripesize(disk_super); + tree_root->nodesize = nodesize; + tree_root->leafsize = leafsize; + tree_root->sectorsize = sectorsize; + tree_root->stripesize = stripesize; + + ret = btrfs_read_sys_array(tree_root); + BUG_ON(ret); + blocksize = btrfs_level_size(tree_root, + btrfs_super_chunk_root_level(disk_super)); + generation = btrfs_super_chunk_root_generation(disk_super); + + __setup_root(nodesize, leafsize, sectorsize, stripesize, + chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID); + + chunk_root->node = read_tree_block(chunk_root, + btrfs_super_chunk_root(disk_super), + blocksize, generation); + + BUG_ON(!chunk_root->node); + + read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid, + (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node), + BTRFS_UUID_SIZE); + + if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) { + ret = btrfs_read_chunk_tree(chunk_root); + BUG_ON(ret); + } + + blocksize = btrfs_level_size(tree_root, + btrfs_super_root_level(disk_super)); + generation = btrfs_super_generation(disk_super); + + tree_root->node = read_tree_block(tree_root, + btrfs_super_root(disk_super), + blocksize, generation); + BUG_ON(!tree_root->node); + ret = find_and_setup_root(tree_root, fs_info, + BTRFS_EXTENT_TREE_OBJECTID, extent_root); + BUG_ON(ret); + extent_root->track_dirty = 1; + + ret = find_and_setup_root(tree_root, fs_info, + BTRFS_DEV_TREE_OBJECTID, dev_root); + BUG_ON(ret); + dev_root->track_dirty = 1; + + ret = find_and_setup_root(tree_root, fs_info, + BTRFS_CSUM_TREE_OBJECTID, csum_root); + BUG_ON(ret); + csum_root->track_dirty = 1; + + BUG_ON(ret); + + find_and_setup_log_root(tree_root, fs_info, disk_super); + + fs_info->generation = generation + 1; + btrfs_read_block_groups(fs_info->tree_root); + + key.objectid = BTRFS_FS_TREE_OBJECTID; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + fs_info->fs_root = btrfs_read_fs_root(fs_info, &key); + + fs_info->data_alloc_profile = (u64)-1; + fs_info->metadata_alloc_profile = (u64)-1; + fs_info->system_alloc_profile = fs_info->metadata_alloc_profile; + + return fs_info->fs_root; +} + +int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr) +{ + u8 fsid[BTRFS_FSID_SIZE]; + struct btrfs_super_block buf; + int i; + int ret; + u64 transid = 0; + u64 bytenr; + + if (sb_bytenr != BTRFS_SUPER_INFO_OFFSET) { + ret = pread64(fd, &buf, sizeof(buf), sb_bytenr); + if (ret < sizeof(buf)) + return -1; + + if (btrfs_super_bytenr(&buf) != sb_bytenr || + strncmp((char *)(&buf.magic), BTRFS_MAGIC, + sizeof(buf.magic))) + return -1; + + memcpy(sb, &buf, sizeof(*sb)); + return 0; + } + + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + bytenr = btrfs_sb_offset(i); + ret = pread64(fd, &buf, sizeof(buf), bytenr); + if (ret < sizeof(buf)) + break; + + if (btrfs_super_bytenr(&buf) != bytenr || + strncmp((char *)(&buf.magic), BTRFS_MAGIC, + sizeof(buf.magic))) + continue; + + if (i == 0) + memcpy(fsid, buf.fsid, sizeof(fsid)); + else if (memcmp(fsid, buf.fsid, sizeof(fsid))) + continue; + + if (btrfs_super_generation(&buf) > transid) { + memcpy(sb, &buf, sizeof(*sb)); + transid = btrfs_super_generation(&buf); + } + } + + return transid > 0 ? 0 : -1; +} + +int write_dev_supers(struct btrfs_root *root, struct btrfs_super_block *sb, + struct btrfs_device *device) +{ + u64 bytenr; + u32 crc; + int i, ret; + + if (root->fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) { + btrfs_set_super_bytenr(sb, root->fs_info->super_bytenr); + + 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, (char *)&sb->csum[0]); + + ret = pwrite64(device->fd, sb, BTRFS_SUPER_INFO_SIZE, + root->fs_info->super_bytenr); + BUG_ON(ret != BTRFS_SUPER_INFO_SIZE); + return 0; + } + + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + bytenr = btrfs_sb_offset(i); + if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes) + break; + + btrfs_set_super_bytenr(sb, bytenr); + + 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, (char *)&sb->csum[0]); + + ret = pwrite64(device->fd, sb, BTRFS_SUPER_INFO_SIZE, bytenr); + BUG_ON(ret != BTRFS_SUPER_INFO_SIZE); + } + return 0; +} + +int write_all_supers(struct btrfs_root *root) +{ + struct list_head *cur; + struct list_head *head = &root->fs_info->fs_devices->devices; + struct btrfs_device *dev; + struct btrfs_super_block *sb; + struct btrfs_dev_item *dev_item; + int ret; + u64 flags; + + sb = &root->fs_info->super_copy; + dev_item = &sb->dev_item; + list_for_each(cur, head) { + dev = list_entry(cur, struct btrfs_device, dev_list); + if (!dev->writeable) + continue; + + btrfs_set_stack_device_generation(dev_item, 0); + btrfs_set_stack_device_type(dev_item, dev->type); + btrfs_set_stack_device_id(dev_item, dev->devid); + btrfs_set_stack_device_total_bytes(dev_item, dev->total_bytes); + btrfs_set_stack_device_bytes_used(dev_item, dev->bytes_used); + btrfs_set_stack_device_io_align(dev_item, dev->io_align); + btrfs_set_stack_device_io_width(dev_item, dev->io_width); + btrfs_set_stack_device_sector_size(dev_item, dev->sector_size); + memcpy(dev_item->uuid, dev->uuid, BTRFS_UUID_SIZE); + memcpy(dev_item->fsid, dev->fs_devices->fsid, BTRFS_UUID_SIZE); + + flags = btrfs_super_flags(sb); + btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN); + + ret = write_dev_supers(root, sb, dev); + BUG_ON(ret); + } + return 0; +} + +int write_ctree_super(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + int ret; + struct btrfs_root *tree_root = root->fs_info->tree_root; + struct btrfs_root *chunk_root = root->fs_info->chunk_root; + + if (root->fs_info->readonly) + return 0; + + btrfs_set_super_generation(&root->fs_info->super_copy, + trans->transid); + btrfs_set_super_root(&root->fs_info->super_copy, + tree_root->node->start); + btrfs_set_super_root_level(&root->fs_info->super_copy, + btrfs_header_level(tree_root->node)); + btrfs_set_super_chunk_root(&root->fs_info->super_copy, + chunk_root->node->start); + btrfs_set_super_chunk_root_level(&root->fs_info->super_copy, + btrfs_header_level(chunk_root->node)); + btrfs_set_super_chunk_root_generation(&root->fs_info->super_copy, + btrfs_header_generation(chunk_root->node)); + + ret = write_all_supers(root); + if (ret) + fprintf(stderr, "failed to write new super block err %d\n", ret); + return ret; +} + +static int close_all_devices(struct btrfs_fs_info *fs_info) +{ + struct list_head *list; + struct list_head *next; + struct btrfs_device *device; + + return 0; + + list = &fs_info->fs_devices->devices; + list_for_each(next, list) { + device = list_entry(next, struct btrfs_device, dev_list); + close(device->fd); + } + return 0; +} + +int close_ctree(struct btrfs_root *root) +{ + int ret; + struct btrfs_trans_handle *trans; + struct btrfs_fs_info *fs_info = root->fs_info; + + trans = btrfs_start_transaction(root, 1); + btrfs_commit_transaction(trans, root); + trans = btrfs_start_transaction(root, 1); + ret = commit_tree_roots(trans, fs_info); + BUG_ON(ret); + ret = __commit_transaction(trans, root); + BUG_ON(ret); + write_ctree_super(trans, root); + btrfs_free_transaction(root, trans); + btrfs_free_block_groups(fs_info); + + free_fs_roots(fs_info); + + if (fs_info->extent_root->node) + free_extent_buffer(fs_info->extent_root->node); + if (fs_info->tree_root->node) + free_extent_buffer(fs_info->tree_root->node); + if (fs_info->chunk_root->node) + free_extent_buffer(fs_info->chunk_root->node); + if (fs_info->dev_root->node) + free_extent_buffer(fs_info->dev_root->node); + if (fs_info->csum_root->node) + free_extent_buffer(fs_info->csum_root->node); + + if (root->fs_info->log_root_tree) { + if (root->fs_info->log_root_tree->node) + free_extent_buffer(root->fs_info->log_root_tree->node); + free(root->fs_info->log_root_tree); + } + + close_all_devices(root->fs_info); + extent_io_tree_cleanup(&fs_info->extent_cache); + extent_io_tree_cleanup(&fs_info->free_space_cache); + extent_io_tree_cleanup(&fs_info->block_group_cache); + extent_io_tree_cleanup(&fs_info->pinned_extents); + extent_io_tree_cleanup(&fs_info->pending_del); + extent_io_tree_cleanup(&fs_info->extent_ins); + + free(fs_info->tree_root); + free(fs_info->extent_root); + free(fs_info->chunk_root); + free(fs_info->dev_root); + free(fs_info->csum_root); + free(fs_info); + + return 0; +} + +int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct extent_buffer *eb) +{ + return clear_extent_buffer_dirty(eb); +} + +int wait_on_tree_block_writeback(struct btrfs_root *root, + struct extent_buffer *eb) +{ + return 0; +} + +void btrfs_mark_buffer_dirty(struct extent_buffer *eb) +{ + set_extent_buffer_dirty(eb); +} + +int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid) +{ + int ret; + + ret = extent_buffer_uptodate(buf); + if (!ret) + return ret; + + ret = verify_parent_transid(buf->tree, buf, parent_transid); + return !ret; +} + +int btrfs_set_buffer_uptodate(struct extent_buffer *eb) +{ + return set_extent_buffer_uptodate(eb); +} diff --git a/lib/disk-io.h b/lib/disk-io.h new file mode 100644 index 0000000..49e5692 --- /dev/null +++ b/lib/disk-io.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __DISKIO__ +#define __DISKIO__ + +#define BTRFS_SUPER_INFO_OFFSET (64 * 1024) +#define BTRFS_SUPER_INFO_SIZE 4096 + +#define BTRFS_SUPER_MIRROR_MAX 3 +#define BTRFS_SUPER_MIRROR_SHIFT 12 + +static inline u64 btrfs_sb_offset(int mirror) +{ + u64 start = 16 * 1024; + if (mirror) + return start << (BTRFS_SUPER_MIRROR_SHIFT * mirror); + return BTRFS_SUPER_INFO_OFFSET; +} + +struct btrfs_device; + +struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, + u32 blocksize, u64 parent_transid); +int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, + u64 parent_transid); +struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root, + u64 bytenr, u32 blocksize); +int clean_tree_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *buf); +struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes); +struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, + int writes); +int close_ctree(struct btrfs_root *root); +int write_ctree_super(struct btrfs_trans_handle *trans, + struct btrfs_root *root); +int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr); +int btrfs_map_bh_to_logical(struct btrfs_root *root, struct extent_buffer *bh, + u64 logical); +struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root, + u64 bytenr, u32 blocksize); +struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, + struct btrfs_key *location); +struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info, + struct btrfs_key *location); +int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root); +void btrfs_mark_buffer_dirty(struct extent_buffer *buf); +int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid); +int btrfs_set_buffer_uptodate(struct extent_buffer *buf); +int wait_on_tree_block_writeback(struct btrfs_root *root, + struct extent_buffer *buf); +u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len); +void btrfs_csum_final(u32 crc, char *result); + +int btrfs_commit_transaction(struct btrfs_trans_handle *trans, + struct btrfs_root *root); +int btrfs_open_device(struct btrfs_device *dev); +int csum_tree_block_size(struct extent_buffer *buf, u16 csum_sectorsize, + int verify); +int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, + int verify); +int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid); +#endif diff --git a/lib/extent-cache.c b/lib/extent-cache.c new file mode 100644 index 0000000..b871e18 --- /dev/null +++ b/lib/extent-cache.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include "kerncompat.h" +#include "extent-cache.h" + +void cache_tree_init(struct cache_tree *tree) +{ + tree->root.rb_node = NULL; +} + +static struct rb_node *tree_insert(struct rb_root *root, u64 offset, + u64 size, struct rb_node *node) +{ + struct rb_node ** p = &root->rb_node; + struct rb_node * parent = NULL; + struct cache_extent *entry; + + while(*p) { + parent = *p; + entry = rb_entry(parent, struct cache_extent, rb_node); + + if (offset + size <= entry->start) + p = &(*p)->rb_left; + else if (offset >= entry->start + entry->size) + p = &(*p)->rb_right; + else + return parent; + } + + entry = rb_entry(parent, struct cache_extent, rb_node); + rb_link_node(node, parent, p); + rb_insert_color(node, root); + return NULL; +} + +static struct rb_node *__tree_search(struct rb_root *root, u64 offset, + u64 size, struct rb_node **prev_ret) +{ + struct rb_node * n = root->rb_node; + struct rb_node *prev = NULL; + struct cache_extent *entry; + struct cache_extent *prev_entry = NULL; + + while(n) { + entry = rb_entry(n, struct cache_extent, rb_node); + prev = n; + prev_entry = entry; + + if (offset + size <= entry->start) + n = n->rb_left; + else if (offset >= entry->start + entry->size) + n = n->rb_right; + else + return n; + } + if (!prev_ret) + return NULL; + + while(prev && offset >= prev_entry->start + prev_entry->size) { + prev = rb_next(prev); + prev_entry = rb_entry(prev, struct cache_extent, rb_node); + } + *prev_ret = prev; + return NULL; +} + +struct cache_extent *alloc_cache_extent(u64 start, u64 size) +{ + struct cache_extent *pe = malloc(sizeof(*pe)); + + if (!pe) + return pe; + pe->start = start; + pe->size = size; + return pe; +} + +int insert_existing_cache_extent(struct cache_tree *tree, + struct cache_extent *pe) +{ + struct rb_node *found; + struct cache_extent *entry; + + found = tree_insert(&tree->root, pe->start, pe->size, &pe->rb_node); + if (found) { + entry = rb_entry(found, struct cache_extent, rb_node); + return -EEXIST; + } + return 0; +} + +int insert_cache_extent(struct cache_tree *tree, u64 start, u64 size) +{ + struct cache_extent *pe = alloc_cache_extent(start, size); + int ret; + ret = insert_existing_cache_extent(tree, pe); + if (ret) + free(pe); + return ret; +} + +struct cache_extent *find_cache_extent(struct cache_tree *tree, + u64 start, u64 size) +{ + struct rb_node *prev; + struct rb_node *ret; + struct cache_extent *entry; + ret = __tree_search(&tree->root, start, size, &prev); + if (!ret) + return NULL; + + entry = rb_entry(ret, struct cache_extent, rb_node); + return entry; +} + +struct cache_extent *find_first_cache_extent(struct cache_tree *tree, + u64 start) +{ + struct rb_node *prev; + struct rb_node *ret; + struct cache_extent *entry; + + ret = __tree_search(&tree->root, start, 1, &prev); + if (!ret) + ret = prev; + if (!ret) + return NULL; + entry = rb_entry(ret, struct cache_extent, rb_node); + return entry; +} + +struct cache_extent *prev_cache_extent(struct cache_extent *pe) +{ + struct rb_node *node = rb_prev(&pe->rb_node); + + if (!node) + return NULL; + return rb_entry(node, struct cache_extent, rb_node); +} + +struct cache_extent *next_cache_extent(struct cache_extent *pe) +{ + struct rb_node *node = rb_next(&pe->rb_node); + + if (!node) + return NULL; + return rb_entry(node, struct cache_extent, rb_node); +} + +void remove_cache_extent(struct cache_tree *tree, + struct cache_extent *pe) +{ + rb_erase(&pe->rb_node, &tree->root); +} + diff --git a/lib/extent-cache.h b/lib/extent-cache.h new file mode 100644 index 0000000..7f2f2a6 --- /dev/null +++ b/lib/extent-cache.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __PENDING_EXTENT__ +#define __PENDING_EXTENT__ +#include "kerncompat.h" +#include "rbtree.h" + +struct cache_tree { + struct rb_root root; +}; + +struct cache_extent { + struct rb_node rb_node; + u64 start; + u64 size; +}; + +void cache_tree_init(struct cache_tree *tree); +void remove_cache_extent(struct cache_tree *tree, + struct cache_extent *pe); +struct cache_extent *find_first_cache_extent(struct cache_tree *tree, + u64 start); +struct cache_extent *prev_cache_extent(struct cache_extent *pe); +struct cache_extent *next_cache_extent(struct cache_extent *pe); +struct cache_extent *find_cache_extent(struct cache_tree *tree, + u64 start, u64 size); +int insert_cache_extent(struct cache_tree *tree, u64 start, u64 size); +int insert_existing_cache_extent(struct cache_tree *tree, + struct cache_extent *pe); + +static inline int cache_tree_empty(struct cache_tree *tree) +{ + return RB_EMPTY_ROOT(&tree->root); +} + +static inline void free_cache_extent(struct cache_extent *pe) +{ + free(pe); +} + +struct cache_extent *alloc_pending_extent(u64 start, u64 size); + +#endif diff --git a/lib/extent-tree.c b/lib/extent-tree.c new file mode 100644 index 0000000..b2f9bb2 --- /dev/null +++ b/lib/extent-tree.c @@ -0,0 +1,3285 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "crc32c.h" +#include "volumes.h" + +#define BLOCK_GROUP_DATA EXTENT_WRITEBACK +#define BLOCK_GROUP_METADATA EXTENT_UPTODATE +#define BLOCK_GROUP_SYSTEM EXTENT_NEW + +#define BLOCK_GROUP_DIRTY EXTENT_DIRTY + +#define PENDING_EXTENT_INSERT 0 +#define PENDING_EXTENT_DELETE 1 +#define PENDING_BACKREF_UPDATE 2 + +struct pending_extent_op { + int type; + u64 bytenr; + u64 num_bytes; + u64 flags; + struct btrfs_disk_key key; + int level; +}; + +static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 root_objectid, u64 generation, + u64 flags, struct btrfs_disk_key *key, + int level, struct btrfs_key *ins); +static int __free_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 parent, + u64 root_objectid, u64 owner_objectid, + u64 owner_offset, int refs_to_drop); +static int finish_current_insert(struct btrfs_trans_handle *trans, struct + btrfs_root *extent_root); +static int del_pending_extents(struct btrfs_trans_handle *trans, struct + btrfs_root *extent_root); + +static int remove_sb_from_cache(struct btrfs_root *root, + struct btrfs_block_group_cache *cache) +{ + u64 bytenr; + u64 *logical; + int stripe_len; + int i, nr, ret; + struct extent_io_tree *free_space_cache; + + free_space_cache = &root->fs_info->free_space_cache; + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + bytenr = btrfs_sb_offset(i); + ret = btrfs_rmap_block(&root->fs_info->mapping_tree, + cache->key.objectid, bytenr, 0, + &logical, &nr, &stripe_len); + BUG_ON(ret); + while (nr--) { + clear_extent_dirty(free_space_cache, logical[nr], + logical[nr] + stripe_len - 1, GFP_NOFS); + } + kfree(logical); + } + return 0; +} + +static int cache_block_group(struct btrfs_root *root, + struct btrfs_block_group_cache *block_group) +{ + struct btrfs_path *path; + int ret; + struct btrfs_key key; + struct extent_buffer *leaf; + struct extent_io_tree *free_space_cache; + int slot; + u64 last; + u64 hole_size; + + if (!block_group) + return 0; + + root = root->fs_info->extent_root; + free_space_cache = &root->fs_info->free_space_cache; + + if (block_group->cached) + return 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + path->reada = 2; + last = max_t(u64, block_group->key.objectid, BTRFS_SUPER_INFO_OFFSET); + key.objectid = last; + key.offset = 0; + btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + goto err; + + while(1) { + leaf = path->nodes[0]; + slot = path->slots[0]; + if (slot >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, path); + if (ret < 0) + goto err; + if (ret == 0) { + continue; + } else { + break; + } + } + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid < block_group->key.objectid) { + goto next; + } + if (key.objectid >= block_group->key.objectid + + block_group->key.offset) { + break; + } + + if (btrfs_key_type(&key) == BTRFS_EXTENT_ITEM_KEY) { + if (key.objectid > last) { + hole_size = key.objectid - last; + set_extent_dirty(free_space_cache, last, + last + hole_size - 1, + GFP_NOFS); + } + last = key.objectid + key.offset; + } +next: + path->slots[0]++; + } + + if (block_group->key.objectid + + block_group->key.offset > last) { + hole_size = block_group->key.objectid + + block_group->key.offset - last; + set_extent_dirty(free_space_cache, last, + last + hole_size - 1, GFP_NOFS); + } + remove_sb_from_cache(root, block_group); + block_group->cached = 1; +err: + btrfs_free_path(path); + return 0; +} + +struct btrfs_block_group_cache *btrfs_lookup_first_block_group(struct + btrfs_fs_info *info, + u64 bytenr) +{ + struct extent_io_tree *block_group_cache; + struct btrfs_block_group_cache *block_group = NULL; + u64 ptr; + u64 start; + u64 end; + int ret; + + bytenr = max_t(u64, bytenr, + BTRFS_SUPER_INFO_OFFSET + BTRFS_SUPER_INFO_SIZE); + block_group_cache = &info->block_group_cache; + ret = find_first_extent_bit(block_group_cache, + bytenr, &start, &end, + BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA | + BLOCK_GROUP_SYSTEM); + if (ret) { + return NULL; + } + ret = get_state_private(block_group_cache, start, &ptr); + if (ret) + return NULL; + + block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr; + return block_group; +} + +struct btrfs_block_group_cache *btrfs_lookup_block_group(struct + btrfs_fs_info *info, + u64 bytenr) +{ + struct extent_io_tree *block_group_cache; + struct btrfs_block_group_cache *block_group = NULL; + u64 ptr; + u64 start; + u64 end; + int ret; + + block_group_cache = &info->block_group_cache; + ret = find_first_extent_bit(block_group_cache, + bytenr, &start, &end, + BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA | + BLOCK_GROUP_SYSTEM); + if (ret) { + return NULL; + } + ret = get_state_private(block_group_cache, start, &ptr); + if (ret) + return NULL; + + block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr; + if (block_group->key.objectid <= bytenr && bytenr < + block_group->key.objectid + block_group->key.offset) + return block_group; + return NULL; +} + +static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits) +{ + return (cache->flags & bits) == bits; +} + +static int noinline find_search_start(struct btrfs_root *root, + struct btrfs_block_group_cache **cache_ret, + u64 *start_ret, int num, int data) +{ + int ret; + struct btrfs_block_group_cache *cache = *cache_ret; + u64 last; + u64 start = 0; + u64 end = 0; + u64 search_start = *start_ret; + int wrapped = 0; + + if (!cache) { + goto out; + } +again: + ret = cache_block_group(root, cache); + if (ret) + goto out; + + last = max(search_start, cache->key.objectid); + if (cache->ro || !block_group_bits(cache, data)) { + goto new_group; + } + + while(1) { + ret = find_first_extent_bit(&root->fs_info->free_space_cache, + last, &start, &end, EXTENT_DIRTY); + if (ret) { + goto new_group; + } + + start = max(last, start); + last = end + 1; + if (last - start < num) { + continue; + } + if (start + num > cache->key.objectid + cache->key.offset) { + goto new_group; + } + *start_ret = start; + return 0; + } +out: + cache = btrfs_lookup_block_group(root->fs_info, search_start); + if (!cache) { + printk("Unable to find block group for %llu\n", + (unsigned long long)search_start); + WARN_ON(1); + } + return -ENOSPC; + +new_group: + last = cache->key.objectid + cache->key.offset; +wrapped: + cache = btrfs_lookup_first_block_group(root->fs_info, last); + if (!cache) { +no_cache: + if (!wrapped) { + wrapped = 1; + last = search_start; + goto wrapped; + } + goto out; + } + cache = btrfs_find_block_group(root, cache, last, data, 0); + cache = btrfs_find_block_group(root, cache, last, data, 0); + if (!cache) + goto no_cache; + + *cache_ret = cache; + goto again; +} + +static u64 div_factor(u64 num, int factor) +{ + if (factor == 10) + return num; + num *= factor; + num /= 10; + return num; +} + +static int block_group_state_bits(u64 flags) +{ + int bits = 0; + if (flags & BTRFS_BLOCK_GROUP_DATA) + bits |= BLOCK_GROUP_DATA; + if (flags & BTRFS_BLOCK_GROUP_METADATA) + bits |= BLOCK_GROUP_METADATA; + if (flags & BTRFS_BLOCK_GROUP_SYSTEM) + bits |= BLOCK_GROUP_SYSTEM; + return bits; +} + +struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, + struct btrfs_block_group_cache + *hint, u64 search_start, + int data, int owner) +{ + struct btrfs_block_group_cache *cache; + struct extent_io_tree *block_group_cache; + struct btrfs_block_group_cache *found_group = NULL; + struct btrfs_fs_info *info = root->fs_info; + u64 used; + u64 last = 0; + u64 hint_last; + u64 start; + u64 end; + u64 free_check; + u64 ptr; + int bit; + int ret; + int full_search = 0; + int factor = 10; + + block_group_cache = &info->block_group_cache; + + if (!owner) + factor = 10; + + bit = block_group_state_bits(data); + + if (search_start) { + struct btrfs_block_group_cache *shint; + shint = btrfs_lookup_block_group(info, search_start); + if (shint && !shint->ro && block_group_bits(shint, data)) { + used = btrfs_block_group_used(&shint->item); + if (used + shint->pinned < + div_factor(shint->key.offset, factor)) { + return shint; + } + } + } + if (hint && !hint->ro && block_group_bits(hint, data)) { + used = btrfs_block_group_used(&hint->item); + if (used + hint->pinned < + div_factor(hint->key.offset, factor)) { + return hint; + } + last = hint->key.objectid + hint->key.offset; + hint_last = last; + } else { + if (hint) + hint_last = max(hint->key.objectid, search_start); + else + hint_last = search_start; + + last = hint_last; + } +again: + while(1) { + ret = find_first_extent_bit(block_group_cache, last, + &start, &end, bit); + if (ret) + break; + + ret = get_state_private(block_group_cache, start, &ptr); + if (ret) + break; + + cache = (struct btrfs_block_group_cache *)(unsigned long)ptr; + last = cache->key.objectid + cache->key.offset; + used = btrfs_block_group_used(&cache->item); + + if (!cache->ro && block_group_bits(cache, data)) { + if (full_search) + free_check = cache->key.offset; + else + free_check = div_factor(cache->key.offset, + factor); + + if (used + cache->pinned < free_check) { + found_group = cache; + goto found; + } + } + cond_resched(); + } + if (!full_search) { + last = search_start; + full_search = 1; + goto again; + } +found: + return found_group; +} + +/* + * Back reference rules. Back refs have three main goals: + * + * 1) differentiate between all holders of references to an extent so that + * when a reference is dropped we can make sure it was a valid reference + * before freeing the extent. + * + * 2) Provide enough information to quickly find the holders of an extent + * if we notice a given block is corrupted or bad. + * + * 3) Make it easy to migrate blocks for FS shrinking or storage pool + * maintenance. This is actually the same as #2, but with a slightly + * different use case. + * + * There are two kinds of back refs. The implicit back refs is optimized + * for pointers in non-shared tree blocks. For a given pointer in a block, + * back refs of this kind provide information about the block's owner tree + * and the pointer's key. These information allow us to find the block by + * b-tree searching. The full back refs is for pointers in tree blocks not + * referenced by their owner trees. The location of tree block is recorded + * in the back refs. Actually the full back refs is generic, and can be + * used in all cases the implicit back refs is used. The major shortcoming + * of the full back refs is its overhead. Every time a tree block gets + * COWed, we have to update back refs entry for all pointers in it. + * + * For a newly allocated tree block, we use implicit back refs for + * pointers in it. This means most tree related operations only involve + * implicit back refs. For a tree block created in old transaction, the + * only way to drop a reference to it is COW it. So we can detect the + * event that tree block loses its owner tree's reference and do the + * back refs conversion. + * + * When a tree block is COW'd through a tree, there are four cases: + * + * The reference count of the block is one and the tree is the block's + * owner tree. Nothing to do in this case. + * + * The reference count of the block is one and the tree is not the + * block's owner tree. In this case, full back refs is used for pointers + * in the block. Remove these full back refs, add implicit back refs for + * every pointers in the new block. + * + * The reference count of the block is greater than one and the tree is + * the block's owner tree. In this case, implicit back refs is used for + * pointers in the block. Add full back refs for every pointers in the + * block, increase lower level extents' reference counts. The original + * implicit back refs are entailed to the new block. + * + * The reference count of the block is greater than one and the tree is + * not the block's owner tree. Add implicit back refs for every pointer in + * the new block, increase lower level extents' reference count. + * + * Back Reference Key composing: + * + * The key objectid corresponds to the first byte in the extent, + * The key type is used to differentiate between types of back refs. + * There are different meanings of the key offset for different types + * of back refs. + * + * File extents can be referenced by: + * + * - multiple snapshots, subvolumes, or different generations in one subvol + * - different files inside a single subvolume + * - different offsets inside a file (bookend extents in file.c) + * + * The extent ref structure for the implicit back refs has fields for: + * + * - Objectid of the subvolume root + * - objectid of the file holding the reference + * - original offset in the file + * - how many bookend extents + * + * The key offset for the implicit back refs is hash of the first + * three fields. + * + * The extent ref structure for the full back refs has field for: + * + * - number of pointers in the tree leaf + * + * The key offset for the implicit back refs is the first byte of + * the tree leaf + * + * When a file extent is allocated, The implicit back refs is used. + * the fields are filled in: + * + * (root_key.objectid, inode objectid, offset in file, 1) + * + * When a file extent is removed file truncation, we find the + * corresponding implicit back refs and check the following fields: + * + * (btrfs_header_owner(leaf), inode objectid, offset in file) + * + * Btree extents can be referenced by: + * + * - Different subvolumes + * + * Both the implicit back refs and the full back refs for tree blocks + * only consist of key. The key offset for the implicit back refs is + * objectid of block's owner tree. The key offset for the full back refs + * is the first byte of parent block. + * + * When implicit back refs is used, information about the lowest key and + * level of the tree block are required. These information are stored in + * tree block info structure. + */ + +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 +static int convert_extent_item_v0(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 owner, u32 extra_size) +{ + struct btrfs_extent_item *item; + struct btrfs_extent_item_v0 *ei0; + struct btrfs_extent_ref_v0 *ref0; + struct btrfs_tree_block_info *bi; + struct extent_buffer *leaf; + struct btrfs_key key; + struct btrfs_key found_key; + u32 new_size = sizeof(*item); + u64 refs; + int ret; + + leaf = path->nodes[0]; + BUG_ON(btrfs_item_size_nr(leaf, path->slots[0]) != sizeof(*ei0)); + + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + ei0 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_item_v0); + refs = btrfs_extent_refs_v0(leaf, ei0); + + if (owner == (u64)-1) { + while (1) { + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, path); + if (ret < 0) + return ret; + BUG_ON(ret > 0); + leaf = path->nodes[0]; + } + btrfs_item_key_to_cpu(leaf, &found_key, + path->slots[0]); + BUG_ON(key.objectid != found_key.objectid); + if (found_key.type != BTRFS_EXTENT_REF_V0_KEY) { + path->slots[0]++; + continue; + } + ref0 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_ref_v0); + owner = btrfs_ref_objectid_v0(leaf, ref0); + break; + } + } + btrfs_release_path(root, path); + + if (owner < BTRFS_FIRST_FREE_OBJECTID) + new_size += sizeof(*bi); + + new_size -= sizeof(*ei0); + ret = btrfs_search_slot(trans, root, &key, path, new_size, 1); + if (ret < 0) + return ret; + BUG_ON(ret); + + ret = btrfs_extend_item(trans, root, path, new_size); + BUG_ON(ret); + + leaf = path->nodes[0]; + item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); + btrfs_set_extent_refs(leaf, item, refs); + /* FIXME: get real generation */ + btrfs_set_extent_generation(leaf, item, 0); + if (owner < BTRFS_FIRST_FREE_OBJECTID) { + btrfs_set_extent_flags(leaf, item, + BTRFS_EXTENT_FLAG_TREE_BLOCK | + BTRFS_BLOCK_FLAG_FULL_BACKREF); + bi = (struct btrfs_tree_block_info *)(item + 1); + /* FIXME: get first key of the block */ + memset_extent_buffer(leaf, 0, (unsigned long)bi, sizeof(*bi)); + btrfs_set_tree_block_level(leaf, bi, (int)owner); + } else { + btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_DATA); + } + btrfs_mark_buffer_dirty(leaf); + return 0; +} +#endif + +static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset) +{ + u32 high_crc = ~(u32)0; + u32 low_crc = ~(u32)0; + __le64 lenum; + + lenum = cpu_to_le64(root_objectid); + high_crc = btrfs_crc32c(high_crc, &lenum, sizeof(lenum)); + lenum = cpu_to_le64(owner); + low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum)); + lenum = cpu_to_le64(offset); + low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum)); + + return ((u64)high_crc << 31) ^ (u64)low_crc; +} + +static u64 hash_extent_data_ref_item(struct extent_buffer *leaf, + struct btrfs_extent_data_ref *ref) +{ + return hash_extent_data_ref(btrfs_extent_data_ref_root(leaf, ref), + btrfs_extent_data_ref_objectid(leaf, ref), + btrfs_extent_data_ref_offset(leaf, ref)); +} + +static int match_extent_data_ref(struct extent_buffer *leaf, + struct btrfs_extent_data_ref *ref, + u64 root_objectid, u64 owner, u64 offset) +{ + if (btrfs_extent_data_ref_root(leaf, ref) != root_objectid || + btrfs_extent_data_ref_objectid(leaf, ref) != owner || + btrfs_extent_data_ref_offset(leaf, ref) != offset) + return 0; + return 1; +} + +static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr, u64 parent, + u64 root_objectid, + u64 owner, u64 offset) +{ + struct btrfs_key key; + struct btrfs_extent_data_ref *ref; + struct extent_buffer *leaf; + u32 nritems; + int ret; + int recow; + int err = -ENOENT; + + key.objectid = bytenr; + if (parent) { + key.type = BTRFS_SHARED_DATA_REF_KEY; + key.offset = parent; + } else { + key.type = BTRFS_EXTENT_DATA_REF_KEY; + key.offset = hash_extent_data_ref(root_objectid, + owner, offset); + } +again: + recow = 0; + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret < 0) { + err = ret; + goto fail; + } + + if (parent) { + if (!ret) + return 0; +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + key.type = BTRFS_EXTENT_REF_V0_KEY; + btrfs_release_path(root, path); + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret < 0) { + err = ret; + goto fail; + } + if (!ret) + return 0; +#endif + goto fail; + } + + leaf = path->nodes[0]; + nritems = btrfs_header_nritems(leaf); + while (1) { + if (path->slots[0] >= nritems) { + ret = btrfs_next_leaf(root, path); + if (ret < 0) + err = ret; + if (ret) + goto fail; + + leaf = path->nodes[0]; + nritems = btrfs_header_nritems(leaf); + recow = 1; + } + + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + if (key.objectid != bytenr || + key.type != BTRFS_EXTENT_DATA_REF_KEY) + goto fail; + + ref = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_data_ref); + + if (match_extent_data_ref(leaf, ref, root_objectid, + owner, offset)) { + if (recow) { + btrfs_release_path(root, path); + goto again; + } + err = 0; + break; + } + path->slots[0]++; + } +fail: + return err; +} + +static noinline int insert_extent_data_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr, u64 parent, + u64 root_objectid, u64 owner, + u64 offset, int refs_to_add) +{ + struct btrfs_key key; + struct extent_buffer *leaf; + u32 size; + u32 num_refs; + int ret; + + key.objectid = bytenr; + if (parent) { + key.type = BTRFS_SHARED_DATA_REF_KEY; + key.offset = parent; + size = sizeof(struct btrfs_shared_data_ref); + } else { + key.type = BTRFS_EXTENT_DATA_REF_KEY; + key.offset = hash_extent_data_ref(root_objectid, + owner, offset); + size = sizeof(struct btrfs_extent_data_ref); + } + + ret = btrfs_insert_empty_item(trans, root, path, &key, size); + if (ret && ret != -EEXIST) + goto fail; + + leaf = path->nodes[0]; + if (parent) { + struct btrfs_shared_data_ref *ref; + ref = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_shared_data_ref); + if (ret == 0) { + btrfs_set_shared_data_ref_count(leaf, ref, refs_to_add); + } else { + num_refs = btrfs_shared_data_ref_count(leaf, ref); + num_refs += refs_to_add; + btrfs_set_shared_data_ref_count(leaf, ref, num_refs); + } + } else { + struct btrfs_extent_data_ref *ref; + while (ret == -EEXIST) { + ref = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_data_ref); + if (match_extent_data_ref(leaf, ref, root_objectid, + owner, offset)) + break; + btrfs_release_path(root, path); + + key.offset++; + ret = btrfs_insert_empty_item(trans, root, path, &key, + size); + if (ret && ret != -EEXIST) + goto fail; + + leaf = path->nodes[0]; + } + ref = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_data_ref); + if (ret == 0) { + btrfs_set_extent_data_ref_root(leaf, ref, + root_objectid); + btrfs_set_extent_data_ref_objectid(leaf, ref, owner); + btrfs_set_extent_data_ref_offset(leaf, ref, offset); + btrfs_set_extent_data_ref_count(leaf, ref, refs_to_add); + } else { + num_refs = btrfs_extent_data_ref_count(leaf, ref); + num_refs += refs_to_add; + btrfs_set_extent_data_ref_count(leaf, ref, num_refs); + } + } + btrfs_mark_buffer_dirty(leaf); + ret = 0; +fail: + btrfs_release_path(root, path); + return ret; +} + +static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + int refs_to_drop) +{ + struct btrfs_key key; + struct btrfs_extent_data_ref *ref1 = NULL; + struct btrfs_shared_data_ref *ref2 = NULL; + struct extent_buffer *leaf; + u32 num_refs = 0; + int ret = 0; + + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + + if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { + ref1 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_data_ref); + num_refs = btrfs_extent_data_ref_count(leaf, ref1); + } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) { + ref2 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_shared_data_ref); + num_refs = btrfs_shared_data_ref_count(leaf, ref2); +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) { + struct btrfs_extent_ref_v0 *ref0; + ref0 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_ref_v0); + num_refs = btrfs_ref_count_v0(leaf, ref0); +#endif + } else { + BUG(); + } + + BUG_ON(num_refs < refs_to_drop); + num_refs -= refs_to_drop; + + if (num_refs == 0) { + ret = btrfs_del_item(trans, root, path); + } else { + if (key.type == BTRFS_EXTENT_DATA_REF_KEY) + btrfs_set_extent_data_ref_count(leaf, ref1, num_refs); + else if (key.type == BTRFS_SHARED_DATA_REF_KEY) + btrfs_set_shared_data_ref_count(leaf, ref2, num_refs); +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + else { + struct btrfs_extent_ref_v0 *ref0; + ref0 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_ref_v0); + btrfs_set_ref_count_v0(leaf, ref0, num_refs); + } +#endif + btrfs_mark_buffer_dirty(leaf); + } + return ret; +} + +static noinline u32 extent_data_ref_count(struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_extent_inline_ref *iref) +{ + struct btrfs_key key; + struct extent_buffer *leaf; + struct btrfs_extent_data_ref *ref1; + struct btrfs_shared_data_ref *ref2; + u32 num_refs = 0; + + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + if (iref) { + if (btrfs_extent_inline_ref_type(leaf, iref) == + BTRFS_EXTENT_DATA_REF_KEY) { + ref1 = (struct btrfs_extent_data_ref *)(&iref->offset); + num_refs = btrfs_extent_data_ref_count(leaf, ref1); + } else { + ref2 = (struct btrfs_shared_data_ref *)(iref + 1); + num_refs = btrfs_shared_data_ref_count(leaf, ref2); + } + } else if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { + ref1 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_data_ref); + num_refs = btrfs_extent_data_ref_count(leaf, ref1); + } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) { + ref2 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_shared_data_ref); + num_refs = btrfs_shared_data_ref_count(leaf, ref2); +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) { + struct btrfs_extent_ref_v0 *ref0; + ref0 = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_ref_v0); + num_refs = btrfs_ref_count_v0(leaf, ref0); +#endif + } else { + BUG(); + } + return num_refs; +} + +static noinline int lookup_tree_block_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr, u64 parent, + u64 root_objectid) +{ + struct btrfs_key key; + int ret; + + key.objectid = bytenr; + if (parent) { + key.type = BTRFS_SHARED_BLOCK_REF_KEY; + key.offset = parent; + } else { + key.type = BTRFS_TREE_BLOCK_REF_KEY; + key.offset = root_objectid; + } + + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret > 0) + ret = -ENOENT; +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + if (ret == -ENOENT && parent) { + btrfs_release_path(root, path); + key.type = BTRFS_EXTENT_REF_V0_KEY; + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret > 0) + ret = -ENOENT; + } +#endif + return ret; +} + +static noinline int insert_tree_block_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr, u64 parent, + u64 root_objectid) +{ + struct btrfs_key key; + int ret; + + key.objectid = bytenr; + if (parent) { + key.type = BTRFS_SHARED_BLOCK_REF_KEY; + key.offset = parent; + } else { + key.type = BTRFS_TREE_BLOCK_REF_KEY; + key.offset = root_objectid; + } + + ret = btrfs_insert_empty_item(trans, root, path, &key, 0); + + btrfs_release_path(root, path); + return ret; +} + +static inline int extent_ref_type(u64 parent, u64 owner) +{ + if (owner < BTRFS_FIRST_FREE_OBJECTID) { + if (parent > 0) + return BTRFS_SHARED_BLOCK_REF_KEY; + else + return BTRFS_TREE_BLOCK_REF_KEY; + } else { + if (parent > 0) + return BTRFS_SHARED_DATA_REF_KEY; + else + return BTRFS_EXTENT_DATA_REF_KEY; + } +} + +static int find_next_key(struct btrfs_path *path, struct btrfs_key *key) + +{ + int level; + for (level = 0; level < BTRFS_MAX_LEVEL; level++) { + if (!path->nodes[level]) + break; + if (path->slots[level] + 1 >= + btrfs_header_nritems(path->nodes[level])) + continue; + if (level == 0) + btrfs_item_key_to_cpu(path->nodes[level], key, + path->slots[level] + 1); + else + btrfs_node_key_to_cpu(path->nodes[level], key, + path->slots[level] + 1); + return 0; + } + return 1; +} + +static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_extent_inline_ref **ref_ret, + u64 bytenr, u64 num_bytes, + u64 parent, u64 root_objectid, + u64 owner, u64 offset, int insert) +{ + struct btrfs_key key; + struct extent_buffer *leaf; + struct btrfs_extent_item *ei; + struct btrfs_extent_inline_ref *iref; + u64 flags; + u32 item_size; + unsigned long ptr; + unsigned long end; + int extra_size; + int type; + int want; + int ret; + int err = 0; + + key.objectid = bytenr; + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = num_bytes; + + want = extent_ref_type(parent, owner); + if (insert) + extra_size = btrfs_extent_inline_ref_size(want); + else + extra_size = -1; + ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1); + if (ret < 0) { + err = ret; + goto out; + } + BUG_ON(ret); + + leaf = path->nodes[0]; + item_size = btrfs_item_size_nr(leaf, path->slots[0]); +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + if (item_size < sizeof(*ei)) { + if (!insert) { + err = -ENOENT; + goto out; + } + ret = convert_extent_item_v0(trans, root, path, owner, + extra_size); + if (ret < 0) { + err = ret; + goto out; + } + leaf = path->nodes[0]; + item_size = btrfs_item_size_nr(leaf, path->slots[0]); + } +#endif + BUG_ON(item_size < sizeof(*ei)); + + ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); + flags = btrfs_extent_flags(leaf, ei); + + ptr = (unsigned long)(ei + 1); + end = (unsigned long)ei + item_size; + + if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { + ptr += sizeof(struct btrfs_tree_block_info); + BUG_ON(ptr > end); + } else { + BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA)); + } + + err = -ENOENT; + while (1) { + if (ptr >= end) { + WARN_ON(ptr > end); + break; + } + iref = (struct btrfs_extent_inline_ref *)ptr; + type = btrfs_extent_inline_ref_type(leaf, iref); + if (want < type) + break; + if (want > type) { + ptr += btrfs_extent_inline_ref_size(type); + continue; + } + + if (type == BTRFS_EXTENT_DATA_REF_KEY) { + struct btrfs_extent_data_ref *dref; + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + if (match_extent_data_ref(leaf, dref, root_objectid, + owner, offset)) { + err = 0; + break; + } + if (hash_extent_data_ref_item(leaf, dref) < + hash_extent_data_ref(root_objectid, owner, offset)) + break; + } else { + u64 ref_offset; + ref_offset = btrfs_extent_inline_ref_offset(leaf, iref); + if (parent > 0) { + if (parent == ref_offset) { + err = 0; + break; + } + if (ref_offset < parent) + break; + } else { + if (root_objectid == ref_offset) { + err = 0; + break; + } + if (ref_offset < root_objectid) + break; + } + } + ptr += btrfs_extent_inline_ref_size(type); + } + if (err == -ENOENT && insert) { + if (item_size + extra_size >= + BTRFS_MAX_EXTENT_ITEM_SIZE(root)) { + err = -EAGAIN; + goto out; + } + /* + * To add new inline back ref, we have to make sure + * there is no corresponding back ref item. + * For simplicity, we just do not add new inline back + * ref if there is any back ref item. + */ + if (find_next_key(path, &key) == 0 && key.objectid == bytenr && + key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) { + err = -EAGAIN; + goto out; + } + } + *ref_ret = (struct btrfs_extent_inline_ref *)ptr; +out: + return err; +} + +static int setup_inline_extent_backref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_extent_inline_ref *iref, + u64 parent, u64 root_objectid, + u64 owner, u64 offset, int refs_to_add) +{ + struct extent_buffer *leaf; + struct btrfs_extent_item *ei; + unsigned long ptr; + unsigned long end; + unsigned long item_offset; + u64 refs; + int size; + int type; + int ret; + + leaf = path->nodes[0]; + ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); + item_offset = (unsigned long)iref - (unsigned long)ei; + + type = extent_ref_type(parent, owner); + size = btrfs_extent_inline_ref_size(type); + + ret = btrfs_extend_item(trans, root, path, size); + BUG_ON(ret); + + ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); + refs = btrfs_extent_refs(leaf, ei); + refs += refs_to_add; + btrfs_set_extent_refs(leaf, ei, refs); + + ptr = (unsigned long)ei + item_offset; + end = (unsigned long)ei + btrfs_item_size_nr(leaf, path->slots[0]); + if (ptr < end - size) + memmove_extent_buffer(leaf, ptr + size, ptr, + end - size - ptr); + + iref = (struct btrfs_extent_inline_ref *)ptr; + btrfs_set_extent_inline_ref_type(leaf, iref, type); + if (type == BTRFS_EXTENT_DATA_REF_KEY) { + struct btrfs_extent_data_ref *dref; + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + btrfs_set_extent_data_ref_root(leaf, dref, root_objectid); + btrfs_set_extent_data_ref_objectid(leaf, dref, owner); + btrfs_set_extent_data_ref_offset(leaf, dref, offset); + btrfs_set_extent_data_ref_count(leaf, dref, refs_to_add); + } else if (type == BTRFS_SHARED_DATA_REF_KEY) { + struct btrfs_shared_data_ref *sref; + sref = (struct btrfs_shared_data_ref *)(iref + 1); + btrfs_set_shared_data_ref_count(leaf, sref, refs_to_add); + btrfs_set_extent_inline_ref_offset(leaf, iref, parent); + } else if (type == BTRFS_SHARED_BLOCK_REF_KEY) { + btrfs_set_extent_inline_ref_offset(leaf, iref, parent); + } else { + btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid); + } + btrfs_mark_buffer_dirty(leaf); + return 0; +} + +static int lookup_extent_backref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_extent_inline_ref **ref_ret, + u64 bytenr, u64 num_bytes, u64 parent, + u64 root_objectid, u64 owner, u64 offset) +{ + int ret; + + ret = lookup_inline_extent_backref(trans, root, path, ref_ret, + bytenr, num_bytes, parent, + root_objectid, owner, offset, 0); + if (ret != -ENOENT) + return ret; + + btrfs_release_path(root, path); + *ref_ret = NULL; + + if (owner < BTRFS_FIRST_FREE_OBJECTID) { + ret = lookup_tree_block_ref(trans, root, path, bytenr, parent, + root_objectid); + } else { + ret = lookup_extent_data_ref(trans, root, path, bytenr, parent, + root_objectid, owner, offset); + } + return ret; +} + +static int update_inline_extent_backref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_extent_inline_ref *iref, + int refs_to_mod) +{ + struct extent_buffer *leaf; + struct btrfs_extent_item *ei; + struct btrfs_extent_data_ref *dref = NULL; + struct btrfs_shared_data_ref *sref = NULL; + unsigned long ptr; + unsigned long end; + u32 item_size; + int size; + int type; + int ret; + u64 refs; + + leaf = path->nodes[0]; + ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); + refs = btrfs_extent_refs(leaf, ei); + WARN_ON(refs_to_mod < 0 && refs + refs_to_mod <= 0); + refs += refs_to_mod; + btrfs_set_extent_refs(leaf, ei, refs); + + type = btrfs_extent_inline_ref_type(leaf, iref); + + if (type == BTRFS_EXTENT_DATA_REF_KEY) { + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + refs = btrfs_extent_data_ref_count(leaf, dref); + } else if (type == BTRFS_SHARED_DATA_REF_KEY) { + sref = (struct btrfs_shared_data_ref *)(iref + 1); + refs = btrfs_shared_data_ref_count(leaf, sref); + } else { + refs = 1; + BUG_ON(refs_to_mod != -1); + } + + BUG_ON(refs_to_mod < 0 && refs < -refs_to_mod); + refs += refs_to_mod; + + if (refs > 0) { + if (type == BTRFS_EXTENT_DATA_REF_KEY) + btrfs_set_extent_data_ref_count(leaf, dref, refs); + else + btrfs_set_shared_data_ref_count(leaf, sref, refs); + } else { + size = btrfs_extent_inline_ref_size(type); + item_size = btrfs_item_size_nr(leaf, path->slots[0]); + ptr = (unsigned long)iref; + end = (unsigned long)ei + item_size; + if (ptr + size < end) + memmove_extent_buffer(leaf, ptr, ptr + size, + end - ptr - size); + item_size -= size; + ret = btrfs_truncate_item(trans, root, path, item_size, 1); + BUG_ON(ret); + } + btrfs_mark_buffer_dirty(leaf); + return 0; +} + +static int insert_inline_extent_backref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr, u64 num_bytes, u64 parent, + u64 root_objectid, u64 owner, + u64 offset, int refs_to_add) +{ + struct btrfs_extent_inline_ref *iref; + int ret; + + ret = lookup_inline_extent_backref(trans, root, path, &iref, + bytenr, num_bytes, parent, + root_objectid, owner, offset, 1); + if (ret == 0) { + BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID); + ret = update_inline_extent_backref(trans, root, path, iref, + refs_to_add); + } else if (ret == -ENOENT) { + ret = setup_inline_extent_backref(trans, root, path, iref, + parent, root_objectid, + owner, offset, refs_to_add); + } + return ret; +} + +static int insert_extent_backref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr, u64 parent, u64 root_objectid, + u64 owner, u64 offset, int refs_to_add) +{ + int ret; + + if (owner >= BTRFS_FIRST_FREE_OBJECTID) { + ret = insert_extent_data_ref(trans, root, path, bytenr, + parent, root_objectid, + owner, offset, refs_to_add); + } else { + BUG_ON(refs_to_add != 1); + ret = insert_tree_block_ref(trans, root, path, bytenr, + parent, root_objectid); + } + return ret; +} + +static int remove_extent_backref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_extent_inline_ref *iref, + int refs_to_drop, int is_data) +{ + int ret; + + BUG_ON(!is_data && refs_to_drop != 1); + if (iref) { + ret = update_inline_extent_backref(trans, root, path, iref, + -refs_to_drop); + } else if (is_data) { + ret = remove_extent_data_ref(trans, root, path, refs_to_drop); + } else { + ret = btrfs_del_item(trans, root, path); + } + return ret; +} + +int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 parent, + u64 root_objectid, u64 owner, u64 offset) +{ + struct btrfs_path *path; + struct extent_buffer *leaf; + struct btrfs_extent_item *item; + u64 refs; + int ret; + int err = 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + path->reada = 1; + path->leave_spinning = 1; + + ret = insert_inline_extent_backref(trans, root->fs_info->extent_root, + path, bytenr, num_bytes, parent, + root_objectid, owner, offset, 1); + if (ret == 0) + goto out; + + if (ret != -EAGAIN) { + err = ret; + goto out; + } + + leaf = path->nodes[0]; + item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); + refs = btrfs_extent_refs(leaf, item); + btrfs_set_extent_refs(leaf, item, refs + 1); + + btrfs_mark_buffer_dirty(leaf); + btrfs_release_path(root->fs_info->extent_root, path); + + path->reada = 1; + path->leave_spinning = 1; + + /* now insert the actual backref */ + ret = insert_extent_backref(trans, root->fs_info->extent_root, + path, bytenr, parent, root_objectid, + owner, offset, 1); + if (ret) + err = ret; +out: + btrfs_free_path(path); + finish_current_insert(trans, root->fs_info->extent_root); + del_pending_extents(trans, root->fs_info->extent_root); + BUG_ON(err); + return err; +} + +int btrfs_extent_post_op(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + finish_current_insert(trans, root->fs_info->extent_root); + del_pending_extents(trans, root->fs_info->extent_root); + return 0; +} + +int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytenr, + u64 num_bytes, u64 *refs, u64 *flags) +{ + struct btrfs_path *path; + int ret; + struct btrfs_key key; + struct extent_buffer *l; + struct btrfs_extent_item *item; + u32 item_size; + u64 num_refs; + u64 extent_flags; + + WARN_ON(num_bytes < root->sectorsize); + path = btrfs_alloc_path(); + path->reada = 1; + key.objectid = bytenr; + key.offset = num_bytes; + btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); + ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path, + 0, 0); + if (ret < 0) + goto out; + if (ret != 0) { + btrfs_print_leaf(root, path->nodes[0]); + printk("failed to find block number %Lu\n", bytenr); + BUG(); + } + + l = path->nodes[0]; + item_size = btrfs_item_size_nr(l, path->slots[0]); + if (item_size >= sizeof(*item)) { + item = btrfs_item_ptr(l, path->slots[0], + struct btrfs_extent_item); + num_refs = btrfs_extent_refs(l, item); + extent_flags = btrfs_extent_flags(l, item); + } else { +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + struct btrfs_extent_item_v0 *ei0; + BUG_ON(item_size != sizeof(*ei0)); + ei0 = btrfs_item_ptr(l, path->slots[0], + struct btrfs_extent_item_v0); + num_refs = btrfs_extent_refs_v0(l, ei0); + /* FIXME: this isn't correct for data */ + extent_flags = BTRFS_BLOCK_FLAG_FULL_BACKREF; +#else + BUG(); +#endif + } + BUG_ON(num_refs == 0); + item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item); + if (refs) + *refs = num_refs; + if (flags) + *flags = extent_flags; +out: + btrfs_free_path(path); + return 0; +} + +int btrfs_set_block_flags(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 flags) +{ + struct btrfs_path *path; + int ret; + struct btrfs_key key; + struct extent_buffer *l; + struct btrfs_extent_item *item; + u32 item_size; + + WARN_ON(num_bytes < root->sectorsize); + path = btrfs_alloc_path(); + path->reada = 1; + key.objectid = bytenr; + key.offset = num_bytes; + btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); + ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path, + 0, 0); + if (ret < 0) + goto out; + if (ret != 0) { + btrfs_print_leaf(root, path->nodes[0]); + printk("failed to find block number %Lu\n", + (unsigned long long)bytenr); + BUG(); + } + l = path->nodes[0]; + item_size = btrfs_item_size_nr(l, path->slots[0]); +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + if (item_size < sizeof(*item)) { + ret = convert_extent_item_v0(trans, root->fs_info->extent_root, + path, (u64)-1, 0); + if (ret < 0) + goto out; + + l = path->nodes[0]; + item_size = btrfs_item_size_nr(l, path->slots[0]); + } +#endif + BUG_ON(item_size < sizeof(*item)); + item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item); + flags |= btrfs_extent_flags(l, item); + btrfs_set_extent_flags(l, item, flags); +out: + btrfs_free_path(path); + finish_current_insert(trans, root->fs_info->extent_root); + del_pending_extents(trans, root->fs_info->extent_root); + return ret; +} + +static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf, + int record_parent, int inc) +{ + u64 bytenr; + u64 num_bytes; + u64 parent; + u64 ref_root; + u32 nritems; + struct btrfs_key key; + struct btrfs_file_extent_item *fi; + int i; + int level; + int ret = 0; + int faili = 0; + int (*process_func)(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64, u64, u64, u64, u64, u64); + + ref_root = btrfs_header_owner(buf); + nritems = btrfs_header_nritems(buf); + level = btrfs_header_level(buf); + + if (!root->ref_cows && level == 0) + return 0; + + if (inc) + process_func = btrfs_inc_extent_ref; + else + process_func = btrfs_free_extent; + + if (record_parent) + parent = buf->start; + else + parent = 0; + + for (i = 0; i < nritems; i++) { + cond_resched(); + if (level == 0) { + btrfs_item_key_to_cpu(buf, &key, i); + if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) + continue; + fi = btrfs_item_ptr(buf, i, + struct btrfs_file_extent_item); + if (btrfs_file_extent_type(buf, fi) == + BTRFS_FILE_EXTENT_INLINE) + continue; + bytenr = btrfs_file_extent_disk_bytenr(buf, fi); + if (bytenr == 0) + continue; + + num_bytes = btrfs_file_extent_disk_num_bytes(buf, fi); + key.offset -= btrfs_file_extent_offset(buf, fi); + ret = process_func(trans, root, bytenr, num_bytes, + parent, ref_root, key.objectid, + key.offset); + if (ret) { + faili = i; + WARN_ON(1); + goto fail; + } + } else { + bytenr = btrfs_node_blockptr(buf, i); + num_bytes = btrfs_level_size(root, level - 1); + ret = process_func(trans, root, bytenr, num_bytes, + parent, ref_root, level - 1, 0); + if (ret) { + faili = i; + WARN_ON(1); + goto fail; + } + } + } + return 0; +fail: + WARN_ON(1); +#if 0 + for (i =0; i < faili; i++) { + if (level == 0) { + u64 disk_bytenr; + btrfs_item_key_to_cpu(buf, &key, i); + if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) + continue; + fi = btrfs_item_ptr(buf, i, + struct btrfs_file_extent_item); + if (btrfs_file_extent_type(buf, fi) == + BTRFS_FILE_EXTENT_INLINE) + continue; + disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi); + if (disk_bytenr == 0) + continue; + err = btrfs_free_extent(trans, root, disk_bytenr, + btrfs_file_extent_disk_num_bytes(buf, + fi), 0); + BUG_ON(err); + } else { + bytenr = btrfs_node_blockptr(buf, i); + err = btrfs_free_extent(trans, root, bytenr, + btrfs_level_size(root, level - 1), 0); + BUG_ON(err); + } + } +#endif + return ret; +} + +int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct extent_buffer *buf, int record_parent) +{ + return __btrfs_mod_ref(trans, root, buf, record_parent, 1); +} + +int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct extent_buffer *buf, int record_parent) +{ + return __btrfs_mod_ref(trans, root, buf, record_parent, 0); +} + +static int write_one_cache_group(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_block_group_cache *cache) +{ + int ret; + int pending_ret; + struct btrfs_root *extent_root = root->fs_info->extent_root; + unsigned long bi; + struct extent_buffer *leaf; + + ret = btrfs_search_slot(trans, extent_root, &cache->key, path, 0, 1); + if (ret < 0) + goto fail; + BUG_ON(ret); + + leaf = path->nodes[0]; + bi = btrfs_item_ptr_offset(leaf, path->slots[0]); + write_extent_buffer(leaf, &cache->item, bi, sizeof(cache->item)); + btrfs_mark_buffer_dirty(leaf); + btrfs_release_path(extent_root, path); +fail: + finish_current_insert(trans, extent_root); + pending_ret = del_pending_extents(trans, extent_root); + if (ret) + return ret; + if (pending_ret) + return pending_ret; + return 0; + +} + +int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + struct extent_io_tree *block_group_cache; + struct btrfs_block_group_cache *cache; + int ret; + struct btrfs_path *path; + u64 last = 0; + u64 start; + u64 end; + u64 ptr; + + block_group_cache = &root->fs_info->block_group_cache; + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + while(1) { + ret = find_first_extent_bit(block_group_cache, last, + &start, &end, BLOCK_GROUP_DIRTY); + if (ret) { + if (last == 0) + break; + last = 0; + continue; + } + + last = end + 1; + ret = get_state_private(block_group_cache, start, &ptr); + BUG_ON(ret); + + clear_extent_bits(block_group_cache, start, end, + BLOCK_GROUP_DIRTY, GFP_NOFS); + + cache = (struct btrfs_block_group_cache *)(unsigned long)ptr; + ret = write_one_cache_group(trans, root, path, cache); + BUG_ON(ret); + } + btrfs_free_path(path); + return 0; +} + +static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info, + u64 flags) +{ + struct list_head *head = &info->space_info; + struct list_head *cur; + struct btrfs_space_info *found; + list_for_each(cur, head) { + found = list_entry(cur, struct btrfs_space_info, list); + if (found->flags == flags) + return found; + } + return NULL; + +} + +static int update_space_info(struct btrfs_fs_info *info, u64 flags, + u64 total_bytes, u64 bytes_used, + struct btrfs_space_info **space_info) +{ + struct btrfs_space_info *found; + + found = __find_space_info(info, flags); + if (found) { + found->total_bytes += total_bytes; + found->bytes_used += bytes_used; + WARN_ON(found->total_bytes < found->bytes_used); + *space_info = found; + return 0; + } + found = kmalloc(sizeof(*found), GFP_NOFS); + if (!found) + return -ENOMEM; + + list_add(&found->list, &info->space_info); + found->flags = flags; + found->total_bytes = total_bytes; + found->bytes_used = bytes_used; + found->bytes_pinned = 0; + found->full = 0; + *space_info = found; + return 0; +} + + +static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) +{ + u64 extra_flags = flags & (BTRFS_BLOCK_GROUP_RAID0 | + BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_DUP); + if (extra_flags) { + if (flags & BTRFS_BLOCK_GROUP_DATA) + fs_info->avail_data_alloc_bits |= extra_flags; + if (flags & BTRFS_BLOCK_GROUP_METADATA) + fs_info->avail_metadata_alloc_bits |= extra_flags; + if (flags & BTRFS_BLOCK_GROUP_SYSTEM) + fs_info->avail_system_alloc_bits |= extra_flags; + } +} + +static int do_chunk_alloc(struct btrfs_trans_handle *trans, + struct btrfs_root *extent_root, u64 alloc_bytes, + u64 flags) +{ + struct btrfs_space_info *space_info; + u64 thresh; + u64 start; + u64 num_bytes; + int ret; + + space_info = __find_space_info(extent_root->fs_info, flags); + if (!space_info) { + ret = update_space_info(extent_root->fs_info, flags, + 0, 0, &space_info); + BUG_ON(ret); + } + BUG_ON(!space_info); + + if (space_info->full) + return 0; + + thresh = div_factor(space_info->total_bytes, 7); + if ((space_info->bytes_used + space_info->bytes_pinned + alloc_bytes) < + thresh) + return 0; + + ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes, flags); + if (ret == -ENOSPC) { + space_info->full = 1; + return 0; + } + + BUG_ON(ret); + + ret = btrfs_make_block_group(trans, extent_root, 0, flags, + BTRFS_FIRST_CHUNK_TREE_OBJECTID, start, num_bytes); + BUG_ON(ret); + return 0; +} + +static int update_block_group(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, int alloc, + int mark_free) +{ + struct btrfs_block_group_cache *cache; + struct btrfs_fs_info *info = root->fs_info; + u64 total = num_bytes; + u64 old_val; + u64 byte_in_group; + u64 start; + u64 end; + + /* block accounting for super block */ + old_val = btrfs_super_bytes_used(&info->super_copy); + if (alloc) + old_val += num_bytes; + else + old_val -= num_bytes; + btrfs_set_super_bytes_used(&info->super_copy, old_val); + + /* block accounting for root item */ + old_val = btrfs_root_used(&root->root_item); + if (alloc) + old_val += num_bytes; + else + old_val -= num_bytes; + btrfs_set_root_used(&root->root_item, old_val); + + while(total) { + cache = btrfs_lookup_block_group(info, bytenr); + if (!cache) { + return -1; + } + byte_in_group = bytenr - cache->key.objectid; + WARN_ON(byte_in_group > cache->key.offset); + start = cache->key.objectid; + end = start + cache->key.offset - 1; + set_extent_bits(&info->block_group_cache, start, end, + BLOCK_GROUP_DIRTY, GFP_NOFS); + + old_val = btrfs_block_group_used(&cache->item); + num_bytes = min(total, cache->key.offset - byte_in_group); + if (alloc) { + old_val += num_bytes; + cache->space_info->bytes_used += num_bytes; + } else { + old_val -= num_bytes; + cache->space_info->bytes_used -= num_bytes; + if (mark_free) { + set_extent_dirty(&info->free_space_cache, + bytenr, bytenr + num_bytes - 1, + GFP_NOFS); + } + } + btrfs_set_block_group_used(&cache->item, old_val); + total -= num_bytes; + bytenr += num_bytes; + } + return 0; +} + +static int update_pinned_extents(struct btrfs_root *root, + u64 bytenr, u64 num, int pin) +{ + u64 len; + struct btrfs_block_group_cache *cache; + struct btrfs_fs_info *fs_info = root->fs_info; + + if (pin) { + set_extent_dirty(&fs_info->pinned_extents, + bytenr, bytenr + num - 1, GFP_NOFS); + } else { + clear_extent_dirty(&fs_info->pinned_extents, + bytenr, bytenr + num - 1, GFP_NOFS); + } + while (num > 0) { + cache = btrfs_lookup_block_group(fs_info, bytenr); + WARN_ON(!cache); + len = min(num, cache->key.offset - + (bytenr - cache->key.objectid)); + if (pin) { + cache->pinned += len; + cache->space_info->bytes_pinned += len; + fs_info->total_pinned += len; + } else { + cache->pinned -= len; + cache->space_info->bytes_pinned -= len; + fs_info->total_pinned -= len; + } + bytenr += len; + num -= len; + } + return 0; +} + +int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy) +{ + u64 last = 0; + u64 start; + u64 end; + struct extent_io_tree *pinned_extents = &root->fs_info->pinned_extents; + int ret; + + while(1) { + ret = find_first_extent_bit(pinned_extents, last, + &start, &end, EXTENT_DIRTY); + if (ret) + break; + set_extent_dirty(copy, start, end, GFP_NOFS); + last = end + 1; + } + return 0; +} + +int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_io_tree *unpin) +{ + u64 start; + u64 end; + int ret; + struct extent_io_tree *free_space_cache; + free_space_cache = &root->fs_info->free_space_cache; + + while(1) { + ret = find_first_extent_bit(unpin, 0, &start, &end, + EXTENT_DIRTY); + if (ret) + break; + update_pinned_extents(root, start, end + 1 - start, 0); + clear_extent_dirty(unpin, start, end, GFP_NOFS); + set_extent_dirty(free_space_cache, start, end, GFP_NOFS); + } + return 0; +} + +static int finish_current_insert(struct btrfs_trans_handle *trans, + struct btrfs_root *extent_root) +{ + u64 start; + u64 end; + u64 priv; + struct btrfs_fs_info *info = extent_root->fs_info; + struct btrfs_path *path; + struct pending_extent_op *extent_op; + struct btrfs_key key; + int ret; + + path = btrfs_alloc_path(); + + while(1) { + ret = find_first_extent_bit(&info->extent_ins, 0, &start, + &end, EXTENT_LOCKED); + if (ret) + break; + + ret = get_state_private(&info->extent_ins, start, &priv); + BUG_ON(ret); + extent_op = (struct pending_extent_op *)(unsigned long)priv; + + if (extent_op->type == PENDING_EXTENT_INSERT) { + key.objectid = start; + key.offset = end + 1 - start; + key.type = BTRFS_EXTENT_ITEM_KEY; + ret = alloc_reserved_tree_block(trans, extent_root, + extent_root->root_key.objectid, + trans->transid, + extent_op->flags, + &extent_op->key, + extent_op->level, &key); + } else { + BUG_ON(1); + } + + clear_extent_bits(&info->extent_ins, start, end, EXTENT_LOCKED, + GFP_NOFS); + kfree(extent_op); + } + btrfs_free_path(path); + return 0; +} + +static int pin_down_bytes(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, int is_data) +{ + int err = 0; + struct extent_buffer *buf; + + if (is_data) + goto pinit; + + buf = btrfs_find_tree_block(root, bytenr, num_bytes); + if (!buf) + goto pinit; + + /* we can reuse a block if it hasn't been written + * and it is from this transaction. We can't + * reuse anything from the tree log root because + * it has tiny sub-transactions. + */ + if (btrfs_buffer_uptodate(buf, 0)) { + u64 header_owner = btrfs_header_owner(buf); + u64 header_transid = btrfs_header_generation(buf); + if (header_owner != BTRFS_TREE_LOG_OBJECTID && + header_transid == trans->transid && + !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) { + clean_tree_block(NULL, root, buf); + free_extent_buffer(buf); + return 1; + } + } + free_extent_buffer(buf); +pinit: + update_pinned_extents(root, bytenr, num_bytes, 1); + + BUG_ON(err < 0); + return 0; +} + +/* + * remove an extent from the root, returns 0 on success + */ +static int __free_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 parent, + u64 root_objectid, u64 owner_objectid, + u64 owner_offset, int refs_to_drop) +{ + + struct btrfs_key key; + struct btrfs_path *path; + struct btrfs_extent_ops *ops = root->fs_info->extent_ops; + struct btrfs_root *extent_root = root->fs_info->extent_root; + struct extent_buffer *leaf; + struct btrfs_extent_item *ei; + struct btrfs_extent_inline_ref *iref; + int ret; + int is_data; + int extent_slot = 0; + int found_extent = 0; + int num_to_del = 1; + u32 item_size; + u64 refs; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + path->reada = 1; + path->leave_spinning = 1; + + is_data = owner_objectid >= BTRFS_FIRST_FREE_OBJECTID; + BUG_ON(!is_data && refs_to_drop != 1); + + ret = lookup_extent_backref(trans, extent_root, path, &iref, + bytenr, num_bytes, parent, + root_objectid, owner_objectid, + owner_offset); + if (ret == 0) { + extent_slot = path->slots[0]; + while (extent_slot >= 0) { + btrfs_item_key_to_cpu(path->nodes[0], &key, + extent_slot); + if (key.objectid != bytenr) + break; + if (key.type == BTRFS_EXTENT_ITEM_KEY && + key.offset == num_bytes) { + found_extent = 1; + break; + } + if (path->slots[0] - extent_slot > 5) + break; + extent_slot--; + } +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + item_size = btrfs_item_size_nr(path->nodes[0], extent_slot); + if (found_extent && item_size < sizeof(*ei)) + found_extent = 0; +#endif + if (!found_extent) { + BUG_ON(iref); + ret = remove_extent_backref(trans, extent_root, path, + NULL, refs_to_drop, + is_data); + BUG_ON(ret); + btrfs_release_path(extent_root, path); + path->leave_spinning = 1; + + key.objectid = bytenr; + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = num_bytes; + + ret = btrfs_search_slot(trans, extent_root, + &key, path, -1, 1); + if (ret) { + printk(KERN_ERR "umm, got %d back from search" + ", was looking for %llu\n", ret, + (unsigned long long)bytenr); + btrfs_print_leaf(extent_root, path->nodes[0]); + } + BUG_ON(ret); + extent_slot = path->slots[0]; + } + } else { + btrfs_print_leaf(extent_root, path->nodes[0]); + WARN_ON(1); + printk(KERN_ERR "btrfs unable to find ref byte nr %llu " + "parent %llu root %llu owner %llu offset %llu\n", + (unsigned long long)bytenr, + (unsigned long long)parent, + (unsigned long long)root_objectid, + (unsigned long long)owner_objectid, + (unsigned long long)owner_offset); + } + + leaf = path->nodes[0]; + item_size = btrfs_item_size_nr(leaf, extent_slot); +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + if (item_size < sizeof(*ei)) { + BUG_ON(found_extent || extent_slot != path->slots[0]); + ret = convert_extent_item_v0(trans, extent_root, path, + owner_objectid, 0); + BUG_ON(ret < 0); + + btrfs_release_path(extent_root, path); + path->leave_spinning = 1; + + key.objectid = bytenr; + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = num_bytes; + + ret = btrfs_search_slot(trans, extent_root, &key, path, + -1, 1); + if (ret) { + printk(KERN_ERR "umm, got %d back from search" + ", was looking for %llu\n", ret, + (unsigned long long)bytenr); + btrfs_print_leaf(extent_root, path->nodes[0]); + } + BUG_ON(ret); + extent_slot = path->slots[0]; + leaf = path->nodes[0]; + item_size = btrfs_item_size_nr(leaf, extent_slot); + } +#endif + BUG_ON(item_size < sizeof(*ei)); + ei = btrfs_item_ptr(leaf, extent_slot, + struct btrfs_extent_item); + if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID) { + struct btrfs_tree_block_info *bi; + BUG_ON(item_size < sizeof(*ei) + sizeof(*bi)); + bi = (struct btrfs_tree_block_info *)(ei + 1); + WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi)); + } + + refs = btrfs_extent_refs(leaf, ei); + BUG_ON(refs < refs_to_drop); + refs -= refs_to_drop; + + if (refs > 0) { + /* + * In the case of inline back ref, reference count will + * be updated by remove_extent_backref + */ + if (iref) { + BUG_ON(!found_extent); + } else { + btrfs_set_extent_refs(leaf, ei, refs); + btrfs_mark_buffer_dirty(leaf); + } + if (found_extent) { + ret = remove_extent_backref(trans, extent_root, path, + iref, refs_to_drop, + is_data); + BUG_ON(ret); + } + } else { + int mark_free = 0; + int pin = 1; + + if (found_extent) { + BUG_ON(is_data && refs_to_drop != + extent_data_ref_count(root, path, iref)); + if (iref) { + BUG_ON(path->slots[0] != extent_slot); + } else { + BUG_ON(path->slots[0] != extent_slot + 1); + path->slots[0] = extent_slot; + num_to_del = 2; + } + } + + if (ops && ops->free_extent) { + ret = ops->free_extent(root, bytenr, num_bytes); + if (ret > 0) { + pin = 0; + mark_free = 0; + } + } + + if (pin) { + ret = pin_down_bytes(trans, root, bytenr, num_bytes, + is_data); + if (ret > 0) + mark_free = 1; + BUG_ON(ret < 0); + } + + ret = btrfs_del_items(trans, extent_root, path, path->slots[0], + num_to_del); + BUG_ON(ret); + btrfs_release_path(extent_root, path); + + if (is_data) { + ret = btrfs_del_csums(trans, root, bytenr, num_bytes); + BUG_ON(ret); + } + + ret = update_block_group(trans, root, bytenr, num_bytes, 0, + mark_free); + BUG_ON(ret); + } + btrfs_free_path(path); + finish_current_insert(trans, extent_root); + return ret; +} + +/* + * find all the blocks marked as pending in the radix tree and remove + * them from the extent map + */ +static int del_pending_extents(struct btrfs_trans_handle *trans, struct + btrfs_root *extent_root) +{ + int ret; + int err = 0; + u64 start; + u64 end; + u64 priv; + struct extent_io_tree *pending_del; + struct extent_io_tree *extent_ins; + struct pending_extent_op *extent_op; + + extent_ins = &extent_root->fs_info->extent_ins; + pending_del = &extent_root->fs_info->pending_del; + + while(1) { + ret = find_first_extent_bit(pending_del, 0, &start, &end, + EXTENT_LOCKED); + if (ret) + break; + + ret = get_state_private(pending_del, start, &priv); + BUG_ON(ret); + extent_op = (struct pending_extent_op *)(unsigned long)priv; + + clear_extent_bits(pending_del, start, end, EXTENT_LOCKED, + GFP_NOFS); + + if (!test_range_bit(extent_ins, start, end, + EXTENT_LOCKED, 0)) { + ret = __free_extent(trans, extent_root, + start, end + 1 - start, 0, + extent_root->root_key.objectid, + extent_op->level, 0, 1); + kfree(extent_op); + } else { + kfree(extent_op); + ret = get_state_private(extent_ins, start, &priv); + BUG_ON(ret); + extent_op = (struct pending_extent_op *) + (unsigned long)priv; + + clear_extent_bits(extent_ins, start, end, + EXTENT_LOCKED, GFP_NOFS); + + if (extent_op->type == PENDING_BACKREF_UPDATE) + BUG_ON(1); + + kfree(extent_op); + } + if (ret) + err = ret; + } + return err; +} + +/* + * remove an extent from the root, returns 0 on success + */ + +int btrfs_free_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 parent, + u64 root_objectid, u64 owner, u64 offset) +{ + struct btrfs_root *extent_root = root->fs_info->extent_root; + int pending_ret; + int ret; + + WARN_ON(num_bytes < root->sectorsize); + if (root == extent_root) { + struct pending_extent_op *extent_op; + + extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS); + BUG_ON(!extent_op); + + extent_op->type = PENDING_EXTENT_DELETE; + extent_op->bytenr = bytenr; + extent_op->num_bytes = num_bytes; + extent_op->level = (int)owner; + + set_extent_bits(&root->fs_info->pending_del, + bytenr, bytenr + num_bytes - 1, + EXTENT_LOCKED, GFP_NOFS); + set_state_private(&root->fs_info->pending_del, + bytenr, (unsigned long)extent_op); + return 0; + } + ret = __free_extent(trans, root, bytenr, num_bytes, parent, + root_objectid, owner, offset, 1); + pending_ret = del_pending_extents(trans, root->fs_info->extent_root); + return ret ? ret : pending_ret; +} + +static u64 stripe_align(struct btrfs_root *root, u64 val) +{ + u64 mask = ((u64)root->stripesize - 1); + u64 ret = (val + mask) & ~mask; + return ret; +} + +/* + * walks the btree of allocated extents and find a hole of a given size. + * The key ins is changed to record the hole: + * ins->objectid == block start + * ins->flags = BTRFS_EXTENT_ITEM_KEY + * ins->offset == number of blocks + * Any available blocks before search_start are skipped. + */ +static int noinline find_free_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *orig_root, + u64 num_bytes, u64 empty_size, + u64 search_start, u64 search_end, + u64 hint_byte, struct btrfs_key *ins, + u64 exclude_start, u64 exclude_nr, + int data) +{ + int ret; + u64 orig_search_start = search_start; + struct btrfs_root * root = orig_root->fs_info->extent_root; + struct btrfs_fs_info *info = root->fs_info; + u64 total_needed = num_bytes; + struct btrfs_block_group_cache *block_group; + int full_scan = 0; + int wrapped = 0; + + WARN_ON(num_bytes < root->sectorsize); + btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY); + + if (hint_byte) { + block_group = btrfs_lookup_first_block_group(info, hint_byte); + if (!block_group) + hint_byte = search_start; + block_group = btrfs_find_block_group(root, block_group, + hint_byte, data, 1); + } else { + block_group = btrfs_find_block_group(root, + trans->block_group, + search_start, data, 1); + } + + total_needed += empty_size; + +check_failed: + if (!block_group) { + block_group = btrfs_lookup_first_block_group(info, + search_start); + if (!block_group) + block_group = btrfs_lookup_first_block_group(info, + orig_search_start); + } + ret = find_search_start(root, &block_group, &search_start, + total_needed, data); + if (ret) + goto error; + + search_start = stripe_align(root, search_start); + ins->objectid = search_start; + ins->offset = num_bytes; + + if (ins->objectid + num_bytes > + block_group->key.objectid + block_group->key.offset) { + search_start = block_group->key.objectid + + block_group->key.offset; + goto new_group; + } + + if (test_range_bit(&info->extent_ins, ins->objectid, + ins->objectid + num_bytes -1, EXTENT_LOCKED, 0)) { + search_start = ins->objectid + num_bytes; + goto new_group; + } + + if (test_range_bit(&info->pinned_extents, ins->objectid, + ins->objectid + num_bytes -1, EXTENT_DIRTY, 0)) { + search_start = ins->objectid + num_bytes; + goto new_group; + } + + if (exclude_nr > 0 && (ins->objectid + num_bytes > exclude_start && + ins->objectid < exclude_start + exclude_nr)) { + search_start = exclude_start + exclude_nr; + goto new_group; + } + + if (!(data & BTRFS_BLOCK_GROUP_DATA)) { + block_group = btrfs_lookup_block_group(info, ins->objectid); + if (block_group) + trans->block_group = block_group; + } + ins->offset = num_bytes; + return 0; + +new_group: + block_group = btrfs_lookup_first_block_group(info, search_start); + if (!block_group) { + search_start = orig_search_start; + if (full_scan) { + ret = -ENOSPC; + goto error; + } + if (wrapped) { + if (!full_scan) + total_needed -= empty_size; + full_scan = 1; + } else + wrapped = 1; + } + cond_resched(); + block_group = btrfs_find_block_group(root, block_group, + search_start, data, 0); + goto check_failed; + +error: + return ret; +} + +static int btrfs_reserve_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 num_bytes, u64 empty_size, + u64 hint_byte, u64 search_end, + struct btrfs_key *ins, int data) +{ + int ret; + u64 search_start = 0; + u64 alloc_profile; + struct btrfs_fs_info *info = root->fs_info; + + if (info->extent_ops) { + struct btrfs_extent_ops *ops = info->extent_ops; + ret = ops->alloc_extent(root, num_bytes, hint_byte, ins); + BUG_ON(ret); + goto found; + } + + if (data) { + alloc_profile = info->avail_data_alloc_bits & + info->data_alloc_profile; + data = BTRFS_BLOCK_GROUP_DATA | alloc_profile; + } else if ((info->system_allocs > 0 || root == info->chunk_root) && + info->system_allocs >= 0) { + alloc_profile = info->avail_system_alloc_bits & + info->system_alloc_profile; + data = BTRFS_BLOCK_GROUP_SYSTEM | alloc_profile; + } else { + alloc_profile = info->avail_metadata_alloc_bits & + info->metadata_alloc_profile; + data = BTRFS_BLOCK_GROUP_METADATA | alloc_profile; + } + + if (root->ref_cows) { + if (!(data & BTRFS_BLOCK_GROUP_METADATA)) { + ret = do_chunk_alloc(trans, root->fs_info->extent_root, + num_bytes, + BTRFS_BLOCK_GROUP_METADATA); + BUG_ON(ret); + } + ret = do_chunk_alloc(trans, root->fs_info->extent_root, + num_bytes + 2 * 1024 * 1024, data); + BUG_ON(ret); + } + + WARN_ON(num_bytes < root->sectorsize); + ret = find_free_extent(trans, root, num_bytes, empty_size, + search_start, search_end, hint_byte, ins, + trans->alloc_exclude_start, + trans->alloc_exclude_nr, data); + BUG_ON(ret); +found: + clear_extent_dirty(&root->fs_info->free_space_cache, + ins->objectid, ins->objectid + ins->offset - 1, + GFP_NOFS); + return ret; +} + +static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 root_objectid, u64 generation, + u64 flags, struct btrfs_disk_key *key, + int level, struct btrfs_key *ins) +{ + int ret; + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_extent_item *extent_item; + struct btrfs_tree_block_info *block_info; + struct btrfs_extent_inline_ref *iref; + struct btrfs_path *path; + struct extent_buffer *leaf; + u32 size = sizeof(*extent_item) + sizeof(*block_info) + sizeof(*iref); + + path = btrfs_alloc_path(); + BUG_ON(!path); + + path->leave_spinning = 1; + ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path, + ins, size); + BUG_ON(ret); + + leaf = path->nodes[0]; + extent_item = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_item); + btrfs_set_extent_refs(leaf, extent_item, 1); + btrfs_set_extent_generation(leaf, extent_item, generation); + btrfs_set_extent_flags(leaf, extent_item, + flags | BTRFS_EXTENT_FLAG_TREE_BLOCK); + block_info = (struct btrfs_tree_block_info *)(extent_item + 1); + + btrfs_set_tree_block_key(leaf, block_info, key); + btrfs_set_tree_block_level(leaf, block_info, level); + + iref = (struct btrfs_extent_inline_ref *)(block_info + 1); + btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY); + btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid); + + btrfs_mark_buffer_dirty(leaf); + btrfs_free_path(path); + + ret = update_block_group(trans, root, ins->objectid, ins->offset, + 1, 0); + if (ret) { + printk(KERN_ERR "btrfs update block group failed for %llu " + "%llu\n", (unsigned long long)ins->objectid, + (unsigned long long)ins->offset); + BUG(); + } + return ret; +} + +static int alloc_tree_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 num_bytes, + u64 root_objectid, u64 generation, + u64 flags, struct btrfs_disk_key *key, + int level, u64 empty_size, u64 hint_byte, + u64 search_end, struct btrfs_key *ins) +{ + int ret; + ret = btrfs_reserve_extent(trans, root, num_bytes, empty_size, + hint_byte, search_end, ins, 0); + BUG_ON(ret); + + if (root_objectid == BTRFS_EXTENT_TREE_OBJECTID) { + struct pending_extent_op *extent_op; + + extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS); + BUG_ON(!extent_op); + + extent_op->type = PENDING_EXTENT_INSERT; + extent_op->bytenr = ins->objectid; + extent_op->num_bytes = ins->offset; + extent_op->level = level; + extent_op->flags = flags; + memcpy(&extent_op->key, key, sizeof(*key)); + + set_extent_bits(&root->fs_info->extent_ins, ins->objectid, + ins->objectid + ins->offset - 1, + EXTENT_LOCKED, GFP_NOFS); + set_state_private(&root->fs_info->extent_ins, + ins->objectid, (unsigned long)extent_op); + } else { + ret = alloc_reserved_tree_block(trans, root, root_objectid, + generation, flags, + key, level, ins); + finish_current_insert(trans, root->fs_info->extent_root); + del_pending_extents(trans, root->fs_info->extent_root); + } + return ret; +} + +/* + * helper function to allocate a block for a given tree + * returns the tree buffer or NULL. + */ +struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u32 blocksize, u64 root_objectid, + struct btrfs_disk_key *key, int level, + u64 hint, u64 empty_size) +{ + struct btrfs_key ins; + int ret; + struct extent_buffer *buf; + + ret = alloc_tree_block(trans, root, blocksize, root_objectid, + trans->transid, 0, key, level, + empty_size, hint, (u64)-1, &ins); + if (ret) { + BUG_ON(ret > 0); + return ERR_PTR(ret); + } + + buf = btrfs_find_create_tree_block(root, ins.objectid, blocksize); + if (!buf) { + btrfs_free_extent(trans, root, ins.objectid, ins.offset, + 0, root->root_key.objectid, level, 0); + BUG_ON(1); + return ERR_PTR(-ENOMEM); + } + btrfs_set_buffer_uptodate(buf); + trans->blocks_used++; + + return buf; +} + +#if 0 + +static int noinline drop_leaf_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *leaf) +{ + u64 leaf_owner; + u64 leaf_generation; + struct btrfs_key key; + struct btrfs_file_extent_item *fi; + int i; + int nritems; + int ret; + + BUG_ON(!btrfs_is_leaf(leaf)); + nritems = btrfs_header_nritems(leaf); + leaf_owner = btrfs_header_owner(leaf); + leaf_generation = btrfs_header_generation(leaf); + + for (i = 0; i < nritems; i++) { + u64 disk_bytenr; + + btrfs_item_key_to_cpu(leaf, &key, i); + if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) + continue; + fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item); + if (btrfs_file_extent_type(leaf, fi) == + BTRFS_FILE_EXTENT_INLINE) + continue; + /* + * FIXME make sure to insert a trans record that + * repeats the snapshot del on crash + */ + disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + if (disk_bytenr == 0) + continue; + ret = btrfs_free_extent(trans, root, disk_bytenr, + btrfs_file_extent_disk_num_bytes(leaf, fi), + leaf->start, leaf_owner, leaf_generation, + key.objectid, 0); + BUG_ON(ret); + } + return 0; +} + +static void noinline reada_walk_down(struct btrfs_root *root, + struct extent_buffer *node, + int slot) +{ + u64 bytenr; + u64 last = 0; + u32 nritems; + u32 refs; + u32 blocksize; + int ret; + int i; + int level; + int skipped = 0; + + nritems = btrfs_header_nritems(node); + level = btrfs_header_level(node); + if (level) + return; + + for (i = slot; i < nritems && skipped < 32; i++) { + bytenr = btrfs_node_blockptr(node, i); + if (last && ((bytenr > last && bytenr - last > 32 * 1024) || + (last > bytenr && last - bytenr > 32 * 1024))) { + skipped++; + continue; + } + blocksize = btrfs_level_size(root, level - 1); + if (i != slot) { + ret = btrfs_lookup_extent_ref(NULL, root, bytenr, + blocksize, &refs); + BUG_ON(ret); + if (refs != 1) { + skipped++; + continue; + } + } + mutex_unlock(&root->fs_info->fs_mutex); + ret = readahead_tree_block(root, bytenr, blocksize, + btrfs_node_ptr_generation(node, i)); + last = bytenr + blocksize; + cond_resched(); + mutex_lock(&root->fs_info->fs_mutex); + if (ret) + break; + } +} + +/* + * helper function for drop_snapshot, this walks down the tree dropping ref + * counts as it goes. + */ +static int noinline walk_down_tree(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, int *level) +{ + u64 root_owner; + u64 root_gen; + u64 bytenr; + u64 ptr_gen; + struct extent_buffer *next; + struct extent_buffer *cur; + struct extent_buffer *parent; + u32 blocksize; + int ret; + u32 refs; + + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + ret = btrfs_lookup_extent_ref(trans, root, + path->nodes[*level]->start, + path->nodes[*level]->len, &refs); + BUG_ON(ret); + if (refs > 1) + goto out; + + /* + * walk down to the last node level and free all the leaves + */ + while(*level >= 0) { + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + cur = path->nodes[*level]; + + if (btrfs_header_level(cur) != *level) + WARN_ON(1); + + if (path->slots[*level] >= + btrfs_header_nritems(cur)) + break; + if (*level == 0) { + ret = drop_leaf_ref(trans, root, cur); + BUG_ON(ret); + break; + } + bytenr = btrfs_node_blockptr(cur, path->slots[*level]); + ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]); + blocksize = btrfs_level_size(root, *level - 1); + ret = btrfs_lookup_extent_ref(trans, root, bytenr, blocksize, + &refs); + BUG_ON(ret); + if (refs != 1) { + parent = path->nodes[*level]; + root_owner = btrfs_header_owner(parent); + root_gen = btrfs_header_generation(parent); + path->slots[*level]++; + ret = btrfs_free_extent(trans, root, bytenr, blocksize, + parent->start, root_owner, + root_gen, *level - 1, 1); + BUG_ON(ret); + continue; + } + next = btrfs_find_tree_block(root, bytenr, blocksize); + if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) { + free_extent_buffer(next); + reada_walk_down(root, cur, path->slots[*level]); + mutex_unlock(&root->fs_info->fs_mutex); + next = read_tree_block(root, bytenr, blocksize, + ptr_gen); + mutex_lock(&root->fs_info->fs_mutex); + } + WARN_ON(*level <= 0); + if (path->nodes[*level-1]) + free_extent_buffer(path->nodes[*level-1]); + path->nodes[*level-1] = next; + *level = btrfs_header_level(next); + path->slots[*level] = 0; + } +out: + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + + if (path->nodes[*level] == root->node) { + root_owner = root->root_key.objectid; + parent = path->nodes[*level]; + } else { + parent = path->nodes[*level + 1]; + root_owner = btrfs_header_owner(parent); + } + + root_gen = btrfs_header_generation(parent); + ret = btrfs_free_extent(trans, root, path->nodes[*level]->start, + path->nodes[*level]->len, parent->start, + root_owner, root_gen, *level, 1); + free_extent_buffer(path->nodes[*level]); + path->nodes[*level] = NULL; + *level += 1; + BUG_ON(ret); + return 0; +} + +/* + * helper for dropping snapshots. This walks back up the tree in the path + * to find the first node higher up where we haven't yet gone through + * all the slots + */ +static int noinline walk_up_tree(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, int *level) +{ + u64 root_owner; + u64 root_gen; + struct btrfs_root_item *root_item = &root->root_item; + int i; + int slot; + int ret; + + for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { + slot = path->slots[i]; + if (slot < btrfs_header_nritems(path->nodes[i]) - 1) { + struct extent_buffer *node; + struct btrfs_disk_key disk_key; + node = path->nodes[i]; + path->slots[i]++; + *level = i; + WARN_ON(*level == 0); + btrfs_node_key(node, &disk_key, path->slots[i]); + memcpy(&root_item->drop_progress, + &disk_key, sizeof(disk_key)); + root_item->drop_level = i; + return 0; + } else { + struct extent_buffer *parent; + if (path->nodes[*level] == root->node) + parent = path->nodes[*level]; + else + parent = path->nodes[*level + 1]; + + root_owner = btrfs_header_owner(parent); + root_gen = btrfs_header_generation(parent); + ret = btrfs_free_extent(trans, root, + path->nodes[*level]->start, + path->nodes[*level]->len, + parent->start, root_owner, + root_gen, *level, 1); + BUG_ON(ret); + free_extent_buffer(path->nodes[*level]); + path->nodes[*level] = NULL; + *level = i + 1; + } + } + return 1; +} + +/* + * drop the reference count on the tree rooted at 'snap'. This traverses + * the tree freeing any blocks that have a ref count of zero after being + * decremented. + */ +int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root + *root) +{ + int ret = 0; + int wret; + int level; + struct btrfs_path *path; + int i; + int orig_level; + struct btrfs_root_item *root_item = &root->root_item; + + path = btrfs_alloc_path(); + BUG_ON(!path); + + level = btrfs_header_level(root->node); + orig_level = level; + if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { + path->nodes[level] = root->node; + extent_buffer_get(root->node); + path->slots[level] = 0; + } else { + struct btrfs_key key; + struct btrfs_disk_key found_key; + struct extent_buffer *node; + + btrfs_disk_key_to_cpu(&key, &root_item->drop_progress); + level = root_item->drop_level; + path->lowest_level = level; + wret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (wret < 0) { + ret = wret; + goto out; + } + node = path->nodes[level]; + btrfs_node_key(node, &found_key, path->slots[level]); + WARN_ON(memcmp(&found_key, &root_item->drop_progress, + sizeof(found_key))); + } + while(1) { + wret = walk_down_tree(trans, root, path, &level); + if (wret < 0) + ret = wret; + if (wret != 0) + break; + + wret = walk_up_tree(trans, root, path, &level); + if (wret < 0) + ret = wret; + if (wret != 0) + break; + /* + ret = -EAGAIN; + break; + */ + } + for (i = 0; i <= orig_level; i++) { + if (path->nodes[i]) { + free_extent_buffer(path->nodes[i]); + path->nodes[i] = NULL; + } + } +out: + btrfs_free_path(path); + return ret; +} + +#endif + +int btrfs_free_block_groups(struct btrfs_fs_info *info) +{ + u64 start; + u64 end; + u64 ptr; + int ret; + while(1) { + ret = find_first_extent_bit(&info->block_group_cache, 0, + &start, &end, (unsigned int)-1); + if (ret) + break; + ret = get_state_private(&info->block_group_cache, start, &ptr); + if (!ret) + kfree((void *)(unsigned long)ptr); + clear_extent_bits(&info->block_group_cache, start, + end, (unsigned int)-1, GFP_NOFS); + } + while(1) { + ret = find_first_extent_bit(&info->free_space_cache, 0, + &start, &end, EXTENT_DIRTY); + if (ret) + break; + clear_extent_dirty(&info->free_space_cache, start, + end, GFP_NOFS); + } + return 0; +} + +int find_first_block_group(struct btrfs_root *root, struct btrfs_path *path, + struct btrfs_key *key) +{ + int ret; + struct btrfs_key found_key; + struct extent_buffer *leaf; + int slot; + + ret = btrfs_search_slot(NULL, root, key, path, 0, 0); + if (ret < 0) + return ret; + while(1) { + slot = path->slots[0]; + leaf = path->nodes[0]; + if (slot >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, path); + if (ret == 0) + continue; + if (ret < 0) + goto error; + break; + } + btrfs_item_key_to_cpu(leaf, &found_key, slot); + + if (found_key.objectid >= key->objectid && + found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) + return 0; + path->slots[0]++; + } + ret = -ENOENT; +error: + return ret; +} + +int btrfs_read_block_groups(struct btrfs_root *root) +{ + struct btrfs_path *path; + int ret; + int bit; + struct btrfs_block_group_cache *cache; + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_space_info *space_info; + struct extent_io_tree *block_group_cache; + struct btrfs_key key; + struct btrfs_key found_key; + struct extent_buffer *leaf; + + block_group_cache = &info->block_group_cache; + + root = info->extent_root; + key.objectid = 0; + key.offset = 0; + btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY); + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + while(1) { + ret = find_first_block_group(root, path, &key); + if (ret > 0) { + ret = 0; + goto error; + } + if (ret != 0) { + goto error; + } + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + cache = kzalloc(sizeof(*cache), GFP_NOFS); + if (!cache) { + ret = -ENOMEM; + break; + } + + read_extent_buffer(leaf, &cache->item, + btrfs_item_ptr_offset(leaf, path->slots[0]), + sizeof(cache->item)); + memcpy(&cache->key, &found_key, sizeof(found_key)); + cache->cached = 0; + cache->pinned = 0; + key.objectid = found_key.objectid + found_key.offset; + btrfs_release_path(root, path); + cache->flags = btrfs_block_group_flags(&cache->item); + bit = 0; + if (cache->flags & BTRFS_BLOCK_GROUP_DATA) { + bit = BLOCK_GROUP_DATA; + } else if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) { + bit = BLOCK_GROUP_SYSTEM; + } else if (cache->flags & BTRFS_BLOCK_GROUP_METADATA) { + bit = BLOCK_GROUP_METADATA; + } + set_avail_alloc_bits(info, cache->flags); + if (btrfs_chunk_readonly(root, cache->key.objectid)) + cache->ro = 1; + + ret = update_space_info(info, cache->flags, found_key.offset, + btrfs_block_group_used(&cache->item), + &space_info); + BUG_ON(ret); + cache->space_info = space_info; + + /* use EXTENT_LOCKED to prevent merging */ + set_extent_bits(block_group_cache, found_key.objectid, + found_key.objectid + found_key.offset - 1, + bit | EXTENT_LOCKED, GFP_NOFS); + set_state_private(block_group_cache, found_key.objectid, + (unsigned long)cache); + } + ret = 0; +error: + btrfs_free_path(path); + return ret; +} + +int btrfs_make_block_group(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytes_used, + u64 type, u64 chunk_objectid, u64 chunk_offset, + u64 size) +{ + int ret; + int bit = 0; + struct btrfs_root *extent_root; + struct btrfs_block_group_cache *cache; + struct extent_io_tree *block_group_cache; + + extent_root = root->fs_info->extent_root; + block_group_cache = &root->fs_info->block_group_cache; + + cache = kzalloc(sizeof(*cache), GFP_NOFS); + BUG_ON(!cache); + cache->key.objectid = chunk_offset; + cache->key.offset = size; + + btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY); + btrfs_set_block_group_used(&cache->item, bytes_used); + btrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid); + cache->flags = type; + btrfs_set_block_group_flags(&cache->item, type); + + ret = update_space_info(root->fs_info, cache->flags, size, bytes_used, + &cache->space_info); + BUG_ON(ret); + + bit = block_group_state_bits(type); + set_extent_bits(block_group_cache, chunk_offset, + chunk_offset + size - 1, + bit | EXTENT_LOCKED, GFP_NOFS); + + set_state_private(block_group_cache, chunk_offset, + (unsigned long)cache); + ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item, + sizeof(cache->item)); + BUG_ON(ret); + + finish_current_insert(trans, extent_root); + ret = del_pending_extents(trans, extent_root); + BUG_ON(ret); + set_avail_alloc_bits(extent_root->fs_info, type); + return 0; +} + +/* + * This is for converter use only. + * + * In that case, we don't know where are free blocks located. + * Therefore all block group cache entries must be setup properly + * before doing any block allocation. + */ +int btrfs_make_block_groups(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + u64 total_bytes; + u64 cur_start; + u64 group_type; + u64 group_size; + u64 group_align; + u64 total_data = 0; + u64 total_metadata = 0; + u64 chunk_objectid; + int ret; + int bit; + struct btrfs_root *extent_root; + struct btrfs_block_group_cache *cache; + struct extent_io_tree *block_group_cache; + + extent_root = root->fs_info->extent_root; + block_group_cache = &root->fs_info->block_group_cache; + chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); + group_align = 64 * root->sectorsize; + + cur_start = 0; + while (cur_start < total_bytes) { + group_size = total_bytes / 12; + group_size = min_t(u64, group_size, total_bytes - cur_start); + if (cur_start == 0) { + bit = BLOCK_GROUP_SYSTEM; + group_type = BTRFS_BLOCK_GROUP_SYSTEM; + group_size /= 4; + group_size &= ~(group_align - 1); + group_size = max_t(u64, group_size, 8 * 1024 * 1024); + group_size = min_t(u64, group_size, 32 * 1024 * 1024); + } else { + group_size &= ~(group_align - 1); + if (total_data >= total_metadata * 2) { + group_type = BTRFS_BLOCK_GROUP_METADATA; + group_size = min_t(u64, group_size, + 1ULL * 1024 * 1024 * 1024); + total_metadata += group_size; + } else { + group_type = BTRFS_BLOCK_GROUP_DATA; + group_size = min_t(u64, group_size, + 5ULL * 1024 * 1024 * 1024); + total_data += group_size; + } + if ((total_bytes - cur_start) * 4 < group_size * 5) + group_size = total_bytes - cur_start; + } + + cache = kzalloc(sizeof(*cache), GFP_NOFS); + BUG_ON(!cache); + + cache->key.objectid = cur_start; + cache->key.offset = group_size; + btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY); + + btrfs_set_block_group_used(&cache->item, 0); + btrfs_set_block_group_chunk_objectid(&cache->item, + chunk_objectid); + btrfs_set_block_group_flags(&cache->item, group_type); + + cache->flags = group_type; + + ret = update_space_info(root->fs_info, group_type, group_size, + 0, &cache->space_info); + BUG_ON(ret); + set_avail_alloc_bits(extent_root->fs_info, group_type); + + set_extent_bits(block_group_cache, cur_start, + cur_start + group_size - 1, + bit | EXTENT_LOCKED, GFP_NOFS); + set_state_private(block_group_cache, cur_start, + (unsigned long)cache); + cur_start += group_size; + } + /* then insert all the items */ + cur_start = 0; + while(cur_start < total_bytes) { + cache = btrfs_lookup_block_group(root->fs_info, cur_start); + BUG_ON(!cache); + + ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item, + sizeof(cache->item)); + BUG_ON(ret); + + finish_current_insert(trans, extent_root); + ret = del_pending_extents(trans, extent_root); + BUG_ON(ret); + + cur_start = cache->key.objectid + cache->key.offset; + } + return 0; +} + +int btrfs_update_block_group(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 bytenr, u64 num_bytes, int alloc, + int mark_free) +{ + return update_block_group(trans, root, bytenr, num_bytes, + alloc, mark_free); +} diff --git a/lib/extent_io.c b/lib/extent_io.c new file mode 100644 index 0000000..069c199 --- /dev/null +++ b/lib/extent_io.c @@ -0,0 +1,781 @@ + +/* + * Copyright (C) 2007 Oracle. 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 600 +#define __USE_XOPEN2K +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "extent_io.h" +#include "list.h" + +u64 cache_max = 1024 * 1024 * 32; + +void extent_io_tree_init(struct extent_io_tree *tree) +{ + cache_tree_init(&tree->state); + cache_tree_init(&tree->cache); + INIT_LIST_HEAD(&tree->lru); + tree->cache_size = 0; +} + +static struct extent_state *alloc_extent_state(void) +{ + struct extent_state *state; + + state = malloc(sizeof(*state)); + if (!state) + return NULL; + state->refs = 1; + state->state = 0; + state->private = 0; + return state; +} + +static void free_extent_state(struct extent_state *state) +{ + state->refs--; + BUG_ON(state->refs < 0); + if (state->refs == 0) + free(state); +} + +void extent_io_tree_cleanup(struct extent_io_tree *tree) +{ + struct extent_state *es; + struct extent_buffer *eb; + struct cache_extent *cache; + + while(!list_empty(&tree->lru)) { + eb = list_entry(tree->lru.next, struct extent_buffer, lru); + if (eb->refs != 1) { + fprintf(stderr, "extent buffer leak: " + "start %llu len %u\n", + (unsigned long long)eb->start, eb->len); + eb->refs = 1; + } + free_extent_buffer(eb); + } + while (1) { + cache = find_first_cache_extent(&tree->state, 0); + if (!cache) + break; + es = container_of(cache, struct extent_state, cache_node); + remove_cache_extent(&tree->state, &es->cache_node); + free_extent_state(es); + } +} + +static inline void update_extent_state(struct extent_state *state) +{ + state->cache_node.start = state->start; + state->cache_node.size = state->end + 1 - state->start; +} + +/* + * Utility function to look for merge candidates inside a given range. + * Any extents with matching state are merged together into a single + * extent in the tree. Extents with EXTENT_IO in their state field are + * not merged + */ +static int merge_state(struct extent_io_tree *tree, + struct extent_state *state) +{ + struct extent_state *other; + struct cache_extent *other_node; + + if (state->state & EXTENT_IOBITS) + return 0; + + other_node = prev_cache_extent(&state->cache_node); + if (other_node) { + other = container_of(other_node, struct extent_state, + cache_node); + if (other->end == state->start - 1 && + other->state == state->state) { + state->start = other->start; + update_extent_state(state); + remove_cache_extent(&tree->state, &other->cache_node); + free_extent_state(other); + } + } + other_node = next_cache_extent(&state->cache_node); + if (other_node) { + other = container_of(other_node, struct extent_state, + cache_node); + if (other->start == state->end + 1 && + other->state == state->state) { + other->start = state->start; + update_extent_state(other); + remove_cache_extent(&tree->state, &state->cache_node); + free_extent_state(state); + } + } + return 0; +} + +/* + * insert an extent_state struct into the tree. 'bits' are set on the + * struct before it is inserted. + */ +static int insert_state(struct extent_io_tree *tree, + struct extent_state *state, u64 start, u64 end, + int bits) +{ + int ret; + + BUG_ON(end < start); + state->state |= bits; + state->start = start; + state->end = end; + update_extent_state(state); + ret = insert_existing_cache_extent(&tree->state, &state->cache_node); + BUG_ON(ret); + merge_state(tree, state); + return 0; +} + +/* + * split a given extent state struct in two, inserting the preallocated + * struct 'prealloc' as the newly created second half. 'split' indicates an + * offset inside 'orig' where it should be split. + */ +static int split_state(struct extent_io_tree *tree, struct extent_state *orig, + struct extent_state *prealloc, u64 split) +{ + int ret; + prealloc->start = orig->start; + prealloc->end = split - 1; + prealloc->state = orig->state; + update_extent_state(prealloc); + orig->start = split; + update_extent_state(orig); + ret = insert_existing_cache_extent(&tree->state, + &prealloc->cache_node); + BUG_ON(ret); + return 0; +} + +/* + * clear some bits on a range in the tree. + */ +static int clear_state_bit(struct extent_io_tree *tree, + struct extent_state *state, int bits) +{ + int ret = state->state & bits; + + state->state &= ~bits; + if (state->state == 0) { + remove_cache_extent(&tree->state, &state->cache_node); + free_extent_state(state); + } else { + merge_state(tree, state); + } + return ret; +} + +/* + * set some bits on a range in the tree. + */ +int clear_extent_bits(struct extent_io_tree *tree, u64 start, + u64 end, int bits, gfp_t mask) +{ + struct extent_state *state; + struct extent_state *prealloc = NULL; + struct cache_extent *node; + u64 last_end; + int err; + int set = 0; + +again: + prealloc = alloc_extent_state(); + if (!prealloc) + return -ENOMEM; + + /* + * this search will find the extents that end after + * our range starts + */ + node = find_first_cache_extent(&tree->state, start); + if (!node) + goto out; + state = container_of(node, struct extent_state, cache_node); + if (state->start > end) + goto out; + last_end = state->end; + + /* + * | ---- desired range ---- | + * | state | or + * | ------------- state -------------- | + * + * We need to split the extent we found, and may flip + * bits on second half. + * + * If the extent we found extends past our range, we + * just split and search again. It'll get split again + * the next time though. + * + * If the extent we found is inside our range, we clear + * the desired bit on it. + */ + if (state->start < start) { + err = split_state(tree, state, prealloc, start); + BUG_ON(err == -EEXIST); + prealloc = NULL; + if (err) + goto out; + if (state->end <= end) { + set |= clear_state_bit(tree, state, bits); + if (last_end == (u64)-1) + goto out; + start = last_end + 1; + } else { + start = state->start; + } + goto search_again; + } + /* + * | ---- desired range ---- | + * | state | + * We need to split the extent, and clear the bit + * on the first half + */ + if (state->start <= end && state->end > end) { + err = split_state(tree, state, prealloc, end + 1); + BUG_ON(err == -EEXIST); + + set |= clear_state_bit(tree, prealloc, bits); + prealloc = NULL; + goto out; + } + + start = state->end + 1; + set |= clear_state_bit(tree, state, bits); + if (last_end == (u64)-1) + goto out; + start = last_end + 1; + goto search_again; +out: + if (prealloc) + free_extent_state(prealloc); + return set; + +search_again: + if (start > end) + goto out; + goto again; +} + +/* + * set some bits on a range in the tree. + */ +int set_extent_bits(struct extent_io_tree *tree, u64 start, + u64 end, int bits, gfp_t mask) +{ + struct extent_state *state; + struct extent_state *prealloc = NULL; + struct cache_extent *node; + int err = 0; + int set; + u64 last_start; + u64 last_end; +again: + prealloc = alloc_extent_state(); + if (!prealloc) + return -ENOMEM; + + /* + * this search will find the extents that end after + * our range starts + */ + node = find_first_cache_extent(&tree->state, start); + if (!node) { + err = insert_state(tree, prealloc, start, end, bits); + BUG_ON(err == -EEXIST); + prealloc = NULL; + goto out; + } + + state = container_of(node, struct extent_state, cache_node); + last_start = state->start; + last_end = state->end; + + /* + * | ---- desired range ---- | + * | state | + * + * Just lock what we found and keep going + */ + if (state->start == start && state->end <= end) { + set = state->state & bits; + state->state |= bits; + merge_state(tree, state); + if (last_end == (u64)-1) + goto out; + start = last_end + 1; + goto search_again; + } + /* + * | ---- desired range ---- | + * | state | + * or + * | ------------- state -------------- | + * + * We need to split the extent we found, and may flip bits on + * second half. + * + * If the extent we found extends past our + * range, we just split and search again. It'll get split + * again the next time though. + * + * If the extent we found is inside our range, we set the + * desired bit on it. + */ + if (state->start < start) { + set = state->state & bits; + err = split_state(tree, state, prealloc, start); + BUG_ON(err == -EEXIST); + prealloc = NULL; + if (err) + goto out; + if (state->end <= end) { + state->state |= bits; + start = state->end + 1; + merge_state(tree, state); + if (last_end == (u64)-1) + goto out; + start = last_end + 1; + } else { + start = state->start; + } + goto search_again; + } + /* + * | ---- desired range ---- | + * | state | or | state | + * + * There's a hole, we need to insert something in it and + * ignore the extent we found. + */ + if (state->start > start) { + u64 this_end; + if (end < last_start) + this_end = end; + else + this_end = last_start -1; + err = insert_state(tree, prealloc, start, this_end, + bits); + BUG_ON(err == -EEXIST); + prealloc = NULL; + if (err) + goto out; + start = this_end + 1; + goto search_again; + } + /* + * | ---- desired range ---- | + * | ---------- state ---------- | + * We need to split the extent, and set the bit + * on the first half + */ + set = state->state & bits; + err = split_state(tree, state, prealloc, end + 1); + BUG_ON(err == -EEXIST); + + state->state |= bits; + merge_state(tree, prealloc); + prealloc = NULL; +out: + if (prealloc) + free_extent_state(prealloc); + return err; +search_again: + if (start > end) + goto out; + goto again; +} + +int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, + gfp_t mask) +{ + return set_extent_bits(tree, start, end, EXTENT_DIRTY, mask); +} + +int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, + gfp_t mask) +{ + return clear_extent_bits(tree, start, end, EXTENT_DIRTY, mask); +} + +int find_first_extent_bit(struct extent_io_tree *tree, u64 start, + u64 *start_ret, u64 *end_ret, int bits) +{ + struct cache_extent *node; + struct extent_state *state; + int ret = 1; + + /* + * this search will find all the extents that end after + * our range starts. + */ + node = find_first_cache_extent(&tree->state, start); + if (!node) + goto out; + + while(1) { + state = container_of(node, struct extent_state, cache_node); + if (state->end >= start && (state->state & bits)) { + *start_ret = state->start; + *end_ret = state->end; + ret = 0; + break; + } + node = next_cache_extent(node); + if (!node) + break; + } +out: + return ret; +} + +int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, + int bits, int filled) +{ + struct extent_state *state = NULL; + struct cache_extent *node; + int bitset = 0; + + node = find_first_cache_extent(&tree->state, start); + while (node && start <= end) { + state = container_of(node, struct extent_state, cache_node); + + if (filled && state->start > start) { + bitset = 0; + break; + } + if (state->start > end) + break; + if (state->state & bits) { + bitset = 1; + if (!filled) + break; + } else if (filled) { + bitset = 0; + break; + } + start = state->end + 1; + if (start > end) + break; + node = next_cache_extent(node); + if (!node) { + if (filled) + bitset = 0; + break; + } + } + return bitset; +} + +int set_state_private(struct extent_io_tree *tree, u64 start, u64 private) +{ + struct cache_extent *node; + struct extent_state *state; + int ret = 0; + + node = find_first_cache_extent(&tree->state, start); + if (!node) { + ret = -ENOENT; + goto out; + } + state = container_of(node, struct extent_state, cache_node); + if (state->start != start) { + ret = -ENOENT; + goto out; + } + state->private = private; +out: + return ret; +} + +int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private) +{ + struct cache_extent *node; + struct extent_state *state; + int ret = 0; + + node = find_first_cache_extent(&tree->state, start); + if (!node) { + ret = -ENOENT; + goto out; + } + state = container_of(node, struct extent_state, cache_node); + if (state->start != start) { + ret = -ENOENT; + goto out; + } + *private = state->private; +out: + return ret; +} + +static int free_some_buffers(struct extent_io_tree *tree) +{ + u32 nrscan = 0; + struct extent_buffer *eb; + struct list_head *node, *next; + + if (tree->cache_size < cache_max) + return 0; + list_for_each_safe(node, next, &tree->lru) { + eb = list_entry(node, struct extent_buffer, lru); + if (eb->refs == 1) { + free_extent_buffer(eb); + if (tree->cache_size < cache_max) + break; + } else { + list_move_tail(&eb->lru, &tree->lru); + } + if (nrscan++ > 64) + break; + } + return 0; +} + +static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree, + u64 bytenr, u32 blocksize) +{ + struct extent_buffer *eb; + int ret; + + eb = malloc(sizeof(struct extent_buffer) + blocksize); + if (!eb) { + BUG(); + return NULL; + } + + eb->start = bytenr; + eb->len = blocksize; + eb->refs = 2; + eb->flags = 0; + eb->tree = tree; + eb->fd = -1; + eb->dev_bytenr = (u64)-1; + eb->cache_node.start = bytenr; + eb->cache_node.size = blocksize; + + free_some_buffers(tree); + ret = insert_existing_cache_extent(&tree->cache, &eb->cache_node); + if (ret) { + free(eb); + return NULL; + } + list_add_tail(&eb->lru, &tree->lru); + tree->cache_size += blocksize; + return eb; +} + +void free_extent_buffer(struct extent_buffer *eb) +{ + if (!eb) + return; + + eb->refs--; + BUG_ON(eb->refs < 0); + if (eb->refs == 0) { + struct extent_io_tree *tree = eb->tree; + BUG_ON(eb->flags & EXTENT_DIRTY); + list_del_init(&eb->lru); + remove_cache_extent(&tree->cache, &eb->cache_node); + BUG_ON(tree->cache_size < eb->len); + tree->cache_size -= eb->len; + free(eb); + } +} + +struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree, + u64 bytenr, u32 blocksize) +{ + struct extent_buffer *eb = NULL; + struct cache_extent *cache; + + cache = find_cache_extent(&tree->cache, bytenr, blocksize); + if (cache && cache->start == bytenr && cache->size == blocksize) { + eb = container_of(cache, struct extent_buffer, cache_node); + list_move_tail(&eb->lru, &tree->lru); + eb->refs++; + } + return eb; +} + +struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree, + u64 start) +{ + struct extent_buffer *eb = NULL; + struct cache_extent *cache; + + cache = find_first_cache_extent(&tree->cache, start); + if (cache) { + eb = container_of(cache, struct extent_buffer, cache_node); + list_move_tail(&eb->lru, &tree->lru); + eb->refs++; + } + return eb; +} + +struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, + u64 bytenr, u32 blocksize) +{ + struct extent_buffer *eb; + struct cache_extent *cache; + + cache = find_cache_extent(&tree->cache, bytenr, blocksize); + if (cache && cache->start == bytenr && cache->size == blocksize) { + eb = container_of(cache, struct extent_buffer, cache_node); + list_move_tail(&eb->lru, &tree->lru); + eb->refs++; + } else { + if (cache) { + eb = container_of(cache, struct extent_buffer, + cache_node); + BUG_ON(eb->refs != 1); + free_extent_buffer(eb); + } + eb = __alloc_extent_buffer(tree, bytenr, blocksize); + } + return eb; +} + +int read_extent_from_disk(struct extent_buffer *eb) +{ + int ret; + ret = pread(eb->fd, eb->data, eb->len, eb->dev_bytenr); + if (ret < 0) + goto out; + if (ret != eb->len) { + ret = -EIO; + goto out; + } + ret = 0; +out: + return ret; +} + +int write_extent_to_disk(struct extent_buffer *eb) +{ + int ret; + ret = pwrite(eb->fd, eb->data, eb->len, eb->dev_bytenr); + if (ret < 0) + goto out; + if (ret != eb->len) { + ret = -EIO; + goto out; + } + ret = 0; +out: + return ret; +} + +int set_extent_buffer_uptodate(struct extent_buffer *eb) +{ + eb->flags |= EXTENT_UPTODATE; + return 0; +} + +int clear_extent_buffer_uptodate(struct extent_io_tree *tree, + struct extent_buffer *eb) +{ + eb->flags &= ~EXTENT_UPTODATE; + return 0; +} + +int extent_buffer_uptodate(struct extent_buffer *eb) +{ + if (eb->flags & EXTENT_UPTODATE) + return 1; + return 0; +} + +int set_extent_buffer_dirty(struct extent_buffer *eb) +{ + struct extent_io_tree *tree = eb->tree; + if (!(eb->flags & EXTENT_DIRTY)) { + eb->flags |= EXTENT_DIRTY; + set_extent_dirty(tree, eb->start, eb->start + eb->len - 1, 0); + extent_buffer_get(eb); + } + return 0; +} + +int clear_extent_buffer_dirty(struct extent_buffer *eb) +{ + struct extent_io_tree *tree = eb->tree; + if (eb->flags & EXTENT_DIRTY) { + eb->flags &= ~EXTENT_DIRTY; + clear_extent_dirty(tree, eb->start, eb->start + eb->len - 1, 0); + free_extent_buffer(eb); + } + return 0; +} + +int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv, + unsigned long start, unsigned long len) +{ + return memcmp(eb->data + start, ptrv, len); +} + +void read_extent_buffer(struct extent_buffer *eb, void *dst, + unsigned long start, unsigned long len) +{ + memcpy(dst, eb->data + start, len); +} + +void write_extent_buffer(struct extent_buffer *eb, const void *src, + unsigned long start, unsigned long len) +{ + memcpy(eb->data + start, src, len); +} + +void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, + unsigned long dst_offset, unsigned long src_offset, + unsigned long len) +{ + memcpy(dst->data + dst_offset, src->data + src_offset, len); +} + +void memcpy_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, + unsigned long src_offset, unsigned long len) +{ + memcpy(dst->data + dst_offset, dst->data + src_offset, len); +} + +void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, + unsigned long src_offset, unsigned long len) +{ + memmove(dst->data + dst_offset, dst->data + src_offset, len); +} + +void memset_extent_buffer(struct extent_buffer *eb, char c, + unsigned long start, unsigned long len) +{ + memset(eb->data + start, c, len); +} diff --git a/lib/extent_io.h b/lib/extent_io.h new file mode 100644 index 0000000..a5d6bf0 --- /dev/null +++ b/lib/extent_io.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __EXTENTMAP__ +#define __EXTENTMAP__ +#include "kerncompat.h" +#include "extent-cache.h" +#include "list.h" + +#define EXTENT_DIRTY 1 +#define EXTENT_WRITEBACK (1 << 1) +#define EXTENT_UPTODATE (1 << 2) +#define EXTENT_LOCKED (1 << 3) +#define EXTENT_NEW (1 << 4) +#define EXTENT_DELALLOC (1 << 5) +#define EXTENT_DEFRAG (1 << 6) +#define EXTENT_DEFRAG_DONE (1 << 7) +#define EXTENT_BUFFER_FILLED (1 << 8) +#define EXTENT_CSUM (1 << 9) +#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK) + +struct extent_io_tree { + struct cache_tree state; + struct cache_tree cache; + struct list_head lru; + u64 cache_size; +}; + +struct extent_state { + struct cache_extent cache_node; + u64 start; + u64 end; + int refs; + unsigned long state; + u64 private; +}; + +struct extent_buffer { + struct cache_extent cache_node; + u64 start; + u64 dev_bytenr; + u32 len; + struct extent_io_tree *tree; + struct list_head lru; + int refs; + int flags; + int fd; + char data[]; +}; + +static inline void extent_buffer_get(struct extent_buffer *eb) +{ + eb->refs++; +} + +void extent_io_tree_init(struct extent_io_tree *tree); +void extent_io_tree_cleanup(struct extent_io_tree *tree); +int set_extent_bits(struct extent_io_tree *tree, u64 start, + u64 end, int bits, gfp_t mask); +int clear_extent_bits(struct extent_io_tree *tree, u64 start, + u64 end, int bits, gfp_t mask); +int find_first_extent_bit(struct extent_io_tree *tree, u64 start, + u64 *start_ret, u64 *end_ret, int bits); +int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, + int bits, int filled); +int set_extent_dirty(struct extent_io_tree *tree, u64 start, + u64 end, gfp_t mask); +int clear_extent_dirty(struct extent_io_tree *tree, u64 start, + u64 end, gfp_t mask); +int extent_buffer_uptodate(struct extent_buffer *eb); +int set_extent_buffer_uptodate(struct extent_buffer *eb); +int clear_extent_buffer_uptodate(struct extent_io_tree *tree, + struct extent_buffer *eb); +int set_state_private(struct extent_io_tree *tree, u64 start, u64 private); +int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private); +struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree, + u64 bytenr, u32 blocksize); +struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree, + u64 start); +struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, + u64 bytenr, u32 blocksize); +void free_extent_buffer(struct extent_buffer *eb); +int read_extent_from_disk(struct extent_buffer *eb); +int write_extent_to_disk(struct extent_buffer *eb); +int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv, + unsigned long start, unsigned long len); +void read_extent_buffer(struct extent_buffer *eb, void *dst, + unsigned long start, unsigned long len); +void write_extent_buffer(struct extent_buffer *eb, const void *src, + unsigned long start, unsigned long len); +void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, + unsigned long dst_offset, unsigned long src_offset, + unsigned long len); +void memcpy_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, + unsigned long src_offset, unsigned long len); +void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, + unsigned long src_offset, unsigned long len); +void memset_extent_buffer(struct extent_buffer *eb, char c, + unsigned long start, unsigned long len); +int set_extent_buffer_dirty(struct extent_buffer *eb); +int clear_extent_buffer_dirty(struct extent_buffer *eb); +#endif diff --git a/lib/file-item.c b/lib/file-item.c new file mode 100644 index 0000000..9732282 --- /dev/null +++ b/lib/file-item.c @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "transaction.h" +#include "print-tree.h" +#include "crc32c.h" + +#define MAX_CSUM_ITEMS(r,size) ((((BTRFS_LEAF_DATA_SIZE(r) - \ + sizeof(struct btrfs_item) * 2) / \ + size) - 1)) +int btrfs_create_file(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 dirid, u64 *objectid) +{ + return 0; +} + +int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 objectid, u64 pos, u64 offset, + u64 disk_num_bytes, u64 num_bytes) +{ + int ret = 0; + struct btrfs_file_extent_item *item; + struct btrfs_key file_key; + struct btrfs_path *path; + struct extent_buffer *leaf; + + path = btrfs_alloc_path(); + BUG_ON(!path); + file_key.objectid = objectid; + file_key.offset = pos; + btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); + + ret = btrfs_insert_empty_item(trans, root, path, &file_key, + sizeof(*item)); + if (ret < 0) + goto out; + BUG_ON(ret); + leaf = path->nodes[0]; + item = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + btrfs_set_file_extent_disk_bytenr(leaf, item, offset); + btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes); + btrfs_set_file_extent_offset(leaf, item, 0); + btrfs_set_file_extent_num_bytes(leaf, item, num_bytes); + btrfs_set_file_extent_ram_bytes(leaf, item, num_bytes); + btrfs_set_file_extent_generation(leaf, item, trans->transid); + btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG); + btrfs_set_file_extent_compression(leaf, item, 0); + btrfs_set_file_extent_encryption(leaf, item, 0); + btrfs_set_file_extent_other_encoding(leaf, item, 0); + btrfs_mark_buffer_dirty(leaf); +out: + btrfs_free_path(path); + return ret; +} + +int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + u64 offset, char *buffer, size_t size) +{ + struct btrfs_key key; + struct btrfs_path *path; + struct extent_buffer *leaf; + unsigned long ptr; + struct btrfs_file_extent_item *ei; + u32 datasize; + int err = 0; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = objectid; + key.offset = offset; + btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); + + datasize = btrfs_file_extent_calc_inline_size(size); + ret = btrfs_insert_empty_item(trans, root, path, &key, datasize); + if (ret) { + err = ret; + goto fail; + } + + leaf = path->nodes[0]; + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + btrfs_set_file_extent_generation(leaf, ei, trans->transid); + btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE); + btrfs_set_file_extent_ram_bytes(leaf, ei, size); + btrfs_set_file_extent_compression(leaf, ei, 0); + btrfs_set_file_extent_encryption(leaf, ei, 0); + btrfs_set_file_extent_other_encoding(leaf, ei, 0); + + ptr = btrfs_file_extent_inline_start(ei) + offset - key.offset; + write_extent_buffer(leaf, buffer, ptr, size); + btrfs_mark_buffer_dirty(leaf); +fail: + btrfs_free_path(path); + return err; +} + +struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr, int cow) +{ + int ret; + struct btrfs_key file_key; + struct btrfs_key found_key; + struct btrfs_csum_item *item; + struct extent_buffer *leaf; + u64 csum_offset = 0; + u16 csum_size = + btrfs_super_csum_size(&root->fs_info->super_copy); + int csums_in_item; + + file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; + file_key.offset = bytenr; + btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY); + ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); + if (ret < 0) + goto fail; + leaf = path->nodes[0]; + if (ret > 0) { + ret = 1; + if (path->slots[0] == 0) + goto fail; + path->slots[0]--; + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY) + goto fail; + + csum_offset = (bytenr - found_key.offset) / root->sectorsize; + csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]); + csums_in_item /= csum_size; + + if (csum_offset >= csums_in_item) { + ret = -EFBIG; + goto fail; + } + } + item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); + item = (struct btrfs_csum_item *)((unsigned char *)item + + csum_offset * csum_size); + return item; +fail: + if (ret > 0) + ret = -ENOENT; + return ERR_PTR(ret); +} + +int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 objectid, + u64 offset, int mod) +{ + int ret; + struct btrfs_key file_key; + int ins_len = mod < 0 ? -1 : 0; + int cow = mod != 0; + + file_key.objectid = objectid; + file_key.offset = offset; + btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); + ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow); + return ret; +} + +int btrfs_csum_file_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 alloc_end, + u64 bytenr, char *data, size_t len) +{ + int ret; + struct btrfs_key file_key; + struct btrfs_key found_key; + u64 next_offset = (u64)-1; + int found_next = 0; + struct btrfs_path *path; + struct btrfs_csum_item *item; + struct extent_buffer *leaf = NULL; + u64 csum_offset; + u32 csum_result = ~(u32)0; + u32 nritems; + u32 ins_size; + u16 csum_size = + btrfs_super_csum_size(&root->fs_info->super_copy); + + path = btrfs_alloc_path(); + BUG_ON(!path); + + file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; + file_key.offset = bytenr; + file_key.type = BTRFS_EXTENT_CSUM_KEY; + + item = btrfs_lookup_csum(trans, root, path, bytenr, 1); + if (!IS_ERR(item)) { + leaf = path->nodes[0]; + goto found; + } + ret = PTR_ERR(item); + if (ret == -EFBIG) { + u32 item_size; + /* we found one, but it isn't big enough yet */ + leaf = path->nodes[0]; + item_size = btrfs_item_size_nr(leaf, path->slots[0]); + if ((item_size / csum_size) >= MAX_CSUM_ITEMS(root, csum_size)) { + /* already at max size, make a new one */ + goto insert; + } + } else { + int slot = path->slots[0] + 1; + /* we didn't find a csum item, insert one */ + nritems = btrfs_header_nritems(path->nodes[0]); + if (path->slots[0] >= nritems - 1) { + ret = btrfs_next_leaf(root, path); + if (ret == 1) + found_next = 1; + if (ret != 0) + goto insert; + slot = 0; + } + btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot); + if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || + found_key.type != BTRFS_EXTENT_CSUM_KEY) { + found_next = 1; + goto insert; + } + next_offset = found_key.offset; + found_next = 1; + goto insert; + } + + /* + * at this point, we know the tree has an item, but it isn't big + * enough yet to put our csum in. Grow it + */ + btrfs_release_path(root, path); + ret = btrfs_search_slot(trans, root, &file_key, path, + csum_size, 1); + if (ret < 0) + goto fail; + if (ret == 0) { + BUG(); + } + if (path->slots[0] == 0) { + goto insert; + } + path->slots[0]--; + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + csum_offset = (file_key.offset - found_key.offset) / root->sectorsize; + if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || + found_key.type != BTRFS_EXTENT_CSUM_KEY || + csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) { + goto insert; + } + if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) / + csum_size) { + u32 diff = (csum_offset + 1) * csum_size; + diff = diff - btrfs_item_size_nr(leaf, path->slots[0]); + if (diff != csum_size) + goto insert; + ret = btrfs_extend_item(trans, root, path, diff); + BUG_ON(ret); + goto csum; + } + +insert: + btrfs_release_path(root, path); + csum_offset = 0; + if (found_next) { + u64 tmp = min(alloc_end, next_offset); + tmp -= file_key.offset; + tmp /= root->sectorsize; + tmp = max((u64)1, tmp); + tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root, csum_size)); + ins_size = csum_size * tmp; + } else { + ins_size = csum_size; + } + ret = btrfs_insert_empty_item(trans, root, path, &file_key, + ins_size); + if (ret < 0) + goto fail; + if (ret != 0) { + WARN_ON(1); + goto fail; + } +csum: + leaf = path->nodes[0]; + item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); + ret = 0; + item = (struct btrfs_csum_item *)((unsigned char *)item + + csum_offset * csum_size); +found: + csum_result = btrfs_csum_data(root, data, csum_result, len); + btrfs_csum_final(csum_result, (char *)&csum_result); + if (csum_result == 0) { + printk("csum result is 0 for block %llu\n", + (unsigned long long)bytenr); + } + + write_extent_buffer(leaf, &csum_result, (unsigned long)item, + csum_size); + btrfs_mark_buffer_dirty(path->nodes[0]); +fail: + btrfs_release_path(root, path); + btrfs_free_path(path); + return ret; +} + +/* + * helper function for csum removal, this expects the + * key to describe the csum pointed to by the path, and it expects + * the csum to overlap the range [bytenr, len] + * + * The csum should not be entirely contained in the range and the + * range should not be entirely contained in the csum. + * + * This calls btrfs_truncate_item with the correct args based on the + * overlap, and fixes up the key as required. + */ +static noinline int truncate_one_csum(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_key *key, + u64 bytenr, u64 len) +{ + struct extent_buffer *leaf; + u16 csum_size = + btrfs_super_csum_size(&root->fs_info->super_copy); + u64 csum_end; + u64 end_byte = bytenr + len; + u32 blocksize = root->sectorsize; + int ret; + + leaf = path->nodes[0]; + csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; + csum_end *= root->sectorsize; + csum_end += key->offset; + + if (key->offset < bytenr && csum_end <= end_byte) { + /* + * [ bytenr - len ] + * [ ] + * [csum ] + * A simple truncate off the end of the item + */ + u32 new_size = (bytenr - key->offset) / blocksize; + new_size *= csum_size; + ret = btrfs_truncate_item(trans, root, path, new_size, 1); + BUG_ON(ret); + } else if (key->offset >= bytenr && csum_end > end_byte && + end_byte > key->offset) { + /* + * [ bytenr - len ] + * [ ] + * [csum ] + * we need to truncate from the beginning of the csum + */ + u32 new_size = (csum_end - end_byte) / blocksize; + new_size *= csum_size; + + ret = btrfs_truncate_item(trans, root, path, new_size, 0); + BUG_ON(ret); + + key->offset = end_byte; + ret = btrfs_set_item_key_safe(trans, root, path, key); + BUG_ON(ret); + } else { + BUG(); + } + return 0; +} + +/* + * deletes the csum items from the csum tree for a given + * range of bytes. + */ +int btrfs_del_csums(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytenr, u64 len) +{ + struct btrfs_path *path; + struct btrfs_key key; + u64 end_byte = bytenr + len; + u64 csum_end; + struct extent_buffer *leaf; + int ret; + u16 csum_size = + btrfs_super_csum_size(&root->fs_info->super_copy); + int blocksize = root->sectorsize; + + root = root->fs_info->csum_root; + + path = btrfs_alloc_path(); + + while (1) { + key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; + key.offset = end_byte - 1; + key.type = BTRFS_EXTENT_CSUM_KEY; + + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret > 0) { + if (path->slots[0] == 0) + goto out; + path->slots[0]--; + } + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + + if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || + key.type != BTRFS_EXTENT_CSUM_KEY) { + break; + } + + if (key.offset >= end_byte) + break; + + csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; + csum_end *= blocksize; + csum_end += key.offset; + + /* this csum ends before we start, we're done */ + if (csum_end <= bytenr) + break; + + /* delete the entire item, it is inside our range */ + if (key.offset >= bytenr && csum_end <= end_byte) { + ret = btrfs_del_item(trans, root, path); + BUG_ON(ret); + } else if (key.offset < bytenr && csum_end > end_byte) { + unsigned long offset; + unsigned long shift_len; + unsigned long item_offset; + /* + * [ bytenr - len ] + * [csum ] + * + * Our bytes are in the middle of the csum, + * we need to split this item and insert a new one. + * + * But we can't drop the path because the + * csum could change, get removed, extended etc. + * + * The trick here is the max size of a csum item leaves + * enough room in the tree block for a single + * item header. So, we split the item in place, + * adding a new header pointing to the existing + * bytes. Then we loop around again and we have + * a nicely formed csum item that we can neatly + * truncate. + */ + offset = (bytenr - key.offset) / blocksize; + offset *= csum_size; + + shift_len = (len / blocksize) * csum_size; + + item_offset = btrfs_item_ptr_offset(leaf, + path->slots[0]); + + memset_extent_buffer(leaf, 0, item_offset + offset, + shift_len); + key.offset = bytenr; + + /* + * btrfs_split_item returns -EAGAIN when the + * item changed size or key + */ + ret = btrfs_split_item(trans, root, path, &key, offset); + BUG_ON(ret && ret != -EAGAIN); + + key.offset = end_byte - 1; + } else { + ret = truncate_one_csum(trans, root, path, + &key, bytenr, len); + BUG_ON(ret); + } + btrfs_release_path(root, path); + } +out: + btrfs_free_path(path); + return 0; +} diff --git a/lib/hash.h b/lib/hash.h new file mode 100644 index 0000000..804fcda --- /dev/null +++ b/lib/hash.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __HASH__ +#define __HASH__ +#include "crc32c.h" + +static inline u64 btrfs_name_hash(const char *name, int len) +{ + return crc32c((u32)~1, name, len); +} +#endif diff --git a/lib/inode-item.c b/lib/inode-item.c new file mode 100644 index 0000000..28865f7 --- /dev/null +++ b/lib/inode-item.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2007 Oracle. 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 "ctree.h" +#include "disk-io.h" +#include "transaction.h" + +int find_name_in_backref(struct btrfs_path *path, const char * name, + int name_len, struct btrfs_inode_ref **ref_ret) +{ + struct extent_buffer *leaf; + struct btrfs_inode_ref *ref; + unsigned long ptr; + unsigned long name_ptr; + u32 item_size; + u32 cur_offset = 0; + int len; + + leaf = path->nodes[0]; + item_size = btrfs_item_size_nr(leaf, path->slots[0]); + ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); + while (cur_offset < item_size) { + ref = (struct btrfs_inode_ref *)(ptr + cur_offset); + len = btrfs_inode_ref_name_len(leaf, ref); + name_ptr = (unsigned long)(ref + 1); + cur_offset += len + sizeof(*ref); + if (len != name_len) + continue; + if (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) { + *ref_ret = ref; + return 1; + } + } + return 0; +} + +int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + const char *name, int name_len, + u64 inode_objectid, u64 ref_objectid) +{ + struct btrfs_path *path; + struct btrfs_key key; + struct btrfs_inode_ref *ref; + struct extent_buffer *leaf; + unsigned long ptr; + unsigned long item_start; + u32 item_size; + u32 sub_item_len; + int ret; + int del_len = name_len + sizeof(*ref); + + key.objectid = inode_objectid; + key.offset = ref_objectid; + btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY); + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret > 0) { + ret = -ENOENT; + goto out; + } else if (ret < 0) { + goto out; + } + if (!find_name_in_backref(path, name, name_len, &ref)) { + ret = -ENOENT; + goto out; + } + leaf = path->nodes[0]; + item_size = btrfs_item_size_nr(leaf, path->slots[0]); + if (del_len == item_size) { + ret = btrfs_del_item(trans, root, path); + goto out; + } + ptr = (unsigned long)ref; + sub_item_len = name_len + sizeof(*ref); + item_start = btrfs_item_ptr_offset(leaf, path->slots[0]); + memmove_extent_buffer(leaf, ptr, ptr + sub_item_len, + item_size - (ptr + sub_item_len - item_start)); + ret = btrfs_truncate_item(trans, root, path, + item_size - sub_item_len, 1); + BUG_ON(ret); +out: + btrfs_free_path(path); + return ret; +} + +int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + const char *name, int name_len, + u64 inode_objectid, u64 ref_objectid, u64 index) +{ + struct btrfs_path *path; + struct btrfs_key key; + struct btrfs_inode_ref *ref; + unsigned long ptr; + int ret; + int ins_len = name_len + sizeof(*ref); + + key.objectid = inode_objectid; + key.offset = ref_objectid; + btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY); + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ret = btrfs_insert_empty_item(trans, root, path, &key, + ins_len); + if (ret == -EEXIST) { + u32 old_size; + + if (find_name_in_backref(path, name, name_len, &ref)) + goto out; + + old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]); + ret = btrfs_extend_item(trans, root, path, ins_len); + BUG_ON(ret); + ref = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_ref); + ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size); + btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len); + btrfs_set_inode_ref_index(path->nodes[0], ref, index); + ptr = (unsigned long)(ref + 1); + ret = 0; + } else if (ret < 0) { + goto out; + } else { + ref = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_ref); + btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len); + btrfs_set_inode_ref_index(path->nodes[0], ref, index); + ptr = (unsigned long)(ref + 1); + } + write_extent_buffer(path->nodes[0], name, ptr, name_len); + btrfs_mark_buffer_dirty(path->nodes[0]); + +out: + btrfs_free_path(path); + return ret; +} + +int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 objectid) +{ + struct btrfs_key key; + int ret; + key.objectid = objectid; + btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); + key.offset = 0; + + ret = btrfs_insert_empty_item(trans, root, path, &key, + sizeof(struct btrfs_inode_item)); + if (ret == 0 && objectid > root->highest_inode) + root->highest_inode = objectid; + return ret; +} + +int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, + struct btrfs_key *location, int mod) +{ + int ins_len = mod < 0 ? -1 : 0; + int cow = mod != 0; + int ret; + int slot; + struct extent_buffer *leaf; + struct btrfs_key found_key; + + ret = btrfs_search_slot(trans, root, location, path, ins_len, cow); + if (ret > 0 && btrfs_key_type(location) == BTRFS_ROOT_ITEM_KEY && + location->offset == (u64)-1 && path->slots[0] != 0) { + slot = path->slots[0] - 1; + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &found_key, slot); + if (found_key.objectid == location->objectid && + btrfs_key_type(&found_key) == btrfs_key_type(location)) { + path->slots[0]--; + return 0; + } + } + return ret; +} + +int btrfs_insert_inode(struct btrfs_trans_handle *trans, struct btrfs_root + *root, u64 objectid, struct btrfs_inode_item + *inode_item) +{ + int ret; + struct btrfs_key key; + + key.objectid = objectid; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_insert_item(trans, root, &key, inode_item, + sizeof(*inode_item)); + return ret; +} diff --git a/lib/inode-map.c b/lib/inode-map.c new file mode 100644 index 0000000..d2970d4 --- /dev/null +++ b/lib/inode-map.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007 Oracle. 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 "ctree.h" +#include "disk-io.h" +#include "transaction.h" + +int btrfs_find_highest_inode(struct btrfs_root *root, u64 *objectid) +{ + struct btrfs_path *path; + int ret; + struct extent_buffer *l; + struct btrfs_key search_key; + struct btrfs_key found_key; + int slot; + + path = btrfs_alloc_path(); + BUG_ON(!path); + + search_key.objectid = (u64)-1; + search_key.offset = (u64)-1; + ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); + if (ret < 0) + goto error; + BUG_ON(ret == 0); + if (path->slots[0] > 0) { + slot = path->slots[0] - 1; + l = path->nodes[0]; + btrfs_item_key_to_cpu(l, &found_key, slot); + *objectid = found_key.objectid; + } else { + *objectid = BTRFS_FIRST_FREE_OBJECTID; + } + ret = 0; +error: + btrfs_free_path(path); + return ret; +} + +/* + * walks the btree of allocated inodes and find a hole. + */ +int btrfs_find_free_objectid(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 dirid, u64 *objectid) +{ + struct btrfs_path *path; + struct btrfs_key key; + int ret; + int slot = 0; + u64 last_ino = 0; + int start_found; + struct extent_buffer *l; + struct btrfs_key search_key; + u64 search_start = dirid; + + path = btrfs_alloc_path(); + BUG_ON(!path); + search_start = root->last_inode_alloc; + search_start = max((unsigned long long)search_start, + BTRFS_FIRST_FREE_OBJECTID); + search_key.objectid = search_start; + search_key.offset = 0; + + btrfs_init_path(path); + start_found = 0; + ret = btrfs_search_slot(trans, root, &search_key, path, 0, 0); + if (ret < 0) + goto error; + + if (path->slots[0] > 0) + path->slots[0]--; + + while (1) { + l = path->nodes[0]; + slot = path->slots[0]; + if (slot >= btrfs_header_nritems(l)) { + ret = btrfs_next_leaf(root, path); + if (ret == 0) + continue; + if (ret < 0) + goto error; + if (!start_found) { + *objectid = search_start; + start_found = 1; + goto found; + } + *objectid = last_ino > search_start ? + last_ino : search_start; + goto found; + } + btrfs_item_key_to_cpu(l, &key, slot); + if (key.objectid >= search_start) { + if (start_found) { + if (last_ino < search_start) + last_ino = search_start; + if (key.objectid > last_ino) { + *objectid = last_ino; + goto found; + } + } + } + start_found = 1; + last_ino = key.objectid + 1; + path->slots[0]++; + } + // FIXME -ENOSPC +found: + root->last_inode_alloc = *objectid; + btrfs_release_path(root, path); + btrfs_free_path(path); + BUG_ON(*objectid < search_start); + return 0; +error: + btrfs_release_path(root, path); + btrfs_free_path(path); + return ret; +} diff --git a/lib/ioctl.h b/lib/ioctl.h new file mode 100644 index 0000000..4410ac0 --- /dev/null +++ b/lib/ioctl.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __IOCTL_ +#define __IOCTL_ +#include +#include + +#define BTRFS_IOCTL_MAGIC 0x94 +#define BTRFS_VOL_NAME_MAX 255 +#define BTRFS_PATH_NAME_MAX 4087 + +struct btrfs_ioctl_vol_args { + __s64 fd; + char name[BTRFS_PATH_NAME_MAX + 1]; +}; + +#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \ + struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ + struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \ + struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_SCAN_DEV _IOW(BTRFS_IOCTL_MAGIC, 4, \ + struct btrfs_ioctl_vol_args) +/* trans start and trans end are dangerous, and only for + * use by applications that know how to avoid the + * resulting deadlocks + */ +#define BTRFS_IOC_TRANS_START _IO(BTRFS_IOCTL_MAGIC, 6) +#define BTRFS_IOC_TRANS_END _IO(BTRFS_IOCTL_MAGIC, 7) +#define BTRFS_IOC_SYNC _IO(BTRFS_IOCTL_MAGIC, 8) + +#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) +#define BTRFS_IOC_ADD_DEV _IOW(BTRFS_IOCTL_MAGIC, 10, \ + struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_RM_DEV _IOW(BTRFS_IOCTL_MAGIC, 11, \ + struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_BALANCE _IOW(BTRFS_IOCTL_MAGIC, 12, \ + struct btrfs_ioctl_vol_args) +/* 13 is for CLONE_RANGE */ +#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ + struct btrfs_ioctl_vol_args) + +#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ + struct btrfs_ioctl_vol_args) +#endif diff --git a/lib/kerncompat.h b/lib/kerncompat.h new file mode 100644 index 0000000..e4c8ce0 --- /dev/null +++ b/lib/kerncompat.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __KERNCOMPAT +#define __KERNCOMPAT +#include +#include +#include +#include +#include +#include +#include + +#ifndef READ +#define READ 0 +#define WRITE 1 +#define READA 2 +#endif + +#define gfp_t int +#define get_cpu_var(p) (p) +#define __get_cpu_var(p) (p) +#define BITS_PER_LONG (sizeof(long) * 8) +#define __GFP_BITS_SHIFT 20 +#define __GFP_BITS_MASK ((int)((1 << __GFP_BITS_SHIFT) - 1)) +#define GFP_KERNEL 0 +#define GFP_NOFS 0 +#define __read_mostly +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define ULONG_MAX (~0UL) +#define BUG() abort() +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise__ __attribute__((bitwise)) +#else +#define __force +#define __bitwise__ +#endif + +#ifndef __CHECKER__ +#include +typedef __u32 u32; +typedef __u64 u64; +typedef __u16 u16; +typedef __u8 u8; +#else +typedef unsigned int u32; +typedef unsigned int __u32; +typedef unsigned long long u64; +typedef unsigned char u8; +typedef unsigned short u16; +#endif + + +struct vma_shared { int prio_tree_node; }; +struct vm_area_struct { + unsigned long vm_pgoff; + unsigned long vm_start; + unsigned long vm_end; + struct vma_shared shared; +}; + +struct page { + unsigned long index; +}; + +struct mutex { + unsigned long lock; +}; + +#define mutex_init(m) \ +do { \ + (m)->lock = 1; \ +} while (0) + +static inline void mutex_lock(struct mutex *m) +{ + m->lock--; +} + +static inline void mutex_unlock(struct mutex *m) +{ + m->lock++; +} + +static inline int mutex_is_locked(struct mutex *m) +{ + return (m->lock != 1); +} + +#define cond_resched() do { } while (0) +#define preempt_enable() do { } while (0) +#define preempt_disable() do { } while (0) + +#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +/** + * __set_bit - Set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * Unlike set_bit(), this function is non-atomic and may be reordered. + * If it's called on the same region of memory simultaneously, the effect + * may be that only one operation succeeds. + */ +static inline void __set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + + *p |= mask; +} + +static inline void __clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + + *p &= ~mask; +} + +/** + * test_bit - Determine whether a bit is set + * @nr: bit number to test + * @addr: Address to start counting from + */ +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); +} + +/* + * error pointer + */ +#define MAX_ERRNO 4095 +#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO) + +static inline void *ERR_PTR(long error) +{ + return (void *) error; +} + +static inline long PTR_ERR(const void *ptr) +{ + return (long) ptr; +} + +static inline long IS_ERR(const void *ptr) +{ + return IS_ERR_VALUE((unsigned long)ptr); +} + +/* + * max/min macro + */ +#define min(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#define max(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +#define max_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) + +/* + * printk + */ +#define printk(fmt, args...) fprintf(stderr, fmt, ##args) +#define KERN_CRIT "" +#define KERN_ERR "" + +/* + * kmalloc/kfree + */ +#define kmalloc(x, y) malloc(x) +#define kzalloc(x, y) calloc(1, x) +#define kstrdup(x, y) strdup(x) +#define kfree(x) free(x) + +#define BUG_ON(c) assert(!(c)) +#define WARN_ON(c) assert(!(c)) + +#undef offsetof +#ifdef __compiler_offsetof +#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) +#else +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#ifdef __CHECKER__ +#define __CHECK_ENDIAN__ +#define __bitwise __bitwise__ +#else +#define __bitwise +#endif + +typedef u16 __bitwise __le16; +typedef u16 __bitwise __be16; +typedef u32 __bitwise __le32; +typedef u32 __bitwise __be32; +typedef u64 __bitwise __le64; +typedef u64 __bitwise __be64; + +/* Macros to generate set/get funcs for the struct fields + * assume there is a lefoo_to_cpu for every type, so lets make a simple + * one for u8: + */ +#define le8_to_cpu(v) (v) +#define cpu_to_le8(v) (v) +#define __le8 u8 + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le64(x) ((__force __le64)(u64)(bswap_64(x))) +#define le64_to_cpu(x) ((__force u64)(__le64)(bswap_64(x))) +#define cpu_to_le32(x) ((__force __le32)(u32)(bswap_32(x))) +#define le32_to_cpu(x) ((__force u32)(__le32)(bswap_32(x))) +#define cpu_to_le16(x) ((__force __le16)(u16)(bswap_16(x))) +#define le16_to_cpu(x) ((__force u16)(__le16)(bswap_16(x))) +#else +#define cpu_to_le64(x) ((__force __le64)(u64)(x)) +#define le64_to_cpu(x) ((__force u64)(__le64)(x)) +#define cpu_to_le32(x) ((__force __le32)(u32)(x)) +#define le32_to_cpu(x) ((__force u32)(__le32)(x)) +#define cpu_to_le16(x) ((__force __le16)(u16)(x)) +#define le16_to_cpu(x) ((__force u16)(__le16)(x)) +#endif +#endif + +#ifndef noinline +#define noinline +#endif diff --git a/lib/list.h b/lib/list.h new file mode 100644 index 0000000..d31090c --- /dev/null +++ b/lib/list.h @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +#ifndef CONFIG_DEBUG_LIST +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} +#else +extern void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next); +#endif + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +#ifndef CONFIG_DEBUG_LIST +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} +#else +extern void list_add(struct list_head *new, struct list_head *head); +#endif + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +#ifndef CONFIG_DEBUG_LIST +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} +#else +extern void list_del(struct list_head *entry); +#endif + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * Note: if 'old' was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue. + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +#endif diff --git a/lib/print-tree.c b/lib/print-tree.c new file mode 100644 index 0000000..59f4358 --- /dev/null +++ b/lib/print-tree.c @@ -0,0 +1,667 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" + +static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item, + struct btrfs_dir_item *di) +{ + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u32 data_len; + char namebuf[BTRFS_NAME_LEN]; + struct btrfs_disk_key location; + + total = btrfs_item_size(eb, item); + while(cur < total) { + btrfs_dir_item_key(eb, di, &location); + printf("\t\tlocation "); + btrfs_print_key(&location); + printf(" type %u\n", btrfs_dir_type(eb, di)); + name_len = btrfs_dir_name_len(eb, di); + data_len = btrfs_dir_data_len(eb, di); + len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf); + read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len); + printf("\t\tnamelen %u datalen %u name: %.*s\n", + name_len, data_len, len, namebuf); + len = sizeof(*di) + name_len + data_len; + di = (struct btrfs_dir_item *)((char *)di + len); + cur += len; + } + return 0; +} + +static int print_inode_ref_item(struct extent_buffer *eb, struct btrfs_item *item, + struct btrfs_inode_ref *ref) +{ + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u64 index; + char namebuf[BTRFS_NAME_LEN]; + total = btrfs_item_size(eb, item); + while(cur < total) { + name_len = btrfs_inode_ref_name_len(eb, ref); + index = btrfs_inode_ref_index(eb, ref); + len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf); + read_extent_buffer(eb, namebuf, (unsigned long)(ref + 1), len); + printf("\t\tinode ref index %llu namelen %u name: %.*s\n", + (unsigned long long)index, name_len, len, namebuf); + len = sizeof(*ref) + name_len; + ref = (struct btrfs_inode_ref *)((char *)ref + len); + cur += len; + } + return 0; +} + +static void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk) +{ + int num_stripes = btrfs_chunk_num_stripes(eb, chunk); + int i; + printf("\t\tchunk length %llu owner %llu type %llu num_stripes %d\n", + (unsigned long long)btrfs_chunk_length(eb, chunk), + (unsigned long long)btrfs_chunk_owner(eb, chunk), + (unsigned long long)btrfs_chunk_type(eb, chunk), + num_stripes); + for (i = 0 ; i < num_stripes ; i++) { + printf("\t\t\tstripe %d devid %llu offset %llu\n", i, + (unsigned long long)btrfs_stripe_devid_nr(eb, chunk, i), + (unsigned long long)btrfs_stripe_offset_nr(eb, chunk, i)); + } +} +static void print_dev_item(struct extent_buffer *eb, + struct btrfs_dev_item *dev_item) +{ + printf("\t\tdev item devid %llu " + "total_bytes %llu bytes used %Lu\n", + (unsigned long long)btrfs_device_id(eb, dev_item), + (unsigned long long)btrfs_device_total_bytes(eb, dev_item), + (unsigned long long)btrfs_device_bytes_used(eb, dev_item)); +} + +static void print_uuids(struct extent_buffer *eb) +{ + char fs_uuid[37]; + char chunk_uuid[37]; + u8 disk_uuid[BTRFS_UUID_SIZE]; + + read_extent_buffer(eb, disk_uuid, (unsigned long)btrfs_header_fsid(eb), + BTRFS_FSID_SIZE); + + fs_uuid[36] = '\0'; + uuid_unparse(disk_uuid, fs_uuid); + + read_extent_buffer(eb, disk_uuid, + (unsigned long)btrfs_header_chunk_tree_uuid(eb), + BTRFS_UUID_SIZE); + + chunk_uuid[36] = '\0'; + uuid_unparse(disk_uuid, chunk_uuid); + printf("fs uuid %s\nchunk uuid %s\n", fs_uuid, chunk_uuid); +} + +static void print_file_extent_item(struct extent_buffer *eb, + struct btrfs_item *item, + struct btrfs_file_extent_item *fi) +{ + int extent_type = btrfs_file_extent_type(eb, fi); + + if (extent_type == BTRFS_FILE_EXTENT_INLINE) { + printf("\t\tinline extent data size %u " + "ram %u compress %d\n", + btrfs_file_extent_inline_item_len(eb, item), + btrfs_file_extent_inline_len(eb, fi), + btrfs_file_extent_compression(eb, fi)); + return; + } + if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) { + printf("\t\tprealloc data disk byte %llu nr %llu\n", + (unsigned long long)btrfs_file_extent_disk_bytenr(eb, fi), + (unsigned long long)btrfs_file_extent_disk_num_bytes(eb, fi)); + printf("\t\tprealloc data offset %llu nr %llu\n", + (unsigned long long)btrfs_file_extent_offset(eb, fi), + (unsigned long long)btrfs_file_extent_num_bytes(eb, fi)); + return; + } + printf("\t\textent data disk byte %llu nr %llu\n", + (unsigned long long)btrfs_file_extent_disk_bytenr(eb, fi), + (unsigned long long)btrfs_file_extent_disk_num_bytes(eb, fi)); + printf("\t\textent data offset %llu nr %llu ram %llu\n", + (unsigned long long)btrfs_file_extent_offset(eb, fi), + (unsigned long long)btrfs_file_extent_num_bytes(eb, fi), + (unsigned long long)btrfs_file_extent_ram_bytes(eb, fi)); + printf("\t\textent compression %d\n", + btrfs_file_extent_compression(eb, fi)); +} + +static void print_extent_item(struct extent_buffer *eb, int slot) +{ + struct btrfs_extent_item *ei; + struct btrfs_extent_inline_ref *iref; + struct btrfs_extent_data_ref *dref; + struct btrfs_shared_data_ref *sref; + struct btrfs_disk_key key; + unsigned long end; + unsigned long ptr; + int type; + u32 item_size = btrfs_item_size_nr(eb, slot); + u64 flags; + u64 offset; + + if (item_size < sizeof(*ei)) { +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + struct btrfs_extent_item_v0 *ei0; + BUG_ON(item_size != sizeof(*ei0)); + ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0); + printf("\t\textent refs %u\n", + btrfs_extent_refs_v0(eb, ei0)); + return; +#else + BUG(); +#endif + } + + ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); + flags = btrfs_extent_flags(eb, ei); + + printf("\t\textent refs %llu gen %llu flags %llu\n", + (unsigned long long)btrfs_extent_refs(eb, ei), + (unsigned long long)btrfs_extent_generation(eb, ei), + (unsigned long long)flags); + + if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { + struct btrfs_tree_block_info *info; + info = (struct btrfs_tree_block_info *)(ei + 1); + btrfs_tree_block_key(eb, info, &key); + printf("\t\ttree block key (%llu %x %llu) level %d\n", + (unsigned long long)btrfs_disk_key_objectid(&key), + key.type, + (unsigned long long)btrfs_disk_key_offset(&key), + btrfs_tree_block_level(eb, info)); + iref = (struct btrfs_extent_inline_ref *)(info + 1); + } else { + iref = (struct btrfs_extent_inline_ref *)(ei + 1); + } + + ptr = (unsigned long)iref; + end = (unsigned long)ei + item_size; + while (ptr < end) { + iref = (struct btrfs_extent_inline_ref *)ptr; + type = btrfs_extent_inline_ref_type(eb, iref); + offset = btrfs_extent_inline_ref_offset(eb, iref); + switch (type) { + case BTRFS_TREE_BLOCK_REF_KEY: + printf("\t\ttree block backref root %llu\n", + (unsigned long long)offset); + break; + case BTRFS_SHARED_BLOCK_REF_KEY: + printf("\t\tshared block backref parent %llu\n", + (unsigned long long)offset); + break; + case BTRFS_EXTENT_DATA_REF_KEY: + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + printf("\t\textent data backref root %llu " + "objectid %llu offset %llu count %u\n", + (unsigned long long)btrfs_extent_data_ref_root(eb, dref), + (unsigned long long)btrfs_extent_data_ref_objectid(eb, dref), + (unsigned long long)btrfs_extent_data_ref_offset(eb, dref), + btrfs_extent_data_ref_count(eb, dref)); + break; + case BTRFS_SHARED_DATA_REF_KEY: + sref = (struct btrfs_shared_data_ref *)(iref + 1); + printf("\t\tshared data backref parent %llu count %u\n", + (unsigned long long)offset, + btrfs_shared_data_ref_count(eb, sref)); + break; + default: + BUG(); + } + ptr += btrfs_extent_inline_ref_size(type); + } + WARN_ON(ptr > end); +} + +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 +static void print_extent_ref_v0(struct extent_buffer *eb, int slot) +{ + struct btrfs_extent_ref_v0 *ref0; + + ref0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_ref_v0); + printf("\t\textent back ref root %llu gen %llu " + "owner %llu num_refs %lu\n", + (unsigned long long)btrfs_ref_root_v0(eb, ref0), + (unsigned long long)btrfs_ref_generation_v0(eb, ref0), + (unsigned long long)btrfs_ref_objectid_v0(eb, ref0), + (unsigned long)btrfs_ref_count_v0(eb, ref0)); +} +#endif + +static void print_root_ref(struct extent_buffer *leaf, int slot, char *tag) +{ + struct btrfs_root_ref *ref; + char namebuf[BTRFS_NAME_LEN]; + int namelen; + + ref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); + namelen = btrfs_root_ref_name_len(leaf, ref); + read_extent_buffer(leaf, namebuf, (unsigned long)(ref + 1), namelen); + printf("\t\troot %s key dirid %llu sequence %llu name %.*s\n", tag, + (unsigned long long)btrfs_root_ref_dirid(leaf, ref), + (unsigned long long)btrfs_root_ref_sequence(leaf, ref), + namelen, namebuf); +} + +static void print_key_type(u8 type) +{ + switch (type) { + case BTRFS_INODE_ITEM_KEY: + printf("INODE_ITEM"); + break; + case BTRFS_INODE_REF_KEY: + printf("INODE_REF"); + break; + case BTRFS_DIR_ITEM_KEY: + printf("DIR_ITEM"); + break; + case BTRFS_DIR_INDEX_KEY: + printf("DIR_INDEX"); + break; + case BTRFS_DIR_LOG_ITEM_KEY: + printf("DIR_LOG_ITEM"); + break; + case BTRFS_DIR_LOG_INDEX_KEY: + printf("DIR_LOG_INDEX"); + break; + case BTRFS_XATTR_ITEM_KEY: + printf("XATTR_ITEM"); + break; + case BTRFS_ORPHAN_ITEM_KEY: + printf("ORPHAN_ITEM"); + break; + case BTRFS_ROOT_ITEM_KEY: + printf("ROOT_ITEM"); + break; + case BTRFS_ROOT_REF_KEY: + printf("ROOT_REF"); + break; + case BTRFS_ROOT_BACKREF_KEY: + printf("ROOT_BACKREF"); + break; + case BTRFS_EXTENT_ITEM_KEY: + printf("EXTENT_ITEM"); + break; + case BTRFS_TREE_BLOCK_REF_KEY: + printf("TREE_BLOCK_REF"); + break; + case BTRFS_SHARED_BLOCK_REF_KEY: + printf("SHARED_BLOCK_REF"); + break; + case BTRFS_EXTENT_DATA_REF_KEY: + printf("EXTENT_DATA_REF"); + break; + case BTRFS_SHARED_DATA_REF_KEY: + printf("SHARED_DATA_REF"); + break; + case BTRFS_EXTENT_REF_V0_KEY: + printf("EXTENT_REF_V0"); + break; + case BTRFS_CSUM_ITEM_KEY: + printf("CSUM_ITEM"); + break; + case BTRFS_EXTENT_CSUM_KEY: + printf("EXTENT_CSUM"); + break; + case BTRFS_EXTENT_DATA_KEY: + printf("EXTENT_DATA"); + break; + case BTRFS_BLOCK_GROUP_ITEM_KEY: + printf("BLOCK_GROUP_ITEM"); + break; + case BTRFS_CHUNK_ITEM_KEY: + printf("CHUNK_ITEM"); + break; + case BTRFS_DEV_ITEM_KEY: + printf("DEV_ITEM"); + break; + case BTRFS_DEV_EXTENT_KEY: + printf("DEV_EXTENT"); + break; + case BTRFS_STRING_ITEM_KEY: + printf("STRING_ITEM"); + break; + default: + printf("UNKNOWN"); + }; +} + +static void print_objectid(unsigned long long objectid, u8 type) +{ + if (type == BTRFS_DEV_EXTENT_KEY) { + printf("%llu", objectid); /* device id */ + return; + } + + switch (objectid) { + case BTRFS_ROOT_TREE_OBJECTID: + if (type == BTRFS_DEV_ITEM_KEY) + printf("DEV_ITEMS"); + else + printf("ROOT_TREE"); + break; + case BTRFS_EXTENT_TREE_OBJECTID: + printf("EXTENT_TREE"); + break; + case BTRFS_CHUNK_TREE_OBJECTID: + printf("CHUNK_TREE"); + break; + case BTRFS_DEV_TREE_OBJECTID: + printf("DEV_TREE"); + break; + case BTRFS_FS_TREE_OBJECTID: + printf("FS_TREE"); + break; + case BTRFS_ROOT_TREE_DIR_OBJECTID: + printf("ROOT_TREE_DIR"); + break; + case BTRFS_CSUM_TREE_OBJECTID: + printf("CSUM_TREE"); + break; + case BTRFS_ORPHAN_OBJECTID: + printf("ORPHAN"); + break; + case BTRFS_TREE_LOG_OBJECTID: + printf("TREE_LOG"); + break; + case BTRFS_TREE_LOG_FIXUP_OBJECTID: + printf("LOG_FIXUP"); + break; + case BTRFS_TREE_RELOC_OBJECTID: + printf("TREE_RELOC"); + break; + case BTRFS_DATA_RELOC_TREE_OBJECTID: + printf("DATA_RELOC_TREE"); + break; + case BTRFS_EXTENT_CSUM_OBJECTID: + printf("EXTENT_CSUM"); + break; + case BTRFS_MULTIPLE_OBJECTIDS: + printf("MULTIPLE"); + break; + case BTRFS_FIRST_CHUNK_TREE_OBJECTID: + printf("FIRST_CHUNK_TREE"); + break; + default: + printf("%llu", objectid); + } +} + +void btrfs_print_key(struct btrfs_disk_key *disk_key) +{ + u8 type; + printf("key ("); + type = btrfs_disk_key_type(disk_key); + print_objectid((unsigned long long)btrfs_disk_key_objectid(disk_key), + type); + printf(" "); + print_key_type(type); + printf(" %llu)", (unsigned long long)btrfs_disk_key_offset(disk_key)); +} + +void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) +{ + int i; + char *str; + struct btrfs_item *item; + struct btrfs_root_item *ri; + struct btrfs_dir_item *di; + struct btrfs_inode_item *ii; + struct btrfs_file_extent_item *fi; + struct btrfs_csum_item *ci; + struct btrfs_block_group_item *bi; + struct btrfs_extent_data_ref *dref; + struct btrfs_shared_data_ref *sref; + struct btrfs_inode_ref *iref; + struct btrfs_dev_extent *dev_extent; + struct btrfs_disk_key disk_key; + struct btrfs_root_item root_item; + struct btrfs_block_group_item bg_item; + struct btrfs_dir_log_item *dlog; + u32 nr = btrfs_header_nritems(l); + u32 type; + + printf("leaf %llu items %d free space %d generation %llu owner %llu\n", + (unsigned long long)btrfs_header_bytenr(l), nr, + btrfs_leaf_free_space(root, l), + (unsigned long long)btrfs_header_generation(l), + (unsigned long long)btrfs_header_owner(l)); + print_uuids(l); + fflush(stdout); + for (i = 0 ; i < nr ; i++) { + item = btrfs_item_nr(l, i); + btrfs_item_key(l, &disk_key, i); + type = btrfs_disk_key_type(&disk_key); + printf("\titem %d ", i); + btrfs_print_key(&disk_key); + printf(" itemoff %d itemsize %d\n", + btrfs_item_offset(l, item), + btrfs_item_size(l, item)); + switch (type) { + case BTRFS_INODE_ITEM_KEY: + ii = btrfs_item_ptr(l, i, struct btrfs_inode_item); + printf("\t\tinode generation %llu size %llu block group %llu mode %o links %u\n", + (unsigned long long)btrfs_inode_generation(l, ii), + (unsigned long long)btrfs_inode_size(l, ii), + (unsigned long long)btrfs_inode_block_group(l,ii), + btrfs_inode_mode(l, ii), + btrfs_inode_nlink(l, ii)); + break; + case BTRFS_INODE_REF_KEY: + iref = btrfs_item_ptr(l, i, struct btrfs_inode_ref); + print_inode_ref_item(l, item, iref); + break; + case BTRFS_DIR_ITEM_KEY: + case BTRFS_DIR_INDEX_KEY: + case BTRFS_XATTR_ITEM_KEY: + di = btrfs_item_ptr(l, i, struct btrfs_dir_item); + print_dir_item(l, item, di); + break; + case BTRFS_DIR_LOG_INDEX_KEY: + case BTRFS_DIR_LOG_ITEM_KEY: + dlog = btrfs_item_ptr(l, i, struct btrfs_dir_log_item); + printf("\t\tdir log end %Lu\n", + btrfs_dir_log_end(l, dlog)); + break; + case BTRFS_ORPHAN_ITEM_KEY: + printf("\t\torphan item\n"); + break; + case BTRFS_ROOT_ITEM_KEY: + ri = btrfs_item_ptr(l, i, struct btrfs_root_item); + read_extent_buffer(l, &root_item, (unsigned long)ri, sizeof(root_item)); + printf("\t\troot data bytenr %llu level %d dirid %llu refs %u\n", + (unsigned long long)btrfs_root_bytenr(&root_item), + btrfs_root_level(&root_item), + (unsigned long long)btrfs_root_dirid(&root_item), + btrfs_root_refs(&root_item)); + if (btrfs_root_refs(&root_item) == 0) { + struct btrfs_key drop_key; + btrfs_disk_key_to_cpu(&drop_key, + &root_item.drop_progress); + printf("\t\tdrop "); + btrfs_print_key(&root_item.drop_progress); + printf(" level %d\n", root_item.drop_level); + } + break; + case BTRFS_ROOT_REF_KEY: + print_root_ref(l, i, "ref"); + break; + case BTRFS_ROOT_BACKREF_KEY: + print_root_ref(l, i, "backref"); + break; + case BTRFS_EXTENT_ITEM_KEY: + print_extent_item(l, i); + break; + case BTRFS_TREE_BLOCK_REF_KEY: + printf("\t\ttree block backref\n"); + break; + case BTRFS_SHARED_BLOCK_REF_KEY: + printf("\t\tshared block backref\n"); + break; + case BTRFS_EXTENT_DATA_REF_KEY: + dref = btrfs_item_ptr(l, i, struct btrfs_extent_data_ref); + printf("\t\textent data backref root %llu " + "objectid %llu offset %llu count %u\n", + (unsigned long long)btrfs_extent_data_ref_root(l, dref), + (unsigned long long)btrfs_extent_data_ref_objectid(l, dref), + (unsigned long long)btrfs_extent_data_ref_offset(l, dref), + btrfs_extent_data_ref_count(l, dref)); + break; + case BTRFS_SHARED_DATA_REF_KEY: + sref = btrfs_item_ptr(l, i, struct btrfs_shared_data_ref); + printf("\t\tshared data backref count %u\n", + btrfs_shared_data_ref_count(l, sref)); + break; + case BTRFS_EXTENT_REF_V0_KEY: +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + print_extent_ref_v0(l, i); +#else + BUG(); +#endif + break; + case BTRFS_CSUM_ITEM_KEY: + ci = btrfs_item_ptr(l, i, struct btrfs_csum_item); + printf("\t\tcsum item\n"); + break; + case BTRFS_EXTENT_CSUM_KEY: + ci = btrfs_item_ptr(l, i, struct btrfs_csum_item); + printf("\t\textent csum item\n"); + break; + case BTRFS_EXTENT_DATA_KEY: + fi = btrfs_item_ptr(l, i, + struct btrfs_file_extent_item); + print_file_extent_item(l, item, fi); + break; + case BTRFS_BLOCK_GROUP_ITEM_KEY: + bi = btrfs_item_ptr(l, i, + struct btrfs_block_group_item); + read_extent_buffer(l, &bg_item, (unsigned long)bi, + sizeof(bg_item)); + printf("\t\tblock group used %llu chunk_objectid %llu flags %llu\n", + (unsigned long long)btrfs_block_group_used(&bg_item), + (unsigned long long)btrfs_block_group_chunk_objectid(&bg_item), + (unsigned long long)btrfs_block_group_flags(&bg_item)); + break; + case BTRFS_CHUNK_ITEM_KEY: + print_chunk(l, btrfs_item_ptr(l, i, struct btrfs_chunk)); + break; + case BTRFS_DEV_ITEM_KEY: + print_dev_item(l, btrfs_item_ptr(l, i, + struct btrfs_dev_item)); + break; + case BTRFS_DEV_EXTENT_KEY: + dev_extent = btrfs_item_ptr(l, i, + struct btrfs_dev_extent); + printf("\t\tdev extent chunk_tree %llu\n" + "\t\tchunk objectid %llu chunk offset %llu " + "length %llu\n", + (unsigned long long) + btrfs_dev_extent_chunk_tree(l, dev_extent), + (unsigned long long) + btrfs_dev_extent_chunk_objectid(l, dev_extent), + (unsigned long long) + btrfs_dev_extent_chunk_offset(l, dev_extent), + (unsigned long long) + btrfs_dev_extent_length(l, dev_extent)); + break; + case BTRFS_STRING_ITEM_KEY: + /* dirty, but it's simple */ + str = l->data + btrfs_item_ptr_offset(l, i); + printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str); + break; + }; + fflush(stdout); + } +} + +void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb) +{ + int i; + u32 nr; + u32 size; + struct btrfs_disk_key disk_key; + struct btrfs_key key; + + if (!eb) + return; + nr = btrfs_header_nritems(eb); + if (btrfs_is_leaf(eb)) { + btrfs_print_leaf(root, eb); + return; + } + printf("node %llu level %d items %d free %u generation %llu owner %llu\n", + (unsigned long long)eb->start, + btrfs_header_level(eb), nr, + (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr, + (unsigned long long)btrfs_header_generation(eb), + (unsigned long long)btrfs_header_owner(eb)); + print_uuids(eb); + fflush(stdout); + size = btrfs_level_size(root, btrfs_header_level(eb) - 1); + for (i = 0; i < nr; i++) { + u64 blocknr = btrfs_node_blockptr(eb, i); + btrfs_node_key(eb, &disk_key, i); + btrfs_disk_key_to_cpu(&key, &disk_key); + printf("\t"); + btrfs_print_key(&disk_key); + printf(" block %llu (%llu) gen %llu\n", + (unsigned long long)blocknr, + (unsigned long long)blocknr / size, + (unsigned long long)btrfs_node_ptr_generation(eb, i)); + fflush(stdout); + } + for (i = 0; i < nr; i++) { + struct extent_buffer *next = read_tree_block(root, + btrfs_node_blockptr(eb, i), + size, + btrfs_node_ptr_generation(eb, i)); + if (!next) { + fprintf(stderr, "failed to read %llu in tree %llu\n", + (unsigned long long)btrfs_node_blockptr(eb, i), + (unsigned long long)btrfs_header_owner(eb)); + continue; + } + if (btrfs_is_leaf(next) && + btrfs_header_level(eb) != 1) + BUG(); + if (btrfs_header_level(next) != + btrfs_header_level(eb) - 1) + BUG(); + btrfs_print_tree(root, next); + free_extent_buffer(next); + } +} + diff --git a/lib/print-tree.h b/lib/print-tree.h new file mode 100644 index 0000000..4d1a01a --- /dev/null +++ b/lib/print-tree.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __PRINT_TREE_ +#define __PRINT_TREE_ +void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l); +void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *t); +void btrfs_print_key(struct btrfs_disk_key *disk_key); +#endif diff --git a/lib/radix-tree.c b/lib/radix-tree.c new file mode 100644 index 0000000..ed01810 --- /dev/null +++ b/lib/radix-tree.c @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +/* + * Copyright (C) 2001 Momchil Velikov + * Portions Copyright (C) 2001 Christoph Hellwig + * Copyright (C) 2005 SGI, Christoph Lameter + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "kerncompat.h" +#include "radix-tree.h" +#ifdef __KERNEL__ +#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6) +#else +#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */ +#endif + +#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) +#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) + +#define RADIX_TREE_TAG_LONGS \ + ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG) + +struct radix_tree_node { + unsigned int count; + void *slots[RADIX_TREE_MAP_SIZE]; + unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS]; +}; + +struct radix_tree_path { + struct radix_tree_node *node; + int offset; +}; + +#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) +#define RADIX_TREE_MAX_PATH (RADIX_TREE_INDEX_BITS/RADIX_TREE_MAP_SHIFT + 2) + +static unsigned long height_to_maxindex[RADIX_TREE_MAX_PATH] __read_mostly; + +/* + * Per-cpu pool of preloaded nodes + */ +struct radix_tree_preload { + int nr; + struct radix_tree_node *nodes[RADIX_TREE_MAX_PATH]; +}; +struct radix_tree_preload radix_tree_preloads = { 0, }; + +static inline gfp_t root_gfp_mask(struct radix_tree_root *root) +{ + return root->gfp_mask & __GFP_BITS_MASK; +} + +static int internal_nodes = 0; +/* + * This assumes that the caller has performed appropriate preallocation, and + * that the caller has pinned this thread of control to the current CPU. + */ +static struct radix_tree_node * +radix_tree_node_alloc(struct radix_tree_root *root) +{ + struct radix_tree_node *ret; + ret = malloc(sizeof(struct radix_tree_node)); + if (ret) { + memset(ret, 0, sizeof(struct radix_tree_node)); + internal_nodes++; + } + return ret; +} + +static inline void +radix_tree_node_free(struct radix_tree_node *node) +{ + internal_nodes--; + free(node); +} + +/* + * Load up this CPU's radix_tree_node buffer with sufficient objects to + * ensure that the addition of a single element in the tree cannot fail. On + * success, return zero, with preemption disabled. On error, return -ENOMEM + * with preemption not disabled. + */ +int radix_tree_preload(gfp_t gfp_mask) +{ + struct radix_tree_preload *rtp; + struct radix_tree_node *node; + int ret = -ENOMEM; + + preempt_disable(); + rtp = &__get_cpu_var(radix_tree_preloads); + while (rtp->nr < ARRAY_SIZE(rtp->nodes)) { + preempt_enable(); + node = radix_tree_node_alloc(NULL); + if (node == NULL) + goto out; + preempt_disable(); + rtp = &__get_cpu_var(radix_tree_preloads); + if (rtp->nr < ARRAY_SIZE(rtp->nodes)) + rtp->nodes[rtp->nr++] = node; + else + radix_tree_node_free(node); + } + ret = 0; +out: + return ret; +} + +static inline void tag_set(struct radix_tree_node *node, unsigned int tag, + int offset) +{ + __set_bit(offset, node->tags[tag]); +} + +static inline void tag_clear(struct radix_tree_node *node, unsigned int tag, + int offset) +{ + __clear_bit(offset, node->tags[tag]); +} + +static inline int tag_get(struct radix_tree_node *node, unsigned int tag, + int offset) +{ + return test_bit(offset, node->tags[tag]); +} + +static inline void root_tag_set(struct radix_tree_root *root, unsigned int tag) +{ + root->gfp_mask |= (__force gfp_t)(1 << (tag + __GFP_BITS_SHIFT)); +} + + +static inline void root_tag_clear(struct radix_tree_root *root, unsigned int tag) +{ + root->gfp_mask &= (__force gfp_t)~(1 << (tag + __GFP_BITS_SHIFT)); +} + +static inline void root_tag_clear_all(struct radix_tree_root *root) +{ + root->gfp_mask &= __GFP_BITS_MASK; +} + +static inline int root_tag_get(struct radix_tree_root *root, unsigned int tag) +{ + return (__force unsigned)root->gfp_mask & (1 << (tag + __GFP_BITS_SHIFT)); +} + +/* + * Returns 1 if any slot in the node has this tag set. + * Otherwise returns 0. + */ +static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag) +{ + int idx; + for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { + if (node->tags[tag][idx]) + return 1; + } + return 0; +} + +/* + * Return the maximum key which can be store into a + * radix tree with height HEIGHT. + */ +static inline unsigned long radix_tree_maxindex(unsigned int height) +{ + return height_to_maxindex[height]; +} + +/* + * Extend a radix tree so it can store key @index. + */ +static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) +{ + struct radix_tree_node *node; + unsigned int height; + int tag; + + /* Figure out what the height should be. */ + height = root->height + 1; + while (index > radix_tree_maxindex(height)) + height++; + + if (root->rnode == NULL) { + root->height = height; + goto out; + } + + do { + if (!(node = radix_tree_node_alloc(root))) + return -ENOMEM; + + /* Increase the height. */ + node->slots[0] = root->rnode; + + /* Propagate the aggregated tag info into the new root */ + for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { + if (root_tag_get(root, tag)) + tag_set(node, tag, 0); + } + + node->count = 1; + root->rnode = node; + root->height++; + } while (height > root->height); +out: + return 0; +} + +/** + * radix_tree_insert - insert into a radix tree + * @root: radix tree root + * @index: index key + * @item: item to insert + * + * Insert an item into the radix tree at position @index. + */ +int radix_tree_insert(struct radix_tree_root *root, + unsigned long index, void *item) +{ + struct radix_tree_node *node = NULL, *slot; + unsigned int height, shift; + int offset; + int error; + + /* Make sure the tree is high enough. */ + if (index > radix_tree_maxindex(root->height)) { + error = radix_tree_extend(root, index); + if (error) + return error; + } + + slot = root->rnode; + height = root->height; + shift = (height-1) * RADIX_TREE_MAP_SHIFT; + + offset = 0; /* uninitialised var warning */ + while (height > 0) { + if (slot == NULL) { + /* Have to add a child node. */ + if (!(slot = radix_tree_node_alloc(root))) + return -ENOMEM; + if (node) { + node->slots[offset] = slot; + node->count++; + } else + root->rnode = slot; + } + + /* Go a level down */ + offset = (index >> shift) & RADIX_TREE_MAP_MASK; + node = slot; + slot = node->slots[offset]; + shift -= RADIX_TREE_MAP_SHIFT; + height--; + } + + if (slot != NULL) + return -EEXIST; + + if (node) { + node->count++; + node->slots[offset] = item; + BUG_ON(tag_get(node, 0, offset)); + BUG_ON(tag_get(node, 1, offset)); + } else { + root->rnode = item; + BUG_ON(root_tag_get(root, 0)); + BUG_ON(root_tag_get(root, 1)); + } + + return 0; +} + +static inline void **__lookup_slot(struct radix_tree_root *root, + unsigned long index) +{ + unsigned int height, shift; + struct radix_tree_node **slot; + + height = root->height; + + if (index > radix_tree_maxindex(height)) + return NULL; + + if (height == 0 && root->rnode) + return (void *)&root->rnode; + + shift = (height-1) * RADIX_TREE_MAP_SHIFT; + slot = &root->rnode; + + while (height > 0) { + if (*slot == NULL) + return NULL; + + slot = (struct radix_tree_node **) + ((*slot)->slots + + ((index >> shift) & RADIX_TREE_MAP_MASK)); + shift -= RADIX_TREE_MAP_SHIFT; + height--; + } + + return (void **)slot; +} + +/** + * radix_tree_lookup_slot - lookup a slot in a radix tree + * @root: radix tree root + * @index: index key + * + * Lookup the slot corresponding to the position @index in the radix tree + * @root. This is useful for update-if-exists operations. + */ +void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) +{ + return __lookup_slot(root, index); +} + +/** + * radix_tree_lookup - perform lookup operation on a radix tree + * @root: radix tree root + * @index: index key + * + * Lookup the item at the position @index in the radix tree @root. + */ +void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) +{ + void **slot; + + slot = __lookup_slot(root, index); + return slot != NULL ? *slot : NULL; +} + +/** + * radix_tree_tag_set - set a tag on a radix tree node + * @root: radix tree root + * @index: index key + * @tag: tag index + * + * Set the search tag (which must be < RADIX_TREE_MAX_TAGS) + * corresponding to @index in the radix tree. From + * the root all the way down to the leaf node. + * + * Returns the address of the tagged item. Setting a tag on a not-present + * item is a bug. + */ +void *radix_tree_tag_set(struct radix_tree_root *root, + unsigned long index, unsigned int tag) +{ + unsigned int height, shift; + struct radix_tree_node *slot; + + height = root->height; + BUG_ON(index > radix_tree_maxindex(height)); + + slot = root->rnode; + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; + + while (height > 0) { + int offset; + + offset = (index >> shift) & RADIX_TREE_MAP_MASK; + if (!tag_get(slot, tag, offset)) + tag_set(slot, tag, offset); + slot = slot->slots[offset]; + BUG_ON(slot == NULL); + shift -= RADIX_TREE_MAP_SHIFT; + height--; + } + + /* set the root's tag bit */ + if (slot && !root_tag_get(root, tag)) + root_tag_set(root, tag); + + return slot; +} + +/** + * radix_tree_tag_clear - clear a tag on a radix tree node + * @root: radix tree root + * @index: index key + * @tag: tag index + * + * Clear the search tag (which must be < RADIX_TREE_MAX_TAGS) + * corresponding to @index in the radix tree. If + * this causes the leaf node to have no tags set then clear the tag in the + * next-to-leaf node, etc. + * + * Returns the address of the tagged item on success, else NULL. ie: + * has the same return value and semantics as radix_tree_lookup(). + */ +void *radix_tree_tag_clear(struct radix_tree_root *root, + unsigned long index, unsigned int tag) +{ + struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; + struct radix_tree_node *slot = NULL; + unsigned int height, shift; + + height = root->height; + if (index > radix_tree_maxindex(height)) + goto out; + + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; + pathp->node = NULL; + slot = root->rnode; + + while (height > 0) { + int offset; + + if (slot == NULL) + goto out; + + offset = (index >> shift) & RADIX_TREE_MAP_MASK; + pathp[1].offset = offset; + pathp[1].node = slot; + slot = slot->slots[offset]; + pathp++; + shift -= RADIX_TREE_MAP_SHIFT; + height--; + } + + if (slot == NULL) + goto out; + + while (pathp->node) { + if (!tag_get(pathp->node, tag, pathp->offset)) + goto out; + tag_clear(pathp->node, tag, pathp->offset); + if (any_tag_set(pathp->node, tag)) + goto out; + pathp--; + } + + /* clear the root's tag bit */ + if (root_tag_get(root, tag)) + root_tag_clear(root, tag); + +out: + return slot; +} + +#ifndef __KERNEL__ /* Only the test harness uses this at present */ +/** + * radix_tree_tag_get - get a tag on a radix tree node + * @root: radix tree root + * @index: index key + * @tag: tag index (< RADIX_TREE_MAX_TAGS) + * + * Return values: + * + * 0: tag not present or not set + * 1: tag set + */ +int radix_tree_tag_get(struct radix_tree_root *root, + unsigned long index, unsigned int tag) +{ + unsigned int height, shift; + struct radix_tree_node *slot; + int saw_unset_tag = 0; + + height = root->height; + if (index > radix_tree_maxindex(height)) + return 0; + + /* check the root's tag bit */ + if (!root_tag_get(root, tag)) + return 0; + + if (height == 0) + return 1; + + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; + slot = root->rnode; + + for ( ; ; ) { + int offset; + + if (slot == NULL) + return 0; + + offset = (index >> shift) & RADIX_TREE_MAP_MASK; + + /* + * This is just a debug check. Later, we can bale as soon as + * we see an unset tag. + */ + if (!tag_get(slot, tag, offset)) + saw_unset_tag = 1; + if (height == 1) { + int ret = tag_get(slot, tag, offset); + + BUG_ON(ret && saw_unset_tag); + return !!ret; + } + slot = slot->slots[offset]; + shift -= RADIX_TREE_MAP_SHIFT; + height--; + } +} +#endif + +static unsigned int +__lookup(struct radix_tree_root *root, void **results, unsigned long index, + unsigned int max_items, unsigned long *next_index) +{ + unsigned int nr_found = 0; + unsigned int shift, height; + struct radix_tree_node *slot; + unsigned long i; + + height = root->height; + if (height == 0) { + if (root->rnode && index == 0) + results[nr_found++] = root->rnode; + goto out; + } + + shift = (height-1) * RADIX_TREE_MAP_SHIFT; + slot = root->rnode; + + for ( ; height > 1; height--) { + + for (i = (index >> shift) & RADIX_TREE_MAP_MASK ; + i < RADIX_TREE_MAP_SIZE; i++) { + if (slot->slots[i] != NULL) + break; + index &= ~((1UL << shift) - 1); + index += 1UL << shift; + if (index == 0) + goto out; /* 32-bit wraparound */ + } + if (i == RADIX_TREE_MAP_SIZE) + goto out; + + shift -= RADIX_TREE_MAP_SHIFT; + slot = slot->slots[i]; + } + + /* Bottom level: grab some items */ + for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) { + index++; + if (slot->slots[i]) { + results[nr_found++] = slot->slots[i]; + if (nr_found == max_items) + goto out; + } + } +out: + *next_index = index; + return nr_found; +} + +/** + * radix_tree_gang_lookup - perform multiple lookup on a radix tree + * @root: radix tree root + * @results: where the results of the lookup are placed + * @first_index: start the lookup from this key + * @max_items: place up to this many items at *results + * + * Performs an index-ascending scan of the tree for present items. Places + * them at *@results and returns the number of items which were placed at + * *@results. + * + * The implementation is naive. + */ +unsigned int +radix_tree_gang_lookup(struct radix_tree_root *root, void **results, + unsigned long first_index, unsigned int max_items) +{ + const unsigned long max_index = radix_tree_maxindex(root->height); + unsigned long cur_index = first_index; + unsigned int ret = 0; + + while (ret < max_items) { + unsigned int nr_found; + unsigned long next_index; /* Index of next search */ + + if (cur_index > max_index) + break; + nr_found = __lookup(root, results + ret, cur_index, + max_items - ret, &next_index); + ret += nr_found; + if (next_index == 0) + break; + cur_index = next_index; + } + return ret; +} + +/* + * FIXME: the two tag_get()s here should use find_next_bit() instead of + * open-coding the search. + */ +static unsigned int +__lookup_tag(struct radix_tree_root *root, void **results, unsigned long index, + unsigned int max_items, unsigned long *next_index, unsigned int tag) +{ + unsigned int nr_found = 0; + unsigned int shift; + unsigned int height = root->height; + struct radix_tree_node *slot; + + if (height == 0) { + if (root->rnode && index == 0) + results[nr_found++] = root->rnode; + goto out; + } + + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; + slot = root->rnode; + + do { + unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK; + + for ( ; i < RADIX_TREE_MAP_SIZE; i++) { + if (tag_get(slot, tag, i)) { + BUG_ON(slot->slots[i] == NULL); + break; + } + index &= ~((1UL << shift) - 1); + index += 1UL << shift; + if (index == 0) + goto out; /* 32-bit wraparound */ + } + if (i == RADIX_TREE_MAP_SIZE) + goto out; + height--; + if (height == 0) { /* Bottom level: grab some items */ + unsigned long j = index & RADIX_TREE_MAP_MASK; + + for ( ; j < RADIX_TREE_MAP_SIZE; j++) { + index++; + if (tag_get(slot, tag, j)) { + BUG_ON(slot->slots[j] == NULL); + results[nr_found++] = slot->slots[j]; + if (nr_found == max_items) + goto out; + } + } + } + shift -= RADIX_TREE_MAP_SHIFT; + slot = slot->slots[i]; + } while (height > 0); +out: + *next_index = index; + return nr_found; +} + +/** + * radix_tree_gang_lookup_tag - perform multiple lookup on a radix tree + * based on a tag + * @root: radix tree root + * @results: where the results of the lookup are placed + * @first_index: start the lookup from this key + * @max_items: place up to this many items at *results + * @tag: the tag index (< RADIX_TREE_MAX_TAGS) + * + * Performs an index-ascending scan of the tree for present items which + * have the tag indexed by @tag set. Places the items at *@results and + * returns the number of items which were placed at *@results. + */ +unsigned int +radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, + unsigned long first_index, unsigned int max_items, + unsigned int tag) +{ + const unsigned long max_index = radix_tree_maxindex(root->height); + unsigned long cur_index = first_index; + unsigned int ret = 0; + + /* check the root's tag bit */ + if (!root_tag_get(root, tag)) + return 0; + + while (ret < max_items) { + unsigned int nr_found; + unsigned long next_index; /* Index of next search */ + + if (cur_index > max_index) + break; + nr_found = __lookup_tag(root, results + ret, cur_index, + max_items - ret, &next_index, tag); + ret += nr_found; + if (next_index == 0) + break; + cur_index = next_index; + } + return ret; +} + +/** + * radix_tree_shrink - shrink height of a radix tree to minimal + * @root radix tree root + */ +static inline void radix_tree_shrink(struct radix_tree_root *root) +{ + /* try to shrink tree height */ + while (root->height > 0 && + root->rnode->count == 1 && + root->rnode->slots[0]) { + struct radix_tree_node *to_free = root->rnode; + + root->rnode = to_free->slots[0]; + root->height--; + /* must only free zeroed nodes into the slab */ + tag_clear(to_free, 0, 0); + tag_clear(to_free, 1, 0); + to_free->slots[0] = NULL; + to_free->count = 0; + radix_tree_node_free(to_free); + } +} + +/** + * radix_tree_delete - delete an item from a radix tree + * @root: radix tree root + * @index: index key + * + * Remove the item at @index from the radix tree rooted at @root. + * + * Returns the address of the deleted item, or NULL if it was not present. + */ +void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) +{ + struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; + struct radix_tree_node *slot = NULL; + unsigned int height, shift; + int tag; + int offset; + + height = root->height; + if (index > radix_tree_maxindex(height)) + goto out; + + slot = root->rnode; + if (height == 0 && root->rnode) { + root_tag_clear_all(root); + root->rnode = NULL; + goto out; + } + + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; + pathp->node = NULL; + + do { + if (slot == NULL) + goto out; + + pathp++; + offset = (index >> shift) & RADIX_TREE_MAP_MASK; + pathp->offset = offset; + pathp->node = slot; + slot = slot->slots[offset]; + shift -= RADIX_TREE_MAP_SHIFT; + height--; + } while (height > 0); + + if (slot == NULL) + goto out; + + /* + * Clear all tags associated with the just-deleted item + */ + for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { + if (tag_get(pathp->node, tag, pathp->offset)) + radix_tree_tag_clear(root, index, tag); + } + + /* Now free the nodes we do not need anymore */ + while (pathp->node) { + pathp->node->slots[pathp->offset] = NULL; + pathp->node->count--; + + if (pathp->node->count) { + if (pathp->node == root->rnode) + radix_tree_shrink(root); + goto out; + } + + /* Node with zero slots in use so free it */ + radix_tree_node_free(pathp->node); + + pathp--; + } + root_tag_clear_all(root); + root->height = 0; + root->rnode = NULL; + +out: + return slot; +} + +/** + * radix_tree_tagged - test whether any items in the tree are tagged + * @root: radix tree root + * @tag: tag to test + */ +int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) +{ + return root_tag_get(root, tag); +} + +static unsigned long __maxindex(unsigned int height) +{ + unsigned int tmp = height * RADIX_TREE_MAP_SHIFT; + unsigned long index = (~0UL >> (RADIX_TREE_INDEX_BITS - tmp - 1)) >> 1; + + if (tmp >= RADIX_TREE_INDEX_BITS) + index = ~0UL; + return index; +} + +static void radix_tree_init_maxindex(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(height_to_maxindex); i++) + height_to_maxindex[i] = __maxindex(i); +} + +void radix_tree_init(void) +{ + radix_tree_init_maxindex(); +} diff --git a/lib/radix-tree.h b/lib/radix-tree.h new file mode 100644 index 0000000..d99ea7e --- /dev/null +++ b/lib/radix-tree.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +/* + * Copyright (C) 2001 Momchil Velikov + * Portions Copyright (C) 2001 Christoph Hellwig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _LINUX_RADIX_TREE_H +#define _LINUX_RADIX_TREE_H + +#include "kerncompat.h" + +#define RADIX_TREE_MAX_TAGS 2 + +/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */ +struct radix_tree_root { + unsigned int height; + gfp_t gfp_mask; + struct radix_tree_node *rnode; +}; + +#define RADIX_TREE_INIT(mask) { \ + .height = 0, \ + .gfp_mask = (mask), \ + .rnode = NULL, \ +} + +#define RADIX_TREE(name, mask) \ + struct radix_tree_root name = RADIX_TREE_INIT(mask) + +#define INIT_RADIX_TREE(root, mask) \ +do { \ + (root)->height = 0; \ + (root)->gfp_mask = (mask); \ + (root)->rnode = NULL; \ +} while (0) + +int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); +void *radix_tree_lookup(struct radix_tree_root *, unsigned long); +void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); +void *radix_tree_delete(struct radix_tree_root *, unsigned long); +unsigned int +radix_tree_gang_lookup(struct radix_tree_root *root, void **results, + unsigned long first_index, unsigned int max_items); +int radix_tree_preload(gfp_t gfp_mask); +void radix_tree_init(void); +void *radix_tree_tag_set(struct radix_tree_root *root, + unsigned long index, unsigned int tag); +void *radix_tree_tag_clear(struct radix_tree_root *root, + unsigned long index, unsigned int tag); +int radix_tree_tag_get(struct radix_tree_root *root, + unsigned long index, unsigned int tag); +unsigned int +radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, + unsigned long first_index, unsigned int max_items, + unsigned int tag); +int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); + +static inline void radix_tree_preload_end(void) +{ + preempt_enable(); +} + +#endif /* _LINUX_RADIX_TREE_H */ diff --git a/lib/rbtree.c b/lib/rbtree.c new file mode 100644 index 0000000..6ad800f --- /dev/null +++ b/lib/rbtree.c @@ -0,0 +1,389 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + (C) 2002 David Woodhouse + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 02111-1307 USA + + linux/lib/rbtree.c +*/ + +#include "rbtree.h" + +static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *right = node->rb_right; + struct rb_node *parent = rb_parent(node); + + if ((node->rb_right = right->rb_left)) + rb_set_parent(right->rb_left, node); + right->rb_left = node; + + rb_set_parent(right, parent); + + if (parent) + { + if (node == parent->rb_left) + parent->rb_left = right; + else + parent->rb_right = right; + } + else + root->rb_node = right; + rb_set_parent(node, right); +} + +static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *left = node->rb_left; + struct rb_node *parent = rb_parent(node); + + if ((node->rb_left = left->rb_right)) + rb_set_parent(left->rb_right, node); + left->rb_right = node; + + rb_set_parent(left, parent); + + if (parent) + { + if (node == parent->rb_right) + parent->rb_right = left; + else + parent->rb_left = left; + } + else + root->rb_node = left; + rb_set_parent(node, left); +} + +void rb_insert_color(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *parent, *gparent; + + while ((parent = rb_parent(node)) && rb_is_red(parent)) + { + gparent = rb_parent(parent); + + if (parent == gparent->rb_left) + { + { + register struct rb_node *uncle = gparent->rb_right; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->rb_right == node) + { + register struct rb_node *tmp; + __rb_rotate_left(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_right(gparent, root); + } else { + { + register struct rb_node *uncle = gparent->rb_left; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->rb_left == node) + { + register struct rb_node *tmp; + __rb_rotate_right(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_left(gparent, root); + } + } + + rb_set_black(root->rb_node); +} + +static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, + struct rb_root *root) +{ + struct rb_node *other; + + while ((!node || rb_is_black(node)) && node != root->rb_node) + { + if (parent->rb_left == node) + { + other = parent->rb_right; + if (rb_is_red(other)) + { + rb_set_black(other); + rb_set_red(parent); + __rb_rotate_left(parent, root); + other = parent->rb_right; + } + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) + { + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->rb_right || rb_is_black(other->rb_right)) + { + struct rb_node *o_left; + if ((o_left = other->rb_left)) + rb_set_black(o_left); + rb_set_red(other); + __rb_rotate_right(other, root); + other = parent->rb_right; + } + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + if (other->rb_right) + rb_set_black(other->rb_right); + __rb_rotate_left(parent, root); + node = root->rb_node; + break; + } + } + else + { + other = parent->rb_left; + if (rb_is_red(other)) + { + rb_set_black(other); + rb_set_red(parent); + __rb_rotate_right(parent, root); + other = parent->rb_left; + } + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) + { + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->rb_left || rb_is_black(other->rb_left)) + { + register struct rb_node *o_right; + if ((o_right = other->rb_right)) + rb_set_black(o_right); + rb_set_red(other); + __rb_rotate_left(other, root); + other = parent->rb_left; + } + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + if (other->rb_left) + rb_set_black(other->rb_left); + __rb_rotate_right(parent, root); + node = root->rb_node; + break; + } + } + } + if (node) + rb_set_black(node); +} + +void rb_erase(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *child, *parent; + int color; + + if (!node->rb_left) + child = node->rb_right; + else if (!node->rb_right) + child = node->rb_left; + else + { + struct rb_node *old = node, *left; + + node = node->rb_right; + while ((left = node->rb_left) != NULL) + node = left; + child = node->rb_right; + parent = rb_parent(node); + color = rb_color(node); + + if (child) + rb_set_parent(child, parent); + if (parent == old) { + parent->rb_right = child; + parent = node; + } else + parent->rb_left = child; + + node->rb_parent_color = old->rb_parent_color; + node->rb_right = old->rb_right; + node->rb_left = old->rb_left; + + if (rb_parent(old)) + { + if (rb_parent(old)->rb_left == old) + rb_parent(old)->rb_left = node; + else + rb_parent(old)->rb_right = node; + } else + root->rb_node = node; + + rb_set_parent(old->rb_left, node); + if (old->rb_right) + rb_set_parent(old->rb_right, node); + goto color; + } + + parent = rb_parent(node); + color = rb_color(node); + + if (child) + rb_set_parent(child, parent); + if (parent) + { + if (parent->rb_left == node) + parent->rb_left = child; + else + parent->rb_right = child; + } + else + root->rb_node = child; + + color: + if (color == RB_BLACK) + __rb_erase_color(child, parent, root); +} + +/* + * This function returns the first node (in sort order) of the tree. + */ +struct rb_node *rb_first(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_left) + n = n->rb_left; + return n; +} + +struct rb_node *rb_last(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_right) + n = n->rb_right; + return n; +} + +struct rb_node *rb_next(struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* If we have a right-hand child, go down and then left as far + as we can. */ + if (node->rb_right) { + node = node->rb_right; + while (node->rb_left) + node=node->rb_left; + return node; + } + + /* No right-hand children. Everything down and left is + smaller than us, so any 'next' node must be in the general + direction of our parent. Go up the tree; any time the + ancestor is a right-hand child of its parent, keep going + up. First time it's a left-hand child of its parent, said + parent is our 'next' node. */ + while ((parent = rb_parent(node)) && node == parent->rb_right) + node = parent; + + return parent; +} + +struct rb_node *rb_prev(struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* If we have a left-hand child, go down and then right as far + as we can. */ + if (node->rb_left) { + node = node->rb_left; + while (node->rb_right) + node=node->rb_right; + return node; + } + + /* No left-hand children. Go up till we find an ancestor which + is a right-hand child of its parent */ + while ((parent = rb_parent(node)) && node == parent->rb_left) + node = parent; + + return parent; +} + +void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root) +{ + struct rb_node *parent = rb_parent(victim); + + /* Set the surrounding nodes to point to the replacement */ + if (parent) { + if (victim == parent->rb_left) + parent->rb_left = new; + else + parent->rb_right = new; + } else { + root->rb_node = new; + } + if (victim->rb_left) + rb_set_parent(victim->rb_left, new); + if (victim->rb_right) + rb_set_parent(victim->rb_right, new); + + /* Copy the pointers/colour from the victim to the replacement */ + *new = *victim; +} diff --git a/lib/rbtree.h b/lib/rbtree.h new file mode 100644 index 0000000..bed054d --- /dev/null +++ b/lib/rbtree.h @@ -0,0 +1,160 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 02111-1307 USA + + linux/include/linux/rbtree.h + + To use rbtrees you'll have to implement your own insert and search cores. + This will avoid us to use callbacks and to drop drammatically performances. + I know it's not the cleaner way, but in C (not in C++) to get + performances and genericity... + + Some example of insert and search follows here. The search is a plain + normal search over an ordered tree. The insert instead must be implemented + int two steps: as first thing the code must insert the element in + order as a red leaf in the tree, then the support library function + rb_insert_color() must be called. Such function will do the + not trivial work to rebalance the rbtree if necessary. + +----------------------------------------------------------------------- +static inline struct page * rb_search_page_cache(struct inode * inode, + unsigned long offset) +{ + struct rb_node * n = inode->i_rb_page_cache.rb_node; + struct page * page; + + while (n) + { + page = rb_entry(n, struct page, rb_page_cache); + + if (offset < page->offset) + n = n->rb_left; + else if (offset > page->offset) + n = n->rb_right; + else + return page; + } + return NULL; +} + +static inline struct page * __rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct rb_node ** p = &inode->i_rb_page_cache.rb_node; + struct rb_node * parent = NULL; + struct page * page; + + while (*p) + { + parent = *p; + page = rb_entry(parent, struct page, rb_page_cache); + + if (offset < page->offset) + p = &(*p)->rb_left; + else if (offset > page->offset) + p = &(*p)->rb_right; + else + return page; + } + + rb_link_node(node, parent, p); + + return NULL; +} + +static inline struct page * rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct page * ret; + if ((ret = __rb_insert_page_cache(inode, offset, node))) + goto out; + rb_insert_color(node, &inode->i_rb_page_cache); + out: + return ret; +} +----------------------------------------------------------------------- +*/ + +#ifndef _LINUX_RBTREE_H +#define _LINUX_RBTREE_H +#include "kerncompat.h" +struct rb_node +{ + unsigned long rb_parent_color; +#define RB_RED 0 +#define RB_BLACK 1 + struct rb_node *rb_right; + struct rb_node *rb_left; +} __attribute__((aligned(sizeof(long)))); + /* The alignment might seem pointless, but allegedly CRIS needs it */ + +struct rb_root +{ + struct rb_node *rb_node; + void (*rotate_notify)(struct rb_node *old_parent, struct rb_node *node); + +}; + + +#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) +#define rb_color(r) ((r)->rb_parent_color & 1) +#define rb_is_red(r) (!rb_color(r)) +#define rb_is_black(r) rb_color(r) +#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) +#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) + +static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) +{ + rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; +} +static inline void rb_set_color(struct rb_node *rb, int color) +{ + rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; +} + +#define RB_ROOT (struct rb_root) { NULL, } +#define rb_entry(ptr, type, member) container_of(ptr, type, member) + +#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) +#define RB_EMPTY_NODE(node) (rb_parent(node) == node) +#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) + +extern void rb_insert_color(struct rb_node *, struct rb_root *); +extern void rb_erase(struct rb_node *, struct rb_root *); + +/* Find logical next and previous nodes in a tree */ +extern struct rb_node *rb_next(struct rb_node *); +extern struct rb_node *rb_prev(struct rb_node *); +extern struct rb_node *rb_first(struct rb_root *); +extern struct rb_node *rb_last(struct rb_root *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root); + +static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, + struct rb_node ** rb_link) +{ + node->rb_parent_color = (unsigned long )parent; + node->rb_left = node->rb_right = NULL; + + *rb_link = node; +} + +#endif /* _LINUX_RBTREE_H */ diff --git a/lib/root-tree.c b/lib/root-tree.c new file mode 100644 index 0000000..782472c --- /dev/null +++ b/lib/root-tree.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2007 Oracle. 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 "ctree.h" +#include "transaction.h" +#include "disk-io.h" +#include "print-tree.h" + +int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, + struct btrfs_root_item *item, struct btrfs_key *key) +{ + struct btrfs_path *path; + struct btrfs_key search_key; + struct btrfs_key found_key; + struct extent_buffer *l; + int ret; + int slot; + + search_key.objectid = objectid; + search_key.type = BTRFS_ROOT_ITEM_KEY; + search_key.offset = (u64)-1; + + path = btrfs_alloc_path(); + BUG_ON(!path); + ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); + if (ret < 0) + goto out; + + BUG_ON(ret == 0); + l = path->nodes[0]; + BUG_ON(path->slots[0] == 0); + slot = path->slots[0] - 1; + btrfs_item_key_to_cpu(l, &found_key, slot); + if (found_key.objectid != objectid) { + ret = 1; + goto out; + } + read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot), + sizeof(*item)); + memcpy(key, &found_key, sizeof(found_key)); + ret = 0; +out: + btrfs_release_path(root, path); + btrfs_free_path(path); + return ret; +} + +int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *key, struct btrfs_root_item + *item) +{ + struct btrfs_path *path; + struct extent_buffer *l; + int ret; + int slot; + unsigned long ptr; + + path = btrfs_alloc_path(); + BUG_ON(!path); + ret = btrfs_search_slot(trans, root, key, path, 0, 1); + if (ret < 0) + goto out; + BUG_ON(ret != 0); + l = path->nodes[0]; + slot = path->slots[0]; + ptr = btrfs_item_ptr_offset(l, slot); + write_extent_buffer(l, item, ptr, sizeof(*item)); + btrfs_mark_buffer_dirty(path->nodes[0]); +out: + btrfs_release_path(root, path); + btrfs_free_path(path); + return ret; +} + +int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_key *key, struct btrfs_root_item + *item) +{ + int ret; + ret = btrfs_insert_item(trans, root, key, item, sizeof(*item)); + return ret; +} + +#if 0 +int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid, + struct btrfs_root *latest) +{ + struct btrfs_root *dead_root; + struct btrfs_item *item; + struct btrfs_root_item *ri; + struct btrfs_key key; + struct btrfs_path *path; + int ret; + u32 nritems; + struct extent_buffer *leaf; + int slot; + + key.objectid = objectid; + btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); + key.offset = 0; + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + goto err; + while(1) { + leaf = path->nodes[0]; + nritems = btrfs_header_nritems(leaf); + slot = path->slots[0]; + if (slot >= nritems) { + ret = btrfs_next_leaf(root, path); + if (ret) + break; + leaf = path->nodes[0]; + nritems = btrfs_header_nritems(leaf); + slot = path->slots[0]; + } + item = btrfs_item_nr(leaf, slot); + btrfs_item_key_to_cpu(leaf, &key, slot); + if (btrfs_key_type(&key) != BTRFS_ROOT_ITEM_KEY) + goto next; + + if (key.objectid < objectid) + goto next; + + if (key.objectid > objectid) + break; + + ri = btrfs_item_ptr(leaf, slot, struct btrfs_root_item); + if (btrfs_disk_root_refs(leaf, ri) != 0) + goto next; + + dead_root = btrfs_read_fs_root_no_radix(root->fs_info, &key); + if (IS_ERR(dead_root)) { + ret = PTR_ERR(dead_root); + goto err; + } + + ret = btrfs_add_dead_root(dead_root, latest, + &root->fs_info->dead_roots); + if (ret) + goto err; +next: + slot++; + path->slots[0]++; + } + ret = 0; +err: + btrfs_free_path(path); + return ret; +} +#endif + +int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct btrfs_key *key) +{ + struct btrfs_path *path; + int ret; + u32 refs; + struct btrfs_root_item *ri; + struct extent_buffer *leaf; + + path = btrfs_alloc_path(); + BUG_ON(!path); + ret = btrfs_search_slot(trans, root, key, path, -1, 1); + if (ret < 0) + goto out; + if (ret) { +btrfs_print_leaf(root, path->nodes[0]); +printk("failed to del %llu %u %llu\n", + (unsigned long long)key->objectid, + key->type, + (unsigned long long)key->offset); + + } + BUG_ON(ret != 0); + leaf = path->nodes[0]; + ri = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_item); + + refs = btrfs_disk_root_refs(leaf, ri); + BUG_ON(refs != 0); + ret = btrfs_del_item(trans, root, path); +out: + btrfs_release_path(root, path); + btrfs_free_path(path); + return ret; +} + +/* + * add a btrfs_root_ref item. type is either BTRFS_ROOT_REF_KEY + * or BTRFS_ROOT_BACKREF_KEY. + * + * The dirid, sequence, name and name_len refer to the directory entry + * that is referencing the root. + * + * For a forward ref, the root_id is the id of the tree referencing + * the root and ref_id is the id of the subvol or snapshot. + * + * For a back ref the root_id is the id of the subvol or snapshot and + * ref_id is the id of the tree referencing it. + */ +int btrfs_add_root_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *tree_root, + u64 root_id, u8 type, u64 ref_id, + u64 dirid, u64 sequence, + const char *name, int name_len) +{ + struct btrfs_key key; + int ret; + struct btrfs_path *path; + struct btrfs_root_ref *ref; + struct extent_buffer *leaf; + unsigned long ptr; + + + path = btrfs_alloc_path(); + + key.objectid = root_id; + key.type = type; + key.offset = ref_id; + + ret = btrfs_insert_empty_item(trans, tree_root, path, &key, + sizeof(*ref) + name_len); + BUG_ON(ret); + + leaf = path->nodes[0]; + ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref); + btrfs_set_root_ref_dirid(leaf, ref, dirid); + btrfs_set_root_ref_sequence(leaf, ref, sequence); + btrfs_set_root_ref_name_len(leaf, ref, name_len); + ptr = (unsigned long)(ref + 1); + write_extent_buffer(leaf, name, ptr, name_len); + btrfs_mark_buffer_dirty(leaf); + + btrfs_free_path(path); + return ret; +} diff --git a/lib/transaction.h b/lib/transaction.h new file mode 100644 index 0000000..a1070e0 --- /dev/null +++ b/lib/transaction.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __TRANSACTION__ +#define __TRANSACTION__ + +struct btrfs_trans_handle { + u64 transid; + u64 alloc_exclude_start; + u64 alloc_exclude_nr; + unsigned long blocks_reserved; + unsigned long blocks_used; + struct btrfs_block_group_cache *block_group; +}; + +static inline struct btrfs_trans_handle * +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(root->commit_root); + BUG_ON(fs_info->running_transaction); + fs_info->running_transaction = h; + fs_info->generation++; + h->transid = fs_info->generation; + h->alloc_exclude_start = 0; + h->alloc_exclude_nr = 0; + h->blocks_reserved = num_blocks; + h->blocks_used = 0; + h->block_group = NULL; + root->last_trans = h->transid; + root->commit_root = root->node; + extent_buffer_get(root->node); + return h; +} + +static inline void btrfs_free_transaction(struct btrfs_root *root, + struct btrfs_trans_handle *handle) +{ + memset(handle, 0, sizeof(*handle)); + free(handle); +} + +#endif diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..2f4c6e1 --- /dev/null +++ b/lib/utils.c @@ -0,0 +1,822 @@ +/* + * Copyright (C) 2007 Oracle. 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 600 +#define __USE_XOPEN2K +#include +#include +#ifndef __CHECKER__ +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "transaction.h" +#include "crc32c.h" +#include "utils.h" +#include "volumes.h" +#include "ioctl.h" + +#ifdef __CHECKER__ +#define BLKGETSIZE64 0 +static inline int ioctl(int fd, int define, u64 *size) { return 0; } +#endif + +static u64 reference_root_table[] = { + [1] = BTRFS_ROOT_TREE_OBJECTID, + [2] = BTRFS_EXTENT_TREE_OBJECTID, + [3] = BTRFS_CHUNK_TREE_OBJECTID, + [4] = BTRFS_DEV_TREE_OBJECTID, + [5] = BTRFS_FS_TREE_OBJECTID, + [6] = BTRFS_CSUM_TREE_OBJECTID, +}; + +int make_btrfs(int fd, const char *device, const char *label, + u64 blocks[7], u64 num_bytes, u32 nodesize, + u32 leafsize, u32 sectorsize, u32 stripesize) +{ + struct btrfs_super_block super; + struct extent_buffer *buf; + struct btrfs_root_item root_item; + struct btrfs_disk_key disk_key; + struct btrfs_extent_item *extent_item; + struct btrfs_inode_item *inode_item; + struct btrfs_chunk *chunk; + struct btrfs_dev_item *dev_item; + struct btrfs_dev_extent *dev_extent; + u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; + u8 *ptr; + int i; + int ret; + u32 itemoff; + u32 nritems = 0; + u64 first_free; + u64 ref_root; + u32 array_size; + u32 item_size; + + first_free = BTRFS_SUPER_INFO_OFFSET + sectorsize * 2 - 1; + first_free &= ~((u64)sectorsize - 1); + + memset(&super, 0, sizeof(super)); + + num_bytes = (num_bytes / sectorsize) * sectorsize; + uuid_generate(super.fsid); + uuid_generate(super.dev_item.uuid); + uuid_generate(chunk_tree_uuid); + + btrfs_set_super_bytenr(&super, blocks[0]); + btrfs_set_super_num_devices(&super, 1); + strncpy((char *)&super.magic, BTRFS_MAGIC, sizeof(super.magic)); + btrfs_set_super_generation(&super, 1); + btrfs_set_super_root(&super, blocks[1]); + btrfs_set_super_chunk_root(&super, blocks[3]); + btrfs_set_super_total_bytes(&super, num_bytes); + btrfs_set_super_bytes_used(&super, 6 * leafsize); + btrfs_set_super_sectorsize(&super, sectorsize); + btrfs_set_super_leafsize(&super, leafsize); + btrfs_set_super_nodesize(&super, nodesize); + btrfs_set_super_stripesize(&super, stripesize); + btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32); + btrfs_set_super_chunk_root_generation(&super, 1); + if (label) + strcpy(super.label, label); + + buf = malloc(sizeof(*buf) + max(sectorsize, leafsize)); + + /* create the tree of root objects */ + memset(buf->data, 0, leafsize); + buf->len = leafsize; + btrfs_set_header_bytenr(buf, blocks[1]); + btrfs_set_header_nritems(buf, 4); + btrfs_set_header_generation(buf, 1); + btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV); + btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID); + write_extent_buffer(buf, super.fsid, (unsigned long) + btrfs_header_fsid(buf), BTRFS_FSID_SIZE); + + write_extent_buffer(buf, chunk_tree_uuid, (unsigned long) + btrfs_header_chunk_tree_uuid(buf), + BTRFS_UUID_SIZE); + + /* create the items for the root tree */ + memset(&root_item, 0, sizeof(root_item)); + inode_item = &root_item.inode; + btrfs_set_stack_inode_generation(inode_item, 1); + btrfs_set_stack_inode_size(inode_item, 3); + btrfs_set_stack_inode_nlink(inode_item, 1); + btrfs_set_stack_inode_nbytes(inode_item, leafsize); + btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755); + btrfs_set_root_refs(&root_item, 1); + btrfs_set_root_used(&root_item, leafsize); + btrfs_set_root_generation(&root_item, 1); + + memset(&disk_key, 0, sizeof(disk_key)); + btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY); + btrfs_set_disk_key_offset(&disk_key, 0); + nritems = 0; + + itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - sizeof(root_item); + btrfs_set_root_bytenr(&root_item, blocks[2]); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), + sizeof(root_item)); + write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf, + nritems), sizeof(root_item)); + nritems++; + + itemoff = itemoff - sizeof(root_item); + btrfs_set_root_bytenr(&root_item, blocks[4]); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), + sizeof(root_item)); + write_extent_buffer(buf, &root_item, + btrfs_item_ptr_offset(buf, nritems), + sizeof(root_item)); + nritems++; + + itemoff = itemoff - sizeof(root_item); + btrfs_set_root_bytenr(&root_item, blocks[5]); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), + sizeof(root_item)); + write_extent_buffer(buf, &root_item, + btrfs_item_ptr_offset(buf, nritems), + sizeof(root_item)); + nritems++; + + itemoff = itemoff - sizeof(root_item); + btrfs_set_root_bytenr(&root_item, blocks[6]); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), + sizeof(root_item)); + write_extent_buffer(buf, &root_item, + btrfs_item_ptr_offset(buf, nritems), + sizeof(root_item)); + nritems++; + + + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, leafsize, blocks[1]); + BUG_ON(ret != leafsize); + + /* create the items for the extent tree */ + nritems = 0; + itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize); + for (i = 1; i < 7; i++) { + BUG_ON(blocks[i] < first_free); + BUG_ON(blocks[i] < blocks[i - 1]); + + /* create extent item */ + itemoff -= sizeof(struct btrfs_extent_item) + + sizeof(struct btrfs_tree_block_info); + btrfs_set_disk_key_objectid(&disk_key, blocks[i]); + btrfs_set_disk_key_offset(&disk_key, leafsize); + btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), + itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), + sizeof(struct btrfs_extent_item) + + sizeof(struct btrfs_tree_block_info)); + extent_item = btrfs_item_ptr(buf, nritems, + struct btrfs_extent_item); + btrfs_set_extent_refs(buf, extent_item, 1); + btrfs_set_extent_generation(buf, extent_item, 1); + btrfs_set_extent_flags(buf, extent_item, + BTRFS_EXTENT_FLAG_TREE_BLOCK); + nritems++; + + /* create extent ref */ + ref_root = reference_root_table[i]; + btrfs_set_disk_key_objectid(&disk_key, blocks[i]); + btrfs_set_disk_key_offset(&disk_key, ref_root); + btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), + itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), 0); + nritems++; + } + btrfs_set_header_bytenr(buf, blocks[2]); + btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_header_nritems(buf, nritems); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, leafsize, blocks[2]); + BUG_ON(ret != leafsize); + + /* create the chunk tree */ + nritems = 0; + item_size = sizeof(*dev_item); + itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - item_size; + + /* first device 1 (there is no device 0) */ + btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID); + btrfs_set_disk_key_offset(&disk_key, 1); + btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), item_size); + + dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item); + btrfs_set_device_id(buf, dev_item, 1); + btrfs_set_device_generation(buf, dev_item, 0); + btrfs_set_device_total_bytes(buf, dev_item, num_bytes); + btrfs_set_device_bytes_used(buf, dev_item, + BTRFS_MKFS_SYSTEM_GROUP_SIZE); + btrfs_set_device_io_align(buf, dev_item, sectorsize); + btrfs_set_device_io_width(buf, dev_item, sectorsize); + btrfs_set_device_sector_size(buf, dev_item, sectorsize); + btrfs_set_device_type(buf, dev_item, 0); + + write_extent_buffer(buf, super.dev_item.uuid, + (unsigned long)btrfs_device_uuid(dev_item), + BTRFS_UUID_SIZE); + write_extent_buffer(buf, super.fsid, + (unsigned long)btrfs_device_fsid(dev_item), + BTRFS_UUID_SIZE); + read_extent_buffer(buf, &super.dev_item, (unsigned long)dev_item, + sizeof(*dev_item)); + + nritems++; + item_size = btrfs_chunk_item_size(1); + itemoff = itemoff - item_size; + + /* then we have chunk 0 */ + btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_disk_key_offset(&disk_key, 0); + btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), item_size); + + chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk); + btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE); + btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_chunk_stripe_len(buf, chunk, 64 * 1024); + btrfs_set_chunk_type(buf, chunk, BTRFS_BLOCK_GROUP_SYSTEM); + btrfs_set_chunk_io_align(buf, chunk, sectorsize); + btrfs_set_chunk_io_width(buf, chunk, sectorsize); + btrfs_set_chunk_sector_size(buf, chunk, sectorsize); + btrfs_set_chunk_num_stripes(buf, chunk, 1); + btrfs_set_stripe_devid_nr(buf, chunk, 0, 1); + btrfs_set_stripe_offset_nr(buf, chunk, 0, 0); + nritems++; + + write_extent_buffer(buf, super.dev_item.uuid, + (unsigned long)btrfs_stripe_dev_uuid(&chunk->stripe), + BTRFS_UUID_SIZE); + + /* copy the key for the chunk to the system array */ + ptr = super.sys_chunk_array; + array_size = sizeof(disk_key); + + memcpy(ptr, &disk_key, sizeof(disk_key)); + ptr += sizeof(disk_key); + + /* copy the chunk to the system array */ + read_extent_buffer(buf, ptr, (unsigned long)chunk, item_size); + array_size += item_size; + ptr += item_size; + btrfs_set_super_sys_array_size(&super, array_size); + + btrfs_set_header_bytenr(buf, blocks[3]); + btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID); + btrfs_set_header_nritems(buf, nritems); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, leafsize, blocks[3]); + + /* create the device tree */ + nritems = 0; + itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - + sizeof(struct btrfs_dev_extent); + + btrfs_set_disk_key_objectid(&disk_key, 1); + btrfs_set_disk_key_offset(&disk_key, 0); + btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), + sizeof(struct btrfs_dev_extent)); + dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent); + btrfs_set_dev_extent_chunk_tree(buf, dev_extent, + BTRFS_CHUNK_TREE_OBJECTID); + btrfs_set_dev_extent_chunk_objectid(buf, dev_extent, + BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_dev_extent_chunk_offset(buf, dev_extent, 0); + + write_extent_buffer(buf, chunk_tree_uuid, + (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent), + BTRFS_UUID_SIZE); + + btrfs_set_dev_extent_length(buf, dev_extent, + BTRFS_MKFS_SYSTEM_GROUP_SIZE); + nritems++; + + btrfs_set_header_bytenr(buf, blocks[4]); + btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID); + btrfs_set_header_nritems(buf, nritems); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, leafsize, blocks[4]); + + /* create the FS root */ + btrfs_set_header_bytenr(buf, blocks[5]); + btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID); + btrfs_set_header_nritems(buf, 0); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, leafsize, blocks[5]); + BUG_ON(ret != leafsize); + + /* finally create the csum root */ + btrfs_set_header_bytenr(buf, blocks[6]); + btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID); + btrfs_set_header_nritems(buf, 0); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, leafsize, blocks[6]); + BUG_ON(ret != leafsize); + + /* and write out the super block */ + BUG_ON(sizeof(super) > sectorsize); + memset(buf->data, 0, sectorsize); + memcpy(buf->data, &super, sizeof(super)); + buf->len = sectorsize; + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, sectorsize, blocks[0]); + BUG_ON(ret != sectorsize); + + + free(buf); + return 0; +} + +static u64 device_size(int fd, struct stat *st) +{ + u64 size; + if (S_ISREG(st->st_mode)) { + return st->st_size; + } + if (!S_ISBLK(st->st_mode)) { + return 0; + } + if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { + return size; + } + return 0; +} + +static int zero_blocks(int fd, off_t start, size_t len) +{ + char *buf = malloc(len); + int ret = 0; + ssize_t written; + + if (!buf) + return -ENOMEM; + memset(buf, 0, len); + written = pwrite(fd, buf, len, start); + if (written != len) + ret = -EIO; + free(buf); + return ret; +} + +static int zero_dev_start(int fd) +{ + off_t start = 0; + size_t len = 2 * 1024 * 1024; + +#ifdef __sparc__ + /* don't overwrite the disk labels on sparc */ + start = 1024; + len -= 1024; +#endif + return zero_blocks(fd, start, len); +} + +static int zero_dev_end(int fd, u64 dev_size) +{ + size_t len = 2 * 1024 * 1024; + off_t start = dev_size - len; + + return zero_blocks(fd, start, len); +} + +int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, + struct btrfs_root *root, int fd, char *path, + u64 block_count, u32 io_width, u32 io_align, + u32 sectorsize) +{ + struct btrfs_super_block *disk_super; + struct btrfs_super_block *super = &root->fs_info->super_copy; + struct btrfs_device *device; + struct btrfs_dev_item *dev_item; + char *buf; + u64 total_bytes; + u64 num_devs; + int ret; + + device = kmalloc(sizeof(*device), GFP_NOFS); + if (!device) + return -ENOMEM; + buf = kmalloc(sectorsize, GFP_NOFS); + if (!buf) { + kfree(device); + return -ENOMEM; + } + BUG_ON(sizeof(*disk_super) > sectorsize); + memset(buf, 0, sectorsize); + + disk_super = (struct btrfs_super_block *)buf; + dev_item = &disk_super->dev_item; + + uuid_generate(device->uuid); + device->devid = 0; + device->type = 0; + device->io_width = io_width; + device->io_align = io_align; + device->sector_size = sectorsize; + device->fd = fd; + device->writeable = 1; + device->total_bytes = block_count; + device->bytes_used = 0; + device->total_ios = 0; + device->dev_root = root->fs_info->dev_root; + + ret = btrfs_add_device(trans, root, device); + BUG_ON(ret); + + total_bytes = btrfs_super_total_bytes(super) + block_count; + btrfs_set_super_total_bytes(super, total_bytes); + + num_devs = btrfs_super_num_devices(super) + 1; + btrfs_set_super_num_devices(super, num_devs); + + memcpy(disk_super, super, sizeof(*disk_super)); + + printf("adding device %s id %llu\n", path, + (unsigned long long)device->devid); + + btrfs_set_super_bytenr(disk_super, BTRFS_SUPER_INFO_OFFSET); + btrfs_set_stack_device_id(dev_item, device->devid); + btrfs_set_stack_device_type(dev_item, device->type); + btrfs_set_stack_device_io_align(dev_item, device->io_align); + btrfs_set_stack_device_io_width(dev_item, device->io_width); + btrfs_set_stack_device_sector_size(dev_item, device->sector_size); + btrfs_set_stack_device_total_bytes(dev_item, device->total_bytes); + btrfs_set_stack_device_bytes_used(dev_item, device->bytes_used); + memcpy(&dev_item->uuid, device->uuid, BTRFS_UUID_SIZE); + + ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET); + BUG_ON(ret != sectorsize); + + kfree(buf); + list_add(&device->dev_list, &root->fs_info->fs_devices->devices); + device->fs_devices = root->fs_info->fs_devices; + return 0; +} + +int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret) +{ + u64 block_count; + u64 bytenr; + struct stat st; + int i, ret; + + ret = fstat(fd, &st); + if (ret < 0) { + fprintf(stderr, "unable to stat %s\n", file); + exit(1); + } + + block_count = device_size(fd, &st); + if (block_count == 0) { + fprintf(stderr, "unable to find %s size\n", file); + exit(1); + } + zero_end = 1; + + if (block_count < 256 * 1024 * 1024) { + fprintf(stderr, "device %s is too small " + "(must be at least 256 MB)\n", file); + exit(1); + } + ret = zero_dev_start(fd); + if (ret) { + fprintf(stderr, "failed to zero device start %d\n", ret); + exit(1); + } + + for (i = 0 ; i < BTRFS_SUPER_MIRROR_MAX; i++) { + bytenr = btrfs_sb_offset(i); + if (bytenr >= block_count) + break; + zero_blocks(fd, bytenr, BTRFS_SUPER_INFO_SIZE); + } + + if (zero_end) { + ret = zero_dev_end(fd, block_count); + if (ret) { + fprintf(stderr, "failed to zero device end %d\n", ret); + exit(1); + } + } + *block_count_ret = block_count; + return 0; +} + +int btrfs_make_root_dir(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid) +{ + int ret; + struct btrfs_inode_item inode_item; + + memset(&inode_item, 0, sizeof(inode_item)); + btrfs_set_stack_inode_generation(&inode_item, trans->transid); + btrfs_set_stack_inode_size(&inode_item, 0); + btrfs_set_stack_inode_nlink(&inode_item, 1); + btrfs_set_stack_inode_nbytes(&inode_item, root->leafsize); + btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0555); + + if (root->fs_info->tree_root == root) + btrfs_set_super_root_dir(&root->fs_info->super_copy, objectid); + + ret = btrfs_insert_inode(trans, root, objectid, &inode_item); + if (ret) + goto error; + + ret = btrfs_insert_inode_ref(trans, root, "..", 2, objectid, objectid, 0); + if (ret) + goto error; + + btrfs_set_root_dirid(&root->root_item, objectid); + ret = 0; +error: + return ret; +} + +/* + * returns 1 if the device was mounted, < 0 on error or 0 if everything + * is safe to continue. TODO, this should also scan multi-device filesystems + */ +int check_mounted(char *file) +{ + struct mntent *mnt; + struct stat st_buf; + dev_t file_dev = 0; + dev_t file_rdev = 0; + ino_t file_ino = 0; + FILE *f; + int ret = 0; + + if ((f = setmntent ("/proc/mounts", "r")) == NULL) + return -errno; + + if (stat(file, &st_buf) < 0) { + return -errno; + } else { + if (S_ISBLK(st_buf.st_mode)) { + file_rdev = st_buf.st_rdev; + } else { + file_dev = st_buf.st_dev; + file_ino = st_buf.st_ino; + } + } + + while ((mnt = getmntent (f)) != NULL) { + if (strcmp(file, mnt->mnt_fsname) == 0) + break; + + if (stat(mnt->mnt_fsname, &st_buf) == 0) { + if (S_ISBLK(st_buf.st_mode)) { + if (file_rdev && (file_rdev == st_buf.st_rdev)) + break; + } else if (file_dev && ((file_dev == st_buf.st_dev) && + (file_ino == st_buf.st_ino))) { + break; + } + } + } + + if (mnt) { + /* found an entry in mnt table */ + ret = 1; + } + + endmntent (f); + return ret; +} + +struct pending_dir { + struct list_head list; + char name[256]; +}; + +void btrfs_register_one_device(char *fname) +{ + struct btrfs_ioctl_vol_args args; + int fd; + int ret; + + fd = open("/dev/btrfs-control", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "failed to open /dev/btrfs-control " + "skipping device registration\n"); + return; + } + strcpy(args.name, fname); + ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); + close(fd); +} + +int btrfs_scan_one_dir(char *dirname, int run_ioctl) +{ + DIR *dirp = NULL; + struct dirent *dirent; + struct pending_dir *pending; + struct stat st; + int ret; + int fd; + int dirname_len; + int pathlen; + char *fullpath; + struct list_head pending_list; + struct btrfs_fs_devices *tmp_devices; + u64 num_devices; + + INIT_LIST_HEAD(&pending_list); + + pending = malloc(sizeof(*pending)); + if (!pending) + return -ENOMEM; + strcpy(pending->name, dirname); + +again: + dirname_len = strlen(pending->name); + pathlen = 1024; + fullpath = malloc(pathlen); + dirname = pending->name; + + if (!fullpath) { + ret = -ENOMEM; + goto fail; + } + dirp = opendir(dirname); + if (!dirp) { + fprintf(stderr, "Unable to open /sys/block for scanning\n"); + return -ENOENT; + } + while(1) { + dirent = readdir(dirp); + if (!dirent) + break; + if (dirent->d_name[0] == '.') + continue; + if (dirname_len + strlen(dirent->d_name) + 2 > pathlen) { + ret = -EFAULT; + goto fail; + } + snprintf(fullpath, pathlen, "%s/%s", dirname, dirent->d_name); + ret = lstat(fullpath, &st); + if (ret < 0) { + fprintf(stderr, "failed to stat %s\n", fullpath); + continue; + } + if (S_ISLNK(st.st_mode)) + continue; + if (S_ISDIR(st.st_mode)) { + struct pending_dir *next = malloc(sizeof(*next)); + if (!next) { + ret = -ENOMEM; + goto fail; + } + strcpy(next->name, fullpath); + list_add_tail(&next->list, &pending_list); + } + if (!S_ISBLK(st.st_mode)) { + continue; + } + fd = open(fullpath, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "failed to read %s\n", fullpath); + continue; + } + ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices, + &num_devices, + BTRFS_SUPER_INFO_OFFSET); + if (ret == 0 && run_ioctl > 0) { + btrfs_register_one_device(fullpath); + } + close(fd); + } + if (!list_empty(&pending_list)) { + free(pending); + pending = list_entry(pending_list.next, struct pending_dir, + list); + list_del(&pending->list); + closedir(dirp); + goto again; + } + ret = 0; +fail: + free(pending); + if (dirp) + closedir(dirp); + return ret; +} + +int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs, + int run_ioctls) +{ + return btrfs_scan_one_dir("/dev", run_ioctls); +} + +int btrfs_device_already_in_root(struct btrfs_root *root, int fd, + int super_offset) +{ + struct btrfs_super_block *disk_super; + char *buf; + int ret = 0; + + buf = malloc(BTRFS_SUPER_INFO_SIZE); + if (!buf) { + ret = -ENOMEM; + goto out; + } + ret = pread(fd, buf, BTRFS_SUPER_INFO_SIZE, super_offset); + if (ret != BTRFS_SUPER_INFO_SIZE) + goto brelse; + + ret = 0; + disk_super = (struct btrfs_super_block *)buf; + if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC, + sizeof(disk_super->magic))) + goto brelse; + + if (!memcmp(disk_super->fsid, root->fs_info->super_copy.fsid, + BTRFS_FSID_SIZE)) + ret = 1; +brelse: + free(buf); +out: + return ret; +} + +static char *size_strs[] = { "", "KB", "MB", "GB", "TB", + "PB", "EB", "ZB", "YB"}; +char *pretty_sizes(u64 size) +{ + int num_divs = 0; + u64 last_size = size; + u64 fract_size = size; + float fraction; + char *pretty; + + while(size > 0) { + fract_size = last_size; + last_size = size; + size /= 1024; + num_divs++; + } + if (num_divs == 0) + num_divs = 1; + if (num_divs > ARRAY_SIZE(size_strs)) + return NULL; + + fraction = (float)fract_size / 1024; + pretty = malloc(16); + sprintf(pretty, "%.2f%s", fraction, size_strs[num_divs-1]); + return pretty; +} + diff --git a/lib/utils.h b/lib/utils.h new file mode 100644 index 0000000..7ff542b --- /dev/null +++ b/lib/utils.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __UTILS__ +#define __UTILS__ + +#define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024) + +int make_btrfs(int fd, const char *device, const char *label, + u64 blocks[6], u64 num_bytes, u32 nodesize, + u32 leafsize, u32 sectorsize, u32 stripesize); +int btrfs_make_root_dir(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid); +int btrfs_prepare_device(int fd, char *file, int zero_end, + u64 *block_count_ret); +int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, + struct btrfs_root *root, int fd, char *path, + u64 block_count, u32 io_width, u32 io_align, + u32 sectorsize); +int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs, + int run_ioctls); +void btrfs_register_one_device(char *fname); +int btrfs_scan_one_dir(char *dirname, int run_ioctl); +int check_mounted(char *devicename); +int btrfs_device_already_in_root(struct btrfs_root *root, int fd, + int super_offset); +char *pretty_sizes(u64 size); +#endif diff --git a/lib/volumes.c b/lib/volumes.c new file mode 100644 index 0000000..7671855 --- /dev/null +++ b/lib/volumes.c @@ -0,0 +1,1450 @@ +/* + * Copyright (C) 2007 Oracle. 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 600 +#define __USE_XOPEN2K +#include +#include +#include +#include +#include +#include +#include +#include "ctree.h" +#include "disk-io.h" +#include "transaction.h" +#include "print-tree.h" +#include "volumes.h" + +struct stripe { + struct btrfs_device *dev; + u64 physical; +}; + +struct map_lookup { + struct cache_extent ce; + u64 type; + int io_align; + int io_width; + int stripe_len; + int sector_size; + int num_stripes; + int sub_stripes; + struct btrfs_bio_stripe stripes[]; +}; + +#define map_lookup_size(n) (sizeof(struct map_lookup) + \ + (sizeof(struct btrfs_bio_stripe) * (n))) + +static LIST_HEAD(fs_uuids); + +static struct btrfs_device *__find_device(struct list_head *head, u64 devid, + u8 *uuid) +{ + struct btrfs_device *dev; + struct list_head *cur; + + list_for_each(cur, head) { + dev = list_entry(cur, struct btrfs_device, dev_list); + if (dev->devid == devid && + !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE)) { + return dev; + } + } + return NULL; +} + +static struct btrfs_fs_devices *find_fsid(u8 *fsid) +{ + struct list_head *cur; + struct btrfs_fs_devices *fs_devices; + + list_for_each(cur, &fs_uuids) { + fs_devices = list_entry(cur, struct btrfs_fs_devices, list); + if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0) + return fs_devices; + } + return NULL; +} + +static int device_list_add(const char *path, + struct btrfs_super_block *disk_super, + u64 devid, struct btrfs_fs_devices **fs_devices_ret) +{ + struct btrfs_device *device; + struct btrfs_fs_devices *fs_devices; + u64 found_transid = btrfs_super_generation(disk_super); + + fs_devices = find_fsid(disk_super->fsid); + if (!fs_devices) { + fs_devices = kzalloc(sizeof(*fs_devices), GFP_NOFS); + if (!fs_devices) + return -ENOMEM; + INIT_LIST_HEAD(&fs_devices->devices); + list_add(&fs_devices->list, &fs_uuids); + memcpy(fs_devices->fsid, disk_super->fsid, BTRFS_FSID_SIZE); + fs_devices->latest_devid = devid; + fs_devices->latest_trans = found_transid; + fs_devices->lowest_devid = (u64)-1; + device = NULL; + } else { + device = __find_device(&fs_devices->devices, devid, + disk_super->dev_item.uuid); + } + if (!device) { + device = kzalloc(sizeof(*device), GFP_NOFS); + if (!device) { + /* we can safely leave the fs_devices entry around */ + return -ENOMEM; + } + device->devid = devid; + memcpy(device->uuid, disk_super->dev_item.uuid, + BTRFS_UUID_SIZE); + device->name = kstrdup(path, GFP_NOFS); + if (!device->name) { + kfree(device); + return -ENOMEM; + } + device->label = kstrdup(disk_super->label, GFP_NOFS); + device->total_devs = btrfs_super_num_devices(disk_super); + device->super_bytes_used = btrfs_super_bytes_used(disk_super); + device->total_bytes = + btrfs_stack_device_total_bytes(&disk_super->dev_item); + device->bytes_used = + btrfs_stack_device_bytes_used(&disk_super->dev_item); + list_add(&device->dev_list, &fs_devices->devices); + device->fs_devices = fs_devices; + } + + if (found_transid > fs_devices->latest_trans) { + fs_devices->latest_devid = devid; + fs_devices->latest_trans = found_transid; + } + if (fs_devices->lowest_devid > devid) { + fs_devices->lowest_devid = devid; + } + *fs_devices_ret = fs_devices; + return 0; +} + +int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) +{ + struct btrfs_fs_devices *seed_devices; + struct list_head *cur; + struct btrfs_device *device; +again: + list_for_each(cur, &fs_devices->devices) { + device = list_entry(cur, struct btrfs_device, dev_list); + close(device->fd); + device->fd = -1; + device->writeable = 0; + } + + seed_devices = fs_devices->seed; + fs_devices->seed = NULL; + if (seed_devices) { + fs_devices = seed_devices; + goto again; + } + + return 0; +} + +int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, int flags) +{ + int fd; + struct list_head *head = &fs_devices->devices; + struct list_head *cur; + struct btrfs_device *device; + int ret; + + list_for_each(cur, head) { + device = list_entry(cur, struct btrfs_device, dev_list); + + fd = open(device->name, flags); + if (fd < 0) { + ret = -errno; + goto fail; + } + + if (device->devid == fs_devices->latest_devid) + fs_devices->latest_bdev = fd; + if (device->devid == fs_devices->lowest_devid) + fs_devices->lowest_bdev = fd; + device->fd = fd; + if (flags == O_RDWR) + device->writeable = 1; + } + return 0; +fail: + btrfs_close_devices(fs_devices); + return ret; +} + +int btrfs_scan_one_device(int fd, const char *path, + struct btrfs_fs_devices **fs_devices_ret, + u64 *total_devs, u64 super_offset) +{ + struct btrfs_super_block *disk_super; + char *buf; + int ret; + u64 devid; + char uuidbuf[37]; + + buf = malloc(4096); + if (!buf) { + ret = -ENOMEM; + goto error; + } + disk_super = (struct btrfs_super_block *)buf; + ret = btrfs_read_dev_super(fd, disk_super, super_offset); + if (ret < 0) { + ret = -EIO; + goto error_brelse; + } + devid = le64_to_cpu(disk_super->dev_item.devid); + if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP) + *total_devs = 1; + else + *total_devs = btrfs_super_num_devices(disk_super); + uuid_unparse(disk_super->fsid, uuidbuf); + + ret = device_list_add(path, disk_super, devid, fs_devices_ret); + +error_brelse: + free(buf); +error: + return ret; +} + +/* + * this uses a pretty simple search, the expectation is that it is + * called very infrequently and that a given device has a small number + * of extents + */ +static int find_free_dev_extent(struct btrfs_trans_handle *trans, + struct btrfs_device *device, + struct btrfs_path *path, + u64 num_bytes, u64 *start) +{ + struct btrfs_key key; + struct btrfs_root *root = device->dev_root; + struct btrfs_dev_extent *dev_extent = NULL; + u64 hole_size = 0; + u64 last_byte = 0; + u64 search_start = 0; + u64 search_end = device->total_bytes; + int ret; + int slot = 0; + int start_found; + struct extent_buffer *l; + + start_found = 0; + path->reada = 2; + + /* FIXME use last free of some kind */ + + /* we don't want to overwrite the superblock on the drive, + * so we make sure to start at an offset of at least 1MB + */ + search_start = max((u64)1024 * 1024, search_start); + + if (root->fs_info->alloc_start + num_bytes <= device->total_bytes) + search_start = max(root->fs_info->alloc_start, search_start); + + key.objectid = device->devid; + key.offset = search_start; + key.type = BTRFS_DEV_EXTENT_KEY; + ret = btrfs_search_slot(trans, root, &key, path, 0, 0); + if (ret < 0) + goto error; + ret = btrfs_previous_item(root, path, 0, key.type); + if (ret < 0) + goto error; + l = path->nodes[0]; + btrfs_item_key_to_cpu(l, &key, path->slots[0]); + while (1) { + l = path->nodes[0]; + slot = path->slots[0]; + if (slot >= btrfs_header_nritems(l)) { + ret = btrfs_next_leaf(root, path); + if (ret == 0) + continue; + if (ret < 0) + goto error; +no_more_items: + if (!start_found) { + if (search_start >= search_end) { + ret = -ENOSPC; + goto error; + } + *start = search_start; + start_found = 1; + goto check_pending; + } + *start = last_byte > search_start ? + last_byte : search_start; + if (search_end <= *start) { + ret = -ENOSPC; + goto error; + } + goto check_pending; + } + btrfs_item_key_to_cpu(l, &key, slot); + + if (key.objectid < device->devid) + goto next; + + if (key.objectid > device->devid) + goto no_more_items; + + if (key.offset >= search_start && key.offset > last_byte && + start_found) { + if (last_byte < search_start) + last_byte = search_start; + hole_size = key.offset - last_byte; + if (key.offset > last_byte && + hole_size >= num_bytes) { + *start = last_byte; + goto check_pending; + } + } + if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY) { + goto next; + } + + start_found = 1; + dev_extent = btrfs_item_ptr(l, slot, struct btrfs_dev_extent); + last_byte = key.offset + btrfs_dev_extent_length(l, dev_extent); +next: + path->slots[0]++; + cond_resched(); + } +check_pending: + /* we have to make sure we didn't find an extent that has already + * been allocated by the map tree or the original allocation + */ + btrfs_release_path(root, path); + BUG_ON(*start < search_start); + + if (*start + num_bytes > search_end) { + ret = -ENOSPC; + goto error; + } + /* check for pending inserts here */ + return 0; + +error: + btrfs_release_path(root, path); + return ret; +} + +int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans, + struct btrfs_device *device, + u64 chunk_tree, u64 chunk_objectid, + u64 chunk_offset, + u64 num_bytes, u64 *start) +{ + int ret; + struct btrfs_path *path; + struct btrfs_root *root = device->dev_root; + struct btrfs_dev_extent *extent; + struct extent_buffer *leaf; + struct btrfs_key key; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ret = find_free_dev_extent(trans, device, path, num_bytes, start); + if (ret) { + goto err; + } + + key.objectid = device->devid; + key.offset = *start; + key.type = BTRFS_DEV_EXTENT_KEY; + ret = btrfs_insert_empty_item(trans, root, path, &key, + sizeof(*extent)); + BUG_ON(ret); + + leaf = path->nodes[0]; + extent = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_dev_extent); + btrfs_set_dev_extent_chunk_tree(leaf, extent, chunk_tree); + btrfs_set_dev_extent_chunk_objectid(leaf, extent, chunk_objectid); + btrfs_set_dev_extent_chunk_offset(leaf, extent, chunk_offset); + + write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, + (unsigned long)btrfs_dev_extent_chunk_tree_uuid(extent), + BTRFS_UUID_SIZE); + + btrfs_set_dev_extent_length(leaf, extent, num_bytes); + btrfs_mark_buffer_dirty(leaf); +err: + btrfs_free_path(path); + return ret; +} + +static int find_next_chunk(struct btrfs_root *root, u64 objectid, u64 *offset) +{ + struct btrfs_path *path; + int ret; + struct btrfs_key key; + struct btrfs_chunk *chunk; + struct btrfs_key found_key; + + path = btrfs_alloc_path(); + BUG_ON(!path); + + key.objectid = objectid; + key.offset = (u64)-1; + key.type = BTRFS_CHUNK_ITEM_KEY; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + goto error; + + BUG_ON(ret == 0); + + ret = btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY); + if (ret) { + *offset = 0; + } else { + btrfs_item_key_to_cpu(path->nodes[0], &found_key, + path->slots[0]); + if (found_key.objectid != objectid) + *offset = 0; + else { + chunk = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_chunk); + *offset = found_key.offset + + btrfs_chunk_length(path->nodes[0], chunk); + } + } + ret = 0; +error: + btrfs_free_path(path); + return ret; +} + +static int find_next_devid(struct btrfs_root *root, struct btrfs_path *path, + u64 *objectid) +{ + int ret; + struct btrfs_key key; + struct btrfs_key found_key; + + key.objectid = BTRFS_DEV_ITEMS_OBJECTID; + key.type = BTRFS_DEV_ITEM_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + goto error; + + BUG_ON(ret == 0); + + ret = btrfs_previous_item(root, path, BTRFS_DEV_ITEMS_OBJECTID, + BTRFS_DEV_ITEM_KEY); + if (ret) { + *objectid = 1; + } else { + btrfs_item_key_to_cpu(path->nodes[0], &found_key, + path->slots[0]); + *objectid = found_key.offset + 1; + } + ret = 0; +error: + btrfs_release_path(root, path); + return ret; +} + +/* + * the device information is stored in the chunk root + * the btrfs_device struct should be fully filled in + */ +int btrfs_add_device(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_device *device) +{ + int ret; + struct btrfs_path *path; + struct btrfs_dev_item *dev_item; + struct extent_buffer *leaf; + struct btrfs_key key; + unsigned long ptr; + u64 free_devid = 0; + + root = root->fs_info->chunk_root; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ret = find_next_devid(root, path, &free_devid); + if (ret) + goto out; + + key.objectid = BTRFS_DEV_ITEMS_OBJECTID; + key.type = BTRFS_DEV_ITEM_KEY; + key.offset = free_devid; + + ret = btrfs_insert_empty_item(trans, root, path, &key, + sizeof(*dev_item)); + if (ret) + goto out; + + leaf = path->nodes[0]; + dev_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_item); + + device->devid = free_devid; + btrfs_set_device_id(leaf, dev_item, device->devid); + btrfs_set_device_generation(leaf, dev_item, 0); + btrfs_set_device_type(leaf, dev_item, device->type); + btrfs_set_device_io_align(leaf, dev_item, device->io_align); + btrfs_set_device_io_width(leaf, dev_item, device->io_width); + btrfs_set_device_sector_size(leaf, dev_item, device->sector_size); + btrfs_set_device_total_bytes(leaf, dev_item, device->total_bytes); + btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used); + btrfs_set_device_group(leaf, dev_item, 0); + btrfs_set_device_seek_speed(leaf, dev_item, 0); + btrfs_set_device_bandwidth(leaf, dev_item, 0); + btrfs_set_device_start_offset(leaf, dev_item, 0); + + ptr = (unsigned long)btrfs_device_uuid(dev_item); + write_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE); + ptr = (unsigned long)btrfs_device_fsid(dev_item); + write_extent_buffer(leaf, root->fs_info->fsid, ptr, BTRFS_UUID_SIZE); + btrfs_mark_buffer_dirty(leaf); + ret = 0; + +out: + btrfs_free_path(path); + return ret; +} + +int btrfs_update_device(struct btrfs_trans_handle *trans, + struct btrfs_device *device) +{ + int ret; + struct btrfs_path *path; + struct btrfs_root *root; + struct btrfs_dev_item *dev_item; + struct extent_buffer *leaf; + struct btrfs_key key; + + root = device->dev_root->fs_info->chunk_root; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = BTRFS_DEV_ITEMS_OBJECTID; + key.type = BTRFS_DEV_ITEM_KEY; + key.offset = device->devid; + + ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + if (ret < 0) + goto out; + + if (ret > 0) { + ret = -ENOENT; + goto out; + } + + leaf = path->nodes[0]; + dev_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_item); + + btrfs_set_device_id(leaf, dev_item, device->devid); + btrfs_set_device_type(leaf, dev_item, device->type); + btrfs_set_device_io_align(leaf, dev_item, device->io_align); + btrfs_set_device_io_width(leaf, dev_item, device->io_width); + btrfs_set_device_sector_size(leaf, dev_item, device->sector_size); + btrfs_set_device_total_bytes(leaf, dev_item, device->total_bytes); + btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used); + btrfs_mark_buffer_dirty(leaf); + +out: + btrfs_free_path(path); + return ret; +} + +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) +{ + struct btrfs_super_block *super_copy = &root->fs_info->super_copy; + struct btrfs_disk_key disk_key; + u32 array_size; + u8 *ptr; + + array_size = btrfs_super_sys_array_size(super_copy); + if (array_size + item_size > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) + return -EFBIG; + + ptr = super_copy->sys_chunk_array + array_size; + btrfs_cpu_key_to_disk(&disk_key, key); + memcpy(ptr, &disk_key, sizeof(disk_key)); + ptr += sizeof(disk_key); + memcpy(ptr, chunk, item_size); + item_size += sizeof(disk_key); + btrfs_set_super_sys_array_size(super_copy, array_size + item_size); + return 0; +} + +static u64 div_factor(u64 num, int factor) +{ + if (factor == 10) + return num; + num *= factor; + return num / 10; +} + +static u64 chunk_bytes_by_type(u64 type, u64 calc_size, int num_stripes, + int sub_stripes) +{ + if (type & (BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP)) + return calc_size; + else if (type & BTRFS_BLOCK_GROUP_RAID10) + return calc_size * (num_stripes / sub_stripes); + else + return calc_size * num_stripes; +} + + +int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, + struct btrfs_root *extent_root, u64 *start, + u64 *num_bytes, u64 type) +{ + u64 dev_offset; + struct btrfs_fs_info *info = extent_root->fs_info; + struct btrfs_root *chunk_root = extent_root->fs_info->chunk_root; + struct btrfs_stripe *stripes; + struct btrfs_device *device = NULL; + struct btrfs_chunk *chunk; + struct list_head private_devs; + struct list_head *dev_list = &extent_root->fs_info->fs_devices->devices; + struct list_head *cur; + struct map_lookup *map; + int min_stripe_size = 1 * 1024 * 1024; + u64 physical; + u64 calc_size = 8 * 1024 * 1024; + u64 min_free; + u64 max_chunk_size = 4 * calc_size; + u64 avail; + u64 max_avail = 0; + u64 percent_max; + int num_stripes = 1; + int min_stripes = 1; + int sub_stripes = 0; + int looped = 0; + int ret; + int index; + int stripe_len = 64 * 1024; + struct btrfs_key key; + + if (list_empty(dev_list)) { + return -ENOSPC; + } + + if (type & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_RAID10 | + BTRFS_BLOCK_GROUP_DUP)) { + if (type & BTRFS_BLOCK_GROUP_SYSTEM) { + calc_size = 8 * 1024 * 1024; + max_chunk_size = calc_size * 2; + min_stripe_size = 1 * 1024 * 1024; + } else if (type & BTRFS_BLOCK_GROUP_DATA) { + calc_size = 1024 * 1024 * 1024; + max_chunk_size = 10 * calc_size; + min_stripe_size = 64 * 1024 * 1024; + } else if (type & BTRFS_BLOCK_GROUP_METADATA) { + calc_size = 1024 * 1024 * 1024; + max_chunk_size = 4 * calc_size; + min_stripe_size = 32 * 1024 * 1024; + } + } + if (type & BTRFS_BLOCK_GROUP_RAID1) { + num_stripes = min_t(u64, 2, + btrfs_super_num_devices(&info->super_copy)); + if (num_stripes < 2) + return -ENOSPC; + min_stripes = 2; + } + if (type & BTRFS_BLOCK_GROUP_DUP) { + num_stripes = 2; + min_stripes = 2; + } + if (type & (BTRFS_BLOCK_GROUP_RAID0)) { + num_stripes = btrfs_super_num_devices(&info->super_copy); + min_stripes = 2; + } + if (type & (BTRFS_BLOCK_GROUP_RAID10)) { + num_stripes = btrfs_super_num_devices(&info->super_copy); + if (num_stripes < 4) + return -ENOSPC; + num_stripes &= ~(u32)1; + sub_stripes = 2; + min_stripes = 4; + } + + /* we don't want a chunk larger than 10% of the FS */ + percent_max = div_factor(btrfs_super_total_bytes(&info->super_copy), 1); + max_chunk_size = min(percent_max, max_chunk_size); + +again: + if (chunk_bytes_by_type(type, calc_size, num_stripes, sub_stripes) > + max_chunk_size) { + calc_size = max_chunk_size; + calc_size /= num_stripes; + calc_size /= stripe_len; + calc_size *= stripe_len; + } + /* we don't want tiny stripes */ + calc_size = max_t(u64, calc_size, min_stripe_size); + + calc_size /= stripe_len; + calc_size *= stripe_len; + INIT_LIST_HEAD(&private_devs); + cur = dev_list->next; + index = 0; + + if (type & BTRFS_BLOCK_GROUP_DUP) + min_free = calc_size * 2; + else + min_free = calc_size; + + /* build a private list of devices we will allocate from */ + while(index < num_stripes) { + device = list_entry(cur, struct btrfs_device, dev_list); + avail = device->total_bytes - device->bytes_used; + cur = cur->next; + if (avail >= min_free) { + list_move_tail(&device->dev_list, &private_devs); + index++; + if (type & BTRFS_BLOCK_GROUP_DUP) + index++; + } else if (avail > max_avail) + max_avail = avail; + if (cur == dev_list) + break; + } + if (index < num_stripes) { + list_splice(&private_devs, dev_list); + if (index >= min_stripes) { + num_stripes = index; + if (type & (BTRFS_BLOCK_GROUP_RAID10)) { + num_stripes /= sub_stripes; + num_stripes *= sub_stripes; + } + looped = 1; + goto again; + } + if (!looped && max_avail > 0) { + looped = 1; + calc_size = max_avail; + goto again; + } + return -ENOSPC; + } + key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + key.type = BTRFS_CHUNK_ITEM_KEY; + ret = find_next_chunk(chunk_root, BTRFS_FIRST_CHUNK_TREE_OBJECTID, + &key.offset); + if (ret) + return ret; + + chunk = kmalloc(btrfs_chunk_item_size(num_stripes), GFP_NOFS); + if (!chunk) + return -ENOMEM; + + map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); + if (!map) { + kfree(chunk); + return -ENOMEM; + } + + stripes = &chunk->stripe; + *num_bytes = chunk_bytes_by_type(type, calc_size, + num_stripes, sub_stripes); + index = 0; + while(index < num_stripes) { + struct btrfs_stripe *stripe; + BUG_ON(list_empty(&private_devs)); + cur = private_devs.next; + device = list_entry(cur, struct btrfs_device, dev_list); + + /* loop over this device again if we're doing a dup group */ + if (!(type & BTRFS_BLOCK_GROUP_DUP) || + (index == num_stripes - 1)) + list_move_tail(&device->dev_list, dev_list); + + ret = btrfs_alloc_dev_extent(trans, device, + info->chunk_root->root_key.objectid, + BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset, + calc_size, &dev_offset); + BUG_ON(ret); + + device->bytes_used += calc_size; + ret = btrfs_update_device(trans, device); + BUG_ON(ret); + + map->stripes[index].dev = device; + map->stripes[index].physical = dev_offset; + stripe = stripes + index; + btrfs_set_stack_stripe_devid(stripe, device->devid); + btrfs_set_stack_stripe_offset(stripe, dev_offset); + memcpy(stripe->dev_uuid, device->uuid, BTRFS_UUID_SIZE); + physical = dev_offset; + index++; + } + BUG_ON(!list_empty(&private_devs)); + + /* key was set above */ + btrfs_set_stack_chunk_length(chunk, *num_bytes); + btrfs_set_stack_chunk_owner(chunk, extent_root->root_key.objectid); + btrfs_set_stack_chunk_stripe_len(chunk, stripe_len); + btrfs_set_stack_chunk_type(chunk, type); + btrfs_set_stack_chunk_num_stripes(chunk, num_stripes); + btrfs_set_stack_chunk_io_align(chunk, stripe_len); + btrfs_set_stack_chunk_io_width(chunk, stripe_len); + btrfs_set_stack_chunk_sector_size(chunk, extent_root->sectorsize); + btrfs_set_stack_chunk_sub_stripes(chunk, sub_stripes); + map->sector_size = extent_root->sectorsize; + map->stripe_len = stripe_len; + map->io_align = stripe_len; + map->io_width = stripe_len; + map->type = type; + map->num_stripes = num_stripes; + map->sub_stripes = sub_stripes; + + ret = btrfs_insert_item(trans, chunk_root, &key, chunk, + btrfs_chunk_item_size(num_stripes)); + BUG_ON(ret); + *start = key.offset;; + + map->ce.start = key.offset; + map->ce.size = *num_bytes; + + ret = insert_existing_cache_extent( + &extent_root->fs_info->mapping_tree.cache_tree, + &map->ce); + BUG_ON(ret); + + if (type & BTRFS_BLOCK_GROUP_SYSTEM) { + ret = btrfs_add_system_chunk(trans, chunk_root, &key, + chunk, btrfs_chunk_item_size(num_stripes)); + BUG_ON(ret); + } + + kfree(chunk); + return ret; +} + +void btrfs_mapping_init(struct btrfs_mapping_tree *tree) +{ + cache_tree_init(&tree->cache_tree); +} + +int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len) +{ + struct cache_extent *ce; + struct map_lookup *map; + int ret; + u64 offset; + + ce = find_first_cache_extent(&map_tree->cache_tree, logical); + BUG_ON(!ce); + BUG_ON(ce->start > logical || ce->start + ce->size < logical); + map = container_of(ce, struct map_lookup, ce); + + offset = logical - ce->start; + if (map->type & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1)) + ret = map->num_stripes; + else if (map->type & BTRFS_BLOCK_GROUP_RAID10) + ret = map->sub_stripes; + else + ret = 1; + return ret; +} + +int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, + u64 chunk_start, u64 physical, u64 devid, + u64 **logical, int *naddrs, int *stripe_len) +{ + struct cache_extent *ce; + struct map_lookup *map; + u64 *buf; + u64 bytenr; + u64 length; + u64 stripe_nr; + int i, j, nr = 0; + + ce = find_first_cache_extent(&map_tree->cache_tree, chunk_start); + BUG_ON(!ce); + map = container_of(ce, struct map_lookup, ce); + + length = ce->size; + if (map->type & BTRFS_BLOCK_GROUP_RAID10) + length = ce->size / (map->num_stripes / map->sub_stripes); + else if (map->type & BTRFS_BLOCK_GROUP_RAID0) + length = ce->size / map->num_stripes; + + buf = kzalloc(sizeof(u64) * map->num_stripes, GFP_NOFS); + + for (i = 0; i < map->num_stripes; i++) { + if (devid && map->stripes[i].dev->devid != devid) + continue; + if (map->stripes[i].physical > physical || + map->stripes[i].physical + length <= physical) + continue; + + stripe_nr = (physical - map->stripes[i].physical) / + map->stripe_len; + + if (map->type & BTRFS_BLOCK_GROUP_RAID10) { + stripe_nr = (stripe_nr * map->num_stripes + i) / + map->sub_stripes; + } else if (map->type & BTRFS_BLOCK_GROUP_RAID0) { + stripe_nr = stripe_nr * map->num_stripes + i; + } + bytenr = ce->start + stripe_nr * map->stripe_len; + for (j = 0; j < nr; j++) { + if (buf[j] == bytenr) + break; + } + if (j == nr) + buf[nr++] = bytenr; + } + + *logical = buf; + *naddrs = nr; + *stripe_len = map->stripe_len; + + return 0; +} + +int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, + u64 logical, u64 *length, + struct btrfs_multi_bio **multi_ret, int mirror_num) +{ + struct cache_extent *ce; + struct map_lookup *map; + u64 offset; + u64 stripe_offset; + u64 stripe_nr; + int stripes_allocated = 8; + int stripes_required = 1; + int stripe_index; + int i; + struct btrfs_multi_bio *multi = NULL; + + if (multi_ret && rw == READ) { + stripes_allocated = 1; + } +again: + if (multi_ret) { + multi = kzalloc(btrfs_multi_bio_size(stripes_allocated), + GFP_NOFS); + if (!multi) + return -ENOMEM; + } + + ce = find_first_cache_extent(&map_tree->cache_tree, logical); + BUG_ON(!ce); + BUG_ON(ce->start > logical || ce->start + ce->size < logical); + map = container_of(ce, struct map_lookup, ce); + offset = logical - ce->start; + + if (rw == WRITE) { + if (map->type & (BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_DUP)) { + stripes_required = map->num_stripes; + } else if (map->type & BTRFS_BLOCK_GROUP_RAID10) { + stripes_required = map->sub_stripes; + } + } + /* if our multi bio struct is too small, back off and try again */ + if (multi_ret && rw == WRITE && + stripes_allocated < stripes_required) { + stripes_allocated = map->num_stripes; + kfree(multi); + goto again; + } + stripe_nr = offset; + /* + * stripe_nr counts the total number of stripes we have to stride + * to get to this block + */ + stripe_nr = stripe_nr / map->stripe_len; + + stripe_offset = stripe_nr * map->stripe_len; + BUG_ON(offset < stripe_offset); + + /* stripe_offset is the offset of this block in its stripe*/ + stripe_offset = offset - stripe_offset; + + if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_RAID10 | + BTRFS_BLOCK_GROUP_DUP)) { + /* we limit the length of each bio to what fits in a stripe */ + *length = min_t(u64, ce->size - offset, + map->stripe_len - stripe_offset); + } else { + *length = ce->size - offset; + } + + if (!multi_ret) + goto out; + + multi->num_stripes = 1; + stripe_index = 0; + if (map->type & BTRFS_BLOCK_GROUP_RAID1) { + if (rw == WRITE) + multi->num_stripes = map->num_stripes; + else if (mirror_num) + stripe_index = mirror_num - 1; + else + stripe_index = stripe_nr % map->num_stripes; + } else if (map->type & BTRFS_BLOCK_GROUP_RAID10) { + int factor = map->num_stripes / map->sub_stripes; + + stripe_index = stripe_nr % factor; + stripe_index *= map->sub_stripes; + + if (rw == WRITE) + multi->num_stripes = map->sub_stripes; + else if (mirror_num) + stripe_index += mirror_num - 1; + else + stripe_index = stripe_nr % map->sub_stripes; + + stripe_nr = stripe_nr / factor; + } else if (map->type & BTRFS_BLOCK_GROUP_DUP) { + if (rw == WRITE) + multi->num_stripes = map->num_stripes; + else if (mirror_num) + stripe_index = mirror_num - 1; + } else { + /* + * after this do_div call, stripe_nr is the number of stripes + * on this device we have to walk to find the data, and + * stripe_index is the number of our device in the stripe array + */ + stripe_index = stripe_nr % map->num_stripes; + stripe_nr = stripe_nr / map->num_stripes; + } + BUG_ON(stripe_index >= map->num_stripes); + + for (i = 0; i < multi->num_stripes; i++) { + multi->stripes[i].physical = + map->stripes[stripe_index].physical + stripe_offset + + stripe_nr * map->stripe_len; + multi->stripes[i].dev = map->stripes[stripe_index].dev; + stripe_index++; + } + *multi_ret = multi; +out: + return 0; +} + +struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid, + u8 *uuid, u8 *fsid) +{ + struct btrfs_device *device; + struct btrfs_fs_devices *cur_devices; + + cur_devices = root->fs_info->fs_devices; + while (cur_devices) { + if (!fsid || + !memcmp(cur_devices->fsid, fsid, BTRFS_UUID_SIZE)) { + device = __find_device(&cur_devices->devices, + devid, uuid); + if (device) + return device; + } + cur_devices = cur_devices->seed; + } + return NULL; +} + +int btrfs_bootstrap_super_map(struct btrfs_mapping_tree *map_tree, + struct btrfs_fs_devices *fs_devices) +{ + struct map_lookup *map; + u64 logical = BTRFS_SUPER_INFO_OFFSET; + u64 length = BTRFS_SUPER_INFO_SIZE; + int num_stripes = 0; + int sub_stripes = 0; + int ret; + int i; + struct list_head *cur; + + list_for_each(cur, &fs_devices->devices) { + num_stripes++; + } + map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); + if (!map) + return -ENOMEM; + + map->ce.start = logical; + map->ce.size = length; + map->num_stripes = num_stripes; + map->sub_stripes = sub_stripes; + map->io_width = length; + map->io_align = length; + map->sector_size = length; + map->stripe_len = length; + map->type = BTRFS_BLOCK_GROUP_RAID1; + + i = 0; + list_for_each(cur, &fs_devices->devices) { + struct btrfs_device *device = list_entry(cur, + struct btrfs_device, + dev_list); + map->stripes[i].physical = logical; + map->stripes[i].dev = device; + i++; + } + ret = insert_existing_cache_extent(&map_tree->cache_tree, &map->ce); + if (ret == -EEXIST) { + struct cache_extent *old; + struct map_lookup *old_map; + old = find_cache_extent(&map_tree->cache_tree, logical, length); + old_map = container_of(old, struct map_lookup, ce); + remove_cache_extent(&map_tree->cache_tree, old); + kfree(old_map); + ret = insert_existing_cache_extent(&map_tree->cache_tree, + &map->ce); + } + BUG_ON(ret); + return 0; +} + +int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset) +{ + struct cache_extent *ce; + struct map_lookup *map; + struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree; + int readonly = 0; + int i; + + ce = find_first_cache_extent(&map_tree->cache_tree, chunk_offset); + BUG_ON(!ce); + + map = container_of(ce, struct map_lookup, ce); + for (i = 0; i < map->num_stripes; i++) { + if (!map->stripes[i].dev->writeable) { + readonly = 1; + break; + } + } + + return readonly; +} + +static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, + struct extent_buffer *leaf, + struct btrfs_chunk *chunk) +{ + struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree; + struct map_lookup *map; + struct cache_extent *ce; + u64 logical; + u64 length; + u64 devid; + u8 uuid[BTRFS_UUID_SIZE]; + int num_stripes; + int ret; + int i; + + logical = key->offset; + length = btrfs_chunk_length(leaf, chunk); + + ce = find_first_cache_extent(&map_tree->cache_tree, logical); + + /* already mapped? */ + if (ce && ce->start <= logical && ce->start + ce->size > logical) { + return 0; + } + + num_stripes = btrfs_chunk_num_stripes(leaf, chunk); + map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); + if (!map) + return -ENOMEM; + + map->ce.start = logical; + map->ce.size = length; + map->num_stripes = num_stripes; + map->io_width = btrfs_chunk_io_width(leaf, chunk); + map->io_align = btrfs_chunk_io_align(leaf, chunk); + map->sector_size = btrfs_chunk_sector_size(leaf, chunk); + map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk); + map->type = btrfs_chunk_type(leaf, chunk); + map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk); + + for (i = 0; i < num_stripes; i++) { + map->stripes[i].physical = + btrfs_stripe_offset_nr(leaf, chunk, i); + devid = btrfs_stripe_devid_nr(leaf, chunk, i); + read_extent_buffer(leaf, uuid, (unsigned long) + btrfs_stripe_dev_uuid_nr(chunk, i), + BTRFS_UUID_SIZE); + map->stripes[i].dev = btrfs_find_device(root, devid, uuid, + NULL); + if (!map->stripes[i].dev) { + kfree(map); + return -EIO; + } + + } + ret = insert_existing_cache_extent(&map_tree->cache_tree, &map->ce); + BUG_ON(ret); + + return 0; +} + +static int fill_device_from_item(struct extent_buffer *leaf, + struct btrfs_dev_item *dev_item, + struct btrfs_device *device) +{ + unsigned long ptr; + + device->devid = btrfs_device_id(leaf, dev_item); + device->total_bytes = btrfs_device_total_bytes(leaf, dev_item); + device->bytes_used = btrfs_device_bytes_used(leaf, dev_item); + device->type = btrfs_device_type(leaf, dev_item); + device->io_align = btrfs_device_io_align(leaf, dev_item); + device->io_width = btrfs_device_io_width(leaf, dev_item); + device->sector_size = btrfs_device_sector_size(leaf, dev_item); + + ptr = (unsigned long)btrfs_device_uuid(dev_item); + read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE); + + return 0; +} + +static int open_seed_devices(struct btrfs_root *root, u8 *fsid) +{ + struct btrfs_fs_devices *fs_devices; + int ret; + + fs_devices = root->fs_info->fs_devices->seed; + while (fs_devices) { + if (!memcmp(fs_devices->fsid, fsid, BTRFS_UUID_SIZE)) { + ret = 0; + goto out; + } + fs_devices = fs_devices->seed; + } + + fs_devices = find_fsid(fsid); + if (!fs_devices) { + ret = -ENOENT; + goto out; + } + + ret = btrfs_open_devices(fs_devices, O_RDONLY); + if (ret) + goto out; + + fs_devices->seed = root->fs_info->fs_devices->seed; + root->fs_info->fs_devices->seed = fs_devices; +out: + return ret; +} + +static int read_one_dev(struct btrfs_root *root, + struct extent_buffer *leaf, + struct btrfs_dev_item *dev_item) +{ + struct btrfs_device *device; + u64 devid; + int ret = 0; + u8 fs_uuid[BTRFS_UUID_SIZE]; + u8 dev_uuid[BTRFS_UUID_SIZE]; + + devid = btrfs_device_id(leaf, dev_item); + read_extent_buffer(leaf, dev_uuid, + (unsigned long)btrfs_device_uuid(dev_item), + BTRFS_UUID_SIZE); + read_extent_buffer(leaf, fs_uuid, + (unsigned long)btrfs_device_fsid(dev_item), + BTRFS_UUID_SIZE); + + if (memcmp(fs_uuid, root->fs_info->fsid, BTRFS_UUID_SIZE)) { + ret = open_seed_devices(root, fs_uuid); + if (ret) + return ret; + } + + device = btrfs_find_device(root, devid, dev_uuid, fs_uuid); + if (!device) { + printk("warning devid %llu not found already\n", + (unsigned long long)devid); + device = kmalloc(sizeof(*device), GFP_NOFS); + if (!device) + return -ENOMEM; + device->total_ios = 0; + list_add(&device->dev_list, + &root->fs_info->fs_devices->devices); + } + + fill_device_from_item(leaf, dev_item, device); + device->dev_root = root->fs_info->dev_root; + return ret; +} + +int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf) +{ + struct btrfs_dev_item *dev_item; + + dev_item = (struct btrfs_dev_item *)offsetof(struct btrfs_super_block, + dev_item); + return read_one_dev(root, buf, dev_item); +} + +int btrfs_read_sys_array(struct btrfs_root *root) +{ + struct btrfs_super_block *super_copy = &root->fs_info->super_copy; + struct extent_buffer *sb; + struct btrfs_disk_key *disk_key; + struct btrfs_chunk *chunk; + struct btrfs_key key; + u32 num_stripes; + u32 array_size; + u32 len = 0; + u8 *ptr; + unsigned long sb_ptr; + u32 cur; + int ret; + + sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET, + BTRFS_SUPER_INFO_SIZE); + if (!sb) + return -ENOMEM; + btrfs_set_buffer_uptodate(sb); + write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE); + array_size = btrfs_super_sys_array_size(super_copy); + + /* + * we do this loop twice, once for the device items and + * once for all of the chunks. This way there are device + * structs filled in for every chunk + */ + ptr = super_copy->sys_chunk_array; + sb_ptr = offsetof(struct btrfs_super_block, sys_chunk_array); + cur = 0; + + while (cur < array_size) { + disk_key = (struct btrfs_disk_key *)ptr; + btrfs_disk_key_to_cpu(&key, disk_key); + + len = sizeof(*disk_key); + ptr += len; + sb_ptr += len; + cur += len; + + if (key.type == BTRFS_CHUNK_ITEM_KEY) { + chunk = (struct btrfs_chunk *)sb_ptr; + ret = read_one_chunk(root, &key, sb, chunk); + BUG_ON(ret); + num_stripes = btrfs_chunk_num_stripes(sb, chunk); + len = btrfs_chunk_item_size(num_stripes); + } else { + BUG(); + } + ptr += len; + sb_ptr += len; + cur += len; + } + free_extent_buffer(sb); + return 0; +} + +int btrfs_read_chunk_tree(struct btrfs_root *root) +{ + struct btrfs_path *path; + struct extent_buffer *leaf; + struct btrfs_key key; + struct btrfs_key found_key; + int ret; + int slot; + + root = root->fs_info->chunk_root; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + /* first we search for all of the device items, and then we + * read in all of the chunk items. This way we can create chunk + * mappings that reference all of the devices that are afound + */ + key.objectid = BTRFS_DEV_ITEMS_OBJECTID; + key.offset = 0; + key.type = 0; +again: + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + while(1) { + leaf = path->nodes[0]; + slot = path->slots[0]; + if (slot >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, path); + if (ret == 0) + continue; + if (ret < 0) + goto error; + break; + } + btrfs_item_key_to_cpu(leaf, &found_key, slot); + if (key.objectid == BTRFS_DEV_ITEMS_OBJECTID) { + if (found_key.objectid != BTRFS_DEV_ITEMS_OBJECTID) + break; + if (found_key.type == BTRFS_DEV_ITEM_KEY) { + struct btrfs_dev_item *dev_item; + dev_item = btrfs_item_ptr(leaf, slot, + struct btrfs_dev_item); + ret = read_one_dev(root, leaf, dev_item); + BUG_ON(ret); + } + } else if (found_key.type == BTRFS_CHUNK_ITEM_KEY) { + struct btrfs_chunk *chunk; + chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk); + ret = read_one_chunk(root, &found_key, leaf, chunk); + BUG_ON(ret); + } + path->slots[0]++; + } + if (key.objectid == BTRFS_DEV_ITEMS_OBJECTID) { + key.objectid = 0; + btrfs_release_path(root, path); + goto again; + } + + btrfs_free_path(path); + ret = 0; +error: + return ret; +} + +struct list_head *btrfs_scanned_uuids(void) +{ + return &fs_uuids; +} diff --git a/lib/volumes.h b/lib/volumes.h new file mode 100644 index 0000000..bb78751 --- /dev/null +++ b/lib/volumes.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __BTRFS_VOLUMES_ +#define __BTRFS_VOLUMES_ +struct btrfs_device { + struct list_head dev_list; + struct btrfs_root *dev_root; + struct btrfs_fs_devices *fs_devices; + + u64 total_ios; + + int fd; + + int writeable; + + char *name; + + /* these are read off the super block, only in the progs */ + char *label; + u64 total_devs; + u64 super_bytes_used; + + /* the internal btrfs device id */ + u64 devid; + + /* size of the device */ + u64 total_bytes; + + /* bytes used */ + u64 bytes_used; + + /* optimal io alignment for this device */ + u32 io_align; + + /* optimal io width for this device */ + u32 io_width; + + /* minimal io size for this device */ + u32 sector_size; + + /* type and info about this device */ + u64 type; + + /* physical drive uuid (or lvm uuid) */ + u8 uuid[BTRFS_UUID_SIZE]; +}; + +struct btrfs_fs_devices { + u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ + + /* the device with this id has the most recent coyp of the super */ + u64 latest_devid; + u64 latest_trans; + u64 lowest_devid; + int latest_bdev; + int lowest_bdev; + struct list_head devices; + struct list_head list; + + int seeding; + struct btrfs_fs_devices *seed; +}; + +struct btrfs_bio_stripe { + struct btrfs_device *dev; + u64 physical; +}; + +struct btrfs_multi_bio { + int error; + int num_stripes; + struct btrfs_bio_stripe stripes[]; +}; + +#define btrfs_multi_bio_size(n) (sizeof(struct btrfs_multi_bio) + \ + (sizeof(struct btrfs_bio_stripe) * (n))) + +int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans, + struct btrfs_device *device, + u64 chunk_tree, u64 chunk_objectid, + u64 chunk_offset, + u64 num_bytes, u64 *start); +int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, + u64 logical, u64 *length, + struct btrfs_multi_bio **multi_ret, int mirror_num); +int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, + u64 chunk_start, u64 physical, u64 devid, + u64 **logical, int *naddrs, int *stripe_len); +int btrfs_read_sys_array(struct btrfs_root *root); +int btrfs_read_chunk_tree(struct btrfs_root *root); +int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, + struct btrfs_root *extent_root, u64 *start, + u64 *num_bytes, u64 type); +int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf); +int btrfs_add_device(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_device *device); +int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, + int flags); +int btrfs_close_devices(struct btrfs_fs_devices *fs_devices); +int btrfs_add_device(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_device *device); +int btrfs_update_device(struct btrfs_trans_handle *trans, + struct btrfs_device *device); +int btrfs_scan_one_device(int fd, const char *path, + struct btrfs_fs_devices **fs_devices_ret, + u64 *total_devs, u64 super_offset); +int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len); +int btrfs_bootstrap_super_map(struct btrfs_mapping_tree *map_tree, + struct btrfs_fs_devices *fs_devices); +struct list_head *btrfs_scanned_uuids(void); +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); +#endif diff --git a/list.h b/list.h deleted file mode 100644 index d31090c..0000000 --- a/list.h +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef _LINUX_LIST_H -#define _LINUX_LIST_H - -#define LIST_POISON1 ((void *) 0x00100100) -#define LIST_POISON2 ((void *) 0x00200200) - -/* - * Simple doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -static inline void INIT_LIST_HEAD(struct list_head *list) -{ - list->next = list; - list->prev = list; -} - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -#ifndef CONFIG_DEBUG_LIST -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} -#else -extern void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next); -#endif - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -#ifndef CONFIG_DEBUG_LIST -static inline void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} -#else -extern void list_add(struct list_head *new, struct list_head *head); -#endif - - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_del(struct list_head * prev, struct list_head * next) -{ - next->prev = prev; - prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty on entry does not return true after this, the entry is - * in an undefined state. - */ -#ifndef CONFIG_DEBUG_LIST -static inline void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - entry->next = LIST_POISON1; - entry->prev = LIST_POISON2; -} -#else -extern void list_del(struct list_head *entry); -#endif - -/** - * list_replace - replace old entry by new one - * @old : the element to be replaced - * @new : the new element to insert - * Note: if 'old' was empty, it will be overwritten. - */ -static inline void list_replace(struct list_head *old, - struct list_head *new) -{ - new->next = old->next; - new->next->prev = new; - new->prev = old->prev; - new->prev->next = new; -} - -static inline void list_replace_init(struct list_head *old, - struct list_head *new) -{ - list_replace(old, new); - INIT_LIST_HEAD(old); -} -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -static inline void list_del_init(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); -} - -/** - * list_move - delete from one list and add as another's head - * @list: the entry to move - * @head: the head that will precede our entry - */ -static inline void list_move(struct list_head *list, struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add(list, head); -} - -/** - * list_move_tail - delete from one list and add as another's tail - * @list: the entry to move - * @head: the head that will follow our entry - */ -static inline void list_move_tail(struct list_head *list, - struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add_tail(list, head); -} - -/** - * list_is_last - tests whether @list is the last entry in list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_last(const struct list_head *list, - const struct list_head *head) -{ - return list->next == head; -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(const struct list_head *head) -{ - return head->next == head; -} - -/** - * list_empty_careful - tests whether a list is empty and not being modified - * @head: the list to test - * - * Description: - * tests whether a list is empty _and_ checks that no other CPU might be - * in the process of modifying either member (next or prev) - * - * NOTE: using list_empty_careful() without synchronization - * can only be safe if the only activity that can happen - * to the list entry is list_del_init(). Eg. it cannot be used - * if another CPU could re-list_add() it. - */ -static inline int list_empty_careful(const struct list_head *head) -{ - struct list_head *next = head->next; - return (next == head) && (next == head->prev); -} - -static inline void __list_splice(struct list_head *list, - struct list_head *head) -{ - struct list_head *first = list->next; - struct list_head *last = list->prev; - struct list_head *at = head->next; - - first->prev = head; - head->next = first; - - last->next = at; - at->prev = last; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -static inline void list_splice(struct list_head *list, struct list_head *head) -{ - if (!list_empty(list)) - __list_splice(list, head); -} - -/** - * list_splice_init - join two lists and reinitialise the emptied list. - * @list: the new list to add. - * @head: the place to add it in the first list. - * - * The list at @list is reinitialised - */ -static inline void list_splice_init(struct list_head *list, - struct list_head *head) -{ - if (!list_empty(list)) { - __list_splice(list, head); - INIT_LIST_HEAD(list); - } -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ - container_of(ptr, type, member) - -/** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); \ - pos = pos->next) - -/** - * __list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - * - * This variant differs from list_for_each() in that it's the - * simplest possible list iteration code, no prefetching is done. - * Use this for code that knows the list to be very short (empty - * or 1 entry) most of the time. - */ -#define __list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - -/** - * list_for_each_prev - iterate over a list backwards - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each_prev(pos, head) \ - for (pos = (head)->prev; pos != (head); \ - pos = pos->prev) - -/** - * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos: the &struct list_head to use as a loop cursor. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); \ - pos = n, n = pos->next) - -/** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_reverse - iterate backwards over list of given type. - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_reverse(pos, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.prev, typeof(*pos), member)) - -/** - * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue - * @pos: the type * to use as a start point - * @head: the head of the list - * @member: the name of the list_struct within the struct. - * - * Prepares a pos entry for use as a start point in list_for_each_entry_continue. - */ -#define list_prepare_entry(pos, head, member) \ - ((pos) ? : list_entry(head, typeof(*pos), member)) - -/** - * list_for_each_entry_continue - continue iteration over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Continue to iterate over list of given type, continuing after - * the current position. - */ -#define list_for_each_entry_continue(pos, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_from - iterate over list of given type from the current point - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type, continuing from current position. - */ -#define list_for_each_entry_from(pos, head, member) \ - for (; &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_continue - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type, continuing after current point, - * safe against removal of list entry. - */ -#define list_for_each_entry_safe_continue(pos, n, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_from - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type from current point, safe against - * removal of list entry. - */ -#define list_for_each_entry_safe_from(pos, n, head, member) \ - for (n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_reverse - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate backwards over list of given type, safe against removal - * of list entry. - */ -#define list_for_each_entry_safe_reverse(pos, n, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member), \ - n = list_entry(pos->member.prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.prev, typeof(*n), member)) - -#endif diff --git a/misc/Makefile b/misc/Makefile new file mode 100644 index 0000000..4a39c80 --- /dev/null +++ b/misc/Makefile @@ -0,0 +1,79 @@ +CC=gcc +AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 +CFLAGS = -g -Werror -Os -I../lib/ +# +CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ + -Wuninitialized -Wshadow -Wundef +DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ + +INSTALL= install +prefix ?= /usr/local +bindir = $(prefix)/bin +LIBS=-luuid +TOPDIR=../ + +progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ + btrfs-map-logical + +# make C=1 to enable sparse +ifdef C + check=sparse $(CHECKFLAGS) +else + check=ls +endif + +.c.o: + $(check) $< + $(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $< + +all: version $(progs) + +version: + bash version.sh + +btrfsctl: $(TOPDIR)/lib/libbtrfs.a btrfsctl.o + gcc $(CFLAGS) -static -o btrfsctl btrfsctl.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +btrfs-vol: $(TOPDIR)/lib/libbtrfs.a btrfs-vol.o + gcc $(CFLAGS) -o btrfs-vol btrfs-vol.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +btrfs-show: $(TOPDIR)/lib/libbtrfs.a btrfs-show.o + gcc $(CFLAGS) -o btrfs-show btrfs-show.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +btrfsck: $(TOPDIR)/lib/libbtrfs.a btrfsck.o + gcc $(CFLAGS) -o btrfsck btrfsck.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +mkfs.btrfs: $(TOPDIR)/lib/libbtrfs.a mkfs.o + gcc $(CFLAGS) -o mkfs.btrfs mkfs.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +btrfs-debug-tree: $(TOPDIR)/lib/libbtrfs.a debug-tree.o + gcc $(CFLAGS) -o btrfs-debug-tree debug-tree.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +btrfstune: $(TOPDIR)/lib/libbtrfs.a btrfstune.o + gcc $(CFLAGS) -o btrfstune btrfstune.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +btrfs-map-logical: $(TOPDIR)/lib/libbtrfs.a btrfs-map-logical.o + gcc $(CFLAGS) -o btrfs-map-logical btrfs-map-logical.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +btrfs-image: $(TOPDIR)/lib/libbtrfs.a btrfs-image.o + gcc $(CFLAGS) -o btrfs-image btrfs-image.o $(TOPDIR)/lib/libbtrfs.a -lpthread -lz $(LDFLAGS) $(LIBS) + +dir-test: $(TOPDIR)/lib/libbtrfs.a dir-test.o + gcc $(CFLAGS) -o dir-test dir-test.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +quick-test: $(TOPDIR)/lib/libbtrfs.a quick-test.o + gcc $(CFLAGS) -o quick-test quick-test.o $(TOPDIR)/lib/libbtrfs.a $(LDFLAGS) $(LIBS) + +convert: $(TOPDIR)/lib/libbtrfs.a convert.o + gcc $(CFLAGS) -o btrfs-convert convert.o $(TOPDIR)/lib/libbtrfs.a -lext2fs $(LDFLAGS) $(LIBS) + +clean : + rm -f $(progs) cscope.out *.o .*.d btrfs-convert + rm -f version.h + +install: $(progs) install-man + $(INSTALL) -m755 -d $(DESTDIR)$(bindir) + $(INSTALL) $(progs) $(DESTDIR)$(bindir) + if [ -e btrfs-convert ]; then $(INSTALL) btrfs-convert $(DESTDIR)$(bindir); fi + +-include .*.d diff --git a/misc/bcp b/misc/bcp new file mode 100644 index 0000000..5729e91 --- /dev/null +++ b/misc/bcp @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# Copyright (C) 2007 Oracle. 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. +# +import sys, os, stat, fcntl +from optparse import OptionParser + +def copylink(srcname, dst, filename, statinfo, force_name): + dstname = os.path.join(dst, force_name or filename) + if not os.path.exists(dstname): + link_target = os.readlink(srcname) + os.symlink(link_target, dstname) + +def copydev(srcname, dst, filename, statinfo, force_name): + devbits = statinfo.st_mode & (stat.S_IFBLK | stat.S_IFCHR) + mode = stat.S_IMODE(statinfo.st_mode) | devbits + dstname = os.path.join(dst, force_name or filename) + if not os.path.exists(dstname): + os.mknod(dstname, mode, statinfo.st_rdev) + +def copyfile(srcname, dst, filename, statinfo, force_name): + written = 0 + dstname = os.path.join(dst, force_name or filename) + + st_mode = statinfo.st_mode + if stat.S_ISLNK(st_mode): + copylink(srcname, dst, part, statinfo, None) + return + elif stat.S_ISBLK(st_mode) or stat.S_ISCHR(st_mode): + copydev(srcname, dst, part, statinfo, None) + return + elif not stat.S_ISREG(st_mode): + return + + try: + os.unlink(dstname) + except: + pass + + if options.link: + os.link(srcname, dstname) + return + + dstf = file(dstname, 'w') + srcf = file(srcname, 'r') + + ret = 1 + + try: + if not options.copy: + ret = fcntl.ioctl(dstf.fileno(), 1074041865, srcf.fileno()) + except: + pass + + if ret != 0: + while True: + buf = srcf.read(256 * 1024) + if not buf: + break + written += len(buf) + dstf.write(buf) + + os.chmod(dstname, stat.S_IMODE(statinfo.st_mode)) + os.chown(dstname, statinfo.st_uid, statinfo.st_gid) + + +usage = "usage: %prog [options]" +parser = OptionParser(usage=usage) +parser.add_option("-l", "--link", help="Create hard links", default=False, + action="store_true") +parser.add_option("-c", "--copy", help="Copy file bytes (don't cow)", + default=False, action="store_true") + +(options,args) = parser.parse_args() + +if len(args) < 2: + sys.stderr.write("source or destination not specified\n") + sys.exit(1) + +if options.link and options.copy: + sys.stderr.write("Both -l and -c specified, using copy mode\n") + options.link = False + + +total_args = len(args) +src_args = total_args - 1 +orig_dst = args[-1] + +if src_args > 1: + if not os.path.exists(orig_dst): + os.makedirs(orig_dst) + if not os.path.isdir(orig_dst): + sys.stderr.write("Destination %s is not a directory\n" % orig_dst) + exit(1) + +for srci in xrange(0, src_args): + src = args[srci] + if os.path.isfile(src): + statinfo = os.lstat(src) + force_name = None + if src_args == 1: + if not os.path.isdir(orig_dst): + force_name = os.path.basename(orig_dst) + orig_dst = os.path.dirname(orig_dst) or '.' + copyfile(src, orig_dst, os.path.basename(src), statinfo, force_name) + continue + + if src_args > 1 or os.path.exists(orig_dst): + dst = os.path.join(orig_dst, os.path.basename(src)) + else: + dst = orig_dst + + if not os.path.exists(dst): + os.makedirs(dst) + statinfo = os.stat(src) + os.chmod(dst, stat.S_IMODE(statinfo.st_mode)) + os.chown(dst, statinfo.st_uid, statinfo.st_gid) + + iter = os.walk(src, topdown=True) + + for (dirpath, dirnames, filenames) in iter: + for x in dirnames: + srcname = os.path.join(dirpath, x) + statinfo = os.lstat(srcname) + + if srcname.startswith(src): + part = srcname[len(src) + 1:] + + if stat.S_ISLNK(statinfo.st_mode): + copylink(srcname, dst, part, statinfo, None) + continue + + dst_dir = os.path.join(dst, part) + if not os.path.exists(dst_dir): + os.makedirs(dst_dir) + + os.chmod(dst_dir, stat.S_IMODE(statinfo.st_mode)) + os.chown(dst_dir, statinfo.st_uid, statinfo.st_gid) + + for f in filenames: + srcname = os.path.join(dirpath, f) + if srcname.startswith(src): + part = srcname[len(src) + 1:] + + statinfo = os.lstat(srcname) + copyfile(srcname, dst, part, statinfo, None) + + diff --git a/misc/btrfs-image.c b/misc/btrfs-image.c new file mode 100644 index 0000000..f2bbcc8 --- /dev/null +++ b/misc/btrfs-image.c @@ -0,0 +1,886 @@ +/* + * Copyright (C) 2008 Oracle. 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "crc32c.h" +#include "ctree.h" +#include "disk-io.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" + + +#define HEADER_MAGIC 0xbd5c25e27295668bULL +#define MAX_PENDING_SIZE (256 * 1024) +#define BLOCK_SIZE 1024 +#define BLOCK_MASK (BLOCK_SIZE - 1) + +#define COMPRESS_NONE 0 +#define COMPRESS_ZLIB 1 + +struct meta_cluster_item { + __le64 bytenr; + __le32 size; +} __attribute__ ((__packed__)); + +struct meta_cluster_header { + __le64 magic; + __le64 bytenr; + __le32 nritems; + u8 compress; +} __attribute__ ((__packed__)); + +/* cluster header + index items + buffers */ +struct meta_cluster { + struct meta_cluster_header header; + struct meta_cluster_item items[]; +} __attribute__ ((__packed__)); + +#define ITEMS_PER_CLUSTER ((BLOCK_SIZE - sizeof(struct meta_cluster)) / \ + sizeof(struct meta_cluster_item)) + +struct async_work { + struct list_head list; + struct list_head ordered; + u64 start; + u64 size; + u8 *buffer; + size_t bufsize; +}; + +struct metadump_struct { + struct btrfs_root *root; + FILE *out; + + struct meta_cluster *cluster; + + pthread_t *threads; + size_t num_threads; + pthread_mutex_t mutex; + pthread_cond_t cond; + + struct list_head list; + struct list_head ordered; + size_t num_items; + size_t num_ready; + + u64 pending_start; + u64 pending_size; + + int compress_level; + int done; +}; + +struct mdrestore_struct { + FILE *in; + FILE *out; + + pthread_t *threads; + size_t num_threads; + pthread_mutex_t mutex; + pthread_cond_t cond; + + struct list_head list; + size_t num_items; + + int compress_method; + int done; +}; + +static void csum_block(u8 *buf, size_t len) +{ + char result[BTRFS_CRC32_SIZE]; + u32 crc = ~(u32)0; + crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len - BTRFS_CSUM_SIZE); + btrfs_csum_final(crc, result); + memcpy(buf, result, BTRFS_CRC32_SIZE); +} + +/* + * zero inline extents and csum items + */ +static void zero_items(u8 *dst, struct extent_buffer *src) +{ + struct btrfs_file_extent_item *fi; + struct btrfs_item *item; + struct btrfs_key key; + u32 nritems = btrfs_header_nritems(src); + size_t size; + unsigned long ptr; + int i, extent_type; + + for (i = 0; i < nritems; i++) { + item = btrfs_item_nr(src, i); + btrfs_item_key_to_cpu(src, &key, i); + if (key.type == BTRFS_CSUM_ITEM_KEY) { + size = btrfs_item_size_nr(src, i); + memset(dst + btrfs_leaf_data(src) + + btrfs_item_offset_nr(src, i), 0, size); + continue; + } + if (key.type != BTRFS_EXTENT_DATA_KEY) + continue; + + fi = btrfs_item_ptr(src, i, struct btrfs_file_extent_item); + extent_type = btrfs_file_extent_type(src, fi); + if (extent_type != BTRFS_FILE_EXTENT_INLINE) + continue; + + ptr = btrfs_file_extent_inline_start(fi); + size = btrfs_file_extent_inline_item_len(src, item); + memset(dst + ptr, 0, size); + } +} + +/* + * copy buffer and zero useless data in the buffer + */ +static void copy_buffer(u8 *dst, struct extent_buffer *src) +{ + int level; + size_t size; + u32 nritems; + + memcpy(dst, src->data, src->len); + if (src->start == BTRFS_SUPER_INFO_OFFSET) + return; + + level = btrfs_header_level(src); + nritems = btrfs_header_nritems(src); + + if (nritems == 0) { + size = sizeof(struct btrfs_header); + memset(dst + size, 0, src->len - size); + } else if (level == 0) { + size = btrfs_leaf_data(src) + + btrfs_item_offset_nr(src, nritems - 1) - + btrfs_item_nr_offset(nritems); + memset(dst + btrfs_item_nr_offset(nritems), 0, size); + zero_items(dst, src); + } else { + size = offsetof(struct btrfs_node, ptrs) + + sizeof(struct btrfs_key_ptr) * nritems; + memset(dst + size, 0, src->len - size); + } + csum_block(dst, src->len); +} + +static void *dump_worker(void *data) +{ + struct metadump_struct *md = (struct metadump_struct *)data; + struct async_work *async; + int ret; + + while (1) { + pthread_mutex_lock(&md->mutex); + while (list_empty(&md->list)) { + if (md->done) { + pthread_mutex_unlock(&md->mutex); + goto out; + } + pthread_cond_wait(&md->cond, &md->mutex); + } + async = list_entry(md->list.next, struct async_work, list); + list_del_init(&async->list); + pthread_mutex_unlock(&md->mutex); + + if (md->compress_level > 0) { + u8 *orig = async->buffer; + + async->bufsize = compressBound(async->size); + async->buffer = malloc(async->bufsize); + + ret = compress2(async->buffer, + (unsigned long *)&async->bufsize, + orig, async->size, md->compress_level); + BUG_ON(ret != Z_OK); + + free(orig); + } + + pthread_mutex_lock(&md->mutex); + md->num_ready++; + pthread_mutex_unlock(&md->mutex); + } +out: + pthread_exit(NULL); +} + +static void meta_cluster_init(struct metadump_struct *md, u64 start) +{ + struct meta_cluster_header *header; + + md->num_items = 0; + md->num_ready = 0; + header = &md->cluster->header; + header->magic = cpu_to_le64(HEADER_MAGIC); + header->bytenr = cpu_to_le64(start); + header->nritems = cpu_to_le32(0); + header->compress = md->compress_level > 0 ? + COMPRESS_ZLIB : COMPRESS_NONE; +} + +static int metadump_init(struct metadump_struct *md, struct btrfs_root *root, + FILE *out, int num_threads, int compress_level) +{ + int i, ret; + + memset(md, 0, sizeof(*md)); + pthread_cond_init(&md->cond, NULL); + pthread_mutex_init(&md->mutex, NULL); + INIT_LIST_HEAD(&md->list); + INIT_LIST_HEAD(&md->ordered); + md->root = root; + md->out = out; + md->pending_start = (u64)-1; + md->compress_level = compress_level; + md->cluster = calloc(1, BLOCK_SIZE); + if (!md->cluster) + return -ENOMEM; + + meta_cluster_init(md, 0); + if (!num_threads) + return 0; + + md->num_threads = num_threads; + md->threads = calloc(num_threads, sizeof(pthread_t)); + if (!md->threads) + return -ENOMEM; + for (i = 0; i < num_threads; i++) { + ret = pthread_create(md->threads + i, NULL, dump_worker, md); + if (ret) + break; + } + return ret; +} + +static void metadump_destroy(struct metadump_struct *md) +{ + int i; + pthread_mutex_lock(&md->mutex); + md->done = 1; + pthread_cond_broadcast(&md->cond); + pthread_mutex_unlock(&md->mutex); + + for (i = 0; i < md->num_threads; i++) + pthread_join(md->threads[i], NULL); + + pthread_cond_destroy(&md->cond); + pthread_mutex_destroy(&md->mutex); + free(md->threads); + free(md->cluster); +} + +static int write_zero(FILE *out, size_t size) +{ + static char zero[BLOCK_SIZE]; + return fwrite(zero, size, 1, out); +} + +static int write_buffers(struct metadump_struct *md, u64 *next) +{ + struct meta_cluster_header *header = &md->cluster->header; + struct meta_cluster_item *item; + struct async_work *async; + u64 bytenr = 0; + u32 nritems = 0; + int ret; + + if (list_empty(&md->ordered)) + goto out; + + /* wait until all buffers are compressed */ + while (md->num_items > md->num_ready) { + struct timespec ts = { + .tv_sec = 0, + .tv_nsec = 10000000, + }; + pthread_mutex_unlock(&md->mutex); + nanosleep(&ts, NULL); + pthread_mutex_lock(&md->mutex); + } + + /* setup and write index block */ + list_for_each_entry(async, &md->ordered, ordered) { + item = md->cluster->items + nritems; + item->bytenr = cpu_to_le64(async->start); + item->size = cpu_to_le32(async->bufsize); + nritems++; + } + header->nritems = cpu_to_le32(nritems); + + ret = fwrite(md->cluster, BLOCK_SIZE, 1, md->out); + BUG_ON(ret != 1); + + /* write buffers */ + bytenr += le64_to_cpu(header->bytenr) + BLOCK_SIZE; + while (!list_empty(&md->ordered)) { + async = list_entry(md->ordered.next, struct async_work, + ordered); + list_del_init(&async->ordered); + + bytenr += async->bufsize; + ret = fwrite(async->buffer, async->bufsize, 1, md->out); + BUG_ON(ret != 1); + + free(async->buffer); + free(async); + } + + /* zero unused space in the last block */ + if (bytenr & BLOCK_MASK) { + size_t size = BLOCK_SIZE - (bytenr & BLOCK_MASK); + + bytenr += size; + ret = write_zero(md->out, size); + BUG_ON(ret != 1); + } +out: + *next = bytenr; + return 0; +} + +static int flush_pending(struct metadump_struct *md, int done) +{ + struct async_work *async = NULL; + struct extent_buffer *eb; + u64 blocksize = md->root->nodesize; + u64 start; + u64 size; + size_t offset; + int ret; + + if (md->pending_size) { + async = calloc(1, sizeof(*async)); + if (!async) + return -ENOMEM; + + async->start = md->pending_start; + async->size = md->pending_size; + async->bufsize = async->size; + async->buffer = malloc(async->bufsize); + + offset = 0; + start = async->start; + size = async->size; + while (size > 0) { + eb = read_tree_block(md->root, start, blocksize, 0); + BUG_ON(!eb); + copy_buffer(async->buffer + offset, eb); + free_extent_buffer(eb); + start += blocksize; + offset += blocksize; + size -= blocksize; + } + + md->pending_start = (u64)-1; + md->pending_size = 0; + } else if (!done) { + return 0; + } + + pthread_mutex_lock(&md->mutex); + if (async) { + list_add_tail(&async->ordered, &md->ordered); + md->num_items++; + if (md->compress_level > 0) { + list_add_tail(&async->list, &md->list); + pthread_cond_signal(&md->cond); + } else { + md->num_ready++; + } + } + if (md->num_items >= ITEMS_PER_CLUSTER || done) { + ret = write_buffers(md, &start); + BUG_ON(ret); + meta_cluster_init(md, start); + } + pthread_mutex_unlock(&md->mutex); + return 0; +} + +static int add_metadata(u64 start, u64 size, struct metadump_struct *md) +{ + int ret; + if (md->pending_size + size > MAX_PENDING_SIZE || + md->pending_start + md->pending_size != start) { + ret = flush_pending(md, 0); + if (ret) + return ret; + md->pending_start = start; + } + readahead_tree_block(md->root, start, size, 0); + md->pending_size += size; + return 0; +} + +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 +static int is_tree_block(struct btrfs_root *extent_root, + struct btrfs_path *path, u64 bytenr) +{ + struct extent_buffer *leaf; + struct btrfs_key key; + u64 ref_objectid; + int ret; + + leaf = path->nodes[0]; + while (1) { + struct btrfs_extent_ref_v0 *ref_item; + path->slots[0]++; + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(extent_root, path); + BUG_ON(ret < 0); + if (ret > 0) + break; + leaf = path->nodes[0]; + } + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + if (key.objectid != bytenr) + break; + if (key.type != BTRFS_EXTENT_REF_V0_KEY) + continue; + ref_item = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_ref_v0); + ref_objectid = btrfs_ref_objectid_v0(leaf, ref_item); + if (ref_objectid < BTRFS_FIRST_FREE_OBJECTID) + return 1; + break; + } + return 0; +} +#endif + +static int create_metadump(const char *input, FILE *out, int num_threads, + int compress_level) +{ + struct btrfs_root *root; + struct btrfs_root *extent_root; + struct btrfs_path *path; + struct extent_buffer *leaf; + struct btrfs_extent_item *ei; + struct btrfs_key key; + struct metadump_struct metadump; + u64 bytenr; + u64 num_bytes; + int ret; + + root = open_ctree(input, 0, 0); + BUG_ON(root->nodesize != root->leafsize); + + ret = metadump_init(&metadump, root, out, num_threads, + compress_level); + BUG_ON(ret); + + ret = add_metadata(BTRFS_SUPER_INFO_OFFSET, 4096, &metadump); + BUG_ON(ret); + + extent_root = root->fs_info->extent_root; + path = btrfs_alloc_path(); + + bytenr = BTRFS_SUPER_INFO_OFFSET + 4096; + key.objectid = bytenr; + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); + BUG_ON(ret < 0); + + while (1) { + leaf = path->nodes[0]; + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(extent_root, path); + BUG_ON(ret < 0); + if (ret > 0) + break; + leaf = path->nodes[0]; + } + + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + if (key.objectid < bytenr || + key.type != BTRFS_EXTENT_ITEM_KEY) { + path->slots[0]++; + continue; + } + + bytenr = key.objectid; + num_bytes = key.offset; + + if (btrfs_item_size_nr(leaf, path->slots[0]) > sizeof(*ei)) { + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_item); + if (btrfs_extent_flags(leaf, ei) & + BTRFS_EXTENT_FLAG_TREE_BLOCK) { + ret = add_metadata(bytenr, num_bytes, + &metadump); + BUG_ON(ret); + } + } else { +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + if (is_tree_block(extent_root, path, bytenr)) { + ret = add_metadata(bytenr, num_bytes, + &metadump); + BUG_ON(ret); + } +#else + BUG_ON(1); +#endif + } + bytenr += num_bytes; + } + + ret = flush_pending(&metadump, 1); + BUG_ON(ret); + + metadump_destroy(&metadump); + + btrfs_free_path(path); + ret = close_ctree(root); + return 0; +} + +static void update_super(u8 *buffer) +{ + struct btrfs_super_block *super = (struct btrfs_super_block *)buffer; + struct btrfs_chunk *chunk; + struct btrfs_disk_key *key; + u32 sectorsize = btrfs_super_sectorsize(super); + u64 flags = btrfs_super_flags(super); + + flags |= BTRFS_SUPER_FLAG_METADUMP; + btrfs_set_super_flags(super, flags); + + key = (struct btrfs_disk_key *)(super->sys_chunk_array); + chunk = (struct btrfs_chunk *)(super->sys_chunk_array + + sizeof(struct btrfs_disk_key)); + + btrfs_set_disk_key_objectid(key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_disk_key_type(key, BTRFS_CHUNK_ITEM_KEY); + btrfs_set_disk_key_offset(key, 0); + + btrfs_set_stack_chunk_length(chunk, (u64)-1); + btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_stack_chunk_stripe_len(chunk, 64 * 1024); + btrfs_set_stack_chunk_type(chunk, BTRFS_BLOCK_GROUP_SYSTEM); + btrfs_set_stack_chunk_io_align(chunk, sectorsize); + btrfs_set_stack_chunk_io_width(chunk, sectorsize); + btrfs_set_stack_chunk_sector_size(chunk, sectorsize); + btrfs_set_stack_chunk_num_stripes(chunk, 1); + btrfs_set_stack_chunk_sub_stripes(chunk, 0); + chunk->stripe.devid = super->dev_item.devid; + chunk->stripe.offset = cpu_to_le64(0); + memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid, BTRFS_UUID_SIZE); + btrfs_set_super_sys_array_size(super, sizeof(*key) + sizeof(*chunk)); + csum_block(buffer, 4096); +} + +static void *restore_worker(void *data) +{ + struct mdrestore_struct *mdres = (struct mdrestore_struct *)data; + struct async_work *async; + size_t size; + u8 *buffer; + u8 *outbuf; + int outfd; + int ret; + + outfd = fileno(mdres->out); + buffer = malloc(MAX_PENDING_SIZE * 2); + BUG_ON(!buffer); + + while (1) { + pthread_mutex_lock(&mdres->mutex); + while (list_empty(&mdres->list)) { + if (mdres->done) { + pthread_mutex_unlock(&mdres->mutex); + goto out; + } + pthread_cond_wait(&mdres->cond, &mdres->mutex); + } + async = list_entry(mdres->list.next, struct async_work, list); + list_del_init(&async->list); + pthread_mutex_unlock(&mdres->mutex); + + if (mdres->compress_method == COMPRESS_ZLIB) { + size = MAX_PENDING_SIZE * 2; + ret = uncompress(buffer, (unsigned long *)&size, + async->buffer, async->bufsize); + BUG_ON(ret != Z_OK); + outbuf = buffer; + } else { + outbuf = async->buffer; + size = async->bufsize; + } + + if (async->start == BTRFS_SUPER_INFO_OFFSET) + update_super(outbuf); + + ret = pwrite64(outfd, outbuf, size, async->start); + BUG_ON(ret != size); + + pthread_mutex_lock(&mdres->mutex); + mdres->num_items--; + pthread_mutex_unlock(&mdres->mutex); + + free(async->buffer); + free(async); + } +out: + free(buffer); + pthread_exit(NULL); +} + +static int mdresotre_init(struct mdrestore_struct *mdres, + FILE *in, FILE *out, int num_threads) +{ + int i, ret = 0; + + memset(mdres, 0, sizeof(*mdres)); + pthread_cond_init(&mdres->cond, NULL); + pthread_mutex_init(&mdres->mutex, NULL); + INIT_LIST_HEAD(&mdres->list); + mdres->in = in; + mdres->out = out; + + if (!num_threads) + return 0; + + mdres->num_threads = num_threads; + mdres->threads = calloc(num_threads, sizeof(pthread_t)); + if (!mdres->threads) + return -ENOMEM; + for (i = 0; i < num_threads; i++) { + ret = pthread_create(mdres->threads + i, NULL, restore_worker, + mdres); + if (ret) + break; + } + return ret; +} + +static void mdresotre_destroy(struct mdrestore_struct *mdres) +{ + int i; + pthread_mutex_lock(&mdres->mutex); + mdres->done = 1; + pthread_cond_broadcast(&mdres->cond); + pthread_mutex_unlock(&mdres->mutex); + + for (i = 0; i < mdres->num_threads; i++) + pthread_join(mdres->threads[i], NULL); + + pthread_cond_destroy(&mdres->cond); + pthread_mutex_destroy(&mdres->mutex); + free(mdres->threads); +} + +static int add_cluster(struct meta_cluster *cluster, + struct mdrestore_struct *mdres, u64 *next) +{ + struct meta_cluster_item *item; + struct meta_cluster_header *header = &cluster->header; + struct async_work *async; + u64 bytenr; + u32 i, nritems; + int ret; + + BUG_ON(mdres->num_items); + mdres->compress_method = header->compress; + + bytenr = le64_to_cpu(header->bytenr) + BLOCK_SIZE; + nritems = le32_to_cpu(header->nritems); + for (i = 0; i < nritems; i++) { + item = &cluster->items[i]; + async = calloc(1, sizeof(*async)); + async->start = le64_to_cpu(item->bytenr); + async->bufsize = le32_to_cpu(item->size); + async->buffer = malloc(async->bufsize); + ret = fread(async->buffer, async->bufsize, 1, mdres->in); + BUG_ON(ret != 1); + bytenr += async->bufsize; + + pthread_mutex_lock(&mdres->mutex); + list_add_tail(&async->list, &mdres->list); + mdres->num_items++; + pthread_cond_signal(&mdres->cond); + pthread_mutex_unlock(&mdres->mutex); + } + if (bytenr & BLOCK_MASK) { + char buffer[BLOCK_MASK]; + size_t size = BLOCK_SIZE - (bytenr & BLOCK_MASK); + + bytenr += size; + ret = fread(buffer, size, 1, mdres->in); + BUG_ON(ret != 1); + } + *next = bytenr; + return 0; +} + +static int wait_for_worker(struct mdrestore_struct *mdres) +{ + pthread_mutex_lock(&mdres->mutex); + while (mdres->num_items > 0) { + struct timespec ts = { + .tv_sec = 0, + .tv_nsec = 10000000, + }; + pthread_mutex_unlock(&mdres->mutex); + nanosleep(&ts, NULL); + pthread_mutex_lock(&mdres->mutex); + } + pthread_mutex_unlock(&mdres->mutex); + return 0; +} + +static int restore_metadump(const char *input, FILE *out, int num_threads) +{ + struct meta_cluster *cluster; + struct meta_cluster_header *header; + struct mdrestore_struct mdrestore; + u64 bytenr = 0; + FILE *in; + int ret; + + if (!strcmp(input, "-")) { + in = stdin; + } else { + in = fopen(input, "r"); + if (!in) { + perror("unable to open metadump image"); + return 1; + } + } + + cluster = malloc(BLOCK_SIZE); + BUG_ON(!cluster); + + ret = mdresotre_init(&mdrestore, in, out, num_threads); + BUG_ON(ret); + + while (1) { + ret = fread(cluster, BLOCK_SIZE, 1, in); + if (!ret) + break; + + header = &cluster->header; + if (le64_to_cpu(header->magic) != HEADER_MAGIC || + le64_to_cpu(header->bytenr) != bytenr) { + fprintf(stderr, "bad header in metadump image\n"); + return 1; + } + ret = add_cluster(cluster, &mdrestore, &bytenr); + BUG_ON(ret); + + wait_for_worker(&mdrestore); + } + + mdresotre_destroy(&mdrestore); + free(cluster); + if (in != stdin) + fclose(in); + return ret; +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfs-image [options] source target\n"); + fprintf(stderr, "\t-r \trestore metadump image\n"); + fprintf(stderr, "\t-c value\tcompression level (0 ~ 9)\n"); + fprintf(stderr, "\t-t value\tnumber of threads (1 ~ 32)\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + char *source; + char *target; + int num_threads = 0; + int compress_level = 0; + int create = 1; + int ret; + FILE *out; + + while (1) { + int c = getopt(argc, argv, "rc:t:"); + if (c < 0) + break; + switch (c) { + case 'r': + create = 0; + break; + case 't': + num_threads = atoi(optarg); + if (num_threads <= 0 || num_threads > 32) + print_usage(); + break; + case 'c': + compress_level = atoi(optarg); + if (compress_level < 0 || compress_level > 9) + print_usage(); + break; + default: + print_usage(); + } + } + + argc = argc - optind; + if (argc != 2) + print_usage(); + source = argv[optind]; + target = argv[optind + 1]; + + if (create && !strcmp(target, "-")) { + out = stdout; + } else { + out = fopen(target, "w+"); + if (!out) { + perror("unable to create target file"); + exit(1); + } + } + + if (num_threads == 0 && compress_level > 0) { + num_threads = sysconf(_SC_NPROCESSORS_ONLN); + if (num_threads <= 0) + num_threads = 1; + } + + if (create) + ret = create_metadump(source, out, num_threads, + compress_level); + else + ret = restore_metadump(source, out, 1); + + if (out == stdout) + fflush(out); + else + fclose(out); + + exit(ret); +} diff --git a/misc/btrfs-map-logical.c b/misc/btrfs-map-logical.c new file mode 100644 index 0000000..a109c6a --- /dev/null +++ b/misc/btrfs-map-logical.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2009 Oracle. 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 +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "volumes.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "list.h" +#include "version.h" + +/* we write the mirror info to stdout unless they are dumping the data + * to stdout + * */ +static FILE *info_file; + +struct extent_buffer *debug_read_block(struct btrfs_root *root, u64 bytenr, + u32 blocksize, int copy) +{ + int ret; + int dev_nr; + struct extent_buffer *eb; + u64 length; + struct btrfs_multi_bio *multi = NULL; + struct btrfs_device *device; + int num_copies; + int mirror_num = 1; + + eb = btrfs_find_create_tree_block(root, bytenr, blocksize); + if (!eb) + return NULL; + + dev_nr = 0; + length = blocksize; + while (1) { + ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, + eb->start, &length, &multi, mirror_num); + BUG_ON(ret); + device = multi->stripes[0].dev; + eb->fd = device->fd; + device->total_ios++; + eb->dev_bytenr = multi->stripes[0].physical; + + fprintf(info_file, "mirror %d logical %Lu physical %Lu " + "device %s\n", mirror_num, bytenr, eb->dev_bytenr, + device->name); + kfree(multi); + + if (!copy || mirror_num == copy) + ret = read_extent_from_disk(eb); + + num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, + eb->start, eb->len); + if (num_copies == 1) + break; + + mirror_num++; + if (mirror_num > num_copies) + break; + } + return eb; +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); + fprintf(stderr, "\t-l Logical extent to map\n"); + fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); + fprintf(stderr, "\t-o Output file to hold the extent\n"); + fprintf(stderr, "\t-s Number of bytes to read\n"); + exit(1); +} + +static struct option long_options[] = { + /* { "byte-count", 1, NULL, 'b' }, */ + { "logical", 1, NULL, 'l' }, + { "copy", 1, NULL, 'c' }, + { "output", 1, NULL, 'c' }, + { "bytes", 1, NULL, 'b' }, + { 0, 0, 0, 0} +}; + +int main(int ac, char **av) +{ + struct cache_tree root_cache; + struct btrfs_root *root; + struct extent_buffer *eb; + char *dev; + char *output_file = NULL; + u64 logical = 0; + int ret = 0; + int option_index = 0; + int copy = 0; + u64 bytes = 0; + int out_fd = 0; + int err; + + while(1) { + int c; + c = getopt_long(ac, av, "l:c:o:b:", long_options, + &option_index); + if (c < 0) + break; + switch(c) { + case 'l': + logical = atoll(optarg); + if (logical == 0) { + fprintf(stderr, + "invalid extent number\n"); + print_usage(); + } + break; + case 'c': + copy = atoi(optarg); + if (copy == 0) { + fprintf(stderr, + "invalid copy number\n"); + print_usage(); + } + break; + case 'b': + bytes = atoll(optarg); + if (bytes == 0) { + fprintf(stderr, + "invalid byte count\n"); + print_usage(); + } + break; + case 'o': + output_file = strdup(optarg); + break; + default: + print_usage(); + } + } + ac = ac - optind; + if (ac == 0) + print_usage(); + if (logical == 0) + print_usage(); + if (copy < 0) + print_usage(); + + dev = av[optind]; + + radix_tree_init(); + cache_tree_init(&root_cache); + + root = open_ctree(dev, 0, 0); + if (!root) { + fprintf(stderr, "Open ctree failed\n"); + exit(1); + } + + if (output_file) { + if (strcmp(output_file, "-") == 0) { + out_fd = 1; + info_file = stderr; + } else { + out_fd = open(output_file, O_RDWR | O_CREAT, 0600); + if (out_fd < 0) + goto close; + err = ftruncate(out_fd, 0); + if (err) { + close(out_fd); + goto close; + } + info_file = stdout; + } + } + + if (bytes == 0) + bytes = root->sectorsize; + + bytes = (bytes + root->sectorsize - 1) / root->sectorsize; + bytes *= root->sectorsize; + + while (bytes > 0) { + eb = debug_read_block(root, logical, root->sectorsize, copy); + if (eb && output_file) { + err = write(out_fd, eb->data, eb->len); + if (err < 0 || err != eb->len) { + fprintf(stderr, "output file write failed\n"); + goto out_close_fd; + } + } + free_extent_buffer(eb); + logical += root->sectorsize; + bytes -= root->sectorsize; + } + +out_close_fd: + if (output_file && out_fd != 1) + close(out_fd); +close: + close_ctree(root); + return ret; +} diff --git a/misc/btrfs-show.c b/misc/btrfs-show.c new file mode 100644 index 0000000..c49626c --- /dev/null +++ b/misc/btrfs-show.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2007 Oracle. 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 _GNU_SOURCE +#ifndef __CHECKER__ +#include +#include +#include "ioctl.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "transaction.h" +#include "utils.h" +#include "volumes.h" +#include "version.h" + +static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) +{ + struct list_head *cur; + struct btrfs_device *device; + + list_for_each(cur, &fs_devices->devices) { + device = list_entry(cur, struct btrfs_device, dev_list); + if ((device->label && strcmp(device->label, search) == 0) || + strcmp(device->name, search) == 0) + return 1; + } + return 0; +} + +static void print_one_uuid(struct btrfs_fs_devices *fs_devices) +{ + char uuidbuf[37]; + struct list_head *cur; + struct btrfs_device *device; + char *super_bytes_used; + u64 devs_found = 0; + u64 total; + + uuid_unparse(fs_devices->fsid, uuidbuf); + device = list_entry(fs_devices->devices.next, struct btrfs_device, + dev_list); + if (device->label && device->label[0]) + printf("Label: %s ", device->label); + else + printf("Label: none "); + + super_bytes_used = pretty_sizes(device->super_bytes_used); + + total = device->total_devs; + printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, + (unsigned long long)total, super_bytes_used); + + free(super_bytes_used); + + list_for_each(cur, &fs_devices->devices) { + char *total_bytes; + char *bytes_used; + device = list_entry(cur, struct btrfs_device, dev_list); + total_bytes = pretty_sizes(device->total_bytes); + bytes_used = pretty_sizes(device->bytes_used); + printf("\tdevid %4llu size %s used %s path %s\n", + (unsigned long long)device->devid, + total_bytes, bytes_used, device->name); + free(total_bytes); + free(bytes_used); + devs_found++; + } + if (devs_found < total) { + printf("\t*** Some devices missing\n"); + } + printf("\n"); +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfs-show [search label or device]\n"); + fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); + exit(1); +} + +static struct option long_options[] = { + /* { "byte-count", 1, NULL, 'b' }, */ + { 0, 0, 0, 0} +}; + +int main(int ac, char **av) +{ + struct list_head *all_uuids; + struct btrfs_fs_devices *fs_devices; + struct list_head *cur_uuid; + char *search = NULL; + int ret; + int option_index = 0; + + while(1) { + int c; + c = getopt_long(ac, av, "", long_options, + &option_index); + if (c < 0) + break; + switch(c) { + default: + print_usage(); + } + } + ac = ac - optind; + if (ac != 0) { + search = av[optind]; + } + + ret = btrfs_scan_one_dir("/dev", 0); + if (ret) + fprintf(stderr, "error %d while scanning\n", ret); + + all_uuids = btrfs_scanned_uuids(); + list_for_each(cur_uuid, all_uuids) { + fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices, + list); + if (search && uuid_search(fs_devices, search) == 0) + continue; + print_one_uuid(fs_devices); + } + printf("%s\n", BTRFS_BUILD_VERSION); + return 0; +} + diff --git a/misc/btrfs-vol.c b/misc/btrfs-vol.c new file mode 100644 index 0000000..8069778 --- /dev/null +++ b/misc/btrfs-vol.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2007 Oracle. 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 _GNU_SOURCE +#ifndef __CHECKER__ +#include +#include +#include "ioctl.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "transaction.h" +#include "utils.h" +#include "volumes.h" + +#ifdef __CHECKER__ +#define BLKGETSIZE64 0 +#define BTRFS_IOC_SNAP_CREATE 0 +#define BTRFS_IOC_ADD_DEV 0 +#define BTRFS_IOC_RM_DEV 0 +#define BTRFS_VOL_NAME_MAX 255 +struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; }; +static inline int ioctl(int fd, int define, void *arg) { return 0; } +#endif + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfs-vol [options] mount_point\n"); + fprintf(stderr, "\t-a device add one device\n"); + fprintf(stderr, "\t-b balance chunks across all devices\n"); + fprintf(stderr, "\t-r device remove one device\n"); + exit(1); +} + +static struct option long_options[] = { + /* { "byte-count", 1, NULL, 'b' }, */ + { "add", 1, NULL, 'a' }, + { "balance", 0, NULL, 'b' }, + { "remove", 1, NULL, 'r' }, + { 0, 0, 0, 0} +}; + +int main(int ac, char **av) +{ + struct stat st; + char *device = NULL; + char *mnt = NULL; + int ret; + int option_index = 0; + int cmd = 0; + int fd; + int devfd = 0; + DIR *dirstream; + struct btrfs_ioctl_vol_args args; + u64 dev_block_count = 0; + + while(1) { + int c; + c = getopt_long(ac, av, "a:br:", long_options, + &option_index); + if (c < 0) + break; + switch(c) { + case 'a': + device = strdup(optarg); + cmd = BTRFS_IOC_ADD_DEV; + break; + case 'b': + cmd = BTRFS_IOC_BALANCE; + break; + case 'r': + device = strdup(optarg); + cmd = BTRFS_IOC_RM_DEV; + break; + default: + print_usage(); + } + } + ac = ac - optind; + if (ac == 0) + print_usage(); + mnt = av[optind]; + + if (device && strcmp(device, "missing") == 0 && + cmd == BTRFS_IOC_RM_DEV) { + fprintf(stderr, "removing missing devices from %s\n", mnt); + } else if (device) { + devfd = open(device, O_RDWR); + if (!devfd) { + fprintf(stderr, "Unable to open device %s\n", device); + } + ret = fstat(devfd, &st); + if (ret) { + fprintf(stderr, "Unable to stat %s\n", device); + exit(1); + } + if (!S_ISBLK(st.st_mode)) { + fprintf(stderr, "%s is not a block device\n", device); + exit(1); + } + } + dirstream = opendir(mnt); + if (!dirstream) { + fprintf(stderr, "Unable to open directory %s\n", mnt); + exit(1); + } + if (cmd == BTRFS_IOC_ADD_DEV) { + ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count); + if (ret) { + fprintf(stderr, "Unable to init %s\n", device); + exit(1); + } + } + fd = dirfd(dirstream); + if (device) + strcpy(args.name, device); + else + args.name[0] = '\0'; + + ret = ioctl(fd, cmd, &args); + printf("ioctl returns %d\n", ret); + return 0; +} + diff --git a/misc/btrfsck.c b/misc/btrfsck.c new file mode 100644 index 0000000..73f1836 --- /dev/null +++ b/misc/btrfsck.c @@ -0,0 +1,2867 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "list.h" +#include "version.h" + +static u64 bytes_used = 0; +static u64 total_csum_bytes = 0; +static u64 total_btree_bytes = 0; +static u64 total_fs_tree_bytes = 0; +static u64 btree_space_waste = 0; +static u64 data_bytes_allocated = 0; +static u64 data_bytes_referenced = 0; +static int found_old_backref = 0; + +struct extent_backref { + struct list_head list; + unsigned int is_data:1; + unsigned int found_extent_tree:1; + unsigned int full_backref:1; + unsigned int found_ref:1; +}; + +struct data_backref { + struct extent_backref node; + union { + u64 parent; + u64 root; + }; + u64 owner; + u64 offset; + u32 num_refs; + u32 found_ref; +}; + +struct tree_backref { + struct extent_backref node; + union { + u64 parent; + u64 root; + }; +}; + +struct extent_record { + struct list_head backrefs; + struct cache_extent cache; + struct btrfs_disk_key parent_key; + u64 start; + u64 nr; + u64 refs; + u64 extent_item_refs; + unsigned int content_checked:1; + unsigned int owner_ref_checked:1; + unsigned int is_root:1; +}; + +struct inode_backref { + struct list_head list; + unsigned int found_dir_item:1; + unsigned int found_dir_index:1; + unsigned int found_inode_ref:1; + unsigned int filetype:8; + int errors; + u64 dir; + u64 index; + u16 namelen; + char name[0]; +}; + +#define REF_ERR_NO_DIR_ITEM (1 << 0) +#define REF_ERR_NO_DIR_INDEX (1 << 1) +#define REF_ERR_NO_INODE_REF (1 << 2) +#define REF_ERR_DUP_DIR_ITEM (1 << 3) +#define REF_ERR_DUP_DIR_INDEX (1 << 4) +#define REF_ERR_DUP_INODE_REF (1 << 5) +#define REF_ERR_INDEX_UNMATCH (1 << 6) +#define REF_ERR_FILETYPE_UNMATCH (1 << 7) +#define REF_ERR_NAME_TOO_LONG (1 << 8) // 100 +#define REF_ERR_NO_ROOT_REF (1 << 9) +#define REF_ERR_NO_ROOT_BACKREF (1 << 10) +#define REF_ERR_DUP_ROOT_REF (1 << 11) +#define REF_ERR_DUP_ROOT_BACKREF (1 << 12) + +struct inode_record { + struct list_head backrefs; + unsigned int checked:1; + unsigned int merging:1; + unsigned int found_inode_item:1; + unsigned int found_dir_item:1; + unsigned int found_file_extent:1; + unsigned int found_csum_item:1; + unsigned int some_csum_missing:1; + unsigned int nodatasum:1; + int errors; + + u64 ino; + u32 nlink; + u32 imode; + u64 isize; + u64 nbytes; + + u32 found_link; + u64 found_size; + u64 extent_start; + u64 extent_end; + u64 first_extent_gap; + + u32 refs; +}; + +#define I_ERR_NO_INODE_ITEM (1 << 0) +#define I_ERR_NO_ORPHAN_ITEM (1 << 1) +#define I_ERR_DUP_INODE_ITEM (1 << 2) +#define I_ERR_DUP_DIR_INDEX (1 << 3) +#define I_ERR_ODD_DIR_ITEM (1 << 4) +#define I_ERR_ODD_FILE_EXTENT (1 << 5) +#define I_ERR_BAD_FILE_EXTENT (1 << 6) +#define I_ERR_FILE_EXTENT_OVERLAP (1 << 7) +#define I_ERR_FILE_EXTENT_DISCOUNT (1 << 8) // 100 +#define I_ERR_DIR_ISIZE_WRONG (1 << 9) +#define I_ERR_FILE_NBYTES_WRONG (1 << 10) // 400 +#define I_ERR_ODD_CSUM_ITEM (1 << 11) +#define I_ERR_SOME_CSUM_MISSING (1 << 12) +#define I_ERR_LINK_COUNT_WRONG (1 << 13) + +struct root_backref { + struct list_head list; + unsigned int found_dir_item:1; + unsigned int found_dir_index:1; + unsigned int found_back_ref:1; + unsigned int found_forward_ref:1; + unsigned int reachable:1; + int errors; + u64 ref_root; + u64 dir; + u64 index; + u16 namelen; + char name[0]; +}; + +struct root_record { + struct list_head backrefs; + struct cache_extent cache; + unsigned int found_root_item:1; + u64 objectid; + u32 found_ref; +}; + +struct ptr_node { + struct cache_extent cache; + void *data; +}; + +struct shared_node { + struct cache_extent cache; + struct cache_tree root_cache; + struct cache_tree inode_cache; + struct inode_record *current; + u32 refs; +}; + +struct block_info { + u64 start; + u32 size; +}; + +struct walk_control { + struct cache_tree shared; + struct shared_node *nodes[BTRFS_MAX_LEVEL]; + int active_node; + int root_level; +}; + +static u8 imode_to_type(u32 imode) +{ +#define S_SHIFT 12 + static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = { + [S_IFREG >> S_SHIFT] = BTRFS_FT_REG_FILE, + [S_IFDIR >> S_SHIFT] = BTRFS_FT_DIR, + [S_IFCHR >> S_SHIFT] = BTRFS_FT_CHRDEV, + [S_IFBLK >> S_SHIFT] = BTRFS_FT_BLKDEV, + [S_IFIFO >> S_SHIFT] = BTRFS_FT_FIFO, + [S_IFSOCK >> S_SHIFT] = BTRFS_FT_SOCK, + [S_IFLNK >> S_SHIFT] = BTRFS_FT_SYMLINK, + }; + + return btrfs_type_by_mode[(imode & S_IFMT) >> S_SHIFT]; +#undef S_SHIFT +} + +static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) +{ + struct inode_record *rec; + struct inode_backref *backref; + struct inode_backref *orig; + size_t size; + + rec = malloc(sizeof(*rec)); + memcpy(rec, orig_rec, sizeof(*rec)); + rec->refs = 1; + INIT_LIST_HEAD(&rec->backrefs); + + list_for_each_entry(orig, &orig_rec->backrefs, list) { + size = sizeof(*orig) + orig->namelen + 1; + backref = malloc(size); + memcpy(backref, orig, size); + list_add_tail(&backref->list, &rec->backrefs); + } + return rec; +} + +static struct inode_record *get_inode_rec(struct cache_tree *inode_cache, + u64 ino, int mod) +{ + struct ptr_node *node; + struct cache_extent *cache; + struct inode_record *rec = NULL; + int ret; + + cache = find_cache_extent(inode_cache, ino, 1); + if (cache) { + node = container_of(cache, struct ptr_node, cache); + rec = node->data; + if (mod && rec->refs > 1) { + node->data = clone_inode_rec(rec); + rec->refs--; + rec = node->data; + } + } else if (mod) { + rec = calloc(1, sizeof(*rec)); + rec->ino = ino; + rec->extent_start = (u64)-1; + rec->first_extent_gap = (u64)-1; + rec->refs = 1; + INIT_LIST_HEAD(&rec->backrefs); + + node = malloc(sizeof(*node)); + node->cache.start = ino; + node->cache.size = 1; + node->data = rec; + + ret = insert_existing_cache_extent(inode_cache, &node->cache); + BUG_ON(ret); + } + return rec; +} + +static void free_inode_rec(struct inode_record *rec) +{ + struct inode_backref *backref; + + if (--rec->refs > 0) + return; + + while (!list_empty(&rec->backrefs)) { + backref = list_entry(rec->backrefs.next, + struct inode_backref, list); + list_del(&backref->list); + free(backref); + } + free(rec); +} + +static int can_free_inode_rec(struct inode_record *rec) +{ + if (!rec->errors && rec->checked && rec->found_inode_item && + rec->nlink == rec->found_link && list_empty(&rec->backrefs)) + return 1; + return 0; +} + +static void maybe_free_inode_rec(struct cache_tree *inode_cache, + struct inode_record *rec) +{ + struct cache_extent *cache; + struct inode_backref *tmp, *backref; + struct ptr_node *node; + unsigned char filetype; + + if (!rec->found_inode_item) + return; + + filetype = imode_to_type(rec->imode); + list_for_each_entry_safe(backref, tmp, &rec->backrefs, list) { + if (backref->found_dir_item && backref->found_dir_index) { + if (backref->filetype != filetype) + backref->errors |= REF_ERR_FILETYPE_UNMATCH; + if (!backref->errors && backref->found_inode_ref) { + list_del(&backref->list); + free(backref); + } + } + } + + if (!rec->checked || rec->merging) + return; + + if (S_ISDIR(rec->imode)) { + if (rec->found_size != rec->isize) + rec->errors |= I_ERR_DIR_ISIZE_WRONG; + if (rec->found_file_extent) + rec->errors |= I_ERR_ODD_FILE_EXTENT; + } else if (S_ISREG(rec->imode) || S_ISLNK(rec->imode)) { + if (rec->found_dir_item) + rec->errors |= I_ERR_ODD_DIR_ITEM; + if (rec->found_size != rec->nbytes) + rec->errors |= I_ERR_FILE_NBYTES_WRONG; + if (rec->extent_start == (u64)-1 || rec->extent_start > 0) + rec->first_extent_gap = 0; + if (rec->nlink > 0 && (rec->extent_end < rec->isize || + rec->first_extent_gap < rec->isize)) + rec->errors |= I_ERR_FILE_EXTENT_DISCOUNT; + } + + if (S_ISREG(rec->imode) || S_ISLNK(rec->imode)) { + if (rec->found_csum_item && rec->nodatasum) + rec->errors |= I_ERR_ODD_CSUM_ITEM; + if (rec->some_csum_missing && !rec->nodatasum) + rec->errors |= I_ERR_SOME_CSUM_MISSING; + } + + BUG_ON(rec->refs != 1); + if (can_free_inode_rec(rec)) { + cache = find_cache_extent(inode_cache, rec->ino, 1); + node = container_of(cache, struct ptr_node, cache); + BUG_ON(node->data != rec); + remove_cache_extent(inode_cache, &node->cache); + free(node); + free_inode_rec(rec); + } +} + +static int check_orphan_item(struct btrfs_root *root, u64 ino) +{ + struct btrfs_path path; + struct btrfs_key key; + int ret; + + key.objectid = BTRFS_ORPHAN_OBJECTID; + key.type = BTRFS_ORPHAN_ITEM_KEY; + key.offset = ino; + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + btrfs_release_path(root, &path); + if (ret > 0) + ret = -ENOENT; + return ret; +} + +static int process_inode_item(struct extent_buffer *eb, + int slot, struct btrfs_key *key, + struct shared_node *active_node) +{ + struct inode_record *rec; + struct btrfs_inode_item *item; + + rec = active_node->current; + BUG_ON(rec->ino != key->objectid || rec->refs > 1); + if (rec->found_inode_item) { + rec->errors |= I_ERR_DUP_INODE_ITEM; + return 1; + } + item = btrfs_item_ptr(eb, slot, struct btrfs_inode_item); + rec->nlink = btrfs_inode_nlink(eb, item); + rec->isize = btrfs_inode_size(eb, item); + rec->nbytes = btrfs_inode_nbytes(eb, item); + rec->imode = btrfs_inode_mode(eb, item); + if (btrfs_inode_flags(eb, item) & BTRFS_INODE_NODATASUM) + rec->nodatasum = 1; + rec->found_inode_item = 1; + if (rec->nlink == 0) + rec->errors |= I_ERR_NO_ORPHAN_ITEM; + maybe_free_inode_rec(&active_node->inode_cache, rec); + return 0; +} + +static struct inode_backref *get_inode_backref(struct inode_record *rec, + const char *name, + int namelen, u64 dir) +{ + struct inode_backref *backref; + + list_for_each_entry(backref, &rec->backrefs, list) { + if (backref->dir != dir || backref->namelen != namelen) + continue; + if (memcmp(name, backref->name, namelen)) + continue; + return backref; + } + + backref = malloc(sizeof(*backref) + namelen + 1); + memset(backref, 0, sizeof(*backref)); + backref->dir = dir; + backref->namelen = namelen; + memcpy(backref->name, name, namelen); + backref->name[namelen] = '\0'; + list_add_tail(&backref->list, &rec->backrefs); + rec->found_link++; + return backref; +} + +static int add_inode_backref(struct cache_tree *inode_cache, + u64 ino, u64 dir, u64 index, + const char *name, int namelen, + int filetype, int itemtype, int errors) +{ + struct inode_record *rec; + struct inode_backref *backref; + + rec = get_inode_rec(inode_cache, ino, 1); + backref = get_inode_backref(rec, name, namelen, dir); + if (errors) + backref->errors |= errors; + if (itemtype == BTRFS_DIR_INDEX_KEY) { + if (backref->found_dir_index) + backref->errors |= REF_ERR_DUP_DIR_INDEX; + if (backref->found_inode_ref && backref->index != index) + backref->errors |= REF_ERR_INDEX_UNMATCH; + if (backref->found_dir_item && backref->filetype != filetype) + backref->errors |= REF_ERR_FILETYPE_UNMATCH; + + backref->index = index; + backref->filetype = filetype; + backref->found_dir_index = 1; + } else if (itemtype == BTRFS_DIR_ITEM_KEY) { + if (backref->found_dir_item) + backref->errors |= REF_ERR_DUP_DIR_ITEM; + if (backref->found_dir_index && backref->filetype != filetype) + backref->errors |= REF_ERR_FILETYPE_UNMATCH; + + backref->filetype = filetype; + backref->found_dir_item = 1; + } else if (itemtype == BTRFS_INODE_REF_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->index = index; + backref->found_inode_ref = 1; + } else { + BUG_ON(1); + } + + maybe_free_inode_rec(inode_cache, rec); + return 0; +} + +static int merge_inode_recs(struct inode_record *src, struct inode_record *dst, + struct cache_tree *dst_cache) +{ + struct inode_backref *backref; + + dst->merging = 1; + list_for_each_entry(backref, &src->backrefs, list) { + if (backref->found_dir_index) { + add_inode_backref(dst_cache, dst->ino, backref->dir, + backref->index, backref->name, + backref->namelen, backref->filetype, + BTRFS_DIR_INDEX_KEY, backref->errors); + } + if (backref->found_dir_item) { + add_inode_backref(dst_cache, dst->ino, + backref->dir, 0, backref->name, + backref->namelen, backref->filetype, + BTRFS_DIR_ITEM_KEY, backref->errors); + } + if (backref->found_inode_ref) { + add_inode_backref(dst_cache, dst->ino, + backref->dir, backref->index, + backref->name, backref->namelen, 0, + BTRFS_INODE_REF_KEY, backref->errors); + } + } + + if (src->found_dir_item) + dst->found_dir_item = 1; + if (src->found_file_extent) + dst->found_file_extent = 1; + if (src->found_csum_item) + dst->found_csum_item = 1; + if (src->some_csum_missing) + dst->some_csum_missing = 1; + if (dst->first_extent_gap > src->first_extent_gap) + dst->first_extent_gap = src->first_extent_gap; + + dst->found_size += src->found_size; + if (src->extent_start != (u64)-1) { + if (dst->extent_start == (u64)-1) { + dst->extent_start = src->extent_start; + dst->extent_end = src->extent_end; + } else { + if (dst->extent_end > src->extent_start) + dst->errors |= I_ERR_FILE_EXTENT_OVERLAP; + else if (dst->extent_end < src->extent_start && + dst->extent_end < dst->first_extent_gap) + dst->first_extent_gap = dst->extent_end; + if (dst->extent_end < src->extent_end) + dst->extent_end = src->extent_end; + } + } + + dst->errors |= src->errors; + if (src->found_inode_item) { + if (!dst->found_inode_item) { + dst->nlink = src->nlink; + dst->isize = src->isize; + dst->nbytes = src->nbytes; + dst->imode = src->imode; + dst->nodatasum = src->nodatasum; + dst->found_inode_item = 1; + } else { + dst->errors |= I_ERR_DUP_INODE_ITEM; + } + } + dst->merging = 0; + + return 0; +} + +static int splice_shared_node(struct shared_node *src_node, + struct shared_node *dst_node) +{ + struct cache_extent *cache; + struct ptr_node *node, *ins; + struct cache_tree *src, *dst; + struct inode_record *rec, *conflict; + u64 current_ino = 0; + int splice = 0; + int ret; + + if (--src_node->refs == 0) + splice = 1; + if (src_node->current) + current_ino = src_node->current->ino; + + src = &src_node->root_cache; + dst = &dst_node->root_cache; +again: + cache = find_first_cache_extent(src, 0); + while (cache) { + node = container_of(cache, struct ptr_node, cache); + rec = node->data; + cache = next_cache_extent(cache); + + if (splice) { + remove_cache_extent(src, &node->cache); + ins = node; + } else { + ins = malloc(sizeof(*ins)); + ins->cache.start = node->cache.start; + ins->cache.size = node->cache.size; + ins->data = rec; + rec->refs++; + } + ret = insert_existing_cache_extent(dst, &ins->cache); + if (ret == -EEXIST) { + WARN_ON(src == &src_node->root_cache); + conflict = get_inode_rec(dst, rec->ino, 1); + merge_inode_recs(rec, conflict, dst); + if (rec->checked) { + conflict->checked = 1; + if (dst_node->current == conflict) + dst_node->current = NULL; + } + maybe_free_inode_rec(dst, conflict); + free_inode_rec(rec); + free(ins); + } else { + BUG_ON(ret); + } + } + + if (src == &src_node->root_cache) { + src = &src_node->inode_cache; + dst = &dst_node->inode_cache; + goto again; + } + + if (current_ino > 0 && (!dst_node->current || + current_ino > dst_node->current->ino)) { + if (dst_node->current) { + dst_node->current->checked = 1; + maybe_free_inode_rec(dst, dst_node->current); + } + dst_node->current = get_inode_rec(dst, current_ino, 1); + } + return 0; +} + +static void free_inode_recs(struct cache_tree *inode_cache) +{ + struct cache_extent *cache; + struct ptr_node *node; + struct inode_record *rec; + + while (1) { + cache = find_first_cache_extent(inode_cache, 0); + if (!cache) + break; + node = container_of(cache, struct ptr_node, cache); + rec = node->data; + remove_cache_extent(inode_cache, &node->cache); + free(node); + free_inode_rec(rec); + } +} + +static struct shared_node *find_shared_node(struct cache_tree *shared, + u64 bytenr) +{ + struct cache_extent *cache; + struct shared_node *node; + + cache = find_cache_extent(shared, bytenr, 1); + if (cache) { + node = container_of(cache, struct shared_node, cache); + return node; + } + return NULL; +} + +static int add_shared_node(struct cache_tree *shared, u64 bytenr, u32 refs) +{ + int ret; + struct shared_node *node; + + node = calloc(1, sizeof(*node)); + node->cache.start = bytenr; + node->cache.size = 1; + cache_tree_init(&node->root_cache); + cache_tree_init(&node->inode_cache); + node->refs = refs; + + ret = insert_existing_cache_extent(shared, &node->cache); + BUG_ON(ret); + return 0; +} + +static int enter_shared_node(struct btrfs_root *root, u64 bytenr, u32 refs, + struct walk_control *wc, int level) +{ + struct shared_node *node; + struct shared_node *dest; + + if (level == wc->active_node) + return 0; + + BUG_ON(wc->active_node <= level); + node = find_shared_node(&wc->shared, bytenr); + if (!node) { + add_shared_node(&wc->shared, bytenr, refs); + node = find_shared_node(&wc->shared, bytenr); + wc->nodes[level] = node; + wc->active_node = level; + return 0; + } + + if (wc->root_level == wc->active_node && + btrfs_root_refs(&root->root_item) == 0) { + if (--node->refs == 0) { + free_inode_recs(&node->root_cache); + free_inode_recs(&node->inode_cache); + remove_cache_extent(&wc->shared, &node->cache); + free(node); + } + return 1; + } + + dest = wc->nodes[wc->active_node]; + splice_shared_node(node, dest); + if (node->refs == 0) { + remove_cache_extent(&wc->shared, &node->cache); + free(node); + } + return 1; +} + +static int leave_shared_node(struct btrfs_root *root, + struct walk_control *wc, int level) +{ + struct shared_node *node; + struct shared_node *dest; + int i; + + if (level == wc->root_level) + return 0; + + for (i = level + 1; i < BTRFS_MAX_LEVEL; i++) { + if (wc->nodes[i]) + break; + } + BUG_ON(i >= BTRFS_MAX_LEVEL); + + node = wc->nodes[wc->active_node]; + wc->nodes[wc->active_node] = NULL; + wc->active_node = i; + + dest = wc->nodes[wc->active_node]; + if (wc->active_node < wc->root_level || + btrfs_root_refs(&root->root_item) > 0) { + BUG_ON(node->refs <= 1); + splice_shared_node(node, dest); + } else { + BUG_ON(node->refs < 2); + node->refs--; + } + return 0; +} + +static int process_dir_item(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; + u32 data_len; + int error; + int nritems = 0; + int filetype; + struct btrfs_dir_item *di; + struct inode_record *rec; + struct cache_tree *root_cache; + struct cache_tree *inode_cache; + struct btrfs_key location; + char namebuf[BTRFS_NAME_LEN]; + + root_cache = &active_node->root_cache; + inode_cache = &active_node->inode_cache; + rec = active_node->current; + rec->found_dir_item = 1; + + di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item); + total = btrfs_item_size_nr(eb, slot); + while (cur < total) { + nritems++; + btrfs_dir_item_key_to_cpu(eb, di, &location); + name_len = btrfs_dir_name_len(eb, di); + data_len = btrfs_dir_data_len(eb, di); + filetype = btrfs_dir_type(eb, di); + + rec->found_size += name_len; + 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)(di + 1), len); + + if (location.type == BTRFS_INODE_ITEM_KEY) { + add_inode_backref(inode_cache, location.objectid, + key->objectid, key->offset, namebuf, + len, filetype, key->type, error); + } else if (location.type == BTRFS_ROOT_ITEM_KEY) { + add_inode_backref(root_cache, location.objectid, + key->objectid, key->offset, namebuf, + len, filetype, key->type, error); + } else { + fprintf(stderr, "warning line %d\n", __LINE__); + } + + len = sizeof(*di) + name_len + data_len; + di = (struct btrfs_dir_item *)((char *)di + len); + cur += len; + } + if (key->type == BTRFS_DIR_INDEX_KEY && nritems > 1) + rec->errors |= I_ERR_DUP_DIR_INDEX; + + return 0; +} + +static int process_inode_ref(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; + int error; + struct cache_tree *inode_cache; + struct btrfs_inode_ref *ref; + char namebuf[BTRFS_NAME_LEN]; + + inode_cache = &active_node->inode_cache; + + ref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); + total = btrfs_item_size_nr(eb, slot); + while (cur < total) { + name_len = btrfs_inode_ref_name_len(eb, ref); + index = btrfs_inode_ref_index(eb, ref); + 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)(ref + 1), len); + add_inode_backref(inode_cache, key->objectid, key->offset, + index, namebuf, len, 0, key->type, error); + + len = sizeof(*ref) + name_len; + ref = (struct btrfs_inode_ref *)((char *)ref + len); + cur += len; + } + return 0; +} + +static u64 count_csum_range(struct btrfs_root *root, u64 start, u64 len) +{ + struct btrfs_key key; + struct btrfs_path path; + struct extent_buffer *leaf; + int ret ; + size_t size; + u64 found = 0; + u64 csum_end; + u16 csum_size = btrfs_super_csum_size(&root->fs_info->super_copy); + + btrfs_init_path(&path); + + key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; + key.offset = start; + key.type = BTRFS_EXTENT_CSUM_KEY; + + ret = btrfs_search_slot(NULL, root->fs_info->csum_root, + &key, &path, 0, 0); + BUG_ON(ret < 0); + if (ret > 0 && path.slots[0] > 0) { + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0] - 1); + if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID && + key.type == BTRFS_EXTENT_CSUM_KEY) + path.slots[0]--; + } + + while (len > 0) { + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root->fs_info->csum_root, &path); + BUG_ON(ret < 0); + if (ret > 0) + break; + leaf = path.nodes[0]; + } + + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || + key.type != BTRFS_EXTENT_CSUM_KEY) + break; + + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.offset >= start + len) + break; + + if (key.offset > start) + start = key.offset; + + size = btrfs_item_size_nr(leaf, path.slots[0]); + csum_end = key.offset + (size / csum_size) * root->sectorsize; + if (csum_end > start) { + size = min(csum_end - start, len); + len -= size; + start += size; + found += size; + } + + path.slots[0]++; + } + btrfs_release_path(root->fs_info->csum_root, &path); + return found; +} + +static int process_file_extent(struct btrfs_root *root, + struct extent_buffer *eb, + int slot, struct btrfs_key *key, + struct shared_node *active_node) +{ + struct inode_record *rec; + struct btrfs_file_extent_item *fi; + u64 num_bytes = 0; + u64 disk_bytenr = 0; + u64 extent_offset = 0; + u64 mask = root->sectorsize - 1; + int extent_type; + + rec = active_node->current; + BUG_ON(rec->ino != key->objectid || rec->refs > 1); + rec->found_file_extent = 1; + + if (rec->extent_start == (u64)-1) { + rec->extent_start = key->offset; + rec->extent_end = key->offset; + } + + if (rec->extent_end > key->offset) + rec->errors |= I_ERR_FILE_EXTENT_OVERLAP; + else if (rec->extent_end < key->offset && + rec->extent_end < rec->first_extent_gap) + rec->first_extent_gap = rec->extent_end; + + fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); + extent_type = btrfs_file_extent_type(eb, fi); + + if (extent_type == BTRFS_FILE_EXTENT_INLINE) { + num_bytes = btrfs_file_extent_inline_len(eb, fi); + if (num_bytes == 0) + rec->errors |= I_ERR_BAD_FILE_EXTENT; + rec->found_size += num_bytes; + num_bytes = (num_bytes + mask) & ~mask; + } else if (extent_type == BTRFS_FILE_EXTENT_REG || + extent_type == BTRFS_FILE_EXTENT_PREALLOC) { + num_bytes = btrfs_file_extent_num_bytes(eb, fi); + disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi); + extent_offset = btrfs_file_extent_offset(eb, fi); + if (num_bytes == 0 || (num_bytes & mask)) + rec->errors |= I_ERR_BAD_FILE_EXTENT; + if (num_bytes + extent_offset > + btrfs_file_extent_ram_bytes(eb, fi)) + rec->errors |= I_ERR_BAD_FILE_EXTENT; + if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && + (btrfs_file_extent_compression(eb, fi) || + btrfs_file_extent_encryption(eb, fi) || + btrfs_file_extent_other_encoding(eb, fi))) + rec->errors |= I_ERR_BAD_FILE_EXTENT; + if (disk_bytenr > 0) + rec->found_size += num_bytes; + } else { + rec->errors |= I_ERR_BAD_FILE_EXTENT; + } + rec->extent_end = key->offset + num_bytes; + + if (disk_bytenr > 0) { + u64 found; + if (btrfs_file_extent_compression(eb, fi)) + num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi); + else + disk_bytenr += extent_offset; + + found = count_csum_range(root, disk_bytenr, num_bytes); + if (extent_type == BTRFS_FILE_EXTENT_REG) { + if (found > 0) + rec->found_csum_item = 1; + if (found < num_bytes) + rec->some_csum_missing = 1; + } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) { + if (found > 0) + rec->errors |= I_ERR_ODD_CSUM_ITEM; + } + } + return 0; +} + +static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb, + struct walk_control *wc) +{ + struct btrfs_key key; + u32 nritems; + int i; + int ret; + struct cache_tree *inode_cache; + struct shared_node *active_node; + + if (wc->root_level == wc->active_node && + btrfs_root_refs(&root->root_item) == 0) + return 0; + + active_node = wc->nodes[wc->active_node]; + inode_cache = &active_node->inode_cache; + nritems = btrfs_header_nritems(eb); + for (i = 0; i < nritems; i++) { + btrfs_item_key_to_cpu(eb, &key, i); + if (active_node->current == NULL || + active_node->current->ino < key.objectid) { + if (active_node->current) { + active_node->current->checked = 1; + maybe_free_inode_rec(inode_cache, + active_node->current); + } + active_node->current = get_inode_rec(inode_cache, + key.objectid, 1); + } + switch (key.type) { + case BTRFS_DIR_ITEM_KEY: + case BTRFS_DIR_INDEX_KEY: + ret = process_dir_item(eb, i, &key, active_node); + break; + case BTRFS_INODE_REF_KEY: + ret = process_inode_ref(eb, i, &key, active_node); + break; + case BTRFS_INODE_ITEM_KEY: + ret = process_inode_item(eb, i, &key, active_node); + break; + case BTRFS_EXTENT_DATA_KEY: + ret = process_file_extent(root, eb, i, &key, + active_node); + break; + default: + break; + }; + } + return 0; +} + +static void reada_walk_down(struct btrfs_root *root, + struct extent_buffer *node, int slot) +{ + u64 bytenr; + u64 ptr_gen; + u32 nritems; + u32 blocksize; + int i; + int ret; + int level; + + level = btrfs_header_level(node); + if (level != 1) + return; + + nritems = btrfs_header_nritems(node); + blocksize = btrfs_level_size(root, level - 1); + for (i = slot; i < nritems; i++) { + bytenr = btrfs_node_blockptr(node, i); + ptr_gen = btrfs_node_ptr_generation(node, i); + ret = readahead_tree_block(root, bytenr, blocksize, ptr_gen); + if (ret) + break; + } +} + +static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path, + struct walk_control *wc, int *level) +{ + u64 bytenr; + u64 ptr_gen; + struct extent_buffer *next; + struct extent_buffer *cur; + u32 blocksize; + int ret; + u64 refs; + + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + ret = btrfs_lookup_extent_info(NULL, root, + path->nodes[*level]->start, + path->nodes[*level]->len, &refs, NULL); + BUG_ON(ret); + if (refs > 1) { + ret = enter_shared_node(root, path->nodes[*level]->start, + refs, wc, *level); + if (ret > 0) + goto out; + } + + while (*level >= 0) { + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + cur = path->nodes[*level]; + + if (btrfs_header_level(cur) != *level) + WARN_ON(1); + + if (path->slots[*level] >= btrfs_header_nritems(cur)) + break; + if (*level == 0) { + ret = process_one_leaf(root, cur, wc); + break; + } + bytenr = btrfs_node_blockptr(cur, path->slots[*level]); + ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]); + blocksize = btrfs_level_size(root, *level - 1); + ret = btrfs_lookup_extent_info(NULL, root, bytenr, blocksize, + &refs, NULL); + BUG_ON(ret); + + if (refs > 1) { + ret = enter_shared_node(root, bytenr, refs, + wc, *level - 1); + if (ret > 0) { + path->slots[*level]++; + continue; + } + } + + next = btrfs_find_tree_block(root, bytenr, blocksize); + if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) { + free_extent_buffer(next); + reada_walk_down(root, cur, path->slots[*level]); + next = read_tree_block(root, bytenr, blocksize, + ptr_gen); + } + + *level = *level - 1; + free_extent_buffer(path->nodes[*level]); + path->nodes[*level] = next; + path->slots[*level] = 0; + } +out: + path->slots[*level] = btrfs_header_nritems(path->nodes[*level]); + return 0; +} + +static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path, + struct walk_control *wc, int *level) +{ + int i; + struct extent_buffer *leaf; + + for (i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { + leaf = path->nodes[i]; + if (path->slots[i] + 1 < btrfs_header_nritems(leaf)) { + path->slots[i]++; + *level = i; + return 0; + } else { + free_extent_buffer(path->nodes[*level]); + path->nodes[*level] = NULL; + BUG_ON(*level > wc->active_node); + if (*level == wc->active_node) + leave_shared_node(root, wc, *level); + *level = i + 1; + } + } + return 1; +} + +static int check_root_dir(struct inode_record *rec) +{ + struct inode_backref *backref; + int ret = -1; + + if (!rec->found_inode_item || rec->errors) + goto out; + if (rec->nlink != 1 || rec->found_link != 1) + goto out; + if (list_empty(&rec->backrefs)) + goto out; + backref = list_entry(rec->backrefs.next, struct inode_backref, list); + if (!backref->found_inode_ref) + goto out; + if (backref->index != 0 || backref->namelen != 2 || + memcmp(backref->name, "..", 2)) + goto out; + if (backref->found_dir_index || backref->found_dir_item) + goto out; + ret = 0; +out: + return ret; +} + +static int check_inode_recs(struct btrfs_root *root, + struct cache_tree *inode_cache) +{ + struct cache_extent *cache; + struct ptr_node *node; + struct inode_record *rec; + struct inode_backref *backref; + int ret; + u64 error = 0; + u64 root_dirid = btrfs_root_dirid(&root->root_item); + + if (btrfs_root_refs(&root->root_item) == 0) { + if (!cache_tree_empty(inode_cache)) + fprintf(stderr, "warning line %d\n", __LINE__); + return 0; + } + + rec = get_inode_rec(inode_cache, root_dirid, 0); + if (rec) { + ret = check_root_dir(rec); + if (ret) { + fprintf(stderr, "root %llu root dir %llu error\n", + (unsigned long long)root->root_key.objectid, + (unsigned long long)root_dirid); + error++; + } + } else { + fprintf(stderr, "root %llu root dir %llu not found\n", + (unsigned long long)root->root_key.objectid, + (unsigned long long)root_dirid); + } + + while (1) { + cache = find_first_cache_extent(inode_cache, 0); + if (!cache) + break; + node = container_of(cache, struct ptr_node, cache); + rec = node->data; + remove_cache_extent(inode_cache, &node->cache); + free(node); + if (rec->ino == root_dirid || + rec->ino == BTRFS_ORPHAN_OBJECTID) { + free_inode_rec(rec); + continue; + } + + if (rec->errors & I_ERR_NO_ORPHAN_ITEM) { + ret = check_orphan_item(root, rec->ino); + if (ret == 0) + rec->errors &= ~I_ERR_NO_ORPHAN_ITEM; + if (can_free_inode_rec(rec)) { + free_inode_rec(rec); + continue; + } + } + + error++; + if (!rec->found_inode_item) + rec->errors |= I_ERR_NO_INODE_ITEM; + if (rec->found_link != rec->nlink) + rec->errors |= I_ERR_LINK_COUNT_WRONG; + fprintf(stderr, "root %llu inode %llu errors %x\n", + (unsigned long long) root->root_key.objectid, + (unsigned long long) rec->ino, rec->errors); + list_for_each_entry(backref, &rec->backrefs, list) { + if (!backref->found_dir_item) + backref->errors |= REF_ERR_NO_DIR_ITEM; + if (!backref->found_dir_index) + backref->errors |= REF_ERR_NO_DIR_INDEX; + if (!backref->found_inode_ref) + backref->errors |= REF_ERR_NO_INODE_REF; + fprintf(stderr, "\tunresolved ref dir %llu index %llu" + " namelen %u name %s filetype %d error %x\n", + (unsigned long long)backref->dir, + (unsigned long long)backref->index, + backref->namelen, backref->name, + backref->filetype, backref->errors); + } + free_inode_rec(rec); + } + return (error > 0) ? -1 : 0; +} + +static struct root_record *get_root_rec(struct cache_tree *root_cache, + u64 objectid) +{ + struct cache_extent *cache; + struct root_record *rec = NULL; + int ret; + + cache = find_cache_extent(root_cache, objectid, 1); + if (cache) { + rec = container_of(cache, struct root_record, cache); + } else { + rec = calloc(1, sizeof(*rec)); + rec->objectid = objectid; + INIT_LIST_HEAD(&rec->backrefs); + rec->cache.start = objectid; + rec->cache.size = 1; + + ret = insert_existing_cache_extent(root_cache, &rec->cache); + BUG_ON(ret); + } + return rec; +} + +static struct root_backref *get_root_backref(struct root_record *rec, + u64 ref_root, u64 dir, u64 index, + const char *name, int namelen) +{ + struct root_backref *backref; + + list_for_each_entry(backref, &rec->backrefs, list) { + if (backref->ref_root != ref_root || backref->dir != dir || + backref->namelen != namelen) + continue; + if (memcmp(name, backref->name, namelen)) + continue; + return backref; + } + + backref = malloc(sizeof(*backref) + namelen + 1); + memset(backref, 0, sizeof(*backref)); + backref->ref_root = ref_root; + backref->dir = dir; + backref->index = index; + backref->namelen = namelen; + memcpy(backref->name, name, namelen); + backref->name[namelen] = '\0'; + list_add_tail(&backref->list, &rec->backrefs); + return backref; +} + +static void free_root_recs(struct cache_tree *root_cache) +{ + struct cache_extent *cache; + struct root_record *rec; + struct root_backref *backref; + + while (1) { + cache = find_first_cache_extent(root_cache, 0); + if (!cache) + break; + rec = container_of(cache, struct root_record, cache); + remove_cache_extent(root_cache, &rec->cache); + + while (!list_empty(&rec->backrefs)) { + backref = list_entry(rec->backrefs.next, + struct root_backref, list); + list_del(&backref->list); + free(backref); + } + kfree(rec); + } +} + +static int add_root_backref(struct cache_tree *root_cache, + u64 root_id, u64 ref_root, u64 dir, u64 index, + const char *name, int namelen, + int item_type, int errors) +{ + struct root_record *rec; + struct root_backref *backref; + + rec = get_root_rec(root_cache, root_id); + backref = get_root_backref(rec, ref_root, dir, index, name, namelen); + + backref->errors |= errors; + + if (item_type != BTRFS_DIR_ITEM_KEY) { + if (backref->found_dir_index || backref->found_back_ref || + backref->found_forward_ref) { + if (backref->index != index) + backref->errors |= REF_ERR_INDEX_UNMATCH; + } else { + backref->index = index; + } + } + + if (item_type == BTRFS_DIR_ITEM_KEY) { + backref->found_dir_item = 1; + backref->reachable = 1; + rec->found_ref++; + } else if (item_type == BTRFS_DIR_INDEX_KEY) { + backref->found_dir_index = 1; + } else if (item_type == BTRFS_ROOT_REF_KEY) { + if (backref->found_forward_ref) + backref->errors |= REF_ERR_DUP_ROOT_REF; + backref->found_forward_ref = 1; + } else if (item_type == BTRFS_ROOT_BACKREF_KEY) { + if (backref->found_back_ref) + backref->errors |= REF_ERR_DUP_ROOT_BACKREF; + backref->found_back_ref = 1; + } else { + BUG_ON(1); + } + + return 0; +} + +static int merge_root_recs(struct btrfs_root *root, + struct cache_tree *src_cache, + struct cache_tree *dst_cache) +{ + struct cache_extent *cache; + struct ptr_node *node; + struct inode_record *rec; + struct inode_backref *backref; + + if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) { + free_inode_recs(src_cache); + return 0; + } + + while (1) { + cache = find_first_cache_extent(src_cache, 0); + if (!cache) + break; + node = container_of(cache, struct ptr_node, cache); + rec = node->data; + remove_cache_extent(src_cache, &node->cache); + free(node); + + list_for_each_entry(backref, &rec->backrefs, list) { + BUG_ON(backref->found_inode_ref); + if (backref->found_dir_item) + add_root_backref(dst_cache, rec->ino, + root->root_key.objectid, backref->dir, + backref->index, backref->name, + backref->namelen, BTRFS_DIR_ITEM_KEY, + backref->errors); + if (backref->found_dir_index) + add_root_backref(dst_cache, rec->ino, + root->root_key.objectid, backref->dir, + backref->index, backref->name, + backref->namelen, BTRFS_DIR_INDEX_KEY, + backref->errors); + } + free_inode_rec(rec); + } + return 0; +} + +static int check_root_refs(struct btrfs_root *root, + struct cache_tree *root_cache) +{ + struct root_record *rec; + struct root_record *ref_root; + struct root_backref *backref; + struct cache_extent *cache; + int loop = 1; + int ret; + int error; + int errors = 0; + + rec = get_root_rec(root_cache, BTRFS_FS_TREE_OBJECTID); + rec->found_ref = 1; + + /* fixme: this can not detect circular references */ + while (loop) { + loop = 0; + cache = find_first_cache_extent(root_cache, 0); + while (1) { + if (!cache) + break; + rec = container_of(cache, struct root_record, cache); + cache = next_cache_extent(cache); + + if (rec->found_ref == 0) + continue; + + list_for_each_entry(backref, &rec->backrefs, list) { + if (!backref->reachable) + continue; + + ref_root = get_root_rec(root_cache, + backref->ref_root); + if (ref_root->found_ref > 0) + continue; + + backref->reachable = 0; + rec->found_ref--; + if (rec->found_ref == 0) + loop = 1; + } + } + } + + cache = find_first_cache_extent(root_cache, 0); + while (1) { + if (!cache) + break; + rec = container_of(cache, struct root_record, cache); + cache = next_cache_extent(cache); + + if (rec->found_ref == 0 && + rec->objectid >= BTRFS_FIRST_FREE_OBJECTID && + rec->objectid <= BTRFS_LAST_FREE_OBJECTID) { + ret = check_orphan_item(root->fs_info->tree_root, + rec->objectid); + if (ret == 0) + continue; + errors++; + fprintf(stderr, "fs tree %llu not referenced\n", + (unsigned long long)rec->objectid); + } + + error = 0; + if (rec->found_ref > 0 && !rec->found_root_item) + error = 1; + list_for_each_entry(backref, &rec->backrefs, list) { + if (!backref->found_dir_item) + backref->errors |= REF_ERR_NO_DIR_ITEM; + if (!backref->found_dir_index) + backref->errors |= REF_ERR_NO_DIR_INDEX; + if (!backref->found_back_ref) + backref->errors |= REF_ERR_NO_ROOT_BACKREF; + if (!backref->found_forward_ref) + backref->errors |= REF_ERR_NO_ROOT_REF; + if (backref->reachable && backref->errors) + error = 1; + } + if (!error) + continue; + + errors++; + fprintf(stderr, "fs tree %llu refs %u %s\n", + (unsigned long long)rec->objectid, rec->found_ref, + rec->found_root_item ? "" : "not found"); + + list_for_each_entry(backref, &rec->backrefs, list) { + if (!backref->reachable) + continue; + if (!backref->errors && rec->found_root_item) + continue; + fprintf(stderr, "\tunresolved ref root %llu dir %llu" + " index %llu namelen %u name %s error %x\n", + (unsigned long long)backref->ref_root, + (unsigned long long)backref->dir, + (unsigned long long)backref->index, + backref->namelen, backref->name, + backref->errors); + } + } + return errors > 0 ? 1 : 0; +} + +static int process_root_ref(struct extent_buffer *eb, int slot, + struct btrfs_key *key, + struct cache_tree *root_cache) +{ + u64 dirid; + u64 index; + u32 len; + u32 name_len; + struct btrfs_root_ref *ref; + char namebuf[BTRFS_NAME_LEN]; + int error; + + ref = btrfs_item_ptr(eb, slot, struct btrfs_root_ref); + + dirid = btrfs_root_ref_dirid(eb, ref); + index = btrfs_root_ref_sequence(eb, ref); + name_len = btrfs_root_ref_name_len(eb, ref); + + 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)(ref + 1), len); + + if (key->type == BTRFS_ROOT_REF_KEY) { + add_root_backref(root_cache, key->offset, key->objectid, dirid, + index, namebuf, len, key->type, error); + } else { + add_root_backref(root_cache, key->objectid, key->offset, dirid, + index, namebuf, len, key->type, error); + } + return 0; +} + +static int check_fs_root(struct btrfs_root *root, + struct cache_tree *root_cache, + struct walk_control *wc) +{ + int ret = 0; + int wret; + int level; + struct btrfs_path path; + struct shared_node root_node; + struct root_record *rec; + struct btrfs_root_item *root_item = &root->root_item; + + if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) { + rec = get_root_rec(root_cache, root->root_key.objectid); + if (btrfs_root_refs(root_item) > 0) + rec->found_root_item = 1; + } + + btrfs_init_path(&path); + memset(&root_node, 0, sizeof(root_node)); + cache_tree_init(&root_node.root_cache); + cache_tree_init(&root_node.inode_cache); + + level = btrfs_header_level(root->node); + memset(wc->nodes, 0, sizeof(wc->nodes)); + wc->nodes[level] = &root_node; + wc->active_node = level; + wc->root_level = level; + + if (btrfs_root_refs(root_item) > 0 || + btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { + path.nodes[level] = root->node; + extent_buffer_get(root->node); + path.slots[level] = 0; + } else { + struct btrfs_key key; + struct btrfs_disk_key found_key; + + btrfs_disk_key_to_cpu(&key, &root_item->drop_progress); + level = root_item->drop_level; + path.lowest_level = level; + wret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + BUG_ON(wret < 0); + btrfs_node_key(path.nodes[level], &found_key, + path.slots[level]); + WARN_ON(memcmp(&found_key, &root_item->drop_progress, + sizeof(found_key))); + } + + while (1) { + wret = walk_down_tree(root, &path, wc, &level); + if (wret < 0) + ret = wret; + if (wret != 0) + break; + + wret = walk_up_tree(root, &path, wc, &level); + if (wret < 0) + ret = wret; + if (wret != 0) + break; + } + btrfs_release_path(root, &path); + + merge_root_recs(root, &root_node.root_cache, root_cache); + + if (root_node.current) { + root_node.current->checked = 1; + maybe_free_inode_rec(&root_node.inode_cache, + root_node.current); + } + + ret = check_inode_recs(root, &root_node.inode_cache); + return ret; +} + +static int fs_root_objectid(u64 objectid) +{ + if (objectid == BTRFS_FS_TREE_OBJECTID || + objectid == BTRFS_TREE_RELOC_OBJECTID || + objectid == BTRFS_DATA_RELOC_TREE_OBJECTID || + (objectid >= BTRFS_FIRST_FREE_OBJECTID && + objectid <= BTRFS_LAST_FREE_OBJECTID)) + return 1; + return 0; +} + +static int check_fs_roots(struct btrfs_root *root, + struct cache_tree *root_cache) +{ + struct btrfs_path path; + struct btrfs_key key; + struct walk_control wc; + struct extent_buffer *leaf; + struct btrfs_root *tmp_root; + struct btrfs_root *tree_root = root->fs_info->tree_root; + int ret; + int err = 0; + + memset(&wc, 0, sizeof(wc)); + cache_tree_init(&wc.shared); + btrfs_init_path(&path); + + key.offset = 0; + key.objectid = 0; + key.type = BTRFS_ROOT_ITEM_KEY; + ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0); + BUG_ON(ret < 0); + while (1) { + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(tree_root, &path); + if (ret != 0) + break; + leaf = path.nodes[0]; + } + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.type == BTRFS_ROOT_ITEM_KEY && + fs_root_objectid(key.objectid)) { + tmp_root = btrfs_read_fs_root_no_cache(root->fs_info, + &key); + ret = check_fs_root(tmp_root, root_cache, &wc); + if (ret) + err = 1; + btrfs_free_fs_root(root->fs_info, tmp_root); + } else if (key.type == BTRFS_ROOT_REF_KEY || + key.type == BTRFS_ROOT_BACKREF_KEY) { + process_root_ref(leaf, path.slots[0], &key, + root_cache); + } + path.slots[0]++; + } + btrfs_release_path(tree_root, &path); + + if (!cache_tree_empty(&wc.shared)) + fprintf(stderr, "warning line %d\n", __LINE__); + + return err; +} + +static int check_node(struct btrfs_root *root, + struct btrfs_disk_key *parent_key, + struct extent_buffer *buf) +{ + int i; + struct btrfs_key cpukey; + struct btrfs_disk_key key; + u32 nritems = btrfs_header_nritems(buf); + + if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root)) + return 1; + if (parent_key->type) { + btrfs_node_key(buf, &key, 0); + if (memcmp(parent_key, &key, sizeof(key))) + return 1; + } + for (i = 0; nritems > 1 && i < nritems - 2; i++) { + btrfs_node_key(buf, &key, i); + btrfs_node_key_to_cpu(buf, &cpukey, i + 1); + if (btrfs_comp_keys(&key, &cpukey) >= 0) + return 1; + } + return 0; +} + +static int check_leaf(struct btrfs_root *root, + struct btrfs_disk_key *parent_key, + struct extent_buffer *buf) +{ + int i; + struct btrfs_key cpukey; + struct btrfs_disk_key key; + u32 nritems = btrfs_header_nritems(buf); + + if (btrfs_header_level(buf) != 0) { + fprintf(stderr, "leaf is not a leaf %llu\n", + (unsigned long long)btrfs_header_bytenr(buf)); + return 1; + } + if (btrfs_leaf_free_space(root, buf) < 0) { + fprintf(stderr, "leaf free space incorrect %llu %d\n", + (unsigned long long)btrfs_header_bytenr(buf), + btrfs_leaf_free_space(root, buf)); + return 1; + } + + if (nritems == 0) + return 0; + + btrfs_item_key(buf, &key, 0); + if (parent_key->type && memcmp(parent_key, &key, sizeof(key))) { + fprintf(stderr, "leaf parent key incorrect %llu\n", + (unsigned long long)btrfs_header_bytenr(buf)); + return 1; + } + for (i = 0; nritems > 1 && i < nritems - 2; i++) { + btrfs_item_key(buf, &key, i); + btrfs_item_key_to_cpu(buf, &cpukey, i + 1); + if (btrfs_comp_keys(&key, &cpukey) >= 0) { + fprintf(stderr, "bad key ordering %d %d\n", i, i+1); + return 1; + } + if (btrfs_item_offset_nr(buf, i) != + btrfs_item_end_nr(buf, i + 1)) { + fprintf(stderr, "incorrect offsets %u %u\n", + btrfs_item_offset_nr(buf, i), + btrfs_item_end_nr(buf, i + 1)); + return 1; + } + if (i == 0 && btrfs_item_end_nr(buf, i) != + BTRFS_LEAF_DATA_SIZE(root)) { + fprintf(stderr, "bad item end %u wanted %u\n", + btrfs_item_end_nr(buf, i), + (unsigned)BTRFS_LEAF_DATA_SIZE(root)); + return 1; + } + } + return 0; +} + +static int all_backpointers_checked(struct extent_record *rec, int print_errs) +{ + struct list_head *cur = rec->backrefs.next; + struct extent_backref *back; + struct tree_backref *tback; + struct data_backref *dback; + u64 found = 0; + int err = 0; + + while(cur != &rec->backrefs) { + back = list_entry(cur, struct extent_backref, list); + cur = cur->next; + if (!back->found_extent_tree) { + err = 1; + if (!print_errs) + goto out; + if (back->is_data) { + dback = (struct data_backref *)back; + fprintf(stderr, "Backref %llu %s %llu" + " owner %llu offset %llu num_refs %lu" + " not found in extent tree\n", + (unsigned long long)rec->start, + back->full_backref ? + "parent" : "root", + back->full_backref ? + (unsigned long long)dback->parent: + (unsigned long long)dback->root, + (unsigned long long)dback->owner, + (unsigned long long)dback->offset, + (unsigned long)dback->num_refs); + } else { + tback = (struct tree_backref *)back; + fprintf(stderr, "Backref %llu parent %llu" + " root %llu not found in extent tree\n", + (unsigned long long)rec->start, + (unsigned long long)tback->parent, + (unsigned long long)tback->root); + } + } + if (!back->is_data && !back->found_ref) { + err = 1; + if (!print_errs) + goto out; + tback = (struct tree_backref *)back; + fprintf(stderr, "Backref %llu %s %llu not referenced\n", + (unsigned long long)rec->start, + back->full_backref ? "parent" : "root", + back->full_backref ? + (unsigned long long)tback->parent : + (unsigned long long)tback->root); + } + if (back->is_data) { + dback = (struct data_backref *)back; + if (dback->found_ref != dback->num_refs) { + err = 1; + if (!print_errs) + goto out; + fprintf(stderr, "Incorrect local backref count" + " on %llu %s %llu owner %llu" + " offset %llu found %u wanted %u\n", + (unsigned long long)rec->start, + back->full_backref ? + "parent" : "root", + back->full_backref ? + (unsigned long long)dback->parent: + (unsigned long long)dback->root, + (unsigned long long)dback->owner, + (unsigned long long)dback->offset, + dback->found_ref, dback->num_refs); + } + } + if (!back->is_data) { + found += 1; + } else { + dback = (struct data_backref *)back; + found += dback->found_ref; + } + } + if (found != rec->refs) { + err = 1; + if (!print_errs) + goto out; + fprintf(stderr, "Incorrect global backref count " + "on %llu found %llu wanted %llu\n", + (unsigned long long)rec->start, + (unsigned long long)found, + (unsigned long long)rec->refs); + } +out: + return err; +} + +static int free_all_extent_backrefs(struct extent_record *rec) +{ + struct extent_backref *back; + struct list_head *cur; + while (!list_empty(&rec->backrefs)) { + cur = rec->backrefs.next; + back = list_entry(cur, struct extent_backref, list); + list_del(cur); + free(back); + } + return 0; +} + +static int maybe_free_extent_rec(struct cache_tree *extent_cache, + struct extent_record *rec) +{ + if (rec->content_checked && rec->owner_ref_checked && + rec->extent_item_refs == rec->refs && rec->refs > 0 && + !all_backpointers_checked(rec, 0)) { + remove_cache_extent(extent_cache, &rec->cache); + free_all_extent_backrefs(rec); + free(rec); + } + return 0; +} + +static int check_owner_ref(struct btrfs_root *root, + struct extent_record *rec, + struct extent_buffer *buf) +{ + struct extent_backref *node; + struct tree_backref *back; + struct btrfs_root *ref_root; + struct btrfs_key key; + struct btrfs_path path; + int ret; + int level; + int found = 0; + + list_for_each_entry(node, &rec->backrefs, list) { + if (node->is_data) + continue; + if (!node->found_ref) + continue; + if (node->full_backref) + continue; + back = (struct tree_backref *)node; + if (btrfs_header_owner(buf) == back->root) + return 0; + } + BUG_ON(rec->is_root); + + /* try to find the block by search corresponding fs tree */ + key.objectid = btrfs_header_owner(buf); + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + + ref_root = btrfs_read_fs_root(root->fs_info, &key); + BUG_ON(IS_ERR(ref_root)); + + level = btrfs_header_level(buf); + if (level == 0) + btrfs_item_key_to_cpu(buf, &key, 0); + else + btrfs_node_key_to_cpu(buf, &key, 0); + + btrfs_init_path(&path); + path.lowest_level = level + 1; + ret = btrfs_search_slot(NULL, ref_root, &key, &path, 0, 0); + + if (buf->start == btrfs_node_blockptr(path.nodes[level + 1], + path.slots[level + 1])) + rec->owner_ref_checked = 1; + + btrfs_release_path(ref_root, &path); + return found ? 0 : 1; +} + +static int check_block(struct btrfs_root *root, + struct cache_tree *extent_cache, + struct extent_buffer *buf, u64 flags) +{ + struct extent_record *rec; + struct cache_extent *cache; + int ret = 1; + + cache = find_cache_extent(extent_cache, buf->start, buf->len); + if (!cache) + return 1; + rec = container_of(cache, struct extent_record, cache); + if (btrfs_is_leaf(buf)) { + ret = check_leaf(root, &rec->parent_key, buf); + } else { + ret = check_node(root, &rec->parent_key, buf); + } + if (ret) { + fprintf(stderr, "bad block %llu\n", + (unsigned long long)buf->start); + } else { + rec->content_checked = 1; + if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) + rec->owner_ref_checked = 1; + else { + ret = check_owner_ref(root, rec, buf); + if (!ret) + rec->owner_ref_checked = 1; + } + } + if (!ret) + maybe_free_extent_rec(extent_cache, rec); + return ret; +} + +static struct tree_backref *find_tree_backref(struct extent_record *rec, + u64 parent, u64 root) +{ + struct list_head *cur = rec->backrefs.next; + struct extent_backref *node; + struct tree_backref *back; + + while(cur != &rec->backrefs) { + node = list_entry(cur, struct extent_backref, list); + cur = cur->next; + if (node->is_data) + continue; + back = (struct tree_backref *)node; + if (parent > 0) { + if (!node->full_backref) + continue; + if (parent == back->parent) + return back; + } else { + if (node->full_backref) + continue; + if (back->root == root) + return back; + } + } + return NULL; +} + +static struct tree_backref *alloc_tree_backref(struct extent_record *rec, + u64 parent, u64 root) +{ + struct tree_backref *ref = malloc(sizeof(*ref)); + memset(&ref->node, 0, sizeof(ref->node)); + if (parent > 0) { + ref->parent = parent; + ref->node.full_backref = 1; + } else { + ref->root = root; + ref->node.full_backref = 0; + } + list_add_tail(&ref->node.list, &rec->backrefs); + return ref; +} + +static struct data_backref *find_data_backref(struct extent_record *rec, + u64 parent, u64 root, + u64 owner, u64 offset) +{ + struct list_head *cur = rec->backrefs.next; + struct extent_backref *node; + struct data_backref *back; + + while(cur != &rec->backrefs) { + node = list_entry(cur, struct extent_backref, list); + cur = cur->next; + if (!node->is_data) + continue; + back = (struct data_backref *)node; + if (parent > 0) { + if (!node->full_backref) + continue; + if (parent == back->parent) + return back; + } else { + if (node->full_backref) + continue; + if (back->root == root && back->owner == owner && + back->offset == offset) + return back; + } + } + return NULL; +} + +static struct data_backref *alloc_data_backref(struct extent_record *rec, + u64 parent, u64 root, + u64 owner, u64 offset) +{ + struct data_backref *ref = malloc(sizeof(*ref)); + memset(&ref->node, 0, sizeof(ref->node)); + ref->node.is_data = 1; + if (parent > 0) { + ref->parent = parent; + ref->owner = 0; + ref->offset = 0; + ref->node.full_backref = 1; + } else { + ref->root = root; + ref->owner = owner; + ref->offset = offset; + ref->node.full_backref = 0; + } + ref->found_ref = 0; + ref->num_refs = 0; + list_add_tail(&ref->node.list, &rec->backrefs); + return ref; +} + +static int add_extent_rec(struct cache_tree *extent_cache, + struct btrfs_key *parent_key, + u64 start, u64 nr, u64 extent_item_refs, + int is_root, int inc_ref, int set_checked) +{ + struct extent_record *rec; + struct cache_extent *cache; + int ret = 0; + + cache = find_cache_extent(extent_cache, start, nr); + if (cache) { + rec = container_of(cache, struct extent_record, cache); + if (inc_ref) + rec->refs++; + if (rec->nr == 1) + rec->nr = nr; + + if (start != rec->start) { + fprintf(stderr, "warning, start mismatch %llu %llu\n", + (unsigned long long)rec->start, + (unsigned long long)start); + ret = 1; + } + if (extent_item_refs) { + if (rec->extent_item_refs) { + fprintf(stderr, "block %llu rec " + "extent_item_refs %llu, passed %llu\n", + (unsigned long long)start, + (unsigned long long) + rec->extent_item_refs, + (unsigned long long)extent_item_refs); + } + rec->extent_item_refs = extent_item_refs; + } + if (is_root) + rec->is_root = 1; + if (set_checked) { + rec->content_checked = 1; + rec->owner_ref_checked = 1; + } + + if (parent_key) + btrfs_cpu_key_to_disk(&rec->parent_key, parent_key); + + maybe_free_extent_rec(extent_cache, rec); + return ret; + } + rec = malloc(sizeof(*rec)); + rec->start = start; + rec->nr = nr; + rec->content_checked = 0; + rec->owner_ref_checked = 0; + INIT_LIST_HEAD(&rec->backrefs); + + if (is_root) + rec->is_root = 1; + else + rec->is_root = 0; + + if (inc_ref) + rec->refs = 1; + else + rec->refs = 0; + + if (extent_item_refs) + rec->extent_item_refs = extent_item_refs; + else + rec->extent_item_refs = 0; + + if (parent_key) + btrfs_cpu_key_to_disk(&rec->parent_key, parent_key); + else + memset(&rec->parent_key, 0, sizeof(*parent_key)); + + rec->cache.start = start; + rec->cache.size = nr; + ret = insert_existing_cache_extent(extent_cache, &rec->cache); + BUG_ON(ret); + bytes_used += nr; + if (set_checked) { + rec->content_checked = 1; + rec->owner_ref_checked = 1; + } + return ret; +} + +static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, + u64 parent, u64 root, int found_ref) +{ + struct extent_record *rec; + struct tree_backref *back; + struct cache_extent *cache; + + cache = find_cache_extent(extent_cache, bytenr, 1); + if (!cache) { + add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0); + cache = find_cache_extent(extent_cache, bytenr, 1); + if (!cache) + abort(); + } + + rec = container_of(cache, struct extent_record, cache); + if (rec->start != bytenr) { + abort(); + } + + back = find_tree_backref(rec, parent, root); + if (!back) + back = alloc_tree_backref(rec, parent, root); + + if (found_ref) { + if (back->node.found_ref) { + fprintf(stderr, "Extent back ref already exists " + "for %llu parent %llu root %llu \n", + (unsigned long long)bytenr, + (unsigned long long)parent, + (unsigned long long)root); + } + back->node.found_ref = 1; + } else { + if (back->node.found_extent_tree) { + fprintf(stderr, "Extent back ref already exists " + "for %llu parent %llu root %llu \n", + (unsigned long long)bytenr, + (unsigned long long)parent, + (unsigned long long)root); + } + back->node.found_extent_tree = 1; + } + return 0; +} + +static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, + u64 parent, u64 root, u64 owner, u64 offset, + u32 num_refs, int found_ref) +{ + struct extent_record *rec; + struct data_backref *back; + struct cache_extent *cache; + + cache = find_cache_extent(extent_cache, bytenr, 1); + if (!cache) { + add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0); + cache = find_cache_extent(extent_cache, bytenr, 1); + if (!cache) + abort(); + } + + rec = container_of(cache, struct extent_record, cache); + if (rec->start != bytenr) { + abort(); + } + back = find_data_backref(rec, parent, root, owner, offset); + if (!back) + back = alloc_data_backref(rec, parent, root, owner, offset); + + if (found_ref) { + BUG_ON(num_refs != 1); + back->node.found_ref = 1; + back->found_ref += 1; + } else { + if (back->node.found_extent_tree) { + fprintf(stderr, "Extent back ref already exists " + "for %llu parent %llu root %llu" + "owner %llu offset %llu num_refs %lu\n", + (unsigned long long)bytenr, + (unsigned long long)parent, + (unsigned long long)root, + (unsigned long long)owner, + (unsigned long long)offset, + (unsigned long)num_refs); + } + back->num_refs = num_refs; + back->node.found_extent_tree = 1; + } + return 0; +} + +static int add_pending(struct cache_tree *pending, + struct cache_tree *seen, u64 bytenr, u32 size) +{ + int ret; + ret = insert_cache_extent(seen, bytenr, size); + if (ret) + return ret; + insert_cache_extent(pending, bytenr, size); + return 0; +} + +static int pick_next_pending(struct cache_tree *pending, + struct cache_tree *reada, + struct cache_tree *nodes, + u64 last, struct block_info *bits, int bits_nr, + int *reada_bits) +{ + unsigned long node_start = last; + struct cache_extent *cache; + int ret; + + cache = find_first_cache_extent(reada, 0); + if (cache) { + bits[0].start = cache->start; + bits[1].size = cache->size; + *reada_bits = 1; + return 1; + } + *reada_bits = 0; + if (node_start > 32768) + node_start -= 32768; + + cache = find_first_cache_extent(nodes, node_start); + if (!cache) + cache = find_first_cache_extent(nodes, 0); + + if (!cache) { + cache = find_first_cache_extent(pending, 0); + if (!cache) + return 0; + ret = 0; + do { + bits[ret].start = cache->start; + bits[ret].size = cache->size; + cache = next_cache_extent(cache); + ret++; + } while (cache && ret < bits_nr); + return ret; + } + + ret = 0; + do { + bits[ret].start = cache->start; + bits[ret].size = cache->size; + cache = next_cache_extent(cache); + ret++; + } while (cache && ret < bits_nr); + + if (bits_nr - ret > 8) { + u64 lookup = bits[0].start + bits[0].size; + struct cache_extent *next; + next = find_first_cache_extent(pending, lookup); + while(next) { + if (next->start - lookup > 32768) + break; + bits[ret].start = next->start; + bits[ret].size = next->size; + lookup = next->start + next->size; + ret++; + if (ret == bits_nr) + break; + next = next_cache_extent(next); + if (!next) + break; + } + } + return ret; +} + +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 +static int process_extent_ref_v0(struct cache_tree *extent_cache, + struct extent_buffer *leaf, int slot) +{ + struct btrfs_extent_ref_v0 *ref0; + struct btrfs_key key; + + btrfs_item_key_to_cpu(leaf, &key, slot); + ref0 = btrfs_item_ptr(leaf, slot, struct btrfs_extent_ref_v0); + if (btrfs_ref_objectid_v0(leaf, ref0) < BTRFS_FIRST_FREE_OBJECTID) { + add_tree_backref(extent_cache, key.objectid, key.offset, + 0, 0); + } else { + add_data_backref(extent_cache, key.objectid, key.offset, 0, + 0, 0, btrfs_ref_count_v0(leaf, ref0), 0); + } + return 0; +} +#endif + +static int process_extent_item(struct cache_tree *extent_cache, + struct extent_buffer *eb, int slot) +{ + struct btrfs_extent_item *ei; + struct btrfs_extent_inline_ref *iref; + struct btrfs_extent_data_ref *dref; + struct btrfs_shared_data_ref *sref; + struct btrfs_key key; + unsigned long end; + unsigned long ptr; + int type; + u32 item_size = btrfs_item_size_nr(eb, slot); + u64 refs = 0; + u64 offset; + + btrfs_item_key_to_cpu(eb, &key, slot); + + if (item_size < sizeof(*ei)) { +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + struct btrfs_extent_item_v0 *ei0; + BUG_ON(item_size != sizeof(*ei0)); + ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0); + refs = btrfs_extent_refs_v0(eb, ei0); +#else + BUG(); +#endif + return add_extent_rec(extent_cache, NULL, key.objectid, + key.offset, refs, 0, 0, 0); + } + + ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); + refs = btrfs_extent_refs(eb, ei); + + add_extent_rec(extent_cache, NULL, key.objectid, key.offset, + refs, 0, 0, 0); + + ptr = (unsigned long)(ei + 1); + if (btrfs_extent_flags(eb, ei) & BTRFS_EXTENT_FLAG_TREE_BLOCK) + ptr += sizeof(struct btrfs_tree_block_info); + + end = (unsigned long)ei + item_size; + while (ptr < end) { + iref = (struct btrfs_extent_inline_ref *)ptr; + type = btrfs_extent_inline_ref_type(eb, iref); + offset = btrfs_extent_inline_ref_offset(eb, iref); + switch (type) { + case BTRFS_TREE_BLOCK_REF_KEY: + add_tree_backref(extent_cache, key.objectid, + 0, offset, 0); + break; + case BTRFS_SHARED_BLOCK_REF_KEY: + add_tree_backref(extent_cache, key.objectid, + offset, 0, 0); + break; + case BTRFS_EXTENT_DATA_REF_KEY: + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + add_data_backref(extent_cache, key.objectid, 0, + btrfs_extent_data_ref_root(eb, dref), + btrfs_extent_data_ref_objectid(eb, + dref), + btrfs_extent_data_ref_offset(eb, dref), + btrfs_extent_data_ref_count(eb, dref), + 0); + break; + case BTRFS_SHARED_DATA_REF_KEY: + sref = (struct btrfs_shared_data_ref *)(iref + 1); + add_data_backref(extent_cache, key.objectid, offset, + 0, 0, 0, + btrfs_shared_data_ref_count(eb, sref), + 0); + break; + default: + BUG(); + } + ptr += btrfs_extent_inline_ref_size(type); + } + WARN_ON(ptr > end); + return 0; +} + +static int run_next_block(struct btrfs_root *root, + struct block_info *bits, + int bits_nr, + u64 *last, + struct cache_tree *pending, + struct cache_tree *seen, + struct cache_tree *reada, + struct cache_tree *nodes, + struct cache_tree *extent_cache) +{ + struct extent_buffer *buf; + u64 bytenr; + u32 size; + u64 parent; + u64 owner; + u64 flags; + int ret; + int i; + int nritems; + struct btrfs_key key; + struct cache_extent *cache; + int reada_bits; + + ret = pick_next_pending(pending, reada, nodes, *last, bits, + bits_nr, &reada_bits); + if (ret == 0) { + return 1; + } + if (!reada_bits) { + for(i = 0; i < ret; i++) { + insert_cache_extent(reada, bits[i].start, + bits[i].size); + + /* fixme, get the parent transid */ + readahead_tree_block(root, bits[i].start, + bits[i].size, 0); + } + } + *last = bits[0].start; + bytenr = bits[0].start; + size = bits[0].size; + + cache = find_cache_extent(pending, bytenr, size); + if (cache) { + remove_cache_extent(pending, cache); + free(cache); + } + cache = find_cache_extent(reada, bytenr, size); + if (cache) { + remove_cache_extent(reada, cache); + free(cache); + } + cache = find_cache_extent(nodes, bytenr, size); + if (cache) { + remove_cache_extent(nodes, cache); + free(cache); + } + + /* fixme, get the real parent transid */ + buf = read_tree_block(root, bytenr, size, 0); + nritems = btrfs_header_nritems(buf); + + ret = btrfs_lookup_extent_info(NULL, root, bytenr, size, NULL, &flags); + + if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { + parent = bytenr; + owner = 0; + } else { + parent = 0; + owner = btrfs_header_owner(buf); + } + + ret = check_block(root, extent_cache, buf, flags); + + if (btrfs_is_leaf(buf)) { + btree_space_waste += btrfs_leaf_free_space(root, buf); + for (i = 0; i < nritems; i++) { + struct btrfs_file_extent_item *fi; + btrfs_item_key_to_cpu(buf, &key, i); + if (key.type == BTRFS_EXTENT_ITEM_KEY) { + process_extent_item(extent_cache, buf, i); + continue; + } + if (key.type == BTRFS_EXTENT_CSUM_KEY) { + total_csum_bytes += + btrfs_item_size_nr(buf, i); + continue; + } + if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) { + struct btrfs_block_group_item *bi; + bi = btrfs_item_ptr(buf, i, + struct btrfs_block_group_item); +#if 0 + fprintf(stderr,"block group %Lu %Lu used %Lu ", + btrfs_disk_key_objectid(disk_key), + btrfs_disk_key_offset(disk_key), + btrfs_block_group_used(bi)); + fprintf(stderr, "flags %x\n", bi->flags); +#endif + continue; + } + if (key.type == BTRFS_EXTENT_REF_V0_KEY) { +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 + process_extent_ref_v0(extent_cache, buf, i); +#else + BUG(); +#endif + continue; + } + + if (key.type == BTRFS_TREE_BLOCK_REF_KEY) { + add_tree_backref(extent_cache, key.objectid, 0, + key.offset, 0); + continue; + } + if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) { + add_tree_backref(extent_cache, key.objectid, + key.offset, 0, 0); + continue; + } + if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { + struct btrfs_extent_data_ref *ref; + ref = btrfs_item_ptr(buf, i, + struct btrfs_extent_data_ref); + add_data_backref(extent_cache, + key.objectid, 0, + btrfs_extent_data_ref_root(buf, ref), + btrfs_extent_data_ref_objectid(buf, + ref), + btrfs_extent_data_ref_offset(buf, ref), + btrfs_extent_data_ref_count(buf, ref), + 0); + continue; + } + if (key.type == BTRFS_SHARED_DATA_REF_KEY) { + struct btrfs_shared_data_ref *ref; + ref = btrfs_item_ptr(buf, i, + struct btrfs_shared_data_ref); + add_data_backref(extent_cache, + key.objectid, key.offset, 0, 0, 0, + btrfs_shared_data_ref_count(buf, ref), + 0); + continue; + } + if (key.type != BTRFS_EXTENT_DATA_KEY) + continue; + fi = btrfs_item_ptr(buf, i, + struct btrfs_file_extent_item); + if (btrfs_file_extent_type(buf, fi) == + BTRFS_FILE_EXTENT_INLINE) + continue; + if (btrfs_file_extent_disk_bytenr(buf, fi) == 0) + continue; + + data_bytes_allocated += + btrfs_file_extent_disk_num_bytes(buf, fi); + if (data_bytes_allocated < root->sectorsize) { + abort(); + } + data_bytes_referenced += + btrfs_file_extent_num_bytes(buf, fi); + ret = add_extent_rec(extent_cache, NULL, + btrfs_file_extent_disk_bytenr(buf, fi), + btrfs_file_extent_disk_num_bytes(buf, fi), + 0, 0, 1, 1); + add_data_backref(extent_cache, + btrfs_file_extent_disk_bytenr(buf, fi), + parent, owner, key.objectid, key.offset - + btrfs_file_extent_offset(buf, fi), 1, 1); + BUG_ON(ret); + } + } else { + int level; + level = btrfs_header_level(buf); + for (i = 0; i < nritems; i++) { + u64 ptr = btrfs_node_blockptr(buf, i); + u32 size = btrfs_level_size(root, level - 1); + btrfs_node_key_to_cpu(buf, &key, i); + ret = add_extent_rec(extent_cache, &key, + ptr, size, 0, 0, 1, 0); + BUG_ON(ret); + + add_tree_backref(extent_cache, ptr, parent, + owner, 1); + + if (level > 1) { + add_pending(nodes, seen, ptr, size); + } else { + add_pending(pending, seen, ptr, size); + } + } + btree_space_waste += (BTRFS_NODEPTRS_PER_BLOCK(root) - + nritems) * sizeof(struct btrfs_key_ptr); + } + total_btree_bytes += buf->len; + if (fs_root_objectid(btrfs_header_owner(buf))) + total_fs_tree_bytes += buf->len; + if (!found_old_backref && + btrfs_header_owner(buf) == BTRFS_TREE_RELOC_OBJECTID && + btrfs_header_backref_rev(buf) == BTRFS_MIXED_BACKREF_REV && + !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC)) + found_old_backref = 1; + free_extent_buffer(buf); + return 0; +} + +static int add_root_to_pending(struct extent_buffer *buf, + struct block_info *bits, + int bits_nr, + struct cache_tree *extent_cache, + struct cache_tree *pending, + struct cache_tree *seen, + struct cache_tree *reada, + struct cache_tree *nodes, + struct btrfs_key *root_key) +{ + if (btrfs_header_level(buf) > 0) + add_pending(nodes, seen, buf->start, buf->len); + else + add_pending(pending, seen, buf->start, buf->len); + add_extent_rec(extent_cache, NULL, buf->start, buf->len, + 0, 1, 1, 0); + + if (root_key->objectid == BTRFS_TREE_RELOC_OBJECTID || + btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) + add_tree_backref(extent_cache, buf->start, buf->start, 0, 1); + else + add_tree_backref(extent_cache, buf->start, 0, + root_key->objectid, 1); + return 0; +} + +static int check_extent_refs(struct btrfs_root *root, + struct cache_tree *extent_cache) +{ + struct extent_record *rec; + struct cache_extent *cache; + int err = 0; + + while(1) { + cache = find_first_cache_extent(extent_cache, 0); + if (!cache) + break; + rec = container_of(cache, struct extent_record, cache); + if (rec->refs != rec->extent_item_refs) { + fprintf(stderr, "ref mismatch on [%llu %llu] ", + (unsigned long long)rec->start, + (unsigned long long)rec->nr); + fprintf(stderr, "extent item %llu, found %llu\n", + (unsigned long long)rec->extent_item_refs, + (unsigned long long)rec->refs); + err = 1; + } + if (all_backpointers_checked(rec, 1)) { + fprintf(stderr, "backpointer mismatch on [%llu %llu]\n", + (unsigned long long)rec->start, + (unsigned long long)rec->nr); + + err = 1; + } + if (!rec->owner_ref_checked) { + fprintf(stderr, "owner ref check failed [%llu %llu]\n", + (unsigned long long)rec->start, + (unsigned long long)rec->nr); + err = 1; + } + + remove_cache_extent(extent_cache, cache); + free_all_extent_backrefs(rec); + free(rec); + } + return err; +} + +static int check_extents(struct btrfs_root *root) +{ + struct cache_tree extent_cache; + struct cache_tree seen; + struct cache_tree pending; + struct cache_tree reada; + struct cache_tree nodes; + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_key found_key; + int ret; + u64 last = 0; + struct block_info *bits; + int bits_nr; + struct extent_buffer *leaf; + int slot; + struct btrfs_root_item ri; + + cache_tree_init(&extent_cache); + cache_tree_init(&seen); + cache_tree_init(&pending); + cache_tree_init(&nodes); + cache_tree_init(&reada); + + bits_nr = 1024; + bits = malloc(bits_nr * sizeof(struct block_info)); + if (!bits) { + perror("malloc"); + exit(1); + } + + add_root_to_pending(root->fs_info->tree_root->node, bits, bits_nr, + &extent_cache, &pending, &seen, &reada, &nodes, + &root->fs_info->tree_root->root_key); + + add_root_to_pending(root->fs_info->chunk_root->node, bits, bits_nr, + &extent_cache, &pending, &seen, &reada, &nodes, + &root->fs_info->chunk_root->root_key); + + btrfs_init_path(&path); + key.offset = 0; + key.objectid = 0; + btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); + ret = btrfs_search_slot(NULL, root->fs_info->tree_root, + &key, &path, 0, 0); + BUG_ON(ret < 0); + while(1) { + leaf = path.nodes[0]; + slot = path.slots[0]; + if (slot >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); + if (ret != 0) + break; + leaf = path.nodes[0]; + slot = path.slots[0]; + } + btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); + if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { + unsigned long offset; + struct extent_buffer *buf; + + offset = btrfs_item_ptr_offset(leaf, path.slots[0]); + read_extent_buffer(leaf, &ri, offset, sizeof(ri)); + buf = read_tree_block(root->fs_info->tree_root, + btrfs_root_bytenr(&ri), + btrfs_level_size(root, + btrfs_root_level(&ri)), 0); + add_root_to_pending(buf, bits, bits_nr, &extent_cache, + &pending, &seen, &reada, &nodes, + &found_key); + free_extent_buffer(buf); + } + path.slots[0]++; + } + btrfs_release_path(root, &path); + while(1) { + ret = run_next_block(root, bits, bits_nr, &last, &pending, + &seen, &reada, &nodes, &extent_cache); + if (ret != 0) + break; + } + ret = check_extent_refs(root, &extent_cache); + return ret; +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfsck dev\n"); + fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); + exit(1); +} + +int main(int ac, char **av) +{ + struct cache_tree root_cache; + struct btrfs_root *root; + int ret; + + if (ac < 2) + print_usage(); + + radix_tree_init(); + cache_tree_init(&root_cache); + root = open_ctree(av[1], 0, 0); + + if (root == NULL) + return 1; + + ret = check_extents(root); + if (ret) + goto out; + ret = check_fs_roots(root, &root_cache); + if (ret) + goto out; + + ret = check_root_refs(root, &root_cache); +out: + free_root_recs(&root_cache); + close_ctree(root); + + if (found_old_backref) { + /* + * there was a disk format change when mixed + * backref was in testing tree. The old format + * existed about one week. + */ + printf("\n * Found old mixed backref format. " + "The old format is not supported! *" + "\n * Please mount the FS in readonly mode, " + "backup data and re-format the FS. *\n\n"); + ret = 1; + } + printf("found %llu bytes used err is %d\n", + (unsigned long long)bytes_used, ret); + printf("total csum bytes: %llu\n",(unsigned long long)total_csum_bytes); + printf("total tree bytes: %llu\n", + (unsigned long long)total_btree_bytes); + printf("total fs tree bytes: %llu\n", + (unsigned long long)total_fs_tree_bytes); + printf("btree space waste bytes: %llu\n", + (unsigned long long)btree_space_waste); + printf("file data blocks allocated: %llu\n referenced %llu\n", + (unsigned long long)data_bytes_allocated, + (unsigned long long)data_bytes_referenced); + printf("%s\n", BTRFS_BUILD_VERSION); + return ret; +} diff --git a/misc/btrfsctl.c b/misc/btrfsctl.c new file mode 100644 index 0000000..66c4e89 --- /dev/null +++ b/misc/btrfsctl.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2007 Oracle. 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. + */ + +#ifndef __CHECKER__ +#include +#include +#include "ioctl.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" + +#ifdef __CHECKER__ +#define BLKGETSIZE64 0 +#define BTRFS_IOC_SNAP_CREATE 0 +#define BTRFS_VOL_NAME_MAX 255 +struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; }; +static inline int ioctl(int fd, int define, void *arg) { return 0; } +#endif + +static void print_usage(void) +{ + printf("usage: btrfsctl [ -d file|dir] [ -s snap_name subvol|tree ]\n"); + printf(" [-r size] [-A device] [-a] [-c] [-D dir .]\n"); + printf("\t-d filename: defragments one file\n"); + printf("\t-d directory: defragments the entire Btree\n"); + printf("\t-s snap_name dir: creates a new snapshot of dir\n"); + printf("\t-S subvol_name dir: creates a new subvolume\n"); + printf("\t-r [+-]size[gkm]: resize the FS by size amount\n"); + printf("\t-A device: scans the device file for a Btrfs filesystem\n"); + printf("\t-a: scans all devices for Btrfs filesystems\n"); + printf("\t-c: forces a single FS sync\n"); + printf("\t-D: delete snapshot\n"); + printf("%s\n", BTRFS_BUILD_VERSION); + exit(1); +} + +static int open_file_or_dir(const char *fname) +{ + int ret; + struct stat st; + DIR *dirstream; + int fd; + + ret = stat(fname, &st); + if (ret < 0) { + perror("stat:"); + exit(1); + } + if (S_ISDIR(st.st_mode)) { + dirstream = opendir(fname); + if (!dirstream) { + perror("opendir"); + exit(1); + } + fd = dirfd(dirstream); + } else { + fd = open(fname, O_RDWR); + } + if (fd < 0) { + perror("open"); + exit(1); + } + return fd; +} +int main(int ac, char **av) +{ + char *fname = NULL; + char *snap_location = NULL; + int snap_fd = 0; + int fd; + int ret; + struct btrfs_ioctl_vol_args args; + char *name = NULL; + int i; + unsigned long command = 0; + int len; + char *fullpath; + + if (ac == 2 && strcmp(av[1], "-a") == 0) { + fprintf(stderr, "Scanning for Btrfs filesystems\n"); + btrfs_scan_one_dir("/dev", 1); + exit(0); + } + for (i = 1; i < ac; i++) { + if (strcmp(av[i], "-s") == 0) { + if (i + 1 >= ac - 1) { + fprintf(stderr, "-s requires an arg"); + print_usage(); + } + fullpath = av[i + 1]; + + snap_location = strdup(fullpath); + snap_location = dirname(snap_location); + + snap_fd = open_file_or_dir(snap_location); + + name = strdup(fullpath); + name = basename(name); + len = strlen(name); + + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, + "snapshot name zero length or too long\n"); + exit(1); + } + if (strchr(name, '/')) { + fprintf(stderr, + "error: / not allowed in names\n"); + exit(1); + } + command = BTRFS_IOC_SNAP_CREATE; + } else if (strcmp(av[i], "-S") == 0) { + if (i + 1 >= ac - 1) { + fprintf(stderr, "-S requires an arg"); + print_usage(); + } + name = av[i + 1]; + len = strlen(name); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, + "snapshot name zero length or too long\n"); + exit(1); + } + if (strchr(name, '/')) { + fprintf(stderr, + "error: / not allowed in names\n"); + exit(1); + } + command = BTRFS_IOC_SUBVOL_CREATE; + } else if (strcmp(av[i], "-d") == 0) { + if (i >= ac - 1) { + fprintf(stderr, "-d requires an arg\n"); + print_usage(); + } + command = BTRFS_IOC_DEFRAG; + } else if (strcmp(av[i], "-D") == 0) { + if (i >= ac - 1) { + fprintf(stderr, "-D requires an arg\n"); + print_usage(); + } + command = BTRFS_IOC_SNAP_DESTROY; + name = av[i + 1]; + len = strlen(name); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "-D size too long\n"); + exit(1); + } + } else if (strcmp(av[i], "-A") == 0) { + if (i >= ac - 1) { + fprintf(stderr, "-A requires an arg\n"); + print_usage(); + } + command = BTRFS_IOC_SCAN_DEV; + } else if (strcmp(av[i], "-r") == 0) { + if (i >= ac - 1) { + fprintf(stderr, "-r requires an arg\n"); + print_usage(); + } + name = av[i + 1]; + len = strlen(name); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "-r size too long\n"); + exit(1); + } + command = BTRFS_IOC_RESIZE; + } else if (strcmp(av[i], "-c") == 0) { + command = BTRFS_IOC_SYNC; + } + } + if (command == 0) { + fprintf(stderr, "no valid commands given\n"); + print_usage(); + exit(1); + } + fname = av[ac - 1]; + + if (command == BTRFS_IOC_SCAN_DEV) { + fd = open("/dev/btrfs-control", O_RDWR); + if (fd < 0) { + perror("failed to open /dev/btrfs-control"); + exit(1); + } + name = fname; + } else { + fd = open_file_or_dir(fname); + } + + if (name) + strcpy(args.name, name); + else + args.name[0] = '\0'; + + if (command == BTRFS_IOC_SNAP_CREATE) { + args.fd = fd; + ret = ioctl(snap_fd, command, &args); + } else + ret = ioctl(fd, command, &args); + if (ret < 0) { + perror("ioctl:"); + exit(1); + } + if (ret == 0) { + printf("operation complete\n"); + } else { + printf("ioctl failed with error %d\n", ret); + } + printf("%s\n", BTRFS_BUILD_VERSION); + if (ret) + exit(0); + else + exit(1); +} + diff --git a/misc/btrfstune.c b/misc/btrfstune.c new file mode 100644 index 0000000..47830c5 --- /dev/null +++ b/misc/btrfstune.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2008 Oracle. 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 +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "disk-io.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" + +static char *device; + +int update_seeding_flag(struct btrfs_root *root, int set_flag) +{ + struct btrfs_trans_handle *trans; + struct btrfs_super_block *disk_super; + u64 super_flags; + + disk_super = &root->fs_info->super_copy; + super_flags = btrfs_super_flags(disk_super); + if (set_flag) { + if (super_flags & BTRFS_SUPER_FLAG_SEEDING) { + fprintf(stderr, "seeding flag is already set on %s\n", + device); + return 1; + } + super_flags |= BTRFS_SUPER_FLAG_SEEDING; + } else { + if (!(super_flags & BTRFS_SUPER_FLAG_SEEDING)) { + fprintf(stderr, "seeding flag is not set on %s\n", + device); + return 1; + } + super_flags &= ~BTRFS_SUPER_FLAG_SEEDING; + } + + trans = btrfs_start_transaction(root, 1); + btrfs_set_super_flags(disk_super, super_flags); + btrfs_commit_transaction(trans, root); + + return 0; +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfstune [options] device\n"); + fprintf(stderr, "\t-S value\tenable/disable seeding\n"); +} + +int main(int argc, char *argv[]) +{ + struct btrfs_root *root; + int success = 0; + int seeding_flag = 0; + int seeding_value = 0; + int ret; + + while(1) { + int c = getopt(argc, argv, "S:"); + if (c < 0) + break; + switch(c) { + case 'S': + seeding_flag = 1; + seeding_value = atoi(optarg); + break; + default: + print_usage(); + return 1; + } + } + + argc = argc - optind; + device = argv[optind]; + if (argc != 1) { + print_usage(); + return 1; + } + + if (check_mounted(device)) { + fprintf(stderr, "%s is mounted\n", device); + return 1; + } + + root = open_ctree(device, 0, 1); + + if (seeding_flag) { + ret = update_seeding_flag(root, seeding_value); + if (!ret) + success++; + } + + if (success > 0) { + ret = 0; + } else { + root->fs_info->readonly = 1; + ret = 1; + } + close_ctree(root); + + return ret; +} diff --git a/misc/convert.c b/misc/convert.c new file mode 100644 index 0000000..d037c98 --- /dev/null +++ b/misc/convert.c @@ -0,0 +1,2872 @@ +/* + * Copyright (C) 2007 Oracle. 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 600 +#define _GNU_SOURCE 1 +#ifndef __CHECKER__ +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "disk-io.h" +#include "volumes.h" +#include "transaction.h" +#include "crc32c.h" +#include "utils.h" +#include +#include +#include + +#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO) +#define STRIPE_LEN (64 * 1024) +#define EXT2_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID + +/* + * Open Ext2fs in readonly mode, read block allocation bitmap and + * inode bitmap into memory. + */ +static int open_ext2fs(const char *name, ext2_filsys *ret_fs) +{ + errcode_t ret; + ext2_filsys ext2_fs; + ext2_ino_t ino; + ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs); + if (ret) { + fprintf(stderr, "ext2fs_open: %s\n", error_message(ret)); + goto fail; + } + ret = ext2fs_read_inode_bitmap(ext2_fs); + if (ret) { + fprintf(stderr, "ext2fs_read_inode_bitmap: %s\n", + error_message(ret)); + goto fail; + } + ret = ext2fs_read_block_bitmap(ext2_fs); + if (ret) { + fprintf(stderr, "ext2fs_read_block_bitmap: %s\n", + error_message(ret)); + goto fail; + } + /* + * search each block group for a free inode. this set up + * uninit block/inode bitmaps appropriately. + */ + ino = 1; + while (ino <= ext2_fs->super->s_inodes_count) { + ext2_ino_t foo; + ext2fs_new_inode(ext2_fs, ino, 0, NULL, &foo); + ino += EXT2_INODES_PER_GROUP(ext2_fs->super); + } + + *ret_fs = ext2_fs; + return 0; +fail: + return -1; +} + +static int close_ext2fs(ext2_filsys fs) +{ + ext2fs_close(fs); + return 0; +} + +static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret) +{ + blk_t block; + + if (!ext2fs_new_block(fs, goal, NULL, &block)) { + ext2fs_fast_mark_block_bitmap(fs->block_map, block); + *block_ret = block; + return 0; + } + return -ENOSPC; +} + +static int ext2_free_block(ext2_filsys fs, u64 block) +{ + BUG_ON(block != (blk_t)block); + ext2fs_fast_unmark_block_bitmap(fs->block_map, block); + return 0; +} + +static int cache_free_extents(struct btrfs_root *root, ext2_filsys ext2_fs) + +{ + int i, ret = 0; + blk_t block; + u64 bytenr; + u64 blocksize = ext2_fs->blocksize; + + block = ext2_fs->super->s_first_data_block; + for (; block < ext2_fs->super->s_blocks_count; block++) { + if (ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block)) + continue; + bytenr = block * blocksize; + ret = set_extent_dirty(&root->fs_info->free_space_cache, + bytenr, bytenr + blocksize - 1, 0); + BUG_ON(ret); + } + + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + bytenr = btrfs_sb_offset(i); + bytenr &= ~((u64)STRIPE_LEN - 1); + if (bytenr >= blocksize * ext2_fs->super->s_blocks_count) + break; + clear_extent_dirty(&root->fs_info->free_space_cache, bytenr, + bytenr + STRIPE_LEN - 1, 0); + } + + clear_extent_dirty(&root->fs_info->free_space_cache, + 0, BTRFS_SUPER_INFO_OFFSET - 1, 0); + + return 0; +} + +static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes, + u64 hint_byte, struct btrfs_key *ins) +{ + u64 start; + u64 end; + u64 last = hint_byte; + int ret; + int wrapped = 0; + struct btrfs_block_group_cache *cache; + + while(1) { + ret = find_first_extent_bit(&root->fs_info->free_space_cache, + last, &start, &end, EXTENT_DIRTY); + if (ret) { + if (wrapped++ == 0) { + last = 0; + continue; + } else { + goto fail; + } + } + + start = max(last, start); + last = end + 1; + if (last - start < num_bytes) + continue; + + last = start + num_bytes; + if (test_range_bit(&root->fs_info->pinned_extents, + start, last - 1, EXTENT_DIRTY, 0)) + continue; + + cache = btrfs_lookup_block_group(root->fs_info, start); + BUG_ON(!cache); + if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM || + last > cache->key.objectid + cache->key.offset) { + last = cache->key.objectid + cache->key.offset; + continue; + } + + clear_extent_dirty(&root->fs_info->free_space_cache, + start, start + num_bytes - 1, 0); + + ins->objectid = start; + ins->offset = num_bytes; + ins->type = BTRFS_EXTENT_ITEM_KEY; + return 0; + } +fail: + fprintf(stderr, "not enough free space\n"); + return -ENOSPC; +} + +static int intersect_with_sb(u64 bytenr, u64 num_bytes) +{ + int i; + u64 offset; + + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + offset = btrfs_sb_offset(i); + offset &= ~((u64)STRIPE_LEN - 1); + + if (bytenr < offset + STRIPE_LEN && + bytenr + num_bytes > offset) + return 1; + } + return 0; +} + +static int custom_free_extent(struct btrfs_root *root, u64 bytenr, + u64 num_bytes) +{ + return intersect_with_sb(bytenr, num_bytes); +} + +struct btrfs_extent_ops extent_ops = { + .alloc_extent = custom_alloc_extent, + .free_extent = custom_free_extent, +}; + +struct dir_iterate_data { + struct btrfs_trans_handle *trans; + struct btrfs_root *root; + struct btrfs_inode_item *inode; + u64 objectid; + u64 index_cnt; + u64 parent; + int errcode; +}; + +static u8 filetype_conversion_table[EXT2_FT_MAX] = { + [EXT2_FT_UNKNOWN] = BTRFS_FT_UNKNOWN, + [EXT2_FT_REG_FILE] = BTRFS_FT_REG_FILE, + [EXT2_FT_DIR] = BTRFS_FT_DIR, + [EXT2_FT_CHRDEV] = BTRFS_FT_CHRDEV, + [EXT2_FT_BLKDEV] = BTRFS_FT_BLKDEV, + [EXT2_FT_FIFO] = BTRFS_FT_FIFO, + [EXT2_FT_SOCK] = BTRFS_FT_SOCK, + [EXT2_FT_SYMLINK] = BTRFS_FT_SYMLINK, +}; + +static int dir_iterate_proc(ext2_ino_t dir, int entry, + struct ext2_dir_entry *old, + int offset, int blocksize, + char *buf,void *priv_data) +{ + int ret; + int file_type; + u64 objectid; + u64 inode_size; + char dotdot[] = ".."; + struct btrfs_key location; + struct ext2_dir_entry_2 *dirent = (struct ext2_dir_entry_2 *)old; + struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data; + + objectid = dirent->inode + INO_OFFSET; + if (!strncmp(dirent->name, dotdot, dirent->name_len)) { + if (dirent->name_len == 2) { + BUG_ON(idata->parent != 0); + idata->parent = objectid; + } + return 0; + } + if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO) + return 0; + + location.objectid = objectid; + location.offset = 0; + btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); + + file_type = dirent->file_type; + BUG_ON(file_type > EXT2_FT_SYMLINK); + ret = btrfs_insert_dir_item(idata->trans, idata->root, + dirent->name, dirent->name_len, + idata->objectid, &location, + filetype_conversion_table[file_type], + idata->index_cnt); + if (ret) + goto fail; + ret = btrfs_insert_inode_ref(idata->trans, idata->root, + dirent->name, dirent->name_len, + objectid, idata->objectid, + idata->index_cnt); + if (ret) + goto fail; + idata->index_cnt++; + inode_size = btrfs_stack_inode_size(idata->inode) + + dirent->name_len * 2; + btrfs_set_stack_inode_size(idata->inode, inode_size); + return 0; +fail: + idata->errcode = ret; + return BLOCK_ABORT; +} + +static int create_dir_entries(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *btrfs_inode, + ext2_filsys ext2_fs, ext2_ino_t ext2_ino) +{ + int ret; + errcode_t err; + struct dir_iterate_data data = { + .trans = trans, + .root = root, + .inode = btrfs_inode, + .objectid = objectid, + .index_cnt = 2, + .parent = 0, + .errcode = 0, + }; + + err = ext2fs_dir_iterate2(ext2_fs, ext2_ino, 0, NULL, + dir_iterate_proc, &data); + if (err) + goto error; + ret = data.errcode; + if (ret == 0 && data.parent == objectid) { + ret = btrfs_insert_inode_ref(trans, root, "..", 2, + objectid, objectid, 0); + } + return ret; +error: + fprintf(stderr, "ext2fs_dir_iterate2: %s\n", error_message(err)); + return -1; +} + +static int read_disk_extent(struct btrfs_root *root, u64 bytenr, + u32 num_bytes, char *buffer) +{ + int ret; + struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; + + ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr); + if (ret != num_bytes) + goto fail; + ret = 0; +fail: + if (ret > 0) + ret = -1; + return ret; +} +/* + * Record a file extent. Do all the required works, such as inserting + * file extent item, inserting extent item and backref item into extent + * tree and updating block accounting. + */ +static int record_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 file_pos, u64 disk_bytenr, + u64 num_bytes, int checksum) +{ + int ret; + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_root *extent_root = info->extent_root; + struct extent_buffer *leaf; + struct btrfs_file_extent_item *fi; + struct btrfs_key ins_key; + struct btrfs_path path; + struct btrfs_extent_item *ei; + u32 blocksize = root->sectorsize; + u64 nbytes; + + if (disk_bytenr == 0) { + ret = btrfs_insert_file_extent(trans, root, objectid, + file_pos, disk_bytenr, + num_bytes, num_bytes); + return ret; + } + + btrfs_init_path(&path); + + if (checksum) { + u64 offset; + char *buffer; + + ret = -ENOMEM; + buffer = malloc(blocksize); + if (!buffer) + goto fail; + for (offset = 0; offset < num_bytes; offset += blocksize) { + ret = read_disk_extent(root, disk_bytenr + offset, + blocksize, buffer); + if (ret) + break; + ret = btrfs_csum_file_block(trans, + root->fs_info->csum_root, + disk_bytenr + num_bytes, + disk_bytenr + offset, + buffer, blocksize); + if (ret) + break; + } + free(buffer); + if (ret) + goto fail; + } + + ins_key.objectid = objectid; + ins_key.offset = file_pos; + btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY); + ret = btrfs_insert_empty_item(trans, root, &path, &ins_key, + sizeof(*fi)); + if (ret) + goto fail; + leaf = path.nodes[0]; + fi = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_file_extent_item); + btrfs_set_file_extent_generation(leaf, fi, trans->transid); + btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); + btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr); + btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes); + btrfs_set_file_extent_offset(leaf, fi, 0); + btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes); + btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes); + btrfs_set_file_extent_compression(leaf, fi, 0); + btrfs_set_file_extent_encryption(leaf, fi, 0); + btrfs_set_file_extent_other_encoding(leaf, fi, 0); + btrfs_mark_buffer_dirty(leaf); + + nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes; + btrfs_set_stack_inode_nbytes(inode, nbytes); + + btrfs_release_path(root, &path); + + ins_key.objectid = disk_bytenr; + ins_key.offset = num_bytes; + ins_key.type = BTRFS_EXTENT_ITEM_KEY; + + ret = btrfs_insert_empty_item(trans, extent_root, &path, + &ins_key, sizeof(*ei)); + if (ret == 0) { + leaf = path.nodes[0]; + ei = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_extent_item); + + btrfs_set_extent_refs(leaf, ei, 0); + btrfs_set_extent_generation(leaf, ei, 0); + btrfs_set_extent_flags(leaf, ei, BTRFS_EXTENT_FLAG_DATA); + + btrfs_mark_buffer_dirty(leaf); + + ret = btrfs_update_block_group(trans, root, disk_bytenr, + num_bytes, 1, 0); + if (ret) + goto fail; + } else if (ret != -EEXIST) { + goto fail; + } + btrfs_extent_post_op(trans, extent_root); + + ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0, + root->root_key.objectid, + objectid, file_pos); + if (ret) + goto fail; + ret = 0; +fail: + btrfs_release_path(root, &path); + return ret; +} + +static int record_file_blocks(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 file_block, u64 disk_block, + u64 num_blocks, int checksum) +{ + u64 file_pos = file_block * root->sectorsize; + u64 disk_bytenr = disk_block * root->sectorsize; + u64 num_bytes = num_blocks * root->sectorsize; + return record_file_extent(trans, root, objectid, inode, file_pos, + disk_bytenr, num_bytes, checksum); +} + +struct blk_iterate_data { + struct btrfs_trans_handle *trans; + struct btrfs_root *root; + struct btrfs_inode_item *inode; + u64 objectid; + u64 first_block; + u64 disk_block; + u64 num_blocks; + u64 boundary; + int checksum; + int errcode; +}; + +static int block_iterate_proc(ext2_filsys ext2_fs, + u64 disk_block, u64 file_block, + struct blk_iterate_data *idata) +{ + int ret; + int sb_region; + int do_barrier; + struct btrfs_root *root = idata->root; + struct btrfs_trans_handle *trans = idata->trans; + struct btrfs_block_group_cache *cache; + u64 bytenr = disk_block * root->sectorsize; + + sb_region = intersect_with_sb(bytenr, root->sectorsize); + do_barrier = sb_region || disk_block >= idata->boundary; + if ((idata->num_blocks > 0 && do_barrier) || + (file_block > idata->first_block + idata->num_blocks) || + (disk_block != idata->disk_block + idata->num_blocks)) { + if (idata->num_blocks > 0) { + ret = record_file_blocks(trans, root, idata->objectid, + idata->inode, idata->first_block, + idata->disk_block, idata->num_blocks, + idata->checksum); + if (ret) + goto fail; + idata->first_block += idata->num_blocks; + idata->num_blocks = 0; + } + if (file_block > idata->first_block) { + ret = record_file_blocks(trans, root, idata->objectid, + idata->inode, idata->first_block, + 0, file_block - idata->first_block, + idata->checksum); + if (ret) + goto fail; + } + + if (sb_region) { + bytenr += STRIPE_LEN - 1; + bytenr &= ~((u64)STRIPE_LEN - 1); + } else { + cache = btrfs_lookup_block_group(root->fs_info, bytenr); + BUG_ON(!cache); + bytenr = cache->key.objectid + cache->key.offset; + } + + idata->first_block = file_block; + idata->disk_block = disk_block; + idata->boundary = bytenr / root->sectorsize; + } + idata->num_blocks++; + return 0; +fail: + idata->errcode = ret; + return BLOCK_ABORT; +} + +static int __block_iterate_proc(ext2_filsys fs, blk_t *blocknr, + e2_blkcnt_t blockcnt, blk_t ref_block, + int ref_offset, void *priv_data) +{ + struct blk_iterate_data *idata; + idata = (struct blk_iterate_data *)priv_data; + return block_iterate_proc(fs, *blocknr, blockcnt, idata); +} + +/* + * traverse file's data blocks, record these data blocks as file extents. + */ +static int create_file_extents(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *btrfs_inode, + ext2_filsys ext2_fs, ext2_ino_t ext2_ino, + int datacsum, int packing) +{ + int ret; + char *buffer = NULL; + errcode_t err; + u32 last_block; + u32 sectorsize = root->sectorsize; + u64 inode_size = btrfs_stack_inode_size(btrfs_inode); + struct blk_iterate_data data = { + .trans = trans, + .root = root, + .inode = btrfs_inode, + .objectid = objectid, + .first_block = 0, + .disk_block = 0, + .num_blocks = 0, + .boundary = (u64)-1, + .checksum = datacsum, + .errcode = 0, + }; + err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY, + NULL, __block_iterate_proc, &data); + if (err) + goto error; + ret = data.errcode; + if (ret) + goto fail; + if (packing && data.first_block == 0 && data.num_blocks > 0 && + inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) { + u64 num_bytes = data.num_blocks * sectorsize; + u64 disk_bytenr = data.disk_block * sectorsize; + u64 nbytes; + + buffer = malloc(num_bytes); + if (!buffer) + return -ENOMEM; + ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer); + if (ret) + goto fail; + if (num_bytes > inode_size) + num_bytes = inode_size; + ret = btrfs_insert_inline_extent(trans, root, objectid, + 0, buffer, num_bytes); + if (ret) + goto fail; + nbytes = btrfs_stack_inode_nbytes(btrfs_inode) + num_bytes; + btrfs_set_stack_inode_nbytes(btrfs_inode, nbytes); + } else if (data.num_blocks > 0) { + ret = record_file_blocks(trans, root, objectid, btrfs_inode, + data.first_block, data.disk_block, + data.num_blocks, data.checksum); + if (ret) + goto fail; + } + data.first_block += data.num_blocks; + last_block = (inode_size + sectorsize - 1) / sectorsize; + if (last_block > data.first_block) { + ret = record_file_blocks(trans, root, objectid, btrfs_inode, + data.first_block, 0, last_block - + data.first_block, data.checksum); + } +fail: + if (buffer) + free(buffer); + return ret; +error: + fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err)); + return -1; +} + +static int create_symbol_link(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *btrfs_inode, + ext2_filsys ext2_fs, ext2_ino_t ext2_ino, + struct ext2_inode *ext2_inode) +{ + int ret; + char *pathname; + u64 inode_size = btrfs_stack_inode_size(btrfs_inode); + if (ext2fs_inode_data_blocks(ext2_fs, ext2_inode)) { + btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1); + ret = create_file_extents(trans, root, objectid, btrfs_inode, + ext2_fs, ext2_ino, 1, 1); + btrfs_set_stack_inode_size(btrfs_inode, inode_size); + return ret; + } + + pathname = (char *)&(ext2_inode->i_block[0]); + BUG_ON(pathname[inode_size] != 0); + ret = btrfs_insert_inline_extent(trans, root, objectid, 0, + pathname, inode_size + 1); + btrfs_set_stack_inode_nbytes(btrfs_inode, inode_size + 1); + return ret; +} + +/* + * Following xattr/acl related codes are based on codes in + * fs/ext3/xattr.c and fs/ext3/acl.c + */ +#define EXT2_XATTR_BHDR(ptr) ((struct ext2_ext_attr_header *)(ptr)) +#define EXT2_XATTR_BFIRST(ptr) \ + ((struct ext2_ext_attr_entry *)(EXT2_XATTR_BHDR(ptr) + 1)) +#define EXT2_XATTR_IHDR(inode) \ + ((struct ext2_ext_attr_header *) ((void *)(inode) + \ + EXT2_GOOD_OLD_INODE_SIZE + (inode)->i_extra_isize)) +#define EXT2_XATTR_IFIRST(inode) \ + ((struct ext2_ext_attr_entry *) ((void *)EXT2_XATTR_IHDR(inode) + \ + sizeof(EXT2_XATTR_IHDR(inode)->h_magic))) + +static int ext2_xattr_check_names(struct ext2_ext_attr_entry *entry, + const void *end) +{ + struct ext2_ext_attr_entry *next; + + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { + next = EXT2_EXT_ATTR_NEXT(entry); + if ((void *)next >= end) + return -EIO; + entry = next; + } + return 0; +} + +static int ext2_xattr_check_block(const char *buf, size_t size) +{ + int error; + struct ext2_ext_attr_header *header = EXT2_XATTR_BHDR(buf); + + if (header->h_magic != EXT2_EXT_ATTR_MAGIC || + header->h_blocks != 1) + return -EIO; + error = ext2_xattr_check_names(EXT2_XATTR_BFIRST(buf), buf + size); + return error; +} + +static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry, + size_t size) +{ + size_t value_size = entry->e_value_size; + + if (entry->e_value_block != 0 || value_size > size || + entry->e_value_offs + value_size > size) + return -EIO; + return 0; +} + +#define EXT2_ACL_VERSION 0x0001 + +typedef struct { + __le16 e_tag; + __le16 e_perm; + __le32 e_id; +} ext2_acl_entry; + +typedef struct { + __le16 e_tag; + __le16 e_perm; +} ext2_acl_entry_short; + +typedef struct { + __le32 a_version; +} ext2_acl_header; + +static inline int ext2_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(ext2_acl_header); + s = size - 4 * sizeof(ext2_acl_entry_short); + if (s < 0) { + if (size % sizeof(ext2_acl_entry_short)) + return -1; + return size / sizeof(ext2_acl_entry_short); + } else { + if (s % sizeof(ext2_acl_entry)) + return -1; + return s / sizeof(ext2_acl_entry) + 4; + } +} + +#define ACL_EA_VERSION 0x0002 + +typedef struct { + __le16 e_tag; + __le16 e_perm; + __le32 e_id; +} acl_ea_entry; + +typedef struct { + __le32 a_version; + acl_ea_entry a_entries[0]; +} acl_ea_header; + +static inline size_t acl_ea_size(int count) +{ + return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry); +} + +static int ext2_acl_to_xattr(void *dst, const void *src, + size_t dst_size, size_t src_size) +{ + int i, count; + const void *end = src + src_size; + acl_ea_header *ext_acl = (acl_ea_header *)dst; + acl_ea_entry *dst_entry = ext_acl->a_entries; + ext2_acl_entry *src_entry; + + if (src_size < sizeof(ext2_acl_header)) + goto fail; + if (((ext2_acl_header *)src)->a_version != + cpu_to_le32(EXT2_ACL_VERSION)) + goto fail; + src += sizeof(ext2_acl_header); + count = ext2_acl_count(src_size); + if (count <= 0) + goto fail; + + BUG_ON(dst_size < acl_ea_size(count)); + ext_acl->a_version = cpu_to_le32(ACL_EA_VERSION); + for (i = 0; i < count; i++, dst_entry++) { + src_entry = (ext2_acl_entry *)src; + if (src + sizeof(ext2_acl_entry_short) > end) + goto fail; + dst_entry->e_tag = src_entry->e_tag; + dst_entry->e_perm = src_entry->e_perm; + switch (le16_to_cpu(src_entry->e_tag)) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + src += sizeof(ext2_acl_entry_short); + dst_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); + break; + case ACL_USER: + case ACL_GROUP: + src += sizeof(ext2_acl_entry); + if (src > end) + goto fail; + dst_entry->e_id = src_entry->e_id; + break; + default: + goto fail; + } + } + if (src != end) + goto fail; + return 0; +fail: + return -EINVAL; +} + +static char *xattr_prefix_table[] = { + [1] = "user.", + [2] = "system.posix_acl_access", + [3] = "system.posix_acl_default", + [4] = "trusted.", + [6] = "security.", +}; + +static int copy_single_xattr(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct ext2_ext_attr_entry *entry, + const void *data, u32 datalen) +{ + int ret = 0; + int name_len; + int name_index; + void *databuf = NULL; + char namebuf[XATTR_NAME_MAX + 1]; + + name_index = entry->e_name_index; + if (name_index >= ARRAY_SIZE(xattr_prefix_table) || + xattr_prefix_table[name_index] == NULL) + return -EOPNOTSUPP; + name_len = strlen(xattr_prefix_table[name_index]) + + entry->e_name_len; + if (name_len >= sizeof(namebuf)) + return -ERANGE; + + if (name_index == 2 || name_index == 3) { + size_t bufsize = acl_ea_size(ext2_acl_count(datalen)); + databuf = malloc(bufsize); + if (!databuf) + return -ENOMEM; + ret = ext2_acl_to_xattr(databuf, data, bufsize, datalen); + if (ret) + goto out; + data = databuf; + datalen = bufsize; + } + strcpy(namebuf, xattr_prefix_table[name_index]); + strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len); + if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) - + sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) { + fprintf(stderr, "skip large xattr on inode %Lu name %.*s\n", + objectid - INO_OFFSET, name_len, namebuf); + goto out; + } + ret = btrfs_insert_xattr_item(trans, root, namebuf, name_len, + data, datalen, objectid); +out: + if (databuf) + free(databuf); + return ret; +} + +static int copy_extended_attrs(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *btrfs_inode, + ext2_filsys ext2_fs, ext2_ino_t ext2_ino) +{ + int ret = 0; + int inline_ea = 0; + errcode_t err; + u32 datalen; + u32 block_size = ext2_fs->blocksize; + u32 inode_size = EXT2_INODE_SIZE(ext2_fs->super); + struct ext2_inode_large *ext2_inode; + struct ext2_ext_attr_entry *entry; + void *data; + char *buffer = NULL; + char inode_buf[EXT2_GOOD_OLD_INODE_SIZE]; + + if (inode_size <= EXT2_GOOD_OLD_INODE_SIZE) { + ext2_inode = (struct ext2_inode_large *)inode_buf; + } else { + ext2_inode = (struct ext2_inode_large *)malloc(inode_size); + if (!ext2_inode) + return -ENOMEM; + } + err = ext2fs_read_inode_full(ext2_fs, ext2_ino, (void *)ext2_inode, + inode_size); + if (err) { + fprintf(stderr, "ext2fs_read_inode_full: %s\n", + error_message(err)); + ret = -1; + goto out; + } + + if (ext2_ino > ext2_fs->super->s_first_ino && + inode_size > EXT2_GOOD_OLD_INODE_SIZE) { + if (EXT2_GOOD_OLD_INODE_SIZE + + ext2_inode->i_extra_isize > inode_size) { + ret = -EIO; + goto out; + } + if (ext2_inode->i_extra_isize != 0 && + EXT2_XATTR_IHDR(ext2_inode)->h_magic == + EXT2_EXT_ATTR_MAGIC) { + inline_ea = 1; + } + } + if (inline_ea) { + int total; + void *end = (void *)ext2_inode + inode_size; + entry = EXT2_XATTR_IFIRST(ext2_inode); + total = end - (void *)entry; + ret = ext2_xattr_check_names(entry, end); + if (ret) + goto out; + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { + ret = ext2_xattr_check_entry(entry, total); + if (ret) + goto out; + data = (void *)EXT2_XATTR_IFIRST(ext2_inode) + + entry->e_value_offs; + datalen = entry->e_value_size; + ret = copy_single_xattr(trans, root, objectid, + entry, data, datalen); + if (ret) + goto out; + entry = EXT2_EXT_ATTR_NEXT(entry); + } + } + + if (ext2_inode->i_file_acl == 0) + goto out; + + buffer = malloc(block_size); + if (!buffer) { + ret = -ENOMEM; + goto out; + } + err = ext2fs_read_ext_attr(ext2_fs, ext2_inode->i_file_acl, buffer); + if (err) { + fprintf(stderr, "ext2fs_read_ext_attr: %s\n", + error_message(err)); + ret = -1; + goto out; + } + ret = ext2_xattr_check_block(buffer, block_size); + if (ret) + goto out; + + entry = EXT2_XATTR_BFIRST(buffer); + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { + ret = ext2_xattr_check_entry(entry, block_size); + if (ret) + goto out; + data = buffer + entry->e_value_offs; + datalen = entry->e_value_size; + ret = copy_single_xattr(trans, root, objectid, + entry, data, datalen); + if (ret) + goto out; + entry = EXT2_EXT_ATTR_NEXT(entry); + } +out: + if (buffer != NULL) + free(buffer); + if ((void *)ext2_inode != inode_buf) + free(ext2_inode); + return ret; +} +#define MINORBITS 20 +#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) + +static inline dev_t old_decode_dev(u16 val) +{ + return MKDEV((val >> 8) & 255, val & 255); +} + +static inline dev_t new_decode_dev(u32 dev) +{ + unsigned major = (dev & 0xfff00) >> 8; + unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); + return MKDEV(major, minor); +} + +static int copy_inode_item(struct btrfs_inode_item *dst, + struct ext2_inode *src, u32 blocksize) +{ + btrfs_set_stack_inode_generation(dst, 1); + btrfs_set_stack_inode_size(dst, src->i_size); + btrfs_set_stack_inode_nbytes(dst, 0); + btrfs_set_stack_inode_block_group(dst, 0); + btrfs_set_stack_inode_nlink(dst, src->i_links_count); + btrfs_set_stack_inode_uid(dst, src->i_uid | (src->i_uid_high << 16)); + btrfs_set_stack_inode_gid(dst, src->i_gid | (src->i_gid_high << 16)); + btrfs_set_stack_inode_mode(dst, src->i_mode); + btrfs_set_stack_inode_rdev(dst, 0); + btrfs_set_stack_inode_flags(dst, 0); + btrfs_set_stack_timespec_sec(&dst->atime, src->i_atime); + btrfs_set_stack_timespec_nsec(&dst->atime, 0); + btrfs_set_stack_timespec_sec(&dst->ctime, src->i_ctime); + btrfs_set_stack_timespec_nsec(&dst->ctime, 0); + btrfs_set_stack_timespec_sec(&dst->mtime, src->i_mtime); + btrfs_set_stack_timespec_nsec(&dst->mtime, 0); + btrfs_set_stack_timespec_sec(&dst->otime, 0); + btrfs_set_stack_timespec_nsec(&dst->otime, 0); + + if (S_ISDIR(src->i_mode)) { + btrfs_set_stack_inode_size(dst, 0); + btrfs_set_stack_inode_nlink(dst, 1); + } + if (S_ISREG(src->i_mode)) { + btrfs_set_stack_inode_size(dst, (u64)src->i_size_high << 32 | + (u64)src->i_size); + } + if (!S_ISREG(src->i_mode) && !S_ISDIR(src->i_mode) && + !S_ISLNK(src->i_mode)) { + if (src->i_block[0]) { + btrfs_set_stack_inode_rdev(dst, + old_decode_dev(src->i_block[0])); + } else { + btrfs_set_stack_inode_rdev(dst, + new_decode_dev(src->i_block[1])); + } + } + return 0; +} + +/* + * copy a single inode. do all the required works, such as cloning + * inode item, creating file extents and creating directory entries. + */ +static int copy_single_inode(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + ext2_filsys ext2_fs, ext2_ino_t ext2_ino, + struct ext2_inode *ext2_inode, + int datacsum, int packing, int noxattr) +{ + int ret; + struct btrfs_key inode_key; + struct btrfs_inode_item btrfs_inode; + + if (ext2_inode->i_links_count == 0) + return 0; + + copy_inode_item(&btrfs_inode, ext2_inode, ext2_fs->blocksize); + if (!datacsum && S_ISREG(ext2_inode->i_mode)) { + u32 flags = btrfs_stack_inode_flags(&btrfs_inode) | + BTRFS_INODE_NODATASUM; + btrfs_set_stack_inode_flags(&btrfs_inode, flags); + } + + switch (ext2_inode->i_mode & S_IFMT) { + case S_IFREG: + ret = create_file_extents(trans, root, objectid, &btrfs_inode, + ext2_fs, ext2_ino, datacsum, packing); + break; + case S_IFDIR: + ret = create_dir_entries(trans, root, objectid, &btrfs_inode, + ext2_fs, ext2_ino); + break; + case S_IFLNK: + ret = create_symbol_link(trans, root, objectid, &btrfs_inode, + ext2_fs, ext2_ino, ext2_inode); + break; + default: + ret = 0; + break; + } + if (ret) + return ret; + + if (!noxattr) { + ret = copy_extended_attrs(trans, root, objectid, &btrfs_inode, + ext2_fs, ext2_ino); + if (ret) + return ret; + } + inode_key.objectid = objectid; + inode_key.offset = 0; + btrfs_set_key_type(&inode_key, BTRFS_INODE_ITEM_KEY); + ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); + return ret; +} + +static int copy_disk_extent(struct btrfs_root *root, u64 dst_bytenr, + u64 src_bytenr, u32 num_bytes) +{ + int ret; + char *buffer; + struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; + + buffer = malloc(num_bytes); + if (!buffer) + return -ENOMEM; + ret = pread(fs_devs->latest_bdev, buffer, num_bytes, src_bytenr); + if (ret != num_bytes) + goto fail; + ret = pwrite(fs_devs->latest_bdev, buffer, num_bytes, dst_bytenr); + if (ret != num_bytes) + goto fail; + ret = 0; +fail: + free(buffer); + if (ret > 0) + ret = -1; + return ret; +} +/* + * scan ext2's inode bitmap and copy all used inode. + */ +static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs, + int datacsum, int packing, int noxattr) +{ + int ret; + errcode_t err; + ext2_inode_scan ext2_scan; + struct ext2_inode ext2_inode; + ext2_ino_t ext2_ino; + u64 objectid; + struct btrfs_trans_handle *trans; + + trans = btrfs_start_transaction(root, 1); + if (!trans) + return -ENOMEM; + err = ext2fs_open_inode_scan(ext2_fs, 0, &ext2_scan); + if (err) { + fprintf(stderr, "ext2fs_open_inode_scan: %s\n", error_message(err)); + return -1; + } + while (!(err = ext2fs_get_next_inode(ext2_scan, &ext2_ino, + &ext2_inode))) { + /* no more inodes */ + if (ext2_ino == 0) + break; + /* skip special inode in ext2fs */ + if (ext2_ino < EXT2_GOOD_OLD_FIRST_INO && + ext2_ino != EXT2_ROOT_INO) + continue; + objectid = ext2_ino + INO_OFFSET; + ret = copy_single_inode(trans, root, + objectid, ext2_fs, ext2_ino, + &ext2_inode, datacsum, packing, + noxattr); + if (ret) + return ret; + if (trans->blocks_used >= 4096) { + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); + trans = btrfs_start_transaction(root, 1); + BUG_ON(!trans); + } + } + if (err) { + fprintf(stderr, "ext2fs_get_next_inode: %s\n", error_message(err)); + return -1; + } + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); + + return ret; +} + +/* + * Construct a range of ext2fs image file. + * scan block allocation bitmap, find all blocks used by the ext2fs + * in this range and create file extents that point to these blocks. + * + * Note: Before calling the function, no file extent points to blocks + * in this range + */ +static int create_image_file_range(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 start_byte, u64 end_byte, + ext2_filsys ext2_fs) +{ + u32 blocksize = ext2_fs->blocksize; + u32 block = start_byte / blocksize; + u32 last_block = (end_byte + blocksize - 1) / blocksize; + int ret = 0; + struct blk_iterate_data data = { + .trans = trans, + .root = root, + .inode = inode, + .objectid = objectid, + .first_block = block, + .disk_block = 0, + .num_blocks = 0, + .boundary = (u64)-1, + .checksum = 0, + .errcode = 0, + }; + for (; start_byte < end_byte; block++, start_byte += blocksize) { + if (!ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block)) + continue; + ret = block_iterate_proc(NULL, block, block, &data); + if (ret & BLOCK_ABORT) { + ret = data.errcode; + goto fail; + } + } + if (data.num_blocks > 0) { + ret = record_file_blocks(trans, root, objectid, inode, + data.first_block, data.disk_block, + data.num_blocks, 0); + if (ret) + goto fail; + data.first_block += data.num_blocks; + } + if (last_block > data.first_block) { + ret = record_file_blocks(trans, root, objectid, inode, + data.first_block, 0, last_block - + data.first_block, 0); + if (ret) + goto fail; + } +fail: + return ret; +} +/* + * Create the ext2fs image file. + */ +static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs, + const char *name) +{ + int ret; + struct btrfs_key key; + struct btrfs_key location; + struct btrfs_path path; + struct btrfs_inode_item btrfs_inode; + struct btrfs_inode_item *inode_item; + struct extent_buffer *leaf; + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_root *extent_root = fs_info->extent_root; + struct btrfs_trans_handle *trans; + struct btrfs_extent_item *ei; + struct btrfs_extent_inline_ref *iref; + struct btrfs_extent_data_ref *dref; + u64 bytenr; + u64 num_bytes; + u64 objectid; + u64 last_byte; + u64 first_free; + u64 total_bytes; + u32 sectorsize = root->sectorsize; + + total_bytes = btrfs_super_total_bytes(&fs_info->super_copy); + first_free = BTRFS_SUPER_INFO_OFFSET + sectorsize * 2 - 1; + first_free &= ~((u64)sectorsize - 1); + + memset(&btrfs_inode, 0, sizeof(btrfs_inode)); + btrfs_set_stack_inode_generation(&btrfs_inode, 1); + btrfs_set_stack_inode_size(&btrfs_inode, total_bytes); + btrfs_set_stack_inode_nlink(&btrfs_inode, 1); + btrfs_set_stack_inode_nbytes(&btrfs_inode, 0); + btrfs_set_stack_inode_mode(&btrfs_inode, S_IFREG | 0400); + btrfs_set_stack_inode_flags(&btrfs_inode, BTRFS_INODE_NODATASUM | + BTRFS_INODE_READONLY); + btrfs_init_path(&path); + trans = btrfs_start_transaction(root, 1); + BUG_ON(!trans); + + objectid = btrfs_root_dirid(&root->root_item); + ret = btrfs_find_free_objectid(trans, root, objectid, &objectid); + if (ret) + goto fail; + + /* + * copy blocks covered by extent #0 to new positions. extent #0 is + * special, we can't rely on relocate_extents_range to relocate it. + */ + for (last_byte = 0; last_byte < first_free; last_byte += sectorsize) { + ret = custom_alloc_extent(root, sectorsize, 0, &key); + if (ret) + goto fail; + ret = copy_disk_extent(root, key.objectid, last_byte, + sectorsize); + if (ret) + goto fail; + ret = record_file_extent(trans, root, objectid, + &btrfs_inode, last_byte, + key.objectid, sectorsize, 0); + if (ret) + goto fail; + } + + while(1) { + key.objectid = last_byte; + key.offset = 0; + btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); + ret = btrfs_search_slot(trans, fs_info->extent_root, + &key, &path, 0, 0); + if (ret < 0) + goto fail; +next: + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(extent_root, &path); + if (ret < 0) + goto fail; + if (ret > 0) + break; + leaf = path.nodes[0]; + } + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (last_byte > key.objectid || + key.type != BTRFS_EXTENT_ITEM_KEY) { + path.slots[0]++; + goto next; + } + + bytenr = key.objectid; + num_bytes = key.offset; + ei = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_extent_item); + if (!(btrfs_extent_flags(leaf, ei) & BTRFS_EXTENT_FLAG_DATA)) { + path.slots[0]++; + goto next; + } + + BUG_ON(btrfs_item_size_nr(leaf, path.slots[0]) != sizeof(*ei) + + btrfs_extent_inline_ref_size(BTRFS_EXTENT_DATA_REF_KEY)); + + iref = (struct btrfs_extent_inline_ref *)(ei + 1); + key.type = btrfs_extent_inline_ref_type(leaf, iref); + BUG_ON(key.type != BTRFS_EXTENT_DATA_REF_KEY); + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + if (btrfs_extent_data_ref_root(leaf, dref) != + BTRFS_FS_TREE_OBJECTID) { + path.slots[0]++; + goto next; + } + + if (bytenr > last_byte) { + ret = create_image_file_range(trans, root, objectid, + &btrfs_inode, last_byte, + bytenr, ext2_fs); + if (ret) + goto fail; + } + ret = record_file_extent(trans, root, objectid, &btrfs_inode, + bytenr, bytenr, num_bytes, 0); + if (ret) + goto fail; + last_byte = bytenr + num_bytes; + btrfs_release_path(extent_root, &path); + + if (trans->blocks_used >= 4096) { + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); + trans = btrfs_start_transaction(root, 1); + BUG_ON(!trans); + } + } + btrfs_release_path(root, &path); + if (total_bytes > last_byte) { + ret = create_image_file_range(trans, root, objectid, + &btrfs_inode, last_byte, + total_bytes, ext2_fs); + if (ret) + goto fail; + } + + ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); + if (ret) + goto fail; + + location.objectid = objectid; + location.offset = 0; + btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); + ret = btrfs_insert_dir_item(trans, root, name, strlen(name), + btrfs_root_dirid(&root->root_item), + &location, EXT2_FT_REG_FILE, objectid); + if (ret) + goto fail; + ret = btrfs_insert_inode_ref(trans, root, name, strlen(name), + objectid, + btrfs_root_dirid(&root->root_item), + objectid); + if (ret) + goto fail; + location.objectid = btrfs_root_dirid(&root->root_item); + location.offset = 0; + btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); + ret = btrfs_lookup_inode(trans, root, &path, &location, 1); + if (ret) + goto fail; + leaf = path.nodes[0]; + inode_item = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_inode_item); + btrfs_set_inode_size(leaf, inode_item, strlen(name) * 2 + + btrfs_inode_size(leaf, inode_item)); + btrfs_mark_buffer_dirty(leaf); + btrfs_release_path(root, &path); + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); +fail: + btrfs_release_path(root, &path); + return ret; +} + +struct btrfs_root *link_subvol(struct btrfs_root *root, const char *base, + u64 root_objectid) +{ + struct btrfs_trans_handle *trans; + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *new_root = NULL; + struct btrfs_path *path; + struct btrfs_inode_item *inode_item; + struct extent_buffer *leaf; + struct btrfs_key key; + u64 dirid = btrfs_root_dirid(&root->root_item); + u64 index = 2; + char buf[64]; + int i; + int ret; + + path = btrfs_alloc_path(); + BUG_ON(!path); + + key.objectid = dirid; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + BUG_ON(ret <= 0); + + if (path->slots[0] > 0) { + path->slots[0]--; + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY) + index = key.offset + 1; + } + btrfs_release_path(root, path); + + trans = btrfs_start_transaction(root, 1); + BUG_ON(!trans); + + key.objectid = dirid; + key.offset = 0; + key.type = BTRFS_INODE_ITEM_KEY; + + ret = btrfs_lookup_inode(trans, root, path, &key, 1); + BUG_ON(ret); + leaf = path->nodes[0]; + inode_item = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_inode_item); + + key.objectid = root_objectid; + key.offset = (u64)-1; + key.type = BTRFS_ROOT_ITEM_KEY; + + strcpy(buf, base); + for (i = 0; i < 1024; i++) { + ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), + dirid, &key, BTRFS_FT_DIR, index); + if (ret != -EEXIST) + break; + sprintf(buf, "%s%d", base, i); + } + if (ret) + goto fail; + + btrfs_set_inode_size(leaf, inode_item, strlen(buf) * 2 + + btrfs_inode_size(leaf, inode_item)); + btrfs_mark_buffer_dirty(leaf); + btrfs_release_path(root, path); + + /* add the backref first */ + ret = btrfs_add_root_ref(trans, tree_root, root_objectid, + BTRFS_ROOT_BACKREF_KEY, + root->root_key.objectid, + dirid, index, buf, strlen(buf)); + BUG_ON(ret); + + /* now add the forward ref */ + ret = btrfs_add_root_ref(trans, tree_root, root->root_key.objectid, + BTRFS_ROOT_REF_KEY, root_objectid, + dirid, index, buf, strlen(buf)); + + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); + + new_root = btrfs_read_fs_root(fs_info, &key); + if (IS_ERR(new_root)) + new_root = NULL; +fail: + btrfs_free_path(path); + return new_root; +} + +/* + * Fixup block accounting. The initial block accounting created by + * make_block_groups isn't accuracy in this case. + */ +static int fixup_block_accounting(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + int ret; + int slot; + u64 start = 0; + u64 bytes_used = 0; + struct btrfs_path path; + struct btrfs_key key; + struct extent_buffer *leaf; + struct btrfs_block_group_cache *cache; + struct btrfs_fs_info *fs_info = root->fs_info; + + while(1) { + cache = btrfs_lookup_block_group(fs_info, start); + if (!cache) + break; + start = cache->key.objectid + cache->key.offset; + btrfs_set_block_group_used(&cache->item, 0); + cache->space_info->bytes_used = 0; + } + + btrfs_init_path(&path); + key.offset = 0; + key.objectid = 0; + btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); + ret = btrfs_search_slot(trans, root->fs_info->extent_root, + &key, &path, 0, 0); + if (ret < 0) + return ret; + while(1) { + leaf = path.nodes[0]; + slot = path.slots[0]; + if (slot >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, &path); + if (ret < 0) + return ret; + if (ret > 0) + break; + leaf = path.nodes[0]; + slot = path.slots[0]; + } + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.type == BTRFS_EXTENT_ITEM_KEY) { + bytes_used += key.offset; + ret = btrfs_update_block_group(trans, root, + key.objectid, key.offset, 1, 0); + BUG_ON(ret); + } + path.slots[0]++; + } + btrfs_set_super_bytes_used(&root->fs_info->super_copy, bytes_used); + btrfs_release_path(root, &path); + return 0; +} + +static int create_chunk_mapping(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_root *chunk_root = info->chunk_root; + struct btrfs_root *extent_root = info->extent_root; + struct btrfs_device *device; + struct btrfs_block_group_cache *cache; + struct btrfs_dev_extent *extent; + struct extent_buffer *leaf; + struct btrfs_chunk chunk; + struct btrfs_key key; + struct btrfs_path path; + u64 cur_start; + u64 total_bytes; + u64 chunk_objectid; + int ret; + + btrfs_init_path(&path); + + total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); + chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + + BUG_ON(list_empty(&info->fs_devices->devices)); + device = list_entry(info->fs_devices->devices.next, + struct btrfs_device, dev_list); + BUG_ON(device->devid != info->fs_devices->latest_devid); + + /* delete device extent created by make_btrfs */ + key.objectid = device->devid; + key.offset = 0; + key.type = BTRFS_DEV_EXTENT_KEY; + ret = btrfs_search_slot(trans, device->dev_root, &key, &path, -1, 1); + if (ret < 0) + goto err; + + BUG_ON(ret > 0); + ret = btrfs_del_item(trans, device->dev_root, &path); + if (ret) + goto err; + btrfs_release_path(device->dev_root, &path); + + /* delete chunk item created by make_btrfs */ + key.objectid = chunk_objectid; + key.offset = 0; + key.type = BTRFS_CHUNK_ITEM_KEY; + ret = btrfs_search_slot(trans, chunk_root, &key, &path, -1, 1); + if (ret < 0) + goto err; + + BUG_ON(ret > 0); + ret = btrfs_del_item(trans, chunk_root, &path); + if (ret) + goto err; + btrfs_release_path(chunk_root, &path); + + /* for each block group, create device extent and chunk item */ + cur_start = 0; + while (cur_start < total_bytes) { + cache = btrfs_lookup_block_group(root->fs_info, cur_start); + BUG_ON(!cache); + + /* insert device extent */ + key.objectid = device->devid; + key.offset = cache->key.objectid; + key.type = BTRFS_DEV_EXTENT_KEY; + ret = btrfs_insert_empty_item(trans, device->dev_root, &path, + &key, sizeof(*extent)); + if (ret) + goto err; + + leaf = path.nodes[0]; + extent = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_dev_extent); + + btrfs_set_dev_extent_chunk_tree(leaf, extent, + chunk_root->root_key.objectid); + btrfs_set_dev_extent_chunk_objectid(leaf, extent, + chunk_objectid); + btrfs_set_dev_extent_chunk_offset(leaf, extent, + cache->key.objectid); + btrfs_set_dev_extent_length(leaf, extent, cache->key.offset); + write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, + (unsigned long)btrfs_dev_extent_chunk_tree_uuid(extent), + BTRFS_UUID_SIZE); + btrfs_mark_buffer_dirty(leaf); + btrfs_release_path(device->dev_root, &path); + + /* insert chunk item */ + btrfs_set_stack_chunk_length(&chunk, cache->key.offset); + btrfs_set_stack_chunk_owner(&chunk, + extent_root->root_key.objectid); + btrfs_set_stack_chunk_stripe_len(&chunk, STRIPE_LEN); + btrfs_set_stack_chunk_type(&chunk, cache->flags); + btrfs_set_stack_chunk_io_align(&chunk, device->io_align); + btrfs_set_stack_chunk_io_width(&chunk, device->io_width); + btrfs_set_stack_chunk_sector_size(&chunk, device->sector_size); + btrfs_set_stack_chunk_num_stripes(&chunk, 1); + btrfs_set_stack_chunk_sub_stripes(&chunk, 0); + btrfs_set_stack_stripe_devid(&chunk.stripe, device->devid); + btrfs_set_stack_stripe_offset(&chunk.stripe, + cache->key.objectid); + memcpy(&chunk.stripe.dev_uuid, device->uuid, BTRFS_UUID_SIZE); + + key.objectid = chunk_objectid; + key.offset = cache->key.objectid; + key.type = BTRFS_CHUNK_ITEM_KEY; + + ret = btrfs_insert_item(trans, chunk_root, &key, &chunk, + btrfs_chunk_item_size(1)); + if (ret) + goto err; + + cur_start = cache->key.objectid + cache->key.offset; + } + + device->bytes_used = total_bytes; + ret = btrfs_update_device(trans, device); +err: + btrfs_release_path(device->dev_root, &path); + return ret; +} + +static int create_subvol(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 root_objectid) +{ + struct extent_buffer *tmp; + struct btrfs_root *new_root; + struct btrfs_key key; + struct btrfs_root_item root_item; + int ret; + + ret = btrfs_copy_root(trans, root, root->node, &tmp, + root_objectid); + BUG_ON(ret); + + memcpy(&root_item, &root->root_item, sizeof(root_item)); + btrfs_set_root_bytenr(&root_item, tmp->start); + btrfs_set_root_level(&root_item, btrfs_header_level(tmp)); + btrfs_set_root_generation(&root_item, trans->transid); + free_extent_buffer(tmp); + + key.objectid = root_objectid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = trans->transid; + ret = btrfs_insert_root(trans, root->fs_info->tree_root, + &key, &root_item); + + key.offset = (u64)-1; + new_root = btrfs_read_fs_root(root->fs_info, &key); + BUG_ON(!new_root || IS_ERR(new_root)); + + ret = btrfs_make_root_dir(trans, new_root, BTRFS_FIRST_FREE_OBJECTID); + BUG_ON(ret); + + return 0; +} + +static int init_btrfs(struct btrfs_root *root) +{ + int ret; + struct btrfs_key location; + struct btrfs_trans_handle *trans; + struct btrfs_fs_info *fs_info = root->fs_info; + struct extent_buffer *tmp; + + trans = btrfs_start_transaction(root, 1); + BUG_ON(!trans); + ret = btrfs_make_block_groups(trans, root); + if (ret) + goto err; + ret = fixup_block_accounting(trans, root); + if (ret) + goto err; + ret = create_chunk_mapping(trans, root); + if (ret) + goto err; + ret = btrfs_make_root_dir(trans, fs_info->tree_root, + BTRFS_ROOT_TREE_DIR_OBJECTID); + if (ret) + goto err; + memcpy(&location, &root->root_key, sizeof(location)); + location.offset = (u64)-1; + ret = btrfs_insert_dir_item(trans, fs_info->tree_root, "default", 7, + btrfs_super_root_dir(&fs_info->super_copy), + &location, BTRFS_FT_DIR, 0); + if (ret) + goto err; + ret = btrfs_insert_inode_ref(trans, fs_info->tree_root, "default", 7, + location.objectid, + btrfs_super_root_dir(&fs_info->super_copy), 0); + if (ret) + goto err; + btrfs_set_root_dirid(&fs_info->fs_root->root_item, + BTRFS_FIRST_FREE_OBJECTID); + + /* subvol for ext2 image file */ + ret = create_subvol(trans, root, EXT2_IMAGE_SUBVOL_OBJECTID); + BUG_ON(ret); + /* subvol for data relocation */ + ret = create_subvol(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID); + BUG_ON(ret); + + ret = __btrfs_cow_block(trans, fs_info->csum_root, + fs_info->csum_root->node, NULL, 0, &tmp, 0, 0); + BUG_ON(ret); + free_extent_buffer(tmp); + + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); +err: + return ret; +} + +/* + * Migrate super block to it's default position and zero 0 ~ 16k + */ +static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) +{ + int ret; + struct extent_buffer *buf; + struct btrfs_super_block *super; + u32 len; + u32 bytenr; + + BUG_ON(sectorsize < sizeof(*super)); + buf = malloc(sizeof(*buf) + sectorsize); + if (!buf) + return -ENOMEM; + + buf->len = sectorsize; + ret = pread(fd, buf->data, sectorsize, old_bytenr); + if (ret != sectorsize) + goto fail; + + super = (struct btrfs_super_block *)buf->data; + BUG_ON(btrfs_super_bytenr(super) != old_bytenr); + btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET); + + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET); + if (ret != sectorsize) + goto fail; + + ret = fsync(fd); + if (ret) + goto fail; + + memset(buf->data, 0, sectorsize); + for (bytenr = 0; bytenr < BTRFS_SUPER_INFO_OFFSET; ) { + len = BTRFS_SUPER_INFO_OFFSET - bytenr; + if (len > sectorsize) + len = sectorsize; + ret = pwrite(fd, buf->data, len, bytenr); + if (ret != len) { + fprintf(stderr, "unable to zero fill device\n"); + break; + } + bytenr += len; + } + ret = 0; + fsync(fd); +fail: + free(buf); + if (ret > 0) + ret = -1; + return ret; +} + +static int prepare_system_chunk_sb(struct btrfs_super_block *super) +{ + struct btrfs_chunk *chunk; + struct btrfs_disk_key *key; + u32 sectorsize = btrfs_super_sectorsize(super); + + key = (struct btrfs_disk_key *)(super->sys_chunk_array); + chunk = (struct btrfs_chunk *)(super->sys_chunk_array + + sizeof(struct btrfs_disk_key)); + + btrfs_set_disk_key_objectid(key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_disk_key_type(key, BTRFS_CHUNK_ITEM_KEY); + btrfs_set_disk_key_offset(key, 0); + + btrfs_set_stack_chunk_length(chunk, btrfs_super_total_bytes(super)); + btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_stack_chunk_stripe_len(chunk, 64 * 1024); + btrfs_set_stack_chunk_type(chunk, BTRFS_BLOCK_GROUP_SYSTEM); + btrfs_set_stack_chunk_io_align(chunk, sectorsize); + btrfs_set_stack_chunk_io_width(chunk, sectorsize); + btrfs_set_stack_chunk_sector_size(chunk, sectorsize); + btrfs_set_stack_chunk_num_stripes(chunk, 1); + btrfs_set_stack_chunk_sub_stripes(chunk, 0); + chunk->stripe.devid = super->dev_item.devid; + chunk->stripe.offset = cpu_to_le64(0); + memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid, BTRFS_UUID_SIZE); + btrfs_set_super_sys_array_size(super, sizeof(*key) + sizeof(*chunk)); + return 0; +} + +static int prepare_system_chunk(int fd, u64 sb_bytenr, u32 sectorsize) +{ + int ret; + struct extent_buffer *buf; + struct btrfs_super_block *super; + + BUG_ON(sectorsize < sizeof(*super)); + buf = malloc(sizeof(*buf) + sectorsize); + if (!buf) + return -ENOMEM; + + buf->len = sectorsize; + ret = pread(fd, buf->data, sectorsize, sb_bytenr); + if (ret != sectorsize) + goto fail; + + super = (struct btrfs_super_block *)buf->data; + BUG_ON(btrfs_super_bytenr(super) != sb_bytenr); + BUG_ON(btrfs_super_num_devices(super) != 1); + + ret = prepare_system_chunk_sb(super); + if (ret) + goto fail; + + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, sectorsize, sb_bytenr); + if (ret != sectorsize) + goto fail; + + ret = 0; +fail: + free(buf); + if (ret > 0) + ret = -1; + return ret; +} + +static int relocate_one_reference(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 extent_start, u64 extent_size, + struct btrfs_key *extent_key, + struct extent_io_tree *reloc_tree) +{ + struct extent_buffer *leaf; + struct btrfs_file_extent_item *fi; + struct btrfs_key key; + struct btrfs_path path; + struct btrfs_inode_item inode; + struct blk_iterate_data data; + u64 bytenr; + u64 num_bytes; + u64 cur_offset; + u64 new_pos; + u64 nbytes; + u64 sector_end; + u32 sectorsize = root->sectorsize; + unsigned long ptr; + int datacsum; + int fd; + int ret; + + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, extent_key, &path, -1, 1); + if (ret) + goto fail; + + leaf = path.nodes[0]; + fi = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_file_extent_item); + BUG_ON(btrfs_file_extent_offset(leaf, fi) > 0); + if (extent_start != btrfs_file_extent_disk_bytenr(leaf, fi) || + extent_size != btrfs_file_extent_disk_num_bytes(leaf, fi)) { + ret = 1; + goto fail; + } + + bytenr = extent_start + btrfs_file_extent_offset(leaf, fi); + num_bytes = btrfs_file_extent_num_bytes(leaf, fi); + + ret = btrfs_del_item(trans, root, &path); + if (ret) + goto fail; + + ret = btrfs_free_extent(trans, root, extent_start, extent_size, 0, + root->root_key.objectid, + extent_key->objectid, extent_key->offset); + if (ret) + goto fail; + + btrfs_release_path(root, &path); + + key.objectid = extent_key->objectid; + key.offset = 0; + key.type = BTRFS_INODE_ITEM_KEY; + ret = btrfs_lookup_inode(trans, root, &path, &key, 0); + if (ret) + goto fail; + + leaf = path.nodes[0]; + ptr = btrfs_item_ptr_offset(leaf, path.slots[0]); + read_extent_buffer(leaf, &inode, ptr, sizeof(inode)); + btrfs_release_path(root, &path); + + BUG_ON(num_bytes & (sectorsize - 1)); + nbytes = btrfs_stack_inode_nbytes(&inode) - num_bytes; + btrfs_set_stack_inode_nbytes(&inode, nbytes); + datacsum = !(btrfs_stack_inode_flags(&inode) & BTRFS_INODE_NODATASUM); + + data = (struct blk_iterate_data) { + .trans = trans, + .root = root, + .inode = &inode, + .objectid = extent_key->objectid, + .first_block = extent_key->offset / sectorsize, + .disk_block = 0, + .num_blocks = 0, + .boundary = (u64)-1, + .checksum = datacsum, + .errcode = 0, + }; + + cur_offset = extent_key->offset; + while (num_bytes > 0) { + sector_end = bytenr + sectorsize - 1; + if (test_range_bit(reloc_tree, bytenr, sector_end, + EXTENT_LOCKED, 1)) { + ret = get_state_private(reloc_tree, bytenr, &new_pos); + BUG_ON(ret); + } else { + ret = custom_alloc_extent(root, sectorsize, 0, &key); + if (ret) + goto fail; + new_pos = key.objectid; + + if (cur_offset == extent_key->offset) { + fd = root->fs_info->fs_devices->latest_bdev; + readahead(fd, bytenr, num_bytes); + } + ret = copy_disk_extent(root, new_pos, bytenr, + sectorsize); + if (ret) + goto fail; + ret = set_extent_bits(reloc_tree, bytenr, sector_end, + EXTENT_LOCKED, GFP_NOFS); + BUG_ON(ret); + ret = set_state_private(reloc_tree, bytenr, new_pos); + BUG_ON(ret); + } + + ret = block_iterate_proc(NULL, new_pos / sectorsize, + cur_offset / sectorsize, &data); + if (ret & BLOCK_ABORT) { + ret = data.errcode; + goto fail; + } + + cur_offset += sectorsize; + bytenr += sectorsize; + num_bytes -= sectorsize; + } + + if (data.num_blocks > 0) { + ret = record_file_blocks(trans, root, + extent_key->objectid, &inode, + data.first_block, data.disk_block, + data.num_blocks, datacsum); + if (ret) + goto fail; + } + + key.objectid = extent_key->objectid; + key.offset = 0; + key.type = BTRFS_INODE_ITEM_KEY; + ret = btrfs_lookup_inode(trans, root, &path, &key, 1); + if (ret) + goto fail; + + leaf = path.nodes[0]; + ptr = btrfs_item_ptr_offset(leaf, path.slots[0]); + write_extent_buffer(leaf, &inode, ptr, sizeof(inode)); + btrfs_mark_buffer_dirty(leaf); + btrfs_release_path(root, &path); + +fail: + btrfs_release_path(root, &path); + return ret; +} + +static int relocate_extents_range(struct btrfs_root *fs_root, + struct btrfs_root *ext2_root, + u64 start_byte, u64 end_byte) +{ + struct btrfs_fs_info *info = fs_root->fs_info; + struct btrfs_root *extent_root = info->extent_root; + struct btrfs_root *cur_root = NULL; + struct btrfs_trans_handle *trans; + struct btrfs_extent_data_ref *dref; + struct btrfs_extent_inline_ref *iref; + struct btrfs_extent_item *ei; + struct extent_buffer *leaf; + struct btrfs_key key; + struct btrfs_key extent_key; + struct btrfs_path path; + struct extent_io_tree reloc_tree; + unsigned long ptr; + unsigned long end; + u64 cur_byte; + u64 num_bytes; + u64 ref_root; + u64 num_extents; + int pass = 0; + int ret; + + btrfs_init_path(&path); + extent_io_tree_init(&reloc_tree); + + key.objectid = start_byte; + key.offset = 0; + key.type = BTRFS_EXTENT_ITEM_KEY; + ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); + if (ret < 0) + goto fail; + if (ret > 0) { + ret = btrfs_previous_item(extent_root, &path, 0, + BTRFS_EXTENT_ITEM_KEY); + if (ret < 0) + goto fail; + if (ret == 0) { + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.objectid + key.offset > start_byte) + start_byte = key.objectid; + } + } + btrfs_release_path(extent_root, &path); +again: + cur_root = (pass % 2 == 0) ? ext2_root : fs_root; + num_extents = 0; + + trans = btrfs_start_transaction(cur_root, 1); + BUG_ON(!trans); + + cur_byte = start_byte; + while (1) { + key.objectid = cur_byte; + key.offset = 0; + key.type = BTRFS_EXTENT_ITEM_KEY; + ret = btrfs_search_slot(trans, extent_root, + &key, &path, 0, 0); + if (ret < 0) + goto fail; +next: + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(extent_root, &path); + if (ret < 0) + goto fail; + if (ret > 0) + break; + leaf = path.nodes[0]; + } + + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.objectid < cur_byte || + key.type != BTRFS_EXTENT_ITEM_KEY) { + path.slots[0]++; + goto next; + } + if (key.objectid >= end_byte) + break; + + num_extents++; + + cur_byte = key.objectid; + num_bytes = key.offset; + ei = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_extent_item); + BUG_ON(!(btrfs_extent_flags(leaf, ei) & + BTRFS_EXTENT_FLAG_DATA)); + + ptr = btrfs_item_ptr_offset(leaf, path.slots[0]); + end = ptr + btrfs_item_size_nr(leaf, path.slots[0]); + + ptr += sizeof(struct btrfs_extent_item); + + while (ptr < end) { + iref = (struct btrfs_extent_inline_ref *)ptr; + key.type = btrfs_extent_inline_ref_type(leaf, iref); + BUG_ON(key.type != BTRFS_EXTENT_DATA_REF_KEY); + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + ref_root = btrfs_extent_data_ref_root(leaf, dref); + extent_key.objectid = + btrfs_extent_data_ref_objectid(leaf, dref); + extent_key.offset = + btrfs_extent_data_ref_offset(leaf, dref); + extent_key.type = BTRFS_EXTENT_DATA_KEY; + BUG_ON(btrfs_extent_data_ref_count(leaf, dref) != 1); + + if (ref_root == cur_root->root_key.objectid) + break; + + ptr += btrfs_extent_inline_ref_size(key.type); + } + + if (ptr >= end) { + path.slots[0]++; + goto next; + } + + ret = relocate_one_reference(trans, cur_root, cur_byte, + num_bytes, &extent_key, + &reloc_tree); + if (ret < 0) + goto fail; + + cur_byte += num_bytes; + btrfs_release_path(extent_root, &path); + + if (trans->blocks_used >= 4096) { + ret = btrfs_commit_transaction(trans, cur_root); + BUG_ON(ret); + trans = btrfs_start_transaction(cur_root, 1); + BUG_ON(!trans); + } + } + btrfs_release_path(cur_root, &path); + + ret = btrfs_commit_transaction(trans, cur_root); + BUG_ON(ret); + + if (num_extents > 0 && pass++ < 16) + goto again; + + ret = (num_extents > 0) ? -1 : 0; +fail: + btrfs_release_path(cur_root, &path); + extent_io_tree_cleanup(&reloc_tree); + return ret; +} + +/* + * relocate data in system chunk + */ +static int cleanup_sys_chunk(struct btrfs_root *fs_root, + struct btrfs_root *ext2_root) +{ + struct btrfs_block_group_cache *cache; + int i, ret = 0; + u64 offset = 0; + u64 end_byte; + + while(1) { + cache = btrfs_lookup_block_group(fs_root->fs_info, offset); + if (!cache) + break; + + end_byte = cache->key.objectid + cache->key.offset; + if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) { + ret = relocate_extents_range(fs_root, ext2_root, + cache->key.objectid, + end_byte); + if (ret) + goto fail; + } + offset = end_byte; + } + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + offset = btrfs_sb_offset(i); + offset &= ~((u64)STRIPE_LEN - 1); + + ret = relocate_extents_range(fs_root, ext2_root, + offset, offset + STRIPE_LEN); + if (ret) + goto fail; + } + ret = 0; +fail: + return ret; +} + +static int fixup_chunk_mapping(struct btrfs_root *root) +{ + struct btrfs_trans_handle *trans; + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_root *chunk_root = info->chunk_root; + struct extent_buffer *leaf; + struct btrfs_key key; + struct btrfs_path path; + struct btrfs_chunk chunk; + unsigned long ptr; + u32 size; + u64 type; + int ret; + + btrfs_init_path(&path); + + trans = btrfs_start_transaction(root, 1); + BUG_ON(!trans); + + /* + * recow the whole chunk tree. this will move all chunk tree blocks + * into system block group. + */ + memset(&key, 0, sizeof(key)); + while (1) { + ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1); + if (ret < 0) + goto err; + + ret = btrfs_next_leaf(chunk_root, &path); + if (ret < 0) + goto err; + if (ret > 0) + break; + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + btrfs_release_path(chunk_root, &path); + } + btrfs_release_path(chunk_root, &path); + + /* fixup the system chunk array in super block */ + btrfs_set_super_sys_array_size(&info->super_copy, 0); + + key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + key.offset = 0; + key.type = BTRFS_CHUNK_ITEM_KEY; + + ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 0); + if (ret < 0) + goto err; + BUG_ON(ret != 0); + while(1) { + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(chunk_root, &path); + if (ret < 0) + goto err; + if (ret > 0) + break; + leaf = path.nodes[0]; + } + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.type != BTRFS_CHUNK_ITEM_KEY) + goto next; + + ptr = btrfs_item_ptr_offset(leaf, path.slots[0]); + size = btrfs_item_size_nr(leaf, path.slots[0]); + BUG_ON(size != sizeof(chunk)); + read_extent_buffer(leaf, &chunk, ptr, size); + type = btrfs_stack_chunk_type(&chunk); + + if (!(type & BTRFS_BLOCK_GROUP_SYSTEM)) + goto next; + + ret = btrfs_add_system_chunk(trans, chunk_root, &key, + &chunk, size); + if (ret) + goto err; +next: + path.slots[0]++; + } + + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); +err: + btrfs_release_path(chunk_root, &path); + return ret; +} + +int do_convert(const char *devname, int datacsum, int packing, int noxattr) +{ + int i, fd, ret; + u32 blocksize; + u64 blocks[7]; + u64 total_bytes; + u64 super_bytenr; + ext2_filsys ext2_fs; + struct btrfs_root *root; + struct btrfs_root *ext2_root; + + ret = open_ext2fs(devname, &ext2_fs); + if (ret) { + fprintf(stderr, "unable to open the Ext2fs\n"); + goto fail; + } + blocksize = ext2_fs->blocksize; + total_bytes = (u64)ext2_fs->super->s_blocks_count * blocksize; + if (blocksize < 4096) { + fprintf(stderr, "block size is too small\n"); + goto fail; + } + if (!(ext2_fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE)) { + fprintf(stderr, "filetype feature is missing\n"); + goto fail; + } + for (i = 0; i < 7; i++) { + ret = ext2_alloc_block(ext2_fs, 0, blocks + i); + if (ret) { + fprintf(stderr, "not enough free space\n"); + goto fail; + } + blocks[i] *= blocksize; + } + super_bytenr = blocks[0]; + fd = open(devname, O_RDWR); + if (fd < 0) { + fprintf(stderr, "unable to open %s\n", devname); + goto fail; + } + ret = make_btrfs(fd, devname, ext2_fs->super->s_volume_name, + blocks, total_bytes, blocksize, blocksize, + blocksize, blocksize); + if (ret) { + fprintf(stderr, "unable to create initial ctree\n"); + goto fail; + } + /* create a system chunk that maps the whole device */ + ret = prepare_system_chunk(fd, super_bytenr, blocksize); + if (ret) { + fprintf(stderr, "unable to update system chunk\n"); + goto fail; + } + root = open_ctree_fd(fd, devname, super_bytenr, O_RDWR); + if (!root) { + fprintf(stderr, "unable to open ctree\n"); + goto fail; + } + ret = cache_free_extents(root, ext2_fs); + if (ret) { + fprintf(stderr, "error during cache_free_extents %d\n", ret); + goto fail; + } + root->fs_info->extent_ops = &extent_ops; + /* recover block allocation bitmap */ + for (i = 0; i < 7; i++) { + blocks[i] /= blocksize; + ext2_free_block(ext2_fs, blocks[i]); + } + ret = init_btrfs(root); + if (ret) { + fprintf(stderr, "unable to setup the root tree\n"); + goto fail; + } + printf("creating btrfs metadata.\n"); + ret = copy_inodes(root, ext2_fs, datacsum, packing, noxattr); + if (ret) { + fprintf(stderr, "error during copy_inodes %d\n", ret); + goto fail; + } + printf("creating ext2fs image file.\n"); + ext2_root = link_subvol(root, "ext2_saved", EXT2_IMAGE_SUBVOL_OBJECTID); + if (!ext2_root) { + fprintf(stderr, "unable to create subvol\n"); + goto fail; + } + ret = create_ext2_image(ext2_root, ext2_fs, "image"); + if (ret) { + fprintf(stderr, "error during create_ext2_image %d\n", ret); + goto fail; + } + printf("cleaning up system chunk.\n"); + ret = cleanup_sys_chunk(root, ext2_root); + if (ret) { + fprintf(stderr, "error during cleanup_sys_chunk %d\n", ret); + goto fail; + } + ret = close_ctree(root); + if (ret) { + fprintf(stderr, "error during close_ctree %d\n", ret); + goto fail; + } + close_ext2fs(ext2_fs); + + /* + * If this step succeed, we get a mountable btrfs. Otherwise + * the ext2fs is left unchanged. + */ + ret = migrate_super_block(fd, super_bytenr, blocksize); + if (ret) { + fprintf(stderr, "unable to migrate super block\n"); + goto fail; + } + + root = open_ctree_fd(fd, devname, 0, O_RDWR); + if (!root) { + fprintf(stderr, "unable to open ctree\n"); + goto fail; + } + /* move chunk tree into system chunk. */ + ret = fixup_chunk_mapping(root); + if (ret) { + fprintf(stderr, "error during fixup_chunk_tree\n"); + goto fail; + } + ret = close_ctree(root); + close(fd); + + printf("conversion complete.\n"); + return 0; +fail: + fprintf(stderr, "conversion aborted.\n"); + return -1; +} + +static int may_rollback(struct btrfs_root *root) +{ + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_multi_bio *multi = NULL; + u64 bytenr; + u64 length; + u64 physical; + u64 total_bytes; + int num_stripes; + int ret; + + if (btrfs_super_num_devices(&info->super_copy) != 1) + goto fail; + + bytenr = BTRFS_SUPER_INFO_OFFSET; + total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); + + while (1) { + ret = btrfs_map_block(&info->mapping_tree, WRITE, bytenr, + &length, &multi, 0); + if (ret) + goto fail; + + num_stripes = multi->num_stripes; + physical = multi->stripes[0].physical; + kfree(multi); + + if (num_stripes != 1 || physical != bytenr) + goto fail; + + bytenr += length; + if (bytenr >= total_bytes) + break; + } + return 0; +fail: + return -1; +} + +int do_rollback(const char *devname, int force) +{ + int fd; + int ret; + int i; + struct btrfs_root *root; + struct btrfs_root *ext2_root; + struct btrfs_root *chunk_root; + struct btrfs_dir_item *dir; + struct btrfs_inode_item *inode; + struct btrfs_file_extent_item *fi; + struct btrfs_trans_handle *trans; + struct extent_buffer *leaf; + struct btrfs_block_group_cache *cache1; + struct btrfs_block_group_cache *cache2; + struct btrfs_key key; + struct btrfs_path path; + struct extent_io_tree io_tree; + char *buf; + char *name; + u64 bytenr; + u64 num_bytes; + u64 root_dir; + u64 objectid; + u64 offset; + u64 start; + u64 end; + u64 sb_bytenr; + u64 first_free; + u64 total_bytes; + u32 sectorsize; + + extent_io_tree_init(&io_tree); + + fd = open(devname, O_RDWR); + if (fd < 0) { + fprintf(stderr, "unable to open %s\n", devname); + goto fail; + } + root = open_ctree_fd(fd, devname, 0, O_RDWR); + if (!root) { + fprintf(stderr, "unable to open ctree\n"); + goto fail; + } + ret = may_rollback(root); + if (ret < 0) { + fprintf(stderr, "unable to do rollback\n"); + goto fail; + } + + sectorsize = root->sectorsize; + buf = malloc(sectorsize); + if (!buf) { + fprintf(stderr, "unable to allocate memory\n"); + goto fail; + } + + btrfs_init_path(&path); + + key.objectid = EXT2_IMAGE_SUBVOL_OBJECTID; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + ext2_root = btrfs_read_fs_root(root->fs_info, &key); + if (!ext2_root || IS_ERR(ext2_root)) { + fprintf(stderr, "unable to open subvol %llu\n", + key.objectid); + goto fail; + } + + name = "image"; + root_dir = btrfs_root_dirid(&root->root_item); + dir = btrfs_lookup_dir_item(NULL, ext2_root, &path, + root_dir, name, strlen(name), 0); + if (!dir || IS_ERR(dir)) { + fprintf(stderr, "unable to find file %s\n", name); + goto fail; + } + leaf = path.nodes[0]; + btrfs_dir_item_key_to_cpu(leaf, dir, &key); + btrfs_release_path(ext2_root, &path); + + objectid = key.objectid; + + ret = btrfs_lookup_inode(NULL, ext2_root, &path, &key, 0); + if (ret) { + fprintf(stderr, "unable to find inode item\n"); + goto fail; + } + leaf = path.nodes[0]; + inode = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item); + total_bytes = btrfs_inode_size(leaf, inode); + btrfs_release_path(ext2_root, &path); + + key.objectid = objectid; + key.offset = 0; + btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); + ret = btrfs_search_slot(NULL, ext2_root, &key, &path, 0, 0); + if (ret != 0) { + fprintf(stderr, "unable to find first file extent\n"); + btrfs_release_path(ext2_root, &path); + goto fail; + } + + /* build mapping tree for the relocated blocks */ + for (offset = 0; offset < total_bytes; ) { + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, &path); + if (ret != 0) + break; + continue; + } + + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.objectid != objectid || key.offset != offset || + btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) + break; + + fi = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_file_extent_item); + if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) + break; + if (btrfs_file_extent_compression(leaf, fi) || + btrfs_file_extent_encryption(leaf, fi) || + btrfs_file_extent_other_encoding(leaf, fi)) + break; + + bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + /* skip holes and direct mapped extents */ + if (bytenr == 0 || bytenr == offset) + goto next_extent; + + bytenr += btrfs_file_extent_offset(leaf, fi); + num_bytes = btrfs_file_extent_num_bytes(leaf, fi); + + cache1 = btrfs_lookup_block_group(root->fs_info, offset); + cache2 = btrfs_lookup_block_group(root->fs_info, + offset + num_bytes - 1); + if (!cache1 || cache1 != cache2 || + (!(cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM) && + !intersect_with_sb(offset, num_bytes))) + break; + + set_extent_bits(&io_tree, offset, offset + num_bytes - 1, + EXTENT_LOCKED, GFP_NOFS); + set_state_private(&io_tree, offset, bytenr); +next_extent: + offset += btrfs_file_extent_num_bytes(leaf, fi); + path.slots[0]++; + } + btrfs_release_path(ext2_root, &path); + + if (offset < total_bytes) { + fprintf(stderr, "unable to build extent mapping\n"); + goto fail; + } + + first_free = BTRFS_SUPER_INFO_OFFSET + 2 * sectorsize - 1; + first_free &= ~((u64)sectorsize - 1); + /* backup for extent #0 should exist */ + if(!test_range_bit(&io_tree, 0, first_free - 1, EXTENT_LOCKED, 1)) { + fprintf(stderr, "no backup for the first extent\n"); + goto fail; + } + /* force no allocation from system block group */ + root->fs_info->system_allocs = -1; + trans = btrfs_start_transaction(root, 1); + BUG_ON(!trans); + /* + * recow the whole chunk tree, this will remove all chunk tree blocks + * from system block group + */ + chunk_root = root->fs_info->chunk_root; + memset(&key, 0, sizeof(key)); + while (1) { + ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1); + if (ret < 0) + break; + + ret = btrfs_next_leaf(chunk_root, &path); + if (ret) + break; + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + btrfs_release_path(chunk_root, &path); + } + btrfs_release_path(chunk_root, &path); + + offset = 0; + num_bytes = 0; + while(1) { + cache1 = btrfs_lookup_block_group(root->fs_info, offset); + if (!cache1) + break; + + if (cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM) + num_bytes += btrfs_block_group_used(&cache1->item); + + offset = cache1->key.objectid + cache1->key.offset; + } + /* only extent #0 left in system block group? */ + if (num_bytes > first_free) { + fprintf(stderr, "unable to empty system block group\n"); + goto fail; + } + /* create a system chunk that maps the whole device */ + ret = prepare_system_chunk_sb(&root->fs_info->super_copy); + if (ret) { + fprintf(stderr, "unable to update system chunk\n"); + goto fail; + } + + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); + + ret = close_ctree(root); + if (ret) { + fprintf(stderr, "error during close_ctree %d\n", ret); + goto fail; + } + + /* zero btrfs super block mirrors */ + memset(buf, 0, sectorsize); + for (i = 1 ; i < BTRFS_SUPER_MIRROR_MAX; i++) { + bytenr = btrfs_sb_offset(i); + if (bytenr >= total_bytes) + break; + ret = pwrite(fd, buf, sectorsize, bytenr); + } + + sb_bytenr = (u64)-1; + /* copy all relocated blocks back */ + while(1) { + ret = find_first_extent_bit(&io_tree, 0, &start, &end, + EXTENT_LOCKED); + if (ret) + break; + + ret = get_state_private(&io_tree, start, &bytenr); + BUG_ON(ret); + + clear_extent_bits(&io_tree, start, end, EXTENT_LOCKED, + GFP_NOFS); + + while (start <= end) { + if (start == BTRFS_SUPER_INFO_OFFSET) { + sb_bytenr = bytenr; + goto next_sector; + } + ret = pread(fd, buf, sectorsize, bytenr); + if (ret < 0) { + fprintf(stderr, "error during pread %d\n", ret); + goto fail; + } + BUG_ON(ret != sectorsize); + ret = pwrite(fd, buf, sectorsize, start); + if (ret < 0) { + fprintf(stderr, "error during pwrite %d\n", ret); + goto fail; + } + BUG_ON(ret != sectorsize); +next_sector: + start += sectorsize; + bytenr += sectorsize; + } + } + + ret = fsync(fd); + if (ret) { + fprintf(stderr, "error during fsync %d\n", ret); + goto fail; + } + /* + * finally, overwrite btrfs super block. + */ + ret = pread(fd, buf, sectorsize, sb_bytenr); + if (ret < 0) { + fprintf(stderr, "error during pread %d\n", ret); + goto fail; + } + BUG_ON(ret != sectorsize); + ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET); + if (ret < 0) { + fprintf(stderr, "error during pwrite %d\n", ret); + goto fail; + } + BUG_ON(ret != sectorsize); + ret = fsync(fd); + if (ret) { + fprintf(stderr, "error during fsync %d\n", ret); + goto fail; + } + + close(fd); + free(buf); + extent_io_tree_cleanup(&io_tree); + printf("rollback complete.\n"); + return 0; +fail: + fprintf(stderr, "rollback aborted.\n"); + return -1; +} + +static void print_usage(void) +{ + printf("usage: btrfs-convert [-d] [-i] [-n] [-r] device\n"); + printf("\t-d disable data checksum\n"); + printf("\t-i ignore xattrs and ACLs\n"); + printf("\t-n disable packing of small files\n"); + printf("\t-r roll back to ext2fs\n"); +} + +int main(int argc, char *argv[]) +{ + int ret; + int packing = 1; + int noxattr = 0; + int datacsum = 1; + int rollback = 0; + char *file; + while(1) { + int c = getopt(argc, argv, "dinr"); + if (c < 0) + break; + switch(c) { + case 'd': + datacsum = 0; + break; + case 'i': + noxattr = 1; + break; + case 'n': + packing = 0; + break; + case 'r': + rollback = 1; + break; + default: + print_usage(); + return 1; + } + } + argc = argc - optind; + if (argc != 1) { + print_usage(); + return 1; + } + + file = argv[optind]; + if (check_mounted(file)) { + fprintf(stderr, "%s is mounted\n", file); + return 1; + } + + if (rollback) { + ret = do_rollback(file, 0); + } else { + ret = do_convert(file, datacsum, packing, noxattr); + } + if (ret) + return 1; + return 0; +} diff --git a/misc/debug-tree.c b/misc/debug-tree.c new file mode 100644 index 0000000..1d47519 --- /dev/null +++ b/misc/debug-tree.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "version.h" + +static int print_usage(void) +{ + fprintf(stderr, "usage: debug-tree [ -e ] device\n"); + fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); + exit(1); +} + +static void print_extent_leaf(struct btrfs_root *root, struct extent_buffer *l) +{ + int i; + struct btrfs_item *item; +// struct btrfs_extent_ref *ref; + struct btrfs_key key; + static u64 last = 0; + static u64 last_len = 0; + u32 nr = btrfs_header_nritems(l); + u32 type; + + for (i = 0 ; i < nr ; i++) { + item = btrfs_item_nr(l, i); + btrfs_item_key_to_cpu(l, &key, i); + type = btrfs_key_type(&key); + switch (type) { + case BTRFS_EXTENT_ITEM_KEY: + last_len = key.offset; + last = key.objectid; + break; +#if 0 + case BTRFS_EXTENT_REF_KEY: + ref = btrfs_item_ptr(l, i, struct btrfs_extent_ref); + printf("%llu %llu extent back ref root %llu gen %llu " + "owner %llu num_refs %lu\n", + (unsigned long long)last, + (unsigned long long)last_len, + (unsigned long long)btrfs_ref_root(l, ref), + (unsigned long long)btrfs_ref_generation(l, ref), + (unsigned long long)btrfs_ref_objectid(l, ref), + (unsigned long)btrfs_ref_num_refs(l, ref)); + break; +#endif + }; + fflush(stdout); + } +} + +static void print_extents(struct btrfs_root *root, struct extent_buffer *eb) +{ + int i; + u32 nr; + u32 size; + + if (!eb) + return; + if (btrfs_is_leaf(eb)) { + print_extent_leaf(root, eb); + return; + } + size = btrfs_level_size(root, btrfs_header_level(eb) - 1); + nr = btrfs_header_nritems(eb); + for (i = 0; i < nr; i++) { + struct extent_buffer *next = read_tree_block(root, + btrfs_node_blockptr(eb, i), + size, + btrfs_node_ptr_generation(eb, i)); + if (btrfs_is_leaf(next) && + btrfs_header_level(eb) != 1) + BUG(); + if (btrfs_header_level(next) != + btrfs_header_level(eb) - 1) + BUG(); + print_extents(root, next); + free_extent_buffer(next); + } +} + +int main(int ac, char **av) +{ + struct btrfs_root *root; + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_root_item ri; + struct extent_buffer *leaf; + struct btrfs_disk_key disk_key; + struct btrfs_key found_key; + char uuidbuf[37]; + int ret; + int slot; + int extent_only = 0; + struct btrfs_root *tree_root_scan; + + radix_tree_init(); + + while(1) { + int c; + c = getopt(ac, av, "e"); + if (c < 0) + break; + switch(c) { + case 'e': + extent_only = 1; + break; + default: + print_usage(); + } + } + ac = ac - optind; + if (ac != 1) + print_usage(); + + root = open_ctree(av[optind], 0, 0); + if (!root) { + fprintf(stderr, "unable to open %s\n", av[optind]); + exit(1); + } + if (!extent_only) { + printf("root tree\n"); + btrfs_print_tree(root->fs_info->tree_root, + root->fs_info->tree_root->node); + + printf("chunk tree\n"); + btrfs_print_tree(root->fs_info->chunk_root, + root->fs_info->chunk_root->node); + } + tree_root_scan = root->fs_info->tree_root; + + btrfs_init_path(&path); +again: + key.offset = 0; + key.objectid = 0; + btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); + ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0); + BUG_ON(ret < 0); + while(1) { + leaf = path.nodes[0]; + slot = path.slots[0]; + if (slot >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(tree_root_scan, &path); + if (ret != 0) + break; + leaf = path.nodes[0]; + slot = path.slots[0]; + } + btrfs_item_key(leaf, &disk_key, path.slots[0]); + btrfs_disk_key_to_cpu(&found_key, &disk_key); + if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { + unsigned long offset; + struct extent_buffer *buf; + int skip = extent_only; + + offset = btrfs_item_ptr_offset(leaf, slot); + read_extent_buffer(leaf, &ri, offset, sizeof(ri)); + buf = read_tree_block(tree_root_scan, + btrfs_root_bytenr(&ri), + tree_root_scan->leafsize, 0); + switch(found_key.objectid) { + case BTRFS_ROOT_TREE_OBJECTID: + if (!skip) + printf("root"); + break; + case BTRFS_EXTENT_TREE_OBJECTID: + skip = 0; + if (!extent_only) + printf("extent"); + break; + case BTRFS_CHUNK_TREE_OBJECTID: + if (!skip) { + printf("chunk"); + } + break; + case BTRFS_DEV_TREE_OBJECTID: + if (!skip) { + printf("device"); + } + break; + case BTRFS_FS_TREE_OBJECTID: + if (!skip) { + printf("fs"); + } + break; + case BTRFS_ROOT_TREE_DIR_OBJECTID: + if (!skip) { + printf("directory"); + } + break; + case BTRFS_CSUM_TREE_OBJECTID: + if (!skip) { + printf("checksum"); + } + break; + case BTRFS_ORPHAN_OBJECTID: + if (!skip) { + printf("orphan"); + } + break; + case BTRFS_TREE_LOG_OBJECTID: + if (!skip) { + printf("log"); + } + break; + case BTRFS_TREE_LOG_FIXUP_OBJECTID: + if (!skip) { + printf("log fixup"); + } + break; + case BTRFS_TREE_RELOC_OBJECTID: + if (!skip) { + printf("reloc"); + } + break; + case BTRFS_DATA_RELOC_TREE_OBJECTID: + if (!skip) { + printf("data reloc"); + } + break; + case BTRFS_EXTENT_CSUM_OBJECTID: + if (!skip) { + printf("extent checksum"); + } + case BTRFS_MULTIPLE_OBJECTIDS: + if (!skip) { + printf("multiple"); + } + break; + default: + if (!skip) { + printf("file"); + } + } + if (!skip && !extent_only) { + printf(" tree "); + btrfs_print_key(&disk_key); + printf(" \n"); + btrfs_print_tree(tree_root_scan, buf); + } else if (extent_only && !skip) { + print_extents(tree_root_scan, buf); + } + } + path.slots[0]++; + } + btrfs_release_path(root, &path); + + if (tree_root_scan == root->fs_info->tree_root && + root->fs_info->log_root_tree) { + tree_root_scan = root->fs_info->log_root_tree; + goto again; + } + + if (extent_only) + return 0; + + printf("total bytes %llu\n", + (unsigned long long)btrfs_super_total_bytes(&root->fs_info->super_copy)); + printf("bytes used %llu\n", + (unsigned long long)btrfs_super_bytes_used(&root->fs_info->super_copy)); + uuidbuf[36] = '\0'; + uuid_unparse(root->fs_info->super_copy.fsid, uuidbuf); + printf("uuid %s\n", uuidbuf); + printf("%s\n", BTRFS_BUILD_VERSION); + return 0; +} diff --git a/misc/mkfs.c b/misc/mkfs.c new file mode 100644 index 0000000..2e99b95 --- /dev/null +++ b/misc/mkfs.c @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2007 Oracle. 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 + +#ifndef __CHECKER__ +#include +#include +#include "ioctl.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "disk-io.h" +#include "volumes.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" + +static u64 parse_size(char *s) +{ + int len = strlen(s); + char c; + u64 mult = 1; + + if (!isdigit(s[len - 1])) { + c = tolower(s[len - 1]); + switch (c) { + case 'g': + mult *= 1024; + case 'm': + mult *= 1024; + case 'k': + mult *= 1024; + case 'b': + break; + default: + fprintf(stderr, "Unknown size descriptor %c\n", c); + exit(1); + } + s[len - 1] = '\0'; + } + return atol(s) * mult; +} + +static int make_root_dir(struct btrfs_root *root) +{ + struct btrfs_trans_handle *trans; + struct btrfs_key location; + u64 bytes_used; + u64 chunk_start = 0; + u64 chunk_size = 0; + int ret; + + trans = btrfs_start_transaction(root, 1); + bytes_used = btrfs_super_bytes_used(&root->fs_info->super_copy); + + root->fs_info->system_allocs = 1; + ret = btrfs_make_block_group(trans, root, bytes_used, + BTRFS_BLOCK_GROUP_SYSTEM, + BTRFS_FIRST_CHUNK_TREE_OBJECTID, + 0, BTRFS_MKFS_SYSTEM_GROUP_SIZE); + BUG_ON(ret); + + ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, + &chunk_start, &chunk_size, + BTRFS_BLOCK_GROUP_METADATA); + BUG_ON(ret); + ret = btrfs_make_block_group(trans, root, 0, + BTRFS_BLOCK_GROUP_METADATA, + BTRFS_FIRST_CHUNK_TREE_OBJECTID, + chunk_start, chunk_size); + BUG_ON(ret); + + root->fs_info->system_allocs = 0; + btrfs_commit_transaction(trans, root); + trans = btrfs_start_transaction(root, 1); + BUG_ON(!trans); + + ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, + &chunk_start, &chunk_size, + BTRFS_BLOCK_GROUP_DATA); + BUG_ON(ret); + ret = btrfs_make_block_group(trans, root, 0, + BTRFS_BLOCK_GROUP_DATA, + BTRFS_FIRST_CHUNK_TREE_OBJECTID, + chunk_start, chunk_size); + BUG_ON(ret); + + ret = btrfs_make_root_dir(trans, root->fs_info->tree_root, + BTRFS_ROOT_TREE_DIR_OBJECTID); + if (ret) + goto err; + ret = btrfs_make_root_dir(trans, root, BTRFS_FIRST_FREE_OBJECTID); + if (ret) + goto err; + memcpy(&location, &root->fs_info->fs_root->root_key, sizeof(location)); + location.offset = (u64)-1; + ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root, + "default", 7, + btrfs_super_root_dir(&root->fs_info->super_copy), + &location, BTRFS_FT_DIR, 0); + if (ret) + goto err; + + ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root, + "default", 7, location.objectid, + BTRFS_ROOT_TREE_DIR_OBJECTID, 0); + if (ret) + goto err; + + btrfs_commit_transaction(trans, root); +err: + return ret; +} + +static int recow_roots(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + int ret; + struct extent_buffer *tmp; + struct btrfs_fs_info *info = root->fs_info; + + ret = __btrfs_cow_block(trans, info->fs_root, info->fs_root->node, + NULL, 0, &tmp, 0, 0); + BUG_ON(ret); + free_extent_buffer(tmp); + + ret = __btrfs_cow_block(trans, info->tree_root, info->tree_root->node, + NULL, 0, &tmp, 0, 0); + BUG_ON(ret); + free_extent_buffer(tmp); + + ret = __btrfs_cow_block(trans, info->extent_root, + info->extent_root->node, NULL, 0, &tmp, 0, 0); + BUG_ON(ret); + free_extent_buffer(tmp); + + ret = __btrfs_cow_block(trans, info->chunk_root, info->chunk_root->node, + NULL, 0, &tmp, 0, 0); + BUG_ON(ret); + free_extent_buffer(tmp); + + + ret = __btrfs_cow_block(trans, info->dev_root, info->dev_root->node, + NULL, 0, &tmp, 0, 0); + BUG_ON(ret); + free_extent_buffer(tmp); + + ret = __btrfs_cow_block(trans, info->csum_root, info->csum_root->node, + NULL, 0, &tmp, 0, 0); + BUG_ON(ret); + free_extent_buffer(tmp); + + return 0; +} + +static int create_one_raid_group(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 type) +{ + u64 chunk_start; + u64 chunk_size; + int ret; + + ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, + &chunk_start, &chunk_size, type); + BUG_ON(ret); + ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0, + type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, + chunk_start, chunk_size); + BUG_ON(ret); + return ret; +} + +static int create_raid_groups(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 data_profile, + u64 metadata_profile) +{ + u64 num_devices = btrfs_super_num_devices(&root->fs_info->super_copy); + u64 allowed; + int ret; + + if (num_devices == 1) + allowed = BTRFS_BLOCK_GROUP_DUP; + else if (num_devices >= 4) { + allowed = BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_RAID10; + } else + allowed = BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1; + + if (allowed & metadata_profile) { + ret = create_one_raid_group(trans, root, + BTRFS_BLOCK_GROUP_SYSTEM | + (allowed & metadata_profile)); + BUG_ON(ret); + + ret = create_one_raid_group(trans, root, + BTRFS_BLOCK_GROUP_METADATA | + (allowed & metadata_profile)); + BUG_ON(ret); + + ret = recow_roots(trans, root); + BUG_ON(ret); + } + if (num_devices > 1 && (allowed & data_profile)) { + ret = create_one_raid_group(trans, root, + BTRFS_BLOCK_GROUP_DATA | + (allowed & data_profile)); + BUG_ON(ret); + } + return 0; +} + +static int create_data_reloc_tree(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + struct btrfs_key location; + struct btrfs_root_item root_item; + struct extent_buffer *tmp; + u64 objectid = BTRFS_DATA_RELOC_TREE_OBJECTID; + int ret; + + ret = btrfs_copy_root(trans, root, root->node, &tmp, objectid); + BUG_ON(ret); + + memcpy(&root_item, &root->root_item, sizeof(root_item)); + btrfs_set_root_bytenr(&root_item, tmp->start); + btrfs_set_root_level(&root_item, btrfs_header_level(tmp)); + btrfs_set_root_generation(&root_item, trans->transid); + free_extent_buffer(tmp); + + location.objectid = objectid; + location.type = BTRFS_ROOT_ITEM_KEY; + location.offset = 0; + ret = btrfs_insert_root(trans, root->fs_info->tree_root, + &location, &root_item); + BUG_ON(ret); + return 0; +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: mkfs.btrfs [options] dev [ dev ... ]\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, "\t -A --alloc-start the offset to start the FS\n"); + fprintf(stderr, "\t -b --byte-count total number of bytes in the FS\n"); + fprintf(stderr, "\t -d --data data profile, raid0, raid1, raid10 or single\n"); + fprintf(stderr, "\t -l --leafsize size of btree leaves\n"); + fprintf(stderr, "\t -L --label set a label\n"); + fprintf(stderr, "\t -m --metadata metadata profile, values like data profile\n"); + fprintf(stderr, "\t -n --nodesize size of btree nodes\n"); + fprintf(stderr, "\t -s --sectorsize min block allocation\n"); + fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); + exit(1); +} + +static void print_version(void) +{ + fprintf(stderr, "mkfs.btrfs, part of %s\n", BTRFS_BUILD_VERSION); + exit(0); +} + +static u64 parse_profile(char *s) +{ + if (strcmp(s, "raid0") == 0) { + return BTRFS_BLOCK_GROUP_RAID0; + } else if (strcmp(s, "raid1") == 0) { + return BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP; + } else if (strcmp(s, "raid10") == 0) { + return BTRFS_BLOCK_GROUP_RAID10 | BTRFS_BLOCK_GROUP_DUP; + } else if (strcmp(s, "single") == 0) { + return 0; + } else { + fprintf(stderr, "Unknown option %s\n", s); + print_usage(); + } + return 0; +} + +static char *parse_label(char *input) +{ + int i; + int len = strlen(input); + + if (len > BTRFS_LABEL_SIZE) { + fprintf(stderr, "Label %s is too long (max %d)\n", input, + BTRFS_LABEL_SIZE); + 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); +} + +static struct option long_options[] = { + { "alloc-start", 1, NULL, 'A'}, + { "byte-count", 1, NULL, 'b' }, + { "leafsize", 1, NULL, 'l' }, + { "label", 1, NULL, 'L'}, + { "metadata", 1, NULL, 'm' }, + { "nodesize", 1, NULL, 'n' }, + { "sectorsize", 1, NULL, 's' }, + { "data", 1, NULL, 'd' }, + { "version", 0, NULL, 'V' }, + { 0, 0, 0, 0} +}; + +int main(int ac, char **av) +{ + char *file; + struct btrfs_root *root; + struct btrfs_trans_handle *trans; + char *label = NULL; + char *first_file; + u64 block_count = 0; + u64 dev_block_count = 0; + u64 blocks[7]; + u64 alloc_start = 0; + u64 metadata_profile = BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP; + u64 data_profile = BTRFS_BLOCK_GROUP_RAID0; + u32 leafsize = getpagesize(); + u32 sectorsize = 4096; + u32 nodesize = leafsize; + u32 stripesize = 4096; + int zero_end = 1; + int option_index = 0; + int fd; + int first_fd; + int ret; + int i; + + while(1) { + int c; + c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:V", long_options, + &option_index); + if (c < 0) + break; + switch(c) { + case 'A': + alloc_start = parse_size(optarg); + break; + case 'd': + data_profile = parse_profile(optarg); + break; + case 'l': + leafsize = parse_size(optarg); + break; + case 'L': + label = parse_label(optarg); + break; + case 'm': + metadata_profile = parse_profile(optarg); + break; + case 'n': + nodesize = parse_size(optarg); + break; + case 's': + sectorsize = parse_size(optarg); + break; + case 'b': + block_count = parse_size(optarg); + if (block_count < 256*1024*1024) { + fprintf(stderr, "File system size " + "%llu bytes is too small, " + "256M is required at least\n", + (unsigned long long)block_count); + exit(1); + } + zero_end = 0; + break; + case 'V': + print_version(); + break; + default: + print_usage(); + } + } + sectorsize = max(sectorsize, (u32)getpagesize()); + if (leafsize < sectorsize || (leafsize & (sectorsize - 1))) { + fprintf(stderr, "Illegal leafsize %u\n", leafsize); + exit(1); + } + if (nodesize < sectorsize || (nodesize & (sectorsize - 1))) { + fprintf(stderr, "Illegal nodesize %u\n", nodesize); + exit(1); + } + ac = ac - optind; + if (ac == 0) + print_usage(); + + printf("\nWARNING! - %s IS EXPERIMENTAL\n", BTRFS_BUILD_VERSION); + printf("WARNING! - see http://btrfs.wiki.kernel.org before using\n\n"); + + file = av[optind++]; + ret = check_mounted(file); + if (ret < 0) { + fprintf(stderr, "error checking %s mount status\n", file); + exit(1); + } + if (ret == 1) { + fprintf(stderr, "%s is mounted\n", file); + exit(1); + } + ac--; + fd = open(file, O_RDWR); + if (fd < 0) { + fprintf(stderr, "unable to open %s\n", file); + exit(1); + } + first_fd = fd; + first_file = file; + ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count); + if (block_count == 0) + block_count = dev_block_count; + + blocks[0] = BTRFS_SUPER_INFO_OFFSET; + for (i = 1; i < 7; i++) { + blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 + + leafsize * i; + } + + ret = make_btrfs(fd, file, label, blocks, block_count, + nodesize, leafsize, + sectorsize, stripesize); + if (ret) { + fprintf(stderr, "error during mkfs %d\n", ret); + exit(1); + } + root = open_ctree(file, 0, O_RDWR); + root->fs_info->alloc_start = alloc_start; + + ret = make_root_dir(root); + if (ret) { + fprintf(stderr, "failed to setup the root directory\n"); + exit(1); + } + + trans = btrfs_start_transaction(root, 1); + + if (ac == 0) + goto raid_groups; + + btrfs_register_one_device(file); + if (!root) { + fprintf(stderr, "ctree init failed\n"); + return -1; + } + + zero_end = 1; + while(ac-- > 0) { + file = av[optind++]; + ret = check_mounted(file); + if (ret < 0) { + fprintf(stderr, "error checking %s mount status\n", + file); + exit(1); + } + if (ret == 1) { + fprintf(stderr, "%s is mounted\n", file); + exit(1); + } + fd = open(file, O_RDWR); + if (fd < 0) { + fprintf(stderr, "unable to open %s\n", file); + exit(1); + } + ret = btrfs_device_already_in_root(root, fd, + BTRFS_SUPER_INFO_OFFSET); + if (ret) { + fprintf(stderr, "skipping duplicate device %s in FS\n", + file); + close(fd); + continue; + } + ret = btrfs_prepare_device(fd, file, zero_end, + &dev_block_count); + + BUG_ON(ret); + + ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count, + sectorsize, sectorsize, sectorsize); + BUG_ON(ret); + btrfs_register_one_device(file); + } + +raid_groups: + ret = create_raid_groups(trans, root, data_profile, + metadata_profile); + BUG_ON(ret); + + ret = create_data_reloc_tree(trans, root); + BUG_ON(ret); + + printf("fs created label %s on %s\n\tnodesize %u leafsize %u " + "sectorsize %u size %s\n", + label, first_file, nodesize, leafsize, sectorsize, + pretty_sizes(btrfs_super_total_bytes(&root->fs_info->super_copy))); + + printf("%s\n", BTRFS_BUILD_VERSION); + btrfs_commit_transaction(trans, root); + ret = close_ctree(root); + BUG_ON(ret); + + free(label); + return 0; +} + diff --git a/misc/version.sh b/misc/version.sh new file mode 100644 index 0000000..af3e441 --- /dev/null +++ b/misc/version.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# +# determine-version -- report a useful version for releases +# +# Copyright 2008, Aron Griffis +# Copyright 2008, Oracle +# Released under the GNU GPLv2 + +v="Btrfs v0.19" + +which git &> /dev/null +if [ $? == 0 -a -d .git ]; then + if head=`git rev-parse --verify HEAD 2>/dev/null`; then + if tag=`git describe --tags 2>/dev/null`; then + v="$tag" + fi + + # Are there uncommitted changes? + git update-index --refresh --unmerged > /dev/null + if git diff-index --name-only HEAD | grep -v "^scripts/package" \ + | read dummy; then + v="$v"-dirty + fi + fi +fi + +which hg &> /dev/null +if [ $? == 0 -a -d .hg ]; then + last=$(hg tags | grep -m1 -o '^v[0-9.]\+') + + # now check if the repo has commits since then... + if [[ $(hg id -t) == $last || \ + $(hg di -r "$last:." | awk '/^diff/{print $NF}' | sort -u) == .hgtags ]] + then + # check if it's dirty + if [[ $(hg id | cut -d' ' -f1) == *+ ]]; then + v=$last+ + else + v=$last + fi + else + # includes dirty flag + v=$last+$(hg id -i) + fi +fi + +echo "#ifndef __BUILD_VERSION" > .build-version.h +echo "#define __BUILD_VERSION" >> .build-version.h +echo "#define BTRFS_BUILD_VERSION \"Btrfs $v\"" >> .build-version.h +echo "#endif" >> .build-version.h + +diff -q version.h .build-version.h >& /dev/null + +if [ $? == 0 ]; then + rm .build-version.h + exit 0 +fi + +mv .build-version.h version.h diff --git a/mkfs.c b/mkfs.c deleted file mode 100644 index 2e99b95..0000000 --- a/mkfs.c +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 - -#ifndef __CHECKER__ -#include -#include -#include "ioctl.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "ctree.h" -#include "disk-io.h" -#include "volumes.h" -#include "transaction.h" -#include "utils.h" -#include "version.h" - -static u64 parse_size(char *s) -{ - int len = strlen(s); - char c; - u64 mult = 1; - - if (!isdigit(s[len - 1])) { - c = tolower(s[len - 1]); - switch (c) { - case 'g': - mult *= 1024; - case 'm': - mult *= 1024; - case 'k': - mult *= 1024; - case 'b': - break; - default: - fprintf(stderr, "Unknown size descriptor %c\n", c); - exit(1); - } - s[len - 1] = '\0'; - } - return atol(s) * mult; -} - -static int make_root_dir(struct btrfs_root *root) -{ - struct btrfs_trans_handle *trans; - struct btrfs_key location; - u64 bytes_used; - u64 chunk_start = 0; - u64 chunk_size = 0; - int ret; - - trans = btrfs_start_transaction(root, 1); - bytes_used = btrfs_super_bytes_used(&root->fs_info->super_copy); - - root->fs_info->system_allocs = 1; - ret = btrfs_make_block_group(trans, root, bytes_used, - BTRFS_BLOCK_GROUP_SYSTEM, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, - 0, BTRFS_MKFS_SYSTEM_GROUP_SIZE); - BUG_ON(ret); - - ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, - &chunk_start, &chunk_size, - BTRFS_BLOCK_GROUP_METADATA); - BUG_ON(ret); - ret = btrfs_make_block_group(trans, root, 0, - BTRFS_BLOCK_GROUP_METADATA, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, - chunk_start, chunk_size); - BUG_ON(ret); - - root->fs_info->system_allocs = 0; - btrfs_commit_transaction(trans, root); - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - - ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, - &chunk_start, &chunk_size, - BTRFS_BLOCK_GROUP_DATA); - BUG_ON(ret); - ret = btrfs_make_block_group(trans, root, 0, - BTRFS_BLOCK_GROUP_DATA, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, - chunk_start, chunk_size); - BUG_ON(ret); - - ret = btrfs_make_root_dir(trans, root->fs_info->tree_root, - BTRFS_ROOT_TREE_DIR_OBJECTID); - if (ret) - goto err; - ret = btrfs_make_root_dir(trans, root, BTRFS_FIRST_FREE_OBJECTID); - if (ret) - goto err; - memcpy(&location, &root->fs_info->fs_root->root_key, sizeof(location)); - location.offset = (u64)-1; - ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root, - "default", 7, - btrfs_super_root_dir(&root->fs_info->super_copy), - &location, BTRFS_FT_DIR, 0); - if (ret) - goto err; - - ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root, - "default", 7, location.objectid, - BTRFS_ROOT_TREE_DIR_OBJECTID, 0); - if (ret) - goto err; - - btrfs_commit_transaction(trans, root); -err: - return ret; -} - -static int recow_roots(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - int ret; - struct extent_buffer *tmp; - struct btrfs_fs_info *info = root->fs_info; - - ret = __btrfs_cow_block(trans, info->fs_root, info->fs_root->node, - NULL, 0, &tmp, 0, 0); - BUG_ON(ret); - free_extent_buffer(tmp); - - ret = __btrfs_cow_block(trans, info->tree_root, info->tree_root->node, - NULL, 0, &tmp, 0, 0); - BUG_ON(ret); - free_extent_buffer(tmp); - - ret = __btrfs_cow_block(trans, info->extent_root, - info->extent_root->node, NULL, 0, &tmp, 0, 0); - BUG_ON(ret); - free_extent_buffer(tmp); - - ret = __btrfs_cow_block(trans, info->chunk_root, info->chunk_root->node, - NULL, 0, &tmp, 0, 0); - BUG_ON(ret); - free_extent_buffer(tmp); - - - ret = __btrfs_cow_block(trans, info->dev_root, info->dev_root->node, - NULL, 0, &tmp, 0, 0); - BUG_ON(ret); - free_extent_buffer(tmp); - - ret = __btrfs_cow_block(trans, info->csum_root, info->csum_root->node, - NULL, 0, &tmp, 0, 0); - BUG_ON(ret); - free_extent_buffer(tmp); - - return 0; -} - -static int create_one_raid_group(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 type) -{ - u64 chunk_start; - u64 chunk_size; - int ret; - - ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, - &chunk_start, &chunk_size, type); - BUG_ON(ret); - ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0, - type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, - chunk_start, chunk_size); - BUG_ON(ret); - return ret; -} - -static int create_raid_groups(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 data_profile, - u64 metadata_profile) -{ - u64 num_devices = btrfs_super_num_devices(&root->fs_info->super_copy); - u64 allowed; - int ret; - - if (num_devices == 1) - allowed = BTRFS_BLOCK_GROUP_DUP; - else if (num_devices >= 4) { - allowed = BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_RAID10; - } else - allowed = BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1; - - if (allowed & metadata_profile) { - ret = create_one_raid_group(trans, root, - BTRFS_BLOCK_GROUP_SYSTEM | - (allowed & metadata_profile)); - BUG_ON(ret); - - ret = create_one_raid_group(trans, root, - BTRFS_BLOCK_GROUP_METADATA | - (allowed & metadata_profile)); - BUG_ON(ret); - - ret = recow_roots(trans, root); - BUG_ON(ret); - } - if (num_devices > 1 && (allowed & data_profile)) { - ret = create_one_raid_group(trans, root, - BTRFS_BLOCK_GROUP_DATA | - (allowed & data_profile)); - BUG_ON(ret); - } - return 0; -} - -static int create_data_reloc_tree(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - struct btrfs_key location; - struct btrfs_root_item root_item; - struct extent_buffer *tmp; - u64 objectid = BTRFS_DATA_RELOC_TREE_OBJECTID; - int ret; - - ret = btrfs_copy_root(trans, root, root->node, &tmp, objectid); - BUG_ON(ret); - - memcpy(&root_item, &root->root_item, sizeof(root_item)); - btrfs_set_root_bytenr(&root_item, tmp->start); - btrfs_set_root_level(&root_item, btrfs_header_level(tmp)); - btrfs_set_root_generation(&root_item, trans->transid); - free_extent_buffer(tmp); - - location.objectid = objectid; - location.type = BTRFS_ROOT_ITEM_KEY; - location.offset = 0; - ret = btrfs_insert_root(trans, root->fs_info->tree_root, - &location, &root_item); - BUG_ON(ret); - return 0; -} - -static void print_usage(void) -{ - fprintf(stderr, "usage: mkfs.btrfs [options] dev [ dev ... ]\n"); - fprintf(stderr, "options:\n"); - fprintf(stderr, "\t -A --alloc-start the offset to start the FS\n"); - fprintf(stderr, "\t -b --byte-count total number of bytes in the FS\n"); - fprintf(stderr, "\t -d --data data profile, raid0, raid1, raid10 or single\n"); - fprintf(stderr, "\t -l --leafsize size of btree leaves\n"); - fprintf(stderr, "\t -L --label set a label\n"); - fprintf(stderr, "\t -m --metadata metadata profile, values like data profile\n"); - fprintf(stderr, "\t -n --nodesize size of btree nodes\n"); - fprintf(stderr, "\t -s --sectorsize min block allocation\n"); - fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); - exit(1); -} - -static void print_version(void) -{ - fprintf(stderr, "mkfs.btrfs, part of %s\n", BTRFS_BUILD_VERSION); - exit(0); -} - -static u64 parse_profile(char *s) -{ - if (strcmp(s, "raid0") == 0) { - return BTRFS_BLOCK_GROUP_RAID0; - } else if (strcmp(s, "raid1") == 0) { - return BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP; - } else if (strcmp(s, "raid10") == 0) { - return BTRFS_BLOCK_GROUP_RAID10 | BTRFS_BLOCK_GROUP_DUP; - } else if (strcmp(s, "single") == 0) { - return 0; - } else { - fprintf(stderr, "Unknown option %s\n", s); - print_usage(); - } - return 0; -} - -static char *parse_label(char *input) -{ - int i; - int len = strlen(input); - - if (len > BTRFS_LABEL_SIZE) { - fprintf(stderr, "Label %s is too long (max %d)\n", input, - BTRFS_LABEL_SIZE); - 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); -} - -static struct option long_options[] = { - { "alloc-start", 1, NULL, 'A'}, - { "byte-count", 1, NULL, 'b' }, - { "leafsize", 1, NULL, 'l' }, - { "label", 1, NULL, 'L'}, - { "metadata", 1, NULL, 'm' }, - { "nodesize", 1, NULL, 'n' }, - { "sectorsize", 1, NULL, 's' }, - { "data", 1, NULL, 'd' }, - { "version", 0, NULL, 'V' }, - { 0, 0, 0, 0} -}; - -int main(int ac, char **av) -{ - char *file; - struct btrfs_root *root; - struct btrfs_trans_handle *trans; - char *label = NULL; - char *first_file; - u64 block_count = 0; - u64 dev_block_count = 0; - u64 blocks[7]; - u64 alloc_start = 0; - u64 metadata_profile = BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP; - u64 data_profile = BTRFS_BLOCK_GROUP_RAID0; - u32 leafsize = getpagesize(); - u32 sectorsize = 4096; - u32 nodesize = leafsize; - u32 stripesize = 4096; - int zero_end = 1; - int option_index = 0; - int fd; - int first_fd; - int ret; - int i; - - while(1) { - int c; - c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:V", long_options, - &option_index); - if (c < 0) - break; - switch(c) { - case 'A': - alloc_start = parse_size(optarg); - break; - case 'd': - data_profile = parse_profile(optarg); - break; - case 'l': - leafsize = parse_size(optarg); - break; - case 'L': - label = parse_label(optarg); - break; - case 'm': - metadata_profile = parse_profile(optarg); - break; - case 'n': - nodesize = parse_size(optarg); - break; - case 's': - sectorsize = parse_size(optarg); - break; - case 'b': - block_count = parse_size(optarg); - if (block_count < 256*1024*1024) { - fprintf(stderr, "File system size " - "%llu bytes is too small, " - "256M is required at least\n", - (unsigned long long)block_count); - exit(1); - } - zero_end = 0; - break; - case 'V': - print_version(); - break; - default: - print_usage(); - } - } - sectorsize = max(sectorsize, (u32)getpagesize()); - if (leafsize < sectorsize || (leafsize & (sectorsize - 1))) { - fprintf(stderr, "Illegal leafsize %u\n", leafsize); - exit(1); - } - if (nodesize < sectorsize || (nodesize & (sectorsize - 1))) { - fprintf(stderr, "Illegal nodesize %u\n", nodesize); - exit(1); - } - ac = ac - optind; - if (ac == 0) - print_usage(); - - printf("\nWARNING! - %s IS EXPERIMENTAL\n", BTRFS_BUILD_VERSION); - printf("WARNING! - see http://btrfs.wiki.kernel.org before using\n\n"); - - file = av[optind++]; - ret = check_mounted(file); - if (ret < 0) { - fprintf(stderr, "error checking %s mount status\n", file); - exit(1); - } - if (ret == 1) { - fprintf(stderr, "%s is mounted\n", file); - exit(1); - } - ac--; - fd = open(file, O_RDWR); - if (fd < 0) { - fprintf(stderr, "unable to open %s\n", file); - exit(1); - } - first_fd = fd; - first_file = file; - ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count); - if (block_count == 0) - block_count = dev_block_count; - - blocks[0] = BTRFS_SUPER_INFO_OFFSET; - for (i = 1; i < 7; i++) { - blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 + - leafsize * i; - } - - ret = make_btrfs(fd, file, label, blocks, block_count, - nodesize, leafsize, - sectorsize, stripesize); - if (ret) { - fprintf(stderr, "error during mkfs %d\n", ret); - exit(1); - } - root = open_ctree(file, 0, O_RDWR); - root->fs_info->alloc_start = alloc_start; - - ret = make_root_dir(root); - if (ret) { - fprintf(stderr, "failed to setup the root directory\n"); - exit(1); - } - - trans = btrfs_start_transaction(root, 1); - - if (ac == 0) - goto raid_groups; - - btrfs_register_one_device(file); - if (!root) { - fprintf(stderr, "ctree init failed\n"); - return -1; - } - - zero_end = 1; - while(ac-- > 0) { - file = av[optind++]; - ret = check_mounted(file); - if (ret < 0) { - fprintf(stderr, "error checking %s mount status\n", - file); - exit(1); - } - if (ret == 1) { - fprintf(stderr, "%s is mounted\n", file); - exit(1); - } - fd = open(file, O_RDWR); - if (fd < 0) { - fprintf(stderr, "unable to open %s\n", file); - exit(1); - } - ret = btrfs_device_already_in_root(root, fd, - BTRFS_SUPER_INFO_OFFSET); - if (ret) { - fprintf(stderr, "skipping duplicate device %s in FS\n", - file); - close(fd); - continue; - } - ret = btrfs_prepare_device(fd, file, zero_end, - &dev_block_count); - - BUG_ON(ret); - - ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count, - sectorsize, sectorsize, sectorsize); - BUG_ON(ret); - btrfs_register_one_device(file); - } - -raid_groups: - ret = create_raid_groups(trans, root, data_profile, - metadata_profile); - BUG_ON(ret); - - ret = create_data_reloc_tree(trans, root); - BUG_ON(ret); - - printf("fs created label %s on %s\n\tnodesize %u leafsize %u " - "sectorsize %u size %s\n", - label, first_file, nodesize, leafsize, sectorsize, - pretty_sizes(btrfs_super_total_bytes(&root->fs_info->super_copy))); - - printf("%s\n", BTRFS_BUILD_VERSION); - btrfs_commit_transaction(trans, root); - ret = close_ctree(root); - BUG_ON(ret); - - free(label); - return 0; -} - diff --git a/print-tree.c b/print-tree.c deleted file mode 100644 index 59f4358..0000000 --- a/print-tree.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "print-tree.h" - -static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item, - struct btrfs_dir_item *di) -{ - u32 total; - u32 cur = 0; - u32 len; - u32 name_len; - u32 data_len; - char namebuf[BTRFS_NAME_LEN]; - struct btrfs_disk_key location; - - total = btrfs_item_size(eb, item); - while(cur < total) { - btrfs_dir_item_key(eb, di, &location); - printf("\t\tlocation "); - btrfs_print_key(&location); - printf(" type %u\n", btrfs_dir_type(eb, di)); - name_len = btrfs_dir_name_len(eb, di); - data_len = btrfs_dir_data_len(eb, di); - len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf); - read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len); - printf("\t\tnamelen %u datalen %u name: %.*s\n", - name_len, data_len, len, namebuf); - len = sizeof(*di) + name_len + data_len; - di = (struct btrfs_dir_item *)((char *)di + len); - cur += len; - } - return 0; -} - -static int print_inode_ref_item(struct extent_buffer *eb, struct btrfs_item *item, - struct btrfs_inode_ref *ref) -{ - u32 total; - u32 cur = 0; - u32 len; - u32 name_len; - u64 index; - char namebuf[BTRFS_NAME_LEN]; - total = btrfs_item_size(eb, item); - while(cur < total) { - name_len = btrfs_inode_ref_name_len(eb, ref); - index = btrfs_inode_ref_index(eb, ref); - len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf); - read_extent_buffer(eb, namebuf, (unsigned long)(ref + 1), len); - printf("\t\tinode ref index %llu namelen %u name: %.*s\n", - (unsigned long long)index, name_len, len, namebuf); - len = sizeof(*ref) + name_len; - ref = (struct btrfs_inode_ref *)((char *)ref + len); - cur += len; - } - return 0; -} - -static void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk) -{ - int num_stripes = btrfs_chunk_num_stripes(eb, chunk); - int i; - printf("\t\tchunk length %llu owner %llu type %llu num_stripes %d\n", - (unsigned long long)btrfs_chunk_length(eb, chunk), - (unsigned long long)btrfs_chunk_owner(eb, chunk), - (unsigned long long)btrfs_chunk_type(eb, chunk), - num_stripes); - for (i = 0 ; i < num_stripes ; i++) { - printf("\t\t\tstripe %d devid %llu offset %llu\n", i, - (unsigned long long)btrfs_stripe_devid_nr(eb, chunk, i), - (unsigned long long)btrfs_stripe_offset_nr(eb, chunk, i)); - } -} -static void print_dev_item(struct extent_buffer *eb, - struct btrfs_dev_item *dev_item) -{ - printf("\t\tdev item devid %llu " - "total_bytes %llu bytes used %Lu\n", - (unsigned long long)btrfs_device_id(eb, dev_item), - (unsigned long long)btrfs_device_total_bytes(eb, dev_item), - (unsigned long long)btrfs_device_bytes_used(eb, dev_item)); -} - -static void print_uuids(struct extent_buffer *eb) -{ - char fs_uuid[37]; - char chunk_uuid[37]; - u8 disk_uuid[BTRFS_UUID_SIZE]; - - read_extent_buffer(eb, disk_uuid, (unsigned long)btrfs_header_fsid(eb), - BTRFS_FSID_SIZE); - - fs_uuid[36] = '\0'; - uuid_unparse(disk_uuid, fs_uuid); - - read_extent_buffer(eb, disk_uuid, - (unsigned long)btrfs_header_chunk_tree_uuid(eb), - BTRFS_UUID_SIZE); - - chunk_uuid[36] = '\0'; - uuid_unparse(disk_uuid, chunk_uuid); - printf("fs uuid %s\nchunk uuid %s\n", fs_uuid, chunk_uuid); -} - -static void print_file_extent_item(struct extent_buffer *eb, - struct btrfs_item *item, - struct btrfs_file_extent_item *fi) -{ - int extent_type = btrfs_file_extent_type(eb, fi); - - if (extent_type == BTRFS_FILE_EXTENT_INLINE) { - printf("\t\tinline extent data size %u " - "ram %u compress %d\n", - btrfs_file_extent_inline_item_len(eb, item), - btrfs_file_extent_inline_len(eb, fi), - btrfs_file_extent_compression(eb, fi)); - return; - } - if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) { - printf("\t\tprealloc data disk byte %llu nr %llu\n", - (unsigned long long)btrfs_file_extent_disk_bytenr(eb, fi), - (unsigned long long)btrfs_file_extent_disk_num_bytes(eb, fi)); - printf("\t\tprealloc data offset %llu nr %llu\n", - (unsigned long long)btrfs_file_extent_offset(eb, fi), - (unsigned long long)btrfs_file_extent_num_bytes(eb, fi)); - return; - } - printf("\t\textent data disk byte %llu nr %llu\n", - (unsigned long long)btrfs_file_extent_disk_bytenr(eb, fi), - (unsigned long long)btrfs_file_extent_disk_num_bytes(eb, fi)); - printf("\t\textent data offset %llu nr %llu ram %llu\n", - (unsigned long long)btrfs_file_extent_offset(eb, fi), - (unsigned long long)btrfs_file_extent_num_bytes(eb, fi), - (unsigned long long)btrfs_file_extent_ram_bytes(eb, fi)); - printf("\t\textent compression %d\n", - btrfs_file_extent_compression(eb, fi)); -} - -static void print_extent_item(struct extent_buffer *eb, int slot) -{ - struct btrfs_extent_item *ei; - struct btrfs_extent_inline_ref *iref; - struct btrfs_extent_data_ref *dref; - struct btrfs_shared_data_ref *sref; - struct btrfs_disk_key key; - unsigned long end; - unsigned long ptr; - int type; - u32 item_size = btrfs_item_size_nr(eb, slot); - u64 flags; - u64 offset; - - if (item_size < sizeof(*ei)) { -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - struct btrfs_extent_item_v0 *ei0; - BUG_ON(item_size != sizeof(*ei0)); - ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0); - printf("\t\textent refs %u\n", - btrfs_extent_refs_v0(eb, ei0)); - return; -#else - BUG(); -#endif - } - - ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); - flags = btrfs_extent_flags(eb, ei); - - printf("\t\textent refs %llu gen %llu flags %llu\n", - (unsigned long long)btrfs_extent_refs(eb, ei), - (unsigned long long)btrfs_extent_generation(eb, ei), - (unsigned long long)flags); - - if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { - struct btrfs_tree_block_info *info; - info = (struct btrfs_tree_block_info *)(ei + 1); - btrfs_tree_block_key(eb, info, &key); - printf("\t\ttree block key (%llu %x %llu) level %d\n", - (unsigned long long)btrfs_disk_key_objectid(&key), - key.type, - (unsigned long long)btrfs_disk_key_offset(&key), - btrfs_tree_block_level(eb, info)); - iref = (struct btrfs_extent_inline_ref *)(info + 1); - } else { - iref = (struct btrfs_extent_inline_ref *)(ei + 1); - } - - ptr = (unsigned long)iref; - end = (unsigned long)ei + item_size; - while (ptr < end) { - iref = (struct btrfs_extent_inline_ref *)ptr; - type = btrfs_extent_inline_ref_type(eb, iref); - offset = btrfs_extent_inline_ref_offset(eb, iref); - switch (type) { - case BTRFS_TREE_BLOCK_REF_KEY: - printf("\t\ttree block backref root %llu\n", - (unsigned long long)offset); - break; - case BTRFS_SHARED_BLOCK_REF_KEY: - printf("\t\tshared block backref parent %llu\n", - (unsigned long long)offset); - break; - case BTRFS_EXTENT_DATA_REF_KEY: - dref = (struct btrfs_extent_data_ref *)(&iref->offset); - printf("\t\textent data backref root %llu " - "objectid %llu offset %llu count %u\n", - (unsigned long long)btrfs_extent_data_ref_root(eb, dref), - (unsigned long long)btrfs_extent_data_ref_objectid(eb, dref), - (unsigned long long)btrfs_extent_data_ref_offset(eb, dref), - btrfs_extent_data_ref_count(eb, dref)); - break; - case BTRFS_SHARED_DATA_REF_KEY: - sref = (struct btrfs_shared_data_ref *)(iref + 1); - printf("\t\tshared data backref parent %llu count %u\n", - (unsigned long long)offset, - btrfs_shared_data_ref_count(eb, sref)); - break; - default: - BUG(); - } - ptr += btrfs_extent_inline_ref_size(type); - } - WARN_ON(ptr > end); -} - -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 -static void print_extent_ref_v0(struct extent_buffer *eb, int slot) -{ - struct btrfs_extent_ref_v0 *ref0; - - ref0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_ref_v0); - printf("\t\textent back ref root %llu gen %llu " - "owner %llu num_refs %lu\n", - (unsigned long long)btrfs_ref_root_v0(eb, ref0), - (unsigned long long)btrfs_ref_generation_v0(eb, ref0), - (unsigned long long)btrfs_ref_objectid_v0(eb, ref0), - (unsigned long)btrfs_ref_count_v0(eb, ref0)); -} -#endif - -static void print_root_ref(struct extent_buffer *leaf, int slot, char *tag) -{ - struct btrfs_root_ref *ref; - char namebuf[BTRFS_NAME_LEN]; - int namelen; - - ref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); - namelen = btrfs_root_ref_name_len(leaf, ref); - read_extent_buffer(leaf, namebuf, (unsigned long)(ref + 1), namelen); - printf("\t\troot %s key dirid %llu sequence %llu name %.*s\n", tag, - (unsigned long long)btrfs_root_ref_dirid(leaf, ref), - (unsigned long long)btrfs_root_ref_sequence(leaf, ref), - namelen, namebuf); -} - -static void print_key_type(u8 type) -{ - switch (type) { - case BTRFS_INODE_ITEM_KEY: - printf("INODE_ITEM"); - break; - case BTRFS_INODE_REF_KEY: - printf("INODE_REF"); - break; - case BTRFS_DIR_ITEM_KEY: - printf("DIR_ITEM"); - break; - case BTRFS_DIR_INDEX_KEY: - printf("DIR_INDEX"); - break; - case BTRFS_DIR_LOG_ITEM_KEY: - printf("DIR_LOG_ITEM"); - break; - case BTRFS_DIR_LOG_INDEX_KEY: - printf("DIR_LOG_INDEX"); - break; - case BTRFS_XATTR_ITEM_KEY: - printf("XATTR_ITEM"); - break; - case BTRFS_ORPHAN_ITEM_KEY: - printf("ORPHAN_ITEM"); - break; - case BTRFS_ROOT_ITEM_KEY: - printf("ROOT_ITEM"); - break; - case BTRFS_ROOT_REF_KEY: - printf("ROOT_REF"); - break; - case BTRFS_ROOT_BACKREF_KEY: - printf("ROOT_BACKREF"); - break; - case BTRFS_EXTENT_ITEM_KEY: - printf("EXTENT_ITEM"); - break; - case BTRFS_TREE_BLOCK_REF_KEY: - printf("TREE_BLOCK_REF"); - break; - case BTRFS_SHARED_BLOCK_REF_KEY: - printf("SHARED_BLOCK_REF"); - break; - case BTRFS_EXTENT_DATA_REF_KEY: - printf("EXTENT_DATA_REF"); - break; - case BTRFS_SHARED_DATA_REF_KEY: - printf("SHARED_DATA_REF"); - break; - case BTRFS_EXTENT_REF_V0_KEY: - printf("EXTENT_REF_V0"); - break; - case BTRFS_CSUM_ITEM_KEY: - printf("CSUM_ITEM"); - break; - case BTRFS_EXTENT_CSUM_KEY: - printf("EXTENT_CSUM"); - break; - case BTRFS_EXTENT_DATA_KEY: - printf("EXTENT_DATA"); - break; - case BTRFS_BLOCK_GROUP_ITEM_KEY: - printf("BLOCK_GROUP_ITEM"); - break; - case BTRFS_CHUNK_ITEM_KEY: - printf("CHUNK_ITEM"); - break; - case BTRFS_DEV_ITEM_KEY: - printf("DEV_ITEM"); - break; - case BTRFS_DEV_EXTENT_KEY: - printf("DEV_EXTENT"); - break; - case BTRFS_STRING_ITEM_KEY: - printf("STRING_ITEM"); - break; - default: - printf("UNKNOWN"); - }; -} - -static void print_objectid(unsigned long long objectid, u8 type) -{ - if (type == BTRFS_DEV_EXTENT_KEY) { - printf("%llu", objectid); /* device id */ - return; - } - - switch (objectid) { - case BTRFS_ROOT_TREE_OBJECTID: - if (type == BTRFS_DEV_ITEM_KEY) - printf("DEV_ITEMS"); - else - printf("ROOT_TREE"); - break; - case BTRFS_EXTENT_TREE_OBJECTID: - printf("EXTENT_TREE"); - break; - case BTRFS_CHUNK_TREE_OBJECTID: - printf("CHUNK_TREE"); - break; - case BTRFS_DEV_TREE_OBJECTID: - printf("DEV_TREE"); - break; - case BTRFS_FS_TREE_OBJECTID: - printf("FS_TREE"); - break; - case BTRFS_ROOT_TREE_DIR_OBJECTID: - printf("ROOT_TREE_DIR"); - break; - case BTRFS_CSUM_TREE_OBJECTID: - printf("CSUM_TREE"); - break; - case BTRFS_ORPHAN_OBJECTID: - printf("ORPHAN"); - break; - case BTRFS_TREE_LOG_OBJECTID: - printf("TREE_LOG"); - break; - case BTRFS_TREE_LOG_FIXUP_OBJECTID: - printf("LOG_FIXUP"); - break; - case BTRFS_TREE_RELOC_OBJECTID: - printf("TREE_RELOC"); - break; - case BTRFS_DATA_RELOC_TREE_OBJECTID: - printf("DATA_RELOC_TREE"); - break; - case BTRFS_EXTENT_CSUM_OBJECTID: - printf("EXTENT_CSUM"); - break; - case BTRFS_MULTIPLE_OBJECTIDS: - printf("MULTIPLE"); - break; - case BTRFS_FIRST_CHUNK_TREE_OBJECTID: - printf("FIRST_CHUNK_TREE"); - break; - default: - printf("%llu", objectid); - } -} - -void btrfs_print_key(struct btrfs_disk_key *disk_key) -{ - u8 type; - printf("key ("); - type = btrfs_disk_key_type(disk_key); - print_objectid((unsigned long long)btrfs_disk_key_objectid(disk_key), - type); - printf(" "); - print_key_type(type); - printf(" %llu)", (unsigned long long)btrfs_disk_key_offset(disk_key)); -} - -void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) -{ - int i; - char *str; - struct btrfs_item *item; - struct btrfs_root_item *ri; - struct btrfs_dir_item *di; - struct btrfs_inode_item *ii; - struct btrfs_file_extent_item *fi; - struct btrfs_csum_item *ci; - struct btrfs_block_group_item *bi; - struct btrfs_extent_data_ref *dref; - struct btrfs_shared_data_ref *sref; - struct btrfs_inode_ref *iref; - struct btrfs_dev_extent *dev_extent; - struct btrfs_disk_key disk_key; - struct btrfs_root_item root_item; - struct btrfs_block_group_item bg_item; - struct btrfs_dir_log_item *dlog; - u32 nr = btrfs_header_nritems(l); - u32 type; - - printf("leaf %llu items %d free space %d generation %llu owner %llu\n", - (unsigned long long)btrfs_header_bytenr(l), nr, - btrfs_leaf_free_space(root, l), - (unsigned long long)btrfs_header_generation(l), - (unsigned long long)btrfs_header_owner(l)); - print_uuids(l); - fflush(stdout); - for (i = 0 ; i < nr ; i++) { - item = btrfs_item_nr(l, i); - btrfs_item_key(l, &disk_key, i); - type = btrfs_disk_key_type(&disk_key); - printf("\titem %d ", i); - btrfs_print_key(&disk_key); - printf(" itemoff %d itemsize %d\n", - btrfs_item_offset(l, item), - btrfs_item_size(l, item)); - switch (type) { - case BTRFS_INODE_ITEM_KEY: - ii = btrfs_item_ptr(l, i, struct btrfs_inode_item); - printf("\t\tinode generation %llu size %llu block group %llu mode %o links %u\n", - (unsigned long long)btrfs_inode_generation(l, ii), - (unsigned long long)btrfs_inode_size(l, ii), - (unsigned long long)btrfs_inode_block_group(l,ii), - btrfs_inode_mode(l, ii), - btrfs_inode_nlink(l, ii)); - break; - case BTRFS_INODE_REF_KEY: - iref = btrfs_item_ptr(l, i, struct btrfs_inode_ref); - print_inode_ref_item(l, item, iref); - break; - case BTRFS_DIR_ITEM_KEY: - case BTRFS_DIR_INDEX_KEY: - case BTRFS_XATTR_ITEM_KEY: - di = btrfs_item_ptr(l, i, struct btrfs_dir_item); - print_dir_item(l, item, di); - break; - case BTRFS_DIR_LOG_INDEX_KEY: - case BTRFS_DIR_LOG_ITEM_KEY: - dlog = btrfs_item_ptr(l, i, struct btrfs_dir_log_item); - printf("\t\tdir log end %Lu\n", - btrfs_dir_log_end(l, dlog)); - break; - case BTRFS_ORPHAN_ITEM_KEY: - printf("\t\torphan item\n"); - break; - case BTRFS_ROOT_ITEM_KEY: - ri = btrfs_item_ptr(l, i, struct btrfs_root_item); - read_extent_buffer(l, &root_item, (unsigned long)ri, sizeof(root_item)); - printf("\t\troot data bytenr %llu level %d dirid %llu refs %u\n", - (unsigned long long)btrfs_root_bytenr(&root_item), - btrfs_root_level(&root_item), - (unsigned long long)btrfs_root_dirid(&root_item), - btrfs_root_refs(&root_item)); - if (btrfs_root_refs(&root_item) == 0) { - struct btrfs_key drop_key; - btrfs_disk_key_to_cpu(&drop_key, - &root_item.drop_progress); - printf("\t\tdrop "); - btrfs_print_key(&root_item.drop_progress); - printf(" level %d\n", root_item.drop_level); - } - break; - case BTRFS_ROOT_REF_KEY: - print_root_ref(l, i, "ref"); - break; - case BTRFS_ROOT_BACKREF_KEY: - print_root_ref(l, i, "backref"); - break; - case BTRFS_EXTENT_ITEM_KEY: - print_extent_item(l, i); - break; - case BTRFS_TREE_BLOCK_REF_KEY: - printf("\t\ttree block backref\n"); - break; - case BTRFS_SHARED_BLOCK_REF_KEY: - printf("\t\tshared block backref\n"); - break; - case BTRFS_EXTENT_DATA_REF_KEY: - dref = btrfs_item_ptr(l, i, struct btrfs_extent_data_ref); - printf("\t\textent data backref root %llu " - "objectid %llu offset %llu count %u\n", - (unsigned long long)btrfs_extent_data_ref_root(l, dref), - (unsigned long long)btrfs_extent_data_ref_objectid(l, dref), - (unsigned long long)btrfs_extent_data_ref_offset(l, dref), - btrfs_extent_data_ref_count(l, dref)); - break; - case BTRFS_SHARED_DATA_REF_KEY: - sref = btrfs_item_ptr(l, i, struct btrfs_shared_data_ref); - printf("\t\tshared data backref count %u\n", - btrfs_shared_data_ref_count(l, sref)); - break; - case BTRFS_EXTENT_REF_V0_KEY: -#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - print_extent_ref_v0(l, i); -#else - BUG(); -#endif - break; - case BTRFS_CSUM_ITEM_KEY: - ci = btrfs_item_ptr(l, i, struct btrfs_csum_item); - printf("\t\tcsum item\n"); - break; - case BTRFS_EXTENT_CSUM_KEY: - ci = btrfs_item_ptr(l, i, struct btrfs_csum_item); - printf("\t\textent csum item\n"); - break; - case BTRFS_EXTENT_DATA_KEY: - fi = btrfs_item_ptr(l, i, - struct btrfs_file_extent_item); - print_file_extent_item(l, item, fi); - break; - case BTRFS_BLOCK_GROUP_ITEM_KEY: - bi = btrfs_item_ptr(l, i, - struct btrfs_block_group_item); - read_extent_buffer(l, &bg_item, (unsigned long)bi, - sizeof(bg_item)); - printf("\t\tblock group used %llu chunk_objectid %llu flags %llu\n", - (unsigned long long)btrfs_block_group_used(&bg_item), - (unsigned long long)btrfs_block_group_chunk_objectid(&bg_item), - (unsigned long long)btrfs_block_group_flags(&bg_item)); - break; - case BTRFS_CHUNK_ITEM_KEY: - print_chunk(l, btrfs_item_ptr(l, i, struct btrfs_chunk)); - break; - case BTRFS_DEV_ITEM_KEY: - print_dev_item(l, btrfs_item_ptr(l, i, - struct btrfs_dev_item)); - break; - case BTRFS_DEV_EXTENT_KEY: - dev_extent = btrfs_item_ptr(l, i, - struct btrfs_dev_extent); - printf("\t\tdev extent chunk_tree %llu\n" - "\t\tchunk objectid %llu chunk offset %llu " - "length %llu\n", - (unsigned long long) - btrfs_dev_extent_chunk_tree(l, dev_extent), - (unsigned long long) - btrfs_dev_extent_chunk_objectid(l, dev_extent), - (unsigned long long) - btrfs_dev_extent_chunk_offset(l, dev_extent), - (unsigned long long) - btrfs_dev_extent_length(l, dev_extent)); - break; - case BTRFS_STRING_ITEM_KEY: - /* dirty, but it's simple */ - str = l->data + btrfs_item_ptr_offset(l, i); - printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str); - break; - }; - fflush(stdout); - } -} - -void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb) -{ - int i; - u32 nr; - u32 size; - struct btrfs_disk_key disk_key; - struct btrfs_key key; - - if (!eb) - return; - nr = btrfs_header_nritems(eb); - if (btrfs_is_leaf(eb)) { - btrfs_print_leaf(root, eb); - return; - } - printf("node %llu level %d items %d free %u generation %llu owner %llu\n", - (unsigned long long)eb->start, - btrfs_header_level(eb), nr, - (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr, - (unsigned long long)btrfs_header_generation(eb), - (unsigned long long)btrfs_header_owner(eb)); - print_uuids(eb); - fflush(stdout); - size = btrfs_level_size(root, btrfs_header_level(eb) - 1); - for (i = 0; i < nr; i++) { - u64 blocknr = btrfs_node_blockptr(eb, i); - btrfs_node_key(eb, &disk_key, i); - btrfs_disk_key_to_cpu(&key, &disk_key); - printf("\t"); - btrfs_print_key(&disk_key); - printf(" block %llu (%llu) gen %llu\n", - (unsigned long long)blocknr, - (unsigned long long)blocknr / size, - (unsigned long long)btrfs_node_ptr_generation(eb, i)); - fflush(stdout); - } - for (i = 0; i < nr; i++) { - struct extent_buffer *next = read_tree_block(root, - btrfs_node_blockptr(eb, i), - size, - btrfs_node_ptr_generation(eb, i)); - if (!next) { - fprintf(stderr, "failed to read %llu in tree %llu\n", - (unsigned long long)btrfs_node_blockptr(eb, i), - (unsigned long long)btrfs_header_owner(eb)); - continue; - } - if (btrfs_is_leaf(next) && - btrfs_header_level(eb) != 1) - BUG(); - if (btrfs_header_level(next) != - btrfs_header_level(eb) - 1) - BUG(); - btrfs_print_tree(root, next); - free_extent_buffer(next); - } -} - diff --git a/print-tree.h b/print-tree.h deleted file mode 100644 index 4d1a01a..0000000 --- a/print-tree.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __PRINT_TREE_ -#define __PRINT_TREE_ -void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l); -void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *t); -void btrfs_print_key(struct btrfs_disk_key *disk_key); -#endif diff --git a/quick-test.c b/quick-test.c deleted file mode 100644 index 351c706..0000000 --- a/quick-test.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "print-tree.h" -#include "transaction.h" - -/* for testing only */ -int next_key(int i, int max_key) { - return rand() % max_key; - // return i; -} - -int main(int ac, char **av) { - struct btrfs_key ins; - struct btrfs_key last = { (u64)-1, 0, 0}; - char *buf; - int i; - int num; - int ret; - int run_size = 300000; - int max_key = 100000000; - int tree_size = 2; - struct btrfs_path path; - struct btrfs_root *root; - struct btrfs_trans_handle *trans; - - buf = malloc(512); - memset(buf, 0, 512); - - radix_tree_init(); - - root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); - trans = btrfs_start_transaction(root, 1); - srand(55); - btrfs_set_key_type(&ins, BTRFS_STRING_ITEM_KEY); - for (i = 0; i < run_size; i++) { - num = next_key(i, max_key); - // num = i; - sprintf(buf, "string-%d", num); - if (i % 10000 == 0) - fprintf(stderr, "insert %d:%d\n", num, i); - ins.objectid = num; - ins.offset = 0; - ret = btrfs_insert_item(trans, root, &ins, buf, 512); - if (!ret) - tree_size++; - if (i == run_size - 5) { - btrfs_commit_transaction(trans, root); - trans = btrfs_start_transaction(root, 1); - } - } - btrfs_commit_transaction(trans, root); - close_ctree(root); - exit(1); - root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); - printf("starting search\n"); - srand(55); - for (i = 0; i < run_size; i++) { - num = next_key(i, max_key); - ins.objectid = num; - btrfs_init_path(&path); - if (i % 10000 == 0) - fprintf(stderr, "search %d:%d\n", num, i); - ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0); - if (ret) { - btrfs_print_tree(root, root->node); - printf("unable to find %d\n", num); - exit(1); - } - btrfs_release_path(root, &path); - } - close_ctree(root); - - root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); - printf("node %p level %d total ptrs %d free spc %lu\n", root->node, - btrfs_header_level(root->node), - btrfs_header_nritems(root->node), - (unsigned long)BTRFS_NODEPTRS_PER_BLOCK(root) - - btrfs_header_nritems(root->node)); - printf("all searches good, deleting some items\n"); - i = 0; - srand(55); - trans = btrfs_start_transaction(root, 1); - for (i = 0 ; i < run_size/4; i++) { - num = next_key(i, max_key); - ins.objectid = num; - btrfs_init_path(&path); - ret = btrfs_search_slot(trans, root, &ins, &path, -1, 1); - if (!ret) { - if (i % 10000 == 0) - fprintf(stderr, "del %d:%d\n", num, i); - ret = btrfs_del_item(trans, root, &path); - if (ret != 0) - BUG(); - tree_size--; - } - btrfs_release_path(root, &path); - } - btrfs_commit_transaction(trans, root); - close_ctree(root); - - root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); - trans = btrfs_start_transaction(root, 1); - srand(128); - for (i = 0; i < run_size; i++) { - num = next_key(i, max_key); - sprintf(buf, "string-%d", num); - ins.objectid = num; - if (i % 10000 == 0) - fprintf(stderr, "insert %d:%d\n", num, i); - ret = btrfs_insert_item(trans, root, &ins, buf, 512); - if (!ret) - tree_size++; - } - btrfs_commit_transaction(trans, root); - close_ctree(root); - - root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); - srand(128); - printf("starting search2\n"); - for (i = 0; i < run_size; i++) { - num = next_key(i, max_key); - ins.objectid = num; - btrfs_init_path(&path); - if (i % 10000 == 0) - fprintf(stderr, "search %d:%d\n", num, i); - ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0); - if (ret) { - btrfs_print_tree(root, root->node); - printf("unable to find %d\n", num); - exit(1); - } - btrfs_release_path(root, &path); - } - printf("starting big long delete run\n"); - trans = btrfs_start_transaction(root, 1); - while(root->node && btrfs_header_nritems(root->node) > 0) { - struct extent_buffer *leaf; - int slot; - ins.objectid = (u64)-1; - btrfs_init_path(&path); - ret = btrfs_search_slot(trans, root, &ins, &path, -1, 1); - if (ret == 0) - BUG(); - - leaf = path.nodes[0]; - slot = path.slots[0]; - if (slot != btrfs_header_nritems(leaf)) - BUG(); - while(path.slots[0] > 0) { - path.slots[0] -= 1; - slot = path.slots[0]; - leaf = path.nodes[0]; - - btrfs_item_key_to_cpu(leaf, &last, slot); - - if (tree_size % 10000 == 0) - printf("big del %d:%d\n", tree_size, i); - ret = btrfs_del_item(trans, root, &path); - if (ret != 0) { - printf("del_item returned %d\n", ret); - BUG(); - } - tree_size--; - } - btrfs_release_path(root, &path); - } - /* - printf("previous tree:\n"); - btrfs_print_tree(root, root->commit_root); - printf("map before commit\n"); - btrfs_print_tree(root->extent_root, root->extent_root->node); - */ - btrfs_commit_transaction(trans, root); - printf("tree size is now %d\n", tree_size); - printf("root %p commit root %p\n", root->node, root->commit_root); - btrfs_print_tree(root, root->node); - close_ctree(root); - return 0; -} diff --git a/radix-tree.c b/radix-tree.c deleted file mode 100644 index ed01810..0000000 --- a/radix-tree.c +++ /dev/null @@ -1,854 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -/* - * Copyright (C) 2001 Momchil Velikov - * Portions Copyright (C) 2001 Christoph Hellwig - * Copyright (C) 2005 SGI, Christoph Lameter - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2, or (at - * your option) any later version. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "kerncompat.h" -#include "radix-tree.h" -#ifdef __KERNEL__ -#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6) -#else -#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */ -#endif - -#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) -#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) - -#define RADIX_TREE_TAG_LONGS \ - ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG) - -struct radix_tree_node { - unsigned int count; - void *slots[RADIX_TREE_MAP_SIZE]; - unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS]; -}; - -struct radix_tree_path { - struct radix_tree_node *node; - int offset; -}; - -#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) -#define RADIX_TREE_MAX_PATH (RADIX_TREE_INDEX_BITS/RADIX_TREE_MAP_SHIFT + 2) - -static unsigned long height_to_maxindex[RADIX_TREE_MAX_PATH] __read_mostly; - -/* - * Per-cpu pool of preloaded nodes - */ -struct radix_tree_preload { - int nr; - struct radix_tree_node *nodes[RADIX_TREE_MAX_PATH]; -}; -struct radix_tree_preload radix_tree_preloads = { 0, }; - -static inline gfp_t root_gfp_mask(struct radix_tree_root *root) -{ - return root->gfp_mask & __GFP_BITS_MASK; -} - -static int internal_nodes = 0; -/* - * This assumes that the caller has performed appropriate preallocation, and - * that the caller has pinned this thread of control to the current CPU. - */ -static struct radix_tree_node * -radix_tree_node_alloc(struct radix_tree_root *root) -{ - struct radix_tree_node *ret; - ret = malloc(sizeof(struct radix_tree_node)); - if (ret) { - memset(ret, 0, sizeof(struct radix_tree_node)); - internal_nodes++; - } - return ret; -} - -static inline void -radix_tree_node_free(struct radix_tree_node *node) -{ - internal_nodes--; - free(node); -} - -/* - * Load up this CPU's radix_tree_node buffer with sufficient objects to - * ensure that the addition of a single element in the tree cannot fail. On - * success, return zero, with preemption disabled. On error, return -ENOMEM - * with preemption not disabled. - */ -int radix_tree_preload(gfp_t gfp_mask) -{ - struct radix_tree_preload *rtp; - struct radix_tree_node *node; - int ret = -ENOMEM; - - preempt_disable(); - rtp = &__get_cpu_var(radix_tree_preloads); - while (rtp->nr < ARRAY_SIZE(rtp->nodes)) { - preempt_enable(); - node = radix_tree_node_alloc(NULL); - if (node == NULL) - goto out; - preempt_disable(); - rtp = &__get_cpu_var(radix_tree_preloads); - if (rtp->nr < ARRAY_SIZE(rtp->nodes)) - rtp->nodes[rtp->nr++] = node; - else - radix_tree_node_free(node); - } - ret = 0; -out: - return ret; -} - -static inline void tag_set(struct radix_tree_node *node, unsigned int tag, - int offset) -{ - __set_bit(offset, node->tags[tag]); -} - -static inline void tag_clear(struct radix_tree_node *node, unsigned int tag, - int offset) -{ - __clear_bit(offset, node->tags[tag]); -} - -static inline int tag_get(struct radix_tree_node *node, unsigned int tag, - int offset) -{ - return test_bit(offset, node->tags[tag]); -} - -static inline void root_tag_set(struct radix_tree_root *root, unsigned int tag) -{ - root->gfp_mask |= (__force gfp_t)(1 << (tag + __GFP_BITS_SHIFT)); -} - - -static inline void root_tag_clear(struct radix_tree_root *root, unsigned int tag) -{ - root->gfp_mask &= (__force gfp_t)~(1 << (tag + __GFP_BITS_SHIFT)); -} - -static inline void root_tag_clear_all(struct radix_tree_root *root) -{ - root->gfp_mask &= __GFP_BITS_MASK; -} - -static inline int root_tag_get(struct radix_tree_root *root, unsigned int tag) -{ - return (__force unsigned)root->gfp_mask & (1 << (tag + __GFP_BITS_SHIFT)); -} - -/* - * Returns 1 if any slot in the node has this tag set. - * Otherwise returns 0. - */ -static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag) -{ - int idx; - for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { - if (node->tags[tag][idx]) - return 1; - } - return 0; -} - -/* - * Return the maximum key which can be store into a - * radix tree with height HEIGHT. - */ -static inline unsigned long radix_tree_maxindex(unsigned int height) -{ - return height_to_maxindex[height]; -} - -/* - * Extend a radix tree so it can store key @index. - */ -static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) -{ - struct radix_tree_node *node; - unsigned int height; - int tag; - - /* Figure out what the height should be. */ - height = root->height + 1; - while (index > radix_tree_maxindex(height)) - height++; - - if (root->rnode == NULL) { - root->height = height; - goto out; - } - - do { - if (!(node = radix_tree_node_alloc(root))) - return -ENOMEM; - - /* Increase the height. */ - node->slots[0] = root->rnode; - - /* Propagate the aggregated tag info into the new root */ - for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { - if (root_tag_get(root, tag)) - tag_set(node, tag, 0); - } - - node->count = 1; - root->rnode = node; - root->height++; - } while (height > root->height); -out: - return 0; -} - -/** - * radix_tree_insert - insert into a radix tree - * @root: radix tree root - * @index: index key - * @item: item to insert - * - * Insert an item into the radix tree at position @index. - */ -int radix_tree_insert(struct radix_tree_root *root, - unsigned long index, void *item) -{ - struct radix_tree_node *node = NULL, *slot; - unsigned int height, shift; - int offset; - int error; - - /* Make sure the tree is high enough. */ - if (index > radix_tree_maxindex(root->height)) { - error = radix_tree_extend(root, index); - if (error) - return error; - } - - slot = root->rnode; - height = root->height; - shift = (height-1) * RADIX_TREE_MAP_SHIFT; - - offset = 0; /* uninitialised var warning */ - while (height > 0) { - if (slot == NULL) { - /* Have to add a child node. */ - if (!(slot = radix_tree_node_alloc(root))) - return -ENOMEM; - if (node) { - node->slots[offset] = slot; - node->count++; - } else - root->rnode = slot; - } - - /* Go a level down */ - offset = (index >> shift) & RADIX_TREE_MAP_MASK; - node = slot; - slot = node->slots[offset]; - shift -= RADIX_TREE_MAP_SHIFT; - height--; - } - - if (slot != NULL) - return -EEXIST; - - if (node) { - node->count++; - node->slots[offset] = item; - BUG_ON(tag_get(node, 0, offset)); - BUG_ON(tag_get(node, 1, offset)); - } else { - root->rnode = item; - BUG_ON(root_tag_get(root, 0)); - BUG_ON(root_tag_get(root, 1)); - } - - return 0; -} - -static inline void **__lookup_slot(struct radix_tree_root *root, - unsigned long index) -{ - unsigned int height, shift; - struct radix_tree_node **slot; - - height = root->height; - - if (index > radix_tree_maxindex(height)) - return NULL; - - if (height == 0 && root->rnode) - return (void *)&root->rnode; - - shift = (height-1) * RADIX_TREE_MAP_SHIFT; - slot = &root->rnode; - - while (height > 0) { - if (*slot == NULL) - return NULL; - - slot = (struct radix_tree_node **) - ((*slot)->slots + - ((index >> shift) & RADIX_TREE_MAP_MASK)); - shift -= RADIX_TREE_MAP_SHIFT; - height--; - } - - return (void **)slot; -} - -/** - * radix_tree_lookup_slot - lookup a slot in a radix tree - * @root: radix tree root - * @index: index key - * - * Lookup the slot corresponding to the position @index in the radix tree - * @root. This is useful for update-if-exists operations. - */ -void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) -{ - return __lookup_slot(root, index); -} - -/** - * radix_tree_lookup - perform lookup operation on a radix tree - * @root: radix tree root - * @index: index key - * - * Lookup the item at the position @index in the radix tree @root. - */ -void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) -{ - void **slot; - - slot = __lookup_slot(root, index); - return slot != NULL ? *slot : NULL; -} - -/** - * radix_tree_tag_set - set a tag on a radix tree node - * @root: radix tree root - * @index: index key - * @tag: tag index - * - * Set the search tag (which must be < RADIX_TREE_MAX_TAGS) - * corresponding to @index in the radix tree. From - * the root all the way down to the leaf node. - * - * Returns the address of the tagged item. Setting a tag on a not-present - * item is a bug. - */ -void *radix_tree_tag_set(struct radix_tree_root *root, - unsigned long index, unsigned int tag) -{ - unsigned int height, shift; - struct radix_tree_node *slot; - - height = root->height; - BUG_ON(index > radix_tree_maxindex(height)); - - slot = root->rnode; - shift = (height - 1) * RADIX_TREE_MAP_SHIFT; - - while (height > 0) { - int offset; - - offset = (index >> shift) & RADIX_TREE_MAP_MASK; - if (!tag_get(slot, tag, offset)) - tag_set(slot, tag, offset); - slot = slot->slots[offset]; - BUG_ON(slot == NULL); - shift -= RADIX_TREE_MAP_SHIFT; - height--; - } - - /* set the root's tag bit */ - if (slot && !root_tag_get(root, tag)) - root_tag_set(root, tag); - - return slot; -} - -/** - * radix_tree_tag_clear - clear a tag on a radix tree node - * @root: radix tree root - * @index: index key - * @tag: tag index - * - * Clear the search tag (which must be < RADIX_TREE_MAX_TAGS) - * corresponding to @index in the radix tree. If - * this causes the leaf node to have no tags set then clear the tag in the - * next-to-leaf node, etc. - * - * Returns the address of the tagged item on success, else NULL. ie: - * has the same return value and semantics as radix_tree_lookup(). - */ -void *radix_tree_tag_clear(struct radix_tree_root *root, - unsigned long index, unsigned int tag) -{ - struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; - struct radix_tree_node *slot = NULL; - unsigned int height, shift; - - height = root->height; - if (index > radix_tree_maxindex(height)) - goto out; - - shift = (height - 1) * RADIX_TREE_MAP_SHIFT; - pathp->node = NULL; - slot = root->rnode; - - while (height > 0) { - int offset; - - if (slot == NULL) - goto out; - - offset = (index >> shift) & RADIX_TREE_MAP_MASK; - pathp[1].offset = offset; - pathp[1].node = slot; - slot = slot->slots[offset]; - pathp++; - shift -= RADIX_TREE_MAP_SHIFT; - height--; - } - - if (slot == NULL) - goto out; - - while (pathp->node) { - if (!tag_get(pathp->node, tag, pathp->offset)) - goto out; - tag_clear(pathp->node, tag, pathp->offset); - if (any_tag_set(pathp->node, tag)) - goto out; - pathp--; - } - - /* clear the root's tag bit */ - if (root_tag_get(root, tag)) - root_tag_clear(root, tag); - -out: - return slot; -} - -#ifndef __KERNEL__ /* Only the test harness uses this at present */ -/** - * radix_tree_tag_get - get a tag on a radix tree node - * @root: radix tree root - * @index: index key - * @tag: tag index (< RADIX_TREE_MAX_TAGS) - * - * Return values: - * - * 0: tag not present or not set - * 1: tag set - */ -int radix_tree_tag_get(struct radix_tree_root *root, - unsigned long index, unsigned int tag) -{ - unsigned int height, shift; - struct radix_tree_node *slot; - int saw_unset_tag = 0; - - height = root->height; - if (index > radix_tree_maxindex(height)) - return 0; - - /* check the root's tag bit */ - if (!root_tag_get(root, tag)) - return 0; - - if (height == 0) - return 1; - - shift = (height - 1) * RADIX_TREE_MAP_SHIFT; - slot = root->rnode; - - for ( ; ; ) { - int offset; - - if (slot == NULL) - return 0; - - offset = (index >> shift) & RADIX_TREE_MAP_MASK; - - /* - * This is just a debug check. Later, we can bale as soon as - * we see an unset tag. - */ - if (!tag_get(slot, tag, offset)) - saw_unset_tag = 1; - if (height == 1) { - int ret = tag_get(slot, tag, offset); - - BUG_ON(ret && saw_unset_tag); - return !!ret; - } - slot = slot->slots[offset]; - shift -= RADIX_TREE_MAP_SHIFT; - height--; - } -} -#endif - -static unsigned int -__lookup(struct radix_tree_root *root, void **results, unsigned long index, - unsigned int max_items, unsigned long *next_index) -{ - unsigned int nr_found = 0; - unsigned int shift, height; - struct radix_tree_node *slot; - unsigned long i; - - height = root->height; - if (height == 0) { - if (root->rnode && index == 0) - results[nr_found++] = root->rnode; - goto out; - } - - shift = (height-1) * RADIX_TREE_MAP_SHIFT; - slot = root->rnode; - - for ( ; height > 1; height--) { - - for (i = (index >> shift) & RADIX_TREE_MAP_MASK ; - i < RADIX_TREE_MAP_SIZE; i++) { - if (slot->slots[i] != NULL) - break; - index &= ~((1UL << shift) - 1); - index += 1UL << shift; - if (index == 0) - goto out; /* 32-bit wraparound */ - } - if (i == RADIX_TREE_MAP_SIZE) - goto out; - - shift -= RADIX_TREE_MAP_SHIFT; - slot = slot->slots[i]; - } - - /* Bottom level: grab some items */ - for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) { - index++; - if (slot->slots[i]) { - results[nr_found++] = slot->slots[i]; - if (nr_found == max_items) - goto out; - } - } -out: - *next_index = index; - return nr_found; -} - -/** - * radix_tree_gang_lookup - perform multiple lookup on a radix tree - * @root: radix tree root - * @results: where the results of the lookup are placed - * @first_index: start the lookup from this key - * @max_items: place up to this many items at *results - * - * Performs an index-ascending scan of the tree for present items. Places - * them at *@results and returns the number of items which were placed at - * *@results. - * - * The implementation is naive. - */ -unsigned int -radix_tree_gang_lookup(struct radix_tree_root *root, void **results, - unsigned long first_index, unsigned int max_items) -{ - const unsigned long max_index = radix_tree_maxindex(root->height); - unsigned long cur_index = first_index; - unsigned int ret = 0; - - while (ret < max_items) { - unsigned int nr_found; - unsigned long next_index; /* Index of next search */ - - if (cur_index > max_index) - break; - nr_found = __lookup(root, results + ret, cur_index, - max_items - ret, &next_index); - ret += nr_found; - if (next_index == 0) - break; - cur_index = next_index; - } - return ret; -} - -/* - * FIXME: the two tag_get()s here should use find_next_bit() instead of - * open-coding the search. - */ -static unsigned int -__lookup_tag(struct radix_tree_root *root, void **results, unsigned long index, - unsigned int max_items, unsigned long *next_index, unsigned int tag) -{ - unsigned int nr_found = 0; - unsigned int shift; - unsigned int height = root->height; - struct radix_tree_node *slot; - - if (height == 0) { - if (root->rnode && index == 0) - results[nr_found++] = root->rnode; - goto out; - } - - shift = (height - 1) * RADIX_TREE_MAP_SHIFT; - slot = root->rnode; - - do { - unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK; - - for ( ; i < RADIX_TREE_MAP_SIZE; i++) { - if (tag_get(slot, tag, i)) { - BUG_ON(slot->slots[i] == NULL); - break; - } - index &= ~((1UL << shift) - 1); - index += 1UL << shift; - if (index == 0) - goto out; /* 32-bit wraparound */ - } - if (i == RADIX_TREE_MAP_SIZE) - goto out; - height--; - if (height == 0) { /* Bottom level: grab some items */ - unsigned long j = index & RADIX_TREE_MAP_MASK; - - for ( ; j < RADIX_TREE_MAP_SIZE; j++) { - index++; - if (tag_get(slot, tag, j)) { - BUG_ON(slot->slots[j] == NULL); - results[nr_found++] = slot->slots[j]; - if (nr_found == max_items) - goto out; - } - } - } - shift -= RADIX_TREE_MAP_SHIFT; - slot = slot->slots[i]; - } while (height > 0); -out: - *next_index = index; - return nr_found; -} - -/** - * radix_tree_gang_lookup_tag - perform multiple lookup on a radix tree - * based on a tag - * @root: radix tree root - * @results: where the results of the lookup are placed - * @first_index: start the lookup from this key - * @max_items: place up to this many items at *results - * @tag: the tag index (< RADIX_TREE_MAX_TAGS) - * - * Performs an index-ascending scan of the tree for present items which - * have the tag indexed by @tag set. Places the items at *@results and - * returns the number of items which were placed at *@results. - */ -unsigned int -radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, - unsigned long first_index, unsigned int max_items, - unsigned int tag) -{ - const unsigned long max_index = radix_tree_maxindex(root->height); - unsigned long cur_index = first_index; - unsigned int ret = 0; - - /* check the root's tag bit */ - if (!root_tag_get(root, tag)) - return 0; - - while (ret < max_items) { - unsigned int nr_found; - unsigned long next_index; /* Index of next search */ - - if (cur_index > max_index) - break; - nr_found = __lookup_tag(root, results + ret, cur_index, - max_items - ret, &next_index, tag); - ret += nr_found; - if (next_index == 0) - break; - cur_index = next_index; - } - return ret; -} - -/** - * radix_tree_shrink - shrink height of a radix tree to minimal - * @root radix tree root - */ -static inline void radix_tree_shrink(struct radix_tree_root *root) -{ - /* try to shrink tree height */ - while (root->height > 0 && - root->rnode->count == 1 && - root->rnode->slots[0]) { - struct radix_tree_node *to_free = root->rnode; - - root->rnode = to_free->slots[0]; - root->height--; - /* must only free zeroed nodes into the slab */ - tag_clear(to_free, 0, 0); - tag_clear(to_free, 1, 0); - to_free->slots[0] = NULL; - to_free->count = 0; - radix_tree_node_free(to_free); - } -} - -/** - * radix_tree_delete - delete an item from a radix tree - * @root: radix tree root - * @index: index key - * - * Remove the item at @index from the radix tree rooted at @root. - * - * Returns the address of the deleted item, or NULL if it was not present. - */ -void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) -{ - struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; - struct radix_tree_node *slot = NULL; - unsigned int height, shift; - int tag; - int offset; - - height = root->height; - if (index > radix_tree_maxindex(height)) - goto out; - - slot = root->rnode; - if (height == 0 && root->rnode) { - root_tag_clear_all(root); - root->rnode = NULL; - goto out; - } - - shift = (height - 1) * RADIX_TREE_MAP_SHIFT; - pathp->node = NULL; - - do { - if (slot == NULL) - goto out; - - pathp++; - offset = (index >> shift) & RADIX_TREE_MAP_MASK; - pathp->offset = offset; - pathp->node = slot; - slot = slot->slots[offset]; - shift -= RADIX_TREE_MAP_SHIFT; - height--; - } while (height > 0); - - if (slot == NULL) - goto out; - - /* - * Clear all tags associated with the just-deleted item - */ - for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { - if (tag_get(pathp->node, tag, pathp->offset)) - radix_tree_tag_clear(root, index, tag); - } - - /* Now free the nodes we do not need anymore */ - while (pathp->node) { - pathp->node->slots[pathp->offset] = NULL; - pathp->node->count--; - - if (pathp->node->count) { - if (pathp->node == root->rnode) - radix_tree_shrink(root); - goto out; - } - - /* Node with zero slots in use so free it */ - radix_tree_node_free(pathp->node); - - pathp--; - } - root_tag_clear_all(root); - root->height = 0; - root->rnode = NULL; - -out: - return slot; -} - -/** - * radix_tree_tagged - test whether any items in the tree are tagged - * @root: radix tree root - * @tag: tag to test - */ -int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) -{ - return root_tag_get(root, tag); -} - -static unsigned long __maxindex(unsigned int height) -{ - unsigned int tmp = height * RADIX_TREE_MAP_SHIFT; - unsigned long index = (~0UL >> (RADIX_TREE_INDEX_BITS - tmp - 1)) >> 1; - - if (tmp >= RADIX_TREE_INDEX_BITS) - index = ~0UL; - return index; -} - -static void radix_tree_init_maxindex(void) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(height_to_maxindex); i++) - height_to_maxindex[i] = __maxindex(i); -} - -void radix_tree_init(void) -{ - radix_tree_init_maxindex(); -} diff --git a/radix-tree.h b/radix-tree.h deleted file mode 100644 index d99ea7e..0000000 --- a/radix-tree.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -/* - * Copyright (C) 2001 Momchil Velikov - * Portions Copyright (C) 2001 Christoph Hellwig - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2, or (at - * your option) any later version. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#ifndef _LINUX_RADIX_TREE_H -#define _LINUX_RADIX_TREE_H - -#include "kerncompat.h" - -#define RADIX_TREE_MAX_TAGS 2 - -/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */ -struct radix_tree_root { - unsigned int height; - gfp_t gfp_mask; - struct radix_tree_node *rnode; -}; - -#define RADIX_TREE_INIT(mask) { \ - .height = 0, \ - .gfp_mask = (mask), \ - .rnode = NULL, \ -} - -#define RADIX_TREE(name, mask) \ - struct radix_tree_root name = RADIX_TREE_INIT(mask) - -#define INIT_RADIX_TREE(root, mask) \ -do { \ - (root)->height = 0; \ - (root)->gfp_mask = (mask); \ - (root)->rnode = NULL; \ -} while (0) - -int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); -void *radix_tree_lookup(struct radix_tree_root *, unsigned long); -void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); -void *radix_tree_delete(struct radix_tree_root *, unsigned long); -unsigned int -radix_tree_gang_lookup(struct radix_tree_root *root, void **results, - unsigned long first_index, unsigned int max_items); -int radix_tree_preload(gfp_t gfp_mask); -void radix_tree_init(void); -void *radix_tree_tag_set(struct radix_tree_root *root, - unsigned long index, unsigned int tag); -void *radix_tree_tag_clear(struct radix_tree_root *root, - unsigned long index, unsigned int tag); -int radix_tree_tag_get(struct radix_tree_root *root, - unsigned long index, unsigned int tag); -unsigned int -radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, - unsigned long first_index, unsigned int max_items, - unsigned int tag); -int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); - -static inline void radix_tree_preload_end(void) -{ - preempt_enable(); -} - -#endif /* _LINUX_RADIX_TREE_H */ diff --git a/random-test.c b/random-test.c deleted file mode 100644 index 571735d..0000000 --- a/random-test.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 -#include -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "print-tree.h" -#include "transaction.h" - -int keep_running = 1; -struct btrfs_super_block super; - -static int setup_key(struct radix_tree_root *root, struct btrfs_key *key, - int exists) -{ - int num = rand(); - unsigned long res[2]; - int ret; - - key->flags = 0; - btrfs_set_key_type(key, BTRFS_STRING_ITEM_KEY); - key->offset = 0; -again: - ret = radix_tree_gang_lookup(root, (void **)res, num, 2); - if (exists) { - if (ret == 0) - return -1; - num = res[0]; - } else if (ret != 0 && num == res[0]) { - num++; - if (ret > 1 && num == res[1]) { - num++; - goto again; - } - } - key->objectid = num; - return 0; -} - -static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct radix_tree_root *radix) -{ - struct btrfs_path path; - struct btrfs_key key; - int ret; - char buf[128]; - unsigned long oid; - btrfs_init_path(&path); - ret = setup_key(radix, &key, 0); - sprintf(buf, "str-%llu\n", (unsigned long long)key.objectid); - ret = btrfs_insert_item(trans, root, &key, buf, strlen(buf)); - if (ret) - goto error; - oid = (unsigned long)key.objectid; - radix_tree_preload(GFP_KERNEL); - ret = radix_tree_insert(radix, oid, (void *)oid); - radix_tree_preload_end(); - if (ret) - goto error; - return ret; -error: - printf("failed to insert %llu\n", (unsigned long long)key.objectid); - return -1; -} - -static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct radix_tree_root *radix) -{ - struct btrfs_path path; - struct btrfs_key key; - int ret; - char buf[128]; - btrfs_init_path(&path); - ret = setup_key(radix, &key, 1); - if (ret < 0) - return 0; - sprintf(buf, "str-%llu\n", (unsigned long long)key.objectid); - ret = btrfs_insert_item(trans, root, &key, buf, strlen(buf)); - if (ret != -EEXIST) { - printf("insert on %llu gave us %d\n", - (unsigned long long)key.objectid, ret); - return 1; - } - return 0; -} - -static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct radix_tree_root *radix) -{ - struct btrfs_path path; - struct btrfs_key key; - int ret; - unsigned long *ptr; - btrfs_init_path(&path); - ret = setup_key(radix, &key, 1); - if (ret < 0) - return 0; - ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); - if (ret) - goto error; - ret = btrfs_del_item(trans, root, &path); - btrfs_release_path(root, &path); - if (ret != 0) - goto error; - ptr = radix_tree_delete(radix, key.objectid); - if (!ptr) - goto error; - return 0; -error: - printf("failed to delete %llu\n", (unsigned long long)key.objectid); - return -1; -} - -static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct radix_tree_root *radix) -{ - struct btrfs_path path; - struct btrfs_key key; - int ret; - btrfs_init_path(&path); - ret = setup_key(radix, &key, 1); - if (ret < 0) - return 0; - ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); - btrfs_release_path(root, &path); - if (ret) - goto error; - return 0; -error: - printf("unable to find key %llu\n", (unsigned long long)key.objectid); - return -1; -} - -static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct radix_tree_root *radix) -{ - struct btrfs_path path; - struct btrfs_key key; - int ret; - btrfs_init_path(&path); - ret = setup_key(radix, &key, 0); - if (ret < 0) - return ret; - ret = btrfs_search_slot(trans, root, &key, &path, 0, 0); - btrfs_release_path(root, &path); - if (ret <= 0) - goto error; - return 0; -error: - printf("able to find key that should not exist %llu\n", - (unsigned long long)key.objectid); - return -1; -} - -static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct radix_tree_root *radix, int nr) -{ - struct btrfs_path path; - struct btrfs_key key; - unsigned long found = 0; - int ret; - int slot; - int *ptr; - int count = 0; - - key.offset = 0; - key.flags = 0; - btrfs_set_key_type(&key, BTRFS_STRING_ITEM_KEY); - key.objectid = (unsigned long)-1; - while(nr-- >= 0) { - btrfs_init_path(&path); - ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); - if (ret < 0) { - btrfs_release_path(root, &path); - return ret; - } - if (ret != 0) { - if (path.slots[0] == 0) { - btrfs_release_path(root, &path); - break; - } - path.slots[0] -= 1; - } - slot = path.slots[0]; - found = btrfs_disk_key_objectid( - &path.nodes[0]->leaf.items[slot].key); - ret = btrfs_del_item(trans, root, &path); - count++; - if (ret) { - fprintf(stderr, - "failed to remove %lu from tree\n", - found); - return -1; - } - btrfs_release_path(root, &path); - ptr = radix_tree_delete(radix, found); - if (!ptr) - goto error; - if (!keep_running) - break; - } - return 0; -error: - fprintf(stderr, "failed to delete from the radix %lu\n", found); - return -1; -} - -static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct radix_tree_root *radix, int count) -{ - int i; - int ret = 0; - for (i = 0; i < count; i++) { - ret = ins_one(trans, root, radix); - if (ret) { - fprintf(stderr, "fill failed\n"); - goto out; - } - if (i % 1000 == 0) { - ret = btrfs_commit_transaction(trans, root, &super); - if (ret) { - fprintf(stderr, "fill commit failed\n"); - return ret; - } - } - if (i && i % 10000 == 0) { - printf("bigfill %d\n", i); - } - if (!keep_running) - break; - } -out: - return ret; -} - -static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct radix_tree_root *radix) -{ - int ret; - int nr = rand() % 5000; - static int run_nr = 0; - - /* do the bulk op much less frequently */ - if (run_nr++ % 100) - return 0; - ret = empty_tree(trans, root, radix, nr); - if (ret) - return ret; - ret = fill_tree(trans, root, radix, nr); - if (ret) - return ret; - return 0; -} - - -int (*ops[])(struct btrfs_trans_handle *, - struct btrfs_root *root, struct radix_tree_root *radix) = - { ins_one, insert_dup, del_one, lookup_item, - lookup_enoent, bulk_op }; - -static int fill_radix(struct btrfs_root *root, struct radix_tree_root *radix) -{ - struct btrfs_path path; - struct btrfs_key key; - unsigned long found = 0; - int ret; - int slot; - int i; - - key.offset = 0; - key.flags = 0; - btrfs_set_key_type(&key, BTRFS_STRING_ITEM_KEY); - key.objectid = (unsigned long)-1; - while(1) { - btrfs_init_path(&path); - ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); - if (ret < 0) { - btrfs_release_path(root, &path); - return ret; - } - slot = path.slots[0]; - if (ret != 0) { - if (slot == 0) { - btrfs_release_path(root, &path); - break; - } - slot -= 1; - } - for (i = slot; i >= 0; i--) { - found = btrfs_disk_key_objectid(&path.nodes[0]-> - leaf.items[i].key); - radix_tree_preload(GFP_KERNEL); - ret = radix_tree_insert(radix, found, (void *)found); - if (ret) { - fprintf(stderr, - "failed to insert %lu into radix\n", - found); - exit(1); - } - - radix_tree_preload_end(); - } - btrfs_release_path(root, &path); - key.objectid = found - 1; - if (key.objectid > found) - break; - } - return 0; -} -void sigstopper(int ignored) -{ - keep_running = 0; - fprintf(stderr, "caught exit signal, stopping\n"); -} - -int print_usage(void) -{ - printf("usage: tester [-ih] [-c count] [-f count]\n"); - printf("\t -c count -- iteration count after filling\n"); - printf("\t -f count -- run this many random inserts before starting\n"); - printf("\t -i -- only do initial fill\n"); - printf("\t -h -- this help text\n"); - exit(1); -} -int main(int ac, char **av) -{ - RADIX_TREE(radix, GFP_KERNEL); - struct btrfs_root *root; - int i; - int ret; - int count; - int op; - int iterations = 20000; - int init_fill_count = 800000; - int err = 0; - int initial_only = 0; - struct btrfs_trans_handle *trans; - radix_tree_init(); - root = open_ctree("dbfile", &super); - fill_radix(root, &radix); - - signal(SIGTERM, sigstopper); - signal(SIGINT, sigstopper); - - for (i = 1 ; i < ac ; i++) { - if (strcmp(av[i], "-i") == 0) { - initial_only = 1; - } else if (strcmp(av[i], "-c") == 0) { - iterations = atoi(av[i+1]); - i++; - } else if (strcmp(av[i], "-f") == 0) { - init_fill_count = atoi(av[i+1]); - i++; - } else { - print_usage(); - } - } - printf("initial fill\n"); - trans = btrfs_start_transaction(root, 1); - ret = fill_tree(trans, root, &radix, init_fill_count); - printf("starting run\n"); - if (ret) { - err = ret; - goto out; - } - if (initial_only == 1) { - goto out; - } - for (i = 0; i < iterations; i++) { - op = rand() % ARRAY_SIZE(ops); - count = rand() % 128; - if (i % 2000 == 0) { - printf("%d\n", i); - fflush(stdout); - } - if (i && i % 5000 == 0) { - printf("open & close, root level %d nritems %d\n", - btrfs_header_level(&root->node->node.header), - btrfs_header_nritems(&root->node->node.header)); - close_ctree(root, &super); - root = open_ctree("dbfile", &super); - } - while(count--) { - ret = ops[op](trans, root, &radix); - if (ret) { - fprintf(stderr, "op %d failed %d:%d\n", - op, i, iterations); - btrfs_print_tree(root, root->node); - fprintf(stderr, "op %d failed %d:%d\n", - op, i, iterations); - err = ret; - goto out; - } - if (ops[op] == bulk_op) - break; - if (keep_running == 0) { - err = 0; - goto out; - } - } - } -out: - close_ctree(root, &super); - return err; -} - diff --git a/rbtree.c b/rbtree.c deleted file mode 100644 index 6ad800f..0000000 --- a/rbtree.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - Red Black Trees - (C) 1999 Andrea Arcangeli - (C) 2002 David Woodhouse - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - 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 02111-1307 USA - - linux/lib/rbtree.c -*/ - -#include "rbtree.h" - -static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *right = node->rb_right; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_right = right->rb_left)) - rb_set_parent(right->rb_left, node); - right->rb_left = node; - - rb_set_parent(right, parent); - - if (parent) - { - if (node == parent->rb_left) - parent->rb_left = right; - else - parent->rb_right = right; - } - else - root->rb_node = right; - rb_set_parent(node, right); -} - -static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *left = node->rb_left; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_left = left->rb_right)) - rb_set_parent(left->rb_right, node); - left->rb_right = node; - - rb_set_parent(left, parent); - - if (parent) - { - if (node == parent->rb_right) - parent->rb_right = left; - else - parent->rb_left = left; - } - else - root->rb_node = left; - rb_set_parent(node, left); -} - -void rb_insert_color(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *parent, *gparent; - - while ((parent = rb_parent(node)) && rb_is_red(parent)) - { - gparent = rb_parent(parent); - - if (parent == gparent->rb_left) - { - { - register struct rb_node *uncle = gparent->rb_right; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } - } - - if (parent->rb_right == node) - { - register struct rb_node *tmp; - __rb_rotate_left(parent, root); - tmp = parent; - parent = node; - node = tmp; - } - - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_right(gparent, root); - } else { - { - register struct rb_node *uncle = gparent->rb_left; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } - } - - if (parent->rb_left == node) - { - register struct rb_node *tmp; - __rb_rotate_right(parent, root); - tmp = parent; - parent = node; - node = tmp; - } - - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_left(gparent, root); - } - } - - rb_set_black(root->rb_node); -} - -static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, - struct rb_root *root) -{ - struct rb_node *other; - - while ((!node || rb_is_black(node)) && node != root->rb_node) - { - if (parent->rb_left == node) - { - other = parent->rb_right; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_left(parent, root); - other = parent->rb_right; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); - } - else - { - if (!other->rb_right || rb_is_black(other->rb_right)) - { - struct rb_node *o_left; - if ((o_left = other->rb_left)) - rb_set_black(o_left); - rb_set_red(other); - __rb_rotate_right(other, root); - other = parent->rb_right; - } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_right) - rb_set_black(other->rb_right); - __rb_rotate_left(parent, root); - node = root->rb_node; - break; - } - } - else - { - other = parent->rb_left; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_right(parent, root); - other = parent->rb_left; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); - } - else - { - if (!other->rb_left || rb_is_black(other->rb_left)) - { - register struct rb_node *o_right; - if ((o_right = other->rb_right)) - rb_set_black(o_right); - rb_set_red(other); - __rb_rotate_left(other, root); - other = parent->rb_left; - } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_left) - rb_set_black(other->rb_left); - __rb_rotate_right(parent, root); - node = root->rb_node; - break; - } - } - } - if (node) - rb_set_black(node); -} - -void rb_erase(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *child, *parent; - int color; - - if (!node->rb_left) - child = node->rb_right; - else if (!node->rb_right) - child = node->rb_left; - else - { - struct rb_node *old = node, *left; - - node = node->rb_right; - while ((left = node->rb_left) != NULL) - node = left; - child = node->rb_right; - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent == old) { - parent->rb_right = child; - parent = node; - } else - parent->rb_left = child; - - node->rb_parent_color = old->rb_parent_color; - node->rb_right = old->rb_right; - node->rb_left = old->rb_left; - - if (rb_parent(old)) - { - if (rb_parent(old)->rb_left == old) - rb_parent(old)->rb_left = node; - else - rb_parent(old)->rb_right = node; - } else - root->rb_node = node; - - rb_set_parent(old->rb_left, node); - if (old->rb_right) - rb_set_parent(old->rb_right, node); - goto color; - } - - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent) - { - if (parent->rb_left == node) - parent->rb_left = child; - else - parent->rb_right = child; - } - else - root->rb_node = child; - - color: - if (color == RB_BLACK) - __rb_erase_color(child, parent, root); -} - -/* - * This function returns the first node (in sort order) of the tree. - */ -struct rb_node *rb_first(struct rb_root *root) -{ - struct rb_node *n; - - n = root->rb_node; - if (!n) - return NULL; - while (n->rb_left) - n = n->rb_left; - return n; -} - -struct rb_node *rb_last(struct rb_root *root) -{ - struct rb_node *n; - - n = root->rb_node; - if (!n) - return NULL; - while (n->rb_right) - n = n->rb_right; - return n; -} - -struct rb_node *rb_next(struct rb_node *node) -{ - struct rb_node *parent; - - if (rb_parent(node) == node) - return NULL; - - /* If we have a right-hand child, go down and then left as far - as we can. */ - if (node->rb_right) { - node = node->rb_right; - while (node->rb_left) - node=node->rb_left; - return node; - } - - /* No right-hand children. Everything down and left is - smaller than us, so any 'next' node must be in the general - direction of our parent. Go up the tree; any time the - ancestor is a right-hand child of its parent, keep going - up. First time it's a left-hand child of its parent, said - parent is our 'next' node. */ - while ((parent = rb_parent(node)) && node == parent->rb_right) - node = parent; - - return parent; -} - -struct rb_node *rb_prev(struct rb_node *node) -{ - struct rb_node *parent; - - if (rb_parent(node) == node) - return NULL; - - /* If we have a left-hand child, go down and then right as far - as we can. */ - if (node->rb_left) { - node = node->rb_left; - while (node->rb_right) - node=node->rb_right; - return node; - } - - /* No left-hand children. Go up till we find an ancestor which - is a right-hand child of its parent */ - while ((parent = rb_parent(node)) && node == parent->rb_left) - node = parent; - - return parent; -} - -void rb_replace_node(struct rb_node *victim, struct rb_node *new, - struct rb_root *root) -{ - struct rb_node *parent = rb_parent(victim); - - /* Set the surrounding nodes to point to the replacement */ - if (parent) { - if (victim == parent->rb_left) - parent->rb_left = new; - else - parent->rb_right = new; - } else { - root->rb_node = new; - } - if (victim->rb_left) - rb_set_parent(victim->rb_left, new); - if (victim->rb_right) - rb_set_parent(victim->rb_right, new); - - /* Copy the pointers/colour from the victim to the replacement */ - *new = *victim; -} diff --git a/rbtree.h b/rbtree.h deleted file mode 100644 index bed054d..0000000 --- a/rbtree.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - Red Black Trees - (C) 1999 Andrea Arcangeli - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - 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 02111-1307 USA - - linux/include/linux/rbtree.h - - To use rbtrees you'll have to implement your own insert and search cores. - This will avoid us to use callbacks and to drop drammatically performances. - I know it's not the cleaner way, but in C (not in C++) to get - performances and genericity... - - Some example of insert and search follows here. The search is a plain - normal search over an ordered tree. The insert instead must be implemented - int two steps: as first thing the code must insert the element in - order as a red leaf in the tree, then the support library function - rb_insert_color() must be called. Such function will do the - not trivial work to rebalance the rbtree if necessary. - ------------------------------------------------------------------------ -static inline struct page * rb_search_page_cache(struct inode * inode, - unsigned long offset) -{ - struct rb_node * n = inode->i_rb_page_cache.rb_node; - struct page * page; - - while (n) - { - page = rb_entry(n, struct page, rb_page_cache); - - if (offset < page->offset) - n = n->rb_left; - else if (offset > page->offset) - n = n->rb_right; - else - return page; - } - return NULL; -} - -static inline struct page * __rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct rb_node ** p = &inode->i_rb_page_cache.rb_node; - struct rb_node * parent = NULL; - struct page * page; - - while (*p) - { - parent = *p; - page = rb_entry(parent, struct page, rb_page_cache); - - if (offset < page->offset) - p = &(*p)->rb_left; - else if (offset > page->offset) - p = &(*p)->rb_right; - else - return page; - } - - rb_link_node(node, parent, p); - - return NULL; -} - -static inline struct page * rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct page * ret; - if ((ret = __rb_insert_page_cache(inode, offset, node))) - goto out; - rb_insert_color(node, &inode->i_rb_page_cache); - out: - return ret; -} ------------------------------------------------------------------------ -*/ - -#ifndef _LINUX_RBTREE_H -#define _LINUX_RBTREE_H -#include "kerncompat.h" -struct rb_node -{ - unsigned long rb_parent_color; -#define RB_RED 0 -#define RB_BLACK 1 - struct rb_node *rb_right; - struct rb_node *rb_left; -} __attribute__((aligned(sizeof(long)))); - /* The alignment might seem pointless, but allegedly CRIS needs it */ - -struct rb_root -{ - struct rb_node *rb_node; - void (*rotate_notify)(struct rb_node *old_parent, struct rb_node *node); - -}; - - -#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) -#define rb_color(r) ((r)->rb_parent_color & 1) -#define rb_is_red(r) (!rb_color(r)) -#define rb_is_black(r) rb_color(r) -#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) -#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) - -static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) -{ - rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; -} -static inline void rb_set_color(struct rb_node *rb, int color) -{ - rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; -} - -#define RB_ROOT (struct rb_root) { NULL, } -#define rb_entry(ptr, type, member) container_of(ptr, type, member) - -#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) -#define RB_EMPTY_NODE(node) (rb_parent(node) == node) -#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) - -extern void rb_insert_color(struct rb_node *, struct rb_root *); -extern void rb_erase(struct rb_node *, struct rb_root *); - -/* Find logical next and previous nodes in a tree */ -extern struct rb_node *rb_next(struct rb_node *); -extern struct rb_node *rb_prev(struct rb_node *); -extern struct rb_node *rb_first(struct rb_root *); -extern struct rb_node *rb_last(struct rb_root *); - -/* Fast replacement of a single node without remove/rebalance/add/rebalance */ -extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, - struct rb_root *root); - -static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, - struct rb_node ** rb_link) -{ - node->rb_parent_color = (unsigned long )parent; - node->rb_left = node->rb_right = NULL; - - *rb_link = node; -} - -#endif /* _LINUX_RBTREE_H */ diff --git a/root-tree.c b/root-tree.c deleted file mode 100644 index 782472c..0000000 --- a/root-tree.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 "ctree.h" -#include "transaction.h" -#include "disk-io.h" -#include "print-tree.h" - -int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, - struct btrfs_root_item *item, struct btrfs_key *key) -{ - struct btrfs_path *path; - struct btrfs_key search_key; - struct btrfs_key found_key; - struct extent_buffer *l; - int ret; - int slot; - - search_key.objectid = objectid; - search_key.type = BTRFS_ROOT_ITEM_KEY; - search_key.offset = (u64)-1; - - path = btrfs_alloc_path(); - BUG_ON(!path); - ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); - if (ret < 0) - goto out; - - BUG_ON(ret == 0); - l = path->nodes[0]; - BUG_ON(path->slots[0] == 0); - slot = path->slots[0] - 1; - btrfs_item_key_to_cpu(l, &found_key, slot); - if (found_key.objectid != objectid) { - ret = 1; - goto out; - } - read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot), - sizeof(*item)); - memcpy(key, &found_key, sizeof(found_key)); - ret = 0; -out: - btrfs_release_path(root, path); - btrfs_free_path(path); - return ret; -} - -int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *key, struct btrfs_root_item - *item) -{ - struct btrfs_path *path; - struct extent_buffer *l; - int ret; - int slot; - unsigned long ptr; - - path = btrfs_alloc_path(); - BUG_ON(!path); - ret = btrfs_search_slot(trans, root, key, path, 0, 1); - if (ret < 0) - goto out; - BUG_ON(ret != 0); - l = path->nodes[0]; - slot = path->slots[0]; - ptr = btrfs_item_ptr_offset(l, slot); - write_extent_buffer(l, item, ptr, sizeof(*item)); - btrfs_mark_buffer_dirty(path->nodes[0]); -out: - btrfs_release_path(root, path); - btrfs_free_path(path); - return ret; -} - -int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_key *key, struct btrfs_root_item - *item) -{ - int ret; - ret = btrfs_insert_item(trans, root, key, item, sizeof(*item)); - return ret; -} - -#if 0 -int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid, - struct btrfs_root *latest) -{ - struct btrfs_root *dead_root; - struct btrfs_item *item; - struct btrfs_root_item *ri; - struct btrfs_key key; - struct btrfs_path *path; - int ret; - u32 nritems; - struct extent_buffer *leaf; - int slot; - - key.objectid = objectid; - btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); - key.offset = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) - goto err; - while(1) { - leaf = path->nodes[0]; - nritems = btrfs_header_nritems(leaf); - slot = path->slots[0]; - if (slot >= nritems) { - ret = btrfs_next_leaf(root, path); - if (ret) - break; - leaf = path->nodes[0]; - nritems = btrfs_header_nritems(leaf); - slot = path->slots[0]; - } - item = btrfs_item_nr(leaf, slot); - btrfs_item_key_to_cpu(leaf, &key, slot); - if (btrfs_key_type(&key) != BTRFS_ROOT_ITEM_KEY) - goto next; - - if (key.objectid < objectid) - goto next; - - if (key.objectid > objectid) - break; - - ri = btrfs_item_ptr(leaf, slot, struct btrfs_root_item); - if (btrfs_disk_root_refs(leaf, ri) != 0) - goto next; - - dead_root = btrfs_read_fs_root_no_radix(root->fs_info, &key); - if (IS_ERR(dead_root)) { - ret = PTR_ERR(dead_root); - goto err; - } - - ret = btrfs_add_dead_root(dead_root, latest, - &root->fs_info->dead_roots); - if (ret) - goto err; -next: - slot++; - path->slots[0]++; - } - ret = 0; -err: - btrfs_free_path(path); - return ret; -} -#endif - -int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct btrfs_key *key) -{ - struct btrfs_path *path; - int ret; - u32 refs; - struct btrfs_root_item *ri; - struct extent_buffer *leaf; - - path = btrfs_alloc_path(); - BUG_ON(!path); - ret = btrfs_search_slot(trans, root, key, path, -1, 1); - if (ret < 0) - goto out; - if (ret) { -btrfs_print_leaf(root, path->nodes[0]); -printk("failed to del %llu %u %llu\n", - (unsigned long long)key->objectid, - key->type, - (unsigned long long)key->offset); - - } - BUG_ON(ret != 0); - leaf = path->nodes[0]; - ri = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_item); - - refs = btrfs_disk_root_refs(leaf, ri); - BUG_ON(refs != 0); - ret = btrfs_del_item(trans, root, path); -out: - btrfs_release_path(root, path); - btrfs_free_path(path); - return ret; -} - -/* - * add a btrfs_root_ref item. type is either BTRFS_ROOT_REF_KEY - * or BTRFS_ROOT_BACKREF_KEY. - * - * The dirid, sequence, name and name_len refer to the directory entry - * that is referencing the root. - * - * For a forward ref, the root_id is the id of the tree referencing - * the root and ref_id is the id of the subvol or snapshot. - * - * For a back ref the root_id is the id of the subvol or snapshot and - * ref_id is the id of the tree referencing it. - */ -int btrfs_add_root_ref(struct btrfs_trans_handle *trans, - struct btrfs_root *tree_root, - u64 root_id, u8 type, u64 ref_id, - u64 dirid, u64 sequence, - const char *name, int name_len) -{ - struct btrfs_key key; - int ret; - struct btrfs_path *path; - struct btrfs_root_ref *ref; - struct extent_buffer *leaf; - unsigned long ptr; - - - path = btrfs_alloc_path(); - - key.objectid = root_id; - key.type = type; - key.offset = ref_id; - - ret = btrfs_insert_empty_item(trans, tree_root, path, &key, - sizeof(*ref) + name_len); - BUG_ON(ret); - - leaf = path->nodes[0]; - ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref); - btrfs_set_root_ref_dirid(leaf, ref, dirid); - btrfs_set_root_ref_sequence(leaf, ref, sequence); - btrfs_set_root_ref_name_len(leaf, ref, name_len); - ptr = (unsigned long)(ref + 1); - write_extent_buffer(leaf, name, ptr, name_len); - btrfs_mark_buffer_dirty(leaf); - - btrfs_free_path(path); - return ret; -} diff --git a/show-blocks b/show-blocks deleted file mode 100755 index 0164be9..0000000 --- a/show-blocks +++ /dev/null @@ -1,325 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2007 Oracle. 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. -# -import sys, os, signal, time, commands, tempfile, random - -# numpy seems to override random() with something else. Instantiate our -# own here -randgen = random.Random() -randgen.seed(50) - -from optparse import OptionParser -from matplotlib import rcParams -from matplotlib.font_manager import fontManager, FontProperties -import numpy - -rcParams['numerix'] = 'numpy' -rcParams['backend'] = 'Agg' -rcParams['interactive'] = 'False' -from pylab import * - -class AnnoteFinder: - """ - callback for matplotlib to display an annotation when points are clicked on. The - point which is closest to the click and within xtol and ytol is identified. - - Register this function like this: - - scatter(xdata, ydata) - af = AnnoteFinder(xdata, ydata, annotes) - connect('button_press_event', af) - """ - - def __init__(self, axis=None): - if axis is None: - self.axis = gca() - else: - self.axis= axis - self.drawnAnnotations = {} - self.links = [] - - def clear(self): - for k in self.drawnAnnotations.keys(): - self.drawnAnnotations[k].set_visible(False) - - def __call__(self, event): - if event.inaxes: - if event.button != 1: - self.clear() - draw() - return - clickX = event.xdata - clickY = event.ydata - if (self.axis is None) or (self.axis==event.inaxes): - self.drawAnnote(event.inaxes, clickX, clickY) - - def drawAnnote(self, axis, x, y): - """ - Draw the annotation on the plot - """ - if self.drawnAnnotations.has_key((x,y)): - markers = self.drawnAnnotations[(x,y)] - markers.set_visible(not markers.get_visible()) - draw() - else: - t = axis.text(x,y, "(%3.2f, %3.2f)"%(x,y), bbox=dict(facecolor='red', - alpha=0.8)) - self.drawnAnnotations[(x,y)] = t - draw() - -def loaddata(fh,delimiter=None, converters=None): - - #14413824 8192 extent back ref root 5 gen 10 owner 282 num_refs 1 - def iter(fh, delimiter, converters): - global total_data - global total_metadata - for i,line in enumerate(fh): - line = line.split(' ') - start = float(line[0]) - len = float(line[1]) - owner = float(line[10]) - root = float(line[6]) - if owner <= 255: - total_metadata += int(len) - else: - total_data += int(len) - if start < zoommin or (zoommax != 0 and start > zoommax): - continue - yield start - yield len - yield owner - yield root - X = numpy.fromiter(iter(fh, delimiter, converters), dtype=float) - return X - -def run_debug_tree(device): - p = os.popen('btrfs-debug-tree -e ' + device) - data = loaddata(p) - return data - -def shapeit(X): - lines = len(X) / 4 - X.shape = (lines, 4) - -def line_picker(line, mouseevent): - if mouseevent.xdata is None: return False, dict() - print "%d %d\n", mouseevent.xdata, mouseevent.ydata - return False, dict() - -def xycalc(byte): - byte = byte / bytes_per_cell - yval = floor(byte / num_cells) - xval = byte % num_cells - return (xval, yval + 1) - -# record the color used for each root the first time we find it -root_colors = {} -# there are lots of good colormaps to choose from -# http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps -# -meta_cmap = get_cmap("gist_ncar") -data_done = False - -def plotone(a, xvals, yvals, owner, root, lines, labels): - global data_done - add_label = False - - if owner: - if options.meta_only: - return - color = "blue" - label = "Data" - if not data_done: - add_label = True - data_done = True - else: - if options.data_only: - return - if root not in root_colors: - color = meta_cmap(randgen.random()) - label = "Meta %d" % int(root) - root_colors[root] = (color, label) - add_label = True - else: - color, label = root_colors[root] - - plotlines = a.plot(xvals, yvals, 's', color=color, mfc=color, mec=color, - markersize=.23, label=label) - if add_label: - lines += plotlines - labels.append(label) - print "add label %s" % label - -def parse_zoom(): - def parse_num(s): - mult = 1 - c = s.lower()[-1] - if c == 't': - mult = 1024 * 1024 * 1024 * 1024 - elif c == 'g': - mult = 1024 * 1024 * 1024 - elif c == 'm': - mult = 1024 * 1024 - elif c == 'k': - mult = 1024 - else: - c = None - if c: - num = int(s[:-1]) * mult - else: - num = int(s) - return num - - if not options.zoom: - return (0, 0) - - vals = options.zoom.split(':') - if len(vals) != 2: - sys.stderr.write("warning: unable to parse zoom %s\n" % options.zoom) - return (0, 0) - zoommin = parse_num(vals[0]) - zoommax = parse_num(vals[1]) - return (zoommin, zoommax) - -usage = "usage: %prog [options]" -parser = OptionParser(usage=usage) -parser.add_option("-d", "--device", help="Btrfs device", default="") -parser.add_option("-i", "--input-file", help="debug-tree data", default="") -parser.add_option("-o", "--output", help="Output file", default="blocks.png") -parser.add_option("-z", "--zoom", help="Zoom", default=None) -parser.add_option("", "--data-only", help="Only print data blocks", - default=False, action="store_true") -parser.add_option("", "--meta-only", help="Only print metadata blocks", - default=False, action="store_true") - -(options,args) = parser.parse_args() - -if not options.device and not options.input_file: - parser.print_help() - sys.exit(1) - -zoommin, zoommax = parse_zoom() -total_data = 0 -total_metadata = 0 - -if options.device: - data = run_debug_tree(options.device) -elif options.input_file: - data = loaddata(file(options.input_file)) -shapeit(data) - -# try to drop out the least common data points by creating -# a historgram of the sectors seen. -sectors = data[:,0] -sizes = data[:,1] -datalen = len(data) -sectormax = numpy.max(sectors) -sectormin = 0 -num_cells = 800 -total_cells = num_cells * num_cells -byte_range = sectormax - sectormin -bytes_per_cell = byte_range / total_cells - -f = figure(figsize=(8,6)) - -# Throughput goes at the botoom -a = subplot(1, 1, 1) -subplots_adjust(right=0.7) -datai = 0 -xvals = [] -yvals = [] -last_owner = 0 -last_root = 0 -lines = [] -labels = [] -while datai < datalen: - row = data[datai] - datai += 1 - byte = row[0] - size = row[1] - owner = row[2] - root = row[3] - - if owner <= 255: - owner = 0 - else: - owner = 1 - - if len(xvals) and (owner != last_owner or last_root != root): - plotone(a, xvals, yvals, last_owner, last_root, lines, labels) - xvals = [] - yvals = [] - cell = 0 - while cell < size: - xy = xycalc(byte) - byte += bytes_per_cell - cell += bytes_per_cell - if xy: - xvals.append(xy[0]) - yvals.append(xy[1]) - last_owner = owner - last_root = root - -if xvals: - plotone(a, xvals, yvals, last_owner, last_root, lines, labels) - -# make sure the final second goes on the x axes -ticks = [] -a.set_xticks(ticks) -ticks = a.get_yticks() - -first_tick = ticks[1] * bytes_per_cell * num_cells -if first_tick > 1024 * 1024 * 1024 * 1024: - scale = 1024 * 1024 * 1024 * 1024; - scalestr = "TB" -elif first_tick > 1024 * 1024 * 1024: - scale = 1024 * 1024 * 1024; - scalestr = "GB" -elif first_tick > 1024 * 1024: - scale = 1024 * 1024; - scalestr = "MB" -elif first_tick > 1024: - scale = 1024; - scalestr = "KB" -else: - scalestr = "Bytes" - scale = 1 - -ylabels = [ str(int((x * bytes_per_cell * num_cells) / scale)) for x in ticks ] -a.set_yticklabels(ylabels) -a.set_ylabel('Disk offset (%s)' % scalestr) -a.set_xlim(0, num_cells) -a.set_title('Blocks') - -a.legend(lines, labels, loc=(1.05, 0.8), shadow=True, pad=0.1, numpoints=1, - handletextsep = 0.005, - labelsep = 0.01, - markerscale=10, - prop=FontProperties(size='x-small') ) - -if total_data == 0: - percent_meta = 100 -else: - percent_meta = (float(total_metadata) / float(total_data)) * 100 - -print "Total metadata bytes %d data %d ratio %.3f" % (total_metadata, - total_data, percent_meta) -print "saving graph to %s" % options.output -savefig(options.output, orientation='landscape') -show() - diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..e8df52c --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,88 @@ +CC=gcc +AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 +CFLAGS = -g -Werror -Os +objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ + root-tree.o dir-item.o file-item.o inode-item.o \ + inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ + volumes.o utils.o + +# +CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ + -Wuninitialized -Wshadow -Wundef +DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ + +INSTALL= install +prefix ?= /usr/local +bindir = $(prefix)/bin +LIBS=-luuid + +progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ + btrfs-map-logical + +# make C=1 to enable sparse +ifdef C + check=sparse $(CHECKFLAGS) +else + check=ls +endif + +.c.o: + $(check) $< + $(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $< + + +all: version $(progs) manpages + +version: + bash version.sh + +btrfsctl: $(objects) btrfsctl.o + gcc $(CFLAGS) -static -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) + +btrfs-vol: $(objects) btrfs-vol.o + gcc $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS) + gcc $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS) + +btrfsck: $(objects) btrfsck.o + gcc $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS) + +mkfs.btrfs: $(objects) mkfs.o + gcc $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS) + +btrfs-debug-tree: $(objects) debug-tree.o + gcc $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS) + +btrfstune: $(objects) btrfstune.o + gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS) + +btrfs-map-logical: $(objects) btrfs-map-logical.o + gcc $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS) + +btrfs-image: $(objects) btrfs-image.o + gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) + +dir-test: $(objects) dir-test.o + gcc $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS) + +quick-test: $(objects) quick-test.o + gcc $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS) + +convert: $(objects) convert.o + gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) $(LIBS) + +manpages: + cd man; make + +install-man: + cd man; make install + +clean : + rm -f $(progs) cscope.out *.o .*.d btrfs-convert + cd man; make clean + +install: $(progs) install-man + $(INSTALL) -m755 -d $(DESTDIR)$(bindir) + $(INSTALL) $(progs) $(DESTDIR)$(bindir) + if [ -e btrfs-convert ]; then $(INSTALL) btrfs-convert $(DESTDIR)$(bindir); fi + +-include .*.d diff --git a/tests/dir-test.c b/tests/dir-test.c new file mode 100644 index 0000000..44f2758 --- /dev/null +++ b/tests/dir-test.c @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "hash.h" +#include "transaction.h" + +int keep_running = 1; +struct btrfs_super_block super; +static u64 dir_oid = 0; +static u64 file_oid = 33778; + +static int find_num(struct radix_tree_root *root, unsigned long *num_ret, + int exists) +{ + unsigned long num = rand(); + unsigned long res[2]; + int ret; + +again: + ret = radix_tree_gang_lookup(root, (void **)res, num, 2); + if (exists) { + if (ret == 0) + return -1; + num = res[0]; + } else if (ret != 0 && num == res[0]) { + num++; + if (ret > 1 && num == res[1]) { + num++; + goto again; + } + } + *num_ret = num; + return 0; +} + +static void initial_inode_init(struct btrfs_root *root, + struct btrfs_inode_item *inode_item) +{ + memset(inode_item, 0, sizeof(*inode_item)); + btrfs_set_inode_generation(inode_item, root->fs_info->generation); + btrfs_set_inode_mode(inode_item, S_IFREG | 0700); +} + +static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct radix_tree_root *radix) +{ + int ret; + char buf[128]; + unsigned long oid; + u64 objectid; + struct btrfs_path path; + struct btrfs_key inode_map; + struct btrfs_inode_item inode_item; + + find_num(radix, &oid, 0); + sprintf(buf, "str-%lu", oid); + + ret = btrfs_find_free_objectid(trans, root, dir_oid + 1, &objectid); + if (ret) + goto error; + + inode_map.objectid = objectid; + inode_map.flags = 0; + btrfs_set_key_type(&inode_map, BTRFS_INODE_ITEM_KEY); + inode_map.offset = 0; + + initial_inode_init(root, &inode_item); + ret = btrfs_insert_inode(trans, root, objectid, &inode_item); + if (ret) + goto error; + ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid, + &inode_map, BTRFS_FT_UNKNOWN); + if (ret) + goto error; + + radix_tree_preload(GFP_KERNEL); + ret = radix_tree_insert(radix, oid, (void *)oid); + radix_tree_preload_end(); + if (ret) + goto error; + return ret; +error: + if (ret != -EEXIST) + goto fatal; + + /* + * if we got an EEXIST, it may be due to hash collision, double + * check + */ + btrfs_init_path(&path); + ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf, + strlen(buf), 0); + if (ret) + goto fatal_release; + if (!btrfs_match_dir_item_name(root, &path, buf, strlen(buf))) { + struct btrfs_dir_item *di; + char *found; + u32 found_len; + u64 myhash; + u64 foundhash; + + di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0], + struct btrfs_dir_item); + found = (char *)(di + 1); + found_len = btrfs_dir_name_len(di); + myhash = btrfs_name_hash(buf, strlen(buf)); + foundhash = btrfs_name_hash(found, found_len); + if (myhash != foundhash) + goto fatal_release; + btrfs_release_path(root, &path); + return 0; + } +fatal_release: + btrfs_release_path(root, &path); +fatal: + printf("failed to insert %lu ret %d\n", oid, ret); + return -1; +} + +static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct radix_tree_root *radix) +{ + int ret; + char buf[128]; + unsigned long oid; + struct btrfs_key key; + + ret = find_num(radix, &oid, 1); + if (ret < 0) + return 0; + sprintf(buf, "str-%lu", oid); + + key.objectid = file_oid; + key.flags = 0; + btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); + key.offset = 0; + ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid, + &key, BTRFS_FT_UNKNOWN); + if (ret != -EEXIST) { + printf("insert on %s gave us %d\n", buf, ret); + return 1; + } + return 0; +} + +static int del_dir_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct radix_tree_root *radix, + unsigned long radix_index, + struct btrfs_path *path) +{ + int ret; + unsigned long *ptr; + u64 file_objectid; + struct btrfs_dir_item *di; + + /* find the inode number of the file */ + di = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0], + struct btrfs_dir_item); + file_objectid = btrfs_disk_key_objectid(&di->location); + + /* delete the directory item */ + ret = btrfs_del_item(trans, root, path); + if (ret) + goto out_release; + btrfs_release_path(root, path); + + /* delete the inode */ + btrfs_init_path(path); + ret = btrfs_lookup_inode(trans, root, path, file_objectid, -1); + if (ret) + goto out_release; + ret = btrfs_del_item(trans, root, path); + if (ret) + goto out_release; + btrfs_release_path(root, path); + + if (root->fs_info->last_inode_alloc > file_objectid) + root->fs_info->last_inode_alloc = file_objectid; + ptr = radix_tree_delete(radix, radix_index); + if (!ptr) { + ret = -5555; + goto out; + } + return 0; +out_release: + btrfs_release_path(root, path); +out: + printf("failed to delete %lu %d\n", radix_index, ret); + return -1; +} + +static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct radix_tree_root *radix) +{ + int ret; + char buf[128]; + unsigned long oid; + struct btrfs_path path; + + ret = find_num(radix, &oid, 1); + if (ret < 0) + return 0; + sprintf(buf, "str-%lu", oid); + btrfs_init_path(&path); + ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf, + strlen(buf), -1); + if (ret) + goto out_release; + + ret = del_dir_item(trans, root, radix, oid, &path); + if (ret) + goto out_release; + return ret; +out_release: + btrfs_release_path(root, &path); + printf("failed to delete %lu %d\n", oid, ret); + return -1; +} + +static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct radix_tree_root *radix) +{ + struct btrfs_path path; + char buf[128]; + int ret; + unsigned long oid; + u64 objectid; + struct btrfs_dir_item *di; + + ret = find_num(radix, &oid, 1); + if (ret < 0) + return 0; + sprintf(buf, "str-%lu", oid); + btrfs_init_path(&path); + ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf, + strlen(buf), 0); + if (!ret) { + di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0], + struct btrfs_dir_item); + objectid = btrfs_disk_key_objectid(&di->location); + } + btrfs_release_path(root, &path); + if (ret) { + printf("unable to find key %lu\n", oid); + return -1; + } + return 0; +} + +static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct radix_tree_root *radix) +{ + struct btrfs_path path; + char buf[128]; + int ret; + unsigned long oid; + + ret = find_num(radix, &oid, 0); + if (ret < 0) + return 0; + sprintf(buf, "str-%lu", oid); + btrfs_init_path(&path); + ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf, + strlen(buf), 0); + btrfs_release_path(root, &path); + if (!ret) { + printf("able to find key that should not exist %lu\n", oid); + return -1; + } + return 0; +} + +static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct radix_tree_root *radix, int nr) +{ + struct btrfs_path path; + struct btrfs_key key; + unsigned long found = 0; + u32 found_len; + int ret; + int slot; + int count = 0; + char buf[128]; + struct btrfs_dir_item *di; + + key.offset = (u64)-1; + key.flags = 0; + btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); + key.objectid = dir_oid; + while(nr-- >= 0) { + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); + if (ret < 0) { + btrfs_release_path(root, &path); + return ret; + } + if (ret != 0) { + if (path.slots[0] == 0) { + btrfs_release_path(root, &path); + break; + } + path.slots[0] -= 1; + } + slot = path.slots[0]; + di = btrfs_item_ptr(&path.nodes[0]->leaf, slot, + struct btrfs_dir_item); + found_len = btrfs_dir_name_len(di); + memcpy(buf, (char *)(di + 1), found_len); + BUG_ON(found_len > 128); + buf[found_len] = '\0'; + found = atoi(buf + 4); + ret = del_dir_item(trans, root, radix, found, &path); + count++; + if (ret) { + fprintf(stderr, + "failed to remove %lu from tree\n", + found); + return -1; + } + if (!keep_running) + break; + } + return 0; + fprintf(stderr, "failed to delete from the radix %lu\n", found); + return -1; +} + +static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct radix_tree_root *radix, int count) +{ + int i; + int ret = 0; + for (i = 0; i < count; i++) { + ret = ins_one(trans, root, radix); + if (ret) { + fprintf(stderr, "fill failed\n"); + goto out; + } + if (i % 1000 == 0) { + ret = btrfs_commit_transaction(trans, root, &super); + if (ret) { + fprintf(stderr, "fill commit failed\n"); + return ret; + } + } + if (i && i % 10000 == 0) { + printf("bigfill %d\n", i); + } + if (!keep_running) + break; + } +out: + return ret; +} + +static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct radix_tree_root *radix) +{ + int ret; + int nr = rand() % 5000; + static int run_nr = 0; + + /* do the bulk op much less frequently */ + if (run_nr++ % 100) + return 0; + ret = empty_tree(trans, root, radix, nr); + if (ret) + return ret; + ret = fill_tree(trans, root, radix, nr); + if (ret) + return ret; + return 0; +} + + +int (*ops[])(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct + radix_tree_root *radix) = + { ins_one, insert_dup, del_one, lookup_item, + lookup_enoent, bulk_op }; + +void sigstopper(int ignored) +{ + keep_running = 0; + fprintf(stderr, "caught exit signal, stopping\n"); +} + +int print_usage(void) +{ + printf("usage: tester [-ih] [-c count] [-f count]\n"); + printf("\t -c count -- iteration count after filling\n"); + printf("\t -f count -- run this many random inserts before starting\n"); + printf("\t -i -- only do initial fill\n"); + printf("\t -h -- this help text\n"); + exit(1); +} +int main(int ac, char **av) +{ + RADIX_TREE(radix, GFP_KERNEL); + struct btrfs_root *root; + int i; + int ret; + int count; + int op; + int iterations = 20000; + int init_fill_count = 800000; + int err = 0; + int initial_only = 0; + struct btrfs_trans_handle *trans; + radix_tree_init(); + + root = open_ctree(av[ac-1], &super, 0); + trans = btrfs_start_transaction(root, 1); + + dir_oid = btrfs_super_root_dir(&super); + + signal(SIGTERM, sigstopper); + signal(SIGINT, sigstopper); + + for (i = 1 ; i < ac - 1; i++) { + if (strcmp(av[i], "-i") == 0) { + initial_only = 1; + } else if (strcmp(av[i], "-c") == 0) { + iterations = atoi(av[i+1]); + i++; + } else if (strcmp(av[i], "-f") == 0) { + init_fill_count = atoi(av[i+1]); + i++; + } else { + print_usage(); + } + } + printf("initial fill\n"); + ret = fill_tree(trans, root, &radix, init_fill_count); + printf("starting run\n"); + if (ret) { + err = ret; + goto out; + } + if (initial_only == 1) { + goto out; + } + for (i = 0; i < iterations; i++) { + op = rand() % ARRAY_SIZE(ops); + count = rand() % 128; + if (i % 2000 == 0) { + printf("%d\n", i); + fflush(stdout); + } + if (i && i % 5000 == 0) { + printf("open & close, root level %d nritems %d\n", + btrfs_header_level(&root->node->node.header), + btrfs_header_nritems(&root->node->node.header)); + close_ctree(root, &super); + root = open_ctree("dbfile", &super, 0); + } + while(count--) { + ret = ops[op](trans, root, &radix); + if (ret) { + fprintf(stderr, "op %d failed %d:%d\n", + op, i, iterations); + btrfs_print_tree(root, root->node); + fprintf(stderr, "op %d failed %d:%d\n", + op, i, iterations); + err = ret; + goto out; + } + if (ops[op] == bulk_op) + break; + if (keep_running == 0) { + err = 0; + goto out; + } + } + } +out: + close_ctree(root, &super); + return err; +} + diff --git a/tests/hasher.c b/tests/hasher.c new file mode 100644 index 0000000..b80407c --- /dev/null +++ b/tests/hasher.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include +#include "kerncompat.h" +#include "hash.h" + +int main() { + u64 result; + int ret; + char line[255]; + char *p; + while(1) { + p = fgets(line, 255, stdin); + if (!p) + break; + if (strlen(line) == 0) + continue; + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + result = btrfs_name_hash(line, strlen(line)); + printf("hash returns %llu\n", (unsigned long long)result); + } + return 0; +} diff --git a/tests/quick-test.c b/tests/quick-test.c new file mode 100644 index 0000000..351c706 --- /dev/null +++ b/tests/quick-test.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" + +/* for testing only */ +int next_key(int i, int max_key) { + return rand() % max_key; + // return i; +} + +int main(int ac, char **av) { + struct btrfs_key ins; + struct btrfs_key last = { (u64)-1, 0, 0}; + char *buf; + int i; + int num; + int ret; + int run_size = 300000; + int max_key = 100000000; + int tree_size = 2; + struct btrfs_path path; + struct btrfs_root *root; + struct btrfs_trans_handle *trans; + + buf = malloc(512); + memset(buf, 0, 512); + + radix_tree_init(); + + root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); + trans = btrfs_start_transaction(root, 1); + srand(55); + btrfs_set_key_type(&ins, BTRFS_STRING_ITEM_KEY); + for (i = 0; i < run_size; i++) { + num = next_key(i, max_key); + // num = i; + sprintf(buf, "string-%d", num); + if (i % 10000 == 0) + fprintf(stderr, "insert %d:%d\n", num, i); + ins.objectid = num; + ins.offset = 0; + ret = btrfs_insert_item(trans, root, &ins, buf, 512); + if (!ret) + tree_size++; + if (i == run_size - 5) { + btrfs_commit_transaction(trans, root); + trans = btrfs_start_transaction(root, 1); + } + } + btrfs_commit_transaction(trans, root); + close_ctree(root); + exit(1); + root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); + printf("starting search\n"); + srand(55); + for (i = 0; i < run_size; i++) { + num = next_key(i, max_key); + ins.objectid = num; + btrfs_init_path(&path); + if (i % 10000 == 0) + fprintf(stderr, "search %d:%d\n", num, i); + ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0); + if (ret) { + btrfs_print_tree(root, root->node); + printf("unable to find %d\n", num); + exit(1); + } + btrfs_release_path(root, &path); + } + close_ctree(root); + + root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); + printf("node %p level %d total ptrs %d free spc %lu\n", root->node, + btrfs_header_level(root->node), + btrfs_header_nritems(root->node), + (unsigned long)BTRFS_NODEPTRS_PER_BLOCK(root) - + btrfs_header_nritems(root->node)); + printf("all searches good, deleting some items\n"); + i = 0; + srand(55); + trans = btrfs_start_transaction(root, 1); + for (i = 0 ; i < run_size/4; i++) { + num = next_key(i, max_key); + ins.objectid = num; + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, &ins, &path, -1, 1); + if (!ret) { + if (i % 10000 == 0) + fprintf(stderr, "del %d:%d\n", num, i); + ret = btrfs_del_item(trans, root, &path); + if (ret != 0) + BUG(); + tree_size--; + } + btrfs_release_path(root, &path); + } + btrfs_commit_transaction(trans, root); + close_ctree(root); + + root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); + trans = btrfs_start_transaction(root, 1); + srand(128); + for (i = 0; i < run_size; i++) { + num = next_key(i, max_key); + sprintf(buf, "string-%d", num); + ins.objectid = num; + if (i % 10000 == 0) + fprintf(stderr, "insert %d:%d\n", num, i); + ret = btrfs_insert_item(trans, root, &ins, buf, 512); + if (!ret) + tree_size++; + } + btrfs_commit_transaction(trans, root); + close_ctree(root); + + root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR); + srand(128); + printf("starting search2\n"); + for (i = 0; i < run_size; i++) { + num = next_key(i, max_key); + ins.objectid = num; + btrfs_init_path(&path); + if (i % 10000 == 0) + fprintf(stderr, "search %d:%d\n", num, i); + ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0); + if (ret) { + btrfs_print_tree(root, root->node); + printf("unable to find %d\n", num); + exit(1); + } + btrfs_release_path(root, &path); + } + printf("starting big long delete run\n"); + trans = btrfs_start_transaction(root, 1); + while(root->node && btrfs_header_nritems(root->node) > 0) { + struct extent_buffer *leaf; + int slot; + ins.objectid = (u64)-1; + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, &ins, &path, -1, 1); + if (ret == 0) + BUG(); + + leaf = path.nodes[0]; + slot = path.slots[0]; + if (slot != btrfs_header_nritems(leaf)) + BUG(); + while(path.slots[0] > 0) { + path.slots[0] -= 1; + slot = path.slots[0]; + leaf = path.nodes[0]; + + btrfs_item_key_to_cpu(leaf, &last, slot); + + if (tree_size % 10000 == 0) + printf("big del %d:%d\n", tree_size, i); + ret = btrfs_del_item(trans, root, &path); + if (ret != 0) { + printf("del_item returned %d\n", ret); + BUG(); + } + tree_size--; + } + btrfs_release_path(root, &path); + } + /* + printf("previous tree:\n"); + btrfs_print_tree(root, root->commit_root); + printf("map before commit\n"); + btrfs_print_tree(root->extent_root, root->extent_root->node); + */ + btrfs_commit_transaction(trans, root); + printf("tree size is now %d\n", tree_size); + printf("root %p commit root %p\n", root->node, root->commit_root); + btrfs_print_tree(root, root->node); + close_ctree(root); + return 0; +} diff --git a/tests/random-test.c b/tests/random-test.c new file mode 100644 index 0000000..571735d --- /dev/null +++ b/tests/random-test.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2007 Oracle. 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 +#include +#include +#include "kerncompat.h" +#include "radix-tree.h" +#include "ctree.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" + +int keep_running = 1; +struct btrfs_super_block super; + +static int setup_key(struct radix_tree_root *root, struct btrfs_key *key, + int exists) +{ + int num = rand(); + unsigned long res[2]; + int ret; + + key->flags = 0; + btrfs_set_key_type(key, BTRFS_STRING_ITEM_KEY); + key->offset = 0; +again: + ret = radix_tree_gang_lookup(root, (void **)res, num, 2); + if (exists) { + if (ret == 0) + return -1; + num = res[0]; + } else if (ret != 0 && num == res[0]) { + num++; + if (ret > 1 && num == res[1]) { + num++; + goto again; + } + } + key->objectid = num; + return 0; +} + +static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct radix_tree_root *radix) +{ + struct btrfs_path path; + struct btrfs_key key; + int ret; + char buf[128]; + unsigned long oid; + btrfs_init_path(&path); + ret = setup_key(radix, &key, 0); + sprintf(buf, "str-%llu\n", (unsigned long long)key.objectid); + ret = btrfs_insert_item(trans, root, &key, buf, strlen(buf)); + if (ret) + goto error; + oid = (unsigned long)key.objectid; + radix_tree_preload(GFP_KERNEL); + ret = radix_tree_insert(radix, oid, (void *)oid); + radix_tree_preload_end(); + if (ret) + goto error; + return ret; +error: + printf("failed to insert %llu\n", (unsigned long long)key.objectid); + return -1; +} + +static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct radix_tree_root *radix) +{ + struct btrfs_path path; + struct btrfs_key key; + int ret; + char buf[128]; + btrfs_init_path(&path); + ret = setup_key(radix, &key, 1); + if (ret < 0) + return 0; + sprintf(buf, "str-%llu\n", (unsigned long long)key.objectid); + ret = btrfs_insert_item(trans, root, &key, buf, strlen(buf)); + if (ret != -EEXIST) { + printf("insert on %llu gave us %d\n", + (unsigned long long)key.objectid, ret); + return 1; + } + return 0; +} + +static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct radix_tree_root *radix) +{ + struct btrfs_path path; + struct btrfs_key key; + int ret; + unsigned long *ptr; + btrfs_init_path(&path); + ret = setup_key(radix, &key, 1); + if (ret < 0) + return 0; + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); + if (ret) + goto error; + ret = btrfs_del_item(trans, root, &path); + btrfs_release_path(root, &path); + if (ret != 0) + goto error; + ptr = radix_tree_delete(radix, key.objectid); + if (!ptr) + goto error; + return 0; +error: + printf("failed to delete %llu\n", (unsigned long long)key.objectid); + return -1; +} + +static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct radix_tree_root *radix) +{ + struct btrfs_path path; + struct btrfs_key key; + int ret; + btrfs_init_path(&path); + ret = setup_key(radix, &key, 1); + if (ret < 0) + return 0; + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); + btrfs_release_path(root, &path); + if (ret) + goto error; + return 0; +error: + printf("unable to find key %llu\n", (unsigned long long)key.objectid); + return -1; +} + +static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct radix_tree_root *radix) +{ + struct btrfs_path path; + struct btrfs_key key; + int ret; + btrfs_init_path(&path); + ret = setup_key(radix, &key, 0); + if (ret < 0) + return ret; + ret = btrfs_search_slot(trans, root, &key, &path, 0, 0); + btrfs_release_path(root, &path); + if (ret <= 0) + goto error; + return 0; +error: + printf("able to find key that should not exist %llu\n", + (unsigned long long)key.objectid); + return -1; +} + +static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct radix_tree_root *radix, int nr) +{ + struct btrfs_path path; + struct btrfs_key key; + unsigned long found = 0; + int ret; + int slot; + int *ptr; + int count = 0; + + key.offset = 0; + key.flags = 0; + btrfs_set_key_type(&key, BTRFS_STRING_ITEM_KEY); + key.objectid = (unsigned long)-1; + while(nr-- >= 0) { + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); + if (ret < 0) { + btrfs_release_path(root, &path); + return ret; + } + if (ret != 0) { + if (path.slots[0] == 0) { + btrfs_release_path(root, &path); + break; + } + path.slots[0] -= 1; + } + slot = path.slots[0]; + found = btrfs_disk_key_objectid( + &path.nodes[0]->leaf.items[slot].key); + ret = btrfs_del_item(trans, root, &path); + count++; + if (ret) { + fprintf(stderr, + "failed to remove %lu from tree\n", + found); + return -1; + } + btrfs_release_path(root, &path); + ptr = radix_tree_delete(radix, found); + if (!ptr) + goto error; + if (!keep_running) + break; + } + return 0; +error: + fprintf(stderr, "failed to delete from the radix %lu\n", found); + return -1; +} + +static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct radix_tree_root *radix, int count) +{ + int i; + int ret = 0; + for (i = 0; i < count; i++) { + ret = ins_one(trans, root, radix); + if (ret) { + fprintf(stderr, "fill failed\n"); + goto out; + } + if (i % 1000 == 0) { + ret = btrfs_commit_transaction(trans, root, &super); + if (ret) { + fprintf(stderr, "fill commit failed\n"); + return ret; + } + } + if (i && i % 10000 == 0) { + printf("bigfill %d\n", i); + } + if (!keep_running) + break; + } +out: + return ret; +} + +static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct radix_tree_root *radix) +{ + int ret; + int nr = rand() % 5000; + static int run_nr = 0; + + /* do the bulk op much less frequently */ + if (run_nr++ % 100) + return 0; + ret = empty_tree(trans, root, radix, nr); + if (ret) + return ret; + ret = fill_tree(trans, root, radix, nr); + if (ret) + return ret; + return 0; +} + + +int (*ops[])(struct btrfs_trans_handle *, + struct btrfs_root *root, struct radix_tree_root *radix) = + { ins_one, insert_dup, del_one, lookup_item, + lookup_enoent, bulk_op }; + +static int fill_radix(struct btrfs_root *root, struct radix_tree_root *radix) +{ + struct btrfs_path path; + struct btrfs_key key; + unsigned long found = 0; + int ret; + int slot; + int i; + + key.offset = 0; + key.flags = 0; + btrfs_set_key_type(&key, BTRFS_STRING_ITEM_KEY); + key.objectid = (unsigned long)-1; + while(1) { + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) { + btrfs_release_path(root, &path); + return ret; + } + slot = path.slots[0]; + if (ret != 0) { + if (slot == 0) { + btrfs_release_path(root, &path); + break; + } + slot -= 1; + } + for (i = slot; i >= 0; i--) { + found = btrfs_disk_key_objectid(&path.nodes[0]-> + leaf.items[i].key); + radix_tree_preload(GFP_KERNEL); + ret = radix_tree_insert(radix, found, (void *)found); + if (ret) { + fprintf(stderr, + "failed to insert %lu into radix\n", + found); + exit(1); + } + + radix_tree_preload_end(); + } + btrfs_release_path(root, &path); + key.objectid = found - 1; + if (key.objectid > found) + break; + } + return 0; +} +void sigstopper(int ignored) +{ + keep_running = 0; + fprintf(stderr, "caught exit signal, stopping\n"); +} + +int print_usage(void) +{ + printf("usage: tester [-ih] [-c count] [-f count]\n"); + printf("\t -c count -- iteration count after filling\n"); + printf("\t -f count -- run this many random inserts before starting\n"); + printf("\t -i -- only do initial fill\n"); + printf("\t -h -- this help text\n"); + exit(1); +} +int main(int ac, char **av) +{ + RADIX_TREE(radix, GFP_KERNEL); + struct btrfs_root *root; + int i; + int ret; + int count; + int op; + int iterations = 20000; + int init_fill_count = 800000; + int err = 0; + int initial_only = 0; + struct btrfs_trans_handle *trans; + radix_tree_init(); + root = open_ctree("dbfile", &super); + fill_radix(root, &radix); + + signal(SIGTERM, sigstopper); + signal(SIGINT, sigstopper); + + for (i = 1 ; i < ac ; i++) { + if (strcmp(av[i], "-i") == 0) { + initial_only = 1; + } else if (strcmp(av[i], "-c") == 0) { + iterations = atoi(av[i+1]); + i++; + } else if (strcmp(av[i], "-f") == 0) { + init_fill_count = atoi(av[i+1]); + i++; + } else { + print_usage(); + } + } + printf("initial fill\n"); + trans = btrfs_start_transaction(root, 1); + ret = fill_tree(trans, root, &radix, init_fill_count); + printf("starting run\n"); + if (ret) { + err = ret; + goto out; + } + if (initial_only == 1) { + goto out; + } + for (i = 0; i < iterations; i++) { + op = rand() % ARRAY_SIZE(ops); + count = rand() % 128; + if (i % 2000 == 0) { + printf("%d\n", i); + fflush(stdout); + } + if (i && i % 5000 == 0) { + printf("open & close, root level %d nritems %d\n", + btrfs_header_level(&root->node->node.header), + btrfs_header_nritems(&root->node->node.header)); + close_ctree(root, &super); + root = open_ctree("dbfile", &super); + } + while(count--) { + ret = ops[op](trans, root, &radix); + if (ret) { + fprintf(stderr, "op %d failed %d:%d\n", + op, i, iterations); + btrfs_print_tree(root, root->node); + fprintf(stderr, "op %d failed %d:%d\n", + op, i, iterations); + err = ret; + goto out; + } + if (ops[op] == bulk_op) + break; + if (keep_running == 0) { + err = 0; + goto out; + } + } + } +out: + close_ctree(root, &super); + return err; +} + diff --git a/tests/show-blocks b/tests/show-blocks new file mode 100644 index 0000000..0164be9 --- /dev/null +++ b/tests/show-blocks @@ -0,0 +1,325 @@ +#!/usr/bin/env python +# +# Copyright (C) 2007 Oracle. 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. +# +import sys, os, signal, time, commands, tempfile, random + +# numpy seems to override random() with something else. Instantiate our +# own here +randgen = random.Random() +randgen.seed(50) + +from optparse import OptionParser +from matplotlib import rcParams +from matplotlib.font_manager import fontManager, FontProperties +import numpy + +rcParams['numerix'] = 'numpy' +rcParams['backend'] = 'Agg' +rcParams['interactive'] = 'False' +from pylab import * + +class AnnoteFinder: + """ + callback for matplotlib to display an annotation when points are clicked on. The + point which is closest to the click and within xtol and ytol is identified. + + Register this function like this: + + scatter(xdata, ydata) + af = AnnoteFinder(xdata, ydata, annotes) + connect('button_press_event', af) + """ + + def __init__(self, axis=None): + if axis is None: + self.axis = gca() + else: + self.axis= axis + self.drawnAnnotations = {} + self.links = [] + + def clear(self): + for k in self.drawnAnnotations.keys(): + self.drawnAnnotations[k].set_visible(False) + + def __call__(self, event): + if event.inaxes: + if event.button != 1: + self.clear() + draw() + return + clickX = event.xdata + clickY = event.ydata + if (self.axis is None) or (self.axis==event.inaxes): + self.drawAnnote(event.inaxes, clickX, clickY) + + def drawAnnote(self, axis, x, y): + """ + Draw the annotation on the plot + """ + if self.drawnAnnotations.has_key((x,y)): + markers = self.drawnAnnotations[(x,y)] + markers.set_visible(not markers.get_visible()) + draw() + else: + t = axis.text(x,y, "(%3.2f, %3.2f)"%(x,y), bbox=dict(facecolor='red', + alpha=0.8)) + self.drawnAnnotations[(x,y)] = t + draw() + +def loaddata(fh,delimiter=None, converters=None): + + #14413824 8192 extent back ref root 5 gen 10 owner 282 num_refs 1 + def iter(fh, delimiter, converters): + global total_data + global total_metadata + for i,line in enumerate(fh): + line = line.split(' ') + start = float(line[0]) + len = float(line[1]) + owner = float(line[10]) + root = float(line[6]) + if owner <= 255: + total_metadata += int(len) + else: + total_data += int(len) + if start < zoommin or (zoommax != 0 and start > zoommax): + continue + yield start + yield len + yield owner + yield root + X = numpy.fromiter(iter(fh, delimiter, converters), dtype=float) + return X + +def run_debug_tree(device): + p = os.popen('btrfs-debug-tree -e ' + device) + data = loaddata(p) + return data + +def shapeit(X): + lines = len(X) / 4 + X.shape = (lines, 4) + +def line_picker(line, mouseevent): + if mouseevent.xdata is None: return False, dict() + print "%d %d\n", mouseevent.xdata, mouseevent.ydata + return False, dict() + +def xycalc(byte): + byte = byte / bytes_per_cell + yval = floor(byte / num_cells) + xval = byte % num_cells + return (xval, yval + 1) + +# record the color used for each root the first time we find it +root_colors = {} +# there are lots of good colormaps to choose from +# http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps +# +meta_cmap = get_cmap("gist_ncar") +data_done = False + +def plotone(a, xvals, yvals, owner, root, lines, labels): + global data_done + add_label = False + + if owner: + if options.meta_only: + return + color = "blue" + label = "Data" + if not data_done: + add_label = True + data_done = True + else: + if options.data_only: + return + if root not in root_colors: + color = meta_cmap(randgen.random()) + label = "Meta %d" % int(root) + root_colors[root] = (color, label) + add_label = True + else: + color, label = root_colors[root] + + plotlines = a.plot(xvals, yvals, 's', color=color, mfc=color, mec=color, + markersize=.23, label=label) + if add_label: + lines += plotlines + labels.append(label) + print "add label %s" % label + +def parse_zoom(): + def parse_num(s): + mult = 1 + c = s.lower()[-1] + if c == 't': + mult = 1024 * 1024 * 1024 * 1024 + elif c == 'g': + mult = 1024 * 1024 * 1024 + elif c == 'm': + mult = 1024 * 1024 + elif c == 'k': + mult = 1024 + else: + c = None + if c: + num = int(s[:-1]) * mult + else: + num = int(s) + return num + + if not options.zoom: + return (0, 0) + + vals = options.zoom.split(':') + if len(vals) != 2: + sys.stderr.write("warning: unable to parse zoom %s\n" % options.zoom) + return (0, 0) + zoommin = parse_num(vals[0]) + zoommax = parse_num(vals[1]) + return (zoommin, zoommax) + +usage = "usage: %prog [options]" +parser = OptionParser(usage=usage) +parser.add_option("-d", "--device", help="Btrfs device", default="") +parser.add_option("-i", "--input-file", help="debug-tree data", default="") +parser.add_option("-o", "--output", help="Output file", default="blocks.png") +parser.add_option("-z", "--zoom", help="Zoom", default=None) +parser.add_option("", "--data-only", help="Only print data blocks", + default=False, action="store_true") +parser.add_option("", "--meta-only", help="Only print metadata blocks", + default=False, action="store_true") + +(options,args) = parser.parse_args() + +if not options.device and not options.input_file: + parser.print_help() + sys.exit(1) + +zoommin, zoommax = parse_zoom() +total_data = 0 +total_metadata = 0 + +if options.device: + data = run_debug_tree(options.device) +elif options.input_file: + data = loaddata(file(options.input_file)) +shapeit(data) + +# try to drop out the least common data points by creating +# a historgram of the sectors seen. +sectors = data[:,0] +sizes = data[:,1] +datalen = len(data) +sectormax = numpy.max(sectors) +sectormin = 0 +num_cells = 800 +total_cells = num_cells * num_cells +byte_range = sectormax - sectormin +bytes_per_cell = byte_range / total_cells + +f = figure(figsize=(8,6)) + +# Throughput goes at the botoom +a = subplot(1, 1, 1) +subplots_adjust(right=0.7) +datai = 0 +xvals = [] +yvals = [] +last_owner = 0 +last_root = 0 +lines = [] +labels = [] +while datai < datalen: + row = data[datai] + datai += 1 + byte = row[0] + size = row[1] + owner = row[2] + root = row[3] + + if owner <= 255: + owner = 0 + else: + owner = 1 + + if len(xvals) and (owner != last_owner or last_root != root): + plotone(a, xvals, yvals, last_owner, last_root, lines, labels) + xvals = [] + yvals = [] + cell = 0 + while cell < size: + xy = xycalc(byte) + byte += bytes_per_cell + cell += bytes_per_cell + if xy: + xvals.append(xy[0]) + yvals.append(xy[1]) + last_owner = owner + last_root = root + +if xvals: + plotone(a, xvals, yvals, last_owner, last_root, lines, labels) + +# make sure the final second goes on the x axes +ticks = [] +a.set_xticks(ticks) +ticks = a.get_yticks() + +first_tick = ticks[1] * bytes_per_cell * num_cells +if first_tick > 1024 * 1024 * 1024 * 1024: + scale = 1024 * 1024 * 1024 * 1024; + scalestr = "TB" +elif first_tick > 1024 * 1024 * 1024: + scale = 1024 * 1024 * 1024; + scalestr = "GB" +elif first_tick > 1024 * 1024: + scale = 1024 * 1024; + scalestr = "MB" +elif first_tick > 1024: + scale = 1024; + scalestr = "KB" +else: + scalestr = "Bytes" + scale = 1 + +ylabels = [ str(int((x * bytes_per_cell * num_cells) / scale)) for x in ticks ] +a.set_yticklabels(ylabels) +a.set_ylabel('Disk offset (%s)' % scalestr) +a.set_xlim(0, num_cells) +a.set_title('Blocks') + +a.legend(lines, labels, loc=(1.05, 0.8), shadow=True, pad=0.1, numpoints=1, + handletextsep = 0.005, + labelsep = 0.01, + markerscale=10, + prop=FontProperties(size='x-small') ) + +if total_data == 0: + percent_meta = 100 +else: + percent_meta = (float(total_metadata) / float(total_data)) * 100 + +print "Total metadata bytes %d data %d ratio %.3f" % (total_metadata, + total_data, percent_meta) +print "saving graph to %s" % options.output +savefig(options.output, orientation='landscape') +show() + diff --git a/transaction.h b/transaction.h deleted file mode 100644 index a1070e0..0000000 --- a/transaction.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __TRANSACTION__ -#define __TRANSACTION__ - -struct btrfs_trans_handle { - u64 transid; - u64 alloc_exclude_start; - u64 alloc_exclude_nr; - unsigned long blocks_reserved; - unsigned long blocks_used; - struct btrfs_block_group_cache *block_group; -}; - -static inline struct btrfs_trans_handle * -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(root->commit_root); - BUG_ON(fs_info->running_transaction); - fs_info->running_transaction = h; - fs_info->generation++; - h->transid = fs_info->generation; - h->alloc_exclude_start = 0; - h->alloc_exclude_nr = 0; - h->blocks_reserved = num_blocks; - h->blocks_used = 0; - h->block_group = NULL; - root->last_trans = h->transid; - root->commit_root = root->node; - extent_buffer_get(root->node); - return h; -} - -static inline void btrfs_free_transaction(struct btrfs_root *root, - struct btrfs_trans_handle *handle) -{ - memset(handle, 0, sizeof(*handle)); - free(handle); -} - -#endif diff --git a/utils.c b/utils.c deleted file mode 100644 index 2f4c6e1..0000000 --- a/utils.c +++ /dev/null @@ -1,822 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 600 -#define __USE_XOPEN2K -#include -#include -#ifndef __CHECKER__ -#include -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include "kerncompat.h" -#include "radix-tree.h" -#include "ctree.h" -#include "disk-io.h" -#include "transaction.h" -#include "crc32c.h" -#include "utils.h" -#include "volumes.h" -#include "ioctl.h" - -#ifdef __CHECKER__ -#define BLKGETSIZE64 0 -static inline int ioctl(int fd, int define, u64 *size) { return 0; } -#endif - -static u64 reference_root_table[] = { - [1] = BTRFS_ROOT_TREE_OBJECTID, - [2] = BTRFS_EXTENT_TREE_OBJECTID, - [3] = BTRFS_CHUNK_TREE_OBJECTID, - [4] = BTRFS_DEV_TREE_OBJECTID, - [5] = BTRFS_FS_TREE_OBJECTID, - [6] = BTRFS_CSUM_TREE_OBJECTID, -}; - -int make_btrfs(int fd, const char *device, const char *label, - u64 blocks[7], u64 num_bytes, u32 nodesize, - u32 leafsize, u32 sectorsize, u32 stripesize) -{ - struct btrfs_super_block super; - struct extent_buffer *buf; - struct btrfs_root_item root_item; - struct btrfs_disk_key disk_key; - struct btrfs_extent_item *extent_item; - struct btrfs_inode_item *inode_item; - struct btrfs_chunk *chunk; - struct btrfs_dev_item *dev_item; - struct btrfs_dev_extent *dev_extent; - u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; - u8 *ptr; - int i; - int ret; - u32 itemoff; - u32 nritems = 0; - u64 first_free; - u64 ref_root; - u32 array_size; - u32 item_size; - - first_free = BTRFS_SUPER_INFO_OFFSET + sectorsize * 2 - 1; - first_free &= ~((u64)sectorsize - 1); - - memset(&super, 0, sizeof(super)); - - num_bytes = (num_bytes / sectorsize) * sectorsize; - uuid_generate(super.fsid); - uuid_generate(super.dev_item.uuid); - uuid_generate(chunk_tree_uuid); - - btrfs_set_super_bytenr(&super, blocks[0]); - btrfs_set_super_num_devices(&super, 1); - strncpy((char *)&super.magic, BTRFS_MAGIC, sizeof(super.magic)); - btrfs_set_super_generation(&super, 1); - btrfs_set_super_root(&super, blocks[1]); - btrfs_set_super_chunk_root(&super, blocks[3]); - btrfs_set_super_total_bytes(&super, num_bytes); - btrfs_set_super_bytes_used(&super, 6 * leafsize); - btrfs_set_super_sectorsize(&super, sectorsize); - btrfs_set_super_leafsize(&super, leafsize); - btrfs_set_super_nodesize(&super, nodesize); - btrfs_set_super_stripesize(&super, stripesize); - btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32); - btrfs_set_super_chunk_root_generation(&super, 1); - if (label) - strcpy(super.label, label); - - buf = malloc(sizeof(*buf) + max(sectorsize, leafsize)); - - /* create the tree of root objects */ - memset(buf->data, 0, leafsize); - buf->len = leafsize; - btrfs_set_header_bytenr(buf, blocks[1]); - btrfs_set_header_nritems(buf, 4); - btrfs_set_header_generation(buf, 1); - btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV); - btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID); - write_extent_buffer(buf, super.fsid, (unsigned long) - btrfs_header_fsid(buf), BTRFS_FSID_SIZE); - - write_extent_buffer(buf, chunk_tree_uuid, (unsigned long) - btrfs_header_chunk_tree_uuid(buf), - BTRFS_UUID_SIZE); - - /* create the items for the root tree */ - memset(&root_item, 0, sizeof(root_item)); - inode_item = &root_item.inode; - btrfs_set_stack_inode_generation(inode_item, 1); - btrfs_set_stack_inode_size(inode_item, 3); - btrfs_set_stack_inode_nlink(inode_item, 1); - btrfs_set_stack_inode_nbytes(inode_item, leafsize); - btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755); - btrfs_set_root_refs(&root_item, 1); - btrfs_set_root_used(&root_item, leafsize); - btrfs_set_root_generation(&root_item, 1); - - memset(&disk_key, 0, sizeof(disk_key)); - btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY); - btrfs_set_disk_key_offset(&disk_key, 0); - nritems = 0; - - itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, blocks[2]); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), - sizeof(root_item)); - write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf, - nritems), sizeof(root_item)); - nritems++; - - itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, blocks[4]); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), - sizeof(root_item)); - write_extent_buffer(buf, &root_item, - btrfs_item_ptr_offset(buf, nritems), - sizeof(root_item)); - nritems++; - - itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, blocks[5]); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), - sizeof(root_item)); - write_extent_buffer(buf, &root_item, - btrfs_item_ptr_offset(buf, nritems), - sizeof(root_item)); - nritems++; - - itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, blocks[6]); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), - sizeof(root_item)); - write_extent_buffer(buf, &root_item, - btrfs_item_ptr_offset(buf, nritems), - sizeof(root_item)); - nritems++; - - - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, leafsize, blocks[1]); - BUG_ON(ret != leafsize); - - /* create the items for the extent tree */ - nritems = 0; - itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize); - for (i = 1; i < 7; i++) { - BUG_ON(blocks[i] < first_free); - BUG_ON(blocks[i] < blocks[i - 1]); - - /* create extent item */ - itemoff -= sizeof(struct btrfs_extent_item) + - sizeof(struct btrfs_tree_block_info); - btrfs_set_disk_key_objectid(&disk_key, blocks[i]); - btrfs_set_disk_key_offset(&disk_key, leafsize); - btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), - itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), - sizeof(struct btrfs_extent_item) + - sizeof(struct btrfs_tree_block_info)); - extent_item = btrfs_item_ptr(buf, nritems, - struct btrfs_extent_item); - btrfs_set_extent_refs(buf, extent_item, 1); - btrfs_set_extent_generation(buf, extent_item, 1); - btrfs_set_extent_flags(buf, extent_item, - BTRFS_EXTENT_FLAG_TREE_BLOCK); - nritems++; - - /* create extent ref */ - ref_root = reference_root_table[i]; - btrfs_set_disk_key_objectid(&disk_key, blocks[i]); - btrfs_set_disk_key_offset(&disk_key, ref_root); - btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), - itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), 0); - nritems++; - } - btrfs_set_header_bytenr(buf, blocks[2]); - btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_header_nritems(buf, nritems); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, leafsize, blocks[2]); - BUG_ON(ret != leafsize); - - /* create the chunk tree */ - nritems = 0; - item_size = sizeof(*dev_item); - itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - item_size; - - /* first device 1 (there is no device 0) */ - btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID); - btrfs_set_disk_key_offset(&disk_key, 1); - btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), item_size); - - dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item); - btrfs_set_device_id(buf, dev_item, 1); - btrfs_set_device_generation(buf, dev_item, 0); - btrfs_set_device_total_bytes(buf, dev_item, num_bytes); - btrfs_set_device_bytes_used(buf, dev_item, - BTRFS_MKFS_SYSTEM_GROUP_SIZE); - btrfs_set_device_io_align(buf, dev_item, sectorsize); - btrfs_set_device_io_width(buf, dev_item, sectorsize); - btrfs_set_device_sector_size(buf, dev_item, sectorsize); - btrfs_set_device_type(buf, dev_item, 0); - - write_extent_buffer(buf, super.dev_item.uuid, - (unsigned long)btrfs_device_uuid(dev_item), - BTRFS_UUID_SIZE); - write_extent_buffer(buf, super.fsid, - (unsigned long)btrfs_device_fsid(dev_item), - BTRFS_UUID_SIZE); - read_extent_buffer(buf, &super.dev_item, (unsigned long)dev_item, - sizeof(*dev_item)); - - nritems++; - item_size = btrfs_chunk_item_size(1); - itemoff = itemoff - item_size; - - /* then we have chunk 0 */ - btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_disk_key_offset(&disk_key, 0); - btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), item_size); - - chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk); - btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE); - btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_chunk_stripe_len(buf, chunk, 64 * 1024); - btrfs_set_chunk_type(buf, chunk, BTRFS_BLOCK_GROUP_SYSTEM); - btrfs_set_chunk_io_align(buf, chunk, sectorsize); - btrfs_set_chunk_io_width(buf, chunk, sectorsize); - btrfs_set_chunk_sector_size(buf, chunk, sectorsize); - btrfs_set_chunk_num_stripes(buf, chunk, 1); - btrfs_set_stripe_devid_nr(buf, chunk, 0, 1); - btrfs_set_stripe_offset_nr(buf, chunk, 0, 0); - nritems++; - - write_extent_buffer(buf, super.dev_item.uuid, - (unsigned long)btrfs_stripe_dev_uuid(&chunk->stripe), - BTRFS_UUID_SIZE); - - /* copy the key for the chunk to the system array */ - ptr = super.sys_chunk_array; - array_size = sizeof(disk_key); - - memcpy(ptr, &disk_key, sizeof(disk_key)); - ptr += sizeof(disk_key); - - /* copy the chunk to the system array */ - read_extent_buffer(buf, ptr, (unsigned long)chunk, item_size); - array_size += item_size; - ptr += item_size; - btrfs_set_super_sys_array_size(&super, array_size); - - btrfs_set_header_bytenr(buf, blocks[3]); - btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID); - btrfs_set_header_nritems(buf, nritems); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, leafsize, blocks[3]); - - /* create the device tree */ - nritems = 0; - itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - - sizeof(struct btrfs_dev_extent); - - btrfs_set_disk_key_objectid(&disk_key, 1); - btrfs_set_disk_key_offset(&disk_key, 0); - btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), - sizeof(struct btrfs_dev_extent)); - dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent); - btrfs_set_dev_extent_chunk_tree(buf, dev_extent, - BTRFS_CHUNK_TREE_OBJECTID); - btrfs_set_dev_extent_chunk_objectid(buf, dev_extent, - BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_dev_extent_chunk_offset(buf, dev_extent, 0); - - write_extent_buffer(buf, chunk_tree_uuid, - (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent), - BTRFS_UUID_SIZE); - - btrfs_set_dev_extent_length(buf, dev_extent, - BTRFS_MKFS_SYSTEM_GROUP_SIZE); - nritems++; - - btrfs_set_header_bytenr(buf, blocks[4]); - btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID); - btrfs_set_header_nritems(buf, nritems); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, leafsize, blocks[4]); - - /* create the FS root */ - btrfs_set_header_bytenr(buf, blocks[5]); - btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID); - btrfs_set_header_nritems(buf, 0); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, leafsize, blocks[5]); - BUG_ON(ret != leafsize); - - /* finally create the csum root */ - btrfs_set_header_bytenr(buf, blocks[6]); - btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID); - btrfs_set_header_nritems(buf, 0); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, leafsize, blocks[6]); - BUG_ON(ret != leafsize); - - /* and write out the super block */ - BUG_ON(sizeof(super) > sectorsize); - memset(buf->data, 0, sectorsize); - memcpy(buf->data, &super, sizeof(super)); - buf->len = sectorsize; - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, sectorsize, blocks[0]); - BUG_ON(ret != sectorsize); - - - free(buf); - return 0; -} - -static u64 device_size(int fd, struct stat *st) -{ - u64 size; - if (S_ISREG(st->st_mode)) { - return st->st_size; - } - if (!S_ISBLK(st->st_mode)) { - return 0; - } - if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { - return size; - } - return 0; -} - -static int zero_blocks(int fd, off_t start, size_t len) -{ - char *buf = malloc(len); - int ret = 0; - ssize_t written; - - if (!buf) - return -ENOMEM; - memset(buf, 0, len); - written = pwrite(fd, buf, len, start); - if (written != len) - ret = -EIO; - free(buf); - return ret; -} - -static int zero_dev_start(int fd) -{ - off_t start = 0; - size_t len = 2 * 1024 * 1024; - -#ifdef __sparc__ - /* don't overwrite the disk labels on sparc */ - start = 1024; - len -= 1024; -#endif - return zero_blocks(fd, start, len); -} - -static int zero_dev_end(int fd, u64 dev_size) -{ - size_t len = 2 * 1024 * 1024; - off_t start = dev_size - len; - - return zero_blocks(fd, start, len); -} - -int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, - struct btrfs_root *root, int fd, char *path, - u64 block_count, u32 io_width, u32 io_align, - u32 sectorsize) -{ - struct btrfs_super_block *disk_super; - struct btrfs_super_block *super = &root->fs_info->super_copy; - struct btrfs_device *device; - struct btrfs_dev_item *dev_item; - char *buf; - u64 total_bytes; - u64 num_devs; - int ret; - - device = kmalloc(sizeof(*device), GFP_NOFS); - if (!device) - return -ENOMEM; - buf = kmalloc(sectorsize, GFP_NOFS); - if (!buf) { - kfree(device); - return -ENOMEM; - } - BUG_ON(sizeof(*disk_super) > sectorsize); - memset(buf, 0, sectorsize); - - disk_super = (struct btrfs_super_block *)buf; - dev_item = &disk_super->dev_item; - - uuid_generate(device->uuid); - device->devid = 0; - device->type = 0; - device->io_width = io_width; - device->io_align = io_align; - device->sector_size = sectorsize; - device->fd = fd; - device->writeable = 1; - device->total_bytes = block_count; - device->bytes_used = 0; - device->total_ios = 0; - device->dev_root = root->fs_info->dev_root; - - ret = btrfs_add_device(trans, root, device); - BUG_ON(ret); - - total_bytes = btrfs_super_total_bytes(super) + block_count; - btrfs_set_super_total_bytes(super, total_bytes); - - num_devs = btrfs_super_num_devices(super) + 1; - btrfs_set_super_num_devices(super, num_devs); - - memcpy(disk_super, super, sizeof(*disk_super)); - - printf("adding device %s id %llu\n", path, - (unsigned long long)device->devid); - - btrfs_set_super_bytenr(disk_super, BTRFS_SUPER_INFO_OFFSET); - btrfs_set_stack_device_id(dev_item, device->devid); - btrfs_set_stack_device_type(dev_item, device->type); - btrfs_set_stack_device_io_align(dev_item, device->io_align); - btrfs_set_stack_device_io_width(dev_item, device->io_width); - btrfs_set_stack_device_sector_size(dev_item, device->sector_size); - btrfs_set_stack_device_total_bytes(dev_item, device->total_bytes); - btrfs_set_stack_device_bytes_used(dev_item, device->bytes_used); - memcpy(&dev_item->uuid, device->uuid, BTRFS_UUID_SIZE); - - ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET); - BUG_ON(ret != sectorsize); - - kfree(buf); - list_add(&device->dev_list, &root->fs_info->fs_devices->devices); - device->fs_devices = root->fs_info->fs_devices; - return 0; -} - -int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret) -{ - u64 block_count; - u64 bytenr; - struct stat st; - int i, ret; - - ret = fstat(fd, &st); - if (ret < 0) { - fprintf(stderr, "unable to stat %s\n", file); - exit(1); - } - - block_count = device_size(fd, &st); - if (block_count == 0) { - fprintf(stderr, "unable to find %s size\n", file); - exit(1); - } - zero_end = 1; - - if (block_count < 256 * 1024 * 1024) { - fprintf(stderr, "device %s is too small " - "(must be at least 256 MB)\n", file); - exit(1); - } - ret = zero_dev_start(fd); - if (ret) { - fprintf(stderr, "failed to zero device start %d\n", ret); - exit(1); - } - - for (i = 0 ; i < BTRFS_SUPER_MIRROR_MAX; i++) { - bytenr = btrfs_sb_offset(i); - if (bytenr >= block_count) - break; - zero_blocks(fd, bytenr, BTRFS_SUPER_INFO_SIZE); - } - - if (zero_end) { - ret = zero_dev_end(fd, block_count); - if (ret) { - fprintf(stderr, "failed to zero device end %d\n", ret); - exit(1); - } - } - *block_count_ret = block_count; - return 0; -} - -int btrfs_make_root_dir(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid) -{ - int ret; - struct btrfs_inode_item inode_item; - - memset(&inode_item, 0, sizeof(inode_item)); - btrfs_set_stack_inode_generation(&inode_item, trans->transid); - btrfs_set_stack_inode_size(&inode_item, 0); - btrfs_set_stack_inode_nlink(&inode_item, 1); - btrfs_set_stack_inode_nbytes(&inode_item, root->leafsize); - btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0555); - - if (root->fs_info->tree_root == root) - btrfs_set_super_root_dir(&root->fs_info->super_copy, objectid); - - ret = btrfs_insert_inode(trans, root, objectid, &inode_item); - if (ret) - goto error; - - ret = btrfs_insert_inode_ref(trans, root, "..", 2, objectid, objectid, 0); - if (ret) - goto error; - - btrfs_set_root_dirid(&root->root_item, objectid); - ret = 0; -error: - return ret; -} - -/* - * returns 1 if the device was mounted, < 0 on error or 0 if everything - * is safe to continue. TODO, this should also scan multi-device filesystems - */ -int check_mounted(char *file) -{ - struct mntent *mnt; - struct stat st_buf; - dev_t file_dev = 0; - dev_t file_rdev = 0; - ino_t file_ino = 0; - FILE *f; - int ret = 0; - - if ((f = setmntent ("/proc/mounts", "r")) == NULL) - return -errno; - - if (stat(file, &st_buf) < 0) { - return -errno; - } else { - if (S_ISBLK(st_buf.st_mode)) { - file_rdev = st_buf.st_rdev; - } else { - file_dev = st_buf.st_dev; - file_ino = st_buf.st_ino; - } - } - - while ((mnt = getmntent (f)) != NULL) { - if (strcmp(file, mnt->mnt_fsname) == 0) - break; - - if (stat(mnt->mnt_fsname, &st_buf) == 0) { - if (S_ISBLK(st_buf.st_mode)) { - if (file_rdev && (file_rdev == st_buf.st_rdev)) - break; - } else if (file_dev && ((file_dev == st_buf.st_dev) && - (file_ino == st_buf.st_ino))) { - break; - } - } - } - - if (mnt) { - /* found an entry in mnt table */ - ret = 1; - } - - endmntent (f); - return ret; -} - -struct pending_dir { - struct list_head list; - char name[256]; -}; - -void btrfs_register_one_device(char *fname) -{ - struct btrfs_ioctl_vol_args args; - int fd; - int ret; - - fd = open("/dev/btrfs-control", O_RDONLY); - if (fd < 0) { - fprintf(stderr, "failed to open /dev/btrfs-control " - "skipping device registration\n"); - return; - } - strcpy(args.name, fname); - ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); - close(fd); -} - -int btrfs_scan_one_dir(char *dirname, int run_ioctl) -{ - DIR *dirp = NULL; - struct dirent *dirent; - struct pending_dir *pending; - struct stat st; - int ret; - int fd; - int dirname_len; - int pathlen; - char *fullpath; - struct list_head pending_list; - struct btrfs_fs_devices *tmp_devices; - u64 num_devices; - - INIT_LIST_HEAD(&pending_list); - - pending = malloc(sizeof(*pending)); - if (!pending) - return -ENOMEM; - strcpy(pending->name, dirname); - -again: - dirname_len = strlen(pending->name); - pathlen = 1024; - fullpath = malloc(pathlen); - dirname = pending->name; - - if (!fullpath) { - ret = -ENOMEM; - goto fail; - } - dirp = opendir(dirname); - if (!dirp) { - fprintf(stderr, "Unable to open /sys/block for scanning\n"); - return -ENOENT; - } - while(1) { - dirent = readdir(dirp); - if (!dirent) - break; - if (dirent->d_name[0] == '.') - continue; - if (dirname_len + strlen(dirent->d_name) + 2 > pathlen) { - ret = -EFAULT; - goto fail; - } - snprintf(fullpath, pathlen, "%s/%s", dirname, dirent->d_name); - ret = lstat(fullpath, &st); - if (ret < 0) { - fprintf(stderr, "failed to stat %s\n", fullpath); - continue; - } - if (S_ISLNK(st.st_mode)) - continue; - if (S_ISDIR(st.st_mode)) { - struct pending_dir *next = malloc(sizeof(*next)); - if (!next) { - ret = -ENOMEM; - goto fail; - } - strcpy(next->name, fullpath); - list_add_tail(&next->list, &pending_list); - } - if (!S_ISBLK(st.st_mode)) { - continue; - } - fd = open(fullpath, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "failed to read %s\n", fullpath); - continue; - } - ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices, - &num_devices, - BTRFS_SUPER_INFO_OFFSET); - if (ret == 0 && run_ioctl > 0) { - btrfs_register_one_device(fullpath); - } - close(fd); - } - if (!list_empty(&pending_list)) { - free(pending); - pending = list_entry(pending_list.next, struct pending_dir, - list); - list_del(&pending->list); - closedir(dirp); - goto again; - } - ret = 0; -fail: - free(pending); - if (dirp) - closedir(dirp); - return ret; -} - -int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs, - int run_ioctls) -{ - return btrfs_scan_one_dir("/dev", run_ioctls); -} - -int btrfs_device_already_in_root(struct btrfs_root *root, int fd, - int super_offset) -{ - struct btrfs_super_block *disk_super; - char *buf; - int ret = 0; - - buf = malloc(BTRFS_SUPER_INFO_SIZE); - if (!buf) { - ret = -ENOMEM; - goto out; - } - ret = pread(fd, buf, BTRFS_SUPER_INFO_SIZE, super_offset); - if (ret != BTRFS_SUPER_INFO_SIZE) - goto brelse; - - ret = 0; - disk_super = (struct btrfs_super_block *)buf; - if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC, - sizeof(disk_super->magic))) - goto brelse; - - if (!memcmp(disk_super->fsid, root->fs_info->super_copy.fsid, - BTRFS_FSID_SIZE)) - ret = 1; -brelse: - free(buf); -out: - return ret; -} - -static char *size_strs[] = { "", "KB", "MB", "GB", "TB", - "PB", "EB", "ZB", "YB"}; -char *pretty_sizes(u64 size) -{ - int num_divs = 0; - u64 last_size = size; - u64 fract_size = size; - float fraction; - char *pretty; - - while(size > 0) { - fract_size = last_size; - last_size = size; - size /= 1024; - num_divs++; - } - if (num_divs == 0) - num_divs = 1; - if (num_divs > ARRAY_SIZE(size_strs)) - return NULL; - - fraction = (float)fract_size / 1024; - pretty = malloc(16); - sprintf(pretty, "%.2f%s", fraction, size_strs[num_divs-1]); - return pretty; -} - diff --git a/utils.h b/utils.h deleted file mode 100644 index 7ff542b..0000000 --- a/utils.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __UTILS__ -#define __UTILS__ - -#define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024) - -int make_btrfs(int fd, const char *device, const char *label, - u64 blocks[6], u64 num_bytes, u32 nodesize, - u32 leafsize, u32 sectorsize, u32 stripesize); -int btrfs_make_root_dir(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid); -int btrfs_prepare_device(int fd, char *file, int zero_end, - u64 *block_count_ret); -int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, - struct btrfs_root *root, int fd, char *path, - u64 block_count, u32 io_width, u32 io_align, - u32 sectorsize); -int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs, - int run_ioctls); -void btrfs_register_one_device(char *fname); -int btrfs_scan_one_dir(char *dirname, int run_ioctl); -int check_mounted(char *devicename); -int btrfs_device_already_in_root(struct btrfs_root *root, int fd, - int super_offset); -char *pretty_sizes(u64 size); -#endif diff --git a/version.sh b/version.sh deleted file mode 100644 index af3e441..0000000 --- a/version.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash -# -# determine-version -- report a useful version for releases -# -# Copyright 2008, Aron Griffis -# Copyright 2008, Oracle -# Released under the GNU GPLv2 - -v="Btrfs v0.19" - -which git &> /dev/null -if [ $? == 0 -a -d .git ]; then - if head=`git rev-parse --verify HEAD 2>/dev/null`; then - if tag=`git describe --tags 2>/dev/null`; then - v="$tag" - fi - - # Are there uncommitted changes? - git update-index --refresh --unmerged > /dev/null - if git diff-index --name-only HEAD | grep -v "^scripts/package" \ - | read dummy; then - v="$v"-dirty - fi - fi -fi - -which hg &> /dev/null -if [ $? == 0 -a -d .hg ]; then - last=$(hg tags | grep -m1 -o '^v[0-9.]\+') - - # now check if the repo has commits since then... - if [[ $(hg id -t) == $last || \ - $(hg di -r "$last:." | awk '/^diff/{print $NF}' | sort -u) == .hgtags ]] - then - # check if it's dirty - if [[ $(hg id | cut -d' ' -f1) == *+ ]]; then - v=$last+ - else - v=$last - fi - else - # includes dirty flag - v=$last+$(hg id -i) - fi -fi - -echo "#ifndef __BUILD_VERSION" > .build-version.h -echo "#define __BUILD_VERSION" >> .build-version.h -echo "#define BTRFS_BUILD_VERSION \"Btrfs $v\"" >> .build-version.h -echo "#endif" >> .build-version.h - -diff -q version.h .build-version.h >& /dev/null - -if [ $? == 0 ]; then - rm .build-version.h - exit 0 -fi - -mv .build-version.h version.h diff --git a/volumes.c b/volumes.c deleted file mode 100644 index 7671855..0000000 --- a/volumes.c +++ /dev/null @@ -1,1450 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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 600 -#define __USE_XOPEN2K -#include -#include -#include -#include -#include -#include -#include -#include "ctree.h" -#include "disk-io.h" -#include "transaction.h" -#include "print-tree.h" -#include "volumes.h" - -struct stripe { - struct btrfs_device *dev; - u64 physical; -}; - -struct map_lookup { - struct cache_extent ce; - u64 type; - int io_align; - int io_width; - int stripe_len; - int sector_size; - int num_stripes; - int sub_stripes; - struct btrfs_bio_stripe stripes[]; -}; - -#define map_lookup_size(n) (sizeof(struct map_lookup) + \ - (sizeof(struct btrfs_bio_stripe) * (n))) - -static LIST_HEAD(fs_uuids); - -static struct btrfs_device *__find_device(struct list_head *head, u64 devid, - u8 *uuid) -{ - struct btrfs_device *dev; - struct list_head *cur; - - list_for_each(cur, head) { - dev = list_entry(cur, struct btrfs_device, dev_list); - if (dev->devid == devid && - !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE)) { - return dev; - } - } - return NULL; -} - -static struct btrfs_fs_devices *find_fsid(u8 *fsid) -{ - struct list_head *cur; - struct btrfs_fs_devices *fs_devices; - - list_for_each(cur, &fs_uuids) { - fs_devices = list_entry(cur, struct btrfs_fs_devices, list); - if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0) - return fs_devices; - } - return NULL; -} - -static int device_list_add(const char *path, - struct btrfs_super_block *disk_super, - u64 devid, struct btrfs_fs_devices **fs_devices_ret) -{ - struct btrfs_device *device; - struct btrfs_fs_devices *fs_devices; - u64 found_transid = btrfs_super_generation(disk_super); - - fs_devices = find_fsid(disk_super->fsid); - if (!fs_devices) { - fs_devices = kzalloc(sizeof(*fs_devices), GFP_NOFS); - if (!fs_devices) - return -ENOMEM; - INIT_LIST_HEAD(&fs_devices->devices); - list_add(&fs_devices->list, &fs_uuids); - memcpy(fs_devices->fsid, disk_super->fsid, BTRFS_FSID_SIZE); - fs_devices->latest_devid = devid; - fs_devices->latest_trans = found_transid; - fs_devices->lowest_devid = (u64)-1; - device = NULL; - } else { - device = __find_device(&fs_devices->devices, devid, - disk_super->dev_item.uuid); - } - if (!device) { - device = kzalloc(sizeof(*device), GFP_NOFS); - if (!device) { - /* we can safely leave the fs_devices entry around */ - return -ENOMEM; - } - device->devid = devid; - memcpy(device->uuid, disk_super->dev_item.uuid, - BTRFS_UUID_SIZE); - device->name = kstrdup(path, GFP_NOFS); - if (!device->name) { - kfree(device); - return -ENOMEM; - } - device->label = kstrdup(disk_super->label, GFP_NOFS); - device->total_devs = btrfs_super_num_devices(disk_super); - device->super_bytes_used = btrfs_super_bytes_used(disk_super); - device->total_bytes = - btrfs_stack_device_total_bytes(&disk_super->dev_item); - device->bytes_used = - btrfs_stack_device_bytes_used(&disk_super->dev_item); - list_add(&device->dev_list, &fs_devices->devices); - device->fs_devices = fs_devices; - } - - if (found_transid > fs_devices->latest_trans) { - fs_devices->latest_devid = devid; - fs_devices->latest_trans = found_transid; - } - if (fs_devices->lowest_devid > devid) { - fs_devices->lowest_devid = devid; - } - *fs_devices_ret = fs_devices; - return 0; -} - -int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) -{ - struct btrfs_fs_devices *seed_devices; - struct list_head *cur; - struct btrfs_device *device; -again: - list_for_each(cur, &fs_devices->devices) { - device = list_entry(cur, struct btrfs_device, dev_list); - close(device->fd); - device->fd = -1; - device->writeable = 0; - } - - seed_devices = fs_devices->seed; - fs_devices->seed = NULL; - if (seed_devices) { - fs_devices = seed_devices; - goto again; - } - - return 0; -} - -int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, int flags) -{ - int fd; - struct list_head *head = &fs_devices->devices; - struct list_head *cur; - struct btrfs_device *device; - int ret; - - list_for_each(cur, head) { - device = list_entry(cur, struct btrfs_device, dev_list); - - fd = open(device->name, flags); - if (fd < 0) { - ret = -errno; - goto fail; - } - - if (device->devid == fs_devices->latest_devid) - fs_devices->latest_bdev = fd; - if (device->devid == fs_devices->lowest_devid) - fs_devices->lowest_bdev = fd; - device->fd = fd; - if (flags == O_RDWR) - device->writeable = 1; - } - return 0; -fail: - btrfs_close_devices(fs_devices); - return ret; -} - -int btrfs_scan_one_device(int fd, const char *path, - struct btrfs_fs_devices **fs_devices_ret, - u64 *total_devs, u64 super_offset) -{ - struct btrfs_super_block *disk_super; - char *buf; - int ret; - u64 devid; - char uuidbuf[37]; - - buf = malloc(4096); - if (!buf) { - ret = -ENOMEM; - goto error; - } - disk_super = (struct btrfs_super_block *)buf; - ret = btrfs_read_dev_super(fd, disk_super, super_offset); - if (ret < 0) { - ret = -EIO; - goto error_brelse; - } - devid = le64_to_cpu(disk_super->dev_item.devid); - if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP) - *total_devs = 1; - else - *total_devs = btrfs_super_num_devices(disk_super); - uuid_unparse(disk_super->fsid, uuidbuf); - - ret = device_list_add(path, disk_super, devid, fs_devices_ret); - -error_brelse: - free(buf); -error: - return ret; -} - -/* - * this uses a pretty simple search, the expectation is that it is - * called very infrequently and that a given device has a small number - * of extents - */ -static int find_free_dev_extent(struct btrfs_trans_handle *trans, - struct btrfs_device *device, - struct btrfs_path *path, - u64 num_bytes, u64 *start) -{ - struct btrfs_key key; - struct btrfs_root *root = device->dev_root; - struct btrfs_dev_extent *dev_extent = NULL; - u64 hole_size = 0; - u64 last_byte = 0; - u64 search_start = 0; - u64 search_end = device->total_bytes; - int ret; - int slot = 0; - int start_found; - struct extent_buffer *l; - - start_found = 0; - path->reada = 2; - - /* FIXME use last free of some kind */ - - /* we don't want to overwrite the superblock on the drive, - * so we make sure to start at an offset of at least 1MB - */ - search_start = max((u64)1024 * 1024, search_start); - - if (root->fs_info->alloc_start + num_bytes <= device->total_bytes) - search_start = max(root->fs_info->alloc_start, search_start); - - key.objectid = device->devid; - key.offset = search_start; - key.type = BTRFS_DEV_EXTENT_KEY; - ret = btrfs_search_slot(trans, root, &key, path, 0, 0); - if (ret < 0) - goto error; - ret = btrfs_previous_item(root, path, 0, key.type); - if (ret < 0) - goto error; - l = path->nodes[0]; - btrfs_item_key_to_cpu(l, &key, path->slots[0]); - while (1) { - l = path->nodes[0]; - slot = path->slots[0]; - if (slot >= btrfs_header_nritems(l)) { - ret = btrfs_next_leaf(root, path); - if (ret == 0) - continue; - if (ret < 0) - goto error; -no_more_items: - if (!start_found) { - if (search_start >= search_end) { - ret = -ENOSPC; - goto error; - } - *start = search_start; - start_found = 1; - goto check_pending; - } - *start = last_byte > search_start ? - last_byte : search_start; - if (search_end <= *start) { - ret = -ENOSPC; - goto error; - } - goto check_pending; - } - btrfs_item_key_to_cpu(l, &key, slot); - - if (key.objectid < device->devid) - goto next; - - if (key.objectid > device->devid) - goto no_more_items; - - if (key.offset >= search_start && key.offset > last_byte && - start_found) { - if (last_byte < search_start) - last_byte = search_start; - hole_size = key.offset - last_byte; - if (key.offset > last_byte && - hole_size >= num_bytes) { - *start = last_byte; - goto check_pending; - } - } - if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY) { - goto next; - } - - start_found = 1; - dev_extent = btrfs_item_ptr(l, slot, struct btrfs_dev_extent); - last_byte = key.offset + btrfs_dev_extent_length(l, dev_extent); -next: - path->slots[0]++; - cond_resched(); - } -check_pending: - /* we have to make sure we didn't find an extent that has already - * been allocated by the map tree or the original allocation - */ - btrfs_release_path(root, path); - BUG_ON(*start < search_start); - - if (*start + num_bytes > search_end) { - ret = -ENOSPC; - goto error; - } - /* check for pending inserts here */ - return 0; - -error: - btrfs_release_path(root, path); - return ret; -} - -int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans, - struct btrfs_device *device, - u64 chunk_tree, u64 chunk_objectid, - u64 chunk_offset, - u64 num_bytes, u64 *start) -{ - int ret; - struct btrfs_path *path; - struct btrfs_root *root = device->dev_root; - struct btrfs_dev_extent *extent; - struct extent_buffer *leaf; - struct btrfs_key key; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - ret = find_free_dev_extent(trans, device, path, num_bytes, start); - if (ret) { - goto err; - } - - key.objectid = device->devid; - key.offset = *start; - key.type = BTRFS_DEV_EXTENT_KEY; - ret = btrfs_insert_empty_item(trans, root, path, &key, - sizeof(*extent)); - BUG_ON(ret); - - leaf = path->nodes[0]; - extent = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_dev_extent); - btrfs_set_dev_extent_chunk_tree(leaf, extent, chunk_tree); - btrfs_set_dev_extent_chunk_objectid(leaf, extent, chunk_objectid); - btrfs_set_dev_extent_chunk_offset(leaf, extent, chunk_offset); - - write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, - (unsigned long)btrfs_dev_extent_chunk_tree_uuid(extent), - BTRFS_UUID_SIZE); - - btrfs_set_dev_extent_length(leaf, extent, num_bytes); - btrfs_mark_buffer_dirty(leaf); -err: - btrfs_free_path(path); - return ret; -} - -static int find_next_chunk(struct btrfs_root *root, u64 objectid, u64 *offset) -{ - struct btrfs_path *path; - int ret; - struct btrfs_key key; - struct btrfs_chunk *chunk; - struct btrfs_key found_key; - - path = btrfs_alloc_path(); - BUG_ON(!path); - - key.objectid = objectid; - key.offset = (u64)-1; - key.type = BTRFS_CHUNK_ITEM_KEY; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) - goto error; - - BUG_ON(ret == 0); - - ret = btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY); - if (ret) { - *offset = 0; - } else { - btrfs_item_key_to_cpu(path->nodes[0], &found_key, - path->slots[0]); - if (found_key.objectid != objectid) - *offset = 0; - else { - chunk = btrfs_item_ptr(path->nodes[0], path->slots[0], - struct btrfs_chunk); - *offset = found_key.offset + - btrfs_chunk_length(path->nodes[0], chunk); - } - } - ret = 0; -error: - btrfs_free_path(path); - return ret; -} - -static int find_next_devid(struct btrfs_root *root, struct btrfs_path *path, - u64 *objectid) -{ - int ret; - struct btrfs_key key; - struct btrfs_key found_key; - - key.objectid = BTRFS_DEV_ITEMS_OBJECTID; - key.type = BTRFS_DEV_ITEM_KEY; - key.offset = (u64)-1; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) - goto error; - - BUG_ON(ret == 0); - - ret = btrfs_previous_item(root, path, BTRFS_DEV_ITEMS_OBJECTID, - BTRFS_DEV_ITEM_KEY); - if (ret) { - *objectid = 1; - } else { - btrfs_item_key_to_cpu(path->nodes[0], &found_key, - path->slots[0]); - *objectid = found_key.offset + 1; - } - ret = 0; -error: - btrfs_release_path(root, path); - return ret; -} - -/* - * the device information is stored in the chunk root - * the btrfs_device struct should be fully filled in - */ -int btrfs_add_device(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_device *device) -{ - int ret; - struct btrfs_path *path; - struct btrfs_dev_item *dev_item; - struct extent_buffer *leaf; - struct btrfs_key key; - unsigned long ptr; - u64 free_devid = 0; - - root = root->fs_info->chunk_root; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - ret = find_next_devid(root, path, &free_devid); - if (ret) - goto out; - - key.objectid = BTRFS_DEV_ITEMS_OBJECTID; - key.type = BTRFS_DEV_ITEM_KEY; - key.offset = free_devid; - - ret = btrfs_insert_empty_item(trans, root, path, &key, - sizeof(*dev_item)); - if (ret) - goto out; - - leaf = path->nodes[0]; - dev_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_item); - - device->devid = free_devid; - btrfs_set_device_id(leaf, dev_item, device->devid); - btrfs_set_device_generation(leaf, dev_item, 0); - btrfs_set_device_type(leaf, dev_item, device->type); - btrfs_set_device_io_align(leaf, dev_item, device->io_align); - btrfs_set_device_io_width(leaf, dev_item, device->io_width); - btrfs_set_device_sector_size(leaf, dev_item, device->sector_size); - btrfs_set_device_total_bytes(leaf, dev_item, device->total_bytes); - btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used); - btrfs_set_device_group(leaf, dev_item, 0); - btrfs_set_device_seek_speed(leaf, dev_item, 0); - btrfs_set_device_bandwidth(leaf, dev_item, 0); - btrfs_set_device_start_offset(leaf, dev_item, 0); - - ptr = (unsigned long)btrfs_device_uuid(dev_item); - write_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE); - ptr = (unsigned long)btrfs_device_fsid(dev_item); - write_extent_buffer(leaf, root->fs_info->fsid, ptr, BTRFS_UUID_SIZE); - btrfs_mark_buffer_dirty(leaf); - ret = 0; - -out: - btrfs_free_path(path); - return ret; -} - -int btrfs_update_device(struct btrfs_trans_handle *trans, - struct btrfs_device *device) -{ - int ret; - struct btrfs_path *path; - struct btrfs_root *root; - struct btrfs_dev_item *dev_item; - struct extent_buffer *leaf; - struct btrfs_key key; - - root = device->dev_root->fs_info->chunk_root; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - key.objectid = BTRFS_DEV_ITEMS_OBJECTID; - key.type = BTRFS_DEV_ITEM_KEY; - key.offset = device->devid; - - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); - if (ret < 0) - goto out; - - if (ret > 0) { - ret = -ENOENT; - goto out; - } - - leaf = path->nodes[0]; - dev_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_item); - - btrfs_set_device_id(leaf, dev_item, device->devid); - btrfs_set_device_type(leaf, dev_item, device->type); - btrfs_set_device_io_align(leaf, dev_item, device->io_align); - btrfs_set_device_io_width(leaf, dev_item, device->io_width); - btrfs_set_device_sector_size(leaf, dev_item, device->sector_size); - btrfs_set_device_total_bytes(leaf, dev_item, device->total_bytes); - btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used); - btrfs_mark_buffer_dirty(leaf); - -out: - btrfs_free_path(path); - return ret; -} - -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) -{ - struct btrfs_super_block *super_copy = &root->fs_info->super_copy; - struct btrfs_disk_key disk_key; - u32 array_size; - u8 *ptr; - - array_size = btrfs_super_sys_array_size(super_copy); - if (array_size + item_size > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) - return -EFBIG; - - ptr = super_copy->sys_chunk_array + array_size; - btrfs_cpu_key_to_disk(&disk_key, key); - memcpy(ptr, &disk_key, sizeof(disk_key)); - ptr += sizeof(disk_key); - memcpy(ptr, chunk, item_size); - item_size += sizeof(disk_key); - btrfs_set_super_sys_array_size(super_copy, array_size + item_size); - return 0; -} - -static u64 div_factor(u64 num, int factor) -{ - if (factor == 10) - return num; - num *= factor; - return num / 10; -} - -static u64 chunk_bytes_by_type(u64 type, u64 calc_size, int num_stripes, - int sub_stripes) -{ - if (type & (BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP)) - return calc_size; - else if (type & BTRFS_BLOCK_GROUP_RAID10) - return calc_size * (num_stripes / sub_stripes); - else - return calc_size * num_stripes; -} - - -int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, - struct btrfs_root *extent_root, u64 *start, - u64 *num_bytes, u64 type) -{ - u64 dev_offset; - struct btrfs_fs_info *info = extent_root->fs_info; - struct btrfs_root *chunk_root = extent_root->fs_info->chunk_root; - struct btrfs_stripe *stripes; - struct btrfs_device *device = NULL; - struct btrfs_chunk *chunk; - struct list_head private_devs; - struct list_head *dev_list = &extent_root->fs_info->fs_devices->devices; - struct list_head *cur; - struct map_lookup *map; - int min_stripe_size = 1 * 1024 * 1024; - u64 physical; - u64 calc_size = 8 * 1024 * 1024; - u64 min_free; - u64 max_chunk_size = 4 * calc_size; - u64 avail; - u64 max_avail = 0; - u64 percent_max; - int num_stripes = 1; - int min_stripes = 1; - int sub_stripes = 0; - int looped = 0; - int ret; - int index; - int stripe_len = 64 * 1024; - struct btrfs_key key; - - if (list_empty(dev_list)) { - return -ENOSPC; - } - - if (type & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_RAID10 | - BTRFS_BLOCK_GROUP_DUP)) { - if (type & BTRFS_BLOCK_GROUP_SYSTEM) { - calc_size = 8 * 1024 * 1024; - max_chunk_size = calc_size * 2; - min_stripe_size = 1 * 1024 * 1024; - } else if (type & BTRFS_BLOCK_GROUP_DATA) { - calc_size = 1024 * 1024 * 1024; - max_chunk_size = 10 * calc_size; - min_stripe_size = 64 * 1024 * 1024; - } else if (type & BTRFS_BLOCK_GROUP_METADATA) { - calc_size = 1024 * 1024 * 1024; - max_chunk_size = 4 * calc_size; - min_stripe_size = 32 * 1024 * 1024; - } - } - if (type & BTRFS_BLOCK_GROUP_RAID1) { - num_stripes = min_t(u64, 2, - btrfs_super_num_devices(&info->super_copy)); - if (num_stripes < 2) - return -ENOSPC; - min_stripes = 2; - } - if (type & BTRFS_BLOCK_GROUP_DUP) { - num_stripes = 2; - min_stripes = 2; - } - if (type & (BTRFS_BLOCK_GROUP_RAID0)) { - num_stripes = btrfs_super_num_devices(&info->super_copy); - min_stripes = 2; - } - if (type & (BTRFS_BLOCK_GROUP_RAID10)) { - num_stripes = btrfs_super_num_devices(&info->super_copy); - if (num_stripes < 4) - return -ENOSPC; - num_stripes &= ~(u32)1; - sub_stripes = 2; - min_stripes = 4; - } - - /* we don't want a chunk larger than 10% of the FS */ - percent_max = div_factor(btrfs_super_total_bytes(&info->super_copy), 1); - max_chunk_size = min(percent_max, max_chunk_size); - -again: - if (chunk_bytes_by_type(type, calc_size, num_stripes, sub_stripes) > - max_chunk_size) { - calc_size = max_chunk_size; - calc_size /= num_stripes; - calc_size /= stripe_len; - calc_size *= stripe_len; - } - /* we don't want tiny stripes */ - calc_size = max_t(u64, calc_size, min_stripe_size); - - calc_size /= stripe_len; - calc_size *= stripe_len; - INIT_LIST_HEAD(&private_devs); - cur = dev_list->next; - index = 0; - - if (type & BTRFS_BLOCK_GROUP_DUP) - min_free = calc_size * 2; - else - min_free = calc_size; - - /* build a private list of devices we will allocate from */ - while(index < num_stripes) { - device = list_entry(cur, struct btrfs_device, dev_list); - avail = device->total_bytes - device->bytes_used; - cur = cur->next; - if (avail >= min_free) { - list_move_tail(&device->dev_list, &private_devs); - index++; - if (type & BTRFS_BLOCK_GROUP_DUP) - index++; - } else if (avail > max_avail) - max_avail = avail; - if (cur == dev_list) - break; - } - if (index < num_stripes) { - list_splice(&private_devs, dev_list); - if (index >= min_stripes) { - num_stripes = index; - if (type & (BTRFS_BLOCK_GROUP_RAID10)) { - num_stripes /= sub_stripes; - num_stripes *= sub_stripes; - } - looped = 1; - goto again; - } - if (!looped && max_avail > 0) { - looped = 1; - calc_size = max_avail; - goto again; - } - return -ENOSPC; - } - key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; - key.type = BTRFS_CHUNK_ITEM_KEY; - ret = find_next_chunk(chunk_root, BTRFS_FIRST_CHUNK_TREE_OBJECTID, - &key.offset); - if (ret) - return ret; - - chunk = kmalloc(btrfs_chunk_item_size(num_stripes), GFP_NOFS); - if (!chunk) - return -ENOMEM; - - map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); - if (!map) { - kfree(chunk); - return -ENOMEM; - } - - stripes = &chunk->stripe; - *num_bytes = chunk_bytes_by_type(type, calc_size, - num_stripes, sub_stripes); - index = 0; - while(index < num_stripes) { - struct btrfs_stripe *stripe; - BUG_ON(list_empty(&private_devs)); - cur = private_devs.next; - device = list_entry(cur, struct btrfs_device, dev_list); - - /* loop over this device again if we're doing a dup group */ - if (!(type & BTRFS_BLOCK_GROUP_DUP) || - (index == num_stripes - 1)) - list_move_tail(&device->dev_list, dev_list); - - ret = btrfs_alloc_dev_extent(trans, device, - info->chunk_root->root_key.objectid, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset, - calc_size, &dev_offset); - BUG_ON(ret); - - device->bytes_used += calc_size; - ret = btrfs_update_device(trans, device); - BUG_ON(ret); - - map->stripes[index].dev = device; - map->stripes[index].physical = dev_offset; - stripe = stripes + index; - btrfs_set_stack_stripe_devid(stripe, device->devid); - btrfs_set_stack_stripe_offset(stripe, dev_offset); - memcpy(stripe->dev_uuid, device->uuid, BTRFS_UUID_SIZE); - physical = dev_offset; - index++; - } - BUG_ON(!list_empty(&private_devs)); - - /* key was set above */ - btrfs_set_stack_chunk_length(chunk, *num_bytes); - btrfs_set_stack_chunk_owner(chunk, extent_root->root_key.objectid); - btrfs_set_stack_chunk_stripe_len(chunk, stripe_len); - btrfs_set_stack_chunk_type(chunk, type); - btrfs_set_stack_chunk_num_stripes(chunk, num_stripes); - btrfs_set_stack_chunk_io_align(chunk, stripe_len); - btrfs_set_stack_chunk_io_width(chunk, stripe_len); - btrfs_set_stack_chunk_sector_size(chunk, extent_root->sectorsize); - btrfs_set_stack_chunk_sub_stripes(chunk, sub_stripes); - map->sector_size = extent_root->sectorsize; - map->stripe_len = stripe_len; - map->io_align = stripe_len; - map->io_width = stripe_len; - map->type = type; - map->num_stripes = num_stripes; - map->sub_stripes = sub_stripes; - - ret = btrfs_insert_item(trans, chunk_root, &key, chunk, - btrfs_chunk_item_size(num_stripes)); - BUG_ON(ret); - *start = key.offset;; - - map->ce.start = key.offset; - map->ce.size = *num_bytes; - - ret = insert_existing_cache_extent( - &extent_root->fs_info->mapping_tree.cache_tree, - &map->ce); - BUG_ON(ret); - - if (type & BTRFS_BLOCK_GROUP_SYSTEM) { - ret = btrfs_add_system_chunk(trans, chunk_root, &key, - chunk, btrfs_chunk_item_size(num_stripes)); - BUG_ON(ret); - } - - kfree(chunk); - return ret; -} - -void btrfs_mapping_init(struct btrfs_mapping_tree *tree) -{ - cache_tree_init(&tree->cache_tree); -} - -int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len) -{ - struct cache_extent *ce; - struct map_lookup *map; - int ret; - u64 offset; - - ce = find_first_cache_extent(&map_tree->cache_tree, logical); - BUG_ON(!ce); - BUG_ON(ce->start > logical || ce->start + ce->size < logical); - map = container_of(ce, struct map_lookup, ce); - - offset = logical - ce->start; - if (map->type & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1)) - ret = map->num_stripes; - else if (map->type & BTRFS_BLOCK_GROUP_RAID10) - ret = map->sub_stripes; - else - ret = 1; - return ret; -} - -int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, - u64 chunk_start, u64 physical, u64 devid, - u64 **logical, int *naddrs, int *stripe_len) -{ - struct cache_extent *ce; - struct map_lookup *map; - u64 *buf; - u64 bytenr; - u64 length; - u64 stripe_nr; - int i, j, nr = 0; - - ce = find_first_cache_extent(&map_tree->cache_tree, chunk_start); - BUG_ON(!ce); - map = container_of(ce, struct map_lookup, ce); - - length = ce->size; - if (map->type & BTRFS_BLOCK_GROUP_RAID10) - length = ce->size / (map->num_stripes / map->sub_stripes); - else if (map->type & BTRFS_BLOCK_GROUP_RAID0) - length = ce->size / map->num_stripes; - - buf = kzalloc(sizeof(u64) * map->num_stripes, GFP_NOFS); - - for (i = 0; i < map->num_stripes; i++) { - if (devid && map->stripes[i].dev->devid != devid) - continue; - if (map->stripes[i].physical > physical || - map->stripes[i].physical + length <= physical) - continue; - - stripe_nr = (physical - map->stripes[i].physical) / - map->stripe_len; - - if (map->type & BTRFS_BLOCK_GROUP_RAID10) { - stripe_nr = (stripe_nr * map->num_stripes + i) / - map->sub_stripes; - } else if (map->type & BTRFS_BLOCK_GROUP_RAID0) { - stripe_nr = stripe_nr * map->num_stripes + i; - } - bytenr = ce->start + stripe_nr * map->stripe_len; - for (j = 0; j < nr; j++) { - if (buf[j] == bytenr) - break; - } - if (j == nr) - buf[nr++] = bytenr; - } - - *logical = buf; - *naddrs = nr; - *stripe_len = map->stripe_len; - - return 0; -} - -int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, - u64 logical, u64 *length, - struct btrfs_multi_bio **multi_ret, int mirror_num) -{ - struct cache_extent *ce; - struct map_lookup *map; - u64 offset; - u64 stripe_offset; - u64 stripe_nr; - int stripes_allocated = 8; - int stripes_required = 1; - int stripe_index; - int i; - struct btrfs_multi_bio *multi = NULL; - - if (multi_ret && rw == READ) { - stripes_allocated = 1; - } -again: - if (multi_ret) { - multi = kzalloc(btrfs_multi_bio_size(stripes_allocated), - GFP_NOFS); - if (!multi) - return -ENOMEM; - } - - ce = find_first_cache_extent(&map_tree->cache_tree, logical); - BUG_ON(!ce); - BUG_ON(ce->start > logical || ce->start + ce->size < logical); - map = container_of(ce, struct map_lookup, ce); - offset = logical - ce->start; - - if (rw == WRITE) { - if (map->type & (BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_DUP)) { - stripes_required = map->num_stripes; - } else if (map->type & BTRFS_BLOCK_GROUP_RAID10) { - stripes_required = map->sub_stripes; - } - } - /* if our multi bio struct is too small, back off and try again */ - if (multi_ret && rw == WRITE && - stripes_allocated < stripes_required) { - stripes_allocated = map->num_stripes; - kfree(multi); - goto again; - } - stripe_nr = offset; - /* - * stripe_nr counts the total number of stripes we have to stride - * to get to this block - */ - stripe_nr = stripe_nr / map->stripe_len; - - stripe_offset = stripe_nr * map->stripe_len; - BUG_ON(offset < stripe_offset); - - /* stripe_offset is the offset of this block in its stripe*/ - stripe_offset = offset - stripe_offset; - - if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_RAID10 | - BTRFS_BLOCK_GROUP_DUP)) { - /* we limit the length of each bio to what fits in a stripe */ - *length = min_t(u64, ce->size - offset, - map->stripe_len - stripe_offset); - } else { - *length = ce->size - offset; - } - - if (!multi_ret) - goto out; - - multi->num_stripes = 1; - stripe_index = 0; - if (map->type & BTRFS_BLOCK_GROUP_RAID1) { - if (rw == WRITE) - multi->num_stripes = map->num_stripes; - else if (mirror_num) - stripe_index = mirror_num - 1; - else - stripe_index = stripe_nr % map->num_stripes; - } else if (map->type & BTRFS_BLOCK_GROUP_RAID10) { - int factor = map->num_stripes / map->sub_stripes; - - stripe_index = stripe_nr % factor; - stripe_index *= map->sub_stripes; - - if (rw == WRITE) - multi->num_stripes = map->sub_stripes; - else if (mirror_num) - stripe_index += mirror_num - 1; - else - stripe_index = stripe_nr % map->sub_stripes; - - stripe_nr = stripe_nr / factor; - } else if (map->type & BTRFS_BLOCK_GROUP_DUP) { - if (rw == WRITE) - multi->num_stripes = map->num_stripes; - else if (mirror_num) - stripe_index = mirror_num - 1; - } else { - /* - * after this do_div call, stripe_nr is the number of stripes - * on this device we have to walk to find the data, and - * stripe_index is the number of our device in the stripe array - */ - stripe_index = stripe_nr % map->num_stripes; - stripe_nr = stripe_nr / map->num_stripes; - } - BUG_ON(stripe_index >= map->num_stripes); - - for (i = 0; i < multi->num_stripes; i++) { - multi->stripes[i].physical = - map->stripes[stripe_index].physical + stripe_offset + - stripe_nr * map->stripe_len; - multi->stripes[i].dev = map->stripes[stripe_index].dev; - stripe_index++; - } - *multi_ret = multi; -out: - return 0; -} - -struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid, - u8 *uuid, u8 *fsid) -{ - struct btrfs_device *device; - struct btrfs_fs_devices *cur_devices; - - cur_devices = root->fs_info->fs_devices; - while (cur_devices) { - if (!fsid || - !memcmp(cur_devices->fsid, fsid, BTRFS_UUID_SIZE)) { - device = __find_device(&cur_devices->devices, - devid, uuid); - if (device) - return device; - } - cur_devices = cur_devices->seed; - } - return NULL; -} - -int btrfs_bootstrap_super_map(struct btrfs_mapping_tree *map_tree, - struct btrfs_fs_devices *fs_devices) -{ - struct map_lookup *map; - u64 logical = BTRFS_SUPER_INFO_OFFSET; - u64 length = BTRFS_SUPER_INFO_SIZE; - int num_stripes = 0; - int sub_stripes = 0; - int ret; - int i; - struct list_head *cur; - - list_for_each(cur, &fs_devices->devices) { - num_stripes++; - } - map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); - if (!map) - return -ENOMEM; - - map->ce.start = logical; - map->ce.size = length; - map->num_stripes = num_stripes; - map->sub_stripes = sub_stripes; - map->io_width = length; - map->io_align = length; - map->sector_size = length; - map->stripe_len = length; - map->type = BTRFS_BLOCK_GROUP_RAID1; - - i = 0; - list_for_each(cur, &fs_devices->devices) { - struct btrfs_device *device = list_entry(cur, - struct btrfs_device, - dev_list); - map->stripes[i].physical = logical; - map->stripes[i].dev = device; - i++; - } - ret = insert_existing_cache_extent(&map_tree->cache_tree, &map->ce); - if (ret == -EEXIST) { - struct cache_extent *old; - struct map_lookup *old_map; - old = find_cache_extent(&map_tree->cache_tree, logical, length); - old_map = container_of(old, struct map_lookup, ce); - remove_cache_extent(&map_tree->cache_tree, old); - kfree(old_map); - ret = insert_existing_cache_extent(&map_tree->cache_tree, - &map->ce); - } - BUG_ON(ret); - return 0; -} - -int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset) -{ - struct cache_extent *ce; - struct map_lookup *map; - struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree; - int readonly = 0; - int i; - - ce = find_first_cache_extent(&map_tree->cache_tree, chunk_offset); - BUG_ON(!ce); - - map = container_of(ce, struct map_lookup, ce); - for (i = 0; i < map->num_stripes; i++) { - if (!map->stripes[i].dev->writeable) { - readonly = 1; - break; - } - } - - return readonly; -} - -static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, - struct extent_buffer *leaf, - struct btrfs_chunk *chunk) -{ - struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree; - struct map_lookup *map; - struct cache_extent *ce; - u64 logical; - u64 length; - u64 devid; - u8 uuid[BTRFS_UUID_SIZE]; - int num_stripes; - int ret; - int i; - - logical = key->offset; - length = btrfs_chunk_length(leaf, chunk); - - ce = find_first_cache_extent(&map_tree->cache_tree, logical); - - /* already mapped? */ - if (ce && ce->start <= logical && ce->start + ce->size > logical) { - return 0; - } - - num_stripes = btrfs_chunk_num_stripes(leaf, chunk); - map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); - if (!map) - return -ENOMEM; - - map->ce.start = logical; - map->ce.size = length; - map->num_stripes = num_stripes; - map->io_width = btrfs_chunk_io_width(leaf, chunk); - map->io_align = btrfs_chunk_io_align(leaf, chunk); - map->sector_size = btrfs_chunk_sector_size(leaf, chunk); - map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk); - map->type = btrfs_chunk_type(leaf, chunk); - map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk); - - for (i = 0; i < num_stripes; i++) { - map->stripes[i].physical = - btrfs_stripe_offset_nr(leaf, chunk, i); - devid = btrfs_stripe_devid_nr(leaf, chunk, i); - read_extent_buffer(leaf, uuid, (unsigned long) - btrfs_stripe_dev_uuid_nr(chunk, i), - BTRFS_UUID_SIZE); - map->stripes[i].dev = btrfs_find_device(root, devid, uuid, - NULL); - if (!map->stripes[i].dev) { - kfree(map); - return -EIO; - } - - } - ret = insert_existing_cache_extent(&map_tree->cache_tree, &map->ce); - BUG_ON(ret); - - return 0; -} - -static int fill_device_from_item(struct extent_buffer *leaf, - struct btrfs_dev_item *dev_item, - struct btrfs_device *device) -{ - unsigned long ptr; - - device->devid = btrfs_device_id(leaf, dev_item); - device->total_bytes = btrfs_device_total_bytes(leaf, dev_item); - device->bytes_used = btrfs_device_bytes_used(leaf, dev_item); - device->type = btrfs_device_type(leaf, dev_item); - device->io_align = btrfs_device_io_align(leaf, dev_item); - device->io_width = btrfs_device_io_width(leaf, dev_item); - device->sector_size = btrfs_device_sector_size(leaf, dev_item); - - ptr = (unsigned long)btrfs_device_uuid(dev_item); - read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE); - - return 0; -} - -static int open_seed_devices(struct btrfs_root *root, u8 *fsid) -{ - struct btrfs_fs_devices *fs_devices; - int ret; - - fs_devices = root->fs_info->fs_devices->seed; - while (fs_devices) { - if (!memcmp(fs_devices->fsid, fsid, BTRFS_UUID_SIZE)) { - ret = 0; - goto out; - } - fs_devices = fs_devices->seed; - } - - fs_devices = find_fsid(fsid); - if (!fs_devices) { - ret = -ENOENT; - goto out; - } - - ret = btrfs_open_devices(fs_devices, O_RDONLY); - if (ret) - goto out; - - fs_devices->seed = root->fs_info->fs_devices->seed; - root->fs_info->fs_devices->seed = fs_devices; -out: - return ret; -} - -static int read_one_dev(struct btrfs_root *root, - struct extent_buffer *leaf, - struct btrfs_dev_item *dev_item) -{ - struct btrfs_device *device; - u64 devid; - int ret = 0; - u8 fs_uuid[BTRFS_UUID_SIZE]; - u8 dev_uuid[BTRFS_UUID_SIZE]; - - devid = btrfs_device_id(leaf, dev_item); - read_extent_buffer(leaf, dev_uuid, - (unsigned long)btrfs_device_uuid(dev_item), - BTRFS_UUID_SIZE); - read_extent_buffer(leaf, fs_uuid, - (unsigned long)btrfs_device_fsid(dev_item), - BTRFS_UUID_SIZE); - - if (memcmp(fs_uuid, root->fs_info->fsid, BTRFS_UUID_SIZE)) { - ret = open_seed_devices(root, fs_uuid); - if (ret) - return ret; - } - - device = btrfs_find_device(root, devid, dev_uuid, fs_uuid); - if (!device) { - printk("warning devid %llu not found already\n", - (unsigned long long)devid); - device = kmalloc(sizeof(*device), GFP_NOFS); - if (!device) - return -ENOMEM; - device->total_ios = 0; - list_add(&device->dev_list, - &root->fs_info->fs_devices->devices); - } - - fill_device_from_item(leaf, dev_item, device); - device->dev_root = root->fs_info->dev_root; - return ret; -} - -int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf) -{ - struct btrfs_dev_item *dev_item; - - dev_item = (struct btrfs_dev_item *)offsetof(struct btrfs_super_block, - dev_item); - return read_one_dev(root, buf, dev_item); -} - -int btrfs_read_sys_array(struct btrfs_root *root) -{ - struct btrfs_super_block *super_copy = &root->fs_info->super_copy; - struct extent_buffer *sb; - struct btrfs_disk_key *disk_key; - struct btrfs_chunk *chunk; - struct btrfs_key key; - u32 num_stripes; - u32 array_size; - u32 len = 0; - u8 *ptr; - unsigned long sb_ptr; - u32 cur; - int ret; - - sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET, - BTRFS_SUPER_INFO_SIZE); - if (!sb) - return -ENOMEM; - btrfs_set_buffer_uptodate(sb); - write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE); - array_size = btrfs_super_sys_array_size(super_copy); - - /* - * we do this loop twice, once for the device items and - * once for all of the chunks. This way there are device - * structs filled in for every chunk - */ - ptr = super_copy->sys_chunk_array; - sb_ptr = offsetof(struct btrfs_super_block, sys_chunk_array); - cur = 0; - - while (cur < array_size) { - disk_key = (struct btrfs_disk_key *)ptr; - btrfs_disk_key_to_cpu(&key, disk_key); - - len = sizeof(*disk_key); - ptr += len; - sb_ptr += len; - cur += len; - - if (key.type == BTRFS_CHUNK_ITEM_KEY) { - chunk = (struct btrfs_chunk *)sb_ptr; - ret = read_one_chunk(root, &key, sb, chunk); - BUG_ON(ret); - num_stripes = btrfs_chunk_num_stripes(sb, chunk); - len = btrfs_chunk_item_size(num_stripes); - } else { - BUG(); - } - ptr += len; - sb_ptr += len; - cur += len; - } - free_extent_buffer(sb); - return 0; -} - -int btrfs_read_chunk_tree(struct btrfs_root *root) -{ - struct btrfs_path *path; - struct extent_buffer *leaf; - struct btrfs_key key; - struct btrfs_key found_key; - int ret; - int slot; - - root = root->fs_info->chunk_root; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - /* first we search for all of the device items, and then we - * read in all of the chunk items. This way we can create chunk - * mappings that reference all of the devices that are afound - */ - key.objectid = BTRFS_DEV_ITEMS_OBJECTID; - key.offset = 0; - key.type = 0; -again: - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - while(1) { - leaf = path->nodes[0]; - slot = path->slots[0]; - if (slot >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, path); - if (ret == 0) - continue; - if (ret < 0) - goto error; - break; - } - btrfs_item_key_to_cpu(leaf, &found_key, slot); - if (key.objectid == BTRFS_DEV_ITEMS_OBJECTID) { - if (found_key.objectid != BTRFS_DEV_ITEMS_OBJECTID) - break; - if (found_key.type == BTRFS_DEV_ITEM_KEY) { - struct btrfs_dev_item *dev_item; - dev_item = btrfs_item_ptr(leaf, slot, - struct btrfs_dev_item); - ret = read_one_dev(root, leaf, dev_item); - BUG_ON(ret); - } - } else if (found_key.type == BTRFS_CHUNK_ITEM_KEY) { - struct btrfs_chunk *chunk; - chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk); - ret = read_one_chunk(root, &found_key, leaf, chunk); - BUG_ON(ret); - } - path->slots[0]++; - } - if (key.objectid == BTRFS_DEV_ITEMS_OBJECTID) { - key.objectid = 0; - btrfs_release_path(root, path); - goto again; - } - - btrfs_free_path(path); - ret = 0; -error: - return ret; -} - -struct list_head *btrfs_scanned_uuids(void) -{ - return &fs_uuids; -} diff --git a/volumes.h b/volumes.h deleted file mode 100644 index bb78751..0000000 --- a/volumes.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2007 Oracle. 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. - */ - -#ifndef __BTRFS_VOLUMES_ -#define __BTRFS_VOLUMES_ -struct btrfs_device { - struct list_head dev_list; - struct btrfs_root *dev_root; - struct btrfs_fs_devices *fs_devices; - - u64 total_ios; - - int fd; - - int writeable; - - char *name; - - /* these are read off the super block, only in the progs */ - char *label; - u64 total_devs; - u64 super_bytes_used; - - /* the internal btrfs device id */ - u64 devid; - - /* size of the device */ - u64 total_bytes; - - /* bytes used */ - u64 bytes_used; - - /* optimal io alignment for this device */ - u32 io_align; - - /* optimal io width for this device */ - u32 io_width; - - /* minimal io size for this device */ - u32 sector_size; - - /* type and info about this device */ - u64 type; - - /* physical drive uuid (or lvm uuid) */ - u8 uuid[BTRFS_UUID_SIZE]; -}; - -struct btrfs_fs_devices { - u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ - - /* the device with this id has the most recent coyp of the super */ - u64 latest_devid; - u64 latest_trans; - u64 lowest_devid; - int latest_bdev; - int lowest_bdev; - struct list_head devices; - struct list_head list; - - int seeding; - struct btrfs_fs_devices *seed; -}; - -struct btrfs_bio_stripe { - struct btrfs_device *dev; - u64 physical; -}; - -struct btrfs_multi_bio { - int error; - int num_stripes; - struct btrfs_bio_stripe stripes[]; -}; - -#define btrfs_multi_bio_size(n) (sizeof(struct btrfs_multi_bio) + \ - (sizeof(struct btrfs_bio_stripe) * (n))) - -int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans, - struct btrfs_device *device, - u64 chunk_tree, u64 chunk_objectid, - u64 chunk_offset, - u64 num_bytes, u64 *start); -int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, - u64 logical, u64 *length, - struct btrfs_multi_bio **multi_ret, int mirror_num); -int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, - u64 chunk_start, u64 physical, u64 devid, - u64 **logical, int *naddrs, int *stripe_len); -int btrfs_read_sys_array(struct btrfs_root *root); -int btrfs_read_chunk_tree(struct btrfs_root *root); -int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, - struct btrfs_root *extent_root, u64 *start, - u64 *num_bytes, u64 type); -int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf); -int btrfs_add_device(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_device *device); -int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, - int flags); -int btrfs_close_devices(struct btrfs_fs_devices *fs_devices); -int btrfs_add_device(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_device *device); -int btrfs_update_device(struct btrfs_trans_handle *trans, - struct btrfs_device *device); -int btrfs_scan_one_device(int fd, const char *path, - struct btrfs_fs_devices **fs_devices_ret, - u64 *total_devs, u64 super_offset); -int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len); -int btrfs_bootstrap_super_map(struct btrfs_mapping_tree *map_tree, - struct btrfs_fs_devices *fs_devices); -struct list_head *btrfs_scanned_uuids(void); -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); -#endif -- 1.7.0.rc0.48.gdace5