summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri John Ledkov <xnox@ubuntu.com>2018-02-19 15:51:31 +0000
committerDimitri John Ledkov <xnox@ubuntu.com>2018-02-19 15:52:49 +0000
commitb70cb0d0a21394d5d6b00b51f064115c2724cea8 (patch)
treef001381d2ee826e6665e003e7c6dccb084a54316
parentf1b0adb46b2c193e940f8c22b35036d2ee76c673 (diff)
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml33
-rw-r--r--Android.mk1
-rw-r--r--CHANGES21
-rw-r--r--Documentation/Makefile.in29
-rw-r--r--Documentation/btrfs-balance.8.gzbin6128 -> 6108 bytes
-rw-r--r--Documentation/btrfs-check.8.gzbin2742 -> 2726 bytes
-rw-r--r--Documentation/btrfs-convert.8.gzbin2755 -> 2746 bytes
-rw-r--r--Documentation/btrfs-device.8.gzbin4852 -> 4832 bytes
-rw-r--r--Documentation/btrfs-filesystem.8.gzbin5909 -> 5879 bytes
-rw-r--r--Documentation/btrfs-filesystem.asciidoc2
-rw-r--r--Documentation/btrfs-find-root.8.gzbin869 -> 863 bytes
-rw-r--r--Documentation/btrfs-image.8.gzbin1473 -> 1464 bytes
-rw-r--r--Documentation/btrfs-inspect-internal.8.gzbin3062 -> 3037 bytes
-rw-r--r--Documentation/btrfs-ioctl.asciidoc2
-rw-r--r--Documentation/btrfs-man5.asciidoc2
-rw-r--r--Documentation/btrfs-map-logical.8.gzbin883 -> 875 bytes
-rw-r--r--Documentation/btrfs-property.8.gzbin1527 -> 1519 bytes
-rw-r--r--Documentation/btrfs-qgroup.8.gzbin2388 -> 2377 bytes
-rw-r--r--Documentation/btrfs-quota.8.gzbin4908 -> 4891 bytes
-rw-r--r--Documentation/btrfs-receive.8.gzbin2033 -> 2026 bytes
-rw-r--r--Documentation/btrfs-replace.8.gzbin1646 -> 1634 bytes
-rw-r--r--Documentation/btrfs-replace.asciidoc2
-rw-r--r--Documentation/btrfs-rescue.8.gzbin2059 -> 2047 bytes
-rw-r--r--Documentation/btrfs-rescue.asciidoc2
-rw-r--r--Documentation/btrfs-restore.8.gzbin1991 -> 1980 bytes
-rw-r--r--Documentation/btrfs-scrub.8.gzbin2168 -> 2158 bytes
-rw-r--r--Documentation/btrfs-select-super.8.gzbin1275 -> 1267 bytes
-rw-r--r--Documentation/btrfs-send.8.gzbin1734 -> 1719 bytes
-rw-r--r--Documentation/btrfs-subvolume.8.gzbin3242 -> 3226 bytes
-rw-r--r--Documentation/btrfs.5.gzbin10993 -> 11006 bytes
-rw-r--r--Documentation/btrfs.8.gzbin2234 -> 2228 bytes
-rw-r--r--Documentation/btrfstune.8.gzbin2050 -> 2051 bytes
-rw-r--r--Documentation/fsck.btrfs.8.gzbin1123 -> 1128 bytes
-rw-r--r--Documentation/mkfs.btrfs.8.gzbin6298 -> 6542 bytes
-rw-r--r--Documentation/mkfs.btrfs.asciidoc16
-rw-r--r--Makefile26
-rw-r--r--VERSION1
-rwxr-xr-xautogen.sh1
-rw-r--r--btrfs-calc-size.c1
-rw-r--r--btrfs-list.c24
-rw-r--r--check/main.c (renamed from cmds-check.c)5569
-rw-r--r--check/mode-common.c351
-rw-r--r--check/mode-common.h100
-rw-r--r--check/mode-lowmem.c4573
-rw-r--r--check/mode-lowmem.h67
-rw-r--r--check/mode-original.h294
-rw-r--r--chunk-recover.c18
-rw-r--r--cmds-balance.c9
-rw-r--r--cmds-device.c25
-rw-r--r--cmds-fi-usage.c16
-rw-r--r--cmds-filesystem.c30
-rw-r--r--cmds-inspect-dump-super.c4
-rw-r--r--cmds-inspect-tree-stats.c4
-rw-r--r--cmds-inspect.c6
-rw-r--r--cmds-qgroup.c24
-rw-r--r--cmds-quota.c12
-rw-r--r--cmds-receive.c2
-rw-r--r--cmds-replace.c26
-rw-r--r--cmds-restore.c21
-rw-r--r--cmds-scrub.c23
-rw-r--r--cmds-subvolume.c32
-rwxr-xr-xconfigure253
-rw-r--r--configure.ac26
-rw-r--r--convert/main.c8
-rw-r--r--convert/source-ext2.c6
-rw-r--r--convert/source-fs.h1
-rw-r--r--convert/source-reiserfs.c6
-rw-r--r--ctree.c97
-rw-r--r--ctree.h57
-rw-r--r--debian/changelog6
-rw-r--r--dir-item.c3
-rw-r--r--disk-io.c28
-rw-r--r--extent-tree.c50
-rw-r--r--file-item.c2
-rw-r--r--image/main.c60
-rw-r--r--mkfs/common.c38
-rw-r--r--mkfs/main.c1000
-rw-r--r--mkfs/rootdir.c955
-rw-r--r--mkfs/rootdir.h38
-rw-r--r--print-tree.c4
-rw-r--r--qgroup.c64
-rw-r--r--quick-test.c2
-rw-r--r--send-utils.c17
-rw-r--r--super-recover.c28
-rw-r--r--tests/README.md86
-rwxr-xr-xtests/clean-tests.sh33
-rwxr-xr-xtests/cli-tests.sh34
-rwxr-xr-xtests/cli-tests/001-btrfs/test.sh2
-rwxr-xr-xtests/cli-tests/002-balance-full-no-filters/test.sh2
-rwxr-xr-xtests/cli-tests/003-fi-resize-args/test.sh2
-rwxr-xr-xtests/cli-tests/004-send-parent-multi-subvol/test.sh2
-rwxr-xr-xtests/cli-tests/005-qgroup-show/test.sh2
-rwxr-xr-xtests/cli-tests/006-qgroup-show-sync/test.sh2
-rwxr-xr-xtests/cli-tests/007-check-force/test.sh2
-rwxr-xr-xtests/cli-tests/008-subvolume-get-set-default/test.sh2
-rw-r--r--tests/common26
-rwxr-xr-xtests/convert-tests.sh34
-rwxr-xr-xtests/convert-tests/001-ext2-basic/test.sh4
-rwxr-xr-xtests/convert-tests/002-ext3-basic/test.sh4
-rwxr-xr-xtests/convert-tests/003-ext4-basic/test.sh4
-rwxr-xr-xtests/convert-tests/004-ext2-backup-superblock-ranges/test.sh2
-rwxr-xr-xtests/convert-tests/005-delete-all-rollback/test.sh4
-rwxr-xr-xtests/convert-tests/006-large-hole-extent/test.sh4
-rwxr-xr-xtests/convert-tests/007-unsupported-block-sizes/test.sh4
-rwxr-xr-xtests/convert-tests/008-readonly-image/test.sh4
-rwxr-xr-xtests/convert-tests/009-common-inode-flags/test.sh4
-rwxr-xr-xtests/convert-tests/010-reiserfs-basic/test.sh4
-rwxr-xr-xtests/convert-tests/011-reiserfs-delete-all-rollback/test.sh4
-rwxr-xr-xtests/convert-tests/012-reiserfs-large-hole-extent/test.sh4
-rwxr-xr-xtests/convert-tests/013-reiserfs-common-inode-flags/test.sh4
-rwxr-xr-xtests/convert-tests/014-reiserfs-tail-handling/test.sh4
-rwxr-xr-xtests/convert-tests/015-no-rollback-after-balance/test.sh4
-rwxr-xr-xtests/export-testsuite.sh55
-rwxr-xr-xtests/fsck-tests.sh34
-rwxr-xr-xtests/fsck-tests/006-bad-root-items/test.sh2
-rwxr-xr-xtests/fsck-tests/012-leaf-corruption/test.sh2
-rwxr-xr-xtests/fsck-tests/013-extent-tree-rebuild/test.sh4
-rwxr-xr-xtests/fsck-tests/015-tree-reloc-tree/test.sh (renamed from tests/fsck-tests/027-tree-reloc-tree/test.sh)0
-rw-r--r--tests/fsck-tests/015-tree-reloc-tree/tree_reloc_for_data_reloc.img.xz (renamed from tests/fsck-tests/027-tree-reloc-tree/tree_reloc_for_data_reloc.img.xz)bin2112 -> 2112 bytes
-rw-r--r--tests/fsck-tests/015-tree-reloc-tree/tree_reloc_for_fs_tree.img.xz (renamed from tests/fsck-tests/027-tree-reloc-tree/tree_reloc_for_fs_tree.img.xz)bin2424 -> 2424 bytes
-rwxr-xr-xtests/fsck-tests/018-leaf-crossing-stripes/test.sh2
-rwxr-xr-xtests/fsck-tests/019-non-skinny-false-alert/test.sh2
-rwxr-xr-xtests/fsck-tests/020-extent-ref-cases/test.sh2
-rwxr-xr-xtests/fsck-tests/021-partially-dropped-snapshot-case/test.sh2
-rwxr-xr-xtests/fsck-tests/022-qgroup-rescan-halfway/test.sh2
-rwxr-xr-xtests/fsck-tests/023-qgroup-stack-overflow/test.sh2
-rwxr-xr-xtests/fsck-tests/024-clear-space-cache/test.sh2
-rwxr-xr-xtests/fsck-tests/025-file-extents/test.sh2
-rwxr-xr-xtests/fsck-tests/026-bad-dir-item-name/test.sh2
-rwxr-xr-xtests/fsck-tests/028-unaligned-super-dev-sizes/test.sh17
-rw-r--r--tests/fssum.c27
-rwxr-xr-xtests/fuzz-tests.sh34
-rwxr-xr-xtests/fuzz-tests/001-simple-check-unmounted/test.sh4
-rwxr-xr-xtests/fuzz-tests/002-simple-image/test.sh4
-rwxr-xr-xtests/fuzz-tests/003-multi-check-unmounted/test.sh4
-rwxr-xr-xtests/fuzz-tests/004-simple-dump-tree/test.sh4
-rwxr-xr-xtests/fuzz-tests/005-simple-dump-super/test.sh4
-rwxr-xr-xtests/fuzz-tests/006-simple-tree-stats/test.sh4
-rwxr-xr-xtests/fuzz-tests/007-simple-super-recover/test.sh4
-rwxr-xr-xtests/fuzz-tests/008-simple-chunk-recover/test.sh4
-rwxr-xr-xtests/fuzz-tests/009-simple-zero-log/test.sh4
-rwxr-xr-xtests/misc-tests.sh36
-rwxr-xr-xtests/misc-tests/001-btrfstune-features/test.sh2
-rwxr-xr-xtests/misc-tests/002-uuid-rewrite/test.sh6
-rwxr-xr-xtests/misc-tests/003-zero-log/test.sh4
-rwxr-xr-xtests/misc-tests/004-shrink-fs/test.sh2
-rwxr-xr-xtests/misc-tests/005-convert-progress-thread-crash/test.sh2
-rwxr-xr-xtests/misc-tests/006-image-on-missing-device/test.sh2
-rwxr-xr-xtests/misc-tests/007-subvolume-sync/test.sh2
-rwxr-xr-xtests/misc-tests/008-leaf-crossing-stripes/test.sh2
-rwxr-xr-xtests/misc-tests/009-subvolume-sync-must-wait/test.sh2
-rwxr-xr-xtests/misc-tests/010-convert-delete-ext2-subvol/test.sh2
-rwxr-xr-xtests/misc-tests/011-delete-missing-device/test.sh2
-rwxr-xr-xtests/misc-tests/012-find-root-no-result/test.sh2
-rwxr-xr-xtests/misc-tests/013-subvolume-sync-crash/test.sh2
-rwxr-xr-xtests/misc-tests/014-filesystem-label/test.sh2
-rwxr-xr-xtests/misc-tests/015-dump-super-garbage/test.sh2
-rwxr-xr-xtests/misc-tests/016-send-clone-src/test.sh2
-rwxr-xr-xtests/misc-tests/017-recv-stream-malformatted/test.sh2
-rwxr-xr-xtests/misc-tests/018-recv-end-of-stream/test.sh2
-rwxr-xr-xtests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh4
-rwxr-xr-xtests/misc-tests/020-fix-superblock-corruption/test.sh2
-rwxr-xr-xtests/misc-tests/021-image-multi-devices/test.sh2
-rwxr-xr-xtests/misc-tests/022-filesystem-du-on-empty-subvol/test.sh2
-rwxr-xr-xtests/misc-tests/023-device-usage-with-missing-device/test.sh2
-rwxr-xr-xtests/misc-tests/024-inspect-internal-rootid/test.sh2
-rwxr-xr-xtests/misc-tests/025-zstd-compression/test.sh2
-rwxr-xr-xtests/misc-tests/026-image-non-printable-chars/test.sh2
-rwxr-xr-xtests/misc-tests/027-subvol-list-deleted-toplevel/test.sh2
-rwxr-xr-xtests/misc-tests/028-superblock-recover/test.sh57
-rwxr-xr-xtests/mkfs-tests.sh34
-rwxr-xr-xtests/mkfs-tests/001-basic-profiles/test.sh2
-rwxr-xr-xtests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh2
-rwxr-xr-xtests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh2
-rwxr-xr-xtests/mkfs-tests/004-rootdir-keeps-size/test.sh4
-rwxr-xr-xtests/mkfs-tests/005-long-device-name-for-ssd/test.sh2
-rwxr-xr-xtests/mkfs-tests/006-partitioned-loopdev/test.sh2
-rwxr-xr-xtests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh2
-rwxr-xr-xtests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh2
-rwxr-xr-xtests/mkfs-tests/009-special-files-for-rootdir/test.sh2
-rwxr-xr-xtests/mkfs-tests/010-minimal-size/test.sh63
-rwxr-xr-xtests/mkfs-tests/011-rootdir-create-file/test.sh15
-rwxr-xr-xtests/mkfs-tests/012-rootdir-no-shrink/test.sh39
-rwxr-xr-xtests/mkfs-tests/013-reserved-1M-for-single/test.sh44
-rw-r--r--tests/testsuite-files22
-rwxr-xr-xtravis/build-default13
-rwxr-xr-xtravis/build-dep-reiserfs15
-rwxr-xr-xtravis/build-dep-zstd14
-rw-r--r--travis/images/ci-musl-x86_64/Dockerfile10
-rwxr-xr-xtravis/images/test-build15
-rw-r--r--utils.c94
-rw-r--r--utils.h5
-rw-r--r--uuid-tree.c4
-rwxr-xr-xversion.sh37
-rw-r--r--volumes.c59
196 files changed, 8197 insertions, 7096 deletions
diff --git a/.gitignore b/.gitignore
index 8e607f6e..4abd3ee5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,6 +43,7 @@ libbtrfs.so.0.1
library-test
library-test-static
/fssum
+testsuite-id
/tests/*-tests-results.txt
/tests/test-console.txt
diff --git a/.travis.yml b/.travis.yml
index f9bd5eb9..6581fbd2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,7 +27,7 @@ cache:
ccache: true
git:
- depth: 2
+ depth: 1
dist: trusty
group: unstable
@@ -39,6 +39,9 @@ branches:
- master
- release-test
+services:
+ - docker
+
env:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
@@ -49,26 +52,10 @@ before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq e2fslibs-dev gcc libacl1-dev libblkid-dev liblzo2-dev make pkg-config udev zlib1g-dev acl attr reiserfsprogs
- echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
- - "mkdir tmp-reiser;
- cd tmp-reiser;
- wget https://www.kernel.org/pub/linux/kernel/people/jeffm/reiserfsprogs/v3.6.27/reiserfsprogs-3.6.27.tar.xz;
- tar xf reiserfsprogs-3.6.27.tar.xz;
- cd reiserfsprogs-3.6.27;
- ./configure --prefix=/usr;
- make all;
- sudo make install;
- cd ../..
- "
- - "mkdir tmp-zstd;
- cd tmp-zstd;
- wget https://github.com/facebook/zstd/archive/v1.3.1.tar.gz;
- tar xf v1.3.1.tar.gz;
- cd zstd-1.3.1;
- make;
- sudo make install PREFIX=/usr;
- cd ../..
- "
- - "./autogen.sh && ./configure --disable-documentation && make"
+ - docker pull kdave/ci-musl-x86_64
+ - travis/build-dep-reiserfs
+ - travis/build-dep-zstd
+ - travis/build-default --disable-documentation
addons:
coverity_scan:
@@ -81,9 +68,13 @@ addons:
branch_pattern: coverity_scan
script:
+ # quick build tests
+ - "if travis/should-run-test; then docker run -it kdave/ci-musl-x86_64 ./test-build $TRAVIS_BRANCH --disable-documentation --disable-backtrace; fi"
+ # real tests
- "if travis/should-run-test; then make TEST_LOG=dump test-cli; fi"
- "if travis/should-run-test; then make TEST_LOG=dump test-mkfs; fi"
- "if travis/should-run-test; then make TEST_LOG=dump test-check; fi"
- "if travis/should-run-test; then make TEST_LOG=dump TEST_ENABLE_OVERRIDE=true TEST_ARGS_CHECK=--mode=lowmem test-check; fi"
- "if travis/should-run-test; then make TEST_LOG=dump test-misc; fi"
+ # long running tests
- "if [ $TRAVIS_BRANCH = release-test ]; then make TEST_LOG=dump test-convert; fi"
diff --git a/Android.mk b/Android.mk
index 958b8bd7..de71a5d1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -38,7 +38,6 @@ libbtrfs_headers := send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-
kernel-lib/crc32c.h kernel-lib/list.h kerncompat.h \
kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
-TESTS := fsck-tests.sh convert-tests.sh
blkid_objects := partition/ superblocks/ topology/
diff --git a/CHANGES b/CHANGES
index 77c496bf..16016a33 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,24 @@
+btrfs-progs-4.15.1 (2018-02-16)
+ * build
+ * fix build on musl
+ * support asciidoctor for doc generation
+ * cleanups
+ * sync some code with kernel
+ * check: move code to own directory, split to more files
+ * tests
+ * more build tests in travis
+ * tests now pass with asan and ubsan
+ * testsuite can be exported and used separately
+
+btrfs-progs-4.15 (2018-02-01)
+ * mkfs --rootdir reworked, does not minimize the final image but can be still
+ done using a new option --shrink
+ * fix allocation of system chunk, don't allocate from the reserved area
+ * other
+ * new and updated tests
+ * cleanups, refactoring
+ * doc updates
+
btrfs-progs-4.14.1 (2018-01-05)
* dump-tree: print times of root items
* check: fix several lowmem mode bugs
diff --git a/Documentation/Makefile.in b/Documentation/Makefile.in
index bdc3dc3f..64947afb 100644
--- a/Documentation/Makefile.in
+++ b/Documentation/Makefile.in
@@ -46,8 +46,21 @@ man3dir = $(mandir)/man3
man5dir = $(mandir)/man5
man8dir = $(mandir)/man8
+ifeq (@ASCIIDOC_TOOL@,asciidoc)
ASCIIDOC = @ASCIIDOC@
-ASCIIDOC_EXTRA =
+ASCIIDOC_ARGS = -abtrfs_version=$(BTRFS_VERSION) -f asciidoc.conf
+ASCIIDOC_HTML = html
+ASCIIDOC_DOCBOOK = docbook
+ASCIIDOC_DEPS = asciidoc.conf
+endif
+ifeq (@ASCIIDOC_TOOL@,asciidoctor)
+ASCIIDOC = @ASCIIDOCTOR@
+ASCIIDOC_ARGS = -abtrfs_version=$(BTRFS_VERSION)
+ASCIIDOC_HTML = xhtml5
+ASCIIDOC_DOCBOOK = docbook45
+ASCIIDOC_DEPS =
+endif
+
MANPAGE_XSL = manpage-normal.xsl
XMLTO = @XMLTO@
XMLTO_EXTRA =
@@ -98,7 +111,7 @@ uninstall:
$(RMDIR) -p --ignore-fail-on-non-empty $(DESTDIR)$(man8dir)
clean:
- $(QUIET_RM)$(RM) -f *.xml *.xml+ *.5 *.5.gz *.8 *.8.gz *.html
+ $(QUIET_RM)$(RM) -f *.xml *.xml+ *.3 *.3.gz *.5 *.5.gz *.8 *.8.gz *.html
%.3.gz : %.3
$(QUIET_GZIP)$(GZIPCMD) -n -c $< > $@
@@ -121,16 +134,12 @@ clean:
$(QUIET_XMLTO)$(RM) -f $@ && \
$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
-%.xml : %.asciidoc asciidoc.conf
+%.xml : %.asciidoc $(ASCIIDOC_DEPS)
$(QUIET_ASCIIDOC)$(RM) -f $@+ $@ && \
- $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
- $(ASCIIDOC_EXTRA) -abtrfs_version=$(BTRFS_VERSION) \
- -o $@+ $< && \
+ $(ASCIIDOC) $(ASCIIDOC_ARGS) -b $(ASCIIDOC_DOCBOOK) -d manpage -o $@+ $< && \
$(MV) $@+ $@
-%.html : %.asciidoc asciidoc.conf
+%.html : %.asciidoc $(ASCIIDOC_DEPS)
$(QUIET_ASCIIDOC)$(RM) -f $@+ $@ && \
- $(ASCIIDOC) -b html -d article -f asciidoc.conf \
- $(ASCIIDOC_EXTRA) -abtrfs_version=$(BTRFS_VERSION) \
- -o $@+ $< && \
+ $(ASCIIDOC) $(ASCIIDOC_ARGS) -b $(ASCIIDOC_HTML) -d article -o $@+ $< && \
$(MV) $@+ $@
diff --git a/Documentation/btrfs-balance.8.gz b/Documentation/btrfs-balance.8.gz
index e2344c16..4e7ff56e 100644
--- a/Documentation/btrfs-balance.8.gz
+++ b/Documentation/btrfs-balance.8.gz
Binary files differ
diff --git a/Documentation/btrfs-check.8.gz b/Documentation/btrfs-check.8.gz
index 475ea594..fde8c5fb 100644
--- a/Documentation/btrfs-check.8.gz
+++ b/Documentation/btrfs-check.8.gz
Binary files differ
diff --git a/Documentation/btrfs-convert.8.gz b/Documentation/btrfs-convert.8.gz
index f361a835..2a283976 100644
--- a/Documentation/btrfs-convert.8.gz
+++ b/Documentation/btrfs-convert.8.gz
Binary files differ
diff --git a/Documentation/btrfs-device.8.gz b/Documentation/btrfs-device.8.gz
index 785a0046..d2f09280 100644
--- a/Documentation/btrfs-device.8.gz
+++ b/Documentation/btrfs-device.8.gz
Binary files differ
diff --git a/Documentation/btrfs-filesystem.8.gz b/Documentation/btrfs-filesystem.8.gz
index 727f2421..2955df08 100644
--- a/Documentation/btrfs-filesystem.8.gz
+++ b/Documentation/btrfs-filesystem.8.gz
Binary files differ
diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc
index 961405ba..13c2d7cc 100644
--- a/Documentation/btrfs-filesystem.asciidoc
+++ b/Documentation/btrfs-filesystem.asciidoc
@@ -361,7 +361,7 @@ specify the devid though.
*$ btrfs filesystem resize 1:max /path*
Let's assume that devid 1 exists and the filesystem does not occupy the whole
-block device, eg. it has been enlarged and we wan the grow the filesystem. By
+block device, eg. it has been enlarged and we want to grow the filesystem. By
simply using 'max' as size we will achieve that.
NOTE: There are two ways to minimize the filesystem on a given device. The
diff --git a/Documentation/btrfs-find-root.8.gz b/Documentation/btrfs-find-root.8.gz
index c90e2244..11c03171 100644
--- a/Documentation/btrfs-find-root.8.gz
+++ b/Documentation/btrfs-find-root.8.gz
Binary files differ
diff --git a/Documentation/btrfs-image.8.gz b/Documentation/btrfs-image.8.gz
index e5591ab1..be540124 100644
--- a/Documentation/btrfs-image.8.gz
+++ b/Documentation/btrfs-image.8.gz
Binary files differ
diff --git a/Documentation/btrfs-inspect-internal.8.gz b/Documentation/btrfs-inspect-internal.8.gz
index b8147ecf..318ac56d 100644
--- a/Documentation/btrfs-inspect-internal.8.gz
+++ b/Documentation/btrfs-inspect-internal.8.gz
Binary files differ
diff --git a/Documentation/btrfs-ioctl.asciidoc b/Documentation/btrfs-ioctl.asciidoc
index 11bf62b1..09c76fef 100644
--- a/Documentation/btrfs-ioctl.asciidoc
+++ b/Documentation/btrfs-ioctl.asciidoc
@@ -1,5 +1,5 @@
btrfs-ioctl(3)
-================
+==============
NAME
----
diff --git a/Documentation/btrfs-man5.asciidoc b/Documentation/btrfs-man5.asciidoc
index 1f444d73..367736ce 100644
--- a/Documentation/btrfs-man5.asciidoc
+++ b/Documentation/btrfs-man5.asciidoc
@@ -1,5 +1,5 @@
btrfs-man5(5)
-==============
+=============
NAME
----
diff --git a/Documentation/btrfs-map-logical.8.gz b/Documentation/btrfs-map-logical.8.gz
index a1ae0432..2f9a9c12 100644
--- a/Documentation/btrfs-map-logical.8.gz
+++ b/Documentation/btrfs-map-logical.8.gz
Binary files differ
diff --git a/Documentation/btrfs-property.8.gz b/Documentation/btrfs-property.8.gz
index d8ecdf79..b05b0a6d 100644
--- a/Documentation/btrfs-property.8.gz
+++ b/Documentation/btrfs-property.8.gz
Binary files differ
diff --git a/Documentation/btrfs-qgroup.8.gz b/Documentation/btrfs-qgroup.8.gz
index a493240d..f76382ef 100644
--- a/Documentation/btrfs-qgroup.8.gz
+++ b/Documentation/btrfs-qgroup.8.gz
Binary files differ
diff --git a/Documentation/btrfs-quota.8.gz b/Documentation/btrfs-quota.8.gz
index 5a94e29f..45c9b665 100644
--- a/Documentation/btrfs-quota.8.gz
+++ b/Documentation/btrfs-quota.8.gz
Binary files differ
diff --git a/Documentation/btrfs-receive.8.gz b/Documentation/btrfs-receive.8.gz
index c842f36e..f0d644e8 100644
--- a/Documentation/btrfs-receive.8.gz
+++ b/Documentation/btrfs-receive.8.gz
Binary files differ
diff --git a/Documentation/btrfs-replace.8.gz b/Documentation/btrfs-replace.8.gz
index c5d3d449..fe411c4f 100644
--- a/Documentation/btrfs-replace.8.gz
+++ b/Documentation/btrfs-replace.8.gz
Binary files differ
diff --git a/Documentation/btrfs-replace.asciidoc b/Documentation/btrfs-replace.asciidoc
index 35ecb1f8..bc73b0b7 100644
--- a/Documentation/btrfs-replace.asciidoc
+++ b/Documentation/btrfs-replace.asciidoc
@@ -1,5 +1,5 @@
btrfs-replace(8)
-===============
+================
NAME
----
diff --git a/Documentation/btrfs-rescue.8.gz b/Documentation/btrfs-rescue.8.gz
index 11ea6215..061456ac 100644
--- a/Documentation/btrfs-rescue.8.gz
+++ b/Documentation/btrfs-rescue.8.gz
Binary files differ
diff --git a/Documentation/btrfs-rescue.asciidoc b/Documentation/btrfs-rescue.asciidoc
index 743a23a6..f94a0ff2 100644
--- a/Documentation/btrfs-rescue.asciidoc
+++ b/Documentation/btrfs-rescue.asciidoc
@@ -1,5 +1,5 @@
btrfs-rescue(8)
-==============
+===============
NAME
----
diff --git a/Documentation/btrfs-restore.8.gz b/Documentation/btrfs-restore.8.gz
index c4c2b201..75181ba5 100644
--- a/Documentation/btrfs-restore.8.gz
+++ b/Documentation/btrfs-restore.8.gz
Binary files differ
diff --git a/Documentation/btrfs-scrub.8.gz b/Documentation/btrfs-scrub.8.gz
index 56b3a58f..331c1a9b 100644
--- a/Documentation/btrfs-scrub.8.gz
+++ b/Documentation/btrfs-scrub.8.gz
Binary files differ
diff --git a/Documentation/btrfs-select-super.8.gz b/Documentation/btrfs-select-super.8.gz
index e19b070f..d1bd2502 100644
--- a/Documentation/btrfs-select-super.8.gz
+++ b/Documentation/btrfs-select-super.8.gz
Binary files differ
diff --git a/Documentation/btrfs-send.8.gz b/Documentation/btrfs-send.8.gz
index e7afb09a..808a4bb2 100644
--- a/Documentation/btrfs-send.8.gz
+++ b/Documentation/btrfs-send.8.gz
Binary files differ
diff --git a/Documentation/btrfs-subvolume.8.gz b/Documentation/btrfs-subvolume.8.gz
index 9ae7d367..ba8ff190 100644
--- a/Documentation/btrfs-subvolume.8.gz
+++ b/Documentation/btrfs-subvolume.8.gz
Binary files differ
diff --git a/Documentation/btrfs.5.gz b/Documentation/btrfs.5.gz
index f0dbd468..c33db2bb 100644
--- a/Documentation/btrfs.5.gz
+++ b/Documentation/btrfs.5.gz
Binary files differ
diff --git a/Documentation/btrfs.8.gz b/Documentation/btrfs.8.gz
index 13d14727..1ad76a10 100644
--- a/Documentation/btrfs.8.gz
+++ b/Documentation/btrfs.8.gz
Binary files differ
diff --git a/Documentation/btrfstune.8.gz b/Documentation/btrfstune.8.gz
index 9de752e8..2a4babfc 100644
--- a/Documentation/btrfstune.8.gz
+++ b/Documentation/btrfstune.8.gz
Binary files differ
diff --git a/Documentation/fsck.btrfs.8.gz b/Documentation/fsck.btrfs.8.gz
index 1275946e..e47e730c 100644
--- a/Documentation/fsck.btrfs.8.gz
+++ b/Documentation/fsck.btrfs.8.gz
Binary files differ
diff --git a/Documentation/mkfs.btrfs.8.gz b/Documentation/mkfs.btrfs.8.gz
index c455e9ea..36275e8b 100644
--- a/Documentation/mkfs.btrfs.8.gz
+++ b/Documentation/mkfs.btrfs.8.gz
Binary files differ
diff --git a/Documentation/mkfs.btrfs.asciidoc b/Documentation/mkfs.btrfs.asciidoc
index f69c529d..2a1c3592 100644
--- a/Documentation/mkfs.btrfs.asciidoc
+++ b/Documentation/mkfs.btrfs.asciidoc
@@ -100,7 +100,21 @@ Please see the mount option 'discard' for that in `btrfs`(5).
*-r|--rootdir <rootdir>*::
Populate the toplevel subvolume with files from 'rootdir'. This does not
-require root permissions and does not mount the filesystem.
+require root permissions to write the new files or to mount the filesystem.
++
+NOTE: This option may enlarge the image or file to ensure it's big enough to
+contain the files from 'rootdir'. Since version 4.14.1 the filesystem size is
+not minimized. Please see option '--shrink' if you need that functionality.
+
+*--shrink*::
+Shrink the filesystem to its minimal size, only works with '--rootdir' option.
++
+If the destination is a regular file, this option will also truncate the
+file to the minimal size. Otherwise it will reduce the filesystem available
+space. Extra space will not be usable unless the filesystem is mounted and
+resized using 'btrfs filesystem resize'.
++
+NOTE: prior to version 4.14.1, the shrinking was done automatically.
*-O|--features <feature1>[,<feature2>...]*::
A list of filesystem features turned on at mkfs time. Not all features are
diff --git a/Makefile b/Makefile
index 6369e8f4..92cfe7b5 100644
--- a/Makefile
+++ b/Makefile
@@ -109,11 +109,11 @@ objects = ctree.o disk-io.o kernel-lib/radix-tree.o extent-tree.o print-tree.o \
fsfeatures.o kernel-lib/tables.o kernel-lib/raid56.o transaction.o
cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
- cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-check.o \
+ cmds-quota.o cmds-qgroup.o cmds-replace.o check/main.o \
cmds-restore.o cmds-rescue.o chunk-recover.o super-recover.o \
cmds-property.o cmds-fi-usage.o cmds-inspect-dump-tree.o \
cmds-inspect-dump-super.o cmds-inspect-tree-stats.o cmds-fi-du.o \
- mkfs/common.o
+ mkfs/common.o check/mode-common.o check/mode-lowmem.o
libbtrfs_objects = send-stream.o send-utils.o kernel-lib/rbtree.o btrfs-list.o \
kernel-lib/crc32c.o messages.o \
uuid-tree.o utils-lib.o rbtree-utils.o
@@ -123,13 +123,11 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l
extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
convert_objects = convert/main.o convert/common.o convert/source-fs.o \
convert/source-ext2.o convert/source-reiserfs.o
-mkfs_objects = mkfs/main.o mkfs/common.o
+mkfs_objects = mkfs/main.o mkfs/common.o mkfs/rootdir.o
image_objects = image/main.o image/sanitize.o
all_objects = $(objects) $(cmds_objects) $(libbtrfs_objects) $(convert_objects) \
$(mkfs_objects) $(image_objects)
-TESTS = fsck-tests.sh convert-tests.sh
-
udev_rules = 64-btrfs-dm.rules
ifeq ("$(origin V)", "command line")
@@ -220,7 +218,7 @@ cmds_restore_cflags = -DBTRFSRESTORE_ZSTD=$(BTRFSRESTORE_ZSTD)
CHECKER_FLAGS += $(btrfs_convert_cflags)
# collect values of the variables above
-standalone_deps = $(foreach dep,$(patsubst %,%_objects,$(subst -,_,$(filter btrfs-%, $(progs)))),$($(dep)))
+standalone_deps = $(foreach dep,$(patsubst %,%_objects,$(subst -,_,$(filter btrfs-%, $(progs) $(progs_extra)))),$($(dep)))
SUBDIRS =
BUILDDIRS = $(patsubst %,build-%,$(SUBDIRS))
@@ -305,7 +303,7 @@ test-fsck: btrfs btrfs-image btrfs-corrupt-block mkfs.btrfs btrfstune
$(Q)bash tests/fsck-tests.sh
test-misc: btrfs btrfs-image btrfs-corrupt-block mkfs.btrfs btrfstune fssum \
- btrfs-zero-log btrfs-find-root btrfs-select-super
+ btrfs-zero-log btrfs-find-root btrfs-select-super btrfs-convert
@echo " [TEST] misc-tests.sh"
$(Q)bash tests/misc-tests.sh
@@ -313,11 +311,11 @@ test-mkfs: btrfs mkfs.btrfs
@echo " [TEST] mkfs-tests.sh"
$(Q)bash tests/mkfs-tests.sh
-test-fuzz: btrfs
+test-fuzz: btrfs btrfs-image
@echo " [TEST] fuzz-tests.sh"
$(Q)bash tests/fuzz-tests.sh
-test-cli: btrfs
+test-cli: btrfs mkfs.btrfs
@echo " [TEST] cli-tests.sh"
$(Q)bash tests/cli-tests.sh
@@ -331,7 +329,11 @@ test-inst: all
$(MAKE) $(MAKEOPTS) DESTDIR=$$tmpdest install && \
$(RM) -rf -- $$tmpdest
-test: test-fsck test-mkfs test-convert test-misc test-fuzz test-cli
+test: test-fsck test-mkfs test-misc test-cli test-convert test-fuzz
+
+testsuite: btrfs-corrupt-block fssum
+ @echo "Export tests as a package"
+ $(Q)cd tests && ./export-testsuite.sh
#
# NOTE: For static compiles, you need to have all the required libs
@@ -339,7 +341,7 @@ test: test-fsck test-mkfs test-convert test-misc test-fuzz test-cli
#
static: $(progs_static)
-version.h: version.sh version.h.in configure.ac
+version.h: version.h.in configure.ac
@echo " [SH] $@"
$(Q)bash ./config.status --silent $@
@@ -544,7 +546,7 @@ clean: $(CLEANDIRS)
kernel-shared/*.o kernel-shared/*.o.d \
image/*.o image/*.o.d \
convert/*.o convert/*.o.d \
- mkfs/*.o mkfs/*.o.d \
+ mkfs/*.o mkfs/*.o.d check/*.o check/*.o.d \
dir-test ioctl-test quick-test library-test library-test-static \
mktables btrfs.static mkfs.btrfs.static fssum \
$(check_defs) \
diff --git a/VERSION b/VERSION
new file mode 100644
index 00000000..1597f02b
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+v4.15.1
diff --git a/autogen.sh b/autogen.sh
index 80442c5b..342941bf 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -61,7 +61,6 @@ echo " autoconf: $(autoconf --version | head -1)"
echo " autoheader: $(autoheader --version | head -1)"
echo " automake: $(automake --version | head -1)"
-chmod +x version.sh
rm -rf autom4te.cache
aclocal -I m4 $AL_OPTS &&
diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
index 1ac7c785..d2d68ab2 100644
--- a/btrfs-calc-size.c
+++ b/btrfs-calc-size.c
@@ -19,6 +19,7 @@
#include "volumes.h"
#include "utils.h"
#include "commands.h"
+#include "help.h"
int main(int argc, char **argv)
{
diff --git a/btrfs-list.c b/btrfs-list.c
index b6d76585..e01c5899 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -644,8 +644,8 @@ static int lookup_ino_path(int fd, struct root_info *ri)
ri->ref_tree = 0;
return -ENOENT;
}
- error("failed to lookup path for root %llu: %s",
- (unsigned long long)ri->ref_tree, strerror(errno));
+ error("failed to lookup path for root %llu: %m",
+ (unsigned long long)ri->ref_tree);
return ret;
}
@@ -695,9 +695,8 @@ static u64 find_root_gen(int fd)
/* this ioctl fills in ino_args->treeid */
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args);
if (ret < 0) {
- error("failed to lookup path for dirid %llu: %s",
- (unsigned long long)BTRFS_FIRST_FREE_OBJECTID,
- strerror(errno));
+ error("failed to lookup path for dirid %llu: %m",
+ (unsigned long long)BTRFS_FIRST_FREE_OBJECTID);
return 0;
}
@@ -721,7 +720,7 @@ static u64 find_root_gen(int fd)
while (1) {
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0) {
- error("can't perform the search: %s", strerror(errno));
+ error("can't perform the search: %m");
return 0;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -781,8 +780,8 @@ static char *__ino_resolve(int fd, u64 dirid)
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
if (ret < 0) {
- error("failed to lookup path for dirid %llu: %s",
- (unsigned long long)dirid, strerror(errno));
+ error("failed to lookup path for dirid %llu: %m",
+ (unsigned long long)dirid);
return ERR_PTR(ret);
}
@@ -860,7 +859,7 @@ static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name)
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0) {
- error("can't perform the search: %s", strerror(errno));
+ error("can't perform the search: %m");
return NULL;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -1496,7 +1495,7 @@ static int btrfs_list_subvols(int fd, struct root_lookup *root_lookup)
ret = list_subvol_search(fd, root_lookup);
if (ret) {
- error("can't perform the search: %s", strerror(errno));
+ error("can't perform the search: %m");
return ret;
}
@@ -1732,7 +1731,7 @@ int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
while(1) {
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0) {
- error("can't perform the search: %s", strerror(errno));
+ error("can't perform the search: %m");
break;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -1926,8 +1925,7 @@ int btrfs_list_get_path_rootid(int fd, u64 *treeid)
ret = lookup_path_rootid(fd, treeid);
if (ret < 0)
- error("cannot resolve rootid for path: %s",
- strerror(errno));
+ error("cannot resolve rootid for path: %m");
return ret;
}
diff --git a/cmds-check.c b/check/main.c
index 7fc30da8..97baae58 100644
--- a/cmds-check.c
+++ b/check/main.c
@@ -43,6 +43,9 @@
#include "kernel-shared/ulist.h"
#include "hash.h"
#include "help.h"
+#include "check/mode-common.h"
+#include "check/mode-original.h"
+#include "check/mode-lowmem.h"
enum task_position {
TASK_EXTENTS,
@@ -58,22 +61,22 @@ struct task_ctx {
struct task_info *info;
};
-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 total_extent_tree_bytes = 0;
-static u64 btree_space_waste = 0;
-static u64 data_bytes_allocated = 0;
-static u64 data_bytes_referenced = 0;
-static LIST_HEAD(duplicate_extents);
-static LIST_HEAD(delete_items);
-static int no_holes = 0;
-static int init_extent_tree = 0;
-static int check_data_csum = 0;
-static struct btrfs_fs_info *global_info;
-static struct task_ctx ctx = { 0 };
-static struct cache_tree *roots_info_cache = NULL;
+u64 bytes_used = 0;
+u64 total_csum_bytes = 0;
+u64 total_btree_bytes = 0;
+u64 total_fs_tree_bytes = 0;
+u64 total_extent_tree_bytes = 0;
+u64 btree_space_waste = 0;
+u64 data_bytes_allocated = 0;
+u64 data_bytes_referenced = 0;
+LIST_HEAD(duplicate_extents);
+LIST_HEAD(delete_items);
+int no_holes = 0;
+int init_extent_tree = 0;
+int check_data_csum = 0;
+struct btrfs_fs_info *global_info;
+struct task_ctx ctx = { 0 };
+struct cache_tree *roots_info_cache = NULL;
enum btrfs_check_mode {
CHECK_MODE_ORIGINAL,
@@ -84,62 +87,6 @@ enum btrfs_check_mode {
static enum btrfs_check_mode check_mode = CHECK_MODE_DEFAULT;
-struct extent_backref {
- struct rb_node node;
- unsigned int is_data:1;
- unsigned int found_extent_tree:1;
- unsigned int full_backref:1;
- unsigned int found_ref:1;
- unsigned int broken:1;
-};
-
-static inline struct extent_backref* rb_node_to_extent_backref(struct rb_node *node)
-{
- return rb_entry(node, struct extent_backref, node);
-}
-
-struct data_backref {
- struct extent_backref node;
- union {
- u64 parent;
- u64 root;
- };
- u64 owner;
- u64 offset;
- u64 disk_bytenr;
- u64 bytes;
- u64 ram_bytes;
- u32 num_refs;
- u32 found_ref;
-};
-
-#define ROOT_DIR_ERROR (1<<1) /* bad ROOT_DIR */
-#define DIR_ITEM_MISSING (1<<2) /* DIR_ITEM not found */
-#define DIR_ITEM_MISMATCH (1<<3) /* DIR_ITEM found but not match */
-#define INODE_REF_MISSING (1<<4) /* INODE_REF/INODE_EXTREF not found */
-#define INODE_ITEM_MISSING (1<<5) /* INODE_ITEM not found */
-#define INODE_ITEM_MISMATCH (1<<6) /* INODE_ITEM found but not match */
-#define FILE_EXTENT_ERROR (1<<7) /* bad FILE_EXTENT */
-#define ODD_CSUM_ITEM (1<<8) /* CSUM_ITEM error */
-#define CSUM_ITEM_MISSING (1<<9) /* CSUM_ITEM not found */
-#define LINK_COUNT_ERROR (1<<10) /* INODE_ITEM nlink count error */
-#define NBYTES_ERROR (1<<11) /* INODE_ITEM nbytes count error */
-#define ISIZE_ERROR (1<<12) /* INODE_ITEM size count error */
-#define ORPHAN_ITEM (1<<13) /* INODE_ITEM no reference */
-#define NO_INODE_ITEM (1<<14) /* no inode_item */
-#define LAST_ITEM (1<<15) /* Complete this tree traversal */
-#define ROOT_REF_MISSING (1<<16) /* ROOT_REF not found */
-#define ROOT_REF_MISMATCH (1<<17) /* ROOT_REF found but not match */
-#define DIR_INDEX_MISSING (1<<18) /* INODE_INDEX not found */
-#define DIR_INDEX_MISMATCH (1<<19) /* INODE_INDEX found but not match */
-#define DIR_COUNT_AGAIN (1<<20) /* DIR isize should be recalculated */
-#define BG_ACCOUNTING_ERROR (1<<21) /* Block group accounting error */
-
-static inline struct data_backref* to_data_backref(struct extent_backref *back)
-{
- return container_of(back, struct data_backref, node);
-}
-
static int compare_data_backref(struct rb_node *node1, struct rb_node *node2)
{
struct extent_backref *ext1 = rb_node_to_extent_backref(node1);
@@ -185,34 +132,6 @@ static int compare_data_backref(struct rb_node *node1, struct rb_node *node2)
return 0;
}
-/*
- * Much like data_backref, just removed the undetermined members
- * and change it to use list_head.
- * During extent scan, it is stored in root->orphan_data_extent.
- * During fs tree scan, it is then moved to inode_rec->orphan_data_extents.
- */
-struct orphan_data_extent {
- struct list_head list;
- u64 root;
- u64 objectid;
- u64 offset;
- u64 disk_bytenr;
- u64 disk_len;
-};
-
-struct tree_backref {
- struct extent_backref node;
- union {
- u64 parent;
- u64 root;
- };
-};
-
-static inline struct tree_backref* to_tree_backref(struct extent_backref *back)
-{
- return container_of(back, struct tree_backref, node);
-}
-
static int compare_tree_backref(struct rb_node *node1, struct rb_node *node2)
{
struct extent_backref *ext1 = rb_node_to_extent_backref(node1);
@@ -254,228 +173,6 @@ static int compare_extent_backref(struct rb_node *node1, struct rb_node *node2)
return compare_tree_backref(node1, node2);
}
-/* Explicit initialization for extent_record::flag_block_full_backref */
-enum { FLAG_UNSET = 2 };
-
-struct extent_record {
- struct list_head backrefs;
- struct list_head dups;
- struct rb_root backref_tree;
- struct list_head list;
- struct cache_extent cache;
- struct btrfs_disk_key parent_key;
- u64 start;
- u64 max_size;
- u64 nr;
- u64 refs;
- u64 extent_item_refs;
- u64 generation;
- u64 parent_generation;
- u64 info_objectid;
- u32 num_duplicates;
- u8 info_level;
- unsigned int flag_block_full_backref:2;
- unsigned int found_rec:1;
- unsigned int content_checked:1;
- unsigned int owner_ref_checked:1;
- unsigned int is_root:1;
- unsigned int metadata:1;
- unsigned int bad_full_backref:1;
- unsigned int crossing_stripes:1;
- unsigned int wrong_chunk_type:1;
-};
-
-static inline struct extent_record* to_extent_record(struct list_head *entry)
-{
- return container_of(entry, struct extent_record, list);
-}
-
-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;
- u8 filetype;
- u8 ref_type;
- int errors;
- u64 dir;
- u64 index;
- u16 namelen;
- char name[0];
-};
-
-static inline struct inode_backref* to_inode_backref(struct list_head *entry)
-{
- return list_entry(entry, struct inode_backref, list);
-}
-
-struct root_item_record {
- struct list_head list;
- u64 objectid;
- u64 bytenr;
- u64 last_snapshot;
- u8 level;
- u8 drop_level;
- struct btrfs_key drop_key;
-};
-
-#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 file_extent_hole {
- struct rb_node node;
- u64 start;
- u64 len;
-};
-
-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;
- struct rb_root holes;
- struct list_head orphan_extents;
-
- 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)
-#define I_ERR_FILE_EXTENT_ORPHAN (1 << 14)
-
-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];
-};
-
-static inline struct root_backref* to_root_backref(struct list_head *entry)
-{
- return list_entry(entry, struct root_backref, list);
-}
-
-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;
-};
-
-struct bad_item {
- struct btrfs_key key;
- u64 root_id;
- struct list_head list;
-};
-
-struct extent_entry {
- u64 bytenr;
- u64 bytes;
- int count;
- int broken;
- struct list_head list;
-};
-
-struct root_item_info {
- /* level of the root */
- u8 level;
- /* number of nodes at this level, must be 1 for a root */
- int node_count;
- u64 bytenr;
- u64 gen;
- struct cache_extent cache_extent;
-};
-
-/*
- * Error bit for low memory mode check.
- *
- * Currently no caller cares about it yet. Just internal use for error
- * classification.
- */
-#define BACKREF_MISSING (1 << 0) /* Backref missing in extent tree */
-#define BACKREF_MISMATCH (1 << 1) /* Backref exists but does not match */
-#define BYTES_UNALIGNED (1 << 2) /* Some bytes are not aligned */
-#define REFERENCER_MISSING (1 << 3) /* Referencer not found */
-#define REFERENCER_MISMATCH (1 << 4) /* Referenceer found but does not match */
-#define CROSSING_STRIPE_BOUNDARY (1 << 4) /* For kernel scrub workaround */
-#define ITEM_SIZE_MISMATCH (1 << 5) /* Bad item size */
-#define UNKNOWN_TYPE (1 << 6) /* Unknown type */
-#define ACCOUNTING_MISMATCH (1 << 7) /* Used space accounting error */
-#define CHUNK_TYPE_MISMATCH (1 << 8)
static void *print_status_check(void *p)
{
@@ -715,8 +412,6 @@ static void free_file_extent_holes(struct rb_root *holes)
}
}
-static void reset_cached_block_groups(struct btrfs_fs_info *fs_info);
-
static void record_root_in_trans(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
@@ -728,23 +423,6 @@ static void record_root_in_trans(struct btrfs_trans_handle *trans,
}
}
-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 int device_record_compare(struct rb_node *node1, struct rb_node *node2)
{
struct device_record *rec1;
@@ -1635,8 +1313,9 @@ static int process_dir_item(struct extent_buffer *eb,
namebuf, len, filetype,
key->type, error);
} else {
- fprintf(stderr, "invalid location in dir item %u\n",
- location.type);
+ fprintf(stderr,
+ "unknown location type %d in DIR_ITEM[%llu %llu]\n",
+ location.type, key->objectid, key->offset);
add_inode_backref(inode_cache, BTRFS_MULTIPLE_OBJECTIDS,
key->objectid, key->offset, namebuf,
len, filetype, key->type, error);
@@ -1743,78 +1422,6 @@ static int process_inode_extref(struct extent_buffer *eb,
}
-static int count_csum_range(struct btrfs_root *root, u64 start,
- u64 len, u64 *found)
-{
- struct btrfs_key key;
- struct btrfs_path path;
- struct extent_buffer *leaf;
- int ret;
- size_t size;
- *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);
- if (ret < 0)
- goto out;
- 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);
- if (ret > 0)
- break;
- else if (ret < 0)
- goto out;
- 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->fs_info->sectorsize;
- if (csum_end > start) {
- size = min(csum_end - start, len);
- len -= size;
- start += size;
- *found += size;
- }
-
- path.slots[0]++;
- }
-out:
- btrfs_release_path(&path);
- if (ret < 0)
- return ret;
- return 0;
-}
-
static int process_file_extent(struct btrfs_root *root,
struct extent_buffer *eb,
int slot, struct btrfs_key *key,
@@ -1894,7 +1501,8 @@ static int process_file_extent(struct btrfs_root *root,
else
disk_bytenr += extent_offset;
- ret = count_csum_range(root, disk_bytenr, num_bytes, &found);
+ ret = count_csum_range(root->fs_info, disk_bytenr, num_bytes,
+ &found);
if (ret < 0)
return ret;
if (extent_type == BTRFS_FILE_EXTENT_REG) {
@@ -1971,403 +1579,6 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
return ret;
}
-struct node_refs {
- u64 bytenr[BTRFS_MAX_LEVEL];
- u64 refs[BTRFS_MAX_LEVEL];
- int need_check[BTRFS_MAX_LEVEL];
- /* field for checking all trees */
- int checked[BTRFS_MAX_LEVEL];
- /* the corresponding extent should be marked as full backref or not */
- int full_backref[BTRFS_MAX_LEVEL];
-};
-
-static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
- struct extent_buffer *eb, struct node_refs *nrefs,
- u64 level, int check_all);
-static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
- unsigned int ext_ref);
-
-/*
- * Returns >0 Found error, not fatal, should continue
- * Returns <0 Fatal error, must exit the whole check
- * Returns 0 No errors found
- */
-static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path,
- struct node_refs *nrefs, int *level, int ext_ref)
-{
- struct extent_buffer *cur = path->nodes[0];
- struct btrfs_key key;
- u64 cur_bytenr;
- u32 nritems;
- u64 first_ino = 0;
- int root_level = btrfs_header_level(root->node);
- int i;
- int ret = 0; /* Final return value */
- int err = 0; /* Positive error bitmap */
-
- cur_bytenr = cur->start;
-
- /* skip to first inode item or the first inode number change */
- nritems = btrfs_header_nritems(cur);
- for (i = 0; i < nritems; i++) {
- btrfs_item_key_to_cpu(cur, &key, i);
- if (i == 0)
- first_ino = key.objectid;
- if (key.type == BTRFS_INODE_ITEM_KEY ||
- (first_ino && first_ino != key.objectid))
- break;
- }
- if (i == nritems) {
- path->slots[0] = nritems;
- return 0;
- }
- path->slots[0] = i;
-
-again:
- err |= check_inode_item(root, path, ext_ref);
-
- /* modify cur since check_inode_item may change path */
- cur = path->nodes[0];
-
- if (err & LAST_ITEM)
- goto out;
-
- /* still have inode items in thie leaf */
- if (cur->start == cur_bytenr)
- goto again;
-
- /*
- * we have switched to another leaf, above nodes may
- * have changed, here walk down the path, if a node
- * or leaf is shared, check whether we can skip this
- * node or leaf.
- */
- for (i = root_level; i >= 0; i--) {
- if (path->nodes[i]->start == nrefs->bytenr[i])
- continue;
-
- ret = update_nodes_refs(root, path->nodes[i]->start,
- path->nodes[i], nrefs, i, 0);
- if (ret)
- goto out;
-
- if (!nrefs->need_check[i]) {
- *level += 1;
- break;
- }
- }
-
- for (i = 0; i < *level; i++) {
- free_extent_buffer(path->nodes[i]);
- path->nodes[i] = NULL;
- }
-out:
- err &= ~LAST_ITEM;
- if (err && !ret)
- ret = err;
- return ret;
-}
-
-static void reada_walk_down(struct btrfs_root *root,
- struct extent_buffer *node, int slot)
-{
- struct btrfs_fs_info *fs_info = root->fs_info;
- u64 bytenr;
- u64 ptr_gen;
- u32 nritems;
- int i;
- int level;
-
- level = btrfs_header_level(node);
- if (level != 1)
- return;
-
- nritems = btrfs_header_nritems(node);
- for (i = slot; i < nritems; i++) {
- bytenr = btrfs_node_blockptr(node, i);
- ptr_gen = btrfs_node_ptr_generation(node, i);
- readahead_tree_block(fs_info, bytenr, ptr_gen);
- }
-}
-
-/*
- * Check the child node/leaf by the following condition:
- * 1. the first item key of the node/leaf should be the same with the one
- * in parent.
- * 2. block in parent node should match the child node/leaf.
- * 3. generation of parent node and child's header should be consistent.
- *
- * Or the child node/leaf pointed by the key in parent is not valid.
- *
- * We hope to check leaf owner too, but since subvol may share leaves,
- * which makes leaf owner check not so strong, key check should be
- * sufficient enough for that case.
- */
-static int check_child_node(struct extent_buffer *parent, int slot,
- struct extent_buffer *child)
-{
- struct btrfs_key parent_key;
- struct btrfs_key child_key;
- int ret = 0;
-
- btrfs_node_key_to_cpu(parent, &parent_key, slot);
- if (btrfs_header_level(child) == 0)
- btrfs_item_key_to_cpu(child, &child_key, 0);
- else
- btrfs_node_key_to_cpu(child, &child_key, 0);
-
- if (memcmp(&parent_key, &child_key, sizeof(parent_key))) {
- ret = -EINVAL;
- fprintf(stderr,
- "Wrong key of child node/leaf, wanted: (%llu, %u, %llu), have: (%llu, %u, %llu)\n",
- parent_key.objectid, parent_key.type, parent_key.offset,
- child_key.objectid, child_key.type, child_key.offset);
- }
- if (btrfs_header_bytenr(child) != btrfs_node_blockptr(parent, slot)) {
- ret = -EINVAL;
- fprintf(stderr, "Wrong block of child node/leaf, wanted: %llu, have: %llu\n",
- btrfs_node_blockptr(parent, slot),
- btrfs_header_bytenr(child));
- }
- if (btrfs_node_ptr_generation(parent, slot) !=
- btrfs_header_generation(child)) {
- ret = -EINVAL;
- fprintf(stderr, "Wrong generation of child node/leaf, wanted: %llu, have: %llu\n",
- btrfs_header_generation(child),
- btrfs_node_ptr_generation(parent, slot));
- }
- return ret;
-}
-
-/*
- * for a tree node or leaf, if it's shared, indeed we don't need to iterate it
- * in every fs or file tree check. Here we find its all root ids, and only check
- * it in the fs or file tree which has the smallest root id.
- */
-static int need_check(struct btrfs_root *root, struct ulist *roots)
-{
- struct rb_node *node;
- struct ulist_node *u;
-
- /*
- * @roots can be empty if it belongs to tree reloc tree
- * In that case, we should always check the leaf, as we can't use
- * the tree owner to ensure some other root will check it.
- */
- if (roots->nnodes == 1 || roots->nnodes == 0)
- return 1;
-
- node = rb_first(&roots->root);
- u = rb_entry(node, struct ulist_node, rb_node);
- /*
- * current root id is not smallest, we skip it and let it be checked
- * in the fs or file tree who hash the smallest root id.
- */
- if (root->objectid != u->val)
- return 0;
-
- return 1;
-}
-
-static int calc_extent_flag_v2(struct btrfs_root *root, struct extent_buffer *eb,
- u64 *flags_ret)
-{
- struct btrfs_root *extent_root = root->fs_info->extent_root;
- struct btrfs_root_item *ri = &root->root_item;
- struct btrfs_extent_inline_ref *iref;
- struct btrfs_extent_item *ei;
- struct btrfs_key key;
- struct btrfs_path *path = NULL;
- unsigned long ptr;
- unsigned long end;
- u64 flags;
- u64 owner = 0;
- u64 offset;
- int slot;
- int type;
- int ret = 0;
-
- /*
- * Except file/reloc tree, we can not have FULL BACKREF MODE
- */
- if (root->objectid < BTRFS_FIRST_FREE_OBJECTID)
- goto normal;
-
- /* root node */
- if (eb->start == btrfs_root_bytenr(ri))
- goto normal;
-
- if (btrfs_header_flag(eb, BTRFS_HEADER_FLAG_RELOC))
- goto full_backref;
-
- owner = btrfs_header_owner(eb);
- if (owner == root->objectid)
- goto normal;
-
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
- key.objectid = btrfs_header_bytenr(eb);
- key.type = (u8)-1;
- key.offset = (u64)-1;
-
- ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
- if (ret <= 0) {
- ret = -EIO;
- goto out;
- }
-
- if (ret > 0) {
- ret = btrfs_previous_extent_item(extent_root, path,
- key.objectid);
- if (ret)
- goto full_backref;
-
- }
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
-
- eb = path->nodes[0];
- slot = path->slots[0];
- ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
-
- flags = btrfs_extent_flags(eb, ei);
- if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
- goto full_backref;
-
- ptr = (unsigned long)(ei + 1);
- end = (unsigned long)ei + btrfs_item_size_nr(eb, slot);
-
- if (key.type == BTRFS_EXTENT_ITEM_KEY)
- ptr += sizeof(struct btrfs_tree_block_info);
-
-next:
- /* Reached extent item ends normally */
- if (ptr == end)
- goto full_backref;
-
- /* Beyond extent item end, wrong item size */
- if (ptr > end) {
- error("extent item at bytenr %llu slot %d has wrong size",
- eb->start, slot);
- goto full_backref;
- }
-
- iref = (struct btrfs_extent_inline_ref *)ptr;
- offset = btrfs_extent_inline_ref_offset(eb, iref);
- type = btrfs_extent_inline_ref_type(eb, iref);
-
- if (type == BTRFS_TREE_BLOCK_REF_KEY && offset == owner)
- goto normal;
- ptr += btrfs_extent_inline_ref_size(type);
- goto next;
-
-normal:
- *flags_ret &= ~BTRFS_BLOCK_FLAG_FULL_BACKREF;
- goto out;
-
-full_backref:
- *flags_ret |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
-out:
- btrfs_free_path(path);
- return ret;
-}
-
-/*
- * for a tree node or leaf, we record its reference count, so later if we still
- * process this node or leaf, don't need to compute its reference count again.
- *
- * @bytenr if @bytenr == (u64)-1, only update nrefs->full_backref[level]
- */
-static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
- struct extent_buffer *eb, struct node_refs *nrefs,
- u64 level, int check_all)
-{
- struct ulist *roots;
- u64 refs = 0;
- u64 flags = 0;
- int root_level = btrfs_header_level(root->node);
- int check;
- int ret;
-
- if (nrefs->bytenr[level] == bytenr)
- return 0;
-
- if (bytenr != (u64)-1) {
- /* the return value of this function seems a mistake */
- ret = btrfs_lookup_extent_info(NULL, root, bytenr,
- level, 1, &refs, &flags);
- /* temporary fix */
- if (ret < 0 && !check_all)
- return ret;
-
- nrefs->bytenr[level] = bytenr;
- nrefs->refs[level] = refs;
- nrefs->full_backref[level] = 0;
- nrefs->checked[level] = 0;
-
- if (refs > 1) {
- ret = btrfs_find_all_roots(NULL, root->fs_info, bytenr,
- 0, &roots);
- if (ret)
- return -EIO;
-
- check = need_check(root, roots);
- ulist_free(roots);
- nrefs->need_check[level] = check;
- } else {
- if (!check_all) {
- nrefs->need_check[level] = 1;
- } else {
- if (level == root_level) {
- nrefs->need_check[level] = 1;
- } else {
- /*
- * The node refs may have not been
- * updated if upper needs checking (the
- * lowest root_objectid) the node can
- * be checked.
- */
- nrefs->need_check[level] =
- nrefs->need_check[level + 1];
- }
- }
- }
- }
-
- if (check_all && eb) {
- calc_extent_flag_v2(root, eb, &flags);
- if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
- nrefs->full_backref[level] = 1;
- }
-
- return 0;
-}
-
-/*
- * @level if @level == -1 means extent data item
- * else normal treeblocl.
- */
-static int should_check_extent_strictly(struct btrfs_root *root,
- struct node_refs *nrefs, int level)
-{
- int root_level = btrfs_header_level(root->node);
-
- if (level > root_level || level < -1)
- return 1;
- if (level == root_level)
- return 1;
- /*
- * if the upper node is marked full backref, it should contain shared
- * backref of the parent (except owner == root->objectid).
- */
- while (++level <= root_level)
- if (nrefs->refs[level] > 1)
- return 0;
-
- return 1;
-}
-
static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
struct walk_control *wc, int *level,
struct node_refs *nrefs)
@@ -2497,344 +1708,6 @@ out:
return err;
}
-static int fs_root_objectid(u64 objectid);
-
-/*
- * Update global fs information.
- */
-static void account_bytes(struct btrfs_root *root, struct btrfs_path *path,
- int level)
-{
- u32 free_nrs;
- struct extent_buffer *eb = path->nodes[level];
-
- total_btree_bytes += eb->len;
- if (fs_root_objectid(root->objectid))
- total_fs_tree_bytes += eb->len;
- if (btrfs_header_owner(eb) == BTRFS_EXTENT_TREE_OBJECTID)
- total_extent_tree_bytes += eb->len;
-
- if (level == 0) {
- btree_space_waste += btrfs_leaf_free_space(root, eb);
- } else {
- free_nrs = (BTRFS_NODEPTRS_PER_BLOCK(root) -
- btrfs_header_nritems(eb));
- btree_space_waste += free_nrs * sizeof(struct btrfs_key_ptr);
- }
-}
-
-/*
- * This function only handles BACKREF_MISSING,
- * If corresponding extent item exists, increase the ref, else insert an extent
- * item and backref.
- *
- * Returns error bits after repair.
- */
-static int repair_tree_block_ref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct extent_buffer *node,
- struct node_refs *nrefs, int level, int err)
-{
- struct btrfs_fs_info *fs_info = root->fs_info;
- struct btrfs_root *extent_root = fs_info->extent_root;
- struct btrfs_path path;
- struct btrfs_extent_item *ei;
- struct btrfs_tree_block_info *bi;
- struct btrfs_key key;
- struct extent_buffer *eb;
- u32 size = sizeof(*ei);
- u32 node_size = root->fs_info->nodesize;
- int insert_extent = 0;
- int skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
- int root_level = btrfs_header_level(root->node);
- int generation;
- int ret;
- u64 owner;
- u64 bytenr;
- u64 flags = BTRFS_EXTENT_FLAG_TREE_BLOCK;
- u64 parent = 0;
-
- if ((err & BACKREF_MISSING) == 0)
- return err;
-
- WARN_ON(level > BTRFS_MAX_LEVEL);
- WARN_ON(level < 0);
-
- btrfs_init_path(&path);
- bytenr = btrfs_header_bytenr(node);
- owner = btrfs_header_owner(node);
- generation = btrfs_header_generation(node);
-
- key.objectid = bytenr;
- key.type = (u8)-1;
- key.offset = (u64)-1;
-
- /* Search for the extent item */
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (ret <= 0) {
- ret = -EIO;
- goto out;
- }
-
- ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
- if (ret)
- insert_extent = 1;
-
- /* calculate if the extent item flag is full backref or not */
- if (nrefs->full_backref[level] != 0)
- flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
-
- /* insert an extent item */
- if (insert_extent) {
- struct btrfs_disk_key copy_key;
-
- generation = btrfs_header_generation(node);
-
- if (level < root_level && nrefs->full_backref[level + 1] &&
- owner != root->objectid) {
- flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
- }
-
- key.objectid = bytenr;
- if (!skinny_metadata) {
- key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = node_size;
- size += sizeof(*bi);
- } else {
- key.type = BTRFS_METADATA_ITEM_KEY;
- key.offset = level;
- }
-
- btrfs_release_path(&path);
- ret = btrfs_insert_empty_item(trans, extent_root, &path, &key,
- size);
- if (ret)
- goto out;
-
- eb = path.nodes[0];
- ei = btrfs_item_ptr(eb, path.slots[0], struct btrfs_extent_item);
-
- btrfs_set_extent_refs(eb, ei, 0);
- btrfs_set_extent_generation(eb, ei, generation);
- btrfs_set_extent_flags(eb, ei, flags);
-
- if (!skinny_metadata) {
- bi = (struct btrfs_tree_block_info *)(ei + 1);
- memset_extent_buffer(eb, 0, (unsigned long)bi,
- sizeof(*bi));
- btrfs_set_disk_key_objectid(&copy_key, root->objectid);
- btrfs_set_disk_key_type(&copy_key, 0);
- btrfs_set_disk_key_offset(&copy_key, 0);
-
- btrfs_set_tree_block_level(eb, bi, level);
- btrfs_set_tree_block_key(eb, bi, &copy_key);
- }
- btrfs_mark_buffer_dirty(eb);
- printf("Added an extent item [%llu %u]\n", bytenr, node_size);
- btrfs_update_block_group(trans, extent_root, bytenr, node_size,
- 1, 0);
-
- nrefs->refs[level] = 0;
- nrefs->full_backref[level] =
- flags & BTRFS_BLOCK_FLAG_FULL_BACKREF;
- btrfs_release_path(&path);
- }
-
- if (level < root_level && nrefs->full_backref[level + 1] &&
- owner != root->objectid)
- parent = nrefs->bytenr[level + 1];
-
- /* increase the ref */
- ret = btrfs_inc_extent_ref(trans, extent_root, bytenr, node_size,
- parent, root->objectid, level, 0);
-
- nrefs->refs[level]++;
-out:
- btrfs_release_path(&path);
- if (ret) {
- error(
- "failed to repair tree block ref start %llu root %llu due to %s",
- bytenr, root->objectid, strerror(-ret));
- } else {
- printf("Added one tree block ref start %llu %s %llu\n",
- bytenr, parent ? "parent" : "root",
- parent ? parent : root->objectid);
- err &= ~BACKREF_MISSING;
- }
-
- return err;
-}
-
-static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
- unsigned int ext_ref);
-static int check_tree_block_ref(struct btrfs_root *root,
- struct extent_buffer *eb, u64 bytenr,
- int level, u64 owner, struct node_refs *nrefs);
-static int check_leaf_items(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
- struct node_refs *nrefs, int account_bytes);
-
-/*
- * @trans just for lowmem repair mode
- * @check all if not 0 then check all tree block backrefs and items
- * 0 then just check relationship of items in fs tree(s)
- *
- * Returns >0 Found error, should continue
- * Returns <0 Fatal error, must exit the whole check
- * Returns 0 No errors found
- */
-static int walk_down_tree_v2(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
- int *level, struct node_refs *nrefs, int ext_ref,
- int check_all)
-
-{
- enum btrfs_tree_block_status status;
- u64 bytenr;
- u64 ptr_gen;
- struct btrfs_fs_info *fs_info = root->fs_info;
- struct extent_buffer *next;
- struct extent_buffer *cur;
- int ret;
- int err = 0;
- int check;
- int account_file_data = 0;
-
- WARN_ON(*level < 0);
- WARN_ON(*level >= BTRFS_MAX_LEVEL);
-
- ret = update_nodes_refs(root, btrfs_header_bytenr(path->nodes[*level]),
- path->nodes[*level], nrefs, *level, check_all);
- if (ret < 0)
- return ret;
-
- while (*level >= 0) {
- WARN_ON(*level < 0);
- WARN_ON(*level >= BTRFS_MAX_LEVEL);
- cur = path->nodes[*level];
- bytenr = btrfs_header_bytenr(cur);
- check = nrefs->need_check[*level];
-
- if (btrfs_header_level(cur) != *level)
- WARN_ON(1);
- /*
- * Update bytes accounting and check tree block ref
- * NOTE: Doing accounting and check before checking nritems
- * is necessary because of empty node/leaf.
- */
- if ((check_all && !nrefs->checked[*level]) ||
- (!check_all && nrefs->need_check[*level])) {
- ret = check_tree_block_ref(root, cur,
- btrfs_header_bytenr(cur), btrfs_header_level(cur),
- btrfs_header_owner(cur), nrefs);
-
- if (repair && ret)
- ret = repair_tree_block_ref(trans, root,
- path->nodes[*level], nrefs, *level, ret);
- err |= ret;
-
- if (check_all && nrefs->need_check[*level] &&
- nrefs->refs[*level]) {
- account_bytes(root, path, *level);
- account_file_data = 1;
- }
- nrefs->checked[*level] = 1;
- }
-
- if (path->slots[*level] >= btrfs_header_nritems(cur))
- break;
-
- /* Don't forgot to check leaf/node validation */
- if (*level == 0) {
- /* skip duplicate check */
- if (check || !check_all) {
- ret = btrfs_check_leaf(root, NULL, cur);
- if (ret != BTRFS_TREE_BLOCK_CLEAN) {
- err |= -EIO;
- break;
- }
- }
-
- ret = 0;
- if (!check_all)
- ret = process_one_leaf_v2(root, path, nrefs,
- level, ext_ref);
- else
- ret = check_leaf_items(trans, root, path,
- nrefs, account_file_data);
- err |= ret;
- break;
- } else {
- if (check || !check_all) {
- ret = btrfs_check_node(root, NULL, cur);
- if (ret != BTRFS_TREE_BLOCK_CLEAN) {
- err |= -EIO;
- break;
- }
- }
- }
-
- bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
- ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
-
- ret = update_nodes_refs(root, bytenr, NULL, nrefs, *level - 1,
- check_all);
- if (ret < 0)
- break;
- /*
- * check all trees in check_chunks_and_extent_v2
- * check shared node once in check_fs_roots
- */
- if (!check_all && !nrefs->need_check[*level - 1]) {
- path->slots[*level]++;
- continue;
- }
-
- next = btrfs_find_tree_block(fs_info, bytenr, fs_info->nodesize);
- if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
- free_extent_buffer(next);
- reada_walk_down(root, cur, path->slots[*level]);
- next = read_tree_block(fs_info, bytenr, ptr_gen);
- if (!extent_buffer_uptodate(next)) {
- struct btrfs_key node_key;
-
- btrfs_node_key_to_cpu(path->nodes[*level],
- &node_key,
- path->slots[*level]);
- btrfs_add_corrupt_extent_record(fs_info,
- &node_key, path->nodes[*level]->start,
- fs_info->nodesize, *level);
- err |= -EIO;
- break;
- }
- }
-
- ret = check_child_node(cur, path->slots[*level], next);
- err |= ret;
- if (ret < 0)
- break;
-
- if (btrfs_is_leaf(next))
- status = btrfs_check_leaf(root, NULL, next);
- else
- status = btrfs_check_node(root, NULL, next);
- if (status != BTRFS_TREE_BLOCK_CLEAN) {
- free_extent_buffer(next);
- err |= -EIO;
- break;
- }
-
- *level = *level - 1;
- free_extent_buffer(path->nodes[*level]);
- path->nodes[*level] = next;
- path->slots[*level] = 0;
- account_file_data = 0;
-
- update_nodes_refs(root, (u64)-1, next, nrefs, *level, check_all);
- }
- return err;
-}
-
static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path,
struct walk_control *wc, int *level)
{
@@ -2847,35 +1720,13 @@ static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path,
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 walk_up_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
- 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;
- *level = i + 1;
}
+ 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;
}
@@ -2938,7 +1789,7 @@ static int repair_inode_isize(struct btrfs_trans_handle *trans,
btrfs_set_inode_size(path->nodes[0], ei, rec->found_size);
btrfs_mark_buffer_dirty(path->nodes[0]);
rec->errors &= ~I_ERR_DIR_ISIZE_WRONG;
- printf("reset isize for dir %Lu root %Lu\n", rec->ino,
+ printf("reset isize for dir %llu root %llu\n", rec->ino,
root->root_key.objectid);
out:
btrfs_release_path(path);
@@ -3094,47 +1945,6 @@ static int delete_dir_index(struct btrfs_root *root,
return ret;
}
-static int __create_inode_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 ino, u64 size,
- u64 nbytes, u64 nlink, u32 mode)
-{
- struct btrfs_inode_item ii;
- time_t now = time(NULL);
- int ret;
-
- btrfs_set_stack_inode_size(&ii, size);
- btrfs_set_stack_inode_nbytes(&ii, nbytes);
- btrfs_set_stack_inode_nlink(&ii, nlink);
- btrfs_set_stack_inode_mode(&ii, mode);
- btrfs_set_stack_inode_generation(&ii, trans->transid);
- btrfs_set_stack_timespec_nsec(&ii.atime, 0);
- btrfs_set_stack_timespec_sec(&ii.ctime, now);
- btrfs_set_stack_timespec_nsec(&ii.ctime, 0);
- btrfs_set_stack_timespec_sec(&ii.mtime, now);
- btrfs_set_stack_timespec_nsec(&ii.mtime, 0);
- btrfs_set_stack_timespec_sec(&ii.otime, 0);
- btrfs_set_stack_timespec_nsec(&ii.otime, 0);
-
- ret = btrfs_insert_inode(trans, root, ino, &ii);
- ASSERT(!ret);
-
- warning("root %llu inode %llu recreating inode item, this may "
- "be incomplete, please check permissions and content after "
- "the fsck completes.\n", (unsigned long long)root->objectid,
- (unsigned long long)ino);
-
- return 0;
-}
-
-static int create_inode_item_lowmem(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 ino,
- u8 filetype)
-{
- u32 mode = (filetype == BTRFS_FT_DIR ? S_IFDIR : S_IFREG) | 0755;
-
- return __create_inode_item(trans, root, ino, 0, 0, 0, mode);
-}
-
static int create_inode_item(struct btrfs_root *root,
struct inode_record *rec, int root_dir)
{
@@ -3165,7 +1975,7 @@ static int create_inode_item(struct btrfs_root *root,
mode = S_IFREG | 0755;
}
- ret = __create_inode_item(trans, root, rec->ino, size, rec->nbytes,
+ ret = insert_inode_item(trans, root, rec->ino, size, rec->nbytes,
nlink, mode);
btrfs_commit_transaction(trans, root);
return 0;
@@ -3395,98 +2205,6 @@ out:
return ret;
}
-static int get_highest_inode(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *path,
- u64 *highest_ino)
-{
- struct btrfs_key key, found_key;
- int ret;
-
- btrfs_init_path(path);
- key.objectid = BTRFS_LAST_FREE_OBJECTID;
- key.offset = -1;
- key.type = BTRFS_INODE_ITEM_KEY;
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
- if (ret == 1) {
- btrfs_item_key_to_cpu(path->nodes[0], &found_key,
- path->slots[0] - 1);
- *highest_ino = found_key.objectid;
- ret = 0;
- }
- if (*highest_ino >= BTRFS_LAST_FREE_OBJECTID)
- ret = -EOVERFLOW;
- btrfs_release_path(path);
- return ret;
-}
-
-/*
- * Link inode to dir 'lost+found'. Increase @ref_count.
- *
- * Returns 0 means success.
- * Returns <0 means failure.
- */
-static int link_inode_to_lostfound(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *path,
- u64 ino, char *namebuf, u32 name_len,
- u8 filetype, u64 *ref_count)
-{
- char *dir_name = "lost+found";
- u64 lost_found_ino;
- int ret;
- u32 mode = 0700;
-
- btrfs_release_path(path);
- ret = get_highest_inode(trans, root, path, &lost_found_ino);
- if (ret < 0)
- goto out;
- lost_found_ino++;
-
- ret = btrfs_mkdir(trans, root, dir_name, strlen(dir_name),
- BTRFS_FIRST_FREE_OBJECTID, &lost_found_ino,
- mode);
- if (ret < 0) {
- error("failed to create '%s' dir: %s", dir_name, strerror(-ret));
- goto out;
- }
- ret = btrfs_add_link(trans, root, ino, lost_found_ino,
- namebuf, name_len, filetype, NULL, 1, 0);
- /*
- * Add ".INO" suffix several times to handle case where
- * "FILENAME.INO" is already taken by another file.
- */
- while (ret == -EEXIST) {
- /*
- * Conflicting file name, add ".INO" as suffix * +1 for '.'
- */
- if (name_len + count_digits(ino) + 1 > BTRFS_NAME_LEN) {
- ret = -EFBIG;
- goto out;
- }
- snprintf(namebuf + name_len, BTRFS_NAME_LEN - name_len,
- ".%llu", ino);
- name_len += count_digits(ino) + 1;
- ret = btrfs_add_link(trans, root, ino, lost_found_ino, namebuf,
- name_len, filetype, NULL, 1, 0);
- }
- if (ret < 0) {
- error("failed to link the inode %llu to %s dir: %s",
- ino, dir_name, strerror(-ret));
- goto out;
- }
-
- ++*ref_count;
- printf("Moving file '%.*s' to '%s' dir since it has no valid backref\n",
- name_len, namebuf, dir_name);
-out:
- btrfs_release_path(path);
- if (ret)
- error("failed to move file '%.*s' to '%s' dir", name_len,
- namebuf, dir_name);
- return ret;
-}
-
static int repair_inode_nlinks(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
@@ -4580,14 +3298,6 @@ skip_walking:
return ret;
}
-static int fs_root_objectid(u64 objectid)
-{
- if (objectid == BTRFS_TREE_RELOC_OBJECTID ||
- objectid == BTRFS_DATA_RELOC_TREE_OBJECTID)
- return 1;
- return is_fstree(objectid);
-}
-
static int check_fs_roots(struct btrfs_fs_info *fs_info,
struct cache_tree *root_cache)
{
@@ -4686,1807 +3396,6 @@ out:
return err;
}
-/*
- * Find the @index according by @ino and name.
- * Notice:time efficiency is O(N)
- *
- * @root: the root of the fs/file tree
- * @index_ret: the index as return value
- * @namebuf: the name to match
- * @name_len: the length of name to match
- * @file_type: the file_type of INODE_ITEM to match
- *
- * Returns 0 if found and *@index_ret will be modified with right value
- * Returns< 0 not found and *@index_ret will be (u64)-1
- */
-static int find_dir_index(struct btrfs_root *root, u64 dirid, u64 location_id,
- u64 *index_ret, char *namebuf, u32 name_len,
- u8 file_type)
-{
- struct btrfs_path path;
- struct extent_buffer *node;
- struct btrfs_dir_item *di;
- struct btrfs_key key;
- struct btrfs_key location;
- char name[BTRFS_NAME_LEN] = {0};
-
- u32 total;
- u32 cur = 0;
- u32 len;
- u32 data_len;
- u8 filetype;
- int slot;
- int ret;
-
- ASSERT(index_ret);
-
- /* search from the last index */
- key.objectid = dirid;
- key.offset = (u64)-1;
- key.type = BTRFS_DIR_INDEX_KEY;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
- if (ret < 0)
- return ret;
-
-loop:
- ret = btrfs_previous_item(root, &path, dirid, BTRFS_DIR_INDEX_KEY);
- if (ret) {
- ret = -ENOENT;
- *index_ret = (64)-1;
- goto out;
- }
- /* Check whether inode_id/filetype/name match */
- node = path.nodes[0];
- slot = path.slots[0];
- di = btrfs_item_ptr(node, slot, struct btrfs_dir_item);
- total = btrfs_item_size_nr(node, slot);
- while (cur < total) {
- ret = -ENOENT;
- len = btrfs_dir_name_len(node, di);
- data_len = btrfs_dir_data_len(node, di);
-
- btrfs_dir_item_key_to_cpu(node, di, &location);
- if (location.objectid != location_id ||
- location.type != BTRFS_INODE_ITEM_KEY ||
- location.offset != 0)
- goto next;
-
- filetype = btrfs_dir_type(node, di);
- if (file_type != filetype)
- goto next;
-
- if (len > BTRFS_NAME_LEN)
- len = BTRFS_NAME_LEN;
-
- read_extent_buffer(node, name, (unsigned long)(di + 1), len);
- if (len != name_len || strncmp(namebuf, name, len))
- goto next;
-
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- *index_ret = key.offset;
- ret = 0;
- goto out;
-next:
- len += sizeof(*di) + data_len;
- di = (struct btrfs_dir_item *)((char *)di + len);
- cur += len;
- }
- goto loop;
-
-out:
- btrfs_release_path(&path);
- return ret;
-}
-
-/*
- * Find DIR_ITEM/DIR_INDEX for the given key and check it with the specified
- * INODE_REF/INODE_EXTREF match.
- *
- * @root: the root of the fs/file tree
- * @key: the key of the DIR_ITEM/DIR_INDEX, key->offset will be right
- * value while find index
- * @location_key: location key of the struct btrfs_dir_item to match
- * @name: the name to match
- * @namelen: the length of name
- * @file_type: the type of file to math
- *
- * Return 0 if no error occurred.
- * Return DIR_ITEM_MISSING/DIR_INDEX_MISSING if couldn't find
- * DIR_ITEM/DIR_INDEX
- * Return DIR_ITEM_MISMATCH/DIR_INDEX_MISMATCH if INODE_REF/INODE_EXTREF
- * and DIR_ITEM/DIR_INDEX mismatch
- */
-static int find_dir_item(struct btrfs_root *root, struct btrfs_key *key,
- struct btrfs_key *location_key, char *name,
- u32 namelen, u8 file_type)
-{
- struct btrfs_path path;
- struct extent_buffer *node;
- struct btrfs_dir_item *di;
- struct btrfs_key location;
- char namebuf[BTRFS_NAME_LEN] = {0};
- u32 total;
- u32 cur = 0;
- u32 len;
- u32 data_len;
- u8 filetype;
- int slot;
- int ret;
-
- /* get the index by traversing all index */
- if (key->type == BTRFS_DIR_INDEX_KEY && key->offset == (u64)-1) {
- ret = find_dir_index(root, key->objectid,
- location_key->objectid, &key->offset,
- name, namelen, file_type);
- if (ret)
- ret = DIR_INDEX_MISSING;
- return ret;
- }
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
- if (ret) {
- ret = key->type == BTRFS_DIR_ITEM_KEY ? DIR_ITEM_MISSING :
- DIR_INDEX_MISSING;
- goto out;
- }
-
- /* Check whether inode_id/filetype/name match */
- node = path.nodes[0];
- slot = path.slots[0];
- di = btrfs_item_ptr(node, slot, struct btrfs_dir_item);
- total = btrfs_item_size_nr(node, slot);
- while (cur < total) {
- ret = key->type == BTRFS_DIR_ITEM_KEY ?
- DIR_ITEM_MISMATCH : DIR_INDEX_MISMATCH;
-
- len = btrfs_dir_name_len(node, di);
- data_len = btrfs_dir_data_len(node, di);
-
- btrfs_dir_item_key_to_cpu(node, di, &location);
- if (location.objectid != location_key->objectid ||
- location.type != location_key->type ||
- location.offset != location_key->offset)
- goto next;
-
- filetype = btrfs_dir_type(node, di);
- if (file_type != filetype)
- goto next;
-
- if (len > BTRFS_NAME_LEN) {
- len = BTRFS_NAME_LEN;
- warning("root %llu %s[%llu %llu] name too long %u, trimmed",
- root->objectid,
- key->type == BTRFS_DIR_ITEM_KEY ?
- "DIR_ITEM" : "DIR_INDEX",
- key->objectid, key->offset, len);
- }
- read_extent_buffer(node, namebuf, (unsigned long)(di + 1),
- len);
- if (len != namelen || strncmp(namebuf, name, len))
- goto next;
-
- ret = 0;
- goto out;
-next:
- len += sizeof(*di) + data_len;
- di = (struct btrfs_dir_item *)((char *)di + len);
- cur += len;
- }
-
-out:
- btrfs_release_path(&path);
- return ret;
-}
-
-/*
- * Prints inode ref error message
- */
-static void print_inode_ref_err(struct btrfs_root *root, struct btrfs_key *key,
- u64 index, const char *namebuf, int name_len,
- u8 filetype, int err)
-{
- if (!err)
- return;
-
- /* root dir error */
- if (key->objectid == BTRFS_FIRST_FREE_OBJECTID) {
- error(
- "root %llu root dir shouldn't have INODE REF[%llu %llu] name %s",
- root->objectid, key->objectid, key->offset, namebuf);
- return;
- }
-
- /* normal error */
- if (err & (DIR_ITEM_MISMATCH | DIR_ITEM_MISSING))
- error("root %llu DIR ITEM[%llu %llu] %s name %s filetype %u",
- root->objectid, key->offset,
- btrfs_name_hash(namebuf, name_len),
- err & DIR_ITEM_MISMATCH ? "mismatch" : "missing",
- namebuf, filetype);
- if (err & (DIR_INDEX_MISMATCH | DIR_INDEX_MISSING))
- error("root %llu DIR INDEX[%llu %llu] %s name %s filetype %u",
- root->objectid, key->offset, index,
- err & DIR_ITEM_MISMATCH ? "mismatch" : "missing",
- namebuf, filetype);
-}
-
-/*
- * Insert the missing inode item.
- *
- * Returns 0 means success.
- * Returns <0 means error.
- */
-static int repair_inode_item_missing(struct btrfs_root *root, u64 ino,
- u8 filetype)
-{
- struct btrfs_key key;
- struct btrfs_trans_handle *trans;
- struct btrfs_path path;
- int ret;
-
- key.objectid = ino;
- key.type = BTRFS_INODE_ITEM_KEY;
- key.offset = 0;
-
- btrfs_init_path(&path);
- trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- ret = -EIO;
- goto out;
- }
-
- ret = btrfs_search_slot(trans, root, &key, &path, 1, 1);
- if (ret < 0 || !ret)
- goto fail;
-
- /* insert inode item */
- create_inode_item_lowmem(trans, root, ino, filetype);
- ret = 0;
-fail:
- btrfs_commit_transaction(trans, root);
-out:
- if (ret)
- error("failed to repair root %llu INODE ITEM[%llu] missing",
- root->objectid, ino);
- btrfs_release_path(&path);
- return ret;
-}
-
-/*
- * The ternary means dir item, dir index and relative inode ref.
- * The function handles errs: INODE_MISSING, DIR_INDEX_MISSING
- * DIR_INDEX_MISMATCH, DIR_ITEM_MISSING, DIR_ITEM_MISMATCH by the follow
- * strategy:
- * If two of three is missing or mismatched, delete the existing one.
- * If one of three is missing or mismatched, add the missing one.
- *
- * returns 0 means success.
- * returns not 0 means on error;
- */
-int repair_ternary_lowmem(struct btrfs_root *root, u64 dir_ino, u64 ino,
- u64 index, char *name, int name_len, u8 filetype,
- int err)
-{
- struct btrfs_trans_handle *trans;
- int stage = 0;
- int ret = 0;
-
- /*
- * stage shall be one of following valild values:
- * 0: Fine, nothing to do.
- * 1: One of three is wrong, so add missing one.
- * 2: Two of three is wrong, so delete existed one.
- */
- if (err & (DIR_INDEX_MISMATCH | DIR_INDEX_MISSING))
- stage++;
- if (err & (DIR_ITEM_MISMATCH | DIR_ITEM_MISSING))
- stage++;
- if (err & (INODE_REF_MISSING))
- stage++;
-
- /* stage must be smllarer than 3 */
- ASSERT(stage < 3);
-
- trans = btrfs_start_transaction(root, 1);
- if (stage == 2) {
- ret = btrfs_unlink(trans, root, ino, dir_ino, index, name,
- name_len, 0);
- goto out;
- }
- if (stage == 1) {
- ret = btrfs_add_link(trans, root, ino, dir_ino, name, name_len,
- filetype, &index, 1, 1);
- goto out;
- }
-out:
- btrfs_commit_transaction(trans, root);
-
- if (ret)
- error("fail to repair inode %llu name %s filetype %u",
- ino, name, filetype);
- else
- printf("%s ref/dir_item of inode %llu name %s filetype %u\n",
- stage == 2 ? "Delete" : "Add",
- ino, name, filetype);
-
- return ret;
-}
-
-/*
- * Traverse the given INODE_REF and call find_dir_item() to find related
- * DIR_ITEM/DIR_INDEX.
- *
- * @root: the root of the fs/file tree
- * @ref_key: the key of the INODE_REF
- * @path the path provides node and slot
- * @refs: the count of INODE_REF
- * @mode: the st_mode of INODE_ITEM
- * @name_ret: returns with the first ref's name
- * @name_len_ret: len of the name_ret
- *
- * Return 0 if no error occurred.
- */
-static int check_inode_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
- struct btrfs_path *path, char *name_ret,
- u32 *namelen_ret, u64 *refs_ret, int mode)
-{
- struct btrfs_key key;
- struct btrfs_key location;
- struct btrfs_inode_ref *ref;
- struct extent_buffer *node;
- char namebuf[BTRFS_NAME_LEN] = {0};
- u32 total;
- u32 cur = 0;
- u32 len;
- u32 name_len;
- u64 index;
- int ret;
- int err = 0;
- int tmp_err;
- int slot;
- int need_research = 0;
- u64 refs;
-
-begin:
- err = 0;
- cur = 0;
- refs = *refs_ret;
-
- /* since after repair, path and the dir item may be changed */
- if (need_research) {
- need_research = 0;
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, ref_key, path, 0, 0);
- /* the item was deleted, let path point to the last checked item */
- if (ret > 0) {
- if (path->slots[0] == 0)
- btrfs_prev_leaf(root, path);
- else
- path->slots[0]--;
- }
- if (ret)
- goto out;
- }
-
- location.objectid = ref_key->objectid;
- location.type = BTRFS_INODE_ITEM_KEY;
- location.offset = 0;
- node = path->nodes[0];
- slot = path->slots[0];
-
- memset(namebuf, 0, sizeof(namebuf) / sizeof(*namebuf));
- ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref);
- total = btrfs_item_size_nr(node, slot);
-
-next:
- /* Update inode ref count */
- refs++;
- tmp_err = 0;
- index = btrfs_inode_ref_index(node, ref);
- name_len = btrfs_inode_ref_name_len(node, ref);
-
- if (name_len <= BTRFS_NAME_LEN) {
- len = name_len;
- } else {
- len = BTRFS_NAME_LEN;
- warning("root %llu INODE_REF[%llu %llu] name too long",
- root->objectid, ref_key->objectid, ref_key->offset);
- }
-
- read_extent_buffer(node, namebuf, (unsigned long)(ref + 1), len);
-
- /* copy the first name found to name_ret */
- if (refs == 1 && name_ret) {
- memcpy(name_ret, namebuf, len);
- *namelen_ret = len;
- }
-
- /* Check root dir ref */
- if (ref_key->objectid == BTRFS_FIRST_FREE_OBJECTID) {
- if (index != 0 || len != strlen("..") ||
- strncmp("..", namebuf, len) ||
- ref_key->offset != BTRFS_FIRST_FREE_OBJECTID) {
- /* set err bits then repair will delete the ref */
- err |= DIR_INDEX_MISSING;
- err |= DIR_ITEM_MISSING;
- }
- goto end;
- }
-
- /* Find related DIR_INDEX */
- key.objectid = ref_key->offset;
- key.type = BTRFS_DIR_INDEX_KEY;
- key.offset = index;
- tmp_err |= find_dir_item(root, &key, &location, namebuf, len,
- imode_to_type(mode));
-
- /* Find related dir_item */
- key.objectid = ref_key->offset;
- key.type = BTRFS_DIR_ITEM_KEY;
- key.offset = btrfs_name_hash(namebuf, len);
- tmp_err |= find_dir_item(root, &key, &location, namebuf, len,
- imode_to_type(mode));
-end:
- if (tmp_err && repair) {
- ret = repair_ternary_lowmem(root, ref_key->offset,
- ref_key->objectid, index, namebuf,
- name_len, imode_to_type(mode),
- tmp_err);
- if (!ret) {
- need_research = 1;
- goto begin;
- }
- }
- print_inode_ref_err(root, ref_key, index, namebuf, name_len,
- imode_to_type(mode), tmp_err);
- err |= tmp_err;
- len = sizeof(*ref) + name_len;
- ref = (struct btrfs_inode_ref *)((char *)ref + len);
- cur += len;
- if (cur < total)
- goto next;
-
-out:
- *refs_ret = refs;
- return err;
-}
-
-/*
- * Traverse the given INODE_EXTREF and call find_dir_item() to find related
- * DIR_ITEM/DIR_INDEX.
- *
- * @root: the root of the fs/file tree
- * @ref_key: the key of the INODE_EXTREF
- * @refs: the count of INODE_EXTREF
- * @mode: the st_mode of INODE_ITEM
- *
- * Return 0 if no error occurred.
- */
-static int check_inode_extref(struct btrfs_root *root,
- struct btrfs_key *ref_key,
- struct extent_buffer *node, int slot, u64 *refs,
- int mode)
-{
- struct btrfs_key key;
- struct btrfs_key location;
- struct btrfs_inode_extref *extref;
- char namebuf[BTRFS_NAME_LEN] = {0};
- u32 total;
- u32 cur = 0;
- u32 len;
- u32 name_len;
- u64 index;
- u64 parent;
- int ret;
- int err = 0;
-
- location.objectid = ref_key->objectid;
- location.type = BTRFS_INODE_ITEM_KEY;
- location.offset = 0;
-
- extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref);
- total = btrfs_item_size_nr(node, slot);
-
-next:
- /* update inode ref count */
- (*refs)++;
- name_len = btrfs_inode_extref_name_len(node, extref);
- index = btrfs_inode_extref_index(node, extref);
- parent = btrfs_inode_extref_parent(node, extref);
- if (name_len <= BTRFS_NAME_LEN) {
- len = name_len;
- } else {
- len = BTRFS_NAME_LEN;
- warning("root %llu INODE_EXTREF[%llu %llu] name too long",
- root->objectid, ref_key->objectid, ref_key->offset);
- }
- read_extent_buffer(node, namebuf, (unsigned long)(extref + 1), len);
-
- /* Check root dir ref name */
- if (index == 0 && strncmp(namebuf, "..", name_len)) {
- error("root %llu INODE_EXTREF[%llu %llu] ROOT_DIR name shouldn't be %s",
- root->objectid, ref_key->objectid, ref_key->offset,
- namebuf);
- err |= ROOT_DIR_ERROR;
- }
-
- /* find related dir_index */
- key.objectid = parent;
- key.type = BTRFS_DIR_INDEX_KEY;
- key.offset = index;
- ret = find_dir_item(root, &key, &location, namebuf, len, mode);
- err |= ret;
-
- /* find related dir_item */
- key.objectid = parent;
- key.type = BTRFS_DIR_ITEM_KEY;
- key.offset = btrfs_name_hash(namebuf, len);
- ret = find_dir_item(root, &key, &location, namebuf, len, mode);
- err |= ret;
-
- len = sizeof(*extref) + name_len;
- extref = (struct btrfs_inode_extref *)((char *)extref + len);
- cur += len;
-
- if (cur < total)
- goto next;
-
- return err;
-}
-
-/*
- * Find INODE_REF/INODE_EXTREF for the given key and check it with the specified
- * DIR_ITEM/DIR_INDEX match.
- * Return with @index_ret.
- *
- * @root: the root of the fs/file tree
- * @key: the key of the INODE_REF/INODE_EXTREF
- * @name: the name in the INODE_REF/INODE_EXTREF
- * @namelen: the length of name in the INODE_REF/INODE_EXTREF
- * @index_ret: the index in the INODE_REF/INODE_EXTREF,
- * value (64)-1 means do not check index
- * @ext_ref: the EXTENDED_IREF feature
- *
- * Return 0 if no error occurred.
- * Return >0 for error bitmap
- */
-static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key,
- char *name, int namelen, u64 *index_ret,
- unsigned int ext_ref)
-{
- struct btrfs_path path;
- struct btrfs_inode_ref *ref;
- struct btrfs_inode_extref *extref;
- struct extent_buffer *node;
- char ref_namebuf[BTRFS_NAME_LEN] = {0};
- u32 total;
- u32 cur = 0;
- u32 len;
- u32 ref_namelen;
- u64 ref_index;
- u64 parent;
- u64 dir_id;
- int slot;
- int ret;
-
- ASSERT(index_ret);
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
- if (ret) {
- ret = INODE_REF_MISSING;
- goto extref;
- }
-
- node = path.nodes[0];
- slot = path.slots[0];
-
- ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref);
- total = btrfs_item_size_nr(node, slot);
-
- /* Iterate all entry of INODE_REF */
- while (cur < total) {
- ret = INODE_REF_MISSING;
-
- ref_namelen = btrfs_inode_ref_name_len(node, ref);
- ref_index = btrfs_inode_ref_index(node, ref);
- if (*index_ret != (u64)-1 && *index_ret != ref_index)
- goto next_ref;
-
- if (cur + sizeof(*ref) + ref_namelen > total ||
- ref_namelen > BTRFS_NAME_LEN) {
- warning("root %llu INODE %s[%llu %llu] name too long",
- root->objectid,
- key->type == BTRFS_INODE_REF_KEY ?
- "REF" : "EXTREF",
- key->objectid, key->offset);
-
- if (cur + sizeof(*ref) > total)
- break;
- len = min_t(u32, total - cur - sizeof(*ref),
- BTRFS_NAME_LEN);
- } else {
- len = ref_namelen;
- }
-
- read_extent_buffer(node, ref_namebuf, (unsigned long)(ref + 1),
- len);
-
- if (len != namelen || strncmp(ref_namebuf, name, len))
- goto next_ref;
-
- *index_ret = ref_index;
- ret = 0;
- goto out;
-next_ref:
- len = sizeof(*ref) + ref_namelen;
- ref = (struct btrfs_inode_ref *)((char *)ref + len);
- cur += len;
- }
-
-extref:
- /* Skip if not support EXTENDED_IREF feature */
- if (!ext_ref)
- goto out;
-
- btrfs_release_path(&path);
- btrfs_init_path(&path);
-
- dir_id = key->offset;
- key->type = BTRFS_INODE_EXTREF_KEY;
- key->offset = btrfs_extref_hash(dir_id, name, namelen);
-
- ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
- if (ret) {
- ret = INODE_REF_MISSING;
- goto out;
- }
-
- node = path.nodes[0];
- slot = path.slots[0];
-
- extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref);
- cur = 0;
- total = btrfs_item_size_nr(node, slot);
-
- /* Iterate all entry of INODE_EXTREF */
- while (cur < total) {
- ret = INODE_REF_MISSING;
-
- ref_namelen = btrfs_inode_extref_name_len(node, extref);
- ref_index = btrfs_inode_extref_index(node, extref);
- parent = btrfs_inode_extref_parent(node, extref);
- if (*index_ret != (u64)-1 && *index_ret != ref_index)
- goto next_extref;
-
- if (parent != dir_id)
- goto next_extref;
-
- if (ref_namelen <= BTRFS_NAME_LEN) {
- len = ref_namelen;
- } else {
- len = BTRFS_NAME_LEN;
- warning("root %llu INODE %s[%llu %llu] name too long",
- root->objectid,
- key->type == BTRFS_INODE_REF_KEY ?
- "REF" : "EXTREF",
- key->objectid, key->offset);
- }
- read_extent_buffer(node, ref_namebuf,
- (unsigned long)(extref + 1), len);
-
- if (len != namelen || strncmp(ref_namebuf, name, len))
- goto next_extref;
-
- *index_ret = ref_index;
- ret = 0;
- goto out;
-
-next_extref:
- len = sizeof(*extref) + ref_namelen;
- extref = (struct btrfs_inode_extref *)((char *)extref + len);
- cur += len;
-
- }
-out:
- btrfs_release_path(&path);
- return ret;
-}
-
-static void print_dir_item_err(struct btrfs_root *root, struct btrfs_key *key,
- u64 ino, u64 index, const char *namebuf,
- int name_len, u8 filetype, int err)
-{
- if (err & (DIR_ITEM_MISMATCH | DIR_ITEM_MISSING)) {
- error("root %llu DIR ITEM[%llu %llu] name %s filetype %d %s",
- root->objectid, key->objectid, key->offset, namebuf,
- filetype,
- err & DIR_ITEM_MISMATCH ? "mismath" : "missing");
- }
-
- if (err & (DIR_INDEX_MISMATCH | DIR_INDEX_MISSING)) {
- error("root %llu DIR INDEX[%llu %llu] name %s filetype %d %s",
- root->objectid, key->objectid, index, namebuf, filetype,
- err & DIR_ITEM_MISMATCH ? "mismath" : "missing");
- }
-
- if (err & (INODE_ITEM_MISSING | INODE_ITEM_MISMATCH)) {
- error(
- "root %llu INODE_ITEM[%llu] index %llu name %s filetype %d %s",
- root->objectid, ino, index, namebuf, filetype,
- err & INODE_ITEM_MISMATCH ? "mismath" : "missing");
- }
-
- if (err & INODE_REF_MISSING)
- error(
- "root %llu INODE REF[%llu, %llu] name %s filetype %u missing",
- root->objectid, ino, key->objectid, namebuf, filetype);
-
-}
-
-/*
- * Call repair_inode_item_missing and repair_ternary_lowmem to repair
- *
- * Returns error after repair
- */
-static int repair_dir_item(struct btrfs_root *root, u64 dirid, u64 ino,
- u64 index, u8 filetype, char *namebuf, u32 name_len,
- int err)
-{
- int ret;
-
- if (err & INODE_ITEM_MISSING) {
- ret = repair_inode_item_missing(root, ino, filetype);
- if (!ret)
- err &= ~(INODE_ITEM_MISMATCH | INODE_ITEM_MISSING);
- }
-
- if (err & ~(INODE_ITEM_MISMATCH | INODE_ITEM_MISSING)) {
- ret = repair_ternary_lowmem(root, dirid, ino, index, namebuf,
- name_len, filetype, err);
- if (!ret) {
- err &= ~(DIR_INDEX_MISMATCH | DIR_INDEX_MISSING);
- err &= ~(DIR_ITEM_MISMATCH | DIR_ITEM_MISSING);
- err &= ~(INODE_REF_MISSING);
- }
- }
- return err;
-}
-
-static int __count_dir_isize(struct btrfs_root *root, u64 ino, int type,
- u64 *size_ret)
-{
- struct btrfs_key key;
- struct btrfs_path path;
- u32 len;
- struct btrfs_dir_item *di;
- int ret;
- int cur = 0;
- int total = 0;
-
- ASSERT(size_ret);
- *size_ret = 0;
-
- key.objectid = ino;
- key.type = type;
- key.offset = (u64)-1;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
- if (ret < 0) {
- ret = -EIO;
- goto out;
- }
- /* if found, go to spacial case */
- if (ret == 0)
- goto special_case;
-
-loop:
- ret = btrfs_previous_item(root, &path, ino, type);
-
- if (ret) {
- ret = 0;
- goto out;
- }
-
-special_case:
- di = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_dir_item);
- cur = 0;
- total = btrfs_item_size_nr(path.nodes[0], path.slots[0]);
-
- while (cur < total) {
- len = btrfs_dir_name_len(path.nodes[0], di);
- if (len > BTRFS_NAME_LEN)
- len = BTRFS_NAME_LEN;
- *size_ret += len;
-
- len += btrfs_dir_data_len(path.nodes[0], di);
- len += sizeof(*di);
- di = (struct btrfs_dir_item *)((char *)di + len);
- cur += len;
- }
- goto loop;
-
-out:
- btrfs_release_path(&path);
- return ret;
-}
-
-static int count_dir_isize(struct btrfs_root *root, u64 ino, u64 *size)
-{
- u64 item_size;
- u64 index_size;
- int ret;
-
- ASSERT(size);
- ret = __count_dir_isize(root, ino, BTRFS_DIR_ITEM_KEY, &item_size);
- if (ret)
- goto out;
-
- ret = __count_dir_isize(root, ino, BTRFS_DIR_INDEX_KEY, &index_size);
- if (ret)
- goto out;
-
- *size = item_size + index_size;
-
-out:
- if (ret)
- error("failed to count root %llu INODE[%llu] root size",
- root->objectid, ino);
- return ret;
-}
-
-/*
- * Traverse the given DIR_ITEM/DIR_INDEX and check related INODE_ITEM and
- * call find_inode_ref() to check related INODE_REF/INODE_EXTREF.
- *
- * @root: the root of the fs/file tree
- * @key: the key of the INODE_REF/INODE_EXTREF
- * @path: the path
- * @size: the st_size of the INODE_ITEM
- * @ext_ref: the EXTENDED_IREF feature
- *
- * Return 0 if no error occurred.
- * Return DIR_COUNT_AGAIN if the isize of the inode should be recalculated.
- */
-static int check_dir_item(struct btrfs_root *root, struct btrfs_key *di_key,
- struct btrfs_path *path, u64 *size,
- unsigned int ext_ref)
-{
- struct btrfs_dir_item *di;
- struct btrfs_inode_item *ii;
- struct btrfs_key key;
- struct btrfs_key location;
- struct extent_buffer *node;
- int slot;
- char namebuf[BTRFS_NAME_LEN] = {0};
- u32 total;
- u32 cur = 0;
- u32 len;
- u32 name_len;
- u32 data_len;
- u8 filetype;
- u32 mode = 0;
- u64 index;
- int ret;
- int err;
- int tmp_err;
- int need_research = 0;
-
- /*
- * For DIR_ITEM set index to (u64)-1, so that find_inode_ref
- * ignore index check.
- */
- if (di_key->type == BTRFS_DIR_INDEX_KEY)
- index = di_key->offset;
- else
- index = (u64)-1;
-begin:
- err = 0;
- cur = 0;
-
- /* since after repair, path and the dir item may be changed */
- if (need_research) {
- need_research = 0;
- err |= DIR_COUNT_AGAIN;
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, di_key, path, 0, 0);
- /* the item was deleted, let path point the last checked item */
- if (ret > 0) {
- if (path->slots[0] == 0)
- btrfs_prev_leaf(root, path);
- else
- path->slots[0]--;
- }
- if (ret)
- goto out;
- }
-
- node = path->nodes[0];
- slot = path->slots[0];
-
- di = btrfs_item_ptr(node, slot, struct btrfs_dir_item);
- total = btrfs_item_size_nr(node, slot);
- memset(namebuf, 0, sizeof(namebuf) / sizeof(*namebuf));
-
- while (cur < total) {
- data_len = btrfs_dir_data_len(node, di);
- tmp_err = 0;
- if (data_len)
- error("root %llu %s[%llu %llu] data_len shouldn't be %u",
- root->objectid,
- di_key->type == BTRFS_DIR_ITEM_KEY ? "DIR_ITEM" : "DIR_INDEX",
- di_key->objectid, di_key->offset, data_len);
-
- name_len = btrfs_dir_name_len(node, di);
- if (name_len <= BTRFS_NAME_LEN) {
- len = name_len;
- } else {
- len = BTRFS_NAME_LEN;
- warning("root %llu %s[%llu %llu] name too long",
- root->objectid,
- di_key->type == BTRFS_DIR_ITEM_KEY ? "DIR_ITEM" : "DIR_INDEX",
- di_key->objectid, di_key->offset);
- }
- (*size) += name_len;
- read_extent_buffer(node, namebuf, (unsigned long)(di + 1),
- len);
- filetype = btrfs_dir_type(node, di);
-
- if (di_key->type == BTRFS_DIR_ITEM_KEY &&
- di_key->offset != btrfs_name_hash(namebuf, len)) {
- err |= -EIO;
- error("root %llu DIR_ITEM[%llu %llu] name %s namelen %u filetype %u mismatch with its hash, wanted %llu have %llu",
- root->objectid, di_key->objectid, di_key->offset,
- namebuf, len, filetype, di_key->offset,
- btrfs_name_hash(namebuf, len));
- }
-
- btrfs_dir_item_key_to_cpu(node, di, &location);
- /* Ignore related ROOT_ITEM check */
- if (location.type == BTRFS_ROOT_ITEM_KEY)
- goto next;
-
- btrfs_release_path(path);
- /* Check relative INODE_ITEM(existence/filetype) */
- ret = btrfs_search_slot(NULL, root, &location, path, 0, 0);
- if (ret) {
- tmp_err |= INODE_ITEM_MISSING;
- goto next;
- }
-
- ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
- struct btrfs_inode_item);
- mode = btrfs_inode_mode(path->nodes[0], ii);
- if (imode_to_type(mode) != filetype) {
- tmp_err |= INODE_ITEM_MISMATCH;
- goto next;
- }
-
- /* Check relative INODE_REF/INODE_EXTREF */
- key.objectid = location.objectid;
- key.type = BTRFS_INODE_REF_KEY;
- key.offset = di_key->objectid;
- tmp_err |= find_inode_ref(root, &key, namebuf, len,
- &index, ext_ref);
-
- /* check relative INDEX/ITEM */
- key.objectid = di_key->objectid;
- if (key.type == BTRFS_DIR_ITEM_KEY) {
- key.type = BTRFS_DIR_INDEX_KEY;
- key.offset = index;
- } else {
- key.type = BTRFS_DIR_ITEM_KEY;
- key.offset = btrfs_name_hash(namebuf, name_len);
- }
-
- tmp_err |= find_dir_item(root, &key, &location, namebuf,
- name_len, filetype);
- /* find_dir_item may find index */
- if (key.type == BTRFS_DIR_INDEX_KEY)
- index = key.offset;
-next:
-
- if (tmp_err && repair) {
- ret = repair_dir_item(root, di_key->objectid,
- location.objectid, index,
- imode_to_type(mode), namebuf,
- name_len, tmp_err);
- if (ret != tmp_err) {
- need_research = 1;
- goto begin;
- }
- }
- btrfs_release_path(path);
- print_dir_item_err(root, di_key, location.objectid, index,
- namebuf, name_len, filetype, tmp_err);
- err |= tmp_err;
- len = sizeof(*di) + name_len + data_len;
- di = (struct btrfs_dir_item *)((char *)di + len);
- cur += len;
-
- if (di_key->type == BTRFS_DIR_INDEX_KEY && cur < total) {
- error("root %llu DIR_INDEX[%llu %llu] should contain only one entry",
- root->objectid, di_key->objectid,
- di_key->offset);
- break;
- }
- }
-out:
- /* research path */
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, di_key, path, 0, 0);
- if (ret)
- err |= ret > 0 ? -ENOENT : ret;
- return err;
-}
-
-/*
- * Wrapper function of btrfs_punch_hole.
- *
- * Returns 0 means success.
- * Returns not 0 means error.
- */
-static int punch_extent_hole(struct btrfs_root *root, u64 ino, u64 start,
- u64 len)
-{
- struct btrfs_trans_handle *trans;
- int ret = 0;
-
- trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans))
- return PTR_ERR(trans);
-
- ret = btrfs_punch_hole(trans, root, ino, start, len);
- if (ret)
- error("failed to add hole [%llu, %llu] in inode [%llu]",
- start, len, ino);
- else
- printf("Add a hole [%llu, %llu] in inode [%llu]\n", start, len,
- ino);
-
- btrfs_commit_transaction(trans, root);
- return ret;
-}
-
-/*
- * Check file extent datasum/hole, update the size of the file extents,
- * check and update the last offset of the file extent.
- *
- * @root: the root of fs/file tree.
- * @fkey: the key of the file extent.
- * @nodatasum: INODE_NODATASUM feature.
- * @size: the sum of all EXTENT_DATA items size for this inode.
- * @end: the offset of the last extent.
- *
- * Return 0 if no error occurred.
- */
-static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
- struct extent_buffer *node, int slot,
- unsigned int nodatasum, u64 *size, u64 *end)
-{
- struct btrfs_file_extent_item *fi;
- u64 disk_bytenr;
- u64 disk_num_bytes;
- u64 extent_num_bytes;
- u64 extent_offset;
- u64 csum_found; /* In byte size, sectorsize aligned */
- u64 search_start; /* Logical range start we search for csum */
- u64 search_len; /* Logical range len we search for csum */
- unsigned int extent_type;
- unsigned int is_hole;
- int compressed = 0;
- int ret;
- int err = 0;
-
- fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
-
- /* Check inline extent */
- extent_type = btrfs_file_extent_type(node, fi);
- if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
- struct btrfs_item *e = btrfs_item_nr(slot);
- u32 item_inline_len;
-
- item_inline_len = btrfs_file_extent_inline_item_len(node, e);
- extent_num_bytes = btrfs_file_extent_inline_len(node, slot, fi);
- compressed = btrfs_file_extent_compression(node, fi);
- if (extent_num_bytes == 0) {
- error(
- "root %llu EXTENT_DATA[%llu %llu] has empty inline extent",
- root->objectid, fkey->objectid, fkey->offset);
- err |= FILE_EXTENT_ERROR;
- }
- if (!compressed && extent_num_bytes != item_inline_len) {
- error(
- "root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u",
- root->objectid, fkey->objectid, fkey->offset,
- extent_num_bytes, item_inline_len);
- err |= FILE_EXTENT_ERROR;
- }
- *end += extent_num_bytes;
- *size += extent_num_bytes;
- return err;
- }
-
- /* Check extent type */
- if (extent_type != BTRFS_FILE_EXTENT_REG &&
- extent_type != BTRFS_FILE_EXTENT_PREALLOC) {
- err |= FILE_EXTENT_ERROR;
- error("root %llu EXTENT_DATA[%llu %llu] type bad",
- root->objectid, fkey->objectid, fkey->offset);
- return err;
- }
-
- /* Check REG_EXTENT/PREALLOC_EXTENT */
- disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
- disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
- extent_num_bytes = btrfs_file_extent_num_bytes(node, fi);
- extent_offset = btrfs_file_extent_offset(node, fi);
- compressed = btrfs_file_extent_compression(node, fi);
- is_hole = (disk_bytenr == 0) && (disk_num_bytes == 0);
-
- /*
- * Check EXTENT_DATA csum
- *
- * For plain (uncompressed) extent, we should only check the range
- * we're referring to, as it's possible that part of prealloc extent
- * has been written, and has csum:
- *
- * |<--- Original large preallocated extent A ---->|
- * |<- Prealloc File Extent ->|<- Regular Extent ->|
- * No csum Has csum
- *
- * For compressed extent, we should check the whole range.
- */
- if (!compressed) {
- search_start = disk_bytenr + extent_offset;
- search_len = extent_num_bytes;
- } else {
- search_start = disk_bytenr;
- search_len = disk_num_bytes;
- }
- ret = count_csum_range(root, search_start, search_len, &csum_found);
- if (csum_found > 0 && nodatasum) {
- err |= ODD_CSUM_ITEM;
- error("root %llu EXTENT_DATA[%llu %llu] nodatasum shouldn't have datasum",
- root->objectid, fkey->objectid, fkey->offset);
- } else if (extent_type == BTRFS_FILE_EXTENT_REG && !nodatasum &&
- !is_hole && (ret < 0 || csum_found < search_len)) {
- err |= CSUM_ITEM_MISSING;
- error("root %llu EXTENT_DATA[%llu %llu] csum missing, have: %llu, expected: %llu",
- root->objectid, fkey->objectid, fkey->offset,
- csum_found, search_len);
- } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && csum_found > 0) {
- err |= ODD_CSUM_ITEM;
- error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu",
- root->objectid, fkey->objectid, fkey->offset, csum_found);
- }
-
- /* Check EXTENT_DATA hole */
- if (!no_holes && *end != fkey->offset) {
- if (repair)
- ret = punch_extent_hole(root, fkey->objectid,
- *end, fkey->offset - *end);
- if (!repair || ret) {
- err |= FILE_EXTENT_ERROR;
- error(
-"root %llu EXTENT_DATA[%llu %llu] gap exists, expected: EXTENT_DATA[%llu %llu]",
- root->objectid, fkey->objectid, fkey->offset,
- fkey->objectid, *end);
- }
- }
-
- *end += extent_num_bytes;
- if (!is_hole)
- *size += extent_num_bytes;
-
- return err;
-}
-
-/*
- * Set inode item nbytes to @nbytes
- *
- * Returns 0 on success
- * Returns != 0 on error
- */
-static int repair_inode_nbytes_lowmem(struct btrfs_root *root,
- struct btrfs_path *path,
- u64 ino, u64 nbytes)
-{
- struct btrfs_trans_handle *trans;
- struct btrfs_inode_item *ii;
- struct btrfs_key key;
- struct btrfs_key research_key;
- int err = 0;
- int ret;
-
- btrfs_item_key_to_cpu(path->nodes[0], &research_key, path->slots[0]);
-
- key.objectid = ino;
- key.type = BTRFS_INODE_ITEM_KEY;
- key.offset = 0;
-
- trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- err |= ret;
- goto out;
- }
-
- btrfs_release_path(path);
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
- if (ret > 0)
- ret = -ENOENT;
- if (ret) {
- err |= ret;
- goto fail;
- }
-
- ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
- struct btrfs_inode_item);
- btrfs_set_inode_nbytes(path->nodes[0], ii, nbytes);
- btrfs_mark_buffer_dirty(path->nodes[0]);
-fail:
- btrfs_commit_transaction(trans, root);
-out:
- if (ret)
- error("failed to set nbytes in inode %llu root %llu",
- ino, root->root_key.objectid);
- else
- printf("Set nbytes in inode item %llu root %llu\n to %llu", ino,
- root->root_key.objectid, nbytes);
-
- /* research path */
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, &research_key, path, 0, 0);
- err |= ret;
-
- return err;
-}
-
-/*
- * Set directory inode isize to @isize.
- *
- * Returns 0 on success.
- * Returns != 0 on error.
- */
-static int repair_dir_isize_lowmem(struct btrfs_root *root,
- struct btrfs_path *path,
- u64 ino, u64 isize)
-{
- struct btrfs_trans_handle *trans;
- struct btrfs_inode_item *ii;
- struct btrfs_key key;
- struct btrfs_key research_key;
- int ret;
- int err = 0;
-
- btrfs_item_key_to_cpu(path->nodes[0], &research_key, path->slots[0]);
-
- key.objectid = ino;
- key.type = BTRFS_INODE_ITEM_KEY;
- key.offset = 0;
-
- trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- err |= ret;
- goto out;
- }
-
- btrfs_release_path(path);
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
- if (ret > 0)
- ret = -ENOENT;
- if (ret) {
- err |= ret;
- goto fail;
- }
-
- ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
- struct btrfs_inode_item);
- btrfs_set_inode_size(path->nodes[0], ii, isize);
- btrfs_mark_buffer_dirty(path->nodes[0]);
-fail:
- btrfs_commit_transaction(trans, root);
-out:
- if (ret)
- error("failed to set isize in inode %llu root %llu",
- ino, root->root_key.objectid);
- else
- printf("Set isize in inode %llu root %llu to %llu\n",
- ino, root->root_key.objectid, isize);
-
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, &research_key, path, 0, 0);
- err |= ret;
-
- return err;
-}
-
-/*
- * Wrapper function for btrfs_add_orphan_item().
- *
- * Returns 0 on success.
- * Returns != 0 on error.
- */
-static int repair_inode_orphan_item_lowmem(struct btrfs_root *root,
- struct btrfs_path *path, u64 ino)
-{
- struct btrfs_trans_handle *trans;
- struct btrfs_key research_key;
- int ret;
- int err = 0;
-
- btrfs_item_key_to_cpu(path->nodes[0], &research_key, path->slots[0]);
-
- trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- err |= ret;
- goto out;
- }
-
- btrfs_release_path(path);
- ret = btrfs_add_orphan_item(trans, root, path, ino);
- err |= ret;
- btrfs_commit_transaction(trans, root);
-out:
- if (ret)
- error("failed to add inode %llu as orphan item root %llu",
- ino, root->root_key.objectid);
- else
- printf("Added inode %llu as orphan item root %llu\n",
- ino, root->root_key.objectid);
-
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, &research_key, path, 0, 0);
- err |= ret;
-
- return err;
-}
-
-/* Set inode_item nlink to @ref_count.
- * If @ref_count == 0, move it to "lost+found" and increase @ref_count.
- *
- * Returns 0 on success
- */
-static int repair_inode_nlinks_lowmem(struct btrfs_root *root,
- struct btrfs_path *path, u64 ino,
- const char *name, u32 namelen,
- u64 ref_count, u8 filetype, u64 *nlink)
-{
- struct btrfs_trans_handle *trans;
- struct btrfs_inode_item *ii;
- struct btrfs_key key;
- struct btrfs_key old_key;
- char namebuf[BTRFS_NAME_LEN] = {0};
- int name_len;
- int ret;
- int ret2;
-
- /* save the key */
- btrfs_item_key_to_cpu(path->nodes[0], &old_key, path->slots[0]);
-
- if (name && namelen) {
- ASSERT(namelen <= BTRFS_NAME_LEN);
- memcpy(namebuf, name, namelen);
- name_len = namelen;
- } else {
- sprintf(namebuf, "%llu", ino);
- name_len = count_digits(ino);
- printf("Can't find file name for inode %llu, use %s instead\n",
- ino, namebuf);
- }
-
- trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- goto out;
- }
-
- btrfs_release_path(path);
- /* if refs is 0, put it into lostfound */
- if (ref_count == 0) {
- ret = link_inode_to_lostfound(trans, root, path, ino, namebuf,
- name_len, filetype, &ref_count);
- if (ret)
- goto fail;
- }
-
- /* reset inode_item's nlink to ref_count */
- key.objectid = ino;
- key.type = BTRFS_INODE_ITEM_KEY;
- key.offset = 0;
-
- btrfs_release_path(path);
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
- if (ret > 0)
- ret = -ENOENT;
- if (ret)
- goto fail;
-
- ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
- struct btrfs_inode_item);
- btrfs_set_inode_nlink(path->nodes[0], ii, ref_count);
- btrfs_mark_buffer_dirty(path->nodes[0]);
-
- if (nlink)
- *nlink = ref_count;
-fail:
- btrfs_commit_transaction(trans, root);
-out:
- if (ret)
- error(
- "fail to repair nlink of inode %llu root %llu name %s filetype %u",
- root->objectid, ino, namebuf, filetype);
- else
- printf("Fixed nlink of inode %llu root %llu name %s filetype %u\n",
- root->objectid, ino, namebuf, filetype);
-
- /* research */
- btrfs_release_path(path);
- ret2 = btrfs_search_slot(NULL, root, &old_key, path, 0, 0);
- if (ret2 < 0)
- return ret |= ret2;
- return ret;
-}
-
-/*
- * Check INODE_ITEM and related ITEMs (the same inode number)
- * 1. check link count
- * 2. check inode ref/extref
- * 3. check dir item/index
- *
- * @ext_ref: the EXTENDED_IREF feature
- *
- * Return 0 if no error occurred.
- * Return >0 for error or hit the traversal is done(by error bitmap)
- */
-static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
- unsigned int ext_ref)
-{
- struct extent_buffer *node;
- struct btrfs_inode_item *ii;
- struct btrfs_key key;
- struct btrfs_key last_key;
- u64 inode_id;
- u32 mode;
- u64 nlink;
- u64 nbytes;
- u64 isize;
- u64 size = 0;
- u64 refs = 0;
- u64 extent_end = 0;
- u64 extent_size = 0;
- unsigned int dir;
- unsigned int nodatasum;
- int slot;
- int ret;
- int err = 0;
- char namebuf[BTRFS_NAME_LEN] = {0};
- u32 name_len = 0;
-
- node = path->nodes[0];
- slot = path->slots[0];
-
- btrfs_item_key_to_cpu(node, &key, slot);
- inode_id = key.objectid;
-
- if (inode_id == BTRFS_ORPHAN_OBJECTID) {
- ret = btrfs_next_item(root, path);
- if (ret > 0)
- err |= LAST_ITEM;
- return err;
- }
-
- ii = btrfs_item_ptr(node, slot, struct btrfs_inode_item);
- isize = btrfs_inode_size(node, ii);
- nbytes = btrfs_inode_nbytes(node, ii);
- mode = btrfs_inode_mode(node, ii);
- dir = imode_to_type(mode) == BTRFS_FT_DIR;
- nlink = btrfs_inode_nlink(node, ii);
- nodatasum = btrfs_inode_flags(node, ii) & BTRFS_INODE_NODATASUM;
-
- while (1) {
- btrfs_item_key_to_cpu(path->nodes[0], &last_key, path->slots[0]);
- ret = btrfs_next_item(root, path);
- if (ret < 0) {
- /* out will fill 'err' rusing current statistics */
- goto out;
- } else if (ret > 0) {
- err |= LAST_ITEM;
- goto out;
- }
-
- node = path->nodes[0];
- slot = path->slots[0];
- btrfs_item_key_to_cpu(node, &key, slot);
- if (key.objectid != inode_id)
- goto out;
-
- switch (key.type) {
- case BTRFS_INODE_REF_KEY:
- ret = check_inode_ref(root, &key, path, namebuf,
- &name_len, &refs, mode);
- err |= ret;
- break;
- case BTRFS_INODE_EXTREF_KEY:
- if (key.type == BTRFS_INODE_EXTREF_KEY && !ext_ref)
- warning("root %llu EXTREF[%llu %llu] isn't supported",
- root->objectid, key.objectid,
- key.offset);
- ret = check_inode_extref(root, &key, node, slot, &refs,
- mode);
- err |= ret;
- break;
- case BTRFS_DIR_ITEM_KEY:
- case BTRFS_DIR_INDEX_KEY:
- if (!dir) {
- warning("root %llu INODE[%llu] mode %u shouldn't have DIR_INDEX[%llu %llu]",
- root->objectid, inode_id,
- imode_to_type(mode), key.objectid,
- key.offset);
- }
- ret = check_dir_item(root, &key, path, &size, ext_ref);
- err |= ret;
- break;
- case BTRFS_EXTENT_DATA_KEY:
- if (dir) {
- warning("root %llu DIR INODE[%llu] shouldn't EXTENT_DATA[%llu %llu]",
- root->objectid, inode_id, key.objectid,
- key.offset);
- }
- ret = check_file_extent(root, &key, node, slot,
- nodatasum, &extent_size,
- &extent_end);
- err |= ret;
- break;
- case BTRFS_XATTR_ITEM_KEY:
- break;
- default:
- error("ITEM[%llu %u %llu] UNKNOWN TYPE",
- key.objectid, key.type, key.offset);
- }
- }
-
-out:
- if (err & LAST_ITEM) {
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, &last_key, path, 0, 0);
- if (ret)
- return err;
- }
-
- /* verify INODE_ITEM nlink/isize/nbytes */
- if (dir) {
- if (repair && (err & DIR_COUNT_AGAIN)) {
- err &= ~DIR_COUNT_AGAIN;
- count_dir_isize(root, inode_id, &size);
- }
-
- if ((nlink != 1 || refs != 1) && repair) {
- ret = repair_inode_nlinks_lowmem(root, path, inode_id,
- namebuf, name_len, refs, imode_to_type(mode),
- &nlink);
- }
-
- if (nlink != 1) {
- err |= LINK_COUNT_ERROR;
- error("root %llu DIR INODE[%llu] shouldn't have more than one link(%llu)",
- root->objectid, inode_id, nlink);
- }
-
- /*
- * Just a warning, as dir inode nbytes is just an
- * instructive value.
- */
- if (!IS_ALIGNED(nbytes, root->fs_info->nodesize)) {
- warning("root %llu DIR INODE[%llu] nbytes should be aligned to %u",
- root->objectid, inode_id,
- root->fs_info->nodesize);
- }
-
- if (isize != size) {
- if (repair)
- ret = repair_dir_isize_lowmem(root, path,
- inode_id, size);
- if (!repair || ret) {
- err |= ISIZE_ERROR;
- error(
- "root %llu DIR INODE [%llu] size %llu not equal to %llu",
- root->objectid, inode_id, isize, size);
- }
- }
- } else {
- if (nlink != refs) {
- if (repair)
- ret = repair_inode_nlinks_lowmem(root, path,
- inode_id, namebuf, name_len, refs,
- imode_to_type(mode), &nlink);
- if (!repair || ret) {
- err |= LINK_COUNT_ERROR;
- error(
- "root %llu INODE[%llu] nlink(%llu) not equal to inode_refs(%llu)",
- root->objectid, inode_id, nlink, refs);
- }
- } else if (!nlink) {
- if (repair)
- ret = repair_inode_orphan_item_lowmem(root,
- path, inode_id);
- if (!repair || ret) {
- err |= ORPHAN_ITEM;
- error("root %llu INODE[%llu] is orphan item",
- root->objectid, inode_id);
- }
- }
-
- if (!nbytes && !no_holes && extent_end < isize) {
- if (repair)
- ret = punch_extent_hole(root, inode_id,
- extent_end, isize - extent_end);
- if (!repair || ret) {
- err |= NBYTES_ERROR;
- error(
- "root %llu INODE[%llu] size %llu should have a file extent hole",
- root->objectid, inode_id, isize);
- }
- }
-
- if (nbytes != extent_size) {
- if (repair)
- ret = repair_inode_nbytes_lowmem(root, path,
- inode_id, extent_size);
- if (!repair || ret) {
- err |= NBYTES_ERROR;
- error(
- "root %llu INODE[%llu] nbytes %llu not equal to extent_size %llu",
- root->objectid, inode_id, nbytes,
- extent_size);
- }
- }
- }
-
- if (err & LAST_ITEM)
- btrfs_next_item(root, path);
- return err;
-}
-
-/*
- * Insert the missing inode item and inode ref.
- *
- * Normal INODE_ITEM_MISSING and INODE_REF_MISSING are handled in backref * dir.
- * Root dir should be handled specially because root dir is the root of fs.
- *
- * returns err (>0 or 0) after repair
- */
-static int repair_fs_first_inode(struct btrfs_root *root, int err)
-{
- struct btrfs_trans_handle *trans;
- struct btrfs_key key;
- struct btrfs_path path;
- int filetype = BTRFS_FT_DIR;
- int ret = 0;
-
- btrfs_init_path(&path);
-
- if (err & INODE_REF_MISSING) {
- key.objectid = BTRFS_FIRST_FREE_OBJECTID;
- key.type = BTRFS_INODE_REF_KEY;
- key.offset = BTRFS_FIRST_FREE_OBJECTID;
-
- trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- goto out;
- }
-
- btrfs_release_path(&path);
- ret = btrfs_search_slot(trans, root, &key, &path, 1, 1);
- if (ret)
- goto trans_fail;
-
- ret = btrfs_insert_inode_ref(trans, root, "..", 2,
- BTRFS_FIRST_FREE_OBJECTID,
- BTRFS_FIRST_FREE_OBJECTID, 0);
- if (ret)
- goto trans_fail;
-
- printf("Add INODE_REF[%llu %llu] name %s\n",
- BTRFS_FIRST_FREE_OBJECTID, BTRFS_FIRST_FREE_OBJECTID,
- "..");
- err &= ~INODE_REF_MISSING;
-trans_fail:
- if (ret)
- error("fail to insert first inode's ref");
- btrfs_commit_transaction(trans, root);
- }
-
- if (err & INODE_ITEM_MISSING) {
- ret = repair_inode_item_missing(root,
- BTRFS_FIRST_FREE_OBJECTID, filetype);
- if (ret)
- goto out;
- err &= ~INODE_ITEM_MISSING;
- }
-out:
- if (ret)
- error("fail to repair first inode");
- btrfs_release_path(&path);
- return err;
-}
-
-/*
- * check first root dir's inode_item and inode_ref
- *
- * returns 0 means no error
- * returns >0 means error
- * returns <0 means fatal error
- */
-static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
-{
- struct btrfs_path path;
- struct btrfs_key key;
- struct btrfs_inode_item *ii;
- u64 index;
- u32 mode;
- int err = 0;
- int ret;
-
- key.objectid = BTRFS_FIRST_FREE_OBJECTID;
- key.type = BTRFS_INODE_ITEM_KEY;
- key.offset = 0;
-
- /* For root being dropped, we don't need to check first inode */
- if (btrfs_root_refs(&root->root_item) == 0 &&
- btrfs_disk_key_objectid(&root->root_item.drop_progress) >=
- BTRFS_FIRST_FREE_OBJECTID)
- return 0;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
- if (ret < 0)
- goto out;
- if (ret > 0) {
- ret = 0;
- err |= INODE_ITEM_MISSING;
- } else {
- ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
- struct btrfs_inode_item);
- mode = btrfs_inode_mode(path.nodes[0], ii);
- if (imode_to_type(mode) != BTRFS_FT_DIR)
- err |= INODE_ITEM_MISMATCH;
- }
-
- /* lookup first inode ref */
- key.offset = BTRFS_FIRST_FREE_OBJECTID;
- key.type = BTRFS_INODE_REF_KEY;
- /* special index value */
- index = 0;
-
- ret = find_inode_ref(root, &key, "..", strlen(".."), &index, ext_ref);
- if (ret < 0)
- goto out;
- err |= ret;
-
-out:
- btrfs_release_path(&path);
-
- if (err && repair)
- err = repair_fs_first_inode(root, err);
-
- if (err & (INODE_ITEM_MISSING | INODE_ITEM_MISMATCH))
- error("root dir INODE_ITEM is %s",
- err & INODE_ITEM_MISMATCH ? "mismatch" : "missing");
- if (err & INODE_REF_MISSING)
- error("root dir INODE_REF is missing");
-
- return ret < 0 ? ret : err;
-}
-
static struct tree_backref *find_tree_backref(struct extent_record *rec,
u64 parent, u64 root)
{
@@ -6546,278 +3455,6 @@ static struct data_backref *find_data_backref(struct extent_record *rec,
return back;
}
-/*
- * This function calls walk_down_tree_v2 and walk_up_tree_v2 to check tree
- * blocks and integrity of fs tree items.
- *
- * @root: the root of the tree to be checked.
- * @ext_ref feature EXTENDED_IREF is enable or not.
- * @account if NOT 0 means check the tree (including tree)'s treeblocks.
- * otherwise means check fs tree(s) items relationship and
- * @root MUST be a fs tree root.
- * Returns 0 represents OK.
- * Returns not 0 represents error.
- */
-static int check_btrfs_root(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, unsigned int ext_ref,
- int check_all)
-
-{
- struct btrfs_path path;
- struct node_refs nrefs;
- struct btrfs_root_item *root_item = &root->root_item;
- int ret;
- int level;
- int err = 0;
-
- memset(&nrefs, 0, sizeof(nrefs));
- if (!check_all) {
- /*
- * We need to manually check the first inode item (256)
- * As the following traversal function will only start from
- * the first inode item in the leaf, if inode item (256) is
- * missing we will skip it forever.
- */
- ret = check_fs_first_inode(root, ext_ref);
- if (ret < 0)
- return ret;
- }
-
-
- level = btrfs_header_level(root->node);
- btrfs_init_path(&path);
-
- if (btrfs_root_refs(root_item) > 0 ||
- btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
- path.nodes[level] = root->node;
- path.slots[level] = 0;
- extent_buffer_get(root->node);
- } else {
- struct btrfs_key key;
-
- btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
- level = root_item->drop_level;
- path.lowest_level = level;
- ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
- if (ret < 0)
- goto out;
- ret = 0;
- }
-
- while (1) {
- ret = walk_down_tree_v2(trans, root, &path, &level, &nrefs,
- ext_ref, check_all);
-
- err |= !!ret;
-
- /* if ret is negative, walk shall stop */
- if (ret < 0) {
- ret = err;
- break;
- }
-
- ret = walk_up_tree_v2(root, &path, &level);
- if (ret != 0) {
- /* Normal exit, reset ret to err */
- ret = err;
- break;
- }
- }
-
-out:
- btrfs_release_path(&path);
- return ret;
-}
-
-/*
- * Iterate all items in the tree and call check_inode_item() to check.
- *
- * @root: the root of the tree to be checked.
- * @ext_ref: the EXTENDED_IREF feature
- *
- * Return 0 if no error found.
- * Return <0 for error.
- */
-static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
-{
- reset_cached_block_groups(root->fs_info);
- return check_btrfs_root(NULL, root, ext_ref, 0);
-}
-
-/*
- * Find the relative ref for root_ref and root_backref.
- *
- * @root: the root of the root tree.
- * @ref_key: the key of the root ref.
- *
- * Return 0 if no error occurred.
- */
-static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
- struct extent_buffer *node, int slot)
-{
- struct btrfs_path path;
- struct btrfs_key key;
- struct btrfs_root_ref *ref;
- struct btrfs_root_ref *backref;
- char ref_name[BTRFS_NAME_LEN] = {0};
- char backref_name[BTRFS_NAME_LEN] = {0};
- u64 ref_dirid;
- u64 ref_seq;
- u32 ref_namelen;
- u64 backref_dirid;
- u64 backref_seq;
- u32 backref_namelen;
- u32 len;
- int ret;
- int err = 0;
-
- ref = btrfs_item_ptr(node, slot, struct btrfs_root_ref);
- ref_dirid = btrfs_root_ref_dirid(node, ref);
- ref_seq = btrfs_root_ref_sequence(node, ref);
- ref_namelen = btrfs_root_ref_name_len(node, ref);
-
- if (ref_namelen <= BTRFS_NAME_LEN) {
- len = ref_namelen;
- } else {
- len = BTRFS_NAME_LEN;
- warning("%s[%llu %llu] ref_name too long",
- ref_key->type == BTRFS_ROOT_REF_KEY ?
- "ROOT_REF" : "ROOT_BACKREF", ref_key->objectid,
- ref_key->offset);
- }
- read_extent_buffer(node, ref_name, (unsigned long)(ref + 1), len);
-
- /* Find relative root_ref */
- key.objectid = ref_key->offset;
- key.type = BTRFS_ROOT_BACKREF_KEY + BTRFS_ROOT_REF_KEY - ref_key->type;
- key.offset = ref_key->objectid;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
- if (ret) {
- err |= ROOT_REF_MISSING;
- error("%s[%llu %llu] couldn't find relative ref",
- ref_key->type == BTRFS_ROOT_REF_KEY ?
- "ROOT_REF" : "ROOT_BACKREF",
- ref_key->objectid, ref_key->offset);
- goto out;
- }
-
- backref = btrfs_item_ptr(path.nodes[0], path.slots[0],
- struct btrfs_root_ref);
- backref_dirid = btrfs_root_ref_dirid(path.nodes[0], backref);
- backref_seq = btrfs_root_ref_sequence(path.nodes[0], backref);
- backref_namelen = btrfs_root_ref_name_len(path.nodes[0], backref);
-
- if (backref_namelen <= BTRFS_NAME_LEN) {
- len = backref_namelen;
- } else {
- len = BTRFS_NAME_LEN;
- warning("%s[%llu %llu] ref_name too long",
- key.type == BTRFS_ROOT_REF_KEY ?
- "ROOT_REF" : "ROOT_BACKREF",
- key.objectid, key.offset);
- }
- read_extent_buffer(path.nodes[0], backref_name,
- (unsigned long)(backref + 1), len);
-
- if (ref_dirid != backref_dirid || ref_seq != backref_seq ||
- ref_namelen != backref_namelen ||
- strncmp(ref_name, backref_name, len)) {
- err |= ROOT_REF_MISMATCH;
- error("%s[%llu %llu] mismatch relative ref",
- ref_key->type == BTRFS_ROOT_REF_KEY ?
- "ROOT_REF" : "ROOT_BACKREF",
- ref_key->objectid, ref_key->offset);
- }
-out:
- btrfs_release_path(&path);
- return err;
-}
-
-/*
- * Check all fs/file tree in low_memory mode.
- *
- * 1. for fs tree root item, call check_fs_root_v2()
- * 2. for fs tree root ref/backref, call check_root_ref()
- *
- * Return 0 if no error occurred.
- */
-static int check_fs_roots_v2(struct btrfs_fs_info *fs_info)
-{
- struct btrfs_root *tree_root = fs_info->tree_root;
- struct btrfs_root *cur_root = NULL;
- struct btrfs_path path;
- struct btrfs_key key;
- struct extent_buffer *node;
- unsigned int ext_ref;
- int slot;
- int ret;
- int err = 0;
-
- ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF);
-
- btrfs_init_path(&path);
- key.objectid = BTRFS_FS_TREE_OBJECTID;
- key.offset = 0;
- key.type = BTRFS_ROOT_ITEM_KEY;
-
- ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
- if (ret < 0) {
- err = ret;
- goto out;
- } else if (ret > 0) {
- err = -ENOENT;
- goto out;
- }
-
- while (1) {
- node = path.nodes[0];
- slot = path.slots[0];
- btrfs_item_key_to_cpu(node, &key, slot);
- if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
- goto out;
- if (key.type == BTRFS_ROOT_ITEM_KEY &&
- fs_root_objectid(key.objectid)) {
- if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
- cur_root = btrfs_read_fs_root_no_cache(fs_info,
- &key);
- } else {
- key.offset = (u64)-1;
- cur_root = btrfs_read_fs_root(fs_info, &key);
- }
-
- if (IS_ERR(cur_root)) {
- error("Fail to read fs/subvol tree: %lld",
- key.objectid);
- err = -EIO;
- goto next;
- }
-
- ret = check_fs_root_v2(cur_root, ext_ref);
- err |= ret;
-
- if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
- btrfs_free_fs_root(cur_root);
- } else if (key.type == BTRFS_ROOT_REF_KEY ||
- key.type == BTRFS_ROOT_BACKREF_KEY) {
- ret = check_root_ref(tree_root, &key, node, slot);
- err |= ret;
- }
-next:
- ret = btrfs_next_item(tree_root, &path);
- if (ret > 0)
- goto out;
- if (ret < 0) {
- err = ret;
- goto out;
- }
- }
-
-out:
- btrfs_release_path(&path);
- return err;
-}
static int do_check_fs_roots(struct btrfs_fs_info *fs_info,
struct cache_tree *root_cache)
@@ -6827,7 +3464,7 @@ static int do_check_fs_roots(struct btrfs_fs_info *fs_info,
if (!ctx.progress_enabled)
fprintf(stderr, "checking fs roots\n");
if (check_mode == CHECK_MODE_LOWMEM)
- ret = check_fs_roots_v2(fs_info);
+ ret = check_fs_roots_lowmem(fs_info);
else
ret = check_fs_roots(fs_info, root_cache);
@@ -6850,22 +3487,21 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
goto out;
if (back->is_data) {
dback = to_data_backref(back);
- fprintf(stderr, "Data backref %llu %s %llu"
- " owner %llu offset %llu num_refs %lu"
- " not found in extent tree\n",
+ fprintf(stderr,
+"data 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->parent :
(unsigned long long)dback->root,
(unsigned long long)dback->owner,
(unsigned long long)dback->offset,
(unsigned long)dback->num_refs);
} else {
tback = to_tree_backref(back);
- fprintf(stderr, "Tree backref %llu parent %llu"
- " root %llu not found in extent tree\n",
+ fprintf(stderr,
+"tree 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);
@@ -6876,7 +3512,8 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
if (!print_errs)
goto out;
tback = to_tree_backref(back);
- fprintf(stderr, "Backref %llu %s %llu not referenced back %p\n",
+ fprintf(stderr,
+ "backref %llu %s %llu not referenced back %p\n",
(unsigned long long)rec->start,
back->full_backref ? "parent" : "root",
back->full_backref ?
@@ -6889,26 +3526,25 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
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 back %p\n",
+ fprintf(stderr,
+"incorrect local backref count on %llu %s %llu owner %llu offset %llu found %u wanted %u back %p\n",
(unsigned long long)rec->start,
back->full_backref ?
"parent" : "root",
back->full_backref ?
- (unsigned long long)dback->parent:
+ (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, back);
+ dback->found_ref, dback->num_refs,
+ back);
}
if (dback->disk_bytenr != rec->start) {
err = 1;
if (!print_errs)
goto out;
- fprintf(stderr, "Backref disk bytenr does not"
- " match extent record, bytenr=%llu, "
- "ref bytenr=%llu\n",
+ fprintf(stderr,
+"backref disk bytenr does not match extent record, bytenr=%llu, ref bytenr=%llu\n",
(unsigned long long)rec->start,
(unsigned long long)dback->disk_bytenr);
}
@@ -6917,9 +3553,8 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
err = 1;
if (!print_errs)
goto out;
- fprintf(stderr, "Backref bytes do not match "
- "extent backref, bytenr=%llu, ref "
- "bytes=%llu, backref bytes=%llu\n",
+ fprintf(stderr,
+"backref bytes do not match extent backref, bytenr=%llu, ref bytes=%llu, backref bytes=%llu\n",
(unsigned long long)rec->start,
(unsigned long long)rec->nr,
(unsigned long long)dback->bytes);
@@ -6936,8 +3571,8 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
err = 1;
if (!print_errs)
goto out;
- fprintf(stderr, "Incorrect global backref count "
- "on %llu found %llu wanted %llu\n",
+ 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);
@@ -7107,6 +3742,7 @@ static int swap_values(struct btrfs_root *root, struct btrfs_path *path,
sizeof(struct btrfs_key_ptr));
if (slot == 0) {
struct btrfs_disk_key key;
+
btrfs_node_key(buf, &key, 0);
btrfs_fixup_low_keys(root, path, &key,
btrfs_header_level(buf) + 1);
@@ -7233,18 +3869,18 @@ again:
unsigned int shift = 0, offset;
if (i == 0 && btrfs_item_end_nr(buf, i) !=
- BTRFS_LEAF_DATA_SIZE(root)) {
+ BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
if (btrfs_item_end_nr(buf, i) >
- BTRFS_LEAF_DATA_SIZE(root)) {
+ BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
ret = delete_bogus_item(root, path, buf, i);
if (!ret)
goto again;
- fprintf(stderr, "item is off the end of the "
- "leaf, can't fix\n");
+ fprintf(stderr,
+ "item is off the end of the leaf, can't fix\n");
ret = -EIO;
break;
}
- shift = BTRFS_LEAF_DATA_SIZE(root) -
+ shift = BTRFS_LEAF_DATA_SIZE(root->fs_info) -
btrfs_item_end_nr(buf, i);
} else if (i > 0 && btrfs_item_end_nr(buf, i) !=
btrfs_item_offset_nr(buf, i - 1)) {
@@ -7428,7 +4064,7 @@ static struct tree_backref *find_tree_backref(struct extent_record *rec,
struct extent_backref *node;
struct tree_backref *back;
- while(cur != &rec->backrefs) {
+ while (cur != &rec->backrefs) {
node = to_extent_backref(cur);
cur = cur->next;
if (node->is_data)
@@ -7480,7 +4116,7 @@ static struct data_backref *find_data_backref(struct extent_record *rec,
struct extent_backref *node;
struct data_backref *back;
- while(cur != &rec->backrefs) {
+ while (cur != &rec->backrefs) {
node = to_extent_backref(cur);
cur = cur->next;
if (!node->is_data)
@@ -7706,12 +4342,13 @@ static int add_extent_rec(struct cache_tree *extent_cache,
if (tmpl->extent_item_refs && !dup) {
if (rec->extent_item_refs) {
- fprintf(stderr, "block %llu rec "
- "extent_item_refs %llu, passed %llu\n",
+ fprintf(stderr,
+ "block %llu rec extent_item_refs %llu, passed %llu\n",
(unsigned long long)tmpl->start,
(unsigned long long)
rec->extent_item_refs,
- (unsigned long long)tmpl->extent_item_refs);
+ (unsigned long long)
+ tmpl->extent_item_refs);
}
rec->extent_item_refs = tmpl->extent_item_refs;
}
@@ -7795,8 +4432,8 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
if (found_ref) {
if (back->node.found_ref) {
- fprintf(stderr, "Extent back ref already exists "
- "for %llu parent %llu root %llu \n",
+ 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);
@@ -7804,8 +4441,8 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
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",
+ 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);
@@ -7892,9 +4529,8 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
rec->owner_ref_checked = 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",
+ 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,
@@ -7917,6 +4553,7 @@ static int add_pending(struct cache_tree *pending,
struct cache_tree *seen, u64 bytenr, u32 size)
{
int ret;
+
ret = add_cache_extent(seen, bytenr, size);
if (ret)
return ret;
@@ -7950,17 +4587,17 @@ static int pick_next_pending(struct cache_tree *pending,
cache = search_cache_extent(nodes, 0);
if (!cache) {
- cache = search_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;
+ cache = search_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;
@@ -7974,8 +4611,9 @@ static int pick_next_pending(struct cache_tree *pending,
if (bits_nr - ret > 8) {
u64 lookup = bits[0].start + bits[0].size;
struct cache_extent *next;
+
next = search_cache_extent(pending, lookup);
- while(next) {
+ while (next) {
if (next->start - lookup > 32768)
break;
bits[ret].start = next->start;
@@ -8366,7 +5004,14 @@ static int process_extent_item(struct btrfs_root *root,
if (item_size < sizeof(*ei)) {
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
struct btrfs_extent_item_v0 *ei0;
- BUG_ON(item_size != sizeof(*ei0));
+
+ if (item_size != sizeof(*ei0)) {
+ error(
+ "invalid extent item format: ITEM[%llu %u %llu] leaf: %llu slot: %d",
+ key.objectid, key.type, key.offset,
+ btrfs_header_bytenr(eb), slot);
+ BUG();
+ }
ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0);
refs = btrfs_extent_refs_v0(eb, ei0);
#else
@@ -8454,7 +5099,8 @@ static int process_extent_item(struct btrfs_root *root,
0, num_bytes);
break;
default:
- fprintf(stderr, "corrupt extent record: key %Lu %u %Lu\n",
+ fprintf(stderr,
+ "corrupt extent record: key [%llu,%u,%llu]\n",
key.objectid, key.type, num_bytes);
goto out;
}
@@ -8537,19 +5183,19 @@ static int check_cache_range(struct btrfs_root *root,
entry = btrfs_find_free_space(cache->free_space_ctl, offset, bytes);
if (!entry) {
- fprintf(stderr, "There is no free space entry for %Lu-%Lu\n",
+ fprintf(stderr, "there is no free space entry for %llu-%llu\n",
offset, offset+bytes);
return -EINVAL;
}
if (entry->offset != offset) {
- fprintf(stderr, "Wanted offset %Lu, found %Lu\n", offset,
+ fprintf(stderr, "wanted offset %llu, found %llu\n", offset,
entry->offset);
return -EINVAL;
}
if (entry->bytes != bytes) {
- fprintf(stderr, "Wanted bytes %Lu, found %Lu for off %Lu\n",
+ fprintf(stderr, "wanted bytes %llu, found %llu for off %llu\n",
bytes, entry->bytes, offset);
return -EINVAL;
}
@@ -8699,7 +5345,7 @@ static int check_space_cache(struct btrfs_root *root)
ret = verify_space_cache(root, cache);
if (ret) {
- fprintf(stderr, "cache appears valid but isn't %Lu\n",
+ fprintf(stderr, "cache appears valid but isn't %llu\n",
cache->key.objectid);
error++;
}
@@ -8712,8 +5358,8 @@ static int check_space_cache(struct btrfs_root *root)
static int check_extent_csums(struct btrfs_root *root, u64 bytenr,
u64 num_bytes, unsigned long leaf_offset,
- struct extent_buffer *eb) {
-
+ struct extent_buffer *eb)
+{
struct btrfs_fs_info *fs_info = root->fs_info;
u64 offset = 0;
u16 csum_size = btrfs_super_csum_size(fs_info->super_copy);
@@ -8909,8 +5555,9 @@ again:
out:
if (num_bytes && !ret) {
- fprintf(stderr, "There are no extents for csum range "
- "%Lu-%Lu\n", bytenr, bytenr+num_bytes);
+ fprintf(stderr,
+ "there are no extents for csum range %llu-%llu\n",
+ bytenr, bytenr+num_bytes);
ret = 1;
}
@@ -8985,8 +5632,8 @@ skip_csum_check:
} else if (key.offset != offset + num_bytes) {
ret = check_extent_exists(root, offset, num_bytes);
if (ret) {
- fprintf(stderr, "Csum exists for %Lu-%Lu but "
- "there is no extent record\n",
+ fprintf(stderr,
+ "csum exists for %llu-%llu but there is no extent record\n",
offset, offset+num_bytes);
errors++;
}
@@ -9002,7 +5649,8 @@ skip_csum_check:
}
static int is_dropped_key(struct btrfs_key *key,
- struct btrfs_key *drop_key) {
+ struct btrfs_key *drop_key)
+{
if (key->objectid < drop_key->objectid)
return 1;
else if (key->objectid == drop_key->objectid) {
@@ -9179,7 +5827,7 @@ static int run_next_block(struct btrfs_root *root,
return 1;
if (!reada_bits) {
- for(i = 0; i < nritems; i++) {
+ for (i = 0; i < nritems; i++) {
ret = add_cache_extent(reada, bits[i].start,
bits[i].size);
if (ret == -EEXIST)
@@ -9289,6 +5937,7 @@ static int run_next_block(struct btrfs_root *root,
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);
/*
* Check key type against the leaf owner.
@@ -9363,6 +6012,7 @@ static int run_next_block(struct btrfs_root *root,
}
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,
@@ -9377,6 +6027,7 @@ static int run_next_block(struct btrfs_root *root,
}
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,
@@ -9414,9 +6065,9 @@ static int run_next_block(struct btrfs_root *root,
data_bytes_allocated +=
btrfs_file_extent_disk_num_bytes(buf, fi);
- if (data_bytes_allocated < root->fs_info->sectorsize) {
+ if (data_bytes_allocated < root->fs_info->sectorsize)
abort();
- }
+
data_bytes_referenced +=
btrfs_file_extent_num_bytes(buf, fi);
add_data_backref(extent_cache,
@@ -9449,7 +6100,8 @@ static int run_next_block(struct btrfs_root *root,
memset(&tmpl, 0, sizeof(tmpl));
btrfs_cpu_key_to_disk(&tmpl.parent_key, &key);
- tmpl.parent_generation = btrfs_node_ptr_generation(buf, i);
+ tmpl.parent_generation =
+ btrfs_node_ptr_generation(buf, i);
tmpl.start = ptr;
tmpl.nr = size;
tmpl.refs = 1;
@@ -9468,13 +6120,12 @@ static int run_next_block(struct btrfs_root *root,
continue;
}
- if (level > 1) {
+ if (level > 1)
add_pending(nodes, seen, ptr, size);
- } else {
+ else
add_pending(pending, seen, ptr, size);
- }
}
- btree_space_waste += (BTRFS_NODEPTRS_PER_BLOCK(root) -
+ btree_space_waste += (BTRFS_NODEPTRS_PER_BLOCK(fs_info) -
nritems) * sizeof(struct btrfs_key_ptr);
}
total_btree_bytes += buf->len;
@@ -9544,6 +6195,7 @@ static int free_extent_hook(struct btrfs_trans_handle *trans,
rec = container_of(cache, struct extent_record, cache);
if (is_data) {
struct data_backref *back;
+
back = find_data_backref(rec, parent, root_objectid, owner,
offset, 1, bytenr, num_bytes);
if (!back)
@@ -9569,6 +6221,7 @@ static int free_extent_hook(struct btrfs_trans_handle *trans,
}
} else {
struct tree_backref *back;
+
back = find_tree_backref(rec, parent, root_objectid);
if (!back)
goto out;
@@ -9608,7 +6261,7 @@ static int delete_extent_records(struct btrfs_trans_handle *trans,
key.type = (u8)-1;
key.offset = (u64)-1;
- while(1) {
+ while (1) {
ret = btrfs_search_slot(trans, root->fs_info->extent_root,
&key, path, 0, 1);
if (ret < 0)
@@ -9648,7 +6301,8 @@ static int delete_extent_records(struct btrfs_trans_handle *trans,
continue;
}
- fprintf(stderr, "repair deleting extent record: key %Lu %u %Lu\n",
+ fprintf(stderr,
+ "repair deleting extent record: key [%llu,%u,%llu]\n",
found_key.objectid, found_key.type, found_key.offset);
ret = btrfs_del_item(trans, root->fs_info->extent_root, path);
@@ -9661,7 +6315,7 @@ static int delete_extent_records(struct btrfs_trans_handle *trans,
u64 bytes = (found_key.type == BTRFS_EXTENT_ITEM_KEY) ?
found_key.offset : root->fs_info->nodesize;
- ret = btrfs_update_block_group(trans, root, bytenr,
+ ret = btrfs_update_block_group(root, bytenr,
bytes, 0, 0);
if (ret)
break;
@@ -9721,7 +6375,7 @@ static int record_extent(struct btrfs_trans_handle *trans,
btrfs_set_extent_flags(leaf, ei,
BTRFS_EXTENT_FLAG_DATA);
} else {
- struct btrfs_disk_key copy_key;;
+ struct btrfs_disk_key copy_key;
bi = (struct btrfs_tree_block_info *)(ei + 1);
memset_extent_buffer(leaf, 0, (unsigned long)bi,
@@ -9736,11 +6390,11 @@ static int record_extent(struct btrfs_trans_handle *trans,
btrfs_set_tree_block_key(leaf, bi, &copy_key);
btrfs_set_extent_flags(leaf, ei,
- BTRFS_EXTENT_FLAG_TREE_BLOCK | flags);
+ flags | BTRFS_EXTENT_FLAG_TREE_BLOCK);
}
btrfs_mark_buffer_dirty(leaf);
- ret = btrfs_update_block_group(trans, extent_root, rec->start,
+ ret = btrfs_update_block_group(extent_root, rec->start,
rec->max_size, 1, 0);
if (ret)
goto fail;
@@ -9774,18 +6428,14 @@ static int record_extent(struct btrfs_trans_handle *trans,
if (ret)
break;
}
- fprintf(stderr, "adding new data backref"
- " on %llu %s %llu owner %llu"
- " offset %llu found %d\n",
- (unsigned long long)rec->start,
- back->full_backref ?
- "parent" : "root",
- back->full_backref ?
- (unsigned long long)parent :
- (unsigned long long)dback->root,
- (unsigned long long)dback->owner,
- (unsigned long long)dback->offset,
- dback->found_ref);
+ fprintf(stderr,
+"adding new data backref on %llu %s %llu owner %llu offset %llu found %d\n",
+ (unsigned long long)rec->start,
+ back->full_backref ? "parent" : "root",
+ back->full_backref ? (unsigned long long)parent :
+ (unsigned long long)dback->root,
+ (unsigned long long)dback->owner,
+ (unsigned long long)dback->offset, dback->found_ref);
} else {
u64 parent;
struct tree_backref *tback;
@@ -9799,8 +6449,8 @@ static int record_extent(struct btrfs_trans_handle *trans,
ret = btrfs_inc_extent_ref(trans, info->extent_root,
rec->start, rec->max_size,
parent, tback->root, 0, 0);
- fprintf(stderr, "adding new tree backref on "
- "start %llu len %llu parent %llu root %llu\n",
+ fprintf(stderr,
+"adding new tree backref on start %llu len %llu parent %llu root %llu\n",
rec->start, rec->max_size, parent, tback->root);
}
fail:
@@ -9938,14 +6588,14 @@ static int repair_ref(struct btrfs_fs_info *info, struct btrfs_path *path,
*/
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
if (ret < 0) {
- fprintf(stderr, "Error cowing down to ref [%Lu, %u, %Lu]: %d\n",
+ fprintf(stderr, "error cowing down to ref [%llu,%u,%llu]: %d\n",
key.objectid, key.type, key.offset, ret);
goto out;
}
if (ret > 0) {
- fprintf(stderr, "Well that's odd, we just found this key "
- "[%Lu, %u, %Lu]\n", key.objectid, key.type,
- key.offset);
+ fprintf(stderr,
+ "well that's odd, we just found this key [%llu,%u,%llu]\n",
+ key.objectid, key.type, key.offset);
ret = -EINVAL;
goto out;
}
@@ -9955,10 +6605,8 @@ static int repair_ref(struct btrfs_fs_info *info, struct btrfs_path *path,
if (btrfs_file_extent_compression(leaf, fi) &&
dback->disk_bytenr != entry->bytenr) {
- fprintf(stderr, "Ref doesn't match the record start and is "
- "compressed, please take a btrfs-image of this file "
- "system and send it to a btrfs developer so they can "
- "complete this functionality for bytenr %Lu\n",
+ fprintf(stderr,
+"ref doesn't match the record start and is compressed, please take a btrfs-image of this file system and send it to a btrfs developer so they can complete this functionality for bytenr %llu\n",
dback->disk_bytenr);
ret = -EINVAL;
goto out;
@@ -9974,9 +6622,8 @@ static int repair_ref(struct btrfs_fs_info *info, struct btrfs_path *path,
if (dback->disk_bytenr + offset +
btrfs_file_extent_num_bytes(leaf, fi) >
entry->bytenr + entry->bytes) {
- fprintf(stderr, "Ref is past the entry end, please "
- "take a btrfs-image of this file system and "
- "send it to a btrfs developer, ref %Lu\n",
+ fprintf(stderr,
+"ref is past the entry end, please take a btrfs-image of this file system and send it to a btrfs developer, ref %llu\n",
dback->disk_bytenr);
ret = -EINVAL;
goto out;
@@ -9989,9 +6636,8 @@ static int repair_ref(struct btrfs_fs_info *info, struct btrfs_path *path,
offset = btrfs_file_extent_offset(leaf, fi);
if (dback->disk_bytenr + offset < entry->bytenr) {
- fprintf(stderr, "Ref is before the entry start, please"
- " take a btrfs-image of this file system and "
- "send it to a btrfs developer, ref %Lu\n",
+ fprintf(stderr,
+"ref is before the entry start, please take a btrfs-image of this file system and send it to a btrfs developer, ref %llu\n",
dback->disk_bytenr);
ret = -EINVAL;
goto out;
@@ -10097,8 +6743,9 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
if (nr_entries <= 1 && !mismatch)
goto out;
- fprintf(stderr, "attempting to repair backref discrepency for bytenr "
- "%Lu\n", rec->start);
+ fprintf(stderr,
+ "attempting to repair backref discrepency for bytenr %llu\n",
+ rec->start);
/*
* First we want to see if the backrefs can agree amongst themselves who
@@ -10114,9 +6761,8 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
if (!best) {
entry = find_entry(&entries, rec->start, rec->nr);
if (!entry && (!broken_entries || !rec->found_rec)) {
- fprintf(stderr, "Backrefs don't agree with each other "
- "and extent record doesn't agree with anybody,"
- " so we can't fix bytenr %Lu bytes %Lu\n",
+ fprintf(stderr,
+"backrefs don't agree with each other and extent record doesn't agree with anybody, so we can't fix bytenr %llu bytes %llu\n",
rec->start, rec->nr);
ret = -EINVAL;
goto out;
@@ -10139,10 +6785,9 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
entry->count++;
best = find_most_right_entry(&entries);
if (!best) {
- fprintf(stderr, "Backrefs and extent record evenly "
- "split on who is right, this is going to "
- "require user input to fix bytenr %Lu bytes "
- "%Lu\n", rec->start, rec->nr);
+ fprintf(stderr,
+"backrefs and extent record evenly split on who is right, this is going to require user input to fix bytenr %llu bytes %llu\n",
+ rec->start, rec->nr);
ret = -EINVAL;
goto out;
}
@@ -10154,10 +6799,8 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
* deal with it properly here yet, so just bail out of that's the case.
*/
if (best->bytenr != rec->start) {
- fprintf(stderr, "Extent start and backref starts don't match, "
- "please use btrfs-image on this file system and send "
- "it to a btrfs developer so they can make fsck fix "
- "this particular case. bytenr is %Lu, bytes is %Lu\n",
+ fprintf(stderr,
+"extent start and backref starts don't match, please use btrfs-image on this file system and send it to a btrfs developer so they can make fsck fix this particular case. bytenr is %llu, bytes is %llu\n",
rec->start, rec->nr);
ret = -EINVAL;
goto out;
@@ -10307,10 +6950,8 @@ static int delete_duplicate_records(struct btrfs_root *root,
continue;
if (tmp->start + tmp->nr < good->start + good->nr) {
- fprintf(stderr, "Ok we have overlapping extents that "
- "aren't completely covered by each other, this "
- "is going to require more careful thought. "
- "The extents are [%Lu-%Lu] and [%Lu-%Lu]\n",
+ fprintf(stderr,
+"Ok we have overlapping extents that aren't completely covered by each other, this is going to require more careful thought. The extents are [%llu-%llu] and [%llu-%llu]\n",
tmp->start, tmp->nr, good->start, good->nr);
abort();
}
@@ -10342,9 +6983,9 @@ static int delete_duplicate_records(struct btrfs_root *root,
/* Shouldn't happen but just in case */
if (tmp->metadata) {
- fprintf(stderr, "Well this shouldn't happen, extent "
- "record overlaps but is metadata? "
- "[%Lu, %Lu]\n", tmp->start, tmp->nr);
+ fprintf(stderr,
+"well this shouldn't happen, extent record overlaps but is metadata? [%llu, %llu]\n",
+ tmp->start, tmp->nr);
abort();
}
@@ -10446,6 +7087,7 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (cache) {
struct extent_record *tmp;
+
tmp = container_of(cache, struct extent_record, cache);
/*
@@ -10627,7 +7269,8 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
continue;
rec->bad_full_backref = 0;
- ret = record_extent(trans, info, &path, rec, back, allocated, flags);
+ ret = record_extent(trans, info, &path, rec, back, allocated,
+ flags);
allocated = 1;
if (ret)
@@ -10636,6 +7279,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
out:
if (trans) {
int err = btrfs_commit_transaction(trans, info->extent_root);
+
if (!ret)
ret = err;
}
@@ -10757,8 +7401,8 @@ again:
goto del_ptr;
}
/*
- * we couldn't find the bad block. TODO, search all the nodes for pointers
- * to this block
+ * We couldn't find the bad block.
+ * TODO: search all the nodes for pointers to this block
*/
if (eb == info->extent_root->node) {
ret = -ENOENT;
@@ -10770,7 +7414,7 @@ again:
}
del_ptr:
- printk("deleting pointer to block %Lu\n", corrupt->cache.start);
+ printk("deleting pointer to block %llu\n", corrupt->cache.start);
ret = btrfs_del_ptr(info->extent_root, &path, level, slot);
out:
@@ -10802,31 +7446,6 @@ static int prune_corrupt_blocks(struct btrfs_fs_info *info)
return 0;
}
-static void reset_cached_block_groups(struct btrfs_fs_info *fs_info)
-{
- struct btrfs_block_group_cache *cache;
- u64 start, end;
- int ret;
-
- while (1) {
- ret = find_first_extent_bit(&fs_info->free_space_cache, 0,
- &start, &end, EXTENT_DIRTY);
- if (ret)
- break;
- clear_extent_dirty(&fs_info->free_space_cache, start, end);
- }
-
- start = 0;
- while (1) {
- cache = btrfs_lookup_first_block_group(fs_info, start);
- if (!cache)
- break;
- if (cache->cached)
- cache->cached = 0;
- start = cache->key.objectid + cache->key.offset;
- }
-}
-
static int check_extent_refs(struct btrfs_root *root,
struct cache_tree *extent_cache)
{
@@ -10844,7 +7463,7 @@ static int check_extent_refs(struct btrfs_root *root,
* extents in the FS
*/
cache = search_cache_extent(extent_cache, 0);
- while(cache) {
+ while (cache) {
rec = container_of(cache, struct extent_record, cache);
set_extent_dirty(root->fs_info->excluded_extents,
rec->start,
@@ -10854,7 +7473,7 @@ static int check_extent_refs(struct btrfs_root *root,
/* pin down all the corrupted blocks too */
cache = search_cache_extent(root->fs_info->corrupt_blocks, 0);
- while(cache) {
+ while (cache) {
set_extent_dirty(root->fs_info->excluded_extents,
cache->start,
cache->start + cache->size - 1);
@@ -10899,7 +7518,7 @@ static int check_extent_refs(struct btrfs_root *root,
if (had_dups)
return -EAGAIN;
- while(1) {
+ while (1) {
int cur_err = 0;
int fix = 0;
@@ -10908,8 +7527,9 @@ static int check_extent_refs(struct btrfs_root *root,
break;
rec = container_of(cache, struct extent_record, cache);
if (rec->num_duplicates) {
- fprintf(stderr, "extent item %llu has multiple extent "
- "items\n", (unsigned long long)rec->start);
+ fprintf(stderr,
+ "extent item %llu has multiple extent items\n",
+ (unsigned long long)rec->start);
cur_err = 1;
}
@@ -10942,7 +7562,8 @@ static int check_extent_refs(struct btrfs_root *root,
}
if (repair && fix) {
- ret = fixup_extent_refs(root->fs_info, extent_cache, rec);
+ ret = fixup_extent_refs(root->fs_info, extent_cache,
+ rec);
if (ret)
goto repair_abort;
}
@@ -11244,22 +7865,6 @@ static int check_device_used(struct device_record *dev_rec,
}
/*
- * Extra (optional) check for dev_item size to report possbile problem on a new
- * kernel.
- */
-static void check_dev_size_alignment(u64 devid, u64 total_bytes, u32 sectorsize)
-{
- if (!IS_ALIGNED(total_bytes, sectorsize)) {
- warning(
-"unaligned total_bytes detected for devid %llu, have %llu should be aligned to %u",
- devid, total_bytes, sectorsize);
- warning(
-"this is OK for older kernel, but may cause kernel warning for newer kernels");
- warning("this can be fixed by 'btrfs rescue fix-device-size'");
- }
-}
-
-/*
* Unlike device size alignment check above, some super total_bytes check
* failure can lead to mount failure for newer kernel.
*
@@ -11340,8 +7945,8 @@ static int add_root_item_to_list(struct list_head *head,
u8 level, u8 drop_level,
struct btrfs_key *drop_key)
{
-
struct root_item_record *ri_rec;
+
ri_rec = malloc(sizeof(*ri_rec));
if (!ri_rec)
return -ENOMEM;
@@ -11389,6 +7994,7 @@ static int deal_root_from_list(struct list_head *list,
while (!list_empty(list)) {
struct root_item_record *rec;
struct extent_buffer *buf;
+
rec = list_entry(list->next,
struct root_item_record, list);
last = 0;
@@ -11520,7 +8126,7 @@ again:
ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
if (ret < 0)
goto out;
- while(1) {
+ while (1) {
leaf = path.nodes[0];
slot = path.slots[0];
if (slot >= btrfs_header_nritems(path.nodes[0])) {
@@ -11647,1807 +8253,6 @@ loop:
goto again;
}
-static int check_extent_inline_ref(struct extent_buffer *eb,
- struct btrfs_key *key, struct btrfs_extent_inline_ref *iref)
-{
- int ret;
- u8 type = btrfs_extent_inline_ref_type(eb, iref);
-
- switch (type) {
- case BTRFS_TREE_BLOCK_REF_KEY:
- case BTRFS_EXTENT_DATA_REF_KEY:
- case BTRFS_SHARED_BLOCK_REF_KEY:
- case BTRFS_SHARED_DATA_REF_KEY:
- ret = 0;
- break;
- default:
- error("extent[%llu %u %llu] has unknown ref type: %d",
- key->objectid, key->type, key->offset, type);
- ret = UNKNOWN_TYPE;
- break;
- }
-
- return ret;
-}
-
-/*
- * Check backrefs of a tree block given by @bytenr or @eb.
- *
- * @root: the root containing the @bytenr or @eb
- * @eb: tree block extent buffer, can be NULL
- * @bytenr: bytenr of the tree block to search
- * @level: tree level of the tree block
- * @owner: owner of the tree block
- *
- * Return >0 for any error found and output error message
- * Return 0 for no error found
- */
-static int check_tree_block_ref(struct btrfs_root *root,
- struct extent_buffer *eb, u64 bytenr,
- int level, u64 owner, struct node_refs *nrefs)
-{
- struct btrfs_key key;
- struct btrfs_root *extent_root = root->fs_info->extent_root;
- struct btrfs_path path;
- struct btrfs_extent_item *ei;
- struct btrfs_extent_inline_ref *iref;
- struct extent_buffer *leaf;
- unsigned long end;
- unsigned long ptr;
- int slot;
- int skinny_level;
- int root_level = btrfs_header_level(root->node);
- int type;
- u32 nodesize = root->fs_info->nodesize;
- u32 item_size;
- u64 offset;
- int found_ref = 0;
- int err = 0;
- int ret;
- int strict = 1;
- int parent = 0;
-
- btrfs_init_path(&path);
- key.objectid = bytenr;
- if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA))
- key.type = BTRFS_METADATA_ITEM_KEY;
- else
- key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = (u64)-1;
-
- /* Search for the backref in extent tree */
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (ret < 0) {
- err |= BACKREF_MISSING;
- goto out;
- }
- ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
- if (ret) {
- err |= BACKREF_MISSING;
- goto out;
- }
-
- leaf = path.nodes[0];
- slot = path.slots[0];
- btrfs_item_key_to_cpu(leaf, &key, slot);
-
- ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
-
- if (key.type == BTRFS_METADATA_ITEM_KEY) {
- skinny_level = (int)key.offset;
- iref = (struct btrfs_extent_inline_ref *)(ei + 1);
- } else {
- struct btrfs_tree_block_info *info;
-
- info = (struct btrfs_tree_block_info *)(ei + 1);
- skinny_level = btrfs_tree_block_level(leaf, info);
- iref = (struct btrfs_extent_inline_ref *)(info + 1);
- }
-
-
- if (eb) {
- u64 header_gen;
- u64 extent_gen;
-
- /*
- * Due to the feature of shared tree blocks, if the upper node
- * is a fs root or shared node, the extent of checked node may
- * not be updated until the next CoW.
- */
- if (nrefs)
- strict = should_check_extent_strictly(root, nrefs,
- level);
- if (!(btrfs_extent_flags(leaf, ei) &
- BTRFS_EXTENT_FLAG_TREE_BLOCK)) {
- error(
- "extent[%llu %u] backref type mismatch, missing bit: %llx",
- key.objectid, nodesize,
- BTRFS_EXTENT_FLAG_TREE_BLOCK);
- err = BACKREF_MISMATCH;
- }
- header_gen = btrfs_header_generation(eb);
- extent_gen = btrfs_extent_generation(leaf, ei);
- if (header_gen != extent_gen) {
- error(
- "extent[%llu %u] backref generation mismatch, wanted: %llu, have: %llu",
- key.objectid, nodesize, header_gen,
- extent_gen);
- err = BACKREF_MISMATCH;
- }
- if (level != skinny_level) {
- error(
- "extent[%llu %u] level mismatch, wanted: %u, have: %u",
- key.objectid, nodesize, level, skinny_level);
- err = BACKREF_MISMATCH;
- }
- if (!is_fstree(owner) && btrfs_extent_refs(leaf, ei) != 1) {
- error(
- "extent[%llu %u] is referred by other roots than %llu",
- key.objectid, nodesize, root->objectid);
- err = BACKREF_MISMATCH;
- }
- }
-
- /*
- * Iterate the extent/metadata item to find the exact backref
- */
- item_size = btrfs_item_size_nr(leaf, slot);
- 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(leaf, iref);
- offset = btrfs_extent_inline_ref_offset(leaf, iref);
-
- ret = check_extent_inline_ref(leaf, &key, iref);
- if (ret) {
- err |= ret;
- break;
- }
- if (type == BTRFS_TREE_BLOCK_REF_KEY) {
- if (offset == root->objectid)
- found_ref = 1;
- if (!strict && owner == offset)
- found_ref = 1;
- } else if (type == BTRFS_SHARED_BLOCK_REF_KEY) {
- /*
- * Backref of tree reloc root points to itself, no need
- * to check backref any more.
- *
- * This may be an error of loop backref, but extent tree
- * checker should have already handled it.
- * Here we only need to avoid infinite iteration.
- */
- if (offset == bytenr) {
- found_ref = 1;
- } else {
- /*
- * Check if the backref points to valid
- * referencer
- */
- found_ref = !check_tree_block_ref( root, NULL,
- offset, level + 1, owner,
- NULL);
- }
- }
-
- if (found_ref)
- break;
- ptr += btrfs_extent_inline_ref_size(type);
- }
-
- /*
- * Inlined extent item doesn't have what we need, check
- * TREE_BLOCK_REF_KEY
- */
- if (!found_ref) {
- btrfs_release_path(&path);
- key.objectid = bytenr;
- key.type = BTRFS_TREE_BLOCK_REF_KEY;
- key.offset = root->objectid;
-
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (!ret)
- found_ref = 1;
- }
- /*
- * Finally check SHARED BLOCK REF, any found will be good
- * Here we're not doing comprehensive extent backref checking,
- * only need to ensure there is some extent referring to this
- * tree block.
- */
- if (!found_ref) {
- btrfs_release_path(&path);
- key.objectid = bytenr;
- key.type = BTRFS_SHARED_BLOCK_REF_KEY;
- key.offset = (u64)-1;
-
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (ret < 0) {
- err |= BACKREF_MISSING;
- goto out;
- }
- ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
- if (ret) {
- err |= BACKREF_MISSING;
- goto out;
- }
- found_ref = 1;
- }
- if (!found_ref)
- err |= BACKREF_MISSING;
-out:
- btrfs_release_path(&path);
- if (nrefs && strict &&
- level < root_level && nrefs->full_backref[level + 1])
- parent = nrefs->bytenr[level + 1];
- if (eb && (err & BACKREF_MISSING))
- error(
- "extent[%llu %u] backref lost (owner: %llu, level: %u) %s %llu",
- bytenr, nodesize, owner, level,
- parent ? "parent" : "root",
- parent ? parent : root->objectid);
- return err;
-}
-
-/*
- * If @err contains BACKREF_MISSING then add extent of the
- * file_extent_data_item.
- *
- * Returns error bits after reapir.
- */
-static int repair_extent_data_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *pathp,
- struct node_refs *nrefs,
- int err)
-{
- struct btrfs_file_extent_item *fi;
- struct btrfs_key fi_key;
- struct btrfs_key key;
- struct btrfs_extent_item *ei;
- struct btrfs_path path;
- struct btrfs_root *extent_root = root->fs_info->extent_root;
- struct extent_buffer *eb;
- u64 size;
- u64 disk_bytenr;
- u64 num_bytes;
- u64 parent;
- u64 offset;
- u64 extent_offset;
- u64 file_offset;
- int generation;
- int slot;
- int ret = 0;
-
- eb = pathp->nodes[0];
- slot = pathp->slots[0];
- btrfs_item_key_to_cpu(eb, &fi_key, slot);
- fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
-
- if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE ||
- btrfs_file_extent_disk_bytenr(eb, fi) == 0)
- return err;
-
- file_offset = fi_key.offset;
- generation = btrfs_file_extent_generation(eb, fi);
- disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi);
- num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi);
- extent_offset = btrfs_file_extent_offset(eb, fi);
- offset = file_offset - extent_offset;
-
- /* now repair only adds backref */
- if ((err & BACKREF_MISSING) == 0)
- return err;
-
- /* search extent item */
- key.objectid = disk_bytenr;
- key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = num_bytes;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (ret < 0) {
- ret = -EIO;
- goto out;
- }
-
- /* insert an extent item */
- if (ret > 0) {
- key.objectid = disk_bytenr;
- key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = num_bytes;
- size = sizeof(*ei);
-
- btrfs_release_path(&path);
- ret = btrfs_insert_empty_item(trans, extent_root, &path, &key,
- size);
- if (ret)
- goto out;
- eb = path.nodes[0];
- ei = btrfs_item_ptr(eb, path.slots[0], struct btrfs_extent_item);
-
- btrfs_set_extent_refs(eb, ei, 0);
- btrfs_set_extent_generation(eb, ei, generation);
- btrfs_set_extent_flags(eb, ei, BTRFS_EXTENT_FLAG_DATA);
-
- btrfs_mark_buffer_dirty(eb);
- ret = btrfs_update_block_group(trans, extent_root, disk_bytenr,
- num_bytes, 1, 0);
- btrfs_release_path(&path);
- }
-
- if (nrefs->full_backref[0])
- parent = btrfs_header_bytenr(eb);
- else
- parent = 0;
-
- ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, parent,
- root->objectid,
- parent ? BTRFS_FIRST_FREE_OBJECTID : fi_key.objectid,
- offset);
- if (ret) {
- error(
- "failed to increase extent data backref[%llu %llu] root %llu",
- disk_bytenr, num_bytes, root->objectid);
- goto out;
- } else {
- printf("Add one extent data backref [%llu %llu]\n",
- disk_bytenr, num_bytes);
- }
-
- err &= ~BACKREF_MISSING;
-out:
- if (ret)
- error("can't repair root %llu extent data item[%llu %llu]",
- root->objectid, disk_bytenr, num_bytes);
- return err;
-}
-
-/*
- * Check EXTENT_DATA item, mainly for its dbackref in extent tree
- *
- * Return >0 any error found and output error message
- * Return 0 for no error found
- */
-static int check_extent_data_item(struct btrfs_root *root,
- struct btrfs_path *pathp,
- struct node_refs *nrefs, int account_bytes)
-{
- struct btrfs_file_extent_item *fi;
- struct extent_buffer *eb = pathp->nodes[0];
- struct btrfs_path path;
- struct btrfs_root *extent_root = root->fs_info->extent_root;
- struct btrfs_key fi_key;
- struct btrfs_key dbref_key;
- struct extent_buffer *leaf;
- struct btrfs_extent_item *ei;
- struct btrfs_extent_inline_ref *iref;
- struct btrfs_extent_data_ref *dref;
- u64 owner;
- u64 disk_bytenr;
- u64 disk_num_bytes;
- u64 extent_num_bytes;
- u64 extent_flags;
- u64 offset;
- u32 item_size;
- unsigned long end;
- unsigned long ptr;
- int type;
- int found_dbackref = 0;
- int slot = pathp->slots[0];
- int err = 0;
- int ret;
- int strict;
-
- btrfs_item_key_to_cpu(eb, &fi_key, slot);
- fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
-
- /* Nothing to check for hole and inline data extents */
- if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE ||
- btrfs_file_extent_disk_bytenr(eb, fi) == 0)
- return 0;
-
- disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi);
- disk_num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi);
- extent_num_bytes = btrfs_file_extent_num_bytes(eb, fi);
- offset = btrfs_file_extent_offset(eb, fi);
-
- /* Check unaligned disk_num_bytes and num_bytes */
- if (!IS_ALIGNED(disk_num_bytes, root->fs_info->sectorsize)) {
- error(
-"file extent [%llu, %llu] has unaligned disk num bytes: %llu, should be aligned to %u",
- fi_key.objectid, fi_key.offset, disk_num_bytes,
- root->fs_info->sectorsize);
- err |= BYTES_UNALIGNED;
- } else if (account_bytes) {
- data_bytes_allocated += disk_num_bytes;
- }
- if (!IS_ALIGNED(extent_num_bytes, root->fs_info->sectorsize)) {
- error(
-"file extent [%llu, %llu] has unaligned num bytes: %llu, should be aligned to %u",
- fi_key.objectid, fi_key.offset, extent_num_bytes,
- root->fs_info->sectorsize);
- err |= BYTES_UNALIGNED;
- } else if (account_bytes) {
- data_bytes_referenced += extent_num_bytes;
- }
- owner = btrfs_header_owner(eb);
-
- /* Check the extent item of the file extent in extent tree */
- btrfs_init_path(&path);
- dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
- dbref_key.type = BTRFS_EXTENT_ITEM_KEY;
- dbref_key.offset = btrfs_file_extent_disk_num_bytes(eb, fi);
-
- ret = btrfs_search_slot(NULL, extent_root, &dbref_key, &path, 0, 0);
- if (ret)
- goto out;
-
- leaf = path.nodes[0];
- slot = path.slots[0];
- ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
-
- extent_flags = btrfs_extent_flags(leaf, ei);
-
- if (!(extent_flags & BTRFS_EXTENT_FLAG_DATA)) {
- error(
- "extent[%llu %llu] backref type mismatch, wanted bit: %llx",
- disk_bytenr, disk_num_bytes,
- BTRFS_EXTENT_FLAG_DATA);
- err |= BACKREF_MISMATCH;
- }
-
- /* Check data backref inside that extent item */
- item_size = btrfs_item_size_nr(leaf, path.slots[0]);
- iref = (struct btrfs_extent_inline_ref *)(ei + 1);
- ptr = (unsigned long)iref;
- end = (unsigned long)ei + item_size;
- strict = should_check_extent_strictly(root, nrefs, -1);
-
- while (ptr < end) {
- u64 ref_root;
- u64 ref_objectid;
- u64 ref_offset;
- bool match = false;
-
- iref = (struct btrfs_extent_inline_ref *)ptr;
- type = btrfs_extent_inline_ref_type(leaf, iref);
- dref = (struct btrfs_extent_data_ref *)(&iref->offset);
-
- ret = check_extent_inline_ref(leaf, &dbref_key, iref);
- if (ret) {
- err |= ret;
- break;
- }
- if (type == BTRFS_EXTENT_DATA_REF_KEY) {
- ref_root = btrfs_extent_data_ref_root(leaf, dref);
- ref_objectid = btrfs_extent_data_ref_objectid(leaf, dref);
- ref_offset = btrfs_extent_data_ref_offset(leaf, dref);
-
- if (ref_objectid == fi_key.objectid &&
- ref_offset == fi_key.offset - offset)
- match = true;
- if (ref_root == root->objectid && match)
- found_dbackref = 1;
- else if (!strict && owner == ref_root && match)
- found_dbackref = 1;
- } else if (type == BTRFS_SHARED_DATA_REF_KEY) {
- found_dbackref = !check_tree_block_ref(root, NULL,
- btrfs_extent_inline_ref_offset(leaf, iref),
- 0, owner, NULL);
- }
-
- if (found_dbackref)
- break;
- ptr += btrfs_extent_inline_ref_size(type);
- }
-
- if (!found_dbackref) {
- btrfs_release_path(&path);
-
- /* Didn't find inlined data backref, try EXTENT_DATA_REF_KEY */
- dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
- dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY;
- dbref_key.offset = hash_extent_data_ref(root->objectid,
- fi_key.objectid, fi_key.offset - offset);
-
- ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
- &dbref_key, &path, 0, 0);
- if (!ret) {
- found_dbackref = 1;
- goto out;
- }
-
- btrfs_release_path(&path);
-
- /*
- * Neither inlined nor EXTENT_DATA_REF found, try
- * SHARED_DATA_REF as last chance.
- */
- dbref_key.objectid = disk_bytenr;
- dbref_key.type = BTRFS_SHARED_DATA_REF_KEY;
- dbref_key.offset = eb->start;
-
- ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
- &dbref_key, &path, 0, 0);
- if (!ret) {
- found_dbackref = 1;
- goto out;
- }
- }
-
-out:
- if (!found_dbackref)
- err |= BACKREF_MISSING;
- btrfs_release_path(&path);
- if (err & BACKREF_MISSING) {
- error("data extent[%llu %llu] backref lost",
- disk_bytenr, disk_num_bytes);
- }
- return err;
-}
-
-/*
- * Get real tree block level for the case like shared block
- * Return >= 0 as tree level
- * Return <0 for error
- */
-static int query_tree_block_level(struct btrfs_fs_info *fs_info, u64 bytenr)
-{
- struct extent_buffer *eb;
- struct btrfs_path path;
- struct btrfs_key key;
- struct btrfs_extent_item *ei;
- u64 flags;
- u64 transid;
- u8 backref_level;
- u8 header_level;
- int ret;
-
- /* Search extent tree for extent generation and level */
- key.objectid = bytenr;
- key.type = BTRFS_METADATA_ITEM_KEY;
- key.offset = (u64)-1;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, &path, 0, 0);
- if (ret < 0)
- goto release_out;
- ret = btrfs_previous_extent_item(fs_info->extent_root, &path, bytenr);
- if (ret < 0)
- goto release_out;
- if (ret > 0) {
- ret = -ENOENT;
- goto release_out;
- }
-
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- ei = btrfs_item_ptr(path.nodes[0], path.slots[0],
- struct btrfs_extent_item);
- flags = btrfs_extent_flags(path.nodes[0], ei);
- if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) {
- ret = -ENOENT;
- goto release_out;
- }
-
- /* Get transid for later read_tree_block() check */
- transid = btrfs_extent_generation(path.nodes[0], ei);
-
- /* Get backref level as one source */
- if (key.type == BTRFS_METADATA_ITEM_KEY) {
- backref_level = key.offset;
- } else {
- struct btrfs_tree_block_info *info;
-
- info = (struct btrfs_tree_block_info *)(ei + 1);
- backref_level = btrfs_tree_block_level(path.nodes[0], info);
- }
- btrfs_release_path(&path);
-
- /* Get level from tree block as an alternative source */
- eb = read_tree_block(fs_info, bytenr, transid);
- if (!extent_buffer_uptodate(eb)) {
- free_extent_buffer(eb);
- return -EIO;
- }
- header_level = btrfs_header_level(eb);
- free_extent_buffer(eb);
-
- if (header_level != backref_level)
- return -EIO;
- return header_level;
-
-release_out:
- btrfs_release_path(&path);
- return ret;
-}
-
-/*
- * Check if a tree block backref is valid (points to a valid tree block)
- * if level == -1, level will be resolved
- * Return >0 for any error found and print error message
- */
-static int check_tree_block_backref(struct btrfs_fs_info *fs_info, u64 root_id,
- u64 bytenr, int level)
-{
- struct btrfs_root *root;
- struct btrfs_key key;
- struct btrfs_path path;
- struct extent_buffer *eb;
- struct extent_buffer *node;
- u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
- int err = 0;
- int ret;
-
- /* Query level for level == -1 special case */
- if (level == -1)
- level = query_tree_block_level(fs_info, bytenr);
- if (level < 0) {
- err |= REFERENCER_MISSING;
- goto out;
- }
-
- key.objectid = root_id;
- key.type = BTRFS_ROOT_ITEM_KEY;
- key.offset = (u64)-1;
-
- root = btrfs_read_fs_root(fs_info, &key);
- if (IS_ERR(root)) {
- err |= REFERENCER_MISSING;
- goto out;
- }
-
- /* Read out the tree block to get item/node key */
- eb = read_tree_block(fs_info, bytenr, 0);
- if (!extent_buffer_uptodate(eb)) {
- err |= REFERENCER_MISSING;
- free_extent_buffer(eb);
- goto out;
- }
-
- /* Empty tree, no need to check key */
- if (!btrfs_header_nritems(eb) && !level) {
- free_extent_buffer(eb);
- goto out;
- }
-
- if (level)
- btrfs_node_key_to_cpu(eb, &key, 0);
- else
- btrfs_item_key_to_cpu(eb, &key, 0);
-
- free_extent_buffer(eb);
-
- btrfs_init_path(&path);
- path.lowest_level = level;
- /* Search with the first key, to ensure we can reach it */
- ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
- if (ret < 0) {
- err |= REFERENCER_MISSING;
- goto release_out;
- }
-
- node = path.nodes[level];
- if (btrfs_header_bytenr(node) != bytenr) {
- error(
- "extent [%llu %d] referencer bytenr mismatch, wanted: %llu, have: %llu",
- bytenr, nodesize, bytenr,
- btrfs_header_bytenr(node));
- err |= REFERENCER_MISMATCH;
- }
- if (btrfs_header_level(node) != level) {
- error(
- "extent [%llu %d] referencer level mismatch, wanted: %d, have: %d",
- bytenr, nodesize, level,
- btrfs_header_level(node));
- err |= REFERENCER_MISMATCH;
- }
-
-release_out:
- btrfs_release_path(&path);
-out:
- if (err & REFERENCER_MISSING) {
- if (level < 0)
- error("extent [%llu %d] lost referencer (owner: %llu)",
- bytenr, nodesize, root_id);
- else
- error(
- "extent [%llu %d] lost referencer (owner: %llu, level: %u)",
- bytenr, nodesize, root_id, level);
- }
-
- return err;
-}
-
-/*
- * Check if tree block @eb is tree reloc root.
- * Return 0 if it's not or any problem happens
- * Return 1 if it's a tree reloc root
- */
-static int is_tree_reloc_root(struct btrfs_fs_info *fs_info,
- struct extent_buffer *eb)
-{
- struct btrfs_root *tree_reloc_root;
- struct btrfs_key key;
- u64 bytenr = btrfs_header_bytenr(eb);
- u64 owner = btrfs_header_owner(eb);
- int ret = 0;
-
- key.objectid = BTRFS_TREE_RELOC_OBJECTID;
- key.offset = owner;
- key.type = BTRFS_ROOT_ITEM_KEY;
-
- tree_reloc_root = btrfs_read_fs_root_no_cache(fs_info, &key);
- if (IS_ERR(tree_reloc_root))
- return 0;
-
- if (bytenr == btrfs_header_bytenr(tree_reloc_root->node))
- ret = 1;
- btrfs_free_fs_root(tree_reloc_root);
- return ret;
-}
-
-/*
- * Check referencer for shared block backref
- * If level == -1, this function will resolve the level.
- */
-static int check_shared_block_backref(struct btrfs_fs_info *fs_info,
- u64 parent, u64 bytenr, int level)
-{
- struct extent_buffer *eb;
- u32 nr;
- int found_parent = 0;
- int i;
-
- eb = read_tree_block(fs_info, parent, 0);
- if (!extent_buffer_uptodate(eb))
- goto out;
-
- if (level == -1)
- level = query_tree_block_level(fs_info, bytenr);
- if (level < 0)
- goto out;
-
- /* It's possible it's a tree reloc root */
- if (parent == bytenr) {
- if (is_tree_reloc_root(fs_info, eb))
- found_parent = 1;
- goto out;
- }
-
- if (level + 1 != btrfs_header_level(eb))
- goto out;
-
- nr = btrfs_header_nritems(eb);
- for (i = 0; i < nr; i++) {
- if (bytenr == btrfs_node_blockptr(eb, i)) {
- found_parent = 1;
- break;
- }
- }
-out:
- free_extent_buffer(eb);
- if (!found_parent) {
- error(
- "shared extent[%llu %u] lost its parent (parent: %llu, level: %u)",
- bytenr, fs_info->nodesize, parent, level);
- return REFERENCER_MISSING;
- }
- return 0;
-}
-
-/*
- * Check referencer for normal (inlined) data ref
- * If len == 0, it will be resolved by searching in extent tree
- */
-static int check_extent_data_backref(struct btrfs_fs_info *fs_info,
- u64 root_id, u64 objectid, u64 offset,
- u64 bytenr, u64 len, u32 count)
-{
- struct btrfs_root *root;
- struct btrfs_root *extent_root = fs_info->extent_root;
- struct btrfs_key key;
- struct btrfs_path path;
- struct extent_buffer *leaf;
- struct btrfs_file_extent_item *fi;
- u32 found_count = 0;
- int slot;
- int ret = 0;
-
- if (!len) {
- key.objectid = bytenr;
- key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = (u64)-1;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (ret < 0)
- goto out;
- ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
- if (ret)
- goto out;
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- if (key.objectid != bytenr ||
- key.type != BTRFS_EXTENT_ITEM_KEY)
- goto out;
- len = key.offset;
- btrfs_release_path(&path);
- }
- key.objectid = root_id;
- key.type = BTRFS_ROOT_ITEM_KEY;
- key.offset = (u64)-1;
- btrfs_init_path(&path);
-
- root = btrfs_read_fs_root(fs_info, &key);
- if (IS_ERR(root))
- goto out;
-
- key.objectid = objectid;
- key.type = BTRFS_EXTENT_DATA_KEY;
- /*
- * It can be nasty as data backref offset is
- * file offset - file extent offset, which is smaller or
- * equal to original backref offset. The only special case is
- * overflow. So we need to special check and do further search.
- */
- key.offset = offset & (1ULL << 63) ? 0 : offset;
-
- ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
- if (ret < 0)
- goto out;
-
- /*
- * Search afterwards to get correct one
- * NOTE: As we must do a comprehensive check on the data backref to
- * make sure the dref count also matches, we must iterate all file
- * extents for that inode.
- */
- while (1) {
- leaf = path.nodes[0];
- slot = path.slots[0];
-
- if (slot >= btrfs_header_nritems(leaf) ||
- btrfs_header_owner(leaf) != root_id)
- goto next;
- btrfs_item_key_to_cpu(leaf, &key, slot);
- if (key.objectid != objectid || key.type != BTRFS_EXTENT_DATA_KEY)
- break;
- fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
- /*
- * Except normal disk bytenr and disk num bytes, we still
- * need to do extra check on dbackref offset as
- * dbackref offset = file_offset - file_extent_offset
- *
- * Also, we must check the leaf owner.
- * In case of shared tree blocks (snapshots) we can inherit
- * leaves from source snapshot.
- * In that case, reference from source snapshot should not
- * count.
- */
- if (btrfs_file_extent_disk_bytenr(leaf, fi) == bytenr &&
- btrfs_file_extent_disk_num_bytes(leaf, fi) == len &&
- (u64)(key.offset - btrfs_file_extent_offset(leaf, fi)) ==
- offset && btrfs_header_owner(leaf) == root_id)
- found_count++;
-
-next:
- ret = btrfs_next_item(root, &path);
- if (ret)
- break;
- }
-out:
- btrfs_release_path(&path);
- if (found_count != count) {
- error(
-"extent[%llu, %llu] referencer count mismatch (root: %llu, owner: %llu, offset: %llu) wanted: %u, have: %u",
- bytenr, len, root_id, objectid, offset, count, found_count);
- return REFERENCER_MISSING;
- }
- return 0;
-}
-
-/*
- * Check if the referencer of a shared data backref exists
- */
-static int check_shared_data_backref(struct btrfs_fs_info *fs_info,
- u64 parent, u64 bytenr)
-{
- struct extent_buffer *eb;
- struct btrfs_key key;
- struct btrfs_file_extent_item *fi;
- u32 nr;
- int found_parent = 0;
- int i;
-
- eb = read_tree_block(fs_info, parent, 0);
- if (!extent_buffer_uptodate(eb))
- goto out;
-
- nr = btrfs_header_nritems(eb);
- for (i = 0; i < nr; i++) {
- btrfs_item_key_to_cpu(eb, &key, i);
- if (key.type != BTRFS_EXTENT_DATA_KEY)
- continue;
-
- fi = btrfs_item_ptr(eb, i, struct btrfs_file_extent_item);
- if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE)
- continue;
-
- if (btrfs_file_extent_disk_bytenr(eb, fi) == bytenr) {
- found_parent = 1;
- break;
- }
- }
-
-out:
- free_extent_buffer(eb);
- if (!found_parent) {
- error("shared extent %llu referencer lost (parent: %llu)",
- bytenr, parent);
- return REFERENCER_MISSING;
- }
- return 0;
-}
-
-/*
- * Only delete backref if REFERENCER_MISSING now
- *
- * Returns <0 the extent was deleted
- * Returns >0 the backref was deleted but extent still exists, returned value
- * means error after repair
- * Returns 0 nothing happened
- */
-static int repair_extent_item(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 err)
-{
- struct btrfs_key old_key;
- int freed = 0;
- int ret;
-
- btrfs_item_key_to_cpu(path->nodes[0], &old_key, path->slots[0]);
-
- if (err & (REFERENCER_MISSING | REFERENCER_MISMATCH)) {
- /* delete the backref */
- ret = btrfs_free_extent(trans, root->fs_info->fs_root, bytenr,
- num_bytes, parent, root_objectid, owner, offset);
- if (!ret) {
- freed = 1;
- err &= ~REFERENCER_MISSING;
- printf("Delete backref in extent [%llu %llu]\n",
- bytenr, num_bytes);
- } else {
- error("fail to delete backref in extent [%llu %llu]",
- bytenr, num_bytes);
- }
- }
-
- /* btrfs_free_extent may delete the extent */
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, &old_key, path, 0, 0);
-
- if (ret)
- ret = -ENOENT;
- else if (freed)
- ret = err;
- return ret;
-}
-
-/*
- * This function will check a given extent item, including its backref and
- * itself (like crossing stripe boundary and type)
- *
- * Since we don't use extent_record anymore, introduce new error bit
- */
-static int check_extent_item(struct btrfs_trans_handle *trans,
- struct btrfs_fs_info *fs_info,
- struct btrfs_path *path)
-{
- struct btrfs_extent_item *ei;
- struct btrfs_extent_inline_ref *iref;
- struct btrfs_extent_data_ref *dref;
- struct extent_buffer *eb = path->nodes[0];
- unsigned long end;
- unsigned long ptr;
- int slot = path->slots[0];
- int type;
- u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
- u32 item_size = btrfs_item_size_nr(eb, slot);
- u64 flags;
- u64 offset;
- u64 parent;
- u64 num_bytes;
- u64 root_objectid;
- u64 owner;
- u64 owner_offset;
- int metadata = 0;
- int level;
- struct btrfs_key key;
- int ret;
- int err = 0;
-
- btrfs_item_key_to_cpu(eb, &key, slot);
- if (key.type == BTRFS_EXTENT_ITEM_KEY) {
- bytes_used += key.offset;
- num_bytes = key.offset;
- } else {
- bytes_used += nodesize;
- num_bytes = nodesize;
- }
-
- if (item_size < sizeof(*ei)) {
- /*
- * COMPAT_EXTENT_TREE_V0 case, but it's already a super
- * old thing when on disk format is still un-determined.
- * No need to care about it anymore
- */
- error("unsupported COMPAT_EXTENT_TREE_V0 detected");
- return -ENOTTY;
- }
-
- ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
- flags = btrfs_extent_flags(eb, ei);
-
- if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
- metadata = 1;
- if (metadata && check_crossing_stripes(global_info, key.objectid,
- eb->len)) {
- error("bad metadata [%llu, %llu) crossing stripe boundary",
- key.objectid, key.objectid + nodesize);
- err |= CROSSING_STRIPE_BOUNDARY;
- }
-
- ptr = (unsigned long)(ei + 1);
-
- if (metadata && key.type == BTRFS_EXTENT_ITEM_KEY) {
- /* Old EXTENT_ITEM metadata */
- struct btrfs_tree_block_info *info;
-
- info = (struct btrfs_tree_block_info *)ptr;
- level = btrfs_tree_block_level(eb, info);
- ptr += sizeof(struct btrfs_tree_block_info);
- } else {
- /* New METADATA_ITEM */
- level = key.offset;
- }
- end = (unsigned long)ei + item_size;
-
-next:
- /* Reached extent item end normally */
- if (ptr == end)
- goto out;
-
- /* Beyond extent item end, wrong item size */
- if (ptr > end) {
- err |= ITEM_SIZE_MISMATCH;
- error("extent item at bytenr %llu slot %d has wrong size",
- eb->start, slot);
- goto out;
- }
-
- parent = 0;
- root_objectid = 0;
- owner = 0;
- owner_offset = 0;
- /* Now check every backref in this extent item */
- 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:
- root_objectid = offset;
- owner = level;
- ret = check_tree_block_backref(fs_info, offset, key.objectid,
- level);
- err |= ret;
- break;
- case BTRFS_SHARED_BLOCK_REF_KEY:
- parent = offset;
- ret = check_shared_block_backref(fs_info, offset, key.objectid,
- level);
- err |= ret;
- break;
- case BTRFS_EXTENT_DATA_REF_KEY:
- dref = (struct btrfs_extent_data_ref *)(&iref->offset);
- root_objectid = btrfs_extent_data_ref_root(eb, dref);
- owner = btrfs_extent_data_ref_objectid(eb, dref);
- owner_offset = btrfs_extent_data_ref_offset(eb, dref);
- ret = check_extent_data_backref(fs_info, root_objectid, owner,
- owner_offset, key.objectid, key.offset,
- btrfs_extent_data_ref_count(eb, dref));
- err |= ret;
- break;
- case BTRFS_SHARED_DATA_REF_KEY:
- parent = offset;
- ret = check_shared_data_backref(fs_info, offset, key.objectid);
- err |= ret;
- break;
- default:
- error("extent[%llu %d %llu] has unknown ref type: %d",
- key.objectid, key.type, key.offset, type);
- ret = UNKNOWN_TYPE;
- err |= ret;
- goto out;
- }
-
- if (err && repair) {
- ret = repair_extent_item(trans, fs_info->extent_root, path,
- key.objectid, num_bytes, parent, root_objectid,
- owner, owner_offset, ret);
- if (ret < 0)
- goto out;
- if (ret) {
- goto next;
- err = ret;
- }
- }
-
- ptr += btrfs_extent_inline_ref_size(type);
- goto next;
-
-out:
- return err;
-}
-
-/*
- * Check if a dev extent item is referred correctly by its chunk
- */
-static int check_dev_extent_item(struct btrfs_fs_info *fs_info,
- struct extent_buffer *eb, int slot)
-{
- struct btrfs_root *chunk_root = fs_info->chunk_root;
- struct btrfs_dev_extent *ptr;
- struct btrfs_path path;
- struct btrfs_key chunk_key;
- struct btrfs_key devext_key;
- struct btrfs_chunk *chunk;
- struct extent_buffer *l;
- int num_stripes;
- u64 length;
- int i;
- int found_chunk = 0;
- int ret;
-
- btrfs_item_key_to_cpu(eb, &devext_key, slot);
- ptr = btrfs_item_ptr(eb, slot, struct btrfs_dev_extent);
- length = btrfs_dev_extent_length(eb, ptr);
-
- chunk_key.objectid = btrfs_dev_extent_chunk_objectid(eb, ptr);
- chunk_key.type = BTRFS_CHUNK_ITEM_KEY;
- chunk_key.offset = btrfs_dev_extent_chunk_offset(eb, ptr);
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, chunk_root, &chunk_key, &path, 0, 0);
- if (ret)
- goto out;
-
- l = path.nodes[0];
- chunk = btrfs_item_ptr(l, path.slots[0], struct btrfs_chunk);
- ret = btrfs_check_chunk_valid(fs_info, l, chunk, path.slots[0],
- chunk_key.offset);
- if (ret < 0)
- goto out;
-
- if (btrfs_stripe_length(fs_info, l, chunk) != length)
- goto out;
-
- num_stripes = btrfs_chunk_num_stripes(l, chunk);
- for (i = 0; i < num_stripes; i++) {
- u64 devid = btrfs_stripe_devid_nr(l, chunk, i);
- u64 offset = btrfs_stripe_offset_nr(l, chunk, i);
-
- if (devid == devext_key.objectid &&
- offset == devext_key.offset) {
- found_chunk = 1;
- break;
- }
- }
-out:
- btrfs_release_path(&path);
- if (!found_chunk) {
- error(
- "device extent[%llu, %llu, %llu] did not find the related chunk",
- devext_key.objectid, devext_key.offset, length);
- return REFERENCER_MISSING;
- }
- return 0;
-}
-
-/*
- * Check if the used space is correct with the dev item
- */
-static int check_dev_item(struct btrfs_fs_info *fs_info,
- struct extent_buffer *eb, int slot)
-{
- struct btrfs_root *dev_root = fs_info->dev_root;
- struct btrfs_dev_item *dev_item;
- struct btrfs_path path;
- struct btrfs_key key;
- struct btrfs_dev_extent *ptr;
- u64 total_bytes;
- u64 dev_id;
- u64 used;
- u64 total = 0;
- int ret;
-
- dev_item = btrfs_item_ptr(eb, slot, struct btrfs_dev_item);
- dev_id = btrfs_device_id(eb, dev_item);
- used = btrfs_device_bytes_used(eb, dev_item);
- total_bytes = btrfs_device_total_bytes(eb, dev_item);
-
- key.objectid = dev_id;
- key.type = BTRFS_DEV_EXTENT_KEY;
- key.offset = 0;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, dev_root, &key, &path, 0, 0);
- if (ret < 0) {
- btrfs_item_key_to_cpu(eb, &key, slot);
- error("cannot find any related dev extent for dev[%llu, %u, %llu]",
- key.objectid, key.type, key.offset);
- btrfs_release_path(&path);
- return REFERENCER_MISSING;
- }
-
- /* Iterate dev_extents to calculate the used space of a device */
- while (1) {
- if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
- goto next;
-
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- if (key.objectid > dev_id)
- break;
- if (key.type != BTRFS_DEV_EXTENT_KEY || key.objectid != dev_id)
- goto next;
-
- ptr = btrfs_item_ptr(path.nodes[0], path.slots[0],
- struct btrfs_dev_extent);
- total += btrfs_dev_extent_length(path.nodes[0], ptr);
-next:
- ret = btrfs_next_item(dev_root, &path);
- if (ret)
- break;
- }
- btrfs_release_path(&path);
-
- if (used != total) {
- btrfs_item_key_to_cpu(eb, &key, slot);
- error(
-"Dev extent's total-byte %llu is not equal to bytes-used %llu in dev[%llu, %u, %llu]",
- total, used, BTRFS_ROOT_TREE_OBJECTID,
- BTRFS_DEV_EXTENT_KEY, dev_id);
- return ACCOUNTING_MISMATCH;
- }
- check_dev_size_alignment(dev_id, total_bytes, fs_info->sectorsize);
-
- return 0;
-}
-
-/*
- * Check a block group item with its referener (chunk) and its used space
- * with extent/metadata item
- */
-static int check_block_group_item(struct btrfs_fs_info *fs_info,
- struct extent_buffer *eb, int slot)
-{
- struct btrfs_root *extent_root = fs_info->extent_root;
- struct btrfs_root *chunk_root = fs_info->chunk_root;
- struct btrfs_block_group_item *bi;
- struct btrfs_block_group_item bg_item;
- struct btrfs_path path;
- struct btrfs_key bg_key;
- struct btrfs_key chunk_key;
- struct btrfs_key extent_key;
- struct btrfs_chunk *chunk;
- struct extent_buffer *leaf;
- struct btrfs_extent_item *ei;
- u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
- u64 flags;
- u64 bg_flags;
- u64 used;
- u64 total = 0;
- int ret;
- int err = 0;
-
- btrfs_item_key_to_cpu(eb, &bg_key, slot);
- bi = btrfs_item_ptr(eb, slot, struct btrfs_block_group_item);
- read_extent_buffer(eb, &bg_item, (unsigned long)bi, sizeof(bg_item));
- used = btrfs_block_group_used(&bg_item);
- bg_flags = btrfs_block_group_flags(&bg_item);
-
- chunk_key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
- chunk_key.type = BTRFS_CHUNK_ITEM_KEY;
- chunk_key.offset = bg_key.objectid;
-
- btrfs_init_path(&path);
- /* Search for the referencer chunk */
- ret = btrfs_search_slot(NULL, chunk_root, &chunk_key, &path, 0, 0);
- if (ret) {
- error(
- "block group[%llu %llu] did not find the related chunk item",
- bg_key.objectid, bg_key.offset);
- err |= REFERENCER_MISSING;
- } else {
- chunk = btrfs_item_ptr(path.nodes[0], path.slots[0],
- struct btrfs_chunk);
- if (btrfs_chunk_length(path.nodes[0], chunk) !=
- bg_key.offset) {
- error(
- "block group[%llu %llu] related chunk item length does not match",
- bg_key.objectid, bg_key.offset);
- err |= REFERENCER_MISMATCH;
- }
- }
- btrfs_release_path(&path);
-
- /* Search from the block group bytenr */
- extent_key.objectid = bg_key.objectid;
- extent_key.type = 0;
- extent_key.offset = 0;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, extent_root, &extent_key, &path, 0, 0);
- if (ret < 0)
- goto out;
-
- /* Iterate extent tree to account used space */
- while (1) {
- leaf = path.nodes[0];
-
- /* Search slot can point to the last item beyond leaf nritems */
- if (path.slots[0] >= btrfs_header_nritems(leaf))
- goto next;
-
- btrfs_item_key_to_cpu(leaf, &extent_key, path.slots[0]);
- if (extent_key.objectid >= bg_key.objectid + bg_key.offset)
- break;
-
- if (extent_key.type != BTRFS_METADATA_ITEM_KEY &&
- extent_key.type != BTRFS_EXTENT_ITEM_KEY)
- goto next;
- if (extent_key.objectid < bg_key.objectid)
- goto next;
-
- if (extent_key.type == BTRFS_METADATA_ITEM_KEY)
- total += nodesize;
- else
- total += extent_key.offset;
-
- ei = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_extent_item);
- flags = btrfs_extent_flags(leaf, ei);
- if (flags & BTRFS_EXTENT_FLAG_DATA) {
- if (!(bg_flags & BTRFS_BLOCK_GROUP_DATA)) {
- error(
- "bad extent[%llu, %llu) type mismatch with chunk",
- extent_key.objectid,
- extent_key.objectid + extent_key.offset);
- err |= CHUNK_TYPE_MISMATCH;
- }
- } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
- if (!(bg_flags & (BTRFS_BLOCK_GROUP_SYSTEM |
- BTRFS_BLOCK_GROUP_METADATA))) {
- error(
- "bad extent[%llu, %llu) type mismatch with chunk",
- extent_key.objectid,
- extent_key.objectid + nodesize);
- err |= CHUNK_TYPE_MISMATCH;
- }
- }
-next:
- ret = btrfs_next_item(extent_root, &path);
- if (ret)
- break;
- }
-
-out:
- btrfs_release_path(&path);
-
- if (total != used) {
- error(
- "block group[%llu %llu] used %llu but extent items used %llu",
- bg_key.objectid, bg_key.offset, used, total);
- err |= BG_ACCOUNTING_ERROR;
- }
- return err;
-}
-
-/*
- * Add block group item to the extent tree if @err contains REFERENCER_MISSING.
- * FIXME: We still need to repair error of dev_item.
- *
- * Returns error after repair.
- */
-static int repair_chunk_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *chunk_root,
- struct btrfs_path *path, int err)
-{
- struct btrfs_chunk *chunk;
- struct btrfs_key chunk_key;
- struct extent_buffer *eb = path->nodes[0];
- u64 length;
- int slot = path->slots[0];
- u64 type;
- int ret = 0;
-
- btrfs_item_key_to_cpu(eb, &chunk_key, slot);
- if (chunk_key.type != BTRFS_CHUNK_ITEM_KEY)
- return err;
- chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
- type = btrfs_chunk_type(path->nodes[0], chunk);
- length = btrfs_chunk_length(eb, chunk);
-
- if (err & REFERENCER_MISSING) {
- ret = btrfs_make_block_group(trans, chunk_root->fs_info, 0,
- type, chunk_key.objectid, chunk_key.offset, length);
- if (ret) {
- error("fail to add block group item[%llu %llu]",
- chunk_key.offset, length);
- goto out;
- } else {
- err &= ~REFERENCER_MISSING;
- printf("Added block group item[%llu %llu]\n",
- chunk_key.offset, length);
- }
- }
-
-out:
- return err;
-}
-
-/*
- * Check a chunk item.
- * Including checking all referred dev_extents and block group
- */
-static int check_chunk_item(struct btrfs_fs_info *fs_info,
- struct extent_buffer *eb, int slot)
-{
- struct btrfs_root *extent_root = fs_info->extent_root;
- struct btrfs_root *dev_root = fs_info->dev_root;
- struct btrfs_path path;
- struct btrfs_key chunk_key;
- struct btrfs_key bg_key;
- struct btrfs_key devext_key;
- struct btrfs_chunk *chunk;
- struct extent_buffer *leaf;
- struct btrfs_block_group_item *bi;
- struct btrfs_block_group_item bg_item;
- struct btrfs_dev_extent *ptr;
- u64 length;
- u64 chunk_end;
- u64 stripe_len;
- u64 type;
- int num_stripes;
- u64 offset;
- u64 objectid;
- int i;
- int ret;
- int err = 0;
-
- btrfs_item_key_to_cpu(eb, &chunk_key, slot);
- chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
- length = btrfs_chunk_length(eb, chunk);
- chunk_end = chunk_key.offset + length;
- ret = btrfs_check_chunk_valid(fs_info, eb, chunk, slot,
- chunk_key.offset);
- if (ret < 0) {
- error("chunk[%llu %llu) is invalid", chunk_key.offset,
- chunk_end);
- err |= BYTES_UNALIGNED | UNKNOWN_TYPE;
- goto out;
- }
- type = btrfs_chunk_type(eb, chunk);
-
- bg_key.objectid = chunk_key.offset;
- bg_key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
- bg_key.offset = length;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(NULL, extent_root, &bg_key, &path, 0, 0);
- if (ret) {
- error(
- "chunk[%llu %llu) did not find the related block group item",
- chunk_key.offset, chunk_end);
- err |= REFERENCER_MISSING;
- } else{
- leaf = path.nodes[0];
- bi = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_block_group_item);
- read_extent_buffer(leaf, &bg_item, (unsigned long)bi,
- sizeof(bg_item));
- if (btrfs_block_group_flags(&bg_item) != type) {
- error(
-"chunk[%llu %llu) related block group item flags mismatch, wanted: %llu, have: %llu",
- chunk_key.offset, chunk_end, type,
- btrfs_block_group_flags(&bg_item));
- err |= REFERENCER_MISSING;
- }
- }
-
- num_stripes = btrfs_chunk_num_stripes(eb, chunk);
- stripe_len = btrfs_stripe_length(fs_info, eb, chunk);
- for (i = 0; i < num_stripes; i++) {
- btrfs_release_path(&path);
- btrfs_init_path(&path);
- devext_key.objectid = btrfs_stripe_devid_nr(eb, chunk, i);
- devext_key.type = BTRFS_DEV_EXTENT_KEY;
- devext_key.offset = btrfs_stripe_offset_nr(eb, chunk, i);
-
- ret = btrfs_search_slot(NULL, dev_root, &devext_key, &path,
- 0, 0);
- if (ret)
- goto not_match_dev;
-
- leaf = path.nodes[0];
- ptr = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_dev_extent);
- objectid = btrfs_dev_extent_chunk_objectid(leaf, ptr);
- offset = btrfs_dev_extent_chunk_offset(leaf, ptr);
- if (objectid != chunk_key.objectid ||
- offset != chunk_key.offset ||
- btrfs_dev_extent_length(leaf, ptr) != stripe_len)
- goto not_match_dev;
- continue;
-not_match_dev:
- err |= BACKREF_MISSING;
- error(
- "chunk[%llu %llu) stripe %d did not find the related dev extent",
- chunk_key.objectid, chunk_end, i);
- continue;
- }
- btrfs_release_path(&path);
-out:
- return err;
-}
-
-static int delete_extent_tree_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *path)
-{
- struct btrfs_key key;
- int ret = 0;
-
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
- btrfs_release_path(path);
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
- if (ret) {
- ret = -ENOENT;
- goto out;
- }
-
- ret = btrfs_del_item(trans, root, path);
- if (ret)
- goto out;
-
- if (path->slots[0] == 0)
- btrfs_prev_leaf(root, path);
- else
- path->slots[0]--;
-out:
- if (ret)
- error("failed to delete root %llu item[%llu, %u, %llu]",
- root->objectid, key.objectid, key.type, key.offset);
- else
- printf("Deleted root %llu item[%llu, %u, %llu]\n",
- root->objectid, key.objectid, key.type, key.offset);
- return ret;
-}
-
-/*
- * Main entry function to check known items and update related accounting info
- */
-static int check_leaf_items(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
- struct node_refs *nrefs, int account_bytes)
-{
- struct btrfs_fs_info *fs_info = root->fs_info;
- struct btrfs_key key;
- struct extent_buffer *eb;
- int slot;
- int type;
- struct btrfs_extent_data_ref *dref;
- int ret = 0;
- int err = 0;
-
-again:
- eb = path->nodes[0];
- slot = path->slots[0];
- if (slot >= btrfs_header_nritems(eb)) {
- if (slot == 0) {
- error("empty leaf [%llu %u] root %llu", eb->start,
- root->fs_info->nodesize, root->objectid);
- err |= EIO;
- }
- goto out;
- }
-
- btrfs_item_key_to_cpu(eb, &key, slot);
- type = key.type;
-
- switch (type) {
- case BTRFS_EXTENT_DATA_KEY:
- ret = check_extent_data_item(root, path, nrefs, account_bytes);
- if (repair && ret)
- ret = repair_extent_data_item(trans, root, path, nrefs,
- ret);
- err |= ret;
- break;
- case BTRFS_BLOCK_GROUP_ITEM_KEY:
- ret = check_block_group_item(fs_info, eb, slot);
- if (repair &&
- ret & REFERENCER_MISSING)
- ret = delete_extent_tree_item(trans, root, path);
- err |= ret;
- break;
- case BTRFS_DEV_ITEM_KEY:
- ret = check_dev_item(fs_info, eb, slot);
- err |= ret;
- break;
- case BTRFS_CHUNK_ITEM_KEY:
- ret = check_chunk_item(fs_info, eb, slot);
- if (repair && ret)
- ret = repair_chunk_item(trans, root, path, ret);
- err |= ret;
- break;
- case BTRFS_DEV_EXTENT_KEY:
- ret = check_dev_extent_item(fs_info, eb, slot);
- err |= ret;
- break;
- case BTRFS_EXTENT_ITEM_KEY:
- case BTRFS_METADATA_ITEM_KEY:
- ret = check_extent_item(trans, fs_info, path);
- err |= ret;
- break;
- case BTRFS_EXTENT_CSUM_KEY:
- total_csum_bytes += btrfs_item_size_nr(eb, slot);
- err |= ret;
- break;
- case BTRFS_TREE_BLOCK_REF_KEY:
- ret = check_tree_block_backref(fs_info, key.offset,
- key.objectid, -1);
- if (repair &&
- ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
- ret = delete_extent_tree_item(trans, root, path);
- err |= ret;
- break;
- case BTRFS_EXTENT_DATA_REF_KEY:
- dref = btrfs_item_ptr(eb, slot, struct btrfs_extent_data_ref);
- ret = check_extent_data_backref(fs_info,
- btrfs_extent_data_ref_root(eb, dref),
- btrfs_extent_data_ref_objectid(eb, dref),
- btrfs_extent_data_ref_offset(eb, dref),
- key.objectid, 0,
- btrfs_extent_data_ref_count(eb, dref));
- if (repair &&
- ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
- ret = delete_extent_tree_item(trans, root, path);
- err |= ret;
- break;
- case BTRFS_SHARED_BLOCK_REF_KEY:
- ret = check_shared_block_backref(fs_info, key.offset,
- key.objectid, -1);
- if (repair &&
- ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
- ret = delete_extent_tree_item(trans, root, path);
- err |= ret;
- break;
- case BTRFS_SHARED_DATA_REF_KEY:
- ret = check_shared_data_backref(fs_info, key.offset,
- key.objectid);
- if (repair &&
- ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
- ret = delete_extent_tree_item(trans, root, path);
- err |= ret;
- break;
- default:
- break;
- }
-
- ++path->slots[0];
- goto again;
-out:
- return err;
-}
-
-/*
- * Low memory usage version check_chunks_and_extents.
- */
-static int check_chunks_and_extents_v2(struct btrfs_fs_info *fs_info)
-{
- struct btrfs_trans_handle *trans = NULL;
- struct btrfs_path path;
- struct btrfs_key old_key;
- struct btrfs_key key;
- struct btrfs_root *root1;
- struct btrfs_root *root;
- struct btrfs_root *cur_root;
- int err = 0;
- int ret;
-
- root = fs_info->fs_root;
-
- if (repair) {
- trans = btrfs_start_transaction(fs_info->extent_root, 1);
- if (IS_ERR(trans)) {
- error("failed to start transaction before check");
- return PTR_ERR(trans);
- }
- }
-
- root1 = root->fs_info->chunk_root;
- ret = check_btrfs_root(trans, root1, 0, 1);
- err |= ret;
-
- root1 = root->fs_info->tree_root;
- ret = check_btrfs_root(trans, root1, 0, 1);
- err |= ret;
-
- btrfs_init_path(&path);
- key.objectid = BTRFS_EXTENT_TREE_OBJECTID;
- key.offset = 0;
- key.type = BTRFS_ROOT_ITEM_KEY;
-
- ret = btrfs_search_slot(NULL, root1, &key, &path, 0, 0);
- if (ret) {
- error("cannot find extent tree in tree_root");
- goto out;
- }
-
- while (1) {
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- if (key.type != BTRFS_ROOT_ITEM_KEY)
- goto next;
- old_key = key;
- key.offset = (u64)-1;
-
- if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
- cur_root = btrfs_read_fs_root_no_cache(root->fs_info,
- &key);
- else
- cur_root = btrfs_read_fs_root(root->fs_info, &key);
- if (IS_ERR(cur_root) || !cur_root) {
- error("failed to read tree: %lld", key.objectid);
- goto next;
- }
-
- ret = check_btrfs_root(trans, cur_root, 0, 1);
- err |= ret;
-
- if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
- btrfs_free_fs_root(cur_root);
-
- btrfs_release_path(&path);
- ret = btrfs_search_slot(NULL, root->fs_info->tree_root,
- &old_key, &path, 0, 0);
- if (ret)
- goto out;
-next:
- ret = btrfs_next_item(root1, &path);
- if (ret)
- goto out;
- }
-out:
-
- /* if repair, update block accounting */
- if (repair) {
- ret = btrfs_fix_block_accounting(trans, root);
- if (ret)
- err |= ret;
- else
- err &= ~BG_ACCOUNTING_ERROR;
- }
-
- if (trans)
- btrfs_commit_transaction(trans, root->fs_info->extent_root);
-
- btrfs_release_path(&path);
-
- return err;
-}
-
static int do_check_chunks_and_extents(struct btrfs_fs_info *fs_info)
{
int ret;
@@ -13455,7 +8260,7 @@ static int do_check_chunks_and_extents(struct btrfs_fs_info *fs_info)
if (!ctx.progress_enabled)
fprintf(stderr, "checking extents\n");
if (check_mode == CHECK_MODE_LOWMEM)
- ret = check_chunks_and_extents_v2(fs_info);
+ ret = check_chunks_and_extents_lowmem(fs_info);
else
ret = check_chunks_and_extents(fs_info);
@@ -13674,8 +8479,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info)
chunk = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_chunk);
btrfs_add_block_group(fs_info, 0,
- btrfs_chunk_type(leaf, chunk),
- key.objectid, key.offset,
+ btrfs_chunk_type(leaf, chunk), key.offset,
btrfs_chunk_length(leaf, chunk));
set_extent_dirty(&fs_info->free_space_cache, key.offset,
key.offset + btrfs_chunk_length(leaf, chunk));
@@ -14250,6 +9054,7 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
struct btrfs_key found_key;
struct btrfs_extent_item *ei;
struct btrfs_extent_inline_ref *iref;
+ unsigned long item_end;
int slot = path.slots[0];
int type;
u64 flags;
@@ -14278,6 +9083,7 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
flags = btrfs_extent_flags(leaf, ei);
+ item_end = (unsigned long)ei + btrfs_item_size_nr(leaf, slot);
if (found_key.type == BTRFS_EXTENT_ITEM_KEY &&
!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK))
@@ -14295,6 +9101,15 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
}
/*
+ * It's a valid extent/metadata item that has no inline ref,
+ * but SHARED_BLOCK_REF or other shared references.
+ * So we need to do extra check to avoid reading beyond leaf
+ * boudnary.
+ */
+ if ((unsigned long)iref >= item_end)
+ goto next;
+
+ /*
* For a root extent, it must be of the following type and the
* first (and only one) iref in the item.
*/
diff --git a/check/mode-common.c b/check/mode-common.c
new file mode 100644
index 00000000..1b56a968
--- /dev/null
+++ b/check/mode-common.c
@@ -0,0 +1,351 @@
+/*
+ * 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 <time.h>
+#include "ctree.h"
+#include "internal.h"
+#include "messages.h"
+#include "transaction.h"
+#include "utils.h"
+#include "disk-io.h"
+#include "check/mode-common.h"
+
+/*
+ * Search in csum tree to find how many bytes of range [@start, @start + @len)
+ * has the corresponding csum item.
+ *
+ * @start: range start
+ * @len: range length
+ * @found: return value of found csum bytes
+ * unit is BYTE.
+ */
+int count_csum_range(struct btrfs_fs_info *fs_info, u64 start,
+ u64 len, u64 *found)
+{
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct extent_buffer *leaf;
+ int ret;
+ size_t size;
+ *found = 0;
+ u64 csum_end;
+ u16 csum_size = btrfs_super_csum_size(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, fs_info->csum_root,
+ &key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+ 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(fs_info->csum_root, &path);
+ if (ret > 0)
+ break;
+ else if (ret < 0)
+ goto out;
+ 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) *
+ fs_info->sectorsize;
+ if (csum_end > start) {
+ size = min(csum_end - start, len);
+ len -= size;
+ start += size;
+ *found += size;
+ }
+
+ path.slots[0]++;
+ }
+out:
+ btrfs_release_path(&path);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+/*
+ * Wrapper to insert one inode item into given @root
+ * Timestamp will be set to current time.
+ *
+ * @root: the root to insert inode item into
+ * @ino: inode number
+ * @size: inode size
+ * @nbytes: nbytes (real used size, without hole)
+ * @nlink: number of links
+ * @mode: file mode, including S_IF* bits
+ */
+int insert_inode_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 ino, u64 size,
+ u64 nbytes, u64 nlink, u32 mode)
+{
+ struct btrfs_inode_item ii;
+ time_t now = time(NULL);
+ int ret;
+
+ btrfs_set_stack_inode_size(&ii, size);
+ btrfs_set_stack_inode_nbytes(&ii, nbytes);
+ btrfs_set_stack_inode_nlink(&ii, nlink);
+ btrfs_set_stack_inode_mode(&ii, mode);
+ btrfs_set_stack_inode_generation(&ii, trans->transid);
+ btrfs_set_stack_timespec_nsec(&ii.atime, 0);
+ btrfs_set_stack_timespec_sec(&ii.ctime, now);
+ btrfs_set_stack_timespec_nsec(&ii.ctime, 0);
+ btrfs_set_stack_timespec_sec(&ii.mtime, now);
+ btrfs_set_stack_timespec_nsec(&ii.mtime, 0);
+ btrfs_set_stack_timespec_sec(&ii.otime, 0);
+ btrfs_set_stack_timespec_nsec(&ii.otime, 0);
+
+ ret = btrfs_insert_inode(trans, root, ino, &ii);
+ ASSERT(!ret);
+
+ warning("root %llu inode %llu recreating inode item, this may "
+ "be incomplete, please check permissions and content after "
+ "the fsck completes.\n", (unsigned long long)root->objectid,
+ (unsigned long long)ino);
+
+ return 0;
+}
+
+static int get_highest_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, struct btrfs_path *path,
+ u64 *highest_ino)
+{
+ struct btrfs_key key, found_key;
+ int ret;
+
+ btrfs_init_path(path);
+ key.objectid = BTRFS_LAST_FREE_OBJECTID;
+ key.offset = -1;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ if (ret == 1) {
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+ path->slots[0] - 1);
+ *highest_ino = found_key.objectid;
+ ret = 0;
+ }
+ if (*highest_ino >= BTRFS_LAST_FREE_OBJECTID)
+ ret = -EOVERFLOW;
+ btrfs_release_path(path);
+ return ret;
+}
+
+/*
+ * Link inode to dir 'lost+found'. Increase @ref_count.
+ *
+ * Returns 0 means success.
+ * Returns <0 means failure.
+ */
+int link_inode_to_lostfound(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 ino, char *namebuf, u32 name_len,
+ u8 filetype, u64 *ref_count)
+{
+ char *dir_name = "lost+found";
+ u64 lost_found_ino;
+ int ret;
+ u32 mode = 0700;
+
+ btrfs_release_path(path);
+ ret = get_highest_inode(trans, root, path, &lost_found_ino);
+ if (ret < 0)
+ goto out;
+ lost_found_ino++;
+
+ ret = btrfs_mkdir(trans, root, dir_name, strlen(dir_name),
+ BTRFS_FIRST_FREE_OBJECTID, &lost_found_ino,
+ mode);
+ if (ret < 0) {
+ error("failed to create '%s' dir: %s", dir_name, strerror(-ret));
+ goto out;
+ }
+ ret = btrfs_add_link(trans, root, ino, lost_found_ino,
+ namebuf, name_len, filetype, NULL, 1, 0);
+ /*
+ * Add ".INO" suffix several times to handle case where
+ * "FILENAME.INO" is already taken by another file.
+ */
+ while (ret == -EEXIST) {
+ /*
+ * Conflicting file name, add ".INO" as suffix * +1 for '.'
+ */
+ if (name_len + count_digits(ino) + 1 > BTRFS_NAME_LEN) {
+ ret = -EFBIG;
+ goto out;
+ }
+ snprintf(namebuf + name_len, BTRFS_NAME_LEN - name_len,
+ ".%llu", ino);
+ name_len += count_digits(ino) + 1;
+ ret = btrfs_add_link(trans, root, ino, lost_found_ino, namebuf,
+ name_len, filetype, NULL, 1, 0);
+ }
+ if (ret < 0) {
+ error("failed to link the inode %llu to %s dir: %s",
+ ino, dir_name, strerror(-ret));
+ goto out;
+ }
+
+ ++*ref_count;
+ printf("Moving file '%.*s' to '%s' dir since it has no valid backref\n",
+ name_len, namebuf, dir_name);
+out:
+ btrfs_release_path(path);
+ if (ret)
+ error("failed to move file '%.*s' to '%s' dir", name_len,
+ namebuf, dir_name);
+ return ret;
+}
+
+/*
+ * Extra (optional) check for dev_item size to report possbile problem on a new
+ * kernel.
+ */
+void check_dev_size_alignment(u64 devid, u64 total_bytes, u32 sectorsize)
+{
+ if (!IS_ALIGNED(total_bytes, sectorsize)) {
+ warning(
+"unaligned total_bytes detected for devid %llu, have %llu should be aligned to %u",
+ devid, total_bytes, sectorsize);
+ warning(
+"this is OK for older kernel, but may cause kernel warning for newer kernels");
+ warning("this can be fixed by 'btrfs rescue fix-device-size'");
+ }
+}
+
+void reada_walk_down(struct btrfs_root *root, struct extent_buffer *node,
+ int slot)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ u64 bytenr;
+ u64 ptr_gen;
+ u32 nritems;
+ int i;
+ int level;
+
+ level = btrfs_header_level(node);
+ if (level != 1)
+ return;
+
+ nritems = btrfs_header_nritems(node);
+ for (i = slot; i < nritems; i++) {
+ bytenr = btrfs_node_blockptr(node, i);
+ ptr_gen = btrfs_node_ptr_generation(node, i);
+ readahead_tree_block(fs_info, bytenr, ptr_gen);
+ }
+}
+
+/*
+ * Check the child node/leaf by the following condition:
+ * 1. the first item key of the node/leaf should be the same with the one
+ * in parent.
+ * 2. block in parent node should match the child node/leaf.
+ * 3. generation of parent node and child's header should be consistent.
+ *
+ * Or the child node/leaf pointed by the key in parent is not valid.
+ *
+ * We hope to check leaf owner too, but since subvol may share leaves,
+ * which makes leaf owner check not so strong, key check should be
+ * sufficient enough for that case.
+ */
+int check_child_node(struct extent_buffer *parent, int slot,
+ struct extent_buffer *child)
+{
+ struct btrfs_key parent_key;
+ struct btrfs_key child_key;
+ int ret = 0;
+
+ btrfs_node_key_to_cpu(parent, &parent_key, slot);
+ if (btrfs_header_level(child) == 0)
+ btrfs_item_key_to_cpu(child, &child_key, 0);
+ else
+ btrfs_node_key_to_cpu(child, &child_key, 0);
+
+ if (memcmp(&parent_key, &child_key, sizeof(parent_key))) {
+ ret = -EINVAL;
+ fprintf(stderr,
+ "Wrong key of child node/leaf, wanted: (%llu, %u, %llu), have: (%llu, %u, %llu)\n",
+ parent_key.objectid, parent_key.type, parent_key.offset,
+ child_key.objectid, child_key.type, child_key.offset);
+ }
+ if (btrfs_header_bytenr(child) != btrfs_node_blockptr(parent, slot)) {
+ ret = -EINVAL;
+ fprintf(stderr, "Wrong block of child node/leaf, wanted: %llu, have: %llu\n",
+ btrfs_node_blockptr(parent, slot),
+ btrfs_header_bytenr(child));
+ }
+ if (btrfs_node_ptr_generation(parent, slot) !=
+ btrfs_header_generation(child)) {
+ ret = -EINVAL;
+ fprintf(stderr, "Wrong generation of child node/leaf, wanted: %llu, have: %llu\n",
+ btrfs_header_generation(child),
+ btrfs_node_ptr_generation(parent, slot));
+ }
+ return ret;
+}
+
+void reset_cached_block_groups(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_block_group_cache *cache;
+ u64 start, end;
+ int ret;
+
+ while (1) {
+ ret = find_first_extent_bit(&fs_info->free_space_cache, 0,
+ &start, &end, EXTENT_DIRTY);
+ if (ret)
+ break;
+ clear_extent_dirty(&fs_info->free_space_cache, start, end);
+ }
+
+ start = 0;
+ while (1) {
+ cache = btrfs_lookup_first_block_group(fs_info, start);
+ if (!cache)
+ break;
+ if (cache->cached)
+ cache->cached = 0;
+ start = cache->key.objectid + cache->key.offset;
+ }
+}
diff --git a/check/mode-common.h b/check/mode-common.h
new file mode 100644
index 00000000..ffae782b
--- /dev/null
+++ b/check/mode-common.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+/*
+ * Defines and function declarations for code shared by both lowmem and
+ * original mode
+ */
+#ifndef __BTRFS_CHECK_MODE_COMMON_H__
+#define __BTRFS_CHECK_MODE_COMMON_H__
+
+#include <sys/stat.h>
+#include "ctree.h"
+
+/*
+ * Use for tree walk to walk through trees whose leaves/nodes can be shared
+ * between different trees. (Namely subvolume/fs trees)
+ */
+struct node_refs {
+ u64 bytenr[BTRFS_MAX_LEVEL];
+ u64 refs[BTRFS_MAX_LEVEL];
+ int need_check[BTRFS_MAX_LEVEL];
+ /* field for checking all trees */
+ int checked[BTRFS_MAX_LEVEL];
+ /* the corresponding extent should be marked as full backref or not */
+ int full_backref[BTRFS_MAX_LEVEL];
+};
+
+extern u64 bytes_used;
+extern u64 total_csum_bytes;
+extern u64 total_btree_bytes;
+extern u64 total_fs_tree_bytes;
+extern u64 total_extent_tree_bytes;
+extern u64 btree_space_waste;
+extern u64 data_bytes_allocated;
+extern u64 data_bytes_referenced;
+extern struct list_head duplicate_extents;
+extern struct list_head delete_items;
+extern int no_holes;
+extern int init_extent_tree;
+extern int check_data_csum;
+extern struct btrfs_fs_info *global_info;
+extern struct task_ctx ctx;
+extern struct cache_tree *roots_info_cache;
+
+static inline 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 inline int fs_root_objectid(u64 objectid)
+{
+ if (objectid == BTRFS_TREE_RELOC_OBJECTID ||
+ objectid == BTRFS_DATA_RELOC_TREE_OBJECTID)
+ return 1;
+ return is_fstree(objectid);
+}
+
+int count_csum_range(struct btrfs_fs_info *fs_info, u64 start,
+ u64 len, u64 *found);
+int insert_inode_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 ino, u64 size,
+ u64 nbytes, u64 nlink, u32 mode);
+int link_inode_to_lostfound(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 ino, char *namebuf, u32 name_len,
+ u8 filetype, u64 *ref_count);
+void check_dev_size_alignment(u64 devid, u64 total_bytes, u32 sectorsize);
+void reada_walk_down(struct btrfs_root *root, struct extent_buffer *node,
+ int slot);
+int check_child_node(struct extent_buffer *parent, int slot,
+ struct extent_buffer *child);
+void reset_cached_block_groups(struct btrfs_fs_info *fs_info);
+
+#endif
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
new file mode 100644
index 00000000..62bcf3d2
--- /dev/null
+++ b/check/mode-lowmem.c
@@ -0,0 +1,4573 @@
+/*
+ * 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 <time.h>
+#include "ctree.h"
+#include "repair.h"
+#include "transaction.h"
+#include "messages.h"
+#include "disk-io.h"
+#include "backref.h"
+#include "hash.h"
+#include "internal.h"
+#include "utils.h"
+#include "volumes.h"
+#include "check/mode-common.h"
+#include "check/mode-lowmem.h"
+
+static int calc_extent_flag(struct btrfs_root *root, struct extent_buffer *eb,
+ u64 *flags_ret)
+{
+ struct btrfs_root *extent_root = root->fs_info->extent_root;
+ struct btrfs_root_item *ri = &root->root_item;
+ struct btrfs_extent_inline_ref *iref;
+ struct btrfs_extent_item *ei;
+ struct btrfs_key key;
+ struct btrfs_path *path = NULL;
+ unsigned long ptr;
+ unsigned long end;
+ u64 flags;
+ u64 owner = 0;
+ u64 offset;
+ int slot;
+ int type;
+ int ret = 0;
+
+ /*
+ * Except file/reloc tree, we can not have FULL BACKREF MODE
+ */
+ if (root->objectid < BTRFS_FIRST_FREE_OBJECTID)
+ goto normal;
+
+ /* root node */
+ if (eb->start == btrfs_root_bytenr(ri))
+ goto normal;
+
+ if (btrfs_header_flag(eb, BTRFS_HEADER_FLAG_RELOC))
+ goto full_backref;
+
+ owner = btrfs_header_owner(eb);
+ if (owner == root->objectid)
+ goto normal;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = btrfs_header_bytenr(eb);
+ key.type = (u8)-1;
+ key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
+ if (ret <= 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (ret > 0) {
+ ret = btrfs_previous_extent_item(extent_root, path,
+ key.objectid);
+ if (ret)
+ goto full_backref;
+
+ }
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+
+ eb = path->nodes[0];
+ slot = path->slots[0];
+ ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
+
+ flags = btrfs_extent_flags(eb, ei);
+ if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
+ goto full_backref;
+
+ ptr = (unsigned long)(ei + 1);
+ end = (unsigned long)ei + btrfs_item_size_nr(eb, slot);
+
+ if (key.type == BTRFS_EXTENT_ITEM_KEY)
+ ptr += sizeof(struct btrfs_tree_block_info);
+
+next:
+ /* Reached extent item ends normally */
+ if (ptr == end)
+ goto full_backref;
+
+ /* Beyond extent item end, wrong item size */
+ if (ptr > end) {
+ error("extent item at bytenr %llu slot %d has wrong size",
+ eb->start, slot);
+ goto full_backref;
+ }
+
+ iref = (struct btrfs_extent_inline_ref *)ptr;
+ offset = btrfs_extent_inline_ref_offset(eb, iref);
+ type = btrfs_extent_inline_ref_type(eb, iref);
+
+ if (type == BTRFS_TREE_BLOCK_REF_KEY && offset == owner)
+ goto normal;
+ ptr += btrfs_extent_inline_ref_size(type);
+ goto next;
+
+normal:
+ *flags_ret &= ~BTRFS_BLOCK_FLAG_FULL_BACKREF;
+ goto out;
+
+full_backref:
+ *flags_ret |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
+out:
+ btrfs_free_path(path);
+ return ret;
+}
+
+/*
+ * for a tree node or leaf, if it's shared, indeed we don't need to iterate it
+ * in every fs or file tree check. Here we find its all root ids, and only check
+ * it in the fs or file tree which has the smallest root id.
+ */
+static int need_check(struct btrfs_root *root, struct ulist *roots)
+{
+ struct rb_node *node;
+ struct ulist_node *u;
+
+ /*
+ * @roots can be empty if it belongs to tree reloc tree
+ * In that case, we should always check the leaf, as we can't use
+ * the tree owner to ensure some other root will check it.
+ */
+ if (roots->nnodes == 1 || roots->nnodes == 0)
+ return 1;
+
+ node = rb_first(&roots->root);
+ u = rb_entry(node, struct ulist_node, rb_node);
+ /*
+ * current root id is not smallest, we skip it and let it be checked
+ * in the fs or file tree who hash the smallest root id.
+ */
+ if (root->objectid != u->val)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * for a tree node or leaf, we record its reference count, so later if we still
+ * process this node or leaf, don't need to compute its reference count again.
+ *
+ * @bytenr if @bytenr == (u64)-1, only update nrefs->full_backref[level]
+ */
+static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
+ struct extent_buffer *eb, struct node_refs *nrefs,
+ u64 level, int check_all)
+{
+ struct ulist *roots;
+ u64 refs = 0;
+ u64 flags = 0;
+ int root_level = btrfs_header_level(root->node);
+ int check;
+ int ret;
+
+ if (nrefs->bytenr[level] == bytenr)
+ return 0;
+
+ if (bytenr != (u64)-1) {
+ /* the return value of this function seems a mistake */
+ ret = btrfs_lookup_extent_info(NULL, root, bytenr,
+ level, 1, &refs, &flags);
+ /* temporary fix */
+ if (ret < 0 && !check_all)
+ return ret;
+
+ nrefs->bytenr[level] = bytenr;
+ nrefs->refs[level] = refs;
+ nrefs->full_backref[level] = 0;
+ nrefs->checked[level] = 0;
+
+ if (refs > 1) {
+ ret = btrfs_find_all_roots(NULL, root->fs_info, bytenr,
+ 0, &roots);
+ if (ret)
+ return -EIO;
+
+ check = need_check(root, roots);
+ ulist_free(roots);
+ nrefs->need_check[level] = check;
+ } else {
+ if (!check_all) {
+ nrefs->need_check[level] = 1;
+ } else {
+ if (level == root_level) {
+ nrefs->need_check[level] = 1;
+ } else {
+ /*
+ * The node refs may have not been
+ * updated if upper needs checking (the
+ * lowest root_objectid) the node can
+ * be checked.
+ */
+ nrefs->need_check[level] =
+ nrefs->need_check[level + 1];
+ }
+ }
+ }
+ }
+
+ if (check_all && eb) {
+ calc_extent_flag(root, eb, &flags);
+ if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
+ nrefs->full_backref[level] = 1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function only handles BACKREF_MISSING,
+ * If corresponding extent item exists, increase the ref, else insert an extent
+ * item and backref.
+ *
+ * Returns error bits after repair.
+ */
+static int repair_tree_block_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct extent_buffer *node,
+ struct node_refs *nrefs, int level, int err)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ struct btrfs_path path;
+ struct btrfs_extent_item *ei;
+ struct btrfs_tree_block_info *bi;
+ struct btrfs_key key;
+ struct extent_buffer *eb;
+ u32 size = sizeof(*ei);
+ u32 node_size = root->fs_info->nodesize;
+ int insert_extent = 0;
+ int skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
+ int root_level = btrfs_header_level(root->node);
+ int generation;
+ int ret;
+ u64 owner;
+ u64 bytenr;
+ u64 flags = BTRFS_EXTENT_FLAG_TREE_BLOCK;
+ u64 parent = 0;
+
+ if ((err & BACKREF_MISSING) == 0)
+ return err;
+
+ WARN_ON(level > BTRFS_MAX_LEVEL);
+ WARN_ON(level < 0);
+
+ btrfs_init_path(&path);
+ bytenr = btrfs_header_bytenr(node);
+ owner = btrfs_header_owner(node);
+ generation = btrfs_header_generation(node);
+
+ key.objectid = bytenr;
+ key.type = (u8)-1;
+ key.offset = (u64)-1;
+
+ /* Search for the extent item */
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret <= 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
+ if (ret)
+ insert_extent = 1;
+
+ /* calculate if the extent item flag is full backref or not */
+ if (nrefs->full_backref[level] != 0)
+ flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
+
+ /* insert an extent item */
+ if (insert_extent) {
+ struct btrfs_disk_key copy_key;
+
+ generation = btrfs_header_generation(node);
+
+ if (level < root_level && nrefs->full_backref[level + 1] &&
+ owner != root->objectid) {
+ flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
+ }
+
+ key.objectid = bytenr;
+ if (!skinny_metadata) {
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = node_size;
+ size += sizeof(*bi);
+ } else {
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ key.offset = level;
+ }
+
+ btrfs_release_path(&path);
+ ret = btrfs_insert_empty_item(trans, extent_root, &path, &key,
+ size);
+ if (ret)
+ goto out;
+
+ eb = path.nodes[0];
+ ei = btrfs_item_ptr(eb, path.slots[0], struct btrfs_extent_item);
+
+ btrfs_set_extent_refs(eb, ei, 0);
+ btrfs_set_extent_generation(eb, ei, generation);
+ btrfs_set_extent_flags(eb, ei, flags);
+
+ if (!skinny_metadata) {
+ bi = (struct btrfs_tree_block_info *)(ei + 1);
+ memset_extent_buffer(eb, 0, (unsigned long)bi,
+ sizeof(*bi));
+ btrfs_set_disk_key_objectid(&copy_key, root->objectid);
+ btrfs_set_disk_key_type(&copy_key, 0);
+ btrfs_set_disk_key_offset(&copy_key, 0);
+
+ btrfs_set_tree_block_level(eb, bi, level);
+ btrfs_set_tree_block_key(eb, bi, &copy_key);
+ }
+ btrfs_mark_buffer_dirty(eb);
+ printf("Added an extent item [%llu %u]\n", bytenr, node_size);
+ btrfs_update_block_group(extent_root, bytenr, node_size, 1, 0);
+
+ nrefs->refs[level] = 0;
+ nrefs->full_backref[level] =
+ flags & BTRFS_BLOCK_FLAG_FULL_BACKREF;
+ btrfs_release_path(&path);
+ }
+
+ if (level < root_level && nrefs->full_backref[level + 1] &&
+ owner != root->objectid)
+ parent = nrefs->bytenr[level + 1];
+
+ /* increase the ref */
+ ret = btrfs_inc_extent_ref(trans, extent_root, bytenr, node_size,
+ parent, root->objectid, level, 0);
+
+ nrefs->refs[level]++;
+out:
+ btrfs_release_path(&path);
+ if (ret) {
+ error(
+ "failed to repair tree block ref start %llu root %llu due to %s",
+ bytenr, root->objectid, strerror(-ret));
+ } else {
+ printf("Added one tree block ref start %llu %s %llu\n",
+ bytenr, parent ? "parent" : "root",
+ parent ? parent : root->objectid);
+ err &= ~BACKREF_MISSING;
+ }
+
+ return err;
+}
+
+/*
+ * Update global fs information.
+ */
+static void account_bytes(struct btrfs_root *root, struct btrfs_path *path,
+ int level)
+{
+ u32 free_nrs;
+ struct extent_buffer *eb = path->nodes[level];
+
+ total_btree_bytes += eb->len;
+ if (fs_root_objectid(root->objectid))
+ total_fs_tree_bytes += eb->len;
+ if (btrfs_header_owner(eb) == BTRFS_EXTENT_TREE_OBJECTID)
+ total_extent_tree_bytes += eb->len;
+
+ if (level == 0) {
+ btree_space_waste += btrfs_leaf_free_space(root, eb);
+ } else {
+ free_nrs = (BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) -
+ btrfs_header_nritems(eb));
+ btree_space_waste += free_nrs * sizeof(struct btrfs_key_ptr);
+ }
+}
+
+/*
+ * Find the @index according by @ino and name.
+ * Notice:time efficiency is O(N)
+ *
+ * @root: the root of the fs/file tree
+ * @index_ret: the index as return value
+ * @namebuf: the name to match
+ * @name_len: the length of name to match
+ * @file_type: the file_type of INODE_ITEM to match
+ *
+ * Returns 0 if found and *@index_ret will be modified with right value
+ * Returns< 0 not found and *@index_ret will be (u64)-1
+ */
+static int find_dir_index(struct btrfs_root *root, u64 dirid, u64 location_id,
+ u64 *index_ret, char *namebuf, u32 name_len,
+ u8 file_type)
+{
+ struct btrfs_path path;
+ struct extent_buffer *node;
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+ struct btrfs_key location;
+ char name[BTRFS_NAME_LEN] = {0};
+
+ u32 total;
+ u32 cur = 0;
+ u32 len;
+ u32 data_len;
+ u8 filetype;
+ int slot;
+ int ret;
+
+ ASSERT(index_ret);
+
+ /* search from the last index */
+ key.objectid = dirid;
+ key.offset = (u64)-1;
+ key.type = BTRFS_DIR_INDEX_KEY;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ return ret;
+
+loop:
+ ret = btrfs_previous_item(root, &path, dirid, BTRFS_DIR_INDEX_KEY);
+ if (ret) {
+ ret = -ENOENT;
+ *index_ret = (64)-1;
+ goto out;
+ }
+ /* Check whether inode_id/filetype/name match */
+ node = path.nodes[0];
+ slot = path.slots[0];
+ di = btrfs_item_ptr(node, slot, struct btrfs_dir_item);
+ total = btrfs_item_size_nr(node, slot);
+ while (cur < total) {
+ ret = -ENOENT;
+ len = btrfs_dir_name_len(node, di);
+ data_len = btrfs_dir_data_len(node, di);
+
+ btrfs_dir_item_key_to_cpu(node, di, &location);
+ if (location.objectid != location_id ||
+ location.type != BTRFS_INODE_ITEM_KEY ||
+ location.offset != 0)
+ goto next;
+
+ filetype = btrfs_dir_type(node, di);
+ if (file_type != filetype)
+ goto next;
+
+ if (len > BTRFS_NAME_LEN)
+ len = BTRFS_NAME_LEN;
+
+ read_extent_buffer(node, name, (unsigned long)(di + 1), len);
+ if (len != name_len || strncmp(namebuf, name, len))
+ goto next;
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ *index_ret = key.offset;
+ ret = 0;
+ goto out;
+next:
+ len += sizeof(*di) + data_len;
+ di = (struct btrfs_dir_item *)((char *)di + len);
+ cur += len;
+ }
+ goto loop;
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * Find DIR_ITEM/DIR_INDEX for the given key and check it with the specified
+ * INODE_REF/INODE_EXTREF match.
+ *
+ * @root: the root of the fs/file tree
+ * @key: the key of the DIR_ITEM/DIR_INDEX, key->offset will be right
+ * value while find index
+ * @location_key: location key of the struct btrfs_dir_item to match
+ * @name: the name to match
+ * @namelen: the length of name
+ * @file_type: the type of file to math
+ *
+ * Return 0 if no error occurred.
+ * Return DIR_ITEM_MISSING/DIR_INDEX_MISSING if couldn't find
+ * DIR_ITEM/DIR_INDEX
+ * Return DIR_ITEM_MISMATCH/DIR_INDEX_MISMATCH if INODE_REF/INODE_EXTREF
+ * and DIR_ITEM/DIR_INDEX mismatch
+ */
+static int find_dir_item(struct btrfs_root *root, struct btrfs_key *key,
+ struct btrfs_key *location_key, char *name,
+ u32 namelen, u8 file_type)
+{
+ struct btrfs_path path;
+ struct extent_buffer *node;
+ struct btrfs_dir_item *di;
+ struct btrfs_key location;
+ char namebuf[BTRFS_NAME_LEN] = {0};
+ u32 total;
+ u32 cur = 0;
+ u32 len;
+ u32 data_len;
+ u8 filetype;
+ int slot;
+ int ret;
+
+ /* get the index by traversing all index */
+ if (key->type == BTRFS_DIR_INDEX_KEY && key->offset == (u64)-1) {
+ ret = find_dir_index(root, key->objectid,
+ location_key->objectid, &key->offset,
+ name, namelen, file_type);
+ if (ret)
+ ret = DIR_INDEX_MISSING;
+ return ret;
+ }
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
+ if (ret) {
+ ret = key->type == BTRFS_DIR_ITEM_KEY ? DIR_ITEM_MISSING :
+ DIR_INDEX_MISSING;
+ goto out;
+ }
+
+ /* Check whether inode_id/filetype/name match */
+ node = path.nodes[0];
+ slot = path.slots[0];
+ di = btrfs_item_ptr(node, slot, struct btrfs_dir_item);
+ total = btrfs_item_size_nr(node, slot);
+ while (cur < total) {
+ ret = key->type == BTRFS_DIR_ITEM_KEY ?
+ DIR_ITEM_MISMATCH : DIR_INDEX_MISMATCH;
+
+ len = btrfs_dir_name_len(node, di);
+ data_len = btrfs_dir_data_len(node, di);
+
+ btrfs_dir_item_key_to_cpu(node, di, &location);
+ if (location.objectid != location_key->objectid ||
+ location.type != location_key->type ||
+ location.offset != location_key->offset)
+ goto next;
+
+ filetype = btrfs_dir_type(node, di);
+ if (file_type != filetype)
+ goto next;
+
+ if (len > BTRFS_NAME_LEN) {
+ len = BTRFS_NAME_LEN;
+ warning("root %llu %s[%llu %llu] name too long %u, trimmed",
+ root->objectid,
+ key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX",
+ key->objectid, key->offset, len);
+ }
+ read_extent_buffer(node, namebuf, (unsigned long)(di + 1),
+ len);
+ if (len != namelen || strncmp(namebuf, name, len))
+ goto next;
+
+ ret = 0;
+ goto out;
+next:
+ len += sizeof(*di) + data_len;
+ di = (struct btrfs_dir_item *)((char *)di + len);
+ cur += len;
+ }
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * The ternary means dir item, dir index and relative inode ref.
+ * The function handles errs: INODE_MISSING, DIR_INDEX_MISSING
+ * DIR_INDEX_MISMATCH, DIR_ITEM_MISSING, DIR_ITEM_MISMATCH by the follow
+ * strategy:
+ * If two of three is missing or mismatched, delete the existing one.
+ * If one of three is missing or mismatched, add the missing one.
+ *
+ * returns 0 means success.
+ * returns not 0 means on error;
+ */
+int repair_ternary_lowmem(struct btrfs_root *root, u64 dir_ino, u64 ino,
+ u64 index, char *name, int name_len, u8 filetype,
+ int err)
+{
+ struct btrfs_trans_handle *trans;
+ int stage = 0;
+ int ret = 0;
+
+ /*
+ * stage shall be one of following valild values:
+ * 0: Fine, nothing to do.
+ * 1: One of three is wrong, so add missing one.
+ * 2: Two of three is wrong, so delete existed one.
+ */
+ if (err & (DIR_INDEX_MISMATCH | DIR_INDEX_MISSING))
+ stage++;
+ if (err & (DIR_ITEM_MISMATCH | DIR_ITEM_MISSING))
+ stage++;
+ if (err & (INODE_REF_MISSING))
+ stage++;
+
+ /* stage must be smllarer than 3 */
+ ASSERT(stage < 3);
+
+ trans = btrfs_start_transaction(root, 1);
+ if (stage == 2) {
+ ret = btrfs_unlink(trans, root, ino, dir_ino, index, name,
+ name_len, 0);
+ goto out;
+ }
+ if (stage == 1) {
+ ret = btrfs_add_link(trans, root, ino, dir_ino, name, name_len,
+ filetype, &index, 1, 1);
+ goto out;
+ }
+out:
+ btrfs_commit_transaction(trans, root);
+
+ if (ret)
+ error("fail to repair inode %llu name %s filetype %u",
+ ino, name, filetype);
+ else
+ printf("%s ref/dir_item of inode %llu name %s filetype %u\n",
+ stage == 2 ? "Delete" : "Add",
+ ino, name, filetype);
+
+ return ret;
+}
+
+/*
+ * Prints inode ref error message
+ */
+static void print_inode_ref_err(struct btrfs_root *root, struct btrfs_key *key,
+ u64 index, const char *namebuf, int name_len,
+ u8 filetype, int err)
+{
+ if (!err)
+ return;
+
+ /* root dir error */
+ if (key->objectid == BTRFS_FIRST_FREE_OBJECTID) {
+ error(
+ "root %llu root dir shouldn't have INODE REF[%llu %llu] name %s",
+ root->objectid, key->objectid, key->offset, namebuf);
+ return;
+ }
+
+ /* normal error */
+ if (err & (DIR_ITEM_MISMATCH | DIR_ITEM_MISSING))
+ error("root %llu DIR ITEM[%llu %llu] %s name %s filetype %u",
+ root->objectid, key->offset,
+ btrfs_name_hash(namebuf, name_len),
+ err & DIR_ITEM_MISMATCH ? "mismatch" : "missing",
+ namebuf, filetype);
+ if (err & (DIR_INDEX_MISMATCH | DIR_INDEX_MISSING))
+ error("root %llu DIR INDEX[%llu %llu] %s name %s filetype %u",
+ root->objectid, key->offset, index,
+ err & DIR_ITEM_MISMATCH ? "mismatch" : "missing",
+ namebuf, filetype);
+}
+
+/*
+ * Traverse the given INODE_REF and call find_dir_item() to find related
+ * DIR_ITEM/DIR_INDEX.
+ *
+ * @root: the root of the fs/file tree
+ * @ref_key: the key of the INODE_REF
+ * @path the path provides node and slot
+ * @refs: the count of INODE_REF
+ * @mode: the st_mode of INODE_ITEM
+ * @name_ret: returns with the first ref's name
+ * @name_len_ret: len of the name_ret
+ *
+ * Return 0 if no error occurred.
+ */
+static int check_inode_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
+ struct btrfs_path *path, char *name_ret,
+ u32 *namelen_ret, u64 *refs_ret, int mode)
+{
+ struct btrfs_key key;
+ struct btrfs_key location;
+ struct btrfs_inode_ref *ref;
+ struct extent_buffer *node;
+ char namebuf[BTRFS_NAME_LEN] = {0};
+ u32 total;
+ u32 cur = 0;
+ u32 len;
+ u32 name_len;
+ u64 index;
+ int ret;
+ int err = 0;
+ int tmp_err;
+ int slot;
+ int need_research = 0;
+ u64 refs;
+
+begin:
+ err = 0;
+ cur = 0;
+ refs = *refs_ret;
+
+ /* since after repair, path and the dir item may be changed */
+ if (need_research) {
+ need_research = 0;
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(NULL, root, ref_key, path, 0, 0);
+ /*
+ * The item was deleted, let the path point to the last checked
+ * item.
+ */
+ if (ret > 0) {
+ if (path->slots[0] == 0)
+ btrfs_prev_leaf(root, path);
+ else
+ path->slots[0]--;
+ }
+ if (ret)
+ goto out;
+ }
+
+ location.objectid = ref_key->objectid;
+ location.type = BTRFS_INODE_ITEM_KEY;
+ location.offset = 0;
+ node = path->nodes[0];
+ slot = path->slots[0];
+
+ memset(namebuf, 0, sizeof(namebuf) / sizeof(*namebuf));
+ ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref);
+ total = btrfs_item_size_nr(node, slot);
+
+next:
+ /* Update inode ref count */
+ refs++;
+ tmp_err = 0;
+ index = btrfs_inode_ref_index(node, ref);
+ name_len = btrfs_inode_ref_name_len(node, ref);
+
+ if (name_len <= BTRFS_NAME_LEN) {
+ len = name_len;
+ } else {
+ len = BTRFS_NAME_LEN;
+ warning("root %llu INODE_REF[%llu %llu] name too long",
+ root->objectid, ref_key->objectid, ref_key->offset);
+ }
+
+ read_extent_buffer(node, namebuf, (unsigned long)(ref + 1), len);
+
+ /* copy the first name found to name_ret */
+ if (refs == 1 && name_ret) {
+ memcpy(name_ret, namebuf, len);
+ *namelen_ret = len;
+ }
+
+ /* Check root dir ref */
+ if (ref_key->objectid == BTRFS_FIRST_FREE_OBJECTID) {
+ if (index != 0 || len != strlen("..") ||
+ strncmp("..", namebuf, len) ||
+ ref_key->offset != BTRFS_FIRST_FREE_OBJECTID) {
+ /* set err bits then repair will delete the ref */
+ err |= DIR_INDEX_MISSING;
+ err |= DIR_ITEM_MISSING;
+ }
+ goto end;
+ }
+
+ /* Find related DIR_INDEX */
+ key.objectid = ref_key->offset;
+ key.type = BTRFS_DIR_INDEX_KEY;
+ key.offset = index;
+ tmp_err |= find_dir_item(root, &key, &location, namebuf, len,
+ imode_to_type(mode));
+
+ /* Find related dir_item */
+ key.objectid = ref_key->offset;
+ key.type = BTRFS_DIR_ITEM_KEY;
+ key.offset = btrfs_name_hash(namebuf, len);
+ tmp_err |= find_dir_item(root, &key, &location, namebuf, len,
+ imode_to_type(mode));
+end:
+ if (tmp_err && repair) {
+ ret = repair_ternary_lowmem(root, ref_key->offset,
+ ref_key->objectid, index, namebuf,
+ name_len, imode_to_type(mode),
+ tmp_err);
+ if (!ret) {
+ need_research = 1;
+ goto begin;
+ }
+ }
+ print_inode_ref_err(root, ref_key, index, namebuf, name_len,
+ imode_to_type(mode), tmp_err);
+ err |= tmp_err;
+ len = sizeof(*ref) + name_len;
+ ref = (struct btrfs_inode_ref *)((char *)ref + len);
+ cur += len;
+ if (cur < total)
+ goto next;
+
+out:
+ *refs_ret = refs;
+ return err;
+}
+
+/*
+ * Traverse the given INODE_EXTREF and call find_dir_item() to find related
+ * DIR_ITEM/DIR_INDEX.
+ *
+ * @root: the root of the fs/file tree
+ * @ref_key: the key of the INODE_EXTREF
+ * @refs: the count of INODE_EXTREF
+ * @mode: the st_mode of INODE_ITEM
+ *
+ * Return 0 if no error occurred.
+ */
+static int check_inode_extref(struct btrfs_root *root,
+ struct btrfs_key *ref_key,
+ struct extent_buffer *node, int slot, u64 *refs,
+ int mode)
+{
+ struct btrfs_key key;
+ struct btrfs_key location;
+ struct btrfs_inode_extref *extref;
+ char namebuf[BTRFS_NAME_LEN] = {0};
+ u32 total;
+ u32 cur = 0;
+ u32 len;
+ u32 name_len;
+ u64 index;
+ u64 parent;
+ int ret;
+ int err = 0;
+
+ location.objectid = ref_key->objectid;
+ location.type = BTRFS_INODE_ITEM_KEY;
+ location.offset = 0;
+
+ extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref);
+ total = btrfs_item_size_nr(node, slot);
+
+next:
+ /* update inode ref count */
+ (*refs)++;
+ name_len = btrfs_inode_extref_name_len(node, extref);
+ index = btrfs_inode_extref_index(node, extref);
+ parent = btrfs_inode_extref_parent(node, extref);
+ if (name_len <= BTRFS_NAME_LEN) {
+ len = name_len;
+ } else {
+ len = BTRFS_NAME_LEN;
+ warning("root %llu INODE_EXTREF[%llu %llu] name too long",
+ root->objectid, ref_key->objectid, ref_key->offset);
+ }
+ read_extent_buffer(node, namebuf, (unsigned long)(extref + 1), len);
+
+ /* Check root dir ref name */
+ if (index == 0 && strncmp(namebuf, "..", name_len)) {
+ error("root %llu INODE_EXTREF[%llu %llu] ROOT_DIR name shouldn't be %s",
+ root->objectid, ref_key->objectid, ref_key->offset,
+ namebuf);
+ err |= ROOT_DIR_ERROR;
+ }
+
+ /* find related dir_index */
+ key.objectid = parent;
+ key.type = BTRFS_DIR_INDEX_KEY;
+ key.offset = index;
+ ret = find_dir_item(root, &key, &location, namebuf, len, mode);
+ err |= ret;
+
+ /* find related dir_item */
+ key.objectid = parent;
+ key.type = BTRFS_DIR_ITEM_KEY;
+ key.offset = btrfs_name_hash(namebuf, len);
+ ret = find_dir_item(root, &key, &location, namebuf, len, mode);
+ err |= ret;
+
+ len = sizeof(*extref) + name_len;
+ extref = (struct btrfs_inode_extref *)((char *)extref + len);
+ cur += len;
+
+ if (cur < total)
+ goto next;
+
+ return err;
+}
+
+/*
+ * Find INODE_REF/INODE_EXTREF for the given key and check it with the specified
+ * DIR_ITEM/DIR_INDEX match.
+ * Return with @index_ret.
+ *
+ * @root: the root of the fs/file tree
+ * @key: the key of the INODE_REF/INODE_EXTREF
+ * @name: the name in the INODE_REF/INODE_EXTREF
+ * @namelen: the length of name in the INODE_REF/INODE_EXTREF
+ * @index_ret: the index in the INODE_REF/INODE_EXTREF,
+ * value (64)-1 means do not check index
+ * @ext_ref: the EXTENDED_IREF feature
+ *
+ * Return 0 if no error occurred.
+ * Return >0 for error bitmap
+ */
+static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key,
+ char *name, int namelen, u64 *index_ret,
+ unsigned int ext_ref)
+{
+ struct btrfs_path path;
+ struct btrfs_inode_ref *ref;
+ struct btrfs_inode_extref *extref;
+ struct extent_buffer *node;
+ char ref_namebuf[BTRFS_NAME_LEN] = {0};
+ u32 total;
+ u32 cur = 0;
+ u32 len;
+ u32 ref_namelen;
+ u64 ref_index;
+ u64 parent;
+ u64 dir_id;
+ int slot;
+ int ret;
+
+ ASSERT(index_ret);
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
+ if (ret) {
+ ret = INODE_REF_MISSING;
+ goto extref;
+ }
+
+ node = path.nodes[0];
+ slot = path.slots[0];
+
+ ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref);
+ total = btrfs_item_size_nr(node, slot);
+
+ /* Iterate all entry of INODE_REF */
+ while (cur < total) {
+ ret = INODE_REF_MISSING;
+
+ ref_namelen = btrfs_inode_ref_name_len(node, ref);
+ ref_index = btrfs_inode_ref_index(node, ref);
+ if (*index_ret != (u64)-1 && *index_ret != ref_index)
+ goto next_ref;
+
+ if (cur + sizeof(*ref) + ref_namelen > total ||
+ ref_namelen > BTRFS_NAME_LEN) {
+ warning("root %llu INODE %s[%llu %llu] name too long",
+ root->objectid,
+ key->type == BTRFS_INODE_REF_KEY ?
+ "REF" : "EXTREF",
+ key->objectid, key->offset);
+
+ if (cur + sizeof(*ref) > total)
+ break;
+ len = min_t(u32, total - cur - sizeof(*ref),
+ BTRFS_NAME_LEN);
+ } else {
+ len = ref_namelen;
+ }
+
+ read_extent_buffer(node, ref_namebuf, (unsigned long)(ref + 1),
+ len);
+
+ if (len != namelen || strncmp(ref_namebuf, name, len))
+ goto next_ref;
+
+ *index_ret = ref_index;
+ ret = 0;
+ goto out;
+next_ref:
+ len = sizeof(*ref) + ref_namelen;
+ ref = (struct btrfs_inode_ref *)((char *)ref + len);
+ cur += len;
+ }
+
+extref:
+ /* Skip if not support EXTENDED_IREF feature */
+ if (!ext_ref)
+ goto out;
+
+ btrfs_release_path(&path);
+ btrfs_init_path(&path);
+
+ dir_id = key->offset;
+ key->type = BTRFS_INODE_EXTREF_KEY;
+ key->offset = btrfs_extref_hash(dir_id, name, namelen);
+
+ ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
+ if (ret) {
+ ret = INODE_REF_MISSING;
+ goto out;
+ }
+
+ node = path.nodes[0];
+ slot = path.slots[0];
+
+ extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref);
+ cur = 0;
+ total = btrfs_item_size_nr(node, slot);
+
+ /* Iterate all entry of INODE_EXTREF */
+ while (cur < total) {
+ ret = INODE_REF_MISSING;
+
+ ref_namelen = btrfs_inode_extref_name_len(node, extref);
+ ref_index = btrfs_inode_extref_index(node, extref);
+ parent = btrfs_inode_extref_parent(node, extref);
+ if (*index_ret != (u64)-1 && *index_ret != ref_index)
+ goto next_extref;
+
+ if (parent != dir_id)
+ goto next_extref;
+
+ if (ref_namelen <= BTRFS_NAME_LEN) {
+ len = ref_namelen;
+ } else {
+ len = BTRFS_NAME_LEN;
+ warning("root %llu INODE %s[%llu %llu] name too long",
+ root->objectid,
+ key->type == BTRFS_INODE_REF_KEY ?
+ "REF" : "EXTREF",
+ key->objectid, key->offset);
+ }
+ read_extent_buffer(node, ref_namebuf,
+ (unsigned long)(extref + 1), len);
+
+ if (len != namelen || strncmp(ref_namebuf, name, len))
+ goto next_extref;
+
+ *index_ret = ref_index;
+ ret = 0;
+ goto out;
+
+next_extref:
+ len = sizeof(*extref) + ref_namelen;
+ extref = (struct btrfs_inode_extref *)((char *)extref + len);
+ cur += len;
+
+ }
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+static int create_inode_item_lowmem(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 ino,
+ u8 filetype)
+{
+ u32 mode = (filetype == BTRFS_FT_DIR ? S_IFDIR : S_IFREG) | 0755;
+
+ return insert_inode_item(trans, root, ino, 0, 0, 0, mode);
+}
+
+/*
+ * Insert the missing inode item.
+ *
+ * Returns 0 means success.
+ * Returns <0 means error.
+ */
+static int repair_inode_item_missing(struct btrfs_root *root, u64 ino,
+ u8 filetype)
+{
+ struct btrfs_key key;
+ struct btrfs_trans_handle *trans;
+ struct btrfs_path path;
+ int ret;
+
+ key.objectid = ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ btrfs_init_path(&path);
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = btrfs_search_slot(trans, root, &key, &path, 1, 1);
+ if (ret < 0 || !ret)
+ goto fail;
+
+ /* insert inode item */
+ create_inode_item_lowmem(trans, root, ino, filetype);
+ ret = 0;
+fail:
+ btrfs_commit_transaction(trans, root);
+out:
+ if (ret)
+ error("failed to repair root %llu INODE ITEM[%llu] missing",
+ root->objectid, ino);
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * Call repair_inode_item_missing and repair_ternary_lowmem to repair
+ *
+ * Returns error after repair
+ */
+static int repair_dir_item(struct btrfs_root *root, u64 dirid, u64 ino,
+ u64 index, u8 filetype, char *namebuf, u32 name_len,
+ int err)
+{
+ int ret;
+
+ if (err & INODE_ITEM_MISSING) {
+ ret = repair_inode_item_missing(root, ino, filetype);
+ if (!ret)
+ err &= ~(INODE_ITEM_MISMATCH | INODE_ITEM_MISSING);
+ }
+
+ if (err & ~(INODE_ITEM_MISMATCH | INODE_ITEM_MISSING)) {
+ ret = repair_ternary_lowmem(root, dirid, ino, index, namebuf,
+ name_len, filetype, err);
+ if (!ret) {
+ err &= ~(DIR_INDEX_MISMATCH | DIR_INDEX_MISSING);
+ err &= ~(DIR_ITEM_MISMATCH | DIR_ITEM_MISSING);
+ err &= ~(INODE_REF_MISSING);
+ }
+ }
+ return err;
+}
+
+static void print_dir_item_err(struct btrfs_root *root, struct btrfs_key *key,
+ u64 ino, u64 index, const char *namebuf,
+ int name_len, u8 filetype, int err)
+{
+ if (err & (DIR_ITEM_MISMATCH | DIR_ITEM_MISSING)) {
+ error("root %llu DIR ITEM[%llu %llu] name %s filetype %d %s",
+ root->objectid, key->objectid, key->offset, namebuf,
+ filetype,
+ err & DIR_ITEM_MISMATCH ? "mismath" : "missing");
+ }
+
+ if (err & (DIR_INDEX_MISMATCH | DIR_INDEX_MISSING)) {
+ error("root %llu DIR INDEX[%llu %llu] name %s filetype %d %s",
+ root->objectid, key->objectid, index, namebuf, filetype,
+ err & DIR_ITEM_MISMATCH ? "mismath" : "missing");
+ }
+
+ if (err & (INODE_ITEM_MISSING | INODE_ITEM_MISMATCH)) {
+ error(
+ "root %llu INODE_ITEM[%llu] index %llu name %s filetype %d %s",
+ root->objectid, ino, index, namebuf, filetype,
+ err & INODE_ITEM_MISMATCH ? "mismath" : "missing");
+ }
+
+ if (err & INODE_REF_MISSING)
+ error(
+ "root %llu INODE REF[%llu, %llu] name %s filetype %u missing",
+ root->objectid, ino, key->objectid, namebuf, filetype);
+
+}
+
+/*
+ * Traverse the given DIR_ITEM/DIR_INDEX and check related INODE_ITEM and
+ * call find_inode_ref() to check related INODE_REF/INODE_EXTREF.
+ *
+ * @root: the root of the fs/file tree
+ * @key: the key of the INODE_REF/INODE_EXTREF
+ * @path: the path
+ * @size: the st_size of the INODE_ITEM
+ * @ext_ref: the EXTENDED_IREF feature
+ *
+ * Return 0 if no error occurred.
+ * Return DIR_COUNT_AGAIN if the isize of the inode should be recalculated.
+ */
+static int check_dir_item(struct btrfs_root *root, struct btrfs_key *di_key,
+ struct btrfs_path *path, u64 *size,
+ unsigned int ext_ref)
+{
+ struct btrfs_dir_item *di;
+ struct btrfs_inode_item *ii;
+ struct btrfs_key key;
+ struct btrfs_key location;
+ struct extent_buffer *node;
+ int slot;
+ char namebuf[BTRFS_NAME_LEN] = {0};
+ u32 total;
+ u32 cur = 0;
+ u32 len;
+ u32 name_len;
+ u32 data_len;
+ u8 filetype;
+ u32 mode = 0;
+ u64 index;
+ int ret;
+ int err;
+ int tmp_err;
+ int need_research = 0;
+
+ /*
+ * For DIR_ITEM set index to (u64)-1, so that find_inode_ref
+ * ignore index check.
+ */
+ if (di_key->type == BTRFS_DIR_INDEX_KEY)
+ index = di_key->offset;
+ else
+ index = (u64)-1;
+begin:
+ err = 0;
+ cur = 0;
+
+ /* since after repair, path and the dir item may be changed */
+ if (need_research) {
+ need_research = 0;
+ err |= DIR_COUNT_AGAIN;
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(NULL, root, di_key, path, 0, 0);
+ /* the item was deleted, let path point the last checked item */
+ if (ret > 0) {
+ if (path->slots[0] == 0)
+ btrfs_prev_leaf(root, path);
+ else
+ path->slots[0]--;
+ }
+ if (ret)
+ goto out;
+ }
+
+ node = path->nodes[0];
+ slot = path->slots[0];
+
+ di = btrfs_item_ptr(node, slot, struct btrfs_dir_item);
+ total = btrfs_item_size_nr(node, slot);
+ memset(namebuf, 0, sizeof(namebuf) / sizeof(*namebuf));
+
+ while (cur < total) {
+ data_len = btrfs_dir_data_len(node, di);
+ tmp_err = 0;
+ if (data_len)
+ error("root %llu %s[%llu %llu] data_len shouldn't be %u",
+ root->objectid,
+ di_key->type == BTRFS_DIR_ITEM_KEY ? "DIR_ITEM" : "DIR_INDEX",
+ di_key->objectid, di_key->offset, data_len);
+
+ name_len = btrfs_dir_name_len(node, di);
+ if (name_len <= BTRFS_NAME_LEN) {
+ len = name_len;
+ } else {
+ len = BTRFS_NAME_LEN;
+ warning("root %llu %s[%llu %llu] name too long",
+ root->objectid,
+ di_key->type == BTRFS_DIR_ITEM_KEY ? "DIR_ITEM" : "DIR_INDEX",
+ di_key->objectid, di_key->offset);
+ }
+ (*size) += name_len;
+ read_extent_buffer(node, namebuf, (unsigned long)(di + 1),
+ len);
+ filetype = btrfs_dir_type(node, di);
+
+ if (di_key->type == BTRFS_DIR_ITEM_KEY &&
+ di_key->offset != btrfs_name_hash(namebuf, len)) {
+ err |= -EIO;
+ error("root %llu DIR_ITEM[%llu %llu] name %s namelen %u filetype %u mismatch with its hash, wanted %llu have %llu",
+ root->objectid, di_key->objectid, di_key->offset,
+ namebuf, len, filetype, di_key->offset,
+ btrfs_name_hash(namebuf, len));
+ }
+
+ btrfs_dir_item_key_to_cpu(node, di, &location);
+ /* Ignore related ROOT_ITEM check */
+ if (location.type == BTRFS_ROOT_ITEM_KEY)
+ goto next;
+
+ btrfs_release_path(path);
+ /* Check relative INODE_ITEM(existence/filetype) */
+ ret = btrfs_search_slot(NULL, root, &location, path, 0, 0);
+ if (ret) {
+ tmp_err |= INODE_ITEM_MISSING;
+ goto next;
+ }
+
+ ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_inode_item);
+ mode = btrfs_inode_mode(path->nodes[0], ii);
+ if (imode_to_type(mode) != filetype) {
+ tmp_err |= INODE_ITEM_MISMATCH;
+ goto next;
+ }
+
+ /* Check relative INODE_REF/INODE_EXTREF */
+ key.objectid = location.objectid;
+ key.type = BTRFS_INODE_REF_KEY;
+ key.offset = di_key->objectid;
+ tmp_err |= find_inode_ref(root, &key, namebuf, len,
+ &index, ext_ref);
+
+ /* check relative INDEX/ITEM */
+ key.objectid = di_key->objectid;
+ if (key.type == BTRFS_DIR_ITEM_KEY) {
+ key.type = BTRFS_DIR_INDEX_KEY;
+ key.offset = index;
+ } else {
+ key.type = BTRFS_DIR_ITEM_KEY;
+ key.offset = btrfs_name_hash(namebuf, name_len);
+ }
+
+ tmp_err |= find_dir_item(root, &key, &location, namebuf,
+ name_len, filetype);
+ /* find_dir_item may find index */
+ if (key.type == BTRFS_DIR_INDEX_KEY)
+ index = key.offset;
+next:
+
+ if (tmp_err && repair) {
+ ret = repair_dir_item(root, di_key->objectid,
+ location.objectid, index,
+ imode_to_type(mode), namebuf,
+ name_len, tmp_err);
+ if (ret != tmp_err) {
+ need_research = 1;
+ goto begin;
+ }
+ }
+ btrfs_release_path(path);
+ print_dir_item_err(root, di_key, location.objectid, index,
+ namebuf, name_len, filetype, tmp_err);
+ err |= tmp_err;
+ len = sizeof(*di) + name_len + data_len;
+ di = (struct btrfs_dir_item *)((char *)di + len);
+ cur += len;
+
+ if (di_key->type == BTRFS_DIR_INDEX_KEY && cur < total) {
+ error("root %llu DIR_INDEX[%llu %llu] should contain only one entry",
+ root->objectid, di_key->objectid,
+ di_key->offset);
+ break;
+ }
+ }
+out:
+ /* research path */
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(NULL, root, di_key, path, 0, 0);
+ if (ret)
+ err |= ret > 0 ? -ENOENT : ret;
+ return err;
+}
+
+/*
+ * Wrapper function of btrfs_punch_hole.
+ *
+ * Returns 0 means success.
+ * Returns not 0 means error.
+ */
+static int punch_extent_hole(struct btrfs_root *root, u64 ino, u64 start,
+ u64 len)
+{
+ struct btrfs_trans_handle *trans;
+ int ret = 0;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans))
+ return PTR_ERR(trans);
+
+ ret = btrfs_punch_hole(trans, root, ino, start, len);
+ if (ret)
+ error("failed to add hole [%llu, %llu] in inode [%llu]",
+ start, len, ino);
+ else
+ printf("Add a hole [%llu, %llu] in inode [%llu]\n", start, len,
+ ino);
+
+ btrfs_commit_transaction(trans, root);
+ return ret;
+}
+
+/*
+ * Check file extent datasum/hole, update the size of the file extents,
+ * check and update the last offset of the file extent.
+ *
+ * @root: the root of fs/file tree.
+ * @fkey: the key of the file extent.
+ * @nodatasum: INODE_NODATASUM feature.
+ * @size: the sum of all EXTENT_DATA items size for this inode.
+ * @end: the offset of the last extent.
+ *
+ * Return 0 if no error occurred.
+ */
+static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
+ struct extent_buffer *node, int slot,
+ unsigned int nodatasum, u64 *size, u64 *end)
+{
+ struct btrfs_file_extent_item *fi;
+ u64 disk_bytenr;
+ u64 disk_num_bytes;
+ u64 extent_num_bytes;
+ u64 extent_offset;
+ u64 csum_found; /* In byte size, sectorsize aligned */
+ u64 search_start; /* Logical range start we search for csum */
+ u64 search_len; /* Logical range len we search for csum */
+ unsigned int extent_type;
+ unsigned int is_hole;
+ int compressed = 0;
+ int ret;
+ int err = 0;
+
+ fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
+
+ /* Check inline extent */
+ extent_type = btrfs_file_extent_type(node, fi);
+ if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ struct btrfs_item *e = btrfs_item_nr(slot);
+ u32 item_inline_len;
+
+ item_inline_len = btrfs_file_extent_inline_item_len(node, e);
+ extent_num_bytes = btrfs_file_extent_inline_len(node, slot, fi);
+ compressed = btrfs_file_extent_compression(node, fi);
+ if (extent_num_bytes == 0) {
+ error(
+ "root %llu EXTENT_DATA[%llu %llu] has empty inline extent",
+ root->objectid, fkey->objectid, fkey->offset);
+ err |= FILE_EXTENT_ERROR;
+ }
+ if (!compressed && extent_num_bytes != item_inline_len) {
+ error(
+ "root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u",
+ root->objectid, fkey->objectid, fkey->offset,
+ extent_num_bytes, item_inline_len);
+ err |= FILE_EXTENT_ERROR;
+ }
+ *end += extent_num_bytes;
+ *size += extent_num_bytes;
+ return err;
+ }
+
+ /* Check extent type */
+ if (extent_type != BTRFS_FILE_EXTENT_REG &&
+ extent_type != BTRFS_FILE_EXTENT_PREALLOC) {
+ err |= FILE_EXTENT_ERROR;
+ error("root %llu EXTENT_DATA[%llu %llu] type bad",
+ root->objectid, fkey->objectid, fkey->offset);
+ return err;
+ }
+
+ /* Check REG_EXTENT/PREALLOC_EXTENT */
+ disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
+ disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
+ extent_num_bytes = btrfs_file_extent_num_bytes(node, fi);
+ extent_offset = btrfs_file_extent_offset(node, fi);
+ compressed = btrfs_file_extent_compression(node, fi);
+ is_hole = (disk_bytenr == 0) && (disk_num_bytes == 0);
+
+ /*
+ * Check EXTENT_DATA csum
+ *
+ * For plain (uncompressed) extent, we should only check the range
+ * we're referring to, as it's possible that part of prealloc extent
+ * has been written, and has csum:
+ *
+ * |<--- Original large preallocated extent A ---->|
+ * |<- Prealloc File Extent ->|<- Regular Extent ->|
+ * No csum Has csum
+ *
+ * For compressed extent, we should check the whole range.
+ */
+ if (!compressed) {
+ search_start = disk_bytenr + extent_offset;
+ search_len = extent_num_bytes;
+ } else {
+ search_start = disk_bytenr;
+ search_len = disk_num_bytes;
+ }
+ ret = count_csum_range(root->fs_info, search_start, search_len,
+ &csum_found);
+ if (csum_found > 0 && nodatasum) {
+ err |= ODD_CSUM_ITEM;
+ error("root %llu EXTENT_DATA[%llu %llu] nodatasum shouldn't have datasum",
+ root->objectid, fkey->objectid, fkey->offset);
+ } else if (extent_type == BTRFS_FILE_EXTENT_REG && !nodatasum &&
+ !is_hole && (ret < 0 || csum_found < search_len)) {
+ err |= CSUM_ITEM_MISSING;
+ error("root %llu EXTENT_DATA[%llu %llu] csum missing, have: %llu, expected: %llu",
+ root->objectid, fkey->objectid, fkey->offset,
+ csum_found, search_len);
+ } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC &&
+ csum_found > 0) {
+ err |= ODD_CSUM_ITEM;
+ error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu",
+ root->objectid, fkey->objectid, fkey->offset, csum_found);
+ }
+
+ /* Check EXTENT_DATA hole */
+ if (!no_holes && *end != fkey->offset) {
+ if (repair)
+ ret = punch_extent_hole(root, fkey->objectid,
+ *end, fkey->offset - *end);
+ if (!repair || ret) {
+ err |= FILE_EXTENT_ERROR;
+ error(
+"root %llu EXTENT_DATA[%llu %llu] gap exists, expected: EXTENT_DATA[%llu %llu]",
+ root->objectid, fkey->objectid, fkey->offset,
+ fkey->objectid, *end);
+ }
+ }
+
+ *end += extent_num_bytes;
+ if (!is_hole)
+ *size += extent_num_bytes;
+
+ return err;
+}
+
+static int __count_dir_isize(struct btrfs_root *root, u64 ino, int type,
+ u64 *size_ret)
+{
+ struct btrfs_key key;
+ struct btrfs_path path;
+ u32 len;
+ struct btrfs_dir_item *di;
+ int ret;
+ int cur = 0;
+ int total = 0;
+
+ ASSERT(size_ret);
+ *size_ret = 0;
+
+ key.objectid = ino;
+ key.type = type;
+ key.offset = (u64)-1;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0) {
+ ret = -EIO;
+ goto out;
+ }
+ /* if found, go to spacial case */
+ if (ret == 0)
+ goto special_case;
+
+loop:
+ ret = btrfs_previous_item(root, &path, ino, type);
+
+ if (ret) {
+ ret = 0;
+ goto out;
+ }
+
+special_case:
+ di = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_dir_item);
+ cur = 0;
+ total = btrfs_item_size_nr(path.nodes[0], path.slots[0]);
+
+ while (cur < total) {
+ len = btrfs_dir_name_len(path.nodes[0], di);
+ if (len > BTRFS_NAME_LEN)
+ len = BTRFS_NAME_LEN;
+ *size_ret += len;
+
+ len += btrfs_dir_data_len(path.nodes[0], di);
+ len += sizeof(*di);
+ di = (struct btrfs_dir_item *)((char *)di + len);
+ cur += len;
+ }
+ goto loop;
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+static int count_dir_isize(struct btrfs_root *root, u64 ino, u64 *size)
+{
+ u64 item_size;
+ u64 index_size;
+ int ret;
+
+ ASSERT(size);
+ ret = __count_dir_isize(root, ino, BTRFS_DIR_ITEM_KEY, &item_size);
+ if (ret)
+ goto out;
+
+ ret = __count_dir_isize(root, ino, BTRFS_DIR_INDEX_KEY, &index_size);
+ if (ret)
+ goto out;
+
+ *size = item_size + index_size;
+
+out:
+ if (ret)
+ error("failed to count root %llu INODE[%llu] root size",
+ root->objectid, ino);
+ return ret;
+}
+
+/*
+ * Set inode item nbytes to @nbytes
+ *
+ * Returns 0 on success
+ * Returns != 0 on error
+ */
+static int repair_inode_nbytes_lowmem(struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 ino, u64 nbytes)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_inode_item *ii;
+ struct btrfs_key key;
+ struct btrfs_key research_key;
+ int err = 0;
+ int ret;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &research_key, path->slots[0]);
+
+ key.objectid = ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ err |= ret;
+ goto out;
+ }
+
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ if (ret > 0)
+ ret = -ENOENT;
+ if (ret) {
+ err |= ret;
+ goto fail;
+ }
+
+ ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_inode_item);
+ btrfs_set_inode_nbytes(path->nodes[0], ii, nbytes);
+ btrfs_mark_buffer_dirty(path->nodes[0]);
+fail:
+ btrfs_commit_transaction(trans, root);
+out:
+ if (ret)
+ error("failed to set nbytes in inode %llu root %llu",
+ ino, root->root_key.objectid);
+ else
+ printf("Set nbytes in inode item %llu root %llu\n to %llu", ino,
+ root->root_key.objectid, nbytes);
+
+ /* research path */
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(NULL, root, &research_key, path, 0, 0);
+ err |= ret;
+
+ return err;
+}
+
+/*
+ * Set directory inode isize to @isize.
+ *
+ * Returns 0 on success.
+ * Returns != 0 on error.
+ */
+static int repair_dir_isize_lowmem(struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 ino, u64 isize)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_inode_item *ii;
+ struct btrfs_key key;
+ struct btrfs_key research_key;
+ int ret;
+ int err = 0;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &research_key, path->slots[0]);
+
+ key.objectid = ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ err |= ret;
+ goto out;
+ }
+
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ if (ret > 0)
+ ret = -ENOENT;
+ if (ret) {
+ err |= ret;
+ goto fail;
+ }
+
+ ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_inode_item);
+ btrfs_set_inode_size(path->nodes[0], ii, isize);
+ btrfs_mark_buffer_dirty(path->nodes[0]);
+fail:
+ btrfs_commit_transaction(trans, root);
+out:
+ if (ret)
+ error("failed to set isize in inode %llu root %llu",
+ ino, root->root_key.objectid);
+ else
+ printf("Set isize in inode %llu root %llu to %llu\n",
+ ino, root->root_key.objectid, isize);
+
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(NULL, root, &research_key, path, 0, 0);
+ err |= ret;
+
+ return err;
+}
+
+/*
+ * Wrapper function for btrfs_add_orphan_item().
+ *
+ * Returns 0 on success.
+ * Returns != 0 on error.
+ */
+static int repair_inode_orphan_item_lowmem(struct btrfs_root *root,
+ struct btrfs_path *path, u64 ino)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_key research_key;
+ int ret;
+ int err = 0;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &research_key, path->slots[0]);
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ err |= ret;
+ goto out;
+ }
+
+ btrfs_release_path(path);
+ ret = btrfs_add_orphan_item(trans, root, path, ino);
+ err |= ret;
+ btrfs_commit_transaction(trans, root);
+out:
+ if (ret)
+ error("failed to add inode %llu as orphan item root %llu",
+ ino, root->root_key.objectid);
+ else
+ printf("Added inode %llu as orphan item root %llu\n",
+ ino, root->root_key.objectid);
+
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(NULL, root, &research_key, path, 0, 0);
+ err |= ret;
+
+ return err;
+}
+
+/* Set inode_item nlink to @ref_count.
+ * If @ref_count == 0, move it to "lost+found" and increase @ref_count.
+ *
+ * Returns 0 on success
+ */
+static int repair_inode_nlinks_lowmem(struct btrfs_root *root,
+ struct btrfs_path *path, u64 ino,
+ const char *name, u32 namelen,
+ u64 ref_count, u8 filetype, u64 *nlink)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_inode_item *ii;
+ struct btrfs_key key;
+ struct btrfs_key old_key;
+ char namebuf[BTRFS_NAME_LEN] = {0};
+ int name_len;
+ int ret;
+ int ret2;
+
+ /* save the key */
+ btrfs_item_key_to_cpu(path->nodes[0], &old_key, path->slots[0]);
+
+ if (name && namelen) {
+ ASSERT(namelen <= BTRFS_NAME_LEN);
+ memcpy(namebuf, name, namelen);
+ name_len = namelen;
+ } else {
+ sprintf(namebuf, "%llu", ino);
+ name_len = count_digits(ino);
+ printf("Can't find file name for inode %llu, use %s instead\n",
+ ino, namebuf);
+ }
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ goto out;
+ }
+
+ btrfs_release_path(path);
+ /* if refs is 0, put it into lostfound */
+ if (ref_count == 0) {
+ ret = link_inode_to_lostfound(trans, root, path, ino, namebuf,
+ name_len, filetype, &ref_count);
+ if (ret)
+ goto fail;
+ }
+
+ /* reset inode_item's nlink to ref_count */
+ key.objectid = ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ if (ret > 0)
+ ret = -ENOENT;
+ if (ret)
+ goto fail;
+
+ ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_inode_item);
+ btrfs_set_inode_nlink(path->nodes[0], ii, ref_count);
+ btrfs_mark_buffer_dirty(path->nodes[0]);
+
+ if (nlink)
+ *nlink = ref_count;
+fail:
+ btrfs_commit_transaction(trans, root);
+out:
+ if (ret)
+ error(
+ "fail to repair nlink of inode %llu root %llu name %s filetype %u",
+ root->objectid, ino, namebuf, filetype);
+ else
+ printf("Fixed nlink of inode %llu root %llu name %s filetype %u\n",
+ root->objectid, ino, namebuf, filetype);
+
+ /* research */
+ btrfs_release_path(path);
+ ret2 = btrfs_search_slot(NULL, root, &old_key, path, 0, 0);
+ if (ret2 < 0)
+ return ret |= ret2;
+ return ret;
+}
+
+/*
+ * Check INODE_ITEM and related ITEMs (the same inode number)
+ * 1. check link count
+ * 2. check inode ref/extref
+ * 3. check dir item/index
+ *
+ * @ext_ref: the EXTENDED_IREF feature
+ *
+ * Return 0 if no error occurred.
+ * Return >0 for error or hit the traversal is done(by error bitmap)
+ */
+static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
+ unsigned int ext_ref)
+{
+ struct extent_buffer *node;
+ struct btrfs_inode_item *ii;
+ struct btrfs_key key;
+ struct btrfs_key last_key;
+ u64 inode_id;
+ u32 mode;
+ u64 nlink;
+ u64 nbytes;
+ u64 isize;
+ u64 size = 0;
+ u64 refs = 0;
+ u64 extent_end = 0;
+ u64 extent_size = 0;
+ unsigned int dir;
+ unsigned int nodatasum;
+ int slot;
+ int ret;
+ int err = 0;
+ char namebuf[BTRFS_NAME_LEN] = {0};
+ u32 name_len = 0;
+
+ node = path->nodes[0];
+ slot = path->slots[0];
+
+ btrfs_item_key_to_cpu(node, &key, slot);
+ inode_id = key.objectid;
+
+ if (inode_id == BTRFS_ORPHAN_OBJECTID) {
+ ret = btrfs_next_item(root, path);
+ if (ret > 0)
+ err |= LAST_ITEM;
+ return err;
+ }
+
+ ii = btrfs_item_ptr(node, slot, struct btrfs_inode_item);
+ isize = btrfs_inode_size(node, ii);
+ nbytes = btrfs_inode_nbytes(node, ii);
+ mode = btrfs_inode_mode(node, ii);
+ dir = imode_to_type(mode) == BTRFS_FT_DIR;
+ nlink = btrfs_inode_nlink(node, ii);
+ nodatasum = btrfs_inode_flags(node, ii) & BTRFS_INODE_NODATASUM;
+
+ while (1) {
+ btrfs_item_key_to_cpu(path->nodes[0], &last_key, path->slots[0]);
+ ret = btrfs_next_item(root, path);
+ if (ret < 0) {
+ /* out will fill 'err' rusing current statistics */
+ goto out;
+ } else if (ret > 0) {
+ err |= LAST_ITEM;
+ goto out;
+ }
+
+ node = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(node, &key, slot);
+ if (key.objectid != inode_id)
+ goto out;
+
+ switch (key.type) {
+ case BTRFS_INODE_REF_KEY:
+ ret = check_inode_ref(root, &key, path, namebuf,
+ &name_len, &refs, mode);
+ err |= ret;
+ break;
+ case BTRFS_INODE_EXTREF_KEY:
+ if (key.type == BTRFS_INODE_EXTREF_KEY && !ext_ref)
+ warning("root %llu EXTREF[%llu %llu] isn't supported",
+ root->objectid, key.objectid,
+ key.offset);
+ ret = check_inode_extref(root, &key, node, slot, &refs,
+ mode);
+ err |= ret;
+ break;
+ case BTRFS_DIR_ITEM_KEY:
+ case BTRFS_DIR_INDEX_KEY:
+ if (!dir) {
+ warning("root %llu INODE[%llu] mode %u shouldn't have DIR_INDEX[%llu %llu]",
+ root->objectid, inode_id,
+ imode_to_type(mode), key.objectid,
+ key.offset);
+ }
+ ret = check_dir_item(root, &key, path, &size, ext_ref);
+ err |= ret;
+ break;
+ case BTRFS_EXTENT_DATA_KEY:
+ if (dir) {
+ warning("root %llu DIR INODE[%llu] shouldn't EXTENT_DATA[%llu %llu]",
+ root->objectid, inode_id, key.objectid,
+ key.offset);
+ }
+ ret = check_file_extent(root, &key, node, slot,
+ nodatasum, &extent_size,
+ &extent_end);
+ err |= ret;
+ break;
+ case BTRFS_XATTR_ITEM_KEY:
+ break;
+ default:
+ error("ITEM[%llu %u %llu] UNKNOWN TYPE",
+ key.objectid, key.type, key.offset);
+ }
+ }
+
+out:
+ if (err & LAST_ITEM) {
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(NULL, root, &last_key, path, 0, 0);
+ if (ret)
+ return err;
+ }
+
+ /* verify INODE_ITEM nlink/isize/nbytes */
+ if (dir) {
+ if (repair && (err & DIR_COUNT_AGAIN)) {
+ err &= ~DIR_COUNT_AGAIN;
+ count_dir_isize(root, inode_id, &size);
+ }
+
+ if ((nlink != 1 || refs != 1) && repair) {
+ ret = repair_inode_nlinks_lowmem(root, path, inode_id,
+ namebuf, name_len, refs, imode_to_type(mode),
+ &nlink);
+ }
+
+ if (nlink != 1) {
+ err |= LINK_COUNT_ERROR;
+ error("root %llu DIR INODE[%llu] shouldn't have more than one link(%llu)",
+ root->objectid, inode_id, nlink);
+ }
+
+ /*
+ * Just a warning, as dir inode nbytes is just an
+ * instructive value.
+ */
+ if (!IS_ALIGNED(nbytes, root->fs_info->nodesize)) {
+ warning("root %llu DIR INODE[%llu] nbytes should be aligned to %u",
+ root->objectid, inode_id,
+ root->fs_info->nodesize);
+ }
+
+ if (isize != size) {
+ if (repair)
+ ret = repair_dir_isize_lowmem(root, path,
+ inode_id, size);
+ if (!repair || ret) {
+ err |= ISIZE_ERROR;
+ error(
+ "root %llu DIR INODE [%llu] size %llu not equal to %llu",
+ root->objectid, inode_id, isize, size);
+ }
+ }
+ } else {
+ if (nlink != refs) {
+ if (repair)
+ ret = repair_inode_nlinks_lowmem(root, path,
+ inode_id, namebuf, name_len, refs,
+ imode_to_type(mode), &nlink);
+ if (!repair || ret) {
+ err |= LINK_COUNT_ERROR;
+ error(
+ "root %llu INODE[%llu] nlink(%llu) not equal to inode_refs(%llu)",
+ root->objectid, inode_id, nlink, refs);
+ }
+ } else if (!nlink) {
+ if (repair)
+ ret = repair_inode_orphan_item_lowmem(root,
+ path, inode_id);
+ if (!repair || ret) {
+ err |= ORPHAN_ITEM;
+ error("root %llu INODE[%llu] is orphan item",
+ root->objectid, inode_id);
+ }
+ }
+
+ if (!nbytes && !no_holes && extent_end < isize) {
+ if (repair)
+ ret = punch_extent_hole(root, inode_id,
+ extent_end, isize - extent_end);
+ if (!repair || ret) {
+ err |= NBYTES_ERROR;
+ error(
+ "root %llu INODE[%llu] size %llu should have a file extent hole",
+ root->objectid, inode_id, isize);
+ }
+ }
+
+ if (nbytes != extent_size) {
+ if (repair)
+ ret = repair_inode_nbytes_lowmem(root, path,
+ inode_id, extent_size);
+ if (!repair || ret) {
+ err |= NBYTES_ERROR;
+ error(
+ "root %llu INODE[%llu] nbytes %llu not equal to extent_size %llu",
+ root->objectid, inode_id, nbytes,
+ extent_size);
+ }
+ }
+ }
+
+ if (err & LAST_ITEM)
+ btrfs_next_item(root, path);
+ return err;
+}
+
+/*
+ * Returns >0 Found error, not fatal, should continue
+ * Returns <0 Fatal error, must exit the whole check
+ * Returns 0 No errors found
+ */
+static int process_one_leaf(struct btrfs_root *root, struct btrfs_path *path,
+ struct node_refs *nrefs, int *level, int ext_ref)
+{
+ struct extent_buffer *cur = path->nodes[0];
+ struct btrfs_key key;
+ u64 cur_bytenr;
+ u32 nritems;
+ u64 first_ino = 0;
+ int root_level = btrfs_header_level(root->node);
+ int i;
+ int ret = 0; /* Final return value */
+ int err = 0; /* Positive error bitmap */
+
+ cur_bytenr = cur->start;
+
+ /* skip to first inode item or the first inode number change */
+ nritems = btrfs_header_nritems(cur);
+ for (i = 0; i < nritems; i++) {
+ btrfs_item_key_to_cpu(cur, &key, i);
+ if (i == 0)
+ first_ino = key.objectid;
+ if (key.type == BTRFS_INODE_ITEM_KEY ||
+ (first_ino && first_ino != key.objectid))
+ break;
+ }
+ if (i == nritems) {
+ path->slots[0] = nritems;
+ return 0;
+ }
+ path->slots[0] = i;
+
+again:
+ err |= check_inode_item(root, path, ext_ref);
+
+ /* modify cur since check_inode_item may change path */
+ cur = path->nodes[0];
+
+ if (err & LAST_ITEM)
+ goto out;
+
+ /* still have inode items in thie leaf */
+ if (cur->start == cur_bytenr)
+ goto again;
+
+ /*
+ * we have switched to another leaf, above nodes may
+ * have changed, here walk down the path, if a node
+ * or leaf is shared, check whether we can skip this
+ * node or leaf.
+ */
+ for (i = root_level; i >= 0; i--) {
+ if (path->nodes[i]->start == nrefs->bytenr[i])
+ continue;
+
+ ret = update_nodes_refs(root, path->nodes[i]->start,
+ path->nodes[i], nrefs, i, 0);
+ if (ret)
+ goto out;
+
+ if (!nrefs->need_check[i]) {
+ *level += 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < *level; i++) {
+ free_extent_buffer(path->nodes[i]);
+ path->nodes[i] = NULL;
+ }
+out:
+ err &= ~LAST_ITEM;
+ if (err && !ret)
+ ret = err;
+ return ret;
+}
+
+/*
+ * @level if @level == -1 means extent data item
+ * else normal treeblocl.
+ */
+static int should_check_extent_strictly(struct btrfs_root *root,
+ struct node_refs *nrefs, int level)
+{
+ int root_level = btrfs_header_level(root->node);
+
+ if (level > root_level || level < -1)
+ return 1;
+ if (level == root_level)
+ return 1;
+ /*
+ * if the upper node is marked full backref, it should contain shared
+ * backref of the parent (except owner == root->objectid).
+ */
+ while (++level <= root_level)
+ if (nrefs->refs[level] > 1)
+ return 0;
+
+ return 1;
+}
+
+static int check_extent_inline_ref(struct extent_buffer *eb,
+ struct btrfs_key *key, struct btrfs_extent_inline_ref *iref)
+{
+ int ret;
+ u8 type = btrfs_extent_inline_ref_type(eb, iref);
+
+ switch (type) {
+ case BTRFS_TREE_BLOCK_REF_KEY:
+ case BTRFS_EXTENT_DATA_REF_KEY:
+ case BTRFS_SHARED_BLOCK_REF_KEY:
+ case BTRFS_SHARED_DATA_REF_KEY:
+ ret = 0;
+ break;
+ default:
+ error("extent[%llu %u %llu] has unknown ref type: %d",
+ key->objectid, key->type, key->offset, type);
+ ret = UNKNOWN_TYPE;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Check backrefs of a tree block given by @bytenr or @eb.
+ *
+ * @root: the root containing the @bytenr or @eb
+ * @eb: tree block extent buffer, can be NULL
+ * @bytenr: bytenr of the tree block to search
+ * @level: tree level of the tree block
+ * @owner: owner of the tree block
+ *
+ * Return >0 for any error found and output error message
+ * Return 0 for no error found
+ */
+static int check_tree_block_ref(struct btrfs_root *root,
+ struct extent_buffer *eb, u64 bytenr,
+ int level, u64 owner, struct node_refs *nrefs)
+{
+ struct btrfs_key key;
+ struct btrfs_root *extent_root = root->fs_info->extent_root;
+ struct btrfs_path path;
+ struct btrfs_extent_item *ei;
+ struct btrfs_extent_inline_ref *iref;
+ struct extent_buffer *leaf;
+ unsigned long end;
+ unsigned long ptr;
+ int slot;
+ int skinny_level;
+ int root_level = btrfs_header_level(root->node);
+ int type;
+ u32 nodesize = root->fs_info->nodesize;
+ u32 item_size;
+ u64 offset;
+ int found_ref = 0;
+ int err = 0;
+ int ret;
+ int strict = 1;
+ int parent = 0;
+
+ btrfs_init_path(&path);
+ key.objectid = bytenr;
+ if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA))
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ else
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = (u64)-1;
+
+ /* Search for the backref in extent tree */
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret < 0) {
+ err |= BACKREF_MISSING;
+ goto out;
+ }
+ ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
+ if (ret) {
+ err |= BACKREF_MISSING;
+ goto out;
+ }
+
+ leaf = path.nodes[0];
+ slot = path.slots[0];
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+
+ ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
+
+ if (key.type == BTRFS_METADATA_ITEM_KEY) {
+ skinny_level = (int)key.offset;
+ iref = (struct btrfs_extent_inline_ref *)(ei + 1);
+ } else {
+ struct btrfs_tree_block_info *info;
+
+ info = (struct btrfs_tree_block_info *)(ei + 1);
+ skinny_level = btrfs_tree_block_level(leaf, info);
+ iref = (struct btrfs_extent_inline_ref *)(info + 1);
+ }
+
+
+ if (eb) {
+ u64 header_gen;
+ u64 extent_gen;
+
+ /*
+ * Due to the feature of shared tree blocks, if the upper node
+ * is a fs root or shared node, the extent of checked node may
+ * not be updated until the next CoW.
+ */
+ if (nrefs)
+ strict = should_check_extent_strictly(root, nrefs,
+ level);
+ if (!(btrfs_extent_flags(leaf, ei) &
+ BTRFS_EXTENT_FLAG_TREE_BLOCK)) {
+ error(
+ "extent[%llu %u] backref type mismatch, missing bit: %llx",
+ key.objectid, nodesize,
+ BTRFS_EXTENT_FLAG_TREE_BLOCK);
+ err = BACKREF_MISMATCH;
+ }
+ header_gen = btrfs_header_generation(eb);
+ extent_gen = btrfs_extent_generation(leaf, ei);
+ if (header_gen != extent_gen) {
+ error(
+ "extent[%llu %u] backref generation mismatch, wanted: %llu, have: %llu",
+ key.objectid, nodesize, header_gen,
+ extent_gen);
+ err = BACKREF_MISMATCH;
+ }
+ if (level != skinny_level) {
+ error(
+ "extent[%llu %u] level mismatch, wanted: %u, have: %u",
+ key.objectid, nodesize, level, skinny_level);
+ err = BACKREF_MISMATCH;
+ }
+ if (!is_fstree(owner) && btrfs_extent_refs(leaf, ei) != 1) {
+ error(
+ "extent[%llu %u] is referred by other roots than %llu",
+ key.objectid, nodesize, root->objectid);
+ err = BACKREF_MISMATCH;
+ }
+ }
+
+ /*
+ * Iterate the extent/metadata item to find the exact backref
+ */
+ item_size = btrfs_item_size_nr(leaf, slot);
+ 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(leaf, iref);
+ offset = btrfs_extent_inline_ref_offset(leaf, iref);
+
+ ret = check_extent_inline_ref(leaf, &key, iref);
+ if (ret) {
+ err |= ret;
+ break;
+ }
+ if (type == BTRFS_TREE_BLOCK_REF_KEY) {
+ if (offset == root->objectid)
+ found_ref = 1;
+ if (!strict && owner == offset)
+ found_ref = 1;
+ } else if (type == BTRFS_SHARED_BLOCK_REF_KEY) {
+ /*
+ * Backref of tree reloc root points to itself, no need
+ * to check backref any more.
+ *
+ * This may be an error of loop backref, but extent tree
+ * checker should have already handled it.
+ * Here we only need to avoid infinite iteration.
+ */
+ if (offset == bytenr) {
+ found_ref = 1;
+ } else {
+ /*
+ * Check if the backref points to valid
+ * referencer
+ */
+ found_ref = !check_tree_block_ref(root, NULL,
+ offset, level + 1, owner, NULL);
+ }
+ }
+
+ if (found_ref)
+ break;
+ ptr += btrfs_extent_inline_ref_size(type);
+ }
+
+ /*
+ * Inlined extent item doesn't have what we need, check
+ * TREE_BLOCK_REF_KEY
+ */
+ if (!found_ref) {
+ btrfs_release_path(&path);
+ key.objectid = bytenr;
+ key.type = BTRFS_TREE_BLOCK_REF_KEY;
+ key.offset = root->objectid;
+
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (!ret)
+ found_ref = 1;
+ }
+ /*
+ * Finally check SHARED BLOCK REF, any found will be good
+ * Here we're not doing comprehensive extent backref checking,
+ * only need to ensure there is some extent referring to this
+ * tree block.
+ */
+ if (!found_ref) {
+ btrfs_release_path(&path);
+ key.objectid = bytenr;
+ key.type = BTRFS_SHARED_BLOCK_REF_KEY;
+ key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret < 0) {
+ err |= BACKREF_MISSING;
+ goto out;
+ }
+ ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
+ if (ret) {
+ err |= BACKREF_MISSING;
+ goto out;
+ }
+ found_ref = 1;
+ }
+ if (!found_ref)
+ err |= BACKREF_MISSING;
+out:
+ btrfs_release_path(&path);
+ if (nrefs && strict &&
+ level < root_level && nrefs->full_backref[level + 1])
+ parent = nrefs->bytenr[level + 1];
+ if (eb && (err & BACKREF_MISSING))
+ error(
+ "extent[%llu %u] backref lost (owner: %llu, level: %u) %s %llu",
+ bytenr, nodesize, owner, level,
+ parent ? "parent" : "root",
+ parent ? parent : root->objectid);
+ return err;
+}
+
+/*
+ * If @err contains BACKREF_MISSING then add extent of the
+ * file_extent_data_item.
+ *
+ * Returns error bits after reapir.
+ */
+static int repair_extent_data_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *pathp,
+ struct node_refs *nrefs,
+ int err)
+{
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_key fi_key;
+ struct btrfs_key key;
+ struct btrfs_extent_item *ei;
+ struct btrfs_path path;
+ struct btrfs_root *extent_root = root->fs_info->extent_root;
+ struct extent_buffer *eb;
+ u64 size;
+ u64 disk_bytenr;
+ u64 num_bytes;
+ u64 parent;
+ u64 offset;
+ u64 extent_offset;
+ u64 file_offset;
+ int generation;
+ int slot;
+ int ret = 0;
+
+ eb = pathp->nodes[0];
+ slot = pathp->slots[0];
+ btrfs_item_key_to_cpu(eb, &fi_key, slot);
+ fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
+
+ if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE ||
+ btrfs_file_extent_disk_bytenr(eb, fi) == 0)
+ return err;
+
+ file_offset = fi_key.offset;
+ generation = btrfs_file_extent_generation(eb, fi);
+ disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi);
+ num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi);
+ extent_offset = btrfs_file_extent_offset(eb, fi);
+ offset = file_offset - extent_offset;
+
+ /* now repair only adds backref */
+ if ((err & BACKREF_MISSING) == 0)
+ return err;
+
+ /* search extent item */
+ key.objectid = disk_bytenr;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = num_bytes;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret < 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* insert an extent item */
+ if (ret > 0) {
+ key.objectid = disk_bytenr;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = num_bytes;
+ size = sizeof(*ei);
+
+ btrfs_release_path(&path);
+ ret = btrfs_insert_empty_item(trans, extent_root, &path, &key,
+ size);
+ if (ret)
+ goto out;
+ eb = path.nodes[0];
+ ei = btrfs_item_ptr(eb, path.slots[0], struct btrfs_extent_item);
+
+ btrfs_set_extent_refs(eb, ei, 0);
+ btrfs_set_extent_generation(eb, ei, generation);
+ btrfs_set_extent_flags(eb, ei, BTRFS_EXTENT_FLAG_DATA);
+
+ btrfs_mark_buffer_dirty(eb);
+ ret = btrfs_update_block_group(extent_root, disk_bytenr,
+ num_bytes, 1, 0);
+ btrfs_release_path(&path);
+ }
+
+ if (nrefs->full_backref[0])
+ parent = btrfs_header_bytenr(eb);
+ else
+ parent = 0;
+
+ ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, parent,
+ root->objectid,
+ parent ? BTRFS_FIRST_FREE_OBJECTID : fi_key.objectid,
+ offset);
+ if (ret) {
+ error(
+ "failed to increase extent data backref[%llu %llu] root %llu",
+ disk_bytenr, num_bytes, root->objectid);
+ goto out;
+ } else {
+ printf("Add one extent data backref [%llu %llu]\n",
+ disk_bytenr, num_bytes);
+ }
+
+ err &= ~BACKREF_MISSING;
+out:
+ if (ret)
+ error("can't repair root %llu extent data item[%llu %llu]",
+ root->objectid, disk_bytenr, num_bytes);
+ return err;
+}
+
+/*
+ * Check EXTENT_DATA item, mainly for its dbackref in extent tree
+ *
+ * Return >0 any error found and output error message
+ * Return 0 for no error found
+ */
+static int check_extent_data_item(struct btrfs_root *root,
+ struct btrfs_path *pathp,
+ struct node_refs *nrefs, int account_bytes)
+{
+ struct btrfs_file_extent_item *fi;
+ struct extent_buffer *eb = pathp->nodes[0];
+ struct btrfs_path path;
+ struct btrfs_root *extent_root = root->fs_info->extent_root;
+ struct btrfs_key fi_key;
+ struct btrfs_key dbref_key;
+ struct extent_buffer *leaf;
+ struct btrfs_extent_item *ei;
+ struct btrfs_extent_inline_ref *iref;
+ struct btrfs_extent_data_ref *dref;
+ u64 owner;
+ u64 disk_bytenr;
+ u64 disk_num_bytes;
+ u64 extent_num_bytes;
+ u64 extent_flags;
+ u64 offset;
+ u32 item_size;
+ unsigned long end;
+ unsigned long ptr;
+ int type;
+ int found_dbackref = 0;
+ int slot = pathp->slots[0];
+ int err = 0;
+ int ret;
+ int strict;
+
+ btrfs_item_key_to_cpu(eb, &fi_key, slot);
+ fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
+
+ /* Nothing to check for hole and inline data extents */
+ if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE ||
+ btrfs_file_extent_disk_bytenr(eb, fi) == 0)
+ return 0;
+
+ disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi);
+ disk_num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi);
+ extent_num_bytes = btrfs_file_extent_num_bytes(eb, fi);
+ offset = btrfs_file_extent_offset(eb, fi);
+
+ /* Check unaligned disk_num_bytes and num_bytes */
+ if (!IS_ALIGNED(disk_num_bytes, root->fs_info->sectorsize)) {
+ error(
+"file extent [%llu, %llu] has unaligned disk num bytes: %llu, should be aligned to %u",
+ fi_key.objectid, fi_key.offset, disk_num_bytes,
+ root->fs_info->sectorsize);
+ err |= BYTES_UNALIGNED;
+ } else if (account_bytes) {
+ data_bytes_allocated += disk_num_bytes;
+ }
+ if (!IS_ALIGNED(extent_num_bytes, root->fs_info->sectorsize)) {
+ error(
+"file extent [%llu, %llu] has unaligned num bytes: %llu, should be aligned to %u",
+ fi_key.objectid, fi_key.offset, extent_num_bytes,
+ root->fs_info->sectorsize);
+ err |= BYTES_UNALIGNED;
+ } else if (account_bytes) {
+ data_bytes_referenced += extent_num_bytes;
+ }
+ owner = btrfs_header_owner(eb);
+
+ /* Check the extent item of the file extent in extent tree */
+ btrfs_init_path(&path);
+ dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
+ dbref_key.type = BTRFS_EXTENT_ITEM_KEY;
+ dbref_key.offset = btrfs_file_extent_disk_num_bytes(eb, fi);
+
+ ret = btrfs_search_slot(NULL, extent_root, &dbref_key, &path, 0, 0);
+ if (ret)
+ goto out;
+
+ leaf = path.nodes[0];
+ slot = path.slots[0];
+ ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
+
+ extent_flags = btrfs_extent_flags(leaf, ei);
+
+ if (!(extent_flags & BTRFS_EXTENT_FLAG_DATA)) {
+ error(
+ "extent[%llu %llu] backref type mismatch, wanted bit: %llx",
+ disk_bytenr, disk_num_bytes,
+ BTRFS_EXTENT_FLAG_DATA);
+ err |= BACKREF_MISMATCH;
+ }
+
+ /* Check data backref inside that extent item */
+ item_size = btrfs_item_size_nr(leaf, path.slots[0]);
+ iref = (struct btrfs_extent_inline_ref *)(ei + 1);
+ ptr = (unsigned long)iref;
+ end = (unsigned long)ei + item_size;
+ strict = should_check_extent_strictly(root, nrefs, -1);
+
+ while (ptr < end) {
+ u64 ref_root;
+ u64 ref_objectid;
+ u64 ref_offset;
+ bool match = false;
+
+ iref = (struct btrfs_extent_inline_ref *)ptr;
+ type = btrfs_extent_inline_ref_type(leaf, iref);
+ dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+
+ ret = check_extent_inline_ref(leaf, &dbref_key, iref);
+ if (ret) {
+ err |= ret;
+ break;
+ }
+ if (type == BTRFS_EXTENT_DATA_REF_KEY) {
+ ref_root = btrfs_extent_data_ref_root(leaf, dref);
+ ref_objectid = btrfs_extent_data_ref_objectid(leaf,
+ dref);
+ ref_offset = btrfs_extent_data_ref_offset(leaf, dref);
+
+ if (ref_objectid == fi_key.objectid &&
+ ref_offset == fi_key.offset - offset)
+ match = true;
+ if (ref_root == root->objectid && match)
+ found_dbackref = 1;
+ else if (!strict && owner == ref_root && match)
+ found_dbackref = 1;
+ } else if (type == BTRFS_SHARED_DATA_REF_KEY) {
+ found_dbackref = !check_tree_block_ref(root, NULL,
+ btrfs_extent_inline_ref_offset(leaf, iref),
+ 0, owner, NULL);
+ }
+
+ if (found_dbackref)
+ break;
+ ptr += btrfs_extent_inline_ref_size(type);
+ }
+
+ if (!found_dbackref) {
+ btrfs_release_path(&path);
+
+ /* Didn't find inlined data backref, try EXTENT_DATA_REF_KEY */
+ dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
+ dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY;
+ dbref_key.offset = hash_extent_data_ref(root->objectid,
+ fi_key.objectid, fi_key.offset - offset);
+
+ ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
+ &dbref_key, &path, 0, 0);
+ if (!ret) {
+ found_dbackref = 1;
+ goto out;
+ }
+
+ btrfs_release_path(&path);
+
+ /*
+ * Neither inlined nor EXTENT_DATA_REF found, try
+ * SHARED_DATA_REF as last chance.
+ */
+ dbref_key.objectid = disk_bytenr;
+ dbref_key.type = BTRFS_SHARED_DATA_REF_KEY;
+ dbref_key.offset = eb->start;
+
+ ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
+ &dbref_key, &path, 0, 0);
+ if (!ret) {
+ found_dbackref = 1;
+ goto out;
+ }
+ }
+
+out:
+ if (!found_dbackref)
+ err |= BACKREF_MISSING;
+ btrfs_release_path(&path);
+ if (err & BACKREF_MISSING) {
+ error("data extent[%llu %llu] backref lost",
+ disk_bytenr, disk_num_bytes);
+ }
+ return err;
+}
+
+/*
+ * Check a block group item with its referener (chunk) and its used space
+ * with extent/metadata item
+ */
+static int check_block_group_item(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *eb, int slot)
+{
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ struct btrfs_root *chunk_root = fs_info->chunk_root;
+ struct btrfs_block_group_item *bi;
+ struct btrfs_block_group_item bg_item;
+ struct btrfs_path path;
+ struct btrfs_key bg_key;
+ struct btrfs_key chunk_key;
+ struct btrfs_key extent_key;
+ struct btrfs_chunk *chunk;
+ struct extent_buffer *leaf;
+ struct btrfs_extent_item *ei;
+ u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
+ u64 flags;
+ u64 bg_flags;
+ u64 used;
+ u64 total = 0;
+ int ret;
+ int err = 0;
+
+ btrfs_item_key_to_cpu(eb, &bg_key, slot);
+ bi = btrfs_item_ptr(eb, slot, struct btrfs_block_group_item);
+ read_extent_buffer(eb, &bg_item, (unsigned long)bi, sizeof(bg_item));
+ used = btrfs_block_group_used(&bg_item);
+ bg_flags = btrfs_block_group_flags(&bg_item);
+
+ chunk_key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+ chunk_key.type = BTRFS_CHUNK_ITEM_KEY;
+ chunk_key.offset = bg_key.objectid;
+
+ btrfs_init_path(&path);
+ /* Search for the referencer chunk */
+ ret = btrfs_search_slot(NULL, chunk_root, &chunk_key, &path, 0, 0);
+ if (ret) {
+ error(
+ "block group[%llu %llu] did not find the related chunk item",
+ bg_key.objectid, bg_key.offset);
+ err |= REFERENCER_MISSING;
+ } else {
+ chunk = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_chunk);
+ if (btrfs_chunk_length(path.nodes[0], chunk) !=
+ bg_key.offset) {
+ error(
+ "block group[%llu %llu] related chunk item length does not match",
+ bg_key.objectid, bg_key.offset);
+ err |= REFERENCER_MISMATCH;
+ }
+ }
+ btrfs_release_path(&path);
+
+ /* Search from the block group bytenr */
+ extent_key.objectid = bg_key.objectid;
+ extent_key.type = 0;
+ extent_key.offset = 0;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, extent_root, &extent_key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ /* Iterate extent tree to account used space */
+ while (1) {
+ leaf = path.nodes[0];
+
+ /* Search slot can point to the last item beyond leaf nritems */
+ if (path.slots[0] >= btrfs_header_nritems(leaf))
+ goto next;
+
+ btrfs_item_key_to_cpu(leaf, &extent_key, path.slots[0]);
+ if (extent_key.objectid >= bg_key.objectid + bg_key.offset)
+ break;
+
+ if (extent_key.type != BTRFS_METADATA_ITEM_KEY &&
+ extent_key.type != BTRFS_EXTENT_ITEM_KEY)
+ goto next;
+ if (extent_key.objectid < bg_key.objectid)
+ goto next;
+
+ if (extent_key.type == BTRFS_METADATA_ITEM_KEY)
+ total += nodesize;
+ else
+ total += extent_key.offset;
+
+ ei = btrfs_item_ptr(leaf, path.slots[0],
+ struct btrfs_extent_item);
+ flags = btrfs_extent_flags(leaf, ei);
+ if (flags & BTRFS_EXTENT_FLAG_DATA) {
+ if (!(bg_flags & BTRFS_BLOCK_GROUP_DATA)) {
+ error(
+ "bad extent[%llu, %llu) type mismatch with chunk",
+ extent_key.objectid,
+ extent_key.objectid + extent_key.offset);
+ err |= CHUNK_TYPE_MISMATCH;
+ }
+ } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+ if (!(bg_flags & (BTRFS_BLOCK_GROUP_SYSTEM |
+ BTRFS_BLOCK_GROUP_METADATA))) {
+ error(
+ "bad extent[%llu, %llu) type mismatch with chunk",
+ extent_key.objectid,
+ extent_key.objectid + nodesize);
+ err |= CHUNK_TYPE_MISMATCH;
+ }
+ }
+next:
+ ret = btrfs_next_item(extent_root, &path);
+ if (ret)
+ break;
+ }
+
+out:
+ btrfs_release_path(&path);
+
+ if (total != used) {
+ error(
+ "block group[%llu %llu] used %llu but extent items used %llu",
+ bg_key.objectid, bg_key.offset, used, total);
+ err |= BG_ACCOUNTING_ERROR;
+ }
+ return err;
+}
+
+/*
+ * Get real tree block level for the case like shared block
+ * Return >= 0 as tree level
+ * Return <0 for error
+ */
+static int query_tree_block_level(struct btrfs_fs_info *fs_info, u64 bytenr)
+{
+ struct extent_buffer *eb;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_extent_item *ei;
+ u64 flags;
+ u64 transid;
+ u8 backref_level;
+ u8 header_level;
+ int ret;
+
+ /* Search extent tree for extent generation and level */
+ key.objectid = bytenr;
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ key.offset = (u64)-1;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto release_out;
+ ret = btrfs_previous_extent_item(fs_info->extent_root, &path, bytenr);
+ if (ret < 0)
+ goto release_out;
+ if (ret > 0) {
+ ret = -ENOENT;
+ goto release_out;
+ }
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ ei = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_extent_item);
+ flags = btrfs_extent_flags(path.nodes[0], ei);
+ if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) {
+ ret = -ENOENT;
+ goto release_out;
+ }
+
+ /* Get transid for later read_tree_block() check */
+ transid = btrfs_extent_generation(path.nodes[0], ei);
+
+ /* Get backref level as one source */
+ if (key.type == BTRFS_METADATA_ITEM_KEY) {
+ backref_level = key.offset;
+ } else {
+ struct btrfs_tree_block_info *info;
+
+ info = (struct btrfs_tree_block_info *)(ei + 1);
+ backref_level = btrfs_tree_block_level(path.nodes[0], info);
+ }
+ btrfs_release_path(&path);
+
+ /* Get level from tree block as an alternative source */
+ eb = read_tree_block(fs_info, bytenr, transid);
+ if (!extent_buffer_uptodate(eb)) {
+ free_extent_buffer(eb);
+ return -EIO;
+ }
+ header_level = btrfs_header_level(eb);
+ free_extent_buffer(eb);
+
+ if (header_level != backref_level)
+ return -EIO;
+ return header_level;
+
+release_out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * Check if a tree block backref is valid (points to a valid tree block)
+ * if level == -1, level will be resolved
+ * Return >0 for any error found and print error message
+ */
+static int check_tree_block_backref(struct btrfs_fs_info *fs_info, u64 root_id,
+ u64 bytenr, int level)
+{
+ struct btrfs_root *root;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct extent_buffer *eb;
+ struct extent_buffer *node;
+ u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
+ int err = 0;
+ int ret;
+
+ /* Query level for level == -1 special case */
+ if (level == -1)
+ level = query_tree_block_level(fs_info, bytenr);
+ if (level < 0) {
+ err |= REFERENCER_MISSING;
+ goto out;
+ }
+
+ key.objectid = root_id;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+
+ root = btrfs_read_fs_root(fs_info, &key);
+ if (IS_ERR(root)) {
+ err |= REFERENCER_MISSING;
+ goto out;
+ }
+
+ /* Read out the tree block to get item/node key */
+ eb = read_tree_block(fs_info, bytenr, 0);
+ if (!extent_buffer_uptodate(eb)) {
+ err |= REFERENCER_MISSING;
+ free_extent_buffer(eb);
+ goto out;
+ }
+
+ /* Empty tree, no need to check key */
+ if (!btrfs_header_nritems(eb) && !level) {
+ free_extent_buffer(eb);
+ goto out;
+ }
+
+ if (level)
+ btrfs_node_key_to_cpu(eb, &key, 0);
+ else
+ btrfs_item_key_to_cpu(eb, &key, 0);
+
+ free_extent_buffer(eb);
+
+ btrfs_init_path(&path);
+ path.lowest_level = level;
+ /* Search with the first key, to ensure we can reach it */
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0) {
+ err |= REFERENCER_MISSING;
+ goto release_out;
+ }
+
+ node = path.nodes[level];
+ if (btrfs_header_bytenr(node) != bytenr) {
+ error(
+ "extent [%llu %d] referencer bytenr mismatch, wanted: %llu, have: %llu",
+ bytenr, nodesize, bytenr,
+ btrfs_header_bytenr(node));
+ err |= REFERENCER_MISMATCH;
+ }
+ if (btrfs_header_level(node) != level) {
+ error(
+ "extent [%llu %d] referencer level mismatch, wanted: %d, have: %d",
+ bytenr, nodesize, level,
+ btrfs_header_level(node));
+ err |= REFERENCER_MISMATCH;
+ }
+
+release_out:
+ btrfs_release_path(&path);
+out:
+ if (err & REFERENCER_MISSING) {
+ if (level < 0)
+ error("extent [%llu %d] lost referencer (owner: %llu)",
+ bytenr, nodesize, root_id);
+ else
+ error(
+ "extent [%llu %d] lost referencer (owner: %llu, level: %u)",
+ bytenr, nodesize, root_id, level);
+ }
+
+ return err;
+}
+
+/*
+ * Check if tree block @eb is tree reloc root.
+ * Return 0 if it's not or any problem happens
+ * Return 1 if it's a tree reloc root
+ */
+static int is_tree_reloc_root(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *eb)
+{
+ struct btrfs_root *tree_reloc_root;
+ struct btrfs_key key;
+ u64 bytenr = btrfs_header_bytenr(eb);
+ u64 owner = btrfs_header_owner(eb);
+ int ret = 0;
+
+ key.objectid = BTRFS_TREE_RELOC_OBJECTID;
+ key.offset = owner;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+
+ tree_reloc_root = btrfs_read_fs_root_no_cache(fs_info, &key);
+ if (IS_ERR(tree_reloc_root))
+ return 0;
+
+ if (bytenr == btrfs_header_bytenr(tree_reloc_root->node))
+ ret = 1;
+ btrfs_free_fs_root(tree_reloc_root);
+ return ret;
+}
+
+/*
+ * Check referencer for shared block backref
+ * If level == -1, this function will resolve the level.
+ */
+static int check_shared_block_backref(struct btrfs_fs_info *fs_info,
+ u64 parent, u64 bytenr, int level)
+{
+ struct extent_buffer *eb;
+ u32 nr;
+ int found_parent = 0;
+ int i;
+
+ eb = read_tree_block(fs_info, parent, 0);
+ if (!extent_buffer_uptodate(eb))
+ goto out;
+
+ if (level == -1)
+ level = query_tree_block_level(fs_info, bytenr);
+ if (level < 0)
+ goto out;
+
+ /* It's possible it's a tree reloc root */
+ if (parent == bytenr) {
+ if (is_tree_reloc_root(fs_info, eb))
+ found_parent = 1;
+ goto out;
+ }
+
+ if (level + 1 != btrfs_header_level(eb))
+ goto out;
+
+ nr = btrfs_header_nritems(eb);
+ for (i = 0; i < nr; i++) {
+ if (bytenr == btrfs_node_blockptr(eb, i)) {
+ found_parent = 1;
+ break;
+ }
+ }
+out:
+ free_extent_buffer(eb);
+ if (!found_parent) {
+ error(
+ "shared extent[%llu %u] lost its parent (parent: %llu, level: %u)",
+ bytenr, fs_info->nodesize, parent, level);
+ return REFERENCER_MISSING;
+ }
+ return 0;
+}
+
+/*
+ * Check referencer for normal (inlined) data ref
+ * If len == 0, it will be resolved by searching in extent tree
+ */
+static int check_extent_data_backref(struct btrfs_fs_info *fs_info,
+ u64 root_id, u64 objectid, u64 offset,
+ u64 bytenr, u64 len, u32 count)
+{
+ struct btrfs_root *root;
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct extent_buffer *leaf;
+ struct btrfs_file_extent_item *fi;
+ u32 found_count = 0;
+ int slot;
+ int ret = 0;
+
+ if (!len) {
+ key.objectid = bytenr;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = (u64)-1;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+ ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
+ if (ret)
+ goto out;
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.objectid != bytenr ||
+ key.type != BTRFS_EXTENT_ITEM_KEY)
+ goto out;
+ len = key.offset;
+ btrfs_release_path(&path);
+ }
+ key.objectid = root_id;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ btrfs_init_path(&path);
+
+ root = btrfs_read_fs_root(fs_info, &key);
+ if (IS_ERR(root))
+ goto out;
+
+ key.objectid = objectid;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ /*
+ * It can be nasty as data backref offset is
+ * file offset - file extent offset, which is smaller or
+ * equal to original backref offset. The only special case is
+ * overflow. So we need to special check and do further search.
+ */
+ key.offset = offset & (1ULL << 63) ? 0 : offset;
+
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Search afterwards to get correct one
+ * NOTE: As we must do a comprehensive check on the data backref to
+ * make sure the dref count also matches, we must iterate all file
+ * extents for that inode.
+ */
+ while (1) {
+ leaf = path.nodes[0];
+ slot = path.slots[0];
+
+ if (slot >= btrfs_header_nritems(leaf) ||
+ btrfs_header_owner(leaf) != root_id)
+ goto next;
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.objectid != objectid ||
+ key.type != BTRFS_EXTENT_DATA_KEY)
+ break;
+ fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+ /*
+ * Except normal disk bytenr and disk num bytes, we still
+ * need to do extra check on dbackref offset as
+ * dbackref offset = file_offset - file_extent_offset
+ *
+ * Also, we must check the leaf owner.
+ * In case of shared tree blocks (snapshots) we can inherit
+ * leaves from source snapshot.
+ * In that case, reference from source snapshot should not
+ * count.
+ */
+ if (btrfs_file_extent_disk_bytenr(leaf, fi) == bytenr &&
+ btrfs_file_extent_disk_num_bytes(leaf, fi) == len &&
+ (u64)(key.offset - btrfs_file_extent_offset(leaf, fi)) ==
+ offset && btrfs_header_owner(leaf) == root_id)
+ found_count++;
+
+next:
+ ret = btrfs_next_item(root, &path);
+ if (ret)
+ break;
+ }
+out:
+ btrfs_release_path(&path);
+ if (found_count != count) {
+ error(
+"extent[%llu, %llu] referencer count mismatch (root: %llu, owner: %llu, offset: %llu) wanted: %u, have: %u",
+ bytenr, len, root_id, objectid, offset, count,
+ found_count);
+ return REFERENCER_MISSING;
+ }
+ return 0;
+}
+
+/*
+ * Check if the referencer of a shared data backref exists
+ */
+static int check_shared_data_backref(struct btrfs_fs_info *fs_info,
+ u64 parent, u64 bytenr)
+{
+ struct extent_buffer *eb;
+ struct btrfs_key key;
+ struct btrfs_file_extent_item *fi;
+ u32 nr;
+ int found_parent = 0;
+ int i;
+
+ eb = read_tree_block(fs_info, parent, 0);
+ if (!extent_buffer_uptodate(eb))
+ goto out;
+
+ nr = btrfs_header_nritems(eb);
+ for (i = 0; i < nr; i++) {
+ btrfs_item_key_to_cpu(eb, &key, i);
+ if (key.type != BTRFS_EXTENT_DATA_KEY)
+ continue;
+
+ fi = btrfs_item_ptr(eb, i, struct btrfs_file_extent_item);
+ if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE)
+ continue;
+
+ if (btrfs_file_extent_disk_bytenr(eb, fi) == bytenr) {
+ found_parent = 1;
+ break;
+ }
+ }
+
+out:
+ free_extent_buffer(eb);
+ if (!found_parent) {
+ error("shared extent %llu referencer lost (parent: %llu)",
+ bytenr, parent);
+ return REFERENCER_MISSING;
+ }
+ return 0;
+}
+
+/*
+ * Only delete backref if REFERENCER_MISSING now
+ *
+ * Returns <0 the extent was deleted
+ * Returns >0 the backref was deleted but extent still exists, returned value
+ * means error after repair
+ * Returns 0 nothing happened
+ */
+static int repair_extent_item(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 err)
+{
+ struct btrfs_key old_key;
+ int freed = 0;
+ int ret;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &old_key, path->slots[0]);
+
+ if (err & (REFERENCER_MISSING | REFERENCER_MISMATCH)) {
+ /* delete the backref */
+ ret = btrfs_free_extent(trans, root->fs_info->fs_root, bytenr,
+ num_bytes, parent, root_objectid, owner, offset);
+ if (!ret) {
+ freed = 1;
+ err &= ~REFERENCER_MISSING;
+ printf("Delete backref in extent [%llu %llu]\n",
+ bytenr, num_bytes);
+ } else {
+ error("fail to delete backref in extent [%llu %llu]",
+ bytenr, num_bytes);
+ }
+ }
+
+ /* btrfs_free_extent may delete the extent */
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(NULL, root, &old_key, path, 0, 0);
+
+ if (ret)
+ ret = -ENOENT;
+ else if (freed)
+ ret = err;
+ return ret;
+}
+
+/*
+ * This function will check a given extent item, including its backref and
+ * itself (like crossing stripe boundary and type)
+ *
+ * Since we don't use extent_record anymore, introduce new error bit
+ */
+static int check_extent_item(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path)
+{
+ struct btrfs_extent_item *ei;
+ struct btrfs_extent_inline_ref *iref;
+ struct btrfs_extent_data_ref *dref;
+ struct extent_buffer *eb = path->nodes[0];
+ unsigned long end;
+ unsigned long ptr;
+ int slot = path->slots[0];
+ int type;
+ u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
+ u32 item_size = btrfs_item_size_nr(eb, slot);
+ u64 flags;
+ u64 offset;
+ u64 parent;
+ u64 num_bytes;
+ u64 root_objectid;
+ u64 owner;
+ u64 owner_offset;
+ int metadata = 0;
+ int level;
+ struct btrfs_key key;
+ int ret;
+ int err = 0;
+
+ btrfs_item_key_to_cpu(eb, &key, slot);
+ if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+ bytes_used += key.offset;
+ num_bytes = key.offset;
+ } else {
+ bytes_used += nodesize;
+ num_bytes = nodesize;
+ }
+
+ if (item_size < sizeof(*ei)) {
+ /*
+ * COMPAT_EXTENT_TREE_V0 case, but it's already a super
+ * old thing when on disk format is still un-determined.
+ * No need to care about it anymore
+ */
+ error("unsupported COMPAT_EXTENT_TREE_V0 detected");
+ return -ENOTTY;
+ }
+
+ ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
+ flags = btrfs_extent_flags(eb, ei);
+
+ if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
+ metadata = 1;
+ if (metadata && check_crossing_stripes(global_info, key.objectid,
+ eb->len)) {
+ error("bad metadata [%llu, %llu) crossing stripe boundary",
+ key.objectid, key.objectid + nodesize);
+ err |= CROSSING_STRIPE_BOUNDARY;
+ }
+
+ ptr = (unsigned long)(ei + 1);
+
+ if (metadata && key.type == BTRFS_EXTENT_ITEM_KEY) {
+ /* Old EXTENT_ITEM metadata */
+ struct btrfs_tree_block_info *info;
+
+ info = (struct btrfs_tree_block_info *)ptr;
+ level = btrfs_tree_block_level(eb, info);
+ ptr += sizeof(struct btrfs_tree_block_info);
+ } else {
+ /* New METADATA_ITEM */
+ level = key.offset;
+ }
+ end = (unsigned long)ei + item_size;
+
+next:
+ /* Reached extent item end normally */
+ if (ptr == end)
+ goto out;
+
+ /* Beyond extent item end, wrong item size */
+ if (ptr > end) {
+ err |= ITEM_SIZE_MISMATCH;
+ error("extent item at bytenr %llu slot %d has wrong size",
+ eb->start, slot);
+ goto out;
+ }
+
+ parent = 0;
+ root_objectid = 0;
+ owner = 0;
+ owner_offset = 0;
+ /* Now check every backref in this extent item */
+ 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:
+ root_objectid = offset;
+ owner = level;
+ ret = check_tree_block_backref(fs_info, offset, key.objectid,
+ level);
+ err |= ret;
+ break;
+ case BTRFS_SHARED_BLOCK_REF_KEY:
+ parent = offset;
+ ret = check_shared_block_backref(fs_info, offset, key.objectid,
+ level);
+ err |= ret;
+ break;
+ case BTRFS_EXTENT_DATA_REF_KEY:
+ dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+ root_objectid = btrfs_extent_data_ref_root(eb, dref);
+ owner = btrfs_extent_data_ref_objectid(eb, dref);
+ owner_offset = btrfs_extent_data_ref_offset(eb, dref);
+ ret = check_extent_data_backref(fs_info, root_objectid, owner,
+ owner_offset, key.objectid, key.offset,
+ btrfs_extent_data_ref_count(eb, dref));
+ err |= ret;
+ break;
+ case BTRFS_SHARED_DATA_REF_KEY:
+ parent = offset;
+ ret = check_shared_data_backref(fs_info, offset, key.objectid);
+ err |= ret;
+ break;
+ default:
+ error("extent[%llu %d %llu] has unknown ref type: %d",
+ key.objectid, key.type, key.offset, type);
+ ret = UNKNOWN_TYPE;
+ err |= ret;
+ goto out;
+ }
+
+ if (err && repair) {
+ ret = repair_extent_item(trans, fs_info->extent_root, path,
+ key.objectid, num_bytes, parent, root_objectid,
+ owner, owner_offset, ret);
+ if (ret < 0)
+ goto out;
+ if (ret) {
+ goto next;
+ err = ret;
+ }
+ }
+
+ ptr += btrfs_extent_inline_ref_size(type);
+ goto next;
+
+out:
+ return err;
+}
+
+/*
+ * Check if a dev extent item is referred correctly by its chunk
+ */
+static int check_dev_extent_item(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *eb, int slot)
+{
+ struct btrfs_root *chunk_root = fs_info->chunk_root;
+ struct btrfs_dev_extent *ptr;
+ struct btrfs_path path;
+ struct btrfs_key chunk_key;
+ struct btrfs_key devext_key;
+ struct btrfs_chunk *chunk;
+ struct extent_buffer *l;
+ int num_stripes;
+ u64 length;
+ int i;
+ int found_chunk = 0;
+ int ret;
+
+ btrfs_item_key_to_cpu(eb, &devext_key, slot);
+ ptr = btrfs_item_ptr(eb, slot, struct btrfs_dev_extent);
+ length = btrfs_dev_extent_length(eb, ptr);
+
+ chunk_key.objectid = btrfs_dev_extent_chunk_objectid(eb, ptr);
+ chunk_key.type = BTRFS_CHUNK_ITEM_KEY;
+ chunk_key.offset = btrfs_dev_extent_chunk_offset(eb, ptr);
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, chunk_root, &chunk_key, &path, 0, 0);
+ if (ret)
+ goto out;
+
+ l = path.nodes[0];
+ chunk = btrfs_item_ptr(l, path.slots[0], struct btrfs_chunk);
+ ret = btrfs_check_chunk_valid(fs_info, l, chunk, path.slots[0],
+ chunk_key.offset);
+ if (ret < 0)
+ goto out;
+
+ if (btrfs_stripe_length(fs_info, l, chunk) != length)
+ goto out;
+
+ num_stripes = btrfs_chunk_num_stripes(l, chunk);
+ for (i = 0; i < num_stripes; i++) {
+ u64 devid = btrfs_stripe_devid_nr(l, chunk, i);
+ u64 offset = btrfs_stripe_offset_nr(l, chunk, i);
+
+ if (devid == devext_key.objectid &&
+ offset == devext_key.offset) {
+ found_chunk = 1;
+ break;
+ }
+ }
+out:
+ btrfs_release_path(&path);
+ if (!found_chunk) {
+ error(
+ "device extent[%llu, %llu, %llu] did not find the related chunk",
+ devext_key.objectid, devext_key.offset, length);
+ return REFERENCER_MISSING;
+ }
+ return 0;
+}
+
+/*
+ * Check if the used space is correct with the dev item
+ */
+static int check_dev_item(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *eb, int slot)
+{
+ struct btrfs_root *dev_root = fs_info->dev_root;
+ struct btrfs_dev_item *dev_item;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_dev_extent *ptr;
+ u64 total_bytes;
+ u64 dev_id;
+ u64 used;
+ u64 total = 0;
+ int ret;
+
+ dev_item = btrfs_item_ptr(eb, slot, struct btrfs_dev_item);
+ dev_id = btrfs_device_id(eb, dev_item);
+ used = btrfs_device_bytes_used(eb, dev_item);
+ total_bytes = btrfs_device_total_bytes(eb, dev_item);
+
+ key.objectid = dev_id;
+ key.type = BTRFS_DEV_EXTENT_KEY;
+ key.offset = 0;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, dev_root, &key, &path, 0, 0);
+ if (ret < 0) {
+ btrfs_item_key_to_cpu(eb, &key, slot);
+ error("cannot find any related dev extent for dev[%llu, %u, %llu]",
+ key.objectid, key.type, key.offset);
+ btrfs_release_path(&path);
+ return REFERENCER_MISSING;
+ }
+
+ /* Iterate dev_extents to calculate the used space of a device */
+ while (1) {
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
+ goto next;
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.objectid > dev_id)
+ break;
+ if (key.type != BTRFS_DEV_EXTENT_KEY || key.objectid != dev_id)
+ goto next;
+
+ ptr = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_dev_extent);
+ total += btrfs_dev_extent_length(path.nodes[0], ptr);
+next:
+ ret = btrfs_next_item(dev_root, &path);
+ if (ret)
+ break;
+ }
+ btrfs_release_path(&path);
+
+ if (used != total) {
+ btrfs_item_key_to_cpu(eb, &key, slot);
+ error(
+"Dev extent's total-byte %llu is not equal to bytes-used %llu in dev[%llu, %u, %llu]",
+ total, used, BTRFS_ROOT_TREE_OBJECTID,
+ BTRFS_DEV_EXTENT_KEY, dev_id);
+ return ACCOUNTING_MISMATCH;
+ }
+ check_dev_size_alignment(dev_id, total_bytes, fs_info->sectorsize);
+
+ return 0;
+}
+
+/*
+ * Check a chunk item.
+ * Including checking all referred dev_extents and block group
+ */
+static int check_chunk_item(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *eb, int slot)
+{
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ struct btrfs_root *dev_root = fs_info->dev_root;
+ struct btrfs_path path;
+ struct btrfs_key chunk_key;
+ struct btrfs_key bg_key;
+ struct btrfs_key devext_key;
+ struct btrfs_chunk *chunk;
+ struct extent_buffer *leaf;
+ struct btrfs_block_group_item *bi;
+ struct btrfs_block_group_item bg_item;
+ struct btrfs_dev_extent *ptr;
+ u64 length;
+ u64 chunk_end;
+ u64 stripe_len;
+ u64 type;
+ int num_stripes;
+ u64 offset;
+ u64 objectid;
+ int i;
+ int ret;
+ int err = 0;
+
+ btrfs_item_key_to_cpu(eb, &chunk_key, slot);
+ chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
+ length = btrfs_chunk_length(eb, chunk);
+ chunk_end = chunk_key.offset + length;
+ ret = btrfs_check_chunk_valid(fs_info, eb, chunk, slot,
+ chunk_key.offset);
+ if (ret < 0) {
+ error("chunk[%llu %llu) is invalid", chunk_key.offset,
+ chunk_end);
+ err |= BYTES_UNALIGNED | UNKNOWN_TYPE;
+ goto out;
+ }
+ type = btrfs_chunk_type(eb, chunk);
+
+ bg_key.objectid = chunk_key.offset;
+ bg_key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+ bg_key.offset = length;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, extent_root, &bg_key, &path, 0, 0);
+ if (ret) {
+ error(
+ "chunk[%llu %llu) did not find the related block group item",
+ chunk_key.offset, chunk_end);
+ err |= REFERENCER_MISSING;
+ } else{
+ leaf = path.nodes[0];
+ bi = btrfs_item_ptr(leaf, path.slots[0],
+ struct btrfs_block_group_item);
+ read_extent_buffer(leaf, &bg_item, (unsigned long)bi,
+ sizeof(bg_item));
+ if (btrfs_block_group_flags(&bg_item) != type) {
+ error(
+"chunk[%llu %llu) related block group item flags mismatch, wanted: %llu, have: %llu",
+ chunk_key.offset, chunk_end, type,
+ btrfs_block_group_flags(&bg_item));
+ err |= REFERENCER_MISSING;
+ }
+ }
+
+ num_stripes = btrfs_chunk_num_stripes(eb, chunk);
+ stripe_len = btrfs_stripe_length(fs_info, eb, chunk);
+ for (i = 0; i < num_stripes; i++) {
+ btrfs_release_path(&path);
+ btrfs_init_path(&path);
+ devext_key.objectid = btrfs_stripe_devid_nr(eb, chunk, i);
+ devext_key.type = BTRFS_DEV_EXTENT_KEY;
+ devext_key.offset = btrfs_stripe_offset_nr(eb, chunk, i);
+
+ ret = btrfs_search_slot(NULL, dev_root, &devext_key, &path,
+ 0, 0);
+ if (ret)
+ goto not_match_dev;
+
+ leaf = path.nodes[0];
+ ptr = btrfs_item_ptr(leaf, path.slots[0],
+ struct btrfs_dev_extent);
+ objectid = btrfs_dev_extent_chunk_objectid(leaf, ptr);
+ offset = btrfs_dev_extent_chunk_offset(leaf, ptr);
+ if (objectid != chunk_key.objectid ||
+ offset != chunk_key.offset ||
+ btrfs_dev_extent_length(leaf, ptr) != stripe_len)
+ goto not_match_dev;
+ continue;
+not_match_dev:
+ err |= BACKREF_MISSING;
+ error(
+ "chunk[%llu %llu) stripe %d did not find the related dev extent",
+ chunk_key.objectid, chunk_end, i);
+ continue;
+ }
+ btrfs_release_path(&path);
+out:
+ return err;
+}
+
+/*
+ * Add block group item to the extent tree if @err contains REFERENCER_MISSING.
+ * FIXME: We still need to repair error of dev_item.
+ *
+ * Returns error after repair.
+ */
+static int repair_chunk_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *chunk_root,
+ struct btrfs_path *path, int err)
+{
+ struct btrfs_chunk *chunk;
+ struct btrfs_key chunk_key;
+ struct extent_buffer *eb = path->nodes[0];
+ u64 length;
+ int slot = path->slots[0];
+ u64 type;
+ int ret = 0;
+
+ btrfs_item_key_to_cpu(eb, &chunk_key, slot);
+ if (chunk_key.type != BTRFS_CHUNK_ITEM_KEY)
+ return err;
+ chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
+ type = btrfs_chunk_type(path->nodes[0], chunk);
+ length = btrfs_chunk_length(eb, chunk);
+
+ if (err & REFERENCER_MISSING) {
+ ret = btrfs_make_block_group(trans, chunk_root->fs_info, 0,
+ type, chunk_key.offset, length);
+ if (ret) {
+ error("fail to add block group item[%llu %llu]",
+ chunk_key.offset, length);
+ goto out;
+ } else {
+ err &= ~REFERENCER_MISSING;
+ printf("Added block group item[%llu %llu]\n",
+ chunk_key.offset, length);
+ }
+ }
+
+out:
+ return err;
+}
+
+static int delete_extent_tree_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path)
+{
+ struct btrfs_key key;
+ int ret = 0;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ if (ret) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = btrfs_del_item(trans, root, path);
+ if (ret)
+ goto out;
+
+ if (path->slots[0] == 0)
+ btrfs_prev_leaf(root, path);
+ else
+ path->slots[0]--;
+out:
+ if (ret)
+ error("failed to delete root %llu item[%llu, %u, %llu]",
+ root->objectid, key.objectid, key.type, key.offset);
+ else
+ printf("Deleted root %llu item[%llu, %u, %llu]\n",
+ root->objectid, key.objectid, key.type, key.offset);
+ return ret;
+}
+
+/*
+ * Main entry function to check known items and update related accounting info
+ */
+static int check_leaf_items(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, struct btrfs_path *path,
+ struct node_refs *nrefs, int account_bytes)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_key key;
+ struct extent_buffer *eb;
+ int slot;
+ int type;
+ struct btrfs_extent_data_ref *dref;
+ int ret = 0;
+ int err = 0;
+
+again:
+ eb = path->nodes[0];
+ slot = path->slots[0];
+ if (slot >= btrfs_header_nritems(eb)) {
+ if (slot == 0) {
+ error("empty leaf [%llu %u] root %llu", eb->start,
+ root->fs_info->nodesize, root->objectid);
+ err |= EIO;
+ }
+ goto out;
+ }
+
+ btrfs_item_key_to_cpu(eb, &key, slot);
+ type = key.type;
+
+ switch (type) {
+ case BTRFS_EXTENT_DATA_KEY:
+ ret = check_extent_data_item(root, path, nrefs, account_bytes);
+ if (repair && ret)
+ ret = repair_extent_data_item(trans, root, path, nrefs,
+ ret);
+ err |= ret;
+ break;
+ case BTRFS_BLOCK_GROUP_ITEM_KEY:
+ ret = check_block_group_item(fs_info, eb, slot);
+ if (repair &&
+ ret & REFERENCER_MISSING)
+ ret = delete_extent_tree_item(trans, root, path);
+ err |= ret;
+ break;
+ case BTRFS_DEV_ITEM_KEY:
+ ret = check_dev_item(fs_info, eb, slot);
+ err |= ret;
+ break;
+ case BTRFS_CHUNK_ITEM_KEY:
+ ret = check_chunk_item(fs_info, eb, slot);
+ if (repair && ret)
+ ret = repair_chunk_item(trans, root, path, ret);
+ err |= ret;
+ break;
+ case BTRFS_DEV_EXTENT_KEY:
+ ret = check_dev_extent_item(fs_info, eb, slot);
+ err |= ret;
+ break;
+ case BTRFS_EXTENT_ITEM_KEY:
+ case BTRFS_METADATA_ITEM_KEY:
+ ret = check_extent_item(trans, fs_info, path);
+ err |= ret;
+ break;
+ case BTRFS_EXTENT_CSUM_KEY:
+ total_csum_bytes += btrfs_item_size_nr(eb, slot);
+ err |= ret;
+ break;
+ case BTRFS_TREE_BLOCK_REF_KEY:
+ ret = check_tree_block_backref(fs_info, key.offset,
+ key.objectid, -1);
+ if (repair &&
+ ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
+ ret = delete_extent_tree_item(trans, root, path);
+ err |= ret;
+ break;
+ case BTRFS_EXTENT_DATA_REF_KEY:
+ dref = btrfs_item_ptr(eb, slot, struct btrfs_extent_data_ref);
+ ret = check_extent_data_backref(fs_info,
+ btrfs_extent_data_ref_root(eb, dref),
+ btrfs_extent_data_ref_objectid(eb, dref),
+ btrfs_extent_data_ref_offset(eb, dref),
+ key.objectid, 0,
+ btrfs_extent_data_ref_count(eb, dref));
+ if (repair &&
+ ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
+ ret = delete_extent_tree_item(trans, root, path);
+ err |= ret;
+ break;
+ case BTRFS_SHARED_BLOCK_REF_KEY:
+ ret = check_shared_block_backref(fs_info, key.offset,
+ key.objectid, -1);
+ if (repair &&
+ ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
+ ret = delete_extent_tree_item(trans, root, path);
+ err |= ret;
+ break;
+ case BTRFS_SHARED_DATA_REF_KEY:
+ ret = check_shared_data_backref(fs_info, key.offset,
+ key.objectid);
+ if (repair &&
+ ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
+ ret = delete_extent_tree_item(trans, root, path);
+ err |= ret;
+ break;
+ default:
+ break;
+ }
+
+ ++path->slots[0];
+ goto again;
+out:
+ return err;
+}
+
+/*
+ * @trans just for lowmem repair mode
+ * @check all if not 0 then check all tree block backrefs and items
+ * 0 then just check relationship of items in fs tree(s)
+ *
+ * Returns >0 Found error, should continue
+ * Returns <0 Fatal error, must exit the whole check
+ * Returns 0 No errors found
+ */
+static int walk_down_tree(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, struct btrfs_path *path,
+ int *level, struct node_refs *nrefs, int ext_ref,
+ int check_all)
+{
+ enum btrfs_tree_block_status status;
+ u64 bytenr;
+ u64 ptr_gen;
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct extent_buffer *next;
+ struct extent_buffer *cur;
+ int ret;
+ int err = 0;
+ int check;
+ int account_file_data = 0;
+
+ WARN_ON(*level < 0);
+ WARN_ON(*level >= BTRFS_MAX_LEVEL);
+
+ ret = update_nodes_refs(root, btrfs_header_bytenr(path->nodes[*level]),
+ path->nodes[*level], nrefs, *level, check_all);
+ if (ret < 0)
+ return ret;
+
+ while (*level >= 0) {
+ WARN_ON(*level < 0);
+ WARN_ON(*level >= BTRFS_MAX_LEVEL);
+ cur = path->nodes[*level];
+ bytenr = btrfs_header_bytenr(cur);
+ check = nrefs->need_check[*level];
+
+ if (btrfs_header_level(cur) != *level)
+ WARN_ON(1);
+ /*
+ * Update bytes accounting and check tree block ref
+ * NOTE: Doing accounting and check before checking nritems
+ * is necessary because of empty node/leaf.
+ */
+ if ((check_all && !nrefs->checked[*level]) ||
+ (!check_all && nrefs->need_check[*level])) {
+ ret = check_tree_block_ref(root, cur,
+ btrfs_header_bytenr(cur), btrfs_header_level(cur),
+ btrfs_header_owner(cur), nrefs);
+
+ if (repair && ret)
+ ret = repair_tree_block_ref(trans, root,
+ path->nodes[*level], nrefs, *level, ret);
+ err |= ret;
+
+ if (check_all && nrefs->need_check[*level] &&
+ nrefs->refs[*level]) {
+ account_bytes(root, path, *level);
+ account_file_data = 1;
+ }
+ nrefs->checked[*level] = 1;
+ }
+
+ if (path->slots[*level] >= btrfs_header_nritems(cur))
+ break;
+
+ /* Don't forgot to check leaf/node validation */
+ if (*level == 0) {
+ /* skip duplicate check */
+ if (check || !check_all) {
+ ret = btrfs_check_leaf(root, NULL, cur);
+ if (ret != BTRFS_TREE_BLOCK_CLEAN) {
+ err |= -EIO;
+ break;
+ }
+ }
+
+ ret = 0;
+ if (!check_all)
+ ret = process_one_leaf(root, path, nrefs,
+ level, ext_ref);
+ else
+ ret = check_leaf_items(trans, root, path,
+ nrefs, account_file_data);
+ err |= ret;
+ break;
+ }
+ if (check || !check_all) {
+ ret = btrfs_check_node(root, NULL, cur);
+ if (ret != BTRFS_TREE_BLOCK_CLEAN) {
+ err |= -EIO;
+ break;
+ }
+ }
+
+ bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
+ ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
+
+ ret = update_nodes_refs(root, bytenr, NULL, nrefs, *level - 1,
+ check_all);
+ if (ret < 0)
+ break;
+ /*
+ * check all trees in check_chunks_and_extent
+ * check shared node once in check_fs_roots
+ */
+ if (!check_all && !nrefs->need_check[*level - 1]) {
+ path->slots[*level]++;
+ continue;
+ }
+
+ next = btrfs_find_tree_block(fs_info, bytenr, fs_info->nodesize);
+ if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
+ free_extent_buffer(next);
+ reada_walk_down(root, cur, path->slots[*level]);
+ next = read_tree_block(fs_info, bytenr, ptr_gen);
+ if (!extent_buffer_uptodate(next)) {
+ struct btrfs_key node_key;
+
+ btrfs_node_key_to_cpu(path->nodes[*level],
+ &node_key,
+ path->slots[*level]);
+ btrfs_add_corrupt_extent_record(fs_info,
+ &node_key, path->nodes[*level]->start,
+ fs_info->nodesize, *level);
+ err |= -EIO;
+ break;
+ }
+ }
+
+ ret = check_child_node(cur, path->slots[*level], next);
+ err |= ret;
+ if (ret < 0)
+ break;
+
+ if (btrfs_is_leaf(next))
+ status = btrfs_check_leaf(root, NULL, next);
+ else
+ status = btrfs_check_node(root, NULL, next);
+ if (status != BTRFS_TREE_BLOCK_CLEAN) {
+ free_extent_buffer(next);
+ err |= -EIO;
+ break;
+ }
+
+ *level = *level - 1;
+ free_extent_buffer(path->nodes[*level]);
+ path->nodes[*level] = next;
+ path->slots[*level] = 0;
+ account_file_data = 0;
+
+ update_nodes_refs(root, (u64)-1, next, nrefs, *level, check_all);
+ }
+ return err;
+}
+
+static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path,
+ 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;
+ }
+ free_extent_buffer(path->nodes[*level]);
+ path->nodes[*level] = NULL;
+ *level = i + 1;
+ }
+ return 1;
+}
+
+/*
+ * Insert the missing inode item and inode ref.
+ *
+ * Normal INODE_ITEM_MISSING and INODE_REF_MISSING are handled in backref * dir.
+ * Root dir should be handled specially because root dir is the root of fs.
+ *
+ * returns err (>0 or 0) after repair
+ */
+static int repair_fs_first_inode(struct btrfs_root *root, int err)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ int filetype = BTRFS_FT_DIR;
+ int ret = 0;
+
+ btrfs_init_path(&path);
+
+ if (err & INODE_REF_MISSING) {
+ key.objectid = BTRFS_FIRST_FREE_OBJECTID;
+ key.type = BTRFS_INODE_REF_KEY;
+ key.offset = BTRFS_FIRST_FREE_OBJECTID;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ goto out;
+ }
+
+ btrfs_release_path(&path);
+ ret = btrfs_search_slot(trans, root, &key, &path, 1, 1);
+ if (ret)
+ goto trans_fail;
+
+ ret = btrfs_insert_inode_ref(trans, root, "..", 2,
+ BTRFS_FIRST_FREE_OBJECTID,
+ BTRFS_FIRST_FREE_OBJECTID, 0);
+ if (ret)
+ goto trans_fail;
+
+ printf("Add INODE_REF[%llu %llu] name %s\n",
+ BTRFS_FIRST_FREE_OBJECTID, BTRFS_FIRST_FREE_OBJECTID,
+ "..");
+ err &= ~INODE_REF_MISSING;
+trans_fail:
+ if (ret)
+ error("fail to insert first inode's ref");
+ btrfs_commit_transaction(trans, root);
+ }
+
+ if (err & INODE_ITEM_MISSING) {
+ ret = repair_inode_item_missing(root,
+ BTRFS_FIRST_FREE_OBJECTID, filetype);
+ if (ret)
+ goto out;
+ err &= ~INODE_ITEM_MISSING;
+ }
+out:
+ if (ret)
+ error("fail to repair first inode");
+ btrfs_release_path(&path);
+ return err;
+}
+
+/*
+ * check first root dir's inode_item and inode_ref
+ *
+ * returns 0 means no error
+ * returns >0 means error
+ * returns <0 means fatal error
+ */
+static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_inode_item *ii;
+ u64 index;
+ u32 mode;
+ int err = 0;
+ int ret;
+
+ key.objectid = BTRFS_FIRST_FREE_OBJECTID;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ /* For root being dropped, we don't need to check first inode */
+ if (btrfs_root_refs(&root->root_item) == 0 &&
+ btrfs_disk_key_objectid(&root->root_item.drop_progress) >=
+ BTRFS_FIRST_FREE_OBJECTID)
+ return 0;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = 0;
+ err |= INODE_ITEM_MISSING;
+ } else {
+ ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_inode_item);
+ mode = btrfs_inode_mode(path.nodes[0], ii);
+ if (imode_to_type(mode) != BTRFS_FT_DIR)
+ err |= INODE_ITEM_MISMATCH;
+ }
+
+ /* lookup first inode ref */
+ key.offset = BTRFS_FIRST_FREE_OBJECTID;
+ key.type = BTRFS_INODE_REF_KEY;
+ /* special index value */
+ index = 0;
+
+ ret = find_inode_ref(root, &key, "..", strlen(".."), &index, ext_ref);
+ if (ret < 0)
+ goto out;
+ err |= ret;
+
+out:
+ btrfs_release_path(&path);
+
+ if (err && repair)
+ err = repair_fs_first_inode(root, err);
+
+ if (err & (INODE_ITEM_MISSING | INODE_ITEM_MISMATCH))
+ error("root dir INODE_ITEM is %s",
+ err & INODE_ITEM_MISMATCH ? "mismatch" : "missing");
+ if (err & INODE_REF_MISSING)
+ error("root dir INODE_REF is missing");
+
+ return ret < 0 ? ret : err;
+}
+
+/*
+ * This function calls walk_down_tree and walk_up_tree to check tree
+ * blocks and integrity of fs tree items.
+ *
+ * @root: the root of the tree to be checked.
+ * @ext_ref feature EXTENDED_IREF is enable or not.
+ * @account if NOT 0 means check the tree (including tree)'s treeblocks.
+ * otherwise means check fs tree(s) items relationship and
+ * @root MUST be a fs tree root.
+ * Returns 0 represents OK.
+ * Returns not 0 represents error.
+ */
+static int check_btrfs_root(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, unsigned int ext_ref,
+ int check_all)
+{
+ struct btrfs_path path;
+ struct node_refs nrefs;
+ struct btrfs_root_item *root_item = &root->root_item;
+ int ret;
+ int level;
+ int err = 0;
+
+ memset(&nrefs, 0, sizeof(nrefs));
+ if (!check_all) {
+ /*
+ * We need to manually check the first inode item (256)
+ * As the following traversal function will only start from
+ * the first inode item in the leaf, if inode item (256) is
+ * missing we will skip it forever.
+ */
+ ret = check_fs_first_inode(root, ext_ref);
+ if (ret < 0)
+ return ret;
+ }
+
+
+ level = btrfs_header_level(root->node);
+ btrfs_init_path(&path);
+
+ if (btrfs_root_refs(root_item) > 0 ||
+ btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
+ path.nodes[level] = root->node;
+ path.slots[level] = 0;
+ extent_buffer_get(root->node);
+ } else {
+ struct btrfs_key key;
+
+ btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
+ level = root_item->drop_level;
+ path.lowest_level = level;
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+ ret = 0;
+ }
+
+ while (1) {
+ ret = walk_down_tree(trans, root, &path, &level, &nrefs,
+ ext_ref, check_all);
+
+ err |= !!ret;
+
+ /* if ret is negative, walk shall stop */
+ if (ret < 0) {
+ ret = err;
+ break;
+ }
+
+ ret = walk_up_tree(root, &path, &level);
+ if (ret != 0) {
+ /* Normal exit, reset ret to err */
+ ret = err;
+ break;
+ }
+ }
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * Iterate all items in the tree and call check_inode_item() to check.
+ *
+ * @root: the root of the tree to be checked.
+ * @ext_ref: the EXTENDED_IREF feature
+ *
+ * Return 0 if no error found.
+ * Return <0 for error.
+ */
+static int check_fs_root(struct btrfs_root *root, unsigned int ext_ref)
+{
+ reset_cached_block_groups(root->fs_info);
+ return check_btrfs_root(NULL, root, ext_ref, 0);
+}
+
+/*
+ * Find the relative ref for root_ref and root_backref.
+ *
+ * @root: the root of the root tree.
+ * @ref_key: the key of the root ref.
+ *
+ * Return 0 if no error occurred.
+ */
+static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
+ struct extent_buffer *node, int slot)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_root_ref *ref;
+ struct btrfs_root_ref *backref;
+ char ref_name[BTRFS_NAME_LEN] = {0};
+ char backref_name[BTRFS_NAME_LEN] = {0};
+ u64 ref_dirid;
+ u64 ref_seq;
+ u32 ref_namelen;
+ u64 backref_dirid;
+ u64 backref_seq;
+ u32 backref_namelen;
+ u32 len;
+ int ret;
+ int err = 0;
+
+ ref = btrfs_item_ptr(node, slot, struct btrfs_root_ref);
+ ref_dirid = btrfs_root_ref_dirid(node, ref);
+ ref_seq = btrfs_root_ref_sequence(node, ref);
+ ref_namelen = btrfs_root_ref_name_len(node, ref);
+
+ if (ref_namelen <= BTRFS_NAME_LEN) {
+ len = ref_namelen;
+ } else {
+ len = BTRFS_NAME_LEN;
+ warning("%s[%llu %llu] ref_name too long",
+ ref_key->type == BTRFS_ROOT_REF_KEY ?
+ "ROOT_REF" : "ROOT_BACKREF", ref_key->objectid,
+ ref_key->offset);
+ }
+ read_extent_buffer(node, ref_name, (unsigned long)(ref + 1), len);
+
+ /* Find relative root_ref */
+ key.objectid = ref_key->offset;
+ key.type = BTRFS_ROOT_BACKREF_KEY + BTRFS_ROOT_REF_KEY - ref_key->type;
+ key.offset = ref_key->objectid;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret) {
+ err |= ROOT_REF_MISSING;
+ error("%s[%llu %llu] couldn't find relative ref",
+ ref_key->type == BTRFS_ROOT_REF_KEY ?
+ "ROOT_REF" : "ROOT_BACKREF",
+ ref_key->objectid, ref_key->offset);
+ goto out;
+ }
+
+ backref = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_root_ref);
+ backref_dirid = btrfs_root_ref_dirid(path.nodes[0], backref);
+ backref_seq = btrfs_root_ref_sequence(path.nodes[0], backref);
+ backref_namelen = btrfs_root_ref_name_len(path.nodes[0], backref);
+
+ if (backref_namelen <= BTRFS_NAME_LEN) {
+ len = backref_namelen;
+ } else {
+ len = BTRFS_NAME_LEN;
+ warning("%s[%llu %llu] ref_name too long",
+ key.type == BTRFS_ROOT_REF_KEY ?
+ "ROOT_REF" : "ROOT_BACKREF",
+ key.objectid, key.offset);
+ }
+ read_extent_buffer(path.nodes[0], backref_name,
+ (unsigned long)(backref + 1), len);
+
+ if (ref_dirid != backref_dirid || ref_seq != backref_seq ||
+ ref_namelen != backref_namelen ||
+ strncmp(ref_name, backref_name, len)) {
+ err |= ROOT_REF_MISMATCH;
+ error("%s[%llu %llu] mismatch relative ref",
+ ref_key->type == BTRFS_ROOT_REF_KEY ?
+ "ROOT_REF" : "ROOT_BACKREF",
+ ref_key->objectid, ref_key->offset);
+ }
+out:
+ btrfs_release_path(&path);
+ return err;
+}
+
+/*
+ * Check all fs/file tree in low_memory mode.
+ *
+ * 1. for fs tree root item, call check_fs_root()
+ * 2. for fs tree root ref/backref, call check_root_ref()
+ *
+ * Return 0 if no error occurred.
+ */
+int check_fs_roots_lowmem(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_root *tree_root = fs_info->tree_root;
+ struct btrfs_root *cur_root = NULL;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct extent_buffer *node;
+ unsigned int ext_ref;
+ int slot;
+ int ret;
+ int err = 0;
+
+ ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF);
+
+ btrfs_init_path(&path);
+ key.objectid = BTRFS_FS_TREE_OBJECTID;
+ key.offset = 0;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+
+ ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
+ if (ret < 0) {
+ err = ret;
+ goto out;
+ } else if (ret > 0) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ while (1) {
+ node = path.nodes[0];
+ slot = path.slots[0];
+ btrfs_item_key_to_cpu(node, &key, slot);
+ if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
+ goto out;
+ if (key.type == BTRFS_ROOT_ITEM_KEY &&
+ fs_root_objectid(key.objectid)) {
+ if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
+ cur_root = btrfs_read_fs_root_no_cache(fs_info,
+ &key);
+ } else {
+ key.offset = (u64)-1;
+ cur_root = btrfs_read_fs_root(fs_info, &key);
+ }
+
+ if (IS_ERR(cur_root)) {
+ error("Fail to read fs/subvol tree: %lld",
+ key.objectid);
+ err = -EIO;
+ goto next;
+ }
+
+ ret = check_fs_root(cur_root, ext_ref);
+ err |= ret;
+
+ if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+ btrfs_free_fs_root(cur_root);
+ } else if (key.type == BTRFS_ROOT_REF_KEY ||
+ key.type == BTRFS_ROOT_BACKREF_KEY) {
+ ret = check_root_ref(tree_root, &key, node, slot);
+ err |= ret;
+ }
+next:
+ ret = btrfs_next_item(tree_root, &path);
+ if (ret > 0)
+ goto out;
+ if (ret < 0) {
+ err = ret;
+ goto out;
+ }
+ }
+
+out:
+ btrfs_release_path(&path);
+ return err;
+}
+
+/*
+ * Low memory usage version check_chunks_and_extents.
+ */
+int check_chunks_and_extents_lowmem(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_trans_handle *trans = NULL;
+ struct btrfs_path path;
+ struct btrfs_key old_key;
+ struct btrfs_key key;
+ struct btrfs_root *root1;
+ struct btrfs_root *root;
+ struct btrfs_root *cur_root;
+ int err = 0;
+ int ret;
+
+ root = fs_info->fs_root;
+
+ if (repair) {
+ trans = btrfs_start_transaction(fs_info->extent_root, 1);
+ if (IS_ERR(trans)) {
+ error("failed to start transaction before check");
+ return PTR_ERR(trans);
+ }
+ }
+
+ root1 = root->fs_info->chunk_root;
+ ret = check_btrfs_root(trans, root1, 0, 1);
+ err |= ret;
+
+ root1 = root->fs_info->tree_root;
+ ret = check_btrfs_root(trans, root1, 0, 1);
+ err |= ret;
+
+ btrfs_init_path(&path);
+ key.objectid = BTRFS_EXTENT_TREE_OBJECTID;
+ key.offset = 0;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+
+ ret = btrfs_search_slot(NULL, root1, &key, &path, 0, 0);
+ if (ret) {
+ error("cannot find extent tree in tree_root");
+ goto out;
+ }
+
+ while (1) {
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.type != BTRFS_ROOT_ITEM_KEY)
+ goto next;
+ old_key = key;
+ key.offset = (u64)-1;
+
+ if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+ cur_root = btrfs_read_fs_root_no_cache(root->fs_info,
+ &key);
+ else
+ cur_root = btrfs_read_fs_root(root->fs_info, &key);
+ if (IS_ERR(cur_root) || !cur_root) {
+ error("failed to read tree: %lld", key.objectid);
+ goto next;
+ }
+
+ ret = check_btrfs_root(trans, cur_root, 0, 1);
+ err |= ret;
+
+ if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+ btrfs_free_fs_root(cur_root);
+
+ btrfs_release_path(&path);
+ ret = btrfs_search_slot(NULL, root->fs_info->tree_root,
+ &old_key, &path, 0, 0);
+ if (ret)
+ goto out;
+next:
+ ret = btrfs_next_item(root1, &path);
+ if (ret)
+ goto out;
+ }
+out:
+
+ /* if repair, update block accounting */
+ if (repair) {
+ ret = btrfs_fix_block_accounting(trans, root);
+ if (ret)
+ err |= ret;
+ else
+ err &= ~BG_ACCOUNTING_ERROR;
+ }
+
+ if (trans)
+ btrfs_commit_transaction(trans, root->fs_info->extent_root);
+
+ btrfs_release_path(&path);
+
+ return err;
+}
diff --git a/check/mode-lowmem.h b/check/mode-lowmem.h
new file mode 100644
index 00000000..73d57999
--- /dev/null
+++ b/check/mode-lowmem.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+/*
+ * Defines and function declarations for lowmem mode check.
+ */
+#ifndef __BTRFS_CHECK_MODE_LOWMEM_H__
+#define __BTRFS_CHECK_MODE_LOWMEM_H__
+
+#include "check/mode-common.h"
+
+#define ROOT_DIR_ERROR (1<<1) /* bad ROOT_DIR */
+#define DIR_ITEM_MISSING (1<<2) /* DIR_ITEM not found */
+#define DIR_ITEM_MISMATCH (1<<3) /* DIR_ITEM found but not match */
+#define INODE_REF_MISSING (1<<4) /* INODE_REF/INODE_EXTREF not found */
+#define INODE_ITEM_MISSING (1<<5) /* INODE_ITEM not found */
+#define INODE_ITEM_MISMATCH (1<<6) /* INODE_ITEM found but not match */
+#define FILE_EXTENT_ERROR (1<<7) /* bad FILE_EXTENT */
+#define ODD_CSUM_ITEM (1<<8) /* CSUM_ITEM error */
+#define CSUM_ITEM_MISSING (1<<9) /* CSUM_ITEM not found */
+#define LINK_COUNT_ERROR (1<<10) /* INODE_ITEM nlink count error */
+#define NBYTES_ERROR (1<<11) /* INODE_ITEM nbytes count error */
+#define ISIZE_ERROR (1<<12) /* INODE_ITEM size count error */
+#define ORPHAN_ITEM (1<<13) /* INODE_ITEM no reference */
+#define NO_INODE_ITEM (1<<14) /* no inode_item */
+#define LAST_ITEM (1<<15) /* Complete this tree traversal */
+#define ROOT_REF_MISSING (1<<16) /* ROOT_REF not found */
+#define ROOT_REF_MISMATCH (1<<17) /* ROOT_REF found but not match */
+#define DIR_INDEX_MISSING (1<<18) /* INODE_INDEX not found */
+#define DIR_INDEX_MISMATCH (1<<19) /* INODE_INDEX found but not match */
+#define DIR_COUNT_AGAIN (1<<20) /* DIR isize should be recalculated */
+#define BG_ACCOUNTING_ERROR (1<<21) /* Block group accounting error */
+
+/*
+ * Error bit for low memory mode check.
+ *
+ * Currently no caller cares about it yet. Just internal use for error
+ * classification.
+ */
+#define BACKREF_MISSING (1 << 0) /* Backref missing in extent tree */
+#define BACKREF_MISMATCH (1 << 1) /* Backref exists but does not match */
+#define BYTES_UNALIGNED (1 << 2) /* Some bytes are not aligned */
+#define REFERENCER_MISSING (1 << 3) /* Referencer not found */
+#define REFERENCER_MISMATCH (1 << 4) /* Referenceer found but does not match */
+#define CROSSING_STRIPE_BOUNDARY (1 << 4) /* For kernel scrub workaround */
+#define ITEM_SIZE_MISMATCH (1 << 5) /* Bad item size */
+#define UNKNOWN_TYPE (1 << 6) /* Unknown type */
+#define ACCOUNTING_MISMATCH (1 << 7) /* Used space accounting error */
+#define CHUNK_TYPE_MISMATCH (1 << 8)
+
+int check_fs_roots_lowmem(struct btrfs_fs_info *fs_info);
+int check_chunks_and_extents_lowmem(struct btrfs_fs_info *fs_info);
+
+#endif
diff --git a/check/mode-original.h b/check/mode-original.h
new file mode 100644
index 00000000..f859af47
--- /dev/null
+++ b/check/mode-original.h
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+/*
+ * Defines and function declarations for original mode check.
+ */
+
+#ifndef __BTRFS_CHECK_MODE_ORIGINAL_H__
+#define __BTRFS_CHECK_MODE_ORIGINAL_H__
+
+#include "rbtree-utils.h"
+
+struct extent_backref {
+ struct rb_node node;
+ unsigned int is_data:1;
+ unsigned int found_extent_tree:1;
+ unsigned int full_backref:1;
+ unsigned int found_ref:1;
+ unsigned int broken:1;
+};
+
+static inline struct extent_backref* rb_node_to_extent_backref(struct rb_node *node)
+{
+ return rb_entry(node, struct extent_backref, node);
+}
+
+struct data_backref {
+ struct extent_backref node;
+ union {
+ u64 parent;
+ u64 root;
+ };
+ u64 owner;
+ u64 offset;
+ u64 disk_bytenr;
+ u64 bytes;
+ u64 ram_bytes;
+ u32 num_refs;
+ u32 found_ref;
+};
+
+static inline struct data_backref* to_data_backref(struct extent_backref *back)
+{
+ return container_of(back, struct data_backref, node);
+}
+
+/*
+ * Much like data_backref, just removed the undetermined members
+ * and change it to use list_head.
+ * During extent scan, it is stored in root->orphan_data_extent.
+ * During fs tree scan, it is then moved to inode_rec->orphan_data_extents.
+ */
+struct orphan_data_extent {
+ struct list_head list;
+ u64 root;
+ u64 objectid;
+ u64 offset;
+ u64 disk_bytenr;
+ u64 disk_len;
+};
+
+struct tree_backref {
+ struct extent_backref node;
+ union {
+ u64 parent;
+ u64 root;
+ };
+};
+
+static inline struct tree_backref* to_tree_backref(struct extent_backref *back)
+{
+ return container_of(back, struct tree_backref, node);
+}
+
+/* Explicit initialization for extent_record::flag_block_full_backref */
+enum { FLAG_UNSET = 2 };
+
+struct extent_record {
+ struct list_head backrefs;
+ struct list_head dups;
+ struct rb_root backref_tree;
+ struct list_head list;
+ struct cache_extent cache;
+ struct btrfs_disk_key parent_key;
+ u64 start;
+ u64 max_size;
+ u64 nr;
+ u64 refs;
+ u64 extent_item_refs;
+ u64 generation;
+ u64 parent_generation;
+ u64 info_objectid;
+ u32 num_duplicates;
+ u8 info_level;
+ unsigned int flag_block_full_backref:2;
+ unsigned int found_rec:1;
+ unsigned int content_checked:1;
+ unsigned int owner_ref_checked:1;
+ unsigned int is_root:1;
+ unsigned int metadata:1;
+ unsigned int bad_full_backref:1;
+ unsigned int crossing_stripes:1;
+ unsigned int wrong_chunk_type:1;
+};
+
+static inline struct extent_record* to_extent_record(struct list_head *entry)
+{
+ return container_of(entry, struct extent_record, list);
+}
+
+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;
+ u8 filetype;
+ u8 ref_type;
+ int errors;
+ u64 dir;
+ u64 index;
+ u16 namelen;
+ char name[0];
+};
+
+static inline struct inode_backref* to_inode_backref(struct list_head *entry)
+{
+ return list_entry(entry, struct inode_backref, list);
+}
+
+struct root_item_record {
+ struct list_head list;
+ u64 objectid;
+ u64 bytenr;
+ u64 last_snapshot;
+ u8 level;
+ u8 drop_level;
+ struct btrfs_key drop_key;
+};
+
+#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 file_extent_hole {
+ struct rb_node node;
+ u64 start;
+ u64 len;
+};
+
+#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)
+#define I_ERR_FILE_EXTENT_ORPHAN (1 << 14)
+
+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;
+ struct rb_root holes;
+ struct list_head orphan_extents;
+
+ u32 refs;
+};
+
+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];
+};
+
+static inline struct root_backref* to_root_backref(struct list_head *entry)
+{
+ return list_entry(entry, struct root_backref, list);
+}
+
+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;
+};
+
+struct bad_item {
+ struct btrfs_key key;
+ u64 root_id;
+ struct list_head list;
+};
+
+struct extent_entry {
+ u64 bytenr;
+ u64 bytes;
+ int count;
+ int broken;
+ struct list_head list;
+};
+
+struct root_item_info {
+ /* level of the root */
+ u8 level;
+ /* number of nodes at this level, must be 1 for a root */
+ int node_count;
+ u64 bytenr;
+ u64 gen;
+ struct cache_extent cache_extent;
+};
+
+#endif
diff --git a/chunk-recover.c b/chunk-recover.c
index 4a6d7141..705bcf52 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -451,24 +451,6 @@ static void print_device_extent_tree(struct device_extent_tree *tree)
printf("\n");
}
-static void print_device_info(struct btrfs_device *device, char *prefix)
-{
- if (prefix)
- printf("%s", prefix);
- printf("Device: id = %llu, name = %s\n",
- device->devid, device->name);
-}
-
-static void print_all_devices(struct list_head *devices)
-{
- struct btrfs_device *dev;
-
- printf("All Devices:\n");
- list_for_each_entry(dev, devices, dev_list)
- print_device_info(dev, "\t");
- printf("\n");
-}
-
static void print_scan_result(struct recover_control *rc)
{
if (!rc->verbose)
diff --git a/cmds-balance.c b/cmds-balance.c
index 3cc0f62d..0c91bdf1 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -475,8 +475,7 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
fprintf(stderr, "balance canceled by user\n");
ret = 0;
} else {
- error("error during balancing '%s': %s", path,
- strerror(errno));
+ error("error during balancing '%s': %m", path);
if (errno != EINPROGRESS)
fprintf(stderr,
"There may be more info in syslog - try dmesg | tail\n");
@@ -794,9 +793,9 @@ static int cmd_balance_resume(int argc, char **argv)
else
ret = 1;
} else {
- error("error during balancing '%s': %s\n"
+ error("error during balancing '%s': %m\n"
"There may be more info in syslog - try dmesg | tail",
- path, strerror(errno));
+ path);
ret = 1;
}
} else {
@@ -868,7 +867,7 @@ static int cmd_balance_status(int argc, char **argv)
ret = 0;
goto out;
}
- error("balance status on '%s' failed: %s", path, strerror(errno));
+ error("balance status on '%s' failed: %m", path);
ret = 2;
goto out;
}
diff --git a/cmds-device.c b/cmds-device.c
index f4cdb39f..86459d1b 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -120,8 +120,8 @@ static int cmd_device_add(int argc, char **argv)
path = canonicalize_path(argv[i]);
if (!path) {
- error("could not canonicalize pathname '%s': %s",
- argv[i], strerror(errno));
+ error("could not canonicalize pathname '%s': %m",
+ argv[i]);
ret++;
goto error_out;
}
@@ -130,8 +130,7 @@ static int cmd_device_add(int argc, char **argv)
strncpy_null(ioctl_args.name, path);
res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
if (res < 0) {
- error("error adding device '%s': %s",
- path, strerror(errno));
+ error("error adding device '%s': %m", path);
ret++;
}
free(path);
@@ -192,8 +191,7 @@ static int _cmd_device_remove(int argc, char **argv,
*/
if (res < 0 && (errno == ENOTTY || errno == EOPNOTSUPP)) {
if (is_devid) {
- error("device delete by id failed: %s",
- strerror(errno));
+ error("device delete by id failed: %m");
ret++;
continue;
}
@@ -311,8 +309,7 @@ static int cmd_device_scan(int argc, char **argv)
}
path = canonicalize_path(argv[i]);
if (!path) {
- error("could not canonicalize path '%s': %s",
- argv[i], strerror(errno));
+ error("could not canonicalize path '%s': %m", argv[i]);
ret = 1;
goto out;
}
@@ -355,8 +352,8 @@ static int cmd_device_ready(int argc, char **argv)
path = canonicalize_path(argv[optind]);
if (!path) {
- error("could not canonicalize pathname '%s': %s",
- argv[optind], strerror(errno));
+ error("could not canonicalize pathname '%s': %m",
+ argv[optind]);
ret = 1;
goto out;
}
@@ -371,8 +368,8 @@ static int cmd_device_ready(int argc, char **argv)
strncpy_null(args.name, path);
ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
if (ret < 0) {
- error("unable to determine if device '%s' is ready for mount: %s",
- path, strerror(errno));
+ error("unable to determine if device '%s' is ready for mount: %m",
+ path);
ret = 1;
}
@@ -466,8 +463,8 @@ static int cmd_device_stats(int argc, char **argv)
args.flags = flags;
if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
- error("device stats ioctl failed on %s: %s",
- path, strerror(errno));
+ error("device stats ioctl failed on %s: %m",
+ path);
err |= 1;
} else {
char *canonical_path;
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 0b0e47fe..de7ad668 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -164,8 +164,7 @@ static int load_chunk_info(int fd, struct chunk_info **info_ptr, int *info_count
return -e;
if (ret < 0) {
- error("cannot look up chunk tree info: %s",
- strerror(e));
+ error("cannot look up chunk tree info: %m");
return 1;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -244,8 +243,7 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
if (ret < 0) {
- error("cannot get space info on '%s': %s", path,
- strerror(errno));
+ error("cannot get space info on '%s': %m", path);
free(sargs);
return NULL;
}
@@ -270,8 +268,8 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
if (ret < 0) {
- error("cannot get space info with %u slots: %s",
- count, strerror(errno));
+ error("cannot get space info with %u slots: %m",
+ count);
free(sargs);
return NULL;
}
@@ -356,8 +354,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
}
if (r_total_size == 0) {
- error("cannot get space info on '%s': %s",
- path, strerror(errno));
+ error("cannot get space info on '%s': %m", path);
ret = 1;
goto exit;
@@ -554,8 +551,7 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
if (ret < 0) {
if (errno == EPERM)
return -errno;
- error("cannot get filesystem info: %s",
- strerror(errno));
+ error("cannot get filesystem info: %m");
return 1;
}
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 17d399d5..467aff11 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -71,7 +71,7 @@ static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret)
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
if (ret < 0) {
- error("cannot get space info: %s", strerror(errno));
+ error("cannot get space info: %m");
free(sargs);
return -errno;
}
@@ -92,8 +92,8 @@ static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret)
sargs->total_spaces = 0;
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
if (ret < 0) {
- error("cannot get space info with %llu slots: %s",
- count, strerror(errno));
+ error("cannot get space info with %llu slots: %m",
+ count);
free(sargs);
return -errno;
}
@@ -173,7 +173,6 @@ static int match_search_item_kernel(u8 *fsid, char *mnt, char *label,
static int uuid_search(struct btrfs_fs_devices *fs_devices, const char *search)
{
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
- struct list_head *cur;
struct btrfs_device *device;
int search_len = strlen(search);
@@ -182,8 +181,7 @@ static int uuid_search(struct btrfs_fs_devices *fs_devices, const char *search)
if (!strncmp(uuidbuf, search, search_len))
return 1;
- list_for_each(cur, &fs_devices->devices) {
- device = list_entry(cur, struct btrfs_device, dev_list);
+ list_for_each_entry(device, &fs_devices->devices, dev_list) {
if ((device->label && strcmp(device->label, search) == 0) ||
strcmp(device->name, search) == 0)
return 1;
@@ -815,7 +813,7 @@ static const char * const cmd_filesystem_sync_usage[] = {
static int cmd_filesystem_sync(int argc, char **argv)
{
- int fd, res, e;
+ int fd, res;
char *path;
DIR *dirstream = NULL;
@@ -831,10 +829,9 @@ static int cmd_filesystem_sync(int argc, char **argv)
return 1;
res = ioctl(fd, BTRFS_IOC_SYNC);
- e = errno;
close_file_or_dir(fd, dirstream);
if( res < 0 ){
- error("sync ioctl failed on '%s': %s", path, strerror(e));
+ error("sync ioctl failed on '%s': %m", path);
return 1;
}
@@ -881,7 +878,6 @@ static int defrag_callback(const char *fpath, const struct stat *sb,
int typeflag, struct FTW *ftwbuf)
{
int ret = 0;
- int err = 0;
int fd = 0;
if ((typeflag == FTW_F) && S_ISREG(sb->st_mode)) {
@@ -889,7 +885,6 @@ static int defrag_callback(const char *fpath, const struct stat *sb,
printf("%s\n", fpath);
fd = open(fpath, O_RDWR);
if (fd < 0) {
- err = errno;
goto error;
}
ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range);
@@ -901,14 +896,13 @@ static int defrag_callback(const char *fpath, const struct stat *sb,
return ENOTTY;
}
if (ret) {
- err = errno;
goto error;
}
}
return 0;
error:
- error("defrag failed on %s: %s", fpath, strerror(err));
+ error("defrag failed on %s: %m", fpath);
defrag_global_errors++;
return 0;
}
@@ -1027,16 +1021,14 @@ static int cmd_filesystem_defrag(int argc, char **argv)
dirstream = NULL;
fd = open_file_or_dir(argv[i], &dirstream);
if (fd < 0) {
- error("cannot open %s: %s", argv[i],
- strerror(errno));
+ error("cannot open %s: %m", argv[i]);
ret = -errno;
goto next;
}
ret = fstat(fd, &st);
if (ret) {
- error("failed to stat %s: %s",
- argv[i], strerror(errno));
+ error("failed to stat %s: %m", argv[i]);
ret = -errno;
goto next;
}
@@ -1117,7 +1109,7 @@ static int cmd_filesystem_resize(int argc, char **argv)
res = stat(path, &st);
if (res < 0) {
- error("resize: cannot stat %s: %s", path, strerror(errno));
+ error("resize: cannot stat %s: %m", path);
return 1;
}
if (!S_ISDIR(st.st_mode)) {
@@ -1144,7 +1136,7 @@ static int cmd_filesystem_resize(int argc, char **argv)
path);
break;
default:
- error("unable to resize '%s': %s", path, strerror(e));
+ error("unable to resize '%s': %m", path);
break;
}
return 1;
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
index 23a71155..150c2e5a 100644
--- a/cmds-inspect-dump-super.c
+++ b/cmds-inspect-dump-super.c
@@ -472,7 +472,7 @@ static int load_and_dump_sb(char *filename, int fd, u64 sb_bytenr, int full,
error("failed to read the superblock on %s at %llu",
filename, (unsigned long long)sb_bytenr);
- error("error = '%s', errno = %d", strerror(errno), errno);
+ error("error = '%m', errno = %d", errno);
return 1;
}
printf("superblock: bytenr=%llu, device=%s\n", sb_bytenr, filename);
@@ -583,7 +583,7 @@ int cmd_inspect_dump_super(int argc, char **argv)
filename = argv[i];
fd = open(filename, O_RDONLY);
if (fd < 0) {
- error("cannot open %s: %s", filename, strerror(errno));
+ error("cannot open %s: %m", filename);
ret = 1;
goto out;
}
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
index 82a6a16c..eced0db9 100644
--- a/cmds-inspect-tree-stats.c
+++ b/cmds-inspect-tree-stats.c
@@ -336,7 +336,7 @@ static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
stat.max_cluster_size = root->fs_info->nodesize;
path.nodes[level] = root->node;
if (gettimeofday(&start, NULL)) {
- error("cannot get time: %s", strerror(errno));
+ error("cannot get time: %m");
goto out;
}
if (!level) {
@@ -350,7 +350,7 @@ static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
if (ret)
goto out;
if (gettimeofday(&end, NULL)) {
- error("cannot get time: %s", strerror(errno));
+ error("cannot get time: %m");
goto out;
}
timeval_subtract(&diff, &end, &start);
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 885f3abe..afd7fe48 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -52,7 +52,7 @@ static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend)
ret = ioctl(fd, BTRFS_IOC_INO_PATHS, &ipa);
if (ret < 0) {
- error("ino paths ioctl: %s", strerror(errno));
+ error("ino paths ioctl: %m");
goto out;
}
@@ -189,7 +189,7 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
ret = ioctl(fd, BTRFS_IOC_LOGICAL_INO, &loi);
if (ret < 0) {
- error("logical ino ioctl: %s", strerror(errno));
+ error("logical ino ioctl: %m");
goto out;
}
@@ -524,7 +524,7 @@ static int print_min_dev_size(int fd, u64 devid)
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0) {
- error("tree search ioctl: %s", strerror(errno));
+ error("tree search ioctl: %m");
ret = 1;
goto out;
}
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 38382ea9..48686436 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -96,7 +96,7 @@ static int _cmd_qgroup_assign(int assign, int argc, char **argv,
ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
if (ret < 0) {
- error("unable to assign quota group: %s", strerror(errno));
+ error("unable to assign quota group: %m");
close_file_or_dir(fd, dirstream);
return 1;
}
@@ -117,8 +117,7 @@ static int _cmd_qgroup_assign(int assign, int argc, char **argv,
memset(&qargs, 0, sizeof(qargs));
ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &qargs);
if (ret < 0)
- error("quota rescan failed: %s",
- strerror(errno));
+ error("quota rescan failed: %m");
} else {
warning("quotas may be inconsistent, rescan needed");
}
@@ -131,7 +130,6 @@ static int _cmd_qgroup_create(int create, int argc, char **argv)
{
int ret = 0;
int fd;
- int e;
char *path;
struct btrfs_ioctl_qgroup_create_args args;
DIR *dirstream = NULL;
@@ -149,11 +147,10 @@ static int _cmd_qgroup_create(int create, int argc, char **argv)
return 1;
ret = ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args);
- e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- error("unable to %s quota group: %s",
- create ? "create":"destroy", strerror(e));
+ error("unable to %s quota group: %m",
+ create ? "create":"destroy");
return 1;
}
return 0;
@@ -377,8 +374,7 @@ static int cmd_qgroup_show(int argc, char **argv)
if (sync) {
ret = ioctl(fd, BTRFS_IOC_SYNC);
if (ret < 0)
- warning("sync ioctl failed on '%s': %s", path,
- strerror(errno));
+ warning("sync ioctl failed on '%s': %m", path);
}
if (filter_flag) {
@@ -399,11 +395,9 @@ static int cmd_qgroup_show(int argc, char **argv)
qgroupid);
}
ret = btrfs_show_qgroups(fd, filter_set, comparer_set);
- if (ret == -ENOENT)
- error("can't list qgroups: quotas not enabled");
- else if (ret < 0)
- error("can't list qgroups: %s", strerror(-ret));
close_file_or_dir(fd, dirstream);
+ free(filter_set);
+ free(comparer_set);
out:
return !!ret;
@@ -423,7 +417,6 @@ static int cmd_qgroup_limit(int argc, char **argv)
{
int ret = 0;
int fd;
- int e;
char *path = NULL;
struct btrfs_ioctl_qgroup_limit_args args;
unsigned long long size;
@@ -494,10 +487,9 @@ static int cmd_qgroup_limit(int argc, char **argv)
return 1;
ret = ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args);
- e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- error("unable to limit requested quota group: %s", strerror(e));
+ error("unable to limit requested quota group: %m");
return 1;
}
return 0;
diff --git a/cmds-quota.c b/cmds-quota.c
index 15bd4b93..745889d1 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -35,7 +35,6 @@ static int quota_ctl(int cmd, int argc, char **argv)
{
int ret = 0;
int fd;
- int e;
char *path = argv[1];
struct btrfs_ioctl_quota_ctl_args args;
DIR *dirstream = NULL;
@@ -51,10 +50,9 @@ static int quota_ctl(int cmd, int argc, char **argv)
return 1;
ret = ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args);
- e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- error("quota command failed: %s", strerror(e));
+ error("quota command failed: %m");
return 1;
}
return 0;
@@ -158,8 +156,7 @@ static int cmd_quota_rescan(int argc, char **argv)
if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN_STATUS) {
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- error("could not obtain quota rescan status: %s",
- strerror(e));
+ error("could not obtain quota rescan status: %m");
return 1;
}
if (!args.flags)
@@ -174,7 +171,7 @@ static int cmd_quota_rescan(int argc, char **argv)
printf("quota rescan started\n");
fflush(stdout);
} else if (ret < 0 && (!wait_for_completion || e != EINPROGRESS)) {
- error("quota rescan failed: %s", strerror(e));
+ error("quota rescan failed: %m");
close_file_or_dir(fd, dirstream);
return 1;
}
@@ -183,8 +180,7 @@ static int cmd_quota_rescan(int argc, char **argv)
ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT, &args);
e = errno;
if (ret < 0) {
- error("quota rescan wait failed: %s",
- strerror(e));
+ error("quota rescan wait failed: %m");
close_file_or_dir(fd, dirstream);
return 1;
}
diff --git a/cmds-receive.c b/cmds-receive.c
index e584cef0..68123a31 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -1330,7 +1330,7 @@ int cmd_receive(int argc, char **argv)
if (fromfile[0]) {
receive_fd = open(fromfile, O_RDONLY | O_NOATIME);
if (receive_fd < 0) {
- error("cannot open %s: %s", fromfile, strerror(errno));
+ error("cannot open %s: %m", fromfile);
goto out;
}
}
diff --git a/cmds-replace.c b/cmds-replace.c
index a3ea977c..032a44fc 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -169,8 +169,8 @@ static int cmd_replace_start(int argc, char **argv)
ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
if (ret < 0) {
fprintf(stderr,
- "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
- path, strerror(errno));
+ "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %m",
+ path);
if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
fprintf(stderr, ", %s\n",
replace_dev_result2string(status_args.result));
@@ -194,8 +194,8 @@ static int cmd_replace_start(int argc, char **argv)
srcdev = argv[optind];
dstdev = canonicalize_path(argv[optind + 1]);
if (!dstdev) {
- error("cannot canonicalize path '%s': %s",
- argv[optind + 1], strerror(errno));
+ error("cannot canonicalize path '%s': %m",
+ argv[optind + 1]);
goto leave_with_error;
}
@@ -250,7 +250,7 @@ static int cmd_replace_start(int argc, char **argv)
fddstdev = open(dstdev, O_RDWR);
if (fddstdev < 0) {
- error("unable to open %s: %s", dstdev, strerror(errno));
+ error("unable to open %s: %m", dstdev);
goto leave_with_error;
}
strncpy((char *)start_args.start.tgtdev_name, dstdev,
@@ -268,7 +268,7 @@ static int cmd_replace_start(int argc, char **argv)
dev_replace_handle_sigint(fdmnt);
if (!do_not_background) {
if (daemon(0, 0) < 0) {
- error("backgrounding failed: %s", strerror(errno));
+ error("backgrounding failed: %m");
goto leave_with_error;
}
}
@@ -279,8 +279,8 @@ static int cmd_replace_start(int argc, char **argv)
if (do_not_background) {
if (ret < 0) {
fprintf(stderr,
- "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s",
- path, strerror(errno));
+ "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %m",
+ path);
if (start_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
fprintf(stderr, ", %s\n",
replace_dev_result2string(start_args.result));
@@ -374,8 +374,8 @@ static int print_replace_status(int fd, const char *path, int once)
args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
if (ret < 0) {
- fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
- path, strerror(errno));
+ fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %m",
+ path);
if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
fprintf(stderr, ", %s\n",
replace_dev_result2string(args.result));
@@ -498,7 +498,6 @@ static int cmd_replace_cancel(int argc, char **argv)
int ret;
int c;
int fd;
- int e;
char *path;
DIR *dirstream = NULL;
@@ -521,11 +520,10 @@ static int cmd_replace_cancel(int argc, char **argv)
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
- e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s",
- path, strerror(e));
+ fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %m",
+ path);
if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
fprintf(stderr, ", %s\n",
replace_dev_result2string(args.result));
diff --git a/cmds-restore.c b/cmds-restore.c
index 6196a1ed..ade35f0f 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -442,7 +442,7 @@ again:
pos+total);
if (done < 0) {
ret = -1;
- error("cannot write data: %d %s", errno, strerror(errno));
+ error("cannot write data: %d %m", errno);
goto out;
}
total += done;
@@ -587,8 +587,8 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
data_len = len;
if (fsetxattr(fd, name, data, data_len, 0))
- error("setting extended attribute %s on file %s: %s",
- name, file_name, strerror(errno));
+ error("setting extended attribute %s on file %s: %m",
+ name, file_name);
len = sizeof(*di) + name_len + data_len;
cur += len;
@@ -624,13 +624,13 @@ static int copy_metadata(struct btrfs_root *root, int fd,
ret = fchown(fd, btrfs_inode_uid(path.nodes[0], inode_item),
btrfs_inode_gid(path.nodes[0], inode_item));
if (ret) {
- error("failed to change owner: %s", strerror(errno));
+ error("failed to change owner: %m");
goto out;
}
ret = fchmod(fd, btrfs_inode_mode(path.nodes[0], inode_item));
if (ret) {
- error("failed to change mode: %s", strerror(errno));
+ error("failed to change mode: %m");
goto out;
}
@@ -644,7 +644,7 @@ static int copy_metadata(struct btrfs_root *root, int fd,
ret = futimens(fd, times);
if (ret) {
- error("failed to set times: %s", strerror(errno));
+ error("failed to set times: %m");
goto out;
}
}
@@ -904,8 +904,8 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
if (!dry_run) {
ret = symlink(symlink_target, path_name);
if (ret < 0) {
- fprintf(stderr, "Failed to restore symlink '%s': %s\n",
- path_name, strerror(errno));
+ fprintf(stderr, "Failed to restore symlink '%s': %m\n",
+ path_name);
goto out;
}
}
@@ -937,8 +937,7 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
btrfs_inode_gid(path.nodes[0], inode_item),
AT_SYMLINK_NOFOLLOW);
if (ret) {
- fprintf(stderr, "Failed to change owner: %s\n",
- strerror(errno));
+ fprintf(stderr, "Failed to change owner: %m\n");
goto out;
}
@@ -952,7 +951,7 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
ret = utimensat(-1, file, times, AT_SYMLINK_NOFOLLOW);
if (ret)
- fprintf(stderr, "Failed to set times: %s\n", strerror(errno));
+ fprintf(stderr, "Failed to set times: %m\n");
out:
btrfs_release_path(&path);
return ret;
diff --git a/cmds-scrub.c b/cmds-scrub.c
index 5388fdcf..dabe7d9a 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -849,8 +849,7 @@ static void *scrub_one_dev(void *ctx)
IOPRIO_PRIO_VALUE(sp->ioprio_class,
sp->ioprio_classdata));
if (ret)
- warning("setting ioprio failed: %s (ignored)",
- strerror(errno));
+ warning("setting ioprio failed: %m (ignored)");
ret = ioctl(sp->fd, BTRFS_IOC_SCRUB, &sp->scrub_args);
gettimeofday(&tv, NULL);
@@ -1195,8 +1194,8 @@ static int scrub_start(int argc, char **argv, int resume)
if (mkdir_p(datafile)) {
warning_on(!do_quiet,
- "cannot create scrub data file, mkdir %s failed: %s. Status recording disabled",
- datafile, strerror(errno));
+ "cannot create scrub data file, mkdir %s failed: %m. Status recording disabled",
+ datafile);
do_record = 0;
}
free(datafile);
@@ -1267,7 +1266,7 @@ static int scrub_start(int argc, char **argv, int resume)
spc.progress = calloc(fi_args.num_devices * 2, sizeof(*spc.progress));
if (!t_devs || !sp || !spc.progress) {
- error_on(!do_quiet, "scrub failed: %s", strerror(errno));
+ error_on(!do_quiet, "scrub failed: %m");
err = 1;
goto out;
}
@@ -1346,9 +1345,9 @@ static int scrub_start(int argc, char **argv, int resume)
ret = listen(prg_fd, 100);
if (ret == -1) {
warning_on(!do_quiet,
- "failed to open the progress status socket at %s: %s. Progress cannot be queried",
+ "failed to open the progress status socket at %s: %m. Progress cannot be queried",
sock_path[0] ? sock_path :
- SCRUB_PROGRESS_SOCKET_PATH, strerror(errno));
+ SCRUB_PROGRESS_SOCKET_PATH);
if (prg_fd != -1) {
close(prg_fd);
prg_fd = -1;
@@ -1372,8 +1371,7 @@ static int scrub_start(int argc, char **argv, int resume)
if (do_background) {
pid = fork();
if (pid == -1) {
- error_on(!do_quiet, "cannot scrub, fork failed: %s",
- strerror(errno));
+ error_on(!do_quiet, "cannot scrub, fork failed: %m");
err = 1;
goto out;
}
@@ -1391,8 +1389,8 @@ static int scrub_start(int argc, char **argv, int resume)
}
ret = wait(&stat);
if (ret != pid) {
- error_on(!do_quiet, "wait failed (ret=%d): %s",
- ret, strerror(errno));
+ error_on(!do_quiet, "wait failed (ret=%d): %m",
+ ret);
err = 1;
goto out;
}
@@ -1720,8 +1718,7 @@ static int cmd_scrub_status(int argc, char **argv)
fdres = socket(AF_UNIX, SOCK_STREAM, 0);
if (fdres == -1) {
- error("failed to create socket to receive progress information: %s",
- strerror(errno));
+ error("failed to create socket to receive progress information: %m");
err = 1;
goto out;
}
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index dc626a64..8a473f7a 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -210,7 +210,7 @@ static int cmd_subvol_create(int argc, char **argv)
}
if (res < 0) {
- error("cannot create subvolume: %s", strerror(errno));
+ error("cannot create subvolume: %m");
goto out;
}
@@ -325,8 +325,7 @@ again:
cpath = realpath(path, NULL);
if (!cpath) {
ret = errno;
- error("cannot find real path for '%s': %s",
- path, strerror(errno));
+ error("cannot find real path for '%s': %m", path);
goto out;
}
dupdname = strdup(cpath);
@@ -348,8 +347,7 @@ again:
strncpy_null(args.name, vname);
res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
if(res < 0 ){
- error("cannot delete '%s/%s': %s", dname, vname,
- strerror(errno));
+ error("cannot delete '%s/%s': %m", dname, vname);
ret = 1;
goto out;
}
@@ -357,8 +355,7 @@ again:
if (commit_mode == COMMIT_EACH) {
res = wait_for_commit(fd);
if (res < 0) {
- error("unable to wait for commit after '%s': %s",
- path, strerror(errno));
+ error("unable to wait for commit after '%s': %m", path);
ret = 1;
}
} else if (commit_mode == COMMIT_AFTER) {
@@ -415,8 +412,8 @@ keep_fd:
if (res < 0) {
uuid_unparse(seen->fsid, uuidbuf);
error(
- "unable to do final sync after deletion: %s, fsid: %s",
- strerror(errno), uuidbuf);
+ "unable to do final sync after deletion: %m, fsid: %s",
+ uuidbuf);
ret = 1;
} else if (verbose > 0) {
uuid_unparse(seen->fsid, uuidbuf);
@@ -776,7 +773,7 @@ static int cmd_subvol_snapshot(int argc, char **argv)
res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
if (res < 0) {
- error("cannot snapshot '%s': %s", subvol, strerror(errno));
+ error("cannot snapshot '%s': %m", subvol);
goto out;
}
@@ -819,8 +816,7 @@ static int cmd_subvol_get_default(int argc, char **argv)
ret = btrfs_list_get_default_subvolume(fd, &default_id);
if (ret) {
- error("failed to look up default subvolume: %s",
- strerror(errno));
+ error("failed to look up default subvolume: %m");
goto out;
}
@@ -868,7 +864,7 @@ static const char * const cmd_subvol_set_default_usage[] = {
static int cmd_subvol_set_default(int argc, char **argv)
{
- int ret=0, fd, e;
+ int ret=0, fd;
u64 objectid;
char *path;
char *subvolid;
@@ -915,11 +911,9 @@ static int cmd_subvol_set_default(int argc, char **argv)
}
ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
- e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- error("unable to set a new default subvolume: %s",
- strerror(e));
+ error("unable to set a new default subvolume: %m");
return 1;
}
return 0;
@@ -963,8 +957,7 @@ static int cmd_subvol_find_new(int argc, char **argv)
ret = ioctl(fd, BTRFS_IOC_SYNC);
if (ret < 0) {
- error("sync ioctl failed on '%s': %s",
- subvol, strerror(errno));
+ error("sync ioctl failed on '%s': %m", subvol);
close_file_or_dir(fd, dirstream);
return 1;
}
@@ -1039,8 +1032,7 @@ static int cmd_subvol_show(int argc, char **argv)
memset(&get_ri, 0, sizeof(get_ri));
fullpath = realpath(argv[optind], NULL);
if (!fullpath) {
- error("cannot find real path for '%s': %s",
- argv[optind], strerror(errno));
+ error("cannot find real path for '%s': %m", argv[optind]);
goto out;
}
diff --git a/configure b/configure
index 1e07165e..67085908 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for btrfs-progs v4.14.1.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.15.1.
#
# Report bugs to <linux-btrfs@vger.kernel.org>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='btrfs-progs'
PACKAGE_TARNAME='btrfs-progs'
-PACKAGE_VERSION='v4.14.1'
-PACKAGE_STRING='btrfs-progs v4.14.1'
+PACKAGE_VERSION='v4.15.1'
+PACKAGE_STRING='btrfs-progs v4.15.1'
PACKAGE_BUGREPORT='linux-btrfs@vger.kernel.org'
PACKAGE_URL='http://btrfs.wiki.kernel.org'
@@ -654,11 +654,13 @@ COM_ERR_CFLAGS
EXT2FS_LIBS
EXT2FS_CFLAGS
DISABLE_BTRFSCONVERT
+ASCIIDOC_TOOL
+ASCIIDOCTOR
+ASCIIDOC
SED
MV
GZIP
XMLTO
-ASCIIDOC
DISABLE_DOCUMENTATION
PKG_CONFIG_LIBDIR
PKG_CONFIG_PATH
@@ -1302,7 +1304,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures btrfs-progs v4.14.1 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.15.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1367,7 +1369,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of btrfs-progs v4.14.1:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.15.1:";;
esac
cat <<\_ACEOF
@@ -1379,7 +1381,7 @@ Optional Features:
--disable-backtrace disable btrfs backtrace
--disable-documentation do not build domumentation
--disable-convert do not build btrfs-convert
- --disable-zstd[=yes] build with zstd support (default: yes)
+ --disable-zstd build without zstd support
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1490,7 +1492,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-btrfs-progs configure v4.14.1
+btrfs-progs configure v4.15.1
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1859,7 +1861,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by btrfs-progs $as_me v4.14.1, which was
+It was created by btrfs-progs $as_me v4.15.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -4934,6 +4936,7 @@ fi
+
for ac_func in openat
do :
ac_fn_c_check_func "$LINENO" "openat" "ac_cv_func_openat"
@@ -5136,48 +5139,8 @@ else
fi
+ASCIIDOC_TOOL="none"
if test "x$enable_documentation" = xyes; then
- # Extract the first word of "asciidoc", so it can be a program name with args.
-set dummy asciidoc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_ASCIIDOC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- case $ASCIIDOC in
- [\\/]* | ?:[\\/]*)
- ac_cv_path_ASCIIDOC="$ASCIIDOC" # Let the user override the test with a path.
- ;;
- *)
- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_path_ASCIIDOC="$as_dir/$ac_word$ac_exec_ext"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
- test -z "$ac_cv_path_ASCIIDOC" && ac_cv_path_ASCIIDOC="asciidoc"
- ;;
-esac
-fi
-ASCIIDOC=$ac_cv_path_ASCIIDOC
-if test -n "$ASCIIDOC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ASCIIDOC" >&5
-$as_echo "$ASCIIDOC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
# Extract the first word of "xmlto", so it can be a program name with args.
set dummy xmlto; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -5370,8 +5333,99 @@ $as_echo "$ac_cv_path_SED" >&6; }
SED="$ac_cv_path_SED"
rm -f conftest.sed
+ # Extract the first word of "asciidoc", so it can be a program name with args.
+set dummy asciidoc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ASCIIDOC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $ASCIIDOC in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ASCIIDOC="$ASCIIDOC" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_ASCIIDOC="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ASCIIDOC=$ac_cv_path_ASCIIDOC
+if test -n "$ASCIIDOC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ASCIIDOC" >&5
+$as_echo "$ASCIIDOC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
+
+ # Extract the first word of "asciidoctor", so it can be a program name with args.
+set dummy asciidoctor; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ASCIIDOCTOR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $ASCIIDOCTOR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ASCIIDOCTOR="$ASCIIDOCTOR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_ASCIIDOCTOR="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ASCIIDOCTOR=$ac_cv_path_ASCIIDOCTOR
+if test -n "$ASCIIDOCTOR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ASCIIDOCTOR" >&5
+$as_echo "$ASCIIDOCTOR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+ if test -n "$ASCIIDOC"; then
+ ASCIIDOC_TOOL="asciidoc"
+ else
+ if test -n "$ASCIIDOCTOR"; then
+ ASCIIDOC_TOOL="asciidoctor"
+ else
+ as_fn_error $? "cannot find asciidoc or asciidoctor, cannot build documentation" "$LINENO" 5
+ fi
+ fi
+fi
+
+
# Check whether --enable-convert was given.
if test "${enable_convert+set}" = set; then :
enableval=$enable_convert;
@@ -5413,8 +5467,8 @@ if test "x$enable_convert" = xyes; then
if test "x$with_convert" = "xauto" || echo "$with_convert" | grep -q "ext2"; then
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for EXT2FS" >&5
-$as_echo_n "checking for EXT2FS... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ext2fs >= 1.42" >&5
+$as_echo_n "checking for ext2fs >= 1.42... " >&6; }
if test -n "$EXT2FS_CFLAGS"; then
pkg_cv_EXT2FS_CFLAGS="$EXT2FS_CFLAGS"
@@ -5454,7 +5508,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -5472,8 +5526,8 @@ fi
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for EXT2FS" >&5
-$as_echo_n "checking for EXT2FS... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ext2fs" >&5
+$as_echo_n "checking for ext2fs... " >&6; }
if test -n "$EXT2FS_CFLAGS"; then
pkg_cv_EXT2FS_CFLAGS="$EXT2FS_CFLAGS"
@@ -5513,7 +5567,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -5540,7 +5594,7 @@ Alternatively, you may set the environment variables EXT2FS_CFLAGS
and EXT2FS_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -5565,12 +5619,12 @@ $as_echo "#define HAVE_OLD_E2FSPROGS 1" >>confdefs.h
fi
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for EXT2FS" >&5
-$as_echo_n "checking for EXT2FS... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ext2fs" >&5
+$as_echo_n "checking for ext2fs... " >&6; }
if test -n "$EXT2FS_CFLAGS"; then
pkg_cv_EXT2FS_CFLAGS="$EXT2FS_CFLAGS"
@@ -5610,7 +5664,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -5637,7 +5691,7 @@ Alternatively, you may set the environment variables EXT2FS_CFLAGS
and EXT2FS_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -5670,8 +5724,8 @@ $as_echo "yes" >&6; }
fi
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for COM_ERR" >&5
-$as_echo_n "checking for COM_ERR... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for com_err" >&5
+$as_echo_n "checking for com_err... " >&6; }
if test -n "$COM_ERR_CFLAGS"; then
pkg_cv_COM_ERR_CFLAGS="$COM_ERR_CFLAGS"
@@ -5711,7 +5765,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -5738,7 +5792,7 @@ Alternatively, you may set the environment variables COM_ERR_CFLAGS
and COM_ERR_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -5765,8 +5819,8 @@ fi
if test "x$with_convert" = "xauto"; then
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for REISERFS" >&5
-$as_echo_n "checking for REISERFS... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for reiserfscore >= 3.6.27" >&5
+$as_echo_n "checking for reiserfscore >= 3.6.27... " >&6; }
if test -n "$REISERFS_CFLAGS"; then
pkg_cv_REISERFS_CFLAGS="$REISERFS_CFLAGS"
@@ -5806,7 +5860,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -5824,7 +5878,7 @@ fi
BTRFSCONVERT_REISERFS=0
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
BTRFSCONVERT_REISERFS=0
else
@@ -5837,8 +5891,8 @@ fi
elif echo "$with_convert" | grep -q "reiserfs"; then
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for REISERFS" >&5
-$as_echo_n "checking for REISERFS... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for reiserfscore >= 3.6.27" >&5
+$as_echo_n "checking for reiserfscore >= 3.6.27... " >&6; }
if test -n "$REISERFS_CFLAGS"; then
pkg_cv_REISERFS_CFLAGS="$REISERFS_CFLAGS"
@@ -5878,7 +5932,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -5905,7 +5959,7 @@ Alternatively, you may set the environment variables REISERFS_CFLAGS
and REISERFS_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -5998,8 +6052,8 @@ fi
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BLKID" >&5
-$as_echo_n "checking for BLKID... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for blkid" >&5
+$as_echo_n "checking for blkid... " >&6; }
if test -n "$BLKID_CFLAGS"; then
pkg_cv_BLKID_CFLAGS="$BLKID_CFLAGS"
@@ -6039,7 +6093,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -6066,7 +6120,7 @@ Alternatively, you may set the environment variables BLKID_CFLAGS
and BLKID_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -6102,8 +6156,8 @@ fi
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for UUID" >&5
-$as_echo_n "checking for UUID... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid" >&5
+$as_echo_n "checking for uuid... " >&6; }
if test -n "$UUID_CFLAGS"; then
pkg_cv_UUID_CFLAGS="$UUID_CFLAGS"
@@ -6143,7 +6197,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -6170,7 +6224,7 @@ Alternatively, you may set the environment variables UUID_CFLAGS
and UUID_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -6206,8 +6260,8 @@ fi
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZLIB" >&5
-$as_echo_n "checking for ZLIB... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for zlib" >&5
+$as_echo_n "checking for zlib... " >&6; }
if test -n "$ZLIB_CFLAGS"; then
pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS"
@@ -6247,7 +6301,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -6274,7 +6328,7 @@ Alternatively, you may set the environment variables ZLIB_CFLAGS
and ZLIB_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -6317,24 +6371,11 @@ else
fi
-if test "x$enable_zstd" = xauto; then
- if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libzstd >= 1.0.0\""; } >&5
- ($PKG_CONFIG --exists --print-errors "libzstd >= 1.0.0") 2>&5
- ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- enable_zstd=yes
-else
- enable_zstd=no
-fi
-fi
-
if test "x$enable_zstd" = xyes; then
pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZSTD" >&5
-$as_echo_n "checking for ZSTD... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libzstd >= 1.0.0" >&5
+$as_echo_n "checking for libzstd >= 1.0.0... " >&6; }
if test -n "$ZSTD_CFLAGS"; then
pkg_cv_ZSTD_CFLAGS="$ZSTD_CFLAGS"
@@ -6374,7 +6415,7 @@ fi
if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -6401,7 +6442,7 @@ Alternatively, you may set the environment variables ZSTD_CFLAGS
and ZSTD_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -7021,7 +7062,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by btrfs-progs $as_me v4.14.1, which was
+This file was extended by btrfs-progs $as_me v4.15.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7084,7 +7125,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-btrfs-progs config.status v4.14.1
+btrfs-progs config.status v4.15.1
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -7812,6 +7853,7 @@ fi
ldflags: ${LDFLAGS}
documentation: ${enable_documentation}
+ doc generator: ${ASCIIDOC_TOOL}
backtrace support: ${enable_backtrace}
btrfs-convert: ${enable_convert} ${convertfs:+($convertfs)}
btrfs-restore zstd: ${enable_zstd}
@@ -7833,6 +7875,7 @@ $as_echo "
ldflags: ${LDFLAGS}
documentation: ${enable_documentation}
+ doc generator: ${ASCIIDOC_TOOL}
backtrace support: ${enable_backtrace}
btrfs-convert: ${enable_convert} ${convertfs:+($convertfs)}
btrfs-restore zstd: ${enable_zstd}
diff --git a/configure.ac b/configure.ac
index bfc010c2..fcdde731 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_INIT([btrfs-progs],
- m4_esyscmd([./version.sh --configure]),
+ m4_chomp(m4_include([VERSION])),
[linux-btrfs@vger.kernel.org],,
[http://btrfs.wiki.kernel.org])
@@ -39,6 +39,7 @@ AC_CHECK_TOOL([AR], [ar])
AC_PATH_PROG([RM], [rm], [rm])
AC_PATH_PROG([RMDIR], [rmdir], [rmdir])
+
AC_CHECK_FUNCS([openat], [],
[AC_MSG_ERROR([cannot find openat() function])])
@@ -88,13 +89,27 @@ AS_IF([test "x$enable_documentation" = xyes], [DISABLE_DOCUMENTATION=0], [DISABL
AC_SUBST([DISABLE_DOCUMENTATION])
dnl detect tools to build documentation
+ASCIIDOC_TOOL="none"
if test "x$enable_documentation" = xyes; then
- AC_PATH_PROG([ASCIIDOC], [asciidoc], [asciidoc])
AC_PATH_PROG([XMLTO], [xmlto], [xmlto])
AC_PATH_PROG([GZIP], [gzip], [gzip])
AC_PATH_PROG([MV], [mv], [mv])
AC_PROG_SED
+ AC_PATH_PROG([ASCIIDOC], [asciidoc])
+ AC_PATH_PROG([ASCIIDOCTOR], [asciidoctor])
+
+ dnl asciidoc is preferred
+ if test -n "$ASCIIDOC"; then
+ ASCIIDOC_TOOL="asciidoc"
+ else
+ if test -n "$ASCIIDOCTOR"; then
+ ASCIIDOC_TOOL="asciidoctor"
+ else
+ AC_MSG_ERROR([cannot find asciidoc or asciidoctor, cannot build documentation])
+ fi
+ fi
fi
+AC_SUBST([ASCIIDOC_TOOL])
AC_ARG_ENABLE([convert],
AS_HELP_STRING([--disable-convert], [do not build btrfs-convert]),
@@ -183,14 +198,10 @@ PKG_CHECK_MODULES(ZLIB, [zlib])
PKG_STATIC(ZLIB_LIBS_STATIC, [zlib])
AC_ARG_ENABLE([zstd],
- AS_HELP_STRING([--disable-zstd@<:@=yes@:>@], [build with zstd support (default: yes)]),
+ AS_HELP_STRING([--disable-zstd], [build without zstd support]),
[], [enable_zstd=yes]
)
-if test "x$enable_zstd" = xauto; then
- PKG_CHECK_EXISTS([libzstd >= 1.0.0], [enable_zstd=yes], [enable_zstd=no])
-fi
-
if test "x$enable_zstd" = xyes; then
PKG_CHECK_MODULES(ZSTD, [libzstd >= 1.0.0])
PKG_STATIC(ZSTD_LIBS_STATIC, [libzstd])
@@ -250,6 +261,7 @@ AC_MSG_RESULT([
ldflags: ${LDFLAGS}
documentation: ${enable_documentation}
+ doc generator: ${ASCIIDOC_TOOL}
backtrace support: ${enable_backtrace}
btrfs-convert: ${enable_convert} ${convertfs:+($convertfs)}
btrfs-restore zstd: ${enable_zstd}
diff --git a/convert/main.c b/convert/main.c
index 89f92611..b3ea31d7 100644
--- a/convert/main.c
+++ b/convert/main.c
@@ -916,9 +916,7 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans,
if (ret < 0)
break;
ret = btrfs_make_block_group(trans, fs_info, 0,
- BTRFS_BLOCK_GROUP_DATA,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- cur, len);
+ BTRFS_BLOCK_GROUP_DATA, cur, len);
if (ret < 0)
break;
cur += len;
@@ -1115,7 +1113,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
goto fail;
fd = open(devname, O_RDWR);
if (fd < 0) {
- error("unable to open %s: %s", devname, strerror(errno));
+ error("unable to open %s: %m", devname);
goto fail;
}
btrfs_parse_features_to_string(features_buf, features);
@@ -1526,7 +1524,7 @@ static int do_rollback(const char *devname)
}
fd = open(devname, O_RDWR);
if (fd < 0) {
- error("unable to open %s: %s", devname, strerror(errno));
+ error("unable to open %s: %m", devname);
ret = -EIO;
goto free_mem;
}
diff --git a/convert/source-ext2.c b/convert/source-ext2.c
index e9277213..b1492c78 100644
--- a/convert/source-ext2.c
+++ b/convert/source-ext2.c
@@ -95,6 +95,7 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
return 0;
fail:
ext2fs_close(ext2_fs);
+ ext2fs_free(ext2_fs);
return -1;
}
@@ -179,6 +180,7 @@ static void ext2_close_fs(struct btrfs_convert_context *cctx)
cctx->volume_name = NULL;
}
ext2fs_close(cctx->fs_data);
+ ext2fs_free(cctx->fs_data);
}
static u8 ext2_filetype_conversion_table[EXT2_FT_MAX] = {
@@ -309,7 +311,7 @@ static int ext2_create_file_extents(struct btrfs_trans_handle *trans,
goto fail;
if ((convert_flags & CONVERT_FLAG_INLINE_DATA) && data.first_block == 0
&& data.num_blocks > 0
- && inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
+ && inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info)) {
u64 num_bytes = data.num_blocks * sectorsize;
u64 disk_bytenr = data.disk_block * sectorsize;
u64 nbytes;
@@ -520,7 +522,7 @@ static int ext2_copy_single_xattr(struct btrfs_trans_handle *trans,
}
strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX);
strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
- if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) -
+ if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root->fs_info) -
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);
diff --git a/convert/source-fs.h b/convert/source-fs.h
index 4e5babef..f5314aff 100644
--- a/convert/source-fs.h
+++ b/convert/source-fs.h
@@ -21,6 +21,7 @@
#include <linux/kdev_t.h>
#include <sys/types.h>
#include <pthread.h>
+#include <sys/types.h>
#define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c
index be79d8e2..39d6f072 100644
--- a/convert/source-reiserfs.c
+++ b/convert/source-reiserfs.c
@@ -376,7 +376,7 @@ static int reiserfs_convert_tail(struct btrfs_trans_handle *trans,
u64 isize;
int ret;
- if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root))
+ if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info))
return convert_direct(trans, root, objectid, inode, body,
length, offset, convert_flags);
@@ -676,7 +676,7 @@ static int reiserfs_xattr_indirect_fn(reiserfs_filsys_t fs, u64 position,
size_t alloc = min(position + num_blocks * fs->fs_blocksize, size);
char *body;
- if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root) -
+ if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root->fs_info) -
sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
fprintf(stderr, "skip large xattr on objectid %llu name %.*s\n",
xa_data->target_oid, (int)xa_data->namelen,
@@ -714,7 +714,7 @@ static int reiserfs_xattr_direct_fn(reiserfs_filsys_t fs, __u64 position,
struct reiserfs_xattr_data *xa_data = data;
char *newbody;
- if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root) -
+ if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root->fs_info) -
sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
fprintf(stderr, "skip large xattr on objectid %llu name %.*s\n",
xa_data->target_oid, (int)xa_data->namelen,
diff --git a/ctree.c b/ctree.c
index 4fc33b14..45b368ce 100644
--- a/ctree.c
+++ b/ctree.c
@@ -408,12 +408,12 @@ static int btrfs_comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2)
* 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)
+static inline unsigned int leaf_data_end(const struct btrfs_fs_info *fs_info,
+ const struct extent_buffer *leaf)
{
u32 nr = btrfs_header_nritems(leaf);
if (nr == 0)
- return BTRFS_LEAF_DATA_SIZE(root);
+ return BTRFS_LEAF_DATA_SIZE(fs_info);
return btrfs_item_offset_nr(leaf, nr - 1);
}
@@ -427,7 +427,7 @@ btrfs_check_node(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
u32 nritems = btrfs_header_nritems(buf);
enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
- if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root))
+ if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root->fs_info))
goto fail;
ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
@@ -515,24 +515,26 @@ btrfs_check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
goto fail;
}
if (i == 0 && btrfs_item_end_nr(buf, i) !=
- BTRFS_LEAF_DATA_SIZE(root)) {
+ BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
fprintf(stderr, "bad item end %u wanted %u\n",
btrfs_item_end_nr(buf, i),
- (unsigned)BTRFS_LEAF_DATA_SIZE(root));
+ (unsigned)BTRFS_LEAF_DATA_SIZE(root->fs_info));
goto fail;
}
}
for (i = 0; i < nritems; i++) {
- if (btrfs_item_end_nr(buf, i) > BTRFS_LEAF_DATA_SIZE(root)) {
+ if (btrfs_item_end_nr(buf, i) >
+ BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
btrfs_item_key(buf, &key, 0);
btrfs_print_key(&key);
fflush(stdout);
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
fprintf(stderr, "slot end outside of leaf %llu > %llu\n",
(unsigned long long)btrfs_item_end_nr(buf, i),
- (unsigned long long)BTRFS_LEAF_DATA_SIZE(root));
+ (unsigned long long)BTRFS_LEAF_DATA_SIZE(
+ root->fs_info));
goto fail;
}
}
@@ -712,7 +714,7 @@ static int balance_level(struct btrfs_trans_handle *trans,
return ret;
}
if (btrfs_header_nritems(mid) >
- BTRFS_NODEPTRS_PER_BLOCK(root) / 4)
+ BTRFS_NODEPTRS_PER_BLOCK(fs_info) / 4)
return 0;
left = read_node_slot(fs_info, parent, pslot - 1);
@@ -880,7 +882,7 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
if (extent_buffer_uptodate(left)) {
u32 left_nr;
left_nr = btrfs_header_nritems(left);
- if (left_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) {
+ if (left_nr >= BTRFS_NODEPTRS_PER_BLOCK(fs_info) - 1) {
wret = 1;
} else {
ret = btrfs_cow_block(trans, root, left, parent,
@@ -923,7 +925,7 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
if (extent_buffer_uptodate(right)) {
u32 right_nr;
right_nr = btrfs_header_nritems(right);
- if (right_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) {
+ if (right_nr >= BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) - 1) {
wret = 1;
} else {
ret = btrfs_cow_block(trans, root, right,
@@ -1142,7 +1144,7 @@ again:
p->slots[level] = slot;
if ((p->search_for_split || ins_len > 0) &&
btrfs_header_nritems(b) >=
- BTRFS_NODEPTRS_PER_BLOCK(root) - 3) {
+ BTRFS_NODEPTRS_PER_BLOCK(fs_info) - 3) {
int sret = split_node(trans, root, p, level);
BUG_ON(sret > 0);
if (sret)
@@ -1288,7 +1290,7 @@ static int push_node_left(struct btrfs_trans_handle *trans,
src_nritems = btrfs_header_nritems(src);
dst_nritems = btrfs_header_nritems(dst);
- push_items = BTRFS_NODEPTRS_PER_BLOCK(root) - dst_nritems;
+ push_items = BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) - dst_nritems;
WARN_ON(btrfs_header_generation(src) != trans->transid);
WARN_ON(btrfs_header_generation(dst) != trans->transid);
@@ -1358,7 +1360,7 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
src_nritems = btrfs_header_nritems(src);
dst_nritems = btrfs_header_nritems(dst);
- push_items = BTRFS_NODEPTRS_PER_BLOCK(root) - dst_nritems;
+ push_items = BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) - dst_nritems;
if (push_items <= 0) {
return 1;
}
@@ -1486,7 +1488,7 @@ static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
nritems = btrfs_header_nritems(lower);
if (slot > nritems)
BUG();
- if (nritems == BTRFS_NODEPTRS_PER_BLOCK(root))
+ if (nritems == BTRFS_NODEPTRS_PER_BLOCK(root->fs_info))
BUG();
if (slot < nritems) {
/* shift the items */
@@ -1535,7 +1537,7 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
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)
+ BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) - 3)
return 0;
if (ret < 0)
return ret;
@@ -1619,7 +1621,7 @@ static int leaf_space_used(struct extent_buffer *l, int start, int nr)
*/
int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf)
{
- u32 nodesize = (root ? BTRFS_LEAF_DATA_SIZE(root) : leaf->len);
+ u32 nodesize = (root ? BTRFS_LEAF_DATA_SIZE(root->fs_info) : leaf->len);
int nritems = btrfs_header_nritems(leaf);
int ret;
ret = nodesize - leaf_space_used(leaf, 0, nritems);
@@ -1733,19 +1735,19 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root
right_nritems = btrfs_header_nritems(right);
push_space = btrfs_item_end_nr(left, left_nritems - push_items);
- push_space -= leaf_data_end(root, left);
+ push_space -= leaf_data_end(fs_info, left);
/* make room in the right data area */
- data_end = leaf_data_end(root, right);
+ data_end = leaf_data_end(fs_info, 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);
+ BTRFS_LEAF_DATA_SIZE(root->fs_info) - 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),
+ BTRFS_LEAF_DATA_SIZE(root->fs_info) - push_space,
+ btrfs_leaf_data(left) + leaf_data_end(fs_info, left),
push_space);
memmove_extent_buffer(right, btrfs_item_nr_offset(push_items),
@@ -1760,7 +1762,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root
/* update the item pointers */
right_nritems += push_items;
btrfs_set_header_nritems(right, right_nritems);
- push_space = BTRFS_LEAF_DATA_SIZE(root);
+ push_space = BTRFS_LEAF_DATA_SIZE(root->fs_info);
for (i = 0; i < right_nritems; i++) {
item = btrfs_item_nr(i);
push_space -= btrfs_item_size(right, item);
@@ -1879,11 +1881,11 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
btrfs_item_nr_offset(0),
push_items * sizeof(struct btrfs_item));
- push_space = BTRFS_LEAF_DATA_SIZE(root) -
+ push_space = BTRFS_LEAF_DATA_SIZE(root->fs_info) -
btrfs_item_offset_nr(right, push_items -1);
copy_extent_buffer(left, right, btrfs_leaf_data(left) +
- leaf_data_end(root, left) - push_space,
+ leaf_data_end(fs_info, left) - push_space,
btrfs_leaf_data(right) +
btrfs_item_offset_nr(right, push_items - 1),
push_space);
@@ -1897,7 +1899,8 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
item = btrfs_item_nr(i);
ioff = btrfs_item_offset(left, item);
btrfs_set_item_offset(left, item,
- ioff - (BTRFS_LEAF_DATA_SIZE(root) - old_left_item_size));
+ ioff - (BTRFS_LEAF_DATA_SIZE(root->fs_info) -
+ old_left_item_size));
}
btrfs_set_header_nritems(left, old_left_nritems + push_items);
@@ -1909,11 +1912,13 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
if (push_items < right_nritems) {
push_space = btrfs_item_offset_nr(right, push_items - 1) -
- leaf_data_end(root, right);
+ leaf_data_end(fs_info, right);
memmove_extent_buffer(right, btrfs_leaf_data(right) +
- BTRFS_LEAF_DATA_SIZE(root) - push_space,
+ BTRFS_LEAF_DATA_SIZE(root->fs_info) -
+ push_space,
btrfs_leaf_data(right) +
- leaf_data_end(root, right), push_space);
+ leaf_data_end(fs_info, right),
+ push_space);
memmove_extent_buffer(right, btrfs_item_nr_offset(0),
btrfs_item_nr_offset(push_items),
@@ -1922,7 +1927,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
}
right_nritems -= push_items;
btrfs_set_header_nritems(right, right_nritems);
- push_space = BTRFS_LEAF_DATA_SIZE(root);
+ push_space = BTRFS_LEAF_DATA_SIZE(root->fs_info);
for (i = 0; i < right_nritems; i++) {
item = btrfs_item_nr(i);
push_space = push_space - btrfs_item_size(right, item);
@@ -1972,18 +1977,20 @@ static noinline int copy_for_split(struct btrfs_trans_handle *trans,
nritems = nritems - mid;
btrfs_set_header_nritems(right, nritems);
- data_copy_size = btrfs_item_end_nr(l, mid) - leaf_data_end(root, l);
+ data_copy_size = btrfs_item_end_nr(l, mid) -
+ leaf_data_end(root->fs_info, 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) -
+ btrfs_leaf_data(right) +
+ BTRFS_LEAF_DATA_SIZE(root->fs_info) -
data_copy_size, btrfs_leaf_data(l) +
- leaf_data_end(root, l), data_copy_size);
+ leaf_data_end(root->fs_info, l), data_copy_size);
- rt_data_off = BTRFS_LEAF_DATA_SIZE(root) -
+ rt_data_off = BTRFS_LEAF_DATA_SIZE(root->fs_info) -
btrfs_item_end_nr(l, mid);
for (i = 0; i < nritems; i++) {
@@ -2044,7 +2051,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
l = path->nodes[0];
slot = path->slots[0];
if (extend && data_size + btrfs_item_size_nr(l, slot) +
- sizeof(struct btrfs_item) > BTRFS_LEAF_DATA_SIZE(root))
+ sizeof(struct btrfs_item) > BTRFS_LEAF_DATA_SIZE(root->fs_info))
return -EOVERFLOW;
/* first try to make some room by pushing left and right */
@@ -2079,21 +2086,22 @@ again:
if (mid <= slot) {
if (nritems == 1 ||
leaf_space_used(l, mid, nritems - mid) + data_size >
- BTRFS_LEAF_DATA_SIZE(root)) {
+ BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
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)) {
+ data_size >
+ BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
split = 2;
}
}
}
} else {
if (leaf_space_used(l, 0, mid) + data_size >
- BTRFS_LEAF_DATA_SIZE(root)) {
+ BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
if (!extend && data_size && slot == 0) {
split = 0;
} else if ((extend || !data_size) && slot == 0) {
@@ -2102,7 +2110,8 @@ again:
mid = slot;
if (mid != nritems &&
leaf_space_used(l, mid, nritems - mid) +
- data_size > BTRFS_LEAF_DATA_SIZE(root)) {
+ data_size >
+ BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
split = 2 ;
}
}
@@ -2317,7 +2326,7 @@ int btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path,
return 0;
nritems = btrfs_header_nritems(leaf);
- data_end = leaf_data_end(root, leaf);
+ data_end = leaf_data_end(root->fs_info, leaf);
old_data_start = btrfs_item_offset_nr(leaf, slot);
@@ -2406,7 +2415,7 @@ int btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path,
leaf = path->nodes[0];
nritems = btrfs_header_nritems(leaf);
- data_end = leaf_data_end(root, leaf);
+ data_end = leaf_data_end(root->fs_info, leaf);
if (btrfs_leaf_free_space(root, leaf) < data_size) {
btrfs_print_leaf(root, leaf);
@@ -2492,7 +2501,7 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
leaf = path->nodes[0];
nritems = btrfs_header_nritems(leaf);
- data_end = leaf_data_end(root, leaf);
+ data_end = leaf_data_end(root->fs_info, leaf);
if (btrfs_leaf_free_space(root, leaf) < total_size) {
btrfs_print_leaf(root, leaf);
@@ -2683,7 +2692,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
nritems = btrfs_header_nritems(leaf);
if (slot + nr != nritems) {
- int data_end = leaf_data_end(root, leaf);
+ int data_end = leaf_data_end(root->fs_info, leaf);
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
data_end + dsize,
@@ -2727,7 +2736,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
}
/* delete the leaf if it is mostly empty */
- if (used < BTRFS_LEAF_DATA_SIZE(root) / 4) {
+ if (used < BTRFS_LEAF_DATA_SIZE(root->fs_info) / 4) {
/* push_leaf_left fixes the path.
* make sure the path still points to our leaf
* for possible call to del_ptr below
diff --git a/ctree.h b/ctree.h
index ef422ea6..17cdac76 100644
--- a/ctree.h
+++ b/ctree.h
@@ -356,18 +356,9 @@ struct btrfs_header {
u8 level;
} __attribute__ ((__packed__));
-#define BTRFS_NODEPTRS_PER_BLOCK(r) (((r)->fs_info->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->fs_info->nodesize))
-#define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \
- sizeof(struct btrfs_item) - \
- sizeof(struct btrfs_file_extent_item))
-#define BTRFS_MAX_XATTR_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \
- sizeof(struct btrfs_item) -\
- sizeof(struct btrfs_dir_item))
-
+#define BTRFS_LEAF_DATA_SIZE(fs_info) \
+ (__BTRFS_LEAF_DATA_SIZE(fs_info->nodesize))
/*
* this is a very generous portion of the super block, giving us
@@ -599,7 +590,8 @@ struct btrfs_extent_item_v0 {
__le32 refs;
} __attribute__ ((__packed__));
-#define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r) >> 4) - \
+#define BTRFS_MAX_EXTENT_ITEM_SIZE(r) \
+ ((BTRFS_LEAF_DATA_SIZE(r->fs_info) >> 4) - \
sizeof(struct btrfs_item))
#define BTRFS_MAX_EXTENT_SIZE SZ_128M
@@ -1189,6 +1181,29 @@ struct btrfs_root {
struct rb_node rb_node;
};
+static inline u32 BTRFS_MAX_ITEM_SIZE(const struct btrfs_fs_info *info)
+{
+ return BTRFS_LEAF_DATA_SIZE(info) - sizeof(struct btrfs_item);
+}
+
+static inline u32 BTRFS_NODEPTRS_PER_BLOCK(const struct btrfs_fs_info *info)
+{
+ return BTRFS_LEAF_DATA_SIZE(info) / sizeof(struct btrfs_key_ptr);
+}
+
+#define BTRFS_FILE_EXTENT_INLINE_DATA_START \
+ (offsetof(struct btrfs_file_extent_item, disk_bytenr))
+static inline u32 BTRFS_MAX_INLINE_DATA_SIZE(const struct btrfs_fs_info *info)
+{
+ return BTRFS_MAX_ITEM_SIZE(info) -
+ BTRFS_FILE_EXTENT_INLINE_DATA_START;
+}
+
+static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)
+{
+ return BTRFS_MAX_ITEM_SIZE(info) - sizeof(struct btrfs_dir_item);
+}
+
/*
* 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
@@ -1842,7 +1857,7 @@ static inline u32 btrfs_item_end_nr(struct extent_buffer *eb, int nr)
return btrfs_item_end(eb, btrfs_item_nr(nr));
}
-static inline u32 btrfs_item_offset_nr(struct extent_buffer *eb, int nr)
+static inline u32 btrfs_item_offset_nr(const struct extent_buffer *eb, int nr)
{
return btrfs_item_offset(eb, btrfs_item_nr(nr));
}
@@ -2467,7 +2482,7 @@ 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);
+ struct btrfs_key *ins, bool is_data);
int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
void btrfs_pin_extent(struct btrfs_fs_info *fs_info, u64 bytenr, u64 num_bytes);
@@ -2486,12 +2501,6 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
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 offset, int metadata, u64 *refs, u64 *flags);
@@ -2529,15 +2538,13 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info);
int btrfs_read_block_groups(struct btrfs_root *root);
struct btrfs_block_group_cache *
btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
- u64 chunk_objectid, u64 chunk_offset, u64 size);
+ u64 chunk_offset, u64 size);
int btrfs_make_block_group(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 bytes_used,
- u64 type, u64 chunk_objectid, u64 chunk_offset,
- u64 size);
+ u64 type, u64 chunk_offset, u64 size);
int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info);
-int btrfs_update_block_group(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 bytenr, u64 num,
+int btrfs_update_block_group(struct btrfs_root *root, u64 bytenr, u64 num,
int alloc, int mark_free);
int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid,
diff --git a/debian/changelog b/debian/changelog
index 86df3a52..b35695de 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+btrfs-progs (4.15.1-1) unstable; urgency=medium
+
+ * New upstream release
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com> Mon, 19 Feb 2018 15:50:12 +0000
+
btrfs-progs (4.14.1-1) unstable; urgency=medium
* New upstream release.
diff --git a/dir-item.c b/dir-item.c
index 462546c0..0b7250c9 100644
--- a/dir-item.c
+++ b/dir-item.c
@@ -311,7 +311,8 @@ static int verify_dir_item(struct btrfs_root *root,
/* BTRFS_MAX_XATTR_SIZE is the same for all dir items */
if ((btrfs_dir_data_len(leaf, dir_item) +
- btrfs_dir_name_len(leaf, dir_item)) > BTRFS_MAX_XATTR_SIZE(root)) {
+ btrfs_dir_name_len(leaf, dir_item)) >
+ BTRFS_MAX_XATTR_SIZE(root->fs_info)) {
fprintf(stderr, "invalid dir item name + data len: %u + %u\n",
(unsigned)btrfs_dir_name_len(leaf, dir_item),
(unsigned)btrfs_dir_data_len(leaf, dir_item));
diff --git a/disk-io.c b/disk-io.c
index f5edc479..76958aef 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -1212,7 +1212,7 @@ struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
ret = stat(filename, &st);
if (ret < 0) {
- error("cannot stat '%s': %s", filename, strerror(errno));
+ error("cannot stat '%s': %m", filename);
return NULL;
}
if (!(((st.st_mode & S_IFMT) == S_IFREG) || ((st.st_mode & S_IFMT) == S_IFBLK))) {
@@ -1225,7 +1225,7 @@ struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
fp = open(filename, oflags);
if (fp < 0) {
- error("cannot open '%s': %s", filename, strerror(errno));
+ error("cannot open '%s': %m", filename);
return NULL;
}
info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr,
@@ -1419,6 +1419,23 @@ error_out:
return -EIO;
}
+/*
+ * btrfs_read_dev_super - read a valid superblock from a block device
+ * @fd: file descriptor of the device
+ * @sb: buffer where the superblock is going to be read in
+ * @sb_bytenr: offset of the particular superblock copy we want
+ * @sbflags: flags controlling how the superblock is read
+ *
+ * This function is used by various btrfs comands to obtain a valid superblock.
+ *
+ * It's mode of operation is controlled by the @sb_bytenr and @sbdflags
+ * parameters. If SBREAD_RECOVER flag is set and @sb_bytenr is
+ * BTRFS_SUPER_INFO_OFFSET then the function reads all 3 superblock copies and
+ * returns the newest one. If SBREAD_RECOVER is not set then only a single
+ * copy is read, which one is decided by @sb_bytenr. If @sb_bytenr !=
+ * BTRFS_SUPER_INFO_OFFSET then the @sbflags is effectively ignored and only a
+ * single copy is read.
+ */
int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
unsigned sbflags)
{
@@ -1549,14 +1566,12 @@ write_err:
if (ret > 0)
fprintf(stderr, "WARNING: failed to write all sb data\n");
else
- fprintf(stderr, "WARNING: failed to write sb: %s\n",
- strerror(errno));
+ fprintf(stderr, "WARNING: failed to write sb: %m\n");
return ret;
}
int write_all_supers(struct btrfs_fs_info *fs_info)
{
- struct list_head *cur;
struct list_head *head = &fs_info->fs_devices->devices;
struct btrfs_device *dev;
struct btrfs_super_block *sb;
@@ -1566,8 +1581,7 @@ int write_all_supers(struct btrfs_fs_info *fs_info)
sb = fs_info->super_copy;
dev_item = &sb->dev_item;
- list_for_each(cur, head) {
- dev = list_entry(cur, struct btrfs_device, dev_list);
+ list_for_each_entry(dev, head, dev_list) {
if (!dev->writeable)
continue;
diff --git a/extent-tree.c b/extent-tree.c
index 055582c3..e2ae74a7 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -1005,7 +1005,6 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
extra_size = -1;
if (owner < BTRFS_FIRST_FREE_OBJECTID && skinny_metadata) {
- skinny_metadata = 1;
key.type = BTRFS_METADATA_ITEM_KEY;
key.offset = owner;
} else if (skinny_metadata) {
@@ -1916,13 +1915,12 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
BUG_ON(ret);
ret = btrfs_make_block_group(trans, fs_info, 0, space_info->flags,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID, start, num_bytes);
+ start, num_bytes);
BUG_ON(ret);
return 0;
}
-static int update_block_group(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int update_block_group(struct btrfs_root *root,
u64 bytenr, u64 num_bytes, int alloc,
int mark_free)
{
@@ -2387,7 +2385,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
BUG_ON(ret);
}
- update_block_group(trans, root, bytenr, num_bytes, 0, mark_free);
+ update_block_group(root, bytenr, num_bytes, 0, mark_free);
}
fail:
btrfs_free_path(path);
@@ -2652,36 +2650,37 @@ 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)
+ struct btrfs_key *ins, bool is_data)
{
int ret;
u64 search_start = 0;
u64 alloc_profile;
+ u64 profile;
struct btrfs_fs_info *info = root->fs_info;
- if (data) {
+ if (is_data) {
alloc_profile = info->avail_data_alloc_bits &
info->data_alloc_profile;
- data = BTRFS_BLOCK_GROUP_DATA | alloc_profile;
+ profile = BTRFS_BLOCK_GROUP_DATA | alloc_profile;
} else if (info->system_allocs == 1 || root == info->chunk_root) {
alloc_profile = info->avail_system_alloc_bits &
info->system_alloc_profile;
- data = BTRFS_BLOCK_GROUP_SYSTEM | alloc_profile;
+ profile = 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;
+ profile = BTRFS_BLOCK_GROUP_METADATA | alloc_profile;
}
if (root->ref_cows) {
- if (!(data & BTRFS_BLOCK_GROUP_METADATA)) {
+ if (!(profile & BTRFS_BLOCK_GROUP_METADATA)) {
ret = do_chunk_alloc(trans, info,
num_bytes,
BTRFS_BLOCK_GROUP_METADATA);
BUG_ON(ret);
}
ret = do_chunk_alloc(trans, info,
- num_bytes + SZ_2M, data);
+ num_bytes + SZ_2M, profile);
BUG_ON(ret);
}
@@ -2689,7 +2688,7 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
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);
+ trans->alloc_exclude_nr, profile);
if (ret < 0)
return ret;
clear_extent_dirty(&info->free_space_cache,
@@ -2747,7 +2746,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(leaf);
btrfs_free_path(path);
- ret = update_block_group(trans, root, ins->objectid, fs_info->nodesize,
+ ret = update_block_group(root, ins->objectid, fs_info->nodesize,
1, 0);
return ret;
}
@@ -3311,7 +3310,7 @@ error:
struct btrfs_block_group_cache *
btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
- u64 chunk_objectid, u64 chunk_offset, u64 size)
+ u64 chunk_offset, u64 size)
{
int ret;
int bit = 0;
@@ -3327,7 +3326,8 @@ btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
cache->key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
btrfs_set_block_group_used(&cache->item, bytes_used);
- btrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid);
+ btrfs_set_block_group_chunk_objectid(&cache->item,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
cache->flags = type;
btrfs_set_block_group_flags(&cache->item, type);
@@ -3352,15 +3352,14 @@ btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
int btrfs_make_block_group(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 bytes_used,
- u64 type, u64 chunk_objectid, u64 chunk_offset,
- u64 size)
+ u64 type, u64 chunk_offset, u64 size)
{
int ret;
struct btrfs_root *extent_root = fs_info->extent_root;
struct btrfs_block_group_cache *cache;
- cache = btrfs_add_block_group(fs_info, bytes_used, type,
- chunk_objectid, chunk_offset, size);
+ cache = btrfs_add_block_group(fs_info, bytes_used, type, chunk_offset,
+ size);
ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item,
sizeof(cache->item));
BUG_ON(ret);
@@ -3474,12 +3473,11 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
return 0;
}
-int btrfs_update_block_group(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+int btrfs_update_block_group(struct btrfs_root *root,
u64 bytenr, u64 num_bytes, int alloc,
int mark_free)
{
- return update_block_group(trans, root, bytenr, num_bytes,
+ return update_block_group(root, bytenr, num_bytes,
alloc, mark_free);
}
@@ -3898,12 +3896,12 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
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,
+ ret = btrfs_update_block_group(root,
key.objectid, key.offset, 1, 0);
BUG_ON(ret);
} else if (key.type == BTRFS_METADATA_ITEM_KEY) {
bytes_used += fs_info->nodesize;
- ret = btrfs_update_block_group(trans, root,
+ ret = btrfs_update_block_group(root,
key.objectid, fs_info->nodesize, 1, 0);
if (ret)
goto out;
@@ -4058,7 +4056,7 @@ static int __btrfs_record_file_extent(struct btrfs_trans_handle *trans,
BTRFS_EXTENT_FLAG_DATA);
btrfs_mark_buffer_dirty(leaf);
- ret = btrfs_update_block_group(trans, root, disk_bytenr,
+ ret = btrfs_update_block_group(root, disk_bytenr,
num_bytes, 1, 0);
if (ret)
goto fail;
diff --git a/file-item.c b/file-item.c
index 8e169e18..7b0ff358 100644
--- a/file-item.c
+++ b/file-item.c
@@ -27,7 +27,7 @@
#include "crc32c.h"
#include "internal.h"
-#define MAX_CSUM_ITEMS(r,size) ((((BTRFS_LEAF_DATA_SIZE(r) - \
+#define MAX_CSUM_ITEMS(r, size) ((((BTRFS_LEAF_DATA_SIZE(r->fs_info) - \
sizeof(struct btrfs_item) * 2) / \
size) - 1))
int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
diff --git a/image/main.c b/image/main.c
index 54a2d7d5..9c75c8b4 100644
--- a/image/main.c
+++ b/image/main.c
@@ -514,7 +514,7 @@ static int write_buffers(struct metadump_struct *md, u64 *next)
ret = fwrite(&md->cluster, BLOCK_SIZE, 1, md->out);
if (ret != 1) {
- error("unable to write out cluster: %s", strerror(errno));
+ error("unable to write out cluster: %m");
return -errno;
}
@@ -530,8 +530,7 @@ static int write_buffers(struct metadump_struct *md, u64 *next)
ret = fwrite(async->buffer, async->bufsize, 1,
md->out);
if (ret != 1) {
- error("unable to write out cluster: %s",
- strerror(errno));
+ error("unable to write out cluster: %m");
err = -errno;
ret = 0;
}
@@ -547,8 +546,7 @@ static int write_buffers(struct metadump_struct *md, u64 *next)
bytenr += size;
ret = write_zero(md->out, size);
if (ret != 1) {
- error("unable to zero out buffer: %s",
- strerror(errno));
+ error("unable to zero out buffer: %m");
err = -errno;
}
}
@@ -646,9 +644,8 @@ static int flush_pending(struct metadump_struct *md, int done)
if (ret < size) {
free(async->buffer);
free(async);
- error("unable to read superblock at %llu: %s",
- (unsigned long long)start,
- strerror(errno));
+ error("unable to read superblock at %llu: %m",
+ (unsigned long long)start);
return -errno;
}
size = 0;
@@ -1341,8 +1338,7 @@ static void write_backup_supers(int fd, u8 *buf)
if (fstat(fd, &st)) {
error(
- "cannot stat restore point, won't be able to write backup supers: %s",
- strerror(errno));
+ "cannot stat restore point, won't be able to write backup supers: %m");
return;
}
@@ -1358,8 +1354,7 @@ static void write_backup_supers(int fd, u8 *buf)
if (ret < BTRFS_SUPER_INFO_SIZE) {
if (ret < 0)
error(
- "problem writing out backup super block %d: %s",
- i, strerror(errno));
+ "problem writing out backup super block %d: %m", i);
else
error("short write writing out backup super block");
break;
@@ -1467,8 +1462,7 @@ static void *restore_worker(void *data)
error:
if (ret < 0) {
- error("unable to write to device: %s",
- strerror(errno));
+ error("unable to write to device: %m");
err = errno;
} else {
error("short write");
@@ -1640,7 +1634,7 @@ static int add_cluster(struct meta_cluster *cluster,
}
ret = fread(async->buffer, async->bufsize, 1, mdres->in);
if (ret != 1) {
- error("unable to read buffer: %s", strerror(errno));
+ error("unable to read buffer: %m");
free(async->buffer);
free(async);
return -EIO;
@@ -1670,7 +1664,7 @@ static int add_cluster(struct meta_cluster *cluster,
bytenr += size;
ret = fread(buffer, size, 1, mdres->in);
if (ret != 1) {
- error("failed to read buffer: %s", strerror(errno));
+ error("failed to read buffer: %m");
return -EIO;
}
}
@@ -1848,7 +1842,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
bytenr = current_cluster;
while (1) {
if (fseek(mdres->in, current_cluster, SEEK_SET)) {
- error("seek failed: %s", strerror(errno));
+ error("seek failed: %m");
ret = -EIO;
break;
}
@@ -1867,9 +1861,8 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
ret = -EIO;
break;
} else if (ret < 0) {
- error("unable to read image at %llu: %s",
- (unsigned long long)cluster_bytenr,
- strerror(errno));
+ error("unable to read image at %llu: %m",
+ (unsigned long long)cluster_bytenr);
break;
}
ret = 0;
@@ -1901,7 +1894,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
if (mdres->compress_method == COMPRESS_ZLIB) {
ret = fread(tmp, bufsize, 1, mdres->in);
if (ret != 1) {
- error("read error: %s", strerror(errno));
+ error("read error: %m");
ret = -EIO;
break;
}
@@ -1919,8 +1912,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
} else {
ret = fread(buffer, bufsize, 1, mdres->in);
if (ret != 1) {
- error("read error: %s",
- strerror(errno));
+ error("read error: %m");
ret = -EIO;
break;
}
@@ -1973,7 +1965,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
ret = fread(cluster, BLOCK_SIZE, 1, mdres->in);
if (ret <= 0) {
- error("unable to read cluster: %s", strerror(errno));
+ error("unable to read cluster: %m");
return -EIO;
}
ret = 0;
@@ -1995,7 +1987,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
break;
bytenr += le32_to_cpu(item->size);
if (fseek(mdres->in, le32_to_cpu(item->size), SEEK_CUR)) {
- error("seek failed: %s", strerror(errno));
+ error("seek failed: %m");
return -EIO;
}
}
@@ -2014,7 +2006,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
ret = fread(buffer, le32_to_cpu(item->size), 1, mdres->in);
if (ret != 1) {
- error("unable to read buffer: %s", strerror(errno));
+ error("unable to read buffer: %m");
free(buffer);
return -EIO;
}
@@ -2196,8 +2188,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
} else {
in = fopen(input, "r");
if (!in) {
- error("unable to open metadump image: %s",
- strerror(errno));
+ error("unable to open metadump image: %m");
return 1;
}
}
@@ -2238,7 +2229,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
}
if (in != stdin && fseek(in, 0, SEEK_SET)) {
- error("seek failed: %s", strerror(errno));
+ error("seek failed: %m");
goto out;
}
@@ -2278,7 +2269,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
info = root->fs_info;
if (stat(target, &st)) {
- error("stat %s failed: %s", target, strerror(errno));
+ error("stat %s failed: %m", target);
close_ctree(info->chunk_root);
free(cluster);
return 1;
@@ -2359,7 +2350,7 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
/* update other devices' super block */
fp = open(other_dev, O_CREAT | O_RDWR, 0600);
if (fp < 0) {
- error("could not open %s: %s", other_dev, strerror(errno));
+ error("could not open %s: %m", other_dev);
ret = -EIO;
goto out;
}
@@ -2554,8 +2545,7 @@ int main(int argc, char *argv[])
0, target, multi_devices);
}
if (ret) {
- error("%s failed: %s", (create) ? "create" : "restore",
- strerror(errno));
+ error("%s failed: %m", (create) ? "create" : "restore");
goto out;
}
@@ -2613,8 +2603,8 @@ out:
unlink_ret = unlink(target);
if (unlink_ret)
- error("unlink output file %s failed: %s",
- target, strerror(errno));
+ error("unlink output file %s failed: %m",
+ target);
}
}
diff --git a/mkfs/common.c b/mkfs/common.c
index dd5e7ecf..16916ca2 100644
--- a/mkfs/common.c
+++ b/mkfs/common.c
@@ -100,6 +100,21 @@ static int btrfs_create_tree_root(int fd, struct btrfs_mkfs_config *cfg,
*
* The superblock signature is not valid, denotes a partially created
* filesystem, needs to be finalized.
+ *
+ * The temporary fs will have the following chunk layout:
+ * Device extent:
+ * 0 1M 5M ......
+ * | Reserved | dev extent for SYS chunk |
+ *
+ * And chunk mapping will be:
+ * Chunk mapping:
+ * 0 1M 5M
+ * | | System chunk, 1:1 mapped |
+ *
+ * That's to say, there will only be *ONE* system chunk, mapped to
+ * [1M, 5M) physical offset.
+ * And the only chunk is also in logical address [1M, 5M), containing
+ * all essential tree blocks.
*/
int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
{
@@ -154,8 +169,8 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
cfg->blocks[MKFS_SUPER_BLOCK] = BTRFS_SUPER_INFO_OFFSET;
for (i = 1; i < MKFS_BLOCK_COUNT; i++) {
- cfg->blocks[i] = BTRFS_SUPER_INFO_OFFSET + SZ_1M +
- cfg->nodesize * i;
+ cfg->blocks[i] = BTRFS_BLOCK_RESERVED_1M_FOR_SUPER +
+ cfg->nodesize * (i - 1);
}
btrfs_set_super_bytenr(&super, cfg->blocks[MKFS_SUPER_BLOCK]);
@@ -309,7 +324,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
/* 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_offset(&disk_key, BTRFS_BLOCK_RESERVED_1M_FOR_SUPER);
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(nritems), itemoff);
@@ -325,7 +340,8 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_chunk_sector_size(buf, chunk, cfg->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);
+ btrfs_set_stripe_offset_nr(buf, chunk, 0,
+ BTRFS_BLOCK_RESERVED_1M_FOR_SUPER);
nritems++;
write_extent_buffer(buf, super.dev_item.uuid,
@@ -363,7 +379,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
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_offset(&disk_key, BTRFS_BLOCK_RESERVED_1M_FOR_SUPER);
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(nritems), itemoff);
@@ -374,7 +390,8 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
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);
+ btrfs_set_dev_extent_chunk_offset(buf, dev_extent,
+ BTRFS_BLOCK_RESERVED_1M_FOR_SUPER);
write_extent_buffer(buf, chunk_tree_uuid,
(unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent),
@@ -466,6 +483,8 @@ u64 btrfs_min_dev_size(u32 nodesize, int mixed, u64 meta_profile,
/*
* Minimal size calculation is complex due to several factors:
+ * 0) Reserved 1M range.
+ *
* 1) Temporary chunk reuse
* If specified chunk profile is SINGLE, we can reuse
* temporary chunks, no need to allocate new chunks.
@@ -484,7 +503,8 @@ u64 btrfs_min_dev_size(u32 nodesize, int mixed, u64 meta_profile,
* The latter two are all 8M, accroding to @calc_size of
* btrfs_alloc_chunk().
*/
- reserved += BTRFS_MKFS_SYSTEM_GROUP_SIZE + SZ_8M * 2;
+ reserved += BTRFS_BLOCK_RESERVED_1M_FOR_SUPER +
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE + SZ_8M * 2;
/*
* For real chunks, we need to select different sizes:
@@ -693,11 +713,11 @@ int test_dev_for_mkfs(const char *file, int force_overwrite)
/* check if the device is busy */
fd = open(file, O_RDWR|O_EXCL);
if (fd < 0) {
- error("unable to open %s: %s", file, strerror(errno));
+ error("unable to open %s: %m", file);
return 1;
}
if (fstat(fd, &st)) {
- error("unable to stat %s: %s", file, strerror(errno));
+ error("unable to stat %s: %m", file);
close(fd);
return 1;
}
diff --git a/mkfs/main.c b/mkfs/main.c
index d817ad8d..5a717f70 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -24,19 +24,14 @@
#include "ioctl.h"
#include <stdio.h>
#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
/* #include <sys/dir.h> included via androidcompat.h */
#include <fcntl.h>
+#include <limits.h>
#include <unistd.h>
#include <getopt.h>
#include <uuid/uuid.h>
#include <ctype.h>
-#include <sys/xattr.h>
-#include <limits.h>
-#include <linux/limits.h>
#include <blkid/blkid.h>
-#include <ftw.h>
#include "ctree.h"
#include "disk-io.h"
#include "volumes.h"
@@ -45,20 +40,11 @@
#include "list_sort.h"
#include "help.h"
#include "mkfs/common.h"
+#include "mkfs/rootdir.h"
#include "fsfeatures.h"
-int path_cat_out(char *out, const char *p1, const char *p2);
-
-static u64 index_cnt = 2;
static int verbose = 1;
-struct directory_name_entry {
- const char *dir_name;
- const char *path;
- ino_t inum;
- struct list_head list;
-};
-
struct mkfs_allocation {
u64 data;
u64 metadata;
@@ -81,10 +67,14 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
bytes_used = btrfs_super_bytes_used(fs_info->super_copy);
root->fs_info->system_allocs = 1;
+ /*
+ * First temporary system chunk must match the chunk layout
+ * created in make_btrfs().
+ */
ret = btrfs_make_block_group(trans, fs_info, bytes_used,
BTRFS_BLOCK_GROUP_SYSTEM,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- 0, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
+ BTRFS_BLOCK_RESERVED_1M_FOR_SUPER,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE);
allocation->system += BTRFS_MKFS_SYSTEM_GROUP_SIZE;
if (ret)
return ret;
@@ -103,7 +93,6 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_METADATA |
BTRFS_BLOCK_GROUP_DATA,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
if (ret)
return ret;
@@ -120,7 +109,6 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
return ret;
ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_METADATA,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
allocation->metadata += chunk_size;
if (ret)
@@ -155,7 +143,6 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans,
return ret;
ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_DATA,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
allocation->data += chunk_size;
if (ret)
@@ -264,8 +251,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
return ret;
ret = btrfs_make_block_group(trans, fs_info, 0,
- type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- chunk_start, chunk_size);
+ type, chunk_start, chunk_size);
type &= BTRFS_BLOCK_GROUP_TYPE_MASK;
if (type == BTRFS_BLOCK_GROUP_DATA) {
@@ -367,6 +353,7 @@ static void print_usage(int ret)
printf(" creation:\n");
printf("\t-b|--byte-count SIZE set filesystem size to SIZE (on the first device)\n");
printf("\t-r|--rootdir DIR copy files from DIR to the image root directory\n");
+ printf("\t--shrink (with --rootdir) shrink the filled filesystem to minimal size\n");
printf("\t-K|--nodiscard do not perform whole device TRIM\n");
printf("\t-f|--force force overwrite of existing filesystem\n");
printf(" general:\n");
@@ -415,748 +402,6 @@ static char *parse_label(const char *input)
return strdup(input);
}
-static int add_directory_items(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- ino_t parent_inum, const char *name,
- struct stat *st, int *dir_index_cnt)
-{
- int ret;
- int name_len;
- struct btrfs_key location;
- u8 filetype = 0;
-
- name_len = strlen(name);
-
- location.objectid = objectid;
- location.offset = 0;
- location.type = BTRFS_INODE_ITEM_KEY;
-
- if (S_ISDIR(st->st_mode))
- filetype = BTRFS_FT_DIR;
- if (S_ISREG(st->st_mode))
- filetype = BTRFS_FT_REG_FILE;
- if (S_ISLNK(st->st_mode))
- filetype = BTRFS_FT_SYMLINK;
- if (S_ISSOCK(st->st_mode))
- filetype = BTRFS_FT_SOCK;
- if (S_ISCHR(st->st_mode))
- filetype = BTRFS_FT_CHRDEV;
- if (S_ISBLK(st->st_mode))
- filetype = BTRFS_FT_BLKDEV;
- if (S_ISFIFO(st->st_mode))
- filetype = BTRFS_FT_FIFO;
-
- ret = btrfs_insert_dir_item(trans, root, name, name_len,
- parent_inum, &location,
- filetype, index_cnt);
- if (ret)
- return ret;
- ret = btrfs_insert_inode_ref(trans, root, name, name_len,
- objectid, parent_inum, index_cnt);
- *dir_index_cnt = index_cnt;
- index_cnt++;
-
- return ret;
-}
-
-static int fill_inode_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_inode_item *dst, struct stat *src)
-{
- u64 blocks = 0;
- u64 sectorsize = root->fs_info->sectorsize;
-
- /*
- * btrfs_inode_item has some reserved fields
- * and represents on-disk inode entry, so
- * zero everything to prevent information leak
- */
- memset(dst, 0, sizeof (*dst));
-
- btrfs_set_stack_inode_generation(dst, trans->transid);
- btrfs_set_stack_inode_size(dst, src->st_size);
- btrfs_set_stack_inode_nbytes(dst, 0);
- btrfs_set_stack_inode_block_group(dst, 0);
- btrfs_set_stack_inode_nlink(dst, src->st_nlink);
- btrfs_set_stack_inode_uid(dst, src->st_uid);
- btrfs_set_stack_inode_gid(dst, src->st_gid);
- btrfs_set_stack_inode_mode(dst, src->st_mode);
- btrfs_set_stack_inode_rdev(dst, 0);
- btrfs_set_stack_inode_flags(dst, 0);
- btrfs_set_stack_timespec_sec(&dst->atime, src->st_atime);
- btrfs_set_stack_timespec_nsec(&dst->atime, 0);
- btrfs_set_stack_timespec_sec(&dst->ctime, src->st_ctime);
- btrfs_set_stack_timespec_nsec(&dst->ctime, 0);
- btrfs_set_stack_timespec_sec(&dst->mtime, src->st_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->st_mode)) {
- btrfs_set_stack_inode_size(dst, 0);
- btrfs_set_stack_inode_nlink(dst, 1);
- }
- if (S_ISREG(src->st_mode)) {
- btrfs_set_stack_inode_size(dst, (u64)src->st_size);
- if (src->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root))
- btrfs_set_stack_inode_nbytes(dst, src->st_size);
- else {
- blocks = src->st_size / sectorsize;
- if (src->st_size % sectorsize)
- blocks += 1;
- blocks *= sectorsize;
- btrfs_set_stack_inode_nbytes(dst, blocks);
- }
- }
- if (S_ISLNK(src->st_mode))
- btrfs_set_stack_inode_nbytes(dst, src->st_size + 1);
-
- return 0;
-}
-
-static int directory_select(const struct direct *entry)
-{
- if (entry->d_name[0] == '.' &&
- (entry->d_name[1] == 0 ||
- (entry->d_name[1] == '.' && entry->d_name[2] == 0)))
- return 0;
- return 1;
-}
-
-static void free_namelist(struct direct **files, int count)
-{
- int i;
-
- if (count < 0)
- return;
-
- for (i = 0; i < count; ++i)
- free(files[i]);
- free(files);
-}
-
-static u64 calculate_dir_inode_size(const char *dirname)
-{
- int count, i;
- struct direct **files, *cur_file;
- u64 dir_inode_size = 0;
-
- count = scandir(dirname, &files, directory_select, NULL);
-
- for (i = 0; i < count; i++) {
- cur_file = files[i];
- dir_inode_size += strlen(cur_file->d_name);
- }
-
- free_namelist(files, count);
-
- dir_inode_size *= 2;
- return dir_inode_size;
-}
-
-static int add_inode_items(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct stat *st, const char *name,
- u64 self_objectid,
- struct btrfs_inode_item *inode_ret)
-{
- int ret;
- struct btrfs_inode_item btrfs_inode;
- u64 objectid;
- u64 inode_size = 0;
-
- fill_inode_item(trans, root, &btrfs_inode, st);
- objectid = self_objectid;
-
- if (S_ISDIR(st->st_mode)) {
- inode_size = calculate_dir_inode_size(name);
- btrfs_set_stack_inode_size(&btrfs_inode, inode_size);
- }
-
- ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
-
- *inode_ret = btrfs_inode;
- return ret;
-}
-
-static int add_xattr_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- const char *file_name)
-{
- int ret;
- int cur_name_len;
- char xattr_list[XATTR_LIST_MAX];
- char *cur_name;
- char cur_value[XATTR_SIZE_MAX];
- char delimiter = '\0';
- char *next_location = xattr_list;
-
- ret = llistxattr(file_name, xattr_list, XATTR_LIST_MAX);
- if (ret < 0) {
- if(errno == ENOTSUP)
- return 0;
- error("getting a list of xattr failed for %s: %s", file_name,
- strerror(errno));
- return ret;
- }
- if (ret == 0)
- return ret;
-
- cur_name = strtok(xattr_list, &delimiter);
- while (cur_name != NULL) {
- cur_name_len = strlen(cur_name);
- next_location += cur_name_len + 1;
-
- ret = getxattr(file_name, cur_name, cur_value, XATTR_SIZE_MAX);
- if (ret < 0) {
- if(errno == ENOTSUP)
- return 0;
- error("gettig a xattr value failed for %s attr %s: %s",
- file_name, cur_name, strerror(errno));
- return ret;
- }
-
- ret = btrfs_insert_xattr_item(trans, root, cur_name,
- cur_name_len, cur_value,
- ret, objectid);
- if (ret) {
- error("inserting a xattr item failed for %s: %s",
- file_name, strerror(-ret));
- }
-
- cur_name = strtok(next_location, &delimiter);
- }
-
- return ret;
-}
-
-static int add_symbolic_link(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- u64 objectid, const char *path_name)
-{
- int ret;
- char buf[PATH_MAX];
-
- ret = readlink(path_name, buf, sizeof(buf));
- if (ret <= 0) {
- error("readlink failed for %s: %s", path_name, strerror(errno));
- goto fail;
- }
- if (ret >= sizeof(buf)) {
- error("symlink too long for %s", path_name);
- ret = -1;
- goto fail;
- }
-
- buf[ret] = '\0'; /* readlink does not do it for us */
- ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
- buf, ret + 1);
-fail:
- return ret;
-}
-
-static int add_file_items(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_inode_item *btrfs_inode, u64 objectid,
- struct stat *st, const char *path_name)
-{
- int ret = -1;
- ssize_t ret_read;
- u64 bytes_read = 0;
- struct btrfs_key key;
- int blocks;
- u32 sectorsize = root->fs_info->sectorsize;
- u64 first_block = 0;
- u64 file_pos = 0;
- u64 cur_bytes;
- u64 total_bytes;
- struct extent_buffer *eb = NULL;
- int fd;
-
- if (st->st_size == 0)
- return 0;
-
- fd = open(path_name, O_RDONLY);
- if (fd == -1) {
- error("cannot open %s: %s", path_name, strerror(errno));
- return ret;
- }
-
- blocks = st->st_size / sectorsize;
- if (st->st_size % sectorsize)
- blocks += 1;
-
- if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
- char *buffer = malloc(st->st_size);
-
- if (!buffer) {
- ret = -ENOMEM;
- goto end;
- }
-
- ret_read = pread64(fd, buffer, st->st_size, bytes_read);
- if (ret_read == -1) {
- error("cannot read %s at offset %llu length %llu: %s",
- path_name, (unsigned long long)bytes_read,
- (unsigned long long)st->st_size,
- strerror(errno));
- free(buffer);
- goto end;
- }
-
- ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
- buffer, st->st_size);
- free(buffer);
- goto end;
- }
-
- /* round up our st_size to the FS blocksize */
- total_bytes = (u64)blocks * sectorsize;
-
- /*
- * do our IO in extent buffers so it can work
- * against any raid type
- */
- eb = calloc(1, sizeof(*eb) + sectorsize);
- if (!eb) {
- ret = -ENOMEM;
- goto end;
- }
-
-again:
-
- /*
- * keep our extent size at 1MB max, this makes it easier to work inside
- * the tiny block groups created during mkfs
- */
- cur_bytes = min(total_bytes, (u64)SZ_1M);
- ret = btrfs_reserve_extent(trans, root, cur_bytes, 0, 0, (u64)-1,
- &key, 1);
- if (ret)
- goto end;
-
- first_block = key.objectid;
- bytes_read = 0;
-
- while (bytes_read < cur_bytes) {
-
- memset(eb->data, 0, sectorsize);
-
- ret_read = pread64(fd, eb->data, sectorsize, file_pos + bytes_read);
- if (ret_read == -1) {
- error("cannot read %s at offset %llu length %llu: %s",
- path_name,
- (unsigned long long)file_pos + bytes_read,
- (unsigned long long)sectorsize,
- strerror(errno));
- goto end;
- }
-
- eb->start = first_block + bytes_read;
- eb->len = sectorsize;
-
- /*
- * we're doing the csum before we record the extent, but
- * that's ok
- */
- ret = btrfs_csum_file_block(trans, root->fs_info->csum_root,
- first_block + bytes_read + sectorsize,
- first_block + bytes_read,
- eb->data, sectorsize);
- if (ret)
- goto end;
-
- ret = write_and_map_eb(root->fs_info, eb);
- if (ret) {
- error("failed to write %s", path_name);
- goto end;
- }
-
- bytes_read += sectorsize;
- }
-
- if (bytes_read) {
- ret = btrfs_record_file_extent(trans, root, objectid, btrfs_inode,
- file_pos, first_block, cur_bytes);
- if (ret)
- goto end;
-
- }
-
- file_pos += cur_bytes;
- total_bytes -= cur_bytes;
-
- if (total_bytes)
- goto again;
-
-end:
- free(eb);
- close(fd);
- return ret;
-}
-
-static int traverse_directory(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, const char *dir_name,
- struct directory_name_entry *dir_head)
-{
- int ret = 0;
-
- struct btrfs_inode_item cur_inode;
- struct btrfs_inode_item *inode_item;
- int count, i, dir_index_cnt;
- struct direct **files;
- struct stat st;
- struct directory_name_entry *dir_entry, *parent_dir_entry;
- struct direct *cur_file;
- ino_t parent_inum, cur_inum;
- ino_t highest_inum = 0;
- const char *parent_dir_name;
- char real_path[PATH_MAX];
- struct btrfs_path path;
- struct extent_buffer *leaf;
- struct btrfs_key root_dir_key;
- u64 root_dir_inode_size = 0;
-
- /* Add list for source directory */
- dir_entry = malloc(sizeof(struct directory_name_entry));
- if (!dir_entry)
- return -ENOMEM;
- dir_entry->dir_name = dir_name;
- dir_entry->path = realpath(dir_name, real_path);
- if (!dir_entry->path) {
- error("realpath failed for %s: %s", dir_name, strerror(errno));
- ret = -1;
- goto fail_no_dir;
- }
-
- parent_inum = highest_inum + BTRFS_FIRST_FREE_OBJECTID;
- dir_entry->inum = parent_inum;
- list_add_tail(&dir_entry->list, &dir_head->list);
-
- btrfs_init_path(&path);
-
- root_dir_key.objectid = btrfs_root_dirid(&root->root_item);
- root_dir_key.offset = 0;
- root_dir_key.type = BTRFS_INODE_ITEM_KEY;
- ret = btrfs_lookup_inode(trans, root, &path, &root_dir_key, 1);
- if (ret) {
- error("failed to lookup root dir: %d", ret);
- goto fail_no_dir;
- }
-
- leaf = path.nodes[0];
- inode_item = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_inode_item);
-
- root_dir_inode_size = calculate_dir_inode_size(dir_name);
- btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size);
- btrfs_mark_buffer_dirty(leaf);
-
- btrfs_release_path(&path);
-
- do {
- parent_dir_entry = list_entry(dir_head->list.next,
- struct directory_name_entry,
- list);
- list_del(&parent_dir_entry->list);
-
- parent_inum = parent_dir_entry->inum;
- parent_dir_name = parent_dir_entry->dir_name;
- if (chdir(parent_dir_entry->path)) {
- error("chdir failed for %s: %s",
- parent_dir_name, strerror(errno));
- ret = -1;
- goto fail_no_files;
- }
-
- count = scandir(parent_dir_entry->path, &files,
- directory_select, NULL);
- if (count == -1)
- {
- error("scandir failed for %s: %s",
- parent_dir_name, strerror (errno));
- ret = -1;
- goto fail;
- }
-
- for (i = 0; i < count; i++) {
- cur_file = files[i];
-
- if (lstat(cur_file->d_name, &st) == -1) {
- error("lstat failed for %s: %s",
- cur_file->d_name, strerror(errno));
- ret = -1;
- goto fail;
- }
-
- cur_inum = st.st_ino;
- ret = add_directory_items(trans, root,
- cur_inum, parent_inum,
- cur_file->d_name,
- &st, &dir_index_cnt);
- if (ret) {
- error("unable to add directory items for %s: %d",
- cur_file->d_name, ret);
- goto fail;
- }
-
- ret = add_inode_items(trans, root, &st,
- cur_file->d_name, cur_inum,
- &cur_inode);
- if (ret == -EEXIST) {
- if (st.st_nlink <= 1) {
- error(
- "item %s already exists but has wrong st_nlink %lu <= 1",
- cur_file->d_name,
- (unsigned long)st.st_nlink);
- goto fail;
- }
- continue;
- }
- if (ret) {
- error("unable to add inode items for %s: %d",
- cur_file->d_name, ret);
- goto fail;
- }
-
- ret = add_xattr_item(trans, root,
- cur_inum, cur_file->d_name);
- if (ret) {
- error("unable to add xattr items for %s: %d",
- cur_file->d_name, ret);
- if(ret != -ENOTSUP)
- goto fail;
- }
-
- if (S_ISDIR(st.st_mode)) {
- char tmp[PATH_MAX];
-
- dir_entry = malloc(sizeof(struct directory_name_entry));
- if (!dir_entry) {
- ret = -ENOMEM;
- goto fail;
- }
- dir_entry->dir_name = cur_file->d_name;
- if (path_cat_out(tmp, parent_dir_entry->path,
- cur_file->d_name)) {
- error("invalid path: %s/%s",
- parent_dir_entry->path,
- cur_file->d_name);
- ret = -EINVAL;
- goto fail;
- }
- dir_entry->path = strdup(tmp);
- if (!dir_entry->path) {
- error("not enough memory to store path");
- ret = -ENOMEM;
- goto fail;
- }
- dir_entry->inum = cur_inum;
- list_add_tail(&dir_entry->list, &dir_head->list);
- } else if (S_ISREG(st.st_mode)) {
- ret = add_file_items(trans, root, &cur_inode,
- cur_inum, &st,
- cur_file->d_name);
- if (ret) {
- error("unable to add file items for %s: %d",
- cur_file->d_name, ret);
- goto fail;
- }
- } else if (S_ISLNK(st.st_mode)) {
- ret = add_symbolic_link(trans, root,
- cur_inum, cur_file->d_name);
- if (ret) {
- error("unable to add symlink for %s: %d",
- cur_file->d_name, ret);
- goto fail;
- }
- }
- }
-
- free_namelist(files, count);
- free(parent_dir_entry);
-
- index_cnt = 2;
-
- } while (!list_empty(&dir_head->list));
-
-out:
- return !!ret;
-fail:
- free_namelist(files, count);
-fail_no_files:
- free(parent_dir_entry);
- goto out;
-fail_no_dir:
- free(dir_entry);
- goto out;
-}
-
-static int create_chunks(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 num_of_meta_chunks,
- u64 size_of_data,
- struct mkfs_allocation *allocation)
-{
- struct btrfs_fs_info *fs_info = root->fs_info;
- u64 chunk_start;
- u64 chunk_size;
- u64 meta_type = BTRFS_BLOCK_GROUP_METADATA;
- u64 data_type = BTRFS_BLOCK_GROUP_DATA;
- u64 minimum_data_chunk_size = SZ_8M;
- u64 i;
- int ret;
-
- for (i = 0; i < num_of_meta_chunks; i++) {
- ret = btrfs_alloc_chunk(trans, fs_info,
- &chunk_start, &chunk_size, meta_type);
- if (ret)
- return ret;
- ret = btrfs_make_block_group(trans, fs_info, 0,
- meta_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- chunk_start, chunk_size);
- allocation->metadata += chunk_size;
- if (ret)
- return ret;
- set_extent_dirty(&root->fs_info->free_space_cache,
- chunk_start, chunk_start + chunk_size - 1);
- }
-
- if (size_of_data < minimum_data_chunk_size)
- size_of_data = minimum_data_chunk_size;
-
- ret = btrfs_alloc_data_chunk(trans, fs_info,
- &chunk_start, size_of_data, data_type, 0);
- if (ret)
- return ret;
- ret = btrfs_make_block_group(trans, fs_info, 0,
- data_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- chunk_start, size_of_data);
- allocation->data += size_of_data;
- if (ret)
- return ret;
- set_extent_dirty(&root->fs_info->free_space_cache,
- chunk_start, chunk_start + size_of_data - 1);
- return ret;
-}
-
-static int make_image(const char *source_dir, struct btrfs_root *root)
-{
- int ret;
- struct btrfs_trans_handle *trans;
- struct stat root_st;
- struct directory_name_entry dir_head;
- struct directory_name_entry *dir_entry = NULL;
-
- ret = lstat(source_dir, &root_st);
- if (ret) {
- error("unable to lstat %s: %s", source_dir, strerror(errno));
- ret = -errno;
- goto out;
- }
-
- INIT_LIST_HEAD(&dir_head.list);
-
- trans = btrfs_start_transaction(root, 1);
- BUG_ON(IS_ERR(trans));
- ret = traverse_directory(trans, root, source_dir, &dir_head);
- if (ret) {
- error("unable to traverse directory %s: %d", source_dir, ret);
- goto fail;
- }
- ret = btrfs_commit_transaction(trans, root);
- if (ret) {
- error("transaction commit failed: %d", ret);
- goto out;
- }
-
- if (verbose)
- printf("Making image is completed.\n");
- return 0;
-fail:
- /*
- * Since we don't have btrfs_abort_transaction() yet, uncommitted trans
- * will trigger a BUG_ON().
- *
- * However before mkfs is fully finished, the magic number is invalid,
- * so even we commit transaction here, the fs still can't be mounted.
- *
- * To do a graceful error out, here we commit transaction as a
- * workaround.
- * Since we have already hit some problem, the return value doesn't
- * matter now.
- */
- btrfs_commit_transaction(trans, root);
- while (!list_empty(&dir_head.list)) {
- dir_entry = list_entry(dir_head.list.next,
- struct directory_name_entry, list);
- list_del(&dir_entry->list);
- free(dir_entry);
- }
-out:
- return ret;
-}
-
-/*
- * This ignores symlinks with unreadable targets and subdirs that can't
- * be read. It's a best-effort to give a rough estimate of the size of
- * a subdir. It doesn't guarantee that prepopulating btrfs from this
- * tree won't still run out of space.
- */
-static u64 global_total_size;
-static u64 fs_block_size;
-static int ftw_add_entry_size(const char *fpath, const struct stat *st,
- int type)
-{
- if (type == FTW_F || type == FTW_D)
- global_total_size += round_up(st->st_size, fs_block_size);
-
- return 0;
-}
-
-static u64 size_sourcedir(const char *dir_name, u64 sectorsize,
- u64 *num_of_meta_chunks_ret, u64 *size_of_data_ret)
-{
- u64 dir_size = 0;
- u64 total_size = 0;
- int ret;
- u64 default_chunk_size = SZ_8M;
- u64 allocated_meta_size = SZ_8M;
- u64 allocated_total_size = 20 * SZ_1M; /* 20MB */
- u64 num_of_meta_chunks = 0;
- u64 num_of_data_chunks = 0;
- u64 num_of_allocated_meta_chunks =
- allocated_meta_size / default_chunk_size;
-
- global_total_size = 0;
- fs_block_size = sectorsize;
- ret = ftw(dir_name, ftw_add_entry_size, 10);
- dir_size = global_total_size;
- if (ret < 0) {
- error("ftw subdir walk of %s failed: %s", dir_name,
- strerror(errno));
- exit(1);
- }
-
- num_of_data_chunks = (dir_size + default_chunk_size - 1) /
- default_chunk_size;
-
- num_of_meta_chunks = (dir_size / 2) / default_chunk_size;
- if (((dir_size / 2) % default_chunk_size) != 0)
- num_of_meta_chunks++;
- if (num_of_meta_chunks <= num_of_allocated_meta_chunks)
- num_of_meta_chunks = 0;
- else
- num_of_meta_chunks -= num_of_allocated_meta_chunks;
-
- total_size = allocated_total_size +
- (num_of_data_chunks * default_chunk_size) +
- (num_of_meta_chunks * default_chunk_size);
-
- *num_of_meta_chunks_ret = num_of_meta_chunks;
- *size_of_data_ret = num_of_data_chunks * default_chunk_size;
- return total_size;
-}
-
static int zero_output_file(int out_fd, u64 size)
{
int loop_num;
@@ -1427,6 +672,38 @@ out:
return ret;
}
+/*
+ * Just update chunk allocation info, since --rootdir may allocate new
+ * chunks which is not updated in @allocation structure.
+ */
+static void update_chunk_allocation(struct btrfs_fs_info *fs_info,
+ struct mkfs_allocation *allocation)
+{
+ struct btrfs_block_group_cache *bg_cache;
+ const u64 mixed_flag = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA;
+ u64 search_start = 0;
+
+ allocation->mixed = 0;
+ allocation->data = 0;
+ allocation->metadata = 0;
+ allocation->system = 0;
+ while (1) {
+ bg_cache = btrfs_lookup_first_block_group(fs_info,
+ search_start);
+ if (!bg_cache)
+ break;
+ if ((bg_cache->flags & mixed_flag) == mixed_flag)
+ allocation->mixed += bg_cache->key.offset;
+ else if (bg_cache->flags & BTRFS_BLOCK_GROUP_DATA)
+ allocation->data += bg_cache->key.offset;
+ else if (bg_cache->flags & BTRFS_BLOCK_GROUP_METADATA)
+ allocation->metadata += bg_cache->key.offset;
+ else
+ allocation->system += bg_cache->key.offset;
+ search_start = bg_cache->key.objectid + bg_cache->key.offset;
+ }
+}
+
int main(int argc, char **argv)
{
char *file;
@@ -1445,7 +722,7 @@ int main(int argc, char **argv)
u32 stripesize = 4096;
int zero_end = 1;
int fd = -1;
- int ret;
+ int ret = 0;
int close_ret;
int i;
int mixed = 0;
@@ -1456,11 +733,11 @@ int main(int argc, char **argv)
int ssd = 0;
int force_overwrite = 0;
char *source_dir = NULL;
- int source_dir_set = 0;
- u64 num_of_meta_chunks = 0;
- u64 size_of_data = 0;
+ bool source_dir_set = false;
+ bool shrink_rootdir = false;
u64 source_dir_size = 0;
u64 min_dev_size;
+ u64 shrink_size;
int dev_cnt = 0;
int saved_optind;
char fs_uuid[BTRFS_UUID_UNPARSED_SIZE] = { 0 };
@@ -1470,6 +747,7 @@ int main(int argc, char **argv)
while(1) {
int c;
+ enum { GETOPT_VAL_SHRINK = 257 };
static const struct option long_options[] = {
{ "alloc-start", required_argument, NULL, 'A'},
{ "byte-count", required_argument, NULL, 'b' },
@@ -1487,6 +765,7 @@ int main(int argc, char **argv)
{ "features", required_argument, NULL, 'O' },
{ "uuid", required_argument, NULL, 'U' },
{ "quiet", 0, NULL, 'q' },
+ { "shrink", no_argument, NULL, GETOPT_VAL_SHRINK },
{ "help", no_argument, NULL, GETOPT_VAL_HELP },
{ NULL, 0, NULL, 0}
};
@@ -1554,7 +833,7 @@ int main(int argc, char **argv)
goto success;
case 'r':
source_dir = optarg;
- source_dir_set = 1;
+ source_dir_set = true;
break;
case 'U':
strncpy(fs_uuid, optarg,
@@ -1566,6 +845,9 @@ int main(int argc, char **argv)
case 'q':
verbose = 0;
break;
+ case GETOPT_VAL_SHRINK:
+ shrink_rootdir = true;
+ break;
case GETOPT_VAL_HELP:
default:
print_usage(c != GETOPT_VAL_HELP);
@@ -1588,6 +870,10 @@ int main(int argc, char **argv)
error("the option -r is limited to a single device");
goto error;
}
+ if (shrink_rootdir && !source_dir_set) {
+ error("the option --shrink must be used with --rootdir");
+ goto error;
+ }
if (*fs_uuid) {
uuid_t dummy_uuid;
@@ -1604,7 +890,9 @@ int main(int argc, char **argv)
while (dev_cnt-- > 0) {
file = argv[optind++];
- if (is_block_device(file) == 1)
+ if (source_dir_set && is_path_exist(file) == 0)
+ ret = 0;
+ else if (is_block_device(file) == 1)
ret = test_dev_for_mkfs(file, force_overwrite);
else
ret = test_status_for_mkfs(file, force_overwrite);
@@ -1677,6 +965,53 @@ int main(int argc, char **argv)
min_dev_size = btrfs_min_dev_size(nodesize, mixed, metadata_profile,
data_profile);
+ /*
+ * Enlarge the destination file or create a new one, using the size
+ * calculated from source dir.
+ *
+ * This must be done before minimal device size checks.
+ */
+ if (source_dir_set) {
+ int oflags = O_RDWR;
+ struct stat statbuf;
+
+ if (is_path_exist(file) == 0)
+ oflags |= O_CREAT;
+
+ fd = open(file, oflags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
+ S_IROTH);
+ if (fd < 0) {
+ error("unable to open %s: %m", file);
+ goto error;
+ }
+
+ ret = fstat(fd, &statbuf);
+ if (ret < 0) {
+ error("unable to stat %s: %m", file);
+ ret = -errno;
+ goto error;
+ }
+
+ /*
+ * Block_count not specified, use file/device size first.
+ * Or we will always use source_dir_size calculated for mkfs.
+ */
+ if (!block_count)
+ block_count = btrfs_device_size(fd, &statbuf);
+ source_dir_size = btrfs_mkfs_size_dir(source_dir, sectorsize,
+ min_dev_size, metadata_profile, data_profile);
+ if (block_count < source_dir_size)
+ block_count = source_dir_size;
+ ret = zero_output_file(fd, block_count);
+ if (ret) {
+ error("unable to zero the output file");
+ close(fd);
+ goto error;
+ }
+ /* our "device" is the new image file */
+ dev_block_count = block_count;
+ close(fd);
+ }
/* Check device/block_count after the nodesize is determined */
if (block_count && block_count < min_dev_size) {
error("size %llu is too small to make a usable filesystem",
@@ -1691,8 +1026,7 @@ int main(int argc, char **argv)
path = argv[i];
ret = test_minimum_size(path, min_dev_size);
if (ret < 0) {
- error("failed to check size for %s: %s",
- path, strerror(-ret));
+ error("failed to check size for %s: %m", path);
goto error;
}
if (ret > 0) {
@@ -1710,51 +1044,27 @@ int main(int argc, char **argv)
dev_cnt--;
- if (!source_dir_set) {
- /*
- * open without O_EXCL so that the problem should not
- * occur by the following processing.
- * (btrfs_register_one_device() fails if O_EXCL is on)
- */
- fd = open(file, O_RDWR);
- if (fd < 0) {
- error("unable to open %s: %s", file, strerror(errno));
- goto error;
- }
- ret = btrfs_prepare_device(fd, file, &dev_block_count,
- block_count,
- (zero_end ? PREP_DEVICE_ZERO_END : 0) |
- (discard ? PREP_DEVICE_DISCARD : 0) |
- (verbose ? PREP_DEVICE_VERBOSE : 0));
- if (ret) {
- goto error;
- }
- if (block_count && block_count > dev_block_count) {
- error("%s is smaller than requested size, expected %llu, found %llu",
- file,
- (unsigned long long)block_count,
- (unsigned long long)dev_block_count);
- goto error;
- }
- } else {
- fd = open(file, O_CREAT | O_RDWR,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
- if (fd < 0) {
- error("unable to open %s: %s", file, strerror(errno));
- goto error;
- }
-
- source_dir_size = size_sourcedir(source_dir, sectorsize,
- &num_of_meta_chunks, &size_of_data);
- if(block_count < source_dir_size)
- block_count = source_dir_size;
- ret = zero_output_file(fd, block_count);
- if (ret) {
- error("unable to zero the output file");
- goto error;
- }
- /* our "device" is the new image file */
- dev_block_count = block_count;
+ /*
+ * Open without O_EXCL so that the problem should not occur by the
+ * following operation in kernel:
+ * (btrfs_register_one_device() fails if O_EXCL is on)
+ */
+ fd = open(file, O_RDWR);
+ if (fd < 0) {
+ error("unable to open %s: %m", file);
+ goto error;
+ }
+ ret = btrfs_prepare_device(fd, file, &dev_block_count, block_count,
+ (zero_end ? PREP_DEVICE_ZERO_END : 0) |
+ (discard ? PREP_DEVICE_DISCARD : 0) |
+ (verbose ? PREP_DEVICE_VERBOSE : 0));
+ if (ret)
+ goto error;
+ if (block_count && block_count > dev_block_count) {
+ error("%s is smaller than requested size, expected %llu, found %llu",
+ file, (unsigned long long)block_count,
+ (unsigned long long)dev_block_count);
+ goto error;
}
/* To create the first block group and chunk 0 in make_btrfs */
@@ -1843,7 +1153,7 @@ int main(int argc, char **argv)
*/
fd = open(file, O_RDWR);
if (fd < 0) {
- error("unable to open %s: %s", file, strerror(errno));
+ error("unable to open %s: %m", file);
goto error;
}
ret = btrfs_device_already_in_root(root, fd,
@@ -1880,13 +1190,11 @@ int main(int argc, char **argv)
}
raid_groups:
- if (!source_dir_set) {
- ret = create_raid_groups(trans, root, data_profile,
- metadata_profile, mixed, &allocation);
- if (ret) {
- error("unable to create raid groups: %d", ret);
- goto out;
- }
+ ret = create_raid_groups(trans, root, data_profile,
+ metadata_profile, mixed, &allocation);
+ if (ret) {
+ error("unable to create raid groups: %d", ret);
+ goto out;
}
ret = create_tree(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID);
@@ -1901,28 +1209,6 @@ raid_groups:
goto out;
}
- if (source_dir_set) {
- trans = btrfs_start_transaction(root, 1);
- BUG_ON(IS_ERR(trans));
- ret = create_chunks(trans, root,
- num_of_meta_chunks, size_of_data,
- &allocation);
- if (ret) {
- error("unable to create chunks: %d", ret);
- goto out;
- }
- ret = btrfs_commit_transaction(trans, root);
- if (ret) {
- error("transaction commit failed: %d", ret);
- goto out;
- }
-
- ret = make_image(source_dir, root);
- if (ret) {
- error("error wihle filling filesystem: %d", ret);
- goto out;
- }
- }
ret = cleanup_temp_chunks(fs_info, &allocation, data_profile,
metadata_profile, metadata_profile);
if (ret < 0) {
@@ -1930,9 +1216,27 @@ raid_groups:
goto out;
}
+ if (source_dir_set) {
+ ret = btrfs_mkfs_fill_dir(source_dir, root, verbose);
+ if (ret) {
+ error("error wihle filling filesystem: %d", ret);
+ goto out;
+ }
+ if (shrink_rootdir) {
+ ret = btrfs_mkfs_shrink_fs(fs_info, &shrink_size,
+ shrink_rootdir);
+ if (ret < 0) {
+ error("error while shrinking filesystem: %d",
+ ret);
+ goto out;
+ }
+ }
+ }
+
if (verbose) {
char features_buf[64];
+ update_chunk_allocation(fs_info, &allocation);
printf("Label: %s\n", label);
printf("UUID: %s\n", mkfs_cfg.fs_uuid);
printf("Node size: %u\n", nodesize);
diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
new file mode 100644
index 00000000..e06b65ac
--- /dev/null
+++ b/mkfs/rootdir.c
@@ -0,0 +1,955 @@
+/*
+ * Copyright (C) 2017 SUSE. 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.
+ */
+
+#include "kerncompat.h"
+#include "androidcompat.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <linux/limits.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include "ctree.h"
+#include "volumes.h"
+#include "internal.h"
+#include "disk-io.h"
+#include "messages.h"
+#include "transaction.h"
+#include "utils.h"
+#include "mkfs/rootdir.h"
+#include "mkfs/common.h"
+#include "send-utils.h"
+
+static u32 fs_block_size;
+
+static u64 index_cnt = 2;
+
+/*
+ * Size estimate will be done using the following data:
+ * 1) Number of inodes
+ * Since we will later shrink the fs, over-estimate is completely fine here
+ * as long as our estimate ensures we can populate the image without ENOSPC.
+ * So we only record how many inodes there are, and account the maximum
+ * space for each inode.
+ *
+ * 2) Data space for each (regular) inode
+ * To estimate data chunk size.
+ * Don't care if it can fit as an inline extent.
+ * Always round them up to sectorsize.
+ */
+static u64 ftw_meta_nr_inode;
+static u64 ftw_data_size;
+
+static int add_directory_items(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ ino_t parent_inum, const char *name,
+ struct stat *st, int *dir_index_cnt)
+{
+ int ret;
+ int name_len;
+ struct btrfs_key location;
+ u8 filetype = 0;
+
+ name_len = strlen(name);
+
+ location.objectid = objectid;
+ location.offset = 0;
+ location.type = BTRFS_INODE_ITEM_KEY;
+
+ if (S_ISDIR(st->st_mode))
+ filetype = BTRFS_FT_DIR;
+ if (S_ISREG(st->st_mode))
+ filetype = BTRFS_FT_REG_FILE;
+ if (S_ISLNK(st->st_mode))
+ filetype = BTRFS_FT_SYMLINK;
+ if (S_ISSOCK(st->st_mode))
+ filetype = BTRFS_FT_SOCK;
+ if (S_ISCHR(st->st_mode))
+ filetype = BTRFS_FT_CHRDEV;
+ if (S_ISBLK(st->st_mode))
+ filetype = BTRFS_FT_BLKDEV;
+ if (S_ISFIFO(st->st_mode))
+ filetype = BTRFS_FT_FIFO;
+
+ ret = btrfs_insert_dir_item(trans, root, name, name_len,
+ parent_inum, &location,
+ filetype, index_cnt);
+ if (ret)
+ return ret;
+ ret = btrfs_insert_inode_ref(trans, root, name, name_len,
+ objectid, parent_inum, index_cnt);
+ *dir_index_cnt = index_cnt;
+ index_cnt++;
+
+ return ret;
+}
+
+static int fill_inode_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_inode_item *dst, struct stat *src)
+{
+ u64 blocks = 0;
+ u64 sectorsize = root->fs_info->sectorsize;
+
+ /*
+ * btrfs_inode_item has some reserved fields
+ * and represents on-disk inode entry, so
+ * zero everything to prevent information leak
+ */
+ memset(dst, 0, sizeof(*dst));
+
+ btrfs_set_stack_inode_generation(dst, trans->transid);
+ btrfs_set_stack_inode_size(dst, src->st_size);
+ btrfs_set_stack_inode_nbytes(dst, 0);
+ btrfs_set_stack_inode_block_group(dst, 0);
+ btrfs_set_stack_inode_nlink(dst, src->st_nlink);
+ btrfs_set_stack_inode_uid(dst, src->st_uid);
+ btrfs_set_stack_inode_gid(dst, src->st_gid);
+ btrfs_set_stack_inode_mode(dst, src->st_mode);
+ btrfs_set_stack_inode_rdev(dst, 0);
+ btrfs_set_stack_inode_flags(dst, 0);
+ btrfs_set_stack_timespec_sec(&dst->atime, src->st_atime);
+ btrfs_set_stack_timespec_nsec(&dst->atime, 0);
+ btrfs_set_stack_timespec_sec(&dst->ctime, src->st_ctime);
+ btrfs_set_stack_timespec_nsec(&dst->ctime, 0);
+ btrfs_set_stack_timespec_sec(&dst->mtime, src->st_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->st_mode)) {
+ btrfs_set_stack_inode_size(dst, 0);
+ btrfs_set_stack_inode_nlink(dst, 1);
+ }
+ if (S_ISREG(src->st_mode)) {
+ btrfs_set_stack_inode_size(dst, (u64)src->st_size);
+ if (src->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info))
+ btrfs_set_stack_inode_nbytes(dst, src->st_size);
+ else {
+ blocks = src->st_size / sectorsize;
+ if (src->st_size % sectorsize)
+ blocks += 1;
+ blocks *= sectorsize;
+ btrfs_set_stack_inode_nbytes(dst, blocks);
+ }
+ }
+ if (S_ISLNK(src->st_mode))
+ btrfs_set_stack_inode_nbytes(dst, src->st_size + 1);
+
+ return 0;
+}
+
+static int directory_select(const struct direct *entry)
+{
+ if (entry->d_name[0] == '.' &&
+ (entry->d_name[1] == 0 ||
+ (entry->d_name[1] == '.' && entry->d_name[2] == 0)))
+ return 0;
+ return 1;
+}
+
+static void free_namelist(struct direct **files, int count)
+{
+ int i;
+
+ if (count < 0)
+ return;
+
+ for (i = 0; i < count; ++i)
+ free(files[i]);
+ free(files);
+}
+
+static u64 calculate_dir_inode_size(const char *dirname)
+{
+ int count, i;
+ struct direct **files, *cur_file;
+ u64 dir_inode_size = 0;
+
+ count = scandir(dirname, &files, directory_select, NULL);
+
+ for (i = 0; i < count; i++) {
+ cur_file = files[i];
+ dir_inode_size += strlen(cur_file->d_name);
+ }
+
+ free_namelist(files, count);
+
+ dir_inode_size *= 2;
+ return dir_inode_size;
+}
+
+static int add_inode_items(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct stat *st, const char *name,
+ u64 self_objectid,
+ struct btrfs_inode_item *inode_ret)
+{
+ int ret;
+ struct btrfs_inode_item btrfs_inode;
+ u64 objectid;
+ u64 inode_size = 0;
+
+ fill_inode_item(trans, root, &btrfs_inode, st);
+ objectid = self_objectid;
+
+ if (S_ISDIR(st->st_mode)) {
+ inode_size = calculate_dir_inode_size(name);
+ btrfs_set_stack_inode_size(&btrfs_inode, inode_size);
+ }
+
+ ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
+
+ *inode_ret = btrfs_inode;
+ return ret;
+}
+
+static int add_xattr_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ const char *file_name)
+{
+ int ret;
+ int cur_name_len;
+ char xattr_list[XATTR_LIST_MAX];
+ char *cur_name;
+ char cur_value[XATTR_SIZE_MAX];
+ char delimiter = '\0';
+ char *next_location = xattr_list;
+
+ ret = llistxattr(file_name, xattr_list, XATTR_LIST_MAX);
+ if (ret < 0) {
+ if (errno == ENOTSUP)
+ return 0;
+ error("getting a list of xattr failed for %s: %s", file_name,
+ strerror(errno));
+ return ret;
+ }
+ if (ret == 0)
+ return ret;
+
+ cur_name = strtok(xattr_list, &delimiter);
+ while (cur_name != NULL) {
+ cur_name_len = strlen(cur_name);
+ next_location += cur_name_len + 1;
+
+ ret = getxattr(file_name, cur_name, cur_value, XATTR_SIZE_MAX);
+ if (ret < 0) {
+ if (errno == ENOTSUP)
+ return 0;
+ error("gettig a xattr value failed for %s attr %s: %s",
+ file_name, cur_name, strerror(errno));
+ return ret;
+ }
+
+ ret = btrfs_insert_xattr_item(trans, root, cur_name,
+ cur_name_len, cur_value,
+ ret, objectid);
+ if (ret) {
+ error("inserting a xattr item failed for %s: %s",
+ file_name, strerror(-ret));
+ }
+
+ cur_name = strtok(next_location, &delimiter);
+ }
+
+ return ret;
+}
+
+static int add_symbolic_link(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ u64 objectid, const char *path_name)
+{
+ int ret;
+ char buf[PATH_MAX];
+
+ ret = readlink(path_name, buf, sizeof(buf));
+ if (ret <= 0) {
+ error("readlink failed for %s: %s", path_name, strerror(errno));
+ goto fail;
+ }
+ if (ret >= sizeof(buf)) {
+ error("symlink too long for %s", path_name);
+ ret = -1;
+ goto fail;
+ }
+
+ buf[ret] = '\0'; /* readlink does not do it for us */
+ ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
+ buf, ret + 1);
+fail:
+ return ret;
+}
+
+static int add_file_items(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_inode_item *btrfs_inode, u64 objectid,
+ struct stat *st, const char *path_name)
+{
+ int ret = -1;
+ ssize_t ret_read;
+ u64 bytes_read = 0;
+ struct btrfs_key key;
+ int blocks;
+ u32 sectorsize = root->fs_info->sectorsize;
+ u64 first_block = 0;
+ u64 file_pos = 0;
+ u64 cur_bytes;
+ u64 total_bytes;
+ struct extent_buffer *eb = NULL;
+ int fd;
+
+ if (st->st_size == 0)
+ return 0;
+
+ fd = open(path_name, O_RDONLY);
+ if (fd == -1) {
+ error("cannot open %s: %s", path_name, strerror(errno));
+ return ret;
+ }
+
+ blocks = st->st_size / sectorsize;
+ if (st->st_size % sectorsize)
+ blocks += 1;
+
+ if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info)) {
+ char *buffer = malloc(st->st_size);
+
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ ret_read = pread64(fd, buffer, st->st_size, bytes_read);
+ if (ret_read == -1) {
+ error("cannot read %s at offset %llu length %llu: %s",
+ path_name, (unsigned long long)bytes_read,
+ (unsigned long long)st->st_size,
+ strerror(errno));
+ free(buffer);
+ goto end;
+ }
+
+ ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
+ buffer, st->st_size);
+ free(buffer);
+ goto end;
+ }
+
+ /* round up our st_size to the FS blocksize */
+ total_bytes = (u64)blocks * sectorsize;
+
+ /*
+ * do our IO in extent buffers so it can work
+ * against any raid type
+ */
+ eb = calloc(1, sizeof(*eb) + sectorsize);
+ if (!eb) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+again:
+
+ /*
+ * keep our extent size at 1MB max, this makes it easier to work inside
+ * the tiny block groups created during mkfs
+ */
+ cur_bytes = min(total_bytes, (u64)SZ_1M);
+ ret = btrfs_reserve_extent(trans, root, cur_bytes, 0, 0, (u64)-1,
+ &key, 1);
+ if (ret)
+ goto end;
+
+ first_block = key.objectid;
+ bytes_read = 0;
+
+ while (bytes_read < cur_bytes) {
+
+ memset(eb->data, 0, sectorsize);
+
+ ret_read = pread64(fd, eb->data, sectorsize, file_pos +
+ bytes_read);
+ if (ret_read == -1) {
+ error("cannot read %s at offset %llu length %llu: %s",
+ path_name,
+ (unsigned long long)file_pos + bytes_read,
+ (unsigned long long)sectorsize,
+ strerror(errno));
+ goto end;
+ }
+
+ eb->start = first_block + bytes_read;
+ eb->len = sectorsize;
+
+ /*
+ * we're doing the csum before we record the extent, but
+ * that's ok
+ */
+ ret = btrfs_csum_file_block(trans, root->fs_info->csum_root,
+ first_block + bytes_read + sectorsize,
+ first_block + bytes_read,
+ eb->data, sectorsize);
+ if (ret)
+ goto end;
+
+ ret = write_and_map_eb(root->fs_info, eb);
+ if (ret) {
+ error("failed to write %s", path_name);
+ goto end;
+ }
+
+ bytes_read += sectorsize;
+ }
+
+ if (bytes_read) {
+ ret = btrfs_record_file_extent(trans, root, objectid,
+ btrfs_inode, file_pos, first_block, cur_bytes);
+ if (ret)
+ goto end;
+
+ }
+
+ file_pos += cur_bytes;
+ total_bytes -= cur_bytes;
+
+ if (total_bytes)
+ goto again;
+
+end:
+ free(eb);
+ close(fd);
+ return ret;
+}
+
+static int traverse_directory(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, const char *dir_name,
+ struct directory_name_entry *dir_head)
+{
+ int ret = 0;
+
+ struct btrfs_inode_item cur_inode;
+ struct btrfs_inode_item *inode_item;
+ int count, i, dir_index_cnt;
+ struct direct **files;
+ struct stat st;
+ struct directory_name_entry *dir_entry, *parent_dir_entry;
+ struct direct *cur_file;
+ ino_t parent_inum, cur_inum;
+ ino_t highest_inum = 0;
+ const char *parent_dir_name;
+ struct btrfs_path path;
+ struct extent_buffer *leaf;
+ struct btrfs_key root_dir_key;
+ u64 root_dir_inode_size = 0;
+
+ /* Add list for source directory */
+ dir_entry = malloc(sizeof(struct directory_name_entry));
+ if (!dir_entry)
+ return -ENOMEM;
+ dir_entry->dir_name = dir_name;
+ dir_entry->path = realpath(dir_name, NULL);
+ if (!dir_entry->path) {
+ error("realpath failed for %s: %s", dir_name, strerror(errno));
+ ret = -1;
+ goto fail_no_dir;
+ }
+
+ parent_inum = highest_inum + BTRFS_FIRST_FREE_OBJECTID;
+ dir_entry->inum = parent_inum;
+ list_add_tail(&dir_entry->list, &dir_head->list);
+
+ btrfs_init_path(&path);
+
+ root_dir_key.objectid = btrfs_root_dirid(&root->root_item);
+ root_dir_key.offset = 0;
+ root_dir_key.type = BTRFS_INODE_ITEM_KEY;
+ ret = btrfs_lookup_inode(trans, root, &path, &root_dir_key, 1);
+ if (ret) {
+ error("failed to lookup root dir: %d", ret);
+ goto fail_no_dir;
+ }
+
+ leaf = path.nodes[0];
+ inode_item = btrfs_item_ptr(leaf, path.slots[0],
+ struct btrfs_inode_item);
+
+ root_dir_inode_size = calculate_dir_inode_size(dir_name);
+ btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size);
+ btrfs_mark_buffer_dirty(leaf);
+
+ btrfs_release_path(&path);
+
+ do {
+ parent_dir_entry = list_entry(dir_head->list.next,
+ struct directory_name_entry,
+ list);
+ list_del(&parent_dir_entry->list);
+
+ parent_inum = parent_dir_entry->inum;
+ parent_dir_name = parent_dir_entry->dir_name;
+ if (chdir(parent_dir_entry->path)) {
+ error("chdir failed for %s: %s",
+ parent_dir_name, strerror(errno));
+ ret = -1;
+ goto fail_no_files;
+ }
+
+ count = scandir(parent_dir_entry->path, &files,
+ directory_select, NULL);
+ if (count == -1) {
+ error("scandir failed for %s: %s",
+ parent_dir_name, strerror(errno));
+ ret = -1;
+ goto fail;
+ }
+
+ for (i = 0; i < count; i++) {
+ cur_file = files[i];
+
+ if (lstat(cur_file->d_name, &st) == -1) {
+ error("lstat failed for %s: %s",
+ cur_file->d_name, strerror(errno));
+ ret = -1;
+ goto fail;
+ }
+
+ cur_inum = st.st_ino;
+ ret = add_directory_items(trans, root,
+ cur_inum, parent_inum,
+ cur_file->d_name,
+ &st, &dir_index_cnt);
+ if (ret) {
+ error("unable to add directory items for %s: %d",
+ cur_file->d_name, ret);
+ goto fail;
+ }
+
+ ret = add_inode_items(trans, root, &st,
+ cur_file->d_name, cur_inum,
+ &cur_inode);
+ if (ret == -EEXIST) {
+ if (st.st_nlink <= 1) {
+ error(
+ "item %s already exists but has wrong st_nlink %lu <= 1",
+ cur_file->d_name,
+ (unsigned long)st.st_nlink);
+ goto fail;
+ }
+ continue;
+ }
+ if (ret) {
+ error("unable to add inode items for %s: %d",
+ cur_file->d_name, ret);
+ goto fail;
+ }
+
+ ret = add_xattr_item(trans, root,
+ cur_inum, cur_file->d_name);
+ if (ret) {
+ error("unable to add xattr items for %s: %d",
+ cur_file->d_name, ret);
+ if (ret != -ENOTSUP)
+ goto fail;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ char tmp[PATH_MAX];
+
+ dir_entry = malloc(sizeof(*dir_entry));
+ if (!dir_entry) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ dir_entry->dir_name = cur_file->d_name;
+ if (path_cat_out(tmp, parent_dir_entry->path,
+ cur_file->d_name)) {
+ error("invalid path: %s/%s",
+ parent_dir_entry->path,
+ cur_file->d_name);
+ ret = -EINVAL;
+ goto fail;
+ }
+ dir_entry->path = strdup(tmp);
+ if (!dir_entry->path) {
+ error("not enough memory to store path");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ dir_entry->inum = cur_inum;
+ list_add_tail(&dir_entry->list,
+ &dir_head->list);
+ } else if (S_ISREG(st.st_mode)) {
+ ret = add_file_items(trans, root, &cur_inode,
+ cur_inum, &st,
+ cur_file->d_name);
+ if (ret) {
+ error("unable to add file items for %s: %d",
+ cur_file->d_name, ret);
+ goto fail;
+ }
+ } else if (S_ISLNK(st.st_mode)) {
+ ret = add_symbolic_link(trans, root,
+ cur_inum, cur_file->d_name);
+ if (ret) {
+ error("unable to add symlink for %s: %d",
+ cur_file->d_name, ret);
+ goto fail;
+ }
+ }
+ }
+
+ free_namelist(files, count);
+ free(parent_dir_entry->path);
+ free(parent_dir_entry);
+
+ index_cnt = 2;
+
+ } while (!list_empty(&dir_head->list));
+
+out:
+ return !!ret;
+fail:
+ free_namelist(files, count);
+fail_no_files:
+ free(parent_dir_entry);
+ goto out;
+fail_no_dir:
+ free(dir_entry);
+ goto out;
+}
+
+int btrfs_mkfs_fill_dir(const char *source_dir, struct btrfs_root *root,
+ bool verbose)
+{
+ int ret;
+ struct btrfs_trans_handle *trans;
+ struct stat root_st;
+ struct directory_name_entry dir_head;
+ struct directory_name_entry *dir_entry = NULL;
+
+ ret = lstat(source_dir, &root_st);
+ if (ret) {
+ error("unable to lstat %s: %s", source_dir, strerror(errno));
+ ret = -errno;
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&dir_head.list);
+
+ trans = btrfs_start_transaction(root, 1);
+ BUG_ON(IS_ERR(trans));
+ ret = traverse_directory(trans, root, source_dir, &dir_head);
+ if (ret) {
+ error("unable to traverse directory %s: %d", source_dir, ret);
+ goto fail;
+ }
+ ret = btrfs_commit_transaction(trans, root);
+ if (ret) {
+ error("transaction commit failed: %d", ret);
+ goto out;
+ }
+
+ if (verbose)
+ printf("Making image is completed.\n");
+ return 0;
+fail:
+ /*
+ * Since we don't have btrfs_abort_transaction() yet, uncommitted trans
+ * will trigger a BUG_ON().
+ *
+ * However before mkfs is fully finished, the magic number is invalid,
+ * so even we commit transaction here, the fs still can't be mounted.
+ *
+ * To do a graceful error out, here we commit transaction as a
+ * workaround.
+ * Since we have already hit some problem, the return value doesn't
+ * matter now.
+ */
+ btrfs_commit_transaction(trans, root);
+ while (!list_empty(&dir_head.list)) {
+ dir_entry = list_entry(dir_head.list.next,
+ struct directory_name_entry, list);
+ list_del(&dir_entry->list);
+ free(dir_entry->path);
+ free(dir_entry);
+ }
+out:
+ return ret;
+}
+
+static int ftw_add_entry_size(const char *fpath, const struct stat *st,
+ int type)
+{
+ /*
+ * Failed to read the directory, mostly due to EPERM. Abort ASAP, so
+ * we don't need to populate the fs.
+ */
+ if (type == FTW_DNR || type == FTW_NS)
+ return -EPERM;
+
+ if (S_ISREG(st->st_mode))
+ ftw_data_size += round_up(st->st_size, fs_block_size);
+ ftw_meta_nr_inode++;
+
+ return 0;
+}
+
+u64 btrfs_mkfs_size_dir(const char *dir_name, u32 sectorsize, u64 min_dev_size,
+ u64 meta_profile, u64 data_profile)
+{
+ u64 total_size = 0;
+ int ret;
+
+ u64 meta_size = 0; /* Based on @ftw_meta_nr_inode */
+ u64 meta_chunk_size = 0; /* Based on @meta_size */
+ u64 data_chunk_size = 0; /* Based on @ftw_data_size */
+
+ u64 meta_threshold = SZ_8M;
+ u64 data_threshold = SZ_8M;
+
+ float data_multipler = 1;
+ float meta_multipler = 1;
+
+ fs_block_size = sectorsize;
+ ftw_data_size = 0;
+ ftw_meta_nr_inode = 0;
+ ret = ftw(dir_name, ftw_add_entry_size, 10);
+ if (ret < 0) {
+ error("ftw subdir walk of %s failed: %s", dir_name,
+ strerror(errno));
+ exit(1);
+ }
+
+
+ /*
+ * Maximum metadata useage for every inode, which will be PATH_MAX
+ * for the following items:
+ * 1) DIR_ITEM
+ * 2) DIR_INDEX
+ * 3) INODE_REF
+ *
+ * Plus possible inline extent size, which is sectorsize.
+ *
+ * And finally, allow metadata usage to increase with data size.
+ * Follow the old kernel 8:1 data:meta ratio.
+ * This is especially important for --rootdir, as the file extent size
+ * upper limit is 1M, instead of 128M in kernel.
+ * This can bump meta usage easily.
+ */
+ meta_size = ftw_meta_nr_inode * (PATH_MAX * 3 + sectorsize) +
+ ftw_data_size / 8;
+
+ /* Minimal chunk size from btrfs_alloc_chunk(). */
+ if (meta_profile & BTRFS_BLOCK_GROUP_DUP) {
+ meta_threshold = SZ_32M;
+ meta_multipler = 2;
+ }
+ if (data_profile & BTRFS_BLOCK_GROUP_DUP) {
+ data_threshold = SZ_64M;
+ data_multipler = 2;
+ }
+
+ /*
+ * Only when the usage is larger than the minimal chunk size (threshold)
+ * we need to allocate new chunk, or the initial chunk in the image is
+ * large enough.
+ */
+ if (meta_size > meta_threshold)
+ meta_chunk_size = (round_up(meta_size, meta_threshold) -
+ meta_threshold) * meta_multipler;
+ if (ftw_data_size > data_threshold)
+ data_chunk_size = (round_up(ftw_data_size, data_threshold) -
+ data_threshold) * data_multipler;
+
+ total_size = data_chunk_size + meta_chunk_size + min_dev_size;
+ return total_size;
+}
+
+/*
+ * Get the end position of the last device extent for given @devid;
+ * @size_ret is exclsuive (means it should be aligned to sectorsize)
+ */
+static int get_device_extent_end(struct btrfs_fs_info *fs_info,
+ u64 devid, u64 *size_ret)
+{
+ struct btrfs_root *dev_root = fs_info->dev_root;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct btrfs_dev_extent *de;
+ int ret;
+
+ key.objectid = devid;
+ key.type = BTRFS_DEV_EXTENT_KEY;
+ key.offset = (u64)-1;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, dev_root, &key, &path, 0, 0);
+ /* Not really possible */
+ BUG_ON(ret == 0);
+
+ ret = btrfs_previous_item(dev_root, &path, devid, BTRFS_DEV_EXTENT_KEY);
+ if (ret < 0)
+ goto out;
+
+ /* No dev_extent at all, not really possible for rootdir case */
+ if (ret > 0) {
+ *size_ret = 0;
+ ret = -EUCLEAN;
+ goto out;
+ }
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ de = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_dev_extent);
+ *size_ret = key.offset + btrfs_dev_extent_length(path.nodes[0], de);
+out:
+ btrfs_release_path(&path);
+
+ return ret;
+}
+
+/*
+ * Set device size to @new_size.
+ *
+ * Only used for --rootdir option.
+ * We will need to reset the following values:
+ * 1) dev item in chunk tree
+ * 2) super->dev_item
+ * 3) super->total_bytes
+ */
+static int set_device_size(struct btrfs_fs_info *fs_info,
+ struct btrfs_device *device, u64 new_size)
+{
+ struct btrfs_root *chunk_root = fs_info->chunk_root;
+ struct btrfs_trans_handle *trans;
+ struct btrfs_dev_item *di;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ int ret;
+
+ /*
+ * Update in-meory device->total_bytes, so that at trans commit time,
+ * super->dev_item will also get updated
+ */
+ device->total_bytes = new_size;
+ btrfs_init_path(&path);
+
+ /* Update device item in chunk tree */
+ trans = btrfs_start_transaction(chunk_root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ error("failed to start transaction: %d (%s)", ret,
+ strerror(-ret));
+ return ret;
+ }
+ key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
+ key.type = BTRFS_DEV_ITEM_KEY;
+ key.offset = device->devid;
+
+ ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1);
+ if (ret < 0)
+ goto err;
+ if (ret > 0)
+ ret = -ENOENT;
+ di = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_dev_item);
+ btrfs_set_device_total_bytes(path.nodes[0], di, new_size);
+ btrfs_mark_buffer_dirty(path.nodes[0]);
+
+ /*
+ * Update super->total_bytes, since it's only used for --rootdir,
+ * there is only one device, just use the @new_size.
+ */
+ btrfs_set_super_total_bytes(fs_info->super_copy, new_size);
+
+ /*
+ * Commit transaction to reflect the updated super->total_bytes and
+ * super->dev_item
+ */
+ ret = btrfs_commit_transaction(trans, chunk_root);
+ if (ret < 0)
+ error("failed to commit current transaction: %d (%s)",
+ ret, strerror(-ret));
+ btrfs_release_path(&path);
+ return ret;
+
+err:
+ btrfs_release_path(&path);
+ /*
+ * Committing the transaction here won't cause problems since the fs
+ * still has an invalid magic number, and something wrong already
+ * happened, we don't care the return value anyway.
+ */
+ btrfs_commit_transaction(trans, chunk_root);
+ return ret;
+}
+
+int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret,
+ bool shrink_file_size)
+{
+ u64 new_size;
+ struct btrfs_device *device;
+ struct list_head *cur;
+ struct stat64 file_stat;
+ int nr_devs = 0;
+ int ret;
+
+ list_for_each(cur, &fs_info->fs_devices->devices)
+ nr_devs++;
+
+ if (nr_devs > 1) {
+ error("cannot shrink fs with more than 1 device");
+ return -ENOTTY;
+ }
+
+ ret = get_device_extent_end(fs_info, 1, &new_size);
+ if (ret < 0) {
+ error("failed to get minimal device size: %d (%s)",
+ ret, strerror(-ret));
+ return ret;
+ }
+
+ BUG_ON(!IS_ALIGNED(new_size, fs_info->sectorsize));
+
+ device = list_entry(fs_info->fs_devices->devices.next,
+ struct btrfs_device, dev_list);
+ ret = set_device_size(fs_info, device, new_size);
+ if (ret < 0)
+ return ret;
+ if (new_size_ret)
+ *new_size_ret = new_size;
+
+ if (shrink_file_size) {
+ ret = fstat64(device->fd, &file_stat);
+ if (ret < 0) {
+ error("failed to stat devid %llu: %s", device->devid,
+ strerror(errno));
+ return ret;
+ }
+ if (!S_ISREG(file_stat.st_mode))
+ return ret;
+ ret = ftruncate64(device->fd, new_size);
+ if (ret < 0) {
+ error("failed to truncate device file of devid %llu: %s",
+ device->devid, strerror(errno));
+ return ret;
+ }
+ }
+ return ret;
+}
diff --git a/mkfs/rootdir.h b/mkfs/rootdir.h
new file mode 100644
index 00000000..f06c7dd1
--- /dev/null
+++ b/mkfs/rootdir.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/*
+ * Defines and functions declarations for mkfs --rootdir
+ */
+
+#ifndef __BTRFS_MKFS_ROOTDIR_H__
+#define __BTRFS_MKFS_ROOTDIR_H__
+
+#include "kernel-lib/list.h"
+
+struct directory_name_entry {
+ const char *dir_name;
+ char *path;
+ ino_t inum;
+ struct list_head list;
+};
+
+int btrfs_mkfs_fill_dir(const char *source_dir, struct btrfs_root *root,
+ bool verbose);
+u64 btrfs_mkfs_size_dir(const char *dir_name, u32 sectorsize, u64 min_dev_size,
+ u64 meta_profile, u64 data_profile);
+int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret,
+ bool shrink_file_size);
+
+#endif
diff --git a/print-tree.c b/print-tree.c
index d3fa8621..45350fea 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -919,8 +919,8 @@ static void print_inode_item(struct extent_buffer *eb,
btrfs_inode_uid(eb, ii),
btrfs_inode_gid(eb, ii),
(unsigned long long)btrfs_inode_rdev(eb,ii),
- (unsigned long long)btrfs_inode_flags(eb,ii),
(unsigned long long)btrfs_inode_sequence(eb, ii),
+ (unsigned long long)btrfs_inode_flags(eb,ii),
flags_str);
print_timespec(eb, btrfs_inode_atime(ii), "\t\tatime ", "\n");
print_timespec(eb, btrfs_inode_ctime(ii), "\t\tctime ", "\n");
@@ -1359,7 +1359,7 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol
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,
+ (u32)BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) - nr,
(unsigned long long)btrfs_header_generation(eb),
(unsigned long long)btrfs_header_owner(eb));
print_uuids(eb);
diff --git a/qgroup.c b/qgroup.c
index 156825fd..11659e83 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -1046,8 +1046,10 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
struct btrfs_ioctl_search_header *sh;
unsigned long off = 0;
unsigned int i;
+ struct btrfs_qgroup_status_item *si;
struct btrfs_qgroup_info_item *info;
struct btrfs_qgroup_limit_item *limit;
+ u64 flags;
u64 qgroupid;
u64 qgroupid1;
@@ -1065,8 +1067,18 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
while (1) {
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- if (ret < 0)
- return -errno;
+ if (ret < 0) {
+ if (errno == ENOENT) {
+ error("can't list qgroups: quotas not enabled");
+ ret = -ENOTTY;
+ } else {
+ error("can't list qgroups: %s",
+ strerror(errno));
+ ret = -errno;
+ }
+
+ break;
+ }
/* the ioctl returns the number of item it found in nr_items */
if (sk->nr_items == 0)
@@ -1082,44 +1094,47 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
off);
off += sizeof(*sh);
- if (btrfs_search_header_type(sh)
- == BTRFS_QGROUP_STATUS_KEY) {
- struct btrfs_qgroup_status_item *si;
- u64 flags;
-
+ switch (btrfs_search_header_type(sh)) {
+ case BTRFS_QGROUP_STATUS_KEY:
si = (struct btrfs_qgroup_status_item *)
(args.buf + off);
flags = btrfs_stack_qgroup_status_flags(si);
+
print_status_flag_warning(flags);
- } else if (btrfs_search_header_type(sh)
- == BTRFS_QGROUP_INFO_KEY) {
+ break;
+ case BTRFS_QGROUP_INFO_KEY:
qgroupid = btrfs_search_header_offset(sh);
info = (struct btrfs_qgroup_info_item *)
(args.buf + off);
- update_qgroup_info(qgroup_lookup, qgroupid,
- info);
- } else if (btrfs_search_header_type(sh)
- == BTRFS_QGROUP_LIMIT_KEY) {
+ ret = update_qgroup_info(qgroup_lookup,
+ qgroupid, info);
+ break;
+ case BTRFS_QGROUP_LIMIT_KEY:
qgroupid = btrfs_search_header_offset(sh);
limit = (struct btrfs_qgroup_limit_item *)
(args.buf + off);
- update_qgroup_limit(qgroup_lookup, qgroupid,
- limit);
- } else if (btrfs_search_header_type(sh)
- == BTRFS_QGROUP_RELATION_KEY) {
+ ret = update_qgroup_limit(qgroup_lookup,
+ qgroupid, limit);
+ break;
+ case BTRFS_QGROUP_RELATION_KEY:
qgroupid = btrfs_search_header_offset(sh);
qgroupid1 = btrfs_search_header_objectid(sh);
if (qgroupid < qgroupid1)
- goto skip;
+ break;
+
+ ret = update_qgroup_relation(qgroup_lookup,
+ qgroupid, qgroupid1);
+ break;
+ default:
+ return ret;
+ }
+
+ if (ret)
+ return ret;
- update_qgroup_relation(qgroup_lookup, qgroupid,
- qgroupid1);
- } else
- goto done;
-skip:
off += btrfs_search_header_len(sh);
/*
@@ -1141,7 +1156,6 @@ skip:
break;
}
-done:
return ret;
}
@@ -1178,8 +1192,6 @@ int btrfs_show_qgroups(int fd,
print_all_qgroups(&sort_tree);
__free_all_qgroups(&qgroup_lookup);
- free(filter_set);
- free(comp_set);
return ret;
}
diff --git a/quick-test.c b/quick-test.c
index b1e7999d..5da47c32 100644
--- a/quick-test.c
+++ b/quick-test.c
@@ -110,7 +110,7 @@ int main(int ac, char **av) {
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) -
+ (unsigned long)BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) -
btrfs_header_nritems(root->node));
printf("all searches good, deleting some items\n");
i = 0;
diff --git a/send-utils.c b/send-utils.c
index 384cc5b7..b5289e76 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -83,8 +83,7 @@ static int btrfs_read_root_item_raw(int mnt_fd, u64 root_id, size_t buf_len,
ret = ioctl(mnt_fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0) {
fprintf(stderr,
- "ERROR: can't perform the search - %s\n",
- strerror(errno));
+ "ERROR: can't perform the search - %m\n");
return 0;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -267,8 +266,8 @@ static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_arg);
if (ret < 0) {
fprintf(stderr,
- "ioctl(BTRFS_IOC_TREE_SEARCH, subvol_id %llu) ret=%d, error: %s\n",
- (unsigned long long)subvol_id, ret, strerror(errno));
+ "ioctl(BTRFS_IOC_TREE_SEARCH, subvol_id %llu) ret=%d, error: %m\n",
+ (unsigned long long)subvol_id, ret);
return ret;
}
@@ -306,8 +305,8 @@ static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_lookup_arg);
if (ret < 0) {
fprintf(stderr,
- "ioctl(BTRFS_IOC_INO_LOOKUP) ret=%d, error: %s\n",
- ret, strerror(errno));
+ "ioctl(BTRFS_IOC_INO_LOOKUP) ret=%d, error: %m\n",
+ ret);
return ret;
}
@@ -586,8 +585,7 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
ret = is_uuid_tree_supported(mnt_fd);
if (ret < 0) {
fprintf(stderr,
- "ERROR: check if we support uuid tree fails - %s\n",
- strerror(errno));
+ "ERROR: check if we support uuid tree fails - %m\n");
return ret;
} else if (ret) {
/* uuid tree is supported */
@@ -608,8 +606,7 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
while (1) {
ret = ioctl(mnt_fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0) {
- fprintf(stderr, "ERROR: can't perform the search - %s\n",
- strerror(errno));
+ fprintf(stderr, "ERROR: can't perform the search - %m\n");
return ret;
}
if (sk->nr_items == 0)
diff --git a/super-recover.c b/super-recover.c
index 6b80416f..880fd771 100644
--- a/super-recover.c
+++ b/super-recover.c
@@ -136,7 +136,7 @@ read_dev_supers(char *filename, struct btrfs_recover_superblock *recover)
max_gen = btrfs_super_generation(sb);
if (max_gen > recover->max_generation)
recover->max_generation = max_gen;
- } else if (ret == -EIO){
+ } else if (ret != -ENOENT){
/*
* Skip superblock which doesn't exist, only adds
* really corrupted superblock
@@ -177,28 +177,6 @@ static int read_fs_supers(struct btrfs_recover_superblock *recover)
return 0;
}
-static struct super_block_record *recover_get_good_super(
- struct btrfs_recover_superblock *recover)
-{
- struct super_block_record *record;
- record = list_entry(recover->good_supers.next,
- struct super_block_record, list);
- return record;
-}
-
-static void print_all_devices(struct list_head *devices)
-{
- struct btrfs_device *dev;
-
- printf("All Devices:\n");
- list_for_each_entry(dev, devices, dev_list) {
- printf("\t");
- printf("Device: id = %llu, name = %s\n",
- dev->devid, dev->name);
- }
- printf("\n");
-}
-
static void print_super_info(struct super_block_record *record)
{
printf("\t\tdevice name = %s\n", record->device_name);
@@ -293,7 +271,9 @@ int btrfs_recover_superblocks(const char *dname,
goto no_recover;
}
}
- record = recover_get_good_super(&recover);
+ record = list_first_entry(&recover.good_supers,
+ struct super_block_record, list);
+
root = open_ctree(record->device_name, record->bytenr,
OPEN_CTREE_RECOVER_SUPER | OPEN_CTREE_WRITES);
if (!root) {
diff --git a/tests/README.md b/tests/README.md
index 04d2ce2a..d4b80da1 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -45,46 +45,51 @@ $ make TEST=001\* test-fsck
$ TEST=001\* ./fsck-tests.sh
```
-will run the first test in fsck-tests subdirectory.
+will run the first test in fsck-tests subdirectory. If the test directories
+follow a good naming scheme, it's possible to select a subset eg. like the
+convert tests for ext[234] filesystems.
## Test structure
-*tests/fsck-tests/:*
+*tests/fsck-tests/*
- * tests targeted at bugs that are fixable by fsck
+ * tests targeted at bugs that are fixable by fsck, the test directory can
+ contain images that will get fixed, or a custom script `./test.sh` that
+ will be run if present
-*tests/convert-tests/:*
+*tests/convert-tests/*
- * coverage tests of ext2/3/4 and btrfs-convert options
+ * coverage tests of ext2/3/4 or reiserfs and btrfs-convert options
-*tests/fuzz-tests/:*
+*tests/fuzz-tests/*
* collection of fuzzed or crafted images
* tests that are supposed to run various utilities on the images and not
crash
-*tests/cli-tests/:*
+*tests/cli-tests/*
* tests for command line interface, option coverage, weird option combinations that should not work
* not necessary to do any functional testing, could be rather lightweight
* functional tests should go to to other test dirs
* the driver script will only execute `./test.sh` in the test directory
-*tests/misc-tests/:*
+*tests/misc-tests/*
* anything that does not fit to the above, the test driver script will only
execute `./test.sh` in the test directory
-*tests/common, tests/common.convert:*
+*tests/common, tests/common.convert*
- * script with shell helpers, separated by functionality
+ * scripts with shell helpers, separated by functionality
-*tests/test.img:*
+*tests/test.img*
- * default testing image, the file is never deleted by the scripts but
- truncated to 0 bytes, so it keeps it's permissions. It's eg. possible to
- host it on NFS, make it `chmod a+w` for root.
+ * default testing image, available as `TEST_DEV` variable, the file is never
+ deleted by the scripts but truncated to 0 bytes, so it keeps it's
+ permissions. It's eg. possible to host it on NFS, make it `chmod a+w` for
+ root.
## Other tuning, environment variables
@@ -125,10 +130,10 @@ Multiple values can be separated by `,`.
### Permissions
-Some commands require root privileges (to mount/umount, access loop devices).
-It is assumed that `sudo` will work in some way (no password, password asked
-and cached). Note that instrumentation is not applied in this case, for safety
-reasons. You need to modify the test script instead.
+Some commands require root privileges (to mount/umount, access loop devices or
+call privileged ioctls). It is assumed that `sudo` will work in some way (no
+password, password asked and cached). Note that instrumentation is not applied
+in this case, for safety reasons. You need to modify the test script instead.
### Cleanup
@@ -143,13 +148,14 @@ the loop devices as they are managed on a per-test basis.
### Prototyping tests, quick tests
There's a script `test-console.sh` that will run shell commands in a loop and
-logs the output with the testing environment set up.
+logs the output with the testing environment set up. It sources the common
+helper scripts so the shell functions are available.
### Runtime dependencies
The tests use some common system utilities like `find`, `rm`, `dd`. Additionally,
specific tests need the following packages installed: `acl`, `attr`,
-`e2fsprogs`, `reiserfsprogs`
+`e2fsprogs`, `reiserfsprogs`.
## New test
@@ -158,13 +164,15 @@ specific tests need the following packages installed: `acl`, `attr`,
an easy start copy an existing `test.sh` script from some test that might be
close to the purpose of your new test. The environment setup includes the
common scripts and/or prepares the test devices. Other scripts contain examples
-how to do mkfs, mount, unmount, check, etc.
+how to do mkfs, mount, unmount, check, loop device management etc.
2. Use the highest unused number in the sequence, write a short descriptive title
and join by dashes `-`. This will become the directory name, eg. `012-subvolume-sync-must-wait`.
3. Write a short description of the bug and how it's tested to the comment at the
-begining of `test.sh`. You don't need to add the file to git yet.
+begining of `test.sh`. You don't need to add the file to git yet. Don't forget
+to make the file executable, otherwise it's not going to be executed by the
+infrastructure.
4. Write the test commands, comment anything that's not obvious.
@@ -178,10 +186,12 @@ $ TEST=012\* ./misc-tests.sh # from tests/
fixed the bug (or both). Subject line of the shall mention the name of the
new directory for ease of search, eg. `btrfs-progs: tests: add 012-subvolume-sync-must-wait`
+7. A commit that fixes a bug should be applied before the test that verifies
+ the fix. This is to keep the git history bisectable.
### Crafted/fuzzed images
-Images that are create by fuzzing or specially crafted to trigger some error
+Images that are created by fuzzing or specially crafted to trigger some error
conditions should be added to the directory *fuzz-tests/images*, accompanied by
a textual description of the source (bugzilla, mail), the reporter, brief
description of the problem or the stack trace.
@@ -191,6 +201,31 @@ the fuzz tests always succeed when run on random checked out. This helps
bisectability.
+# Exported testsuite
+
+The tests are typically run from git on binaries built from the git sources. It
+is possible to extract only the testsuite files and run it independently. Use
+
+```shell
+$ make testsuite
+```
+
+This will gather scripts and generate `tests/btrfs-progs-tests.tar.gz`. The
+files inside the tar are in the top level directory, make sure you extract
+the contents to an empty directory. From there you can start the tests as
+described above (the non-make variant).
+
+By default the binaries found in `$PATH` are used, this will normally mean the
+system binaries. You can also override the `$TOP` shell variable and this
+path will be used as prefix for all btrfs binaries inside the tests.
+
+There are some utilities that are not distributed but are necessary for the
+tests. They are in the top level directory of the testsuite and their path
+cannot be set.
+
+The tests assume write acesss to their directories.
+
+
# Coding style, best practices
## do
@@ -206,8 +241,9 @@ bisectability.
always built when the tests are started through make
* use functions instead of repeating code
* generic helpers could be factored to the `common` script
-* cleanup after successful test
-* use common helpers and variables
+* cleanup files an intermediate state (mount, loop devices, device mapper
+ devices) a after successful test
+* use common helpers and variables where possible
## do not
diff --git a/tests/clean-tests.sh b/tests/clean-tests.sh
index 61baa069..342616f7 100755
--- a/tests/clean-tests.sh
+++ b/tests/clean-tests.sh
@@ -1,9 +1,34 @@
#!/bin/bash
# remove all intermediate files from tests
+LANG=C
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
-TOP=$(readlink -f "$SCRIPT_DIR/../")
-source "$TOP/tests/common"
+if [ -z "$TOP" ]; then
+ TOP=$(readlink -f "$SCRIPT_DIR/../")
+ if [ -f "$TOP/configure.ac" ]; then
+ # inside git
+ TEST_TOP="$TOP/tests/"
+ INTERNAL_BIN="$TOP"
+ else
+ # external, defaults to system binaries
+ TOP=$(dirname `which btrfs`)
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+ fi
+else
+ # assume external, TOP set from commandline
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+fi
+if ! [ -x "$TOP/btrfs" ]; then
+ echo "ERROR: cannot execute btrfs from TOP=$TOP"
+ exit 1
+fi
+TEST_DEV=${TEST_DEV:-}
+RESULTS="$TEST_TOP/cli-tests-results.txt"
+IMAGE="$TEST_TOP/test.img"
+
+source "$TEST_TOP/common"
setup_root_helper
@@ -13,8 +38,8 @@ fi
$SUDO_HELPER umount "$TEST_MNT" &>/dev/null
-if ! cd "$TOP/tests"; then
- echo "ERROR: cannot cd to $TOP/tests"
+if ! cd "$TEST_TOP"; then
+ echo "ERROR: cannot cd to $TEST_TOP"
exit 1
fi
diff --git a/tests/cli-tests.sh b/tests/cli-tests.sh
index 16d6afcf..9e0fbae4 100755
--- a/tests/cli-tests.sh
+++ b/tests/cli-tests.sh
@@ -4,13 +4,35 @@
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
-TOP=$(readlink -f "$SCRIPT_DIR/../")
+if [ -z "$TOP" ]; then
+ TOP=$(readlink -f "$SCRIPT_DIR/../")
+ if [ -f "$TOP/configure.ac" ]; then
+ # inside git
+ TEST_TOP="$TOP/tests/"
+ INTERNAL_BIN="$TOP"
+ else
+ # external, defaults to system binaries
+ TOP=$(dirname `which btrfs`)
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+ fi
+else
+ # assume external, TOP set from commandline
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+fi
+if ! [ -x "$TOP/btrfs" ]; then
+ echo "ERROR: cannot execute btrfs from TOP=$TOP"
+ exit 1
+fi
TEST_DEV=${TEST_DEV:-}
-RESULTS="$TOP/tests/cli-tests-results.txt"
-IMAGE="$TOP/tests/test.img"
+RESULTS="$TEST_TOP/cli-tests-results.txt"
+IMAGE="$TEST_TOP/test.img"
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
+export INTERNAL_BIN
+export TEST_TOP
export TOP
export RESULTS
export LANG
@@ -24,7 +46,7 @@ check_kernel_support
# The tests are driven by their custom script called 'test.sh'
-for i in $(find "$TOP/tests/cli-tests" -maxdepth 1 -mindepth 1 -type d \
+for i in $(find "$TEST_TOP/cli-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
name=$(basename "$i")
@@ -40,5 +62,5 @@ do
_fail "test failed for case $(basename $i)"
fi
fi
- cd "$TOP"
+ cd "$TEST_TOP"
done
diff --git a/tests/cli-tests/001-btrfs/test.sh b/tests/cli-tests/001-btrfs/test.sh
index c680604b..55ab0ad5 100755
--- a/tests/cli-tests/001-btrfs/test.sh
+++ b/tests/cli-tests/001-btrfs/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# test commands of btrfs
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/cli-tests/002-balance-full-no-filters/test.sh b/tests/cli-tests/002-balance-full-no-filters/test.sh
index 0475ea73..3403b700 100755
--- a/tests/cli-tests/002-balance-full-no-filters/test.sh
+++ b/tests/cli-tests/002-balance-full-no-filters/test.sh
@@ -2,7 +2,7 @@
#
# coverage of balance --full-balance
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/cli-tests/003-fi-resize-args/test.sh b/tests/cli-tests/003-fi-resize-args/test.sh
index e4f262b6..f6f598f2 100755
--- a/tests/cli-tests/003-fi-resize-args/test.sh
+++ b/tests/cli-tests/003-fi-resize-args/test.sh
@@ -2,7 +2,7 @@
#
# test parsing of various resize arguments
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/cli-tests/004-send-parent-multi-subvol/test.sh b/tests/cli-tests/004-send-parent-multi-subvol/test.sh
index c1348b50..165ef4a6 100755
--- a/tests/cli-tests/004-send-parent-multi-subvol/test.sh
+++ b/tests/cli-tests/004-send-parent-multi-subvol/test.sh
@@ -2,7 +2,7 @@
#
# minimal test for the following syntax: btrfs send -p parent subvol1 subvol2
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/cli-tests/005-qgroup-show/test.sh b/tests/cli-tests/005-qgroup-show/test.sh
index d9a91831..10521039 100755
--- a/tests/cli-tests/005-qgroup-show/test.sh
+++ b/tests/cli-tests/005-qgroup-show/test.sh
@@ -2,7 +2,7 @@
#
# qgroup show behaviour when quotas are not enabled
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/cli-tests/006-qgroup-show-sync/test.sh b/tests/cli-tests/006-qgroup-show-sync/test.sh
index d552b8b9..aec7a4ba 100755
--- a/tests/cli-tests/006-qgroup-show-sync/test.sh
+++ b/tests/cli-tests/006-qgroup-show-sync/test.sh
@@ -2,7 +2,7 @@
#
# simple test of qgroup show --sync option
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/cli-tests/007-check-force/test.sh b/tests/cli-tests/007-check-force/test.sh
index 12b30205..597f2d60 100755
--- a/tests/cli-tests/007-check-force/test.sh
+++ b/tests/cli-tests/007-check-force/test.sh
@@ -2,7 +2,7 @@
#
# test 'btrfs check --force' on a mounted filesystem
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/cli-tests/008-subvolume-get-set-default/test.sh b/tests/cli-tests/008-subvolume-get-set-default/test.sh
index 9318002e..706ee8c5 100755
--- a/tests/cli-tests/008-subvolume-get-set-default/test.sh
+++ b/tests/cli-tests/008-subvolume-get-set-default/test.sh
@@ -10,7 +10,7 @@ check_default_id()
fi
}
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/common b/tests/common
index 734cd171..fae30f1d 100644
--- a/tests/common
+++ b/tests/common
@@ -290,8 +290,12 @@ run_mustfail_stdout()
check_prereq()
{
- if ! [ -f "$TOP/$1" ]; then
- _fail "Failed prerequisites: $1";
+ if [ "$1" = "btrfs-corrupt-block" -o "$1" = "fssum" ]; then
+ if ! [ -f "$INTERNAL_BIN/$1" ]; then
+ _fail "Failed prerequisites: $INTERNAL_BIN/$1";
+ fi
+ elif ! [ -f "$TOP/$1" ]; then
+ _fail "Failed prerequisites: $TOP/$1";
fi
}
@@ -331,7 +335,7 @@ extract_image()
case "$image" in
*.img)
rm -f "$image.restored"
- : ;;
+ ;;
*.img.xz)
xz --decompress --keep "$image" || \
_fail "failed to decompress image $image" >&2
@@ -444,14 +448,16 @@ prepare_test_dev()
[[ "$size" ]] || size='2G'
# Still truncate it to new size
if [ -n "$TEST_DEV" ]; then
+ truncate -s 0 "$TEST_DEV"
truncate -s "$size" "$TEST_DEV"
return;
fi
- echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
+ echo "\$TEST_DEV not given, using $TEST_TOP/test.img as fallback" >> \
"$RESULTS"
- TEST_DEV="$TOP/tests/test.img"
+ TEST_DEV="$TEST_TOP/test.img"
+ truncate -s 0 "$TEST_DEV"
truncate -s "$size" "$TEST_DEV" || _not_run "create file for loop device failed"
}
@@ -475,10 +481,14 @@ run_check_mount_test_dev()
run_check $SUDO_HELPER mount -t btrfs $loop_opt "$@" "$TEST_DEV" "$TEST_MNT"
}
+# $1-$n: optional paths to unmount, otherwise fallback to TEST_DEV
run_check_umount_test_dev()
{
setup_root_helper
- run_check $SUDO_HELPER umount "$@" "$TEST_DEV"
+ if [ "$#" = 0 ]; then
+ set -- "$TEST_DEV"
+ fi
+ run_check $SUDO_HELPER umount "$@"
}
check_kernel_support()
@@ -626,11 +636,11 @@ cleanup_loopdevs()
init_env()
{
- TEST_MNT="${TEST_MNT:-$TOP/tests/mnt}"
+ TEST_MNT="${TEST_MNT:-$TEST_TOP/mnt}"
export TEST_MNT
mkdir -p "$TEST_MNT" || { echo "Failed mkdir -p $TEST_MNT"; exit 1; }
- source $TOP/tests/common.local
+ source $TEST_TOP/common.local
if [ "$TEST_ENABLE_OVERRIDE" = 'true' -a -n "$RESULTS" ]; then
echo "INCLUDE common.local" >> "$RESULTS"
diff --git a/tests/convert-tests.sh b/tests/convert-tests.sh
index 2a92a58b..4bc915db 100755
--- a/tests/convert-tests.sh
+++ b/tests/convert-tests.sh
@@ -5,14 +5,36 @@
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
-TOP=$(readlink -f "$SCRIPT_DIR/../")
+if [ -z "$TOP" ]; then
+ TOP=$(readlink -f "$SCRIPT_DIR/../")
+ if [ -f "$TOP/configure.ac" ]; then
+ # inside git
+ TEST_TOP="$TOP/tests/"
+ INTERNAL_BIN="$TOP"
+ else
+ # external, defaults to system binaries
+ TOP=$(dirname `which btrfs`)
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+ fi
+else
+ # assume external, TOP set from commandline
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+fi
+if ! [ -x "$TOP/btrfs" ]; then
+ echo "ERROR: cannot execute btrfs from TOP=$TOP"
+ exit 1
+fi
TEST_DEV=${TEST_DEV:-}
-RESULTS="$TOP/tests/convert-tests-results.txt"
-IMAGE="$TOP/tests/test.img"
+RESULTS="$TEST_TOP/convert-tests-results.txt"
+IMAGE="$TEST_TOP/test.img"
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
+export INTERNAL_BIN
+export TEST_TOP
export TOP
export RESULTS
export LANG
@@ -54,7 +76,7 @@ run_one_test() {
}
# Test special images
-for i in $(find "$TOP/tests/convert-tests" -maxdepth 1 -mindepth 1 -type d \
+for i in $(find "$TEST_TOP/convert-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
run_one_test "$i"
diff --git a/tests/convert-tests/001-ext2-basic/test.sh b/tests/convert-tests/001-ext2-basic/test.sh
index af75d948..74cc74e8 100755
--- a/tests/convert-tests/001-ext2-basic/test.sh
+++ b/tests/convert-tests/001-ext2-basic/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/convert-tests/002-ext3-basic/test.sh b/tests/convert-tests/002-ext3-basic/test.sh
index 233e2d94..f0869897 100755
--- a/tests/convert-tests/002-ext3-basic/test.sh
+++ b/tests/convert-tests/002-ext3-basic/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/convert-tests/003-ext4-basic/test.sh b/tests/convert-tests/003-ext4-basic/test.sh
index baf6115c..c5caf67c 100755
--- a/tests/convert-tests/003-ext4-basic/test.sh
+++ b/tests/convert-tests/003-ext4-basic/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh b/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
index cf354d40..dcb9772b 100755
--- a/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
+++ b/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
@@ -10,7 +10,7 @@
# 4) Overlap file extents
# 5) Unable to rollback
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs-convert
check_prereq btrfs
diff --git a/tests/convert-tests/005-delete-all-rollback/test.sh b/tests/convert-tests/005-delete-all-rollback/test.sh
index 31fa2c4b..a5f9d594 100755
--- a/tests/convert-tests/005-delete-all-rollback/test.sh
+++ b/tests/convert-tests/005-delete-all-rollback/test.sh
@@ -2,8 +2,8 @@
# create a base image, convert to btrfs, remove all files, rollback the ext4 image
# note: ext4 only
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/convert-tests/006-large-hole-extent/test.sh b/tests/convert-tests/006-large-hole-extent/test.sh
index 38e97055..a37fcbdc 100755
--- a/tests/convert-tests/006-large-hole-extent/test.sh
+++ b/tests/convert-tests/006-large-hole-extent/test.sh
@@ -5,8 +5,8 @@
# Fast pinpoint regression test. No options combination nor checksum
# verification
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/convert-tests/007-unsupported-block-sizes/test.sh b/tests/convert-tests/007-unsupported-block-sizes/test.sh
index ef010202..9fda5add 100755
--- a/tests/convert-tests/007-unsupported-block-sizes/test.sh
+++ b/tests/convert-tests/007-unsupported-block-sizes/test.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# Check if block sizes smaller than 4k expectedly fail to convert
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/convert-tests/008-readonly-image/test.sh b/tests/convert-tests/008-readonly-image/test.sh
index 064bc271..1a65ea6b 100755
--- a/tests/convert-tests/008-readonly-image/test.sh
+++ b/tests/convert-tests/008-readonly-image/test.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# Check if the converted ext2 image is readonly
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/convert-tests/009-common-inode-flags/test.sh b/tests/convert-tests/009-common-inode-flags/test.sh
index 6d159993..428213bf 100755
--- a/tests/convert-tests/009-common-inode-flags/test.sh
+++ b/tests/convert-tests/009-common-inode-flags/test.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# Check if btrfs-convert can copy common inode flags like SYNC/IMMUTABLE
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/convert-tests/010-reiserfs-basic/test.sh b/tests/convert-tests/010-reiserfs-basic/test.sh
index 87008f15..73652991 100755
--- a/tests/convert-tests/010-reiserfs-basic/test.sh
+++ b/tests/convert-tests/010-reiserfs-basic/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
if ! check_kernel_support_reiserfs >/dev/null; then
_not_run "no reiserfs support"
diff --git a/tests/convert-tests/011-reiserfs-delete-all-rollback/test.sh b/tests/convert-tests/011-reiserfs-delete-all-rollback/test.sh
index 0b8366c8..28877e14 100755
--- a/tests/convert-tests/011-reiserfs-delete-all-rollback/test.sh
+++ b/tests/convert-tests/011-reiserfs-delete-all-rollback/test.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# create a base image, convert to btrfs, remove all files, rollback the reiserfs image
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
if ! check_kernel_support_reiserfs >/dev/null; then
_not_run "no reiserfs support"
diff --git a/tests/convert-tests/012-reiserfs-large-hole-extent/test.sh b/tests/convert-tests/012-reiserfs-large-hole-extent/test.sh
index dde1b3eb..d492779a 100755
--- a/tests/convert-tests/012-reiserfs-large-hole-extent/test.sh
+++ b/tests/convert-tests/012-reiserfs-large-hole-extent/test.sh
@@ -5,8 +5,8 @@
# Fast pinpoint regression test. No options combination nor checksum
# verification
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
if ! check_kernel_support_reiserfs >/dev/null; then
_not_run "no reiserfs support"
diff --git a/tests/convert-tests/013-reiserfs-common-inode-flags/test.sh b/tests/convert-tests/013-reiserfs-common-inode-flags/test.sh
index a15240ce..521e8bd4 100755
--- a/tests/convert-tests/013-reiserfs-common-inode-flags/test.sh
+++ b/tests/convert-tests/013-reiserfs-common-inode-flags/test.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# Check if btrfs-convert can copy common inode flags like SYNC/IMMUTABLE
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
if ! check_kernel_support_reiserfs >/dev/null; then
_not_run "no reiserfs support"
diff --git a/tests/convert-tests/014-reiserfs-tail-handling/test.sh b/tests/convert-tests/014-reiserfs-tail-handling/test.sh
index 335c0091..5714dc6c 100755
--- a/tests/convert-tests/014-reiserfs-tail-handling/test.sh
+++ b/tests/convert-tests/014-reiserfs-tail-handling/test.sh
@@ -6,8 +6,8 @@
# We use separate inputs for tails and real blocks so we can determine
# if there was a failure in copying either.
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
if ! check_kernel_support_reiserfs >/dev/null; then
_not_run "no reiserfs support"
diff --git a/tests/convert-tests/015-no-rollback-after-balance/test.sh b/tests/convert-tests/015-no-rollback-after-balance/test.sh
index 47c9c6fa..2f6407f3 100755
--- a/tests/convert-tests/015-no-rollback-after-balance/test.sh
+++ b/tests/convert-tests/015-no-rollback-after-balance/test.sh
@@ -2,8 +2,8 @@
# Check if btrfs-convert refuses to rollback the filesystem, and leave the fs
# and the convert image untouched
-source "$TOP/tests/common"
-source "$TOP/tests/common.convert"
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
setup_root_helper
prepare_test_dev
diff --git a/tests/export-testsuite.sh b/tests/export-testsuite.sh
new file mode 100755
index 00000000..31b6ecf4
--- /dev/null
+++ b/tests/export-testsuite.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+# export the testsuite files to a separate tar
+
+if ! [ -f testsuite-files ]; then
+ echo "ERROR: cannot find testsuite-files"
+ exit 1
+fi
+
+set -e
+
+TESTSUITE_TAR="btrfs-progs-tests.tar.gz"
+rm -f "$TESTSUITE_TAR"
+
+TIMESTAMP=`date -u "+%Y-%m-%d %T %Z"`
+
+{
+ echo "VERSION=`cat ../VERSION`"
+ echo "GIT_VERSION=`git describe`"
+ echo "TIMESTAMP='$TIMESTAMP'"
+} > testsuite-id
+
+# Due to potentially unwanted files in the testsuite (restored images or other
+# temporary files) we can't simply copy everything so the tar
+#
+# The testsuite-files specifier:
+# F file
+# - directly copy the file from the given path, may be a git tracked file or
+# a built binary
+# G path
+# - a path relative to the top of git, recursively traversed; path
+# postprocessing is needed so the tar gets it relative to tests/
+while read t f; do
+ case "$t" in
+ F) echo "$f";;
+ G)
+ here=`pwd`
+ cd ..
+ git ls-tree -r --name-only --full-name HEAD "$f" |
+ sed -e 's#^tests/##' |
+ sed -e 's#^Documentation#../Documentation#'
+ cd "$here"
+ ;;
+ esac
+done < testsuite-files > testsuite-files-all
+
+echo "create tar: $TESTSUITE_TAR"
+tar cz --sparse -f "$TESTSUITE_TAR" -T testsuite-files-all
+if [ $? -eq 0 ]; then
+ echo "tar created successfully"
+ cat testsuite-id
+ rm -f testsuite-files-all
+ rm -f testsuite-id
+else
+ exit $?
+fi
diff --git a/tests/fsck-tests.sh b/tests/fsck-tests.sh
index 15d26c70..14287bbe 100755
--- a/tests/fsck-tests.sh
+++ b/tests/fsck-tests.sh
@@ -4,13 +4,35 @@
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
-TOP=$(readlink -f "$SCRIPT_DIR/../")
+if [ -z "$TOP" ]; then
+ TOP=$(readlink -f "$SCRIPT_DIR/../")
+ if [ -f "$TOP/configure.ac" ]; then
+ # inside git
+ TEST_TOP="$TOP/tests/"
+ INTERNAL_BIN="$TOP"
+ else
+ # external, defaults to system binaries
+ TOP=$(dirname `which btrfs`)
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+ fi
+else
+ # assume external, TOP set from commandline
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+fi
+if ! [ -x "$TOP/btrfs" ]; then
+ echo "ERROR: cannot execute btrfs from TOP=$TOP"
+ exit 1
+fi
TEST_DEV=${TEST_DEV:-}
-RESULTS="$TOP/tests/fsck-tests-results.txt"
-IMAGE="$TOP/tests/test.img"
+RESULTS="$TEST_TOP/fsck-tests-results.txt"
+IMAGE="$TEST_TOP/test.img"
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
+export INTERNAL_BIN
+export TEST_TOP
export TOP
export RESULTS
export LANG
@@ -46,7 +68,7 @@ run_one_test() {
# Type 1
check_all_images
fi
- cd "$TOP"
+ cd "$TEST_TOP"
}
# Each dir contains one type of error for btrfsck test.
@@ -62,7 +84,7 @@ run_one_test() {
# This is for case btrfs-image can't dump or case needs extra
# check/verify
-for i in $(find "$TOP/tests/fsck-tests" -maxdepth 1 -mindepth 1 -type d \
+for i in $(find "$TEST_TOP/fsck-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
run_one_test "$i"
diff --git a/tests/fsck-tests/006-bad-root-items/test.sh b/tests/fsck-tests/006-bad-root-items/test.sh
index bf3ef781..2cbf67a8 100755
--- a/tests/fsck-tests/006-bad-root-items/test.sh
+++ b/tests/fsck-tests/006-bad-root-items/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/fsck-tests/012-leaf-corruption/test.sh b/tests/fsck-tests/012-leaf-corruption/test.sh
index fc10a4ff..68d9f695 100755
--- a/tests/fsck-tests/012-leaf-corruption/test.sh
+++ b/tests/fsck-tests/012-leaf-corruption/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs-image
diff --git a/tests/fsck-tests/013-extent-tree-rebuild/test.sh b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
index d71c1b2e..02afdda1 100755
--- a/tests/fsck-tests/013-extent-tree-rebuild/test.sh
+++ b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs-corrupt-block
check_prereq mkfs.btrfs
@@ -31,7 +31,7 @@ test_extent_tree_rebuild()
fi
# corrupt extent root node block
- run_check $SUDO_HELPER "$TOP/btrfs-corrupt-block" -l "$extent_root_bytenr" \
+ run_check $SUDO_HELPER "$INTERNAL_BIN/btrfs-corrupt-block" -l "$extent_root_bytenr" \
-b 4096 "$TEST_DEV"
$SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV" >& /dev/null && \
diff --git a/tests/fsck-tests/027-tree-reloc-tree/test.sh b/tests/fsck-tests/015-tree-reloc-tree/test.sh
index afad1e8d..afad1e8d 100755
--- a/tests/fsck-tests/027-tree-reloc-tree/test.sh
+++ b/tests/fsck-tests/015-tree-reloc-tree/test.sh
diff --git a/tests/fsck-tests/027-tree-reloc-tree/tree_reloc_for_data_reloc.img.xz b/tests/fsck-tests/015-tree-reloc-tree/tree_reloc_for_data_reloc.img.xz
index 66d8bde6..66d8bde6 100644
--- a/tests/fsck-tests/027-tree-reloc-tree/tree_reloc_for_data_reloc.img.xz
+++ b/tests/fsck-tests/015-tree-reloc-tree/tree_reloc_for_data_reloc.img.xz
Binary files differ
diff --git a/tests/fsck-tests/027-tree-reloc-tree/tree_reloc_for_fs_tree.img.xz b/tests/fsck-tests/015-tree-reloc-tree/tree_reloc_for_fs_tree.img.xz
index 22af324b..22af324b 100644
--- a/tests/fsck-tests/027-tree-reloc-tree/tree_reloc_for_fs_tree.img.xz
+++ b/tests/fsck-tests/015-tree-reloc-tree/tree_reloc_for_fs_tree.img.xz
Binary files differ
diff --git a/tests/fsck-tests/018-leaf-crossing-stripes/test.sh b/tests/fsck-tests/018-leaf-crossing-stripes/test.sh
index 29eb20b5..2a3f6379 100755
--- a/tests/fsck-tests/018-leaf-crossing-stripes/test.sh
+++ b/tests/fsck-tests/018-leaf-crossing-stripes/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/fsck-tests/019-non-skinny-false-alert/test.sh b/tests/fsck-tests/019-non-skinny-false-alert/test.sh
index 550f2947..32f595da 100755
--- a/tests/fsck-tests/019-non-skinny-false-alert/test.sh
+++ b/tests/fsck-tests/019-non-skinny-false-alert/test.sh
@@ -11,7 +11,7 @@
#
# a buggy check leads to the above messages
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/fsck-tests/020-extent-ref-cases/test.sh b/tests/fsck-tests/020-extent-ref-cases/test.sh
index 0c4f7848..9cf99a51 100755
--- a/tests/fsck-tests/020-extent-ref-cases/test.sh
+++ b/tests/fsck-tests/020-extent-ref-cases/test.sh
@@ -15,7 +15,7 @@
# the beginning of leaf.
# Which caused false alert for lowmem mode.
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/fsck-tests/021-partially-dropped-snapshot-case/test.sh b/tests/fsck-tests/021-partially-dropped-snapshot-case/test.sh
index 5d997e24..e6379f97 100755
--- a/tests/fsck-tests/021-partially-dropped-snapshot-case/test.sh
+++ b/tests/fsck-tests/021-partially-dropped-snapshot-case/test.sh
@@ -2,7 +2,7 @@
# confirm whether 'btrfs check' supports check ing of a partially dropped
# snapshot
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh b/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh
index dcdc1b42..615f003d 100755
--- a/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh
+++ b/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# check whether btrfsck can detect running qgroup rescan
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/fsck-tests/023-qgroup-stack-overflow/test.sh b/tests/fsck-tests/023-qgroup-stack-overflow/test.sh
index ebb07f36..d7b85f07 100755
--- a/tests/fsck-tests/023-qgroup-stack-overflow/test.sh
+++ b/tests/fsck-tests/023-qgroup-stack-overflow/test.sh
@@ -5,7 +5,7 @@
# Fixed by patch:
# btrfs-progs: Fix stack overflow for checking qgroup on tree reloc tree
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/fsck-tests/024-clear-space-cache/test.sh b/tests/fsck-tests/024-clear-space-cache/test.sh
index 76ebcb6b..6a3a31ec 100755
--- a/tests/fsck-tests/024-clear-space-cache/test.sh
+++ b/tests/fsck-tests/024-clear-space-cache/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# confirm that clearing space cache works
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
check_prereq mkfs.btrfs
diff --git a/tests/fsck-tests/025-file-extents/test.sh b/tests/fsck-tests/025-file-extents/test.sh
index ebe8a305..95707596 100755
--- a/tests/fsck-tests/025-file-extents/test.sh
+++ b/tests/fsck-tests/025-file-extents/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# Confirm btrfs check can check file extents without causing false alert
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
check_prereq mkfs.btrfs
diff --git a/tests/fsck-tests/026-bad-dir-item-name/test.sh b/tests/fsck-tests/026-bad-dir-item-name/test.sh
index a1077a8d..a38bf045 100755
--- a/tests/fsck-tests/026-bad-dir-item-name/test.sh
+++ b/tests/fsck-tests/026-bad-dir-item-name/test.sh
@@ -2,7 +2,7 @@
#
# confirm whether check detects name and hash mismatch in dir_item
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/fsck-tests/028-unaligned-super-dev-sizes/test.sh b/tests/fsck-tests/028-unaligned-super-dev-sizes/test.sh
index 6f315fae..4015df2d 100755
--- a/tests/fsck-tests/028-unaligned-super-dev-sizes/test.sh
+++ b/tests/fsck-tests/028-unaligned-super-dev-sizes/test.sh
@@ -3,24 +3,23 @@
# An image with mis-aligned superblock total_bytes, that will be found and
# fixed by 'check' or fixed by 'rescue fix-device-size'
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
-prepare_test_dev
setup_root_helper
check_all_images
-image=$(extract_image "./dev_and_super_mismatch_unaligned.raw.xz")
+TEST_DEV=$(extract_image "./dev_and_super_mismatch_unaligned.raw.xz")
# detect and fix
-run_check "$TOP/btrfs" rescue fix-device-size "$image"
+run_check "$TOP/btrfs" rescue fix-device-size "$TEST_DEV"
# no problem found
-run_check "$TOP/btrfs" rescue fix-device-size "$image"
+run_check "$TOP/btrfs" rescue fix-device-size "$TEST_DEV"
# check if fix-device-size worked
-run_check "$TOP/btrfs" check "$image"
+run_check "$TOP/btrfs" check "$TEST_DEV"
# mount test
run_check_mount_test_dev
-run_check_umount_test_dev
-
-rm -f "$image"
+run_check_umount_test_dev "$TEST_MNT"
+# remove restored image
+rm -- "$TEST_DEV"
diff --git a/tests/fssum.c b/tests/fssum.c
index 5dde9984..2bda5df8 100644
--- a/tests/fssum.c
+++ b/tests/fssum.c
@@ -532,8 +532,8 @@ sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
}
ret = lstat64(namelist[i], &st);
if (ret) {
- fprintf(stderr, "stat failed for %s/%s: %s\n",
- path_prefix, path, strerror(errno));
+ fprintf(stderr, "stat failed for %s/%s: %m\n",
+ path_prefix, path);
exit(-1);
}
sum_add_u64(&meta, level);
@@ -557,8 +557,8 @@ sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
if (fd == -1 && flags[FLAG_OPEN_ERROR]) {
sum_add_u64(&meta, errno);
} else if (fd == -1) {
- fprintf(stderr, "open failed for %s/%s: %s\n",
- path_prefix, path, strerror(errno));
+ fprintf(stderr, "open failed for %s/%s: %m\n",
+ path_prefix, path);
exit(-1);
} else {
sum(fd, level + 1, &cs, path_prefix, path);
@@ -575,9 +575,8 @@ sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
sum_add_u64(&meta, errno);
} else if (fd == -1) {
fprintf(stderr,
- "open failed for %s/%s: %s\n",
- path_prefix, path,
- strerror(errno));
+ "open failed for %s/%s: %m\n",
+ path_prefix, path);
exit(-1);
}
if (fd != -1) {
@@ -585,9 +584,8 @@ sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
if (ret < 0) {
fprintf(stderr,
"read failed for "
- "%s/%s: %s\n",
- path_prefix, path,
- strerror(errno));
+ "%s/%s: %m\n",
+ path_prefix, path);
exit(-1);
}
close(fd);
@@ -693,8 +691,7 @@ main(int argc, char *argv[])
out_fp = fopen(optarg, "w");
if (!out_fp) {
fprintf(stderr,
- "failed to open output file: %s\n",
- strerror(errno));
+ "failed to open output file: %m\n");
exit(-1);
}
break;
@@ -702,8 +699,7 @@ main(int argc, char *argv[])
in_fp = fopen(optarg, "r");
if (!in_fp) {
fprintf(stderr,
- "failed to open input file: %s\n",
- strerror(errno));
+ "failed to open input file: %m\n");
exit(-1);
}
break;
@@ -788,8 +784,7 @@ main(int argc, char *argv[])
fd = open(path, O_RDONLY);
if (fd == -1) {
- fprintf(stderr, "failed to open %s: %s\n", path,
- strerror(errno));
+ fprintf(stderr, "failed to open %s: %m\n", path);
exit(-1);
}
diff --git a/tests/fuzz-tests.sh b/tests/fuzz-tests.sh
index f72385e5..7bc620f0 100755
--- a/tests/fuzz-tests.sh
+++ b/tests/fuzz-tests.sh
@@ -4,13 +4,35 @@
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
-TOP=$(readlink -f "$SCRIPT_DIR/../")
+if [ -z "$TOP" ]; then
+ TOP=$(readlink -f "$SCRIPT_DIR/../")
+ if [ -f "$TOP/configure.ac" ]; then
+ # inside git
+ TEST_TOP="$TOP/tests/"
+ INTERNAL_BIN="$TOP"
+ else
+ # external, defaults to system binaries
+ TOP=$(dirname `which btrfs`)
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+ fi
+else
+ # assume external, TOP set from commandline
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+fi
+if ! [ -x "$TOP/btrfs" ]; then
+ echo "ERROR: cannot execute btrfs from TOP=$TOP"
+ exit 1
+fi
TEST_DEV=${TEST_DEV:-}
-RESULTS="$TOP/tests/fuzz-tests-results.txt"
-IMAGE="$TOP/tests/test.img"
+RESULTS="$TEST_TOP/fuzz-tests-results.txt"
+IMAGE="$TEST_TOP/test.img"
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
+export INTERNAL_BIN
+export TEST_TOP
export TOP
export RESULTS
export LANG
@@ -23,7 +45,7 @@ check_prereq btrfs
# The tests are driven by their custom script called 'test.sh'
-for i in $(find "$TOP/tests/fuzz-tests" -maxdepth 1 -mindepth 1 -type d \
+for i in $(find "$TEST_TOP/fuzz-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
name=$(basename "$i")
@@ -39,5 +61,5 @@ do
_fail "test failed for case $(basename $i)"
fi
fi
- cd "$TOP"
+ cd "$TEST_TOP"
done
diff --git a/tests/fuzz-tests/001-simple-check-unmounted/test.sh b/tests/fuzz-tests/001-simple-check-unmounted/test.sh
index 98fe7b0c..905d37b7 100755
--- a/tests/fuzz-tests/001-simple-check-unmounted/test.sh
+++ b/tests/fuzz-tests/001-simple-check-unmounted/test.sh
@@ -2,7 +2,7 @@
# iterate over all fuzzed images and run 'btrfs check'
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs
@@ -15,6 +15,6 @@ check_image() {
run_mayfail $TOP/btrfs check "$image"
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
exit 0
diff --git a/tests/fuzz-tests/002-simple-image/test.sh b/tests/fuzz-tests/002-simple-image/test.sh
index 42470ecc..034fcf46 100755
--- a/tests/fuzz-tests/002-simple-image/test.sh
+++ b/tests/fuzz-tests/002-simple-image/test.sh
@@ -2,7 +2,7 @@
# iterate over all fuzzed images and run 'btrfs-image'
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs-image
@@ -17,7 +17,7 @@ check_image() {
truncate -s0 target
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
rm -- target
diff --git a/tests/fuzz-tests/003-multi-check-unmounted/test.sh b/tests/fuzz-tests/003-multi-check-unmounted/test.sh
index 9fd7b8aa..35dfe4fc 100755
--- a/tests/fuzz-tests/003-multi-check-unmounted/test.sh
+++ b/tests/fuzz-tests/003-multi-check-unmounted/test.sh
@@ -3,7 +3,7 @@
# iterate over all fuzzed images and run 'btrfs check', try various options to
# get more code coverage
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs
@@ -21,6 +21,6 @@ check_image() {
run_mayfail $TOP/btrfs check --repair "$image"
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
exit 0
diff --git a/tests/fuzz-tests/004-simple-dump-tree/test.sh b/tests/fuzz-tests/004-simple-dump-tree/test.sh
index 89ff214c..6c9e8c45 100755
--- a/tests/fuzz-tests/004-simple-dump-tree/test.sh
+++ b/tests/fuzz-tests/004-simple-dump-tree/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs
@@ -13,6 +13,6 @@ check_image() {
run_mayfail $TOP/btrfs inspect-internal dump-tree "$image"
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
exit 0
diff --git a/tests/fuzz-tests/005-simple-dump-super/test.sh b/tests/fuzz-tests/005-simple-dump-super/test.sh
index fbce3d9f..01c7b628 100755
--- a/tests/fuzz-tests/005-simple-dump-super/test.sh
+++ b/tests/fuzz-tests/005-simple-dump-super/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs
@@ -14,6 +14,6 @@ check_image() {
run_mayfail $TOP/btrfs inspect-internal dump-super -Ffa "$image"
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
exit 0
diff --git a/tests/fuzz-tests/006-simple-tree-stats/test.sh b/tests/fuzz-tests/006-simple-tree-stats/test.sh
index c3410b06..dbed471a 100755
--- a/tests/fuzz-tests/006-simple-tree-stats/test.sh
+++ b/tests/fuzz-tests/006-simple-tree-stats/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs
@@ -13,6 +13,6 @@ check_image() {
run_mayfail $TOP/btrfs inspect-internal tree-stats "$image"
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
exit 0
diff --git a/tests/fuzz-tests/007-simple-super-recover/test.sh b/tests/fuzz-tests/007-simple-super-recover/test.sh
index 885cb352..ceb16348 100755
--- a/tests/fuzz-tests/007-simple-super-recover/test.sh
+++ b/tests/fuzz-tests/007-simple-super-recover/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs
@@ -15,6 +15,6 @@ check_image() {
rm -- "$image".scratch
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
exit 0
diff --git a/tests/fuzz-tests/008-simple-chunk-recover/test.sh b/tests/fuzz-tests/008-simple-chunk-recover/test.sh
index d53453f6..198f88e2 100755
--- a/tests/fuzz-tests/008-simple-chunk-recover/test.sh
+++ b/tests/fuzz-tests/008-simple-chunk-recover/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs
@@ -15,6 +15,6 @@ check_image() {
rm -- "$image".scratch
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
exit 0
diff --git a/tests/fuzz-tests/009-simple-zero-log/test.sh b/tests/fuzz-tests/009-simple-zero-log/test.sh
index 393db3f6..928c66b1 100755
--- a/tests/fuzz-tests/009-simple-zero-log/test.sh
+++ b/tests/fuzz-tests/009-simple-zero-log/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TEST_TOP/common"
setup_root_helper
check_prereq btrfs
@@ -15,6 +15,6 @@ check_image() {
rm -- "$image".scratch
}
-check_all_images $TOP/tests/fuzz-tests/images
+check_all_images $TEST_TOP/fuzz-tests/images
exit 0
diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh
index 08988016..94703a3e 100755
--- a/tests/misc-tests.sh
+++ b/tests/misc-tests.sh
@@ -4,18 +4,40 @@
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
-TOP=$(readlink -f "$SCRIPT_DIR/../")
+if [ -z "$TOP" ]; then
+ TOP=$(readlink -f "$SCRIPT_DIR/../")
+ if [ -f "$TOP/configure.ac" ]; then
+ # inside git
+ TEST_TOP="$TOP/tests/"
+ INTERNAL_BIN="$TOP"
+ else
+ # external, defaults to system binaries
+ TOP=$(dirname `which btrfs`)
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+ fi
+else
+ # assume external, TOP set from commandline
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+fi
+if ! [ -x "$TOP/btrfs" ]; then
+ echo "ERROR: cannot execute btrfs from TOP=$TOP"
+ exit 1
+fi
TEST_DEV=${TEST_DEV:-}
-RESULTS="$TOP/tests/misc-tests-results.txt"
-IMAGE="$TOP/tests/test.img"
+RESULTS="$TEST_TOP/misc-tests-results.txt"
+IMAGE="$TEST_TOP/test.img"
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
+export INTERNAL_BIN
+export TEST_TOP
export TOP
export RESULTS
export LANG
-export TEST_DEV
export IMAGE
+export TEST_DEV
rm -f "$RESULTS"
@@ -31,7 +53,7 @@ check_kernel_support
# The tests are driven by their custom script called 'test.sh'
-for i in $(find "$TOP/tests/misc-tests" -maxdepth 1 -mindepth 1 -type d \
+for i in $(find "$TEST_TOP/misc-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
echo " [TEST/misc] $(basename $i)"
@@ -46,5 +68,5 @@ do
_fail "test failed for case $(basename $i)"
fi
fi
- cd "$TOP"
+ cd "$TEST_TOP"
done
diff --git a/tests/misc-tests/001-btrfstune-features/test.sh b/tests/misc-tests/001-btrfstune-features/test.sh
index bfa7f43e..718e4b08 100755
--- a/tests/misc-tests/001-btrfstune-features/test.sh
+++ b/tests/misc-tests/001-btrfstune-features/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# test btrfstune options that enable filesystem features
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfstune
diff --git a/tests/misc-tests/002-uuid-rewrite/test.sh b/tests/misc-tests/002-uuid-rewrite/test.sh
index fd100fb3..e32aff0c 100755
--- a/tests/misc-tests/002-uuid-rewrite/test.sh
+++ b/tests/misc-tests/002-uuid-rewrite/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# test btrfstune uuid rewriting options
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfstune
@@ -25,7 +25,7 @@ test_uuid_random()
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
--uuid $origuuid \
- --rootdir $TOP/Documentation \
+ --rootdir $INTERNAL_BIN/Documentation \
$TEST_DEV
run_check $TOP/btrfs inspect-internal dump-super "$TEST_DEV"
currentfsid=$(run_check_stdout $TOP/btrfstune -f -u $TEST_DEV | \
@@ -47,7 +47,7 @@ test_uuid_user()
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
--uuid $origuuid \
- --rootdir $TOP/Documentation \
+ --rootdir $INTERNAL_BIN/Documentation \
$TEST_DEV
run_check $TOP/btrfs inspect-internal dump-super "$TEST_DEV"
run_check $TOP/btrfstune -f -U $newuuid \
diff --git a/tests/misc-tests/003-zero-log/test.sh b/tests/misc-tests/003-zero-log/test.sh
index e7c5c806..9d2940f5 100755
--- a/tests/misc-tests/003-zero-log/test.sh
+++ b/tests/misc-tests/003-zero-log/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# test zero-log
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -27,7 +27,7 @@ test_zero_log()
{
# FIXME: we need an image with existing log_root
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
- --rootdir $TOP/Documentation \
+ --rootdir $INTERNAL_BIN/Documentation \
$TEST_DEV
run_check $TOP/btrfs inspect-internal dump-super $TEST_DEV
if [ "$1" = 'standalone' ]; then
diff --git a/tests/misc-tests/004-shrink-fs/test.sh b/tests/misc-tests/004-shrink-fs/test.sh
index 88740358..2f08b0b0 100755
--- a/tests/misc-tests/004-shrink-fs/test.sh
+++ b/tests/misc-tests/004-shrink-fs/test.sh
@@ -4,7 +4,7 @@
# are able to resize (shrink) it to that size.
#
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/005-convert-progress-thread-crash/test.sh b/tests/misc-tests/005-convert-progress-thread-crash/test.sh
index bc71e1fd..b8012c9f 100755
--- a/tests/misc-tests/005-convert-progress-thread-crash/test.sh
+++ b/tests/misc-tests/005-convert-progress-thread-crash/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# test convert-thread-conflict
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq btrfs-convert
diff --git a/tests/misc-tests/006-image-on-missing-device/test.sh b/tests/misc-tests/006-image-on-missing-device/test.sh
index 2766fb17..d8b1cef2 100755
--- a/tests/misc-tests/006-image-on-missing-device/test.sh
+++ b/tests/misc-tests/006-image-on-missing-device/test.sh
@@ -4,7 +4,7 @@
# - btrfs-image must not loop indefinetelly
# - btrfs-image will expectedly fail to produce the dump
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq btrfs-image
check_prereq mkfs.btrfs
diff --git a/tests/misc-tests/007-subvolume-sync/test.sh b/tests/misc-tests/007-subvolume-sync/test.sh
index 243bb8cc..ef03d16b 100755
--- a/tests/misc-tests/007-subvolume-sync/test.sh
+++ b/tests/misc-tests/007-subvolume-sync/test.sh
@@ -4,7 +4,7 @@
# - btrfs subvolume must not loop indefinitely
# - btrfs subvolume return 0 in normal case
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/008-leaf-crossing-stripes/test.sh b/tests/misc-tests/008-leaf-crossing-stripes/test.sh
index 03818062..517bd667 100755
--- a/tests/misc-tests/008-leaf-crossing-stripes/test.sh
+++ b/tests/misc-tests/008-leaf-crossing-stripes/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# test if btrfs-convert creates a filesystem without leaf crossing stripes
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq btrfs-convert
check_prereq btrfs
diff --git a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
index fa3f09ab..15de3355 100755
--- a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
+++ b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
@@ -2,7 +2,7 @@
#
# Verify that subvolume sync waits until the subvolume is cleaned
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
index 7915867c..6d510fea 100755
--- a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
+++ b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
@@ -3,7 +3,7 @@
# verify that convert rollback finds the ext2_subvolume intact and fails if it
# was partially deleted
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq btrfs-convert
check_prereq btrfs
diff --git a/tests/misc-tests/011-delete-missing-device/test.sh b/tests/misc-tests/011-delete-missing-device/test.sh
index 8a1b14b1..469c3be9 100755
--- a/tests/misc-tests/011-delete-missing-device/test.sh
+++ b/tests/misc-tests/011-delete-missing-device/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# make sure that 'missing' is accepted for device deletion
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/012-find-root-no-result/test.sh b/tests/misc-tests/012-find-root-no-result/test.sh
index 983a8a1e..f4a57e76 100755
--- a/tests/misc-tests/012-find-root-no-result/test.sh
+++ b/tests/misc-tests/012-find-root-no-result/test.sh
@@ -3,7 +3,7 @@
# recent fs or balanced fs, whose metadata chunk is the first chunk
# and the only metadata chunk
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq btrfs-find-root
check_prereq btrfs-image
diff --git a/tests/misc-tests/013-subvolume-sync-crash/test.sh b/tests/misc-tests/013-subvolume-sync-crash/test.sh
index cd445961..051b457a 100755
--- a/tests/misc-tests/013-subvolume-sync-crash/test.sh
+++ b/tests/misc-tests/013-subvolume-sync-crash/test.sh
@@ -3,7 +3,7 @@
# Verify that subvolume sync waits until the subvolume is cleaned and does not
# crash at the end
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/014-filesystem-label/test.sh b/tests/misc-tests/014-filesystem-label/test.sh
index 753aa9ea..bd6773cb 100755
--- a/tests/misc-tests/014-filesystem-label/test.sh
+++ b/tests/misc-tests/014-filesystem-label/test.sh
@@ -2,7 +2,7 @@
#
# test label settings
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/015-dump-super-garbage/test.sh b/tests/misc-tests/015-dump-super-garbage/test.sh
index 33fc8332..10d8d5b6 100755
--- a/tests/misc-tests/015-dump-super-garbage/test.sh
+++ b/tests/misc-tests/015-dump-super-garbage/test.sh
@@ -2,7 +2,7 @@
#
# let dump-super dump random data, must not crash
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq btrfs
diff --git a/tests/misc-tests/016-send-clone-src/test.sh b/tests/misc-tests/016-send-clone-src/test.sh
index 2780ebbd..e4fa16a7 100755
--- a/tests/misc-tests/016-send-clone-src/test.sh
+++ b/tests/misc-tests/016-send-clone-src/test.sh
@@ -3,7 +3,7 @@
# test for sending stream size of clone-src option, compare against a send
# stream generated by buggy version
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/017-recv-stream-malformatted/test.sh b/tests/misc-tests/017-recv-stream-malformatted/test.sh
index d199a72e..991f2569 100755
--- a/tests/misc-tests/017-recv-stream-malformatted/test.sh
+++ b/tests/misc-tests/017-recv-stream-malformatted/test.sh
@@ -2,7 +2,7 @@
#
# test receiving stream that's not valid, simple cases
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/018-recv-end-of-stream/test.sh b/tests/misc-tests/018-recv-end-of-stream/test.sh
index 9ca035f7..79e735ea 100755
--- a/tests/misc-tests/018-recv-end-of-stream/test.sh
+++ b/tests/misc-tests/018-recv-end-of-stream/test.sh
@@ -3,7 +3,7 @@
# end of stream conditions: test that no instructions in a stream are still
# received, at least the header must be present
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh b/tests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh
index 182b0cf9..60ec5cf9 100755
--- a/tests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh
+++ b/tests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh
@@ -5,7 +5,7 @@
# have an entry with the same name that corresponds to different inodes in each
# snapshot.
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -14,7 +14,7 @@ check_prereq fssum
setup_root_helper
prepare_test_dev
-FSSUM_PROG="$TOP/fssum"
+FSSUM_PROG="$INTERNAL_BIN/fssum"
srcdir=./send-test-dir
rm -rf "$srcdir"
mkdir -p "$srcdir"
diff --git a/tests/misc-tests/020-fix-superblock-corruption/test.sh b/tests/misc-tests/020-fix-superblock-corruption/test.sh
index 77c1a5aa..8f3d20fe 100755
--- a/tests/misc-tests/020-fix-superblock-corruption/test.sh
+++ b/tests/misc-tests/020-fix-superblock-corruption/test.sh
@@ -2,7 +2,7 @@
#
# Corrupt primary superblock and restore it using backup superblock.
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs-select-super
check_prereq btrfs
diff --git a/tests/misc-tests/021-image-multi-devices/test.sh b/tests/misc-tests/021-image-multi-devices/test.sh
index abf67f90..d78c44fb 100755
--- a/tests/misc-tests/021-image-multi-devices/test.sh
+++ b/tests/misc-tests/021-image-multi-devices/test.sh
@@ -2,7 +2,7 @@
# Test btrfs-image with multiple devices filesystem and verify that restoring
# the created image works against a single device.
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs-image
check_prereq mkfs.btrfs
diff --git a/tests/misc-tests/022-filesystem-du-on-empty-subvol/test.sh b/tests/misc-tests/022-filesystem-du-on-empty-subvol/test.sh
index 72cf076f..54365289 100755
--- a/tests/misc-tests/022-filesystem-du-on-empty-subvol/test.sh
+++ b/tests/misc-tests/022-filesystem-du-on-empty-subvol/test.sh
@@ -2,7 +2,7 @@
#
# btrfs fi du should handle empty subvolumes (with ino == 2)
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/023-device-usage-with-missing-device/test.sh b/tests/misc-tests/023-device-usage-with-missing-device/test.sh
index 3c8ba85c..05894cfe 100755
--- a/tests/misc-tests/023-device-usage-with-missing-device/test.sh
+++ b/tests/misc-tests/023-device-usage-with-missing-device/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# check if 'device slack' is reported as zero when a device is missing
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs-image
check_prereq mkfs.btrfs
diff --git a/tests/misc-tests/024-inspect-internal-rootid/test.sh b/tests/misc-tests/024-inspect-internal-rootid/test.sh
index 40e382bb..71e19044 100755
--- a/tests/misc-tests/024-inspect-internal-rootid/test.sh
+++ b/tests/misc-tests/024-inspect-internal-rootid/test.sh
@@ -2,7 +2,7 @@
#
# test commands of inspect-internal rootid
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/025-zstd-compression/test.sh b/tests/misc-tests/025-zstd-compression/test.sh
index e95dcb36..22795d27 100755
--- a/tests/misc-tests/025-zstd-compression/test.sh
+++ b/tests/misc-tests/025-zstd-compression/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# Test zstd compression support on a prebuilt btrfs image
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
check_global_prereq md5sum
diff --git a/tests/misc-tests/026-image-non-printable-chars/test.sh b/tests/misc-tests/026-image-non-printable-chars/test.sh
index 8018586f..50441a21 100755
--- a/tests/misc-tests/026-image-non-printable-chars/test.sh
+++ b/tests/misc-tests/026-image-non-printable-chars/test.sh
@@ -2,7 +2,7 @@
# check that sanitized names with matching crc do not contain unprintable
# characters, namely 0x7f
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/027-subvol-list-deleted-toplevel/test.sh b/tests/misc-tests/027-subvol-list-deleted-toplevel/test.sh
index ee41d71f..3b73dd36 100755
--- a/tests/misc-tests/027-subvol-list-deleted-toplevel/test.sh
+++ b/tests/misc-tests/027-subvol-list-deleted-toplevel/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# check that the toplevel subvolume is not listed as regular or deleted
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/misc-tests/028-superblock-recover/test.sh b/tests/misc-tests/028-superblock-recover/test.sh
new file mode 100755
index 00000000..1175e480
--- /dev/null
+++ b/tests/misc-tests/028-superblock-recover/test.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+# Test that any superblock is correctly detected and fixed by btrfs rescue
+
+source "$TOP/tests/common"
+
+check_prereq btrfs
+check_prereq mkfs.btrfs
+check_prereq btrfs-select-super
+
+setup_root_helper
+prepare_test_dev 260G
+
+# Create the test file system.
+run_check $SUDO_HELPER "$TOP"/mkfs.btrfs -f "$TEST_DEV"
+
+function check_corruption {
+ local sb_offset=$1
+ local source_sb=$2
+
+ # First we ensure we can mount it successfully
+ run_check_mount_test_dev
+ run_check_umount_test_dev
+
+ # Now corrupt 1k of the superblock at sb_offset
+ run_check $SUDO_HELPER dd bs=1K count=1 seek=$(($sb_offset + 1)) if=/dev/zero of="$TEST_DEV" conv=notrunc
+
+ # if corrupting one of the sb copies, copy it over the initial superblock
+ if [ ! -z $source_sb ]; then
+ local shift_val=$((16 << $source_sb * 12 ))
+ run_check $SUDO_HELPER dd bs=1K count=4 seek=64 skip=$shift_val if="$TEST_DEV" of="$TEST_DEV" conv=notrunc
+ fi
+
+ # we can't use our mount helper, the following works for file image and
+ # block device as TEST_DEV
+ run_mustfail "mounted fs with corrupted superblock" \
+ $SUDO_HELPER mount "$TEST_DEV" "$TEST_MNT"
+
+ # Now run btrfs rescue which should fix the superblock. It uses 2
+ # to signal success of recovery use mayfail to ignore that retval
+ # but still log the output of the command
+ run_mayfail $SUDO_HELPER "$TOP"/btrfs rescue super-recover -yv "$TEST_DEV"
+ if [ $? != 2 ]; then
+ _fail "couldn't rescue super"
+ fi
+
+ run_check_mount_test_dev
+ run_check_umount_test_dev
+}
+
+# Corrupting first superblock
+check_corruption 64
+
+# Corrupting second superblock
+check_corruption 65536 1
+
+# Corrupting third superblock
+check_corruption 268435456 2
diff --git a/tests/mkfs-tests.sh b/tests/mkfs-tests.sh
index c8ff8c83..2ced4ac9 100755
--- a/tests/mkfs-tests.sh
+++ b/tests/mkfs-tests.sh
@@ -4,13 +4,35 @@
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
-TOP=$(readlink -f "$SCRIPT_DIR/../")
+if [ -z "$TOP" ]; then
+ TOP=$(readlink -f "$SCRIPT_DIR/../")
+ if [ -f "$TOP/configure.ac" ]; then
+ # inside git
+ TEST_TOP="$TOP/tests/"
+ INTERNAL_BIN="$TOP"
+ else
+ # external, defaults to system binaries
+ TOP=$(dirname `which btrfs`)
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+ fi
+else
+ # assume external, TOP set from commandline
+ TEST_TOP="$SCRIPT_DIR"
+ INTERNAL_BIN="$TEST_TOP"
+fi
+if ! [ -x "$TOP/btrfs" ]; then
+ echo "ERROR: cannot execute btrfs from TOP=$TOP"
+ exit 1
+fi
TEST_DEV=${TEST_DEV:-}
-RESULTS="$TOP/tests/mkfs-tests-results.txt"
-IMAGE="$TOP/tests/test.img"
+RESULTS="$TEST_TOP/mkfs-tests-results.txt"
+IMAGE="$TEST_TOP/test.img"
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
+export INTERNAL_BIN
+export TEST_TOP
export TOP
export RESULTS
export LANG
@@ -25,7 +47,7 @@ check_kernel_support
# The tests are driven by their custom script called 'test.sh'
-for i in $(find "$TOP/tests/mkfs-tests" -maxdepth 1 -mindepth 1 -type d \
+for i in $(find "$TEST_TOP/mkfs-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
echo " [TEST/mkfs] $(basename $i)"
@@ -40,5 +62,5 @@ do
_fail "test failed for case $(basename $i)"
fi
fi
- cd "$TOP"
+ cd "$TEST_TOP"
done
diff --git a/tests/mkfs-tests/001-basic-profiles/test.sh b/tests/mkfs-tests/001-basic-profiles/test.sh
index 854ee007..b84016f7 100755
--- a/tests/mkfs-tests/001-basic-profiles/test.sh
+++ b/tests/mkfs-tests/001-basic-profiles/test.sh
@@ -2,7 +2,7 @@
# test various blockgroup profile combinations, use loop devices as block
# devices
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
index 37846234..4bc9c262 100755
--- a/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
+++ b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
@@ -2,7 +2,7 @@
#
# Verify that we do not force mixed block groups on small volumes anymore
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
diff --git a/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh b/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh
index 074fc22e..f5327338 100755
--- a/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh
+++ b/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh
@@ -2,7 +2,7 @@
#
# Mixed mode needs equal sectorsize and nodesize
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
diff --git a/tests/mkfs-tests/004-rootdir-keeps-size/test.sh b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
index 7038c8ea..4a84e6db 100755
--- a/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
+++ b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# make sure that mkfs.btrfs --rootsize does not change size of the image
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
@@ -16,7 +16,7 @@ test_mkfs_with_size() {
run_check truncate -s$size $TEST_DEV
imgsize=$(run_check_stdout stat --format=%s $TEST_DEV)
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
- --rootdir $TOP/Documentation \
+ --rootdir $INTERNAL_BIN/Documentation \
$TEST_DEV
tmp=$(run_check_stdout stat --format=%s $TEST_DEV)
if ! [ "$imgsize" = "$tmp" ]; then
diff --git a/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
index 5bdf50e0..55ce676e 100755
--- a/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
+++ b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# a long device name must pass the SSD test
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
diff --git a/tests/mkfs-tests/006-partitioned-loopdev/test.sh b/tests/mkfs-tests/006-partitioned-loopdev/test.sh
index 0c77e5cd..06c254fd 100755
--- a/tests/mkfs-tests/006-partitioned-loopdev/test.sh
+++ b/tests/mkfs-tests/006-partitioned-loopdev/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# recognize partitioned loop devices
-source $TOP/tests/common
+source "$TEST_TOP/common"
if ! losetup --help | grep -q 'partscan'; then
_not_run "losetup --partscan not available"
diff --git a/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
index 3980414f..f7e88c9a 100755
--- a/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
+++ b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# iterate over nodesize and sectorsize combinations
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh b/tests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh
index 955cd2b1..9cc2f9e0 100755
--- a/tests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh
+++ b/tests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh
@@ -4,7 +4,7 @@
# only do mkfs and fsck check, no mounting as
# sub/multi-pagesize is not supported yet
-source $TOP/tests/common
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/mkfs-tests/009-special-files-for-rootdir/test.sh b/tests/mkfs-tests/009-special-files-for-rootdir/test.sh
index d327a0d9..a94d5209 100755
--- a/tests/mkfs-tests/009-special-files-for-rootdir/test.sh
+++ b/tests/mkfs-tests/009-special-files-for-rootdir/test.sh
@@ -6,7 +6,7 @@
#
# Note: sock type is skipped in this test
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
diff --git a/tests/mkfs-tests/010-minimal-size/test.sh b/tests/mkfs-tests/010-minimal-size/test.sh
new file mode 100755
index 00000000..62f8e16a
--- /dev/null
+++ b/tests/mkfs-tests/010-minimal-size/test.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+# test if the reported minimal size of mkfs.btrfs is valid
+
+source "$TOP/tests/common"
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+
+do_test()
+{
+ # 1M should be small enough to reliably fail, we use the output to get
+ # the minimal device size for the given option combination
+ prepare_test_dev 1M
+ output=$(run_mustfail_stdout "mkfs.btrfs for small image" \
+ "$TOP/mkfs.btrfs" -f $@ "$TEST_DEV")
+ good_size=$(echo "$output" | grep -oP "(?<=is )\d+")
+
+ prepare_test_dev "$good_size"
+ echo "Minimal device size is $good_size" >> "$RESULTS"
+ run_check $TOP/mkfs.btrfs -f $@ "$TEST_DEV"
+ run_check_mount_test_dev
+ run_check_umount_test_dev
+}
+
+do_test -n 4k -m single -d single
+do_test -n 4k -m single -d dup
+do_test -n 4k -m dup -d single
+do_test -n 4k -m dup -d dup
+
+do_test -n 8k -m single -d single
+do_test -n 8k -m single -d dup
+do_test -n 8k -m dup -d single
+do_test -n 8k -m dup -d dup
+
+do_test -n 16k -m single -d single
+do_test -n 16k -m single -d dup
+do_test -n 16k -m dup -d single
+do_test -n 16k -m dup -d dup
+
+# Temporary: disable the following tests as they fail inside travis but run
+# fine otherwise. This is probably caused by kernel version, 4.4 fails and 4.14
+# is ok.
+#
+# root_helper mount -t btrfs -o loop /home/travis/build/kdave/btrfs-progs/tests/test.img /home/travis/build/kdave/btrfs-progs/tests/mnt
+# mount: No space left on device
+# failed: root_helper mount -t btrfs -o loop /home/travis/build/kdave/btrfs-progs/tests/test.img /home/travis/build/kdave/btrfs-progs/tests/mnt
+# test failed for case 010-minimal-size
+#
+if [ "$TRAVIS" = true ]; then
+ exit 0
+fi
+
+do_test -n 32k -m single -d single
+do_test -n 32k -m single -d dup
+do_test -n 32k -m dup -d single
+do_test -n 32k -m dup -d dup
+
+do_test -n 64k -m single -d single
+do_test -n 64k -m single -d dup
+do_test -n 64k -m dup -d single
+do_test -n 64k -m dup -d dup
diff --git a/tests/mkfs-tests/011-rootdir-create-file/test.sh b/tests/mkfs-tests/011-rootdir-create-file/test.sh
new file mode 100755
index 00000000..c04f711d
--- /dev/null
+++ b/tests/mkfs-tests/011-rootdir-create-file/test.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# Regression test for mkfs.btrfs --rootdir on non-existent file.
+# Expected behavior: it should create a new file if destination doesn't exist
+# Regression 460e93f25754 ("btrfs-progs: mkfs: check the status of file at mkfs")
+
+source "$TOP/tests/common"
+
+check_prereq mkfs.btrfs
+
+tmp=$(mktemp -d --tmpdir btrfs-progs-mkfs.rootdirXXXXXXX)
+# we can't use TEST_DEV, a file is needed
+img=$(mktemp btrfs-progs-mkfs.rootdirXXXXXXX)
+run_check "$TOP/mkfs.btrfs" -f --rootdir "$TOP/Documentation/" "$img"
+
+rm -rf -- "$img"
diff --git a/tests/mkfs-tests/012-rootdir-no-shrink/test.sh b/tests/mkfs-tests/012-rootdir-no-shrink/test.sh
new file mode 100755
index 00000000..c1cb04f5
--- /dev/null
+++ b/tests/mkfs-tests/012-rootdir-no-shrink/test.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+# Test if mkfs.btrfs --rootdir will skip shrinking correctly
+
+source "$TOP/tests/common"
+
+check_prereq mkfs.btrfs
+
+setup_root_helper
+
+fs_size=$((512 * 1024 * 1024))
+bs=$((1024 * 1024))
+tmp=$(mktemp -d --tmpdir btrfs-progs-mkfs.rootdirXXXXXXX)
+
+prepare_test_dev $fs_size
+
+# No shrink case
+
+run_check "$TOP/mkfs.btrfs" -f --rootdir "$tmp" "$TEST_DEV"
+run_check_mount_test_dev
+
+# We should be able to write at least half of the fs size data since the fs is
+# not shrunk
+run_check $SUDO_HELPER dd if=/dev/zero bs=$bs count=$(($fs_size / $bs / 2)) \
+ of="$TEST_MNT/file"
+
+run_check_umount_test_dev
+
+# Shrink case
+
+run_check "$TOP/mkfs.btrfs" -f --rootdir "$tmp" --shrink "$TEST_DEV"
+run_check_mount_test_dev
+
+run_mustfail "mkfs.btrfs for shrink rootdir" \
+ $SUDO_HELPER dd if=/dev/zero bs=$bs count=$(($fs_size / $bs / 2)) \
+ of="$TEST_MNT/file"
+
+run_check_umount_test_dev
+
+rm -rf -- "$tmp"
diff --git a/tests/mkfs-tests/013-reserved-1M-for-single/test.sh b/tests/mkfs-tests/013-reserved-1M-for-single/test.sh
new file mode 100755
index 00000000..1490df2c
--- /dev/null
+++ b/tests/mkfs-tests/013-reserved-1M-for-single/test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# Test if "-m single" or "--mixed" can cause dev extent to use the reserved 1M
+# range
+#
+# Other profiles will cause mkfs.btrfs to allocate new meta/sys chunks
+# using btrfs_alloc_chunk() which won't use the 0~1M range, so other profiles
+# are safe, but we test them nevertheless.
+
+source "$TOP/tests/common"
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+prepare_test_dev
+
+do_one_test ()
+{
+ run_check "$TOP/mkfs.btrfs" -f "$@" "$TEST_DEV"
+
+ # Use dev-extent tree to find first device extent
+ first_dev_extent=$(run_check_stdout "$TOP/btrfs" inspect-internal \
+ dump-tree -t device "$TEST_DEV" | \
+ grep -oP '(?<=DEV_EXTENT )[[:digit:]]*' | head -n1)
+
+ if [ -z $first_dev_extent ]; then
+ _fail "failed to get first device extent"
+ fi
+
+ echo "first dev extent starts at $first_dev_extent" >> "$RESULTS"
+ echo "reserved range is [0, $(( 1024 * 1024)))" >> "$RESULTS"
+ # First device extent should not start below 1M
+ if [ $first_dev_extent -lt $(( 1024 * 1024 )) ]; then
+ _fail "first device extent occupies reserved 0~1M range"
+ fi
+}
+
+do_one_test --mixed
+do_one_test -m single
+
+do_one_test
+do_one_test -m dup
+do_one_test -d dup
+do_one_test -m dup -d dup
+do_one_test --mixed -m dup -d dup
diff --git a/tests/testsuite-files b/tests/testsuite-files
new file mode 100644
index 00000000..d75e2356
--- /dev/null
+++ b/tests/testsuite-files
@@ -0,0 +1,22 @@
+F README.md
+G Documentation/
+F testsuite-id
+F ../fssum
+F ../btrfs-corrupt-block
+F common
+F common.convert
+F common.local
+G tests/cli-tests/
+F cli-tests.sh
+G tests/convert-tests/
+F convert-tests.sh
+G tests/fsck-tests/
+F fsck-tests.sh
+G tests/fuzz-tests/
+F fuzz-tests.sh
+G tests/misc-tests/
+F misc-tests.sh
+G tests/mkfs-tests/
+F mkfs-tests.sh
+F scan-results.sh
+F test-console.sh
diff --git a/travis/build-default b/travis/build-default
new file mode 100755
index 00000000..b43302d9
--- /dev/null
+++ b/travis/build-default
@@ -0,0 +1,13 @@
+#!/bin/sh
+# usage: $0 [optional arguments to configure]
+
+if ! [ -f "./autogen.sh" ]; then
+ echo "ERROR: cannot find autogen.sh, run from the top level directory"
+ exit 1
+fi
+
+set -e
+
+./autogen.sh
+./configure "$@"
+make
diff --git a/travis/build-dep-reiserfs b/travis/build-dep-reiserfs
new file mode 100755
index 00000000..b32b8058
--- /dev/null
+++ b/travis/build-dep-reiserfs
@@ -0,0 +1,15 @@
+#!/bin/sh
+# download, build and install reiserfs library
+
+version=3.6.27
+
+set -e
+
+mkdir tmp-reiser
+cd tmp-reiser
+wget https://www.kernel.org/pub/linux/kernel/people/jeffm/reiserfsprogs/v${version}/reiserfsprogs-${version}.tar.xz
+tar xf reiserfsprogs-${version}.tar.xz
+cd reiserfsprogs-${version}
+./configure --prefix=/usr
+make all
+sudo make install
diff --git a/travis/build-dep-zstd b/travis/build-dep-zstd
new file mode 100755
index 00000000..22369dd3
--- /dev/null
+++ b/travis/build-dep-zstd
@@ -0,0 +1,14 @@
+#!/bin/sh
+# download, build and install the zstd library
+
+version=1.3.3
+
+set -e
+
+mkdir tmp-zstd
+cd tmp-zstd
+wget https://github.com/facebook/zstd/archive/v${version}.tar.gz
+tar xf v${version}.tar.gz
+cd zstd-${version}
+make
+sudo make install PREFIX=/usr
diff --git a/travis/images/ci-musl-x86_64/Dockerfile b/travis/images/ci-musl-x86_64/Dockerfile
new file mode 100644
index 00000000..dbbe4dc1
--- /dev/null
+++ b/travis/images/ci-musl-x86_64/Dockerfile
@@ -0,0 +1,10 @@
+FROM alpine:edge
+
+WORKDIR /tmp
+
+RUN apk update
+RUN apk add linux-headers musl-dev util-linux-dev bash
+RUN apk add attr-dev acl-dev e2fsprogs-dev zlib-dev lzo-dev zstd-dev
+RUN apk add autoconf automake make gcc tar gzip
+
+COPY ./test-build .
diff --git a/travis/images/test-build b/travis/images/test-build
new file mode 100755
index 00000000..2f05fbf5
--- /dev/null
+++ b/travis/images/test-build
@@ -0,0 +1,15 @@
+#!/bin/sh
+# usage: $0 [branch name] [configure parameters]
+
+urlbase="https://github.com/kdave/btrfs-progs/archive/"
+branch=${1:-devel}
+url=${urlbase}${branch}.tar.gz
+
+shift
+
+echo "btrfs-progs build test of branch ${branch}"
+cd /tmp
+wget "$url" -O ${branch}.tar.gz
+tar xf ${branch}.tar.gz
+cd btrfs-progs-${branch}
+travis/build-default "$@"
diff --git a/utils.c b/utils.c
index 524f463d..e9cb3a82 100644
--- a/utils.c
+++ b/utils.c
@@ -298,7 +298,7 @@ static int btrfs_wipe_existing_sb(int fd)
memset(buf, 0, len);
ret = pwrite(fd, buf, len, offset);
if (ret < 0) {
- error("cannot wipe existing superblock: %s", strerror(errno));
+ error("cannot wipe existing superblock: %m");
ret = -1;
} else if (ret != len) {
error("cannot wipe existing superblock: wrote %d of %zd", ret, len);
@@ -320,7 +320,7 @@ int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret,
ret = fstat(fd, &st);
if (ret < 0) {
- error("unable to stat %s: %s", file, strerror(errno));
+ error("unable to stat %s: %m", file);
return 1;
}
@@ -447,7 +447,7 @@ int is_mount_point(const char *path)
return ret;
}
-static int is_reg_file(const char *path)
+int is_reg_file(const char *path)
{
struct stat statbuf;
@@ -456,6 +456,21 @@ static int is_reg_file(const char *path)
return S_ISREG(statbuf.st_mode);
}
+int is_path_exist(const char *path)
+{
+ struct stat statbuf;
+ int ret;
+
+ ret = stat(path, &statbuf);
+ if (ret < 0) {
+ if (errno == ENOENT)
+ return 0;
+ else
+ return -errno;
+ }
+ return 1;
+}
+
/*
* This function checks if the given input parameter is
* an uuid or a path
@@ -519,7 +534,7 @@ int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
fd = open(dev, O_RDONLY);
if (fd < 0) {
ret = -errno;
- error("cannot open %s: %s", dev, strerror(errno));
+ error("cannot open %s: %m", dev);
goto out;
}
@@ -557,8 +572,8 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose)
return -1;
}
ret = open_file_or_dir(mp, dirstream);
- error_on(verbose && ret < 0, "can't access '%s': %s",
- path, strerror(errno));
+ error_on(verbose && ret < 0, "can't access '%s': %m",
+ path);
} else {
ret = btrfs_open_dir(path, dirstream, 1);
}
@@ -578,8 +593,7 @@ int btrfs_open(const char *path, DIR **dirstream, int verbose, int dir_only)
int ret;
if (statfs(path, &stfs) != 0) {
- error_on(verbose, "cannot access '%s': %s", path,
- strerror(errno));
+ error_on(verbose, "cannot access '%s': %m", path);
return -1;
}
@@ -589,8 +603,7 @@ int btrfs_open(const char *path, DIR **dirstream, int verbose, int dir_only)
}
if (stat(path, &st) != 0) {
- error_on(verbose, "cannot access '%s': %s", path,
- strerror(errno));
+ error_on(verbose, "cannot access '%s': %m", path);
return -1;
}
@@ -601,8 +614,7 @@ int btrfs_open(const char *path, DIR **dirstream, int verbose, int dir_only)
ret = open_file_or_dir(path, dirstream);
if (ret < 0) {
- error_on(verbose, "cannot access '%s': %s", path,
- strerror(errno));
+ error_on(verbose, "cannot access '%s': %m", path);
}
return ret;
@@ -804,14 +816,9 @@ static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices,
const char* file)
{
int ret;
- struct list_head *head;
- struct list_head *cur;
struct btrfs_device *device;
- head = &fs_devices->devices;
- list_for_each(cur, head) {
- device = list_entry(cur, struct btrfs_device, dev_list);
-
+ list_for_each_entry(device, &fs_devices->devices, dev_list) {
if((ret = is_same_loop_file(device->name, file)))
return ret;
}
@@ -888,8 +895,7 @@ int check_mounted(const char* file)
fd = open(file, O_RDONLY);
if (fd < 0) {
- error("mount check: cannot open %s: %s", file,
- strerror(errno));
+ error("mount check: cannot open %s: %m", file);
return -errno;
}
@@ -978,16 +984,14 @@ int btrfs_register_one_device(const char *fname)
fd = open("/dev/btrfs-control", O_RDWR);
if (fd < 0) {
warning(
- "failed to open /dev/btrfs-control, skipping device registration: %s",
- strerror(errno));
+ "failed to open /dev/btrfs-control, skipping device registration: %m");
return -errno;
}
memset(&args, 0, sizeof(args));
strncpy_null(args.name, fname);
ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
if (ret < 0) {
- error("device scan failed on '%s': %s", fname,
- strerror(errno));
+ error("device scan failed on '%s': %m", fname);
ret = -errno;
}
close(fd);
@@ -1257,15 +1261,14 @@ static int set_label_mounted(const char *mount_path, const char *labelp)
fd = open(mount_path, O_RDONLY | O_NOATIME);
if (fd < 0) {
- error("unable to access %s: %s", mount_path, strerror(errno));
+ error("unable to access %s: %m", mount_path);
return -1;
}
memset(label, 0, sizeof(label));
__strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1);
if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) {
- error("unable to set label of %s: %s", mount_path,
- strerror(errno));
+ error("unable to set label of %s: %m", mount_path);
close(fd);
return -1;
}
@@ -1313,7 +1316,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
fd = open(mount_path, O_RDONLY | O_NOATIME);
if (fd < 0) {
- error("unable to access %s: %s", mount_path, strerror(errno));
+ error("unable to access %s: %m", mount_path);
return -1;
}
@@ -1321,8 +1324,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label);
if (ret < 0) {
if (errno != ENOTTY)
- error("unable to get label of %s: %s", mount_path,
- strerror(errno));
+ error("unable to get label of %s: %m", mount_path);
ret = -errno;
close(fd);
return ret;
@@ -1539,10 +1541,16 @@ int open_file_or_dir(const char *fname, DIR **dirstream)
void close_file_or_dir(int fd, DIR *dirstream)
{
- if (dirstream)
+ int old_errno;
+
+ old_errno = errno;
+ if (dirstream) {
closedir(dirstream);
- else if (fd >= 0)
+ } else if (fd >= 0) {
close(fd);
+ }
+
+ errno = old_errno;
}
int get_device_info(int fd, u64 devid,
@@ -1660,7 +1668,7 @@ int get_fs_info(const char *path, struct btrfs_ioctl_fs_info_args *fi_args,
fd = open(path, O_RDONLY);
if (fd < 0) {
ret = -errno;
- error("cannot open %s: %s", path, strerror(errno));
+ error("cannot open %s: %m", path);
goto out;
}
ret = check_mounted_where(fd, path, mp, sizeof(mp),
@@ -1980,7 +1988,7 @@ int btrfs_scan_devices(void)
fd = open(path, O_RDONLY);
if (fd < 0) {
- error("cannot open %s: %s", path, strerror(errno));
+ error("cannot open %s: %m", path);
continue;
}
ret = btrfs_scan_one_device(fd, path, &tmp_devices,
@@ -2701,3 +2709,21 @@ unsigned long total_memory(void)
}
return si.totalram * si.mem_unit; /* bytes */
}
+
+void print_device_info(struct btrfs_device *device, char *prefix)
+{
+ if (prefix)
+ printf("%s", prefix);
+ printf("Device: id = %llu, name = %s\n",
+ device->devid, device->name);
+}
+
+void print_all_devices(struct list_head *devices)
+{
+ struct btrfs_device *dev;
+
+ printf("All Devices:\n");
+ list_for_each_entry(dev, devices, dev_list)
+ print_device_info(dev, "\t");
+ printf("\n");
+}
diff --git a/utils.h b/utils.h
index a82d46f6..b871c9ff 100644
--- a/utils.h
+++ b/utils.h
@@ -122,6 +122,8 @@ int set_label(const char *btrfs_dev, const char *label);
char *__strncpy_null(char *dest, const char *src, size_t n);
int is_block_device(const char *file);
int is_mount_point(const char *file);
+int is_path_exist(const char *file);
+int is_reg_file(const char *path);
int check_arg_type(const char *input);
int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose);
int btrfs_open(const char *path, DIR **dirstream, int verbose, int dir_only);
@@ -171,6 +173,9 @@ int prefixcmp(const char *str, const char *prefix);
unsigned long total_memory(void);
+void print_device_info(struct btrfs_device *device, char *prefix);
+void print_all_devices(struct list_head *devices);
+
/*
* Global program state, configurable by command line and available to
* functions without extra context passing.
diff --git a/uuid-tree.c b/uuid-tree.c
index 8d0b9173..320eb67e 100644
--- a/uuid-tree.c
+++ b/uuid-tree.c
@@ -60,9 +60,9 @@ static int btrfs_uuid_tree_lookup_any(int fd, const u8 *uuid, u8 type,
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_arg);
if (ret < 0) {
fprintf(stderr,
- "ioctl(BTRFS_IOC_TREE_SEARCH, uuid, key %016llx, UUID_KEY, %016llx) ret=%d, error: %s\n",
+ "ioctl(BTRFS_IOC_TREE_SEARCH, uuid, key %016llx, UUID_KEY, %016llx) ret=%d, error: %m\n",
(unsigned long long)key_objectid,
- (unsigned long long)key_offset, ret, strerror(errno));
+ (unsigned long long)key_offset, ret);
ret = -ENOENT;
goto out;
}
diff --git a/version.sh b/version.sh
deleted file mode 100755
index bcdad348..00000000
--- a/version.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-#
-# determine-version -- report a useful version for releases
-#
-# Copyright 2008, Aron Griffis <agriffis@n01se.net>
-# Copyright 2008, Oracle
-# Released under the GNU GPLv2
-
-v="v4.14.1"
-
-opt=$1
-
-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
-
-if [ "$opt" = "--configure" ]; then
- # Omit the trailing newline, so that m4_esyscmd can use the result directly.
- echo -n "$v"
-else
- echo "$v"
-fi
-
-exit 0
-
diff --git a/volumes.c b/volumes.c
index ce3a5405..edad367b 100644
--- a/volumes.c
+++ b/volumes.c
@@ -58,10 +58,8 @@ 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);
+ list_for_each_entry(dev, head, dev_list) {
if (dev->devid == devid &&
!memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE)) {
return dev;
@@ -72,11 +70,9 @@ static struct btrfs_device *__find_device(struct list_head *head, u64 devid,
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);
+ list_for_each_entry(fs_devices, &fs_uuids, list) {
if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0)
return fs_devices;
}
@@ -185,8 +181,8 @@ again:
struct btrfs_device, dev_list);
if (device->fd != -1) {
if (fsync(device->fd) == -1) {
- warning("fsync on device %llu failed: %s",
- device->devid, strerror(errno));
+ warning("fsync on device %llu failed: %m",
+ device->devid);
ret = -errno;
}
if (posix_fadvise(device->fd, 0, 0, POSIX_FADV_DONTNEED))
@@ -234,13 +230,10 @@ void btrfs_close_all_devices(void)
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);
+ list_for_each_entry(device, &fs_devices->devices, dev_list) {
if (!device->name) {
printk("no name for device %llu, skip it now\n", device->devid);
continue;
@@ -249,8 +242,7 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, int flags)
fd = open(device->name, flags);
if (fd < 0) {
ret = -errno;
- error("cannot open device '%s': %s", device->name,
- strerror(errno));
+ error("cannot open device '%s': %m", device->name);
goto fail;
}
@@ -316,9 +308,9 @@ int btrfs_scan_one_device(int fd, const char *path,
* But if we don't find suitable free space, it is used to store the size of
* the max free space.
*/
-static int find_free_dev_extent_start(struct btrfs_trans_handle *trans,
- struct btrfs_device *device, u64 num_bytes,
- u64 search_start, u64 *start, u64 *len)
+static int find_free_dev_extent_start(struct btrfs_device *device,
+ u64 num_bytes, u64 search_start,
+ u64 *start, u64 *len)
{
struct btrfs_key key;
struct btrfs_root *root = device->dev_root;
@@ -457,20 +449,17 @@ out:
return ret;
}
-int find_free_dev_extent(struct btrfs_trans_handle *trans,
- struct btrfs_device *device, u64 num_bytes,
- u64 *start)
+static int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
+ u64 *start)
{
/* FIXME use last free of some kind */
- return find_free_dev_extent_start(trans, device,
- num_bytes, 0, start, NULL);
+ return find_free_dev_extent_start(device, num_bytes, 0, start, NULL);
}
static 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 convert)
+ u64 chunk_offset, u64 num_bytes, u64 *start,
+ int convert)
{
int ret;
struct btrfs_path *path;
@@ -488,8 +477,7 @@ static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
* is responsible to make sure it's free.
*/
if (!convert) {
- ret = find_free_dev_extent(trans, device, num_bytes,
- start);
+ ret = find_free_dev_extent(device, num_bytes, start);
if (ret)
goto err;
}
@@ -504,8 +492,9 @@ static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
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_tree(leaf, extent, BTRFS_CHUNK_TREE_OBJECTID);
+ btrfs_set_dev_extent_chunk_objectid(leaf, extent,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
btrfs_set_dev_extent_chunk_offset(leaf, extent, chunk_offset);
write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid,
@@ -837,7 +826,7 @@ error:
return ret;
}
-#define BTRFS_MAX_DEVS(r) ((BTRFS_LEAF_DATA_SIZE(r) \
+#define BTRFS_MAX_DEVS(r) ((BTRFS_LEAF_DATA_SIZE(r->fs_info) \
- sizeof(struct btrfs_item) \
- sizeof(struct btrfs_chunk)) \
/ sizeof(struct btrfs_stripe) + 1)
@@ -1042,9 +1031,7 @@ again:
(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,
+ ret = btrfs_alloc_dev_extent(trans, device, key.offset,
calc_size, &dev_offset, 0);
if (ret < 0)
goto out_chunk_map;
@@ -1179,9 +1166,7 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
while (index < num_stripes) {
struct btrfs_stripe *stripe;
- ret = btrfs_alloc_dev_extent(trans, device,
- info->chunk_root->root_key.objectid,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset,
+ ret = btrfs_alloc_dev_extent(trans, device, key.offset,
calc_size, &dev_offset, convert);
BUG_ON(ret);
@@ -1726,7 +1711,7 @@ int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
return -EIO;
}
if (btrfs_chunk_sector_size(leaf, chunk) != sectorsize) {
- error("invalid chunk sectorsize %llu",
+ error("invalid chunk sectorsize %llu",
(unsigned long long)btrfs_chunk_sector_size(leaf, chunk));
return -EIO;
}