summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri John Ledkov <xnox@ubuntu.com>2017-02-13 11:24:33 +0000
committerDimitri John Ledkov <xnox@ubuntu.com>2017-02-13 11:24:33 +0000
commit4305d024938113df5d73021a09eb2a991f54ca2f (patch)
treed9e7ecc9db14bcc1394607a9e6c644a8b93e9bea
parente693f0e4ffb1776a05b78264ee3d93d5f07efede (diff)
New upstream release Closes: #849353, #817806, #854915, #845473
-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_filte