summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri John Ledkov <xnox@ubuntu.com>2017-07-31 14:54:24 +0100
committerDimitri John Ledkov <xnox@ubuntu.com>2017-07-31 14:54:24 +0100
commit6a0440391da7a99ffab94ccc66264af9b5f3ce34 (patch)
tree1c7cf4f07b08c4965ab19819ebce367be16fb980
parent5f2e2384443a09e3f1fec71940e9e32b70789102 (diff)
New upstream release.
-rw-r--r--.gitignore5
-rw-r--r--Android.mk26
-rw-r--r--CHANGES91
-rw-r--r--Documentation/CodingConventions19
-rw-r--r--Documentation/DocConventions39
-rw-r--r--Documentation/Makefile.in5
-rw-r--r--Documentation/btrfs-balance.asciidoc16
-rw-r--r--Documentation/btrfs-check.asciidoc8
-rw-r--r--Documentation/btrfs-device.asciidoc26
-rw-r--r--Documentation/btrfs-filesystem.asciidoc12
-rw-r--r--Documentation/btrfs-find-root.asciidoc2
-rw-r--r--Documentation/btrfs-inspect-internal.asciidoc6
-rw-r--r--Documentation/btrfs-man5.asciidoc125
-rw-r--r--Documentation/btrfs-property.asciidoc6
-rw-r--r--Documentation/btrfs-quota.asciidoc8
-rw-r--r--Documentation/btrfs-receive.asciidoc26
-rw-r--r--Documentation/btrfs-replace.asciidoc2
-rw-r--r--Documentation/btrfs-rescue.asciidoc10
-rw-r--r--Documentation/btrfs-restore.asciidoc2
-rw-r--r--Documentation/btrfs-scrub.asciidoc9
-rw-r--r--Documentation/btrfs-send.asciidoc27
-rw-r--r--Documentation/btrfs-subvolume.asciidoc11
-rw-r--r--Documentation/btrfstune.asciidoc8
-rw-r--r--Documentation/mkfs.btrfs.asciidoc4
-rw-r--r--Makefile (renamed from Makefile.in)213
-rw-r--r--Makefile.inc.in36
-rw-r--r--README.md13
-rw-r--r--backref.c14
-rw-r--r--backref.h2
-rw-r--r--btrfs-corrupt-block.c58
-rw-r--r--btrfs-debug-tree.c1
-rw-r--r--btrfs-find-root.c7
-rw-r--r--btrfs-list.c4
-rw-r--r--btrfs-map-logical.c16
-rw-r--r--btrfs-select-super.c3
-rw-r--r--btrfs-zero-log.c1
-rw-r--r--btrfs.c97
-rw-r--r--btrfstune.c20
-rw-r--r--chunk-recover.c46
-rw-r--r--cmds-balance.c3
-rw-r--r--cmds-check.c619
-rw-r--r--cmds-device.c7
-rw-r--r--cmds-fi-du.c2
-rw-r--r--cmds-fi-usage.c11
-rw-r--r--cmds-fi-usage.h4
-rw-r--r--cmds-filesystem.c3
-rw-r--r--cmds-inspect-dump-super.c19
-rw-r--r--cmds-inspect-dump-tree.c50
-rw-r--r--cmds-inspect-tree-stats.c24
-rw-r--r--cmds-inspect.c7
-rw-r--r--cmds-property.c1
-rw-r--r--cmds-qgroup.c1
-rw-r--r--cmds-quota.c1
-rw-r--r--cmds-receive.c53
-rw-r--r--cmds-replace.c3
-rw-r--r--cmds-rescue.c1
-rw-r--r--cmds-restore.c29
-rw-r--r--cmds-scrub.c3
-rw-r--r--cmds-send.c3
-rw-r--r--cmds-subvolume.c57
-rw-r--r--commands.h14
-rw-r--r--common-defs.h22
-rwxr-xr-xconfig/config.guess2
-rwxr-xr-xconfig/config.sub10
-rwxr-xr-xconfigure22
-rw-r--r--configure.ac2
-rw-r--r--convert/Makefile7
-rw-r--r--convert/common.c884
-rw-r--r--convert/common.h56
-rw-r--r--convert/main.c2269
-rw-r--r--convert/source-ext2.c886
-rw-r--r--convert/source-ext2.h113
-rw-r--r--convert/source-fs.c280
-rw-r--r--convert/source-fs.h118
-rw-r--r--ctree.c92
-rw-r--r--ctree.h85
-rw-r--r--debian/changelog6
-rw-r--r--dir-item.c4
-rw-r--r--disk-io.c251
-rw-r--r--disk-io.h52
-rw-r--r--extent-tree.c204
-rw-r--r--extent_io.c30
-rw-r--r--extent_io.h12
-rw-r--r--file-item.c28
-rw-r--r--file.c169
-rw-r--r--find-root.c7
-rw-r--r--free-space-cache.c17
-rw-r--r--free-space-tree.c4
-rw-r--r--fsfeatures.c227
-rw-r--r--fsfeatures.h50
-rw-r--r--help.c106
-rw-r--r--help.h79
-rw-r--r--image/main.c48
-rw-r--r--inode-item.c10
-rw-r--r--inode.c2
-rw-r--r--kerncompat.h7
-rw-r--r--kernel-lib/mktables.c148
-rw-r--r--kernel-lib/raid56.c359
-rw-r--r--kernel-lib/raid56.h58
-rw-r--r--kernel-lib/sizes.h47
-rw-r--r--kernel-shared/ulist.c (renamed from ulist.c)49
-rw-r--r--kernel-shared/ulist.h (renamed from ulist.h)8
-rw-r--r--library-test.c37
-rw-r--r--messages.c77
-rw-r--r--messages.h85
-rw-r--r--mkfs/common.c729
-rw-r--r--mkfs/common.h63
-rw-r--r--mkfs/main.c71
-rw-r--r--print-tree.c53
-rw-r--r--qgroup-verify.c8
-rw-r--r--raid56.c172
-rw-r--r--root-tree.c3
-rw-r--r--send-dump.c9
-rw-r--r--send-stream.c2
-rw-r--r--send.h2
-rw-r--r--super-recover.c2
-rw-r--r--tests/README.md34
-rwxr-xr-xtests/build-tests.sh3
-rwxr-xr-xtests/cli-tests/002-balance-full-no-filters/test.sh2
-rwxr-xr-xtests/cli-tests/003-fi-resize-args/test.sh2
-rwxr-xr-xtests/cli-tests/004-send-parent-multi-subvol/test.sh2
-rwxr-xr-xtests/cli-tests/005-qgroup-show/test.sh2
-rwxr-xr-xtests/cli-tests/006-qgroup-show-sync/test.sh2
-rw-r--r--tests/common10
-rw-r--r--tests/common.convert63
-rw-r--r--tests/common.local2
-rwxr-xr-xtests/convert-tests/001-ext2-basic/test.sh14
-rwxr-xr-xtests/convert-tests/002-ext3-basic/test.sh14
-rwxr-xr-xtests/convert-tests/003-ext4-basic/test.sh14
-rwxr-xr-xtests/convert-tests/004-ext2-backup-superblock-ranges/test.sh22
-rwxr-xr-xtests/convert-tests/005-delete-all-rollback/test.sh12
-rwxr-xr-xtests/convert-tests/006-large-hole-extent/test.sh8
-rwxr-xr-xtests/convert-tests/007-unsupported-block-sizes/test.sh10
-rwxr-xr-xtests/convert-tests/008-readonly-image/test.sh8
-rwxr-xr-xtests/convert-tests/009-common-inode-flags/test.sh14
-rwxr-xr-xtests/fsck-tests.sh1
-rwxr-xr-xtests/fsck-tests/006-bad-root-items/test.sh6
-rwxr-xr-xtests/fsck-tests/012-leaf-corruption/test.sh24
-rwxr-xr-xtests/fsck-tests/013-extent-tree-rebuild/test.sh22
-rwxr-xr-xtests/fsck-tests/018-leaf-crossing-stripes/test.sh4
-rwxr-xr-xtests/fsck-tests/019-non-skinny-false-alert/test.sh4
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/block_group_item_false_alert.raw.xzbin0 -> 47792 bytes
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/shared_data_ref_only.imgbin0 -> 7168 bytes
-rwxr-xr-xtests/fsck-tests/020-extent-ref-cases/test.sh19
-rwxr-xr-xtests/fsck-tests/021-partially-dropped-snapshot-case/test.sh6
-rwxr-xr-xtests/fsck-tests/022-qgroup-rescan-halfway/test.sh4
-rwxr-xr-xtests/fsck-tests/023-qgroup-stack-overflow/test.sh4
-rwxr-xr-xtests/fsck-tests/024-clear-space-cache/test.sh16
-rwxr-xr-xtests/fsck-tests/025-file-extents/test.sh60
-rw-r--r--tests/fssum.c839
-rw-r--r--tests/fuzz-tests/images/bko-97171-btrfs-image.raw.txt (renamed from tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.txt)0
-rw-r--r--tests/fuzz-tests/images/bko-97171-btrfs-image.raw.xz (renamed from tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.xz)bin6748 -> 6748 bytes
-rwxr-xr-xtests/misc-tests.sh3
-rwxr-xr-xtests/misc-tests/009-subvolume-sync-must-wait/test.sh8
-rwxr-xr-xtests/misc-tests/013-subvolume-sync-crash/test.sh8
-rwxr-xr-xtests/misc-tests/014-filesystem-label/test.sh8
-rwxr-xr-xtests/misc-tests/016-send-clone-src/test.sh4
-rwxr-xr-xtests/misc-tests/017-recv-stream-malformatted/test.sh4
-rwxr-xr-xtests/misc-tests/018-recv-end-of-stream/test.sh20
-rwxr-xr-xtests/misc-tests/019-receive-clones-on-munted-subvol/test.sh127
-rwxr-xr-xtests/misc-tests/020-fix-superblock-corruption/test.sh34
-rwxr-xr-xtests/misc-tests/021-image-multi-devices/test.sh50
-rwxr-xr-xtests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh3
-rwxr-xr-xtests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh3
-rw-r--r--tests/sha-private.h28
-rw-r--r--tests/sha.h356
-rw-r--r--tests/sha224-256.c601
-rw-r--r--transaction.h7
-rw-r--r--utils.c1965
-rw-r--r--utils.h338
-rwxr-xr-xversion.sh2
-rw-r--r--volumes.c227
-rw-r--r--volumes.h50
173 files changed, 9559 insertions, 5616 deletions
diff --git a/.gitignore b/.gitignore
index 98b3657b..7d7a5482 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,12 +35,15 @@ btrfs-select-super
btrfs-calc-size
btrfs-crc
btrfstune
+mktables
+kernel-lib/tables.c
libbtrfs.a
libbtrfs.so
libbtrfs.so.0
libbtrfs.so.0.1
library-test
library-test-static
+/fssum
/tests/*-tests-results.txt
/tests/test-console.txt
@@ -67,7 +70,7 @@ cscope.out
.clang_complete
depcomp
libtool
-Makefile
+Makefile.inc
Documentation/Makefile
missing
mkinstalldirs
diff --git a/Android.mk b/Android.mk
index fe3209b6..c417dbf0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,22 +17,27 @@ STATIC_LIBS := -luuid -lblkid -luuid -lz -llzo2 -L. -pthread
btrfs_shared_libraries := libext2_uuid \
libext2_blkid
-objects := ctree.c disk-io.c radix-tree.c extent-tree.c print-tree.c \
+objects := ctree.c disk-io.c kernel-lib/radix-tree.c extent-tree.c print-tree.c \
root-tree.c dir-item.c file-item.c inode-item.c inode-map.c \
extent-cache.c extent_io.c volumes.c utils.c repair.c \
- qgroup.c raid6.c free-space-cache.c list_sort.c props.c \
- ulist.c qgroup-verify.c backref.c string-table.c task-utils.c \
- inode.c file.c find-root.c
+ qgroup.c free-space-cache.c kernel-lib/list_sort.c props.c \
+ kernel-shared/ulist.c qgroup-verify.c backref.c string-table.c task-utils.c \
+ inode.c file.c find-root.c free-space-tree.c help.c send-dump.c \
+ fsfeatures.c kernel-lib/tables.c kernel-lib/raid56.c
cmds_objects := cmds-subvolume.c cmds-filesystem.c cmds-device.c cmds-scrub.c \
cmds-inspect.c cmds-balance.c cmds-send.c cmds-receive.c \
cmds-quota.c cmds-qgroup.c cmds-replace.c cmds-check.c \
cmds-restore.c cmds-rescue.c chunk-recover.c super-recover.c \
- cmds-property.c cmds-fi-usage.c
-libbtrfs_objects := send-stream.c send-utils.c rbtree.c btrfs-list.c crc32c.c \
+ cmds-property.c cmds-fi-usage.c cmds-inspect-dump-tree.c \
+ cmds-inspect-dump-super.c cmds-inspect-tree-stats.c cmds-fi-du.c \
+ mkfs/common.c
+libbtrfs_objects := send-stream.c send-utils.c kernel-lib/rbtree.c btrfs-list.c \
+ kernel-lib/crc32c.c messages.c \
uuid-tree.c utils-lib.c rbtree-utils.c
-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 \
- extent_io.h ioctl.h ctree.h btrfsck.h version.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 kernel-lib/sizes.h kernel-lib/raid56.h \
+ extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
TESTS := fsck-tests.sh convert-tests.sh
blkid_objects := partition/ superblocks/ topology/
@@ -75,7 +80,8 @@ include $(CLEAR_VARS)
LOCAL_MODULE := mkfs.btrfs
LOCAL_SRC_FILES := \
$(objects) \
- mkfs.c
+ mkfs/common.c \
+ mkfs/main.c
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
LOCAL_CFLAGS := $(STATIC_CFLAGS)
diff --git a/CHANGES b/CHANGES
index fcabe663..db24f7c6 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,66 @@
+btrfs-progs-4.12 (2017-07-26)
+ * subvol show: new options --rootid, --uuid to show subvol by the given spec
+ * convert: progress report fixes, found by tsan
+ * image: progress report fixes, found by tsan
+ * fix infinite looping in find-root, or when looking for free extents
+ * other:
+ * code refactoring
+ * docs updates
+ * build: ThreadSanitizer support
+ * tests: stricter checks for mounted filesystem
+
+btrfs-progs-4.11.1 (2017-06-30)
+ * image: restoring from multiple devices
+ * dev stats: make --check option work
+ * check: fix false alert with extent hole on a NO_HOLE filesystem
+ * check: lowmem mode, fix false alert in case of mixed inline and compressed
+ extent
+ * convert: work with large filesystems (many TB)
+ * convert: fix overwriting of eb header flags
+ * convert: do not clear NODATASUM flag in inodes when run with --no-datasum
+ * docs updates
+ * build: sync Android.mk with Makefile
+ * tests:
+ * new tests
+ * fix 008 and 009, shell quotation mistake
+
+btrfs-progs-4.11 (2017-05-18)
+ * receive: fix handling empty stream with -e (multi-stream)
+ * send dump: fix printing long file names
+ * stability fixes for: dump-super, print-tree, check
+ * option parser updates: global options are parsed before the subcommand name
+ (old xfstests will fail)
+ * new and updated tests
+ * documentation updates
+
+btrfs-progs-4.10.2 (2017-03-31)
+ * check: lowmem mode fix for false alert about lost backrefs
+ * convert: minor bugfix
+ * library: fix build, misisng symbols, added tests
+
+btrfs-progs-4.10.1 (2017-03-17)
+ * receive: handle subvolume in path clone
+ * convert: rollback fixed (rewrite was needed to address previous design issues)
+ * build: fix build of 3rd party tools, missing <linux/sizes.h>
+ * dump-tree: print log trees
+ * other
+ * new and updated tests
+
+btrfs-progs-4.10 (2017-03-08)
+ * send: dump output fixes: missing newlies
+ * check: several fixes for the lowmem mode, improved error reporting
+ * build
+ * removed some library deps for binaries that not use them
+ * ctags, cscope
+ * split Makefile to the autotool generated part and the rest, not needed
+ to autogen.sh after adding a file
+ * shared code: sync easy parts with kernel sources
+ * other
+ * lots of cleanups
+ * source file reorganization: convert, mkfs, utils
+ * lots of spelling fixes in docs, other updates
+ * more tests
+
btrfs-progs-4.9.1 (2017-01-27)
* check:
* use correct inode number for lost+found files
@@ -169,7 +232,7 @@ btrfs-progs-4.7 (2016-07-29)
* more tests
btrfs-progs-4.6.1 (2016-06-24)
- * fi resize: negative resize argument accepted again
+ * filesystem resize: negative resize argument accepted again
* qgroup rescan: fix skipping when rescan is in progress
* mkfs: initialize stripesize to correct value
* testsuite updates, mostly convert tests
@@ -214,8 +277,8 @@ btrfs-progs-4.5.2 (2016-05-02)
* check: catch when qgroup numbers mismatch
* check: detect running quota rescan and report mismatches
* balance start: add safety delay before doing a full balance
- * fi sync: is now silent
- * fi show: don't miss filesystems with partially matching uuids
+ * filesystem sync: is now silent
+ * filesystem show: don't miss filesystems with partially matching uuids
* dev ready: accept only one argument for device
* dev stats: print "devid:N" for a missing device instead of "(null)"
* other:
@@ -232,7 +295,7 @@ btrfs-progs-4.5 (2016-03-20)
* btrfs-debug-tree -> btrfs inspect-internal dump-tree
New commands:
- * btrfs fi du - calculate disk usage, including shared extents
+ * btrfs filesystem du - calculate disk usage, including shared extents
Enhancements:
* device delete - delete by id (needs kernel support, not merged to
@@ -325,7 +388,7 @@ btrfs-progs-4.3 (2015-11-06)
* print version info earlier
* print devices sorted by id
* do not truncate target image with --rootsize
- * fi usage:
+ * filesystem usage:
* don't print global block reserve
* print device id
* minor output tuning
@@ -371,11 +434,11 @@ btrfs-progs-4.2.3 (2015-10-19)
* restore: off-by-one symlink path check fix
btrfs-progs-4.2.2 (2015-10-05)
- * fi label: use fallback if the label ioctl is not available
+ * filesystem label: use fallback if the label ioctl is not available
* convert: check nodesize constraints against commandline features (-O)
* scrub: report status 'running' until all devices are finished
* device scanning might crash in some scenarios
- * fi usage: print summary for non-root users
+ * filesystem usage: print summary for non-root users
btrfs-progs-4.2.1 (2015-09-20)
* fix an off-by-one error in cross-stripe boundary check
@@ -534,7 +597,7 @@ btrfs-progs-3.19.1 (2015-03-25)
* build fixes:
* missing macro from public header, BTRFS_BUILD_VERSION
* wrong handling of --enable-convert
- * fi usage: reports correct space for degraded mounts
+ * filesystem usage: reports correct space for degraded mounts
* other:
* mkfs: help string updates
* completion: added 'usage' subcommands
@@ -558,7 +621,7 @@ btrfs-progs-3.18.2 (2015-01-27)
* qgroup show: print human readable sizes, options to say otherwise
* check: new option to explicitly say no to writes
* mkfs: message about trimming is not printed to stderr
- * fi show: fixed return value
+ * filesystem show: fixed return value
* tests: new infrastructure
* btrfstune: force flag can be used together with seeding option
* backtrace support is back
@@ -573,7 +636,7 @@ btrfs-progs-3.18 (2014-12-30)
* mkfs - skinny-metadata feature is now on by default, first introduced in
kernel 3.10
* filesystem usage - give an overview of fs usage in a way that's more
- comprehensible than existing 'fi df'
+ comprehensible than existing 'filesystem df'
* device usage - more detailed information about per-device allocations
* check
* option to set a different tree root byte number
@@ -600,12 +663,12 @@ btrfs-progs-3.17.2 (2014-11-19)
* zero-log: able to reset a fs with bogus log tree pointer (bug_72151)
btrfs-progs-3.17.1 (2014-11-04)
- * fi df: argument handling
+ * filesystem df: argument handling
* fix linking with libbtrfs
* replace: better error reporting
- * fi show: fixed stall if run concurrently with balance
+ * filesystem show: fixed stall if run concurrently with balance
* check: fixed argument parsing for --subvol-extents
- * fi df: SI prefixes corrected
+ * filesystem df: SI prefixes corrected
btrfs-progs-3.17 (2014-10-17)
* check: --init-csum-tree actually does something useful, rebuilds the
@@ -620,7 +683,7 @@ btrfs-progs-3.16.2 (2014-10-01)
* a few fixes in fsck and image tools
btrfs-progs-3.16.1 (2014-09-15)
- * print GlobalReserve in fi df output
+ * print GlobalReserve in filesystem df output
* new option -R in subvol list
* library version defines
* static build is fixed
diff --git a/Documentation/CodingConventions b/Documentation/CodingConventions
new file mode 100644
index 00000000..cee90b30
--- /dev/null
+++ b/Documentation/CodingConventions
@@ -0,0 +1,19 @@
+C style
+-------
+
+The kernel CodingStyle where applicable
+
+https://www.kernel.org/doc/html/latest/process/coding-style.html
+
+Error messages
+--------------
+
+* formatting:
+ * use `error("string ...")`
+ * no trailing newline
+ * small letter starts the first word
+ * no string splitting
+ * move string to new line if it's too long, un-indent to the left if it
+ exceeds 80 chars
+* contents:
+ * be descriptive
diff --git a/Documentation/DocConventions b/Documentation/DocConventions
new file mode 100644
index 00000000..e84ed7a7
--- /dev/null
+++ b/Documentation/DocConventions
@@ -0,0 +1,39 @@
+Manual pages structure:
+
+- add references to all external commands mentioned anywhere in the text to the
+ 'SEE ALSO' section
+ - also add related, not explicitly mentioned
+- the heading levels are validated
+ - mandatory, manual page ===
+ - mandatory, sections ---
+ - optional, sub-sections ~~~
+- command-specific examples are mostly free of restrictions but should be
+ readable in all output formats (manual page, html)
+
+- subcommands are in alphabetical order
+
+- long command output or shell examples: verbatim output
+ - a new paragraph, 2 spaces at the beginning of the line
+
+
+Quotation in subcommands:
+
+- exact syntax: monotype `usage=0`
+- reference to arguments etc: 'italics'
+- command reference: bold *btrfs fi show*
+- section references: italics 'EXAMPLES'
+
+- argument name in option desciption: caps in angle brackets <NAME>
+ - reference in help text: caps NAME
+ also possible: caps italics 'NAME'
+
+- command short description:
+ - command name: bold *command*
+ - optional unspecified: brackets [options]
+ - mandatory argument: angle brackets <path>
+ - optional parameter with argument: [-p <path>]
+
+
+Refrences:
+- full asciidoc syntax: http://asciidoc.org/userguide.html
+- cheatsheet: http://powerman.name/doc/asciidoc
diff --git a/Documentation/Makefile.in b/Documentation/Makefile.in
index aea2cb47..539c6b55 100644
--- a/Documentation/Makefile.in
+++ b/Documentation/Makefile.in
@@ -68,11 +68,6 @@ ifndef V
QUIET_ASCIIDOC = @echo " [ASCII] $@";
QUIET_XMLTO = @echo " [XMLTO] $@";
QUIET_GZIP = @echo " [GZ] $@";
- QUIET_STDERR = 2> /dev/null
- QUIET_SUBDIR0 = +@subdir=
- QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
- $(MAKE) $(PRINT_DIR) -C $$subdir
- export V
endif
endif
diff --git a/Documentation/btrfs-balance.asciidoc b/Documentation/btrfs-balance.asciidoc
index c456898e..fba55140 100644
--- a/Documentation/btrfs-balance.asciidoc
+++ b/Documentation/btrfs-balance.asciidoc
@@ -89,7 +89,7 @@ warned and has a few seconds to cancel the operation before it starts. The
warning and delay can be skipped with '--full-balance' option.
+
Please note that the filters must be written together with the '-d', '-m' and
-'-s' options, because they're optional and bare '-d' etc alwo work and mean no
+'-s' options, because they're optional and bare '-d' etc also work and mean no
filters.
+
`Options`
@@ -143,7 +143,7 @@ The minimum range boundary is inclusive, maximum is exclusive.
*devid=<id>*::
Balances only block groups which have at least one chunk on the given
-device. To list devices with ids use *btrfs fi show*.
+device. To list devices with ids use *btrfs filesystem show*.
*drange=<range>*::
Balance only block groups which overlap with the given byte range on any
@@ -247,7 +247,7 @@ provided.
Let's use the following real life example and start with the output:
--------------------
-$ btrfs fi df /path
+$ btrfs filesystem df /path
Data, single: total=75.81GiB, used=64.44GiB
System, RAID1: total=32.00MiB, used=20.00KiB
Metadata, RAID1: total=15.87GiB, used=8.84GiB
@@ -273,7 +273,7 @@ data and thus will be faster. A typical filter command would look like:
# btrfs balance start -dusage=50 /path
Done, had to relocate 2 out of 97 chunks
-$ btrfs fi df /path
+$ btrfs filesystem df /path
Data, single: total=74.03GiB, used=64.43GiB
System, RAID1: total=32.00MiB, used=20.00KiB
Metadata, RAID1: total=15.87GiB, used=8.84GiB
@@ -288,7 +288,7 @@ usage filter.
# btrfs balance start -dusage=85 /path
Done, had to relocate 13 out of 95 chunks
-$ btrfs fi df /path
+$ btrfs filesystem df /path
Data, single: total=68.03GiB, used=64.43GiB
System, RAID1: total=32.00MiB, used=20.00KiB
Metadata, RAID1: total=15.87GiB, used=8.85GiB
@@ -309,7 +309,7 @@ reflinks updated frequently.
# btrfs balance start -musage=50 /path
Done, had to relocate 4 out of 89 chunks
-$ btrfs fi df /path
+$ btrfs filesystem df /path
Data, single: total=68.03GiB, used=64.43GiB
System, RAID1: total=32.00MiB, used=20.00KiB
Metadata, RAID1: total=14.87GiB, used=8.85GiB
@@ -327,7 +327,7 @@ further compaction:
# btrfs balance start -musage=70 /path
Done, had to relocate 13 out of 88 chunks
-$ btrfs fi df .
+$ btrfs filesystem df .
Data, single: total=68.03GiB, used=64.43GiB
System, RAID1: total=32.00MiB, used=20.00KiB
Metadata, RAID1: total=11.97GiB, used=8.83GiB
@@ -351,7 +351,7 @@ can be used to reclaim unused block groups to make it available.
# btrfs balance start -dusage=0 /path
--------------------
-This should lead to decrease in the 'total' numbers in the *btrfs fi df* output.
+This should lead to decrease in the 'total' numbers in the *btrfs filesystem df* output.
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-check.asciidoc b/Documentation/btrfs-check.asciidoc
index 633cbbf6..28ed9dd7 100644
--- a/Documentation/btrfs-check.asciidoc
+++ b/Documentation/btrfs-check.asciidoc
@@ -30,11 +30,11 @@ data structures satisfy the constraints, point to the right objects or are
correctly connected together.
There are several cross checks that can detect wrong reference counts of shared
-extents, backrefrences, missing extents of inodes, directory and inode
+extents, backreferences, missing extents of inodes, directory and inode
connectivity etc.
The amount of memory required can be high, depending on the size of the
-filesystem, smililarly the run time.
+filesystem, similarly the run time.
SAFE OR ADVISORY OPTIONS
------------------------
@@ -49,7 +49,7 @@ verify checksums of data blocks
+
This expects that the filesystem is otherwise
OK, so this is basically and offline 'scrub' but does not repair data from
-spare coipes.
+spare copies.
--chunk-root <bytenr>::
use the given offset 'bytenr' for the chunk tree root
@@ -111,7 +111,7 @@ NOTE: Do not use unless you know what you're doing.
select mode of operation regarding memory and IO
+
The 'MODE' can be one of 'original' and 'lowmem'. The original mode is mostly
-unoptimized regarding memory consumpption and can lead to out-of-memory
+unoptimized regarding memory consumption and can lead to out-of-memory
conditions on large filesystems. The possible workaround is to export the block
device over network to a machine with enough memory. The low memory mode is
supposed to address the memory consumption, at the cost of increased IO when it
diff --git a/Documentation/btrfs-device.asciidoc b/Documentation/btrfs-device.asciidoc
index eedcac85..88822ece 100644
--- a/Documentation/btrfs-device.asciidoc
+++ b/Documentation/btrfs-device.asciidoc
@@ -20,18 +20,18 @@ Data and metadata are organized in allocation profiles with various redundancy
policies. There's some similarity with traditional RAID levels, but this could
be confusing to users familiar with the traditional meaning. Due to the
similarity, the RAID terminology is widely used in the documentation. See
-`mkfs.btrfs`(9) for more details and the exact profile capabilities and
+`mkfs.btrfs`(8) for more details and the exact profile capabilities and
constraints.
The device management works on a mounted filesystem. Devices can be added,
-removed or replaced, by commands profided by *btrfs device* and *btrfs replace*.
+removed or replaced, by commands provided 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* command and namely the filter 'convert'.
Profile::
A profile describes an allocation policy based on the redundancy/replication
-constrants in connection with the number of devices. The profile applies to
+constraints in connection with the number of devices. The profile applies to
data and metadata block groups separately.
RAID level::
@@ -43,7 +43,7 @@ See the section *TYPICAL USECASES* for some examples.
SUBCOMMAND
----------
-*add* [-Kf] <dev> [<dev>...] <path>::
+*add* [-Kf] <device> [<device>...] <path>::
Add device(s) to the filesystem identified by <path>.
+
If applicable, a whole device discard (TRIM) operation is performed prior to
@@ -62,7 +62,7 @@ do not perform discard (TRIM) by default
-f|--force::::
force overwrite of existing filesystem on the given disk(s)
-*remove* <dev>|<devid> [<dev>|<devid>...] <path>::
+*remove* <device>|<devid> [<device>|<devid>...] <path>::
Remove device(s) from a filesystem identified by <path>
+
Device removal must satisfy the profile constraints, otherwise the command
@@ -72,15 +72,19 @@ using the RAID1 profile. See the example section below.
+
The operation can take long as it needs to move all data from the device.
+
-NOTE: It is not possible to delete the device that was used to mount the
-filesystem. This is a limitation given by the VFS.
+It is possible to delete the device that was used to mount the filesystem. The
+device entry in mount table will be replaced by another device name with the
+lowest device id.
-*delete* <dev>|<devid> [<dev>|<devid>...] <path>::
+*delete* <device>|<devid> [<device>|<devid>...] <path>::
Alias of remove kept for backward compatibility
*ready* <device>::
-Wait until all devices of a multiple-device filesystem are scanned and registered
-within the kernel module.
+Wait until all devices of a multiple-device filesystem are scanned and
+registered within the kernel module. This is to provide a way for automatic
+filesystem mounting tools to wait before the mount can start. The device scan
+is only one of the preconditions and the mount can fail for other reasons.
+Normal users usually do not need this command and may safely ignore it.
*scan* [(--all-devices|-d)|<device> [<device>...]]::
Scan devices for a btrfs filesystem and register them with the kernel module.
@@ -182,7 +186,7 @@ blocks, the disk seeking is the key factor affecting performance.
You'll note that the system block group has been also converted to RAID1, this
normally happens as the system block group also holds metadata (the physical to
-logial mappings).
+logical mappings).
What changed:
diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc
index 0f7ea495..b60ef74b 100644
--- a/Documentation/btrfs-filesystem.asciidoc
+++ b/Documentation/btrfs-filesystem.asciidoc
@@ -14,7 +14,7 @@ DESCRIPTION
*btrfs filesystem* is used to perform several whole filesystem level tasks,
including all the regular filesystem operations like resizing, space stats,
label setting/getting, and defragmentation. There are other whole filesystem
-taks like scrub or balance that are grouped in separate commands.
+tasks like scrub or balance that are grouped in separate commands.
SUBCOMMAND
----------
@@ -120,7 +120,7 @@ defragment files recursively in given directories
flush data for each file before going to the next file.
+
This will limit the amount of dirty data to current file, otherwise the amount
-cumulates from several files and will increase system load. This can also lead
+accumulates from several files and will increase system load. This can also lead
to ENOSPC if there's too much dirty data to write and it's not possible to make
the reservations for the new data (ie. how the COW design works).
+
@@ -141,7 +141,7 @@ files, it will report a count of total bytes, and exclusive (not
shared) bytes. We also calculate a 'set shared' value which is
described below.
+
-Each argument to 'btrfs fi du' will have a 'set shared' value
+Each argument to 'btrfs filesystem du' will have a 'set shared' value
calculated for it. We define each 'set' as those files found by a
recursive search of an argument. The 'set shared' value
then is a sum of all shared space referenced by the set.
@@ -170,7 +170,7 @@ show sizes in GiB, or GB with --si.
--tbytes::::
show sizes in TiB, or TB with --si.
-*label* [<dev>|<mountpoint>] [<newlabel>]::
+*label* [<device>|<mountpoint>] [<newlabel>]::
Show or update the label of a filesystem. This works on a mounted filesystem or
a filesystem image.
+
@@ -196,7 +196,7 @@ If the prefix '+' or '-' is present the size is increased or decreased
by the quantity 'size'.
If no units are specified, bytes are assumed for 'size'.
Optionally, the size parameter may be suffixed by one of the following
-units designators: \'K', \'M', \'G', \'T', \'P', or \'E', which represent
+unit designators: \'K', \'M', \'G', \'T', \'P', or \'E', which represent
KiB, MiB, GiB, TiB, PiB, or EiB, respectively (case does not matter).
+
If 'max' is passed, the filesystem will occupy all available space on the
@@ -262,7 +262,7 @@ root user (due to use of restricted ioctl). For both there's a summary section
with information about space usage:
+
-------------------------
-$ btrfs fi usage /path
+$ btrfs filesystem usage /path
WARNING: cannot read detailed chunk info, RAID5/6 numbers will be incorrect, run as root
Overall:
Device size: 1.82TiB
diff --git a/Documentation/btrfs-find-root.asciidoc b/Documentation/btrfs-find-root.asciidoc
index e04cd3e8..3d6af2df 100644
--- a/Documentation/btrfs-find-root.asciidoc
+++ b/Documentation/btrfs-find-root.asciidoc
@@ -7,7 +7,7 @@ btrfs-find-root - filter to find btrfs root
SYNOPSIS
--------
-*btrfs-find-root* [options] <dev>
+*btrfs-find-root* [options] <device>
DESCRIPTION
-----------
diff --git a/Documentation/btrfs-inspect-internal.asciidoc b/Documentation/btrfs-inspect-internal.asciidoc
index 44615e76..62b10294 100644
--- a/Documentation/btrfs-inspect-internal.asciidoc
+++ b/Documentation/btrfs-inspect-internal.asciidoc
@@ -26,7 +26,7 @@ Show btrfs superblock information stored on given devices in textual form.
By default the first superblock is printed, more details about all copies or
additional backup data can be printed.
+
-Besides verifictaion of the filesystem signature, there are no other sanity
+Besides verification of the filesystem signature, there are no other sanity
checks. The superblock checksum status is reported, the device item and
filesystem UUIDs are checked and reported.
+
@@ -51,8 +51,8 @@ for debugging (disables the '-f' option)
If there are multiple options specified, only the last one applies.
+
-F|--force::::
-attempt to print the superblock even if thre's no valid BTRFS signature found,
-the result may be completely wrong if the data do not resemble a superblock
+attempt to print the superblock even if a valid BTRFS signature is not found;
+the result may be completely wrong if the data does not resemble a superblock
+
-s|--super <bytenr>::::
(see compatibility note above)
diff --git a/Documentation/btrfs-man5.asciidoc b/Documentation/btrfs-man5.asciidoc
index acc4e429..8d9031f5 100644
--- a/Documentation/btrfs-man5.asciidoc
+++ b/Documentation/btrfs-man5.asciidoc
@@ -36,16 +36,6 @@ Enable/disable support for Posix Access Control Lists (ACLs). See the
The support for ACL is build-time configurable (BTRFS_FS_POSIX_ACL) and
mount fails if 'acl' is requested but the feature is not compiled in.
-*alloc_start='bytes'*::
-(default: 1M, minimum: 1M)
-+
-Debugging option to force all block allocations above a certain
-byte threshold on each block device. The value is specified in
-bytes, optionally with a K, M, or G suffix (case insensitive).
-+
-This option was used for testing and has no practical use, it's slated to be
-removed in the future.
-
*autodefrag*::
*noautodefrag*::
(since: 3.0, default: off)
@@ -130,10 +120,15 @@ but a warning is printed if it's more than 300 seconds (5 minutes).
Control BTRFS file data compression. Type may be specified as 'zlib',
'lzo' or 'no' (for no compression, used for remounting). If no type
is specified, 'zlib' is used. If 'compress-force' is specified,
-all files will be compressed, whether or not they compress well. Otherwise
-some simple heuristics are applied to detect an incompressible file. If the
-first blocks written to a file are not compressible, the whole file is
-permanently marked to skip compression.
+the compression will allways be attempted, but the data may end up uncompressed
+if the compression would make them larger.
++
+Otherwise some simple heuristics are applied to detect an incompressible file.
+If the first blocks written to a file are not compressible, the whole file is
+permanently marked to skip compression. As this is too simple, the
+'compress-force' is a workaround that will compress most of the files at the
+cost of some wasted CPU cycles on failed attempts. The heuristics of 'compress'
+will improve in the future so this will not be necessary.
+
NOTE: If compression is enabled, 'nodatacow' and 'nodatasum' are disabled.
@@ -288,12 +283,6 @@ override the internal logic in favor of the metadata allocation if the expected
workload is supposed to be metadata intense (snapshots, reflinks, xattrs,
inlined files).
-*recovery*::
-(since: 3.2, default: off, deprecated since: 4.5)
-+
-NOTE: this option has been replaced by 'usebackuproot' and should not be used
-but will work on 4.5+ kernels.
-
*norecovery*::
(since: 4.5, default: off)
+
@@ -314,9 +303,10 @@ normally be needed.
*skip_balance*::
(since: 3.3, default: off)
+
-Skip automatic resume of interrupted balance operation after mount.
-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.
+Skip automatic resume of an interrupted balance operation. The operation can
+later be resumed with *btrfs balance resume*, or the paused state can be
+removed with *btrfs balance cancel*. The default behaviour is to resume an
+interrupted balance immediately after a volume is mounted.
*space_cache*::
*space_cache='version'*::
@@ -347,19 +337,20 @@ If a version is not explicitly specified, the default implementation will be
chosen, which is 'v1' as of 4.9.
*ssd*::
-*nossd*::
*ssd_spread*::
+*nossd*::
(default: SSD autodetected)
+
Options to control SSD allocation schemes. By default, BTRFS will
enable or disable SSD allocation heuristics depending on whether a
-rotational or non-rotational disk is in use (contents of
-'/sys/block/DEV/queue/rotational'). The 'ssd' and 'nossd' options
-can override this autodetection.
+rotational or non-rotational device is in use (contents of
+'/sys/block/DEV/queue/rotational'). If it is, the 'ssd' option is turned on.
+The option 'nossd' will disable the autodetection.
+
The 'ssd_spread' mount option attempts to allocate into bigger and aligned
chunks of unused space, and may perform better on low-end SSDs. 'ssd_spread'
-implies 'ssd', enabling all other SSD heuristics as well.
+implies 'ssd', enabling all other SSD heuristics as well. The option 'nossd'
+will disable all SSD options.
*subvol='path'*::
Mount subvolume from 'path' rather than the toplevel subvolume. The
@@ -374,12 +365,6 @@ This mount option overrides the default subvolume set for the given filesystem.
NOTE: if both 'subvolid' and 'subvol' are specified, they must point at the
same subvolume, otherwise mount will fail.
-*subvolrootid='objectid'*::
-(irrelevant since: 3.2, formally deprecated since: 3.10)
-+
-A workaround option from times (pre 3.2) when it was not possible to mount a
-subvolume that did not reside directly under the toplevel subvolume.
-
*thread_pool='number'*::
(default: min(NRCPUS + 2, 8) )
+
@@ -419,6 +404,31 @@ NOTE: This option has replaced 'recovery'.
Allow subvolumes to be deleted by their respective owner. Otherwise, only the
root user can do that.
+DEPRECATED MOUNT OPTIONS
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+List of mount options that have been removed, kept for backward compatibility.
+
+*alloc_start='bytes'*::
+(default: 1M, minimum: 1M, deprecated since: 4.13)
++
+Debugging option to force all block allocations above a certain
+byte threshold on each block device. The value is specified in
+bytes, optionally with a K, M, or G suffix (case insensitive).
+
+*recovery*::
+(since: 3.2, default: off, deprecated since: 4.5)
++
+NOTE: this option has been replaced by 'usebackuproot' and should not be used
+but will work on 4.5+ kernels.
+
+*subvolrootid='objectid'*::
+(irrelevant since: 3.2, formally deprecated since: 3.10)
++
+A workaround option from times (pre 3.2) when it was not possible to mount a
+subvolume that did not reside directly under the toplevel subvolume.
+
+
FILESYSTEM FEATURES
-------------------
@@ -440,9 +450,9 @@ 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 had
-been enabled at the mount time and turned off afterwards.
+`/sys/fs/btrfs/UUID/features/`, one file per feature. The status is stored
+inside the file. The value '1' is for enabled and active, while '0' means the
+feature was enabled at mount time but turned off afterwards.
+
Whether a particular feature can be turned on a mounted filesystem can be found
in the directory `/sys/fs/btrfs/features/`, one file per feature. The value '1'
@@ -450,34 +460,36 @@ means the feature can be enabled.
List of features (see also `mkfs.btrfs`(8) section 'FILESYSTEM FEATURES'):
-big_metadata::
+*big_metadata*::
(since: 3.4)
+
-the filesystem uses 'nodesize' bigger than the page size
-compress_lzo::
+the filesystem uses 'nodesize' for metadata blocks, this can be bigger than the
+page size
+
+*compress_lzo*::
(since: 2.6.38)
+
the 'lzo' compression has been used on the filesystem, either as a mount option
or via *btrfs filesystem defrag*.
-default_subvol::
+*default_subvol*::
(since: 2.6.34)
+
the default subvolume has been set on the filesystem
-extended_iref::
+*extended_iref*::
(since: 3.7)
+
increased hardlink limit per file in a directory to 65536, older kernels
supported a varying number of hardlinks depending on the sum of all file name
sizes that can be stored into one metadata block
-mixed_backref::
+*mixed_backref*::
(since: 2.6.31)
+
-the last major disk format change, improved backreferences
+the last major disk format change, improved backreferences, now default
-mixed_groups::
+*mixed_groups*::
(since: 2.6.37)
+
mixed data and metadata block groups, ie. the data and metadata are not
@@ -489,17 +501,18 @@ and vice versa)
on the other hand, the final layout is quite unpredictable and possibly highly
fragmented, which means worse performance
-no_holes::
+*no_holes*::
(since: 3.14)
++
improved representation of file extents where holes are not explicitly
stored as an extent, saves a few percent of metadata if sparse files are used
-raid56::
+*raid56*::
(since: 3.9)
+
the filesystem contains or contained a raid56 profile of block groups
-+
-skinny_metadata::
+
+*skinny_metadata*::
(since: 3.10)
+
reduced-size metadata for extent references, saves a few percent of metadata
@@ -532,7 +545,7 @@ empty files.
*d*::
'no dump', makes sense with 3rd party tools like `dump`(8), on BTRFS the
-attribute can be set/unset on no other special handling is done
+attribute can be set/unset but no other special handling is done
*D*::
'synchronous directory updates', for more details search `open`(2) for 'O_SYNC'
@@ -567,7 +580,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:
+filesystem module:
* scan devices for btrfs filesystem (ie. to let multi-device filesystems mount
automatically) and register them with the kernel module
@@ -575,16 +588,16 @@ filesyste module:
for a given filesystem
* get the supported features (can be also found under '/sys/fs/btrfs/features')
-
-The device is usually created by ..., but can be created manually:
+The device is usually created by a system device node manager (eg. udev), but
+can be created manually:
--------------------
# mknod --mode=600 c 10 234 /dev/btrfs-control
--------------------
-The device is not strictly required but the device scanning will not work and a
-workaround would need to be used to mount a multi-device filesystem. The mount
-option 'device' can trigger the device scanning during mount.
+The control device is not strictly required but the device scanning will not
+work and a workaround would need to be used to mount a multi-device filesystem.
+The mount option 'device' can trigger the device scanning during mount.
SEE ALSO
--------
diff --git a/Documentation/btrfs-property.asciidoc b/Documentation/btrfs-property.asciidoc
index 8b9b7f03..05ab0bc7 100644
--- a/Documentation/btrfs-property.asciidoc
+++ b/Documentation/btrfs-property.asciidoc
@@ -28,14 +28,14 @@ A btrfs object, which is set by <object>, can be a btrfs filesystem
itself, a btrfs subvolume, an inode(file or directory) inside btrfs,
or a device on which a btrfs exists.
+
-The '-t <type>' option can be used to explicitly
+The option '-t' can be used to explicitly
specify what type of object you meant. This is only needed when a
property could be set for more then one object type.
+
Possible types are 's[ubvol]', 'f[ilesystem]', 'i[node]' and 'd[evice]'.
+
-Set the name of property by '<name>'. If no '<name>' is specified,
-all properties for the given object are printed. '<name>' is one of
+Set the name of property by 'name'. If no 'name' is specified,
+all properties for the given object are printed. 'name' is one of
the followings.
ro::::
diff --git a/Documentation/btrfs-quota.asciidoc b/Documentation/btrfs-quota.asciidoc
index 77d4c685..ef2e5d33 100644
--- a/Documentation/btrfs-quota.asciidoc
+++ b/Documentation/btrfs-quota.asciidoc
@@ -52,7 +52,7 @@ On the other hand, the traditional approach has only a poor solution to
restrict directories.
At installation time, the harddisk can be partitioned so that every directory
(eg. /usr, /var/, ...) that needs a limit gets its own partition. The obvious
-problem is, that those limits cannot be changed without a reinstall ation. The
+problem is, that those limits cannot be changed without a reinstallation. The
btrfs subvolume feature builds a bridge. Subvolumes correspond in many ways to
partitions, as every subvolume looks like its own filesystem. With subvolume
quota, it is now possible to restrict each subvolume like a partition, but keep
@@ -69,7 +69,7 @@ both! But somebody else might not want to charge the snapshots to the users.
Btrfs subvolume quota solves these problems by introducing groups of subvolumes
and let the user put limits on them. It is even possible to have groups of
-groups. In the following, we refer to them as 'qgruops'.
+groups. In the following, we refer to them as 'qgroups'.
Each qgroup primarily tracks two numbers, the amount of total referenced
space and the amount of exclusively referenced space.
@@ -84,7 +84,7 @@ from within this qgroup.
SUBVOLUME QUOTA GROUPS
~~~~~~~~~~~~~~~~~~~~~~
-The basic notion of the Subvolume Quota feature is the qouta group, short
+The basic notion of the Subvolume Quota feature is the quota group, short
qgroup. Qgroups are notated as 'level/id', eg. the qgroup 3/2 is a qgroup of
level 3. For level 0, the leading '0/' can be omitted.
Qgroups of level 0 get created automatically when a subvolume/snapshot gets
@@ -182,7 +182,7 @@ when the subvolume is deleted.
When you have several users on a machine, with home directories probably under
/home, you might want to restrict /home as a whole, while restricting every
-user to an indiviual limit as well. This is easily accomplished by creating a
+user to an individual limit as well. This is easily accomplished by creating a
qgroup for /home , eg. 1/1, and assigning all user subvolumes to it.
Restricting this qgroup will limit /home, while every user subvolume can get
its own (lower) limit.
diff --git a/Documentation/btrfs-receive.asciidoc b/Documentation/btrfs-receive.asciidoc
index a6838e5e..1f6847a9 100644
--- a/Documentation/btrfs-receive.asciidoc
+++ b/Documentation/btrfs-receive.asciidoc
@@ -23,7 +23,7 @@ previously generated by *btrfs send*. The received subvolumes are stored to
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:
+*btrfs receive* will fail in the following cases:
1. receiving subvolume already exists
@@ -31,7 +31,7 @@ the stream, and print the stream metadata, one operation per line.
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.
+A subvolume is made read-only after the receiving process finishes successfully (see BUGS below).
`Options`
@@ -66,7 +66,27 @@ 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.
+Does not require the 'path' parameter. The filesystem remains unchanged.
+
+BUGS
+----
+*btrfs receive* sets the subvolume read-only after it completes
+successfully. However, while the receive is in progress, users who have
+write access to files or directories in the receiving 'path' can add,
+remove, or modify files, in which case the resulting read-only subvolume
+will not be an exact copy of the sent subvolume.
+
+If the intention is to create an exact copy, the receiving 'path'
+should be protected from access by users until the receive operation
+has completed and the subvolume is set to read-only.
+
+Additionally, receive does not currently do a very good job of validating
+that an incremental send streams actually makes sense, and it is thus
+possible for a specially crafted send stream to create a subvolume with
+reflinks to arbitrary files in the same filesystem. Because of this,
+users are advised to not use *btrfs receive* on send streams from
+untrusted sources, and to protect trusted streams when sending them
+across untrusted networks.
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-replace.asciidoc b/Documentation/btrfs-replace.asciidoc
index a259f96d..9a8a9ea1 100644
--- a/Documentation/btrfs-replace.asciidoc
+++ b/Documentation/btrfs-replace.asciidoc
@@ -28,7 +28,7 @@ the data is built only using the RAID redundancy mechanisms.
After completion of the operation, the source device is removed from the
filesystem.
If the <srcdev> is a numerical value, it is assumed to be the device id
-of the filesystem which is mounted at <path>, otherwise is is
+of the filesystem which is mounted at <path>, otherwise it is
the path to the source device. If the source device is disconnected,
from the system, you have to use the devid parameter format.
The <targetdev> needs to be same size or larger than the <srcdev>.
diff --git a/Documentation/btrfs-rescue.asciidoc b/Documentation/btrfs-rescue.asciidoc
index 42aca645..a9b471fe 100644
--- a/Documentation/btrfs-rescue.asciidoc
+++ b/Documentation/btrfs-rescue.asciidoc
@@ -42,19 +42,19 @@ verbose mode.
*zero-log* <device>::
clear the filesystem log tree
-
++
This command will clear the filesystem log tree. This may fix a specific
set of problem when the filesystem mount fails due to the log replay. See below
for sample stacktraces that may show up in system log.
-
++
The common case where this happens has been fixed a long time ago,
so it is unlikely that you will see this particular problem, but the utility is
kept around.
-
++
NOTE: clearing the log may lead to loss of changes that were made since the
last transaction commit. This may be up to 30 seconds (default commit period)
or less if the commit was implied by other filesystem activity.
-
++
One can determine whether *zero-log* is needed according to the kernel
backtrace:
----
@@ -66,7 +66,7 @@ backtrace:
? btree_read_extent_buffer_pages+0x76/0xbc [btrfs]
? open_ctree+0xff6/0x132c [btrfs]
----
-
++
If the errors are like above, then *zero-log* should be used to clear
the log and the filesystem may be mounted normally again. The keywords to look
for are 'open_ctree' which says that it's during mount and function names
diff --git a/Documentation/btrfs-restore.asciidoc b/Documentation/btrfs-restore.asciidoc
index 41e90e2f..090dcc55 100644
--- a/Documentation/btrfs-restore.asciidoc
+++ b/Documentation/btrfs-restore.asciidoc
@@ -36,7 +36,7 @@ https://btrfs.wiki.kernel.org/index.php/Restore
OPTIONS
-------
-s|--snapshots::
-get also snapshots that are skippped by default
+get also snapshots that are skipped by default
-x|--xattr::
get extended attributes
diff --git a/Documentation/btrfs-scrub.asciidoc b/Documentation/btrfs-scrub.asciidoc
index f579c39e..eb90a1c4 100644
--- a/Documentation/btrfs-scrub.asciidoc
+++ b/Documentation/btrfs-scrub.asciidoc
@@ -25,11 +25,10 @@ default 'idle' so background scrub should not interfere with normal filesystem
operation significantly.
The scrubbing status is recorded in '/var/lib/btrfs/' in textual files named
-'scrub.status.UUID' for a filesystem identified by the given UUID. (An
-itermediate progress is communicated through a named pipe in file
-'scrub.progress.UUID' in the same directory.) The status file is updated
-periodically every 5 seconds. An resumed scrub will continue from the last
-saved position.
+'scrub.status.UUID' for a filesystem identified by the given UUID. (Progress
+state is communicated through a named pipe in file 'scrub.progress.UUID' in the
+same directory.) The status file is updated every 5 seconds. A resumed scrub
+will continue from the last saved position.
SUBCOMMAND
----------
diff --git a/Documentation/btrfs-send.asciidoc b/Documentation/btrfs-send.asciidoc
index 96659eed..ef345f68 100644
--- a/Documentation/btrfs-send.asciidoc
+++ b/Documentation/btrfs-send.asciidoc
@@ -3,7 +3,7 @@ btrfs-send(8)
NAME
----
-btrfs-send - generate a stream of changes between two subvolumes
+btrfs-send - generate a stream of changes between two subvolume snapshots
SYNOPSIS
--------
@@ -13,20 +13,21 @@ DESCRIPTION
-----------
This command will generate a stream of instructions that describe changes
-between two subvolumes. The stream can be consumed by the *btrfs receive*
-command to replicate the sent subvolume on a different filesystem.
+between two subvolume snapshots. The stream can be consumed by the *btrfs
+receive* command to replicate the sent snapshot on a different filesystem.
The command operates in two modes: full and incremental.
-All subvolumes involved in one send command must be read-only (ie. the
-read-only snapshots and this status cannot be changed if there's a running send
-operation that uses the subvolume).
+All snapshots involved in one send command must be read-only, and this status
+cannot be changed as long as there's a running send operation that uses the
+snapshot.
-In the full mode, the entire subvolume data and metadata will end up in the
+In the full mode, the entire snapshot data and metadata will end up in the
stream.
-In the incremental mode (options '-p' and '-c'), there can be one or more
-parent subvolumes that will establish the base for determining the changes.
-The final stream will be smaller compared to the full mode.
+In the incremental mode (options '-p' and '-c'), previously sent snapshots that
+are available on both the sending and receiving side can be used to reduce the
+amount of information that has to be sent to reconstruct the sent snapshot on a
+different filesystem.
It is allowed to omit the '-p <parent>' option when '-c <clone-src>' options
are given, in which case *btrfs send* will determine a suitable parent among the
@@ -45,8 +46,8 @@ send an incremental stream from 'parent' to 'subvol'
-c <clone-src>::
use this snapshot as a clone source for an incremental send (multiple allowed)
-f <outfile>::
-output is normally written to standard outout so it can be eg. piped to
-receive, use this option to write it to a file
+output is normally written to standard output so it can be, for example, piped
+to btrfs receive. Use this option to write it to a file instead.
--no-data::
send in 'NO_FILE_DATA' mode
+
@@ -58,7 +59,7 @@ useful to show the differences in metadata.
enable verbose output, print generated commands in a readable form, (each
occurrence of this option increases the verbosity level)
-q|--quiet::
-suppress all messagese except errors
+suppress all messages except errors
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc
index 3419b138..5cfe8856 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -66,10 +66,13 @@ If <subvolume> is not a subvolume, btrfs returns an error but continues if
there are more arguments to process.
+
The corresponding directory is removed instantly but the data blocks are
-removed later. The deletion does not involve full commit by default due to
-performance reasons (as a consequence, the subvolume may appear again after a
-crash). Use one of the '--commit' options to wait until the operation is safely
-stored on the media.
+removed later in the background. The command returns immediatelly. See `btrfs
+subvolume sync` how to wait until the subvolume gets completely removed.
++
+The deletion does not involve full transaction commit by default due to
+performance reasons. As a consequence, the subvolume may appear again after a
+crash. Use one of the '--commit' options to wait until the operation is
+safely stored on the device.
+
`Options`
+
diff --git a/Documentation/btrfstune.asciidoc b/Documentation/btrfstune.asciidoc
index 04295ee3..cd7bb532 100644
--- a/Documentation/btrfstune.asciidoc
+++ b/Documentation/btrfstune.asciidoc
@@ -7,7 +7,7 @@ btrfstune - tune various filesystem parameters
SYNOPSIS
--------
-*btrfstune* [options] <dev> [<dev>...]
+*btrfstune* [options] <device> [<device>...]
DESCRIPTION
-----------
@@ -29,11 +29,13 @@ OPTIONS
Enable seeding on a given device. Value 1 will enable seeding, 0 will disable it. +
A seeding filesystem is forced to be mounted read-only. A new device can be added
to the filesystem and will capture all writes keeping the seeding device intact.
+
-r::
(since kernel: 3.7)
+
Enable extended inode refs (hardlink limit per file in a directory is 65536),
enabled by mkfs feature 'extref'.
+
-x::
(since kernel: 3.10)
+
@@ -43,17 +45,21 @@ enabled by mkfs feature 'skinny-metadata'.
All newly created extents will use the new representation. To completely switch
the entire filesystem, run a full balance of the metadata. Please refer to
`btrfs-balance`(8).
+
-n::
(since kernel: 3.14)
+
Enable no-holes feature (more efficient representation of file holes), enabled
by mkfs feature 'no-holes'.
+
-f::
Allow dangerous changes, e.g. clear the seeding flag or change fsid. Make sure
that you are aware of the dangers.
+
-u::
Change fsid to a randomly generated UUID or continue previous fsid change
operation in case it was interrupted.
+
-U <UUID>::
Change fsid to 'UUID'.
+
diff --git a/Documentation/mkfs.btrfs.asciidoc b/Documentation/mkfs.btrfs.asciidoc
index 46a4d2d5..d53d9e26 100644
--- a/Documentation/mkfs.btrfs.asciidoc
+++ b/Documentation/mkfs.btrfs.asciidoc
@@ -76,8 +76,8 @@ Alias for --nodesize. Deprecated.
*-n|--nodesize <size>*::
Specify the nodesize, the tree block size in which btrfs stores metadata. The
default value is 16KiB (16384) or the page size, whichever is bigger. Must be a
-multiple of the sectorsize, but not larger than 64KiB (65536). Leafsize always
-equals nodesize and the options are aliases.
+multiple of the sectorsize and a power of 2, but not larger than 64KiB (65536).
+Leafsize always equals nodesize and the options are aliases.
+
Smaller node size increases fragmentation but lead to higher b-trees which in
turn leads to lower locking contention. Higher node sizes give better packing
diff --git a/Makefile.in b/Makefile
index 0e3a0a0f..b3e2b636 100644
--- a/Makefile.in
+++ b/Makefile
@@ -1,7 +1,6 @@
-# btrfs-progs
#
# Basic build targets:
-# all all main tools
+# all all main tools and the shared library
# static build static bnaries, requires static version of the libraries
# test run the full testsuite
# install install to default location (/usr/local)
@@ -18,6 +17,7 @@
# abort - call abort() on first error (dumps core)
# all - shortcut for all of the above
# asan - enable address sanitizer compiler feature
+# tsan - enable thread sanitizer compiler feature
# ubsan - undefined behaviour sanitizer compiler feature
# bcheck - extended build checks
# W=123 build with warnings (default: off)
@@ -38,17 +38,15 @@
# Export all variables to sub-makes by default
export
-include Makefile.extrawarn
+-include Makefile.inc
+ifneq ($(MAKEFILE_INC_INCLUDED),yes)
+$(error Makefile.inc not generated, please configure first)
+endif
-CC = @CC@
-LN_S = @LN_S@
-AR = @AR@
-RM = @RM@
-RMDIR = @RMDIR@
-INSTALL = @INSTALL@
-DISABLE_DOCUMENTATION = @DISABLE_DOCUMENTATION@
-DISABLE_BTRFSCONVERT = @DISABLE_BTRFSCONVERT@
-BTRFSCONVERT_EXT2 = @BTRFSCONVERT_EXT2@
+TAGS_CMD := ctags
+CSCOPE_CMD := cscope -u -b -c -q
+
+include Makefile.extrawarn
EXTRA_CFLAGS :=
EXTRA_LDFLAGS :=
@@ -60,7 +58,7 @@ DEBUG_CFLAGS :=
TOPDIR := $(shell pwd)
# Common build flags
-CFLAGS = @CFLAGS@ \
+CFLAGS = $(SUBST_CFLAGS) \
-include config.h \
-DBTRFS_FLAT_INCLUDES \
-D_XOPEN_SOURCE=700 \
@@ -72,17 +70,16 @@ CFLAGS = @CFLAGS@ \
$(DEBUG_CFLAGS_INTERNAL) \
$(EXTRA_CFLAGS)
-LDFLAGS = @LDFLAGS@ \
+LDFLAGS = $(SUBST_LDFLAGS) \
-rdynamic -L$(TOPDIR) $(EXTRA_LDFLAGS)
-LIBS = @UUID_LIBS@ @BLKID_LIBS@ @ZLIB_LIBS@ @LZO2_LIBS@ -L. -pthread
-LIBBTRFS_LIBS = $(LIBS)
+LIBS = $(LIBS_BASE)
+LIBBTRFS_LIBS = $(LIBS_BASE)
# Static compilation flags
STATIC_CFLAGS = $(CFLAGS) -ffunction-sections -fdata-sections
STATIC_LDFLAGS = -static -Wl,--gc-sections
-STATIC_LIBS = @UUID_LIBS_STATIC@ @BLKID_LIBS_STATIC@ \
- @ZLIB_LIBS_STATIC@ @LZO2_LIBS_STATIC@ -L. -pthread
+STATIC_LIBS = $(STATIC_LIBS_BASE)
# don't use FORTIFY with sparse because glibc with FORTIFY can
# generate so many sparse errors that sparse stops parsing,
@@ -96,34 +93,32 @@ CHECKER_FLAGS := -include $(check_defs) -D__CHECKER__ \
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 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 send-dump.o
+ qgroup.o free-space-cache.o kernel-lib/list_sort.o props.o \
+ kernel-shared/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 send-dump.o \
+ fsfeatures.o kernel-lib/tables.o kernel-lib/raid56.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
+ cmds-inspect-dump-super.o cmds-inspect-tree-stats.o cmds-fi-du.o \
+ mkfs/common.o
libbtrfs_objects = send-stream.o send-utils.o kernel-lib/rbtree.o btrfs-list.o \
- kernel-lib/crc32c.o \
+ kernel-lib/crc32c.o messages.o \
uuid-tree.o utils-lib.o rbtree-utils.o
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
+ kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
+ extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
+convert_objects = convert/main.o convert/common.o convert/source-fs.o \
+ convert/source-ext2.o
+mkfs_objects = mkfs/main.o mkfs/common.o
+
TESTS = fsck-tests.sh convert-tests.sh
udev_rules = 64-btrfs-dm.rules
-prefix ?= @prefix@
-exec_prefix = @exec_prefix@
-bindir = @bindir@
-libdir ?= @libdir@
-incdir = @includedir@/btrfs
-udevdir = @UDEVDIR@
-udevruledir = ${udevdir}/rules.d
-
ifeq ("$(origin V)", "command line")
BUILD_VERBOSE = $(V)
endif
@@ -163,6 +158,11 @@ ifneq (,$(findstring asan,$(D)))
DEBUG_CFLAGS_INTERNAL += -fsanitize=address
endif
+ifneq (,$(findstring tsan,$(D)))
+ DEBUG_CFLAGS_INTERNAL += -fsanitize=thread -fPIE
+ LD_FLAGS += -fsanitize=thread -ltsan -pie
+endif
+
ifneq (,$(findstring ubsan,$(D)))
DEBUG_CFLAGS_INTERNAL += -fsanitize=undefined
endif
@@ -193,7 +193,6 @@ endif
# external libs required by various binaries; for btrfs-foo,
# specify btrfs_foo_libs = <list of libs>; see $($(subst...)) rules below
-btrfs_convert_libs = @EXT2FS_LIBS@ @COM_ERR_LIBS@
btrfs_convert_cflags = -DBTRFSCONVERT_EXT2=$(BTRFSCONVERT_EXT2)
btrfs_fragments_libs = -lgd -lpng -ljpeg -lfreetype
btrfs_debug_tree_objects = cmds-inspect-dump-tree.o
@@ -219,11 +218,14 @@ endif
.PHONY: $(TESTDIRS)
.PHONY: $(CLEANDIRS)
.PHONY: all install clean
+.PHONY: FORCE
# Create all the static targets
static_objects = $(patsubst %.o, %.static.o, $(objects))
static_cmds_objects = $(patsubst %.o, %.static.o, $(cmds_objects))
static_libbtrfs_objects = $(patsubst %.o, %.static.o, $(libbtrfs_objects))
+static_convert_objects = $(patsubst %.o, %.static.o, $(convert_objects))
+static_mkfs_objects = $(patsubst %.o, %.static.o, $(mkfs_objects))
libs_shared = libbtrfs.so.0.1
libs_static = libbtrfs.a
@@ -265,7 +267,7 @@ endif
$(Q)$(CC) $(STATIC_CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.static.o=%)-cflags)) \
$($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags))
-all: $(progs) $(BUILDDIRS)
+all: $(progs) libbtrfs $(BUILDDIRS)
$(SUBDIRS): $(BUILDDIRS)
$(BUILDDIRS):
@echo "Making all in $(patsubst build-%,%,$@)"
@@ -276,11 +278,12 @@ test-convert: btrfs btrfs-convert
$(Q)bash tests/convert-tests.sh
test-check: test-fsck
-test-fsck: btrfs btrfs-image btrfs-corrupt-block btrfs-debug-tree mkfs.btrfs
+test-fsck: btrfs btrfs-image btrfs-corrupt-block mkfs.btrfs btrfstune
@echo " [TEST] fsck-tests.sh"
$(Q)bash tests/fsck-tests.sh
-test-misc: btrfs btrfs-image btrfs-corrupt-block btrfs-debug-tree mkfs.btrfs btrfstune
+test-misc: btrfs btrfs-image btrfs-corrupt-block mkfs.btrfs btrfstune fssum \
+ btrfs-zero-log btrfs-find-root btrfs-select-super
@echo " [TEST] misc-tests.sh"
$(Q)bash tests/misc-tests.sh
@@ -303,7 +306,7 @@ test-clean:
test-inst: all
@tmpdest=`mktemp --tmpdir -d btrfs-inst.XXXXXX` && \
echo "Test installation to $$tmpdest" && \
- $(MAKE) DESTDIR=$$tmpdest install && \
+ $(MAKE) $(MAKEOPTS) DESTDIR=$$tmpdest install && \
$(RM) -rf -- $$tmpdest
test: test-fsck test-mkfs test-convert test-misc test-fuzz test-cli
@@ -318,6 +321,16 @@ version.h: version.sh version.h.in configure.ac
@echo " [SH] $@"
$(Q)bash ./config.status --silent $@
+mktables: kernel-lib/mktables.c
+ @echo " [CC] $@"
+ $(Q)$(CC) $(CFLAGS) $< -o $@
+
+kernel-lib/tables.c: mktables
+ @echo " [TABLE] $@"
+ $(Q)./mktables > $@ || ($(RM) -f $@ && exit 1)
+
+libbtrfs: $(libs_shared) $(lib_links)
+
$(libs_shared): $(libbtrfs_objects) $(lib_links) send.h
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) $(libbtrfs_objects) $(LDFLAGS) $(LIBBTRFS_LIBS) \
@@ -341,29 +354,27 @@ $(lib_links):
# For static variants, use an extra $(subst) to get rid of the ".static"
# from the target name before translating to list of libs
-btrfs-%.static: $(static_objects) btrfs-%.static.o $(static_libbtrfs_objects) $(patsubst %.o,%.static.o,$(standalone_deps))
+btrfs-%.static: btrfs-%.static.o $(static_objects) $(patsubst %.o,%.static.o,$(standalone_deps)) $(static_libbtrfs_objects)
@echo " [LD] $@"
$(Q)$(CC) $(STATIC_CFLAGS) -o $@ $@.o $(static_objects) \
$(patsubst %.o, %.static.o, $($(subst -,_,$(subst .static,,$@)-objects))) \
$(static_libbtrfs_objects) $(STATIC_LDFLAGS) \
$($(subst -,_,$(subst .static,,$@)-libs)) $(STATIC_LIBS)
-btrfs-%: $(objects) $(libs_static) btrfs-%.o $(standalone_deps)
+btrfs-%: btrfs-%.o $(objects) $(standalone_deps) $(libs_static)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o $@ $(objects) $@.o \
$($(subst -,_,$@-objects)) \
$(libs_static) \
$(LDFLAGS) $(LIBS) $($(subst -,_,$@-libs))
-btrfs: $(objects) btrfs.o $(cmds_objects) $(libs_static)
+btrfs: btrfs.o $(objects) $(cmds_objects) $(libs_static)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o btrfs btrfs.o $(cmds_objects) \
- $(objects) $(libs_static) $(LDFLAGS) $(LIBS)
+ $(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_COMP)
-btrfs.static: $(static_objects) btrfs.static.o $(static_cmds_objects) $(static_libbtrfs_objects)
+btrfs.static: btrfs.static.o $(static_objects) $(static_cmds_objects) $(static_libbtrfs_objects)
@echo " [LD] $@"
- $(Q)$(CC) $(STATIC_CFLAGS) -o btrfs.static btrfs.static.o $(static_cmds_objects) \
- $(static_objects) $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS)
+ $(Q)$(CC) $(STATIC_CFLAGS) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS) $(STATIC_LIBS_COMP)
# For backward compatibility, 'btrfs' changes behaviour to fsck if it's named 'btrfsck'
btrfsck: btrfs
@@ -374,50 +385,45 @@ btrfsck.static: btrfs.static
@echo " [LN] $@"
$(Q)$(LN_S) -f $^ $@
-mkfs.btrfs: $(objects) $(libs_static) mkfs/main.o
+mkfs.btrfs: $(mkfs_objects) $(objects) $(libs_static)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) $(libs_static) mkfs/main.o $(LDFLAGS) $(LIBS)
+ $(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
-mkfs.btrfs.static: $(static_objects) mkfs/main.static.o $(static_libbtrfs_objects)
+mkfs.btrfs.static: $(static_mkfs_objects) $(static_objects) $(static_libbtrfs_objects)
@echo " [LD] $@"
- $(Q)$(CC) $(STATIC_CFLAGS) -o mkfs.btrfs.static mkfs/main.static.o $(static_objects) \
- $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS)
+ $(Q)$(CC) $(STATIC_CFLAGS) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS)
-btrfstune: $(objects) $(libs_static) btrfstune.o
+btrfstune: btrfstune.o $(objects) $(libs_static)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(libs_static) $(LDFLAGS) $(LIBS)
+ $(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
-btrfstune.static: $(static_objects) btrfstune.static.o $(static_libbtrfs_objects)
+btrfstune.static: btrfstune.static.o $(static_objects) $(static_libbtrfs_objects)
@echo " [LD] $@"
- $(Q)$(CC) $(STATIC_CFLAGS) -o $@ btrfstune.static.o $(static_objects) \
- $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS)
+ $(Q)$(CC) $(STATIC_CFLAGS) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS)
-btrfs-image: $(objects) $(libs_static) image/main.o
+btrfs-image: image/main.o $(objects) $(libs_static)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -I$(TOPDIR)/image -o btrfs-image $(objects) image/main.o $(libs_static) $(LDFLAGS) $(LIBS)
+ $(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_COMP)
-btrfs-image.static: $(static_objects) image/main.static.o $(static_libbtrfs_objects)
+btrfs-image.static: image/main.static.o $(static_objects) $(static_libbtrfs_objects)
@echo " [LD] $@"
- $(Q)$(CC) $(STATIC_CFLAGS) -o $@ image/main.static.o $(static_objects) \
- $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS)
+ $(Q)$(CC) $(STATIC_CFLAGS) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS) $(STATIC_LIBS_COMP)
-btrfs-convert: $(objects) $(libs_static) convert/main.o
+btrfs-convert: $(convert_objects) $(objects) $(libs_static)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -I$(TOPDIR)/convert -o btrfs-convert $(objects) convert/main.o $(libs_static) \
- $(LDFLAGS) $(btrfs_convert_libs) $(LIBS)
+ $(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(btrfs_convert_libs) $(LIBS)
-btrfs-convert.static: $(static_objects) convert/main.static.o $(static_libbtrfs_objects)
+btrfs-convert.static: $(static_convert_objects) $(static_objects) $(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)
+ $(Q)$(CC) $(STATIC_CFLAGS) -o $@ $^ $(STATIC_LDFLAGS) $(btrfs_convert_libs) $(STATIC_LIBS)
-dir-test: $(objects) $(libs) dir-test.o
+dir-test: dir-test.o $(objects) $(libs)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o dir-test $(objects) $(libs) dir-test.o $(LDFLAGS) $(LIBS)
+ $(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
-quick-test: $(objects) $(libs) quick-test.o
+quick-test: quick-test.o $(objects) $(libs)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o quick-test $(objects) $(libs) quick-test.o $(LDFLAGS) $(LIBS)
+ $(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
ioctl-test.o: ioctl-test.c ioctl.h kerncompat.h ctree.h
@echo " [CC] $@"
@@ -455,47 +461,69 @@ test-ioctl: ioctl-test ioctl-test-32 ioctl-test-64
$(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) -Wl,-rpath=$(TOPDIR) -lbtrfs
- @echo " [TEST] $@"
- $(Q)./$@
-
-library-test.static: $(libs_static) library-test.static.o
- @echo " [LD] $@"
- $(Q)$(CC) $(STATIC_CFLAGS) -o library-test.static library-test.static.o $(STATIC_LDFLAGS) $(libs_static) $(STATIC_LIBS)
- @echo " [TEST] $@"
- $(Q)./$@
+library-test: library-test.c $(libs_shared)
+ @echo " [TEST PREP] $@"$(eval TMPD=$(shell mktemp -d))
+ $(Q)mkdir -p $(TMPD)/include/btrfs && \
+ cp $(libbtrfs_headers) $(TMPD)/include/btrfs && \
+ cd $(TMPD) && $(CC) -I$(TMPD)/include -o $@ $(addprefix $(TOPDIR)/,$^) -Wl,-rpath=$(TOPDIR) -lbtrfs
+ @echo " [TEST RUN] $@"
+ $(Q)cd $(TMPD) && ./$@
+ @echo " [TEST CLEAN] $@"
+ $(Q)$(RM) -rf -- $(TMPD)
+
+library-test.static: library-test.c $(libs_static)
+ @echo " [TEST PREP] $@"$(eval TMPD=$(shell mktemp -d))
+ $(Q)mkdir -p $(TMPD)/include/btrfs && \
+ cp $(libbtrfs_headers) $(TMPD)/include/btrfs && \
+ cd $(TMPD) && $(CC) -I$(TMPD)/include -o $@ $(addprefix $(TOPDIR)/,$^) $(STATIC_LDFLAGS) $(STATIC_LIBS)
+ @echo " [TEST RUN] $@"
+ $(Q)cd $(TMPD) && ./$@
+ @echo " [TEST CLEAN] $@"
+ $(Q)$(RM) -rf -- $(TMPD)
+
+fssum: tests/fssum.c tests/sha224-256.c
+ @echo " [LD] $@"
+ $(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
test-build: test-build-pre test-build-real
test-build-pre:
- $(MAKE) clean-all
+ $(MAKE) $(MAKEOPTS) clean-all
./autogen.sh
./configure
test-build-real:
- $(MAKE) library-test
- -$(MAKE) library-test.static
- $(MAKE) -j 8 all
- -$(MAKE) -j 8 static
- $(MAKE) -j 8 $(progs_extra)
+ $(MAKE) $(MAKEOPTS) library-test
+ -$(MAKE) $(MAKEOPTS) library-test.static
+ $(MAKE) $(MAKEOPTS) -j 8 all
+ -$(MAKE) $(MAKEOPTS) -j 8 static
+ $(MAKE) $(MAKEOPTS) -j 8 $(progs_extra)
manpages:
$(Q)$(MAKE) $(MAKEOPTS) -C Documentation
+tags: FORCE
+ @echo " [TAGS] $(TAGS_CMD)"
+ $(Q)$(TAGS_CMD) *.[ch] image/*.[ch] convert/*.[ch] mkfs/*.[ch]
+
+cscope: FORCE
+ @echo " [CSCOPE] $(CSCOPE_CMD)"
+ $(Q)ls -1 *.[ch] image/*.[ch] convert/*.[ch] mkfs/*.[ch] > cscope.files
+ $(Q)$(CSCOPE_CMD)
clean-all: clean clean-doc clean-gen
clean: $(CLEANDIRS)
@echo "Cleaning"
- $(Q)$(RM) -f -- $(progs) cscope.out *.o *.o.d \
+ $(Q)$(RM) -f -- $(progs) *.o *.o.d \
kernel-lib/*.o kernel-lib/*.o.d \
+ kernel-shared/*.o kernel-shared/*.o.d \
+ kernel-lib/tables.c \
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 \
+ mktables btrfs.static mkfs.btrfs.static fssum \
$(check_defs) \
$(libs) $(lib_links) \
$(progs_static) $(progs_extra)
@@ -507,8 +535,9 @@ clean-doc:
clean-gen:
@echo "Cleaning Generated Files"
$(Q)$(RM) -rf -- version.h config.status config.cache connfig.log \
- configure.lineno config.status.lineno Makefile \
- Documentation/Makefile \
+ configure.lineno config.status.lineno Makefile.inc \
+ Documentation/Makefile tags \
+ cscope.files cscope.out cscope.in.out cscope.po.out \
config.log config.h config.h.in~ aclocal.m4 \
configure autom4te.cache/ config/
diff --git a/Makefile.inc.in b/Makefile.inc.in
new file mode 100644
index 00000000..4e1b68cb
--- /dev/null
+++ b/Makefile.inc.in
@@ -0,0 +1,36 @@
+# Variables set during configure phase:
+# * build environment, paths
+# * features (eg. convert, documentation)
+export
+
+CC = @CC@
+LN_S = @LN_S@
+AR = @AR@
+RM = @RM@
+RMDIR = @RMDIR@
+INSTALL = @INSTALL@
+DISABLE_DOCUMENTATION = @DISABLE_DOCUMENTATION@
+DISABLE_BTRFSCONVERT = @DISABLE_BTRFSCONVERT@
+BTRFSCONVERT_EXT2 = @BTRFSCONVERT_EXT2@
+
+SUBST_CFLAGS = @CFLAGS@
+SUBST_LDFLAGS = @LDFLAGS@
+
+LIBS_BASE = @UUID_LIBS@ @BLKID_LIBS@ -L. -pthread
+LIBS_COMP = @ZLIB_LIBS@ @LZO2_LIBS@
+STATIC_LIBS_BASE = @UUID_LIBS_STATIC@ @BLKID_LIBS_STATIC@ -L. -pthread
+STATIC_LIBS_COMP = @ZLIB_LIBS_STATIC@ @LZO2_LIBS_STATIC@
+
+prefix ?= @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libdir ?= @libdir@
+incdir = @includedir@/btrfs
+udevdir = @UDEVDIR@
+udevruledir = ${udevdir}/rules.d
+
+# external libs required by various binaries; for btrfs-foo,
+# specify btrfs_foo_libs = <list of libs>; see $($(subst...)) rules in Makefile
+btrfs_convert_libs = @EXT2FS_LIBS@ @COM_ERR_LIBS@
+
+MAKEFILE_INC_INCLUDED = yes
diff --git a/README.md b/README.md
index b9d3d913..7a1af26c 100644
--- a/README.md
+++ b/README.md
@@ -33,17 +33,18 @@ Reporting bugs
--------------
There are several ways, each has its own specifics and audience that can give
-feedback or work on a fix.
+feedback or work on a fix. The following list is sorted in the order of
+preference:
-* [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
+* [github issue tracker](https://github.com/kdave/btrfs-progs/issues)
* 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
+* [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
Development
diff --git a/backref.c b/backref.c
index a43e80da..ce12bbdf 100644
--- a/backref.c
+++ b/backref.c
@@ -20,7 +20,7 @@
#include "ctree.h"
#include "disk-io.h"
#include "backref.h"
-#include "ulist.h"
+#include "kernel-shared/ulist.h"
#include "transaction.h"
#include "internal.h"
@@ -450,8 +450,8 @@ static int __add_missing_keys(struct btrfs_fs_info *fs_info,
if (ref->key_for_search.type)
continue;
BUG_ON(!ref->wanted_disk_byte);
- eb = read_tree_block(fs_info->tree_root, ref->wanted_disk_byte,
- fs_info->tree_root->nodesize, 0);
+ eb = read_tree_block(fs_info, ref->wanted_disk_byte,
+ fs_info->nodesize, 0);
if (!extent_buffer_uptodate(eb)) {
free_extent_buffer(eb);
return -EIO;
@@ -804,9 +804,9 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
ref->level == 0) {
u32 bsz;
struct extent_buffer *eb;
- bsz = fs_info->extent_root->nodesize;
- eb = read_tree_block(fs_info->extent_root,
- ref->parent, bsz, 0);
+ bsz = fs_info->nodesize;
+ eb = read_tree_block(fs_info,
+ ref->parent, bsz, 0);
if (!extent_buffer_uptodate(eb)) {
free_extent_buffer(eb);
ret = -EIO;
@@ -1154,7 +1154,7 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
}
btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]);
if (found_key->type == BTRFS_METADATA_ITEM_KEY)
- size = fs_info->extent_root->nodesize;
+ size = fs_info->nodesize;
else if (found_key->type == BTRFS_EXTENT_ITEM_KEY)
size = found_key->offset;
diff --git a/backref.h b/backref.h
index 3d2ed458..b68c5047 100644
--- a/backref.h
+++ b/backref.h
@@ -19,7 +19,7 @@
#ifndef __BTRFS_BACKREF_H__
#define __BTRFS_BACKREF_H__
-#include "ulist.h"
+#include "kernel-shared/ulist.h"
#include "extent_io.h"
struct inode_fs_paths {
diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index 0e1eb524..3269de5c 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -31,6 +31,7 @@
#include "transaction.h"
#include "list.h"
#include "utils.h"
+#include "help.h"
#define FIELD_BUF_LEN 80
@@ -46,9 +47,8 @@ static int debug_corrupt_block(struct extent_buffer *eb,
length = blocksize;
while (1) {
- ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
- eb->start, &length, &multi,
- mirror_num, NULL);
+ ret = btrfs_map_block(root->fs_info, READ, eb->start, &length,
+ &multi, mirror_num, NULL);
if (ret) {
error("cannot map block %llu length %llu mirror %d: %d",
(unsigned long long)eb->start,
@@ -88,8 +88,8 @@ static int debug_corrupt_block(struct extent_buffer *eb,
fsync(eb->fd);
}
- num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
- eb->start, eb->len);
+ num_copies = btrfs_num_copies(root->fs_info, eb->start,
+ eb->len);
if (num_copies == 1)
break;
@@ -125,7 +125,7 @@ static void print_usage(int ret)
}
static void corrupt_keys(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+ struct btrfs_fs_info *fs_info,
struct extent_buffer *eb)
{
int slot;
@@ -157,28 +157,28 @@ static void corrupt_keys(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(eb);
if (!trans) {
u16 csum_size =
- btrfs_super_csum_size(root->fs_info->super_copy);
+ btrfs_super_csum_size(fs_info->super_copy);
csum_tree_block_size(eb, csum_size, 0);
write_extent_to_disk(eb);
}
}
-static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr)
+static int corrupt_keys_in_block(struct btrfs_fs_info *fs_info, u64 bytenr)
{
struct extent_buffer *eb;
- eb = read_tree_block(root, bytenr, root->nodesize, 0);
+ eb = read_tree_block(fs_info, bytenr, fs_info->nodesize, 0);
if (!extent_buffer_uptodate(eb))
return -EIO;;
- corrupt_keys(NULL, root, eb);
+ corrupt_keys(NULL, fs_info, eb);
free_extent_buffer(eb);
return 0;
}
static int corrupt_extent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 bytenr, u64 copy)
+ struct btrfs_root *root, u64 bytenr)
{
struct btrfs_key key;
struct extent_buffer *leaf;
@@ -270,13 +270,14 @@ static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
btrfs_item_key_to_cpu(eb, &key, victim);
objectid = key.objectid;
- corrupt_extent(trans, root, objectid, 1);
+ corrupt_extent(trans, root, objectid);
}
static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *eb)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
int i;
if (!eb)
@@ -295,8 +296,8 @@ static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
for (i = 0; i < btrfs_header_nritems(eb); i++) {
struct extent_buffer *next;
- next = read_tree_block(root, btrfs_node_blockptr(eb, i),
- root->nodesize,
+ next = read_tree_block(fs_info, btrfs_node_blockptr(eb, i),
+ fs_info->nodesize,
btrfs_node_ptr_generation(eb, i));
if (!extent_buffer_uptodate(next))
continue;
@@ -744,10 +745,11 @@ static void shift_items(struct btrfs_root *root, struct extent_buffer *eb)
}
}
-static int corrupt_metadata_block(struct btrfs_root *root, u64 block,
+static int corrupt_metadata_block(struct btrfs_fs_info *fs_info, u64 block,
char *field)
{
struct btrfs_trans_handle *trans;
+ struct btrfs_root *root;
struct btrfs_path *path;
struct extent_buffer *eb;
struct btrfs_key key, root_key;
@@ -763,7 +765,7 @@ static int corrupt_metadata_block(struct btrfs_root *root, u64 block,
return -EINVAL;
}
- eb = read_tree_block(root, block, root->nodesize, 0);
+ eb = read_tree_block(fs_info, block, fs_info->nodesize, 0);
if (!extent_buffer_uptodate(eb)) {
fprintf(stderr, "Couldn't read in tree block %s\n", field);
return -EINVAL;
@@ -780,7 +782,7 @@ static int corrupt_metadata_block(struct btrfs_root *root, u64 block,
root_key.type = BTRFS_ROOT_ITEM_KEY;
root_key.offset = (u64)-1;
- root = btrfs_read_fs_root(root->fs_info, &root_key);
+ root = btrfs_read_fs_root(fs_info, &root_key);
if (IS_ERR(root)) {
fprintf(stderr, "Couldn't find owner root %llu\n",
key.objectid);
@@ -1225,7 +1227,7 @@ int main(int argc, char **argv)
if (logical == (u64)-1)
print_usage(1);
trans = btrfs_start_transaction(root, 1);
- ret = corrupt_extent (trans, root, logical, 0);
+ ret = corrupt_extent(trans, root, logical);
btrfs_commit_transaction(trans, root);
goto out_close;
}
@@ -1294,7 +1296,8 @@ int main(int argc, char **argv)
if (metadata_block) {
if (*field == 0)
print_usage(1);
- ret = corrupt_metadata_block(root, metadata_block, field);
+ ret = corrupt_metadata_block(root->fs_info, metadata_block,
+ field);
goto out_close;
}
if (corrupt_di) {
@@ -1351,19 +1354,18 @@ int main(int argc, char **argv)
print_usage(1);
if (bytes == 0)
- bytes = root->sectorsize;
+ bytes = root->fs_info->sectorsize;
- bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
- bytes *= root->sectorsize;
+ bytes = round_up(bytes, root->fs_info->sectorsize);
while (bytes > 0) {
if (corrupt_block_keys) {
- corrupt_keys_in_block(root, logical);
+ corrupt_keys_in_block(root->fs_info, logical);
} else {
struct extent_buffer *eb;
eb = btrfs_find_create_tree_block(root->fs_info,
- logical, root->sectorsize);
+ logical, root->fs_info->sectorsize);
if (!eb) {
error(
"not enough memory to allocate extent buffer for bytenr %llu",
@@ -1372,12 +1374,12 @@ int main(int argc, char **argv)
goto out_close;
}
- debug_corrupt_block(eb, root, logical, root->sectorsize,
- copy);
+ debug_corrupt_block(eb, root, logical,
+ root->fs_info->sectorsize, copy);
free_extent_buffer(eb);
}
- logical += root->sectorsize;
- bytes -= root->sectorsize;
+ logical += root->fs_info->sectorsize;
+ bytes -= root->fs_info->sectorsize;
}
return ret;
out_close:
diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
index a645b406..5caae602 100644
--- a/btrfs-debug-tree.c
+++ b/btrfs-debug-tree.c
@@ -21,6 +21,7 @@
#include "utils.h"
#include "commands.h"
#include "cmds-inspect-dump-tree.h"
+#include "help.h"
int main(int argc, char **argv)
{
diff --git a/btrfs-find-root.c b/btrfs-find-root.c
index ed1540a8..e2d2e70c 100644
--- a/btrfs-find-root.c
+++ b/btrfs-find-root.c
@@ -35,8 +35,9 @@
#include "crc32c.h"
#include "extent-cache.h"
#include "find-root.h"
+#include "help.h"
-static void usage(void)
+static void find_root_usage(void)
{
fprintf(stderr, "Usage: find-roots [-a] [-o search_objectid] "
"[ -g search_generation ] [ -l search_level ] <device>\n");
@@ -179,14 +180,14 @@ int main(int argc, char **argv)
break;
case GETOPT_VAL_HELP:
default:
- usage();
+ find_root_usage();
exit(c != GETOPT_VAL_HELP);
}
}
set_argv0(argv);
if (check_argc_min(argc - optind, 1)) {
- usage();
+ find_root_usage();
exit(1);
}
diff --git a/btrfs-list.c b/btrfs-list.c
index 8eec05ea..92a537f4 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -1582,7 +1582,9 @@ int btrfs_get_subvol(int fd, struct root_info *the_ri)
rbn = rb_next(rbn);
continue;
}
- if (!comp_entry_with_rootid(the_ri, ri, 0)) {
+
+ if (!comp_entry_with_rootid(the_ri, ri, 0) ||
+ !uuid_compare(the_ri->uuid, ri->uuid)) {
memcpy(the_ri, ri, offsetof(struct root_info, path));
the_ri->path = strdup_or_null(ri->path);
the_ri->name = strdup_or_null(ri->name);
diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c
index e49a735e..9d049955 100644
--- a/btrfs-map-logical.c
+++ b/btrfs-map-logical.c
@@ -29,8 +29,9 @@
#include "transaction.h"
#include "list.h"
#include "utils.h"
+#include "help.h"
-#define BUFFER_SIZE (64 * 1024)
+#define BUFFER_SIZE SZ_64K
/* we write the mirror info to stdout unless they are dumping the data
* to stdout
@@ -81,7 +82,7 @@ again:
}
logical = key.objectid;
if (key.type == BTRFS_METADATA_ITEM_KEY)
- len = fs_info->tree_root->nodesize;
+ len = fs_info->nodesize;
else
len = key.offset;
@@ -108,9 +109,8 @@ static int __print_mapping_info(struct btrfs_fs_info *fs_info, u64 logical,
int i;
cur_len = len - cur_offset;
- ret = btrfs_map_block(&fs_info->mapping_tree, READ,
- logical + cur_offset, &cur_len,
- &multi, mirror_num, NULL);
+ ret = btrfs_map_block(fs_info, READ, logical + cur_offset,
+ &cur_len, &multi, mirror_num, NULL);
if (ret) {
fprintf(info_file,
"Error: fails to map mirror%d logical %llu: %s\n",
@@ -149,7 +149,7 @@ static int print_mapping_info(struct btrfs_fs_info *fs_info, u64 logical,
int mirror_num;
int ret = 0;
- num_copies = btrfs_num_copies(&fs_info->mapping_tree, logical, len);
+ num_copies = btrfs_num_copies(fs_info, logical, len);
for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) {
ret = __print_mapping_info(fs_info, logical, len, mirror_num);
if (ret < 0)
@@ -169,7 +169,7 @@ static int write_extent_content(struct btrfs_fs_info *fs_info, int out_fd,
while (cur_offset < length) {
cur_len = min_t(u64, length - cur_offset, BUFFER_SIZE);
- ret = read_extent_data(fs_info->tree_root, buffer,
+ ret = read_extent_data(fs_info, buffer,
logical + cur_offset, &cur_len, mirror);
if (ret < 0) {
fprintf(stderr,
@@ -285,7 +285,7 @@ int main(int argc, char **argv)
}
if (bytes == 0)
- bytes = root->nodesize;
+ bytes = root->fs_info->nodesize;
cur_logical = logical;
cur_len = bytes;
diff --git a/btrfs-select-super.c b/btrfs-select-super.c
index 7e96dc0b..e021221e 100644
--- a/btrfs-select-super.c
+++ b/btrfs-select-super.c
@@ -29,6 +29,7 @@
#include "transaction.h"
#include "list.h"
#include "utils.h"
+#include "help.h"
static void print_usage(void)
{
@@ -92,7 +93,7 @@ int main(int argc, char **argv)
/* make the super writing code think we've read the first super */
root->fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;
- ret = write_all_supers(root);
+ ret = write_all_supers(root->fs_info);
/* we don't close the ctree or anything, because we don't want a real
* transaction commit. We just want the super copy we pulled off the
diff --git a/btrfs-zero-log.c b/btrfs-zero-log.c
index 44293998..697026aa 100644
--- a/btrfs-zero-log.c
+++ b/btrfs-zero-log.c
@@ -24,6 +24,7 @@
#include "disk-io.h"
#include "transaction.h"
#include "utils.h"
+#include "help.h"
__attribute__((noreturn)) static void print_usage(void)
{
diff --git a/btrfs.c b/btrfs.c
index cc705153..2d39f2ce 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -17,11 +17,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
#include "volumes.h"
#include "crc32c.h"
#include "commands.h"
#include "utils.h"
+#include "help.h"
static const char * const btrfs_cmd_group_usage[] = {
"btrfs [--help] [--version] <group> [<group>...] <command> [<args>]",
@@ -163,24 +165,77 @@ static int cmd_version(int argc, char **argv)
return 0;
}
-static void check_options(int argc, char **argv)
+/*
+ * Parse global options, between binary name and first non-option argument
+ * after processing all valid options (including those with arguments).
+ *
+ * Returns index to argv where parsting stopped, optind is reset to 1
+ */
+static int handle_global_options(int argc, char **argv)
{
- const char *arg;
+ enum { OPT_HELP = 256, OPT_VERSION, OPT_FULL };
+ static const struct option long_options[] = {
+ { "help", no_argument, NULL, OPT_HELP },
+ { "version", no_argument, NULL, OPT_VERSION },
+ { "full", no_argument, NULL, OPT_FULL },
+ { NULL, 0, NULL, 0}
+ };
+ int shift;
if (argc == 0)
- return;
+ return 0;
- arg = argv[0];
+ opterr = 0;
+ while (1) {
+ int c;
+
+ c = getopt_long(argc, argv, "+", long_options, NULL);
+ if (c < 0)
+ break;
+
+ switch (c) {
+ case OPT_HELP: break;
+ case OPT_VERSION: break;
+ case OPT_FULL: break;
+ default:
+ fprintf(stderr, "Unknown global option: %s\n",
+ argv[optind - 1]);
+ exit(129);
+ }
+ }
- if (arg[0] != '-' ||
- !strcmp(arg, "--help") ||
- !strcmp(arg, "--version"))
- return;
+ shift = optind;
+ optind = 1;
- fprintf(stderr, "Unknown option: %s\n", arg);
- fprintf(stderr, "usage: %s\n",
- btrfs_cmd_group.usagestr[0]);
- exit(129);
+ return shift;
+}
+
+void handle_special_globals(int shift, int argc, char **argv)
+{
+ int has_help = 0;
+ int has_full = 0;
+ int i;
+
+ for (i = 0; i < shift; i++) {
+ if (strcmp(argv[i], "--help") == 0)
+ has_help = 1;
+ else if (strcmp(argv[i], "--full") == 0)
+ has_full = 1;
+ }
+
+ if (has_help) {
+ if (has_full)
+ usage_command_group(&btrfs_cmd_group, 1, 0);
+ else
+ cmd_help(argc, argv);
+ exit(0);
+ }
+
+ for (i = 0; i < shift; i++)
+ if (strcmp(argv[i], "--version") == 0) {
+ cmd_version(argc, argv);
+ exit(0);
+ }
}
static const struct cmd_group btrfs_cmd_group = {
@@ -212,6 +267,8 @@ int main(int argc, char **argv)
const char *bname;
int ret;
+ btrfs_config_init();
+
if ((bname = strrchr(argv[0], '/')) != NULL)
bname++;
else
@@ -220,13 +277,15 @@ int main(int argc, char **argv)
if (!strcmp(bname, "btrfsck")) {
argv[0] = "check";
} else {
- argc--;
- argv++;
- check_options(argc, argv);
- if (argc > 0) {
- if (!prefixcmp(argv[0], "--"))
- argv[0] += 2;
- } else {
+ int shift;
+
+ shift = handle_global_options(argc, argv);
+ handle_special_globals(shift, argc, argv);
+ while (shift-- > 0) {
+ argc--;
+ argv++;
+ }
+ if (argc == 0) {
usage_command_group_short(&btrfs_cmd_group);
exit(1);
}
diff --git a/btrfstune.c b/btrfstune.c
index e8e3d00a..1ed73a92 100644
--- a/btrfstune.c
+++ b/btrfstune.c
@@ -32,6 +32,7 @@
#include "transaction.h"
#include "utils.h"
#include "volumes.h"
+#include "help.h"
static char *device;
static int force = 0;
@@ -110,7 +111,7 @@ static int change_header_uuid(struct btrfs_root *root, struct extent_buffer *eb)
write_extent_buffer(eb, fs_info->new_chunk_tree_uuid,
btrfs_header_chunk_tree_uuid(eb),
BTRFS_UUID_SIZE);
- ret = write_tree_block(NULL, root, eb);
+ ret = write_tree_block(NULL, fs_info, eb);
return ret;
}
@@ -148,7 +149,7 @@ static int change_extents_uuid(struct btrfs_fs_info *fs_info)
goto next;
bytenr = key.objectid;
- eb = read_tree_block(root, bytenr, root->nodesize, 0);
+ eb = read_tree_block(fs_info, bytenr, fs_info->nodesize, 0);
if (IS_ERR(eb)) {
error("failed to read tree block: %llu", bytenr);
ret = PTR_ERR(eb);
@@ -176,10 +177,9 @@ out:
return ret;
}
-static int change_device_uuid(struct btrfs_root *root, struct extent_buffer *eb,
+static int change_device_uuid(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,
int slot)
{
- struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_dev_item *di;
int ret = 0;
@@ -192,7 +192,7 @@ static int change_device_uuid(struct btrfs_root *root, struct extent_buffer *eb,
write_extent_buffer(eb, fs_info->new_fsid,
(unsigned long)btrfs_device_fsid(di),
BTRFS_FSID_SIZE);
- ret = write_tree_block(NULL, root, eb);
+ ret = write_tree_block(NULL, fs_info, eb);
return ret;
}
@@ -215,7 +215,7 @@ static int change_devices_uuid(struct btrfs_fs_info *fs_info)
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(fs_info, path.nodes[0], path.slots[0]);
if (ret < 0)
goto out;
next:
@@ -242,7 +242,7 @@ static int change_fsid_prepare(struct btrfs_fs_info *fs_info)
btrfs_set_super_flags(fs_info->super_copy, flags);
memcpy(fs_info->super_copy->fsid, fs_info->new_fsid, BTRFS_FSID_SIZE);
- ret = write_all_supers(tree_root);
+ ret = write_all_supers(fs_info);
if (ret < 0)
return ret;
@@ -250,7 +250,7 @@ static int change_fsid_prepare(struct btrfs_fs_info *fs_info)
write_extent_buffer(tree_root->node, fs_info->new_chunk_tree_uuid,
btrfs_header_chunk_tree_uuid(tree_root->node),
BTRFS_UUID_SIZE);
- return write_tree_block(NULL, tree_root, tree_root->node);
+ return write_tree_block(NULL, fs_info, tree_root->node);
}
static int change_fsid_done(struct btrfs_fs_info *fs_info)
@@ -260,7 +260,7 @@ static int change_fsid_done(struct btrfs_fs_info *fs_info)
flags &= ~BTRFS_SUPER_FLAG_CHANGING_FSID;
btrfs_set_super_flags(fs_info->super_copy, flags);
- return write_all_supers(fs_info->tree_root);
+ return write_all_supers(fs_info);
}
/*
@@ -354,7 +354,7 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str)
BTRFS_FSID_SIZE);
memcpy(fs_info->super_copy->fsid, fs_info->new_fsid,
BTRFS_FSID_SIZE);
- ret = write_all_supers(fs_info->tree_root);
+ ret = write_all_supers(fs_info);
if (ret < 0)
goto out;
diff --git a/chunk-recover.c b/chunk-recover.c
index e6b26ac3..f1fa7504 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -942,11 +942,12 @@ static int build_device_map_by_chunk_record(struct btrfs_root *root,
u64 devid;
u8 uuid[BTRFS_UUID_SIZE];
u16 num_stripes;
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_mapping_tree *map_tree;
struct map_lookup *map;
struct stripe *stripe;
- map_tree = &root->fs_info->mapping_tree;
+ map_tree = &fs_info->mapping_tree;
num_stripes = chunk->num_stripes;
map = malloc(btrfs_map_lookup_size(num_stripes));
if (!map)
@@ -965,7 +966,7 @@ static int build_device_map_by_chunk_record(struct btrfs_root *root,
devid = stripe->devid;
memcpy(uuid, stripe->dev_uuid, BTRFS_UUID_SIZE);
map->stripes[i].physical = stripe->offset;
- map->stripes[i].dev = btrfs_find_device(root, devid,
+ map->stripes[i].dev = btrfs_find_device(fs_info, devid,
uuid, NULL);
if (!map->stripes[i].dev) {
free(map);
@@ -1070,7 +1071,7 @@ again:
key.type == BTRFS_METADATA_ITEM_KEY) {
old_val = btrfs_super_bytes_used(fs_info->super_copy);
if (key.type == BTRFS_METADATA_ITEM_KEY)
- old_val += root->nodesize;
+ old_val += fs_info->nodesize;
else
old_val += key.offset;
btrfs_set_super_bytes_used(fs_info->super_copy,
@@ -1086,7 +1087,7 @@ again:
if (key.objectid < end) {
if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
- key.objectid += root->sectorsize;
+ key.objectid += fs_info->sectorsize;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
}
@@ -1115,8 +1116,8 @@ static int block_group_free_all_extent(struct btrfs_root *root,
end = start + cache->key.offset - 1;
set_extent_bits(&info->block_group_cache, start, end,
- BLOCK_GROUP_DIRTY, GFP_NOFS);
- set_extent_dirty(&info->free_space_cache, start, end, GFP_NOFS);
+ BLOCK_GROUP_DIRTY);
+ set_extent_dirty(&info->free_space_cache, start, end);
btrfs_set_block_group_used(&cache->item, 0);
@@ -1163,7 +1164,7 @@ static int __rebuild_chunk_root(struct btrfs_trans_handle *trans,
btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
btrfs_set_disk_key_offset(&disk_key, min_devid);
- cow = btrfs_alloc_free_block(trans, root, root->nodesize,
+ cow = btrfs_alloc_free_block(trans, root, root->fs_info->nodesize,
BTRFS_CHUNK_TREE_OBJECTID,
&disk_key, 0, 0, 0);
btrfs_set_header_bytenr(cow, cow->start);
@@ -1286,13 +1287,14 @@ static int rebuild_chunk_tree(struct btrfs_trans_handle *trans,
static int rebuild_sys_array(struct recover_control *rc,
struct btrfs_root *root)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_chunk *chunk;
struct btrfs_key key;
struct chunk_record *chunk_rec;
int ret = 0;
u16 num_stripes;
- btrfs_set_super_sys_array_size(root->fs_info->super_copy, 0);
+ btrfs_set_super_sys_array_size(fs_info->super_copy, 0);
list_for_each_entry(chunk_rec, &rc->good_chunks, list) {
if (!(chunk_rec->type_flags & BTRFS_BLOCK_GROUP_SYSTEM))
@@ -1309,7 +1311,7 @@ static int rebuild_sys_array(struct recover_control *rc,
key.type = BTRFS_CHUNK_ITEM_KEY;
key.offset = chunk_rec->offset;
- ret = btrfs_add_system_chunk(NULL, root, &key, chunk,
+ ret = btrfs_add_system_chunk(fs_info, &key, chunk,
btrfs_chunk_item_size(num_stripes));
free(chunk);
if (ret)
@@ -1340,7 +1342,7 @@ static int calculate_bg_used(struct btrfs_root *extent_root,
found_key.type != BTRFS_EXTENT_DATA_KEY)
goto next;
if (found_key.type == BTRFS_METADATA_ITEM_KEY)
- used_ret += extent_root->nodesize;
+ used_ret += extent_root->fs_info->nodesize;
else
used_ret += found_key.offset;
next:
@@ -1448,10 +1450,6 @@ open_ctree_with_broken_chunk(struct recover_control *rc)
struct btrfs_fs_info *fs_info;
struct btrfs_super_block *disk_super;
struct extent_buffer *eb;
- u32 sectorsize;
- u32 nodesize;
- u32 leafsize;
- u32 stripesize;
int ret;
fs_info = btrfs_new_fs_info(1, BTRFS_SUPER_INFO_OFFSET);
@@ -1476,18 +1474,16 @@ open_ctree_with_broken_chunk(struct recover_control *rc)
}
memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
+ fs_info->sectorsize = btrfs_super_sectorsize(disk_super);
+ fs_info->nodesize = btrfs_super_nodesize(disk_super);
+ fs_info->stripesize = btrfs_super_stripesize(disk_super);
ret = btrfs_check_fs_compatibility(disk_super, OPEN_CTREE_WRITES);
if (ret)
goto out_devices;
- nodesize = btrfs_super_nodesize(disk_super);
- leafsize = btrfs_super_leafsize(disk_super);
- sectorsize = btrfs_super_sectorsize(disk_super);
- stripesize = btrfs_super_stripesize(disk_super);
-
- btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize,
- fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
+ btrfs_setup_root(fs_info->chunk_root, fs_info,
+ BTRFS_CHUNK_TREE_OBJECTID);
ret = build_device_maps_by_chunk_records(rc, fs_info->chunk_root);
if (ret)
@@ -1839,7 +1835,7 @@ static int next_csum(struct btrfs_root *root,
int ret = 0;
struct btrfs_root *csum_root = root->fs_info->csum_root;
struct btrfs_csum_item *csum_item;
- u32 blocksize = root->sectorsize;
+ u32 blocksize = root->fs_info->sectorsize;
u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
int csums_in_item = btrfs_item_size_nr(*leaf, *slot) / csum_size;
@@ -1911,7 +1907,7 @@ static int check_one_csum(int fd, u64 start, u32 len, u32 tree_csum)
goto out;
}
ret = 0;
- csum_result = btrfs_csum_data(NULL, data, csum_result, len);
+ csum_result = btrfs_csum_data(data, csum_result, len);
btrfs_csum_final(csum_result, (u8 *)&csum_result);
if (csum_result != tree_csum)
ret = 1;
@@ -1922,7 +1918,7 @@ out:
static u64 item_end_offset(struct btrfs_root *root, struct btrfs_key *key,
struct extent_buffer *leaf, int slot) {
- u32 blocksize = root->sectorsize;
+ u32 blocksize = root->fs_info->sectorsize;
u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
u64 offset = btrfs_item_size_nr(leaf, slot);
@@ -2012,7 +2008,7 @@ static int rebuild_raid_data_chunk_stripes(struct recover_control *rc,
u64 chunk_end = chunk->offset + chunk->length;
u64 csum_offset = 0;
u64 data_offset;
- u32 blocksize = root->sectorsize;
+ u32 blocksize = root->fs_info->sectorsize;
u32 tree_csum;
int index = 0;
int num_unordered = 0;
diff --git a/cmds-balance.c b/cmds-balance.c
index f17345ed..3cc0f62d 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -32,6 +32,7 @@
#include "commands.h"
#include "utils.h"
+#include "help.h"
static const char * const balance_cmd_group_usage[] = {
"btrfs balance <command> [options] <path>",
@@ -442,7 +443,7 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
printf("WARNING:\n\n");
printf("\tFull balance without filters requested. This operation is very\n");
printf("\tintense and takes potentially very long. It is recommended to\n");
- printf("\tuse the balance filters to narrow down the balanced data.\n");
+ printf("\tuse the balance filters to narrow down the scope of balance.\n");
printf("\tUse 'btrfs balance start --full-balance' option to skip this\n");
printf("\twarning. The operation will start in %d seconds.\n", delay);
printf("\tUse Ctrl-C to stop it.\n");
diff --git a/cmds-check.c b/cmds-check.c
index 37e5ff18..c5faa2b3 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -40,8 +40,9 @@
#include "qgroup-verify.h"
#include "rbtree-utils.h"
#include "backref.h"
-#include "ulist.h"
+#include "kernel-shared/ulist.h"
#include "hash.h"
+#include "help.h"
enum task_position {
TASK_EXTENTS,
@@ -828,7 +829,8 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
}
if (!found)
fprintf(stderr, "\tstart: 0, len: %llu\n",
- round_up(rec->isize, root->sectorsize));
+ round_up(rec->isize,
+ root->fs_info->sectorsize));
}
}
@@ -1477,8 +1479,7 @@ out:
return has_parent ? 0 : 2;
}
-static int process_dir_item(struct btrfs_root *root,
- struct extent_buffer *eb,
+static int process_dir_item(struct extent_buffer *eb,
int slot, struct btrfs_key *key,
struct shared_node *active_node)
{
@@ -1512,13 +1513,19 @@ static int process_dir_item(struct btrfs_root *root,
filetype = btrfs_dir_type(eb, di);
rec->found_size += name_len;
- if (name_len <= BTRFS_NAME_LEN) {
+ if (cur + sizeof(*di) + name_len > total ||
+ name_len > BTRFS_NAME_LEN) {
+ error = REF_ERR_NAME_TOO_LONG;
+
+ if (cur + sizeof(*di) > total)
+ break;
+ len = min_t(u32, total - cur - sizeof(*di),
+ BTRFS_NAME_LEN);
+ } else {
len = name_len;
error = 0;
- } else {
- len = BTRFS_NAME_LEN;
- error = REF_ERR_NAME_TOO_LONG;
}
+
read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len);
if (location.type == BTRFS_INODE_ITEM_KEY) {
@@ -1569,13 +1576,22 @@ static int process_inode_ref(struct extent_buffer *eb,
while (cur < total) {
name_len = btrfs_inode_ref_name_len(eb, ref);
index = btrfs_inode_ref_index(eb, ref);
- if (name_len <= BTRFS_NAME_LEN) {
+
+ /* inode_ref + namelen should not cross item boundary */
+ if (cur + sizeof(*ref) + name_len > total ||
+ name_len > BTRFS_NAME_LEN) {
+ if (total < cur + sizeof(*ref))
+ break;
+
+ /* Still try to read out the remaining part */
+ len = min_t(u32, total - cur - sizeof(*ref),
+ BTRFS_NAME_LEN);
+ error = REF_ERR_NAME_TOO_LONG;
+ } else {
len = name_len;
error = 0;
- } else {
- len = BTRFS_NAME_LEN;
- error = REF_ERR_NAME_TOO_LONG;
}
+
read_extent_buffer(eb, namebuf, (unsigned long)(ref + 1), len);
add_inode_backref(inode_cache, key->objectid, key->offset,
index, namebuf, len, 0, key->type, error);
@@ -1684,7 +1700,8 @@ static int count_csum_range(struct btrfs_root *root, u64 start,
start = key.offset;
size = btrfs_item_size_nr(leaf, path.slots[0]);
- csum_end = key.offset + (size / csum_size) * root->sectorsize;
+ csum_end = key.offset + (size / csum_size) *
+ root->fs_info->sectorsize;
if (csum_end > start) {
size = min(csum_end - start, len);
len -= size;
@@ -1711,7 +1728,7 @@ static int process_file_extent(struct btrfs_root *root,
u64 num_bytes = 0;
u64 disk_bytenr = 0;
u64 extent_offset = 0;
- u64 mask = root->sectorsize - 1;
+ u64 mask = root->fs_info->sectorsize - 1;
int extent_type;
int ret;
@@ -1835,7 +1852,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
switch (key.type) {
case BTRFS_DIR_ITEM_KEY:
case BTRFS_DIR_INDEX_KEY:
- ret = process_dir_item(root, eb, i, &key, active_node);
+ ret = process_dir_item(eb, i, &key, active_node);
break;
case BTRFS_INODE_REF_KEY:
ret = process_inode_ref(eb, i, &key, active_node);
@@ -1868,6 +1885,11 @@ static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
unsigned int ext_ref);
+/*
+ * Returns >0 Found error, not fatal, should continue
+ * Returns <0 Fatal error, must exit the whole check
+ * Returns 0 No errors found
+ */
static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path,
struct node_refs *nrefs, int *level, int ext_ref)
{
@@ -1937,19 +1959,15 @@ again:
}
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;
+ ret = err;
return ret;
}
static void reada_walk_down(struct btrfs_root *root,
struct extent_buffer *node, int slot)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
u64 bytenr;
u64 ptr_gen;
u32 nritems;
@@ -1962,11 +1980,11 @@ static void reada_walk_down(struct btrfs_root *root,
return;
nritems = btrfs_header_nritems(node);
- blocksize = root->nodesize;
+ blocksize = fs_info->nodesize;
for (i = slot; i < nritems; i++) {
bytenr = btrfs_node_blockptr(node, i);
ptr_gen = btrfs_node_ptr_generation(node, i);
- readahead_tree_block(root, bytenr, blocksize, ptr_gen);
+ readahead_tree_block(fs_info, bytenr, blocksize, ptr_gen);
}
}
@@ -1983,8 +2001,7 @@ static void reada_walk_down(struct btrfs_root *root,
* which makes leaf owner check not so strong, key check should be
* sufficient enough for that case.
*/
-static int check_child_node(struct btrfs_root *root,
- struct extent_buffer *parent, int slot,
+static int check_child_node(struct extent_buffer *parent, int slot,
struct extent_buffer *child)
{
struct btrfs_key parent_key;
@@ -2088,6 +2105,7 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
enum btrfs_tree_block_status status;
u64 bytenr;
u64 ptr_gen;
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct extent_buffer *next;
struct extent_buffer *cur;
u32 blocksize;
@@ -2139,7 +2157,7 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
}
bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
- blocksize = root->nodesize;
+ blocksize = fs_info->nodesize;
if (bytenr == nrefs->bytenr[*level - 1]) {
refs = nrefs->refs[*level - 1];
@@ -2163,11 +2181,11 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
}
}
- next = btrfs_find_tree_block(root, bytenr, blocksize);
+ next = btrfs_find_tree_block(fs_info, 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,
+ next = read_tree_block(root->fs_info, bytenr, blocksize,
ptr_gen);
if (!extent_buffer_uptodate(next)) {
struct btrfs_key node_key;
@@ -2178,14 +2196,16 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
btrfs_add_corrupt_extent_record(root->fs_info,
&node_key,
path->nodes[*level]->start,
- root->nodesize, *level);
+ root->fs_info->nodesize,
+ *level);
err = -EIO;
goto out;
}
}
- ret = check_child_node(root, cur, path->slots[*level], next);
+ ret = check_child_node(cur, path->slots[*level], next);
if (ret) {
+ free_extent_buffer(next);
err = ret;
goto out;
}
@@ -2213,12 +2233,18 @@ out:
static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
unsigned int ext_ref);
+/*
+ * Returns >0 Found error, should continue
+ * Returns <0 Fatal error, must exit the whole check
+ * Returns 0 No errors found
+ */
static int walk_down_tree_v2(struct btrfs_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 btrfs_fs_info *fs_info = root->fs_info;
struct extent_buffer *next;
struct extent_buffer *cur;
u32 blocksize;
@@ -2261,7 +2287,7 @@ static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
}
bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
- blocksize = root->nodesize;
+ blocksize = fs_info->nodesize;
ret = update_nodes_refs(root, bytenr, nrefs, *level - 1);
if (ret)
@@ -2271,11 +2297,11 @@ static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
continue;
}
- next = btrfs_find_tree_block(root, bytenr, blocksize);
+ next = btrfs_find_tree_block(fs_info, 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,
+ next = read_tree_block(fs_info, bytenr, blocksize,
ptr_gen);
if (!extent_buffer_uptodate(next)) {
struct btrfs_key node_key;
@@ -2283,16 +2309,17 @@ static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
btrfs_node_key_to_cpu(path->nodes[*level],
&node_key,
path->slots[*level]);
- btrfs_add_corrupt_extent_record(root->fs_info,
+ btrfs_add_corrupt_extent_record(fs_info,
&node_key,
path->nodes[*level]->start,
- root->nodesize, *level);
+ fs_info->nodesize,
+ *level);
ret = -EIO;
break;
}
}
- ret = check_child_node(root, cur, path->slots[*level], next);
+ ret = check_child_node(cur, path->slots[*level], next);
if (ret < 0)
break;
@@ -2534,8 +2561,6 @@ static int add_missing_dir_index(struct btrfs_root *root,
}
static int delete_dir_index(struct btrfs_root *root,
- struct cache_tree *inode_cache,
- struct inode_record *rec,
struct inode_backref *backref)
{
struct btrfs_trans_handle *trans;
@@ -2577,7 +2602,7 @@ static int delete_dir_index(struct btrfs_root *root,
static int create_inode_item(struct btrfs_root *root,
struct inode_record *rec,
- struct inode_backref *backref, int root_dir)
+ int root_dir)
{
struct btrfs_trans_handle *trans;
struct btrfs_inode_item inode_item;
@@ -2643,7 +2668,7 @@ static int repair_inode_backrefs(struct btrfs_root *root,
list_for_each_entry_safe(backref, tmp, &rec->backrefs, list) {
if (!delete && rec->ino == root_dirid) {
if (!rec->found_inode_item) {
- ret = create_inode_item(root, rec, backref, 1);
+ ret = create_inode_item(root, rec, 1);
if (ret)
break;
repaired++;
@@ -2658,12 +2683,13 @@ static int repair_inode_backrefs(struct btrfs_root *root,
((backref->found_dir_index && !backref->found_inode_ref) ||
(backref->found_dir_index && backref->found_inode_ref &&
(backref->errors & REF_ERR_INDEX_UNMATCH)))) {
- ret = delete_dir_index(root, inode_cache, rec, backref);
+ ret = delete_dir_index(root, backref);
if (ret)
break;
repaired++;
list_del(&backref->list);
free(backref);
+ continue;
}
if (!delete && !backref->found_dir_index &&
@@ -2674,12 +2700,12 @@ static int repair_inode_backrefs(struct btrfs_root *root,
break;
repaired++;
if (backref->found_dir_item &&
- backref->found_dir_index &&
backref->found_dir_index) {
if (!backref->errors &&
backref->found_inode_ref) {
list_del(&backref->list);
free(backref);
+ continue;
}
}
}
@@ -2729,7 +2755,7 @@ static int repair_inode_backrefs(struct btrfs_root *root,
backref->found_dir_item &&
!(backref->errors & REF_ERR_INDEX_UNMATCH) &&
!rec->found_inode_item)) {
- ret = create_inode_item(root, rec, backref, 0);
+ ret = create_inode_item(root, rec, 0);
if (ret)
break;
repaired++;
@@ -3213,7 +3239,8 @@ static int repair_inode_discount_extent(struct btrfs_trans_handle *trans,
/* special case for a file losing all its file extent */
if (!found) {
ret = btrfs_punch_hole(trans, root, rec->ino, 0,
- round_up(rec->isize, root->sectorsize));
+ round_up(rec->isize,
+ root->fs_info->sectorsize));
if (ret < 0)
goto out;
}
@@ -3825,8 +3852,7 @@ static int repair_btree(struct btrfs_root *root,
path.slots[level]);
/* Remove the ptr */
- ret = btrfs_del_ptr(trans, root, &path, level,
- path.slots[level]);
+ ret = btrfs_del_ptr(root, &path, level, path.slots[level]);
if (ret < 0)
goto out;
/*
@@ -3834,9 +3860,9 @@ static int repair_btree(struct btrfs_root *root,
* return value is not concerned.
*/
btrfs_release_path(&path);
- ret = btrfs_free_extent(trans, root, offset, root->nodesize,
- 0, root->root_key.objectid,
- level - 1, 0);
+ ret = btrfs_free_extent(trans, root, offset,
+ root->fs_info->nodesize, 0,
+ root->root_key.objectid, level - 1, 0);
cache = next_cache_extent(cache);
}
@@ -4223,16 +4249,22 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key,
if (imode_to_type(mode) != filetype)
goto next;
- if (name_len <= BTRFS_NAME_LEN) {
- len = name_len;
- } else {
- len = BTRFS_NAME_LEN;
+ if (cur + sizeof(*di) + name_len > total ||
+ name_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);
+ root->objectid,
+ key->type == BTRFS_DIR_ITEM_KEY ?
+ "DIR_ITEM" : "DIR_INDEX",
+ key->objectid, key->offset, name_len);
+
+ if (cur + sizeof(*di) > total)
+ break;
+ len = min_t(u32, total - cur - sizeof(*di),
+ BTRFS_NAME_LEN);
+ } else {
+ len = name_len;
}
+
read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len);
if (len != namelen || strncmp(namebuf, name, len))
goto next;
@@ -4293,12 +4325,16 @@ next:
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;
+ if (cur + sizeof(*ref) + name_len > total ||
+ name_len > BTRFS_NAME_LEN) {
warning("root %llu INODE_REF[%llu %llu] name too long",
root->objectid, ref_key->objectid, ref_key->offset);
+
+ if (total < cur + sizeof(*ref))
+ goto out;
+ len = min_t(u32, total - cur - sizeof(*ref), BTRFS_NAME_LEN);
+ } else {
+ len = name_len;
}
read_extent_buffer(node, namebuf, (unsigned long)(ref + 1), len);
@@ -4331,6 +4367,7 @@ next:
if (cur < total)
goto next;
+out:
return err;
}
@@ -4468,16 +4505,22 @@ static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key,
if (index != (u64)-1 && index != ref_index)
goto next_ref;
- if (ref_namelen <= BTRFS_NAME_LEN) {
- len = ref_namelen;
- } else {
- len = BTRFS_NAME_LEN;
+ if (cur + sizeof(*ref) + ref_namelen > total ||
+ ref_namelen > BTRFS_NAME_LEN) {
warning("root %llu INODE %s[%llu %llu] name too long",
root->objectid,
key->type == BTRFS_INODE_REF_KEY ?
"REF" : "EXTREF",
key->objectid, key->offset);
+
+ if (cur + sizeof(*ref) > total)
+ break;
+ len = min_t(u32, total - cur - sizeof(*ref),
+ BTRFS_NAME_LEN);
+ } else {
+ len = ref_namelen;
}
+
read_extent_buffer(node, ref_namebuf, (unsigned long)(ref + 1),
len);
@@ -4609,15 +4652,20 @@ static int check_dir_item(struct btrfs_root *root, struct btrfs_key *key,
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;
+ if (cur + sizeof(*di) + name_len > total ||
+ name_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);
+
+ if (cur + sizeof(*di) > total)
+ break;
+ len = min_t(u32, total - cur - sizeof(*di),
+ BTRFS_NAME_LEN);
+ } else {
+ len = name_len;
}
(*size) += name_len;
@@ -4703,25 +4751,41 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
u64 disk_bytenr;
u64 disk_num_bytes;
u64 extent_num_bytes;
- u64 found;
+ u64 extent_offset;
+ u64 csum_found; /* In byte size, sectorsize aligned */
+ u64 search_start; /* Logical range start we search for csum */
+ u64 search_len; /* Logical range len we search for csum */
unsigned int extent_type;
unsigned int is_hole;
+ int compressed = 0;
int ret;
int err = 0;
fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
+ /* Check inline extent */
extent_type = btrfs_file_extent_type(node, fi);
- /* 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)
+ compressed = btrfs_file_extent_compression(node, fi);
+ if (extent_num_bytes == 0) {
+ error(
+ "root %llu EXTENT_DATA[%llu %llu] has empty inline extent",
+ root->objectid, fkey->objectid, fkey->offset);
+ err |= FILE_EXTENT_ERROR;
+ }
+ if (!compressed && extent_num_bytes != item_inline_len) {
+ error(
+ "root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u",
+ root->objectid, fkey->objectid, fkey->offset,
+ extent_num_bytes, item_inline_len);
err |= FILE_EXTENT_ERROR;
+ }
+ *end += extent_num_bytes;
*size += extent_num_bytes;
return err;
}
@@ -4739,32 +4803,49 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
extent_num_bytes = btrfs_file_extent_num_bytes(node, fi);
+ extent_offset = btrfs_file_extent_offset(node, fi);
+ compressed = btrfs_file_extent_compression(node, fi);
is_hole = (disk_bytenr == 0) && (disk_num_bytes == 0);
- /* Check EXTENT_DATA datasum */
- ret = count_csum_range(root, disk_bytenr, disk_num_bytes, &found);
- if (found > 0 && nodatasum) {
+ /*
+ * Check EXTENT_DATA csum
+ *
+ * For plain (uncompressed) extent, we should only check the range
+ * we're referring to, as it's possible that part of prealloc extent
+ * has been written, and has csum:
+ *
+ * |<--- Original large preallocated extent A ---->|
+ * |<- Prealloc File Extent ->|<- Regular Extent ->|
+ * No csum Has csum
+ *
+ * For compressed extent, we should check the whole range.
+ */
+ if (!compressed) {
+ search_start = disk_bytenr + extent_offset;
+ search_len = extent_num_bytes;
+ } else {
+ search_start = disk_bytenr;
+ search_len = disk_num_bytes;
+ }
+ ret = count_csum_range(root, search_start, search_len, &csum_found);
+ if (csum_found > 0 && nodatasum) {
err |= ODD_CSUM_ITEM;
error("root %llu EXTENT_DATA[%llu %llu] nodatasum shouldn't have datasum",
root->objectid, fkey->objectid, fkey->offset);
} else if (extent_type == BTRFS_FILE_EXTENT_REG && !nodatasum &&
- !is_hole &&
- (ret < 0 || found == 0 || found < disk_num_bytes)) {
+ !is_hole && (ret < 0 || csum_found < search_len)) {
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) {
+ error("root %llu EXTENT_DATA[%llu %llu] csum missing, have: %llu, expected: %llu",
+ root->objectid, fkey->objectid, fkey->offset,
+ csum_found, search_len);
+ } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && csum_found > 0) {
err |= ODD_CSUM_ITEM;
- error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have datasum",
- root->objectid, fkey->objectid, fkey->offset);
+ error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu",
+ root->objectid, fkey->objectid, fkey->offset, csum_found);
}
/* Check EXTENT_DATA hole */
- if (no_holes && 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) {
+ if (!no_holes && *end != fkey->offset) {
err |= FILE_EXTENT_ERROR;
error("root %llu EXTENT_DATA[%llu %llu] interrupt",
root->objectid, fkey->objectid, fkey->offset);
@@ -4905,9 +4986,10 @@ out:
* Just a warning, as dir inode nbytes is just an
* instructive value.
*/
- if (!IS_ALIGNED(nbytes, root->nodesize)) {
+ if (!IS_ALIGNED(nbytes, root->fs_info->nodesize)) {
warning("root %llu DIR INODE[%llu] nbytes should be aligned to %u",
- root->objectid, inode_id, root->nodesize);
+ root->objectid, inode_id,
+ root->fs_info->nodesize);
}
if (isize != size) {
@@ -4965,6 +5047,8 @@ static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
if (ret > 0) {
ret = 0;
err |= INODE_ITEM_MISSING;
+ error("first inode item of root %llu is missing",
+ root->objectid);
}
err |= check_inode_item(root, &path, ext_ref);
@@ -4990,8 +5074,9 @@ 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 ret;
int level;
+ int err = 0;
/*
* We need to manually check the first inode item(256)
@@ -5025,17 +5110,21 @@ static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
}
while (1) {
- wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
- if (wret < 0)
- ret = wret;
- if (wret != 0)
+ ret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
+ err |= !!ret;
+
+ /* if ret is negative, walk shall stop */
+ if (ret < 0) {
+ ret = err;
break;
+ }
- wret = walk_up_tree_v2(root, &path, &level);
- if (wret < 0)
- ret = wret;
- if (wret != 0)
+ ret = walk_up_tree_v2(root, &path, &level);
+ if (ret != 0) {
+ /* Normal exit, reset ret to err */
+ ret = err;
break;
+ }
}
out:
@@ -5345,8 +5434,7 @@ static int free_all_extent_backrefs(struct extent_record *rec)
return 0;
}
-static void free_extent_record_cache(struct btrfs_fs_info *fs_info,
- struct cache_tree *extent_cache)
+static void free_extent_record_cache(struct cache_tree *extent_cache)
{
struct cache_extent *cache;
struct extent_record *rec;
@@ -5545,9 +5633,7 @@ static int swap_values(struct btrfs_root *root, struct btrfs_path *path,
return 0;
}
-static int fix_key_order(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *path)
+static int fix_key_order(struct btrfs_root *root, struct btrfs_path *path)
{
struct extent_buffer *buf;
struct btrfs_key k1, k2;
@@ -5575,8 +5661,7 @@ static int fix_key_order(struct btrfs_trans_handle *trans,
return ret;
}
-static int delete_bogus_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int delete_bogus_item(struct btrfs_root *root,
struct btrfs_path *path,
struct extent_buffer *buf, int slot)
{
@@ -5611,9 +5696,7 @@ static int delete_bogus_item(struct btrfs_trans_handle *trans,
return 0;
}
-static int fix_item_offset(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *path)
+static int fix_item_offset(struct btrfs_root *root, struct btrfs_path *path)
{
struct extent_buffer *buf;
int i;
@@ -5630,8 +5713,7 @@ again:
BTRFS_LEAF_DATA_SIZE(root)) {
if (btrfs_item_end_nr(buf, i) >
BTRFS_LEAF_DATA_SIZE(root)) {
- ret = delete_bogus_item(trans, root, path,
- buf, i);
+ ret = delete_bogus_item(root, path, buf, i);
if (!ret)
goto again;
fprintf(stderr, "item is off the end of the "
@@ -5645,8 +5727,7 @@ again:
btrfs_item_offset_nr(buf, i - 1)) {
if (btrfs_item_end_nr(buf, i) >
btrfs_item_offset_nr(buf, i - 1)) {
- ret = delete_bogus_item(trans, root, path,
- buf, i);
+ ret = delete_bogus_item(root, path, buf, i);
if (!ret)
goto again;
fprintf(stderr, "items overlap, can't fix\n");
@@ -5738,9 +5819,9 @@ static int try_to_fix_bad_block(struct btrfs_root *root,
break;
}
if (status == BTRFS_TREE_BLOCK_BAD_KEY_ORDER)
- ret = fix_key_order(trans, search_root, &path);
+ ret = fix_key_order(search_root, &path);
else if (status == BTRFS_TREE_BLOCK_INVALID_OFFSETS)
- ret = fix_item_offset(trans, search_root, &path);
+ ret = fix_item_offset(search_root, &path);
if (ret) {
btrfs_commit_transaction(trans, search_root);
break;
@@ -5993,6 +6074,7 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
struct extent_record *rec;
int ret = 0;
+ BUG_ON(tmpl->max_size == 0);
rec = malloc(sizeof(*rec));
if (!rec)
return -ENOMEM;
@@ -6027,7 +6109,7 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
if (tmpl->metadata)
rec->crossing_stripes = check_crossing_stripes(global_info,
- rec->start, global_info->tree_root->nodesize);
+ rec->start, global_info->nodesize);
check_extent_type(rec);
return ret;
}
@@ -6129,7 +6211,7 @@ static int add_extent_rec(struct cache_tree *extent_cache,
if (tmpl->metadata)
rec->crossing_stripes = check_crossing_stripes(
global_info, rec->start,
- global_info->tree_root->nodesize);
+ global_info->nodesize);
check_extent_type(rec);
maybe_free_extent_rec(extent_cache, rec);
return ret;
@@ -6156,6 +6238,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
tmpl.start = bytenr;
tmpl.nr = 1;
tmpl.metadata = 1;
+ tmpl.max_size = 1;
ret = add_extent_rec_nolookup(extent_cache, &tmpl);
if (ret)
@@ -6543,7 +6626,7 @@ static int process_chunk_item(struct cache_tree *chunk_cache,
* wrong onwer(3) out of chunk tree, to pass both chunk tree check
* and owner<->key_type check.
*/
- ret = btrfs_check_chunk_valid(global_info->tree_root, eb, chunk, slot,
+ ret = btrfs_check_chunk_valid(global_info, eb, chunk, slot,
key->offset);
if (ret < 0) {
error("chunk(%llu, %llu) is not valid, ignore it",
@@ -6725,14 +6808,14 @@ static int process_extent_item(struct btrfs_root *root,
if (key.type == BTRFS_METADATA_ITEM_KEY) {
metadata = 1;
- num_bytes = root->nodesize;
+ num_bytes = root->fs_info->nodesize;
} else {
num_bytes = key.offset;
}
- if (!IS_ALIGNED(key.objectid, root->sectorsize)) {
+ if (!IS_ALIGNED(key.objectid, root->fs_info->sectorsize)) {
error("ignoring invalid extent, bytenr %llu is not aligned to %u",
- key.objectid, root->sectorsize);
+ key.objectid, root->fs_info->sectorsize);
return -EIO;
}
if (item_size < sizeof(*ei)) {
@@ -6761,14 +6844,14 @@ static int process_extent_item(struct btrfs_root *root,
metadata = 1;
else
metadata = 0;
- if (metadata && num_bytes != root->nodesize) {
+ if (metadata && num_bytes != root->fs_info->nodesize) {
error("ignore invalid metadata extent, length %llu does not equal to %u",
- num_bytes, root->nodesize);
+ num_bytes, root->fs_info->nodesize);
return -EIO;
}
- if (!metadata && !IS_ALIGNED(num_bytes, root->sectorsize)) {
+ if (!metadata && !IS_ALIGNED(num_bytes, root->fs_info->sectorsize)) {
error("ignore invalid data extent, length %llu is not aligned to %u",
- num_bytes, root->sectorsize);
+ num_bytes, root->fs_info->sectorsize);
return -EIO;
}
@@ -6796,14 +6879,16 @@ static int process_extent_item(struct btrfs_root *root,
ret = add_tree_backref(extent_cache, key.objectid,
0, offset, 0);
if (ret < 0)
- error("add_tree_backref failed: %s",
+ error(
+ "add_tree_backref failed (extent items tree block): %s",
strerror(-ret));
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
ret = add_tree_backref(extent_cache, key.objectid,
offset, 0, 0);
if (ret < 0)
- error("add_tree_backref failed: %s",
+ error(
+ "add_tree_backref failed (extent items shared block): %s",
strerror(-ret));
break;
case BTRFS_EXTENT_DATA_REF_KEY:
@@ -6847,7 +6932,7 @@ static int check_cache_range(struct btrfs_root *root,
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
- ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
+ ret = btrfs_rmap_block(root->fs_info,
cache->key.objectid, bytenr, 0,
&logical, &nr, &stripe_len);
if (ret)
@@ -6974,7 +7059,7 @@ static int verify_space_cache(struct btrfs_root *root,
if (key.type == BTRFS_EXTENT_ITEM_KEY)
last = key.objectid + key.offset;
else
- last = key.objectid + root->nodesize;
+ last = key.objectid + root->fs_info->nodesize;
path.slots[0]++;
continue;
}
@@ -6986,7 +7071,7 @@ static int verify_space_cache(struct btrfs_root *root,
if (key.type == BTRFS_EXTENT_ITEM_KEY)
last = key.objectid + key.offset;
else
- last = key.objectid + root->nodesize;
+ last = key.objectid + root->fs_info->nodesize;
path.slots[0]++;
}
@@ -7036,7 +7121,7 @@ static int check_space_cache(struct btrfs_root *root)
start = cache->key.objectid + cache->key.offset;
if (!cache->free_space_ctl) {
if (btrfs_init_free_space_ctl(cache,
- root->sectorsize)) {
+ root->fs_info->sectorsize)) {
ret = -ENOMEM;
break;
}
@@ -7084,8 +7169,9 @@ static int check_extent_csums(struct btrfs_root *root, u64 bytenr,
u64 num_bytes, unsigned long leaf_offset,
struct extent_buffer *eb) {
+ struct btrfs_fs_info *fs_info = root->fs_info;
u64 offset = 0;
- u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
+ u16 csum_size = btrfs_super_csum_size(fs_info->super_copy);
char *data;
unsigned long csum_offset;
u32 csum;
@@ -7097,7 +7183,7 @@ static int check_extent_csums(struct btrfs_root *root, u64 bytenr,
int mirror;
int num_copies;
- if (num_bytes % root->sectorsize)
+ if (num_bytes % fs_info->sectorsize)
return -EINVAL;
data = malloc(num_bytes);
@@ -7109,7 +7195,7 @@ static int check_extent_csums(struct btrfs_root *root, u64 bytenr,
again:
read_len = num_bytes - offset;
/* read as much space once a time */
- ret = read_extent_data(root, data + offset,
+ ret = read_extent_data(fs_info, data + offset,
bytenr + offset, &read_len, mirror);
if (ret)
goto out;
@@ -7119,12 +7205,12 @@ again:
csum = ~(u32)0;
tmp = offset + data_checked;
- csum = btrfs_csum_data(NULL, (char *)data + tmp,
- csum, root->sectorsize);
+ csum = btrfs_csum_data((char *)data + tmp,
+ csum, fs_info->sectorsize);
btrfs_csum_final(csum, (u8 *)&csum);
csum_offset = leaf_offset +
- tmp / root->sectorsize * csum_size;
+ tmp / fs_info->sectorsize * csum_size;
read_extent_buffer(eb, (char *)&csum_expected,
csum_offset, csum_size);
/* try another mirror */
@@ -7132,15 +7218,14 @@ again:
fprintf(stderr, "mirror %d bytenr %llu csum %u expected csum %u\n",
mirror, bytenr + tmp,
csum, csum_expected);
- num_copies = btrfs_num_copies(
- &root->fs_info->mapping_tree,
+ num_copies = btrfs_num_copies(root->fs_info,
bytenr, num_bytes);
if (mirror < num_copies - 1) {
mirror += 1;
goto again;
}
}
- data_checked += root->sectorsize;
+ data_checked += fs_info->sectorsize;
}
offset += read_len;
}
@@ -7341,7 +7426,7 @@ static int check_csums(struct btrfs_root *root)
}
data_len = (btrfs_item_size_nr(leaf, path.slots[0]) /
- csum_size) * root->sectorsize;
+ csum_size) * root->fs_info->sectorsize;
if (!check_data_csum)
goto skip_csum_check;
leaf_offset = btrfs_item_ptr_offset(leaf, path.slots[0]);
@@ -7406,8 +7491,7 @@ static int is_dropped_key(struct btrfs_key *key,
* assumption and simply indicate that we _think_ that the FULL BACKREF needs to
* be set or not and then we can check later once we've gathered all the refs.
*/
-static int calc_extent_flag(struct btrfs_root *root,
- struct cache_tree *extent_cache,
+static int calc_extent_flag(struct cache_tree *extent_cache,
struct extent_buffer *buf,
struct root_item_record *ri,
u64 *flags)
@@ -7527,6 +7611,7 @@ static int run_next_block(struct btrfs_root *root,
struct device_extent_tree *dev_extent_cache,
struct root_item_record *ri)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct extent_buffer *buf;
struct extent_record *rec = NULL;
u64 bytenr;
@@ -7556,7 +7641,7 @@ static int run_next_block(struct btrfs_root *root,
continue;
/* fixme, get the parent transid */
- readahead_tree_block(root, bits[i].start,
+ readahead_tree_block(fs_info, bits[i].start,
bits[i].size, 0);
}
}
@@ -7586,7 +7671,7 @@ static int run_next_block(struct btrfs_root *root,
}
/* fixme, get the real parent transid */
- buf = read_tree_block(root, bytenr, size, gen);
+ buf = read_tree_block(root->fs_info, bytenr, size, gen);
if (!extent_buffer_uptodate(buf)) {
record_bad_block_io(root->fs_info,
extent_cache, bytenr, size);
@@ -7601,7 +7686,7 @@ static int run_next_block(struct btrfs_root *root,
btrfs_header_level(buf), 1, NULL,
&flags);
if (ret < 0) {
- ret = calc_extent_flag(root, extent_cache, buf, ri, &flags);
+ ret = calc_extent_flag(extent_cache, buf, ri, &flags);
if (ret < 0) {
fprintf(stderr, "Couldn't calc extent flags\n");
flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
@@ -7609,7 +7694,7 @@ static int run_next_block(struct btrfs_root *root,
}
} else {
flags = 0;
- ret = calc_extent_flag(root, extent_cache, buf, ri, &flags);
+ ret = calc_extent_flag(extent_cache, buf, ri, &flags);
if (ret < 0) {
fprintf(stderr, "Couldn't calc extent flags\n");
flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
@@ -7718,7 +7803,8 @@ static int run_next_block(struct btrfs_root *root,
ret = add_tree_backref(extent_cache,
key.objectid, 0, key.offset, 0);
if (ret < 0)
- error("add_tree_backref failed: %s",
+ error(
+ "add_tree_backref failed (leaf tree block): %s",
strerror(-ret));
continue;
}
@@ -7726,7 +7812,8 @@ static int run_next_block(struct btrfs_root *root,
ret = add_tree_backref(extent_cache,
key.objectid, key.offset, 0, 0);
if (ret < 0)
- error("add_tree_backref failed: %s",
+ error(
+ "add_tree_backref failed (leaf shared block): %s",
strerror(-ret));
continue;
}
@@ -7741,7 +7828,7 @@ static int run_next_block(struct btrfs_root *root,
ref),
btrfs_extent_data_ref_offset(buf, ref),
btrfs_extent_data_ref_count(buf, ref),
- 0, root->sectorsize);
+ 0, root->fs_info->sectorsize);
continue;
}
if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
@@ -7751,7 +7838,7 @@ static int run_next_block(struct btrfs_root *root,
add_data_backref(extent_cache,
key.objectid, key.offset, 0, 0, 0,
btrfs_shared_data_ref_count(buf, ref),
- 0, root->sectorsize);
+ 0, root->fs_info->sectorsize);
continue;
}
if (key.type == BTRFS_ORPHAN_ITEM_KEY) {
@@ -7783,7 +7870,7 @@ static int run_next_block(struct btrfs_root *root,
data_bytes_allocated +=
btrfs_file_extent_disk_num_bytes(buf, fi);
- if (data_bytes_allocated < root->sectorsize) {
+ if (data_bytes_allocated < root->fs_info->sectorsize) {
abort();
}
data_bytes_referenced +=
@@ -7807,7 +7894,7 @@ static int run_next_block(struct btrfs_root *root,
struct extent_record tmpl;
ptr = btrfs_node_blockptr(buf, i);
- size = root->nodesize;
+ size = root->fs_info->nodesize;
btrfs_node_key_to_cpu(buf, &key, i);
if (ri != NULL) {
if ((level == ri->drop_level)
@@ -7831,7 +7918,8 @@ static int run_next_block(struct btrfs_root *root,
ret = add_tree_backref(extent_cache, ptr, parent,
owner, 1);
if (ret < 0) {
- error("add_tree_backref failed: %s",
+ error(
+ "add_tree_backref failed (non-leaf block): %s",
strerror(-ret));
continue;
}
@@ -7968,7 +8056,7 @@ out:
static int delete_extent_records(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
- u64 bytenr, u64 new_len)
+ u64 bytenr)
{
struct btrfs_key key;
struct btrfs_key found_key;
@@ -8032,7 +8120,7 @@ static int delete_extent_records(struct btrfs_trans_handle *trans,
if (found_key.type == BTRFS_EXTENT_ITEM_KEY ||
found_key.type == BTRFS_METADATA_ITEM_KEY) {
u64 bytes = (found_key.type == BTRFS_EXTENT_ITEM_KEY) ?
- found_key.offset : root->nodesize;
+ found_key.offset : root->fs_info->nodesize;
ret = btrfs_update_block_group(trans, root, bytenr,
bytes, 0, 0);
@@ -8066,7 +8154,7 @@ static int record_extent(struct btrfs_trans_handle *trans,
if (!back->is_data)
rec->max_size = max_t(u64, rec->max_size,
- info->extent_root->nodesize);
+ info->nodesize);
if (!allocated) {
u32 item_size = sizeof(*ei);
@@ -8578,8 +8666,7 @@ out:
return ret;
}
-static int process_duplicates(struct btrfs_root *root,
- struct cache_tree *extent_cache,
+static int process_duplicates(struct cache_tree *extent_cache,
struct extent_record *rec)
{
struct extent_record *good, *tmp;
@@ -8974,7 +9061,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,
- rec->start, rec->max_size);
+ rec->start);
if (ret < 0)
goto out;
@@ -9144,7 +9231,7 @@ again:
del_ptr:
printk("deleting pointer to block %Lu\n", corrupt->cache.start);
- ret = btrfs_del_ptr(trans, info->extent_root, &path, level, slot);
+ ret = btrfs_del_ptr(info->extent_root, &path, level, slot);
out:
btrfs_release_path(&path);
@@ -9186,8 +9273,7 @@ static void reset_cached_block_groups(struct btrfs_fs_info *fs_info)
&start, &end, EXTENT_DIRTY);
if (ret)
break;
- clear_extent_dirty(&fs_info->free_space_cache, start, end,
- GFP_NOFS);
+ clear_extent_dirty(&fs_info->free_space_cache, start, end);
}
start = 0;
@@ -9221,8 +9307,7 @@ static int check_extent_refs(struct btrfs_root *root,
rec = container_of(cache, struct extent_record, cache);
set_extent_dirty(root->fs_info->excluded_extents,
rec->start,
- rec->start + rec->max_size - 1,
- GFP_NOFS);
+ rec->start + rec->max_size - 1);
cache = next_cache_extent(cache);
}
@@ -9231,8 +9316,7 @@ static int check_extent_refs(struct btrfs_root *root,
while(cache) {
set_extent_dirty(root->fs_info->excluded_extents,
cache->start,
- cache->start + cache->size - 1,
- GFP_NOFS);
+ cache->start + cache->size - 1);
cache = next_cache_extent(cache);
}
prune_corrupt_blocks(root->fs_info);
@@ -9257,7 +9341,7 @@ static int check_extent_refs(struct btrfs_root *root,
* process_duplicates() will return 0, otherwise it will return
* 1 and we
*/
- if (process_duplicates(root, extent_cache, rec))
+ if (process_duplicates(extent_cache, rec))
continue;
ret = delete_duplicate_records(root, rec);
if (ret < 0)
@@ -9358,8 +9442,7 @@ static int check_extent_refs(struct btrfs_root *root,
if (!init_extent_tree && repair && (!cur_err || fix))
clear_extent_dirty(root->fs_info->excluded_extents,
rec->start,
- rec->start + rec->max_size - 1,
- GFP_NOFS);
+ rec->start + rec->max_size - 1);
free(rec);
}
repair_abort:
@@ -9701,7 +9784,7 @@ static int deal_root_from_list(struct list_head *list,
rec = list_entry(list->next,
struct root_item_record, list);
last = 0;
- buf = read_tree_block(root->fs_info->tree_root,
+ buf = read_tree_block(root->fs_info,
rec->bytenr, rec->level_size, 0);
if (!extent_buffer_uptodate(buf)) {
free_extent_buffer(buf);
@@ -9814,14 +9897,14 @@ again:
level = btrfs_header_level(root1->node);
ret = add_root_item_to_list(&normal_trees, root1->root_key.objectid,
root1->node->start, 0, level, 0,
- root1->nodesize, NULL);
+ root1->fs_info->nodesize, NULL);
if (ret < 0)
goto out;
root1 = root->fs_info->chunk_root;
level = btrfs_header_level(root1->node);
ret = add_root_item_to_list(&normal_trees, root1->root_key.objectid,
root1->node->start, 0, level, 0,
- root1->nodesize, NULL);
+ root1->fs_info->nodesize, NULL);
if (ret < 0)
goto out;
btrfs_init_path(&path);
@@ -9852,7 +9935,7 @@ again:
last_snapshot = btrfs_root_last_snapshot(&ri);
if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) {
level = btrfs_root_level(&ri);
- level_size = root->nodesize;
+ level_size = root->fs_info->nodesize;
ret = add_root_item_to_list(&normal_trees,
found_key.objectid,
btrfs_root_bytenr(&ri),
@@ -9862,7 +9945,7 @@ again:
goto out;
} else {
level = btrfs_root_level(&ri);
- level_size = root->nodesize;
+ level_size = root->fs_info->nodesize;
objectid = found_key.objectid;
btrfs_disk_key_to_cpu(&found_key,
&ri.drop_progress);
@@ -9942,6 +10025,8 @@ out:
free_extent_cache_tree(&pending);
free_extent_cache_tree(&reada);
free_extent_cache_tree(&nodes);
+ free_root_item_list(&normal_trees);
+ free_root_item_list(&dropping_trees);
return ret;
loop:
free_corrupt_blocks_tree(root->fs_info->corrupt_blocks);
@@ -9953,7 +10038,7 @@ loop:
free_block_group_tree(&block_group_cache);
free_device_cache_tree(&dev_cache);
free_device_extent_tree(&dev_extent_cache);
- free_extent_record_cache(root->fs_info, &extent_cache);
+ free_extent_record_cache(&extent_cache);
free_root_item_list(&normal_trees);
free_root_item_list(&dropping_trees);
extent_io_tree_cleanup(&excluded_extents);
@@ -9987,7 +10072,7 @@ static int check_tree_block_ref(struct btrfs_root *root,
int slot;
int skinny_level;
int type;
- u32 nodesize = root->nodesize;
+ u32 nodesize = root->fs_info->nodesize;
u32 item_size;
u64 offset;
int tree_reloc_root = 0;
@@ -10172,20 +10257,20 @@ static int check_extent_data_item(struct btrfs_root *root,
extent_num_bytes = btrfs_file_extent_num_bytes(eb, fi);
/* Check unaligned disk_num_bytes and num_bytes */
- if (!IS_ALIGNED(disk_num_bytes, root->sectorsize)) {
+ if (!IS_ALIGNED(disk_num_bytes, root->fs_info->sectorsize)) {
error(
"file extent [%llu, %llu] has unaligned disk num bytes: %llu, should be aligned to %u",
fi_key.objectid, fi_key.offset, disk_num_bytes,
- root->sectorsize);
+ root->fs_info->sectorsize);
err |= BYTES_UNALIGNED;
} else {
data_bytes_allocated += disk_num_bytes;
}
- if (!IS_ALIGNED(extent_num_bytes, root->sectorsize)) {
+ if (!IS_ALIGNED(extent_num_bytes, root->fs_info->sectorsize)) {
error(
"file extent [%llu, %llu] has unaligned num bytes: %llu, should be aligned to %u",
fi_key.objectid, fi_key.offset, extent_num_bytes,
- root->sectorsize);
+ root->fs_info->sectorsize);
err |= BYTES_UNALIGNED;
} else {
data_bytes_referenced += extent_num_bytes;
@@ -10199,10 +10284,8 @@ static int check_extent_data_item(struct btrfs_root *root,
dbref_key.offset = btrfs_file_extent_disk_num_bytes(eb, fi);
ret = btrfs_search_slot(NULL, extent_root, &dbref_key, &path, 0, 0);
- if (ret) {
- err |= BACKREF_MISSING;
- goto error;
- }
+ if (ret)
+ goto out;
leaf = path.nodes[0];
slot = path.slots[0];
@@ -10243,11 +10326,10 @@ static int check_extent_data_item(struct btrfs_root *root,
ptr += btrfs_extent_inline_ref_size(type);
}
- /* Didn't found inlined data backref, try EXTENT_DATA_REF_KEY */
if (!found_dbackref) {
btrfs_release_path(&path);
- btrfs_init_path(&path);
+ /* Didn't find inlined data backref, try EXTENT_DATA_REF_KEY */
dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY;
dbref_key.offset = hash_extent_data_ref(root->objectid,
@@ -10255,13 +10337,32 @@ static int check_extent_data_item(struct btrfs_root *root,
ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
&dbref_key, &path, 0, 0);
- if (!ret)
+ if (!ret) {
+ found_dbackref = 1;
+ goto out;
+ }
+
+ btrfs_release_path(&path);
+
+ /*
+ * Neither inlined nor EXTENT_DATA_REF found, try
+ * SHARED_DATA_REF as last chance.
+ */
+ dbref_key.objectid = disk_bytenr;
+ dbref_key.type = BTRFS_SHARED_DATA_REF_KEY;
+ dbref_key.offset = eb->start;
+
+ ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
+ &dbref_key, &path, 0, 0);
+ if (!ret) {
found_dbackref = 1;
+ goto out;
+ }
}
+out:
if (!found_dbackref)
err |= BACKREF_MISSING;
-error:
btrfs_release_path(&path);
if (err & BACKREF_MISSING) {
error("data extent[%llu %llu] backref lost",
@@ -10329,7 +10430,7 @@ static int query_tree_block_level(struct btrfs_fs_info *fs_info, u64 bytenr)
btrfs_release_path(&path);
/* Get level from tree block as an alternative source */
- eb = read_tree_block_fs_info(fs_info, bytenr, nodesize, transid);
+ eb = read_tree_block(fs_info, bytenr, nodesize, transid);
if (!extent_buffer_uptodate(eb)) {
free_extent_buffer(eb);
return -EIO;
@@ -10382,7 +10483,7 @@ static int check_tree_block_backref(struct btrfs_fs_info *fs_info, u64 root_id,
}
/* Read out the tree block to get item/node key */
- eb = read_tree_block(root, bytenr, root->nodesize, 0);
+ eb = read_tree_block(fs_info, bytenr, root->fs_info->nodesize, 0);
if (!extent_buffer_uptodate(eb)) {
err |= REFERENCER_MISSING;
free_extent_buffer(eb);
@@ -10484,7 +10585,7 @@ static int check_shared_block_backref(struct btrfs_fs_info *fs_info,
int found_parent = 0;
int i;
- eb = read_tree_block_fs_info(fs_info, parent, nodesize, 0);
+ eb = read_tree_block(fs_info, parent, nodesize, 0);
if (!extent_buffer_uptodate(eb))
goto out;
@@ -10591,6 +10692,8 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info,
leaf = path.nodes[0];
slot = path.slots[0];
+ if (slot >= btrfs_header_nritems(leaf))
+ goto next;
btrfs_item_key_to_cpu(leaf, &key, slot);
if (key.objectid != objectid || key.type != BTRFS_EXTENT_DATA_KEY)
break;
@@ -10606,6 +10709,7 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info,
offset)
found_count++;
+next:
ret = btrfs_next_item(root, &path);
if (ret)
break;
@@ -10635,7 +10739,7 @@ static int check_shared_data_backref(struct btrfs_fs_info *fs_info,
int found_parent = 0;
int i;
- eb = read_tree_block_fs_info(fs_info, parent, nodesize, 0);
+ eb = read_tree_block(fs_info, parent, nodesize, 0);
if (!extent_buffer_uptodate(eb))
goto out;
@@ -10733,13 +10837,20 @@ static int check_extent_item(struct btrfs_fs_info *fs_info,
}
end = (unsigned long)ei + item_size;
- if (ptr >= end) {
+next:
+ /* Reached extent item end normally */
+ if (ptr == end)
+ goto out;
+
+ /* Beyond extent item end, wrong item size */
+ if (ptr > end) {
err |= ITEM_SIZE_MISMATCH;
+ error("extent item at bytenr %llu slot %d has wrong size",
+ eb->start, slot);
goto out;
}
/* Now check every backref in this extent item */
-next:
iref = (struct btrfs_extent_inline_ref *)ptr;
type = btrfs_extent_inline_ref_type(eb, iref);
offset = btrfs_extent_inline_ref_offset(eb, iref);
@@ -10776,8 +10887,7 @@ next:
}
ptr += btrfs_extent_inline_ref_size(type);
- if (ptr < end)
- goto next;
+ goto next;
out:
return err;
@@ -10817,7 +10927,12 @@ static int check_dev_extent_item(struct btrfs_fs_info *fs_info,
l = path.nodes[0];
chunk = btrfs_item_ptr(l, path.slots[0], struct btrfs_chunk);
- if (btrfs_chunk_length(l, chunk) != length)
+ ret = btrfs_check_chunk_valid(fs_info, l, chunk, path.slots[0],
+ chunk_key.offset);
+ if (ret < 0)
+ goto out;
+
+ if (btrfs_stripe_length(fs_info, l, chunk) != length)
goto out;
num_stripes = btrfs_chunk_num_stripes(l, chunk);
@@ -10878,8 +10993,10 @@ static int check_dev_item(struct btrfs_fs_info *fs_info,
/* Iterate dev_extents to calculate the used space of a device */
while (1) {
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
+ goto next;
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
if (key.objectid > dev_id)
break;
if (key.type != BTRFS_DEV_EXTENT_KEY || key.objectid != dev_id)
@@ -10976,6 +11093,11 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
/* Iterate extent tree to account used space */
while (1) {
leaf = path.nodes[0];
+
+ /* Search slot can point to the last item beyond leaf nritems */
+ if (path.slots[0] >= btrfs_header_nritems(leaf))
+ goto next;
+
btrfs_item_key_to_cpu(leaf, &extent_key, path.slots[0]);
if (extent_key.objectid >= bg_key.objectid + bg_key.offset)
break;
@@ -11048,11 +11170,10 @@ static int check_chunk_item(struct btrfs_fs_info *fs_info,
struct btrfs_block_group_item *bi;
struct btrfs_block_group_item bg_item;
struct btrfs_dev_extent *ptr;
- u32 sectorsize = btrfs_super_sectorsize(fs_info->super_copy);
u64 length;
u64 chunk_end;
+ u64 stripe_len;
u64 type;
- u64 profile;
int num_stripes;
u64 offset;
u64 objectid;
@@ -11064,25 +11185,15 @@ static int check_chunk_item(struct btrfs_fs_info *fs_info,
chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
length = btrfs_chunk_length(eb, chunk);
chunk_end = chunk_key.offset + length;
- if (!IS_ALIGNED(length, sectorsize)) {
- error("chunk[%llu %llu) not aligned to %u",
- chunk_key.offset, chunk_end, sectorsize);
- err |= BYTES_UNALIGNED;
+ ret = btrfs_check_chunk_valid(fs_info, eb, chunk, slot,
+ chunk_key.offset);
+ if (ret < 0) {
+ error("chunk[%llu %llu) is invalid", chunk_key.offset,
+ chunk_end);
+ err |= BYTES_UNALIGNED | UNKNOWN_TYPE;
goto out;
}
-
type = btrfs_chunk_type(eb, chunk);
- profile = type & BTRFS_BLOCK_GROUP_PROFILE_MASK;
- if (!(type & BTRFS_BLOCK_GROUP_TYPE_MASK)) {
- error("chunk[%llu %llu) has no chunk type",
- chunk_key.offset, chunk_end);
- err |= UNKNOWN_TYPE;
- }
- if (profile && (profile & (profile - 1))) {
- error("chunk[%llu %llu) multiple profiles detected: %llx",
- chunk_key.offset, chunk_end, profile);
- err |= UNKNOWN_TYPE;
- }
bg_key.objectid = chunk_key.offset;
bg_key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
@@ -11111,6 +11222,7 @@ static int check_chunk_item(struct btrfs_fs_info *fs_info,
}
num_stripes = btrfs_chunk_num_stripes(eb, chunk);
+ stripe_len = btrfs_stripe_length(fs_info, eb, chunk);
for (i = 0; i < num_stripes; i++) {
btrfs_release_path(&path);
btrfs_init_path(&path);
@@ -11130,7 +11242,7 @@ static int check_chunk_item(struct btrfs_fs_info *fs_info,
offset = btrfs_dev_extent_chunk_offset(leaf, ptr);
if (objectid != chunk_key.objectid ||
offset != chunk_key.offset ||
- btrfs_dev_extent_length(leaf, ptr) != length)
+ btrfs_dev_extent_length(leaf, ptr) != stripe_len)
goto not_match_dev;
continue;
not_match_dev:
@@ -11389,7 +11501,8 @@ static int traverse_tree_block(struct btrfs_root *root,
* As a btrfs tree has most 8 levels (0..7), so it's quite safe
* to call the function itself.
*/
- eb = read_tree_block(root, blocknr, root->nodesize, 0);
+ eb = read_tree_block(root->fs_info, blocknr,
+ root->fs_info->nodesize, 0);
if (extent_buffer_uptodate(eb)) {
ret = traverse_tree_block(root, eb);
err |= ret;
@@ -11480,7 +11593,7 @@ static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
goto init;
}
c = btrfs_alloc_free_block(trans, root,
- root->nodesize,
+ root->fs_info->nodesize,
root->root_key.objectid,
&disk_key, level, 0, 0);
if (IS_ERR(c)) {
@@ -11575,8 +11688,7 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
* in, but for now this doesn't actually use the root so
* just pass in extent_root.
*/
- tmp = read_tree_block(fs_info->extent_root, bytenr,
- nodesize, 0);
+ tmp = read_tree_block(fs_info, bytenr, nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading root block\n");
return -EIO;
@@ -11594,7 +11706,7 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
continue;
}
- tmp = read_tree_block(fs_info->extent_root, bytenr,
+ tmp = read_tree_block(fs_info, bytenr,
nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading tree block\n");
@@ -11676,8 +11788,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info)
key.objectid, key.offset,
btrfs_chunk_length(leaf, chunk));
set_extent_dirty(&fs_info->free_space_cache, key.offset,
- key.offset + btrfs_chunk_length(leaf, chunk),
- GFP_NOFS);
+ key.offset + btrfs_chunk_length(leaf, chunk));
path.slots[0]++;
}
start = 0;
@@ -11974,13 +12085,14 @@ static int populate_csum(struct btrfs_trans_handle *trans,
struct btrfs_root *csum_root, char *buf, u64 start,
u64 len)
{
+ struct btrfs_fs_info *fs_info = csum_root->fs_info;
u64 offset = 0;
u64 sectorsize;
int ret = 0;
while (offset < len) {
- sectorsize = csum_root->sectorsize;
- ret = read_extent_data(csum_root, buf, start + offset,
+ sectorsize = fs_info->sectorsize;
+ ret = read_extent_data(fs_info, buf, start + offset,
&sectorsize, 0);
if (ret)
break;
@@ -12007,7 +12119,7 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans,
int slot = 0;
int ret = 0;
- buf = malloc(cur_root->fs_info->csum_root->sectorsize);
+ buf = malloc(cur_root->fs_info->sectorsize);
if (!buf)
return -ENOMEM;
@@ -12139,7 +12251,7 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans,
return ret;
}
- buf = malloc(csum_root->sectorsize);
+ buf = malloc(csum_root->fs_info->sectorsize);
if (!buf) {
btrfs_release_path(&path);
return -ENOMEM;
@@ -12340,8 +12452,7 @@ out:
return ret;
}
-static int maybe_repair_root_item(struct btrfs_fs_info *info,
- struct btrfs_path *path,
+static int maybe_repair_root_item(struct btrfs_path *path,
const struct btrfs_key *root_key,
const int read_only_mode)
{
@@ -12496,8 +12607,7 @@ again:
if (found_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
goto next;
- ret = maybe_repair_root_item(info, &path, &found_key,
- trans ? 0 : 1);
+ ret = maybe_repair_root_item(&path, &found_key, trans ? 0 : 1);
if (ret < 0)
goto out;
if (ret) {
@@ -12923,8 +13033,10 @@ int cmd_check(int argc, char **argv)
ret = repair_root_items(info);
err |= !!ret;
- if (ret < 0)
+ if (ret < 0) {
+ error("failed to repair root items: %s", strerror(-ret));
goto close_out;
+ }
if (repair) {
fprintf(stderr, "Fixed %d roots.\n", ret);
ret = 0;
@@ -12947,8 +13059,13 @@ int cmd_check(int argc, char **argv)
}
ret = check_space_cache(root);
err |= !!ret;
- if (ret)
+ if (ret) {
+ if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
+ error("errors found in free space tree");
+ else
+ error("errors found in free space cache");
goto out;
+ }
/*
* We used to have to have these hole extents in between our real
@@ -12964,22 +13081,28 @@ int cmd_check(int argc, char **argv)
else
ret = check_fs_roots(root, &root_cache);
err |= !!ret;
- if (ret)
+ if (ret) {
+ error("errors found in fs roots");
goto out;
+ }
fprintf(stderr, "checking csums\n");
ret = check_csums(root);
err |= !!ret;
- if (ret)
+ if (ret) {
+ error("errors found in csum tree");
goto out;
+ }
fprintf(stderr, "checking root refs\n");
/* 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)
+ if (ret) {
+ error("errors found in root refs");
goto out;
+ }
}
while (repair && !list_empty(&root->fs_info->recow_ebs)) {
@@ -12990,8 +13113,10 @@ int cmd_check(int argc, char **argv)
list_del_init(&eb->recow);
ret = recow_extent_buffer(root, eb);
err |= !!ret;
- if (ret)
+ if (ret) {
+ error("fails to fix transid errors");
break;
+ }
}
while (!list_empty(&delete_items)) {
@@ -13010,13 +13135,17 @@ int cmd_check(int argc, char **argv)
fprintf(stderr, "checking quota groups\n");
ret = qgroup_verify_all(info);
err |= !!ret;
- if (ret)
+ if (ret) {
+ error("failed to check quota groups");
goto out;
+ }
report_qgroups(0);
ret = repair_qgroups(info, &qgroups_repaired);
err |= !!ret;
- if (err)
+ if (err) {
+ error("failed to repair quota groups");
goto out;
+ }
ret = 0;
}
@@ -13037,8 +13166,12 @@ out:
"backup data and re-format the FS. *\n\n");
err |= 1;
}
- printf("found %llu bytes used err is %d\n",
- (unsigned long long)bytes_used, ret);
+ printf("found %llu bytes used, ",
+ (unsigned long long)bytes_used);
+ if (err)
+ printf("error(s) found\n");
+ else
+ printf("no error found\n");
printf("total csum bytes: %llu\n",(unsigned long long)total_csum_bytes);
printf("total tree bytes: %llu\n",
(unsigned long long)total_btree_bytes);
diff --git a/cmds-device.c b/cmds-device.c
index de62cd42..4337eb27 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -32,6 +32,8 @@
#include "cmds-fi-usage.h"
#include "commands.h"
+#include "help.h"
+#include "mkfs/common.h"
static const char * const device_cmd_group_usage[] = {
"btrfs device <command> [<args>]",
@@ -398,6 +400,7 @@ static int cmd_device_stats(int argc, char **argv)
while (1) {
int c;
static const struct option long_options[] = {
+ {"check", no_argument, NULL, 'c'},
{"reset", no_argument, NULL, 'z'},
{NULL, 0, NULL, 0}
};
@@ -533,8 +536,8 @@ static int _cmd_device_usage(int fd, char *path, unsigned unit_mode)
for (i = 0; i < devcount; i++) {
printf("%s, ID: %llu\n", devinfo[i].path, devinfo[i].devid);
- print_device_sizes(fd, &devinfo[i], unit_mode);
- print_device_chunks(fd, &devinfo[i], chunkinfo, chunkcount,
+ print_device_sizes(&devinfo[i], unit_mode);
+ print_device_chunks(&devinfo[i], chunkinfo, chunkcount,
unit_mode);
printf("\n");
}
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
index 2fc11945..f106f45b 100644
--- a/cmds-fi-du.c
+++ b/cmds-fi-du.c
@@ -40,6 +40,8 @@
#include "rbtree.h"
#include "interval_tree_generic.h"
+#include "help.h"
+#include "fsfeatures.h"
static int summarize = 0;
static unsigned unit_mode = UNITS_RAW;
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 88e346ad..101a0c4e 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -31,6 +31,7 @@
#include "commands.h"
#include "version.h"
+#include "help.h"
/*
* Add the chunk info to the chunk_info list
@@ -285,7 +286,7 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
* which compose the chunk, which could be different from the number of devices
* if a disk is added later.
*/
-static void get_raid56_used(int fd, struct chunk_info *chunks, int chunkcount,
+static void get_raid56_used(struct chunk_info *chunks, int chunkcount,
u64 *raid5_used, u64 *raid6_used)
{
struct chunk_info *info_ptr = chunks;
@@ -301,7 +302,7 @@ static void get_raid56_used(int fd, struct chunk_info *chunks, int chunkcount,
}
}
-#define MIN_UNALOCATED_THRESH (16 * 1024 * 1024)
+#define MIN_UNALOCATED_THRESH SZ_16M
static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
int chunkcount, struct device_info *devinfo, int devcount,
char *path, unsigned unit_mode)
@@ -359,7 +360,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
ret = 1;
goto exit;
}
- get_raid56_used(fd, chunkinfo, chunkcount, &raid5_used, &raid6_used);
+ get_raid56_used(chunkinfo, chunkcount, &raid5_used, &raid6_used);
for (i = 0; i < sargs->total_spaces; i++) {
int ratio;
@@ -996,7 +997,7 @@ out:
return !!ret;
}
-void print_device_chunks(int fd, struct device_info *devinfo,
+void print_device_chunks(struct device_info *devinfo,
struct chunk_info *chunks_info_ptr,
int chunks_info_count, unsigned unit_mode)
{
@@ -1032,7 +1033,7 @@ void print_device_chunks(int fd, struct device_info *devinfo,
unit_mode | UNITS_NEGATIVE));
}
-void print_device_sizes(int fd, struct device_info *devinfo, unsigned unit_mode)
+void print_device_sizes(struct device_info *devinfo, unsigned unit_mode)
{
printf(" Device size: %*s%10s\n",
(int)(20 - strlen("Device size")), "",
diff --git a/cmds-fi-usage.h b/cmds-fi-usage.h
index adb8ad30..f967ba8b 100644
--- a/cmds-fi-usage.h
+++ b/cmds-fi-usage.h
@@ -49,9 +49,9 @@ struct chunk_info {
int load_chunk_and_device_info(int fd, struct chunk_info **chunkinfo,
int *chunkcount, struct device_info **devinfo, int *devcount);
-void print_device_chunks(int fd, struct device_info *devinfo,
+void print_device_chunks(struct device_info *devinfo,
struct chunk_info *chunks_info_ptr,
int chunks_info_count, unsigned unit_mode);
-void print_device_sizes(int fd, struct device_info *devinfo, unsigned unit_mode);
+void print_device_sizes(struct device_info *devinfo, unsigned unit_mode);
#endif
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index e7d31364..e2e41e91 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -38,6 +38,7 @@
#include "list_sort.h"
#include "disk-io.h"
#include "cmds-fi-du.h"
+#include "help.h"
/*
* for btrfs fi show, we maintain a hash of fsids we've already printed.
@@ -1030,7 +1031,7 @@ static int cmd_filesystem_defrag(int argc, char **argv)
* better results and is independent of the kernel default. We have to
* use the v2 defrag ioctl.
*/
- thresh = 32 * 1024 * 1024;
+ thresh = SZ_32M;
defrag_global_errors = 0;
defrag_global_verbose = 0;
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
index ba0d708e..98e0270a 100644
--- a/cmds-inspect-dump-super.c
+++ b/cmds-inspect-dump-super.c
@@ -34,13 +34,14 @@
#include "commands.h"
#include "crc32c.h"
#include "cmds-inspect-dump-super.h"
+#include "help.h"
static int check_csum_sblock(void *sb, int csum_size)
{
u8 result[BTRFS_CSUM_SIZE];
u32 crc = ~(u32)0;
- crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE,
+ crc = btrfs_csum_data((char *)sb + BTRFS_CSUM_SIZE,
crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
btrfs_csum_final(crc, result);
@@ -64,13 +65,21 @@ static void print_sys_chunk_array(struct btrfs_super_block *sb)
buf = malloc(sizeof(*buf) + sizeof(*sb));
if (!buf) {
error("not enough memory");
- goto out;
+ return;
}
write_extent_buffer(buf, sb, 0, sizeof(*sb));
+ buf->len = sizeof(*sb);
array_size = btrfs_super_sys_array_size(sb);
array_ptr = sb->sys_chunk_array;
sb_array_offset = offsetof(struct btrfs_super_block, sys_chunk_array);
+
+ if (array_size > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
+ error("sys_array_size %u shouldn't exceed %u bytes",
+ array_size, BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
+ goto out;
+ }
+
cur_offset = 0;
item = 0;
@@ -123,8 +132,8 @@ static void print_sys_chunk_array(struct btrfs_super_block *sb)
item++;
}
- free(buf);
out:
+ free(buf);
return;
out_short_read:
@@ -388,8 +397,8 @@ static void dump_superblock(struct btrfs_super_block *sb, int full)
(unsigned long long)btrfs_super_sectorsize(sb));
printf("nodesize\t\t%llu\n",
(unsigned long long)btrfs_super_nodesize(sb));
- printf("leafsize\t\t%llu\n",
- (unsigned long long)btrfs_super_leafsize(sb));
+ printf("leafsize (deprecated)\t\t%u\n",
+ le32_to_cpu(sb->__unused_leafsize));
printf("stripesize\t\t%llu\n",
(unsigned long long)btrfs_super_stripesize(sb));
printf("root_dir\t\t%llu\n",
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
index df7be617..93dff086 100644
--- a/cmds-inspect-dump-tree.c
+++ b/cmds-inspect-dump-tree.c
@@ -32,6 +32,7 @@
#include "commands.h"
#include "utils.h"
#include "cmds-inspect-dump-tree.h"
+#include "help.h"
static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
{
@@ -48,10 +49,11 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
return;
}
- size = root->nodesize;
+ size = root->fs_info->nodesize;
nr = btrfs_header_nritems(eb);
for (i = 0; i < nr; i++) {
- next = read_tree_block(root, btrfs_node_blockptr(eb, i),
+ next = read_tree_block(root->fs_info,
+ btrfs_node_blockptr(eb, i),
size, btrfs_node_ptr_generation(eb, i));
if (!extent_buffer_uptodate(next))
continue;
@@ -310,9 +312,9 @@ int cmd_inspect_dump_tree(int argc, char **argv)
}
if (block_only) {
- leaf = read_tree_block(root,
+ leaf = read_tree_block(info,
block_only,
- root->nodesize, 0);
+ info->nodesize, 0);
if (extent_buffer_uptodate(leaf) &&
btrfs_header_level(leaf) != 0) {
@@ -321,9 +323,9 @@ int cmd_inspect_dump_tree(int argc, char **argv)
}
if (!leaf) {
- leaf = read_tree_block(root,
+ leaf = read_tree_block(info,
block_only,
- root->nodesize, 0);
+ info->nodesize, 0);
}
if (!extent_buffer_uptodate(leaf)) {
error("failed to read %llu",
@@ -343,6 +345,11 @@ int cmd_inspect_dump_tree(int argc, char **argv)
printf("chunk tree: %llu level %d\n",
(unsigned long long)info->chunk_root->node->start,
btrfs_header_level(info->chunk_root->node));
+ if (info->log_root_tree)
+ printf("log root tree: %llu level %d\n",
+ info->log_root_tree->node->start,
+ btrfs_header_level(
+ info->log_root_tree->node));
} else {
if (info->tree_root->node) {
printf("root tree\n");
@@ -355,6 +362,12 @@ int cmd_inspect_dump_tree(int argc, char **argv)
btrfs_print_tree(info->chunk_root,
info->chunk_root->node, 1);
}
+
+ if (info->log_root_tree) {
+ printf("log root tree\n");
+ btrfs_print_tree(info->log_root_tree,
+ info->log_root_tree->node, 1);
+ }
}
}
tree_root_scan = info->tree_root;
@@ -370,21 +383,32 @@ again:
if (tree_id && tree_id == BTRFS_ROOT_TREE_OBJECTID) {
if (!info->tree_root->node) {
error("cannot print root tree, invalid pointer");
- goto no_node;
+ goto close_root;
}
printf("root tree\n");
btrfs_print_tree(info->tree_root, info->tree_root->node, 1);
- goto no_node;
+ goto close_root;
}
if (tree_id && tree_id == BTRFS_CHUNK_TREE_OBJECTID) {
if (!info->chunk_root->node) {
error("cannot print chunk tree, invalid pointer");
- goto no_node;
+ goto close_root;
}
printf("chunk tree\n");
btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1);
- goto no_node;
+ goto close_root;
+ }
+
+ if (tree_id && tree_id == BTRFS_TREE_LOG_OBJECTID) {
+ if (!info->log_root_tree) {
+ error("cannot print log root tree, invalid pointer");
+ goto close_root;
+ }
+ printf("log root tree\n");
+ btrfs_print_tree(info->log_root_tree, info->log_root_tree->node,
+ 1);
+ goto close_root;
}
key.offset = 0;
@@ -416,10 +440,8 @@ again:
offset = btrfs_item_ptr_offset(leaf, slot);
read_extent_buffer(leaf, &ri, offset, sizeof(ri));
- buf = read_tree_block(tree_root_scan,
- btrfs_root_bytenr(&ri),
- tree_root_scan->nodesize,
- 0);
+ buf = read_tree_block(info, btrfs_root_bytenr(&ri),
+ info->nodesize, 0);
if (!extent_buffer_uptodate(buf))
goto next;
if (tree_id && found_key.objectid != tree_id) {
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
index 0e2786c9..05f4f616 100644
--- a/cmds-inspect-tree-stats.c
+++ b/cmds-inspect-tree-stats.c
@@ -36,6 +36,7 @@
#include "utils.h"
#include "commands.h"
#include "cmds-inspect-tree-stats.h"
+#include "help.h"
static int verbose = 0;
static int no_pretty = 0;
@@ -104,7 +105,7 @@ static int walk_leaf(struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_key found_key;
int i;
- stat->total_bytes += root->nodesize;
+ stat->total_bytes += root->fs_info->nodesize;
stat->total_leaves++;
if (!find_inline)
@@ -136,12 +137,13 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
struct root_stats *stat, int level, int find_inline)
{
struct extent_buffer *b = path->nodes[level];
+ u32 nodesize = root->fs_info->nodesize;
u64 last_block;
- u64 cluster_size = root->nodesize;
+ u64 cluster_size = nodesize;
int i;
int ret = 0;
- stat->total_bytes += root->nodesize;
+ stat->total_bytes += nodesize;
stat->total_nodes++;
last_block = btrfs_header_bytenr(b);
@@ -151,8 +153,8 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
path->slots[level] = i;
if ((level - 1) > 0 || find_inline) {
- tmp = read_tree_block(root, cur_blocknr,
- root->nodesize,
+ tmp = read_tree_block(root->fs_info, cur_blocknr,
+ nodesize,
btrfs_node_ptr_generation(b, i));
if (!extent_buffer_uptodate(tmp)) {
error("failed to read blocknr %llu",
@@ -166,9 +168,9 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
find_inline);
else
ret = walk_leaf(root, path, stat, find_inline);
- if (last_block + root->nodesize != cur_blocknr) {
+ if (last_block + nodesize != cur_blocknr) {
u64 distance = calc_distance(last_block +
- root->nodesize,
+ nodesize,
cur_blocknr);
stat->total_seeks++;
stat->total_seek_len += distance;
@@ -185,7 +187,7 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
stat->forward_seeks++;
else
stat->backward_seeks++;
- if (cluster_size != root->nodesize) {
+ if (cluster_size != nodesize) {
stat->total_cluster_size += cluster_size;
stat->total_clusters++;
if (cluster_size < stat->min_cluster_size)
@@ -193,9 +195,9 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
if (cluster_size > stat->max_cluster_size)
stat->max_cluster_size = cluster_size;
}
- cluster_size = root->nodesize;
+ cluster_size = nodesize;
} else {
- cluster_size += root->nodesize;
+ cluster_size += nodesize;
}
last_block = cur_blocknr;
if (cur_blocknr < stat->lowest_bytenr)
@@ -333,7 +335,7 @@ static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
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;
+ stat.max_cluster_size = root->fs_info->nodesize;
path.nodes[level] = root->node;
if (gettimeofday(&start, NULL)) {
error("cannot get time: %s", strerror(errno));
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 5e58a284..58b59560 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -34,6 +34,7 @@
#include "cmds-inspect-dump-tree.h"
#include "cmds-inspect-dump-super.h"
#include "cmds-inspect-tree-stats.h"
+#include "help.h"
static const char * const inspect_cmd_group_usage[] = {
"btrfs inspect-internal <command> <args>",
@@ -173,7 +174,7 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
if (check_argc_exact(argc - optind, 2))
usage(cmd_inspect_logical_resolve_usage);
- size = min(size, (u64)64 * 1024);
+ size = min(size, (u64)SZ_64K);
inodes = malloc(size);
if (!inodes)
return 1;
@@ -486,7 +487,7 @@ static void adjust_dev_min_size(struct list_head *extents,
* chunk tree, so often this can lead to the need of allocating
* a new system chunk too, which has a maximum size of 32Mb.
*/
- *min_size += 32 * 1024 * 1024;
+ *min_size += SZ_32M;
}
}
@@ -500,7 +501,7 @@ static int print_min_dev_size(int fd, u64 devid)
* possibility of deprecating/removing it has been discussed, so we
* ignore it here.
*/
- u64 min_size = 1 * 1024 * 1024ull;
+ u64 min_size = SZ_1M;
struct btrfs_ioctl_search_args args;
struct btrfs_ioctl_search_key *sk = &args.key;
u64 last_pos = (u64)-1;
diff --git a/cmds-property.c b/cmds-property.c
index 854bff56..9ae12460 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -26,6 +26,7 @@
#include "props.h"
#include "ctree.h"
#include "utils.h"
+#include "help.h"
static const char * const property_cmd_group_usage[] = {
"btrfs property get/set/list [-t <type>] <object> [<name>] [value]",
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index f4503fd9..38382ea9 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -26,6 +26,7 @@
#include "commands.h"
#include "qgroup.h"
#include "utils.h"
+#include "help.h"
static const char * const qgroup_cmd_group_usage[] = {
"btrfs qgroup <command> [options] <path>",
diff --git a/cmds-quota.c b/cmds-quota.c
index f9b422dc..15bd4b93 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -24,6 +24,7 @@
#include "commands.h"
#include "utils.h"
+#include "help.h"
static const char * const quota_cmd_group_usage[] = {
"btrfs quota <command> [options] <path>",
diff --git a/cmds-receive.c b/cmds-receive.c
index 166d37dc..72e9c8f3 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -50,6 +50,7 @@
#include "send-stream.h"
#include "send-utils.h"
#include "send-dump.h"
+#include "help.h"
static int g_verbose = 0;
@@ -314,8 +315,8 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
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, rctx->full_root_path) == NULL) {
+ if (strstr(parent_subvol->path, rctx->full_root_path) != parent_subvol->path ||
+ (sub_len > root_len && parent_subvol->path[root_len] != '/')) {
error(
"parent subvol is not reachable from inside the root subvol");
ret = -ENOENT;
@@ -323,7 +324,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
}
if (sub_len == root_len) {
- parent_subvol->path[0] = '/';
+ parent_subvol->path[0] = '.';
parent_subvol->path[1] = '\0';
} else {
/*
@@ -782,7 +783,24 @@ static int process_clone(const char *path, u64 offset, u64 len,
r->subvol_parent_name);
}
}*/
- subvol_path = strdup(si->path);
+
+ /* strip the subvolume that we are receiving to from the start of subvol_path */
+ if (rctx->full_root_path) {
+ size_t root_len = strlen(rctx->full_root_path);
+ size_t sub_len = strlen(si->path);
+
+ if (sub_len > root_len &&
+ strstr(si->path, rctx->full_root_path) == si->path &&
+ si->path[root_len] == '/') {
+ subvol_path = strdup(si->path + root_len + 1);
+ } else {
+ error("clone: source subvol path %s unreachable from %s",
+ si->path, rctx->full_root_path);
+ goto out;
+ }
+ } else {
+ subvol_path = strdup(si->path);
+ }
}
ret = path_cat_out(full_clone_path, subvol_path, clone_path);
@@ -1073,7 +1091,7 @@ static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
char *dest_dir_full_path;
char root_subvol_path[PATH_MAX];
int end = 0;
- int count;
+ int iterations = 0;
dest_dir_full_path = realpath(tomnt, NULL);
if (!dest_dir_full_path) {
@@ -1168,7 +1186,6 @@ static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
if (ret < 0)
goto out;
- count = 0;
while (!end) {
if (rctx->cached_capabilities_len) {
if (g_verbose >= 3)
@@ -1182,22 +1199,28 @@ static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
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;
+ if (ret < 0) {
+ if (ret != -ENODATA)
+ goto out;
+
+ /* Empty stream is invalid */
+ if (iterations == 0) {
+ error("empty stream is not considered valid");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = 1;
}
- count++;
- if (ret)
+ if (ret > 0)
end = 1;
close_inode_for_write(rctx);
ret = finish_subvol(rctx);
if (ret < 0)
goto out;
+
+ iterations++;
}
ret = 0;
diff --git a/cmds-replace.c b/cmds-replace.c
index 9345da23..a3ea977c 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -37,7 +37,8 @@
#include "disk-io.h"
#include "commands.h"
-
+#include "help.h"
+#include "mkfs/common.h"
static int print_replace_status(int fd, const char *path, int once);
static char *time2string(char *buf, size_t s, __u64 t);
diff --git a/cmds-rescue.c b/cmds-rescue.c
index f597997b..7e7344ee 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -24,6 +24,7 @@
#include "disk-io.h"
#include "commands.h"
#include "utils.h"
+#include "help.h"
static const char * const rescue_cmd_group_usage[] = {
"btrfs rescue <command> [options] <path>",
diff --git a/cmds-restore.c b/cmds-restore.c
index bdd35bd7..c174bdd5 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -42,6 +42,7 @@
#include "volumes.h"
#include "utils.h"
#include "commands.h"
+#include "help.h"
static char fs_name[PATH_MAX];
static char path_name[PATH_MAX];
@@ -125,7 +126,7 @@ static int decompress_lzo(struct btrfs_root *root, unsigned char *inbuf,
inbuf += LZO_LEN;
tot_in += LZO_LEN;
- new_len = lzo1x_worst_compress(root->sectorsize);
+ new_len = lzo1x_worst_compress(root->fs_info->sectorsize);
ret = lzo1x_decompress_safe((const unsigned char *)inbuf, in_len,
(unsigned char *)outbuf,
(void *)&new_len, NULL);
@@ -142,8 +143,8 @@ static int decompress_lzo(struct btrfs_root *root, unsigned char *inbuf,
* If the 4 byte header does not fit to the rest of the page we
* have to move to the next one, unless we read some garbage
*/
- mod_page = tot_in % root->sectorsize;
- rem_page = root->sectorsize - mod_page;
+ mod_page = tot_in % root->fs_info->sectorsize;
+ rem_page = root->fs_info->sectorsize - mod_page;
if (rem_page < LZO_LEN) {
inbuf += rem_page;
tot_in += rem_page;
@@ -180,6 +181,7 @@ static int next_leaf(struct btrfs_root *root, struct btrfs_path *path)
int offset = 1;
struct extent_buffer *c;
struct extent_buffer *next = NULL;
+ struct btrfs_fs_info *fs_info = root->fs_info;
again:
for (; level < BTRFS_MAX_LEVEL; level++) {
@@ -209,7 +211,7 @@ again:
if (path->reada)
reada_for_search(root, path, level, slot, 0);
- next = read_node_slot(root, c, slot);
+ next = read_node_slot(fs_info, c, slot);
if (extent_buffer_uptodate(next))
break;
offset++;
@@ -225,7 +227,7 @@ again:
break;
if (path->reada)
reada_for_search(root, path, level, 0, 0);
- next = read_node_slot(root, next, 0);
+ next = read_node_slot(fs_info, next, 0);
if (!extent_buffer_uptodate(next))
goto again;
}
@@ -344,8 +346,8 @@ static int copy_one_extent(struct btrfs_root *root, int fd,
}
again:
length = size_left;
- ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
- bytenr, &length, &multi, mirror_num, NULL);
+ ret = btrfs_map_block(root->fs_info, READ, bytenr, &length, &multi,
+ mirror_num, NULL);
if (ret) {
error("cannot map block logical %llu length %llu: %d",
(unsigned long long)bytenr,
@@ -364,8 +366,7 @@ again:
done = pread(dev_fd, inbuf+count, length, dev_bytenr);
/* Need both checks, or we miss negative values due to u64 conversion */
if (done < 0 || done < length) {
- num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
- bytenr, length);
+ num_copies = btrfs_num_copies(root->fs_info, bytenr, length);
mirror_num++;
/* mirror_num is 1-indexed, so num_copies is a valid mirror. */
if (mirror_num > num_copies) {
@@ -402,8 +403,7 @@ again:
ret = decompress(root, inbuf, outbuf, disk_size, &ram_size, compress);
if (ret) {
- num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
- bytenr, length);
+ num_copies = btrfs_num_copies(root->fs_info, bytenr, length);
mirror_num++;
if (mirror_num >= num_copies) {
ret = -1;
@@ -1254,8 +1254,8 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location,
if (!root_location)
root_location = btrfs_super_root(fs_info->super_copy);
generation = btrfs_super_generation(fs_info->super_copy);
- root->node = read_tree_block(root, root_location,
- root->nodesize, generation);
+ root->node = read_tree_block(fs_info, root_location,
+ fs_info->nodesize, generation);
if (!extent_buffer_uptodate(root->node)) {
fprintf(stderr, "Error opening tree root\n");
close_ctree(root);
@@ -1501,7 +1501,8 @@ int cmd_restore(int argc, char **argv)
if (fs_location != 0) {
free_extent_buffer(root->node);
- root->node = read_tree_block(root, fs_location, root->nodesize, 0);
+ root->node = read_tree_block(root->fs_info, fs_location,
+ root->fs_info->nodesize, 0);
if (!extent_buffer_uptodate(root->node)) {
fprintf(stderr, "Failed to read fs location\n");
ret = 1;
diff --git a/cmds-scrub.c b/cmds-scrub.c
index 2cf7f308..5388fdcf 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -44,6 +44,7 @@
#include "disk-io.h"
#include "commands.h"
+#include "help.h"
static const char * const scrub_cmd_group_usage[] = {
"btrfs scrub <command> [options] <path>|<device>",
@@ -467,7 +468,7 @@ static struct scrub_file_record **scrub_read_file(int fd, int report_errors)
{
int avail = 0;
int old_avail = 0;
- char l[16 * 1024];
+ char l[SZ_16K];
int state = 0;
int curr = -1;
int i = 0;
diff --git a/cmds-send.c b/cmds-send.c
index cec11e6b..c5ecdaa1 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -43,8 +43,9 @@
#include "send.h"
#include "send-utils.h"
+#include "help.h"
-#define SEND_BUFFER_SIZE (64 * 1024)
+#define SEND_BUFFER_SIZE SZ_64K
/*
* Default is 1 for historical reasons, changing may break scripts that expect
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 7384de45..666f6e05 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -37,6 +37,7 @@
#include "utils.h"
#include "btrfs-list.h"
#include "utils.h"
+#include "help.h"
static int is_subvolume_cleaned(int fd, u64 subvolid)
{
@@ -890,8 +891,13 @@ static int cmd_subvol_find_new(int argc, char **argv)
}
static const char * const cmd_subvol_show_usage[] = {
- "btrfs subvolume show <subvol-path>",
- "Show more information of the subvolume",
+ "btrfs subvolume show [options] <subvol-path>|<mnt>",
+ "Show more information about the subvolume",
+ "-r|--rootid rootid of the subvolume",
+ "-u|--uuid uuid of the subvolume",
+ "",
+ "If no option is specified, <subvol-path> will be shown, otherwise",
+ "the rootid or uuid are resolved relative to the <mnt> path.",
NULL
};
@@ -906,12 +912,46 @@ static int cmd_subvol_show(int argc, char **argv)
int fd = -1;
int ret = 1;
DIR *dirstream1 = NULL;
+ int by_rootid = 0;
+ int by_uuid = 0;
+ u64 rootid_arg;
+ u8 uuid_arg[BTRFS_UUID_SIZE];
+
+ while (1) {
+ int c;
+ static const struct option long_options[] = {
+ { "rootid", required_argument, NULL, 'r'},
+ { "uuid", required_argument, NULL, 'u'},
+ { NULL, 0, NULL, 0 }
+ };
- clean_args_no_options(argc, argv, cmd_subvol_show_usage);
+ c = getopt_long(argc, argv, "r:u:", long_options, NULL);
+ if (c < 0)
+ break;
+
+ switch (c) {
+ case 'r':
+ rootid_arg = arg_strtou64(optarg);
+ by_rootid = 1;
+ break;
+ case 'u':
+ uuid_parse(optarg, uuid_arg);
+ by_uuid = 1;
+ break;
+ default:
+ usage(cmd_subvol_show_usage);
+ }
+ }
if (check_argc_exact(argc - optind, 1))
usage(cmd_subvol_show_usage);
+ if (by_rootid && by_uuid) {
+ error(
+ "options --rootid and --uuid cannot be used at the same time");
+ usage(cmd_subvol_show_usage);
+ }
+
memset(&get_ri, 0, sizeof(get_ri));
fullpath = realpath(argv[optind], NULL);
if (!fullpath) {
@@ -920,7 +960,14 @@ static int cmd_subvol_show(int argc, char **argv)
goto out;
}
- ret = get_subvol_info(fullpath, &get_ri);
+ if (by_rootid) {
+ ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
+ } else if (by_uuid) {
+ ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
+ } else {
+ ret = get_subvol_info(fullpath, &get_ri);
+ }
+
if (ret) {
if (ret < 0) {
error("Failed to get subvol info %s: %s",
@@ -933,7 +980,7 @@ static int cmd_subvol_show(int argc, char **argv)
}
/* print the info */
- printf("%s\n", fullpath);
+ printf("%s\n", get_ri.full_path);
printf("\tName: \t\t\t%s\n", get_ri.name);
if (uuid_is_null(get_ri.uuid))
diff --git a/commands.h b/commands.h
index 94229c11..01bf387e 100644
--- a/commands.h
+++ b/commands.h
@@ -65,25 +65,11 @@ struct cmd_group {
const struct cmd_struct commands[];
};
-/* btrfs.c */
-int prefixcmp(const char *str, const char *prefix);
-
int handle_command_group(const struct cmd_group *grp, int argc,
char **argv);
-/* help.c */
extern const char * const generic_cmd_help_usage[];
-void usage(const char * const *usagestr) __attribute__((noreturn));
-void usage_command(const struct cmd_struct *cmd, int full, int err);
-void usage_command_group(const struct cmd_group *grp, int all, int err);
-void usage_command_group_short(const struct cmd_group *grp);
-
-void help_unknown_token(const char *arg, const struct cmd_group *grp) __attribute__((noreturn));
-void help_ambiguous_token(const char *arg, const struct cmd_group *grp) __attribute__((noreturn));
-
-void help_command_group(const struct cmd_group *grp, int argc, char **argv);
-
extern const struct cmd_group subvolume_cmd_group;
extern const struct cmd_group filesystem_cmd_group;
extern const struct cmd_group balance_cmd_group;
diff --git a/common-defs.h b/common-defs.h
new file mode 100644
index 00000000..adec9a95
--- /dev/null
+++ b/common-defs.h
@@ -0,0 +1,22 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_COMMON_DEFS_H__
+#define __BTRFS_COMMON_DEFS_H__
+
+#define BTRFS_UUID_UNPARSED_SIZE 37
+
+#endif
diff --git a/config/config.guess b/config/config.guess
index eab94e23..b9da8803 100755
--- a/config/config.guess
+++ b/config/config.guess
@@ -1013,7 +1013,7 @@ EOF
echo powerpcle-${VENDOR}-linux-${LIBC}
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
- echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}
exit ;;
sh64*:Linux:*:*)
echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}
diff --git a/config/config.sub b/config/config.sub
index 7ffe3737..a93c4879 100755
--- a/config/config.sub
+++ b/config/config.sub
@@ -1058,12 +1058,18 @@ case $basic_machine in
rtpc | rtpc-*)
basic_machine=romp-ibm
;;
- s390 | s390-*)
+ s390)
basic_machine=s390-ibm
;;
- s390x | s390x-*)
+ s390-*)
+ basic_machine=s390-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ s390x)
basic_machine=s390x-ibm
;;
+ s390x-*)
+ basic_machine=s390x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
sa29200)
basic_machine=a29k-amd
os=-udi
diff --git a/configure b/configure
index cfff29d1..5b91d293 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.9.1.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.12.
#
# 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.9.1'
-PACKAGE_STRING='btrfs-progs v4.9.1'
+PACKAGE_VERSION='v4.12'
+PACKAGE_STRING='btrfs-progs v4.12'
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.9.1 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.12 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.9.1:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.12:";;
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.9.1
+btrfs-progs configure v4.12
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.9.1, which was
+It was created by btrfs-progs $as_me v4.12, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -6187,7 +6187,7 @@ fi
ac_config_headers="$ac_config_headers config.h"
-ac_config_files="$ac_config_files Makefile Documentation/Makefile version.h"
+ac_config_files="$ac_config_files Makefile.inc Documentation/Makefile version.h"
cat >confcache <<\_ACEOF
@@ -6697,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.9.1, which was
+This file was extended by btrfs-progs $as_me v4.12, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6760,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.9.1
+btrfs-progs config.status v4.12
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -6883,7 +6883,7 @@ for ac_config_target in $ac_config_targets
do
case $ac_config_target in
"config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
- "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "Makefile.inc") CONFIG_FILES="$CONFIG_FILES Makefile.inc" ;;
"Documentation/Makefile") CONFIG_FILES="$CONFIG_FILES Documentation/Makefile" ;;
"version.h") CONFIG_FILES="$CONFIG_FILES version.h" ;;
diff --git a/configure.ac b/configure.ac
index 559a6f67..99c18694 100644
--- a/configure.ac
+++ b/configure.ac
@@ -198,7 +198,7 @@ AC_SUBST([LIBBTRFS_PATCHLEVEL])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
-Makefile
+Makefile.inc
Documentation/Makefile
version.h
])
diff --git a/convert/Makefile b/convert/Makefile
new file mode 100644
index 00000000..66dd7d0b
--- /dev/null
+++ b/convert/Makefile
@@ -0,0 +1,7 @@
+include ../Makefile.inc
+
+all:
+ $(MAKE) -C .. btrfs-convert
+
+clean:
+ -$(RM) -f -- *.o *.o.d
diff --git a/convert/common.c b/convert/common.c
new file mode 100644
index 00000000..3860f3b9
--- /dev/null
+++ b/convert/common.c
@@ -0,0 +1,884 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <unistd.h>
+#include <uuid/uuid.h>
+#include "disk-io.h"
+#include "volumes.h"
+#include "utils.h"
+#include "mkfs/common.h"
+#include "convert/common.h"
+
+#define BTRFS_CONVERT_META_GROUP_SIZE SZ_32M
+
+/*
+ * Reserve space from free_tree.
+ * The algorithm is very simple, find the first cache_extent with enough space
+ * and allocate from its beginning.
+ */
+static int reserve_free_space(struct cache_tree *free_tree, u64 len,
+ u64 *ret_start)
+{
+ struct cache_extent *cache;
+ int found = 0;
+
+ ASSERT(ret_start != NULL);
+ cache = first_cache_extent(free_tree);
+ while (cache) {
+ if (cache->size > len) {
+ found = 1;
+ *ret_start = cache->start;
+
+ cache->size -= len;
+ if (cache->size == 0) {
+ remove_cache_extent(free_tree, cache);
+ free(cache);
+ } else {
+ cache->start += len;
+ }
+ break;
+ }
+ cache = next_cache_extent(cache);
+ }
+ if (!found)
+ return -ENOSPC;
+ return 0;
+}
+
+static inline int write_temp_super(int fd, struct btrfs_super_block *sb,
+ u64 sb_bytenr)
+{
+ u32 crc = ~(u32)0;
+ int ret;
+
+ crc = btrfs_csum_data((char *)sb + BTRFS_CSUM_SIZE, crc,
+ BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+ btrfs_csum_final(crc, &sb->csum[0]);
+ ret = pwrite(fd, sb, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
+ if (ret < BTRFS_SUPER_INFO_SIZE)
+ ret = (ret < 0 ? -errno : -EIO);
+ else
+ ret = 0;
+ return ret;
+}
+
+/*
+ * Setup temporary superblock at cfg->super_bynter
+ * Needed info are extracted from cfg, and root_bytenr, chunk_bytenr
+ *
+ * For now sys chunk array will be empty and dev_item is empty too.
+ * They will be re-initialized at temp chunk tree setup.
+ *
+ * The superblock signature is not valid, denotes a partially created
+ * filesystem, needs to be finalized.
+ */
+static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg,
+ u64 root_bytenr, u64 chunk_bytenr)
+{
+ unsigned char chunk_uuid[BTRFS_UUID_SIZE];
+ char super_buf[BTRFS_SUPER_INFO_SIZE];
+ struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf;
+ int ret;
+
+ memset(super_buf, 0, BTRFS_SUPER_INFO_SIZE);
+ cfg->num_bytes = round_down(cfg->num_bytes, cfg->sectorsize);
+
+ if (*cfg->fs_uuid) {
+ if (uuid_parse(cfg->fs_uuid, super->fsid) != 0) {
+ error("cound not parse UUID: %s", cfg->fs_uuid);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!test_uuid_unique(cfg->fs_uuid)) {
+ error("non-unique UUID: %s", cfg->fs_uuid);
+ ret = -EINVAL;
+ goto out;
+ }
+ } else {
+ uuid_generate(super->fsid);
+ uuid_unparse(super->fsid, cfg->fs_uuid);
+ }
+ uuid_generate(chunk_uuid);
+ uuid_unparse(chunk_uuid, cfg->chunk_uuid);
+
+ btrfs_set_super_bytenr(super, cfg->super_bytenr);
+ btrfs_set_super_num_devices(super, 1);
+ btrfs_set_super_magic(super, BTRFS_MAGIC_PARTIAL);
+ btrfs_set_super_generation(super, 1);
+ btrfs_set_super_root(super, root_bytenr);
+ btrfs_set_super_chunk_root(super, chunk_bytenr);
+ btrfs_set_super_total_bytes(super, cfg->num_bytes);
+ /*
+ * Temporary filesystem will only have 6 tree roots:
+ * chunk tree, root tree, extent_tree, device tree, fs tree
+ * and csum tree.
+ */
+ btrfs_set_super_bytes_used(super, 6 * cfg->nodesize);
+ btrfs_set_super_sectorsize(super, cfg->sectorsize);
+ super->__unused_leafsize = cpu_to_le32(cfg->nodesize);
+ btrfs_set_super_nodesize(super, cfg->nodesize);
+ btrfs_set_super_stripesize(super, cfg->stripesize);
+ btrfs_set_super_csum_type(super, BTRFS_CSUM_TYPE_CRC32);
+ btrfs_set_super_chunk_root(super, chunk_bytenr);
+ btrfs_set_super_cache_generation(super, -1);
+ btrfs_set_super_incompat_flags(super, cfg->features);
+ if (cfg->label)
+ __strncpy_null(super->label, cfg->label, BTRFS_LABEL_SIZE - 1);
+
+ /* Sys chunk array will be re-initialized at chunk tree init time */
+ super->sys_chunk_array_size = 0;
+
+ ret = write_temp_super(fd, super, cfg->super_bytenr);
+out:
+ return ret;
+}
+
+static int setup_temp_extent_buffer(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ u64 bytenr, u64 owner)
+{
+ unsigned char fsid[BTRFS_FSID_SIZE];
+ unsigned char chunk_uuid[BTRFS_UUID_SIZE];
+ int ret;
+
+ ret = uuid_parse(cfg->fs_uuid, fsid);
+ if (ret)
+ return -EINVAL;
+ ret = uuid_parse(cfg->chunk_uuid, chunk_uuid);
+ if (ret)
+ return -EINVAL;
+
+ memset(buf->data, 0, cfg->nodesize);
+ buf->len = cfg->nodesize;
+ btrfs_set_header_bytenr(buf, bytenr);
+ btrfs_set_header_generation(buf, 1);
+ btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
+ btrfs_set_header_owner(buf, owner);
+ btrfs_set_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN);
+ write_extent_buffer(buf, chunk_uuid, btrfs_header_chunk_tree_uuid(buf),
+ BTRFS_UUID_SIZE);
+ write_extent_buffer(buf, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE);
+ return 0;
+}
+
+static void insert_temp_root_item(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff, u64 objectid,
+ u64 bytenr)
+{
+ struct btrfs_root_item root_item;
+ struct btrfs_inode_item *inode_item;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(root_item);
+ memset(&root_item, 0, sizeof(root_item));
+ inode_item = &root_item.inode;
+ btrfs_set_stack_inode_generation(inode_item, 1);
+ btrfs_set_stack_inode_size(inode_item, 3);
+ btrfs_set_stack_inode_nlink(inode_item, 1);
+ btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
+ btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
+ btrfs_set_root_refs(&root_item, 1);
+ btrfs_set_root_used(&root_item, cfg->nodesize);
+ btrfs_set_root_generation(&root_item, 1);
+ btrfs_set_root_bytenr(&root_item, bytenr);
+
+ memset(&disk_key, 0, sizeof(disk_key));
+ btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, objectid);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(root_item));
+ write_extent_buffer(buf, &root_item,
+ btrfs_item_ptr_offset(buf, *slot),
+ sizeof(root_item));
+ (*slot)++;
+}
+
+/*
+ * Setup an extent buffer for tree block.
+ */
+static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf,
+ u64 bytenr)
+{
+ int ret;
+
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+
+ /* Temporary extent buffer is always mapped 1:1 on disk */
+ ret = pwrite(fd, buf->data, buf->len, bytenr);
+ if (ret < buf->len)
+ ret = (ret < 0 ? ret : -EIO);
+ else
+ ret = 0;
+ return ret;
+}
+
+static int setup_temp_root_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 root_bytenr, u64 extent_bytenr,
+ u64 dev_bytenr, u64 fs_bytenr, u64 csum_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /*
+ * Provided bytenr must in ascending order, or tree root will have a
+ * bad key order.
+ */
+ if (!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr &&
+ dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr)) {
+ error("bad tree bytenr order: "
+ "root < extent %llu < %llu, "
+ "extent < dev %llu < %llu, "
+ "dev < fs %llu < %llu, "
+ "fs < csum %llu < %llu",
+ (unsigned long long)root_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)csum_bytenr);
+ return -EINVAL;
+ }
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = setup_temp_extent_buffer(buf, cfg, root_bytenr,
+ BTRFS_ROOT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_EXTENT_TREE_OBJECTID, extent_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_DEV_TREE_OBJECTID, dev_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_FS_TREE_OBJECTID, fs_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_CSUM_TREE_OBJECTID, csum_bytenr);
+
+ ret = write_temp_extent_buffer(fd, buf, root_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+static int insert_temp_dev_item(int fd, struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff)
+{
+ struct btrfs_disk_key disk_key;
+ struct btrfs_dev_item *dev_item;
+ char super_buf[BTRFS_SUPER_INFO_SIZE];
+ unsigned char dev_uuid[BTRFS_UUID_SIZE];
+ unsigned char fsid[BTRFS_FSID_SIZE];
+ struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf;
+ int ret;
+
+ ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE, cfg->super_bytenr);
+ if (ret < BTRFS_SUPER_INFO_SIZE) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(*dev_item);
+ /* setup device item 1, 0 is for replace case */
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
+ btrfs_set_disk_key_offset(&disk_key, 1);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_item));
+
+ dev_item = btrfs_item_ptr(buf, *slot, struct btrfs_dev_item);
+ /* Generate device uuid */
+ uuid_generate(dev_uuid);
+ write_extent_buffer(buf, dev_uuid,
+ (unsigned long)btrfs_device_uuid(dev_item),
+ BTRFS_UUID_SIZE);
+ uuid_parse(cfg->fs_uuid, fsid);
+ write_extent_buffer(buf, fsid,
+ (unsigned long)btrfs_device_fsid(dev_item),
+ BTRFS_FSID_SIZE);
+ btrfs_set_device_id(buf, dev_item, 1);
+ btrfs_set_device_generation(buf, dev_item, 0);
+ btrfs_set_device_total_bytes(buf, dev_item, cfg->num_bytes);
+ /*
+ * The number must match the initial SYSTEM and META chunk size
+ */
+ btrfs_set_device_bytes_used(buf, dev_item,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE +
+ BTRFS_CONVERT_META_GROUP_SIZE);
+ btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_type(buf, dev_item, 0);
+
+ /* Super dev_item is not complete, copy the complete one to sb */
+ read_extent_buffer(buf, &super->dev_item, (unsigned long)dev_item,
+ sizeof(*dev_item));
+ ret = write_temp_super(fd, super, cfg->super_bytenr);
+ (*slot)++;
+out:
+ return ret;
+}
+
+static int insert_temp_chunk_item(int fd, struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff, u64 start, u64 len,
+ u64 type)
+{
+ struct btrfs_chunk *chunk;
+ struct btrfs_disk_key disk_key;
+ char super_buf[BTRFS_SUPER_INFO_SIZE];
+ struct btrfs_super_block *sb = (struct btrfs_super_block *)super_buf;
+ int ret = 0;
+
+ ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE,
+ cfg->super_bytenr);
+ if (ret < BTRFS_SUPER_INFO_SIZE) {
+ ret = (ret < 0 ? ret : -EIO);
+ return ret;
+ }
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= btrfs_chunk_item_size(1);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ btrfs_set_disk_key_offset(&disk_key, start);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot),
+ btrfs_chunk_item_size(1));
+
+ chunk = btrfs_item_ptr(buf, *slot, struct btrfs_chunk);
+ btrfs_set_chunk_length(buf, chunk, len);
+ btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID);
+ btrfs_set_chunk_stripe_len(buf, chunk, BTRFS_STRIPE_LEN);
+ btrfs_set_chunk_type(buf, chunk, type);
+ btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_num_stripes(buf, chunk, 1);
+ /* TODO: Support DUP profile for system chunk */
+ btrfs_set_stripe_devid_nr(buf, chunk, 0, 1);
+ /* We are doing 1:1 mapping, so start is its dev offset */
+ btrfs_set_stripe_offset_nr(buf, chunk, 0, start);
+ write_extent_buffer(buf, &sb->dev_item.uuid,
+ (unsigned long)btrfs_stripe_dev_uuid_nr(chunk, 0),
+ BTRFS_UUID_SIZE);
+ (*slot)++;
+
+ /*
+ * If it's system chunk, also copy it to super block.
+ */
+ if (type & BTRFS_BLOCK_GROUP_SYSTEM) {
+ char *cur;
+ u32 array_size;
+
+ cur = (char *)sb->sys_chunk_array
+ + btrfs_super_sys_array_size(sb);
+ memcpy(cur, &disk_key, sizeof(disk_key));
+ cur += sizeof(disk_key);
+ read_extent_buffer(buf, cur, (unsigned long int)chunk,
+ btrfs_chunk_item_size(1));
+ array_size = btrfs_super_sys_array_size(sb);
+ array_size += btrfs_chunk_item_size(1) +
+ sizeof(disk_key);
+ btrfs_set_super_sys_array_size(sb, array_size);
+
+ ret = write_temp_super(fd, sb, cfg->super_bytenr);
+ }
+ return ret;
+}
+
+static int setup_temp_chunk_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 sys_chunk_start, u64 meta_chunk_start,
+ u64 chunk_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /* Must ensure SYS chunk starts before META chunk */
+ if (meta_chunk_start < sys_chunk_start) {
+ error("wrong chunk order: meta < system %llu < %llu",
+ (unsigned long long)meta_chunk_start,
+ (unsigned long long)sys_chunk_start);
+ return -EINVAL;
+ }
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, chunk_bytenr,
+ BTRFS_CHUNK_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = insert_temp_dev_item(fd, buf, cfg, &slot, &itemoff);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
+ sys_chunk_start,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
+ meta_chunk_start,
+ BTRFS_CONVERT_META_GROUP_SIZE,
+ BTRFS_BLOCK_GROUP_METADATA);
+ if (ret < 0)
+ goto out;
+ ret = write_temp_extent_buffer(fd, buf, chunk_bytenr);
+
+out:
+ free(buf);
+ return ret;
+}
+
+static void insert_temp_dev_extent(struct extent_buffer *buf,
+ int *slot, u32 *itemoff, u64 start, u64 len)
+{
+ struct btrfs_dev_extent *dev_extent;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(*dev_extent);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, 1);
+ btrfs_set_disk_key_offset(&disk_key, start);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_extent));
+
+ dev_extent = btrfs_item_ptr(buf, *slot, struct btrfs_dev_extent);
+ btrfs_set_dev_extent_chunk_objectid(buf, dev_extent,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ btrfs_set_dev_extent_length(buf, dev_extent, len);
+ btrfs_set_dev_extent_chunk_offset(buf, dev_extent, start);
+ btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
+ BTRFS_CHUNK_TREE_OBJECTID);
+ (*slot)++;
+}
+
+static int setup_temp_dev_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 sys_chunk_start, u64 meta_chunk_start,
+ u64 dev_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /* Must ensure SYS chunk starts before META chunk */
+ if (meta_chunk_start < sys_chunk_start) {
+ error("wrong chunk order: meta < system %llu < %llu",
+ (unsigned long long)meta_chunk_start,
+ (unsigned long long)sys_chunk_start);
+ return -EINVAL;
+ }
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, dev_bytenr,
+ BTRFS_DEV_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ insert_temp_dev_extent(buf, &slot, &itemoff, sys_chunk_start,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE);
+ insert_temp_dev_extent(buf, &slot, &itemoff, meta_chunk_start,
+ BTRFS_CONVERT_META_GROUP_SIZE);
+ ret = write_temp_extent_buffer(fd, buf, dev_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+static int setup_temp_fs_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 fs_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ int ret;
+
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, fs_bytenr,
+ BTRFS_FS_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ /*
+ * Temporary fs tree is completely empty.
+ */
+ ret = write_temp_extent_buffer(fd, buf, fs_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+static int setup_temp_csum_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 csum_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ int ret;
+
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, csum_bytenr,
+ BTRFS_CSUM_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ /*
+ * Temporary csum tree is completely empty.
+ */
+ ret = write_temp_extent_buffer(fd, buf, csum_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+/*
+ * Insert one temporary extent item.
+ *
+ * NOTE: if skinny_metadata is not enabled, this function must be called
+ * after all other trees are initialized.
+ * Or fs without skinny-metadata will be screwed up.
+ */
+static int insert_temp_extent_item(int fd, struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff, u64 bytenr,
+ u64 ref_root)
+{
+ struct extent_buffer *tmp;
+ struct btrfs_extent_item *ei;
+ struct btrfs_extent_inline_ref *iref;
+ struct btrfs_disk_key disk_key;
+ struct btrfs_disk_key tree_info_key;
+ struct btrfs_tree_block_info *info;
+ int itemsize;
+ int skinny_metadata = cfg->features &
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
+ int ret;
+
+ if (skinny_metadata)
+ itemsize = sizeof(*ei) + sizeof(*iref);
+ else
+ itemsize = sizeof(*ei) + sizeof(*iref) +
+ sizeof(struct btrfs_tree_block_info);
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ *(itemoff) -= itemsize;
+
+ if (skinny_metadata) {
+ btrfs_set_disk_key_type(&disk_key, BTRFS_METADATA_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+ } else {
+ btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
+ }
+ btrfs_set_disk_key_objectid(&disk_key, bytenr);
+
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), itemsize);
+
+ ei = btrfs_item_ptr(buf, *slot, struct btrfs_extent_item);
+ btrfs_set_extent_refs(buf, ei, 1);
+ btrfs_set_extent_generation(buf, ei, 1);
+ btrfs_set_extent_flags(buf, ei, BTRFS_EXTENT_FLAG_TREE_BLOCK);
+
+ if (skinny_metadata) {
+ iref = (struct btrfs_extent_inline_ref *)(ei + 1);
+ } else {
+ info = (struct btrfs_tree_block_info *)(ei + 1);
+ iref = (struct btrfs_extent_inline_ref *)(info + 1);
+ }
+ btrfs_set_extent_inline_ref_type(buf, iref,
+ BTRFS_TREE_BLOCK_REF_KEY);
+ btrfs_set_extent_inline_ref_offset(buf, iref, ref_root);
+
+ (*slot)++;
+ if (skinny_metadata)
+ return 0;
+
+ /*
+ * Lastly, check the tree block key by read the tree block
+ * Since we do 1:1 mapping for convert case, we can directly
+ * read the bytenr from disk
+ */
+ tmp = malloc(sizeof(*tmp) + cfg->nodesize);
+ if (!tmp)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(tmp, cfg, bytenr, ref_root);
+ if (ret < 0)
+ goto out;
+ ret = pread(fd, tmp->data, cfg->nodesize, bytenr);
+ if (ret < cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+ if (btrfs_header_nritems(tmp) == 0) {
+ btrfs_set_disk_key_type(&tree_info_key, 0);
+ btrfs_set_disk_key_objectid(&tree_info_key, 0);
+ btrfs_set_disk_key_offset(&tree_info_key, 0);
+ } else {
+ btrfs_item_key(tmp, &tree_info_key, 0);
+ }
+ btrfs_set_tree_block_key(buf, info, &tree_info_key);
+
+out:
+ free(tmp);
+ return ret;
+}
+
+static void insert_temp_block_group(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff,
+ u64 bytenr, u64 len, u64 used, u64 flag)
+{
+ struct btrfs_block_group_item bgi;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(bgi);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_BLOCK_GROUP_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, bytenr);
+ btrfs_set_disk_key_offset(&disk_key, len);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(bgi));
+
+ btrfs_set_block_group_flags(&bgi, flag);
+ btrfs_set_block_group_used(&bgi, used);
+ btrfs_set_block_group_chunk_objectid(&bgi,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ write_extent_buffer(buf, &bgi, btrfs_item_ptr_offset(buf, *slot),
+ sizeof(bgi));
+ (*slot)++;
+}
+
+static int setup_temp_extent_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 chunk_bytenr, u64 root_bytenr,
+ u64 extent_bytenr, u64 dev_bytenr,
+ u64 fs_bytenr, u64 csum_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /*
+ * We must ensure provided bytenr are in ascending order,
+ * or extent tree key order will be broken.
+ */
+ if (!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr &&
+ extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr &&
+ fs_bytenr < csum_bytenr)) {
+ error("bad tree bytenr order: "
+ "chunk < root %llu < %llu, "
+ "root < extent %llu < %llu, "
+ "extent < dev %llu < %llu, "
+ "dev < fs %llu < %llu, "
+ "fs < csum %llu < %llu",
+ (unsigned long long)chunk_bytenr,
+ (unsigned long long)root_bytenr,
+ (unsigned long long)root_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)csum_bytenr);
+ return -EINVAL;
+ }
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = setup_temp_extent_buffer(buf, cfg, extent_bytenr,
+ BTRFS_EXTENT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ chunk_bytenr, BTRFS_CHUNK_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ insert_temp_block_group(buf, cfg, &slot, &itemoff, chunk_bytenr,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE, cfg->nodesize,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ root_bytenr, BTRFS_ROOT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ /* 5 tree block used, root, extent, dev, fs and csum*/
+ insert_temp_block_group(buf, cfg, &slot, &itemoff, root_bytenr,
+ BTRFS_CONVERT_META_GROUP_SIZE, cfg->nodesize * 5,
+ BTRFS_BLOCK_GROUP_METADATA);
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ extent_bytenr, BTRFS_EXTENT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ dev_bytenr, BTRFS_DEV_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ fs_bytenr, BTRFS_FS_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ csum_bytenr, BTRFS_CSUM_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = write_temp_extent_buffer(fd, buf, extent_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+/*
+ * Improved version of make_btrfs().
+ *
+ * This one will
+ * 1) Do chunk allocation to avoid used data
+ * And after this function, extent type matches chunk type
+ * 2) Better structured code
+ * No super long hand written codes to initialized all tree blocks
+ * Split into small blocks and reuse codes.
+ * TODO: Reuse tree operation facilities by introducing new flags
+ */
+int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
+{
+ struct cache_tree *free_space = &cctx->free_space;
+ struct cache_tree *used_space = &cctx->used_space;
+ u64 sys_chunk_start;
+ u64 meta_chunk_start;
+ /* chunk tree bytenr, in system chunk */
+ u64 chunk_bytenr;
+ /* metadata trees bytenr, in metadata chunk */
+ u64 root_bytenr;
+ u64 extent_bytenr;
+ u64 dev_bytenr;
+ u64 fs_bytenr;
+ u64 csum_bytenr;
+ int ret;
+
+ /* Source filesystem must be opened, checked and analyzed in advance */
+ ASSERT(!cache_tree_empty(used_space));
+
+ /*
+ * reserve space for temporary superblock first
+ * Here we allocate a little larger space, to keep later
+ * free space will be STRIPE_LEN aligned
+ */
+ ret = reserve_free_space(free_space, BTRFS_STRIPE_LEN,
+ &cfg->super_bytenr);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Then reserve system chunk space
+ * TODO: Change system group size depending on cctx->total_bytes.
+ * If using current 4M, it can only handle less than one TB for
+ * worst case and then run out of sys space.
+ */
+ ret = reserve_free_space(free_space, BTRFS_MKFS_SYSTEM_GROUP_SIZE,
+ &sys_chunk_start);
+ if (ret < 0)
+ goto out;
+ ret = reserve_free_space(free_space, BTRFS_CONVERT_META_GROUP_SIZE,
+ &meta_chunk_start);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Allocated meta/sys chunks will be mapped 1:1 with device offset.
+ *
+ * Inside the allocated metadata chunk, the layout will be:
+ * | offset | contents |
+ * -------------------------------------
+ * | +0 | tree root |
+ * | +nodesize | extent root |
+ * | +nodesize * 2 | device root |
+ * | +nodesize * 3 | fs tree |
+ * | +nodesize * 4 | csum tree |
+ * -------------------------------------
+ * Inside the allocated system chunk, the layout will be:
+ * | offset | contents |
+ * -------------------------------------
+ * | +0 | chunk root |
+ * -------------------------------------
+ */
+ chunk_bytenr = sys_chunk_start;
+ root_bytenr = meta_chunk_start;
+ extent_bytenr = meta_chunk_start + cfg->nodesize;
+ dev_bytenr = meta_chunk_start + cfg->nodesize * 2;
+ fs_bytenr = meta_chunk_start + cfg->nodesize * 3;
+ csum_bytenr = meta_chunk_start + cfg->nodesize * 4;
+
+ ret = setup_temp_super(fd, cfg, root_bytenr, chunk_bytenr);
+ if (ret < 0)
+ goto out;
+
+ ret = setup_temp_root_tree(fd, cfg, root_bytenr, extent_bytenr,
+ dev_bytenr, fs_bytenr, csum_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_chunk_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
+ chunk_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_dev_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
+ dev_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_fs_tree(fd, cfg, fs_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_csum_tree(fd, cfg, csum_bytenr);
+ if (ret < 0)
+ goto out;
+ /*
+ * Setup extent tree last, since it may need to read tree block key
+ * for non-skinny metadata case.
+ */
+ ret = setup_temp_extent_tree(fd, cfg, chunk_bytenr, root_bytenr,
+ extent_bytenr, dev_bytenr, fs_bytenr,
+ csum_bytenr);
+out:
+ return ret;
+}
+
diff --git a/convert/common.h b/convert/common.h
new file mode 100644
index 00000000..2f4ea485
--- /dev/null
+++ b/convert/common.h
@@ -0,0 +1,56 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+/*
+ * Defines and function declarations for users of the mkfs API, no internal
+ * defintions.
+ */
+
+#ifndef __BTRFS_CONVERT_COMMON_H__
+#define __BTRFS_CONVERT_COMMON_H__
+
+#include "kerncompat.h"
+#include "common-defs.h"
+#include "extent-cache.h"
+
+struct btrfs_mkfs_config;
+
+struct btrfs_convert_context {
+ u32 blocksize;
+ u64 first_data_block;
+ u64 block_count;
+ u64 inodes_count;
+ u64 free_inodes_count;
+ u64 total_bytes;
+ char *volume_name;
+ const struct btrfs_convert_operations *convert_ops;
+
+ /* The accurate used space of old filesystem */
+ struct cache_tree used_space;
+
+ /* Batched ranges which must be covered by data chunks */
+ struct cache_tree data_chunks;
+
+ /* Free space which is not covered by data_chunks */
+ struct cache_tree free_space;
+
+ void *fs_data;
+};
+
+int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx);
+
+#endif
diff --git a/convert/main.c b/convert/main.c
index 8d9f29fa..0deccd9c 100644
--- a/convert/main.c
+++ b/convert/main.c
@@ -16,69 +16,116 @@
* Boston, MA 021110-1307, USA.
*/
+/*
+ * Btrfs convert design:
+ *
+ * The overall design of btrfs convert is like the following:
+ *
+ * |<------------------Old fs----------------------------->|
+ * |<- used ->| |<- used ->| |<- used ->|
+ * ||
+ * \/
+ * |<---------------Btrfs fs------------------------------>|
+ * |<- Old data chunk ->|< new chunk (D/M/S)>|<- ODC ->|
+ * |<-Old-FE->| |<-Old-FE->|<- Btrfs extents ->|<-Old-FE->|
+ *
+ * ODC = Old data chunk, btrfs chunks containing old fs data
+ * Mapped 1:1 (logical address == device offset)
+ * Old-FE = file extents pointing to old fs.
+ *
+ * So old fs used space is (mostly) kept as is, while btrfs will insert
+ * its chunk (Data/Meta/Sys) into large enough free space.
+ * In this way, we can create different profiles for metadata/data for
+ * converted fs.
+ *
+ * We must reserve and relocate 3 ranges for btrfs:
+ * * [0, 1M) - area never used for any data except the first
+ * superblock
+ * * [btrfs_sb_offset(1), +64K) - 1st superblock backup copy
+ * * [btrfs_sb_offset(2), +64K) - 2nd, dtto
+ *
+ * Most work is spent handling corner cases around these reserved ranges.
+ *
+ * Detailed workflow is:
+ * 1) Scan old fs used space and calculate data chunk layout
+ * 1.1) Scan old fs
+ * We can a map used space of old fs
+ *
+ * 1.2) Calculate data chunk layout - this is the hard part
+ * New data chunks must meet 3 conditions using result fomr 1.1
+ * a. Large enough to be a chunk
+ * b. Doesn't intersect reserved ranges
+ * c. Covers all the remaining old fs used space
+ *
+ * NOTE: This can be simplified if we don't need to handle backup supers
+ *
+ * 1.3) Calculate usable space for new btrfs chunks
+ * Btrfs chunk usable space must meet 3 conditions using result from 1.2
+ * a. Large enough to be a chunk
+ * b. Doesn't intersect reserved ranges
+ * c. Doesn't cover any data chunks in 1.1
+ *
+ * 2) Create basic btrfs filesystem structure
+ * Initial metadata and sys chunks are inserted in the first availabe
+ * space found in step 1.3
+ * Then insert all data chunks into the basic btrfs
+ *
+ * 3) Create convert image
+ * We need to relocate reserved ranges here.
+ * After this step, the convert image is done, and we can use the image
+ * as reflink source to create old files
+ *
+ * 4) Iterate old fs to create files
+ * We just reflink file extents from old fs to newly created files on
+ * btrfs.
+ */
+
#include "kerncompat.h"
-#include <sys/ioctl.h>
-#include <sys/mount.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
-#include <uuid/uuid.h>
-#include <linux/limits.h>
#include <getopt.h>
+#include <pthread.h>
+#include <stdbool.h>
#include "ctree.h"
#include "disk-io.h"
#include "volumes.h"
#include "transaction.h"
-#include "crc32c.h"
#include "utils.h"
#include "task-utils.h"
+#include "help.h"
+#include "mkfs/common.h"
+#include "convert/common.h"
+#include "convert/source-fs.h"
+#include "fsfeatures.h"
-#if BTRFSCONVERT_EXT2
-#include <ext2fs/ext2_fs.h>
-#include <ext2fs/ext2fs.h>
-#include <ext2fs/ext2_ext_attr.h>
-
-#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO)
-
-/*
- * Compatibility code for e2fsprogs 1.41 which doesn't support RO compat flag
- * BIGALLOC.
- * Unlike normal RO compat flag, BIGALLOC affects how e2fsprogs check used
- * space, and btrfs-convert heavily relies on it.
- */
-#ifdef HAVE_OLD_E2FSPROGS
-#define EXT2FS_CLUSTER_RATIO(fs) (1)
-#define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_BLOCKS_PER_GROUP(s))
-#define EXT2FS_B2C(fs, blk) (blk)
-#endif
+const struct btrfs_convert_operations ext2_convert_ops;
+static const struct btrfs_convert_operations *convert_operations[] = {
+#if BTRFSCONVERT_EXT2
+ &ext2_convert_ops,
#endif
-
-#define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
-
-struct task_ctx {
- uint32_t max_copy_inodes;
- uint32_t cur_copy_inodes;
- struct task_info *info;
};
static void *print_copied_inodes(void *p)
{
struct task_ctx *priv = p;
const char work_indicator[] = { '.', 'o', 'O', 'o' };
- uint32_t count = 0;
+ u64 count = 0;
task_period_start(priv->info, 1000 /* 1s */);
while (1) {
count++;
- printf("copy inodes [%c] [%10d/%10d]\r",
- work_indicator[count % 4], priv->cur_copy_inodes,
- priv->max_copy_inodes);
+ pthread_mutex_lock(&priv->mutex);
+ printf("copy inodes [%c] [%10llu/%10llu]\r",
+ work_indicator[count % 4],
+ (unsigned long long)priv->cur_copy_inodes,
+ (unsigned long long)priv->max_copy_inodes);
+ pthread_mutex_unlock(&priv->mutex);
fflush(stdout);
task_period_wait(priv->info);
}
@@ -94,38 +141,11 @@ static int after_copied_inodes(void *p)
return 0;
}
-struct btrfs_convert_context;
-struct btrfs_convert_operations {
- const char *name;
- int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname);
- int (*read_used_space)(struct btrfs_convert_context *cctx);
- int (*copy_inodes)(struct btrfs_convert_context *cctx,
- 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)
-{
- cache_tree_init(&cctx->used);
- cache_tree_init(&cctx->data_chunks);
- cache_tree_init(&cctx->free);
-}
-
-static void clean_convert_context(struct btrfs_convert_context *cctx)
-{
- free_extent_cache_tree(&cctx->used);
- free_extent_cache_tree(&cctx->data_chunks);
- free_extent_cache_tree(&cctx->free);
-}
-
static inline int copy_inodes(struct btrfs_convert_context *cctx,
- struct btrfs_root *root, int datacsum,
- int packing, int noxattr, struct task_ctx *p)
+ struct btrfs_root *root, u32 convert_flags,
+ struct task_ctx *p)
{
- return cctx->convert_ops->copy_inodes(cctx, root, datacsum, packing,
- noxattr, p);
+ return cctx->convert_ops->copy_inodes(cctx, root, convert_flags, p);
}
static inline void convert_close_fs(struct btrfs_convert_context *cctx)
@@ -138,72 +158,11 @@ 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;
- u64 offset;
-
- for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
- offset = btrfs_sb_offset(i);
- offset &= ~((u64)BTRFS_STRIPE_LEN - 1);
-
- if (bytenr < offset + BTRFS_STRIPE_LEN &&
- bytenr + num_bytes > offset)
- return 1;
- }
- return 0;
-}
-
-static int convert_insert_dirent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- const char *name, size_t name_len,
- u64 dir, u64 objectid,
- u8 file_type, u64 index_cnt,
- struct btrfs_inode_item *inode)
-{
- int ret;
- u64 inode_size;
- struct btrfs_key location = {
- .objectid = objectid,
- .offset = 0,
- .type = BTRFS_INODE_ITEM_KEY,
- };
-
- ret = btrfs_insert_dir_item(trans, root, name, name_len,
- dir, &location, file_type, index_cnt);
- if (ret)
- return ret;
- ret = btrfs_insert_inode_ref(trans, root, name, name_len,
- objectid, dir, index_cnt);
- if (ret)
- return ret;
- inode_size = btrfs_stack_inode_size(inode) + name_len * 2;
- btrfs_set_stack_inode_size(inode, inode_size);
-
- return 0;
-}
-
-static int read_disk_extent(struct btrfs_root *root, u64 bytenr,
- u32 num_bytes, char *buffer)
-{
- int ret;
- struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices;
-
- ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr);
- if (ret != num_bytes)
- goto fail;
- ret = 0;
-fail:
- if (ret > 0)
- ret = -1;
- return ret;
-}
-
static int csum_disk_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 disk_bytenr, u64 num_bytes)
{
- u32 blocksize = root->sectorsize;
+ u32 blocksize = root->fs_info->sectorsize;
u64 offset;
char *buffer;
int ret = 0;
@@ -228,198 +187,12 @@ static int csum_disk_extent(struct btrfs_trans_handle *trans,
return ret;
}
-struct blk_iterate_data {
- struct btrfs_trans_handle *trans;
- struct btrfs_root *root;
- struct btrfs_root *convert_root;
- struct btrfs_inode_item *inode;
- u64 convert_ino;
- u64 objectid;
- u64 first_block;
- u64 disk_block;
- u64 num_blocks;
- u64 boundary;
- int checksum;
- int errcode;
-};
-
-static void init_blk_iterate_data(struct blk_iterate_data *data,
- struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_inode_item *inode,
- u64 objectid, int checksum)
-{
- struct btrfs_key key;
-
- data->trans = trans;
- data->root = root;
- data->inode = inode;
- data->objectid = objectid;
- data->first_block = 0;
- data->disk_block = 0;
- data->num_blocks = 0;
- data->boundary = (u64)-1;
- data->checksum = checksum;
- data->errcode = 0;
-
- key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
- key.type = BTRFS_ROOT_ITEM_KEY;
- key.offset = (u64)-1;
- data->convert_root = btrfs_read_fs_root(root->fs_info, &key);
- /* Impossible as we just opened it before */
- BUG_ON(!data->convert_root || IS_ERR(data->convert_root));
- data->convert_ino = BTRFS_FIRST_FREE_OBJECTID + 1;
-}
-
-/*
- * Record a file extent in original filesystem into btrfs one.
- * The special point is, old disk_block can point to a reserved range.
- * So here, we don't use disk_block directly but search convert_root
- * to get the real disk_bytenr.
- */
-static int record_file_blocks(struct blk_iterate_data *data,
- u64 file_block, u64 disk_block, u64 num_blocks)
-{
- int ret = 0;
- struct btrfs_root *root = data->root;
- struct btrfs_root *convert_root = data->convert_root;
- 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;
- u64 cur_off = old_disk_bytenr;
-
- /* Hole, pass it to record_file_extent directly */
- if (old_disk_bytenr == 0)
- return btrfs_record_file_extent(data->trans, root,
- data->objectid, data->inode, file_pos, 0,
- num_bytes);
-
- btrfs_init_path(&path);
-
- /*
- * Search real disk bytenr from convert root
- */
- while (cur_off < old_disk_bytenr + num_bytes) {
- struct btrfs_key key;
- struct btrfs_file_extent_item *fi;
- struct extent_buffer *node;
- int slot;
- u64 extent_disk_bytenr;
- u64 extent_num_bytes;
- u64 real_disk_bytenr;
- u64 cur_len;
-
- key.objectid = data->convert_ino;
- key.type = BTRFS_EXTENT_DATA_KEY;
- key.offset = cur_off;
-
- 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,
- data->convert_ino,
- BTRFS_EXTENT_DATA_KEY);
- if (ret < 0)
- break;
- if (ret > 0) {
- ret = -ENOENT;
- break;
- }
- }
- 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_num_bytes(node, fi);
- BUG_ON(cur_off - key.offset >= extent_num_bytes);
- btrfs_release_path(&path);
-
- if (extent_disk_bytenr)
- real_disk_bytenr = cur_off - key.offset +
- extent_disk_bytenr;
- else
- real_disk_bytenr = 0;
- cur_len = min(key.offset + extent_num_bytes,
- old_disk_bytenr + num_bytes) - cur_off;
- ret = btrfs_record_file_extent(data->trans, data->root,
- data->objectid, data->inode, file_pos,
- real_disk_bytenr, cur_len);
- if (ret < 0)
- break;
- cur_off += cur_len;
- file_pos += cur_len;
-
- /*
- * No need to care about csum
- * As every byte of old fs image is calculated for csum, no
- * need to waste CPU cycles now.
- */
- }
- btrfs_release_path(&path);
- return ret;
-}
-
-static int block_iterate_proc(u64 disk_block, u64 file_block,
- struct blk_iterate_data *idata)
-{
- int ret = 0;
- int sb_region;
- int do_barrier;
- struct btrfs_root *root = idata->root;
- struct btrfs_block_group_cache *cache;
- u64 bytenr = disk_block * root->sectorsize;
-
- sb_region = intersect_with_sb(bytenr, root->sectorsize);
- do_barrier = sb_region || disk_block >= idata->boundary;
- if ((idata->num_blocks > 0 && do_barrier) ||
- (file_block > idata->first_block + idata->num_blocks) ||
- (disk_block != idata->disk_block + idata->num_blocks)) {
- if (idata->num_blocks > 0) {
- ret = record_file_blocks(idata, idata->first_block,
- idata->disk_block,
- idata->num_blocks);
- if (ret)
- goto fail;
- idata->first_block += idata->num_blocks;
- idata->num_blocks = 0;
- }
- if (file_block > idata->first_block) {
- ret = record_file_blocks(idata, idata->first_block,
- 0, file_block - idata->first_block);
- if (ret)
- goto fail;
- }
-
- if (sb_region) {
- bytenr += BTRFS_STRIPE_LEN - 1;
- bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1);
- } else {
- cache = btrfs_lookup_block_group(root->fs_info, bytenr);
- BUG_ON(!cache);
- bytenr = cache->key.objectid + cache->key.offset;
- }
-
- idata->first_block = file_block;
- idata->disk_block = disk_block;
- idata->boundary = bytenr / root->sectorsize;
- }
- idata->num_blocks++;
-fail:
- return ret;
-}
-
static int create_image_file_range(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct cache_tree *used,
struct btrfs_inode_item *inode,
u64 ino, u64 bytenr, u64 *ret_len,
- int datacsum)
+ u32 convert_flags)
{
struct cache_extent *cache;
struct btrfs_block_group_cache *bg_cache;
@@ -427,13 +200,14 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
u64 disk_bytenr;
int i;
int ret;
+ u32 datacsum = convert_flags & CONVERT_FLAG_DATACSUM;
- if (bytenr != round_down(bytenr, root->sectorsize)) {
+ if (bytenr != round_down(bytenr, root->fs_info->sectorsize)) {
error("bytenr not sectorsize aligned: %llu",
(unsigned long long)bytenr);
return -EINVAL;
}
- if (len != round_down(len, root->sectorsize)) {
+ if (len != round_down(len, root->fs_info->sectorsize)) {
error("length not sectorsize aligned: %llu",
(unsigned long long)len);
return -EINVAL;
@@ -441,47 +215,40 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
len = min_t(u64, len, BTRFS_MAX_EXTENT_SIZE);
/*
- * Skip sb ranges first
- * [0, 1M), [sb_offset(1), +64K), [sb_offset(2), +64K].
+ * Skip reserved ranges first
*
* Or we will insert a hole into current image file, and later
* migrate block will fail as there is already a file extent.
*/
- if (bytenr < 1024 * 1024) {
- *ret_len = 1024 * 1024 - bytenr;
- return 0;
- }
- for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) {
- u64 cur = btrfs_sb_offset(i);
+ for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) {
+ const struct simple_range *reserved = &btrfs_reserved_ranges[i];
- if (bytenr >= cur && bytenr < cur + BTRFS_STRIPE_LEN) {
- *ret_len = cur + BTRFS_STRIPE_LEN - bytenr;
+ /*
+ * |-- reserved --|
+ * |--range---|
+ * or
+ * |---- reserved ----|
+ * |-- range --|
+ * Skip to reserved range end
+ */
+ if (bytenr >= reserved->start && bytenr < range_end(reserved)) {
+ *ret_len = range_end(reserved) - bytenr;
return 0;
}
- }
- for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) {
- u64 cur = btrfs_sb_offset(i);
/*
- * |--reserved--|
+ * |---reserved---|
* |----range-------|
- * May still need to go through file extent inserts
+ * Leading part may still create a file extent
*/
- if (bytenr < cur && bytenr + len >= cur) {
- len = min_t(u64, len, cur - bytenr);
+ if (bytenr < reserved->start &&
+ bytenr + len >= range_end(reserved)) {
+ len = min_t(u64, len, reserved->start - bytenr);
break;
}
- /*
- * |--reserved--|
- * |---range---|
- * Drop out, no need to insert anything
- */
- if (bytenr >= cur && bytenr < cur + BTRFS_STRIPE_LEN) {
- *ret_len = cur + BTRFS_STRIPE_LEN - bytenr;
- return 0;
- }
}
+ /* Check if we are going to insert regular file extent, or hole */
cache = search_cache_extent(used, bytenr);
if (cache) {
if (cache->start <= bytenr) {
@@ -489,6 +256,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
* |///////Used///////|
* |<--insert--->|
* bytenr
+ * Insert one real file extent
*/
len = min_t(u64, len, cache->start + cache->size -
bytenr);
@@ -498,6 +266,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
* |//Used//|
* |<-insert-->|
* bytenr
+ * Insert one hole
*/
len = min(len, cache->start - bytenr);
disk_bytenr = 0;
@@ -508,6 +277,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
* |//Used//| |EOF
* |<-insert-->|
* bytenr
+ * Insert one hole
*/
disk_bytenr = 0;
datacsum = 0;
@@ -526,7 +296,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
bg_cache->key.offset - bytenr);
}
- if (len != round_down(len, root->sectorsize)) {
+ if (len != round_down(len, root->fs_info->sectorsize)) {
error("remaining length not sectorsize aligned: %llu",
(unsigned long long)len);
return -EINVAL;
@@ -553,25 +323,33 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct cache_tree *used,
struct btrfs_inode_item *inode, int fd,
- u64 ino, u64 start, u64 len, int datacsum)
+ u64 ino, const struct simple_range *range,
+ u32 convert_flags)
{
- u64 cur_off = start;
- u64 cur_len = len;
- u64 hole_start = start;
+ u64 cur_off = range->start;
+ u64 cur_len = range->len;
+ u64 hole_start = range->start;
u64 hole_len;
struct cache_extent *cache;
struct btrfs_key key;
struct extent_buffer *eb;
int ret = 0;
- while (cur_off < start + len) {
+ /*
+ * It's possible that there are holes in reserved range:
+ * |<---------------- Reserved range ---------------------->|
+ * |<- Old fs data ->| |<- Old fs data ->|
+ * So here we need to iterate through old fs used space and only
+ * migrate ranges that covered by old fs data.
+ */
+ while (cur_off < range_end(range)) {
cache = lookup_cache_extent(used, cur_off, cur_len);
if (!cache)
break;
cur_off = max(cache->start, cur_off);
- cur_len = min(cache->start + cache->size, start + len) -
+ cur_len = min(cache->start + cache->size, range_end(range)) -
cur_off;
- BUG_ON(cur_len < root->sectorsize);
+ BUG_ON(cur_len < root->fs_info->sectorsize);
/* reserve extent for the data */
ret = btrfs_reserve_extent(trans, root, cur_len, 0, 0, (u64)-1,
@@ -595,7 +373,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans,
eb->len = key.offset;
/* Write the data */
- ret = write_and_map_eb(trans, root, eb);
+ ret = write_and_map_eb(root->fs_info, eb);
free(eb);
if (ret < 0)
break;
@@ -606,7 +384,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans,
if (ret < 0)
break;
/* Finally, insert csum items */
- if (datacsum)
+ if (convert_flags & CONVERT_FLAG_DATACSUM)
ret = csum_disk_extent(trans, root, key.objectid,
key.offset);
@@ -621,56 +399,43 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans,
cur_off += key.offset;
hole_start = cur_off;
- cur_len = start + len - cur_off;
+ cur_len = range_end(range) - cur_off;
}
- /* Last hole */
- if (start + len - hole_start > 0)
+ /*
+ * Last hole
+ * |<---- reserved -------->|
+ * |<- Old fs data ->| |
+ * | Hole |
+ */
+ if (range_end(range) - hole_start > 0)
ret = btrfs_record_file_extent(trans, root, ino, inode,
- hole_start, 0, start + len - hole_start);
+ hole_start, 0, range_end(range) - hole_start);
return ret;
}
/*
* Relocate the used ext2 data in reserved ranges
- * [0,1M)
- * [btrfs_sb_offset(1), +BTRFS_STRIPE_LEN)
- * [btrfs_sb_offset(2), +BTRFS_STRIPE_LEN)
*/
static int migrate_reserved_ranges(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct cache_tree *used,
struct btrfs_inode_item *inode, int fd,
- u64 ino, u64 total_bytes, int datacsum)
+ u64 ino, u64 total_bytes, u32 convert_flags)
{
- u64 cur_off;
- u64 cur_len;
+ int i;
int ret = 0;
- /* 0 ~ 1M */
- cur_off = 0;
- cur_len = 1024 * 1024;
- ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino,
- cur_off, cur_len, datacsum);
- if (ret < 0)
- return ret;
+ for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) {
+ const struct simple_range *range = &btrfs_reserved_ranges[i];
- /* second sb(fisrt sb is included in 0~1M) */
- cur_off = btrfs_sb_offset(1);
- cur_len = min(total_bytes, cur_off + BTRFS_STRIPE_LEN) - cur_off;
- if (cur_off > total_bytes)
- return ret;
- ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino,
- cur_off, cur_len, datacsum);
- if (ret < 0)
- return ret;
+ if (range->start > total_bytes)
+ return ret;
+ ret = migrate_one_reserved_range(trans, root, used, inode, fd,
+ ino, range, convert_flags);
+ if (ret < 0)
+ return ret;
+ }
- /* Last sb */
- cur_off = btrfs_sb_offset(2);
- cur_len = min(total_bytes, cur_off + BTRFS_STRIPE_LEN) - cur_off;
- if (cur_off > total_bytes)
- return ret;
- ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino,
- cur_off, cur_len, datacsum);
return ret;
}
@@ -843,26 +608,25 @@ static int wipe_one_reserved_range(struct cache_tree *tree,
static int wipe_reserved_ranges(struct cache_tree *tree, u64 min_stripe_size,
int ensure_size)
{
+ int i;
int ret;
- ret = wipe_one_reserved_range(tree, 0, 1024 * 1024, min_stripe_size,
- ensure_size);
- if (ret < 0)
- return ret;
- ret = wipe_one_reserved_range(tree, btrfs_sb_offset(1),
- BTRFS_STRIPE_LEN, min_stripe_size, ensure_size);
- if (ret < 0)
- return ret;
- ret = wipe_one_reserved_range(tree, btrfs_sb_offset(2),
- BTRFS_STRIPE_LEN, min_stripe_size, ensure_size);
+ for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) {
+ const struct simple_range *range = &btrfs_reserved_ranges[i];
+
+ ret = wipe_one_reserved_range(tree, range->start, range->len,
+ min_stripe_size, ensure_size);
+ if (ret < 0)
+ return ret;
+ }
return ret;
}
static int calculate_available_space(struct btrfs_convert_context *cctx)
{
- struct cache_tree *used = &cctx->used;
+ struct cache_tree *used = &cctx->used_space;
struct cache_tree *data_chunks = &cctx->data_chunks;
- struct cache_tree *free = &cctx->free;
+ struct cache_tree *free = &cctx->free_space;
struct cache_extent *cache;
u64 cur_off = 0;
/*
@@ -964,7 +728,7 @@ static int convert_read_used_space(struct btrfs_convert_context *cctx)
static int create_image(struct btrfs_root *root,
struct btrfs_mkfs_config *cfg,
struct btrfs_convert_context *cctx, int fd,
- u64 size, char *name, int datacsum)
+ u64 size, char *name, u32 convert_flags)
{
struct btrfs_inode_item buf;
struct btrfs_trans_handle *trans;
@@ -977,7 +741,7 @@ static int create_image(struct btrfs_root *root,
u64 flags = BTRFS_INODE_READONLY;
int ret;
- if (!datacsum)
+ if (!(convert_flags & CONVERT_FLAG_DATACSUM))
flags |= BTRFS_INODE_NODATASUM;
trans = btrfs_start_transaction(root, 1);
@@ -1020,7 +784,7 @@ static int create_image(struct btrfs_root *root,
* Create a new used space cache, which doesn't contain the reserved
* range
*/
- for (cache = first_cache_extent(&cctx->used); cache;
+ for (cache = first_cache_extent(&cctx->used_space); cache;
cache = next_cache_extent(cache)) {
ret = add_cache_extent(&used_tmp, cache->start, cache->size);
if (ret < 0)
@@ -1039,15 +803,15 @@ static int create_image(struct btrfs_root *root,
u64 len = size - cur;
ret = create_image_file_range(trans, root, &used_tmp,
- &buf, ino, cur, &len, datacsum);
+ &buf, ino, cur, &len,
+ convert_flags);
if (ret < 0)
goto out;
cur += len;
}
/* Handle the reserved ranges */
- ret = migrate_reserved_ranges(trans, root, &cctx->used, &buf, fd, ino,
- cfg->num_bytes, datacsum);
-
+ ret = migrate_reserved_ranges(trans, root, &cctx->used_space, &buf, fd,
+ ino, cfg->num_bytes, convert_flags);
key.objectid = ino;
key.type = BTRFS_INODE_ITEM_KEY;
@@ -1250,7 +1014,8 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans,
*/
max_chunk_size = cfg->num_bytes / 10;
max_chunk_size = min((u64)(1024 * 1024 * 1024), max_chunk_size);
- max_chunk_size = round_down(max_chunk_size, extent_root->sectorsize);
+ max_chunk_size = round_down(max_chunk_size,
+ extent_root->fs_info->sectorsize);
for (cache = first_cache_extent(data_chunks); cache;
cache = next_cache_extent(cache)) {
@@ -1262,12 +1027,12 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans,
len = min(max_chunk_size,
cache->start + cache->size - cur);
- ret = btrfs_alloc_data_chunk(trans, extent_root,
+ ret = btrfs_alloc_data_chunk(trans, fs_info,
&cur_backup, len,
BTRFS_BLOCK_GROUP_DATA, 1);
if (ret < 0)
break;
- ret = btrfs_make_block_group(trans, extent_root, 0,
+ ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_DATA,
BTRFS_FIRST_CHUNK_TREE_OBJECTID,
cur, len);
@@ -1291,8 +1056,7 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans,
* But the convert image subvolume is *NOT* linked to fs tree yet.
*/
static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root,
- struct btrfs_convert_context *cctx, int datacsum,
- int packing, int noxattr)
+ struct btrfs_convert_context *cctx, u32 convert_flags)
{
struct btrfs_key location;
struct btrfs_trans_handle *trans;
@@ -1412,951 +1176,11 @@ fail:
return ret;
}
-static int prepare_system_chunk_sb(struct btrfs_super_block *super)
-{
- struct btrfs_chunk *chunk;
- struct btrfs_disk_key *key;
- u32 sectorsize = btrfs_super_sectorsize(super);
-
- key = (struct btrfs_disk_key *)(super->sys_chunk_array);
- chunk = (struct btrfs_chunk *)(super->sys_chunk_array +
- sizeof(struct btrfs_disk_key));
-
- btrfs_set_disk_key_objectid(key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
- btrfs_set_disk_key_type(key, BTRFS_CHUNK_ITEM_KEY);
- btrfs_set_disk_key_offset(key, 0);
-
- btrfs_set_stack_chunk_length(chunk, btrfs_super_total_bytes(super));
- btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID);
- btrfs_set_stack_chunk_stripe_len(chunk, BTRFS_STRIPE_LEN);
- btrfs_set_stack_chunk_type(chunk, BTRFS_BLOCK_GROUP_SYSTEM);
- btrfs_set_stack_chunk_io_align(chunk, sectorsize);
- btrfs_set_stack_chunk_io_width(chunk, sectorsize);
- btrfs_set_stack_chunk_sector_size(chunk, sectorsize);
- btrfs_set_stack_chunk_num_stripes(chunk, 1);
- btrfs_set_stack_chunk_sub_stripes(chunk, 0);
- chunk->stripe.devid = super->dev_item.devid;
- btrfs_set_stack_stripe_offset(&chunk->stripe, 0);
- memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid, BTRFS_UUID_SIZE);
- btrfs_set_super_sys_array_size(super, sizeof(*key) + sizeof(*chunk));
- return 0;
-}
-
-#if BTRFSCONVERT_EXT2
-
-/*
- * Open Ext2fs in readonly mode, read block allocation bitmap and
- * inode bitmap into memory.
- */
-static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
-{
- errcode_t ret;
- ext2_filsys ext2_fs;
- ext2_ino_t ino;
- u32 ro_feature;
-
- ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs);
- if (ret) {
- fprintf(stderr, "ext2fs_open: %s\n", error_message(ret));
- return -1;
- }
- /*
- * We need to know exactly the used space, some RO compat flags like
- * BIGALLOC will affect how used space is present.
- * So we need manuall check any unsupported RO compat flags
- */
- ro_feature = ext2_fs->super->s_feature_ro_compat;
- if (ro_feature & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
- error(
-"unsupported RO features detected: %x, abort convert to avoid possible corruption",
- ro_feature & ~EXT2_LIB_FEATURE_COMPAT_SUPP);
- goto fail;
- }
- ret = ext2fs_read_inode_bitmap(ext2_fs);
- if (ret) {
- fprintf(stderr, "ext2fs_read_inode_bitmap: %s\n",
- error_message(ret));
- goto fail;
- }
- ret = ext2fs_read_block_bitmap(ext2_fs);
- if (ret) {
- fprintf(stderr, "ext2fs_read_block_bitmap: %s\n",
- error_message(ret));
- goto fail;
- }
- /*
- * search each block group for a free inode. this set up
- * uninit block/inode bitmaps appropriately.
- */
- ino = 1;
- while (ino <= ext2_fs->super->s_inodes_count) {
- ext2_ino_t foo;
- ext2fs_new_inode(ext2_fs, ino, 0, NULL, &foo);
- ino += EXT2_INODES_PER_GROUP(ext2_fs->super);
- }
-
- if (!(ext2_fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE)) {
- error("filetype feature is missing");
- goto fail;
- }
-
- cctx->fs_data = ext2_fs;
- cctx->blocksize = ext2_fs->blocksize;
- cctx->block_count = ext2_fs->super->s_blocks_count;
- cctx->total_bytes = ext2_fs->blocksize * ext2_fs->super->s_blocks_count;
- cctx->volume_name = strndup(ext2_fs->super->s_volume_name, 16);
- cctx->first_data_block = ext2_fs->super->s_first_data_block;
- cctx->inodes_count = ext2_fs->super->s_inodes_count;
- cctx->free_inodes_count = ext2_fs->super->s_free_inodes_count;
- return 0;
-fail:
- ext2fs_close(ext2_fs);
- return -1;
-}
-
-static int __ext2_add_one_block(ext2_filsys fs, char *bitmap,
- unsigned long group_nr, struct cache_tree *used)
-{
- unsigned long offset;
- unsigned i;
- int ret = 0;
-
- offset = fs->super->s_first_data_block;
- 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;
-
- start = (i + offset) * EXT2FS_CLUSTER_RATIO(fs);
- start *= fs->blocksize;
- ret = add_merge_cache_extent(used, start,
- fs->blocksize);
- if (ret < 0)
- break;
- }
- }
- return ret;
-}
-
-/*
- * Read all used ext2 space into cctx->used cache tree
- */
-static int ext2_read_used_space(struct btrfs_convert_context *cctx)
-{
- ext2_filsys fs = (ext2_filsys)cctx->fs_data;
- blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
- struct cache_tree *used_tree = &cctx->used;
- char *block_bitmap = NULL;
- unsigned long i;
- int block_nbytes;
- int ret = 0;
-
- block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
- /* Shouldn't happen */
- BUG_ON(!fs->block_map);
-
- block_bitmap = malloc(block_nbytes);
- if (!block_bitmap)
- return -ENOMEM;
-
- for (i = 0; i < fs->group_desc_count; i++) {
- ret = ext2fs_get_block_bitmap_range(fs->block_map, blk_itr,
- block_nbytes * 8, block_bitmap);
- if (ret) {
- error("fail to get bitmap from ext2, %s",
- strerror(-ret));
- break;
- }
- ret = __ext2_add_one_block(fs, block_bitmap, i, used_tree);
- if (ret < 0) {
- error("fail to build used space tree, %s",
- strerror(-ret));
- break;
- }
- blk_itr += EXT2_CLUSTERS_PER_GROUP(fs->super);
- }
-
- free(block_bitmap);
- return ret;
-}
-
-static void ext2_close_fs(struct btrfs_convert_context *cctx)
-{
- if (cctx->volume_name) {
- free(cctx->volume_name);
- cctx->volume_name = NULL;
- }
- ext2fs_close(cctx->fs_data);
-}
-
-struct dir_iterate_data {
- struct btrfs_trans_handle *trans;
- struct btrfs_root *root;
- struct btrfs_inode_item *inode;
- u64 objectid;
- u64 index_cnt;
- u64 parent;
- int errcode;
-};
-
-static u8 ext2_filetype_conversion_table[EXT2_FT_MAX] = {
- [EXT2_FT_UNKNOWN] = BTRFS_FT_UNKNOWN,
- [EXT2_FT_REG_FILE] = BTRFS_FT_REG_FILE,
- [EXT2_FT_DIR] = BTRFS_FT_DIR,
- [EXT2_FT_CHRDEV] = BTRFS_FT_CHRDEV,
- [EXT2_FT_BLKDEV] = BTRFS_FT_BLKDEV,
- [EXT2_FT_FIFO] = BTRFS_FT_FIFO,
- [EXT2_FT_SOCK] = BTRFS_FT_SOCK,
- [EXT2_FT_SYMLINK] = BTRFS_FT_SYMLINK,
-};
-
-static int ext2_dir_iterate_proc(ext2_ino_t dir, int entry,
- struct ext2_dir_entry *dirent,
- int offset, int blocksize,
- char *buf,void *priv_data)
-{
- int ret;
- int file_type;
- u64 objectid;
- char dotdot[] = "..";
- struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data;
- int name_len;
-
- name_len = dirent->name_len & 0xFF;
-
- objectid = dirent->inode + INO_OFFSET;
- if (!strncmp(dirent->name, dotdot, name_len)) {
- if (name_len == 2) {
- BUG_ON(idata->parent != 0);
- idata->parent = objectid;
- }
- return 0;
- }
- if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO)
- return 0;
-
- file_type = dirent->name_len >> 8;
- BUG_ON(file_type > EXT2_FT_SYMLINK);
-
- ret = convert_insert_dirent(idata->trans, idata->root, dirent->name,
- name_len, idata->objectid, objectid,
- ext2_filetype_conversion_table[file_type],
- idata->index_cnt, idata->inode);
- if (ret < 0) {
- idata->errcode = ret;
- return BLOCK_ABORT;
- }
-
- idata->index_cnt++;
- return 0;
-}
-
-static int ext2_create_dir_entries(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- struct btrfs_inode_item *btrfs_inode,
- ext2_filsys ext2_fs, ext2_ino_t ext2_ino)
-{
- int ret;
- errcode_t err;
- struct dir_iterate_data data = {
- .trans = trans,
- .root = root,
- .inode = btrfs_inode,
- .objectid = objectid,
- .index_cnt = 2,
- .parent = 0,
- .errcode = 0,
- };
-
- err = ext2fs_dir_iterate2(ext2_fs, ext2_ino, 0, NULL,
- ext2_dir_iterate_proc, &data);
- if (err)
- goto error;
- ret = data.errcode;
- if (ret == 0 && data.parent == objectid) {
- ret = btrfs_insert_inode_ref(trans, root, "..", 2,
- objectid, objectid, 0);
- }
- return ret;
-error:
- fprintf(stderr, "ext2fs_dir_iterate2: %s\n", error_message(err));
- return -1;
-}
-
-static int ext2_block_iterate_proc(ext2_filsys fs, blk_t *blocknr,
- e2_blkcnt_t blockcnt, blk_t ref_block,
- int ref_offset, void *priv_data)
-{
- int ret;
- struct blk_iterate_data *idata;
- idata = (struct blk_iterate_data *)priv_data;
- ret = block_iterate_proc(*blocknr, blockcnt, idata);
- if (ret) {
- idata->errcode = ret;
- return BLOCK_ABORT;
- }
- return 0;
-}
-
-/*
- * traverse file's data blocks, record these data blocks as file extents.
- */
-static int ext2_create_file_extents(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- struct btrfs_inode_item *btrfs_inode,
- ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
- int datacsum, int packing)
-{
- int ret;
- char *buffer = NULL;
- errcode_t err;
- u32 last_block;
- u32 sectorsize = root->sectorsize;
- u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
- struct blk_iterate_data data;
-
- init_blk_iterate_data(&data, trans, root, btrfs_inode, objectid,
- datacsum);
-
- err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY,
- NULL, ext2_block_iterate_proc, &data);
- if (err)
- goto error;
- ret = data.errcode;
- if (ret)
- goto fail;
- if (packing && data.first_block == 0 && data.num_blocks > 0 &&
- inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
- u64 num_bytes = data.num_blocks * sectorsize;
- u64 disk_bytenr = data.disk_block * sectorsize;
- u64 nbytes;
-
- buffer = malloc(num_bytes);
- if (!buffer)
- return -ENOMEM;
- ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer);
- if (ret)
- goto fail;
- if (num_bytes > inode_size)
- num_bytes = inode_size;
- ret = btrfs_insert_inline_extent(trans, root, objectid,
- 0, buffer, num_bytes);
- if (ret)
- goto fail;
- nbytes = btrfs_stack_inode_nbytes(btrfs_inode) + num_bytes;
- btrfs_set_stack_inode_nbytes(btrfs_inode, nbytes);
- } else if (data.num_blocks > 0) {
- ret = record_file_blocks(&data, data.first_block,
- data.disk_block, data.num_blocks);
- if (ret)
- goto fail;
- }
- data.first_block += data.num_blocks;
- last_block = (inode_size + sectorsize - 1) / sectorsize;
- if (last_block > data.first_block) {
- ret = record_file_blocks(&data, data.first_block, 0,
- last_block - data.first_block);
- }
-fail:
- free(buffer);
- return ret;
-error:
- fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err));
- return -1;
-}
-
-static int ext2_create_symbol_link(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- struct btrfs_inode_item *btrfs_inode,
- ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
- struct ext2_inode *ext2_inode)
-{
- int ret;
- char *pathname;
- u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
- if (ext2fs_inode_data_blocks(ext2_fs, ext2_inode)) {
- btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1);
- ret = ext2_create_file_extents(trans, root, objectid,
- btrfs_inode, ext2_fs, ext2_ino, 1, 1);
- btrfs_set_stack_inode_size(btrfs_inode, inode_size);
- return ret;
- }
-
- pathname = (char *)&(ext2_inode->i_block[0]);
- BUG_ON(pathname[inode_size] != 0);
- ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
- pathname, inode_size + 1);
- btrfs_set_stack_inode_nbytes(btrfs_inode, inode_size + 1);
- return ret;
-}
-
-/*
- * Following xattr/acl related codes are based on codes in
- * fs/ext3/xattr.c and fs/ext3/acl.c
- */
-#define EXT2_XATTR_BHDR(ptr) ((struct ext2_ext_attr_header *)(ptr))
-#define EXT2_XATTR_BFIRST(ptr) \
- ((struct ext2_ext_attr_entry *)(EXT2_XATTR_BHDR(ptr) + 1))
-#define EXT2_XATTR_IHDR(inode) \
- ((struct ext2_ext_attr_header *) ((void *)(inode) + \
- EXT2_GOOD_OLD_INODE_SIZE + (inode)->i_extra_isize))
-#define EXT2_XATTR_IFIRST(inode) \
- ((struct ext2_ext_attr_entry *) ((void *)EXT2_XATTR_IHDR(inode) + \
- sizeof(EXT2_XATTR_IHDR(inode)->h_magic)))
-
-static int ext2_xattr_check_names(struct ext2_ext_attr_entry *entry,
- const void *end)
-{
- struct ext2_ext_attr_entry *next;
-
- while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
- next = EXT2_EXT_ATTR_NEXT(entry);
- if ((void *)next >= end)
- return -EIO;
- entry = next;
- }
- return 0;
-}
-
-static int ext2_xattr_check_block(const char *buf, size_t size)
-{
- int error;
- struct ext2_ext_attr_header *header = EXT2_XATTR_BHDR(buf);
-
- if (header->h_magic != EXT2_EXT_ATTR_MAGIC ||
- header->h_blocks != 1)
- return -EIO;
- error = ext2_xattr_check_names(EXT2_XATTR_BFIRST(buf), buf + size);
- return error;
-}
-
-static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry,
- size_t size)
-{
- size_t value_size = entry->e_value_size;
-
- if (entry->e_value_block != 0 || value_size > size ||
- entry->e_value_offs + value_size > size)
- return -EIO;
- return 0;
-}
-
-#define EXT2_ACL_VERSION 0x0001
-
-/* 23.2.5 acl_tag_t values */
-
-#define ACL_UNDEFINED_TAG (0x00)
-#define ACL_USER_OBJ (0x01)
-#define ACL_USER (0x02)
-#define ACL_GROUP_OBJ (0x04)
-#define ACL_GROUP (0x08)
-#define ACL_MASK (0x10)
-#define ACL_OTHER (0x20)
-
-/* 23.2.7 ACL qualifier constants */
-
-#define ACL_UNDEFINED_ID ((id_t)-1)
-
-typedef struct {
- __le16 e_tag;
- __le16 e_perm;
- __le32 e_id;
-} ext2_acl_entry;
-
-typedef struct {
- __le16 e_tag;
- __le16 e_perm;
-} ext2_acl_entry_short;
-
-typedef struct {
- __le32 a_version;
-} ext2_acl_header;
-
-static inline int ext2_acl_count(size_t size)
-{
- ssize_t s;
- size -= sizeof(ext2_acl_header);
- s = size - 4 * sizeof(ext2_acl_entry_short);
- if (s < 0) {
- if (size % sizeof(ext2_acl_entry_short))
- return -1;
- return size / sizeof(ext2_acl_entry_short);
- } else {
- if (s % sizeof(ext2_acl_entry))
- return -1;
- return s / sizeof(ext2_acl_entry) + 4;
- }
-}
-
-#define ACL_EA_VERSION 0x0002
-
-typedef struct {
- __le16 e_tag;
- __le16 e_perm;
- __le32 e_id;
-} acl_ea_entry;
-
-typedef struct {
- __le32 a_version;
- acl_ea_entry a_entries[0];
-} acl_ea_header;
-
-static inline size_t acl_ea_size(int count)
-{
- return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry);
-}
-
-static int ext2_acl_to_xattr(void *dst, const void *src,
- size_t dst_size, size_t src_size)
-{
- int i, count;
- const void *end = src + src_size;
- acl_ea_header *ext_acl = (acl_ea_header *)dst;
- acl_ea_entry *dst_entry = ext_acl->a_entries;
- ext2_acl_entry *src_entry;
-
- if (src_size < sizeof(ext2_acl_header))
- goto fail;
- if (((ext2_acl_header *)src)->a_version !=
- cpu_to_le32(EXT2_ACL_VERSION))
- goto fail;
- src += sizeof(ext2_acl_header);
- count = ext2_acl_count(src_size);
- if (count <= 0)
- goto fail;
-
- BUG_ON(dst_size < acl_ea_size(count));
- ext_acl->a_version = cpu_to_le32(ACL_EA_VERSION);
- for (i = 0; i < count; i++, dst_entry++) {
- src_entry = (ext2_acl_entry *)src;
- if (src + sizeof(ext2_acl_entry_short) > end)
- goto fail;
- dst_entry->e_tag = src_entry->e_tag;
- dst_entry->e_perm = src_entry->e_perm;
- switch (le16_to_cpu(src_entry->e_tag)) {
- case ACL_USER_OBJ:
- case ACL_GROUP_OBJ:
- case ACL_MASK:
- case ACL_OTHER:
- src += sizeof(ext2_acl_entry_short);
- dst_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
- break;
- case ACL_USER:
- case ACL_GROUP:
- src += sizeof(ext2_acl_entry);
- if (src > end)
- goto fail;
- dst_entry->e_id = src_entry->e_id;
- break;
- default:
- goto fail;
- }
- }
- if (src != end)
- goto fail;
- return 0;
-fail:
- return -EINVAL;
-}
-
-static char *xattr_prefix_table[] = {
- [1] = "user.",
- [2] = "system.posix_acl_access",
- [3] = "system.posix_acl_default",
- [4] = "trusted.",
- [6] = "security.",
-};
-
-static int ext2_copy_single_xattr(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- struct ext2_ext_attr_entry *entry,
- const void *data, u32 datalen)
-{
- int ret = 0;
- int name_len;
- int name_index;
- void *databuf = NULL;
- char namebuf[XATTR_NAME_MAX + 1];
-
- name_index = entry->e_name_index;
- if (name_index >= ARRAY_SIZE(xattr_prefix_table) ||
- xattr_prefix_table[name_index] == NULL)
- return -EOPNOTSUPP;
- name_len = strlen(xattr_prefix_table[name_index]) +
- entry->e_name_len;
- if (name_len >= sizeof(namebuf))
- return -ERANGE;
-
- if (name_index == 2 || name_index == 3) {
- size_t bufsize = acl_ea_size(ext2_acl_count(datalen));
- databuf = malloc(bufsize);
- if (!databuf)
- return -ENOMEM;
- ret = ext2_acl_to_xattr(databuf, data, bufsize, datalen);
- if (ret)
- goto out;
- data = databuf;
- datalen = bufsize;
- }
- strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX);
- strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
- if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) -
- sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
- fprintf(stderr, "skip large xattr on inode %Lu name %.*s\n",
- objectid - INO_OFFSET, name_len, namebuf);
- goto out;
- }
- ret = btrfs_insert_xattr_item(trans, root, namebuf, name_len,
- data, datalen, objectid);
-out:
- free(databuf);
- return ret;
-}
-
-static int ext2_copy_extended_attrs(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- struct btrfs_inode_item *btrfs_inode,
- ext2_filsys ext2_fs, ext2_ino_t ext2_ino)
-{
- int ret = 0;
- int inline_ea = 0;
- errcode_t err;
- u32 datalen;
- u32 block_size = ext2_fs->blocksize;
- u32 inode_size = EXT2_INODE_SIZE(ext2_fs->super);
- struct ext2_inode_large *ext2_inode;
- struct ext2_ext_attr_entry *entry;
- void *data;
- char *buffer = NULL;
- char inode_buf[EXT2_GOOD_OLD_INODE_SIZE];
-
- if (inode_size <= EXT2_GOOD_OLD_INODE_SIZE) {
- ext2_inode = (struct ext2_inode_large *)inode_buf;
- } else {
- ext2_inode = (struct ext2_inode_large *)malloc(inode_size);
- if (!ext2_inode)
- return -ENOMEM;
- }
- err = ext2fs_read_inode_full(ext2_fs, ext2_ino, (void *)ext2_inode,
- inode_size);
- if (err) {
- fprintf(stderr, "ext2fs_read_inode_full: %s\n",
- error_message(err));
- ret = -1;
- goto out;
- }
-
- if (ext2_ino > ext2_fs->super->s_first_ino &&
- inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
- if (EXT2_GOOD_OLD_INODE_SIZE +
- ext2_inode->i_extra_isize > inode_size) {
- ret = -EIO;
- goto out;
- }
- if (ext2_inode->i_extra_isize != 0 &&
- EXT2_XATTR_IHDR(ext2_inode)->h_magic ==
- EXT2_EXT_ATTR_MAGIC) {
- inline_ea = 1;
- }
- }
- if (inline_ea) {
- int total;
- void *end = (void *)ext2_inode + inode_size;
- entry = EXT2_XATTR_IFIRST(ext2_inode);
- total = end - (void *)entry;
- ret = ext2_xattr_check_names(entry, end);
- if (ret)
- goto out;
- while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
- ret = ext2_xattr_check_entry(entry, total);
- if (ret)
- goto out;
- data = (void *)EXT2_XATTR_IFIRST(ext2_inode) +
- entry->e_value_offs;
- datalen = entry->e_value_size;
- ret = ext2_copy_single_xattr(trans, root, objectid,
- entry, data, datalen);
- if (ret)
- goto out;
- entry = EXT2_EXT_ATTR_NEXT(entry);
- }
- }
-
- if (ext2_inode->i_file_acl == 0)
- goto out;
-
- buffer = malloc(block_size);
- if (!buffer) {
- ret = -ENOMEM;
- goto out;
- }
- err = ext2fs_read_ext_attr(ext2_fs, ext2_inode->i_file_acl, buffer);
- if (err) {
- fprintf(stderr, "ext2fs_read_ext_attr: %s\n",
- error_message(err));
- ret = -1;
- goto out;
- }
- ret = ext2_xattr_check_block(buffer, block_size);
- if (ret)
- goto out;
-
- entry = EXT2_XATTR_BFIRST(buffer);
- while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
- ret = ext2_xattr_check_entry(entry, block_size);
- if (ret)
- goto out;
- data = buffer + entry->e_value_offs;
- datalen = entry->e_value_size;
- ret = ext2_copy_single_xattr(trans, root, objectid,
- entry, data, datalen);
- if (ret)
- goto out;
- entry = EXT2_EXT_ATTR_NEXT(entry);
- }
-out:
- free(buffer);
- if ((void *)ext2_inode != inode_buf)
- free(ext2_inode);
- return ret;
-}
-#define MINORBITS 20
-#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi))
-
-static inline dev_t old_decode_dev(u16 val)
-{
- return MKDEV((val >> 8) & 255, val & 255);
-}
-
-static inline dev_t new_decode_dev(u32 dev)
-{
- unsigned major = (dev & 0xfff00) >> 8;
- unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
- return MKDEV(major, minor);
-}
-
-static void ext2_copy_inode_item(struct btrfs_inode_item *dst,
- struct ext2_inode *src, u32 blocksize)
-{
- btrfs_set_stack_inode_generation(dst, 1);
- btrfs_set_stack_inode_sequence(dst, 0);
- btrfs_set_stack_inode_transid(dst, 1);
- btrfs_set_stack_inode_size(dst, src->i_size);
- btrfs_set_stack_inode_nbytes(dst, 0);
- btrfs_set_stack_inode_block_group(dst, 0);
- btrfs_set_stack_inode_nlink(dst, src->i_links_count);
- btrfs_set_stack_inode_uid(dst, src->i_uid | (src->i_uid_high << 16));
- btrfs_set_stack_inode_gid(dst, src->i_gid | (src->i_gid_high << 16));
- btrfs_set_stack_inode_mode(dst, src->i_mode);
- btrfs_set_stack_inode_rdev(dst, 0);
- btrfs_set_stack_inode_flags(dst, 0);
- btrfs_set_stack_timespec_sec(&dst->atime, src->i_atime);
- btrfs_set_stack_timespec_nsec(&dst->atime, 0);
- btrfs_set_stack_timespec_sec(&dst->ctime, src->i_ctime);
- btrfs_set_stack_timespec_nsec(&dst->ctime, 0);
- btrfs_set_stack_timespec_sec(&dst->mtime, src->i_mtime);
- btrfs_set_stack_timespec_nsec(&dst->mtime, 0);
- btrfs_set_stack_timespec_sec(&dst->otime, 0);
- btrfs_set_stack_timespec_nsec(&dst->otime, 0);
-
- if (S_ISDIR(src->i_mode)) {
- btrfs_set_stack_inode_size(dst, 0);
- btrfs_set_stack_inode_nlink(dst, 1);
- }
- if (S_ISREG(src->i_mode)) {
- btrfs_set_stack_inode_size(dst, (u64)src->i_size_high << 32 |
- (u64)src->i_size);
- }
- if (!S_ISREG(src->i_mode) && !S_ISDIR(src->i_mode) &&
- !S_ISLNK(src->i_mode)) {
- if (src->i_block[0]) {
- btrfs_set_stack_inode_rdev(dst,
- old_decode_dev(src->i_block[0]));
- } else {
- btrfs_set_stack_inode_rdev(dst,
- new_decode_dev(src->i_block[1]));
- }
- }
- 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
- * inode item, creating file extents and creating directory entries.
- */
-static int ext2_copy_single_inode(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
- struct ext2_inode *ext2_inode,
- int datacsum, int packing, int noxattr)
-{
- int ret;
- struct btrfs_inode_item btrfs_inode;
-
- if (ext2_inode->i_links_count == 0)
- return 0;
-
- ext2_copy_inode_item(&btrfs_inode, ext2_inode, ext2_fs->blocksize);
- if (!datacsum && S_ISREG(ext2_inode->i_mode)) {
- u32 flags = btrfs_stack_inode_flags(&btrfs_inode) |
- BTRFS_INODE_NODATASUM;
- btrfs_set_stack_inode_flags(&btrfs_inode, flags);
- }
- ext2_convert_inode_flags(&btrfs_inode, ext2_inode);
-
- switch (ext2_inode->i_mode & S_IFMT) {
- case S_IFREG:
- ret = ext2_create_file_extents(trans, root, objectid,
- &btrfs_inode, ext2_fs, ext2_ino, datacsum, packing);
- break;
- case S_IFDIR:
- ret = ext2_create_dir_entries(trans, root, objectid,
- &btrfs_inode, ext2_fs, ext2_ino);
- break;
- case S_IFLNK:
- ret = ext2_create_symbol_link(trans, root, objectid,
- &btrfs_inode, ext2_fs, ext2_ino, ext2_inode);
- break;
- default:
- ret = 0;
- break;
- }
- if (ret)
- return ret;
-
- if (!noxattr) {
- ret = ext2_copy_extended_attrs(trans, root, objectid,
- &btrfs_inode, ext2_fs, ext2_ino);
- if (ret)
- return ret;
- }
- return btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
-}
-
-/*
- * scan ext2's inode bitmap and copy all used inodes.
- */
-static int ext2_copy_inodes(struct btrfs_convert_context *cctx,
- struct btrfs_root *root,
- int datacsum, int packing, int noxattr, struct task_ctx *p)
-{
- ext2_filsys ext2_fs = cctx->fs_data;
- int ret;
- errcode_t err;
- ext2_inode_scan ext2_scan;
- struct ext2_inode ext2_inode;
- ext2_ino_t ext2_ino;
- u64 objectid;
- struct btrfs_trans_handle *trans;
-
- trans = btrfs_start_transaction(root, 1);
- if (!trans)
- return -ENOMEM;
- err = ext2fs_open_inode_scan(ext2_fs, 0, &ext2_scan);
- if (err) {
- fprintf(stderr, "ext2fs_open_inode_scan: %s\n", error_message(err));
- return -1;
- }
- while (!(err = ext2fs_get_next_inode(ext2_scan, &ext2_ino,
- &ext2_inode))) {
- /* no more inodes */
- if (ext2_ino == 0)
- break;
- /* skip special inode in ext2fs */
- if (ext2_ino < EXT2_GOOD_OLD_FIRST_INO &&
- ext2_ino != EXT2_ROOT_INO)
- continue;
- objectid = ext2_ino + INO_OFFSET;
- ret = ext2_copy_single_inode(trans, root,
- objectid, ext2_fs, ext2_ino,
- &ext2_inode, datacsum, packing,
- noxattr);
- p->cur_copy_inodes++;
- if (ret)
- return ret;
- if (trans->blocks_used >= 4096) {
- ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
- trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
- }
- }
- if (err) {
- fprintf(stderr, "ext2fs_get_next_inode: %s\n", error_message(err));
- return -1;
- }
- ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
- ext2fs_close_inode_scan(ext2_scan);
-
- return ret;
-}
-
-static const struct btrfs_convert_operations ext2_convert_ops = {
- .name = "ext2",
- .open_fs = ext2_open_fs,
- .read_used_space = ext2_read_used_space,
- .copy_inodes = ext2_copy_inodes,
- .close_fs = ext2_close_fs,
- .check_state = ext2_check_state,
-};
-
-#endif
-
-static const struct btrfs_convert_operations *convert_operations[] = {
-#if BTRFSCONVERT_EXT2
- &ext2_convert_ops,
-#endif
-};
-
static int convert_open_fs(const char *devname,
struct btrfs_convert_context *cctx)
{
int i;
- memset(cctx, 0, sizeof(*cctx));
-
for (i = 0; i < ARRAY_SIZE(convert_operations); i++) {
int ret = convert_operations[i]->open_fs(cctx, devname);
@@ -2370,9 +1194,8 @@ static int convert_open_fs(const char *devname,
return -1;
}
-static int do_convert(const char *devname, int datacsum, int packing,
- int noxattr, u32 nodesize, int copylabel, const char *fslabel,
- int progress, u64 features)
+static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
+ const char *fslabel, int progress, u64 features)
{
int ret;
int fd = -1;
@@ -2382,7 +1205,7 @@ static int do_convert(const char *devname, int datacsum, int packing,
struct btrfs_root *image_root;
struct btrfs_convert_context cctx;
struct btrfs_key key;
- char *subvol_name = NULL;
+ char subvol_name[SOURCE_FS_NAME_LEN + 8];
struct task_ctx ctx;
char features_buf[64];
struct btrfs_mkfs_config mkfs_cfg;
@@ -2421,15 +1244,13 @@ static int do_convert(const char *devname, int datacsum, int packing,
printf("\tnodesize: %u\n", nodesize);
printf("\tfeatures: %s\n", features_buf);
+ memset(&mkfs_cfg, 0, sizeof(mkfs_cfg));
mkfs_cfg.label = cctx.volume_name;
mkfs_cfg.num_bytes = total_bytes;
mkfs_cfg.nodesize = nodesize;
mkfs_cfg.sectorsize = blocksize;
mkfs_cfg.stripesize = blocksize;
mkfs_cfg.features = features;
- /* New convert need these space */
- memset(mkfs_cfg.chunk_uuid, 0, BTRFS_UUID_UNPARSED_SIZE);
- memset(mkfs_cfg.fs_uuid, 0, BTRFS_UUID_UNPARSED_SIZE);
ret = make_convert_btrfs(fd, &mkfs_cfg, &cctx);
if (ret) {
@@ -2443,19 +1264,15 @@ static int do_convert(const char *devname, int datacsum, int packing,
error("unable to open ctree");
goto fail;
}
- ret = init_btrfs(&mkfs_cfg, root, &cctx, datacsum, packing, noxattr);
+ ret = init_btrfs(&mkfs_cfg, root, &cctx, convert_flags);
if (ret) {
error("unable to setup the root tree: %d", ret);
goto fail;
}
printf("creating %s image file\n", cctx.convert_ops->name);
- ret = asprintf(&subvol_name, "%s_saved", cctx.convert_ops->name);
- if (ret < 0) {
- error("memory allocation failure for subvolume name: %s_saved",
+ snprintf(subvol_name, sizeof(subvol_name), "%s_saved",
cctx.convert_ops->name);
- goto fail;
- }
key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
key.offset = (u64)-1;
key.type = BTRFS_ROOT_ITEM_KEY;
@@ -2465,13 +1282,19 @@ static int do_convert(const char *devname, int datacsum, int packing,
goto fail;
}
ret = create_image(image_root, &mkfs_cfg, &cctx, fd,
- mkfs_cfg.num_bytes, "image", datacsum);
+ mkfs_cfg.num_bytes, "image",
+ convert_flags);
if (ret) {
error("failed to create %s/image: %d", subvol_name, ret);
goto fail;
}
printf("creating btrfs metadata");
+ ret = pthread_mutex_init(&ctx.mutex, NULL);
+ if (ret) {
+ error("failed to initialize mutex: %d", ret);
+ goto fail;
+ }
ctx.max_copy_inodes = (cctx.inodes_count - cctx.free_inodes_count);
ctx.cur_copy_inodes = 0;
@@ -2480,7 +1303,7 @@ static int do_convert(const char *devname, int datacsum, int packing,
&ctx);
task_start(ctx.info);
}
- ret = copy_inodes(&cctx, root, datacsum, packing, noxattr, &ctx);
+ ret = copy_inodes(&cctx, root, convert_flags, &ctx);
if (ret) {
error("error during copy_inodes %d", ret);
goto fail;
@@ -2496,14 +1319,12 @@ static int do_convert(const char *devname, int datacsum, int packing,
goto fail;
}
- free(subvol_name);
-
memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE);
- if (copylabel == 1) {
+ if (convert_flags & CONVERT_FLAG_COPY_LABEL) {
__strncpy_null(root->fs_info->super_copy->label,
cctx.volume_name, BTRFS_LABEL_SIZE - 1);
printf("copy label '%s'\n", root->fs_info->super_copy->label);
- } else if (copylabel == -1) {
+ } else if (convert_flags & CONVERT_FLAG_SET_LABEL) {
strcpy(root->fs_info->super_copy->label, fslabel);
printf("set label to '%s'\n", fslabel);
}
@@ -2548,479 +1369,404 @@ fail:
}
/*
- * Check if a non 1:1 mapped chunk can be rolled back.
- * For new convert, it's OK while for old convert it's not.
+ * Read out data of convert image which is in btrfs reserved ranges so we can
+ * use them to overwrite the ranges during rollback.
*/
-static int may_rollback_chunk(struct btrfs_fs_info *fs_info, u64 bytenr)
+static int read_reserved_ranges(struct btrfs_root *root, u64 ino,
+ u64 total_bytes, char *reserved_ranges[])
{
- struct btrfs_block_group_cache *bg;
- struct btrfs_key key;
- struct btrfs_path path;
- struct btrfs_root *extent_root = fs_info->extent_root;
- u64 bg_start;
- u64 bg_end;
- int ret;
-
- bg = btrfs_lookup_first_block_group(fs_info, bytenr);
- if (!bg)
- return -ENOENT;
- bg_start = bg->key.objectid;
- bg_end = bg->key.objectid + bg->key.offset;
-
- key.objectid = bg_end;
- key.type = BTRFS_METADATA_ITEM_KEY;
- key.offset = 0;
- btrfs_init_path(&path);
-
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (ret < 0)
- return ret;
+ int i;
+ int ret = 0;
- while (1) {
- struct btrfs_extent_item *ei;
+ for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) {
+ const struct simple_range *range = &btrfs_reserved_ranges[i];
- ret = btrfs_previous_extent_item(extent_root, &path, bg_start);
- if (ret > 0) {
- ret = 0;
+ if (range->start + range->len >= total_bytes)
break;
- }
- if (ret < 0)
+ ret = btrfs_read_file(root, ino, range->start, range->len,
+ reserved_ranges[i]);
+ if (ret < range->len) {
+ error(
+ "failed to read data of convert image, offset=%llu len=%llu ret=%d",
+ range->start, range->len, ret);
+ if (ret >= 0)
+ ret = -EIO;
break;
+ }
+ ret = 0;
+ }
+ return ret;
+}
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- if (key.type == BTRFS_METADATA_ITEM_KEY)
- continue;
- /* Now it's EXTENT_ITEM_KEY only */
- ei = btrfs_item_ptr(path.nodes[0], path.slots[0],
- struct btrfs_extent_item);
- /*
- * Found data extent, means this is old convert must follow 1:1
- * mapping.
- */
- if (btrfs_extent_flags(path.nodes[0], ei)
- & BTRFS_EXTENT_FLAG_DATA) {
- ret = -EINVAL;
+static bool is_subset_of_reserved_ranges(u64 start, u64 len)
+{
+ int i;
+ bool ret = false;
+
+ for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) {
+ const struct simple_range *range = &btrfs_reserved_ranges[i];
+
+ if (start >= range->start && start + len <= range_end(range)) {
+ ret = true;
break;
}
}
- btrfs_release_path(&path);
return ret;
}
-static int may_rollback(struct btrfs_root *root)
+static bool is_chunk_direct_mapped(struct btrfs_fs_info *fs_info, u64 start)
{
- struct btrfs_fs_info *info = root->fs_info;
- struct btrfs_multi_bio *multi = NULL;
- u64 bytenr;
- u64 length;
- u64 physical;
- u64 total_bytes;
- int num_stripes;
+ struct cache_extent *ce;
+ struct map_lookup *map;
+ bool ret = false;
+
+ ce = search_cache_extent(&fs_info->mapping_tree.cache_tree, start);
+ if (!ce)
+ goto out;
+ if (ce->start > start || ce->start + ce->size < start)
+ goto out;
+
+ map = container_of(ce, struct map_lookup, ce);
+
+ /* Not SINGLE chunk */
+ if (map->num_stripes != 1)
+ goto out;
+
+ /* Chunk's logical doesn't match with phisical, not 1:1 mapped */
+ if (map->ce.start != map->stripes[0].physical)
+ goto out;
+ ret = true;
+out:
+ return ret;
+}
+
+/*
+ * Iterate all file extents of the convert image.
+ *
+ * All file extents except ones in btrfs_reserved_ranges must be mapped 1:1
+ * on disk. (Means thier file_offset must match their on disk bytenr)
+ *
+ * File extents in reserved ranges can be relocated to other place, and in
+ * that case we will read them out for later use.
+ */
+static int check_convert_image(struct btrfs_root *image_root, u64 ino,
+ u64 total_size, char *reserved_ranges[])
+{
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct btrfs_fs_info *fs_info = image_root->fs_info;
+ u64 checked_bytes = 0;
int ret;
- if (btrfs_super_num_devices(info->super_copy) != 1)
- goto fail;
+ key.objectid = ino;
+ key.offset = 0;
+ key.type = BTRFS_EXTENT_DATA_KEY;
- bytenr = BTRFS_SUPER_INFO_OFFSET;
- total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy);
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0);
+ /*
+ * It's possible that some fs doesn't store any (including sb)
+ * data into 0~1M range, and NO_HOLES is enabled.
+ *
+ * So we only need to check if ret < 0
+ */
+ if (ret < 0) {
+ error("failed to iterate file extents at offset 0: %s",
+ strerror(-ret));
+ btrfs_release_path(&path);
+ return ret;
+ }
+ /* Loop from the first file extents */
while (1) {
- ret = btrfs_map_block(&info->mapping_tree, WRITE, bytenr,
- &length, &multi, 0, NULL);
- if (ret) {
- if (ret == -ENOENT) {
- /* removed block group at the tail */
- if (length == (u64)-1)
- break;
+ struct btrfs_file_extent_item *fi;
+ struct extent_buffer *leaf = path.nodes[0];
+ u64 disk_bytenr;
+ u64 file_offset;
+ u64 ram_bytes;
+ int slot = path.slots[0];
- /* removed block group in the middle */
- goto next;
- }
- goto fail;
+ if (slot >= btrfs_header_nritems(leaf))
+ goto next;
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+
+ /*
+ * Iteration is done, exit normally, we have extra check out of
+ * the loop
+ */
+ if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY) {
+ ret = 0;
+ break;
+ }
+ file_offset = key.offset;
+ fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+ if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) {
+ ret = -EINVAL;
+ error(
+ "ino %llu offset %llu doesn't have a regular file extent",
+ ino, file_offset);
+ break;
+ }
+ if (btrfs_file_extent_compression(leaf, fi) ||
+ btrfs_file_extent_encryption(leaf, fi) ||
+ btrfs_file_extent_other_encoding(leaf, fi)) {
+ ret = -EINVAL;
+ error(
+ "ino %llu offset %llu doesn't have a plain file extent",
+ ino, file_offset);
+ break;
}
- num_stripes = multi->num_stripes;
- physical = multi->stripes[0].physical;
- free(multi);
+ disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
+ ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi);
- if (num_stripes != 1) {
- error("num stripes for bytenr %llu is not 1", bytenr);
- goto fail;
- }
+ checked_bytes += ram_bytes;
+ /* Skip hole */
+ if (disk_bytenr == 0)
+ goto next;
/*
- * Extra check for new convert, as metadata chunk from new
- * convert is much more free than old convert, it doesn't need
- * to do 1:1 mapping.
+ * Most file extents must be 1:1 mapped, which means 2 things:
+ * 1) File extent file offset == disk_bytenr
+ * 2) That data chunk's logical == chunk's physical
+ *
+ * So file extent's file offset == physical position on disk.
+ *
+ * And after rolling back btrfs reserved range, other part
+ * remains what old fs used to be.
*/
- if (physical != bytenr) {
+ if (file_offset != disk_bytenr ||
+ !is_chunk_direct_mapped(fs_info, disk_bytenr)) {
/*
- * Check if it's a metadata chunk and has only metadata
- * extent.
+ * Only file extent in btrfs reserved ranges are
+ * allowed to be non-1:1 mapped
*/
- ret = may_rollback_chunk(info, bytenr);
- if (ret < 0)
- goto fail;
+ if (!is_subset_of_reserved_ranges(file_offset,
+ ram_bytes)) {
+ ret = -EINVAL;
+ error(
+ "ino %llu offset %llu file extent should not be relocated",
+ ino, file_offset);
+ break;
+ }
}
next:
- bytenr += length;
- if (bytenr >= total_bytes)
+ ret = btrfs_next_item(image_root, &path);
+ if (ret) {
+ if (ret > 0)
+ ret = 0;
break;
+ }
}
- return 0;
-fail:
- return -1;
+ btrfs_release_path(&path);
+ /*
+ * For HOLES mode (without NO_HOLES), we must ensure file extents
+ * cover the whole range of the image
+ */
+ if (!ret && !btrfs_fs_incompat(fs_info, NO_HOLES)) {
+ if (checked_bytes != total_size) {
+ ret = -EINVAL;
+ error("inode %llu has some file extents not checked",
+ ino);
+ return ret;
+ }
+ }
+
+ /* So far so good, read old data located in btrfs reserved ranges */
+ ret = read_reserved_ranges(image_root, ino, total_size,
+ reserved_ranges);
+ return ret;
}
+/*
+ * btrfs rollback is just reverted convert:
+ * |<---------------Btrfs fs------------------------------>|
+ * |<- Old data chunk ->|< new chunk (D/M/S)>|<- ODC ->|
+ * |<-Old-FE->| |<-Old-FE->|<- Btrfs extents ->|<-Old-FE->|
+ * ||
+ * \/
+ * |<------------------Old fs----------------------------->|
+ * |<- used ->| |<- used ->| |<- used ->|
+ *
+ * However things are much easier than convert, we don't really need to
+ * do the complex space calculation, but only to handle btrfs reserved space
+ *
+ * |<---------------------------Btrfs fs----------------------------->|
+ * | RSV 1 | | Old | | RSV 2 | | Old | | RSV 3 |
+ * | 0~1M | | Fs | | SB2 + 64K | | Fs | | SB3 + 64K |
+ *
+ * On the other hande, the converted fs image in btrfs is a completely
+ * valid old fs.
+ *
+ * |<-----------------Converted fs image in btrfs-------------------->|
+ * | RSV 1 | | Old | | RSV 2 | | Old | | RSV 3 |
+ * | Relocated | | Fs | | Relocated | | Fs | | Relocated |
+ *
+ * Used space in fs image should be at the same physical position on disk.
+ * We only need to recover the data in reserved ranges, so the whole
+ * old fs is back.
+ *
+ * The idea to rollback is also straightforward, we just "read" out the data
+ * of reserved ranges, and write them back to there they should be.
+ * Then the old fs is back.
+ */
static int do_rollback(const char *devname)
{
- int fd = -1;
- int ret;
- int i;
struct btrfs_root *root;
struct btrfs_root *image_root;
- struct btrfs_root *chunk_root;
- struct btrfs_dir_item *dir;
- struct btrfs_inode_item *inode;
- struct btrfs_file_extent_item *fi;
- struct btrfs_trans_handle *trans;
- struct extent_buffer *leaf;
- struct btrfs_block_group_cache *cache1;
- struct btrfs_block_group_cache *cache2;
+ struct btrfs_fs_info *fs_info;
struct btrfs_key key;
struct btrfs_path path;
- struct extent_io_tree io_tree;
- char *buf = NULL;
- char *name;
- u64 bytenr;
- u64 num_bytes;
- u64 root_dir;
- u64 objectid;
- u64 offset;
- u64 start;
- u64 end;
- u64 sb_bytenr;
- u64 first_free;
+ struct btrfs_dir_item *dir;
+ struct btrfs_inode_item *inode_item;
+ char *image_name = "image";
+ char *reserved_ranges[ARRAY_SIZE(btrfs_reserved_ranges)] = { NULL };
u64 total_bytes;
- u32 sectorsize;
+ u64 fsize;
+ u64 root_dir;
+ u64 ino;
+ int fd = -1;
+ int ret;
+ int i;
- extent_io_tree_init(&io_tree);
+ for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) {
+ const struct simple_range *range = &btrfs_reserved_ranges[i];
+ reserved_ranges[i] = calloc(1, range->len);
+ if (!reserved_ranges[i]) {
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+ }
fd = open(devname, O_RDWR);
if (fd < 0) {
error("unable to open %s: %s", devname, strerror(errno));
- goto fail;
+ ret = -EIO;
+ goto free_mem;
}
+ fsize = lseek(fd, 0, SEEK_END);
root = open_ctree_fd(fd, devname, 0, OPEN_CTREE_WRITES);
if (!root) {
error("unable to open ctree");
- goto fail;
+ ret = -EIO;
+ goto free_mem;
}
- ret = may_rollback(root);
- if (ret < 0) {
- error("unable to do rollback: %d", ret);
- goto fail;
- }
-
- sectorsize = root->sectorsize;
- buf = malloc(sectorsize);
- if (!buf) {
- error("unable to allocate memory");
- goto fail;
- }
-
- btrfs_init_path(&path);
+ fs_info = root->fs_info;
+ /*
+ * Search root backref first, or after subvolume deletion (orphan),
+ * we can still rollback the image.
+ */
key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
key.type = BTRFS_ROOT_BACKREF_KEY;
key.offset = BTRFS_FS_TREE_OBJECTID;
- ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key, &path, 0,
- 0);
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
btrfs_release_path(&path);
if (ret > 0) {
- error("unable to convert ext2 image subvolume, is it deleted?");
- goto fail;
+ error("unable to find ext2 image subvolume, is it deleted?");
+ ret = -ENOENT;
+ goto close_fs;
} else if (ret < 0) {
- error("unable to open ext2_saved, id %llu: %s",
- (unsigned long long)key.objectid, strerror(-ret));
- goto fail;
+ error("failed to find ext2 image subvolume: %s",
+ strerror(-ret));
+ goto close_fs;
}
+ /* Search convert subvolume */
key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1;
- image_root = btrfs_read_fs_root(root->fs_info, &key);
- if (!image_root || IS_ERR(image_root)) {
- error("unable to open subvolume %llu: %ld",
- (unsigned long long)key.objectid, PTR_ERR(image_root));
- goto fail;
+ image_root = btrfs_read_fs_root(fs_info, &key);
+ if (IS_ERR(image_root)) {
+ ret = PTR_ERR(image_root);
+ error("failed to open convert image subvolume: %s",
+ strerror(-ret));
+ goto close_fs;
}
- name = "image";
- root_dir = btrfs_root_dirid(&root->root_item);
- dir = btrfs_lookup_dir_item(NULL, image_root, &path,
- root_dir, name, strlen(name), 0);
+ /* Search the image file */
+ root_dir = btrfs_root_dirid(&image_root->root_item);
+ dir = btrfs_lookup_dir_item(NULL, image_root, &path, root_dir,
+ image_name, strlen(image_name), 0);
+
if (!dir || IS_ERR(dir)) {
- error("unable to find file %s: %ld", name, PTR_ERR(dir));
- goto fail;
+ btrfs_release_path(&path);
+ if (dir)
+ ret = PTR_ERR(dir);
+ else
+ ret = -ENOENT;
+ error("failed to locate file %s: %s", image_name,
+ strerror(-ret));
+ goto close_fs;
}
- leaf = path.nodes[0];
- btrfs_dir_item_key_to_cpu(leaf, dir, &key);
+ btrfs_dir_item_key_to_cpu(path.nodes[0], dir, &key);
btrfs_release_path(&path);
- objectid = key.objectid;
+ /* Get total size of the original image */
+ ino = key.objectid;
ret = btrfs_lookup_inode(NULL, image_root, &path, &key, 0);
- if (ret) {
- error("unable to find inode item: %d", ret);
- goto fail;
- }
- leaf = path.nodes[0];
- inode = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item);
- total_bytes = btrfs_inode_size(leaf, inode);
- btrfs_release_path(&path);
- key.objectid = objectid;
- key.offset = 0;
- 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);
+ if (ret < 0) {
btrfs_release_path(&path);
- goto fail;
- }
-
- /* build mapping tree for the relocated blocks */
- for (offset = 0; offset < total_bytes; ) {
- leaf = path.nodes[0];
- if (path.slots[0] >= btrfs_header_nritems(leaf)) {
- ret = btrfs_next_leaf(root, &path);
- if (ret != 0)
- break;
- continue;
- }
-
- btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
- if (key.objectid != objectid || key.offset != offset ||
- key.type != BTRFS_EXTENT_DATA_KEY)
- break;
-
- fi = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_file_extent_item);
- if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG)
- break;
- if (btrfs_file_extent_compression(leaf, fi) ||
- btrfs_file_extent_encryption(leaf, fi) ||
- btrfs_file_extent_other_encoding(leaf, fi))
- break;
-
- bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
- /* skip holes and direct mapped extents */
- if (bytenr == 0 || bytenr == offset)
- goto next_extent;
-
- bytenr += btrfs_file_extent_offset(leaf, fi);
- num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
-
- cache1 = btrfs_lookup_block_group(root->fs_info, offset);
- cache2 = btrfs_lookup_block_group(root->fs_info,
- offset + num_bytes - 1);
- /*
- * Here we must take consideration of old and new convert
- * behavior.
- * For old convert case, sign, there is no consist chunk type
- * that will cover the extent. META/DATA/SYS are all possible.
- * Just ensure relocate one is in SYS chunk.
- * For new convert case, they are all covered by DATA chunk.
- *
- * So, there is not valid chunk type check for it now.
- */
- if (cache1 != cache2)
- break;
-
- set_extent_bits(&io_tree, offset, offset + num_bytes - 1,
- EXTENT_LOCKED, GFP_NOFS);
- set_state_private(&io_tree, offset, bytenr);
-next_extent:
- offset += btrfs_file_extent_num_bytes(leaf, fi);
- path.slots[0]++;
+ error("unable to find inode %llu: %s", ino, strerror(-ret));
+ goto close_fs;
}
+ inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_inode_item);
+ total_bytes = btrfs_inode_size(path.nodes[0], inode_item);
btrfs_release_path(&path);
- if (offset < total_bytes) {
- error("unable to build extent mapping (offset %llu, total_bytes %llu)",
- (unsigned long long)offset,
- (unsigned long long)total_bytes);
- error("converted filesystem after balance is unable to rollback");
- goto fail;
+ /* Check if we can rollback the image */
+ ret = check_convert_image(image_root, ino, total_bytes, reserved_ranges);
+ if (ret < 0) {
+ error("old fs image can't be rolled back");
+ goto close_fs;
}
+close_fs:
+ btrfs_release_path(&path);
+ close_ctree_fs_info(fs_info);
+ if (ret)
+ goto free_mem;
- first_free = BTRFS_SUPER_INFO_OFFSET + 2 * sectorsize - 1;
- first_free &= ~((u64)sectorsize - 1);
- /* backup for extent #0 should exist */
- if(!test_range_bit(&io_tree, 0, first_free - 1, EXTENT_LOCKED, 1)) {
- error("no backup for the first extent");
- goto fail;
- }
- /* force no allocation from system block group */
- root->fs_info->system_allocs = -1;
- trans = btrfs_start_transaction(root, 1);
- 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
+ * Everything is OK, just write back old fs data into btrfs reserved
+ * ranges
+ *
+ * Here, we starts from the backup blocks first, so if something goes
+ * wrong, the fs is still mountable
*/
- chunk_root = root->fs_info->chunk_root;
- memset(&key, 0, sizeof(key));
- while (1) {
- ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1);
- if (ret < 0)
- break;
- ret = btrfs_next_leaf(chunk_root, &path);
- if (ret)
- break;
+ for (i = ARRAY_SIZE(btrfs_reserved_ranges) - 1; i >= 0; i--) {
+ u64 real_size;
+ const struct simple_range *range = &btrfs_reserved_ranges[i];
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- btrfs_release_path(&path);
- }
- btrfs_release_path(&path);
-
- offset = 0;
- num_bytes = 0;
- while(1) {
- cache1 = btrfs_lookup_block_group(root->fs_info, offset);
- if (!cache1)
- break;
-
- if (cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM)
- num_bytes += btrfs_block_group_used(&cache1->item);
-
- offset = cache1->key.objectid + cache1->key.offset;
- }
- /* only extent #0 left in system block group? */
- if (num_bytes > first_free) {
- error(
- "unable to empty system block group (num_bytes %llu, first_free %llu",
- (unsigned long long)num_bytes,
- (unsigned long long)first_free);
- goto fail;
- }
- /* create a system chunk that maps the whole device */
- ret = prepare_system_chunk_sb(root->fs_info->super_copy);
- if (ret) {
- error("unable to update system chunk: %d", ret);
- goto fail;
- }
-
- ret = btrfs_commit_transaction(trans, root);
- if (ret) {
- error("transaction commit failed: %d", ret);
- goto fail;
- }
-
- ret = close_ctree(root);
- if (ret) {
- error("close_ctree failed: %d", ret);
- goto fail;
- }
-
- /* zero btrfs super block mirrors */
- memset(buf, 0, sectorsize);
- for (i = 1 ; i < BTRFS_SUPER_MIRROR_MAX; i++) {
- bytenr = btrfs_sb_offset(i);
- if (bytenr >= total_bytes)
- break;
- ret = pwrite(fd, buf, sectorsize, bytenr);
- if (ret != sectorsize) {
- error("zeroing superblock mirror %d failed: %d",
- i, ret);
- goto fail;
- }
- }
-
- sb_bytenr = (u64)-1;
- /* copy all relocated blocks back */
- while(1) {
- ret = find_first_extent_bit(&io_tree, 0, &start, &end,
- EXTENT_LOCKED);
- if (ret)
- break;
-
- ret = get_state_private(&io_tree, start, &bytenr);
- BUG_ON(ret);
-
- clear_extent_bits(&io_tree, start, end, EXTENT_LOCKED,
- GFP_NOFS);
+ if (range_end(range) >= fsize)
+ continue;
- while (start <= end) {
- if (start == BTRFS_SUPER_INFO_OFFSET) {
- sb_bytenr = bytenr;
- goto next_sector;
- }
- ret = pread(fd, buf, sectorsize, bytenr);
- if (ret < 0) {
- error("reading superblock at %llu failed: %d",
- (unsigned long long)bytenr, ret);
- goto fail;
- }
- BUG_ON(ret != sectorsize);
- ret = pwrite(fd, buf, sectorsize, start);
- if (ret < 0) {
- error("writing superblock at %llu failed: %d",
- (unsigned long long)start, ret);
- goto fail;
- }
- BUG_ON(ret != sectorsize);
-next_sector:
- start += sectorsize;
- bytenr += sectorsize;
+ real_size = min(range_end(range), fsize) - range->start;
+ ret = pwrite(fd, reserved_ranges[i], real_size, range->start);
+ if (ret < real_size) {
+ if (ret < 0)
+ ret = -errno;
+ else
+ ret = -EIO;
+ error("failed to recover range [%llu, %llu): %s",
+ range->start, real_size, strerror(-ret));
+ goto free_mem;
}
+ ret = 0;
}
- ret = fsync(fd);
- if (ret < 0) {
- error("fsync failed: %s", strerror(errno));
- goto fail;
- }
- /*
- * finally, overwrite btrfs super block.
- */
- ret = pread(fd, buf, sectorsize, sb_bytenr);
- if (ret < 0) {
- error("reading primary superblock failed: %s",
- strerror(errno));
- goto fail;
- }
- BUG_ON(ret != sectorsize);
- ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET);
- if (ret < 0) {
- error("writing primary superblock failed: %s",
- strerror(errno));
- goto fail;
- }
- BUG_ON(ret != sectorsize);
- ret = fsync(fd);
- if (ret < 0) {
- error("fsync failed: %s", strerror(errno));
- goto fail;
- }
-
- close(fd);
- free(buf);
- extent_io_tree_cleanup(&io_tree);
- printf("rollback complete\n");
- return 0;
-
-fail:
- if (fd != -1)
- close(fd);
- free(buf);
- error("rollback aborted");
- return -1;
+free_mem:
+ for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++)
+ free(reserved_ranges[i]);
+ if (ret)
+ error("rollback failed");
+ else
+ printf("rollback succeeded\n");
+ return ret;
}
static void print_usage(void)
@@ -3096,7 +1842,7 @@ int main(int argc, char *argv[])
rollback = 1;
break;
case 'l':
- copylabel = -1;
+ copylabel = CONVERT_FLAG_SET_LABEL;
if (strlen(optarg) >= BTRFS_LABEL_SIZE) {
warning(
"label too long, trimmed to %d bytes",
@@ -3105,7 +1851,7 @@ int main(int argc, char *argv[])
__strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE - 1);
break;
case 'L':
- copylabel = 1;
+ copylabel = CONVERT_FLAG_COPY_LABEL;
break;
case 'p':
progress = 1;
@@ -3178,8 +1924,13 @@ int main(int argc, char *argv[])
if (rollback) {
ret = do_rollback(file);
} else {
- ret = do_convert(file, datacsum, packing, noxattr, nodesize,
- copylabel, fslabel, progress, features);
+ u32 cf = 0;
+
+ cf |= datacsum ? CONVERT_FLAG_DATACSUM : 0;
+ cf |= packing ? CONVERT_FLAG_INLINE_DATA : 0;
+ cf |= noxattr ? 0 : CONVERT_FLAG_XATTR;
+ cf |= copylabel;
+ ret = do_convert(file, cf, nodesize, fslabel, progress, features);
}
if (ret)
return 1;
diff --git a/convert/source-ext2.c b/convert/source-ext2.c
new file mode 100644
index 00000000..24744e22
--- /dev/null
+++ b/convert/source-ext2.c
@@ -0,0 +1,886 @@
+/*
+ * 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.
+ */
+
+#if BTRFSCONVERT_EXT2
+
+#include "kerncompat.h"
+#include <linux/limits.h>
+#include <pthread.h>
+#include "disk-io.h"
+#include "transaction.h"
+#include "utils.h"
+#include "convert/common.h"
+#include "convert/source-ext2.h"
+
+/*
+ * Open Ext2fs in readonly mode, read block allocation bitmap and
+ * inode bitmap into memory.
+ */
+static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
+{
+ errcode_t ret;
+ ext2_filsys ext2_fs;
+ ext2_ino_t ino;
+ u32 ro_feature;
+ int open_flag = EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS;
+
+ ret = ext2fs_open(name, open_flag, 0, 0, unix_io_manager, &ext2_fs);
+ if (ret) {
+ fprintf(stderr, "ext2fs_open: %s\n", error_message(ret));
+ return -1;
+ }
+ /*
+ * We need to know exactly the used space, some RO compat flags like
+ * BIGALLOC will affect how used space is present.
+ * So we need manuall check any unsupported RO compat flags
+ */
+ ro_feature = ext2_fs->super->s_feature_ro_compat;
+ if (ro_feature & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
+ error(
+"unsupported RO features detected: %x, abort convert to avoid possible corruption",
+ ro_feature & ~EXT2_LIB_FEATURE_COMPAT_SUPP);
+ goto fail;
+ }
+ ret = ext2fs_read_inode_bitmap(ext2_fs);
+ if (ret) {
+ fprintf(stderr, "ext2fs_read_inode_bitmap: %s\n",
+ error_message(ret));
+ goto fail;
+ }
+ ret = ext2fs_read_block_bitmap(ext2_fs);
+ if (ret) {
+ fprintf(stderr, "ext2fs_read_block_bitmap: %s\n",
+ error_message(ret));
+ goto fail;
+ }
+ /*
+ * search each block group for a free inode. this set up
+ * uninit block/inode bitmaps appropriately.
+ */
+ ino = 1;
+ while (ino <= ext2_fs->super->s_inodes_count) {
+ ext2_ino_t foo;
+ ext2fs_new_inode(ext2_fs, ino, 0, NULL, &foo);
+ ino += EXT2_INODES_PER_GROUP(ext2_fs->super);
+ }
+
+ if (!(ext2_fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+ error("filetype feature is missing");
+ goto fail;
+ }
+
+ cctx->fs_data = ext2_fs;
+ cctx->blocksize = ext2_fs->blocksize;
+ cctx->block_count = ext2_fs->super->s_blocks_count;
+ cctx->total_bytes = ext2_fs->blocksize * ext2_fs->super->s_blocks_count;
+ cctx->volume_name = strndup(ext2_fs->super->s_volume_name, 16);
+ cctx->first_data_block = ext2_fs->super->s_first_data_block;
+ cctx->inodes_count = ext2_fs->super->s_inodes_count;
+ cctx->free_inodes_count = ext2_fs->super->s_free_inodes_count;
+ return 0;
+fail:
+ ext2fs_close(ext2_fs);
+ return -1;
+}
+
+static int __ext2_add_one_block(ext2_filsys fs, char *bitmap,
+ unsigned long group_nr, struct cache_tree *used)
+{
+ unsigned long offset;
+ unsigned i;
+ int ret = 0;
+
+ offset = fs->super->s_first_data_block;
+ 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;
+
+ start = (i + offset) * EXT2FS_CLUSTER_RATIO(fs);
+ start *= fs->blocksize;
+ ret = add_merge_cache_extent(used, start,
+ fs->blocksize);
+ if (ret < 0)
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Read all used ext2 space into cctx->used cache tree
+ */
+static int ext2_read_used_space(struct btrfs_convert_context *cctx)
+{
+ ext2_filsys fs = (ext2_filsys)cctx->fs_data;
+ blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
+ struct cache_tree *used_tree = &cctx->used_space;
+ char *block_bitmap = NULL;
+ unsigned long i;
+ int block_nbytes;
+ int ret = 0;
+
+ block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
+ if (!block_nbytes) {
+ error("EXT2_CLUSTERS_PER_GROUP too small: %llu",
+ (unsigned long long)(EXT2_CLUSTERS_PER_GROUP(fs->super)));
+ return -EINVAL;
+ }
+
+ block_bitmap = malloc(block_nbytes);
+ if (!block_bitmap)
+ return -ENOMEM;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ ret = ext2fs_get_block_bitmap_range2(fs->block_map, blk_itr,
+ block_nbytes * 8, block_bitmap);
+ if (ret) {
+ error("fail to get bitmap from ext2, %s",
+ strerror(-ret));
+ break;
+ }
+ ret = __ext2_add_one_block(fs, block_bitmap, i, used_tree);
+ if (ret < 0) {
+ error("fail to build used space tree, %s",
+ strerror(-ret));
+ break;
+ }
+ blk_itr += EXT2_CLUSTERS_PER_GROUP(fs->super);
+ }
+
+ free(block_bitmap);
+ return ret;
+}
+
+static void ext2_close_fs(struct btrfs_convert_context *cctx)
+{
+ if (cctx->volume_name) {
+ free(cctx->volume_name);
+ cctx->volume_name = NULL;
+ }
+ ext2fs_close(cctx->fs_data);
+}
+
+static u8 ext2_filetype_conversion_table[EXT2_FT_MAX] = {
+ [EXT2_FT_UNKNOWN] = BTRFS_FT_UNKNOWN,
+ [EXT2_FT_REG_FILE] = BTRFS_FT_REG_FILE,
+ [EXT2_FT_DIR] = BTRFS_FT_DIR,
+ [EXT2_FT_CHRDEV] = BTRFS_FT_CHRDEV,
+ [EXT2_FT_BLKDEV] = BTRFS_FT_BLKDEV,
+ [EXT2_FT_FIFO] = BTRFS_FT_FIFO,
+ [EXT2_FT_SOCK] = BTRFS_FT_SOCK,
+ [EXT2_FT_SYMLINK] = BTRFS_FT_SYMLINK,
+};
+
+static int ext2_dir_iterate_proc(ext2_ino_t dir, int entry,
+ struct ext2_dir_entry *dirent,
+ int offset, int blocksize,
+ char *buf,void *priv_data)
+{
+ int ret;
+ int file_type;
+ u64 objectid;
+ char dotdot[] = "..";
+ struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data;
+ int name_len;
+
+ name_len = dirent->name_len & 0xFF;
+
+ objectid = dirent->inode + INO_OFFSET;
+ if (!strncmp(dirent->name, dotdot, name_len)) {
+ if (name_len == 2) {
+ BUG_ON(idata->parent != 0);
+ idata->parent = objectid;
+ }
+ return 0;
+ }
+ if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO)
+ return 0;
+
+ file_type = dirent->name_len >> 8;
+ BUG_ON(file_type > EXT2_FT_SYMLINK);
+
+ ret = convert_insert_dirent(idata->trans, idata->root, dirent->name,
+ name_len, idata->objectid, objectid,
+ ext2_filetype_conversion_table[file_type],
+ idata->index_cnt, idata->inode);
+ if (ret < 0) {
+ idata->errcode = ret;
+ return BLOCK_ABORT;
+ }
+
+ idata->index_cnt++;
+ return 0;
+}
+
+static int ext2_create_dir_entries(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct btrfs_inode_item *btrfs_inode,
+ ext2_filsys ext2_fs, ext2_ino_t ext2_ino)
+{
+ int ret;
+ errcode_t err;
+ struct dir_iterate_data data = {
+ .trans = trans,
+ .root = root,
+ .inode = btrfs_inode,
+ .objectid = objectid,
+ .index_cnt = 2,
+ .parent = 0,
+ .errcode = 0,
+ };
+
+ err = ext2fs_dir_iterate2(ext2_fs, ext2_ino, 0, NULL,
+ ext2_dir_iterate_proc, &data);
+ if (err)
+ goto error;
+ ret = data.errcode;
+ if (ret == 0 && data.parent == objectid) {
+ ret = btrfs_insert_inode_ref(trans, root, "..", 2,
+ objectid, objectid, 0);
+ }
+ return ret;
+error:
+ fprintf(stderr, "ext2fs_dir_iterate2: %s\n", error_message(err));
+ return -1;
+}
+
+static int ext2_block_iterate_proc(ext2_filsys fs, blk_t *blocknr,
+ e2_blkcnt_t blockcnt, blk_t ref_block,
+ int ref_offset, void *priv_data)
+{
+ int ret;
+ struct blk_iterate_data *idata;
+ idata = (struct blk_iterate_data *)priv_data;
+ ret = block_iterate_proc(*blocknr, blockcnt, idata);
+ if (ret) {
+ idata->errcode = ret;
+ return BLOCK_ABORT;
+ }
+ return 0;
+}
+
+/*
+ * traverse file's data blocks, record these data blocks as file extents.
+ */
+static int ext2_create_file_extents(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct btrfs_inode_item *btrfs_inode,
+ ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
+ u32 convert_flags)
+{
+ int ret;
+ char *buffer = NULL;
+ errcode_t err;
+ u32 last_block;
+ u32 sectorsize = root->fs_info->sectorsize;
+ u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
+ struct blk_iterate_data data;
+
+ init_blk_iterate_data(&data, trans, root, btrfs_inode, objectid,
+ convert_flags & CONVERT_FLAG_DATACSUM);
+
+ err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY,
+ NULL, ext2_block_iterate_proc, &data);
+ if (err)
+ goto error;
+ ret = data.errcode;
+ if (ret)
+ goto fail;
+ if ((convert_flags & CONVERT_FLAG_INLINE_DATA) && data.first_block == 0
+ && data.num_blocks > 0
+ && inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
+ u64 num_bytes = data.num_blocks * sectorsize;
+ u64 disk_bytenr = data.disk_block * sectorsize;
+ u64 nbytes;
+
+ buffer = malloc(num_bytes);
+ if (!buffer)
+ return -ENOMEM;
+ ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer);
+ if (ret)
+ goto fail;
+ if (num_bytes > inode_size)
+ num_bytes = inode_size;
+ ret = btrfs_insert_inline_extent(trans, root, objectid,
+ 0, buffer, num_bytes);
+ if (ret)
+ goto fail;
+ nbytes = btrfs_stack_inode_nbytes(btrfs_inode) + num_bytes;
+ btrfs_set_stack_inode_nbytes(btrfs_inode, nbytes);
+ } else if (data.num_blocks > 0) {
+ ret = record_file_blocks(&data, data.first_block,
+ data.disk_block, data.num_blocks);
+ if (ret)
+ goto fail;
+ }
+ data.first_block += data.num_blocks;
+ last_block = (inode_size + sectorsize - 1) / sectorsize;
+ if (last_block > data.first_block) {
+ ret = record_file_blocks(&data, data.first_block, 0,
+ last_block - data.first_block);
+ }
+fail:
+ free(buffer);
+ return ret;
+error:
+ fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err));
+ return -1;
+}
+
+static int ext2_create_symlink(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct btrfs_inode_item *btrfs_inode,
+ ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
+ struct ext2_inode *ext2_inode)
+{
+ int ret;
+ char *pathname;
+ u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
+ if (ext2fs_inode_data_blocks2(ext2_fs, ext2_inode)) {
+ btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1);
+ ret = ext2_create_file_extents(trans, root, objectid,
+ btrfs_inode, ext2_fs, ext2_ino,
+ CONVERT_FLAG_DATACSUM |
+ CONVERT_FLAG_INLINE_DATA);
+ btrfs_set_stack_inode_size(btrfs_inode, inode_size);
+ return ret;
+ }
+
+ pathname = (char *)&(ext2_inode->i_block[0]);
+ BUG_ON(pathname[inode_size] != 0);
+ ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
+ pathname, inode_size + 1);
+ btrfs_set_stack_inode_nbytes(btrfs_inode, inode_size + 1);
+ return ret;
+}
+
+/*
+ * Following xattr/acl related codes are based on codes in
+ * fs/ext3/xattr.c and fs/ext3/acl.c
+ */
+#define EXT2_XATTR_BHDR(ptr) ((struct ext2_ext_attr_header *)(ptr))
+#define EXT2_XATTR_BFIRST(ptr) \
+ ((struct ext2_ext_attr_entry *)(EXT2_XATTR_BHDR(ptr) + 1))
+#define EXT2_XATTR_IHDR(inode) \
+ ((struct ext2_ext_attr_header *) ((void *)(inode) + \
+ EXT2_GOOD_OLD_INODE_SIZE + (inode)->i_extra_isize))
+#define EXT2_XATTR_IFIRST(inode) \
+ ((struct ext2_ext_attr_entry *) ((void *)EXT2_XATTR_IHDR(inode) + \
+ sizeof(EXT2_XATTR_IHDR(inode)->h_magic)))
+
+static int ext2_xattr_check_names(struct ext2_ext_attr_entry *entry,
+ const void *end)
+{
+ struct ext2_ext_attr_entry *next;
+
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ next = EXT2_EXT_ATTR_NEXT(entry);
+ if ((void *)next >= end)
+ return -EIO;
+ entry = next;
+ }
+ return 0;
+}
+
+static int ext2_xattr_check_block(const char *buf, size_t size)
+{
+ int error;
+ struct ext2_ext_attr_header *header = EXT2_XATTR_BHDR(buf);
+
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC ||
+ header->h_blocks != 1)
+ return -EIO;
+ error = ext2_xattr_check_names(EXT2_XATTR_BFIRST(buf), buf + size);
+ return error;
+}
+
+static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry,
+ size_t size)
+{
+ size_t value_size = entry->e_value_size;
+
+ if (entry->e_value_block != 0 || value_size > size ||
+ entry->e_value_offs + value_size > size)
+ return -EIO;
+ return 0;
+}
+
+static inline int ext2_acl_count(size_t size)
+{
+ ssize_t s;
+ size -= sizeof(ext2_acl_header);
+ s = size - 4 * sizeof(ext2_acl_entry_short);
+ if (s < 0) {
+ if (size % sizeof(ext2_acl_entry_short))
+ return -1;
+ return size / sizeof(ext2_acl_entry_short);
+ } else {
+ if (s % sizeof(ext2_acl_entry))
+ return -1;
+ return s / sizeof(ext2_acl_entry) + 4;
+ }
+}
+
+static inline size_t acl_ea_size(int count)
+{
+ return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry);
+}
+
+static int ext2_acl_to_xattr(void *dst, const void *src,
+ size_t dst_size, size_t src_size)
+{
+ int i, count;
+ const void *end = src + src_size;
+ acl_ea_header *ext_acl = (acl_ea_header *)dst;
+ acl_ea_entry *dst_entry = ext_acl->a_entries;
+ ext2_acl_entry *src_entry;
+
+ if (src_size < sizeof(ext2_acl_header))
+ goto fail;
+ if (((ext2_acl_header *)src)->a_version !=
+ cpu_to_le32(EXT2_ACL_VERSION))
+ goto fail;
+ src += sizeof(ext2_acl_header);
+ count = ext2_acl_count(src_size);
+ if (count <= 0)
+ goto fail;
+
+ BUG_ON(dst_size < acl_ea_size(count));
+ ext_acl->a_version = cpu_to_le32(ACL_EA_VERSION);
+ for (i = 0; i < count; i++, dst_entry++) {
+ src_entry = (ext2_acl_entry *)src;
+ if (src + sizeof(ext2_acl_entry_short) > end)
+ goto fail;
+ dst_entry->e_tag = src_entry->e_tag;
+ dst_entry->e_perm = src_entry->e_perm;
+ switch (le16_to_cpu(src_entry->e_tag)) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ src += sizeof(ext2_acl_entry_short);
+ dst_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
+ break;
+ case ACL_USER:
+ case ACL_GROUP:
+ src += sizeof(ext2_acl_entry);
+ if (src > end)
+ goto fail;
+ dst_entry->e_id = src_entry->e_id;
+ break;
+ default:
+ goto fail;
+ }
+ }
+ if (src != end)
+ goto fail;
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static char *xattr_prefix_table[] = {
+ [1] = "user.",
+ [2] = "system.posix_acl_access",
+ [3] = "system.posix_acl_default",
+ [4] = "trusted.",
+ [6] = "security.",
+};
+
+static int ext2_copy_single_xattr(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct ext2_ext_attr_entry *entry,
+ const void *data, u32 datalen)
+{
+ int ret = 0;
+ int name_len;
+ int name_index;
+ void *databuf = NULL;
+ char namebuf[XATTR_NAME_MAX + 1];
+
+ name_index = entry->e_name_index;
+ if (name_index >= ARRAY_SIZE(xattr_prefix_table) ||
+ xattr_prefix_table[name_index] == NULL)
+ return -EOPNOTSUPP;
+ name_len = strlen(xattr_prefix_table[name_index]) +
+ entry->e_name_len;
+ if (name_len >= sizeof(namebuf))
+ return -ERANGE;
+
+ if (name_index == 2 || name_index == 3) {
+ size_t bufsize = acl_ea_size(ext2_acl_count(datalen));
+ databuf = malloc(bufsize);
+ if (!databuf)
+ return -ENOMEM;
+ ret = ext2_acl_to_xattr(databuf, data, bufsize, datalen);
+ if (ret)
+ goto out;
+ data = databuf;
+ datalen = bufsize;
+ }
+ strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX);
+ strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
+ if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) -
+ sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
+ fprintf(stderr, "skip large xattr on inode %Lu name %.*s\n",
+ objectid - INO_OFFSET, name_len, namebuf);
+ goto out;
+ }
+ ret = btrfs_insert_xattr_item(trans, root, namebuf, name_len,
+ data, datalen, objectid);
+out:
+ free(databuf);
+ return ret;
+}
+
+static int ext2_copy_extended_attrs(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct btrfs_inode_item *btrfs_inode,
+ ext2_filsys ext2_fs, ext2_ino_t ext2_ino)
+{
+ int ret = 0;
+ int inline_ea = 0;
+ errcode_t err;
+ u32 datalen;
+ u32 block_size = ext2_fs->blocksize;
+ u32 inode_size = EXT2_INODE_SIZE(ext2_fs->super);
+ struct ext2_inode_large *ext2_inode;
+ struct ext2_ext_attr_entry *entry;
+ void *data;
+ char *buffer = NULL;
+ char inode_buf[EXT2_GOOD_OLD_INODE_SIZE];
+
+ if (inode_size <= EXT2_GOOD_OLD_INODE_SIZE) {
+ ext2_inode = (struct ext2_inode_large *)inode_buf;
+ } else {
+ ext2_inode = (struct ext2_inode_large *)malloc(inode_size);
+ if (!ext2_inode)
+ return -ENOMEM;
+ }
+ err = ext2fs_read_inode_full(ext2_fs, ext2_ino, (void *)ext2_inode,
+ inode_size);
+ if (err) {
+ fprintf(stderr, "ext2fs_read_inode_full: %s\n",
+ error_message(err));
+ ret = -1;
+ goto out;
+ }
+
+ if (ext2_ino > ext2_fs->super->s_first_ino &&
+ inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
+ if (EXT2_GOOD_OLD_INODE_SIZE +
+ ext2_inode->i_extra_isize > inode_size) {
+ ret = -EIO;
+ goto out;
+ }
+ if (ext2_inode->i_extra_isize != 0 &&
+ EXT2_XATTR_IHDR(ext2_inode)->h_magic ==
+ EXT2_EXT_ATTR_MAGIC) {
+ inline_ea = 1;
+ }
+ }
+ if (inline_ea) {
+ int total;
+ void *end = (void *)ext2_inode + inode_size;
+ entry = EXT2_XATTR_IFIRST(ext2_inode);
+ total = end - (void *)entry;
+ ret = ext2_xattr_check_names(entry, end);
+ if (ret)
+ goto out;
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ ret = ext2_xattr_check_entry(entry, total);
+ if (ret)
+ goto out;
+ data = (void *)EXT2_XATTR_IFIRST(ext2_inode) +
+ entry->e_value_offs;
+ datalen = entry->e_value_size;
+ ret = ext2_copy_single_xattr(trans, root, objectid,
+ entry, data, datalen);
+ if (ret)
+ goto out;
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+ }
+
+ if (ext2_inode->i_file_acl == 0)
+ goto out;
+
+ buffer = malloc(block_size);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ err = ext2fs_read_ext_attr2(ext2_fs, ext2_inode->i_file_acl, buffer);
+ if (err) {
+ fprintf(stderr, "ext2fs_read_ext_attr2: %s\n",
+ error_message(err));
+ ret = -1;
+ goto out;
+ }
+ ret = ext2_xattr_check_block(buffer, block_size);
+ if (ret)
+ goto out;
+
+ entry = EXT2_XATTR_BFIRST(buffer);
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ ret = ext2_xattr_check_entry(entry, block_size);
+ if (ret)
+ goto out;
+ data = buffer + entry->e_value_offs;
+ datalen = entry->e_value_size;
+ ret = ext2_copy_single_xattr(trans, root, objectid,
+ entry, data, datalen);
+ if (ret)
+ goto out;
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+out:
+ free(buffer);
+ if ((void *)ext2_inode != inode_buf)
+ free(ext2_inode);
+ return ret;
+}
+#define MINORBITS 20
+#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi))
+
+static inline dev_t old_decode_dev(u16 val)
+{
+ return MKDEV((val >> 8) & 255, val & 255);
+}
+
+static inline dev_t new_decode_dev(u32 dev)
+{
+ unsigned major = (dev & 0xfff00) >> 8;
+ unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
+ return MKDEV(major, minor);
+}
+
+static void ext2_copy_inode_item(struct btrfs_inode_item *dst,
+ struct ext2_inode *src, u32 blocksize)
+{
+ btrfs_set_stack_inode_generation(dst, 1);
+ btrfs_set_stack_inode_sequence(dst, 0);
+ btrfs_set_stack_inode_transid(dst, 1);
+ btrfs_set_stack_inode_size(dst, src->i_size);
+ btrfs_set_stack_inode_nbytes(dst, 0);
+ btrfs_set_stack_inode_block_group(dst, 0);
+ btrfs_set_stack_inode_nlink(dst, src->i_links_count);
+ btrfs_set_stack_inode_uid(dst, src->i_uid | (src->i_uid_high << 16));
+ btrfs_set_stack_inode_gid(dst, src->i_gid | (src->i_gid_high << 16));
+ btrfs_set_stack_inode_mode(dst, src->i_mode);
+ btrfs_set_stack_inode_rdev(dst, 0);
+ btrfs_set_stack_inode_flags(dst, 0);
+ btrfs_set_stack_timespec_sec(&dst->atime, src->i_atime);
+ btrfs_set_stack_timespec_nsec(&dst->atime, 0);
+ btrfs_set_stack_timespec_sec(&dst->ctime, src->i_ctime);
+ btrfs_set_stack_timespec_nsec(&dst->ctime, 0);
+ btrfs_set_stack_timespec_sec(&dst->mtime, src->i_mtime);
+ btrfs_set_stack_timespec_nsec(&dst->mtime, 0);
+ btrfs_set_stack_timespec_sec(&dst->otime, 0);
+ btrfs_set_stack_timespec_nsec(&dst->otime, 0);
+
+ if (S_ISDIR(src->i_mode)) {
+ btrfs_set_stack_inode_size(dst, 0);
+ btrfs_set_stack_inode_nlink(dst, 1);
+ }
+ if (S_ISREG(src->i_mode)) {
+ btrfs_set_stack_inode_size(dst, (u64)src->i_size_high << 32 |
+ (u64)src->i_size);
+ }
+ if (!S_ISREG(src->i_mode) && !S_ISDIR(src->i_mode) &&
+ !S_ISLNK(src->i_mode)) {
+ if (src->i_block[0]) {
+ btrfs_set_stack_inode_rdev(dst,
+ old_decode_dev(src->i_block[0]));
+ } else {
+ btrfs_set_stack_inode_rdev(dst,
+ new_decode_dev(src->i_block[1]));
+ }
+ }
+ 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 = btrfs_stack_inode_flags(dst);
+
+ 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
+ * inode item, creating file extents and creating directory entries.
+ */
+static int ext2_copy_single_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
+ struct ext2_inode *ext2_inode,
+ u32 convert_flags)
+{
+ int ret;
+ struct btrfs_inode_item btrfs_inode;
+
+ if (ext2_inode->i_links_count == 0)
+ return 0;
+
+ ext2_copy_inode_item(&btrfs_inode, ext2_inode, ext2_fs->blocksize);
+ if (!(convert_flags & CONVERT_FLAG_DATACSUM)
+ && S_ISREG(ext2_inode->i_mode)) {
+ u32 flags = btrfs_stack_inode_flags(&btrfs_inode) |
+ BTRFS_INODE_NODATASUM;
+ btrfs_set_stack_inode_flags(&btrfs_inode, flags);
+ }
+ ext2_convert_inode_flags(&btrfs_inode, ext2_inode);
+
+ switch (ext2_inode->i_mode & S_IFMT) {
+ case S_IFREG:
+ ret = ext2_create_file_extents(trans, root, objectid,
+ &btrfs_inode, ext2_fs, ext2_ino, convert_flags);
+ break;
+ case S_IFDIR:
+ ret = ext2_create_dir_entries(trans, root, objectid,
+ &btrfs_inode, ext2_fs, ext2_ino);
+ break;
+ case S_IFLNK:
+ ret = ext2_create_symlink(trans, root, objectid,
+ &btrfs_inode, ext2_fs, ext2_ino, ext2_inode);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if (ret)
+ return ret;
+
+ if (convert_flags & CONVERT_FLAG_XATTR) {
+ ret = ext2_copy_extended_attrs(trans, root, objectid,
+ &btrfs_inode, ext2_fs, ext2_ino);
+ if (ret)
+ return ret;
+ }
+ return btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
+}
+
+static int ext2_is_special_inode(ext2_ino_t ino)
+{
+ if (ino < EXT2_GOOD_OLD_FIRST_INO && ino != EXT2_ROOT_INO)
+ return 1;
+ return 0;
+}
+
+/*
+ * scan ext2's inode bitmap and copy all used inodes.
+ */
+static int ext2_copy_inodes(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root,
+ u32 convert_flags, struct task_ctx *p)
+{
+ ext2_filsys ext2_fs = cctx->fs_data;
+ int ret;
+ errcode_t err;
+ ext2_inode_scan ext2_scan;
+ struct ext2_inode ext2_inode;
+ ext2_ino_t ext2_ino;
+ u64 objectid;
+ struct btrfs_trans_handle *trans;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (!trans)
+ return -ENOMEM;
+ err = ext2fs_open_inode_scan(ext2_fs, 0, &ext2_scan);
+ if (err) {
+ fprintf(stderr, "ext2fs_open_inode_scan: %s\n", error_message(err));
+ return -1;
+ }
+ while (!(err = ext2fs_get_next_inode(ext2_scan, &ext2_ino,
+ &ext2_inode))) {
+ /* no more inodes */
+ if (ext2_ino == 0)
+ break;
+ if (ext2_is_special_inode(ext2_ino))
+ continue;
+ objectid = ext2_ino + INO_OFFSET;
+ ret = ext2_copy_single_inode(trans, root,
+ objectid, ext2_fs, ext2_ino,
+ &ext2_inode, convert_flags);
+ pthread_mutex_lock(&p->mutex);
+ p->cur_copy_inodes++;
+ pthread_mutex_unlock(&p->mutex);
+ if (ret)
+ return ret;
+ if (trans->blocks_used >= 4096) {
+ ret = btrfs_commit_transaction(trans, root);
+ BUG_ON(ret);
+ trans = btrfs_start_transaction(root, 1);
+ BUG_ON(!trans);
+ }
+ }
+ if (err) {
+ fprintf(stderr, "ext2fs_get_next_inode: %s\n", error_message(err));
+ return -1;
+ }
+ ret = btrfs_commit_transaction(trans, root);
+ BUG_ON(ret);
+ ext2fs_close_inode_scan(ext2_scan);
+
+ return ret;
+}
+
+const struct btrfs_convert_operations ext2_convert_ops = {
+ .name = "ext2",
+ .open_fs = ext2_open_fs,
+ .read_used_space = ext2_read_used_space,
+ .copy_inodes = ext2_copy_inodes,
+ .close_fs = ext2_close_fs,
+ .check_state = ext2_check_state,
+};
+
+#endif /* BTRFSCONVERT_EXT2 */
diff --git a/convert/source-ext2.h b/convert/source-ext2.h
new file mode 100644
index 00000000..20a278ed
--- /dev/null
+++ b/convert/source-ext2.h
@@ -0,0 +1,113 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_CONVERT_SOURCE_EXT2_H__
+#define __BTRFS_CONVERT_SOURCE_EXT2_H__
+
+#if BTRFSCONVERT_EXT2
+
+#include "kerncompat.h"
+
+#include <ext2fs/ext2_fs.h>
+#include <ext2fs/ext2fs.h>
+#include <ext2fs/ext2_ext_attr.h>
+#include "convert/source-fs.h"
+
+#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO)
+
+/*
+ * Compatibility code for e2fsprogs 1.41 which doesn't support RO compat flag
+ * BIGALLOC.
+ * Unlike normal RO compat flag, BIGALLOC affects how e2fsprogs check used
+ * space, and btrfs-convert heavily relies on it.
+ */
+#ifdef HAVE_OLD_E2FSPROGS
+#define EXT2FS_CLUSTER_RATIO(fs) (1)
+#define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_BLOCKS_PER_GROUP(s))
+#define EXT2FS_B2C(fs, blk) (blk)
+#endif
+
+/*
+ * Following xattr/acl related codes are based on codes in
+ * fs/ext3/xattr.c and fs/ext3/acl.c
+ */
+#define EXT2_XATTR_BHDR(ptr) ((struct ext2_ext_attr_header *)(ptr))
+#define EXT2_XATTR_BFIRST(ptr) \
+ ((struct ext2_ext_attr_entry *)(EXT2_XATTR_BHDR(ptr) + 1))
+#define EXT2_XATTR_IHDR(inode) \
+ ((struct ext2_ext_attr_header *) ((void *)(inode) + \
+ EXT2_GOOD_OLD_INODE_SIZE + (inode)->i_extra_isize))
+#define EXT2_XATTR_IFIRST(inode) \
+ ((struct ext2_ext_attr_entry *) ((void *)EXT2_XATTR_IHDR(inode) + \
+ sizeof(EXT2_XATTR_IHDR(inode)->h_magic)))
+
+struct dir_iterate_data {
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *root;
+ struct btrfs_inode_item *inode;
+ u64 objectid;
+ u64 index_cnt;
+ u64 parent;
+ int errcode;
+};
+
+#define EXT2_ACL_VERSION 0x0001
+
+/* 23.2.5 acl_tag_t values */
+
+#define ACL_UNDEFINED_TAG (0x00)
+#define ACL_USER_OBJ (0x01)
+#define ACL_USER (0x02)
+#define ACL_GROUP_OBJ (0x04)
+#define ACL_GROUP (0x08)
+#define ACL_MASK (0x10)
+#define ACL_OTHER (0x20)
+
+/* 23.2.7 ACL qualifier constants */
+
+#define ACL_UNDEFINED_ID ((id_t)-1)
+
+typedef struct {
+ __le16 e_tag;
+ __le16 e_perm;
+ __le32 e_id;
+} ext2_acl_entry;
+
+typedef struct {
+ __le16 e_tag;
+ __le16 e_perm;
+} ext2_acl_entry_short;
+
+typedef struct {
+ __le32 a_version;
+} ext2_acl_header;
+
+#define ACL_EA_VERSION 0x0002
+
+typedef struct {
+ __le16 e_tag;
+ __le16 e_perm;
+ __le32 e_id;
+} acl_ea_entry;
+
+typedef struct {
+ __le32 a_version;
+ acl_ea_entry a_entries[0];
+} acl_ea_header;
+
+#endif /* BTRFSCONVERT_EXT2 */
+
+#endif
diff --git a/convert/source-fs.c b/convert/source-fs.c
new file mode 100644
index 00000000..59e36095
--- /dev/null
+++ b/convert/source-fs.c
@@ -0,0 +1,280 @@
+/*
+ * 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 <unistd.h>
+#include "internal.h"
+#include "disk-io.h"
+#include "volumes.h"
+#include "convert/common.h"
+#include "convert/source-fs.h"
+
+const struct simple_range btrfs_reserved_ranges[3] = {
+ { 0, SZ_1M },
+ { BTRFS_SB_MIRROR_OFFSET(1), SZ_64K },
+ { BTRFS_SB_MIRROR_OFFSET(2), SZ_64K }
+};
+
+static int intersect_with_sb(u64 bytenr, u64 num_bytes)
+{
+ int i;
+ u64 offset;
+
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ offset = btrfs_sb_offset(i);
+ offset &= ~((u64)BTRFS_STRIPE_LEN - 1);
+
+ if (bytenr < offset + BTRFS_STRIPE_LEN &&
+ bytenr + num_bytes > offset)
+ return 1;
+ }
+ return 0;
+}
+
+void init_convert_context(struct btrfs_convert_context *cctx)
+{
+ memset(cctx, 0, sizeof(*cctx));
+
+ cache_tree_init(&cctx->used_space);
+ cache_tree_init(&cctx->data_chunks);
+ cache_tree_init(&cctx->free_space);
+}
+
+void clean_convert_context(struct btrfs_convert_context *cctx)
+{
+ free_extent_cache_tree(&cctx->used_space);
+ free_extent_cache_tree(&cctx->data_chunks);
+ free_extent_cache_tree(&cctx->free_space);
+}
+
+int block_iterate_proc(u64 disk_block, u64 file_block,
+ struct blk_iterate_data *idata)
+{
+ int ret = 0;
+ int sb_region;
+ int do_barrier;
+ struct btrfs_root *root = idata->root;
+ struct btrfs_block_group_cache *cache;
+ u32 sectorsize = root->fs_info->sectorsize;
+ u64 bytenr = disk_block * sectorsize;
+
+ sb_region = intersect_with_sb(bytenr, sectorsize);
+ do_barrier = sb_region || disk_block >= idata->boundary;
+ if ((idata->num_blocks > 0 && do_barrier) ||
+ (file_block > idata->first_block + idata->num_blocks) ||
+ (disk_block != idata->disk_block + idata->num_blocks)) {
+ if (idata->num_blocks > 0) {
+ ret = record_file_blocks(idata, idata->first_block,
+ idata->disk_block,
+ idata->num_blocks);
+ if (ret)
+ goto fail;
+ idata->first_block += idata->num_blocks;
+ idata->num_blocks = 0;
+ }
+ if (file_block > idata->first_block) {
+ ret = record_file_blocks(idata, idata->first_block,
+ 0, file_block - idata->first_block);
+ if (ret)
+ goto fail;
+ }
+
+ if (sb_region) {
+ bytenr += BTRFS_STRIPE_LEN - 1;
+ bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1);
+ } else {
+ cache = btrfs_lookup_block_group(root->fs_info, bytenr);
+ BUG_ON(!cache);
+ bytenr = cache->key.objectid + cache->key.offset;
+ }
+
+ idata->first_block = file_block;
+ idata->disk_block = disk_block;
+ idata->boundary = bytenr / sectorsize;
+ }
+ idata->num_blocks++;
+fail:
+ return ret;
+}
+
+void init_blk_iterate_data(struct blk_iterate_data *data,
+ struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_inode_item *inode,
+ u64 objectid, int checksum)
+{
+ struct btrfs_key key;
+
+ data->trans = trans;
+ data->root = root;
+ data->inode = inode;
+ data->objectid = objectid;
+ data->first_block = 0;
+ data->disk_block = 0;
+ data->num_blocks = 0;
+ data->boundary = (u64)-1;
+ data->checksum = checksum;
+ data->errcode = 0;
+
+ key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ data->convert_root = btrfs_read_fs_root(root->fs_info, &key);
+ /* Impossible as we just opened it before */
+ BUG_ON(!data->convert_root || IS_ERR(data->convert_root));
+ data->convert_ino = BTRFS_FIRST_FREE_OBJECTID + 1;
+}
+
+int convert_insert_dirent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ const char *name, size_t name_len,
+ u64 dir, u64 objectid,
+ u8 file_type, u64 index_cnt,
+ struct btrfs_inode_item *inode)
+{
+ int ret;
+ u64 inode_size;
+ struct btrfs_key location = {
+ .objectid = objectid,
+ .offset = 0,
+ .type = BTRFS_INODE_ITEM_KEY,
+ };
+
+ ret = btrfs_insert_dir_item(trans, root, name, name_len,
+ dir, &location, file_type, index_cnt);
+ if (ret)
+ return ret;
+ ret = btrfs_insert_inode_ref(trans, root, name, name_len,
+ objectid, dir, index_cnt);
+ if (ret)
+ return ret;
+ inode_size = btrfs_stack_inode_size(inode) + name_len * 2;
+ btrfs_set_stack_inode_size(inode, inode_size);
+
+ return 0;
+}
+
+int read_disk_extent(struct btrfs_root *root, u64 bytenr,
+ u32 num_bytes, char *buffer)
+{
+ int ret;
+ struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices;
+
+ ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr);
+ if (ret != num_bytes)
+ goto fail;
+ ret = 0;
+fail:
+ if (ret > 0)
+ ret = -1;
+ return ret;
+}
+
+/*
+ * Record a file extent in original filesystem into btrfs one.
+ * The special point is, old disk_block can point to a reserved range.
+ * So here, we don't use disk_block directly but search convert_root
+ * to get the real disk_bytenr.
+ */
+int record_file_blocks(struct blk_iterate_data *data,
+ u64 file_block, u64 disk_block, u64 num_blocks)
+{
+ int ret = 0;
+ struct btrfs_root *root = data->root;
+ struct btrfs_root *convert_root = data->convert_root;
+ struct btrfs_path path;
+ u32 sectorsize = root->fs_info->sectorsize;
+ u64 file_pos = file_block * sectorsize;
+ u64 old_disk_bytenr = disk_block * sectorsize;
+ u64 num_bytes = num_blocks * sectorsize;
+ u64 cur_off = old_disk_bytenr;
+
+ /* Hole, pass it to record_file_extent directly */
+ if (old_disk_bytenr == 0)
+ return btrfs_record_file_extent(data->trans, root,
+ data->objectid, data->inode, file_pos, 0,
+ num_bytes);
+
+ btrfs_init_path(&path);
+
+ /*
+ * Search real disk bytenr from convert root
+ */
+ while (cur_off < old_disk_bytenr + num_bytes) {
+ struct btrfs_key key;
+ struct btrfs_file_extent_item *fi;
+ struct extent_buffer *node;
+ int slot;
+ u64 extent_disk_bytenr;
+ u64 extent_num_bytes;
+ u64 real_disk_bytenr;
+ u64 cur_len;
+
+ key.objectid = data->convert_ino;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = cur_off;
+
+ 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,
+ data->convert_ino,
+ BTRFS_EXTENT_DATA_KEY);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = -ENOENT;
+ break;
+ }
+ }
+ 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_num_bytes(node, fi);
+ BUG_ON(cur_off - key.offset >= extent_num_bytes);
+ btrfs_release_path(&path);
+
+ if (extent_disk_bytenr)
+ real_disk_bytenr = cur_off - key.offset +
+ extent_disk_bytenr;
+ else
+ real_disk_bytenr = 0;
+ cur_len = min(key.offset + extent_num_bytes,
+ old_disk_bytenr + num_bytes) - cur_off;
+ ret = btrfs_record_file_extent(data->trans, data->root,
+ data->objectid, data->inode, file_pos,
+ real_disk_bytenr, cur_len);
+ if (ret < 0)
+ break;
+ cur_off += cur_len;
+ file_pos += cur_len;
+
+ /*
+ * No need to care about csum
+ * As every byte of old fs image is calculated for csum, no
+ * need to waste CPU cycles now.
+ */
+ }
+ btrfs_release_path(&path);
+ return ret;
+}
+
diff --git a/convert/source-fs.h b/convert/source-fs.h
new file mode 100644
index 00000000..3a6fa46c
--- /dev/null
+++ b/convert/source-fs.h
@@ -0,0 +1,118 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_CONVERT_SOURCE_FS_H__
+#define __BTRFS_CONVERT_SOURCE_FS_H__
+
+#include "kerncompat.h"
+#include <pthread.h>
+
+
+#define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
+
+/*
+ * Reresents a simple contiguous range.
+ *
+ * For multiple or non-contiguous ranges, use extent_cache_tree from
+ * extent-cache.c
+ */
+struct simple_range {
+ u64 start;
+ u64 len;
+};
+
+extern const struct simple_range btrfs_reserved_ranges[3];
+
+struct task_info;
+
+struct task_ctx {
+ pthread_mutex_t mutex;
+ u64 max_copy_inodes;
+ u64 cur_copy_inodes;
+ struct task_info *info;
+};
+
+struct btrfs_convert_context;
+
+#define SOURCE_FS_NAME_LEN (16)
+
+#define CONVERT_FLAG_DATACSUM (1U << 0)
+#define CONVERT_FLAG_INLINE_DATA (1U << 1)
+#define CONVERT_FLAG_XATTR (1U << 2)
+#define CONVERT_FLAG_COPY_LABEL (1U << 3)
+#define CONVERT_FLAG_SET_LABEL (1U << 4)
+
+struct btrfs_convert_operations {
+ const char name[SOURCE_FS_NAME_LEN];
+ int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname);
+ int (*read_used_space)(struct btrfs_convert_context *cctx);
+ int (*copy_inodes)(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root, u32 covert_flags,
+ struct task_ctx *p);
+ void (*close_fs)(struct btrfs_convert_context *cctx);
+ int (*check_state)(struct btrfs_convert_context *cctx);
+};
+
+struct btrfs_trans_handle;
+struct btrfs_root;
+struct btrfs_inode_item;
+
+struct blk_iterate_data {
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *root;
+ struct btrfs_root *convert_root;
+ struct btrfs_inode_item *inode;
+ u64 convert_ino;
+ u64 objectid;
+ u64 first_block;
+ u64 disk_block;
+ u64 num_blocks;
+ u64 boundary;
+ int checksum;
+ int errcode;
+};
+
+void init_convert_context(struct btrfs_convert_context *cctx);
+void clean_convert_context(struct btrfs_convert_context *cctx);
+int block_iterate_proc(u64 disk_block, u64 file_block,
+ struct blk_iterate_data *idata);
+void init_blk_iterate_data(struct blk_iterate_data *data,
+ struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_inode_item *inode,
+ u64 objectid, int checksum);
+int convert_insert_dirent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ const char *name, size_t name_len,
+ u64 dir, u64 objectid,
+ u8 file_type, u64 index_cnt,
+ struct btrfs_inode_item *inode);
+int read_disk_extent(struct btrfs_root *root, u64 bytenr,
+ u32 num_bytes, char *buffer);
+int record_file_blocks(struct blk_iterate_data *data,
+ u64 file_block, u64 disk_block, u64 num_blocks);
+
+/*
+ * Simple range functions
+ *
+ * Get range end (exclusive)
+ */
+static inline u64 range_end(const struct simple_range *range)
+{
+ return (range->start + range->len);
+}
+
+#endif
diff --git a/ctree.c b/ctree.c
index d07ec7d9..f3c13e1c 100644
--- a/ctree.c
+++ b/ctree.c
@@ -21,6 +21,7 @@
#include "print-tree.h"
#include "repair.h"
#include "internal.h"
+#include "sizes.h"
static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, int level);
@@ -368,7 +369,7 @@ int btrfs_cow_block(struct btrfs_trans_handle *trans,
return 0;
}
- search_start = buf->start & ~((u64)(1024 * 1024 * 1024) - 1);
+ search_start = buf->start & ~((u64)SZ_1G - 1);
ret = __btrfs_cow_block(trans, root, buf, parent,
parent_slot, cow_ret, search_start, 0);
return ret;
@@ -636,7 +637,7 @@ static int bin_search(struct extent_buffer *eb, struct btrfs_key *key,
slot);
}
-struct extent_buffer *read_node_slot(struct btrfs_root *root,
+struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
struct extent_buffer *parent, int slot)
{
int level = btrfs_header_level(parent);
@@ -648,8 +649,8 @@ struct extent_buffer *read_node_slot(struct btrfs_root *root,
if (level == 0)
return NULL;
- return read_tree_block(root, btrfs_node_blockptr(parent, slot),
- root->nodesize,
+ return read_tree_block(fs_info, btrfs_node_blockptr(parent, slot),
+ fs_info->nodesize,
btrfs_node_ptr_generation(parent, slot));
}
@@ -661,6 +662,7 @@ static int balance_level(struct btrfs_trans_handle *trans,
struct extent_buffer *mid;
struct extent_buffer *left = NULL;
struct extent_buffer *parent = NULL;
+ struct btrfs_fs_info *fs_info = root->fs_info;
int ret = 0;
int wret;
int pslot;
@@ -691,7 +693,7 @@ static int balance_level(struct btrfs_trans_handle *trans,
return 0;
/* promote the child to a root */
- child = read_node_slot(root, mid, 0);
+ child = read_node_slot(fs_info, mid, 0);
BUG_ON(!extent_buffer_uptodate(child));
ret = btrfs_cow_block(trans, root, child, mid, 0, &child);
BUG_ON(ret);
@@ -700,7 +702,6 @@ static int balance_level(struct btrfs_trans_handle *trans,
add_root_to_dirty_list(root);
path->nodes[level] = NULL;
clean_tree_block(trans, root, mid);
- wait_on_tree_block_writeback(root, mid);
/* once for the path */
free_extent_buffer(mid);
@@ -715,7 +716,7 @@ static int balance_level(struct btrfs_trans_handle *trans,
BTRFS_NODEPTRS_PER_BLOCK(root) / 4)
return 0;
- left = read_node_slot(root, parent, pslot - 1);
+ left = read_node_slot(fs_info, parent, pslot - 1);
if (extent_buffer_uptodate(left)) {
wret = btrfs_cow_block(trans, root, left,
parent, pslot - 1, &left);
@@ -724,7 +725,7 @@ static int balance_level(struct btrfs_trans_handle *trans,
goto enospc;
}
}
- right = read_node_slot(root, parent, pslot + 1);
+ right = read_node_slot(fs_info, parent, pslot + 1);
if (extent_buffer_uptodate(right)) {
wret = btrfs_cow_block(trans, root, right,
parent, pslot + 1, &right);
@@ -754,11 +755,9 @@ static int balance_level(struct btrfs_trans_handle *trans,
u32 blocksize = right->len;
clean_tree_block(trans, root, right);
- wait_on_tree_block_writeback(root, right);
free_extent_buffer(right);
right = NULL;
- wret = btrfs_del_ptr(trans, root, path,
- level + 1, pslot + 1);
+ wret = btrfs_del_ptr(root, path, level + 1, pslot + 1);
if (wret)
ret = wret;
wret = btrfs_free_extent(trans, root, bytenr,
@@ -802,10 +801,9 @@ static int balance_level(struct btrfs_trans_handle *trans,
u64 bytenr = mid->start;
u32 blocksize = mid->len;
clean_tree_block(trans, root, mid);
- wait_on_tree_block_writeback(root, mid);
free_extent_buffer(mid);
mid = NULL;
- wret = btrfs_del_ptr(trans, root, path, level + 1, pslot);
+ wret = btrfs_del_ptr(root, path, level + 1, pslot);
if (wret)
ret = wret;
wret = btrfs_free_extent(trans, root, bytenr, blocksize,
@@ -857,6 +855,7 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
struct extent_buffer *mid;
struct extent_buffer *left = NULL;
struct extent_buffer *parent = NULL;
+ struct btrfs_fs_info *fs_info = root->fs_info;
int ret = 0;
int wret;
int pslot;
@@ -876,7 +875,7 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
if (!parent)
return 1;
- left = read_node_slot(root, parent, pslot - 1);
+ left = read_node_slot(fs_info, parent, pslot - 1);
/* first, try to make some room in the middle buffer */
if (extent_buffer_uptodate(left)) {
@@ -917,7 +916,7 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
}
free_extent_buffer(left);
}
- right= read_node_slot(root, parent, pslot + 1);
+ right= read_node_slot(fs_info, parent, pslot + 1);
/*
* then try to empty the right most buffer into the middle
@@ -969,6 +968,7 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
int level, int slot, u64 objectid)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct extent_buffer *node;
struct btrfs_disk_key disk_key;
u32 nritems;
@@ -990,8 +990,8 @@ void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
node = path->nodes[level];
search = btrfs_node_blockptr(node, slot);
- blocksize = root->nodesize;
- eb = btrfs_find_tree_block(root, search, blocksize);
+ blocksize = fs_info->nodesize;
+ eb = btrfs_find_tree_block(fs_info, search, blocksize);
if (eb) {
free_extent_buffer(eb);
return;
@@ -1021,14 +1021,14 @@ void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
if ((search >= lowest_read && search <= highest_read) ||
(search < lowest_read && lowest_read - search <= 32768) ||
(search > highest_read && search - highest_read <= 32768)) {
- readahead_tree_block(root, search, blocksize,
+ readahead_tree_block(fs_info, search, blocksize,
btrfs_node_ptr_generation(node, nr));
nread += blocksize;
}
nscan++;
- if (path->reada < 2 && (nread > (256 * 1024) || nscan > 32))
+ if (path->reada < 2 && (nread > SZ_256K || nscan > 32))
break;
- if(nread > (1024 * 1024) || nscan > 128)
+ if(nread > SZ_1M || nscan > 128)
break;
if (search < lowest_read)
@@ -1105,6 +1105,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
int ret;
int level;
int should_reada = p->reada;
+ struct btrfs_fs_info *fs_info = root->fs_info;
u8 lowest_level = 0;
lowest_level = p->lowest_level;
@@ -1172,7 +1173,7 @@ again:
reada_for_search(root, p, level, slot,
key->objectid);
- b = read_node_slot(root, b, slot);
+ b = read_node_slot(fs_info, b, slot);
if (!extent_buffer_uptodate(b))
return -EIO;
} else {
@@ -1423,7 +1424,7 @@ static int noinline insert_new_root(struct btrfs_trans_handle *trans,
else
btrfs_node_key(lower, &lower_key, 0);
- c = btrfs_alloc_free_block(trans, root, root->nodesize,
+ c = btrfs_alloc_free_block(trans, root, root->fs_info->nodesize,
root->root_key.objectid, &lower_key,
level, root->node->start, 0);
@@ -1490,7 +1491,8 @@ static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
BUG();
if (nritems == BTRFS_NODEPTRS_PER_BLOCK(root))
BUG();
- if (slot != nritems) {
+ if (slot < nritems) {
+ /* shift the items */
memmove_extent_buffer(lower,
btrfs_node_key_ptr_offset(slot + 1),
btrfs_node_key_ptr_offset(slot),
@@ -1546,7 +1548,7 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
mid = (c_nritems + 1) / 2;
btrfs_node_key(c, &disk_key, mid);
- split = btrfs_alloc_free_block(trans, root, root->nodesize,
+ split = btrfs_alloc_free_block(trans, root, root->fs_info->nodesize,
root->root_key.objectid,
&disk_key, level, c->start, 0);
if (IS_ERR(split))
@@ -1647,6 +1649,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root
struct extent_buffer *right;
struct extent_buffer *upper;
struct btrfs_disk_key disk_key;
+ struct btrfs_fs_info *fs_info = root->fs_info;
int slot;
u32 i;
int free_space;
@@ -1668,7 +1671,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root
if (slot >= btrfs_header_nritems(upper) - 1)
return 1;
- right = read_node_slot(root, upper, slot + 1);
+ right = read_node_slot(fs_info, upper, slot + 1);
if (!extent_buffer_uptodate(right)) {
if (IS_ERR(right))
return PTR_ERR(right);
@@ -1800,6 +1803,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
struct btrfs_disk_key disk_key;
struct extent_buffer *right = path->nodes[0];
struct extent_buffer *left;
+ struct btrfs_fs_info *fs_info = root->fs_info;
int slot;
int i;
int free_space;
@@ -1824,7 +1828,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
return 1;
}
- left = read_node_slot(root, path->nodes[1], slot - 1);
+ left = read_node_slot(fs_info, path->nodes[1], slot - 1);
free_space = btrfs_leaf_free_space(root, left);
if (free_space < data_size) {
free_extent_buffer(left);
@@ -2113,7 +2117,7 @@ again:
else
btrfs_item_key(l, &disk_key, mid);
- right = btrfs_alloc_free_block(trans, root, root->nodesize,
+ right = btrfs_alloc_free_block(trans, root, root->fs_info->nodesize,
root->root_key.objectid,
&disk_key, 0, l->start, 0);
if (IS_ERR(right)) {
@@ -2252,7 +2256,7 @@ split:
nritems = btrfs_header_nritems(leaf);
- if (slot != nritems) {
+ if (slot < nritems) {
/* shift the items */
memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + 1),
btrfs_item_nr_offset(slot),
@@ -2294,9 +2298,7 @@ split:
return ret;
}
-int btrfs_truncate_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *path,
+int btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path,
u32 new_size, int from_end)
{
int ret = 0;
@@ -2391,8 +2393,7 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
return ret;
}
-int btrfs_extend_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
+int btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path,
u32 data_size)
{
int ret = 0;
@@ -2506,7 +2507,7 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
slot = path->slots[0];
BUG_ON(slot < 0);
- if (slot != nritems) {
+ if (slot < nritems) {
unsigned int old_data = btrfs_item_end_nr(leaf, slot);
if (old_data < data_end) {
@@ -2601,15 +2602,16 @@ int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
* continuing all the way the root if required. The root is converted into
* a leaf if all the nodes are emptied.
*/
-int btrfs_del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
- struct btrfs_path *path, int level, int slot)
+int btrfs_del_ptr(struct btrfs_root *root, struct btrfs_path *path,
+ int level, int slot)
{
struct extent_buffer *parent = path->nodes[level];
u32 nritems;
int ret = 0;
nritems = btrfs_header_nritems(parent);
- if (slot != nritems -1) {
+ if (slot < nritems - 1) {
+ /* shift the items */
memmove_extent_buffer(parent,
btrfs_node_key_ptr_offset(slot),
btrfs_node_key_ptr_offset(slot + 1),
@@ -2650,7 +2652,7 @@ static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans,
int ret;
WARN_ON(btrfs_header_generation(leaf) != trans->transid);
- ret = btrfs_del_ptr(trans, root, path, 1, path->slots[1]);
+ ret = btrfs_del_ptr(root, path, 1, path->slots[1]);
if (ret)
return ret;
@@ -2713,8 +2715,6 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
btrfs_set_header_level(leaf, 0);
} else {
clean_tree_block(trans, root, leaf);
- wait_on_tree_block_writeback(root, leaf);
-
wret = btrfs_del_leaf(trans, root, path, leaf);
BUG_ON(ret);
if (wret)
@@ -2751,8 +2751,6 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
if (btrfs_header_nritems(leaf) == 0) {
clean_tree_block(trans, root, leaf);
- wait_on_tree_block_writeback(root, leaf);
-
path->slots[1] = slot;
ret = btrfs_del_leaf(trans, root, path, leaf);
BUG_ON(ret);
@@ -2780,6 +2778,7 @@ int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
int level = 1;
struct extent_buffer *c;
struct extent_buffer *next = NULL;
+ struct btrfs_fs_info *fs_info = root->fs_info;
while(level < BTRFS_MAX_LEVEL) {
if (!path->nodes[level])
@@ -2795,7 +2794,7 @@ int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
}
slot--;
- next = read_node_slot(root, c, slot);
+ next = read_node_slot(fs_info, c, slot);
if (!extent_buffer_uptodate(next)) {
if (IS_ERR(next))
return PTR_ERR(next);
@@ -2815,7 +2814,7 @@ int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
path->slots[level] = slot;
if (!level)
break;
- next = read_node_slot(root, next, slot);
+ next = read_node_slot(fs_info, next, slot);
if (!extent_buffer_uptodate(next)) {
if (IS_ERR(next))
return PTR_ERR(next);
@@ -2836,6 +2835,7 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
int level = 1;
struct extent_buffer *c;
struct extent_buffer *next = NULL;
+ struct btrfs_fs_info *fs_info = root->fs_info;
while(level < BTRFS_MAX_LEVEL) {
if (!path->nodes[level])
@@ -2853,7 +2853,7 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
if (path->reada)
reada_for_search(root, path, level, slot, 0);
- next = read_node_slot(root, c, slot);
+ next = read_node_slot(fs_info, c, slot);
if (!extent_buffer_uptodate(next))
return -EIO;
break;
@@ -2869,7 +2869,7 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
break;
if (path->reada)
reada_for_search(root, path, level, 0, 0);
- next = read_node_slot(root, next, 0);
+ next = read_node_slot(fs_info, next, 0);
if (!extent_buffer_uptodate(next))
return -EIO;
}
diff --git a/ctree.h b/ctree.h
index 0c34ae20..48ae8903 100644
--- a/ctree.h
+++ b/ctree.h
@@ -26,6 +26,7 @@
#include "extent-cache.h"
#include "extent_io.h"
#include "ioctl.h"
+#include "sizes.h"
#else
#include <btrfs/list.h>
#include <btrfs/kerncompat.h>
@@ -33,6 +34,7 @@
#include <btrfs/extent-cache.h>
#include <btrfs/extent_io.h>
#include <btrfs/ioctl.h>
+#include <btrfs/sizes.h>
#endif /* BTRFS_FLAT_INCLUDES */
struct btrfs_root;
@@ -350,11 +352,11 @@ struct btrfs_header {
u8 level;
} __attribute__ ((__packed__));
-#define BTRFS_NODEPTRS_PER_BLOCK(r) (((r)->nodesize - \
+#define BTRFS_NODEPTRS_PER_BLOCK(r) (((r)->fs_info->nodesize - \
sizeof(struct btrfs_header)) / \
sizeof(struct btrfs_key_ptr))
#define __BTRFS_LEAF_DATA_SIZE(bs) ((bs) - sizeof(struct btrfs_header))
-#define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->nodesize))
+#define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->fs_info->nodesize))
#define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \
sizeof(struct btrfs_item) - \
sizeof(struct btrfs_file_extent_item))
@@ -438,7 +440,7 @@ struct btrfs_super_block {
__le32 sectorsize;
__le32 nodesize;
/* Unused and must be equal to nodesize */
- __le32 leafsize;
+ __le32 __unused_leafsize;
__le32 stripesize;
__le32 sys_chunk_array_size;
__le64 chunk_root_generation;
@@ -601,7 +603,7 @@ struct btrfs_extent_item_v0 {
#define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r) >> 4) - \
sizeof(struct btrfs_item))
-#define BTRFS_MAX_EXTENT_SIZE (128 * 1024 * 1024)
+#define BTRFS_MAX_EXTENT_SIZE SZ_128M
#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0)
#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1)
@@ -952,7 +954,7 @@ struct btrfs_csum_item {
* - the first 64k blank is useful for some boot loader/manager
* - the first 1M could be scratched by buggy partitioner or somesuch
*/
-#define BTRFS_BLOCK_RESERVED_1M_FOR_SUPER ((u64)1024 * 1024)
+#define BTRFS_BLOCK_RESERVED_1M_FOR_SUPER ((u64)SZ_1M)
/* tag for the radix tree of block groups in ram */
#define BTRFS_BLOCK_GROUP_DATA (1ULL << 0)
@@ -1145,6 +1147,10 @@ struct btrfs_fs_info {
struct cache_tree *fsck_extent_cache;
struct cache_tree *corrupt_blocks;
+ /* Cached block sizes */
+ u32 nodesize;
+ u32 sectorsize;
+ u32 stripesize;
};
/*
@@ -1160,18 +1166,6 @@ struct btrfs_root {
u64 objectid;
u64 last_trans;
- /* data allocations are done in sectorsize units */
- u32 sectorsize;
-
- /* node allocations are done in nodesize units */
- u32 nodesize;
-
- /* Unused, equal to nodesize */
- u32 leafsize;
-
- /* leaf allocations are done in nodesize units */
- u32 stripesize;
-
int ref_cows;
int track_dirty;
@@ -2037,33 +2031,12 @@ static inline unsigned long btrfs_header_chunk_tree_uuid(struct extent_buffer *e
return offsetof(struct btrfs_header, chunk_tree_uuid);
}
-static inline u8 *btrfs_super_fsid(struct extent_buffer *eb)
-{
- unsigned long ptr = offsetof(struct btrfs_super_block, fsid);
- return (u8 *)ptr;
-}
-
static inline u8 *btrfs_header_csum(struct extent_buffer *eb)
{
unsigned long ptr = offsetof(struct btrfs_header, csum);
return (u8 *)ptr;
}
-static inline struct btrfs_node *btrfs_buffer_node(struct extent_buffer *eb)
-{
- return NULL;
-}
-
-static inline struct btrfs_leaf *btrfs_buffer_leaf(struct extent_buffer *eb)
-{
- return NULL;
-}
-
-static inline struct btrfs_header *btrfs_buffer_header(struct extent_buffer *eb)
-{
- return NULL;
-}
-
static inline int btrfs_is_leaf(struct extent_buffer *eb)
{
return (btrfs_header_level(eb) == 0);
@@ -2178,8 +2151,6 @@ BTRFS_SETGET_STACK_FUNCS(super_sectorsize, struct btrfs_super_block,
sectorsize, 32);
BTRFS_SETGET_STACK_FUNCS(super_nodesize, struct btrfs_super_block,
nodesize, 32);
-BTRFS_SETGET_STACK_FUNCS(super_leafsize, struct btrfs_super_block,
- leafsize, 32);
BTRFS_SETGET_STACK_FUNCS(super_stripesize, struct btrfs_super_block,
stripesize, 32);
BTRFS_SETGET_STACK_FUNCS(super_root_dir, struct btrfs_super_block,
@@ -2429,17 +2400,6 @@ static inline u32 btrfs_file_extent_inline_len(struct extent_buffer *eb,
return btrfs_file_extent_ram_bytes(eb, fi);
}
-/*
- * NOTE: Backward compatibility, do not use.
- * Replacement: read nodesize directly
- */
-__attribute__((deprecated))
-static inline u32 btrfs_level_size(struct btrfs_root *root, int level) {
- if (level == 0)
- return root->leafsize;
- return root->nodesize;
-}
-
#define btrfs_fs_incompat(fs_info, opt) \
__btrfs_fs_incompat((fs_info), BTRFS_FEATURE_INCOMPAT_##opt)
@@ -2538,11 +2498,11 @@ struct btrfs_block_group_cache *
btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
u64 chunk_objectid, u64 chunk_offset, u64 size);
int btrfs_make_block_group(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 bytes_used,
+ struct btrfs_fs_info *fs_info, u64 bytes_used,
u64 type, u64 chunk_objectid, u64 chunk_offset,
u64 size);
int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
- struct btrfs_root *root);
+ struct btrfs_fs_info *fs_info);
int btrfs_update_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, u64 num,
int alloc, int mark_free);
@@ -2563,8 +2523,8 @@ u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset);
/* ctree.c */
int btrfs_comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2);
-int btrfs_del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
- struct btrfs_path *path, int level, int slot);
+int btrfs_del_ptr(struct btrfs_root *root, struct btrfs_path *path,
+ int level, int slot);
enum btrfs_tree_block_status
btrfs_check_node(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
struct extent_buffer *buf);
@@ -2573,7 +2533,7 @@ btrfs_check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
struct extent_buffer *buf);
void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
int level, int slot, u64 objectid);
-struct extent_buffer *read_node_slot(struct btrfs_root *root,
+struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
struct extent_buffer *parent, int slot);
int btrfs_previous_item(struct btrfs_root *root,
struct btrfs_path *path, u64 min_objectid,
@@ -2596,11 +2556,9 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *buf,
struct extent_buffer **cow_ret, u64 new_root_objectid);
-int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root
- *root, struct btrfs_path *path, u32 data_size);
-int btrfs_truncate_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *path,
+int btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path,
+ u32 data_size);
+int btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path,
u32 new_size, int from_end);
int btrfs_split_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
@@ -2734,7 +2692,7 @@ int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
struct btrfs_inode_ref *btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_path *path,
const char *name, int namelen, u64 ino, u64 parent_ino,
- u64 index, int ins_len);
+ int ins_len);
int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, const char *name, int name_len,
u64 ino, u64 parent_ino, u64 *index);
@@ -2797,4 +2755,7 @@ int btrfs_get_extent(struct btrfs_trans_handle *trans,
int btrfs_punch_hole(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 ino, u64 offset, u64 len);
+int btrfs_read_file(struct btrfs_root *root, u64 ino, u64 start, int len,
+ char *dest);
+
#endif
diff --git a/debian/changelog b/debian/changelog
index cb0b3db1..535d19ba 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+btrfs-progs (4.12-1) UNRELEASED; urgency=medium
+
+ * New upstream release.
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com> Mon, 31 Jul 2017 14:53:54 +0100
+
btrfs-progs (4.9.1-1) unstable; urgency=medium
[ Dimitri John Ledkov ]
diff --git a/dir-item.c b/dir-item.c
index 846fc292..e34f6935 100644
--- a/dir-item.c
+++ b/dir-item.c
@@ -46,7 +46,7 @@ static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
di = btrfs_match_dir_item_name(root, path, name, name_len);
if (di)
return ERR_PTR(-EEXIST);
- ret = btrfs_extend_item(trans, root, path, data_size);
+ ret = btrfs_extend_item(root, path, data_size);
WARN_ON(ret > 0);
}
if (ret < 0)
@@ -272,7 +272,7 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
start = btrfs_item_ptr_offset(leaf, path->slots[0]);
memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
item_len - (ptr + sub_item_len - start));
- btrfs_truncate_item(trans, root, path, item_len - sub_item_len, 1);
+ btrfs_truncate_item(root, path, item_len - sub_item_len, 1);
}
return ret;
}
diff --git a/disk-io.c b/disk-io.c
index 2a94d4fc..8cf800ea 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -56,7 +56,7 @@ static int check_tree_block(struct btrfs_fs_info *fs_info,
{
struct btrfs_fs_devices *fs_devices;
- u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
+ u32 nodesize = fs_info->nodesize;
int ret = BTRFS_BAD_FSID;
if (buf->start != btrfs_header_bytenr(buf))
@@ -118,7 +118,7 @@ static void print_tree_block_error(struct btrfs_fs_info *fs_info,
}
}
-u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
+u32 btrfs_csum_data(char *data, u32 seed, size_t len)
{
return crc32c(seed, data, len);
}
@@ -164,8 +164,8 @@ int verify_tree_block_csum_silent(struct extent_buffer *buf, u16 csum_size)
return __csum_tree_block_size(buf, csum_size, 1, 1);
}
-static int csum_tree_block_fs_info(struct btrfs_fs_info *fs_info,
- struct extent_buffer *buf, int verify)
+int csum_tree_block(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *buf, int verify)
{
u16 csum_size =
btrfs_super_csum_size(fs_info->super_copy);
@@ -174,16 +174,10 @@ static int csum_tree_block_fs_info(struct btrfs_fs_info *fs_info,
return csum_tree_block_size(buf, csum_size, verify);
}
-int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
- int verify)
-{
- return csum_tree_block_fs_info(root->fs_info, buf, verify);
-}
-
-struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
+struct extent_buffer *btrfs_find_tree_block(struct btrfs_fs_info *fs_info,
u64 bytenr, u32 blocksize)
{
- return find_extent_buffer(&root->fs_info->extent_cache,
+ return find_extent_buffer(&fs_info->extent_cache,
bytenr, blocksize);
}
@@ -193,21 +187,21 @@ struct extent_buffer* btrfs_find_create_tree_block(
return alloc_extent_buffer(&fs_info->extent_cache, bytenr, blocksize);
}
-void readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
- u64 parent_transid)
+void readahead_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
+ u32 blocksize, u64 parent_transid)
{
struct extent_buffer *eb;
u64 length;
struct btrfs_multi_bio *multi = NULL;
struct btrfs_device *device;
- eb = btrfs_find_tree_block(root, bytenr, blocksize);
+ eb = btrfs_find_tree_block(fs_info, bytenr, blocksize);
if (!(eb && btrfs_buffer_uptodate(eb, parent_transid)) &&
- !btrfs_map_block(&root->fs_info->mapping_tree, READ,
- bytenr, &length, &multi, 0, NULL)) {
+ !btrfs_map_block(fs_info, READ, bytenr, &length, &multi, 0,
+ NULL)) {
device = multi->stripes[0].dev;
device->total_ios++;
- blocksize = min(blocksize, (u32)(64 * 1024));
+ blocksize = min(blocksize, (u32)SZ_64K);
readahead(device->fd, multi->stripes[0].physical, blocksize);
}
@@ -262,9 +256,8 @@ int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirr
if (!info->on_restoring &&
eb->start != BTRFS_SUPER_INFO_OFFSET) {
- ret = btrfs_map_block(&info->mapping_tree, READ,
- eb->start + offset, &read_len, &multi,
- mirror, NULL);
+ ret = btrfs_map_block(info, READ, eb->start + offset,
+ &read_len, &multi, mirror, NULL);
if (ret) {
printk("Couldn't map the block %Lu\n", eb->start + offset);
kfree(multi);
@@ -306,15 +299,15 @@ int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirr
return 0;
}
-struct extent_buffer* read_tree_block_fs_info(
+struct extent_buffer* read_tree_block(
struct btrfs_fs_info *fs_info, u64 bytenr, u32 blocksize,
u64 parent_transid)
{
int ret;
struct extent_buffer *eb;
u64 best_transid = 0;
- u32 sectorsize = btrfs_super_sectorsize(fs_info->super_copy);
- u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
+ u32 sectorsize = fs_info->sectorsize;
+ u32 nodesize = fs_info->nodesize;
int mirror_num = 0;
int good_mirror = 0;
int num_copies;
@@ -346,7 +339,7 @@ struct extent_buffer* read_tree_block_fs_info(
while (1) {
ret = read_whole_eb(fs_info, eb, mirror_num);
- if (ret == 0 && csum_tree_block_fs_info(fs_info, eb, 1) == 0 &&
+ if (ret == 0 && csum_tree_block(fs_info, eb, 1) == 0 &&
check_tree_block(fs_info, eb) == 0 &&
verify_parent_transid(eb->tree, eb, parent_transid, ignore)
== 0) {
@@ -371,8 +364,7 @@ struct extent_buffer* read_tree_block_fs_info(
ret = -EIO;
break;
}
- num_copies = btrfs_num_copies(&fs_info->mapping_tree,
- eb->start, eb->len);
+ num_copies = btrfs_num_copies(fs_info, eb->start, eb->len);
if (num_copies == 1) {
ignore = 1;
continue;
@@ -392,18 +384,17 @@ struct extent_buffer* read_tree_block_fs_info(
return ERR_PTR(ret);
}
-int read_extent_data(struct btrfs_root *root, char *data,
- u64 logical, u64 *len, int mirror)
+int read_extent_data(struct btrfs_fs_info *fs_info, char *data, u64 logical,
+ u64 *len, int mirror)
{
u64 offset = 0;
struct btrfs_multi_bio *multi = NULL;
- struct btrfs_fs_info *info = root->fs_info;
struct btrfs_device *device;
int ret = 0;
u64 max_len = *len;
- ret = btrfs_map_block(&info->mapping_tree, READ, logical, len,
- &multi, mirror, NULL);
+ ret = btrfs_map_block(fs_info, READ, logical, len, &multi, mirror,
+ NULL);
if (ret) {
fprintf(stderr, "Couldn't map the block %llu\n",
logical + offset);
@@ -426,9 +417,7 @@ err:
return ret;
}
-int write_and_map_eb(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct extent_buffer *eb)
+int write_and_map_eb(struct btrfs_fs_info *fs_info, struct extent_buffer *eb)
{
int ret;
int dev_nr;
@@ -438,11 +427,11 @@ int write_and_map_eb(struct btrfs_trans_handle *trans,
dev_nr = 0;
length = eb->len;
- ret = btrfs_map_block(&root->fs_info->mapping_tree, WRITE,
- eb->start, &length, &multi, 0, &raid_map);
+ ret = btrfs_map_block(fs_info, WRITE, eb->start, &length,
+ &multi, 0, &raid_map);
if (raid_map) {
- ret = write_raid56_with_parity(root->fs_info, eb, multi,
+ ret = write_raid56_with_parity(fs_info, eb, multi,
length, raid_map);
BUG_ON(ret);
} else while (dev_nr < multi->num_stripes) {
@@ -460,12 +449,12 @@ int write_and_map_eb(struct btrfs_trans_handle *trans,
}
int write_tree_block(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+ struct btrfs_fs_info *fs_info,
struct extent_buffer *eb)
{
- if (check_tree_block(root->fs_info, eb)) {
- print_tree_block_error(root->fs_info, eb,
- check_tree_block(root->fs_info, eb));
+ if (check_tree_block(fs_info, eb)) {
+ print_tree_block_error(fs_info, eb,
+ check_tree_block(fs_info, eb));
BUG();
}
@@ -473,21 +462,16 @@ int write_tree_block(struct btrfs_trans_handle *trans,
BUG();
btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
- csum_tree_block(root, eb, 0);
+ csum_tree_block(fs_info, eb, 0);
- return write_and_map_eb(trans, root, eb);
+ return write_and_map_eb(fs_info, eb);
}
-void btrfs_setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
- u32 stripesize, struct btrfs_root *root,
- struct btrfs_fs_info *fs_info, u64 objectid)
+void btrfs_setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
+ u64 objectid)
{
root->node = NULL;
root->commit_root = NULL;
- root->sectorsize = sectorsize;
- root->nodesize = nodesize;
- root->leafsize = leafsize;
- root->stripesize = stripesize;
root->ref_cows = 0;
root->track_dirty = 0;
@@ -564,8 +548,9 @@ static int __commit_transaction(struct btrfs_trans_handle *trans,
{
u64 start;
u64 end;
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct extent_buffer *eb;
- struct extent_io_tree *tree = &root->fs_info->extent_cache;
+ struct extent_io_tree *tree = &fs_info->extent_cache;
int ret;
while(1) {
@@ -576,7 +561,7 @@ static int __commit_transaction(struct btrfs_trans_handle *trans,
while(start <= end) {
eb = find_first_extent_buffer(tree, start);
BUG_ON(!eb || eb->start != start);
- ret = write_tree_block(trans, root, eb);
+ ret = write_tree_block(trans, fs_info, eb);
BUG_ON(ret);
start += eb->len;
clear_extent_buffer_dirty(eb);
@@ -614,10 +599,10 @@ commit_tree:
BUG_ON(ret);
ret = __commit_transaction(trans, root);
BUG_ON(ret);
- write_ctree_super(trans, root);
+ write_ctree_super(trans, fs_info);
btrfs_finish_extent_commit(trans, fs_info->extent_root,
&fs_info->pinned_extents);
- btrfs_free_transaction(root, trans);
+ kfree(trans);
free_extent_buffer(root->commit_root);
root->commit_root = NULL;
fs_info->running_transaction = NULL;
@@ -633,18 +618,17 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
u32 blocksize;
u64 generation;
- btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
- tree_root->sectorsize, tree_root->stripesize,
- root, fs_info, objectid);
+ btrfs_setup_root(root, fs_info, objectid);
ret = btrfs_find_last_root(tree_root, objectid,
&root->root_item, &root->root_key);
if (ret)
return ret;
- blocksize = root->nodesize;
+ blocksize = fs_info->nodesize;
generation = btrfs_root_generation(&root->root_item);
- root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
- blocksize, generation);
+ root->node = read_tree_block(fs_info,
+ btrfs_root_bytenr(&root->root_item),
+ blocksize, generation);
if (!extent_buffer_uptodate(root->node))
return -EIO;
@@ -667,13 +651,12 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
return 0;
}
- blocksize = tree_root->nodesize;
+ blocksize = fs_info->nodesize;
- btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
- tree_root->sectorsize, tree_root->stripesize,
- log_root, fs_info, BTRFS_TREE_LOG_OBJECTID);
+ btrfs_setup_root(log_root, fs_info,
+ BTRFS_TREE_LOG_OBJECTID);
- log_root->node = read_tree_block(tree_root, blocknr,
+ log_root->node = read_tree_block(fs_info, blocknr,
blocksize,
btrfs_super_generation(disk_super) + 1);
@@ -733,9 +716,8 @@ struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
goto insert;
}
- btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
- tree_root->sectorsize, tree_root->stripesize,
- root, fs_info, location->objectid);
+ btrfs_setup_root(root, fs_info,
+ location->objectid);
path = btrfs_alloc_path();
if (!path) {
@@ -762,9 +744,10 @@ out:
return ERR_PTR(ret);
}
generation = btrfs_root_generation(&root->root_item);
- blocksize = root->nodesize;
- root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
- blocksize, generation);
+ blocksize = fs_info->nodesize;
+ root->node = read_tree_block(fs_info,
+ btrfs_root_bytenr(&root->root_item),
+ blocksize, generation);
if (!extent_buffer_uptodate(root->node)) {
free(root);
return ERR_PTR(-EIO);
@@ -817,7 +800,8 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
return fs_info->csum_root;
if (location->objectid == BTRFS_QUOTA_TREE_OBJECTID)
- return fs_info->quota_root;
+ return fs_info->quota_enabled ? fs_info->quota_root :
+ ERR_PTR(-ENOENT);
BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
location->offset != (u64)-1);
@@ -839,12 +823,14 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
{
+ if (fs_info->quota_root)
+ free(fs_info->quota_root);
+
free(fs_info->tree_root);
free(fs_info->extent_root);
free(fs_info->chunk_root);
free(fs_info->dev_root);
free(fs_info->csum_root);
- free(fs_info->quota_root);
free(fs_info->free_space_root);
free(fs_info->super_copy);
free(fs_info->log_root_tree);
@@ -997,23 +983,13 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
struct btrfs_super_block *sb = fs_info->super_copy;
struct btrfs_root *root;
struct btrfs_key key;
- u32 sectorsize;
- u32 nodesize;
- u32 leafsize;
- u32 stripesize;
u64 generation;
u32 blocksize;
int ret;
- nodesize = btrfs_super_nodesize(sb);
- leafsize = btrfs_super_leafsize(sb);
- sectorsize = btrfs_super_sectorsize(sb);
- stripesize = btrfs_super_stripesize(sb);
-
root = fs_info->tree_root;
- btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize,
- root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
- blocksize = root->nodesize;
+ btrfs_setup_root(root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
+ blocksize = fs_info->nodesize;
generation = btrfs_super_generation(sb);
if (!root_tree_bytenr && !(flags & OPEN_CTREE_BACKUP_ROOT)) {
@@ -1030,7 +1006,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
generation = btrfs_backup_tree_root_gen(backup);
}
- root->node = read_tree_block(root, root_tree_bytenr, blocksize,
+ root->node = read_tree_block(fs_info, root_tree_bytenr, blocksize,
generation);
if (!extent_buffer_uptodate(root->node)) {
fprintf(stderr, "Couldn't read tree root\n");
@@ -1059,8 +1035,12 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
ret = find_and_setup_root(root, fs_info, BTRFS_QUOTA_TREE_OBJECTID,
fs_info->quota_root);
- if (ret == 0)
+ if (ret) {
+ free(fs_info->quota_root);
+ fs_info->quota_root = NULL;
+ } else {
fs_info->quota_enabled = 1;
+ }
if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
ret = find_and_setup_root(root, fs_info, BTRFS_FREE_SPACE_TREE_OBJECTID,
@@ -1187,33 +1167,22 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info,
u64 chunk_root_bytenr)
{
struct btrfs_super_block *sb = fs_info->super_copy;
- u32 sectorsize;
- u32 nodesize;
- u32 leafsize;
- u32 blocksize;
- u32 stripesize;
u64 generation;
int ret;
- nodesize = btrfs_super_nodesize(sb);
- leafsize = btrfs_super_leafsize(sb);
- sectorsize = btrfs_super_sectorsize(sb);
- stripesize = btrfs_super_stripesize(sb);
-
- btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize,
- fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
+ btrfs_setup_root(fs_info->chunk_root, fs_info,
+ BTRFS_CHUNK_TREE_OBJECTID);
- ret = btrfs_read_sys_array(fs_info->chunk_root);
+ ret = btrfs_read_sys_array(fs_info);
if (ret)
return ret;
- blocksize = fs_info->chunk_root->nodesize;
generation = btrfs_super_chunk_root_generation(sb);
if (chunk_root_bytenr && !IS_ALIGNED(chunk_root_bytenr,
- btrfs_super_sectorsize(sb))) {
+ fs_info->sectorsize)) {
warning("chunk_root_bytenr %llu is unaligned to %u, ignore it",
- chunk_root_bytenr, btrfs_super_sectorsize(sb));
+ chunk_root_bytenr, fs_info->sectorsize);
chunk_root_bytenr = 0;
}
@@ -1222,9 +1191,10 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info,
else
generation = 0;
- fs_info->chunk_root->node = read_tree_block(fs_info->chunk_root,
+ fs_info->chunk_root->node = read_tree_block(fs_info,
chunk_root_bytenr,
- blocksize, generation);
+ fs_info->nodesize,
+ generation);
if (!extent_buffer_uptodate(fs_info->chunk_root->node)) {
if (fs_info->ignore_chunk_tree_error) {
warning("cannot read chunk root, continue anyway");
@@ -1237,7 +1207,7 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info,
}
if (!(btrfs_super_flags(sb) & BTRFS_SUPER_FLAG_METADUMP)) {
- ret = btrfs_read_chunk_tree(fs_info->chunk_root);
+ ret = btrfs_read_chunk_tree(fs_info);
if (ret) {
fprintf(stderr, "Couldn't read chunk tree\n");
return ret;
@@ -1328,6 +1298,9 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
}
memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
+ fs_info->sectorsize = btrfs_super_sectorsize(disk_super);
+ fs_info->nodesize = btrfs_super_nodesize(disk_super);
+ fs_info->stripesize = btrfs_super_stripesize(disk_super);
ret = btrfs_check_fs_compatibility(fs_info->super_copy, flags);
if (ret)
@@ -1463,7 +1436,7 @@ static int check_super(struct btrfs_super_block *sb, unsigned sbflags)
csum_size = btrfs_csum_sizes[csum_type];
crc = ~(u32)0;
- crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
+ crc = btrfs_csum_data((char *)sb + BTRFS_CSUM_SIZE, crc,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
btrfs_csum_final(crc, result);
@@ -1658,7 +1631,7 @@ int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
return transid > 0 ? 0 : -1;
}
-static int write_dev_supers(struct btrfs_root *root,
+static int write_dev_supers(struct btrfs_fs_info *fs_info,
struct btrfs_super_block *sb,
struct btrfs_device *device)
{
@@ -1666,10 +1639,10 @@ static int write_dev_supers(struct btrfs_root *root,
u32 crc;
int i, ret;
- if (root->fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) {
- btrfs_set_super_bytenr(sb, root->fs_info->super_bytenr);
+ if (fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) {
+ btrfs_set_super_bytenr(sb, fs_info->super_bytenr);
crc = ~(u32)0;
- crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
+ crc = btrfs_csum_data((char *)sb + BTRFS_CSUM_SIZE, crc,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
btrfs_csum_final(crc, &sb->csum[0]);
@@ -1677,9 +1650,9 @@ static int write_dev_supers(struct btrfs_root *root,
* super_copy is BTRFS_SUPER_INFO_SIZE bytes and is
* zero filled, we can use it directly
*/
- ret = pwrite64(device->fd, root->fs_info->super_copy,
+ ret = pwrite64(device->fd, fs_info->super_copy,
BTRFS_SUPER_INFO_SIZE,
- root->fs_info->super_bytenr);
+ fs_info->super_bytenr);
if (ret != BTRFS_SUPER_INFO_SIZE)
goto write_err;
return 0;
@@ -1693,7 +1666,7 @@ static int write_dev_supers(struct btrfs_root *root,
btrfs_set_super_bytenr(sb, bytenr);
crc = ~(u32)0;
- crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
+ crc = btrfs_csum_data((char *)sb + BTRFS_CSUM_SIZE, crc,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
btrfs_csum_final(crc, &sb->csum[0]);
@@ -1701,7 +1674,7 @@ static int write_dev_supers(struct btrfs_root *root,
* super_copy is BTRFS_SUPER_INFO_SIZE bytes and is
* zero filled, we can use it directly
*/
- ret = pwrite64(device->fd, root->fs_info->super_copy,
+ ret = pwrite64(device->fd, fs_info->super_copy,
BTRFS_SUPER_INFO_SIZE, bytenr);
if (ret != BTRFS_SUPER_INFO_SIZE)
goto write_err;
@@ -1718,17 +1691,17 @@ write_err:
return ret;
}
-int write_all_supers(struct btrfs_root *root)
+int write_all_supers(struct btrfs_fs_info *fs_info)
{
struct list_head *cur;
- struct list_head *head = &root->fs_info->fs_devices->devices;
+ struct list_head *head = &fs_info->fs_devices->devices;
struct btrfs_device *dev;
struct btrfs_super_block *sb;
struct btrfs_dev_item *dev_item;
int ret;
u64 flags;
- sb = root->fs_info->super_copy;
+ sb = fs_info->super_copy;
dev_item = &sb->dev_item;
list_for_each(cur, head) {
dev = list_entry(cur, struct btrfs_device, dev_list);
@@ -1749,36 +1722,36 @@ int write_all_supers(struct btrfs_root *root)
flags = btrfs_super_flags(sb);
btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN);
- ret = write_dev_supers(root, sb, dev);
+ ret = write_dev_supers(fs_info, sb, dev);
BUG_ON(ret);
}
return 0;
}
int write_ctree_super(struct btrfs_trans_handle *trans,
- struct btrfs_root *root)
+ struct btrfs_fs_info *fs_info)
{
int ret;
- struct btrfs_root *tree_root = root->fs_info->tree_root;
- struct btrfs_root *chunk_root = root->fs_info->chunk_root;
+ struct btrfs_root *tree_root = fs_info->tree_root;
+ struct btrfs_root *chunk_root = fs_info->chunk_root;
- if (root->fs_info->readonly)
+ if (fs_info->readonly)
return 0;
- btrfs_set_super_generation(root->fs_info->super_copy,
+ btrfs_set_super_generation(fs_info->super_copy,
trans->transid);
- btrfs_set_super_root(root->fs_info->super_copy,
+ btrfs_set_super_root(fs_info->super_copy,
tree_root->node->start);
- btrfs_set_super_root_level(root->fs_info->super_copy,
+ btrfs_set_super_root_level(fs_info->super_copy,
btrfs_header_level(tree_root->node));
- btrfs_set_super_chunk_root(root->fs_info->super_copy,
+ btrfs_set_super_chunk_root(fs_info->super_copy,
chunk_root->node->start);
- btrfs_set_super_chunk_root_level(root->fs_info->super_copy,
+ btrfs_set_super_chunk_root_level(fs_info->super_copy,
btrfs_header_level(chunk_root->node));
- btrfs_set_super_chunk_root_generation(root->fs_info->super_copy,
+ btrfs_set_super_chunk_root_generation(fs_info->super_copy,
btrfs_header_generation(chunk_root->node));
- ret = write_all_supers(root);
+ ret = write_all_supers(fs_info);
if (ret)
fprintf(stderr, "failed to write new super block err %d\n", ret);
return ret;
@@ -1800,14 +1773,14 @@ int close_ctree_fs_info(struct btrfs_fs_info *fs_info)
BUG_ON(ret);
ret = __commit_transaction(trans, root);
BUG_ON(ret);
- write_ctree_super(trans, root);
- btrfs_free_transaction(root, trans);
+ write_ctree_super(trans, fs_info);
+ kfree(trans);
}
if (fs_info->finalize_on_close) {
btrfs_set_super_magic(fs_info->super_copy, BTRFS_MAGIC);
root->fs_info->finalize_on_close = 0;
- ret = write_all_supers(root);
+ ret = write_all_supers(fs_info);
if (ret)
fprintf(stderr,
"failed to write new super block err %d\n", ret);
@@ -1817,10 +1790,10 @@ int close_ctree_fs_info(struct btrfs_fs_info *fs_info)
free_fs_roots_tree(&fs_info->fs_root_tree);
btrfs_release_all_roots(fs_info);
- btrfs_close_devices(fs_info->fs_devices);
+ ret = btrfs_close_devices(fs_info->fs_devices);
btrfs_cleanup_all_caches(fs_info);
btrfs_free_fs_info(fs_info);
- return 0;
+ return ret;
}
int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
@@ -1829,12 +1802,6 @@ int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
return clear_extent_buffer_dirty(eb);
}
-int wait_on_tree_block_writeback(struct btrfs_root *root,
- struct extent_buffer *eb)
-{
- return 0;
-}
-
void btrfs_mark_buffer_dirty(struct extent_buffer *eb)
{
set_extent_buffer_dirty(eb);
diff --git a/disk-io.h b/disk-io.h
index 1c8387e7..dfe4cf01 100644
--- a/disk-io.h
+++ b/disk-io.h
@@ -21,8 +21,9 @@
#include "kerncompat.h"
#include "ctree.h"
+#include "sizes.h"
-#define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
+#define BTRFS_SUPER_INFO_OFFSET SZ_64K
#define BTRFS_SUPER_INFO_SIZE 4096
#define BTRFS_SUPER_MIRROR_MAX 3
@@ -97,38 +98,36 @@ enum btrfs_read_sb_flags {
SBREAD_PARTIAL = (1 << 1),
};
+/*
+ * Use macro to define mirror super block position,
+ * so we can use it in static array initialization
+ */
+#define BTRFS_SB_MIRROR_OFFSET(mirror) ((u64)(SZ_16K) << \
+ (BTRFS_SUPER_MIRROR_SHIFT * (mirror)))
+
static inline u64 btrfs_sb_offset(int mirror)
{
- u64 start = 16 * 1024;
if (mirror)
- return start << (BTRFS_SUPER_MIRROR_SHIFT * mirror);
+ return BTRFS_SB_MIRROR_OFFSET(mirror);
return BTRFS_SUPER_INFO_OFFSET;
}
struct btrfs_device;
int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror);
-struct extent_buffer* read_tree_block_fs_info(
+struct extent_buffer* read_tree_block(
struct btrfs_fs_info *fs_info, u64 bytenr, u32 blocksize,
u64 parent_transid);
-static inline struct extent_buffer* read_tree_block(
- struct btrfs_root *root, u64 bytenr, u32 blocksize,
- u64 parent_transid)
-{
- return read_tree_block_fs_info(root->fs_info, bytenr, blocksize,
- parent_transid);
-}
-int read_extent_data(struct btrfs_root *root, char *data, u64 logical,
+int read_extent_data(struct btrfs_fs_info *fs_info, char *data, u64 logical,
u64 *len, int mirror);
-void readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
- u64 parent_transid);
+void readahead_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
+ u32 blocksize, u64 parent_transid);
struct extent_buffer* btrfs_find_create_tree_block(
struct btrfs_fs_info *fs_info, u64 bytenr, u32 blocksize);
-void btrfs_setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
- u32 stripesize, struct btrfs_root *root,
- struct btrfs_fs_info *fs_info, u64 objectid);
+void btrfs_setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
+ u64 objectid);
int clean_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *buf);
@@ -162,14 +161,14 @@ static inline int close_ctree(struct btrfs_root *root)
return close_ctree_fs_info(root->fs_info);
}
-int write_all_supers(struct btrfs_root *root);
+int write_all_supers(struct btrfs_fs_info *fs_info);
int write_ctree_super(struct btrfs_trans_handle *trans,
- struct btrfs_root *root);
+ struct btrfs_fs_info *fs_info);
int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
unsigned sbflags);
int btrfs_map_bh_to_logical(struct btrfs_root *root, struct extent_buffer *bh,
u64 logical);
-struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
+struct extent_buffer *btrfs_find_tree_block(struct btrfs_fs_info *fs_info,
u64 bytenr, u32 blocksize);
struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
struct btrfs_key *location);
@@ -179,9 +178,7 @@ int btrfs_free_fs_root(struct btrfs_root *root);
void btrfs_mark_buffer_dirty(struct extent_buffer *buf);
int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid);
int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
-int wait_on_tree_block_writeback(struct btrfs_root *root,
- struct extent_buffer *buf);
-u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len);
+u32 btrfs_csum_data(char *data, u32 seed, size_t len);
void btrfs_csum_final(u32 crc, u8 *result);
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
@@ -192,13 +189,8 @@ int csum_tree_block_size(struct extent_buffer *buf, u16 csum_sectorsize,
int verify_tree_block_csum_silent(struct extent_buffer *buf, u16 csum_size);
int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid);
int write_tree_block(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct extent_buffer *eb);
-int write_and_map_eb(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+ struct btrfs_fs_info *fs_info,
struct extent_buffer *eb);
-
-/* 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);
+int write_and_map_eb(struct btrfs_fs_info *fs_info, struct extent_buffer *eb);
#endif
diff --git a/extent-tree.c b/extent-tree.c
index b2847ff9..46b8a05c 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -69,18 +69,18 @@ static int remove_sb_from_cache(struct btrfs_root *root,
u64 *logical;
int stripe_len;
int i, nr, ret;
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct extent_io_tree *free_space_cache;
- free_space_cache = &root->fs_info->free_space_cache;
+ free_space_cache = &fs_info->free_space_cache;
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
- ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
- cache->key.objectid, bytenr, 0,
+ ret = btrfs_rmap_block(fs_info, cache->key.objectid, bytenr, 0,
&logical, &nr, &stripe_len);
BUG_ON(ret);
while (nr--) {
clear_extent_dirty(free_space_cache, logical[nr],
- logical[nr] + stripe_len - 1, GFP_NOFS);
+ logical[nr] + stripe_len - 1);
}
kfree(logical);
}
@@ -149,11 +149,10 @@ static int cache_block_group(struct btrfs_root *root,
if (key.objectid > last) {
hole_size = key.objectid - last;
set_extent_dirty(free_space_cache, last,
- last + hole_size - 1,
- GFP_NOFS);
+ last + hole_size - 1);
}
if (key.type == BTRFS_METADATA_ITEM_KEY)
- last = key.objectid + root->nodesize;
+ last = key.objectid + root->fs_info->nodesize;
else
last = key.objectid + key.offset;
}
@@ -165,8 +164,7 @@ next:
block_group->key.offset > last) {
hole_size = block_group->key.objectid +
block_group->key.offset - last;
- set_extent_dirty(free_space_cache, last,
- last + hole_size - 1, GFP_NOFS);
+ set_extent_dirty(free_space_cache, last, last + hole_size - 1);
}
remove_sb_from_cache(root, block_group);
block_group->cached = 1;
@@ -574,7 +572,7 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
return ret;
BUG_ON(ret);
- ret = btrfs_extend_item(trans, root, path, new_size);
+ ret = btrfs_extend_item(root, path, new_size);
BUG_ON(ret);
leaf = path->nodes[0];
@@ -857,8 +855,7 @@ static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans,
return ret;
}
-static noinline u32 extent_data_ref_count(struct btrfs_root *root,
- struct btrfs_path *path,
+static noinline u32 extent_data_ref_count(struct btrfs_path *path,
struct btrfs_extent_inline_ref *iref)
{
struct btrfs_key key;
@@ -1164,8 +1161,7 @@ out:
return err;
}
-static int setup_inline_extent_backref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int setup_inline_extent_backref(struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_extent_inline_ref *iref,
u64 parent, u64 root_objectid,
@@ -1188,7 +1184,7 @@ static int setup_inline_extent_backref(struct btrfs_trans_handle *trans,
type = extent_ref_type(parent, owner);
size = btrfs_extent_inline_ref_size(type);
- ret = btrfs_extend_item(trans, root, path, size);
+ ret = btrfs_extend_item(root, path, size);
BUG_ON(ret);
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
@@ -1308,7 +1304,7 @@ static int update_inline_extent_backref(struct btrfs_trans_handle *trans,
memmove_extent_buffer(leaf, ptr, ptr + size,
end - ptr - size);
item_size -= size;
- ret = btrfs_truncate_item(trans, root, path, item_size, 1);
+ ret = btrfs_truncate_item(root, path, item_size, 1);
BUG_ON(ret);
}
btrfs_mark_buffer_dirty(leaf);
@@ -1333,7 +1329,7 @@ static int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
ret = update_inline_extent_backref(trans, root, path, iref,
refs_to_add);
} else if (ret == -ENOENT) {
- ret = setup_inline_extent_backref(trans, root, path, iref,
+ ret = setup_inline_extent_backref(root, path, iref,
parent, root_objectid,
owner, offset, refs_to_add);
}
@@ -1456,7 +1452,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
if (metadata &&
!btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) {
- offset = root->nodesize;
+ offset = root->fs_info->nodesize;
metadata = 0;
}
@@ -1491,14 +1487,14 @@ again:
path->slots[0]);
if (key.objectid == bytenr &&
key.type == BTRFS_EXTENT_ITEM_KEY &&
- key.offset == root->nodesize)
+ key.offset == root->fs_info->nodesize)
ret = 0;
}
if (ret) {
btrfs_release_path(path);
key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = root->nodesize;
+ key.offset = root->fs_info->nodesize;
metadata = 0;
goto again;
}
@@ -1562,7 +1558,7 @@ int btrfs_set_block_flags(struct btrfs_trans_handle *trans,
key.offset = level;
key.type = BTRFS_METADATA_ITEM_KEY;
} else {
- key.offset = root->nodesize;
+ key.offset = root->fs_info->nodesize;
key.type = BTRFS_EXTENT_ITEM_KEY;
}
@@ -1579,13 +1575,13 @@ again:
btrfs_item_key_to_cpu(path->nodes[0], &key,
path->slots[0]);
if (key.objectid == bytenr &&
- key.offset == root->nodesize &&
+ key.offset == root->fs_info->nodesize &&
key.type == BTRFS_EXTENT_ITEM_KEY)
ret = 0;
}
if (ret) {
btrfs_release_path(path);
- key.offset = root->nodesize;
+ key.offset = root->fs_info->nodesize;
key.type = BTRFS_EXTENT_ITEM_KEY;
goto again;
}
@@ -1683,7 +1679,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
}
} else {
bytenr = btrfs_node_blockptr(buf, i);
- num_bytes = root->nodesize;
+ num_bytes = root->fs_info->nodesize;
ret = process_func(trans, root, bytenr, num_bytes,
parent, ref_root, level - 1, 0);
if (ret) {
@@ -1774,7 +1770,7 @@ int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
BUG_ON(ret);
clear_extent_bits(block_group_cache, start, end,
- BLOCK_GROUP_DIRTY, GFP_NOFS);
+ BLOCK_GROUP_DIRTY);
cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
ret = write_one_cache_group(trans, root, path, cache);
@@ -1876,7 +1872,7 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
}
static int do_chunk_alloc(struct btrfs_trans_handle *trans,
- struct btrfs_root *extent_root, u64 alloc_bytes,
+ struct btrfs_fs_info *fs_info, u64 alloc_bytes,
u64 flags)
{
struct btrfs_space_info *space_info;
@@ -1885,10 +1881,9 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
u64 num_bytes;
int ret;
- space_info = __find_space_info(extent_root->fs_info, flags);
+ space_info = __find_space_info(fs_info, flags);
if (!space_info) {
- ret = update_space_info(extent_root->fs_info, flags,
- 0, 0, &space_info);
+ ret = update_space_info(fs_info, flags, 0, 0, &space_info);
BUG_ON(ret);
}
BUG_ON(!space_info);
@@ -1904,14 +1899,14 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
/*
* Avoid allocating given chunk type
*/
- if (extent_root->fs_info->avoid_meta_chunk_alloc &&
+ if (fs_info->avoid_meta_chunk_alloc &&
(flags & BTRFS_BLOCK_GROUP_METADATA))
return 0;
- if (extent_root->fs_info->avoid_sys_chunk_alloc &&
+ if (fs_info->avoid_sys_chunk_alloc &&
(flags & BTRFS_BLOCK_GROUP_SYSTEM))
return 0;
- ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes,
+ ret = btrfs_alloc_chunk(trans, fs_info, &start, &num_bytes,
space_info->flags);
if (ret == -ENOSPC) {
space_info->full = 1;
@@ -1920,7 +1915,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
BUG_ON(ret);
- ret = btrfs_make_block_group(trans, extent_root, 0, space_info->flags,
+ ret = btrfs_make_block_group(trans, fs_info, 0, space_info->flags,
BTRFS_FIRST_CHUNK_TREE_OBJECTID, start, num_bytes);
BUG_ON(ret);
return 0;
@@ -1965,7 +1960,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
start = cache->key.objectid;
end = start + cache->key.offset - 1;
set_extent_bits(&info->block_group_cache, start, end,
- BLOCK_GROUP_DIRTY, GFP_NOFS);
+ BLOCK_GROUP_DIRTY);
old_val = btrfs_block_group_used(&cache->item);
num_bytes = min(total, cache->key.offset - byte_in_group);
@@ -1978,8 +1973,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
cache->space_info->bytes_used -= num_bytes;
if (mark_free) {
set_extent_dirty(&info->free_space_cache,
- bytenr, bytenr + num_bytes - 1,
- GFP_NOFS);
+ bytenr, bytenr + num_bytes - 1);
}
}
btrfs_set_block_group_used(&cache->item, old_val);
@@ -1998,15 +1992,15 @@ static int update_pinned_extents(struct btrfs_root *root,
if (pin) {
set_extent_dirty(&fs_info->pinned_extents,
- bytenr, bytenr + num - 1, GFP_NOFS);
+ bytenr, bytenr + num - 1);
} else {
clear_extent_dirty(&fs_info->pinned_extents,
- bytenr, bytenr + num - 1, GFP_NOFS);
+ bytenr, bytenr + num - 1);
}
while (num > 0) {
cache = btrfs_lookup_block_group(fs_info, bytenr);
if (!cache) {
- len = min((u64)root->sectorsize, num);
+ len = min((u64)fs_info->sectorsize, num);
goto next;
}
WARN_ON(!cache);
@@ -2044,8 +2038,8 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
if (ret)
break;
update_pinned_extents(root, start, end + 1 - start, 0);
- clear_extent_dirty(unpin, start, end, GFP_NOFS);
- set_extent_dirty(free_space_cache, start, end, GFP_NOFS);
+ clear_extent_dirty(unpin, start, end);
+ set_extent_dirty(free_space_cache, start, end);
}
return 0;
}
@@ -2108,8 +2102,7 @@ static int finish_current_insert(struct btrfs_trans_handle *trans,
BUG_ON(1);
}
- clear_extent_bits(&info->extent_ins, start, end, EXTENT_LOCKED,
- GFP_NOFS);
+ clear_extent_bits(&info->extent_ins, start, end, EXTENT_LOCKED);
kfree(extent_op);
}
return 0;
@@ -2125,7 +2118,7 @@ static int pin_down_bytes(struct btrfs_trans_handle *trans,
if (is_data)
goto pinit;
- buf = btrfs_find_tree_block(root, bytenr, num_bytes);
+ buf = btrfs_find_tree_block(root->fs_info, bytenr, num_bytes);
if (!buf)
goto pinit;
@@ -2366,7 +2359,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
if (found_extent) {
BUG_ON(is_data && refs_to_drop !=
- extent_data_ref_count(root, path, iref));
+ extent_data_ref_count(path, iref));
if (iref) {
BUG_ON(path->slots[0] != extent_slot);
} else {
@@ -2431,8 +2424,7 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct
BUG_ON(ret);
extent_op = (struct pending_extent_op *)(unsigned long)priv;
- clear_extent_bits(pending_del, start, end, EXTENT_LOCKED,
- GFP_NOFS);
+ clear_extent_bits(pending_del, start, end, EXTENT_LOCKED);
if (!test_range_bit(extent_ins, start, end,
EXTENT_LOCKED, 0)) {
@@ -2449,7 +2441,7 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct
(unsigned long)priv;
clear_extent_bits(extent_ins, start, end,
- EXTENT_LOCKED, GFP_NOFS);
+ EXTENT_LOCKED);
if (extent_op->type == PENDING_BACKREF_UPDATE)
BUG_ON(1);
@@ -2486,7 +2478,7 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans,
int pending_ret;
int ret;
- WARN_ON(num_bytes < root->sectorsize);
+ WARN_ON(num_bytes < root->fs_info->sectorsize);
if (root == extent_root) {
struct pending_extent_op *extent_op;
@@ -2500,7 +2492,7 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans,
set_extent_bits(&root->fs_info->pending_del,
bytenr, bytenr + num_bytes - 1,
- EXTENT_LOCKED, GFP_NOFS);
+ EXTENT_LOCKED);
set_state_private(&root->fs_info->pending_del,
bytenr, (unsigned long)extent_op);
return 0;
@@ -2513,9 +2505,7 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans,
static u64 stripe_align(struct btrfs_root *root, u64 val)
{
- u64 mask = ((u64)root->stripesize - 1);
- u64 ret = (val + mask) & ~mask;
- return ret;
+ return round_up(val, (u64)root->fs_info->stripesize);
}
/*
@@ -2543,7 +2533,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
int full_scan = 0;
int wrapped = 0;
- WARN_ON(num_bytes < root->sectorsize);
+ WARN_ON(num_bytes < info->sectorsize);
ins->type = BTRFS_EXTENT_ITEM_KEY;
search_start = stripe_align(root, search_start);
@@ -2621,8 +2611,9 @@ check_failed:
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;
+ search_start = round_up(
+ bg_offset + num_bytes, BTRFS_STRIPE_LEN) +
+ bg_cache->key.objectid;
goto new_group;
}
no_bg_cache:
@@ -2685,25 +2676,24 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
if (root->ref_cows) {
if (!(data & BTRFS_BLOCK_GROUP_METADATA)) {
- ret = do_chunk_alloc(trans, root->fs_info->extent_root,
+ ret = do_chunk_alloc(trans, info,
num_bytes,
BTRFS_BLOCK_GROUP_METADATA);
BUG_ON(ret);
}
- ret = do_chunk_alloc(trans, root->fs_info->extent_root,
- num_bytes + 2 * 1024 * 1024, data);
+ ret = do_chunk_alloc(trans, info,
+ num_bytes + SZ_2M, data);
BUG_ON(ret);
}
- WARN_ON(num_bytes < root->sectorsize);
+ WARN_ON(num_bytes < info->sectorsize);
ret = find_free_extent(trans, root, num_bytes, empty_size,
search_start, search_end, hint_byte, ins,
trans->alloc_exclude_start,
trans->alloc_exclude_nr, data);
BUG_ON(ret);
- clear_extent_dirty(&root->fs_info->free_space_cache,
- ins->objectid, ins->objectid + ins->offset - 1,
- GFP_NOFS);
+ clear_extent_dirty(&info->free_space_cache,
+ ins->objectid, ins->objectid + ins->offset - 1);
return ret;
}
@@ -2757,7 +2747,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(leaf);
btrfs_free_path(path);
- ret = update_block_group(trans, root, ins->objectid, root->nodesize,
+ ret = update_block_group(trans, root, ins->objectid, fs_info->nodesize,
1, 0);
return ret;
}
@@ -2789,7 +2779,7 @@ static int alloc_tree_block(struct btrfs_trans_handle *trans,
set_extent_bits(&root->fs_info->extent_ins, ins->objectid,
ins->objectid + ins->offset - 1,
- EXTENT_LOCKED, GFP_NOFS);
+ EXTENT_LOCKED);
set_state_private(&root->fs_info->extent_ins,
ins->objectid, (unsigned long)extent_op);
} else {
@@ -2908,8 +2898,8 @@ static void noinline reada_walk_down(struct btrfs_root *root,
for (i = slot; i < nritems && skipped < 32; i++) {
bytenr = btrfs_node_blockptr(node, i);
- if (last && ((bytenr > last && bytenr - last > 32 * 1024) ||
- (last > bytenr && last - bytenr > 32 * 1024))) {
+ if (last && ((bytenr > last && bytenr - last > SZ_32K) ||
+ (last > bytenr && last - bytenr > SZ_32K))) {
skipped++;
continue;
}
@@ -3123,15 +3113,14 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
kfree(cache);
}
clear_extent_bits(&info->block_group_cache, start,
- end, (unsigned int)-1, GFP_NOFS);
+ end, (unsigned int)-1);
}
while(1) {
ret = find_first_extent_bit(&info->free_space_cache, 0,
&start, &end, EXTENT_DIRTY);
if (ret)
break;
- clear_extent_dirty(&info->free_space_cache, start,
- end, GFP_NOFS);
+ clear_extent_dirty(&info->free_space_cache, start, end);
}
while (!list_empty(&info->space_info)) {
@@ -3192,7 +3181,7 @@ static void account_super_bytes(struct btrfs_fs_info *fs_info,
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
- ret = btrfs_rmap_block(&fs_info->mapping_tree,
+ ret = btrfs_rmap_block(fs_info,
cache->key.objectid, bytenr,
0, &logical, &nr, &stripe_len);
if (ret)
@@ -3259,6 +3248,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
}
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+
cache = kzalloc(sizeof(*cache), GFP_NOFS);
if (!cache) {
ret = -ENOMEM;
@@ -3275,6 +3265,17 @@ int btrfs_read_block_groups(struct btrfs_root *root)
if (found_key.offset == 0)
key.objectid++;
btrfs_release_path(path);
+
+ /*
+ * Skip 0 sized block group, don't insert them into block
+ * group cache tree, as its length is 0, it won't get
+ * freed at close_ctree() time.
+ */
+ if (found_key.offset == 0) {
+ free(cache);
+ continue;
+ }
+
cache->flags = btrfs_block_group_flags(&cache->item);
bit = 0;
if (cache->flags & BTRFS_BLOCK_GROUP_DATA) {
@@ -3285,7 +3286,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
bit = BLOCK_GROUP_METADATA;
}
set_avail_alloc_bits(info, cache->flags);
- if (btrfs_chunk_readonly(root, cache->key.objectid))
+ if (btrfs_chunk_readonly(info, cache->key.objectid))
cache->ro = 1;
account_super_bytes(info, cache);
@@ -3299,7 +3300,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
/* use EXTENT_LOCKED to prevent merging */
set_extent_bits(block_group_cache, found_key.objectid,
found_key.objectid + found_key.offset - 1,
- bit | EXTENT_LOCKED, GFP_NOFS);
+ bit | EXTENT_LOCKED);
set_state_private(block_group_cache, found_key.objectid,
(unsigned long)cache);
}
@@ -3339,7 +3340,7 @@ btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
bit = block_group_state_bits(type);
ret = set_extent_bits(block_group_cache, chunk_offset,
chunk_offset + size - 1,
- bit | EXTENT_LOCKED, GFP_NOFS);
+ bit | EXTENT_LOCKED);
BUG_ON(ret);
ret = set_state_private(block_group_cache, chunk_offset,
@@ -3351,17 +3352,16 @@ btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
}
int btrfs_make_block_group(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 bytes_used,
+ struct btrfs_fs_info *fs_info, u64 bytes_used,
u64 type, u64 chunk_objectid, u64 chunk_offset,
u64 size)
{
int ret;
- struct btrfs_root *extent_root;
+ struct btrfs_root *extent_root = fs_info->extent_root;
struct btrfs_block_group_cache *cache;
- cache = btrfs_add_block_group(root->fs_info, bytes_used, type,
+ cache = btrfs_add_block_group(fs_info, bytes_used, type,
chunk_objectid, chunk_offset, size);
- extent_root = root->fs_info->extent_root;
ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item,
sizeof(cache->item));
BUG_ON(ret);
@@ -3382,7 +3382,7 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
* before doing any block allocation.
*/
int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
- struct btrfs_root *root)
+ struct btrfs_fs_info *fs_info)
{
u64 total_bytes;
u64 cur_start;
@@ -3394,15 +3394,14 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
u64 chunk_objectid;
int ret;
int bit;
- struct btrfs_root *extent_root;
+ struct btrfs_root *extent_root = fs_info->extent_root;
struct btrfs_block_group_cache *cache;
struct extent_io_tree *block_group_cache;
- extent_root = root->fs_info->extent_root;
- block_group_cache = &root->fs_info->block_group_cache;
+ block_group_cache = &fs_info->block_group_cache;
chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
- total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy);
- group_align = 64 * root->sectorsize;
+ total_bytes = btrfs_super_total_bytes(fs_info->super_copy);
+ group_align = 64 * fs_info->sectorsize;
cur_start = 0;
while (cur_start < total_bytes) {
@@ -3413,19 +3412,18 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
group_type = BTRFS_BLOCK_GROUP_SYSTEM;
group_size /= 4;
group_size &= ~(group_align - 1);
- group_size = max_t(u64, group_size, 8 * 1024 * 1024);
- group_size = min_t(u64, group_size, 32 * 1024 * 1024);
+ group_size = max_t(u64, group_size, SZ_8M);
+ group_size = min_t(u64, group_size, SZ_32M);
} else {
group_size &= ~(group_align - 1);
if (total_data >= total_metadata * 2) {
group_type = BTRFS_BLOCK_GROUP_METADATA;
- group_size = min_t(u64, group_size,
- 1ULL * 1024 * 1024 * 1024);
+ group_size = min_t(u64, group_size, SZ_1G);
total_metadata += group_size;
} else {
group_type = BTRFS_BLOCK_GROUP_DATA;
group_size = min_t(u64, group_size,
- 5ULL * 1024 * 1024 * 1024);
+ 5ULL * SZ_1G);
total_data += group_size;
}
if ((total_bytes - cur_start) * 4 < group_size * 5)
@@ -3446,14 +3444,14 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
cache->flags = group_type;
- ret = update_space_info(root->fs_info, group_type, group_size,
+ ret = update_space_info(fs_info, group_type, group_size,
0, &cache->space_info);
BUG_ON(ret);
- set_avail_alloc_bits(extent_root->fs_info, group_type);
+ set_avail_alloc_bits(fs_info, group_type);
set_extent_bits(block_group_cache, cur_start,
cur_start + group_size - 1,
- bit | EXTENT_LOCKED, GFP_NOFS);
+ bit | EXTENT_LOCKED);
set_state_private(block_group_cache, cur_start,
(unsigned long)cache);
cur_start += group_size;
@@ -3461,7 +3459,7 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
/* then insert all the items */
cur_start = 0;
while(cur_start < total_bytes) {
- cache = btrfs_lookup_block_group(root->fs_info, cur_start);
+ cache = btrfs_lookup_block_group(fs_info, cur_start);
BUG_ON(!cache);
ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item,
@@ -3642,7 +3640,7 @@ out:
static int free_chunk_item(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info,
- u64 bytenr, u64 len)
+ u64 bytenr)
{
struct btrfs_path *path;
struct btrfs_key key;
@@ -3729,7 +3727,7 @@ static int free_block_group_cache(struct btrfs_trans_handle *trans,
kfree(cache->free_space_ctl);
}
clear_extent_bits(&fs_info->block_group_cache, bytenr, bytenr + len,
- (unsigned int)-1, GFP_NOFS);
+ (unsigned int)-1);
ret = free_space_info(fs_info, flags, len, 0, NULL);
if (ret < 0)
goto out;
@@ -3819,7 +3817,7 @@ int btrfs_free_block_group(struct btrfs_trans_handle *trans,
btrfs_unpin_extent(fs_info, bytenr, len);
goto out;
}
- ret = free_chunk_item(trans, fs_info, bytenr, len);
+ ret = free_chunk_item(trans, fs_info, bytenr);
if (ret < 0) {
fprintf(stderr,
"failed to free chunk for [%llu,%llu)\n",
@@ -3875,7 +3873,7 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
set_extent_bits(&root->fs_info->block_group_cache,
cache->key.objectid,
cache->key.objectid + cache->key.offset -1,
- BLOCK_GROUP_DIRTY, GFP_NOFS);
+ BLOCK_GROUP_DIRTY);
}
btrfs_init_path(&path);
@@ -3905,9 +3903,9 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
key.objectid, key.offset, 1, 0);
BUG_ON(ret);
} else if (key.type == BTRFS_METADATA_ITEM_KEY) {
- bytes_used += root->nodesize;
+ bytes_used += fs_info->nodesize;
ret = btrfs_update_block_group(trans, root,
- key.objectid, root->nodesize, 1, 0);
+ key.objectid, fs_info->nodesize, 1, 0);
BUG_ON(ret);
}
path.slots[0]++;
@@ -3929,7 +3927,7 @@ static void __get_extent_size(struct btrfs_root *root, struct btrfs_path *path,
if (key.type == BTRFS_EXTENT_ITEM_KEY)
*len = key.offset;
else
- *len = root->nodesize;
+ *len = root->fs_info->nodesize;
}
/*
@@ -4145,7 +4143,7 @@ static int add_excluded_extent(struct btrfs_root *root,
{
u64 end = start + num_bytes - 1;
set_extent_bits(&root->fs_info->pinned_extents,
- start, end, EXTENT_UPTODATE, GFP_NOFS);
+ start, end, EXTENT_UPTODATE);
return 0;
}
@@ -4158,7 +4156,7 @@ void free_excluded_extents(struct btrfs_root *root,
end = start + cache->key.offset - 1;
clear_extent_bits(&root->fs_info->pinned_extents,
- start, end, EXTENT_UPTODATE, GFP_NOFS);
+ start, end, EXTENT_UPTODATE);
}
int exclude_super_stripes(struct btrfs_root *root,
@@ -4180,7 +4178,7 @@ int exclude_super_stripes(struct btrfs_root *root,
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
- ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
+ ret = btrfs_rmap_block(root->fs_info,
cache->key.objectid, bytenr,
0, &logical, &nr, &stripe_len);
if (ret)
diff --git a/extent_io.c b/extent_io.c
index 7b9eb8ef..1c6c1425 100644
--- a/extent_io.c
+++ b/extent_io.c
@@ -192,8 +192,7 @@ static int clear_state_bit(struct extent_io_tree *tree,
/*
* clear some bits on a range in the tree.
*/
-int clear_extent_bits(struct extent_io_tree *tree, u64 start,
- u64 end, int bits, gfp_t mask)
+int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits)
{
struct extent_state *state;
struct extent_state *prealloc = NULL;
@@ -287,8 +286,7 @@ search_again:
/*
* set some bits on a range in the tree.
*/
-int set_extent_bits(struct extent_io_tree *tree, u64 start,
- u64 end, int bits, gfp_t mask)
+int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits)
{
struct extent_state *state;
struct extent_state *prealloc = NULL;
@@ -411,16 +409,14 @@ search_again:
goto again;
}
-int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
- gfp_t mask)
+int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end)
{
- return set_extent_bits(tree, start, end, EXTENT_DIRTY, mask);
+ return set_extent_bits(tree, start, end, EXTENT_DIRTY);
}
-int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
- gfp_t mask)
+int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end)
{
- return clear_extent_bits(tree, start, end, EXTENT_DIRTY, mask);
+ return clear_extent_bits(tree, start, end, EXTENT_DIRTY);
}
int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
@@ -702,8 +698,8 @@ int read_data_from_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
while (bytes_left) {
read_len = bytes_left;
- ret = btrfs_map_block(&info->mapping_tree, READ, offset,
- &read_len, &multi, mirror, NULL);
+ ret = btrfs_map_block(info, READ, offset, &read_len, &multi,
+ mirror, NULL);
if (ret) {
fprintf(stderr, "Couldn't map the block %Lu\n",
offset);
@@ -756,8 +752,8 @@ int write_data_to_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
this_len = bytes_left;
dev_nr = 0;
- ret = btrfs_map_block(&info->mapping_tree, WRITE, offset,
- &this_len, &multi, mirror, &raid_map);
+ ret = btrfs_map_block(info, WRITE, offset, &this_len, &multi,
+ mirror, &raid_map);
if (ret) {
fprintf(stderr, "Couldn't map the block %Lu\n",
offset);
@@ -769,7 +765,7 @@ int write_data_to_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
u64 stripe_len = this_len;
this_len = min(this_len, bytes_left);
- this_len = min(this_len, (u64)info->tree_root->nodesize);
+ this_len = min(this_len, (u64)info->nodesize);
eb = malloc(sizeof(struct extent_buffer) + this_len);
if (!eb) {
@@ -838,7 +834,7 @@ int set_extent_buffer_dirty(struct extent_buffer *eb)
struct extent_io_tree *tree = eb->tree;
if (!(eb->flags & EXTENT_DIRTY)) {
eb->flags |= EXTENT_DIRTY;
- set_extent_dirty(tree, eb->start, eb->start + eb->len - 1, 0);
+ set_extent_dirty(tree, eb->start, eb->start + eb->len - 1);
extent_buffer_get(eb);
}
return 0;
@@ -849,7 +845,7 @@ int clear_extent_buffer_dirty(struct extent_buffer *eb)
struct extent_io_tree *tree = eb->tree;
if (eb->flags & EXTENT_DIRTY) {
eb->flags &= ~EXTENT_DIRTY;
- clear_extent_dirty(tree, eb->start, eb->start + eb->len - 1, 0);
+ clear_extent_dirty(tree, eb->start, eb->start + eb->len - 1);
free_extent_buffer(eb);
}
return 0;
diff --git a/extent_io.h b/extent_io.h
index bd6cf9ef..e6174894 100644
--- a/extent_io.h
+++ b/extent_io.h
@@ -107,18 +107,14 @@ static inline void extent_buffer_get(struct extent_buffer *eb)
void extent_io_tree_init(struct extent_io_tree *tree);
void extent_io_tree_cleanup(struct extent_io_tree *tree);
-int set_extent_bits(struct extent_io_tree *tree, u64 start,
- u64 end, int bits, gfp_t mask);
-int clear_extent_bits(struct extent_io_tree *tree, u64 start,
- u64 end, int bits, gfp_t mask);
+int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits);
+int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits);
int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
u64 *start_ret, u64 *end_ret, int bits);
int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
int bits, int filled);
-int set_extent_dirty(struct extent_io_tree *tree, u64 start,
- u64 end, gfp_t mask);
-int clear_extent_dirty(struct extent_io_tree *tree, u64 start,
- u64 end, gfp_t mask);
+int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end);
+int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end);
static inline int set_extent_buffer_uptodate(struct extent_buffer *eb)
{
eb->flags |= EXTENT_UPTODATE;
diff --git a/file-item.c b/file-item.c
index e462b4bb..6877a1a9 100644
--- a/file-item.c
+++ b/file-item.c
@@ -162,7 +162,8 @@ btrfs_lookup_csum(struct btrfs_trans_handle *trans,
if (found_key.type != BTRFS_EXTENT_CSUM_KEY)
goto fail;
- csum_offset = (bytenr - found_key.offset) / root->sectorsize;
+ csum_offset = (bytenr - found_key.offset) /
+ root->fs_info->sectorsize;
csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]);
csums_in_item /= csum_size;
@@ -195,6 +196,7 @@ int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
struct extent_buffer *leaf = NULL;
u64 csum_offset;
u32 csum_result = ~(u32)0;
+ u32 sectorsize = root->fs_info->sectorsize;
u32 nritems;
u32 ins_size;
u16 csum_size =
@@ -265,7 +267,7 @@ int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
path->slots[0]--;
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
- csum_offset = (file_key.offset - found_key.offset) / root->sectorsize;
+ csum_offset = (file_key.offset - found_key.offset) / sectorsize;
if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID ||
found_key.type != BTRFS_EXTENT_CSUM_KEY ||
csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) {
@@ -277,7 +279,7 @@ int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
diff = diff - btrfs_item_size_nr(leaf, path->slots[0]);
if (diff != csum_size)
goto insert;
- ret = btrfs_extend_item(trans, root, path, diff);
+ ret = btrfs_extend_item(root, path, diff);
BUG_ON(ret);
goto csum;
}
@@ -288,7 +290,7 @@ insert:
if (found_next) {
u64 tmp = min(alloc_end, next_offset);
tmp -= file_key.offset;
- tmp /= root->sectorsize;
+ tmp /= sectorsize;
tmp = max((u64)1, tmp);
tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root, csum_size));
ins_size = csum_size * tmp;
@@ -310,7 +312,7 @@ csum:
item = (struct btrfs_csum_item *)((unsigned char *)item +
csum_offset * csum_size);
found:
- csum_result = btrfs_csum_data(root, data, csum_result, len);
+ csum_result = btrfs_csum_data(data, csum_result, len);
btrfs_csum_final(csum_result, (u8 *)&csum_result);
if (csum_result == 0) {
printk("csum result is 0 for block %llu\n",
@@ -336,8 +338,7 @@ fail:
* This calls btrfs_truncate_item with the correct args based on the
* overlap, and fixes up the key as required.
*/
-static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int truncate_one_csum(struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_key *key,
u64 bytenr, u64 len)
@@ -347,12 +348,12 @@ static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
btrfs_super_csum_size(root->fs_info->super_copy);
u64 csum_end;
u64 end_byte = bytenr + len;
- u32 blocksize = root->sectorsize;
+ u32 blocksize = root->fs_info->sectorsize;
int ret;
leaf = path->nodes[0];
csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size;
- csum_end *= root->sectorsize;
+ csum_end *= root->fs_info->sectorsize;
csum_end += key->offset;
if (key->offset < bytenr && csum_end <= end_byte) {
@@ -364,7 +365,7 @@ static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
*/
u32 new_size = (bytenr - key->offset) / blocksize;
new_size *= csum_size;
- ret = btrfs_truncate_item(trans, root, path, new_size, 1);
+ ret = btrfs_truncate_item(root, path, new_size, 1);
BUG_ON(ret);
} else if (key->offset >= bytenr && csum_end > end_byte &&
end_byte > key->offset) {
@@ -377,7 +378,7 @@ static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
u32 new_size = (csum_end - end_byte) / blocksize;
new_size *= csum_size;
- ret = btrfs_truncate_item(trans, root, path, new_size, 0);
+ ret = btrfs_truncate_item(root, path, new_size, 0);
BUG_ON(ret);
key->offset = end_byte;
@@ -404,7 +405,7 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
int ret;
u16 csum_size =
btrfs_super_csum_size(root->fs_info->super_copy);
- int blocksize = root->sectorsize;
+ int blocksize = root->fs_info->sectorsize;
root = root->fs_info->csum_root;
@@ -489,8 +490,7 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
key.offset = end_byte - 1;
} else {
- ret = truncate_one_csum(trans, root, path,
- &key, bytenr, len);
+ ret = truncate_one_csum(root, path, &key, bytenr, len);
BUG_ON(ret);
}
btrfs_release_path(path);
diff --git a/file.c b/file.c
index dd0b04b8..f5e645c4 100644
--- a/file.c
+++ b/file.c
@@ -18,6 +18,8 @@
#include <sys/stat.h>
#include "ctree.h"
+#include "utils.h"
+#include "disk-io.h"
#include "transaction.h"
#include "kerncompat.h"
@@ -160,3 +162,170 @@ out:
btrfs_free_path(path);
return ret;
}
+
+/*
+ * Read out content of one inode.
+ *
+ * @root: fs/subvolume root containing the inode
+ * @ino: inode number
+ * @start: offset inside the file, aligned to sectorsize
+ * @len: length to read, aligned to sectorisize
+ * @dest: where data will be stored
+ *
+ * NOTE:
+ * 1) compression data is not supported yet
+ * 2) @start and @len must be aligned to sectorsize
+ * 3) data read out is also aligned to sectorsize, not truncated to inode size
+ *
+ * Return < 0 for fatal error during read.
+ * Otherwise return the number of succesfully read data in bytes.
+ */
+int btrfs_read_file(struct btrfs_root *root, u64 ino, u64 start, int len,
+ char *dest)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct extent_buffer *leaf;
+ struct btrfs_inode_item *ii;
+ u64 isize;
+ int no_holes = btrfs_fs_incompat(fs_info, NO_HOLES);
+ int slot;
+ int read = 0;
+ int ret;
+
+ if (!IS_ALIGNED(start, fs_info->sectorsize) ||
+ !IS_ALIGNED(len, fs_info->sectorsize)) {
+ warning("@start and @len must be aligned to %u for function %s",
+ fs_info->sectorsize, __func__);
+ return -EINVAL;
+ }
+
+ btrfs_init_path(&path);
+ key.objectid = ino;
+ key.offset = start;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ if (ret > 0) {
+ ret = btrfs_previous_item(root, &path, ino, BTRFS_EXTENT_DATA_KEY);
+ if (ret > 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+ }
+
+ /*
+ * Reset @dest to all 0, so we don't need to care about holes in
+ * no_hole mode, but focus on reading non-hole part.
+ */
+ memset(dest, 0, len);
+ while (1) {
+ struct btrfs_file_extent_item *fi;
+ u64 extent_start;
+ u64 extent_len;
+ u64 read_start;
+ u64 read_len;
+ u64 read_len_ret;
+ u64 disk_bytenr;
+
+ leaf = path.nodes[0];
+ slot = path.slots[0];
+
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.objectid > ino)
+ break;
+ if (key.type != BTRFS_EXTENT_DATA_KEY || key.objectid != ino)
+ goto next;
+
+ extent_start = key.offset;
+ if (extent_start >= start + len)
+ break;
+
+ fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+ if (btrfs_file_extent_compression(leaf, fi) !=
+ BTRFS_COMPRESS_NONE) {
+ ret = -ENOTTY;
+ break;
+ }
+
+ /* Inline extent, one inode should only one inline extent */
+ if (btrfs_file_extent_type(leaf, fi) ==
+ BTRFS_FILE_EXTENT_INLINE) {
+ extent_len = btrfs_file_extent_inline_len(leaf, slot,
+ fi);
+ if (extent_start + extent_len <= start)
+ goto next;
+ read_extent_buffer(leaf, dest,
+ btrfs_file_extent_inline_start(fi), extent_len);
+ read += round_up(extent_len, fs_info->sectorsize);
+ break;
+ }
+
+ extent_len = btrfs_file_extent_num_bytes(leaf, fi);
+ if (extent_start + extent_len <= start)
+ goto next;
+
+ read_start = max(start, extent_start);
+ read_len = min(start + len, extent_start + extent_len) -
+ read_start;
+
+ /* We have already zeroed @dest, nothing to do */
+ if (btrfs_file_extent_type(leaf, fi) ==
+ BTRFS_FILE_EXTENT_PREALLOC ||
+ btrfs_file_extent_disk_num_bytes(leaf, fi) == 0) {
+ read += read_len;
+ goto next;
+ }
+
+ disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi) +
+ btrfs_file_extent_offset(leaf, fi);
+ read_len_ret = read_len;
+ ret = read_extent_data(fs_info, dest + read_start - start, disk_bytenr,
+ &read_len_ret, 0);
+ if (ret < 0)
+ break;
+ /* Short read, something went wrong */
+ if (read_len_ret != read_len)
+ return -EIO;
+ read += read_len;
+next:
+ ret = btrfs_next_item(root, &path);
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ }
+
+ /*
+ * Special trick for no_holes, since for no_holes we don't have good
+ * method to account skipped and tailling holes, we used
+ * min(inode size, len) as return value
+ */
+ if (no_holes) {
+ btrfs_release_path(&path);
+ key.objectid = ino;
+ key.offset = 0;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ ret = btrfs_lookup_inode(NULL, root, &path, &key, 0);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+ ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_inode_item);
+ isize = round_up(btrfs_inode_size(path.nodes[0], ii),
+ fs_info->sectorsize);
+ read = min_t(u64, isize - start, len);
+ }
+out:
+ btrfs_release_path(&path);
+ if (!ret)
+ ret = read;
+ return ret;
+}
diff --git a/find-root.c b/find-root.c
index 89d36119..1765adf8 100644
--- a/find-root.c
+++ b/find-root.c
@@ -118,11 +118,11 @@ int btrfs_find_root_search(struct btrfs_fs_info *fs_info,
fs_info->suppress_check_block_errors = 1;
while (1) {
if (filter->objectid != BTRFS_CHUNK_TREE_OBJECTID)
- ret = btrfs_next_bg_metadata(&fs_info->mapping_tree,
+ ret = btrfs_next_bg_metadata(fs_info,
&chunk_offset,
&chunk_size);
else
- ret = btrfs_next_bg_system(&fs_info->mapping_tree,
+ ret = btrfs_next_bg_system(fs_info,
&chunk_offset,
&chunk_size);
if (ret) {
@@ -133,8 +133,7 @@ int btrfs_find_root_search(struct btrfs_fs_info *fs_info,
for (offset = chunk_offset;
offset < chunk_offset + chunk_size;
offset += nodesize) {
- eb = read_tree_block_fs_info(fs_info, offset, nodesize,
- 0);
+ eb = read_tree_block(fs_info, offset, nodesize, 0);
if (!eb || IS_ERR(eb))
continue;
ret = add_eb_to_result(eb, result, nodesize, filter,
diff --git a/free-space-cache.c b/free-space-cache.c
index 286b185e..4bf4a6cb 100644
--- a/free-space-cache.c
+++ b/free-space-cache.c
@@ -33,7 +33,7 @@
* filesystem
*/
#define BITS_PER_BITMAP(sectorsize) ((sectorsize) * 8)
-#define MAX_CACHE_BYTES_PER_GIG (32 * 1024)
+#define MAX_CACHE_BYTES_PER_GIG SZ_32K
static int link_free_space(struct btrfs_free_space_ctl *ctl,
struct btrfs_free_space *info);
@@ -54,7 +54,8 @@ static int io_ctl_init(struct io_ctl *io_ctl, u64 size, u64 ino,
struct btrfs_root *root)
{
memset(io_ctl, 0, sizeof(struct io_ctl));
- io_ctl->num_pages = (size + root->sectorsize - 1) / root->sectorsize;
+ io_ctl->num_pages = (size + root->fs_info->sectorsize - 1) /
+ root->fs_info->sectorsize;
io_ctl->buffer = kzalloc(size, GFP_NOFS);
if (!io_ctl->buffer)
return -ENOMEM;
@@ -81,11 +82,12 @@ static void io_ctl_unmap_page(struct io_ctl *io_ctl)
static void io_ctl_map_page(struct io_ctl *io_ctl, int clear)
{
BUG_ON(io_ctl->index >= io_ctl->num_pages);
- io_ctl->cur = io_ctl->buffer + (io_ctl->index++ * io_ctl->root->sectorsize);
+ io_ctl->cur = io_ctl->buffer + (io_ctl->index++ *
+ io_ctl->root->fs_info->sectorsize);
io_ctl->orig = io_ctl->cur;
- io_ctl->size = io_ctl->root->sectorsize;
+ io_ctl->size = io_ctl->root->fs_info->sectorsize;
if (clear)
- memset(io_ctl->cur, 0, io_ctl->root->sectorsize);
+ memset(io_ctl->cur, 0, io_ctl->root->fs_info->sectorsize);
}
static void io_ctl_drop_pages(struct io_ctl *io_ctl)
@@ -210,7 +212,8 @@ static int io_ctl_check_crc(struct io_ctl *io_ctl, int index)
val = *tmp;
io_ctl_map_page(io_ctl, 0);
- crc = crc32c(crc, io_ctl->orig + offset, io_ctl->root->sectorsize - offset);
+ crc = crc32c(crc, io_ctl->orig + offset,
+ io_ctl->root->fs_info->sectorsize - offset);
btrfs_csum_final(crc, (u8 *)&crc);
if (val != crc) {
printk("btrfs: csum mismatch on free space cache\n");
@@ -257,7 +260,7 @@ static int io_ctl_read_bitmap(struct io_ctl *io_ctl,
if (ret)
return ret;
- memcpy(entry->bitmap, io_ctl->cur, io_ctl->root->sectorsize);
+ memcpy(entry->bitmap, io_ctl->cur, io_ctl->root->fs_info->sectorsize);
io_ctl_unmap_page(io_ctl);
return 0;
diff --git a/free-space-tree.c b/free-space-tree.c
index f3a51263..69a4eca8 100644
--- a/free-space-tree.c
+++ b/free-space-tree.c
@@ -205,7 +205,7 @@ static int load_free_space_bitmaps(struct btrfs_fs_info *fs_info,
offset = key.objectid;
while (offset < key.objectid + key.offset) {
bit = free_space_test_bit(block_group, path, offset,
- root->sectorsize);
+ fs_info->sectorsize);
if (prev_bit == 0 && bit == 1) {
extent_start = offset;
} else if (prev_bit == 1 && bit == 0) {
@@ -213,7 +213,7 @@ static int load_free_space_bitmaps(struct btrfs_fs_info *fs_info,
extent_count++;
}
prev_bit = bit;
- offset += root->sectorsize;
+ offset += fs_info->sectorsize;
}
}
diff --git a/fsfeatures.c b/fsfeatures.c
new file mode 100644
index 00000000..7d85d60f
--- /dev/null
+++ b/fsfeatures.c
@@ -0,0 +1,227 @@
+/*
+ * 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 <sys/utsname.h>
+#include <linux/version.h>
+#include "fsfeatures.h"
+#include "ctree.h"
+#include "utils.h"
+
+/*
+ * Insert a root item for temporary tree root
+ *
+ * Only used in make_btrfs_v2().
+ */
+#define VERSION_TO_STRING3(a,b,c) #a "." #b "." #c, KERNEL_VERSION(a,b,c)
+#define VERSION_TO_STRING2(a,b) #a "." #b, KERNEL_VERSION(a,b,0)
+
+/*
+ * Feature stability status and versions: compat <= safe <= default
+ */
+static const struct btrfs_fs_feature {
+ const char *name;
+ u64 flag;
+ const char *sysfs_name;
+ /*
+ * Compatibility with kernel of given version. Filesystem can be
+ * mounted.
+ */
+ const char *compat_str;
+ u32 compat_ver;
+ /*
+ * Considered safe for use, but is not on by default, even if the
+ * kernel supports the feature.
+ */
+ const char *safe_str;
+ u32 safe_ver;
+ /*
+ * Considered safe for use and will be turned on by default if
+ * supported by the running kernel.
+ */
+ const char *default_str;
+ u32 default_ver;
+ const char *desc;
+} mkfs_features[] = {
+ { "mixed-bg", BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS,
+ "mixed_groups",
+ VERSION_TO_STRING3(2,6,37),
+ VERSION_TO_STRING3(2,6,37),
+ NULL, 0,
+ "mixed data and metadata block groups" },
+ { "extref", BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF,
+ "extended_iref",
+ VERSION_TO_STRING2(3,7),
+ VERSION_TO_STRING2(3,12),
+ VERSION_TO_STRING2(3,12),
+ "increased hardlink limit per file to 65536" },
+ { "raid56", BTRFS_FEATURE_INCOMPAT_RAID56,
+ "raid56",
+ VERSION_TO_STRING2(3,9),
+ NULL, 0,
+ NULL, 0,
+ "raid56 extended format" },
+ { "skinny-metadata", BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA,
+ "skinny_metadata",
+ VERSION_TO_STRING2(3,10),
+ VERSION_TO_STRING2(3,18),
+ VERSION_TO_STRING2(3,18),
+ "reduced-size metadata extent refs" },
+ { "no-holes", BTRFS_FEATURE_INCOMPAT_NO_HOLES,
+ "no_holes",
+ VERSION_TO_STRING2(3,14),
+ VERSION_TO_STRING2(4,0),
+ NULL, 0,
+ "no explicit hole extents for files" },
+ /* Keep this one last */
+ { "list-all", BTRFS_FEATURE_LIST_ALL, NULL }
+};
+
+static int parse_one_fs_feature(const char *name, u64 *flags)
+{
+ int i;
+ int found = 0;
+
+ for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
+ if (name[0] == '^' &&
+ !strcmp(mkfs_features[i].name, name + 1)) {
+ *flags &= ~ mkfs_features[i].flag;
+ found = 1;
+ } else if (!strcmp(mkfs_features[i].name, name)) {
+ *flags |= mkfs_features[i].flag;
+ found = 1;
+ }
+ }
+
+ return !found;
+}
+
+void btrfs_parse_features_to_string(char *buf, u64 flags)
+{
+ int i;
+
+ buf[0] = 0;
+
+ for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
+ if (flags & mkfs_features[i].flag) {
+ if (*buf)
+ strcat(buf, ", ");
+ strcat(buf, mkfs_features[i].name);
+ }
+ }
+}
+
+void btrfs_process_fs_features(u64 flags)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
+ if (flags & mkfs_features[i].flag) {
+ printf("Turning ON incompat feature '%s': %s\n",
+ mkfs_features[i].name,
+ mkfs_features[i].desc);
+ }
+ }
+}
+
+void btrfs_list_all_fs_features(u64 mask_disallowed)
+{
+ int i;
+
+ fprintf(stderr, "Filesystem features available:\n");
+ for (i = 0; i < ARRAY_SIZE(mkfs_features) - 1; i++) {
+ const struct btrfs_fs_feature *feat = &mkfs_features[i];
+
+ if (feat->flag & mask_disallowed)
+ continue;
+ fprintf(stderr, "%-20s- %s (0x%llx", feat->name, feat->desc,
+ feat->flag);
+ if (feat->compat_ver)
+ fprintf(stderr, ", compat=%s", feat->compat_str);
+ if (feat->safe_ver)
+ fprintf(stderr, ", safe=%s", feat->safe_str);
+ if (feat->default_ver)
+ fprintf(stderr, ", default=%s", feat->default_str);
+ fprintf(stderr, ")\n");
+ }
+}
+
+/*
+ * Return NULL if all features were parsed fine, otherwise return the name of
+ * the first unparsed.
+ */
+char* btrfs_parse_fs_features(char *namelist, u64 *flags)
+{
+ char *this_char;
+ char *save_ptr = NULL; /* Satisfy static checkers */
+
+ for (this_char = strtok_r(namelist, ",", &save_ptr);
+ this_char != NULL;
+ this_char = strtok_r(NULL, ",", &save_ptr)) {
+ if (parse_one_fs_feature(this_char, flags))
+ return this_char;
+ }
+
+ return NULL;
+}
+
+void print_kernel_version(FILE *stream, u32 version)
+{
+ u32 v[3];
+
+ v[0] = version & 0xFF;
+ v[1] = (version >> 8) & 0xFF;
+ v[2] = version >> 16;
+ fprintf(stream, "%u.%u", v[2], v[1]);
+ if (v[0])
+ fprintf(stream, ".%u", v[0]);
+}
+
+u32 get_running_kernel_version(void)
+{
+ struct utsname utsbuf;
+ char *tmp;
+ char *saveptr = NULL;
+ u32 version;
+
+ uname(&utsbuf);
+ if (strcmp(utsbuf.sysname, "Linux") != 0) {
+ error("unsupported system: %s", utsbuf.sysname);
+ exit(1);
+ }
+ /* 1.2.3-4-name */
+ tmp = strchr(utsbuf.release, '-');
+ if (tmp)
+ *tmp = 0;
+
+ tmp = strtok_r(utsbuf.release, ".", &saveptr);
+ if (!string_is_numerical(tmp))
+ return (u32)-1;
+ version = atoi(tmp) << 16;
+ tmp = strtok_r(NULL, ".", &saveptr);
+ if (!string_is_numerical(tmp))
+ return (u32)-1;
+ version |= atoi(tmp) << 8;
+ tmp = strtok_r(NULL, ".", &saveptr);
+ if (tmp) {
+ if (!string_is_numerical(tmp))
+ return (u32)-1;
+ version |= atoi(tmp);
+ }
+
+ return version;
+}
+
diff --git a/fsfeatures.h b/fsfeatures.h
new file mode 100644
index 00000000..513ed1e7
--- /dev/null
+++ b/fsfeatures.h
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_FSFEATURES_H__
+#define __BTRFS_FSFEATURES_H__
+
+#include "kerncompat.h"
+
+#define BTRFS_MKFS_DEFAULT_NODE_SIZE SZ_16K
+#define BTRFS_MKFS_DEFAULT_FEATURES \
+ (BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF \
+ | BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)
+
+/*
+ * Avoid multi-device features (RAID56) and mixed block groups
+ */
+#define BTRFS_CONVERT_ALLOWED_FEATURES \
+ (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF \
+ | BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL \
+ | BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO \
+ | BTRFS_FEATURE_INCOMPAT_COMPRESS_LZOv2 \
+ | BTRFS_FEATURE_INCOMPAT_BIG_METADATA \
+ | BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF \
+ | BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA \
+ | BTRFS_FEATURE_INCOMPAT_NO_HOLES)
+
+#define BTRFS_FEATURE_LIST_ALL (1ULL << 63)
+
+void btrfs_list_all_fs_features(u64 mask_disallowed);
+char* btrfs_parse_fs_features(char *namelist, u64 *flags);
+void btrfs_process_fs_features(u64 flags);
+void btrfs_parse_features_to_string(char *buf, u64 flags);
+void print_kernel_version(FILE *stream, u32 version);
+u32 get_running_kernel_version(void);
+int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features);
+
+#endif
diff --git a/help.c b/help.c
index 5573f0f9..19b0d357 100644
--- a/help.c
+++ b/help.c
@@ -18,15 +18,112 @@
#include <stdlib.h>
#include <string.h>
#include <limits.h>
+#include <getopt.h>
#include "commands.h"
#include "utils.h"
+#include "help.h"
#define USAGE_SHORT 1U
#define USAGE_LONG 2U
#define USAGE_OPTIONS 4U
#define USAGE_LISTING 8U
+static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
+
+const char *get_argv0_buf(void)
+{
+ return argv0_buf;
+}
+
+void fixup_argv0(char **argv, const char *token)
+{
+ int len = strlen(argv0_buf);
+
+ snprintf(argv0_buf + len, sizeof(argv0_buf) - len, " %s", token);
+ argv[0] = argv0_buf;
+}
+
+void set_argv0(char **argv)
+{
+ strncpy(argv0_buf, argv[0], sizeof(argv0_buf));
+ argv0_buf[sizeof(argv0_buf) - 1] = 0;
+}
+
+int check_argc_exact(int nargs, int expected)
+{
+ if (nargs < expected)
+ fprintf(stderr, "%s: too few arguments\n", argv0_buf);
+ if (nargs > expected)
+ fprintf(stderr, "%s: too many arguments\n", argv0_buf);
+
+ return nargs != expected;
+}
+
+int check_argc_min(int nargs, int expected)
+{
+ if (nargs < expected) {
+ fprintf(stderr, "%s: too few arguments\n", argv0_buf);
+ return 1;
+ }
+
+ return 0;
+}
+
+int check_argc_max(int nargs, int expected)
+{
+ if (nargs > expected) {
+ fprintf(stderr, "%s: too many arguments\n", argv0_buf);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Preprocess @argv with getopt_long to reorder options and consume the "--"
+ * option separator.
+ * Unknown short and long options are reported, optionally the @usage is printed
+ * before exit.
+ */
+void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
+{
+ static const struct option long_options[] = {
+ {NULL, 0, NULL, 0}
+ };
+
+ while (1) {
+ int c = getopt_long(argc, argv, "", long_options, NULL);
+
+ if (c < 0)
+ break;
+
+ switch (c) {
+ default:
+ if (usagestr)
+ usage(usagestr);
+ }
+ }
+}
+
+/*
+ * Same as clean_args_no_options but pass through arguments that could look
+ * like short options. Eg. reisze which takes a negative resize argument like
+ * '-123M' .
+ *
+ * This accepts only two forms:
+ * - "-- option1 option2 ..."
+ * - "option1 option2 ..."
+ */
+void clean_args_no_options_relaxed(int argc, char *argv[], const char * const *usagestr)
+{
+ if (argc <= 1)
+ return;
+
+ if (strcmp(argv[1], "--") == 0)
+ optind = 2;
+}
+
static int do_usage_one_command(const char * const *usagestr,
unsigned int flags, FILE *outf)
{
@@ -262,12 +359,3 @@ void help_command_group(const struct cmd_group *grp, int argc, char **argv)
usage_command_group(grp, full, 0);
}
-int prefixcmp(const char *str, const char *prefix)
-{
- for (; ; str++, prefix++)
- if (!*prefix)
- return 0;
- else if (*str != *prefix)
- return (unsigned char)*prefix - (unsigned char)*str;
-}
-
diff --git a/help.h b/help.h
new file mode 100644
index 00000000..7458e745
--- /dev/null
+++ b/help.h
@@ -0,0 +1,79 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_HELP_H__
+#define __BTRFS_HELP_H__
+
+#define GETOPT_VAL_SI 256
+#define GETOPT_VAL_IEC 257
+#define GETOPT_VAL_RAW 258
+#define GETOPT_VAL_HUMAN_READABLE 259
+#define GETOPT_VAL_KBYTES 260
+#define GETOPT_VAL_MBYTES 261
+#define GETOPT_VAL_GBYTES 262
+#define GETOPT_VAL_TBYTES 263
+
+#define GETOPT_VAL_HELP 270
+
+#define ARGV0_BUF_SIZE PATH_MAX
+
+#define HELPINFO_UNITS_LONG \
+ "--raw raw numbers in bytes", \
+ "--human-readable human friendly numbers, base 1024 (default)", \
+ "--iec use 1024 as a base (KiB, MiB, GiB, TiB)", \
+ "--si use 1000 as a base (kB, MB, GB, TB)", \
+ "--kbytes show sizes in KiB, or kB with --si", \
+ "--mbytes show sizes in MiB, or MB with --si", \
+ "--gbytes show sizes in GiB, or GB with --si", \
+ "--tbytes show sizes in TiB, or TB with --si"
+
+#define HELPINFO_UNITS_SHORT_LONG \
+ "-b|--raw raw numbers in bytes", \
+ "-h|--human-readable", \
+ " human friendly numbers, base 1024 (default)", \
+ "-H human friendly numbers, base 1000", \
+ "--iec use 1024 as a base (KiB, MiB, GiB, TiB)", \
+ "--si use 1000 as a base (kB, MB, GB, TB)", \
+ "-k|--kbytes show sizes in KiB, or kB with --si", \
+ "-m|--mbytes show sizes in MiB, or MB with --si", \
+ "-g|--gbytes show sizes in GiB, or GB with --si", \
+ "-t|--tbytes show sizes in TiB, or TB with --si"
+
+struct cmd_struct;
+struct cmd_group;
+
+void usage(const char * const *usagestr) __attribute__((noreturn));
+void usage_command(const struct cmd_struct *cmd, int full, int err);
+void usage_command_group(const struct cmd_group *grp, int all, int err);
+void usage_command_group_short(const struct cmd_group *grp);
+
+void help_unknown_token(const char *arg, const struct cmd_group *grp) __attribute__((noreturn));
+void help_ambiguous_token(const char *arg, const struct cmd_group *grp) __attribute__((noreturn));
+
+void help_command_group(const struct cmd_group *grp, int argc, char **argv);
+
+int check_argc_exact(int nargs, int expected);
+int check_argc_min(int nargs, int expected);
+int check_argc_max(int nargs, int expected);
+void clean_args_no_options(int argc, char *argv[], const char * const *usage);
+void clean_args_no_options_relaxed(int argc, char *argv[],
+ const char * const *usagestr);
+
+void fixup_argv0(char **argv, const char *token);
+void set_argv0(char **argv);
+const char *get_argv0_buf(void);
+
+#endif
diff --git a/image/main.c b/image/main.c
index 0158844b..80588e81 100644
--- a/image/main.c
+++ b/image/main.c
@@ -35,6 +35,7 @@
#include "utils.h"
#include "volumes.h"
#include "extent_io.h"
+#include "help.h"
#define HEADER_MAGIC 0xbd5c25e27295668bULL
#define MAX_PENDING_SIZE (256 * 1024)
@@ -875,6 +876,7 @@ static int read_data_extent(struct metadump_struct *md,
struct async_work *async)
{
struct btrfs_root *root = md->root;
+ struct btrfs_fs_info *fs_info = root->fs_info;
u64 bytes_left = async->size;
u64 logical = async->start;
u64 offset = 0;
@@ -883,14 +885,13 @@ static int read_data_extent(struct metadump_struct *md,
int cur_mirror;
int ret;
- num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, logical,
- bytes_left);
+ num_copies = btrfs_num_copies(root->fs_info, logical, bytes_left);
/* Try our best to read data, just like read_tree_block() */
for (cur_mirror = 0; cur_mirror < num_copies; cur_mirror++) {
while (bytes_left) {
read_len = bytes_left;
- ret = read_extent_data(root,
+ ret = read_extent_data(fs_info,
(char *)(async->buffer + offset),
logical, &read_len, cur_mirror);
if (ret < 0)
@@ -918,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 blocksize = md->root->fs_info->nodesize;
u64 start = 0;
u64 size;
size_t offset;
@@ -972,7 +973,8 @@ static int flush_pending(struct metadump_struct *md, int done)
while (!md->data && size > 0) {
u64 this_read = min(blocksize, size);
- eb = read_tree_block(md->root, start, this_read, 0);
+ eb = read_tree_block(md->root->fs_info, start,
+ this_read, 0);
if (!extent_buffer_uptodate(eb)) {
free(async->buffer);
free(async);
@@ -1027,7 +1029,7 @@ static int add_extent(u64 start, u64 size, struct metadump_struct *md,
return ret;
md->pending_start = start;
}
- readahead_tree_block(md->root, start, size, 0);
+ readahead_tree_block(md->root->fs_info, start, size, 0);
md->pending_size += size;
md->data = data;
return 0;
@@ -1076,13 +1078,15 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
struct extent_buffer *tmp;
struct btrfs_root_item *ri;
struct btrfs_key key;
+ struct btrfs_fs_info *fs_info = root->fs_info;
u64 bytenr;
int level;
int nritems = 0;
int i = 0;
int ret;
- ret = add_extent(btrfs_header_bytenr(eb), root->nodesize, metadump, 0);
+ ret = add_extent(btrfs_header_bytenr(eb), fs_info->nodesize,
+ metadump, 0);
if (ret) {
error("unable to add metadata block %llu: %d",
btrfs_header_bytenr(eb), ret);
@@ -1101,7 +1105,8 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
continue;
ri = btrfs_item_ptr(eb, i, struct btrfs_root_item);
bytenr = btrfs_disk_root_bytenr(eb, ri);
- tmp = read_tree_block(root, bytenr, root->nodesize, 0);
+ tmp = read_tree_block(fs_info, bytenr,
+ fs_info->nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
error("unable to read log root block");
return -EIO;
@@ -1112,7 +1117,8 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
return ret;
} else {
bytenr = btrfs_node_blockptr(eb, i);
- tmp = read_tree_block(root, bytenr, root->nodesize, 0);
+ tmp = read_tree_block(fs_info, bytenr,
+ fs_info->nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
error("unable to read log root block");
return -EIO;
@@ -1128,8 +1134,7 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
}
static int copy_log_trees(struct btrfs_root *root,
- struct metadump_struct *metadump,
- struct btrfs_path *path)
+ struct metadump_struct *metadump)
{
u64 blocknr = btrfs_super_log_root(root->fs_info->super_copy);
@@ -1260,7 +1265,7 @@ static int copy_from_extent_tree(struct metadump_struct *metadump,
bytenr = key.objectid;
if (key.type == BTRFS_METADATA_ITEM_KEY) {
- num_bytes = extent_root->nodesize;
+ num_bytes = extent_root->fs_info->nodesize;
} else {
num_bytes = key.offset;
}
@@ -1374,7 +1379,7 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
}
}
- ret = copy_log_trees(root, &metadump, &path);
+ ret = copy_log_trees(root, &metadump);
if (ret) {
err = ret;
goto out;
@@ -1500,6 +1505,7 @@ static int update_super(struct mdrestore_struct *mdres, u8 *buffer)
flags |= BTRFS_SUPER_FLAG_METADUMP_V2;
btrfs_set_super_flags(super, flags);
btrfs_set_super_sys_array_size(super, new_array_size);
+ btrfs_set_super_num_devices(super, 1);
csum_block(buffer, BTRFS_SUPER_INFO_SIZE);
return 0;
@@ -1714,14 +1720,15 @@ static void *restore_worker(void *data)
}
async = list_entry(mdres->list.next, struct async_work, list);
list_del_init(&async->list);
- pthread_mutex_unlock(&mdres->mutex);
if (mdres->compress_method == COMPRESS_ZLIB) {
size = compress_size;
+ pthread_mutex_unlock(&mdres->mutex);
ret = uncompress(buffer, (unsigned long *)&size,
async->buffer, async->bufsize);
+ pthread_mutex_lock(&mdres->mutex);
if (ret != Z_OK) {
- error("decompressiion failed with %d", ret);
+ error("decompression failed with %d", ret);
err = -EIO;
}
outbuf = buffer;
@@ -1797,7 +1804,6 @@ error:
if (!mdres->multi_devices && async->start == BTRFS_SUPER_INFO_OFFSET)
write_backup_supers(outfd, outbuf);
- pthread_mutex_lock(&mdres->mutex);
if (err && !mdres->error)
mdres->error = err;
mdres->num_items--;
@@ -1898,7 +1904,7 @@ static int fill_mdres_info(struct mdrestore_struct *mdres,
ret = uncompress(buffer, (unsigned long *)&size,
async->buffer, async->bufsize);
if (ret != Z_OK) {
- error("decompressiion failed with %d", ret);
+ error("decompression failed with %d", ret);
free(buffer);
return -EIO;
}
@@ -1927,7 +1933,9 @@ static int add_cluster(struct meta_cluster *cluster,
u32 i, nritems;
int ret;
+ pthread_mutex_lock(&mdres->mutex);
mdres->compress_method = header->compress;
+ pthread_mutex_unlock(&mdres->mutex);
bytenr = le64_to_cpu(header->bytenr) + BLOCK_SIZE;
nritems = le32_to_cpu(header->nritems);
@@ -2170,7 +2178,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
continue;
}
error(
- "unknown state after reading cluster at %llu, probably crrupted data",
+ "unknown state after reading cluster at %llu, probably corrupted data",
cluster_bytenr);
ret = -EIO;
break;
@@ -2219,7 +2227,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
(unsigned long *)&size, tmp,
bufsize);
if (ret != Z_OK) {
- error("decompressiion failed with %d",
+ error("decompression failed with %d",
ret);
ret = -EIO;
break;
@@ -2339,7 +2347,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) {
- error("decompressiion failed with %d", ret);
+ error("decompression failed with %d", ret);
free(buffer);
free(tmp);
return -EIO;
diff --git a/inode-item.c b/inode-item.c
index 5dd79dd3..1cc10667 100644
--- a/inode-item.c
+++ b/inode-item.c
@@ -79,7 +79,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
goto out;
old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]);
- ret = btrfs_extend_item(trans, root, path, ins_len);
+ ret = btrfs_extend_item(root, path, ins_len);
BUG_ON(ret);
ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_inode_ref);
@@ -160,7 +160,7 @@ int btrfs_insert_inode(struct btrfs_trans_handle *trans, struct btrfs_root
struct btrfs_inode_ref *btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_path *path,
const char *name, int namelen, u64 ino, u64 parent_ino,
- u64 index, int ins_len)
+ int ins_len)
{
struct btrfs_key key;
struct btrfs_inode_ref *ret_inode_ref = NULL;
@@ -312,7 +312,7 @@ int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
memmove_extent_buffer(leaf, ptr, ptr + del_len,
item_size - (ptr + del_len - item_start));
- btrfs_truncate_item(trans, root, path, item_size - del_len, 1);
+ btrfs_truncate_item(root, path, item_size - del_len, 1);
out:
btrfs_free_path(path);
@@ -354,7 +354,7 @@ int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
name, name_len, NULL))
goto out;
- btrfs_extend_item(trans, root, path, ins_len);
+ btrfs_extend_item(root, path, ins_len);
ret = 0;
}
@@ -433,7 +433,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
item_size - (ptr + sub_item_len - item_start));
- btrfs_truncate_item(trans, root, path, item_size - sub_item_len, 1);
+ btrfs_truncate_item(root, path, item_size - sub_item_len, 1);
btrfs_mark_buffer_dirty(path->nodes[0]);
out:
btrfs_free_path(path);
diff --git a/inode.c b/inode.c
index 991b8ddb..6b8bf40f 100644
--- a/inode.c
+++ b/inode.c
@@ -286,7 +286,7 @@ int btrfs_unlink(struct btrfs_trans_handle *trans, struct btrfs_root *root,
/* check the ref and backref exists */
inode_ref = btrfs_lookup_inode_ref(trans, root, path, name, namelen,
- ino, parent_ino, index, 0);
+ ino, parent_ino, 0);
if (IS_ERR(inode_ref)) {
ret = PTR_ERR(inode_ref);
goto out;
diff --git a/kerncompat.h b/kerncompat.h
index 1493cad8..fa96715f 100644
--- a/kerncompat.h
+++ b/kerncompat.h
@@ -299,6 +299,9 @@ static inline void assert_trace(const char *assertion, const char *filename,
fprintf(stderr,
"%s:%d: %s: Assertion `%s` failed, value %ld\n",
filename, line, func, assertion, val);
+#ifndef BTRFS_DISABLE_BACKTRACE
+ print_trace();
+#endif
abort();
exit(1);
}
@@ -314,11 +317,13 @@ static inline void assert_trace(const char *assertion, const char *filename,
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
+#ifndef __bitwise
#ifdef __CHECKER__
#define __bitwise __bitwise__
#else
#define __bitwise
-#endif
+#endif /* __CHECKER__ */
+#endif /* __bitwise */
/* Alignment check */
#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
diff --git a/kernel-lib/mktables.c b/kernel-lib/mktables.c
new file mode 100644
index 00000000..85f621fe
--- /dev/null
+++ b/kernel-lib/mktables.c
@@ -0,0 +1,148 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2 or (at your
+ * option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * mktables.c
+ *
+ * Make RAID-6 tables. This is a host user space program to be run at
+ * compile time.
+ */
+
+/*
+ * Btrfs-progs port, with following minor fixes:
+ * 1) Use "kerncompat.h"
+ * 2) Get rid of __KERNEL__ related macros
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <time.h>
+
+static uint8_t gfmul(uint8_t a, uint8_t b)
+{
+ uint8_t v = 0;
+
+ while (b) {
+ if (b & 1)
+ v ^= a;
+ a = (a << 1) ^ (a & 0x80 ? 0x1d : 0);
+ b >>= 1;
+ }
+
+ return v;
+}
+
+static uint8_t gfpow(uint8_t a, int b)
+{
+ uint8_t v = 1;
+
+ b %= 255;
+ if (b < 0)
+ b += 255;
+
+ while (b) {
+ if (b & 1)
+ v = gfmul(v, a);
+ a = gfmul(a, a);
+ b >>= 1;
+ }
+
+ return v;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, j, k;
+ uint8_t v;
+ uint8_t exptbl[256], invtbl[256];
+
+ printf("#include \"kerncompat.h\"\n");
+
+ /* Compute multiplication table */
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_gfmul[256][256] =\n"
+ "{\n");
+ for (i = 0; i < 256; i++) {
+ printf("\t{\n");
+ for (j = 0; j < 256; j += 8) {
+ printf("\t\t");
+ for (k = 0; k < 8; k++)
+ printf("0x%02x,%c", gfmul(i, j + k),
+ (k == 7) ? '\n' : ' ');
+ }
+ printf("\t},\n");
+ }
+ printf("};\n");
+
+ /* Compute vector multiplication table */
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_vgfmul[256][32] =\n"
+ "{\n");
+ for (i = 0; i < 256; i++) {
+ printf("\t{\n");
+ for (j = 0; j < 16; j += 8) {
+ printf("\t\t");
+ for (k = 0; k < 8; k++)
+ printf("0x%02x,%c", gfmul(i, j + k),
+ (k == 7) ? '\n' : ' ');
+ }
+ for (j = 0; j < 16; j += 8) {
+ printf("\t\t");
+ for (k = 0; k < 8; k++)
+ printf("0x%02x,%c", gfmul(i, (j + k) << 4),
+ (k == 7) ? '\n' : ' ');
+ }
+ printf("\t},\n");
+ }
+ printf("};\n");
+
+ /* Compute power-of-2 table (exponent) */
+ v = 1;
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_gfexp[256] =\n" "{\n");
+ for (i = 0; i < 256; i += 8) {
+ printf("\t");
+ for (j = 0; j < 8; j++) {
+ exptbl[i + j] = v;
+ printf("0x%02x,%c", v, (j == 7) ? '\n' : ' ');
+ v = gfmul(v, 2);
+ if (v == 1)
+ v = 0; /* For entry 255, not a real entry */
+ }
+ }
+ printf("};\n");
+
+ /* Compute inverse table x^-1 == x^254 */
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_gfinv[256] =\n" "{\n");
+ for (i = 0; i < 256; i += 8) {
+ printf("\t");
+ for (j = 0; j < 8; j++) {
+ invtbl[i + j] = v = gfpow(i + j, 254);
+ printf("0x%02x,%c", v, (j == 7) ? '\n' : ' ');
+ }
+ }
+ printf("};\n");
+
+ /* Compute inv(2^x + 1) (exponent-xor-inverse) table */
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_gfexi[256] =\n" "{\n");
+ for (i = 0; i < 256; i += 8) {
+ printf("\t");
+ for (j = 0; j < 8; j++)
+ printf("0x%02x,%c", invtbl[exptbl[i + j] ^ 1],
+ (j == 7) ? '\n' : ' ');
+ }
+ printf("};\n");
+
+ return 0;
+}
diff --git a/kernel-lib/raid56.c b/kernel-lib/raid56.c
new file mode 100644
index 00000000..e3a9339e
--- /dev/null
+++ b/kernel-lib/raid56.c
@@ -0,0 +1,359 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Added helpers for unaligned native int access
+ */
+
+/*
+ * raid6int1.c
+ *
+ * 1-way unrolled portable integer math RAID-6 instruction set
+ *
+ * This file was postprocessed using unroll.pl and then ported to userspace
+ */
+#include <stdint.h>
+#include <unistd.h>
+#include "kerncompat.h"
+#include "ctree.h"
+#include "disk-io.h"
+#include "volumes.h"
+#include "utils.h"
+#include "kernel-lib/raid56.h"
+
+/*
+ * This is the C data type to use
+ */
+
+/* Change this from BITS_PER_LONG if there is something better... */
+#if BITS_PER_LONG == 64
+# define NBYTES(x) ((x) * 0x0101010101010101UL)
+# define NSIZE 8
+# define NSHIFT 3
+typedef uint64_t unative_t;
+#define put_unaligned_native(val,p) put_unaligned_64((val),(p))
+#define get_unaligned_native(p) get_unaligned_64((p))
+#else
+# define NBYTES(x) ((x) * 0x01010101U)
+# define NSIZE 4
+# define NSHIFT 2
+typedef uint32_t unative_t;
+#define put_unaligned_native(val,p) put_unaligned_32((val),(p))
+#define get_unaligned_native(p) get_unaligned_32((p))
+#endif
+
+/*
+ * These sub-operations are separate inlines since they can sometimes be
+ * specially optimized using architecture-specific hacks.
+ */
+
+/*
+ * The SHLBYTE() operation shifts each byte left by 1, *not*
+ * rolling over into the next byte
+ */
+static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
+{
+ unative_t vv;
+
+ vv = (v << 1) & NBYTES(0xfe);
+ return vv;
+}
+
+/*
+ * The MASK() operation returns 0xFF in any byte for which the high
+ * bit is 1, 0x00 for any byte for which the high bit is 0.
+ */
+static inline __attribute_const__ unative_t MASK(unative_t v)
+{
+ unative_t vv;
+
+ vv = v & NBYTES(0x80);
+ vv = (vv << 1) - (vv >> 7); /* Overflow on the top bit is OK */
+ return vv;
+}
+
+
+void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ uint8_t **dptr = (uint8_t **)ptrs;
+ uint8_t *p, *q;
+ int d, z, z0;
+
+ unative_t wd0, wq0, wp0, w10, w20;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ for ( d = 0 ; d < bytes ; d += NSIZE*1 ) {
+ wq0 = wp0 = get_unaligned_native(&dptr[z0][d+0*NSIZE]);
+ for ( z = z0-1 ; z >= 0 ; z-- ) {
+ wd0 = get_unaligned_native(&dptr[z][d+0*NSIZE]);
+ wp0 ^= wd0;
+ w20 = MASK(wq0);
+ w10 = SHLBYTE(wq0);
+ w20 &= NBYTES(0x1d);
+ w10 ^= w20;
+ wq0 = w10 ^ wd0;
+ }
+ put_unaligned_native(wp0, &p[d+NSIZE*0]);
+ put_unaligned_native(wq0, &q[d+NSIZE*0]);
+ }
+}
+
+static void xor_range(char *dst, const char*src, size_t size)
+{
+ /* Move to DWORD aligned */
+ while (size && ((unsigned long)dst & sizeof(unsigned long))) {
+ *dst++ ^= *src++;
+ size--;
+ }
+
+ /* DWORD aligned part */
+ while (size >= sizeof(unsigned long)) {
+ *(unsigned long *)dst ^= *(unsigned long *)src;
+ src += sizeof(unsigned long);
+ dst += sizeof(unsigned long);
+ size -= sizeof(unsigned long);
+ }
+ /* Remaining */
+ while (size) {
+ *dst++ ^= *src++;
+ size--;
+ }
+}
+
+/*
+ * Generate desired data/parity stripe for RAID5
+ *
+ * @nr_devs: Total number of devices, including parity
+ * @stripe_len: Stripe length
+ * @data: Data, with special layout:
+ * data[0]: Data stripe 0
+ * data[nr_devs-2]: Last data stripe
+ * data[nr_devs-1]: RAID5 parity
+ * @dest: To generate which data. should follow above data layout
+ */
+int raid5_gen_result(int nr_devs, size_t stripe_len, int dest, void **data)
+{
+ int i;
+ char *buf = data[dest];
+
+ /* Validation check */
+ if (stripe_len <= 0 || stripe_len != BTRFS_STRIPE_LEN) {
+ error("invalid parameter for %s", __func__);
+ return -EINVAL;
+ }
+
+ if (dest >= nr_devs || nr_devs < 2) {
+ error("invalid parameter for %s", __func__);
+ return -EINVAL;
+ }
+ /* Shortcut for 2 devs RAID5, which is just RAID1 */
+ if (nr_devs == 2) {
+ memcpy(data[dest], data[1 - dest], stripe_len);
+ return 0;
+ }
+ memset(buf, 0, stripe_len);
+ for (i = 0; i < nr_devs; i++) {
+ if (i == dest)
+ continue;
+ xor_range(buf, data[i], stripe_len);
+ }
+ return 0;
+}
+
+/*
+ * Raid 6 recovery code copied from kernel lib/raid6/recov.c.
+ * With modifications:
+ * - rename from raid6_2data_recov_intx1
+ * - kfree/free modification for btrfs-progs
+ */
+int raid6_recov_data2(int nr_devs, size_t stripe_len, int dest1, int dest2,
+ void **data)
+{
+ u8 *p, *q, *dp, *dq;
+ u8 px, qx, db;
+ const u8 *pbmul; /* P multiplier table for B data */
+ const u8 *qmul; /* Q multiplier table (for both) */
+ char *zero_mem1, *zero_mem2;
+ int ret = 0;
+
+ /* Early check */
+ if (dest1 < 0 || dest1 >= nr_devs - 2 ||
+ dest2 < 0 || dest2 >= nr_devs - 2 || dest1 >= dest2)
+ return -EINVAL;
+
+ zero_mem1 = calloc(1, stripe_len);
+ zero_mem2 = calloc(1, stripe_len);
+ if (!zero_mem1 || !zero_mem2) {
+ free(zero_mem1);
+ free(zero_mem2);
+ return -ENOMEM;
+ }
+
+ p = (u8 *)data[nr_devs - 2];
+ q = (u8 *)data[nr_devs - 1];
+
+ /* Compute syndrome with zero for the missing data pages
+ Use the dead data pages as temporary storage for
+ delta p and delta q */
+ dp = (u8 *)data[dest1];
+ data[dest1] = (void *)zero_mem1;
+ data[nr_devs - 2] = dp;
+ dq = (u8 *)data[dest2];
+ data[dest2] = (void *)zero_mem2;
+ data[nr_devs - 1] = dq;
+
+ raid6_gen_syndrome(nr_devs, stripe_len, data);
+
+ /* Restore pointer table */
+ data[dest1] = dp;
+ data[dest2] = dq;
+ data[nr_devs - 2] = p;
+ data[nr_devs - 1] = q;
+
+ /* Now, pick the proper data tables */
+ pbmul = raid6_gfmul[raid6_gfexi[dest2 - dest1]];
+ qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[dest1]^raid6_gfexp[dest2]]];
+
+ /* Now do it... */
+ while ( stripe_len-- ) {
+ px = *p ^ *dp;
+ qx = qmul[*q ^ *dq];
+ *dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */
+ *dp++ = db ^ px; /* Reconstructed A */
+ p++; q++;
+ }
+
+ free(zero_mem1);
+ free(zero_mem2);
+ return ret;
+}
+
+/*
+ * Raid 6 recover code copied from kernel lib/raid6/recov.c
+ * - rename from raid6_datap_recov_intx1()
+ * - parameter changed from faila to dest1
+ */
+int raid6_recov_datap(int nr_devs, size_t stripe_len, int dest1, void **data)
+{
+ u8 *p, *q, *dq;
+ const u8 *qmul; /* Q multiplier table */
+ char *zero_mem;
+
+ p = (u8 *)data[nr_devs - 2];
+ q = (u8 *)data[nr_devs - 1];
+
+ zero_mem = calloc(1, stripe_len);
+ if (!zero_mem)
+ return -ENOMEM;
+
+ /* Compute syndrome with zero for the missing data page
+ Use the dead data page as temporary storage for delta q */
+ dq = (u8 *)data[dest1];
+ data[dest1] = (void *)zero_mem;
+ data[nr_devs - 1] = dq;
+
+ raid6_gen_syndrome(nr_devs, stripe_len, data);
+
+ /* Restore pointer table */
+ data[dest1] = dq;
+ data[nr_devs - 1] = q;
+
+ /* Now, pick the proper data tables */
+ qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[dest1]]];
+
+ /* Now do it... */
+ while ( stripe_len-- ) {
+ *p++ ^= *dq = qmul[*q ^ *dq];
+ q++; dq++;
+ }
+ return 0;
+}
+
+/* Original raid56 recovery wrapper */
+int raid56_recov(int nr_devs, size_t stripe_len, u64 profile, int dest1,
+ int dest2, void **data)
+{
+ int min_devs;
+ int ret;
+
+ if (profile & BTRFS_BLOCK_GROUP_RAID5)
+ min_devs = 2;
+ else if (profile & BTRFS_BLOCK_GROUP_RAID6)
+ min_devs = 3;
+ else
+ return -EINVAL;
+ if (nr_devs < min_devs)
+ return -EINVAL;
+
+ /* Nothing to recover */
+ if (dest1 == -1 && dest2 == -1)
+ return 0;
+
+ /* Reorder dest1/2, so only dest2 can be -1 */
+ if (dest1 == -1) {
+ dest1 = dest2;
+ dest2 = -1;
+ } else if (dest2 != -1 && dest1 != -1) {
+ /* Reorder dest1/2, ensure dest2 > dest1 */
+ if (dest1 > dest2) {
+ int tmp;
+
+ tmp = dest2;
+ dest2 = dest1;
+ dest1 = tmp;
+ }
+ }
+
+ if (profile & BTRFS_BLOCK_GROUP_RAID5) {
+ if (dest2 != -1)
+ return 1;
+ return raid5_gen_result(nr_devs, stripe_len, dest1, data);
+ }
+
+ /* RAID6 one dev corrupted case*/
+ if (dest2 == -1) {
+ /* Regenerate P/Q */
+ if (dest1 == nr_devs - 1 || dest1 == nr_devs - 2) {
+ raid6_gen_syndrome(nr_devs, stripe_len, data);
+ return 0;
+ }
+
+ /* Regerneate data from P */
+ return raid5_gen_result(nr_devs - 1, stripe_len, dest1, data);
+ }
+
+ /* P/Q bot corrupted */
+ if (dest1 == nr_devs - 2 && dest2 == nr_devs - 1) {
+ raid6_gen_syndrome(nr_devs, stripe_len, data);
+ return 0;
+ }
+
+ /* 2 Data corrupted */
+ if (dest2 < nr_devs - 2)
+ return raid6_recov_data2(nr_devs, stripe_len, dest1, dest2,
+ data);
+ /* Data and P*/
+ if (dest2 == nr_devs - 1)
+ return raid6_recov_datap(nr_devs, stripe_len, dest1, data);
+
+ /*
+ * Final case, Data and Q, recover data first then regenerate Q
+ */
+ ret = raid5_gen_result(nr_devs - 1, stripe_len, dest1, data);
+ if (ret < 0)
+ return ret;
+ raid6_gen_syndrome(nr_devs, stripe_len, data);
+ return 0;
+}
diff --git a/kernel-lib/raid56.h b/kernel-lib/raid56.h
new file mode 100644
index 00000000..56d13f81
--- /dev/null
+++ b/kernel-lib/raid56.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+/*
+ * Original headers from kernel library for RAID5/6 calculations, not from
+ * btrfs kernel header.
+ */
+
+#ifndef __BTRFS_PROGS_RAID56_H__
+#define __BTRFS_PROGS_RAID56_H__
+
+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);
+
+/*
+ * Headers synchronized from kernel include/linux/raid/pq.h
+ * No modification at all.
+ *
+ * Galois field tables.
+ */
+extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256)));
+extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256)));
+extern const u8 raid6_gfexp[256] __attribute__((aligned(256)));
+extern const u8 raid6_gfinv[256] __attribute__((aligned(256)));
+extern const u8 raid6_gfexi[256] __attribute__((aligned(256)));
+
+/* Recover raid6 with 2 data stripes corrupted */
+int raid6_recov_data2(int nr_devs, size_t stripe_len, int dest1, int dest2,
+ void **data);
+
+/* Recover data and P */
+int raid6_recov_datap(int nr_devs, size_t stripe_len, int dest1, void **data);
+
+/*
+ * Recover raid56 data
+ * @dest1/2 can be -1 to indicate correct data
+ *
+ * Return >0 for unrecoverable case.
+ * Return 0 for recoverable case, And recovered data will be stored into @data
+ * Return <0 for fatal error
+ */
+int raid56_recov(int nr_devs, size_t stripe_len, u64 profile, int dest1,
+ int dest2, void **data);
+
+#endif
diff --git a/kernel-lib/sizes.h b/kernel-lib/sizes.h
new file mode 100644
index 00000000..ce3e8150
--- /dev/null
+++ b/kernel-lib/sizes.h
@@ -0,0 +1,47 @@
+/*
+ * include/linux/sizes.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_SIZES_H__
+#define __LINUX_SIZES_H__
+
+#define SZ_1 0x00000001
+#define SZ_2 0x00000002
+#define SZ_4 0x00000004
+#define SZ_8 0x00000008
+#define SZ_16 0x00000010
+#define SZ_32 0x00000020
+#define SZ_64 0x00000040
+#define SZ_128 0x00000080
+#define SZ_256 0x00000100
+#define SZ_512 0x00000200
+
+#define SZ_1K 0x00000400
+#define SZ_2K 0x00000800
+#define SZ_4K 0x00001000
+#define SZ_8K 0x00002000
+#define SZ_16K 0x00004000
+#define SZ_32K 0x00008000
+#define SZ_64K 0x00010000
+#define SZ_128K 0x00020000
+#define SZ_256K 0x00040000
+#define SZ_512K 0x00080000
+
+#define SZ_1M 0x00100000
+#define SZ_2M 0x00200000
+#define SZ_4M 0x00400000
+#define SZ_8M 0x00800000
+#define SZ_16M 0x01000000
+#define SZ_32M 0x02000000
+#define SZ_64M 0x04000000
+#define SZ_128M 0x08000000
+#define SZ_256M 0x10000000
+#define SZ_512M 0x20000000
+
+#define SZ_1G 0x40000000
+#define SZ_2G 0x80000000
+
+#endif /* __LINUX_SIZES_H__ */
diff --git a/ulist.c b/kernel-shared/ulist.c
index c5eca455..156a92fc 100644
--- a/ulist.c
+++ b/kernel-shared/ulist.c
@@ -4,8 +4,6 @@
* Distributed under the GNU GPL license version 2.
*/
-//#include <linux/slab.h>
-#include <stdlib.h>
#include "kerncompat.h"
#include "ulist.h"
#include "ctree.h"
@@ -134,6 +132,15 @@ static struct ulist_node *ulist_rbtree_search(struct ulist *ulist, u64 val)
return NULL;
}
+static void ulist_rbtree_erase(struct ulist *ulist, struct ulist_node *node)
+{
+ rb_erase(&node->rb_node, &ulist->root);
+ list_del(&node->list);
+ kfree(node);
+ BUG_ON(ulist->nnodes == 0);
+ ulist->nnodes--;
+}
+
static int ulist_rbtree_insert(struct ulist *ulist, struct ulist_node *ins)
{
struct rb_node **p = &ulist->root.rb_node;
@@ -199,9 +206,6 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
node->val = val;
node->aux = aux;
-#ifdef CONFIG_BTRFS_DEBUG
- node->seqnum = ulist->nnodes;
-#endif
ret = ulist_rbtree_insert(ulist, node);
ASSERT(!ret);
@@ -211,6 +215,33 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
return 1;
}
+/*
+ * ulist_del - delete one node from ulist
+ * @ulist: ulist to remove node from
+ * @val: value to delete
+ * @aux: aux to delete
+ *
+ * The deletion will only be done when *BOTH* val and aux matches.
+ * Return 0 for successful delete.
+ * Return > 0 for not found.
+ */
+int ulist_del(struct ulist *ulist, u64 val, u64 aux)
+{
+ struct ulist_node *node;
+
+ node = ulist_rbtree_search(ulist, val);
+ /* Not found */
+ if (!node)
+ return 1;
+
+ if (node->aux != aux)
+ return 1;
+
+ /* Found and delete */
+ ulist_rbtree_erase(ulist, node);
+ return 0;
+}
+
/**
* ulist_next - iterate ulist
* @ulist: ulist to iterate
@@ -239,15 +270,7 @@ struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_iterator *uiter)
uiter->cur_list = uiter->cur_list->next;
} else {
uiter->cur_list = ulist->nodes.next;
-#ifdef CONFIG_BTRFS_DEBUG
- uiter->i = 0;
-#endif
}
node = list_entry(uiter->cur_list, struct ulist_node, list);
-#ifdef CONFIG_BTRFS_DEBUG
- ASSERT(node->seqnum == uiter->i);
- ASSERT(uiter->i >= 0 && uiter->i < ulist->nnodes);
- uiter->i++;
-#endif
return node;
}
diff --git a/ulist.h b/kernel-shared/ulist.h
index 88f5b826..1ada7de2 100644
--- a/ulist.h
+++ b/kernel-shared/ulist.h
@@ -20,9 +20,6 @@
*
*/
struct ulist_iterator {
-#ifdef CONFIG_BTRFS_DEBUG
- int i;
-#endif
struct list_head *cur_list; /* hint to start search */
};
@@ -33,10 +30,6 @@ struct ulist_node {
u64 val; /* value to store */
u64 aux; /* auxiliary value saved along with the val */
-#ifdef CONFIG_BTRFS_DEBUG
- int seqnum; /* sequence number this node is added */
-#endif
-
struct list_head list; /* used to link node */
struct rb_node rb_node; /* used to speed up search */
};
@@ -58,6 +51,7 @@ void ulist_free(struct ulist *ulist);
int ulist_add(struct ulist *ulist, u64 val, u64 aux, gfp_t gfp_mask);
int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
u64 *old_aux, gfp_t gfp_mask);
+int ulist_del(struct ulist *ulist, u64 val, u64 aux);
/* just like ulist_add_merge() but take a pointer for the aux data */
static inline int ulist_add_merge_ptr(struct ulist *ulist, u64 val, void *aux,
diff --git a/library-test.c b/library-test.c
index 9d14bbf9..2be238d0 100644
--- a/library-test.c
+++ b/library-test.c
@@ -16,10 +16,45 @@
* Boston, MA 021110-1307, USA.
*/
+#if BTRFS_FLAT_INCLUDES
#include "kerncompat.h"
#include "version.h"
-#include "send-stream.h"
+#include "rbtree.h"
+#include "radix-tree.h"
+#include "crc32c.h"
+#include "list.h"
+#include "sizes.h"
+#include "ctree.h"
+#include "extent_io.h"
+#include "ioctl.h"
#include "btrfs-list.h"
+#include "btrfsck.h"
+#include "extent-cache.h"
+#include "send.h"
+#include "send-stream.h"
+#include "send-utils.h"
+#else
+/*
+ * This needs to include headers the same way as an external program but must
+ * not use the existing system headers, so we use "...".
+ */
+#include "btrfs/kerncompat.h"
+#include "btrfs/version.h"
+#include "btrfs/rbtree.h"
+#include "btrfs/radix-tree.h"
+#include "btrfs/crc32c.h"
+#include "btrfs/list.h"
+#include "btrfs/sizes.h"
+#include "btrfs/ctree.h"
+#include "btrfs/extent_io.h"
+#include "btrfs/ioctl.h"
+#include "btrfs/btrfs-list.h"
+#include "btrfs/btrfsck.h"
+#include "btrfs/extent-cache.h"
+#include "btrfs/send.h"
+#include "btrfs/send-stream.h"
+#include "btrfs/send-utils.h"
+#endif
/*
* Reduced code snippet from snapper.git/snapper/Btrfs.cc
diff --git a/messages.c b/messages.c
new file mode 100644
index 00000000..7600091f
--- /dev/null
+++ b/messages.c
@@ -0,0 +1,77 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "messages.h"
+
+__attribute__ ((format (printf, 1, 2)))
+void __btrfs_warning(const char *fmt, ...)
+{
+ va_list args;
+
+ fputs("WARNING: ", stderr);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+}
+
+__attribute__ ((format (printf, 1, 2)))
+void __btrfs_error(const char *fmt, ...)
+{
+ va_list args;
+
+ fputs("ERROR: ", stderr);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+}
+
+__attribute__ ((format (printf, 2, 3)))
+int __btrfs_warning_on(int condition, const char *fmt, ...)
+{
+ va_list args;
+
+ if (!condition)
+ return 0;
+
+ fputs("WARNING: ", stderr);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+
+ return 1;
+}
+
+__attribute__ ((format (printf, 2, 3)))
+int __btrfs_error_on(int condition, const char *fmt, ...)
+{
+ va_list args;
+
+ if (!condition)
+ return 0;
+
+ fputs("ERROR: ", stderr);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+
+ return 1;
+}
diff --git a/messages.h b/messages.h
new file mode 100644
index 00000000..a308b4b1
--- /dev/null
+++ b/messages.h
@@ -0,0 +1,85 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_MESSAGES_H__
+#define __BTRFS_MESSAGES_H__
+
+#if DEBUG_VERBOSE_ERROR
+#define PRINT_VERBOSE_ERROR fprintf(stderr, "%s:%d:", __FILE__, __LINE__)
+#else
+#define PRINT_VERBOSE_ERROR
+#endif
+
+#if DEBUG_TRACE_ON_ERROR
+#define PRINT_TRACE_ON_ERROR print_trace()
+#else
+#define PRINT_TRACE_ON_ERROR
+#endif
+
+#if DEBUG_ABORT_ON_ERROR
+#define DO_ABORT_ON_ERROR abort()
+#else
+#define DO_ABORT_ON_ERROR
+#endif
+
+#define error(fmt, ...) \
+ do { \
+ PRINT_TRACE_ON_ERROR; \
+ PRINT_VERBOSE_ERROR; \
+ __btrfs_error((fmt), ##__VA_ARGS__); \
+ DO_ABORT_ON_ERROR; \
+ } while (0)
+
+#define error_on(cond, fmt, ...) \
+ do { \
+ if ((cond)) \
+ PRINT_TRACE_ON_ERROR; \
+ if ((cond)) \
+ PRINT_VERBOSE_ERROR; \
+ __btrfs_error_on((cond), (fmt), ##__VA_ARGS__); \
+ if ((cond)) \
+ DO_ABORT_ON_ERROR; \
+ } while (0)
+
+#define warning(fmt, ...) \
+ do { \
+ PRINT_TRACE_ON_ERROR; \
+ PRINT_VERBOSE_ERROR; \
+ __btrfs_warning((fmt), ##__VA_ARGS__); \
+ } while (0)
+
+#define warning_on(cond, fmt, ...) \
+ do { \
+ if ((cond)) \
+ PRINT_TRACE_ON_ERROR; \
+ if ((cond)) \
+ PRINT_VERBOSE_ERROR; \
+ __btrfs_warning_on((cond), (fmt), ##__VA_ARGS__); \
+ } while (0)
+
+__attribute__ ((format (printf, 1, 2)))
+void __btrfs_warning(const char *fmt, ...);
+
+__attribute__ ((format (printf, 1, 2)))
+void __btrfs_error(const char *fmt, ...);
+
+__attribute__ ((format (printf, 2, 3)))
+int __btrfs_warning_on(int condition, const char *fmt, ...);
+
+__attribute__ ((format (printf, 2, 3)))
+int __btrfs_error_on(int condition, const char *fmt, ...);
+
+#endif
diff --git a/mkfs/common.c b/mkfs/common.c
new file mode 100644
index 00000000..e4785c58
--- /dev/null
+++ b/mkfs/common.c
@@ -0,0 +1,729 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <unistd.h>
+#include <uuid/uuid.h>
+#include <blkid/blkid.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "ctree.h"
+#include "disk-io.h"
+#include "volumes.h"
+#include "utils.h"
+#include "mkfs/common.h"
+
+static u64 reference_root_table[] = {
+ [1] = BTRFS_ROOT_TREE_OBJECTID,
+ [2] = BTRFS_EXTENT_TREE_OBJECTID,
+ [3] = BTRFS_CHUNK_TREE_OBJECTID,
+ [4] = BTRFS_DEV_TREE_OBJECTID,
+ [5] = BTRFS_FS_TREE_OBJECTID,
+ [6] = BTRFS_CSUM_TREE_OBJECTID,
+};
+
+/*
+ * @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID
+ *
+ * The superblock signature is not valid, denotes a partially created
+ * filesystem, needs to be finalized.
+ */
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
+{
+ struct btrfs_super_block super;
+ struct extent_buffer *buf;
+ struct btrfs_root_item root_item;
+ struct btrfs_disk_key disk_key;
+ struct btrfs_extent_item *extent_item;
+ struct btrfs_inode_item *inode_item;
+ struct btrfs_chunk *chunk;
+ struct btrfs_dev_item *dev_item;
+ struct btrfs_dev_extent *dev_extent;
+ u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+ u8 *ptr;
+ int i;
+ int ret;
+ u32 itemoff;
+ u32 nritems = 0;
+ u64 first_free;
+ u64 ref_root;
+ u32 array_size;
+ u32 item_size;
+ int skinny_metadata = !!(cfg->features &
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
+ u64 num_bytes;
+
+ buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
+ if (!buf)
+ return -ENOMEM;
+
+ first_free = BTRFS_SUPER_INFO_OFFSET + cfg->sectorsize * 2 - 1;
+ first_free &= ~((u64)cfg->sectorsize - 1);
+
+ memset(&super, 0, sizeof(super));
+
+ num_bytes = (cfg->num_bytes / cfg->sectorsize) * cfg->sectorsize;
+ if (*cfg->fs_uuid) {
+ if (uuid_parse(cfg->fs_uuid, super.fsid) != 0) {
+ error("cannot not parse UUID: %s", cfg->fs_uuid);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!test_uuid_unique(cfg->fs_uuid)) {
+ error("non-unique UUID: %s", cfg->fs_uuid);
+ ret = -EBUSY;
+ goto out;
+ }
+ } else {
+ uuid_generate(super.fsid);
+ uuid_unparse(super.fsid, cfg->fs_uuid);
+ }
+ uuid_generate(super.dev_item.uuid);
+ uuid_generate(chunk_tree_uuid);
+
+ cfg->blocks[0] = BTRFS_SUPER_INFO_OFFSET;
+ for (i = 1; i < 7; i++) {
+ cfg->blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 +
+ cfg->nodesize * i;
+ }
+
+ btrfs_set_super_bytenr(&super, cfg->blocks[0]);
+ btrfs_set_super_num_devices(&super, 1);
+ btrfs_set_super_magic(&super, BTRFS_MAGIC_PARTIAL);
+ btrfs_set_super_generation(&super, 1);
+ btrfs_set_super_root(&super, cfg->blocks[1]);
+ btrfs_set_super_chunk_root(&super, cfg->blocks[3]);
+ btrfs_set_super_total_bytes(&super, num_bytes);
+ btrfs_set_super_bytes_used(&super, 6 * cfg->nodesize);
+ btrfs_set_super_sectorsize(&super, cfg->sectorsize);
+ super.__unused_leafsize = cpu_to_le32(cfg->nodesize);
+ btrfs_set_super_nodesize(&super, cfg->nodesize);
+ btrfs_set_super_stripesize(&super, cfg->stripesize);
+ btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
+ btrfs_set_super_chunk_root_generation(&super, 1);
+ btrfs_set_super_cache_generation(&super, -1);
+ btrfs_set_super_incompat_flags(&super, cfg->features);
+ if (cfg->label)
+ __strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
+
+ /* create the tree of root objects */
+ memset(buf->data, 0, cfg->nodesize);
+ buf->len = cfg->nodesize;
+ btrfs_set_header_bytenr(buf, cfg->blocks[1]);
+ btrfs_set_header_nritems(buf, 4);
+ btrfs_set_header_generation(buf, 1);
+ btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
+ btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID);
+ write_extent_buffer(buf, super.fsid, btrfs_header_fsid(),
+ BTRFS_FSID_SIZE);
+
+ write_extent_buffer(buf, chunk_tree_uuid,
+ btrfs_header_chunk_tree_uuid(buf),
+ BTRFS_UUID_SIZE);
+
+ /* create the items for the root tree */
+ memset(&root_item, 0, sizeof(root_item));
+ inode_item = &root_item.inode;
+ btrfs_set_stack_inode_generation(inode_item, 1);
+ btrfs_set_stack_inode_size(inode_item, 3);
+ btrfs_set_stack_inode_nlink(inode_item, 1);
+ btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
+ btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
+ btrfs_set_root_refs(&root_item, 1);
+ btrfs_set_root_used(&root_item, cfg->nodesize);
+ btrfs_set_root_generation(&root_item, 1);
+
+ memset(&disk_key, 0, sizeof(disk_key));
+ btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+ nritems = 0;
+
+ itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - sizeof(root_item);
+ btrfs_set_root_bytenr(&root_item, cfg->blocks[2]);
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID);
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
+ sizeof(root_item));
+ write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf,
+ nritems), sizeof(root_item));
+ nritems++;
+
+ itemoff = itemoff - sizeof(root_item);
+ btrfs_set_root_bytenr(&root_item, cfg->blocks[4]);
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID);
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
+ sizeof(root_item));
+ write_extent_buffer(buf, &root_item,
+ btrfs_item_ptr_offset(buf, nritems),
+ sizeof(root_item));
+ nritems++;
+
+ itemoff = itemoff - sizeof(root_item);
+ btrfs_set_root_bytenr(&root_item, cfg->blocks[5]);
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID);
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
+ sizeof(root_item));
+ write_extent_buffer(buf, &root_item,
+ btrfs_item_ptr_offset(buf, nritems),
+ sizeof(root_item));
+ nritems++;
+
+ itemoff = itemoff - sizeof(root_item);
+ btrfs_set_root_bytenr(&root_item, cfg->blocks[6]);
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID);
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
+ sizeof(root_item));
+ write_extent_buffer(buf, &root_item,
+ btrfs_item_ptr_offset(buf, nritems),
+ sizeof(root_item));
+ nritems++;
+
+
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[1]);
+ if (ret != cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+
+ /* create the items for the extent tree */
+ memset(buf->data + sizeof(struct btrfs_header), 0,
+ cfg->nodesize - sizeof(struct btrfs_header));
+ nritems = 0;
+ itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ for (i = 1; i < 7; i++) {
+ item_size = sizeof(struct btrfs_extent_item);
+ if (!skinny_metadata)
+ item_size += sizeof(struct btrfs_tree_block_info);
+
+ if (cfg->blocks[i] < first_free) {
+ error("block[%d] below first free: %llu < %llu",
+ i, (unsigned long long)cfg->blocks[i],
+ (unsigned long long)first_free);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (cfg->blocks[i] < cfg->blocks[i - 1]) {
+ error("blocks %d and %d in reverse order: %llu < %llu",
+ i, i - 1,
+ (unsigned long long)cfg->blocks[i],
+ (unsigned long long)cfg->blocks[i - 1]);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* create extent item */
+ itemoff -= item_size;
+ btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]);
+ if (skinny_metadata) {
+ btrfs_set_disk_key_type(&disk_key,
+ BTRFS_METADATA_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+ } else {
+ btrfs_set_disk_key_type(&disk_key,
+ BTRFS_EXTENT_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
+ }
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
+ itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
+ item_size);
+ extent_item = btrfs_item_ptr(buf, nritems,
+ struct btrfs_extent_item);
+ btrfs_set_extent_refs(buf, extent_item, 1);
+ btrfs_set_extent_generation(buf, extent_item, 1);
+ btrfs_set_extent_flags(buf, extent_item,
+ BTRFS_EXTENT_FLAG_TREE_BLOCK);
+ nritems++;
+
+ /* create extent ref */
+ ref_root = reference_root_table[i];
+ btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]);
+ btrfs_set_disk_key_offset(&disk_key, ref_root);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY);
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
+ itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0);
+ nritems++;
+ }
+ btrfs_set_header_bytenr(buf, cfg->blocks[2]);
+ btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID);
+ btrfs_set_header_nritems(buf, nritems);
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[2]);
+ if (ret != cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+
+ /* create the chunk tree */
+ memset(buf->data + sizeof(struct btrfs_header), 0,
+ cfg->nodesize - sizeof(struct btrfs_header));
+ nritems = 0;
+ item_size = sizeof(*dev_item);
+ itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - item_size;
+
+ /* first device 1 (there is no device 0) */
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
+ btrfs_set_disk_key_offset(&disk_key, 1);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
+
+ dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item);
+ btrfs_set_device_id(buf, dev_item, 1);
+ btrfs_set_device_generation(buf, dev_item, 0);
+ btrfs_set_device_total_bytes(buf, dev_item, num_bytes);
+ btrfs_set_device_bytes_used(buf, dev_item,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE);
+ btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_type(buf, dev_item, 0);
+
+ write_extent_buffer(buf, super.dev_item.uuid,
+ (unsigned long)btrfs_device_uuid(dev_item),
+ BTRFS_UUID_SIZE);
+ write_extent_buffer(buf, super.fsid,
+ (unsigned long)btrfs_device_fsid(dev_item),
+ BTRFS_UUID_SIZE);
+ read_extent_buffer(buf, &super.dev_item, (unsigned long)dev_item,
+ sizeof(*dev_item));
+
+ nritems++;
+ item_size = btrfs_chunk_item_size(1);
+ itemoff = itemoff - item_size;
+
+ /* then we have chunk 0 */
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
+
+ chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk);
+ btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
+ btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID);
+ btrfs_set_chunk_stripe_len(buf, chunk, BTRFS_STRIPE_LEN);
+ btrfs_set_chunk_type(buf, chunk, BTRFS_BLOCK_GROUP_SYSTEM);
+ btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_num_stripes(buf, chunk, 1);
+ btrfs_set_stripe_devid_nr(buf, chunk, 0, 1);
+ btrfs_set_stripe_offset_nr(buf, chunk, 0, 0);
+ nritems++;
+
+ write_extent_buffer(buf, super.dev_item.uuid,
+ (unsigned long)btrfs_stripe_dev_uuid(&chunk->stripe),
+ BTRFS_UUID_SIZE);
+
+ /* copy the key for the chunk to the system array */
+ ptr = super.sys_chunk_array;
+ array_size = sizeof(disk_key);
+
+ memcpy(ptr, &disk_key, sizeof(disk_key));
+ ptr += sizeof(disk_key);
+
+ /* copy the chunk to the system array */
+ read_extent_buffer(buf, ptr, (unsigned long)chunk, item_size);
+ array_size += item_size;
+ ptr += item_size;
+ btrfs_set_super_sys_array_size(&super, array_size);
+
+ btrfs_set_header_bytenr(buf, cfg->blocks[3]);
+ btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID);
+ btrfs_set_header_nritems(buf, nritems);
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[3]);
+ if (ret != cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+
+ /* create the device tree */
+ memset(buf->data + sizeof(struct btrfs_header), 0,
+ cfg->nodesize - sizeof(struct btrfs_header));
+ nritems = 0;
+ itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) -
+ sizeof(struct btrfs_dev_extent);
+
+ btrfs_set_disk_key_objectid(&disk_key, 1);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
+ btrfs_set_item_key(buf, &disk_key, nritems);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
+ sizeof(struct btrfs_dev_extent));
+ dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent);
+ btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
+ BTRFS_CHUNK_TREE_OBJECTID);
+ btrfs_set_dev_extent_chunk_objectid(buf, dev_extent,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ btrfs_set_dev_extent_chunk_offset(buf, dev_extent, 0);
+
+ write_extent_buffer(buf, chunk_tree_uuid,
+ (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent),
+ BTRFS_UUID_SIZE);
+
+ btrfs_set_dev_extent_length(buf, dev_extent,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE);
+ nritems++;
+
+ btrfs_set_header_bytenr(buf, cfg->blocks[4]);
+ btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID);
+ btrfs_set_header_nritems(buf, nritems);
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[4]);
+ if (ret != cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+
+ /* create the FS root */
+ memset(buf->data + sizeof(struct btrfs_header), 0,
+ cfg->nodesize - sizeof(struct btrfs_header));
+ btrfs_set_header_bytenr(buf, cfg->blocks[5]);
+ btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID);
+ btrfs_set_header_nritems(buf, 0);
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[5]);
+ if (ret != cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+ /* finally create the csum root */
+ memset(buf->data + sizeof(struct btrfs_header), 0,
+ cfg->nodesize - sizeof(struct btrfs_header));
+ btrfs_set_header_bytenr(buf, cfg->blocks[6]);
+ btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID);
+ btrfs_set_header_nritems(buf, 0);
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[6]);
+ if (ret != cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+
+ /* and write out the super block */
+ memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
+ memcpy(buf->data, &super, sizeof(super));
+ buf->len = BTRFS_SUPER_INFO_SIZE;
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, cfg->blocks[0]);
+ if (ret != BTRFS_SUPER_INFO_SIZE) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ free(buf);
+ return ret;
+}
+
+u64 btrfs_min_dev_size(u32 nodesize)
+{
+ return 2 * (BTRFS_MKFS_SYSTEM_GROUP_SIZE +
+ btrfs_min_global_blk_rsv_size(nodesize));
+}
+
+/*
+ * Btrfs minimum size calculation is complicated, it should include at least:
+ * 1. system group size
+ * 2. minimum global block reserve
+ * 3. metadata used at mkfs
+ * 4. space reservation to create uuid for first mount.
+ * Also, raid factor should also be taken into consideration.
+ * To avoid the overkill calculation, (system group + global block rsv) * 2
+ * for *EACH* device should be good enough.
+ */
+u64 btrfs_min_global_blk_rsv_size(u32 nodesize)
+{
+ return (u64)nodesize << 10;
+}
+
+#define isoctal(c) (((c) & ~7) == '0')
+
+static inline void translate(char *f, char *t)
+{
+ while (*f != '\0') {
+ if (*f == '\\' &&
+ isoctal(f[1]) && isoctal(f[2]) && isoctal(f[3])) {
+ *t++ = 64*(f[1] & 7) + 8*(f[2] & 7) + (f[3] & 7);
+ f += 4;
+ } else
+ *t++ = *f++;
+ }
+ *t = '\0';
+ return;
+}
+
+/*
+ * Checks if the swap device.
+ * Returns 1 if swap device, < 0 on error or 0 if not swap device.
+ */
+static int is_swap_device(const char *file)
+{
+ FILE *f;
+ struct stat st_buf;
+ dev_t dev;
+ ino_t ino = 0;
+ char tmp[PATH_MAX];
+ char buf[PATH_MAX];
+ char *cp;
+ int ret = 0;
+
+ if (stat(file, &st_buf) < 0)
+ return -errno;
+ if (S_ISBLK(st_buf.st_mode))
+ dev = st_buf.st_rdev;
+ else if (S_ISREG(st_buf.st_mode)) {
+ dev = st_buf.st_dev;
+ ino = st_buf.st_ino;
+ } else
+ return 0;
+
+ if ((f = fopen("/proc/swaps", "r")) == NULL)
+ return 0;
+
+ /* skip the first line */
+ if (fgets(tmp, sizeof(tmp), f) == NULL)
+ goto out;
+
+ while (fgets(tmp, sizeof(tmp), f) != NULL) {
+ if ((cp = strchr(tmp, ' ')) != NULL)
+ *cp = '\0';
+ if ((cp = strchr(tmp, '\t')) != NULL)
+ *cp = '\0';
+ translate(tmp, buf);
+ if (stat(buf, &st_buf) != 0)
+ continue;
+ if (S_ISBLK(st_buf.st_mode)) {
+ if (dev == st_buf.st_rdev) {
+ ret = 1;
+ break;
+ }
+ } else if (S_ISREG(st_buf.st_mode)) {
+ if (dev == st_buf.st_dev && ino == st_buf.st_ino) {
+ ret = 1;
+ break;
+ }
+ }
+ }
+
+out:
+ fclose(f);
+
+ return ret;
+}
+
+/*
+ * Check for existing filesystem or partition table on device.
+ * Returns:
+ * 1 for existing fs or partition
+ * 0 for nothing found
+ * -1 for internal error
+ */
+static int check_overwrite(const char *device)
+{
+ const char *type;
+ blkid_probe pr = NULL;
+ int ret;
+ blkid_loff_t size;
+
+ if (!device || !*device)
+ return 0;
+
+ ret = -1; /* will reset on success of all setup calls */
+
+ pr = blkid_new_probe_from_filename(device);
+ if (!pr)
+ goto out;
+
+ size = blkid_probe_get_size(pr);
+ if (size < 0)
+ goto out;
+
+ /* nothing to overwrite on a 0-length device */
+ if (size == 0) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = blkid_probe_enable_partitions(pr, 1);
+ if (ret < 0)
+ goto out;
+
+ ret = blkid_do_fullprobe(pr);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Blkid returns 1 for nothing found and 0 when it finds a signature,
+ * but we want the exact opposite, so reverse the return value here.
+ *
+ * In addition print some useful diagnostics about what actually is
+ * on the device.
+ */
+ if (ret) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
+ fprintf(stderr,
+ "%s appears to contain an existing "
+ "filesystem (%s).\n", device, type);
+ } else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
+ fprintf(stderr,
+ "%s appears to contain a partition "
+ "table (%s).\n", device, type);
+ } else {
+ fprintf(stderr,
+ "%s appears to contain something weird "
+ "according to blkid\n", device);
+ }
+ ret = 1;
+
+out:
+ if (pr)
+ blkid_free_probe(pr);
+ if (ret == -1)
+ fprintf(stderr,
+ "probe of %s failed, cannot detect "
+ "existing filesystem.\n", device);
+ return ret;
+}
+
+/*
+ * Check if a device is suitable for btrfs
+ * returns:
+ * 1: something is wrong, an error is printed
+ * 0: all is fine
+ */
+int test_dev_for_mkfs(const char *file, int force_overwrite)
+{
+ int ret, fd;
+ struct stat st;
+
+ ret = is_swap_device(file);
+ if (ret < 0) {
+ error("checking status of %s: %s", file, strerror(-ret));
+ return 1;
+ }
+ if (ret == 1) {
+ error("%s is a swap device", file);
+ return 1;
+ }
+ if (!force_overwrite) {
+ if (check_overwrite(file)) {
+ error("use the -f option to force overwrite of %s",
+ file);
+ return 1;
+ }
+ }
+ ret = check_mounted(file);
+ if (ret < 0) {
+ error("cannot check mount status of %s: %s", file,
+ strerror(-ret));
+ return 1;
+ }
+ if (ret == 1) {
+ error("%s is mounted", file);
+ return 1;
+ }
+ /* check if the device is busy */
+ fd = open(file, O_RDWR|O_EXCL);
+ if (fd < 0) {
+ error("unable to open %s: %s", file, strerror(errno));
+ return 1;
+ }
+ if (fstat(fd, &st)) {
+ error("unable to stat %s: %s", file, strerror(errno));
+ close(fd);
+ return 1;
+ }
+ if (!S_ISBLK(st.st_mode)) {
+ error("%s is not a block device", file);
+ close(fd);
+ return 1;
+ }
+ close(fd);
+ return 0;
+}
+
+int is_vol_small(const char *file)
+{
+ int fd = -1;
+ int e;
+ struct stat st;
+ u64 size;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+ if (fstat(fd, &st) < 0) {
+ e = -errno;
+ close(fd);
+ return e;
+ }
+ size = btrfs_device_size(fd, &st);
+ if (size == 0) {
+ close(fd);
+ return -1;
+ }
+ if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) {
+ close(fd);
+ return 1;
+ } else {
+ close(fd);
+ return 0;
+ }
+}
+
+int test_minimum_size(const char *file, u32 nodesize)
+{
+ int fd;
+ struct stat statbuf;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+ if (stat(file, &statbuf) < 0) {
+ close(fd);
+ return -errno;
+ }
+ if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(nodesize)) {
+ close(fd);
+ return 1;
+ }
+ close(fd);
+ return 0;
+}
+
+
diff --git a/mkfs/common.h b/mkfs/common.h
new file mode 100644
index 00000000..666a75b3
--- /dev/null
+++ b/mkfs/common.h
@@ -0,0 +1,63 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+/*
+ * Defines and function declarations for users of the mkfs API, no internal
+ * defintions.
+ */
+
+#ifndef __BTRFS_MKFS_COMMON_H__
+#define __BTRFS_MKFS_COMMON_H__
+
+#include "kerncompat.h"
+#include "common-defs.h"
+
+#define BTRFS_MKFS_SYSTEM_GROUP_SIZE SZ_4M
+#define BTRFS_MKFS_SMALL_VOLUME_SIZE SZ_1G
+
+struct btrfs_mkfs_config {
+ /* Label of the new filesystem */
+ const char *label;
+ /* Blck sizes */
+ u32 nodesize;
+ u32 sectorsize;
+ u32 stripesize;
+ /* Bitfield of incompat features, BTRFS_FEATURE_INCOMPAT_* */
+ u64 features;
+ /* Size of the filesystem in bytes */
+ u64 num_bytes;
+
+ /* Output fields, set during creation */
+
+ /* Logical addresses of superblock [0] and other tree roots */
+ u64 blocks[8];
+ char fs_uuid[BTRFS_UUID_UNPARSED_SIZE];
+ char chunk_uuid[BTRFS_UUID_UNPARSED_SIZE];
+
+ /* Superblock offset after make_btrfs */
+ u64 super_bytenr;
+};
+
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg);
+u64 btrfs_min_dev_size(u32 nodesize);
+u64 btrfs_min_global_blk_rsv_size(u32 nodesize);
+int test_minimum_size(const char *file, u32 nodesize);
+int is_vol_small(const char *file);
+int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
+ u64 dev_cnt, int mixed, int ssd);
+int test_dev_for_mkfs(const char *file, int force_overwrite);
+
+#endif
diff --git a/mkfs/main.c b/mkfs/main.c
index 72834c9c..2b109a53 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -43,6 +43,9 @@
#include "transaction.h"
#include "utils.h"
#include "list_sort.h"
+#include "help.h"
+#include "mkfs/common.h"
+#include "fsfeatures.h"
static u64 index_cnt = 2;
static int verbose = 1;
@@ -64,6 +67,7 @@ struct mkfs_allocation {
static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
struct mkfs_allocation *allocation)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_trans_handle *trans;
u64 bytes_used;
u64 chunk_start = 0;
@@ -71,10 +75,10 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
int ret;
trans = btrfs_start_transaction(root, 1);
- bytes_used = btrfs_super_bytes_used(root->fs_info->super_copy);
+ bytes_used = btrfs_super_bytes_used(fs_info->super_copy);
root->fs_info->system_allocs = 1;
- ret = btrfs_make_block_group(trans, root, bytes_used,
+ ret = btrfs_make_block_group(trans, fs_info, bytes_used,
BTRFS_BLOCK_GROUP_SYSTEM,
BTRFS_FIRST_CHUNK_TREE_OBJECTID,
0, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
@@ -83,7 +87,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
return ret;
if (mixed) {
- ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
+ ret = btrfs_alloc_chunk(trans, fs_info,
&chunk_start, &chunk_size,
BTRFS_BLOCK_GROUP_METADATA |
BTRFS_BLOCK_GROUP_DATA);
@@ -93,7 +97,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
}
if (ret)
return ret;
- ret = btrfs_make_block_group(trans, root, 0,
+ ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_METADATA |
BTRFS_BLOCK_GROUP_DATA,
BTRFS_FIRST_CHUNK_TREE_OBJECTID,
@@ -102,7 +106,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
return ret;
allocation->mixed += chunk_size;
} else {
- ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
+ ret = btrfs_alloc_chunk(trans, fs_info,
&chunk_start, &chunk_size,
BTRFS_BLOCK_GROUP_METADATA);
if (ret == -ENOSPC) {
@@ -111,7 +115,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
}
if (ret)
return ret;
- ret = btrfs_make_block_group(trans, root, 0,
+ ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_METADATA,
BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
@@ -131,12 +135,13 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int mixed,
struct mkfs_allocation *allocation)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
u64 chunk_start = 0;
u64 chunk_size = 0;
int ret = 0;
if (!mixed) {
- ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
+ ret = btrfs_alloc_chunk(trans, fs_info,
&chunk_start, &chunk_size,
BTRFS_BLOCK_GROUP_DATA);
if (ret == -ENOSPC) {
@@ -145,7 +150,7 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans,
}
if (ret)
return ret;
- ret = btrfs_make_block_group(trans, root, 0,
+ ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_DATA,
BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
@@ -158,8 +163,8 @@ err:
return ret;
}
-static int make_root_dir(struct btrfs_trans_handle *trans, struct btrfs_root *root,
- struct mkfs_allocation *allocation)
+static int make_root_dir(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root)
{
struct btrfs_key location;
int ret;
@@ -241,11 +246,12 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
struct mkfs_allocation *allocation)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
u64 chunk_start;
u64 chunk_size;
int ret;
- ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
+ ret = btrfs_alloc_chunk(trans, fs_info,
&chunk_start, &chunk_size, type);
if (ret == -ENOSPC) {
error("not enough free space to allocate chunk");
@@ -254,7 +260,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
if (ret)
return ret;
- ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0,
+ ret = btrfs_make_block_group(trans, fs_info, 0,
type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
@@ -448,7 +454,7 @@ static int fill_inode_item(struct btrfs_trans_handle *trans,
struct btrfs_inode_item *dst, struct stat *src)
{
u64 blocks = 0;
- u64 sectorsize = root->sectorsize;
+ u64 sectorsize = root->fs_info->sectorsize;
/*
* btrfs_inode_item has some reserved fields
@@ -541,8 +547,8 @@ static u64 calculate_dir_inode_size(const char *dirname)
static int add_inode_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct stat *st, const char *name,
- u64 self_objectid, ino_t parent_inum,
- int dir_index_cnt, struct btrfs_inode_item *inode_ret)
+ u64 self_objectid,
+ struct btrfs_inode_item *inode_ret)
{
int ret;
struct btrfs_inode_item btrfs_inode;
@@ -642,15 +648,14 @@ fail:
static int add_file_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_inode_item *btrfs_inode, u64 objectid,
- ino_t parent_inum, struct stat *st,
- const char *path_name, int out_fd)
+ struct stat *st, const char *path_name)
{
int ret = -1;
ssize_t ret_read;
u64 bytes_read = 0;
struct btrfs_key key;
int blocks;
- u32 sectorsize = root->sectorsize;
+ u32 sectorsize = root->fs_info->sectorsize;
u64 first_block = 0;
u64 file_pos = 0;
u64 cur_bytes;
@@ -751,7 +756,7 @@ again:
if (ret)
goto end;
- ret = write_and_map_eb(trans, root, eb);
+ ret = write_and_map_eb(root->fs_info, eb);
if (ret) {
error("failed to write %s", path_name);
goto end;
@@ -901,7 +906,6 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
ret = add_inode_items(trans, root, &st,
cur_file->d_name, cur_inum,
- parent_inum, dir_index_cnt,
&cur_inode);
if (ret == -EEXIST) {
if (st.st_nlink <= 1) {
@@ -941,8 +945,8 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
list_add_tail(&dir_entry->list, &dir_head->list);
} else if (S_ISREG(st.st_mode)) {
ret = add_file_items(trans, root, &cur_inode,
- cur_inum, parent_inum, &st,
- cur_file->d_name, out_fd);
+ cur_inum, &st,
+ cur_file->d_name);
if (ret) {
error("unable to add file items for %s: %d",
cur_file->d_name, ret);
@@ -983,6 +987,7 @@ static int create_chunks(struct btrfs_trans_handle *trans,
u64 size_of_data,
struct mkfs_allocation *allocation)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
u64 chunk_start;
u64 chunk_size;
u64 meta_type = BTRFS_BLOCK_GROUP_METADATA;
@@ -992,35 +997,35 @@ static int create_chunks(struct btrfs_trans_handle *trans,
int ret;
for (i = 0; i < num_of_meta_chunks; i++) {
- ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
+ ret = btrfs_alloc_chunk(trans, fs_info,
&chunk_start, &chunk_size, meta_type);
if (ret)
return ret;
- ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0,
+ ret = btrfs_make_block_group(trans, fs_info, 0,
meta_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
allocation->metadata += chunk_size;
if (ret)
return ret;
set_extent_dirty(&root->fs_info->free_space_cache,
- chunk_start, chunk_start + chunk_size - 1, 0);
+ chunk_start, chunk_start + chunk_size - 1);
}
if (size_of_data < minimum_data_chunk_size)
size_of_data = minimum_data_chunk_size;
- ret = btrfs_alloc_data_chunk(trans, root->fs_info->extent_root,
+ ret = btrfs_alloc_data_chunk(trans, fs_info,
&chunk_start, size_of_data, data_type, 0);
if (ret)
return ret;
- ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0,
+ ret = btrfs_make_block_group(trans, fs_info, 0,
data_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, size_of_data);
allocation->data += size_of_data;
if (ret)
return ret;
set_extent_dirty(&root->fs_info->free_space_cache,
- chunk_start, chunk_start + size_of_data - 1, 0);
+ chunk_start, chunk_start + size_of_data - 1);
return ret;
}
@@ -1397,7 +1402,6 @@ int main(int argc, char **argv)
char *label = NULL;
u64 block_count = 0;
u64 dev_block_count = 0;
- u64 blocks[7];
u64 alloc_start = 0;
u64 metadata_profile = 0;
u64 data_profile = 0;
@@ -1720,12 +1724,6 @@ int main(int argc, char **argv)
exit(1);
}
- blocks[0] = BTRFS_SUPER_INFO_OFFSET;
- for (i = 1; i < 7; i++) {
- blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 +
- nodesize * i;
- }
-
if (group_profile_max_safe_loss(metadata_profile) <
group_profile_max_safe_loss(data_profile)){
warning("metadata has lower redundancy than data!\n");
@@ -1733,7 +1731,6 @@ int main(int argc, char **argv)
mkfs_cfg.label = label;
memcpy(mkfs_cfg.fs_uuid, fs_uuid, sizeof(mkfs_cfg.fs_uuid));
- memcpy(mkfs_cfg.blocks, blocks, sizeof(blocks));
mkfs_cfg.num_bytes = dev_block_count;
mkfs_cfg.nodesize = nodesize;
mkfs_cfg.sectorsize = sectorsize;
@@ -1774,7 +1771,7 @@ int main(int argc, char **argv)
exit(1);
}
- ret = make_root_dir(trans, root, &allocation);
+ ret = make_root_dir(trans, root);
if (ret) {
error("failed to setup the root directory: %d", ret);
exit(1);
diff --git a/print-tree.c b/print-tree.c
index 5af80e87..5927ed35 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -197,10 +197,16 @@ static void qgroup_flags_to_str(u64 flags, char *ret)
void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk)
{
- int num_stripes = btrfs_chunk_num_stripes(eb, chunk);
+ u16 num_stripes = btrfs_chunk_num_stripes(eb, chunk);
int i;
+ u32 chunk_item_size = btrfs_chunk_item_size(num_stripes);
char chunk_flags_str[32] = {0};
+ if ((unsigned long)chunk + chunk_item_size > eb->len) {
+ printf("\t\tchunk item invalid\n");
+ return;
+ }
+
bg_flags_to_str(btrfs_chunk_type(eb, chunk), chunk_flags_str);
printf("\t\tlength %llu owner %llu stripe_len %llu type %s\n",
(unsigned long long)btrfs_chunk_length(eb, chunk),
@@ -216,9 +222,21 @@ void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk)
for (i = 0 ; i < num_stripes ; i++) {
unsigned char dev_uuid[BTRFS_UUID_SIZE];
char str_dev_uuid[BTRFS_UUID_UNPARSED_SIZE];
+ u64 uuid_offset;
+ u64 stripe_offset;
+
+ uuid_offset = (unsigned long)btrfs_stripe_dev_uuid_nr(chunk, i);
+ stripe_offset = (unsigned long)btrfs_stripe_nr(chunk, i);
+
+ if (uuid_offset < stripe_offset ||
+ (uuid_offset + BTRFS_UUID_SIZE) >
+ (stripe_offset + sizeof(struct btrfs_stripe))) {
+ printf("\t\t\tstripe %d invalid\n", i);
+ break;
+ }
read_extent_buffer(eb, dev_uuid,
- (unsigned long)btrfs_stripe_dev_uuid_nr(chunk, i),
+ uuid_offset,
BTRFS_UUID_SIZE);
uuid_unparse(dev_uuid, str_dev_uuid);
printf("\t\t\tstripe %d devid %llu offset %llu\n", i,
@@ -318,7 +336,7 @@ static void print_file_extent_item(struct extent_buffer *eb,
int slot,
struct btrfs_file_extent_item *fi)
{
- int extent_type = btrfs_file_extent_type(eb, fi);
+ unsigned char extent_type = btrfs_file_extent_type(eb, fi);
char compress_str[16];
compress_type_to_str(btrfs_file_extent_compression(eb, fi),
@@ -938,13 +956,35 @@ static void print_dev_stats(struct extent_buffer *eb,
}
}
+/* Caller must ensure sizeof(*ret) >= 14 "WRITTEN|RELOC" */
+static void header_flags_to_str(u64 flags, char *ret)
+{
+ int empty = 1;
+
+ if (flags & BTRFS_HEADER_FLAG_WRITTEN) {
+ empty = 0;
+ strcpy(ret, "WRITTEN");
+ }
+ if (flags & BTRFS_HEADER_FLAG_RELOC) {
+ if (!empty)
+ strcat(ret, "|");
+ strcat(ret, "RELOC");
+ }
+}
+
void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *eb)
{
struct btrfs_item *item;
struct btrfs_disk_key disk_key;
+ char flags_str[128];
u32 i;
u32 nr;
+ u64 flags;
+ u8 backref_rev;
+ flags = btrfs_header_flags(eb) & ~BTRFS_BACKREF_REV_MASK;
+ backref_rev = btrfs_header_flags(eb) >> BTRFS_BACKREF_REV_SHIFT;
+ header_flags_to_str(flags, flags_str);
nr = btrfs_header_nritems(eb);
printf("leaf %llu items %d free space %d generation %llu owner %llu\n",
@@ -952,6 +992,8 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *eb)
btrfs_leaf_free_space(root, eb),
(unsigned long long)btrfs_header_generation(eb),
(unsigned long long)btrfs_header_owner(eb));
+ printf("leaf %llu flags 0x%llx(%s) backref revision %d\n",
+ btrfs_header_bytenr(eb), flags, flags_str, backref_rev);
print_uuids(eb);
fflush(stdout);
@@ -1255,7 +1297,7 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol
(unsigned long long)btrfs_header_owner(eb));
print_uuids(eb);
fflush(stdout);
- size = root->nodesize;
+ size = root->fs_info->nodesize;
for (i = 0; i < nr; i++) {
u64 blocknr = btrfs_node_blockptr(eb, i);
btrfs_node_key(eb, &disk_key, i);
@@ -1272,7 +1314,8 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol
return;
for (i = 0; i < nr; i++) {
- next = read_tree_block(root, btrfs_node_blockptr(eb, i), size,
+ next = read_tree_block(root->fs_info,
+ 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",
diff --git a/qgroup-verify.c b/qgroup-verify.c
index ff46bc4c..5162adbb 100644
--- a/qgroup-verify.c
+++ b/qgroup-verify.c
@@ -27,7 +27,7 @@
#include "disk-io.h"
#include "print-tree.h"
#include "utils.h"
-#include "ulist.h"
+#include "kernel-shared/ulist.h"
#include "rbtree-utils.h"
#include "transaction.h"
#include "repair.h"
@@ -711,7 +711,7 @@ static int travel_tree(struct btrfs_fs_info *info, struct btrfs_root *root,
// printf("travel_tree: bytenr: %llu\tnum_bytes: %llu\tref_parent: %llu\n",
// bytenr, num_bytes, ref_parent);
- eb = read_tree_block(root, bytenr, num_bytes, 0);
+ eb = read_tree_block(info, bytenr, num_bytes, 0);
if (!extent_buffer_uptodate(eb))
return -EIO;
@@ -736,7 +736,7 @@ static int travel_tree(struct btrfs_fs_info *info, struct btrfs_root *root,
nr = btrfs_header_nritems(eb);
for (i = 0; i < nr; i++) {
new_bytenr = btrfs_node_blockptr(eb, i);
- new_num_bytes = root->nodesize;
+ new_num_bytes = info->nodesize;
ret = travel_tree(info, root, new_bytenr, new_num_bytes,
ref_parent);
@@ -1185,7 +1185,7 @@ static int scan_extents(struct btrfs_fs_info *info,
bytenr = key.objectid;
num_bytes = key.offset;
if (key.type == BTRFS_METADATA_ITEM_KEY) {
- num_bytes = info->extent_root->nodesize;
+ num_bytes = info->nodesize;
meta = 1;
}
diff --git a/raid56.c b/raid56.c
deleted file mode 100644
index 8c79c456..00000000
--- a/raid56.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * Added helpers for unaligned native int access
- */
-
-/*
- * raid6int1.c
- *
- * 1-way unrolled portable integer math RAID-6 instruction set
- *
- * This file was postprocessed using unroll.pl and then ported to userspace
- */
-#include <stdint.h>
-#include <unistd.h>
-#include "kerncompat.h"
-#include "ctree.h"
-#include "disk-io.h"
-#include "volumes.h"
-#include "utils.h"
-
-/*
- * This is the C data type to use
- */
-
-/* Change this from BITS_PER_LONG if there is something better... */
-#if BITS_PER_LONG == 64
-# define NBYTES(x) ((x) * 0x0101010101010101UL)
-# define NSIZE 8
-# define NSHIFT 3
-typedef uint64_t unative_t;
-#define put_unaligned_native(val,p) put_unaligned_64((val),(p))
-#define get_unaligned_native(p) get_unaligned_64((p))
-#else
-# define NBYTES(x) ((x) * 0x01010101U)
-# define NSIZE 4
-# define NSHIFT 2
-typedef uint32_t unative_t;
-#define put_unaligned_native(val,p) put_unaligned_32((val),(p))
-#define get_unaligned_native(p) get_unaligned_32((p))
-#endif
-
-/*
- * These sub-operations are separate inlines since they can sometimes be
- * specially optimized using architecture-specific hacks.
- */
-
-/*
- * The SHLBYTE() operation shifts each byte left by 1, *not*
- * rolling over into the next byte
- */
-static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
-{
- unative_t vv;
-
- vv = (v << 1) & NBYTES(0xfe);
- return vv;
-}
-
-/*
- * The MASK() operation returns 0xFF in any byte for which the high
- * bit is 1, 0x00 for any byte for which the high bit is 0.
- */
-static inline __attribute_const__ unative_t MASK(unative_t v)
-{
- unative_t vv;
-
- vv = v & NBYTES(0x80);
- vv = (vv << 1) - (vv >> 7); /* Overflow on the top bit is OK */
- return vv;
-}
-
-
-void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- uint8_t **dptr = (uint8_t **)ptrs;
- uint8_t *p, *q;
- int d, z, z0;
-
- unative_t wd0, wq0, wp0, w10, w20;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- for ( d = 0 ; d < bytes ; d += NSIZE*1 ) {
- wq0 = wp0 = get_unaligned_native(&dptr[z0][d+0*NSIZE]);
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- wd0 = get_unaligned_native(&dptr[z][d+0*NSIZE]);
- wp0 ^= wd0;
- w20 = MASK(wq0);
- w10 = SHLBYTE(wq0);
- w20 &= NBYTES(0x1d);
- w10 ^= w20;
- wq0 = w10 ^ wd0;
- }
- put_unaligned_native(wp0, &p[d+NSIZE*0]);
- put_unaligned_native(wq0, &q[d+NSIZE*0]);
- }
-}
-
-static void xor_range(char *dst, const char*src, size_t size)
-{
- /* Move to DWORD aligned */
- while (size && ((unsigned long)dst & sizeof(unsigned long))) {
- *dst++ ^= *src++;
- size--;
- }
-
- /* DWORD aligned part */
- while (size >= sizeof(unsigned long)) {
- *(unsigned long *)dst ^= *(unsigned long *)src;
- src += sizeof(unsigned long);
- dst += sizeof(unsigned long);
- size -= sizeof(unsigned long);
- }
- /* Remaining */
- while (size) {
- *dst++ ^= *src++;
- size--;
- }
-}
-
-/*
- * Generate desired data/parity stripe for RAID5
- *
- * @nr_devs: Total number of devices, including parity
- * @stripe_len: Stripe length
- * @data: Data, with special layout:
- * data[0]: Data stripe 0
- * data[nr_devs-2]: Last data stripe
- * data[nr_devs-1]: RAID5 parity
- * @dest: To generate which data. should follow above data layout
- */
-int raid5_gen_result(int nr_devs, size_t stripe_len, int dest, void **data)
-{
- int i;
- char *buf = data[dest];
-
- /* Validation check */
- if (stripe_len <= 0 || stripe_len != BTRFS_STRIPE_LEN) {
- error("invalid parameter for %s", __func__);
- return -EINVAL;
- }
-
- if (dest >= nr_devs || nr_devs < 2) {
- error("invalid parameter for %s", __func__);
- return -EINVAL;
- }
- /* Shortcut for 2 devs RAID5, which is just RAID1 */
- if (nr_devs == 2) {
- memcpy(data[dest], data[1 - dest], stripe_len);
- return 0;
- }
- memset(buf, 0, stripe_len);
- for (i = 0; i < nr_devs; i++) {
- if (i == dest)
- continue;
- xor_range(buf, data[i], stripe_len);
- }
- return 0;
-}
diff --git a/root-tree.c b/root-tree.c
index ab01a140..6b8f8c1c 100644
--- a/root-tree.c
+++ b/root-tree.c
@@ -51,7 +51,8 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
l = path->nodes[0];
slot = path->slots[0] - 1;
btrfs_item_key_to_cpu(l, &found_key, slot);
- if (found_key.objectid != objectid) {
+ if (found_key.type != BTRFS_ROOT_ITEM_KEY ||
+ found_key.objectid != objectid) {
ret = -ENOENT;
goto out;
}
diff --git a/send-dump.c b/send-dump.c
index 4c44246f..1591e0cc 100644
--- a/send-dump.c
+++ b/send-dump.c
@@ -112,11 +112,14 @@ static int __print_dump(int subvol, void *user, const char *path,
/* Unified header */
printf("%-16s", title);
ret = print_path_escaped(out_path);
- if (!fmt)
+ if (!fmt) {
+ putchar('\n');
return 0;
- /* Short paths ale aligned to 32 chars */
- while (ret++ < 32)
+ }
+ /* Short paths are aligned to 32 chars; longer paths get a single space */
+ do {
putchar(' ');
+ } while (++ret < 32);
va_start(args, fmt);
/* Operation specified ones */
vprintf(fmt, args);
diff --git a/send-stream.c b/send-stream.c
index 5a028cd9..78f2571a 100644
--- a/send-stream.c
+++ b/send-stream.c
@@ -492,7 +492,7 @@ int btrfs_read_and_process_send_stream(int fd,
if (ret < 0)
goto out;
if (ret) {
- ret = 1;
+ ret = -ENODATA;
goto out;
}
diff --git a/send.h b/send.h
index ae9aa029..fe613cbb 100644
--- a/send.h
+++ b/send.h
@@ -29,7 +29,7 @@ extern "C" {
#define BTRFS_SEND_STREAM_MAGIC "btrfs-stream"
#define BTRFS_SEND_STREAM_VERSION 1
-#define BTRFS_SEND_BUF_SIZE (1024 * 64)
+#define BTRFS_SEND_BUF_SIZE SZ_64K
#define BTRFS_SEND_READ_SIZE (1024 * 48)
enum btrfs_tlv_type {
diff --git a/super-recover.c b/super-recover.c
index 5298d46c..6b80416f 100644
--- a/super-recover.c
+++ b/super-recover.c
@@ -302,7 +302,7 @@ int btrfs_recover_superblocks(const char *dname,
}
/* reset super_bytenr in order that we will rewrite all supers */
root->fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;
- ret = write_all_supers(root);
+ ret = write_all_supers(root->fs_info);
if (!ret)
ret = 2;
else
diff --git a/tests/README.md b/tests/README.md
index bb2846a1..a8d3a2ba 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -182,3 +182,37 @@ description of the problem or the stack trace.
If you have a fix for the problem, please submit it prior to the test image, so
the fuzz tests always succeed when run on random checked out. This helps
bisectability.
+
+
+# Coding style, best practices
+
+## do
+
+* quote all variables by default, any path, even the TOP could need that, and
+ we use it everywhere
+ * there are exceptions:
+ * `$SUDO_HELPER` as it might be intentionally unset
+ * the variable is obviously set to a value that does not require it
+* use `#!/bin/bash` explicitly
+* check for all external dependencies (`check_prereq_global`)
+* check for internal dependencies (`check_prereq`), though the basic set is
+ always built when the tests are started through make
+* use functions instead of repeating code
+ * generic helpers could be factored to the `common` script
+* cleanup after successful test
+* use common helpers and variables
+
+## do not
+
+* pull external dependencies if we can find a way to replace them: example is
+ `xfs_io` that's conveniently used in fstests but we'd require `xfsprogs`,
+ so use `dd` instead
+* throw away (redirect to */dev/null*) output of commands unless it's justified
+ (ie. really too much text, unnecessary slowdown) -- the test output log is
+ regenerated all the time and we need to be able to analyze test failures or
+ just observe how the tests progress
+* cleanup after failed test -- the testsuite stops on first failure and the
+ developer can eg. access the environment that the test created and do further
+ debugging
+ * this might change in the future so the tests cover as much as possible, but
+ this would require to enhance all tests with a cleanup phase
diff --git a/tests/build-tests.sh b/tests/build-tests.sh
index 04e3fd19..4dc8744e 100755
--- a/tests/build-tests.sh
+++ b/tests/build-tests.sh
@@ -63,6 +63,9 @@ function build_make_targets() {
# defaults, library
target="library-test"
buildme
+ # defaults, static library
+ target="library-test.static"
+ buildme
}
# main()
diff --git a/tests/cli-tests/002-balance-full-no-filters/test.sh b/tests/cli-tests/002-balance-full-no-filters/test.sh
index 81a719eb..0501aad2 100755
--- a/tests/cli-tests/002-balance-full-no-filters/test.sh
+++ b/tests/cli-tests/002-balance-full-no-filters/test.sh
@@ -10,7 +10,7 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev 2g
-run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
run_check $SUDO_HELPER "$TOP/btrfs" balance start --full-balance "$TEST_MNT"
diff --git a/tests/cli-tests/003-fi-resize-args/test.sh b/tests/cli-tests/003-fi-resize-args/test.sh
index b835e078..e4f262b6 100755
--- a/tests/cli-tests/003-fi-resize-args/test.sh
+++ b/tests/cli-tests/003-fi-resize-args/test.sh
@@ -10,7 +10,7 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev 2g
-run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
# missing the one of the required arguments
diff --git a/tests/cli-tests/004-send-parent-multi-subvol/test.sh b/tests/cli-tests/004-send-parent-multi-subvol/test.sh
index 72a9eb36..49226f9b 100755
--- a/tests/cli-tests/004-send-parent-multi-subvol/test.sh
+++ b/tests/cli-tests/004-send-parent-multi-subvol/test.sh
@@ -10,7 +10,7 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev 2g
-run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
here=`pwd`
diff --git a/tests/cli-tests/005-qgroup-show/test.sh b/tests/cli-tests/005-qgroup-show/test.sh
index 46d3c3a7..2af13033 100755
--- a/tests/cli-tests/005-qgroup-show/test.sh
+++ b/tests/cli-tests/005-qgroup-show/test.sh
@@ -10,7 +10,7 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev 2g
-run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
run_mayfail "$TOP/btrfs" qgroup show "$TEST_MNT"
run_mayfail $SUDO_HELPER "$TOP/btrfs" qgroup show "$TEST_MNT"
diff --git a/tests/cli-tests/006-qgroup-show-sync/test.sh b/tests/cli-tests/006-qgroup-show-sync/test.sh
index 30d0a9a1..d552b8b9 100755
--- a/tests/cli-tests/006-qgroup-show-sync/test.sh
+++ b/tests/cli-tests/006-qgroup-show-sync/test.sh
@@ -10,7 +10,7 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev 1g
-run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/Sub"
diff --git a/tests/common b/tests/common
index 51c2e267..bed60094 100644
--- a/tests/common
+++ b/tests/common
@@ -106,7 +106,7 @@ run_check()
ins=$(_get_spec_ins "$@")
spec=$(($ins-1))
cmd=$(eval echo "\${$spec}")
- spec=$(_cmd_spec "$cmd")
+ spec=$(_cmd_spec "${@:$spec}")
set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
echo "############### $@" >> "$RESULTS" 2>&1
if [[ $TEST_LOG =~ tty ]]; then echo "CMD: $@" > /dev/tty; fi
@@ -128,7 +128,7 @@ run_check_stdout()
ins=$(_get_spec_ins "$@")
spec=$(($ins-1))
cmd=$(eval echo "\${$spec}")
- spec=$(_cmd_spec "$cmd")
+ spec=$(_cmd_spec "${@:$spec}")
set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
echo "############### $@" >> "$RESULTS" 2>&1
if [[ $TEST_LOG =~ tty ]]; then echo "CMD(stdout): $@" > /dev/tty; fi
@@ -152,7 +152,7 @@ run_mayfail()
ins=$(_get_spec_ins "$@")
spec=$(($ins-1))
cmd=$(eval echo "\${$spec}")
- spec=$(_cmd_spec "$cmd")
+ spec=$(_cmd_spec "${@:$spec}")
set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
echo "############### $@" >> "$RESULTS" 2>&1
if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mayfail): $@" > /dev/tty; fi
@@ -188,7 +188,7 @@ run_mustfail()
ins=$(_get_spec_ins "$@")
spec=$(($ins-1))
cmd=$(eval echo "\${$spec}")
- spec=$(_cmd_spec "$cmd")
+ spec=$(_cmd_spec "${@:$spec}")
set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
echo "############### $@" >> "$RESULTS" 2>&1
if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): $@" > /dev/tty; fi
@@ -387,7 +387,7 @@ run_check_mount_test_dev()
_fail "Invalid \$TEST_MNT: $TEST_MNT"
}
- run_check $SUDO_HELPER mount $loop_opt "$@" "$TEST_DEV" "$TEST_MNT"
+ run_check $SUDO_HELPER mount -t btrfs $loop_opt "$@" "$TEST_DEV" "$TEST_MNT"
}
run_check_umount_test_dev()
diff --git a/tests/common.convert b/tests/common.convert
index 8c9242e5..2c19a4be 100644
--- a/tests/common.convert
+++ b/tests/common.convert
@@ -1,6 +1,39 @@
#!/bin/bash
# helpers for btrfs-convert tests
+# mount image of converted filesystem of a given type
+# $1: type of the filesystem
+run_check_mount_convert_dev()
+{
+ local fstype
+ local loop_opt
+
+ setup_root_helper
+
+ fstype="$1"
+ shift
+ if [ -z "$fstype" ]; then
+ _fail "Missing source filesystem type"
+ fi
+ if [ "$fstype" = 'btrfs' ]; then
+ _fail "Incorrect type for converted filesystem: btrfs"
+ fi
+
+ if [[ -b "$TEST_DEV" ]]; then
+ loop_opt=""
+ elif [[ -f "$TEST_DEV" ]]; then
+ loop_opt="-o loop"
+ else
+ _fail "Invalid \$TEST_DEV: $TEST_DEV"
+ fi
+
+ [[ -d "$TEST_MNT" ]] || {
+ _fail "Invalid \$TEST_MNT: $TEST_MNT"
+ }
+
+ run_check $SUDO_HELPER mount $loop_opt -t "$fstype" "$@" "$TEST_DEV" "$TEST_MNT"
+}
+
populate_fs() {
for dataset_type in 'small' 'hardlink' 'fast_symlink' 'brokenlink' 'perm' 'sparse' 'acls' 'fifo' 'slow_symlink'; do
@@ -22,8 +55,13 @@ convert_test_preamble() {
# prepare TEST_DEV before conversion, create filesystem and mount it, image
# size is 512MB
-# $@: free form, command to create the filesystem, with appended -F
+# $1: type of the filesystem
+# $2+: free form, command to create the filesystem, with appended -F
convert_test_prep_fs() {
+ local fstype
+
+ fstype="$1"
+ shift
# TEST_DEV not removed as the file might have special permissions, eg.
# when test image is on NFS and would not be writable for root
run_check truncate -s 0 "$TEST_DEV"
@@ -32,7 +70,7 @@ convert_test_prep_fs() {
run_check "$@" -F "$TEST_DEV"
# create a file to check btrfs-convert can convert regular file correct
- run_check_mount_test_dev
+ run_check_mount_convert_dev "$fstype"
# create a file inside the fs before convert, to make sure there is
# data covering btrfs backup superblock range (64M)
@@ -173,11 +211,13 @@ convert_test_post_rollback() {
}
# simple wrapper for a convert test
-# $1: btrfs features, argument to -O
-# $2: description of the test "ext2 8k nodesize"
-# $3: nodesize value
-# $4 + rest: command to create the ext2 image
+# $1: type of the converted filesystem
+# $2: btrfs features, argument to -O
+# $3: description of the test "ext2 8k nodesize"
+# $4: nodesize value
+# $5 + rest: command to create the ext2 image
convert_test() {
+ local fstype
local features
local nodesize
local msg
@@ -185,12 +225,13 @@ convert_test() {
local EXT_PERMTMP
local EXT_ACLTMP
- features="$1"
- msg="$2"
- nodesize="$3"
- shift 3
+ fstype="$1"
+ features="$2"
+ msg="$3"
+ nodesize="$4"
+ shift 4
convert_test_preamble "$features" "$msg" "$nodesize" "$@"
- convert_test_prep_fs "$@"
+ convert_test_prep_fs "$fstype" "$@"
populate_fs
CHECKSUMTMP=$(mktemp --tmpdir btrfs-progs-convert.XXXXXXXXXX)
EXT_PERMTMP=$(mktemp --tmpdir btrfs-progs-convert.permXXXXXX)
diff --git a/tests/common.local b/tests/common.local
index 9f567c27..4f56bb08 100644
--- a/tests/common.local
+++ b/tests/common.local
@@ -17,7 +17,7 @@ TEST_ARGS_CHECK=--mode=lowmem
# break tests
_skip_spec()
{
- if echo "$TEST_CHECK" | grep -q 'mode=lowmem' &&
+ if echo "$TEST_ARGS_CHECK" | grep -q 'mode=lowmem' &&
echo "$@" | grep -q -- '--repair'; then
return 0
fi
diff --git a/tests/convert-tests/001-ext2-basic/test.sh b/tests/convert-tests/001-ext2-basic/test.sh
index 8f4f935d..7d8e87d8 100755
--- a/tests/convert-tests/001-ext2-basic/test.sh
+++ b/tests/convert-tests/001-ext2-basic/test.sh
@@ -1,16 +1,16 @@
#!/bin/bash
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
setup_root_helper
prepare_test_dev 512M
check_prereq btrfs-convert
for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
- convert_test "$feature" "ext2 4k nodesize" 4096 mke2fs -b 4096
- convert_test "$feature" "ext2 8k nodesize" 8192 mke2fs -b 4096
- convert_test "$feature" "ext2 16k nodesize" 16384 mke2fs -b 4096
- convert_test "$feature" "ext2 32k nodesize" 32768 mke2fs -b 4096
- convert_test "$feature" "ext2 64k nodesize" 65536 mke2fs -b 4096
+ convert_test ext2 "$feature" "ext2 4k nodesize" 4096 mke2fs -b 4096
+ convert_test ext2 "$feature" "ext2 8k nodesize" 8192 mke2fs -b 4096
+ convert_test ext2 "$feature" "ext2 16k nodesize" 16384 mke2fs -b 4096
+ convert_test ext2 "$feature" "ext2 32k nodesize" 32768 mke2fs -b 4096
+ convert_test ext2 "$feature" "ext2 64k nodesize" 65536 mke2fs -b 4096
done
diff --git a/tests/convert-tests/002-ext3-basic/test.sh b/tests/convert-tests/002-ext3-basic/test.sh
index aeb111eb..5a33c2ca 100755
--- a/tests/convert-tests/002-ext3-basic/test.sh
+++ b/tests/convert-tests/002-ext3-basic/test.sh
@@ -1,16 +1,16 @@
#!/bin/bash
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
setup_root_helper
prepare_test_dev 512M
check_prereq btrfs-convert
for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
- convert_test "$feature" "ext3 4k nodesize" 4096 mke2fs -j -b 4096
- convert_test "$feature" "ext3 8k nodesize" 8192 mke2fs -j -b 4096
- convert_test "$feature" "ext3 16k nodesize" 16384 mke2fs -j -b 4096
- convert_test "$feature" "ext3 32k nodesize" 32768 mke2fs -j -b 4096
- convert_test "$feature" "ext3 64k nodesize" 65536 mke2fs -j -b 4096
+ convert_test ext3 "$feature" "ext3 4k nodesize" 4096 mke2fs -j -b 4096
+ convert_test ext3 "$feature" "ext3 8k nodesize" 8192 mke2fs -j -b 4096
+ convert_test ext3 "$feature" "ext3 16k nodesize" 16384 mke2fs -j -b 4096
+ convert_test ext3 "$feature" "ext3 32k nodesize" 32768 mke2fs -j -b 4096
+ convert_test ext3 "$feature" "ext3 64k nodesize" 65536 mke2fs -j -b 4096
done
diff --git a/tests/convert-tests/003-ext4-basic/test.sh b/tests/convert-tests/003-ext4-basic/test.sh
index 531c81bd..df8bec28 100755
--- a/tests/convert-tests/003-ext4-basic/test.sh
+++ b/tests/convert-tests/003-ext4-basic/test.sh
@@ -1,16 +1,16 @@
#!/bin/bash
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
setup_root_helper
prepare_test_dev 512M
check_prereq btrfs-convert
for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
- convert_test "$feature" "ext4 4k nodesize" 4096 mke2fs -t ext4 -b 4096
- convert_test "$feature" "ext4 8k nodesize" 8192 mke2fs -t ext4 -b 4096
- convert_test "$feature" "ext4 16k nodesize" 16384 mke2fs -t ext4 -b 4096
- convert_test "$feature" "ext4 32k nodesize" 32768 mke2fs -t ext4 -b 4096
- convert_test "$feature" "ext4 64k nodesize" 65536 mke2fs -t ext4 -b 4096
+ convert_test ext4 "$feature" "ext4 4k nodesize" 4096 mke2fs -t ext4 -b 4096
+ convert_test ext4 "$feature" "ext4 8k nodesize" 8192 mke2fs -t ext4 -b 4096
+ convert_test ext4 "$feature" "ext4 16k nodesize" 16384 mke2fs -t ext4 -b 4096
+ convert_test ext4 "$feature" "ext4 32k nodesize" 32768 mke2fs -t ext4 -b 4096
+ convert_test ext4 "$feature" "ext4 64k nodesize" 65536 mke2fs -t ext4 -b 4096
done
diff --git a/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh b/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
index c56650b2..0ce62f78 100755
--- a/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
+++ b/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
@@ -10,7 +10,7 @@
# 4) Overlap file extents
# 5) Unable to rollback
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs-convert
check_prereq btrfs
@@ -23,20 +23,20 @@ prepare_test_dev 512M
# override common function
function check_image() {
TEST_DEV="$1"
- run_check e2fsck -n -f $TEST_DEV
- run_check $TOP/btrfs-convert $TEST_DEV
- run_check $TOP/btrfs check $TEST_DEV
- run_check $TOP/btrfs inspect-internal dump-super $TEST_DEV
+ run_check e2fsck -n -f "$TEST_DEV"
+ run_check "$TOP/btrfs-convert" "$TEST_DEV"
+ run_check "$TOP/btrfs" check "$TEST_DEV"
+ run_check "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV"
run_check_mount_test_dev
- run_check $SUDO_HELPER e2fsck -n -f $TEST_MNT/ext2_saved/image
- run_check $SUDO_HELPER umount $TEST_MNT
+ run_check $SUDO_HELPER e2fsck -n -f "$TEST_MNT/ext2_saved/image"
+ run_check $SUDO_HELPER umount "$TEST_MNT"
- run_check $TOP/btrfs check $TEST_DEV
- run_check $TOP/btrfs-convert -r $TEST_DEV
- run_check e2fsck -n -f $TEST_DEV
+ run_check "$TOP/btrfs" check "$TEST_DEV"
+ run_check "$TOP/btrfs-convert" -r "$TEST_DEV"
+ run_check e2fsck -n -f "$TEST_DEV"
- rm -f $TEST_DEV
+ rm -f "$TEST_DEV"
}
check_all_images
diff --git a/tests/convert-tests/005-delete-all-rollback/test.sh b/tests/convert-tests/005-delete-all-rollback/test.sh
index cf576e70..337413bb 100755
--- a/tests/convert-tests/005-delete-all-rollback/test.sh
+++ b/tests/convert-tests/005-delete-all-rollback/test.sh
@@ -2,8 +2,8 @@
# create a base image, convert to btrfs, remove all files, rollback the ext4 image
# note: ext4 only
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
setup_root_helper
prepare_test_dev 512M
@@ -26,7 +26,7 @@ do_test() {
nodesize="$3"
shift 3
convert_test_preamble "$features" "$msg" "$nodesize" "$@"
- convert_test_prep_fs "$@"
+ convert_test_prep_fs ext4 "$@"
populate_fs
CHECKSUMTMP=$(mktemp --tmpdir btrfs-progs-convert.XXXXXXXXXX)
convert_test_gen_checksums "$CHECKSUMTMP"
@@ -43,16 +43,16 @@ do_test() {
# ext2_saved/image must not be deleted
run_mayfail $SUDO_HELPER find "$TEST_MNT"/ -mindepth 1 -path '*ext2_saved' -prune -o -exec rm -vrf "{}" \;
cd "$here"
- run_check $TOP/btrfs filesystem sync "$TEST_MNT"
+ run_check "$TOP/btrfs" filesystem sync "$TEST_MNT"
run_check_umount_test_dev
convert_test_post_rollback
- run_check_mount_test_dev
+ run_check_mount_convert_dev ext4
convert_test_post_check_checksums "$CHECKSUMTMP"
run_check_umount_test_dev
# mount again and verify checksums
- run_check_mount_test_dev
+ run_check_mount_convert_dev ext4
convert_test_post_check_checksums "$CHECKSUMTMP"
run_check_umount_test_dev
diff --git a/tests/convert-tests/006-large-hole-extent/test.sh b/tests/convert-tests/006-large-hole-extent/test.sh
index d3bc093c..f63a1186 100755
--- a/tests/convert-tests/006-large-hole-extent/test.sh
+++ b/tests/convert-tests/006-large-hole-extent/test.sh
@@ -5,8 +5,8 @@
# Fast pinpoint regression test. No options combination nor checksum
# verification
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
setup_root_helper
prepare_test_dev 512M
@@ -14,9 +14,9 @@ check_prereq btrfs-convert
default_mke2fs="mke2fs -t ext4 -b 4096"
convert_test_preamble '' 'large hole extent test' 16k "$default_mke2fs"
-convert_test_prep_fs $default_mke2fs
+convert_test_prep_fs ext4 $default_mke2fs
-run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/file bs=1M \
+run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/file" bs=1M \
count=1 seek=1024 > /dev/null 2>&1
run_check_umount_test_dev
diff --git a/tests/convert-tests/007-unsupported-block-sizes/test.sh b/tests/convert-tests/007-unsupported-block-sizes/test.sh
index 9ba17751..af8ec357 100755
--- a/tests/convert-tests/007-unsupported-block-sizes/test.sh
+++ b/tests/convert-tests/007-unsupported-block-sizes/test.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# Check if block sizes smaller than 4k expectedly fail to convert
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
setup_root_helper
prepare_test_dev 512M
@@ -11,11 +11,11 @@ check_prereq btrfs-convert
for bs in 1024 2048; do
default_mke2fs="mke2fs -t ext4 -b $bs"
convert_test_preamble '' "unsupported block size $bs" 16k "$default_mke2fs"
- convert_test_prep_fs $default_mke2fs
+ convert_test_prep_fs ext4 $default_mke2fs
- run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/file bs=1M \
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/file" bs=1M \
count=1 seek=1024 > /dev/null 2>&1
run_check_umount_test_dev
- run_mustfail "$bs block converted" $TOP/btrfs-convert $TEST_DEV
+ run_mustfail "$bs block converted" "$TOP/btrfs-convert" "$TEST_DEV"
done
diff --git a/tests/convert-tests/008-readonly-image/test.sh b/tests/convert-tests/008-readonly-image/test.sh
index 4e422378..4d5e629f 100755
--- a/tests/convert-tests/008-readonly-image/test.sh
+++ b/tests/convert-tests/008-readonly-image/test.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# Check if the converted ext2 image is readonly
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
setup_root_helper
prepare_test_dev 512M
@@ -10,13 +10,13 @@ check_prereq btrfs-convert
default_mke2fs="mke2fs -t ext4 -b 4096"
convert_test_preamble '' 'readonly image test' 16k "$default_mke2fs"
-convert_test_prep_fs $default_mke2fs
+convert_test_prep_fs ext4 $default_mke2fs
run_check_umount_test_dev
convert_test_do_convert
run_check_mount_test_dev
# It's expected to fail
-$SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/ext2_save/image bs=1M count=1 \
+$SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/ext2_save/image" bs=1M count=1 \
&> /dev/null
if [ $? -ne 1 ]; then
echo "after convert ext2_save/image is not read-only"
diff --git a/tests/convert-tests/009-common-inode-flags/test.sh b/tests/convert-tests/009-common-inode-flags/test.sh
index 6f26d187..f42fb681 100755
--- a/tests/convert-tests/009-common-inode-flags/test.sh
+++ b/tests/convert-tests/009-common-inode-flags/test.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# Check if btrfs-convert can copy common inode flags like SYNC/IMMUTABLE
-source $TOP/tests/common
-source $TOP/tests/common.convert
+source "$TOP/tests/common"
+source "$TOP/tests/common.convert"
setup_root_helper
prepare_test_dev 512M
@@ -11,20 +11,20 @@ check_prereq btrfs-convert
fail=0
default_mke2fs="mke2fs -t ext4 -b 4096"
convert_test_preamble '' 'common inode flags test' 16k "$default_mke2fs"
-convert_test_prep_fs $default_mke2fs
+convert_test_prep_fs ext4 $default_mke2fs
# create file with specific flags
-run_check $SUDO_HELPER touch $TEST_MNT/flag_test
-run_check $SUDO_HELPER chattr +aAdSi $TEST_MNT/flag_test
+run_check $SUDO_HELPER touch "$TEST_MNT/flag_test"
+run_check $SUDO_HELPER chattr +aAdSi "$TEST_MNT/flag_test"
run_check_umount_test_dev
convert_test_do_convert
run_check_mount_test_dev
# Log the status
-run_check lsattr $TEST_MNT/flag_test
+run_check lsattr "$TEST_MNT/flag_test"
# Above flags should be copied to btrfs flags, and lsattr should get them
-run_check_stdout lsattr $TEST_MNT/flag_test | cut -f1 -d\ | grep "[aAdiS]" -q
+run_check_stdout lsattr "$TEST_MNT/flag_test" | cut -f1 -d\ | grep "[aAdiS]" -q
if [ $? -ne 0 ]; then
rm tmp_output
_fail "no common inode flags are copied after convert"
diff --git a/tests/fsck-tests.sh b/tests/fsck-tests.sh
index 44cca1b8..15d26c70 100755
--- a/tests/fsck-tests.sh
+++ b/tests/fsck-tests.sh
@@ -23,6 +23,7 @@ rm -f "$RESULTS"
check_prereq btrfs-corrupt-block
check_prereq btrfs-image
check_prereq btrfs
+check_prereq btrfstune
check_kernel_support
run_one_test() {
diff --git a/tests/fsck-tests/006-bad-root-items/test.sh b/tests/fsck-tests/006-bad-root-items/test.sh
index 84332348..bf3ef781 100755
--- a/tests/fsck-tests/006-bad-root-items/test.sh
+++ b/tests/fsck-tests/006-bad-root-items/test.sh
@@ -1,15 +1,15 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
-echo "extracting image default_case.tar.xz" >> $RESULTS
+echo "extracting image default_case.tar.xz" >> "$RESULTS"
tar --no-same-owner -xJf default_case.tar.xz || \
_fail "failed to extract default_case.tar.xz"
check_image test.img
-echo "extracting image skinny_case.tar.xz" >> $RESULTS
+echo "extracting image skinny_case.tar.xz" >> "$RESULTS"
tar --no-same-owner -xJf skinny_case.tar.xz || \
_fail "failed to extract skinny_case.tar.xz"
check_image test.img
diff --git a/tests/fsck-tests/012-leaf-corruption/test.sh b/tests/fsck-tests/012-leaf-corruption/test.sh
index a308727d..fc10a4ff 100755
--- a/tests/fsck-tests/012-leaf-corruption/test.sh
+++ b/tests/fsck-tests/012-leaf-corruption/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs-image
@@ -37,16 +37,16 @@ leaf_no_data_ext_list=(
generate_leaf_corrupt_no_data_ext()
{
dest=$1
- echo "generating leaf_corrupt_no_data_ext.btrfs-image" >> $RESULTS
+ echo "generating leaf_corrupt_no_data_ext.btrfs-image" >> "$RESULTS"
tar --no-same-owner -xJf ./no_data_extent.tar.xz || \
_fail "failed to extract leaf_corrupt_no_data_ext.btrfs-image"
- $TOP/btrfs-image -r test.img.btrfs-image $dest || \
+ "$TOP/btrfs-image" -r test.img.btrfs-image "$dest" || \
_fail "failed to extract leaf_corrupt_no_data_ext.btrfs-image"
# leaf at 4206592 and 20905984 contains no regular data
# extent, clear its csum to corrupt the leaf.
for x in 4206592 20905984; do
- dd if=/dev/zero of=$dest bs=1 count=32 conv=notrunc seek=$x \
+ dd if=/dev/zero of="$dest" bs=1 count=32 conv=notrunc seek="$x" \
1>/dev/null 2>&1
done
}
@@ -60,21 +60,21 @@ check_inode()
name=$5
# Check whether the inode exists
- exists=$($SUDO_HELPER find $path -inum $ino)
+ exists=$($SUDO_HELPER find "$path" -inum "$ino")
if [ -z "$exists" ]; then
_fail "inode $ino not recovered correctly"
fi
# Check inode type
- found_mode=$(printf "%o" 0x$($SUDO_HELPER stat $exists -c %f))
- if [ $found_mode -ne $mode ]; then
+ found_mode=$(printf "%o" 0x$($SUDO_HELPER stat "$exists" -c %f))
+ if [ "$found_mode" -ne "$mode" ]; then
echo "$found_mode"
_fail "inode $ino modes not recovered"
fi
# Check inode size
- found_size=$($SUDO_HELPER stat $exists -c %s)
- if [ $mode -ne 41700 -a $found_size -ne $size ]; then
+ found_size=$($SUDO_HELPER stat "$exists" -c %s)
+ if [ $mode -ne 41700 -a "$found_size" -ne "$size" ]; then
_fail "inode $ino size not recovered correctly"
fi
@@ -90,11 +90,11 @@ check_inode()
check_leaf_corrupt_no_data_ext()
{
image=$1
- $SUDO_HELPER mount -o loop $image -o ro $TEST_MNT
+ $SUDO_HELPER mount -o loop -t btrfs "$image" -o ro "$TEST_MNT"
i=0
while [ $i -lt ${#leaf_no_data_ext_list[@]} ]; do
- check_inode $TEST_MNT/lost+found \
+ check_inode "$TEST_MNT/lost+found" \
${leaf_no_data_ext_list[i]} \
${leaf_no_data_ext_list[i + 1]} \
${leaf_no_data_ext_list[i + 2]} \
@@ -102,7 +102,7 @@ check_leaf_corrupt_no_data_ext()
${leaf_no_data_ext_list[i + 4]}
((i+=4))
done
- $SUDO_HELPER umount $TEST_MNT
+ $SUDO_HELPER umount "$TEST_MNT"
}
setup_root_helper
diff --git a/tests/fsck-tests/013-extent-tree-rebuild/test.sh b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
index 37bdcd9c..90fe2e83 100755
--- a/tests/fsck-tests/013-extent-tree-rebuild/test.sh
+++ b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs-corrupt-block
check_prereq mkfs.btrfs
@@ -12,32 +12,32 @@ prepare_test_dev 1G
# test whether fsck can rebuild a corrupted extent tree
test_extent_tree_rebuild()
{
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
generate_dataset small
for i in `seq 1 100`;do
- run_check $SUDO_HELPER $TOP/btrfs sub snapshot $TEST_MNT \
- $TEST_MNT/snapaaaaaaa_$i
+ run_check $SUDO_HELPER "$TOP/btrfs" sub snapshot "$TEST_MNT" \
+ "$TEST_MNT/snapaaaaaaa_$i"
done
run_check_umount_test_dev
# get extent root bytenr
- extent_root_bytenr=`$SUDO_HELPER $TOP/btrfs inspect-internal dump-tree -r $TEST_DEV | \
+ extent_root_bytenr=`$SUDO_HELPER "$TOP/btrfs" inspect-internal dump-tree -r "$TEST_DEV" | \
grep extent | awk '{print $7}'`
- if [ -z $extent_root_bytenr ];then
+ if [ -z "$extent_root_bytenr" ];then
_fail "fail to get extent root bytenr"
fi
# corrupt extent root node block
- run_check $SUDO_HELPER $TOP/btrfs-corrupt-block -l $extent_root_bytenr \
- -b 4096 $TEST_DEV
+ run_check $SUDO_HELPER "$TOP/btrfs-corrupt-block" -l "$extent_root_bytenr" \
+ -b 4096 "$TEST_DEV"
- $SUDO_HELPER $TOP/btrfs check $TEST_DEV >& /dev/null && \
+ $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV" >& /dev/null && \
_fail "btrfs check should detect failure"
- run_check $SUDO_HELPER $TOP/btrfs check --init-extent-tree $TEST_DEV
- run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
+ run_check $SUDO_HELPER "$TOP/btrfs" check --repair --init-extent-tree "$TEST_DEV"
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV"
}
test_extent_tree_rebuild
diff --git a/tests/fsck-tests/018-leaf-crossing-stripes/test.sh b/tests/fsck-tests/018-leaf-crossing-stripes/test.sh
index c453ab5c..29eb20b5 100755
--- a/tests/fsck-tests/018-leaf-crossing-stripes/test.sh
+++ b/tests/fsck-tests/018-leaf-crossing-stripes/test.sh
@@ -1,11 +1,11 @@
#!/bin/bash
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
image=$(extract_image "./default_case.raw.xz")
-run_check_stdout $TOP/btrfs check "$image" 2>&1 |
+run_check_stdout "$TOP/btrfs" check "$image" 2>&1 |
grep -q "crossing stripe boundary" ||
_fail "no expected error message in the output"
diff --git a/tests/fsck-tests/019-non-skinny-false-alert/test.sh b/tests/fsck-tests/019-non-skinny-false-alert/test.sh
index a7f8e862..550f2947 100755
--- a/tests/fsck-tests/019-non-skinny-false-alert/test.sh
+++ b/tests/fsck-tests/019-non-skinny-false-alert/test.sh
@@ -11,12 +11,12 @@
#
# a buggy check leads to the above messages
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
image=$(extract_image "./default_case.img.xz")
-run_check_stdout $TOP/btrfs check "$image" 2>&1 |
+run_check_stdout "$TOP/btrfs" check "$image" 2>&1 |
grep -q "type mismatch with chunk" &&
_fail "unexpected error message in the output"
diff --git a/tests/fsck-tests/020-extent-ref-cases/block_group_item_false_alert.raw.xz b/tests/fsck-tests/020-extent-ref-cases/block_group_item_false_alert.raw.xz
new file mode 100644
index 00000000..559c3fa9
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/block_group_item_false_alert.raw.xz
Binary files differ
diff --git a/tests/fsck-tests/020-extent-ref-cases/shared_data_ref_only.img b/tests/fsck-tests/020-extent-ref-cases/shared_data_ref_only.img
new file mode 100644
index 00000000..6d2b95e4
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/shared_data_ref_only.img
Binary files differ
diff --git a/tests/fsck-tests/020-extent-ref-cases/test.sh b/tests/fsck-tests/020-extent-ref-cases/test.sh
index c2b6a006..1e1e4e23 100755
--- a/tests/fsck-tests/020-extent-ref-cases/test.sh
+++ b/tests/fsck-tests/020-extent-ref-cases/test.sh
@@ -8,16 +8,23 @@
# * shared_data_ref
# * no_inline_ref (a extent item without inline ref)
# * no_skinny_ref
+#
+# Special check for lowmem regression
+# * block_group_item_false_alert
+# Containing a block group and its first extent at
+# the beginning of leaf.
+# Which caused false alert for lowmem mode.
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
-for img in *.img
+for img in *.img *.raw.xz
do
- image=$(extract_image $img)
- run_check_stdout $TOP/btrfs check "$image" 2>&1 |
- grep -q "Errors found in extent allocation tree or chunk allocation" &&
- _fail "unexpected error occurred when checking $img"
+ image=$(extract_image "$img")
+
+ # Since the return value bug is already fixed, we don't need
+ # the old grep hack to detect bug.
+ run_check "$TOP/btrfs" check "$image"
rm -f "$image"
done
diff --git a/tests/fsck-tests/021-partially-dropped-snapshot-case/test.sh b/tests/fsck-tests/021-partially-dropped-snapshot-case/test.sh
index eb8d8849..44a33a63 100755
--- a/tests/fsck-tests/021-partially-dropped-snapshot-case/test.sh
+++ b/tests/fsck-tests/021-partially-dropped-snapshot-case/test.sh
@@ -1,14 +1,14 @@
#!/bin/bash
# confirm whether btrfsck supports to check a partially dropped snapshot
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
for img in *.img
do
- image=$(extract_image $img)
- run_check_stdout $TOP/btrfs check "$image" 2>&1 |
+ image=$(extract_image "$img")
+ run_check_stdout "$TOP/btrfs" check "$image" 2>&1 |
grep -q "Errors found in extent allocation tree or chunk allocation"
if [ $? -eq 0 ]; then
rm -f "$image"
diff --git a/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh b/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh
index 1dc8f8fc..dcdc1b42 100755
--- a/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh
+++ b/tests/fsck-tests/022-qgroup-rescan-halfway/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# check whether btrfsck can detect running qgroup rescan
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
@@ -9,7 +9,7 @@ check_image() {
local image
image=$1
- run_check_stdout $TOP/btrfs check "$image" 2>&1 | \
+ run_check_stdout "$TOP/btrfs" check "$image" 2>&1 | \
grep -q "Counts for qgroup id"
if [ $? -eq 0 ]; then
_fail "Btrfs check doesn't detect rescan correctly"
diff --git a/tests/fsck-tests/023-qgroup-stack-overflow/test.sh b/tests/fsck-tests/023-qgroup-stack-overflow/test.sh
index a304eac5..ebb07f36 100755
--- a/tests/fsck-tests/023-qgroup-stack-overflow/test.sh
+++ b/tests/fsck-tests/023-qgroup-stack-overflow/test.sh
@@ -5,13 +5,13 @@
# Fixed by patch:
# btrfs-progs: Fix stack overflow for checking qgroup on tree reloc tree
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
check_image()
{
- run_check $TOP/btrfs check "$1"
+ run_check "$TOP/btrfs" check "$1"
}
check_all_images
diff --git a/tests/fsck-tests/024-clear-space-cache/test.sh b/tests/fsck-tests/024-clear-space-cache/test.sh
index 2945ae87..6cf8440b 100755
--- a/tests/fsck-tests/024-clear-space-cache/test.sh
+++ b/tests/fsck-tests/024-clear-space-cache/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# confirm that clearing space cache works
-source $TOP/tests/common
+source "$TOP/tests/common"
check_prereq btrfs
check_prereq mkfs.btrfs
@@ -9,21 +9,21 @@ check_prereq mkfs.btrfs
setup_root_helper
prepare_test_dev 1G
-run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $TEST_DEV
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
# Create files that takes at least 3 data chunks, while
# can still be removed to create free space inside one chunk.
for i in $(seq 0 6); do
- run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/file_${i} bs=1M \
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/file_${i}" bs=1M \
count=64 > /dev/null 2>&1
done
sync
# Remove file 1 3 5 to create holes
for i in 1 3 5; do
- run_check $SUDO_HELPER rm $TEST_MNT/file_${i}
+ run_check $SUDO_HELPER rm "$TEST_MNT/file_${i}"
done
sync
@@ -31,17 +31,17 @@ sync
run_check_umount_test_dev
# Clear space cache and re-check fs
-run_check $TOP/btrfs check --clear-space-cache v1 $TEST_DEV
-run_check $TOP/btrfs check $TEST_DEV
+run_check "$TOP/btrfs" check --clear-space-cache v1 "$TEST_DEV"
+run_check "$TOP/btrfs" check "$TEST_DEV"
# Manually recheck space cache and super space cache generation
-run_check_stdout $TOP/btrfs inspect-internal dump-tree -t root $TEST_DEV | \
+run_check_stdout "$TOP/btrfs" inspect-internal dump-tree -t root "$TEST_DEV" | \
grep -q FREE_SPACE
if [ $? -eq 0 ]; then
_fail "clear space cache doesn't clear all space cache"
fi
-run_check_stdout $TOP/btrfs inspect-internal dump-super $TEST_DEV |
+run_check_stdout $TOP/btrfs inspect-internal dump-super "$TEST_DEV" |
grep -q 'cache_generation.*18446744073709551615'
if [ $? -ne 0 ]; then
_fail "clear space cache doesn't set cache_generation correctly"
diff --git a/tests/fsck-tests/025-file-extents/test.sh b/tests/fsck-tests/025-file-extents/test.sh
new file mode 100755
index 00000000..ebe8a305
--- /dev/null
+++ b/tests/fsck-tests/025-file-extents/test.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+# Confirm btrfs check can check file extents without causing false alert
+
+source "$TOP/tests/common"
+
+check_prereq btrfs
+check_prereq mkfs.btrfs
+check_prereq btrfstune
+check_global_prereq dd
+check_global_prereq fallocate
+check_global_prereq truncate
+
+setup_root_helper
+prepare_test_dev 128M
+
+# Do some write into a large prealloc range
+# Lowmem mode can report missing csum due to wrong csum range
+test_paritical_write_into_prealloc()
+{
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+ run_check_mount_test_dev
+
+ run_check $SUDO_HELPER fallocate -l 128K "$TEST_MNT/file"
+ sync
+ run_check $SUDO_HELPER dd conv=notrunc if=/dev/zero of="$TEST_MNT/file" bs=1K count=64
+ run_check_umount_test_dev
+ run_check "$TOP/btrfs" check "$TEST_DEV"
+}
+
+# Inline compressed file extent
+# Lowmem mode can cause silent error without any error message
+# due to too restrict check on inline extent size
+test_compressed_inline_extent()
+{
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+ run_check_mount_test_dev -o compress=lzo,max_inline=2048
+
+ run_check $SUDO_HELPER dd conv=notrunc if=/dev/null of="$TEST_MNT/file" bs=1K count=1
+ run_check_umount_test_dev
+ run_check "$TOP/btrfs" check "$TEST_DEV"
+}
+
+# File extent hole with NO_HOLES incompat feature set.
+# Lowmem mode will cause a false alert as it doesn't allow any file hole
+# extents, while we can set NO_HOLES at anytime we want, it's definitely a
+# false alert
+test_hole_extent_with_no_holes_flag()
+{
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+ run_check_mount_test_dev
+
+ run_check $SUDO_HELPER truncate -s 16K "$TEST_MNT/tmp"
+ run_check_umount_test_dev
+ run_check $SUDO_HELPER "$TOP/btrfstune" -n "$TEST_DEV"
+ run_check "$TOP/btrfs" check "$TEST_DEV"
+}
+
+test_paritical_write_into_prealloc
+test_compressed_inline_extent
+test_hole_extent_with_no_holes_flag
diff --git a/tests/fssum.c b/tests/fssum.c
new file mode 100644
index 00000000..5dde9984
--- /dev/null
+++ b/tests/fssum.c
@@ -0,0 +1,839 @@
+/*
+ * Copyright (C) 2012 STRATO AG. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include "kerncompat.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/sysmacros.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <time.h>
+#include <stdint.h>
+#include "tests/sha.h"
+
+#define CS_SIZE 32
+#define CHUNKS 128
+
+#ifndef SEEK_DATA
+#define SEEK_DATA 3
+#define SEEK_HOLE 4
+#endif
+
+/* TODO: add hardlink recognition */
+/* TODO: add xattr/acl */
+
+struct excludes {
+ char *path;
+ int len;
+};
+
+typedef struct _sum {
+ SHA256Context sha;
+ unsigned char out[CS_SIZE];
+} sum_t;
+
+typedef int (*sum_file_data_t)(int fd, sum_t *dst);
+
+int gen_manifest = 0;
+int in_manifest = 0;
+char *checksum = NULL;
+struct excludes *excludes;
+int n_excludes = 0;
+int verbose = 0;
+FILE *out_fp;
+FILE *in_fp;
+
+enum _flags {
+ FLAG_UID,
+ FLAG_GID,
+ FLAG_MODE,
+ FLAG_ATIME,
+ FLAG_MTIME,
+ FLAG_CTIME,
+ FLAG_DATA,
+ FLAG_OPEN_ERROR,
+ FLAG_STRUCTURE,
+ NUM_FLAGS
+};
+
+const char flchar[] = "ugoamcdes";
+char line[65536];
+
+int flags[NUM_FLAGS] = {1, 1, 1, 1, 1, 0, 1, 0, 0};
+
+char *
+getln(char *buf, int size, FILE *fp)
+{
+ char *p;
+ int l;
+
+ p = fgets(buf, size, fp);
+ if (!p)
+ return NULL;
+
+ l = strlen(p);
+ while(l > 0 && (p[l - 1] == '\n' || p[l - 1] == '\r'))
+ p[--l] = 0;
+
+ return p;
+}
+
+void
+parse_flag(int c)
+{
+ int i;
+ int is_upper = 0;
+
+ if (c >= 'A' && c <= 'Z') {
+ is_upper = 1;
+ c += 'a' - 'A';
+ }
+ for (i = 0; flchar[i]; ++i) {
+ if (flchar[i] == c) {
+ flags[i] = is_upper ? 0 : 1;
+ return;
+ }
+ }
+ fprintf(stderr, "unrecognized flag %c\n", c);
+ exit(-1);
+}
+
+void
+parse_flags(char *p)
+{
+ while (*p)
+ parse_flag(*p++);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: fssum <options> <path>\n");
+ fprintf(stderr, " options:\n");
+ fprintf(stderr, " -f : write out a full manifest file\n");
+ fprintf(stderr, " -w <file> : send output to file\n");
+ fprintf(stderr, " -v : verbose mode (debugging only)\n");
+ fprintf(stderr,
+ " -r <file> : read checksum or manifest from file\n");
+ fprintf(stderr, " -[ugoamcde] : specify which fields to include in checksum calculation.\n");
+ fprintf(stderr, " u : include uid\n");
+ fprintf(stderr, " g : include gid\n");
+ fprintf(stderr, " o : include mode\n");
+ fprintf(stderr, " m : include mtime\n");
+ fprintf(stderr, " a : include atime\n");
+ fprintf(stderr, " c : include ctime\n");
+ fprintf(stderr, " d : include file data\n");
+ fprintf(stderr, " e : include open errors (aborts otherwise)\n");
+ fprintf(stderr, " s : include block structure (holes)\n");
+ fprintf(stderr, " -[UGOAMCDES]: exclude respective field from calculation\n");
+ fprintf(stderr, " -n : reset all flags\n");
+ fprintf(stderr, " -N : set all flags\n");
+ fprintf(stderr, " -x path : exclude path when building checksum (multiple ok)\n");
+ fprintf(stderr, " -h : this help\n\n");
+ fprintf(stderr, "The default field mask is ugoamCdES. If the checksum/manifest is read from a\n");
+ fprintf(stderr, "file, the mask is taken from there and the values given on the command line\n");
+ fprintf(stderr, "are ignored.\n");
+ exit(-1);
+}
+
+static char buf[65536];
+
+void *
+alloc(size_t sz)
+{
+ void *p = malloc(sz);
+
+ if (!p) {
+ fprintf(stderr, "malloc failed\n");
+ exit(-1);
+ }
+
+ return p;
+}
+
+void
+sum_init(sum_t *cs)
+{
+ SHA256Reset(&cs->sha);
+}
+
+void
+sum_fini(sum_t *cs)
+{
+ SHA256Result(&cs->sha, cs->out);
+}
+
+void
+sum_add(sum_t *cs, void *buf, int size)
+{
+ SHA256Input(&cs->sha, buf, size);
+}
+
+void
+sum_add_sum(sum_t *dst, sum_t *src)
+{
+ sum_add(dst, src->out, sizeof(src->out));
+}
+
+void
+sum_add_u64(sum_t *dst, uint64_t val)
+{
+ uint64_t v = cpu_to_le64(val);
+ sum_add(dst, &v, sizeof(v));
+}
+
+void
+sum_add_time(sum_t *dst, time_t t)
+{
+ sum_add_u64(dst, t);
+}
+
+char *
+sum_to_string(sum_t *dst)
+{
+ int i;
+ char *s = alloc(CS_SIZE * 2 + 1);
+
+ for (i = 0; i < CS_SIZE; ++i)
+ sprintf(s + i * 2, "%02x", dst->out[i]);
+
+ return s;
+}
+
+int
+sum_file_data_permissive(int fd, sum_t *dst)
+{
+ int ret;
+ off_t pos;
+ off_t old;
+ int i;
+ uint64_t zeros = 0;
+
+ pos = lseek(fd, 0, SEEK_CUR);
+ if (pos == (off_t)-1)
+ return errno == ENXIO ? 0 : -2;
+
+ while (1) {
+ old = pos;
+ pos = lseek(fd, pos, SEEK_DATA);
+ if (pos == (off_t)-1) {
+ if (errno == ENXIO) {
+ ret = 0;
+ pos = lseek(fd, 0, SEEK_END);
+ if (pos != (off_t)-1)
+ zeros += pos - old;
+ } else {
+ ret = -2;
+ }
+ break;
+ }
+ ret = read(fd, buf, sizeof(buf));
+ assert(ret); /* eof found by lseek */
+ if (ret <= 0)
+ break;
+ if (old < pos) /* hole */
+ zeros += pos - old;
+ for (i = 0; i < ret; ++i) {
+ for (old = i; buf[i] == 0 && i < ret; ++i)
+ ;
+ if (old < i) /* code like a hole */
+ zeros += i - old;
+ if (i == ret)
+ break;
+ if (zeros) {
+ if (verbose >= 2)
+ fprintf(stderr,
+ "adding %llu zeros to sum\n",
+ (unsigned long long)zeros);
+ sum_add_u64(dst, 0);
+ sum_add_u64(dst, zeros);
+ zeros = 0;
+ }
+ for (old = i; buf[i] != 0 && i < ret; ++i)
+ ;
+ if (verbose >= 2)
+ fprintf(stderr, "adding %u non-zeros to sum\n",
+ i - (int)old);
+ sum_add(dst, buf + old, i - old);
+ }
+ pos += ret;
+ }
+
+ if (zeros) {
+ if (verbose >= 2)
+ fprintf(stderr,
+ "adding %llu zeros to sum (finishing)\n",
+ (unsigned long long)zeros);
+ sum_add_u64(dst, 0);
+ sum_add_u64(dst, zeros);
+ }
+
+ return ret;
+}
+
+int
+sum_file_data_strict(int fd, sum_t *dst)
+{
+ int ret;
+ off_t pos;
+
+ pos = lseek(fd, 0, SEEK_CUR);
+ if (pos == (off_t)-1)
+ return errno == ENXIO ? 0 : -2;
+
+ while (1) {
+ pos = lseek(fd, pos, SEEK_DATA);
+ if (pos == (off_t)-1)
+ return errno == ENXIO ? 0 : -2;
+ ret = read(fd, buf, sizeof(buf));
+ assert(ret); /* eof found by lseek */
+ if (ret <= 0)
+ return ret;
+ if (verbose >= 2)
+ fprintf(stderr,
+ "adding to sum at file offset %llu, %d bytes\n",
+ (unsigned long long)pos, ret);
+ sum_add_u64(dst, (uint64_t)pos);
+ sum_add(dst, buf, ret);
+ pos += ret;
+ }
+}
+
+char *
+escape(char *in)
+{
+ char *out = alloc(strlen(in) * 3 + 1);
+ char *src = in;
+ char *dst = out;
+
+ for (; *src; ++src) {
+ if (*src >= 32 && *src < 127 && *src != '\\') {
+ *dst++ = *src;
+ } else {
+ sprintf(dst, "\\%02x", (unsigned char)*src);
+ dst += 3;
+ }
+ }
+ *dst = 0;
+
+ return out;
+}
+
+void
+excess_file(const char *fn)
+{
+ printf("only in local fs: %s\n", fn);
+}
+
+void
+missing_file(const char *fn)
+{
+ printf("only in remote fs: %s\n", fn);
+}
+
+int
+pathcmp(const char *a, const char *b)
+{
+ int len_a = strlen(a);
+ int len_b = strlen(b);
+
+ /*
+ * as the containing directory is sent after the files, it has to
+ * come out bigger in the comparison.
+ */
+ if (len_a < len_b && a[len_a - 1] == '/' && strncmp(a, b, len_a) == 0)
+ return 1;
+ if (len_a > len_b && b[len_b - 1] == '/' && strncmp(a, b, len_b) == 0)
+ return -1;
+
+ return strcmp(a, b);
+}
+
+void
+check_match(char *fn, char *local_m, char *remote_m,
+ char *local_c, char *remote_c)
+{
+ int match_m = !strcmp(local_m, remote_m);
+ int match_c = !strcmp(local_c, remote_c);
+
+ if (match_m && !match_c) {
+ printf("data mismatch in %s\n", fn);
+ } else if (!match_m && match_c) {
+ printf("metadata mismatch in %s\n", fn);
+ } else if (!match_m && !match_c) {
+ printf("metadata and data mismatch in %s\n", fn);
+ }
+}
+
+char *prev_fn;
+char *prev_m;
+char *prev_c;
+void
+check_manifest(char *fn, char *m, char *c, int last_call)
+{
+ char *rem_m;
+ char *rem_c;
+ char *l;
+ int cmp;
+
+ if (prev_fn) {
+ if (last_call)
+ cmp = -1;
+ else
+ cmp = pathcmp(prev_fn, fn);
+ if (cmp > 0) {
+ excess_file(fn);
+ return;
+ } else if (cmp < 0) {
+ missing_file(prev_fn);
+ } else {
+ check_match(fn, m, prev_m, c, prev_c);
+ }
+ free(prev_fn);
+ free(prev_m);
+ free(prev_c);
+ prev_fn = NULL;
+ prev_m = NULL;
+ prev_c = NULL;
+ if (cmp == 0)
+ return;
+ }
+ while ((l = getln(line, sizeof(line), in_fp))) {
+ rem_c = strrchr(l, ' ');
+ if (!rem_c) {
+ if (checksum)
+ free(checksum);
+
+ /* final cs */
+ checksum = strdup(l);
+ break;
+ }
+ if (rem_c == l) {
+malformed:
+ fprintf(stderr, "malformed input\n");
+ exit(-1);
+ }
+ *rem_c++ = 0;
+ rem_m = strrchr(l, ' ');
+ if (!rem_m)
+ goto malformed;
+ *rem_m++ = 0;
+
+ if (last_call)
+ cmp = -1;
+ else
+ cmp = pathcmp(l, fn);
+ if (cmp == 0) {
+ check_match(fn, m, rem_m, c, rem_c);
+ return;
+ } else if (cmp > 0) {
+ excess_file(fn);
+ prev_fn = strdup(l);
+ prev_m = strdup(rem_m);
+ prev_c = strdup(rem_c);
+ return;
+ }
+ missing_file(l);
+ }
+ if (!last_call)
+ excess_file(fn);
+}
+
+int
+namecmp(const void *aa, const void *bb)
+{
+ char * const *a = aa;
+ char * const *b = bb;
+
+ return strcmp(*a, *b);
+}
+
+void
+sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
+{
+ DIR *d;
+ struct dirent *de;
+ char **namelist = NULL;
+ int alloclen = 0;
+ int entries = 0;
+ int i;
+ int ret;
+ int fd;
+ int excl;
+ sum_file_data_t sum_file_data = flags[FLAG_STRUCTURE] ?
+ sum_file_data_strict : sum_file_data_permissive;
+
+ d = fdopendir(dirfd);
+ if (!d) {
+ perror("opendir");
+ exit(-1);
+ }
+ while((de = readdir(d))) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+ if (entries == alloclen) {
+ alloclen += CHUNKS;
+ namelist = realloc(namelist,
+ alloclen * sizeof(*namelist));
+ if (!namelist) {
+ fprintf(stderr, "malloc failed\n");
+ exit(-1);
+ }
+ }
+ namelist[entries] = strdup(de->d_name);
+ if (!namelist[entries]) {
+ fprintf(stderr, "malloc failed\n");
+ exit(-1);
+ }
+ ++entries;
+ }
+
+ qsort(namelist, entries, sizeof(*namelist), namecmp);
+ for (i = 0; i < entries; ++i) {
+ struct stat64 st;
+ sum_t cs;
+ sum_t meta;
+ char *path;
+
+ sum_init(&cs);
+ sum_init(&meta);
+ path = alloc(strlen(path_in) + strlen(namelist[i]) + 3);
+ sprintf(path, "%s/%s", path_in, namelist[i]);
+ for (excl = 0; excl < n_excludes; ++excl) {
+ if (strncmp(excludes[excl].path, path,
+ excludes[excl].len) == 0)
+ goto next;
+ }
+
+ ret = fchdir(dirfd);
+ if (ret == -1) {
+ perror("fchdir");
+ exit(-1);
+ }
+ ret = lstat64(namelist[i], &st);
+ if (ret) {
+ fprintf(stderr, "stat failed for %s/%s: %s\n",
+ path_prefix, path, strerror(errno));
+ exit(-1);
+ }
+ sum_add_u64(&meta, level);
+ sum_add(&meta, namelist[i], strlen(namelist[i]));
+ if (!S_ISDIR(st.st_mode))
+ sum_add_u64(&meta, st.st_nlink);
+ if (flags[FLAG_UID])
+ sum_add_u64(&meta, st.st_uid);
+ if (flags[FLAG_GID])
+ sum_add_u64(&meta, st.st_gid);
+ if (flags[FLAG_MODE])
+ sum_add_u64(&meta, st.st_mode);
+ if (flags[FLAG_ATIME])
+ sum_add_time(&meta, st.st_atime);
+ if (flags[FLAG_MTIME])
+ sum_add_time(&meta, st.st_mtime);
+ if (flags[FLAG_CTIME])
+ sum_add_time(&meta, st.st_ctime);
+ if (S_ISDIR(st.st_mode)) {
+ fd = openat(dirfd, namelist[i], 0);
+ if (fd == -1 && flags[FLAG_OPEN_ERROR]) {
+ sum_add_u64(&meta, errno);
+ } else if (fd == -1) {
+ fprintf(stderr, "open failed for %s/%s: %s\n",
+ path_prefix, path, strerror(errno));
+ exit(-1);
+ } else {
+ sum(fd, level + 1, &cs, path_prefix, path);
+ close(fd);
+ }
+ } else if (S_ISREG(st.st_mode)) {
+ sum_add_u64(&meta, st.st_size);
+ if (flags[FLAG_DATA]) {
+ if (verbose)
+ fprintf(stderr, "file %s\n",
+ namelist[i]);
+ fd = openat(dirfd, namelist[i], 0);
+ if (fd == -1 && flags[FLAG_OPEN_ERROR]) {
+ sum_add_u64(&meta, errno);
+ } else if (fd == -1) {
+ fprintf(stderr,
+ "open failed for %s/%s: %s\n",
+ path_prefix, path,
+ strerror(errno));
+ exit(-1);
+ }
+ if (fd != -1) {
+ ret = sum_file_data(fd, &cs);
+ if (ret < 0) {
+ fprintf(stderr,
+ "read failed for "
+ "%s/%s: %s\n",
+ path_prefix, path,
+ strerror(errno));
+ exit(-1);
+ }
+ close(fd);
+ }
+ }
+ } else if (S_ISLNK(st.st_mode)) {
+ ret = readlink(namelist[i], buf, sizeof(buf));
+ if (ret == -1) {
+ perror("readlink");
+ exit(-1);
+ }
+ sum_add(&cs, buf, ret);
+ } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
+ sum_add_u64(&cs, major(st.st_rdev));
+ sum_add_u64(&cs, minor(st.st_rdev));
+ }
+ sum_fini(&cs);
+ sum_fini(&meta);
+ if (gen_manifest || in_manifest) {
+ char *fn;
+ char *m;
+ char *c;
+
+ if (S_ISDIR(st.st_mode))
+ strcat(path, "/");
+ fn = escape(path);
+ m = sum_to_string(&meta);
+ c = sum_to_string(&cs);
+
+ if (gen_manifest)
+ fprintf(out_fp, "%s %s %s\n", fn, m, c);
+ if (in_manifest)
+ check_manifest(fn, m, c, 0);
+ free(c);
+ free(m);
+ free(fn);
+ }
+ sum_add_sum(dircs, &cs);
+ sum_add_sum(dircs, &meta);
+next:
+ free(path);
+ free(namelist[i]);
+ }
+
+ free(namelist);
+ closedir(d);
+}
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ int c;
+ char *path;
+ int fd;
+ sum_t cs;
+ char *sumstring;
+ char flagstring[sizeof(flchar)];
+ int ret = 0;
+ int i;
+ int plen;
+ int elen;
+ int n_flags = 0;
+ const char *allopts = "heEfuUgGoOaAmMcCdDsSnNw:r:vx:";
+
+ out_fp = stdout;
+ while ((c = getopt(argc, argv, allopts)) != EOF) {
+ switch(c) {
+ case 'f':
+ gen_manifest = 1;
+ break;
+ case 'u':
+ case 'U':
+ case 'g':
+ case 'G':
+ case 'o':
+ case 'O':
+ case 'a':
+ case 'A':
+ case 'm':
+ case 'M':
+ case 'c':
+ case 'C':
+ case 'd':
+ case 'D':
+ case 'e':
+ case 'E':
+ case 's':
+ case 'S':
+ ++n_flags;
+ parse_flag(c);
+ break;
+ case 'n':
+ for (i = 0; i < NUM_FLAGS; ++i)
+ flags[i] = 0;
+ break;
+ case 'N':
+ for (i = 0; i < NUM_FLAGS; ++i)
+ flags[i] = 1;
+ break;
+ case 'w':
+ out_fp = fopen(optarg, "w");
+ if (!out_fp) {
+ fprintf(stderr,
+ "failed to open output file: %s\n",
+ strerror(errno));
+ exit(-1);
+ }
+ break;
+ case 'r':
+ in_fp = fopen(optarg, "r");
+ if (!in_fp) {
+ fprintf(stderr,
+ "failed to open input file: %s\n",
+ strerror(errno));
+ exit(-1);
+ }
+ break;
+ case 'x':
+ ++n_excludes;
+ excludes = realloc(excludes,
+ sizeof(*excludes) * n_excludes);
+ if (!excludes) {
+ fprintf(stderr,
+ "failed to alloc exclude space\n");
+ exit(-1);
+ }
+ excludes[n_excludes - 1].path = optarg;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ }
+ }
+
+ if (optind + 1 != argc) {
+ fprintf(stderr, "missing path\n");
+ usage();
+ }
+
+ if (in_fp) {
+ char *l = getln(line, sizeof(line), in_fp);
+ char *p;
+
+ if (l == NULL) {
+ fprintf(stderr, "failed to read line from input\n");
+ exit(-1);
+ }
+ if (strncmp(l, "Flags: ", 7) == 0) {
+ l += 7;
+ in_manifest = 1;
+ parse_flags(l);
+ } else if ((p = strchr(l, ':'))) {
+ *p++ = 0;
+ parse_flags(l);
+
+ if (checksum)
+ free(checksum);
+ checksum = strdup(p);
+ } else {
+ fprintf(stderr, "invalid input file format\n");
+ exit(-1);
+ }
+ if (n_flags)
+ fprintf(stderr, "warning: "
+ "command line flags ignored in -r mode\n");
+ }
+ strcpy(flagstring, flchar);
+ for (i = 0; i < NUM_FLAGS; ++i) {
+ if (flags[i] == 0)
+ flagstring[i] -= 'a' - 'A';
+ }
+
+ path = argv[optind];
+ plen = strlen(path);
+ if (path[plen - 1] == '/') {
+ --plen;
+ path[plen] = '\0';
+ }
+
+ for (i = 0; i < n_excludes; ++i) {
+ if (strncmp(path, excludes[i].path, plen) != 0)
+ fprintf(stderr,
+ "warning: exclude %s outside of path %s\n",
+ excludes[i].path, path);
+ else
+ excludes[i].path += plen;
+ elen = strlen(excludes[i].path);
+ if (excludes[i].path[elen - 1] == '/')
+ --elen;
+ excludes[i].path[elen] = '\0';
+ excludes[i].len = elen;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "failed to open %s: %s\n", path,
+ strerror(errno));
+ exit(-1);
+ }
+
+ if (gen_manifest)
+ fprintf(out_fp, "Flags: %s\n", flagstring);
+
+ sum_init(&cs);
+ sum(fd, 1, &cs, path, "");
+ sum_fini(&cs);
+
+ close(fd);
+ if (in_manifest)
+ check_manifest("", "", "", 1);
+
+ if (!checksum) {
+ if (in_manifest) {
+ fprintf(stderr, "malformed input\n");
+ exit(-1);
+ }
+ if (!gen_manifest)
+ fprintf(out_fp, "%s:", flagstring);
+
+ sumstring = sum_to_string(&cs);
+ fprintf(out_fp, "%s\n", sumstring);
+ free(sumstring);
+ } else {
+ sumstring = sum_to_string(&cs);
+ if (strcmp(checksum, sumstring) == 0) {
+ printf("OK\n");
+ ret = 0;
+ } else {
+ printf("FAIL\n");
+ ret = 1;
+ }
+
+ free(checksum);
+ free(sumstring);
+ }
+
+ if (in_fp)
+ fclose(in_fp);
+
+ if (out_fp != stdout)
+ fclose(out_fp);
+
+ exit(ret);
+}
diff --git a/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.txt b/tests/fuzz-tests/images/bko-97171-btrfs-image.raw.txt
index 9685ed46..9685ed46 100644
--- a/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.txt
+++ b/tests/fuzz-tests/images/bko-97171-btrfs-image.raw.txt
diff --git a/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.xz b/tests/fuzz-tests/images/bko-97171-btrfs-image.raw.xz
index f3f0944d..f3f0944d 100644
--- a/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.xz
+++ b/tests/fuzz-tests/images/bko-97171-btrfs-image.raw.xz
Binary files differ
diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh
index 1c645c9b..08988016 100755
--- a/tests/misc-tests.sh
+++ b/tests/misc-tests.sh
@@ -24,6 +24,9 @@ check_prereq btrfs-corrupt-block
check_prereq btrfs-image
check_prereq btrfstune
check_prereq btrfs
+check_prereq btrfs-zero-log
+check_prereq btrfs-find-root
+check_prereq btrfs-select-super
check_kernel_support
# The tests are driven by their custom script called 'test.sh'
diff --git a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
index 92c896f9..fa3f09ab 100755
--- a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
+++ b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
@@ -9,9 +9,9 @@ check_prereq btrfs
setup_root_helper
-run_check truncate -s 2G $IMAGE
-run_check $TOP/mkfs.btrfs -f $IMAGE
-run_check $SUDO_HELPER mount $IMAGE $TEST_MNT
+prepare_test_dev
+run_check $TOP/mkfs.btrfs -f "$TEST_DEV"
+run_check_mount_test_dev
run_check $SUDO_HELPER chmod a+rw $TEST_MNT
cd $TEST_MNT
@@ -49,4 +49,4 @@ run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
wait
cd ..
-run_check $SUDO_HELPER umount $TEST_MNT
+run_check_umount_test_dev
diff --git a/tests/misc-tests/013-subvolume-sync-crash/test.sh b/tests/misc-tests/013-subvolume-sync-crash/test.sh
index 4cb1b4e7..cd445961 100755
--- a/tests/misc-tests/013-subvolume-sync-crash/test.sh
+++ b/tests/misc-tests/013-subvolume-sync-crash/test.sh
@@ -10,9 +10,9 @@ check_prereq btrfs
setup_root_helper
-run_check truncate -s 2G $IMAGE
-run_check $TOP/mkfs.btrfs -f $IMAGE
-run_check $SUDO_HELPER mount $IMAGE $TEST_MNT
+prepare_test_dev
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+run_check_mount_test_dev
run_check $SUDO_HELPER chmod a+rw $TEST_MNT
cd $TEST_MNT
@@ -46,4 +46,4 @@ run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
wait
cd ..
-run_check $SUDO_HELPER umount $TEST_MNT
+run_check_umount_test_dev
diff --git a/tests/misc-tests/014-filesystem-label/test.sh b/tests/misc-tests/014-filesystem-label/test.sh
index a5e08ccc..753aa9ea 100755
--- a/tests/misc-tests/014-filesystem-label/test.sh
+++ b/tests/misc-tests/014-filesystem-label/test.sh
@@ -9,9 +9,9 @@ check_prereq btrfs
setup_root_helper
-run_check truncate -s 2G $IMAGE
-run_check $TOP/mkfs.btrfs -L BTRFS-TEST-LABEL -f $IMAGE
-run_check $SUDO_HELPER mount $IMAGE $TEST_MNT
+prepare_test_dev
+run_check "$TOP/mkfs.btrfs" -L BTRFS-TEST-LABEL -f "$TEST_DEV"
+run_check_mount_test_dev
run_check $SUDO_HELPER chmod a+rw $TEST_MNT
cd $TEST_MNT
@@ -66,4 +66,4 @@ run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
cd ..
-run_check $SUDO_HELPER umount $TEST_MNT
+run_check_umount_test_dev
diff --git a/tests/misc-tests/016-send-clone-src/test.sh b/tests/misc-tests/016-send-clone-src/test.sh
index e256eef9..479da677 100755
--- a/tests/misc-tests/016-send-clone-src/test.sh
+++ b/tests/misc-tests/016-send-clone-src/test.sh
@@ -9,9 +9,9 @@ check_prereq mkfs.btrfs
check_prereq btrfs
setup_root_helper
-prepare_test_dev 1g
-run_check $TOP/mkfs.btrfs -f $IMAGE
+prepare_test_dev 1g
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
here=`pwd`
diff --git a/tests/misc-tests/017-recv-stream-malformatted/test.sh b/tests/misc-tests/017-recv-stream-malformatted/test.sh
index 884b7d42..3756be27 100755
--- a/tests/misc-tests/017-recv-stream-malformatted/test.sh
+++ b/tests/misc-tests/017-recv-stream-malformatted/test.sh
@@ -8,9 +8,9 @@ check_prereq mkfs.btrfs
check_prereq btrfs
setup_root_helper
-prepare_test_dev 1g
-run_check $TOP/mkfs.btrfs -f $IMAGE
+prepare_test_dev 1g
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
echo -n '' | run_mayfail $SUDO_HELPER "$TOP/btrfs" receive "$TEST_MNT" &&
diff --git a/tests/misc-tests/018-recv-end-of-stream/test.sh b/tests/misc-tests/018-recv-end-of-stream/test.sh
index d39683e9..3b8a0319 100755
--- a/tests/misc-tests/018-recv-end-of-stream/test.sh
+++ b/tests/misc-tests/018-recv-end-of-stream/test.sh
@@ -13,6 +13,8 @@ prepare_test_dev 1g
here=`pwd`
+# All helpers can exercise various options passed to 'btrfs receive'
+
test_full_empty_stream() {
local str
@@ -34,7 +36,7 @@ test_full_empty_stream() {
run_check $TOP/mkfs.btrfs -f $TEST_DEV
run_check_mount_test_dev
- run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$str" "$TEST_MNT"
+ run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$str" "$TEST_MNT"
run_check_umount_test_dev
run_check rm -f -- "$str"
@@ -65,7 +67,7 @@ test_full_simple_stream() {
run_check $TOP/mkfs.btrfs -f $TEST_DEV
run_check_mount_test_dev
- run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$str" "$TEST_MNT"
+ run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$str" "$TEST_MNT"
run_check_umount_test_dev
run_check rm -f -- "$str"
@@ -96,8 +98,8 @@ test_incr_empty_stream() {
run_check $TOP/mkfs.btrfs -f $TEST_DEV
run_check_mount_test_dev
- run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$fstr" "$TEST_MNT"
- run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$istr" "$TEST_MNT"
+ run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$fstr" "$TEST_MNT"
+ run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$istr" "$TEST_MNT"
run_check_umount_test_dev
run_check rm -f -- "$fstr" "$istr"
@@ -136,8 +138,8 @@ test_incr_simple_stream() {
run_check $TOP/mkfs.btrfs -f $TEST_DEV
run_check_mount_test_dev
- run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$fstr" "$TEST_MNT"
- run_check $SUDO_HELPER $TOP/btrfs receive -v -f "$istr" "$TEST_MNT"
+ run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$fstr" "$TEST_MNT"
+ run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$istr" "$TEST_MNT"
run_check_umount_test_dev
run_check rm -f -- "$fstr" "$istr"
@@ -147,3 +149,9 @@ test_full_empty_stream
test_full_simple_stream
test_incr_empty_stream
test_incr_simple_stream
+
+extra_opt=-e
+test_full_empty_stream $extra_opt
+test_full_simple_stream $extra_opt
+test_incr_empty_stream $extra_opt
+test_incr_simple_stream $extra_opt
diff --git a/tests/misc-tests/019-receive-clones-on-munted-subvol/test.sh b/tests/misc-tests/019-receive-clones-on-munted-subvol/test.sh
new file mode 100755
index 00000000..182b0cf9
--- /dev/null
+++ b/tests/misc-tests/019-receive-clones-on-munted-subvol/test.sh
@@ -0,0 +1,127 @@
+#! /bin/bash
+#
+# Test that an incremental send operation works when in both snapshots there are
+# two directory inodes that have the same number but different generations and
+# have an entry with the same name that corresponds to different inodes in each
+# snapshot.
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+check_prereq fssum
+
+setup_root_helper
+prepare_test_dev
+
+FSSUM_PROG="$TOP/fssum"
+srcdir=./send-test-dir
+rm -rf "$srcdir"
+mkdir -p "$srcdir"
+run_check chmod a+rw "$srcdir"
+
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+run_check_mount_test_dev
+
+BLOCK_SIZE=$(stat -f -c %S "$TEST_MNT")
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/foo"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/bar"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/baz"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/snap"
+
+tr '\000' 'A' < /dev/null |
+ run_check $SUDO_HELPER dd of=$TEST_MNT/foo/file_a bs=$BLOCK_SIZE count=32
+tr '\000' 'B' < /dev/null |
+ run_check $SUDO_HELPER dd of=$TEST_MNT/bar/file_b bs=$BLOCK_SIZE count=32
+
+run_check $SUDO_HELPER cp --reflink=always "$TEST_MNT/foo/file_a" "$TEST_MNT/baz/file_a"
+run_check $SUDO_HELPER cp --reflink=always "$TEST_MNT/bar/file_b" "$TEST_MNT/baz/file_b"
+
+# Filesystem looks like:
+#
+# .
+# |--- foo/
+# | |--- file_a
+# |--- bar/
+# | |--- file_b
+# |--- baz/
+# | |--- file_a (clone of "foo/file_a")
+# | |--- file_b (clone of "bar/file_b")
+# |--- snap/
+#
+
+# create snapshots and send streams
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "$TEST_MNT/foo" "$TEST_MNT/snap/foo.0"
+run_check $SUDO_HELPER "$TOP/btrfs" send -f "$srcdir/foo.0.snap" "$TEST_MNT/snap/foo.0"
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "$TEST_MNT/bar" "$TEST_MNT/snap/bar.0"
+run_check $SUDO_HELPER "$TOP/btrfs" send -f "$srcdir/bar.0.snap" "$TEST_MNT/snap/bar.0"
+
+run_check $SUDO_HELPER cp --reflink=always "$TEST_MNT/foo/file_a" "$TEST_MNT/foo/file_a.clone"
+run_check $SUDO_HELPER rm -f -- "$TEST_MNT/foo/file_a"
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "$TEST_MNT/foo" \
+ "$TEST_MNT/snap/foo.1"
+run_check $SUDO_HELPER "$TOP/btrfs" send -p "$TEST_MNT/snap/foo.0" -f "$srcdir/foo.1.snap" \
+ "$TEST_MNT/snap/foo.1"
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "$TEST_MNT/baz" \
+ "$TEST_MNT/snap/baz.0"
+run_check $SUDO_HELPER "$TOP/btrfs" send -p "$TEST_MNT/snap/foo.1" \
+ -c "$TEST_MNT/snap/bar.0" -f "$srcdir/baz.0.snap" \
+ "$TEST_MNT/snap/baz.0"
+
+# Filesystem looks like:
+#
+# .
+# |--- foo/
+# | |--- file_a.clone (clone of "foo/file_a")
+# |--- bar/
+# | |--- file_b
+# |--- baz/
+# | |--- file_a (clone of "foo/file_a")
+# | |--- file_b (clone of "bar/file_b")
+# |--- snap/
+# |--- bar.0/ (snapshot of "bar")
+# | |--- file_b
+# |--- foo.0/ (snapshot of "foo")
+# | |--- file_a
+# |--- foo.1/ (snapshot of "foo")
+# | |--- file_a.clone
+# |--- baz.0/ (snapshot of "baz")
+# | |--- file_a
+# | |--- file_b
+
+run_check $FSSUM_PROG -A -f -w "$srcdir/foo.0.fssum" "$TEST_MNT/snap/foo.0"
+run_check $FSSUM_PROG -A -f -w "$srcdir/foo.1.fssum" "$TEST_MNT/snap/foo.1"
+run_check $FSSUM_PROG -A -f -w "$srcdir/bar.0.fssum" "$TEST_MNT/snap/bar.0"
+run_check $FSSUM_PROG -A -f -w "$srcdir/baz.0.fssum" "$TEST_MNT/snap/baz.0"
+
+run_check_umount_test_dev
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+run_check_mount_test_dev
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/dest"
+run_check_umount_test_dev
+run_check_mount_test_dev -o subvol=/dest
+
+run_check $SUDO_HELPER "$TOP/btrfs" receive -f "$srcdir/foo.0.snap" "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" receive -f "$srcdir/bar.0.snap" "$TEST_MNT"
+
+# if "dest/" is not correctly stripped from the beginning of the path to
+# "foo.0" in the target fs, we would get an error here because the clone source
+# "foo.0/file_a" for "foo.1/file_a.clone" can't be found.
+run_check $SUDO_HELPER "$TOP/btrfs" receive -f "$srcdir/foo.1.snap" "$TEST_MNT"
+
+# same here, but with send -c instead of -p
+run_check $SUDO_HELPER "$TOP/btrfs" receive -f "$srcdir/baz.0.snap" "$TEST_MNT"
+
+run_check $FSSUM_PROG -r "$srcdir/foo.0.fssum" "$TEST_MNT/foo.0"
+run_check $FSSUM_PROG -r "$srcdir/foo.1.fssum" "$TEST_MNT/foo.1"
+run_check $FSSUM_PROG -r "$srcdir/bar.0.fssum" "$TEST_MNT/bar.0"
+run_check $FSSUM_PROG -r "$srcdir/baz.0.fssum" "$TEST_MNT/baz.0"
+
+run_check_umount_test_dev
+
+rm -rf -- "$srcdir"
diff --git a/tests/misc-tests/020-fix-superblock-corruption/test.sh b/tests/misc-tests/020-fix-superblock-corruption/test.sh
new file mode 100755
index 00000000..77c1a5aa
--- /dev/null
+++ b/tests/misc-tests/020-fix-superblock-corruption/test.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Corrupt primary superblock and restore it using backup superblock.
+
+source "$TOP/tests/common"
+
+check_prereq btrfs-select-super
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev
+
+FIRST_SUPERBLOCK_OFFSET=65536
+
+test_superblock_restore()
+{
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+
+ # Corrupt superblock checksum
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_DEV" \
+ seek="$FIRST_SUPERBLOCK_OFFSET" bs=1 count=4 conv=notrunc
+
+ # Run btrfs check to detect corruption
+ run_mayfail "$TOP/btrfs" check "$TEST_DEV" && \
+ _fail "btrfs check should detect corruption"
+
+ # Copy backup superblock to primary
+ run_check "$TOP/btrfs-select-super" -s 1 "$TEST_DEV"
+
+ # Perform btrfs check
+ run_check "$TOP/btrfs" check "$TEST_DEV"
+}
+
+test_superblock_restore
diff --git a/tests/misc-tests/021-image-multi-devices/test.sh b/tests/misc-tests/021-image-multi-devices/test.sh
new file mode 100755
index 00000000..abf67f90
--- /dev/null
+++ b/tests/misc-tests/021-image-multi-devices/test.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+# Test btrfs-image with multiple devices filesystem and verify that restoring
+# the created image works against a single device.
+
+source "$TOP/tests/common"
+
+check_prereq btrfs-image
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+
+rm -f dev1 dev2
+run_check truncate -s 2G dev1
+run_check truncate -s 2G dev2
+chmod a+w dev1 dev2
+
+loop1=$(run_check_stdout $SUDO_HELPER losetup --find --show dev1)
+loop2=$(run_check_stdout $SUDO_HELPER losetup --find --show dev2)
+
+# Create the test file system.
+
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f $loop1 $loop2
+run_check $SUDO_HELPER mount $loop1 "$TEST_MNT"
+run_check $SUDO_HELPER dd bs=1M count=1 if=/dev/zero "of=$TEST_MNT/foobar"
+orig_md5=$(run_check_stdout md5sum "$TEST_MNT/foobar" | cut -d ' ' -f 1)
+run_check $SUDO_HELPER umount "$TEST_MNT"
+
+# Create the image to restore later.
+run_check $SUDO_HELPER "$TOP/btrfs-image" $loop1 "$IMAGE"
+
+# Wipe out the filesystem from the devices, restore the image on a single
+# device, check everything works and file foobar is there and with 1Mb of
+# zeroes.
+run_check $SUDO_HELPER wipefs -a $loop1
+run_check $SUDO_HELPER wipefs -a $loop2
+
+run_check $SUDO_HELPER $TOP/btrfs-image -r "$IMAGE" $loop1
+
+run_check $SUDO_HELPER mount $loop1 "$TEST_MNT"
+new_md5=$(run_check_stdout md5sum "$TEST_MNT/foobar" | cut -d ' ' -f 1)
+run_check $SUDO_HELPER umount "$TEST_MNT"
+
+# Cleanup loop devices.
+run_check $SUDO_HELPER losetup -d $loop1
+run_check $SUDO_HELPER losetup -d $loop2
+rm -f dev1 dev2
+
+# Compare the file digests.
+[ $orig_md5 == $new_md5 ] || _fail "File digests do not match"
diff --git a/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
index 855fbd18..37846234 100755
--- a/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
+++ b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
@@ -8,6 +8,5 @@ check_prereq mkfs.btrfs
setup_root_helper
-run_check truncate -s 512M $IMAGE
-mixed=$(run_check_stdout $TOP/mkfs.btrfs -n 64k -f $IMAGE | egrep 'Data|Metadata')
+mixed=$(run_check_stdout "$TOP/mkfs.btrfs" -b 512M -n 64k -f "$TEST_DEV" | egrep 'Data|Metadata')
echo "$mixed" | grep -q -v 'Data+Metadata:' || _fail "unexpected: created a mixed-bg filesystem"
diff --git a/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh b/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh
index 289d5ff0..074fc22e 100755
--- a/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh
+++ b/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh
@@ -6,7 +6,6 @@ source $TOP/tests/common
check_prereq mkfs.btrfs
-run_check truncate -s 512M $IMAGE
-run_mayfail $TOP/mkfs.btrfs -f -M -s 4096 -n 16384 "$IMAGE" && _fail
+run_mayfail "$TOP/mkfs.btrfs" -b 512M -f -M -s 4096 -n 16384 "$TEST_DEV" && _fail
exit 0
diff --git a/tests/sha-private.h b/tests/sha-private.h
new file mode 100644
index 00000000..6e9c4520
--- /dev/null
+++ b/tests/sha-private.h
@@ -0,0 +1,28 @@
+/************************ sha-private.h ************************/
+/***************** See RFC 6234 for details. *******************/
+#ifndef _SHA_PRIVATE__H
+#define _SHA_PRIVATE__H
+/*
+ * These definitions are defined in FIPS 180-3, section 4.1.
+ * Ch() and Maj() are defined identically in sections 4.1.1,
+ * 4.1.2, and 4.1.3.
+ *
+ * The definitions used in FIPS 180-3 are as follows:
+ */
+
+#ifndef USE_MODIFIED_MACROS
+#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#else /* USE_MODIFIED_MACROS */
+/*
+ * The following definitions are equivalent and potentially faster.
+ */
+
+#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
+#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
+
+#endif /* USE_MODIFIED_MACROS */
+
+#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))
+
+#endif /* _SHA_PRIVATE__H */
diff --git a/tests/sha.h b/tests/sha.h
new file mode 100644
index 00000000..1ffd6880
--- /dev/null
+++ b/tests/sha.h
@@ -0,0 +1,356 @@
+/**************************** sha.h ****************************/
+/***************** See RFC 6234 for details. *******************/
+/*
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ - Redistributions of source code must retain the above
+ copyright notice, this list of conditions and
+ the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of Internet Society, IETF or IETF Trust, nor
+ the names of specific contributors, may be used to endorse or
+ promote products derived from this software without specific
+ prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef _SHA_H_
+#define _SHA_H_
+
+/*
+ * Description:
+ * This file implements the Secure Hash Algorithms
+ * as defined in the U.S. National Institute of Standards
+ * and Technology Federal Information Processing Standards
+ * Publication (FIPS PUB) 180-3 published in October 2008
+ * and formerly defined in its predecessors, FIPS PUB 180-1
+ * and FIP PUB 180-2.
+ *
+ * A combined document showing all algorithms is available at
+ * http://csrc.nist.gov/publications/fips/
+ * fips180-3/fips180-3_final.pdf
+ *
+ * The five hashes are defined in these sizes:
+ * SHA-1 20 byte / 160 bit
+ * SHA-224 28 byte / 224 bit
+ * SHA-256 32 byte / 256 bit
+ * SHA-384 48 byte / 384 bit
+ * SHA-512 64 byte / 512 bit
+ *
+ * Compilation Note:
+ * These files may be compiled with two options:
+ * USE_32BIT_ONLY - use 32-bit arithmetic only, for systems
+ * without 64-bit integers
+ *
+ * USE_MODIFIED_MACROS - use alternate form of the SHA_Ch()
+ * and SHA_Maj() macros that are equivalent
+ * and potentially faster on many systems
+ *
+ */
+
+#include <stdint.h>
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typedef the following:
+ * name meaning
+ * uint64_t unsigned 64-bit integer
+ * uint32_t unsigned 32-bit integer
+ * uint8_t unsigned 8-bit integer (i.e., unsigned char)
+ * int_least16_t integer of >= 16 bits
+ *
+ * See stdint-example.h
+ */
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+/*
+ * All SHA functions return one of these values.
+ */
+enum {
+ shaSuccess = 0,
+ shaNull, /* Null pointer parameter */
+ shaInputTooLong, /* input data too long */
+ shaStateError, /* called Input after FinalBits or Result */
+ shaBadParam /* passed a bad parameter */
+};
+#endif /* _SHA_enum_ */
+
+/*
+ * These constants hold size information for each of the SHA
+ * hashing operations
+ */
+enum {
+ SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64,
+ SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128,
+ SHA512_Message_Block_Size = 128,
+ USHA_Max_Message_Block_Size = SHA512_Message_Block_Size,
+
+ SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32,
+ SHA384HashSize = 48, SHA512HashSize = 64,
+ USHAMaxHashSize = SHA512HashSize,
+
+ SHA1HashSizeBits = 160, SHA224HashSizeBits = 224,
+ SHA256HashSizeBits = 256, SHA384HashSizeBits = 384,
+ SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits
+};
+
+/*
+ * These constants are used in the USHA (Unified SHA) functions.
+ */
+typedef enum SHAversion {
+ SHA1, SHA224, SHA256, SHA384, SHA512
+} SHAversion;
+
+/*
+ * This structure will hold context information for the SHA-1
+ * hashing operation.
+ */
+typedef struct SHA1Context {
+ uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
+
+ uint32_t Length_High; /* Message length in bits */
+ uint32_t Length_Low; /* Message length in bits */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 512-bit message blocks */
+ uint8_t Message_Block[SHA1_Message_Block_Size];
+
+ int Computed; /* Is the hash computed? */
+ int Corrupted; /* Cumulative corruption code */
+} SHA1Context;
+
+/*
+ * This structure will hold context information for the SHA-256
+ * hashing operation.
+ */
+typedef struct SHA256Context {
+ uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */
+
+ uint32_t Length_High; /* Message length in bits */
+ uint32_t Length_Low; /* Message length in bits */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 512-bit message blocks */
+ uint8_t Message_Block[SHA256_Message_Block_Size];
+
+ int Computed; /* Is the hash computed? */
+ int Corrupted; /* Cumulative corruption code */
+} SHA256Context;
+
+/*
+ * This structure will hold context information for the SHA-512
+ * hashing operation.
+ */
+typedef struct SHA512Context {
+#ifdef USE_32BIT_ONLY
+ uint32_t Intermediate_Hash[SHA512HashSize/4]; /* Message Digest */
+ uint32_t Length[4]; /* Message length in bits */
+#else /* !USE_32BIT_ONLY */
+ uint64_t Intermediate_Hash[SHA512HashSize/8]; /* Message Digest */
+ uint64_t Length_High, Length_Low; /* Message length in bits */
+#endif /* USE_32BIT_ONLY */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 1024-bit message blocks */
+ uint8_t Message_Block[SHA512_Message_Block_Size];
+
+ int Computed; /* Is the hash computed?*/
+ int Corrupted; /* Cumulative corruption code */
+} SHA512Context;
+
+/*
+ * This structure will hold context information for the SHA-224
+ * hashing operation. It uses the SHA-256 structure for computation.
+ */
+typedef struct SHA256Context SHA224Context;
+
+/*
+ * This structure will hold context information for the SHA-384
+ * hashing operation. It uses the SHA-512 structure for computation.
+ */
+typedef struct SHA512Context SHA384Context;
+
+/*
+ * This structure holds context information for all SHA
+ * hashing operations.
+ */
+typedef struct USHAContext {
+ int whichSha; /* which SHA is being used */
+ union {
+ SHA1Context sha1Context;
+ SHA224Context sha224Context; SHA256Context sha256Context;
+ SHA384Context sha384Context; SHA512Context sha512Context;
+ } ctx;
+} USHAContext;
+
+/*
+ * This structure will hold context information for the HMAC
+ * keyed-hashing operation.
+ */
+typedef struct HMACContext {
+ int whichSha; /* which SHA is being used */
+ int hashSize; /* hash size of SHA being used */
+ int blockSize; /* block size of SHA being used */
+ USHAContext shaContext; /* SHA context */
+ unsigned char k_opad[USHA_Max_Message_Block_Size];
+ /* outer padding - key XORd with opad */
+ int Computed; /* Is the MAC computed? */
+ int Corrupted; /* Cumulative corruption code */
+
+} HMACContext;
+
+/*
+ * This structure will hold context information for the HKDF
+ * extract-and-expand Key Derivation Functions.
+ */
+typedef struct HKDFContext {
+ int whichSha; /* which SHA is being used */
+ HMACContext hmacContext;
+ int hashSize; /* hash size of SHA being used */
+ unsigned char prk[USHAMaxHashSize];
+ /* pseudo-random key - output of hkdfInput */
+ int Computed; /* Is the key material computed? */
+ int Corrupted; /* Cumulative corruption code */
+} HKDFContext;
+
+/*
+ * Function Prototypes
+ */
+
+/* SHA-1 */
+extern int SHA1Reset(SHA1Context *);
+extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA1FinalBits(SHA1Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA1Result(SHA1Context *,
+ uint8_t Message_Digest[SHA1HashSize]);
+
+/* SHA-224 */
+extern int SHA224Reset(SHA224Context *);
+extern int SHA224Input(SHA224Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA224FinalBits(SHA224Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA224Result(SHA224Context *,
+ uint8_t Message_Digest[SHA224HashSize]);
+
+/* SHA-256 */
+extern int SHA256Reset(SHA256Context *);
+extern int SHA256Input(SHA256Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA256FinalBits(SHA256Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA256Result(SHA256Context *,
+ uint8_t Message_Digest[SHA256HashSize]);
+
+/* SHA-384 */
+extern int SHA384Reset(SHA384Context *);
+extern int SHA384Input(SHA384Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA384FinalBits(SHA384Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA384Result(SHA384Context *,
+ uint8_t Message_Digest[SHA384HashSize]);
+
+/* SHA-512 */
+extern int SHA512Reset(SHA512Context *);
+extern int SHA512Input(SHA512Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA512FinalBits(SHA512Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA512Result(SHA512Context *,
+ uint8_t Message_Digest[SHA512HashSize]);
+
+/* Unified SHA functions, chosen by whichSha */
+extern int USHAReset(USHAContext *context, SHAversion whichSha);
+extern int USHAInput(USHAContext *context,
+ const uint8_t *bytes, unsigned int bytecount);
+extern int USHAFinalBits(USHAContext *context,
+ uint8_t bits, unsigned int bit_count);
+extern int USHAResult(USHAContext *context,
+ uint8_t Message_Digest[USHAMaxHashSize]);
+extern int USHABlockSize(enum SHAversion whichSha);
+extern int USHAHashSize(enum SHAversion whichSha);
+extern int USHAHashSizeBits(enum SHAversion whichSha);
+extern const char *USHAHashName(enum SHAversion whichSha);
+
+/*
+ * HMAC Keyed-Hashing for Message Authentication, RFC 2104,
+ * for all SHAs.
+ * This interface allows a fixed-length text input to be used.
+ */
+extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */
+ const unsigned char *text, /* pointer to data stream */
+ int text_len, /* length of data stream */
+ const unsigned char *key, /* pointer to authentication key */
+ int key_len, /* length of authentication key */
+ uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */
+
+/*
+ * HMAC Keyed-Hashing for Message Authentication, RFC 2104,
+ * for all SHAs.
+ * This interface allows any length of text input to be used.
+ */
+extern int hmacReset(HMACContext *context, enum SHAversion whichSha,
+ const unsigned char *key, int key_len);
+extern int hmacInput(HMACContext *context, const unsigned char *text,
+ int text_len);
+extern int hmacFinalBits(HMACContext *context, uint8_t bits,
+ unsigned int bit_count);
+extern int hmacResult(HMACContext *context,
+ uint8_t digest[USHAMaxHashSize]);
+
+/*
+ * HKDF HMAC-based Extract-and-Expand Key Derivation Function,
+ * RFC 5869, for all SHAs.
+ */
+extern int hkdf(SHAversion whichSha, const unsigned char *salt,
+ int salt_len, const unsigned char *ikm, int ikm_len,
+ const unsigned char *info, int info_len,
+ uint8_t okm[ ], int okm_len);
+extern int hkdfExtract(SHAversion whichSha, const unsigned char *salt,
+ int salt_len, const unsigned char *ikm,
+ int ikm_len, uint8_t prk[USHAMaxHashSize]);
+extern int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ],
+ int prk_len, const unsigned char *info,
+ int info_len, uint8_t okm[ ], int okm_len);
+
+/*
+ * HKDF HMAC-based Extract-and-Expand Key Derivation Function,
+ * RFC 5869, for all SHAs.
+ * This interface allows any length of text input to be used.
+ */
+extern int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
+ const unsigned char *salt, int salt_len);
+extern int hkdfInput(HKDFContext *context, const unsigned char *ikm,
+ int ikm_len);
+extern int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
+ unsigned int ikm_bit_count);
+extern int hkdfResult(HKDFContext *context,
+ uint8_t prk[USHAMaxHashSize],
+ const unsigned char *info, int info_len,
+ uint8_t okm[USHAMaxHashSize], int okm_len);
+#endif /* _SHA_H_ */
diff --git a/tests/sha224-256.c b/tests/sha224-256.c
new file mode 100644
index 00000000..2d963e65
--- /dev/null
+++ b/tests/sha224-256.c
@@ -0,0 +1,601 @@
+/*
+RFC 6234 SHAs, HMAC-SHAs, and HKDF May 2011
+
+
+Copyright Notice
+
+ Copyright (c) 2011 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+*/
+
+/************************* sha224-256.c ************************/
+/***************** See RFC 6234 for details. *******************/
+/* Copyright (c) 2011 IETF Trust and the persons identified as */
+/* authors of the code. All rights reserved. */
+/* See sha.h for terms of use and redistribution. */
+
+/*
+ * Description:
+ * This file implements the Secure Hash Algorithms SHA-224 and
+ * SHA-256 as defined in the U.S. National Institute of Standards
+ * and Technology Federal Information Processing Standards
+ * Publication (FIPS PUB) 180-3 published in October 2008
+ * and formerly defined in its predecessors, FIPS PUB 180-1
+ * and FIP PUB 180-2.
+ *
+ * A combined document showing all algorithms is available at
+ * http://csrc.nist.gov/publications/fips/
+ * fips180-3/fips180-3_final.pdf
+ *
+ * The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
+ * message digests for a given data stream. It should take about
+ * 2**n steps to find a message with the same digest as a given
+ * message and 2**(n/2) to find any two messages with the same
+ * digest, when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-224 and SHA-256 are defined in terms of 32-bit "words".
+ * This code uses <stdint.h> (included via "sha.h") to define 32-
+ * and 8-bit unsigned integer types. If your C compiler does not
+ * support 32-bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-224 and SHA-256 are designed to work with messages less
+ * than 2^64 bits long. This implementation uses SHA224/256Input()
+ * to hash the bits that are a multiple of the size of an 8-bit
+ * octet, and then optionally uses SHA224/256FinalBits()
+ * to hash the final few bits of the input.
+ */
+
+#include "tests/sha.h"
+#include "tests/sha-private.h"
+
+/* Define the SHA shift, rotate left, and rotate right macros */
+#define SHA256_SHR(bits,word) ((word) >> (bits))
+#define SHA256_ROTL(bits,word) \
+ (((word) << (bits)) | ((word) >> (32-(bits))))
+#define SHA256_ROTR(bits,word) \
+ (((word) >> (bits)) | ((word) << (32-(bits))))
+
+/* Define the SHA SIGMA and sigma macros */
+#define SHA256_SIGMA0(word) \
+ (SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
+#define SHA256_SIGMA1(word) \
+ (SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
+#define SHA256_sigma0(word) \
+ (SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
+#define SHA256_sigma1(word) \
+ (SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
+
+/*
+ * Add "length" to the length.
+ * Set Corrupted when overflow has occurred.
+ */
+static uint32_t addTemp;
+#define SHA224_256AddLength(context, length) \
+ (addTemp = (context)->Length_Low, (context)->Corrupted = \
+ (((context)->Length_Low += (length)) < addTemp) && \
+ (++(context)->Length_High == 0) ? shaInputTooLong : \
+ (context)->Corrupted )
+
+/* Local Function Prototypes */
+static int SHA224_256Reset(SHA256Context *context, uint32_t *H0);
+static void SHA224_256ProcessMessageBlock(SHA256Context *context);
+static void SHA224_256Finalize(SHA256Context *context,
+ uint8_t Pad_Byte);
+static void SHA224_256PadMessage(SHA256Context *context,
+ uint8_t Pad_Byte);
+static int SHA224_256ResultN(SHA256Context *context,
+ uint8_t Message_Digest[ ], int HashSize);
+
+/* Initial Hash Values: FIPS 180-3 section 5.3.2 */
+static uint32_t SHA224_H0[SHA256HashSize/4] = {
+ 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939,
+ 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4
+};
+
+/* Initial Hash Values: FIPS 180-3 section 5.3.3 */
+static uint32_t SHA256_H0[SHA256HashSize/4] = {
+ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
+ 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
+};
+
+/*
+ * SHA224Reset
+ *
+ * Description:
+ * This function will initialize the SHA224Context in preparation
+ * for computing a new SHA224 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224Reset(SHA224Context *context)
+{
+ return SHA224_256Reset(context, SHA224_H0);
+}
+
+/*
+ * SHA224Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_array[ ]: [in]
+ * An array of octets representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int SHA224Input(SHA224Context *context, const uint8_t *message_array,
+ unsigned int length)
+{
+ return SHA256Input(context, message_array, length);
+}
+
+/*
+ * SHA224FinalBits
+ *
+ * Description:
+ * This function will add in any final bits of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_bits: [in]
+ * The final bits of the message, in the upper portion of the
+ * byte. (Use 0b###00000 instead of 0b00000### to input the
+ * three bits ###.)
+ * length: [in]
+ * The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224FinalBits(SHA224Context *context,
+ uint8_t message_bits, unsigned int length)
+{
+ return SHA256FinalBits(context, message_bits, length);
+}
+
+/*
+ * SHA224Result
+ *
+ * Description:
+ * This function will return the 224-bit message digest
+ * into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 27.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224Result(SHA224Context *context,
+ uint8_t Message_Digest[SHA224HashSize])
+{
+ return SHA224_256ResultN(context, Message_Digest, SHA224HashSize);
+}
+
+/*
+ * SHA256Reset
+ *
+ * Description:
+ * This function will initialize the SHA256Context in preparation
+ * for computing a new SHA256 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Reset(SHA256Context *context)
+{
+ return SHA224_256Reset(context, SHA256_H0);
+}
+
+/*
+ * SHA256Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_array[ ]: [in]
+ * An array of octets representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Input(SHA256Context *context, const uint8_t *message_array,
+ unsigned int length)
+{
+ if (!context) return shaNull;
+ if (!length) return shaSuccess;
+ if (!message_array) return shaNull;
+ if (context->Computed) return context->Corrupted = shaStateError;
+ if (context->Corrupted) return context->Corrupted;
+
+ while (length--) {
+ context->Message_Block[context->Message_Block_Index++] =
+ *message_array;
+
+ if ((SHA224_256AddLength(context, 8) == shaSuccess) &&
+ (context->Message_Block_Index == SHA256_Message_Block_Size))
+ SHA224_256ProcessMessageBlock(context);
+
+ message_array++;
+ }
+
+ return context->Corrupted;
+
+}
+
+/*
+ * SHA256FinalBits
+ *
+ * Description:
+ * This function will add in any final bits of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_bits: [in]
+ * The final bits of the message, in the upper portion of the
+ * byte. (Use 0b###00000 instead of 0b00000### to input the
+ * three bits ###.)
+ * length: [in]
+ * The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256FinalBits(SHA256Context *context,
+ uint8_t message_bits, unsigned int length)
+{
+ static uint8_t masks[8] = {
+ /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
+ /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
+ /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
+ /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
+ };
+ static uint8_t markbit[8] = {
+ /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
+ /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
+ /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
+ /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
+ };
+
+ if (!context) return shaNull;
+ if (!length) return shaSuccess;
+ if (context->Corrupted) return context->Corrupted;
+ if (context->Computed) return context->Corrupted = shaStateError;
+ if (length >= 8) return context->Corrupted = shaBadParam;
+
+ SHA224_256AddLength(context, length);
+ SHA224_256Finalize(context, (uint8_t)
+ ((message_bits & masks[length]) | markbit[length]));
+
+ return context->Corrupted;
+}
+
+/*
+ * SHA256Result
+ *
+ * Description:
+ * This function will return the 256-bit message digest
+ * into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 31.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Result(SHA256Context *context,
+ uint8_t Message_Digest[SHA256HashSize])
+{
+ return SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
+}
+
+/*
+ * SHA224_256Reset
+ *
+ * Description:
+ * This helper function will initialize the SHA256Context in
+ * preparation for computing a new SHA-224 or SHA-256 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ * H0[ ]: [in]
+ * The initial hash value array to use.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static int SHA224_256Reset(SHA256Context *context, uint32_t *H0)
+{
+ if (!context) return shaNull;
+
+ context->Length_High = context->Length_Low = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = H0[0];
+ context->Intermediate_Hash[1] = H0[1];
+ context->Intermediate_Hash[2] = H0[2];
+ context->Intermediate_Hash[3] = H0[3];
+ context->Intermediate_Hash[4] = H0[4];
+ context->Intermediate_Hash[5] = H0[5];
+ context->Intermediate_Hash[6] = H0[6];
+ context->Intermediate_Hash[7] = H0[7];
+
+ context->Computed = 0;
+ context->Corrupted = shaSuccess;
+
+ return shaSuccess;
+}
+
+/*
+ * SHA224_256ProcessMessageBlock
+ *
+ * Description:
+ * This helper function will process the next 512 bits of the
+ * message stored in the Message_Block array.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the Secure Hash Standard.
+ */
+static void SHA224_256ProcessMessageBlock(SHA256Context *context)
+{
+ /* Constants defined in FIPS 180-3, section 4.2.2 */
+ static const uint32_t K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
+ 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
+ 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
+ 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
+ 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
+ 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
+ 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
+ 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
+ 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+ int t, t4; /* Loop counter */
+ uint32_t temp1, temp2; /* Temporary word value */
+ uint32_t W[64]; /* Word sequence */
+ uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = t4 = 0; t < 16; t++, t4 += 4)
+ W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
+ (((uint32_t)context->Message_Block[t4 + 1]) << 16) |
+ (((uint32_t)context->Message_Block[t4 + 2]) << 8) |
+ (((uint32_t)context->Message_Block[t4 + 3]));
+
+ for (t = 16; t < 64; t++)
+ W[t] = SHA256_sigma1(W[t-2]) + W[t-7] +
+ SHA256_sigma0(W[t-15]) + W[t-16];
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+ F = context->Intermediate_Hash[5];
+ G = context->Intermediate_Hash[6];
+ H = context->Intermediate_Hash[7];
+
+ for (t = 0; t < 64; t++) {
+ temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t];
+ temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C);
+ H = G;
+ G = F;
+ F = E;
+ E = D + temp1;
+ D = C;
+ C = B;
+ B = A;
+ A = temp1 + temp2;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+ context->Intermediate_Hash[5] += F;
+ context->Intermediate_Hash[6] += G;
+ context->Intermediate_Hash[7] += H;
+
+ context->Message_Block_Index = 0;
+}
+
+/*
+ * SHA224_256Finalize
+ *
+ * Description:
+ * This helper function finishes off the digest calculations.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * Pad_Byte: [in]
+ * The last byte to add to the message block before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static void SHA224_256Finalize(SHA256Context *context,
+ uint8_t Pad_Byte)
+{
+ int i;
+ SHA224_256PadMessage(context, Pad_Byte);
+ /* message may be sensitive, so clear it out */
+ for (i = 0; i < SHA256_Message_Block_Size; ++i)
+ context->Message_Block[i] = 0;
+ context->Length_High = 0; /* and clear length */
+ context->Length_Low = 0;
+ context->Computed = 1;
+}
+
+/*
+ * SHA224_256PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to the next
+ * even multiple of 512 bits. The first padding bit must be a '1'.
+ * The last 64 bits represent the length of the original message.
+ * All bits in between should be 0. This helper function will pad
+ * the message according to those rules by filling the
+ * Message_Block array accordingly. When it returns, it can be
+ * assumed that the message digest has been computed.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to pad.
+ * Pad_Byte: [in]
+ * The last byte to add to the message block before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * Nothing.
+ */
+static void SHA224_256PadMessage(SHA256Context *context,
+ uint8_t Pad_Byte)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) {
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+ while (context->Message_Block_Index < SHA256_Message_Block_Size)
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ SHA224_256ProcessMessageBlock(context);
+ } else
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+
+ while (context->Message_Block_Index < (SHA256_Message_Block_Size-8))
+ context->Message_Block[context->Message_Block_Index++] = 0;
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
+ context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
+ context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
+ context->Message_Block[59] = (uint8_t)(context->Length_High);
+ context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
+ context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
+ context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
+ context->Message_Block[63] = (uint8_t)(context->Length_Low);
+
+ SHA224_256ProcessMessageBlock(context);
+}
+
+/*
+ * SHA224_256ResultN
+ *
+ * Description:
+ * This helper function will return the 224-bit or 256-bit message
+ * digest into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 27/31.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ * HashSize: [in]
+ * The size of the hash, either 28 or 32.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static int SHA224_256ResultN(SHA256Context *context,
+ uint8_t Message_Digest[ ], int HashSize)
+{
+ int i;
+
+ if (!context) return shaNull;
+ if (!Message_Digest) return shaNull;
+ if (context->Corrupted) return context->Corrupted;
+
+ if (!context->Computed)
+ SHA224_256Finalize(context, 0x80);
+
+ for (i = 0; i < HashSize; ++i)
+ Message_Digest[i] = (uint8_t)
+ (context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
+
+ return shaSuccess;
+}
diff --git a/transaction.h b/transaction.h
index 13e09a64..0c41af51 100644
--- a/transaction.h
+++ b/transaction.h
@@ -54,11 +54,4 @@ btrfs_start_transaction(struct btrfs_root *root, int num_blocks)
return h;
}
-static inline void btrfs_free_transaction(struct btrfs_root *root,
- struct btrfs_trans_handle *handle)
-{
- memset(handle, 0, sizeof(*handle));
- free(handle);
-}
-
#endif
diff --git a/utils.c b/utils.c
index 5a481976..bb049133 100644
--- a/utils.c
+++ b/utils.c
@@ -38,8 +38,6 @@
#include <sys/statfs.h>
#include <linux/magic.h>
#include <getopt.h>
-#include <sys/utsname.h>
-#include <linux/version.h>
#include "kerncompat.h"
#include "radix-tree.h"
@@ -51,6 +49,7 @@
#include "volumes.h"
#include "ioctl.h"
#include "commands.h"
+#include "mkfs/common.h"
#ifndef BLKDISCARD
#define BLKDISCARD _IO(0x12,119)
@@ -58,60 +57,10 @@
static int btrfs_scan_done = 0;
-static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
-
static int rand_seed_initlized = 0;
static unsigned short rand_seed[3];
-const char *get_argv0_buf(void)
-{
- return argv0_buf;
-}
-
-void fixup_argv0(char **argv, const char *token)
-{
- int len = strlen(argv0_buf);
-
- snprintf(argv0_buf + len, sizeof(argv0_buf) - len, " %s", token);
- argv[0] = argv0_buf;
-}
-
-void set_argv0(char **argv)
-{
- strncpy(argv0_buf, argv[0], sizeof(argv0_buf));
- argv0_buf[sizeof(argv0_buf) - 1] = 0;
-}
-
-int check_argc_exact(int nargs, int expected)
-{
- if (nargs < expected)
- fprintf(stderr, "%s: too few arguments\n", argv0_buf);
- if (nargs > expected)
- fprintf(stderr, "%s: too many arguments\n", argv0_buf);
-
- return nargs != expected;
-}
-
-int check_argc_min(int nargs, int expected)
-{
- if (nargs < expected) {
- fprintf(stderr, "%s: too few arguments\n", argv0_buf);
- return 1;
- }
-
- return 0;
-}
-
-int check_argc_max(int nargs, int expected)
-{
- if (nargs > expected) {
- fprintf(stderr, "%s: too many arguments\n", argv0_buf);
- return 1;
- }
-
- return 0;
-}
-
+struct btrfs_config bconf;
/*
* Discard the given range in one go
@@ -132,7 +81,7 @@ static int discard_blocks(int fd, u64 start, u64 len)
{
while (len > 0) {
/* 1G granularity */
- u64 chunk_size = min_t(u64, len, 1*1024*1024*1024);
+ u64 chunk_size = min_t(u64, len, SZ_1G);
int ret;
ret = discard_range(fd, start, chunk_size);
@@ -145,15 +94,6 @@ static int discard_blocks(int fd, u64 start, u64 len)
return 0;
}
-static u64 reference_root_table[] = {
- [1] = BTRFS_ROOT_TREE_OBJECTID,
- [2] = BTRFS_EXTENT_TREE_OBJECTID,
- [3] = BTRFS_CHUNK_TREE_OBJECTID,
- [4] = BTRFS_DEV_TREE_OBJECTID,
- [5] = BTRFS_FS_TREE_OBJECTID,
- [6] = BTRFS_CSUM_TREE_OBJECTID,
-};
-
int test_uuid_unique(char *fs_uuid)
{
int unique = 1;
@@ -183,1474 +123,6 @@ int test_uuid_unique(char *fs_uuid)
return unique;
}
-/*
- * Reserve space from free_tree.
- * The algorithm is very simple, find the first cache_extent with enough space
- * and allocate from its beginning.
- */
-static int reserve_free_space(struct cache_tree *free_tree, u64 len,
- u64 *ret_start)
-{
- struct cache_extent *cache;
- int found = 0;
-
- ASSERT(ret_start != NULL);
- cache = first_cache_extent(free_tree);
- while (cache) {
- if (cache->size > len) {
- found = 1;
- *ret_start = cache->start;
-
- cache->size -= len;
- if (cache->size == 0) {
- remove_cache_extent(free_tree, cache);
- free(cache);
- } else {
- cache->start += len;
- }
- break;
- }
- cache = next_cache_extent(cache);
- }
- if (!found)
- return -ENOSPC;
- return 0;
-}
-
-static inline int write_temp_super(int fd, struct btrfs_super_block *sb,
- u64 sb_bytenr)
-{
- u32 crc = ~(u32)0;
- int ret;
-
- crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
- BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, &sb->csum[0]);
- ret = pwrite(fd, sb, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
- if (ret < BTRFS_SUPER_INFO_SIZE)
- ret = (ret < 0 ? -errno : -EIO);
- else
- ret = 0;
- return ret;
-}
-
-/*
- * Setup temporary superblock at cfg->super_bynter
- * Needed info are extracted from cfg, and root_bytenr, chunk_bytenr
- *
- * For now sys chunk array will be empty and dev_item is empty too.
- * They will be re-initialized at temp chunk tree setup.
- *
- * The superblock signature is not valid, denotes a partially created
- * filesystem, needs to be finalized.
- */
-static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg,
- u64 root_bytenr, u64 chunk_bytenr)
-{
- unsigned char chunk_uuid[BTRFS_UUID_SIZE];
- char super_buf[BTRFS_SUPER_INFO_SIZE];
- struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf;
- int ret;
-
- memset(super_buf, 0, BTRFS_SUPER_INFO_SIZE);
- cfg->num_bytes = round_down(cfg->num_bytes, cfg->sectorsize);
-
- if (*cfg->fs_uuid) {
- if (uuid_parse(cfg->fs_uuid, super->fsid) != 0) {
- error("cound not parse UUID: %s", cfg->fs_uuid);
- ret = -EINVAL;
- goto out;
- }
- if (!test_uuid_unique(cfg->fs_uuid)) {
- error("non-unique UUID: %s", cfg->fs_uuid);
- ret = -EINVAL;
- goto out;
- }
- } else {
- uuid_generate(super->fsid);
- uuid_unparse(super->fsid, cfg->fs_uuid);
- }
- uuid_generate(chunk_uuid);
- uuid_unparse(chunk_uuid, cfg->chunk_uuid);
-
- btrfs_set_super_bytenr(super, cfg->super_bytenr);
- btrfs_set_super_num_devices(super, 1);
- btrfs_set_super_magic(super, BTRFS_MAGIC_PARTIAL);
- btrfs_set_super_generation(super, 1);
- btrfs_set_super_root(super, root_bytenr);
- btrfs_set_super_chunk_root(super, chunk_bytenr);
- btrfs_set_super_total_bytes(super, cfg->num_bytes);
- /*
- * Temporary filesystem will only have 6 tree roots:
- * chunk tree, root tree, extent_tree, device tree, fs tree
- * and csum tree.
- */
- btrfs_set_super_bytes_used(super, 6 * cfg->nodesize);
- btrfs_set_super_sectorsize(super, cfg->sectorsize);
- btrfs_set_super_leafsize(super, cfg->nodesize);
- btrfs_set_super_nodesize(super, cfg->nodesize);
- btrfs_set_super_stripesize(super, cfg->stripesize);
- btrfs_set_super_csum_type(super, BTRFS_CSUM_TYPE_CRC32);
- btrfs_set_super_chunk_root(super, chunk_bytenr);
- btrfs_set_super_cache_generation(super, -1);
- btrfs_set_super_incompat_flags(super, cfg->features);
- if (cfg->label)
- __strncpy_null(super->label, cfg->label, BTRFS_LABEL_SIZE - 1);
-
- /* Sys chunk array will be re-initialized at chunk tree init time */
- super->sys_chunk_array_size = 0;
-
- ret = write_temp_super(fd, super, cfg->super_bytenr);
-out:
- return ret;
-}
-
-/*
- * Setup an extent buffer for tree block.
- */
-static int setup_temp_extent_buffer(struct extent_buffer *buf,
- struct btrfs_mkfs_config *cfg,
- u64 bytenr, u64 owner)
-{
- unsigned char fsid[BTRFS_FSID_SIZE];
- unsigned char chunk_uuid[BTRFS_UUID_SIZE];
- int ret;
-
- ret = uuid_parse(cfg->fs_uuid, fsid);
- if (ret)
- return -EINVAL;
- ret = uuid_parse(cfg->chunk_uuid, chunk_uuid);
- if (ret)
- return -EINVAL;
-
- memset(buf->data, 0, cfg->nodesize);
- buf->len = cfg->nodesize;
- btrfs_set_header_bytenr(buf, bytenr);
- btrfs_set_header_generation(buf, 1);
- btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
- btrfs_set_header_owner(buf, owner);
- btrfs_set_header_flags(buf, BTRFS_HEADER_FLAG_WRITTEN);
- write_extent_buffer(buf, chunk_uuid, btrfs_header_chunk_tree_uuid(buf),
- BTRFS_UUID_SIZE);
- write_extent_buffer(buf, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE);
- return 0;
-}
-
-static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf,
- u64 bytenr)
-{
- int ret;
-
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
-
- /* Temporary extent buffer is always mapped 1:1 on disk */
- ret = pwrite(fd, buf->data, buf->len, bytenr);
- if (ret < buf->len)
- ret = (ret < 0 ? ret : -EIO);
- else
- ret = 0;
- return ret;
-}
-
-/*
- * Insert a root item for temporary tree root
- *
- * Only used in make_btrfs_v2().
- */
-static void insert_temp_root_item(struct extent_buffer *buf,
- struct btrfs_mkfs_config *cfg,
- int *slot, u32 *itemoff, u64 objectid,
- u64 bytenr)
-{
- struct btrfs_root_item root_item;
- struct btrfs_inode_item *inode_item;
- struct btrfs_disk_key disk_key;
-
- btrfs_set_header_nritems(buf, *slot + 1);
- (*itemoff) -= sizeof(root_item);
- memset(&root_item, 0, sizeof(root_item));
- inode_item = &root_item.inode;
- btrfs_set_stack_inode_generation(inode_item, 1);
- btrfs_set_stack_inode_size(inode_item, 3);
- btrfs_set_stack_inode_nlink(inode_item, 1);
- btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
- btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
- btrfs_set_root_refs(&root_item, 1);
- btrfs_set_root_used(&root_item, cfg->nodesize);
- btrfs_set_root_generation(&root_item, 1);
- btrfs_set_root_bytenr(&root_item, bytenr);
-
- memset(&disk_key, 0, sizeof(disk_key));
- btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY);
- btrfs_set_disk_key_objectid(&disk_key, objectid);
- btrfs_set_disk_key_offset(&disk_key, 0);
-
- btrfs_set_item_key(buf, &disk_key, *slot);
- btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(root_item));
- write_extent_buffer(buf, &root_item,
- btrfs_item_ptr_offset(buf, *slot),
- sizeof(root_item));
- (*slot)++;
-}
-
-static int setup_temp_root_tree(int fd, struct btrfs_mkfs_config *cfg,
- u64 root_bytenr, u64 extent_bytenr,
- u64 dev_bytenr, u64 fs_bytenr, u64 csum_bytenr)
-{
- struct extent_buffer *buf = NULL;
- u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
- int slot = 0;
- int ret;
-
- /*
- * Provided bytenr must in ascending order, or tree root will have a
- * bad key order.
- */
- if (!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr &&
- dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr)) {
- error("bad tree bytenr order: "
- "root < extent %llu < %llu, "
- "extent < dev %llu < %llu, "
- "dev < fs %llu < %llu, "
- "fs < csum %llu < %llu",
- (unsigned long long)root_bytenr,
- (unsigned long long)extent_bytenr,
- (unsigned long long)extent_bytenr,
- (unsigned long long)dev_bytenr,
- (unsigned long long)dev_bytenr,
- (unsigned long long)fs_bytenr,
- (unsigned long long)fs_bytenr,
- (unsigned long long)csum_bytenr);
- return -EINVAL;
- }
- buf = malloc(sizeof(*buf) + cfg->nodesize);
- if (!buf)
- return -ENOMEM;
-
- ret = setup_temp_extent_buffer(buf, cfg, root_bytenr,
- BTRFS_ROOT_TREE_OBJECTID);
- if (ret < 0)
- goto out;
-
- insert_temp_root_item(buf, cfg, &slot, &itemoff,
- BTRFS_EXTENT_TREE_OBJECTID, extent_bytenr);
- insert_temp_root_item(buf, cfg, &slot, &itemoff,
- BTRFS_DEV_TREE_OBJECTID, dev_bytenr);
- insert_temp_root_item(buf, cfg, &slot, &itemoff,
- BTRFS_FS_TREE_OBJECTID, fs_bytenr);
- insert_temp_root_item(buf, cfg, &slot, &itemoff,
- BTRFS_CSUM_TREE_OBJECTID, csum_bytenr);
-
- ret = write_temp_extent_buffer(fd, buf, root_bytenr);
-out:
- free(buf);
- return ret;
-}
-
-static int insert_temp_dev_item(int fd, struct extent_buffer *buf,
- struct btrfs_mkfs_config *cfg,
- int *slot, u32 *itemoff)
-{
- struct btrfs_disk_key disk_key;
- struct btrfs_dev_item *dev_item;
- char super_buf[BTRFS_SUPER_INFO_SIZE];
- unsigned char dev_uuid[BTRFS_UUID_SIZE];
- unsigned char fsid[BTRFS_FSID_SIZE];
- struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf;
- int ret;
-
- ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE, cfg->super_bytenr);
- if (ret < BTRFS_SUPER_INFO_SIZE) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
-
- btrfs_set_header_nritems(buf, *slot + 1);
- (*itemoff) -= sizeof(*dev_item);
- /* setup device item 1, 0 is for replace case */
- btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
- btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
- btrfs_set_disk_key_offset(&disk_key, 1);
- btrfs_set_item_key(buf, &disk_key, *slot);
- btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_item));
-
- dev_item = btrfs_item_ptr(buf, *slot, struct btrfs_dev_item);
- /* Generate device uuid */
- uuid_generate(dev_uuid);
- write_extent_buffer(buf, dev_uuid,
- (unsigned long)btrfs_device_uuid(dev_item),
- BTRFS_UUID_SIZE);
- uuid_parse(cfg->fs_uuid, fsid);
- write_extent_buffer(buf, fsid,
- (unsigned long)btrfs_device_fsid(dev_item),
- BTRFS_FSID_SIZE);
- btrfs_set_device_id(buf, dev_item, 1);
- btrfs_set_device_generation(buf, dev_item, 0);
- btrfs_set_device_total_bytes(buf, dev_item, cfg->num_bytes);
- /*
- * The number must match the initial SYSTEM and META chunk size
- */
- btrfs_set_device_bytes_used(buf, dev_item,
- BTRFS_MKFS_SYSTEM_GROUP_SIZE +
- BTRFS_CONVERT_META_GROUP_SIZE);
- btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize);
- btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize);
- btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize);
- btrfs_set_device_type(buf, dev_item, 0);
-
- /* Super dev_item is not complete, copy the complete one to sb */
- read_extent_buffer(buf, &super->dev_item, (unsigned long)dev_item,
- sizeof(*dev_item));
- ret = write_temp_super(fd, super, cfg->super_bytenr);
- (*slot)++;
-out:
- return ret;
-}
-
-static int insert_temp_chunk_item(int fd, struct extent_buffer *buf,
- struct btrfs_mkfs_config *cfg,
- int *slot, u32 *itemoff, u64 start, u64 len,
- u64 type)
-{
- struct btrfs_chunk *chunk;
- struct btrfs_disk_key disk_key;
- char super_buf[BTRFS_SUPER_INFO_SIZE];
- struct btrfs_super_block *sb = (struct btrfs_super_block *)super_buf;
- int ret = 0;
-
- ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE,
- cfg->super_bytenr);
- if (ret < BTRFS_SUPER_INFO_SIZE) {
- ret = (ret < 0 ? ret : -EIO);
- return ret;
- }
-
- btrfs_set_header_nritems(buf, *slot + 1);
- (*itemoff) -= btrfs_chunk_item_size(1);
- btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
- btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
- btrfs_set_disk_key_offset(&disk_key, start);
- btrfs_set_item_key(buf, &disk_key, *slot);
- btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(*slot),
- btrfs_chunk_item_size(1));
-
- chunk = btrfs_item_ptr(buf, *slot, struct btrfs_chunk);
- btrfs_set_chunk_length(buf, chunk, len);
- btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID);
- btrfs_set_chunk_stripe_len(buf, chunk, 64 * 1024);
- btrfs_set_chunk_type(buf, chunk, type);
- btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize);
- btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize);
- btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize);
- btrfs_set_chunk_num_stripes(buf, chunk, 1);
- /* TODO: Support DUP profile for system chunk */
- btrfs_set_stripe_devid_nr(buf, chunk, 0, 1);
- /* We are doing 1:1 mapping, so start is its dev offset */
- btrfs_set_stripe_offset_nr(buf, chunk, 0, start);
- write_extent_buffer(buf, &sb->dev_item.uuid,
- (unsigned long)btrfs_stripe_dev_uuid_nr(chunk, 0),
- BTRFS_UUID_SIZE);
- (*slot)++;
-
- /*
- * If it's system chunk, also copy it to super block.
- */
- if (type & BTRFS_BLOCK_GROUP_SYSTEM) {
- char *cur;
- u32 array_size;
-
- cur = (char *)sb->sys_chunk_array
- + btrfs_super_sys_array_size(sb);
- memcpy(cur, &disk_key, sizeof(disk_key));
- cur += sizeof(disk_key);
- read_extent_buffer(buf, cur, (unsigned long int)chunk,
- btrfs_chunk_item_size(1));
- array_size = btrfs_super_sys_array_size(sb);
- array_size += btrfs_chunk_item_size(1) +
- sizeof(disk_key);
- btrfs_set_super_sys_array_size(sb, array_size);
-
- ret = write_temp_super(fd, sb, cfg->super_bytenr);
- }
- return ret;
-}
-
-static int setup_temp_chunk_tree(int fd, struct btrfs_mkfs_config *cfg,
- u64 sys_chunk_start, u64 meta_chunk_start,
- u64 chunk_bytenr)
-{
- struct extent_buffer *buf = NULL;
- u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
- int slot = 0;
- int ret;
-
- /* Must ensure SYS chunk starts before META chunk */
- if (meta_chunk_start < sys_chunk_start) {
- error("wrong chunk order: meta < system %llu < %llu",
- (unsigned long long)meta_chunk_start,
- (unsigned long long)sys_chunk_start);
- return -EINVAL;
- }
- buf = malloc(sizeof(*buf) + cfg->nodesize);
- if (!buf)
- return -ENOMEM;
- ret = setup_temp_extent_buffer(buf, cfg, chunk_bytenr,
- BTRFS_CHUNK_TREE_OBJECTID);
- if (ret < 0)
- goto out;
-
- ret = insert_temp_dev_item(fd, buf, cfg, &slot, &itemoff);
- if (ret < 0)
- goto out;
- ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
- sys_chunk_start,
- BTRFS_MKFS_SYSTEM_GROUP_SIZE,
- BTRFS_BLOCK_GROUP_SYSTEM);
- if (ret < 0)
- goto out;
- ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
- meta_chunk_start,
- BTRFS_CONVERT_META_GROUP_SIZE,
- BTRFS_BLOCK_GROUP_METADATA);
- if (ret < 0)
- goto out;
- ret = write_temp_extent_buffer(fd, buf, chunk_bytenr);
-
-out:
- free(buf);
- return ret;
-}
-
-static void insert_temp_dev_extent(struct extent_buffer *buf,
- int *slot, u32 *itemoff, u64 start, u64 len)
-{
- struct btrfs_dev_extent *dev_extent;
- struct btrfs_disk_key disk_key;
-
- btrfs_set_header_nritems(buf, *slot + 1);
- (*itemoff) -= sizeof(*dev_extent);
- btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
- btrfs_set_disk_key_objectid(&disk_key, 1);
- btrfs_set_disk_key_offset(&disk_key, start);
- btrfs_set_item_key(buf, &disk_key, *slot);
- btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_extent));
-
- dev_extent = btrfs_item_ptr(buf, *slot, struct btrfs_dev_extent);
- btrfs_set_dev_extent_chunk_objectid(buf, dev_extent,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID);
- btrfs_set_dev_extent_length(buf, dev_extent, len);
- btrfs_set_dev_extent_chunk_offset(buf, dev_extent, start);
- btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
- BTRFS_CHUNK_TREE_OBJECTID);
- (*slot)++;
-}
-
-static int setup_temp_dev_tree(int fd, struct btrfs_mkfs_config *cfg,
- u64 sys_chunk_start, u64 meta_chunk_start,
- u64 dev_bytenr)
-{
- struct extent_buffer *buf = NULL;
- u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
- int slot = 0;
- int ret;
-
- /* Must ensure SYS chunk starts before META chunk */
- if (meta_chunk_start < sys_chunk_start) {
- error("wrong chunk order: meta < system %llu < %llu",
- (unsigned long long)meta_chunk_start,
- (unsigned long long)sys_chunk_start);
- return -EINVAL;
- }
- buf = malloc(sizeof(*buf) + cfg->nodesize);
- if (!buf)
- return -ENOMEM;
- ret = setup_temp_extent_buffer(buf, cfg, dev_bytenr,
- BTRFS_DEV_TREE_OBJECTID);
- if (ret < 0)
- goto out;
- insert_temp_dev_extent(buf, &slot, &itemoff, sys_chunk_start,
- BTRFS_MKFS_SYSTEM_GROUP_SIZE);
- insert_temp_dev_extent(buf, &slot, &itemoff, meta_chunk_start,
- BTRFS_CONVERT_META_GROUP_SIZE);
- ret = write_temp_extent_buffer(fd, buf, dev_bytenr);
-out:
- free(buf);
- return ret;
-}
-
-static int setup_temp_fs_tree(int fd, struct btrfs_mkfs_config *cfg,
- u64 fs_bytenr)
-{
- struct extent_buffer *buf = NULL;
- int ret;
-
- buf = malloc(sizeof(*buf) + cfg->nodesize);
- if (!buf)
- return -ENOMEM;
- ret = setup_temp_extent_buffer(buf, cfg, fs_bytenr,
- BTRFS_FS_TREE_OBJECTID);
- if (ret < 0)
- goto out;
- /*
- * Temporary fs tree is completely empty.
- */
- ret = write_temp_extent_buffer(fd, buf, fs_bytenr);
-out:
- free(buf);
- return ret;
-}
-
-static int setup_temp_csum_tree(int fd, struct btrfs_mkfs_config *cfg,
- u64 csum_bytenr)
-{
- struct extent_buffer *buf = NULL;
- int ret;
-
- buf = malloc(sizeof(*buf) + cfg->nodesize);
- if (!buf)
- return -ENOMEM;
- ret = setup_temp_extent_buffer(buf, cfg, csum_bytenr,
- BTRFS_CSUM_TREE_OBJECTID);
- if (ret < 0)
- goto out;
- /*
- * Temporary csum tree is completely empty.
- */
- ret = write_temp_extent_buffer(fd, buf, csum_bytenr);
-out:
- free(buf);
- return ret;
-}
-
-/*
- * Insert one temporary extent item.
- *
- * NOTE: if skinny_metadata is not enabled, this function must be called
- * after all other trees are initialized.
- * Or fs without skinny-metadata will be screwed up.
- */
-static int insert_temp_extent_item(int fd, struct extent_buffer *buf,
- struct btrfs_mkfs_config *cfg,
- int *slot, u32 *itemoff, u64 bytenr,
- u64 ref_root)
-{
- struct extent_buffer *tmp;
- struct btrfs_extent_item *ei;
- struct btrfs_extent_inline_ref *iref;
- struct btrfs_disk_key disk_key;
- struct btrfs_disk_key tree_info_key;
- struct btrfs_tree_block_info *info;
- int itemsize;
- int skinny_metadata = cfg->features &
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
- int ret;
-
- if (skinny_metadata)
- itemsize = sizeof(*ei) + sizeof(*iref);
- else
- itemsize = sizeof(*ei) + sizeof(*iref) +
- sizeof(struct btrfs_tree_block_info);
-
- btrfs_set_header_nritems(buf, *slot + 1);
- *(itemoff) -= itemsize;
-
- if (skinny_metadata) {
- btrfs_set_disk_key_type(&disk_key, BTRFS_METADATA_ITEM_KEY);
- btrfs_set_disk_key_offset(&disk_key, 0);
- } else {
- btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY);
- btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
- }
- btrfs_set_disk_key_objectid(&disk_key, bytenr);
-
- btrfs_set_item_key(buf, &disk_key, *slot);
- btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(*slot), itemsize);
-
- ei = btrfs_item_ptr(buf, *slot, struct btrfs_extent_item);
- btrfs_set_extent_refs(buf, ei, 1);
- btrfs_set_extent_generation(buf, ei, 1);
- btrfs_set_extent_flags(buf, ei, BTRFS_EXTENT_FLAG_TREE_BLOCK);
-
- if (skinny_metadata) {
- iref = (struct btrfs_extent_inline_ref *)(ei + 1);
- } else {
- info = (struct btrfs_tree_block_info *)(ei + 1);
- iref = (struct btrfs_extent_inline_ref *)(info + 1);
- }
- btrfs_set_extent_inline_ref_type(buf, iref,
- BTRFS_TREE_BLOCK_REF_KEY);
- btrfs_set_extent_inline_ref_offset(buf, iref, ref_root);
-
- (*slot)++;
- if (skinny_metadata)
- return 0;
-
- /*
- * Lastly, check the tree block key by read the tree block
- * Since we do 1:1 mapping for convert case, we can directly
- * read the bytenr from disk
- */
- tmp = malloc(sizeof(*tmp) + cfg->nodesize);
- if (!tmp)
- return -ENOMEM;
- ret = setup_temp_extent_buffer(tmp, cfg, bytenr, ref_root);
- if (ret < 0)
- goto out;
- ret = pread(fd, tmp->data, cfg->nodesize, bytenr);
- if (ret < cfg->nodesize) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
- if (btrfs_header_nritems(tmp) == 0) {
- btrfs_set_disk_key_type(&tree_info_key, 0);
- btrfs_set_disk_key_objectid(&tree_info_key, 0);
- btrfs_set_disk_key_offset(&tree_info_key, 0);
- } else {
- btrfs_item_key(tmp, &tree_info_key, 0);
- }
- btrfs_set_tree_block_key(buf, info, &tree_info_key);
-
-out:
- free(tmp);
- return ret;
-}
-
-static void insert_temp_block_group(struct extent_buffer *buf,
- struct btrfs_mkfs_config *cfg,
- int *slot, u32 *itemoff,
- u64 bytenr, u64 len, u64 used, u64 flag)
-{
- struct btrfs_block_group_item bgi;
- struct btrfs_disk_key disk_key;
-
- btrfs_set_header_nritems(buf, *slot + 1);
- (*itemoff) -= sizeof(bgi);
- btrfs_set_disk_key_type(&disk_key, BTRFS_BLOCK_GROUP_ITEM_KEY);
- btrfs_set_disk_key_objectid(&disk_key, bytenr);
- btrfs_set_disk_key_offset(&disk_key, len);
- btrfs_set_item_key(buf, &disk_key, *slot);
- btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(bgi));
-
- btrfs_set_block_group_flags(&bgi, flag);
- btrfs_set_block_group_used(&bgi, used);
- btrfs_set_block_group_chunk_objectid(&bgi,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID);
- write_extent_buffer(buf, &bgi, btrfs_item_ptr_offset(buf, *slot),
- sizeof(bgi));
- (*slot)++;
-}
-
-static int setup_temp_extent_tree(int fd, struct btrfs_mkfs_config *cfg,
- u64 chunk_bytenr, u64 root_bytenr,
- u64 extent_bytenr, u64 dev_bytenr,
- u64 fs_bytenr, u64 csum_bytenr)
-{
- struct extent_buffer *buf = NULL;
- u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
- int slot = 0;
- int ret;
-
- /*
- * We must ensure provided bytenr are in ascending order,
- * or extent tree key order will be broken.
- */
- if (!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr &&
- extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr &&
- fs_bytenr < csum_bytenr)) {
- error("bad tree bytenr order: "
- "chunk < root %llu < %llu, "
- "root < extent %llu < %llu, "
- "extent < dev %llu < %llu, "
- "dev < fs %llu < %llu, "
- "fs < csum %llu < %llu",
- (unsigned long long)chunk_bytenr,
- (unsigned long long)root_bytenr,
- (unsigned long long)root_bytenr,
- (unsigned long long)extent_bytenr,
- (unsigned long long)extent_bytenr,
- (unsigned long long)dev_bytenr,
- (unsigned long long)dev_bytenr,
- (unsigned long long)fs_bytenr,
- (unsigned long long)fs_bytenr,
- (unsigned long long)csum_bytenr);
- return -EINVAL;
- }
- buf = malloc(sizeof(*buf) + cfg->nodesize);
- if (!buf)
- return -ENOMEM;
-
- ret = setup_temp_extent_buffer(buf, cfg, extent_bytenr,
- BTRFS_EXTENT_TREE_OBJECTID);
- if (ret < 0)
- goto out;
-
- ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
- chunk_bytenr, BTRFS_CHUNK_TREE_OBJECTID);
- if (ret < 0)
- goto out;
-
- insert_temp_block_group(buf, cfg, &slot, &itemoff, chunk_bytenr,
- BTRFS_MKFS_SYSTEM_GROUP_SIZE, cfg->nodesize,
- BTRFS_BLOCK_GROUP_SYSTEM);
-
- ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
- root_bytenr, BTRFS_ROOT_TREE_OBJECTID);
- if (ret < 0)
- goto out;
-
- /* 5 tree block used, root, extent, dev, fs and csum*/
- insert_temp_block_group(buf, cfg, &slot, &itemoff, root_bytenr,
- BTRFS_CONVERT_META_GROUP_SIZE, cfg->nodesize * 5,
- BTRFS_BLOCK_GROUP_METADATA);
-
- ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
- extent_bytenr, BTRFS_EXTENT_TREE_OBJECTID);
- if (ret < 0)
- goto out;
- ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
- dev_bytenr, BTRFS_DEV_TREE_OBJECTID);
- if (ret < 0)
- goto out;
- ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
- fs_bytenr, BTRFS_FS_TREE_OBJECTID);
- if (ret < 0)
- goto out;
- ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
- csum_bytenr, BTRFS_CSUM_TREE_OBJECTID);
- if (ret < 0)
- goto out;
-
- ret = write_temp_extent_buffer(fd, buf, extent_bytenr);
-out:
- free(buf);
- return ret;
-}
-
-/*
- * Improved version of make_btrfs().
- *
- * This one will
- * 1) Do chunk allocation to avoid used data
- * And after this function, extent type matches chunk type
- * 2) Better structured code
- * No super long hand written codes to initialized all tree blocks
- * Split into small blocks and reuse codes.
- * TODO: Reuse tree operation facilities by introducing new flags
- */
-int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
- struct btrfs_convert_context *cctx)
-{
- struct cache_tree *free = &cctx->free;
- struct cache_tree *used = &cctx->used;
- u64 sys_chunk_start;
- u64 meta_chunk_start;
- /* chunk tree bytenr, in system chunk */
- u64 chunk_bytenr;
- /* metadata trees bytenr, in metadata chunk */
- u64 root_bytenr;
- u64 extent_bytenr;
- u64 dev_bytenr;
- u64 fs_bytenr;
- u64 csum_bytenr;
- int ret;
-
- /* Shouldn't happen */
- BUG_ON(cache_tree_empty(used));
-
- /*
- * reserve space for temporary superblock first
- * Here we allocate a little larger space, to keep later
- * free space will be STRIPE_LEN aligned
- */
- ret = reserve_free_space(free, BTRFS_STRIPE_LEN,
- &cfg->super_bytenr);
- if (ret < 0)
- goto out;
-
- /*
- * Then reserve system chunk space
- * TODO: Change system group size depending on cctx->total_bytes.
- * If using current 4M, it can only handle less than one TB for
- * worst case and then run out of sys space.
- */
- ret = reserve_free_space(free, BTRFS_MKFS_SYSTEM_GROUP_SIZE,
- &sys_chunk_start);
- if (ret < 0)
- goto out;
- ret = reserve_free_space(free, BTRFS_CONVERT_META_GROUP_SIZE,
- &meta_chunk_start);
- if (ret < 0)
- goto out;
-
- /*
- * Allocated meta/sys chunks will be mapped 1:1 with device offset.
- *
- * Inside the allocated metadata chunk, the layout will be:
- * | offset | contents |
- * -------------------------------------
- * | +0 | tree root |
- * | +nodesize | extent root |
- * | +nodesize * 2 | device root |
- * | +nodesize * 3 | fs tree |
- * | +nodesize * 4 | csum tree |
- * -------------------------------------
- * Inside the allocated system chunk, the layout will be:
- * | offset | contents |
- * -------------------------------------
- * | +0 | chunk root |
- * -------------------------------------
- */
- chunk_bytenr = sys_chunk_start;
- root_bytenr = meta_chunk_start;
- extent_bytenr = meta_chunk_start + cfg->nodesize;
- dev_bytenr = meta_chunk_start + cfg->nodesize * 2;
- fs_bytenr = meta_chunk_start + cfg->nodesize * 3;
- csum_bytenr = meta_chunk_start + cfg->nodesize * 4;
-
- ret = setup_temp_super(fd, cfg, root_bytenr, chunk_bytenr);
- if (ret < 0)
- goto out;
-
- ret = setup_temp_root_tree(fd, cfg, root_bytenr, extent_bytenr,
- dev_bytenr, fs_bytenr, csum_bytenr);
- if (ret < 0)
- goto out;
- ret = setup_temp_chunk_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
- chunk_bytenr);
- if (ret < 0)
- goto out;
- ret = setup_temp_dev_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
- dev_bytenr);
- if (ret < 0)
- goto out;
- ret = setup_temp_fs_tree(fd, cfg, fs_bytenr);
- if (ret < 0)
- goto out;
- ret = setup_temp_csum_tree(fd, cfg, csum_bytenr);
- if (ret < 0)
- goto out;
- /*
- * Setup extent tree last, since it may need to read tree block key
- * for non-skinny metadata case.
- */
- ret = setup_temp_extent_tree(fd, cfg, chunk_bytenr, root_bytenr,
- extent_bytenr, dev_bytenr, fs_bytenr,
- csum_bytenr);
-out:
- return ret;
-}
-
-/*
- * @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID
- *
- * The superblock signature is not valid, denotes a partially created
- * filesystem, needs to be finalized.
- */
-int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
-{
- struct btrfs_super_block super;
- struct extent_buffer *buf;
- struct btrfs_root_item root_item;
- struct btrfs_disk_key disk_key;
- struct btrfs_extent_item *extent_item;
- struct btrfs_inode_item *inode_item;
- struct btrfs_chunk *chunk;
- struct btrfs_dev_item *dev_item;
- struct btrfs_dev_extent *dev_extent;
- u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
- u8 *ptr;
- int i;
- int ret;
- u32 itemoff;
- u32 nritems = 0;
- u64 first_free;
- u64 ref_root;
- u32 array_size;
- u32 item_size;
- int skinny_metadata = !!(cfg->features &
- BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
- u64 num_bytes;
-
- buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
- if (!buf)
- return -ENOMEM;
-
- first_free = BTRFS_SUPER_INFO_OFFSET + cfg->sectorsize * 2 - 1;
- first_free &= ~((u64)cfg->sectorsize - 1);
-
- memset(&super, 0, sizeof(super));
-
- num_bytes = (cfg->num_bytes / cfg->sectorsize) * cfg->sectorsize;
- if (*cfg->fs_uuid) {
- if (uuid_parse(cfg->fs_uuid, super.fsid) != 0) {
- error("cannot not parse UUID: %s", cfg->fs_uuid);
- ret = -EINVAL;
- goto out;
- }
- if (!test_uuid_unique(cfg->fs_uuid)) {
- error("non-unique UUID: %s", cfg->fs_uuid);
- ret = -EBUSY;
- goto out;
- }
- } else {
- uuid_generate(super.fsid);
- uuid_unparse(super.fsid, cfg->fs_uuid);
- }
- uuid_generate(super.dev_item.uuid);
- uuid_generate(chunk_tree_uuid);
-
- btrfs_set_super_bytenr(&super, cfg->blocks[0]);
- btrfs_set_super_num_devices(&super, 1);
- btrfs_set_super_magic(&super, BTRFS_MAGIC_PARTIAL);
- btrfs_set_super_generation(&super, 1);
- btrfs_set_super_root(&super, cfg->blocks[1]);
- btrfs_set_super_chunk_root(&super, cfg->blocks[3]);
- btrfs_set_super_total_bytes(&super, num_bytes);
- btrfs_set_super_bytes_used(&super, 6 * cfg->nodesize);
- btrfs_set_super_sectorsize(&super, cfg->sectorsize);
- btrfs_set_super_leafsize(&super, cfg->nodesize);
- btrfs_set_super_nodesize(&super, cfg->nodesize);
- btrfs_set_super_stripesize(&super, cfg->stripesize);
- btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
- btrfs_set_super_chunk_root_generation(&super, 1);
- btrfs_set_super_cache_generation(&super, -1);
- btrfs_set_super_incompat_flags(&super, cfg->features);
- if (cfg->label)
- __strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
-
- /* create the tree of root objects */
- memset(buf->data, 0, cfg->nodesize);
- buf->len = cfg->nodesize;
- btrfs_set_header_bytenr(buf, cfg->blocks[1]);
- btrfs_set_header_nritems(buf, 4);
- btrfs_set_header_generation(buf, 1);
- btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
- btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID);
- write_extent_buffer(buf, super.fsid, btrfs_header_fsid(),
- BTRFS_FSID_SIZE);
-
- write_extent_buffer(buf, chunk_tree_uuid,
- btrfs_header_chunk_tree_uuid(buf),
- BTRFS_UUID_SIZE);
-
- /* create the items for the root tree */
- memset(&root_item, 0, sizeof(root_item));
- inode_item = &root_item.inode;
- btrfs_set_stack_inode_generation(inode_item, 1);
- btrfs_set_stack_inode_size(inode_item, 3);
- btrfs_set_stack_inode_nlink(inode_item, 1);
- btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
- btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
- btrfs_set_root_refs(&root_item, 1);
- btrfs_set_root_used(&root_item, cfg->nodesize);
- btrfs_set_root_generation(&root_item, 1);
-
- memset(&disk_key, 0, sizeof(disk_key));
- btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY);
- btrfs_set_disk_key_offset(&disk_key, 0);
- nritems = 0;
-
- itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - sizeof(root_item);
- btrfs_set_root_bytenr(&root_item, cfg->blocks[2]);
- btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID);
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems),
- sizeof(root_item));
- write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf,
- nritems), sizeof(root_item));
- nritems++;
-
- itemoff = itemoff - sizeof(root_item);
- btrfs_set_root_bytenr(&root_item, cfg->blocks[4]);
- btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID);
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems),
- sizeof(root_item));
- write_extent_buffer(buf, &root_item,
- btrfs_item_ptr_offset(buf, nritems),
- sizeof(root_item));
- nritems++;
-
- itemoff = itemoff - sizeof(root_item);
- btrfs_set_root_bytenr(&root_item, cfg->blocks[5]);
- btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID);
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems),
- sizeof(root_item));
- write_extent_buffer(buf, &root_item,
- btrfs_item_ptr_offset(buf, nritems),
- sizeof(root_item));
- nritems++;
-
- itemoff = itemoff - sizeof(root_item);
- btrfs_set_root_bytenr(&root_item, cfg->blocks[6]);
- btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID);
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems),
- sizeof(root_item));
- write_extent_buffer(buf, &root_item,
- btrfs_item_ptr_offset(buf, nritems),
- sizeof(root_item));
- nritems++;
-
-
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[1]);
- if (ret != cfg->nodesize) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
-
- /* create the items for the extent tree */
- memset(buf->data + sizeof(struct btrfs_header), 0,
- cfg->nodesize - sizeof(struct btrfs_header));
- nritems = 0;
- itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
- for (i = 1; i < 7; i++) {
- item_size = sizeof(struct btrfs_extent_item);
- if (!skinny_metadata)
- item_size += sizeof(struct btrfs_tree_block_info);
-
- if (cfg->blocks[i] < first_free) {
- error("block[%d] below first free: %llu < %llu",
- i, (unsigned long long)cfg->blocks[i],
- (unsigned long long)first_free);
- ret = -EINVAL;
- goto out;
- }
- if (cfg->blocks[i] < cfg->blocks[i - 1]) {
- error("blocks %d and %d in reverse order: %llu < %llu",
- i, i - 1,
- (unsigned long long)cfg->blocks[i],
- (unsigned long long)cfg->blocks[i - 1]);
- ret = -EINVAL;
- goto out;
- }
-
- /* create extent item */
- itemoff -= item_size;
- btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]);
- if (skinny_metadata) {
- btrfs_set_disk_key_type(&disk_key,
- BTRFS_METADATA_ITEM_KEY);
- btrfs_set_disk_key_offset(&disk_key, 0);
- } else {
- btrfs_set_disk_key_type(&disk_key,
- BTRFS_EXTENT_ITEM_KEY);
- btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
- }
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
- itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems),
- item_size);
- extent_item = btrfs_item_ptr(buf, nritems,
- struct btrfs_extent_item);
- btrfs_set_extent_refs(buf, extent_item, 1);
- btrfs_set_extent_generation(buf, extent_item, 1);
- btrfs_set_extent_flags(buf, extent_item,
- BTRFS_EXTENT_FLAG_TREE_BLOCK);
- nritems++;
-
- /* create extent ref */
- ref_root = reference_root_table[i];
- btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]);
- btrfs_set_disk_key_offset(&disk_key, ref_root);
- btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY);
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
- itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0);
- nritems++;
- }
- btrfs_set_header_bytenr(buf, cfg->blocks[2]);
- btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID);
- btrfs_set_header_nritems(buf, nritems);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[2]);
- if (ret != cfg->nodesize) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
-
- /* create the chunk tree */
- memset(buf->data + sizeof(struct btrfs_header), 0,
- cfg->nodesize - sizeof(struct btrfs_header));
- nritems = 0;
- item_size = sizeof(*dev_item);
- itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - item_size;
-
- /* first device 1 (there is no device 0) */
- btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
- btrfs_set_disk_key_offset(&disk_key, 1);
- btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
-
- dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item);
- btrfs_set_device_id(buf, dev_item, 1);
- btrfs_set_device_generation(buf, dev_item, 0);
- btrfs_set_device_total_bytes(buf, dev_item, num_bytes);
- btrfs_set_device_bytes_used(buf, dev_item,
- BTRFS_MKFS_SYSTEM_GROUP_SIZE);
- btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize);
- btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize);
- btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize);
- btrfs_set_device_type(buf, dev_item, 0);
-
- write_extent_buffer(buf, super.dev_item.uuid,
- (unsigned long)btrfs_device_uuid(dev_item),
- BTRFS_UUID_SIZE);
- write_extent_buffer(buf, super.fsid,
- (unsigned long)btrfs_device_fsid(dev_item),
- BTRFS_UUID_SIZE);
- read_extent_buffer(buf, &super.dev_item, (unsigned long)dev_item,
- sizeof(*dev_item));
-
- nritems++;
- item_size = btrfs_chunk_item_size(1);
- itemoff = itemoff - item_size;
-
- /* then we have chunk 0 */
- btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
- btrfs_set_disk_key_offset(&disk_key, 0);
- btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
-
- chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk);
- btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
- btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID);
- btrfs_set_chunk_stripe_len(buf, chunk, 64 * 1024);
- btrfs_set_chunk_type(buf, chunk, BTRFS_BLOCK_GROUP_SYSTEM);
- btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize);
- btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize);
- btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize);
- btrfs_set_chunk_num_stripes(buf, chunk, 1);
- btrfs_set_stripe_devid_nr(buf, chunk, 0, 1);
- btrfs_set_stripe_offset_nr(buf, chunk, 0, 0);
- nritems++;
-
- write_extent_buffer(buf, super.dev_item.uuid,
- (unsigned long)btrfs_stripe_dev_uuid(&chunk->stripe),
- BTRFS_UUID_SIZE);
-
- /* copy the key for the chunk to the system array */
- ptr = super.sys_chunk_array;
- array_size = sizeof(disk_key);
-
- memcpy(ptr, &disk_key, sizeof(disk_key));
- ptr += sizeof(disk_key);
-
- /* copy the chunk to the system array */
- read_extent_buffer(buf, ptr, (unsigned long)chunk, item_size);
- array_size += item_size;
- ptr += item_size;
- btrfs_set_super_sys_array_size(&super, array_size);
-
- btrfs_set_header_bytenr(buf, cfg->blocks[3]);
- btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID);
- btrfs_set_header_nritems(buf, nritems);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[3]);
- if (ret != cfg->nodesize) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
-
- /* create the device tree */
- memset(buf->data + sizeof(struct btrfs_header), 0,
- cfg->nodesize - sizeof(struct btrfs_header));
- nritems = 0;
- itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) -
- sizeof(struct btrfs_dev_extent);
-
- btrfs_set_disk_key_objectid(&disk_key, 1);
- btrfs_set_disk_key_offset(&disk_key, 0);
- btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
- btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(nritems),
- sizeof(struct btrfs_dev_extent));
- dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent);
- btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
- BTRFS_CHUNK_TREE_OBJECTID);
- btrfs_set_dev_extent_chunk_objectid(buf, dev_extent,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID);
- btrfs_set_dev_extent_chunk_offset(buf, dev_extent, 0);
-
- write_extent_buffer(buf, chunk_tree_uuid,
- (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent),
- BTRFS_UUID_SIZE);
-
- btrfs_set_dev_extent_length(buf, dev_extent,
- BTRFS_MKFS_SYSTEM_GROUP_SIZE);
- nritems++;
-
- btrfs_set_header_bytenr(buf, cfg->blocks[4]);
- btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID);
- btrfs_set_header_nritems(buf, nritems);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[4]);
- if (ret != cfg->nodesize) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
-
- /* create the FS root */
- memset(buf->data + sizeof(struct btrfs_header), 0,
- cfg->nodesize - sizeof(struct btrfs_header));
- btrfs_set_header_bytenr(buf, cfg->blocks[5]);
- btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID);
- btrfs_set_header_nritems(buf, 0);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[5]);
- if (ret != cfg->nodesize) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
- /* finally create the csum root */
- memset(buf->data + sizeof(struct btrfs_header), 0,
- cfg->nodesize - sizeof(struct btrfs_header));
- btrfs_set_header_bytenr(buf, cfg->blocks[6]);
- btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID);
- btrfs_set_header_nritems(buf, 0);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[6]);
- if (ret != cfg->nodesize) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
-
- /* and write out the super block */
- memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
- memcpy(buf->data, &super, sizeof(super));
- buf->len = BTRFS_SUPER_INFO_SIZE;
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, cfg->blocks[0]);
- if (ret != BTRFS_SUPER_INFO_SIZE) {
- ret = (ret < 0 ? -errno : -EIO);
- goto out;
- }
-
- ret = 0;
-
-out:
- free(buf);
- return ret;
-}
-
-#define VERSION_TO_STRING3(a,b,c) #a "." #b "." #c, KERNEL_VERSION(a,b,c)
-#define VERSION_TO_STRING2(a,b) #a "." #b, KERNEL_VERSION(a,b,0)
-
-/*
- * Feature stability status and versions: compat <= safe <= default
- */
-static const struct btrfs_fs_feature {
- const char *name;
- u64 flag;
- const char *sysfs_name;
- /*
- * Compatibility with kernel of given version. Filesystem can be
- * mounted.
- */
- const char *compat_str;
- u32 compat_ver;
- /*
- * Considered safe for use, but is not on by default, even if the
- * kernel supports the feature.
- */
- const char *safe_str;
- u32 safe_ver;
- /*
- * Considered safe for use and will be turned on by default if
- * supported by the running kernel.
- */
- const char *default_str;
- u32 default_ver;
- const char *desc;
-} mkfs_features[] = {
- { "mixed-bg", BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS,
- "mixed_groups",
- VERSION_TO_STRING3(2,6,37),
- VERSION_TO_STRING3(2,6,37),
- NULL, 0,
- "mixed data and metadata block groups" },
- { "extref", BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF,
- "extended_iref",
- VERSION_TO_STRING2(3,7),
- VERSION_TO_STRING2(3,12),
- VERSION_TO_STRING2(3,12),
- "increased hardlink limit per file to 65536" },
- { "raid56", BTRFS_FEATURE_INCOMPAT_RAID56,
- "raid56",
- VERSION_TO_STRING2(3,9),
- NULL, 0,
- NULL, 0,
- "raid56 extended format" },
- { "skinny-metadata", BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA,
- "skinny_metadata",
- VERSION_TO_STRING2(3,10),
- VERSION_TO_STRING2(3,18),
- VERSION_TO_STRING2(3,18),
- "reduced-size metadata extent refs" },
- { "no-holes", BTRFS_FEATURE_INCOMPAT_NO_HOLES,
- "no_holes",
- VERSION_TO_STRING2(3,14),
- VERSION_TO_STRING2(4,0),
- NULL, 0,
- "no explicit hole extents for files" },
- /* Keep this one last */
- { "list-all", BTRFS_FEATURE_LIST_ALL, NULL }
-};
-
-static int parse_one_fs_feature(const char *name, u64 *flags)
-{
- int i;
- int found = 0;
-
- for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
- if (name[0] == '^' &&
- !strcmp(mkfs_features[i].name, name + 1)) {
- *flags &= ~ mkfs_features[i].flag;
- found = 1;
- } else if (!strcmp(mkfs_features[i].name, name)) {
- *flags |= mkfs_features[i].flag;
- found = 1;
- }
- }
-
- return !found;
-}
-
-void btrfs_parse_features_to_string(char *buf, u64 flags)
-{
- int i;
-
- buf[0] = 0;
-
- for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
- if (flags & mkfs_features[i].flag) {
- if (*buf)
- strcat(buf, ", ");
- strcat(buf, mkfs_features[i].name);
- }
- }
-}
-
-void btrfs_process_fs_features(u64 flags)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
- if (flags & mkfs_features[i].flag) {
- printf("Turning ON incompat feature '%s': %s\n",
- mkfs_features[i].name,
- mkfs_features[i].desc);
- }
- }
-}
-
-void btrfs_list_all_fs_features(u64 mask_disallowed)
-{
- int i;
-
- fprintf(stderr, "Filesystem features available:\n");
- for (i = 0; i < ARRAY_SIZE(mkfs_features) - 1; i++) {
- const struct btrfs_fs_feature *feat = &mkfs_features[i];
-
- if (feat->flag & mask_disallowed)
- continue;
- fprintf(stderr, "%-20s- %s (0x%llx", feat->name, feat->desc,
- feat->flag);
- if (feat->compat_ver)
- fprintf(stderr, ", compat=%s", feat->compat_str);
- if (feat->safe_ver)
- fprintf(stderr, ", safe=%s", feat->safe_str);
- if (feat->default_ver)
- fprintf(stderr, ", default=%s", feat->default_str);
- fprintf(stderr, ")\n");
- }
-}
-
-/*
- * Return NULL if all features were parsed fine, otherwise return the name of
- * the first unparsed.
- */
-char* btrfs_parse_fs_features(char *namelist, u64 *flags)
-{
- char *this_char;
- char *save_ptr = NULL; /* Satisfy static checkers */
-
- for (this_char = strtok_r(namelist, ",", &save_ptr);
- this_char != NULL;
- this_char = strtok_r(NULL, ",", &save_ptr)) {
- if (parse_one_fs_feature(this_char, flags))
- return this_char;
- }
-
- return NULL;
-}
-
-void print_kernel_version(FILE *stream, u32 version)
-{
- u32 v[3];
-
- v[0] = version & 0xFF;
- v[1] = (version >> 8) & 0xFF;
- v[2] = version >> 16;
- fprintf(stream, "%u.%u", v[2], v[1]);
- if (v[0])
- fprintf(stream, ".%u", v[0]);
-}
-
-u32 get_running_kernel_version(void)
-{
- struct utsname utsbuf;
- char *tmp;
- char *saveptr = NULL;
- u32 version;
-
- uname(&utsbuf);
- if (strcmp(utsbuf.sysname, "Linux") != 0) {
- error("unsupported system: %s", utsbuf.sysname);
- exit(1);
- }
- /* 1.2.3-4-name */
- tmp = strchr(utsbuf.release, '-');
- if (tmp)
- *tmp = 0;
-
- tmp = strtok_r(utsbuf.release, ".", &saveptr);
- if (!string_is_numerical(tmp))
- return (u32)-1;
- version = atoi(tmp) << 16;
- tmp = strtok_r(NULL, ".", &saveptr);
- if (!string_is_numerical(tmp))
- return (u32)-1;
- version |= atoi(tmp) << 8;
- tmp = strtok_r(NULL, ".", &saveptr);
- if (tmp) {
- if (!string_is_numerical(tmp))
- return (u32)-1;
- version |= atoi(tmp);
- }
-
- return version;
-}
-
u64 btrfs_device_size(int fd, struct stat *st)
{
u64 size;
@@ -1682,7 +154,7 @@ static int zero_blocks(int fd, off_t start, size_t len)
return ret;
}
-#define ZERO_DEV_BYTES (2 * 1024 * 1024)
+#define ZERO_DEV_BYTES SZ_2M
/* don't write outside the device by clamping the region to the device size */
static int zero_dev_clamped(int fd, off_t start, ssize_t len, u64 dev_size)
@@ -1707,7 +179,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
u32 sectorsize)
{
struct btrfs_super_block *disk_super;
- struct btrfs_super_block *super = root->fs_info->super_copy;
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_super_block *super = fs_info->super_copy;
struct btrfs_device *device;
struct btrfs_dev_item *dev_item;
char *buf = NULL;
@@ -1742,7 +215,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
device->total_bytes = device_total_bytes;
device->bytes_used = 0;
device->total_ios = 0;
- device->dev_root = root->fs_info->dev_root;
+ device->dev_root = fs_info->dev_root;
device->name = strdup(path);
if (!device->name) {
ret = -ENOMEM;
@@ -1750,7 +223,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
}
INIT_LIST_HEAD(&device->dev_list);
- ret = btrfs_add_device(trans, root, device);
+ ret = btrfs_add_device(trans, fs_info, device);
if (ret)
goto out;
@@ -1776,8 +249,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
BUG_ON(ret != sectorsize);
free(buf);
- list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
- device->fs_devices = root->fs_info->fs_devices;
+ list_add(&device->dev_list, &fs_info->fs_devices->devices);
+ device->fs_devices = fs_info->fs_devices;
return 0;
out:
@@ -1906,7 +379,7 @@ int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
btrfs_set_stack_inode_generation(&inode_item, trans->transid);
btrfs_set_stack_inode_size(&inode_item, 0);
btrfs_set_stack_inode_nlink(&inode_item, 1);
- btrfs_set_stack_inode_nbytes(&inode_item, root->nodesize);
+ btrfs_set_stack_inode_nbytes(&inode_item, root->fs_info->nodesize);
btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0755);
btrfs_set_stack_timespec_sec(&inode_item.atime, now);
btrfs_set_stack_timespec_nsec(&inode_item.atime, 0);
@@ -3274,159 +1747,6 @@ out:
return ret;
}
-#define isoctal(c) (((c) & ~7) == '0')
-
-static inline void translate(char *f, char *t)
-{
- while (*f != '\0') {
- if (*f == '\\' &&
- isoctal(f[1]) && isoctal(f[2]) && isoctal(f[3])) {
- *t++ = 64*(f[1] & 7) + 8*(f[2] & 7) + (f[3] & 7);
- f += 4;
- } else
- *t++ = *f++;
- }
- *t = '\0';
- return;
-}
-
-/*
- * Checks if the swap device.
- * Returns 1 if swap device, < 0 on error or 0 if not swap device.
- */
-static int is_swap_device(const char *file)
-{
- FILE *f;
- struct stat st_buf;
- dev_t dev;
- ino_t ino = 0;
- char tmp[PATH_MAX];
- char buf[PATH_MAX];
- char *cp;
- int ret = 0;
-
- if (stat(file, &st_buf) < 0)
- return -errno;
- if (S_ISBLK(st_buf.st_mode))
- dev = st_buf.st_rdev;
- else if (S_ISREG(st_buf.st_mode)) {
- dev = st_buf.st_dev;
- ino = st_buf.st_ino;
- } else
- return 0;
-
- if ((f = fopen("/proc/swaps", "r")) == NULL)
- return 0;
-
- /* skip the first line */
- if (fgets(tmp, sizeof(tmp), f) == NULL)
- goto out;
-
- while (fgets(tmp, sizeof(tmp), f) != NULL) {
- if ((cp = strchr(tmp, ' ')) != NULL)
- *cp = '\0';
- if ((cp = strchr(tmp, '\t')) != NULL)
- *cp = '\0';
- translate(tmp, buf);
- if (stat(buf, &st_buf) != 0)
- continue;
- if (S_ISBLK(st_buf.st_mode)) {
- if (dev == st_buf.st_rdev) {
- ret = 1;
- break;
- }
- } else if (S_ISREG(st_buf.st_mode)) {
- if (dev == st_buf.st_dev && ino == st_buf.st_ino) {
- ret = 1;
- break;
- }
- }
- }
-
-out:
- fclose(f);
-
- return ret;
-}
-
-/*
- * Check for existing filesystem or partition table on device.
- * Returns:
- * 1 for existing fs or partition
- * 0 for nothing found
- * -1 for internal error
- */
-static int check_overwrite(const char *device)
-{
- const char *type;
- blkid_probe pr = NULL;
- int ret;
- blkid_loff_t size;
-
- if (!device || !*device)
- return 0;
-
- ret = -1; /* will reset on success of all setup calls */
-
- pr = blkid_new_probe_from_filename(device);
- if (!pr)
- goto out;
-
- size = blkid_probe_get_size(pr);
- if (size < 0)
- goto out;
-
- /* nothing to overwrite on a 0-length device */
- if (size == 0) {
- ret = 0;
- goto out;
- }
-
- ret = blkid_probe_enable_partitions(pr, 1);
- if (ret < 0)
- goto out;
-
- ret = blkid_do_fullprobe(pr);
- if (ret < 0)
- goto out;
-
- /*
- * Blkid returns 1 for nothing found and 0 when it finds a signature,
- * but we want the exact opposite, so reverse the return value here.
- *
- * In addition print some useful diagnostics about what actually is
- * on the device.
- */
- if (ret) {
- ret = 0;
- goto out;
- }
-
- if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
- fprintf(stderr,
- "%s appears to contain an existing "
- "filesystem (%s).\n", device, type);
- } else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
- fprintf(stderr,
- "%s appears to contain a partition "
- "table (%s).\n", device, type);
- } else {
- fprintf(stderr,
- "%s appears to contain something weird "
- "according to blkid\n", device);
- }
- ret = 1;
-
-out:
- if (pr)
- blkid_free_probe(pr);
- if (ret == -1)
- fprintf(stderr,
- "probe of %s failed, cannot detect "
- "existing filesystem.\n", device);
- return ret;
-}
-
static int group_profile_devs_min(u64 flag)
{
switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
@@ -3515,63 +1835,6 @@ int group_profile_max_safe_loss(u64 flags)
}
}
-/*
- * Check if a device is suitable for btrfs
- * returns:
- * 1: something is wrong, an error is printed
- * 0: all is fine
- */
-int test_dev_for_mkfs(const char *file, int force_overwrite)
-{
- int ret, fd;
- struct stat st;
-
- ret = is_swap_device(file);
- if (ret < 0) {
- error("checking status of %s: %s", file, strerror(-ret));
- return 1;
- }
- if (ret == 1) {
- error("%s is a swap device", file);
- return 1;
- }
- if (!force_overwrite) {
- if (check_overwrite(file)) {
- error("use the -f option to force overwrite of %s",
- file);
- return 1;
- }
- }
- ret = check_mounted(file);
- if (ret < 0) {
- error("cannot check mount status of %s: %s", file,
- strerror(-ret));
- return 1;
- }
- if (ret == 1) {
- error("%s is mounted", file);
- return 1;
- }
- /* check if the device is busy */
- fd = open(file, O_RDWR|O_EXCL);
- if (fd < 0) {
- error("unable to open %s: %s", file, strerror(errno));
- return 1;
- }
- if (fstat(fd, &st)) {
- error("unable to stat %s: %s", file, strerror(errno));
- close(fd);
- return 1;
- }
- if (!S_ISBLK(st.st_mode)) {
- error("%s is not a block device", file);
- close(fd);
- return 1;
- }
- close(fd);
- return 0;
-}
-
int btrfs_scan_devices(void)
{
int fd = -1;
@@ -3624,35 +1887,6 @@ int btrfs_scan_devices(void)
return 0;
}
-int is_vol_small(const char *file)
-{
- int fd = -1;
- int e;
- struct stat st;
- u64 size;
-
- fd = open(file, O_RDONLY);
- if (fd < 0)
- return -errno;
- if (fstat(fd, &st) < 0) {
- e = -errno;
- close(fd);
- return e;
- }
- size = btrfs_device_size(fd, &st);
- if (size == 0) {
- close(fd);
- return -1;
- }
- if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) {
- close(fd);
- return 1;
- } else {
- close(fd);
- return 0;
- }
-}
-
/*
* This reads a line from the stdin and only returns non-zero if the
* first whitespace delimited token is a case insensitive match with yes
@@ -3726,27 +1960,6 @@ int find_mount_root(const char *path, char **mount_root)
return ret;
}
-int test_minimum_size(const char *file, u32 nodesize)
-{
- int fd;
- struct stat statbuf;
-
- fd = open(file, O_RDONLY);
- if (fd < 0)
- return -errno;
- if (stat(file, &statbuf) < 0) {
- close(fd);
- return -errno;
- }
- if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(nodesize)) {
- close(fd);
- return 1;
- }
- close(fd);
- return 0;
-}
-
-
/*
* Test if path is a directory
* Returns:
@@ -4054,6 +2267,32 @@ unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode)
return unit_mode;
}
+u64 div_factor(u64 num, int factor)
+{
+ if (factor == 10)
+ return num;
+ num *= factor;
+ num /= 10;
+ return num;
+}
+/*
+ * Get the length of the string converted from a u64 number.
+ *
+ * Result is equal to log10(num) + 1, but without the use of math library.
+ */
+int count_digits(u64 num)
+{
+ int ret = 0;
+
+ if (num == 0)
+ return 1;
+ while (num > 0) {
+ ret++;
+ num /= 10;
+ }
+ return ret;
+}
+
int string_is_numerical(const char *str)
{
if (!str)
@@ -4067,48 +2306,13 @@ int string_is_numerical(const char *str)
return 1;
}
-/*
- * Preprocess @argv with getopt_long to reorder options and consume the "--"
- * option separator.
- * Unknown short and long options are reported, optionally the @usage is printed
- * before exit.
- */
-void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
-{
- static const struct option long_options[] = {
- {NULL, 0, NULL, 0}
- };
-
- while (1) {
- int c = getopt_long(argc, argv, "", long_options, NULL);
-
- if (c < 0)
- break;
-
- switch (c) {
- default:
- if (usagestr)
- usage(usagestr);
- }
- }
-}
-
-/*
- * Same as clean_args_no_options but pass through arguments that could look
- * like short options. Eg. reisze which takes a negative resize argument like
- * '-123M' .
- *
- * This accepts only two forms:
- * - "-- option1 option2 ..."
- * - "option1 option2 ..."
- */
-void clean_args_no_options_relaxed(int argc, char *argv[], const char * const *usagestr)
+int prefixcmp(const char *str, const char *prefix)
{
- if (argc <= 1)
- return;
-
- if (strcmp(argv[1], "--") == 0)
- optind = 2;
+ for (; ; str++, prefix++)
+ if (!*prefix)
+ return 0;
+ else if (*str != *prefix)
+ return (unsigned char)*prefix - (unsigned char)*str;
}
/* Subvolume helper functions */
@@ -4229,6 +2433,59 @@ out:
return ret;
}
+int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri, u64 r_id)
+{
+ int fd;
+ int ret;
+ DIR *dirstream = NULL;
+
+ fd = btrfs_open_dir(mnt, &dirstream, 1);
+ if (fd < 0)
+ return -EINVAL;
+
+ memset(get_ri, 0, sizeof(*get_ri));
+ get_ri->root_id = r_id;
+
+ if (r_id == BTRFS_FS_TREE_OBJECTID)
+ ret = btrfs_get_toplevel_subvol(fd, get_ri);
+ else
+ ret = btrfs_get_subvol(fd, get_ri);
+
+ if (ret)
+ error("can't find rootid '%llu' on '%s': %d", r_id, mnt, ret);
+
+ close_file_or_dir(fd, dirstream);
+
+ return ret;
+}
+
+int get_subvol_info_by_uuid(const char *mnt, struct root_info *get_ri, u8 *uuid_arg)
+{
+ int fd;
+ int ret;
+ DIR *dirstream = NULL;
+
+ fd = btrfs_open_dir(mnt, &dirstream, 1);
+ if (fd < 0)
+ return -EINVAL;
+
+ memset(get_ri, 0, sizeof(*get_ri));
+ uuid_copy(get_ri->uuid, uuid_arg);
+
+ ret = btrfs_get_subvol(fd, get_ri);
+ if (ret) {
+ char uuid_parsed[BTRFS_UUID_UNPARSED_SIZE];
+ uuid_unparse(uuid_arg, uuid_parsed);
+ error("can't find uuid '%s' on '%s': %d",
+ uuid_parsed, mnt, ret);
+ }
+
+ close_file_or_dir(fd, dirstream);
+
+ return ret;
+}
+
+/* Set the seed manually */
void init_rand_seed(u64 seed)
{
int i;
@@ -4278,6 +2535,7 @@ u32 rand_u32(void)
return (u32)jrand48(rand_seed);
}
+/* Return random number in range [0, upper) */
unsigned int rand_range(unsigned int upper)
{
__init_seed();
@@ -4287,3 +2545,32 @@ unsigned int rand_range(unsigned int upper)
*/
return (unsigned int)(jrand48(rand_seed) % upper);
}
+
+int rand_int(void)
+{
+ return (int)(rand_u32());
+}
+
+u64 rand_u64(void)
+{
+ u64 ret = 0;
+
+ ret += rand_u32();
+ ret <<= 32;
+ ret += rand_u32();
+ return ret;
+}
+
+u16 rand_u16(void)
+{
+ return (u16)(rand_u32());
+}
+
+u8 rand_u8(void)
+{
+ return (u8)(rand_u32());
+}
+
+void btrfs_config_init(void)
+{
+}
diff --git a/utils.h b/utils.h
index 3d30bd12..091f8fab 100644
--- a/utils.h
+++ b/utils.h
@@ -23,32 +23,11 @@
#include "ctree.h"
#include <dirent.h>
#include <stdarg.h>
+#include "common-defs.h"
#include "internal.h"
#include "btrfs-list.h"
-
-#define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024)
-#define BTRFS_MKFS_SMALL_VOLUME_SIZE (1024 * 1024 * 1024)
-#define BTRFS_MKFS_DEFAULT_NODE_SIZE 16384
-#define BTRFS_MKFS_DEFAULT_FEATURES \
- (BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF \
- | BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)
-
-/*
- * Avoid multi-device features (RAID56) and mixed block groups
- */
-#define BTRFS_CONVERT_ALLOWED_FEATURES \
- (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF \
- | BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL \
- | BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO \
- | BTRFS_FEATURE_INCOMPAT_COMPRESS_LZOv2 \
- | BTRFS_FEATURE_INCOMPAT_BIG_METADATA \
- | BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF \
- | BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA \
- | BTRFS_FEATURE_INCOMPAT_NO_HOLES)
-
-#define BTRFS_CONVERT_META_GROUP_SIZE (32 * 1024 * 1024)
-
-#define BTRFS_FEATURE_LIST_ALL (1ULL << 63)
+#include "sizes.h"
+#include "messages.h"
#define BTRFS_SCAN_MOUNTED (1ULL << 0)
#define BTRFS_SCAN_LBLKID (1ULL << 1)
@@ -61,28 +40,6 @@
#define BTRFS_ARG_BLKDEV 3
#define BTRFS_ARG_REG 4
-#define BTRFS_UUID_UNPARSED_SIZE 37
-
-#define ARGV0_BUF_SIZE PATH_MAX
-
-#define GETOPT_VAL_SI 256
-#define GETOPT_VAL_IEC 257
-#define GETOPT_VAL_RAW 258
-#define GETOPT_VAL_HUMAN_READABLE 259
-#define GETOPT_VAL_KBYTES 260
-#define GETOPT_VAL_MBYTES 261
-#define GETOPT_VAL_GBYTES 262
-#define GETOPT_VAL_TBYTES 263
-
-#define GETOPT_VAL_HELP 270
-
-int check_argc_exact(int nargs, int expected);
-int check_argc_min(int nargs, int expected);
-int check_argc_max(int nargs, int expected);
-
-void fixup_argv0(char **argv, const char *token);
-void set_argv0(char **argv);
-
/*
* Output modes of size
*/
@@ -107,57 +64,10 @@ void set_argv0(char **argv);
void units_set_mode(unsigned *units, unsigned mode);
void units_set_base(unsigned *units, unsigned base);
-void btrfs_list_all_fs_features(u64 mask_disallowed);
-char* btrfs_parse_fs_features(char *namelist, u64 *flags);
-void btrfs_process_fs_features(u64 flags);
-void btrfs_parse_features_to_string(char *buf, u64 flags);
-void print_kernel_version(FILE *stream, u32 version);
-u32 get_running_kernel_version(void);
-
-struct btrfs_mkfs_config {
- char *label;
- char fs_uuid[BTRFS_UUID_UNPARSED_SIZE];
- char chunk_uuid[BTRFS_UUID_UNPARSED_SIZE];
- u64 blocks[8];
- u64 num_bytes;
- u32 nodesize;
- u32 sectorsize;
- u32 stripesize;
- u64 features;
-
- /* Super bytenr after make_btrfs */
- u64 super_bytenr;
-};
-
-struct btrfs_convert_context {
- u32 blocksize;
- u32 first_data_block;
- u32 block_count;
- u32 inodes_count;
- u32 free_inodes_count;
- u64 total_bytes;
- char *volume_name;
- const struct btrfs_convert_operations *convert_ops;
-
- /* The accurate used space of old filesystem */
- struct cache_tree used;
-
- /* Batched ranges which must be covered by data chunks */
- struct cache_tree data_chunks;
-
- /* Free space which is not covered by data_chunks */
- struct cache_tree free;
-
- void *fs_data;
-};
-
#define PREP_DEVICE_ZERO_END (1U << 0)
#define PREP_DEVICE_DISCARD (1U << 1)
#define PREP_DEVICE_VERBOSE (1U << 2)
-int make_btrfs(int fd, struct btrfs_mkfs_config *cfg);
-int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
- struct btrfs_convert_context *cctx);
int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid);
int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret,
@@ -202,15 +112,11 @@ int btrfs_open_dir(const char *path, DIR **dirstream, int verbose);
u64 btrfs_device_size(int fd, struct stat *st);
/* Helper to always get proper size of the destination string */
#define strncpy_null(dest, src) __strncpy_null(dest, src, sizeof(dest))
-int test_dev_for_mkfs(const char *file, int force_overwrite);
int get_label_mounted(const char *mount_path, char *labelp);
int get_label_unmounted(const char *dev, char *label);
-int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
- u64 dev_cnt, int mixed, int ssd);
int group_profile_max_safe_loss(u64 flags);
-int is_vol_small(const char *file);
-int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
- int verify);
+int csum_tree_block(struct btrfs_fs_info *root, struct extent_buffer *buf,
+ int verify);
int ask_user(const char *question);
int lookup_path_rootid(int fd, u64 *rootid);
int btrfs_scan_devices(void);
@@ -222,242 +128,46 @@ int test_uuid_unique(char *fs_uuid);
u64 disk_size(const char *path);
u64 get_partition_size(const char *dev);
-int test_minimum_size(const char *file, u32 nodesize);
int test_issubvolname(const char *name);
int test_issubvolume(const char *path);
int test_isdir(const char *path);
const char *subvol_strip_mountpoint(const char *mnt, const char *full_path);
int get_subvol_info(const char *fullpath, struct root_info *get_ri);
-
-/*
- * Btrfs minimum size calculation is complicated, it should include at least:
- * 1. system group size
- * 2. minimum global block reserve
- * 3. metadata used at mkfs
- * 4. space reservation to create uuid for first mount.
- * Also, raid factor should also be taken into consideration.
- * To avoid the overkill calculation, (system group + global block rsv) * 2
- * for *EACH* device should be good enough.
- */
-static inline u64 btrfs_min_global_blk_rsv_size(u32 nodesize)
-{
- return (u64)nodesize << 10;
-}
-
-static inline u64 btrfs_min_dev_size(u32 nodesize)
-{
- return 2 * (BTRFS_MKFS_SYSTEM_GROUP_SIZE +
- btrfs_min_global_blk_rsv_size(nodesize));
-}
-
+int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri,
+ u64 rootid_arg);
+int get_subvol_info_by_uuid(const char *mnt, struct root_info *get_ri,
+ u8 *uuid_arg);
int find_next_key(struct btrfs_path *path, struct btrfs_key *key);
const char* btrfs_group_type_str(u64 flag);
const char* btrfs_group_profile_str(u64 flag);
-/*
- * Get the length of the string converted from a u64 number.
- *
- * Result is equal to log10(num) + 1, but without the use of math library.
- */
-static inline int count_digits(u64 num)
-{
- int ret = 0;
-
- if (num == 0)
- return 1;
- while (num > 0) {
- ret++;
- num /= 10;
- }
- return ret;
-}
-
-static inline u64 div_factor(u64 num, int factor)
-{
- if (factor == 10)
- return num;
- num *= factor;
- num /= 10;
- return num;
-}
+int count_digits(u64 num);
+u64 div_factor(u64 num, int factor);
int btrfs_tree_search2_ioctl_supported(int fd);
-int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features);
-
-const char *get_argv0_buf(void);
-
-#define HELPINFO_UNITS_LONG \
- "--raw raw numbers in bytes", \
- "--human-readable human friendly numbers, base 1024 (default)", \
- "--iec use 1024 as a base (KiB, MiB, GiB, TiB)", \
- "--si use 1000 as a base (kB, MB, GB, TB)", \
- "--kbytes show sizes in KiB, or kB with --si", \
- "--mbytes show sizes in MiB, or MB with --si", \
- "--gbytes show sizes in GiB, or GB with --si", \
- "--tbytes show sizes in TiB, or TB with --si"
-
-#define HELPINFO_UNITS_SHORT_LONG \
- "-b|--raw raw numbers in bytes", \
- "-h|--human-readable", \
- " human friendly numbers, base 1024 (default)", \
- "-H human friendly numbers, base 1000", \
- "--iec use 1024 as a base (KiB, MiB, GiB, TiB)", \
- "--si use 1000 as a base (kB, MB, GB, TB)", \
- "-k|--kbytes show sizes in KiB, or kB with --si", \
- "-m|--mbytes show sizes in MiB, or MB with --si", \
- "-g|--gbytes show sizes in GiB, or GB with --si", \
- "-t|--tbytes show sizes in TiB, or TB with --si"
unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode);
-void clean_args_no_options(int argc, char *argv[], const char * const *usage);
-void clean_args_no_options_relaxed(int argc, char *argv[],
- const char * const *usagestr);
int string_is_numerical(const char *str);
+int prefixcmp(const char *str, const char *prefix);
-#if DEBUG_VERBOSE_ERROR
-#define PRINT_VERBOSE_ERROR fprintf(stderr, "%s:%d:", __FILE__, __LINE__)
-#else
-#define PRINT_VERBOSE_ERROR
-#endif
-
-#if DEBUG_TRACE_ON_ERROR
-#define PRINT_TRACE_ON_ERROR print_trace()
-#else
-#define PRINT_TRACE_ON_ERROR
-#endif
-
-#if DEBUG_ABORT_ON_ERROR
-#define DO_ABORT_ON_ERROR abort()
-#else
-#define DO_ABORT_ON_ERROR
-#endif
-
-#define error(fmt, ...) \
- do { \
- PRINT_TRACE_ON_ERROR; \
- PRINT_VERBOSE_ERROR; \
- __error((fmt), ##__VA_ARGS__); \
- DO_ABORT_ON_ERROR; \
- } while (0)
-
-#define error_on(cond, fmt, ...) \
- do { \
- if ((cond)) \
- PRINT_TRACE_ON_ERROR; \
- if ((cond)) \
- PRINT_VERBOSE_ERROR; \
- __error_on((cond), (fmt), ##__VA_ARGS__); \
- if ((cond)) \
- DO_ABORT_ON_ERROR; \
- } while (0)
-
-#define warning(fmt, ...) \
- do { \
- PRINT_TRACE_ON_ERROR; \
- PRINT_VERBOSE_ERROR; \
- __warning((fmt), ##__VA_ARGS__); \
- } while (0)
-
-#define warning_on(cond, fmt, ...) \
- do { \
- if ((cond)) \
- PRINT_TRACE_ON_ERROR; \
- if ((cond)) \
- PRINT_VERBOSE_ERROR; \
- __warning_on((cond), (fmt), ##__VA_ARGS__); \
- } while (0)
-
-__attribute__ ((format (printf, 1, 2)))
-static inline void __warning(const char *fmt, ...)
-{
- va_list args;
-
- fputs("WARNING: ", stderr);
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- fputc('\n', stderr);
-}
-
-__attribute__ ((format (printf, 1, 2)))
-static inline void __error(const char *fmt, ...)
-{
- va_list args;
-
- fputs("ERROR: ", stderr);
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- fputc('\n', stderr);
-}
-
-__attribute__ ((format (printf, 2, 3)))
-static inline int __warning_on(int condition, const char *fmt, ...)
-{
- va_list args;
-
- if (!condition)
- return 0;
-
- fputs("WARNING: ", stderr);
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- fputc('\n', stderr);
-
- return 1;
-}
-
-__attribute__ ((format (printf, 2, 3)))
-static inline int __error_on(int condition, const char *fmt, ...)
-{
- va_list args;
-
- if (!condition)
- return 0;
-
- fputs("ERROR: ", stderr);
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- fputc('\n', stderr);
+/*
+ * Global program state, configurable by command line and available to
+ * functions without extra context passing.
+ */
+struct btrfs_config {
+};
+extern struct btrfs_config bconf;
- return 1;
-}
+void btrfs_config_init(void);
/* Pseudo random number generator wrappers */
+int rand_int(void);
+u8 rand_u8(void);
+u16 rand_u16(void);
u32 rand_u32(void);
-
-static inline int rand_int(void)
-{
- return (int)(rand_u32());
-}
-
-static inline u64 rand_u64(void)
-{
- u64 ret = 0;
-
- ret += rand_u32();
- ret <<= 32;
- ret += rand_u32();
- return ret;
-}
-
-static inline u16 rand_u16(void)
-{
- return (u16)(rand_u32());
-}
-
-static inline u8 rand_u8(void)
-{
- return (u8)(rand_u32());
-}
-
-/* Return random number in range [0, limit) */
+u64 rand_u64(void);
unsigned int rand_range(unsigned int upper);
-
-/* Also allow setting the seed manually */
void init_rand_seed(u64 seed);
#endif
diff --git a/version.sh b/version.sh
index 4908d0fa..7bb6be80 100755
--- a/version.sh
+++ b/version.sh
@@ -6,7 +6,7 @@
# Copyright 2008, Oracle
# Released under the GNU GPLv2
-v="v4.9.1"
+v="v4.12"
opt=$1
diff --git a/volumes.c b/volumes.c
index a0a85edd..2f3943dc 100644
--- a/volumes.c
+++ b/volumes.c
@@ -28,6 +28,7 @@
#include "print-tree.h"
#include "volumes.h"
#include "utils.h"
+#include "kernel-lib/raid56.h"
struct stripe {
struct btrfs_device *dev;
@@ -160,6 +161,7 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
{
struct btrfs_fs_devices *seed_devices;
struct btrfs_device *device;
+ int ret = 0;
again:
if (!fs_devices)
@@ -168,7 +170,11 @@ again:
device = list_entry(fs_devices->devices.next,
struct btrfs_device, dev_list);
if (device->fd != -1) {
- fsync(device->fd);
+ if (fsync(device->fd) == -1) {
+ warning("fsync on device %llu failed: %s",
+ device->devid, strerror(errno));
+ ret = -errno;
+ }
if (posix_fadvise(device->fd, 0, 0, POSIX_FADV_DONTNEED))
fprintf(stderr, "Warning, could not drop caches\n");
close(device->fd);
@@ -197,7 +203,7 @@ again:
free(fs_devices);
}
- return 0;
+ return ret;
}
void btrfs_close_all_devices(void)
@@ -319,7 +325,7 @@ static int find_free_dev_extent_start(struct btrfs_trans_handle *trans,
* used by the boot loader (grub for example), so we make sure to start
* at an offset of at least 1MB.
*/
- min_search_start = max(root->fs_info->alloc_start, 1024ull * 1024);
+ min_search_start = max(root->fs_info->alloc_start, (u64)SZ_1M);
search_start = max(search_start, min_search_start);
path = btrfs_alloc_path();
@@ -579,7 +585,7 @@ error:
* the btrfs_device struct should be fully filled in
*/
int btrfs_add_device(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+ struct btrfs_fs_info *fs_info,
struct btrfs_device *device)
{
int ret;
@@ -587,11 +593,10 @@ int btrfs_add_device(struct btrfs_trans_handle *trans,
struct btrfs_dev_item *dev_item;
struct extent_buffer *leaf;
struct btrfs_key key;
+ struct btrfs_root *root = fs_info->chunk_root;
unsigned long ptr;
u64 free_devid = 0;
- root = root->fs_info->chunk_root;
-
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
@@ -629,7 +634,7 @@ int btrfs_add_device(struct btrfs_trans_handle *trans,
ptr = (unsigned long)btrfs_device_uuid(dev_item);
write_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
ptr = (unsigned long)btrfs_device_fsid(dev_item);
- write_extent_buffer(leaf, root->fs_info->fsid, ptr, BTRFS_UUID_SIZE);
+ write_extent_buffer(leaf, fs_info->fsid, ptr, BTRFS_UUID_SIZE);
btrfs_mark_buffer_dirty(leaf);
ret = 0;
@@ -684,12 +689,10 @@ out:
return ret;
}
-int btrfs_add_system_chunk(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_key *key,
+int btrfs_add_system_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
struct btrfs_chunk *chunk, int item_size)
{
- struct btrfs_super_block *super_copy = root->fs_info->super_copy;
+ struct btrfs_super_block *super_copy = fs_info->super_copy;
struct btrfs_disk_key disk_key;
u32 array_size;
u8 *ptr;
@@ -830,11 +833,11 @@ error:
/ sizeof(struct btrfs_stripe) + 1)
int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
- struct btrfs_root *extent_root, u64 *start,
+ struct btrfs_fs_info *info, u64 *start,
u64 *num_bytes, u64 type)
{
u64 dev_offset;
- struct btrfs_fs_info *info = extent_root->fs_info;
+ struct btrfs_root *extent_root = info->extent_root;
struct btrfs_root *chunk_root = info->chunk_root;
struct btrfs_stripe *stripes;
struct btrfs_device *device = NULL;
@@ -843,8 +846,8 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
struct list_head *dev_list = &info->fs_devices->devices;
struct list_head *cur;
struct map_lookup *map;
- int min_stripe_size = 1 * 1024 * 1024;
- u64 calc_size = 8 * 1024 * 1024;
+ int min_stripe_size = SZ_1M;
+ u64 calc_size = SZ_8M;
u64 min_free;
u64 max_chunk_size = 4 * calc_size;
u64 avail = 0;
@@ -870,19 +873,19 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
BTRFS_BLOCK_GROUP_RAID10 |
BTRFS_BLOCK_GROUP_DUP)) {
if (type & BTRFS_BLOCK_GROUP_SYSTEM) {
- calc_size = 8 * 1024 * 1024;
+ calc_size = SZ_8M;
max_chunk_size = calc_size * 2;
- min_stripe_size = 1 * 1024 * 1024;
+ min_stripe_size = SZ_1M;
max_stripes = BTRFS_MAX_DEVS_SYS_CHUNK;
} else if (type & BTRFS_BLOCK_GROUP_DATA) {
- calc_size = 1024 * 1024 * 1024;
+ calc_size = SZ_1G;
max_chunk_size = 10 * calc_size;
- min_stripe_size = 64 * 1024 * 1024;
+ min_stripe_size = SZ_64M;
max_stripes = BTRFS_MAX_DEVS(chunk_root);
} else if (type & BTRFS_BLOCK_GROUP_METADATA) {
- calc_size = 1024 * 1024 * 1024;
+ calc_size = SZ_1G;
max_chunk_size = 4 * calc_size;
- min_stripe_size = 32 * 1024 * 1024;
+ min_stripe_size = SZ_32M;
max_stripes = BTRFS_MAX_DEVS(chunk_root);
}
}
@@ -1056,9 +1059,9 @@ again:
btrfs_set_stack_chunk_num_stripes(chunk, num_stripes);
btrfs_set_stack_chunk_io_align(chunk, stripe_len);
btrfs_set_stack_chunk_io_width(chunk, stripe_len);
- btrfs_set_stack_chunk_sector_size(chunk, extent_root->sectorsize);
+ btrfs_set_stack_chunk_sector_size(chunk, info->sectorsize);
btrfs_set_stack_chunk_sub_stripes(chunk, sub_stripes);
- map->sector_size = extent_root->sectorsize;
+ map->sector_size = info->sectorsize;
map->stripe_len = stripe_len;
map->io_align = stripe_len;
map->io_width = stripe_len;
@@ -1078,7 +1081,7 @@ again:
BUG_ON(ret);
if (type & BTRFS_BLOCK_GROUP_SYSTEM) {
- ret = btrfs_add_system_chunk(trans, chunk_root, &key,
+ ret = btrfs_add_system_chunk(info, &key,
chunk, btrfs_chunk_item_size(num_stripes));
BUG_ON(ret);
}
@@ -1096,11 +1099,11 @@ again:
* occupied.
*/
int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
- struct btrfs_root *extent_root, u64 *start,
+ struct btrfs_fs_info *info, u64 *start,
u64 num_bytes, u64 type, int convert)
{
u64 dev_offset;
- struct btrfs_fs_info *info = extent_root->fs_info;
+ struct btrfs_root *extent_root = info->extent_root;
struct btrfs_root *chunk_root = info->chunk_root;
struct btrfs_stripe *stripes;
struct btrfs_device *device = NULL;
@@ -1108,7 +1111,7 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
struct list_head *dev_list = &info->fs_devices->devices;
struct list_head *cur;
struct map_lookup *map;
- u64 calc_size = 8 * 1024 * 1024;
+ u64 calc_size = SZ_8M;
int num_stripes = 1;
int sub_stripes = 0;
int ret;
@@ -1119,7 +1122,7 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
key.type = BTRFS_CHUNK_ITEM_KEY;
if (convert) {
- if (*start != round_down(*start, extent_root->sectorsize)) {
+ if (*start != round_down(*start, info->sectorsize)) {
error("DATA chunk start not sectorsize aligned: %llu",
(unsigned long long)*start);
return -EINVAL;
@@ -1184,9 +1187,9 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
btrfs_set_stack_chunk_num_stripes(chunk, num_stripes);
btrfs_set_stack_chunk_io_align(chunk, stripe_len);
btrfs_set_stack_chunk_io_width(chunk, stripe_len);
- btrfs_set_stack_chunk_sector_size(chunk, extent_root->sectorsize);
+ btrfs_set_stack_chunk_sector_size(chunk, info->sectorsize);
btrfs_set_stack_chunk_sub_stripes(chunk, sub_stripes);
- map->sector_size = extent_root->sectorsize;
+ map->sector_size = info->sectorsize;
map->stripe_len = stripe_len;
map->io_align = stripe_len;
map->io_width = stripe_len;
@@ -1210,8 +1213,9 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
return ret;
}
-int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len)
+int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len)
{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
struct cache_extent *ce;
struct map_lookup *map;
int ret;
@@ -1246,9 +1250,10 @@ int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len)
return ret;
}
-int btrfs_next_bg(struct btrfs_mapping_tree *map_tree, u64 *logical,
- u64 *size, u64 type)
+int btrfs_next_bg(struct btrfs_fs_info *fs_info, u64 *logical,
+ u64 *size, u64 type)
{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
struct cache_extent *ce;
struct map_lookup *map;
u64 cur = *logical;
@@ -1274,15 +1279,18 @@ int btrfs_next_bg(struct btrfs_mapping_tree *map_tree, u64 *logical,
*size = ce->size;
return 0;
}
+ if (!cur)
+ ce = next_cache_extent(ce);
}
return -ENOENT;
}
-int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
+int btrfs_rmap_block(struct btrfs_fs_info *fs_info,
u64 chunk_start, u64 physical, u64 devid,
u64 **logical, int *naddrs, int *stripe_len)
{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
struct cache_extent *ce;
struct map_lookup *map;
u64 *buf;
@@ -1374,20 +1382,21 @@ static void sort_parity_stripes(struct btrfs_multi_bio *bbio, u64 *raid_map)
}
}
-int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
+int btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
u64 logical, u64 *length,
struct btrfs_multi_bio **multi_ret, int mirror_num,
u64 **raid_map_ret)
{
- return __btrfs_map_block(map_tree, rw, logical, length, NULL,
+ return __btrfs_map_block(fs_info, rw, logical, length, NULL,
multi_ret, mirror_num, raid_map_ret);
}
-int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
- u64 logical, u64 *length, u64 *type,
- struct btrfs_multi_bio **multi_ret, int mirror_num,
- u64 **raid_map_ret)
+int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
+ u64 logical, u64 *length, u64 *type,
+ struct btrfs_multi_bio **multi_ret, int mirror_num,
+ u64 **raid_map_ret)
{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
struct cache_extent *ce;
struct map_lookup *map;
u64 offset;
@@ -1592,17 +1601,17 @@ out:
return 0;
}
-struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid,
+struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
u8 *uuid, u8 *fsid)
{
struct btrfs_device *device;
struct btrfs_fs_devices *cur_devices;
- cur_devices = root->fs_info->fs_devices;
+ cur_devices = fs_info->fs_devices;
while (cur_devices) {
if (!fsid ||
(!memcmp(cur_devices->fsid, fsid, BTRFS_UUID_SIZE) ||
- root->fs_info->ignore_fsid_mismatch)) {
+ fs_info->ignore_fsid_mismatch)) {
device = __find_device(&cur_devices->devices,
devid, uuid);
if (device)
@@ -1628,11 +1637,11 @@ btrfs_find_device_by_devid(struct btrfs_fs_devices *fs_devices,
return NULL;
}
-int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
+int btrfs_chunk_readonly(struct btrfs_fs_info *fs_info, u64 chunk_offset)
{
struct cache_extent *ce;
struct map_lookup *map;
- struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
int readonly = 0;
int i;
@@ -1641,7 +1650,7 @@ int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
* corresponding chunk, we will rebuild it later
*/
ce = search_cache_extent(&map_tree->cache_tree, chunk_offset);
- if (!root->fs_info->is_chunk_recover)
+ if (!fs_info->is_chunk_recover)
BUG_ON(!ce);
else
return 0;
@@ -1671,7 +1680,7 @@ static struct btrfs_device *fill_missing_device(u64 devid)
* slot == -1: SYSTEM chunk
* return -EIO on error, otherwise return 0
*/
-int btrfs_check_chunk_valid(struct btrfs_root *root,
+int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
struct extent_buffer *leaf,
struct btrfs_chunk *chunk,
int slot, u64 logical)
@@ -1681,6 +1690,8 @@ int btrfs_check_chunk_valid(struct btrfs_root *root,
u16 num_stripes;
u16 sub_stripes;
u64 type;
+ u32 chunk_ondisk_size;
+ u32 sectorsize = fs_info->sectorsize;
length = btrfs_chunk_length(leaf, chunk);
stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
@@ -1691,16 +1702,16 @@ int btrfs_check_chunk_valid(struct btrfs_root *root,
/*
* These valid checks may be insufficient to cover every corner cases.
*/
- if (!IS_ALIGNED(logical, root->sectorsize)) {
+ if (!IS_ALIGNED(logical, sectorsize)) {
error("invalid chunk logical %llu", logical);
return -EIO;
}
- if (btrfs_chunk_sector_size(leaf, chunk) != root->sectorsize) {
+ if (btrfs_chunk_sector_size(leaf, chunk) != sectorsize) {
error("invalid chunk sectorsize %llu",
(unsigned long long)btrfs_chunk_sector_size(leaf, chunk));
return -EIO;
}
- if (!length || !IS_ALIGNED(length, root->sectorsize)) {
+ if (!length || !IS_ALIGNED(length, sectorsize)) {
error("invalid chunk length %llu", length);
return -EIO;
}
@@ -1720,23 +1731,38 @@ int btrfs_check_chunk_valid(struct btrfs_root *root,
BTRFS_BLOCK_GROUP_PROFILE_MASK) & type);
return -EIO;
}
+ if (!(type & BTRFS_BLOCK_GROUP_TYPE_MASK)) {
+ error("missing chunk type flag: %llu", type);
+ return -EIO;
+ }
+ if (!(is_power_of_2(type & BTRFS_BLOCK_GROUP_PROFILE_MASK) ||
+ (type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0)) {
+ error("conflicting chunk type detected: %llu", type);
+ return -EIO;
+ }
+ if ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) &&
+ !is_power_of_2(type & BTRFS_BLOCK_GROUP_PROFILE_MASK)) {
+ error("conflicting chunk profile detected: %llu", type);
+ return -EIO;
+ }
+
+ chunk_ondisk_size = btrfs_chunk_item_size(num_stripes);
/*
* Btrfs_chunk contains at least one stripe, and for sys_chunk
* it can't exceed the system chunk array size
* For normal chunk, it should match its chunk item size.
*/
if (num_stripes < 1 ||
- (slot == -1 && sizeof(struct btrfs_stripe) * num_stripes >
- BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) ||
- (slot >= 0 && sizeof(struct btrfs_stripe) * (num_stripes - 1) >
- btrfs_item_size_nr(leaf, slot))) {
+ (slot == -1 && chunk_ondisk_size > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) ||
+ (slot >= 0 && chunk_ondisk_size > btrfs_item_size_nr(leaf, slot))) {
error("invalid num_stripes: %u", num_stripes);
return -EIO;
}
/*
* Device number check against profile
*/
- if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes == 0) ||
+ if ((type & BTRFS_BLOCK_GROUP_RAID10 && (sub_stripes != 2 ||
+ !IS_ALIGNED(num_stripes, sub_stripes))) ||
(type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
(type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
(type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
@@ -1757,11 +1783,11 @@ int btrfs_check_chunk_valid(struct btrfs_root *root,
*
* For sys chunk in superblock, pass -1 to indicate sys chunk.
*/
-static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
+static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
struct extent_buffer *leaf,
struct btrfs_chunk *chunk, int slot)
{
- struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
struct map_lookup *map;
struct cache_extent *ce;
u64 logical;
@@ -1776,7 +1802,7 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
length = btrfs_chunk_length(leaf, chunk);
num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
/* Validation check */
- ret = btrfs_check_chunk_valid(root, leaf, chunk, slot, logical);
+ ret = btrfs_check_chunk_valid(fs_info, leaf, chunk, slot, logical);
if (ret) {
error("%s checksums match, but it has an invalid chunk, %s",
(slot == -1) ? "Superblock" : "Metadata",
@@ -1812,14 +1838,14 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
read_extent_buffer(leaf, uuid, (unsigned long)
btrfs_stripe_dev_uuid_nr(chunk, i),
BTRFS_UUID_SIZE);
- map->stripes[i].dev = btrfs_find_device(root, devid, uuid,
+ map->stripes[i].dev = btrfs_find_device(fs_info, devid, uuid,
NULL);
if (!map->stripes[i].dev) {
map->stripes[i].dev = fill_missing_device(devid);
printf("warning, device %llu is missing\n",
(unsigned long long)devid);
list_add(&map->stripes[i].dev->dev_list,
- &root->fs_info->fs_devices->devices);
+ &fs_info->fs_devices->devices);
}
}
@@ -1849,12 +1875,12 @@ static int fill_device_from_item(struct extent_buffer *leaf,
return 0;
}
-static int open_seed_devices(struct btrfs_root *root, u8 *fsid)
+static int open_seed_devices(struct btrfs_fs_info *fs_info, u8 *fsid)
{
struct btrfs_fs_devices *fs_devices;
int ret;
- fs_devices = root->fs_info->fs_devices->seed;
+ fs_devices = fs_info->fs_devices->seed;
while (fs_devices) {
if (!memcmp(fs_devices->fsid, fsid, BTRFS_UUID_SIZE)) {
ret = 0;
@@ -1880,13 +1906,13 @@ static int open_seed_devices(struct btrfs_root *root, u8 *fsid)
if (ret)
goto out;
- fs_devices->seed = root->fs_info->fs_devices->seed;
- root->fs_info->fs_devices->seed = fs_devices;
+ fs_devices->seed = fs_info->fs_devices->seed;
+ fs_info->fs_devices->seed = fs_devices;
out:
return ret;
}
-static int read_one_dev(struct btrfs_root *root,
+static int read_one_dev(struct btrfs_fs_info *fs_info,
struct extent_buffer *leaf,
struct btrfs_dev_item *dev_item)
{
@@ -1904,30 +1930,30 @@ static int read_one_dev(struct btrfs_root *root,
(unsigned long)btrfs_device_fsid(dev_item),
BTRFS_UUID_SIZE);
- if (memcmp(fs_uuid, root->fs_info->fsid, BTRFS_UUID_SIZE)) {
- ret = open_seed_devices(root, fs_uuid);
+ if (memcmp(fs_uuid, fs_info->fsid, BTRFS_UUID_SIZE)) {
+ ret = open_seed_devices(fs_info, fs_uuid);
if (ret)
return ret;
}
- device = btrfs_find_device(root, devid, dev_uuid, fs_uuid);
+ device = btrfs_find_device(fs_info, devid, dev_uuid, fs_uuid);
if (!device) {
device = kzalloc(sizeof(*device), GFP_NOFS);
if (!device)
return -ENOMEM;
device->fd = -1;
list_add(&device->dev_list,
- &root->fs_info->fs_devices->devices);
+ &fs_info->fs_devices->devices);
}
fill_device_from_item(leaf, dev_item, device);
- device->dev_root = root->fs_info->dev_root;
+ device->dev_root = fs_info->dev_root;
return ret;
}
-int btrfs_read_sys_array(struct btrfs_root *root)
+int btrfs_read_sys_array(struct btrfs_fs_info *fs_info)
{
- struct btrfs_super_block *super_copy = root->fs_info->super_copy;
+ struct btrfs_super_block *super_copy = fs_info->super_copy;
struct extent_buffer *sb;
struct btrfs_disk_key *disk_key;
struct btrfs_chunk *chunk;
@@ -1940,7 +1966,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
u32 cur_offset;
struct btrfs_key key;
- sb = btrfs_find_create_tree_block(root->fs_info,
+ sb = btrfs_find_create_tree_block(fs_info,
BTRFS_SUPER_INFO_OFFSET,
BTRFS_SUPER_INFO_SIZE);
if (!sb)
@@ -1988,7 +2014,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
if (cur_offset + len > array_size)
goto out_short_read;
- ret = read_one_chunk(root, &key, sb, chunk, -1);
+ ret = read_one_chunk(fs_info, &key, sb, chunk, -1);
if (ret)
break;
} else {
@@ -2012,17 +2038,16 @@ out_short_read:
return -EIO;
}
-int btrfs_read_chunk_tree(struct btrfs_root *root)
+int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
{
struct btrfs_path *path;
struct extent_buffer *leaf;
struct btrfs_key key;
struct btrfs_key found_key;
+ struct btrfs_root *root = fs_info->chunk_root;
int ret;
int slot;
- root = root->fs_info->chunk_root;
-
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
@@ -2055,12 +2080,12 @@ int btrfs_read_chunk_tree(struct btrfs_root *root)
struct btrfs_dev_item *dev_item;
dev_item = btrfs_item_ptr(leaf, slot,
struct btrfs_dev_item);
- ret = read_one_dev(root, leaf, dev_item);
+ ret = read_one_dev(fs_info, leaf, dev_item);
BUG_ON(ret);
} else if (found_key.type == BTRFS_CHUNK_ITEM_KEY) {
struct btrfs_chunk *chunk;
chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk);
- ret = read_one_chunk(root, &found_key, leaf, chunk,
+ ret = read_one_chunk(fs_info, &found_key, leaf, chunk,
slot);
BUG_ON(ret);
}
@@ -2265,3 +2290,47 @@ out:
return ret;
}
+
+/*
+ * Get stripe length from chunk item and its stripe items
+ *
+ * Caller should only call this function after validating the chunk item
+ * by using btrfs_check_chunk_valid().
+ */
+u64 btrfs_stripe_length(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk)
+{
+ u64 stripe_len;
+ u64 chunk_len;
+ u32 num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+ u64 profile = btrfs_chunk_type(leaf, chunk) &
+ BTRFS_BLOCK_GROUP_PROFILE_MASK;
+
+ chunk_len = btrfs_chunk_length(leaf, chunk);
+
+ switch (profile) {
+ case 0: /* Single profile */
+ case BTRFS_BLOCK_GROUP_RAID1:
+ case BTRFS_BLOCK_GROUP_DUP:
+ stripe_len = chunk_len;
+ break;
+ case BTRFS_BLOCK_GROUP_RAID0:
+ stripe_len = chunk_len / num_stripes;
+ break;
+ case BTRFS_BLOCK_GROUP_RAID5:
+ stripe_len = chunk_len / (num_stripes - 1);
+ break;
+ case BTRFS_BLOCK_GROUP_RAID6:
+ stripe_len = chunk_len / (num_stripes - 2);
+ break;
+ case BTRFS_BLOCK_GROUP_RAID10:
+ stripe_len = chunk_len / (num_stripes /
+ btrfs_chunk_sub_stripes(leaf, chunk));
+ break;
+ default:
+ /* Invalid chunk profile found */
+ BUG_ON(1);
+ }
+ return stripe_len;
+}
diff --git a/volumes.h b/volumes.h
index ee7d56ab..d35a4e65 100644
--- a/volumes.h
+++ b/volumes.h
@@ -22,7 +22,7 @@
#include "kerncompat.h"
#include "ctree.h"
-#define BTRFS_STRIPE_LEN (64 * 1024)
+#define BTRFS_STRIPE_LEN SZ_64K
struct btrfs_device {
struct list_head dev_list;
@@ -179,72 +179,70 @@ static inline int check_crossing_stripes(struct btrfs_fs_info *fs_info,
(bg_offset + len - 1) / BTRFS_STRIPE_LEN);
}
-int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
+int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
u64 logical, u64 *length, u64 *type,
struct btrfs_multi_bio **multi_ret, int mirror_num,
u64 **raid_map);
-int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
+int btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
u64 logical, u64 *length,
struct btrfs_multi_bio **multi_ret, int mirror_num,
u64 **raid_map_ret);
-int btrfs_next_bg(struct btrfs_mapping_tree *map_tree, u64 *logical,
+int btrfs_next_bg(struct btrfs_fs_info *map_tree, u64 *logical,
u64 *size, u64 type);
-static inline int btrfs_next_bg_metadata(struct btrfs_mapping_tree *map_tree,
- u64 *logical, u64 *size)
+static inline int btrfs_next_bg_metadata(struct btrfs_fs_info *fs_info,
+ u64 *logical, u64 *size)
{
- return btrfs_next_bg(map_tree, logical, size,
+ return btrfs_next_bg(fs_info, logical, size,
BTRFS_BLOCK_GROUP_METADATA);
}
-static inline int btrfs_next_bg_system(struct btrfs_mapping_tree *map_tree,
- u64 *logical, u64 *size)
+static inline int btrfs_next_bg_system(struct btrfs_fs_info *fs_info,
+ u64 *logical, u64 *size)
{
- return btrfs_next_bg(map_tree, logical, size,
+ return btrfs_next_bg(fs_info, logical, size,
BTRFS_BLOCK_GROUP_SYSTEM);
}
-int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
+int btrfs_rmap_block(struct btrfs_fs_info *fs_info,
u64 chunk_start, u64 physical, u64 devid,
u64 **logical, int *naddrs, int *stripe_len);
-int btrfs_read_sys_array(struct btrfs_root *root);
-int btrfs_read_chunk_tree(struct btrfs_root *root);
+int btrfs_read_sys_array(struct btrfs_fs_info *fs_info);
+int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info);
int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
- struct btrfs_root *extent_root, u64 *start,
+ struct btrfs_fs_info *fs_info, u64 *start,
u64 *num_bytes, u64 type);
int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
- struct btrfs_root *extent_root, u64 *start,
+ struct btrfs_fs_info *fs_info, u64 *start,
u64 num_bytes, u64 type, int convert);
-int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf);
-int btrfs_add_device(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_device *device);
int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
int flags);
int btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
void btrfs_close_all_devices(void);
int btrfs_add_device(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+ struct btrfs_fs_info *fs_info,
struct btrfs_device *device);
int btrfs_update_device(struct btrfs_trans_handle *trans,
struct btrfs_device *device);
int btrfs_scan_one_device(int fd, const char *path,
struct btrfs_fs_devices **fs_devices_ret,
u64 *total_devs, u64 super_offset, unsigned sbflags);
-int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len);
+int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len);
struct list_head *btrfs_scanned_uuids(void);
-int btrfs_add_system_chunk(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_key *key,
+int btrfs_add_system_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
struct btrfs_chunk *chunk, int item_size);
-int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
+int btrfs_chunk_readonly(struct btrfs_fs_info *fs_info, u64 chunk_offset);
struct btrfs_device *
btrfs_find_device_by_devid(struct btrfs_fs_devices *fs_devices,
u64 devid, int instance);
-struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid,
+struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
u8 *uuid, u8 *fsid);
int write_raid56_with_parity(struct btrfs_fs_info *info,
struct extent_buffer *eb,
struct btrfs_multi_bio *multi,
u64 stripe_len, u64 *raid_map);
-int btrfs_check_chunk_valid(struct btrfs_root *root,
+int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
struct extent_buffer *leaf,
struct btrfs_chunk *chunk,
int slot, u64 logical);
+u64 btrfs_stripe_length(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk);
#endif