summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml67
-rw-r--r--CHANGES110
-rw-r--r--Documentation/ReleaseChecklist37
-rw-r--r--Documentation/btrfs-check.asciidoc16
-rw-r--r--Documentation/btrfs-convert.asciidoc3
-rw-r--r--Documentation/btrfs-device.asciidoc21
-rw-r--r--Documentation/btrfs-filesystem.asciidoc2
-rw-r--r--Documentation/btrfs-man5.asciidoc47
-rw-r--r--Documentation/btrfs-qgroup.asciidoc4
-rw-r--r--Documentation/btrfs-quota.asciidoc4
-rw-r--r--Documentation/btrfs-receive.asciidoc46
-rw-r--r--Documentation/btrfs-scrub.asciidoc13
-rw-r--r--Documentation/btrfs-subvolume.asciidoc2
-rw-r--r--Documentation/btrfstune.asciidoc2
-rw-r--r--Documentation/mkfs.btrfs.asciidoc27
-rw-r--r--INSTALL27
-rw-r--r--Makefile.in145
-rw-r--r--README.md69
-rw-r--r--backref.c10
-rw-r--r--btrfs-calc-size.c3
-rw-r--r--btrfs-corrupt-block.c64
-rwxr-xr-xbtrfs-debugfs7
-rw-r--r--btrfs-fragments.c2
-rw-r--r--btrfs-list.c280
-rw-r--r--btrfs-list.h16
-rw-r--r--btrfs-map-logical.c2
-rw-r--r--btrfs-show-super.c3
-rw-r--r--btrfstune.c89
-rw-r--r--chunk-recover.c38
-rw-r--r--cmds-check.c2446
-rw-r--r--cmds-device.c94
-rw-r--r--cmds-fi-du.c34
-rw-r--r--cmds-fi-usage.c34
-rw-r--r--cmds-filesystem.c113
-rw-r--r--cmds-inspect-dump-super.c37
-rw-r--r--cmds-inspect-dump-tree.c71
-rw-r--r--cmds-inspect-tree-stats.c40
-rw-r--r--cmds-inspect.c15
-rw-r--r--cmds-property.c8
-rw-r--r--cmds-qgroup.c45
-rw-r--r--cmds-quota.c40
-rw-r--r--cmds-receive.c465
-rw-r--r--cmds-replace.c2
-rw-r--r--cmds-restore.c262
-rw-r--r--cmds-scrub.c9
-rw-r--r--cmds-send.c255
-rw-r--r--cmds-subvolume.c49
-rw-r--r--config.h.in3
-rwxr-xr-xconfig/config.guess201
-rwxr-xr-xconfig/config.sub51
-rwxr-xr-xconfig/install-sh366
-rwxr-xr-xconfigure35
-rw-r--r--configure.ac12
-rw-r--r--convert/main.c (renamed from btrfs-convert.c)202
-rw-r--r--ctree.c4
-rw-r--r--ctree.h232
-rw-r--r--debian/changelog6
-rw-r--r--dir-item.c10
-rw-r--r--dir-test.c6
-rw-r--r--disk-io.c94
-rw-r--r--disk-io.h16
-rw-r--r--extent-tree.c65
-rw-r--r--extent_io.h3
-rw-r--r--file-item.c28
-rw-r--r--free-space-cache.c129
-rw-r--r--free-space-cache.h2
-rw-r--r--free-space-tree.c86
-rw-r--r--free-space-tree.h1
-rw-r--r--hash.h10
-rw-r--r--help.c2
-rw-r--r--image/main.c (renamed from btrfs-image.c)416
-rw-r--r--inode-item.c20
-rw-r--r--inode-map.c4
-rw-r--r--ioctl-test.c269
-rw-r--r--ioctl.h108
-rw-r--r--kerncompat.h75
-rw-r--r--kernel-lib/bitops.h (renamed from bitops.h)0
-rw-r--r--kernel-lib/crc32c.c (renamed from crc32c.c)4
-rw-r--r--kernel-lib/crc32c.h (renamed from crc32c.h)0
-rw-r--r--kernel-lib/interval_tree_generic.h (renamed from interval_tree_generic.h)0
-rw-r--r--kernel-lib/list.h (renamed from list.h)0
-rw-r--r--kernel-lib/list_sort.c (renamed from list_sort.c)0
-rw-r--r--kernel-lib/list_sort.h (renamed from list_sort.h)0
-rw-r--r--kernel-lib/radix-tree.c (renamed from radix-tree.c)0
-rw-r--r--kernel-lib/radix-tree.h (renamed from radix-tree.h)0
-rw-r--r--kernel-lib/rbtree.c (renamed from rbtree.c)0
-rw-r--r--kernel-lib/rbtree.h (renamed from rbtree.h)0
-rw-r--r--kernel-lib/rbtree_augmented.h (renamed from rbtree_augmented.h)0
-rw-r--r--library-test.c8
-rw-r--r--mkfs/main.c (renamed from mkfs.c)129
-rw-r--r--print-tree.c842
-rw-r--r--qgroup-verify.c100
-rw-r--r--qgroup.c130
-rw-r--r--qgroup.h5
-rw-r--r--quick-test.c2
-rw-r--r--raid56.c (renamed from raid6.c)63
-rw-r--r--random-test.c6
-rw-r--r--root-tree.c35
-rw-r--r--send-dump.c338
-rw-r--r--send-dump.h29
-rw-r--r--send-stream.c325
-rw-r--r--send-test.c447
-rw-r--r--send-utils.c35
-rw-r--r--send-utils.h15
-rw-r--r--string-table.c4
-rw-r--r--string-table.h4
-rw-r--r--super-recover.c39
-rw-r--r--tests/README.md22
-rwxr-xr-xtests/build-tests.sh88
-rwxr-xr-xtests/clean-tests.sh8
-rwxr-xr-xtests/cli-tests.sh22
-rwxr-xr-xtests/cli-tests/001-btrfs/test.sh17
-rwxr-xr-xtests/cli-tests/002-balance-full-no-filters/test.sh12
-rwxr-xr-xtests/cli-tests/003-fi-resize-args/test.sh32
-rwxr-xr-xtests/cli-tests/004-send-parent-multi-subvol/test.sh34
-rwxr-xr-xtests/cli-tests/005-qgroup-show/test.sh21
-rwxr-xr-xtests/cli-tests/006-qgroup-show-sync/test.sh30
-rw-r--r--tests/common313
-rw-r--r--tests/common.convert186
-rw-r--r--tests/common.local25
-rwxr-xr-xtests/convert-tests.sh19
-rwxr-xr-xtests/convert-tests/004-ext2-backup-superblock-ranges/test.sh3
-rwxr-xr-xtests/convert-tests/005-delete-all-rollback/test.sh13
-rwxr-xr-xtests/convert-tests/009-common-inode-flags/test.sh34
-rwxr-xr-xtests/fsck-tests.sh22
-rwxr-xr-xtests/fsck-tests/013-extent-tree-rebuild/test.sh5
-rw-r--r--tests/fsck-tests/022-qgroup-rescan-halfway/qgroup_rescan_halfway.raw.xzbin0 -> 59360 bytes
-rwxr-xr-xtests/fsck-tests/022-qgroup-rescan-halfway/test.sh19
-rw-r--r--tests/fsck-tests/023-qgroup-stack-overflow/quota_balance_loop_backref.raw.xzbin0 -> 55708 bytes
-rwxr-xr-xtests/fsck-tests/023-qgroup-stack-overflow/test.sh17
-rwxr-xr-xtests/fsck-tests/024-clear-space-cache/test.sh48
-rwxr-xr-xtests/fuzz-tests.sh19
-rwxr-xr-xtests/fuzz-tests/004-simple-dump-tree/test.sh18
-rwxr-xr-xtests/fuzz-tests/005-simple-dump-super/test.sh19
-rwxr-xr-xtests/fuzz-tests/006-simple-tree-stats/test.sh18
-rwxr-xr-xtests/fuzz-tests/007-simple-super-recover/test.sh20
-rwxr-xr-xtests/fuzz-tests/008-simple-chunk-recover/test.sh20
-rwxr-xr-xtests/fuzz-tests/009-simple-zero-log/test.sh20
-rw-r--r--tests/fuzz-tests/images/bko-156731.raw.txt83
-rw-r--r--tests/fuzz-tests/images/bko-156731.raw.xzbin0 -> 3824 bytes
-rw-r--r--tests/fuzz-tests/images/bko-156741.raw.txt131
-rw-r--r--tests/fuzz-tests/images/bko-156741.raw.xzbin0 -> 3796 bytes
-rw-r--r--tests/fuzz-tests/images/bko-156811-bad-parent-ref-qgroup-verify.raw.txt94
-rw-r--r--tests/fuzz-tests/images/bko-156811-bad-parent-ref-qgroup-verify.raw.xzbin0 -> 3832 bytes
-rw-r--r--tests/fuzz-tests/images/bko-161811.raw.txt81
-rw-r--r--tests/fuzz-tests/images/bko-161811.raw.xzbin0 -> 10960 bytes
-rw-r--r--tests/fuzz-tests/images/bko-161821.raw.txt42
-rw-r--r--tests/fuzz-tests/images/bko-161821.raw.xzbin0 -> 10596 bytes
-rw-r--r--tests/fuzz-tests/images/bko-166361-blocksize-zero.raw.txt93
-rw-r--r--tests/fuzz-tests/images/bko-166361-blocksize-zero.raw.xzbin0 -> 3840 bytes
-rw-r--r--tests/fuzz-tests/images/bko-167551.raw.txt29
-rw-r--r--tests/fuzz-tests/images/bko-167551.raw.xzbin0 -> 10808 bytes
-rw-r--r--tests/fuzz-tests/images/bko-167781.raw.txt297
-rw-r--r--tests/fuzz-tests/images/bko-167781.raw.xzbin0 -> 3856 bytes
-rw-r--r--tests/fuzz-tests/images/bko-167921.raw.txt55
-rw-r--r--tests/fuzz-tests/images/bko-167921.raw.xzbin0 -> 10956 bytes
-rw-r--r--tests/fuzz-tests/images/bko-168301.raw.txt51
-rw-r--r--tests/fuzz-tests/images/bko-168301.raw.xzbin0 -> 11008 bytes
-rw-r--r--tests/fuzz-tests/images/bko-169301-1-blocksize-zero.raw.txt134
-rw-r--r--tests/fuzz-tests/images/bko-169301-1-blocksize-zero.raw.xzbin0 -> 3828 bytes
-rw-r--r--tests/fuzz-tests/images/bko-169301-2-blocksize-zero.raw.txt185
-rw-r--r--tests/fuzz-tests/images/bko-169301-2-blocksize-zero.raw.xzbin0 -> 3836 bytes
-rw-r--r--tests/fuzz-tests/images/bko-169311-blocksize-zero-qgroup-verify.raw.txt126
-rw-r--r--tests/fuzz-tests/images/bko-169311-blocksize-zero-qgroup-verify.raw.xzbin0 -> 3800 bytes
-rw-r--r--tests/fuzz-tests/images/bko-172811.raw.txt55
-rw-r--r--tests/fuzz-tests/images/bko-172811.raw.xzbin0 -> 10900 bytes
-rw-r--r--tests/fuzz-tests/images/bko-172861.raw.txt68
-rw-r--r--tests/fuzz-tests/images/bko-172861.raw.xzbin0 -> 10828 bytes
-rwxr-xr-xtests/misc-tests.sh20
-rwxr-xr-xtests/misc-tests/001-btrfstune-features/test.sh8
-rwxr-xr-xtests/misc-tests/002-uuid-rewrite/test.sh12
-rwxr-xr-xtests/misc-tests/003-zero-log/test.sh9
-rwxr-xr-xtests/misc-tests/006-image-on-missing-device/test.sh7
-rwxr-xr-xtests/misc-tests/010-convert-delete-ext2-subvol/test.sh5
-rwxr-xr-xtests/misc-tests/011-delete-missing-device/test.sh11
-rw-r--r--tests/misc-tests/016-send-clone-src/multi-clone-src-v4.8.2.stream.xzbin0 -> 2688 bytes
-rwxr-xr-xtests/misc-tests/016-send-clone-src/test.sh50
-rwxr-xr-xtests/misc-tests/017-recv-stream-malformatted/test.sh25
-rwxr-xr-xtests/misc-tests/018-recv-end-of-stream/test.sh149
-rwxr-xr-xtests/mkfs-tests.sh20
-rwxr-xr-xtests/mkfs-tests/001-basic-profiles/test.sh5
-rwxr-xr-xtests/mkfs-tests/005-long-device-name-for-ssd/test.sh3
-rwxr-xr-xtests/mkfs-tests/006-partitioned-loopdev/test.sh8
-rwxr-xr-xtests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh3
-rwxr-xr-xtests/scan-results.sh18
-rwxr-xr-xtests/test-console.sh4
-rw-r--r--utils-lib.c30
-rw-r--r--utils.c379
-rw-r--r--utils.h18
-rwxr-xr-xversion.sh2
-rw-r--r--volumes.c320
-rw-r--r--volumes.h23
193 files changed, 9842 insertions, 4127 deletions
diff --git a/.gitignore b/.gitignore
index 214103d2..98b3657b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,6 +64,7 @@ config/py-compile
config/test-driver
configure
cscope.out
+.clang_complete
depcomp
libtool
Makefile
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..15313df4
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2016 Roman Lebedev.
+#
+# 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.
+#
+
+sudo: required
+
+language: c
+compiler:
+ - gcc
+ - clang
+
+cache: ccache
+
+git:
+ depth: 2
+
+dist: trusty
+group: unstable
+
+branches:
+ only:
+ - devel
+ - coverity_scan
+ - master
+ - release-test
+
+env:
+ global:
+ # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
+ # via the "travis encrypt" command using the project repo's public key
+ - secure: "aOqVfsh0m0iR3ggfuqhri4RHZK1LkQPDfNvPPQLN6VwJnz9QrI7TvLovTM1JwfX+nkyNhRq33+OahQb7PEHWPctFo6o42CcN837syF2fg8ZhTmcU1abJL29GUN/bH3xZXJJoUNJUW79Bp098GEcEmypxJ29Fxs5FQKT57O2FnnJXQLZhsDvSC1tuHtHEnCU1EGmZ6g4QI+eaS3zxKr343WCwRX6xolKHaLZX/UsYMbIMJ1YBaK2zCyOpaXOflQbloI1gcrUcoalIFuwPVbYnu2oXqzsuzHV0ekN+zMAECDNYrTr/OEA0bLR57WC0krLiAr+tA6Rq5E1D6JHg3WxDE7tbuFmrhxW23S9x6xw4+L5KuwNdsIZEdybn4q6zCkkHH3PgOFRF8taxxHKsfJ9fWxZM/kvQa5CNClDrZmfso9U8yWrYgL6fi3fIcuVLE29N2K0v9LkWlsK0REn5/uiEKO5rJ25ytpzCoUg9IRRgMPJaoPtZhSrK+ywZlJqg3f5eRqA7W1A4AEK5cOmWGFW2MOGmTtzhHG/xY0yaPYxgB7u2b7ji8BQdOn3p0ttmBFBxbDChb3LUH+d21iORrYVTG3IDziQTdBLCn/ZUypcLlLXLkkgZMQ9kLhArRmuqlTqPoR2+GNVjRP/uxwZXcszvEb0TATI11rieJW6TN1inWgY="
+
+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
+ - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
+
+addons:
+ coverity_scan:
+ project:
+ name: "kdave/btrfs-progs"
+ description: "btrfs-progs"
+ notification_email: dsterba@suse.cz
+ build_command_prepend: "./autogen.sh && ./configure --disable-documentation"
+ build_command: "make"
+ branch_pattern: coverity_scan
+
+script:
+ - "./autogen.sh && ./configure --disable-documentation && make"
+ - "make TEST_LOG=dump test-cli"
+ - "make TEST_LOG=dump test-mkfs"
+ - "make TEST_LOG=dump test-check"
+ - "make TEST_LOG=dump test-misc"
diff --git a/CHANGES b/CHANGES
index 559267ab..fcabe663 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,113 @@
+btrfs-progs-4.9.1 (2017-01-27)
+ * check:
+ * use correct inode number for lost+found files
+ * lowmem mode: fix false alert on dropped leaf
+ * size reports: negative numbers might appear in size reports during device
+ deletes (previously in EiB units)
+ * mkfs: print device being trimmed
+ * defrag: v1 ioctl support dropped
+ * quota: print message before starting to wait for rescan
+ * qgroup show: new option to sync before printing the stats
+ * other:
+ * corrupt-block enhancements
+ * backtrace and co. cleanups
+ * doc fixes
+
+btrfs-progs-4.9 (2016-12-23)
+ * check: many lowmem mode updates
+ * send: use splice syscall to copy buffer from kernel
+ * receive: new option to dump the stream in textual form
+ * convert:
+ * move sources to own directory
+ * prevent accounting of blocks beyond end of the device
+ * make it work with 64k sectorsize
+ * mkfs: move sources to own directory
+ * defrag: warns if directory used without -r
+ * dev stats:
+ * new option to check stats for non-zero values
+ * add long option for -z
+ * library: version bump to 0.1.2, added subvol_uuid_search2
+ * other:
+ * cleanups
+ * docs updates
+
+btrfs-progs-4.8.5 (2016-11-30)
+ * receive: fix detection of end of stream (error reported even for valid
+ streams)
+ * other:
+ * added test for the receive bug
+ * fix linking of library-test
+
+btrfs-progs-4.8.4 (2016-11-25)
+ * check: support for clearing space cache v2 (free-space-tree)
+ * send:
+ * more sanity checks (with tests), cleanups
+ * fix for fstests/btrfs/038 and btrfs/117 failures
+ * build:
+ * fix compilation of standalone ioctl.h, pull NULL definition
+ * fix library link errors introduced in 4.8.3
+ * tests:
+ * add more fuzzed images from bugzilla
+ * add bogus send stream checks
+ * fixups and enhancements for CI environment builds
+ * misc refinements and updates of testing framework
+ * other:
+ * move sources for btrfs-image to own directory
+ * deprecated and not build by default: btrfs-calc-size, btrfs-show-super
+ * docs updates
+
+btrfs-progs-4.8.3 (2016-11-11)
+ * check:
+ * support for clearing space cache (v1)
+ * size reduction of inode backref structure
+ * send:
+ * fix handling of multiple snapshots (-p and -c options)
+ * transfer buffer increased (should reduce number of context switches)
+ * reuse existing file for output (-f), eg. when root cannot create files (NFS)
+ * dump-tree:
+ * print missing items for various structures
+ * new: dev stats, balance status item
+ * sync key names with kernel (the persistent items)
+ * subvol show: now able to print the toplevel subvolume -- the creation time
+ might be wrong though
+ * mkfs:
+ * store the creation time of toplevel root inode
+ * print UUID in the summary
+ * build: travis CI for devel
+ * other:
+ * lots of cleanups and refactoring
+ * switched to on-stack path structure
+ * fixes from coverity, asan, ubsan
+ * new tests
+ * updates in testing infrastructure
+ * fixed convert test 005
+
+btrfs-progs-4.8.2 (2016-10-26)
+ * convert: also convert file attributes
+ * convert: fix wrong tree block alignment for unalianged block group
+ * check: quota verify fixes, handle reloc tree
+ * build: add stub for FIEMAP_EXTENT_SHARED, compiles on ancient kernels
+ * build: add stub for BUILD_ASSERT when ioctl.h is included
+ * dump-tree: don't crash on unrecognized tree id for -t
+ * tests:
+ * add more ioctl tests
+ * convert: more symlink tests, attribute tests
+ * quota verify for reloc tree
+ * other cleanups
+
+btrfs-progs-4.8.1 (2016-10-12)
+ * 32bit builds fixed
+ * build without backtrace support fixed
+
+btrfs-progs-4.8 (2016-10-05)
+ * error handling improvements all over the place
+ * new fuzzed images, test updates
+ * doc fixups
+ * minor cleanups and improvements
+ * kernel library helpers moved to own directory
+ * qgroup: fix regression leading to incorrect status after check,
+ introduced in 4.7
+
btrfs-progs-4.7.3 (2016-09-21)
* fixed free space tree compat status
* check: low-mem mode: handle partially dropped snapshots
diff --git a/Documentation/ReleaseChecklist b/Documentation/ReleaseChecklist
new file mode 100644
index 00000000..d8bf50c1
--- /dev/null
+++ b/Documentation/ReleaseChecklist
@@ -0,0 +1,37 @@
+Release checklist
+=================
+
+Last code touches:
+
+* make the code ready, collect patches queued for the release
+* look to mailinglist for any relevant last-minute fixes
+
+Pre-checks:
+
+* update package in OBS, (multi arch build checks)
+* submit sources to coverity
+* run all internal functional tests
+ * defaults
+ * D=asan
+ * D=ubsan
+* run all build tests
+* run with fstests
+
+Pre-release:
+
+* write CHANGES entry
+
+Release:
+
+* tag release, sign
+* make tar
+* build check of unpacked tar
+* upload tar to kernel.org
+* refresh git branches, push tags
+
+Post-release:
+
+* write and send announcement mail to mailinglist
+* update wiki://Main_page#News
+* update wiki://Changelog#btrfs-progs
+* update title on IRC
diff --git a/Documentation/btrfs-check.asciidoc b/Documentation/btrfs-check.asciidoc
index abc9f0dc..633cbbf6 100644
--- a/Documentation/btrfs-check.asciidoc
+++ b/Documentation/btrfs-check.asciidoc
@@ -20,7 +20,7 @@ by the option '--readonly'.
*btrfsck* is an alias of *btrfs check* command and is now deprecated.
-WARNING: Do not use '--repair' unless you are adviced to by a developer, an
+WARNING: Do not use '--repair' unless you are advised to by a developer, an
experienced user or accept the fact that 'fsck' cannot possibly fix all sorts
of damage that could happen to a filesystem because of software and hardware
bugs.
@@ -78,6 +78,20 @@ respective superblock offset is within the device size
This can be used to use a different starting point if some of the primary
superblock is damaged.
+--clear-space-cache v1|v2::
+completely wipe all free space cache of given type
++
+For free space cache 'v1', the 'clear_cache' kernel mount option only rebuilds
+the free space cache for block groups that are modified while the filesystem is
+mounted with that option. Thus, using this option with 'v1' makes it possible
+to actually clear the entire free space cache.
++
+For free space cache 'v2', the 'clear_cache' kernel mount option does destroy
+the entire free space cache. This option with 'v2' provides an alternative
+method of clearing the free space cache that doesn't require mounting the
+filesystem.
+
+
DANGEROUS OPTIONS
-----------------
diff --git a/Documentation/btrfs-convert.asciidoc b/Documentation/btrfs-convert.asciidoc
index ecc157cd..cbc1c730 100644
--- a/Documentation/btrfs-convert.asciidoc
+++ b/Documentation/btrfs-convert.asciidoc
@@ -33,6 +33,9 @@ have supported data block size (ie. the same that would be valid for
'mkfs.btrfs'). This is typically the system page size (4KiB on x86_64
machines).
+NOTE: The source filesystem should be clean, you are encouraged to run the
+'fsck' tool if you're not sure.
+
**REMOVE THE ORIGINAL FILESYSTEM METADATA**
By removing the 'ext2_saved' subvolume, all metadata of the original filesystem
diff --git a/Documentation/btrfs-device.asciidoc b/Documentation/btrfs-device.asciidoc
index d05fc457..eedcac85 100644
--- a/Documentation/btrfs-device.asciidoc
+++ b/Documentation/btrfs-device.asciidoc
@@ -27,7 +27,7 @@ The device management works on a mounted filesystem. Devices can be added,
removed or replaced, by commands profided by *btrfs device* and *btrfs replace*.
The profiles can be also changed, provided there's enough workspace to do the
-conversion, using the *btrfs balance* comand and namely the filter 'convert'.
+conversion, using the *btrfs balance* command and namely the filter 'convert'.
Profile::
A profile describes an allocation policy based on the redundancy/replication
@@ -98,16 +98,22 @@ remain as such. Reloading the kernel module will drop this information. There's
an alternative way of mounting multiple-device filesystem without the need for
prior scanning. See the mount option 'device'.
-*stats* [-z] <path>|<device>::
+*stats* [options] <path>|<device>::
Read and print the device IO error statistics for all devices of the given
-filesystem identified by <path> or for a single <device>. See section *DEVICE
-STATS* for more information.
+filesystem identified by <path> or for a single <device>. The filesystem must
+be mounted. See section *DEVICE STATS* for more information about the reported
+statistics and the meaning.
+
`Options`
+
--z::::
+-z|--reset::::
Print the stats and reset the values to zero afterwards.
+-c|--check::::
+Check if the stats are all zeros and return 0 it it is so. Set bit 6 of the
+return code if any of the statistics is no-zero. The error values is 65 if
+reading stats from at least one device failed, otherwise it's 64.
+
*usage* [options] <path> [<path>...]::
Show detailed information about internal allocations in devices.
+
@@ -180,7 +186,7 @@ logial mappings).
What changed:
-* available data space decreased by 3GiB, usable rougly (50 - 3) + (100 - 3) = 144 GiB
+* available data space decreased by 3GiB, usable roughly (50 - 3) + (100 - 3) = 144 GiB
* metadata redundancy increased
IOW, the unequal device sizes allow for combined space for data yet improved
@@ -231,6 +237,9 @@ EXIT STATUS
*btrfs device* returns a zero exit status if it succeeds. Non zero is
returned in case of failure.
+If the '-s' option is used, *btrfs device stats* will add 64 to the
+exit status if any of the error counters is non-zero.
+
AVAILABILITY
------------
*btrfs* is part of btrfs-progs.
diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc
index 9782af9b..0f7ea495 100644
--- a/Documentation/btrfs-filesystem.asciidoc
+++ b/Documentation/btrfs-filesystem.asciidoc
@@ -80,7 +80,7 @@ show sizes in TiB, or TB with --si
If conflicting options are passed, the last one takes precedence.
*defragment* [options] <file>|<dir> [<file>|<dir>...]::
-Defragment file data on a mounted filesystem.
+Defragment file data on a mounted filesystem. Requires kernel 2.6.33 and newer.
+
If '-r' is passed, files in dir will be defragmented recursively.
The start position and the number of bytes to defragment can be specified by
diff --git a/Documentation/btrfs-man5.asciidoc b/Documentation/btrfs-man5.asciidoc
index caa9390b..acc4e429 100644
--- a/Documentation/btrfs-man5.asciidoc
+++ b/Documentation/btrfs-man5.asciidoc
@@ -319,25 +319,32 @@ May be resumed with *btrfs balance resume* or the paused state can be removed
by *btrfs balance cancel*. The default behaviour is to start interrutpd balance.
*space_cache*::
-*space_cache=v2*::
+*space_cache='version'*::
*nospace_cache*::
-('nospace_cache' since: 3.2, 'space_cache=v2' since 4.5, default: on)
-+
-Options to control the free space cache. This affects performance as searching
-for new free blocks could take longer if the space cache is not enabled. On the
-other hand, managing the space cache consumes some resources. It can be
-disabled without clearing at mount time.
-+
-There are two implementations of how the space is tracked. The safe default is
-'v1'. On large filesystems (many-terabytes) and certain workloads the 'v1'
-performance may degrade. This problem is addressed by 'v2', that is based on
-b-trees, sometimes referred to as 'free-space-tree'.
-+
-'Compatibility notes:'
-+
-* the 'v2' has to be enabled manually at mount time, once
-* kernel without 'v2' support will be able to mount the filesystem in read-only mode
-* 'v2' can be removed by mounting with 'clear_cache'
+('nospace_cache' since: 3.2, 'space_cache=v1' and 'space_cache=v2' since 4.5, default: 'space_cache=v1')
++
+Options to control the free space cache. The free space cache greatly improves
+performance when reading block group free space into memory. However, managing
+the space cache consumes some resources, including a small amount of disk
+space.
++
+There are two implementations of the free space cache. The original
+implementation, 'v1', is the safe default. The 'v1' space cache can be disabled
+at mount time with 'nospace_cache' without clearing.
++
+On very large filesystems (many terabytes) and certain workloads, the
+performance of the 'v1' space cache may degrade drastically. The 'v2'
+implementation, which adds a new B-tree called the free space tree, addresses
+this issue. Once enabled, the 'v2' space cache will always be used and cannot
+be disabled unless it is cleared. Use 'clear_cache,space_cache=v1' or
+'clear_cache,nospace_cache' to do so. If 'v2' is enabled, kernels without 'v2'
+support will only be able to mount the filesystem in read-only mode. The
+`btrfs(8)` command currently only has read-only support for 'v2'. A read-write
+command may be run on a 'v2' filesystem by clearing the cache, running the
+command, and then remounting with 'space_cache=v2'.
++
+If a version is not explicitly specified, the default implementation will be
+chosen, which is 'v1' as of 4.9.
*ssd*::
*nossd*::
@@ -434,7 +441,7 @@ dump-super device* will dump a superblock, you can map the value of
after mkfs, on a mounted filesystem::
The features of a filesystem (with a given UUID) are listed in
`/sys/fs/btrfs/UUID/features/`, one file per feature. The status of is stored
-insid the file. The value '1' is for enabled, '0' means the feature was had
+insid the file. The value '1' is for enabled, '0' means the feature had
been enabled at the mount time and turned off afterwards.
+
Whether a particular feature can be turned on a mounted filesystem can be found
@@ -562,7 +569,7 @@ crw------- 1 root root 10, 234 Jan 1 12:00 /dev/btrfs-control
The device accepts some ioctl calls that can perform following actions on the
filesyste module:
-* scan devices for btrfs filesytem (ie. to let multi-device filesystems mount
+* scan devices for btrfs filesystem (ie. to let multi-device filesystems mount
automatically) and register them with the kernel module
* similar to scan, but also wait until the device scanning process is finished
for a given filesystem
diff --git a/Documentation/btrfs-qgroup.asciidoc b/Documentation/btrfs-qgroup.asciidoc
index 438dbc7d..3053f2e6 100644
--- a/Documentation/btrfs-qgroup.asciidoc
+++ b/Documentation/btrfs-qgroup.asciidoc
@@ -126,6 +126,10 @@ Prefix \'+' means ascending order and \'-' means descending order of <attr>.
If no prefix is given, use ascending order by default.
+
If multiple <attr>s is given, use comma to separate.
++
+--sync::::
+To retrieve information after updating the state of qgroups,
+force sync of the filesystem identified by <path> before getting information.
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-quota.asciidoc b/Documentation/btrfs-quota.asciidoc
index 33c3bfd7..77d4c685 100644
--- a/Documentation/btrfs-quota.asciidoc
+++ b/Documentation/btrfs-quota.asciidoc
@@ -16,7 +16,7 @@ of a btrfs filesystem. The quota groups (qgroups) are managed by the subcommand
`btrfs qgroup`(8).
NOTE: the qgroups are different than the traditional user quotas and designed
-to track shared and exlusive data per-subvolume. Plese refer to the section
+to track shared and exclusive data per-subvolume. Please refer to the section
'HIERARCHICAL QUOTA GROUP CONCEPTS' for a detailed description.
PERFORMANCE IMPLICATIONS
@@ -91,7 +91,7 @@ Qgroups of level 0 get created automatically when a subvolume/snapshot gets
created. The ID of the qgroup corresponds to the ID of the subvolume, so 0/5
is the qgroup for the root subvolume.
For the *btrfs qgroup* command, the path to the subvolume can also be used
-instead of '0/ID'. For all higher levels, the ID can be choosen freely.
+instead of '0/ID'. For all higher levels, the ID can be chosen freely.
Each qgroup can contain a set of lower level qgroups, thus creating a hierarchy
of qgroups. Figure 1 shows an example qgroup tree.
diff --git a/Documentation/btrfs-receive.asciidoc b/Documentation/btrfs-receive.asciidoc
index e246603c..a6838e5e 100644
--- a/Documentation/btrfs-receive.asciidoc
+++ b/Documentation/btrfs-receive.asciidoc
@@ -9,32 +9,37 @@ SYNOPSIS
--------
*btrfs receive* [options] <path>
+or
+
+*btrfs receive* --dump [options]
+
DESCRIPTION
-----------
Receive a stream of changes and replicate one or more subvolumes that were
-previously used with *btrfs send* The received subvolumes are stored to
-'path'.
+previously generated by *btrfs send*. The received subvolumes are stored to
+'path', unless '--dump' option is given.
+
+If '--dump' option is specified, *btrfs receive* will only do the validation of
+the stream, and print the stream metadata, one operation per line.
*btrfs receive* will fail int the following cases:
1. receiving subvolume already exists
-2. previously received subvolume was changed after it was received
+2. previously received subvolume has been changed after it was received
-3. default subvolume has changed or you didn't mount BTRFS filesystem at the toplevel subvolume
+3. default subvolume has changed or you didn't mount the filesystem at the toplevel subvolume
A subvolume is made read-only after the receiving process finishes succesfully.
`Options`
-v::
-enable verbose debug output, print each operation (each occurrence of this
-option increases the verbosity level)
+increase verbosity about performed actions, print details about each operation
--f <infile>::
-by default, btrfs receive uses standard input to receive the stream,
-use this option to read from a file instead
+-f <FILE>::
+read the stream from <FILE> instead of stdin,
-C|--chroot::
confine the process to 'path' using `chroot`(1)
@@ -42,19 +47,26 @@ confine the process to 'path' using `chroot`(1)
-e::
terminate after receiving an 'end cmd' marker in the stream.
+
-Without this option, the receiver terminates only if an error is encountered
-or at end of file
+Without this option the receiver side terminates only in case
+of an error on end of file.
---max-errors <N>::
-terminate as soon as N errors happened while processing commands from the send
-stream, default value is 1, 0 means no limit
+-E|--max-errors <NERR>::
+terminate as soon as NERR errors occur while stream processing commands from
+the stream
++
+Default value is 1. A value of 0 means no limit.
--m <mountpoint>::
+-m <ROOTMOUNT>::
the root mount point of the destination filesystem
+
By default the mountpoint is searched in '/proc/self/mounts'.
-If you do not have '/proc', eg. in a chroot environment, use this option to tell
-us where this filesystem is mounted.
+If '/proc' is not accessible, eg. in a chroot environment, use this option to
+tell us where this filesystem is mounted.
+
+--dump::
+dump the stream metadata, one line per operation
++
+Does not require the 'path' parameter. The filesystem chanded.
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-scrub.asciidoc b/Documentation/btrfs-scrub.asciidoc
index 40e793c2..f579c39e 100644
--- a/Documentation/btrfs-scrub.asciidoc
+++ b/Documentation/btrfs-scrub.asciidoc
@@ -20,7 +20,7 @@ structural damage in the filesystem.
The user is supposed to run it manually or via a periodic system service. The
recommended period is a month but could be less. The estimated device bandwidth
-utilization is about 80% on an idle filesytem. The IO priority class is by
+utilization is about 80% on an idle filesystem. The IO priority class is by
default 'idle' so background scrub should not interfere with normal filesystem
operation significantly.
@@ -34,7 +34,7 @@ saved position.
SUBCOMMAND
----------
*cancel* <path>|<device>::
-If a scrub is running on the filesystem identified by 'path>' cancel it.
+If a scrub is running on the filesystem identified by 'path' cancel it.
+
If a 'device' is specified, the corresponding filesystem is found and
*btrfs scrub cancel* behaves as if it was called on that filesystem.
@@ -95,7 +95,14 @@ print separate statistics for each device of the filesystem
EXIT STATUS
-----------
*btrfs scrub* returns a zero exit status if it succeeds. Non zero is
-returned in case of failure.
+returned in case of failure:
+
+1::::
+scrub couldn't be performed
+2::::
+there is nothing to resume
+3::::
+scrub found uncorrectable errors
AVAILABILITY
------------
diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc
index 0a1ca003..3419b138 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -17,7 +17,7 @@ snapshots.
SUBVOLUME AND SNAPSHOT
----------------------
-A subvolume is a part of filesystem with it's own and independent
+A subvolume is a part of filesystem with its own and independent
file/directory hierarchy. Subvolumes can share file extents. A snapshot is
also subvolume, but with a given initial content of the original subvolume.
diff --git a/Documentation/btrfstune.asciidoc b/Documentation/btrfstune.asciidoc
index 1e9aa703..04295ee3 100644
--- a/Documentation/btrfstune.asciidoc
+++ b/Documentation/btrfstune.asciidoc
@@ -20,7 +20,7 @@ complete list of features and kernel version of their introduction at
https://btrfs.wiki.kernel.org/index.php/Changelog#By_feature . Also, the
manual page `mkfs.btrfs`(8) contains more details about the features.
-Some of the features could be enabled on a mounted filesytem. Please refer to
+Some of the features could be enabled on a mounted filesystem. Please refer to
the respective section in `btrfs`(5).
OPTIONS
diff --git a/Documentation/mkfs.btrfs.asciidoc b/Documentation/mkfs.btrfs.asciidoc
index 6515e145..46a4d2d5 100644
--- a/Documentation/mkfs.btrfs.asciidoc
+++ b/Documentation/mkfs.btrfs.asciidoc
@@ -7,25 +7,7 @@ mkfs.btrfs - create a btrfs filesystem
SYNOPSIS
--------
-*mkfs.btrfs*
-$$[-A|--alloc-start <alloc-start>]$$
-$$[-b|--byte-count <byte-count>]$$
-$$[-d|--data <data-profile>]$$
-$$[-m|--metadata <metadata profile>]$$
-$$[-M|--mixed]$$
-$$[-l|--leafsize <leafsize>]$$
-$$[-n|--nodesize <nodesize>]$$
-$$[-s|--sectorsize <sectorsize>]$$
-$$[-L|--label <label>]$$
-$$[-K|--nodiscard]$$
-$$[-r|--rootdir <rootdir>]$$
-$$[-O|--features <feature1>[,<feature2>...]]$$
-$$[-U|--uuid <UUID>]$$
-$$[-f|--force]$$
-$$[-q|--quiet]$$
-$$[--help]$$
-$$[-V|--version]$$
-$$<device> [<device>...]$$
+*mkfs.btrfs* [options] <device> [<device>...]
DESCRIPTION
-----------
@@ -118,6 +100,8 @@ bytes and must not contain newline characters.
*-K|--nodiscard*::
Do not perform whole device TRIM operation on devices that are capable of that.
+This does not affect discard/trim operation when the filesystem is mounted.
+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
@@ -182,6 +166,9 @@ root partition created with RAID1/10/5/6 profiles. The mount action can happen
before all block devices are discovered. The waiting is usually done on the
initramfs/initrd systems.
+As of kernel 4.9, RAID5/6 is still considered experimental and shouldn't be
+employed for production use.
+
FILESYSTEM FEATURES
-------------------
@@ -271,7 +258,7 @@ There are the following block group types available:
| RAID6 | 1 | 2 | 3 to N - 2 | 3/any ^(see note 3)^
|=============================================================
-WARNING: It's not recommended to build btrfs with RAID0/1/10/5/6 prfiles on
+WARNING: It's not recommended to build btrfs with RAID0/1/10/5/6 profiles on
partitions from the same device. Neither redundancy nor performance will be
improved.
diff --git a/INSTALL b/INSTALL
index 85a839f1..0465fb02 100644
--- a/INSTALL
+++ b/INSTALL
@@ -31,26 +31,27 @@ Building from sources
To build from git sources you need to generate the configure script using the
autotools:
- $ ./autogen.sh
+ $ ./autogen.sh
To build from the released tarballs:
- $ ./configure
- $ make
- $ make install
+ $ ./configure
+ $ make
+ $ make install
You may disable building some parts like documentation, btrfs-convert or
backtrace support. See ./configure --help for more.
Specific CFLAGS or LDFLAGS should be set like
- $ CFLAGS=... LDFLAGS=... ./configure --prefix=/usr
+ $ CFLAGS=... LDFLAGS=... ./configure --prefix=/usr
and not as arguments to make. You can specify additional flags to build via
variables EXTRA_CFLAGS and EXTRA_LDFLAGS that get appended to the predefined
-values of the respective variables.
+values of the respective variables. There are further build tuning options
+documented in the Makefile.
- $ make EXTRA_CFLAGS=-ggdb3
+ $ make EXTRA_CFLAGS=-ggdb3
The build utilizes autotools, dependencies for generating the configure
scripts are:
@@ -60,18 +61,18 @@ scripts are:
* pkg-config
-Staticly built binaries
------------------------
+Statically built binaries
+-------------------------
The makefiles are ready to let you build static binaries of the utilities. This
may be handy in rescue environments. Your system has to provide static version
of the libraries.
-$ make static
-$ make btrfs.static
-$ make btrfs-convert.static
+ $ make static
+ $ make btrfs.static
+ $ make btrfs-convert.static
-The resulting static binaries have the '.static' suffix, the intermediate object
+The resulting binaries have the '.static' suffix, the intermediate object
files do not conflict with the normal (dynamic) build.
diff --git a/Makefile.in b/Makefile.in
index 214b0428..0e3a0a0f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -19,11 +19,17 @@
# all - shortcut for all of the above
# asan - enable address sanitizer compiler feature
# ubsan - undefined behaviour sanitizer compiler feature
+# bcheck - extended build checks
# W=123 build with warnings (default: off)
# DEBUG_CFLAGS additional compiler flags for debugging build
# EXTRA_CFLAGS additional compiler flags
# EXTRA_LDFLAGS additional linker flags
#
+# Testing-specific options (see also tests/README.md):
+# TEST=GLOB run test(s) from directories matching GLOB
+# TEST_LOG=tty print name of a command run via the execution helpers
+# TEST_LOG=dump dump testing log file when a test fails
+#
# Static checkers:
# CHECKER static checker binary to be called (default: sparse)
# CHECKER_FLAGS flags to pass to CHECKER, can override CFLAGS
@@ -51,6 +57,8 @@ DEBUG_CFLAGS_DEFAULT = -O0 -U_FORTIFY_SOURCE -ggdb3
DEBUG_CFLAGS_INTERNAL =
DEBUG_CFLAGS :=
+TOPDIR := $(shell pwd)
+
# Common build flags
CFLAGS = @CFLAGS@ \
-include config.h \
@@ -58,12 +66,14 @@ CFLAGS = @CFLAGS@ \
-D_XOPEN_SOURCE=700 \
-fno-strict-aliasing \
-fPIC \
+ -I$(TOPDIR) \
+ -I$(TOPDIR)/kernel-lib \
$(EXTRAWARN_CFLAGS) \
$(DEBUG_CFLAGS_INTERNAL) \
$(EXTRA_CFLAGS)
LDFLAGS = @LDFLAGS@ \
- -rdynamic $(EXTRA_LDFLAGS)
+ -rdynamic -L$(TOPDIR) $(EXTRA_LDFLAGS)
LIBS = @UUID_LIBS@ @BLKID_LIBS@ @ZLIB_LIBS@ @LZO2_LIBS@ -L. -pthread
LIBBTRFS_LIBS = $(LIBS)
@@ -83,22 +93,24 @@ CHECKER_FLAGS := -include $(check_defs) -D__CHECKER__ \
-D__CHECK_ENDIAN__ -Wbitwise -Wuninitialized -Wshadow -Wundef \
-U_FORTIFY_SOURCE
-objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
+objects = ctree.o disk-io.o kernel-lib/radix-tree.o extent-tree.o print-tree.o \
root-tree.o dir-item.o file-item.o inode-item.o inode-map.o \
extent-cache.o extent_io.o volumes.o utils.o repair.o \
- qgroup.o raid6.o free-space-cache.o list_sort.o props.o \
+ qgroup.o raid56.o free-space-cache.o kernel-lib/list_sort.o props.o \
ulist.o qgroup-verify.o backref.o string-table.o task-utils.o \
- inode.o file.o find-root.o free-space-tree.o help.o
+ inode.o file.o find-root.o free-space-tree.o help.o send-dump.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-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
-libbtrfs_objects = send-stream.o send-utils.o rbtree.o btrfs-list.o crc32c.o \
+libbtrfs_objects = send-stream.o send-utils.o kernel-lib/rbtree.o btrfs-list.o \
+ kernel-lib/crc32c.o \
uuid-tree.o utils-lib.o rbtree-utils.o
-libbtrfs_headers = send-stream.h send-utils.h send.h rbtree.h btrfs-list.h \
- crc32c.h list.h kerncompat.h radix-tree.h extent-cache.h \
+libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-list.h \
+ kernel-lib/crc32c.h kernel-lib/list.h kerncompat.h \
+ kernel-lib/radix-tree.h extent-cache.h \
extent_io.h ioctl.h ctree.h btrfsck.h version.h
TESTS = fsck-tests.sh convert-tests.sh
@@ -155,18 +167,23 @@ ifneq (,$(findstring ubsan,$(D)))
DEBUG_CFLAGS_INTERNAL += -fsanitize=undefined
endif
+ifneq (,$(findstring bcheck,$(D)))
+ DEBUG_CFLAGS_INTERNAL += -DDEBUG_BUILD_CHECKS
+endif
+
MAKEOPTS = --no-print-directory Q=$(Q)
# build all by default
-progs = $(progs_install) btrfsck btrfs-corrupt-block btrfs-calc-size
+progs = $(progs_install) btrfsck btrfs-corrupt-block
# install only selected
progs_install = btrfs mkfs.btrfs btrfs-debug-tree \
btrfs-map-logical btrfs-image btrfs-zero-log \
- btrfs-find-root btrfstune btrfs-show-super \
+ btrfs-find-root btrfstune \
btrfs-select-super
-progs_extra = btrfs-fragments
+# other tools, not built by default
+progs_extra = btrfs-fragments btrfs-calc-size btrfs-show-super
progs_static = $(foreach p,$(progs),$(p).static)
@@ -231,7 +248,7 @@ else
endif
%.o.d: %.c
- $(Q)$(CC) -MM -MG -MF $@ -MT $(@:.o.d=.o) -MT $(@:.o.d=.static.o) -MT $@ $(CFLAGS) $<
+ $(Q)$(CC) -MD -MM -MG -MF $@ -MT $(@:.o.d=.o) -MT $(@:.o.d=.static.o) -MT $@ $(CFLAGS) $<
#
# Pick from per-file variables, btrfs_*_cflags
@@ -240,11 +257,13 @@ endif
@$(check_echo) " [SP] $<"
$(Q)$(check) $(CFLAGS) $(CHECKER_FLAGS) $<
@echo " [CC] $@"
- $(Q)$(CC) $(CFLAGS) -c $< $($(subst -,_,$(@:%.o=%)-cflags))
+ $(Q)$(CC) $(CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.o=%)-cflags)) \
+ $($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags))
%.static.o: %.c
@echo " [CC] $@"
- $(Q)$(CC) $(STATIC_CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.static.o=%)-cflags))
+ $(Q)$(CC) $(STATIC_CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.static.o=%)-cflags)) \
+ $($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags))
all: $(progs) $(BUILDDIRS)
$(SUBDIRS): $(BUILDDIRS)
@@ -256,6 +275,7 @@ test-convert: btrfs btrfs-convert
@echo " [TEST] convert-tests.sh"
$(Q)bash tests/convert-tests.sh
+test-check: test-fsck
test-fsck: btrfs btrfs-image btrfs-corrupt-block btrfs-debug-tree mkfs.btrfs
@echo " [TEST] fsck-tests.sh"
$(Q)bash tests/fsck-tests.sh
@@ -286,7 +306,7 @@ test-inst: all
$(MAKE) DESTDIR=$$tmpdest install && \
$(RM) -rf -- $$tmpdest
-test: test-fsck test-mkfs test-convert test-misc test-fuzz
+test: test-fsck test-mkfs test-convert test-misc test-fuzz test-cli
#
# NOTE: For static compiles, you need to have all the required libs
@@ -354,13 +374,13 @@ btrfsck.static: btrfs.static
@echo " [LN] $@"
$(Q)$(LN_S) -f $^ $@
-mkfs.btrfs: $(objects) $(libs_static) mkfs.o
+mkfs.btrfs: $(objects) $(libs_static) mkfs/main.o
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) $(libs_static) mkfs.o $(LDFLAGS) $(LIBS)
+ $(Q)$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) $(libs_static) mkfs/main.o $(LDFLAGS) $(LIBS)
-mkfs.btrfs.static: $(static_objects) mkfs.static.o $(static_libbtrfs_objects)
+mkfs.btrfs.static: $(static_objects) mkfs/main.static.o $(static_libbtrfs_objects)
@echo " [LD] $@"
- $(Q)$(CC) $(STATIC_CFLAGS) -o mkfs.btrfs.static mkfs.static.o $(static_objects) \
+ $(Q)$(CC) $(STATIC_CFLAGS) -o mkfs.btrfs.static mkfs/main.static.o $(static_objects) \
$(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS)
btrfstune: $(objects) $(libs_static) btrfstune.o
@@ -372,6 +392,25 @@ btrfstune.static: $(static_objects) btrfstune.static.o $(static_libbtrfs_objects
$(Q)$(CC) $(STATIC_CFLAGS) -o $@ btrfstune.static.o $(static_objects) \
$(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS)
+btrfs-image: $(objects) $(libs_static) image/main.o
+ @echo " [LD] $@"
+ $(Q)$(CC) $(CFLAGS) -I$(TOPDIR)/image -o btrfs-image $(objects) image/main.o $(libs_static) $(LDFLAGS) $(LIBS)
+
+btrfs-image.static: $(static_objects) image/main.static.o $(static_libbtrfs_objects)
+ @echo " [LD] $@"
+ $(Q)$(CC) $(STATIC_CFLAGS) -o $@ image/main.static.o $(static_objects) \
+ $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS)
+
+btrfs-convert: $(objects) $(libs_static) convert/main.o
+ @echo " [LD] $@"
+ $(Q)$(CC) $(CFLAGS) -I$(TOPDIR)/convert -o btrfs-convert $(objects) convert/main.o $(libs_static) \
+ $(LDFLAGS) $(btrfs_convert_libs) $(LIBS)
+
+btrfs-convert.static: $(static_objects) convert/main.static.o $(static_libbtrfs_objects)
+ @echo " [LD] $@"
+ $(Q)$(CC) $(STATIC_CFLAGS) -o $@ convert/main.static.o $(static_objects) \
+ $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(btrfs_convert_libs) $(STATIC_LIBS)
+
dir-test: $(objects) $(libs) dir-test.o
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o dir-test $(objects) $(libs) dir-test.o $(LDFLAGS) $(LIBS)
@@ -380,21 +419,53 @@ quick-test: $(objects) $(libs) quick-test.o
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o quick-test $(objects) $(libs) quick-test.o $(LDFLAGS) $(LIBS)
-ioctl-test: $(objects) $(libs) ioctl-test.o
- @echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o ioctl-test $(objects) $(libs) ioctl-test.o $(LDFLAGS) $(LIBS)
-
-send-test: $(objects) $(libs) send-test.o
- @echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o send-test $(objects) $(libs) send-test.o $(LDFLAGS) $(LIBS)
+ioctl-test.o: ioctl-test.c ioctl.h kerncompat.h ctree.h
+ @echo " [CC] $@"
+ $(Q)$(CC) $(CFLAGS) -c $< -o $@
+
+ioctl-test-32.o: ioctl-test.c ioctl.h kerncompat.h ctree.h
+ @echo " [CC32] $@"
+ $(Q)$(CC) $(CFLAGS) -m32 -c $< -o $@
+
+ioctl-test-64.o: ioctl-test.c ioctl.h kerncompat.h ctree.h
+ @echo " [CC64] $@"
+ $(Q)$(CC) $(CFLAGS) -m64 -c $< -o $@
+
+ioctl-test: ioctl-test.o
+ @echo " [LD] $@"
+ $(Q)$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+ @echo " ?[PAHOLE] $@.pahole"
+ -$(Q)pahole $@ > $@.pahole
+
+ioctl-test-32: ioctl-test-32.o
+ @echo " [LD32] $@"
+ $(Q)$(CC) $(CFLAGS) -m32 -o $@ $< $(LDFLAGS)
+ @echo " ?[PAHOLE] $@.pahole"
+ -$(Q)pahole $@ > $@.pahole
+
+ioctl-test-64: ioctl-test-64.o
+ @echo " [LD64] $@"
+ $(Q)$(CC) $(CFLAGS) -m64 -o $@ $< $(LDFLAGS)
+ @echo " ?[PAHOLE] $@.pahole"
+ -$(Q)pahole $@ > $@.pahole
+
+test-ioctl: ioctl-test ioctl-test-32 ioctl-test-64
+ @echo " [TEST/ioctl]"
+ $(Q)./ioctl-test > ioctl-test.log
+ $(Q)./ioctl-test-32 > ioctl-test-32.log
+ $(Q)./ioctl-test-64 > ioctl-test-64.log
library-test: $(libs_shared) library-test.o
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o library-test library-test.o $(LDFLAGS) -lbtrfs
+ $(Q)$(CC) $(CFLAGS) -o library-test library-test.o $(LDFLAGS) -Wl,-rpath=$(TOPDIR) -lbtrfs
+ @echo " [TEST] $@"
+ $(Q)./$@
-library-test.static: $(libs_static) library-test.o
+library-test.static: $(libs_static) library-test.static.o
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o library-test-static library-test.o $(LDFLAGS) $(libs_static)
+ $(Q)$(CC) $(STATIC_CFLAGS) -o library-test.static library-test.static.o $(STATIC_LDFLAGS) $(libs_static) $(STATIC_LIBS)
+ @echo " [TEST] $@"
+ $(Q)./$@
test-build: test-build-pre test-build-real
@@ -418,8 +489,12 @@ clean-all: clean clean-doc clean-gen
clean: $(CLEANDIRS)
@echo "Cleaning"
- $(Q)$(RM) -f $(progs) cscope.out *.o *.o.d \
- dir-test ioctl-test quick-test send-test library-test library-test-static \
+ $(Q)$(RM) -f -- $(progs) cscope.out *.o *.o.d \
+ kernel-lib/*.o kernel-lib/*.o.d \
+ image/*.o image/*.o.d \
+ convert/*.o convert/*.o.d \
+ mkfs/*.o mkfs/*.o.d \
+ dir-test ioctl-test quick-test library-test library-test-static \
btrfs.static mkfs.btrfs.static \
$(check_defs) \
$(libs) $(lib_links) \
@@ -431,7 +506,7 @@ clean-doc:
clean-gen:
@echo "Cleaning Generated Files"
- $(Q)$(RM) -rf version.h config.status config.cache connfig.log \
+ $(Q)$(RM) -rf -- version.h config.status config.cache connfig.log \
configure.lineno config.status.lineno Makefile \
Documentation/Makefile \
config.log config.h config.h.in~ aclocal.m4 \
@@ -469,10 +544,10 @@ $(INSTALLDIRS):
uninstall:
$(Q)$(MAKE) $(MAKEOPTS) -C Documentation uninstall
- cd $(DESTDIR)$(incdir); $(RM) -f $(headers)
- $(RMDIR) -p --ignore-fail-on-non-empty $(DESTDIR)$(incdir)
- cd $(DESTDIR)$(libdir); $(RM) -f $(lib_links) $(libs)
- cd $(DESTDIR)$(bindir); $(RM) -f btrfsck fsck.btrfs $(progs_install)
+ cd $(DESTDIR)$(incdir); $(RM) -f -- $(headers)
+ $(RMDIR) -p --ignore-fail-on-non-empty -- $(DESTDIR)$(incdir)
+ cd $(DESTDIR)$(libdir); $(RM) -f -- $(lib_links) $(libs)
+ cd $(DESTDIR)$(bindir); $(RM) -f -- btrfsck fsck.btrfs $(progs_install)
ifneq ($(MAKECMDGOALS),clean)
-include $(objects:.o=.o.d) $(cmds_objects:.o=.o.d) $(subst .btrfs,, $(filter-out btrfsck.o.d, $(progs:=.o.d)))
diff --git a/README.md b/README.md
index 2f9d4e7e..b9d3d913 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-Btrfs-progs
+Btrfs-progs [![build status](https://travis-ci.org/kdave/btrfs-progs.svg?branch=devel)](https://travis-ci.org/kdave/btrfs-progs) [![coverity status](https://scan.coverity.com/projects/617/badge.svg)](https://scan.coverity.com/projects/btrfs-progs)
===========
Userspace utilities to manage btrfs filesystems.
@@ -14,28 +14,79 @@ This repository hosts following utilities:
* **btrfs** &mdash; the main administration tool ([manual page](https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs))
* **mkfs.btrfs** &mdash; utility to create the filesystem ([manual page](https://btrfs.wiki.kernel.org/index.php/Manpage/mkfs.btrfs))
-See INSTALL for build instructions.
+See INSTALL for build instructions and [tests/README.md](tests/README.md) for
+testing information.
Release cycle
-------------
The major version releases are time-based and follow the cycle of the linux
kernel releases. The cycle usually takes 2 months. A minor version releases may
-happen in the meantime if there are queued bug fixes or minor useful
-improvements.
+happen in the meantime if there are bug fixes or minor useful improvements
+queued.
+
+The release tags are signed with a GPG key ID `F2B4 1200 C54E FB30 380C 1756 C565 D5F9 D76D 583B`,
+release tarballs are hosted at [kernel.org](https://www.kernel.org/pub/linux/kernel/people/kdave/btrfs-progs/).
+See file [CHANGES](CHANGES) or [changelogs on wiki](https://btrfs.wiki.kernel.org/index.php/Changelog#By_version_.28btrfs-progs.29).
+
+Reporting bugs
+--------------
+
+There are several ways, each has its own specifics and audience that can give
+feedback or work on a fix.
+
+* [bugzilla.kernel.org](https://bugzilla.kernel.org) -- (requires
+ registration), set the product to Filesystems and component Btrfs, please put
+ 'btrfs-progs' into the subject so it's clear that it's not a kernel bug
+ report
+* to the mailing list *linux-btrfs@vger.kernel.org* -- (not required to
+ subscribe), beware that the mail might get overlooked in other traffic
+* [github issue tracker](https://github.com/kdave/btrfs-sprogs/issues)
+* IRC (irc.freenode.net #btrfs) -- good for discussions eg. if a bug is already
+ known, but reports could miss developers' attention
+
Development
-----------
The patch submissions, development or general discussions take place at
-*linux-btrfs@vger.kernel.org* mailinglist, subsciption not required.
+*linux-btrfs@vger.kernel.org* mailinglist, subsciption is not required to post.
+
+The GitHub pull requests will not be accepted directly, the preferred way is to
+send patches to the mailinglist instead. You can link to a branch in any git
+repository if the mails do not make it to the mailinglist or just for
+convenience (makes it easier to test).
+
+The development model of btrfs-progs shares a lot with the kernel model. The
+github way is different in some ways. We, the upstream community, expect that
+the patches meet some criteria (often lacking in github contributions):
+
+* **one logical change per patch**: eg. not mixing bugfixes, cleanups, features
+ etc., sometimes it's not clear and will be usually pointed out during reviews
+* proper **subject line**: eg. prefix with _btrfs-progs: subpart, ..._ ,
+ descriptive yet not too long, see `git log --oneline` for some inspiration
+* proper **changelog**: the changelogs are often missing or lacking explanation _why_
+ the change was made, or _how_ is something broken, _what_ are user-visible
+ effects of the bug or the fix, _how_ does an improvement help or the intended
+ _usecase_
+* the **Signed-off-by** line: this documents who authored the change, you can read
+ more about the _The Developer's Certificate of Origin_ [here (chapter 11)](https://www.kernel.org/doc/Documentation/SubmittingPatches)
+
+Documentation updates
+---------------------
+
+Documentation fixes or updates do not need much explanation so sticking to the
+code rules in the previous section is not necessary. Github pull requests are
+OK, patches could be sent to me directly and not required to be also in the
+mailinglist. Pointing out typos via IRC also works, although might get
+accidentally lost in the noise.
References
----------
-* [Wiki with more information](https://btrfs.wiki.kernel.org)
-* [Btrfs-progs changelogs](https://btrfs.wiki.kernel.org/index.php/Changelog#By_version_.28btrfs-progs.29)
-* [wiki/FAQ](https://btrfs.wiki.kernel.org/index.php/FAQ)
+* [wiki/Developer's FAQ](https://btrfs.wiki.kernel.org/index.php/Developer's_FAQ)
* [wiki/Getting started](https://btrfs.wiki.kernel.org/index.php/Getting_started)
* [wiki/TODO](https://btrfs.wiki.kernel.org/index.php/Project_ideas#Userspace_tools_projects)
-* [wiki/Developer's FAQ](https://btrfs.wiki.kernel.org/index.php/Developer's_FAQ)
+* [Btrfs-progs changelogs](https://btrfs.wiki.kernel.org/index.php/Changelog#By_version_.28btrfs-progs.29)
+* [wiki/FAQ](https://btrfs.wiki.kernel.org/index.php/FAQ)
+* [Wiki with more information](https://btrfs.wiki.kernel.org)
diff --git a/backref.c b/backref.c
index 7b3b5925..a43e80da 100644
--- a/backref.c
+++ b/backref.c
@@ -739,8 +739,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
key.objectid = bytenr;
key.offset = (u64)-1;
- if (btrfs_fs_incompat(fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA))
+ if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
key.type = BTRFS_METADATA_ITEM_KEY;
else
key.type = BTRFS_EXTENT_ITEM_KEY;
@@ -990,7 +989,7 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
unsigned long ptr;
key.objectid = inode_objectid;
- btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY);
+ key.type = BTRFS_INODE_EXTREF_KEY;
key.offset = start_off;
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
@@ -1030,7 +1029,7 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
ret = -ENOENT;
if (found_key.objectid != inode_objectid)
break;
- if (btrfs_key_type(&found_key) != BTRFS_INODE_EXTREF_KEY)
+ if (found_key.type != BTRFS_INODE_EXTREF_KEY)
break;
ret = 0;
@@ -1136,8 +1135,7 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
struct btrfs_extent_item *ei;
struct btrfs_key key;
- if (btrfs_fs_incompat(fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA))
+ if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
key.type = BTRFS_METADATA_ITEM_KEY;
else
key.type = BTRFS_EXTENT_ITEM_KEY;
diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
index 835fcc50..1c5c61df 100644
--- a/btrfs-calc-size.c
+++ b/btrfs-calc-size.c
@@ -25,6 +25,9 @@ int main(int argc, char **argv)
{
int ret;
+ warning(
+"\nthe tool has been deprecated, please use 'btrfs inspect-internal tree-stats' instead\n");
+
if (argc > 1 && !strcmp(argv[1], "--help"))
usage(cmd_inspect_tree_stats_usage);
diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index 789cbc70..0e1eb524 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -65,7 +65,7 @@ static int debug_corrupt_block(struct extent_buffer *eb,
"mirror %d logical %llu physical %llu device %s\n",
mirror_num, (unsigned long long)bytenr,
(unsigned long long)eb->dev_bytenr, device->name);
- kfree(multi);
+ free(multi);
if (!copy || mirror_num == copy) {
ret = read_extent_from_disk(eb, 0, eb->len);
@@ -308,6 +308,13 @@ static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
enum btrfs_inode_field {
BTRFS_INODE_FIELD_ISIZE,
BTRFS_INODE_FIELD_NBYTES,
+ BTRFS_INODE_FIELD_NLINK,
+ BTRFS_INODE_FIELD_GENERATION,
+ BTRFS_INODE_FIELD_TRANSID,
+ BTRFS_INODE_FIELD_BLOCK_GROUP,
+ BTRFS_INODE_FIELD_MODE,
+ BTRFS_INODE_FIELD_UID,
+ BTRFS_INODE_FIELD_GID,
BTRFS_INODE_FIELD_BAD,
};
@@ -346,6 +353,20 @@ static enum btrfs_inode_field convert_inode_field(char *field)
return BTRFS_INODE_FIELD_ISIZE;
if (!strncmp(field, "nbytes", FIELD_BUF_LEN))
return BTRFS_INODE_FIELD_NBYTES;
+ if (!strncmp(field, "nlink", FIELD_BUF_LEN))
+ return BTRFS_INODE_FIELD_NLINK;
+ if (!strncmp(field, "generation", FIELD_BUF_LEN))
+ return BTRFS_INODE_FIELD_GENERATION;
+ if (!strncmp(field, "transid", FIELD_BUF_LEN))
+ return BTRFS_INODE_FIELD_TRANSID;
+ if (!strncmp(field, "block_group", FIELD_BUF_LEN))
+ return BTRFS_INODE_FIELD_BLOCK_GROUP;
+ if (!strncmp(field, "mode", FIELD_BUF_LEN))
+ return BTRFS_INODE_FIELD_MODE;
+ if (!strncmp(field, "uid", FIELD_BUF_LEN))
+ return BTRFS_INODE_FIELD_UID;
+ if (!strncmp(field, "gid", FIELD_BUF_LEN))
+ return BTRFS_INODE_FIELD_GID;
return BTRFS_INODE_FIELD_BAD;
}
@@ -603,6 +624,41 @@ static int corrupt_inode(struct btrfs_trans_handle *trans,
bogus = generate_u64(orig);
btrfs_set_inode_nbytes(path->nodes[0], ei, bogus);
break;
+ case BTRFS_INODE_FIELD_NLINK:
+ orig = btrfs_inode_nlink(path->nodes[0], ei);
+ bogus = generate_u32(orig);
+ btrfs_set_inode_nlink(path->nodes[0], ei, bogus);
+ break;
+ case BTRFS_INODE_FIELD_GENERATION:
+ orig = btrfs_inode_generation(path->nodes[0], ei);
+ bogus = generate_u64(orig);
+ btrfs_set_inode_generation(path->nodes[0], ei, bogus);
+ break;
+ case BTRFS_INODE_FIELD_TRANSID:
+ orig = btrfs_inode_transid(path->nodes[0], ei);
+ bogus = generate_u64(orig);
+ btrfs_set_inode_transid(path->nodes[0], ei, bogus);
+ break;
+ case BTRFS_INODE_FIELD_BLOCK_GROUP:
+ orig = btrfs_inode_block_group(path->nodes[0], ei);
+ bogus = generate_u64(orig);
+ btrfs_set_inode_block_group(path->nodes[0], ei, bogus);
+ break;
+ case BTRFS_INODE_FIELD_MODE:
+ orig = btrfs_inode_mode(path->nodes[0], ei);
+ bogus = generate_u32(orig);
+ btrfs_set_inode_mode(path->nodes[0], ei, bogus);
+ break;
+ case BTRFS_INODE_FIELD_UID:
+ orig = btrfs_inode_uid(path->nodes[0], ei);
+ bogus = generate_u32(orig);
+ btrfs_set_inode_uid(path->nodes[0], ei, bogus);
+ break;
+ case BTRFS_INODE_FIELD_GID:
+ orig = btrfs_inode_gid(path->nodes[0], ei);
+ bogus = generate_u32(orig);
+ btrfs_set_inode_gid(path->nodes[0], ei, bogus);
+ break;
default:
ret = -EINVAL;
break;
@@ -899,7 +955,11 @@ static int corrupt_item_nocow(struct btrfs_trans_handle *trans,
if (slot == 0)
del = 0;
/* Only accept valid eb */
- BUG_ON(!leaf->data || slot >= btrfs_header_nritems(leaf));
+ if (slot >= btrfs_header_nritems(leaf)) {
+ error("invalid eb: no data or slot out of range: %d >= %d",
+ slot, btrfs_header_nritems(leaf));
+ return -EINVAL;
+ }
btrfs_item_key_to_cpu(leaf, &key, slot);
if (del) {
fprintf(stdout, "Deleting key and data [%llu, %u, %llu].\n",
diff --git a/btrfs-debugfs b/btrfs-debugfs
index 0a654a64..dfb88539 100755
--- a/btrfs-debugfs
+++ b/btrfs-debugfs
@@ -4,7 +4,7 @@
# LGPLv2 license
# Copyright Facebook 2014
-import sys,os,struct,fcntl,ctypes,stat,argparse
+import sys, os, fcntl, ctypes, stat, argparse
# helpers for max ints
maxu64 = (1L << 64) - 1
@@ -233,7 +233,6 @@ def print_file_extents(filename):
s.args.min_objectid = st.st_ino
s.args.max_objectid = st.st_ino
- size = st.st_size
while True:
try:
@@ -314,7 +313,7 @@ def print_block_groups(mountpoint):
try:
fd = os.open(mountpoint, os.O_RDONLY)
- st = os.fstat(fd)
+ os.fstat(fd)
except Exception, e:
sys.stderr.write("Failed to open %s (%s)\n" % (mountpoint, e))
return -1
@@ -336,7 +335,7 @@ def print_block_groups(mountpoint):
h = ctypes.addressof(header)
p_left = args_buffer_size
- for x in xrange(0, s.args.nr_items):
+ for _ in xrange(0, s.args.nr_items):
# for each itme, copy the header from the buffer into
# our header struct
ctypes.memmove(h, p, header_size)
diff --git a/btrfs-fragments.c b/btrfs-fragments.c
index eb75eb7a..46c78d2b 100644
--- a/btrfs-fragments.c
+++ b/btrfs-fragments.c
@@ -218,7 +218,7 @@ list_fragments(int fd, u64 flags, char *dir)
memset(&args, 0, sizeof(args));
- sk->tree_id = 2;
+ sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID;
sk->max_type = -1;
sk->min_type = 0;
sk->max_objectid = (u64)-1;
diff --git a/btrfs-list.c b/btrfs-list.c
index 4cc2ed49..8eec05ea 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -113,7 +113,7 @@ void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
{
int i;
- BUG_ON(column < 0 || column > BTRFS_LIST_ALL);
+ ASSERT(0 <= column && column <= BTRFS_LIST_ALL);
if (column < BTRFS_LIST_ALL) {
btrfs_list_columns[column].need_print = 1;
@@ -124,11 +124,6 @@ void btrfs_list_setup_print_column(enum btrfs_list_column_enum column)
btrfs_list_columns[i].need_print = 1;
}
-static void root_lookup_init(struct root_lookup *tree)
-{
- tree->root.rb_node = NULL;
-}
-
static int comp_entry_with_rootid(struct root_info *entry1,
struct root_info *entry2,
int is_descending)
@@ -237,20 +232,15 @@ struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
return set;
}
-void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set)
-{
- free(comp_set);
-}
-
static int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
enum btrfs_list_comp_enum comparer, int is_descending)
{
struct btrfs_list_comparer_set *set = *comp_set;
int size;
- BUG_ON(!set);
- BUG_ON(comparer >= BTRFS_LIST_COMP_MAX);
- BUG_ON(set->ncomps > set->total);
+ ASSERT(set != NULL);
+ ASSERT(comparer < BTRFS_LIST_COMP_MAX);
+ ASSERT(set->ncomps <= set->total);
if (set->ncomps == set->total) {
void *tmp;
@@ -272,7 +262,7 @@ static int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
*comp_set = set;
}
- BUG_ON(set->comps[set->ncomps].comp_func);
+ ASSERT(set->comps[set->ncomps].comp_func == NULL);
set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
set->comps[set->ncomps].is_descending = is_descending;
@@ -287,7 +277,7 @@ static int sort_comp(struct root_info *entry1, struct root_info *entry2,
int i, ret = 0;
if (!set || !set->ncomps)
- goto comp_rootid;
+ return comp_entry_with_rootid(entry1, entry2, 0);
for (i = 0; i < set->ncomps; i++) {
if (!set->comps[i].comp_func)
@@ -302,10 +292,8 @@ static int sort_comp(struct root_info *entry1, struct root_info *entry2,
rootid_compared = 1;
}
- if (!rootid_compared) {
-comp_rootid:
+ if (!rootid_compared)
ret = comp_entry_with_rootid(entry1, entry2, 0);
- }
return ret;
}
@@ -399,7 +387,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
static int update_root(struct root_lookup *root_lookup,
u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
- time_t ot, void *uuid, void *puuid, void *ruuid)
+ time_t otime, u8 *uuid, u8 *puuid, u8 *ruuid)
{
struct root_info *ri;
@@ -431,8 +419,8 @@ static int update_root(struct root_lookup *root_lookup,
ri->ogen = ogen;
if (!ri->ogen && root_offset)
ri->ogen = root_offset;
- if (ot)
- ri->otime = ot;
+ if (otime)
+ ri->otime = otime;
if (uuid)
memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
if (puuid)
@@ -454,7 +442,7 @@ static int update_root(struct root_lookup *root_lookup,
* name_len: the length of name
* ogen: the original generation of the root
* gen: the current generation of the root
- * ot: the original time(create time) of the root
+ * otime: the original time (creation time) of the root
* uuid: uuid of the root
* puuid: uuid of the root parent if any
* ruuid: uuid of the received subvol, if any
@@ -462,13 +450,13 @@ static int update_root(struct root_lookup *root_lookup,
static int add_root(struct root_lookup *root_lookup,
u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
- time_t ot, void *uuid, void *puuid, void *ruuid)
+ time_t otime, u8 *uuid, u8 *puuid, u8 *ruuid)
{
struct root_info *ri;
int ret;
ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags,
- dir_id, name, name_len, ogen, gen, ot,
+ dir_id, name, name_len, ogen, gen, otime,
uuid, puuid, ruuid);
if (!ret)
return 0;
@@ -503,8 +491,8 @@ static int add_root(struct root_lookup *root_lookup,
ri->ogen = ogen;
if (!ri->ogen && root_offset)
ri->ogen = root_offset;
- if (ot)
- ri->otime = ot;
+ if (otime)
+ ri->otime = otime;
if (uuid)
memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
@@ -516,14 +504,27 @@ static int add_root(struct root_lookup *root_lookup,
memcpy(&ri->ruuid, ruuid, BTRFS_UUID_SIZE);
ret = root_tree_insert(root_lookup, ri);
- if (ret) {
- printf("failed to insert tree %llu\n", (unsigned long long)root_id);
+ if (ret < 0) {
+ error("failed to insert subvolume %llu to tree: %s",
+ (unsigned long long)root_id, strerror(-ret));
exit(1);
}
return 0;
}
-static void __free_root_info(struct rb_node *node)
+/*
+ * Simplified add_root for back references, omits the uuid and original info
+ * parameters, root offset and flags.
+ */
+static int add_root_backref(struct root_lookup *root_lookup, u64 root_id,
+ u64 ref_tree, u64 dir_id, char *name, int name_len)
+{
+ return add_root(root_lookup, root_id, ref_tree, 0, 0, dir_id, name,
+ name_len, 0, 0, 0, NULL, NULL, NULL);
+}
+
+
+static void free_root_info(struct rb_node *node)
{
struct root_info *ri;
@@ -534,11 +535,6 @@ static void __free_root_info(struct rb_node *node)
free(ri);
}
-static inline void __free_all_subvolumn(struct root_lookup *root_tree)
-{
- rb_free_nodes(&root_tree->root, __free_root_info);
-}
-
/*
* for a given root_info, search through the root_lookup tree to construct
* the full path name to it.
@@ -648,9 +644,8 @@ static int lookup_ino_path(int fd, struct root_info *ri)
ri->ref_tree = 0;
return -ENOENT;
}
- fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n",
- (unsigned long long)ri->ref_tree,
- strerror(errno));
+ error("failed to lookup path for root %llu: %s",
+ (unsigned long long)ri->ref_tree, strerror(errno));
return ret;
}
@@ -700,7 +695,7 @@ 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) {
- fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
+ error("failed to lookup path for dirid %llu: %s",
(unsigned long long)BTRFS_FIRST_FREE_OBJECTID,
strerror(errno));
return 0;
@@ -708,7 +703,7 @@ static u64 find_root_gen(int fd)
memset(&args, 0, sizeof(args));
- sk->tree_id = 1;
+ sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
/*
* there may be more than one ROOT_ITEM key if there are
@@ -726,8 +721,7 @@ static u64 find_root_gen(int fd)
while (1) {
ret = ioctl(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: %s", strerror(errno));
return 0;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -787,7 +781,7 @@ static char *__ino_resolve(int fd, u64 dirid)
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
if (ret < 0) {
- fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
+ error("failed to lookup path for dirid %llu: %s",
(unsigned long long)dirid, strerror(errno));
return ERR_PTR(ret);
}
@@ -813,9 +807,10 @@ static char *__ino_resolve(int fd, u64 dirid)
* simple string builder, returning a new string with both
* dirid and name
*/
-static char *build_name(char *dirid, char *name)
+static char *build_name(const char *dirid, const char *name)
{
char *full;
+
if (!dirid)
return strdup(name);
@@ -865,8 +860,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) {
- fprintf(stderr, "ERROR: can't perform the search - %s\n",
- strerror(errno));
+ error("can't perform the search: %s", strerror(errno));
return NULL;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -925,7 +919,7 @@ int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
* search for a dir item with a name 'default' in the tree of
* tree roots, it should point us to a default root
*/
- sk->tree_id = 1;
+ sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
/* don't worry about ancient format and request only one item */
sk->nr_items = 1;
@@ -965,7 +959,7 @@ out:
return 0;
}
-static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
+static int list_subvol_search(int fd, struct root_lookup *root_lookup)
{
int ret;
struct btrfs_ioctl_search_args args;
@@ -973,7 +967,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
struct btrfs_ioctl_search_header sh;
struct btrfs_root_ref *ref;
struct btrfs_root_item *ri;
- unsigned long off = 0;
+ unsigned long off;
int name_len;
char *name;
u64 dir_id;
@@ -981,42 +975,24 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
u64 ogen;
u64 flags;
int i;
- time_t t;
- u8 uuid[BTRFS_UUID_SIZE];
- u8 puuid[BTRFS_UUID_SIZE];
- u8 ruuid[BTRFS_UUID_SIZE];
- root_lookup_init(root_lookup);
+ root_lookup->root.rb_node = NULL;
memset(&args, 0, sizeof(args));
- /* search in the tree of tree roots */
- sk->tree_id = 1;
-
- /*
- * set the min and max to backref keys. The search will
- * only send back this type of key now.
- */
- sk->max_type = BTRFS_ROOT_BACKREF_KEY;
+ sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
+ /* Search both live and deleted subvolumes */
sk->min_type = BTRFS_ROOT_ITEM_KEY;
-
- sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
-
- /*
- * set all the other params to the max, we'll take any objectid
- * and any trans
- */
+ sk->max_type = BTRFS_ROOT_BACKREF_KEY;
+ sk->min_objectid = BTRFS_FS_TREE_OBJECTID;
sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
sk->max_offset = (u64)-1;
sk->max_transid = (u64)-1;
- /* just a big number, doesn't matter much */
- sk->nr_items = 4096;
-
while(1) {
+ sk->nr_items = 4096;
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0)
return ret;
- /* the ioctl returns the number of item it found in nr_items */
if (sk->nr_items == 0)
break;
@@ -1035,22 +1011,29 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
name = (char *)(ref + 1);
dir_id = btrfs_stack_root_ref_dirid(ref);
- add_root(root_lookup, sh.objectid, sh.offset,
- 0, 0, dir_id, name, name_len, 0, 0, 0,
- NULL, NULL, NULL);
- } else if (sh.type == BTRFS_ROOT_ITEM_KEY) {
+ add_root_backref(root_lookup, sh.objectid,
+ sh.offset, dir_id, name,
+ name_len);
+ } else if (sh.type == BTRFS_ROOT_ITEM_KEY &&
+ (sh.objectid >= BTRFS_FIRST_FREE_OBJECTID ||
+ sh.objectid == BTRFS_FS_TREE_OBJECTID)) {
+ time_t otime;
+ u8 uuid[BTRFS_UUID_SIZE];
+ u8 puuid[BTRFS_UUID_SIZE];
+ u8 ruuid[BTRFS_UUID_SIZE];
+
ri = (struct btrfs_root_item *)(args.buf + off);
gen = btrfs_root_generation(ri);
flags = btrfs_root_flags(ri);
if(sh.len >
sizeof(struct btrfs_root_item_v0)) {
- t = btrfs_stack_timespec_sec(&ri->otime);
+ otime = btrfs_stack_timespec_sec(&ri->otime);
ogen = btrfs_root_otransid(ri);
memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);
memcpy(ruuid, ri->received_uuid, BTRFS_UUID_SIZE);
} else {
- t = 0;
+ otime = 0;
ogen = 0;
memset(uuid, 0, BTRFS_UUID_SIZE);
memset(puuid, 0, BTRFS_UUID_SIZE);
@@ -1059,22 +1042,17 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
add_root(root_lookup, sh.objectid, 0,
sh.offset, flags, 0, NULL, 0, ogen,
- gen, t, uuid, puuid, ruuid);
+ gen, otime, uuid, puuid, ruuid);
}
off += sh.len;
- /*
- * record the mins in sk so we can make sure the
- * next search doesn't repeat this root
- */
sk->min_objectid = sh.objectid;
sk->min_type = sh.type;
sk->min_offset = sh.offset;
}
- sk->nr_items = 4096;
sk->min_offset++;
- if (!sk->min_offset) /* overflow */
+ if (!sk->min_offset)
sk->min_type++;
else
continue;
@@ -1209,20 +1187,19 @@ struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
return set;
}
-void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set)
-{
- free(filter_set);
-}
-
-int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
+/*
+ * Setup list filters. Exit if there's not enough memory, as we can't continue
+ * without the structures set up properly.
+ */
+void btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
enum btrfs_list_filter_enum filter, u64 data)
{
struct btrfs_list_filter_set *set = *filter_set;
int size;
- BUG_ON(!set);
- BUG_ON(filter >= BTRFS_LIST_FILTER_MAX);
- BUG_ON(set->nfilters > set->total);
+ ASSERT(set != NULL);
+ ASSERT(filter < BTRFS_LIST_FILTER_MAX);
+ ASSERT(set->nfilters <= set->total);
if (set->nfilters == set->total) {
void *tmp;
@@ -1244,7 +1221,7 @@ int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
*filter_set = set;
}
- BUG_ON(set->filters[set->nfilters].filter_func);
+ ASSERT(set->filters[set->nfilters].filter_func == NULL);
if (filter == BTRFS_LIST_FILTER_DELETED)
set->only_deleted = 1;
@@ -1252,7 +1229,6 @@ int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
set->filters[set->nfilters].data = data;
set->nfilters++;
- return 0;
}
static int filter_root(struct root_info *ri,
@@ -1279,7 +1255,7 @@ static int filter_root(struct root_info *ri,
return 1;
}
-static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
+static void filter_and_sort_subvol(struct root_lookup *all_subvols,
struct root_lookup *sort_tree,
struct btrfs_list_filter_set *filter_set,
struct btrfs_list_comparer_set *comp_set,
@@ -1289,7 +1265,7 @@ static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
struct root_info *entry;
int ret;
- root_lookup_init(sort_tree);
+ sort_tree->root.rb_node = NULL;
n = rb_last(&all_subvols->root);
while (n) {
@@ -1307,7 +1283,7 @@ static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
}
}
-static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
+static int list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
{
struct rb_node *n;
@@ -1331,7 +1307,7 @@ static void print_subvolume_column(struct root_info *subv,
char tstr[256];
char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
- BUG_ON(column >= BTRFS_LIST_ALL || column < 0);
+ ASSERT(0 <= column && column < BTRFS_LIST_ALL);
switch (column) {
case BTRFS_LIST_OBJECTID:
@@ -1389,7 +1365,8 @@ static void print_subvolume_column(struct root_info *subv,
}
}
-static void print_single_volume_info_raw(struct root_info *subv, char *raw_prefix)
+static void print_one_subvol_info_raw(struct root_info *subv,
+ const char *raw_prefix)
{
int i;
@@ -1405,7 +1382,7 @@ static void print_single_volume_info_raw(struct root_info *subv, char *raw_prefi
printf("\n");
}
-static void print_single_volume_info_table(struct root_info *subv)
+static void print_one_subvol_info_table(struct root_info *subv)
{
int i;
@@ -1424,7 +1401,7 @@ static void print_single_volume_info_table(struct root_info *subv)
printf("\n");
}
-static void print_single_volume_info_default(struct root_info *subv)
+static void print_one_subvol_info_default(struct root_info *subv)
{
int i;
@@ -1441,7 +1418,7 @@ static void print_single_volume_info_default(struct root_info *subv)
printf("\n");
}
-static void print_all_volume_info_tab_head(void)
+static void print_all_subvol_info_tab_head(void)
{
int i;
int len;
@@ -1470,27 +1447,27 @@ static void print_all_volume_info_tab_head(void)
}
}
-static void print_all_volume_info(struct root_lookup *sorted_tree,
- int layout, char *raw_prefix)
+static void print_all_subvol_info(struct root_lookup *sorted_tree,
+ enum btrfs_list_layout layout, const char *raw_prefix)
{
struct rb_node *n;
struct root_info *entry;
if (layout == BTRFS_LIST_LAYOUT_TABLE)
- print_all_volume_info_tab_head();
+ print_all_subvol_info_tab_head();
n = rb_first(&sorted_tree->root);
while (n) {
entry = rb_entry(n, struct root_info, sort_node);
switch (layout) {
case BTRFS_LIST_LAYOUT_DEFAULT:
- print_single_volume_info_default(entry);
+ print_one_subvol_info_default(entry);
break;
case BTRFS_LIST_LAYOUT_TABLE:
- print_single_volume_info_table(entry);
+ print_one_subvol_info_table(entry);
break;
case BTRFS_LIST_LAYOUT_RAW:
- print_single_volume_info_raw(entry, raw_prefix);
+ print_one_subvol_info_raw(entry, raw_prefix);
break;
}
n = rb_next(n);
@@ -1501,10 +1478,9 @@ static int btrfs_list_subvols(int fd, struct root_lookup *root_lookup)
{
int ret;
- ret = __list_subvol_search(fd, root_lookup);
+ ret = list_subvol_search(fd, root_lookup);
if (ret) {
- fprintf(stderr, "ERROR: can't perform the search - %s\n",
- strerror(errno));
+ error("can't perform the search: %s", strerror(errno));
return ret;
}
@@ -1512,13 +1488,14 @@ static int btrfs_list_subvols(int fd, struct root_lookup *root_lookup)
* now we have an rbtree full of root_info objects, but we need to fill
* in their path names within the subvol that is referencing each one.
*/
- ret = __list_subvol_fill_paths(fd, root_lookup);
+ ret = list_subvol_fill_paths(fd, root_lookup);
return ret;
}
int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
struct btrfs_list_comparer_set *comp_set,
- int layout, int full_path, char *raw_prefix)
+ enum btrfs_list_layout layout, int full_path,
+ const char *raw_prefix)
{
struct root_lookup root_lookup;
struct root_lookup root_sort;
@@ -1533,11 +1510,11 @@ int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
ret = btrfs_list_subvols(fd, &root_lookup);
if (ret)
return ret;
- __filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
+ filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
comp_set, top_id);
- print_all_volume_info(&root_sort, layout, raw_prefix);
- __free_all_subvolumn(&root_lookup);
+ print_all_subvol_info(&root_sort, layout, raw_prefix);
+ rb_free_nodes(&root_lookup.root, free_root_info);
return 0;
}
@@ -1549,6 +1526,37 @@ static char *strdup_or_null(const char *s)
return strdup(s);
}
+int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri)
+{
+ int ret;
+ struct root_lookup rl;
+ struct rb_node *rbn;
+ struct root_info *ri;
+ u64 root_id;
+
+ ret = btrfs_list_get_path_rootid(fd, &root_id);
+ if (ret)
+ return ret;
+
+ ret = btrfs_list_subvols(fd, &rl);
+ if (ret)
+ return ret;
+
+ rbn = rb_first(&rl.root);
+ ri = rb_entry(rbn, struct root_info, rb_node);
+
+ if (ri->root_id != BTRFS_FS_TREE_OBJECTID)
+ return -ENOENT;
+
+ memcpy(the_ri, ri, offsetof(struct root_info, path));
+ the_ri->path = strdup_or_null("/");
+ the_ri->name = strdup_or_null("<FS_TREE>");
+ the_ri->full_path = strdup_or_null("/");
+ rb_free_nodes(&rl.root, free_root_info);
+
+ return ret;
+}
+
int btrfs_get_subvol(int fd, struct root_info *the_ri)
{
int ret, rr;
@@ -1584,7 +1592,7 @@ int btrfs_get_subvol(int fd, struct root_info *the_ri)
}
rbn = rb_next(rbn);
}
- __free_all_subvolumn(&rl);
+ rb_free_nodes(&rl.root, free_root_info);
return ret;
}
@@ -1631,8 +1639,8 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
disk_offset = 0;
len = btrfs_stack_file_extent_ram_bytes(item);
} else {
- printf("unhandled extent type %d for inode %llu "
- "file offset %llu gen %llu\n",
+ error(
+ "unhandled extent type %d for inode %llu file offset %llu gen %llu",
type,
(unsigned long long)btrfs_search_header_objectid(sh),
(unsigned long long)btrfs_search_header_offset(sh),
@@ -1706,8 +1714,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) {
- fprintf(stderr, "ERROR: can't perform the search - %s\n",
- strerror(errno));
+ error("can't perform the search: %s", strerror(errno));
break;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -1778,11 +1785,11 @@ char *btrfs_list_path_for_root(int fd, u64 root)
if (ret)
return ERR_PTR(ret);
- ret = __list_subvol_search(fd, &root_lookup);
+ ret = list_subvol_search(fd, &root_lookup);
if (ret < 0)
return ERR_PTR(ret);
- ret = __list_subvol_fill_paths(fd, &root_lookup);
+ ret = list_subvol_fill_paths(fd, &root_lookup);
if (ret < 0)
return ERR_PTR(ret);
@@ -1803,7 +1810,7 @@ char *btrfs_list_path_for_root(int fd, u64 root)
n = rb_prev(n);
}
- __free_all_subvolumn(&root_lookup);
+ rb_free_nodes(&root_lookup.root, free_root_info);
return ret_path;
}
@@ -1897,19 +1904,12 @@ int btrfs_list_parse_filter_string(char *opt_arg,
int btrfs_list_get_path_rootid(int fd, u64 *treeid)
{
- int ret;
- struct btrfs_ioctl_ino_lookup_args args;
-
- memset(&args, 0, sizeof(args));
- args.objectid = BTRFS_FIRST_FREE_OBJECTID;
+ int ret;
- ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
- if (ret < 0) {
- fprintf(stderr,
- "ERROR: can't perform the search - %s\n",
+ ret = lookup_path_rootid(fd, treeid);
+ if (ret < 0)
+ error("cannot resolve rootid for path: %s",
strerror(errno));
- return ret;
- }
- *treeid = args.treeid;
- return 0;
+
+ return ret;
}
diff --git a/btrfs-list.h b/btrfs-list.h
index 13f44c3a..6e5fc778 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -31,9 +31,11 @@
#include <time.h>
-#define BTRFS_LIST_LAYOUT_DEFAULT 0
-#define BTRFS_LIST_LAYOUT_TABLE 1
-#define BTRFS_LIST_LAYOUT_RAW 2
+enum btrfs_list_layout {
+ BTRFS_LIST_LAYOUT_DEFAULT = 0,
+ BTRFS_LIST_LAYOUT_TABLE,
+ BTRFS_LIST_LAYOUT_RAW
+};
/*
* one of these for each root we find.
@@ -160,19 +162,19 @@ int btrfs_list_parse_filter_string(char *optarg,
enum btrfs_list_filter_enum type);
void btrfs_list_setup_print_column(enum btrfs_list_column_enum column);
struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void);
-void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set);
-int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
+void btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
enum btrfs_list_filter_enum filter, u64 data);
struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void);
-void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set);
int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
struct btrfs_list_comparer_set *comp_set,
- int is_tab_result, int full_path, char *raw_prefix);
+ enum btrfs_list_layout layot, int full_path,
+ const char *raw_prefix);
int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen);
int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
char *btrfs_list_path_for_root(int fd, u64 root);
int btrfs_list_get_path_rootid(int fd, u64 *treeid);
int btrfs_get_subvol(int fd, struct root_info *the_ri);
+int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri);
#endif
diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c
index f3be1ea8..e49a735e 100644
--- a/btrfs-map-logical.c
+++ b/btrfs-map-logical.c
@@ -125,7 +125,7 @@ static int __print_mapping_info(struct btrfs_fs_info *fs_info, u64 logical,
multi->stripes[0].physical,
device->name);
}
- kfree(multi);
+ free(multi);
multi = NULL;
cur_offset += cur_len;
}
diff --git a/btrfs-show-super.c b/btrfs-show-super.c
index 3e2ceb58..f97f83ba 100644
--- a/btrfs-show-super.c
+++ b/btrfs-show-super.c
@@ -27,6 +27,9 @@ int main(int argc, char **argv)
set_argv0(argv);
+ warning(
+"\nthe tool has been deprecated, please use 'btrfs inspect-internal dump-super' instead\n");
+
if (argc > 1 && !strcmp(argv[1], "--help"))
usage(cmd_inspect_dump_super_usage);
diff --git a/btrfstune.c b/btrfstune.c
index b824f2b8..e8e3d00a 100644
--- a/btrfstune.c
+++ b/btrfstune.c
@@ -50,18 +50,18 @@ static int update_seeding_flag(struct btrfs_root *root, int set_flag)
if (force)
return 0;
else
- fprintf(stderr, "seeding flag is already set on %s\n", device);
+ warning("seeding flag is already set on %s",
+ device);
return 1;
}
super_flags |= BTRFS_SUPER_FLAG_SEEDING;
} else {
if (!(super_flags & BTRFS_SUPER_FLAG_SEEDING)) {
- fprintf(stderr, "seeding flag is not set on %s\n",
- device);
+ warning("seeding flag is not set on %s", device);
return 1;
}
super_flags &= ~BTRFS_SUPER_FLAG_SEEDING;
- fprintf(stderr, "Warning: Seeding flag cleared.\n");
+ warning("seeding flag cleared on %s", device);
}
trans = btrfs_start_transaction(root, 1);
@@ -118,19 +118,16 @@ static int change_header_uuid(struct btrfs_root *root, struct extent_buffer *eb)
static int change_extents_uuid(struct btrfs_fs_info *fs_info)
{
struct btrfs_root *root = fs_info->extent_root;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_key key = {0, 0, 0};
int ret = 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
+ btrfs_init_path(&path);
/*
* Here we don't use transaction as it will takes a lot of reserve
* space, and that will make a near-full btrfs unable to change uuid
*/
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0)
goto out;
@@ -140,33 +137,32 @@ static int change_extents_uuid(struct btrfs_fs_info *fs_info)
u64 flags;
u64 bytenr;
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
if (key.type != BTRFS_EXTENT_ITEM_KEY &&
key.type != BTRFS_METADATA_ITEM_KEY)
goto next;
- ei = btrfs_item_ptr(path->nodes[0], 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);
+ flags = btrfs_extent_flags(path.nodes[0], ei);
if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK))
goto next;
bytenr = key.objectid;
eb = read_tree_block(root, bytenr, root->nodesize, 0);
if (IS_ERR(eb)) {
- fprintf(stderr, "Failed to read tree block: %llu\n",
- bytenr);
+ error("failed to read tree block: %llu", bytenr);
ret = PTR_ERR(eb);
goto out;
}
ret = change_header_uuid(root, eb);
free_extent_buffer(eb);
if (ret < 0) {
- fprintf(stderr, "Failed to change uuid of tree block: %llu\n",
+ error("failed to change uuid of tree block: %llu",
bytenr);
goto out;
}
next:
- ret = btrfs_next_item(root, path);
+ ret = btrfs_next_item(root, &path);
if (ret < 0)
goto out;
if (ret > 0) {
@@ -176,7 +172,7 @@ next:
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -204,28 +200,26 @@ static int change_device_uuid(struct btrfs_root *root, struct extent_buffer *eb,
static int change_devices_uuid(struct btrfs_fs_info *fs_info)
{
struct btrfs_root *root = fs_info->chunk_root;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_key key = {0, 0, 0};
int ret = 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ btrfs_init_path(&path);
/* No transaction again */
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0)
goto out;
while (1) {
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
if (key.type != BTRFS_DEV_ITEM_KEY ||
key.objectid != BTRFS_DEV_ITEMS_OBJECTID)
goto next;
- ret = change_device_uuid(root, path->nodes[0], path->slots[0]);
+ ret = change_device_uuid(root, path.nodes[0], path.slots[0]);
if (ret < 0)
goto out;
next:
- ret = btrfs_next_item(root, path);
+ ret = btrfs_next_item(root, &path);
if (ret < 0)
goto out;
if (ret > 0) {
@@ -234,7 +228,7 @@ next:
}
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -310,8 +304,8 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str)
uuid_parse(new_fsid_str, tmp);
if (memcmp(tmp, new_fsid, BTRFS_FSID_SIZE)) {
- fprintf(stderr,
- "ERROR: New fsid %s is not the same with unfinished fsid change\n",
+ error(
+ "new fsid %s is not the same with unfinished fsid change",
new_fsid_str);
return -EINVAL;
}
@@ -343,7 +337,7 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str)
printf("Change fsid in extents\n");
ret = change_extents_uuid(fs_info);
if (ret < 0) {
- fprintf(stderr, "Failed to change UUID of metadata\n");
+ error("failed to change UUID of metadata: %d", ret);
goto out;
}
@@ -351,7 +345,7 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str)
printf("Change fsid on devices\n");
ret = change_devices_uuid(fs_info);
if (ret < 0) {
- fprintf(stderr, "Failed to change UUID of devices\n");
+ error("failed to change UUID of devices: %d", ret);
goto out;
}
@@ -448,13 +442,11 @@ int main(int argc, char *argv[])
}
if (random_fsid && new_fsid_str) {
- fprintf(stderr,
- "ERROR: Random fsid can't be used with specified fsid\n");
+ error("random fsid can't be used with specified fsid");
return 1;
}
if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str)) {
- fprintf(stderr,
- "ERROR: At least one option should be assigned.\n");
+ error("at least one option should be specified");
print_usage();
return 1;
}
@@ -464,39 +456,36 @@ int main(int argc, char *argv[])
ret = uuid_parse(new_fsid_str, tmp);
if (ret < 0) {
- fprintf(stderr,
- "ERROR: Could not parse UUID: %s\n",
- new_fsid_str);
+ error("could not parse UUID: %s", new_fsid_str);
return 1;
}
if (!test_uuid_unique(new_fsid_str)) {
- fprintf(stderr,
- "ERROR: Fsid %s is not unique\n",
- new_fsid_str);
+ error("fsid %s is not unique", new_fsid_str);
return 1;
}
}
ret = check_mounted(device);
if (ret < 0) {
- fprintf(stderr, "Could not check mount status: %s\n",
+ error("could not check mount status of %s: %s", device,
strerror(-ret));
return 1;
} else if (ret) {
- fprintf(stderr, "%s is mounted\n", device);
+ error("%s is mounted", device);
return 1;
}
root = open_ctree(device, 0, ctree_flags);
if (!root) {
- fprintf(stderr, "Open ctree failed\n");
+ error("open ctree failed");
return 1;
}
if (seeding_flag) {
if (!seeding_value && !force) {
- fprintf(stderr, "Warning: This is dangerous, clearing the seeding flag may cause the derived device not to be mountable!\n");
+ warning(
+"this is dangerous, clearing the seeding flag may cause the derived device not to be mountable!");
ret = ask_user("We are going to clear the seeding flag, are you sure?");
if (!ret) {
fprintf(stderr, "Clear seeding flag canceled\n");
@@ -520,10 +509,10 @@ int main(int argc, char *argv[])
if (random_fsid || new_fsid_str) {
if (!force) {
- fprintf(stderr,
- "Warning: It's highly recommended to run 'btrfs check' before this operation\n");
- fprintf(stderr,
- "Also canceling running UUID change progress may cause corruption\n");
+ warning(
+ "it's highly recommended to run 'btrfs check' before this operation");
+ warning(
+ "also canceling running UUID change progress may cause corruption");
ret = ask_user("We are going to change UUID, are your sure?");
if (!ret) {
fprintf(stderr, "UUID change canceled\n");
@@ -542,7 +531,7 @@ int main(int argc, char *argv[])
} else {
root->fs_info->readonly = 1;
ret = 1;
- fprintf(stderr, "btrfstune failed\n");
+ error("btrfstune failed");
}
out:
close_ctree(root);
diff --git a/chunk-recover.c b/chunk-recover.c
index 4a081db2..e6b26ac3 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -968,7 +968,7 @@ static int build_device_map_by_chunk_record(struct btrfs_root *root,
map->stripes[i].dev = btrfs_find_device(root, devid,
uuid, NULL);
if (!map->stripes[i].dev) {
- kfree(map);
+ free(map);
return -EIO;
}
}
@@ -1398,26 +1398,24 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans,
{
struct chunk_record *chunk_rec;
struct btrfs_key search_key;
- struct btrfs_path *path;
+ struct btrfs_path path;
u64 used = 0;
int ret = 0;
if (list_empty(&rc->rebuild_chunks))
return 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ btrfs_init_path(&path);
list_for_each_entry(chunk_rec, &rc->rebuild_chunks, list) {
search_key.objectid = chunk_rec->offset;
search_key.type = BTRFS_EXTENT_ITEM_KEY;
search_key.offset = 0;
ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
- &search_key, path, 0, 0);
+ &search_key, &path, 0, 0);
if (ret < 0)
goto out;
ret = calculate_bg_used(root->fs_info->extent_root,
- chunk_rec, path, &used);
+ chunk_rec, &path, &used);
/*
* Extent tree is damaged, better to rebuild the whole extent
* tree. Currently, change the used to chunk's len to prevent
@@ -1432,7 +1430,7 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans,
"Mark the block group full to prevent block rsv problems\n");
used = chunk_rec->length;
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
ret = __insert_block_group(trans, chunk_rec,
root->fs_info->extent_root,
used);
@@ -1440,7 +1438,7 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans,
goto out;
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -1479,7 +1477,7 @@ open_ctree_with_broken_chunk(struct recover_control *rc)
memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
- ret = btrfs_check_fs_compatibility(disk_super, 1);
+ ret = btrfs_check_fs_compatibility(disk_super, OPEN_CTREE_WRITES);
if (ret)
goto out_devices;
@@ -1488,7 +1486,7 @@ open_ctree_with_broken_chunk(struct recover_control *rc)
sectorsize = btrfs_super_sectorsize(disk_super);
stripesize = btrfs_super_stripesize(disk_super);
- __setup_root(nodesize, leafsize, sectorsize, stripesize,
+ btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize,
fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
ret = build_device_maps_by_chunk_records(rc, fs_info->chunk_root);
@@ -1914,7 +1912,7 @@ static int check_one_csum(int fd, u64 start, u32 len, u32 tree_csum)
}
ret = 0;
csum_result = btrfs_csum_data(NULL, data, csum_result, len);
- btrfs_csum_final(csum_result, (char *)&csum_result);
+ btrfs_csum_final(csum_result, (u8 *)&csum_result);
if (csum_result != tree_csum)
ret = 1;
out:
@@ -1947,9 +1945,12 @@ static int insert_stripe(struct list_head *devexts,
dev = btrfs_find_device_by_devid(rc->fs_devices, devext->objectid,
0);
if (!dev)
- return 1;
- BUG_ON(btrfs_find_device_by_devid(rc->fs_devices, devext->objectid,
- 1));
+ return -ENOENT;
+ if (btrfs_find_device_by_devid(rc->fs_devices, devext->objectid, 1)) {
+ error("unexpected: found another device with id %llu",
+ (unsigned long long)devext->objectid);
+ return -EINVAL;
+ }
chunk->stripes[index].devid = devext->objectid;
chunk->stripes[index].offset = devext->offset;
@@ -2247,6 +2248,13 @@ static int btrfs_recover_chunks(struct recover_control *rc)
chunk->sub_stripes = calc_sub_nstripes(bg->flags);
ret = insert_cache_extent(&rc->chunk, &chunk->cache);
+ if (ret == -EEXIST) {
+ error("duplicate entry in cache start %llu size %llu",
+ (unsigned long long)chunk->cache.start,
+ (unsigned long long)chunk->cache.size);
+ free(chunk);
+ return ret;
+ }
BUG_ON(ret);
list_del_init(&bg->list);
diff --git a/cmds-check.c b/cmds-check.c
index df97d3b6..37e5ff18 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -41,6 +41,7 @@
#include "rbtree-utils.h"
#include "backref.h"
#include "ulist.h"
+#include "hash.h"
enum task_position {
TASK_EXTENTS,
@@ -112,6 +113,24 @@ struct data_backref {
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 */
+
static inline struct data_backref* to_data_backref(struct extent_backref *back)
{
return container_of(back, struct data_backref, node);
@@ -185,9 +204,9 @@ struct inode_backref {
unsigned int found_dir_item:1;
unsigned int found_dir_index:1;
unsigned int found_inode_ref:1;
- unsigned int filetype:8;
+ u8 filetype;
+ u8 ref_type;
int errors;
- unsigned int ref_type;
u64 dir;
u64 index;
u16 namelen;
@@ -659,6 +678,7 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
struct inode_backref *tmp;
struct orphan_data_extent *src_orphan;
struct orphan_data_extent *dst_orphan;
+ struct rb_node *rb;
size_t size;
int ret;
@@ -691,10 +711,21 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
list_add_tail(&dst_orphan->list, &rec->orphan_extents);
}
ret = copy_file_extent_holes(&rec->holes, &orig_rec->holes);
- BUG_ON(ret < 0);
+ if (ret < 0)
+ goto cleanup_rb;
return rec;
+cleanup_rb:
+ rb = rb_first(&rec->holes);
+ while (rb) {
+ struct file_extent_hole *hole;
+
+ hole = rb_entry(rb, struct file_extent_hole, node);
+ rb = rb_next(rb);
+ free(hole);
+ }
+
cleanup:
if (!list_empty(&rec->backrefs))
list_for_each_entry_safe(orig, tmp, &rec->backrefs, list) {
@@ -924,7 +955,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache,
struct cache_extent *cache;
struct inode_backref *tmp, *backref;
struct ptr_node *node;
- unsigned char filetype;
+ u8 filetype;
if (!rec->found_inode_item)
return;
@@ -1055,7 +1086,7 @@ static struct inode_backref *get_inode_backref(struct inode_record *rec,
static int add_inode_backref(struct cache_tree *inode_cache,
u64 ino, u64 dir, u64 index,
const char *name, int namelen,
- int filetype, int itemtype, int errors)
+ u8 filetype, u8 itemtype, int errors)
{
struct inode_record *rec;
struct inode_backref *backref;
@@ -1458,7 +1489,7 @@ static int process_dir_item(struct btrfs_root *root,
u32 data_len;
int error;
int nritems = 0;
- int filetype;
+ u8 filetype;
struct btrfs_dir_item *di;
struct inode_record *rec;
struct cache_tree *root_cache;
@@ -1826,6 +1857,96 @@ 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];
+};
+
+static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
+ struct node_refs *nrefs, u64 level);
+static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
+ unsigned int ext_ref);
+
+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);
+
+ 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,
+ nrefs, i);
+ 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;
+ /*
+ * Convert any error bitmap to -EIO, as we should avoid
+ * mixing positive and negative return value to represent
+ * error
+ */
+ if (err && !ret)
+ ret = -EIO;
+ return ret;
+}
+
static void reada_walk_down(struct btrfs_root *root,
struct extent_buffer *node, int slot)
{
@@ -1899,10 +2020,66 @@ static int check_child_node(struct btrfs_root *root,
return ret;
}
-struct node_refs {
- u64 bytenr[BTRFS_MAX_LEVEL];
- u64 refs[BTRFS_MAX_LEVEL];
-};
+/*
+ * 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;
+
+ if (roots->nnodes == 1)
+ 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.
+ */
+static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
+ struct node_refs *nrefs, u64 level)
+{
+ int check, ret;
+ u64 refs;
+ struct ulist *roots;
+
+ if (nrefs->bytenr[level] != bytenr) {
+ ret = btrfs_lookup_extent_info(NULL, root, bytenr,
+ level, 1, &refs, NULL);
+ if (ret < 0)
+ return ret;
+
+ nrefs->bytenr[level] = bytenr;
+ nrefs->refs[level] = refs;
+ 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 {
+ nrefs->need_check[level] = 1;
+ }
+ }
+
+ return 0;
+}
static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
struct walk_control *wc, int *level,
@@ -2033,6 +2210,110 @@ out:
return err;
}
+static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
+ unsigned int ext_ref);
+
+static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
+ int *level, struct node_refs *nrefs, int ext_ref)
+{
+ enum btrfs_tree_block_status status;
+ u64 bytenr;
+ u64 ptr_gen;
+ struct extent_buffer *next;
+ struct extent_buffer *cur;
+ u32 blocksize;
+ int ret;
+
+ WARN_ON(*level < 0);
+ WARN_ON(*level >= BTRFS_MAX_LEVEL);
+
+ ret = update_nodes_refs(root, path->nodes[*level]->start,
+ nrefs, *level);
+ if (ret < 0)
+ return ret;
+
+ while (*level >= 0) {
+ WARN_ON(*level < 0);
+ WARN_ON(*level >= BTRFS_MAX_LEVEL);
+ cur = path->nodes[*level];
+
+ if (btrfs_header_level(cur) != *level)
+ WARN_ON(1);
+
+ if (path->slots[*level] >= btrfs_header_nritems(cur))
+ break;
+ /* Don't forgot to check leaf/node validation */
+ if (*level == 0) {
+ ret = btrfs_check_leaf(root, NULL, cur);
+ if (ret != BTRFS_TREE_BLOCK_CLEAN) {
+ ret = -EIO;
+ break;
+ }
+ ret = process_one_leaf_v2(root, path, nrefs,
+ level, ext_ref);
+ break;
+ } else {
+ ret = btrfs_check_node(root, NULL, cur);
+ if (ret != BTRFS_TREE_BLOCK_CLEAN) {
+ ret = -EIO;
+ break;
+ }
+ }
+ bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
+ ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
+ blocksize = root->nodesize;
+
+ ret = update_nodes_refs(root, bytenr, nrefs, *level - 1);
+ if (ret)
+ break;
+ if (!nrefs->need_check[*level - 1]) {
+ path->slots[*level]++;
+ continue;
+ }
+
+ next = btrfs_find_tree_block(root, bytenr, blocksize);
+ if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
+ free_extent_buffer(next);
+ reada_walk_down(root, cur, path->slots[*level]);
+ next = read_tree_block(root, bytenr, blocksize,
+ ptr_gen);
+ 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(root->fs_info,
+ &node_key,
+ path->nodes[*level]->start,
+ root->nodesize, *level);
+ ret = -EIO;
+ break;
+ }
+ }
+
+ ret = check_child_node(root, cur, path->slots[*level], next);
+ 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);
+ ret = -EIO;
+ break;
+ }
+
+ *level = *level - 1;
+ free_extent_buffer(path->nodes[*level]);
+ path->nodes[*level] = next;
+ path->slots[*level] = 0;
+ }
+ return ret;
+}
+
static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path,
struct walk_control *wc, int *level)
{
@@ -2057,6 +2338,27 @@ static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path,
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;
+ }
+ }
+ return 1;
+}
+
static int check_root_dir(struct inode_record *rec)
{
struct inode_backref *backref;
@@ -2174,7 +2476,7 @@ static int add_missing_dir_index(struct btrfs_root *root,
struct inode_record *rec,
struct inode_backref *backref)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_trans_handle *trans;
struct btrfs_dir_item *dir_item;
struct extent_buffer *leaf;
@@ -2185,27 +2487,22 @@ static int add_missing_dir_index(struct btrfs_root *root,
u32 data_size = sizeof(*dir_item) + backref->namelen;
int ret;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- btrfs_free_path(path);
+ if (IS_ERR(trans))
return PTR_ERR(trans);
- }
fprintf(stderr, "repairing missing dir index item for inode %llu\n",
(unsigned long long)rec->ino);
+
+ btrfs_init_path(&path);
key.objectid = backref->dir;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = backref->index;
-
- ret = btrfs_insert_empty_item(trans, root, path, &key, data_size);
+ ret = btrfs_insert_empty_item(trans, root, &path, &key, data_size);
BUG_ON(ret);
- leaf = path->nodes[0];
- dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
+ leaf = path.nodes[0];
+ dir_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_dir_item);
disk_key.objectid = cpu_to_le64(rec->ino);
disk_key.type = BTRFS_INODE_ITEM_KEY;
@@ -2218,7 +2515,7 @@ static int add_missing_dir_index(struct btrfs_root *root,
name_ptr = (unsigned long)(dir_item + 1);
write_extent_buffer(leaf, backref->name, name_ptr, backref->namelen);
btrfs_mark_buffer_dirty(leaf);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
btrfs_commit_transaction(trans, root);
backref->found_dir_index = 1;
@@ -2243,31 +2540,25 @@ static int delete_dir_index(struct btrfs_root *root,
{
struct btrfs_trans_handle *trans;
struct btrfs_dir_item *di;
- struct btrfs_path *path;
+ struct btrfs_path path;
int ret = 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- btrfs_free_path(path);
+ if (IS_ERR(trans))
return PTR_ERR(trans);
- }
-
fprintf(stderr, "Deleting bad dir index [%llu,%u,%llu] root %llu\n",
(unsigned long long)backref->dir,
BTRFS_DIR_INDEX_KEY, (unsigned long long)backref->index,
(unsigned long long)root->objectid);
- di = btrfs_lookup_dir_index(trans, root, path, backref->dir,
+ btrfs_init_path(&path);
+ di = btrfs_lookup_dir_index(trans, root, &path, backref->dir,
backref->name, backref->namelen,
backref->index, -1);
if (IS_ERR(di)) {
ret = PTR_ERR(di);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
btrfs_commit_transaction(trans, root);
if (ret == -ENOENT)
return 0;
@@ -2275,11 +2566,11 @@ static int delete_dir_index(struct btrfs_root *root,
}
if (!di)
- ret = btrfs_del_item(trans, root, path);
+ ret = btrfs_del_item(trans, root, &path);
else
- ret = btrfs_delete_one_dir_name(trans, root, path, di);
+ ret = btrfs_delete_one_dir_name(trans, root, &path, di);
BUG_ON(ret);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
btrfs_commit_transaction(trans, root);
return ret;
}
@@ -2562,6 +2853,31 @@ 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;
+}
+
static int repair_inode_nlinks(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
@@ -2607,11 +2923,9 @@ static int repair_inode_nlinks(struct btrfs_trans_handle *trans,
}
if (rec->found_link == 0) {
- lost_found_ino = root->highest_inode;
- if (lost_found_ino >= BTRFS_LAST_FREE_OBJECTID) {
- ret = -EOVERFLOW;
+ 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,
@@ -2682,48 +2996,46 @@ out:
*/
static int find_normal_file_extent(struct btrfs_root *root, u64 ino)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_key key;
struct btrfs_key found_key;
struct btrfs_file_extent_item *fi;
u8 type;
int ret = 0;
- path = btrfs_alloc_path();
- if (!path)
- goto out;
+ btrfs_init_path(&path);
key.objectid = ino;
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = 0;
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0) {
ret = 0;
goto out;
}
- if (ret && path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
- ret = btrfs_next_leaf(root, path);
+ if (ret && path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(root, &path);
if (ret) {
ret = 0;
goto out;
}
}
while (1) {
- btrfs_item_key_to_cpu(path->nodes[0], &found_key,
- path->slots[0]);
+ btrfs_item_key_to_cpu(path.nodes[0], &found_key,
+ path.slots[0]);
if (found_key.objectid != ino ||
found_key.type != BTRFS_EXTENT_DATA_KEY)
break;
- fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_file_extent_item);
- type = btrfs_file_extent_type(path->nodes[0], fi);
+ type = btrfs_file_extent_type(path.nodes[0], fi);
if (type != BTRFS_FILE_EXTENT_INLINE) {
ret = 1;
goto out;
}
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -2914,7 +3226,7 @@ out:
static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
{
struct btrfs_trans_handle *trans;
- struct btrfs_path *path;
+ struct btrfs_path path;
int ret = 0;
if (!(rec->errors & (I_ERR_DIR_ISIZE_WRONG |
@@ -2926,10 +3238,6 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
I_ERR_FILE_NBYTES_WRONG)))
return rec->errors;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
/*
* For nlink repair, it may create a dir and add link, so
* 2 for parent(256)'s dir_index and dir_item
@@ -2938,27 +3246,26 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
* 2 for lost+found dir's dir_index and dir_item for the file
*/
trans = btrfs_start_transaction(root, 7);
- if (IS_ERR(trans)) {
- btrfs_free_path(path);
+ if (IS_ERR(trans))
return PTR_ERR(trans);
- }
+ btrfs_init_path(&path);
if (rec->errors & I_ERR_NO_INODE_ITEM)
- ret = repair_inode_no_item(trans, root, path, rec);
+ ret = repair_inode_no_item(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_FILE_EXTENT_ORPHAN)
- ret = repair_inode_orphan_extent(trans, root, path, rec);
+ ret = repair_inode_orphan_extent(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_FILE_EXTENT_DISCOUNT)
- ret = repair_inode_discount_extent(trans, root, path, rec);
+ ret = repair_inode_discount_extent(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_DIR_ISIZE_WRONG)
- ret = repair_inode_isize(trans, root, path, rec);
+ ret = repair_inode_isize(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_NO_ORPHAN_ITEM)
- ret = repair_inode_orphan_item(trans, root, path, rec);
+ ret = repair_inode_orphan_item(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_LINK_COUNT_WRONG)
- ret = repair_inode_nlinks(trans, root, path, rec);
+ ret = repair_inode_nlinks(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_FILE_NBYTES_WRONG)
- ret = repair_inode_nbytes(trans, root, path, rec);
+ ret = repair_inode_nbytes(trans, root, &path, rec);
btrfs_commit_transaction(trans, root);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -2982,21 +3289,6 @@ static int check_inode_recs(struct btrfs_root *root,
}
/*
- * We need to record the highest inode number for later 'lost+found'
- * dir creation.
- * We must select an ino not used/referred by any existing inode, or
- * 'lost+found' ino may be a missing ino in a corrupted leaf,
- * this may cause 'lost+found' dir has wrong nlinks.
- */
- cache = last_cache_extent(inode_cache);
- if (cache) {
- node = container_of(cache, struct ptr_node, cache);
- rec = node->data;
- if (rec->ino > root->highest_inode)
- root->highest_inode = rec->ino;
- }
-
- /*
* We need to repair backrefs first because we could change some of the
* errors in the inode recs.
*
@@ -3210,7 +3502,7 @@ static void free_root_record(struct cache_extent *cache)
free(backref);
}
- kfree(rec);
+ free(rec);
}
FREE_EXTENT_CACHE_BASED_TREE(root_recs, free_root_record);
@@ -3491,7 +3783,7 @@ static int repair_btree(struct btrfs_root *root,
struct cache_tree *corrupt_blocks)
{
struct btrfs_trans_handle *trans;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_corrupt_block *corrupt;
struct cache_extent *cache;
struct btrfs_key key;
@@ -3502,23 +3794,20 @@ static int repair_btree(struct btrfs_root *root,
if (cache_tree_empty(corrupt_blocks))
return 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
fprintf(stderr, "Error starting transaction: %s\n",
strerror(-ret));
- goto out_free_path;
+ return ret;
}
+ btrfs_init_path(&path);
cache = first_cache_extent(corrupt_blocks);
while (cache) {
corrupt = container_of(cache, struct btrfs_corrupt_block,
cache);
level = corrupt->level;
- path->lowest_level = level;
+ path.lowest_level = level;
key.objectid = corrupt->key.objectid;
key.type = corrupt->key.type;
key.offset = corrupt->key.offset;
@@ -3529,22 +3818,22 @@ static int repair_btree(struct btrfs_root *root,
* so ins_len set to 0 here.
* Balance will be done after all corrupt node/leaf is deleted.
*/
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
if (ret < 0)
goto out;
- offset = btrfs_node_blockptr(path->nodes[level],
- path->slots[level]);
+ offset = btrfs_node_blockptr(path.nodes[level],
+ path.slots[level]);
/* Remove the ptr */
- ret = btrfs_del_ptr(trans, root, path, level,
- path->slots[level]);
+ ret = btrfs_del_ptr(trans, root, &path, level,
+ path.slots[level]);
if (ret < 0)
goto out;
/*
* Remove the corresponding extent
* return value is not concerned.
*/
- btrfs_release_path(path);
+ btrfs_release_path(&path);
ret = btrfs_free_extent(trans, root, offset, root->nodesize,
0, root->root_key.objectid,
level - 1, 0);
@@ -3557,18 +3846,17 @@ static int repair_btree(struct btrfs_root *root,
corrupt = container_of(cache, struct btrfs_corrupt_block,
cache);
memcpy(&key, &corrupt->key, sizeof(key));
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
if (ret < 0)
goto out;
/* return will always >0 since it won't find the item */
ret = 0;
- btrfs_release_path(path);
+ btrfs_release_path(&path);
cache = next_cache_extent(cache);
}
out:
btrfs_commit_transaction(trans, root);
-out_free_path:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -3836,6 +4124,1100 @@ out:
return err;
}
+/*
+ * 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
+ * @ref_key: the key of the INODE_REF/INODE_EXTREF
+ * @key: the key of the DIR_ITEM/DIR_INDEX
+ * @index: the index in the INODE_REF/INODE_EXTREF, be used to
+ * distinguish root_dir between normal dir/file
+ * @name: the name in the INODE_REF/INODE_EXTREF
+ * @namelen: the length of name in the INODE_REF/INODE_EXTREF
+ * @mode: the st_mode of INODE_ITEM
+ *
+ * Return 0 if no error occurred.
+ * Return ROOT_DIR_ERROR if found DIR_ITEM/DIR_INDEX for root_dir.
+ * Return DIR_ITEM_MISSING if couldn't find DIR_ITEM/DIR_INDEX for normal
+ * dir/file.
+ * Return DIR_ITEM_MISMATCH if INODE_REF/INODE_EXTREF and DIR_ITEM/DIR_INDEX
+ * not match for normal dir/file.
+ */
+static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key,
+ struct btrfs_key *key, u64 index, char *name,
+ u32 namelen, u32 mode)
+{
+ 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 name_len;
+ u32 data_len;
+ u8 filetype;
+ int slot;
+ int ret;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
+ if (ret < 0) {
+ ret = DIR_ITEM_MISSING;
+ goto out;
+ }
+
+ /* Process root dir and goto out*/
+ if (index == 0) {
+ if (ret == 0) {
+ ret = ROOT_DIR_ERROR;
+ error(
+ "root %llu INODE %s[%llu %llu] ROOT_DIR shouldn't have %s",
+ root->objectid,
+ ref_key->type == BTRFS_INODE_REF_KEY ?
+ "REF" : "EXTREF",
+ ref_key->objectid, ref_key->offset,
+ key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX");
+ } else {
+ ret = 0;
+ }
+
+ goto out;
+ }
+
+ /* Process normal file/dir */
+ if (ret > 0) {
+ ret = DIR_ITEM_MISSING;
+ error(
+ "root %llu INODE %s[%llu %llu] doesn't have related %s[%llu %llu] namelen %u filename %s filetype %d",
+ root->objectid,
+ ref_key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF",
+ ref_key->objectid, ref_key->offset,
+ key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX",
+ key->objectid, key->offset, namelen, name,
+ imode_to_type(mode));
+ 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 = DIR_ITEM_MISMATCH;
+ name_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 != ref_key->objectid ||
+ location.type != BTRFS_INODE_ITEM_KEY ||
+ location.offset != 0)
+ goto next;
+
+ filetype = btrfs_dir_type(node, di);
+ if (imode_to_type(mode) != filetype)
+ goto next;
+
+ if (name_len <= BTRFS_NAME_LEN) {
+ len = name_len;
+ } else {
+ 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, name_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) + name_len + data_len;
+ di = (struct btrfs_dir_item *)((char *)di + len);
+ cur += len;
+ }
+ if (ret == DIR_ITEM_MISMATCH)
+ error(
+ "root %llu INODE %s[%llu %llu] and %s[%llu %llu] mismatch namelen %u filename %s filetype %d",
+ root->objectid,
+ ref_key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF",
+ ref_key->objectid, ref_key->offset,
+ key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX",
+ key->objectid, key->offset, namelen, name,
+ imode_to_type(mode));
+out:
+ btrfs_release_path(&path);
+ 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
+ * @refs: the count of INODE_REF
+ * @mode: the st_mode of INODE_ITEM
+ *
+ * Return 0 if no error occurred.
+ */
+static int check_inode_ref(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_inode_ref *ref;
+ char namebuf[BTRFS_NAME_LEN] = {0};
+ u32 total;
+ u32 cur = 0;
+ u32 len;
+ u32 name_len;
+ u64 index;
+ int ret, err = 0;
+
+ ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref);
+ total = btrfs_item_size_nr(node, slot);
+
+next:
+ /* Update inode ref count */
+ (*refs)++;
+
+ 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);
+
+ /* Check root dir ref name */
+ if (index == 0 && strncmp(namebuf, "..", name_len)) {
+ error("root %llu INODE_REF[%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 = ref_key->offset;
+ key.type = BTRFS_DIR_INDEX_KEY;
+ key.offset = index;
+ ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode);
+ err |= ret;
+
+ /* Find related dir_item */
+ key.objectid = ref_key->offset;
+ key.type = BTRFS_DIR_ITEM_KEY;
+ key.offset = btrfs_name_hash(namebuf, len);
+ ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode);
+ err |= ret;
+
+ len = sizeof(*ref) + name_len;
+ ref = (struct btrfs_inode_ref *)((char *)ref + len);
+ cur += len;
+ if (cur < total)
+ goto next;
+
+ 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_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;
+
+ 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, ref_key, &key, index, 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, ref_key, &key, index, 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.
+ *
+ * @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: the index in the INODE_REF/INODE_EXTREF, for DIR_ITEM set index
+ * to (u64)-1
+ * @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,
+ 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;
+
+ 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 != (u64)-1 && index != ref_index)
+ goto next_ref;
+
+ 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)(ref + 1),
+ len);
+
+ if (len != namelen || strncmp(ref_namebuf, name, len))
+ goto next_ref;
+
+ 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 != (u64)-1 && index != 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;
+
+ 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;
+}
+
+/*
+ * 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
+ * @size: the st_size of the INODE_ITEM
+ * @ext_ref: the EXTENDED_IREF feature
+ *
+ * Return 0 if no error occurred.
+ */
+static int check_dir_item(struct btrfs_root *root, struct btrfs_key *key,
+ struct extent_buffer *node, int slot, u64 *size,
+ unsigned int ext_ref)
+{
+ struct btrfs_dir_item *di;
+ struct btrfs_inode_item *ii;
+ struct btrfs_path path;
+ struct btrfs_key location;
+ char namebuf[BTRFS_NAME_LEN] = {0};
+ u32 total;
+ u32 cur = 0;
+ u32 len;
+ u32 name_len;
+ u32 data_len;
+ u8 filetype;
+ u32 mode;
+ u64 index;
+ int ret;
+ int err = 0;
+
+ /*
+ * For DIR_ITEM set index to (u64)-1, so that find_inode_ref
+ * ignore index check.
+ */
+ index = (key->type == BTRFS_DIR_INDEX_KEY) ? key->offset : (u64)-1;
+
+ di = btrfs_item_ptr(node, slot, struct btrfs_dir_item);
+ total = btrfs_item_size_nr(node, slot);
+
+ while (cur < total) {
+ data_len = btrfs_dir_data_len(node, di);
+ if (data_len)
+ error("root %llu %s[%llu %llu] data_len shouldn't be %u",
+ root->objectid, key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX",
+ key->objectid, 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,
+ key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX",
+ key->objectid, key->offset);
+ }
+ (*size) += name_len;
+
+ read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len);
+ filetype = btrfs_dir_type(node, di);
+
+ btrfs_init_path(&path);
+ btrfs_dir_item_key_to_cpu(node, di, &location);
+
+ /* Ignore related ROOT_ITEM check */
+ if (location.type == BTRFS_ROOT_ITEM_KEY)
+ goto next;
+
+ /* Check relative INODE_ITEM(existence/filetype) */
+ ret = btrfs_search_slot(NULL, root, &location, &path, 0, 0);
+ if (ret) {
+ err |= INODE_ITEM_MISSING;
+ error("root %llu %s[%llu %llu] couldn't find relative INODE_ITEM[%llu] namelen %u filename %s filetype %x",
+ root->objectid, key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX", key->objectid,
+ key->offset, location.objectid, name_len,
+ namebuf, filetype);
+ 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) {
+ err |= INODE_ITEM_MISMATCH;
+ error("root %llu %s[%llu %llu] relative INODE_ITEM filetype mismatch namelen %u filename %s filetype %d",
+ root->objectid, key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX", key->objectid,
+ key->offset, name_len, namebuf, filetype);
+ }
+
+ /* Check relative INODE_REF/INODE_EXTREF */
+ location.type = BTRFS_INODE_REF_KEY;
+ location.offset = key->objectid;
+ ret = find_inode_ref(root, &location, namebuf, len,
+ index, ext_ref);
+ err |= ret;
+ if (ret & INODE_REF_MISSING)
+ error("root %llu %s[%llu %llu] relative INODE_REF missing namelen %u filename %s filetype %d",
+ root->objectid, key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX", key->objectid,
+ key->offset, name_len, namebuf, filetype);
+
+next:
+ btrfs_release_path(&path);
+ len = sizeof(*di) + name_len + data_len;
+ di = (struct btrfs_dir_item *)((char *)di + len);
+ cur += len;
+
+ if (key->type == BTRFS_DIR_INDEX_KEY && cur < total) {
+ error("root %llu DIR_INDEX[%llu %llu] should contain only one entry",
+ root->objectid, key->objectid, key->offset);
+ break;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * 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 found;
+ unsigned int extent_type;
+ unsigned int is_hole;
+ int ret;
+ int err = 0;
+
+ fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
+
+ extent_type = btrfs_file_extent_type(node, fi);
+ /* Skip if file extent is inline */
+ 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);
+ if (extent_num_bytes == 0 ||
+ extent_num_bytes != item_inline_len)
+ err |= FILE_EXTENT_ERROR;
+ *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);
+ is_hole = (disk_bytenr == 0) && (disk_num_bytes == 0);
+
+ /* Check EXTENT_DATA datasum */
+ ret = count_csum_range(root, disk_bytenr, disk_num_bytes, &found);
+ if (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 || found == 0 || found < disk_num_bytes)) {
+ err |= CSUM_ITEM_MISSING;
+ error("root %llu EXTENT_DATA[%llu %llu] datasum missing",
+ root->objectid, fkey->objectid, fkey->offset);
+ } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && found > 0) {
+ err |= ODD_CSUM_ITEM;
+ error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have datasum",
+ root->objectid, fkey->objectid, fkey->offset);
+ }
+
+ /* Check EXTENT_DATA hole */
+ if (no_holes && is_hole) {
+ err |= FILE_EXTENT_ERROR;
+ error("root %llu EXTENT_DATA[%llu %llu] shouldn't be hole",
+ root->objectid, fkey->objectid, fkey->offset);
+ } else if (!no_holes && *end != fkey->offset) {
+ err |= FILE_EXTENT_ERROR;
+ error("root %llu EXTENT_DATA[%llu %llu] interrupt",
+ root->objectid, fkey->objectid, fkey->offset);
+ }
+
+ *end += extent_num_bytes;
+ if (!is_hole)
+ *size += extent_num_bytes;
+
+ return err;
+}
+
+/*
+ * 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;
+ 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;
+
+ 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) {
+ 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, node, slot, &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, node, slot, &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:
+ /* verify INODE_ITEM nlink/isize/nbytes */
+ if (dir) {
+ 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->nodesize)) {
+ warning("root %llu DIR INODE[%llu] nbytes should be aligned to %u",
+ root->objectid, inode_id, root->nodesize);
+ }
+
+ if (isize != size) {
+ 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) {
+ 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) {
+ err |= ORPHAN_ITEM;
+ }
+
+ if (!nbytes && !no_holes && extent_end < isize) {
+ 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) {
+ err |= NBYTES_ERROR;
+ error("root %llu INODE[%llu] nbytes(%llu) not equal to extent_size(%llu)",
+ root->objectid, inode_id, nbytes, extent_size);
+ }
+ }
+
+ return err;
+}
+
+static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ 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) >=
+ key.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;
+ }
+
+ err |= check_inode_item(root, &path, ext_ref);
+ err &= ~LAST_ITEM;
+ if (err && !ret)
+ ret = -EIO;
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * Iterate all item on 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)
+{
+ struct btrfs_path path;
+ struct node_refs nrefs;
+ struct btrfs_root_item *root_item = &root->root_item;
+ int ret, wret;
+ int level;
+
+ /*
+ * 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 just skip it forever.
+ */
+ ret = check_fs_first_inode(root, ext_ref);
+ if (ret < 0)
+ return ret;
+
+ memset(&nrefs, 0, sizeof(nrefs));
+ 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) {
+ wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
+ if (wret < 0)
+ ret = wret;
+ if (wret != 0)
+ break;
+
+ wret = walk_up_tree_v2(root, &path, &level);
+ if (wret < 0)
+ ret = wret;
+ if (wret != 0)
+ break;
+ }
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * 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 all_backpointers_checked(struct extent_record *rec, int print_errs)
{
struct list_head *cur = rec->backrefs.next;
@@ -4310,7 +5692,7 @@ static int try_to_fix_bad_block(struct btrfs_root *root,
struct ulist *roots;
struct ulist_node *node;
struct btrfs_root *search_root;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct ulist_iterator iter;
struct btrfs_key root_key, key;
int ret;
@@ -4319,17 +5701,11 @@ static int try_to_fix_bad_block(struct btrfs_root *root,
status != BTRFS_TREE_BLOCK_INVALID_OFFSETS)
return -EIO;
- path = btrfs_alloc_path();
- if (!path)
- return -EIO;
-
- ret = btrfs_find_all_roots(NULL, root->fs_info, buf->start,
- 0, &roots);
- if (ret) {
- btrfs_free_path(path);
+ ret = btrfs_find_all_roots(NULL, root->fs_info, buf->start, 0, &roots);
+ if (ret)
return -EIO;
- }
+ btrfs_init_path(&path);
ULIST_ITER_INIT(&iter);
while ((node = ulist_next(roots, &iter))) {
root_key.objectid = node->val;
@@ -4349,31 +5725,31 @@ static int try_to_fix_bad_block(struct btrfs_root *root,
break;
}
- path->lowest_level = btrfs_header_level(buf);
- path->skip_check_block = 1;
- if (path->lowest_level)
+ path.lowest_level = btrfs_header_level(buf);
+ path.skip_check_block = 1;
+ if (path.lowest_level)
btrfs_node_key_to_cpu(buf, &key, 0);
else
btrfs_item_key_to_cpu(buf, &key, 0);
- ret = btrfs_search_slot(trans, search_root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, search_root, &key, &path, 0, 1);
if (ret) {
ret = -EIO;
btrfs_commit_transaction(trans, search_root);
break;
}
if (status == BTRFS_TREE_BLOCK_BAD_KEY_ORDER)
- ret = fix_key_order(trans, search_root, path);
+ ret = fix_key_order(trans, search_root, &path);
else if (status == BTRFS_TREE_BLOCK_INVALID_OFFSETS)
- ret = fix_item_offset(trans, search_root, path);
+ ret = fix_item_offset(trans, search_root, &path);
if (ret) {
btrfs_commit_transaction(trans, search_root);
break;
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
btrfs_commit_transaction(trans, search_root);
}
ulist_free(roots);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -4643,12 +6019,15 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
rec->cache.start = tmpl->start;
rec->cache.size = tmpl->nr;
ret = insert_cache_extent(extent_cache, &rec->cache);
- BUG_ON(ret);
+ if (ret) {
+ free(rec);
+ return ret;
+ }
bytes_used += rec->nr;
if (tmpl->metadata)
- rec->crossing_stripes = check_crossing_stripes(rec->start,
- global_info->tree_root->nodesize);
+ rec->crossing_stripes = check_crossing_stripes(global_info,
+ rec->start, global_info->tree_root->nodesize);
check_extent_type(rec);
return ret;
}
@@ -4749,7 +6128,8 @@ static int add_extent_rec(struct cache_tree *extent_cache,
*/
if (tmpl->metadata)
rec->crossing_stripes = check_crossing_stripes(
- rec->start, global_info->tree_root->nodesize);
+ global_info, rec->start,
+ global_info->tree_root->nodesize);
check_extent_type(rec);
maybe_free_extent_rec(extent_cache, rec);
return ret;
@@ -4766,6 +6146,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
struct extent_record *rec;
struct tree_backref *back;
struct cache_extent *cache;
+ int ret;
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
@@ -4776,7 +6157,9 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
tmpl.nr = 1;
tmpl.metadata = 1;
- add_extent_rec_nolookup(extent_cache, &tmpl);
+ ret = add_extent_rec_nolookup(extent_cache, &tmpl);
+ if (ret)
+ return ret;
/* really a bug in cache_extent implement now */
cache = lookup_cache_extent(extent_cache, bytenr, 1);
@@ -4830,6 +6213,7 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
struct extent_record *rec;
struct data_backref *back;
struct cache_extent *cache;
+ int ret;
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
@@ -4840,7 +6224,9 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
tmpl.nr = 1;
tmpl.max_size = max_size;
- add_extent_rec_nolookup(extent_cache, &tmpl);
+ ret = add_extent_rec_nolookup(extent_cache, &tmpl);
+ if (ret)
+ return ret;
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
@@ -5474,7 +6860,7 @@ static int check_cache_range(struct btrfs_root *root,
continue;
if (logical[nr] == offset) {
if (stripe_len >= bytes) {
- kfree(logical);
+ free(logical);
return 0;
}
bytes -= stripe_len;
@@ -5482,7 +6868,7 @@ static int check_cache_range(struct btrfs_root *root,
} else if (logical[nr] < offset) {
if (logical[nr] + stripe_len >=
offset + bytes) {
- kfree(logical);
+ free(logical);
return 0;
}
bytes = (offset + bytes) -
@@ -5505,7 +6891,7 @@ static int check_cache_range(struct btrfs_root *root,
offset,
logical[nr] - offset);
if (ret) {
- kfree(logical);
+ free(logical);
return ret;
}
@@ -5516,7 +6902,7 @@ static int check_cache_range(struct btrfs_root *root,
}
}
- kfree(logical);
+ free(logical);
}
entry = btrfs_find_free_space(cache->free_space_ctl, offset, bytes);
@@ -5546,31 +6932,27 @@ static int check_cache_range(struct btrfs_root *root,
static int verify_space_cache(struct btrfs_root *root,
struct btrfs_block_group_cache *cache)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_key key;
u64 last;
int ret = 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
root = root->fs_info->extent_root;
last = max_t(u64, cache->key.objectid, BTRFS_SUPER_INFO_OFFSET);
+ btrfs_init_path(&path);
key.objectid = last;
key.offset = 0;
key.type = BTRFS_EXTENT_ITEM_KEY;
-
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0)
goto out;
ret = 0;
while (1) {
- if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
- ret = btrfs_next_leaf(root, path);
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(root, &path);
if (ret < 0)
goto out;
if (ret > 0) {
@@ -5578,13 +6960,13 @@ static int verify_space_cache(struct btrfs_root *root,
break;
}
}
- leaf = path->nodes[0];
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ leaf = path.nodes[0];
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.objectid >= cache->key.offset + cache->key.objectid)
break;
if (key.type != BTRFS_EXTENT_ITEM_KEY &&
key.type != BTRFS_METADATA_ITEM_KEY) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
@@ -5593,7 +6975,7 @@ static int verify_space_cache(struct btrfs_root *root,
last = key.objectid + key.offset;
else
last = key.objectid + root->nodesize;
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
@@ -5605,7 +6987,7 @@ static int verify_space_cache(struct btrfs_root *root,
last = key.objectid + key.offset;
else
last = key.objectid + root->nodesize;
- path->slots[0]++;
+ path.slots[0]++;
}
if (last < cache->key.objectid + cache->key.offset)
@@ -5614,7 +6996,7 @@ static int verify_space_cache(struct btrfs_root *root,
cache->key.offset - last);
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
if (!ret &&
!RB_EMPTY_ROOT(&cache->free_space_ctl->free_space_offset)) {
@@ -5662,8 +7044,7 @@ static int check_space_cache(struct btrfs_root *root)
btrfs_remove_free_space_cache(cache);
}
- if (btrfs_fs_compat_ro(root->fs_info,
- BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) {
+ if (btrfs_fs_compat_ro(root->fs_info, FREE_SPACE_TREE)) {
ret = exclude_super_stripes(root, cache);
if (ret) {
fprintf(stderr, "could not exclude super stripes: %s\n",
@@ -5740,7 +7121,7 @@ again:
csum = btrfs_csum_data(NULL, (char *)data + tmp,
csum, root->sectorsize);
- btrfs_csum_final(csum, (char *)&csum);
+ btrfs_csum_final(csum, (u8 *)&csum);
csum_offset = leaf_offset +
tmp / root->sectorsize * csum_size;
@@ -5771,33 +7152,28 @@ out:
static int check_extent_exists(struct btrfs_root *root, u64 bytenr,
u64 num_bytes)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_key key;
int ret;
- path = btrfs_alloc_path();
- if (!path) {
- fprintf(stderr, "Error allocating path\n");
- return -ENOMEM;
- }
-
+ btrfs_init_path(&path);
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = (u64)-1;
again:
- ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, path,
+ ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, &path,
0, 0);
if (ret < 0) {
fprintf(stderr, "Error looking up extent record %d\n", ret);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
} else if (ret) {
- if (path->slots[0] > 0) {
- path->slots[0]--;
+ if (path.slots[0] > 0) {
+ path.slots[0]--;
} else {
- ret = btrfs_prev_leaf(root, path);
+ ret = btrfs_prev_leaf(root, &path);
if (ret < 0) {
goto out;
} else if (ret > 0) {
@@ -5807,7 +7183,7 @@ again:
}
}
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
/*
* Block group items come before extent items if they have the same
@@ -5818,10 +7194,10 @@ again:
* EXTENT_ITEM_KEY please?
*/
while (key.type > BTRFS_EXTENT_ITEM_KEY) {
- if (path->slots[0] > 0) {
- path->slots[0]--;
+ if (path.slots[0] > 0) {
+ path.slots[0]--;
} else {
- ret = btrfs_prev_leaf(root, path);
+ ret = btrfs_prev_leaf(root, &path);
if (ret < 0) {
goto out;
} else if (ret > 0) {
@@ -5829,29 +7205,29 @@ again:
goto out;
}
}
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
}
while (num_bytes) {
- if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
- ret = btrfs_next_leaf(root, path);
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(root, &path);
if (ret < 0) {
fprintf(stderr, "Error going to next leaf "
"%d\n", ret);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
} else if (ret) {
break;
}
}
- leaf = path->nodes[0];
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ leaf = path.nodes[0];
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.type != BTRFS_EXTENT_ITEM_KEY) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
if (key.objectid + key.offset < bytenr) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
if (key.objectid > bytenr + num_bytes)
@@ -5884,7 +7260,7 @@ again:
* in real life, but no harm in coding it up
* anyway just in case.
*/
- btrfs_release_path(path);
+ btrfs_release_path(&path);
ret = check_extent_exists(root, new_start,
new_bytes);
if (ret) {
@@ -5897,7 +7273,7 @@ again:
}
num_bytes = key.objectid - bytenr;
}
- path->slots[0]++;
+ path.slots[0]++;
}
ret = 0;
@@ -5908,13 +7284,13 @@ out:
ret = 1;
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
static int check_csums(struct btrfs_root *root)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_key key;
u64 offset = 0, num_bytes = 0;
@@ -5930,28 +7306,24 @@ static int check_csums(struct btrfs_root *root)
return -ENOENT;
}
+ btrfs_init_path(&path);
key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
key.type = BTRFS_EXTENT_CSUM_KEY;
key.offset = 0;
-
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0) {
fprintf(stderr, "Error searching csum tree %d\n", ret);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
- if (ret > 0 && path->slots[0])
- path->slots[0]--;
+ if (ret > 0 && path.slots[0])
+ path.slots[0]--;
ret = 0;
while (1) {
- if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
- ret = btrfs_next_leaf(root, path);
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(root, &path);
if (ret < 0) {
fprintf(stderr, "Error going to next leaf "
"%d\n", ret);
@@ -5960,19 +7332,19 @@ static int check_csums(struct btrfs_root *root)
if (ret)
break;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.type != BTRFS_EXTENT_CSUM_KEY) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
- data_len = (btrfs_item_size_nr(leaf, path->slots[0]) /
+ data_len = (btrfs_item_size_nr(leaf, path.slots[0]) /
csum_size) * root->sectorsize;
if (!check_data_csum)
goto skip_csum_check;
- leaf_offset = btrfs_item_ptr_offset(leaf, path->slots[0]);
+ leaf_offset = btrfs_item_ptr_offset(leaf, path.slots[0]);
ret = check_extent_csums(root, key.offset, data_len,
leaf_offset, leaf);
if (ret)
@@ -5992,10 +7364,10 @@ skip_csum_check:
num_bytes = 0;
}
num_bytes += data_len;
- path->slots[0]++;
+ path.slots[0]++;
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return errors;
}
@@ -6047,7 +7419,9 @@ static int calc_extent_flag(struct btrfs_root *root,
cache = lookup_cache_extent(extent_cache, buf->start, 1);
/* we have added this extent before */
- BUG_ON(!cache);
+ if (!cache)
+ return -ENOENT;
+
rec = container_of(cache, struct extent_record, cache);
/*
@@ -6682,12 +8056,11 @@ static int record_extent(struct btrfs_trans_handle *trans,
struct extent_backref *back,
int allocated, u64 flags)
{
- int ret;
+ int ret = 0;
struct btrfs_root *extent_root = info->extent_root;
struct extent_buffer *leaf;
struct btrfs_key ins_key;
struct btrfs_extent_item *ei;
- struct tree_backref *tback;
struct data_backref *dback;
struct btrfs_tree_block_info *bi;
@@ -6723,7 +8096,6 @@ static int record_extent(struct btrfs_trans_handle *trans,
} else {
struct btrfs_disk_key copy_key;;
- tback = to_tree_backref(back);
bi = (struct btrfs_tree_block_info *)(ei + 1);
memset_extent_buffer(leaf, 0, (unsigned long)bi,
sizeof(*bi));
@@ -6789,6 +8161,7 @@ static int record_extent(struct btrfs_trans_handle *trans,
dback->found_ref);
} else {
u64 parent;
+ struct tree_backref *tback;
tback = to_tree_backref(back);
if (back->full_backref)
@@ -6826,11 +8199,6 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries)
struct extent_entry *entry, *best = NULL, *prev = NULL;
list_for_each_entry(entry, entries, list) {
- if (!prev) {
- prev = entry;
- continue;
- }
-
/*
* If there are as many broken entries as entries then we know
* not to trust this particular entry.
@@ -6839,6 +8207,16 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries)
continue;
/*
+ * Special case, when there are only two entries and 'best' is
+ * the first one
+ */
+ if (!prev) {
+ best = entry;
+ prev = entry;
+ continue;
+ }
+
+ /*
* If our current entry == best then we can't be sure our best
* is really the best, so we need to keep searching.
*/
@@ -7284,17 +8662,13 @@ static int delete_duplicate_records(struct btrfs_root *root,
{
struct btrfs_trans_handle *trans;
LIST_HEAD(delete_list);
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_record *tmp, *good, *n;
int nr_del = 0;
int ret = 0, err;
struct btrfs_key key;
- path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
+ btrfs_init_path(&path);
good = rec;
/* Find the record that covers all of the duplicates. */
@@ -7346,16 +8720,16 @@ static int delete_duplicate_records(struct btrfs_root *root,
abort();
}
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
if (ret) {
if (ret > 0)
ret = -EINVAL;
break;
}
- ret = btrfs_del_item(trans, root, path);
+ ret = btrfs_del_item(trans, root, &path);
if (ret)
break;
- btrfs_release_path(path);
+ btrfs_release_path(&path);
nr_del++;
}
err = btrfs_commit_transaction(trans, root);
@@ -7376,7 +8750,7 @@ out:
free(tmp);
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
if (!ret && !nr_del)
rec->num_duplicates = 0;
@@ -7489,15 +8863,13 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info,
struct extent_backref *back;
struct data_backref *dback;
struct orphan_data_extent *orphan;
- struct btrfs_path *path;
+ struct btrfs_path path;
int recorded_data_ref = 0;
int ret = 0;
if (rec->metadata)
return 1;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ btrfs_init_path(&path);
list_for_each_entry(back, &rec->backrefs, list) {
if (back->full_backref || !back->is_data ||
!back->found_extent_tree)
@@ -7519,7 +8891,8 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info,
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = dback->offset;
- ret = btrfs_search_slot(NULL, dest_root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, dest_root, &key, &path, 0, 0);
+ btrfs_release_path(&path);
/*
* For ret < 0, it's OK since the fs-tree may be corrupted,
* we need to record it for inode/file extent rebuild.
@@ -7546,7 +8919,7 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info,
recorded_data_ref = 1;
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
if (!ret)
return !recorded_data_ref;
else
@@ -7564,7 +8937,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
{
struct btrfs_trans_handle *trans = NULL;
int ret;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct list_head *cur = rec->backrefs.next;
struct cache_extent *cache;
struct extent_backref *back;
@@ -7574,10 +8947,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
if (rec->flag_block_full_backref)
flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
+ btrfs_init_path(&path);
if (rec->refs != rec->extent_item_refs && !rec->metadata) {
/*
* Sometimes the backrefs themselves are so broken they don't
@@ -7586,13 +8956,13 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
* them into the list if we find the backref so that
* verify_backrefs can figure out what to do.
*/
- ret = find_possible_backrefs(info, path, extent_cache, rec);
+ ret = find_possible_backrefs(info, &path, extent_cache, rec);
if (ret < 0)
goto out;
}
/* step one, make sure all of the backrefs agree */
- ret = verify_backrefs(info, path, rec);
+ ret = verify_backrefs(info, &path, rec);
if (ret < 0)
goto out;
@@ -7603,7 +8973,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
}
/* step two, delete all the existing records */
- ret = delete_extent_records(trans, info->extent_root, path,
+ ret = delete_extent_records(trans, info->extent_root, &path,
rec->start, rec->max_size);
if (ret < 0)
@@ -7630,7 +9000,7 @@ 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)
@@ -7643,7 +9013,11 @@ out:
ret = err;
}
- btrfs_free_path(path);
+ if (!ret)
+ fprintf(stderr, "Repaired extent references for %llu\n",
+ (unsigned long long)rec->start);
+
+ btrfs_release_path(&path);
return ret;
}
@@ -7652,7 +9026,7 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info,
{
struct btrfs_trans_handle *trans;
struct btrfs_root *root = fs_info->extent_root;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_extent_item *ei;
struct btrfs_key key;
u64 flags;
@@ -7667,32 +9041,27 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info,
key.offset = rec->max_size;
}
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
trans = btrfs_start_transaction(root, 0);
- if (IS_ERR(trans)) {
- btrfs_free_path(path);
+ if (IS_ERR(trans))
return PTR_ERR(trans);
- }
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
if (ret < 0) {
- btrfs_free_path(path);
+ btrfs_release_path(&path);
btrfs_commit_transaction(trans, root);
return ret;
} else if (ret) {
fprintf(stderr, "Didn't find extent for %llu\n",
(unsigned long long)rec->start);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
btrfs_commit_transaction(trans, root);
return -ENOENT;
}
- ei = btrfs_item_ptr(path->nodes[0], 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);
+ flags = btrfs_extent_flags(path.nodes[0], ei);
if (rec->flag_block_full_backref) {
fprintf(stderr, "setting full backref on %llu\n",
(unsigned long long)key.objectid);
@@ -7702,10 +9071,15 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info,
(unsigned long long)key.objectid);
flags &= ~BTRFS_BLOCK_FLAG_FULL_BACKREF;
}
- btrfs_set_extent_flags(path->nodes[0], ei, flags);
- btrfs_mark_buffer_dirty(path->nodes[0]);
- btrfs_free_path(path);
- return btrfs_commit_transaction(trans, root);
+ btrfs_set_extent_flags(path.nodes[0], ei, flags);
+ btrfs_mark_buffer_dirty(path.nodes[0]);
+ btrfs_release_path(&path);
+ ret = btrfs_commit_transaction(trans, root);
+ if (!ret)
+ fprintf(stderr, "Repaired extent flags for %llu\n",
+ (unsigned long long)rec->start);
+
+ return ret;
}
/* right now we only prune from the extent allocation tree */
@@ -7832,11 +9206,8 @@ static int check_extent_refs(struct btrfs_root *root,
{
struct extent_record *rec;
struct cache_extent *cache;
- int err = 0;
int ret = 0;
- int fixed = 0;
int had_dups = 0;
- int recorded = 0;
if (repair) {
/*
@@ -7905,9 +9276,8 @@ static int check_extent_refs(struct btrfs_root *root,
while(1) {
int cur_err = 0;
+ int fix = 0;
- fixed = 0;
- recorded = 0;
cache = search_cache_extent(extent_cache, 0);
if (!cache)
break;
@@ -7915,7 +9285,6 @@ static int check_extent_refs(struct btrfs_root *root,
if (rec->num_duplicates) {
fprintf(stderr, "extent item %llu has multiple extent "
"items\n", (unsigned long long)rec->start);
- err = 1;
cur_err = 1;
}
@@ -7929,54 +9298,31 @@ static int check_extent_refs(struct btrfs_root *root,
ret = record_orphan_data_extents(root->fs_info, rec);
if (ret < 0)
goto repair_abort;
- if (ret == 0) {
- recorded = 1;
- } else {
- /*
- * we can't use the extent to repair file
- * extent, let the fallback method handle it.
- */
- if (!fixed && repair) {
- ret = fixup_extent_refs(
- root->fs_info,
- extent_cache, rec);
- if (ret)
- goto repair_abort;
- fixed = 1;
- }
- }
- err = 1;
+ fix = ret;
cur_err = 1;
}
if (all_backpointers_checked(rec, 1)) {
fprintf(stderr, "backpointer mismatch on [%llu %llu]\n",
(unsigned long long)rec->start,
(unsigned long long)rec->nr);
-
- if (!fixed && !recorded && repair) {
- ret = fixup_extent_refs(root->fs_info,
- extent_cache, rec);
- if (ret)
- goto repair_abort;
- fixed = 1;
- }
+ fix = 1;
cur_err = 1;
- err = 1;
}
if (!rec->owner_ref_checked) {
fprintf(stderr, "owner ref check failed [%llu %llu]\n",
(unsigned long long)rec->start,
(unsigned long long)rec->nr);
- if (!fixed && !recorded && repair) {
- ret = fixup_extent_refs(root->fs_info,
- extent_cache, rec);
- if (ret)
- goto repair_abort;
- fixed = 1;
- }
- err = 1;
+ fix = 1;
cur_err = 1;
}
+
+ if (repair && fix) {
+ ret = fixup_extent_refs(root->fs_info, extent_cache, rec);
+ if (ret)
+ goto repair_abort;
+ }
+
+
if (rec->bad_full_backref) {
fprintf(stderr, "bad full backref, on [%llu]\n",
(unsigned long long)rec->start);
@@ -7984,9 +9330,8 @@ static int check_extent_refs(struct btrfs_root *root,
ret = fixup_extent_flags(root->fs_info, rec);
if (ret)
goto repair_abort;
- fixed = 1;
+ fix = 1;
}
- err = 1;
cur_err = 1;
}
/*
@@ -7998,7 +9343,6 @@ static int check_extent_refs(struct btrfs_root *root,
fprintf(stderr,
"bad metadata [%llu, %llu) crossing stripe boundary\n",
rec->start, rec->start + rec->max_size);
- err = 1;
cur_err = 1;
}
@@ -8006,13 +9350,12 @@ static int check_extent_refs(struct btrfs_root *root,
fprintf(stderr,
"bad extent [%llu, %llu), type mismatch with chunk\n",
rec->start, rec->start + rec->max_size);
- err = 1;
cur_err = 1;
}
remove_cache_extent(extent_cache, cache);
free_all_extent_backrefs(rec);
- if (!init_extent_tree && repair && (!cur_err || fixed))
+ if (!init_extent_tree && repair && (!cur_err || fix))
clear_extent_dirty(root->fs_info->excluded_extents,
rec->start,
rec->start + rec->max_size - 1,
@@ -8039,11 +9382,9 @@ repair_abort:
if (ret)
goto repair_abort;
}
- if (err)
- fprintf(stderr, "repaired damaged extent references\n");
return ret;
}
- return err;
+ return 0;
}
u64 calc_stripe_length(u64 type, u64 length, int num_stripes)
@@ -8486,7 +9827,7 @@ again:
btrfs_init_path(&path);
key.offset = 0;
key.objectid = 0;
- btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+ key.type = BTRFS_ROOT_ITEM_KEY;
ret = btrfs_search_slot(NULL, root->fs_info->tree_root,
&key, &path, 0, 0);
if (ret < 0)
@@ -8502,7 +9843,7 @@ again:
slot = path.slots[0];
}
btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]);
- if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
+ if (found_key.type == BTRFS_ROOT_ITEM_KEY) {
unsigned long offset;
u64 last_snapshot;
@@ -8649,14 +9990,18 @@ static int check_tree_block_ref(struct btrfs_root *root,
u32 nodesize = root->nodesize;
u32 item_size;
u64 offset;
+ int tree_reloc_root = 0;
int found_ref = 0;
int err = 0;
int ret;
+ if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID &&
+ btrfs_header_bytenr(root->node) == bytenr)
+ tree_reloc_root = 1;
+
btrfs_init_path(&path);
key.objectid = bytenr;
- if (btrfs_fs_incompat(root->fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA))
+ if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA))
key.type = BTRFS_METADATA_ITEM_KEY;
else
key.type = BTRFS_EXTENT_ITEM_KEY;
@@ -8741,9 +10086,16 @@ static int check_tree_block_ref(struct btrfs_root *root,
(offset == root->objectid || offset == owner)) {
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.
+ */
+ if (tree_reloc_root)
+ found_ref = 1;
+ else
/* Check if the backref points to valid referencer */
- found_ref = !check_tree_block_ref(root, NULL, offset,
- level + 1, owner);
+ found_ref = !check_tree_block_ref(root, NULL,
+ offset, level + 1, owner);
}
if (found_ref)
@@ -8794,12 +10146,10 @@ static int check_extent_data_item(struct btrfs_root *root,
struct btrfs_extent_inline_ref *iref;
struct btrfs_extent_data_ref *dref;
u64 owner;
- u64 file_extent_gen;
u64 disk_bytenr;
u64 disk_num_bytes;
u64 extent_num_bytes;
u64 extent_flags;
- u64 extent_gen;
u32 item_size;
unsigned long end;
unsigned long ptr;
@@ -8811,7 +10161,6 @@ static int check_extent_data_item(struct btrfs_root *root,
btrfs_item_key_to_cpu(eb, &fi_key, slot);
fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
- file_extent_gen = btrfs_file_extent_generation(eb, fi);
/* Nothing to check for hole and inline data extents */
if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE ||
@@ -8860,7 +10209,6 @@ static int check_extent_data_item(struct btrfs_root *root,
ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
extent_flags = btrfs_extent_flags(leaf, ei);
- extent_gen = btrfs_extent_generation(leaf, ei);
if (!(extent_flags & BTRFS_EXTENT_FLAG_DATA)) {
error(
@@ -8870,14 +10218,6 @@ static int check_extent_data_item(struct btrfs_root *root,
err |= BACKREF_MISMATCH;
}
- if (file_extent_gen < extent_gen) {
- error(
-"extent[%llu %llu] backref generation mismatch, wanted: <=%llu, have: %llu",
- disk_bytenr, disk_num_bytes, file_extent_gen,
- extent_gen);
- 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);
@@ -9104,6 +10444,34 @@ out:
}
/*
+ * 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.
*/
@@ -9125,6 +10493,13 @@ static int check_shared_block_backref(struct btrfs_fs_info *fs_info,
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;
@@ -9184,7 +10559,7 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info,
btrfs_release_path(&path);
}
key.objectid = root_id;
- btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+ key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1;
btrfs_init_path(&path);
@@ -9336,7 +10711,8 @@ static int check_extent_item(struct btrfs_fs_info *fs_info,
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
metadata = 1;
- if (metadata && check_crossing_stripes(key.objectid, eb->len)) {
+ 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;
@@ -9784,7 +11160,7 @@ static int check_leaf_items(struct btrfs_root *root, struct extent_buffer *eb)
next:
btrfs_item_key_to_cpu(eb, &key, slot);
- type = btrfs_key_type(&key);
+ type = key.type;
switch (type) {
case BTRFS_EXTENT_DATA_KEY:
@@ -10061,7 +11437,11 @@ static int check_chunks_and_extents_v2(struct btrfs_root *root)
goto next;
key.offset = (u64)-1;
- cur_root = btrfs_read_fs_root(root->fs_info, &key);
+ 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;
@@ -10070,6 +11450,8 @@ static int check_chunks_and_extents_v2(struct btrfs_root *root)
ret = traverse_tree_block(cur_root, cur_root->node);
err |= ret;
+ if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+ btrfs_free_fs_root(cur_root);
next:
ret = btrfs_next_item(root1, &path);
if (ret)
@@ -10242,24 +11624,20 @@ static int pin_metadata_blocks(struct btrfs_fs_info *fs_info)
static int reset_block_groups(struct btrfs_fs_info *fs_info)
{
struct btrfs_block_group_cache *cache;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_chunk *chunk;
struct btrfs_key key;
int ret;
u64 start;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
+ btrfs_init_path(&path);
key.objectid = 0;
key.type = BTRFS_CHUNK_ITEM_KEY;
key.offset = 0;
-
- ret = btrfs_search_slot(NULL, fs_info->chunk_root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, fs_info->chunk_root, &key, &path, 0, 0);
if (ret < 0) {
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -10274,10 +11652,10 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info)
/* First we need to create the in-memory block groups */
while (1) {
- if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
- ret = btrfs_next_leaf(fs_info->chunk_root, path);
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(fs_info->chunk_root, &path);
if (ret < 0) {
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
if (ret) {
@@ -10285,15 +11663,14 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info)
break;
}
}
- leaf = path->nodes[0];
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ leaf = path.nodes[0];
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.type != BTRFS_CHUNK_ITEM_KEY) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
- chunk = btrfs_item_ptr(leaf, path->slots[0],
- struct btrfs_chunk);
+ 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,
@@ -10301,7 +11678,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info)
set_extent_dirty(&fs_info->free_space_cache, key.offset,
key.offset + btrfs_chunk_length(leaf, chunk),
GFP_NOFS);
- path->slots[0]++;
+ path.slots[0]++;
}
start = 0;
while (1) {
@@ -10312,7 +11689,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info)
start = cache->key.objectid + cache->key.offset;
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return 0;
}
@@ -10320,22 +11697,18 @@ static int reset_balance(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info)
{
struct btrfs_root *root = fs_info->tree_root;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_key key;
int del_slot, del_nr = 0;
int ret;
int found = 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
+ btrfs_init_path(&path);
key.objectid = BTRFS_BALANCE_OBJECTID;
key.type = BTRFS_BALANCE_ITEM_KEY;
key.offset = 0;
-
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
if (ret) {
if (ret > 0)
ret = 0;
@@ -10345,64 +11718,63 @@ static int reset_balance(struct btrfs_trans_handle *trans,
goto out;
}
- ret = btrfs_del_item(trans, root, path);
+ ret = btrfs_del_item(trans, root, &path);
if (ret)
goto out;
- btrfs_release_path(path);
+ btrfs_release_path(&path);
key.objectid = BTRFS_TREE_RELOC_OBJECTID;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = 0;
-
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
if (ret < 0)
goto out;
while (1) {
- if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
if (!found)
break;
if (del_nr) {
- ret = btrfs_del_items(trans, root, path,
+ ret = btrfs_del_items(trans, root, &path,
del_slot, del_nr);
del_nr = 0;
if (ret)
goto out;
}
key.offset++;
- btrfs_release_path(path);
+ btrfs_release_path(&path);
found = 0;
- ret = btrfs_search_slot(trans, root, &key, path,
+ ret = btrfs_search_slot(trans, root, &key, &path,
-1, 1);
if (ret < 0)
goto out;
continue;
}
found = 1;
- leaf = path->nodes[0];
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ leaf = path.nodes[0];
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.objectid > BTRFS_TREE_RELOC_OBJECTID)
break;
if (key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
if (!del_nr) {
- del_slot = path->slots[0];
+ del_slot = path.slots[0];
del_nr = 1;
} else {
del_nr++;
}
- path->slots[0]++;
+ path.slots[0]++;
}
if (del_nr) {
- ret = btrfs_del_items(trans, root, path, del_slot, del_nr);
+ ret = btrfs_del_items(trans, root, &path, del_slot, del_nr);
if (ret)
goto out;
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
reinit_data_reloc:
key.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID;
@@ -10420,7 +11792,7 @@ reinit_data_reloc:
goto out;
ret = btrfs_make_root_dir(trans, root, BTRFS_FIRST_FREE_OBJECTID);
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -10437,7 +11809,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans,
* the leaves of any fs roots and pin down the bytes for any file
* extents we find. Not hard but why do it if we don't have to?
*/
- if (btrfs_fs_incompat(fs_info, BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)) {
+ if (btrfs_fs_incompat(fs_info, MIXED_GROUPS)) {
fprintf(stderr, "We don't support re-initing the extent tree "
"for mixed block groups yet, please notify a btrfs "
"developer you want to do this so they can add this "
@@ -10510,7 +11882,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans,
static int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_trans_handle *trans;
struct btrfs_key key;
int ret;
@@ -10527,31 +11899,26 @@ static int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb
return PTR_ERR(root);
}
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- btrfs_free_path(path);
+ if (IS_ERR(trans))
return PTR_ERR(trans);
- }
- path->lowest_level = btrfs_header_level(eb);
- if (path->lowest_level)
+ btrfs_init_path(&path);
+ path.lowest_level = btrfs_header_level(eb);
+ if (path.lowest_level)
btrfs_node_key_to_cpu(eb, &key, 0);
else
btrfs_item_key_to_cpu(eb, &key, 0);
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
btrfs_commit_transaction(trans, root);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
static int delete_bad_item(struct btrfs_root *root, struct bad_item *bad)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_trans_handle *trans;
struct btrfs_key key;
int ret;
@@ -10569,26 +11936,21 @@ static int delete_bad_item(struct btrfs_root *root, struct bad_item *bad)
return PTR_ERR(root);
}
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- btrfs_free_path(path);
+ if (IS_ERR(trans))
return PTR_ERR(trans);
- }
- ret = btrfs_search_slot(trans, root, &bad->key, path, -1, 1);
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(trans, root, &bad->key, &path, -1, 1);
if (ret) {
if (ret > 0)
ret = 0;
goto out;
}
- ret = btrfs_del_item(trans, root, path);
+ ret = btrfs_del_item(trans, root, &path);
out:
btrfs_commit_transaction(trans, root);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -10635,7 +11997,7 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans,
struct btrfs_root *csum_root,
struct btrfs_root *cur_root)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_key key;
struct extent_buffer *node;
struct btrfs_file_extent_item *fi;
@@ -10645,30 +12007,25 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans,
int slot = 0;
int ret = 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
buf = malloc(cur_root->fs_info->csum_root->sectorsize);
- if (!buf) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!buf)
+ return -ENOMEM;
+ btrfs_init_path(&path);
key.objectid = 0;
key.offset = 0;
key.type = 0;
-
- ret = btrfs_search_slot(NULL, cur_root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, cur_root, &key, &path, 0, 0);
if (ret < 0)
goto out;
/* Iterate all regular file extents and fill its csum */
while (1) {
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
if (key.type != BTRFS_EXTENT_DATA_KEY)
goto next;
- node = path->nodes[0];
- slot = path->slots[0];
+ node = path.nodes[0];
+ slot = path.slots[0];
fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
if (btrfs_file_extent_type(node, fi) != BTRFS_FILE_EXTENT_REG)
goto next;
@@ -10685,7 +12042,7 @@ next:
* TODO: if next leaf is corrupted, jump to nearest next valid
* leaf.
*/
- ret = btrfs_next_item(cur_root, path);
+ ret = btrfs_next_item(cur_root, &path);
if (ret < 0)
goto out;
if (ret > 0) {
@@ -10695,7 +12052,7 @@ next:
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
free(buf);
return ret;
}
@@ -10704,7 +12061,7 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans,
struct btrfs_root *csum_root)
{
struct btrfs_fs_info *fs_info = csum_root->fs_info;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_root *cur_root;
struct extent_buffer *node;
@@ -10712,15 +12069,11 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans,
int slot = 0;
int ret = 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
+ 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);
+ ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
if (ret < 0)
goto out;
if (ret > 0) {
@@ -10729,8 +12082,8 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans,
}
while (1) {
- node = path->nodes[0];
- slot = path->slots[0];
+ 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;
@@ -10751,7 +12104,7 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans,
if (ret < 0)
goto out;
next:
- ret = btrfs_next_item(tree_root, path);
+ ret = btrfs_next_item(tree_root, &path);
if (ret > 0) {
ret = 0;
goto out;
@@ -10761,7 +12114,7 @@ next:
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -10769,36 +12122,32 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *csum_root)
{
struct btrfs_root *extent_root = csum_root->fs_info->extent_root;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_extent_item *ei;
struct extent_buffer *leaf;
char *buf;
struct btrfs_key key;
int ret;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
+ btrfs_init_path(&path);
key.objectid = 0;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
-
- ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
if (ret < 0) {
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
buf = malloc(csum_root->sectorsize);
if (!buf) {
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return -ENOMEM;
}
while (1) {
- if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
- ret = btrfs_next_leaf(extent_root, path);
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(extent_root, &path);
if (ret < 0)
break;
if (ret) {
@@ -10806,19 +12155,19 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans,
break;
}
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.type != BTRFS_EXTENT_ITEM_KEY) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
- ei = btrfs_item_ptr(leaf, path->slots[0],
+ ei = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_extent_item);
if (!(btrfs_extent_flags(leaf, ei) &
BTRFS_EXTENT_FLAG_DATA)) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
@@ -10826,10 +12175,10 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans,
key.offset);
if (ret)
break;
- path->slots[0]++;
+ path.slots[0]++;
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
free(buf);
return ret;
}
@@ -10877,7 +12226,7 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
int ret = 0;
struct btrfs_key key;
struct extent_buffer *leaf;
- struct btrfs_path *path;
+ struct btrfs_path path;
if (!roots_info_cache) {
roots_info_cache = malloc(sizeof(*roots_info_cache));
@@ -10886,24 +12235,20 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
cache_tree_init(roots_info_cache);
}
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
+ btrfs_init_path(&path);
key.objectid = 0;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
-
- ret = btrfs_search_slot(NULL, info->extent_root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, info->extent_root, &key, &path, 0, 0);
if (ret < 0)
goto out;
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
while (1) {
struct btrfs_key found_key;
struct btrfs_extent_item *ei;
struct btrfs_extent_inline_ref *iref;
- int slot = path->slots[0];
+ int slot = path.slots[0];
int type;
u64 flags;
u64 root_id;
@@ -10912,18 +12257,18 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
struct root_item_info *rii;
if (slot >= btrfs_header_nritems(leaf)) {
- ret = btrfs_next_leaf(info->extent_root, path);
+ ret = btrfs_next_leaf(info->extent_root, &path);
if (ret < 0) {
break;
} else if (ret) {
ret = 0;
break;
}
- leaf = path->nodes[0];
- slot = path->slots[0];
+ leaf = path.nodes[0];
+ slot = path.slots[0];
}
- btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]);
if (found_key.type != BTRFS_EXTENT_ITEM_KEY &&
found_key.type != BTRFS_METADATA_ITEM_KEY)
@@ -10986,11 +12331,11 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
rii->node_count++;
}
next:
- path->slots[0]++;
+ path.slots[0]++;
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -11086,7 +12431,7 @@ static int maybe_repair_root_item(struct btrfs_fs_info *info,
*/
static int repair_root_items(struct btrfs_fs_info *info)
{
- struct btrfs_path *path = NULL;
+ struct btrfs_path path;
struct btrfs_key key;
struct extent_buffer *leaf;
struct btrfs_trans_handle *trans = NULL;
@@ -11094,16 +12439,12 @@ static int repair_root_items(struct btrfs_fs_info *info)
int bad_roots = 0;
int need_trans = 0;
+ btrfs_init_path(&path);
+
ret = build_roots_info_cache(info);
if (ret)
goto out;
- path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
-
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = 0;
@@ -11122,19 +12463,19 @@ again:
}
}
- ret = btrfs_search_slot(trans, info->tree_root, &key, path,
+ ret = btrfs_search_slot(trans, info->tree_root, &key, &path,
0, trans ? 1 : 0);
if (ret < 0)
goto out;
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
while (1) {
struct btrfs_key found_key;
- if (path->slots[0] >= btrfs_header_nritems(leaf)) {
- int no_more_keys = find_next_key(path, &key);
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
+ int no_more_keys = find_next_key(&path, &key);
- btrfs_release_path(path);
+ btrfs_release_path(&path);
if (trans) {
ret = btrfs_commit_transaction(trans,
info->tree_root);
@@ -11148,14 +12489,14 @@ again:
goto again;
}
- btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]);
if (found_key.type != BTRFS_ROOT_ITEM_KEY)
goto next;
if (found_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
goto next;
- ret = maybe_repair_root_item(info, path, &found_key,
+ ret = maybe_repair_root_item(info, &path, &found_key,
trans ? 0 : 1);
if (ret < 0)
goto out;
@@ -11163,18 +12504,18 @@ again:
if (!trans && repair) {
need_trans = 1;
key = found_key;
- btrfs_release_path(path);
+ btrfs_release_path(&path);
goto again;
}
bad_roots++;
}
next:
- path->slots[0]++;
+ path.slots[0]++;
}
ret = 0;
out:
free_roots_info_cache();
- btrfs_free_path(path);
+ btrfs_release_path(&path);
if (trans)
btrfs_commit_transaction(trans, info->tree_root);
if (ret < 0)
@@ -11183,6 +12524,36 @@ out:
return bad_roots;
}
+static int clear_free_space_cache(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_block_group_cache *bg_cache;
+ u64 current = 0;
+ int ret = 0;
+
+ /* Clear all free space cache inodes and its extent data */
+ while (1) {
+ bg_cache = btrfs_lookup_first_block_group(fs_info, current);
+ if (!bg_cache)
+ break;
+ ret = btrfs_clear_free_space_cache(fs_info, bg_cache);
+ if (ret < 0)
+ return ret;
+ current = bg_cache->key.objectid + bg_cache->key.offset;
+ }
+
+ /* Don't forget to set cache_generation to -1 */
+ trans = btrfs_start_transaction(fs_info->tree_root, 0);
+ if (IS_ERR(trans)) {
+ error("failed to update super block cache generation");
+ return PTR_ERR(trans);
+ }
+ btrfs_set_super_cache_generation(fs_info->super_copy, (u64)-1);
+ btrfs_commit_transaction(trans, fs_info->tree_root);
+
+ return ret;
+}
+
const char * const cmd_check_usage[] = {
"btrfs check [options] <device>",
"Check structural integrity of a filesystem (unmounted).",
@@ -11197,19 +12568,20 @@ const char * const cmd_check_usage[] = {
"--readonly run in read-only mode (default)",
"--init-csum-tree create a new CRC tree",
"--init-extent-tree create a new extent tree",
- "--mode <MODE> select mode, allows to make some memory/IO",
- " trade-offs, where MODE is one of:",
+ "--mode <MODE> allows choice of memory/IO trade-offs",
+ " where MODE is one of:",
" original - read inodes and extents to memory (requires",
" more memory, does less IO)",
" lowmem - try to use less memory but read blocks again",
" when needed",
"--check-data-csum verify checksums of data blocks",
- "-Q|--qgroup-report print a report on qgroup consistency",
+ "-Q|--qgroup-report print a report on qgroup consistency",
"-E|--subvol-extents <subvolid>",
" print subvolume extents and sharing state",
"-r|--tree-root <bytenr> use the given bytenr for the tree root",
"--chunk-root <bytenr> use the given bytenr for the chunk tree root",
"-p|--progress indicate progress",
+ "--clear-space-cache v1|v2 clear space cache for v1 or v2",
NULL
};
@@ -11224,9 +12596,11 @@ int cmd_check(int argc, char **argv)
u64 chunk_root_bytenr = 0;
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
int ret;
+ int err = 0;
u64 num;
int init_csum_tree = 0;
int readonly = 0;
+ int clear_space_cache = 0;
int qgroup_report = 0;
int qgroups_repaired = 0;
unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE;
@@ -11236,7 +12610,7 @@ int cmd_check(int argc, char **argv)
enum { GETOPT_VAL_REPAIR = 257, GETOPT_VAL_INIT_CSUM,
GETOPT_VAL_INIT_EXTENT, GETOPT_VAL_CHECK_CSUM,
GETOPT_VAL_READONLY, GETOPT_VAL_CHUNK_TREE,
- GETOPT_VAL_MODE };
+ GETOPT_VAL_MODE, GETOPT_VAL_CLEAR_SPACE_CACHE };
static const struct option long_options[] = {
{ "super", required_argument, NULL, 's' },
{ "repair", no_argument, NULL, GETOPT_VAL_REPAIR },
@@ -11256,6 +12630,8 @@ int cmd_check(int argc, char **argv)
{ "progress", no_argument, NULL, 'p' },
{ "mode", required_argument, NULL,
GETOPT_VAL_MODE },
+ { "clear-space-cache", required_argument, NULL,
+ GETOPT_VAL_CLEAR_SPACE_CACHE},
{ NULL, 0, NULL, 0}
};
@@ -11270,8 +12646,8 @@ int cmd_check(int argc, char **argv)
case 's':
num = arg_strtou64(optarg);
if (num >= BTRFS_SUPER_MIRROR_MAX) {
- fprintf(stderr,
- "ERROR: super mirror should be less than: %d\n",
+ error(
+ "super mirror should be less than %d",
BTRFS_SUPER_MIRROR_MAX);
exit(1);
}
@@ -11327,6 +12703,19 @@ int cmd_check(int argc, char **argv)
exit(1);
}
break;
+ case GETOPT_VAL_CLEAR_SPACE_CACHE:
+ if (strcmp(optarg, "v1") == 0) {
+ clear_space_cache = 1;
+ } else if (strcmp(optarg, "v2") == 0) {
+ clear_space_cache = 2;
+ ctree_flags |= OPEN_CTREE_INVALIDATE_FST;
+ } else {
+ error(
+ "invalid argument to --clear-space-cache, must be v1 or v2");
+ exit(1);
+ }
+ ctree_flags |= OPEN_CTREE_WRITES;
+ break;
}
}
@@ -11340,7 +12729,7 @@ int cmd_check(int argc, char **argv)
/* This check is the only reason for --readonly to exist */
if (readonly && repair) {
- fprintf(stderr, "Repair options are not compatible with --readonly\n");
+ error("repair options are not compatible with --readonly");
exit(1);
}
@@ -11348,7 +12737,7 @@ int cmd_check(int argc, char **argv)
* Not supported yet
*/
if (repair && check_mode == CHECK_MODE_LOWMEM) {
- error("Low memory mode doesn't support repair yet");
+ error("low memory mode doesn't support repair yet");
exit(1);
}
@@ -11356,11 +12745,13 @@ int cmd_check(int argc, char **argv)
cache_tree_init(&root_cache);
if((ret = check_mounted(argv[optind])) < 0) {
- fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret));
+ error("could not check mount status: %s", strerror(-ret));
+ err |= !!ret;
goto err_out;
} else if(ret) {
- fprintf(stderr, "%s is currently mounted. Aborting.\n", argv[optind]);
+ error("%s is currently mounted, aborting", argv[optind]);
ret = -EBUSY;
+ err |= !!ret;
goto err_out;
}
@@ -11371,27 +12762,62 @@ int cmd_check(int argc, char **argv)
info = open_ctree_fs_info(argv[optind], bytenr, tree_root_bytenr,
chunk_root_bytenr, ctree_flags);
if (!info) {
- fprintf(stderr, "Couldn't open file system\n");
+ error("cannot open file system");
ret = -EIO;
+ err |= !!ret;
goto err_out;
}
global_info = info;
root = info->fs_root;
+ if (clear_space_cache == 1) {
+ if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
+ error(
+ "free space cache v2 detected, use --clear-space-cache v2");
+ ret = 1;
+ goto close_out;
+ }
+ printf("Clearing free space cache\n");
+ ret = clear_free_space_cache(info);
+ if (ret) {
+ error("failed to clear free space cache");
+ ret = 1;
+ } else {
+ printf("Free space cache cleared\n");
+ }
+ goto close_out;
+ } else if (clear_space_cache == 2) {
+ if (!btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
+ printf("no free space cache v2 to clear\n");
+ ret = 0;
+ goto close_out;
+ }
+ printf("Clear free space cache v2\n");
+ ret = btrfs_clear_free_space_tree(info);
+ if (ret) {
+ error("failed to clear free space cache v2: %d", ret);
+ ret = 1;
+ } else {
+ printf("free space cache v2 cleared\n");
+ }
+ goto close_out;
+ }
/*
* repair mode will force us to commit transaction which
* will make us fail to load log tree when mounting.
*/
if (repair && btrfs_super_log_root(info->super_copy)) {
- ret = ask_user("repair mode will force to clear out log tree, Are you sure?");
+ ret = ask_user("repair mode will force to clear out log tree, are you sure?");
if (!ret) {
ret = 1;
+ err |= !!ret;
goto close_out;
}
ret = zero_log_tree(root);
+ err |= !!ret;
if (ret) {
- fprintf(stderr, "fail to zero log tree\n");
+ error("failed to zero log tree: %d", ret);
goto close_out;
}
}
@@ -11401,6 +12827,7 @@ int cmd_check(int argc, char **argv)
printf("Print quota groups for %s\nUUID: %s\n", argv[optind],
uuidbuf);
ret = qgroup_verify_all(info);
+ err |= !!ret;
if (ret == 0)
report_qgroups(1);
goto close_out;
@@ -11409,6 +12836,7 @@ int cmd_check(int argc, char **argv)
printf("Print extent state for subvolume %llu on %s\nUUID: %s\n",
subvolid, argv[optind], uuidbuf);
ret = print_extent_state(info, subvolid);
+ err |= !!ret;
goto close_out;
}
printf("Checking filesystem on %s\nUUID: %s\n", argv[optind], uuidbuf);
@@ -11416,7 +12844,8 @@ int cmd_check(int argc, char **argv)
if (!extent_buffer_uptodate(info->tree_root->node) ||
!extent_buffer_uptodate(info->dev_root->node) ||
!extent_buffer_uptodate(info->chunk_root->node)) {
- fprintf(stderr, "Critical roots corrupted, unable to fsck the FS\n");
+ error("critical roots corrupted, unable to check the filesystem");
+ err |= !!ret;
ret = -EIO;
goto close_out;
}
@@ -11426,31 +12855,36 @@ int cmd_check(int argc, char **argv)
trans = btrfs_start_transaction(info->extent_root, 0);
if (IS_ERR(trans)) {
- fprintf(stderr, "Error starting transaction\n");
+ error("error starting transaction");
ret = PTR_ERR(trans);
+ err |= !!ret;
goto close_out;
}
if (init_extent_tree) {
printf("Creating a new extent tree\n");
ret = reinit_extent_tree(trans, info);
+ err |= !!ret;
if (ret)
goto close_out;
}
if (init_csum_tree) {
- fprintf(stderr, "Reinit crc root\n");
+ printf("Reinitialize checksum tree\n");
ret = btrfs_fsck_reinit_root(trans, info->csum_root, 0);
if (ret) {
- fprintf(stderr, "crc root initialization failed\n");
+ error("checksum tree initialization failed: %d",
+ ret);
ret = -EIO;
+ err |= !!ret;
goto close_out;
}
ret = fill_csum_tree(trans, info->csum_root,
init_extent_tree);
+ err |= !!ret;
if (ret) {
- fprintf(stderr, "crc refilling failed\n");
+ error("checksum tree refilling failed: %d", ret);
return -EIO;
}
}
@@ -11459,17 +12893,20 @@ int cmd_check(int argc, char **argv)
* extent entries for all of the items it finds.
*/
ret = btrfs_commit_transaction(trans, info->extent_root);
+ err |= !!ret;
if (ret)
goto close_out;
}
if (!extent_buffer_uptodate(info->extent_root->node)) {
- fprintf(stderr, "Critical roots corrupted, unable to fsck the FS\n");
+ error("critical: extent_root, unable to check the filesystem");
ret = -EIO;
+ err |= !!ret;
goto close_out;
}
if (!extent_buffer_uptodate(info->csum_root->node)) {
- fprintf(stderr, "Checksum root corrupted, rerun with --init-csum-tree option\n");
+ error("critical: csum_root, unable to check the filesystem");
ret = -EIO;
+ err |= !!ret;
goto close_out;
}
@@ -11479,10 +12916,13 @@ int cmd_check(int argc, char **argv)
ret = check_chunks_and_extents_v2(root);
else
ret = check_chunks_and_extents(root);
+ err |= !!ret;
if (ret)
- fprintf(stderr, "Errors found in extent allocation tree or chunk allocation\n");
+ error(
+ "errors found in extent allocation tree or chunk allocation");
ret = repair_root_items(info);
+ err |= !!ret;
if (ret < 0)
goto close_out;
if (repair) {
@@ -11495,16 +12935,18 @@ int cmd_check(int argc, char **argv)
fprintf(stderr,
"Please run a filesystem check with the option --repair to fix them.\n");
ret = 1;
+ err |= !!ret;
goto close_out;
}
if (!ctx.progress_enabled) {
- if (btrfs_fs_compat_ro(info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE))
+ if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
fprintf(stderr, "checking free space tree\n");
else
fprintf(stderr, "checking free space cache\n");
}
ret = check_space_cache(root);
+ err |= !!ret;
if (ret)
goto out;
@@ -11514,23 +12956,31 @@ int cmd_check(int argc, char **argv)
* are no gaps in the file extents for inodes, otherwise we can just
* ignore it when this happens.
*/
- no_holes = btrfs_fs_incompat(root->fs_info,
- BTRFS_FEATURE_INCOMPAT_NO_HOLES);
+ no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
if (!ctx.progress_enabled)
fprintf(stderr, "checking fs roots\n");
- ret = check_fs_roots(root, &root_cache);
+ if (check_mode == CHECK_MODE_LOWMEM)
+ ret = check_fs_roots_v2(root->fs_info);
+ else
+ ret = check_fs_roots(root, &root_cache);
+ err |= !!ret;
if (ret)
goto out;
fprintf(stderr, "checking csums\n");
ret = check_csums(root);
+ err |= !!ret;
if (ret)
goto out;
fprintf(stderr, "checking root refs\n");
- ret = check_root_refs(root, &root_cache);
- if (ret)
- goto out;
+ /* For low memory mode, check_fs_roots_v2 handles root refs */
+ if (check_mode != CHECK_MODE_LOWMEM) {
+ ret = check_root_refs(root, &root_cache);
+ err |= !!ret;
+ if (ret)
+ goto out;
+ }
while (repair && !list_empty(&root->fs_info->recow_ebs)) {
struct extent_buffer *eb;
@@ -11539,6 +12989,7 @@ int cmd_check(int argc, char **argv)
struct extent_buffer, recow);
list_del_init(&eb->recow);
ret = recow_extent_buffer(root, eb);
+ err |= !!ret;
if (ret)
break;
}
@@ -11548,32 +12999,33 @@ int cmd_check(int argc, char **argv)
bad = list_first_entry(&delete_items, struct bad_item, list);
list_del_init(&bad->list);
- if (repair)
+ if (repair) {
ret = delete_bad_item(root, bad);
+ err |= !!ret;
+ }
free(bad);
}
if (info->quota_enabled) {
- int err;
fprintf(stderr, "checking quota groups\n");
- err = qgroup_verify_all(info);
- if (err)
+ ret = qgroup_verify_all(info);
+ err |= !!ret;
+ if (ret)
goto out;
report_qgroups(0);
- err = repair_qgroups(info, &qgroups_repaired);
+ ret = repair_qgroups(info, &qgroups_repaired);
+ err |= !!ret;
if (err)
goto out;
+ ret = 0;
}
if (!list_empty(&root->fs_info->recow_ebs)) {
- fprintf(stderr, "Transid errors in file system\n");
+ error("transid errors in file system");
ret = 1;
+ err |= !!ret;
}
out:
- /* Don't override original ret */
- if (!ret && qgroups_repaired)
- ret = qgroups_repaired;
-
if (found_old_backref) { /*
* there was a disk format change when mixed
* backref was in testing tree. The old format
@@ -11583,7 +13035,7 @@ out:
"The old format is not supported! *"
"\n * Please mount the FS in readonly mode, "
"backup data and re-format the FS. *\n\n");
- ret = 1;
+ err |= 1;
}
printf("found %llu bytes used err is %d\n",
(unsigned long long)bytes_used, ret);
@@ -11608,5 +13060,5 @@ err_out:
if (ctx.progress_enabled)
task_deinit(ctx.info);
- return ret;
+ return err;
}
diff --git a/cmds-device.c b/cmds-device.c
index a939c56f..de62cd42 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -283,7 +283,7 @@ static int cmd_device_scan(int argc, char **argv)
if (all || argc - optind == 0) {
printf("Scanning for Btrfs filesystems\n");
- ret = btrfs_scan_lblkid();
+ ret = btrfs_scan_devices();
error_on(ret, "error %d while scanning", ret);
ret = btrfs_register_all_devices();
error_on(ret, "there are %d errors while registering devices", ret);
@@ -372,10 +372,13 @@ out:
}
static const char * const cmd_device_stats_usage[] = {
- "btrfs device stats [-z] <path>|<device>",
- "Show current device IO stats.",
+ "btrfs device stats [options] <path>|<device>",
+ "Show device IO error statistics",
+ "Show device IO error statistics for all devices of the given filesystem",
+ "identified by PATH or DEVICE. The filesystem must be mounted.",
"",
- "-z show current stats and reset values to zero",
+ "-c|--check return non-zero if any stat counter is not zero",
+ "-z|--reset show current stats and reset values to zero",
NULL
};
@@ -387,13 +390,26 @@ static int cmd_device_stats(int argc, char **argv)
int ret;
int fdmnt;
int i;
- int c;
int err = 0;
+ int check = 0;
__u64 flags = 0;
DIR *dirstream = NULL;
- while ((c = getopt(argc, argv, "z")) != -1) {
+ while (1) {
+ int c;
+ static const struct option long_options[] = {
+ {"reset", no_argument, NULL, 'z'},
+ {NULL, 0, NULL, 0}
+ };
+
+ c = getopt_long(argc, argv, "cz", long_options, NULL);
+ if (c < 0)
+ break;
+
switch (c) {
+ case 'c':
+ check = 1;
+ break;
case 'z':
flags = BTRFS_DEV_STATS_RESET;
break;
@@ -414,7 +430,7 @@ static int cmd_device_stats(int argc, char **argv)
ret = get_fs_info(dev_path, &fi_args, &di_args);
if (ret) {
- error("getting dev info for devstats failed: %s",
+ error("getting device info for %s failed: %s", dev_path,
strerror(-ret));
err = 1;
goto out;
@@ -427,24 +443,37 @@ static int cmd_device_stats(int argc, char **argv)
for (i = 0; i < fi_args.num_devices; i++) {
struct btrfs_ioctl_get_dev_stats args = {0};
- __u8 path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
+ char path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
- strncpy((char *)path, (char *)di_args[i].path,
+ strncpy(path, (char *)di_args[i].path,
BTRFS_DEVICE_PATH_NAME_MAX);
- path[BTRFS_DEVICE_PATH_NAME_MAX] = '\0';
+ path[BTRFS_DEVICE_PATH_NAME_MAX] = 0;
args.devid = di_args[i].devid;
args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
args.flags = flags;
if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
- error("DEV_STATS ioctl failed on %s: %s",
+ error("device stats ioctl failed on %s: %s",
path, strerror(errno));
- err = 1;
+ err |= 1;
} else {
char *canonical_path;
-
- canonical_path = canonicalize_path((char *)path);
+ int j;
+ static const struct {
+ const char name[32];
+ u64 num;
+ } dev_stats[] = {
+ { "write_io_errs", BTRFS_DEV_STAT_WRITE_ERRS },
+ { "read_io_errs", BTRFS_DEV_STAT_READ_ERRS },
+ { "flush_io_errs", BTRFS_DEV_STAT_FLUSH_ERRS },
+ { "corruption_errs",
+ BTRFS_DEV_STAT_CORRUPTION_ERRS },
+ { "generation_errs",
+ BTRFS_DEV_STAT_GENERATION_ERRS },
+ };
+
+ canonical_path = canonicalize_path(path);
/* No path when device is missing. */
if (!canonical_path) {
@@ -457,31 +486,18 @@ static int cmd_device_stats(int argc, char **argv)
"devid:%llu", args.devid);
}
- if (args.nr_items >= BTRFS_DEV_STAT_WRITE_ERRS + 1)
- printf("[%s].write_io_errs %llu\n",
- canonical_path,
- (unsigned long long) args.values[
- BTRFS_DEV_STAT_WRITE_ERRS]);
- if (args.nr_items >= BTRFS_DEV_STAT_READ_ERRS + 1)
- printf("[%s].read_io_errs %llu\n",
- canonical_path,
- (unsigned long long) args.values[
- BTRFS_DEV_STAT_READ_ERRS]);
- if (args.nr_items >= BTRFS_DEV_STAT_FLUSH_ERRS + 1)
- printf("[%s].flush_io_errs %llu\n",
- canonical_path,
- (unsigned long long) args.values[
- BTRFS_DEV_STAT_FLUSH_ERRS]);
- if (args.nr_items >= BTRFS_DEV_STAT_CORRUPTION_ERRS + 1)
- printf("[%s].corruption_errs %llu\n",
- canonical_path,
- (unsigned long long) args.values[
- BTRFS_DEV_STAT_CORRUPTION_ERRS]);
- if (args.nr_items >= BTRFS_DEV_STAT_GENERATION_ERRS + 1)
- printf("[%s].generation_errs %llu\n",
- canonical_path,
- (unsigned long long) args.values[
- BTRFS_DEV_STAT_GENERATION_ERRS]);
+ for (j = 0; j < ARRAY_SIZE(dev_stats); j++) {
+ /* We got fewer items than we know */
+ if (args.nr_items < dev_stats[j].num + 1)
+ continue;
+ printf("[%s].%-16s %llu\n", canonical_path,
+ dev_stats[j].name,
+ (unsigned long long)
+ args.values[dev_stats[j].num]);
+ if ((check == 1)
+ && (args.values[dev_stats[j].num] > 0))
+ err |= 64;
+ }
free(canonical_path);
}
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
index ec8e550f..2fc11945 100644
--- a/cmds-fi-du.c
+++ b/cmds-fi-du.c
@@ -27,8 +27,13 @@
#include <sys/ioctl.h>
#include <linux/fs.h>
+#include <linux/version.h>
#include <linux/fiemap.h>
+#if !defined(FIEMAP_EXTENT_SHARED) && (HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE == 1)
+#define FIEMAP_EXTENT_SHARED 0x00002000
+#endif
+
#include "utils.h"
#include "commands.h"
#include "kerncompat.h"
@@ -79,7 +84,7 @@ static int add_shared_extent(u64 start, u64 len, struct rb_root *root)
{
struct shared_extent *sh;
- BUG_ON(len == 0);
+ ASSERT(len != 0);
sh = calloc(1, sizeof(*sh));
if (!sh)
@@ -111,7 +116,7 @@ static void cleanup_shared_extents(struct rb_root *root)
}
}
-#define dprintf(...)
+#define dbgprintf(...)
/*
* Find all extents which overlap 'n', calculate the space
@@ -123,7 +128,7 @@ static u64 count_unique_bytes(struct rb_root *root, struct shared_extent *n)
u64 wstart = n->start;
u64 wlast = n->last;
- dprintf("Count overlaps:");
+ dbgprintf("Count overlaps:");
do {
/*
@@ -136,7 +141,7 @@ static u64 count_unique_bytes(struct rb_root *root, struct shared_extent *n)
if (wlast < n->last)
wlast = n->last;
- dprintf(" (%llu, %llu)", n->start, n->last);
+ dbgprintf(" (%llu, %llu)", n->start, n->last);
tmp = n;
n = extent_tree_iter_next(n, wstart, wlast);
@@ -145,7 +150,7 @@ static u64 count_unique_bytes(struct rb_root *root, struct shared_extent *n)
free(tmp);
} while (n);
- dprintf("; wstart: %llu wlast: %llu total: %llu\n", wstart,
+ dbgprintf("; wstart: %llu wlast: %llu total: %llu\n", wstart,
wlast, wlast - wstart + 1);
return wlast - wstart + 1;
@@ -228,7 +233,7 @@ static int mark_inode_seen(u64 ino, u64 subvol)
else if (cmp > 0)
p = &(*p)->rb_right;
else
- BUG();
+ return -EEXIST;
}
si = calloc(1, sizeof(*si));
@@ -326,6 +331,12 @@ static int du_calc_file_space(int fd, struct rb_root *shared_extents,
if (flags & SKIP_FLAGS)
continue;
+ if (ext_len == 0) {
+ warning("extent %llu has length 0, skipping",
+ (unsigned long long)fm_ext[i].fe_physical);
+ continue;
+ }
+
file_total += ext_len;
if (flags & FIEMAP_EXTENT_SHARED) {
file_shared += ext_len;
@@ -448,7 +459,7 @@ static int du_add_file(const char *filename, int dirfd,
goto out;
}
- ret = lookup_ino_rootid(fd, &subvol);
+ ret = lookup_path_rootid(fd, &subvol);
if (ret)
goto out_close;
@@ -540,6 +551,7 @@ int cmd_filesystem_du(int argc, char **argv)
{
int ret = 0, err = 0;
int i;
+ u32 kernel_version;
unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
@@ -564,6 +576,14 @@ int cmd_filesystem_du(int argc, char **argv)
if (check_argc_min(argc - optind, 1))
usage(cmd_filesystem_du_usage);
+ kernel_version = get_running_kernel_version();
+
+ if (kernel_version < KERNEL_VERSION(2,6,33)) {
+ warning(
+"old kernel version detected, shared space will be reported as exclusive\n"
+"due to missing support for FIEMAP_EXTENT_SHARED flag");
+ }
+
printf("%10s %10s %10s %s\n", "Total", "Exclusive", "Set shared",
"Filename");
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 04d68b18..88e346ad 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -471,7 +471,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
printf(" Device allocated:\t\t%*s\n", width,
pretty_size_mode(r_total_chunks, unit_mode));
printf(" Device unallocated:\t\t%*s\n", width,
- pretty_size_mode(r_total_unused, unit_mode));
+ pretty_size_mode(r_total_unused, unit_mode | UNITS_NEGATIVE));
printf(" Device missing:\t\t%*s\n", width,
pretty_size_mode(r_total_missing, unit_mode));
printf(" Used:\t\t\t%*s\n", width,
@@ -535,7 +535,11 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
}
for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) {
- BUG_ON(ndevs >= fi_args.num_devices);
+ if (ndevs >= fi_args.num_devices) {
+ error("unexpected number of devices: %d >= %llu", ndevs,
+ (unsigned long long)fi_args.num_devices);
+ goto out;
+ }
memset(&dev_info, 0, sizeof(dev_info));
ret = get_device_info(fd, i, &dev_info);
@@ -543,8 +547,7 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
continue;
if (ret) {
error("cannot get info about device devid=%d", i);
- free(info);
- return ret;
+ goto out;
}
info[ndevs].devid = dev_info.devid;
@@ -559,7 +562,12 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
++ndevs;
}
- BUG_ON(ndevs != fi_args.num_devices);
+ if (ndevs != fi_args.num_devices) {
+ error("unexpected number of devices: %d != %llu", ndevs,
+ (unsigned long long)fi_args.num_devices);
+ goto out;
+ }
+
qsort(info, fi_args.num_devices,
sizeof(struct device_info), cmp_device_info);
@@ -567,6 +575,10 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
*device_info_ptr = info;
return 0;
+
+out:
+ free(info);
+ return ret;
}
int load_chunk_and_device_info(int fd, struct chunk_info **chunkinfo,
@@ -724,8 +736,8 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
unused = get_partition_size(device_info_ptr[i].path)
- total_allocated;
- table_printf(matrix, unallocated_col, vhdr_skip + i,
- ">%s", pretty_size_mode(unused, unit_mode));
+ table_printf(matrix, unallocated_col, vhdr_skip + i, ">%s",
+ pretty_size_mode(unused, unit_mode | UNITS_NEGATIVE));
total_unused += unused;
}
@@ -759,7 +771,8 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
}
table_printf(matrix, unallocated_col, vhdr_skip + device_info_count + 1,
- ">%s", pretty_size_mode(total_unused, unit_mode));
+ ">%s",
+ pretty_size_mode(total_unused, unit_mode | UNITS_NEGATIVE));
table_printf(matrix, 1, vhdr_skip + device_info_count + 2, "<Used");
for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
@@ -872,7 +885,7 @@ static void _cmd_filesystem_usage_linear(unsigned unit_mode,
printf("Unallocated:\n");
print_unused(info_ptr, info_count, device_info_ptr, device_info_count,
- unit_mode);
+ unit_mode | UNITS_NEGATIVE);
}
static int print_filesystem_usage_by_chunk(int fd,
@@ -1015,7 +1028,8 @@ void print_device_chunks(int fd, struct device_info *devinfo,
}
printf(" Unallocated: %*s%10s\n",
(int)(20 - strlen("Unallocated")), "",
- pretty_size_mode(devinfo->size - allocated, unit_mode));
+ pretty_size_mode(devinfo->size - allocated,
+ unit_mode | UNITS_NEGATIVE));
}
void print_device_sizes(int fd, struct device_info *devinfo, unsigned unit_mode)
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 90eccf70..e7d31364 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -149,7 +149,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\n", strerror(errno));
+ error("cannot get space info: %s", strerror(errno));
free(sargs);
return -errno;
}
@@ -248,7 +248,7 @@ static int match_search_item_kernel(__u8 *fsid, char *mnt, char *label,
return 0;
}
-static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search)
+static int uuid_search(struct btrfs_fs_devices *fs_devices, const char *search)
{
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
struct list_head *cur;
@@ -495,7 +495,7 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
if ((fd != -1) && !get_df(fd, &space_info_arg)) {
print_one_fs(&fs_info_arg, dev_info_arg,
space_info_arg, label, unit_mode);
- kfree(space_info_arg);
+ free(space_info_arg);
memset(label, 0, sizeof(label));
found = 1;
}
@@ -509,7 +509,7 @@ out:
return !found;
}
-static int dev_to_fsid(char *dev, __u8 *fsid)
+static int dev_to_fsid(const char *dev, __u8 *fsid)
{
struct btrfs_super_block *disk_super;
char buf[BTRFS_SUPER_INFO_SIZE];
@@ -872,10 +872,10 @@ static int cmd_filesystem_show(int argc, char **argv)
goto out;
devs_only:
- ret = btrfs_scan_lblkid();
+ ret = btrfs_scan_devices();
if (ret) {
- error("blkid device scan returned %d\n", ret);
+ error("blkid device scan returned %d", ret);
return 1;
}
@@ -972,20 +972,6 @@ static const char * const cmd_filesystem_defrag_usage[] = {
NULL
};
-static int do_defrag(int fd, int fancy_ioctl,
- struct btrfs_ioctl_defrag_range_args *range)
-{
- int ret;
-
- if (!fancy_ioctl)
- ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL);
- else
- ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, range);
-
- return ret;
-}
-
-static int defrag_global_fancy_ioctl;
static struct btrfs_ioctl_defrag_range_args defrag_global_range;
static int defrag_global_verbose;
static int defrag_global_errors;
@@ -1004,12 +990,11 @@ static int defrag_callback(const char *fpath, const struct stat *sb,
err = errno;
goto error;
}
- ret = do_defrag(fd, defrag_global_fancy_ioctl, &defrag_global_range);
+ ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range);
close(fd);
- if (ret && errno == ENOTTY && defrag_global_fancy_ioctl) {
- error("defrag range ioctl not "
- "supported in this kernel, please try "
- "without any options.");
+ if (ret && errno == ENOTTY) {
+ error(
+"defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
defrag_global_errors++;
return ENOTTY;
}
@@ -1036,21 +1021,20 @@ static int cmd_filesystem_defrag(int argc, char **argv)
int i;
int recursive = 0;
int ret = 0;
- int e = 0;
int compress_type = BTRFS_COMPRESS_NONE;
DIR *dirstream;
/*
* Kernel has a different default (256K) that is supposed to be safe,
* but it does not defragment very well. The 32M will likely lead to
- * better results and is independent of the kernel default.
+ * better results and is independent of the kernel default. We have to
+ * use the v2 defrag ioctl.
*/
thresh = 32 * 1024 * 1024;
defrag_global_errors = 0;
defrag_global_verbose = 0;
defrag_global_errors = 0;
- defrag_global_fancy_ioctl = 0;
while(1) {
int c = getopt(argc, argv, "vrc::fs:l:t:");
if (c < 0)
@@ -1061,22 +1045,18 @@ static int cmd_filesystem_defrag(int argc, char **argv)
compress_type = BTRFS_COMPRESS_ZLIB;
if (optarg)
compress_type = parse_compress_type(optarg);
- defrag_global_fancy_ioctl = 1;
break;
case 'f':
flush = 1;
- defrag_global_fancy_ioctl = 1;
break;
case 'v':
defrag_global_verbose = 1;
break;
case 's':
start = parse_size(optarg);
- defrag_global_fancy_ioctl = 1;
break;
case 'l':
len = parse_size(optarg);
- defrag_global_fancy_ioctl = 1;
break;
case 't':
thresh = parse_size(optarg);
@@ -1086,7 +1066,6 @@ static int cmd_filesystem_defrag(int argc, char **argv)
thresh, (u32)-1);
thresh = (u32)-1;
}
- defrag_global_fancy_ioctl = 1;
break;
case 'r':
recursive = 1;
@@ -1110,13 +1089,43 @@ static int cmd_filesystem_defrag(int argc, char **argv)
if (flush)
defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
+ /*
+ * Look for directory arguments and warn if the recursive mode is not
+ * requested, as this is not implemented as recursive defragmentation
+ * in kernel. The stat errors are silent here as we check them below.
+ */
+ if (!recursive) {
+ int found = 0;
+
+ for (i = optind; i < argc; i++) {
+ struct stat st;
+
+ if (stat(argv[i], &st))
+ continue;
+
+ if (S_ISDIR(st.st_mode)) {
+ warning(
+ "directory specified but recursive mode not requested: %s",
+ argv[i]);
+ found = 1;
+ }
+ }
+ if (found) {
+ warning(
+"a directory passed to the defrag ioctl will not process the files\n"
+"recursively but will defragment the subvolume tree and the extent tree.\n"
+"If this is not intended, please use option -r .");
+ }
+ }
+
for (i = optind; i < argc; i++) {
struct stat st;
+ int defrag_err = 0;
dirstream = NULL;
fd = open_file_or_dir(argv[i], &dirstream);
if (fd < 0) {
- error("cannot open %s: %s\n", argv[i],
+ error("cannot open %s: %s", argv[i],
strerror(errno));
defrag_global_errors++;
close_file_or_dir(fd, dirstream);
@@ -1130,44 +1139,36 @@ static int cmd_filesystem_defrag(int argc, char **argv)
continue;
}
if (!(S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) {
- error("%s is not a directory or a regular file\n",
+ error("%s is not a directory or a regular file",
argv[i]);
defrag_global_errors++;
close_file_or_dir(fd, dirstream);
continue;
}
- if (recursive) {
- if (S_ISDIR(st.st_mode)) {
- ret = nftw(argv[i], defrag_callback, 10,
+ if (recursive && S_ISDIR(st.st_mode)) {
+ ret = nftw(argv[i], defrag_callback, 10,
FTW_MOUNT | FTW_PHYS);
- if (ret == ENOTTY)
- exit(1);
- /* errors are handled in the callback */
- ret = 0;
- } else {
- if (defrag_global_verbose)
- printf("%s\n", argv[i]);
- ret = do_defrag(fd, defrag_global_fancy_ioctl,
- &defrag_global_range);
- e = errno;
- }
+ if (ret == ENOTTY)
+ exit(1);
+ /* errors are handled in the callback */
+ ret = 0;
} else {
if (defrag_global_verbose)
printf("%s\n", argv[i]);
- ret = do_defrag(fd, defrag_global_fancy_ioctl,
+ ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE,
&defrag_global_range);
- e = errno;
+ defrag_err = errno;
}
close_file_or_dir(fd, dirstream);
- if (ret && e == ENOTTY && defrag_global_fancy_ioctl) {
- error("defrag range ioctl not "
- "supported in this kernel, please try "
- "without any options.");
+ if (ret && defrag_err == ENOTTY) {
+ error(
+"defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
defrag_global_errors++;
break;
}
if (ret) {
- error("defrag failed on %s: %s", argv[i], strerror(e));
+ error("defrag failed on %s: %s", argv[i],
+ strerror(defrag_err));
defrag_global_errors++;
}
}
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
index 0ae740a2..ba0d708e 100644
--- a/cmds-inspect-dump-super.c
+++ b/cmds-inspect-dump-super.c
@@ -37,7 +37,7 @@
static int check_csum_sblock(void *sb, int csum_size)
{
- char result[BTRFS_CSUM_SIZE];
+ u8 result[BTRFS_CSUM_SIZE];
u32 crc = ~(u32)0;
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE,
@@ -100,20 +100,19 @@ static void print_sys_chunk_array(struct btrfs_super_block *sb)
if (cur_offset + len > array_size)
goto out_short_read;
- print_chunk(buf, chunk);
num_stripes = btrfs_chunk_num_stripes(buf, chunk);
if (!num_stripes) {
- printk(
- "ERROR: invalid number of stripes %u in sys_array at offset %u\n",
+ error(
+ "invalid number of stripes %u in sys_array at offset %u",
num_stripes, cur_offset);
break;
}
len = btrfs_chunk_item_size(num_stripes);
if (cur_offset + len > array_size)
goto out_short_read;
+ print_chunk(buf, chunk);
} else {
- printk(
- "ERROR: unexpected item type %u in sys_array at offset %u\n",
+ error("unexpected item type %u in sys_array at offset %u",
(u32)key.type, cur_offset);
break;
}
@@ -129,7 +128,7 @@ out:
return;
out_short_read:
- printk("ERROR: sys_array too short to read %u bytes at offset %u\n",
+ error("sys_array too short to read %u bytes at offset %u",
len, cur_offset);
free(buf);
}
@@ -198,6 +197,16 @@ struct readable_flag_entry {
char *output;
};
+#define DEF_COMPAT_RO_FLAG_ENTRY(bit_name) \
+ {BTRFS_FEATURE_COMPAT_RO_##bit_name, #bit_name}
+
+static struct readable_flag_entry compat_ro_flags_array[] = {
+ DEF_COMPAT_RO_FLAG_ENTRY(FREE_SPACE_TREE),
+ DEF_COMPAT_RO_FLAG_ENTRY(FREE_SPACE_TREE_VALID),
+};
+static const int compat_ro_flags_num = sizeof(compat_ro_flags_array) /
+ sizeof(struct readable_flag_entry);
+
#define DEF_INCOMPAT_FLAG_ENTRY(bit_name) \
{BTRFS_FEATURE_INCOMPAT_##bit_name, #bit_name}
@@ -269,6 +278,19 @@ static void __print_readable_flag(u64 flag, struct readable_flag_entry *array,
printf(")\n");
}
+static void print_readable_compat_ro_flag(u64 flag)
+{
+ /*
+ * We know about the FREE_SPACE_TREE{,_VALID} bits, but we don't
+ * actually support them yet.
+ */
+ return __print_readable_flag(flag, compat_ro_flags_array,
+ compat_ro_flags_num,
+ BTRFS_FEATURE_COMPAT_RO_SUPP |
+ BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE |
+ BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID);
+}
+
static void print_readable_incompat_flag(u64 flag)
{
return __print_readable_flag(flag, incompat_flags_array,
@@ -378,6 +400,7 @@ static void dump_superblock(struct btrfs_super_block *sb, int full)
(unsigned long long)btrfs_super_compat_flags(sb));
printf("compat_ro_flags\t\t0x%llx\n",
(unsigned long long)btrfs_super_compat_ro_flags(sb));
+ print_readable_compat_ro_flag(btrfs_super_compat_ro_flags(sb));
printf("incompat_flags\t\t0x%llx\n",
(unsigned long long)btrfs_super_incompat_flags(sb));
print_readable_incompat_flag(btrfs_super_incompat_flags(sb));
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
index 5e206345..df7be617 100644
--- a/cmds-inspect-dump-tree.c
+++ b/cmds-inspect-dump-tree.c
@@ -35,6 +35,7 @@
static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
{
+ struct extent_buffer *next;
int i;
u32 nr;
u32 size;
@@ -50,21 +51,32 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
size = root->nodesize;
nr = btrfs_header_nritems(eb);
for (i = 0; i < nr; i++) {
- struct extent_buffer *next = read_tree_block(root,
- btrfs_node_blockptr(eb, i),
- size,
- btrfs_node_ptr_generation(eb, i));
+ next = read_tree_block(root, btrfs_node_blockptr(eb, i),
+ size, btrfs_node_ptr_generation(eb, i));
if (!extent_buffer_uptodate(next))
continue;
- if (btrfs_is_leaf(next) &&
- btrfs_header_level(eb) != 1)
- BUG();
- if (btrfs_header_level(next) !=
- btrfs_header_level(eb) - 1)
- BUG();
+ if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) {
+ warning(
+ "eb corrupted: item %d eb level %d next level %d, skipping the rest",
+ i, btrfs_header_level(next),
+ btrfs_header_level(eb));
+ goto out;
+ }
+ if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) {
+ warning(
+ "eb corrupted: item %d eb level %d next level %d, skipping the rest",
+ i, btrfs_header_level(next),
+ btrfs_header_level(eb));
+ goto out;
+ }
print_extents(root, next);
free_extent_buffer(next);
}
+
+ return;
+
+out:
+ free_extent_buffer(next);
}
static void print_old_roots(struct btrfs_super_block *super)
@@ -248,26 +260,27 @@ int cmd_inspect_dump_tree(int argc, char **argv)
case 'b':
block_only = arg_strtou64(optarg);
break;
- case 't':
- if (string_is_numerical(optarg)) {
- tree_id = arg_strtou64(optarg);
- } else {
- const char *end = NULL;
+ case 't': {
+ const char *end = NULL;
+ if (string_is_numerical(optarg))
+ tree_id = arg_strtou64(optarg);
+ else
tree_id = treeid_from_string(optarg, &end);
- if (*end) {
- error("unexpected tree id suffix of '%s': %s\n",
- optarg, end);
- exit(1);
- }
- }
if (!tree_id) {
- error("unrecognized tree id: %s\n",
+ error("unrecognized tree id: %s",
optarg);
exit(1);
}
+
+ if (end && *end) {
+ error("unexpected tree id suffix of '%s': %s",
+ optarg, end);
+ exit(1);
+ }
break;
+ }
default:
usage(cmd_inspect_dump_tree_usage);
}
@@ -376,9 +389,14 @@ again:
key.offset = 0;
key.objectid = 0;
- btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+ key.type = BTRFS_ROOT_ITEM_KEY;
ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0);
- BUG_ON(ret < 0);
+ if (ret < 0) {
+ error("cannot read ROOT_ITEM from tree %llu: %s",
+ (unsigned long long)tree_root_scan->root_key.objectid,
+ strerror(-ret));
+ goto close_root;
+ }
while (1) {
leaf = path.nodes[0];
slot = path.slots[0];
@@ -391,7 +409,7 @@ again:
}
btrfs_item_key(leaf, &disk_key, path.slots[0]);
btrfs_disk_key_to_cpu(&found_key, &disk_key);
- if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
+ if (found_key.type == BTRFS_ROOT_ITEM_KEY) {
unsigned long offset;
struct extent_buffer *buf;
int skip = extent_only | device_only | uuid_tree_only;
@@ -525,8 +543,7 @@ next:
no_node:
btrfs_release_path(&path);
- if (tree_root_scan == info->tree_root &&
- info->log_root_tree) {
+ if (tree_root_scan == info->tree_root && info->log_root_tree) {
tree_root_scan = info->log_root_tree;
goto again;
}
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
index cd7ef3ba..0e2786c9 100644
--- a/cmds-inspect-tree-stats.c
+++ b/cmds-inspect-tree-stats.c
@@ -155,7 +155,7 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
root->nodesize,
btrfs_node_ptr_generation(b, i));
if (!extent_buffer_uptodate(tmp)) {
- fprintf(stderr, "Failed to read blocknr %llu\n",
+ error("failed to read blocknr %llu",
btrfs_node_blockptr(b, i));
continue;
}
@@ -175,7 +175,8 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
if (stat->max_seek_len < distance)
stat->max_seek_len = distance;
if (add_seek(&stat->seek_root, distance)) {
- fprintf(stderr, "Error adding new seek\n");
+ error("cannot add new seek at distance %llu",
+ (unsigned long long)distance);
ret = -ENOMEM;
break;
}
@@ -203,7 +204,7 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
stat->highest_bytenr = cur_blocknr;
free_extent_buffer(tmp);
if (ret) {
- fprintf(stderr, "Error walking down path\n");
+ error("walking down path failed: %d", ret);
break;
}
}
@@ -312,7 +313,7 @@ static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
int find_inline)
{
struct btrfs_root *root;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct rb_node *n;
struct timeval start, end, diff = {0};
struct root_stats stat;
@@ -322,39 +323,34 @@ static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
root = btrfs_read_fs_root(tree_root->fs_info, key);
if (IS_ERR(root)) {
- fprintf(stderr, "Failed to read root %llu\n", key->objectid);
- return 1;
- }
-
- path = btrfs_alloc_path();
- if (!path) {
- fprintf(stderr, "Could not allocate path\n");
+ error("failed to read root %llu", key->objectid);
return 1;
}
+ btrfs_init_path(&path);
memset(&stat, 0, sizeof(stat));
level = btrfs_header_level(root->node);
stat.lowest_bytenr = btrfs_header_bytenr(root->node);
stat.highest_bytenr = stat.lowest_bytenr;
stat.min_cluster_size = (u64)-1;
stat.max_cluster_size = root->nodesize;
- path->nodes[level] = root->node;
+ path.nodes[level] = root->node;
if (gettimeofday(&start, NULL)) {
- fprintf(stderr, "Error getting time: %d\n", errno);
+ error("cannot get time: %s", strerror(errno));
goto out;
}
if (!level) {
- ret = walk_leaf(root, path, &stat, find_inline);
+ ret = walk_leaf(root, &path, &stat, find_inline);
if (ret)
goto out;
goto out_print;
}
- ret = walk_nodes(root, path, &stat, level, find_inline);
+ ret = walk_nodes(root, &path, &stat, level, find_inline);
if (ret)
goto out;
if (gettimeofday(&end, NULL)) {
- fprintf(stderr, "Error getting time: %d\n", errno);
+ error("cannot get time: %s", strerror(errno));
goto out;
}
timeval_subtract(&diff, &end, &start);
@@ -416,13 +412,11 @@ out:
}
/*
- * We only use path to save node data in iterating,
- * without holding eb's ref_cnt in path.
- * Don't use btrfs_free_path() here, it will free these
- * eb again, and cause many problems, as negative ref_cnt
- * or invalid memory access.
+ * We only use path to save node data in iterating, without holding
+ * eb's ref_cnt in path. Don't use btrfs_release_path() here, it will
+ * free these eb again, and cause many problems, as negative ref_cnt or
+ * invalid memory access.
*/
- free(path);
return ret;
}
@@ -468,7 +462,7 @@ int cmd_inspect_tree_stats(int argc, char **argv)
root = open_ctree(argv[optind], 0, 0);
if (!root) {
- fprintf(stderr, "Couldn't open ctree\n");
+ error("cannot open ctree");
exit(1);
}
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 4b7cea07..5e58a284 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -146,7 +146,7 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
struct btrfs_ioctl_logical_ino_args loi;
struct btrfs_data_container *inodes;
u64 size = 4096;
- char full_path[4096];
+ char full_path[PATH_MAX];
char *path_ptr;
DIR *dirstream = NULL;
@@ -207,7 +207,10 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
ret = snprintf(full_path, bytes_left, "%s/", argv[optind+1]);
path_ptr = full_path + ret;
bytes_left -= ret + 1;
- BUG_ON(bytes_left < 0);
+ if (bytes_left < 0) {
+ error("path buffer too small: %d bytes", bytes_left);
+ goto out;
+ }
for (i = 0; i < inodes->elem_cnt; i += 3) {
u64 inum = inodes->val[i];
@@ -230,8 +233,12 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
path_ptr[-1] = '/';
ret = snprintf(path_ptr, bytes_left, "%s",
name);
- BUG_ON(ret >= bytes_left);
free(name);
+ if (ret >= bytes_left) {
+ error("path buffer too small: %d bytes",
+ bytes_left - ret);
+ goto out;
+ }
path_fd = btrfs_open_dir(full_path, &dirs, 1);
if (path_fd < 0) {
ret = -ENOENT;
@@ -319,7 +326,7 @@ static int cmd_inspect_rootid(int argc, char **argv)
goto out;
}
- ret = lookup_ino_rootid(fd, &rootid);
+ ret = lookup_path_rootid(fd, &rootid);
if (ret) {
error("failed to lookup root id: %s", strerror(-ret));
goto out;
diff --git a/cmds-property.c b/cmds-property.c
index e59882b4..854bff56 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -199,12 +199,6 @@ out:
return ret;
}
-static int print_prop_help(const struct prop_handler *prop)
-{
- fprintf(stdout, "%-20s%s\n", prop->name, prop->desc);
- return 0;
-}
-
static int dump_prop(const struct prop_handler *prop,
const char *object,
int types,
@@ -217,7 +211,7 @@ static int dump_prop(const struct prop_handler *prop,
if (!name_and_help)
ret = prop->handler(type, object, prop->name, NULL);
else
- ret = print_prop_help(prop);
+ printf("%-20s%s\n", prop->name, prop->desc);
}
return ret;
}
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index a3bc939a..f4503fd9 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -272,8 +272,7 @@ static int cmd_qgroup_destroy(int argc, char **argv)
}
static const char * const cmd_qgroup_show_usage[] = {
- "btrfs qgroup show -pcreFf "
- "[--sort=qgroupid,rfer,excl,max_rfer,max_excl] <path>",
+ "btrfs qgroup show [options] <path>",
"Show subvolume quota groups.",
"-p print parent qgroup id",
"-c print child qgroup id",
@@ -288,6 +287,7 @@ static const char * const cmd_qgroup_show_usage[] = {
" list qgroups sorted by specified items",
" you can use '+' or '-' in front of each item.",
" (+:ascending, -:descending, ascending default)",
+ "--sync force sync of the filesystem before getting info",
NULL
};
@@ -296,11 +296,11 @@ static int cmd_qgroup_show(int argc, char **argv)
char *path;
int ret = 0;
int fd;
- int e;
DIR *dirstream = NULL;
u64 qgroupid;
int filter_flag = 0;
unsigned unit_mode;
+ int sync = 0;
struct btrfs_qgroup_comparer_set *comparer_set;
struct btrfs_qgroup_filter_set *filter_set;
@@ -311,8 +311,13 @@ static int cmd_qgroup_show(int argc, char **argv)
while (1) {
int c;
+ enum {
+ GETOPT_VAL_SORT = 256,
+ GETOPT_VAL_SYNC
+ };
static const struct option long_options[] = {
- {"sort", required_argument, NULL, 'S'},
+ {"sort", required_argument, NULL, GETOPT_VAL_SORT},
+ {"sync", no_argument, NULL, GETOPT_VAL_SYNC},
{ NULL, 0, NULL, 0 }
};
@@ -342,12 +347,15 @@ static int cmd_qgroup_show(int argc, char **argv)
case 'f':
filter_flag |= 0x2;
break;
- case 'S':
+ case GETOPT_VAL_SORT:
ret = btrfs_qgroup_parse_sort_string(optarg,
&comparer_set);
if (ret)
usage(cmd_qgroup_show_usage);
break;
+ case GETOPT_VAL_SYNC:
+ sync = 1;
+ break;
default:
usage(cmd_qgroup_show_usage);
}
@@ -360,13 +368,26 @@ static int cmd_qgroup_show(int argc, char **argv)
path = argv[optind];
fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0) {
- btrfs_qgroup_free_filter_set(filter_set);
- btrfs_qgroup_free_comparer_set(comparer_set);
+ free(filter_set);
+ free(comparer_set);
return 1;
}
+ if (sync) {
+ ret = ioctl(fd, BTRFS_IOC_SYNC);
+ if (ret < 0)
+ warning("sync ioctl failed on '%s': %s", path,
+ strerror(errno));
+ }
+
if (filter_flag) {
- qgroupid = btrfs_get_path_rootid(fd);
+ ret = lookup_path_rootid(fd, &qgroupid);
+ if (ret < 0) {
+ error("cannot resolve rootid for %s: %s",
+ path, strerror(-ret));
+ close_file_or_dir(fd, dirstream);
+ goto out;
+ }
if (filter_flag & 0x1)
btrfs_qgroup_setup_filter(&filter_set,
BTRFS_QGROUP_FILTER_ALL_PARENT,
@@ -377,11 +398,13 @@ static int cmd_qgroup_show(int argc, char **argv)
qgroupid);
}
ret = btrfs_show_qgroups(fd, filter_set, comparer_set);
- e = errno;
+ 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);
- if (ret < 0)
- error("can't list qgroups: %s", strerror(e));
+out:
return !!ret;
}
diff --git a/cmds-quota.c b/cmds-quota.c
index 75c032b1..f9b422dc 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -154,28 +154,42 @@ static int cmd_quota_rescan(int argc, char **argv)
ret = ioctl(fd, ioctlnum, &args);
e = errno;
- if (wait_for_completion && (ret == 0 || e == EINPROGRESS)) {
- ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT, &args);
- e = errno;
- }
- close_file_or_dir(fd, dirstream);
-
- if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN) {
+ if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN_STATUS) {
+ close_file_or_dir(fd, dirstream);
if (ret < 0) {
- error("quota rescan failed: %s", strerror(e));
+ error("could not obtain quota rescan status: %s",
+ strerror(e));
return 1;
- } else {
- printf("quota rescan started\n");
}
- } else {
- if (!args.flags) {
+ if (!args.flags)
printf("no rescan operation in progress\n");
- } else {
+ else
printf("rescan operation running (current key %lld)\n",
args.progress);
+ return 0;
+ }
+
+ if (ret == 0) {
+ printf("quota rescan started\n");
+ fflush(stdout);
+ } else if (ret < 0 && (!wait_for_completion || e != EINPROGRESS)) {
+ error("quota rescan failed: %s", strerror(e));
+ close_file_or_dir(fd, dirstream);
+ return 1;
+ }
+
+ if (wait_for_completion) {
+ ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT, &args);
+ e = errno;
+ if (ret < 0) {
+ error("quota rescan wait failed: %s",
+ strerror(e));
+ close_file_or_dir(fd, dirstream);
+ return 1;
}
}
+ close_file_or_dir(fd, dirstream);
return 0;
}
diff --git a/cmds-receive.c b/cmds-receive.c
index f4a3a4f1..166d37dc 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -49,6 +49,7 @@
#include "send.h"
#include "send-stream.h"
#include "send-utils.h"
+#include "send-dump.h"
static int g_verbose = 0;
@@ -86,7 +87,7 @@ struct btrfs_receive
int cached_capabilities_len;
};
-static int finish_subvol(struct btrfs_receive *r)
+static int finish_subvol(struct btrfs_receive *rctx)
{
int ret;
int subvol_fd = -1;
@@ -94,21 +95,21 @@ static int finish_subvol(struct btrfs_receive *r)
char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
u64 flags;
- if (r->cur_subvol_path[0] == 0)
+ if (rctx->cur_subvol_path[0] == 0)
return 0;
- subvol_fd = openat(r->mnt_fd, r->cur_subvol_path,
- O_RDONLY | O_NOATIME);
+ subvol_fd = openat(rctx->mnt_fd, rctx->cur_subvol_path,
+ O_RDONLY | O_NOATIME);
if (subvol_fd < 0) {
ret = -errno;
- error("cannot open %s: %s\n",
- r->cur_subvol_path, strerror(-ret));
+ error("cannot open %s: %s",
+ rctx->cur_subvol_path, strerror(-ret));
goto out;
}
memset(&rs_args, 0, sizeof(rs_args));
- memcpy(rs_args.uuid, r->cur_subvol.received_uuid, BTRFS_UUID_SIZE);
- rs_args.stransid = r->cur_subvol.stransid;
+ memcpy(rs_args.uuid, rctx->cur_subvol.received_uuid, BTRFS_UUID_SIZE);
+ rs_args.stransid = rctx->cur_subvol.stransid;
if (g_verbose >= 1) {
uuid_unparse((u8*)rs_args.uuid, uuid_str);
@@ -123,7 +124,7 @@ static int finish_subvol(struct btrfs_receive *r)
strerror(-ret));
goto out;
}
- r->cur_subvol.rtransid = rs_args.rtransid;
+ rctx->cur_subvol.rtransid = rs_args.rtransid;
ret = ioctl(subvol_fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
if (ret < 0) {
@@ -146,8 +147,8 @@ static int finish_subvol(struct btrfs_receive *r)
ret = 0;
out:
- if (r->cur_subvol_path[0]) {
- r->cur_subvol_path[0] = 0;
+ if (rctx->cur_subvol_path[0]) {
+ rctx->cur_subvol_path[0] = 0;
}
if (subvol_fd != -1)
close(subvol_fd);
@@ -158,28 +159,39 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
struct btrfs_ioctl_vol_args args_v1;
char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
- ret = finish_subvol(r);
+ ret = finish_subvol(rctx);
if (ret < 0)
goto out;
- BUG_ON(r->cur_subvol.path);
- BUG_ON(r->cur_subvol_path[0]);
+ if (rctx->cur_subvol.path) {
+ error("subvol: another one already started, path ptr: %s",
+ rctx->cur_subvol.path);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (rctx->cur_subvol_path[0]) {
+ error("subvol: another one already started, path buf: %s",
+ rctx->cur_subvol.path);
+ ret = -EINVAL;
+ goto out;
+ }
- if (*r->dest_dir_path == 0) {
- strncpy_null(r->cur_subvol_path, path);
+ if (*rctx->dest_dir_path == 0) {
+ strncpy_null(rctx->cur_subvol_path, path);
} else {
- ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path);
+ ret = path_cat_out(rctx->cur_subvol_path, rctx->dest_dir_path,
+ path);
if (ret < 0) {
- error("subvol: path invalid: %s\n", path);
+ error("subvol: path invalid: %s", path);
goto out;
}
}
- ret = path_cat3_out(r->full_subvol_path, r->root_path,
- r->dest_dir_path, path);
+ ret = path_cat3_out(rctx->full_subvol_path, rctx->root_path,
+ rctx->dest_dir_path, path);
if (ret < 0) {
error("subvol: path invalid: %s", path);
goto out;
@@ -187,19 +199,19 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
fprintf(stderr, "At subvol %s\n", path);
- memcpy(r->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE);
- r->cur_subvol.stransid = ctransid;
+ memcpy(rctx->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE);
+ rctx->cur_subvol.stransid = ctransid;
if (g_verbose) {
- uuid_unparse((u8*)r->cur_subvol.received_uuid, uuid_str);
+ uuid_unparse((u8*)rctx->cur_subvol.received_uuid, uuid_str);
fprintf(stderr, "receiving subvol %s uuid=%s, stransid=%llu\n",
path, uuid_str,
- r->cur_subvol.stransid);
+ rctx->cur_subvol.stransid);
}
memset(&args_v1, 0, sizeof(args_v1));
strncpy_null(args_v1.name, path);
- ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);
+ ret = ioctl(rctx->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);
if (ret < 0) {
ret = -errno;
error("creating subvolume %s failed: %s", path, strerror(-ret));
@@ -215,29 +227,40 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
struct btrfs_ioctl_vol_args_v2 args_v2;
struct subvol_info *parent_subvol = NULL;
- ret = finish_subvol(r);
+ ret = finish_subvol(rctx);
if (ret < 0)
goto out;
- BUG_ON(r->cur_subvol.path);
- BUG_ON(r->cur_subvol_path[0]);
+ if (rctx->cur_subvol.path) {
+ error("snapshot: another one already started, path ptr: %s",
+ rctx->cur_subvol.path);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (rctx->cur_subvol_path[0]) {
+ error("snapshot: another one already started, path buf: %s",
+ rctx->cur_subvol.path);
+ ret = -EINVAL;
+ goto out;
+ }
- if (*r->dest_dir_path == 0) {
- strncpy_null(r->cur_subvol_path, path);
+ if (*rctx->dest_dir_path == 0) {
+ strncpy_null(rctx->cur_subvol_path, path);
} else {
- ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path);
+ ret = path_cat_out(rctx->cur_subvol_path, rctx->dest_dir_path,
+ path);
if (ret < 0) {
error("snapshot: path invalid: %s", path);
goto out;
}
}
- ret = path_cat3_out(r->full_subvol_path, r->root_path,
- r->dest_dir_path, path);
+ ret = path_cat3_out(rctx->full_subvol_path, rctx->root_path,
+ rctx->dest_dir_path, path);
if (ret < 0) {
error("snapshot: path invalid: %s", path);
goto out;
@@ -245,14 +268,14 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
fprintf(stdout, "At snapshot %s\n", path);
- memcpy(r->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE);
- r->cur_subvol.stransid = ctransid;
+ memcpy(rctx->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE);
+ rctx->cur_subvol.stransid = ctransid;
if (g_verbose) {
- uuid_unparse((u8*)r->cur_subvol.received_uuid, uuid_str);
+ uuid_unparse((u8*)rctx->cur_subvol.received_uuid, uuid_str);
fprintf(stderr, "receiving snapshot %s uuid=%s, "
"ctransid=%llu ", path, uuid_str,
- r->cur_subvol.stransid);
+ rctx->cur_subvol.stransid);
uuid_unparse(parent_uuid, uuid_str);
fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n",
uuid_str, parent_ctransid);
@@ -261,14 +284,19 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
memset(&args_v2, 0, sizeof(args_v2));
strncpy_null(args_v2.name, path);
- parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid,
- parent_ctransid, NULL, subvol_search_by_received_uuid);
- if (!parent_subvol) {
- parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid,
- parent_ctransid, NULL, subvol_search_by_uuid);
+ parent_subvol = subvol_uuid_search(&rctx->sus, 0, parent_uuid,
+ parent_ctransid, NULL,
+ subvol_search_by_received_uuid);
+ if (IS_ERR_OR_NULL(parent_subvol)) {
+ parent_subvol = subvol_uuid_search(&rctx->sus, 0, parent_uuid,
+ parent_ctransid, NULL,
+ subvol_search_by_uuid);
}
- if (!parent_subvol) {
- ret = -ENOENT;
+ if (IS_ERR_OR_NULL(parent_subvol)) {
+ if (!parent_subvol)
+ ret = -ENOENT;
+ else
+ ret = PTR_ERR(parent_subvol);
error("cannot find parent subvolume");
goto out;
}
@@ -278,16 +306,16 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
* subvolume under the root subvolume, so try and adjust the path to be
* relative to our root path.
*/
- if (r->full_root_path) {
+ if (rctx->full_root_path) {
size_t root_len;
size_t sub_len;
- root_len = strlen(r->full_root_path);
+ root_len = strlen(rctx->full_root_path);
sub_len = strlen(parent_subvol->path);
/* First make sure the parent subvol is actually in our path */
if (sub_len < root_len ||
- strstr(parent_subvol->path, r->full_root_path) == NULL) {
+ strstr(parent_subvol->path, rctx->full_root_path) == NULL) {
error(
"parent subvol is not reachable from inside the root subvol");
ret = -ENOENT;
@@ -324,10 +352,10 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
}*/
if (*parent_subvol->path == 0)
- args_v2.fd = dup(r->mnt_fd);
+ args_v2.fd = dup(rctx->mnt_fd);
else
- args_v2.fd = openat(r->mnt_fd, parent_subvol->path,
- O_RDONLY | O_NOATIME);
+ args_v2.fd = openat(rctx->mnt_fd, parent_subvol->path,
+ O_RDONLY | O_NOATIME);
if (args_v2.fd < 0) {
ret = -errno;
if (errno != ENOENT)
@@ -342,7 +370,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
goto out;
}
- ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2);
+ ret = ioctl(rctx->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2);
close(args_v2.fd);
if (ret < 0) {
ret = -errno;
@@ -362,10 +390,10 @@ out:
static int process_mkfile(const char *path, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("mkfile: path invalid: %s", path);
goto out;
@@ -390,10 +418,10 @@ out:
static int process_mkdir(const char *path, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("mkdir: path invalid: %s", path);
goto out;
@@ -415,10 +443,10 @@ out:
static int process_mknod(const char *path, u64 mode, u64 dev, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("mknod: path invalid: %s", path);
goto out;
@@ -441,10 +469,10 @@ out:
static int process_mkfifo(const char *path, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("mkfifo: path invalid: %s", path);
goto out;
@@ -466,10 +494,10 @@ out:
static int process_mksock(const char *path, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("mksock: path invalid: %s", path);
goto out;
@@ -491,10 +519,10 @@ out:
static int process_symlink(const char *path, const char *lnk, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("symlink: path invalid: %s", path);
goto out;
@@ -517,17 +545,17 @@ out:
static int process_rename(const char *from, const char *to, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_from[PATH_MAX];
char full_to[PATH_MAX];
- ret = path_cat_out(full_from, r->full_subvol_path, from);
+ ret = path_cat_out(full_from, rctx->full_subvol_path, from);
if (ret < 0) {
error("rename: source path invalid: %s", from);
goto out;
}
- ret = path_cat_out(full_to, r->full_subvol_path, to);
+ ret = path_cat_out(full_to, rctx->full_subvol_path, to);
if (ret < 0) {
error("rename: target path invalid: %s", to);
goto out;
@@ -550,17 +578,17 @@ out:
static int process_link(const char *path, const char *lnk, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
char full_link_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("link: source path invalid: %s", full_path);
goto out;
}
- ret = path_cat_out(full_link_path, r->full_subvol_path, lnk);
+ ret = path_cat_out(full_link_path, rctx->full_subvol_path, lnk);
if (ret < 0) {
error("link: target path invalid: %s", full_link_path);
goto out;
@@ -583,10 +611,10 @@ out:
static int process_unlink(const char *path, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("unlink: path invalid: %s", path);
goto out;
@@ -608,10 +636,10 @@ out:
static int process_rmdir(const char *path, void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("rmdir: path invalid: %s", path);
goto out;
@@ -630,64 +658,64 @@ out:
return ret;
}
-static int open_inode_for_write(struct btrfs_receive *r, const char *path)
+static int open_inode_for_write(struct btrfs_receive *rctx, const char *path)
{
int ret = 0;
- if (r->write_fd != -1) {
- if (strcmp(r->write_path, path) == 0)
+ if (rctx->write_fd != -1) {
+ if (strcmp(rctx->write_path, path) == 0)
goto out;
- close(r->write_fd);
- r->write_fd = -1;
+ close(rctx->write_fd);
+ rctx->write_fd = -1;
}
- r->write_fd = open(path, O_RDWR);
- if (r->write_fd < 0) {
+ rctx->write_fd = open(path, O_RDWR);
+ if (rctx->write_fd < 0) {
ret = -errno;
error("cannot open %s: %s", path, strerror(-ret));
goto out;
}
- strncpy_null(r->write_path, path);
+ strncpy_null(rctx->write_path, path);
out:
return ret;
}
-static void close_inode_for_write(struct btrfs_receive *r)
+static void close_inode_for_write(struct btrfs_receive *rctx)
{
- if(r->write_fd == -1)
+ if(rctx->write_fd == -1)
return;
- close(r->write_fd);
- r->write_fd = -1;
- r->write_path[0] = 0;
+ close(rctx->write_fd);
+ rctx->write_fd = -1;
+ rctx->write_path[0] = 0;
}
static int process_write(const char *path, const void *data, u64 offset,
u64 len, void *user)
{
int ret = 0;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
u64 pos = 0;
int w;
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("write: path invalid: %s", path);
goto out;
}
- ret = open_inode_for_write(r, full_path);
+ ret = open_inode_for_write(rctx, full_path);
if (ret < 0)
goto out;
while (pos < len) {
- w = pwrite(r->write_fd, (char*)data + pos, len - pos,
+ w = pwrite(rctx->write_fd, (char*)data + pos, len - pos,
offset + pos);
if (w < 0) {
ret = -errno;
- error("writing to %s failed: %s\n",
+ error("writing to %s failed: %s",
path, strerror(-ret));
goto out;
}
@@ -704,7 +732,7 @@ static int process_clone(const char *path, u64 offset, u64 len,
void *user)
{
int ret;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
struct btrfs_ioctl_clone_range_args clone_args;
struct subvol_info *si = NULL;
char full_path[PATH_MAX];
@@ -712,25 +740,29 @@ static int process_clone(const char *path, u64 offset, u64 len,
char full_clone_path[PATH_MAX];
int clone_fd = -1;
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("clone: source path invalid: %s", path);
goto out;
}
- ret = open_inode_for_write(r, full_path);
+ ret = open_inode_for_write(rctx, full_path);
if (ret < 0)
goto out;
- si = subvol_uuid_search(&r->sus, 0, clone_uuid, clone_ctransid, NULL,
- subvol_search_by_received_uuid);
- if (!si) {
- if (memcmp(clone_uuid, r->cur_subvol.received_uuid,
+ si = subvol_uuid_search(&rctx->sus, 0, clone_uuid, clone_ctransid,
+ NULL,
+ subvol_search_by_received_uuid);
+ if (IS_ERR_OR_NULL(si)) {
+ if (memcmp(clone_uuid, rctx->cur_subvol.received_uuid,
BTRFS_UUID_SIZE) == 0) {
/* TODO check generation of extent */
- subvol_path = strdup(r->cur_subvol_path);
+ subvol_path = strdup(rctx->cur_subvol_path);
} else {
- ret = -ENOENT;
+ if (!si)
+ ret = -ENOENT;
+ else
+ ret = PTR_ERR(si);
error("clone: did not find source subvol");
goto out;
}
@@ -759,7 +791,7 @@ static int process_clone(const char *path, u64 offset, u64 len,
goto out;
}
- clone_fd = openat(r->mnt_fd, full_clone_path, O_RDONLY | O_NOATIME);
+ clone_fd = openat(rctx->mnt_fd, full_clone_path, O_RDONLY | O_NOATIME);
if (clone_fd < 0) {
ret = -errno;
error("cannot open %s: %s", full_clone_path, strerror(-ret));
@@ -770,10 +802,10 @@ static int process_clone(const char *path, u64 offset, u64 len,
clone_args.src_offset = clone_offset;
clone_args.src_length = len;
clone_args.dest_offset = offset;
- ret = ioctl(r->write_fd, BTRFS_IOC_CLONE_RANGE, &clone_args);
+ ret = ioctl(rctx->write_fd, BTRFS_IOC_CLONE_RANGE, &clone_args);
if (ret < 0) {
ret = -errno;
- error("failed to clone extents to %s\n%s\n",
+ error("failed to clone extents to %s\n%s",
path, strerror(-ret));
goto out;
}
@@ -794,10 +826,10 @@ static int process_set_xattr(const char *path, const char *name,
const void *data, int len, void *user)
{
int ret = 0;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("set_xattr: path invalid: %s", path);
goto out;
@@ -806,17 +838,17 @@ static int process_set_xattr(const char *path, const char *name,
if (strcmp("security.capability", name) == 0) {
if (g_verbose >= 3)
fprintf(stderr, "set_xattr: cache capabilities\n");
- if (r->cached_capabilities_len)
+ if (rctx->cached_capabilities_len)
warning("capabilities set multiple times per file: %s",
full_path);
- if (len > sizeof(r->cached_capabilities)) {
+ if (len > sizeof(rctx->cached_capabilities)) {
error("capabilities encoded to %d bytes, buffer too small",
len);
ret = -E2BIG;
goto out;
}
- r->cached_capabilities_len = len;
- memcpy(r->cached_capabilities, data, len);
+ rctx->cached_capabilities_len = len;
+ memcpy(rctx->cached_capabilities, data, len);
}
if (g_verbose >= 2) {
@@ -840,10 +872,10 @@ out:
static int process_remove_xattr(const char *path, const char *name, void *user)
{
int ret = 0;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("remove_xattr: path invalid: %s", path);
goto out;
@@ -869,10 +901,10 @@ out:
static int process_truncate(const char *path, u64 size, void *user)
{
int ret = 0;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("truncate: path invalid: %s", path);
goto out;
@@ -895,10 +927,10 @@ out:
static int process_chmod(const char *path, u64 mode, void *user)
{
int ret = 0;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("chmod: path invalid: %s", path);
goto out;
@@ -921,10 +953,10 @@ out:
static int process_chown(const char *path, u64 uid, u64 gid, void *user)
{
int ret = 0;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("chown: path invalid: %s", path);
goto out;
@@ -941,15 +973,15 @@ static int process_chown(const char *path, u64 uid, u64 gid, void *user)
goto out;
}
- if (r->cached_capabilities_len) {
+ if (rctx->cached_capabilities_len) {
if (g_verbose >= 2)
fprintf(stderr, "chown: restore capabilities\n");
ret = lsetxattr(full_path, "security.capability",
- r->cached_capabilities,
- r->cached_capabilities_len, 0);
- memset(r->cached_capabilities, 0,
- sizeof(r->cached_capabilities));
- r->cached_capabilities_len = 0;
+ rctx->cached_capabilities,
+ rctx->cached_capabilities_len, 0);
+ memset(rctx->cached_capabilities, 0,
+ sizeof(rctx->cached_capabilities));
+ rctx->cached_capabilities_len = 0;
if (ret < 0) {
ret = -errno;
error("restoring capabilities %s: %s",
@@ -967,11 +999,11 @@ static int process_utimes(const char *path, struct timespec *at,
void *user)
{
int ret = 0;
- struct btrfs_receive *r = user;
+ struct btrfs_receive *rctx = user;
char full_path[PATH_MAX];
struct timespec tv[2];
- ret = path_cat_out(full_path, r->full_subvol_path, path);
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
if (ret < 0) {
error("utimes: path invalid: %s", path);
goto out;
@@ -1033,7 +1065,7 @@ static struct btrfs_send_ops send_ops = {
.update_extent = process_update_extent,
};
-static int do_receive(struct btrfs_receive *r, const char *tomnt,
+static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
char *realmnt, int r_fd, u64 max_errors)
{
u64 subvol_id;
@@ -1041,6 +1073,7 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
char *dest_dir_full_path;
char root_subvol_path[PATH_MAX];
int end = 0;
+ int count;
dest_dir_full_path = realpath(tomnt, NULL);
if (!dest_dir_full_path) {
@@ -1048,8 +1081,8 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
error("realpath(%s) failed: %s", tomnt, strerror(-ret));
goto out;
}
- r->dest_dir_fd = open(dest_dir_full_path, O_RDONLY | O_NOATIME);
- if (r->dest_dir_fd < 0) {
+ rctx->dest_dir_fd = open(dest_dir_full_path, O_RDONLY | O_NOATIME);
+ if (rctx->dest_dir_fd < 0) {
ret = -errno;
error("cannot open destination directory %s: %s",
dest_dir_full_path, strerror(-ret));
@@ -1057,9 +1090,9 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
}
if (realmnt[0]) {
- r->root_path = realmnt;
+ rctx->root_path = realmnt;
} else {
- ret = find_mount_root(dest_dir_full_path, &r->root_path);
+ ret = find_mount_root(dest_dir_full_path, &rctx->root_path);
if (ret < 0) {
error("failed to determine mount point for %s: %s",
dest_dir_full_path, strerror(-ret));
@@ -1073,10 +1106,10 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
goto out;
}
}
- r->mnt_fd = open(r->root_path, O_RDONLY | O_NOATIME);
- if (r->mnt_fd < 0) {
+ rctx->mnt_fd = open(rctx->root_path, O_RDONLY | O_NOATIME);
+ if (rctx->mnt_fd < 0) {
ret = -errno;
- error("cannot open %s: %s", r->root_path, strerror(-ret));
+ error("cannot open %s: %s", rctx->root_path, strerror(-ret));
goto out;
}
@@ -1085,15 +1118,12 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
* subvolume we're sitting in so that we can adjust the paths of any
* subvols we want to receive in.
*/
- ret = btrfs_list_get_path_rootid(r->mnt_fd, &subvol_id);
- if (ret) {
- error("cannot resolve our subvolid: %d",
- ret);
+ ret = btrfs_list_get_path_rootid(rctx->mnt_fd, &subvol_id);
+ if (ret)
goto out;
- }
root_subvol_path[0] = 0;
- ret = btrfs_subvolid_resolve(r->mnt_fd, root_subvol_path,
+ ret = btrfs_subvolid_resolve(rctx->mnt_fd, root_subvol_path,
PATH_MAX, subvol_id);
if (ret) {
error("cannot resolve our subvol path");
@@ -1105,9 +1135,9 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
* actually set full_root_path.
*/
if (*root_subvol_path)
- r->full_root_path = root_subvol_path;
+ rctx->full_root_path = root_subvol_path;
- if (r->dest_dir_chroot) {
+ if (rctx->dest_dir_chroot) {
if (chroot(dest_dir_full_path)) {
ret = -errno;
error("failed to chroot to %s: %s",
@@ -1121,66 +1151,75 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
goto out;
}
fprintf(stderr, "Chroot to %s\n", dest_dir_full_path);
- r->root_path = strdup("/");
- r->dest_dir_path = r->root_path;
+ rctx->root_path = strdup("/");
+ rctx->dest_dir_path = rctx->root_path;
} else {
/*
* find_mount_root returns a root_path that is a subpath of
* dest_dir_full_path. Now get the other part of root_path,
* which is the destination dir relative to root_path.
*/
- r->dest_dir_path = dest_dir_full_path + strlen(r->root_path);
- while (r->dest_dir_path[0] == '/')
- r->dest_dir_path++;
+ rctx->dest_dir_path = dest_dir_full_path + strlen(rctx->root_path);
+ while (rctx->dest_dir_path[0] == '/')
+ rctx->dest_dir_path++;
}
- ret = subvol_uuid_search_init(r->mnt_fd, &r->sus);
+ ret = subvol_uuid_search_init(rctx->mnt_fd, &rctx->sus);
if (ret < 0)
goto out;
+ count = 0;
while (!end) {
- if (r->cached_capabilities_len) {
+ if (rctx->cached_capabilities_len) {
if (g_verbose >= 3)
fprintf(stderr, "clear cached capabilities\n");
- memset(r->cached_capabilities, 0,
- sizeof(r->cached_capabilities));
- r->cached_capabilities_len = 0;
+ memset(rctx->cached_capabilities, 0,
+ sizeof(rctx->cached_capabilities));
+ rctx->cached_capabilities_len = 0;
}
- ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, r,
- r->honor_end_cmd,
+ ret = btrfs_read_and_process_send_stream(r_fd, &send_ops,
+ rctx,
+ rctx->honor_end_cmd,
max_errors);
if (ret < 0)
goto out;
+ /* Empty stream is invalid */
+ if (ret && count == 0) {
+ error("empty stream is not considered valid");
+ ret = -EINVAL;
+ goto out;
+ }
+ count++;
if (ret)
end = 1;
- close_inode_for_write(r);
- ret = finish_subvol(r);
+ close_inode_for_write(rctx);
+ ret = finish_subvol(rctx);
if (ret < 0)
goto out;
}
ret = 0;
out:
- if (r->write_fd != -1) {
- close(r->write_fd);
- r->write_fd = -1;
+ if (rctx->write_fd != -1) {
+ close(rctx->write_fd);
+ rctx->write_fd = -1;
}
- if (r->root_path != realmnt)
- free(r->root_path);
- r->root_path = NULL;
- r->dest_dir_path = NULL;
+ if (rctx->root_path != realmnt)
+ free(rctx->root_path);
+ rctx->root_path = NULL;
+ rctx->dest_dir_path = NULL;
free(dest_dir_full_path);
- subvol_uuid_search_finit(&r->sus);
- if (r->mnt_fd != -1) {
- close(r->mnt_fd);
- r->mnt_fd = -1;
+ subvol_uuid_search_finit(&rctx->sus);
+ if (rctx->mnt_fd != -1) {
+ close(rctx->mnt_fd);
+ rctx->mnt_fd = -1;
}
- if (r->dest_dir_fd != -1) {
- close(r->dest_dir_fd);
- r->dest_dir_fd = -1;
+ if (rctx->dest_dir_fd != -1) {
+ close(rctx->dest_dir_fd);
+ rctx->dest_dir_fd = -1;
}
return ret;
@@ -1191,24 +1230,27 @@ int cmd_receive(int argc, char **argv)
char *tomnt = NULL;
char fromfile[PATH_MAX];
char realmnt[PATH_MAX];
- struct btrfs_receive r;
+ struct btrfs_receive rctx;
int receive_fd = fileno(stdin);
u64 max_errors = 1;
+ int dump = 0;
int ret = 0;
- memset(&r, 0, sizeof(r));
- r.mnt_fd = -1;
- r.write_fd = -1;
- r.dest_dir_fd = -1;
- r.dest_dir_chroot = 0;
+ memset(&rctx, 0, sizeof(rctx));
+ rctx.mnt_fd = -1;
+ rctx.write_fd = -1;
+ rctx.dest_dir_fd = -1;
+ rctx.dest_dir_chroot = 0;
realmnt[0] = 0;
fromfile[0] = 0;
while (1) {
int c;
+ enum { GETOPT_VAL_DUMP = 257 };
static const struct option long_opts[] = {
{ "max-errors", required_argument, NULL, 'E' },
{ "chroot", no_argument, NULL, 'C' },
+ { "dump", no_argument, NULL, GETOPT_VAL_DUMP },
{ NULL, 0, NULL, 0 }
};
@@ -1229,10 +1271,10 @@ int cmd_receive(int argc, char **argv)
}
break;
case 'e':
- r.honor_end_cmd = 1;
+ rctx.honor_end_cmd = 1;
break;
case 'C':
- r.dest_dir_chroot = 1;
+ rctx.dest_dir_chroot = 1;
break;
case 'E':
max_errors = arg_strtou64(optarg);
@@ -1245,6 +1287,9 @@ int cmd_receive(int argc, char **argv)
goto out;
}
break;
+ case GETOPT_VAL_DUMP:
+ dump = 1;
+ break;
case '?':
default:
error("receive args invalid");
@@ -1252,7 +1297,9 @@ int cmd_receive(int argc, char **argv)
}
}
- if (check_argc_exact(argc - optind, 1))
+ if (dump && check_argc_exact(argc - optind, 0))
+ usage(cmd_receive_usage);
+ if (!dump && check_argc_exact(argc - optind, 1))
usage(cmd_receive_usage);
tomnt = argv[optind];
@@ -1265,42 +1312,56 @@ int cmd_receive(int argc, char **argv)
}
}
- ret = do_receive(&r, tomnt, realmnt, receive_fd, max_errors);
+ if (dump) {
+ struct btrfs_dump_send_args dump_args;
+
+ dump_args.root_path[0] = '.';
+ dump_args.root_path[1] = '\0';
+ dump_args.full_subvol_path[0] = '.';
+ dump_args.full_subvol_path[1] = '\0';
+ ret = btrfs_read_and_process_send_stream(receive_fd,
+ &btrfs_print_send_ops, &dump_args, 0, 0);
+ if (ret < 0)
+ error("failed to dump the send stream: %s",
+ strerror(-ret));
+ } else {
+ ret = do_receive(&rctx, tomnt, realmnt, receive_fd, max_errors);
+ }
+
if (receive_fd != fileno(stdin))
close(receive_fd);
-
out:
return !!ret;
}
const char * const cmd_receive_usage[] = {
- "btrfs receive [-ve] [-f <infile>] [--max-errors <N>] <mount>",
- "Receive subvolumes from stdin.",
+ "btrfs receive [options] <mount>\n"
+ "btrfs receive --dump [options]",
+ "Receive subvolumes from a stream",
"Receives one or more subvolumes that were previously",
"sent with btrfs send. The received subvolumes are stored",
- "into <mount>.",
- "btrfs receive will fail in case a receiving subvolume",
+ "into MOUNT.",
+ "The receive will fail in case the receiving subvolume",
"already exists. It will also fail in case a previously",
- "received subvolume was changed after it was received.",
+ "received subvolume has been changed after it was received.",
"After receiving a subvolume, it is immediately set to",
- "read only.\n",
- "-v Enable verbose debug output. Each",
- " occurrence of this option increases the",
- " verbose level more.",
- "-f <infile> By default, btrfs receive uses stdin",
- " to receive the subvolumes. Use this",
- " option to specify a file to use instead.",
- "-e Terminate after receiving an <end cmd>",
- " in the data stream. Without this option,",
- " the receiver terminates only if an error",
- " is recognized or on EOF.",
+ "read-only.",
+ "",
+ "-v increase verbosity about performed actions",
+ "-f FILE read the stream from FILE instead of stdin",
+ "-e terminate after receiving an <end cmd> marker in the stream.",
+ " Without this option the receiver side terminates only in case",
+ " of an error on end of file.",
"-C|--chroot confine the process to <mount> using chroot",
- "--max-errors <N> Terminate as soon as N errors happened while",
- " processing commands from the send stream.",
+ "-E|--max-errors NERR",
+ " terminate as soon as NERR errors occur while",
+ " stream processing commands from the stream.",
" Default value is 1. A value of 0 means no limit.",
- "-m <mountpoint> The root mount point of the destination fs.",
- " If you do not have /proc use this to tell us where ",
+ "-m ROOTMOUNT the root mount point of the destination filesystem.",
+ " If /proc is not accessible, use this to tell us where",
" this file system is mounted.",
+ "--dump dump stream metadata, one line per operation,",
+ " does not require the MOUNT parameter",
NULL
};
diff --git a/cmds-replace.c b/cmds-replace.c
index d1bf057e..9345da23 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -436,7 +436,7 @@ static int print_replace_status(int fd, const char *path, int once)
printf("Never started");
break;
default:
- error("unknown status from ioctl DEV_REPLACE_STATUS on '%s': %llu\n",
+ error("unknown status from ioctl DEV_REPLACE_STATUS on '%s': %llu",
path, status->replace_state);
return -EINVAL;
}
diff --git a/cmds-restore.c b/cmds-restore.c
index b491f083..bdd35bd7 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -330,7 +330,7 @@ static int copy_one_extent(struct btrfs_root *root, int fd,
inbuf = malloc(size_left);
if (!inbuf) {
- error("not enough memory\n");
+ error("not enough memory");
return -ENOMEM;
}
@@ -356,7 +356,7 @@ again:
dev_fd = device->fd;
device->total_ios++;
dev_bytenr = multi->stripes[0].physical;
- kfree(multi);
+ free(multi);
if (size_left < length)
length = size_left;
@@ -461,7 +461,7 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
int fd, const char *file_name)
{
struct btrfs_key key;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_dir_item *di;
u32 name_len = 0;
@@ -472,25 +472,21 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
char *data = NULL;
int ret = 0;
+ btrfs_init_path(&path);
key.objectid = inode;
key.type = BTRFS_XATTR_ITEM_KEY;
key.offset = 0;
-
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0)
goto out;
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
while (1) {
- if (path->slots[0] >= btrfs_header_nritems(leaf)) {
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
do {
- ret = next_leaf(root, path);
+ ret = next_leaf(root, &path);
if (ret < 0) {
- error("searching for extended attributes: %d\n",
+ error("searching for extended attributes: %d",
ret);
goto out;
} else if (ret) {
@@ -498,17 +494,17 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
ret = 0;
goto out;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
} while (!leaf);
continue;
}
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.type != BTRFS_XATTR_ITEM_KEY || key.objectid != inode)
break;
cur = 0;
- total_len = btrfs_item_size_nr(leaf, path->slots[0]);
- di = btrfs_item_ptr(leaf, path->slots[0],
+ total_len = btrfs_item_size_nr(leaf, path.slots[0]);
+ di = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_dir_item);
while (cur < total_len) {
@@ -548,11 +544,11 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
cur += len;
di = (struct btrfs_dir_item *)((char *)di + len);
}
- path->slots[0]++;
+ path.slots[0]++;
}
ret = 0;
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
free(name);
free(data);
@@ -562,44 +558,39 @@ out:
static int copy_metadata(struct btrfs_root *root, int fd,
struct btrfs_key *key)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_inode_item *inode_item;
int ret;
- path = btrfs_alloc_path();
- if (!path) {
- error("not enough memory");
- return -ENOMEM;
- }
-
- ret = btrfs_lookup_inode(NULL, root, path, key, 0);
+ btrfs_init_path(&path);
+ ret = btrfs_lookup_inode(NULL, root, &path, key, 0);
if (ret == 0) {
struct btrfs_timespec *bts;
struct timespec times[2];
- inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_inode_item);
- ret = fchown(fd, btrfs_inode_uid(path->nodes[0], inode_item),
- btrfs_inode_gid(path->nodes[0], inode_item));
+ 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));
goto out;
}
- ret = fchmod(fd, btrfs_inode_mode(path->nodes[0], inode_item));
+ ret = fchmod(fd, btrfs_inode_mode(path.nodes[0], inode_item));
if (ret) {
error("failed to change mode: %s", strerror(errno));
goto out;
}
bts = btrfs_inode_atime(inode_item);
- times[0].tv_sec = btrfs_timespec_sec(path->nodes[0], bts);
- times[0].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts);
+ times[0].tv_sec = btrfs_timespec_sec(path.nodes[0], bts);
+ times[0].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts);
bts = btrfs_inode_mtime(inode_item);
- times[1].tv_sec = btrfs_timespec_sec(path->nodes[0], bts);
- times[1].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts);
+ times[1].tv_sec = btrfs_timespec_sec(path.nodes[0], bts);
+ times[1].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts);
ret = futimens(fd, times);
if (ret) {
@@ -608,7 +599,7 @@ static int copy_metadata(struct btrfs_root *root, int fd,
}
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -616,7 +607,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
const char *file)
{
struct extent_buffer *leaf;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_file_extent_item *fi;
struct btrfs_inode_item *inode_item;
struct btrfs_timespec *bts;
@@ -629,17 +620,12 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
struct timespec times[2];
int times_ok = 0;
- path = btrfs_alloc_path();
- if (!path) {
- error("not enough memory");
- return -ENOMEM;
- }
-
- ret = btrfs_lookup_inode(NULL, root, path, key, 0);
+ btrfs_init_path(&path);
+ ret = btrfs_lookup_inode(NULL, root, &path, key, 0);
if (ret == 0) {
- inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_inode_item);
- found_size = btrfs_inode_size(path->nodes[0], inode_item);
+ found_size = btrfs_inode_size(path.nodes[0], inode_item);
if (restore_metadata) {
/*
@@ -647,39 +633,39 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
* copyout is finished.
*/
- ret = fchown(fd, btrfs_inode_uid(path->nodes[0], inode_item),
- btrfs_inode_gid(path->nodes[0], inode_item));
+ ret = fchown(fd, btrfs_inode_uid(path.nodes[0], inode_item),
+ btrfs_inode_gid(path.nodes[0], inode_item));
if (ret && !ignore_errors)
goto out;
- ret = fchmod(fd, btrfs_inode_mode(path->nodes[0], inode_item));
+ ret = fchmod(fd, btrfs_inode_mode(path.nodes[0], inode_item));
if (ret && !ignore_errors)
goto out;
bts = btrfs_inode_atime(inode_item);
- times[0].tv_sec = btrfs_timespec_sec(path->nodes[0], bts);
- times[0].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts);
+ times[0].tv_sec = btrfs_timespec_sec(path.nodes[0], bts);
+ times[0].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts);
bts = btrfs_inode_mtime(inode_item);
- times[1].tv_sec = btrfs_timespec_sec(path->nodes[0], bts);
- times[1].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts);
+ times[1].tv_sec = btrfs_timespec_sec(path.nodes[0], bts);
+ times[1].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts);
times_ok = 1;
}
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
key->offset = 0;
key->type = BTRFS_EXTENT_DATA_KEY;
- ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
if (ret < 0) {
error("searching extent data returned %d", ret);
goto out;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
while (!leaf) {
- ret = next_leaf(root, path);
+ ret = next_leaf(root, &path);
if (ret < 0) {
error("cannot get next leaf: %d", ret);
goto out;
@@ -688,7 +674,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
ret = 0;
goto out;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
}
while (1) {
@@ -703,27 +689,27 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
else if (resp == LOOP_DONTASK)
loops = -1;
}
- if (path->slots[0] >= btrfs_header_nritems(leaf)) {
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
do {
- ret = next_leaf(root, path);
+ ret = next_leaf(root, &path);
if (ret < 0) {
fprintf(stderr, "Error searching %d\n", ret);
goto out;
} else if (ret) {
/* No more leaves to search */
- btrfs_free_path(path);
+ btrfs_release_path(&path);
goto set_size;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
} while (!leaf);
continue;
}
- btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]);
if (found_key.objectid != key->objectid)
break;
if (found_key.type != key->type)
break;
- fi = btrfs_item_ptr(leaf, path->slots[0],
+ fi = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_file_extent_item);
extent_type = btrfs_file_extent_type(leaf, fi);
compression = btrfs_file_extent_compression(leaf, fi);
@@ -737,7 +723,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
if (extent_type == BTRFS_FILE_EXTENT_PREALLOC)
goto next;
if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
- ret = copy_one_inline(root, fd, path, found_key.offset);
+ ret = copy_one_inline(root, fd, &path, found_key.offset);
if (ret)
goto out;
} else if (extent_type == BTRFS_FILE_EXTENT_REG) {
@@ -749,10 +735,10 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
warning("weird extent type %d", extent_type);
}
next:
- path->slots[0]++;
+ path.slots[0]++;
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
set_size:
if (found_size) {
ret = ftruncate(fd, (loff_t)found_size);
@@ -772,7 +758,7 @@ set_size:
return 0;
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -809,7 +795,7 @@ static int overwrite_ok(const char * path)
static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
const char *file)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_file_extent_item *extent_item;
struct btrfs_inode_item *inode_item;
@@ -833,29 +819,25 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
}
}
+ btrfs_init_path(&path);
key->type = BTRFS_EXTENT_DATA_KEY;
key->offset = 0;
-
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
- ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
if (ret < 0)
goto out;
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
if (!leaf) {
fprintf(stderr, "Error getting leaf for symlink '%s'\n", file);
ret = -1;
goto out;
}
- extent_item = btrfs_item_ptr(leaf, path->slots[0],
+ extent_item = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_file_extent_item);
len = btrfs_file_extent_inline_item_len(leaf,
- btrfs_item_nr(path->slots[0]));
+ btrfs_item_nr(path.slots[0]));
if (len >= PATH_MAX) {
fprintf(stderr, "Symlink '%s' target length %d is longer than PATH_MAX\n",
fs_name, len);
@@ -890,19 +872,19 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
key->type = BTRFS_INODE_ITEM_KEY;
key->offset = 0;
- btrfs_release_path(path);
+ btrfs_release_path(&path);
- ret = btrfs_lookup_inode(NULL, root, path, key, 0);
+ ret = btrfs_lookup_inode(NULL, root, &path, key, 0);
if (ret) {
fprintf(stderr, "Failed to lookup inode for '%s'\n", file);
goto out;
}
- inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_inode_item);
- ret = fchownat(-1, file, btrfs_inode_uid(path->nodes[0], inode_item),
- btrfs_inode_gid(path->nodes[0], inode_item),
+ ret = fchownat(-1, file, btrfs_inode_uid(path.nodes[0], inode_item),
+ btrfs_inode_gid(path.nodes[0], inode_item),
AT_SYMLINK_NOFOLLOW);
if (ret) {
fprintf(stderr, "Failed to change owner: %s\n",
@@ -911,18 +893,18 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
}
bts = btrfs_inode_atime(inode_item);
- times[0].tv_sec = btrfs_timespec_sec(path->nodes[0], bts);
- times[0].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts);
+ times[0].tv_sec = btrfs_timespec_sec(path.nodes[0], bts);
+ times[0].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts);
bts = btrfs_inode_mtime(inode_item);
- times[1].tv_sec = btrfs_timespec_sec(path->nodes[0], bts);
- times[1].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts);
+ times[1].tv_sec = btrfs_timespec_sec(path.nodes[0], bts);
+ times[1].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts);
ret = utimensat(-1, file, times, AT_SYMLINK_NOFOLLOW);
if (ret)
fprintf(stderr, "Failed to set times: %s\n", strerror(errno));
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -930,7 +912,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
const char *output_rootdir, const char *in_dir,
const regex_t *mreg)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_dir_item *dir_item;
struct btrfs_key found_key, location;
@@ -942,16 +924,10 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
int loops = 0;
u8 type;
- path = btrfs_alloc_path();
- if (!path) {
- fprintf(stderr, "Ran out of memory\n");
- return -ENOMEM;
- }
-
+ btrfs_init_path(&path);
key->offset = 0;
key->type = BTRFS_DIR_INDEX_KEY;
-
- ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
if (ret < 0) {
fprintf(stderr, "Error searching %d\n", ret);
goto out;
@@ -959,12 +935,12 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
ret = 0;
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
while (!leaf) {
if (verbose > 1)
printf("No leaf after search, looking for the next "
"leaf\n");
- ret = next_leaf(root, path);
+ ret = next_leaf(root, &path);
if (ret < 0) {
fprintf(stderr, "Error getting next leaf %d\n",
ret);
@@ -977,7 +953,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
ret = 0;
goto out;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
}
while (leaf) {
@@ -988,9 +964,9 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
break;
}
- if (path->slots[0] >= btrfs_header_nritems(leaf)) {
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
do {
- ret = next_leaf(root, path);
+ ret = next_leaf(root, &path);
if (ret < 0) {
fprintf(stderr, "Error searching %d\n",
ret);
@@ -1004,11 +980,11 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
ret = 0;
goto out;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
} while (!leaf);
continue;
}
- btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]);
if (found_key.objectid != key->objectid) {
if (verbose > 1)
printf("Found objectid=%Lu, key=%Lu\n",
@@ -1021,7 +997,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
found_key.type, key->type);
break;
}
- dir_item = btrfs_item_ptr(leaf, path->slots[0],
+ dir_item = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_dir_item);
name_ptr = (unsigned long)(dir_item + 1);
name_len = btrfs_dir_name_len(leaf, dir_item);
@@ -1153,12 +1129,12 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
if (ret < 0) {
if (ignore_errors)
goto next;
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
}
next:
- path->slots[0]++;
+ path.slots[0]++;
}
if (restore_metadata) {
@@ -1186,7 +1162,7 @@ next:
if (verbose)
printf("Done searching %s\n", in_dir);
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -1195,7 +1171,7 @@ static int do_list_roots(struct btrfs_root *root)
struct btrfs_key key;
struct btrfs_key found_key;
struct btrfs_disk_key disk_key;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_root_item ri;
unsigned long offset;
@@ -1203,38 +1179,33 @@ static int do_list_roots(struct btrfs_root *root)
int ret;
root = root->fs_info->tree_root;
- path = btrfs_alloc_path();
- if (!path) {
- fprintf(stderr, "Failed to alloc path\n");
- return -ENOMEM;
- }
+ btrfs_init_path(&path);
key.offset = 0;
key.objectid = 0;
key.type = BTRFS_ROOT_ITEM_KEY;
-
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0) {
fprintf(stderr, "Failed to do search %d\n", ret);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return -1;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
while (1) {
- slot = path->slots[0];
+ slot = path.slots[0];
if (slot >= btrfs_header_nritems(leaf)) {
- ret = btrfs_next_leaf(root, path);
+ ret = btrfs_next_leaf(root, &path);
if (ret)
break;
- leaf = path->nodes[0];
- slot = path->slots[0];
+ leaf = path.nodes[0];
+ slot = path.slots[0];
}
btrfs_item_key(leaf, &disk_key, slot);
btrfs_disk_key_to_cpu(&found_key, &disk_key);
- if (btrfs_key_type(&found_key) != BTRFS_ROOT_ITEM_KEY) {
- path->slots[0]++;
+ if (found_key.type != BTRFS_ROOT_ITEM_KEY) {
+ path.slots[0]++;
continue;
}
@@ -1244,9 +1215,9 @@ static int do_list_roots(struct btrfs_root *root)
btrfs_print_key(&disk_key);
printf(" %Lu level %d\n", btrfs_root_bytenr(&ri),
btrfs_root_level(&ri));
- path->slots[0]++;
+ path.slots[0]++;
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return 0;
}
@@ -1317,36 +1288,30 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location,
static int find_first_dir(struct btrfs_root *root, u64 *objectid)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_key found_key;
struct btrfs_key key;
int ret = -1;
int i;
+ btrfs_init_path(&path);
key.objectid = 0;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = 0;
-
- path = btrfs_alloc_path();
- if (!path) {
- fprintf(stderr, "Ran out of memory\n");
- return ret;
- }
-
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0) {
fprintf(stderr, "Error searching %d\n", ret);
goto out;
}
- if (!path->nodes[0]) {
+ if (!path.nodes[0]) {
fprintf(stderr, "No leaf!\n");
goto out;
}
again:
- for (i = path->slots[0];
- i < btrfs_header_nritems(path->nodes[0]); i++) {
- btrfs_item_key_to_cpu(path->nodes[0], &found_key, i);
+ for (i = path.slots[0];
+ i < btrfs_header_nritems(path.nodes[0]); i++) {
+ btrfs_item_key_to_cpu(path.nodes[0], &found_key, i);
if (found_key.type != key.type)
continue;
@@ -1357,7 +1322,7 @@ again:
goto out;
}
do {
- ret = next_leaf(root, path);
+ ret = next_leaf(root, &path);
if (ret < 0) {
fprintf(stderr, "Error getting next leaf %d\n",
ret);
@@ -1366,12 +1331,12 @@ again:
fprintf(stderr, "No more leaves\n");
goto out;
}
- } while (!path->nodes[0]);
- if (path->nodes[0])
+ } while (!path.nodes[0]);
+ if (path.nodes[0])
goto again;
printf("Couldn't find a dir index item\n");
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -1380,9 +1345,9 @@ const char * const cmd_restore_usage[] = {
"Try to restore files from a damaged filesystem (unmounted)",
"",
"-s|--snapshots get snapshots",
- "-x|--xattr get extended attributes",
+ "-x|--xattr restore extended attributes",
"-m|--metadata restore owner, mode and times",
- "-S|--symlinks restore symbolic links",
+ "-S|--symlink restore symbolic links",
"-v|--verbose verbose",
"-i|--ignore-errors ignore errors",
"-o|--overwrite overwrite",
@@ -1421,8 +1386,10 @@ int cmd_restore(int argc, char **argv)
while (1) {
int opt;
+ enum { GETOPT_VAL_PATH_REGEX = 256 };
static const struct option long_options[] = {
- { "path-regex", required_argument, NULL, 256},
+ { "path-regex", required_argument, NULL,
+ GETOPT_VAL_PATH_REGEX },
{ "dry-run", no_argument, NULL, 'D'},
{ "metadata", no_argument, NULL, 'm'},
{ "symlinks", no_argument, NULL, 'S'},
@@ -1495,8 +1462,7 @@ int cmd_restore(int argc, char **argv)
case 'c':
match_cflags |= REG_ICASE;
break;
- /* long option without single letter alternative */
- case 256:
+ case GETOPT_VAL_PATH_REGEX:
match_regstr = optarg;
break;
case 'x':
diff --git a/cmds-scrub.c b/cmds-scrub.c
index c03bc5fb..2cf7f308 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -481,7 +481,10 @@ static struct scrub_file_record **scrub_read_file(int fd, int report_errors)
again:
old_avail = avail - i;
- BUG_ON(old_avail < 0);
+ if (old_avail < 0) {
+ error("scrub record file corrupted near byte %d", i);
+ return ERR_PTR(-EINVAL);
+ }
if (old_avail)
memmove(l, l + i, old_avail);
avail = read(fd, l + old_avail, sizeof(l) - old_avail);
@@ -650,7 +653,9 @@ skip:
} while (i < avail);
continue;
}
- BUG();
+ error("internal error: unknown parser state %d near byte %d",
+ state, i);
+ return ERR_PTR(-EINVAL);
}
goto again;
}
diff --git a/cmds-send.c b/cmds-send.c
index 74d01287..cec11e6b 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -44,6 +44,8 @@
#include "send.h"
#include "send-utils.h"
+#define SEND_BUFFER_SIZE (64 * 1024)
+
/*
* Default is 1 for historical reasons, changing may break scripts that expect
* the 'At subvol' message.
@@ -62,64 +64,72 @@ struct btrfs_send {
struct subvol_uuid_search sus;
};
-static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
+static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id)
{
struct subvol_info *si;
- si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
+ si = subvol_uuid_search(&sctx->sus, 0, NULL, 0, path,
subvol_search_by_path);
- if (!si)
- return -ENOENT;
+ if (IS_ERR_OR_NULL(si)) {
+ if (!si)
+ return -ENOENT;
+ else
+ return PTR_ERR(si);
+ }
*root_id = si->root_id;
free(si->path);
free(si);
return 0;
}
-static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
+static struct subvol_info *get_parent(struct btrfs_send *sctx, u64 root_id)
{
struct subvol_info *si_tmp;
struct subvol_info *si;
- si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
+ si_tmp = subvol_uuid_search(&sctx->sus, root_id, NULL, 0, NULL,
subvol_search_by_root_id);
- if (!si_tmp)
- return NULL;
+ if (IS_ERR_OR_NULL(si_tmp))
+ return si_tmp;
- si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
+ si = subvol_uuid_search(&sctx->sus, 0, si_tmp->parent_uuid, 0, NULL,
subvol_search_by_uuid);
free(si_tmp->path);
free(si_tmp);
return si;
}
-static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
+static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found)
{
int ret;
struct subvol_info *parent = NULL;
struct subvol_info *parent2 = NULL;
struct subvol_info *best_parent = NULL;
- __s64 tmp;
u64 best_diff = (u64)-1;
int i;
- parent = get_parent(s, root_id);
- if (!parent) {
- ret = -ENOENT;
+ parent = get_parent(sctx, root_id);
+ if (IS_ERR_OR_NULL(parent)) {
+ if (!parent)
+ ret = -ENOENT;
+ else
+ ret = PTR_ERR(parent);
goto out;
}
- for (i = 0; i < s->clone_sources_count; i++) {
- if (s->clone_sources[i] == parent->root_id) {
+ for (i = 0; i < sctx->clone_sources_count; i++) {
+ if (sctx->clone_sources[i] == parent->root_id) {
best_parent = parent;
parent = NULL;
goto out_found;
}
}
- for (i = 0; i < s->clone_sources_count; i++) {
- parent2 = get_parent(s, s->clone_sources[i]);
- if (!parent2)
+ for (i = 0; i < sctx->clone_sources_count; i++) {
+ s64 tmp;
+
+ parent2 = get_parent(sctx, sctx->clone_sources[i]);
+ if (IS_ERR_OR_NULL(parent2))
continue;
if (parent2->root_id != parent->root_id) {
free(parent2->path);
@@ -130,16 +140,19 @@ static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
free(parent2->path);
free(parent2);
- parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
- 0, NULL, subvol_search_by_root_id);
-
- if (!parent2) {
- ret = -ENOENT;
+ parent2 = subvol_uuid_search(&sctx->sus,
+ sctx->clone_sources[i], NULL, 0, NULL,
+ subvol_search_by_root_id);
+ if (IS_ERR_OR_NULL(parent2)) {
+ if (!parent2)
+ ret = -ENOENT;
+ else
+ ret = PTR_ERR(parent2);
goto out;
}
tmp = parent2->ctransid - parent->ctransid;
if (tmp < 0)
- tmp *= -1;
+ tmp = -tmp;
if (tmp < best_diff) {
if (best_parent) {
free(best_parent->path);
@@ -176,41 +189,44 @@ out:
return ret;
}
-static int add_clone_source(struct btrfs_send *s, u64 root_id)
+static int add_clone_source(struct btrfs_send *sctx, u64 root_id)
{
void *tmp;
- tmp = s->clone_sources;
- s->clone_sources = realloc(s->clone_sources,
- sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
+ tmp = sctx->clone_sources;
+ sctx->clone_sources = realloc(sctx->clone_sources,
+ sizeof(*sctx->clone_sources) * (sctx->clone_sources_count + 1));
- if (!s->clone_sources) {
+ if (!sctx->clone_sources) {
free(tmp);
return -ENOMEM;
}
- s->clone_sources[s->clone_sources_count++] = root_id;
+ sctx->clone_sources[sctx->clone_sources_count++] = root_id;
return 0;
}
-static int write_buf(int fd, const void *buf, int size)
+#if 0
+static int write_buf(int fd, const char *buf, size_t size)
{
int ret;
- int pos = 0;
+ size_t pos = 0;
while (pos < size) {
- ret = write(fd, (char*)buf + pos, size - pos);
- if (ret < 0) {
+ ssize_t wbytes;
+
+ wbytes = write(fd, buf + pos, size - pos);
+ if (wbytes < 0) {
ret = -errno;
error("failed to dump stream: %s", strerror(-ret));
goto out;
}
- if (!ret) {
+ if (!wbytes) {
ret = -EIO;
error("failed to dump stream: %s", strerror(-ret));
goto out;
}
- pos += ret;
+ pos += wbytes;
}
ret = 0;
@@ -218,40 +234,71 @@ out:
return ret;
}
-static void *dump_thread(void *arg_)
+static void* read_sent_data_copy(void *arg)
{
int ret;
- struct btrfs_send *s = (struct btrfs_send*)arg_;
- char buf[4096];
- int readed;
+ struct btrfs_send *sctx = (struct btrfs_send*)arg;
+ char buf[SEND_BUFFER_SIZE];
while (1) {
- readed = read(s->send_fd, buf, sizeof(buf));
- if (readed < 0) {
+ ssize_t rbytes;
+
+ rbytes = read(sctx->send_fd, buf, sizeof(buf));
+ if (rbytes < 0) {
ret = -errno;
- error("failed to read stream from kernel: %s\n",
+ error("failed to read stream from kernel: %s",
strerror(-ret));
goto out;
}
- if (!readed) {
+ if (!rbytes) {
ret = 0;
goto out;
}
- ret = write_buf(s->dump_fd, buf, readed);
+ ret = write_buf(sctx->dump_fd, buf, rbytes);
if (ret < 0)
goto out;
}
out:
- if (ret < 0) {
+ if (ret < 0)
exit(-ret);
+
+ return ERR_PTR(ret);
+}
+#endif
+
+static void *read_sent_data(void *arg)
+{
+ int ret;
+ struct btrfs_send *sctx = (struct btrfs_send*)arg;
+
+ while (1) {
+ ssize_t sbytes;
+
+ /* Source is a pipe, output is either file or stdout */
+ sbytes = splice(sctx->send_fd, NULL, sctx->dump_fd,
+ NULL, SEND_BUFFER_SIZE, SPLICE_F_MORE);
+ if (sbytes < 0) {
+ ret = -errno;
+ error("failed to read stream from kernel: %s",
+ strerror(-ret));
+ goto out;
+ }
+ if (!sbytes) {
+ ret = 0;
+ goto out;
+ }
}
+out:
+ if (ret < 0)
+ exit(-ret);
+
return ERR_PTR(ret);
}
static int do_send(struct btrfs_send *send, u64 parent_root_id,
- int is_first_subvol, int is_last_subvol, char *subvol,
+ int is_first_subvol, int is_last_subvol, const char *subvol,
u64 flags)
{
int ret;
@@ -280,8 +327,7 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
send->send_fd = pipefd[0];
if (!ret)
- ret = pthread_create(&t_read, NULL, dump_thread,
- send);
+ ret = pthread_create(&t_read, NULL, read_sent_data, send);
if (ret) {
ret = -ret;
error("thread setup failed: %s", strerror(-ret));
@@ -339,14 +385,14 @@ out:
return ret;
}
-static int init_root_path(struct btrfs_send *s, const char *subvol)
+static int init_root_path(struct btrfs_send *sctx, const char *subvol)
{
int ret = 0;
- if (s->root_path)
+ if (sctx->root_path)
goto out;
- ret = find_mount_root(subvol, &s->root_path);
+ ret = find_mount_root(subvol, &sctx->root_path);
if (ret < 0) {
error("failed to determine mount point for %s: %s",
subvol, strerror(-ret));
@@ -359,14 +405,14 @@ static int init_root_path(struct btrfs_send *s, const char *subvol)
goto out;
}
- s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
- if (s->mnt_fd < 0) {
+ sctx->mnt_fd = open(sctx->root_path, O_RDONLY | O_NOATIME);
+ if (sctx->mnt_fd < 0) {
ret = -errno;
- error("cannot open '%s': %s", s->root_path, strerror(-ret));
+ error("cannot open '%s': %s", sctx->root_path, strerror(-ret));
goto out;
}
- ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
+ ret = subvol_uuid_search_init(sctx->mnt_fd, &sctx->sus);
if (ret < 0) {
error("failed to initialize subvol search: %s",
strerror(-ret));
@@ -378,13 +424,13 @@ out:
}
-static int is_subvol_ro(struct btrfs_send *s, char *subvol)
+static int is_subvol_ro(struct btrfs_send *sctx, const char *subvol)
{
int ret;
u64 flags;
int fd = -1;
- fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
+ fd = openat(sctx->mnt_fd, subvol, O_RDONLY | O_NOATIME);
if (fd < 0) {
ret = -errno;
error("cannot open %s: %s", subvol, strerror(-ret));
@@ -411,6 +457,37 @@ out:
return ret;
}
+static int set_root_info(struct btrfs_send *sctx, const char *subvol,
+ u64 *root_id)
+{
+ int ret;
+
+ ret = init_root_path(sctx, subvol);
+ if (ret < 0)
+ goto out;
+
+ ret = get_root_id(sctx, subvol_strip_mountpoint(sctx->root_path, subvol),
+ root_id);
+ if (ret < 0) {
+ error("cannot resolve rootid for %s", subvol);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void free_send_info(struct btrfs_send *sctx)
+{
+ if (sctx->mnt_fd >= 0) {
+ close(sctx->mnt_fd);
+ sctx->mnt_fd = -1;
+ }
+ free(sctx->root_path);
+ sctx->root_path = NULL;
+ subvol_uuid_search_finit(&sctx->sus);
+}
+
int cmd_send(int argc, char **argv)
{
char *subvol = NULL;
@@ -460,18 +537,10 @@ int cmd_send(int argc, char **argv)
goto out;
}
- ret = init_root_path(&send, subvol);
+ ret = set_root_info(&send, subvol, &root_id);
if (ret < 0)
goto out;
- ret = get_root_id(&send,
- subvol_strip_mountpoint(send.root_path, subvol),
- &root_id);
- if (ret < 0) {
- error("cannot resolve rootid for %s", subvol);
- goto out;
- }
-
ret = is_subvol_ro(&send, subvol);
if (ret < 0)
goto out;
@@ -486,15 +555,9 @@ int cmd_send(int argc, char **argv)
error("cannot add clone source: %s", strerror(-ret));
goto out;
}
- subvol_uuid_search_finit(&send.sus);
free(subvol);
subvol = NULL;
- if (send.mnt_fd >= 0) {
- close(send.mnt_fd);
- send.mnt_fd = -1;
- }
- free(send.root_path);
- send.root_path = NULL;
+ free_send_info(&send);
full_send = 0;
break;
case 'f':
@@ -548,7 +611,20 @@ int cmd_send(int argc, char **argv)
usage(cmd_send_usage);
if (outname[0]) {
- send.dump_fd = creat(outname, 0600);
+ int tmpfd;
+
+ /*
+ * Try to use an existing file first. Even if send runs as
+ * root, it might not have permissions to create file (eg. on a
+ * NFS) but it should still be able to use a pre-created file.
+ */
+ tmpfd = open(outname, O_WRONLY | O_TRUNC);
+ if (tmpfd < 0) {
+ if (errno == ENOENT)
+ tmpfd = open(outname,
+ O_CREAT | O_WRONLY | O_TRUNC, 0600);
+ }
+ send.dump_fd = tmpfd;
if (send.dump_fd == -1) {
ret = -errno;
error("cannot create '%s': %s", outname, strerror(-ret));
@@ -564,8 +640,6 @@ int cmd_send(int argc, char **argv)
}
/* use first send subvol to determine mount_root */
- subvol = argv[optind];
-
subvol = realpath(argv[optind], NULL);
if (!subvol) {
ret = -errno;
@@ -652,7 +726,11 @@ int cmd_send(int argc, char **argv)
goto out;
}
- if (!full_send && !parent_root_id) {
+ if (!full_send && !snapshot_parent) {
+ ret = set_root_info(&send, subvol, &root_id);
+ if (ret < 0)
+ goto out;
+
ret = find_good_parent(&send, root_id, &parent_root_id);
if (ret < 0) {
error("parent determination failed for %lld",
@@ -661,15 +739,6 @@ int cmd_send(int argc, char **argv)
}
}
- ret = is_subvol_ro(&send, subvol);
- if (ret < 0)
- goto out;
- if (!ret) {
- ret = -EINVAL;
- error("subvolume %s is not read-only", subvol);
- goto out;
- }
-
if (new_end_cmd_semantic) {
/* require new kernel */
is_first_subvol = (i == optind);
@@ -684,16 +753,15 @@ int cmd_send(int argc, char **argv)
if (ret < 0)
goto out;
- if (!full_send) {
+ if (!full_send && !snapshot_parent) {
/* done with this subvol, so add it to the clone sources */
ret = add_clone_source(&send, root_id);
if (ret < 0) {
error("cannot add clone source: %s", strerror(-ret));
goto out;
}
+ free_send_info(&send);
}
-
- parent_root_id = 0;
}
ret = 0;
@@ -702,10 +770,7 @@ out:
free(subvol);
free(snapshot_parent);
free(send.clone_sources);
- if (send.mnt_fd >= 0)
- close(send.mnt_fd);
- free(send.root_path);
- subvol_uuid_search_finit(&send.sus);
+ free_send_info(&send);
return !!ret;
}
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index b3f7cbcb..7384de45 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -431,10 +431,10 @@ static int cmd_subvol_list(int argc, char **argv)
u64 top_id;
int ret = -1, uerr = 0;
char *subvol;
- int is_tab_result = 0;
int is_list_all = 0;
int is_only_in_path = 0;
DIR *dirstream = NULL;
+ enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
filter_set = btrfs_list_alloc_filter_set();
comparer_set = btrfs_list_alloc_comparer_set();
@@ -473,7 +473,7 @@ static int cmd_subvol_list(int argc, char **argv)
is_only_in_path = 1;
break;
case 't':
- is_tab_result = 1;
+ layout = BTRFS_LIST_LAYOUT_TABLE;
break;
case 's':
btrfs_list_setup_filter(&filter_set,
@@ -530,10 +530,6 @@ static int cmd_subvol_list(int argc, char **argv)
}
}
- if (flags)
- btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
- flags);
-
if (check_argc_exact(argc - optind, 1)) {
uerr = 1;
goto out;
@@ -547,11 +543,13 @@ static int cmd_subvol_list(int argc, char **argv)
goto out;
}
+ if (flags)
+ btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
+ flags);
+
ret = btrfs_list_get_path_rootid(fd, &top_id);
- if (ret) {
- error("can't get rootid for '%s'", subvol);
+ if (ret)
goto out;
- }
if (is_list_all)
btrfs_list_setup_filter(&filter_set,
@@ -568,21 +566,15 @@ static int cmd_subvol_list(int argc, char **argv)
btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
- if (is_tab_result)
- ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
- BTRFS_LIST_LAYOUT_TABLE,
- !is_list_all && !is_only_in_path, NULL);
- else
- ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
- BTRFS_LIST_LAYOUT_DEFAULT,
- !is_list_all && !is_only_in_path, NULL);
+ ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
+ layout, !is_list_all && !is_only_in_path, NULL);
out:
close_file_or_dir(fd, dirstream);
if (filter_set)
- btrfs_list_free_filter_set(filter_set);
+ free(filter_set);
if (comparer_set)
- btrfs_list_free_comparer_set(comparer_set);
+ free(comparer_set);
if (uerr)
usage(cmd_subvol_list_usage);
return !!ret;
@@ -803,7 +795,7 @@ static int cmd_subvol_get_default(int argc, char **argv)
BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
if (filter_set)
- btrfs_list_free_filter_set(filter_set);
+ free(filter_set);
out:
close_file_or_dir(fd, dirstream);
return !!ret;
@@ -929,21 +921,12 @@ static int cmd_subvol_show(int argc, char **argv)
}
ret = get_subvol_info(fullpath, &get_ri);
- if (ret == 2) {
- /*
- * Since the top level btrfs was given don't
- * take that as error
- */
- printf("%s is toplevel subvolume\n", fullpath);
- ret = 0;
- goto out;
- }
if (ret) {
if (ret < 0) {
- error("Failed to get subvol info %s: %s\n",
+ error("Failed to get subvol info %s: %s",
fullpath, strerror(-ret));
} else {
- error("Failed to get subvol info %s: %d\n",
+ error("Failed to get subvol info %s: %d",
fullpath, ret);
}
return ret;
@@ -1011,7 +994,7 @@ out:
free(get_ri.path);
free(get_ri.name);
free(get_ri.full_path);
- btrfs_list_free_filter_set(filter_set);
+ free(filter_set);
close_file_or_dir(fd, dirstream1);
free(fullpath);
@@ -1251,7 +1234,7 @@ static int cmd_subvol_sync(int argc, char **argv)
}
if (id < BTRFS_FIRST_FREE_OBJECTID
|| id > BTRFS_LAST_FREE_OBJECTID) {
- error("subvolume id %s out of range\n", arg);
+ error("subvolume id %s out of range", arg);
ret = 1;
goto out;
}
diff --git a/config.h.in b/config.h.in
index 20838444..42167c0a 100644
--- a/config.h.in
+++ b/config.h.in
@@ -27,6 +27,9 @@
/* Define to 1 if you have the `openat' function. */
#undef HAVE_OPENAT
+/* We did not define FIEMAP_EXTENT_SHARED */
+#undef HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE
+
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
diff --git a/config/config.guess b/config/config.guess
index c6fad2f5..eab94e23 100755
--- a/config/config.guess
+++ b/config/config.guess
@@ -1,8 +1,8 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright 1992-2013 Free Software Foundation, Inc.
+# Copyright 1992-2014 Free Software Foundation, Inc.
-timestamp='2013-06-10'
+timestamp='2014-11-04'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -24,12 +24,12 @@ timestamp='2013-06-10'
# program. This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
#
-# Originally written by Per Bothner.
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+# Please send patches to <config-patches@gnu.org>.
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -50,7 +50,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -149,7 +149,7 @@ Linux|GNU|GNU/*)
LIBC=gnu
#endif
EOF
- eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
;;
esac
@@ -589,8 +589,9 @@ EOF
else
IBM_ARCH=powerpc
fi
- if [ -x /usr/bin/oslevel ] ; then
- IBM_REV=`/usr/bin/oslevel`
+ if [ -x /usr/bin/lslpp ] ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
else
IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
fi
@@ -836,7 +837,7 @@ EOF
*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
- i*:MSYS*:*)
+ *:MSYS*:*)
echo ${UNAME_MACHINE}-pc-msys
exit ;;
i*:windows32*:*)
@@ -979,10 +980,10 @@ EOF
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
test x"${CPU}" != x && { echo "${CPU}-${VENDOR}-linux-${LIBC}"; exit; }
;;
- or1k:Linux:*:*)
- echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}
+ openrisc*:Linux:*:*)
+ echo or1k-${VENDOR}-linux-${LIBC}
exit ;;
- or32:Linux:*:*)
+ or32:Linux:*:* | or1k*:Linux:*:*)
echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}
exit ;;
padre:Linux:*:*)
@@ -1270,16 +1271,26 @@ EOF
if test "$UNAME_PROCESSOR" = unknown ; then
UNAME_PROCESSOR=powerpc
fi
- if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
- if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
- (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
- grep IS_64BIT_ARCH >/dev/null
- then
- case $UNAME_PROCESSOR in
- i386) UNAME_PROCESSOR=x86_64 ;;
- powerpc) UNAME_PROCESSOR=powerpc64 ;;
- esac
+ if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # Avoid executing cc on OS X 10.9, as it ships with a stub
+ # that puts up a graphical alert prompting to install
+ # developer tools. Any system running Mac OS X 10.7 or
+ # later (Darwin 11 and later) is required to have a 64-bit
+ # processor. This is not true of the ARM version of Darwin
+ # that Apple uses in portable devices.
+ UNAME_PROCESSOR=x86_64
fi
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
exit ;;
@@ -1371,154 +1382,6 @@ EOF
exit ;;
esac
-eval $set_cc_for_build
-cat >$dummy.c <<EOF
-#ifdef _SEQUENT_
-# include <sys/types.h>
-# include <sys/utsname.h>
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
- /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
- I don't know.... */
- printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include <sys/param.h>
- printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
- "4"
-#else
- ""
-#endif
- ); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
- printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
- printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
- int version;
- version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
- if (version < 4)
- printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
- else
- printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
- exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
- printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
- printf ("ns32k-encore-mach\n"); exit (0);
-#else
- printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
- printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
- printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
- printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
- struct utsname un;
-
- uname(&un);
-
- if (strncmp(un.version, "V2", 2) == 0) {
- printf ("i386-sequent-ptx2\n"); exit (0);
- }
- if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
- printf ("i386-sequent-ptx1\n"); exit (0);
- }
- printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-# include <sys/param.h>
-# if defined (BSD)
-# if BSD == 43
- printf ("vax-dec-bsd4.3\n"); exit (0);
-# else
-# if BSD == 199006
- printf ("vax-dec-bsd4.3reno\n"); exit (0);
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# endif
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# else
- printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
- printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
- exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
- { echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
- case `getsysinfo -f cpu_type` in
- c1*)
- echo c1-convex-bsd
- exit ;;
- c2*)
- if getsysinfo -f scalar_acc
- then echo c32-convex-bsd
- else echo c2-convex-bsd
- fi
- exit ;;
- c34*)
- echo c34-convex-bsd
- exit ;;
- c38*)
- echo c38-convex-bsd
- exit ;;
- c4*)
- echo c4-convex-bsd
- exit ;;
- esac
-fi
-
cat >&2 <<EOF
$0: unable to guess system type
diff --git a/config/config.sub b/config/config.sub
index 8b612ab8..7ffe3737 100755
--- a/config/config.sub
+++ b/config/config.sub
@@ -1,8 +1,8 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright 1992-2013 Free Software Foundation, Inc.
+# Copyright 1992-2014 Free Software Foundation, Inc.
-timestamp='2013-04-24'
+timestamp='2014-12-03'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@ timestamp='2013-04-24'
# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+# Please send patches to <config-patches@gnu.org>.
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
@@ -68,7 +68,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -257,7 +257,7 @@ case $basic_machine in
| avr | avr32 \
| be32 | be64 \
| bfin \
- | c4x | clipper \
+ | c4x | c8051 | clipper \
| d10v | d30v | dlx | dsp16xx \
| epiphany \
| fido | fr30 | frv \
@@ -265,6 +265,7 @@ case $basic_machine in
| hexagon \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
+ | k1om \
| le32 | le64 \
| lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
@@ -282,8 +283,10 @@ case $basic_machine in
| mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
| mipsisa64 | mipsisa64el \
| mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
| mipsr5900 | mipsr5900el \
@@ -295,11 +298,11 @@ case $basic_machine in
| nds32 | nds32le | nds32be \
| nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
- | open8 \
- | or1k | or32 \
+ | open8 | or1k | or1knd | or32 \
| pdp10 | pdp11 | pj | pjl \
| powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
+ | riscv32 | riscv64 \
| rl78 | rx \
| score \
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
@@ -310,6 +313,7 @@ case $basic_machine in
| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
| ubicom32 \
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+ | visium \
| we32k \
| x86 | xc16x | xstormy16 | xtensa \
| z8k | z80)
@@ -324,7 +328,10 @@ case $basic_machine in
c6x)
basic_machine=tic6x-unknown
;;
- m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+ leon|leon[3-9])
+ basic_machine=sparc-$basic_machine
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
@@ -372,7 +379,7 @@ case $basic_machine in
| be32-* | be64-* \
| bfin-* | bs2000-* \
| c[123]* | c30-* | [cjt]90-* | c4x-* \
- | clipper-* | craynv-* | cydra-* \
+ | c8051-* | clipper-* | craynv-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
| elxsi-* \
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
@@ -381,6 +388,7 @@ case $basic_machine in
| hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
+ | k1om-* \
| le32-* | le64-* \
| lm32-* \
| m32c-* | m32r-* | m32rle-* \
@@ -400,8 +408,10 @@ case $basic_machine in
| mips64vr5900-* | mips64vr5900el-* \
| mipsisa32-* | mipsisa32el-* \
| mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa32r6-* | mipsisa32r6el-* \
| mipsisa64-* | mipsisa64el-* \
| mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64r6-* | mipsisa64r6el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
| mipsr5900-* | mipsr5900el-* \
@@ -413,6 +423,7 @@ case $basic_machine in
| nios-* | nios2-* | nios2eb-* | nios2el-* \
| none-* | np1-* | ns16k-* | ns32k-* \
| open8-* \
+ | or1k*-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
@@ -430,6 +441,7 @@ case $basic_machine in
| ubicom32-* \
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
| vax-* \
+ | visium-* \
| we32k-* \
| x86-* | x86_64-* | xc16x-* | xps100-* \
| xstormy16-* | xtensa*-* \
@@ -767,6 +779,9 @@ case $basic_machine in
basic_machine=m68k-isi
os=-sysv
;;
+ leon-*|leon[3-9]-*)
+ basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+ ;;
m68knommu)
basic_machine=m68k-unknown
os=-linux
@@ -794,7 +809,7 @@ case $basic_machine in
os=-mingw64
;;
mingw32)
- basic_machine=i386-pc
+ basic_machine=i686-pc
os=-mingw32
;;
mingw32ce)
@@ -822,6 +837,10 @@ case $basic_machine in
basic_machine=powerpc-unknown
os=-morphos
;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ os=-moxiebox
+ ;;
msdos)
basic_machine=i386-pc
os=-msdos
@@ -830,7 +849,7 @@ case $basic_machine in
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
msys)
- basic_machine=i386-pc
+ basic_machine=i686-pc
os=-msys
;;
mvs)
@@ -1367,14 +1386,14 @@ case $os in
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
- | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1546,6 +1565,9 @@ case $basic_machine in
c4x-* | tic4x-*)
os=-coff
;;
+ c8051-*)
+ os=-elf
+ ;;
hexagon-*)
os=-elf
;;
@@ -1589,9 +1611,6 @@ case $basic_machine in
mips*-*)
os=-elf
;;
- or1k-*)
- os=-elf
- ;;
or32-*)
os=-coff
;;
diff --git a/config/install-sh b/config/install-sh
index 377bb868..0b0fdcbb 100755
--- a/config/install-sh
+++ b/config/install-sh
@@ -1,7 +1,7 @@
#!/bin/sh
# install - install a program, script, or datafile
-scriptversion=2011-11-20.07; # UTC
+scriptversion=2013-12-25.23; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
@@ -41,19 +41,15 @@ scriptversion=2011-11-20.07; # UTC
# This script is compatible with the BSD install script, but was written
# from scratch.
+tab=' '
nl='
'
-IFS=" "" $nl"
+IFS=" $tab$nl"
-# set DOITPROG to echo to test this script
+# Set DOITPROG to "echo" to test this script.
-# Don't use :- since 4.3BSD and earlier shells don't like it.
doit=${DOITPROG-}
-if test -z "$doit"; then
- doit_exec=exec
-else
- doit_exec=$doit
-fi
+doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
@@ -68,17 +64,6 @@ mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
-posix_glob='?'
-initialize_posix_glob='
- test "$posix_glob" != "?" || {
- if (set -f) 2>/dev/null; then
- posix_glob=
- else
- posix_glob=:
- fi
- }
-'
-
posix_mkdir=
# Desired mode of installed file.
@@ -97,7 +82,7 @@ dir_arg=
dst_arg=
copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -137,46 +122,57 @@ while test $# -ne 0; do
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
- shift;;
+ shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
- case $mode in
- *' '* | *' '* | *'
-'* | *'*'* | *'?'* | *'['*)
- echo "$0: invalid mode: $mode" >&2
- exit 1;;
- esac
- shift;;
+ case $mode in
+ *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
-o) chowncmd="$chownprog $2"
- shift;;
+ shift;;
-s) stripcmd=$stripprog;;
- -t) dst_arg=$2
- # Protect names problematic for 'test' and other utilities.
- case $dst_arg in
- -* | [=\(\)!]) dst_arg=./$dst_arg;;
- esac
- shift;;
+ -t)
+ is_target_a_directory=always
+ dst_arg=$2
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ shift;;
- -T) no_target_directory=true;;
+ -T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
- --) shift
- break;;
+ --) shift
+ break;;
- -*) echo "$0: invalid option: $1" >&2
- exit 1;;
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
*) break;;
esac
shift
done
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+ if test -n "$dst_arg"; then
+ echo "$0: target directory not allowed when installing a directory." >&2
+ exit 1
+ fi
+fi
+
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
@@ -208,6 +204,15 @@ if test $# -eq 0; then
fi
if test -z "$dir_arg"; then
+ if test $# -gt 1 || test "$is_target_a_directory" = always; then
+ if test ! -d "$dst_arg"; then
+ echo "$0: $dst_arg: Is not a directory." >&2
+ exit 1
+ fi
+ fi
+fi
+
+if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
@@ -223,16 +228,16 @@ if test -z "$dir_arg"; then
*[0-7])
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw='% 200'
+ u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw=,u+rw
+ u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
@@ -269,41 +274,15 @@ do
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
if test -d "$dst"; then
- if test -n "$no_target_directory"; then
- echo "$0: $dst_arg: Is a directory" >&2
- exit 1
+ if test "$is_target_a_directory" = never; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstdir_status=0
else
- # Prefer dirname, but fall back on a substitute if dirname fails.
- dstdir=`
- (dirname "$dst") 2>/dev/null ||
- expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$dst" : 'X\(//\)[^/]' \| \
- X"$dst" : 'X\(//\)$' \| \
- X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
- echo X"$dst" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
- s//\1/
- q
- }
- /^X\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'
- `
-
+ dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
@@ -314,74 +293,74 @@ do
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
- # Create intermediate dirs using mode 755 as modified by the umask.
- # This is like FreeBSD 'install' as of 1997-10-28.
- umask=`umask`
- case $stripcmd.$umask in
- # Optimize common cases.
- *[2367][2367]) mkdir_umask=$umask;;
- .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
- *[0-7])
- mkdir_umask=`expr $umask + 22 \
- - $umask % 100 % 40 + $umask % 20 \
- - $umask % 10 % 4 + $umask % 2
- `;;
- *) mkdir_umask=$umask,go-w;;
- esac
-
- # With -d, create the new directory with the user-specified mode.
- # Otherwise, rely on $mkdir_umask.
- if test -n "$dir_arg"; then
- mkdir_mode=-m$mode
- else
- mkdir_mode=
- fi
-
- posix_mkdir=false
- case $umask in
- *[123567][0-7][0-7])
- # POSIX mkdir -p sets u+wx bits regardless of umask, which
- # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
- ;;
- *)
- tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
- trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
- if (umask $mkdir_umask &&
- exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
- then
- if test -z "$dir_arg" || {
- # Check for POSIX incompatibilities with -m.
- # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
- # other-writable bit of parent directory when it shouldn't.
- # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
- ls_ld_tmpdir=`ls -ld "$tmpdir"`
- case $ls_ld_tmpdir in
- d????-?r-*) different_mode=700;;
- d????-?--*) different_mode=755;;
- *) false;;
- esac &&
- $mkdirprog -m$different_mode -p -- "$tmpdir" && {
- ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
- test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
- }
- }
- then posix_mkdir=:
- fi
- rmdir "$tmpdir/d" "$tmpdir"
- else
- # Remove any dirs left behind by ancient mkdir implementations.
- rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
- fi
- trap '' 0;;
- esac;;
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ if (umask $mkdir_umask &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ ls_ld_tmpdir=`ls -ld "$tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/d" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
esac
if
$posix_mkdir && (
- umask $mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
@@ -391,53 +370,51 @@ do
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
- /*) prefix='/';;
- [-=\(\)!]*) prefix='./';;
- *) prefix='';;
+ /*) prefix='/';;
+ [-=\(\)!]*) prefix='./';;
+ *) prefix='';;
esac
- eval "$initialize_posix_glob"
-
oIFS=$IFS
IFS=/
- $posix_glob set -f
+ set -f
set fnord $dstdir
shift
- $posix_glob set +f
+ set +f
IFS=$oIFS
prefixes=
for d
do
- test X"$d" = X && continue
-
- prefix=$prefix$d
- if test -d "$prefix"; then
- prefixes=
- else
- if $posix_mkdir; then
- (umask=$mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
- # Don't fail if two instances are running concurrently.
- test -d "$prefix" || exit 1
- else
- case $prefix in
- *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
- *) qprefix=$prefix;;
- esac
- prefixes="$prefixes '$qprefix'"
- fi
- fi
- prefix=$prefix/
+ test X"$d" = X && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
done
if test -n "$prefixes"; then
- # Don't fail if two instances are running concurrently.
- (umask $mkdir_umask &&
- eval "\$doit_exec \$mkdirprog $prefixes") ||
- test -d "$dstdir" || exit 1
- obsolete_mkdir_used=true
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
fi
fi
fi
@@ -472,15 +449,12 @@ do
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
- old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
- new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
-
- eval "$initialize_posix_glob" &&
- $posix_glob set -f &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+ set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
- $posix_glob set +f &&
-
+ set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
@@ -493,24 +467,24 @@ do
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
- # Now remove or move aside any old file at destination location.
- # We try this two ways since rm can't unlink itself on some
- # systems and the destination file might be busy for other
- # reasons. In this case, the final cleanup might fail but the new
- # file should still install successfully.
- {
- test ! -f "$dst" ||
- $doit $rmcmd -f "$dst" 2>/dev/null ||
- { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
- { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
- } ||
- { echo "$0: cannot unlink or rename $dst" >&2
- (exit 1); exit 1
- }
- } &&
-
- # Now rename the file to the real destination.
- $doit $mvcmd "$dsttmp" "$dst"
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
diff --git a/configure b/configure
index bca05c94..cfff29d1 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.7.3.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.9.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.7.3'
-PACKAGE_STRING='btrfs-progs v4.7.3'
+PACKAGE_VERSION='v4.9.1'
+PACKAGE_STRING='btrfs-progs v4.9.1'
PACKAGE_BUGREPORT='linux-btrfs@vger.kernel.org'
PACKAGE_URL='http://btrfs.wiki.kernel.org'
@@ -1290,7 +1290,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.7.3 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.9.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1355,7 +1355,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of btrfs-progs v4.7.3:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.9.1:";;
esac
cat <<\_ACEOF
@@ -1471,7 +1471,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-btrfs-progs configure v4.7.3
+btrfs-progs configure v4.9.1
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1840,7 +1840,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.7.3, which was
+It was created by btrfs-progs $as_me v4.9.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2197,7 +2197,7 @@ fi
LIBBTRFS_MAJOR=0
LIBBTRFS_MINOR=1
-LIBBTRFS_PATCHLEVEL=1
+LIBBTRFS_PATCHLEVEL=2
CFLAGS=${CFLAGS:-"-g -O1 -Wall -D_FORTIFY_SOURCE=2"}
@@ -5755,6 +5755,7 @@ if test "$DISABLE_BTRFSCONVERT" = 0 && test "x$convertfs" = "x"; then
as_fn_error $? "no filesystems for convert, use --disable-convert instead" "$LINENO" 5
fi
+HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE=0
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for FIEMAP_EXTENT_SHARED defined in linux/fiemap.h" >&5
$as_echo_n "checking for FIEMAP_EXTENT_SHARED defined in linux/fiemap.h... " >&6; }
@@ -5790,7 +5791,19 @@ $as_echo "$ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h" >&6; }
if test $ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h != "no"; then :
else
- as_fn_error $? "no definition of FIEMAP_EXTENT_SHARED found" "$LINENO" 5
+ HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE=1
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no definition of FIEMAP_EXTENT_SHARED found, probably old kernel, will use own defintion, 'btrfs fi du' might report wrong numbers" >&5
+$as_echo "$as_me: WARNING: no definition of FIEMAP_EXTENT_SHARED found, probably old kernel, will use own defintion, 'btrfs fi du' might report wrong numbers" >&2;}
+fi
+
+if test "x$HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE" == "x1"; then
+
+$as_echo "#define HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE 1" >>confdefs.h
+
+else
+
+$as_echo "#define HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE 0" >>confdefs.h
+
fi
@@ -6684,7 +6697,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.7.3, which was
+This file was extended by btrfs-progs $as_me v4.9.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6747,7 +6760,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.7.3
+btrfs-progs config.status v4.9.1
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index 8fd8f425..b08bfe60 100644
--- a/configure.ac
+++ b/configure.ac
@@ -10,7 +10,7 @@ fi
dnl library version
LIBBTRFS_MAJOR=0
LIBBTRFS_MINOR=1
-LIBBTRFS_PATCHLEVEL=1
+LIBBTRFS_PATCHLEVEL=2
CFLAGS=${CFLAGS:-"-g -O1 -Wall -D_FORTIFY_SOURCE=2"}
AC_SUBST([CFLAGS])
@@ -144,8 +144,16 @@ if test "$DISABLE_BTRFSCONVERT" = 0 && test "x$convertfs" = "x"; then
AC_MSG_ERROR([no filesystems for convert, use --disable-convert instead])
fi
+HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE=0
AX_CHECK_DEFINE([linux/fiemap.h], [FIEMAP_EXTENT_SHARED], [],
- [AC_MSG_ERROR([no definition of FIEMAP_EXTENT_SHARED found])])
+ [HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE=1
+ AC_MSG_WARN([no definition of FIEMAP_EXTENT_SHARED found, probably old kernel, will use own defintion, 'btrfs fi du' might report wrong numbers])])
+
+if test "x$HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE" == "x1"; then
+AC_DEFINE([HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE], [1], [We defined FIEMAP_EXTENT_SHARED])
+else
+AC_DEFINE([HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE], [0], [We did not define FIEMAP_EXTENT_SHARED])
+fi
dnl Define <NAME>_LIBS= and <NAME>_CFLAGS= by pkg-config
dnl
diff --git a/btrfs-convert.c b/convert/main.c
index 5175f1f7..8d9f29fa 100644
--- a/btrfs-convert.c
+++ b/convert/main.c
@@ -103,6 +103,7 @@ struct btrfs_convert_operations {
struct btrfs_root *root, int datacsum,
int packing, int noxattr, struct task_ctx *p);
void (*close_fs)(struct btrfs_convert_context *cctx);
+ int (*check_state)(struct btrfs_convert_context *cctx);
};
static void init_convert_context(struct btrfs_convert_context *cctx)
@@ -132,6 +133,11 @@ static inline void convert_close_fs(struct btrfs_convert_context *cctx)
cctx->convert_ops->close_fs(cctx);
}
+static inline int convert_check_state(struct btrfs_convert_context *cctx)
+{
+ return cctx->convert_ops->check_state(cctx);
+}
+
static int intersect_with_sb(u64 bytenr, u64 num_bytes)
{
int i;
@@ -277,7 +283,7 @@ static int record_file_blocks(struct blk_iterate_data *data,
int ret = 0;
struct btrfs_root *root = data->root;
struct btrfs_root *convert_root = data->convert_root;
- struct btrfs_path *path;
+ struct btrfs_path path;
u64 file_pos = file_block * root->sectorsize;
u64 old_disk_bytenr = disk_block * root->sectorsize;
u64 num_bytes = num_blocks * root->sectorsize;
@@ -289,9 +295,7 @@ static int record_file_blocks(struct blk_iterate_data *data,
data->objectid, data->inode, file_pos, 0,
num_bytes);
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ btrfs_init_path(&path);
/*
* Search real disk bytenr from convert root
@@ -310,11 +314,11 @@ static int record_file_blocks(struct blk_iterate_data *data,
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = cur_off;
- ret = btrfs_search_slot(NULL, convert_root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, convert_root, &key, &path, 0, 0);
if (ret < 0)
break;
if (ret > 0) {
- ret = btrfs_previous_item(convert_root, path,
+ ret = btrfs_previous_item(convert_root, &path,
data->convert_ino,
BTRFS_EXTENT_DATA_KEY);
if (ret < 0)
@@ -324,17 +328,17 @@ static int record_file_blocks(struct blk_iterate_data *data,
break;
}
}
- node = path->nodes[0];
- slot = path->slots[0];
+ node = path.nodes[0];
+ slot = path.slots[0];
btrfs_item_key_to_cpu(node, &key, slot);
BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY ||
key.objectid != data->convert_ino ||
key.offset > cur_off);
fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
- extent_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
+ extent_num_bytes = btrfs_file_extent_num_bytes(node, fi);
BUG_ON(cur_off - key.offset >= extent_num_bytes);
- btrfs_release_path(path);
+ btrfs_release_path(&path);
if (extent_disk_bytenr)
real_disk_bytenr = cur_off - key.offset +
@@ -357,7 +361,7 @@ static int record_file_blocks(struct blk_iterate_data *data,
* need to waste CPU cycles now.
*/
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -424,8 +428,16 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
int i;
int ret;
- BUG_ON(bytenr != round_down(bytenr, root->sectorsize));
- BUG_ON(len != round_down(len, root->sectorsize));
+ if (bytenr != round_down(bytenr, root->sectorsize)) {
+ error("bytenr not sectorsize aligned: %llu",
+ (unsigned long long)bytenr);
+ return -EINVAL;
+ }
+ if (len != round_down(len, root->sectorsize)) {
+ error("length not sectorsize aligned: %llu",
+ (unsigned long long)len);
+ return -EINVAL;
+ }
len = min_t(u64, len, BTRFS_MAX_EXTENT_SIZE);
/*
@@ -514,7 +526,11 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
bg_cache->key.offset - bytenr);
}
- BUG_ON(len != round_down(len, root->sectorsize));
+ if (len != round_down(len, root->sectorsize)) {
+ error("remaining length not sectorsize aligned: %llu",
+ (unsigned long long)len);
+ return -EINVAL;
+ }
ret = btrfs_record_file_extent(trans, root, ino, inode, bytenr,
disk_bytenr, len);
if (ret < 0)
@@ -952,7 +968,7 @@ static int create_image(struct btrfs_root *root,
{
struct btrfs_inode_item buf;
struct btrfs_trans_handle *trans;
- struct btrfs_path *path = NULL;
+ struct btrfs_path path;
struct btrfs_key key;
struct cache_extent *cache;
struct cache_tree used_tmp;
@@ -969,6 +985,7 @@ static int create_image(struct btrfs_root *root,
return -ENOMEM;
cache_tree_init(&used_tmp);
+ btrfs_init_path(&path);
ret = btrfs_find_free_objectid(trans, root, BTRFS_FIRST_FREE_OBJECTID,
&ino);
@@ -985,24 +1002,19 @@ static int create_image(struct btrfs_root *root,
if (ret < 0)
goto out;
- path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
key.objectid = ino;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
if (ret) {
ret = (ret > 0 ? -ENOENT : ret);
goto out;
}
- read_extent_buffer(path->nodes[0], &buf,
- btrfs_item_ptr_offset(path->nodes[0], path->slots[0]),
+ read_extent_buffer(path.nodes[0], &buf,
+ btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
sizeof(buf));
- btrfs_release_path(path);
+ btrfs_release_path(&path);
/*
* Create a new used space cache, which doesn't contain the reserved
@@ -1040,18 +1052,18 @@ static int create_image(struct btrfs_root *root,
key.objectid = ino;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
if (ret) {
ret = (ret > 0 ? -ENOENT : ret);
goto out;
}
btrfs_set_stack_inode_size(&buf, cfg->num_bytes);
- write_extent_buffer(path->nodes[0], &buf,
- btrfs_item_ptr_offset(path->nodes[0], path->slots[0]),
+ write_extent_buffer(path.nodes[0], &buf,
+ btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
sizeof(buf));
out:
free_extent_cache_tree(&used_tmp);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
btrfs_commit_transaction(trans, root);
return ret;
}
@@ -1063,7 +1075,7 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root,
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_root *new_root = NULL;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_inode_item *inode_item;
struct extent_buffer *leaf;
struct btrfs_key key;
@@ -1078,28 +1090,25 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root,
if (len == 0 || len > BTRFS_NAME_LEN)
return NULL;
- path = btrfs_alloc_path();
- if (!path)
- return NULL;
-
+ btrfs_init_path(&path);
key.objectid = dirid;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = (u64)-1;
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret <= 0) {
error("search for DIR_INDEX dirid %llu failed: %d",
(unsigned long long)dirid, ret);
goto fail;
}
- if (path->slots[0] > 0) {
- path->slots[0]--;
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ if (path.slots[0] > 0) {
+ path.slots[0]--;
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY)
index = key.offset + 1;
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
trans = btrfs_start_transaction(root, 1);
if (!trans) {
@@ -1111,14 +1120,14 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root,
key.offset = 0;
key.type = BTRFS_INODE_ITEM_KEY;
- ret = btrfs_lookup_inode(trans, root, path, &key, 1);
+ ret = btrfs_lookup_inode(trans, root, &path, &key, 1);
if (ret) {
error("search for INODE_ITEM %llu failed: %d",
(unsigned long long)dirid, ret);
goto fail;
}
- leaf = path->nodes[0];
- inode_item = btrfs_item_ptr(leaf, path->slots[0],
+ leaf = path.nodes[0];
+ inode_item = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_inode_item);
key.objectid = root_objectid;
@@ -1143,7 +1152,7 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root,
btrfs_set_inode_size(leaf, inode_item, len * 2 +
btrfs_inode_size(leaf, inode_item));
btrfs_mark_buffer_dirty(leaf);
- btrfs_release_path(path);
+ btrfs_release_path(&path);
/* add the backref first */
ret = btrfs_add_root_ref(trans, tree_root, root_objectid,
@@ -1178,7 +1187,7 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root,
new_root = NULL;
}
fail:
- btrfs_free_path(path);
+ btrfs_init_path(&path);
return new_root;
}
@@ -1351,7 +1360,7 @@ err:
/*
* Migrate super block to its default position and zero 0 ~ 16k
*/
-static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize)
+static int migrate_super_block(int fd, u64 old_bytenr)
{
int ret;
struct extent_buffer *buf;
@@ -1359,13 +1368,13 @@ static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize)
u32 len;
u32 bytenr;
- buf = malloc(sizeof(*buf) + sectorsize);
+ buf = malloc(sizeof(*buf) + BTRFS_SUPER_INFO_SIZE);
if (!buf)
return -ENOMEM;
- buf->len = sectorsize;
- ret = pread(fd, buf->data, sectorsize, old_bytenr);
- if (ret != sectorsize)
+ buf->len = BTRFS_SUPER_INFO_SIZE;
+ ret = pread(fd, buf->data, BTRFS_SUPER_INFO_SIZE, old_bytenr);
+ if (ret != BTRFS_SUPER_INFO_SIZE)
goto fail;
super = (struct btrfs_super_block *)buf->data;
@@ -1373,19 +1382,20 @@ static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize)
btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET);
- if (ret != sectorsize)
+ ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE,
+ BTRFS_SUPER_INFO_OFFSET);
+ if (ret != BTRFS_SUPER_INFO_SIZE)
goto fail;
ret = fsync(fd);
if (ret)
goto fail;
- memset(buf->data, 0, sectorsize);
+ memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
for (bytenr = 0; bytenr < BTRFS_SUPER_INFO_OFFSET; ) {
len = BTRFS_SUPER_INFO_OFFSET - bytenr;
- if (len > sectorsize)
- len = sectorsize;
+ if (len > BTRFS_SUPER_INFO_SIZE)
+ len = BTRFS_SUPER_INFO_SIZE;
ret = pwrite(fd, buf->data, len, bytenr);
if (ret != len) {
fprintf(stderr, "unable to zero fill device\n");
@@ -1487,7 +1497,7 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
if (!(ext2_fs->super->s_feature_incompat &
EXT2_FEATURE_INCOMPAT_FILETYPE)) {
- fprintf(stderr, "filetype feature is missing\n");
+ error("filetype feature is missing");
goto fail;
}
@@ -1516,6 +1526,9 @@ static int __ext2_add_one_block(ext2_filsys fs, char *bitmap,
offset /= EXT2FS_CLUSTER_RATIO(fs);
offset += group_nr * EXT2_CLUSTERS_PER_GROUP(fs->super);
for (i = 0; i < EXT2_CLUSTERS_PER_GROUP(fs->super); i++) {
+ if ((i + offset) >= ext2fs_blocks_count(fs->super))
+ break;
+
if (ext2fs_test_bit(i, bitmap)) {
u64 start;
@@ -2171,6 +2184,42 @@ static void ext2_copy_inode_item(struct btrfs_inode_item *dst,
}
memset(&dst->reserved, 0, sizeof(dst->reserved));
}
+static int ext2_check_state(struct btrfs_convert_context *cctx)
+{
+ ext2_filsys fs = cctx->fs_data;
+
+ if (!(fs->super->s_state & EXT2_VALID_FS))
+ return 1;
+ else if (fs->super->s_state & EXT2_ERROR_FS)
+ return 1;
+ else
+ return 0;
+}
+
+/* EXT2_*_FL to BTRFS_INODE_FLAG_* stringification helper */
+#define COPY_ONE_EXT2_FLAG(flags, ext2_inode, name) ({ \
+ if (ext2_inode->i_flags & EXT2_##name##_FL) \
+ flags |= BTRFS_INODE_##name; \
+})
+
+/*
+ * Convert EXT2_*_FL to corresponding BTRFS_INODE_* flags
+ *
+ * Only a subset of EXT_*_FL is supported in btrfs.
+ */
+static void ext2_convert_inode_flags(struct btrfs_inode_item *dst,
+ struct ext2_inode *src)
+{
+ u64 flags = 0;
+
+ COPY_ONE_EXT2_FLAG(flags, src, APPEND);
+ COPY_ONE_EXT2_FLAG(flags, src, SYNC);
+ COPY_ONE_EXT2_FLAG(flags, src, IMMUTABLE);
+ COPY_ONE_EXT2_FLAG(flags, src, NODUMP);
+ COPY_ONE_EXT2_FLAG(flags, src, NOATIME);
+ COPY_ONE_EXT2_FLAG(flags, src, DIRSYNC);
+ btrfs_set_stack_inode_flags(dst, flags);
+}
/*
* copy a single inode. do all the required works, such as cloning
@@ -2194,6 +2243,7 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans,
BTRFS_INODE_NODATASUM;
btrfs_set_stack_inode_flags(&btrfs_inode, flags);
}
+ ext2_convert_inode_flags(&btrfs_inode, ext2_inode);
switch (ext2_inode->i_mode & S_IFMT) {
case S_IFREG:
@@ -2289,6 +2339,7 @@ static const struct btrfs_convert_operations ext2_convert_ops = {
.read_used_space = ext2_read_used_space,
.copy_inodes = ext2_copy_inodes,
.close_fs = ext2_close_fs,
+ .check_state = ext2_check_state,
};
#endif
@@ -2315,7 +2366,7 @@ static int convert_open_fs(const char *devname,
}
}
- fprintf(stderr, "No file system found to convert.\n");
+ error("no file system found to convert");
return -1;
}
@@ -2340,6 +2391,10 @@ static int do_convert(const char *devname, int datacsum, int packing,
ret = convert_open_fs(devname, &cctx);
if (ret)
goto fail;
+ ret = convert_check_state(&cctx);
+ if (ret)
+ warning(
+ "source filesystem is not clean, running filesystem check is recommended");
ret = convert_read_used_space(&cctx);
if (ret)
goto fail;
@@ -2376,7 +2431,7 @@ static int do_convert(const char *devname, int datacsum, int packing,
memset(mkfs_cfg.chunk_uuid, 0, BTRFS_UUID_UNPARSED_SIZE);
memset(mkfs_cfg.fs_uuid, 0, BTRFS_UUID_UNPARSED_SIZE);
- ret = make_btrfs(fd, &mkfs_cfg, &cctx);
+ ret = make_convert_btrfs(fd, &mkfs_cfg, &cctx);
if (ret) {
error("unable to create initial ctree: %s", strerror(-ret));
goto fail;
@@ -2465,7 +2520,7 @@ static int do_convert(const char *devname, int datacsum, int packing,
* If this step succeed, we get a mountable btrfs. Otherwise
* the source fs is left unchanged.
*/
- ret = migrate_super_block(fd, mkfs_cfg.super_bytenr, blocksize);
+ ret = migrate_super_block(fd, mkfs_cfg.super_bytenr);
if (ret) {
error("unable to migrate super block: %d", ret);
goto fail;
@@ -2586,7 +2641,7 @@ static int may_rollback(struct btrfs_root *root)
num_stripes = multi->num_stripes;
physical = multi->stripes[0].physical;
- kfree(multi);
+ free(multi);
if (num_stripes != 1) {
error("num stripes for bytenr %llu is not 1", bytenr);
@@ -2727,7 +2782,7 @@ static int do_rollback(const char *devname)
key.objectid = objectid;
key.offset = 0;
- btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
+ key.type = BTRFS_EXTENT_DATA_KEY;
ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0);
if (ret != 0) {
error("unable to find first file extent: %d", ret);
@@ -2747,7 +2802,7 @@ static int do_rollback(const char *devname)
btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.objectid != objectid || key.offset != offset ||
- btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
+ key.type != BTRFS_EXTENT_DATA_KEY)
break;
fi = btrfs_item_ptr(leaf, path.slots[0],
@@ -2810,7 +2865,10 @@ next_extent:
/* force no allocation from system block group */
root->fs_info->system_allocs = -1;
trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
+ if (!trans) {
+ error("unable to start transaction");
+ goto fail;
+ }
/*
* recow the whole chunk tree, this will remove all chunk tree blocks
* from system block group
@@ -2859,7 +2917,10 @@ next_extent:
}
ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
+ if (ret) {
+ error("transaction commit failed: %d", ret);
+ goto fail;
+ }
ret = close_ctree(root);
if (ret) {
@@ -2977,7 +3038,7 @@ static void print_usage(void)
printf("\t-O|--features LIST comma separated list of filesystem features\n");
printf("\t--no-progress show only overview, not the detailed progress\n");
printf("\n");
- printf("Suported filesystems:\n");
+ printf("Supported filesystems:\n");
printf("\text2/3/4: %s\n", BTRFSCONVERT_EXT2 ? "yes" : "no");
}
@@ -3037,8 +3098,8 @@ int main(int argc, char *argv[])
case 'l':
copylabel = -1;
if (strlen(optarg) >= BTRFS_LABEL_SIZE) {
- fprintf(stderr,
- "WARNING: label too long, trimmed to %d bytes\n",
+ warning(
+ "label too long, trimmed to %d bytes",
BTRFS_LABEL_SIZE - 1);
}
__strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE - 1);
@@ -3055,8 +3116,7 @@ int main(int argc, char *argv[])
tmp = btrfs_parse_fs_features(tmp, &features);
if (tmp) {
- fprintf(stderr,
- "Unrecognized filesystem feature '%s'\n",
+ error("unrecognized filesystem feature: %s",
tmp);
free(orig);
exit(1);
@@ -3072,8 +3132,7 @@ int main(int argc, char *argv[])
btrfs_parse_features_to_string(buf,
features & ~BTRFS_CONVERT_ALLOWED_FEATURES);
- fprintf(stderr,
- "ERROR: features not allowed for convert: %s\n",
+ error("features not allowed for convert: %s",
buf);
exit(1);
}
@@ -3109,11 +3168,10 @@ int main(int argc, char *argv[])
file = argv[optind];
ret = check_mounted(file);
if (ret < 0) {
- fprintf(stderr, "Could not check mount status: %s\n",
- strerror(-ret));
+ error("could not check mount status: %s", strerror(-ret));
return 1;
} else if (ret) {
- fprintf(stderr, "%s is mounted\n", file);
+ error("%s is mounted", file);
return 1;
}
diff --git a/ctree.c b/ctree.c
index a98ad185..d07ec7d9 100644
--- a/ctree.c
+++ b/ctree.c
@@ -2580,7 +2580,9 @@ int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
unsigned long ptr;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
+
ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size);
if (!ret) {
leaf = path->nodes[0];
diff --git a/ctree.h b/ctree.h
index 297a98c5..0c34ae20 100644
--- a/ctree.h
+++ b/ctree.h
@@ -85,6 +85,9 @@ struct btrfs_free_space_ctl;
/* tracks free space in block groups. */
#define BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL
+/* device stats in the device tree */
+#define BTRFS_DEV_STATS_OBJECTID 0ULL
+
/* for storing balance parameters in the root tree */
#define BTRFS_BALANCE_OBJECTID -4ULL
@@ -464,6 +467,14 @@ struct btrfs_super_block {
* ones specified below then we will fail to mount
*/
#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0)
+/*
+ * Older kernels on big-endian systems produced broken free space tree bitmaps,
+ * and btrfs-progs also used to corrupt the free space tree. If this bit is
+ * clear, then the free space tree cannot be trusted. btrfs-progs can also
+ * intentionally clear this bit to ask the kernel to rebuild the free space
+ * tree.
+ */
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID (1ULL << 1)
#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0)
#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
@@ -490,6 +501,11 @@ struct btrfs_super_block {
#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
+/*
+ * The FREE_SPACE_TREE and FREE_SPACE_TREE_VALID compat_ro bits must not be
+ * added here until read-write support for the free space tree is implemented in
+ * btrfs-progs.
+ */
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
#define BTRFS_FEATURE_INCOMPAT_SUPP \
@@ -552,18 +568,20 @@ struct btrfs_node {
struct btrfs_path {
struct extent_buffer *nodes[BTRFS_MAX_LEVEL];
int slots[BTRFS_MAX_LEVEL];
- /* if there is real range locking, this locks field will change */
+#if 0
+ /* The kernel locking sheme is not done in userspace. */
int locks[BTRFS_MAX_LEVEL];
- int reada;
+#endif
+ signed char reada;
/* keep some upper locks as we walk down */
- int lowest_level;
+ u8 lowest_level;
/*
* set by btrfs_split_item, tells search_slot to keep all locks
* and to force calls to keep space in the nodes
*/
- unsigned int search_for_split:1;
- unsigned int skip_check_block:1;
+ u8 search_for_split;
+ u8 skip_check_block;
};
/*
@@ -785,6 +803,84 @@ struct btrfs_root_ref {
__le16 name_len;
} __attribute__ ((__packed__));
+struct btrfs_disk_balance_args {
+ /*
+ * profiles to operate on, single is denoted by
+ * BTRFS_AVAIL_ALLOC_BIT_SINGLE
+ */
+ __le64 profiles;
+
+ /*
+ * usage filter
+ * BTRFS_BALANCE_ARGS_USAGE with a single value means '0..N'
+ * BTRFS_BALANCE_ARGS_USAGE_RANGE - range syntax, min..max
+ */
+ union {
+ __le64 usage;
+ struct {
+ __le32 usage_min;
+ __le32 usage_max;
+ };
+ };
+
+ /* devid filter */
+ __le64 devid;
+
+ /* devid subset filter [pstart..pend) */
+ __le64 pstart;
+ __le64 pend;
+
+ /* btrfs virtual address space subset filter [vstart..vend) */
+ __le64 vstart;
+ __le64 vend;
+
+ /*
+ * profile to convert to, single is denoted by
+ * BTRFS_AVAIL_ALLOC_BIT_SINGLE
+ */
+ __le64 target;
+
+ /* BTRFS_BALANCE_ARGS_* */
+ __le64 flags;
+
+ /*
+ * BTRFS_BALANCE_ARGS_LIMIT with value 'limit'
+ * BTRFS_BALANCE_ARGS_LIMIT_RANGE - the extend version can use minimum
+ * and maximum
+ */
+ union {
+ __le64 limit;
+ struct {
+ __le32 limit_min;
+ __le32 limit_max;
+ };
+ };
+
+ /*
+ * Process chunks that cross stripes_min..stripes_max devices,
+ * BTRFS_BALANCE_ARGS_STRIPES_RANGE
+ */
+ __le32 stripes_min;
+ __le32 stripes_max;
+
+ __le64 unused[6];
+} __attribute__ ((__packed__));
+
+/*
+ * store balance parameters to disk so that balance can be properly
+ * resumed after crash or unmount
+ */
+struct btrfs_balance_item {
+ /* BTRFS_BALANCE_* */
+ __le64 flags;
+
+ struct btrfs_disk_balance_args data;
+ struct btrfs_disk_balance_args meta;
+ struct btrfs_disk_balance_args sys;
+
+ __le64 unused[4];
+} __attribute__ ((__packed__));
+
#define BTRFS_FILE_EXTENT_INLINE 0
#define BTRFS_FILE_EXTENT_REG 1
#define BTRFS_FILE_EXTENT_PREALLOC 2
@@ -838,6 +934,14 @@ struct btrfs_file_extent_item {
} __attribute__ ((__packed__));
+struct btrfs_dev_stats_item {
+ /*
+ * grow this item struct at the end for future enhancements and keep
+ * the existing values unchanged
+ */
+ __le64 values[BTRFS_DEV_STAT_VALUES_MAX];
+} __attribute__ ((__packed__));
+
struct btrfs_csum_item {
u8 csum;
} __attribute__ ((__packed__));
@@ -1073,7 +1177,6 @@ struct btrfs_root {
u32 type;
- u64 highest_inode;
u64 last_inode_alloc;
/*
@@ -1211,10 +1314,42 @@ struct btrfs_root {
#define BTRFS_QGROUP_RELATION_KEY 246
/*
- * Persistently stores the io stats in the device tree.
- * One key for all stats, (0, BTRFS_DEV_STATS_KEY, devid).
+ * Obsolete name, see BTRFS_TEMPORARY_ITEM_KEY.
+ */
+#define BTRFS_BALANCE_ITEM_KEY 248
+
+/*
+ * The key type for tree items that are stored persistently, but do not need to
+ * exist for extended period of time. The items can exist in any tree.
+ *
+ * [subtype, BTRFS_TEMPORARY_ITEM_KEY, data]
+ *
+ * Existing items:
+ *
+ * - balance status item
+ * (BTRFS_BALANCE_OBJECTID, BTRFS_TEMPORARY_ITEM_KEY, 0)
+ */
+#define BTRFS_TEMPORARY_ITEM_KEY 248
+
+/*
+ * Obsolete name, see BTRFS_PERSISTENT_ITEM_KEY
+ */
+#define BTRFS_DEV_STATS_KEY 249
+
+/*
+ * The key type for tree items that are stored persistently and usually exist
+ * for a long period, eg. filesystem lifetime. The item kinds can be status
+ * information, stats or preference values. The item can exist in any tree.
+ *
+ * [subtype, BTRFS_PERSISTENT_ITEM_KEY, data]
+ *
+ * Existing items:
+ *
+ * - device statistics, store IO stats in the device tree, one key for all
+ * stats
+ * (BTRFS_DEV_STATS_OBJECTID, BTRFS_DEV_STATS_KEY, 0)
*/
-#define BTRFS_DEV_STATS_KEY 249
+#define BTRFS_PERSISTENT_ITEM_KEY 249
/*
* Persistently stores the device replace state in the device tree.
@@ -1246,6 +1381,15 @@ struct btrfs_root {
#define BTRFS_INODE_NODATASUM (1 << 0)
#define BTRFS_INODE_NODATACOW (1 << 1)
#define BTRFS_INODE_READONLY (1 << 2)
+#define BTRFS_INODE_NOCOMPRESS (1 << 3)
+#define BTRFS_INODE_PREALLOC (1 << 4)
+#define BTRFS_INODE_SYNC (1 << 5)
+#define BTRFS_INODE_IMMUTABLE (1 << 6)
+#define BTRFS_INODE_APPEND (1 << 7)
+#define BTRFS_INODE_NODUMP (1 << 8)
+#define BTRFS_INODE_NOATIME (1 << 9)
+#define BTRFS_INODE_DIRSYNC (1 << 10)
+#define BTRFS_INODE_COMPRESS (1 << 11)
#define read_eb_member(eb, ptr, type, member, result) ( \
read_extent_buffer(eb, (char *)(result), \
@@ -1834,17 +1978,6 @@ static inline void btrfs_dir_item_key_to_cpu(struct extent_buffer *eb,
btrfs_disk_key_to_cpu(key, &disk_key);
}
-
-static inline u8 btrfs_key_type(struct btrfs_key *key)
-{
- return key->type;
-}
-
-static inline void btrfs_set_key_type(struct btrfs_key *key, u8 val)
-{
- key->type = val;
-}
-
/* struct btrfs_header */
BTRFS_SETGET_HEADER_FUNCS(header_bytenr, struct btrfs_header, bytenr, 64);
BTRFS_SETGET_HEADER_FUNCS(header_generation, struct btrfs_header,
@@ -2192,6 +2325,49 @@ BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_rsv_referenced,
BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_rsv_exclusive,
struct btrfs_qgroup_limit_item, rsv_exclusive, 64);
+/* btrfs_balance_item */
+BTRFS_SETGET_FUNCS(balance_item_flags, struct btrfs_balance_item, flags, 64);
+
+static inline struct btrfs_disk_balance_args* btrfs_balance_item_data(
+ struct extent_buffer *eb, struct btrfs_balance_item *bi)
+{
+ unsigned long offset = (unsigned long)bi;
+ struct btrfs_balance_item *p;
+ p = (struct btrfs_balance_item *)(eb->data + offset);
+ return &p->data;
+}
+
+static inline struct btrfs_disk_balance_args* btrfs_balance_item_meta(
+ struct extent_buffer *eb, struct btrfs_balance_item *bi)
+{
+ unsigned long offset = (unsigned long)bi;
+ struct btrfs_balance_item *p;
+ p = (struct btrfs_balance_item *)(eb->data + offset);
+ return &p->meta;
+}
+
+static inline struct btrfs_disk_balance_args* btrfs_balance_item_sys(
+ struct extent_buffer *eb, struct btrfs_balance_item *bi)
+{
+ unsigned long offset = (unsigned long)bi;
+ struct btrfs_balance_item *p;
+ p = (struct btrfs_balance_item *)(eb->data + offset);
+ return &p->sys;
+}
+
+/*
+ * btrfs_dev_stats_item helper, returns pointer to the raw array, do the
+ * endiannes conversion, @dsi is offset to eb data
+ */
+static inline __le64* btrfs_dev_stats_values(struct extent_buffer *eb,
+ struct btrfs_dev_stats_item *dsi)
+{
+ unsigned long offset = (unsigned long)dsi;
+ struct btrfs_dev_stats_item *p;
+ p = (struct btrfs_dev_stats_item *)(eb->data + offset);
+ return p->values;
+}
+
/*
* this returns the number of bytes used by the item on disk, minus the
* size of any extent headers. If a file is compressed on disk, this is
@@ -2264,14 +2440,20 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level) {
return root->nodesize;
}
-static inline int btrfs_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag)
+#define btrfs_fs_incompat(fs_info, opt) \
+ __btrfs_fs_incompat((fs_info), BTRFS_FEATURE_INCOMPAT_##opt)
+
+static inline int __btrfs_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag)
{
struct btrfs_super_block *disk_super;
disk_super = fs_info->super_copy;
return !!(btrfs_super_incompat_flags(disk_super) & flag);
}
-static inline int btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag)
+#define btrfs_fs_compat_ro(fs_info, opt) \
+ __btrfs_fs_compat_ro((fs_info), BTRFS_FEATURE_COMPAT_RO_##opt)
+
+static inline int __btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag)
{
struct btrfs_super_block *disk_super;
disk_super = fs_info->super_copy;
@@ -2327,6 +2509,10 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf, int record_parent);
int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf, int record_parent);
+int btrfs_free_tree_block(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct extent_buffer *buf,
+ u64 parent, int last_ref);
int btrfs_free_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 parent,
@@ -2487,6 +2673,8 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_key *key, struct btrfs_root_item
*item);
+int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+ struct btrfs_key *key);
int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_key *key, struct btrfs_root_item
*item);
diff --git a/debian/changelog b/debian/changelog
index d7424990..eb3767c0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+btrfs-progs (4.9.1-1) UNRELEASED; urgency=medium
+
+ * New upstream release Closes: #849353, #817806, #854915, #845473
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com> Mon, 13 Feb 2017 11:21:54 +0000
+
btrfs-progs (4.7.3-1) unstable; urgency=medium
* New upstream release.
diff --git a/dir-item.c b/dir-item.c
index c2ae953e..846fc292 100644
--- a/dir-item.c
+++ b/dir-item.c
@@ -75,7 +75,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
u32 data_size;
key.objectid = dir;
- btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
+ key.type = BTRFS_XATTR_ITEM_KEY;
key.offset = btrfs_name_hash(name, name_len);
path = btrfs_alloc_path();
if (!path)
@@ -125,7 +125,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
u32 data_size;
key.objectid = dir;
- btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
+ key.type = BTRFS_DIR_ITEM_KEY;
key.offset = btrfs_name_hash(name, name_len);
path = btrfs_alloc_path();
if (!path)
@@ -156,7 +156,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
}
btrfs_release_path(path);
- btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
+ key.type = BTRFS_DIR_INDEX_KEY;
key.offset = index;
dir_item = insert_with_overflow(trans, root, path, &key, data_size,
name, name_len);
@@ -196,7 +196,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
struct extent_buffer *leaf;
key.objectid = dir;
- btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
+ key.type = BTRFS_DIR_ITEM_KEY;
key.offset = btrfs_name_hash(name, name_len);
@@ -213,7 +213,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
if (found_key.objectid != dir ||
- btrfs_key_type(&found_key) != BTRFS_DIR_ITEM_KEY ||
+ found_key.type != BTRFS_DIR_ITEM_KEY ||
found_key.offset != key.offset)
return NULL;
diff --git a/dir-test.c b/dir-test.c
index a54b7773..24f2c2e3 100644
--- a/dir-test.c
+++ b/dir-test.c
@@ -87,7 +87,7 @@ static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
inode_map.objectid = objectid;
inode_map.flags = 0;
- btrfs_set_key_type(&inode_map, BTRFS_INODE_ITEM_KEY);
+ inode_map.type = BTRFS_INODE_ITEM_KEY;
inode_map.offset = 0;
initial_inode_init(root, &inode_item);
@@ -158,7 +158,7 @@ static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root
key.objectid = file_oid;
key.flags = 0;
- btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
+ key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
&key, BTRFS_FT_UNKNOWN);
@@ -312,7 +312,7 @@ static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root
key.offset = (u64)-1;
key.flags = 0;
- btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
+ key.type = BTRFS_DIR_ITEM_KEY;
key.objectid = dir_oid;
while(nr-- >= 0) {
btrfs_init_path(&path);
diff --git a/disk-io.c b/disk-io.c
index 2fd3330f..2a94d4fc 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -123,7 +123,7 @@ u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
return crc32c(seed, data, len);
}
-void btrfs_csum_final(u32 crc, char *result)
+void btrfs_csum_final(u32 crc, u8 *result)
{
put_unaligned_le32(~crc, result);
}
@@ -131,7 +131,7 @@ void btrfs_csum_final(u32 crc, char *result)
static int __csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
int verify, int silent)
{
- char result[BTRFS_CSUM_SIZE];
+ u8 result[BTRFS_CSUM_SIZE];
u32 len;
u32 crc = ~(u32)0;
@@ -241,7 +241,7 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
ret = 1;
out:
- clear_extent_buffer_uptodate(io_tree, eb);
+ clear_extent_buffer_uptodate(eb);
return ret;
}
@@ -326,16 +326,17 @@ struct extent_buffer* read_tree_block_fs_info(
* Such unaligned tree block will free overlapping extent buffer,
* causing use-after-free bugs for fuzzed images.
*/
- if (!IS_ALIGNED(bytenr, sectorsize)) {
+ if (bytenr < sectorsize || !IS_ALIGNED(bytenr, sectorsize)) {
error("tree block bytenr %llu is not aligned to sectorsize %u",
bytenr, sectorsize);
return ERR_PTR(-EIO);
}
- if (!IS_ALIGNED(blocksize, nodesize)) {
+ if (blocksize < nodesize || !IS_ALIGNED(blocksize, nodesize)) {
error("tree block size %u is not aligned to nodesize %u",
blocksize, nodesize);
return ERR_PTR(-EIO);
}
+
eb = btrfs_find_create_tree_block(fs_info, bytenr, blocksize);
if (!eb)
return ERR_PTR(-ENOMEM);
@@ -477,7 +478,7 @@ int write_tree_block(struct btrfs_trans_handle *trans,
return write_and_map_eb(trans, root, eb);
}
-int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
+void btrfs_setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
u32 stripesize, struct btrfs_root *root,
struct btrfs_fs_info *fs_info, u64 objectid)
{
@@ -493,7 +494,6 @@ int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
root->fs_info = fs_info;
root->objectid = objectid;
root->last_trans = 0;
- root->highest_inode = 0;
root->last_inode_alloc = 0;
INIT_LIST_HEAD(&root->dirty_list);
@@ -501,7 +501,6 @@ int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
memset(&root->root_key, 0, sizeof(root->root_key));
memset(&root->root_item, 0, sizeof(root->root_item));
root->root_key.objectid = objectid;
- return 0;
}
static int update_cowonly_root(struct btrfs_trans_handle *trans,
@@ -634,7 +633,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
u32 blocksize;
u64 generation;
- __setup_root(tree_root->nodesize, tree_root->leafsize,
+ btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
tree_root->sectorsize, tree_root->stripesize,
root, fs_info, objectid);
ret = btrfs_find_last_root(tree_root, objectid,
@@ -670,7 +669,7 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
blocksize = tree_root->nodesize;
- __setup_root(tree_root->nodesize, tree_root->leafsize,
+ btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
tree_root->sectorsize, tree_root->stripesize,
log_root, fs_info, BTRFS_TREE_LOG_OBJECTID);
@@ -734,12 +733,16 @@ struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
goto insert;
}
- __setup_root(tree_root->nodesize, tree_root->leafsize,
+ btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
tree_root->sectorsize, tree_root->stripesize,
root, fs_info, location->objectid);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path) {
+ free(root);
+ return ERR_PTR(-ENOMEM);
+ }
+
ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0);
if (ret != 0) {
if (ret > 0)
@@ -900,7 +903,8 @@ free_all:
return NULL;
}
-int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable)
+int btrfs_check_fs_compatibility(struct btrfs_super_block *sb,
+ unsigned int flags)
{
u64 features;
@@ -919,13 +923,22 @@ int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable)
btrfs_set_super_incompat_flags(sb, features);
}
- features = btrfs_super_compat_ro_flags(sb) &
- ~BTRFS_FEATURE_COMPAT_RO_SUPP;
- if (writable && features) {
- printk("couldn't open RDWR because of unsupported "
- "option features (%Lx).\n",
- (unsigned long long)features);
- return -ENOTSUP;
+ features = btrfs_super_compat_ro_flags(sb);
+ if (flags & OPEN_CTREE_WRITES) {
+ if (flags & OPEN_CTREE_INVALIDATE_FST) {
+ /* Clear the FREE_SPACE_TREE_VALID bit on disk... */
+ features &= ~BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID;
+ btrfs_set_super_compat_ro_flags(sb, features);
+ /* ... and ignore the free space tree bit. */
+ features &= ~BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE;
+ }
+ if (features & ~BTRFS_FEATURE_COMPAT_RO_SUPP) {
+ printk("couldn't open RDWR because of unsupported "
+ "option features (%Lx).\n",
+ (unsigned long long)features);
+ return -ENOTSUP;
+ }
+
}
return 0;
}
@@ -972,7 +985,7 @@ static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
btrfs_find_create_tree_block(fs_info, 0, nodesize);
if (!info_root->node)
return -ENOMEM;
- clear_extent_buffer_uptodate(NULL, info_root->node);
+ clear_extent_buffer_uptodate(info_root->node);
}
return 0;
@@ -998,7 +1011,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
stripesize = btrfs_super_stripesize(sb);
root = fs_info->tree_root;
- __setup_root(nodesize, leafsize, sectorsize, stripesize,
+ btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize,
root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
blocksize = root->nodesize;
generation = btrfs_super_generation(sb);
@@ -1049,7 +1062,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
if (ret == 0)
fs_info->quota_enabled = 1;
- if (btrfs_fs_compat_ro(fs_info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) {
+ if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
ret = find_and_setup_root(root, fs_info, BTRFS_FREE_SPACE_TREE_OBJECTID,
fs_info->free_space_root);
if (ret) {
@@ -1163,7 +1176,7 @@ int btrfs_scan_fs_devices(int fd, const char *path,
}
if (!skip_devices && total_devs != 1) {
- ret = btrfs_scan_lblkid();
+ ret = btrfs_scan_devices();
if (ret)
return ret;
}
@@ -1187,7 +1200,7 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info,
sectorsize = btrfs_super_sectorsize(sb);
stripesize = btrfs_super_stripesize(sb);
- __setup_root(nodesize, leafsize, sectorsize, stripesize,
+ btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize,
fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
ret = btrfs_read_sys_array(fs_info->chunk_root);
@@ -1316,8 +1329,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
- ret = btrfs_check_fs_compatibility(fs_info->super_copy,
- flags & OPEN_CTREE_WRITES);
+ ret = btrfs_check_fs_compatibility(fs_info->super_copy, flags);
if (ret)
goto out_devices;
@@ -1407,7 +1419,11 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
struct btrfs_fs_info *info;
/* This flags may not return fs_info with any valid root */
- BUG_ON(flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR);
+ if (flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR) {
+ error("invalid open_ctree flags: 0x%llx",
+ (unsigned long long)flags);
+ return NULL;
+ }
info = __open_ctree_fd(fp, path, sb_bytenr, 0, 0, flags);
if (!info)
return NULL;
@@ -1425,7 +1441,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
*/
static int check_super(struct btrfs_super_block *sb, unsigned sbflags)
{
- char result[BTRFS_CSUM_SIZE];
+ u8 result[BTRFS_CSUM_SIZE];
u32 crc;
u16 csum_type;
int csum_size;
@@ -1553,7 +1569,7 @@ static int check_super(struct btrfs_super_block *sb, unsigned sbflags)
}
if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
+ sizeof(struct btrfs_chunk)) {
- error("system chunk array too small %u < %lu",
+ error("system chunk array too small %u < %zu",
btrfs_super_sys_array_size(sb),
sizeof(struct btrfs_disk_key) +
sizeof(struct btrfs_chunk));
@@ -1582,14 +1598,20 @@ int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
if (sb_bytenr != BTRFS_SUPER_INFO_OFFSET) {
ret = pread64(fd, buf, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
+ /* real error */
+ if (ret < 0)
+ return -errno;
+
+ /* Not large enough sb, return -ENOENT instead of normal -EIO */
if (ret < BTRFS_SUPER_INFO_SIZE)
- return -1;
+ return -ENOENT;
if (btrfs_super_bytenr(buf) != sb_bytenr)
- return -1;
+ return -EIO;
- if (check_super(buf, sbflags))
- return -1;
+ ret = check_super(buf, sbflags);
+ if (ret < 0)
+ return ret;
memcpy(sb, buf, BTRFS_SUPER_INFO_SIZE);
return 0;
}
@@ -1649,7 +1671,7 @@ static int write_dev_supers(struct btrfs_root *root,
crc = ~(u32)0;
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, (char *)&sb->csum[0]);
+ btrfs_csum_final(crc, &sb->csum[0]);
/*
* super_copy is BTRFS_SUPER_INFO_SIZE bytes and is
@@ -1673,7 +1695,7 @@ static int write_dev_supers(struct btrfs_root *root,
crc = ~(u32)0;
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, (char *)&sb->csum[0]);
+ btrfs_csum_final(crc, &sb->csum[0]);
/*
* super_copy is BTRFS_SUPER_INFO_SIZE bytes and is
diff --git a/disk-io.h b/disk-io.h
index c404d3f4..1c8387e7 100644
--- a/disk-io.h
+++ b/disk-io.h
@@ -74,6 +74,12 @@ enum btrfs_open_ctree_flags {
/* Allow to open a partially created filesystem */
OPEN_CTREE_FS_PARTIAL = (1U << 12),
+
+ /*
+ * Invalidate the free space tree (i.e., clear the FREE_SPACE_TREE_VALID
+ * compat_ro bit).
+ */
+ OPEN_CTREE_INVALIDATE_FST = (1U << 13),
};
/*
@@ -120,7 +126,7 @@ void readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
struct extent_buffer* btrfs_find_create_tree_block(
struct btrfs_fs_info *fs_info, u64 bytenr, u32 blocksize);
-int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
+void btrfs_setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
u32 stripesize, struct btrfs_root *root,
struct btrfs_fs_info *fs_info, u64 objectid);
int clean_tree_block(struct btrfs_trans_handle *trans,
@@ -128,7 +134,8 @@ int clean_tree_block(struct btrfs_trans_handle *trans,
void btrfs_free_fs_info(struct btrfs_fs_info *fs_info);
struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr);
-int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable);
+int btrfs_check_fs_compatibility(struct btrfs_super_block *sb,
+ unsigned int flags);
int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
unsigned flags);
void btrfs_release_all_roots(struct btrfs_fs_info *fs_info);
@@ -175,7 +182,7 @@ int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
int wait_on_tree_block_writeback(struct btrfs_root *root,
struct extent_buffer *buf);
u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len);
-void btrfs_csum_final(u32 crc, char *result);
+void btrfs_csum_final(u32 crc, u8 *result);
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
@@ -190,7 +197,8 @@ int write_tree_block(struct btrfs_trans_handle *trans,
int write_and_map_eb(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *eb);
-/* raid6.c */
+/* raid56.c */
void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs);
+int raid5_gen_result(int nr_devs, size_t stripe_len, int dest, void **data);
#endif
diff --git a/extent-tree.c b/extent-tree.c
index 0607be66..b2847ff9 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -995,8 +995,7 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
int ret;
int err = 0;
int skinny_metadata =
- btrfs_fs_incompat(root->fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
+ btrfs_fs_incompat(root->fs_info, SKINNY_METADATA);
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
@@ -1456,8 +1455,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
u64 extent_flags;
if (metadata &&
- !btrfs_fs_incompat(root->fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) {
+ !btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) {
offset = root->nodesize;
metadata = 0;
}
@@ -1552,8 +1550,7 @@ int btrfs_set_block_flags(struct btrfs_trans_handle *trans,
struct btrfs_extent_item *item;
u32 item_size;
int skinny_metadata =
- btrfs_fs_incompat(root->fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
+ btrfs_fs_incompat(root->fs_info, SKINNY_METADATA);
path = btrfs_alloc_path();
if (!path)
@@ -1664,7 +1661,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
cond_resched();
if (level == 0) {
btrfs_item_key_to_cpu(buf, &key, i);
- if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
+ if (key.type != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(buf, i,
struct btrfs_file_extent_item);
@@ -2079,8 +2076,7 @@ static int finish_current_insert(struct btrfs_trans_handle *trans,
struct btrfs_key key;
int ret;
int skinny_metadata =
- btrfs_fs_incompat(extent_root->fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
+ btrfs_fs_incompat(extent_root->fs_info, SKINNY_METADATA);
while(1) {
ret = find_first_extent_bit(&info->extent_ins, 0, &start,
@@ -2193,8 +2189,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
u32 item_size;
u64 refs;
int skinny_metadata =
- btrfs_fs_incompat(extent_root->fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
+ btrfs_fs_incompat(extent_root->fs_info, SKINNY_METADATA);
if (root->fs_info->free_extent_hook) {
root->fs_info->free_extent_hook(trans, root, bytenr, num_bytes,
@@ -2467,6 +2462,17 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct
return err;
}
+
+int btrfs_free_tree_block(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct extent_buffer *buf,
+ u64 parent, int last_ref)
+{
+ return btrfs_free_extent(trans, root, buf->start, buf->len, parent,
+ root->root_key.objectid,
+ btrfs_header_level(buf), 0);
+}
+
/*
* remove an extent from the root, returns 0 on success
*/
@@ -2538,7 +2544,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
int wrapped = 0;
WARN_ON(num_bytes < root->sectorsize);
- btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
+ ins->type = BTRFS_EXTENT_ITEM_KEY;
search_start = stripe_align(root, search_start);
@@ -2606,11 +2612,20 @@ check_failed:
}
if (!(data & BTRFS_BLOCK_GROUP_DATA)) {
- if (check_crossing_stripes(ins->objectid, num_bytes)) {
- search_start = round_down(ins->objectid + num_bytes,
- BTRFS_STRIPE_LEN);
+ if (check_crossing_stripes(info, ins->objectid, num_bytes)) {
+ struct btrfs_block_group_cache *bg_cache;
+ u64 bg_offset;
+
+ bg_cache = btrfs_lookup_block_group(info, ins->objectid);
+ if (!bg_cache)
+ goto no_bg_cache;
+ bg_offset = ins->objectid - bg_cache->key.objectid;
+
+ search_start = round_up(bg_offset + num_bytes,
+ BTRFS_STRIPE_LEN) + bg_offset;
goto new_group;
}
+no_bg_cache:
block_group = btrfs_lookup_block_group(info, ins->objectid);
if (block_group)
trans->block_group = block_group;
@@ -2706,15 +2721,14 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_path *path;
struct extent_buffer *leaf;
u32 size = sizeof(*extent_item) + sizeof(*iref);
- int skinny_metadata =
- btrfs_fs_incompat(fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
+ int skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
if (!skinny_metadata)
size += sizeof(*block_info);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
ins, size);
@@ -2779,8 +2793,7 @@ static int alloc_tree_block(struct btrfs_trans_handle *trans,
set_state_private(&root->fs_info->extent_ins,
ins->objectid, (unsigned long)extent_op);
} else {
- if (btrfs_fs_incompat(root->fs_info,
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) {
+ if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) {
ins->offset = level;
ins->type = BTRFS_METADATA_ITEM_KEY;
}
@@ -3230,7 +3243,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
root = info->extent_root;
key.objectid = 0;
key.offset = 0;
- btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY);
+ key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
@@ -3312,7 +3325,7 @@ btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
cache->key.objectid = chunk_offset;
cache->key.offset = size;
- btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY);
+ 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);
cache->flags = type;
@@ -3424,7 +3437,7 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
cache->key.objectid = cur_start;
cache->key.offset = group_size;
- btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY);
+ cache->key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
btrfs_set_block_group_used(&cache->item, 0);
btrfs_set_block_group_chunk_objectid(&cache->item,
@@ -3868,7 +3881,7 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
btrfs_init_path(&path);
key.offset = 0;
key.objectid = 0;
- btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+ key.type = BTRFS_EXTENT_ITEM_KEY;
ret = btrfs_search_slot(trans, root->fs_info->extent_root,
&key, &path, 0, 0);
if (ret < 0)
@@ -4060,7 +4073,7 @@ static int __btrfs_record_file_extent(struct btrfs_trans_handle *trans,
btrfs_release_path(path);
ins_key.objectid = objectid;
ins_key.offset = file_pos;
- btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY);
+ ins_key.type = BTRFS_EXTENT_DATA_KEY;
ret = btrfs_insert_empty_item(trans, root, path, &ins_key,
sizeof(*fi));
if (ret)
diff --git a/extent_io.h b/extent_io.h
index 208c4fea..bd6cf9ef 100644
--- a/extent_io.h
+++ b/extent_io.h
@@ -125,8 +125,7 @@ static inline int set_extent_buffer_uptodate(struct extent_buffer *eb)
return 0;
}
-static inline int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
- struct extent_buffer *eb)
+static inline int clear_extent_buffer_uptodate(struct extent_buffer *eb)
{
eb->flags &= ~EXTENT_UPTODATE;
return 0;
diff --git a/file-item.c b/file-item.c
index 7a3bbf35..e462b4bb 100644
--- a/file-item.c
+++ b/file-item.c
@@ -36,16 +36,29 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
u64 disk_num_bytes, u64 num_bytes)
{
int ret = 0;
+ int is_hole = 0;
struct btrfs_file_extent_item *item;
struct btrfs_key file_key;
struct btrfs_path *path;
struct extent_buffer *leaf;
+ if (offset == 0)
+ is_hole = 1;
+ /* For NO_HOLES, we don't insert hole file extent */
+ if (btrfs_fs_incompat(root->fs_info, NO_HOLES) && is_hole)
+ return 0;
+
+ /* For hole, its disk_bytenr and disk_num_bytes must be 0 */
+ if (is_hole)
+ disk_num_bytes = 0;
+
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
+
file_key.objectid = objectid;
file_key.offset = pos;
- btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
+ file_key.type = BTRFS_EXTENT_DATA_KEY;
ret = btrfs_insert_empty_item(trans, root, path, &file_key,
sizeof(*item));
@@ -90,7 +103,7 @@ int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans,
key.objectid = objectid;
key.offset = offset;
- btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
+ key.type = BTRFS_EXTENT_DATA_KEY;
datasize = btrfs_file_extent_calc_inline_size(size);
ret = btrfs_insert_empty_item(trans, root, path, &key, datasize);
@@ -135,7 +148,7 @@ btrfs_lookup_csum(struct btrfs_trans_handle *trans,
file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
file_key.offset = bytenr;
- btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY);
+ file_key.type = BTRFS_EXTENT_CSUM_KEY;
ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow);
if (ret < 0)
goto fail;
@@ -146,7 +159,7 @@ btrfs_lookup_csum(struct btrfs_trans_handle *trans,
goto fail;
path->slots[0]--;
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
- if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY)
+ if (found_key.type != BTRFS_EXTENT_CSUM_KEY)
goto fail;
csum_offset = (bytenr - found_key.offset) / root->sectorsize;
@@ -188,7 +201,8 @@ int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
btrfs_super_csum_size(root->fs_info->super_copy);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
file_key.offset = bytenr;
@@ -297,7 +311,7 @@ csum:
csum_offset * csum_size);
found:
csum_result = btrfs_csum_data(root, data, csum_result, len);
- btrfs_csum_final(csum_result, (char *)&csum_result);
+ btrfs_csum_final(csum_result, (u8 *)&csum_result);
if (csum_result == 0) {
printk("csum result is 0 for block %llu\n",
(unsigned long long)bytenr);
diff --git a/free-space-cache.c b/free-space-cache.c
index 357d69e7..286b185e 100644
--- a/free-space-cache.c
+++ b/free-space-cache.c
@@ -25,6 +25,7 @@
#include "crc32c.h"
#include "bitops.h"
#include "internal.h"
+#include "utils.h"
/*
* Kernel always uses PAGE_CACHE_SIZE for sectorsize, but we don't have
@@ -210,7 +211,7 @@ static int io_ctl_check_crc(struct io_ctl *io_ctl, int index)
io_ctl_map_page(io_ctl, 0);
crc = crc32c(crc, io_ctl->orig + offset, io_ctl->root->sectorsize - offset);
- btrfs_csum_final(crc, (char *)&crc);
+ btrfs_csum_final(crc, (u8 *)&crc);
if (val != crc) {
printk("btrfs: csum mismatch on free space cache\n");
io_ctl_unmap_page(io_ctl);
@@ -877,3 +878,129 @@ next:
prev = e;
}
}
+
+int btrfs_clear_free_space_cache(struct btrfs_fs_info *fs_info,
+ struct btrfs_block_group_cache *bg)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *tree_root = fs_info->tree_root;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_disk_key location;
+ struct btrfs_free_space_header *sc_header;
+ struct extent_buffer *node;
+ u64 ino;
+ int slot;
+ int ret;
+
+ trans = btrfs_start_transaction(tree_root, 1);
+ if (IS_ERR(trans))
+ return PTR_ERR(trans);
+
+ btrfs_init_path(&path);
+
+ key.objectid = BTRFS_FREE_SPACE_OBJECTID;
+ key.type = 0;
+ key.offset = bg->key.objectid;
+
+ ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1);
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ if (ret < 0)
+ goto out;
+
+ node = path.nodes[0];
+ slot = path.slots[0];
+ sc_header = btrfs_item_ptr(node, slot, struct btrfs_free_space_header);
+ btrfs_free_space_key(node, sc_header, &location);
+ ino = location.objectid;
+
+ /* Delete the free space header, as we have the ino to continue */
+ ret = btrfs_del_item(trans, tree_root, &path);
+ if (ret < 0) {
+ error("failed to remove free space header for block group %llu: %d",
+ bg->key.objectid, ret);
+ goto out;
+ }
+ btrfs_release_path(&path);
+
+ /* Iterate from the end of the free space cache inode */
+ key.objectid = ino;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = (u64)-1;
+ ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1);
+ if (ret < 0) {
+ error("failed to locate free space cache extent for block group %llu: %d",
+ bg->key.objectid, ret);
+ goto out;
+ }
+ while (1) {
+ struct btrfs_file_extent_item *fi;
+ u64 disk_bytenr;
+ u64 disk_num_bytes;
+
+ ret = btrfs_previous_item(tree_root, &path, ino,
+ BTRFS_EXTENT_DATA_KEY);
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ if (ret < 0) {
+ error(
+ "failed to locate free space cache extent for block group %llu: %d",
+ bg->key.objectid, ret);
+ goto out;
+ }
+ node = path.nodes[0];
+ slot = path.slots[0];
+ btrfs_item_key_to_cpu(node, &key, slot);
+ fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
+ disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
+ disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
+
+ ret = btrfs_free_extent(trans, tree_root, disk_bytenr,
+ disk_num_bytes, 0, tree_root->objectid,
+ ino, key.offset);
+ if (ret < 0) {
+ error("failed to remove backref for disk bytenr %llu: %d",
+ disk_bytenr, ret);
+ goto out;
+ }
+ ret = btrfs_del_item(trans, tree_root, &path);
+ if (ret < 0) {
+ error(
+ "failed to remove free space extent data for ino %llu offset %llu: %d",
+ ino, key.offset, ret);
+ goto out;
+ }
+ }
+ btrfs_release_path(&path);
+
+ /* Now delete free space cache inode item */
+ key.objectid = ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1);
+ if (ret > 0)
+ warning("free space inode %llu not found, ignore", ino);
+ if (ret < 0) {
+ error(
+ "failed to locate free space cache inode %llu for block group %llu: %d",
+ ino, bg->key.objectid, ret);
+ goto out;
+ }
+ ret = btrfs_del_item(trans, tree_root, &path);
+ if (ret < 0) {
+ error(
+ "failed to delete free space cache inode %llu for block group %llu: %d",
+ ino, bg->key.objectid, ret);
+ }
+out:
+ btrfs_release_path(&path);
+ if (!ret)
+ btrfs_commit_transaction(trans, tree_root);
+ return ret;
+}
diff --git a/free-space-cache.h b/free-space-cache.h
index 9214077a..707fb6d3 100644
--- a/free-space-cache.h
+++ b/free-space-cache.h
@@ -59,4 +59,6 @@ void unlink_free_space(struct btrfs_free_space_ctl *ctl,
struct btrfs_free_space *info);
int btrfs_add_free_space(struct btrfs_free_space_ctl *ctl, u64 offset,
u64 bytes);
+int btrfs_clear_free_space_cache(struct btrfs_fs_info *fs_info,
+ struct btrfs_block_group_cache *bg);
#endif
diff --git a/free-space-tree.c b/free-space-tree.c
index 3c7a2463..f3a51263 100644
--- a/free-space-tree.c
+++ b/free-space-tree.c
@@ -20,6 +20,7 @@
#include "disk-io.h"
#include "free-space-cache.h"
#include "free-space-tree.h"
+#include "transaction.h"
static struct btrfs_free_space_info *
search_free_space_info(struct btrfs_trans_handle *trans,
@@ -67,6 +68,91 @@ static int free_space_test_bit(struct btrfs_block_group_cache *block_group,
return !!extent_buffer_test_bit(leaf, ptr, i);
}
+static int clear_free_space_tree(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root)
+{
+ struct btrfs_path *path;
+ struct btrfs_key key;
+ int nr;
+ int ret;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = 0;
+ key.type = 0;
+ key.offset = 0;
+
+ while (1) {
+ ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ if (ret < 0)
+ goto out;
+
+ nr = btrfs_header_nritems(path->nodes[0]);
+ if (!nr)
+ break;
+
+ path->slots[0] = 0;
+ ret = btrfs_del_items(trans, root, path, 0, nr);
+ if (ret)
+ goto out;
+
+ btrfs_release_path(path);
+ }
+
+ ret = 0;
+out:
+ btrfs_free_path(path);
+ return ret;
+}
+
+int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *tree_root = fs_info->tree_root;
+ struct btrfs_root *free_space_root = fs_info->free_space_root;
+ int ret;
+ u64 features;
+
+ trans = btrfs_start_transaction(tree_root, 0);
+ if (IS_ERR(trans))
+ return PTR_ERR(trans);
+
+ features = btrfs_super_compat_ro_flags(fs_info->super_copy);
+ features &= ~(BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID |
+ BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE);
+ btrfs_set_super_compat_ro_flags(fs_info->super_copy, features);
+ fs_info->free_space_root = NULL;
+
+ ret = clear_free_space_tree(trans, free_space_root);
+ if (ret)
+ goto abort;
+
+ ret = btrfs_del_root(trans, tree_root, &free_space_root->root_key);
+ if (ret)
+ goto abort;
+
+ list_del(&free_space_root->dirty_list);
+
+ ret = clean_tree_block(trans, tree_root, free_space_root->node);
+ if (ret)
+ goto abort;
+ ret = btrfs_free_tree_block(trans, free_space_root,
+ free_space_root->node, 0, 1);
+ if (ret)
+ goto abort;
+
+ free_extent_buffer(free_space_root->node);
+ free_extent_buffer(free_space_root->commit_root);
+ kfree(free_space_root);
+
+ ret = btrfs_commit_transaction(trans, tree_root);
+
+abort:
+ return ret;
+}
+
static int load_free_space_bitmaps(struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *block_group,
struct btrfs_path *path,
diff --git a/free-space-tree.h b/free-space-tree.h
index 7529a468..4845f13e 100644
--- a/free-space-tree.h
+++ b/free-space-tree.h
@@ -19,6 +19,7 @@
#ifndef __BTRFS_FREE_SPACE_TREE_H__
#define __BTRFS_FREE_SPACE_TREE_H__
+int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info);
int load_free_space_tree(struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *block_group);
diff --git a/hash.h b/hash.h
index ac4c4117..9ff67613 100644
--- a/hash.h
+++ b/hash.h
@@ -25,4 +25,14 @@ static inline u64 btrfs_name_hash(const char *name, int len)
{
return crc32c((u32)~1, name, len);
}
+
+/*
+ * Figure the key offset of an extended inode ref
+ */
+static inline u64 btrfs_extref_hash(u64 parent_objectid, const char *name,
+ int len)
+{
+ return (u64)btrfs_crc32c(parent_objectid, name, len);
+}
+
#endif
diff --git a/help.c b/help.c
index c8bb7204..5573f0f9 100644
--- a/help.c
+++ b/help.c
@@ -148,6 +148,8 @@ static void usage_command_group_internal(const struct cmd_group *grp, int full,
usage_command_internal(cmd->usagestr, cmd->token, full,
1, cmd->flags & CMD_ALIAS, outf);
+ if (cmd->flags & CMD_ALIAS)
+ putchar('\n');
continue;
}
diff --git a/btrfs-image.c b/image/main.c
index 953d368d..0158844b 100644
--- a/btrfs-image.c
+++ b/image/main.c
@@ -44,6 +44,8 @@
#define COMPRESS_NONE 0
#define COMPRESS_ZLIB 1
+#define MAX_WORKER_THREADS (32)
+
struct meta_cluster_item {
__le64 bytenr;
__le32 size;
@@ -95,9 +97,12 @@ struct metadump_struct {
struct btrfs_root *root;
FILE *out;
- struct meta_cluster *cluster;
+ union {
+ struct meta_cluster cluster;
+ char meta_cluster_bytes[BLOCK_SIZE];
+ };
- pthread_t *threads;
+ pthread_t threads[MAX_WORKER_THREADS];
size_t num_threads;
pthread_mutex_t mutex;
pthread_cond_t cond;
@@ -130,7 +135,7 @@ struct mdrestore_struct {
FILE *in;
FILE *out;
- pthread_t *threads;
+ pthread_t threads[MAX_WORKER_THREADS];
size_t num_threads;
pthread_mutex_t mutex;
pthread_cond_t cond;
@@ -163,7 +168,7 @@ static struct extent_buffer *alloc_dummy_eb(u64 bytenr, u32 size);
static void csum_block(u8 *buf, size_t len)
{
- char result[BTRFS_CRC32_SIZE];
+ u8 result[BTRFS_CRC32_SIZE];
u32 crc = ~(u32)0;
crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len - BTRFS_CSUM_SIZE);
btrfs_csum_final(crc, result);
@@ -312,7 +317,7 @@ static u64 logical_to_physical(struct mdrestore_struct *mdres, u64 logical,
entry = tree_search(&mdres->chunk_tree, &search.l, chunk_cmp, 1);
if (!entry) {
if (mdres->in != stdin)
- printf("Couldn't find a chunk, using logical\n");
+ warning("cannot find a chunk, using logical");
return logical;
}
fs_chunk = rb_entry(entry, struct fs_chunk, l);
@@ -354,7 +359,7 @@ static char *find_collision(struct metadump_struct *md, char *name,
val = malloc(sizeof(struct name));
if (!val) {
- fprintf(stderr, "Couldn't sanitize name, enomem\n");
+ error("cannot sanitize name, not enough memory");
free(name);
return NULL;
}
@@ -365,7 +370,7 @@ static char *find_collision(struct metadump_struct *md, char *name,
val->len = name_len;
val->sub = malloc(name_len);
if (!val->sub) {
- fprintf(stderr, "Couldn't sanitize name, enomem\n");
+ error("cannot sanitize name, not enough memory");
free(val);
free(name);
return NULL;
@@ -404,8 +409,8 @@ static char *find_collision(struct metadump_struct *md, char *name,
}
if (!found) {
- fprintf(stderr, "Couldn't find a collision for '%.*s', "
- "generating normal garbage, it won't match indexes\n",
+ warning(
+"cannot find a hash collision for '%.*s', generating garbage, it won't match indexes",
val->len, val->val);
for (i = 0; i < name_len; i++) {
char c = rand_range(94) + 33;
@@ -445,8 +450,7 @@ static void sanitize_dir_item(struct metadump_struct *md, struct extent_buffer *
if (md->sanitize_names > 1) {
buf = malloc(name_len);
if (!buf) {
- fprintf(stderr, "Couldn't sanitize name, "
- "enomem\n");
+ error("cannot sanitize name, not enough memory");
return;
}
read_extent_buffer(eb, buf, name_ptr, name_len);
@@ -455,7 +459,7 @@ static void sanitize_dir_item(struct metadump_struct *md, struct extent_buffer *
garbage = generate_garbage(name_len);
}
if (!garbage) {
- fprintf(stderr, "Couldn't sanitize name, enomem\n");
+ error("cannot sanitize name, not enough memory");
return;
}
write_extent_buffer(eb, garbage, name_ptr, name_len);
@@ -500,8 +504,7 @@ static void sanitize_inode_ref(struct metadump_struct *md,
if (md->sanitize_names > 1) {
buf = malloc(len);
if (!buf) {
- fprintf(stderr, "Couldn't sanitize name, "
- "enomem\n");
+ error("cannot sanitize name, not enough memory");
return;
}
read_extent_buffer(eb, buf, name_ptr, len);
@@ -511,7 +514,7 @@ static void sanitize_inode_ref(struct metadump_struct *md,
}
if (!garbage) {
- fprintf(stderr, "Couldn't sanitize name, enomem\n");
+ error("cannot sanitize name, not enough memory");
return;
}
write_extent_buffer(eb, garbage, name_ptr, len);
@@ -543,11 +546,11 @@ static void sanitize_name(struct metadump_struct *md, u8 *dst,
eb = alloc_dummy_eb(src->start, src->len);
if (!eb) {
- fprintf(stderr, "Couldn't sanitize name, no memory\n");
+ error("cannot sanitize name, not enough memory");
return;
}
- memcpy(eb->data, dst, eb->len);
+ memcpy(eb->data, src->data, src->len);
switch (key->type) {
case BTRFS_DIR_ITEM_KEY:
@@ -673,7 +676,7 @@ static void *dump_worker(void *data)
async->bufsize = compressBound(async->size);
async->buffer = malloc(async->bufsize);
if (!async->buffer) {
- fprintf(stderr, "Error allocating buffer\n");
+ error("not enough memory for async buffer");
pthread_mutex_lock(&md->mutex);
if (!md->error)
md->error = -ENOMEM;
@@ -705,7 +708,7 @@ static void meta_cluster_init(struct metadump_struct *md, u64 start)
md->num_items = 0;
md->num_ready = 0;
- header = &md->cluster->header;
+ header = &md->cluster.header;
header->magic = cpu_to_le64(HEADER_MAGIC);
header->bytenr = cpu_to_le64(start);
header->nritems = cpu_to_le32(0);
@@ -738,8 +741,6 @@ static void metadump_destroy(struct metadump_struct *md, int num_threads)
free(name->sub);
free(name);
}
- free(md->threads);
- free(md->cluster);
}
static int metadump_init(struct metadump_struct *md, struct btrfs_root *root,
@@ -749,14 +750,6 @@ static int metadump_init(struct metadump_struct *md, struct btrfs_root *root,
int i, ret = 0;
memset(md, 0, sizeof(*md));
- md->cluster = calloc(1, BLOCK_SIZE);
- if (!md->cluster)
- return -ENOMEM;
- md->threads = calloc(num_threads, sizeof(pthread_t));
- if (!md->threads) {
- free(md->cluster);
- return -ENOMEM;
- }
INIT_LIST_HEAD(&md->list);
INIT_LIST_HEAD(&md->ordered);
md->root = root;
@@ -796,7 +789,7 @@ static int write_zero(FILE *out, size_t size)
static int write_buffers(struct metadump_struct *md, u64 *next)
{
- struct meta_cluster_header *header = &md->cluster->header;
+ struct meta_cluster_header *header = &md->cluster.header;
struct meta_cluster_item *item;
struct async_work *async;
u64 bytenr = 0;
@@ -820,24 +813,23 @@ static int write_buffers(struct metadump_struct *md, u64 *next)
}
if (err) {
- fprintf(stderr, "One of the threads errored out %s\n",
- strerror(err));
+ error("one of the threads failed: %s", strerror(-err));
goto out;
}
/* setup and write index block */
list_for_each_entry(async, &md->ordered, ordered) {
- item = md->cluster->items + nritems;
+ item = &md->cluster.items[nritems];
item->bytenr = cpu_to_le64(async->start);
item->size = cpu_to_le32(async->bufsize);
nritems++;
}
header->nritems = cpu_to_le32(nritems);
- ret = fwrite(md->cluster, BLOCK_SIZE, 1, md->out);
+ ret = fwrite(&md->cluster, BLOCK_SIZE, 1, md->out);
if (ret != 1) {
- fprintf(stderr, "Error writing out cluster: %d\n", errno);
- return -EIO;
+ error("unable to write out cluster: %s", strerror(errno));
+ return -errno;
}
/* write buffers */
@@ -852,10 +844,10 @@ static int write_buffers(struct metadump_struct *md, u64 *next)
ret = fwrite(async->buffer, async->bufsize, 1,
md->out);
if (ret != 1) {
- err = -EIO;
+ error("unable to write out cluster: %s",
+ strerror(errno));
+ err = -errno;
ret = 0;
- fprintf(stderr, "Error writing out cluster: %d\n",
- errno);
}
free(async->buffer);
@@ -869,9 +861,9 @@ static int write_buffers(struct metadump_struct *md, u64 *next)
bytenr += size;
ret = write_zero(md->out, size);
if (ret != 1) {
- fprintf(stderr, "Error zeroing out buffer: %d\n",
- errno);
- err = -EIO;
+ error("unable to zero out buffer: %s",
+ strerror(errno));
+ err = -errno;
}
}
out:
@@ -927,7 +919,7 @@ static int flush_pending(struct metadump_struct *md, int done)
struct async_work *async = NULL;
struct extent_buffer *eb;
u64 blocksize = md->root->nodesize;
- u64 start;
+ u64 start = 0;
u64 size;
size_t offset;
int ret = 0;
@@ -969,8 +961,10 @@ static int flush_pending(struct metadump_struct *md, int done)
if (ret < size) {
free(async->buffer);
free(async);
- fprintf(stderr, "Error reading superblock\n");
- return -EIO;
+ error("unable to read superblock at %llu: %s",
+ (unsigned long long)start,
+ strerror(errno));
+ return -errno;
}
size = 0;
ret = 0;
@@ -982,8 +976,8 @@ static int flush_pending(struct metadump_struct *md, int done)
if (!extent_buffer_uptodate(eb)) {
free(async->buffer);
free(async);
- fprintf(stderr,
- "Error reading metadata block\n");
+ error("unable to read metadata block %llu",
+ (unsigned long long)start);
return -EIO;
}
copy_buffer(md, async->buffer + offset, eb);
@@ -1013,8 +1007,7 @@ static int flush_pending(struct metadump_struct *md, int done)
if (md->num_items >= ITEMS_PER_CLUSTER || done) {
ret = write_buffers(md, &start);
if (ret)
- fprintf(stderr, "Error writing buffers %d\n",
- errno);
+ error("unable to write buffers: %s", strerror(-ret));
else
meta_cluster_init(md, start);
}
@@ -1091,7 +1084,8 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
ret = add_extent(btrfs_header_bytenr(eb), root->nodesize, metadump, 0);
if (ret) {
- fprintf(stderr, "Error adding metadata block\n");
+ error("unable to add metadata block %llu: %d",
+ btrfs_header_bytenr(eb), ret);
return ret;
}
@@ -1109,8 +1103,7 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
bytenr = btrfs_disk_root_bytenr(eb, ri);
tmp = read_tree_block(root, bytenr, root->nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
- fprintf(stderr,
- "Error reading log root block\n");
+ error("unable to read log root block");
return -EIO;
}
ret = copy_tree_blocks(root, tmp, metadump, 0);
@@ -1121,7 +1114,7 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
bytenr = btrfs_node_blockptr(eb, i);
tmp = read_tree_block(root, bytenr, root->nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
- fprintf(stderr, "Error reading log block\n");
+ error("unable to read log root block");
return -EIO;
}
ret = copy_tree_blocks(root, tmp, metadump, root_tree);
@@ -1145,7 +1138,7 @@ static int copy_log_trees(struct btrfs_root *root,
if (!root->fs_info->log_root_tree ||
!root->fs_info->log_root_tree->node) {
- fprintf(stderr, "Error copying tree log, it wasn't setup\n");
+ error("unable to copy tree log, it has not been setup");
return -EIO;
}
@@ -1171,8 +1164,7 @@ static int copy_space_cache(struct btrfs_root *root,
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0) {
- fprintf(stderr, "Error searching for free space inode %d\n",
- ret);
+ error("free space inode not found: %d", ret);
return ret;
}
@@ -1182,8 +1174,7 @@ static int copy_space_cache(struct btrfs_root *root,
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(root, path);
if (ret < 0) {
- fprintf(stderr, "Error going to next leaf "
- "%d\n", ret);
+ error("cannot go to next leaf %d", ret);
return ret;
}
if (ret > 0)
@@ -1209,8 +1200,7 @@ static int copy_space_cache(struct btrfs_root *root,
num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi);
ret = add_extent(bytenr, num_bytes, metadump, 1);
if (ret) {
- fprintf(stderr, "Error adding space cache blocks %d\n",
- ret);
+ error("unable to add space cache blocks %d", ret);
btrfs_release_path(path);
return ret;
}
@@ -1239,7 +1229,7 @@ static int copy_from_extent_tree(struct metadump_struct *metadump,
ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
if (ret < 0) {
- fprintf(stderr, "Error searching extent root %d\n", ret);
+ error("extent root not found: %d", ret);
return ret;
}
ret = 0;
@@ -1250,8 +1240,7 @@ static int copy_from_extent_tree(struct metadump_struct *metadump,
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(extent_root, path);
if (ret < 0) {
- fprintf(stderr, "Error going to next leaf %d"
- "\n", ret);
+ error("cannot go to next leaf %d", ret);
break;
}
if (ret > 0) {
@@ -1270,10 +1259,18 @@ static int copy_from_extent_tree(struct metadump_struct *metadump,
}
bytenr = key.objectid;
- if (key.type == BTRFS_METADATA_ITEM_KEY)
+ if (key.type == BTRFS_METADATA_ITEM_KEY) {
num_bytes = extent_root->nodesize;
- else
+ } else {
num_bytes = key.offset;
+ }
+
+ if (num_bytes == 0) {
+ error("extent length 0 at bytenr %llu key type %d",
+ (unsigned long long)bytenr, key.type);
+ ret = -EIO;
+ break;
+ }
if (btrfs_item_size_nr(leaf, path->slots[0]) > sizeof(*ei)) {
ei = btrfs_item_ptr(leaf, path->slots[0],
@@ -1283,8 +1280,8 @@ static int copy_from_extent_tree(struct metadump_struct *metadump,
ret = add_extent(bytenr, num_bytes, metadump,
0);
if (ret) {
- fprintf(stderr, "Error adding block "
- "%d\n", ret);
+ error("unable to add block %llu: %d",
+ (unsigned long long)bytenr, ret);
break;
}
}
@@ -1292,8 +1289,8 @@ static int copy_from_extent_tree(struct metadump_struct *metadump,
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
ret = is_tree_block(extent_root, path, bytenr);
if (ret < 0) {
- fprintf(stderr, "Error checking tree block "
- "%d\n", ret);
+ error("failed to check tree block %llu: %d",
+ (unsigned long long)bytenr, ret);
break;
}
@@ -1301,15 +1298,15 @@ static int copy_from_extent_tree(struct metadump_struct *metadump,
ret = add_extent(bytenr, num_bytes, metadump,
0);
if (ret) {
- fprintf(stderr, "Error adding block "
- "%d\n", ret);
+ error("unable to add block %llu: %d",
+ (unsigned long long)bytenr, ret);
break;
}
}
ret = 0;
#else
- fprintf(stderr, "Either extent tree corruption or "
- "you haven't built with V0 support\n");
+ error(
+ "either extent tree is corrupted or you haven't built with V0 support");
ret = -EIO;
break;
#endif
@@ -1326,21 +1323,21 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
int compress_level, int sanitize, int walk_trees)
{
struct btrfs_root *root;
- struct btrfs_path *path = NULL;
+ struct btrfs_path path;
struct metadump_struct metadump;
int ret;
int err = 0;
root = open_ctree(input, 0, 0);
if (!root) {
- fprintf(stderr, "Open ctree failed\n");
+ error("open ctree failed");
return -EIO;
}
ret = metadump_init(&metadump, root, out, num_threads,
compress_level, sanitize);
if (ret) {
- fprintf(stderr, "Error initializing metadump %d\n", ret);
+ error("failed to initialize metadump: %d", ret);
close_ctree(root);
return ret;
}
@@ -1348,17 +1345,12 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
ret = add_extent(BTRFS_SUPER_INFO_OFFSET, BTRFS_SUPER_INFO_SIZE,
&metadump, 0);
if (ret) {
- fprintf(stderr, "Error adding metadata %d\n", ret);
+ error("unable to add metadata: %d", ret);
err = ret;
goto out;
}
- path = btrfs_alloc_path();
- if (!path) {
- fprintf(stderr, "Out of memory allocating path\n");
- err = -ENOMEM;
- goto out;
- }
+ btrfs_init_path(&path);
if (walk_trees) {
ret = copy_tree_blocks(root, root->fs_info->chunk_root->node,
@@ -1375,31 +1367,31 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
goto out;
}
} else {
- ret = copy_from_extent_tree(&metadump, path);
+ ret = copy_from_extent_tree(&metadump, &path);
if (ret) {
err = ret;
goto out;
}
}
- ret = copy_log_trees(root, &metadump, path);
+ ret = copy_log_trees(root, &metadump, &path);
if (ret) {
err = ret;
goto out;
}
- ret = copy_space_cache(root, &metadump, path);
+ ret = copy_space_cache(root, &metadump, &path);
out:
ret = flush_pending(&metadump, 1);
if (ret) {
if (!err)
err = ret;
- fprintf(stderr, "Error flushing pending %d\n", ret);
+ error("failed to flush pending data: %d", ret);
}
metadump_destroy(&metadump, num_threads);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
ret = close_ctree(root);
return err ? err : ret;
}
@@ -1494,8 +1486,7 @@ static int update_super(struct mdrestore_struct *mdres, u8 *buffer)
BTRFS_UUID_SIZE);
new_array_size += sizeof(*chunk);
} else {
- fprintf(stderr, "Bogus key in the sys chunk array "
- "%d\n", key.type);
+ error("bogus key in the sys array %d", key.type);
return -EIO;
}
write_ptr += sizeof(*chunk);
@@ -1659,8 +1650,9 @@ static void write_backup_supers(int fd, u8 *buf)
int ret;
if (fstat(fd, &st)) {
- fprintf(stderr, "Couldn't stat restore point, won't be able "
- "to write backup supers: %d\n", errno);
+ error(
+ "cannot stat restore point, won't be able to write backup supers: %s",
+ strerror(errno));
return;
}
@@ -1675,11 +1667,11 @@ static void write_backup_supers(int fd, u8 *buf)
ret = pwrite64(fd, buf, BTRFS_SUPER_INFO_SIZE, bytenr);
if (ret < BTRFS_SUPER_INFO_SIZE) {
if (ret < 0)
- fprintf(stderr, "Problem writing out backup "
- "super block %d, err %d\n", i, errno);
+ error(
+ "problem writing out backup super block %d: %s",
+ i, strerror(errno));
else
- fprintf(stderr, "Short write writing out "
- "backup super block\n");
+ error("short write writing out backup super block");
break;
}
}
@@ -1699,7 +1691,7 @@ static void *restore_worker(void *data)
outfd = fileno(mdres->out);
buffer = malloc(compress_size);
if (!buffer) {
- fprintf(stderr, "Error allocating buffer\n");
+ error("not enough memory for restore worker buffer");
pthread_mutex_lock(&mdres->mutex);
if (!mdres->error)
mdres->error = -ENOMEM;
@@ -1729,8 +1721,7 @@ static void *restore_worker(void *data)
ret = uncompress(buffer, (unsigned long *)&size,
async->buffer, async->bufsize);
if (ret != Z_OK) {
- fprintf(stderr, "Error decompressing %d\n",
- ret);
+ error("decompressiion failed with %d", ret);
err = -EIO;
}
outbuf = buffer;
@@ -1785,18 +1776,18 @@ static void *restore_worker(void *data)
error:
if (ret < 0) {
- fprintf(stderr, "Error writing to device %d\n",
- errno);
+ error("unable to write to device: %s",
+ strerror(errno));
err = errno;
} else {
- fprintf(stderr, "Short write\n");
+ error("short write");
err = -EIO;
}
}
} else if (async->start != BTRFS_SUPER_INFO_OFFSET) {
ret = write_data_to_disk(mdres->info, outbuf, async->start, size, 0);
if (ret) {
- printk("Error write data\n");
+ error("failed to write data");
exit(1);
}
}
@@ -1843,7 +1834,6 @@ static void mdrestore_destroy(struct mdrestore_struct *mdres, int num_threads)
pthread_cond_destroy(&mdres->cond);
pthread_mutex_destroy(&mdres->mutex);
- free(mdres->threads);
}
static int mdrestore_init(struct mdrestore_struct *mdres,
@@ -1873,14 +1863,14 @@ static int mdrestore_init(struct mdrestore_struct *mdres,
return 0;
mdres->num_threads = num_threads;
- mdres->threads = calloc(num_threads, sizeof(pthread_t));
- if (!mdres->threads)
- return -ENOMEM;
for (i = 0; i < num_threads; i++) {
- ret = pthread_create(mdres->threads + i, NULL, restore_worker,
+ ret = pthread_create(&mdres->threads[i], NULL, restore_worker,
mdres);
- if (ret)
+ if (ret) {
+ /* pthread_create returns errno directly */
+ ret = -ret;
break;
+ }
}
if (ret)
mdrestore_destroy(mdres, i + 1);
@@ -1908,7 +1898,7 @@ static int fill_mdres_info(struct mdrestore_struct *mdres,
ret = uncompress(buffer, (unsigned long *)&size,
async->buffer, async->bufsize);
if (ret != Z_OK) {
- fprintf(stderr, "Error decompressing %d\n", ret);
+ error("decompressiion failed with %d", ret);
free(buffer);
return -EIO;
}
@@ -1945,20 +1935,20 @@ static int add_cluster(struct meta_cluster *cluster,
item = &cluster->items[i];
async = calloc(1, sizeof(*async));
if (!async) {
- fprintf(stderr, "Error allocating async\n");
+ error("not enough memory for async data");
return -ENOMEM;
}
async->start = le64_to_cpu(item->bytenr);
async->bufsize = le32_to_cpu(item->size);
async->buffer = malloc(async->bufsize);
if (!async->buffer) {
- fprintf(stderr, "Error allocating async buffer\n");
+ error("not enough memory for async buffer");
free(async);
return -ENOMEM;
}
ret = fread(async->buffer, async->bufsize, 1, mdres->in);
if (ret != 1) {
- fprintf(stderr, "Error reading buffer %d\n", errno);
+ error("unable to read buffer: %s", strerror(errno));
free(async->buffer);
free(async);
return -EIO;
@@ -1969,7 +1959,7 @@ static int add_cluster(struct meta_cluster *cluster,
if (async->start == BTRFS_SUPER_INFO_OFFSET) {
ret = fill_mdres_info(mdres, async);
if (ret) {
- fprintf(stderr, "Error setting up restore\n");
+ error("unable to set up restore state");
pthread_mutex_unlock(&mdres->mutex);
free(async->buffer);
free(async);
@@ -1988,7 +1978,7 @@ static int add_cluster(struct meta_cluster *cluster,
bytenr += size;
ret = fread(buffer, size, 1, mdres->in);
if (ret != 1) {
- fprintf(stderr, "Error reading in buffer %d\n", errno);
+ error("failed to read buffer: %s", strerror(errno));
return -EIO;
}
}
@@ -2037,20 +2027,25 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer,
memcpy(eb->data, buffer, mdres->nodesize);
if (btrfs_header_bytenr(eb) != bytenr) {
- fprintf(stderr, "Eb bytenr doesn't match found bytenr\n");
+ error("eb bytenr does not match found bytenr: %llu != %llu",
+ (unsigned long long)btrfs_header_bytenr(eb),
+ (unsigned long long)bytenr);
ret = -EIO;
goto out;
}
if (memcmp(mdres->fsid, eb->data + offsetof(struct btrfs_header, fsid),
BTRFS_FSID_SIZE)) {
- fprintf(stderr, "Fsid doesn't match\n");
+ error("filesystem UUID of eb %llu does not match",
+ (unsigned long long)bytenr);
ret = -EIO;
goto out;
}
if (btrfs_header_owner(eb) != BTRFS_CHUNK_TREE_OBJECTID) {
- fprintf(stderr, "Does not belong to the chunk tree\n");
+ error("wrong eb %llu owner %llu",
+ (unsigned long long)bytenr,
+ (unsigned long long)btrfs_header_owner(eb));
ret = -EIO;
goto out;
}
@@ -2078,7 +2073,7 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer,
fs_chunk = malloc(sizeof(struct fs_chunk));
if (!fs_chunk) {
- fprintf(stderr, "Error allocating chunk\n");
+ error("not enough memory to allocate chunk");
ret = -ENOMEM;
break;
}
@@ -2137,13 +2132,13 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
cluster = malloc(BLOCK_SIZE);
if (!cluster) {
- fprintf(stderr, "Error allocating cluster\n");
+ error("not enough memory for cluster");
return -ENOMEM;
}
buffer = malloc(max_size);
if (!buffer) {
- fprintf(stderr, "Error allocating buffer\n");
+ error("not enough memory for buffer");
free(cluster);
return -ENOMEM;
}
@@ -2151,7 +2146,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
if (mdres->compress_method == COMPRESS_ZLIB) {
tmp = malloc(max_size);
if (!tmp) {
- fprintf(stderr, "Error allocating tmp buffer\n");
+ error("not enough memory for buffer");
free(cluster);
free(buffer);
return -ENOMEM;
@@ -2161,7 +2156,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
bytenr = current_cluster;
while (1) {
if (fseek(mdres->in, current_cluster, SEEK_SET)) {
- fprintf(stderr, "Error seeking: %d\n", errno);
+ error("seek failed: %s", strerror(errno));
ret = -EIO;
break;
}
@@ -2174,11 +2169,15 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
bytenr = 0;
continue;
}
- printf("ok this is where we screwed up?\n");
+ error(
+ "unknown state after reading cluster at %llu, probably crrupted data",
+ cluster_bytenr);
ret = -EIO;
break;
} else if (ret < 0) {
- fprintf(stderr, "Error reading image\n");
+ error("unable to read image at %llu: %s",
+ (unsigned long long)cluster_bytenr,
+ strerror(errno));
break;
}
ret = 0;
@@ -2186,7 +2185,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
header = &cluster->header;
if (le64_to_cpu(header->magic) != HEADER_MAGIC ||
le64_to_cpu(header->bytenr) != current_cluster) {
- fprintf(stderr, "bad header in metadump image\n");
+ error("bad header in metadump image");
ret = -EIO;
break;
}
@@ -2201,8 +2200,8 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
item_bytenr = le64_to_cpu(item->bytenr);
if (bufsize > max_size) {
- fprintf(stderr, "item %u size %u too big\n",
- i, bufsize);
+ error("item %u too big: %u > %u", i, bufsize,
+ max_size);
ret = -EIO;
break;
}
@@ -2210,8 +2209,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) {
- fprintf(stderr, "Error reading: %d\n",
- errno);
+ error("read error: %s", strerror(errno));
ret = -EIO;
break;
}
@@ -2221,16 +2219,16 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
(unsigned long *)&size, tmp,
bufsize);
if (ret != Z_OK) {
- fprintf(stderr, "Error decompressing "
- "%d\n", ret);
+ error("decompressiion failed with %d",
+ ret);
ret = -EIO;
break;
}
} else {
ret = fread(buffer, bufsize, 1, mdres->in);
if (ret != 1) {
- fprintf(stderr, "Error reading: %d\n",
- errno);
+ error("read error: %s",
+ strerror(errno));
ret = -EIO;
break;
}
@@ -2283,7 +2281,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
ret = fread(cluster, BLOCK_SIZE, 1, mdres->in);
if (ret <= 0) {
- fprintf(stderr, "Error reading in cluster: %d\n", errno);
+ error("unable to read cluster: %s", strerror(errno));
return -EIO;
}
ret = 0;
@@ -2291,7 +2289,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
header = &cluster->header;
if (le64_to_cpu(header->magic) != HEADER_MAGIC ||
le64_to_cpu(header->bytenr) != 0) {
- fprintf(stderr, "bad header in metadump image\n");
+ error("bad header in metadump image");
return -EIO;
}
@@ -2305,25 +2303,26 @@ 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)) {
- fprintf(stderr, "Error seeking: %d\n", errno);
+ error("seek failed: %s", strerror(errno));
return -EIO;
}
}
if (!item || le64_to_cpu(item->bytenr) != BTRFS_SUPER_INFO_OFFSET) {
- fprintf(stderr, "Huh, didn't find the super?\n");
+ error("did not find superblock at %llu",
+ le64_to_cpu(item->bytenr));
return -EINVAL;
}
buffer = malloc(le32_to_cpu(item->size));
if (!buffer) {
- fprintf(stderr, "Error allocating buffer\n");
+ error("not enough memory to allocate buffer");
return -ENOMEM;
}
ret = fread(buffer, le32_to_cpu(item->size), 1, mdres->in);
if (ret != 1) {
- fprintf(stderr, "Error reading buffer: %d\n", errno);
+ error("unable to read buffer: %s", strerror(errno));
free(buffer);
return -EIO;
}
@@ -2340,7 +2339,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
ret = uncompress(tmp, (unsigned long *)&size,
buffer, le32_to_cpu(item->size));
if (ret != Z_OK) {
- fprintf(stderr, "Error decompressing %d\n", ret);
+ error("decompressiion failed with %d", ret);
free(buffer);
free(tmp);
return -EIO;
@@ -2388,9 +2387,8 @@ static void remap_overlapping_chunks(struct mdrestore_struct *mdres)
list_del_init(&fs_chunk->list);
if (range_contains_super(fs_chunk->physical,
fs_chunk->bytes)) {
- fprintf(stderr, "Remapping a chunk that had a super "
- "mirror inside of it, clearing space cache "
- "so we don't end up with corruption\n");
+ warning(
+"remapping a chunk that had a super mirror inside of it, clearing space cache so we don't end up with corruption");
mdres->clear_space_cache = 1;
}
fs_chunk->physical = mdres->last_physical_offset;
@@ -2404,24 +2402,16 @@ static int fixup_devices(struct btrfs_fs_info *fs_info,
{
struct btrfs_trans_handle *trans;
struct btrfs_dev_item *dev_item;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct extent_buffer *leaf;
struct btrfs_root *root = fs_info->chunk_root;
struct btrfs_key key;
u64 devid, cur_devid;
int ret;
- path = btrfs_alloc_path();
- if (!path) {
- fprintf(stderr, "Error allocating path\n");
- return -ENOMEM;
- }
-
trans = btrfs_start_transaction(fs_info->tree_root, 1);
if (IS_ERR(trans)) {
- fprintf(stderr, "Error starting transaction %ld\n",
- PTR_ERR(trans));
- btrfs_free_path(path);
+ error("cannot starting transaction %ld", PTR_ERR(trans));
return PTR_ERR(trans);
}
@@ -2436,48 +2426,48 @@ static int fixup_devices(struct btrfs_fs_info *fs_info,
key.type = BTRFS_DEV_ITEM_KEY;
key.offset = 0;
+ btrfs_init_path(&path);
+
again:
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
if (ret < 0) {
- fprintf(stderr, "search failed %d\n", ret);
+ error("search failed: %d", ret);
exit(1);
}
while (1) {
- leaf = path->nodes[0];
- if (path->slots[0] >= btrfs_header_nritems(leaf)) {
- ret = btrfs_next_leaf(root, path);
+ leaf = path.nodes[0];
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, &path);
if (ret < 0) {
- fprintf(stderr, "Error going to next leaf "
- "%d\n", ret);
+ error("cannot go to next leaf %d", ret);
exit(1);
}
if (ret > 0) {
ret = 0;
break;
}
- leaf = path->nodes[0];
+ leaf = path.nodes[0];
}
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.type > BTRFS_DEV_ITEM_KEY)
break;
if (key.type != BTRFS_DEV_ITEM_KEY) {
- path->slots[0]++;
+ path.slots[0]++;
continue;
}
- dev_item = btrfs_item_ptr(leaf, path->slots[0],
+ dev_item = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_dev_item);
cur_devid = btrfs_device_id(leaf, dev_item);
if (devid != cur_devid) {
- ret = btrfs_del_item(trans, root, path);
+ ret = btrfs_del_item(trans, root, &path);
if (ret) {
- fprintf(stderr, "Error deleting item %d\n",
- ret);
+ error("cannot delete item: %d", ret);
exit(1);
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
goto again;
}
@@ -2485,13 +2475,13 @@ again:
btrfs_set_device_bytes_used(leaf, dev_item,
mdres->alloced_chunks);
btrfs_mark_buffer_dirty(leaf);
- path->slots[0]++;
+ path.slots[0]++;
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
ret = btrfs_commit_transaction(trans, fs_info->tree_root);
if (ret) {
- fprintf(stderr, "Commit failed %d\n", ret);
+ error("unable to commit transaction: %d", ret);
return ret;
}
return 0;
@@ -2514,20 +2504,20 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
} else {
in = fopen(input, "r");
if (!in) {
- perror("unable to open metadump image");
+ error("unable to open metadump image: %s",
+ strerror(errno));
return 1;
}
}
/* NOTE: open with write mode */
if (fixup_offset) {
- BUG_ON(!target);
info = open_ctree_fs_info(target, 0, 0, 0,
OPEN_CTREE_WRITES |
OPEN_CTREE_RESTORE |
OPEN_CTREE_PARTIAL);
if (!info) {
- fprintf(stderr, "%s: open ctree failed\n", __func__);
+ error("open ctree failed");
ret = -EIO;
goto failed_open;
}
@@ -2535,7 +2525,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
cluster = malloc(BLOCK_SIZE);
if (!cluster) {
- fprintf(stderr, "Error allocating cluster\n");
+ error("not enough memory for cluster");
ret = -ENOMEM;
goto failed_info;
}
@@ -2543,7 +2533,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
ret = mdrestore_init(&mdrestore, in, out, old_restore, num_threads,
fixup_offset, info, multi_devices);
if (ret) {
- fprintf(stderr, "Error initializing mdrestore %d\n", ret);
+ error("failed to initialize metadata restore state: %d", ret);
goto failed_cluster;
}
@@ -2556,7 +2546,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
}
if (in != stdin && fseek(in, 0, SEEK_SET)) {
- fprintf(stderr, "Error seeking %d\n", errno);
+ error("seek failed: %s", strerror(errno));
goto out;
}
@@ -2568,13 +2558,13 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
header = &cluster->header;
if (le64_to_cpu(header->magic) != HEADER_MAGIC ||
le64_to_cpu(header->bytenr) != bytenr) {
- fprintf(stderr, "bad header in metadump image\n");
+ error("bad header in metadump image");
ret = -EIO;
break;
}
ret = add_cluster(cluster, &mdrestore, &bytenr);
if (ret) {
- fprintf(stderr, "Error adding cluster\n");
+ error("failed to add cluster: %d", ret);
break;
}
}
@@ -2589,14 +2579,14 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
OPEN_CTREE_WRITES |
OPEN_CTREE_NO_DEVICES);
if (!root) {
- fprintf(stderr, "unable to open %s\n", target);
+ error("open ctree failed in %s", target);
ret = -EIO;
goto out;
}
info = root->fs_info;
if (stat(target, &st)) {
- fprintf(stderr, "statting %s failed\n", target);
+ error("stat %s failed: %s", target, strerror(errno));
close_ctree(info->chunk_root);
free(cluster);
return 1;
@@ -2643,7 +2633,7 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
btrfs_init_path(&path);
ret = btrfs_search_slot(NULL, info->chunk_root, &key, &path, 0, 0);
if (ret) {
- fprintf(stderr, "ERROR: search key failed\n");
+ error("search key failed: %d", ret);
ret = -EIO;
goto out;
}
@@ -2654,7 +2644,9 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
devid = btrfs_device_id(leaf, dev_item);
if (devid != cur_devid) {
- printk("ERROR: devid %llu mismatch with %llu\n", devid, cur_devid);
+ error("devid mismatch: %llu != %llu",
+ (unsigned long long)devid,
+ (unsigned long long)cur_devid);
ret = -EIO;
goto out;
}
@@ -2670,12 +2662,12 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
btrfs_release_path(&path);
- printk("update disk super on %s devid=%llu\n", other_dev, devid);
+ printf("update disk super on %s devid=%llu\n", other_dev, devid);
/* update other devices' super block */
fp = open(other_dev, O_CREAT | O_RDWR, 0600);
if (fp < 0) {
- fprintf(stderr, "ERROR: could not open %s\n", other_dev);
+ error("could not open %s: %s", other_dev, strerror(errno));
ret = -EIO;
goto out;
}
@@ -2699,9 +2691,9 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
ret = pwrite64(fp, buf, BTRFS_SUPER_INFO_SIZE, BTRFS_SUPER_INFO_OFFSET);
if (ret != BTRFS_SUPER_INFO_SIZE) {
if (ret < 0)
- fprintf(stderr, "ERROR: cannot write superblock: %s\n", strerror(ret));
+ error("cannot write superblock: %s", strerror(ret));
else
- fprintf(stderr, "ERROR: cannot write superblock\n");
+ error("cannot write superblock");
ret = -EIO;
goto out;
}
@@ -2760,9 +2752,10 @@ int main(int argc, char *argv[])
break;
case 't':
num_threads = arg_strtou64(optarg);
- if (num_threads > 32) {
- error("number of threads out of range: %llu",
- (unsigned long long)num_threads);
+ if (num_threads > MAX_WORKER_THREADS) {
+ error("number of threads out of range: %llu > %d",
+ (unsigned long long)num_threads,
+ MAX_WORKER_THREADS);
return 1;
}
break;
@@ -2801,20 +2794,22 @@ int main(int argc, char *argv[])
if (create) {
if (old_restore) {
- fprintf(stderr, "Usage error: create and restore cannot be used at the same time\n");
+ error(
+ "create and restore cannot be used at the same time");
usage_error++;
}
} else {
if (walk_trees || sanitize || compress_level) {
- fprintf(stderr, "Usage error: use -w, -s, -c options for restore makes no sense\n");
+ error(
+ "useing -w, -s, -c options for restore makes no sense");
usage_error++;
}
if (multi_devices && dev_cnt < 2) {
- fprintf(stderr, "Usage error: not enough devices specified for -m option\n");
+ error("not enough devices specified for -m option");
usage_error++;
}
if (!multi_devices && dev_cnt != 1) {
- fprintf(stderr, "Usage error: accepts only 1 device without -m option\n");
+ error("accepts only 1 device without -m option");
usage_error++;
}
}
@@ -2830,7 +2825,7 @@ int main(int argc, char *argv[])
} else {
out = fopen(target, "w+");
if (!out) {
- perror("unable to create target file");
+ error("unable to create target file %s", target);
exit(1);
}
}
@@ -2850,12 +2845,12 @@ int main(int argc, char *argv[])
if (create) {
ret = check_mounted(source);
if (ret < 0) {
- fprintf(stderr, "Could not check mount status: %s\n",
- strerror(-ret));
- exit(1);
- } else if (ret)
- fprintf(stderr,
- "WARNING: The device is mounted. Make sure the filesystem is quiescent.\n");
+ warning("unable to check mount status of: %s",
+ strerror(-ret));
+ } else if (ret) {
+ warning("%s already mounted, results may be inaccurate",
+ source);
+ }
ret = create_metadump(source, out, num_threads,
compress_level, sanitize, walk_trees);
@@ -2864,7 +2859,7 @@ int main(int argc, char *argv[])
0, target, multi_devices);
}
if (ret) {
- printk("%s failed (%s)\n", (create) ? "create" : "restore",
+ error("%s failed: %s", (create) ? "create" : "restore",
strerror(errno));
goto out;
}
@@ -2879,14 +2874,13 @@ int main(int argc, char *argv[])
OPEN_CTREE_PARTIAL |
OPEN_CTREE_RESTORE);
if (!info) {
- fprintf(stderr, "unable to open %s error = %s\n",
- target, strerror(errno));
+ error("open ctree failed at %s", target);
return 1;
}
total_devs = btrfs_super_num_devices(info->super_copy);
if (total_devs != dev_cnt) {
- printk("it needs %llu devices but has only %d\n",
+ error("it needs %llu devices but has only %d",
total_devs, dev_cnt);
close_ctree(info->chunk_root);
goto out;
@@ -2897,7 +2891,7 @@ int main(int argc, char *argv[])
ret = update_disk_super_on_device(info,
argv[optind + i], (u64)i);
if (ret) {
- printk("update disk super failed devid=%d (error=%d)\n",
+ error("update disk superblock failed devid %d: %d",
i, ret);
close_ctree(info->chunk_root);
exit(1);
@@ -2910,8 +2904,7 @@ int main(int argc, char *argv[])
ret = restore_metadump(source, out, 0, num_threads, 1,
target, 1);
if (ret) {
- fprintf(stderr, "fix metadump failed (error=%d)\n",
- ret);
+ error("unable to fixup metadump: %d", ret);
exit(1);
}
}
@@ -2925,9 +2918,8 @@ out:
unlink_ret = unlink(target);
if (unlink_ret)
- fprintf(stderr,
- "unlink output file failed : %s\n",
- strerror(errno));
+ error("unlink output file %s failed: %s",
+ target, strerror(errno));
}
}
diff --git a/inode-item.c b/inode-item.c
index 522d25a4..5dd79dd3 100644
--- a/inode-item.c
+++ b/inode-item.c
@@ -19,7 +19,7 @@
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
-#include "crc32c.h"
+#include "hash.h"
static int find_name_in_backref(struct btrfs_path *path, const char * name,
int name_len, struct btrfs_inode_ref **ref_ret)
@@ -64,7 +64,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
key.objectid = inode_objectid;
key.offset = ref_objectid;
- btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY);
+ key.type = BTRFS_INODE_REF_KEY;
path = btrfs_alloc_path();
if (!path)
@@ -106,8 +106,7 @@ out:
btrfs_free_path(path);
if (ret == -EMLINK) {
- if (btrfs_fs_incompat(root->fs_info,
- BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF))
+ if (btrfs_fs_incompat(root->fs_info, EXTENDED_IREF))
ret = btrfs_insert_inode_extref(trans, root, name,
name_len,
inode_objectid,
@@ -128,13 +127,13 @@ int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
struct btrfs_key found_key;
ret = btrfs_search_slot(trans, root, location, path, ins_len, cow);
- if (ret > 0 && btrfs_key_type(location) == BTRFS_ROOT_ITEM_KEY &&
+ if (ret > 0 && location->type == BTRFS_ROOT_ITEM_KEY &&
location->offset == (u64)-1 && path->slots[0] != 0) {
slot = path->slots[0] - 1;
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &found_key, slot);
if (found_key.objectid == location->objectid &&
- btrfs_key_type(&found_key) == btrfs_key_type(location)) {
+ found_key.type == location->type) {
path->slots[0]--;
return 0;
}
@@ -184,12 +183,6 @@ out:
return ret_inode_ref;
}
-static inline u64 btrfs_extref_hash(u64 parent_ino, const char *name,
- int namelen)
-{
- return (u64)btrfs_crc32c(parent_ino, name, namelen);
-}
-
static int btrfs_find_name_in_ext_backref(struct btrfs_path *path,
u64 parent_ino, const char *name, int namelen,
struct btrfs_inode_extref **extref_ret)
@@ -446,8 +439,7 @@ out:
btrfs_free_path(path);
if (search_ext_refs &&
- btrfs_fs_incompat(root->fs_info,
- BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)) {
+ btrfs_fs_incompat(root->fs_info, EXTENDED_IREF)) {
/*
* No refs were found, or we could not find the name in our ref
* array. Find and remove the extended inode ref then.
diff --git a/inode-map.c b/inode-map.c
index 9e4dcd3c..9000e69b 100644
--- a/inode-map.c
+++ b/inode-map.c
@@ -39,7 +39,9 @@ int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
u64 search_start = dirid;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
+
search_start = root->last_inode_alloc;
search_start = max((unsigned long long)search_start,
BTRFS_FIRST_FREE_OBJECTID);
diff --git a/ioctl-test.c b/ioctl-test.c
index 54fc0135..65d584be 100644
--- a/ioctl-test.c
+++ b/ioctl-test.c
@@ -1,37 +1,252 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include "kerncompat.h"
#include <stdio.h>
#include <stdlib.h>
-#include "kerncompat.h"
+
#include "ioctl.h"
+#include "ctree.h"
+
+#define LIST_32_COMPAT \
+ ONE(BTRFS_IOC_SET_RECEIVED_SUBVOL_32)
+
+#define LIST_64_COMPAT \
+ ONE(BTRFS_IOC_SEND_64)
+
+#define LIST_BASE \
+ ONE(BTRFS_IOC_SNAP_CREATE) \
+ ONE(BTRFS_IOC_DEFRAG) \
+ ONE(BTRFS_IOC_RESIZE) \
+ ONE(BTRFS_IOC_SCAN_DEV) \
+ ONE(BTRFS_IOC_TRANS_START) \
+ ONE(BTRFS_IOC_TRANS_END) \
+ ONE(BTRFS_IOC_SYNC) \
+ ONE(BTRFS_IOC_CLONE) \
+ ONE(BTRFS_IOC_ADD_DEV) \
+ ONE(BTRFS_IOC_RM_DEV) \
+ ONE(BTRFS_IOC_BALANCE) \
+ ONE(BTRFS_IOC_CLONE_RANGE) \
+ ONE(BTRFS_IOC_SUBVOL_CREATE) \
+ ONE(BTRFS_IOC_SNAP_DESTROY) \
+ ONE(BTRFS_IOC_DEFRAG_RANGE) \
+ ONE(BTRFS_IOC_TREE_SEARCH) \
+ ONE(BTRFS_IOC_TREE_SEARCH_V2) \
+ ONE(BTRFS_IOC_INO_LOOKUP) \
+ ONE(BTRFS_IOC_DEFAULT_SUBVOL) \
+ ONE(BTRFS_IOC_SPACE_INFO) \
+ ONE(BTRFS_IOC_START_SYNC) \
+ ONE(BTRFS_IOC_WAIT_SYNC) \
+ ONE(BTRFS_IOC_SNAP_CREATE_V2) \
+ ONE(BTRFS_IOC_SUBVOL_CREATE_V2) \
+ ONE(BTRFS_IOC_SUBVOL_GETFLAGS) \
+ ONE(BTRFS_IOC_SUBVOL_SETFLAGS) \
+ ONE(BTRFS_IOC_SCRUB) \
+ ONE(BTRFS_IOC_SCRUB_CANCEL) \
+ ONE(BTRFS_IOC_SCRUB_PROGRESS) \
+ ONE(BTRFS_IOC_DEV_INFO) \
+ ONE(BTRFS_IOC_FS_INFO) \
+ ONE(BTRFS_IOC_BALANCE_V2) \
+ ONE(BTRFS_IOC_BALANCE_CTL) \
+ ONE(BTRFS_IOC_BALANCE_PROGRESS) \
+ ONE(BTRFS_IOC_INO_PATHS) \
+ ONE(BTRFS_IOC_LOGICAL_INO) \
+ ONE(BTRFS_IOC_SET_RECEIVED_SUBVOL) \
+ ONE(BTRFS_IOC_SEND) \
+ ONE(BTRFS_IOC_DEVICES_READY) \
+ ONE(BTRFS_IOC_QUOTA_CTL) \
+ ONE(BTRFS_IOC_QGROUP_ASSIGN) \
+ ONE(BTRFS_IOC_QGROUP_CREATE) \
+ ONE(BTRFS_IOC_QGROUP_LIMIT) \
+ ONE(BTRFS_IOC_QUOTA_RESCAN) \
+ ONE(BTRFS_IOC_QUOTA_RESCAN_STATUS) \
+ ONE(BTRFS_IOC_QUOTA_RESCAN_WAIT) \
+ ONE(BTRFS_IOC_GET_FSLABEL) \
+ ONE(BTRFS_IOC_SET_FSLABEL) \
+ ONE(BTRFS_IOC_GET_DEV_STATS) \
+ ONE(BTRFS_IOC_DEV_REPLACE) \
+ ONE(BTRFS_IOC_FILE_EXTENT_SAME) \
+ ONE(BTRFS_IOC_GET_FEATURES) \
+ ONE(BTRFS_IOC_SET_FEATURES) \
+ ONE(BTRFS_IOC_GET_SUPPORTED_FEATURES) \
+ ONE(BTRFS_IOC_RM_DEV_V2)
-static unsigned long ioctls[] = {
- BTRFS_IOC_SNAP_CREATE,
- BTRFS_IOC_DEFRAG,
- BTRFS_IOC_RESIZE,
- BTRFS_IOC_SCAN_DEV,
- BTRFS_IOC_TRANS_START,
- BTRFS_IOC_TRANS_END,
- BTRFS_IOC_SYNC,
- BTRFS_IOC_CLONE,
- BTRFS_IOC_ADD_DEV,
- BTRFS_IOC_RM_DEV,
- BTRFS_IOC_BALANCE,
- BTRFS_IOC_SUBVOL_CREATE,
- BTRFS_IOC_SNAP_DESTROY,
- BTRFS_IOC_DEFRAG_RANGE,
- BTRFS_IOC_TREE_SEARCH,
- BTRFS_IOC_INO_LOOKUP,
- BTRFS_IOC_DEFAULT_SUBVOL,
- BTRFS_IOC_SPACE_INFO,
- BTRFS_IOC_SNAP_CREATE_V2,
- 0 };
+#define LIST \
+ LIST_BASE \
+ LIST_32_COMPAT \
+ LIST_64_COMPAT
+
+struct ioctl_number {
+ unsigned long defined;
+ unsigned long expected;
+};
+
+static struct ioctl_number expected_list[] = {
+ { BTRFS_IOC_SNAP_CREATE, 0x0050009401 },
+ { BTRFS_IOC_DEFRAG, 0x0050009402 },
+ { BTRFS_IOC_RESIZE, 0x0050009403 },
+ { BTRFS_IOC_SCAN_DEV, 0x0050009404 },
+ { BTRFS_IOC_TRANS_START, 0x0000009406 },
+ { BTRFS_IOC_TRANS_END, 0x0000009407 },
+ { BTRFS_IOC_SYNC, 0x0000009408 },
+ { BTRFS_IOC_CLONE, 0x0040049409 },
+ { BTRFS_IOC_ADD_DEV, 0x005000940a },
+ { BTRFS_IOC_RM_DEV, 0x005000940b },
+ { BTRFS_IOC_BALANCE, 0x005000940c },
+ { BTRFS_IOC_CLONE_RANGE, 0x004020940d },
+ { BTRFS_IOC_SUBVOL_CREATE, 0x005000940e },
+ { BTRFS_IOC_SNAP_DESTROY, 0x005000940f },
+ { BTRFS_IOC_DEFRAG_RANGE, 0x0040309410 },
+ { BTRFS_IOC_TREE_SEARCH, 0x00d0009411 },
+ { BTRFS_IOC_TREE_SEARCH_V2, 0x00c0709411 },
+ { BTRFS_IOC_INO_LOOKUP, 0x00d0009412 },
+ { BTRFS_IOC_DEFAULT_SUBVOL, 0x0040089413 },
+ { BTRFS_IOC_SPACE_INFO, 0x00c0109414 },
+ { BTRFS_IOC_START_SYNC, 0x0080089418 },
+ { BTRFS_IOC_WAIT_SYNC, 0x0040089416 },
+ { BTRFS_IOC_SNAP_CREATE_V2, 0x0050009417 },
+ { BTRFS_IOC_SUBVOL_CREATE_V2, 0x0050009418 },
+ { BTRFS_IOC_SUBVOL_GETFLAGS, 0x0080089419 },
+ { BTRFS_IOC_SUBVOL_SETFLAGS, 0x004008941a },
+ { BTRFS_IOC_SCRUB, 0x00c400941b },
+ { BTRFS_IOC_SCRUB_CANCEL, 0x000000941c },
+ { BTRFS_IOC_SCRUB_PROGRESS, 0x00c400941d },
+ { BTRFS_IOC_DEV_INFO, 0x00d000941e },
+ { BTRFS_IOC_FS_INFO, 0x008400941f },
+ { BTRFS_IOC_BALANCE_V2, 0x00c4009420 },
+ { BTRFS_IOC_BALANCE_CTL, 0x0040049421 },
+ { BTRFS_IOC_BALANCE_PROGRESS, 0x0084009422 },
+ { BTRFS_IOC_INO_PATHS, 0x00c0389423 },
+ { BTRFS_IOC_LOGICAL_INO, 0x00c0389424 },
+ { BTRFS_IOC_SET_RECEIVED_SUBVOL, 0x00c0c89425 },
+#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED
+ { BTRFS_IOC_SET_RECEIVED_SUBVOL_32, 0x00c0c09425 },
+#endif
+#if BITS_PER_LONG == 32
+ { BTRFS_IOC_SEND, 0x0040449426 },
+#elif BITS_PER_LONG == 64
+ { BTRFS_IOC_SEND, 0x0040489426 },
+#endif
+#ifdef BTRFS_IOC_SEND_64_COMPAT_DEFINED
+ { BTRFS_IOC_SEND_64, 0x0040489426 },
+#endif
+ { BTRFS_IOC_DEVICES_READY, 0x0090009427 },
+ { BTRFS_IOC_QUOTA_CTL, 0x00c0109428 },
+ { BTRFS_IOC_QGROUP_ASSIGN, 0x0040189429 },
+ { BTRFS_IOC_QGROUP_CREATE, 0x004010942a },
+ { BTRFS_IOC_QGROUP_LIMIT, 0x008030942b },
+ { BTRFS_IOC_QUOTA_RESCAN, 0x004040942c },
+ { BTRFS_IOC_QUOTA_RESCAN_STATUS, 0x008040942d },
+ { BTRFS_IOC_QUOTA_RESCAN_WAIT, 0x000000942e },
+ { BTRFS_IOC_GET_FSLABEL, 0x0081009431 },
+ { BTRFS_IOC_SET_FSLABEL, 0x0041009432 },
+ { BTRFS_IOC_GET_DEV_STATS, 0x00c4089434 },
+ { BTRFS_IOC_DEV_REPLACE, 0x00ca289435 },
+ { BTRFS_IOC_FILE_EXTENT_SAME, 0x00c0189436 },
+ { BTRFS_IOC_GET_FEATURES, 0x0080189439 },
+ { BTRFS_IOC_SET_FEATURES, 0x0040309439 },
+ { BTRFS_IOC_GET_SUPPORTED_FEATURES, 0x0080489439 },
+ { BTRFS_IOC_RM_DEV_V2, 0x005000943a },
+};
+
+static struct btrfs_ioctl_vol_args used_vol_args __attribute__((used));
+static struct btrfs_ioctl_vol_args_v2 used_vol_args2 __attribute__((used));
+static struct btrfs_ioctl_clone_range_args used_clone_args __attribute__((used));
+static struct btrfs_ioctl_defrag_range_args used_defrag_args __attribute__((used));
+static struct btrfs_ioctl_search_args used_search_args __attribute__((used));
+static struct btrfs_ioctl_search_args_v2 used_search_args2 __attribute__((used));
+static struct btrfs_ioctl_ino_lookup_args used_ino_lookup __attribute__((used));
+static struct btrfs_ioctl_space_args used_space_args __attribute__((used));
+static struct btrfs_ioctl_scrub_args used_scrub_args __attribute__((used));
+static struct btrfs_ioctl_dev_info_args used_dev_info_args __attribute__((used));
+static struct btrfs_ioctl_fs_info_args used_fs_info_args __attribute__((used));
+static struct btrfs_ioctl_balance_args used_balance_args __attribute__((used));
+static struct btrfs_ioctl_ino_path_args used_path_args __attribute__((used));
+static struct btrfs_ioctl_logical_ino_args used_logical_args __attribute__((used));
+static struct btrfs_ioctl_received_subvol_args used_received_args __attribute__((used));
+#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED
+static struct btrfs_ioctl_received_subvol_args_32 used_received_args32 __attribute__((used));
+#endif
+static struct btrfs_ioctl_send_args used_send_args __attribute__((used));
+#ifdef BTRFS_IOC_SEND_64_COMPAT_DEFINED
+static struct btrfs_ioctl_send_args_64 used_send_args64 __attribute__((used));
+#endif
+static struct btrfs_ioctl_quota_ctl_args used_qgctl_args __attribute__((used));
+static struct btrfs_ioctl_qgroup_assign_args used_qgassign_args __attribute__((used));
+static struct btrfs_ioctl_qgroup_create_args used_qgcreate_args __attribute__((used));
+static struct btrfs_ioctl_qgroup_limit_args used_qglimit_args __attribute__((used));
+static struct btrfs_ioctl_quota_rescan_args used_qgrescan_args __attribute__((used));
+static struct btrfs_ioctl_get_dev_stats used_dev_stats_args __attribute__((used));
+static struct btrfs_ioctl_dev_replace_args used_replace_args __attribute__((used));
+static struct btrfs_ioctl_same_args used_same_args __attribute__((used));
+static struct btrfs_ioctl_feature_flags used_feature_flags __attribute__((used));
+
+const char* value_to_string(unsigned long num)
+{
+#define ONE(x) case x: return #x;
+ switch (num) {
+ LIST_BASE
+ }
+
+ switch (num) {
+ LIST_32_COMPAT
+ }
+
+ switch (num) {
+ LIST_64_COMPAT
+ }
+#undef ONE
+ return "UNKNOWN";
+}
int main(int ac, char **av)
{
- int i = 0;
- while(ioctls[i]) {
- printf("%lu\n" ,ioctls[i]);
- i++;
+ int i;
+ int errors = 0;
+
+ value_to_string(random());
+
+ printf("Sizeof long long: %zu\n", sizeof(unsigned long long));
+ printf("Sizeof long: %zu\n", sizeof(unsigned long));
+ printf("Sizeof pointer: %zu\n", sizeof(void*));
+ printf("Alignof long long: %zu\n", __alignof__(unsigned long long));
+ printf("Alignof long: %zu\n", __alignof__(unsigned long));
+ printf("Alignof pointer: %zu\n", __alignof__(void*));
+ printf("Raw ioctl numbers:\n");
+
+#define ONE(n) printf("%-38s 0x%010lx\n", #n, (unsigned long)n);
+ LIST
+#undef ONE
+
+ for (i = 0; i < ARRAY_SIZE(expected_list); i++) {
+ if (expected_list[i].defined != expected_list[i].expected) {
+ printf("ERROR: wrong value for %s, defined=0x%lx expected=0x%lx\n",
+ value_to_string(expected_list[i].defined),
+ expected_list[i].defined,
+ expected_list[i].expected);
+ errors++;
+ }
}
- return 0;
+
+ if (!errors) {
+ printf("All ok\n");
+ } else {
+ printf("Found %d errors in definitions\n", errors);
+ }
+
+ return !!errors;
}
diff --git a/ioctl.h b/ioctl.h
index 620dd3d2..709e996f 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -25,11 +25,17 @@ extern "C" {
#include <asm/types.h>
#include <linux/ioctl.h>
+#include <stddef.h>
#ifndef __user
#define __user
#endif
+/* We don't want to include entire kerncompat.h */
+#ifndef BUILD_ASSERT
+#define BUILD_ASSERT(x)
+#endif
+
#define BTRFS_IOCTL_MAGIC 0x94
#define BTRFS_VOL_NAME_MAX 255
@@ -39,6 +45,7 @@ struct btrfs_ioctl_vol_args {
__s64 fd;
char name[BTRFS_PATH_NAME_MAX + 1];
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_vol_args) == 4096);
#define BTRFS_DEVICE_PATH_NAME_MAX 1024
@@ -65,6 +72,7 @@ struct btrfs_qgroup_limit {
__u64 rsv_referenced;
__u64 rsv_exclusive;
};
+BUILD_ASSERT(sizeof(struct btrfs_qgroup_limit) == 40);
struct btrfs_qgroup_inherit {
__u64 flags;
@@ -74,11 +82,13 @@ struct btrfs_qgroup_inherit {
struct btrfs_qgroup_limit lim;
__u64 qgroups[0];
};
+BUILD_ASSERT(sizeof(struct btrfs_qgroup_inherit) == 72);
struct btrfs_ioctl_qgroup_limit_args {
__u64 qgroupid;
struct btrfs_qgroup_limit lim;
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_qgroup_limit_args) == 48);
#define BTRFS_SUBVOL_NAME_MAX 4039
struct btrfs_ioctl_vol_args_v2 {
@@ -97,6 +107,7 @@ struct btrfs_ioctl_vol_args_v2 {
__u64 devid;
};
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_vol_args_v2) == 4096);
/*
* structure to report errors and progress to userspace, either as a
@@ -145,6 +156,7 @@ struct btrfs_ioctl_scrub_args {
/* pad to 1k */
__u64 unused[(1024-32-sizeof(struct btrfs_scrub_progress))/8];
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_scrub_args) == 1024);
#define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS 0
#define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID 1
@@ -155,6 +167,7 @@ struct btrfs_ioctl_dev_replace_start_params {
__u8 srcdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */
__u8 tgtdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_replace_start_params) == 2072);
#define BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED 0
#define BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED 1
@@ -169,6 +182,7 @@ struct btrfs_ioctl_dev_replace_status_params {
__u64 num_write_errors; /* out */
__u64 num_uncorrectable_read_errors; /* out */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_replace_status_params) == 48);
#define BTRFS_IOCTL_DEV_REPLACE_CMD_START 0
#define BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS 1
@@ -189,6 +203,7 @@ struct btrfs_ioctl_dev_replace_args {
__u64 spare[64];
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_replace_args) == 2600);
struct btrfs_ioctl_dev_info_args {
__u64 devid; /* in/out */
@@ -198,6 +213,7 @@ struct btrfs_ioctl_dev_info_args {
__u64 unused[379]; /* pad to 4k */
__u8 path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_info_args) == 4096);
struct btrfs_ioctl_fs_info_args {
__u64 max_id; /* out */
@@ -209,12 +225,14 @@ struct btrfs_ioctl_fs_info_args {
__u32 reserved32;
__u64 reserved[122]; /* pad to 1k */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_fs_info_args) == 1024);
struct btrfs_ioctl_feature_flags {
__u64 compat_flags;
__u64 compat_ro_flags;
__u64 incompat_flags;
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_feature_flags) == 24);
/* balance control ioctl modes */
#define BTRFS_BALANCE_CTL_PAUSE 1
@@ -292,6 +310,7 @@ struct btrfs_ioctl_balance_args {
__u64 unused[72]; /* pad to 1k */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_balance_args) == 1024);
#define BTRFS_INO_LOOKUP_PATH_MAX 4080
struct btrfs_ioctl_ino_lookup_args {
@@ -299,6 +318,7 @@ struct btrfs_ioctl_ino_lookup_args {
__u64 objectid;
char name[BTRFS_INO_LOOKUP_PATH_MAX];
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_ino_lookup_args) == 4096);
struct btrfs_ioctl_search_key {
/* which root are we searching. 0 is the tree of tree roots */
@@ -366,6 +386,7 @@ struct btrfs_ioctl_search_args_v2 {
* to store item */
__u64 buf[0]; /* out - found items */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_search_args_v2) == 112);
/* With a @src_length of zero, the range from @src_offset->EOF is cloned! */
struct btrfs_ioctl_clone_range_args {
@@ -373,6 +394,7 @@ struct btrfs_ioctl_clone_range_args {
__u64 src_offset, src_length;
__u64 dest_offset;
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_clone_range_args) == 32);
/* flags for the defrag range ioctl */
#define BTRFS_DEFRAG_RANGE_COMPRESS 1
@@ -402,6 +424,7 @@ struct btrfs_ioctl_same_args {
__u32 reserved2;
struct btrfs_ioctl_same_extent_info info[0];
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_same_args) == 24);
struct btrfs_ioctl_defrag_range_args {
/* start of the defrag operation */
@@ -433,6 +456,7 @@ struct btrfs_ioctl_defrag_range_args {
/* spare for later */
__u32 unused[4];
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_defrag_range_args) == 48);
struct btrfs_ioctl_space_info {
__u64 flags;
@@ -445,6 +469,7 @@ struct btrfs_ioctl_space_args {
__u64 total_spaces;
struct btrfs_ioctl_space_info spaces[0];
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_space_args) == 16);
struct btrfs_data_container {
__u32 bytes_left; /* out -- bytes not needed to deliver output */
@@ -461,6 +486,7 @@ struct btrfs_ioctl_ino_path_args {
/* struct btrfs_data_container *fspath; out */
__u64 fspath; /* out */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_ino_path_args) == 56);
struct btrfs_ioctl_logical_ino_args {
__u64 logical; /* in */
@@ -500,8 +526,9 @@ struct btrfs_ioctl_get_dev_stats {
/* out values: */
__u64 values[BTRFS_DEV_STAT_VALUES_MAX];
- __u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k */
+ __u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k + 8B */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_get_dev_stats) == 1032);
/* BTRFS_IOC_SNAP_CREATE is no longer used by the btrfs command */
#define BTRFS_QUOTA_CTL_ENABLE 1
@@ -511,12 +538,14 @@ struct btrfs_ioctl_quota_ctl_args {
__u64 cmd;
__u64 status;
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_quota_ctl_args) == 16);
struct btrfs_ioctl_quota_rescan_args {
__u64 flags;
__u64 progress;
__u64 reserved[6];
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_quota_rescan_args) == 64);
struct btrfs_ioctl_qgroup_assign_args {
__u64 assign;
@@ -528,6 +557,8 @@ struct btrfs_ioctl_qgroup_create_args {
__u64 create;
__u64 qgroupid;
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_qgroup_create_args) == 16);
+
struct btrfs_ioctl_timespec {
__u64 sec;
__u32 nsec;
@@ -542,6 +573,39 @@ struct btrfs_ioctl_received_subvol_args {
__u64 flags; /* in */
__u64 reserved[16]; /* in */
};
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_received_subvol_args) == 200);
+
+/*
+ * If we have a 32-bit userspace and 64-bit kernel, then the UAPI
+ * structures are incorrect, as the timespec structure from userspace
+ * is 4 bytes too small. We define these alternatives here for backward
+ * compatibility, the kernel understands both values.
+ */
+
+/*
+ * Structure size is different on 32bit and 64bit, has some padding if the
+ * structure is embedded. Packing makes sure the size is same on both, but will
+ * be misaligned on 64bit.
+ *
+ * NOTE: do not use in your code, this is for testing only
+ */
+struct btrfs_ioctl_timespec_32 {
+ __u64 sec;
+ __u32 nsec;
+} __attribute__ ((__packed__));
+
+struct btrfs_ioctl_received_subvol_args_32 {
+ char uuid[BTRFS_UUID_SIZE]; /* in */
+ __u64 stransid; /* in */
+ __u64 rtransid; /* out */
+ struct btrfs_ioctl_timespec_32 stime; /* in */
+ struct btrfs_ioctl_timespec_32 rtime; /* out */
+ __u64 flags; /* in */
+ __u64 reserved[16]; /* in */
+} __attribute__ ((__packed__));
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_received_subvol_args_32) == 192);
+
+#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED 1
/*
* Caller doesn't want file data in the send stream, even if the
@@ -576,6 +640,37 @@ struct btrfs_ioctl_send_args {
__u64 flags; /* in */
__u64 reserved[4]; /* in */
};
+/*
+ * Size of structure depends on pointer width, was not caught in the early
+ * days. Kernel handles pointer width differences transparently.
+ */
+BUILD_ASSERT(sizeof(__u64 *) == 8
+ ? sizeof(struct btrfs_ioctl_send_args) == 72
+ : (sizeof(void *) == 4
+ ? sizeof(struct btrfs_ioctl_send_args) == 68
+ : 0));
+
+/*
+ * Different pointer width leads to structure size change. Kernel should accept
+ * both ioctl values (derived from the structures) for backward compatibility.
+ * Size of this structure is same on 32bit and 64bit though.
+ *
+ * NOTE: do not use in your code, this is for testing only
+ */
+struct btrfs_ioctl_send_args_64 {
+ __s64 send_fd; /* in */
+ __u64 clone_sources_count; /* in */
+ union {
+ __u64 __user *clone_sources; /* in */
+ __u64 __clone_sources_alignment;
+ };
+ __u64 parent_root; /* in */
+ __u64 flags; /* in */
+ __u64 reserved[4]; /* in */
+} __attribute__((packed));
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_send_args_64) == 72);
+
+#define BTRFS_IOC_SEND_64_COMPAT_DEFINED 1
/* Error codes as returned by the kernel */
enum btrfs_err_code {
@@ -688,6 +783,17 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
struct btrfs_ioctl_logical_ino_args)
#define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
struct btrfs_ioctl_received_subvol_args)
+
+#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED
+#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32 _IOWR(BTRFS_IOCTL_MAGIC, 37, \
+ struct btrfs_ioctl_received_subvol_args_32)
+#endif
+
+#ifdef BTRFS_IOC_SEND_64_COMPAT_DEFINED
+#define BTRFS_IOC_SEND_64 _IOW(BTRFS_IOCTL_MAGIC, 38, \
+ struct btrfs_ioctl_send_args_64)
+#endif
+
#define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
struct btrfs_ioctl_vol_args)
diff --git a/kerncompat.h b/kerncompat.h
index 8b9a84c5..1493cad8 100644
--- a/kerncompat.h
+++ b/kerncompat.h
@@ -68,6 +68,14 @@
#define ULONG_MAX (~0UL)
#endif
+#define __token_glue(a,b,c) ___token_glue(a,b,c)
+#define ___token_glue(a,b,c) a ## b ## c
+#ifdef DEBUG_BUILD_CHECKS
+#define BUILD_ASSERT(x) extern int __token_glue(compile_time_assert_,__LINE__,__COUNTER__)[1-2*!(x)] __attribute__((unused))
+#else
+#define BUILD_ASSERT(x)
+#endif
+
#ifndef BTRFS_DISABLE_BACKTRACE
#define MAX_BACKTRACE 16
static inline void print_trace(void)
@@ -78,26 +86,35 @@ static inline void print_trace(void)
size = backtrace(array, MAX_BACKTRACE);
backtrace_symbols_fd(array, size, 2);
}
+#endif
-static inline void assert_trace(const char *assertion, const char *filename,
- const char *func, unsigned line, int val)
+static inline void warning_trace(const char *assertion, const char *filename,
+ const char *func, unsigned line, long val)
{
- if (val)
+ if (!val)
return;
- if (assertion)
- fprintf(stderr, "%s:%d: %s: Assertion `%s` failed.\n",
- filename, line, func, assertion);
- else
- fprintf(stderr, "%s:%d: %s: Assertion failed.\n", filename,
- line, func);
+ fprintf(stderr,
+ "%s:%d: %s: Warning: assertion `%s` failed, value %ld\n",
+ filename, line, func, assertion, val);
+#ifndef BTRFS_DISABLE_BACKTRACE
print_trace();
- exit(1);
+#endif
}
-#define BUG() assert_trace(NULL, __FILE__, __func__, __LINE__, 0)
-#else
-#define BUG() assert(0)
+static inline void bugon_trace(const char *assertion, const char *filename,
+ const char *func, unsigned line, long val)
+{
+ if (!val)
+ return;
+ fprintf(stderr,
+ "%s:%d: %s: BUG_ON `%s` triggered, value %ld\n",
+ filename, line, func, assertion, val);
+#ifndef BTRFS_DISABLE_BACKTRACE
+ print_trace();
#endif
+ abort();
+ exit(1);
+}
#ifdef __CHECKER__
#define __force __attribute__((force))
@@ -236,11 +253,16 @@ static inline long PTR_ERR(const void *ptr)
return (long) ptr;
}
-static inline long IS_ERR(const void *ptr)
+static inline int IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
+static inline int IS_ERR_OR_NULL(const void *ptr)
+{
+ return !ptr || IS_ERR(ptr);
+}
+
/*
* This looks more complex than it should be. But we need to
* get the type for the ~ right in round_down (it needs to be
@@ -269,19 +291,26 @@ static inline long IS_ERR(const void *ptr)
#define vfree(x) free(x)
#ifndef BTRFS_DISABLE_BACKTRACE
-#define BUG_ON(c) assert_trace(#c, __FILE__, __func__, __LINE__, !(c))
-#else
-#define BUG_ON(c) assert(!(c))
-#endif
-
-#define WARN_ON(c) BUG_ON(c)
-
-#ifndef BTRFS_DISABLE_BACKTRACE
-#define ASSERT(c) assert_trace(#c, __FILE__, __func__, __LINE__, (c))
+static inline void assert_trace(const char *assertion, const char *filename,
+ const char *func, unsigned line, long val)
+{
+ if (val)
+ return;
+ fprintf(stderr,
+ "%s:%d: %s: Assertion `%s` failed, value %ld\n",
+ filename, line, func, assertion, val);
+ abort();
+ exit(1);
+}
+#define ASSERT(c) assert_trace(#c, __FILE__, __func__, __LINE__, (long)(c))
#else
#define ASSERT(c) assert(c)
#endif
+#define BUG_ON(c) bugon_trace(#c, __FILE__, __func__, __LINE__, (long)(c))
+#define BUG() BUG_ON(1)
+#define WARN_ON(c) warning_trace(#c, __FILE__, __func__, __LINE__, (long)(c))
+
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
diff --git a/bitops.h b/kernel-lib/bitops.h
index 5b35f9fc..5b35f9fc 100644
--- a/bitops.h
+++ b/kernel-lib/bitops.h
diff --git a/crc32c.c b/kernel-lib/crc32c.c
index dfa4e6c1..29fd01d4 100644
--- a/crc32c.c
+++ b/kernel-lib/crc32c.c
@@ -218,5 +218,9 @@ u32 __crc32c_le(u32 crc, unsigned char const *data, size_t length)
u32 crc32c_le(u32 crc, unsigned char const *data, size_t length)
{
+ /* Use by-byte access for unaligned buffers */
+ if ((unsigned long)data % sizeof(unsigned long))
+ return __crc32c_le(crc, data, length);
+
return crc_function(crc, data, length);
}
diff --git a/crc32c.h b/kernel-lib/crc32c.h
index c552ef6e..c552ef6e 100644
--- a/crc32c.h
+++ b/kernel-lib/crc32c.h
diff --git a/interval_tree_generic.h b/kernel-lib/interval_tree_generic.h
index e26c7322..e26c7322 100644
--- a/interval_tree_generic.h
+++ b/kernel-lib/interval_tree_generic.h
diff --git a/list.h b/kernel-lib/list.h
index db7a58c7..db7a58c7 100644
--- a/list.h
+++ b/kernel-lib/list.h
diff --git a/list_sort.c b/kernel-lib/list_sort.c
index 9e2c2024..9e2c2024 100644
--- a/list_sort.c
+++ b/kernel-lib/list_sort.c
diff --git a/list_sort.h b/kernel-lib/list_sort.h
index 987cd5c4..987cd5c4 100644
--- a/list_sort.h
+++ b/kernel-lib/list_sort.h
diff --git a/radix-tree.c b/kernel-lib/radix-tree.c
index f259ab56..f259ab56 100644
--- a/radix-tree.c
+++ b/kernel-lib/radix-tree.c
diff --git a/radix-tree.h b/kernel-lib/radix-tree.h
index bf96d839..bf96d839 100644
--- a/radix-tree.h
+++ b/kernel-lib/radix-tree.h
diff --git a/rbtree.c b/kernel-lib/rbtree.c
index 92590a57..92590a57 100644
--- a/rbtree.c
+++ b/kernel-lib/rbtree.c
diff --git a/rbtree.h b/kernel-lib/rbtree.h
index 47b662a3..47b662a3 100644
--- a/rbtree.h
+++ b/kernel-lib/rbtree.h
diff --git a/rbtree_augmented.h b/kernel-lib/rbtree_augmented.h
index 5d269784..5d269784 100644
--- a/rbtree_augmented.h
+++ b/kernel-lib/rbtree_augmented.h
diff --git a/library-test.c b/library-test.c
index 142188a7..9d14bbf9 100644
--- a/library-test.c
+++ b/library-test.c
@@ -19,6 +19,7 @@
#include "kerncompat.h"
#include "version.h"
#include "send-stream.h"
+#include "btrfs-list.h"
/*
* Reduced code snippet from snapper.git/snapper/Btrfs.cc
@@ -62,8 +63,15 @@ static int test_send_stream_api() {
return ret;
}
+static int test_list_rootid() {
+ u64 treeid;
+
+ return btrfs_list_get_path_rootid(-1, &treeid);
+}
+
int main() {
test_send_stream_api();
+ test_list_rootid();
return 0;
}
diff --git a/mkfs.c b/mkfs/main.c
index 4b88987c..72834c9c 100644
--- a/mkfs.c
+++ b/mkfs/main.c
@@ -48,8 +48,8 @@ static u64 index_cnt = 2;
static int verbose = 1;
struct directory_name_entry {
- char *dir_name;
- char *path;
+ const char *dir_name;
+ const char *path;
ino_t inum;
struct list_head list;
};
@@ -344,28 +344,34 @@ static int create_data_reloc_tree(struct btrfs_trans_handle *trans,
static void print_usage(int ret)
{
- printf("usage: mkfs.btrfs [options] dev [ dev ... ]\n");
- printf("options:\n");
- printf("\t-A|--alloc-start START the offset to start the FS\n");
- printf("\t-b|--byte-count SIZE total number of bytes in the FS\n");
+ printf("Usage: mkfs.btrfs [options] dev [ dev ... ]\n");
+ printf("Options:\n");
+ printf(" allocation profiles:\n");
printf("\t-d|--data PROFILE data profile, raid0, raid1, raid5, raid6, raid10, dup or single\n");
- printf("\t-f|--force force overwrite of existing filesystem\n");
- printf("\t-l|--leafsize SIZE deprecated, alias for nodesize\n");
- printf("\t-L|--label LABEL set a label\n");
- printf("\t-m|--metadata PROFILE metadata profile, values like data profile\n");
+ printf("\t-m|--metadata PROFILE metadata profile, values like for data profile\n");
printf("\t-M|--mixed mix metadata and data together\n");
+ printf(" features:\n");
printf("\t-n|--nodesize SIZE size of btree nodes\n");
- printf("\t-s|--sectorsize SIZE min block allocation (may not mountable by current kernel)\n");
- printf("\t-r|--rootdir DIR the source directory\n");
+ printf("\t-s|--sectorsize SIZE data block size (may not be mountable by current kernel)\n");
+ printf("\t-O|--features LIST comma separated list of filesystem features (use '-O list-all' to list features)\n");
+ printf("\t-L|--label LABEL set the filesystem label\n");
+ printf("\t-U|--uuid UUID specify the filesystem UUID (must be unique)\n");
+ 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-K|--nodiscard do not perform whole device TRIM\n");
- printf("\t-O|--features LIST comma separated list of filesystem features, use '-O list-all' to list features\n");
- printf("\t-U|--uuid UUID specify the filesystem UUID\n");
+ printf("\t-f|--force force overwrite of existing filesystem\n");
+ printf(" general:\n");
printf("\t-q|--quiet no messages except errors\n");
printf("\t-V|--version print the mkfs.btrfs version and exit\n");
+ printf("\t--help print this help and exit\n");
+ printf(" deprecated:\n");
+ printf("\t-A|--alloc-start START the offset to start the filesystem\n");
+ printf("\t-l|--leafsize SIZE deprecated, alias for nodesize\n");
exit(ret);
}
-static u64 parse_profile(char *s)
+static u64 parse_profile(const char *s)
{
if (strcasecmp(s, "raid0") == 0) {
return BTRFS_BLOCK_GROUP_RAID0;
@@ -389,7 +395,7 @@ static u64 parse_profile(char *s)
return 0;
}
-static char *parse_label(char *input)
+static char *parse_label(const char *input)
{
int len = strlen(input);
@@ -415,7 +421,7 @@ static int add_directory_items(struct btrfs_trans_handle *trans,
location.objectid = objectid;
location.offset = 0;
- btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY);
+ location.type = BTRFS_INODE_ITEM_KEY;
if (S_ISDIR(st->st_mode))
filetype = BTRFS_FT_DIR;
@@ -494,11 +500,11 @@ static int fill_inode_item(struct btrfs_trans_handle *trans,
static int directory_select(const struct direct *entry)
{
- if ((strncmp(entry->d_name, ".", entry->d_reclen) == 0) ||
- (strncmp(entry->d_name, "..", entry->d_reclen) == 0))
+ if (entry->d_name[0] == '.' &&
+ (entry->d_name[1] == 0 ||
+ (entry->d_name[1] == '.' && entry->d_name[2] == 0)))
return 0;
- else
- return 1;
+ return 1;
}
static void free_namelist(struct direct **files, int count)
@@ -513,7 +519,7 @@ static void free_namelist(struct direct **files, int count)
free(files);
}
-static u64 calculate_dir_inode_size(char *dirname)
+static u64 calculate_dir_inode_size(const char *dirname)
{
int count, i;
struct direct **files, *cur_file;
@@ -534,12 +540,11 @@ static u64 calculate_dir_inode_size(char *dirname)
static int add_inode_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
- struct stat *st, char *name,
+ struct stat *st, const char *name,
u64 self_objectid, ino_t parent_inum,
int dir_index_cnt, struct btrfs_inode_item *inode_ret)
{
int ret;
- struct btrfs_key inode_key;
struct btrfs_inode_item btrfs_inode;
u64 objectid;
u64 inode_size = 0;
@@ -552,10 +557,6 @@ static int add_inode_items(struct btrfs_trans_handle *trans,
btrfs_set_stack_inode_size(&btrfs_inode, inode_size);
}
- inode_key.objectid = objectid;
- inode_key.offset = 0;
- btrfs_set_key_type(&inode_key, BTRFS_INODE_ITEM_KEY);
-
ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
*inode_ret = btrfs_inode;
@@ -779,7 +780,7 @@ end:
return ret;
}
-static char *make_path(char *dir, char *name)
+static char *make_path(const char *dir, const char *name)
{
char *path;
@@ -794,7 +795,7 @@ static char *make_path(char *dir, char *name)
}
static int traverse_directory(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, char *dir_name,
+ struct btrfs_root *root, const char *dir_name,
struct directory_name_entry *dir_head, int out_fd)
{
int ret = 0;
@@ -808,7 +809,7 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
struct direct *cur_file;
ino_t parent_inum, cur_inum;
ino_t highest_inum = 0;
- char *parent_dir_name;
+ const char *parent_dir_name;
char real_path[PATH_MAX];
struct btrfs_path path;
struct extent_buffer *leaf;
@@ -835,7 +836,7 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
root_dir_key.objectid = btrfs_root_dirid(&root->root_item);
root_dir_key.offset = 0;
- btrfs_set_key_type(&root_dir_key, BTRFS_INODE_ITEM_KEY);
+ 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);
@@ -905,8 +906,9 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
if (ret == -EEXIST) {
if (st.st_nlink <= 1) {
error(
- "item %s already exists but has wrong st_nlink %ld <= 1",
- cur_file->d_name, st.st_nlink);
+ "item %s already exists but has wrong st_nlink %lu <= 1",
+ cur_file->d_name,
+ (unsigned long)st.st_nlink);
goto fail;
}
continue;
@@ -976,15 +978,6 @@ fail_no_dir:
goto out;
}
-static int open_target(char *output_name)
-{
- int output_fd;
- output_fd = open(output_name, O_CREAT | O_RDWR,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
-
- return output_fd;
-}
-
static int create_chunks(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 num_of_meta_chunks,
u64 size_of_data,
@@ -1031,7 +1024,8 @@ static int create_chunks(struct btrfs_trans_handle *trans,
return ret;
}
-static int make_image(char *source_dir, struct btrfs_root *root, int out_fd)
+static int make_image(const char *source_dir, struct btrfs_root *root,
+ int out_fd)
{
int ret;
struct btrfs_trans_handle *trans;
@@ -1091,7 +1085,7 @@ static int ftw_add_entry_size(const char *fpath, const struct stat *st,
return 0;
}
-static u64 size_sourcedir(char *dir_name, u64 sectorsize,
+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;
@@ -1322,15 +1316,10 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
struct btrfs_root *root = fs_info->extent_root;
struct btrfs_key key;
struct btrfs_key found_key;
- struct btrfs_path *path;
+ struct btrfs_path path;
int ret = 0;
- path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
-
+ btrfs_init_path(&path);
trans = btrfs_start_transaction(root, 1);
key.objectid = 0;
@@ -1342,32 +1331,32 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
* as the rest of the loop may modify the tree, we need to
* start a new search each time.
*/
- ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 0);
if (ret < 0)
goto out;
- btrfs_item_key_to_cpu(path->nodes[0], &found_key,
- path->slots[0]);
+ btrfs_item_key_to_cpu(path.nodes[0], &found_key,
+ path.slots[0]);
if (found_key.objectid < key.objectid)
goto out;
if (found_key.type != BTRFS_BLOCK_GROUP_ITEM_KEY) {
- ret = next_block_group(root, path);
+ ret = next_block_group(root, &path);
if (ret < 0)
goto out;
if (ret > 0) {
ret = 0;
goto out;
}
- btrfs_item_key_to_cpu(path->nodes[0], &found_key,
- path->slots[0]);
+ btrfs_item_key_to_cpu(path.nodes[0], &found_key,
+ path.slots[0]);
}
- bgi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ bgi = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_block_group_item);
- if (is_temp_block_group(path->nodes[0], bgi,
+ if (is_temp_block_group(path.nodes[0], bgi,
data_profile, meta_profile,
sys_profile)) {
- u64 flags = btrfs_disk_block_group_flags(path->nodes[0],
+ u64 flags = btrfs_disk_block_group_flags(path.nodes[0],
bgi);
ret = btrfs_free_block_group(trans, fs_info,
@@ -1389,13 +1378,13 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
BTRFS_BLOCK_GROUP_DATA))
alloc->mixed -= found_key.offset;
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
key.objectid = found_key.objectid + found_key.offset;
}
out:
if (trans)
btrfs_commit_transaction(trans, root);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -1479,6 +1468,7 @@ int main(int argc, char **argv)
break;
case 'l':
warning("--leafsize is deprecated, use --nodesize");
+ /* fall through */
case 'n':
nodesize = parse_size(optarg);
nodesize_forced = 1;
@@ -1636,6 +1626,12 @@ int main(int argc, char **argv)
features))
exit(1);
+ if (sectorsize < sizeof(struct btrfs_super_block)) {
+ error("sectorsize smaller than superblock: %u < %zu",
+ sectorsize, sizeof(struct btrfs_super_block));
+ exit(1);
+ }
+
/* Check device/block_count after the nodesize is determined */
if (block_count && block_count < btrfs_min_dev_size(nodesize)) {
error("size %llu is too small to make a usable filesystem",
@@ -1697,7 +1693,8 @@ int main(int argc, char **argv)
exit(1);
}
} else {
- fd = open_target(file);
+ 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));
exit(1);
@@ -1743,7 +1740,7 @@ int main(int argc, char **argv)
mkfs_cfg.stripesize = stripesize;
mkfs_cfg.features = features;
- ret = make_btrfs(fd, &mkfs_cfg, NULL);
+ ret = make_btrfs(fd, &mkfs_cfg);
if (ret) {
error("error during mkfs: %s", strerror(-ret));
exit(1);
@@ -1899,7 +1896,7 @@ raid_groups:
char features_buf[64];
printf("Label: %s\n", label);
- printf("UUID: %s\n", fs_uuid);
+ printf("UUID: %s\n", mkfs_cfg.fs_uuid);
printf("Node size: %u\n", nodesize);
printf("Sector size: %u\n", sectorsize);
printf("Filesystem size: %s\n",
diff --git a/print-tree.c b/print-tree.c
index f97482f3..5af80e87 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -31,41 +31,26 @@ static void print_dir_item_type(struct extent_buffer *eb,
struct btrfs_dir_item *di)
{
u8 type = btrfs_dir_type(eb, di);
+ static const char* dir_item_str[] = {
+ [BTRFS_FT_REG_FILE] = "FILE",
+ [BTRFS_FT_DIR] = "DIR",
+ [BTRFS_FT_CHRDEV] = "CHRDEV",
+ [BTRFS_FT_BLKDEV] = "BLKDEV",
+ [BTRFS_FT_FIFO] = "FIFO",
+ [BTRFS_FT_SOCK] = "SOCK",
+ [BTRFS_FT_SYMLINK] = "SYMLINK",
+ [BTRFS_FT_XATTR] = "XATTR"
+ };
- switch (type) {
- case BTRFS_FT_REG_FILE:
- printf("FILE");
- break;
- case BTRFS_FT_DIR:
- printf("DIR");
- break;
- case BTRFS_FT_CHRDEV:
- printf("CHRDEV");
- break;
- case BTRFS_FT_BLKDEV:
- printf("BLKDEV");
- break;
- case BTRFS_FT_FIFO:
- printf("FIFO");
- break;
- case BTRFS_FT_SOCK:
- printf("SOCK");
- break;
- case BTRFS_FT_SYMLINK:
- printf("SYMLINK");
- break;
- case BTRFS_FT_XATTR:
- printf("XATTR");
- break;
- default:
- printf("%u", type);
- }
+ if (type < ARRAY_SIZE(dir_item_str) && dir_item_str[type])
+ printf("%s", dir_item_str[type]);
+ else
+ printf("DIR_ITEM.%u", type);
}
-static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item,
+static void print_dir_item(struct extent_buffer *eb, u32 size,
struct btrfs_dir_item *di)
{
- u32 total;
u32 cur = 0;
u32 len;
u32 name_len;
@@ -73,8 +58,7 @@ static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item,
char namebuf[BTRFS_NAME_LEN];
struct btrfs_disk_key location;
- total = btrfs_item_size(eb, item);
- while(cur < total) {
+ while (cur < size) {
btrfs_dir_item_key(eb, di, &location);
printf("\t\tlocation ");
btrfs_print_key(&location);
@@ -85,8 +69,10 @@ static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item,
data_len = btrfs_dir_data_len(eb, di);
len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf);
read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len);
- printf("\t\tnamelen %u datalen %u name: %.*s\n",
- name_len, data_len, len, namebuf);
+ printf("\t\ttransid %llu data_len %u name_len %u\n",
+ btrfs_dir_transid(eb, di),
+ data_len, name_len);
+ printf("\t\tname: %.*s\n", len, namebuf);
if (data_len) {
len = (data_len <= sizeof(namebuf))? data_len: sizeof(namebuf);
read_extent_buffer(eb, namebuf,
@@ -97,14 +83,11 @@ static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item,
di = (struct btrfs_dir_item *)((char *)di + len);
cur += len;
}
- return 0;
}
-static int print_inode_extref_item(struct extent_buffer *eb,
- struct btrfs_item *item,
- struct btrfs_inode_extref *extref)
+static void print_inode_extref_item(struct extent_buffer *eb, u32 size,
+ struct btrfs_inode_extref *extref)
{
- u32 total;
u32 cur = 0;
u32 len;
u32 name_len = 0;
@@ -112,9 +95,7 @@ static int print_inode_extref_item(struct extent_buffer *eb,
u64 parent_objid;
char namebuf[BTRFS_NAME_LEN];
- total = btrfs_item_size(eb, item);
-
- while (cur < total) {
+ while (cur < size) {
index = btrfs_inode_extref_index(eb, extref);
name_len = btrfs_inode_extref_name_len(eb, extref);
parent_objid = btrfs_inode_extref_parent(eb, extref);
@@ -133,20 +114,18 @@ static int print_inode_extref_item(struct extent_buffer *eb,
extref = (struct btrfs_inode_extref *)((char *)extref + len);
cur += len;
}
- return 0;
}
-static int print_inode_ref_item(struct extent_buffer *eb, struct btrfs_item *item,
+static void print_inode_ref_item(struct extent_buffer *eb, u32 size,
struct btrfs_inode_ref *ref)
{
- u32 total;
u32 cur = 0;
u32 len;
u32 name_len;
u64 index;
char namebuf[BTRFS_NAME_LEN];
- total = btrfs_item_size(eb, item);
- while(cur < total) {
+
+ while (cur < size) {
name_len = btrfs_inode_ref_name_len(eb, ref);
index = btrfs_inode_ref_index(eb, ref);
len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf);
@@ -157,7 +136,6 @@ static int print_inode_ref_item(struct extent_buffer *eb, struct btrfs_item *ite
ref = (struct btrfs_inode_ref *)((char *)ref + len);
cur += len;
}
- return 0;
}
/* Caller should ensure sizeof(*ret)>=21 "DATA|METADATA|RAID10" */
@@ -224,12 +202,17 @@ void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk)
char chunk_flags_str[32] = {0};
bg_flags_to_str(btrfs_chunk_type(eb, chunk), chunk_flags_str);
- printf("\t\tchunk length %llu owner %llu stripe_len %llu\n",
+ printf("\t\tlength %llu owner %llu stripe_len %llu type %s\n",
(unsigned long long)btrfs_chunk_length(eb, chunk),
(unsigned long long)btrfs_chunk_owner(eb, chunk),
- (unsigned long long)btrfs_chunk_stripe_len(eb, chunk));
- printf("\t\ttype %s num_stripes %d\n",
- chunk_flags_str, num_stripes);
+ (unsigned long long)btrfs_chunk_stripe_len(eb, chunk),
+ chunk_flags_str);
+ printf("\t\tio_align %u io_width %u sector_size %u\n",
+ btrfs_chunk_io_align(eb, chunk),
+ btrfs_chunk_io_width(eb, chunk),
+ btrfs_chunk_sector_size(eb, chunk));
+ printf("\t\tnum_stripes %hu sub_stripes %hu\n", num_stripes,
+ btrfs_chunk_sub_stripes(eb, chunk));
for (i = 0 ; i < num_stripes ; i++) {
unsigned char dev_uuid[BTRFS_UUID_SIZE];
char str_dev_uuid[BTRFS_UUID_UNPARSED_SIZE];
@@ -241,27 +224,45 @@ void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk)
printf("\t\t\tstripe %d devid %llu offset %llu\n", i,
(unsigned long long)btrfs_stripe_devid_nr(eb, chunk, i),
(unsigned long long)btrfs_stripe_offset_nr(eb, chunk, i));
- printf("\t\t\tdev uuid: %s\n", str_dev_uuid);
+ printf("\t\t\tdev_uuid %s\n", str_dev_uuid);
}
}
static void print_dev_item(struct extent_buffer *eb,
struct btrfs_dev_item *dev_item)
{
- char disk_uuid_c[BTRFS_UUID_UNPARSED_SIZE];
- u8 disk_uuid[BTRFS_UUID_SIZE];
+ char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
+ char fsid_str[BTRFS_UUID_UNPARSED_SIZE];
+ u8 uuid[BTRFS_UUID_SIZE];
+ u8 fsid[BTRFS_UUID_SIZE];
- read_extent_buffer(eb, disk_uuid,
+ read_extent_buffer(eb, uuid,
(unsigned long)btrfs_device_uuid(dev_item),
BTRFS_UUID_SIZE);
- uuid_unparse(disk_uuid, disk_uuid_c);
- printf("\t\tdev item devid %llu "
- "total_bytes %llu bytes used %Lu\n"
- "\t\tdev uuid %s\n",
+ uuid_unparse(uuid, uuid_str);
+ read_extent_buffer(eb, fsid,
+ (unsigned long)btrfs_device_fsid(dev_item),
+ BTRFS_UUID_SIZE);
+ uuid_unparse(fsid, fsid_str);
+ printf("\t\tdevid %llu total_bytes %llu bytes_used %Lu\n"
+ "\t\tio_align %u io_width %u sector_size %u type %llu\n"
+ "\t\tgeneration %llu start_offset %llu dev_group %u\n"
+ "\t\tseek_speed %hhu bandwidth %hhu\n"
+ "\t\tuuid %s\n"
+ "\t\tfsid %s\n",
(unsigned long long)btrfs_device_id(eb, dev_item),
(unsigned long long)btrfs_device_total_bytes(eb, dev_item),
(unsigned long long)btrfs_device_bytes_used(eb, dev_item),
- disk_uuid_c);
+ btrfs_device_io_align(eb, dev_item),
+ btrfs_device_io_width(eb, dev_item),
+ btrfs_device_sector_size(eb, dev_item),
+ (unsigned long long)btrfs_device_type(eb, dev_item),
+ (unsigned long long)btrfs_device_generation(eb, dev_item),
+ (unsigned long long)btrfs_device_start_offset(eb, dev_item),
+ btrfs_device_group(eb, dev_item),
+ btrfs_device_seek_speed(eb, dev_item),
+ btrfs_device_bandwidth(eb, dev_item),
+ uuid_str, fsid_str);
}
static void print_uuids(struct extent_buffer *eb)
@@ -302,6 +303,16 @@ static void compress_type_to_str(u8 compress_type, char *ret)
}
}
+static const char* file_extent_type_to_str(u8 type)
+{
+ switch (type) {
+ case BTRFS_FILE_EXTENT_INLINE: return "inline";
+ case BTRFS_FILE_EXTENT_PREALLOC: return "prealloc";
+ case BTRFS_FILE_EXTENT_REG: return "regular";
+ default: return "unknown";
+ }
+}
+
static void print_file_extent_item(struct extent_buffer *eb,
struct btrfs_item *item,
int slot,
@@ -313,12 +324,16 @@ static void print_file_extent_item(struct extent_buffer *eb,
compress_type_to_str(btrfs_file_extent_compression(eb, fi),
compress_str);
+ printf("\t\tgeneration %llu type %hhu (%s)\n",
+ btrfs_file_extent_generation(eb, fi),
+ extent_type, file_extent_type_to_str(extent_type));
+
if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
- printf("\t\tinline extent data size %u "
- "ram %u compress(%s)\n",
- btrfs_file_extent_inline_item_len(eb, item),
- btrfs_file_extent_inline_len(eb, slot, fi),
- compress_str);
+ printf("\t\tinline extent data size %u ram_bytes %u compression %hhu (%s)\n",
+ btrfs_file_extent_inline_item_len(eb, item),
+ btrfs_file_extent_inline_len(eb, slot, fi),
+ btrfs_file_extent_compression(eb, fi),
+ compress_str);
return;
}
if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
@@ -337,7 +352,9 @@ static void print_file_extent_item(struct extent_buffer *eb,
(unsigned long long)btrfs_file_extent_offset(eb, fi),
(unsigned long long)btrfs_file_extent_num_bytes(eb, fi),
(unsigned long long)btrfs_file_extent_ram_bytes(eb, fi));
- printf("\t\textent compression(%s)\n", compress_str);
+ printf("\t\textent compression %hhu (%s)\n",
+ btrfs_file_extent_compression(eb, fi),
+ compress_str);
}
/* Caller should ensure sizeof(*ret) >= 16("DATA|TREE_BLOCK") */
@@ -470,7 +487,7 @@ static void print_extent_ref_v0(struct extent_buffer *eb, int slot)
}
#endif
-static void print_root_ref(struct extent_buffer *leaf, int slot, char *tag)
+static void print_root_ref(struct extent_buffer *leaf, int slot, const char *tag)
{
struct btrfs_root_ref *ref;
char namebuf[BTRFS_NAME_LEN];
@@ -485,15 +502,14 @@ static void print_root_ref(struct extent_buffer *leaf, int slot, char *tag)
namelen, namebuf);
}
-static int count_bytes(void *buf, int len, char b)
+static int empty_uuid(const u8 *uuid)
{
- int cnt = 0;
int i;
- for (i = 0; i < len; i++) {
- if (((char*)buf)[i] == b)
- cnt++;
- }
- return cnt;
+
+ for (i = 0; i < BTRFS_UUID_SIZE; i++)
+ if (uuid[i])
+ return 0;
+ return 1;
}
/*
@@ -514,6 +530,7 @@ static void print_root(struct extent_buffer *leaf, int slot)
int len;
char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
char flags_str[32] = {0};
+ struct btrfs_key drop_key;
ri = btrfs_item_ptr(leaf, slot, struct btrfs_root_item);
len = btrfs_item_size_nr(leaf, slot);
@@ -522,24 +539,27 @@ static void print_root(struct extent_buffer *leaf, int slot)
read_extent_buffer(leaf, &root_item, (unsigned long)ri, len);
root_flags_to_str(btrfs_root_flags(&root_item), flags_str);
- printf("\t\troot data bytenr %llu level %d dirid %llu refs %u gen %llu lastsnap %llu\n",
+ printf("\t\tgeneration %llu root_dirid %llu bytenr %llu level %hhu refs %u\n",
+ (unsigned long long)btrfs_root_generation(&root_item),
+ (unsigned long long)btrfs_root_dirid(&root_item),
(unsigned long long)btrfs_root_bytenr(&root_item),
btrfs_root_level(&root_item),
- (unsigned long long)btrfs_root_dirid(&root_item),
- btrfs_root_refs(&root_item),
- (unsigned long long)btrfs_root_generation(&root_item),
- (unsigned long long)btrfs_root_last_snapshot(&root_item));
- printf("\t\tflags 0x%llx(%s)\n", btrfs_root_flags(&root_item),
- flags_str);
+ btrfs_root_refs(&root_item));
+ printf("\t\tlastsnap %llu byte_limit %llu bytes_used %llu flags 0x%llx(%s)\n",
+ (unsigned long long)btrfs_root_last_snapshot(&root_item),
+ (unsigned long long)btrfs_root_limit(&root_item),
+ (unsigned long long)btrfs_root_used(&root_item),
+ (unsigned long long)btrfs_root_flags(&root_item),
+ flags_str);
if (root_item.generation == root_item.generation_v2) {
uuid_unparse(root_item.uuid, uuid_str);
printf("\t\tuuid %s\n", uuid_str);
- if (count_bytes(root_item.parent_uuid, BTRFS_UUID_SIZE, 0) != BTRFS_UUID_SIZE) {
+ if (!empty_uuid(root_item.parent_uuid)) {
uuid_unparse(root_item.parent_uuid, uuid_str);
printf("\t\tparent_uuid %s\n", uuid_str);
}
- if (count_bytes(root_item.received_uuid, BTRFS_UUID_SIZE, 0) != BTRFS_UUID_SIZE) {
+ if (!empty_uuid(root_item.received_uuid)) {
uuid_unparse(root_item.received_uuid, uuid_str);
printf("\t\treceived_uuid %s\n", uuid_str);
}
@@ -551,14 +571,11 @@ static void print_root(struct extent_buffer *leaf, int slot)
btrfs_root_rtransid(&root_item));
}
}
- if (btrfs_root_refs(&root_item) == 0) {
- struct btrfs_key drop_key;
- btrfs_disk_key_to_cpu(&drop_key,
- &root_item.drop_progress);
- printf("\t\tdrop ");
- btrfs_print_key(&root_item.drop_progress);
- printf(" level %d\n", root_item.drop_level);
- }
+
+ btrfs_disk_key_to_cpu(&drop_key, &root_item.drop_progress);
+ printf("\t\tdrop ");
+ btrfs_print_key(&root_item.drop_progress);
+ printf(" level %hhu\n", root_item.drop_level);
}
static void print_free_space_header(struct extent_buffer *leaf, int slot)
@@ -579,132 +596,58 @@ static void print_free_space_header(struct extent_buffer *leaf, int slot)
void print_key_type(FILE *stream, u64 objectid, u8 type)
{
+ static const char* key_to_str[256] = {
+ [BTRFS_INODE_ITEM_KEY] = "INODE_ITEM",
+ [BTRFS_INODE_REF_KEY] = "INODE_REF",
+ [BTRFS_INODE_EXTREF_KEY] = "INODE_EXTREF",
+ [BTRFS_DIR_ITEM_KEY] = "DIR_ITEM",
+ [BTRFS_DIR_INDEX_KEY] = "DIR_INDEX",
+ [BTRFS_DIR_LOG_ITEM_KEY] = "DIR_LOG_ITEM",
+ [BTRFS_DIR_LOG_INDEX_KEY] = "DIR_LOG_INDEX",
+ [BTRFS_XATTR_ITEM_KEY] = "XATTR_ITEM",
+ [BTRFS_ORPHAN_ITEM_KEY] = "ORPHAN_ITEM",
+ [BTRFS_ROOT_ITEM_KEY] = "ROOT_ITEM",
+ [BTRFS_ROOT_REF_KEY] = "ROOT_REF",
+ [BTRFS_ROOT_BACKREF_KEY] = "ROOT_BACKREF",
+ [BTRFS_EXTENT_ITEM_KEY] = "EXTENT_ITEM",
+ [BTRFS_METADATA_ITEM_KEY] = "METADATA_ITEM",
+ [BTRFS_TREE_BLOCK_REF_KEY] = "TREE_BLOCK_REF",
+ [BTRFS_SHARED_BLOCK_REF_KEY] = "SHARED_BLOCK_REF",
+ [BTRFS_EXTENT_DATA_REF_KEY] = "EXTENT_DATA_REF",
+ [BTRFS_SHARED_DATA_REF_KEY] = "SHARED_DATA_REF",
+ [BTRFS_EXTENT_REF_V0_KEY] = "EXTENT_REF_V0",
+ [BTRFS_CSUM_ITEM_KEY] = "CSUM_ITEM",
+ [BTRFS_EXTENT_CSUM_KEY] = "EXTENT_CSUM",
+ [BTRFS_EXTENT_DATA_KEY] = "EXTENT_DATA",
+ [BTRFS_BLOCK_GROUP_ITEM_KEY] = "BLOCK_GROUP_ITEM",
+ [BTRFS_FREE_SPACE_INFO_KEY] = "FREE_SPACE_INFO",
+ [BTRFS_FREE_SPACE_EXTENT_KEY] = "FREE_SPACE_EXTENT",
+ [BTRFS_FREE_SPACE_BITMAP_KEY] = "FREE_SPACE_BITMAP",
+ [BTRFS_CHUNK_ITEM_KEY] = "CHUNK_ITEM",
+ [BTRFS_DEV_ITEM_KEY] = "DEV_ITEM",
+ [BTRFS_DEV_EXTENT_KEY] = "DEV_EXTENT",
+ [BTRFS_TEMPORARY_ITEM_KEY] = "TEMPORARY_ITEM",
+ [BTRFS_DEV_REPLACE_KEY] = "DEV_REPLACE",
+ [BTRFS_STRING_ITEM_KEY] = "STRING_ITEM",
+ [BTRFS_QGROUP_STATUS_KEY] = "QGROUP_STATUS",
+ [BTRFS_QGROUP_RELATION_KEY] = "QGROUP_RELATION",
+ [BTRFS_QGROUP_INFO_KEY] = "QGROUP_INFO",
+ [BTRFS_QGROUP_LIMIT_KEY] = "QGROUP_LIMIT",
+ [BTRFS_PERSISTENT_ITEM_KEY] = "PERSISTENT_ITEM",
+ [BTRFS_UUID_KEY_SUBVOL] = "UUID_KEY_SUBVOL",
+ [BTRFS_UUID_KEY_RECEIVED_SUBVOL] = "UUID_KEY_RECEIVED_SUBVOL",
+ };
+
if (type == 0 && objectid == BTRFS_FREE_SPACE_OBJECTID) {
fprintf(stream, "UNTYPED");
return;
}
- switch (type) {
- case BTRFS_INODE_ITEM_KEY:
- fprintf(stream, "INODE_ITEM");
- break;
- case BTRFS_INODE_REF_KEY:
- fprintf(stream, "INODE_REF");
- break;
- case BTRFS_INODE_EXTREF_KEY:
- fprintf(stream, "INODE_EXTREF");
- break;
- case BTRFS_DIR_ITEM_KEY:
- fprintf(stream, "DIR_ITEM");
- break;
- case BTRFS_DIR_INDEX_KEY:
- fprintf(stream, "DIR_INDEX");
- break;
- case BTRFS_DIR_LOG_ITEM_KEY:
- fprintf(stream, "DIR_LOG_ITEM");
- break;
- case BTRFS_DIR_LOG_INDEX_KEY:
- fprintf(stream, "DIR_LOG_INDEX");
- break;
- case BTRFS_XATTR_ITEM_KEY:
- fprintf(stream, "XATTR_ITEM");
- break;
- case BTRFS_ORPHAN_ITEM_KEY:
- fprintf(stream, "ORPHAN_ITEM");
- break;
- case BTRFS_ROOT_ITEM_KEY:
- fprintf(stream, "ROOT_ITEM");
- break;
- case BTRFS_ROOT_REF_KEY:
- fprintf(stream, "ROOT_REF");
- break;
- case BTRFS_ROOT_BACKREF_KEY:
- fprintf(stream, "ROOT_BACKREF");
- break;
- case BTRFS_EXTENT_ITEM_KEY:
- fprintf(stream, "EXTENT_ITEM");
- break;
- case BTRFS_METADATA_ITEM_KEY:
- fprintf(stream, "METADATA_ITEM");
- break;
- case BTRFS_TREE_BLOCK_REF_KEY:
- fprintf(stream, "TREE_BLOCK_REF");
- break;
- case BTRFS_SHARED_BLOCK_REF_KEY:
- fprintf(stream, "SHARED_BLOCK_REF");
- break;
- case BTRFS_EXTENT_DATA_REF_KEY:
- fprintf(stream, "EXTENT_DATA_REF");
- break;
- case BTRFS_SHARED_DATA_REF_KEY:
- fprintf(stream, "SHARED_DATA_REF");
- break;
- case BTRFS_EXTENT_REF_V0_KEY:
- fprintf(stream, "EXTENT_REF_V0");
- break;
- case BTRFS_CSUM_ITEM_KEY:
- fprintf(stream, "CSUM_ITEM");
- break;
- case BTRFS_EXTENT_CSUM_KEY:
- fprintf(stream, "EXTENT_CSUM");
- break;
- case BTRFS_EXTENT_DATA_KEY:
- fprintf(stream, "EXTENT_DATA");
- break;
- case BTRFS_BLOCK_GROUP_ITEM_KEY:
- fprintf(stream, "BLOCK_GROUP_ITEM");
- break;
- case BTRFS_FREE_SPACE_INFO_KEY:
- fprintf(stream, "FREE_SPACE_INFO");
- break;
- case BTRFS_FREE_SPACE_EXTENT_KEY:
- fprintf(stream, "FREE_SPACE_EXTENT");
- break;
- case BTRFS_FREE_SPACE_BITMAP_KEY:
- fprintf(stream, "FREE_SPACE_BITMAP");
- break;
- case BTRFS_CHUNK_ITEM_KEY:
- fprintf(stream, "CHUNK_ITEM");
- break;
- case BTRFS_DEV_ITEM_KEY:
- fprintf(stream, "DEV_ITEM");
- break;
- case BTRFS_DEV_EXTENT_KEY:
- fprintf(stream, "DEV_EXTENT");
- break;
- case BTRFS_BALANCE_ITEM_KEY:
- fprintf(stream, "BALANCE_ITEM");
- break;
- case BTRFS_DEV_REPLACE_KEY:
- fprintf(stream, "DEV_REPLACE");
- break;
- case BTRFS_STRING_ITEM_KEY:
- fprintf(stream, "STRING_ITEM");
- break;
- case BTRFS_QGROUP_STATUS_KEY:
- fprintf(stream, "QGROUP_STATUS");
- break;
- case BTRFS_QGROUP_RELATION_KEY:
- fprintf(stream, "QGROUP_RELATION");
- break;
- case BTRFS_QGROUP_INFO_KEY:
- fprintf(stream, "QGROUP_INFO");
- break;
- case BTRFS_QGROUP_LIMIT_KEY:
- fprintf(stream, "QGROUP_LIMIT");
- break;
- case BTRFS_DEV_STATS_KEY:
- fprintf(stream, "DEV_STATS");
- break;
- case BTRFS_UUID_KEY_SUBVOL:
- fprintf(stream, "UUID_KEY_SUBVOL");
- break;
- case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
- fprintf(stream, "UUID_KEY_RECEIVED_SUBVOL");
- break;
- default:
+
+ if (key_to_str[type])
+ fputs(key_to_str[type], stream);
+ else
fprintf(stream, "UNKNOWN.%d", type);
- };
}
void print_objectid(FILE *stream, u64 objectid, u8 type)
@@ -851,138 +794,237 @@ static void print_uuid_item(struct extent_buffer *l, unsigned long offset,
}
}
-/* Caller should ensure sizeof(*ret) >= 29 "NODATASUM|NODATACOW|READONLY" */
+/* Btrfs inode flag stringification helper */
+#define STRCAT_ONE_INODE_FLAG(flags, name, empty, dst) ({ \
+ if (flags & BTRFS_INODE_##name) { \
+ if (!empty) \
+ strcat(dst, "|"); \
+ strcat(dst, #name); \
+ empty = 0; \
+ } \
+})
+
+/*
+ * Caller should ensure sizeof(*ret) >= 102: all charactors plus '|' of
+ * BTRFS_INODE_* flags
+ */
static void inode_flags_to_str(u64 flags, char *ret)
{
int empty = 1;
- if (flags & BTRFS_INODE_NODATASUM) {
- empty = 0;
- strcpy(ret, "NODATASUM");
- }
- if (flags & BTRFS_INODE_NODATACOW) {
- if (!empty) {
- empty = 0;
- strcat(ret, "|");
- }
- strcat(ret, "NODATACOW");
- }
- if (flags & BTRFS_INODE_READONLY) {
- if (!empty) {
- empty = 0;
- strcat(ret, "|");
- }
- strcat(ret, "READONLY");
- }
+ STRCAT_ONE_INODE_FLAG(flags, NODATASUM, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, NODATACOW, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, READONLY, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, NOCOMPRESS, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, PREALLOC, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, SYNC, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, IMMUTABLE, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, APPEND, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, NODUMP, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, NOATIME, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, DIRSYNC, empty, ret);
+ STRCAT_ONE_INODE_FLAG(flags, COMPRESS, empty, ret);
if (empty)
strcat(ret, "none");
}
-void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
+static void print_timespec(struct extent_buffer *eb,
+ struct btrfs_timespec *timespec, const char *prefix,
+ const char *suffix)
+{
+ struct tm tm;
+ u64 tmp_u64;
+ u32 tmp_u32;
+ time_t tmp_time;
+ char timestamp[256];
+
+ tmp_u64 = btrfs_timespec_sec(eb, timespec);
+ tmp_u32 = btrfs_timespec_nsec(eb, timespec);
+ tmp_time = tmp_u64;
+ localtime_r(&tmp_time, &tm);
+ strftime(timestamp, sizeof(timestamp),
+ "%Y-%m-%d %H:%M:%S", &tm);
+ printf("%s%llu.%u (%s)%s", prefix, (unsigned long long)tmp_u64, tmp_u32,
+ timestamp, suffix);
+}
+
+static void print_inode_item(struct extent_buffer *eb,
+ struct btrfs_inode_item *ii)
+{
+ char flags_str[256];
+
+ memset(flags_str, 0, sizeof(flags_str));
+ inode_flags_to_str(btrfs_inode_flags(eb, ii), flags_str);
+ printf("\t\tinode generation %llu transid %llu size %llu nbytes %llu\n"
+ "\t\tblock group %llu mode %o links %u uid %u gid %u rdev %llu\n"
+ "\t\tsequence %llu flags 0x%llx(%s)\n",
+ (unsigned long long)btrfs_inode_generation(eb, ii),
+ (unsigned long long)btrfs_inode_transid(eb, ii),
+ (unsigned long long)btrfs_inode_size(eb, ii),
+ (unsigned long long)btrfs_inode_nbytes(eb, ii),
+ (unsigned long long)btrfs_inode_block_group(eb,ii),
+ btrfs_inode_mode(eb, ii),
+ btrfs_inode_nlink(eb, ii),
+ 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),
+ flags_str);
+ print_timespec(eb, btrfs_inode_atime(ii), "\t\tatime ", "\n");
+ print_timespec(eb, btrfs_inode_ctime(ii), "\t\tctime ", "\n");
+ print_timespec(eb, btrfs_inode_mtime(ii), "\t\tmtime ", "\n");
+ print_timespec(eb, btrfs_inode_otime(ii), "\t\totime ", "\n");
+}
+
+static void print_disk_balance_args(struct btrfs_disk_balance_args *ba)
+{
+ printf("\t\tprofiles %llu devid %llu target %llu flags %llu\n",
+ (unsigned long long)le64_to_cpu(ba->profiles),
+ (unsigned long long)le64_to_cpu(ba->devid),
+ (unsigned long long)le64_to_cpu(ba->target),
+ (unsigned long long)le64_to_cpu(ba->flags));
+ printf("\t\tusage_min %u usage_max %u pstart %llu pend %llu\n",
+ le32_to_cpu(ba->usage_min),
+ le32_to_cpu(ba->usage_max),
+ (unsigned long long)le64_to_cpu(ba->pstart),
+ (unsigned long long)le64_to_cpu(ba->pend));
+ printf("\t\tvstart %llu vend %llu limit_min %u limit_max %u\n",
+ (unsigned long long)le64_to_cpu(ba->vstart),
+ (unsigned long long)le64_to_cpu(ba->vend),
+ le32_to_cpu(ba->limit_min),
+ le32_to_cpu(ba->limit_max));
+ printf("\t\tstripes_min %u stripes_max %u\n",
+ le32_to_cpu(ba->stripes_min),
+ le32_to_cpu(ba->stripes_max));
+}
+
+static void print_balance_item(struct extent_buffer *eb,
+ struct btrfs_balance_item *bi)
+{
+ printf("\t\tbalance status flags %llu\n",
+ btrfs_balance_item_flags(eb, bi));
+
+ printf("\t\tDATA\n");
+ print_disk_balance_args(btrfs_balance_item_data(eb, bi));
+ printf("\t\tMETADATA\n");
+ print_disk_balance_args(btrfs_balance_item_meta(eb, bi));
+ printf("\t\tSYSTEM\n");
+ print_disk_balance_args(btrfs_balance_item_sys(eb, bi));
+}
+
+static void print_dev_stats(struct extent_buffer *eb,
+ struct btrfs_dev_stats_item *stats, u32 size)
{
int i;
- char *str;
+ u32 known = BTRFS_DEV_STAT_VALUES_MAX * sizeof(__le64);
+ __le64 *values = btrfs_dev_stats_values(eb, stats);
+
+ printf("\t\tdevice stats\n");
+ printf("\t\twrite_errs %llu read_errs %llu flush_errs %llu corruption_errs %llu generation %llu\n",
+ (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_WRITE_ERRS]),
+ (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_READ_ERRS]),
+ (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_FLUSH_ERRS]),
+ (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_CORRUPTION_ERRS]),
+ (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_GENERATION_ERRS]));
+
+ if (known < size) {
+ printf("\t\tunknown stats item bytes %u", size - known);
+ for (i = BTRFS_DEV_STAT_VALUES_MAX; i * sizeof(__le64) < size; i++) {
+ printf("\t\tunknown item %u offset %zu value %llu\n",
+ i, i * sizeof(__le64),
+ (unsigned long long)le64_to_cpu(values[i]));
+ }
+ }
+}
+
+void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *eb)
+{
struct btrfs_item *item;
- struct btrfs_dir_item *di;
- struct btrfs_inode_item *ii;
- struct btrfs_file_extent_item *fi;
- struct btrfs_block_group_item *bi;
- struct btrfs_extent_data_ref *dref;
- struct btrfs_shared_data_ref *sref;
- struct btrfs_inode_ref *iref;
- struct btrfs_inode_extref *iref2;
- struct btrfs_dev_extent *dev_extent;
struct btrfs_disk_key disk_key;
- struct btrfs_block_group_item bg_item;
- struct btrfs_free_space_info *free_info;
- struct btrfs_dir_log_item *dlog;
- struct btrfs_qgroup_info_item *qg_info;
- struct btrfs_qgroup_limit_item *qg_limit;
- struct btrfs_qgroup_status_item *qg_status;
- u32 nr = btrfs_header_nritems(l);
- u64 objectid;
- u32 type;
- char flags_str[32];
+ u32 i;
+ u32 nr;
+
+ nr = btrfs_header_nritems(eb);
printf("leaf %llu items %d free space %d generation %llu owner %llu\n",
- (unsigned long long)btrfs_header_bytenr(l), nr,
- btrfs_leaf_free_space(root, l),
- (unsigned long long)btrfs_header_generation(l),
- (unsigned long long)btrfs_header_owner(l));
- print_uuids(l);
+ (unsigned long long)btrfs_header_bytenr(eb), nr,
+ btrfs_leaf_free_space(root, eb),
+ (unsigned long long)btrfs_header_generation(eb),
+ (unsigned long long)btrfs_header_owner(eb));
+ print_uuids(eb);
fflush(stdout);
- for (i = 0 ; i < nr ; i++) {
+
+ for (i = 0; i < nr; i++) {
+ u32 item_size;
+ void *ptr;
+ u64 objectid;
+ u32 type;
+ u64 offset;
+ char flags_str[256];
+ char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
+ u8 uuid[BTRFS_UUID_SIZE];
+
item = btrfs_item_nr(i);
- btrfs_item_key(l, &disk_key, i);
+ item_size = btrfs_item_size(eb, item);
+ /* Untyped extraction of slot from btrfs_item_ptr */
+ ptr = btrfs_item_ptr(eb, i, void*);
+
+ btrfs_item_key(eb, &disk_key, i);
objectid = btrfs_disk_key_objectid(&disk_key);
type = btrfs_disk_key_type(&disk_key);
+ offset = btrfs_disk_key_offset(&disk_key);
+
printf("\titem %d ", i);
btrfs_print_key(&disk_key);
printf(" itemoff %d itemsize %d\n",
- btrfs_item_offset(l, item),
- btrfs_item_size(l, item));
+ btrfs_item_offset(eb, item),
+ btrfs_item_size(eb, item));
if (type == 0 && objectid == BTRFS_FREE_SPACE_OBJECTID)
- print_free_space_header(l, i);
+ print_free_space_header(eb, i);
switch (type) {
case BTRFS_INODE_ITEM_KEY:
- memset(flags_str, 0, sizeof(flags_str));
- ii = btrfs_item_ptr(l, i, struct btrfs_inode_item);
- inode_flags_to_str(btrfs_inode_flags(l, ii), flags_str);
- printf("\t\tinode generation %llu transid %llu size %llu nbytes %llu\n"
- "\t\tblock group %llu mode %o links %u uid %u gid %u\n"
- "\t\trdev %llu flags 0x%llx(%s)\n",
- (unsigned long long)btrfs_inode_generation(l, ii),
- (unsigned long long)btrfs_inode_transid(l, ii),
- (unsigned long long)btrfs_inode_size(l, ii),
- (unsigned long long)btrfs_inode_nbytes(l, ii),
- (unsigned long long)btrfs_inode_block_group(l,ii),
- btrfs_inode_mode(l, ii),
- btrfs_inode_nlink(l, ii),
- btrfs_inode_uid(l, ii),
- btrfs_inode_gid(l, ii),
- (unsigned long long)btrfs_inode_rdev(l,ii),
- (unsigned long long)btrfs_inode_flags(l,ii),
- flags_str);
+ print_inode_item(eb, ptr);
break;
case BTRFS_INODE_REF_KEY:
- iref = btrfs_item_ptr(l, i, struct btrfs_inode_ref);
- print_inode_ref_item(l, item, iref);
+ print_inode_ref_item(eb, item_size, ptr);
break;
case BTRFS_INODE_EXTREF_KEY:
- iref2 = btrfs_item_ptr(l, i, struct btrfs_inode_extref);
- print_inode_extref_item(l, item, iref2);
+ print_inode_extref_item(eb, item_size, ptr);
break;
case BTRFS_DIR_ITEM_KEY:
case BTRFS_DIR_INDEX_KEY:
case BTRFS_XATTR_ITEM_KEY:
- di = btrfs_item_ptr(l, i, struct btrfs_dir_item);
- print_dir_item(l, item, di);
+ print_dir_item(eb, item_size, ptr);
break;
case BTRFS_DIR_LOG_INDEX_KEY:
- case BTRFS_DIR_LOG_ITEM_KEY:
- dlog = btrfs_item_ptr(l, i, struct btrfs_dir_log_item);
+ case BTRFS_DIR_LOG_ITEM_KEY: {
+ struct btrfs_dir_log_item *dlog;
+
+ dlog = btrfs_item_ptr(eb, i, struct btrfs_dir_log_item);
printf("\t\tdir log end %Lu\n",
- (unsigned long long)btrfs_dir_log_end(l, dlog));
- break;
+ (unsigned long long)btrfs_dir_log_end(eb, dlog));
+ break;
+ }
case BTRFS_ORPHAN_ITEM_KEY:
printf("\t\torphan item\n");
break;
case BTRFS_ROOT_ITEM_KEY:
- print_root(l, i);
+ print_root(eb, i);
break;
case BTRFS_ROOT_REF_KEY:
- print_root_ref(l, i, "ref");
+ print_root_ref(eb, i, "ref");
break;
case BTRFS_ROOT_BACKREF_KEY:
- print_root_ref(l, i, "backref");
+ print_root_ref(eb, i, "backref");
break;
case BTRFS_EXTENT_ITEM_KEY:
- print_extent_item(l, i, 0);
+ print_extent_item(eb, i, 0);
break;
case BTRFS_METADATA_ITEM_KEY:
- print_extent_item(l, i, 1);
+ print_extent_item(eb, i, 1);
break;
case BTRFS_TREE_BLOCK_REF_KEY:
printf("\t\ttree block backref\n");
@@ -990,23 +1032,28 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
case BTRFS_SHARED_BLOCK_REF_KEY:
printf("\t\tshared block backref\n");
break;
- case BTRFS_EXTENT_DATA_REF_KEY:
- dref = btrfs_item_ptr(l, i, struct btrfs_extent_data_ref);
+ case BTRFS_EXTENT_DATA_REF_KEY: {
+ struct btrfs_extent_data_ref *dref;
+
+ dref = btrfs_item_ptr(eb, i, struct btrfs_extent_data_ref);
printf("\t\textent data backref root %llu "
"objectid %llu offset %llu count %u\n",
- (unsigned long long)btrfs_extent_data_ref_root(l, dref),
- (unsigned long long)btrfs_extent_data_ref_objectid(l, dref),
- (unsigned long long)btrfs_extent_data_ref_offset(l, dref),
- btrfs_extent_data_ref_count(l, dref));
+ (unsigned long long)btrfs_extent_data_ref_root(eb, dref),
+ (unsigned long long)btrfs_extent_data_ref_objectid(eb, dref),
+ (unsigned long long)btrfs_extent_data_ref_offset(eb, dref),
+ btrfs_extent_data_ref_count(eb, dref));
break;
- case BTRFS_SHARED_DATA_REF_KEY:
- sref = btrfs_item_ptr(l, i, struct btrfs_shared_data_ref);
+ }
+ case BTRFS_SHARED_DATA_REF_KEY: {
+ struct btrfs_shared_data_ref *sref;
+ sref = btrfs_item_ptr(eb, i, struct btrfs_shared_data_ref);
printf("\t\tshared data backref count %u\n",
- btrfs_shared_data_ref_count(l, sref));
+ btrfs_shared_data_ref_count(eb, sref));
break;
+ }
case BTRFS_EXTENT_REF_V0_KEY:
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
- print_extent_ref_v0(l, i);
+ print_extent_ref_v0(eb, i);
#else
BUG();
#endif
@@ -1018,14 +1065,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
printf("\t\textent csum item\n");
break;
case BTRFS_EXTENT_DATA_KEY:
- fi = btrfs_item_ptr(l, i,
- struct btrfs_file_extent_item);
- print_file_extent_item(l, item, i, fi);
+ print_file_extent_item(eb, item, i, ptr);
break;
- case BTRFS_BLOCK_GROUP_ITEM_KEY:
- bi = btrfs_item_ptr(l, i,
- struct btrfs_block_group_item);
- read_extent_buffer(l, &bg_item, (unsigned long)bi,
+ case BTRFS_BLOCK_GROUP_ITEM_KEY: {
+ struct btrfs_block_group_item bg_item;
+
+ read_extent_buffer(eb, &bg_item, (unsigned long)ptr,
sizeof(bg_item));
memset(flags_str, 0, sizeof(flags_str));
bg_flags_to_str(btrfs_block_group_flags(&bg_item),
@@ -1035,12 +1080,16 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
(unsigned long long)btrfs_block_group_chunk_objectid(&bg_item),
flags_str);
break;
- case BTRFS_FREE_SPACE_INFO_KEY:
- free_info = btrfs_item_ptr(l, i, struct btrfs_free_space_info);
+ }
+ case BTRFS_FREE_SPACE_INFO_KEY: {
+ struct btrfs_free_space_info *free_info;
+
+ free_info = btrfs_item_ptr(eb, i, struct btrfs_free_space_info);
printf("\t\tfree space info extent count %u flags %u\n",
- (unsigned)btrfs_free_space_extent_count(l, free_info),
- (unsigned)btrfs_free_space_flags(l, free_info));
+ (unsigned)btrfs_free_space_extent_count(eb, free_info),
+ (unsigned)btrfs_free_space_flags(eb, free_info));
break;
+ }
case BTRFS_FREE_SPACE_EXTENT_KEY:
printf("\t\tfree space extent\n");
break;
@@ -1048,93 +1097,134 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
printf("\t\tfree space bitmap\n");
break;
case BTRFS_CHUNK_ITEM_KEY:
- print_chunk(l, btrfs_item_ptr(l, i, struct btrfs_chunk));
+ print_chunk(eb, ptr);
break;
case BTRFS_DEV_ITEM_KEY:
- print_dev_item(l, btrfs_item_ptr(l, i,
- struct btrfs_dev_item));
+ print_dev_item(eb, ptr);
break;
- case BTRFS_DEV_EXTENT_KEY:
- dev_extent = btrfs_item_ptr(l, i,
+ case BTRFS_DEV_EXTENT_KEY: {
+ struct btrfs_dev_extent *dev_extent;
+
+ dev_extent = btrfs_item_ptr(eb, i,
struct btrfs_dev_extent);
+ read_extent_buffer(eb, uuid,
+ (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent),
+ BTRFS_UUID_SIZE);
+ uuid_unparse(uuid, uuid_str);
printf("\t\tdev extent chunk_tree %llu\n"
- "\t\tchunk objectid %llu chunk offset %llu "
- "length %llu\n",
+ "\t\tchunk_objectid %llu chunk_offset %llu "
+ "length %llu\n"
+ "\t\tchunk_tree_uuid %s\n",
(unsigned long long)
- btrfs_dev_extent_chunk_tree(l, dev_extent),
+ btrfs_dev_extent_chunk_tree(eb, dev_extent),
(unsigned long long)
- btrfs_dev_extent_chunk_objectid(l, dev_extent),
+ btrfs_dev_extent_chunk_objectid(eb, dev_extent),
(unsigned long long)
- btrfs_dev_extent_chunk_offset(l, dev_extent),
+ btrfs_dev_extent_chunk_offset(eb, dev_extent),
(unsigned long long)
- btrfs_dev_extent_length(l, dev_extent));
+ btrfs_dev_extent_length(eb, dev_extent),
+ uuid_str);
break;
- case BTRFS_QGROUP_STATUS_KEY:
- qg_status = btrfs_item_ptr(l, i,
+ }
+ case BTRFS_QGROUP_STATUS_KEY: {
+ struct btrfs_qgroup_status_item *qg_status;
+
+ qg_status = btrfs_item_ptr(eb, i,
struct btrfs_qgroup_status_item);
memset(flags_str, 0, sizeof(flags_str));
- qgroup_flags_to_str(btrfs_qgroup_status_flags(l, qg_status),
+ qgroup_flags_to_str(btrfs_qgroup_status_flags(eb, qg_status),
flags_str);
printf("\t\tversion %llu generation %llu flags %s "
"scan %lld\n",
(unsigned long long)
- btrfs_qgroup_status_version(l, qg_status),
+ btrfs_qgroup_status_version(eb, qg_status),
(unsigned long long)
- btrfs_qgroup_status_generation(l, qg_status),
+ btrfs_qgroup_status_generation(eb, qg_status),
flags_str,
(unsigned long long)
- btrfs_qgroup_status_rescan(l, qg_status));
+ btrfs_qgroup_status_rescan(eb, qg_status));
break;
+ }
case BTRFS_QGROUP_RELATION_KEY:
break;
- case BTRFS_QGROUP_INFO_KEY:
- qg_info = btrfs_item_ptr(l, i,
+ case BTRFS_QGROUP_INFO_KEY: {
+ struct btrfs_qgroup_info_item *qg_info;
+
+ qg_info = btrfs_item_ptr(eb, i,
struct btrfs_qgroup_info_item);
printf("\t\tgeneration %llu\n"
- "\t\treferenced %llu referenced compressed %llu\n"
- "\t\texclusive %llu exclusive compressed %llu\n",
+ "\t\treferenced %llu referenced_compressed %llu\n"
+ "\t\texclusive %llu exclusive_compressed %llu\n",
(unsigned long long)
- btrfs_qgroup_info_generation(l, qg_info),
+ btrfs_qgroup_info_generation(eb, qg_info),
(unsigned long long)
- btrfs_qgroup_info_referenced(l, qg_info),
+ btrfs_qgroup_info_referenced(eb, qg_info),
(unsigned long long)
- btrfs_qgroup_info_referenced_compressed(l,
+ btrfs_qgroup_info_referenced_compressed(eb,
qg_info),
(unsigned long long)
- btrfs_qgroup_info_exclusive(l, qg_info),
+ btrfs_qgroup_info_exclusive(eb, qg_info),
(unsigned long long)
- btrfs_qgroup_info_exclusive_compressed(l,
+ btrfs_qgroup_info_exclusive_compressed(eb,
qg_info));
break;
- case BTRFS_QGROUP_LIMIT_KEY:
- qg_limit = btrfs_item_ptr(l, i,
+ }
+ case BTRFS_QGROUP_LIMIT_KEY: {
+ struct btrfs_qgroup_limit_item *qg_limit;
+
+ qg_limit = btrfs_item_ptr(eb, i,
struct btrfs_qgroup_limit_item);
printf("\t\tflags %llx\n"
- "\t\tmax referenced %lld max exclusive %lld\n"
- "\t\trsv referenced %lld rsv exclusive %lld\n",
+ "\t\tmax_referenced %lld max_exclusive %lld\n"
+ "\t\trsv_referenced %lld rsv_exclusive %lld\n",
(unsigned long long)
- btrfs_qgroup_limit_flags(l, qg_limit),
+ btrfs_qgroup_limit_flags(eb, qg_limit),
(long long)
- btrfs_qgroup_limit_max_referenced(l, qg_limit),
+ btrfs_qgroup_limit_max_referenced(eb, qg_limit),
(long long)
- btrfs_qgroup_limit_max_exclusive(l, qg_limit),
+ btrfs_qgroup_limit_max_exclusive(eb, qg_limit),
(long long)
- btrfs_qgroup_limit_rsv_referenced(l, qg_limit),
+ btrfs_qgroup_limit_rsv_referenced(eb, qg_limit),
(long long)
- btrfs_qgroup_limit_rsv_exclusive(l, qg_limit));
+ btrfs_qgroup_limit_rsv_exclusive(eb, qg_limit));
break;
+ }
case BTRFS_UUID_KEY_SUBVOL:
case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
- print_uuid_item(l, btrfs_item_ptr_offset(l, i),
- btrfs_item_size_nr(l, i));
+ print_uuid_item(eb, btrfs_item_ptr_offset(eb, i),
+ btrfs_item_size_nr(eb, i));
+ break;
+ case BTRFS_STRING_ITEM_KEY: {
+ const char *str = eb->data + btrfs_item_ptr_offset(eb, i);
+
+ printf("\t\titem data %.*s\n", item_size, str);
break;
- case BTRFS_STRING_ITEM_KEY:
- /* dirty, but it's simple */
- str = l->data + btrfs_item_ptr_offset(l, i);
- printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str);
+ }
+ case BTRFS_PERSISTENT_ITEM_KEY:
+ printf("\t\tpersistent item objectid ");
+ print_objectid(stdout, objectid, BTRFS_PERSISTENT_ITEM_KEY);
+ printf(" offset %llu\n", (unsigned long long)offset);
+ switch (objectid) {
+ case BTRFS_DEV_STATS_OBJECTID:
+ print_dev_stats(eb, ptr, item_size);
+ break;
+ default:
+ printf("\t\tunknown persistent item objectid %llu\n",
+ objectid);
+ }
break;
- case BTRFS_DEV_STATS_KEY:
- printf("\t\tdevice stats\n");
+ case BTRFS_TEMPORARY_ITEM_KEY:
+ printf("\t\ttemporary item objectid ");
+ print_objectid(stdout, objectid, BTRFS_TEMPORARY_ITEM_KEY);
+ printf(" offset %llu\n", (unsigned long long)offset);
+ switch (objectid) {
+ case BTRFS_BALANCE_OBJECTID:
+ print_balance_item(eb, ptr);
+ break;
+ default:
+ printf("\t\tunknown temporary item objectid %llu\n",
+ objectid);
+ }
break;
};
fflush(stdout);
@@ -1143,11 +1233,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow)
{
- int i;
+ u32 i;
u32 nr;
u32 size;
struct btrfs_disk_key disk_key;
struct btrfs_key key;
+ struct extent_buffer *next;
if (!eb)
return;
@@ -1181,23 +1272,34 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol
return;
for (i = 0; i < nr; i++) {
- struct extent_buffer *next = read_tree_block(root,
- btrfs_node_blockptr(eb, i),
- size,
- btrfs_node_ptr_generation(eb, i));
+ next = read_tree_block(root, btrfs_node_blockptr(eb, i), size,
+ btrfs_node_ptr_generation(eb, i));
if (!extent_buffer_uptodate(next)) {
fprintf(stderr, "failed to read %llu in tree %llu\n",
(unsigned long long)btrfs_node_blockptr(eb, i),
(unsigned long long)btrfs_header_owner(eb));
continue;
}
- if (btrfs_is_leaf(next) &&
- btrfs_header_level(eb) != 1)
- BUG();
- if (btrfs_header_level(next) !=
- btrfs_header_level(eb) - 1)
- BUG();
+ if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) {
+ warning(
+ "eb corrupted: item %d eb level %d next level %d, skipping the rest",
+ i, btrfs_header_level(next),
+ btrfs_header_level(eb));
+ goto out;
+ }
+ if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) {
+ warning(
+ "eb corrupted: item %d eb level %d next level %d, skipping the rest",
+ i, btrfs_header_level(next),
+ btrfs_header_level(eb));
+ goto out;
+ }
btrfs_print_tree(root, next, 1);
free_extent_buffer(next);
}
+
+ return;
+
+out:
+ free_extent_buffer(next);
}
diff --git a/qgroup-verify.c b/qgroup-verify.c
index 66eb870a..ff46bc4c 100644
--- a/qgroup-verify.c
+++ b/qgroup-verify.c
@@ -330,9 +330,18 @@ static int find_parent_roots(struct ulist *roots, u64 parent)
* For each unresolved root, we recurse
*/
ref = find_ref_bytenr(parent);
+ if (!ref) {
+ error("bytenr ref not found for parent %llu",
+ (unsigned long long)parent);
+ return -EIO;
+ }
node = &ref->bytenr_node;
- BUG_ON(ref == NULL);
- BUG_ON(ref->bytenr != parent);
+ if (ref->bytenr != parent) {
+ error("found bytenr ref does not match parent: %llu != %llu",
+ (unsigned long long)ref->bytenr,
+ (unsigned long long)parent);
+ return -EIO;
+ }
{
/*
@@ -341,9 +350,15 @@ static int find_parent_roots(struct ulist *roots, u64 parent)
*/
struct rb_node *prev_node = rb_prev(&ref->bytenr_node);
struct ref *prev;
+
if (prev_node) {
prev = rb_entry(prev_node, struct ref, bytenr_node);
- BUG_ON(prev->bytenr == parent);
+ if (prev->bytenr == parent) {
+ error(
+ "unexpected: prev bytenr same as parent: %llu",
+ (unsigned long long)parent);
+ return -EIO;
+ }
}
}
@@ -354,6 +369,11 @@ static int find_parent_roots(struct ulist *roots, u64 parent)
if (ret < 0)
goto out;
}
+ } else if (ref->parent == ref->bytenr) {
+ /*
+ * Special loop case for tree reloc tree
+ */
+ ref->root = BTRFS_TREE_RELOC_OBJECTID;
} else {
ret = find_parent_roots(roots, ref->parent);
if (ret < 0)
@@ -563,6 +583,8 @@ static u64 resolve_one_root(u64 bytenr)
if (ref->root)
return ref->root;
+ if (ref->parent == bytenr)
+ return BTRFS_TREE_RELOC_OBJECTID;
return resolve_one_root(ref->parent);
}
@@ -733,6 +755,9 @@ static int add_refs_for_implied(struct btrfs_fs_info *info, u64 bytenr,
struct btrfs_root *root;
struct btrfs_key key;
+ /* Tree reloc tree doesn't contribute qgroup, skip it */
+ if (root_id == BTRFS_TREE_RELOC_OBJECTID)
+ return 0;
key.objectid = root_id;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1;
@@ -874,15 +899,14 @@ static int add_qgroup_relation(u64 memberid, u64 parentid)
return 0;
}
-static void read_qgroup_status(struct btrfs_path *path,
+static void read_qgroup_status(struct extent_buffer *eb, int slot,
struct counts_tree *counts)
{
struct btrfs_qgroup_status_item *status_item;
u64 flags;
- status_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
- struct btrfs_qgroup_status_item);
- flags = btrfs_qgroup_status_flags(path->nodes[0], status_item);
+ status_item = btrfs_item_ptr(eb, slot, struct btrfs_qgroup_status_item);
+ flags = btrfs_qgroup_status_flags(eb, status_item);
/*
* Since qgroup_inconsist/rescan_running is just one bit,
* assign value directly won't work.
@@ -946,7 +970,7 @@ loop:
}
if (key.type == BTRFS_QGROUP_STATUS_KEY) {
- read_qgroup_status(&path, &counts);
+ read_qgroup_status(leaf, i, &counts);
continue;
}
@@ -1455,29 +1479,24 @@ static int repair_qgroup_info(struct btrfs_fs_info *info,
int ret;
struct btrfs_root *root = info->quota_root;
struct btrfs_trans_handle *trans;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_qgroup_info_item *info_item;
struct btrfs_key key;
printf("Repair qgroup %llu/%llu\n", btrfs_qgroup_level(count->qgroupid),
btrfs_qgroup_subvid(count->qgroupid));
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- btrfs_free_path(path);
+ if (IS_ERR(trans))
return PTR_ERR(trans);
- }
+ btrfs_init_path(&path);
key.objectid = 0;
key.type = BTRFS_QGROUP_INFO_KEY;
key.offset = count->qgroupid;
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
if (ret) {
- error("Could not find disk item for qgroup %llu/%llu.\n",
+ error("could not find disk item for qgroup %llu/%llu",
btrfs_qgroup_level(count->qgroupid),
btrfs_qgroup_subvid(count->qgroupid));
if (ret > 0)
@@ -1485,27 +1504,27 @@ static int repair_qgroup_info(struct btrfs_fs_info *info,
goto out;
}
- info_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ info_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_qgroup_info_item);
- btrfs_set_qgroup_info_generation(path->nodes[0], info_item,
+ btrfs_set_qgroup_info_generation(path.nodes[0], info_item,
trans->transid);
- btrfs_set_qgroup_info_referenced(path->nodes[0], info_item,
+ btrfs_set_qgroup_info_referenced(path.nodes[0], info_item,
count->info.referenced);
- btrfs_set_qgroup_info_referenced_compressed(path->nodes[0], info_item,
+ btrfs_set_qgroup_info_referenced_compressed(path.nodes[0], info_item,
count->info.referenced_compressed);
- btrfs_set_qgroup_info_exclusive(path->nodes[0], info_item,
+ btrfs_set_qgroup_info_exclusive(path.nodes[0], info_item,
count->info.exclusive);
- btrfs_set_qgroup_info_exclusive_compressed(path->nodes[0], info_item,
+ btrfs_set_qgroup_info_exclusive_compressed(path.nodes[0], info_item,
count->info.exclusive_compressed);
- btrfs_mark_buffer_dirty(path->nodes[0]);
+ btrfs_mark_buffer_dirty(path.nodes[0]);
out:
btrfs_commit_transaction(trans, root);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
@@ -1515,53 +1534,48 @@ static int repair_qgroup_status(struct btrfs_fs_info *info)
int ret;
struct btrfs_root *root = info->quota_root;
struct btrfs_trans_handle *trans;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_key key;
struct btrfs_qgroup_status_item *status_item;
printf("Repair qgroup status item\n");
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- btrfs_free_path(path);
+ if (IS_ERR(trans))
return PTR_ERR(trans);
- }
+ btrfs_init_path(&path);
key.objectid = 0;
key.type = BTRFS_QGROUP_STATUS_KEY;
key.offset = 0;
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
if (ret) {
- error("Could not find qgroup status item\n");
+ error("could not find qgroup status item");
if (ret > 0)
ret = -ENOENT;
goto out;
}
- status_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ status_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_qgroup_status_item);
- btrfs_set_qgroup_status_flags(path->nodes[0], status_item,
+ btrfs_set_qgroup_status_flags(path.nodes[0], status_item,
BTRFS_QGROUP_STATUS_FLAG_ON);
- btrfs_set_qgroup_status_rescan(path->nodes[0], status_item, 0);
- btrfs_set_qgroup_status_generation(path->nodes[0], status_item,
+ btrfs_set_qgroup_status_rescan(path.nodes[0], status_item, 0);
+ btrfs_set_qgroup_status_generation(path.nodes[0], status_item,
trans->transid);
- btrfs_mark_buffer_dirty(path->nodes[0]);
+ btrfs_mark_buffer_dirty(path.nodes[0]);
out:
btrfs_commit_transaction(trans, root);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
int repair_qgroups(struct btrfs_fs_info *info, int *repaired)
{
- int ret;
+ int ret = 0;
struct qgroup_count *count, *tmpcount;
*repaired = 0;
diff --git a/qgroup.c b/qgroup.c
index f17fdaee..fffdbb12 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -148,7 +148,7 @@ void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column)
{
int i;
- BUG_ON(column < 0 || column > BTRFS_QGROUP_ALL);
+ ASSERT(0 <= column && column <= BTRFS_QGROUP_ALL);
if (column < BTRFS_QGROUP_ALL) {
btrfs_qgroup_columns[column].need_print = 1;
@@ -213,11 +213,12 @@ static void print_qgroup_column_add_blank(enum btrfs_qgroup_column_enum column,
static void print_qgroup_column(struct btrfs_qgroup *qgroup,
enum btrfs_qgroup_column_enum column)
{
- BUG_ON(column >= BTRFS_QGROUP_ALL || column < 0);
int len;
int unit_mode = btrfs_qgroup_columns[column].unit_mode;
int max_len = btrfs_qgroup_columns[column].max_len;
+ ASSERT(0 <= column && column < BTRFS_QGROUP_ALL);
+
switch (column) {
case BTRFS_QGROUP_QGROUPID:
@@ -438,7 +439,7 @@ struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void)
sizeof(struct btrfs_qgroup_comparer);
set = calloc(1, size);
if (!set) {
- fprintf(stderr, "memory allocation failed\n");
+ error("memory allocation failed");
exit(1);
}
@@ -447,11 +448,6 @@ struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void)
return set;
}
-void btrfs_qgroup_free_comparer_set(struct btrfs_qgroup_comparer_set *comp_set)
-{
- free(comp_set);
-}
-
int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
enum btrfs_qgroup_comp_enum comparer,
int is_descending)
@@ -459,9 +455,9 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
struct btrfs_qgroup_comparer_set *set = *comp_set;
int size;
- BUG_ON(!set);
- BUG_ON(comparer >= BTRFS_QGROUP_COMP_MAX);
- BUG_ON(set->ncomps > set->total);
+ ASSERT(set != NULL);
+ ASSERT(comparer < BTRFS_QGROUP_COMP_MAX);
+ ASSERT(set->ncomps <= set->total);
if (set->ncomps == set->total) {
void *tmp;
@@ -472,7 +468,7 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
tmp = set;
set = realloc(set, size);
if (!set) {
- fprintf(stderr, "memory allocation failed\n");
+ error("memory allocation failed");
free(tmp);
exit(1);
}
@@ -484,7 +480,7 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
*comp_set = set;
}
- BUG_ON(set->comps[set->ncomps].comp_func);
+ ASSERT(set->comps[set->ncomps].comp_func == NULL);
set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
set->comps[set->ncomps].is_descending = is_descending;
@@ -616,7 +612,7 @@ static int update_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
if (pa && child) {
list = malloc(sizeof(*list));
if (!list) {
- fprintf(stderr, "memory allocation failed\n");
+ error("memory allocation failed");
exit(1);
}
list->qgroup = pa;
@@ -645,7 +641,7 @@ static int add_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
bq = calloc(1, sizeof(*bq));
if (!bq) {
- printf("memory allocation failed\n");
+ error("memory allocation failed");
exit(1);
}
if (qgroupid) {
@@ -674,7 +670,7 @@ static int add_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
if (parent && child) {
list = malloc(sizeof(*list));
if (!list) {
- fprintf(stderr, "memory allocation failed\n");
+ error("memory allocation failed");
exit(1);
}
list->qgroup = parent;
@@ -684,8 +680,8 @@ static int add_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
}
ret = qgroup_tree_insert(qgroup_lookup, bq);
if (ret) {
- printf("failed to insert tree %llu\n",
- bq->qgroupid);
+ error("failed to insert %llu into tree: %s",
+ (unsigned long long)bq->qgroupid, strerror(-ret));
exit(1);
}
return ret;
@@ -813,7 +809,7 @@ struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void)
sizeof(struct btrfs_qgroup_filter);
set = calloc(1, size);
if (!set) {
- fprintf(stderr, "memory allocation failed\n");
+ error("memory allocation failed");
exit(1);
}
set->total = BTRFS_QGROUP_NFILTERS_INCREASE;
@@ -821,20 +817,15 @@ struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void)
return set;
}
-void btrfs_qgroup_free_filter_set(struct btrfs_qgroup_filter_set *filter_set)
-{
- free(filter_set);
-}
-
int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set,
enum btrfs_qgroup_filter_enum filter, u64 data)
{
struct btrfs_qgroup_filter_set *set = *filter_set;
int size;
- BUG_ON(!set);
- BUG_ON(filter >= BTRFS_QGROUP_FILTER_MAX);
- BUG_ON(set->nfilters > set->total);
+ ASSERT(set != NULL);
+ ASSERT(filter < BTRFS_QGROUP_FILTER_MAX);
+ ASSERT(set->nfilters <= set->total);
if (set->nfilters == set->total) {
void *tmp;
@@ -845,7 +836,7 @@ int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set,
tmp = set;
set = realloc(set, size);
if (!set) {
- fprintf(stderr, "memory allocation failed\n");
+ error("memory allocation failed");
free(tmp);
exit(1);
}
@@ -855,7 +846,8 @@ int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set,
set->total += BTRFS_QGROUP_NFILTERS_INCREASE;
*filter_set = set;
}
- BUG_ON(set->filters[set->nfilters].filter_func);
+
+ ASSERT(set->filters[set->nfilters].filter_func == NULL);
set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
set->filters[set->nfilters].data = data;
set->nfilters++;
@@ -926,12 +918,13 @@ static int sort_tree_insert(struct qgroup_lookup *sort_tree,
static void __update_columns_max_len(struct btrfs_qgroup *bq,
enum btrfs_qgroup_column_enum column)
{
- BUG_ON(column >= BTRFS_QGROUP_ALL || column < 0);
struct btrfs_qgroup_list *list = NULL;
char tmp[100];
int len;
unsigned unit_mode = btrfs_qgroup_columns[column].unit_mode;
+ ASSERT(0 <= column && column < BTRFS_QGROUP_ALL);
+
switch (column) {
case BTRFS_QGROUP_QGROUPID:
@@ -1032,14 +1025,11 @@ static void __filter_and_sort_qgroups(struct qgroup_lookup *all_qgroups,
static inline void print_status_flag_warning(u64 flags)
{
if (!(flags & BTRFS_QGROUP_STATUS_FLAG_ON))
- fprintf(stderr,
- "WARNING: Quota disabled, qgroup data may be out of date\n");
+ warning("quota disabled, qgroup data may be out of date");
else if (flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN)
- fprintf(stderr,
- "WARNING: Rescan is running, qgroup data may be incorrect\n");
+ warning("rescan is running, qgroup data may be incorrect");
else if (flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT)
- fprintf(stderr,
- "WARNING: Qgroup data inconsistent, rescan recommended\n");
+ warning("qgroup data inconsistent, rescan recommended");
}
static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
@@ -1074,12 +1064,9 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
while (1) {
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- if (ret < 0) {
- fprintf(stderr,
- "ERROR: can't perform the search - %s\n",
- strerror(errno));
- return ret;
- }
+ if (ret < 0)
+ return -errno;
+
/* the ioctl returns the number of item it found in nr_items */
if (sk->nr_items == 0)
break;
@@ -1215,30 +1202,12 @@ int btrfs_show_qgroups(int fd,
print_all_qgroups(&sort_tree);
__free_all_qgroups(&qgroup_lookup);
- btrfs_qgroup_free_filter_set(filter_set);
- btrfs_qgroup_free_comparer_set(comp_set);
+ free(filter_set);
+ free(comp_set);
return ret;
}
-u64 btrfs_get_path_rootid(int fd)
-{
- int ret;
- struct btrfs_ioctl_ino_lookup_args args;
-
- memset(&args, 0, sizeof(args));
- args.objectid = BTRFS_FIRST_FREE_OBJECTID;
-
- ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
- if (ret < 0) {
- fprintf(stderr,
- "ERROR: can't perform the search - %s\n",
- strerror(errno));
- return ret;
- }
- return args.treeid;
-}
-
-int btrfs_qgroup_parse_sort_string(char *opt_arg,
+int btrfs_qgroup_parse_sort_string(const char *opt_arg,
struct btrfs_qgroup_comparer_set **comps)
{
int order;
@@ -1246,8 +1215,14 @@ int btrfs_qgroup_parse_sort_string(char *opt_arg,
char *p;
char **ptr_argv;
int what_to_sort;
+ char *opt_tmp;
+ int ret = 0;
+
+ opt_tmp = strdup(opt_arg);
+ if (!opt_tmp)
+ return -ENOMEM;
- while ((p = strtok(opt_arg, ",")) != NULL) {
+ while ((p = strtok(opt_tmp, ",")) != NULL) {
flag = 0;
ptr_argv = all_sort_items;
@@ -1267,10 +1242,10 @@ int btrfs_qgroup_parse_sort_string(char *opt_arg,
ptr_argv++;
}
- if (flag == 0)
- return -1;
-
- else {
+ if (flag == 0) {
+ ret = -1;
+ goto out;
+ } else {
if (*p == '+') {
order = 0;
p++;
@@ -1281,14 +1256,19 @@ int btrfs_qgroup_parse_sort_string(char *opt_arg,
order = 0;
what_to_sort = btrfs_qgroup_get_sort_item(p);
- if (what_to_sort < 0)
- return -1;
+ if (what_to_sort < 0) {
+ ret = -1;
+ goto out;
+ }
btrfs_qgroup_setup_comparer(comps, what_to_sort, order);
}
- opt_arg = NULL;
+ free(opt_tmp);
+ opt_tmp = NULL;
}
- return 0;
+out:
+ free(opt_tmp);
+ return ret;
}
int qgroup_inherit_size(struct btrfs_qgroup_inherit *p)
@@ -1312,7 +1292,7 @@ qgroup_inherit_realloc(struct btrfs_qgroup_inherit **inherit, int n, int pos)
out = calloc(sizeof(*out) + sizeof(out->qgroups[0]) * (nitems + n), 1);
if (out == NULL) {
- fprintf(stderr, "ERROR: Not enough memory\n");
+ error("not enough memory");
return -ENOMEM;
}
@@ -1340,7 +1320,7 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg)
int pos = 0;
if (qgroupid == 0) {
- fprintf(stderr, "ERROR: bad qgroup specification\n");
+ error("invalid qgroup specification, qgroupid must not 0");
return -EINVAL;
}
@@ -1367,7 +1347,7 @@ int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
p = strchr(arg, ':');
if (!p) {
bad:
- fprintf(stderr, "ERROR: bad copy specification\n");
+ error("invalid copy specification, missing separator :");
return -EINVAL;
}
*p = 0;
diff --git a/qgroup.h b/qgroup.h
index 5886fc33..875fbdf3 100644
--- a/qgroup.h
+++ b/qgroup.h
@@ -77,19 +77,16 @@ enum btrfs_qgroup_filter_enum {
BTRFS_QGROUP_FILTER_MAX,
};
-int btrfs_qgroup_parse_sort_string(char *opt_arg,
+int btrfs_qgroup_parse_sort_string(const char *opt_arg,
struct btrfs_qgroup_comparer_set **comps);
-u64 btrfs_get_path_rootid(int fd);
int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
struct btrfs_qgroup_comparer_set *);
void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column);
void btrfs_qgroup_setup_units(unsigned unit_mode);
struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void);
-void btrfs_qgroup_free_filter_set(struct btrfs_qgroup_filter_set *filter_set);
int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set,
enum btrfs_qgroup_filter_enum, u64 data);
struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void);
-void btrfs_qgroup_free_comparer_set(struct btrfs_qgroup_comparer_set *comp_set);
int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
enum btrfs_qgroup_comp_enum comparer,
int is_descending);
diff --git a/quick-test.c b/quick-test.c
index ffde85d9..f99ecc38 100644
--- a/quick-test.c
+++ b/quick-test.c
@@ -57,7 +57,7 @@ int main(int ac, char **av) {
}
trans = btrfs_start_transaction(root, 1);
srand(55);
- btrfs_set_key_type(&ins, BTRFS_STRING_ITEM_KEY);
+ ins.type = BTRFS_STRING_ITEM_KEY;
for (i = 0; i < run_size; i++) {
num = next_key(i, max_key);
// num = i;
diff --git a/raid6.c b/raid56.c
index 833df5f3..8c79c456 100644
--- a/raid6.c
+++ b/raid56.c
@@ -26,6 +26,8 @@
#include "kerncompat.h"
#include "ctree.h"
#include "disk-io.h"
+#include "volumes.h"
+#include "utils.h"
/*
* This is the C data type to use
@@ -107,3 +109,64 @@ void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
}
}
+static void xor_range(char *dst, const char*src, size_t size)
+{
+ /* Move to DWORD aligned */
+ while (size && ((unsigned long)dst & sizeof(unsigned long))) {
+ *dst++ ^= *src++;
+ size--;
+ }
+
+ /* DWORD aligned part */
+ while (size >= sizeof(unsigned long)) {
+ *(unsigned long *)dst ^= *(unsigned long *)src;
+ src += sizeof(unsigned long);
+ dst += sizeof(unsigned long);
+ size -= sizeof(unsigned long);
+ }
+ /* Remaining */
+ while (size) {
+ *dst++ ^= *src++;
+ size--;
+ }
+}
+
+/*
+ * Generate desired data/parity stripe for RAID5
+ *
+ * @nr_devs: Total number of devices, including parity
+ * @stripe_len: Stripe length
+ * @data: Data, with special layout:
+ * data[0]: Data stripe 0
+ * data[nr_devs-2]: Last data stripe
+ * data[nr_devs-1]: RAID5 parity
+ * @dest: To generate which data. should follow above data layout
+ */
+int raid5_gen_result(int nr_devs, size_t stripe_len, int dest, void **data)
+{
+ int i;
+ char *buf = data[dest];
+
+ /* Validation check */
+ if (stripe_len <= 0 || stripe_len != BTRFS_STRIPE_LEN) {
+ error("invalid parameter for %s", __func__);
+ return -EINVAL;
+ }
+
+ if (dest >= nr_devs || nr_devs < 2) {
+ error("invalid parameter for %s", __func__);
+ return -EINVAL;
+ }
+ /* Shortcut for 2 devs RAID5, which is just RAID1 */
+ if (nr_devs == 2) {
+ memcpy(data[dest], data[1 - dest], stripe_len);
+ return 0;
+ }
+ memset(buf, 0, stripe_len);
+ for (i = 0; i < nr_devs; i++) {
+ if (i == dest)
+ continue;
+ xor_range(buf, data[i], stripe_len);
+ }
+ return 0;
+}
diff --git a/random-test.c b/random-test.c
index b7c6cdb3..410a110b 100644
--- a/random-test.c
+++ b/random-test.c
@@ -37,7 +37,7 @@ static int setup_key(struct radix_tree_root *root, struct btrfs_key *key,
int ret;
key->flags = 0;
- btrfs_set_key_type(key, BTRFS_STRING_ITEM_KEY);
+ key->type = BTRFS_STRING_ITEM_KEY;
key->offset = 0;
again:
ret = radix_tree_gang_lookup(root, (void **)res, num, 2);
@@ -184,7 +184,7 @@ static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root
key.offset = 0;
key.flags = 0;
- btrfs_set_key_type(&key, BTRFS_STRING_ITEM_KEY);
+ key.type = BTRFS_STRING_ITEM_KEY;
key.objectid = (unsigned long)-1;
while(nr-- >= 0) {
btrfs_init_path(&path);
@@ -288,7 +288,7 @@ static int fill_radix(struct btrfs_root *root, struct radix_tree_root *radix)
key.offset = 0;
key.flags = 0;
- btrfs_set_key_type(&key, BTRFS_STRING_ITEM_KEY);
+ key.type = BTRFS_STRING_ITEM_KEY;
key.objectid = (unsigned long)-1;
while(1) {
btrfs_init_path(&path);
diff --git a/root-tree.c b/root-tree.c
index 934d02ef..ab01a140 100644
--- a/root-tree.c
+++ b/root-tree.c
@@ -31,12 +31,14 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
int ret;
int slot;
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
search_key.objectid = objectid;
search_key.type = BTRFS_ROOT_ITEM_KEY;
search_key.offset = (u64)-1;
- path = btrfs_alloc_path();
- BUG_ON(!path);
ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
if (ret < 0)
goto out;
@@ -74,7 +76,9 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
u32 old_len;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
+
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
if (ret < 0)
goto out;
@@ -139,6 +143,31 @@ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
return ret;
}
+/* drop the root item for 'key' from 'root' */
+int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+ struct btrfs_key *key)
+{
+ struct btrfs_path *path;
+ int ret;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ ret = btrfs_search_slot(trans, root, key, path, -1, 1);
+ if (ret < 0)
+ goto out;
+
+ if (ret != 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = btrfs_del_item(trans, root, path);
+out:
+ btrfs_free_path(path);
+ return ret;
+}
+
/*
* add a btrfs_root_ref item. type is either BTRFS_ROOT_REF_KEY
* or BTRFS_ROOT_BACKREF_KEY.
diff --git a/send-dump.c b/send-dump.c
new file mode 100644
index 00000000..4c44246f
--- /dev/null
+++ b/send-dump.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2016 Fujitsu. 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 <unistd.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <libgen.h>
+#include <mntent.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+#include <asm/types.h>
+#include <uuid/uuid.h>
+#include "utils.h"
+#include "commands.h"
+#include "send-utils.h"
+#include "send-stream.h"
+#include "send-dump.h"
+
+#define PATH_CAT_OR_RET(function_name, outpath, path1, path2, ret) \
+({ \
+ ret = path_cat_out(outpath, path1, path2); \
+ if (ret < 0) { \
+ error("%s: path invalid: %s\n", function_name, path2); \
+ return ret; \
+ } \
+})
+
+/*
+ * Print path and escape chaacters (in a C way) that could break the line.
+ * Returns the length of the escaped characters. Unprintable characters are
+ * escaped as octals.
+ */
+static int print_path_escaped(const char *path)
+{
+ size_t i;
+ size_t path_len = strlen(path);
+ int len = 0;
+
+ for (i = 0; i < path_len; i++) {
+ char c = path[i];
+
+ len++;
+ switch (c) {
+ case '\a': putchar('\\'); putchar('a'); len++; break;
+ case '\b': putchar('\\'); putchar('b'); len++; break;
+ case '\e': putchar('\\'); putchar('e'); len++; break;
+ case '\f': putchar('\\'); putchar('f'); len++; break;
+ case '\n': putchar('\\'); putchar('n'); len++; break;
+ case '\r': putchar('\\'); putchar('r'); len++; break;
+ case '\t': putchar('\\'); putchar('t'); len++; break;
+ case '\v': putchar('\\'); putchar('v'); len++; break;
+ case ' ': putchar('\\'); putchar(' '); len++; break;
+ case '\\': putchar('\\'); putchar('\\'); len++; break;
+ default:
+ if (!isprint(c)) {
+ printf("\\%c%c%c",
+ '0' + ((c & 0300) >> 6),
+ '0' + ((c & 070) >> 3),
+ '0' + (c & 07));
+ len += 3;
+ } else {
+ putchar(c);
+ }
+ }
+ }
+ return len;
+}
+
+/*
+ * Underlying PRINT_DUMP, the only difference is how we handle
+ * the full path.
+ */
+__attribute__ ((format (printf, 5, 6)))
+static int __print_dump(int subvol, void *user, const char *path,
+ const char *title, const char *fmt, ...)
+{
+ struct btrfs_dump_send_args *r = user;
+ char full_path[PATH_MAX] = {0};
+ char *out_path;
+ va_list args;
+ int ret;
+
+ if (subvol) {
+ PATH_CAT_OR_RET(title, r->full_subvol_path, r->root_path, path, ret);
+ out_path = r->full_subvol_path;
+ } else {
+ PATH_CAT_OR_RET(title, full_path, r->full_subvol_path, path, ret);
+ out_path = full_path;
+ }
+
+ /* Unified header */
+ printf("%-16s", title);
+ ret = print_path_escaped(out_path);
+ if (!fmt)
+ return 0;
+ /* Short paths ale aligned to 32 chars */
+ while (ret++ < 32)
+ putchar(' ');
+ va_start(args, fmt);
+ /* Operation specified ones */
+ vprintf(fmt, args);
+ va_end(args);
+ putchar('\n');
+ return 0;
+}
+
+/* For subvolume/snapshot operation only */
+#define PRINT_DUMP_SUBVOL(user, path, title, fmt, ...) \
+ __print_dump(1, user, path, title, fmt, ##__VA_ARGS__)
+
+/* For other operations */
+#define PRINT_DUMP(user, path, title, fmt, ...) \
+ __print_dump(0, user, path, title, fmt, ##__VA_ARGS__)
+
+static int print_subvol(const char *path, const u8 *uuid, u64 ctransid,
+ void *user)
+{
+ char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
+
+ uuid_unparse(uuid, uuid_str);
+
+ return PRINT_DUMP_SUBVOL(user, path, "subvol", "uuid=%s transid=%llu",
+ uuid_str, ctransid);
+}
+
+static int print_snapshot(const char *path, const u8 *uuid, u64 ctransid,
+ const u8 *parent_uuid, u64 parent_ctransid,
+ void *user)
+{
+ char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
+ char parent_uuid_str[BTRFS_UUID_UNPARSED_SIZE];
+ int ret;
+
+ uuid_unparse(uuid, uuid_str);
+ uuid_unparse(parent_uuid, parent_uuid_str);
+
+ ret = PRINT_DUMP_SUBVOL(user, path, "snapshot",
+ "uuid=%s transid=%llu parent_uuid=%s parent_transid=%llu",
+ uuid_str, ctransid, parent_uuid_str,
+ parent_ctransid);
+ return ret;
+}
+
+static int print_mkfile(const char *path, void *user)
+{
+ return PRINT_DUMP(user, path, "mkfile", NULL);
+}
+
+static int print_mkdir(const char *path, void *user)
+{
+ return PRINT_DUMP(user, path, "mkdir", NULL);
+}
+
+static int print_mknod(const char *path, u64 mode, u64 dev, void *user)
+{
+ return PRINT_DUMP(user, path, "mknod", "mode=%llo dev=0x%llx", mode,
+ dev);
+}
+
+static int print_mkfifo(const char *path, void *user)
+{
+ return PRINT_DUMP(user, path, "mkfifo", NULL);
+}
+
+static int print_mksock(const char *path, void *user)
+{
+ return PRINT_DUMP(user, path, "mksock", NULL);
+}
+
+static int print_symlink(const char *path, const char *lnk, void *user)
+{
+ return PRINT_DUMP(user, path, "symlink", "dest=%s", lnk);
+}
+
+static int print_rename(const char *from, const char *to, void *user)
+{
+ struct btrfs_dump_send_args *r = user;
+ char full_to[PATH_MAX];
+ int ret;
+
+ PATH_CAT_OR_RET("rename", full_to, r->full_subvol_path, to, ret);
+ return PRINT_DUMP(user, from, "rename", "dest=%s", full_to);
+}
+
+static int print_link(const char *path, const char *lnk, void *user)
+{
+ return PRINT_DUMP(user, path, "link", "dest=%s", lnk);
+}
+
+static int print_unlink(const char *path, void *user)
+{
+ return PRINT_DUMP(user, path, "unlink", NULL);
+}
+
+static int print_rmdir(const char *path, void *user)
+{
+ return PRINT_DUMP(user, path, "rmdir", NULL);
+}
+
+static int print_write(const char *path, const void *data, u64 offset,
+ u64 len, void *user)
+{
+ return PRINT_DUMP(user, path, "write", "offset=%llu len=%llu",
+ offset, len);
+}
+
+static int print_clone(const char *path, u64 offset, u64 len,
+ const u8 *clone_uuid, u64 clone_ctransid,
+ const char *clone_path, u64 clone_offset,
+ void *user)
+{
+ struct btrfs_dump_send_args *r = user;
+ char full_path[PATH_MAX];
+ int ret;
+
+ PATH_CAT_OR_RET("clone", full_path, r->full_subvol_path, clone_path,
+ ret);
+ return PRINT_DUMP(user, path, "clone",
+ "offset=%llu len=%llu from=%s clone_offset=%llu",
+ offset, len, full_path, clone_offset);
+}
+
+static int print_set_xattr(const char *path, const char *name,
+ const void *data, int len, void *user)
+{
+ return PRINT_DUMP(user, path, "set_xattr", "name=%s data=%.*s len=%d",
+ name, len, (char *)data, len);
+}
+
+static int print_remove_xattr(const char *path, const char *name, void *user)
+{
+
+ return PRINT_DUMP(user, path, "remove_xattr", "name=%s", name);
+}
+
+static int print_truncate(const char *path, u64 size, void *user)
+{
+ return PRINT_DUMP(user, path, "truncate", "size=%llu", size);
+}
+
+static int print_chmod(const char *path, u64 mode, void *user)
+{
+ return PRINT_DUMP(user, path, "chmod", "mode=%llo", mode);
+}
+
+static int print_chown(const char *path, u64 uid, u64 gid, void *user)
+{
+ return PRINT_DUMP(user, path, "chown", "gid=%llu uid=%llu", gid, uid);
+}
+
+static int sprintf_timespec(struct timespec *ts, char *dest, int max_size)
+{
+ struct tm tm;
+ int ret;
+
+ if (!localtime_r(&ts->tv_sec, &tm)) {
+ error("failed to convert time %lld.%.9ld to local time",
+ (long long)ts->tv_sec, ts->tv_nsec);
+ return -EINVAL;
+ }
+ ret = strftime(dest, max_size, "%FT%T%z", &tm);
+ if (ret == 0) {
+ error(
+ "time %lld.%ld is too long to convert into readable string",
+ (long long)ts->tv_sec, ts->tv_nsec);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#define TIME_STRING_MAX 64
+static int print_utimes(const char *path, struct timespec *at,
+ struct timespec *mt, struct timespec *ct,
+ void *user)
+{
+ char at_str[TIME_STRING_MAX];
+ char mt_str[TIME_STRING_MAX];
+ char ct_str[TIME_STRING_MAX];
+
+ if (sprintf_timespec(at, at_str, TIME_STRING_MAX - 1) < 0 ||
+ sprintf_timespec(mt, mt_str, TIME_STRING_MAX - 1) < 0 ||
+ sprintf_timespec(ct, ct_str, TIME_STRING_MAX - 1) < 0)
+ return -EINVAL;
+ return PRINT_DUMP(user, path, "utimes", "atime=%s mtime=%s ctime=%s",
+ at_str, mt_str, ct_str);
+}
+
+static int print_update_extent(const char *path, u64 offset, u64 len,
+ void *user)
+{
+ return PRINT_DUMP(user, path, "update_extent", "offset=%llu len=%llu",
+ offset, len);
+}
+
+struct btrfs_send_ops btrfs_print_send_ops = {
+ .subvol = print_subvol,
+ .snapshot = print_snapshot,
+ .mkfile = print_mkfile,
+ .mkdir = print_mkdir,
+ .mknod = print_mknod,
+ .mkfifo = print_mkfifo,
+ .mksock = print_mksock,
+ .symlink = print_symlink,
+ .rename = print_rename,
+ .link = print_link,
+ .unlink = print_unlink,
+ .rmdir = print_rmdir,
+ .write = print_write,
+ .clone = print_clone,
+ .set_xattr = print_set_xattr,
+ .remove_xattr = print_remove_xattr,
+ .truncate = print_truncate,
+ .chmod = print_chmod,
+ .chown = print_chown,
+ .utimes = print_utimes,
+ .update_extent = print_update_extent
+};
diff --git a/send-dump.h b/send-dump.h
new file mode 100644
index 00000000..06a61085
--- /dev/null
+++ b/send-dump.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Fujitsu. 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.
+ */
+
+#ifndef __BTRFS_SEND_DUMP_H__
+#define __BTRFS_SEND_DUMP_H__
+
+#include <linux/limits.h>
+
+struct btrfs_dump_send_args {
+ char full_subvol_path[PATH_MAX];
+ char root_path[PATH_MAX];
+};
+
+extern struct btrfs_send_ops btrfs_print_send_ops;
+
+#endif
diff --git a/send-stream.c b/send-stream.c
index 66c04884..5a028cd9 100644
--- a/send-stream.c
+++ b/send-stream.c
@@ -22,6 +22,7 @@
#include "send.h"
#include "send-stream.h"
#include "crc32c.h"
+#include "utils.h"
struct btrfs_send_stream {
int fd;
@@ -32,141 +33,172 @@ struct btrfs_send_stream {
struct btrfs_tlv_header *cmd_attrs[BTRFS_SEND_A_MAX + 1];
u32 version;
+ /*
+ * end of last successful read, equivalent to start of current
+ * malformated part of block
+ */
+ size_t stream_pos;
+
struct btrfs_send_ops *ops;
void *user;
};
-static int read_buf(struct btrfs_send_stream *s, void *buf, int len)
+/*
+ * Read len bytes to buf.
+ * Return:
+ * 0 - success
+ * < 0 - negative errno in case of error
+ * > 0 - no data read, EOF
+ */
+static int read_buf(struct btrfs_send_stream *sctx, char *buf, size_t len)
{
int ret;
- int pos = 0;
+ size_t pos = 0;
while (pos < len) {
- ret = read(s->fd, (char*)buf + pos, len - pos);
- if (ret < 0) {
+ ssize_t rbytes;
+
+ rbytes = read(sctx->fd, buf + pos, len - pos);
+ if (rbytes < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: read from stream failed. %s\n",
+ error("read from stream failed: %s",
strerror(-ret));
goto out;
}
- if (ret == 0) {
+ if (rbytes == 0) {
ret = 1;
- goto out;
+ goto out_eof;
}
- pos += ret;
+ pos += rbytes;
}
-
ret = 0;
+out_eof:
+ if (0 < pos && pos < len) {
+ error("short read from stream: expected %zu read %zu", len, pos);
+ ret = -EIO;
+ } else {
+ sctx->stream_pos += pos;
+ }
+
out:
return ret;
}
/*
* Reads a single command from kernel space and decodes the TLV's into
- * s->cmd_attrs
+ * sctx->cmd_attrs
+ *
+ * Returns:
+ * 0 - success
+ * < 0 - an error in the command
*/
-static int read_cmd(struct btrfs_send_stream *s)
+static int read_cmd(struct btrfs_send_stream *sctx)
{
int ret;
- int cmd;
- int cmd_len;
- int tlv_type;
- int tlv_len;
+ u16 cmd;
+ u32 cmd_len;
char *data;
- int pos;
- struct btrfs_tlv_header *tlv_hdr;
+ u32 pos;
u32 crc;
u32 crc2;
- memset(s->cmd_attrs, 0, sizeof(s->cmd_attrs));
+ memset(sctx->cmd_attrs, 0, sizeof(sctx->cmd_attrs));
- ret = read_buf(s, s->read_buf, sizeof(*s->cmd_hdr));
+ ASSERT(sizeof(*sctx->cmd_hdr) <= sizeof(sctx->read_buf));
+ ret = read_buf(sctx, sctx->read_buf, sizeof(*sctx->cmd_hdr));
if (ret < 0)
goto out;
if (ret) {
ret = -EINVAL;
- fprintf(stderr, "ERROR: unexpected EOF in stream.\n");
+ error("unexpected EOF in stream");
goto out;
}
- s->cmd_hdr = (struct btrfs_cmd_header *)s->read_buf;
- cmd = le16_to_cpu(s->cmd_hdr->cmd);
- cmd_len = le32_to_cpu(s->cmd_hdr->len);
+ sctx->cmd_hdr = (struct btrfs_cmd_header *)sctx->read_buf;
+ cmd = le16_to_cpu(sctx->cmd_hdr->cmd);
+ cmd_len = le32_to_cpu(sctx->cmd_hdr->len);
+
+ if (cmd_len + sizeof(*sctx->cmd_hdr) >= sizeof(sctx->read_buf)) {
+ ret = -EINVAL;
+ error("command length %u too big for buffer %zu",
+ cmd_len, sizeof(sctx->read_buf));
+ goto out;
+ }
- data = s->read_buf + sizeof(*s->cmd_hdr);
- ret = read_buf(s, data, cmd_len);
+ data = sctx->read_buf + sizeof(*sctx->cmd_hdr);
+ ret = read_buf(sctx, data, cmd_len);
if (ret < 0)
goto out;
if (ret) {
ret = -EINVAL;
- fprintf(stderr, "ERROR: unexpected EOF in stream.\n");
+ error("unexpected EOF in stream");
goto out;
}
- crc = le32_to_cpu(s->cmd_hdr->crc);
- s->cmd_hdr->crc = 0;
+ crc = le32_to_cpu(sctx->cmd_hdr->crc);
+ sctx->cmd_hdr->crc = 0;
- crc2 = crc32c(0, (unsigned char*)s->read_buf,
- sizeof(*s->cmd_hdr) + cmd_len);
+ crc2 = crc32c(0, (unsigned char*)sctx->read_buf,
+ sizeof(*sctx->cmd_hdr) + cmd_len);
if (crc != crc2) {
ret = -EINVAL;
- fprintf(stderr, "ERROR: crc32 mismatch in command.\n");
+ error("crc32 mismatch in command");
goto out;
}
pos = 0;
while (pos < cmd_len) {
+ struct btrfs_tlv_header *tlv_hdr;
+ u16 tlv_type;
+ u16 tlv_len;
+
tlv_hdr = (struct btrfs_tlv_header *)data;
tlv_type = le16_to_cpu(tlv_hdr->tlv_type);
tlv_len = le16_to_cpu(tlv_hdr->tlv_len);
- if (tlv_type <= 0 || tlv_type > BTRFS_SEND_A_MAX ||
- tlv_len < 0 || tlv_len > BTRFS_SEND_BUF_SIZE) {
- fprintf(stderr, "ERROR: invalid tlv in cmd. "
- "tlv_type = %d, tlv_len = %d\n",
+ if (tlv_type == 0 || tlv_type > BTRFS_SEND_A_MAX
+ || tlv_len > BTRFS_SEND_BUF_SIZE) {
+ error("invalid tlv in cmd tlv_type = %hu, tlv_len = %hu",
tlv_type, tlv_len);
ret = -EINVAL;
goto out;
}
- s->cmd_attrs[tlv_type] = tlv_hdr;
+ sctx->cmd_attrs[tlv_type] = tlv_hdr;
data += sizeof(*tlv_hdr) + tlv_len;
pos += sizeof(*tlv_hdr) + tlv_len;
}
- s->cmd = cmd;
+ sctx->cmd = cmd;
ret = 0;
out:
return ret;
}
-static int tlv_get(struct btrfs_send_stream *s, int attr, void **data, int *len)
+static int tlv_get(struct btrfs_send_stream *sctx, int attr, void **data, int *len)
{
int ret;
- struct btrfs_tlv_header *h;
+ struct btrfs_tlv_header *hdr;
if (attr <= 0 || attr > BTRFS_SEND_A_MAX) {
- fprintf(stderr, "ERROR: invalid attribute requested. "
- "attr = %d\n",
- attr);
+ error("invalid attribute requested, attr = %d", attr);
ret = -EINVAL;
goto out;
}
- h = s->cmd_attrs[attr];
- if (!h) {
- fprintf(stderr, "ERROR: attribute %d requested "
- "but not present.\n", attr);
+ hdr = sctx->cmd_attrs[attr];
+ if (!hdr) {
+ error("attribute %d requested but not present", attr);
ret = -ENOENT;
goto out;
}
- *len = le16_to_cpu(h->tlv_len);
- *data = h + 1;
+ *len = le16_to_cpu(hdr->tlv_len);
+ *data = hdr + 1;
ret = 0;
@@ -190,8 +222,8 @@ out:
#define TLV_CHECK_LEN(expected, got) \
do { \
if (expected != got) { \
- fprintf(stderr, "ERROR: invalid size for attribute. " \
- "expected = %d, got = %d\n", \
+ error("invalid size for attribute, " \
+ "expected = %d, got = %d", \
(int)expected, (int)got); \
ret = -EINVAL; \
goto tlv_get_failed; \
@@ -212,13 +244,13 @@ out:
#define TLV_GET_U32(s, attr, v) TLV_GET_INT(s, attr, 32, v)
#define TLV_GET_U64(s, attr, v) TLV_GET_INT(s, attr, 64, v)
-static int tlv_get_string(struct btrfs_send_stream *s, int attr, char **str)
+static int tlv_get_string(struct btrfs_send_stream *sctx, int attr, char **str)
{
int ret;
void *data;
int len = 0;
- TLV_GET(s, attr, &data, &len);
+ TLV_GET(sctx, attr, &data, &len);
*str = malloc(len + 1);
if (!*str)
@@ -234,14 +266,14 @@ tlv_get_failed:
#define TLV_GET_STRING(s, attr, str) \
__TLV_DO_WHILE_GOTO_FAIL(tlv_get_string(s, attr, str))
-static int tlv_get_timespec(struct btrfs_send_stream *s,
+static int tlv_get_timespec(struct btrfs_send_stream *sctx,
int attr, struct timespec *ts)
{
int ret;
int len;
struct btrfs_timespec *bts;
- TLV_GET(s, attr, (void**)&bts, &len);
+ TLV_GET(sctx, attr, (void**)&bts, &len);
TLV_CHECK_LEN(sizeof(*bts), len);
ts->tv_sec = le64_to_cpu(bts->sec);
@@ -254,13 +286,13 @@ tlv_get_failed:
#define TLV_GET_TIMESPEC(s, attr, ts) \
__TLV_DO_WHILE_GOTO_FAIL(tlv_get_timespec(s, attr, ts))
-static int tlv_get_uuid(struct btrfs_send_stream *s, int attr, u8 *uuid)
+static int tlv_get_uuid(struct btrfs_send_stream *sctx, int attr, u8 *uuid)
{
int ret;
int len;
void *data;
- TLV_GET(s, attr, &data, &len);
+ TLV_GET(sctx, attr, &data, &len);
TLV_CHECK_LEN(BTRFS_UUID_SIZE, len);
memcpy(uuid, data, BTRFS_UUID_SIZE);
@@ -272,7 +304,7 @@ tlv_get_failed:
#define TLV_GET_UUID(s, attr, uuid) \
__TLV_DO_WHILE_GOTO_FAIL(tlv_get_uuid(s, attr, uuid))
-static int read_and_process_cmd(struct btrfs_send_stream *s)
+static int read_and_process_cmd(struct btrfs_send_stream *sctx)
{
int ret;
char *path = NULL;
@@ -297,129 +329,129 @@ static int read_and_process_cmd(struct btrfs_send_stream *s)
int len;
int xattr_len;
- ret = read_cmd(s);
+ ret = read_cmd(sctx);
if (ret)
goto out;
- switch (s->cmd) {
+ switch (sctx->cmd) {
case BTRFS_SEND_C_SUBVOL:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_UUID(s, BTRFS_SEND_A_UUID, uuid);
- TLV_GET_U64(s, BTRFS_SEND_A_CTRANSID, &ctransid);
- ret = s->ops->subvol(path, uuid, ctransid, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_UUID(sctx, BTRFS_SEND_A_UUID, uuid);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_CTRANSID, &ctransid);
+ ret = sctx->ops->subvol(path, uuid, ctransid, sctx->user);
break;
case BTRFS_SEND_C_SNAPSHOT:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_UUID(s, BTRFS_SEND_A_UUID, uuid);
- TLV_GET_U64(s, BTRFS_SEND_A_CTRANSID, &ctransid);
- TLV_GET_UUID(s, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
- TLV_GET_U64(s, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
- ret = s->ops->snapshot(path, uuid, ctransid, clone_uuid,
- clone_ctransid, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_UUID(sctx, BTRFS_SEND_A_UUID, uuid);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_CTRANSID, &ctransid);
+ TLV_GET_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
+ ret = sctx->ops->snapshot(path, uuid, ctransid, clone_uuid,
+ clone_ctransid, sctx->user);
break;
case BTRFS_SEND_C_MKFILE:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- ret = s->ops->mkfile(path, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ ret = sctx->ops->mkfile(path, sctx->user);
break;
case BTRFS_SEND_C_MKDIR:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- ret = s->ops->mkdir(path, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ ret = sctx->ops->mkdir(path, sctx->user);
break;
case BTRFS_SEND_C_MKNOD:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_U64(s, BTRFS_SEND_A_MODE, &mode);
- TLV_GET_U64(s, BTRFS_SEND_A_RDEV, &dev);
- ret = s->ops->mknod(path, mode, dev, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_MODE, &mode);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_RDEV, &dev);
+ ret = sctx->ops->mknod(path, mode, dev, sctx->user);
break;
case BTRFS_SEND_C_MKFIFO:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- ret = s->ops->mkfifo(path, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ ret = sctx->ops->mkfifo(path, sctx->user);
break;
case BTRFS_SEND_C_MKSOCK:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- ret = s->ops->mksock(path, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ ret = sctx->ops->mksock(path, sctx->user);
break;
case BTRFS_SEND_C_SYMLINK:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH_LINK, &path_to);
- ret = s->ops->symlink(path, path_to, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_LINK, &path_to);
+ ret = sctx->ops->symlink(path, path_to, sctx->user);
break;
case BTRFS_SEND_C_RENAME:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH_TO, &path_to);
- ret = s->ops->rename(path, path_to, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_TO, &path_to);
+ ret = sctx->ops->rename(path, path_to, sctx->user);
break;
case BTRFS_SEND_C_LINK:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH_LINK, &path_to);
- ret = s->ops->link(path, path_to, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_LINK, &path_to);
+ ret = sctx->ops->link(path, path_to, sctx->user);
break;
case BTRFS_SEND_C_UNLINK:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- ret = s->ops->unlink(path, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ ret = sctx->ops->unlink(path, sctx->user);
break;
case BTRFS_SEND_C_RMDIR:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- ret = s->ops->rmdir(path, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ ret = sctx->ops->rmdir(path, sctx->user);
break;
case BTRFS_SEND_C_WRITE:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_U64(s, BTRFS_SEND_A_FILE_OFFSET, &offset);
- TLV_GET(s, BTRFS_SEND_A_DATA, &data, &len);
- ret = s->ops->write(path, data, offset, len, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
+ TLV_GET(sctx, BTRFS_SEND_A_DATA, &data, &len);
+ ret = sctx->ops->write(path, data, offset, len, sctx->user);
break;
case BTRFS_SEND_C_CLONE:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_U64(s, BTRFS_SEND_A_FILE_OFFSET, &offset);
- TLV_GET_U64(s, BTRFS_SEND_A_CLONE_LEN, &len);
- TLV_GET_UUID(s, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
- TLV_GET_U64(s, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
- TLV_GET_STRING(s, BTRFS_SEND_A_CLONE_PATH, &clone_path);
- TLV_GET_U64(s, BTRFS_SEND_A_CLONE_OFFSET, &clone_offset);
- ret = s->ops->clone(path, offset, len, clone_uuid,
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_LEN, &len);
+ TLV_GET_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_CLONE_PATH, &clone_path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_OFFSET, &clone_offset);
+ ret = sctx->ops->clone(path, offset, len, clone_uuid,
clone_ctransid, clone_path, clone_offset,
- s->user);
+ sctx->user);
break;
case BTRFS_SEND_C_SET_XATTR:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_STRING(s, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
- TLV_GET(s, BTRFS_SEND_A_XATTR_DATA, &xattr_data, &xattr_len);
- ret = s->ops->set_xattr(path, xattr_name, xattr_data,
- xattr_len, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
+ TLV_GET(sctx, BTRFS_SEND_A_XATTR_DATA, &xattr_data, &xattr_len);
+ ret = sctx->ops->set_xattr(path, xattr_name, xattr_data,
+ xattr_len, sctx->user);
break;
case BTRFS_SEND_C_REMOVE_XATTR:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_STRING(s, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
- ret = s->ops->remove_xattr(path, xattr_name, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
+ ret = sctx->ops->remove_xattr(path, xattr_name, sctx->user);
break;
case BTRFS_SEND_C_TRUNCATE:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_U64(s, BTRFS_SEND_A_SIZE, &tmp);
- ret = s->ops->truncate(path, tmp, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_SIZE, &tmp);
+ ret = sctx->ops->truncate(path, tmp, sctx->user);
break;
case BTRFS_SEND_C_CHMOD:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_U64(s, BTRFS_SEND_A_MODE, &tmp);
- ret = s->ops->chmod(path, tmp, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_MODE, &tmp);
+ ret = sctx->ops->chmod(path, tmp, sctx->user);
break;
case BTRFS_SEND_C_CHOWN:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_U64(s, BTRFS_SEND_A_UID, &tmp);
- TLV_GET_U64(s, BTRFS_SEND_A_GID, &tmp2);
- ret = s->ops->chown(path, tmp, tmp2, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_UID, &tmp);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_GID, &tmp2);
+ ret = sctx->ops->chown(path, tmp, tmp2, sctx->user);
break;
case BTRFS_SEND_C_UTIMES:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_TIMESPEC(s, BTRFS_SEND_A_ATIME, &at);
- TLV_GET_TIMESPEC(s, BTRFS_SEND_A_MTIME, &mt);
- TLV_GET_TIMESPEC(s, BTRFS_SEND_A_CTIME, &ct);
- ret = s->ops->utimes(path, &at, &mt, &ct, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_ATIME, &at);
+ TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_MTIME, &mt);
+ TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_CTIME, &ct);
+ ret = sctx->ops->utimes(path, &at, &mt, &ct, sctx->user);
break;
case BTRFS_SEND_C_UPDATE_EXTENT:
- TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
- TLV_GET_U64(s, BTRFS_SEND_A_FILE_OFFSET, &offset);
- TLV_GET_U64(s, BTRFS_SEND_A_SIZE, &tmp);
- ret = s->ops->update_extent(path, offset, tmp, s->user);
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_SIZE, &tmp);
+ ret = sctx->ops->update_extent(path, offset, tmp, sctx->user);
break;
case BTRFS_SEND_C_END:
ret = 1;
@@ -446,16 +478,17 @@ int btrfs_read_and_process_send_stream(int fd,
u64 max_errors)
{
int ret;
- struct btrfs_send_stream s;
+ struct btrfs_send_stream sctx;
struct btrfs_stream_header hdr;
u64 errors = 0;
int last_err = 0;
- s.fd = fd;
- s.ops = ops;
- s.user = user;
+ sctx.fd = fd;
+ sctx.ops = ops;
+ sctx.user = user;
+ sctx.stream_pos = 0;
- ret = read_buf(&s, &hdr, sizeof(hdr));
+ ret = read_buf(&sctx, (char*)&hdr, sizeof(hdr));
if (ret < 0)
goto out;
if (ret) {
@@ -465,20 +498,20 @@ int btrfs_read_and_process_send_stream(int fd,
if (strcmp(hdr.magic, BTRFS_SEND_STREAM_MAGIC)) {
ret = -EINVAL;
- fprintf(stderr, "ERROR: Unexpected header\n");
+ error("unexpected header");
goto out;
}
- s.version = le32_to_cpu(hdr.version);
- if (s.version > BTRFS_SEND_STREAM_VERSION) {
+ sctx.version = le32_to_cpu(hdr.version);
+ if (sctx.version > BTRFS_SEND_STREAM_VERSION) {
ret = -EINVAL;
- fprintf(stderr, "ERROR: Stream version %d not supported. "
- "Please upgrade btrfs-progs\n", s.version);
+ error("stream version %d not supported, please use newer version",
+ sctx.version);
goto out;
}
while (1) {
- ret = read_and_process_cmd(&s);
+ ret = read_and_process_cmd(&sctx);
if (ret < 0) {
last_err = ret;
errors++;
diff --git a/send-test.c b/send-test.c
deleted file mode 100644
index 4645b898..00000000
--- a/send-test.c
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Copyright (C) 2013 SUSE. All rights reserved.
- *
- * This code is adapted from cmds-send.c and cmds-receive.c,
- * Both of which are:
- *
- * Copyright (C) 2012 Alexander Block. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License v2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 021110-1307, USA.
- */
-
-#include <unistd.h>
-#include <stdint.h>
-#include <dirent.h>
-#include <pthread.h>
-#include <math.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <libgen.h>
-#include <mntent.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <asm/types.h>
-#include <uuid/uuid.h>
-
-/*
- * This should be compilable without the rest of the btrfs-progs
- * source distribution.
- */
-#if BTRFS_FLAT_INCLUDES
-#include "send-utils.h"
-#include "send-stream.h"
-#else
-#include <btrfs/send-utils.h>
-#include <btrfs/send-stream.h>
-#endif /* BTRFS_FLAT_INCLUDES */
-
-static int pipefd[2];
-struct btrfs_ioctl_send_args io_send = {0, };
-static char *subvol_path;
-static char *root_path;
-
-struct recv_args {
- char *full_subvol_path;
- char *root_path;
-};
-
-void usage(int error)
-{
- printf("send-test <btrfs root> <subvol>\n");
- if (error)
- exit(error);
-}
-
-static int print_subvol(const char *path, const u8 *uuid, u64 ctransid,
- void *user)
-{
- struct recv_args *r = user;
- char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
-
- r->full_subvol_path = path_cat(r->root_path, path);
- uuid_unparse(uuid, uuid_str);
-
- printf("subvol\t%s\t%llu\t%s\n", uuid_str,
- (unsigned long long)ctransid, r->full_subvol_path);
-
- return 0;
-}
-
-static int print_snapshot(const char *path, const u8 *uuid, u64 ctransid,
- const u8 *parent_uuid, u64 parent_ctransid,
- void *user)
-{
- struct recv_args *r = user;
- char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
- char parent_uuid_str[BTRFS_UUID_UNPARSED_SIZE];
-
- r->full_subvol_path = path_cat(r->root_path, path);
- uuid_unparse(uuid, uuid_str);
- uuid_unparse(parent_uuid, parent_uuid_str);
-
- printf("snapshot\t%s\t%llu\t%s\t%llu\t%s\n", uuid_str,
- (unsigned long long)ctransid, parent_uuid_str,
- (unsigned long long)parent_ctransid, r->full_subvol_path);
-
- return 0;
-}
-
-static int print_mkfile(const char *path, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("mkfile\t%s\n", full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_mkdir(const char *path, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("mkdir\t%s\n", full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_mknod(const char *path, u64 mode, u64 dev, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("mknod\t%llo\t0x%llx\t%s\n", (unsigned long long)mode,
- (unsigned long long)dev, full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_mkfifo(const char *path, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("mkfifo\t%s\n", full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_mksock(const char *path, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("mksock\t%s\n", full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_symlink(const char *path, const char *lnk, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("symlink\t%s\t%s\n", lnk, full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_rename(const char *from, const char *to, void *user)
-{
- struct recv_args *r = user;
- char *full_from = path_cat(r->full_subvol_path, from);
- char *full_to = path_cat(r->full_subvol_path, to);
-
- printf("rename\t%s\t%s\n", from, to);
-
- free(full_from);
- free(full_to);
- return 0;
-}
-
-static int print_link(const char *path, const char *lnk, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("link\t%s\t%s\n", lnk, full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_unlink(const char *path, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("unlink\t%s\n", full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_rmdir(const char *path, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("rmdir\t%s\n", full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_write(const char *path, const void *data, u64 offset,
- u64 len, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("write\t%llu\t%llu\t%s\n", (unsigned long long)offset,
- (unsigned long long)len, full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_clone(const char *path, u64 offset, u64 len,
- const u8 *clone_uuid, u64 clone_ctransid,
- const char *clone_path, u64 clone_offset,
- void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("clone\t%s\t%s\n", full_path, clone_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_set_xattr(const char *path, const char *name,
- const void *data, int len, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("set_xattr\t%s\t%s\t%d\n", full_path,
- name, len);
-
- free(full_path);
- return 0;
-}
-
-static int print_remove_xattr(const char *path, const char *name, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("remove_xattr\t%s\t%s\n", full_path, name);
-
- free(full_path);
- return 0;
-}
-
-static int print_truncate(const char *path, u64 size, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("truncate\t%llu\t%s\n", (unsigned long long)size, full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_chmod(const char *path, u64 mode, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("chmod\t%llo\t%s\n", (unsigned long long)mode, full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_chown(const char *path, u64 uid, u64 gid, void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("chown\t%llu\t%llu\t%s\n", (unsigned long long)uid,
- (unsigned long long)gid, full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_utimes(const char *path, struct timespec *at,
- struct timespec *mt, struct timespec *ct,
- void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("utimes\t%s\n", full_path);
-
- free(full_path);
- return 0;
-}
-
-static int print_update_extent(const char *path, u64 offset, u64 len,
- void *user)
-{
- struct recv_args *r = user;
- char *full_path = path_cat(r->full_subvol_path, path);
-
- printf("update_extent\t%s\t%llu\t%llu\n", full_path, offset, len);
-
- free(full_path);
- return 0;
-}
-
-static struct btrfs_send_ops send_ops_print = {
- .subvol = print_subvol,
- .snapshot = print_snapshot,
- .mkfile = print_mkfile,
- .mkdir = print_mkdir,
- .mknod = print_mknod,
- .mkfifo = print_mkfifo,
- .mksock = print_mksock,
- .symlink = print_symlink,
- .rename = print_rename,
- .link = print_link,
- .unlink = print_unlink,
- .rmdir = print_rmdir,
- .write = print_write,
- .clone = print_clone,
- .set_xattr = print_set_xattr,
- .remove_xattr = print_remove_xattr,
- .truncate = print_truncate,
- .chmod = print_chmod,
- .chown = print_chown,
- .utimes = print_utimes,
- .update_extent = print_update_extent,
-};
-
-static void *process_thread(void *arg_)
-{
- int ret;
-
- while (1) {
- ret = btrfs_read_and_process_send_stream(pipefd[0],
- &send_ops_print, arg_, 0);
- if (ret)
- break;
- }
-
- if (ret > 0)
- ret = 0;
-
- return ERR_PTR(ret);
-}
-
-int main(int argc, char **argv)
-{
- int ret = 0;
- int subvol_fd;
- pthread_t t_read;
- void *t_err = NULL;
- struct recv_args r;
-
- if (argc != 3)
- usage(EINVAL);
-
- root_path = realpath(argv[1], NULL);
- if (!root_path) {
- ret = errno;
- usage(ret);
- }
-
- subvol_path = realpath(argv[2], NULL);
- if (!subvol_path) {
- ret = errno;
- usage(ret);
- }
-
- r.full_subvol_path = subvol_path;
- r.root_path = root_path;
-
- subvol_fd = open(subvol_path, O_RDONLY|O_NOATIME);
- if (subvol_fd < 0) {
- ret = errno;
- fprintf(stderr, "ERROR: Subvolume open failed. %s\n",
- strerror(ret));
- goto out;
- }
-
- ret = pipe(pipefd);
- if (ret < 0) {
- ret = errno;
- fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(ret));
- goto out;
- }
-
- ret = pthread_create(&t_read, NULL, process_thread, &r);
- if (ret < 0) {
- ret = errno;
- fprintf(stderr, "ERROR: pthread create failed. %s\n",
- strerror(ret));
- goto out;
- }
-
- io_send.send_fd = pipefd[1];
- io_send.clone_sources_count = 0;
- io_send.clone_sources = NULL;
- io_send.parent_root = 0;
- io_send.flags = BTRFS_SEND_FLAG_NO_FILE_DATA;
-
- ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
- if (ret < 0) {
- ret = errno;
- fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
- strerror(ret));
- goto out;
- }
-
- close(pipefd[1]);
-
- ret = pthread_join(t_read, &t_err);
- if (ret) {
- fprintf(stderr, "ERROR: pthread_join failed: %s\n",
- strerror(ret));
- goto out;
- }
- if (t_err) {
- ret = (long int)t_err;
- fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
- "(%s)\n", (long int)t_err, strerror(ret));
- goto out;
- }
-
-out:
- return !!ret;
-}
diff --git a/send-utils.c b/send-utils.c
index a85fa084..384cc5b7 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -439,6 +439,19 @@ struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
const char *path,
enum subvol_search_type type)
{
+ struct subvol_info *si;
+
+ si = subvol_uuid_search2(s, root_id, uuid, transid, path, type);
+ if (IS_ERR(si))
+ return NULL;
+ return si;
+}
+
+struct subvol_info *subvol_uuid_search2(struct subvol_uuid_search *s,
+ u64 root_id, const u8 *uuid, u64 transid,
+ const char *path,
+ enum subvol_search_type type)
+{
int ret = 0;
struct btrfs_root_item root_item;
struct subvol_info *info = NULL;
@@ -474,6 +487,10 @@ struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
goto out;
info = calloc(1, sizeof(*info));
+ if (!info) {
+ ret = -ENOMEM;
+ goto out;
+ }
info->root_id = root_id;
memcpy(info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
memcpy(info->received_uuid, root_item.received_uuid, BTRFS_UUID_SIZE);
@@ -484,17 +501,27 @@ struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
info->rtransid = btrfs_root_rtransid(&root_item);
if (type == subvol_search_by_path) {
info->path = strdup(path);
+ if (!info->path) {
+ ret = -ENOMEM;
+ goto out;
+ }
} else {
info->path = malloc(PATH_MAX);
+ if (!info->path) {
+ ret = -ENOMEM;
+ goto out;
+ }
ret = btrfs_subvolid_resolve(s->mnt_fd, info->path,
PATH_MAX, root_id);
}
out:
- if (ret && info) {
- free(info->path);
- free(info);
- info = NULL;
+ if (ret) {
+ if (info) {
+ free(info->path);
+ free(info);
+ }
+ return ERR_PTR(ret);
}
return info;
diff --git a/send-utils.h b/send-utils.h
index 677a1db2..e8f86912 100644
--- a/send-utils.h
+++ b/send-utils.h
@@ -80,10 +80,25 @@ struct subvol_uuid_search {
int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s);
void subvol_uuid_search_finit(struct subvol_uuid_search *s);
+/*
+ * Search for a subvolume by given type (received uuid, root id, path), returns
+ * pointer to newly allocated struct subvol_info or NULL in case it's not found
+ * or there was another error. This ambiguity of error value is fixed by
+ * subvol_uuid_search2 that returns a negative errno in case of an error, of a
+ * valid pointer otherwise.
+ *
+ * This function will be deprecated in the future, please consider using v2 in
+ * new code unless you need to keep backward compatibility with older
+ * btrfs-progs.
+ */
struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
u64 root_id, const u8 *uuid, u64 transid,
const char *path,
enum subvol_search_type type);
+struct subvol_info *subvol_uuid_search2(struct subvol_uuid_search *s,
+ u64 root_id, const u8 *uuid, u64 transid,
+ const char *path,
+ enum subvol_search_type type);
void subvol_uuid_search_add(struct subvol_uuid_search *s,
struct subvol_info *si);
diff --git a/string-table.c b/string-table.c
index 5eda1ac5..95833768 100644
--- a/string-table.c
+++ b/string-table.c
@@ -49,7 +49,7 @@ struct string_table *table_create(int columns, int rows)
* be replaced by a '=====' dimensioned on the basis of the column width
*/
char *table_vprintf(struct string_table *tab, int column, int row,
- char *fmt, va_list ap)
+ const char *fmt, va_list ap)
{
int idx = tab->ncols * row + column;
char *msg = calloc(100, 1);
@@ -71,7 +71,7 @@ char *table_vprintf(struct string_table *tab, int column, int row,
*/
__attribute__ ((format (printf, 4, 5)))
char *table_printf(struct string_table *tab, int column, int row,
- char *fmt, ...)
+ const char *fmt, ...)
{
va_list ap;
char *ret;
diff --git a/string-table.h b/string-table.h
index c1695d8d..516ea97a 100644
--- a/string-table.h
+++ b/string-table.h
@@ -25,9 +25,9 @@ struct string_table {
struct string_table *table_create(int columns, int rows);
char *table_printf(struct string_table *tab, int column, int row,
- char *fmt, ...);
+ const char *fmt, ...);
char *table_vprintf(struct string_table *tab, int column, int row,
- char *fmt, va_list ap);
+ const char *fmt, va_list ap);
void table_dump(struct string_table *tab);
void table_free(struct string_table *);
diff --git a/super-recover.c b/super-recover.c
index 635e804b..5298d46c 100644
--- a/super-recover.c
+++ b/super-recover.c
@@ -88,24 +88,6 @@ void free_recover_superblock(struct btrfs_recover_superblock *recover)
}
}
-static int check_super(u64 bytenr, struct btrfs_super_block *sb)
-{
- int csum_size = btrfs_super_csum_size(sb);
- char result[csum_size];
- u32 crc = ~(u32)0;
-
- if (btrfs_super_bytenr(sb) != bytenr)
- return 0;
- if (sb->magic != cpu_to_le64(BTRFS_MAGIC))
- return 0;
-
- crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE,
- crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, result);
-
- return !memcmp(sb, &result, csum_size);
-}
-
static int add_superblock_record(struct btrfs_super_block *sb, char *fname,
u64 bytenr, struct list_head *head)
{
@@ -133,24 +115,20 @@ read_dev_supers(char *filename, struct btrfs_recover_superblock *recover)
int i, ret, fd;
u8 buf[BTRFS_SUPER_INFO_SIZE];
u64 max_gen, bytenr;
+ struct btrfs_super_block *sb = (struct btrfs_super_block *)buf;
+
/* just ignore errno that were set in btrfs_scan_fs_devices() */
errno = 0;
- struct btrfs_super_block *sb = (struct btrfs_super_block *)buf;
-
fd = open(filename, O_RDONLY);
if (fd < 0)
return -errno;
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
- ret = pread64(fd, buf, sizeof(buf), bytenr);
- if (ret < sizeof(buf)) {
- ret = -errno;
- goto out;
- }
- ret = check_super(bytenr, sb);
- if (ret) {
+
+ ret = btrfs_read_dev_super(fd, sb, bytenr, SBREAD_DEFAULT);
+ if (!ret) {
ret = add_superblock_record(sb, filename, bytenr,
&recover->good_supers);
if (ret)
@@ -158,13 +136,18 @@ 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 {
+ } else if (ret == -EIO){
+ /*
+ * Skip superblock which doesn't exist, only adds
+ * really corrupted superblock
+ */
ret = add_superblock_record(sb, filename, bytenr,
&recover->bad_supers);
if (ret)
goto out;
}
}
+ ret = 0;
out:
close(fd);
return ret;
diff --git a/tests/README.md b/tests/README.md
index ca45cf6f..bb2846a1 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,7 +1,8 @@
# Btrfs-progs tests
A testsuite covering functionality of btrfs-progs, ie. the checker, image, mkfs
-and similar tools. There are no special requirements on kernel features, the
+and similar tools. There are no additional requirements on kernel features
+(other than `CONFIG_BTRFS_FS` built-in or module), the
tests build on top of the core functionality like snapshots and device
management. In some cases optional features are turned on by mkfs and the
filesystem image could be mounted, such tests might fail if there's lack of
@@ -106,11 +107,22 @@ are possible.
Note: instrumentation is not applied to privileged commands (anything that uses
the root helper).
-### Verbosity
+### Verbosity, test tuning
-Setting the variable `TEST_LOG=tty` will print all commands executed by some of
-the wrappers (`run_check` etc), other commands are not printed to the terminal
-(but the full output is in the log).
+* `TEST_LOG=tty` -- setting the variable will print all commands executed by
+ some of the wrappers (`run_check` etc), other commands are not printed to the
+ terminal (but the full output is in the log)
+
+* `TEST_LOG=dump` -- dump the entire testing log when a test fails
+
+* `TEST_ENABLE_OVERRIDE` -- defined either as make arguments or via
+ `tests/common.local` to enable additional arguments to some commands, using
+ the variable(s) below (default: false, enable by setting to 'true')
+
+* `TEST_ARGS_CHECK` -- user-defined arguments to `btrfs check`, before the
+ test-specific arguments
+
+Multiple values can be separated by `,`.
### Permissions
diff --git a/tests/build-tests.sh b/tests/build-tests.sh
new file mode 100755
index 00000000..04e3fd19
--- /dev/null
+++ b/tests/build-tests.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+# test various compilation options
+# - 32bit, 64bit
+# - dynamic, static
+# - various configure options
+#
+# Arguments: anything will be passed to 'make', eg. define CC, D, V
+#
+# Requirements for full coverage:
+# - static version of all libs
+# - 32bit/64bit libraries, also the static variants
+
+make=make
+opts="-j16 $@"
+
+conf=
+target=
+
+function die() {
+ echo "ERROR: $@"
+ exit 1
+}
+
+function check_result() {
+ local ret
+ local str
+
+ ret=$1
+
+ str="RESULT of target($target) conf($conf): "
+ case $ret in
+ 0) str="$str OK";;
+ *) str="$str FAIL";;
+ esac
+ echo "$str"
+ verdict="$verdict
+$str"
+}
+
+function buildme() {
+ make clean-all
+
+ ./autogen.sh && configure "$conf" || die "configure not working with: $@"
+ $make clean
+ $make $opts $target
+ check_result "$?"
+ echo "VERDICT: $verdict"
+}
+
+function build_make_targets() {
+ # defaults
+ target=
+ buildme
+ # defaults, static
+ target=static
+ buildme
+ # defaults, 32bit
+ target="EXTRA_CFLAGS=-m32"
+ buildme
+ # defaults, 64bit
+ target="EXTRA_CFLAGS=-m64"
+ buildme
+ # defaults, library
+ target="library-test"
+ buildme
+}
+
+# main()
+if ! [ -f configure.ac ]; then
+ echo "Please run me from the top directory"
+ exit 1
+fi
+
+verdict=
+conf=
+build_make_targets
+
+conf='--disable-documentation'
+build_make_targets
+
+conf='--disable-backtrace'
+build_make_targets
+
+conf='--disable-convert'
+build_make_targets
+
+echo "---------------------------------------------------"
+echo "$verdict"
diff --git a/tests/clean-tests.sh b/tests/clean-tests.sh
index 7f18e6f0..61baa069 100755
--- a/tests/clean-tests.sh
+++ b/tests/clean-tests.sh
@@ -1,9 +1,9 @@
#!/bin/bash
# remove all intermediate files from tests
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
-source $TOP/tests/common
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+TOP=$(readlink -f "$SCRIPT_DIR/../")
+source "$TOP/tests/common"
setup_root_helper
@@ -13,7 +13,7 @@ fi
$SUDO_HELPER umount "$TEST_MNT" &>/dev/null
-if ! cd $TOP/tests; then
+if ! cd "$TOP/tests"; then
echo "ERROR: cannot cd to $TOP/tests"
exit 1
fi
diff --git a/tests/cli-tests.sh b/tests/cli-tests.sh
index 72f7865a..16d6afcf 100755
--- a/tests/cli-tests.sh
+++ b/tests/cli-tests.sh
@@ -3,13 +3,13 @@
# command line interface coverage tests
LANG=C
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+TOP=$(readlink -f "$SCRIPT_DIR/../")
TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/cli-tests-results.txt"
IMAGE="$TOP/tests/test.img"
-source $TOP/tests/common
+source "$TOP/tests/common"
export TOP
export RESULTS
@@ -17,24 +17,28 @@ export LANG
export IMAGE
export TEST_DEV
-rm -f $RESULTS
+rm -f "$RESULTS"
check_prereq btrfs
+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 "$TOP/tests/cli-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
- name=$(basename $i)
- cd $i
+ name=$(basename "$i")
+ cd "$i"
if [ -x test.sh ]; then
- echo "=== Entering $i" >> $RESULTS
+ echo "=== Entering $i" >> "$RESULTS"
echo " [TEST/cli] $name"
./test.sh
if [ $? -ne 0 ]; then
+ if [[ $TEST_LOG =~ dump ]]; then
+ cat "$RESULTS"
+ fi
_fail "test failed for case $(basename $i)"
fi
fi
- cd $TOP
+ cd "$TOP"
done
diff --git a/tests/cli-tests/001-btrfs/test.sh b/tests/cli-tests/001-btrfs/test.sh
index 1de2f6f2..c680604b 100755
--- a/tests/cli-tests/001-btrfs/test.sh
+++ b/tests/cli-tests/001-btrfs/test.sh
@@ -1,15 +1,18 @@
#!/bin/bash
# test commands of btrfs
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
# returns 1
run_mayfail $TOP/btrfs || true
-run_check $TOP/btrfs version
-run_check $TOP/btrfs version --
-run_check $TOP/btrfs help
-run_check $TOP/btrfs help --
-run_check $TOP/btrfs --help
-run_check $TOP/btrfs --help --full
+run_check "$TOP/btrfs" version
+run_check "$TOP/btrfs" version --
+run_check "$TOP/btrfs" help
+run_check "$TOP/btrfs" help --
+run_check "$TOP/btrfs" help --full
+run_check "$TOP/btrfs" --help
+run_check "$TOP/btrfs" --help --full
+run_check "$TOP/btrfs" --version
+run_check "$TOP/btrfs" --version --help
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 c2757f24..81a719eb 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 "$TOP/tests/common"
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -10,12 +10,12 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev 2g
-run_check $TOP/mkfs.btrfs -f $IMAGE
+run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
run_check_mount_test_dev
-run_check $SUDO_HELPER $TOP/btrfs balance start --full-balance $TEST_MNT
-run_check $SUDO_HELPER $TOP/btrfs balance start $TEST_MNT
-run_check $SUDO_HELPER $TOP/btrfs balance --full-balance $TEST_MNT
-run_check $SUDO_HELPER $TOP/btrfs balance $TEST_MNT
+run_check $SUDO_HELPER "$TOP/btrfs" balance start --full-balance "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" balance start "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" balance --full-balance "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" balance "$TEST_MNT"
run_check_umount_test_dev
diff --git a/tests/cli-tests/003-fi-resize-args/test.sh b/tests/cli-tests/003-fi-resize-args/test.sh
index 2f136fa2..b835e078 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 "$TOP/tests/common"
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -10,37 +10,37 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev 2g
-run_check $TOP/mkfs.btrfs -f $IMAGE
+run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
run_check_mount_test_dev
# missing the one of the required arguments
for sep in '' '--'; do
- run_check_stdout $TOP/btrfs filesystem resize $sep |
+ run_check_stdout "$TOP/btrfs" filesystem resize $sep |
grep -q "btrfs filesystem resize: too few arguments"
- run_check_stdout $TOP/btrfs filesystem resize $sep $TEST_MNT |
+ run_check_stdout "$TOP/btrfs" filesystem resize $sep "$TEST_MNT" |
grep -q "btrfs filesystem resize: too few arguments"
- run_check_stdout $TOP/btrfs filesystem resize $sep -128M |
+ run_check_stdout "$TOP/btrfs" filesystem resize $sep -128M |
grep -q "btrfs filesystem resize: too few arguments"
- run_check_stdout $TOP/btrfs filesystem resize $sep +128M |
+ run_check_stdout "$TOP/btrfs" filesystem resize $sep +128M |
grep -q "btrfs filesystem resize: too few arguments"
- run_check_stdout $TOP/btrfs filesystem resize $sep 512M |
+ run_check_stdout "$TOP/btrfs" filesystem resize $sep 512M |
grep -q "btrfs filesystem resize: too few arguments"
- run_check_stdout $TOP/btrfs filesystem resize $sep 1:-128M |
+ run_check_stdout "$TOP/btrfs" filesystem resize $sep 1:-128M |
grep -q "btrfs filesystem resize: too few arguments"
- run_check_stdout $TOP/btrfs filesystem resize $sep 1:512M |
+ run_check_stdout "$TOP/btrfs" filesystem resize $sep 1:512M |
grep -q "btrfs filesystem resize: too few arguments"
- run_check_stdout $TOP/btrfs filesystem resize $sep 1:+128M |
+ run_check_stdout "$TOP/btrfs" filesystem resize $sep 1:+128M |
grep -q "btrfs filesystem resize: too few arguments"
done
# valid resize
for sep in '' '--'; do
- run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep -128M $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep +128M $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep 512M $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep 1:-128M $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep 1:512M $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep 1:+128M $TEST_MNT
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem resize $sep -128M "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem resize $sep +128M "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem resize $sep 512M "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem resize $sep 1:-128M "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem resize $sep 1:512M "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem resize $sep 1:+128M "$TEST_MNT"
done
run_check_umount_test_dev
diff --git a/tests/cli-tests/004-send-parent-multi-subvol/test.sh b/tests/cli-tests/004-send-parent-multi-subvol/test.sh
new file mode 100755
index 00000000..72a9eb36
--- /dev/null
+++ b/tests/cli-tests/004-send-parent-multi-subvol/test.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# minimal test for the following syntax: btrfs send -p parent subvol1 subvol2
+
+source "$TOP/tests/common"
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev 2g
+
+run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check_mount_test_dev
+
+here=`pwd`
+cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create subv-parent
+run_check $SUDO_HELPER dd if=/dev/urandom of=subv-parent/file bs=1M count=10
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv-parent subv-snap1
+run_check $SUDO_HELPER dd if=/dev/urandom of=subv-parent/file bs=1M count=10
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv-parent subv-snap2
+run_check $SUDO_HELPER dd if=/dev/urandom of=subv-parent/file bs=1M count=10
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv-parent subv-snap3
+
+run_check truncate -s0 "$here"/send.stream
+run_check chmod a+w "$here"/send.stream
+run_check $SUDO_HELPER "$TOP/btrfs" send -f "$here"/send.stream -p subv-snap1 subv-snap2 subv-snap3
+
+cd "$here" || _fail "cannot chdir back to test directory"
+rm send.stream
+
+run_check_umount_test_dev
diff --git a/tests/cli-tests/005-qgroup-show/test.sh b/tests/cli-tests/005-qgroup-show/test.sh
new file mode 100755
index 00000000..46d3c3a7
--- /dev/null
+++ b/tests/cli-tests/005-qgroup-show/test.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# qgroup show behaviour when quotas are not enabled
+
+source "$TOP/tests/common"
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev 2g
+
+run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check_mount_test_dev
+run_mayfail "$TOP/btrfs" qgroup show "$TEST_MNT"
+run_mayfail $SUDO_HELPER "$TOP/btrfs" qgroup show "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" quota enable "$TEST_MNT"
+run_mayfail "$TOP/btrfs" qgroup show "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" qgroup show "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" quota disable "$TEST_MNT"
+run_check_umount_test_dev
diff --git a/tests/cli-tests/006-qgroup-show-sync/test.sh b/tests/cli-tests/006-qgroup-show-sync/test.sh
new file mode 100755
index 00000000..30d0a9a1
--- /dev/null
+++ b/tests/cli-tests/006-qgroup-show-sync/test.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# simple test of qgroup show --sync option
+
+source "$TOP/tests/common"
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev 1g
+
+run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check_mount_test_dev
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/Sub"
+run_check $SUDO_HELPER "$TOP/btrfs" quota enable "$TEST_MNT/Sub"
+
+for opt in '' '--' '--sync'; do
+ run_check $SUDO_HELPER "$TOP/btrfs" qgroup limit 300M "$TEST_MNT/Sub"
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/Sub/file" bs=1M count=200
+
+ run_check $SUDO_HELPER "$TOP/btrfs" qgroup show -re $opt "$TEST_MNT/Sub"
+
+ run_check $SUDO_HELPER "$TOP/btrfs" qgroup limit none "$TEST_MNT/Sub"
+ run_check $SUDO_HELPER rm -f "$TEST_MNT/Sub/file"
+ run_check "$TOP/btrfs" filesystem sync "$TEST_MNT/Sub"
+done
+
+run_check_umount_test_dev
diff --git a/tests/common b/tests/common
index c50b661f..51c2e267 100644
--- a/tests/common
+++ b/tests/common
@@ -3,16 +3,34 @@
# Common routines for all tests
#
+# assert that argument is not empty and is an existing path (file or directory)
+_assert_path()
+{
+ local path
+
+ path="$1"
+ if [ -z "$path" ]; then
+ echo "ASSERTION FAIL: $path is not valid"
+ exit 1
+ fi
+
+ if [ -f "$path" -o -d "$path" -o -b "$path" ]; then
+ return 0
+ fi
+ echo "ASSERTION FAIL: $path is not valid"
+ exit 1
+}
+
_fail()
{
- echo "$*" | tee -a $RESULTS
+ echo "$*" | tee -a "$RESULTS"
exit 1
}
# log a message to the results file
_log()
{
- echo "$*" | tee -a $RESULTS
+ echo "$*" | tee -a "$RESULTS"
}
_not_run()
@@ -21,14 +39,81 @@ _not_run()
exit 0
}
+# debugging helper
+_dump_args()
+{
+ local i
+
+ i=1
+ echo "DUMP args for ${FUNCNAME[1]}:"
+ while [ $# -gt 0 ]; do
+ echo "ARG[$i]: $1"
+ i=$(($i+1))
+ shift
+ done
+}
+
+# read arguments, look if we're calling btrfs and if there's a known
+# subcommand, return argument index to insert, taking root helper into
+# consideration, returns 2 for unknown subcommand
+_get_spec_ins()
+{
+ if [ "$1" = 'root_helper' ]; then
+ if [[ $2 =~ /btrfs$ ]]; then
+ echo -n 4
+ return
+ fi
+ else
+ if [[ $1 =~ /btrfs$ ]]; then
+ echo -n 3
+ return
+ fi
+ fi
+ echo -n 2
+}
+
+# return command-specific arguments if enabled
+_cmd_spec()
+{
+ if [ "$TEST_ENABLE_OVERRIDE" = 'true' ]; then
+ # if defined via common.local, use it, otherwise pass make
+ # arguments
+ if [ "$(type -t _skip_spec)" = 'function' ]; then
+ if _skip_spec "$@"; then
+ return
+ fi
+ fi
+ case "$1" in
+ check) echo -n "$TEST_ARGS_CHECK" ;;
+ esac
+ fi
+}
+
+# Argument passing magic:
+# the command passed to run_* helpers is inspected, if there's 'btrfs command'
+# found and there are defined additional arguments, they're inserted just after
+# the command name, ie. any arguments in the test could override them.
+#
+# The root helper is recognized. Unrecognized subcommands or external tools
+# are not affected.
+
run_check()
{
- echo "############### $@" >> $RESULTS 2>&1
- if [ "$TEST_LOG" = 'tty' ]; then echo "CMD: $@" > /dev/tty; fi
+ local spec
+ local ins
+ local cmd
+
+ ins=$(_get_spec_ins "$@")
+ spec=$(($ins-1))
+ cmd=$(eval echo "\${$spec}")
+ spec=$(_cmd_spec "$cmd")
+ set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
+ echo "############### $@" >> "$RESULTS" 2>&1
+ if [[ $TEST_LOG =~ tty ]]; then echo "CMD: $@" > /dev/tty; fi
if [ "$1" = 'root_helper' ]; then
- "$@" >> $RESULTS 2>&1 || _fail "failed: $@"
+ "$@" >> "$RESULTS" 2>&1 || _fail "failed: $@"
else
- $INSTRUMENT "$@" >> $RESULTS 2>&1 || _fail "failed: $@"
+ $INSTRUMENT "$@" >> "$RESULTS" 2>&1 || _fail "failed: $@"
fi
}
@@ -36,28 +121,55 @@ run_check()
# can be processed further
run_check_stdout()
{
- echo "############### $@" >> $RESULTS 2>&1
- if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(stdout): $@" > /dev/tty; fi
+ local spec
+ local ins
+ local cmd
+
+ ins=$(_get_spec_ins "$@")
+ spec=$(($ins-1))
+ cmd=$(eval echo "\${$spec}")
+ spec=$(_cmd_spec "$cmd")
+ set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
+ echo "############### $@" >> "$RESULTS" 2>&1
+ if [[ $TEST_LOG =~ tty ]]; then echo "CMD(stdout): $@" > /dev/tty; fi
if [ "$1" = 'root_helper' ]; then
- "$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@"
+ "$@" 2>&1 | tee -a "$RESULTS" || _fail "failed: $@"
else
- $INSTRUMENT "$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@"
+ $INSTRUMENT "$@" 2>&1 | tee -a "$RESULTS" || _fail "failed: $@"
fi
}
-# same as run_check but does not fail the test, output is logged
+# same as run_check but does not fail the test if it's handled gracefully by
+# the tool, unexpected failure like segfault or abor will exit forcibly
+# output is logged
run_mayfail()
{
- echo "############### $@" >> $RESULTS 2>&1
- if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(mayfail): $@" > /dev/tty; fi
+ local spec
+ local ins
+ local cmd
+ local ret
+
+ ins=$(_get_spec_ins "$@")
+ spec=$(($ins-1))
+ cmd=$(eval echo "\${$spec}")
+ spec=$(_cmd_spec "$cmd")
+ set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
+ echo "############### $@" >> "$RESULTS" 2>&1
+ if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mayfail): $@" > /dev/tty; fi
if [ "$1" = 'root_helper' ]; then
- "$@" >> $RESULTS 2>&1
+ "$@" >> "$RESULTS" 2>&1
else
- $INSTRUMENT "$@" >> $RESULTS 2>&1
+ $INSTRUMENT "$@" >> "$RESULTS" 2>&1
fi
- if [ $? != 0 ]; then
- echo "failed (ignored): $@" >> $RESULTS
- return 1
+ ret=$?
+ if [ $ret != 0 ]; then
+ echo "failed (ignored, ret=$ret): $@" >> "$RESULTS"
+ if [ $ret == 139 ]; then
+ _fail "mayfail: returned code 139 (SEGFAULT), not ignored"
+ elif [ $ret == 134 ]; then
+ _fail "mayfail: returned code 134 (SIGABRT), not ignored"
+ fi
+ return $ret
fi
}
@@ -65,23 +177,31 @@ run_mayfail()
# same as run_check but expects the command to fail, output is logged
run_mustfail()
{
+ local spec
+ local ins
+ local cmd
local msg
msg="$1"
shift
- echo "############### $@" >> $RESULTS 2>&1
- if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(mustfail): $@" > /dev/tty; fi
+ ins=$(_get_spec_ins "$@")
+ spec=$(($ins-1))
+ cmd=$(eval echo "\${$spec}")
+ spec=$(_cmd_spec "$cmd")
+ set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
+ echo "############### $@" >> "$RESULTS" 2>&1
+ if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): $@" > /dev/tty; fi
if [ "$1" = 'root_helper' ]; then
- "$@" >> $RESULTS 2>&1
+ "$@" >> "$RESULTS" 2>&1
else
- $INSTRUMENT "$@" >> $RESULTS 2>&1
+ $INSTRUMENT "$@" >> "$RESULTS" 2>&1
fi
if [ $? != 0 ]; then
- echo "failed (expected): $@" >> $RESULTS
+ echo "failed (expected): $@" >> "$RESULTS"
return 0
else
- echo "succeeded (unexpected!): $@" >> $RESULTS
+ echo "succeeded (unexpected!): $@" >> "$RESULTS"
_fail "unexpected success: $msg"
return 1
fi
@@ -89,7 +209,7 @@ run_mustfail()
check_prereq()
{
- if ! [ -f $TOP/$1 ]; then
+ if ! [ -f "$TOP/$1" ]; then
_fail "Failed prerequisites: $1";
fi
}
@@ -107,12 +227,12 @@ check_image()
local image
image=$1
- echo "testing image $(basename $image)" >> $RESULTS
- $TOP/btrfs check $image >> $RESULTS 2>&1
+ echo "testing image $(basename $image)" >> "$RESULTS"
+ "$TOP/btrfs" check "$image" >> "$RESULTS" 2>&1
[ $? -eq 0 ] && _fail "btrfs check should have detected corruption"
- run_check $TOP/btrfs check --repair $image
- run_check $TOP/btrfs check $image
+ run_check "$TOP/btrfs" check --repair "$image"
+ run_check "$TOP/btrfs" check "$image"
}
# Extract a usable image from packed formats
@@ -120,6 +240,7 @@ check_image()
# - dtto compressed by XZ, suffix .raw.xz
# - meta-dump images with suffix .img
# - dtto compressed by XZ, suffix .img.xz
+# - compressed send stream, .stream.xz
extract_image()
{
local image
@@ -128,30 +249,36 @@ extract_image()
image="$1"
case "$image" in
*.img)
- rm -f $image.restored
+ rm -f "$image.restored"
: ;;
*.img.xz)
xz --decompress --keep "$image" || \
_fail "failed to decompress image $image" >&2
image=${image%%.xz}
- rm -f $image.restored
+ rm -f "$image.restored"
cleanme=$image
;;
*.raw)
- cp --sparse=auto $image $image.restored
+ cp --sparse=auto "$image" "$image.restored"
;;
*.raw.xz)
xz --decompress --keep "$image" || \
_fail "failed to decompress image $image" >&2
image=${image%%.xz}
- mv "$image" "$image".restored
+ mv "$image" "$image.restored"
+ ;;
+ *.stream.xz)
+ xz --decompress --keep "$image" || \
+ _fail "failed to decompress file $image" >&2
+ image=${image%%.xz}
+ mv "$image" "$image.restored"
;;
esac
- if ! [ -f $image.restored ]; then
- echo "restoring image $(basename $image)" >> $RESULTS
- $TOP/btrfs-image -r $image $image.restored \
- &>> $RESULTS \
+ if ! [ -f "$image.restored" ]; then
+ echo "restoring image $(basename $image)" >> "$RESULTS"
+ "$TOP/btrfs-image" -r "$image" "$image.restored" \
+ &>> "$RESULTS" \
|| _fail "failed to restore image $image" >&2
fi
@@ -167,7 +294,11 @@ check_all_images()
local extracted
dir="$1"
- for image in $(find $dir \( -iname '*.img' -o \
+ if [ -z "$dir" ]; then
+ dir=.
+ fi
+ _assert_path "$dir"
+ for image in $(find "$dir" \( -iname '*.img' -o \
-iname '*.img.xz' -o \
-iname '*.raw' -o \
-iname '*.raw.xz' \) | sort)
@@ -233,7 +364,7 @@ prepare_test_dev()
[[ "$size" ]] || size='2G'
echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
- $RESULTS
+ "$RESULTS"
TEST_DEV="$TOP/tests/test.img"
truncate -s "$size" "$TEST_DEV" || _not_run "create file for loop device failed"
@@ -265,10 +396,114 @@ run_check_umount_test_dev()
run_check $SUDO_HELPER umount "$@" "$TEST_DEV"
}
+check_kernel_support()
+{
+ if ! grep -iq 'btrfs' /proc/filesystems; then
+ echo "WARNING: btrfs filesystem not listed in /proc/filesystems, some tests might fail"
+ return 1
+ fi
+ return 0
+}
+
+# how many files to create.
+DATASET_SIZE=50
+
+generate_dataset() {
+
+ dataset_type="$1"
+ dirpath=$TEST_MNT/$dataset_type
+ run_check $SUDO_HELPER mkdir -p "$dirpath"
+
+ case "$dataset_type" in
+ small)
+ for num in $(seq 1 "$DATASET_SIZE"); do
+ run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
+ count=1 >/dev/null 2>&1
+ done
+ ;;
+
+ hardlink)
+ for num in $(seq 1 "$DATASET_SIZE"); do
+ run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
+ run_check $SUDO_HELPER ln "$dirpath/$dataset_type.$num" "$dirpath/hlink.$num"
+ done
+ ;;
+
+ fast_symlink)
+ for num in $(seq 1 "$DATASET_SIZE"); do
+ run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
+ run_check cd "$dirpath" && \
+ $SUDO_HELPER ln -s "$dataset_type.$num" "$dirpath/slink.$num" && \
+ cd /
+ done
+ ;;
+
+ brokenlink)
+ for num in $(seq 1 "$DATASET_SIZE"); do
+ run_check $SUDO_HELPER ln -s "$dirpath/$dataset_type.$num" "$dirpath/blink.$num"
+ done
+ ;;
+
+ perm)
+ for modes in 777 775 755 750 700 666 664 644 640 600 444 440 400 000 \
+ 1777 1775 1755 1750 1700 1666 1664 1644 1640 1600 1444 1440 1400 1000 \
+ 2777 2775 2755 2750 2700 2666 2664 2644 2640 2600 2444 2440 2400 2000 \
+ 4777 4775 4755 4750 4700 4666 4664 4644 4640 4600 4444 4440 4400 4000; do
+ run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$modes"
+ run_check $SUDO_HELPER chmod "$modes" "$dirpath/$dataset_type.$modes"
+ done
+ ;;
+
+ sparse)
+ for num in $(seq 1 "$DATASET_SIZE"); do
+ run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
+ count=1 >/dev/null 2>&1
+ run_check $SUDO_HELPER truncate -s 500K "$dirpath/$dataset_type.$num"
+ run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
+ oflag=append conv=notrunc count=1 >/dev/null 2>&1
+ run_check $SUDO_HELPER truncate -s 800K "$dirpath/$dataset_type.$num"
+ done
+ ;;
+
+ acls)
+ for num in $(seq 1 "$DATASET_SIZE"); do
+ run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
+ run_check $SUDO_HELPER setfacl -m "u:root:x" "$dirpath/$dataset_type.$num"
+ run_check $SUDO_HELPER setfattr -n user.foo -v "bar$num" "$dirpath/$dataset_type.$num"
+ done
+ ;;
+
+ fifo)
+ for num in $(seq 1 "$DATASET_SIZE"); do
+ run_check $SUDO_HELPER mkfifo "$dirpath/$dataset_type.$num"
+ done
+ ;;
+
+ slow_symlink)
+ long_filename=`date +%s | sha256sum | cut -f1 -d'-'`
+ run_check $SUDO_HELPER touch "$dirpath/$long_filename"
+ for num in $(seq 1 "$DATASET_SIZE"); do
+ run_check $SUDO_HELPER ln -s "$dirpath/$long_filename" "$dirpath/slow_slink.$num"
+ done
+ ;;
+ large)
+ run_check $SUDO_HELPER dd if=/dev/urandom bs=32M count=1 \
+ of="$dirpath/$dataset_type" >/dev/null 2>&1
+ ;;
+ esac
+}
+
init_env()
{
TEST_MNT="${TEST_MNT:-$TOP/tests/mnt}"
export TEST_MNT
mkdir -p "$TEST_MNT" || { echo "Failed mkdir -p $TEST_MNT"; exit 1; }
+
+ source $TOP/tests/common.local
+
+ if [ "$TEST_ENABLE_OVERRIDE" = 'true' -a -n "$RESULTS" ]; then
+ echo "INCLUDE common.local" >> "$RESULTS"
+ echo " check: $TEST_ARGS_CHECK" >> "$RESULTS"
+ fi
}
init_env
diff --git a/tests/common.convert b/tests/common.convert
index 1e00d389..8c9242e5 100644
--- a/tests/common.convert
+++ b/tests/common.convert
@@ -1,82 +1,9 @@
#!/bin/bash
# helpers for btrfs-convert tests
-# how many files to create.
-DATASET_SIZE=50
-
-generate_dataset() {
-
- dataset_type="$1"
- dirpath=$TEST_MNT/$dataset_type
- run_check $SUDO_HELPER mkdir -p $dirpath
-
- case $dataset_type in
- small)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
- count=1 >/dev/null 2>&1
- done
- ;;
-
- hardlink)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER ln $dirpath/$dataset_type.$num $dirpath/hlink.$num
- done
- ;;
-
- symlink)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER ln -s $dirpath/$dataset_type.$num $dirpath/slink.$num
- done
- ;;
-
- brokenlink)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER ln -s $dirpath/$dataset_type.$num $dirpath/blink.$num
- done
- ;;
-
- perm)
- for modes in 777 775 755 750 700 666 664 644 640 600 444 440 400 000 \
- 1777 1775 1755 1750 1700 1666 1664 1644 1640 1600 1444 1440 1400 1000 \
- 2777 2775 2755 2750 2700 2666 2664 2644 2640 2600 2444 2440 2400 2000 \
- 4777 4775 4755 4750 4700 4666 4664 4644 4640 4600 4444 4440 4400 4000; do
- if [[ "$modes" == *9* ]] || [[ "$modes" == *8* ]]
- then
- continue;
- else
- run_check $SUDO_HELPER touch $dirpath/$dataset_type.$modes
- run_check $SUDO_HELPER chmod $modes $dirpath/$dataset_type.$modes
- fi
- done
- ;;
-
- sparse)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
- count=1 >/dev/null 2>&1
- run_check $SUDO_HELPER truncate -s 500K $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
- oflag=append conv=notrunc count=1 >/dev/null 2>&1
- run_check $SUDO_HELPER truncate -s 800K $dirpath/$dataset_type.$num
- done
- ;;
-
- acls)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER setfacl -m "u:root:x" $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER setfattr -n user.foo -v bar$num $dirpath/$dataset_type.$num
- done
- ;;
- esac
-}
-
populate_fs() {
- for dataset_type in 'small' 'hardlink' 'symlink' 'brokenlink' 'perm' 'sparse' 'acls'; do
+ for dataset_type in 'small' 'hardlink' 'fast_symlink' 'brokenlink' 'perm' 'sparse' 'acls' 'fifo' 'slow_symlink'; do
generate_dataset "$dataset_type"
done
}
@@ -90,7 +17,7 @@ convert_test_preamble() {
msg="$2"
shift 3
echo " [TEST/conv] $msg, btrfs" "${features:-defaults}"
- echo "creating ext image with: $@" >> $RESULTS
+ echo "creating ext image with: $@" >> "$RESULTS"
}
# prepare TEST_DEV before conversion, create filesystem and mount it, image
@@ -99,10 +26,10 @@ convert_test_preamble() {
convert_test_prep_fs() {
# TEST_DEV not removed as the file might have special permissions, eg.
# when test image is on NFS and would not be writable for root
- run_check truncate -s 0 $TEST_DEV
+ run_check truncate -s 0 "$TEST_DEV"
# 256MB is the smallest acceptable btrfs image.
- run_check truncate -s 512M $TEST_DEV
- run_check "$@" -F $TEST_DEV
+ run_check truncate -s 512M "$TEST_DEV"
+ run_check "$@" -F "$TEST_DEV"
# create a file to check btrfs-convert can convert regular file correct
run_check_mount_test_dev
@@ -110,35 +37,36 @@ convert_test_prep_fs() {
# create a file inside the fs before convert, to make sure there is
# data covering btrfs backup superblock range (64M)
run_check $SUDO_HELPER dd if=/dev/zero bs=1M count=64 \
- of=$TEST_MNT/convert_space_holder
+ of="$TEST_MNT/convert_space_holder"
}
# generate md5 checksums of files on $TEST_MNT
# $1: path where the checksums will be stored
convert_test_gen_checksums() {
- local CHECKSUMTMP
- CHECKSUMTMP="$1"
+ _assert_path "$1"
- run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/test bs=$nodesize \
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/test" "bs=$nodesize" \
count=1 >/dev/null 2>&1
- run_check_stdout $SUDO_HELPER find $TEST_MNT -type f ! -name 'image' -exec md5sum {} \+ > "$CHECKSUMTMP"
+ run_check_stdout $SUDO_HELPER find "$TEST_MNT" -type f ! -name 'image' -exec md5sum {} \+ > "$1"
}
# list $TEST_MNT data set file permissions.
# $1: path where the permissions will be stored
convert_test_perm() {
local PERMTMP
+
+ _assert_path "$1"
PERMTMP="$1"
FILES_LIST=$(mktemp --tmpdir btrfs-progs-convert.fileslistXXXXXX)
- run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/test bs=$nodesize \
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/test" "bs=$nodesize" \
count=1 >/dev/null 2>&1
- run_check_stdout $SUDO_HELPER find $TEST_MNT -type f ! -name 'image' -fprint $FILES_LIST
+ run_check_stdout $SUDO_HELPER find "$TEST_MNT" -type f ! -name 'image' -fprint "$FILES_LIST"
# Fix directory entries order
- sort $FILES_LIST -o $FILES_LIST
- for file in `cat $FILES_LIST` ;do
- run_check_stdout $SUDO_HELPER getfacl --absolute-names $file >> "$PERMTMP"
+ sort "$FILES_LIST" -o "$FILES_LIST"
+ for file in `cat "$FILES_LIST"` ;do
+ run_check_stdout $SUDO_HELPER getfacl --absolute-names "$file" >> "$PERMTMP"
done
- rm -- $FILES_LIST
+ rm -- "$FILES_LIST"
}
# list acls of files on $TEST_MNT
# $1: path where the acls will be stored
@@ -147,22 +75,22 @@ convert_test_acl() {
ACLTMP="$1"
FILES_LIST=$(mktemp --tmpdir btrfs-progs-convert.fileslistXXXXXX)
- run_check_stdout $SUDO_HELPER find $TEST_MNT/acls -type f -fprint $FILES_LIST
+ run_check_stdout $SUDO_HELPER find "$TEST_MNT/acls" -type f -fprint "$FILES_LIST"
# Fix directory entries order
- sort $FILES_LIST -o $FILES_LIST
- for file in `cat $FILES_LIST`;do
- run_check_stdout $SUDO_HELPER getfattr --absolute-names -d $file >> "$ACLTMP"
+ sort "$FILES_LIST" -o "$FILES_LIST"
+ for file in `cat "$FILES_LIST"`;do
+ run_check_stdout $SUDO_HELPER getfattr --absolute-names -d "$file" >> "$ACLTMP"
done
- rm -- $FILES_LIST
+ rm -- "$FILES_LIST"
}
# do conversion with given features and nodesize, fsck afterwards
# $1: features, argument of -O, can be empty
# $2: nodesize, argument of -N, can be empty
convert_test_do_convert() {
- run_check $TOP/btrfs-convert ${1:+-O "$1"} ${2:+-N "$2"} $TEST_DEV
- run_check $TOP/btrfs check $TEST_DEV
- run_check $TOP/btrfs-show-super -Ffa $TEST_DEV
+ run_check "$TOP/btrfs-convert" ${1:+-O "$1"} ${2:+-N "$2"} "$TEST_DEV"
+ run_check "$TOP/btrfs" check "$TEST_DEV"
+ run_check "$TOP/btrfs" inspect-internal dump-super -Ffa "$TEST_DEV"
}
# post conversion check, verify file permissions.
@@ -171,21 +99,22 @@ convert_test_post_check_permissions() {
local EXT_PERMTMP
local BTRFS_PERMTMP
+ _assert_path "$1"
EXT_PERMTMP="$1"
BTRFS_PERMTMP=$(mktemp --tmpdir btrfs-progs-convert.permXXXXXX)
convert_test_perm "$BTRFS_PERMTMP"
- btrfs_perm=`md5sum $BTRFS_PERMTMP | cut -f1 -d' '`
- ext_perm=`md5sum $EXT_PERMTMP | cut -f1 -d' '`
+ btrfs_perm=`md5sum "$BTRFS_PERMTMP" | cut -f1 -d' '`
+ ext_perm=`md5sum "$EXT_PERMTMP" | cut -f1 -d' '`
if [ "$btrfs_perm" != "$ext_perm" ];
then
- btrfs_perm_file=`md5sum $BTRFS_PERMTMP | cut -f2 -d' '`
- ext_perm_file=`md5sum $EXT_PERMTMP | cut -f2 -d' '`
+ btrfs_perm_file=`md5sum "$BTRFS_PERMTMP" | cut -f2 -d' '`
+ ext_perm_file=`md5sum "$EXT_PERMTMP" | cut -f2 -d' '`
_fail "file permission failed. Mismatched BTRFS:$btrfs_perm_file:$btrfs_perm EXT:$ext_perm_file:$ext_perm"
fi
- rm -- $BTRFS_PERMTMP
+ rm -- "$BTRFS_PERMTMP"
}
# post conversion check, compare BTRFS file acls against EXT.
# $1: file with ext acls.
@@ -193,47 +122,54 @@ convert_test_post_check_acl() {
local EXT_ACLTMP
local BTRFS_ACLTMP
+ _assert_path "$1"
EXT_ACLTMP="$1"
BTRFS_ACLTMP=$(mktemp --tmpdir btrfs-progs-convert.aclsXXXXXXX)
convert_test_acl "$BTRFS_ACLTMP"
- btrfs_acl=`md5sum $BTRFS_ACLTMP | cut -f1 -d' '`
- ext_acl=`md5sum $EXT_ACLTMP | cut -f1 -d' '`
+ btrfs_acl=`md5sum "$BTRFS_ACLTMP" | cut -f1 -d' '`
+ ext_acl=`md5sum "$EXT_ACLTMP" | cut -f1 -d' '`
if [ "$btrfs_acl" != "$ext_acl" ]
then
- btrfs_acl_file=`md5sum $BTRFS_ACLTMP | cut -f2 -d' '`
- ext_acl_file=`md5sum $EXT_ACLTMP | cut -f2 -d' '`
+ btrfs_acl_file=`md5sum "$BTRFS_ACLTMP" | cut -f2 -d' '`
+ ext_acl_file=`md5sum "$EXT_ACLTMP" | cut -f2 -d' '`
_fail "file acl failed. Mismatched BTRFS:$btrfs_acl_file:$btrfs_acl EXT:$ext_acl_file:$ext_acl"
fi
- rm -- $BTRFS_ACLTMP
+ rm -- "$BTRFS_ACLTMP"
}
+
# post conversion checks, verify md5sums
+convert_test_post_check_checksums() {
+ _assert_path "$1"
+ run_check_stdout $SUDO_HELPER md5sum -c "$1" |
+ grep -q 'FAILED' && _fail "file validation failed"
+}
+
+# post conversion checks, all three in one call, on an unmounted image
# $1: file with checksums
# $2: file with permissions.
# $3: file with acl entries.
-convert_test_post_check() {
- local CHECKSUMTMP
- local EXT_PERMTMP
- local EXT_ACLTMP
-
- CHECKSUMTMP="$1"
- EXT_PERMTMP="$2"
- EXT_ACLTMP="$3"
+convert_test_post_checks_all() {
+ _assert_path "$1"
+ _assert_path "$2"
+ _assert_path "$3"
run_check_mount_test_dev
- run_check_stdout $SUDO_HELPER md5sum -c "$CHECKSUMTMP" |
- grep -q 'FAILED' && _fail "file validation failed"
- convert_test_post_check_permissions "$EXT_PERMTMP"
- convert_test_post_check_acl "$EXT_ACLTMP"
+ convert_test_post_check_checksums "$1"
+ convert_test_post_check_permissions "$2"
+ convert_test_post_check_acl "$3"
+
+ # Create a large file to trigger data chunk allocation
+ generate_dataset "large"
run_check_umount_test_dev
}
# do rollback and fsck
convert_test_post_rollback() {
- run_check $TOP/btrfs-convert --rollback $TEST_DEV
- run_check fsck -n -t ext2,ext3,ext4 $TEST_DEV
+ run_check "$TOP/btrfs-convert" --rollback "$TEST_DEV"
+ run_check fsck -n -t ext2,ext3,ext4 "$TEST_DEV"
}
# simple wrapper for a convert test
@@ -266,10 +202,10 @@ convert_test() {
run_check_umount_test_dev
convert_test_do_convert "$features" "$nodesize"
- convert_test_post_check "$CHECKSUMTMP" "$EXT_PERMTMP" "$EXT_ACLTMP"
- rm $CHECKSUMTMP
- rm $EXT_PERMTMP
- rm $EXT_ACLTMP
+ convert_test_post_checks_all "$CHECKSUMTMP" "$EXT_PERMTMP" "$EXT_ACLTMP"
+ rm -- "$CHECKSUMTMP"
+ rm -- "$EXT_PERMTMP"
+ rm -- "$EXT_ACLTMP"
convert_test_post_rollback
}
diff --git a/tests/common.local b/tests/common.local
new file mode 100644
index 00000000..9f567c27
--- /dev/null
+++ b/tests/common.local
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# additional arguments to various commands
+
+# already defined, eg. via make argument
+if [ -n "$TEST_ENABLE_OVERRIDE" ]; then
+ return
+fi
+
+# set to 'true'
+TEST_ENABLE_OVERRIDE=false
+
+TEST_ARGS_CHECK=--mode=lowmem
+
+# gets arguments of a current command and can decide if the argument insertion
+# should happen, eg. if some option combination does not make sense or would
+# break tests
+_skip_spec()
+{
+ if echo "$TEST_CHECK" | grep -q 'mode=lowmem' &&
+ echo "$@" | grep -q -- '--repair'; then
+ return 0
+ fi
+ return 1
+}
diff --git a/tests/convert-tests.sh b/tests/convert-tests.sh
index 0e025f99..c5663367 100755
--- a/tests/convert-tests.sh
+++ b/tests/convert-tests.sh
@@ -4,14 +4,14 @@
# clean.
LANG=C
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+TOP=$(readlink -f "$SCRIPT_DIR/../")
TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/convert-tests-results.txt"
IMAGE="$TOP/tests/test.img"
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
export TOP
export RESULTS
@@ -19,7 +19,9 @@ export LANG
export IMAGE
export TEST_DEV
-rm -f $RESULTS
+rm -f "$RESULTS"
+
+check_kernel_support
run_one_test() {
local testdir
@@ -29,12 +31,15 @@ run_one_test() {
testname=$(basename "$testdir")
echo " [TEST/conv] $testname"
cd "$testdir"
- echo "=== Entering $testname" >> $RESULTS
+ echo "=== Entering $testname" >> "$RESULTS"
if [ -x test.sh ]; then
# Only support custom test scripts
./test.sh
if [ $? -ne 0 ]; then
_fail "test failed for case $testname"
+ if [[ $TEST_LOG =~ dump ]]; then
+ cat "$RESULTS"
+ fi
fi
else
_fail "custom test script not found"
@@ -42,7 +47,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 "$TOP/tests/convert-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
run_one_test "$i"
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 d85e4de4..c56650b2 100755
--- a/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
+++ b/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
@@ -14,7 +14,6 @@ source $TOP/tests/common
check_prereq btrfs-convert
check_prereq btrfs
-check_prereq btrfs-show-super
check_global_prereq e2fsck
check_global_prereq xzcat
@@ -27,7 +26,7 @@ function check_image() {
run_check e2fsck -n -f $TEST_DEV
run_check $TOP/btrfs-convert $TEST_DEV
run_check $TOP/btrfs check $TEST_DEV
- run_check $TOP/btrfs-show-super $TEST_DEV
+ run_check $TOP/btrfs inspect-internal dump-super $TEST_DEV
run_check_mount_test_dev
run_check $SUDO_HELPER e2fsck -n -f $TEST_MNT/ext2_saved/image
diff --git a/tests/convert-tests/005-delete-all-rollback/test.sh b/tests/convert-tests/005-delete-all-rollback/test.sh
index d498e5f8..cf576e70 100755
--- a/tests/convert-tests/005-delete-all-rollback/test.sh
+++ b/tests/convert-tests/005-delete-all-rollback/test.sh
@@ -34,9 +34,10 @@ do_test() {
run_check_umount_test_dev
convert_test_do_convert "$features" "$nodesize"
- convert_test_post_check "$CHECKSUMTMP"
run_check_mount_test_dev
+ convert_test_post_check_checksums "$CHECKSUMTMP"
+
here=$(pwd)
cd "$TEST_MNT" || _fail "cannot cd to TEST_MNT"
# ext2_saved/image must not be deleted
@@ -45,10 +46,16 @@ do_test() {
run_check $TOP/btrfs filesystem sync "$TEST_MNT"
run_check_umount_test_dev
convert_test_post_rollback
- convert_test_post_check "$CHECKSUMTMP"
+
+ run_check_mount_test_dev
+ convert_test_post_check_checksums "$CHECKSUMTMP"
+ run_check_umount_test_dev
# mount again and verify checksums
- convert_test_post_check "$CHECKSUMTMP"
+ run_check_mount_test_dev
+ convert_test_post_check_checksums "$CHECKSUMTMP"
+ run_check_umount_test_dev
+
rm "$CHECKSUMTMP"
}
diff --git a/tests/convert-tests/009-common-inode-flags/test.sh b/tests/convert-tests/009-common-inode-flags/test.sh
new file mode 100755
index 00000000..6f26d187
--- /dev/null
+++ b/tests/convert-tests/009-common-inode-flags/test.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+# Check if btrfs-convert can copy common inode flags like SYNC/IMMUTABLE
+
+source $TOP/tests/common
+source $TOP/tests/common.convert
+
+setup_root_helper
+prepare_test_dev 512M
+check_prereq btrfs-convert
+
+fail=0
+default_mke2fs="mke2fs -t ext4 -b 4096"
+convert_test_preamble '' 'common inode flags test' 16k "$default_mke2fs"
+convert_test_prep_fs $default_mke2fs
+
+# create file with specific flags
+run_check $SUDO_HELPER touch $TEST_MNT/flag_test
+run_check $SUDO_HELPER chattr +aAdSi $TEST_MNT/flag_test
+
+run_check_umount_test_dev
+convert_test_do_convert
+run_check_mount_test_dev
+
+# Log the status
+run_check lsattr $TEST_MNT/flag_test
+# Above flags should be copied to btrfs flags, and lsattr should get them
+run_check_stdout lsattr $TEST_MNT/flag_test | cut -f1 -d\ | grep "[aAdiS]" -q
+if [ $? -ne 0 ]; then
+ rm tmp_output
+ _fail "no common inode flags are copied after convert"
+fi
+
+run_check_umount_test_dev
+convert_test_post_rollback
diff --git a/tests/fsck-tests.sh b/tests/fsck-tests.sh
index d1cd7329..44cca1b8 100755
--- a/tests/fsck-tests.sh
+++ b/tests/fsck-tests.sh
@@ -3,13 +3,13 @@
# loop through all of our bad images and make sure fsck repairs them properly
LANG=C
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+TOP=$(readlink -f "$SCRIPT_DIR/../")
TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/fsck-tests-results.txt"
IMAGE="$TOP/tests/test.img"
-source $TOP/tests/common
+source "$TOP/tests/common"
export TOP
export RESULTS
@@ -17,31 +17,35 @@ export LANG
export IMAGE
export TEST_DEV
-rm -f $RESULTS
+rm -f "$RESULTS"
# test rely on corrupting blocks tool
check_prereq btrfs-corrupt-block
check_prereq btrfs-image
check_prereq btrfs
+check_kernel_support
run_one_test() {
local testname
testname="$1"
echo " [TEST/fsck] $(basename $testname)"
- cd $testname
- echo "=== Entering $testname" >> $RESULTS
+ cd "$testname"
+ echo "=== Entering $testname" >> "$RESULTS"
if [ -x test.sh ]; then
# Type 2
./test.sh
if [ $? -ne 0 ]; then
+ if [[ $TEST_LOG =~ dump ]]; then
+ cat "$RESULTS"
+ fi
_fail "test failed for case $(basename $testname)"
fi
else
# Type 1
- check_all_images `pwd`
+ check_all_images
fi
- cd $TOP
+ cd "$TOP"
}
# Each dir contains one type of error for btrfsck test.
@@ -57,7 +61,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 "$TOP/tests/fsck-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
run_one_test "$i"
diff --git a/tests/fsck-tests/013-extent-tree-rebuild/test.sh b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
index ff7d28e5..37bdcd9c 100755
--- a/tests/fsck-tests/013-extent-tree-rebuild/test.sh
+++ b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
@@ -3,7 +3,6 @@
source $TOP/tests/common
check_prereq btrfs-corrupt-block
-check_prereq btrfs-debug-tree
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -16,7 +15,7 @@ test_extent_tree_rebuild()
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $TEST_DEV
run_check_mount_test_dev
- run_check $SUDO_HELPER cp -aR /lib/modules/`uname -r`/ $TEST_MNT
+ generate_dataset small
for i in `seq 1 100`;do
run_check $SUDO_HELPER $TOP/btrfs sub snapshot $TEST_MNT \
@@ -25,7 +24,7 @@ test_extent_tree_rebuild()
run_check_umount_test_dev
# get extent root bytenr
- extent_root_bytenr=`$SUDO_HELPER $TOP/btrfs-debug-tree -r $TEST_DEV | \
+ extent_root_bytenr=`$SUDO_HELPER $TOP/btrfs inspect-internal dump-tree -r $TEST_DEV | \
grep extent | awk '{print $7}'`
if [ -z $extent_root_bytenr ];then
_fail "fail to get extent root bytenr"
diff --git a/tests/fsck-tests/022-qgroup-rescan-halfway/qgroup_rescan_halfway.raw.xz b/tests/fsck-tests/022-qgroup-rescan-halfway/qgroup_rescan_halfway.raw.xz
new file mode 100644
index 00000000..8cb620f7
--- /dev/null
+++ b/tests/fsck-tests/022-qgroup-rescan-halfway/qgroup_rescan_halfway.raw.xz
Binary files differ
diff --git a/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh b/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh
new file mode 100755
index 00000000..1dc8f8fc
--- /dev/null
+++ b/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+# check whether btrfsck can detect running qgroup rescan
+
+source $TOP/tests/common
+
+check_prereq btrfs
+
+check_image() {
+ local image
+
+ image=$1
+ run_check_stdout $TOP/btrfs check "$image" 2>&1 | \
+ grep -q "Counts for qgroup id"
+ if [ $? -eq 0 ]; then
+ _fail "Btrfs check doesn't detect rescan correctly"
+ fi
+}
+
+check_all_images
diff --git a/tests/fsck-tests/023-qgroup-stack-overflow/quota_balance_loop_backref.raw.xz b/tests/fsck-tests/023-qgroup-stack-overflow/quota_balance_loop_backref.raw.xz
new file mode 100644
index 00000000..a0759739
--- /dev/null
+++ b/tests/fsck-tests/023-qgroup-stack-overflow/quota_balance_loop_backref.raw.xz
Binary files differ
diff --git a/tests/fsck-tests/023-qgroup-stack-overflow/test.sh b/tests/fsck-tests/023-qgroup-stack-overflow/test.sh
new file mode 100755
index 00000000..a304eac5
--- /dev/null
+++ b/tests/fsck-tests/023-qgroup-stack-overflow/test.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Check whether btrfs check quota verify will cause stack overflow.
+# This is caused by lack of handling of tree reloc tree.
+# Fixed by patch:
+# btrfs-progs: Fix stack overflow for checking qgroup on tree reloc tree
+
+source $TOP/tests/common
+
+check_prereq btrfs
+
+check_image()
+{
+ run_check $TOP/btrfs check "$1"
+}
+
+check_all_images
diff --git a/tests/fsck-tests/024-clear-space-cache/test.sh b/tests/fsck-tests/024-clear-space-cache/test.sh
new file mode 100755
index 00000000..2945ae87
--- /dev/null
+++ b/tests/fsck-tests/024-clear-space-cache/test.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+# confirm that clearing space cache works
+
+source $TOP/tests/common
+
+check_prereq btrfs
+check_prereq mkfs.btrfs
+
+setup_root_helper
+prepare_test_dev 1G
+
+run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $TEST_DEV
+run_check_mount_test_dev
+
+# Create files that takes at least 3 data chunks, while
+# can still be removed to create free space inside one chunk.
+
+for i in $(seq 0 6); do
+ run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/file_${i} bs=1M \
+ count=64 > /dev/null 2>&1
+done
+sync
+
+# Remove file 1 3 5 to create holes
+for i in 1 3 5; do
+ run_check $SUDO_HELPER rm $TEST_MNT/file_${i}
+done
+
+sync
+
+run_check_umount_test_dev
+
+# Clear space cache and re-check fs
+run_check $TOP/btrfs check --clear-space-cache v1 $TEST_DEV
+run_check $TOP/btrfs check $TEST_DEV
+
+# Manually recheck space cache and super space cache generation
+run_check_stdout $TOP/btrfs inspect-internal dump-tree -t root $TEST_DEV | \
+ grep -q FREE_SPACE
+if [ $? -eq 0 ]; then
+ _fail "clear space cache doesn't clear all space cache"
+fi
+
+run_check_stdout $TOP/btrfs inspect-internal dump-super $TEST_DEV |
+ grep -q 'cache_generation.*18446744073709551615'
+if [ $? -ne 0 ]; then
+ _fail "clear space cache doesn't set cache_generation correctly"
+fi
diff --git a/tests/fuzz-tests.sh b/tests/fuzz-tests.sh
index 29691cae..f72385e5 100755
--- a/tests/fuzz-tests.sh
+++ b/tests/fuzz-tests.sh
@@ -3,13 +3,13 @@
# misc tests on fuzzed or crafted images
LANG=C
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+TOP=$(readlink -f "$SCRIPT_DIR/../")
TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/fuzz-tests-results.txt"
IMAGE="$TOP/tests/test.img"
-source $TOP/tests/common
+source "$TOP/tests/common"
export TOP
export RESULTS
@@ -17,24 +17,27 @@ export LANG
export IMAGE
export TEST_DEV
-rm -f $RESULTS
+rm -f "$RESULTS"
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 "$TOP/tests/fuzz-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
- name=$(basename $i)
+ name=$(basename "$i")
cd $i
if [ -x test.sh ]; then
- echo "=== Entering $i" >> $RESULTS
+ echo "=== Entering $i" >> "$RESULTS"
echo " [TEST/fuzz] $name"
./test.sh
if [ $? -ne 0 ]; then
+ if [[ $TEST_LOG =~ dump ]]; then
+ cat "$RESULTS"
+ fi
_fail "test failed for case $(basename $i)"
fi
fi
- cd $TOP
+ cd "$TOP"
done
diff --git a/tests/fuzz-tests/004-simple-dump-tree/test.sh b/tests/fuzz-tests/004-simple-dump-tree/test.sh
new file mode 100755
index 00000000..89ff214c
--- /dev/null
+++ b/tests/fuzz-tests/004-simple-dump-tree/test.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+source $TOP/tests/common
+
+setup_root_helper
+check_prereq btrfs
+
+# redefine the one provided by common
+check_image() {
+ local image
+
+ image=$1
+ run_mayfail $TOP/btrfs inspect-internal dump-tree "$image"
+}
+
+check_all_images $TOP/tests/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
new file mode 100755
index 00000000..fbce3d9f
--- /dev/null
+++ b/tests/fuzz-tests/005-simple-dump-super/test.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+source $TOP/tests/common
+
+setup_root_helper
+check_prereq btrfs
+
+# redefine the one provided by common
+check_image() {
+ local image
+
+ image=$1
+ run_mayfail $TOP/btrfs inspect-internal dump-super "$image"
+ run_mayfail $TOP/btrfs inspect-internal dump-super -Ffa "$image"
+}
+
+check_all_images $TOP/tests/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
new file mode 100755
index 00000000..c3410b06
--- /dev/null
+++ b/tests/fuzz-tests/006-simple-tree-stats/test.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+source $TOP/tests/common
+
+setup_root_helper
+check_prereq btrfs
+
+# redefine the one provided by common
+check_image() {
+ local image
+
+ image=$1
+ run_mayfail $TOP/btrfs inspect-internal tree-stats "$image"
+}
+
+check_all_images $TOP/tests/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
new file mode 100755
index 00000000..885cb352
--- /dev/null
+++ b/tests/fuzz-tests/007-simple-super-recover/test.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+source $TOP/tests/common
+
+setup_root_helper
+check_prereq btrfs
+
+# redefine the one provided by common
+check_image() {
+ local image
+
+ image=$1
+ run_check cp "$image" "$image".scratch
+ run_mayfail $TOP/btrfs rescue super-recover -y -v "$image".scratch
+ rm -- "$image".scratch
+}
+
+check_all_images $TOP/tests/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
new file mode 100755
index 00000000..d53453f6
--- /dev/null
+++ b/tests/fuzz-tests/008-simple-chunk-recover/test.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+source $TOP/tests/common
+
+setup_root_helper
+check_prereq btrfs
+
+# redefine the one provided by common
+check_image() {
+ local image
+
+ image=$1
+ run_check cp "$image" "$image".scratch
+ run_mayfail $TOP/btrfs rescue chunk-recover -y -v "$image".scratch
+ rm -- "$image".scratch
+}
+
+check_all_images $TOP/tests/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
new file mode 100755
index 00000000..393db3f6
--- /dev/null
+++ b/tests/fuzz-tests/009-simple-zero-log/test.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+source $TOP/tests/common
+
+setup_root_helper
+check_prereq btrfs
+
+# redefine the one provided by common
+check_image() {
+ local image
+
+ image=$1
+ run_check cp "$image" "$image".scratch
+ run_mayfail $TOP/btrfs rescue zero-log "$image".scratch
+ rm -- "$image".scratch
+}
+
+check_all_images $TOP/tests/fuzz-tests/images
+
+exit 0
diff --git a/tests/fuzz-tests/images/bko-156731.raw.txt b/tests/fuzz-tests/images/bko-156731.raw.txt
new file mode 100644
index 00000000..aea35f1f
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-156731.raw.txt
@@ -0,0 +1,83 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=156731
+Lukas Lueg 2016-09-13 19:53:59 UTC
+
+More news from the fuzzer. The attached image causes btrfsck to
+buffer-overflow. Using btrfs-progs v4.7-42-g56e9586, compiled with ASAN
+(doesn't crash without)
+
+==17647==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000017980 at pc 0x00000052dde3 bp 0x7ffecc974fe0 sp 0x7ffecc974fd8
+READ of size 4 at 0x621000017980 thread T0
+ #0 0x52dde2 in btrfs_extent_data_ref_count /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:1582:1
+ #1 0x5329ae in run_next_block /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:6380:6
+ #2 0x52f584 in deal_root_from_list /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:8391:10
+ #3 0x520f81 in check_chunks_and_extents /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:8558:8
+ #4 0x51e5a9 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11493:9
+ #5 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #6 0x7faced2c8730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #7 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+cat crashing_images/id:000047,sig:11,src:000343+000051,op:splice,rep:4.log
+=================================================================
+==17647==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000017980 at pc 0x00000052dde3 bp 0x7ffecc974fe0 sp 0x7ffecc974fd8
+READ of size 4 at 0x621000017980 thread T0
+ #0 0x52dde2 in btrfs_extent_data_ref_count /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:1582:1
+ #1 0x5329ae in run_next_block /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:6380:6
+ #2 0x52f584 in deal_root_from_list /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:8391:10
+ #3 0x520f81 in check_chunks_and_extents /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:8558:8
+ #4 0x51e5a9 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11493:9
+ #5 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #6 0x7faced2c8730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #7 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+0x621000017980 is located 0 bytes to the right of 4224-byte region [0x621000016900,0x621000017980)
+allocated by thread T0 here:
+ #0 0x4bfca0 in calloc (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4bfca0)
+ #1 0x5c16ca in __alloc_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:542:7
+ #2 0x5c1b26 in alloc_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:646:8
+ #3 0x58de0c in btrfs_find_create_tree_block /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:193:9
+ #4 0x58e880 in read_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:339:7
+ #5 0x5918a2 in read_tree_block /home/lukas/dev/btrfsfuzz/src-asan/./disk-io.h:112:9
+ #6 0x591712 in find_and_setup_root /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:647:15
+ #7 0x593243 in setup_root_or_create_block /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:966:8
+ #8 0x592850 in btrfs_setup_all_roots /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:1031:8
+ #9 0x5948fe in __open_ctree_fd /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:1341:8
+ #10 0x5942b5 in open_ctree_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:1387:9
+ #11 0x51dff2 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11382:9
+ #12 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #13 0x7faced2c8730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+
+SUMMARY: AddressSanitizer: heap-buffer-overflow /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:1582:1 in btrfs_extent_data_ref_count
+Shadow bytes around the buggy address:
+ 0x0c427fffaee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x0c427fffaef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x0c427fffaf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x0c427fffaf10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x0c427fffaf20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+=>0x0c427fffaf30:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+Shadow byte legend (one shadow byte represents 8 application bytes):
+ Addressable: 00
+ Partially addressable: 01 02 03 04 05 06 07
+ Heap left redzone: fa
+ Heap right redzone: fb
+ Freed heap region: fd
+ Stack left redzone: f1
+ Stack mid redzone: f2
+ Stack right redzone: f3
+ Stack partial redzone: f4
+ Stack after return: f5
+ Stack use after scope: f8
+ Global redzone: f9
+ Global init order: f6
+ Poisoned by user: f7
+ Container overflow: fc
+ Array cookie: ac
+ Intra object redzone: bb
+ ASan internal: fe
+ Left alloca redzone: ca
+ Right alloca redzone: cb
+==17647==ABORTING
diff --git a/tests/fuzz-tests/images/bko-156731.raw.xz b/tests/fuzz-tests/images/bko-156731.raw.xz
new file mode 100644
index 00000000..74a5c2a9
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-156731.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-156741.raw.txt b/tests/fuzz-tests/images/bko-156741.raw.txt
new file mode 100644
index 00000000..ca52677a
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-156741.raw.txt
@@ -0,0 +1,131 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=156741
+Lukas Lueg 2016-09-13 19:56:16 UTC
+
+More news from the fuzzer. The attached image causes btrfsck to
+buffer-overflow. Using btrfs-progs v4.7-42-g56e9586, compiled with ASAN
+(doesn't crash without).
+
+==23161==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000017980 at pc 0x0000005299d3 bp 0x7fff110ce980 sp 0x7fff110ce978
+READ of size 1 at 0x621000017980 thread T0
+ #0 0x5299d2 in btrfs_extent_inline_ref_type /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:1588:1
+ #1 0x540f54 in build_roots_info_cache /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:10965:10
+ #2 0x52163e in repair_root_items /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11108:8
+ #3 0x51e5c3 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11497:8
+ #4 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #5 0x7f067cc9f730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #6 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+cat crashing_images/id:000073,sig:11,src:000504+000275,op:splice,rep:4.log
+parent transid verify failed on 1122304 wanted 3472328296227680304 found 1
+parent transid verify failed on 1122304 wanted 3472328296227680304 found 1
+Ignoring transid failure
+Chunk[256, 228, 0]: length(4194304), offset(0), type(2) is not found in block group
+Chunk[256, 228, 4194304]: length(1638400), offset(4194304), type(5) is not found in block group
+Chunk[256, 228, 5832704]: length(1638400), offset(5832704), type(5) is not found in block group
+ref mismatch on [131072 4096] extent item 0, found 1
+Backref 131072 parent 3 root 3 not found in extent tree
+backpointer mismatch on [131072 4096]
+ref mismatch on [1118208 4096] extent item 1, found 0
+Backref 1118208 root 1 not referenced back 0x60300000ee00
+Incorrect global backref count on 1118208 found 1 wanted 0
+backpointer mismatch on [1118208 4096]
+owner ref check failed [1118208 4096]
+ref mismatch on [1126400 4096] extent item 1, found 0
+Backref 1126400 root 3 not referenced back 0x60300000edd0
+Incorrect global backref count on 1126400 found 1 wanted 0
+backpointer mismatch on [1126400 4096]
+owner ref check failed [1126400 4096]
+ref mismatch on [1130496 4096] extent item 1, found 0
+Backref 1130496 root 4 not referenced back 0x60300000eda0
+Incorrect global backref count on 1130496 found 1 wanted 0
+backpointer mismatch on [1130496 4096]
+owner ref check failed [1130496 4096]
+ref mismatch on [1134592 4096] extent item 1, found 0
+Backref 1134592 root 5 not referenced back 0x60300000ed70
+Incorrect global backref count on 1134592 found 1 wanted 0
+backpointer mismatch on [1134592 4096]
+owner ref check failed [1134592 4096]
+ref mismatch on [1138688 4096] extent item 1, found 0
+Backref 1138688 root 7 not referenced back 0x60300000ed40
+Incorrect global backref count on 1138688 found 1 wanted 0
+backpointer mismatch on [1138688 4096]
+owner ref check failed [1138688 4096]
+ref mismatch on [4194304 4096] extent item 0, found 1
+Backref 4194304 parent 5 root 5 not found in extent tree
+backpointer mismatch on [4194304 4096]
+ref mismatch on [4198400 4096] extent item 0, found 1
+Backref 4198400 parent 1 root 1 not found in extent tree
+backpointer mismatch on [4198400 4096]
+ref mismatch on [4227072 4096] extent item 0, found 1
+Backref 4227072 parent 4 root 4 not found in extent tree
+backpointer mismatch on [4227072 4096]
+ref mismatch on [4231168 4096] extent item 0, found 1
+Backref 4231168 parent 7 root 7 not found in extent tree
+backpointer mismatch on [4231168 4096]
+ref mismatch on [3472328296227680304 3472328296227680304] extent item 0, found 1
+Backref 3472328296227680304 root 1 owner 6 offset 0 num_refs 0 not found in extent tree
+Incorrect local backref count on 3472328296227680304 root 1 owner 6 offset 0 found 1 wanted 0 back 0x60700000dca0
+backpointer mismatch on [3472328296227680304 3472328296227680304]
+=================================================================
+==23161==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000017980 at pc 0x0000005299d3 bp 0x7fff110ce980 sp 0x7fff110ce978
+READ of size 1 at 0x621000017980 thread T0
+ #0 0x5299d2 in btrfs_extent_inline_ref_type /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:1588:1
+ #1 0x540f54 in build_roots_info_cache /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:10965:10
+ #2 0x52163e in repair_root_items /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11108:8
+ #3 0x51e5c3 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11497:8
+ #4 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #5 0x7f067cc9f730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #6 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+0x621000017980 is located 0 bytes to the right of 4224-byte region [0x621000016900,0x621000017980)
+allocated by thread T0 here:
+ #0 0x4bfca0 in calloc (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4bfca0)
+ #1 0x5c16ca in __alloc_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:542:7
+ #2 0x5c1b26 in alloc_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:646:8
+ #3 0x58de0c in btrfs_find_create_tree_block /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:193:9
+ #4 0x58e880 in read_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:339:7
+ #5 0x5918a2 in read_tree_block /home/lukas/dev/btrfsfuzz/src-asan/./disk-io.h:112:9
+ #6 0x591712 in find_and_setup_root /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:647:15
+ #7 0x593243 in setup_root_or_create_block /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:966:8
+ #8 0x592850 in btrfs_setup_all_roots /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:1031:8
+ #9 0x5948fe in __open_ctree_fd /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:1341:8
+ #10 0x5942b5 in open_ctree_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:1387:9
+ #11 0x51dff2 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11382:9
+ #12 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #13 0x7f067cc9f730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+
+SUMMARY: AddressSanitizer: heap-buffer-overflow /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:1588:1 in btrfs_extent_inline_ref_type
+Shadow bytes around the buggy address:
+ 0x0c427fffaee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x0c427fffaef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x0c427fffaf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x0c427fffaf10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x0c427fffaf20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+=>0x0c427fffaf30:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffaf80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+Shadow byte legend (one shadow byte represents 8 application bytes):
+ Addressable: 00
+ Partially addressable: 01 02 03 04 05 06 07
+ Heap left redzone: fa
+ Heap right redzone: fb
+ Freed heap region: fd
+ Stack left redzone: f1
+ Stack mid redzone: f2
+ Stack right redzone: f3
+ Stack partial redzone: f4
+ Stack after return: f5
+ Stack use after scope: f8
+ Global redzone: f9
+ Global init order: f6
+ Poisoned by user: f7
+ Container overflow: fc
+ Array cookie: ac
+ Intra object redzone: bb
+ ASan internal: fe
+ Left alloca redzone: ca
+ Right alloca redzone: cb
+==23161==ABORTING
diff --git a/tests/fuzz-tests/images/bko-156741.raw.xz b/tests/fuzz-tests/images/bko-156741.raw.xz
new file mode 100644
index 00000000..af4de268
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-156741.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-156811-bad-parent-ref-qgroup-verify.raw.txt b/tests/fuzz-tests/images/bko-156811-bad-parent-ref-qgroup-verify.raw.txt
new file mode 100644
index 00000000..6e4a5418
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-156811-bad-parent-ref-qgroup-verify.raw.txt
@@ -0,0 +1,94 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=156811
+Lukas Lueg 2016-09-14 19:19:46 UTC
+
+More news from the fuzzer. The attached image causes btrfsck to engage in
+undefined behavior; using btrfs-progs v4.7-42-g56e9586. You need to compile
+with UBSAN in order to reproduce.
+
+The juicy parts:
+
+qgroup-verify.c:333:15: runtime error: member access within null pointer of type 'struct ref'
+ #0 0x88684f in find_parent_roots /home/lukas/dev/btrfsfuzz/src-ubsan/qgroup-verify.c:333:15
+ #1 0x877a71 in account_all_refs /home/lukas/dev/btrfsfuzz/src-ubsan/qgroup-verify.c:525:11
+ #2 0x87513b in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-ubsan/qgroup-verify.c:1372:8
+ #3 0x536d3a in cmd_check /home/lukas/dev/btrfsfuzz/src-ubsan/cmds-check.c:11637:9
+ #4 0x490560 in main /home/lukas/dev/btrfsfuzz/src-ubsan/btrfs.c:243:8
+ #5 0x7f35b46ab730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #6 0x422188 in _start (/home/lukas/dev/btrfsfuzz/bin-ubsan/bin/btrfs+0x422188)
+
+
+We don't strictly need UBSAN as the error can be spotted by naked eye in
+find_parent_root(): The line "node = &ref->bytenr_node" gets a reference to a
+member of a NULL pointer before the pointer is checked against being NULL on
+the next line. It should be the other way around...
+
+crc32c.c:75:19: runtime error: load of misaligned address 0x74200001cc9c for type 'unsigned long', which requires 8 byte alignment
+0x74200001cc9c: note: pointer points here
+ 00 00 00 00 b7 0e 65 6c 64 61 40 4b a5 0d 0f ba 33 0c 75 27 00 00 02 00 00 00 00 00 01 00 00 00
+ ^
+ #0 0x907c52 in crc32c_intel /home/lukas/dev/btrfsfuzz/src-ubsan/crc32c.c:75:19
+ #1 0x6f9845 in __csum_tree_block_size /home/lukas/dev/btrfsfuzz/src-ubsan/disk-io.c:139:8
+ #2 0x6f96b8 in csum_tree_block_size /home/lukas/dev/btrfsfuzz/src-ubsan/disk-io.c:159:9
+ #3 0x6fda28 in read_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-ubsan/disk-io.c:348:19
+ #4 0x71669f in btrfs_setup_chunk_tree_and_device_map /home/lukas/dev/btrfsfuzz/src-ubsan/disk-io.c:1210:30
+ #5 0x7187e4 in __open_ctree_fd /home/lukas/dev/btrfsfuzz/src-ubsan/disk-io.c:1322:8
+ #6 0x717a6d in open_ctree_fs_info /home/lukas/dev/btrfsfuzz/src-ubsan/disk-io.c:1381:9
+ #7 0x533791 in cmd_check /home/lukas/dev/btrfsfuzz/src-ubsan/cmds-check.c:11449:9
+ #8 0x490560 in main /home/lukas/dev/btrfsfuzz/src-ubsan/btrfs.c:243:8
+ #9 0x7f35b46ab730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #10 0x422188 in _start (/home/lukas/dev/btrfsfuzz/bin-ubsan/bin/btrfs+0x422188)
+
+SUMMARY: MemorySanitizer: undefined-behavior crc32c.c:75:19 in
+checking extents
+Chunk[256, 228, 0]: length(4194304), offset(0), type(2) is not found in block group
+Chunk[256, 228, 0] stripe[1, 0] is not found in dev extent
+Chunk[256, 228, 4194304]: length(1638400), offset(4194304), type(5) is not found in block group
+Chunk[256, 228, 4194304] stripe[1, 4194304] is not found in dev extent
+Chunk[256, 228, 5832704]: length(1638400), offset(5832704), type(5) is not found in block group
+Chunk[256, 228, 5832704] stripe[1, 5832704] is not found in dev extent
+ref mismatch on [131072 4096] extent item 0, found 1
+Backref 131072 parent 3 root 3 not found in extent tree
+backpointer mismatch on [131072 4096]
+ref mismatch on [4194304 4096] extent item 0, found 1
+Backref 4194304 parent 5 root 5 not found in extent tree
+backpointer mismatch on [4194304 4096]
+ref mismatch on [4198400 4096] extent item 0, found 1
+Backref 4198400 parent 1 root 1 not found in extent tree
+backpointer mismatch on [4198400 4096]
+ref mismatch on [4231168 4096] extent item 0, found 1
+Backref 4231168 parent 7 root 7 not found in extent tree
+backpointer mismatch on [4231168 4096]
+ref mismatch on [3472328296227680304 3472328296227680304] extent item 0, found 1
+Backref 3472328296227680304 root 1 owner 2 offset 0 num_refs 0 not found in extent tree
+Incorrect local backref count on 3472328296227680304 root 1 owner 2 offset 0 found 1 wanted 0 back 0x70c00000ed00
+backpointer mismatch on [3472328296227680304 3472328296227680304]
+Dev extent's total-byte(0) is not equal to byte-used(7471104) in dev[1, 216, 1]
+Errors found in extent allocation tree or chunk allocation
+checking free space cache
+checking fs roots
+checking csums
+checking root refs
+checking quota groups
+qgroup-verify.c:333:15: runtime error: member access within null pointer of type 'struct ref'
+ #0 0x88684f in find_parent_roots /home/lukas/dev/btrfsfuzz/src-ubsan/qgroup-verify.c:333:15
+ #1 0x877a71 in account_all_refs /home/lukas/dev/btrfsfuzz/src-ubsan/qgroup-verify.c:525:11
+ #2 0x87513b in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-ubsan/qgroup-verify.c:1372:8
+ #3 0x536d3a in cmd_check /home/lukas/dev/btrfsfuzz/src-ubsan/cmds-check.c:11637:9
+ #4 0x490560 in main /home/lukas/dev/btrfsfuzz/src-ubsan/btrfs.c:243:8
+ #5 0x7f35b46ab730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #6 0x422188 in _start (/home/lukas/dev/btrfsfuzz/bin-ubsan/bin/btrfs+0x422188)
+
+SUMMARY: MemorySanitizer: undefined-behavior qgroup-verify.c:333:15 in
+qgroup-verify.c:334: find_parent_roots: Assertion `ref == NULL` failed.
+btrfs check(backtrace+0x51)[0x43f6d1]
+btrfs check[0x883611]
+btrfs check[0x880ce9]
+btrfs check[0x8868b1]
+btrfs check[0x877a72]
+btrfs check(qgroup_verify_all+0x26c)[0x87513c]
+btrfs check(cmd_check+0x457b)[0x536d3b]
+btrfs check(main+0x6a1)[0x490561]
+/lib64/libc.so.6(__libc_start_main+0xf1)[0x7f35b46ab731]
+btrfs check(_start+0x29)[0x422189]
+Checking filesystem on ubsan_logs/id:002289,src:001702+002037,op:splice,rep:4.img
+UUID: b70e656c-6461-404b-a50d-0fba330c7527
diff --git a/tests/fuzz-tests/images/bko-156811-bad-parent-ref-qgroup-verify.raw.xz b/tests/fuzz-tests/images/bko-156811-bad-parent-ref-qgroup-verify.raw.xz
new file mode 100644
index 00000000..7e499f77
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-156811-bad-parent-ref-qgroup-verify.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-161811.raw.txt b/tests/fuzz-tests/images/bko-161811.raw.txt
new file mode 100644
index 00000000..93374e98
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-161811.raw.txt
@@ -0,0 +1,81 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=161811
+Lukas Lueg 2016-09-16 20:03:35 UTC
+
+More news from the fuzzer. The attached image causes a global-buffer-overflow
+in btrfsck; using btrfs-progs v4.7-42-g56e9586. You need to compile with ASAN
+in order to reproduce.
+
+The juicy parts:
+
+==16657==ERROR: AddressSanitizer: global-buffer-overflow on address 0x00000064726f at pc 0x00000054eadd bp 0x7ffec6d9b980 sp 0x7ffec6d9b978
+READ of size 1 at 0x00000064726f thread T0
+ #0 0x54eadc in imode_to_type /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:635:9
+ #1 0x54673a in maybe_free_inode_rec /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:932:13
+ #2 0x54a79a in add_inode_backref /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:1104:2
+ #3 0x54b6d2 in process_inode_ref /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:1549:3
+ #4 0x5489e4 in process_one_leaf /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:1810:10
+ #5 0x54522e in walk_down_tree /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:1958:10
+ #6 0x54372e in check_fs_root /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:3668:10
+ #7 0x5224ef in check_fs_roots /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:3809:10
+ #8 0x51e772 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11533:8
+ #9 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #10 0x7f4a5c29f730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #11 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+bad full backref, on [4198400]
+checking free space cache
+checking fs roots
+=================================================================
+==16657==ERROR: AddressSanitizer: global-buffer-overflow on address 0x00000064726f at pc 0x00000054eadd bp 0x7ffec6d9b980 sp 0x7ffec6d9b978
+READ of size 1 at 0x00000064726f thread T0
+ #0 0x54eadc in imode_to_type /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:635:9
+ #1 0x54673a in maybe_free_inode_rec /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:932:13
+ #2 0x54a79a in add_inode_backref /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:1104:2
+ #3 0x54b6d2 in process_inode_ref /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:1549:3
+ #4 0x5489e4 in process_one_leaf /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:1810:10
+ #5 0x54522e in walk_down_tree /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:1958:10
+ #6 0x54372e in check_fs_root /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:3668:10
+ #7 0x5224ef in check_fs_roots /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:3809:10
+ #8 0x51e772 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11533:8
+ #9 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #10 0x7f4a5c29f730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #11 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+0x00000064726f is located 49 bytes to the left of global variable '<string literal>' defined in 'cmds-check.c:3051:2' (0x6472a0) of size 17
+ '<string literal>' is ascii string 'check_inode_recs'
+0x00000064726f is located 0 bytes to the right of global variable 'btrfs_type_by_mode' defined in 'cmds-check.c:625:23' (0x647260) of size 15
+SUMMARY: AddressSanitizer: global-buffer-overflow /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:635:9 in imode_to_type
+Shadow bytes around the buggy address:
+ 0x0000800c0df0: f9 f9 f9 f9 00 00 05 f9 f9 f9 f9 f9 00 00 02 f9
+ 0x0000800c0e00: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 00 07 f9 f9
+ 0x0000800c0e10: f9 f9 f9 f9 00 00 00 00 f9 f9 f9 f9 00 00 00 01
+ 0x0000800c0e20: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 00 00 01 f9
+ 0x0000800c0e30: f9 f9 f9 f9 00 07 f9 f9 f9 f9 f9 f9 00 00 05 f9
+=>0x0000800c0e40: f9 f9 f9 f9 00 00 02 f9 f9 f9 f9 f9 00[07]f9 f9
+ 0x0000800c0e50: f9 f9 f9 f9 00 00 01 f9 f9 f9 f9 f9 00 00 00 07
+ 0x0000800c0e60: f9 f9 f9 f9 00 00 00 00 00 04 f9 f9 f9 f9 f9 f9
+ 0x0000800c0e70: 00 00 00 00 03 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
+ 0x0000800c0e80: 00 00 00 00 00 05 f9 f9 f9 f9 f9 f9 00 00 00 00
+ 0x0000800c0e90: 00 00 03 f9 f9 f9 f9 f9 00 00 06 f9 f9 f9 f9 f9
+Shadow byte legend (one shadow byte represents 8 application bytes):
+ Addressable: 00
+ Partially addressable: 01 02 03 04 05 06 07
+ Heap left redzone: fa
+ Heap right redzone: fb
+ Freed heap region: fd
+ Stack left redzone: f1
+ Stack mid redzone: f2
+ Stack right redzone: f3
+ Stack partial redzone: f4
+ Stack after return: f5
+ Stack use after scope: f8
+ Global redzone: f9
+ Global init order: f6
+ Poisoned by user: f7
+ Container overflow: fc
+ Array cookie: ac
+ Intra object redzone: bb
+ ASan internal: fe
+ Left alloca redzone: ca
+ Right alloca redzone: cb
+==16657==ABORTING
diff --git a/tests/fuzz-tests/images/bko-161811.raw.xz b/tests/fuzz-tests/images/bko-161811.raw.xz
new file mode 100644
index 00000000..8ac31951
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-161811.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-161821.raw.txt b/tests/fuzz-tests/images/bko-161821.raw.txt
new file mode 100644
index 00000000..c06b0ea7
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-161821.raw.txt
@@ -0,0 +1,42 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=161821
+Lukas Lueg 2016-09-16 20:45:58 UTC
+
+More news from the fuzzer. The attached image causes a segmentation fault when
+running btrfsck over it; using btrfs-progs v4.7.2-55-g2b7c507
+
+The juicy parts:
+
+==29097==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000070 (pc 0x000000581939 bp 0x7fff1f168590 sp 0x7fff1f168590 T0)
+ #0 0x581938 in extent_buffer_get /home/lukas/dev/btrfsfuzz/src-asan/./extent_io.h:105:10
+ #1 0x583daf in btrfs_search_slot /home/lukas/dev/btrfsfuzz/src-asan/ctree.c:1118:2
+ #2 0x538652 in check_owner_ref /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:4043:8
+ #3 0x535ca5 in check_block /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:4433:10
+ #4 0x532464 in run_next_block /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:6292:8
+ #5 0x52f584 in deal_root_from_list /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:8391:10
+ #6 0x520f81 in check_chunks_and_extents /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:8558:8
+ #7 0x51e5a9 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11493:9
+ #8 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #9 0x7f42d367b730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #10 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+parent transid verify failed on 4198400 wanted 14 found 1114126
+parent transid verify failed on 4198400 wanted 14 found 1114126
+Ignoring transid failure
+ASAN:DEADLYSIGNAL
+=================================================================
+==29097==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000070 (pc 0x000000581939 bp 0x7fff1f168590 sp 0x7fff1f168590 T0)
+ #0 0x581938 in extent_buffer_get /home/lukas/dev/btrfsfuzz/src-asan/./extent_io.h:105:10
+ #1 0x583daf in btrfs_search_slot /home/lukas/dev/btrfsfuzz/src-asan/ctree.c:1118:2
+ #2 0x538652 in check_owner_ref /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:4043:8
+ #3 0x535ca5 in check_block /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:4433:10
+ #4 0x532464 in run_next_block /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:6292:8
+ #5 0x52f584 in deal_root_from_list /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:8391:10
+ #6 0x520f81 in check_chunks_and_extents /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:8558:8
+ #7 0x51e5a9 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11493:9
+ #8 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #9 0x7f42d367b730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #10 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+AddressSanitizer can not provide additional info.
+SUMMARY: AddressSanitizer: SEGV /home/lukas/dev/btrfsfuzz/src-asan/./extent_io.h:105:10 in extent_buffer_get
+==29097==ABORTING
diff --git a/tests/fuzz-tests/images/bko-161821.raw.xz b/tests/fuzz-tests/images/bko-161821.raw.xz
new file mode 100644
index 00000000..6c673ea4
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-161821.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-166361-blocksize-zero.raw.txt b/tests/fuzz-tests/images/bko-166361-blocksize-zero.raw.txt
new file mode 100644
index 00000000..bca52969
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-166361-blocksize-zero.raw.txt
@@ -0,0 +1,93 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=166361
+Lukas Lueg 2016-09-17 16:45:53 UTC
+
+More news from the fuzzer. The attached image causes a segmentation fault when
+running btrfsck over it; using btrfs-progs v4.7.1-17-g2076992
+
+The juicy parts:
+
+==30530==ERROR: AddressSanitizer: SEGV on unknown address 0x62103031ae21 (pc 0x0000005f19b9 bp 0x7ffd4aa30a90 sp 0x7ffd4aa30a90 T0)
+ #0 0x5f19b8 in btrfs_qgroup_status_flags /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:2136:1
+ #1 0x5f16f5 in read_qgroup_status /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:885:10
+ #2 0x5ef38a in load_quota_info /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:949:5
+ #3 0x5eeefc in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:1351:8
+ #4 0x51f08f in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11637:9
+ #5 0x4f0f81 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #6 0x7f7eb11aa730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #7 0x4213f8 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4213f8)
+
+parent transid verify failed on 4198400 wanted 4 found 5
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 4198400 wanted 26388280060160 found 5
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+checking extents
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+Chunk[256, 228, 0]: length(4194304), offset(0), type(2) is not found in block group
+Chunk[256, 228, 0] stripe[1, 0] is not found in dev extent
+Chunk[256, 228, 4194304]: length(1638400), offset(4194304), type(5) is not found in block group
+Chunk[256, 228, 4194304] stripe[1, 4194304] is not found in dev extent
+Chunk[256, 228, 5832704]: length(1638400), offset(5832704), type(5) is not found in block group
+Chunk[256, 228, 5832704] stripe[1, 5832704] is not found in dev extent
+ref mismatch on [0 4096] extent item 0, found 1
+Backref 0 parent 0 root 0 not found in extent tree
+backpointer mismatch on [0 4096]
+ref mismatch on [131072 4096] extent item 0, found 1
+Backref 131072 parent 3 root 3 not found in extent tree
+backpointer mismatch on [131072 4096]
+ref mismatch on [4198400 4096] extent item 0, found 1
+Backref 4198400 parent 1 root 1 not found in extent tree
+backpointer mismatch on [4198400 4096]
+ref mismatch on [4231168 4096] extent item 0, found 1
+Backref 4231168 parent 7 root 7 not found in extent tree
+backpointer mismatch on [4231168 4096]
+ref mismatch on [3472328296227680304 3472328296227680304] extent item 0, found 1
+Backref 3472328296227680304 root 1 owner 2 offset 0 num_refs 0 not found in extent tree
+Incorrect local backref count on 3472328296227680304 root 1 owner 2 offset 0 found 1 wanted 0 back 0x60800000bda0
+backpointer mismatch on [3472328296227680304 3472328296227680304]
+Dev extent's total-byte(0) is not equal to byte-used(7471104) in dev[1, 216, 1]
+Errors found in extent allocation tree or chunk allocation
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+parent transid verify failed on 4198400 wanted 26388280060160 found 5
+Ignoring transid failure
+checking free space cache
+checking fs roots
+root 5 root dir 3472328296227680304 not found
+checking csums
+checking root refs
+checking quota groups
+ASAN:DEADLYSIGNAL
+=================================================================
+==30530==ERROR: AddressSanitizer: SEGV on unknown address 0x62103031ae21 (pc 0x0000005f19b9 bp 0x7ffd4aa30a90 sp 0x7ffd4aa30a90 T0)
+ #0 0x5f19b8 in btrfs_qgroup_status_flags /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:2136:1
+ #1 0x5f16f5 in read_qgroup_status /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:885:10
+ #2 0x5ef38a in load_quota_info /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:949:5
+ #3 0x5eeefc in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:1351:8
+ #4 0x51f08f in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11637:9
+ #5 0x4f0f81 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #6 0x7f7eb11aa730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #7 0x4213f8 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4213f8)
+
+AddressSanitizer can not provide additional info.
+SUMMARY: AddressSanitizer: SEGV /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:2136:1 in btrfs_qgroup_status_flags
+==30530==ABORTING
diff --git a/tests/fuzz-tests/images/bko-166361-blocksize-zero.raw.xz b/tests/fuzz-tests/images/bko-166361-blocksize-zero.raw.xz
new file mode 100644
index 00000000..2b3c6741
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-166361-blocksize-zero.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-167551.raw.txt b/tests/fuzz-tests/images/bko-167551.raw.txt
new file mode 100644
index 00000000..c2ae8548
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-167551.raw.txt
@@ -0,0 +1,29 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=167551
+Lukas Lueg 2016-09-17 18:32:31 UTC
+
+More news from the fuzzer. The attached image causes btrfsck to enter what
+seems to be an endless loop; using btrfs-progs v4.7.2-55-g2b7c507
+
+Starting program: /home/lukas/dev/btrfsfuzz/bin/bin/btrfsck hang000022.img
+Missing separate debuginfos, use: dnf debuginfo-install glibc-2.23.1-10.fc24.x86_64
+[Thread debugging using libthread_db enabled]
+Using host libthread_db library "/lib64/libthread_db.so.1".
+
+Program received signal SIGINT, Interrupt.
+0x00000000004576b7 in alloc_extent_buffer (tree=0x6b5420, bytenr=4198400, blocksize=4096) at extent_io.c:628
+628 {
+Missing separate debuginfos, use: dnf debuginfo-install libblkid-2.28.2-1.fc24.x86_64 libuuid-2.28.2-1.fc24.x86_64 lzo-2.08-8.fc24.x86_64 zlib-1.2.8-10.fc24.x86_64
+#0 0x00000000004576b7 in alloc_extent_buffer (tree=0x6b5420, bytenr=4198400, blocksize=4096) at extent_io.c:628
+#1 0x0000000000444be3 in read_tree_block_fs_info (fs_info=0x6b53a0, bytenr=4198400, blocksize=4096, parent_transid=14) at disk-io.c:339
+#2 0x0000000000440845 in btrfs_search_slot (trans=<optimized out>, root=<optimized out>, key=<optimized out>, p=<optimized out>,
+ ins_len=<optimized out>, cow=<optimized out>) at ctree.c:1175
+#3 0x000000000044bf8a in find_first_block_group (root=0x6b5850, path=0x6b41d0, key=0x7fffffffde78) at extent-tree.c:3142
+#4 0x000000000044bd3a in btrfs_read_block_groups (root=0x6b5850) at extent-tree.c:3240
+#5 0x00000000004464b3 in btrfs_setup_all_roots (fs_info=0x6b53a0, root_tree_bytenr=4202496, flags=<optimized out>) at disk-io.c:1077
+#6 0x0000000000446fc5 in __open_ctree_fd (fp=<optimized out>, path=<optimized out>, sb_bytenr=65536, root_tree_bytenr=<optimized out>,
+ chunk_root_bytenr=<optimized out>, flags=<optimized out>) at disk-io.c:1341
+#7 0x0000000000446d65 in open_ctree_fs_info (filename=0x7fffffffe4f5 "hang000022.img", sb_bytenr=0, root_tree_bytenr=0,
+ chunk_root_bytenr=0, flags=64) at disk-io.c:1387
+#8 0x000000000041bbe2 in cmd_check (argc=<optimized out>, argv=<optimized out>) at cmds-check.c:11382
+#9 0x000000000040a10d in main (argc=<optimized out>, argv=0x7fffffffe218) at btrfs.c:243
+quit
diff --git a/tests/fuzz-tests/images/bko-167551.raw.xz b/tests/fuzz-tests/images/bko-167551.raw.xz
new file mode 100644
index 00000000..2292fb4b
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-167551.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-167781.raw.txt b/tests/fuzz-tests/images/bko-167781.raw.txt
new file mode 100644
index 00000000..f185fb6f
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-167781.raw.txt
@@ -0,0 +1,297 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=167781
+Lukas Lueg 2016-09-17 19:01:47 UTC
+
+More news from the fuzzer. The attached image causes btrfsck to overflow it's
+stack by what seems to be an infinite (or at least sufficiently deep) recursion
+in resolve_one_root(); using btrfs-progs v4.7-42-g56e9586.
+
+
+checking extents
+Chunk[256, 228, 0]: length(4194304), offset(0), type(2) is not found in block group
+Chunk[256, 228, 0] stripe[1, 0] is not found in dev extent
+Chunk[256, 228, 4194304]: length(1638400), offset(4194304), type(5) is not found in block group
+Chunk[256, 228, 4194304] stripe[1, 4194304] is not found in dev extent
+Chunk[256, 228, 5832704]: length(1638400), offset(5832704), type(5) is not found in block group
+Chunk[256, 228, 5832704] stripe[1, 5832704] is not found in dev extent
+ref mismatch on [131072 4096] extent item 0, found 1
+Backref 131072 parent 3 root 3 not found in extent tree
+backpointer mismatch on [131072 4096]
+bad extent [131072, 135168), type mismatch with chunk
+ref mismatch on [4194304 4096] extent item 0, found 1
+Backref 4194304 parent 5 root 5 not found in extent tree
+backpointer mismatch on [4194304 4096]
+ref mismatch on [4198400 4096] extent item 0, found 1
+Backref 4198400 parent 1 root 1 not found in extent tree
+backpointer mismatch on [4198400 4096]
+ref mismatch on [4231168 4096] extent item 0, found 1
+Backref 4231168 parent 7 root 7 not found in extent tree
+backpointer mismatch on [4231168 4096]
+ref mismatch on [3472328296227680304 3472328296227680304] extent item 0, found 1
+Backref 3472328296227680304 root 1 owner 2 offset 0 num_refs 0 not found in extent tree
+Incorrect local backref count on 3472328296227680304 root 1 owner 2 offset 0 found 1 wanted 0 back 0x60800000bd20
+backpointer mismatch on [3472328296227680304 3472328296227680304]
+Dev extent's total-byte(0) is not equal to byte-used(7471104) in dev[1, 216, 1]
+Errors found in extent allocation tree or chunk allocation
+checking free space cache
+checking fs roots
+checking csums
+checking root refs
+checking quota groups
+ASAN:DEADLYSIGNAL
+=================================================================
+==9638==ERROR: AddressSanitizer: stack-overflow on address 0x7ffc0e2d1ff8 (pc 0x0000005f2ed7 bp 0x7ffc0e2d2010 sp 0x7ffc0e2d2000 T0)
+ #0 0x5f2ed6 in find_ref_bytenr /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:253:46
+ #1 0x5f2cba in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:560:20
+ #2 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #3 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #4 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #5 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #6 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #7 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #8 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #9 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #10 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #11 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #12 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #13 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #14 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #15 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #16 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #17 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #18 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #19 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #20 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #21 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #22 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #23 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #24 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #25 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #26 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #27 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #28 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #29 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #30 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #31 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #32 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #33 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #34 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #35 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #36 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #37 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #38 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #39 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #40 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #41 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #42 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #43 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #44 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #45 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #46 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #47 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #48 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #49 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #50 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #51 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #52 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #53 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #54 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #55 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #56 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #57 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #58 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #59 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #60 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #61 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #62 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #63 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #64 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #65 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #66 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #67 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #68 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #69 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #70 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #71 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #72 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #73 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #74 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #75 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #76 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #77 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #78 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #79 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #80 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #81 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #82 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #83 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #84 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #85 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #86 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #87 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #88 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #89 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #90 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #91 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #92 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #93 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #94 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #95 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #96 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #97 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #98 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #99 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #100 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #101 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #102 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #103 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #104 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #105 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #106 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #107 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #108 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #109 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #110 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #111 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #112 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #113 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #114 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #115 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #116 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #117 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #118 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #119 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #120 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #121 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #122 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #123 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #124 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #125 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #126 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #127 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #128 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #129 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #130 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #131 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #132 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #133 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #134 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #135 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #136 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #137 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #138 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #139 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #140 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #141 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #142 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #143 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #144 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #145 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #146 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #147 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #148 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #149 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #150 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #151 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #152 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #153 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #154 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #155 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #156 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #157 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #158 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #159 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #160 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #161 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #162 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #163 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #164 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #165 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #166 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #167 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #168 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #169 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #170 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #171 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #172 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #173 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #174 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #175 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #176 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #177 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #178 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #179 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #180 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #181 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #182 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #183 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #184 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #185 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #186 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #187 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #188 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #189 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #190 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #191 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #192 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #193 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #194 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #195 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #196 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #197 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #198 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #199 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #200 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #201 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #202 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #203 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #204 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #205 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #206 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #207 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #208 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #209 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #210 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #211 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #212 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #213 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #214 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #215 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #216 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #217 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #218 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #219 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #220 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #221 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #222 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #223 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #224 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #225 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #226 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #227 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #228 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #229 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #230 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #231 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #232 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #233 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #234 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #235 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #236 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #237 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #238 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #239 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #240 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #241 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #242 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #243 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #244 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #245 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #246 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #247 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #248 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #249 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #250 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+ #251 0x5f2d1e in resolve_one_root /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:566:9
+
+SUMMARY: AddressSanitizer: stack-overflow /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:253:46 in find_ref_bytenr
+==9638==ABORTING
diff --git a/tests/fuzz-tests/images/bko-167781.raw.xz b/tests/fuzz-tests/images/bko-167781.raw.xz
new file mode 100644
index 00000000..a4bd1de5
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-167781.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-167921.raw.txt b/tests/fuzz-tests/images/bko-167921.raw.txt
new file mode 100644
index 00000000..04ae8a19
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-167921.raw.txt
@@ -0,0 +1,55 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=167921
+Lukas Lueg 2016-09-17 19:16:19 UTC
+
+More news from the fuzzer. The attached image causes a call to abort() when
+running btrfsck over it; using btrfs-progs v4.7.2-55-g2b7c507
+
+Program received signal SIGABRT, Aborted.
+0x00007ffff6fae6f5 in raise () from /lib64/libc.so.6
+#0 0x00007ffff6fae6f5 in raise () from /lib64/libc.so.6
+#1 0x00007ffff6fb02fa in abort () from /lib64/libc.so.6
+#2 0x000000000042390b in run_next_block (root=<optimized out>, bits=<optimized out>, bits_nr=1024, last=<optimized out>,
+ pending=<optimized out>, seen=<optimized out>, reada=<optimized out>, nodes=<optimized out>, extent_cache=<optimized out>,
+ chunk_cache=<optimized out>, dev_cache=<optimized out>, block_group_cache=<optimized out>, dev_extent_cache=<optimized out>,
+ ri=<optimized out>) at cmds-check.c:6424
+#3 0x0000000000421d9b in deal_root_from_list (list=<optimized out>, root=<optimized out>, bits=<optimized out>, bits_nr=1024,
+ pending=<optimized out>, seen=<optimized out>, reada=<optimized out>, nodes=<optimized out>, extent_cache=<optimized out>,
+ chunk_cache=<optimized out>, dev_cache=<optimized out>, block_group_cache=<optimized out>, dev_extent_cache=<optimized out>)
+ at cmds-check.c:8391
+#4 0x000000000041d1d2 in check_chunks_and_extents (root=<optimized out>) at cmds-check.c:8567
+#5 0x000000000041bf0b in cmd_check (argc=<optimized out>, argv=<optimized out>) at cmds-check.c:11493
+#6 0x000000000040a10d in main (argc=<optimized out>, argv=0x7fffffffe218) at btrfs.c:243
+
+parent transid verify failed on 4194304 wanted 65305493131755520 found 14
+parent transid verify failed on 4194304 wanted 65305493131755520 found 14
+Ignoring transid failure
+Checking filesystem on crashing_images/id:000162,sig:06,src:000059+001444,op:splice,rep:2.img
+UUID: 056b0872-c0a7-4121-8ac9-2263ffbee306
+checking extents/bin/sh: line 3: 3091 Aborted LD_LIBRARY_PATH=/home/lukas/dev/btrfsfuzz/bin-asan/lib LD_PRELOAD=/home/lukas/dev/afl_git/libdislocator/libdislocator.so ASAN_OPTIONS=detect_leaks=0 /home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfsck crashing_images/id:000162,sig:06,src:000059+001444,op:splice,rep:2.img
+Starting program: /home/lukas/dev/btrfsfuzz/bin/bin/btrfsck crash000160.img
+Missing separate debuginfos, use: dnf debuginfo-install glibc-2.23.1-10.fc24.x86_64
+[Thread debugging using libthread_db enabled]
+Using host libthread_db library "/lib64/libthread_db.so.1".
+[Inferior 1 (process 21730) exited with code 0376]
+Missing separate debuginfos, use: dnf debuginfo-install libblkid-2.28.2-1.fc24.x86_64 libuuid-2.28.2-1.fc24.x86_64 lzo-2.08-8.fc24.x86_64 zlib-1.2.8-10.fc24.x86_64
+No stack.
+Starting program: /home/lukas/dev/btrfsfuzz/bin/bin/btrfsck crash000162.img
+[Thread debugging using libthread_db enabled]
+Using host libthread_db library "/lib64/libthread_db.so.1".
+
+Program received signal SIGABRT, Aborted.
+0x00007ffff6fae6f5 in raise () from /lib64/libc.so.6
+#0 0x00007ffff6fae6f5 in raise () from /lib64/libc.so.6
+#1 0x00007ffff6fb02fa in abort () from /lib64/libc.so.6
+#2 0x000000000042390b in run_next_block (root=<optimized out>, bits=<optimized out>, bits_nr=1024, last=<optimized out>,
+ pending=<optimized out>, seen=<optimized out>, reada=<optimized out>, nodes=<optimized out>, extent_cache=<optimized out>,
+ chunk_cache=<optimized out>, dev_cache=<optimized out>, block_group_cache=<optimized out>, dev_extent_cache=<optimized out>,
+ ri=<optimized out>) at cmds-check.c:6424
+#3 0x0000000000421d9b in deal_root_from_list (list=<optimized out>, root=<optimized out>, bits=<optimized out>, bits_nr=1024,
+ pending=<optimized out>, seen=<optimized out>, reada=<optimized out>, nodes=<optimized out>, extent_cache=<optimized out>,
+ chunk_cache=<optimized out>, dev_cache=<optimized out>, block_group_cache=<optimized out>, dev_extent_cache=<optimized out>)
+ at cmds-check.c:8391
+#4 0x000000000041d1d2 in check_chunks_and_extents (root=<optimized out>) at cmds-check.c:8567
+#5 0x000000000041bf0b in cmd_check (argc=<optimized out>, argv=<optimized out>) at cmds-check.c:11493
+#6 0x000000000040a10d in main (argc=<optimized out>, argv=0x7fffffffe218) at btrfs.c:243
+quit
diff --git a/tests/fuzz-tests/images/bko-167921.raw.xz b/tests/fuzz-tests/images/bko-167921.raw.xz
new file mode 100644
index 00000000..41d7157e
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-167921.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-168301.raw.txt b/tests/fuzz-tests/images/bko-168301.raw.txt
new file mode 100644
index 00000000..9f3bab87
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-168301.raw.txt
@@ -0,0 +1,51 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=168301
+Lukas Lueg 2016-09-17 20:00:11 UTC
+
+More news from the fuzzer. The attached image causes a call to abort() when
+running btrfsck over it; using btrfs-progs v4.7.2-55-g2b7c507
+
+Program received signal SIGABRT, Aborted.
+0x00007ffff6fae6f5 in raise () from /lib64/libc.so.6
+#0 0x00007ffff6fae6f5 in raise () from /lib64/libc.so.6
+#1 0x00007ffff6fb02fa in abort () from /lib64/libc.so.6
+#2 0x0000000000424fc7 in add_data_backref (extent_cache=0x7fffffffdfe0, bytenr=18446744073709551615, parent=<optimized out>,
+ root=<optimized out>, owner=<optimized out>, offset=<optimized out>, num_refs=<optimized out>, found_ref=<optimized out>,
+ max_size=4096) at cmds-check.c:4856
+#3 0x00000000004234bd in run_next_block (root=<optimized out>, bits=<optimized out>, bits_nr=1024, last=<optimized out>,
+ pending=<optimized out>, seen=<optimized out>, reada=<optimized out>, nodes=<optimized out>, extent_cache=<optimized out>,
+ chunk_cache=<optimized out>, dev_cache=<optimized out>, block_group_cache=<optimized out>, dev_extent_cache=<optimized out>,
+ ri=<optimized out>) at cmds-check.c:6388
+#4 0x0000000000421d9b in deal_root_from_list (list=<optimized out>, root=<optimized out>, bits=<optimized out>, bits_nr=1024,
+ pending=<optimized out>, seen=<optimized out>, reada=<optimized out>, nodes=<optimized out>, extent_cache=<optimized out>,
+ chunk_cache=<optimized out>, dev_cache=<optimized out>, block_group_cache=<optimized out>, dev_extent_cache=<optimized out>)
+ at cmds-check.c:8391
+#5 0x000000000041d160 in check_chunks_and_extents (root=<optimized out>) at cmds-check.c:8558
+#6 0x000000000041bf0b in cmd_check (argc=<optimized out>, argv=<optimized out>) at cmds-check.c:11493
+#7 0x000000000040a10d in main (argc=<optimized out>, argv=0x7fffffffe218) at btrfs.c:243
+
+Checking filesystem on crashing_images/id:000170,sig:06,src:001268,op:havoc,rep:8.img
+UUID: 056b0872-c0a7-4121-8ac9-2263ffbee306
+checking extents/bin/sh: line 3: 4644 Aborted LD_LIBRARY_PATH=/home/lukas/dev/btrfsfuzz/bin-asan/lib LD_PRELOAD=/home/lukas/dev/afl_git/libdislocator/libdislocator.so ASAN_OPTIONS=detect_leaks=0 /home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfsck crashing_images/id:000170,sig:06,src:001268,op:havoc,rep:8.img
+Starting program: /home/lukas/dev/btrfsfuzz/bin/bin/btrfsck crash000170.img
+[Thread debugging using libthread_db enabled]
+Using host libthread_db library "/lib64/libthread_db.so.1".
+
+Program received signal SIGABRT, Aborted.
+0x00007ffff6fae6f5 in raise () from /lib64/libc.so.6
+#0 0x00007ffff6fae6f5 in raise () from /lib64/libc.so.6
+#1 0x00007ffff6fb02fa in abort () from /lib64/libc.so.6
+#2 0x0000000000424fc7 in add_data_backref (extent_cache=0x7fffffffdfe0, bytenr=18446744073709551615, parent=<optimized out>,
+ root=<optimized out>, owner=<optimized out>, offset=<optimized out>, num_refs=<optimized out>, found_ref=<optimized out>,
+ max_size=4096) at cmds-check.c:4856
+#3 0x00000000004234bd in run_next_block (root=<optimized out>, bits=<optimized out>, bits_nr=1024, last=<optimized out>,
+ pending=<optimized out>, seen=<optimized out>, reada=<optimized out>, nodes=<optimized out>, extent_cache=<optimized out>,
+ chunk_cache=<optimized out>, dev_cache=<optimized out>, block_group_cache=<optimized out>, dev_extent_cache=<optimized out>,
+ ri=<optimized out>) at cmds-check.c:6388
+#4 0x0000000000421d9b in deal_root_from_list (list=<optimized out>, root=<optimized out>, bits=<optimized out>, bits_nr=1024,
+ pending=<optimized out>, seen=<optimized out>, reada=<optimized out>, nodes=<optimized out>, extent_cache=<optimized out>,
+ chunk_cache=<optimized out>, dev_cache=<optimized out>, block_group_cache=<optimized out>, dev_extent_cache=<optimized out>)
+ at cmds-check.c:8391
+#5 0x000000000041d160 in check_chunks_and_extents (root=<optimized out>) at cmds-check.c:8558
+#6 0x000000000041bf0b in cmd_check (argc=<optimized out>, argv=<optimized out>) at cmds-check.c:11493
+#7 0x000000000040a10d in main (argc=<optimized out>, argv=0x7fffffffe218) at btrfs.c:243
+quit
diff --git a/tests/fuzz-tests/images/bko-168301.raw.xz b/tests/fuzz-tests/images/bko-168301.raw.xz
new file mode 100644
index 00000000..4c7f4623
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-168301.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-169301-1-blocksize-zero.raw.txt b/tests/fuzz-tests/images/bko-169301-1-blocksize-zero.raw.txt
new file mode 100644
index 00000000..c5ec8ee1
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-169301-1-blocksize-zero.raw.txt
@@ -0,0 +1,134 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=169301
+Lukas Lueg 2016-09-18 09:07:55 UTC
+
+More news from the fuzzer. The attached image causes a heap-use-after-free
+when running btrfsck with ASAN over it; using btrfs-progs v4.7.2-56-ge8c2013
+
+==3439==ERROR: AddressSanitizer: heap-use-after-free on address 0x621000014170 at pc 0x0000005c05ae bp 0x7ffe84ef8d00 sp 0x7ffe84ef8cf8
+READ of size 4 at 0x621000014170 thread T0
+ #0 0x5c05ad in free_extent_buffer /home/slave/dev/btrfsfuzz/src-asan/extent_io.c:579:10
+ #1 0x59360c in btrfs_release_all_roots /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:1096:3
+ #2 0x5961bb in close_ctree_fs_info /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:1805:2
+ #3 0x5246e7 in close_ctree /home/slave/dev/btrfsfuzz/src-asan/./disk-io.h:155:9
+ #4 0x51e334 in cmd_check /home/slave/dev/btrfsfuzz/src-asan/cmds-check.c:11618:2
+ #5 0x4f0ee1 in main /home/slave/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #6 0x7f792c60e730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #7 0x421358 in _start (/home/slave/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+Probably somewhat related to this: The image crash000255.img causes btrfsck to
+try to allocate around 3.5gb of memory in one chunk, sending ASAN into a death
+spiral. On systems with sufficient memory, the heap-use-after-free turns up.
+
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+parent transid verify failed on 0 wanted 3472328296227680304 found 0
+Ignoring transid failure
+Chunk[256, 228, 0]: length(4194304), offset(0), type(2) is not found in block group
+Chunk[256, 228, 0] stripe[1, 0] is not found in dev extent
+Chunk[256, 228, 4194304]: length(1638400), offset(4194304), type(5) is not found in block group
+Chunk[256, 228, 4194304] stripe[1, 4194304] is not found in dev extent
+Chunk[256, 228, 5832704]: length(1638400), offset(5832704), type(5) is not found in block group
+Chunk[256, 228, 5832704] stripe[1, 5832704] is not found in dev extent
+ref mismatch on [0 4096] extent item 0, found 1
+Backref 0 parent 0 root 0 not found in extent tree
+backpointer mismatch on [0 4096]
+bad extent [0, 4096), type mismatch with chunk
+ref mismatch on [131072 4096] extent item 0, found 1
+Backref 131072 parent 3 root 3 not found in extent tree
+backpointer mismatch on [131072 4096]
+ref mismatch on [4198400 4096] extent item 0, found 1
+Backref 4198400 parent 1 root 1 not found in extent tree
+backpointer mismatch on [4198400 4096]
+ref mismatch on [4231168 4096] extent item 0, found 1
+Backref 4231168 parent 7 root 7 not found in extent tree
+backpointer mismatch on [4231168 4096]
+ref mismatch on [3472328296227680304 3472328296227680304] extent item 0, found 1
+Backref 3472328296227680304 root 1 owner 2 offset 0 num_refs 0 not found in extent tree
+Incorrect local backref count on 3472328296227680304 root 1 owner 2 offset 0 found 1 wanted 0 back 0x60700000ddf0
+backpointer mismatch on [3472328296227680304 3472328296227680304]
+Dev extent's total-byte(0) is not equal to byte-used(7471104) in dev[1, 216, 1]
+checking free space cache
+checking fs roots
+root 5 root dir 3472328296227680304 not found
+checking csums
+checking root refs
+checking quota groups
+ERROR: while mapping refs: -5
+=================================================================
+==3439==ERROR: AddressSanitizer: heap-use-after-free on address 0x621000014170 at pc 0x0000005c05ae bp 0x7ffe84ef8d00 sp 0x7ffe84ef8cf8
+READ of size 4 at 0x621000014170 thread T0
+ #0 0x5c05ad in free_extent_buffer /home/slave/dev/btrfsfuzz/src-asan/extent_io.c:579:10
+ #1 0x59360c in btrfs_release_all_roots /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:1096:3
+ #2 0x5961bb in close_ctree_fs_info /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:1805:2
+ #3 0x5246e7 in close_ctree /home/slave/dev/btrfsfuzz/src-asan/./disk-io.h:155:9
+ #4 0x51e334 in cmd_check /home/slave/dev/btrfsfuzz/src-asan/cmds-check.c:11618:2
+ #5 0x4f0ee1 in main /home/slave/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #6 0x7f792c60e730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #7 0x421358 in _start (/home/slave/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+0x621000014170 is located 112 bytes inside of 4224-byte region [0x621000014100,0x621000015180)
+freed by thread T0 here:
+ #0 0x4bf990 in __interceptor_cfree.localalias.1 (/home/slave/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4bf990)
+ #1 0x5c0582 in free_extent_buffer /home/slave/dev/btrfsfuzz/src-asan/extent_io.c:591:3
+ #2 0x5c1b18 in alloc_extent_buffer /home/slave/dev/btrfsfuzz/src-asan/extent_io.c:644:4
+ #3 0x58de0c in btrfs_find_create_tree_block /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:193:9
+ #4 0x58e880 in read_tree_block_fs_info /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:339:7
+ #5 0x5f2d74 in read_tree_block /home/slave/dev/btrfsfuzz/src-asan/./disk-io.h:112:9
+ #6 0x5f2b52 in travel_tree /home/slave/dev/btrfsfuzz/src-asan/qgroup-verify.c:692:7
+ #7 0x5f299b in add_refs_for_implied /home/slave/dev/btrfsfuzz/src-asan/qgroup-verify.c:748:8
+ #8 0x5efd39 in map_implied_refs /home/slave/dev/btrfsfuzz/src-asan/qgroup-verify.c:766:9
+ #9 0x5eed89 in qgroup_verify_all /home/slave/dev/btrfsfuzz/src-asan/qgroup-verify.c:1366:8
+ #10 0x51ea14 in cmd_check /home/slave/dev/btrfsfuzz/src-asan/cmds-check.c:11571:9
+ #11 0x4f0ee1 in main /home/slave/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #12 0x7f792c60e730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+
+previously allocated by thread T0 here:
+ #0 0x4bfca0 in calloc (/home/slave/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4bfca0)
+ #1 0x5c16ca in __alloc_extent_buffer /home/slave/dev/btrfsfuzz/src-asan/extent_io.c:542:7
+ #2 0x5c1b26 in alloc_extent_buffer /home/slave/dev/btrfsfuzz/src-asan/extent_io.c:646:8
+ #3 0x58de0c in btrfs_find_create_tree_block /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:193:9
+ #4 0x58e880 in read_tree_block_fs_info /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:339:7
+ #5 0x5918a2 in read_tree_block /home/slave/dev/btrfsfuzz/src-asan/./disk-io.h:112:9
+ #6 0x591712 in find_and_setup_root /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:647:15
+ #7 0x593243 in setup_root_or_create_block /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:966:8
+ #8 0x592a06 in btrfs_setup_all_roots /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:1045:8
+ #9 0x5948fe in __open_ctree_fd /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:1341:8
+ #10 0x5942b5 in open_ctree_fs_info /home/slave/dev/btrfsfuzz/src-asan/disk-io.c:1387:9
+ #11 0x51dff2 in cmd_check /home/slave/dev/btrfsfuzz/src-asan/cmds-check.c:11382:9
+ #12 0x4f0ee1 in main /home/slave/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #13 0x7f792c60e730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+
+SUMMARY: AddressSanitizer: heap-use-after-free /home/slave/dev/btrfsfuzz/src-asan/extent_io.c:579:10 in free_extent_buffer
+Shadow bytes around the buggy address:
+ 0x0c427fffa7d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffa7e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffa7f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffa800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c427fffa810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+=>0x0c427fffa820: fd fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]fd
+ 0x0c427fffa830: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+ 0x0c427fffa840: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+ 0x0c427fffa850: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+ 0x0c427fffa860: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+ 0x0c427fffa870: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+Shadow byte legend (one shadow byte represents 8 application bytes):
+ Addressable: 00
+ Partially addressable: 01 02 03 04 05 06 07
+ Heap left redzone: fa
+ Heap right redzone: fb
+ Freed heap region: fd
+ Stack left redzone: f1
+ Stack mid redzone: f2
+ Stack right redzone: f3
+ Stack partial redzone: f4
+ Stack after return: f5
+ Stack use after scope: f8
+ Global redzone: f9
+ Global init order: f6
+ Poisoned by user: f7
+ Container overflow: fc
+ Array cookie: ac
+ Intra object redzone: bb
+ ASan internal: fe
+ Left alloca redzone: ca
+ Right alloca redzone: cb
+==3439==ABORTING
diff --git a/tests/fuzz-tests/images/bko-169301-1-blocksize-zero.raw.xz b/tests/fuzz-tests/images/bko-169301-1-blocksize-zero.raw.xz
new file mode 100644
index 00000000..70c2b22f
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-169301-1-blocksize-zero.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-169301-2-blocksize-zero.raw.txt b/tests/fuzz-tests/images/bko-169301-2-blocksize-zero.raw.txt
new file mode 100644
index 00000000..0af00ec6
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-169301-2-blocksize-zero.raw.txt
@@ -0,0 +1,185 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=169301
+Lukas Lueg 2016-09-18 09:07:55 UTC
+
+parent transid verify failed on 4231168 wanted 274877906948 found 4
+Ignoring transid failure
+parent transid verify failed on 4222976 wanted 3472328296227680304 found 4
+parent transid verify failed on 4222976 wanted 3472328296227680304 found 4
+Ignoring transid failure
+checking extents
+Chunk[256, 228, 0]: length(4194304), offset(0), type(2) is not found in block group
+Chunk[256, 228, 0] stripe[1, 0] is not found in dev extent
+Chunk[256, 228, 4194304]: length(1638400), offset(4194304), type(5) is not found in block group
+Chunk[256, 228, 4194304] stripe[1, 4194304] is not found in dev extent
+Chunk[256, 228, 5832704]: length(1638400), offset(5832704), type(5) is not found in block group
+Chunk[256, 228, 5832704] stripe[1, 5832704] is not found in dev extent
+ref mismatch on [131072 4096] extent item 0, found 1
+Backref 131072 parent 3 root 3 not found in extent tree
+backpointer mismatch on [131072 4096]
+ref mismatch on [4194304 4096] extent item 0, found 1
+Backref 4194304 parent 5 root 5 not found in extent tree
+backpointer mismatch on [4194304 4096]
+ref mismatch on [4198400 4096] extent item 0, found 1
+Backref 4198400 parent 1 root 1 not found in extent tree
+backpointer mismatch on [4198400 4096]
+ref mismatch on [4231168 4096] extent item 0, found 1
+Backref 4231168 parent 7 root 7 not found in extent tree
+backpointer mismatch on [4231168 4096]
+ref mismatch on [3472328296227680304 3472328296227680304] extent item 0, found 1
+Backref 3472328296227680304 root 1 owner 2 offset 0 num_refs 0 not found in extent tree
+Incorrect local backref count on 3472328296227680304 root 1 owner 2 offset 0 found 1 wanted 0 back 0x60800000bc20
+backpointer mismatch on [3472328296227680304 3472328296227680304]
+Dev extent's total-byte(0) is not equal to byte-used(7471104) in dev[1, 216, 1]
+Errors found in extent allocation tree or chunk allocation
+checking free space cache
+checking fs roots
+checking csums
+checking root refs
+checking quota groups
+==23294==ERROR: AddressSanitizer failed to allocate 0xe4ff4000 (3841933312) bytes of LargeMmapAllocator (error code: 12)
+==23294==Process memory map follows:
+ 0x000000400000-0x0000006a6000 /home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs
+ 0x0000008a6000-0x0000008b9000 /home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs
+ 0x0000008b9000-0x0000008ef000 /home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs
+ 0x0000008ef000-0x000001567000
+ 0x00007fff7000-0x00008fff7000
+ 0x00008fff7000-0x02008fff7000
+ 0x02008fff7000-0x10007fff8000
+ 0x600000000000-0x602000000000
+ 0x602000000000-0x602000010000
+ 0x602000010000-0x603000000000
+ 0x603000000000-0x603000010000
+ 0x603000010000-0x604000000000
+ 0x604000000000-0x604000010000
+ 0x604000010000-0x606000000000
+ 0x606000000000-0x606000010000
+ 0x606000010000-0x607000000000
+ 0x607000000000-0x607000010000
+ 0x607000010000-0x608000000000
+ 0x608000000000-0x608000010000
+ 0x608000010000-0x60c000000000
+ 0x60c000000000-0x60c000010000
+ 0x60c000010000-0x60d000000000
+ 0x60d000000000-0x60d000010000
+ 0x60d000010000-0x60e000000000
+ 0x60e000000000-0x60e000010000
+ 0x60e000010000-0x611000000000
+ 0x611000000000-0x611000010000
+ 0x611000010000-0x616000000000
+ 0x616000000000-0x616000020000
+ 0x616000020000-0x619000000000
+ 0x619000000000-0x619000020000
+ 0x619000020000-0x621000000000
+ 0x621000000000-0x621000020000
+ 0x621000020000-0x624000000000
+ 0x624000000000-0x624000020000
+ 0x624000020000-0x629000000000
+ 0x629000000000-0x629000010000
+ 0x629000010000-0x640000000000
+ 0x640000000000-0x640000003000
+ 0x7f62fb97e000-0x7f62fdcd0000
+ 0x7f62fdcd0000-0x7f62fde89000 /usr/lib64/libc-2.23.so
+ 0x7f62fde89000-0x7f62fe088000 /usr/lib64/libc-2.23.so
+ 0x7f62fe088000-0x7f62fe08c000 /usr/lib64/libc-2.23.so
+ 0x7f62fe08c000-0x7f62fe08e000 /usr/lib64/libc-2.23.so
+ 0x7f62fe08e000-0x7f62fe092000
+ 0x7f62fe092000-0x7f62fe0a8000 /usr/lib64/libgcc_s-6.1.1-20160621.so.1
+ 0x7f62fe0a8000-0x7f62fe2a7000 /usr/lib64/libgcc_s-6.1.1-20160621.so.1
+ 0x7f62fe2a7000-0x7f62fe2a8000 /usr/lib64/libgcc_s-6.1.1-20160621.so.1
+ 0x7f62fe2a8000-0x7f62fe2a9000 /usr/lib64/libgcc_s-6.1.1-20160621.so.1
+ 0x7f62fe2a9000-0x7f62fe2ac000 /usr/lib64/libdl-2.23.so
+ 0x7f62fe2ac000-0x7f62fe4ab000 /usr/lib64/libdl-2.23.so
+ 0x7f62fe4ab000-0x7f62fe4ac000 /usr/lib64/libdl-2.23.so
+ 0x7f62fe4ac000-0x7f62fe4ad000 /usr/lib64/libdl-2.23.so
+ 0x7f62fe4ad000-0x7f62fe5b5000 /usr/lib64/libm-2.23.so
+ 0x7f62fe5b5000-0x7f62fe7b4000 /usr/lib64/libm-2.23.so
+ 0x7f62fe7b4000-0x7f62fe7b5000 /usr/lib64/libm-2.23.so
+ 0x7f62fe7b5000-0x7f62fe7b6000 /usr/lib64/libm-2.23.so
+ 0x7f62fe7b6000-0x7f62fe7bd000 /usr/lib64/librt-2.23.so
+ 0x7f62fe7bd000-0x7f62fe9bc000 /usr/lib64/librt-2.23.so
+ 0x7f62fe9bc000-0x7f62fe9bd000 /usr/lib64/librt-2.23.so
+ 0x7f62fe9bd000-0x7f62fe9be000 /usr/lib64/librt-2.23.so
+ 0x7f62fe9be000-0x7f62fe9d5000 /usr/lib64/libpthread-2.23.so
+ 0x7f62fe9d5000-0x7f62febd4000 /usr/lib64/libpthread-2.23.so
+ 0x7f62febd4000-0x7f62febd5000 /usr/lib64/libpthread-2.23.so
+ 0x7f62febd5000-0x7f62febd6000 /usr/lib64/libpthread-2.23.so
+ 0x7f62febd6000-0x7f62febda000
+ 0x7f62febda000-0x7f62febfc000 /usr/lib64/liblzo2.so.2.0.0
+ 0x7f62febfc000-0x7f62fedfb000 /usr/lib64/liblzo2.so.2.0.0
+ 0x7f62fedfb000-0x7f62fedfc000 /usr/lib64/liblzo2.so.2.0.0
+ 0x7f62fedfc000-0x7f62fedfd000
+ 0x7f62fedfd000-0x7f62fee12000 /usr/lib64/libz.so.1.2.8
+ 0x7f62fee12000-0x7f62ff011000 /usr/lib64/libz.so.1.2.8
+ 0x7f62ff011000-0x7f62ff012000 /usr/lib64/libz.so.1.2.8
+ 0x7f62ff012000-0x7f62ff013000 /usr/lib64/libz.so.1.2.8
+ 0x7f62ff013000-0x7f62ff050000 /usr/lib64/libblkid.so.1.1.0
+ 0x7f62ff050000-0x7f62ff250000 /usr/lib64/libblkid.so.1.1.0
+ 0x7f62ff250000-0x7f62ff254000 /usr/lib64/libblkid.so.1.1.0
+ 0x7f62ff254000-0x7f62ff255000 /usr/lib64/libblkid.so.1.1.0
+ 0x7f62ff255000-0x7f62ff256000
+ 0x7f62ff256000-0x7f62ff25a000 /usr/lib64/libuuid.so.1.3.0
+ 0x7f62ff25a000-0x7f62ff459000 /usr/lib64/libuuid.so.1.3.0
+ 0x7f62ff459000-0x7f62ff45a000 /usr/lib64/libuuid.so.1.3.0
+ 0x7f62ff45a000-0x7f62ff45b000
+ 0x7f62ff45b000-0x7f62ff45d000 /home/lukas/dev/afl_git/libdislocator/libdislocator.so
+ 0x7f62ff45d000-0x7f62ff65c000 /home/lukas/dev/afl_git/libdislocator/libdislocator.so
+ 0x7f62ff65c000-0x7f62ff65d000 /home/lukas/dev/afl_git/libdislocator/libdislocator.so
+ 0x7f62ff65d000-0x7f62ff65e000 /home/lukas/dev/afl_git/libdislocator/libdislocator.so
+ 0x7f62ff65e000-0x7f62ff682000 /usr/lib64/ld-2.23.so
+ 0x7f62ff810000-0x7f62ff879000
+ 0x7f62ff879000-0x7f62ff881000
+ 0x7f62ff881000-0x7f62ff882000 /usr/lib64/ld-2.23.so
+ 0x7f62ff882000-0x7f62ff883000 /usr/lib64/ld-2.23.so
+ 0x7f62ff883000-0x7f62ff884000
+ 0x7fff5a065000-0x7fff5a086000 [stack]
+ 0x7fff5a0c7000-0x7fff5a0ca000 [vvar]
+ 0x7fff5a0ca000-0x7fff5a0cc000 [vdso]
+ 0xffffffffff600000-0xffffffffff601000 [vsyscall]
+==23294==End of process memory map.
+==23294==AddressSanitizer CHECK failed: /builddir/build/BUILD/compiler-rt-3.8.0.src/lib/sanitizer_common/sanitizer_common.cc:183 "((0 && "unable to mmap")) != (0)" (0x0, 0x0)
+ #0 0x4c90cd in __asan::AsanCheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4c90cd)
+ #1 0x4cfa73 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4cfa73)
+ #2 0x4cfc61 in __sanitizer::ReportMmapFailureAndDie(unsigned long, char const*, char const*, int, bool) (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4cfc61)
+ #3 0x4d8922 in __sanitizer::MmapOrDie(unsigned long, char const*, bool) (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4d8922)
+ #4 0x42dbab in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x42dbab)
+ #5 0x4259fb in __asan::asan_calloc(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4259fb)
+ #6 0x4bfd1a in calloc (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4bfd1a)
+ #7 0x5c181a in __alloc_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:542:7
+ #8 0x5c1c76 in alloc_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:646:8
+ #9 0x58e01c in btrfs_find_create_tree_block /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:193:9
+ #10 0x58ea90 in read_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:339:7
+ #11 0x5f2f84 in read_tree_block /home/lukas/dev/btrfsfuzz/src-asan/./disk-io.h:112:9
+ #12 0x5f2d62 in travel_tree /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:692:7
+ #13 0x5f2bab in add_refs_for_implied /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:748:8
+ #14 0x5eff59 in map_implied_refs /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:766:9
+ #15 0x5eefa9 in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:1366:8
+ #16 0x51f08f in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11637:9
+ #17 0x4f0f81 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #18 0x7f62fdcf0730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #19 0x4213f8 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4213f8)
+
+checking free space cache
+checking fs roots
+checking csums
+checking root refs
+checking quota groups
+ERROR: while mapping refs: -5
+checking extentsErrors found in extent allocation tree or chunk allocationfound 3472328296227696688 bytes used err is 0
+total csum bytes: 0
+total tree bytes: 16384
+total fs tree bytes: 4096
+total extent tree bytes: 0
+btree space waste bytes: 12674
+file data blocks allocated: 3472328296227680304
+ referenced 3472328296227680304
+extent_io.c:580: free_extent_buffer: Assertion `eb->refs < 0` failed.
+../btrfs[0x47a4a3]
+../btrfs[0x47a550]
+../btrfs(free_extent_buffer+0x6e)[0x47b73c]
+../btrfs(btrfs_release_all_roots+0x8c)[0x461cdf]
+../btrfs(close_ctree_fs_info+0x1f3)[0x46391a]
+../btrfs[0x424043]
+../btrfs(cmd_check+0xe1a)[0x43f352]
+../btrfs(main+0x12b)[0x40b581]
+/lib64/libc.so.6(__libc_start_main+0xf1)[0x7f970daf3291]
+../btrfs(_start+0x2a)[0x40afba]
diff --git a/tests/fuzz-tests/images/bko-169301-2-blocksize-zero.raw.xz b/tests/fuzz-tests/images/bko-169301-2-blocksize-zero.raw.xz
new file mode 100644
index 00000000..68f7ffd4
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-169301-2-blocksize-zero.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-169311-blocksize-zero-qgroup-verify.raw.txt b/tests/fuzz-tests/images/bko-169311-blocksize-zero-qgroup-verify.raw.txt
new file mode 100644
index 00000000..c3d491be
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-169311-blocksize-zero-qgroup-verify.raw.txt
@@ -0,0 +1,126 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=169311
+Lukas Lueg 2016-09-18 09:23:44 UTC
+
+More news from the fuzzer. The attached image causes a heap-buffer-overflow
+when running btrfsck with ASAN over it; using btrfs-prog s v4.7.2-56-ge8c2013
+
+
+==32491==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c00000bf5c at pc 0x000000614b63 bp 0x7ffeacb5c3b0 sp 0x7ffeacb
+5c3a8
+READ of size 8 at 0x60c00000bf5c thread T0
+ #0 0x614b62 in crc32c_intel /home/lukas/dev/btrfsfuzz/src-asan/crc32c.c:75:19
+ #1 0x614c09 in crc32c_le /home/lukas/dev/btrfsfuzz/src-asan/crc32c.c:221:9
+ #2 0x58de58 in __csum_tree_block_size /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:139:8
+ #3 0x58dd88 in csum_tree_block_size /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:159:9
+ #4 0x58dfa1 in csum_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:174:9
+ #5 0x58eb64 in read_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:348:19
+ #6 0x5f2f84 in read_tree_block /home/lukas/dev/btrfsfuzz/src-asan/./disk-io.h:112:9
+ #7 0x5f2d62 in travel_tree /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:692:7
+ #8 0x5f2bab in add_refs_for_implied /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:748:8
+ #9 0x5eff59 in map_implied_refs /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:766:9
+ #10 0x5eefa9 in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:1366:8
+ #11 0x51f08f in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11637:9
+ #12 0x4f0f81 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #13 0x7fbf35742730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #14 0x4213f8 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4213f8)
+
+checking extents
+Chunk[256, 228, 0]: length(4194304), offset(0), type(2) is not found in block group
+Chunk[256, 228, 0] stripe[1, 0] is not found in dev extent
+Chunk[256, 228, 4194304]: length(1638400), offset(4194304), type(5) is not found in block group
+Chunk[256, 228, 4194304] stripe[1, 4194304] is not found in dev extent
+Chunk[256, 228, 5832704]: length(1638400), offset(5832704), type(5) is not found in block group
+Chunk[256, 228, 5832704] stripe[1, 5832704] is not found in dev extent
+ref mismatch on [131072 4096] extent item 0, found 1
+Backref 131072 parent 3 root 3 not found in extent tree
+backpointer mismatch on [131072 4096]
+ref mismatch on [4194304 4096] extent item 0, found 1
+Backref 4194304 parent 5 root 5 not found in extent tree
+backpointer mismatch on [4194304 4096]
+ref mismatch on [4198400 4096] extent item 0, found 1
+Backref 4198400 parent 1 root 1 not found in extent tree
+backpointer mismatch on [4198400 4096]
+ref mismatch on [4231168 4096] extent item 0, found 1
+Backref 4231168 parent 7 root 7 not found in extent tree
+backpointer mismatch on [4231168 4096]
+ref mismatch on [3472328296227680304 3472328296227680304] extent item 0, found 1
+Backref 3472328296227680304 root 1 owner 2 offset 0 num_refs 0 not found in extent tree
+Incorrect local backref count on 3472328296227680304 root 1 owner 2 offset 0 found 1 wanted 0 back 0x60800000bc20
+backpointer mismatch on [3472328296227680304 3472328296227680304]
+Dev extent's total-byte(0) is not equal to byte-used(7471104) in dev[1, 216, 1]
+Errors found in extent allocation tree or chunk allocation
+checking free space cache
+checking fs roots
+checking csums
+checking root refs
+checking quota groups
+=================================================================
+==32491==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c00000bf5c at pc 0x000000614b63 bp 0x7ffeacb5c3b0 sp 0x7ffeacb5c3a8
+READ of size 8 at 0x60c00000bf5c thread T0
+ #0 0x614b62 in crc32c_intel /home/lukas/dev/btrfsfuzz/src-asan/crc32c.c:75:19
+ #1 0x614c09 in crc32c_le /home/lukas/dev/btrfsfuzz/src-asan/crc32c.c:221:9
+ #2 0x58de58 in __csum_tree_block_size /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:139:8
+ #3 0x58dd88 in csum_tree_block_size /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:159:9
+ #4 0x58dfa1 in csum_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:174:9
+ #5 0x58eb64 in read_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:348:19
+ #6 0x5f2f84 in read_tree_block /home/lukas/dev/btrfsfuzz/src-asan/./disk-io.h:112:9
+ #7 0x5f2d62 in travel_tree /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:692:7
+ #8 0x5f2bab in add_refs_for_implied /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:748:8
+ #9 0x5eff59 in map_implied_refs /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:766:9
+ #10 0x5eefa9 in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:1366:8
+ #11 0x51f08f in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11637:9
+ #12 0x4f0f81 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #13 0x7fbf35742730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #14 0x4213f8 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4213f8)
+
+0x60c00000bf5c is located 28 bytes to the right of 128-byte region [0x60c00000bec0,0x60c00000bf40)
+allocated by thread T0 here:
+ #0 0x4bfd40 in calloc (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4bfd40)
+ #1 0x5c181a in __alloc_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:542:7
+ #2 0x5c1c76 in alloc_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:646:8
+ #3 0x58e01c in btrfs_find_create_tree_block /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:193:9
+ #4 0x58ea90 in read_tree_block_fs_info /home/lukas/dev/btrfsfuzz/src-asan/disk-io.c:339:7
+ #5 0x5f2f84 in read_tree_block /home/lukas/dev/btrfsfuzz/src-asan/./disk-io.h:112:9
+ #6 0x5f2d62 in travel_tree /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:692:7
+ #7 0x5f2bab in add_refs_for_implied /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:748:8
+ #8 0x5eff59 in map_implied_refs /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:766:9
+ #9 0x5eefa9 in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:1366:8
+ #10 0x51f08f in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11637:9
+ #11 0x4f0f81 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #12 0x7fbf35742730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+
+SUMMARY: AddressSanitizer: heap-buffer-overflow /home/lukas/dev/btrfsfuzz/src-asan/crc32c.c:75:19 in crc32c_intel
+Shadow bytes around the buggy address:
+ 0x0c187fff9790: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c187fff97a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c187fff97b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c187fff97c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c187fff97d0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
+=>0x0c187fff97e0: 00 00 00 00 00 00 00 00 fa fa fa[fa]fa fa fa fa
+ 0x0c187fff97f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
+ 0x0c187fff9800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c187fff9810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c187fff9820: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c187fff9830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+Shadow byte legend (one shadow byte represents 8 application bytes):
+ Addressable: 00
+ Partially addressable: 01 02 03 04 05 06 07
+ Heap left redzone: fa
+ Heap right redzone: fb
+ Freed heap region: fd
+ Stack left redzone: f1
+ Stack mid redzone: f2
+ Stack right redzone: f3
+ Stack partial redzone: f4
+ Stack after return: f5
+ Stack use after scope: f8
+ Global redzone: f9
+ Global init order: f6
+ Poisoned by user: f7
+ Container overflow: fc
+ Array cookie: ac
+ Intra object redzone: bb
+ ASan internal: fe
+ Left alloca redzone: ca
+ Right alloca redzone: cb
+==32491==ABORTING
diff --git a/tests/fuzz-tests/images/bko-169311-blocksize-zero-qgroup-verify.raw.xz b/tests/fuzz-tests/images/bko-169311-blocksize-zero-qgroup-verify.raw.xz
new file mode 100644
index 00000000..c506d1bc
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-169311-blocksize-zero-qgroup-verify.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-172811.raw.txt b/tests/fuzz-tests/images/bko-172811.raw.txt
new file mode 100644
index 00000000..bbdf99b5
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-172811.raw.txt
@@ -0,0 +1,55 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=172811
+Lukas Lueg 2016-09-23 18:34:15 UTC
+
+More news from the fuzzer. The attached image causes a segmentation fault when
+running btrfsck over it; using btrfs-progs v4.7.2-55-g2b7c507
+
+This may be the same cause as 156721, the call-tree is different, though.
+
+The juicy parts:
+
+==19342==ERROR: AddressSanitizer: SEGV on unknown address 0x0000000000e5 (pc 0x7f3b12e1df50 bp 0x7ffeb50b4260 sp 0x7ffeb50b39e8 T0)
+ #0 0x7f3b12e1df4f in __memmove_avx_unaligned (/lib64/libc.so.6+0x149f4f)
+ #1 0x4a982c in __asan_memcpy (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4a982c)
+ #2 0x5c2d59 in read_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:867:2
+ #3 0x52eaa6 in btrfs_node_key /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:1667:2
+ #4 0x5436c7 in check_fs_root /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:3661:3
+ #5 0x5224ef in check_fs_roots /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:3809:10
+ #6 0x51e772 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11533:8
+ #7 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #8 0x7f3b12cf4730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #9 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+parent transid verify failed on 4198400 wanted 65305493131755520 found 14
+parent transid verify failed on 4198400 wanted 65305493131755520 found 14
+Ignoring transid failure
+ERROR: add_tree_backref failed: File exists
+ERROR: add_tree_backref failed: File exists
+parent transid verify failed on 131072 wanted 36283884678912 found 4
+parent transid verify failed on 131072 wanted 36283884678912 found 4
+Ignoring transid failure
+ERROR: tree block bytenr 1280 is not aligned to sectorsize 4096
+checking free space cache
+checking fs roots
+root 5 root dir 41471 not found
+parent transid verify failed on 4198400 wanted 4 found 14
+Ignoring transid failure
+parent transid verify failed on 131072 wanted 36283884678912 found 4
+Ignoring transid failure
+ASAN:DEADLYSIGNAL
+=================================================================
+==19342==ERROR: AddressSanitizer: SEGV on unknown address 0x0000000000e5 (pc 0x7f3b12e1df50 bp 0x7ffeb50b4260 sp 0x7ffeb50b39e8 T0)
+ #0 0x7f3b12e1df4f in __memmove_avx_unaligned (/lib64/libc.so.6+0x149f4f)
+ #1 0x4a982c in __asan_memcpy (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x4a982c)
+ #2 0x5c2d59 in read_extent_buffer /home/lukas/dev/btrfsfuzz/src-asan/extent_io.c:867:2
+ #3 0x52eaa6 in btrfs_node_key /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:1667:2
+ #4 0x5436c7 in check_fs_root /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:3661:3
+ #5 0x5224ef in check_fs_roots /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:3809:10
+ #6 0x51e772 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11533:8
+ #7 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #8 0x7f3b12cf4730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #9 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+AddressSanitizer can not provide additional info.
+SUMMARY: AddressSanitizer: SEGV (/lib64/libc.so.6+0x149f4f) in __memmove_avx_unaligned
+==19342==ABORTING
diff --git a/tests/fuzz-tests/images/bko-172811.raw.xz b/tests/fuzz-tests/images/bko-172811.raw.xz
new file mode 100644
index 00000000..08546c2b
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-172811.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-172861.raw.txt b/tests/fuzz-tests/images/bko-172861.raw.txt
new file mode 100644
index 00000000..f395333f
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-172861.raw.txt
@@ -0,0 +1,68 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=172861
+Lukas Lueg 2016-09-24 15:40:54 UTC
+
+More news from the fuzzer. The attached image causes a segmentation fault when
+running btrfsck over it; using btrfs-progs v4.7.2-55-g2b7c507
+
+The juicy parts:
+
+==12279==ERROR: AddressSanitizer: SEGV on unknown address 0x6210010719f9 (pc 0x0000005f30bd bp 0x7ffcf39cc670 sp 0x7ffcf39cc670 T0)
+ #0 0x5f30bc in btrfs_file_extent_type /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:2083:1
+ #1 0x5f2f49 in add_refs_for_leaf_items /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:664:17
+ #2 0x5f2ba9 in travel_tree /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:704:9
+ #3 0x5f2c0a in travel_tree /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:719:9
+ #4 0x5f299b in add_refs_for_implied /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:748:8
+ #5 0x5efd39 in map_implied_refs /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:766:9
+ #6 0x5eed89 in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:1366:8
+ #7 0x51ea14 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11571:9
+ #8 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #9 0x7f811e227730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #10 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+Extent back ref already exists for 0 parent 0 root 0
+Extent back ref already exists for 0 parent 0 root 0
+Extent back ref already exists for 0 parent 0 root 0
+Chunk[256, 228, 0]: length(4194304), offset(0), type(2) is not found in block group
+Chunk[256, 228, 0] stripe[1, 0] is not found in dev extent
+Chunk[256, 228, 4194304]: length(1638400), offset(4194304), type(5) is not found in block group
+Chunk[256, 228, 4194304] stripe[1, 4194304] is not found in dev extent
+Chunk[256, 228, 5832704]: length(1638400), offset(5832704), type(5) is not found in block group
+Chunk[256, 228, 5832704] stripe[1, 5832704] is not found in dev extent
+Chunk[256, 228, 7471104]: length(9306112), offset(7471104), type(5) is not found in block group
+Chunk[256, 228, 7471104] stripe[1, 7471104] is not found in dev extent
+ref mismatch on [0 4096] extent item 0, found 4
+Backref 0 parent 0 root 0 not found in extent tree
+Incorrect global backref count on 0 found 1 wanted 4
+backpointer mismatch on [0 4096]
+bad extent [0, 4096), type mismatch with chunk
+ref mismatch on [135168 4096] extent item 0, found 1
+Backref 135168 parent 3 root 3 not found in extent tree
+backpointer mismatch on [135168 4096]
+ref mismatch on [4202496 4096] extent item 0, found 1
+Backref 4202496 parent 1 root 1 not found in extent tree
+backpointer mismatch on [4202496 4096]
+Dev extent's total-byte(0) is not equal to byte-used(16777216) in dev[1, 216, 1]
+checking free space cache
+checking fs roots
+root 5 root dir 0 not found
+checking csums
+checking root refs
+checking quota groups
+ASAN:DEADLYSIGNAL
+=================================================================
+==12279==ERROR: AddressSanitizer: SEGV on unknown address 0x6210010719f9 (pc 0x0000005f30bd bp 0x7ffcf39cc670 sp 0x7ffcf39cc670 T0)
+ #0 0x5f30bc in btrfs_file_extent_type /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:2083:1
+ #1 0x5f2f49 in add_refs_for_leaf_items /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:664:17
+ #2 0x5f2ba9 in travel_tree /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:704:9
+ #3 0x5f2c0a in travel_tree /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:719:9
+ #4 0x5f299b in add_refs_for_implied /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:748:8
+ #5 0x5efd39 in map_implied_refs /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:766:9
+ #6 0x5eed89 in qgroup_verify_all /home/lukas/dev/btrfsfuzz/src-asan/qgroup-verify.c:1366:8
+ #7 0x51ea14 in cmd_check /home/lukas/dev/btrfsfuzz/src-asan/cmds-check.c:11571:9
+ #8 0x4f0ee1 in main /home/lukas/dev/btrfsfuzz/src-asan/btrfs.c:243:8
+ #9 0x7f811e227730 in __libc_start_main (/lib64/libc.so.6+0x20730)
+ #10 0x421358 in _start (/home/lukas/dev/btrfsfuzz/bin-asan/bin/btrfs+0x421358)
+
+AddressSanitizer can not provide additional info.
+SUMMARY: AddressSanitizer: SEGV /home/lukas/dev/btrfsfuzz/src-asan/./ctree.h:2083:1 in btrfs_file_extent_type
+==12279==ABORTING
diff --git a/tests/fuzz-tests/images/bko-172861.raw.xz b/tests/fuzz-tests/images/bko-172861.raw.xz
new file mode 100644
index 00000000..c57661d9
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-172861.raw.xz
Binary files differ
diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh
index eefe8a81..1c645c9b 100755
--- a/tests/misc-tests.sh
+++ b/tests/misc-tests.sh
@@ -3,13 +3,13 @@
# Misc tests
LANG=C
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+TOP=$(readlink -f "$SCRIPT_DIR/../")
TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/misc-tests-results.txt"
IMAGE="$TOP/tests/test.img"
-source $TOP/tests/common
+source "$TOP/tests/common"
export TOP
export RESULTS
@@ -17,27 +17,31 @@ export LANG
export TEST_DEV
export IMAGE
-rm -f $RESULTS
+rm -f "$RESULTS"
# test rely on corrupting blocks tool
check_prereq btrfs-corrupt-block
check_prereq btrfs-image
check_prereq btrfstune
check_prereq btrfs
+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 "$TOP/tests/misc-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
echo " [TEST/misc] $(basename $i)"
- cd $i
- echo "=== Entering $i" >> $RESULTS
+ cd "$i"
+ echo "=== Entering $i" >> "$RESULTS"
if [ -x test.sh ]; then
./test.sh
if [ $? -ne 0 ]; then
+ if [[ $TEST_LOG =~ dump ]]; then
+ cat "$RESULTS"
+ fi
_fail "test failed for case $(basename $i)"
fi
fi
- cd $TOP
+ cd "$TOP"
done
diff --git a/tests/misc-tests/001-btrfstune-features/test.sh b/tests/misc-tests/001-btrfstune-features/test.sh
index c858d701..bfa7f43e 100755
--- a/tests/misc-tests/001-btrfstune-features/test.sh
+++ b/tests/misc-tests/001-btrfstune-features/test.sh
@@ -3,8 +3,6 @@
source $TOP/tests/common
-check_prereq btrfs-debug-tree
-check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfstune
check_prereq btrfs
@@ -16,7 +14,7 @@ prepare_test_dev
# parameters:
# - option for mkfs.btrfs -O, empty for defaults
# - option for btrfstune
-# - string representing the feature in btrfs-show-super dump
+# - string representing the feature in dump-super output
test_feature()
{
local mkfsfeatures
@@ -28,12 +26,12 @@ test_feature()
sbflag="$3"
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $mkfsfeatures $TEST_DEV
- if run_check_stdout $TOP/btrfs-show-super $TEST_DEV | \
+ if run_check_stdout $TOP/btrfs inspect-internal dump-super $TEST_DEV | \
grep -q "$sbflag"; then
_fail "FAIL: feature $sbflag must not be set on the base image"
fi
run_check $TOP/btrfstune $tuneopt $TEST_DEV
- if ! run_check_stdout $TOP/btrfs-show-super $TEST_DEV | \
+ if ! run_check_stdout $TOP/btrfs inspect-internal dump-super $TEST_DEV | \
grep -q "$sbflag"; then
_fail "FAIL: feature $sbflag not set"
fi
diff --git a/tests/misc-tests/002-uuid-rewrite/test.sh b/tests/misc-tests/002-uuid-rewrite/test.sh
index d84ec6ca..fd100fb3 100755
--- a/tests/misc-tests/002-uuid-rewrite/test.sh
+++ b/tests/misc-tests/002-uuid-rewrite/test.sh
@@ -3,8 +3,6 @@
source $TOP/tests/common
-check_prereq btrfs-debug-tree
-check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfstune
check_prereq btrfs
@@ -15,7 +13,7 @@ get_fs_uuid() {
local image
image="$1"
- run_check_stdout $TOP/btrfs-show-super "$image" | \
+ run_check_stdout $TOP/btrfs inspect-internal dump-super "$image" | \
grep '^fsid' | awk '{print $2}'
}
@@ -29,13 +27,13 @@ test_uuid_random()
--uuid $origuuid \
--rootdir $TOP/Documentation \
$TEST_DEV
- run_check $TOP/btrfs-show-super "$TEST_DEV"
+ run_check $TOP/btrfs inspect-internal dump-super "$TEST_DEV"
currentfsid=$(run_check_stdout $TOP/btrfstune -f -u $TEST_DEV | \
grep -i 'current fsid:' | awk '{print $3}')
if ! [ $currentfsid = $origuuid ]; then
_fail "FAIL: current UUID mismatch"
fi
- run_check $TOP/btrfs-show-super "$TEST_DEV"
+ run_check $TOP/btrfs inspect-internal dump-super "$TEST_DEV"
run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
}
@@ -51,10 +49,10 @@ test_uuid_user()
--uuid $origuuid \
--rootdir $TOP/Documentation \
$TEST_DEV
- run_check $TOP/btrfs-show-super "$TEST_DEV"
+ run_check $TOP/btrfs inspect-internal dump-super "$TEST_DEV"
run_check $TOP/btrfstune -f -U $newuuid \
$TEST_DEV
- # btrfs-show-super is called within get_fs_uuid
+ # btrfs inspect-internal dump-super is called within get_fs_uuid
fsid=$(get_fs_uuid $TEST_DEV)
if ! [ $fsid = $newuuid ]; then
_fail "FAIL: UUID not rewritten"
diff --git a/tests/misc-tests/003-zero-log/test.sh b/tests/misc-tests/003-zero-log/test.sh
index b650930e..e7c5c806 100755
--- a/tests/misc-tests/003-zero-log/test.sh
+++ b/tests/misc-tests/003-zero-log/test.sh
@@ -3,7 +3,6 @@
source $TOP/tests/common
-check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfs
prepare_test_dev
@@ -13,14 +12,14 @@ get_log_root()
local image
image="$1"
- $TOP/btrfs-show-super "$image" | \
+ $TOP/btrfs inspect-internal dump-super "$image" | \
grep '^log_root\>' | awk '{print $2}'
}
get_log_root_level() {
local image
image="$1"
- $TOP/btrfs-show-super "$image" | \
+ $TOP/btrfs inspect-internal dump-super "$image" | \
grep '^log_root_level' | awk '{print $2}'
}
@@ -30,7 +29,7 @@ test_zero_log()
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
--rootdir $TOP/Documentation \
$TEST_DEV
- run_check $TOP/btrfs-show-super $TEST_DEV
+ run_check $TOP/btrfs inspect-internal dump-super $TEST_DEV
if [ "$1" = 'standalone' ]; then
run_check $TOP/btrfs rescue zero-log $TEST_DEV
else
@@ -44,7 +43,7 @@ test_zero_log()
if [ "$log_root_level" != 0 ]; then
_fail "FAIL: log_root_level not reset"
fi
- run_check $TOP/btrfs-show-super $TEST_DEV
+ run_check $TOP/btrfs inspect-internal dump-super $TEST_DEV
run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
}
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 b22a95d7..5b6fe065 100755
--- a/tests/misc-tests/006-image-on-missing-device/test.sh
+++ b/tests/misc-tests/006-image-on-missing-device/test.sh
@@ -6,7 +6,6 @@
source $TOP/tests/common
-check_prereq btrfs-show-super
check_prereq btrfs-image
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -40,7 +39,7 @@ cleanup_devices()
for i in `seq $ndevs`; do
truncate -s0 img$i
done
- run_check $SUDO_HELPER losetup --list
+ run_check $SUDO_HELPER losetup --all
}
test_image_dump()
@@ -61,12 +60,12 @@ test_run()
run_check $SUDO_HELPER umount $TEST_MNT
test_image_dump
- run_check $TOP/btrfs filesystem show $dev1
+ run_check $SUDO_HELPER $TOP/btrfs filesystem show $dev1
# create a degraded raid1 filesystem, check must succeed
# btrfs-image must not loop
run_mayfail wipefs -a $dev2
run_check $SUDO_HELPER losetup -d $dev2
- run_check $TOP/btrfs filesystem show $dev1
+ run_check $SUDO_HELPER $TOP/btrfs filesystem show $dev1
test_image_dump
}
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 451e453a..7915867c 100755
--- a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
+++ b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
@@ -6,7 +6,6 @@
source $TOP/tests/common
check_prereq btrfs-convert
-check_prereq btrfs-debug-tree
check_prereq btrfs
setup_root_helper
@@ -15,11 +14,11 @@ prepare_test_dev
run_check truncate -s 2G "$TEST_DEV"
run_check mkfs.ext4 -F "$TEST_DEV"
run_check $TOP/btrfs-convert "$TEST_DEV"
-run_check $TOP/btrfs-debug-tree "$TEST_DEV"
+run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-tree "$TEST_DEV"
run_check_mount_test_dev
run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c "$TEST_MNT/ext2_saved"
run_check_umount_test_dev
-run_check $TOP/btrfs-debug-tree "$TEST_DEV"
+run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-tree "$TEST_DEV"
run_check_stdout $TOP/btrfs-convert --rollback "$TEST_DEV" |
grep -q 'is it deleted' || _fail "unexpected rollback"
diff --git a/tests/misc-tests/011-delete-missing-device/test.sh b/tests/misc-tests/011-delete-missing-device/test.sh
index 26645f10..5b5f9786 100755
--- a/tests/misc-tests/011-delete-missing-device/test.sh
+++ b/tests/misc-tests/011-delete-missing-device/test.sh
@@ -3,7 +3,6 @@
source $TOP/tests/common
-check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -33,22 +32,22 @@ cleanup_devices()
for i in `seq $ndevs`; do
truncate -s0 img$i
done
- run_check $SUDO_HELPER losetup --list
+ run_check $SUDO_HELPER losetup --all
}
test_do_mkfs()
{
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $@ ${devs[@]}
- run_check $TOP/btrfs-show-super $dev1
+ run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $dev1
run_check $SUDO_HELPER $TOP/btrfs check $dev1
- run_check $TOP/btrfs filesystem show
+ run_check $SUDO_HELPER $TOP/btrfs filesystem show
}
test_wipefs()
{
- run_check wipefs -a $devtodel
+ run_check $SUDO_HELPER wipefs -a $devtodel
run_check $SUDO_HELPER losetup -d $devtodel
- run_check losetup -a
+ run_check $SUDO_HELPER losetup --all
run_check $TOP/btrfs filesystem show
}
test_delete_missing()
diff --git a/tests/misc-tests/016-send-clone-src/multi-clone-src-v4.8.2.stream.xz b/tests/misc-tests/016-send-clone-src/multi-clone-src-v4.8.2.stream.xz
new file mode 100644
index 00000000..34c14ed5
--- /dev/null
+++ b/tests/misc-tests/016-send-clone-src/multi-clone-src-v4.8.2.stream.xz
Binary files differ
diff --git a/tests/misc-tests/016-send-clone-src/test.sh b/tests/misc-tests/016-send-clone-src/test.sh
new file mode 100755
index 00000000..e256eef9
--- /dev/null
+++ b/tests/misc-tests/016-send-clone-src/test.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# test for sending stream size of clone-src option, compare against a send
+# stream generated by buggy version
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev 1g
+
+run_check $TOP/mkfs.btrfs -f $IMAGE
+run_check_mount_test_dev
+
+here=`pwd`
+cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
+
+run_check $SUDO_HELPER $TOP/btrfs subvolume create subv-parent1
+for i in 1 2 3; do
+ run_check $SUDO_HELPER dd if=/dev/zero of=subv-parent1/file1_$i bs=1M count=1
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv-parent1 subv-snap1_$i
+done
+
+run_check $SUDO_HELPER $TOP/btrfs subvolume create subv-parent2
+for i in 1 2 3; do
+ run_check $SUDO_HELPER dd if=/dev/zero of=subv-parent2/file2_$i bs=1M count=1
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv-parent2 subv-snap2_$i
+done
+
+truncate -s0 "$here"/send-stream.img
+chmod a+w "$here"/send-stream.img
+run_check $SUDO_HELPER $TOP/btrfs send -f "$here"/send-stream.img \
+ -c subv-snap1_1 -c subv-snap2_1 subv-snap1_[23] subv-snap2_[23]
+
+image=$(extract_image "$here"/multi-clone-src-v4.8.2.stream.xz)
+old_stream_size=`stat --format=%s "$image"`
+stream_size=`stat --format=%s "$here"/send-stream.img`
+
+if [ $old_stream_size -lt $stream_size ]; then
+ run_check ls -l "$image" "$here"/send-stream.img
+ _fail "sending stream size is bigger than old stream"
+fi
+
+run_check rm -f -- "$image" "$here"/send-stream.img
+
+cd "$here" || _fail "cannot chdir back to test directory"
+
+run_check_umount_test_dev
diff --git a/tests/misc-tests/017-recv-stream-malformatted/test.sh b/tests/misc-tests/017-recv-stream-malformatted/test.sh
new file mode 100755
index 00000000..884b7d42
--- /dev/null
+++ b/tests/misc-tests/017-recv-stream-malformatted/test.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# test receiving stream that's not valid, simple cases
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev 1g
+
+run_check $TOP/mkfs.btrfs -f $IMAGE
+run_check_mount_test_dev
+
+echo -n '' | run_mayfail $SUDO_HELPER "$TOP/btrfs" receive "$TEST_MNT" &&
+ _fail "unexpected: received empty stream"
+
+echo -n '1' | run_mayfail $SUDO_HELPER "$TOP/btrfs" receive "$TEST_MNT" &&
+ _fail "unexpected: received stream with shrot and corrupted header"
+
+echo -n '12345678901234567' | run_mayfail $SUDO_HELPER "$TOP/btrfs" receive "$TEST_MNT" &&
+ _fail "unexpected: received stream with corrupted header"
+
+run_check_umount_test_dev
diff --git a/tests/misc-tests/018-recv-end-of-stream/test.sh b/tests/misc-tests/018-recv-end-of-stream/test.sh
new file mode 100755
index 00000000..d39683e9
--- /dev/null
+++ b/tests/misc-tests/018-recv-end-of-stream/test.sh
@@ -0,0 +1,149 @@
+#!/bin/bash
+#
+# 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
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev 1g
+
+here=`pwd`
+
+test_full_empty_stream() {
+ local str
+
+ str="$here/stream-full-empty.stream"
+ run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check_mount_test_dev
+
+ cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
+
+ run_check $SUDO_HELPER $TOP/btrfs subvolume create subv1
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv1-snap
+
+ truncate -s0 "$str"
+ chmod a+w "$str"
+ run_check $SUDO_HELPER $TOP/btrfs send -f "$str" subv1-snap
+
+ cd "$here" || _fail "cannot chdir back to test directory"
+ run_check_umount_test_dev
+
+ run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check_mount_test_dev
+ run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$str" "$TEST_MNT"
+ run_check_umount_test_dev
+
+ run_check rm -f -- "$str"
+}
+
+test_full_simple_stream() {
+ local str
+
+ str="$here/stream-full-simple.stream"
+ run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check_mount_test_dev
+
+ cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
+
+ run_check $SUDO_HELPER $TOP/btrfs subvolume create subv1
+ for i in 1 2 3; do
+ run_check $SUDO_HELPER dd if=/dev/zero of=subv1/file1_$i bs=1M count=1
+ done
+
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv1-snap
+
+ truncate -s0 "$str"
+ chmod a+w "$str"
+ run_check $SUDO_HELPER $TOP/btrfs send -f "$str" subv1-snap
+
+ cd "$here" || _fail "cannot chdir back to test directory"
+ run_check_umount_test_dev
+
+ run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check_mount_test_dev
+ run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$str" "$TEST_MNT"
+ run_check_umount_test_dev
+
+ run_check rm -f -- "$str"
+}
+
+test_incr_empty_stream() {
+ local fstr
+ local istr
+
+ fstr="$here/stream-full-empty.stream"
+ istr="$here/stream-incr-empty.stream"
+ run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check_mount_test_dev
+
+ cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
+
+ run_check $SUDO_HELPER $TOP/btrfs subvolume create subv1
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv1-snap
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv2-snap
+
+ truncate -s0 "$fstr" "$istr"
+ chmod a+w "$fstr" "$istr"
+ run_check $SUDO_HELPER $TOP/btrfs send -f "$fstr" subv1-snap
+ run_check $SUDO_HELPER $TOP/btrfs send -p subv1-snap -f "$istr" subv2-snap
+
+ cd "$here" || _fail "cannot chdir back to test directory"
+ run_check_umount_test_dev
+
+ run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check_mount_test_dev
+ run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$fstr" "$TEST_MNT"
+ run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$istr" "$TEST_MNT"
+ run_check_umount_test_dev
+
+ run_check rm -f -- "$fstr" "$istr"
+}
+
+test_incr_simple_stream() {
+ local str
+
+ fstr="$here/stream-full-simple.stream"
+ istr="$here/stream-incr-simple.stream"
+ run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check_mount_test_dev
+
+ cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
+
+ run_check $SUDO_HELPER $TOP/btrfs subvolume create subv1
+ for i in 1 2 3; do
+ run_check $SUDO_HELPER dd if=/dev/zero of=subv1/file1_$i bs=1M count=1
+ done
+
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv1-snap
+
+ for i in 1 2 3; do
+ run_check $SUDO_HELPER dd if=/dev/urandom of=subv1/file1_$i bs=1M count=1
+ done
+
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv2-snap
+
+ truncate -s0 "$fstr" "$istr"
+ chmod a+w "$fstr" "$istr"
+ run_check $SUDO_HELPER $TOP/btrfs send -f "$fstr" subv1-snap
+ run_check $SUDO_HELPER $TOP/btrfs send -p subv1-snap -f "$istr" subv2-snap
+
+ cd "$here" || _fail "cannot chdir back to test directory"
+ run_check_umount_test_dev
+
+ run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check_mount_test_dev
+ run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$fstr" "$TEST_MNT"
+ run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$istr" "$TEST_MNT"
+ run_check_umount_test_dev
+
+ run_check rm -f -- "$fstr" "$istr"
+}
+
+test_full_empty_stream
+test_full_simple_stream
+test_incr_empty_stream
+test_incr_simple_stream
diff --git a/tests/mkfs-tests.sh b/tests/mkfs-tests.sh
index 1afc0282..c8ff8c83 100755
--- a/tests/mkfs-tests.sh
+++ b/tests/mkfs-tests.sh
@@ -3,13 +3,13 @@
# mkfs.btrfs tests
LANG=C
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+TOP=$(readlink -f "$SCRIPT_DIR/../")
TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/mkfs-tests-results.txt"
IMAGE="$TOP/tests/test.img"
-source $TOP/tests/common
+source "$TOP/tests/common"
export TOP
export RESULTS
@@ -17,24 +17,28 @@ export LANG
export IMAGE
export TEST_DEV
-rm -f $RESULTS
+rm -f "$RESULTS"
check_prereq mkfs.btrfs
check_prereq btrfs
+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 "$TOP/tests/mkfs-tests" -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
echo " [TEST/mkfs] $(basename $i)"
- cd $i
- echo "=== Entering $i" >> $RESULTS
+ cd "$i"
+ echo "=== Entering $i" >> "$RESULTS"
if [ -x test.sh ]; then
./test.sh
if [ $? -ne 0 ]; then
+ if [[ $TEST_LOG =~ dump ]]; then
+ cat "$RESULTS"
+ fi
_fail "test failed for case $(basename $i)"
fi
fi
- cd $TOP
+ cd "$TOP"
done
diff --git a/tests/mkfs-tests/001-basic-profiles/test.sh b/tests/mkfs-tests/001-basic-profiles/test.sh
index a6769214..0dc9a2bd 100755
--- a/tests/mkfs-tests/001-basic-profiles/test.sh
+++ b/tests/mkfs-tests/001-basic-profiles/test.sh
@@ -4,7 +4,6 @@
source $TOP/tests/common
-check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -33,12 +32,12 @@ cleanup_devices()
for i in `seq $ndevs`; do
truncate -s0 img$i
done
- run_check $SUDO_HELPER losetup --list
+ run_check $SUDO_HELPER losetup --all
}
test_get_info()
{
- run_check $TOP/btrfs-show-super $dev1
+ run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $dev1
run_check $SUDO_HELPER $TOP/btrfs check $dev1
run_check $SUDO_HELPER mount $dev1 $TEST_MNT
run_check $TOP/btrfs filesystem df $TEST_MNT
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 cff495e6..63fb1785 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
@@ -4,7 +4,6 @@
source $TOP/tests/common
check_prereq mkfs.btrfs
-check_prereq btrfs-show-super
setup_root_helper
prepare_test_dev
@@ -33,7 +32,7 @@ run_check cat $rot
# test
run_check_stdout $SUDO_HELPER $TOP/mkfs.btrfs -f $@ $dmdev |
grep -q 'SSD detected:.*yes' || _fail 'SSD not detected'
-run_check $TOP/btrfs-show-super $dmdev
+run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $dmdev
# cleanup
run_check $SUDO_HELPER dmsetup remove $dmname
diff --git a/tests/mkfs-tests/006-partitioned-loopdev/test.sh b/tests/mkfs-tests/006-partitioned-loopdev/test.sh
index 7c9fb829..12f37842 100755
--- a/tests/mkfs-tests/006-partitioned-loopdev/test.sh
+++ b/tests/mkfs-tests/006-partitioned-loopdev/test.sh
@@ -3,8 +3,12 @@
source $TOP/tests/common
+if ! losetup --help | grep -q 'partscan'; then
+ _not_run "losetup --partscan not available"
+ exit 0
+fi
+
check_prereq mkfs.btrfs
-check_prereq btrfs-show-super
setup_root_helper
@@ -19,7 +23,7 @@ base=$(basename $loopdev)
# expect partitions named like loop0p1 etc
for looppart in $(ls /dev/$base?*); do
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $looppart
- run_check $TOP/btrfs-show-super $looppart
+ run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $looppart
done
# cleanup
diff --git a/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
index d5374cbd..3980414f 100755
--- a/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
+++ b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
@@ -3,7 +3,6 @@
source $TOP/tests/common
-check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -13,7 +12,7 @@ prepare_test_dev
test_mkfs_single()
{
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f "$@" $TEST_DEV
- run_check $TOP/btrfs-show-super $TEST_DEV
+ run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $TEST_DEV
run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
}
diff --git a/tests/scan-results.sh b/tests/scan-results.sh
new file mode 100755
index 00000000..f3ebcbc4
--- /dev/null
+++ b/tests/scan-results.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# look for some error messages in all test logs
+
+for i in *.txt; do
+ echo "Scanning $i"
+ last=
+ while read line; do
+ case "$line" in
+ ===\ Entering*) last="$line" ;;
+ *Assertion*failed*) echo "ASSERTION FAILED: $last" ;;
+ *runtime\ error*) echo "RUNTIME ERROR (sanitizer): $last" ;;
+ *AddressSanitizer*heap-use-after-free*) echo "RUNTIME ERROR (use after free): $last" ;;
+ *Warning:\ assertion*failed*) echo "ASSERTION WARNING: $last" ;;
+ *) : ;;
+ esac
+ done < "$i"
+done
diff --git a/tests/test-console.sh b/tests/test-console.sh
index 365cc971..779e541f 100755
--- a/tests/test-console.sh
+++ b/tests/test-console.sh
@@ -2,8 +2,8 @@
# a shell with test environment set up, logged commands and output
LANG=C
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+TOP=$(readlink -f "$SCRIPT_DIR/../")
TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/test-console.txt"
IMAGE="$TOP/tests/test.img"
diff --git a/utils-lib.c b/utils-lib.c
index 79ef35e3..044f93fc 100644
--- a/utils-lib.c
+++ b/utils-lib.c
@@ -2,9 +2,13 @@
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
+#include <sys/ioctl.h>
+#include <ioctl.h>
#if BTRFS_FLAT_INCLUDES
+#include "ctree.h"
#else
+#include <btrfs/ctree.h>
#endif /* BTRFS_FLAT_INCLUDES */
/*
@@ -38,3 +42,29 @@ u64 arg_strtou64(const char *str)
}
return value;
}
+
+/*
+ * For a given:
+ * - file or directory return the containing tree root id
+ * - subvolume return its own tree id
+ * - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is
+ * undefined and function returns -1
+ */
+int lookup_path_rootid(int fd, u64 *rootid)
+{
+ struct btrfs_ioctl_ino_lookup_args args;
+ int ret;
+
+ memset(&args, 0, sizeof(args));
+ args.treeid = 0;
+ args.objectid = BTRFS_FIRST_FREE_OBJECTID;
+
+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+ if (ret < 0)
+ return -errno;
+
+ *rootid = args.treeid;
+
+ return 0;
+}
+
diff --git a/utils.c b/utils.c
index 45fde2e9..5a481976 100644
--- a/utils.c
+++ b/utils.c
@@ -38,6 +38,8 @@
#include <sys/statfs.h>
#include <linux/magic.h>
#include <getopt.h>
+#include <sys/utsname.h>
+#include <linux/version.h>
#include "kerncompat.h"
#include "radix-tree.h"
@@ -192,7 +194,7 @@ static int reserve_free_space(struct cache_tree *free_tree, u64 len,
struct cache_extent *cache;
int found = 0;
- BUG_ON(!ret_start);
+ ASSERT(ret_start != NULL);
cache = first_cache_extent(free_tree);
while (cache) {
if (cache->size > len) {
@@ -223,7 +225,7 @@ static inline int write_temp_super(int fd, struct btrfs_super_block *sb,
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, (char *)&sb->csum[0]);
+ btrfs_csum_final(crc, &sb->csum[0]);
ret = pwrite(fd, sb, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
if (ret < BTRFS_SUPER_INFO_SIZE)
ret = (ret < 0 ? -errno : -EIO);
@@ -405,8 +407,23 @@ static int setup_temp_root_tree(int fd, struct btrfs_mkfs_config *cfg,
* Provided bytenr must in ascending order, or tree root will have a
* bad key order.
*/
- BUG_ON(!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr &&
- dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr));
+ if (!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr &&
+ dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr)) {
+ error("bad tree bytenr order: "
+ "root < extent %llu < %llu, "
+ "extent < dev %llu < %llu, "
+ "dev < fs %llu < %llu, "
+ "fs < csum %llu < %llu",
+ (unsigned long long)root_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)csum_bytenr);
+ return -EINVAL;
+ }
buf = malloc(sizeof(*buf) + cfg->nodesize);
if (!buf)
return -ENOMEM;
@@ -543,14 +560,18 @@ static int insert_temp_chunk_item(int fd, struct extent_buffer *buf,
*/
if (type & BTRFS_BLOCK_GROUP_SYSTEM) {
char *cur;
+ u32 array_size;
- cur = (char *)sb->sys_chunk_array + sb->sys_chunk_array_size;
+ cur = (char *)sb->sys_chunk_array
+ + btrfs_super_sys_array_size(sb);
memcpy(cur, &disk_key, sizeof(disk_key));
cur += sizeof(disk_key);
read_extent_buffer(buf, cur, (unsigned long int)chunk,
btrfs_chunk_item_size(1));
- sb->sys_chunk_array_size += btrfs_chunk_item_size(1) +
+ array_size = btrfs_super_sys_array_size(sb);
+ array_size += btrfs_chunk_item_size(1) +
sizeof(disk_key);
+ btrfs_set_super_sys_array_size(sb, array_size);
ret = write_temp_super(fd, sb, cfg->super_bytenr);
}
@@ -567,7 +588,12 @@ static int setup_temp_chunk_tree(int fd, struct btrfs_mkfs_config *cfg,
int ret;
/* Must ensure SYS chunk starts before META chunk */
- BUG_ON(meta_chunk_start < sys_chunk_start);
+ if (meta_chunk_start < sys_chunk_start) {
+ error("wrong chunk order: meta < system %llu < %llu",
+ (unsigned long long)meta_chunk_start,
+ (unsigned long long)sys_chunk_start);
+ return -EINVAL;
+ }
buf = malloc(sizeof(*buf) + cfg->nodesize);
if (!buf)
return -ENOMEM;
@@ -633,7 +659,12 @@ static int setup_temp_dev_tree(int fd, struct btrfs_mkfs_config *cfg,
int ret;
/* Must ensure SYS chunk starts before META chunk */
- BUG_ON(meta_chunk_start < sys_chunk_start);
+ if (meta_chunk_start < sys_chunk_start) {
+ error("wrong chunk order: meta < system %llu < %llu",
+ (unsigned long long)meta_chunk_start,
+ (unsigned long long)sys_chunk_start);
+ return -EINVAL;
+ }
buf = malloc(sizeof(*buf) + cfg->nodesize);
if (!buf)
return -ENOMEM;
@@ -829,9 +860,27 @@ static int setup_temp_extent_tree(int fd, struct btrfs_mkfs_config *cfg,
* We must ensure provided bytenr are in ascending order,
* or extent tree key order will be broken.
*/
- BUG_ON(!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr &&
- extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr &&
- fs_bytenr < csum_bytenr));
+ if (!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr &&
+ extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr &&
+ fs_bytenr < csum_bytenr)) {
+ error("bad tree bytenr order: "
+ "chunk < root %llu < %llu, "
+ "root < extent %llu < %llu, "
+ "extent < dev %llu < %llu, "
+ "dev < fs %llu < %llu, "
+ "fs < csum %llu < %llu",
+ (unsigned long long)chunk_bytenr,
+ (unsigned long long)root_bytenr,
+ (unsigned long long)root_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)csum_bytenr);
+ return -EINVAL;
+ }
buf = malloc(sizeof(*buf) + cfg->nodesize);
if (!buf)
return -ENOMEM;
@@ -894,7 +943,7 @@ out:
* Split into small blocks and reuse codes.
* TODO: Reuse tree operation facilities by introducing new flags
*/
-static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
struct btrfs_convert_context *cctx)
{
struct cache_tree *free = &cctx->free;
@@ -1003,8 +1052,7 @@ out:
* The superblock signature is not valid, denotes a partially created
* filesystem, needs to be finalized.
*/
-int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
- struct btrfs_convert_context *cctx)
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
{
struct btrfs_super_block super;
struct extent_buffer *buf;
@@ -1029,8 +1077,6 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
u64 num_bytes;
- if (cctx)
- return make_convert_btrfs(fd, cfg, cctx);
buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
if (!buf)
return -ENOMEM;
@@ -1054,8 +1100,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
}
} else {
uuid_generate(super.fsid);
- if (cfg->fs_uuid)
- uuid_unparse(super.fsid, cfg->fs_uuid);
+ uuid_unparse(super.fsid, cfg->fs_uuid);
}
uuid_generate(super.dev_item.uuid);
uuid_generate(chunk_tree_uuid);
@@ -1176,8 +1221,21 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
if (!skinny_metadata)
item_size += sizeof(struct btrfs_tree_block_info);
- BUG_ON(cfg->blocks[i] < first_free);
- BUG_ON(cfg->blocks[i] < cfg->blocks[i - 1]);
+ if (cfg->blocks[i] < first_free) {
+ error("block[%d] below first free: %llu < %llu",
+ i, (unsigned long long)cfg->blocks[i],
+ (unsigned long long)first_free);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (cfg->blocks[i] < cfg->blocks[i - 1]) {
+ error("blocks %d and %d in reverse order: %llu < %llu",
+ i, i - 1,
+ (unsigned long long)cfg->blocks[i],
+ (unsigned long long)cfg->blocks[i - 1]);
+ ret = -EINVAL;
+ goto out;
+ }
/* create extent item */
itemoff -= item_size;
@@ -1377,7 +1435,6 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
}
/* and write out the super block */
- BUG_ON(sizeof(super) > cfg->sectorsize);
memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
memcpy(buf->data, &super, sizeof(super));
buf->len = BTRFS_SUPER_INFO_SIZE;
@@ -1395,20 +1452,65 @@ out:
return ret;
}
+#define VERSION_TO_STRING3(a,b,c) #a "." #b "." #c, KERNEL_VERSION(a,b,c)
+#define VERSION_TO_STRING2(a,b) #a "." #b, KERNEL_VERSION(a,b,0)
+
+/*
+ * Feature stability status and versions: compat <= safe <= default
+ */
static const struct btrfs_fs_feature {
const char *name;
u64 flag;
+ const char *sysfs_name;
+ /*
+ * Compatibility with kernel of given version. Filesystem can be
+ * mounted.
+ */
+ const char *compat_str;
+ u32 compat_ver;
+ /*
+ * Considered safe for use, but is not on by default, even if the
+ * kernel supports the feature.
+ */
+ const char *safe_str;
+ u32 safe_ver;
+ /*
+ * Considered safe for use and will be turned on by default if
+ * supported by the running kernel.
+ */
+ const char *default_str;
+ u32 default_ver;
const char *desc;
} mkfs_features[] = {
{ "mixed-bg", BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS,
+ "mixed_groups",
+ VERSION_TO_STRING3(2,6,37),
+ VERSION_TO_STRING3(2,6,37),
+ NULL, 0,
"mixed data and metadata block groups" },
{ "extref", BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF,
+ "extended_iref",
+ VERSION_TO_STRING2(3,7),
+ VERSION_TO_STRING2(3,12),
+ VERSION_TO_STRING2(3,12),
"increased hardlink limit per file to 65536" },
{ "raid56", BTRFS_FEATURE_INCOMPAT_RAID56,
+ "raid56",
+ VERSION_TO_STRING2(3,9),
+ NULL, 0,
+ NULL, 0,
"raid56 extended format" },
{ "skinny-metadata", BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA,
+ "skinny_metadata",
+ VERSION_TO_STRING2(3,10),
+ VERSION_TO_STRING2(3,18),
+ VERSION_TO_STRING2(3,18),
"reduced-size metadata extent refs" },
{ "no-holes", BTRFS_FEATURE_INCOMPAT_NO_HOLES,
+ "no_holes",
+ VERSION_TO_STRING2(3,14),
+ VERSION_TO_STRING2(4,0),
+ NULL, 0,
"no explicit hole extents for files" },
/* Keep this one last */
{ "list-all", BTRFS_FEATURE_LIST_ALL, NULL }
@@ -1467,17 +1569,19 @@ void btrfs_list_all_fs_features(u64 mask_disallowed)
fprintf(stderr, "Filesystem features available:\n");
for (i = 0; i < ARRAY_SIZE(mkfs_features) - 1; i++) {
- char *is_default = "";
+ const struct btrfs_fs_feature *feat = &mkfs_features[i];
- if (mkfs_features[i].flag & mask_disallowed)
+ if (feat->flag & mask_disallowed)
continue;
- if (mkfs_features[i].flag & BTRFS_MKFS_DEFAULT_FEATURES)
- is_default = ", default";
- fprintf(stderr, "%-20s- %s (0x%llx%s)\n",
- mkfs_features[i].name,
- mkfs_features[i].desc,
- mkfs_features[i].flag,
- is_default);
+ fprintf(stderr, "%-20s- %s (0x%llx", feat->name, feat->desc,
+ feat->flag);
+ if (feat->compat_ver)
+ fprintf(stderr, ", compat=%s", feat->compat_str);
+ if (feat->safe_ver)
+ fprintf(stderr, ", safe=%s", feat->safe_str);
+ if (feat->default_ver)
+ fprintf(stderr, ", default=%s", feat->default_str);
+ fprintf(stderr, ")\n");
}
}
@@ -1500,6 +1604,53 @@ char* btrfs_parse_fs_features(char *namelist, u64 *flags)
return NULL;
}
+void print_kernel_version(FILE *stream, u32 version)
+{
+ u32 v[3];
+
+ v[0] = version & 0xFF;
+ v[1] = (version >> 8) & 0xFF;
+ v[2] = version >> 16;
+ fprintf(stream, "%u.%u", v[2], v[1]);
+ if (v[0])
+ fprintf(stream, ".%u", v[0]);
+}
+
+u32 get_running_kernel_version(void)
+{
+ struct utsname utsbuf;
+ char *tmp;
+ char *saveptr = NULL;
+ u32 version;
+
+ uname(&utsbuf);
+ if (strcmp(utsbuf.sysname, "Linux") != 0) {
+ error("unsupported system: %s", utsbuf.sysname);
+ exit(1);
+ }
+ /* 1.2.3-4-name */
+ tmp = strchr(utsbuf.release, '-');
+ if (tmp)
+ *tmp = 0;
+
+ tmp = strtok_r(utsbuf.release, ".", &saveptr);
+ if (!string_is_numerical(tmp))
+ return (u32)-1;
+ version = atoi(tmp) << 16;
+ tmp = strtok_r(NULL, ".", &saveptr);
+ if (!string_is_numerical(tmp))
+ return (u32)-1;
+ version |= atoi(tmp) << 8;
+ tmp = strtok_r(NULL, ".", &saveptr);
+ if (tmp) {
+ if (!string_is_numerical(tmp))
+ return (u32)-1;
+ version |= atoi(tmp);
+ }
+
+ return version;
+}
+
u64 btrfs_device_size(int fd, struct stat *st)
{
u64 size;
@@ -1551,7 +1702,7 @@ static int zero_dev_clamped(int fd, off_t start, ssize_t len, u64 dev_size)
}
int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, int fd, char *path,
+ struct btrfs_root *root, int fd, const char *path,
u64 device_total_bytes, u32 io_width, u32 io_align,
u32 sectorsize)
{
@@ -1566,13 +1717,16 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
device_total_bytes = (device_total_bytes / sectorsize) * sectorsize;
- device = kzalloc(sizeof(*device), GFP_NOFS);
- if (!device)
- goto err_nomem;
- buf = kzalloc(sectorsize, GFP_NOFS);
- if (!buf)
- goto err_nomem;
- BUG_ON(sizeof(*disk_super) > sectorsize);
+ device = calloc(1, sizeof(*device));
+ if (!device) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ buf = calloc(1, sectorsize);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
disk_super = (struct btrfs_super_block *)buf;
dev_item = &disk_super->dev_item;
@@ -1590,12 +1744,15 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
device->total_ios = 0;
device->dev_root = root->fs_info->dev_root;
device->name = strdup(path);
- if (!device->name)
- goto err_nomem;
+ if (!device->name) {
+ ret = -ENOMEM;
+ goto out;
+ }
INIT_LIST_HEAD(&device->dev_list);
ret = btrfs_add_device(trans, root, device);
- BUG_ON(ret);
+ if (ret)
+ goto out;
fs_total_bytes = btrfs_super_total_bytes(super) + device_total_bytes;
btrfs_set_super_total_bytes(super, fs_total_bytes);
@@ -1618,15 +1775,15 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET);
BUG_ON(ret != sectorsize);
- kfree(buf);
+ free(buf);
list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
device->fs_devices = root->fs_info->fs_devices;
return 0;
-err_nomem:
- kfree(device);
- kfree(buf);
- return -ENOMEM;
+out:
+ free(device);
+ free(buf);
+ return ret;
}
static int btrfs_wipe_existing_sb(int fd)
@@ -1709,8 +1866,8 @@ int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret,
*/
if (discard_range(fd, 0, 0) == 0) {
if (opflags & PREP_DEVICE_VERBOSE)
- printf("Performing full device TRIM (%s) ...\n",
- pretty_size(block_count));
+ printf("Performing full device TRIM %s (%s) ...\n",
+ file, pretty_size(block_count));
discard_blocks(fd, 0, block_count);
}
}
@@ -1757,7 +1914,7 @@ int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
btrfs_set_stack_timespec_nsec(&inode_item.ctime, 0);
btrfs_set_stack_timespec_sec(&inode_item.mtime, now);
btrfs_set_stack_timespec_nsec(&inode_item.mtime, 0);
- btrfs_set_stack_timespec_sec(&inode_item.otime, 0);
+ btrfs_set_stack_timespec_sec(&inode_item.otime, now);
btrfs_set_stack_timespec_nsec(&inode_item.otime, 0);
if (root->fs_info->tree_root == root)
@@ -2275,7 +2432,7 @@ int check_mounted_where(int fd, const char *file, char *where, int size,
/* scan other devices */
if (is_btrfs && total_devs > 1) {
- ret = btrfs_scan_lblkid();
+ ret = btrfs_scan_devices();
if (ret)
return ret;
}
@@ -2355,7 +2512,7 @@ int btrfs_register_one_device(const char *fname)
/*
* Register all devices in the fs_uuid list created in the user
- * space. Ensure btrfs_scan_lblkid() is called before this func.
+ * space. Ensure btrfs_scan_devices() is called before this func.
*/
int btrfs_register_all_devices(void)
{
@@ -2446,12 +2603,19 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod
int mult = 0;
const char** suffix = NULL;
u64 last_size;
+ int negative;
if (str_size == 0)
return 0;
+ negative = !!(unit_mode & UNITS_NEGATIVE);
+ unit_mode &= ~UNITS_NEGATIVE;
+
if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_RAW) {
- snprintf(str, str_size, "%llu", size);
+ if (negative)
+ snprintf(str, str_size, "%lld", size);
+ else
+ snprintf(str, str_size, "%llu", size);
return 0;
}
@@ -2486,10 +2650,22 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod
num_divs = 0;
break;
default:
- while (size >= mult) {
- last_size = size;
- size /= mult;
- num_divs++;
+ if (negative) {
+ s64 ssize = (s64)size;
+ s64 last_ssize = ssize;
+
+ while ((ssize < 0 ? -ssize : ssize) >= mult) {
+ last_ssize = ssize;
+ ssize /= mult;
+ num_divs++;
+ }
+ last_size = (u64)last_ssize;
+ } else {
+ while (size >= mult) {
+ last_size = size;
+ size /= mult;
+ num_divs++;
+ }
}
/*
* If the value is smaller than base, we didn't do any
@@ -2507,7 +2683,12 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod
assert(0);
return -1;
}
- fraction = (float)last_size / base;
+
+ if (negative) {
+ fraction = (float)(s64)last_size / base;
+ } else {
+ fraction = (float)last_size / base;
+ }
return snprintf(str, str_size, "%.2f%s", fraction, suffix[num_divs]);
}
@@ -2818,7 +2999,7 @@ path:
fd = open(p, O_RDONLY);
if (fd < 0)
goto err;
- ret = lookup_ino_rootid(fd, &id);
+ ret = lookup_path_rootid(fd, &id);
if (ret)
error("failed to lookup root id: %s", strerror(-ret));
close(fd);
@@ -2970,13 +3151,13 @@ again:
*
* Returns 0 on success, or a negative errno.
*/
-int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
+int get_fs_info(const char *path, struct btrfs_ioctl_fs_info_args *fi_args,
struct btrfs_ioctl_dev_info_args **di_ret)
{
int fd = -1;
int ret = 0;
int ndevs = 0;
- int i = 0;
+ u64 last_devid = 0;
int replacing = 0;
struct btrfs_fs_devices *fs_devices_mnt = NULL;
struct btrfs_ioctl_dev_info_args *di_args;
@@ -2989,7 +3170,6 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
if (is_block_device(path) == 1) {
struct btrfs_super_block *disk_super;
char buf[BTRFS_SUPER_INFO_SIZE];
- u64 devid;
/* Ensure it's mounted, then set path to the mountpoint */
fd = open(path, O_RDONLY);
@@ -3017,10 +3197,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
ret = -EIO;
goto out;
}
- devid = btrfs_stack_device_id(&disk_super->dev_item);
-
- fi_args->max_id = devid;
- i = devid;
+ last_devid = btrfs_stack_device_id(&disk_super->dev_item);
+ fi_args->max_id = last_devid;
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
close(fd);
@@ -3057,8 +3235,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
fi_args->num_devices++;
ndevs++;
replacing = 1;
- if (i == 0)
- i++;
+ if (last_devid == 0)
+ last_devid++;
}
}
@@ -3073,8 +3251,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
if (replacing)
memcpy(di_args, &tmp, sizeof(tmp));
- for (; i <= fi_args->max_id; ++i) {
- ret = get_device_info(fd, i, &di_args[ndevs]);
+ for (; last_devid <= fi_args->max_id; last_devid++) {
+ ret = get_device_info(fd, last_devid, &di_args[ndevs]);
if (ret == -ENODEV)
continue;
if (ret)
@@ -3394,7 +3572,7 @@ int test_dev_for_mkfs(const char *file, int force_overwrite)
return 0;
}
-int btrfs_scan_lblkid(void)
+int btrfs_scan_devices(void)
{
int fd = -1;
int ret;
@@ -3494,31 +3672,6 @@ int ask_user(const char *question)
}
/*
- * For a given:
- * - file or directory return the containing tree root id
- * - subvolume return its own tree id
- * - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is
- * undefined and function returns -1
- */
-int lookup_ino_rootid(int fd, u64 *rootid)
-{
- struct btrfs_ioctl_ino_lookup_args args;
- int ret;
-
- memset(&args, 0, sizeof(args));
- args.treeid = 0;
- args.objectid = BTRFS_FIRST_FREE_OBJECTID;
-
- ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
- if (ret < 0)
- return -errno;
-
- *rootid = args.treeid;
-
- return 0;
-}
-
-/*
* return 0 if a btrfs mount point is found
* return 1 if a mount point is found but not btrfs
* return <0 if something goes wrong
@@ -3717,6 +3870,10 @@ u64 get_partition_size(const char *dev)
return result;
}
+/*
+ * Check if the BTRFS_IOC_TREE_SEARCH_V2 ioctl is supported on a given
+ * filesystem, opened at fd
+ */
int btrfs_tree_search2_ioctl_supported(int fd)
{
struct btrfs_ioctl_search_args_v2 *args2;
@@ -3724,10 +3881,6 @@ int btrfs_tree_search2_ioctl_supported(int fd)
int args2_size = 1024;
char args2_buf[args2_size];
int ret;
- static int v2_supported = -1;
-
- if (v2_supported != -1)
- return v2_supported;
args2 = (struct btrfs_ioctl_search_args_v2 *)args2_buf;
sk = &(args2->key);
@@ -3748,13 +3901,10 @@ int btrfs_tree_search2_ioctl_supported(int fd)
args2->buf_size = args2_size - sizeof(struct btrfs_ioctl_search_args_v2);
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, args2);
if (ret == -EOPNOTSUPP)
- v2_supported = 0;
+ return 0;
else if (ret == 0)
- v2_supported = 1;
- else
- return ret;
-
- return v2_supported;
+ return 1;
+ return ret;
}
int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features)
@@ -3906,6 +4056,8 @@ unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode)
int string_is_numerical(const char *str)
{
+ if (!str)
+ return 0;
if (!(*str >= '0' && *str <= '9'))
return 0;
while (*str >= '0' && *str <= '9')
@@ -4052,27 +4204,20 @@ int get_subvol_info(const char *fullpath, struct root_info *get_ri)
goto out;
ret = btrfs_list_get_path_rootid(fd, &sv_id);
- if (ret) {
- error("can't get rootid for '%s'", fullpath);
+ if (ret)
goto out;
- }
mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
if (mntfd < 0)
goto out;
- if (sv_id == BTRFS_FS_TREE_OBJECTID) {
- ret = 2;
- /*
- * So that caller may decide if thats an error or just fine.
- */
- goto out;
- }
-
memset(get_ri, 0, sizeof(*get_ri));
get_ri->root_id = sv_id;
- ret = btrfs_get_subvol(mntfd, get_ri);
+ if (sv_id == BTRFS_FS_TREE_OBJECTID)
+ ret = btrfs_get_toplevel_subvol(mntfd, get_ri);
+ else
+ ret = btrfs_get_subvol(mntfd, get_ri);
if (ret)
error("can't find '%s': %d", svpath, ret);
diff --git a/utils.h b/utils.h
index f4e11bf3..3d30bd12 100644
--- a/utils.h
+++ b/utils.h
@@ -95,6 +95,8 @@ void set_argv0(char **argv);
#define UNITS_RAW (1U << UNITS_MODE_SHIFT)
#define UNITS_BINARY (2U << UNITS_MODE_SHIFT)
#define UNITS_DECIMAL (3U << UNITS_MODE_SHIFT)
+/* Interpret the u64 value as s64 */
+#define UNITS_NEGATIVE (4U << UNITS_MODE_SHIFT)
#define UNITS_MODE_MASK ((1U << UNITS_MODE_SHIFT) - 1)
#define UNITS_MODE_SHIFT (8)
#define UNITS_HUMAN_BINARY (UNITS_BINARY)
@@ -109,6 +111,8 @@ void btrfs_list_all_fs_features(u64 mask_disallowed);
char* btrfs_parse_fs_features(char *namelist, u64 *flags);
void btrfs_process_fs_features(u64 flags);
void btrfs_parse_features_to_string(char *buf, u64 flags);
+void print_kernel_version(FILE *stream, u32 version);
+u32 get_running_kernel_version(void);
struct btrfs_mkfs_config {
char *label;
@@ -151,14 +155,15 @@ struct btrfs_convert_context {
#define PREP_DEVICE_DISCARD (1U << 1)
#define PREP_DEVICE_VERBOSE (1U << 2)
-int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
- struct btrfs_convert_context *cctx);
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg);
+int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx);
int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid);
int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret,
u64 max_block_count, unsigned opflags);
int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, int fd, char *path,
+ struct btrfs_root *root, int fd, const char *path,
u64 block_count, u32 io_width, u32 io_align,
u32 sectorsize);
int btrfs_scan_for_fsid(int run_ioctls);
@@ -176,7 +181,6 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_bytes, unsigned unit_mo
#define pretty_size(size) pretty_size_mode(size, UNITS_DEFAULT)
const char *pretty_size_mode(u64 size, unsigned mode);
-int get_mountpt(char *dev, char *mntpt, size_t size);
u64 parse_size(char *s);
u64 parse_qgroupid(const char *p);
u64 arg_strtou64(const char *str);
@@ -184,7 +188,7 @@ int arg_copy_path(char *dest, const char *src, int destlen);
int open_file_or_dir(const char *fname, DIR **dirstream);
int open_file_or_dir3(const char *fname, DIR **dirstream, int open_flags);
void close_file_or_dir(int fd, DIR *dirstream);
-int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
+int get_fs_info(const char *path, struct btrfs_ioctl_fs_info_args *fi_args,
struct btrfs_ioctl_dev_info_args **di_ret);
int get_label(const char *btrfs_dev, char *label);
int set_label(const char *btrfs_dev, const char *label);
@@ -208,8 +212,8 @@ int is_vol_small(const char *file);
int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
int verify);
int ask_user(const char *question);
-int lookup_ino_rootid(int fd, u64 *rootid);
-int btrfs_scan_lblkid(void);
+int lookup_path_rootid(int fd, u64 *rootid);
+int btrfs_scan_devices(void);
int get_btrfs_mount(const char *dev, char *mp, size_t mp_size);
int find_mount_root(const char *path, char **mount_root);
int get_device_info(int fd, u64 devid,
diff --git a/version.sh b/version.sh
index 5a99acfa..4908d0fa 100755
--- a/version.sh
+++ b/version.sh
@@ -6,7 +6,7 @@
# Copyright 2008, Oracle
# Released under the GNU GPLv2
-v="v4.7.3"
+v="v4.9.1"
opt=$1
diff --git a/volumes.c b/volumes.c
index 2d07e66a..a0a85edd 100644
--- a/volumes.c
+++ b/volumes.c
@@ -162,6 +162,8 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
struct btrfs_device *device;
again:
+ if (!fs_devices)
+ return 0;
while (!list_empty(&fs_devices->devices)) {
device = list_entry(fs_devices->devices.next,
struct btrfs_device, dev_list);
@@ -274,53 +276,79 @@ int btrfs_scan_one_device(int fd, const char *path,
}
/*
+ * find_free_dev_extent_start - find free space in the specified device
+ * @device: the device which we search the free space in
+ * @num_bytes: the size of the free space that we need
+ * @search_start: the position from which to begin the search
+ * @start: store the start of the free space.
+ * @len: the size of the free space. that we find, or the size
+ * of the max free space if we don't find suitable free space
+ *
* this uses a pretty simple search, the expectation is that it is
* called very infrequently and that a given device has a small number
* of extents
+ *
+ * @start is used to store the start of the free space if we find. But if we
+ * don't find suitable free space, it will be used to store the start position
+ * of the max free space.
+ *
+ * @len is used to store the size of the free space that we find.
+ * 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(struct btrfs_trans_handle *trans,
- struct btrfs_device *device,
- struct btrfs_path *path,
- u64 num_bytes, u64 *start)
+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)
{
struct btrfs_key key;
struct btrfs_root *root = device->dev_root;
- struct btrfs_dev_extent *dev_extent = NULL;
- u64 hole_size = 0;
- u64 last_byte = 0;
- u64 search_start = root->fs_info->alloc_start;
+ struct btrfs_dev_extent *dev_extent;
+ struct btrfs_path *path;
+ u64 hole_size;
+ u64 max_hole_start;
+ u64 max_hole_size;
+ u64 extent_end;
u64 search_end = device->total_bytes;
int ret;
- int slot = 0;
- int start_found;
+ int slot;
struct extent_buffer *l;
+ u64 min_search_start;
- start_found = 0;
- path->reada = 2;
+ /*
+ * We don't want to overwrite the superblock on the drive nor any area
+ * used by the boot loader (grub for example), so we make sure to start
+ * at an offset of at least 1MB.
+ */
+ min_search_start = max(root->fs_info->alloc_start, 1024ull * 1024);
+ search_start = max(search_start, min_search_start);
- /* FIXME use last free of some kind */
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
- /* we don't want to overwrite the superblock on the drive,
- * so we make sure to start at an offset of at least 1MB
- */
- search_start = max(BTRFS_BLOCK_RESERVED_1M_FOR_SUPER, search_start);
+ max_hole_start = search_start;
+ max_hole_size = 0;
if (search_start >= search_end) {
ret = -ENOSPC;
- goto error;
+ goto out;
}
+ path->reada = 2;
+
key.objectid = device->devid;
key.offset = search_start;
key.type = BTRFS_DEV_EXTENT_KEY;
- ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
- if (ret < 0)
- goto error;
- ret = btrfs_previous_item(root, path, 0, key.type);
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0)
- goto error;
- l = path->nodes[0];
- btrfs_item_key_to_cpu(l, &key, path->slots[0]);
+ goto out;
+ if (ret > 0) {
+ ret = btrfs_previous_item(root, path, key.objectid, key.type);
+ if (ret < 0)
+ goto out;
+ }
+
while (1) {
l = path->nodes[0];
slot = path->slots[0];
@@ -329,24 +357,9 @@ static int find_free_dev_extent(struct btrfs_trans_handle *trans,
if (ret == 0)
continue;
if (ret < 0)
- goto error;
-no_more_items:
- if (!start_found) {
- if (search_start >= search_end) {
- ret = -ENOSPC;
- goto error;
- }
- *start = search_start;
- start_found = 1;
- goto check_pending;
- }
- *start = last_byte > search_start ?
- last_byte : search_start;
- if (search_end <= *start) {
- ret = -ENOSPC;
- goto error;
- }
- goto check_pending;
+ goto out;
+
+ break;
}
btrfs_item_key_to_cpu(l, &key, slot);
@@ -354,49 +367,85 @@ no_more_items:
goto next;
if (key.objectid > device->devid)
- goto no_more_items;
-
- if (key.offset >= search_start && key.offset > last_byte &&
- start_found) {
- if (last_byte < search_start)
- last_byte = search_start;
- hole_size = key.offset - last_byte;
- if (key.offset > last_byte &&
- hole_size >= num_bytes) {
- *start = last_byte;
- goto check_pending;
- }
- }
- if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY) {
+ break;
+
+ if (key.type != BTRFS_DEV_EXTENT_KEY)
goto next;
+
+ if (key.offset > search_start) {
+ hole_size = key.offset - search_start;
+
+ /*
+ * Have to check before we set max_hole_start, otherwise
+ * we could end up sending back this offset anyway.
+ */
+ if (hole_size > max_hole_size) {
+ max_hole_start = search_start;
+ max_hole_size = hole_size;
+ }
+
+ /*
+ * If this free space is greater than which we need,
+ * it must be the max free space that we have found
+ * until now, so max_hole_start must point to the start
+ * of this free space and the length of this free space
+ * is stored in max_hole_size. Thus, we return
+ * max_hole_start and max_hole_size and go back to the
+ * caller.
+ */
+ if (hole_size >= num_bytes) {
+ ret = 0;
+ goto out;
+ }
}
- start_found = 1;
dev_extent = btrfs_item_ptr(l, slot, struct btrfs_dev_extent);
- last_byte = key.offset + btrfs_dev_extent_length(l, dev_extent);
+ extent_end = key.offset + btrfs_dev_extent_length(l,
+ dev_extent);
+ if (extent_end > search_start)
+ search_start = extent_end;
next:
path->slots[0]++;
cond_resched();
}
-check_pending:
- /* we have to make sure we didn't find an extent that has already
- * been allocated by the map tree or the original allocation
+
+ /*
+ * At this point, search_start should be the end of
+ * allocated dev extents, and when shrinking the device,
+ * search_end may be smaller than search_start.
*/
- btrfs_release_path(path);
- BUG_ON(*start < search_start);
+ if (search_end > search_start) {
+ hole_size = search_end - search_start;
- if (*start + num_bytes > search_end) {
- ret = -ENOSPC;
- goto error;
+ if (hole_size > max_hole_size) {
+ max_hole_start = search_start;
+ max_hole_size = hole_size;
+ }
}
- /* check for pending inserts here */
- return 0;
-error:
- btrfs_release_path(path);
+ /* See above. */
+ if (max_hole_size < num_bytes)
+ ret = -ENOSPC;
+ else
+ ret = 0;
+
+out:
+ btrfs_free_path(path);
+ *start = max_hole_start;
+ if (len)
+ *len = max_hole_size;
return ret;
}
+int find_free_dev_extent(struct btrfs_trans_handle *trans,
+ 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);
+}
+
static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
struct btrfs_device *device,
u64 chunk_tree, u64 chunk_objectid,
@@ -419,7 +468,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, path, num_bytes,
+ ret = find_free_dev_extent(trans, device, num_bytes,
start);
if (ret)
goto err;
@@ -459,7 +508,8 @@ static int find_next_chunk(struct btrfs_root *root, u64 objectid, u64 *offset)
struct btrfs_key found_key;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
key.objectid = objectid;
key.offset = (u64)-1;
@@ -740,7 +790,7 @@ static int btrfs_device_avail_bytes(struct btrfs_trans_handle *trans,
goto next;
if (key.objectid > device->devid)
break;
- if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY)
+ if (key.type != BTRFS_DEV_EXTENT_KEY)
goto next;
if (key.offset > search_end)
break;
@@ -1069,13 +1119,20 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
key.type = BTRFS_CHUNK_ITEM_KEY;
if (convert) {
- BUG_ON(*start != round_down(*start, extent_root->sectorsize));
+ if (*start != round_down(*start, extent_root->sectorsize)) {
+ error("DATA chunk start not sectorsize aligned: %llu",
+ (unsigned long long)*start);
+ return -EINVAL;
+ }
key.offset = *start;
dev_offset = *start;
} else {
+ u64 tmp;
+
ret = find_next_chunk(chunk_root,
BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- &key.offset);
+ &tmp);
+ key.offset = tmp;
if (ret)
return ret;
}
@@ -2056,25 +2113,39 @@ static int rmw_eb(struct btrfs_fs_info *info,
return 0;
}
-static void split_eb_for_raid56(struct btrfs_fs_info *info,
- struct extent_buffer *orig_eb,
+static int split_eb_for_raid56(struct btrfs_fs_info *info,
+ struct extent_buffer *orig_eb,
struct extent_buffer **ebs,
u64 stripe_len, u64 *raid_map,
int num_stripes)
{
- struct extent_buffer *eb;
+ struct extent_buffer **tmp_ebs;
u64 start = orig_eb->start;
u64 this_eb_start;
int i;
- int ret;
+ int ret = 0;
+
+ tmp_ebs = calloc(num_stripes, sizeof(*tmp_ebs));
+ if (!tmp_ebs)
+ return -ENOMEM;
+ /* Alloc memory in a row for data stripes */
for (i = 0; i < num_stripes; i++) {
if (raid_map[i] >= BTRFS_RAID5_P_STRIPE)
break;
- eb = calloc(1, sizeof(struct extent_buffer) + stripe_len);
- if (!eb)
- BUG();
+ tmp_ebs[i] = calloc(1, sizeof(**tmp_ebs) + stripe_len);
+ if (!tmp_ebs[i]) {
+ ret = -ENOMEM;
+ goto clean_up;
+ }
+ }
+
+ for (i = 0; i < num_stripes; i++) {
+ struct extent_buffer *eb = tmp_ebs[i];
+
+ if (raid_map[i] >= BTRFS_RAID5_P_STRIPE)
+ break;
eb->start = raid_map[i];
eb->len = stripe_len;
@@ -2088,12 +2159,21 @@ static void split_eb_for_raid56(struct btrfs_fs_info *info,
if (start > this_eb_start ||
start + orig_eb->len < this_eb_start + stripe_len) {
ret = rmw_eb(info, eb, orig_eb);
- BUG_ON(ret);
+ if (ret)
+ goto clean_up;
} else {
- memcpy(eb->data, orig_eb->data + eb->start - start, stripe_len);
+ memcpy(eb->data, orig_eb->data + eb->start - start,
+ stripe_len);
}
ebs[i] = eb;
}
+ free(tmp_ebs);
+ return ret;
+clean_up:
+ for (i = 0; i < num_stripes; i++)
+ free(tmp_ebs[i]);
+ free(tmp_ebs);
+ return ret;
}
int write_raid56_with_parity(struct btrfs_fs_info *info,
@@ -2103,18 +2183,25 @@ int write_raid56_with_parity(struct btrfs_fs_info *info,
{
struct extent_buffer **ebs, *p_eb = NULL, *q_eb = NULL;
int i;
- int j;
int ret;
int alloc_size = eb->len;
+ void **pointers;
- ebs = kmalloc(sizeof(*ebs) * multi->num_stripes, GFP_NOFS);
- BUG_ON(!ebs);
+ ebs = malloc(sizeof(*ebs) * multi->num_stripes);
+ pointers = malloc(sizeof(*pointers) * multi->num_stripes);
+ if (!ebs || !pointers) {
+ free(ebs);
+ free(pointers);
+ return -ENOMEM;
+ }
if (stripe_len > alloc_size)
alloc_size = stripe_len;
- split_eb_for_raid56(info, eb, ebs, stripe_len, raid_map,
- multi->num_stripes);
+ ret = split_eb_for_raid56(info, eb, ebs, stripe_len, raid_map,
+ multi->num_stripes);
+ if (ret)
+ goto out;
for (i = 0; i < multi->num_stripes; i++) {
struct extent_buffer *new_eb;
@@ -2122,11 +2209,17 @@ int write_raid56_with_parity(struct btrfs_fs_info *info,
ebs[i]->dev_bytenr = multi->stripes[i].physical;
ebs[i]->fd = multi->stripes[i].dev->fd;
multi->stripes[i].dev->total_ios++;
- BUG_ON(ebs[i]->start != raid_map[i]);
+ if (ebs[i]->start != raid_map[i]) {
+ ret = -EINVAL;
+ goto out_free_split;
+ }
continue;
}
- new_eb = kmalloc(sizeof(*eb) + alloc_size, GFP_NOFS);
- BUG_ON(!new_eb);
+ new_eb = malloc(sizeof(*eb) + alloc_size);
+ if (!new_eb) {
+ ret = -ENOMEM;
+ goto out_free_split;
+ }
new_eb->dev_bytenr = multi->stripes[i].physical;
new_eb->fd = multi->stripes[i].dev->fd;
multi->stripes[i].dev->total_ios++;
@@ -2138,12 +2231,6 @@ int write_raid56_with_parity(struct btrfs_fs_info *info,
q_eb = new_eb;
}
if (q_eb) {
- void **pointers;
-
- pointers = kmalloc(sizeof(*pointers) * multi->num_stripes,
- GFP_NOFS);
- BUG_ON(!pointers);
-
ebs[multi->num_stripes - 2] = p_eb;
ebs[multi->num_stripes - 1] = q_eb;
@@ -2151,31 +2238,30 @@ int write_raid56_with_parity(struct btrfs_fs_info *info,
pointers[i] = ebs[i]->data;
raid6_gen_syndrome(multi->num_stripes, stripe_len, pointers);
- kfree(pointers);
} else {
ebs[multi->num_stripes - 1] = p_eb;
- memcpy(p_eb->data, ebs[0]->data, stripe_len);
- for (j = 1; j < multi->num_stripes - 1; j++) {
- for (i = 0; i < stripe_len; i += sizeof(u64)) {
- u64 p_eb_data;
- u64 ebs_data;
-
- p_eb_data = get_unaligned_64(p_eb->data + i);
- ebs_data = get_unaligned_64(ebs[j]->data + i);
- p_eb_data ^= ebs_data;
- put_unaligned_64(p_eb_data, p_eb->data + i);
- }
- }
+ for (i = 0; i < multi->num_stripes; i++)
+ pointers[i] = ebs[i]->data;
+ ret = raid5_gen_result(multi->num_stripes, stripe_len,
+ multi->num_stripes - 1, pointers);
+ if (ret < 0)
+ goto out_free_split;
}
for (i = 0; i < multi->num_stripes; i++) {
ret = write_extent_to_disk(ebs[i]);
- BUG_ON(ret);
- if (ebs[i] != eb)
- kfree(ebs[i]);
+ if (ret < 0)
+ goto out_free_split;
}
- kfree(ebs);
+out_free_split:
+ for (i = 0; i < multi->num_stripes; i++) {
+ if (ebs[i] != eb)
+ free(ebs[i]);
+ }
+out:
+ free(ebs);
+ free(pointers);
- return 0;
+ return ret;
}
diff --git a/volumes.h b/volumes.h
index d7b7d3cc..ee7d56ab 100644
--- a/volumes.h
+++ b/volumes.h
@@ -155,11 +155,28 @@ struct map_lookup {
* Check if the given range cross stripes.
* To ensure kernel scrub won't causing bug on with METADATA in mixed
* block group
+ *
+ * Return 1 if the range crosses STRIPE boundary
+ * Return 0 if the range doesn't cross STRIPE boundary or it
+ * doesn't belong to any block group (no boundary to cross)
*/
-static inline int check_crossing_stripes(u64 start, u64 len)
+static inline int check_crossing_stripes(struct btrfs_fs_info *fs_info,
+ u64 start, u64 len)
{
- return (start / BTRFS_STRIPE_LEN) !=
- ((start + len - 1) / BTRFS_STRIPE_LEN);
+ struct btrfs_block_group_cache *bg_cache;
+ u64 bg_offset;
+
+ bg_cache = btrfs_lookup_block_group(fs_info, start);
+ /*
+ * Does not belong to block group, no boundary to cross
+ * although it's a bigger problem, but here we don't care.
+ */
+ if (!bg_cache)
+ return 0;
+ bg_offset = start - bg_cache->key.objectid;
+
+ return (bg_offset / BTRFS_STRIPE_LEN !=
+ (bg_offset + len - 1) / BTRFS_STRIPE_LEN);
}
int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,